Cloud Run とは
GCP が提供するサーバレスのコンピュートサービス。コンテナイメージを指定してデプロイすると、url が割り当てられ、http や grpc のリクエストが受けられる。
スケーリングも勝手に行えるので、Kubernetes でいう HPA や Cluster Autoscaler に気を遣う必要がない。
PipeCD とは
GitOps で Deploy できる Deploy tool。
過去回。
前回までは Terraform を試していたが、今回は Cloud Run を Deploy してみる。
事前準備
http request に対して hello を返す go のアプリを書き、CI で Artifact Registry に push するところまで整えた。
PipeCD は「Deploy」をするツール(CDだし)なので、それ以前の Image Build まではやる必要がある。
PipeCD の準備
PipeCD は Example の「canary」を参考にし、ほぼそのまま使った。
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 を作成する。
ちなみに 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 は多分管理者じゃなくてもっと小さくてもいいと思う。
特にハマったのがここで、
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
によると App Engine とか Cloud Composer がそうだと言っているように見えるけど Cloud Run は書いてない。まぁとにかく最近のサービスはこれが必要ということにしておこう。
Let's Deploy
こういう Image を更新する PR を作ります。
初回だったので plan-preview は何も言っていません。
初回デプロイ終了後はこういう状況で、
PR をマージすると PipeCD の UI で Deploy が進んでいます。CloudRunApp で設定したように、10% -> 30秒待つ -> 50% -> 30秒待つ -> 100% という風に Rollout していきます。
10% Deploy 完了
さらに Pipeline は進んで
50% 完了
最後まで終わりました
最終バージョンにトラフィックが 100% 割り振られています。
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/v1
の Service
という kind に traffic というものが存在し、これで Revision に対するトラフィックの割合を指定できます。PipeCD は CloudRunApp に従ってこれを操作していると思われます。
kind は他に Route, Revision, Configuration があるようです。
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 を発行することができます。
PipeCD は Analysis という、対象の url へのヘルスチェックの機能があるため、個人的にはせっかく CloudRun が 0% Deploy & preview url (tag) をサポートしているので、0% でリリースし、そこへアクセスして成功率が問題ないことを確認しながら徐々にロールアウト、ということが実現できればかなり安全にアプリケーションのリリースができるだろうと思いました。
他のデプロイ方式との比較
もっともストレートなのは 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 のサポートに目をつけたのは賢いなと思いました。