前回の記事では、Dockerコンテナの手動構築とDockerfileでの構築、docker-composeでの構築を行いました。
最近ではKubernetesを使用したシステムも多くなっているようですので、Kubernetesについても少しふれておきたいと思います。
とはいえ、Kubernetesを使ったシステムはインフラ構築をすることと同じぐらいのバリエーションがありますので、ここで紹介するのはほんの一部です。
どういったものなのかということの「さわり」程度が理解できるところまでは紹介したいと思います。

Kubernetesとは

wikipediaでは以下のように説明されています。

Wikipedia:「コンテナ化したアプリケーションのデプロイ、スケーリング、および管理を行うための、オープンソースのコンテナオーケストレーションシステムである」

https://ja.wikipedia.org/wiki/Kubernetes

kubernetesは「クーベルネテス」などと読むようです。

docker-composeでもオーケストレーション的なことはできますが構築や起動を行うための機能でした。
それに対してkubernetesはシステムを運用するための仕組みといえます。
どちらかというとコンテナの構築より管理がメインの機能となります。
たとえば、コンテナの起動に関しても冗長化した状態で起動し、障害発生時には自動的に別のノードが起動するようになっています。
また、コンテナの更新を行う場合でもシステムが停止しないようにノードを順次入れ替える機能などもあるようです。(実際に動かしたことはないですが。。。)

具体的な機能としては以下のものがあります。

  • コンテナオーケストレーション
  • デプロイやアップデート(アプリケーション)
  • アプリケーションリソースの自動スケーリング
  • リリース効率化の実現
  • ローリングアップデート
  • 自動回復機能

kubernetesを使ったシステムの構造

kubernetesを使ったシステムは以下のような構造となっています。

kubernetesは自身が仮想化の仕組みを持っておらず、ユーザが(Docker,VertualBox,podmanなど)どの仮想化の仕組みを使うか選べれるようになっています。
そのため、仮想化の仕組みをどれを使うかをユーザが指定する必要がありますが、基本的にはインストールされているものが利用されるようです。
今回の環境の場合、Dockerがインストールされているので、kubernetesのインスタンスはDockerコンテナの一つとして立ち上がってきます。

kubernetesの内部は各ノードで分かれており、メインノードとワーカーノードがあります。
メインノードはkubernetesの本体でOSのようなものです。
ユーザ(我々)が意識しなければいけないのはワーカーノードのほうで、こちらでコンテナを動かしたりネットワークを構成したりします。
メインノードはこれらの機能を実現するためのシステムが動いているので、リソースは必要となりますが構造などをあまり意識する必要はないと思います。

ワーカーノードの中は上記のような構造です。
何層にもなっているのでわかりにくいですが、冗長化構成などを実現するために必要な階層となっています。
ただ、毎回このような複雑な階層の構築をしたくないので、各層はデフォルトで動くようになっています。
最小単位は”pod”で、”pod”の中でDockerコンテナなどを動かします。
“Replicas”や”pod”単位で冗長化することができ、冗長化された”pod”は”deployment”でまとめられます。
“service”はネットワークを制御するもの(と思われます)で”deployment”に対してポートの開放などを行います。

一般的な用語としては”deployment”はアプリなどをサーバー配置することに使われますがkubernetesでは”pod”をまとめるオブジェクトとして扱われます。
“service”も一般の意味と少し違うようなので、勘違いしないように気を付けてください。

kubernetesの種類

kubernetesは以下のような派生版が存在します。

  • K8s
  • K3s
  • K0s
  • minikube
  • microK8s

初期に作られたkubernetesはk8sと表現されます。kとsの間に8文字あるからだそうです。(どこからそういった発想になるんでしょうねぇ。。。)
kubernetesはハードウェアリソースを多く使うので、小規模でも使えるk3sやminikubeができたようです。
構造や機能についてはあまり差はないようです。

今回はminikubeを使用してみます。

minikubeでシステム構築してみよう

前回作成したDockerイメージを使用してminikubeを使ったシステム構築をやってみましょう。
ますは、Dockerサービスを起動しておきます。

sudo systemctl start docker

minikubeではアンダースコア(“_”)が使えないことが多いので以下のファイルを修正しておきます。

nano docker-compose.yml
version: "3"

services:
    ap:
        build:
            context: ./python/
        ports:
            - 8080:8080
        environment:
            TZ: "Asia/Tokyo"
        container_name: python-server
    web:
        build: ./nginx
        ports:
            - 80:80
        expose:
            - 80
        environment:
            APSVHOST: "python-server"
            APSVPORT: "8080"
            TZ: "Asia/Tokyo"
        container_name: "nginx-server"
        depends_on:
            - ap
nano nginx/nginx.conf.template
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;
    server_tokens off;

    keepalive_timeout  65;

    #gzip  on;

    upstream unit-python {
        server ${APSVHOST}:${APSVPORT};
    }
    server {
        listen 80;
        server_name localhost;

        location  /html {
            root /var/www;
        }

        location  / {
            proxy_pass http://unit-python;
            proxy_set_header Host $host;
        }
        location /static/ {
            alias /app/static/;
        }
    }
}

実行時の環境変数を使ってpytho-serverに接続するようにunit.configの生成とunitdの起動をシェルに置き換えます。

nano nginx/Dockerfile
FROM registry.access.redhat.com/ubi9/ubi:latest

RUN yum install -y --disableplugin=subscription-manager nginx nano gettext && \
    yum clean all


RUN mkdir -p /var/www/html

COPY ./index.html /var/www/html/

RUN mkdir -p /etc/nginx/templates
COPY ./nginx.conf.template /etc/nginx/templates/

# ENV APSVHOST=python_server
# ENV APSVPORT=8080
# RUN cat /etc/nginx/templates/nginx.conf.template | envsubst '${APSVHOST},${APSVPORT}'  > /etc/nginx/nginx.conf

EXPOSE 80/tcp

# CMD ["nginx", "-g", "daemon off;", "-c", "/etc/nginx/nginx.conf"]

COPY ./nginx.sh /etc/nginx/
RUN chmod 755 /etc/nginx/nginx.sh
CMD ["/etc/nginx/nginx.sh"]
nano nginx/nginx.sh
#!/bin/sh
cat /etc/nginx/templates/nginx.conf.template | envsubst '${APSVHOST},${APSVPORT}'  > /etc/nginx/nginx.conf
nginx -g "daemon off;" -c /etc/nginx/nginx.conf

ファイルを更新したのでイメージを再構築しておきましょう。

sudo docker compose build

前述の通り、minikubeは仮想環境が必要です。
すでにDockerをインストールしてありますが、root権限以外のユーザでないとdockerの脆弱性対策(?)の影響でminikubeがうまく動いてくれないようです。
なので、rootユーザ以外でdockerコマンドが動くようにしておきましょう。
今回はホストPCとしてVertualBox上のubuntuを使用している関係で、”vboxuser”というユーザに権限を与えます。

sudo gpasswd -a vboxuser docker
cd /var/run/
sudo chown vboxuser:vboxuser  docker.sock

以下のコマンドが動けばOKです。

docker version

minikubeインストール

以下のコマンドでインストールします。
使用するホストPCごとにインストールコマンドが異なるので、正規サイトで環境を選択してインストールするようにしてください。

# インストール
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube_latest_amd64.deb
sudo dpkg -i minikube_latest_amd64.deb

# インストール確認
minikube version

インストール出来たら起動しましょう

minikube start

“docker ps -a”を実行すると、Dockerコンテナとして動作していることが確認できます。

続いて”kubectl”をインストール(?)しておきます。
“kubectl”はデプロイした”pod”や”service”の状態を確認したり削除したりするときに使用する重要なツールになります。
以下のコマンドで使用できるようになります。

minikube kubectl version

“kubectl”コマンドは”minikube kubectl”という感じで使用することになるのですが、毎回、これを入力するのは面倒なので”kubectl”だけで使用できるようにしておきましょう。

alias kubectl="minikube kubectl --"

この設定はminikubeを再起動すると無効になってしまうので、再起動したら再度設定してください。
minikube、kubectlコマンドは以下のものがあります。

# minikubeを終了する
minikube stop
# minikubeを削除する
minikube delete
# minikubeの起動状態を確認する
minikube status
# minikubeのIPアドレスを確認する
minikube ip
# minikubeにSSH接続する
minikube ssh
# minikubeのアドオンリストを表示する
minikube addons list
# Kubernetesダッシュボードを起動する
minikube dashboard

# クラスタ一覧を表示する
kubectl cluster-info
# ノード一覧を表示する
kubectl get node
# サービス一覧を表示する
kubectl get service

minikubeの状況はダッシュボードを使用してブラウザ上のGUIで見ることができます。
以下のコマンドを実行するとURLが表示されるのでホストPC上のブラウザに入力すると以下のような画面が表示されます。(Linuxディスクトップ上のコンソールの場合はブラウザが自動的に起動します。)

minikube dashboard

minikubeにシステムを構築してみる

前回作成したDockerイメージを使用してminikube上にシステムを構築してみましょう。
minikubeにDockerイメージを読み込ませてシステムを構築していきます。
まず、イメージにタグをつけておきます。(”latest”だとうまくいかなかったので”v1″にしています。)

docker tag docker-web nginx-server:v1
docker tag docker-ap python-server:v1

イメージをminikubeにロードします。

minikube image load nginx-server:v1
minikube image load python-server:v1

ロードしたイメージは以下のコマンドで確認できます。

minikube image ls

手動でpythonサーバーのみ構築してみましょう

前回と同様、まずは手動でシステムを構築してみたいと思います。
nginx-serverとpython-serverを連携させるのは難しいので、python-serverのみデプロイしてアクセスしてみましょう。
以下のコマンドでロードしたイメージを使用して”deployment”を作成します。
“deployment”を作成すると”default”クラスタ、”default”レプリカセットに”pod”が自動的に作成されます。

kubectl create deployment python-deployment --image=python-server:v1

デプロイ結果は以下のコマンドで確認できます。

# deployment一覧
kubectl get deployment
# pod一覧
kubectl get pod

次に”service”を作成してポートを開放し、外部からのアクセスができるようにします。

kubectl expose deployment python-deployment --type=NodePort --port=8080

サービス構築の結果は以下のコマンドで確認できます。

# サービス一覧
kubectl get service
# 外部からアクセスするときのURL
minikube service python-deployment --url

ホストPCから”http://localhost:8080″でアクセスできるようにポートフォワードを実行します。

kubectl port-forward service/python-deployment 8080:8080

別コンソールで以下を実行するとFlaskの実行結果が返ってきます。

curl http://localhost:8080

次は定義ファイルを作成してnginx-serverと一緒にデプロイしてみます。
手動で作ったものは、一旦、削除しましょう。

# サービスを削除
kubectl delete service python-deployment
# デプロイメント(pod)を削除
kubectl delete deployment python-deployment

定義ファイルを使って構築してみよう

では、nginx-server、python-serverをデプロイする定義ファイルを作成しましょう。

nano deployment.yaml
apiVersion: apps/v1
# 定義の種類
kind: Deployment
metadata:
  name: python-nginx-deployment
spec:
  # レプリカ数: レプリカとは複数のことで、ここの数字分の Pod を常に起動させる
  replicas: 3
  # 指定した条件に一致する Pod をこの Deployment が管理する (ここではlabelが一致すること)
  selector:
    matchLabels:
      app: python-nginx-deployment
  # 作成する Pod の定義
  template:
    metadata:
      labels:
        app: python-nginx-deployment
    spec:
      # Pod で起動するコンテナの定義 (Docker hubから取ってくる image を記載する)
      containers:
        - name: python-server
          image: python-server:v1
          ports:
            - containerPort: 8080
        - name: nginx-server
          image: nginx-server:v1
          ports:
            - containerPort: 80
          env:
            - name: APSVHOST
              value: "localhost"
            - name: APSVPORT
              value: "8080" 

同一pod内にnginx-server、python-serverの2つのコンテナを配置する構成としました。
この構成の場合、nginx-serverからpython-serverへのアクセスは”localhost”でアクセスできるようなので、「APSVHOST=”localhost”」としています。

続いて、サービスの定義ファイルを作成します。

nano service.yaml
apiVersion: v1
kind: Service
metadata:
  name: python-nginx-service
spec:
  ports:
    - name: python-server
      port: 8080
      targetPort: 8080
      protocol: TCP
    - name: nginx-server
      port: 80
      targetPort: 80
      protocol: TCP
  # 指定したラベルにマッチする Pod に Service へのアクセスが割り振られる
  type: NodePort
  selector:
    app: python-nginx-deployment

作成した定義ファイルを使ってデプロイしましょう。

# サービス
kubectl apply -f service.yaml
# デプロイ
kubectl apply -f deployment.yaml

状況は以下のコマンドで確認できます。

# deploymentの状態
kubectl get pod
# podの状態
kubectl get pod
# serviceの状態
kubectl get service

# deploymentの動作状況(podの状態)
kubectl describe service/python-nginx-service

# podのログ
kubectl logs [pod名] --all-containers
# (例)
kubectl logs python-nginx-deployment-85479bf8cf-qzwc5 --all-containers

# podにSSHコンソールで入る
kubectl exec -it [pod名] --container [コンテナ名] /bin/bash
# (例)
kubectl exec -it python-nginx-deployment-56ccc8cd55-dmdj4 --container nginx-server /bin/bash

# サービスの状態
minikube service python-nginx-service
# (出力結果)
|-----------|----------------------|--------------------|---------------------------|
| NAMESPACE |         NAME         |    TARGET PORT     |            URL            |
|-----------|----------------------|--------------------|---------------------------|
| default   | python-nginx-service | python-server/8080 | http://192.168.49.2:30144 |
|           |                      | nginx-server/80    | http://192.168.49.2:32563 |
|-----------|----------------------|--------------------|---------------------------|

最後にポートフォワードしてホストPCからのアクセスを確認してみましょう。
(80番ポートが何かに使われてたみたいなので、8081を使用しています。)

kubectl port-forward service/python-nginx-service 8081:80
curl http://localhost:8081

“minikube dashboard”で起動状態を確認してみてもいいと思います。

まとめ

今回はminikubeを使用してkubernetesを使ったシステム構築を紹介しました。

紹介した構成はpod内にコンテナを2つ配置する構成ですが、python-sever、nginx-serverそれぞれを別のpodに配置してサービスで連携したり、ロードバランサー(kube-loadbalancer)を使ったりすることもできるようです。

kubernetesにはもっとたくさんの機能や使い方がありますので、冗長化やシステム管理のユースケースに応じてpodの構成やReplicaSetの構成、nodeの分散などを行ってもらえればと思います。