Help us understand the problem. What is going on with this article?

AWS上で動くKubernetesクラスタのためのCI/CDパイプライン

  • いろいろと苦戦したのでまとめます

前提

  • クラウドはAWS
  • ソース管理はCodeCommit
  • ビルド環境はCodeBuild
  • 同じAWSのアカウント上で稼働しているKubernetesクラスタがデプロイターゲット
  • マニフェストファイルはHelmチャートで管理
  • アプリケーション毎にHelmチャートのリポジトリが存在する
  • Spinnakerが使える状態にある

却下案1(CodePipelineですべて管理)

  • CodeCommit
  • CodeBuild(ビルド用)
  • CodeBuild(デプロイ用)

_無題.png

概要

  • 大前提として、AWSにはKubernetesにデプロイするサービスが無い
  • そのため、デプロイの実装 or デプロイツールの構築が必要になる
  • ここではデプロイ用のCodeBuildを作成し、BuildSpecにデプロイコマンドを記載(Helmを使用しているのでHelmコマンド)
  • デプロイ用のBuildSpecもアプリ毎に作る必要がある
  • helm install(初回のデプロイ)とhelm upgrade(2回目以降のデプロイ)を使い分けないといけない
    • これはHelmを使わなければ回避できる
  • CodeBuildの本来の使用用途に合ってない感がすごい

却下案2(全てSpinnakerで管理)

  • CodeCommit
  • Spinnaker
    • CodeCommitへのPushをトリガーにAPIでSpinnakerのパイプラインをスタート
    • API(POST)→LambdaでCodeBuildを叩き、随時API(GET)→LambdaでCodeBuildのStatusを取得
      • Spinnakerのステージとしては「Webhook」で作成する
    • StatusがSucceededになったらのデプロイ
      • Spinnakerのステージとしては「Bake」と「Deploy」

無題.png

概要

  • CDツールのSpinnakerだが、こちらでBuildの状況なども管理するやり方(こちらも本来の使い方とは違うと思いますが)

  • アプリ毎にAPI GatewayとLambda3つ(パイプライン開始用、CodeBuild開始用、CodeBuildのStatus取得用)を作成する必要がある、、、管理したくない

採用案

  • CodePipeline(CI)
    • CodeCommit
    • CodeBuild
  • Spinnaker(CD)

概要

  • 却下案1と却下案2の良いところを合わせたような構成
  • CodePipelineでCodeBuildでのビルドまでを管理し、CDはSpinnakerで実行
  • SpinnakerのパイプラインでトリガーをWebhookに設定しておき、CodeBuildの最後にそのURLを叩く
  • 開発環境・本場環境で別々のCodeBuildを作成し、それぞれのCodePipelineを定義する

__無題.png

CI(開発環境)

CodeBuildの作成

  • CodeBuildでやりたいことは以下の通り

    • ソースコードのビルド、テスト
    • Dockerイメージのビルド、プッシュ
    • S3へHelmChartをアップロード
      • S3経由でSpinnakerに渡すため
    • Spinnakerのパイプライン開始
  • CodeBuildの設定

    • 対象のソースコードと、Helmチャートが格納されているリポジトリのdevelopブランチを「Source」に指定する
    • 公式で提供されているCodeBuild用のDockerイメージ を基に、以下のパッケージを追加
      • helm
      • yq
        • yamlを操作するツール
    • NAT Gatewayが作成されているSubnetを含むVPC内で稼働させる
      • Spinnakerに紐付くセキュリティグループにこのIPアドレスを指定してパイプラインを始められるようにするため
    • HelmチャートをアップロードするS3バケットをArtifactsに指定しておく

Buildspec

  • コメントで概要を記載
buildspec.yaml
version: 0.2

env: 
  variables: 
     REGION_NAME: "ap-northeast-1" 
     REPOSITORY_NAME: "test" 
     BRANCH_NAME: "develop" 
     ECR_REPOSITORY: "XXXXXXXXXXX.dkr.ecr.$REGION_NAME.amazonaws.com/xxxxxxx" 
     # Helmチャートの名前
     CHART_NAME: "test" 
     # Helmチャートをアップロードするバケット内のパス
     ARTIFACT_PATH: "test-dev"
     # Spinnakerのパイプラインでトリガーに設定しているURL
     URL : "deploy-test-dev" 

phases:
  install:
    # Dockerデーモンの起動
    runtime-versions:
      docker: 18
    commands: 
      - nohup /usr/local/bin/dockerd --host=unix:///var/run/docker.sock --host=tcp://0.0.0.0:2375 --storage-driver=overlay&  
      - timeout 15 sh -c "until docker info; do echo .; sleep 1; done"
  pre_build: 
    commands:
      - $(aws ecr get-login --no-include-email --region $REGION_NAME) 
  build:
    commands:
    # - ソースのビルドがあればここに記載
      # CODEBUILD_SRC_DIRは後述
      - cd $CODEBUILD_SRC_DIR
      - docker build -t tmp:latest . 
  post_build:
    commands:
      # CODEBUILD_RESOLVED_SOURCE_VERSIONは、CodeBuildの環境にデフォルトで設定されている環境変数
      # 対象のソースのコミットIDが設定されている
      # ここではビルドするDockerイメージのタグとして使用する
      - COMMIT_ID_APP=`echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-8`
      - docker tag tmp:latest $ECR_REPOSITORY:$COMMIT_ID_APP
      - docker push $ECR_REPOSITORY:$COMMIT_ID_APP
      # CODEBUILD_SRC_DIR_infは後述
      - cd $CODEBUILD_SRC_DIR_inf
      # Helmチャートのvalues.yamlにデプロイ対象のDockerイメージのタグが記載されている
      # これを先ほど取得したコミットIDに書き換え
      # 更新ができなかったので、一時的にtmp.yamlという名前で保存→既存のvalues.yamlと置き換えを行なっている
      - yq -y '.image.tag |=  "'$COMMIT_ID_APP'"' $CHART_NAME/values.yaml  > $CHART_NAME/tmp.yaml 
      - mv $CHART_NAME/tmp.yaml  $CHART_NAME/values.yaml
      # packageコマンドでtgz形式に変換
      # この形式でSpinnakerに渡すことができる
      - helm package $CHART_NAME
      # S3にアップロード
      - aws s3 cp $CHART_NAME*.tgz s3://[backet name]/$ARTIFACT_PATH/$CHART_NAME.tgz
      # Spinnakerのパイプラインを開始
      - curl https://XXXXXXXXXX.com/webhooks/webhook/$URL -X POST -d "{ }" -H "content-type:application/json" 

CODEBUILD_SRC_DIRについて

  • CodeBuildでは、Sourceに指定したソースコードやファイルがCodeBuildの環境にマウントされている
  • CODEBUILD_SRC_DIRにはSource1に指定したSourceのパスが設定されており、例えばリポジトリを設定しておけば、cd $CODEBUILD_SRC_DIRコマンドでそのレポジトリのソースが格納されたパスに移動できる
  • 2つ以上のSourceを指定することもでき、2つ目以降はSource identiferを指定する。ここでは2つ目のSourceにinfというSource identiferを指定したため、CODEBUILD_SRC_DIR_infにそのSourceのパスが設定されている。つまり、CODEBUILD_SRC_DIR_[自分で設定したidentifer]という環境変数で呼び出すことができる

CodePipeline

  • 対象のCodeCommitのリポジトリのdevelopブランチと対象のCodeBuildを指定して保存
  • これにより、developブランチへのpushをトリガーにこのパイプラインか開始される

CD(開発環境)

Spinnakerでのパイプランの作成

  • Application→Actions→「Create Application」からApplicationを作成

スクリーンショット 2019-09-10 8.52.52.png

  • 作成したApplicationの画面に遷移し、PIPELINES→「Configure a new pipeline」をクリック

スクリーンショット 2019-09-10 8.59.14.png

  • パイプラインの名前を入力してCreate

スクリーンショット 2019-09-12 8.09.04.png

Configuration

  • 最初にConfigurationの画面が表示されるので必要な設定を行う

Automated Triggers

  • パイプラインを開始するトリガーを設定する
  • 今回はCodeBuildからURLを叩いて開始したいので、「Webhook」を指定する
  • 「Source」にCodeBuildで指定した文字列を入力する

スクリーンショット 2019-09-12 8.12.26.png

Expected Artifacts

  • Spinnakerに渡したいArtifactを指定する
  • 今回はCodeBuildでS3にアップロードしたHelmチャートを使用したいのでそのパスを入力する
  • 「Use Sefault Artifact」にチェックを入れ、同じパスを入力する

スクリーンショット 2019-09-12 8.18.50.png

ステージ追加

  • 「Add stage」を押してステージを追加する
    スクリーンショット 2019-09-12 8.22.31.png

  • 今回必要なステージは2つ

    • Bake (Manifest)
      • Helmチャートを読み込ませてマニフェストファイルを生成する
    • Deploy(Manifest)
      • Bakeステージで作成したマニフェストファイルをKubernetesクラスタに適用する

Bake (Manifest)

Bake (Manifest) Configuration

  • Template Render
    • リソースの名前やNamespaceを指定する
  • Template Artifact
    • パイプラインのConfigurationで指定したArtifactがプルダウン形式で選択できる
  • Overrides
    • Helmチャートのvalues.yamlで上書きしたい値がある場合はここで指定できる スクリーンショット 2019-09-12 8.27.04.png

Produces Artifacts

  • Base64を指定する
  • 名前を適当に入力

スクリーンショット 2019-09-12 8.33.05.png

Deploy (Manifest)

  • Bakeステージが終わってから始めるので「Depends On」に該当のステージ名を入力する
  • パラレルで複数のステージを実行することもできる

スクリーンショット 2019-09-12 8.34.47.png

Deploy (Manifest) Configuration

  • Manifest Configuration
    • Artifactを選択して、Bakeステージで生成したArtifactをプルダウンから選択する

スクリーンショット 2019-09-12 8.37.24.png

  • 「Save Changes」を押下して完了!
  • 作成したパイプラインが表示されている

スクリーンショット 2019-09-12 8.48.02.png

CI(本番環境)

CodeBuildの作成

  • CodeBuildでやりたいことは以下の通り
    • 開発環境のパイプラインで作成したHelmチャートをS3から取得し、対象のDockerイメージのタグを取得する
    • 本番環境用のHelmチャートに取得したDockerイメージのタグを上書きする
    • 編集したHelmチャートをS3へアップロード
    • Spinnakerのパイプライン(本番環境用)開始
  • CodeBuildの設定
    • 基本的には開発環境用に作成したCodeBuildと同じ
    • 対象のソースコードと、Helmチャートが格納されているリポジトリのmasterブランチを「Source」に指定する

Buildspec

  • コメントで概要を記載
buildspec.yaml
version: 0.2 

env: 
  variables: 
     REPOSITORY_NAME_INF: "test_inf" 
     BRANCH_NAME: "master" 
     CHART_NAME: "test" 
     ARTIFACT_PATH: "test" 
     URL : "deploy-test-prod" 

phases: 
  post_build: 
    commands: 
      # developのパイプラインで作成したHelmチャートをS3から取得し、対象のDockerイメージのタグを取得する
      - cd $CODEBUILD_SRC_DIR_dev 
      - tar -xvf $CHART_NAME.tgz 
      - CURRENT_COMMIT_ID=`yq -r '.image.tag' $CHART_NAME/values.yaml` 
      # 本番環境用のHelmチャートに取得したDockerイメージのタグを上書きする
      - cd $CODEBUILD_SRC_DIR_inf 
      - yq -y '.image.tag |=  "'$CURRENT_COMMIT_ID'"' $CHART_NAME/values.yaml  > $CHART_NAME/tmp.yaml  
      - mv $CHART_NAME/tmp.yaml  $CHART_NAME/values.yaml 
      # packageコマンドでtgz形式に変換
      - helm package $CHART_NAME 
      - mv $CHART_NAME*.tgz $CHART_NAME.tgz 
      # S3にアップロード
      - aws s3 cp $CHART_NAME*.tgz s3://[backet name]/$ARTIFACT_PATH/$CHART_NAME.tgz
      # Spinnakerのパイプラインを開始
      - curl https://XXXXXXXXXX.com/webhooks/webhook/$URL -X POST -d "{ }" -H "content-type:application/json" 

Sourceの設定

  • CodeBuildのSourceについてはCODEBUILD_SRC_DIRについてを参照
  • CodeBuildのSourceには以下を指定している
    • Source identifer:無
      • 該当リポジトリのmasterブランチ
    • Source identifer:dev
      • 開発環境用のパイプラインでアップロードしたHelmチャートが格納されているバケット
    • Source identifer:inf
      • Helmチャートが格納されているリポジトリのmasterブランチ

CodePipeline

  • 対象のCodeCommitのリポジトリのmasterブランチと対象のCodeBuildを指定して保存
  • masterブランチへのpush(merge)をトリガーにこのパイプラインか開始される

CD(本番環境)

  • 本番環境用のパイプラインをSpinnaker上で作成
  • 作成方法は開発環境用に作成したものと同様
  • 変更する点は以下
    • Triggerに設定するURL(CodeBuildeの最後に叩いているURLとあわせる)
    • Expected Artifactsに指定するパス
    • デプロイターゲットとなるKubernetesクラスタ

まとめ

  • AWS上のKubernetsクラスタにデプロイするためのパイプラインを作成
  • リポジトリのpushとCodeBuildeでのビルドはCodePipelineで管理し、KubernetesクラスタへのデプロイはSpinnakerで管理する方法を紹介
  • 改善点があれば随時更新予定!
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away