ツナワタリマイライフ

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

ArgoCD における secret management

argo-cd.readthedocs.io

これを読んだメモ。

Bitnami Sealed Secrets

SealedSecret という CRD に encrypt value を書いて、これを git にホストする。 kubeseal という components がこれをもとに decrypt して k8s secret を生成する。

アーキテクチャはシンプルで、かなり汎用的に使える Kubernetes の secret management tool のようだ。

GoDaddy Kubernetes External Secrets

Deprecated This project has been deprecated. Please take a look at ESO (External Secrets Operator) instead https://github.com/external-secrets/external-secrets

PR 出した。

github.com

External Secrets Operator

github.com

外部のサービスに保存されている Secret を API で取得して kubernetes secret を inject する。

external-secrets.io

resource model に high-level architecture がのっていてわかりやすい。

external-secrets.io

secretStore というリソースが Provider になっている。

汎用的なので複数の Secret Provider を使うなら良い選択肢だろう。

Hashicorp Vault

www.vaultproject.io

kuberentes はこっち。

www.vaultproject.io

正直よくわからなかった。

learn.hashicorp.com

を見るに、http server として vault server が立って、そこに secret を問い合わせる雰囲気があったが、それであれば application に手を入れないといけなくなるのでよくわからない。

Banzai Cloud Bank-Vaults

github.com

Hasicorp vault を使ってなんとかする、ぐらいしか読み取れなかった。

Helm Secrets

github.com

README 眺めて結局何ができるのかよくわからず。

Kustomize secret generator plugins

github.com

これもよくわからず。kustomize の secret generator を go の plugin で使う例を書いてるっぽいが...

aws-secret-operator

github.com

AWS secrets manager から値をとりに行って、AWSSecret という CRD を通じて実際の k8s secret を生成してくれる。

AWS にしか secret がないなら良い選択肢。

KSOPS

github.com

kustomize plugin.

SOPS というのは暗号化のためのツール: Secrets OPerationS

github.com

あんまりよく掴めてないけど、ArgoCD にアドオンして、kustomize を紐解くときの処理に secret を作れるようにするってことかな...

argocd-vault-plugin

github.com

document

argocd-vault-plugin.readthedocs.io

ArgoCD が secret を apply するときに、 を書き換える。

proivider は AWS secret manager を指定することもでき、ArgoCD のプラグインとして動作する。

operator を稼働させなくて良い点がメリットか。

argocd-vault-replacer

github.com

こちらも vault-plugin と似ているように見える。

暗号化に hasicorp vault を使い、かつ external provider をサポートしてない点が違うかな?

まとめ

AWS Secrets Manager に Credential が格納されている場合、External Secrets Operator か aws-secret-operator か argocd-vault-plugin が選択肢になる。

別の External Secrets Provider を使いたいなら External Secrets Operator、AWS だけでいいなら aws-secret-operator。operator の管理をしたくないなら argocd-vault-plugin が良さそうである。

PipeCD で ECS を Deploy する

Deploy するアプリケーション

8080 で listen して、http request があったら hello を出力する Go のアプリを ECR に push する。

github.com

環境変数 "VERSION" を読み取って、それを出力している。

make docker で build し、make push で ECR に push する。今回はこのアプリケーションを v9 から v10 に更新する Deploy を PipeCD で行う。

PipeCD Application

simple の example を参考にした。

github.com

実際に設定したファイルはこんな感じ。

  • app.pipecd.yaml
apiVersion: pipecd.dev/v1beta1
kind: ECSApp
spec:
  name: simple
  labels:
    piped: chaspy-local
    owner: chaspy
  input:
    serviceDefinitionFile: servicedef.yaml
    taskDefinitionFile: taskdef.yaml
    targetGroups:
      primary:
        targetGroupArn: arn:aws:elasticloadbalancing:ap-northeast-1:655123516369:targetgroup/hello8/71d4c3025713a1ee
        containerName: hello
        containerPort: 8080
  description: |
    chaspy test
  • servicedef.yaml
cluster: arn:aws:ecs:ap-northeast-1:655123516369:cluster/pipecd-dev3
serviceName: hello
desiredCount: 2
deploymentConfiguration:
  maximumPercent: 200
  minimumHealthyPercent: 0
schedulingStrategy: REPLICA
# CAUTION: To enable PipeCD controls the deployment
# DeploymentController of type EXTERNAL is required.
deploymentController:
  type: EXTERNAL
enableECSManagedTags: true
propagateTags: SERVICE
launchType: FARGATE
networkConfiguration:
  awsvpcConfiguration:
    assignPublicIp: ENABLED
    securityGroups:
      - sg-eee24b8b
    subnets:
      - subnet-fb7f36d3
      - subnet-5118f008
      - subnet-1803d46f
  • taskdef.yaml
family: hello
executionRoleArn: arn:aws:iam::655123516369:role/ecsTaskExecutionRole
containerDefinitions:
  - command: null
    cpu: 100
    image: 655123516369.dkr.ecr.ap-northeast-1.amazonaws.com/hello:v9
    memory: 100
    mountPoints: []
    name: hello
    portMappings:
      - containerPort: 8080
    environment:
      - name: VERSION
        value: "9"
compatibilities:
  - FARGATE
requiresCompatibilities:
  - FARGATE
networkMode: awsvpc
memory: 512
cpu: 256
pidMode: ""
volumes: []

Taskdef や Servicedef は AWS でのそれぞれの json に対応している。

pipecd.dev

docs.aws.amazon.com

docs.aws.amazon.com

最初わかりづらかったので Document に PR を送った。

github.com

Control Plane 側 Application を登録する。

これは quick sync を一度したあとの状態。

f:id:take_she12:20220410230525p:plain

piped

なお piped には lambda と同様、credential file を読ませる必要がある。

apiVersion: pipecd.dev/v1beta1
kind: Piped
spec:
(skip)

  cloudProviders:
    - name: ecs-dev
      type: ECS
      config:
        region: ap-northeast-1
        profile: default
        credentialsFile: /etc/piped-secret/ecs-cred

(skip)

helm で deploy する。

#!/bin/bash
set -x
helm repo add pipecd https://charts.pipecd.dev
helm install piped pipecd/piped -n pipecd
helm upgrade -i piped pipecd/piped \
    --version=v0.27.2 \
    --namespace=pipecd \
    --set-file config.data=piped.yaml \
    --set-file secret.data.ecs-cred=ecs/credential.txt

なお credential file はこういう形式である必要がある。

[default] aws_access_key_id = your-access-key-id aws_secret_access_key = your-secret-access-key

Deploy

Image tag を更新する PR を作成

f:id:take_she12:20220411063040p:plain

Merge すると Deploy が完了。

f:id:take_she12:20220411062738p:plain

Task Definition が更新された。

その他の Deploy 方法

今回は単純な "simple" のケースを試したが、PipeCD の Pipeline の機能を使って、Wait Approval - 承認を挟んでからのデプロイや、Canary Release もできる。

Target Group を複数用意することで Weight を変更して実現している。

pipecd.dev

PipeCD 以外の ECS Application の Deploy 方法

手を動かしていない前提で、いくつか選択肢をあげてみる。

AWS CLI

task definition で新しい revision を作成し、service からの参照を変えれば良い。

docs.aws.amazon.com

docs.aws.amazon.com

code deploy を組み合わせることもできるようだ。

docs.aws.amazon.com

AWS Code Deploy

aws.amazon.com

canary release をサポートしているが、割合は用意されたもののみのようだ。

GitHub Actions

CI/CD に組み込むことを考えると GitHub Actions は有効な選択肢の1つ。便利な Action が既にある。

github.com

ecspresso

github.com

基本的には AWS SDK の薄いラッパーだが verify や diff など便利なサブコマンドが提供されている。

Terraform

taskdef と servicedef を更新すれば良いので Terraform でも可能。

registry.terraform.io

まとめ

PipeCD で ECS Application を simple にデプロイする方法を試した。

Piped と controll plane の準備が必要な点は他のデプロイツールにより手間がかかるが、デプロイパイプラインを柔軟に組み立てられる点、特に Canary Release を宣言的に行える点は他のツールにはない差別化ポイントである。

2台の MacBook をクラムシェルで使いボタン2つで切り替える

今更ですがプライベート Mac と会社 Mac、利用用途ごとに分けるべきですが、周辺機器は共有したい。そう思いますよね。

ようやく整ったのでまとめます

どうなった

こうなった。

f:id:take_she12:20220223172801p:plain

f:id:take_she12:20220223171847p:plain

スイッチャー

紆余曲折ありこの2つのスイッチャーを使っている。

amzn.to

amzn.to

CableDeconn のほうは映像、LAN、電源いずれも転送できる。逆にいうと UGREEN の方ではディスプレイがダメだった。まぁ確かにできるとは書いていない。

2回ポチすれば切り替えられる。

f:id:take_she12:20220223173043p:plain

トラックパッドでカツカツ、とダブルクリックすれば起きてくれる。

こだわり

特にないが、

1ポチまでいかなかったが、2ポチでも今まで線繋ぎ直してたときより遥かに体験が良くて満足している。

おまけ:使ってる機器

Logitech Webcam C980OW

amzn.to

これまで内臓のカメラを使っていたが、クラムシェルにするので必要になり買った。

画質も良い。満足している。別に最高モデルが必要だった理由はない。

Blue Microphones Yeti Nano Condenser Microphone Streaming Set [Microphone Stand + Shock Mount Set]

amzn.to

これはだいぶ前から使っている。音質が良い。アームも安定している。

REALFORCE TKL SA for Mac / R2TLSA-US3M-BK

www.realforce.co.jp

これもだいぶ前から使っている。超打ちやすい。最高。

Filco Genuine Wood Wrist Rest

amzn.to

リストレスト。木なのが落ち着く。

Magic Trackpad

www.apple.com

有線の理由は充電が面倒だからです。あと切り替えた時も何も考えずに使えるのでなんだかんだ有線のほうが便利。

Anker PowerExpand

後ろに隠れているけれどこいつが便利。実家用にももう1つ買ったぐらい。

amzn.to

SD カードもつけられるのでカメラの画像をインポートするのにも便利。

そう考えると画面裏ではなく手が届くところに配置した方がいいことに今気づいた。

有線 LAN

はやいんです。有線 LAN 無線 LAN よりべんり。

twitter.com

Mackie CR4-XBT

amzn.to

こいつは Bluetooth で主に iPhone とつないで使います。

これ前のモデル CR4BT を使ってたんですが、壊れてしまい、修理に出したらいくらかお金を出したら新モデルを送ってくれるってシステムがあって、それでモデルチェンジできました。モスシステムさん好き。

https://www.soundhouse.co.jp/products/detail/item/217173/www.soundhouse.co.jp

修理交換プログラムというのがある。

mackie-jp.com

(今もだが)この時特に物をちゃんと長く使う、みたいな意識が高くなっていて、すぐに捨てて新しいものを買うのではなく、お金はかかっても修理して使う、っていうことをしたくて。結果的に元の製品はメーカーに送ることになったけど、自分が粗大ゴミとして出すよりは有効活用してもらえると思うのでやってよかった。

I-O Data EX-LD4K271DB 4K (60Hz) 27-inch Monitor

amzn.to

長く使っている。問題なく使えている。

今後買うもの

ディスプレイアーム

浮かせたい。

27インチ1枚でいくのか、もう少し大きいやつ1枚でいくのか、局面でいくのか、27インチ2枚でいくのか、など悩ましいので結局買えてない。

ディスプレイ構成決まらなくてもあとからアーム買い足したりできるのは知っているが、うーん。

イヤホン

もともとイヤホンジャックにつけてイヤホンは使っていたが、Air Pods の接続もそんなにストレスなさそうなのでしばらくこっちで試す。

Air Pods 接続めんどくさくなったら USB のイヤホン買うかも。

昇降式デスク

引っ越した時に買う。

今の机しなってる。もう長く使っている。

会議は立ってやる、っていう風にしたい。

椅子

良い椅子買いたい。座ってみたい。

おわりに

やっぱりギア買うのは楽しい。

PipeCD で AWS Lambda function を Canary Release する

する。

この example を使う。

github.com

Lambda と Canary Release

AWS Lambda では alias と version という概念によって Canary Release が実現できる。

docs.aws.amazon.com

複数の version の lambda application を deploy しておき、alias の weight を操作することで canary release を実現できる。

PipeCD はこの機能を操作し、PipeCD の持つ Pipeline 設定で実現している。

Cloud Run の例はこちら。

blog.chaspy.me

Container Image を ECR へ Push する

PipeCD の lambda の deploy には3種類の方法がある。

pipecd.dev

  1. container image
  2. zip file archives
  3. source code directly

nodejs や python などの場合、2 や 3 の方法が取れるが、Go の場合、通常ビルド済みバイナリはソースコードに含まないため、実質 1 のみ、あるいはビルドバイナリを zip にして S3 に置く方法で 2 を取ることができるが、面倒である。

一応 Go の Binary は GitHub Release などにアップロードされることがほとんどなので、url 指定で使う方式はどうか、と issue だけは書いた。が、他にそういう需要が増えない限りは実装されないだろう。

github.com

というわけで、Go でやりたかったので container image を ECR に push する。

github.com

hello するだけの function である。

github.com

それをこの辺で build して login して push している。

github.com

ここまでが CI (lambda application) 側の責務。

無事 Push される。

f:id:take_she12:20220215134156p:plain

PipeCD Application の登録

登録する。

f:id:take_she12:20220215134504p:plain

PipeCD Configuration の追加

指定した repository の path に以下のファイルを push

function.yaml

apiVersion: pipecd.dev/v1beta1
kind: LambdaFunction
spec:
  name: SimpleFunction
  role: arn:aws:iam::655123516369:role/service-role/lambda
  image: 655123516369.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-api-gateway:v1
  memory: 512
  timeout: 30
  tags:
    app: simple

app.pipecd.yaml

# Deployment pipeline with canary strategy.
# Using version, alias, additional version to do canary, bluegreen.
# https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html
apiVersion: pipecd.dev/v1beta1
kind: LambdaApp
spec:
  pipeline:
    stages:
      # Deploy workloads of the new version.
      # But this is still receiving no traffic.
      - name: LAMBDA_CANARY_ROLLOUT
      # Promote new version to receive amount of traffic.
      - name: LAMBDA_PROMOTE
        with:
          percent: 10
      - name: LAMBDA_PROMOTE
        with:
          percent: 50
      # Promote new version to receive all traffic.
      - name: LAMBDA_PROMOTE
        with:
          percent: 100

push すると最初の quick sync が走ります。quick sync では現在指定している lambda function を単に deploy します。

f:id:take_she12:20220215134810p:plain

完了すると v1 が deploy されています。

f:id:take_she12:20220215134903p:plain

f:id:take_she12:20220215135028p:plain

f:id:take_she12:20220215135033p:plain

f:id:take_she12:20220215135036p:plain

f:id:take_she12:20220215135039p:plain

v2 を canary release する

PipeCD Configuration がある repository で image を更新します。

commit fd3fac104294eebbfa82beeed705296567b12a61
Author: kondo takeshi <chaspy@users.noreply.github.com>
Date:   Tue Feb 15 13:52:34 2022 +0900

    Deploy v2

diff --git a/lambda/canary/function.yaml b/lambda/canary/function.yaml
index 58b1924..a565222 100644
--- a/lambda/canary/function.yaml
+++ b/lambda/canary/function.yaml
@@ -3,7 +3,7 @@ kind: LambdaFunction
 spec:
   name: SimpleFunction
   role: arn:aws:iam::655123516369:role/service-role/lambda
-  image: 655123516369.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-api-gateway:v1
+  image: 655123516369.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-api-gateway:v2
   memory: 512
   timeout: 30
   tags:

merge すると deployment が作成され、

pipeline が plan されます。

f:id:take_she12:20220215135637p:plain

まずは v2 の function が deploy されます。

f:id:take_she12:20220215135713p:plain

待ちがなかったのであっという間に 100% promotion されてしまいましたw

f:id:take_she12:20220215140031p:plain f:id:take_she12:20220215140034p:plain f:id:take_she12:20220215140037p:plain f:id:take_she12:20220215140039p:plain

WAIT STAGE を追加してみる

これだとあんまりなので、30秒ずつ待ち時間を追加する。

v3 を deploy する。

f:id:take_she12:20220215140844p:plain

ちゃんと WAIT が動作しました。

f:id:take_she12:20220215141319p:plain f:id:take_she12:20220215141322p:plain

というわけで本家 example に PR 出しました。(すぐマージされました、Thank you!)

github.com

他の Deploy tool との比較

他のツールは動かしていませんが、調べた範囲でいくつか紹介しています。

素の aws cli

canary を利用しないのであればこれで十分かもしれません。

github.com

build して zip にして aws lambda update-function-code で upload するシンプルなパターン。

AWS SAM

aws.amazon.com

AWS 製のツールで、CLI が提供される。

yaml を input にして、内部では cloudformation が動くようだ。

段階的デプロイについてのドキュメントは以下。

docs.aws.amazon.com

Deployment Preference Type が 10percent ずつしかないのか、というところが自由度が低い気がしますね。

lambroll

github.com

init で初期化できたり、設定ファイルをコード管理して deploy コマンドで扱えるなど、cli よりわかりやすく、かつシンプルなツールなようです。

すでに lambda が存在している場合に init で設定をダウンロードするのもユーザビリティがあって良いですね。

ただ、canary release には対応していないようです。

fujiwara さんのブログを置いておく。

sfujiwara.hatenablog.com

比較

(他にツールがあれば教えてください)

シンプルと、できる機能の大きさでこういう順番になりそうなので、用途に応じて使いわけるといいでしょう。

aws cli < lambroll < PipeCD < SAM

lambda の deploy だけしたいのであれば lambroll, Canary Release をしたいなら PipeCD か SAM になりますが、PipeCD は Control Plane と Piped が必要なので環境構築が他に比べて少し大変な一方、柔軟な Canary, Analysis, Approval の設定が可能なので、すでに PipeCD を使っている環境については強力な選択肢になると思います。

SAM は Lambda 以外の API Gateway など他のサーバレスサービスをまとめてデプロイする、リッチなツールという位置付けになるかと思います。

入社してから3年半

前回

blog.chaspy.me

半年ごとに書いていたのに1年開いてしまった。てへ。

一言で言うと、この1年は次の飛躍のための助走期間だった、と評価している。するしかないとも言う。

まずアウトプットベースで振り返る。

Blog

この1年で書いた会社ブログはわずか3本。

blog.studysapuri.jp

blog.studysapuri.jp

blog.studysapuri.jp

Talk

意外と多い。8本。

How to add a new lint to tflint-ruleset-aws · GitHub

何をしていたのか

9月までは前1年と変わらず Lead Software Engineer。10月からは Engineering Manager になった。

blog.chaspy.me

コードで問題を解決すること

1月 - 3月の間は Prometheus Exporter を中心に、Go をひたすら書いていた。Go に限らず、コードで問題を解決すること、をひたすらやっていた。

もともとコードを書くことが好きで好きでたまらないというタイプではなく、業務上でも機会が多いとも言えない状況で、「必要な時に高いクオリティでコードを書く」ためにはコードを書くしかない。(自明)

いろいろ書いたが、今でも役に立っているものはいくらかあるし、自分にとってコーディングは「苦手なもの」から、書けば書けるな、という感覚を持てる程度にはなった。(得意とは言っていない)

毎日書く、みたいなことは多分難しいが、それでも今後も定期的に、少なくとも1年に1度は最新技術のキャッチアップも兼ねて、集中してコードを書く期間というものは設けていきたいと思う。

github.com

aws config complience 違反のものを metric として送ることができるので、dashboard で眺めたりアラート設定したりできるようになった。MFA 違反のひとを捕らえるのに今でも役に立っている。

metrics-driven / fact-based で問題に向き合うこと

上記のネタに関連するが、Quipper のバリューの1つであり、あらゆるものを metrics 化してコントローラブルにして、事実ベースで問題に向き合うということを、それができていないところにコードを書き技術で解決する、というアプローチが幾分かできたと思う。

Platform を Product として考えること

6月ぐらいから Platform as Product ということについて考えた。考えただけで特に大きな成果が出たとかではないんだが(Output として登壇はした)

自分たちがやっている仕事、作っている Platform が何のために、誰のためにあるのか、その接続が少なくとも自分の中で明確になったことは大きかったように思う。

この時学んだ Product Management は今でもかなり活きている。また、Technical Product Manager との会話も増え、業務もより円滑に進むことができたと思う。

技術戦略を考えること

4月から立ち上がった技術戦略グループの、DevOps Working Group の Lead をやったことで、DevOps - SRE が実現する手段の1つである、デリバリーの高速化とサービス信頼性をコントローラブルにする取り組みを開発チームと密に連携して課題に向き合うことができた。

この活動によって、プロダクト開発部全体として、DevOps という観点でどういう部分に課題があるのかをより解像度高く知ることができ、かつ SRE Team としての優先順位づけのインプットにもなった。

DX Criteria の実施もその一環だった。

blog.studysapuri.jp

チームをより Sustainable にすること

としてのビジョン・ミッション・バリューは EM の yuya-takeyama が整えたのは大きかった。

blog.studysapuri.jp

それ以外にも、特に4月以降は新メンバーの加入が(嬉しいことに)多く、人が変わっても、むしろ人が入ることでより強くなるチームに進化してきているように思う。

具体的には Retrospective。前からも隔週で行ってきたが、単に書いてしゃべるだけ、だったものから、振り返り自体の振り返りを取り入れ、型に囚われることなく、よさそうな提案はまずはやってみて、ダメならやめる、みたいなことを気持ちよくやることができている。かける時間に対して得られるベネフィットは当初に比べてかなり大きくなってきている。

Working Agreement。働く上でのゆるい合意のようなものを数ヶ月に一度やっている。これにより期待値を擦り合わせることができるし、新メンバーにとってもカルチャーを掴むのによりやりやすくなるし、多様な価値観を受け入れられる土壌になっていると思う。

その他 Daily Standup, Alert Handling, Cost check などルーチンワークをチームでまわすということは1年前、2年前から比べるとはるかに良くなった。これは提案、実行、フィードバックをくれるメンバーのおかげである。感謝。

(Engineering Manager として) 期待値を合わせること

わずか3ヶ月間。やったこととしてはいろいろあるが、重点をおいたのはこの点かなと思う。かなり丁寧にやったつもりだ。

これまで所属していたメンバーで仕事ぶりや個人としての信頼関係はそれなりにあるとはいえ、マネージャが変わるというのはそれなりに身構えてしまうものだと思う。

Engineering Manager README を書いて、何をどれぐらいするのか。しないのか。期待してもらうのか、メンバーに何を期待するのか、ということを書いて、個別に話した。

ミッション設定は本人の Will をもとに、身につけていってもらいたい能力や現状の課題という背景と合わせて言語化し、さらにそれをツリー形式で可視化することでメンバー同士の期待値も明らかにした。

今後はメンバー間の期待値をよりわかるようにする場を設けたり、マネージャへのフィードバックをもらえる仕組みなんかも作っていきたいと思う。

今後やること

EM という成果は3ヶ月という短いスパンで出るものではなく、半年でも多分短い。成果がないことに焦る必要はないが、それでも時間がかかるからこそ種まきはなるべく早くやったほうがいい。

Product Security Engineering Team の立ち上げ

現在 Job Description を書いてコードテストを仕上げているところ。

Product Security Engineer とは、SRE が Reliability を Engineering で Product Team に実装していくのと同じように、Security を Engineering で組織に実装していくポジション。

横断組織であるが、横断の Security 課題を解決したり、単にそれっぽいセキュリティ施策を導入するのではない。

1月中には JD 公開予定です。

プロダクト開発組織をより Sustainable にする

組織開発ともいう。組織課題を解決するともいう。それで目指したいことは Sustainable にするってことかなと。

そのために、コーポレートエンジニアリング的なツールを(セキュリティ的な観点入りで)整備したり、エンジニアのキャリアパスを整備したり、EM 自体が育成する・される環境を作ったり、ミッションマネジメントをより洗練させたりしていく。

ビジネスにエンジニアリングで貢献する

去年も掲げていたけど、できていなかったというか、助走期間だった。

プロダクト開発部がいくら SLO を定めてみても、それにより機能開発と非機能開発の優先度が「数値」によって、異なる職種を通じて話し合い、変更されうる状態になっていない。

そこを変えに行く。

そのために、技術戦略グループのマネジメント/リードをして、技術的負債をコントロール化に置く活動を支援したり、自己診断能力を開発できるようにして現状把握できるようにしたりして、機能開発 - ビジネス開発と同じだけ、非機能開発や技術的負債解消活動にも説明責任が果たせるようにしていく。同じものを見て、異なるストーリーを持つ人々と、1つの目的を達成できるようにする。そのために組織も技術も両方見て世界を変えていく。

今のところは、これが無理だと諦めた時に転職を考えるのかなと思ってます。これをやれると思ってるうちはがんばります。

おわりに

楽しくやりましょう。今年もよろしくおねがいします。

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 のサポートに目をつけたのは賢いなと思いました。

GCP Workload Identity Federation を使って GitHub Actions から Artifact Registry に Image を Push する

した。コードはこれ。

github.com

思い出せる限りにやったこと。

GCP

1. Project 新規作成

2. SA 作成、Artifact Registry Writer Role を付与する。

ミニマムだと artifactregistry.repositories.uploadArtifacts があればいいはず。

cloud.google.com

3. Workflow Identity Federation の設定

cloud.google.com

この記事を参考に、GitHub Actions の ID 連携の設定 セクションで CLI でやってる部分を GUI でいれていった。

プールを1つ作って、プロバイダはこんな感じで作った。

f:id:take_she12:20211231234421p:plain

4. Service Account に WIF provider

#!/bin/bash
PROJECT_ID="hello-cloudrun-111111"
gcloud iam service-accounts add-iam-policy-binding "ci-build@${PROJECT_ID}.iam.gserviceaccount.com" \
  --project="${PROJECT_ID}" \
  --role="roles/iam.workloadIdentityUser" \
  --member="principalSet://iam.googleapis.com/projects/222222222222/locations/global/workloadIdentityPools/github-actions/attribute.repository/chaspy/hello-cloudrun"

これは CLI で設定。これで subject を指定することで repository を絞った。

GitHub Actions

5. Secret 追加

GCP_PROJECT_IDGCP_WIF_ID の2つを secret にした。別に見えてもいいような気もしつつ。

6. GitHub Actions を書く

使ってる Actions はこれら。

github.com

github.com

workflow 定義ははこれをみてくれ。

github.com

artifact registry はこの辺見て動かした。

cloud.google.com

ハマったこと

gcloud auth configure-docker asia-northeast1-docker.pkg.dev の部分で指定する region を間違っていて、push が永遠に成功しなかったが、permission がない、としか言われず原因がわからずハマった。ハマるときは絶対に凡ミスなのである。

復習

WIF (Workload Identity Federation) とはなんなのか

cloud.google.com

GCP 上の IAM を他の Provider から使う仕組みのこと。

AWS および OIDC をサポートしている。

これによって Service Account Key を発行せず、短命の Key を都度発行して認証することができるため、セキュリティ上のリスクを大きく減らすことができる。

テクい例がこちら。

blog.studysapuri.jp

OIDC(OpenID Connect) とはなんなのか

openid.net

なんらかの認証情報によって認証されたユーザに対して、ID トークンを払い出す仕様が標準化されたもの。これによって1度認証すればその ID トークンを「連携」することができる。

OAuth 2.0 に乗っかって動く。

OAuth 2.0 とはなんなのか

oauth.net

なんらかのリソースを操作、あるいは API 実行などを行うためのアクセストークンを要求する方法と応答を標準化したもの。

RFC6749 は読んでないけどだいたいそんな感じ。

JWT とはなんなのか

jwt.io

JSON Web Token のこと。

JSON で、署名ができるので改善確認ができるトークンの仕様で、OAuth で発行されるトークンとして使われる。

GitHub Actions が OIDC をサポートしたとはどういうことなのか

github.blog

そのままであるが、OIDC Provider の機能を持ち、Issuer として token の要求あるいは検証ができるようになったため、対応できる Cloud Provider と ID 連携が可能となった。

つまり

  • GitHub Actions の OIDC サポート、および GCP Workload Identity Federation が OIDC をサポートしていることにより、GitHub Actions から Keyless で GCP Resource にアクセス可能になった

めでたしめでたし。

CloudRun をデプロイしようとしてただけなんだがおかしいな...