CI/CDパイプライン - GitHub, CircleCIの連携とGCP(GCR)へのプッシュ

下記の技術を使い、CI/CDパイプラインを構築中です。

  • Docker
  • GitHub
  • CircleCI
  • GCR(Google Container Registry)
  • GKE(Google Kubernetes Engine)
  • Spinnaker

コンテナ化したGoアプリのソースコードをGitHubにプッシュし、CircleCIでジョブを実行し、GCRにdockerのイメージをプッシュ。Spinnkerでイメージの更新を検知し、最後にGKEへデプロイするというフローで構築しています。

この記事ではGitHubとCircleCI、GCPの連携までをまとめます。

ディレクトリ構成

下記のようなディレクトリ構成で試しました。

.
├── .circleci
│   └── config.yml
├── code
│   └── cmd
│       └── web
│           └── main.go
├── df-go.dockerfile
└── docker-compose.yml

.circleci/config.yml

CircleCIのジョブを実行するためのファイルです。CircleCIとGitHubを連携したのち、GitHubへプッシュすると、ここで設定したジョブが実行されます。

version: 3
jobs:
  build:
    docker:
      - image: circleci/golang:1.13
    working_directory: /go/src/github.com/hodanov/ci-cd-test
    steps:
      - checkout
      - run: go get -v -t -d ./...
      # テストコードを書いていれば`go test`でテストが実行され、エラーが出ればジョブが失敗する。
      - run: go test -v ./...

main.go

Goのソースコードです。簡易なWebサーバーが起動します。

package main

import (
	"fmt"
	"net/http"
)

func hoge(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "hogehoge")
}

func top(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello")
}

func main() {
	http.HandleFunc("/", top)
	http.HandleFunc("/hoge", hoge)
	http.ListenAndServe(":8080", nil)
}

df-go.dockerfile

dockerファイルです。 /go/src/go-web-server というディレクトリにGoのソースコードをコピーし、ビルドを実行後、 CMD でアプリケーションを実行させます。

FROM golang:1.13-alpine

WORKDIR /go/src/go-web-server
COPY ./code .

RUN go get -d -v ./... \
    && go install -v ./... \
    && go build -o go-web-server ./cmd/web

CMD ["./go-web-server"]

docker-compose.yml

ローカルPCで挙動を確認するために用意しました。デプロイでは使いません。

version: '3'

services:
  go:
    container_name: go-ci-cd-test
    build:
      context: .
      dockerfile: df-go.dockerfile
    tty: true
    ports:
      - 8080:8080

GitHubリポジトリの作成

リポジトリを作成し、プッシュします。この段階では特に変わった設定必要ないです。

GCPの設定

gloud コマンドを使い、CLIで設定を進めます。

Cloud SDKのセットアップ

Google Cloud SDKはGCPのリソースを管理するためのツール。手元のPCにインストールすることで、GCPをCLIで操作できます。

↓下記ドキュメントでインストールし、 gcloud auth listgcloud config list コマンドで設定が確認できればOK。

https://cloud.google.com/sdk/docs/

途中でプロジェクトを選択する必要があるので、GCPのプロジェクトがない場合は下記コマンドで作成します。

gcloud projects create your-gcp-project-id

# your-gcp-project-idは自分で決めた適当なプロジェクトIDに置き換える

プロジェクトとゾーンをデフォルトに設定

gcloud コマンドを実行する際のプロジェクトとゾーンを設定します。

gcloud config set project your-gcp-project-id
gcloud config set compute/zone asia-northeast1-b

# asia-northeast1-bは東京リージョン

gcloud config list で設定を確認できます。

プロジェクトの課金設定とAPIの有効化

操作するリソースに対してAPIを有効にするため、GCPの課金設定をします。

https://cloud.google.com/billing/docs/how-to/manage-billing-account?hl=ja

課金設定後、下記コマンドでAPIを有効にし、請求先アカウントとプロジェクトを紐付けます。

# APIを有効化
gcloud compute zones list

# APIを有効後、下記コマンドで課金設定が有効化どうかを確認
gcloud beta billing accounts list

# 請求先アカウントとGCPプロジェクトの紐づけ
gcloud beta billing projects link your-gcp-project-id --billing-account xxxxx-yyyyy-zzzzz

GitHubとCircleCIの連携

CircleCI へGitHubのアカウントでログイン。前項で作成したGitHubのリポジトリを選んで Set Up Project を選択、その後 Start build し、表記にしたがって設定を進めます。

設定が完了するとリポジトリへのpushをトリガーにジョブが実行されるようになります。

CircleCIのパイプライン画面

GitHub上でプルリクを出すと下図のようにCircleCIのチェックが確認できます。

CircleCIとGCRの連携

CircleCIからGCRへdockerイメージをプッシュできるようにします。

Cloud SDKの認証

CircleCIからGCRやGKEを操作する際にCloud SDKを使います。

google/cloud-sdk

GCPのCloudIAMというサービスで提供されている サービスアカウント という認証方式を使います。

GCPのCloudIAMとサービスアカウント

CloudIAMはGCPで提供されている権限管理の仕組み。サービスアカウントはCloudIAMが提供する認証方式で、GCP上にホストされているリソース同士のアクセスや外部サービスからのアクセスの権限管理に利用するアカウント

サービスアカウントの作成

↓サービスアカウントの作成

# サービスアカウントの作成
gcloud iam service-accounts create circleci --display-name circleci

# サービスアカウントのリストを確認
gcloud iam service-accounts list

ロールの付与

作成したサービスアカウントに対しGCRへプッシュする権限を付与します。

Container Registry の操作に必要な権限と役割

# roles/strage.adminを付与
gcloud projects add-iam-policy-binding your-gcp-project-id --member serviceAccount:circleci@your-gcp-project.iam.gserviceaccount.com --role roles/storage.admin

# IAMポリシーの確認
gcloud projects get-iam-policy your-gcp-project-id

CircleCIにサービスアカウントを登録

次に、CircleCIにGCPのサービスアカウントを登録します。下記コマンドで秘密鍵を生成し、CircleCIに環境変数として設定します。

# ↓circleci.jsonという名前で秘密鍵の情報が作成されます。
gcloud iam service-accounts keys create --iam-account circleci@your-gcp-project.iam.gserviceaccount.com ./circleci.json

↓CircleCIの環境変数登録画面。Project Settingsを開いて、上で生成したjsonを登録。ここで登録した環境変数は、CircleCIのジョブで参照することができます。

GCRへプッシュするジョブの登録と実行

CircleCIからGCRへdockerイメージをpushするためにGCRのAPIを有効にします。

# GCRのAPIを有効化
gcloud services enable containerregistry.googleapis.com

# 設定の確認
gcloud services list --enabled

.circleci/config.yml を下記に上書きします。GCRへプッシュするための設定をし、イメージをビルド→GCRへプッシュするジョブです。

version: 2
jobs:
  build:
    working_directory: /go/src/github.com/hodanov/ci-cd-test
    docker:
      - image: google/cloud-sdk
    steps:
      - checkout
      # ↓CircleCI上でdocker、docker-composeコマンドを利用するための設定
      - setup_remote_docker:
        version: 18.06.0-ce
      - run:
          name: Setup CLOUD SDK
          command: |
            # CircleCIの環境変数に登録したサービスアカウントの値を`echo`して`gcloud auth`に渡す。
            # `gcloud auth ... --key-file=-`はCLOUD SDKの認証にサービスアカウントを利用するための設定
            echo $GCLOUD_SERVICE_KEY | gcloud auth activate-service-account --key-file=-
            gcloud --quiet config set project your-gcp-project-id
            gcloud --quiet config set compute/zone asia-east1-b
            # ↓dockerのCredential helpersを利用。GCRなどのDocker Registry向けの認証を`docker login`コマンドなしで提供できるようにする設定
            gcloud --quiet auth configure-docker
      - run:
          name: Push docker image
          command: |
            docker image build -t go-web-server .
            TAG=gcr.io/your-gcp-project-id/go-web-server:pushed-`date +%!Y(MISSING)%!m(MISSING)%!d(MISSING)%!H(MISSING)%!M(MISSING)%!S(MISSING)`
            docker image tag go-web-server $TAG
            docker image push $TAG
            LATEST_TAG=grc.io/your-gcp-project-id/go-web-server:latest
            docker image tag go-web-server $LATEST_TAG
            docker image push $LATEST_TAG

config.ymlを更新したらGitHubへプッシュして、GCRへイメージがプッシュされることを確認します。

# プッシュされたdockerイメージのリスト
gcloud container images list

# 指定したイメージのタグを確認
gcloud container images list-tags gcr.io/your-gcp-project-id/go-web-server

まとめ

何回かの失敗の末、ジョブの実行が成功し、GCRへプッシュできました。

次はGCR→spinnaker→GKEの連携を試します。