はじめに
仕事で k8s を触ることになりそうなので基礎知識をとりあえず学習してみる。
触るのは client 側だけだけど minikube 使ってローカル環境でクラスターを立ててみたいと思う。
ってことで学びながら Zenn のスクラップに書いた内容を Qiita にまとめておく。
Zenn の記事
https://zenn.dev/sakana1115/scraps/34917d4a56f6e9
kubernetes とは
コンテナ化されたアプリケーションを管理するためのプラットフォーム
https://kubernetes.io/ja/docs/concepts/overview/
前提として、社内などのたくさんのアプリケーションがあるような状況を想定しているっぽい
メリット
コンテナ管理の簡略化
複数のコンテナ管理を自動化して、アプリケーションのデプロイ、変更の適用、スケールアップやスケールダウンを行ってくれる。コンテナがダウンしてしまったり、間違えて削除してしまったりした場合も自動で回復させることが可能。
これを実現させているのが、宣言的設定である。
宣言的設定とは、あるべき状態だけが定義され、その状態にするための手順は指示しない設定のこと。これによりあるべき状態から外れた場合にその状態に戻そうとしてくれる。宣言的設定と対照的なのが、命令的設定で操作やコマンドを記述した設定であり、Dockerfile がこれにあたる。
物理サーバーとアプリケーションの間の抽象レイヤー
物理サーバーとアプリケーションの間に抽象レイヤーとして入り、両者の間の依存関係を断ち切ってくれる (アプリケーションはk8sにだけ依存する)
複数の物理/仮想サーバーを束ねることが出来き、複数のサーバーで複数のコンテナを動かすことができる。さらに物理サーバーが1つダウンしても他のサーバーでカバーされ、アプリケーション開発者は物理サーバーの構成やメンテナンス、障害を気にする必要がなくなる。
参考
k8s を使う理由は下記の説明がわかりやすかった
https://zenn.dev/esaka/articles/2d117655af1f03cf2444
https://cybozu.github.io/introduction-to-kubernetes/introduction-to-kubernetes.html
前提知識
https://kubernetes.io/ja/docs/concepts/overview/components/
コントロールプレーン (Control Plane)
Kubernetesクラスタのマスターコンポーネント(kube-apiserver, kube-controller-manager, kube-scheduler, etcdなどを実行) で、クラスタの状態を管理・制御を行う。マスターノード上で実行される。
ノード (Node)
クラスタ内の個々の物理または仮想マシンを指し、コンテナ化されたアプリケーションの実行環境
- ワーカーノード: アプリケーションのコンポーネントであるPodをホストする
- マスターノード: クラスタ内のワーカーノードとPodを管理する Control Plane を実行する
一般的にどのNodeでどのアプリケーションを動かすかなどを指定することはなく、アプリケーション側は実行環境であるNodeを意識することはない (どこのNodeで稼働しているかは確認できる)
クラスタ (Cluster)
Kubernetes クラスタは、コンテナ化されたアプリケーションを実行するためのノードの集合。Kubernetes を実行しているときは、クラスタが実行される。
クラスタは、1 つのControl Planeと、1 つ以上のNodeで構成される。この2つは物理レイヤの話しであり、クラスタはこの上のレイヤー
ポッド (Pod)
Kubernetesの最小実行単位であり、1つ以上のコンテナをまとめて実行するためのひとつの空間。Pod内には複数のコンテナを配置することができる。
サービス (Service)
クラスタの内部および外部からPodとの通信を容易に行うためにエンドポイントの提供を行ってくれる機能。クラスター内のPod (コンテナ) に対して一意なネットワークアクセスを提供する抽象化されたリソース。
通常、NodeのIPアドレスは一定ではないが、サービスによってアプリケーションの安定したネットワーク接続を実現する。
デプロイメント (Deployment)
アプリケーションのデプロイとアップデートを管理するためのKubernetesリソース
指定された数のレプリカセットを作成し、Podのデプロイとスケーリングを簡素化する。アプリケーションのバージョンアップやロールバックなどの操作もデプロイメントを介して行われる。
またk8s の特徴である宣言的設定を実現する。
(宣言的設定とは、あるべき状態だけが定義され、その状態にするための手順は指示しない設定のこと。これによりあるべき状態から外れた場合にその状態に戻そうとしてくれる)
ネームスペース (Namespace)
クラスタ内のリソースを論理的に分割するための仕組み。異なるチームやプロジェクト、環境などを区別するために使用される。
ツール
学習に必要なツールたち
ツール名 | 用途 |
---|---|
kubectl | Kubernetesのコマンドラインツール (CLI) Kubernetesと接続してkubernetesを操作するツール |
minikube | Kubernetesをローカルで実行するツール |
通常、k8s環境はサーバーなどで大規模な環境に構築するもの。それをローカルなどの小規模な環境でk8s環境を構築できるようにしたものがminikube
。
Dockerなどの仮想環境上でシングルノードのKubernetesクラスターを構築する。
ツールたちの関係性
簡単に書くと下記のような関係性
PC/Mac -> [kubectl] -> minikube (Docker上で動く)
引用元: https://qiita.com/fukumasa/items/884eadd2694de19d64ff
上記だとVMになっているがMacで立ち上げたDocker container 内で minikube は動作していると思う。
(ここでは触れないが、ややこしいのはnode上のクラスターの上でさらにDocker containerが動作していること)
ツールのインストール
大前提としてDockerがインストールされていること
kubectl をインストール
brew install kubectl
minikube をインストール
brew install minikube
ちなみに 自分の環境では Mac で Docker 動かすのに使っている Rancher Desktop に入っているので kubectl のインストールは不要だった。
minikube を試す
前提知識を学んだ後で、やっと最初に戻ってチュートリアル。
https://kubernetes.io/ja/docs/setup/learning-environment/minikube/
クラスタ構築
通常の k8s だと複数の Node 上にクラスタを構築するが、minikube では一つの Node 上に構築する (=シングルノード)
構築
下記のコマンドでクラスタが構築される (初回はminikubeのdockerコンテナをbuildするので時間かかる)
minikube start
Docker が入っていれば VM は Docker が選ばれるらしいが、一応確認 -> いるのでOK
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6c854c5670cb gcr.io/k8s-minikube/kicbase:v0.0.39 "/usr/local/bin/entr…" 8 minutes ago Up 8 minutes 127.0.0.1:49157->22/tcp, 127.0.0.1:49156->2376/tcp, 127.0.0.1:49155->5000/tcp, 127.0.0.1:49154->8443/tcp, 127.0.0.1:49153->32443/tcp minikube
ちゃんとクラスタが動作しているかも確認 -> RunningになってるのでOK
$ minikube status
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
停止
停止するのは下記のコマンド
minikube stop
止めるとStoppedになることも確認
$ minikube status
minikube
type: Control Plane
host: Stopped
kubelet: Stopped
apiserver: Stopped
kubeconfig: Stopped
もう一度minikube start
すれば、再度クラスタが立ち上がる。
クラスタを操作
minikube によってクラスタが構築されたので、次にクラスタに接続&操作する。
先述した通り、kubectl を用いてクラスタを操作する。
接続するクラスタの設定
なお本来は接続するクラスタを kubectl で設定する必要があるが、minikube start
によってその設定も自動で行われ、設定はkubeconfig (~/.kube/config
) に書き込まれている。
下記で設定された内容を確認可能 (複数のクラスタに接続したい場合は、こちらに従ってcontext を追加する)
$ kubectl config view
念のため、現在の context が minikube になっていることを確認
$ kubectl config current-context
minikube
ちなみに context の切り替えは下記で可能 (minikube ところに 切り替え先のcontext 名をいれる)
$ kubectl config use-context minikube
Switched to context "minikube".
クラスタの構成を確認
接続するクラスタの設定が完了したので、kubectl でクラスタを構成する Nodeを確認してみる。
minikube start
によってクラスタを構築すると下記のようなイメージでクラスタが作成される。
minikube はシングルノードのためクラスタを構成する Node は一つであり、マスターノードも兼ねる。
下記でコマンドで構成するNodeを確認できるので確認してみると 、
$ kubectl get node
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane 4h59m v1.26.3
1つだけしか表示されないのでシングルノードだとわかる
マルチノード化
ここはやらなくてもOKなので参考までに。
minikube の標準ではシングルノードだけれどもワーカーノードを増やしてマルチノード化することもできる。下記の例ではワーカーノードを2つにしている
minikube node add --worker 2
Nodeを確認してみると2つに増えている
$ kubectl get node
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane 4h59m v1.26.3
minikube-m02 Ready <none> 7s v1.26.3
下記のようにクラスタを構成するワーカーノードが2つになる。これによって k8s のメリットである複数のサーバーを束ねることができているのがわかる。
参考
本来、Node は別のマシンで動くはずなのに「なぜ同じMac上で複数 Node を実現できるの?」となると思うが、minikube では Node 自体がコンテナ上で動いており、複数の Node コンテナを立てることでマルチノードを実現できている。
試しに稼働中の Docker コンテナを確認してみると、2つのコンテナが稼働していることがわかる
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a3a21d3ffd77 gcr.io/k8s-minikube/kicbase:v0.0.39 "/usr/local/bin/entr…" 9 hours ago Up 9 hours 127.0.0.1:49167->22/tcp, 127.0.0.1:49166->2376/tcp, 127.0.0.1:49165->5000/tcp, 127.0.0.1:49164->8443/tcp, 127.0.0.1:49163->32443/tcp minikube-m02
6c854c5670cb gcr.io/k8s-minikube/kicbase:v0.0.39 "/usr/local/bin/entr…" 14 hours ago Up 14 hours 127.0.0.1:49162->22/tcp, 127.0.0.1:49161->2376/tcp, 127.0.0.1:49160->5000/tcp, 127.0.0.1:49159->8443/tcp, 127.0.0.1:49158->32443/tcp minikube
Deployment の作成
k8s では 各アプリケーションは Pod という単位でデプロイされ、実行される。 「どんな Pod を用意するか」を決めるためのものが Deployment であり、Deploymentに記述した Pod が自動で起動する。
このような宣言的設定 (あるべき姿の定義) を Deployment で行う。
Deployment の作成 (manifestを使用しないケース)
まずはシンプルに、チュートリアルに従い Deployment を作成してみる。
はじめに Pod が起動していないことを確認
$ kubectl get pod
No resources found in default namespace.
Deployment を作成
$ kubectl create deployment hello-minikube --image=kicbase/echo-server:1.0
deployment.apps/hello-minikube created
これは--image=
で指定したコンテナイメージの Pod をhello-mminikube
という名前で用意するという意味。(チュートリアルの image だとうまくいかないのでこちらのものを用いた)
再度、Pod を確認すると、新たに Pod が起動していることを確認できる
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
hello-minikube-77b6f68484-8pvbw 1/1 Running 0 1s
Service の作成
無事 Pod が立ち上がったが、このままだとクラスタの中からしかアクセスできない。そこで Pod との通信を容易に行うためにエンドポイントの提供してくれる Service 作成する。
下記コマンドで hello-minikube という名前の Deployment を外部に公開する Service を作成することができる。--type=NodePort
とは Node のIPアドレスを使って公開すること指している (参考)。
$ kubectl expose deployment hello-minikube --type=NodePort --port=8080
service/hello-minikube exposed
このService によって Deployment によって定義された Pod のポート (8080) が公開され、外部からアクセスが可能になる
Port forward 設定
実はまだローカル環境からはアクセスできない。
localhost のあるポートへ届いたパケットを 先ほど公開した Pod の 8080 へ転送するように設定する必要がある。下記コマンドでポート転送できる (今回は locahost:7080 を 8080 へ繋げる)
$ kubectl port-forward service/hello-minikube 7080:8080
Forwarding from 127.0.0.1:7080 -> 8080
Forwarding from [::1]:7080 -> 8080
この状態のまま、別の terminl を開いて curl で導通確認する
$ curl http://localhost:7080/
Request served by hello-minikube-77b6f68484-8pvbw
HTTP/1.1 GET /
Host: localhost:7080
Accept: */*
User-Agent: curl/7.88.1
response が返ってきていれば、導通確認はOK
これで立ち上げた Pod を公開することができた。
掃除 (Service, Deployment の削除)
最後に作成した Service, Deployment, Pod を削除させておく。
Service の削除
$ kubectl delete services hello-minikube
service "hello-minikube" deleted
Deployment の削除
$ kubectl delete deployment hello-minikube
deployment.apps "hello-minikube" deleted
Deployment が削除されたことで 定義されていた Pod も停止する
$ kubectl get pod
No resources found in default namespace.
作成した Service も削除されている
kubectl get svc
マニフェストの適用
上記で Deployment および Service を作成したが、通常はマニフェストと呼ばれる yaml ファイルで定義しておき、それを使ってリソースを作成することができる。
マニフェストの定義
マニフェストは下記のように定義される
deployment.yaml
apiVersion: apps/v1
# 定義の種類
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
# 指定した条件に一致する Pod をこの Deployment が管理する (ここではlabelが一致すること)
selector:
matchLabels:
app: nginx
# レプリカ数: レプリカとは複数のことで、ここの数字分の Pod を常に起動させる
replicas: 3
# 作成する Pod の定義
template:
metadata:
labels:
app: nginx
spec:
# Pod で起動するコンテナの定義 (Docker hubから取ってくる image を記載する)
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
# 指定したラベルにマッチする Pod に Service へのアクセスが割り振られる
type: NodePort
selector:
app: nginx
マニフェストの適用
Deployment の定義を適用
$ kubectl apply -f deployment.yaml
deployment.apps/nginx-deployment created
Service の定義を適用
$ kubectl apply -f service.yaml
service/nginx-service created
適用されたリソースを確認
Pod の確認
上記のマニフェストでは Pod のレプリカ数を3としているので、起動中の Pod を確認すると3つ起動しているのを確認することができる。
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-699bfcdcb6-7rf4q 1/1 Running 0 78s
nginx-deployment-699bfcdcb6-h9ntg 1/1 Running 0 78s
nginx-deployment-699bfcdcb6-xh7tl 1/1 Running 0 78s
ちなみに Pod を一つ削除してみても
kubectl delete pod nginx-deployment-699bfcdcb6-xh7tl
Deployment に定義された状態に戻そうと自己復旧するので、すぐに別の Pod が立ち上がる
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-699bfcdcb6-7rf4q 1/1 Running 0 112s
nginx-deployment-699bfcdcb6-h9ntg 1/1 Running 0 112s
nginx-deployment-699bfcdcb6-tzcks 1/1 Running 0 13s
Deployment の確認
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 10m
Service の確認
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 20h
nginx-service NodePort 10.105.159.180 <none> 80:32563/TCP 10m
導通確認
ポート転送を行い
$ kubectl port-forward service/nginx-service 7080:80
Forwarding from 127.0.0.1:7080 -> 80
Forwarding from [::1]:7080 -> 80
Handling connection for 7080
上記の状態で下記のURLへアクセスする
http://localhost:7080/
作成したリソースのイメージは下記の通り。
Deployment のレプリカセットから3つの Pod が起動する。また今回の場合、Pod が起動する Node の指定を特にしていないので、2つの Node に分かれて配置されている。kubectl describe pod
で確認可能
Pod への通信は Service によって適切な Pod へルーティングされる。
ちなみに pod のログは下記コマンドで確認できる
kubectl logs -p {pod name} -n {namespace}
Namespace の作成
クラスタ内のリソースを論理的に分割するための仕組みである Namespace を作成する。
複数のチーム・プロジェクトにまたがる多くのユーザーがいる環境で、クラスタを分けて管理運用した場合に使われる仕組みであり、小規模プロジェクトでは不要っぽい
https://kubernetes.io/ja/docs/concepts/overview/working-with-objects/namespaces/
Namespace の作成
下記コマンドで作成できる。(もちろんマニフェストでも作成可能)
$ kubectl create namespace sample
namespace/sample create
作成した Namespace を確認すると、さきほど作成した sample があるのがわかる
$ kubectl get namespace
NAME STATUS AGE
default Active 21h
kube-node-lease Active 21h
kube-public Active 21h
kube-system Active 21h
kubernetes-dashboard Active 19h
sample Active 66s
Namesapce ごとにリソース作成
Namespace ごとにマニフェストを適用させることができる。-n {namespace}
で指定
(マニフェストに適用させる Namespace を指定しておくこともできる -> 通常こっちの方法)
$ kubectl apply -f deployment.yaml,service.yaml -n sample
deployment.apps/nginx-deployment created
service/nginx-service created
論理的に分割されており、default の Namespace からは作成した Pod を確認できない
$ kubectl get pod
No resources found in default namespace.
Namespace を指定してあげると見れる
$ kubectl get pod -n sample
NAME READY STATUS RESTARTS AGE
nginx-deployment-57c68fcd95-8mzgl 1/1 Running 0 22s
nginx-deployment-57c68fcd95-dmkp5 1/1 Running 0 22s
nginx-deployment-57c68fcd95-xfhq2 1/1 Running 0 22s
おわりに
k8s 特有の用語が多数出てくるのでとっつきにくかったが、実際に操作しながら試すことで、基本知識はイメージ込みで習得できたと思う。(イメージ図を書き出したのが良かった気がする)
業務で使うのは k8s 自体の構築ではなく、各アプリケーション Client 側ってパターンはよくあると思うが、最低限の知識はもって置くのがよいと思う。