※ Wantedly社内のインフラチームで行われた Kubernetes 速習会の資料です。簡単なWebサーバーを立てて、マシンとコンテナをスケールしたり落としてみたりする実験をハンズオンで行いました。
コマンドの実行結果もだいたい載せているのでGKEとかKubernetes触りたいなと思った時に参考にしてみてください。
事前情報
この中で実は、Container Registryを提供してくれているのがポイント高い。OpenSourceのコンテナレジストリは不安定 / dockerhubやquai.ioは同じデータセンター無いと比べネットワーク遅延が気になるので。
あとは、ハイブリッドネットワークと書いてあるとおり、kubernetesの特徴であるりネットワークがflatでクラスタ内でPrivateアドレスでやり取りできるというのも、やっぱり使いやすかったりする。
cf. http://radar.oreilly.com/2015/10/swarm-v-fleet-v-kubernetes-v-mesos.html
その他、各マシンレベルのモニタリングもベータだができてきている。
cf. https://cloud.google.com/monitoring/get-started?hl=ja
ゴール
- 実際、Version 1.0 を超えたKubernetesが本当に使えるレベルか試してみる
- 具体的には、コマンドからWebコンテナの増減(
kubectl
を使う)、クラスタ内のノードマシンの増減(gcloud container
を使う)が出来るようにする
ちなみに、実際に検証してみた感想としては「かなりすごい!」。普通にマシンのスケールとコンテナのスケールが安定して、十分な速度で動くし、Amazonのコンテナエンジンのように命令にラグがあってコントロールを失うみたいなこともほぼない印象。
Dockerでコンテナ化が進んできた今としては、移行がそんなに大変じゃないので、AWSからGKE/GCPに乗り換えすらありえるかも。
コマンドのインストール
Cloud SDK gcloud
と、コンテナを管理するkubectl
をインストールする
gcloud のインストール
curl https://sdk.cloud.google.com | bash
ここで、インストール場所を選択する。デフォルトで
~
以下。つまり自分は/Users/awakia/google-cloud-sdk/
が作成された。
シェルのリロード
gcloud init
kubectl のインストール
$ gcloud components update kubectl
$ which kubectl
/Users/awakia/google-cloud-sdk/bin/kubectl
プロジェクトの作成
から新しいプロジェクトを作成してください。
できたら、とりあえず作ったProject名を今のシェルの環境変数に入れておきましょう。
export PROJECT_ID=kube_test_awakia
基本設定
デフォルトで使う project と compute/zone をセットしておきます。
$ gcloud config set project ${PROJECT_ID}
$ gcloud config set compute/zone asia-east1-a
あと、以下のページでEnable the Container Engine API
をクリックしておきましょう。
クラスタの作成
$ gcloud container clusters create cluster-1 \
--num-nodes 2 \
--machine-type g1-small
Creating cluster cluster-1...done.
Created [https://container.googleapis.com/v1/projects/kube-test-awakia/zones/asia-east1-a/clusters/cluster-1].
kubeconfig entry generated for cluster-1.
NAME ZONE MASTER_VERSION MASTER_IP MACHINE_TYPE NUM_NODES STATUS
cluster-1 asia-east1-a 1.0.7 104.199.138.109 g1-small 2 RUNNING
ちなみに本番は、n1-standard-2
とか以上のクラスで立てたほうが良いと思う。
$ gcloud compute instances list
NAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS
gke-cluster-1-a24b740a-node-e13o asia-east1-a g1-small 10.240.0.6 104.155.204.88 RUNNING
gke-cluster-1-a24b740a-node-zyqw asia-east1-a g1-small 10.240.0.5 104.199.138.219 RUNNING
コンテナの作成
Goのサーバーコンテナの作成
https://github.com/awakia/go_server をcloneしてきてください
git clone git@github.com:awakia/go_server.git
ローカルで動くか確認
$ go build
$ ./go_server
[negroni] listening on :8080
で http://localhost:8080/ でアクセス。
Markedown Editorが動けば良い。
重要なのはバイナリ自体とpublic/
以下のディレクトリ
Dockerで動くか確認
READMEみて、Docker上で動くか確認する
GKE上で動かす
Dockerイメージの作成
$ docker build -t gcr.io/${PROJECT_ID}/docker_go .
Sending build context to Docker daemon 17.62 MB
Step 1 : FROM gliderlabs/alpine:3.2
---> 87f9f1ccfaf3
Step 2 : ENTRYPOINT /bin/go_server_linux_amd64
---> Using cache
---> 972b5333e994
Step 3 : COPY bin/go_server_linux_amd64 /bin
---> Using cache
---> 67716fb04088
Step 4 : COPY public /public
---> Using cache
---> 0d6af2a3485a
Successfully built 0d6af2a3485a
Google Container RegistryにPush
Googleのプライベートレジストリに配布。ここに配布しておくことで各ノードからのpullが速くなるメリットがある。
$ gcloud docker push gcr.io/${PROJECT_ID}/docker_go
The push refers to a repository [gcr.io/kube_test_awakia/docker_go] (len: 1)
0d6af2a3485a: Pushed
67716fb04088: Pushed
972b5333e994: Pushed
87f9f1ccfaf3: Pushed
latest: digest: sha256:727c66eb1304ae533c3d818ed03614b5bfc6c7ef7ac0e8c676b2511ff91544e9 size: 6534
GKEに配置
go-node
という名前のノード(Replication Controller)を作る。これはURIになるのでハイフンはOKだけどアンダースコアはダメ
$ kubectl run go-node --image=gcr.io/${PROJECT_ID}/docker_go --port=8080
CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS
go-node go-node gcr.io/kube_test_awakia/docker_go run=go-node 1
サービスを作る
$ kubectl expose rc go-node --create-external-load-balancer=true
NAME LABELS SELECTOR IP(S) PORT(S)
go-node run=go-node run=go-node 8080/TCP
すぐに以下のコマンドを打つと
$ kubectl get services go-node
NAME LABELS SELECTOR IP(S) PORT(S)
go-node run=go-node run=go-node 10.123.241.212 8080/TCP
Internal IPのみできている。
しばらく待つと、
$ kubectl get services go-node
NAME LABELS SELECTOR IP(S) PORT(S)
go-node run=go-node run=go-node 10.123.241.212 8080/TCP
104.155.216.106
IPが増えていて、この下に出てきた104.155.216.106
がExternal IP (前のはInternal IP)
http://104.155.216.106:8080 でアクセスできるはず。
うまくいかなかった時などの、現状確認の方法
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
example-1egud 1/1 Running 0 22m
go-node-gqwz0 0/1 Error: image kube_test_awakia/docker_go:latest not found 0 5m
こんな感じでエラーが出て何が原因歌がわかる
削除する場合
kubectl expose rc go-node
で作られたReplication Controllerの削除
$ kubectl delete rc go-node
replicationcontrollers/go-node
--create-external-load-balancer=true
でできたServiceの削除
$ kubectl delete services go-node
services/go-node
コンテナ数を変えてみる
$ kubectl scale --replicas=3 replicationcontrollers go-node
scaled
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE NODE
go-node-55kvz 1/1 Running 0 11s gke-cluster-1-2a299914-node-o9gn
go-node-skf1x 1/1 Running 0 11s gke-cluster-1-2a299914-node-g5bx
go-node-tn1dk 1/1 Running 0 11m gke-cluster-1-2a299914-node-o9gn
マシン数を変えてみる
$ gcloud container clusters describe cluster-1 --format yaml | grep -A 1 instanceGroupUrls
instanceGroupUrls:
- https://www.googleapis.com/replicapool/v1beta2/projects/kube-test-awakia/zones/asia-east1-a/instanceGroupManagers/gke-cluster-1-2a299914-group
このinstanceGroupUrls
の最後の部分をコピーして
$ gcloud compute instance-groups managed resize gke-cluster-1-2a299914-group --size 1
Updated [https://www.googleapis.com/compute/v1/projects/kube-test-awakia/zones/asia-east1-a/instanceGroupManagers/gke-cluster-1-2a299914-group].
---
baseInstanceName: gke-cluster-1-2a299914-node
creationTimestamp: '2015-11-10T04:12:26.683-08:00'
currentActions:
abandoning: 0
creating: 0
deleting: 1
none: 1
recreating: 0
refreshing: 0
restarting: 0
fingerprint: 42WmSpB8rSM=
id: '3026761127639671237'
instanceGroup: gke-cluster-1-2a299914-group
instanceTemplate: gke-cluster-1-2a299914-1-0-7
kind: compute#instanceGroupManager
name: gke-cluster-1-2a299914-group
selfLink: https://www.googleapis.com/compute/v1/projects/kube-test-awakia/zones/asia-east1-a/instanceGroupManagers/gke-cluster-1-2a299914-group
targetSize: 1
zone: asia-east1-a
これで、サイズが 2 -> 1 に
自動でコンテナが3つに復活
Before
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE NODE
go-node-55kvz 1/1 Running 0 6m gke-cluster-1-2a299914-node-o9gn
go-node-skf1x 1/1 Running 0 6m gke-cluster-1-2a299914-node-g5bx
go-node-tn1dk 1/1 Running 0 18m gke-cluster-1-2a299914-node-o9gn
After
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE NODE
go-node-55kvz 1/1 Running 0 6m gke-cluster-1-2a299914-node-o9gn
go-node-k1aym 1/1 Running 0 11s gke-cluster-1-2a299914-node-o9gn
go-node-tn1dk 1/1 Running 0 18m gke-cluster-1-2a299914-node-o9gn
真ん中のが死んだマシンにいたので復活していることがわかる
やり残し
- Pod内に複数のコンテナを書いて連携するパターンの検証
- 複数のPodを同時に立ててうまくリソースを配分できるのか検証
- 起動が遅めの大きいアプリケーションの配置の検証
特に参考にした文献
- https://cloud.google.com/container-engine/docs/
- https://cloud.google.com/sdk/gcloud/reference/container/
おまけ: 調査の時に使うコマンドなど
Podの作成
$ kubectl run NAME
--image=image
[--port=port]
[--replicas=replicas]
[--labels=key=value,key=value,...]
$ kubectl run example --image=nginx
CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS
example example nginx run=example 1
状態の確認
rc
はReplication Controller
$ kubectl get rc
CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS
example example nginx run=example 1
pod
のlistは以下で見れる
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
example-u55u9 1/1 Running 0 5m
-o wide をつけると NODE 名までわかる
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE NODE
example-u55u9 1/1 Running 0 6m gke-cluster-1-27d01a3c-node-a6jr
NODE 名がわかると
$ gcloud compute ssh gke-cluster-1-27d01a3c-node-a6jr
という風にsshで入ることが出来る
個別のPod
$ kubectl get pod example-u55u9
NAME READY STATUS RESTARTS AGE
example-u55u9 1/1 Running 0 5m
詳しくdescribe
$ kubectl describe pod example-u55u9
Name: example-u55u9
Namespace: default
Image(s): nginx
Node: gke-cluster-1-27d01a3c-node-a6jr/10.240.0.4
Labels: run=example
Status: Running
Reason:
Message:
IP: 10.120.2.3
Replication Controllers: example (1/1 replicas created)
Containers:
example:
Image: nginx
Limits:
cpu: 100m
State: Running
Started: Tue, 10 Nov 2015 16:58:11 +0900
Ready: True
Restart Count: 0
Conditions:
Type Status
Ready True
Events:
FirstSeen LastSeen Count From SubobjectPath Reason Message
Tue, 10 Nov 2015 16:57:59 +0900 Tue, 10 Nov 2015 16:57:59 +0900 1 {scheduler } scheduled Successfully assigned example-u55u9 to gke-cluster-1-27d01a3c-node-a6jr
Tue, 10 Nov 2015 16:57:59 +0900 Tue, 10 Nov 2015 16:57:59 +0900 1 {kubelet gke-cluster-1-27d01a3c-node-a6jr} implicitly required container POD pulled Pod container image "gcr.io/google_containers/pause:0.8.0" already present on machine
Tue, 10 Nov 2015 16:57:59 +0900 Tue, 10 Nov 2015 16:57:59 +0900 1 {kubelet gke-cluster-1-27d01a3c-node-a6jr} implicitly required container POD created Created with docker id 534d9732c647
Tue, 10 Nov 2015 16:57:59 +0900 Tue, 10 Nov 2015 16:57:59 +0900 1 {kubelet gke-cluster-1-27d01a3c-node-a6jr} implicitly required container POD started Started with docker id 534d9732c647
Tue, 10 Nov 2015 16:58:10 +0900 Tue, 10 Nov 2015 16:58:10 +0900 1 {kubelet gke-cluster-1-27d01a3c-node-a6jr} spec.containers{example} pulled Successfully pulled image "nginx"
Tue, 10 Nov 2015 16:58:10 +0900 Tue, 10 Nov 2015 16:58:10 +0900 1 {kubelet gke-cluster-1-27d01a3c-node-a6jr} spec.containers{example} created Created with docker id 69b4fbb58d20
Tue, 10 Nov 2015 16:58:11 +0900 Tue, 10 Nov 2015 16:58:11 +0900 1 {kubelet gke-cluster-1-27d01a3c-node-a6jr} spec.containers{example} started Started with docker id 69b4fbb58d20