Kubernetesはどこのパブリッククラウドでも動くようになってるしすごいですね。
ただ、今回は手持ちのMacで動かしてみたいと思います。Docker Desktopはインストールされている前提です。
Mac:KubernetesをONにする。
Docker DesktopアプリのPreferenceから、Kubernetesを有効にしてください。起動には結構時間がかかりますので次のステップを進めておきましょう。
サンプルのダウンロード
VSCodeの拡張機能でサクッとGKEにデプロイできるCloud Codeというのがあります。
これのサンプルでcloud-code-samplesをダウンロードします。
デスクトップなどでもどこでもいいです。そしてnodejsのサンプルにはいってください。
cd ~/Desktop
git clone https://github.com/GoogleCloudPlatform/cloud-code-samples.git
cd cloud-code-samples/nodejs/nodejs-hello-world/
ファイルはこんな感じになってるはずです。
.
├── Dockerfile
├── README.md
├── img
│ └── diagram.png
├── kubernetes-manifests
│ ├── hello.deployment.yaml
│ └── hello.service.yaml
├── package-lock.json
├── package.json
├── skaffold.yaml
└── src
└── app.js
ここのREADMEにもあるようにGKEをつかうならコマンド一発でできるそうです。
skaffold run --default-repo=gcr.io/your-project-id-here/cloudcode
ローカルで頑張るには少し作業が必要です。skaffoldの設定でもできるのかもしれませんがよくわからないので使いません(教えてくださいw)
app.jsを少し変更する
あとでわかりやすいようにapp.jsを少し変えます。一ヶ所だけです。
const express = require('express');
const app = express();
// returns a simple respnse
app.get('/', (req, res) => {
console.log(`received request: ${req.method} ${req.url}`);
res.status(200).send(`Hello, world! - ${process.env.HOSTNAME}`); // ここを変更
});
// starts an http server on the $PORT environment variable
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`App listening on port ${PORT}`);
console.log('Press Ctrl+C to quit.');
});
module.exports = app
Dockerfileをビルドしてイメージを作成する
直下のDockerfileをビルドします。
イメージ名はnode-hello-worldです。
docker build -t node-hello-world:latest .
これでイメージもできました。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
node-hello-world latest f1204ec69435 6 seconds ago 921MB
マニフェストファイルを変更
Dockerだけならこの段階で動かせるぜ!となりますが、サンプルプロジェクトのKubernetesマニフェストはすこし変更が必要です。
ついでに稼働するPod数も1つではつまらないので3つにしてみます。
kubernetes-manifests/hello.deployment.yaml を変更します。
replicasを3にするのと、imagePullPolicy: Neverというのを追加します。
# This Deployment manifest defines:
# - single-replica deployment of the container image, with label "app: node-hello-world"
# - Pod exposes port 8080
# - specify PORT environment variable to the container process
# Syntax reference https://kubernetes.io/docs/concepts/configuration/overview/
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-hello-world
spec:
### ↓ 3に増やす ###
replicas: 3
selector:
matchLabels:
app: node-hello-world
template:
metadata:
labels:
app: node-hello-world
spec:
containers:
- name: server
image: node-hello-world
### ↓ これを追加 ###
imagePullPolicy: Never
ports:
- containerPort: 8080
env:
- name: PORT
value: "8080"
imagePullPolicy:とは、イメージをどこから取ってくるかの設定です。Neverにしとけばローカルに作成したイメージを使ってくれるとのことです。
By default, the kubelet will try to pull each image from the specified registry. However, if the imagePullPolicy property of the container is set to IfNotPresent or Never, then a local image is used (preferentially or exclusively, respectively).
実行!
いよいよ実行してみます。kubernetes-manifests以下の2つのマニフェストファイルを使いますので下記コマンドのようになります。
kubectl apply -f kubernetes-manifests
こんな表示がでます。
deployment.apps/node-hello-world configured
service/node-hello-world-external unchanged
次に状態を見てみましょう。
kubectl get pods,services
起動中だとステータス違いますがしばらく立ってうまく起動してるとこんな表示になるはずです。
NAME READY STATUS RESTARTS AGE
pod/node-hello-world-5f6c8599c-758wv 1/1 Running 0 13s
pod/node-hello-world-5f6c8599c-q4jqp 1/1 Running 0 13s
pod/node-hello-world-5f6c8599c-xfdl7 1/1 Running 0 13s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 156m
service/node-hello-world-external LoadBalancer 10.108.217.246 localhost 80:30063/TCP 126m
LoadBalancerとしてnode-hello-world-externalが動いていて、そこにぶら下がるPodが3つというわけですね。
動作確認
curlで見てみます。
curl http://localhost/
こんな結果が返ってきました。なるほど、HOST_NAMEがPodの名前になっていますね。
Hello, world! - node-hello-world-5f6c8599c-xfdl7
3つのPodが動いているはずなので他のPodからのレスポンスは返ってくるでしょうか?
sh-3.2$ for i in {1..10};do echo `curl -s http://localhost/`;done
Hello, world! - node-hello-world-5f6c8599c-xfdl7
Hello, world! - node-hello-world-5f6c8599c-xfdl7
Hello, world! - node-hello-world-5f6c8599c-758wv
Hello, world! - node-hello-world-5f6c8599c-q4jqp
Hello, world! - node-hello-world-5f6c8599c-758wv
Hello, world! - node-hello-world-5f6c8599c-q4jqp
Hello, world! - node-hello-world-5f6c8599c-758wv
Hello, world! - node-hello-world-5f6c8599c-758wv
Hello, world! - node-hello-world-5f6c8599c-xfdl7
Hello, world! - node-hello-world-5f6c8599c-758wv
お〜いい感じです。
ロードバランサはいい感じで分散してくれるのでしょうか?
sh-3.2$ for i in {1..99};do echo `curl -s http://localhost/`;done | sort | uniq -c
33 Hello, world! - node-hello-world-5f6c8599c-758wv
34 Hello, world! - node-hello-world-5f6c8599c-q4jqp
32 Hello, world! - node-hello-world-5f6c8599c-xfdl7
お〜、これまたいい感じ。
イメージを更新したらrollout
Dockerイメージを作り直した際にはrolloutしましょう。
hello.deployment.yamlを更新します。
sh-3.2$ kubectl rollout restart -f kubernetes-manifests/hello.deployment.yaml
deployment.apps/node-hello-world restarted
すかさず状態をみるとこんな感じです。
sh-3.2$ kubectl get pods,services
NAME READY STATUS RESTARTS AGE
pod/node-hello-world-58d65988bc-fqhmn 0/1 ContainerCreating 0 0s
pod/node-hello-world-58d65988bc-zdh5f 1/1 Running 0 2s
pod/node-hello-world-5f6c8599c-758wv 1/1 Running 0 49m
pod/node-hello-world-5f6c8599c-q4jqp 1/1 Terminating 0 11m
pod/node-hello-world-5f6c8599c-xfdl7 1/1 Running 0 11m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 168m
service/node-hello-world-external LoadBalancer 10.108.217.246 localhost 80:30063/TCP 137m
Podが入れ替わってますね。入れ替わってる最中もアクセスできますし、しばらくすると安定します。
終了
サービスを停止します。Podを単独で停止してもすぐ復旧しちゃうので元から止めましょう。
sh-3.2$ kubectl delete -f kubernetes-manifests/
deployment.apps "node-hello-world" deleted
service "node-hello-world-external" deleted
止まりましたかね?確認してみます。
sh-3.2$ kubectl get pods,services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 174m
大丈夫そうです。
プログラムはどうでしょう?
sh-3.2$ curl http://localhost/
curl: (7) Failed to connect to localhost port 80: Connection refused
ばっちりですね。
それにしても...
Kubernetesすごいな〜(Macが重くなるけど...)