Help us understand the problem. What is going on with this article?

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

More than 5 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
awakia
検索とか推薦とかやってきたエンジニア。早稲田の山名研出身。大学院の頃、論文を書こうとしない僕を見かねた教授に、北京のMSRAに追放されるが3ヶ月後無事帰還。 大学を卒業後、エンジニアのブラックホールとの別名を持つGoogleに吸収されそうになるが1年2ヶ月後無事生還。 現在は、Wantedly(https://www.wantedly.com/ )の4番目のエージェントとして救出活動に専念。
http://awakia-n.hatenablog.com/
wantedly
「シゴトでココロオドル」ためのビジネスSNS「Wantedly」の開発・運営をしています。
https://wantedlyinc.com/ja/presentations
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away