Edited at

Google Container Engine で Webサーバ立ててScale、Fail Overさせてみる

More than 3 years have passed since last update.

※ Wantedly社内のインフラチームで行われた Kubernetes 速習会の資料です。簡単なWebサーバーを立てて、マシンとコンテナをスケールしたり落としてみたりする実験をハンズオンで行いました。

コマンドの実行結果もだいたい載せているのでGKEとかKubernetes触りたいなと思った時に参考にしてみてください。


事前情報

https://cloud.google.com/container-engine/

Screen Shot 2015-11-10 at 10.28.53 PM.png

この中で実は、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 のインストール

https://cloud.google.com/sdk/#Quick_Start

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


プロジェクトの作成

https://console.developers.google.com/project

から新しいプロジェクトを作成してください。

できたら、とりあえず作った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をクリックしておきましょう。

https://cloud.google.com/container-engine/docs/before-you-begin


クラスタの作成

$ 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


マシン数を変えてみる

https://cloud.google.com/container-engine/docs/resize-cluster

$ 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/pods/single-container


Podの作成


kubectrl_run

$ 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