🔖

Dockerとkubernetesをlocalで一緒に使ってみた

に公開

はじめに

Dockerとkubernetesをlocalで一緒に使ってみました。
dockerのメーリングリストから以下のメールが来たのがきっかけです。
dockerの連携はdocker-composeでしか実施したことがなく、kubernetesを使ったことがないのでやってみます。

Docker と Kubernetes の違いが問題なのではなく、それらがどう連携すれば最も効果的かが重要です。Docker と Kubernetes は連携して、コンテナ化された開発・デプロイ・管理のための完全なエコシステムを構築します。

開発者が Docker を使ってアプリケーションをセキュアなコンテナとしてパッケージ化した後、Kubernetes がそれをオーケストレーションします。これにより、コンテナの本番運用における管理やデプロイ作業の多くが自動化されます。

Docker を Kubernetes と併用することで、以下のメリットも得られます:

  • 安定した開発環境の確保
  • テストプロセスの効率化
  • アプリケーションセキュリティの向上
  • デプロイの高速化

下記に記載する情報は、メーリングリストで公開されていたlocalでテストする手順を試したものです。

Docker Desktopのone-nodeクラスタを試す

docker desktopにはkubernetesのワンノードクラスタが組み込まれていて簡単に使うことができます。

docker desktopの設定から、Kubernetesを選択すると有効化できます。

今回はkubeadmを選べばよいようです。
Create a cluster containing one or more nodes with kind. Requires the containerd image storeを選べば複数nodeも試せるんですかね。

Apply & RestartするとKubernetesクラスターのインストールが始まります。

Docker desktopのsingle node kubernetesクラスタは設定済みですぐ使うことができます。コマンドラインツールのkubectlもDocker desktopに含まれているので、すぐにコマンドを使うことができます。

Kubernetesについて

kubernetesを使おうとして詰まってしまうよくある問題として、Kubernetesクラスタの管理とソフトウェアの開発が通常違うチームで実施されることです。ワンノードクラスタを使ってアプリケーションが動くことを確認することで、運用チームに実装を依頼することができます。

試す前に2つの概念、NodesPodsだけ理解しましょう。
NodesはPodsが動くマシンのこと。
PodsはNodesで動作するコンテナセットのこと。
一つのNodeは複数のPodsを持つことができます。一つのPodは複数のNodesで動作できませんがdeploymentsと呼ばれる仕組みを使ってレプリカを作ることができます。

典型的なkubernetesクラスタは複数のpodsが動く複数のNodesを持ちます。あるノードが失敗すると、クラスターが失敗を検知すると、他の健康なノードでの実行がスケジュールされます。deploymentを使うとこれが自動で実行されます。

つまりkubernetesは自動回復機能を持ったコンテナアプリの実行プラットフォームです。

マルチコンテナのサンプル

まずは準備です。kubernetesなしでサービスを作ります。

Nginxのリバースプロキシ、Python Flaskウェr部アプリ、Redisデータベース、の構成を作ります。
実装できたら、docker compose up --buildで起動します。
localhost:8080でflask ウェブアプリのアクセスできます。
画面を更新するたびにアクセス回数が増えます。

以下に全サンプルのコードがありますのでご参照ください。

以下にdocker向けのコードのみ記載します。

サンプルコード。k8s-appフォルタ、run-k8s-app.sh以外の実装をします。
[itokohei@koheiito-MacBook-Pro:~/private-repos/test-lab-docker-kubernetes-admin]+[main]
$ tree
.
├── docker-compose.yaml
├── k8s-app
│   ├── nginx-configmap.yaml
│   ├── nginx-deployment.yaml
│   ├── nginx-service.yaml
│   ├── redis-deployment.yaml
│   ├── redis-service.yaml
│   ├── webapp-deployment.yaml
│   └── webapp-service.yaml
├── nginx
│   ├── Dockerfile
│   └── nginx.conf
├── run-k8s-app.sh
└── webapp
    ├── app.py
    └── Dockerfile

nginx/nginx.conf

events {
  worker_connections 1024;
}
http {
  server {
    listen 80;
    location / {
      proxy_pass http://webapp:5000;
    }
  }
}

nginx/Dockerfile

FROM nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf

webapp/app.py

from flask import Flask
import redis
import os

app = Flask(__name__)
redis_host = os.getenv("REDIS_HOST", "localhost")
r = redis.Redis(host=redis_host, port=6379, decode_responses=True)

@app.route('/')
def hello():
    count = r.incr('counter')
    return f'Hello, you have visited {count} times.'

if __name__=='__main__':
    app.run(host="0.0.0.0", port=5000)

webapp/Dockerfile

FROM python:3.11
WORKDIR /app
COPY . . 
RUN pip install Flask redis
CMD ["python", "app.py"]

Kubernetesを使うようにする

上記で作成したサービスをkubernetesを使うように変更します。

作成するリソース

サービス定義ファイル、デプロイ定義ファイル、ConfigMapファイル

Docker composeで定義した3つのサービスのそれぞれに対して、サービス定義ファイルと、デプロイ定義ファイルとNginx向けのConfigMapファイルを作成します。
サービスはPodsを取りまとめる単位で、IF定義のイメージです。
デプロイメントでは、どのイメージとボリュームを使うのか、レプリカはいくつ作られるのか、等のPodの定義をします。

kubernetesではイメージのビルドはしないので事前にビルドしておく必要があります。例えばredisは今回はオフィシャルのイメージをそのまま使っているので変更の必要はありません。

Nginxの場合はデフォルトの設定値を適応する必要があるために少し複雑です。しかし、今回はkubernetesの別のリソースであるConfigMapを使えるのでその必要はありません。ConfigMapによってNginxコンテナの実際の設定を独立して実施できます。これによりNginxの設定を動的にできますし、Kubernetesはpodsにその変更を伝えます。

コンテナよりもConfigMapのほうがバージョン管理もしやすいです。

実装ができたら以下のコマンドでwebappをビルドしておきます。

docker compose build

その後以下コマンドでkubernetesを実行します。

kubectl apply -f ./k8s-app/nginx-configmap.yaml
kubectl apply -f ./k8s-app/redis-deployment.yaml
kubectl apply -f ./k8s-app/redis-service.yaml
kubectl apply -f ./k8s-app/webapp-deployment.yaml
kubectl apply -f ./k8s-app/webapp-service.yaml
kubectl apply -f ./k8s-app/nginx-deployment.yaml
kubectl apply -f ./k8s-app/nginx-service.yaml

以上で以下からdockerの時と同様にサービスにアクセスできるようになります。

localhost:8080

以下にkubernetesを使う場合のサンプルコードを記載します。

サンプルコード。k8s-appフォルタ、run-k8s-app.shの実装をします。

nginx-configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  nginx.conf: |
    events {
      worker_connections 1024;
    }

    http {
      server {
        listen 80;
        location / {
          proxy_pass http://webapp:5000;
        }
      }
    }

nginx-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 1 # podを一つ起動
  selector: # 管理対象のラベルを定義
    matchLabels:
      app: nginx
  template: # podにnginxラベルを付与する
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:alpine
        ports:
        - containerPort: 80
        volumeMounts: # configファイルをサービスから参照する
        - name: nginx-config
          mountPath: /etc/nginx/nginx.conf
          subPath: nginx.conf
      volumes:
      - name: nginx-config
        configMap: # 使用するconfigMapを指定。kubernetesにconfigを宣言して利用可能にする感じ
          name: nginx-config

nginx-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  ports:
  - port: 8080 # サービスが受け付けるポート
    targetPort: 80 # podが待っているポートに転送
  selector: # 転送対象はnginxのlabelがついたpod
    app: nginx

redis-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:alpine
        ports:
        - containerPort: 6379

redis-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: redis
spec:
  ports:
  - port: 6379
  selector:
    app: redis

webapp-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: test-lab-docker-kubernetes-admin-webapp:latest # localでdocker compose buildで生成されたイメージを指定
        env:
        - name: REDIS_HOST
          value: "redis"
        ports:
        - containerPort: 5000

webapp-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: webapp
spec:
  ports:
  - port: 5000
  selector:
    app: webapp

最後に

kubernetesを使ってコンテナのオーケストレーションができました!
想像していたより簡単にできました。
とは言え本番ではもっと複雑な設定があると思うので、デプロイチームに感謝ですね。
以上、ローカルでkubernetesを使いたい時に便利でした!

Discussion