GKE+CircleCI 2.0で継続的デプロイ可能なアプリケーションをシュッと作る

こんにちは:) Pairsエンジニアの竹内です!
この記事は、eureka Engineering Advent Calendar 2017 8日目の記事です。
前回は高橋さんのPairs海外版におけるシステム構成の変遷を暴露するぜでした。

tl;dr

  • GitHubへコードがPushされる度にGKEへデプロイ出来るプロジェクトを手順を追って説明します。
  • Kubernetesのレシピも一緒にデプロイ出来るためReplicationの増減等も簡単に変更出来ます。
  • ↑の環境が整ったプロジェクトをLeiningenテンプレートのspellcardを使って簡単に作れます。

はじめに

Google Kubernetes Engine(GKE)は、コンテナ化されたアプリケーションを複数のノードへデプロイして運用出来るKubernetesベースの便利なSaasです。
普段の業務ではフロントエンドでTypeScriptを書いていたり、サーバーサイドでGoを書いたりする事が多い私ですが、
個人的なウェブサービスの運用や自己研鑽の為にここ8ヶ月程GKEを利用しています。

自分のGKEクラスタでは表題のようにCircleCIを使って継続的デプロイ可能な環境を実現しているのですが、
環境構築にあたり参考にしたCircleCI公式ドキュメントが旧版のCircleCIを使ったドキュメントであったため、自前でCircleCI 2.0向けの設定に書き換えて運用しています。

そんな訳で、今回はその自前の設定を使ってアプリケーションのデプロイ環境を構築するまでの手順を紹介致します。

実現する継続的デプロイ可能な環境

上記の図のように、GitHubへKubernetesのレシピやアプリケーションコードの変更をpushする度に

  1. CircleCI上でコンテナをビルド
  2. ビルドしたコンテナをCircleCI上でテスト
  3. テストが通ったコンテナをGKEがクラスタへデプロイ

と言った形で自動でGKEへアプリケーションがデプロイされるようになります。

続いて、この環境を実現するための手順を一つずつ追っていきます。

1. GKEクラスタを準備する

まずはデプロイ先のGKEクラスタを用意します。
初めてGKEに触れるのであれば、こちらのGoogle Kubernetes Engine クイックスタートgcloudコマンドラインツールのデフォルト設定 までを元に準備をすると良いでしょう。

また、もしあなたが作りたいアプリケーションをClojureで書く予定であれば、
Leiningenテンプレートのspellcardを用いて以下のコマンドだけでこの後の手順2,3を完了出来ます。

lein new spellcard <プロジェクト名> <gcpのプロジェクトID> <GKEクラスタ名>
chmod +x <プロジェクト名>/deploy.sh

以下に、手動で準備する場合についても手順を書いて行きます。

2. アプリケーション向けKubernetesレシピ(Service・Deployment)を用意する

※これから先の手順では、簡単の為に素のnginxコンテナをデプロイするものとして話を進めて行きます。

アプリケーションコンテナのデプロイに必要なKubernetesのDeploymentと、
ロードバランサの役割を果たすKubernetesのServiceを準備します。

sample/k8s/deployment.yml

apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: sample
labels:
app: sample
spec:
replicas: 2 # レプリケーションの増減数
selector:
matchLabels:
app: sample
template:
metadata:
labels:
app: sample
spec:
containers:
- name: sample
image: asia.gcr.io/${PROJECT_NAME}/sample:${CIRCLE_SHA1}
ports:
- containerPort: 80

sample/k8s/service.yml

apiVersion: v1
kind: Service
metadata:
name: sample
spec:
selector:
app: sample
type: NodePort
ports:
- protocol: TCP
port: 80
targetPort: 80
name: http

この2つを以下のように配置します。

sample
└── k8s
├── deployment.yml <-
└── service.yml <-

GCPのproject-idは my-project-id 、アプリケーション名を sample としています。

3. CircleCIの設定ファイルを準備する

CircleCIからコンテナのビルド・テスト・GKEへのデプロイを行うため、

  • CircleCIの設定ファイル本体の .circleci/config.yml ファイル
  • コンテナのレシピとなる Dockerfile
  • コンテナへのテストを行うための docker-compose.test.yml ファイル
  • コンテナのデプロイを行うための deploy.sh ファイル

を追加します。

.circleci/config.yml

version: 2
jobs:
build:
working_directory: /app
environment:
PROJECT_NAME: my-project-id
CLUSTER_NAME: my-cluster-name
CLOUDSDK_COMPUTE_ZONE: asia-northeast1-a
DEBIAN_FRONTEND: noninteractive
GOOGLE_APPLICATION_CREDENTIALS: ${HOME}/account-auth.json
docker:
- image: google/cloud-sdk:171.0.0-alpine
steps:
- checkout
- setup_remote_docker
- restore_cache:
keys:
- v1-{{ .Branch }}
paths:
- /caches/app.tar
- run:
name: Install dependencies
command: |
apk add --no-cache
py-pip=9.0.0-r1
gettext
pip install
docker-compose==1.12.0
gcloud components install kubectl
- run:
name: Install Docker client
command: |
set -x
VER="17.05.0-ce"
curl -L -o /tmp/docker-$VER.tgz https://get.docker.com/builds/Linux/x86_64/docker-$VER.tgz
tar -xz -C /tmp -f /tmp/docker-$VER.tgz
mv /tmp/docker/* /usr/bin
- run:
name: Check docker version
command: |
docker version
- run:
name: Load Docker image layer cache
command: |
set +o pipefail
docker load -i /caches/app.tar | true
- run:
name: Build application Docker image
command: |
docker build --cache-from=asia.gcr.io/${PROJECT_NAME}/sample:latest -t asia.gcr.io/${PROJECT_NAME}/sample:$CIRCLE_SHA1 .
docker tag asia.gcr.io/${PROJECT_NAME}/sample:$CIRCLE_SHA1 asia.gcr.io/${PROJECT_NAME}/sample:latest
- run:
name: Save Docker image layer cache
command: |
mkdir -p /caches
docker save -o /caches/app.tar asia.gcr.io/${PROJECT_NAME}/sample:latest
- save_cache:
key: v1-{{ .Branch }}-{{ epoch }}
paths:
- /caches/app.tar
- run:
name: Run tests
command: |
docker-compose -f docker-compose.test.yml run --rm sample
- deploy:
name: Deploy application Docker image
command: |
if [ "${CIRCLE_BRANCH}" == "master" ]; then
echo $ACCT_AUTH | base64 -d > ${HOME}/account-auth.json
gcloud auth activate-service-account --key-file ${HOME}/account-auth.json
gcloud config set project $PROJECT_NAME
gcloud --quiet config set container/cluster $CLUSTER_NAME
gcloud config set compute/zone ${CLOUDSDK_COMPUTE_ZONE}
gcloud --quiet container clusters get-credentials $CLUSTER_NAME
gcloud config set container/use_client_certificate True
./deploy.sh
fi

Dockerfile

FROM nginx:1.13.7

docker-compose.test.yml

version: "2"
services:
sample:
image: asia.gcr.io/${PROJECT_NAME}/sample:$CIRCLE_SHA1
command: /bin/bash -c "nginx -t -c /etc/nginx/nginx.conf"

deploy.sh

#!/bin/bash
# Exit on any error
set -e
for f in k8s/*.yml
do
envsubst < $f > "generated-$(basename $f)"
done
gcloud docker -- push asia.gcr.io/${PROJECT_NAME}/sample:$CIRCLE_SHA1
kubectl apply -f generated-deployment.yml --record
kubectl apply -f generated-service.yml

以上のファイルもプロジェクトフォルダ内に配置します。

sample
├── .circleci
│ └── config.yml <-
├── deploy.sh <-
├── docker-compose.test.yml <-
├── Dockerfile <-
└── k8s
├── deployment.yml
└── service.yml

また、 deploy.sh がCircleCIから実行できるように実行権限を付与しておきます。

chmod +x deploy.sh

ここまでの準備が出来たら、GitHubレポジトリを立てて一旦コードをPushします。
おおむねこのレポジトリの状態になるはずです。

GitHubレポジトリを準備出来たら、CircleCIを連携させます。

4. CircleCIをGitHubレポジトリと連携させる

CircleCIの連携はCircleCIのダッシュボードから簡単に行なえますが、CircleCIからGKEへデプロイするためのService Account Keyを発行して登録する必要があります。
まず、Service Account Keyはhttps://console.developers.google.com/apis/credentials/serviceaccountkey?project=_のページから以下の手順で準備できます。

  • GKEクラスタを作成したプロジェクトを選択
  • 「認証情報を作成」->「サービスアカウントキー」
  • 「サービスアカウント」を新しい「新しいサービスアカウント」へ選択し、「役割」に「Kubernetes Engine」-> 「Kubernetes Developer」と「ストレージ」-> 「ストレージ管理者」を追加してJSONでキーを作成する

Service Account Keyが準備出来たら、 base64 でbase64形式の文字列にして、CircleCIのダッシュボードから環境変数 $ACCT_AUTH として登録します。
ここまで準備が出来れば、後はGitHubへPushする度に自動でアプリケーションがGKEへデプロイされます。

おわりに

やや駆け足ですが、GKEとCircleCI 2.0を使った継続的デプロイ可能なアプリケーションを構築するまでの手順を紹介いたしました。
GKEを利用し始めてから、レプリケーションの増減や各種ミドルウェアの導入等もアプリケーションと同じ感覚で変更出来るようになり、大変快適な開発環境を得られているので
是非一度お試しいただければと思います:)

また途中にも登場しましたが、こちらのレポジトリに今回のコードサンプルが置いてありますのでご参考ください。
それでは、またの機会にお会いしましょう!

次回は山下さんの Go言語のプロファイリングツール、「pprofのWeb UI」がめちゃくちゃ便利なので紹介する です、お楽しみに!