Jenkins、Packer、Kubernetes を使用したイメージビルドの自動化

Google Compute Engine のインスタンスまたは Docker コンテナを起動するカスタム イメージを作成すると、起動時間の削減と信頼性の向上が実現できます。ソフトウェアをカスタム イメージにプリインストールすることによって、コントロールが及ばないサードパーティのリポジトリの可用性への依存を抑制することもできます。

カスタム イメージに組み込むソフトウェアと構成の量を選択します。スペクトルの一方の終端では、最低限の構成イメージ(このドキュメントでは「基盤イメージ」と呼びます)に(Ubuntu 14.10 のような)基本 OS イメージが含まれ、基本ソフトウェアと構成も組み込まれます。たとえば、Java や Ruby のような言語ランタイムをプリインストールし、リモートログを構成またはセキュリティ パッチの適用を行います。基盤イメージは、アプリケーションを提供するためにさらにカスタマイズが可能な安定性の高いベースライン イメージを提供します。

スペクトルの別の終端には、すべてが構成されたイメージ(このドキュメントでは「不変イメージ」と呼びます)に、基本 OS または基盤イメージだけでなく、アプリケーションの実行に必要なすべてが含まれています。データベース接続情報や機密データなどのランタイム構成は、このイメージに組み込まれるか、環境、メタデータ、起動時の主要管理サービスを経由して提供されます。

イメージをビルドするプロセスでは、ソフトウェアのビルドで多くの共通項(コード(Chef、Puppet、bash など)とコードの記述者)があります。このコードをベースイメージに適用するとビルドが発生し、ビルドプロセスが成功すると、成果物が出力されます。この成果物を一部のテストに配置することもあります。ソフトウェアのビルドに適用するベスト プラクティスの多くは、イメージにも適用されます。バージョンをコントロールし、イメージ構成スクリプトを管理します。このスクリプトに変更が発生した場合ビルドを起動し、イメージのビルドが自動的に実施されます。ビルドが完了すると、結果のイメージ成果物のバージョンを管理し、テストも行います。

学習内容

このソリューションでは、カスタム イメージをビルドする 2 つの一般的な手法と、複数の代表的なオープンソース ツール(Jenkins、Packer、Docker、Kubernetes など)を使用して、自動パイプラインを作成し、イメージのビルドを続行する方法について学習します。このパイプラインは、Google Cloud Platform の Git クラウド リポジトリと連携し、Google Compute Engine イメージと Docker イメージの両方を出力します。

基盤イメージと不変イメージのビルド方法について学習し、また、Google Cloud Platform の複数のプロジェクト間でこれらのイメージにアクセスするためのベスト プラクティスについても学習します。最後に、このドキュメントの終わりにある包括的なチュートリアルで、オープンソースとソリューションのリファレンス実装をデプロイして使用します。

イメージタイプ

スケーラビリティと復元性の高いウェブ アプリケーション ソリューションでは、Google Cloud Platform でのウェブ アプリケーションの実行のリファレンスとして、Ruby on Rails ウェブ アプリケーションが使用されます。 そのソリューションのソースコードはカスタマイズ イメージを使用しません。Google Compute Engine のインスタンスが起動されると、起動スクリプトによって Chef Solo がインストールされ、続いてこのアプリケーションに必要なすべてがインストールされます。インストールされるのは、nginx、Ruby 2、cURL と他のシステムツール、Unicorn、Rails アプリとその gem、imagemagick、アプリの構成のすべてです。

次の図に起動プロセスを示します。

カスタム イメージなしの起動プロセスを示す図。

これは高速プロセスではありません。パッケージに必要なさまざまなリポジトリのダウンロード速度(パッケージをホスティングしているすべてのリポジトリがオンラインで入手可能であると仮定)にもよりますが、個々のインスタンスが起動するには 10~15 分かかります。次のセクションでは、インスタンスの起動プロセスのパフォーマンスと信頼性を基盤イメージと不変イメージがどのように改善するかについて考察します。

基盤イメージ

基盤イメージを作成するときに、イメージに組み込むソフトウェアとパッケージを決定します。次に、その決定を下すときに考慮すべき点を示します。

  • インストール速度 - パッケージが大きいとダウンロードに時間がかかります。ソースからのソフトウェアのビルドに必要な時間も増し、多くの依存関係を持つパッケージで問題が複雑化します。このような種類のソフトウェアとパッケージは基盤イメージに組み込むようにしてください。
  • リモート リポジトリの信頼性 - 基盤イメージにソフトウェアを組み込まず、起動時にダウンロードした場合、リモート リポジトリの可用性を信頼できますか。このリポジトリが起動中に利用できなかった場合、アプリケーションが機能しなくなることはありますか。コントロールが及ばないリモート リポジトリへの依存度を下げるには、重要な依存関係を基盤イメージに組み込むようにしてください。
  • 変更の発生率 - ソフトウェアまたはパッケージは頻繁に変更されていますか。変更が多い場合は、基盤イメージに組み込まず、信頼性が高く、アクセス可能なロケーション(Cloud Storage バケットなど)に保管しておくようにしてください。
  • 必要条件または指定されたセキュリティ - 特定のパッケージ(ログ、OSSEC など)が、組織内のすべてのインスタンス上など、独自の構成の下での実行が認められている場合、これらのパッケージは、他のすべてのイメージを拡張する基盤イメージにインストールする必要があります。セキュリティ チームは、Docker 基盤イメージをビルドするために Chef や Puppet のような拡張ツールを使用することがあります。一方、下流工程の開発者は Dockerfile で基盤を簡単に拡張することができます。

こうした基準では、「スケーラビリティと復元性の高いウェブ アプリケーション ソリューション」からの Ruby on Rails アプリケーションが、Chef Solo、nginx、Ruby、cURL とその他のシステムツール、Unicorn を組み込むことが推奨されます。他の依存関係は起動時にインストールされます。

次の図に、基盤イメージによる起動プロセスを示します。

基盤イメージによる起動プロセスを示す図。

この例では、機能のインスタンスがその構成(データベース接続文字列、API キーなど)を Compute Engine メタデータ サービスから取得しています。構成の管理に、etcd やシンプルな Google Cloud Storage バケットのような別のサービスを使用することもできます。

後続のセクションでは、ここに示す Ruby 基盤イメージのビルドを自動化するために使用するツールについて説明します。

不変イメージ

不変イメージは基盤イメージとは異なり、イメージに組み込まれたソフトウェアをすべて所有します。インスタンスまたはコンテナがイメージから起動されるときに、パッケージのダウンロードやソフトウェアのインストールは発生しません。「スケーラビリティと復元性の高いウェブ アプリケーション ソリューション」からの Ruby on Rails アプリケーションの不変イメージがすべてのソフトウェアを組み込み、起動時に、インスタンスがトラフィックを提供する用意ができています。

不変イメージによる起動プロセスを示す図。

構成と不変イメージ

構成サービスからアプリケーションが必要とする構成データにアクセスできるようにすることも、すべての構成を不変イメージに組み込むこともできます。イメージへの組み込みを選択した場合、イメージにシークレットを組み込むというセキュリティ上のリスクを考慮するようにしてください。不変イメージを Docker Hub のパブリック リポジトリに push している場合、不変イメージには誰でもアクセスできるため、機密情報や秘密情報を入れてはいけません。

デプロイ単位としての不変イメージ

不変イメージをデプロイ単位として使用すると、1 つ以上のインスタンスが予想外の状態になる構成の変動の可能性が排除されます。たとえば、セキュリティ パッチを 1,000 個の実行中のコンテナに適用し、そのときにいくつか更新できなかった場合にこうした状態が発生することがあります。イメージは、なんらかの変更が加えられたときにデプロイしたものになります。OS にソフトウェア パッチが必要な場合、またはログ設定を更新する必要がある場合、こうした変更を組み込むために新しいイメージをビルドして、新しいインスタンスまたはコンテナを起動し、古い物すべてと置き換えることによって、イメージをロールアウトします。アプリケーションの設定を不変イメージに組み込むことを選択すると、データベース接続文字列の更新などの単純な変更でも、新しいイメージが作成され、リリースされます。

自動化されたイメージビルドのパイプラインのアーキテクチャと実装

このセクションでは、自動化されたイメージビルドのパイプラインの実装について詳細を説明します。これは、Jenkins、Packer、Docker、Google Kubernetes Engine を使用して、カスタム イメージを自動的にビルドします。各セクションは、概要、アーキテクチャ図、図のコンポーネントの詳細分析で構成されます。

使用するソフトウェアとサービス

これらのソフトウェアとサービスは、次の自動イメージビルダの作成に使用されます。

ソフトウェア 使用
Jenkins Jenkins は普及型オープンソースの継続的インテグレーション(CI)サーバーです。Jenkins を使用して、イメージ設定スクリプトを含む他のオブジェクトに Git リポジトリをポーリングし、次にこのリポジトリに基づいたイメージをビルドします。
Packer Packer は単一のソース設定から複数のプラットフォーム向けに同じマシンイメージを作成するツールです。Shell、Chef、Puppet、Ansible、Salt など、数多くのさまざまな構成ソースをサポートし、Google Compute Engine、Docker、その他向けにイメージを出力できます。Packer は Jenkins エージェントで使用され、Git リポジトリの構成からイメージをビルドします。
Docker Docker はアプリケーションをコンテナとしてパッケージし、デプロイするオープンソース ツールです。このアーキテクチャとチュートリアルの Jenkins デプロイ(リーダーノードおよびビルド エージェントを含む)は、Docker コンテナとしてデプロイされます。ビルド エージェントは、アーキテクチャの 1 つとして Docker イメージも出力します。
Google Kubernetes Engine オープンソース技術である Kubernetes を搭載した Google Kubernetes Engine により、Google Cloud Platform の仮想マシンで Docker コンテナを実行および管理できます。
Google Container Registry(gcr.io) Google Container Registry には、Google Cloud Platform に基づく、安全な専用 Docker イメージ ストレージが用意されています。Google Cloud Platform で実行され、HTTPS エンドポイントからアクセスできます。
Google Compute Engine Google Kubernetes Engine では、Compute Engine VM を使用して Kubernetes を実行し、Jenkins リーダーとビルド エージェント コンテナをホストします。Jenkins ビルドプロセスは、Docker イメージに加えて Compute Engine の VM イメージも出力します。
Google Cloud Storage Jenkins 構成のバックアップの格納に Google Cloud Storage を使用します。
Nginx Nginx は、リバース プロキシ機能を提供し、受信リクエストが Jenkins リーダーのウェブ インターフェースに転送されます。SSL 接続を終了し、基本認証を提供するように構成できます。

イメージビルダの概要

次の図は、VM イメージと Docker イメージを自動的にビルドするシステムを作成するためにさまざまなコンポーネントがどのように連携しているかを示しています。

イメージビルダ プロジェクトのさまざまなコンポーネントを示す図。

ビルドするイメージごとに Jenkins リーダーにジョブを定義します。このジョブは、イメージをビルドする方法を記述した設定スクリプトと Packer テンプレートが含まれる、ソースコード リポジトリ(この図では Git)をポーリングします。ポーリング プロセスが変更を検出すると、Jenkins リーダーはジョブをビルド エージェントに割り当てます。エージェントは Packer を使用してビルドを実行し、Docker イメージを Google Container Registry に出力し、VM イメージを Google Compute Engine に出力します。

Packer と設定スクリプト

Packer テンプレートと関連する設定スクリプトは、連携してイメージのビルド方法を定義します。このスクリプトはソフトウェアのように処理され、そのスクリプト独自の Git リポジトリに格納されます。ビルドするイメージごとに独自のリポジトリがあり、リポジトリには Packer テンプレートと設定スクリプトが付属しています。

このセクションでは、Ruby と rbenv を追加することによって、Ubuntu 14.04 をカスタマイズするために Chef を使用する 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、シェル スクリプトのようなツールを使用してインスタンスを設定する方法を記述した 1 つ以上のプロビジョナーを定義します。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]"
      ]
    }
  ],
  ...
}

シェフのクックブックとレシピは Packer テンプレートと同じ Git リポジトリに格納されます。

ビルダによるイメージ出力の定義

テンプレートの builders セクションは、新しいイメージを作成するためにプロビジョナーが実行される場所を定義します。Compute Engine イメージと Docker イメージの両方をビルドするには、次の 2 つのビルダを定義します。

{
  "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 属性が組み込まれ、次のようにイメージの push 先となる Docker レジストリとリポジトリでイメージにタグを付けます。

{
  "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 を使用して、Google Container Registry に保存用のイメージにタグ付けします。この Docker イメージが push されると、次のコマンドで取得できるようになります。

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

ビルドする個々のイメージには、その独自のリポジトリに Packer テンプレートと設定スクリプトが含まれ、Jenkins リーダーには、次の図に示すように、リーダーごとに定義されたジョブが含まれます。

カスタム イメージによるイメージビルダ プロジェクトを示す図。

Jenkins と Packer を同時に使用する利点の 1 つとして、Jenkins が Packer テンプレートまたは設定スクリプトに加えた更新を検出し、更新にレスポンスできるということがあります。たとえば、Ruby 基盤イメージにインストールされている Ruby のバージョンを更新した場合、Jenkins リーダーは、リポジトリのクローンの作成、テンプレートに対する Packer の実行、イメージのビルドを行うエージェントを割り当てることによってレスポンスします。

このソリューションの最後にあるチュートリアルに、Packer ビルドを実行するために Jenkins ジョブを設定するプロセスの詳細が記載されています。

プロジェクトの分離

Jenkins リーダーとビルド エージェントは、同じ Cloud Platform プロジェクトで同時に実行され、作成されたイメージがこのプロジェクトに格納されます。プロジェクトでは、機能別にアプリケーションを分離できます。プロジェクトには課金されません。課金の対象となるのは、使用するリソースのみです。このソリューションでは、Jenkins インフラストラクチャは使用するソース コントロール リポジトリから分離され、独自のプロジェクトで実行されます。後続のセクションで説明しますが、Jenkins バックアップは、プロジェクト内の Google Cloud Storage バケットに格納されます。このバックアップで、Jenkins が「イメージハブ」として機能し、イメージを他のプロジェクトに分配できる一方で、他のプロジェクトはプロジェクト独自のコード リポジトリを別のアクセス制御で維持できるようになります。

Kubernetes Engine に Jenkins をインストールして構成する方法の詳細については、Kubernetes Engine での Jenkins をご覧ください。

組織にまたがるイメージのビルドと共有

イメージの共有を容易にするため、このソリューションは Git に格納されたビルドイメージを分離したイメージ設定プロジェクトに配置します。この分離によって、イメージビルダ プロジェクトとビルドイメージの間にプロジェクト分離が実現します。イメージビルダ プロジェクトがハブで、イメージ設定プロジェクトがスポークであるハブアンドスポーク アーキテクチャによって、別々のチームがイメージ設定を容易に所有し、管理できます。

このハブアンドスポーク アーキテクチャを次の図に示します。

ハブ アンド スポーク システムとしてのイメージビルダ プロジェクトを示す図。

アクセス制御(Jenkins クラスタの各イメージ オブジェクトへのアクセスを許可し、Jenkins によってビルドされたイメージへの他のプロジェクトのアクセスを許可する)については以下で説明します。

イメージごとに 1 つのオブジェクト

作成した各プロジェクトには、専用の Git ベースのクラウド リポジトリがあります。作成するプロジェクトの数に制限はありません。課金対象は、プロジェクトで使用する Compute Engine インスタンスなどのリソースのみです。たとえば、PHP イメージ、Ruby イメージ、Wordpress イメージがある場合、次の図で示すように、Google Cloud Platform Console で、それぞれのイメージに独自のプロジェクトが表示されます。

カスタム イメージごとに別のプロジェクトを持つイメージビルダ プロジェクトを示す図。

プロジェクトのクラウド リポジトリには、[ソースコード] メニュー項目からアクセスできます。新しいプロジェクトで、リポジトリの初期化方法を選択します。次の図で示すように、既存の GitHub または Bitbucket リポジトリをミラーリングし、既存のローカル Git リポジトリを push するか、Cloud Repository から新しいローカル Git リポジトリを作成します。

GCP Console でソースコードを参照する方法の画面イメージ。

次の画像は、ビルドを定義している Packer テンプレートと Chef レシピで初期化された Ruby 基盤イメージ プロジェクトを示しています。

Packer テンプレートと Chef レシピを伴う Ruby 基盤イメージ。

設定の歯車をクリックすると、リポジトリの URL が表示されます。この URL は、次の画像に示すように、Jenkins リーダーのリポジトリにビルドジョブを作成するときに必要になります。

Jenkins リーダーのソース リポジトリの設定。

クラウド リポジトリのアクセス制御

Jenkins イメージビルダには、各イメージ設定プロジェクトのクラウド リポジトリに [表示可能] 権限が必要です。次の図に、先に示したハブアンド スポーク アーキテクチャの簡略図を示します。

必要な権限を持つイメージビルダ プロジェクト。

各プロジェクトは、Jenkins イメージビルダ プロジェクトのコンピューティング サービス アカウントのメールアドレスを使用して、このイメージビルダ プロジェクトへのアクセスを許可する必要があります。このアドレス形式は \{PROJECT_ID\}-compute@developer.gserviceaccount.com で、次の図に示すように、GCP Console のそのプロジェクトの [権限] セクションでコピーできます。

プロジェクトの権限セクションからコピーするアドレス。

Jenkins イメージビルダを実行しているプロジェクトにコンピューティング サービス アカウントのメールアドレスを設定したら、次の図に示すように、イメージのビルド元にする Cloud Repository を備えたプロジェクトごとに [権限] セクションに移動し、[メンバーを追加] を選択して [閲覧可能] 権限を付与します。

プロジェクト内での表示可能権限の設定。

これで、イメージビルダ プロジェクトで実行される Jenkins リーダーが、これらのプロジェクトのクラウド リポジトリからのポーリングと pull が可能となり、変更がコミットされるときに新しいイメージをビルドできるようになります。

Compute Engine と Docker イメージの共有

イメージビルダで作成された Compute Engine イメージと Docker イメージは、イメージビルダと同じプロジェクトに格納されます。これらのイメージは他のプロジェクトのアプリケーションで使用され、Compute Engine インスタンスと Docker コンテナを起動します。これらのイメージにアクセスするアプリケーションのプロジェクトは、それぞれイメージビルダ プロジェクトの [表示可能] 権限を持っている必要があります。前のセクションで定義したプロセスに従い、今回は、次の図に示すように、アプリケーションのプロジェクトごとに計算サービス アカウントを配置し、これを [表示可能] 権限を持つメンバーとしてイメージビルダ プロジェクトに追加します。

表示可能権限を持つ他のプロジェクトのイメージビルダ プロジェクトへの追加。

Jenkins のバックアップとリストア

Jenkins リーダーには、Google Cloud Storage に Jenkins 設定とジョブ履歴を定期的にバックアップするために事前に定義されたジョブが含まれています。デフォルトでは、このジョブは、次の図のように定期的(平日の毎日 2 時間に 1 回)に実行されます。

Jenkins リーダーでの自動ビルド設定。

ジョブのビルドステップは、シークレット、ユーザー、ジョブ、履歴を tarball にアーカイブするシェル スクリプトを実行します。作成されるアーカイブには次の 2 つのコピーがあります。1 つは日付スタンプで名前が指定され、もう 1 つは LATEST という名前で、最新のバックアップを自動で簡単にリストアできます。このステップは、次の画像のように、バックアップ対象項目を追加したり、削除したりして、カスタマイズすることができます。

ビルド スクリプトのカスタマイズ方法。

ビルド後の処理では Google Cloud Storage Plugin と、作成した Google メタデータ認証情報を使用して、Google API とやり取りし、バックアップ アーカイブを Google Cloud Storage にアップロードします。日付スタンプのアーカイブと LATEST アーカイブの両方がアップロードされます。次の画像はステップの定義を示しています。

ビルド後の処理を定義するインターフェース。

次の画像は複数のバックアップが蓄積されたバケットを示しています。

プロジェクトの累積バックアップのリスト。

バックアップのリストア

環境変数を使用して Nginx リバース プロキシでの SSL または基本認証を有効にする場合と同様に、環境変数を使用して Jenkins リーダーのレプリケーション コントローラ定義を設定し、サービスが開始されたときにバックアップをリストアできるようにします。次のコードは、レプリケーション コントローラの定義のスニペットです。

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

Jenkins リーダーの Docker イメージは、開始時に GCS_RESTORE_URL 環境変数が存在しているかどうか確認します。存在している場合、その値がバックアップの URL と見なされます(gs:// scheme を含む)。スクリプトは Jenkins リーダー イメージにインストールされた gsutil コマンドライン ツールを使用して、バックアップを安全にダウンロードし、リストアします。

リストア プロセスが発生するのは、コンテナが起動されたときだけです。Jenkins リーダーの起動後にバックアップをリストアする場合、Jenkins リーダーのレプリケーション コントローラのサイズを 0 に変更し、コントローラの定義をバックアップの URL を示すように更新してから、サイズを 1 に戻します。これはチュートリアルで説明します。

チュートリアル

手順やソースコードなど、チュートリアルのすべての内容は、GitHub の https://github.com/GoogleCloudPlatform/kube-jenkins-imager にあります。

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...