1月半ほど前、社内勉強会のおすそ分けとして、わりとゴツいKubernetesハンズオンの記事を書きました。
おかげさまでご好評いただき、今では1500を超えるいいねをいただきました。ありがとうございます。
今回の話とは全然関係ないですが、こちらも頑張って書いているので興味あれば見てやってください。
(マイナーなタグしか付けられなかったせいか、view数がとても少なくて悲しみに暮れています…😢)
QRコード Deep Dive ーデータ符号化とか誤り訂正とかー
開発環境の構築ができるようになろう!
さて、みなさまKubernetesについては完全理解しましたでしょうか?
主要な要素の説明はしているので、完全理解フェーズにたどり着くのも夢ではないと思います。
自信と理解度の関係を図にしました。#完全に理解した pic.twitter.com/SA638Vy9UH
— 廻転楕円体 (@kaitendaentai) 2018年10月17日
ただ、ハンズオンの中では、既存のDockerイメージしか使っていません。
Kubernetesを使ってオリジナルのアプリケーションを構築するには、
Dockerを交えた開発環境の構築という、もうひと山を超えねばなりません。
ここで、前回のハンズオンの目標を見返してみてください。
- 構築・運用ができるような気分になる
できるようになるとは言ってない…!(どーん
ということで、前回入れようと思ったけど力尽きたゴツくなりすぎると思ってやめておいた、
開発環境の構築について、またハンズオン形式でお伝えしようと思います!
ちなみに、本記事は福岡のKubernetes勉強会「ふくばねてす node-2」での発表資料をQiita用にコンバートしたものです。
発表資料 : https://speakerdeck.com/ktam1219/waritogotuikuberneteshanzuon-sofalseatoni
やりたいこと
ローカル開発環境
まずはローカルでの開発環境を作らないことには始まりません。
大まかな開発フローは以下の繰り返しになります。
- コードを編集
- Dockerイメージをbuild
- ローカルのKubernetesクラスターにデプロイ
CI/CD環境
本番環境にリリースする際の作業はさらに手順が増えます。
- コードを編集
- GitにPush
- Dockerイメージをbuild
- コンテナレジストリにDockerイメージをPush
- Kubernetesクラスターにデプロイ
手作業でやろうとすると大変なので、CircleCIなどを使って自動化したいですね。
以上が今回やりたいことなんですが、
なんだか一気にめんどくさい気持ちになってきましたね。
でも、簡単にできるんです。
そう、Skaffoldならね!
Skaffold
Skaffoldは、Google謹製のKubernetesの開発支援ツールです。
クラスタにアプリケーションをデプロイするまでの
一連の作業を(ローカル/リモート問わず)自動化してくれます。
- Dockerイメージのbuild
- Dockerイメージの構造テスト
- Dockerイメージへのタグ付け
- コンテナレジストリにDockerイメージをPush
- KubernetesクラスタにDeploy
先ほど挙げたやりたいことに加え、テストやタグ付けまで…!
また、各フェーズは独立していて、プラグイン的にお好みでやり方を選択できます。
Skaffoldをローカル開発で使ってみよう
ローカルのKubernetes環境を準備する
前回のハンズオンを参考に、下準備の部分まで終わらせます。
https://qiita.com/Kta-M/items/ce475c0063d3d3f36d5d#kubernetes%E3%82%92%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%81%A7%E8%A9%A6%E3%81%97%E3%81%A6%E3%81%BF%E3%82%88%E3%81%86
サンプルアプリケーションを準備する
前回と同様、kubernetes/examplesのguestbookアプリを使いましょう。
$ git clone git@github.com:kubernetes/examples.git
$ mkdir skaffold_example
$ cp -R examples/guestbook/* skaffold_example
$ cd skaffold_example
不要なファイルを削除しておきます。
$ rm -rf all-in-one
$ rm -rf legacy
PCの負担を下げるためちょっと調整 & Dockerイメージの取得先を変更します。
$ vi examples/guestbook/frontend-deployment.yaml
10行目 replicas: 3 <- これを1に変更
19行目 image: gcr.io/google-samples/gb-frontend:v4 <- これを ${dockerhubのユーザー名}/skaffold-example-frontendに変更
$ vi examples/guestbook/redis-slave-deployment.yaml
11行目 replicas: 2 <- これを1に変更
21行目 image: gcr.io/google_samples/gb-redisslave:v1 <- これを ${dockerhubのユーザー名}/skaffold-example-redis-slaveに変更
ingressのマニフェストファイルを作成します。
$ cat << 'EOT' >./guestbook-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: guestbook-ingress
spec:
rules:
- http:
paths:
- path: /
backend:
serviceName: frontend
servicePort: 80
EOT
Skaffoldのインストール & セットアップ
以下のURLを参考にインストールを行います。
https://skaffold.dev/docs/getting-started/#installing-skaffold
macであればHomebrewで入ります。
$ brew install skaffold
Skaffoldの設定ファイルを作成します。
apiVersion: skaffold/v1beta11
kind: Config
# Dockerイメージのビルドに関する設定
build:
artifacts:
# ビルドするイメージ
- image: ${dockerhubのユーザー名}/skaffold-example-frontend
context: php-redis # ソースファイルがあるパスの指定
- image: ${dockerhubのユーザー名}/skaffold-example-redis-slave
context: redis-slave
local: # ローカルでイメージをビルドするときの設定
push: false # ビルドしたイメージをpushするか
# デプロイに関する設定
deploy:
kubectl:
# デプロイ対象のマニフェストファイルの指定
manifests:
- frontend-*.yaml
- redis-master-*.yaml
- redis-slave-*.yaml
- guestbook-ingress.yaml
設定ファイルのその他の項目については、以下を参照してください。いろいろできます。
https://skaffold.dev/docs/references/yaml/
skaffold dev
さて、ここで以下のコマンドを実行してみましょう。
$ skaffold dev
開発用のコマンドです。
実行後、Dockerイメージのビルド、Kubernetesクラスターへのデプロイをおこないます。
その後はファイルの変更を監視して、変更があるたびにビルド、デプロイを繰り返します。
また、ファイル変更監視中は、各Podのログも流してくれます。いたれりつくせり。
Generating tags...
- mohri1219/skaffold-example-frontend -> mohri1219/skaffold-example-frontend:76a6cd0-dirty
- mohri1219/skaffold-example-redis-slave -> mohri1219/skaffold-example-redis-slave:76a6cd0
Tags generated in 49.700092ms
Starting build...
Found [docker-for-desktop] context, using local docker daemon.
Building [mohri1219/skaffold-example-frontend]...
Sending build context to Docker daemon 7.68kB
Step 1/8 : FROM php:5-apache
---> 24c791995c1e
(中略)
Starting deploy...
kubectl client version: 1.14
deployment.apps/frontend created
service/frontend created
ingress.extensions/guestbook-ingress created
deployment.apps/redis-master created
service/redis-master created
deployment.apps/redis-slave created
service/redis-slave created
(中略)
Watching for changes every 1s...
[frontend-7df8f8b7f8-mscxk php-redis] AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 10.1.1.154. Set the 'ServerName' directive globally to suppress this message
前回のハンズオン同様、http://localhost にアクセスすると画面が出てきます。
ファイルの変更検知機能を試してみるために、おもむろにhtmlファイルを書き換えてみます。
<h2>Guestbook!!</h2> <!- "!!"を追加してみる -->
すると、ちゃんと変更を検知してDockerイメージのビルドを始めてくれます!
Generating tags...
- mohri1219/skaffold-example-frontend -> mohri1219/skaffold-example-frontend:76a6cd0-dirty
Tags generated in 62.953504ms
Starting build...
Found [docker-for-desktop] context, using local docker daemon.
Building [mohri1219/skaffold-example-frontend]...
Sending build context to Docker daemon 7.68kB
Step 1/8 : FROM php:5-apache
---> 24c791995c1e
Step 2/8 : RUN pear channel-discover pear.nrk.io
---> Using cache
---> bd9823d8109d
Step 3/8 : RUN pear install nrk/Predis
---> Using cache
---> 390dd33a211f
ブラウザをリロードしてみるとちゃんと変更が反映されていることが確認できます。
最後に、Ctrl+Cで終了してみましょう
すると、DockerイメージやKubernetesクラスター内の各種リソースを削除してくれます!
Pruning images...
untagged image mohri1219/skaffold-example-frontend:3eba4d84484d6648b4a0907db8f472e5df4439d9ba4ca012a5b96f95af4dd09b
untagged image mohri1219/skaffold-example-frontend:c4971ec-dirty
untagged image mohri1219/skaffold-example-frontend@sha256:c2f8eb06b6a9d0624c1f8ea6cba4dfaefa450f11d99c90a209617618b0687730
deleted image sha256:3eba4d84484d6648b4a0907db8f472e5df4439d9ba4ca012a5b96f95af4dd09b
deleted image sha256:42794d3d5de90049ae108429e1536b747b145918b9972edd62e958b58b09c1ec
WARN[0290] builder cleanup: pruning images: Error response from daemon: conflict: unable to delete 48d1ae3a57c7 (cannot be forced) - image is being used by running container 0b0107ee518b
Cleaning up...
deployment.apps "frontend" deleted
service "frontend" deleted
ingress.extensions "guestbook-ingress" deleted
deployment.apps "redis-master" deleted
service "redis-master" deleted
deployment.apps "redis-slave" deleted
service "redis-slave" deleted
Cleanup complete in 3.70313407s
skaffold run
さて、次は以下のコマンドを実行してみましょう。
$ skaffold run
これは単発のデプロイコマンドで、Dockerイメージのビルド、Kubernetesクラスターへのデプロイをおこなって終了します。
終了後も各種リソースはKubernetesクラスター内に残り続けます。
Generating tags...
- mohri1219/skaffold-example-frontend -> mohri1219/skaffold-example-frontend:76a6cd0-dirty
- mohri1219/skaffold-example-redis-slave -> mohri1219/skaffold-example-redis-slave:76a6cd0
Tags generated in 48.802686ms
Starting build...
Found [docker-for-desktop] context, using local docker daemon.
Building [mohri1219/skaffold-example-frontend]...
Sending build context to Docker daemon 7.68kB
Step 1/8 : FROM php:5-apache
---> 24c791995c1e
(中略)
Starting deploy...
kubectl client version: 1.14
deployment.apps/frontend created
service/frontend created
ingress.extensions/guestbook-ingress created
deployment.apps/redis-master created
service/redis-master created
deployment.apps/redis-slave created
service/redis-slave created
Deploy complete in 1.763257066s
Skaffoldでテストもしてみる
Skaffoldのテストは、container-structure-testを使います。
その名の通り、コンテナイメージの構造をテストするものです。
できることは、
- イメージ内のコマンドの出力をチェック
- ファイルシステムのメタデータと内容を検証
- etc...
今回は、skaffold-example-frontend
のファイル存在チェックをしてみましょう。
その他のテスト項目については
https://github.com/GoogleContainerTools/container-structure-test/blob/master/README.md を参照してください。
schemaVersion: 2.0.0
# 各種ファイルの存在確認
fileExistenceTests:
- name: 'php file'
path: '/var/www/html/guestbook.php'
shouldExist: true
- name: 'js file'
path: '/var/www/html/controllers.js'
shouldExist: true
- name: 'html file'
path: '/var/www/html/index.html'
shouldExist: true
skaffoldの設定ファイルにテストの設定を追記しましょう。
apiVersion: skaffold/v1beta11
kind: Config
build:
(略)
# テストの設定
test:
test:
- image: ${dockerhubのユーザー名}/skaffold-example-frontend
structureTests:
- ./structure-test/frontend.yaml
deploy:
(略)
skaffold run
を実行すると、以下のようにログが出てきます。
もちろん、テストが失敗するとデプロイはされません。
$ skaffold run
(中略)
Starting test...
======================================
====== Test file: frontend.yaml ======
======================================
=== RUN: File Existence Test: php file
--- PASS
=== RUN: File Existence Test: js file
--- PASS
=== RUN: File Existence Test: html file
--- PASS
=======================================
=============== RESULTS ===============
=======================================
Passes: 3
Failures: 0
Total tests: 3
PASS
Test complete in 1.455478421s
お片付け
ローカルの開発環境についてはここまで。開発が進められるイメージは持てましたか?
Docker for DesktopのKubernetesを無効化してお片付けをしましょう。
SkaffoldをCI/CD向けに使ってみよう
完成版はこちらにあります。
https://github.com/Kta-M/skaffold_example
GitHubのリポジトリを作成
みんなできるだろうから割愛します。
DockerHubのリポジトリを作成
EKSでクラスタを作成
前回のハンズオンでやったやつです。
相変わらず立てるのに15分ぐらいかかります。とてもつらい。
$ eksctl create cluster \
--name eksctl-handson \
--region ap-northeast-1 \
--nodes 3 \
--nodes-min 3 \
--nodes-max 3 \
--node-type t2.medium \
--ssh-public-key <キーペア名>
ap-northeast-1bが選択できる古いAWSアカウントはAZ指定行なってください。
Skaffoldの設定ファイルを調整
先ほどまでローカルでskaffoldを使ってきましたが、本番環境にデプロイする際は以下の点を変えたいです。
- DockerイメージをコンテナレジストリにPushする
- ingressのマニフェストファイルは使わない
こういった、状況に応じて挙動を切り替えるための仕組みとして、profileという機能があります。
実行時にprofileを与えると、それに応じた設定をオーバーライドしてくれる機能です。
apiVersion: skaffold/v1beta11
kind: Config
(中略)
# 以下を追加
profiles:
- name: prd
build:
local:
push: true # ビルドしたイメージをコンテナレジストリにPushする
deploy:
kubectl:
manifests: # ingressのファイルを除外
- frontend-*.yaml
- redis-master-*.yaml
- redis-slave-*.yaml
以下のようにprd
のprofileを追記しましょう。
こうすることで、 skaffold run --profile prd
とすればprdの設定でオーバーライドしてくれるようになります。
CircleCIの設定
CirlceCIにログインし、GitHubリポジトリを登録しましょう。
変数名 | 値 |
---|---|
AWS_DEFAULT_REGION | ap-northeast-1 |
CUSTER_NAME | eksctl-handson |
DOCKER_HUB_USR | DockerHubのユーザー名 |
DOCKER_HUB_PSW | DockerHubのパスワードをbase64エンコードしたもの |
EKSにデプロイするため、AWSの認証情報を設定します。
ここで、EKSクラスタを立てるのに使ったIAMユーザーと同じものを設定しなければならないことに注意してください。
KubernetesのRBACとIAMが紐付いてしまうので。
参考: https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/managing-auth.html
また、orbを使えと言われてるけど今回は無視します。
orbはバージョン2.1でしか使えないけど、2.1はまだローカルでの動作確認ができないようなので…。とてもつらい。
CircleCIのconfigを作成
以下のようになります。name
を見ていけば何をしているのかだいたい分かるかと思います。
本来は、もろもろインストールしたDockerイメージを作ってからやるべきなんでしょうけど、
分かりやすくするためインストールもcircleciで行っています。
version: 2
jobs:
build:
docker:
- image: docker:18.09
steps:
- run:
name: set shell and install tools
command: |
set -x; \
apk add --no-cache --virtual .fetch-deps curl git python py-pip less groff
- checkout
- setup_remote_docker
- run:
name: install kubectl
command: |
curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.15.0/bin/linux/amd64/kubectl && \
chmod +x ./kubectl && \
mv ./kubectl /usr/local/bin/kubectl
- run:
name: install container-structure-test
command: |
curl -LO https://storage.googleapis.com/container-structure-test/latest/container-structure-test-linux-amd64 && \
chmod +x container-structure-test-linux-amd64 && \
mv container-structure-test-linux-amd64 /usr/local/bin/container-structure-test
- run:
name: install aws cli
command: pip install awscli
- run:
name: install aws-iam-authenticator
command: |
curl -o aws-iam-authenticator https://amazon-eks.s3-us-west-2.amazonaws.com/1.12.7/2019-03-27/bin/linux/amd64/aws-iam-authenticator && \
chmod +x ./aws-iam-authenticator && \
mv ./aws-iam-authenticator /usr/local/bin
- run:
name: setup kubeconfig
command: aws eks update-kubeconfig --name $CLUSTER_NAME
- run:
name: login to dockerhub
command: echo $DOCKER_HUB_PSW | base64 -d | docker login -u $DOCKER_HUB_USR --password-stdin
- run:
name: install skaffold
command: |
curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v0.32.0/skaffold-linux-amd64 && \
chmod +x skaffold && \
mv skaffold /usr/local/bin
- run:
name: skaffold run
command: skaffold run --profile prd
workflows:
version: 2
workflow:
jobs:
- build
GitHubにPush
この状態でもろもろをGitHubにPushしてあげると…
CircleCI上でskaffold run --profile prd
が実行され、もろもろの処理が走ります。
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/frontend LoadBalancer 10.100.61.11 xxxxxx.ap-northeast-1.elb.amazonaws.com 80:31673/TCP 8s
service/kubernetes ClusterIP 10.100.0.1 <none> 443/TCP 23m
service/redis-master ClusterIP 10.100.137.217 <none> 6379/TCP 7s
service/redis-slave ClusterIP 10.100.217.57 <none> 6379/TCP 7s
ELBのドメインを確認してアクセスすると、guestbookアプリが動いているのが確認できると思います!
まとめ
- 「わりとゴツいやつ」から一歩進んで、Kubernetesを使った開発の具体的なイメージが掴めたのであれば幸いです😊
- ただ、我々はKubernetes沼に足を踏み入れたばかり
- 今回紹介しきれなかった用語、エコシステムなどまだ膨大に…
- 俺たちの真の戦いはこれからだ!