Kubernetes入門6: 初期化専用コンテナを起動する
一般的には、何らかのサービスを提供するアプリケーションをコンテナとして起動することが多いです。しかし、DBなどの初期化処理などを実行するコンテナを一度だけ起動したいということもあります。
その場合、KubernetesのInit Containersが要件に適しているでしょう。Configure Pod Initializationを参考に試してみました。
準備
techlunch.hatenablog.com minikubeを用意しておきましょう。
init containerを含むPodを作成する
以下のPod設定ファイルで、1つのアプリケーションコンテナと1つのinit containerを持つPodを作成します。
# https://k8s.io/examples/pods/init-containers.yaml apiVersion: v1 kind: Pod metadata: name: init-demo spec: containers: - name: nginx image: nginx ports: - containerPort: 80 volumeMounts: - name: workdir mountPath: /usr/share/nginx/html # These containers are run during pod initialization initContainers: - name: install image: busybox command: - wget - "-O" - "/work-dir/index.html" - http://kubernetes.io volumeMounts: - name: workdir mountPath: "/work-dir" dnsPolicy: Default volumes: - name: workdir emptyDir: {}
設定ファイルを見ると、init containerとアプリケーションコンテナはworkdirというボリューム
を共有していることがわかります。
init containerは、アプリケーションコンテナが起動する前に、
wget -O /work-dir/index.html http://kubernetes.io
という処理を実行し、完了します。この際にnginxサーバのルートディレクトリにファイルを書き込んでいることがわかります。
それでは実行してみます。
$ kubectl create -f https://k8s.io/examples/pods/init-containers.yaml $ kubectl get pods NAME READY STATUS RESTARTS AGE init-demo 1/1 Running 0 5m
次に、nginxによって提供されているpageがinit containerによって書き込まれたファイルであることを確認します。
$ kubectl exec -it init-demo -- /bin/bash # apt update; apt install curl -y # curl localhost ...
今回はnginxを例にしましたが、MySQLなどのDBに初期データを投入するといった使い方もできます。
コンテナとVMの関係
Dockerブログに、Are Containers Replacing Virtual Machines?という記事がポストされました。
記事ではVMとコンテナの関係や違いについて述べられています。本記事では一部抜粋して紹介します。
Point #1: Containers Are More Agile than VMs
左側がDockerを利用してコンテナをデプロイする時のアーキテクチャになります。コンテナ化されたアプリケーションはホストOSを共有していますので、アプリケーションの起動時間しかかかりません。一方、右側の図の通りVMごとにアプリケーションをデプロイする場合は、OSから起動するため多少時間がかかります。
また、一般的にアプリケーションは一つのDockerイメージとして作成し、そのイメージを元に各環境でDockerコンテナを起動します。そのため、自分のマシンでは動いたという問題が起こりにくくなります。*1
Point #3: Integrate Containers with Your Existing IT Processes
ほとんどの企業はエンタープライズ用途に必須な監視、バックアップ、プロセス自動化といったツールを含んだVM環境を備えています。コンテナは図のように、VM上でも問題なく動かすことができるので、既存の資産を有効活用しつつ徐々にアプリケーションをコンテナ化していくといったことができます。
また、コンテナとVMの両方を管理するという点では、KubeVirtが役に立つかもしれません。
結局のところ、コンテナとVMは競合するものではなく共存するものなので、統合を楽に行うツールなどが今後出てくるかもしれません。
*1:実行する環境ごとに環境情報が異なるので、実際には環境依存で動かない場合もあります。
どのようなワークロードをコンテナ化すべきか
Container JournalのDeciding Which – and How Many – Workloads to Containerizeという記事が興味深かったので、簡単に紹介します。
記事では、
No matter how popular containers become, the reality is that they are going to coexist with virtual machines and bare-metal servers at most organizations. They won’t totally replace older forms of infrastructure.
という考えのもと、どのようなワークロードをコンテナ化すべきかについて述べています。
コンテナ化すべきものとそうでないもの
どのインフラをコンテナ化すべきか考える時に、次の質問を投げかけています。
Persistent data needs.
データの永続化を必要としないstatelessなアプリケーションは、コンテナ化する第一候補となるでしょう。一方、永続化を必要とするアプリケーションのコンテナ化は複雑です。まずは、statelessなアプリケーションのコンテナ化で知見をためていくことを私はおすすめしています。
Workload isolation.
ベアメタル、コンテナ、VMでisolationのレベルは大きく異なるので、要件に従って適切な方法を取る必要があります。私の感覚では、自社が管理するマシン上で様々なアプリケーションを走らせる場合には、コンテナは適していると思います。
Scalability.
コンテナはVMと比べて起動速度も早いですし、スケーラビリティが求められるワークロードはコンテナ化に適していると言えます。
Updates.
迅速かつ継続的に更新が必要なアプリケーションの場合は、Dockerイメージとコンテナの恩恵を強く感じると思います。一方、ほとんど更新を必要としないレガシーなアプリケーションは現在の実行環境から、あえてコンテナ化しても期待する効果を得ることはできないかもしれません。
その他にも記事では、
How Much Should You Containerize?
などについて、述べていますので、興味のある方はご覧ください。
KubeVirtなどに関するPodCastが公開されました
RedHatにより提供されているPodCastにて、KubeVirtやContainer Native Virtualization (CNV) について話されています。KubeVirtは本ブログでも簡単に紹介したとおり、VMをPodと同じレベルのリソースとして管理するためのKubernetes拡張機能です。
CNVは、コンテナ前提となりつつある世の中で、既存の資産であるVM上のワークロードをどのようにして、コンテナと共存していくかということに対する概念、考え方のように思えます。それを実現するための実装としてKubeVirtなどのOSSを開発しているようです。
VMのワークロードもコンテナ化すれば良いじゃないかと思われる方もいるかもしれませんが、OS依存のアプリケーションなど問題は山積みだと思いますので、Kubernetesへの緩やかな移行を促すためにも本プロジェクトは興味深く思っています。もちろんコンテナでは満たせない要件は、今まで通りVMを利用する必要がありますので、そういった意味でも大変重要なプロジェクトだと思います。
VM資産をお持ちで、Kubernetesへの移行を検討してる方は、このPodCastも聞いて見てください。
Kubevirt: VMを管理するためのKubernetesアドオン
コンテナと同じようにVMをKubernetesで管理するためのアドオンであるKubevirtについて、Kubernetsブログで紹介されていたので、試してみました。
Kubevirtは、Custom Resource Definitions (CRDs)などを利用して, Kubernetesを拡張しており、Podと同じようにVMを管理することができます。
準備
Minikubeをインストールしてる前提となるので、上の記事などを参考にインストールします。 私の環境は、VirtualBoxを利用しており、nested virtualizationが使えないため、次のコマンドでエミュレーションを有効化します。
$ kubectl create configmap -n kube-system kubevirt-config --from-literal debug.allowEmulation=true
KubeVirt関連コンポーネントをPodとして実行します。
$ export VERSION=v0.7.0-alpha.2 $ kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/$VERSION/kubevirt.yaml
次のコマンドでPodが立ち上がっていることが確認できます。
$ kubectl get pods --namespace=kube-system NAME READY STATUS RESTARTS AGE ... virt-api-7797f95869-9pvg7 1/1 Running 0 11m virt-api-7797f95869-lhqn7 1/1 Running 0 11m virt-controller-69cc6b4897-jnvl7 1/1 Running 0 11m virt-controller-69cc6b4897-zq2qx 1/1 Running 0 11m virt-handler-fjttr 1/1 Running 0 11m
VMのデプロイ
次のコマンドを見ればわかる通り、Podと同じようにKubernetesの一リソースとして管理が可能です。
# VM作成 $ kubectl apply -f https://raw.githubusercontent.com/kubevirt/demo/master/manifests/vm.yaml # 確認 $ kubectl get vms NAME CREATED AT testvm 1m $ kubectl get vms -o yaml ...
また、本記事では割愛しますが、virtctlをインストールすることで、Kubernetesで作成したVMにアクセスしたりすることが可能です。これは、Podとして作成したコンテナにDockerコマンドを利用してアクセスするのと同じように使うことができます。
今回はKubevirtをほんの少し触った程度で終わります。しかし、今後Kubernetesの導入を考えていく上でレガシーなVM資産との共存を考えていく必要が出てくることを考慮し、別の記事で詳細に触れたいと思っています。
Kubernetes入門5: Podを特定のノードに割り当てる
Kubernetesでは、スケジューラがPodをどのノードで実行するかを判断します。PodをGPUやSSDが搭載された特定のノードで実行したい場合は、nodeSelectorなどを利用することで、実現できます。
KubernetesドキュメントのAssign Pods to Nodesを参考にしながら、試してみました。
準備
可能なら複数台のノードを用意できると良いです。Kubernetesのインストールはkubeadmを利用した方法が簡単です。
ノードにLabelを追加する
KubernetesクラスタにJoin済のノード一覧を表示します。
$ kubectl get nodes NAME STATUS ROLES AGE VERSION master Ready master 8d v1.11.2 worker1 Ready <none> 8d v1.11.2 worker2 Ready <none> 8d v1.11.2
私のクラスタ環境は1台のmaster、2台のworkerで構成されていることがわかります。
次に、worker1にdisktype=ssd
というlabelを付与します。
$ kubectl label nodes worker1 disktype=ssd $ kubectl get nodes --show-labels ...
これで、worker1にはPodを割り当てる時のヒントになるlabelが付与されました。
Podを特定のノード上で動かす
次の設定ファイルでPodを作成します。
# https://k8s.io/examples/pods/pod-nginx.yaml apiVersion: v1 kind: Pod metadata: name: nginx labels: env: test spec: containers: - name: nginx image: nginx imagePullPolicy: IfNotPresent nodeSelector: disktype: ssd
設定ファイルのnodeSelector
の項目に、disktype: ssd
という設定を記述することで、このlabelを持つノードにPodがスケジューリングされます。
Podを作成し、どのノードで実行されているか確認すると、worker1でPodが動いていることがわかります。
$ kubectl create -f https://k8s.io/examples/pods/pod-nginx.yaml $ kubectl get pods --output=wide ...
該当するlabelを持つノードが存在しない場合
Podの設定で、nodeSelectorを指定してる際に、それに該当するノードが存在しない場合の挙動はどうなるか試してみました。
まず、先ほどworker1に付与したlabelを削除します。
$ kubectl label nodes worker1 disktype-
Podを実行して、状態を確認します。
$ kubectl create -f https://k8s.io/examples/pods/pod-nginx.yaml $ kubectl describe pods nginx Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 4s (x5 over 13s) default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector.
すると、nodeSelector
にマッチするノードが存在しないということでPodがどのノードでも実行されていないことがわかります。
以上より、ノードにlabelという情報を付与することで、ユーザがPodを任意のノードに割り当てることができます。GPUやSSDを利用するアプリケーションをデプロイしたい方は、ぜひ試してみてください。
Kubernetes入門4: Liveness and Readiness Probesを使ってみる
Kubernetes上で実行されるアプリケーションコンテナの状態を確認する仕組みとして、Liveness and Readiness Probesがあります。それぞれ、
- Liveness probe
- 失敗するとコンテナを再起動する
- Readiness probe
- 失敗するとコンテナにトラフィックを流さなくなる(コンテナの再起動はしない)
という特徴があります。
今回は、Kubernetes TasksのConfigure Liveness and Readiness Probesを試してみました。
Liveness Probeの例がいくつか載っていますが、この記事では「Linuxコマンド」のみ実行しています。
Linuxコマンドで状態確認
設定ファイルは次の通りです。
# https://k8s.io/examples/pods/probe/exec-liveness.yaml apiVersion: v1 kind: Pod metadata: labels: test: liveness name: liveness-exec spec: containers: - name: liveness image: k8s.gcr.io/busybox args: - /bin/sh - -c - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600 livenessProbe: exec: command: - cat - /tmp/healthy initialDelaySeconds: 5 periodSeconds: 5
この設定ファイルでは、コンテナ一つのPodが定義されており、そのコンテナは起動後に、touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy;
というコマンドを実行しています。このコマンドでは、指定ディレクトリにファイルを作成し、30秒後に削除しています。
一方、liveness probeの設定をみると、5秒ごとに cat /tmp/healthy
コマンドを実行し、ファイルの存在確認を行っています。そのため、30秒後にはコマンドが失敗し、コンテナの再起動を試みることになるはずです。
Podを実行。
$ kubectl create -f https://k8s.io/examples/pods/probe/exec-liveness.yaml
30秒後にPodの状態確認。
$ kubectl describe pod liveness-exec Events: Type Reason Age From Message ---- ------ ---- ---- ------- <snip> Warning Unhealthy 49s (x3 over 59s) kubelet, test_node Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory Normal Killing 18s kubelet, test_node Killing container with id docker://liveness:Container failed liveness probe.. Container will be killed and recreated. <snip>
Liveness probeにより、コンテナの異常を検知し、コンテナ再起動を行っていることがわかります。
Readiness probeについても、元のドキュメントにyamlファイルが載っていますので、興味がある方はご参照ください。