⚙️

Kubernetes (k8s) 基本用語学習

2021/04/25に公開

はじめに

本記事は、あくまでKubernetes (k8s)の用語を中心とした学習メモです。

書いた人のバックグラウンドは以下です。

  • Dockerの前提知識はあるがk8sはなし。
  • 直近の仕事で、k8s環境を少しだけさわる機会があったが、ほぼ作業だったためあまり理解していなかった。
  • クラウド、インフラ寄りの前提知識

k8sが必要な理由

オーケストレーションツールについて

一般的に、素のdockerやdocker-composeで起動したコンテナは、ホストが消滅したら死んでしまう。
実際の環境では、ホストを増やした際に複数コンテナを複数ホストに割り当てたり、ホストが死んでもコンテナのサービスが生きていることが必要となる。

また、dockerは単一ホスト内のコンテナ間のやりとりは容易だが、ホストを跨ぐコンテナ通信だと、連携が煩雑になるという弱点を持つ。

このあたりをよしなにしてくれるのがコンテナオーケストレーションの役割となる。

k8sはこのコンテナオーケストレーションツールの一つで、クラスタ化した際の「複数台のホストの管理・統合」のための運用ツールとして扱える。

k8sができること

コンテナ運用で必要となる、以下のような機能を持っている。

  • 複数のコンテナをPodとして作成。まとめて管理。
  • 複数ホスト上のコンテナの分散。
  • コンテナに対するトラフィックを制御。
  • コンテナの自動修復と耐障害性の実現。
  • ローリングアップデート(コンテナを無停止で徐々にアップデート)。
  • 稼働中アプリのスケーリング。
  • 監視やログなどの一括管理。

k8sの中心用語

Node (ノード)

Nodeは、ワークロードを実行する仮想もしくは物理的なマシン。
k8sでは、Nodeが複数集まってクラスターを形成する形になる(Node単体でもクラスターにはなる)。
実際のコンテナは、Node上で実行されるPod(後述)に配置されることで動いている。

Nodeには、マスターノードとワーカーノードの種類存在し、マスターノードがワーカーノードを制御する形で動いている。
利用者は、マスターノードに対し、kubectlコマンド等で指示を出し、クラスター全体を操作しているイメージ。

  • クラスタの正常性確認
kubectl cluster-info
kubectl get componentstatus
  • クラスタのノード一覧確認
kubectl get nodes

Pod (ポッド)

Podは、コンテナをグループ化する単位。
管理上の基本単位で、Pod内は仮想NICを共有(= IP, ボリュームが同じ)しており、仮想ホストのような動きをする。
単一のコンテナからなるPodも、複数のコンテナからなるPodもありえる。
Podはワーカーノードの中に立ち上げられる。

操作コマンドはdockerコマンドに似ている。

  • Podの起動
kubectl run --image イメージ名:タグ --restart Never Pod名

※単体ポッドの作成時は、--restart Neverオプションが必要。

  • Podのリストを確認
kubectl get pods
  • Podのログ取得コマンド
kubectl logs Pod名
  • Podの詳細確認
kubectl describe pod Pod名
  • Pod内へshログイン
kubectl exec -it Pod名 sh
  • Pod内のコンテナの環境変数を定義
    --env TEST_ENV=hellow_worldオプション

Service (サービス)

Podをクラスター内外に公開する、静的IPを持ったL4ロードバランサー機能を持つリソース
Pod単体だと、中身がコンテナであることからワークロードとして安定しないが、サービスがあることにより、クラスター内外からPodへの安定的なアクセスを提供することができる。
サービスはタイプが3つがある。

1. ClusterIP

いつ消えるかわからないPodのIPを抽象化し、静的IPを持たせることができるServiceタイプ。
これにより、Podへのアクセスをロードバランスしてくれる。
ただしアクセスはCluster内のアクセスに限定されることとなり、クラスター内であれば、ServiceのIPやServiceの名前で相互にアクセスできるようになる。

2. NodePort

ClusterIPでできなかった外部公開もできるServiceタイプ。
これは、NodeIPとNodePort経由にてアクセスが行われる。
このタイプの場合は。アクセスの際にNodeIPとNodePortを指定すると、該当のClusterIPをもつサービスにつないでくれる。

ただし、この場合、PodのポートとNodeのポートが紐づけられてしまう。
以下の例だと、Nodeの30142がPodの8080に紐づいている形。

kubectl get pod,service -ou -o wide
NAME                           TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE    SELECTOR
service/helloworld-nodeport    NodePort    10.104.105.179   <none>        8080:30142/TCP   4s     run=helloworld

これの問題としては

  • アクセスには、NodeIPとNodePortも知っている必要がある。
  • NodePortは3000以上の必要がある。

という点。
Podが起動・停止してIPが入れ替わる可能性があるように、Nodeでもそれが起こりえる(例. EC2 オートスケーリング)ため、考慮が必要となってしまう。

3. LoadBalancer

NodePortタイプのServiceの前に、静的IPをDNS名を持つロードバランサー(LB)をもつ形式。
このLBのDNSにアクセスすることで、そのまま各ノードの特定のポートにルーティングしてくれる。このDNSは、クラウドプロバイダに依存する形式で割り当てられるので、若干挙動が変わる。

これの問題点は、

  • 1つのServiceごとに1つのLBが作られてコスパが悪い
  • L4のLBなのでHTTPのホストとパスでのLB振り分けができない。

という点。

Ingress (イングレス)

Podをクラスター内外に公開するL7のLB。
LoadBalancerタイプのServiceでカバーできないL7のところまで手が届くリソース。
外部からURLのホスト・パスによるService(NodePortタイプ)の振り分けができる。

例えば、

  • hoge.test.com → ServiceA → 該当Pod群
  • hoge.test.com/huga → ServiceB → 該当Pod群

のような感じ

  • Ingressのリストを確認
kubectl get ingress
  • Ingressの詳細確認
kubectl describe ingress ingress名

※ingressのyaml抜粋例

kind: Ingress
~中略~
spec:
  rules:
  - http:
      paths:
      - path: /
        backend:
          serviceName: サービス名
          servicePort: サービスのポート

本番運用では、このリソースでService, Podへのアクセスを担保する。

ReplicaSet (レプリカセット)

実際運用の際、Podは冗長構成をとることが一般的であり、そのためのリソースがReplicaSet。
このリソース内で定義したレプリカの数を自動配置し、維持・冗長化してくれる。
使用する際は、

kind: ReplicaSet
~中略~
spec:
  replicas: (必要数)

のようにyamlで定義する。

Deployment (デプロイメント)

Podのローリングアップデート / ロールバックを無停止更新で実施するリソース。
仕組みとしては、Podのデプロイ時に新しいReplicaSetを作成し、古い方のReplicaSetを1つずつ減らしながら新しいものを増やして段階的に置き換えるようになっている。
PodをDeploymentとして作成するコマンドはこちら

kubectl create deployment --image イメージ名:タグ Deployment名

k8sの定義ファイル

ingressの部分で既にちょっと出てきたが、宣言的(Declarative)に定義したyaml形式ファイルに、リソースの情報を記載する。

※補足

  • 宣言的 (Declarative)
    最終的な状態を宣言しているもの。「何を(What)」を作成するかを定義している。k8sのyamlやIaCコードなどがそう。
  • 命令的 (Imperative)
    必要な情報を1つ1つ定義した上で指示を出すもの。「どのように(how)」を定義している。dockerコマンドが一例(Nginxコンテナを起動するために、docker run(動作) nginx(対象) -p 80:80(方法)
    を定義して実施する)

kubectl runからkubectl yamlへの変換

kubectl runコマンドで、以下のようなオプションをつけることでyamlファイルを生成する。

kubectl run --image イメージ名:タグ \
--restart Never \
--dry-run \
-o yaml \
ポッド名 > hoge_pod.yaml

こうすることで、dry runでyamlを生成し、hoge_pod.yamlに情報を書き込んでいる。
これによってできたyamlを用い、

kubectl apply -f  hoge_pod.yaml

でPodをデプロイできるようになる。

複数コンテナのPodを作成するためのyamlの編集

上記で作成したyamlなどを活用し、複数コンテナからなるPodなども生成可能。
先述のrunコマンドでコンテナ1個のyamlができたら、Spec下に追加するコンテナを定義していく。

deploymentのyaml定義

レプリカセットの数を--replicasオプションで指定して実行することでyaml定義を作成できる。。

kubectl run --image イメージ名:タグ \
--replicas レプリカセット数 \
--dry-run \
-o yaml \
Deployment名 > hoge_deployment.yaml

Podの場合、Pod内のコンテナがSepcで定義できるが、Deploymentの場合、まずDeploymentのSpec(replicasなど)が定義され、その中にPodの定義としての子specが存在する形になる。

k8sのその他用語

Config Map

環境変数などをキーバリューペアとして保存するリソース。
ボリュームにConfig Mapのファイルを置き、それをPodのファイルシステムでマウントして使用する。

  • Config Mapがない場合
    envオプションをつけてrunするか、yamlのenvに記述が必要。
    これだと、環境の再利用が難しい。

  • Config Mapを使う場合
    yamlのenv下で環境変数の名前をnameとして定義し、
    その値をconfigMapKeyRefで引っ張ってくる形にする。
    そこには、引っ張ってくるConfigMapとその中のKeyを指定する。
    そうすることで、他のマニフェストで再利用もできる。

※ConfigMapを用いたPodのyaml例

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: hoge-configmap-env
  name: hoge-configmap-env
spec:
  containers:
  - env:
    # 環境変数の名前を定義
    - name: TEST_ENV
      valueFrom:
        configMapKeyRef:
          # ConfigMapの名前を指定
          name: my-config
          # ConfigMapのKeyを指定
          key: TEST_ENV
    image: イメージ名:タグ
    name: hoge-configmap-env
    ports:
    - containerPort: 8080
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Never
status: {}

定義方法

Config Mapをyamlで定義する場合は、

kubectl create configmap ConfigMap名 \
	--from-literal=TEST_ENV=hogehoge \
	--dry-run \
	-o yaml > configmap.yaml

とすることでconfigmapマニフェストが作成できる。
このyamlをapplyコマンドでリソースとして作成することでConfigMapが作られる。

環境変数の使い方

環境変数を使う場合は、pod.yamlでConfigMap名を指定し、そのKey(上記だとTEST_ENV)を指定することで参照できる。

Secret

ConfigMapに似ているリソースで、Base64でエンコードしたキーバリューペアのリソース。
これは誰でもデコードできてしまうので、DB Passwordなどを保存するべきではないが、SSL Certやトークンを定義するのには有効。
これもconfig mapに似て、

kubectl create secret generic Secret名 \
	--from-literal=SECRET_KEY1=SECRET_VALUE1 \
	--from-literal=SECRET_KEY2=SECRET_VALUE2 \
	--dry-run \
	-o yaml > secret.yaml

でマニフェストファイルを作り、それをもとに編集 → applyで生成することができる。

Volume (ボリューム)

dockerコンテナは、ホストマシンのストレージにdockerマウントすることでデータを保持することができる。
k8sも、Podにボリュームをマウントできる。
k8sの場合、Podが管理上の基本単位で仮想NICを共有しているので、Pod内は同じボリュームファイルシステムである。
つまり、k8s上ではPodがホストに相当する単位になる。

これを活用することで、先述のConfigMapをボリュームとしてPodにマウントすることもできる。
マニフェストファイルのvolumesに、作成するボリュームを指定し、そこにConfigMapを記述する。そして、マニフェストファイルのContainers内のvolumeMountsで作成するvolumeを指定し、コンテナ内のマウントパスを指定することで、ConfigMapのボリュームをコンテナにマウントすることができる。

※該当部抜粋例

kind: Pod
~中略~
Spec:
  containers:
  - image: イメージ名:タグ
    name: hoge-configmap-volume
~中略~
    volumeMounts:
      # Volume名を指定
    - name: my-config-volume
      # Volumeをマウントするコンテナ内のファイルパスを指定
      mountPath: /my-config/TEST_ENV
  volumes:
    # 作成するVolume名を指定
    - name: my-config-volume
      configMap:
        # ConfigMap名を指定
        name: my-config
        items:
        # ConfigMap内にあるKeyの名前をして
        - key: TEST_ENV
          path: keys

PersistentVolume (永続ボリューム)

先述のボリュームは、Podにマウントすることでデータを保持することはできたが、Podも仮想ホストの働きをするものなので、Pod自体が消えるとデータも失われてしまう。
それを解消するため、PersistentVolume(永続ボリューム)が存在する。
これは、コンテナのデータをクラスターワイドのNode上に存在させるためのボリュームで、複数Pod間でファイルを共有したり、ホストマシンなどの外部ストレージにデータを保存することができる。
使用時は、クラスター内に、ワーカーノードが共有できるPV(永続ボリューム)領域を作り、それをPodに分配する。
この分配内容は、PVC(PersistentVolumeClaim)というもので定義する。

例. PVは100MB確保。PVCでは5MBを要求してPodに紐づける場合。
Podは、計100MBのPV上に5MBのボリューム領域を持つことができる。

使用方法としては、pv.yamlでスペックでstorageサイズ、アクセスモードなどを定義、pvc.yamlでアクセスモードやresoucesのrequestsを定義する。
なお、アクセスモードが一致しているPVからPVCは要求をするようになっている。

※yaml例

  • pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv
spec:
  storageClassName: manual
  capacity:
    storage: 100M
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/pvc"
  • pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5M

Namespace

複数のチームやプロダクトが、同一のクラスター・ホスト上で利用される際に用いる、名前空間を定義するリソース。
コマンドとしては、kubectl create namespace hogehogeでhogehogeというNamespaceを作ることができる。
これをkubectl runなどで指定することで、指定したNamespace上にPod等を作成できる。
このNamespaceによって、1つのクラスター内に複数の仮想クラスター環境を作り出せる。

k8sのNode間ネットワーク

dockerのネットワークモードには以下のものがある。

  • bridge:ホストの中のDockerBridge NWからIPが振り分けられる。IPがホストと異なる。
  • host:ホストのIPレンジからIPが振られ、ホストポートを使うので、ポート被りがNG
  • null:リンクなしで、接続不可となる。
  • overlay:k8sなどはこれ。複数Nodeがあるときに使用する。

k8sの場合、パケットを、Node間通信情報内にpod間通信情報を入れたカプセル化することでレイヤ分けし、それぞれのレイヤでカプセル化を解除して通信を行う。このNode間にまたがる通信では、Nodeが使う共通のRouteTable上の定義にのっとって制御がされている。

これを実現するためにはk8sのCNI(Container Network Interface) Pluginが必要。
具体例としては以下。

  • Calico
  • Flannal
  • WeaveNet Crypt

おわりに

とりあえず用語と概要の定着を行うために執筆しました。
あまり具体的な内容がなくて申し訳ないですが、同じような初学者で誰かのお役に立てれば幸いです。

参考

大変お世話になりました。

Udemy「米シリコンバレーDevOps監修!超Kubernetes完全入門(2020)【優しい図解説とハンズオン】

https://kubernetes.io/ja/docs/concepts/architecture/nodes/

https://kubernetes.io/ja/docs/tutorials/kubernetes-basics/explore/explore-intro/

https://kubernetes.io/ja/docs/concepts/services-networking/ingress/

https://kubernetes.io/ja/docs/concepts/workloads/controllers/replicaset/

https://qiita.com/ysakashita/items/924786aa24ef3bb864ef

https://kubernetes.io/ja/docs/concepts/storage/persistent-volumes/

https://kubernetes.io/ja/docs/concepts/overview/working-with-objects/namespaces/

https://www.netone.co.jp/knowledge-center/netone-blog/20191226-1/

Discussion