Kubernetes 上で SlackBot の GitOps

公開:2020/09/28
更新:2020/09/28
21 min読了の目安(約12800字TECH技術記事

こんにちは、当記事は Qiita: Kubernetes Advent Calendar 2019 その2 の7日目の記事です。

Kubernetes クラスタに Drone, Argo CD, Sealed Secrets をインストールし GitOps を実現する方法についての記事です。

モチベーション

自分は Kubernetes の勉強と趣味を兼ねて、自宅に Kubernetes クラスタを自前の Ansible Playbook で構築し運用しています (Ansible による Kubernetes 構築については別記事を書くかもしれません)。
当 Kubernetes 上では個人用の Wiki サービスや監視サービス等が動いており、これにより Kubernetes の扱い方について学ぶことができています。

しかしながら自前の Docker Image を Kubernetes 上で動作させることはしていなかったため、 CNCF Trail Map の2段階目である CI/CD を今まで導入していませんでした。
そのため今回は、簡単なSlackBotを実装し Kubernetes 上で CI / Kubernetes 上に CD します。

GitOps とは

GitOps は Weaveworks が提唱した CD のプラクティス[1] で以下のような特徴があります。

  • 宣言的なリソース管理
  • Git によりバージョン管理され、source of truth (GitリポジトリのHEAD = 実際に動作してるバージョン) である
    • 切り戻しの際も git revert を用いる
  • Git リポジトリの変更を自動でシステムに適用する
    • GitリポジトリのHEAD = 実際に動作してるバージョン を自動で維持し続ける

GitOps は宣言的 API を提供している Kubernetes と相性が良いものとして注目が集まっています。

構築

  • 環境
    • Kubernetes: v1.13.11-gke.14
    • Drone: v1.6.0
    • Argo CD: v1.2.3
    • Sealed Secrets: v0.9.1

マニフェストファイルを用いKubernetes上にCI/CDツールの構築を行います。なお、検証環境のKubernetesクラスタは v1.13.11-gke.14 になります。
(上で自宅K8sと言っときながら何で GKE なんじゃいという感じですが、自宅 Kubernetes 上には既に Drone や Argo CD 等が動作しており、今回以下の手順作成用にまっさらな環境が欲しかったため GKE で実施しています:bow:)

Drone

Drone は基本的に GitHub や Bitbucket 等の Git リポジトリから発せられる Webhook のみを Trigger として起動する[2] CI/CD ツールとなります。

01.png

Drone は大きく分けて v0.x 版と v1.x 版が存在し、Web で Drone について調べるとこれらのバージョンが錯綜していました。今回は (検証時点で) 最新版である v1.6.0 を利用しました。

--- 以下 2019/12/10 追記 (@kaitoy さん ありがとうございます) ---

なお注意点として、公式が配布している Drone v1.x のコンテナイメージは Enterprise 版となっており、無料では年間5000ビルドしかできません。当制限を気にされる方は Opensource版 (https://github.com/drone/drone より自前でソースコードからビルド) を利用してください。
なお、Opensource 版は一部機能が制限されます。詳しくは以下を確認してください。

--- ここまで ---

インストール

インストール方法のドキュメントは以下です。

Drone は、Drone Service と 1 つ以上の Drone Runner より構成されると書かれています。Jenkins で言う Jenkins Master & Jenkins Slave と同じ関係ですね。

なお、今回は使用する Git リポジトリは GitHub , Runner は Docker Runner を利用しました。執筆してる現在は v1.x 版 に Kubernetes Runner (CI が Kubernetes Job として動作) が確認できる[3]のですが、動作検証時点では存在していなかったため今回は Docker Runner を利用しています (この辺り、v0.x 版だと元々 Kubernetes Runner があったみたいで情報が錯綜してました・・)。また、Drone の Database Driver[4] にはインストール簡略化のために SQLite3 を用います (そのため Drone Server は Replica 数 1 の StatefulSet で動作させます)。

マニフェストファイルは 公式のHelmチャート を参考に1から記述しました。以下を参考にして下さい。

  • https://github.com/ShotaKitazawa/deploy-repo-20191207/tree/master/drone
    • 一部値が抜けている箇所があるため、動作させる場合は各自で値を入れて下さい。
      • server-ingress.yaml : spec.rules[0].host : $DRONE_FQDN (GlobalAddress に名前解決可能なドメイン) を記入
      • server-configmap.yaml :
        • DRONE_SERVER_HOST : $DRONE_FQDN (GlobalAddress に名前解決可能なドメイン) を記入
        • DRONE_GITHUB_CLIENT_ID , DRONE_GITHUB_CLIENT_SECRET : 各自で GitHub の ClientID/Secret を取得して記入 (Authorization callback URLhttps://$DRONE_FQDN/login)

なお注意点として、当マニフェストの適用により建つ Drone Server は Web から、GitHub の OAuth 認証 -> 無認可で利用できてしまいます。長期運用する場合は認証の設定を別途行って下さい (自分は自宅K8sにて ingress-nginx を利用しているため、 Ingress.metadata.annotation より設定できる Basic 認証をかけています) 。

利用方法

ブラウザより https://$DRONE_FQDN/ (Ingressリソースで指定したドメイン) へアクセスすると、GitHub の OAuth 認証が求められます。

ログイン後、以下のような画面になります。該当リポジトリをクリックし、ACTIVATE ボタンを押すことで、該当リポジトリから Drone Server への Webhook が自動で登録され、CI が有効化されます。

02.png

Argo CD

Argo CD は動作環境が Kubernetes であることを前提とした CD ツールです。Argo CD が常に Git リポジトリをポーリング監視し HEAD のマニフェストに差分が生じた際は Kubernetes クラスタに自動で適用するような動作となります。
Argo CD Controller により Git リポジトリ上のリソースも Reconciliation loop の監視対象にするという理解です。

Argo CD のこの動作は、[GitOps とは](#GitOps とは) にて述べた GitOps の特徴と一致します。

03.png

インストール

インストール方法のドキュメントは以下です。

Argo CD の単純なインストールは、https://github.com/argoproj/argo-cd にある利用したいバージョンの manifest ファイルを kubectl apply -f するだけです。これにより Argo CD のコントローラと CRD が Kubernetes 上に適用されます。今回は (検証時点で) 最新版である 1.2.3を利用しました。

kubectl create namespace argo-cd
kubectl apply -n argo-cd -f https://raw.githubusercontent.com/argoproj/argo-cd/v1.2.3/manifests/install.yaml

上記マニフェストのみだと外部疎通性がないですが kubectl port-forward コマンドより手元PCから Argo CD の Web UI に入ることができるため、当検証ではそのようにします。

外部疎通性や初期パスワードの変更等についてはドキュメントを参考にして下さい。

利用方法

argocd-server Service への Port Forward 越しにアクセスします。

kubectl port-forward svc/argocd-server -n argocd 8080:80

ブラウザより https://localhost:8080/ へアクセスするとログイン画面が表示されます。初期ユーザ/パスワードは以下になります。

  • username: admin
  • password: 初回デプロイ時の argocd-server の Pod 名

ログインすると以下の画面が表示されます。

04.png

Argo CD がポーリング監視するマニフェストは、Argo CD の提供する CRD である Application リソースにて定義可能です。具体的な定義方法は [以降の手順](#Argo CD の設定) にて説明します。

Sealed Secrets

ここまでのソフトウェアで GitOps は実現できますが、このままだと Kubernetes 上に GitHub Client Secret 等の秘密情報をデプロイするために GitHub に秘密情報をアップロードしなければならなく、セキュリティ上の問題があります (Secretリソースも value が base64 エンコードされているだけなので安全ではないです)。

そのため当記事ではSealed Secrets を利用します。Sealed Secrets はSecretsリソースのvalueを暗号化した SealedSecrets リソースを CRD で提供します。Sealed Secret は以下のように動作するため、Git リポジトリに置くマニフェストファイルには暗号化されたデータのみが乗るようになります。

05.png

インストール

インストール方法は GitHub の README に記述されています。

今回は (検証時点で) 最新版の v0.9.1 をインストールします。

kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.9.1/controller.yaml

なお SealedSecrets の性質上、Kubernetes のSecretリソースとして保存される秘密鍵に対しバックアップなどの管理をしなければならないですが、今回は省略します。

利用方法

kubeseal という CLI ツールを用い、SecretリソースからSealedSecretリソースを生成します。

  • kubeseal のインストール (MacOS)
brew install kubeseal
  • Kubernetes クラスタ上の秘密鍵に対応した公開鍵は以下のコマンドで取得できます。
$ kubeseal --fetch-cert
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
  • 取得できない場合は、kubectl コマンドより直接 Secret リソースの公開鍵を取得します。
$ kubectl get secrets -n kube-system sealed-secrets-keyXXX -o "jsonpath={ .data.tls\.crt }" | base64 -D
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
  • この公開鍵 (~/cert.pem) を用いて Secret リソースを SealedSecret リソースに変換します。
kubeseal -o yaml --cert ~/cert.pem < `対象のSecret.yamlファイル` > sealedsecret.yaml
  • SealedSecret リソースを Kubernetes クラスタにデプロイすると、自動で Secret リソースが作成されることが確認できます。
# リソースの適用
kubectl apply -f sealedsecret.yaml

# SealedSecret リソースの取得
kubectl get sealedsecret

# Secret リソースの取得
kubectl get secret

GitOps の全体像

上で構築したソフトウェアを用い、GitOps によるアプリケーションのデプロイを行います。デプロイフローは以下のようになります。

06.png

上の図の動作を1つずつ順に追っていきます。

  1. アプリケーション開発者が Application Repository の master ブランチに push
  2. 1 を契機に Drone に Webhook が飛び、CI として以下を実行
    • アプリケーションのテスト (今回は省略)
    • アプリケーションの Docker Image を Build
    • アプリケーションの Docker Image を Docker Registry (DockerHub) に push
    • Manifest Repository から リポジトリを clone し、Spec.Metadata.Labels の値や Containers の ImageTag 等を更新し Manifest Repository に push
  3. Manifest Repository にあるマニフェストファイルを Argo CD が Polling 監視しており、更新が走ると差分を Kubernetes クラスタへ Apply を行う
  4. Kubernetes上の各種リソースが更新される、Deployment.spec.templates 以下が更新されるので Pod の再作成による Docker Image の pull が走る

アプリケーション用リポジトリの作成

デモ用のアプリケーション用リポジトリは以下になります。

当アプリケーションは、以下のようにチャットメッセージをオウム返しするだけの Bot となっています。

07.png

以下のコマンドのように AppID と Token を与えることで Bot が起動します。 (2019/12/07 現在、 Token (Bot User OAuth Access Token) と AppID は右の URL 先から取得可能です: https://api.slack.com/apps)

# XXX には任意の値が入ります
SLACK_BOT_APP_ID=XXX SLACK_TOKEN=XXX go run main.go

マニフェスト管理用リポジトリの作成

次に、デモ用のマニフェスト用リポジトリは以下になります。

/mybot/ 以下に, 上記の SlackBot を Kubernetes 上で動作させるためのマニフェストファイルが配置されています。ここで注目する点が2つあります。

1つ目は、SLACK_BOT_APP_IDSLACK_TOKEN は Sealed Secret で暗号化されていることです。Slack Token の情報は秘密情報となるので、Gitリポジトリにアップロードする際には Sealed Secrets 等で暗号化します。

2つ目は、Deployment や Pod に付く Labels に deployDatecommitHash があることです。アプリケーションリポジトリの更新契機で走る Drone が当箇所の value を書き換えることで、現在のマニフェストリポジトリの HEAD (及びKubernetesにデプロイされたアプリケーション) がアプリケーションリポジトリのどのバージョンと対応しているかがひと目で分かるようになります。

Drone の設定

アプリケーションリポジトリのCIの設定を行います。

まず、Drone の WebUI より該当リポジトリを Activate します。今回はアプリケーションリポジトリが対象です。

08.png

次に、 Drone で CI を行う際に必要な秘密情報を、Drone の WebUI から登録します。それぞれ、以下の変数名で値を登録します。

  • deploy_key : GitHub のマニフェストリポジトリを更新するために必要な秘密鍵
  • docker-username : Docker Image の push 先 Docker Registry (今回は Docker Hub) のユーザ名
  • docker-password : Docker Image の push 先 Docker Registry (今回は Docker Hub) のパスワード

09.png

上記設定後、アプリケーションリポジトリに .drone.yml ファイルを配置して GitHub に Push することで、Drone による CI が動作します。
当検証における .drone.yml は以下となります。

  • https://github.com/ShotaKitazawa/demo-application-repo-20191207/blob/master/.drone.yml
    • git-commit-hash step : アプリケーションリポジトリの Git Tag, Git CommitHash を取得
    • docker step : Docker Image をビルドし、Git Tag, Git CommitHash, latest タグを付与して Docker Registry に Push
    • update-manifest step : マニフェストリポジトリにあるマニフェストファイルの Label や image tag 更新する。

Argo CD の設定

Argo CD を用い、 マニフェストリポジトリが更新された際に自動で Kubernetes へ差分の反映を行うようにします。

Argo CD がポーリング監視するマニフェストは、Argo CD の提供する CRD である Application リソースにて定義可能です。今回は以下のように定義します。

自分が GKE で動作させた際以下のようなエラーが出力されました。backendconfigs という GKE の提供する CRD に対する LIST の権限が足りないと怒られています。

10.png

今回は検証なので、 system:serviceaccount:argo-cd:argocd-application-controller に対し cluster-admin 権限を渡してしまいます。GKE 上で長期運用する場合は適切な権限を与えて下さい。

kubectl create clusterrolebinding argocd-application-controller-cluster-admin --clusterrole=cluster-admin --user=system:serviceaccount:argo-cd:argocd-application-controller

動作確認

Argo CD の Application リソースを Kubernetes に適用した時点で Bot がデプロイされます。
また、アプリケーションリポジトリを更新すると、マニフェストリポジトリの該当ファイルが自動で書き換わり、Argo CD により差分が Kubernetes に自動で適用されます。

ここまでで構築したデプロイフローを再掲します。

06.png

試しに Bot の動作を 「オウム返しを2回する」に変更してみます。

アプリケーションリポジトリの以下のコミットにて、Bot の動作を変更しました。

アプリケーションリポジトリに Push を行ったことを契機に Drone による CI が走ることが確認できます。

11.png

当 CI の終了後にマニフェストリポジトリを見ると、Drone より自動で積まれたコミットがあることが確認できます。

そして Slack にてメッセージを送信すると、Bot が2回オウム返しすることを確認できました!

12.png

まとめ

Kubernetes に Drone, Argo CD, Sealed Secrets をインストールして GitOps を実現することができました。
GitOpsの利点として、デプロイされているバージョンがGitリポジトリのHEADであることが保証されているのでリポジトリを見れば何が動いているかが分かることが挙げられます。
今回利用した Argo CD は GitOps を実現するために必要最低限の機能を持つとてもシンプルで良いツールであると感じています。

今回の検証用リポジトリは以下となります。

現在自宅の Kubernetes ではこの CI/CD 環境を利用して、個人利用の静的なポータルサイトや SlackBot が動いてます。これからGitOpsを導入する方に、当記事及び上記の検証用リポジトリが参考になれば幸いです。

Twitter やってます、何かわからないことがあったら気軽に聞いて下さい!

注釈

脚注
  1. 原文: https://www.weave.works/technologies/gitops/ ↩︎

  2. 厳密には、GitリポジトリからのWebhook以外に既に実行したジョブの Restart もサポートされています。 ↩︎

  3. ソースコード: https://github.com/drone-runners/drone-runner-kube ↩︎

  4. ドキュメント: https://docs.drone.io/installation/reference/drone-database-driver/ ↩︎