Jenkins を使用した Google Kubernetes Engine への継続的なデプロイ

このチュートリアルでは、次の図のように、Jenkins と Google Kubernetes Engine(GKE)を使用して継続的デリバリー パイプラインを設定する方法について説明します。

jenkins 継続的デリバリーのアーキテクチャ

目標

  • サンプル アプリケーションを理解する。
  • アプリケーションを GKE にデプロイする。
  • コードを Cloud Source Repositories にアップロードする。
  • Jenkins にデリバリー パイプラインを作成する。
  • 開発環境をデプロイする。
  • カナリア リリースをデプロイする。
  • 本番環境をデプロイする。

費用

このチュートリアルでは、Google Cloud の課金対象となる以下のコンポーネントを使用します。

  • Compute Engine
  • Google Kubernetes Engine
  • Cloud Build

料金計算ツールを使うと、このチュートリアルでの予想使用量に基づいて費用の見積もりを出すことができます。 GCP を初めてご利用の場合には、無料トライアルをご利用いただけます。

始める前に

  1. Google アカウントにログインします。

    Google アカウントをまだお持ちでない場合は、新しいアカウントを登録します。

  2. Google Cloud Console の [プロジェクト セレクタ] ページで、Google Cloud プロジェクトを選択または作成します。

    [プロジェクトの選択] ページに移動

  3. Cloud プロジェクトに対して課金が有効になっていることを確認します。プロジェクトに対して課金が有効になっていることを確認する方法を学習する

  4. Compute Engine, GKE, and Cloud Build API を有効にします。

    API を有効にする

環境の準備

  1. GKE での Jenkins の設定のチュートリアルを完了します。GKE で Jenkins が動作していることを確認します。

  2. Cloud Shell でサンプルコードのクローンを作成します。

    git clone https://github.com/GoogleCloudPlatform/continuous-deployment-on-kubernetes
    cd continuous-deployment-on-kubernetes/sample-app
    
  3. Jenkins サービス アカウントに cluster-admin ロールを割り当てます。

    kubectl create clusterrolebinding jenkins-deploy \
        --clusterrole=cluster-admin --serviceaccount=default:cd-jenkins
    

    このチュートリアルでは、Jenkins サービス アカウントに cluster-admin 権限が必要です。これにより、Kubernetes の Namespace と、アプリに必要なその他のリソースを作成できます。本番環境で使用する場合は、必要な権限をカタログ化し、それらを個別にサービス アカウントに適用する必要があります。

アプリケーションを理解する

継続的デプロイ パイプラインにサンプル アプリケーション gceme をデプロイします。このアプリケーションは Go 言語で記述され、リポジトリの sample-app ディレクトリにあります。Compute Engine インスタンスで gceme バイナリを実行すると、アプリはインスタンスのメタデータを情報カードに表示します。

gceme 情報カード

2 つのオペレーション モードに対応するこのアプリケーションは、マイクロサービスに似た機能を提供します。

  • バックエンド モードでは、gceme はポート 8080 でリッスンし、Compute Engine インスタンスのメタデータを JSON 形式で返します。

  • フロントエンド モードでは、gceme はバックエンド gceme サービスにクエリを送信し、結果の JSON をユーザー インターフェースに表示します。

    gceme アーキテクチャ

フロントエンド モードとバックエンド モードでは、次の 2 つの URL をサポートしています。

  • /version は実行中のバージョンを出力します。
  • /healthz はアプリケーションの状態を報告します。バックエンドに接続可能な場合、フロントエンド モードに状態情報が OK として表示されます。

Kubernetes にサンプルアプリをデプロイする

デプロイ環境を記述したマニフェスト ファイルを使用して、gceme のフロントエンドとバックエンドを Kubernetes にデプロイします。このファイルではデフォルトのイメージを使用しています。このイメージはチュートリアルの後半で更新します。

アプリケーションを次の 2 つの環境にデプロイします。

  • 本番環境。ユーザーがアクセスする公開中のサイト。

  • カナリア環境。一定の量のユーザー トラフィックを受信する容量の少ないサイト。この環境では、ライブ環境に公開する前にソフトウェアのサニティ チェックを行います。

まず、アプリケーションを本番環境にデプロイし、実際のコードを使用してパイプラインを設定します。

  1. Kubernetes 名前空間を作成して、デプロイする本番環境を論理的に隔離します。

    kubectl create ns production
    
  2. デプロイするカナリア環境と本番環境、サービスを作成します。

    kubectl --namespace=production apply -f k8s/production
    kubectl --namespace=production apply -f k8s/canary
    kubectl --namespace=production apply -f k8s/services
    
  3. 本番環境のフロントエンドをスケールアップします。

    kubectl --namespace=production scale deployment gceme-frontend-production --replicas=4
    
  4. 本番環境サービスの外部 IP を取得します。ロードバランサの IP アドレスが表示されるまでに数分かかる場合があります。

    kubectl --namespace=production get service gceme-frontend
    

    処理が完了すると、EXTERNAL-IP 列に IP アドレスが表示されます。

    NAME             TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)        AGE
    gceme-frontend   LoadBalancer   10.35.254.91   35.196.48.78   80:31088/TCP   1m
    
  5. 環境変数にフロントエンド サービスのロードバランサの IP を保存します。

    export FRONTEND_SERVICE_IP=$(kubectl get -o jsonpath="{.status.loadBalancer.ingress[0].ip}"  --namespace=production services gceme-frontend)
    
  6. ブラウザでフロントエンドの外部 IP アドレスを開き、両方のサービスが正常に動作していることを確認します。

  7. 別のターミナルを開き、本番環境のエンドポイントの /version URL をポーリングして、次のセクションでローリング アップデートを確認できるようにします。

    while true; do curl http://$FRONTEND_SERVICE_IP/version; sleep 1;  done
    

サンプルアプリのソースコードをホスティングするリポジトリを作成する

次に、gceme サンプルアプリのコピーを作成し、Cloud Source Repositories に push します。

  1. Cloud Source Repositories でリポジトリを作成します。

    gcloud source repos create gceme
    
  2. ローカルの Git リポジトリを初期化します。

    git init
    git config credential.helper gcloud.sh
    export PROJECT_ID=$(gcloud config get-value project)
    git remote add origin https://source.developers.google.com/p/$PROJECT_ID/r/gceme
    
  3. このリポジトリの Git commit にユーザー名とメールアドレスを設定します。[EMAIL_ADDRESS] を Git メールアドレスに、[USERNAME] を Git ユーザー名に置き換えます。

    git config --global user.email "[EMAIL_ADDRESS]"
    git config --global user.name "[USERNAME]"
    
  4. ファイルの追加、commit、push を行います。

    git add .
    git commit -m "Initial commit"
    git push origin master
    

パイプラインを作成する

Jenkins を使用して、gceme のコピーのテスト、ビルド、Kubernetes クラスタへのデプロイに使用するパイプラインを定義、実行します。

サービス アカウントの認証情報を追加する

Jenkins がコード リポジトリにアクセスできるように認証情報を構成します。

  1. Jenkins ユーザー インターフェースで、左側のナビゲーションにある [認証情報] をクリックします。
  2. [認証情報] テーブルの [Jenkins] リンクをクリックします。

    Jenkins 認証情報グループ

  3. [グローバル認証情報] をクリックします。

  4. 左のナビゲーションで [認証情報の追加] をクリックします。

  5. [種類] プルダウン リストから [メタデータからの Google サービス アカウント] を選択します。

  6. [OK] をクリックします。

これで 2 つのグローバル認証情報が作成されました。2 番目の認証情報の名前をメモしてください。チュートリアルの後半で使用します。

Jenkins 認証情報。

Jenkins ジョブを作成する

次に、Jenkins Pipeline 機能を使用してビルド パイプラインを構成します。Jenkins Pipeline ファイルは、Groovy に似た構文で作成されています。

Jenkins のユーザー インターフェースに移動し、次の手順でパイプライン ジョブを構成します。

  1. インターフェースの左上にある Jenkins のリンクをクリックします。
  2. 左側のナビゲーションで [New Item] リンクをクリックします。
  3. プロジェクトに sample-app という名前を付け、[Multibranch Pipeline] オプションを選択して、[OK] をクリックします。
  4. 次のページで [Add Source] をクリックし、[git] を選択します。
  5. Cloud Source Repositories 内にある sample-app リポジトリの HTTPS クローン URL を [Project Repository] フィールドに貼り付けます。[PROJECT_ID] は実際のプロジェクト ID に置き換えます。

    https://source.developers.google.com/p/[PROJECT_ID]/r/gceme
    
  6. サービス アカウントの追加時に作成した認証情報の名前を、[Credentials] プルダウンから選択します。

  7. [Scan Multibranch Pipeline] セクションで、[Periodically if not otherwise run] ボックスをオンにします。[Interval] の値を「1 minute」に設定します。

  8. [保存] をクリックします。

    Jenkins ジョブ設定を作成する

これらの手順を完了すると、「Branch indexing」という名前のジョブが実行されます。このメタジョブはリポジトリのブランチを識別し、既存のブランチで変更が発生していないことを確認します。Jenkins を更新すると、このジョブが master ブランチに表示されます。

次のステップでコードを変更するまで、このジョブは失敗します。

パイプラインの定義を変更する

カナリア環境用のブランチを canary という名前で作成します。

git checkout -b canary

パイプラインを定義する Jenkinsfile コンテナが Jenkins Pipeline Groovy 構文で作成されます。Jenkinsfile を使用すると、ビルド パイプライン全体を 1 つのファイルで表し、ソースコードと一緒に公開できます。パイプラインは並列化などの強力な機能をサポートしていますが、ユーザーが手動で承認する必要があります。

Jenkinsfile を変更し、1 行目にプロジェクト ID を追加します。

カナリア リリースをデプロイする

パイプラインが正しく構成されたら、gceme アプリケーションに変更を加えて、パイプラインでテスト、パッケージ化、デプロイを行うことができます。

カナリア環境はカナリアテストとして設定されます。変更は、本番環境のロードバランサの内側にある小規模な Pod にリリースされます。この操作を Kubernetes で行うには、同じラベルを共有する複数デプロイを維持する必要があります。このアプリケーションの場合、gceme-frontend サービスは、app: gcemerole: frontend のラベルが付いたすべての Pod の負荷分散を行います。k8s/frontend-canary.yaml カナリア マニフェスト ファイルはレプリカを 1 に設定し、gceme-frontend サービスに必要なラベルを含みます。

現在、5 個中 1 個のフロントエンド Pod がカナリア環境のコードで実行され、残りの 4 個は本番環境のコードで実行されます。すべての Pod をロールアウトする前に、カナリア環境のコードがユーザーの操作に悪影響を及ぼすことはありません。

  1. html.go を開き、blue の 2 つのインスタンスを orange で置き換えます。
  2. main.go を開き、バージョン番号を 1.0.0 から 2.0.0 に変更します。

    const version string = "2.0.0"
    
  3. 次に、これらのファイルをローカルのリポジトリに追加し、commit します。

    git add Jenkinsfile html.go main.go
    git commit -m "Version 2"
    
  4. 最後に、リモートの Git サーバーに変更を push します。

    git push origin canary
    
  5. 変更が Git リポジトリに push されたら、Jenkins のユーザー インターフェースに移動し、ビルドが開始していることを確認します。

    Jenkins の最初のビルド画面

  6. ビルドの実行後、左側のナビゲーションのビルドの横にある下矢印をクリックして、[Console Output] を選択します。

    Jenkins コンソールのナビゲーション

  7. ビルドの出力を数分間トラックし、kubectl --namespace=production apply... メッセージが始まるのを待ちます。処理の開始後、本番環境の /version URL をポーリングしていたターミナルを確認し、リクエストの一部の変更を開始して監視します。これで、ユーザーのサブセットに対する変更がロールアウトされました。

  8. 変更がカナリア環境にデプロイされた後、コードを master ブランチとマージして Git サーバーに push することで、残りのユーザーに引き続きロールアウトできます。

    git checkout master
    git merge canary
    git push origin master
    
  9. およそ 1 分後に、sample-app フォルダ内にある master ジョブが開始します。

    Jenkins マスタージョブ

  10. master リンクをクリックして、パイプラインのステージ、合否情報、タイミングに関する情報を表示します。

    Jenkins パイプラインの生成

  11. 本番環境の URL にポーリングを行い、新しいバージョン 2.0.0 がロールアウトされ、すべてのユーザーからのリクエストを処理していることを確認します。

    export FRONTEND_SERVICE_IP=$(kubectl get -o jsonpath="{.status.loadBalancer.ingress[0].ip}" --namespace=production services gceme-frontend)
    while true; do curl http://$FRONTEND_SERVICE_IP/version; sleep 1;  done
    

プロジェクトの Jenkinsfile を見ると、ワークフローを確認できます。

開発ブランチをデプロイする

カナリア環境に直接 push できない複雑な変更が必要になる場合があります。開発ブランチは、公開サイトに統合する前にコードのテストを行う一連の環境です。これらの環境では、アプリケーションがスケールダウンしますが、ライブ環境と同じ方法でデプロイできます。

機能ブランチから開発環境を作成する場合は、ブランチを Git サーバーに push して、Jenkins で環境をデプロイできます。開発のシナリオでは、外部に公開されているロードバランサは使用しません。アプリケーションを保護するには、kubectl プロキシを使用します。プロキシは Kubernetes API で自らの認証を行い、ローカルマシンからのリクエストをクラスタ内のサービスにリダイレクトします。サービスがインターネットに公開されることはありません。

  1. 別のブランチを作成して、Git サーバーに push します。

    git checkout -b new-feature
    git push origin new-feature
    

    新しいジョブが作成され、開発環境は作成中になります。ジョブのコンソール出力の最後に、環境へのアクセス方法が表示されます。

  2. バックグラウンドでプロキシを開始します。

    kubectl proxy &
    
  3. localhost を使用して、アプリケーションがアクセス可能かどうかを確認します。

    curl http://localhost:8001/api/v1/namespaces/new-feature/services/gceme-frontend:80/proxy/
    
  4. このブランチにコードを push し、開発環境を更新します。完了したら、ブランチを canary と統合し、コードをカナリア環境にデプロイします。

    git checkout canary
    git merge new-feature
    git push origin canary
    
  5. コードが本番環境で問題にならない場合には、デプロイを開始する前に、canary ブランチから master ブランチを統合します。

    git checkout master
    git merge canary
    git push origin master
    
  6. 開発ブランチでの操作が完了したら、サーバーからブランチを削除し、Kubernetes クラスタから環境を削除します。

    git push origin :new-feature
    kubectl delete ns new-feature
    

クリーンアップ

このチュートリアルで使用するリソースについて、Google Cloud Platform アカウントへの課金が発生しないようにする手順は次のとおりです。

プロジェクトの削除

課金をなくす最も簡単な方法は、チュートリアル用に作成したプロジェクトを削除することです。

プロジェクトを削除するには:

  1. Cloud Console で [リソースの管理] ページに移動します。

    [リソースの管理] ページに移動

  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。

次のステップ