EKS上にKonnectのDPをGitHub Actions経由で構築する


KonnectのData Planeの構築はWebUIから簡単に行えるが、それでもログインしたりクラスタ側でhelmコマンドを叩いたり、ある程度の作業が発生する。
今回はこれを全てGitHub Actionsに落とし込んで1クリックで作成しようと思う。

## 前提
以下を前提とする。
- EKSがある
- 以下のAWSに関する環境変数が設定済み
	- `export AWS_REGION=us-east-1`
	- `export AWS_ACCESS_KEY_ID="ASIAV..."`
	- `export AWS_SECRET_ACCESS_KEY="Fh4E..."`
	- `export AWS_SESSION_TOKEN="IQoJ..."`
- Konnectのトークンを発行済

## 準備
### GitHub Actions向け変数の準備

まずはGitHub Actionsで使う変数を用意する。
個人的な好みから、先に環境変数で設定し、それを使ってGitHubのリポジトリに設定するようにする。
ここで設定する環境変数は以下となる。

| 環境変数             | 意味                                                                                                                                                     |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| KONNECT_TOKEN    | Konnectのアクセストークン。CPのIDを取得するためのAPIを発行するのに利用。                                                                                                            |
| EKS_CLUSTER_NAME | EKSのクラスタ名                                                                                                                                              |
| CP_NAME          | DPを展開するCPの名前                                                                                                                                           |
| KONG_REPO        | DPのイメージを格納しているリポジトリ。ここでは[この記事](https://qiita.com/ipppppei/items/581a98d685777266c5e5)で作成したイメージを使いたいので環境変数化している。デフォルトのものを使いたい人は`kong/kong-gateway`を指定する |
| KONG_VER         | DPのバージョン                                                                                                                                               |
| GIT_REPO         | 変数を設定するGitHubのリポジトリ                                                                                                                                    |

ということで以下のように設定する。
```bash
KONNECT_TOKEN=kpat_sHa...
EKS_CLUSTER_NAME=mycluster
CP_NAME=default
KONG_REPO=ghcr.io/imurata/kong-gateway
KONG_VER=3.10.0.1
GIT_REPO=imurata/hoge
```

設定後、[ghコマンド](https://github.com/cli/cli)を使ってSecret、Variablesを設定する。
```bash
gh secret set AWS_ACCESS_KEY_ID --body $AWS_ACCESS_KEY_ID --repo $GIT_REPO
gh secret set AWS_SECRET_ACCESS_KEY --body $AWS_SECRET_ACCESS_KEY --repo $GIT_REPO
gh secret set AWS_SESSION_TOKEN --body $AWS_SESSION_TOKEN --repo $GIT_REPO
gh secret set AWS_REGION --body $AWS_REGION --repo $GIT_REPO
gh secret set KONNECT_TOKEN --body $KONNECT_TOKEN --repo $GIT_REPO
gh secret set EKS_CLUSTER_NAME --body $EKS_CLUSTER_NAME --repo $GIT_REPO
```

``` bash
gh variable set CP_NAME --body $CP_NAME --repo $GIT_REPO
gh variable set KONG_REPO --body $KONG_REPO --repo $GIT_REPO
gh variable set KONG_VER --body $KONG_VER --repo $GIT_REPO
```

また、`KONG_REPO`にアクセスするためのコンテナレジストリ用Secretが必要な場合は作成しておく。
（ここでは事前に作ったが、実際はパイプラインの中で作った方がいいかも）
```sh
kubectl create secret docker-registry ghcr-secret --docker-server=ghcr.io --docker-username=imurata --docker-password=$GITHUB_TOKEN -n kong
```


### パイプラインの作成
以下のような感じでパイプラインを作成してGitにPushする。

```yaml
cat <<'EOF' > ./.github/workflows/deploy_dp.yaml
name: Deploy Kong Data Plane

on:
  workflow_dispatch:

jobs:
  deploy-dp:
    runs-on: ubuntu-latest
    env:
      KONNECT_API: https://us.api.konghq.com/v2

    steps:
      - name: Set up dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y jq openssl

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Set up kubeconfig for EKS
        run: |
          aws eks update-kubeconfig --name ${{ secrets.EKS_CLUSTER_NAME }}

      - name: Get Control Plane ID and upload TLS certs
        id: get_cp
        run: |
          CP_ID=$(curl -s -X GET \
            -H "Authorization: Bearer ${{ secrets.KONNECT_TOKEN }}" \
            "${KONNECT_API}/control-planes" \
            | jq -r --arg name "${{ vars.CP_NAME }}" '.data[] | select(.name==$name) | .id')
          mkdir certs
          openssl req -new -x509 -nodes -newkey rsa:2048 \
            -subj "/CN=kongdp/C=US" \
            -keyout ./certs/tls.key -out ./certs/tls.crt -days 3650
          export CERT=$(awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' ./certs/tls.crt)
          curl -sX POST \
            -H "Authorization: Bearer ${{ secrets.KONNECT_TOKEN }}" \
            ${KONNECT_API}/control-planes/${CP_ID}/dp-client-certificates \
            --json '{"cert":"'"$CERT"'"}'
          CP_ENDPOINT=$(curl -s -X GET \
            -H "Authorization: Bearer ${{ secrets.KONNECT_TOKEN }}" \
            "${KONNECT_API}/control-planes/${CP_ID}" \
            | jq -r '.config.control_plane_endpoint')
          CP_ENDPOINT=$(sed "s@https://@@" <<< $CP_ENDPOINT)
          TP_ENDPOINT=$(curl -s -X GET \
            -H "Authorization: Bearer ${{ secrets.KONNECT_TOKEN }}" \
            "${KONNECT_API}/control-planes/${CP_ID}" \
            | jq -r '.config.telemetry_endpoint')
          TP_ENDPOINT=$(sed "s@https://@@" <<< $TP_ENDPOINT)
          echo "tp_endpoint=$TP_ENDPOINT" >> "$GITHUB_OUTPUT"
          echo "cp_endpoint=$CP_ENDPOINT" >> "$GITHUB_OUTPUT"

      - name: Create Kubernetes namespace and TLS secret
        run: |
          kubectl create ns kong || true
          kubectl create secret tls kong-cluster-cert -n kong \
            --cert=./certs/tls.crt \
            --key=./certs/tls.key \
            --dry-run=client -o yaml | kubectl apply -f -

      - name: Generate values.yaml
        run: |
          cat <<EOF > ./values.yaml
          image:
            repository: ${{ vars.KONG_REPO }}
            tag: ${{ vars.KONG_VER }}
            pullSecrets:
              - ghcr-secret

          secretVolumes:
            - kong-cluster-cert

          admin:
            enabled: false

          env:
            role: data_plane
            database: "off"
            cluster_mtls: pki
            cluster_control_plane: ${{ steps.get_cp.outputs.cp_endpoint }}:443
            cluster_server_name: ${{ steps.get_cp.outputs.cp_endpoint }}
            cluster_telemetry_endpoint: ${{ steps.get_cp.outputs.tp_endpoint }}:443
            cluster_telemetry_server_name: ${{ steps.get_cp.outputs.tp_endpoint }}
            cluster_cert: /etc/secrets/kong-cluster-cert/tls.crt
            cluster_cert_key: /etc/secrets/kong-cluster-cert/tls.key
            lua_ssl_trusted_certificate: system
            konnect_mode: "on"
            vitals: "off"
            nginx_worker_processes: "1"
            upstream_keepalive_max_requests: "100000"
            nginx_http_keepalive_requests: "100000"
            proxy_access_log: "off"
            dns_stale_ttl: "3600"
            router_flavor: expressions

          ingressController:
            enabled: false
            installCRDs: false

          resources:
            requests:
              cpu: 1
              memory: "2Gi"
          EOF

      - name: Deploy with Helm
        run: |
          helm repo add kong https://charts.konghq.com
          helm repo update
          helm upgrade -i my-kong kong/kong -n kong --values ./values.yaml --debug --wait

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: kong-dp-config
          path: |
            ./values.yaml
            ./certs/tls.crt
            ./certs/tls.key
EOF
git add -A 
git commit -m "Initial Commit" 
git push origin main
```
上記のコードについて、ポイントを絞って説明する。

```yaml
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Set up kubeconfig for EKS
        run: |
          aws eks update-kubeconfig --name ${{ secrets.EKS_CLUSTER_NAME }}
```
上記についてはEKSを叩くためのkubeconfigを取得するために実行している。
EKSではないクラスタであれば、以下のような感じで取り込むといいと思う。
```sh
gh secret set KUBECONFIG_DATA --body $(base64 -w 0 ~/.kube/config) --repo $GIT_REPO

```

```yaml
      - name: Set up kubeconfig
        run: |
          echo "${{ secrets.KUBECONFIG_DATA }}" | base64 -d > $HOME/.kube/config
```

次に以下のstepではCPのIDを取得しTLSの証明書のアップロードやvalues.yamlに設定するためのエンドポイントを取得する。
```yaml
- name: Get Control Plane ID and upload TLS certs
        id: get_cp
        :（省略）
```
過去に書いた「[Konnectに繋ぐData PlaneをCLIのみで起動する](https://qiita.com/ipppppei/items/97f5c40d4ad6b3f37ae5)」という記事で実行しているコマンドの詳細を説明しているので、ここでは説明は割愛する。

```yaml
      - name: Create Kubernetes namespace and TLS secret
        run: |
          kubectl create ns kong || true
          kubectl create secret tls kong-cluster-cert -n kong \
            --cert=./certs/tls.crt \
            --key=./certs/tls.key \
            --dry-run=client -o yaml | kubectl apply -f -
```
ここではNamespaceを作成し、証明書をSecretに格納する箇所となる。
UIではSecretは`kubectl create`で作成するが、ここでは既にSecretがある場合を考慮し、`--dry-run`でYAML形式で吐いた後に`kubectl apply`で上書きするようにしている。
今回は省略したが、`KONG_REPO`に対するアクセス用の`pullSecrets`のためのSecretを作成するならここで作っておくとよい。

```yaml
      - name: Generate values.yaml
        run: |
          cat <<EOF > ./values.yaml
          image:
            repository: ${{ vars.KONG_REPO }}
            tag: ${{ vars.KONG_VER }}
            pullSecrets:
              - ghcr-secret
          :（省略）
```
上記以降でHelmのvalues.yamlを作成してDPをデプロイする。
KonnectのUIで提示されるvalues.yamlでは`pullSecrets`の項目はないが、今回は自前のリポジトリ(`KONG_REPO`)にイメージがあることを想定し、そこからPullするための認証情報を引っ張るために設定する。
`cluster_control_plane`、`cluster_server_name`、`cluster_telemetry_endpoint`、`cluster_telemetry_server_name`に関してはControl Planeがないと値が設定できない部分であり事前に値を設定することが難しい。
そのため、CPのIDを取得した際にそれぞれのエンドポイントの値を覚えておいて、values.yaml作成時に反映させている。

ちなみに今回のパイプラインはHelmのインストールで終了させているが、何だったらPodの起動確認やdeckを使って簡易的な疎通確認まで実装してもよいと思う。
今回は検証目的であり省略した。

## 動作確認
パイプラインを`workflow_dispatch`で作成しているので、GitHub Actionsから手動で実行してDPをデプロイする。
以下のような感じで`Actions`から`Run workflow`をクリックして実行する。
![Pasted image 20250508152547.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/e2ec8fea-6863-42a6-b80c-8d711fbb3810.png)

ジョブの実行が完了するとクラスタ上でPodが立っているのが分かる。
```bash
$ kubectl get pod -n kong
NAME                           READY   STATUS    RESTARTS   AGE
my-kong-kong-6898888cb-8cw5l   1/1     Running   0          3m12s
```

KonnectのUIからも確認できる。
![Pasted image 20250508163436.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/364155e6-038b-46f3-b370-525650986285.png)

以上より、1クリックでKonnectのDPを払い出せる環境が作成できた。
なお、注意点として`helm uninstall`で削除してもKonnect側に作成した証明書は残り続けるので、`helm uninstall`後は手動で削除する、もしくは削除用にまたパイプラインを用意した方がいい。