GitHub Actions & kind (Kubernetes IN Docker) で Matrix build の CI を構築

CI で Kubernetes を使いたい場合、自前の Kubernetes 環境か GKE や EKS を利用して常時クラスターを使えるようにしておく方法があります。

しかし、ワーカーのノード数を色々変えてテストしたい、Kubernetes のバージョンを色々変えてテストしたい、常設のクラスターを準備するのがコスト的に厳しい・・などの場合、Docker 環境さえあれば揮発的に素早くクラスターを構築して終了後に破棄できる kind (Kubernetes in Docker) が有効です。

blog.kondoumh.com

GitHub Actions の Marketplace でも kind を使う Actions が公開されていました。

github.com

workflow の steps で以下のように指定するだけで kind でクラスターを構築できます。

    steps:
    :
    - uses: engineerd/setup-kind@v0.2.0
    :

kind の Config ファイルも width: に指定できます。

    - uses: engineerd/setup-kind@v0.2.0
      with:
        config: "k8s/kind.yml"

Kubernetes のバージョンを指定したいときは、kindest/node イメージのバージョンを指定すれば OK です。

    - uses: engineerd/setup-kind@v0.2.0
      with:
        image: kindest/node:v1.14.9

kind を手動起動する場合 Config ファイル内で kubeadmConfigPatches を使って kubeadm のオプションを与えるのですが、この Action では workflow 内で指定可能です。

複数バージョンの Kubernetes を使う Matrix build のサンプルを作ってみました。kindest/node のタグを matrix にしています。

name: "Create cluster using KinD"
on:
 push:
  paths:
  - k8s/**
  - .github/workflows/it_k8s.yml

jobs:
  kind:
    strategy:
      matrix:
        k8s: [1.14.9, 1.15.7]
  
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@master
    - uses: engineerd/setup-kind@v0.2.0
      with:
        config: "k8s/kind.yml"
        image: kindest/node:v${{ matrix.k8s }}
    - name: Testing
      run: |
        kubectl version
        kubectl cluster-info
        kubectl get node -o wide
        kubectl get pods -n kube-system
        kubectl apply -f k8s/nginx_deploy.yml
        kubectl describe deployment nginx-deployment

テストでは kubectl で nginx のマニフェストを apply して deployment を describe する処理を書いています (本来はデプロイしたアプリケーションのエンドポイントに対して E2E のテストを実行します)。

クラスターはデフォルトでシングルノード構成で起動されるので、複数ワーカー構成にしたい場合 Config ファイルで指定します。今回は、master (1) / worker (2) で構築しました。

# three node (two workers) cluster config
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4

nodes:
- role: control-plane
- role: worker
- role: worker

実行すると Kubernetes バージョン毎の結果が表示されます。

f:id:kondoumh:20200114231329p:plain

kind と Matrix build は相性がいいですね。

2019 ふりかえり

Job

引き続きプロダクト開発プロジェクト。1年以上在任してます。Kubernetes を中心とした開発環境・実行環境・CI/CD パイプラインの構築をやって IaC の良さも難しさも実感しています。

別のサービス開発プロジェクトにも少し入りました。こちらはコテコテの Java アプリでコード改善が主体です。

共に来年も続いていきそうです。

Private Project

GitHub の草は生やし続けてます。

野良 Scrapbox アプリは自分が一番のユーザということもあり、かなり更新して Electron にも馴染みました。

Vue は最近触ってませんが、v3 が出たら見ようかなと。

CI/CD ツールも色々と試しました。

11月にリリースされた GitHub Actions を public beta から使って気に入っています。野良 Scrapbox アプリの Windows / macOS のインストーラ配布もできました。

ホームページを Hugo に移行し GitHub Actions でジェネレートから公開まで自動化しました。

Hugo もですが Cloud Native なソフトウェアの多くが Go で書かれており、仕事で Go の開発環境・CI 整備をやっているので、個人的にもツール作成に使い始めました。久々に触るとかなり進化してました。Go Modules はかなりイケてる気がします。

今後 Go や d3.js などで可視化ツールを開発しようと思ってます。

Writing

ブログは週1以上ぐらいのペースで更新してました。

Scrapbox はすっかり会社のインフラになりました。僕もよく書いてます。個人プロジェクトも集約しました。

Software / Services

Netlify

PWA の配布やちょっとしたバッチ処理に使っています。

GCP

VM インスタンスをよく使ってます。GKE も使うようになりました。

AWS

仕事で EKS 中心に使ってます。個人アカウントはあまり使ってませんが、会社でアカウントもらったので来年から色々使うと思います。

DigitalOcean

Droplets をスポット利用してます。Kubernetes もローンチしたので使うかも。

Terraform と GItHub Actions で DigitalOcean Droplets の CI/CD を作る - kondoumh のブログ

VS Code

あらゆる開発環境が VS Code で構築できるようになってます。

Gitpod

iPad でも使える VS Code ベースの Web IDE。VS Code Online も出たし Eclipse Che や CodeSandbox も VS Code ベースだし・・と、Web IDE 界も VS Code が席巻してます。

Emacs

ターミナル作業も多いので設定や利用パッケージを見直しています。

Kibela

ドキュメント下書き・ToDo 管理・コードスニペット管理に使っています。PWA であらゆる場所で閲覧・編集できて便利。ただ、カーソルの挙動がおかしかったり投稿がうまくできなかったりする時があり、改善が望まれます。

Kibela (キベラ) - 個人の発信を組織の力にする情報共有ツール

draw.io

AWS アイコンも充実してシステム構成描くのに重宝します。Electron アプリになりました。

Flowchart Maker & Online Diagram Software

Ingress

Resurgence メダルもらいました。

Gadgets

中華ワイヤレスイヤホンとか Qi 充電器を買ったぐらい。

scrapbox.io

scrapbox.io

奥さん用に iPad Pro 書いました。

scrapbox.io

自分はまだ iPad Air 2 です。iPadOS でかなり使い勝手がよくなり、出先での作業はだいたい賄えてます。もう1世代ぐらい様子見かな。

MacBook Pro 13 Early 2015 も使い続けてます。そろそろ 6コアぐらいのマシンが欲しいところ。持ち歩かないので Mac mini とかでもいいかも。

まとめ

仕事も個人プロジェクトも1年前よりさらに Cloud Native 化が進んでいる気がします。コンピューティングもそれに合わせて変わっていくのでしょう。

Go Modules の hosting と import の関係

とあるプロジェクトでセルフホストしている GitLab で Go Modules を利用しようしてハマったのでメモ。

foo モジュールを private.host/account1/foo に公開したとします。

go.mod

module private.host/account1/foo

go 1.12

利用側では、

import "private.host/account1/foo"

のように指定します。go get / run / build する際、go コマンドは https://private.host/account1/foo?go-get=1 のようにリクエストを投げるようです。GitLab などの VCS hosting サイトはこのリクエストに対して

<meta name="go-import" content="private.host/account1/foo git https://private.host/account1/foo.git">

のように、VCS の種類とリポジトリの URL を含むメタタグを HTML として返すことを期待されます。

go コマンドはこの HTML をパースして VCS の種類、リポジトリのロケーションを特定し、VCS の CLI コマンドにモジュールの取得を委譲します。

godoc.org

gitlab.com では go-get=1 のクエリパラメータ付きリクエストに以下のような HTML を返却しています。

<html>
<head>
  <meta name="go-import" content="gitlab.com/kondoumh/go-samplemodlue git https://gitlab.com/kondoumh/go-samplemodule.git" />
  :
</head>
<body>
  go get https://gitlab.com/kondoumh/go-samplemodule
</body>
</html>

GitHub の場合はこんな感じ

<html lang="en">
  <head>
    <meta charset="utf-8">
      :
    <meta name="go-import" content="github.com/kondoumh/gogreeting git https://github.com/kondoumh/gogreeting.git">
      :
</head>
<body>
  :

Go Modules はソースコードであり、Git 以外の VCS (Mercurial, Bazzar etc.) でもホストされる可能性があります。Java や Node.js などのようにビルド・パッケージングしてホストするわけではないので、このように VCS によらない方法を取っているのでしょう。

件の GitLab は HTTPS ポートが規定の 443 以外で公開されていました。しかし go コマンドは既定のポートにリクエストを投げるので Connection refused でエラーになっていました。443 ポートを受け付けるように設定したら無事に使えるようになりました。

GitLab CI で kaniko を使ってコンテナイメージを build / push

CI パイプラインは Docker コンテナ Runner で実行することが一般的なので、パイプラインの中で docker build するには privileged モードで Runner のコンテナを実行する必要があります。いわゆる DinD (Docker in Docker) です。

build:
  image: docker:latest
  script:
    - docker build -t registry.gitlab.com/kondoumh/sandbox -f Dockerfile .
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD registry.gitlab.com
    - docker push registry.gitlab.com/kondoumh/sandbox
  services:
    - docker:dind

kaniko は Google 謹製のコンテナイメージビルドツールです*1

github.com

Docker daemon に依存せず、コンテナ内、もしくは Kubernetes cluster の Pod 内で Dockerfile からイメージをビルドできるため、DinD を回避しセキュアに運用できます。

GitLab CI でも kaniko の利用ガイドがありました。

https://docs.gitlab.com/ee/ci/docker/using_kaniko.html

/kaniko/.docker/config.json に Container Registry とそのユーザー名、パスワードを設定し、kaniko executer に Dockerfile や push 先などのコンテキストを渡して実行します。kaniko のイメージは debug タグを指定する必要があります。

build:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  script:
    - echo "{\"auths\":{\"registry.gitlab.com\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG

docker:dind を services に指定する必要はありません。実行するとちゃんとイメージが push されました。

f:id:kondoumh:20191228145020p:plain

gitlab.com もいつの間にか Container Registry 提供していたんですね。

*1:蟹工船に由来するネーミング?

Electron アプリの通知 on Windows

Electron は Notification をサポートしていて、Renderer プロセスでも Main プロセスでも表示できます。

野良 Scrapbox アプリで GitHub API 使って latest release を取得し現在のバージョンと違っていたら通知を出し、クリックしたらダウンロードページを開くようコードを追加しました。

f:id:kondoumh:20191221123026p:plain

macOS では普通に動きました。Windows では「しかし何も起こらなかった。」

公式ドキュメントを見ると Application User Model IDs をスタートメニューにインストールしなくてはいけないとか、 Squirrel という更新フレームワークに依存してればちょっとワークアラウンドが省略できるとか書いてあり、要するに Windows のサポートは面倒ということがわかりました。更新フレームワークの導入はオーバーな気がしたので、通知だけ使いたかったのです。

https://electronjs.org/docs/tutorial/notifications#windows

NPM にも通知関連のパッケージがたくさんあり、いろんな人が苦労してそうな感じがしました。

macOS の方はアプリケーション起動時に一度許可すれば、以降は通知してくれます。

ということで、Electron 側の対応を待つことにして Windows 対応は諦めました。

Kubeflow が MicroK8s の Addon になってた

以前 Kubeflow を MiniKF (Minikube ベースの VirtualBox イメージ) で導入しました。

blog.kondoumh.com

その後 Ubuntu 19.10 のリリース時、Kubeflow が MicroK8s の Addon として導入できるようになるというアナウンスがありました。

ubuntu.com

MicroK8s は Snap で簡単にインストールできる Kubernetes 環境です。Addon として Dashboard、CoreDNS、Istio などを microk8s.enable コマンドで簡単に導入できます。

blog.kondoumh.com

そして、つい先日の MicroK8s 1.17 - 11 December 2019 で Addon として追加された模様です。

New addon: kubeflow. Give it a try with microk8s.enable kubeflow.

Release notes | MicroK8s

MicroK8s なので Ubuntu 19.10 が必須というわけでもなさそうですが、環境を用意して導入してみました。

まず Snap で MicroK8s をインストール

$ sudo snap install microk8s --classic
microk8s v1.17.0 from Canonical✓ installed

ユーザーを microk8s グループに追加してログインしなおし。

$ sudo usermod -a -G microk8s hoge

ステータスを確認。kubeflow は disable になっています。

$ microk8s.status --wait-ready
microk8s is running
addons:
cilium: disabled
dashboard: disabled
dns: disabled
fluentd: disabled
gpu: disabled
helm: disabled
ingress: disabled
istio: disabled
jaeger: disabled
juju: disabled
knative: disabled
kubeflow: disabled
linkerd: disabled
metallb: disabled
metrics-server: disabled
prometheus: disabled
rbac: disabled
registry: disabled
storage: disabled

microk8s.kubectl を kubectl として実行できるよう alias を設定

$ sudo snap alias microk8s.kubectl kubectl
Added:
  - microk8s.kubectl as kubectl

kubeflow を enable します。dns , storage など依存するアドオンも enable され最後に kubeflow のデプロイが開始されます。

Pod が大量に起動され、Service が Ready になるまでに 30分ほどかかりました。

$ microk8s.enable kubeflow
Enabling dns...
Enabling storage...
Enabling dashboard...
Enabling ingress...
Enabling rbac...
Enabling juju...
Deploying Kubeflow...
Kubeflow deployed.
Waiting for operator pods to become ready.
Waited 0s for operator pods to come up, 28 remaining.
Waited 15s for operator pods to come up, 27 remaining.
Waited 30s for operator pods to come up, 27 remaining.
Waited 45s for operator pods to come up, 27 remaining.
Waited 60s for operator pods to come up, 25 remaining.
Waited 75s for operator pods to come up, 21 remaining.
Waited 90s for operator pods to come up, 19 remaining.
Waited 105s for operator pods to come up, 18 remaining.
Waited 120s for operator pods to come up, 17 remaining.
Waited 135s for operator pods to come up, 15 remaining.
Waited 150s for operator pods to come up, 13 remaining.
Waited 165s for operator pods to come up, 13 remaining.
Waited 180s for operator pods to come up, 11 remaining.
Waited 195s for operator pods to come up, 10 remaining.
Waited 210s for operator pods to come up, 8 remaining.
Waited 225s for operator pods to come up, 4 remaining.
Waited 240s for operator pods to come up, 3 remaining.
Operator pods ready.
Waiting for service pods to become ready.

Congratulations, Kubeflow is now available.
The dashboard is available at https://localhost/

    Username: admin
    Password: XXXXXXXXXXXXXXXXXXXXXXX

To see these values again, run:
    microk8s.juju config kubeflow-gatekeeper username
    microk8s.juju config kubeflow-gatekeeper password


To tear down Kubeflow and associated infrastructure, run:
   microk8s.disable kubeflow

kubeflow というnamespace が作られ、多くの deployment や service が作られています。0.5 の時よりかなり増えてる感じです。

$ kubectl get deploy,svc -n kubeflow
NAME                                          READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/ambassador                    1/1     1            1           19m
deployment.apps/argo-controller               1/1     1            1           16m
deployment.apps/argo-ui                       1/1     1            1           19m
deployment.apps/jupyter-controller            1/1     1            1           19m
deployment.apps/jupyter-web                   1/1     1            1           18m
deployment.apps/katib-controller              1/1     1            1           18m
deployment.apps/katib-manager                 1/1     1            1           17m
deployment.apps/katib-ui                      1/1     1            1           17m
deployment.apps/kubeflow-dashboard            1/1     1            1           17m
deployment.apps/kubeflow-gatekeeper           1/1     1            1           17m
deployment.apps/kubeflow-login                1/1     1            1           17m
deployment.apps/kubeflow-profiles             1/1     1            1           17m
deployment.apps/metacontroller                1/1     1            1           17m
deployment.apps/metadata                      1/1     1            1           15m
deployment.apps/metadata-ui                   1/1     1            1           14m
deployment.apps/modeldb-backend               1/1     1            1           15m
deployment.apps/modeldb-store                 1/1     1            1           16m
deployment.apps/modeldb-ui                    1/1     1            1           15m
deployment.apps/pipelines-api                 1/1     1            1           15m
deployment.apps/pipelines-persistence         1/1     1            1           15m
deployment.apps/pipelines-scheduledworkflow   1/1     1            1           15m
deployment.apps/pipelines-ui                  1/1     1            1           14m
deployment.apps/pipelines-viewer              1/1     1            1           14m
deployment.apps/pytorch-operator              1/1     1            1           14m
deployment.apps/tf-job-dashboard              1/1     1            1           15m
deployment.apps/tf-job-operator               1/1     1            1           15m

NAME                                           TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)             AGE
service/ambassador                             ClusterIP   10.152.183.73    <none>        80/TCP              19m
service/ambassador-operator                    ClusterIP   10.152.183.100   <none>        30666/TCP           19m
service/argo-controller-operator               ClusterIP   10.152.183.215   <none>        30666/TCP           19m
service/argo-ui                                ClusterIP   10.152.183.55    <none>        8001/TCP            19m
service/argo-ui-operator                       ClusterIP   10.152.183.134   <none>        30666/TCP           19m
service/jupyter-controller-operator            ClusterIP   10.152.183.104   <none>        30666/TCP           19m
service/jupyter-web                            ClusterIP   10.152.183.191   <none>        5000/TCP            18m
service/jupyter-web-operator                   ClusterIP   10.152.183.205   <none>        30666/TCP           19m
service/katib-controller                       ClusterIP   10.152.183.200   <none>        443/TCP             18m
service/katib-controller-operator              ClusterIP   10.152.183.189   <none>        30666/TCP           18m
service/katib-db                               ClusterIP   10.152.183.45    <none>        3306/TCP            18m
service/katib-db-endpoints                     ClusterIP   None             <none>        <none>              18m
service/katib-db-operator                      ClusterIP   10.152.183.48    <none>        30666/TCP           18m
service/katib-manager                          ClusterIP   10.152.183.222   <none>        6789/TCP            17m
service/katib-manager-operator                 ClusterIP   10.152.183.167   <none>        30666/TCP           18m
service/katib-ui                               ClusterIP   10.152.183.22    <none>        80/TCP              17m
service/katib-ui-operator                      ClusterIP   10.152.183.199   <none>        30666/TCP           18m
service/kubeflow-dashboard                     ClusterIP   10.152.183.237   <none>        8082/TCP            17m
service/kubeflow-dashboard-operator            ClusterIP   10.152.183.66    <none>        30666/TCP           18m
service/kubeflow-gatekeeper                    ClusterIP   10.152.183.240   <none>        8085/TCP            17m
service/kubeflow-gatekeeper-operator           ClusterIP   10.152.183.2     <none>        30666/TCP           17m
service/kubeflow-login                         ClusterIP   10.152.183.31    <none>        5000/TCP            17m
service/kubeflow-login-operator                ClusterIP   10.152.183.252   <none>        30666/TCP           17m
service/kubeflow-profiles                      ClusterIP   10.152.183.239   <none>        8081/TCP            17m
service/kubeflow-profiles-operator             ClusterIP   10.152.183.23    <none>        30666/TCP           18m
service/metacontroller                         ClusterIP   10.152.183.112   <none>        9999/TCP            17m
service/metacontroller-operator                ClusterIP   10.152.183.13    <none>        30666/TCP           18m
service/metadata                               ClusterIP   10.152.183.164   <none>        8080/TCP            15m
service/metadata-db                            ClusterIP   10.152.183.209   <none>        3306/TCP            16m
service/metadata-db-endpoints                  ClusterIP   None             <none>        <none>              16m
service/metadata-db-operator                   ClusterIP   10.152.183.67    <none>        30666/TCP           16m
service/metadata-operator                      ClusterIP   10.152.183.121   <none>        30666/TCP           15m
service/metadata-ui                            ClusterIP   10.152.183.182   <none>        3000/TCP            14m
service/metadata-ui-operator                   ClusterIP   10.152.183.6     <none>        30666/TCP           16m
service/minio                                  ClusterIP   10.152.183.213   <none>        9000/TCP            16m
service/minio-endpoints                        ClusterIP   None             <none>        <none>              16m
service/minio-operator                         ClusterIP   10.152.183.136   <none>        30666/TCP           17m
service/modeldb-backend                        ClusterIP   10.152.183.69    <none>        8085/TCP,8080/TCP   15m
service/modeldb-backend-operator               ClusterIP   10.152.183.94    <none>        30666/TCP           16m
service/modeldb-db                             ClusterIP   10.152.183.155   <none>        3306/TCP            15m
service/modeldb-db-endpoints                   ClusterIP   None             <none>        <none>              15m
service/modeldb-db-operator                    ClusterIP   10.152.183.56    <none>        30666/TCP           16m
service/modeldb-store                          ClusterIP   10.152.183.20    <none>        8086/TCP            16m
service/modeldb-store-operator                 ClusterIP   10.152.183.224   <none>        30666/TCP           17m
service/modeldb-ui                             ClusterIP   10.152.183.7     <none>        3000/TCP            15m
service/modeldb-ui-operator                    ClusterIP   10.152.183.130   <none>        30666/TCP           16m
service/pipelines-api                          ClusterIP   10.152.183.43    <none>        8887/TCP,8888/TCP   15m
service/pipelines-api-operator                 ClusterIP   10.152.183.34    <none>        30666/TCP           16m
service/pipelines-db                           ClusterIP   10.152.183.53    <none>        3306/TCP            16m
service/pipelines-db-endpoints                 ClusterIP   None             <none>        <none>              16m
service/pipelines-db-operator                  ClusterIP   10.152.183.32    <none>        30666/TCP           16m
service/pipelines-persistence-operator         ClusterIP   10.152.183.161   <none>        30666/TCP           15m
service/pipelines-scheduledworkflow-operator   ClusterIP   10.152.183.74    <none>        30666/TCP           15m
service/pipelines-ui                           ClusterIP   10.152.183.68    <none>        3000/TCP,3001/TCP   14m
service/pipelines-ui-operator                  ClusterIP   10.152.183.135   <none>        30666/TCP           15m
service/pipelines-viewer                       ClusterIP   10.152.183.61    <none>        8001/TCP            14m
service/pipelines-viewer-operator              ClusterIP   10.152.183.25    <none>        30666/TCP           15m
service/pytorch-operator-operator              ClusterIP   10.152.183.208   <none>        30666/TCP           14m
service/tf-job-dashboard                       ClusterIP   10.152.183.179   <none>        8080/TCP            15m
service/tf-job-dashboard-operator              ClusterIP   10.152.183.49    <none>        30666/TCP           15m
service/tf-job-operator-operator               ClusterIP   10.152.183.241   <none>        30666/TCP           15m

ブラウザで localhost に接続するとログイン画面が出るのでインストール完了時に表示された admin ユーザとパスワードでログインします。パスワードは microk8s.juju コマンドで再表示可能です。

$ microk8s.juju config kubeflow-gatekeeper password

おなじみの kubeflow の dashboard が表示されました。

f:id:kondoumh:20191215111758p:plain

v0.6.0-rc.0 が入ったようです。現在最新版は 0.7 で次は 1.0 になるみたいです。

kubeflow/ROADMAP.md at master · kubeflow/kubeflow · GitHub

ということで、MicroK8s の近い将来のリリースで Kubeflow 1.0 が入るようになるでしょう。

Kubeflow は導入障壁がまだ高いのですが、MicroK8s の Addon になるとかなり手軽になりますね。Kubeflow を利用する開発環境の構築にも良さそうです。今回は、GCP に 8 vCPU 30GB メモリという強めな VM を用意し GNOME と xrdp でリモートデスクトップ接続しました。Core i7 で 32GB メモリ程度の LInux マシンが用意できればけっこう使える環境が作れそうです。

Argo CD で GitOps 環境を構築する on GKE

GitOps というアプリデプロイ戦略が Cloud Native な CD *1 で普及し始めています。

www.weave.works

ソースコードのリポジトリとは別にアプリの設定用リポジトリを用意し、リポジトリの状態とデプロイの状態を一致させるようにツールを利用して管理します。これにより設定の変更を GitHub(GitLab) flow に載せることができ、オペミスやスクリプトのバグによる事故を防ぐことが可能になるとのことです。

GitOps の CD ツールの一つである Argo CD を GKE で使ってみました。

argoproj.github.io

GKE のクラスタを作って credentials を設定し kubectl で操作できるようにします。

$ gcloud container clusters get-credentials xxx-cluster --zone asia-northeast-a --project xxxxx

接続を確認。ノード2つのクラスタを作りました。

$ kubectl get node

NAME                                                STATUS   ROLES    AGE   VERSION
gke-standard-cluster-1-default-pool-xxxxxxxxxxxxxxx   Ready    <none>   11m   v1.13.11-gke.14
gke-standard-cluster-1-default-pool-xxxxxxxxxxxxxxx   Ready    <none>   11m   v1.13.11-gke.14

Argo CD が動作するための namespace argocd を作ります。

$ kubectl create namespace argocd
namespace/argocd created

Argo CD の現時点の最新リリース 1.3.6 のマニフェストを使ってインストールします*2

Release v1.3.6 · argoproj/argo-cd · GitHub

$ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/v1.3.6/manifests/install.yaml

customresourcedefinition.apiextensions.k8s.io/applications.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/appprojects.argoproj.io created
serviceaccount/argocd-application-controller created
serviceaccount/argocd-dex-server created
serviceaccount/argocd-server created
role.rbac.authorization.k8s.io/argocd-application-controller created
role.rbac.authorization.k8s.io/argocd-dex-server created
role.rbac.authorization.k8s.io/argocd-server created
clusterrole.rbac.authorization.k8s.io/argocd-application-controller created
clusterrole.rbac.authorization.k8s.io/argocd-server created
rolebinding.rbac.authorization.k8s.io/argocd-application-controller created
rolebinding.rbac.authorization.k8s.io/argocd-dex-server created
rolebinding.rbac.authorization.k8s.io/argocd-server created
clusterrolebinding.rbac.authorization.k8s.io/argocd-application-controller created
clusterrolebinding.rbac.authorization.k8s.io/argocd-server created
configmap/argocd-cm created
configmap/argocd-rbac-cm created
configmap/argocd-ssh-known-hosts-cm created
configmap/argocd-tls-certs-cm created
secret/argocd-secret created
service/argocd-dex-server created
service/argocd-metrics created
service/argocd-redis created
service/argocd-repo-server created
service/argocd-server-metrics created
service/argocd-server created
deployment.apps/argocd-application-controller created
deployment.apps/argocd-dex-server created
deployment.apps/argocd-redis created
deployment.apps/argocd-repo-server created
deployment.apps/argocd-server created

デプロイが完了したので、argocd の namespace を覗いてみます。

$ kubectl get deploy,pod,svc -n argocd

NAME                                                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.extensions/argocd-application-controller   1/1     1            1           3m29s
deployment.extensions/argocd-dex-server               1/1     1            1           3m29s
deployment.extensions/argocd-redis                    1/1     1            1           3m29s
deployment.extensions/argocd-repo-server              1/1     1            1           3m29s
deployment.extensions/argocd-server                   1/1     1            1           3m28s

NAME                                                 READY   STATUS    RESTARTS   AGE
pod/argocd-application-controller-59b7c6958d-676kv   1/1     Running   0          3m29s
pod/argocd-dex-server-658745cd65-kks5t               1/1     Running   0          3m29s
pod/argocd-redis-fc585c648-nr7z2                     1/1     Running   0          3m29s
pod/argocd-repo-server-6746fdc4dd-64c2x              1/1     Running   0          3m29s
pod/argocd-server-85b5959ffb-st7xt                   1/1     Running   0          3m28s

NAME                            TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)             AGE
service/argocd-dex-server       ClusterIP   10.4.1.108    <none>        5556/TCP,5557/TCP   3m30s
service/argocd-metrics          ClusterIP   10.4.5.166    <none>        8082/TCP            3m30s
service/argocd-redis            ClusterIP   10.4.3.179    <none>        6379/TCP            3m30s
service/argocd-repo-server      ClusterIP   10.4.8.63     <none>        8081/TCP,8084/TCP   3m30s
service/argocd-server           ClusterIP   10.4.7.233    <none>        80/TCP,443/TCP      3m29s
service/argocd-server-metrics   ClusterIP   10.4.15.178   <none>        8083/TCP            3m30s

外部から Argo CD に接続できるよう、LoadBalancer を設定します。

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

しばらく待つと GCP のネットワークサービスに検知されてロードバランサーが作成され、argocd-server に EXTERNAL-IP が割り当てられます。

$ kubectl get svc argocd-server -n argocd
NAME            TYPE           CLUSTER-IP   EXTERNAL-IP     PORT(S)                      AGE
argocd-server   LoadBalancer   10.4.7.233   xx.xx.xx.xx   80:30669/TCP,443:31636/TCP   16m

割り当てられた IP にブラウザでアクセスすると、Argo CD のログイン画面がでます*3

f:id:kondoumh:20191214104659p:plain

admin のパスワードは argocd-server の pod 名になっていますので、kubectl で取得してログインします。

f:id:kondoumh:20191214105054p:plain

今回はアプリを作る手間を省くため Argo CD の example リポジトリをフォークして GitOps の設定をします。

github.com

Argo CD の Setting -> Repositories を開きます。

f:id:kondoumh:20191214111337p:plain

public なリポジトリとしてフォークしたので認証情報なしで CONNECT REPO USING HTTPS に URL を設定するだけです。

f:id:kondoumh:20191214111539p:plain

接続に成功しました。

f:id:kondoumh:20191214111628p:plain

デプロイするアプリ用の namespace を作っておきます。

$ kubectl create ns prod
namespace/prod created

CREATE NEW APP を クリックして、デプロイ対象のアプリ情報を登録します。example の helm-guestbook をデプロイするので、先に登録したリポジトリを選択して Path で helm-guestbook を選択。

f:id:kondoumh:20191214112223p:plain

アプリの namespace は先ほど作成した prod を指定。

f:id:kondoumh:20191214112424p:plain

CREATE をクリックすると GitHub との Sync が開始されデプロイが始まります。

f:id:kondoumh:20191214112557p:plain

kubectl で確認。すでに ロードバランサーも作られてアプリがデプロイされています。

$ kubectl get deployment,svc,pod -n prod
NAME                                             READY   UP-TO-DATE   AVAILABLE   AGE
deployment.extensions/guestbook-helm-guestbook   2/2     2            2           4m2s

NAME                               TYPE           CLUSTER-IP   EXTERNAL-IP    PORT(S)        AGE
service/guestbook-helm-guestbook   LoadBalancer   10.4.11.37   xx.xx.xx.xxx   80:32362/TCP   4m3s

NAME                                            READY   STATUS    RESTARTS   AGE
pod/guestbook-helm-guestbook-764f9f8885-bqzpz   1/1     Running   0          4m2s
pod/guestbook-helm-guestbook-764f9f8885-hqvpd   1/1     Running   0          4m2s

アプリの IP にアクセスすると確かに起動しています。

f:id:kondoumh:20191214113433p:plain

Argo CD でアプリの詳細を見ると、kubectl で取得できる情報がグラフィカルに閲覧可能です。

f:id:kondoumh:20191214113535p:plain

グラフの各ノードをクリックすると、詳細情報が出て、状態、マニフェスト、イベント、ログなどを確認できます。

f:id:kondoumh:20191214114410p:plain

GitOps なので、設定のリポジトリを更新するとそれに応じてデプロイされたアプリの状態も変化します。

ということで、フォークしたリポジトリの設定ファイルで replicaCount を増やしてみます。

f:id:kondoumh:20191214114639p:plain

3分ほど待っていると、Pod が1個増えました。

f:id:kondoumh:20191214115010p:plain

kubectl でも確認できます。

$ kubectl get pod -n prod
NAME                                        READY   STATUS    RESTARTS   AGE
guestbook-helm-guestbook-764f9f8885-bqzpz   1/1     Running   0          29m
guestbook-helm-guestbook-764f9f8885-hqvpd   1/1     Running   0          29m
guestbook-helm-guestbook-764f9f8885-rfb6c   1/1     Running   0          4m25s

以上のように Argo CD は Git リポジトリとアプリのデプロイ状態を同期させることができます。

リアクティブな Web UI が使いやすいですが、もちろん argocd という CLI も提供されています。Helm chart 以外にも Kustomize / ksonnet などのパッケージにも対応しており、個別のツールをインストールしなくてもデプロイできます。

ビルド・テストやコンテナイメージの作成を CI で実施し GitOpt ツールを CD に適用すれば、開発からデプロイまでの自動化を実現できそうです。

*1:継続的デリバリ

*2:Helm chart によるインストールも可能ですが、今回は素のマニフェストを使いました。

*3:TLS 設定がないので警告が出ますが