GitHub ActionsでおうちKubernetesの簡易GitOpsを実現する (Azure Arc 対応 Kubernetes利用)

# はじめに
本記事は [Qiita エンジニアフェスタ 2022 GitHub Actions の自分流の使い方をシェアしよう](https://qiita.com/official-events/14b94a693d6153857db4) の参加記事です。

https://qiita.com/official-campaigns/engineer-festa/2022

学習用に自宅でKubernetesクラスターを動かしているので、これをGitHub Actionsと組み合わせようと思い立ちました。
どうやってGitHub Actionsから自宅クラスターにアクセスさせようか悩んでいたところ、[Azure Arc 対応 Kubernetes](https://docs.microsoft.com/ja-jp/azure/azure-arc/kubernetes/) の [任意の場所からクラスターにアクセスする](https://docs.microsoft.com/ja-jp/azure/azure-arc/kubernetes/conceptual-cluster-connect) 機能が使えそうだとわかりました。
本記事ではKubernetesクラスターをAzure Arcに接続し、GitHub Actionsで操作するところまでの手順を解説します。

# 環境
以下の図の構成を作ります。
![20220619_qiita_githubactions1.drawio (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/78d4dcf8-e8a5-c43a-627e-1f9ed39484f9.png)

Kubernetesクラスターの環境は以下のとおりです。
- ノードOS: Ubuntu Server 22.04
- Kubernetesバージョン: 1.24.2

Azure Arc 対応 Kubernetes を使うには、クラスターに1台以上amd64のノードが必要となります。
おうちKubernetesでよくあるRaspberry Piだけで構築したクラスターには残念ながら導入できません。amd64のノードを追加しましょう。

# Azure Arc 対応 Kubernetesの設定
[公式ドキュメントに記載の手順](https://docs.microsoft.com/ja-jp/azure/azure-arc/kubernetes/quickstart-connect-cluster?tabs=azure-cli)に従い作業を実施します。

## 準備作業
Azure CLIに `connectedk8s` 拡張機能を導入します。

```bash
az extension add --name connectedk8s
```

インストール済みの拡張機能を最新化するときは以下コマンドを実行します。

```bash
az extension update --name connectedk8s
```

Azure Arc 対応 Kubernetes用のプロバイダーをインストールします。

```bash
az provider register --namespace Microsoft.Kubernetes
```
```bash
az provider register --namespace Microsoft.KubernetesConfiguration
```
```bash
az provider register --namespace Microsoft.ExtendedLocation
```
3つとも `Registered` になればOKです。
```bash
$ az provider show -n Microsoft.Kubernetes -o table
Namespace             RegistrationPolicy    RegistrationState
--------------------  --------------------  -------------------
Microsoft.Kubernetes  RegistrationRequired  Registered
```
```bash
$ az provider show -n Microsoft.KubernetesConfiguration -o table
Namespace                          RegistrationPolicy    RegistrationState
---------------------------------  --------------------  -------------------
Microsoft.KubernetesConfiguration  RegistrationRequired  Registered
```
```bash
$ az provider show -n Microsoft.ExtendedLocation -o table
Namespace                   RegistrationPolicy    RegistrationState
--------------------------  --------------------  -------------------
Microsoft.ExtendedLocation  RegistrationRequired  Registered
```

リソースグループを作成します。ここでは `AzureArc` という名前で、東日本リージョンに作成しています。

```bash
az group create --name AzureArc --location japaneast
```
以上で事前の準備作業は完了です。

## KubernetesクラスターをAzure Arcへ接続する
以下のコマンドでKubernetesクラスターをAzure Arcへ接続します。  
`--name` でAzure上での名前を設定します。ここでは `HomeCluster` としました。

```bash
az connectedk8s connect --name HomeCluster --resource-group AzureArc
```

コマンドの実行が終わると、Azure上にAzure Arc 対応 Kubernetesのリソースが作成されていることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/1bddba67-dc8d-e8fd-dc5a-d01339fe2342.png)

また、Kubernetes上では `azure-arc` Namespace配下でPodが起動していることが確認できます。
```bash
$ kubectl get pod -n azure-arc
NAME                                         READY   STATUS    RESTARTS   AGE
cluster-metadata-operator-855c68dcf8-l62w2   2/2     Running   0          6m55s
clusterconnect-agent-77496f7c5c-txjnx        3/3     Running   0          6m55s
clusteridentityoperator-6d74bff55b-89lf8     2/2     Running   0          6m55s
config-agent-65b55bccd9-ph4vr                2/2     Running   0          6m55s
controller-manager-7fcbd6585d-ss5hx          2/2     Running   0          6m55s
extension-manager-767db884cd-bs54v           2/2     Running   0          6m55s
flux-logs-agent-68bc745586-qjd8q             1/1     Running   0          6m55s
kube-aad-proxy-fdf494d66-htn9v               2/2     Running   0          6m55s
metrics-agent-7d6844cd4c-qblcj               2/2     Running   0          6m55s
resource-sync-agent-5d755f9d6f-vgml7         2/2     Running   0          6m55s
```

## クラスター接続の設定
Azure Arc 対応 Kubernetesの `クラスター接続` 機能を使うことで、ネットワーク的にKubernetesクラスターと疎通性がない場所からAzureを介して操作をすることができます。  
[公式ドキュメントで手順が案内されているので](https://docs.microsoft.com/en-us/azure/azure-arc/kubernetes/cluster-connect?tabs=azure-cli)、順に作業を行います。
（2022/07/02時点では日本語版ドキュメントの手順では一部うまく動かないため、英語版ドキュメントを参照しています）

以下のコマンドでクラスター接続機能を有効にします。
```
az connectedk8s enable-features --features cluster-connect -n HomeCluster -g AzureArc
```

次に[接続に使うサービスアカウントを作成](https://docs.microsoft.com/en-us/azure/azure-arc/kubernetes/cluster-connect?tabs=azure-cli#service-account-token-authentication-option)します。  
ここでは `arc-admin` という名前のサービスアカウントを `azure-arc` Namespaceに作成します。

```bash
kubectl create serviceaccount arc-admin -n azure-arc
```
続いて、ClusterRoleBindingを作成し、サービスアカウントに対しアクセス許可を割り当てます。
```bash
kubectl create clusterrolebinding arc-admin-binding --clusterrole cluster-admin --serviceaccount azure-arc:arc-admin
```
最後にサービスアカウントのトークンを取得します。
以下のコマンドでSecretを生成し、
```bash
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: arc-admin-secret
  namespace: azure-arc
  annotations:
    kubernetes.io/service-account.name: arc-admin
type: kubernetes.io/service-account-token
EOF
```
トークンを取得します。
```bash
TOKEN=$(kubectl get secret arc-admin-secret -n azure-arc -o jsonpath='{$.data.token}' | base64 -d | sed $'s/$/\\\n/g')
```
取得したトークンは後ほど使用します。

ここまででAzure Arc 対応 Kubernetesの準備は完了です。

# GitHub Actionsの準備
ここからはAzure Arc 対応 Kubernetesに対するデプロイを行うGitHub Actionsの準備を行います。

## GitHubリポジトリの作成
まずはGitHubのリポジトリを作成しましょう。[GitHub CLI](https://cli.github.com/)を利用します。
```bash
gh repo create homecluster-manage --private
```
## OpenID Connect
GitHub ActionsからAzureリソースを操作するために [OpenID Connect](https://docs.microsoft.com/ja-jp/azure/developer/github/connect-from-azure?tabs=azure-portal%2Clinux#use-the-azure-login-action-with-openid-connect) を利用します。
手で設定してもいいのですが、Microsoftが公開している [Draft](https://github.com/Azure/draft) というツールを使うと楽に設定できるので今回はこちらを使用します。

Azure CLIに `aks-preview` 拡張機能を導入します。
```bash
az extension add --name aks-preview
```
過去にインストール済みの場合は以下コマンドで最新化します。
```bash
az extension update --name aks-preview
```

拡張機能を導入したら、以下コマンドで Draft を起動します。
```bash
az aks draft setup-gh
```
起動後は対話形式のプロンプトが表示されるので、順に入力していきます。
```bash
# Azure AD に登録するアプリの名前を入力
✔ Enter app registration name: homecluster-gitops
# Azure サブスクリプションを選択する
Use the arrow keys to navigate: ↓ ↑ → ←
✔ ########-####-####-####-############
? Please choose the subscription ID you would like to use.:
# Azure リソースグループの名前を入力
✔ Enter resource group name: AzureArc
# GitHub リポジトリの名前を入力
✔ Enter github organization and repo (organization/repoName): ussvgr/homecluster-manage

[Draft] Draft has successfully set up Github OIDC for your project 😃
[Draft] Use 'draft generate-workflow' to generate a Github workflow to build and deploy an application on AKS.
```

完了すると、Azure ADにアプリが追加され、フェデレーション資格情報の設定も完了しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/0e4473a3-3c9e-e564-90de-f3b4eb5e5300.png)
GitHubリポジトリ側も、認証に必要なSecretsが入力された状態になっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/2b017ae8-236b-0707-3eea-ce1f9995d986.png)

## トークンの追加
GitHub ActionsからAzure Arc 対応 Kubernetesを操作するために、[前の項で作成したトークン](https://qiita.com/drafts/423cdc5a6a137c3e4532/edit#%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%83%BC%E6%8E%A5%E7%B6%9A%E3%81%AE%E8%A8%AD%E5%AE%9A)をGitHub ActionsのSecretsに追加します。

ここでもGitHub CLIを利用します。`SA_TOKEN` という名前でSecretを作成します。
```bash
gh secret set SA_TOKEN --app actions --body $TOKEN --repo ussvgr/homecluster-manage
```

## マニフェストとワークフローの準備
Kubernetesクラスターにデプロイするマニフェストと、GitHub Actionsのワークフローを準備します。
以下のような構成でローカルのディレクトリ内にファイルを配置します。

```bash
.
├── .github
│   └── workflows
│       └── deploy-to-arc.yaml
└── manifests
    └── sampleapp.yaml
```

各ファイルの中身は以下のとおりです。
GitHub Actionsのワークフローがこちら。
```deploy-to-arc.yaml
name: deploy to Azure Arc Enabled Kubernetes
"on":
    push:
        branches:
            - main
    workflow_dispatch: null
env:
    DEPLOYMENT_MANIFEST_PATH: ./manifests
jobs:
    deploy:
        permissions:
            actions: read
            contents: read
            id-token: write
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v3
            - name: Azure login
              uses: azure/login@v1
              with:
                client-id: ${{ secrets.AZURE_CLIENT_ID }}
                subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
                tenant-id: ${{ secrets.AZURE_TENANT_ID }}
            - name: Get K8s context
              uses: azure/k8s-set-context@v3
              with:
                method: service-account
                cluster-type: arc
                cluster-name: HomeCluster
                resource-group: AzureArc
                token: ${{ secrets.SA_TOKEN }}
            - name: Deploys application
              uses: Azure/k8s-deploy@v4
              with:
                action: deploy
                manifests: ${{ env.DEPLOYMENT_MANIFEST_PATH }}
```
Kubernetesにデプロイするマニフェストがこちらです。
`sampleapp` という名前でnginxを起動するのみの簡易なものです。
```sampleapp.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: sampleapp
spec:
  type: LoadBalancer
  selector:
    app: sampleapp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sampleapp
  labels:
    app: sampleapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sampleapp
  template:
    metadata:
      labels:
        app: sampleapp
    spec:
      containers:
        - name: sampleapp
          image: nginx:latest
          ports:
            - containerPort: 80
```
　
# GitHub Actionsによるデプロイ
前節までの作業で準備が完了したので、GitHubにPushしてGitHub Actionsを起動してみましょう。

```bash
git init
git add -A
git commit -m "first commit"
git branch -M main
git remote add origin git@github.com:ussvgr/homecluster-manage
git push -u origin main
```

ジョブが正常に完了すると…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/28fa2a3e-901d-d9b5-7961-fe1220878d05.png)

Kubernetesクラスターに `sampleapp` がデプロイされています！
```bash
$ kubectl get pod
NAME                         READY   STATUS    RESTARTS   AGE
sampleapp-596d47dffb-6szm7   1/1     Running   0          30s
$ kubectl get svc
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)        AGE
kubernetes   ClusterIP      10.96.0.1      <none>         443/TCP        53d
sampleapp    LoadBalancer   10.100.6.247   192.168.11.0   80:32533/TCP   37s
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/245a0ac6-fc39-c8b3-485c-9493437ce85e.png)

# おわりに
Azure Arc 対応 Kubernetesで本格的なGitOpsを行う場合、最近GAした [Flux v2](https://docs.microsoft.com/ja-jp/azure/azure-arc/kubernetes/tutorial-use-gitops-flux2) を利用するのが一般的な手順になるでしょう。
とはいえ、Flux v2を利用するとKubernetesクラスターの規模によっては[若干の費用](https://azure.microsoft.com/ja-jp/pricing/details/azure-arc/)が発生します。

なるべくお金を掛けたくない方向けに、簡易的なことであればGitHub Actionsでもできますよ、という紹介でした。
皆様のおうちKubernetes運用の一助になれば幸いです。