経緯
Kubernetesで実践するクラウドネイティブDevOpsという本を買ったので、実際に手を動かして学んでいきたいと思います。
コンテナとかKubernetesを全然知らないわけではないのですが、雰囲気でやってる部分が多いので再確認する意味でもしっかりやっていきたいと思います。
今回はKuberneteオブジェクトの概念を理解する4章です。
Deployment
本番でコンテナを使うにはLinux(Unix)でいうsystemd、runit、supervisordのようにコンテナの状態をスーパーバイズ(監督)し、停止していた場合は起動するするような機能が必要です。
KubernetesではDeploymentがその役割を務めます。
Deploymentオブジェクトには、コンテナイメージの名前や実行したいレプリカの数をはじめ、コンテナを起動するために知っておく必要のある情報がすべて格納されます。
Deploymentリソースと連携して機能するのがコントローラです。コントローラは担当するリソースを監視して、それらのリソースが存在し機能していることを確認します。コントローラは望ましい状態を保証するために、Deploymentが定義された数のレプリカを実行していない場合はレプリカの数を増減させる役割を担っています。(実際にはReplicasetと呼ばれる関連オブジェクトが自動作成され、これがレプリカの管理をします。)
Deploymentは基本的に以下のような挙動をします
- コンテナが仕事を完了して終了した場合、Deploymentはそのコンテナを再起動する
- コンテナがクラッシュ、ユーザがシグナルを送って終了する、kubectlで終了してもDeploymentはそのコンテナを再起動する
上記はデフォルトでの挙動で設定変更することが可能だが、高信頼性の確保のために上記のような挙動がデフォルトになっています。
Deploymentの仕事は関連づいているコンテナを監視して、指定された数のレプリカが常に実行されている状態を確保することです。少なければ追加で起動し多ければ一部を終了させます。
以下のコマンドで現在のNamespaceで有効になっているすべてのDeploymentを確認できます
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
demo 1/1 1 1 19d
Deploymentの詳細を確認したい場合は次のコマンド
$ kubectl describe deployments/demo
Name: demo
Namespace: default
CreationTimestamp: Mon, 24 Feb 2020 13:19:13 +0900
Labels: app=demo
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=demo
Replicas: 1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=demo
Containers:
demo:
Image: <hoge>/myhello
Port: 8888/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: demo-68b4fdb89b (1/1 replicas created)
Events: <none>
Pod Template
フィールドにPodの定義があるのですが、まずはPodについて確認しましょう。
Pod
Podとは1つ以上のコンテナのグループを表現するKubernetesオブジェクトです。
Deploymentは個別のコンテナではなく、Pod単位で管理します。
Podの仕様にはContainersというリストがあり、先の例ではdemoに関する記述があります。
demo:
Image: <hoge>/myhello
Port: 8888/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
ImageスペックとPort番号が、DeploymentがPodを起動して実行し続けるために必要な情報です。
kubectl runコマンドはPodを直接作成したわけではなく、Deploymentを作成しました。
これを受けてDeploymentがPodを作成したのです。
つまりDeploymentとは「myhelloというコンテナが内部にあるPodを1つ実行し続ける必要がある」という望ましい状態の宣言です。
Replicaset
Deploymentが直接Podを管理するのではなく、実際にはReplicasetオブジェクトが行っています。
Replicasetが担当するのは同一のPod、レプリカのグループを管理することです。実行されているPodの数がスペックと比較して差分がある場合は、状況を是正するためにReplicasetコントローラが必要な数のPodを起動(または終了)します。
次にDeploymentがReplicasetを管理して、ユーザがレプリカを更新する際の挙動をコントロールします。
例えば、アプリケーションの新バージョンをデプロイするときに、ユーザがDeploymentを更新する際には、新しいReplicasetが作成されて新しいPodを管理し、更新が完了した時点で古いReplicasetとそのPodは終了されます。
望ましい状態の維持
Kubernetesのコントローラは、個々のリソースによって指定された望ましい状態に照らし合わせて継続的にチェックしています。これは永続的にループすることから、調整ループ(reconcliation loop)と呼ばれます。
Podを手動で停止して確かめてみましょう。
まずはPodの起動を確認します。
$ kubectl get pods --selector app=demo
NAME READY STATUS RESTARTS AGE
demo-68b4fdb89b-r8fj4 1/1 Running 0 17s
Podを終了させます。
$ kubectl delete pods --selector app=demo
pod "demo-68b4fdb89b-r8fj4" deleted
この時にPodをListを表示させると、Terminating
のコンテナと ContainerCreating
のコンテナがあることが確認できます。
$ kubectl get pods --selector app=demo
NAME READY STATUS RESTARTS AGE
demo-68b4fdb89b-r8fj4 0/1 Terminating 0 81s
demo-68b4fdb89b-sj8gm 0/1 ContainerCreating 0 3s
最初と同じようにPodが1つの状態に戻っていますが、名前が変わっています。
$ kubectl get pods --selector app=demo
NAME READY STATUS RESTARTS AGE
demo-68b4fdb89b-sj8gm 1/1 Running 0 19s
このように、ユーザが消した場合でもPodの状態を望ましい状態に保とうとします。
全てを消す場合は以下のコマンドでDeploymentを停止します。
$ kubectl delete all --selector app=demo
pod "demo-68b4fdb89b-sj8gm" deleted
deployment.apps "demo" deleted
replicaset.apps "demo-68b4fdb89b" deleted
Kubernetesスケジューラ
Kubernetesスケジューラの仕事は、スケジューリングされていないPodのキューを監視し、そこから次のPodを取り出して、実行場所となるノードを見つけることです。
Podがノードにスケジューリングされると、そのノードで実行されているkubeletが対応してPodに含まれるコンテナを実際に起動します。
YAML形式のリソースマニフェスト
Kubernetesは本質的に宣言的なシステムなので、望ましい状態に照らして現在の状態を調整します。
具体的にはリソースのマニフェストファイルをユーザが変更し、Kubernetesに読み取るように指示をすれば、あとはKubernetes任せに出来ます。
以下のようなDeploymentマニフェストを実行してみましょう。
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo
labels:
app: demo
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- name: demo
image: cloudnatived/demo:hello
ports:
- containerPort: 8888
kubectl apply
コマンドでYAMLマニフェストをクラスタに送信します。
$ kubectl apply -f k8s/deployment.yaml
deployment.apps/demo created
Podの作成を確認します。
$ kubectl get pods --selector app=demo
NAME READY STATUS RESTARTS AGE
demo-7cbf698c44-mcv76 1/1 Running 0 20s
このdemo PodのWebブラウザでアクセスするには、Serviceというリソースが必要です。
Serviceリソース
ServiceはロードバランサやRプロキシのように振舞い、バックエンドにあるPodグループにリクエストを転送します。
HTTPなどのWebだけではなく任意のPortから任意のPortに転送できます。
今回のデモで使うServiceのマニフェストを確認してみましょう
apiVersion: v1
kind: Service
metadata:
name: demo
labels:
app: demo
spec:
ports:
- port: 9999
protocol: TCP
targetPort: 8888
selector:
app: demo
type: ClusterIP
これは 9999/tcp
に来たトラフィックを 8888/tcp
に転送しています。
selector
で指定した app: demo
ラベルを持つPodに転送します。複数ある場合は分散して転送します。
ここで重要なのは、ユーザのアプリケーションのためのPodのセットを管理するのがDeploymentであり、こうしたPodに対するリクエストをルーティングするために単一のエントリポイントを提供するのがServiceであるという事です。
Helm
Kubernetes用のパッケージマネージャとして人気が高いのがHelmです。
Helmコマンドラインツールを使って、アプリケーションをインストールおよび設定できます。また、Helmチャートと呼ばれるパッケージを作成できます。
YumやAPTなどに近いと思われるかもしれませんが異なる点があります。YumやAPTはバイナリ形式のファイルをダウンロードor作成しますが、Helmにはコンテナイメージが含まれません。KubernetesのDeploymentと同じように、イメージを発見できる場所に関するメタデータが含まれるだけです。
実のところ、HelmチャートはKubernetesのYAMLマニフェストを包み込むラッパのようなものです。
Helmのインストール
公式手順(https://v2.helm.sh/docs/using_helm/#installing-helm)に従ってインストールします。
環境はUbuntu18.04です。
helmは2系と3系で動きが異なり、この本は2系を使っているので2系をインストールします。
$ wget https://get.helm.sh/helm-v2.16.6-linux-amd64.tar.gz
$ tar xvzf helm-v2.16.6-linux-amd64.tar.gz
$ sudo mv linux-amd64/helm /usr/local/bin/helm
$ helm version
Client: &version.Version{SemVer:"v2.16.6", GitCommit:"dd2e5695da88625b190e6b22e9542550ab503a47", GitTreeState:"clean"}
Error: could not find tiller
無事入りました。クラスタにアクセスする権限をHelmに付与するためのKubernetesリソースを作成する必要があります。
demoにそのためのYAMLがあるので適用しましょう。
$ cd demo/
$ cd hello-helm/
$ kubectl apply -f helm-auth.yaml
次にHelmの初期化です。
$ helm init --service-account tiller
Creating /home/ryo/.helm
Creating /home/ryo/.helm/repository
Creating /home/ryo/.helm/repository/cache
Creating /home/ryo/.helm/repository/local
Creating /home/ryo/.helm/plugins
Creating /home/ryo/.helm/starters
Creating /home/ryo/.helm/cache/archive
Creating /home/ryo/.helm/repository/repositories.yaml
Adding stable repo with URL: https://kubernetes-charts.storage.googleapis.com
Adding local repo with URL: http://127.0.0.1:8879/charts
$HELM_HOME has been configured at /home/ryo/.helm.
Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster.
Please note: by default, Tiller is deployed with an insecure 'allow unauthenticated users' policy.
To prevent this, run `helm init` with the --tiller-tls-verify flag.
For more information on securing your installation see: https://v2.helm.sh/docs/securing_installation/
helm version
コマンドで以下のように表示されれば準備完了です。
$ helm version
Client: &version.Version{SemVer:"v2.16.6", GitCommit:"dd2e5695da88625b190e6b22e9542550ab503a47", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.16.6", GitCommit:"dd2e5695da88625b190e6b22e9542550ab503a47", GitTreeState:"clean"}
Helmチャートのインストール
demo/hello-helm/k8s/demo
ディレクトリには以下のようなHelmチャートがあります。
$ ls demo/hello-helm/k8s/demo
Chart.yaml production-values.yaml staging-values.yaml templates values.yaml
とりあえず動かしてみましょう。
$ helm install --name demo demo/hello-helm/k8s/demo
NAME: demo
LAST DEPLOYED: Sat Apr 18 16:13:23 2020
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/Deployment
NAME READY UP-TO-DATE AVAILABLE AGE
demo 0/1 1 0 0s
==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
demo-57457fb5fb-2z8ct 0/1 ContainerCreating 0 0s
==> v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
demo-service LoadBalancer 10.105.38.26 <pending> 80:32261/TCP 0s
実行されているリリースは以下のコマンドで確認できます。
$ helm list
NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
demo 1 Sat Apr 18 16:13:23 2020 DEPLOYED demo-1.0.1 default
Helmで重要な用語
以下の用語を理解しておく必要があります
- チャートとは、アプリケーションをKubernetesで実行するために必要なリソースの定義がすべて格納されるHelmパッケージです
- リポジトリとは、チャートを収集及び共有できる場所です
- リリースとは、Kubernetesクラスタで実行されるチャートの特定のインスタンスです
まとめ
- PodはKubernetesリーソースの一種で、Kubernetesにおける仕事の最小単位であり、単一のコンテナまたは組み合わせで同じノードにスケジューリングされ相互に通信するコンテナのグループです
- Deploymentは高水準のKubernetesリソースであり、Podを宣言的に管理し、必要に応じてPodをデプロイ、スケジューリング、更新、起動されます
- ServiceはKubernetesリソースの一種で、ロードバランサやプロキシに相当する機能を提供し、単一かつ周知の永続的なIPアドレスまたはDNS名を通じて、条件に一致するPodのグループにトラフィックをルーティングします
- Kubernetesスケジューラは、どのノードでもまだ実行されていないPodを監視し、それに適したノードを見つけてPodの実行をノードのKubeletに指示します
- HelmはKubernetes用のパッケージマネージャです。Kubernetesアプリケーションの設定とデプロイを簡素化し、生のYAMLファイルをユーザが保守しなくても、値の単一のセット(アプリケーションや待ち受けるポートなど)やテンプレートのセットを用いてKubernetes用のYAMLファイルを生成できるようにします。