2017-11-24

GKEでPython WSGIアプリケーションをクラスタ化してみる

Google Cloud Platform (GCP) の無料クレジットがまだ余っているものの、有効期限が迫っているので Google Kubernetes/Container Engine (GKE) で遊んでみる。

実使用ではyamlファイルを作成してInfrastructure as Codeまで行うのだろうけど、 今回はチュートリアルのDeploying a containerized web application に倣って、全てコマンドラインオプションで指定する。

アプリケーションはnginxとwsgiコンテナをシングルイメージに詰め込んだものを使用。 Dockerfileはこんな感じ。

FROM tiangolo/uwsgi-nginx:python2.7

COPY ./docker/nginx.conf /etc/nginx/conf.d/default.conf

# install MeCab
RUN apt update && apt install -y mecab mecab-utils mecab-ipadic-utf8 libmecab-dev

# install app
COPY ./app /app
COPY ./docker/uwsgi.ini /app/uwsgi.ini
WORKDIR /app
RUN pip install -r requirements.txt

gcloudコマンドラインツールを設定

毎回指定するのは面倒なので、デフォルト設定をしておく。

$ gcloud config set project summarizer-165702
$ gcloud config set zone us-central1-a

dockerコンテナイメージを作成する

ここらはいつものdockerそのまま。バージョンごとにイメージを作ることになるのでタグも付ける。

$ docker build -t gcr.io/summarizer-165702/summarizer-app:v1 .

イメージができているか確認。

$ docker images

ローカルでちゃんと動くかテスト。

$ docker run --rm -p 8888:80 gcr.io/summarizer-165702/summarizer-app:v1
$ curl http://localhost:8888

コンテナイメージをアップロード

gcloudコマンドを使ってイメージをContainer Registryへアップロードする。

$ gcloud docker -- push gcr.io/summarizer-165702/summarizer-app:v1

コンテナクラスターを作成する

次のコマンドでコンテナクラスターを作成する。 ノード数は3、マシンタイプはg1-small(安いので)にした。インスタンスを3つたちあげるのでしばらく時間が掛かる。 OSはデフォルトのGoogle Container-Optimized OS (cos)。

$ gcloud container clusters create summarizer-cluster --num-nodes=3 --machine-type=g1-small

Creating cluster summarizer-cluster...done.                                                                            
Created [https://container.googleapis.com/v1/projects/summarizer-165702/zones/us-central1-a/clusters/summarizer-cluster].
kubeconfig entry generated for summarizer-cluster.
NAME                ZONE           MASTER_VERSION  MASTER_IP       MACHINE_TYPE  NODE_VERSION  NUM_NODES  STATUS
summarizer-cluster  us-central1-a  1.7.8-gke.0     35.188.186.176  g1-small      1.7.8-gke.0   3          RUNNING

3つのg1-smallインスタンスができた。

$ gcloud compute instances list
NAME                                               ZONE           MACHINE_TYPE  PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP      STATUS
gke-summarizer-cluster-default-pool-2ca45fdf-6s8h  us-central1-a  g1-small                   10.128.0.2   35.188.139.14    RUNNING
gke-summarizer-cluster-default-pool-2ca45fdf-8xxk  us-central1-a  g1-small                   10.128.0.3   104.197.177.170  RUNNING
gke-summarizer-cluster-default-pool-2ca45fdf-hxns  us-central1-a  g1-small                   10.128.0.4   35.202.183.30    RUNNING

アプリケーションをデプロイする

Container Registryへアップロードしたイメージを使用してアプリケーションをデプロイする。

$ kubectl run summarizer-app --image=gcr.io/summarizer-165702/summarizer-app:v1 --port 80
deployment "summarizer-app" created

作成されたPodを確認。Pod数は後で増やすことにする。
Podとはコンテナをグループ化したもの。Kubernetes (k8s)ではコンテナをPod単位で扱う。

$ kubectl get pods
NAME                              READY     STATUS    RESTARTS   AGE
summarizer-app-3299800503-bh28p   1/1       Running   0          2m

インターネットへ公開する

作成したDeploymentへロードバランサーを追加して外部へ公開する。
Deploymentとはデプロイ管理を行うもの。DeploymentがReplicaSetを管理し、ReplicaSetがPodを管理する。

$ kubectl expose deployment summarizer-app --type=LoadBalancer --port 80 --target-port 80
service "summarizer-app" exposed

サービス状況を確認。

$ kubectl get service
NAME             CLUSTER-IP      EXTERNAL-IP      PORT(S)        AGE
kubernetes       10.31.240.1     <none>           443/TCP        10m
summarizer-app   10.31.241.104   130.211.148.49   80:31948/TCP   2m

EXTERNAL-IPが割り当てられるとブラウザでアクセス出来るようになる。

アプリケーションをスケールアップさせる

podを3つに増やしてスケールアップさせてみる。

$ kubectl scale deployment summarizer-app --replicas=3
deployment "summarizer-app" scaled

$ kubectl get deployment summarizer-app
NAME             DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
summarizer-app   3         3         3            1           12m

$ kubectl get pods
NAME                              READY     STATUS              RESTARTS   AGE
summarizer-app-3299800503-bh28p   1/1       Running             0          12m
summarizer-app-3299800503-wqwg5   0/1       ContainerCreating   0          1m
summarizer-app-3299800503-zprpx   0/1       ContainerCreating   0          1m

新バージョンのアプリケーションをデプロイ

アプリケーションの更新手順を確認する。

ソースコードを修正、その後、更新版のイメージをビルドしてContainer Registryへアップロード。

$ docker build -t gcr.io/summarizer-165702/summarizer-app:v2 .
$ gcloud docker -- push gcr.io/summarizer-165702/summarizer-app:v2

v2イメージを展開。

$ kubectl set image deployment/summarizer-app summarizer-app=gcr.io/summarizer-165702/summarizer-app:v2
deployment "summarizer-app" image updated

Container Registryのビルドトリガー機能を使えば、イメージ生成とアップロードは楽できそう。

後始末

しばらく動かしておくつもりだけれども、削除方法も見ておく。

サービスを削除。

$ kubectl delete service summarizer-app

ロードバランサーは非同期で削除される。進捗状況は次のコマンドで確認。

$ gcloud compute forwarding-rules list

コンテナクラスターを削除。

$ gcloud container clusters delete summarizer-app

クラスターサイズを減らしてみる

クラスターサイズを変更するとどうなるか試してみる。ノード数は奇数でしょということで1まで減らす。

$ gcloud container clusters resize --size=1 summarizer-cluster
Pool [default-pool] for [summarizer-cluster] will be resized to 1.

Do you want to continue (Y/n)?  

Resizing summarizer-cluster...done.                                                                                                                                  
Updated [https://container.googleapis.com/v1/projects/summarizer-165702/zones/us-central1-a/clusters/summarizer-cluster].


$ gcloud compute instances list
NAME                                               ZONE           MACHINE_TYPE  PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP    STATUS
gke-summarizer-cluster-default-pool-2ca45fdf-hxns  us-central1-a  g1-small                   10.128.0.4   35.202.183.30  RUNNING

稼働インスタンスが1つになった。続いてDeploymentは...

$ kubectl get deployment summarizer-app
NAME             DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
summarizer-app   3         3         3            2           1h
$ kubectl get pods
NAME                             READY     STATUS    RESTARTS   AGE
summarizer-app-809326932-9zs62   0/1       Pending   0          2m
summarizer-app-809326932-bvvbt   1/1       Running   0          2m
summarizer-app-809326932-h1t9l   1/1       Running   0          7m

PODを再配備している模様。これは面白い。

更にPODを減らしてみる

kubectl scale deployment summarizer-app --replicas=1
deployment "summarizer-app" scaled
$ kubectl get pods
NAME                             READY     STATUS    RESTARTS   AGE
summarizer-app-809326932-h1t9l   1/1       Running   0          30m

減ったへった。

総括

Kubernetesは素晴らしいものです。