Kekeの日記

エンジニア、読書なんでも

Argo CDによってGKEでGitOpsをする

f:id:bobchan1915:20181206151759p:plain

はじめに

最近読んだ鎌倉時代の歌人、鴨長明の『方丈記』にも

ゆく河の流れは絶えずして、しかももとの水にあらず。

とありますが、私たちのソフトウェアも然りです。

毎日更新され、デプロイされ、時に不可逆に破壊され。そして、ちぐはぐな形に落ち着くまで絶えず変更されているように感じます。

そのようなことにならないためにもOpsやCDについて注目しています。

本記事

本記事はKubernetes Advent Calendarの15日目の記事になっています。

どうも、私はもうすぐ大学を卒業(予定)の@timtimtim1026です。

コメントや間違いなどがあればDMやリプを軽く飛ばしてもらえると嬉しいです。

今回は、GitOpsを実現するためのツールとしてArgo CDが注目を集めているので、GitOpsについて考えていこうと思っています。

また今回はたまたまArgo CDを使ってみますが、だいたいのGitOpsに導入しやすいソフトウェアは、似たような構成なので、体系的に学べる記事にできれば嬉しいと思っています。他にもWeave FluxやJenkins Xなどがありますが、それにも通用する知識をつけられれば何よりです。

コンテンツ

本記事の目次は以下の通りです。

興味がある項目だけでも、ぜひ読んでみてください。

GitOpsについて

GitOpsの定義

Weave社のブログではGitOpsの定義とは

Operation by Pull Request = プルリクによるオペレーション

であると書かれています。もともとはGitOpsは、最初にWeave社が提唱したもので以下の原文で定義されているものです。

f:id:bobchan1915:20181214222043p:plain

​GitOps - Operations by Pull Request

理論は分かっても現実世界では何かしらの道具を使って実現します。

GitOpsを実現しようとすると以下のような技術が必要になってきます。

別の見方では

GitOps = Cloud Native + Continuous Delivery

としても捉えることができるのではないかでしょうか。

プロダクションとして使っていくには正しいCloud Native技術を使って継続的デリバリーをすることが非常に大事です。

Cloud Nativeと継続的デリバリー

例えば、両者は以下のような技術があります。

  • Cloud Native: GitHub, GitLab, Docker, Kubernetesなど
  • 継続デリバリー: 今回紹介するArgo CDやSpinnakerなど

f:id:bobchan1915:20181214223222p:plain

この図ではではCIツールでCDできるものも一緒にしました(タスクを書けば複雑なデリバリーをするのもできるといえばできるので)。

Cloud Nativeについては財団があるので参照するといいかもしれません。

Home Page - Cloud Native Computing Foundation

スクリーンショットで撮ってもあふれるくらいのCloud Native技術はたくさんあります。

f:id:bobchan1915:20181211022150p:plain

これらを用いて宣言的で、変更しやすく、チームで運用できるオペレーション手法としてGitを用いるGitOpsが注目されています。

構成

アーキテクチャとしては、大まかには以下のようにgitリポジトリが二つあって

  • アプリケーションリポジトリ: アプリケーションのリポジトリ
  • コンフィグリポジトリ: 宣言的なマニフェストファイル

で構成されています。

f:id:bobchan1915:20181214223517p:plain

用途というと以下のように対応しています。

リポジトリ 用途
アプリケーション アプリそのものを管理
コンフィグ デプロイ環境などの設定を管理

図にすると以下のように対応しています。

f:id:bobchan1915:20181214223845p:plain

特にコンフィグファイルがデプロイ環境を定義しています。例えばKubernetesの状態などです。

また、GitOpsの大きな原則としては以下の3つがあります。

  • 記述できるすべてのものはgitに保存されてないといけない
  • Kubectlは直接使用されるべきではない
  • Operatorパターンに従うKubernetesコントローラーを使用する

第一の原則はつまりgitですべてを管理しろということです。

アプリケーションは、元からgitで管理されていることがほとんどでしょう。それはReactでもRailsでもGoサーバーでも関係ありません。特に新しいことはありません。

f:id:bobchan1915:20181214225718p:plain

しかしながら、環境を記述するコンフィグファイルが別のリポジトリで、git管理であることは珍しいでしょう。

どのクラスタに、どのValue(設定値)で、どのように、、、など記述できるすべてを記述します。

f:id:bobchan1915:20181215024727p:plain

つまり、このリポジトリを見れば現在の状態が監視できるというわけです。

この恩恵は逆も然りです。

デプロイメント環境はすべてgitで管理されているということもできます。

ただ言い方を変えたように捉えられますが、例えばReplica数がおかしいとか、何かデプロイメントされていない、バージョンが違うなどの問題もマニフェストファイルで宣言を間違えているということができるかもしれないからです。

f:id:bobchan1915:20181215030925p:plain

次に第二の原則は宣言する以外、人間が介入するなということです。

特に、この書籍が参考になると思います。

SRE サイトリライアビリティエンジニアリング ―Googleの信頼性を支えるエンジニアリングチーム

SRE サイトリライアビリティエンジニアリング ―Googleの信頼性を支えるエンジニアリングチーム

  • 作者: 澤田武男,関根達夫,細川一茂,矢吹大輔,Betsy Beyer,Chris Jones,Jennifer Petoff,Niall Richard Murphy,Sky株式会社玉川竜司
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2017/08/12
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログ (1件) を見る

例えば、誰かが毎回毎回、手動で設定すると

  • 何をしたのか覚えてない
  • スケールさせにくい
  • 管理者が必要

などなど大変、問題があります。

f:id:bobchan1915:20181215031809p:plain

実際に、いまだこれをしているという話は少ないと思います。AnsibleやChef, Puppetの台頭で構成管理がだいぶ簡単になりました。

しかし、Kubernetesだとkubectlを使って、さっと変更してしまうことが多くあったり、マニフェストファイルをgit管理していかなったりします。

なのでこのように人が直接操作をやめようという、古くからの教訓を再度、強調しているようにも思えます。

f:id:bobchan1915:20181215032306p:plain

第三の原則は具体的に分かっていないので、教えていただけると嬉しいです。

Argoについて

Argoには大きく分けて二つの機能があります。

  • ArgoCI
  • ArgoCD

これを分けて解説します。

まずArgoとは

Argoとは、まとめて説明すると

Kubernetesのためのオープンソースなコンテナネイティブワークフローエンジンです

以下のようなことができます。

  • それぞれのステップがコンテナで動くワークフローを定義できる
  • マルチステップワークフローを定義できる
  • 機械学習やデータ処理などの重い処理もKubernetesで動かすことができる
  • Kubernetes上でCI/CDパイプラインを簡単に実行することができます。

先ほども説明しましたが、Argo CIArgo CDの二つの機能があって、これらがCI/CD機能に対応しています。

公式リポジトリは以下のようになっています。

github.com

今回はArgo CDについてフォーカスするので注意してください。

Argo CDとは

Argo CDとはKubernetesのための宣言的なGitOpsのためのデリバリーツール(コントローラー)です

GitOpsのパターンに従うので、gitリポジトリを使って望む状態を宣言します。

以下のようなフローになります。

f:id:bobchan1915:20181205221133p:plain

つまりGitの状態がデプロイ環境の状態です。

使えるKubernetesのマニフェストファイルは以下の通りになっています。

  • ksonnet
  • kustomize
  • helm
  • プレーンなYAML/jsonマニフェストファイル

現時点ではPlainなYAMLを取得することができませんでした。

どうやら何かしらのテンプレートを使った方がいいみたいです。

本題に戻して、Argo CDはデプロイを自動化します。もちろんCDツールとして可視化や通知などといったようなCDの概念に必要な機能ももちろんあります。

特徴としては

  • デプロイを自動化
  • いろんなマニフェストの書き方(上記)に対応
  • WebUIやCLIツールが用意されていて、要求状態と現在の状態を可視化
  • gitリポのどの状態へもロールバック
  • Webhookインテグレーション
  • 戦略的なデプロイ(ブルーグリーン/カナリアアップグレード)に対応
  • Helmやksonnetなどのパラメータを上書きする機能

などがあります。

構成アーキテクチャ

GitOpsを実現するためにArgo CDを使ったアーキテクチャは以下のようになります。

f:id:bobchan1915:20181205174838p:plain

これから少し解説をします。

1. GitOpsを実現する

以下のようなデプロイメントパイプラインの構築を目指します。

f:id:bobchan1915:20181206042926p:plain

今までも私は数々の会社にインターンとして働いていましたがどれもCIを使ってデプロイしているCIOpsでした。

CircleCIやTravisなどのCIがテストして、デプロイして、、、と何もかもしていた状況です。CIがオペレーションの中心になっていました。

CIOpsをしない方法としてgitリポジトリとしてマニフェストファイルなどのデプロイメントに関するファイルを集めてデプロイする手法がGitOpsです。

先ほどのアーキテクチャ図では以下の主要な部分が提供しています。

f:id:bobchan1915:20181205180331p:plain

例えばproduction-cluster-configのようなリポジトリがあって、それを参照するような形です。

デフォルトではArgo CDはGithubリポジトリを3分おきにポーリングしています。これをリポジトリに変更があるとすぐ反映をしたければWebhookを設定する必要があります。

以下のよう直接CIツールからKubernetesコンテナにデプロイするのではなくて、以下のようにGitリポジトリから同期を取るようにします。

f:id:bobchan1915:20181206044001p:plain

2. CIと接続

この図ではJenkinsやCircleCIなどのCIツールが書かれています。

GitOpsの中では、CIは境界のある明確な概念として定義されます。

CIではユニットテストをするほか、Argo CDへのAPIをトリガーするだけで、とても責任が明確です。

最終的にはKubernetesにトリガーします。

f:id:bobchan1915:20181205181328p:plain

3. CDとしてDeployする

CDといえばあらゆる環境に戦略的なデプロイメントをすることが必要でしょう。

もちろん手動でもできますが第二の原則でもあったように、CDの観点からArgo CDによって自動化をします。

f:id:bobchan1915:20181205201050p:plain

いまさらですが、CDについてわからなければ以下の本が非常にためになります。とてもいい本だと思います。

継続的デリバリー 信頼できるソフトウエアリリースのためのビルド・テスト・デプロイメントの自動化 (アスキードワンゴ)

継続的デリバリー 信頼できるソフトウエアリリースのためのビルド・テスト・デプロイメントの自動化 (アスキードワンゴ)

  • 作者: JezHumble,DavidFarley,和智右桂,高木正弘
  • 出版社/メーカー: ドワンゴ
  • 発売日: 2017/08/09
  • メディア: Kindle版
  • この商品を含むブログを見る

準備

すこし準備が必要です。

もしすでに済んでいる人は飛ばしてください。

GCPにCLIからログインする

以下のようにログインをすることができます。

gcloud auth login

Projectを作成して設定

以下のようにプロジェクトを適当に作成します。

gcloud projects create test-argo-cd --name test-argo-cd

そして設定します。

gcloud config set project test-argo-cd

テストクラスタを作成

以下のようにテストクラスタを作成します。

gcloud cluster container test-argo-cd --region=asia-northeast1-a

Credentialを取得

KubectlやArgoCDのCLIにはクラスタにアクセスするために認証情報が必要なので以下のように取得します。

gcloud container cluster get-credential test-argo --region=asia-northeast

以下のようにノードが作成されているのを確認できます。

kubectl get nodes

NAME                                         STATUS    ROLES     AGE       VERSION
gke-test-argo-cluster-pool-1-cbf3d941-8nkg   Ready     <none>    56s       v1.9.7-gke.11
gke-test-argo-cluster-pool-1-cbf3d941-kr8h   Ready     <none>    3m        v1.9.7-gke.11
gke-test-argo-cluster-pool-1-cbf3d941-mt1w   Ready     <none>    3m        v1.9.7-gke.11

Argo CDをインストール、基本を理解する

ClusterRoleBindingを作る

ClusterRoleBindingを作成します。

kubectl create clusterrolebinding YOURNAME-cluster-admin-binding --clusterrole=cluster-admin --user=YOUREMAIL@gmail.com

そして--userに自分のアドレスを入れてください。

Namespace作成してArgo CDを入れる

まずはargocd Namespaceを作成します。

kubectl create namespace argocd

すると作成されたので以下のように確認できます。

kubectl get ns

NAME          STATUS    AGE
argocd        Active    8s
default       Active    4m
kube-public   Active    4m
kube-system   Active    4m

そしてArgo CDをインストールします。

kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/v0.10.6/manifests/install.yaml

問題なく終了できればargocdNamespace以下にこのようなものが見えるはずです。

Serviceは

kubectl get service

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
argocd-metrics       ClusterIP   10.31.243.134   <none>        8082/TCP            6m
argocd-repo-server   ClusterIP   10.31.247.251   <none>        8081/TCP            6m
argocd-server        ClusterIP   10.31.250.9     <none>        80/TCP,443/TCP      6m
dex-server           ClusterIP   10.31.254.142   <none>        5556/TCP,5557/TCP   6m

Dexだけアーキテクチャ図にはなかったのですが、OpenIDを使うためのものらしいです。

github.com

Deploymentsは

kubectl get deployment

NAME                     DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
application-controller   1         1         1            1           7m
argocd-repo-server       1         1         1            1           7m
argocd-server            1         1         1            1           7m
dex-server               1         1         1            1           7m

Argo CD CLIをインストールします

CLIツールをいれます。

brew install argoproj/tap/argocd

インストールされたことを確認します。

which argocd

/usr/local/bin/argocd

Argo CD API サーバーにアクセス

デフォルトではArgo CDのAPIサーバーは外部IPを持っていないです。

なのでkubectl patchコマンドを実行して外部IPを取得できるようにtype: LoadBalancerにします。

kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'

Port転送でも行うことができます。

kubectl port-forward service/argocd-server 8080:443

すると以下のように外部IPがあります。

kubectl get svc | grep "argocd-server"

argocd-server        LoadBalancer   10.31.250.9     34.213.32.91   80:31249/TCP,443:31763/TCP   1h

そしてPod名前を取得します。

公式ドキュメントでは以下のように

kubectl get pods -n argocd -l app=argocd-server -o name | cut -d'/' -f 2

としていますがこっちの方がわかりやすいので--filterを使って行います。

kubectl get pods -n argocd -l app=argocd-server -o=jsonpath="{..items[].metadata.name}" 

これがArgoCDの初期パスワードです。ユーザー名はadminです。

そして以下のようにログインします。

argocd login [外部IP]

Username: admin
Password:
'admin' logged in successfully

そしてパスワードを変えてください。

argocd account update-password

*** Enter current password:
*** Enter new password:
*** Confirm new password:
Password updated

再度、ログインします。

argocd relogin

これらはkubectlのようにContextがCLIツールが使えるように保存されます。

WebUIからGithub Hookの設定をする

先ほどの外部IPを開きます。

open 34.213.32.91

すると以下のようなページが開きます。

f:id:bobchan1915:20181205225113p:plain

Usernameにadmin、Passworkに先ほど設定したパスワードを入力してください。ログインできると以下のようになります。

f:id:bobchan1915:20181205225306p:plain

以下のような項目になっています。

f:id:bobchan1915:20181205225633p:plain

設定項目を見てみましょう。Repositoryを選択します。

f:id:bobchan1915:20181205225733p:plain そして次にConnect Git Repoで設定をします。

f:id:bobchan1915:20181205225815p:plain

すると以下のような画面が開きます。

f:id:bobchan1915:20181205225844p:plain

ここではサンプルリポジトリをフォークして使ってみます。

github.com

このリンクのURLを入力します。Publicなので何も他に入力する必要はありません。

以下のように接続することができました。

f:id:bobchan1915:20181211025204p:plain

アプリケーションを設定する

次にまたアプリケーションページに戻って、プラスボタンで新しくアプリケーションを設定します。

f:id:bobchan1915:20181205235252p:plain

先ほどのリポジトリを選択します。

f:id:bobchan1915:20181211025133p:plain

すると再帰的にYAMLファイルがあるディレクトリを表示してくれます。

f:id:bobchan1915:20181205235425p:plain

最初の説明にも

gitリポのどの状態へもロールバック

という特徴があるといいました。

以下のフィールドにcommit IDを入力することによって、その状態のマニフェストファイルを参照することができます。

f:id:bobchan1915:20181211025325p:plain

どうやらキャレット^やチルダ~は使えないようです。HEADを追従してGitOpsして行きたいので、必要ないのでいいですが。

どれかを設定すると以下の項目を入力します。

f:id:bobchan1915:20181211025358p:plain

  • Application Name: アプリケーションの名前です
  • Cluster URL: どのクラスタにデプロイするかを選択します
  • Namespace: どのNamespaceにデプロイするかを選択します
  • Path: k8sのマニフェストファイルがどこにあるかを指定

もしかしたらまずこちらの画面がでるケースで設定を入力できるものもあります。Helmkustomizeによって変化します。

f:id:bobchan1915:20181206030729p:plain

Createできると以下のようになります。

f:id:bobchan1915:20181211025512p:plain

しかし、ここではまだ同期していないのでOutOfSyncになっています。

f:id:bobchan1915:20181211025527p:plain

ここからはksonnetのでやっていこうと思います。

もちろんCLIツールからもできます。

argocd app create guestbook-default --repo https://github.com/argoproj/argocd-example-apps.git --path guestbook --env default

ksonnetだったら上のコマンドなのですが、Helmならばvalueファイルを指定します。

argocd app set helm-guestbook --values values-production.yaml

同期する

GitOpsのためにGitリポジトリとクラスタが同期するようにします。

GUIでは以下のようにActionsを押すと出てくるSyncを押します。

f:id:bobchan1915:20181211025005p:plain

Applicationを選択してからもSyncはできます。

f:id:bobchan1915:20181206033247p:plain

エラーが出ました。

f:id:bobchan1915:20181206033134p:plain

解決するためにprod名前空間を作ります。

kubectl create ns prod

もう一回やってみると以下のように同期が成功しています。

f:id:bobchan1915:20181211025042p:plain

CLIからは以下のように同期を実行することができます。

argocd app sync guestbook-default

KIND  NAME  STATUS  HEALTH  HOOK  OPERATIONMSG
Service     ks-guestbook-ui                 service/ks-guestbook-ui unchanged
Deployment  ks-guestbook-ui                 deployment.apps/ks-guestbook-ui unchanged

Application:        guestbook-prod
Operation:          Sync
Phase:              Succeeded
Start:              2018-12-06 03:36:01 +0900 JST
Finished:           2018-12-06 03:36:23 +0900 JST
Duration:           22s
Message:            successfully synced

KIND        NAME             STATUS  HEALTH   HOOK  OPERATIONMSG
Service     ks-guestbook-ui  Synced  Healthy        service/ks-guestbook-ui unchanged
Deployment  ks-guestbook-ui  Synced  Healthy        deployment.apps/ks-guestbook-ui unchanged

状態をCLIから取得するにはapp getコマンドを実行します。

 argocd app get guestbook-prod | pbcopy

Name:               guestbook-prod
Server:             https://kubernetes.default.svc
Namespace:          prod
URL:                https://34.232.34.31/applications/guestbook-prod
Repo:               https://github.com/argoproj/argocd-example-apps.git
Target:             HEAD
Path:               ksonnet-guestbook
Environment:        prod
Sync Policy:        <none>

KIND        NAME             STATUS  HEALTH
Service     ks-guestbook-ui  Synced  Healthy
Deployment  ks-guestbook-ui  Synced  Healthy

ここでSTATUS

  • OutOfSync: 同期が取れていない
  • Synced: 同期されている

ということなので、CLIから適宜状態をみることができます。

またGUI上のアプリケーションの構成要素はそれぞれ詳細をみることができ、また編集できます。

f:id:bobchan1915:20181206034122p:plain

実際にGitOpsになっているかを確認

まずYAMLに何かしらの変更を加えます。今回はreplicas1から2に変更しました。

 spec:
-  replicas: 1
+  replicas: 2
   selector:

そしてgit pushすると以下のようにOutOfSyncになりました。

f:id:bobchan1915:20181211031358p:plain

しばらく待つと以下のようになりました。

f:id:bobchan1915:20181211031513p:plain

次は6にします。

f:id:bobchan1915:20181211031658p:plain

以下のようになります。

機能のDeep dive

リアルタイム同期のためのWebhook

「リアルタイム」という言葉が強いのですが、Argo CDは3分おきにリポジトリの変更をみてデプロイしています。

これをGitに変更があるたびに設定を変えるにはWebhookを設定しなければなりません。これはGithub側で行います

Argo CDの記事に直接的に関係ないので解説しません。

Application(リポジトリ)をまとめるためのProject機能

例えば一つの会社で複数のサービスを抱えている場合、サービスごと、かつ機能ごとにリポジトリを管理することがおおいと思います。

f:id:bobchan1915:20181206040802p:plain

そのようなものを、サービスごとや意味のある集合として定義できるのがProjectsです。

f:id:bobchan1915:20181206041001p:plain

何も設定をしないとdefaultProjectに入ります。ここらへんはKubernetesとよく似ていると思います。

f:id:bobchan1915:20181206041145p:plain

ここではSourceとDestinationとして設定します。

f:id:bobchan1915:20181206041349p:plain

APIドキュメントをみる

http://外部IP/swagger-uiでAPIドキュメントをみることができます。

f:id:bobchan1915:20181206035809p:plain

Hookを設定する

KubernetesのJobとしてHookを設定することができます。

Hook いつトリガーされる?
PreSync 同期される前
Sync すべてのPreSyncが成功して完了したら
Skip Manifestファイルのapplyをスキップした時
PostSync Syncフックがすべて終了してすべてがHealthyになったら行う

HookPolicyというものがあり、どのような状態のときに設定されたHook(Job)を削除されるかを定義する

HookPolicy どんなルールなのか
HookSucceeded Hookが成功したら
HookFailed Hookが失敗したら

HookとHookPolicyを合わせると以下のようなマニフェストファイルをデプロイするといいです。

apiVersion: batch/v1
kind: Job
metadata:
  generateName: integration-test-
  annotations:
    argocd.argoproj.io/hook: PostSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded

例えばこれを使うとSlackなどに通知を送るなどすることができます。

argocdコマンド一覧

以下のようなコマンドが用意されています。

コマンド 何をするのか
acount アカウントの設定
app Argo CD上でのアプリケーションを管理
cluster クラスタの認証をする
context コンテクスを切り替える
help ヘルプコマンド
login Argo CDにログインする
proj プロジェクトを管理する
relogin ログアウトしてログインする
repo リポジトリの認証をする
version バージョンを確認する

まとめ

GitOpsだとマニフェストファイルがあるリポジトリがクラスタの状態であり、そのリポジトリのコミット履歴がクラスタの履歴でもあるので管理をするという面では楽でした。

またフックなども設定することができたり、CDとしては機能が最低限あります。

しかしながら、GitOpsではありませんがSpinnakerの方が利便性が高く、より高度なデプロイメントパイプラインを構築できると思うので、少し比較すると機能不足ではないかと思いました。

しかしながらCIとうまく機能を分離しながらArgo CDとして一つクラスタを用意してGitOpsを実装するのもArgo CDだと簡単にできるし、Helmだけでなくてksonnetやkustomizeにも対応しているので既存の(たぶん)CIOpsの状態との親和性が高いのではないかと思います。

似たものでWeave FluxというGitOpsを実現するためのOSSもあるのでまた使ってみようと思います。

github.com

参考文献

Argo公式ドキュメント

github.com

Japan Container Daysであった発表

www.slideshare.net

KubeCon North America 2018での発表 

speakerdeck.com