はじめに
GKE構築のパイプラインを作成したときのメモ書きです。
自分用の備忘録の側面が強いため、参考にさせて頂いたQiita記事と重複する部分が多いです。
今回はGKEの構築のツールとして「Terraform」、パイプラインの実行環境として「Github Actions」を利用しています。
環境
パイプライン上で利用する環境についてはソースコード内に記述されているため、ローカルの操作に必要なもののみ記述
* macOS Catalina Version 10.15.4
* fish (利用シェル)
* homebrew 2.2.10
* Google Cloud SDK 272.0.0
* gsutil version: 4.46
* kubectl v1.15.5
前準備
gcloudのインストール
homebrew を利用してインストールする。
$ brew cask install google-cloud-sdk
# 利用するシェルに応じたpathの設定
# fishの場合は、 ~/.config/fish/config.fshに以下の内容を記述
source /usr/local/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/path.fish.inc
Kubernetes Engine APIを有効にする
gcloud services enable container.googleapis.com
Google Cloud Storage
GKE構築にTerraformを使うため、stateファイルの置き場所としてGCSを利用する。
バケット名はtf-state-locationとして作成する。
# バケットのタイプと名前を環境変数に設定
$ set GCS_CLASS multi_regional
$ set GCS_BUCKET_NAME tf-state-location
# タイプと名前を引数にバケットを作成
$ gsutil mb -p $GCP_PROJECT -c $GCS_CLASS -l us gs://$GCS_BUCKET_NAME/
サービスアカウント
パイプラインの実行ユーザーとしてサービスアカウントを作成する。
サービスアカウントの作成
サービスアカウント名はgke-terraform-service-accountとして作成する。
# GCPにプロジェクト名を取得して環境変数に設定
$ set GCP_PROJECT (gcloud info --format='value(config.project)')
# サービスアカウント名を環境変数に設定
$ set TERRAFORM_SA gke-terraform-service-account
# プロジェクトと名前を引数にサービスアカウント を作成
$ gcloud iam service-accounts create $TERRAFORM_SA --project=$GCP_PROJECT --display-name $TERRAFORM_SA
# 作成したサービスアカウントのID情報を環境変数に設定(後の章利用する)
$ set TERRAFORM_SA_EMAIL (gcloud iam service-accounts list --project=$GCP_PROJECT --filter="displayName:$TERRAFORM_SA" --format='value(email)')
サービスアカウントへの権限の付与
サービスアカウントが実行ユーザーとなるため、GCPに対する必要な権限を付与する。
GKE作成に関する権限の付与
$ gcloud projects add-iam-policy-binding $GCP_PROJECT \
--role roles/iam.serviceAccountUser \
--member serviceAccount:$TERRAFORM_SA_EMAIL
$ gcloud projects add-iam-policy-binding $GCP_PROJECT \
--role roles/compute.admin \
--member serviceAccount:$TERRAFORM_SA_EMAIL
$ gcloud projects add-iam-policy-binding $GCP_PROJECT \
--role roles/storage.admin \
--member serviceAccount:$TERRAFORM_SA_EMAIL
$ gcloud projects add-iam-policy-binding $GCP_PROJECT \
--role roles/container.clusterAdmin \
--member serviceAccount:$TERRAFORM_SA_EMAIL
# 結果確認用
gcloud projects get-iam-policy $GCP_PROJECT \
--flatten="bindings[].members" \
--filter="bindings.members:serviceAccount:$TERRAFORM_SA_EMAIL" \
--format='table(bindings.members,bindings.role)'
GCSのバケットへの権限付与
パイプラインの中でstateファイル置き場として用意したgcsのバケットにアクセスする必要があるため、サービスアカウントその権限を付与する。
# バケットに対するAdmin権限を付与する(serviceaccountがstorage.adminロールを持っているためやらなくても可)
$ gsutil iam ch serviceAccount:$TERRAFORM_SA_EMAIL:objectAdmin gs://$GCS_BUCKET_NAME/
サービスアカウントのkeyファイル(json)の作成
Terraformを実行するときに必要な、サービスアカウントのkeyを作成する。
# key作成場所のディレクトリ情報を環境変数に設定して、ディレクトリ作成
$ set TERRAFORM_SA_DEST ~/.gcp/terraform-service-account.json
$ mkdir -p (dirname $TERRAFORM_SA_DEST)
# サービスアカウントのkeyファイルを指定したディレクトリに作成
$ gcloud iam service-accounts keys create $TERRAFORM_SA_DEST --iam-account $TERRAFORM_SA_EMAIL
GKEデプロイパイプラインの構築
この章で紹介するコードは以下のGitリポジトリ上で公開している。
参考にした公式ページ等の情報もreadmeに記述しています。
https://github.com/sabure500/gke-terraform
Terraform
.tfファイルは以下のTerraform公式のGKE用のサンプルコードを参考に作成する。
google_container_cluster
環境変数の定義
Terraform内で利用する環境変数を定義する。
variable "project" {}
variable "cluster_name" {
default = "my-gke-cluster"
}
variable "location" {
default = "us-west1-a"
}
variable "machine_type" {
default = "f1-micro"
}
variable "preemptible_machine_type" {
default = "e2-small"
}
variable "network" {
default = "default"
}
variable "min_master_version" {
default = "1.15.11-gke.3"
}
variable "node_version" {
default = "1.15.11-gke.3"
}
GCPのプロバイダ情報の定義
provider "google" {
version = "3.17.0"
project = var.project
region = var.location
}
Backend(tfstateファイルの保管場所)の定義
.tfstate ファイルを管理するための Backend を定義する。
バケット名は、事前に作成した GCS バケット名を指定する。
terraform {
backend "gcs" {
bucket = "tf-state-location"
prefix = "terraform/gke"
}
}
GKEの定義
記述に関する詳細はGitリポジトリ内のreadmeを参照
terraform {
required_version = "0.12.24"
}
resource "google_container_cluster" "primary" {
name = var.cluster_name
location = var.location
remove_default_node_pool = true
initial_node_count = 1
network = var.network
min_master_version = var.min_master_version
node_version = var.node_version
monitoring_service = "none"
logging_service = "none"
master_auth {
username = ""
password = ""
client_certificate_config {
issue_client_certificate = false
}
}
}
resource "google_container_node_pool" "primary_nodes" {
name = "${var.cluster_name}-node-pool"
location = var.location
cluster = google_container_cluster.primary.name
node_count = 1
management {
auto_repair = true
}
node_config {
machine_type = var.machine_type
oauth_scopes = [
"https://www.googleapis.com/auth/devstorage.read_only",
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring"
]
}
}
resource "google_container_node_pool" "primary_preemptible_nodes" {
name = "${var.cluster_name}-preemptible-node-pool"
location = var.location
cluster = google_container_cluster.primary.name
node_count = 2
management {
auto_repair = true
}
node_config {
preemptible = true
machine_type = var.preemptible_machine_type
oauth_scopes = [
"https://www.googleapis.com/auth/devstorage.read_only",
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring",
]
}
}
Github Actions
上記で作成した.tfファイルのTerraformによる実行はGithub Actionsを用いて行う。
Github ActionsによるTerraformの実行は以下の公式のサンプルを参考に作成する。
Terraform Github Actions Get Started
Test用のJobの作成
masterブランチ以外では、terraformのinit,validate,planまでを実行するJobを作成する。
name: 'terraform-test'
on:
push:
branches-ignore:
- master
env:
TERRAFORM_VERSION: 0.12.24
TF_VAR_project: ${{secrets.PROJECT}}
GOOGLE_CREDENTIALS: ${{secrets.GOOGLE_CREDENTIALS}}
jobs:
terraform:
name: 'terraform-test'
runs-on: ubuntu-latest
steps:
- name: 'Checkout'
uses: actions/checkout@v1
- name: 'Terraform Format'
uses: hashicorp/terraform-github-actions@master
with:
tf_actions_version: 0.12.24
tf_actions_subcommand: 'fmt'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: 'Terraform Init'
uses: hashicorp/terraform-github-actions@master
with:
tf_actions_version: 0.12.24
tf_actions_subcommand: 'init'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: 'Terraform Validate'
uses: hashicorp/terraform-github-actions@master
with:
tf_actions_version: 0.12.24
tf_actions_subcommand: 'validate'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: 'Terraform Plan'
uses: hashicorp/terraform-github-actions@master
with:
tf_actions_version: 0.12.24
tf_actions_subcommand: 'plan'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Apply用のJob
masterブランチにpushされた際に、terrafromのinit,applyを実行するJobを作成する。
name: 'terraform-apply'
on:
push:
branches:
- master
# APIアクセスでイベント発火ができる条件
repository_dispatch:
types: [apply]
env:
TERRAFORM_VERSION: 0.12.24
TF_VAR_project: ${{secrets.PROJECT}}
GOOGLE_CREDENTIALS: ${{secrets.GOOGLE_CREDENTIALS}}
jobs:
terraform:
name: 'terraform-apply'
runs-on: ubuntu-latest
steps:
- name: 'Checkout'
uses: actions/checkout@v1
- name: 'Terraform Init'
uses: hashicorp/terraform-github-actions@master
with:
tf_actions_version: 0.12.24
tf_actions_subcommand: 'init'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: 'Terraform Plan'
uses: hashicorp/terraform-github-actions@master
with:
tf_actions_version: 0.12.24
tf_actions_subcommand: 'plan'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: 'Terraform Apply'
uses: hashicorp/terraform-github-actions@master
with:
tf_actions_version: 0.12.24
tf_actions_subcommand: 'apply'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
クラスタ削除用のJob
このGKEクラスタは学習用に用意するため、GitにPushを行わずに気軽にクラスタを削除できるようにJobを用意しておく
name: 'terraform-destroy'
on:
# APIアクセスでイベント発火ができる条件
repository_dispatch:
types: [destroy]
env:
TERRAFORM_VERSION: 0.12.24
TF_VAR_project: ${{secrets.PROJECT}}
GOOGLE_CREDENTIALS: ${{secrets.GOOGLE_CREDENTIALS}}
jobs:
terraform:
name: 'terraform-apply'
runs-on: ubuntu-latest
steps:
- name: 'Checkout'
uses: actions/checkout@v1
- name: 'Terraform Init'
uses: hashicorp/terraform-github-actions@master
with:
tf_actions_version: 0.12.24
tf_actions_subcommand: 'init'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: 'Terraform Plan Destroy'
uses: hashicorp/terraform-github-actions@master
with:
tf_actions_version: 0.12.24
tf_actions_subcommand: 'plan'
args: '-destroy -out=./destroy-plan'
- name: 'Terraform Apply Destroy'
uses: hashicorp/terraform-github-actions@master
with:
tf_actions_version: 0.12.24
tf_actions_subcommand: 'apply'
args: './destroy-plan'
このJobはパイプラインでは実行されず、以下のコマンドでGithubのAPIを直接叩くことで実行できる
(事前にGithubのrepo権限を持つPersonal access tokenを作成しておく必要がある)
curl -vv \
-H "Authorization: token $PERSONAL_ACCESS_TOKEN" \
-H "Accept: application/vnd.github.everest-preview+json" \
"https://api.github.com/repos/sabure500/gke-terraform/dispatches" \
-d '{"event_type": "destroy", "client_payload": {"target_brunch": "master"}}'
また、コマンドからdestroyを実行した時用に、Applyに関しても上記のコマンドのevent_typeをapplyにすることで実施できる
Github Actions内で利用する環境変数の設定
上記で記述したJobでは、以下の2点を外部から受け取る環境変数として設定している。
- PROJECT : GCPのプロジェクト名
- GOOGLE_CREDENTIALS : サービスアカウントの章で作成したservice accountのKey
これらはクレデンシャル情報のため、以下の手順でGithubのSecret機能を用いて登録する。
- Githubのリポジトリ選択画面にて、Settingsタブを選択する
- Optionsの項目から、Secretsを選択する
- Add a new secretから必要なsecretを作成する
リポジトリ運用ルールの設定
上記で記述したJobでは、master以外のブランチでtestを実行してからmasterへpushすると実際にApplyするという流れになっている。
そのため、直接masterにpushを行われるとtestが実行されなくなってしまう。
それを防ぐために、masterへの直push禁止と、プルリクを行う際にtestJobが必ず通っている必要があるといったルールを作成する。
- Githubのリポジトリ選択画面にて、Settingsタブを選択する
- Optionsの項目から、Branchesを選択する
- Branch protection rule の Add ruleを選択する
- Require status checks to pass before merging にチェックを入れ、Require branches to be up to date before mergingにもチェックを入れる
- 表示されるJob名の中からmasterへのMergeの条件にしたいJobを選択する。(今回の場合はterraform-testを選択)
GKE構築結果の確認
Clusterの確認
パイプラインから作成後に実際に構築できているかを確認する
# kubectl の操作対象を GKE に切り替える
gcloud container clusters get-credentials --zone us-west1-a my-gke-cluster
# 結果確認用
kubectl config current-context
Sampleアプリの起動
構築したGKE上でサンプルアプリを稼働させる
# サンプルアプリ用のNamespace作成
kubectl create namespace sample
# サンプルアプリのデプロイ
kubectl -n sample create deployment hello-server --image=gcr.io/google-samples/hello-app:1.0
# サンプルアプリ公開用のservice作成
kubectl -n sample expose deployment hello-server --type=LoadBalancer --port 8080
# 外部IPの取得
kubectl -n sample get service
上記コマンドを実施後に、取得したIPで以下にアクセスする
http://[EXTERNAL-IP]:8080
参考
追記
記事で書いたコードの内容だと、以下のエラーが出るようになりました。
error creating NodePool: googleapi: Error 400: Node pools of f1-micro machines are not supported due to insufficient memory., badRequest
どうやらあるKubernetesのバージョン以降はf1-microはスペック不足で動かないみたいです。(今までも本当は無理だったけど制限されてなかったから作れていただけっぽい?)
自分が学習用で使う分にはe2-smallのNode1台で十分だったため、それだけ残してf1-microの部分はコメントアウトしました。
参考:https://cloud.google.com/kubernetes-engine/docs/concepts/cluster-architecture#memory_cpu