DNSエンドポイントを使用してGitHub ActionsからGKEクラスタにリソースをデプロイする

記事タイトルとURLをコピーする

G-gen の佐々木です。当記事では、GitHub Actions で GKE クラスタにリソースをデプロイする際に、DNS エンドポイントを使用する方法を解説します。

DNS エンドポイントとは

DNS エンドポイントとは、GKE のコントロールプレーンにアクセスする方法として従来からサポートされていた IP アドレスベースのアクセス元制御ではなく、DNS ベースのアクセス元制御を提供する機能です。

DNS エンドポイントが有効化されている場合、たとえコントロールプレーンにパブリック IP アドレスが割り当てられていなくても、IAM で許可されているユーザーはコントロールプレーンに接続することができます。

DNSエンドポイントを使用してコントロールプレーンに接続する

DNS エンドポイントの詳細、および IP アドレスベースのエンドポイントとの比較については、以下の記事をご一読ください。

blog.g-gen.co.jp

GitHub Actions を使用した GKE へのデプロイ

従来の方法

DNS エンドポイントがリリースされる前は、コントロールプレーンのパブリックエンドポイントを無効化している場合、GitHub Actions からコントロールプレーンに直接接続することはできませんでした。

そのため、プライベートエンドポイント経由でコントロールプレーンに到達できる GKE ノード側の VPC 内に GitHub Actions の Self-Hosted Runner を構成し、Runner 上でワークフローを実行する方法などが用いられます。

Self-Hosted Runner は Compute Engine 仮想マシンや GKE クラスタ内の Pod で実行することになるため、GitHub Actions でデプロイパイプラインを構築したい場合、これらのリソースの運用管理も考慮していく必要があります。

Self-Hosted Runnerを使用してプライベートエンドポイント経由でデプロイする

DNS エンドポイントを使用する方法(当記事で解説)

DNS エンドポイントを使用する場合、GitHub Actions ワークフローから Google Cloud API にアクセスすることができれば、パブリック IP アドレスを持たないコントロールプレーンであっても直接接続することができます。

したがって、Self-Hosted Runner の運用を考えることなく、シンプルなパイプライン構成で GKE クラスタにリソースをデプロイできます。

DNSエンドポイント経由でデプロイする

GitHub Actions からコントロールプレーンに接続する際の IAM 認証については Workload Identity を使用します。

Workload Identity では Google Cloud プロジェクトに GitHub リポジトリを プロバイダ として登録し、プロバイダに対して IAM 権限を紐付けることで Google Cloud リソースへのアクセス権を与えることができます。

Workload Identity 連携の詳細については、以下のドキュメントをご一読ください。

当記事では Workload Identity の方式のひとつである Direct Workload Identity を使用することで、IAM サービスアカウントの情報を GitHub Actions ワークフロー側に渡すことなく、プロバイダに対してコントロールプレーンに接続するための IAM 権限を直接付与します。

DNS エンドポイントを使用する GKE クラスタの作成

シェル変数の設定

まずは GitHub Actions のデプロイ先となる GKE クラスタを作成します。

当記事では gcloud CLI を使用してリソースを作成していきます。リソース作成時に何度か使用する値をシェル変数として設定しておきます。

PROJECT_ID={プロジェクトID}  # ex: gha-demo-prj
LOCATION={GKEを作成するロケーション}  # ex: asia-northeast1
CLUSTER_NAME={任意のGKEクラスタの名前}  # ex: mycluster

ネットワークリソースの作成

GKE クラスタを配置する VPC とサブネットを作成していきます。

以下のコマンドで VPC を作成します。

# VPC を作成する
$ gcloud compute networks create vpc-${CLUSTER_NAME} \
    --project=${PROJECT_ID} \
    --subnet-mode=custom

作成した VPC の中にサブネットを作成します。当記事ではサブネットの IP 範囲として 192.168.11.0/24 を設定しています。

# サブネットを作成する
$ gcloud compute networks subnets create subnet-${CLUSTER_NAME} \
    --project=${PROJECT_ID} \
    --network=vpc-${CLUSTER_NAME} \
    --region=${LOCATION} \
    --range=192.168.11.0/24

GKE クラスタの作成

--enable-dns-access フラグを使用し、DNS エンドポイントを有効化した GKE クラスタを作成します。

以下のコマンドで、DNS エンドポイントを有効化した Autopilot モードの GKE クラスタを作成します。--no-enable-ip-access フラグを使用することで IP アドレスベースの接続を無効化します。これで、Google Cloud API にアクセスでき、かつ IAM 権限を持つユーザーのみがコントロールプレーンに接続することができます。

# DNS エンドポイントを有効化した Autopilot クラスタを作成する(10分ほどかかるので注意)
$ gcloud container clusters create-auto ${CLUSTER_NAME} \
    --project=${PROJECT_ID} \
    --network=vpc-${CLUSTER_NAME} \
    --subnetwork=subnet-${CLUSTER_NAME} \
    --enable-private-nodes \
    --enable-dns-access \
    --no-enable-ip-access

IPアドレスベースの接続を無効化し、DNSエンドポイントのみ有効にする

Direct Workload Identity の構成

シェル変数の設定

ここからは、GitHub Actions からコントロールプレーンに接続する際に認証を行うための Workload Identity の設定を行っていきます。

まずは、繰り返し使用する値をシェル変数として設定しておきます。

PROJECT_ID={プロジェクトID}  # ex: gha-demo-prj   
PROJECT_NUMBER={プロジェクト番号}  # ex: 1234567890
WORKLOAD_IDENTITY_POOL={任意のWorkload Identityプール名}  # ex: my-gha-pool
WORKLOAD_IDENTITY_PROVIDER={任意のWorkload Identityプロバイダ名}  # ex: my-gha-provider
GITHUB_REPO={GitHubリポジトリ名}  # ex: my-org/my-repo

Workload Identity の設定

以下のコマンドで Workload Identity プールを作成します。

# Workload Identity プールの作成
$ gcloud iam workload-identity-pools create ${WORKLOAD_IDENTITY_POOL} \
    --project=${PROJECT_ID} \
    --location=global \
    --display-name=${WORKLOAD_IDENTITY_POOL}

作成したプールに、Workload Identity プロバイダを作成します。

--attribute-condition フラグで、この Workload Identity を使用する GitHub Actions ワークフローがある GitHub リポジトリをここで指定します。

# Workload Identity プロバイダの作成
$ gcloud iam workload-identity-pools providers create-oidc ${WORKLOAD_IDENTITY_PROVIDER} \
    --project=${PROJECT_ID} \
    --location=global \
    --workload-identity-pool=${WORKLOAD_IDENTITY_POOL} \
    --display-name=${WORKLOAD_IDENTITY_PROVIDER} \
    --issuer-uri="https://token.actions.githubusercontent.com" \
    --attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository" \
    --attribute-condition="assertion.repository=='${GITHUB_REPO}'"

Workload Identityプールとプロバイダを作成する

最後に、Workload Identity プロバイダに対して GKE クラスタにリソースをデプロイするための権限(roles/container.developer)を付与します。

これにより、GitHub Actions ワークフローが DNS エンドポイント(Google Cloud API の認証)を通して GKE のコントロールプレーンに接続することができます。

# Workload Identity プロバイダに権限を付与する
$ gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member="principalSet://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${WORKLOAD_IDENTITY_POOL}/attribute.repository/${GITHUB_REPO}" \
    --role="roles/container.developer"

Workload IdentityプロバイダにGKEクラスタのアクセス権を付与する

GitHub Actions によるリソースのデプロイ

GitHub リポジトリのディレクトリ構成

Workload Identity プロバイダの作成時に指定した GitHub リポジトリに、GitHub Actions のワークフローと、GKE クラスタにデプロイする Kubernetes リソースのマニフェストファイルを配置します。

リポジトリ内のディレクトリ構成は以下のようにして進めていきます。

.
├── .github
│   └── workflows
│       └── demo-actions.yaml  # GitHub Actions ワークフロー
└── manifests
    └── sample-deployment.yaml  # Kubernetes リソース(deployment)のマニフェストファイル

使用するファイル

GitHub Actions ワークフロー

Workload Identity を使用して GKE クラスタにリソースをデプロイする GitHub Actions ワークフローを作成します。

ワークフローファイル内の以下の箇所を、実際の値に置き換えてください。

置き換える箇所
14行目 {プロジェクトID} GKE クラスタを作成したプロジェクトの ID
15行目 {プロジェクト番号} GKE クラスタを作成したプロジェクトのプロジェクト番号
16行目 {GKEクラスタ名} GKE クラスタの名前
17行目 {GKEクラスタのロケーション} GKE クラスタを作成したロケーション
18行目 {Workload Identityプール名} Workload Idenity プールの名前
19行目 {Workload Identityプロバイダ名} Workload Identity プロバイダの名前
# demo-actions.yaml
name: apply k8s manifest to GKE
  
# main ブランチへの Pull Request / Push で実行
on:
  pull_request:
    branches:
      - main
  push:
    branches:
      - main
  
# 環境変数に GKE クラスタの情報を設定
env:
  PROJECT_ID: {プロジェクトID}  # Google Cloud プロジェクト ID
  PROJECT_NUM: {プロジェクト番号}  # Google Cloud プロジェクト番号
  GKE_CLUSTER: {GKEクラスタ名}  # GKE クラスタ名
  GKE_LOCATION: {GKEクラスタのロケーション}  # GKE クラスタのロケーション
  WID_POOL: {Workload Identityプール名}  # Workload Identity プール名
  WID_PROVIDER: {Workload Identityプロバイダ名}  # Workload Identity プロバイダ名
  
# ジョブ (GitHUb runners で実行)
jobs:
  setup-apply:
    runs-on: ubuntu-latest

    permissions:
      id-token: write
      contents: read
      pull-requests: write

    steps:
    - id: checkout
      name: Checkout
      uses: actions/checkout@v4

    # Workload Identity 連携
    # https://cloud.google.com/iam/docs/using-workload-identity-federation#generate-automatic
    - id: auth
      name: Authenticate to Google Cloud
      uses: google-github-actions/auth@v2
      with: 
        workload_identity_provider: 'projects/${{ env.PROJECT_NUM }}/locations/global/workloadIdentityPools/${{ env.WID_POOL }}/providers/${{ env.WID_PROVIDER }}'  # Workload Identity Pool のプロバイダー

    # https://github.com/google-github-actions/setup-gcloud
    - id: setup-gcloud
      name: Setup gcloud
      uses: google-github-actions/setup-gcloud@v2
      with:
        project_id: ${{ env.PROJECT_ID }}

    # GKE の認証プラグイン
    # https://cloud.google.com/kubernetes-engine/docs/deprecations/auth-plugin?hl=ja
    - id: install-gke-gcloud-auth-plugin
      name: Install gke-gcloud-auth-plugin
      run: |
        gcloud components install gke-gcloud-auth-plugin

    # GKE クラスタの認証情報を取得 (DNS エンドポイントを使用)
    - id: get-gke-credentials
      name: Get GKE credentials
      run: |
        gcloud container clusters get-credentials ${{ env.GKE_CLUSTER }} --region ${{ env.GKE_LOCATION }} --dns-endpoint

    # マニフェストファイルの適用
    - id: apply
      name: Apply
      run: |
        kubectl apply -f ./manifests

Kubernetes マニフェストファイル

GKE クラスタにデプロイするリソースのマニフェストファイルを作成します。

当記事では、nginx コンテナを実行する Pod を3つ作成する Deployment リソースを作成します。

# sample-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
      - name: sample-app
        image: nginx:1.27

GitHub Actions の実行

作成したワークフローファイルとマニフェストファイルをリモートリポジトリに push し、GitHub Actions ワークフローを実行します。

ワークフローが実行され、GitHub Actions から GKE クラスタに対してマニフェストファイルが適用されます。Deployment リソースが作成されていることが GitHub Actions のログからわかります。

GitHub Actionsワークフローから GKE クラスタへのデプロイが成功している

ローカルから GKE クラスタのコントロールプレーンに接続し、デプロイしたリソースの状態を確認します。

# DNS エンドポイントでコントロールプレーンに接続する
$ gcloud container clusters get-credentials ${CLUSTER_NAME} --dns-endpoint
  
# Deployment リソースを表示
$ kubectl get deployments

マニフェストファイルの記述通り、3つの Pod を実行する Deployment リソースが作成されています。

# Deployment リソースを表示(出力例)
$ kubectl get deployments
NAME                READY   UP-TO-DATE   AVAILABLE   AGE
sample-deployment   3/3     3            3           2m7s

修正したマニフェストファイルの反映

マニフェストファイルの修正

マニフェストファイルを修正してからリモートリポジトリに push し、GitHub Actions ワークフローによるデプロイを再度行います。

マニフェストファイル内の spec.replicas の値を 1 にして、Pod 数をスケールインするように修正します。

# sample-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  replicas: 1  # ここを修正する
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
      - name: sample-app
        image: nginx:1.27

GitHub Actions の再実行

再度、リモートリポジトリへの push を行い、GitHub Actions ワークフローを実行します。

ワークフローの実行後、Deployment の状態を確認します。push したマニフェストファイルが GitHub Actions から適用され、Pod の数が1になりました。

# Deployment リソースを確認(出力例)
$ kubectl get deployments
NAME                READY   UP-TO-DATE   AVAILABLE   AGE
sample-deployment   1/1     1            1           7m5s

佐々木 駿太 (記事一覧)

G-gen最北端、北海道在住のクラウドソリューション部エンジニア

2022年6月にG-genにジョイン。Google Cloud Partner Top Engineer 2025 Fellowに選出。好きなGoogle CloudプロダクトはCloud Run。

趣味はコーヒー、小説(SF、ミステリ)、カラオケなど。