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つの概念、Nodes
とPods
だけ理解しましょう。
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