CI / CD の改善に役立つ? Skaffold & Cloud Deploy
Google Cloud Japan Advent Calendar 2022 5 日目です。
4 日は Kohei さんからの 2022 年の Google Workspace を振り返る でした。
今日は Skaffold と Cloud Deploy を取り上げつつ、CI / CD の改善に役立つかもしれない観点をご紹介します。
CI / CD 改善のテーマ
Advent Calendar 1 日目 は DORA レポートを取り上げました。調査回答者の 17% にあたる、もっともパフォーマンスが優れているチーム群は 1 日に何度もデプロイでき、その失敗率は 15% 未満、障害が起きても 1 時間未満で復旧できる。これが 2022 年です。
そこで今日は
実装例をご紹介します。また「ソフトウェアを安全に届けるための最新動向 2022」ではセキュリティの重要性を再確認しましたが、その中でも重要な
- 厳密に成果物管理をする
という観点でも実践的方法をご紹介します。
利用するサービス & OSS
とはいえまずは、実装に利用するツール Cloud Deploy と Skaffold について触れます。
Cloud Deploy は、GKE や Cloud Run へアプリケーションをデプロイするときに利用できるマネージド サービスです。VPC 内で完結したセキュアな環境でデプロイやテストを行うことができたり、成果物管理・継続的デプロイに特化しているため高速なロールアウトやロールバックが行えたりします。
Skaffold は、Cloud Deploy が内部的に利用しているコンテナ開発・ビルド・デプロイのための OSS です。そのため CI / CD に便利な機能が数々実装されていたり、ローカル開発においては Kubernetes または Cloud Run エミュレータ上でホットリロード・デバッグ・ログ転送・ポートフォワードといった機能を提供してくれたりします。
DORA の 4 つの指標を評価する
DORA の 4 指標
なぜ?
DORA はこの 4 つの指標が組織の IT パフォーマンスと強い関係があり、その改善が重要だと主張していま。個人的には、この 4 指標が優れているとプロジェクトとして安心できる、チャレンジがしやすいという印象もあり、よくその改善をお勧めしています。
どうやって?
細かくプロジェクトに沿った実装をしたければ独自で計測することもできます。
しかし Cloud Deploy を使ってデプロイすれば、その「頻度」や「失敗率」をクラウドに自動計測させることもできます。
画像の上半分、左から test-、staging-、prod- という文字が見えますが、これらはデプロイ先である「ターゲット」を表していて、左から順にアプリケーションがデプロイされます。この時、最後のターゲットに対するデプロイが過去 30 日間どうだったかが指標として集計される仕組みです。
Cloud Deploy × Cloud Run の実装例
Cloud Run はデプロイやトラフィック分割が簡単にできるといった特徴があり、敢えてデプロイを Cloud Deploy に任せる理由はないようにも思えます。ですが、敢えてそうする価値のひとつがこちら、「頻度」や「失敗率」の可視化です。
5 ステップで試せます。
-
Cloud Deploy の設定
パイプラインと、
run:location:
に指定したターゲットを用意します。$ gcloud deploy apply
コマンドを使ってパイプラインを作成しましょう。apiVersion: deploy.cloud.google.com/v1 kind: DeliveryPipeline metadata: name: my-app-pipeline serialPipeline: stages: - targetId: test --- apiVersion: deploy.cloud.google.com/v1 kind: Target metadata: name: test run: location: projects/${PROJECT_ID}/locations/${REGION}
-
Cloud Deploy サービス アカウントの権限を確認
Cloud Deploy はデフォルトで Compute Engine サービス アカウントを使います。これに Cloud Run サービスをデプロイする権限を渡します。
$ gcloud projects add-iam-policy-binding ${PROJECT_ID} \ --member "serviceAccount:$( gcloud iam service-accounts list \ --filter='email~-compute@developer.gserviceaccount.com' \ --format='value(email)' )" \ --role "roles/run.developer"
-
Cloud Run サービスのマニフェストを作成
service.yaml を作ります。
image
の実際の名前は、ローカルでは Skaffold が、クラウドでは Cloud Deploy が Tags の設定に従い、都度自動的に置換してくれます!これ、とても便利です。ということで、マニフェスト上はシンプルな名前(ここではapp
)にしておきましょう。apiVersion: serving.knative.dev/v1 kind: Service metadata: name: my-app spec: template: spec: containers: - image: app
-
skaffold.yaml を作成
プロジェクトのルートフォルダに skaffold.yaml を作ります。
image:
には 3 で指定したマニフェストと一致する名前(app
)を指定しましょう。Cloud Run にデプロイする時はdeploy:
にcloudrun: {}
を設定すれば OK です!apiVersion: skaffold/v3alpha1 kind: Config build: artifacts: - image: app context: src manifests: rawYaml: - service.yaml deploy: cloudrun: {}
-
Cloud Deploy からデプロイ
以下は Skaffold で build & push、Cloud Deploy でマニフェストのレンダリングとデプロイを行う例です。ポイントは
build.out
ファイルで成果物一覧を Cloud Deploy に引き渡しているところです。もともとこれは Skaffold の便利機能なのですが、Cloud Deploy がそれを尊重した挙動をします。$ skaffold build --push \ --default-repo "${REGION}-docker.pkg.dev/${PROJECT_ID}/my-apps" \ --file-output "build.out" $ gcloud deploy releases create "release-v1.0" \ --delivery-pipeline "${PIPELINE_NAME}" \ --build-artifacts "build.out" \ --source .
さて、お気づきでしょうか。手順 5. の時点では
- Cloud Run の要素はない(Kubernetes へのデプロイも同じコマンドで OK)
- ビルドも抽象化され、コンテナでも Jib でも Bazel でも 同じコマンドで
- マニフェストのレンダリングも抽象化され、Helm でも Kustomize でも kpt でも、同じコマンドでデプロイできちゃいます
このように、プロジェクトのソフトウェア スタックに依存しない CI / CD のスクリプト & ローカル開発環境が構成できるようになるのも Skaffold を利用するメリットです。
“デプロイ後の検証” の活用
Cloud Run も、Kubernetes のサービスも、デプロイそのものがうまくいっても、アプリケーションが正常に稼働しているとは限りません。デプロイ後に e2e テストや統合テストといった検証プロセスを導入している方も多いと思います。
Skaffold、そして Cloud Deploy は最近デプロイ後の検証に対応しました。
これを設定することで、もし検証に失敗すると Cloud Deploy 上「ロールアウトに失敗」したことになり、「失敗率」がより正確になり、必要に応じてロールバックを自動化するといったことも可能になります!
2 ステップで使えます。先程の Cloud Run の例に手を加えてみましょう。
-
Cloud Deploy パイプラインの設定
パイプライン定義のターゲットには
strategy:standard:verify: true
を指定します。(ターゲット定義においても、Cloud Run 側に認証を設定している場合はアクセスするためのサービス アカウントを指定しましょう)apiVersion: deploy.cloud.google.com/v1 kind: DeliveryPipeline metadata: name: my-app-pipeline serialPipeline: stages: - targetId: test strategy: standard: verify: true --- apiVersion: deploy.cloud.google.com/v1 kind: Target metadata: name: test run: location: projects/${PROJECT_ID}/locations/${REGION} executionConfigs: - usages: [RENDER, DEPLOY, VERIFY] serviceAccount: ${SERVICE_ACCOUNT_WHICH_CAN_ACCESS_CLOUDRUN}
-
skaffold.yaml を変更
検証内容は skaffold.yaml 内に定義します。
apiVersion: skaffold/v3alpha1 kind: Config build: artifacts: - image: app context: src - image: test context: test manifests: rawYaml: - service.yaml deploy: cloudrun: {} verify: - name: verify-connectivity container: name: http-client image: alpine command: ["/bin/sh"] args: ["-c", "wget ${ENDPOINT_URL}"] - name: verify-integration-test container: name: integration-test-client image: test command: ["./test.sh"]
定義にあたってのポイントはこちら
- 検証したいことは
verify:
以下に複数定義できます - テスト用コンテナをプロジェクト内で定義したい場合は、上記のように
build:artifacts:
とverify:container:
のimage: test
を一致させ、build:artifacts:context:
のフォルダにテストスクリプトと Dockerfile を配置 - 検証プロセスは Cloud Deploy の実行環境で動きます
- アプリケーションへの接続を同じ VPC 内からに制限する場合は プライベート プール を Cloud Deploy のターゲット定義内、workerPool に指定 します
- 検証したいことは
ここまでの機能を実際に試したい方にはチュートリアルがあります!ぜひお試しください。
より厳密な成果物管理
なぜ?
「ソフトウェアを安全に届けるための最新動向 2022」ではサプライチェーン攻撃から、いかに成果物を守るかといった話題をとりあげました。SBOM なども先々重視されるかもしれませんが、攻撃が急増する直近では、まずは以下のような要求が先に来そうです。
- 成果物に脆弱な依存や挙動がなかったという事実を証明する
- 成果物とアプリケーション実行環境の状況を確認する
- 問題があった成果物がデプロイされないことを保証する
どうやって?
成果物に脆弱な依存や挙動がなかったという事実を証明する
ソースコードや依存関係などは Software Delivery Shield で守っていくのがよさそうです。ビルドした成果物が改ざんされていないことを示すためには Binary Authorization が有用です。
Binary Authorization の機能として、任意のイメージに電子署名する仕組みがあるので
これを使って Cloud Build などの CI の中で署名しておき
GKE や Cloud Run では署名があるものだけ、稼働を許可すると行った設定が行なえます。
成果物とアプリケーション実行環境の状況を確認する
Cloud Deploy においては、成果物(バイナリと設定ファイルの組み合わせ)はリリースという単位ですべてバージョン管理されます。また、アプリケーションの実行環境 = ターゲットです。これをうまく使うと
- とある環境にデプロイされている成果物の一覧を得たり
- 逆にとある成果物がデプロイされている環境の一覧を得たりする
ことが容易です。
Cloud Run をデプロイしたときのコマンドを改めてみると
$ gcloud deploy releases create "${RELEASE_NAME}" \
--delivery-pipeline "${PIPELINE_NAME}" \
--build-artifacts "build.out" \
--source .
でした。--build-artifacts
はビルドされたバイナリの一覧、--source
はレンダリング対象となる設定ファイルを指定して release
を作成していますね。結果として、こんな情報がみえるようになります。
上図をよくみるとわかるのですが、
- リリースには複数のバイナリが登録できる(一緒にデプロイしたいマイクロサービスをまとめるといったこともできる)
- ターゲット アーティファクト(設定ファイル)は環境差異を踏まえて、環境分作成されている
という仕組みです。
結構よさそうな粒度じゃないですか?
逆にデプロイ先であるターゲット側の画面からも、現在稼働しているリリースと、過去ロールアウトされたリリースの履歴がみえます。
問題があった成果物がデプロイされないことを保証する
Cloud Deploy ではリリースを破棄することができます。
$ gcloud deploy releases abandon "${RELEASE_NAME}" \
--delivery-pipeline "${PIPELINE_NAME}"
破棄されたリリースは
- プロモーション(後続環境へロールアウト)することはできません
- 破棄されたリリースにロールバックすることはできません
- 破棄は解除できません。リリースを破棄すると、完全に非アクティブになります
という特徴があり、例えば検証した結果不具合や脆弱性が見つかった場合、リスクや被害を最小限に留めることが簡単にできます。もし「デプロイ後の検証」などで問題があった場合は通知を経由してこれを利用いただくのがよさそうです。
まとめ
CI / CD は一度動くようになるとあまり頻繁に改善されない印象です。とはいえ世の中の動向を鑑みると、よりチャレンジしやすい環境を作る / より安全なソフトウェア サプライチェーンを構築するといった観点から、CI / CD も見直し続ける相応の動機はありそうです。やれることはたくさんありますが、まずは
- DORA 4 つの指標を評価・改善する
- 厳密に成果物管理をする
あたりを意識して、Skaffold と Cloud Deploy、Binary Authorization を眺めていただけると幸いです!
6 日は Yuki Suwa さんによる「Google Tag Manager から Cloud Retail にユーザーイベントをリアルタイムに送信するための手引き」です!
Discussion