前書き。
この記事はフューチャー Advent Calendar 2019 11日目の記事です。
Cloud Build/ArgoCDの使い方とか初期セットアップとかは他の記事にたくさん情報がありますので、ちょっと凝った?設定やパイプラインの全体像がよくわからんの。って人向けの記事になればと思います。
Cloud Build/ArgoCDそれぞれ3つづつポイントを挙げて紹介していきたいと思います。
GitOpsって何さ。
当然なのですが、提唱元の記事を見ていただくのが一番早いかと。
ザックリ要約(with Google翻訳)してくれているのですが、
- Kubernetesおよびその他のクラウドネイティブテクノロジーのオペレーティングモデル。コンテナ化されたクラスターとアプリケーションの展開、管理、監視を統合するベストプラクティスのセットを提供します。
- アプリケーションを管理するための開発者エクスペリエンスへの道。エンドツーエンドのCI/CDパイプラインとGitワークフローは、操作と開発の両方に適用されます。
ということです。カタカナが多い・・・。
ArgoCDって何さ。
これも公式から。(with Google翻訳)
- Argo CDは、Kubernetes向けの宣言的なGitOps継続的デリバリーツールです。
- アプリケーションの定義、構成、および環境は宣言型であり、バージョン管理されている必要があります。アプリケーションの展開とライフサイクル管理は、自動化され、監査可能で、理解しやすいものでなければなりません。
宣言型であり、という点がKubernetes(以下k8s)と非常に相性よさげですね。
Cloud Buildって何さ。
指定されたリポジトリ&ブランチへのコードPushをトリガーに各StepをDockerコンテナで処理してイメージビルドやそれに付随する作業を行ってくれるサービスです。
お安いしそれなりに高速なので使わない手はない・・・!(ビルド時のマシンタイプも指定できます。)
個人的見解。
個人的な見解でGitOps/ArgoCDいいね!と思っているのは、
- 開発者はデプロイを全く意識しなくていい。(Git/Githubの操作だけでなんかデプロイされる。)
- アプリケーション部分(テスト/ビルド)とインフラ部分(デプロイ)を疎に繋げられる。
- 他のツールに浮気してもよし(差し替えやすい)。
- 組織体によってリポジトリやリソースの管理者が違う場合でも権限を汚染しない。
- k8sで言うと
kubectl
とかしなくていい。手作業怖すぎ。 - Gitが正義。いつだってそれが現実。(差分検知/自動反映でGitのコードがインフラにある。)
もしあなたの組織 or あなた自身がこれらの悩みに直面しているのであれば、GitOps/ArgoCDを検討するのは大いに価値があるとおもいます。(ArgoCDは他ツールに差し替えてしまっても良いのですし。)
ちなみにテスト&ビルドサービスはCloud Buildでもいいですし、他のメジャーなCIツールで実施してもいいですし、プラガブルなのがとても良いですね。
よくあるフローと今回説明するフロー。
【よくあるフロー】
GitOpsでググると必ずと行っていいほどCIとCDはセットで出てきます。実際そうあるべきだとも思います。
下記のような図ですね。(特定のサービスが出てきますが、他のものでも全然構いません。)
【今回説明するフロー】
CIツールの使い方ーとかそのあたりは本質じゃなかったので、テスト部分を省いてCloud Buildをそのまま使います。
決して検証コードを用意するのがめんd(ry
とはいえ、例えばCircleCIとCloud Buildはどちらもステップごとにコンテナベースで指示を行っていくので、基本的な考え方は同じになるかと思います。
ここでは下記GCP謹製ツールを用います。
- ビルドツール: Cloud Build
- コンテナレジストリ: Cloud Container Registory
- リポジトリ管理ツール:Github(をCloud Source Repositoriesに同期)
- デプロイ先:Google Kubernetes Engine
ちょっと補足なのですが、
Cloud Source Repositoriesはプルリクエストのような機能がないため、Githubでそれを代替しています。
また、後述のArgoCDの権限周りなんかもGithubならではの設定となってます。
あえてリポジトリを同期して使っていますが、実際Github単体でもこのアーキテクチャは動作します。
長くなりましたね。ここから本編に入ります。
CloudBuildでアプリとインフラリポジトリの中継をする。
多分このフローで一番忙しいのはこの子になりますね。アプリ変更検知してビルドしてインフラリポジトリにPRせねばならんので・・・
さて、実際どうやるの、というところですがcloudbuild.yaml
を書いていく形になります。(またYAMLか)
ちなみに、Cloud Buildで利用可能なイメージの一覧はこちらになります。
今回Cloud Buildでポイントにしておきたいのは以下の点です。
- ビルドにキャッシュを使いたい。
- Githubの認証鍵をGCSに生で置きたくない。
- 変更してコミットしてPRとか・・・よくわかんない。
早速行きましょう。
ビルドにキャッシュを使いたい。
Cloud Buildはビルドにキャッシュを利用しない(毎回1から)なので、変更が多いアプリだとかなーりイライラします。
GoとかJavaなら・・・!ですが、PHPとかPython, Rubyあたりは辛いです。。。
ので、キャッシュを使えるようにしましょう。
キャッシュにはkanikoを利用します。(公式ドキュメント)
とっても楽なのが、kanikoを使うと勝手にCloud Container RegistoryにPushまでしてくれる・・・!
steps:
## イメージビルド with kaniko cache(cache-ttl: default=>2w)
- name: 'gcr.io/kaniko-project/executor:latest'
id: 'Build & Push docker image'
args:
- --dockerfile=Dockerfile
- --destination=asia.gcr.io/$PROJECT_ID/hogehoge/app:$SHORT_SHA
- --cache=true
- --cache-ttl=72h
これはCloud Build独自の話ですが、$PROJECT_ID
、$SHORT_SHA
は組み込み変数となっており、宣言するだけで使えます。楽ちん。
こちらも公式ドキュメントがあります。
Githubの認証鍵とかTokenをGCSに生で置きたくない。
認証情報はそのままおいちゃダメ、絶対。
面倒だけれども、Cloud KMSで暗号化したものを配置し、それを復号化して使いましょう。
どっちみち置くんかい!という話ですが、KMSへの権限をIAMで絞れば見ただけでは読めないですよ、ね。
(ベストプラクティスがあれば教えてほしい!)
上のイメージビルドのステップに続いて追記しましょう。
※$_
で始まる変数はCloud Buildに設定する環境変数になります。
取り回しが必要なファイルはvolumes:
でマウントしたディレクトリに保存していくことで後続のコンテナからも参照できるようになります。
# GithubのSSH認証鍵を取得(CloudKMSで暗号化済み)
- name: 'gcr.io/cloud-builders/gsutil'
id: Get Github auth key from GCS
entrypoint: /bin/sh
args:
- '-c'
- |
set -x && \
gsutil cp gs://$_BUCKET_NAME/id_rsa.enc /root/.ssh/id_rsa.enc && \
gsutil cp gs://$_BUCKET_NAME/known_hosts /root/.ssh/known_hosts
volumes:
- name: 'ssh'
path: /root/.ssh
# CloudKMSで暗号化されたSSH認証鍵を復号
- name: 'gcr.io/cloud-builders/gcloud'
id: 'Decrypt ssh key with CloudKMS'
args:
- kms
- decrypt
- --ciphertext-file=/root/.ssh/id_rsa.enc
- --plaintext-file=/root/.ssh/id_rsa
- --location=global
- --keyring=$_KEYRING_NAME
- --key=$_KEY_NAME
volumes:
- name: 'ssh'
path: /root/.ssh
# Git configの設定
- name: 'gcr.io/cloud-builders/git'
id: 'Setup git config'
entrypoint: 'bash'
args:
- '-c'
- |
chmod 600 /root/.ssh/id_rsa
cat <<EOF >/root/.ssh/config
Hostname github.com
IdentityFile /root/.ssh/id_rsa
EOF
volumes:
- name: 'ssh'
path: /root/.ssh
変更してコミットしてPRとか・・・よくわかんない。
すみません、ここはほんとに人によるかと思います・・・
個人的なおすすめはhubを使うことですが、自前でコンテナを作りたくなかったのと、シェルのほうがぱぱっとかけるということから下記はシェルでの実装になっています。
前提条件として、
- 自動更新するのはdeploymentのコンテナタグだけ。(他のリソースはCloud Buildから変更させない、しない)
- CommitメッセージやPRメッセージは適当です・・・。
- PRメッセージにアプリのリポジトリ+SHA_HASHを載せると、インフラ側でもここが変わったんだな、と分かってgoodです。
git log
などでよしなに(bashでゴニョって)情報を載せてあげると良いですね。
さぁ、こちらもワンステップごとに見ていきましょう。
# Githubから最新のManifestを取得
- name: 'gcr.io/cloud-builders/git'
id: 'Clone current manifest file'
args:
- clone
- git@github.com:$_GITHUB_REPO
volumes:
- name: 'ssh'
path: /root/.ssh
# Manifestをアップデート
- name: 'gcr.io/cloud-builders/git'
id: 'Update manifest'
entrypoint: /bin/sh
args:
- '-c'
- |
set -x && \
cd hogehogeapp-manifest && \
git config --global user.email $_GITHUB_EMAIL && \
git config --global user.name $_GITHUB_USER && \
git remote set-url origin git@github.com:$_GITHUB_REPO && \
git remote -v && \
git checkout -b $_RELEASE_BRUNCH && \
git pull origin $_RELEASE_BRUNCH && \
sed -i "s/hogehoge\:[a-z0-9]*/hogehoge\:$SHORT_SHA/" hogehoge/deployment.yaml
volumes:
- name: 'ssh'
path: /root/.ssh
# Manifestのコミット
- name: 'gcr.io/cloud-builders/git'
id: 'Commit manifest'
entrypoint: /bin/sh
args:
- '-c'
- |
set -x && \
cd hogehogeapp-manifest/hogehoge && \
git add deployment.yaml && \
git commit -m "Excellent commit message" && \
git push origin $_RELEASE_BRUNCH
volumes:
- name: 'ssh'
path: /root/.ssh
# GithubのAccess Tokenを取得(CLoudKMSで暗号化済み)
- name: 'gcr.io/cloud-builders/gsutil'
id: Get Github access token from GCS
entrypoint: /bin/sh
args:
- '-c'
- |
set -x && \
gsutil cp gs://$_BUCKET_NAME/github_access_token.enc /root/.ssh/github_access_token.enc
volumes:
- name: 'ssh'
path: /root/.ssh
# CLoudKMSで暗号化されたAccess Tokenを復号
- name: 'gcr.io/cloud-builders/gcloud'
id: 'Decrypt access token with CloudKMS'
args:
- kms
- decrypt
- --ciphertext-file=/root/.ssh/github_access_token.enc
- --plaintext-file=/root/.ssh/github_access_token
- --location=global
- --keyring=$_KEYRING_NAME
- --key=$_KEY_NAME
volumes:
- name: 'ssh'
path: /root/.ssh
# ManifestのPR
- name: 'gcr.io/cloud-builders/curl'
id: 'PR manifest'
entrypoint: 'bash'
args:
- '-c'
- |
_ACCESS_TOKEN=`cat /root/.ssh/github_access_token`
repo=`echo -n $_GITHUB_REPO | sed "s/.git//"`
json=$(cat << EOS
{"title": "good title", "head": "${_RELEASE_BRUNCH}", "base": "master", "body": "great PR messages"}
EOS
)
curl -H 'Content-Type:application/json' \
-H "Authorization: token ${_ACCESS_TOKEN}" \
"https://api.github.com/repos/${repo}/pulls" \
-d "$json"
volumes:
- name: 'ssh'
path: /root/.ssh
Cloud Buildはここまで!
お疲れさまでした!コード載せたら冗長で長かったですね・・・。
楽じゃないじゃん・・・(泣
ArgoCDでGKEにデプロイしていく。
さて、デプロイの主役、ArgoCDの登場です。
使ってみた話はあれど、細かい設定もできるよ!という点を紹介していきたいと思います。
ArgoCDを宣言的デプロイでデプロイしても、インストールShellでデプロイしても同じことができますが、
宣言的デプロイをしておいたほうが色々楽です。(ドキュメントはこちら)
今回Argo CDでポイントにしておきたいのは以下の点です。
- Github SSO認証を使いたい。(これはN番煎じ・・・)
- GithubのチームごとにRoleを制限したい!
- コンテナデプロイ前と後になにかしたい!
- (番外編)HPAが・・・なんか変な感じになる・・・
早速行きましょう。
Github SSO認証を使いたい。(これはN番煎じ・・・)
こちらはすでに紹介があるのですが、前提条件なので・・・(GithubでOAuth Appの登録をしておいてください。)
公式ドキュメントはこちら
argocd-cm.yaml
を編集していきます。(ファイル名はデフォルトのものです。)
apiVersion: v1
data:
dex.config: |
connectors:
- type: github
id: github
name: GitHub
config:
clientID: Githubで払い出したクライアントID
clientSecret: $dex.github.clientSecret
orgs:
- name: your-org-name
teams:
- "Admin User"
- "ReadOnly User"
更に、上記のclientSecret
で指定した変数を読み込むため、argocd-secret.yaml
を編集します。
※普通のシークレットでも良いのですが、GithubにBase64でエンコードしただけの認証情報を載せるのもアレなので、今回はSealedSecretを利用して暗号化しています。(なのでkindがSealedSecretになってます。)
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
creationTimestamp: null
name: argocd-secret
namespace: argocd
spec:
encryptedData:
dex.github.clientSecret: AgBy3i4OJSWK+PiTySYZZA9rO43cGDEq.....
変更を適用し、画面にアクセスすると「LOGIN VIA GITHUB」のボタンが増えます!
SSOがこんなに簡単にできるとは・・・
GithubのチームごとにRoleを制限したい!
RBACの設定になります。
これもヒジョーに簡単にできるので、サクッと実装していきましょう。
公式ドキュメントはこちらです。
実は↑のSSO設定の際シレッと設定していましたが、teams
の設定が重要になってきます。
ちなみにteamsの名前はGithubのTeamsタブで確認できる名前を指定しましょう。
※私はslugの設定もできるかなと思って頑張ったんですが、なぜかうまく行かず、、、
apiVersion: v1
data:
dex.config: |
connectors:
- type: github
id: github
name: GitHub
config:
clientID: Githubで払い出したクライアントID
clientSecret: $dex.github.clientSecret
orgs:
- name: your-org-name
teams:
- "Admin User"
- "ReadOnly User"
おもむろにargocd-rbac-cm.yaml
を編集していきます。
p
から始まっている部分が各roleに割り当てる権限になります。
下記の例ではrole:org-admin
とrole:org-user
にそれぞれ権限を付与しているのがわかると思います。
g
から始まっている部分が、組織配下のTeamに上記roleを割り当てる行になります。
apiVersion: v1
kind: ConfigMap
data:
policy.default: role:readonly
policy.csv: |
p, role:org-admin, applications, get, */*, allow
p, role:org-admin, applications, update, */*, allow
p, role:org-admin, applications, delete, */*, allow
p, role:org-admin, applications, sync, */*, allow
p, role:org-admin, applications, override, */*, allow
p, role:org-admin, applications, create, */*, allow
p, role:org-admin, certificates, create, *, allow
p, role:org-admin, certificates, update, *, allow
p, role:org-admin, certificates, delete, *, allow
p, role:org-admin, clusters, create, *, allow
p, role:org-admin, clusters, update, *, allow
p, role:org-admin, clusters, delete, *, allow
p, role:org-admin, repositories, create, *, allow
p, role:org-admin, repositories, update, *, allow
p, role:org-admin, repositories, delete, *, allow
p, role:org-admin, projects, create, *, allow
p, role:org-admin, projects, update, *, allow
p, role:org-admin, projects, delete, *, allow
p, role:org-user, applications, get, */*, allow
p, role:org-user, applications, sync, */*, allow
p, role:org-user, certificates, get, *, allow
p, role:org-user, clusters, get, *, allow
p, role:org-user, repositories, get, *, allow
p, role:org-user, projects, get, *, allow
g, your-org-name:Admin User, role:org-admin
g, your-org-name:ReadOnly User, role:org-user
上記では結構ザックリ権限付与をしていますが、更に細かく指定していくことも可能です。
コンテナデプロイ前と後になにかしたい!
コンテナのデプロイの前後でなにかしたい(例えばDBのシードを流し込むとか、デプロイ結果を通知するとか)場合に便利なHookがあります。
-
PreSync
アプリの新しいバージョンをデプロイする前に、フックを使用して例えばデータベースへシードを投入します。 -
PostSync
フックを使用して、例えばSyncの結果を通知するアプリを実装します。
※他にもSync
やSyncFail
、Skip
などのHookがあります。
フックを実装するときにはargocd.argoproj.io/hook
アノテーションを利用します。
Hookの削除にはargocd.argoproj.io/hook-delete-policy
アノテーションを利用します。
例えばSlackにSyncの完了時に投稿する場合は、下記のようなbatch用YAMLを作成します。
apiVersion: batch/v1
kind: Job
metadata:
generateName: app-slack-notification-
annotations:
argocd.argoproj.io/hook: PostSync
argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
template:
spec:
containers:
- name: slack-notification
image: appropriate/curl
command:
- "curl"
- "-X"
- "POST"
- "--data-urlencode"
- "payload={\"channel\": \"#somechannel\", \"username\": \"hello\", \"text\": \"App Sync succeeded\", \"icon_emoji\": \":ghost:\"}"
- "https://hooks.slack.com/services/..."
restartPolicy: Never
backoffLimit: 2
こちらは公式にマルっと載ってます。
k8s単体だと実現できないような複雑な処理もでき、かゆいところにとっても手が届きます!
(番外編)HPAが・・・なんか変な感じになる・・・
これはやっちゃったやつなのですが、GitOpsだー!とかウキウキしていたらHPAの設定+deploymentにreplicasが残っていて、リリースの度HPAが意図する動作にならない&運用しているとArgoCDの差分に出てくる。。。とか。
HPA+ArgoCDで運用する場合はreplicas
のような動的にk8sに変えられる値はGitで管理しない(つまりYAMLに書かない)ようにしましょう。
普通にドキュメントに書いてあるし、、、ArgoCDはドキュメントが結構手厚めなので、隅々まで目を通しておくと良いと思います。
最後に
いかがでしたでしょうか。
他の記事では言及されていない?ような部分にフォーカスしてデプロイフローと関連ツールを見てきました。
これどうやるんだろ、どうやってるんだろという点の気付きになればと思います。
より良い手段やツールがあればぜひ教えて下さい!それでは!