引き続きkubernetesの使い方について学習。
前回作ったminikubeの環境で実際にyamlファイルを用いた操作を行ってみる。
最低限把握しておくこと
作業の前にザックリと用語と概念を把握。
- Node: 実際にコンテナが動いているマシンのこと。
kubectl get node
で一覧を見ることができる。 - Pod: 同一のNode上で動かすコンテナ群。スケールする場合このPod単位での操作になるので、機能を実現する最低単位で分割していくと良さそう。
- Service: それぞれのNode/Podで動作しているコンテナ同士や外部との通信を司る概念。
- Resource: PodとかServiceとかいった概念をk8sではこう呼ぶらしい。
- Manifestfile: Resourceの設定ファイルで、yamlやjsonなどの形式で記述できる。
今のところの大雑把な理解でいえば、Podがdocker-compose.yamlでいうところのservicesおよびvolumes項、Serviceがnetworks項にあたりそう。
この業界では「インスタンス上で動く何かしらの機能」を指して「サービス」と言ったりする(現にこの記事のタイトルもdocker-composeは前者の意味で使ってる)わけだけど、それと混同しないように注意したい。
PodのManifestfile
ひとまず単純に
- HTTPサーバとしてnginxを起動
- busyboxを別に起動し、そのディレクトリをマウント
といった構成を考えてみる。
pod.yaml
として以下のようなYAMLファイルを記述する。
apiVersion: v1 kind: Pod metadata: name: http labels: app: http-app spec: containers: - name: nginx image: nginx:latest ports: - name: http-port containerPort: 80 volumeMounts: - name: docroot mountPath: /usr/share/nginx/html - name: html image: busybox command: - tail - -f - /dev/null volumeMounts: - name: docroot mountPath: /opt/html lifecycle: postStart: exec: command: - /bin/sh - -c - echo "<html><body>Hello k8s!</body></html>" > /opt/html/index.html volumes: - name: docroot emptyDir: {}
kind項にResourceの種類、metadataで名前やラベルなどの操作のための付加情報、spec内に実際的な記述を書き込んでいく。 このあたりのフォーマットは他のResourceでも同様なので目を慣らしておくと良い。
spec.containers項に実際に起動すべきコンテナ関連の情報を記述する。docker-composeを扱ったことがあればなんとなくニュアンスは分かるところだが、
- ポートなりボリュームなりはそれぞれ名前を付けて、それを用いて抽象的な概念として扱っていく。
- (僕の調べ方が足りないのなければ)volume-from的な他のコンテナのボリュームをそのままマウントする記述はできなそう。今回使用したemptyDir(Pod内のみの共通ボリューム)やhostDir、その他もろもろのストレージサービスを用いる記述でもって対処する必要がある。
- lifecycleという要素で起動時(postStart)や終了時(preStop)に任意のコマンドを実行できる。便利。
- docker-composeなんかだとボリュームをマウントするだけのコンテナはずっと起動しておく必要は無いのだけど、k8s的にはコンテナが死んでしまうとエラー扱いになってしまうため、今回でいうところのbusyboxは
tail -f /dev/null
により永続化している。(もしかしたらもっと良い対処があるのかも。)
4についてはJobというResourceを使って、単に「共有のボリュームにファイルを置く」だけのバッチとして扱うのが良いかもしれない、と書き上げた後で思った。
といった部分に慣れる必要がある。
そんなこんなで、
- コンテナ起動時にhtmlファイルを作成
- それをドキュメントルートしてマウント
といった動作を実現している。
ServiceのManifestfile
先のPodはあくまでNode上で起動しているもののため、実際にWebページとして見えるようにするにはServiceという概念を用いる必要がある。
service.yaml
として以下のような記述を行った。
kind: Service apiVersion: v1 metadata: name: http-service labels: app: http-app spec: selector: app: http-app ports: - port: 8080 targetPort: http-port externalIPs: - 192.168.99.100
spec.selector項にはpodの方でラベルとして設定したものを記述。また、対象となるコンテナのポートを名前で指定する。最後のexternalIPsは・・・ひとまずminikube ip
で拾えるIPをそのまま入力したものを使ってるが、もっと良い方法があるかもしれない。
起動
以上で記述に問題がなければファイルを指定して起動する。
$ kubectl create -f pod.yaml -f service.yaml pod "http" created service "http-service" created
-lオプションでラベルを指定して状態を確認できる。
$ kubectl get po,svc -l app=http-app NAME READY STATUS RESTARTS AGE po/http 2/2 Running 0 4m NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE svc/http-service 10.0.0.48 192.168.99.100 8080/TCP 2m
ひとまずPodでちゃんとコンテナが2つ起動されてること、ServiceでIPやポートが割当てられたことがなんとなく分かるだろう。 他にもlogsコマンドやdescribeコマンドなんかもあるが今回は割愛。
動作確認
Serviceで記述したように192.168.99.100の8080ポートに接続してみると・・・
$ curl 192.168.99.100:8080 <html><body>Hello k8s!</body></html>
意図通りのレスポンスが返ってくることが確認できる。
後片付け
$ kubectl delete -f pod.yaml -f service.yaml pod "http" deleted service "http-service" deleted
以上のようにしてひとまずk8s上でコンテナを立ち上げるところまでのやり方は把握できた。
実際に触ってみるとdocker-composeとは必ずしも機能が一対一で対応しているわけではないので、なまじあちらに慣れていると最初は戸惑うところであるが、実際にサービスとして運用することを考えると合理的な設計であるように思える。
とかくラベルや名前を通して管理するという作法が徹底されていて、例えば同じselectorで選択可能なpodを増やせば簡単にスケールできるし、また逆に適切にラベルを貼ることでBlue/Greenデプロイを実現することもできるのが面白い。(このあたりは次の機会にでも触れようかと思う。)
Kubernetes Cookbook: Building Cloud Native Applications
- 作者: Sébastien Goasguen
- 出版社/メーカー: Oreilly & Associates Inc
- 発売日: 2017/07/25
- メディア: ペーパーバック
- この商品を含むブログを見る