この記事について
半年前からk8sの勉強を始め、GitOps手法でアプリをデプロイする知見を得ることができたので学んだことのまとめ記事です。
GitOps手法とは?
weave.worksが提唱する開発手法です。
https://www.weave.works/blog/gitops-operations-by-pull-request
GitOps is a way to manage systems like Kubernetes
訳: GitOpsは、Kubernetesなどのシステムを管理する方法です
・・・とあるようにk8sで構築するシステムの管理手法と考えて良いと思います。
実際にGitOpsのキーワードで色んな記事を読んできましたが、今の所k8s以外は見たことがありません。
リソースは基本的にGitで管理し、本番環境への反映はプルリクによる操作が望ましいとされています。
この記事でもソースを管理するアプリリポジトリとk8sのyamlを管理するマニフェストリポジトリの2つを使用して構築していきたいと思います。
今回構築する全体像について
- アプリはReact(create-react-app)を使用します
- Nodeへのスケジューリング内容はあくまでイメージです。必ずしも図のように配置されるわけではありません
- ArgoCDからデプロイされるk8sリソースについてもイメージです。HELMのカスタムValue(後述)の内容によってデプロイされるリソースも変わってきます
- GCPのロードバランサーなど記事の内容と直接関係ない部分については省略しています
ではここからが本題です。全体像を区分けして解説していきます。
1. GitHubActionsを使用してDockerイメージをGCRへpush
ここで使用する技術について
-
GitHubActions・・・GitHubのCIツールです。GitHubリポジトリでのイベントを検知してテストやDockerfileのbuild・pushなどのワークフローを実行できます。
-
GCR・・・GoogleContainerRegistryの略で、Dockerイメージのリポジトリサービスです。要はGCP版のDockerHubのようなもの。
アプリリポジトリについて
完成版はこちらになります。
https://github.com/Nishi53454367/gitops_app_create-react-app
create-react-app作成手順について
プロジェクト初期化手順はREADMEに記載してあります。
今回の記事の内容とは関係ないのでこちらの解説は割愛します。
Dockerfileについて
- docker/Dockerfile.devは開発用コンテナです。docker-compose.ymlで使用しています
- docker/Dockerfileがk8sのPodに配置するコンテナです。これをGitHubActionsのワークフローで使用します。ReactリソースをビルドしてNginxのデフォルトディレクトリにコピーしているだけの必要最小限の作りになっています
GitHubActionsワークフローについて
作成手順
GitHub上で対象リポジトリのActionsのタブを選択し、テンプレートを選択します。
今回は以下をベースに修正していきました。
完成版は以下のようになっています。
name: Docker Image CI
on:
# mainブランチへのpushをトリガーにworkflowを実行
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
# GCPプロジェクトの認証
- name: GCP Authenticate
uses: google-github-actions/setup-gcloud@master
with:
version: '290.0.1'
project_id: ${{ secrets.GCP_PROJECT_ID }}
service_account_key: ${{ secrets.GCP_SA_KEY }}
export_default_credentials: true
- name: Configure docker to use the gcloud cli
run: gcloud auth configure-docker
# Dockerfileのbuild
- name: Build the Docker image
run: docker build . -f docker/Dockerfile -t gcr.io/${{ secrets.GCP_PROJECT_ID }}/create_react_app:${{ github.run_number }}
# DockerfileをGCRへpush
- name: Push the docker image
run: docker push gcr.io/${{ secrets.GCP_PROJECT_ID }}/create_react_app:${{ github.run_number }}
-
「GCPプロジェクトの認証」の部分は以下を参考に作成しています
https://github.com/google-github-actions/setup-gcloud/tree/master/setup-gcloud -
GCP_PROJECT_ID、GCP_SA_KEYはこの名前の機密情報をGitHubのSecretsから取得します(後述)
-
GCRへDockerイメージをpushする時は
ホスト/プロジェクトID/{任意のイメージ名:任意のtag}
でタグ付けをする必要があります。- ホストは米国内データセンターの「gcr.io」と「us.gcr.io」、欧州連合の「eu.gcr.io」、アジアのデータセンターの「asia.gcr.io」と何種類か選択できますが、今回は「gcr.io」を使用します
- {任意のイメージ名:任意のtag}の部分は
create-react-app:ワークフローの実行回数
としています。任意のtagの部分は1からインクリメントされていきます
GCP上での作業
GCPプロジェクトについて
今回はgitops-react-app
(プロジェクトIDも同様)という名前のプロジェクトを使用します。
GCRのAPIを有効化
メニューから「Container Registry」を選択して「Container Registry APIを有効化」を押してGCRを使用できる状態にしておきます。
サービスアカウントキー作成
メニューから「IAM と管理」→「サービス アカウント」を選択し、以下の内容でサービスアカウントを作成します。
サービスアカウント名、説明の内容はなんでも大丈夫です。
IDはサービスアカウント名を元に自動入力されます。
似たようなロールで「ストレージオブジェクト管理者」があるので注意です。
作成したサービスアカウントの右側のメニュー(「・・・」を縦並びにしたやつ)から「鍵を作成」でJSON形式を指定してダウンロードします。
GitHub上での作業
SecretsにGCP機密情報を設定
先ほど自分のマシンにダウンロードした鍵をbase64でエンコードします。
cat {ダウンロードした鍵のパスを指定} | base64
鍵本体もですがこの値も簡単にデコードできるので流出しないように注意してください。
この鍵のbase64エンコード値とGCPプロジェクトIDを.github/workflows/docker-image.ymlで使用しているSecrets名でGitHubのSecretsへ設定すれば準備は完了です。
GitHubリポジトリで「Settings」→「Secrets」→「New repository secret」で以下の内容で設定を行います。
Name | Value |
---|---|
GCP_PROJECT_ID | gitops-react-app |
GCP_SA_KEY | 鍵のbase64エンコード値 |
以下のように設定できればOKです。
ここで設定した内容はUpdateを押しても現在の設定値を見ることができず、上書きという形での変更となっています。また、リポジトリをforkしても値が引き継がれることはありません。
動作確認
ではmainブランチへのpushでGCRへDockerイメージが登録されるか確認してみます。
任意の更新を行い、mainブランチへpushを行うと、GitHub上の「GitHubActions」タブでワークフローが実行されていることを確認できます。
こんな感じで緑のチェックマークが付いていることが確認できれば成功です。
GCRを再度確認すると、Dockerイメージが登録されていることが確認できます。
イメージを選択するとDockerイメージをpullする時に必要なリポジトリ情報が確認できます。
以下の部分をHELMのカスタムValue(後述)に設定するので控えておきます。
- リポジトリ:
gcr.io/gitops-react-app/create_react_app
- タグ:
3
タグはワークフローの実行回数を指定しているので、これは3回目の実行であることがわかります。
以上でCIの設定・動作確認が完了です。
2. マニフェストリポジトリをArgoCDで監視
ここで使用する技術について
-
HELM・・・k8sリソースのテンプレートパッケージです。穴埋め方式のテンプレートとPodの数やDeploymentでpullするDockerイメージなど環境によって変更したい値の変数を組み合わせk8sリソースをデプロイすることができます。
この変数を定義するファイルのことをこの記事内でカスタムValueファイルと呼んでいます。アプリケーションでいう環境変数ファイル(.env)と似たようなイメージです。
環境に応じたカスタムValueファイルを用意しておくことで、STG環境と本番環境でyamlを1から準備する必要がなくなります。
繰り返しになりますがHELMは穴埋め方式なので、カスタマイズ性が弱いと思います。(やろうとすると分岐だらけの複雑なテンプレートになりそう。)
複数のアプリでテンプレートを使い回したいような時はKustomizeの方が適していると思います。 -
ArgoCD・・・k8sクラスタにインストールして使用するpull型のCDツールです。
使い始めて間もないですが、GUIもわかりやすくk8s初学者にとっても使いやすいツールだと思います。
マニフェストリポジトリについて
完成版はこちらになります。
https://github.com/Nishi53454367/gitops_manifest_helm
HELM作成手順について
プロジェクト初期化手順はREADMEに記載してあります。
create helm
コマンドを使用して作成すると、deploymentテンプレートがデフォルトでポート80をListenしているシングルPodの構成となっています。
今回は特別な修正を加えなくてもReactアプリをビルドしたNginxコンテナをこのPod内に配置してあげるだけでOKですが、実際にはアプリケーションの構成に合わせた修正が必要になると思います。
カスタムValueファイルについて
今回はカスタムValueファイルは1つだけ作成しています。
実際には「開発用、STG用、本番用」や「サービスA用、サービスB用」など複数用意するケースが多いと思います。
カスタムValueに定義がないとcreate helm
した時に作成されるvalues.yamlがデフォルト値となります。よって、values.yamlを見ればどんな値をカスタムで設定できるか確認できます。
もちろんtemplates配下を修正して任意の名前でセットできるように修正も可能です。
今回作成したカスタムValueファイルは以下の通りです。最小限の設定です。
# Pod
replicaCount: 1
# DockerImage
image:
repository: gcr.io/gitops-react-app/create_react_app
tag: "3"
# Service
service:
type: LoadBalancer
DockerImageの部分はCI構築時に確認したDockerリポジトリの情報をセットします
GCRはprivateリポジトリですが、同じGCPプロジェクト内で構築したGKE上で使用するので認証は不要でDockerイメージをpullできます。
ちなみにファイル名はxxxvalues.yaml
の形式にしておかないとArgoCD管理画面で設定する時(後述)に候補として出てこなかったので、このルールでファイルを作成しておきます。
GCP上での作業
GKEでクラスタを作成
メニューから「Kubernetes Engine」を選択し、以下の内容でクラスタを作成します。
節約のため以下の通りにしていますが、今回の記事の内容とは関係ないので、好きに変更して問題ありません。
- ゾーンはus-west1-a
- マシンタイプは全てe2-small
- プリエンティブルノード有効
クラスタへArgoCDインストール
作成したクラスタを選択して「接続」を押し、CloudShell接続を行い、以下のコマンドを実行していきます。
ArgoCD用のネームスペースを作成
kubectl create namespace argocd
ArgoCDインストール
Non-HA構成の場合(今回はこっちでやります)
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
HA構成の場合
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/v1.0.1/manifests/ha/install.yaml
ArgoCD管理画面を外部から接続できるようにpatch
インストールが終わったら以下のコマンドを実行
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'
ArgoCD管理画面上での作業
ログイン
メニューの「Kubernetes Engine」→「Services と Ingress」を選択してargocd-server
のエンドポイントをクリックしてArgoCDの管理画面を起動します。オレオレ証明書なので警告出ますが続行すると以下の画面が表示されます。
Usernameは、admin
Passwordは、ArgoCDのPod名がデフォルトのパスワードになっています
CloudShellで以下のコマンドを実行することで一覧表示されるargocd-server-xxxxx
のPod名がパスワードになっています。
kubectl get pods --namespace argocd
マニフェストリポジトリ設定
左側のメニューで歯車マークを選択し、「Repositories」を選択します。
「CONNECT REPO USING HTTPS」を選択し、リポジトリの内容を設定します
Repository URLにマニフェストリポジトリのURLを入力して「CONNECT」を押します。
リポジトリがprivateの場合は、GitアカウントのUsernameとPasswordに入力すれば接続できます。※1
以下のようになれば設定完了です。
アプリ作成
GENERAL
Application Nameは、任意の名前でOKです。この名前がデプロイされるk8sリソース名に使用されます。
SOURCE
- Repository URLは先ほど設定したマニフェストリポジトリを選択します
- Revisionは監視対象のブランチを指定します。今回はmainブランチとしています
- PathはRevisionを入力するとカレントディレクトリにHELMテンプレートが格納されているので、「.」が選択できるようになります。ここで候補値として「.」が出てこない場合、どこか設定が誤っている可能性があります。
DESTINATION
Namespaceは今回指定しないのでdefaultにします。
HELM
VALUES FILESの部分でカスタムValueファイルが選べるようになっているので、デプロイしたい内容のファイルを選択します。
ここまでの内容で「CREATE」を押すと以下のカードが作成されます。
これでデプロイの手前までが完了です。
3. ArgoCDでデプロイ
では最後にデプロイを行います。
引き続きArgoCD管理画面で操作します。
デプロイ実行
作成したカードを選択し、「SYNC」を押します。
続けて「SYNCHRONIZE」を押すとデプロイが始まります。
しばらく待って以下のようになれば完了です。
接続確認
メニューの「Kubernetes Engine」→「Services と Ingress」を選択するとArgoCDでデプロイしたリソースcreate-react-app-web
が追加されています。
エンドポイントをクリックするとReactの初期ページを開くことができます。
これで一通り完了です。
ArgoCDでデプロイしたリソースであれば、ステータスやイベントログなどArgoCDの管理画面上で確認できます。もちろんkubectlコマンドでの操作も可能です。
変更内容の反映
マニフェストリポジトリを変更するとArgoCDが変更を検知します。
試しにカスタムValueファイルで定義しているPod数を変更してpushします。
# Pod
replicaCount: 3 # ここを1から3に変更
# DockerImage
image:
repository: gcr.io/gitops-react-app/create_react_app
tag: "3"
# Service
service:
type: LoadBalancer
ArgoCDは一定間隔でマニフェストリポジトリを監視しているので、しばらく待っているとステータスがOutOfSyncに変わります。(「REFRESH」を押して即時にOutOfSyncに変更することも可能です。)
この状態で先ほどと同様に「SYNC」→「SYNCHRONIZE」するとデプロイが行われ、Podが3つ増えます。
カスタムValueファイルを変更しただけですが、当然templates配下を変更しても検知されます。
アプリの変更を反映したい場合は、
アプリ修正してアプリリポジトリへpush
↓
GitHubActionsのワークフローによってGCRへ新しいタグのDockerイメージが登録
(ワークフローの実行回数をタグに含めているのでタグが3から4にインクリメントされる)
↓
あとは同じ要領でカスタムValueファイルを変更してデプロイするだけです。
カスタムValueの修正例
# Pod
replicaCount: 3
# DockerImage
image:
repository: gcr.io/gitops-react-app/create_react_app
tag: "4" # ここを3から4に変更
# Service
service:
type: LoadBalancer
まとめ
今回まとめたのは基本的な使い方のみでしたが、実際にチームで運用する時は、Dockerイメージのタグ付けルールやDBのマイグレーションのタイミングなどなど検討しないといけないことはたくさんあります。運用事例は今後色んな記事を読んだりして勉強したいと思っています。
また、ArgoCDはまだまだ知らない機能が多いと思うので、今後もっと使い込んでいきたいと思います。GUIベースでの操作でしたが、CLI操作も可能なので何か他のサービスと組合わせてデプロイをもっと効率化できないかなーとかも思ったり。
以上、GitHubActionsとArgoCDとHELMを使用したGitOps手法の構築でした。
追記
※1 GitHubではパスワードを使用した認証は将来的に廃止されます。
https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/
したがってGitHubのprivateリポジトリへの認証はGitHub側で発行したアクセストークンを使用するようにしてください。
ArgoCDへのアクセストークンの設定方法は以下の公式ページの通り。
https://argoproj.github.io/argo-cd/user-guide/private-repositories/#access-token
基本的にPassword欄に発行したアクセストークンを設定するだけです。