透過 Jenkins、Packer 和 Kubernetes 自動化建置映像檔

建立自訂映像檔來啟動 Compute Engine 執行個體,或是透過 Docker 容器來縮短啟動時間並提高可靠性。透過將軟體預先安裝至自訂映像檔中,您也可以減少對無法掌控的第三方存放區的可用性的依賴。

您可以選擇要在自訂映像檔中包含多少軟體和設定。從一方面來說,最低設定的映像檔 (在本文件中稱為基礎映像檔) 包含基礎 OS 映像檔 (例如 Ubuntu 14.10),而且也可能包含基本的軟體和設定。例如,您可以預先安裝 Java 或 Ruby 等語言執行階段、設定遠端記錄或套用安全性修補程式。基礎映像檔提供穩定的基準映像檔,該映像檔可以進一步加以自訂來提供應用程式。

另一方面,設定完整的映像檔 (在本文件中稱為不可變更的映像檔),不僅包含基礎 OS 或基礎映像檔,還包含執行應用程式所需的一切項目。執行階段設定 (例如資料庫連線資訊或機密資料) 可以包含在映像檔中,也可以在啟動時透過環境、中繼資料或金鑰管理服務提供。

建構映像檔的程序和建構軟體有很多共同之處:兩者都有程式碼 (Chef、Puppet 和 bash 等) 和編寫程式碼的人員;將程式碼套用至基礎映像檔後就可以產生一個版本;成功的建構程序會輸出成果;而且您經常會想要對成果進行一些測試。 適用於建構軟體的許多最佳做法也適用於映像檔:您可以進行版本控制來管理映像檔設定指令碼;在對這些指令碼進行變更後觸發版本;自動執行映像檔建構作業;以及進行版本管理,甚至是在建構作業完成時測試所產生的映像檔成果。

學習目標

在此解決方案中,您將瞭解建構自訂映像檔的兩種一般方法,以及如何使用幾種常見的開放原始碼工具 (包括 Jenkins、Packer、Docker 和 Kubernetes),來建立自動化管道以持續建構映像檔。此管道會與 Google Cloud Platform (GCP) 中的 Cloud Source Repositories 整合,並輸出 Compute Engine 映像檔和 Docker 映像檔。

您會學習到如何建構基礎映像檔及不可變更的映像檔,並瞭解在 GCP 的多個專案中,如何採取最佳做法來管理這些映像檔的存取權限。此說明文件最後會提供完整的教學課程,帶著您部署並使用此解決方案的開放原始碼參考實作。

映像檔類型

可擴充且有彈性的網路應用程式解決方案中,GCP 上的網頁應用程式是以 Ruby on Rails 網頁應用程式為執行的參考。該解決方案的原始碼並非使用自訂的映像檔;當 Compute Engine 執行個體啟動時,開機指令碼會安裝 Chef Solo,然後安裝應用程式執行所需的所有內容。其中包括 nginx、Ruby 2、cURL 和其他系統工具、Unicorn、Rails 應用程式和其所有 Gem、imagemagick 以及應用程式設定。

下圖說明啟動程序。

顯示不使用自訂映像檔時的啟動程序的圖表。

這個程序並不快,「每個執行個體」都需要 10-15 分鐘才能啟動,視套件所需的各種存放區的下載速度而定,而且這是假設代管那些套件的每個存放區都在線上並且可用的情況下。在以下各節中,您將思考基礎映像檔和不可變更的映像檔如何提高執行個體啟動程序的效能和可靠性。

基礎映像檔

建立基礎映像檔時,您可以決定要在映像檔中包含哪些軟體和套件。在做出決定時,請考慮下列事項:

  • 安裝速度:大型套件的下載速度可能會很慢;必須從來源建構的軟體可能會很耗時;而且包含許多依附元件的套件會使得問題變得更複雜。請考慮在基礎映像檔中包含這些類型的軟體和套件。
  • 遠端存放區的可靠性:如果您並沒有在基礎映像檔中加入軟體,而是要在啟動時下載,您是否信任遠端存放區的可用性?如果在啟動期間該存放區無法使用,它是否會使得您的應用程式無法運作?如要減少對可能無法掌控的遠端存放區的依賴,請考慮將重要的依附元件包含在基礎映像檔中。
  • 變更頻率:該軟體或套件是否經常變更?若是如此,請考慮從排除基礎映像檔中排除這類軟體或套件,並將其改為儲存在可存取的可靠位置,例如 Cloud Storage 值區。
  • 必要或強制要求安全性:如果強制要求在您機構的每個執行個體上使用特定設定執行某些套件 (如日誌記錄、OSSEC 等),那麼這些套件應安裝在衍生其他所有映像檔延伸的基礎映像檔中。資安團隊可以使用更進階的工具 (例如 Chef 或 Puppet) 來建構 Docker 基礎映像檔,而下游開發人員則可以使用 Dockerfile 來輕鬆地延伸該基礎。

這些準則顯示,可擴充且有彈性的網路應用程式解決方案中的 Ruby on Rails 應用程式的基礎映像檔可以包含 Chef Solo、nginx、Ruby、cURL 和其他系統工具以及 Unicorn。其他依附元件則會在啟動時安裝。

下圖說明使用基礎映像檔時的啟動程序:

顯示使用基礎映像檔時的啟動程序的圖表。

此範例中的運作執行個體會從 Compute Engine 中繼資料服務擷取其設定 (例如,資料庫連線字串、API 金鑰等)。您可以選擇使用不同的服務 (例如 etcd) 或簡易的 Cloud Storage 值區來管理設定。

以下各節將著重於用於自動建構下圖所示的 Ruby 基礎映像檔的工具。

不可變更的映像檔

與基礎映像檔不同,不可變更的映像檔會將其所有軟體都包含在映像檔中。從該映像檔啟動執行個體或容器時,將無需下載任何套件或安裝任何軟體。可擴充且有彈性的網路應用程式解決方案中的 Ruby on Rails 應用程式的不可變更的映像檔將包含所有軟體,而該執行個體在啟動時將已準備好處理流量。

顯示使用不可變更的映像檔時的啟動程序的圖表。

設定和不可變更的映像檔

您可以選擇讓您的應用程式從設定服務存取所需的設定資料,也可以將所有設定包含在不可變更的映像檔中。如果您選擇後一種方法,請務必考慮到在映像檔中包含密鑰的安全疑慮。如果要將不可變更的映像檔推送到 Docker Hub 中的公開存放區,則每個人都可以存取那些映像檔,因此其中不應包含任何敏感或機密資訊。

將不可變更的映像檔做為部署單位

使用不可變更的映像檔做為部署單位可避免可能發生設定偏移的情形 (一或多個執行個體處於與預期不同的狀態)。例如,當您將安全性修補程式套用至 100 個執行中的容器而其中一些無法更新時,就會發生這種情況。做出「任何」變更時,該映像檔將變成您部署的內容。如果 OS 需要軟體修補程式或者應更新記錄設定,您應建構新的映像檔以包含那些變更,並透過啟動新的執行個體或容器,以及取代所有舊的映像檔來部署新的映像檔。如果您選擇將應用程式設定組合到不可變更的映像檔中,即使像是更新資料庫連線字串的簡單變更,也要建立和發佈新的映像檔。

自動建構映像檔的管道的架構和實作

本節包含使用 Jenkins、Packer、Docker 和 Google Kubernetes Engine (GKE) 自動建構自訂映像檔的自動建構映像檔管道的實作詳細資料。每個部分均包含一個簡介、架構圖和該圖中的元件的詳細分析。

使用的軟體和服務

這些軟體和服務用於建立自動化映像檔建構工具。

軟體 用法
Jenkins Jenkins 是一種常見的開放原始碼持續整合 (CI) 伺服器。 您將使用 Jenkins 來輪詢包含映像檔設定指令碼的其他專案中的 Git 存放區,然後根據這些存放區建構映像檔。
Packer Packer 是一種可從單一來源設定針對多個平台建立相同機器映像檔的工具。它支援許多不同的設定來源,包括 Shell、Chef、Puppet、Ansible 和 Salt,並且可針對 Compute Engine 和 Docker 等輸出映像檔。Jenkins 代理程式會使用 Packer 從 Git 存放區中的設定建構映像檔。
Docker Docker 是一種開放原始碼工具,用於將應用程式封裝和部署為容器。此架構和教學課程中的 Jenkins 部署 (包括 leader 節點和建構代理程式) 會部署為 Docker 容器。 建構代理程式也會輸出 Docker 映像檔做為其架構之一。
GKE GKE 採用 Kubernetes 開放原始碼技術,可讓您在 GCP 的虛擬機器上執行和管理 Docker 容器。
Container Registry Container Registry 在 GCP 上提供安全的私人 Docke 映像檔儲存空間,是在 GCP 上執行,並透過 HTTPS 端點進行存取。
Compute Engine GKE 使用 Compute Engine VM 來執行 Kubernetes,及代管 Jenkins leader 和建構代理程式容器。除了 Docker 映像檔外,Jenkins 建構程序也會輸出 Compute Engine VM 映像檔。
Cloud Storage 您將使用 Cloud Storage 來儲存 Jenkins 設定的備份。
Nginx Nginx 提供反向 Proxy 功能;將傳入要求轉送至 Jenkins leader 網頁介面。它可以設定來終止安全資料傳輸層 (SSL) 連線並提供基本驗證。

映像檔建構工具總覽

下圖顯示各種元件如何互動以建立可自動建構 VM 和 Docker 映像檔的系統。

顯示映像檔建構工具專案的各種元件的圖表。

您可以在 Jenkins leader 上對要建構的每個映像檔定義工作。該工作會輪詢包含設定指令碼和說明如何建構映像檔的 Packer 範本的原始碼存放區 (在本圖中為Git)。當輪詢程序偵測到變更時,Jenkins leader 會將工作指派給建構代理程式。該代理程式會使用 Packer 執行該版本,從而輸出 Docker 映像檔至 Container Registry,以及輸出 VM 映像檔至 Compute Engine。

Packer 和設定指令碼

Packer 範本和相關聯的設定指令碼一起定義了如何建構映像檔。它們會被視為軟體並儲存在自己的 Git 存放區中。您建構的每個映像檔都會有自己的存放區,其中包含 Packer 範本和設定指令碼。

本節概述一種可能的 Packer 設定,該設定使用 Chef 透過新增 Ruby 和 rbenv 來自訂 Ubuntu 14.04。如需 Packer 的完整說明,請造訪 https://www.packer.io/docs,查看其詳細的說明文件。

映像檔命名和 packer 變數

每當對包含映像檔的 Packer 範本和設定指令碼的 Git 存放區進行變更時,映像檔建構工具就會建構一個映像檔。建議您使用建構映像檔時所用的 Git 分支版本和修訂版本 ID 來命名或標記映像檔。Packer 範本可讓您定義變數,並在執行階段為它們提供值:

{
...
  "variables": {
      "Git_commit": "",
      "Git_branch": "",
      "ruby_version_name": "212",
      "project_id": "null"
  }
...
}

Jenkins 建構代理程式可以找到 Git 分支版本和修訂版本 ID,並將它們做為變數提供給 Packer 指令列工具。您稍後將會在本文件的教學課程部分瞭解其實務應用。

包含佈建工具的程式輔助設定

Packer 範本定義了一或多個佈建工具,這些工具會說明如何使用 Chef、Puppet 或殼層指令碼等工具來設定執行個體。Packer 支援許多佈建工具;如需完整清單,請參閱 Packer 說明文件中的目錄。以下程式碼片段定義了一個 chef-solo 佈建工具,其中包含要執行以設定映像檔的教戰手冊路徑和方案:

{
  ...
  "provisioners": [
    {
      "type": "chef-solo",
      "install_command": "apt-get install -y curl && curl -L https://www.opscode.com/chef/install.sh | {{if .Sudo}}sudo{{end}} bash",
      "cookbook_paths": ["chef/site-cookbooks"],
      "run_list": [{{
        "recipe[ruby]",
        "recipe[ruby::user]",
        "recipe[ruby::ruby212]"
      ]
    }
  ],
  ...
}

chef 教戰手冊和方案會儲存在與 Packer 範本相同的 Git 存放區中。

使用建構工具定義映像檔輸出

範本的 builders 部分定義了佈建工具將執行以建立新映像檔的位置。如要同時建構 Compute Engine 映像檔和 Docker 映像檔,請定義兩個建構工具:

{
  "variables": {...},
  "provisioners": [...],
  "builders": [
    {
      "type": "googlecompute",
      "project_id": "{{user `project_id`}}",
      "source_image": "ubuntu-1410-utopic-v20150202",
      "zone": "us-central1-a",
      "image_name": "{{user `ruby_version_name`}}-{{user `Git_branch`}}-{{user `Git_commit`}}"
    },
    {
      "type": "docker",
      "image": "ubuntu:14.10",
      "commit": "true"
    }
  ],
 ...
}

googlecompute 建構工具包含 project_id 屬性,用於指示產生的映像檔的儲存位置。image_name 屬性 (可為產生的映像檔指定名稱) 會串聯變數以建立包含映像檔相關資訊 (Ruby 的版本、Git 分支版本,以及用於建構映像檔的 Git 修訂版本 ID) 的名稱。googlecompute 構建工具建立的映像檔範例 URI 可能如下所示:

https://www.googleapis.com/compute/v1/projects/image-builder-project-name/global/images/ruby212-master-9909043

docker 建構工具應包含 post-processors 屬性,用於使用其推送目的地的 Docker Registry 和存放區來標記映像檔:

{
  "variables": {...},
  "provisioners": [...],
  "builders": [...],
  "post-processors": [
    [
      {
        "type": "docker-tag",
        "repository": "gcr.io/{{user `project_id`}}/ruby212",
        "tag": "{{user `Git_branch`}}-{{user `Git_commit`}}",
        "only": ["docker"]
      }
    ]
  ]
}

這個後續處理器將會使用執行該版本時所提供的 project_id 來標記映像檔以儲存在 Container Registry 中。推送此 Docker 映像檔後,即可擷取:

docker pull gcr.io/image-builder-project-name/ruby212:master-9909043

您要建構的每個映像檔自己的原始碼存放區中都會包含一個 Packer 範本和設定指令碼,而且 Jenkins leader 將會為每個映像檔定義一個工作,如下圖所示。

顯示包含自訂映像檔的映像檔建構工具專案的圖表。

將 Jenkins 和 Packer 一起使用的一個優點是,Jenkins 可以偵測並回應您對 Packer 範本或設定指令碼所做的任何更新。例如,如果您更新了 Ruby 基礎映像檔中安裝的 Ruby 版本,則 Jenkins leader 會透過指派代理程式來複製存放區,針對範本執行 Packer,以及建構映像檔來做出回應。

此解決方案最後面的教學課程將會詳細介紹設定 Jenkins 工作以執行 Packer 版本的程序。

專案區隔

Jenkins leader 和建構代理程式會在同一個 Cloud Platform 專案中一起執行,而它們建立的映像檔將會儲存在此專案中。專案可讓您依功能區隔應用程式。專案不會產生費用;您只需為所使用的資源付費。在此解決方案中,Jenkins 基礎架構將在自己的專案中執行,與其使用的原始碼控管存放區分開。Jenkins 備份 (會在接下來的部分中討論) 會儲存在專案內的 Google Cloud Storage 值區中。 如此可讓 Jenkins 做為「映像檔中樞」,將映像檔與其他專案共用,同時讓其他專案使用個別的存取權控管來維護自己的程式碼存放區。

在整個機構內建構和共用映像檔

為了便於共用映像檔,此解決方案會將儲存在 Git 中的每個建構映像檔放入不同的映像檔設定專案中。這種區隔在映像檔建構工具專案和建構映像檔之間提供了專案區隔。採用這種軸輻式架構 (其中映像檔建構工具專案是中樞,映像檔設定專案是輪輻) 時,不同的團隊就可以更輕鬆地擁有和管理映像檔設定。

這種軸輻式架構如下圖所示。

顯示使用映像檔建構工具專案做為軸輻式系統的圖表。

下面將會討論存取權控管 (將 Jenkins 叢集的存取權授予每個映像檔專案,以及將其他專案的存取權授予 Jenkins 建構的映像檔)。

每個映像檔一個專案

您建立的每個專案都有一個採用 Git 架構的專用 Cloud Repository。您可以建立的專案數量並無限制,而且您只需為在專案中使用的資源 (例如 Compute Engine 執行個體) 付費。例如,如果您擁有 PHP、Ruby 和 Wordpress 映像檔,則 Google Cloud Platform 主控台中將會顯示每個映像檔自己的專案,如下圖所示。

顯示針對每個自訂映像檔使用不同專案的映像檔建構工具專案的圖表。

專案的 Cloud Repository 可從 [原始碼] 選單項目存取。對於新專案,您可以選擇如何初始化存放區:可鏡射現有的 GitHub 或 Bitbucket 存放區、推送現有的本機 Git 存放區,或是從 Cloud Source Repositories 建立新的本機 Git 存放區,如下圖所示。

如何使用 GCP 主控台瀏覽原始碼的螢幕圖片。

下圖顯示使用定義版本的 Packer 範本和 Chef 方案初始化的 Ruby 基礎映像檔專案。

使用 Packer 範本和 Chef 方案的 Ruby 基礎映像檔。

如要查看存放區的網址,請按一下「Setting」(設定)。建立 Jenkins leader 存放區的建構工作時,會需要用到此網址,如下圖所示。

Jenkins leader 的原始碼存放區設定。

Cloud Repository 存取權控管

Jenkins 映像檔建構工具需要每個映像檔設定專案的 Cloud Repository 的「可以檢視」權限。下圖顯示前面說明的軸輻式架構的簡化版。

具有必要權限的映像檔建構工具專案。

每個專案都必須使用映像檔建構工具專案的運算服務帳戶電子郵件地址,來授予對 Jenkins 映像檔建構工具專案的存取權。該地址格式為 \{PROJECT_ID\}-compute@developer.gserviceaccount.com,可以在 GCP 主控台中的該專案的「權限」區段中進行複製,如下圖所示。

要從專案的「權限」區段複製的地址。

取得執行 Jenkins 映像檔建構工具的專案的運算服務帳戶電子郵件地址後,請前往包含要從中建構映像檔的 Cloud Repository 的每個專案的「權限」區段,選取 [新增成員],然後授予「可以檢視」權限,如下圖所示。

設定可以查看專案中的權限。

在映像檔建構工具專案中執行的 Jenkins leader 現在將可以從這些專案中的 Cloud Repository 進行輪詢和擷取,並在發生變更時建構新的映像檔。

共用 Compute Engine 和 Docker 映像檔

由映像檔建構工具建立的 Compute Engine 和 Docker 映像檔會儲存在與映像檔建構工具相同的專案中。其他專案中的應用程式將使用這些映像檔來啟動 Compute Engine 執行個體和 Docker 容器,而想要存取這些映像檔的每個應用程式專案都必須具有映像檔建構工具專案的「可以檢視」權限。請按照上一節中定義的程序進行操作,這次請找出每個應用程式專案的運算服務帳戶,並將其新增為具有映像檔建構工具專案的「可以檢視」權限的成員,如下圖所示。

將具有「可以檢視」權限的其他專案新增至映像檔建構工具專案。

Jenkins 備份與還原

Jenkins leader 包含一個預先定義的工作,用於定期將 Jenkins 設定和工作記錄 備份到 Google Cloud Storage。根據預設,該工作會定期執行 (平日每兩個小時一次),如下圖所示。

Jenkins leader 的自動化建構設定。

該工作的建構步驟會執行殼層指令碼,將密鑰、使用者、工作和歷史記錄封存到 TAR 壓縮檔中。系統會建立兩個封存複本:一個以日期戳記命名,另一個則命名為 LATEST,可讓您輕鬆地自動還原最新的備份。您可以自訂此步驟以新增或移除要備份的項目,如下圖所示。

如何自訂建構指令碼。

構建後動作會使用 Cloud Storage 外掛程式和您建立的 Google 中繼資料憑證,與 Google API 進行互動,並將備份封存上傳至 Cloud Storage。它會同時上傳日期戳記和 LATEST 封存。下圖顯示步驟定義。

用於定義建構後動作的介面。

下圖顯示包含已累積的一些備份的值區:

專案的累積備份清單。

還原備份

與先前章節相同,需使用環境變數啟用 Nginx 反向 Proxy 上的 SSL 或基本驗證,您可以使用環境變數來設定 Jenkins leader 的複製控制器定義,以便在服務啟動時還原備份。以下程式碼是複製控制器的定義中的程式碼片段:

{
  "kind": "ReplicationController",
  ...
  "spec": {
    ...
    "template": {
      "spec": {
        "containers": [
            {
              "name": "jenkins",
              "env": [
                {
                  "name": "GCS_RESTORE_URL",
                  "value": "gs://your-backup-bucket/jenkins-backup/LATEST.tar.gz"
                }
              ],
             ...
           }
        ]
      }
    }
  }
}

Jenkins leader Docker 映像檔會在啟動時檢查 GCS_RESTORE_URL 環境變數是否存在。如果找到該環境變數,系統會假設值為備份的網址 (包括 gs:// 配置),而指令碼則會使用安裝在 Jenkins leader 映像檔上的 gsutil 指令列工具,以安全的方式下載及還原備份。

還原程序只會在啟動容器時發生。如要在啟動 Jenkins leader 後還原備份,請將其複製控制器的大小調整為 0,更新該控制器的定義以指向備份的網址,然後將大小設回 1。教學課程中會就此進行說明。

教學課程

您可以在 GitHub 的 https://github.com/GoogleCloudPlatform/kube-jenkins-imager 中找到本教學課程的完整內容,包含操作說明和原始碼在內。

本頁內容對您是否有任何幫助?請提供意見:

傳送您對下列選項的寶貴意見...

這個網頁
解決方案