ツナワタリマイライフ

日常ネタから技術ネタ、音楽ネタまで何でも書きます。

PipeCD で Google Cloud Run を Canary Release する

Cloud Run とは

GCP が提供するサーバレスのコンピュートサービス。コンテナイメージを指定してデプロイすると、url が割り当てられ、http や grpc のリクエストが受けられる。

スケーリングも勝手に行えるので、Kubernetes でいう HPA や Cluster Autoscaler に気を遣う必要がない。

PipeCD とは

GitOps で Deploy できる Deploy tool。

過去回。

blog.chaspy.me

blog.chaspy.me

blog.chaspy.me

前回までは Terraform を試していたが、今回は Cloud Run を Deploy してみる。

事前準備

http request に対して hello を返す go のアプリを書き、CI で Artifact Registry に push するところまで整えた。

PipeCD は「Deploy」をするツール(CDだし)なので、それ以前の Image Build まではやる必要がある。

blog.chaspy.me

github.com

PipeCD の準備

PipeCD は Example の「canary」を参考にし、ほぼそのまま使った。

github.com

service.yaml はこんな感じ。

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: canary
spec:
  template:
    metadata:
      annotations:
        autoscaling.knative.dev/maxScale: '2'
    spec:
      containerConcurrency: 80
      containers:
      - args:
        - server
        image: asia-northeast1-docker.pkg.dev/hello-cloudrun-336811/hello-cloudrun/hello-cloudrun:a50a3aae930b07c5ce46851fd04d4e2278c12c68
        ports:
        - containerPort: 8080
        resources:
          limits:
            cpu: 1000m
            memory: 128Mi

.pipe.yaml はこんな感じ。

# Deployment pipeline with canary strategy.
apiVersion: pipecd.dev/v1beta1
kind: CloudRunApp
spec:
  pipeline:
    stages:
      # Promote new version to receive amount of traffic.
      - name: CLOUDRUN_PROMOTE
        with:
          percent: 10
      - name: WAIT
        with:
          duration: 30s
      # Promote new version to receive amount of traffic.
      - name: CLOUDRUN_PROMOTE
        with:
          percent: 50
      - name: WAIT
        with:
          duration: 30s
      # Promote new version to receive all traffic.
      - name: CLOUDRUN_PROMOTE
        with:
          percent: 100

これは example そのまんまですね。

これを repository に push し、control plane で application を作成する。

f:id:take_she12:20220101145921p:plain

ちなみに CloudRun を Deploy するためには piped 側に Provider の設定が必要。

こういう piped.yaml を使って

apiVersion: pipecd.dev/v1beta1
kind: Piped
spec:
  projectID: pipecd
  pipedID: piped-id
  pipedKeyData: piped-key
  apiAddress: control-plane-fqdn:443
  webAddress: https://contol-plane-fqdn
  syncInterval: 1m

  git:
    sshKeyFile: /etc/piped-secret/ssh-key

  repositories:
    - repoId: chaspy-test
      remote: git@github.com:org/repo.git
      branch: main

  chartRepositories:
    - name: pipecd
      address: https://charts.pipecd.dev

  cloudProviders:
    - name: kubernetes-default
      type: KUBERNETES
    - name: terraform-dev
      type: TERRAFORM
      config:
        vars:
          - "project=pipecd-dev"
    - name: cloudrun-dev
      type: CLOUDRUN
      config:
        project: hello-cloudrun-336811
        region: asia-northeast1
        credentialsFile: /etc/piped-secret/cloudrun-sa
    - name: lambda-dev
      type: LAMBDA
      config:
        region: ap-northeast-1
        profile: default
    - name: ecs-dev
      type: ECS
      config:
        region: ap-northeast-1
        profile: default

  secretManagement:
    type: KEY_PAIR
    config:
      privateKeyFile: /etc/piped-secret/secret-management-private-key
      publicKeyFile: /etc/piped-secret/secret-management-public-key

こういうふうにいくらかは local file を渡して helm で deploy している。

#!/bin/bash
helm upgrade -i dev-piped pipecd/piped \
    --version=v0.22.0 \
    --namespace=pipecd \
    --set-file config.data=piped.yaml \
    --set-file secret.data.ssh-key="/Users/chaspy/.ssh/id_rsa" \
    --set-file secret.secretManagementKeyPair.publicKey.data=./public-key \
    --set-file secret.secretManagementKeyPair.privateKey.data=./private-key \
    --set-file secret.data.cloudrun-sa=cloudrun/canary/sa.json

GCP 側の準備

Cloud Run の API の有効化などは必要だと思うが、必要なのは Service Account とその Key。

もともと Artifact Registry に Push するための Service Account があったので、それに Key を発行しました。

権限としては Cloud Run 管理者サービス アカウント ユーザー の2つのロールをつけています。

Cloud Run は多分管理者じゃなくてもっと小さくてもいいと思う。

特にハマったのがここで、

cloud.google.com

servive accout user role は

iam.serviceAccounts.actAs
iam.serviceAccounts.get
iam.serviceAccounts.list
resourcemanager.projects.get
resourcemanager.projects.list

を持っていて、特に iam.serviceAccounts.actAs が必要。これは何かというと、特定のサービスにおいては、SA がそれらのサービスを触る権限に加えて、その SA を使うことそのものを許可する必要があるようだ。

以下のエラーでハマっていた。(しかもなぜかこの時の ID が compute engine default account がでていてさらに混乱した。ci-build という別の ID の SA を使っていたのに。)

googleapi: Error 403: Permission 'iam.serviceaccounts.actAs' denied on service account 1111111111111-compute@developer.gserviceaccount.com (or it may not exist)., forbidden

cloud.google.com

によると App Engine とか Cloud Composer がそうだと言っているように見えるけど Cloud Run は書いてない。まぁとにかく最近のサービスはこれが必要ということにしておこう。

Let's Deploy

こういう Image を更新する PR を作ります。

f:id:take_she12:20220101161549p:plain

初回だったので plan-preview は何も言っていません。

f:id:take_she12:20220101161635p:plain

初回デプロイ終了後はこういう状況で、

f:id:take_she12:20220101161716p:plain

PR をマージすると PipeCD の UI で Deploy が進んでいます。CloudRunApp で設定したように、10% -> 30秒待つ -> 50% -> 30秒待つ -> 100% という風に Rollout していきます。

f:id:take_she12:20220101161825p:plain

10% Deploy 完了

f:id:take_she12:20220101161838p:plain

さらに Pipeline は進んで

f:id:take_she12:20220101161850p:plain

50% 完了

f:id:take_she12:20220101161900p:plain

最後まで終わりました

f:id:take_she12:20220101161913p:plain

最終バージョンにトラフィックが 100% 割り振られています。

f:id:take_she12:20220101161921p:plain

Cloud Run が持つ Canary Release の仕組み

PipeCD がこのような Canary Release をなぜ実現できるのかというと、そもそも Cloud Run 自体が Traffic Splitting の機能を持っているからです。

Cloud Run の実態は Knative が動いているようで、今回用意したservice.yaml もそうですが、Cloud Run を Deploy するとそれを表現した yaml を確認することができます。

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: canary
  namespace: '111111111111'
  selfLink: /apis/serving.knative.dev/v1/namespaces/111111111111/services/canary
  generation: 3
  creationTimestamp: '2022-01-01T03:18:48.557008Z'
  labels:
    cloud.googleapis.com/location: asia-northeast1
  annotations:
    serving.knative.dev/creator: ci-build@hello-cloudrun-111111.iam.gserviceaccount.com
    serving.knative.dev/lastModifier: ci-build@hello-cloudrun-111111.iam.gserviceaccount.com
    run.googleapis.com/ingress: all
    run.googleapis.com/ingress-status: all
spec:
  template:
    metadata:
      annotations:
        autoscaling.knative.dev/maxScale: '2'
    spec:
      containerConcurrency: 80
      timeoutSeconds: 300
      containers:
      - image: asia-northeast1-docker.pkg.dev/hello-cloudrun-336811/hello-cloudrun/hello-cloudrun:a50a3aae930b07c5ce46851fd04d4e2278c12c68
        args:
        - server
        ports:
        - name: http1
          containerPort: 8080
        resources:
          limits:
            cpu: 1000m
            memory: 128Mi
  traffic:
  - revisionName: canary-a50a3aae930b07c5ce46851fd04d4e2278c12c68-246c0b4
    percent: 50
  - revisionName: canary-787644e4f49946792ff4044b7da399874a377269-d60586c
    percent: 50
status:
  observedGeneration: 3
  conditions:
  - type: Ready
    status: 'True'
    lastTransitionTime: '2022-01-01T03:34:57.321507Z'
  - type: ConfigurationsReady
    status: 'True'
    lastTransitionTime: '2022-01-01T03:34:57.321507Z'
  - type: RoutesReady
    status: 'True'
    lastTransitionTime: '2022-01-01T03:34:54.385969Z'
  latestReadyRevisionName: canary-n9jvd
  latestCreatedRevisionName: canary-n9jvd
  traffic:
  - revisionName: canary-a50a3aae930b07c5ce46851fd04d4e2278c12c68-246c0b4
    percent: 50
  - revisionName: canary-787644e4f49946792ff4044b7da399874a377269-d60586c
    percent: 50
  url: https://canary-jqu5pzsjea-an.a.run.app
  address:
    url: https://canary-jqu5pzsjea-an.a.run.app

apiVersion: serving.knative.dev/v1Service という kind に traffic というものが存在し、これで Revision に対するトラフィックの割合を指定できます。PipeCD は CloudRunApp に従ってこれを操作していると思われます。

kind は他に Route, Revision, Configuration があるようです。

knative.dev

Test in Production

Cloud Run は Traffic 0% でデプロイして、タグを付与することで特別な url でアクセスすることができます。本番の実トラフィックに影響を与えることなく、本番相当の環境でテストできる Test In Production を実現できます。

例えば新規でデプロイした Revision に test タグをつけると、https://test---canary-jqu5pzsjea-an.a.run.app/ という url でアクセスが可能です。

この機能は Knative の機能ではなく Cloud Run の拡張機能のようで、PipeCD 側からもこの設定をすることは残念ながらできないようです。

これ自体は gcloud run deploy で実現可能です。--tag と --no-traffic を組み合わせれば Preview URL を発行することができます。

cloud.google.com

PipeCD は Analysis という、対象の url へのヘルスチェックの機能があるため、個人的にはせっかく CloudRun が 0% Deploy & preview url (tag) をサポートしているので、0% でリリースし、そこへアクセスして成功率が問題ないことを確認しながら徐々にロールアウト、ということが実現できればかなり安全にアプリケーションのリリースができるだろうと思いました。

pipecd.dev

他のデプロイ方式との比較

もっともストレートなのは Google Cloud Build を使った継続的デプロイだと思います。 それ以外にも GitHub Actions でも gcloud auth/cli を使うための actions が用意されている&Workload identity Federation もあることから、良い選択肢かと思います。Image の Build と Push は https://github.com/chaspy/hello-cloudrun でしたように簡単ですし、デプロイは gcloud run deploy をすれば実現可能です。

PipeCD を使う優位性は WAIT や APPROVE を使った Pipeline が実現可能という点だと思います。また、ANALYSIS によって、徐々に展開しながらエラーが増えたらすぐ戻すということも実現できます。 GitHub Actions などで自前でやる場合は、Rollout の Promotion は別途何か自分で仕組みを書いてあげる必要があります。

おわりに

Cloud Run の準備に手間取りましたがよくできていると思いましたし、PipeCD が Canary Release をサポートしている Cloud Run のサポートに目をつけたのは賢いなと思いました。