😊

Cloud BuildとArtifact RegistryでCI/CD基盤の構築

2023/07/30に公開

目的

この記事では、GCPの教科書II【コンテナ開発編】の3章のCloud Buildをベースに構築していきます。

具体的には、以下の2つの方法を試します。

  • Cloud BuildとGCR、GKEによるCI/CD基盤構築
  • Cloud BuildとArtifact Registry、GKEによるCI/CD基盤構築

https://www.amazon.co.jp/GCPの教科書II-【コンテナ開発編】-KubernetesとGKE、Cloud-Run、サービスメッシュを詳解-クラウドエース株式会社/dp/4865942416

Cloud BuildとGCR、GKEによるCI/CD基盤構築

この章では、まず3.5節の方法でCI/CD基盤構築を実現していきます。

CSRリポジトリの作成

まずは、CSRのリポジトリを作成します。リポジトリ名は、myrepositoryとします。

gcloud source repos create myrepository
Created [myrepository].

コンテナの準備

続いて、アプリケーションのファイルとDockerfileを作成します。
これはGCP公式ドキュメントのコードとなります。
HTTPリクエストを行うと、「Hello world!」が返ってきます。

以下はmain.goです。

package main

import(	
	"fmt"
	"log"
	"net/http"
	"os"
)

func main() {
	port := "8080"
	if fromEnv := os.Getenv("PORT"); fromEnv != "" {
		port = fromEnv
	}
	server := http.NewServeMux()
	server.HandleFunc("/", hello)
	log.Printf("Server listinig on port %s", port)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), server))

}

func hello(w http.ResponseWriter, r *http.Request) {
	log.Printf("Serving request: %s", r.URL.Path)
	host, _ := os.Hostname()
	fmt.Fprintln(w, "Hello, World!")
	fmt.Fprintf(w, "Version: 1.0.0\n")
	fmt.Fprintf(w, "Hostname: %s\n", host)
}

Dockerfileは以下です。

FROM golang:1.8-alpine
ADD . /go/src/hello-world
RUN go install hello-world

FROM alpine:latest
COPY --from=0 /go/bin/hello-world .
ENV PORT 8080
CMD ["./hello-world"]

GKEのクラスタとマニフェストファイルの作成

以下をk8s-sample.yamlとして作成します。
NAME, PROJECT_ID, IMAGE_NAME, TAG_NAMEは自身の環境にあった設定へ変更してください。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: <NAME>
spec:
  replicas: 1
  selector:
    matchLabels:
      app: <NAME>
  template:
    metadata:
      labels:
        app: <NAME>
    spec:
      containers:
      - name: <NAME>
        image: <NAME>:latest
        ports:
        - containerPort: 8080
        image: gcr.io/<PROJECT_ID>/<IMAGE_NAME>/<TAG_NAME>:latest 
        resources:
          requests:
            memory: "128Mi"
            cpu: "500m"
---
kind: Service
apiVersion: v1
metadata:
  name: <NAME>
spec:
  type: LoadBalancer
  selector:
    app: <NAME>
  ports:
  - name: http
    port: 8080
    targetPort: 8080

GKEのクラスタを作成します。

gcloud container clusters create <CLUSTER_NAME> --num-nodes=3 --preemptible

少々時間がかかります。

Creating cluster <CLUSTER_NAME> in <zones>... Cluster is being configured...
Cluster is being health-checked (master is healthy)...
done.                                                                                     
Created [https://container.googleapis.com/v1/projects/<PROJECT_ID>/zones/<zones>/clusters/<CLUSTER_NAME>].
To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/<zones>/<CLUSTER_NAME>?project=<PROJECT_ID>
kubeconfig entry generated for <CLUSTER_NAME>.
NAME       LOCATION    MASTER_VERSION   MASTER_IP     MACHINE_TYPE  NODE_VERSION     NUM_NODES  STATUS
<CLUSTER_NAME>  <zones> 1.27.2-gke.1200  xxx.xxx.xxx.xxx  e2-medium     1.27.2-gke.1200  3          RUNNING

Cloud Build

Cloud Buildの構成ファイルを作成します。

substitutions:
  _CLOUDSDK_COMPUTE_ZONE: ゾーンを指定
  _CLOUDSDK_CONTAINER_CLUSTER: クラスタ名を指定
  _PROJECT_ID: プロジェクト名を指定
  _IMAGE_NAME: イメージ名を指定
steps:
- name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'gcr.io/${_PROJECT_ID}/${_IMAGE_NAME}:latest', '.']
  id: docker build
- name: 'gcr.io/cloud-builders/docker'
  args: ['push', 'gcr.io/${_PROJECT_ID}/${_IMAGE_NAME}:latest']
  id: docker push
- name: 'gcr.io/cloud-builders/gcloud'
  args: ['container', 'clusters', 'get-credentials', '${_CLOUDSDK_CONTAINER_CLUSTER}', '--zone','${_CLOUDSDK_COMPUTE_ZONE}','--project', '${_PROJECT_ID}']
  id: gcloud container clusters get-credentials
- name: 'gcr.io/cloud-builders/kubectl'
  args: ['apply', '-f', 'k8s-sample.yaml']
  env:
  - 'CLOUDSDK_COMPUTE_ZONE=${_CLOUDSDK_COMPUTE_ZONE}'
  - 'CLOUDSDK_CONTAINER_CLUSTER=${_CLOUDSDK_CONTAINER_CLUSTER}'
  id: kubectl apply

続いて、Cloud Buildのトリガーを作成します。
Cloud Build>トリガーで作成します。

Cloud Buildを実行する

git add .
git commit -m "cloud build test" 
git push origin master

続いて、Cloud Buildの画面でビルド状況を確認します。

403エラーが出た場合は、Cloud Buildが使用するサービスアカウントに「Kubernetes Engine開発者」の役割を付与してください。
Cloud Build>設定>サービスアカウント権限、でステータスを有効にできます。

アプリケーションへリクエスト

Cloud Buildでのビルドが完了したら、作成したアプリケーションへリクエストを実行します。

 kubectl get service 
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)          AGE
k8s-sample   LoadBalancer   xxx.xxx.xxx.xxx   xxx.xxx.xxx.xxx   8080:32652/TCP   2m36s
kubernetes   ClusterIP      xxx.xxx.xxx.xxx     <none>          443/TCP          47m

上記のk8s-sampleのEXTERNAL-IPのIPアドレスに対して、ポート8080で開きます。

http://EXTERNAL-IPのIPアドレス:8080

すると、「Hello, World!」が表示されます。

ここで、次のためにサービスを削除しておきます。

kubectl delete -f k8s-sample.yaml
deployment.apps "k8s-sample" deleted
service "k8s-sample" deleted

Cloud BuildとArtifact Registry、GKEによるCI/CD基盤構築

続いて、GCRからArtifact Registryに変更していきます。

Container Registryは2023年5月15日から非推奨になっており、Artifact Registryに置き換えていきます。
https://cloud.google.com/container-registry/docs/deprecations/container-registry-deprecation?hl=ja&_ga=2.110856261.-1589395183.1689997200

Artifact Registryのリポジトリを作成

gcloud artifacts repositories create cloud-build-sample --repository-format=docker --location=<REGION> --description="Docker repository"
Create request issued for: [cloud-build-sample]
Waiting for operation [projects/<PROJECT_ID>/locations/<REGION>/operations/610418cb-8ec6-4788-aae4-db99be7f4f93] to complete...done.                                                
Created repository [cloud-build-sample].

作成後は以下のコマンドで一覧が確認できます。

gcloud artifacts repositories list
Listing items under project <PROJECT_ID>, across all locations.

                                                                      ARTIFACT_REGISTRY
REPOSITORY          FORMAT  MODE                 DESCRIPTION        LOCATION  LABELS  ENCRYPTION          CREATE_TIME          UPDATE_TIME          SIZE (MB)
cloud-build-sample  DOCKER  STANDARD_REPOSITORY  Docker repository  <REGION>          Google-managed key  2023-07-30T14:40:03  2023-07-30T14:40:03  0

Artifact Registryへのリクエストの認証

以下の<REGION>には、自身の環境での設定を入れてください。
例えば、us-east1などです。

gcloud auth configure-docker <REGION>-docker.pkg.dev 

cloudbuild.yamlの修正

cloudbuild.yamlを以下のように修正します。

substitutions:
  _CLOUDSDK_COMPUTE_REGION: リージョン
  _CLOUDSDK_COMPUTE_ZONE: ゾーン
  _CLOUDSDK_CONTAINER_CLUSTER: クラスタ名
  _PROJECT_ID: プロジェクト名
  _DIRECTORY: ディレクトリ名
  _IMAGE_NAME: イメージ名
steps:
- name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', '${_CLOUDSDK_COMPUTE_REGION}-docker.pkg.dev/${_PROJECT_ID}/${_DIRECTORY}/${_IMAGE_NAME}:latest', '.']
  id: docker build
- name: 'gcr.io/cloud-builders/docker'
  args: ['push', '${_CLOUDSDK_COMPUTE_REGION}-docker.pkg.dev/${_PROJECT_ID}/${_DIRECTORY}/${_IMAGE_NAME}:latest']
  id: docker push
- name: 'gcr.io/cloud-builders/gcloud'
  args: ['container', 'clusters', 'get-credentials', '${_CLOUDSDK_CONTAINER_CLUSTER}', '--zone','${_CLOUDSDK_COMPUTE_ZONE}','--project', '${_PROJECT_ID}']
  id: gcloud container clusters get-credentials
- name: 'gcr.io/cloud-builders/kubectl'
  args: ['apply', '-f', 'k8s-sample.yaml']
  env:
  - 'CLOUDSDK_COMPUTE_ZONE=${_CLOUDSDK_COMPUTE_ZONE}'
  - 'CLOUDSDK_CONTAINER_CLUSTER=${_CLOUDSDK_CONTAINER_CLUSTER}'
  id: kubectl apply

Artifact Registry にイメージを保存するさまざまな方法の例は以下に記載されています。
https://cloud.google.com/build/docs/building/build-containers?hl=ja#store-images

nameフィールドで参照するビルダーイメージの例は以下に記載されています。
nameフィールドでは、タスクを実行するコンテナイメージを指すように指定する必要があります。
https://cloud.google.com/build/docs/cloud-builders?hl=ja#supported_builder_images_provided_by

私の場合、nameフィールドに当初${_CLOUDSDK_COMPUTE_REGION}-docker.pkg.dev/cloud-builders/dockerのように指定していて、ビルダーイメージをうまく読み込めず、
権限周りのエラーが出ました。
例えば、以下のようなエラーがでていました。

Step #2 - "gcloud container clusters get-credentials": Using default tag: latest
Step #2 - "gcloud container clusters get-credentials": Error response from daemon: Head "https://<zone>-docker.pkg.dev/v2/cloud-builders/gcloud/manifests/latest": denied: Permission "artifactregistry.repositories.downloadArtifacts" denied on resource "projects/cloud-builders/locations/<zone>/repositories/gcloud" (or it may not exist)
ERROR: failed to pull because we ran out of retries.
ERROR
ERROR: build step 2 "<zone>-docker.pkg.dev/cloud-builders/gcloud" failed: error pulling build step 2 "<zone>-docker.pkg.dev/cloud-builders/gcloud": generic::unknown: retry budget exhausted (10 attempts): step exited with non-zero status: 1

Cloud Buildの実行

pushを行うことで、Cloud Buildのトリガーを起動します。

git add .
git commit -m "cloud build test" 
git push origin master

Cloud Buildの画面でビルドが完了したことを確認します。
また、Artifact Registryの画面でイメージが作成されたか確認します。

アプリケーションへリクエスト

前回と同様、Cloud Buildでのビルドが完了したら、作成したアプリケーションへリクエストを実行します。

 kubectl get service 
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)          AGE
k8s-sample   LoadBalancer   xxx.xxx.xxx.xxx   xxx.xxx.xxx.xxx   8080:32652/TCP   2m36s
kubernetes   ClusterIP      xxx.xxx.xxx.xxx     <none>          443/TCP          47m

上記のk8s-sampleのEXTERNAL-IPのIPアドレスに対して、ポート8080で開きます。

http://EXTERNAL-IPのIPアドレス:8080

すると、「Hello, World!」が表示されます。

ここで、次のためにサービスを削除しておきます。

kubectl delete -f k8s-sample.yaml
deployment.apps "k8s-sample" deleted
service "k8s-sample" deleted

これで該当のリポジトリへpushすると、Cloud Buildが起動し、Artifact Registryへイメージがpushされ、GKEへデプロイされるCI/CD基盤が構築できました。

まとめ

この記事では、以下の2つの方法を試しました。

  • Cloud BuildとGCR、GKEによるCI/CD基盤構築
  • Cloud BuildとArtifact Registry、GKEによるCI/CD基盤構築

また、Cloud Buildのドキュメントが参考になるかと思います。

https://cloud.google.com/build/docs/overview?hl=ja

GitHubで編集を提案

Discussion