helloworlds

not a noun, it's a verb

【k8s】kubernetes 入門 - #04

今回はPodについて勉強していこうと思います。

DevOpsやMLOps、SREなどの肩書を持っている方であれば仕事で使用しているので理解がある方が多いかと思いますが、

他のエンジニアはPodという単語をなんとなく知っているくらいかと思います。

kubernetesを使用していくとも、AWSGCPの公式ドキュメントではたびたび使用されているような単語ですね。

kubernetesを使用しているクラウドサービスを運用していればもちろん理解しておけなければならないでしょう。

Pod とは?

Podとは、同じ実行環境上で動くアプリケーションコンテナとストレージボリュームの集まりのことです。

Kubernetesクラスタ上でのデプロイ最小単位になります。

これだけ聞いても、馴染みにのない方にとっては、"つまりアプリケーションじゃないの?" とか "運用上どういう考え方でPodとやらをデプロイするべきなのか?" と思うこともあるでしょう。

考え方的なことは後で解説するとして、ここでは、Podが最小のデプロイ単位と覚えておくと良いでしょう。

コンテナの機能

Podについて踏み込んで行く前に、コンテナとOSとの関係をここで簡単にみていきます。

私の記事の#1で、"コンテナはオペレーションシステム(HostOS:kernel)を共有"と説明しました。

【k8s】kubernetes 入門 - #01 - helloworlds

※ ここでは主にコンテナランタイムはDockerを前提に記載します。

コンテナが機能するにあたり、以下のLinuxが持つ機能を利用しています。

  • overlayfs
  • namespaces
  • cgroups

基本的に1つのノードに複数のPod(=複数のコンテナ)がのることは容易に想像できます。

つまりコンテナは、OSカーネルが持っている環境を隔離することができるということになります。

これは、namespacesとcgroupsのOSカーネルの機能が使用されています。

(※overlayfsは割愛)

cgroups

各コンテナは、CPUやメモリといったリソースを制限なく使用できるわけではありませんね。

ここで使用されているのが、Linuxカーネルの機能であるcgroupです。

cgroupはタスクのグループ化、グループ内のタスクに様々なリソース制御を行う仕組みです。

namespaces

ネームスペースは、コンテナを1つの仮想マシンのようにみせるための機能です。

コンテナを区画化し、Linuxカーネルのネームスペースを制御します。

ネームスペース内のプロセスは他のネームスペース内のプロセスとリソースが隔離できることが重要な点です。

これにより、他のノードでも同じ環境を構築することができます。

以下のリソースが分離可能です。

  • PID: プロセスID
  • Network: ネットワークデバイス、port、ルーティングテーブル、ファイアウォールルールなど
  • Cgroup: プロセスの名前空間を分離するためルートディレクト
  • IPC: System System V IPC, POSIX message queues
  • Mount: マウントポイント
  • User/Group: ユーザーID(UID)、グループID(GID)
  • UTS: Host name、NIS domain name

参考: namespaces(7) - Linux manual page

簡単にまとめ

ここまでくると、このように想像できます。

  • Pod内のコンテナは、cgroups上で動作する
    • Linuxネームスペースを共有する
  • Pod内のアプリケーションは、
  • 別のPodは分離されている

上記のことは理解できますが、1つ運用面で疑問に思うことができてきます。

"Podに入れるコンテナはどういったものが良いのか?" です。

Podに入れるもの

例えば、AというコンテナとBというDBのコンテナがあったとします。

当然このAとBのコンテナを1つのアプリケーションとして扱う場合、一緒にいれておけばOKと考えますが、

これはアンチパターンになってしまいます。

このアプリケーションに負荷がかかった場合、Podはスケールします。

このとき、フロントエンドを担うコンテナとDBのコンテナを同時にスケールさせてしまいます。

DBの方は他の条件でスケールさせたいとすると、同じPodに入れておくには効率が悪いことになります。

設計時に考えたいのは、"このコンテナはそれぞれ違うマシンに配置されたとしても正常に動作するかどうか" ということです。

  • 動作する→ Podを分ける
  • 動作しない→ コンテナをまとめて1つのPodにする

必ずしもこの通りではないかもしれまんせんが、基本的には上記を元に設計するのが良さそうです。

(※ アプリケーションやバッチ、APIなどのそれぞれの要件によって、マシンサイズが変動することもありますが、それは違うサービスで実現可能。)

当たり前ですが、Podは違うマシン(ノード)に配置されるということも最初は見落としがちな気がします。

最初の内はマシンが違っていても、動作することを基本的な条件として取り入れておくべきだと思います。

Podの作成してみる

簡単なPodを作成してみます。

適当な場所でYAMLファイルを作成します。

apiVersion: v1
kind: Pod
metadata:
  name:  kuard
spec:
  containers:
  - image: gcr.io/kuar-demo/kuard-amd64:1
    name: kuard
    ports:
    - containerPort: 8080
      name: http
      protocol: TCP

※ minikubeで試しています笑

マニフェストを作成したら、以下のコマンドでapplyします(Podを作成)。

$ kubectl apply -f kuard-pod.yaml -n default 

-n はネームスペース指定のオプションです。

作成の確認は以下のコマンドです。

$ kubectl get pod -n default

NAME    READY   STATUS    RESTARTS   AGE
kuard   1/1     Running   0          11m

次にPodの詳細を確認してきましょう。

$ kubectl describe pod kuard -n default

たくさんの情報が出力されるので、簡単にみていきます。

まず一番上に記載されているのが当Podの情報ですね。

Name:             kuard
Namespace:        default
Priority:         0
Service Account:  default
Node:             minikube/192.168.58.6
Start Time:       Sat, 25 Mar 2023 16:58:11 +0900
Labels:           <none>
Annotations:      <none>
Status:           Running
IP:               10.244.0.5
IPs:
  IP:  10.244.0.5

次にPod内で動作しているコンテナの情報です。

Containers:
  kuard:
    Container ID:   docker://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    Image:          gcr.io/kuar-demo/kuard-amd64:1
    Image ID:       docker-pullable://gcr.io/kuar-demo/kuard-amd64@sha256:bd17153e9a3319f401acc7a27759243f37d422c06cbbf01cb3e1f54bbbfe14f4
    Port:           8080/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Sat, 25 Mar 2023 16:58:17 +0900
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-mwqxn (ro)

次にコンディションです (参考)

Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True

次にボリュームです。 当Podのボリュームのタイプなどが確認できます。

Volumes:
  kube-api-access-mwqxn:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true

続いて、QoSについて。

QoS Class:                   BestEffort

あまり馴染みがないので、簡単に記載すると、

  • BestEffort (優先度が一番低)
  • Burstable
  • Guaranteed (優先度が一番高)

上記のように値があり、Podをkillする優先度合いのことです。 (Podが複数あり、リソースを60%使用したいPodと90%使用したいPodがあった場合に、killされるPodを判別するために使用される)

今回のマニフェストでは定義していませんが、マニフェストにリソースを定義することが可能です。 (Resource RequestsResource Limits)

ノード内にあるPodのリソースの状況をKubernetesが確認し、ある条件で判定しているそうです。(これについては詳しく調べていないので、機会があったら記事にしようと思います。)

なので、人間が手動で定義する項目ではないようです。

以下の2つはPodを特定のノードにスケジューリングする際のもの。

Node-Selectorsは、特定のノードにスケジュールするためのもので、

Tolerationsは、特定のノードにスケジュールしないためのもの。

※ こちらもここでは深く触れないので参考はコチラ

Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s

最後にイベント情報。 ログのようなかんじで、Podの起動がうまくいかない場合などにもエラーが表示されることもあります。

Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  12m   default-scheduler  Successfully assigned default/kuard to minikube
  Normal  Pulling    12m   kubelet            Pulling image "gcr.io/kuar-demo/kuard-amd64:1"
  Normal  Pulled     12m   kubelet            Successfully pulled image "gcr.io/kuar-demo/kuard-amd64:1" in 4.327812798s (4.327878355s including waiting)
  Normal  Created    12m   kubelet            Created container kuard
  Normal  Started    12m   kubelet            Started container kuard

kuardにアクセス

先程起動させたPodにポートフォワードして、ブラウザからアクセスが可能です。

$ kubectl port-forward kuard 8080:8080

まとめ

今回はPodについて勉強していきました。

kubectlコマンドでは、まだまだたくさんのオプションがあります。

重要そうなコマンドや、便利そうなコマンドは随時別の記事にて記載できたらと思います。

次回は、ヘルスチェックとボリュームについて記載した記事を投稿予定です。