GitHub Actionsを使ってラズパイk8sクラスタの自動デプロイ環境を作る
最近Netflixを登録し、ジョジョを一気観しました
6部のアニメ化も期待しています
はじめに
以前作成した ラズパイのk8sクラスタ 上にアプリケーションをデプロイする際に、今までは同じネットワークにあるローカルMacから直接kubectlやhelmコマンドでデプロイを行ってました
利用するDockerイメージのアップロードはGitHub Actionsを利用してgit push → DockerHubへのアップロードを自動化していましたが、今回はk8sへのデプロイ部分もGitOpsで自動化していきたいと思います 💪
GitHub Actionsを利用してラズパイ(複数アーキテクチャ)向けのイメージを作る方法は↓こちら
TL;DR
- GitHub Actionsのセルフホストランナーを利用する
- 構築パターンは以下の2つ
- ラズパイ上に直接構築する
- 専用のイメージを作成し、k8sのPodとして構築する
- 作成した専用イメージ
- 必要なツールをラズパイ上もしくはイメージに含め、GitHub Actionsのジョブ上で利用してデプロイを行う
GitHub Actionsのセルフホストランナー
セルフホストランナーとは独自の環境(VMなど)をGitHub Actionsのジョブ実行環境にすることができる機能です
ポーリングによってGitHubとの通信を行うため、自分のラズパイのように外部に穴あけされていない環境でも利用することができます
セルフホストランナーを利用する上での注意点として、パブリックリポジトリで利用しないことが推奨されています
パブリックリポジトリのフォークでは、ワークフロー中のコードを実行するプルリクエストが作成されると、セルフホストランナー上で危険なコードが実行される可能性があります。
今回はセルフホストランナーを構築する環境として、k8sクラスタとして利用しているラズパイ上に直接設定を行うパターンと、k8s上に専用のPodを立てて利用するパターンの2パターンを試してみます
ラズパイ上に直接設定を行う
まずワークフローを設定するためのプライベートリポジトリを作成し、「Settings」→「Actions」を開きます
「Add Runner」を押し、OSとArchitectureを選びます(ラズパイの場合はOS:Linux、Architecture:ARMを選択)
セフルホストランナーとして利用するラズパイにログインして表示されているコマンドを実行します
# Download $ mkdir actions-runner && cd actions-runner $ curl -O -L https://github.com/actions/runner/releases/download/v2.267.1/actions-runner-linux-arm-2.267.1.tar.gz $ tar xzf ./actions-runner-linux-arm-2.267.1.tar.gz # Configure $ ./config.sh --url https://github.com/sminamot/<作ったprivateリポジトリ> --token XXXX $ ./run.sh
config.sh
の実行時にrunnerの名前、追加のlabel、workフォルダを聞かれますが、ひとまずすべてデフォルトのままEnter
run.sh
を実行し、Listening for Jobs
と表示され、GitHubのActions画面でセルフホストランナーが追加されていればOKです
あとはいつものようにGitHub Actionsのワークフロー設定を追加すればOKですが、yaml中のruns-on
にはself-hosted
を指定するようにしてください
以下は作成したプライベートリポジトリへpushした際に、ラズパイk8sクラスタ上にbusyboxイメージでsleepするだけのpodを作るワークフロー例です
name: Deploy on: push: jobs: deploy: runs-on: self-hosted steps: - name: deploy run: kubectl run busybox --image busybox --restart Never -- /bin/sh -c "sleep 3600"
デプロイするための事前準備として、対象のラズパイ上でkubectlが実行できるようにしておく必要があります
上記yamlを.github/workflows/k8s.yml
として保存しgit pushを行うとデプロイが走りbusyboxのPodがデプロイされました
GitHub上のAction画面
Pod確認
run.sh
と同じディレクトリにあるsvc.sh
を使うことでランナーをサービス化することができます
# 事前にrun.shを動かしている場合は終了しておく # インストール $ ./svc.sh install # 起動 $ ./svc.sh start # ステータス確認 $ ./svc.sh status # 停止 $ ./svc.sh stop # アンインストール $ ./svc.sh uninstall
実際のデプロイフローとしては以下のようになります
- ラズパイ上にデプロイに必要なツールを事前にインストール(kustomizeやhelmなど)
- 作成したプライベートリポジトリ上にデプロイしたいk8sのリソース用yamlを配置し、↑のツールを利用してgit push時にデプロイを行う
k8s上に専用のPodを立てる
上記のランナーをk8sのPod上に立てる方法です
セルフホストランナー用ベースイメージ
Podとして動かすために以下のようなDockerfileとentrypoint.sh/remove.shを作成します
Dockerfileはセルフホストランナーのためのベース用イメージであり、実際に使うイメージではこちらをベースにデプロイに必要なツール等を含めたイメージにします(後述)
また、amd64/armともにbuildできるよう環境別にARGを設定し、FROM base-$TARGETARCH
の記述で各Archに対応できるようにしています
「はじめに」で紹介した複数アーキテクチャ向けのイメージ作成の方法ではbuildxを使っているため、$TARGETARCHにそれぞれのArchの値が入ることで処理の分岐が行われます
(再掲) sminamot-dev.hatenablog.com
entrypoint.shとremove.shを分けているのは、ジョブ実行後以外にPod終了時にもRunnerの解除を行いたいためです
entrypoint.shではrun.shの実行時に--once
オプションをつけてジョブを1つ実行後に終了するようにしています
これによりデプロイ時に生成したファイル等がPod内に残らないようにしています
k8s用イメージ
上記Dockerfileをベースにして実際にk8sクラスタ上に配置するイメージを作成します
自分はデプロイにkubectl、kustomize、sopsを利用しているため、これらのバイナリを追加したイメージにしています
kustomizeとsopsはarm向けのバイナリが公式に提供されていなかったため、docker build時にソースからビルドする手順としています
イメージが用意できたら実際にPod(Deployment)をラズパイk8sクラスタ上にデプロイします
Pod上でkubectlを実行するための、ServiceAccountとClusterRoleBindingも一緒に作成します
deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: github-actions-runner-k8s labels: app: github-actions-runner-k8s spec: replicas: 2 selector: matchLabels: app: github-actions-runner-k8s template: metadata: labels: app: github-actions-runner-k8s spec: serviceAccount: admin containers: - name: github-runner image: sminamot/github-actions-runner-k8s:1.0.0 imagePullPolicy: IfNotPresent lifecycle: preStop: exec: command: ["/bin/sh", "-c", "./remove.sh"] envFrom: - configMapRef: name: github-actions-configmap - secretRef: name: github-actions-secret
↑で書いたとおりpreStopにランナーを外す処理を入れています
serviceaccount.yaml
apiVersion: v1 kind: ServiceAccount metadata: name: admin
clusterrolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: admin-clusterrolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - kind: ServiceAccount name: admin namespace: default
deployment.yaml
中のconfigMapとsecretには以下の環境変数を設定するための値を指定しておきます
key | value | configMap/secret |
---|---|---|
GITHUB_OWNER | 対象リポジトリのOwner(ORG) | configMap |
GITHUB_REPOSITORY | 対象のリポジトリ名 | configMap |
GITHUB_TOKEN | GitHubアクセストークン(repoの権限を付与したもの) | secret |
以上のリソースをすべて作成し、ログを確認すると(sternで確認)、ラズパイ上で直接動かしたときと同様に Listening for Jobs
と出力されランナーが起動しています
対象リポジトリのActionsの設定を見ると、自動的にランナーが追加されていることが分かります
先程のentrypoint.sh
では終了時のremove処理も書かれているので、Podが終了した際に自動的にランナーから削除が行われます
あとは同様に対象のプライベートリポジトリ上にデプロイ用のワークフロー設定を追加すればOKです
終わりに
GitHub Actionsを利用したGitOpsでの自動デプロイ環境の構築方法について紹介しました
2つやり方を紹介しましたが、自分はPodを立てる方のやり方で利用しています
GitHub Actionsでは指定したパス以下のファイルが更新されたときだけ動く設定があるため、k8sデプロイ用のプライベートリポジトリにアプリケーションごとにyamlを配置するディレクトリを作成し、それぞれ更新時のみデプロイされるように設定しました
kustomizeを使った例だと↓のような感じです
ファイル構成
. ├── .github │ └── workflows │ ├── my-app1.yaml │ └── my-app2.yaml ├── my-app1 │ ├── deployment.yaml │ └── kustomization.yaml └── my-app2 ├── deployment.yaml └── kustomization.yaml
.github/workflows/my-app1.yaml
name: Deploy my-app1 on: push: paths: - 'my-app1/**' - '.github/workflows/my-app1.yaml' jobs: deploy: runs-on: self-hosted steps: - uses: actions/checkout@v2 - name: Run kubectl apply run: kustomize build my-app1 | kubectl apply -f -
Let's 自動デプロイライフ🚀