コンテナ間通信の設定をkubernetesとdocker-composeで同じ構成で構築してみた(nginx <-> uWSGI <-> Flask)

2020年9月5日Docker,kubernetes,Flask

kubernetesでコンテナ間の通信を設定する方法の勉強のために、同じ環境をkubernetesとdocker-composeの両方で構築してみました。

環境

クライアント/サーバ

クライアント

ソフトバージョン
OSUbuntu 20.04 LTS (Windows 10のWSL2で構築)
docker19.03.12
docker-compose1.26.2
kubectl1.18.6

 

サーバ

ソフトバージョン
OSUbuntu 20.04 LTS
docker19.03.12
kubernetes1.18.6

kubernetes環境はkubeadmでシングルノード構成です。

※コントロールプレーンにデプロイできる設定

kubeadmでkubernetes環境構築する方法については、以下の記事を参考にして下さい。

アプリケーション構成

アプリケーションはpythonのFlaskで「Hello world」と表示するだけのシンプルなものとし、uWSGIを経由しnginxで公開するようにしました。

コンテナ構成

2つのコンテナで構成

コンテナ名内容
kube-test-nginx1) alpineのnginxコンテナを利用
2) 設定ファイル(default.conf)でkube-test-appへ接続
3) ポート番号:80
kube-test-app1) Flaskでルート(/)にアクセスされた場合に「Hello World」と表示する
2) uWSGIでhttpで公開(socketではない)
3) ポート番号:5000

プロジェクトのファイル構成

プロジェクト全体のディレクトリとファイルの構成です。

/kube-test
├── app                 ・・・Flaskアプリ
│   ├── main.py
│   ├── requirements.txt
│   └── uwsgi.ini
│
├── docker
│   ├── app
│   │   └── Dockerfile  ・・・kube-test-appコンテナ
│   └── nginx
│       └── Dockerfile  ・・・kube-test-nginxコンテナ
│
├── docker-compose.yml  ・・・docker-compose
│
├── kube
│   └── kube-test.yml   ・・・kubernetes
│
└── nginx
    └── default.conf    ・・・kube-test-appへの接続設定

アプリケーションを作成する

kube-test-app(Flask App)

main.py

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
    return "Hello World"

if __name__ == '__main__':
    app.run()

requirements.txt

Flask==1.1.1
uWSGI==2.0.18

uwsgi.ini

[uwsgi]
wsgi-file = main.py
callable = app
master = true
processes = 1
http = :5000
chmod-socket = 666
vacuum = true
die-on-term = true
py-autoreload = 1

httpプロトコルでポート番号を5000に設定します。

kube-test-nginx (nginx)

default.conf

server {
    listen 80;
    charset utf-8;

    location / {
        proxy_pass http://kube-test-app:5000;
    }
}

nginxの /etc/nginx/nginx.conf /etc/nginx/conf.d/ ディレクトリの *.conf ファイルをインクルードするようになっていますので、ルート(/)でアクセスされた場合の転送先設定のみ記載した default.conf を作成します。

転送先のホスト名にFlaskアプリの kube-test-app の5000番ポートを指定しておきます。

Dockerfileを作成する

Dockerfile (kube-test-app)

FROM python:3
WORKDIR /app

RUN apt-get update
RUN apt-get -y install locales && \
    localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
ENV LANG ja_JP.UTF-8 \
    LANGUAGE ja_JP:ja \
    LC_ALL ja_JP.UTF-8 \
    TZ JST-9

RUN pip install --upgrade pip
RUN pip install --upgrade setuptools

COPY ./app/* /app/

RUN pip install --trusted-host pypi.python.org -r /app/requirements.txt

CMD ["uwsgi", "--ini", "/app/uwsgi.ini"]

COPYコマンドが ./app/* /app/ となっているのは、プロジェクトのルートディレクトリから実行されることを想定しているためです。

Dockerfile (kube-test-nginx)

FROM nginx:alpine

COPY ./nginx/default.conf /etc/nginx/conf.d/

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

docker-composeで構築する場合

docker-compose.yml

version: "3.2"
services:
  kube-test-nginx:
    build:
      context: .
      dockerfile: ./docker/nginx/Dockerfile
    container_name: kube-test-nginx
    ports:
      - "80:80"
    depends_on:
      - kube-test-app
    networks:
      - my_network

  kube-test-app:
    restart: always
    build:
      context: .
      dockerfile: ./docker/app/Dockerfile
    ports:
      - "5000:5000"
    container_name: kube-test-app
    working_dir: /app/
    tty: true
    volumes:
      - ./app:/app
    networks:
      - my_network

networks:
  my_network:
    driver: bridge

docker-composeでコンテナ間で通信する場合は以下の手順で Dockerfile を作成します。

  • networkを作成する(ここでは my_network
  • 通信したいコンテナを my_network 上に作成する
  • 同じnetwork上のコンテナはサービス名( kube-test-app kute-test-nginx )で通信できるようになる
  • kube-test-nginx から kube-test-app に通信したい場合、 depends_on kube-test-app を指定する

docker-composeコマンドでデプロイ

以下のコマンドで初回であればコンテナのビルドも同時に実行されます。

プロジェクトのルートディレクトリで実行します。(docker-compose.ymlファイルがある場所)

$ docker-compose up -d

アプリケーションやDockerfileを修正し、キャッシュなしで再ビルドしたい場合は、以下のコマンドでビルドを行った後に、上記のコマンドで起動すると最新の状態で実行できます。

$ docker-compose build --no-cache

実行結果確認

docker-compose ps コマンドでStateがUpとなっていることを確認します。

$ docker-compose ps
     Name                    Command               State           Ports
---------------------------------------------------------------------------------
kube-test-app     uwsgi --ini /app/uwsgi.ini       Up      0.0.0.0:5000->5000/tcp
kube-test-nginx   /docker-entrypoint.sh ngin ...   Up      0.0.0.0:80->80/tcp

ブラウザから http://127.0.0.1/ にアクセスすると「Hello World」と表示ます。

kubernetesで構築する場合

docker imageをdockerhubへpush

kubernetesでのデプロイ時にDocker Hubからコンテナイメージを取得するようにしたいので、事前にDocker Hubへコンテナイメージを登録しておきます。

Docker Hubのアカウントを持っていない方は以下の記事を参考にしてみて下さい。

Docker Hubのアカウントの準備ができれば、Dockerイメージの作成とDocker Hubへイメージを登録します。

Docker Hubへログインしていない場合は、以下のコマンドでログインを済ませておきます。

$ docker login

では、 kube-test-app コンテナから始めます。

$ docker build --no-cache -t [dockerhub_account]/kube-test-app:1.0.0 -f ./docker/app/Dockerfile .

$ docker push [dockerhub_account]/kube-test-app:1.0.0

ビルド時に -t オプション

[Docker Hubのアカウント]/[コンテナ名(kube-test-app)]:[タグ(バージョンなど。今回は1.0.0を指定)]

を指定します。

また、実行はプロジェクトのルートディレクトリから行います。

同様の手順で kube-test-nginx コンテナについても行います。

$ docker build --no-cache -t [dockerhub_account]/kube-test-nginx:1.0.0 -f ./docker/app/Dockerfile .

$ docker push [dockerhub_account]/kube-test-nginx:1.0.0

実際にDocker Hubにアクセスして2つのコンテナイメージが登録されているか確認してみましょう。

kube-test.yml

docker-composeでいうところの、docker-compose.ymlファイルの該当するファイルを作成します。

kube-test.ymlファイルを以下の全体イメージに沿って作成していきます。

コンテナ毎にServiceとDeploymentを分けて4つのYAMLファイルを作成してもよいですし、以下のように1つにまとめて記述することも可能です。

apiVersion: v1
kind: Service
metadata:
  name: kube-test-nginx
  labels:
    app: kube-test-nginx
spec:
  type: NodePort
  ports:
    - name: http-port
      protocol: TCP
      port: 80
      targetPort: 80
      nodePort: 30081
  selector:
    app: kube-test-nginx

---

apiVersion: v1
kind: Service
metadata:
  name: kube-test-app
  labels:
    app: kube-test-app
spec:
  type: ClusterIP
  ports:
    - name: http-port
      protocol: TCP
      port: 5000
      targetPort: 5000
  selector:
    app: kube-test-app

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: kube-test-nginx
spec:
  selector:
    matchLabels:
      app: kube-test-nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: kube-test-nginx
    spec:
      containers:
      - name: kube-test-nginx
        image: [dockerhub_account]/kube-test-nginx:1.0.0
        ports:
        - containerPort: 80

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: kube-test-app
spec:
  selector:
    matchLabels:
      app: kube-test-app
  replicas: 1
  template:
    metadata:
      labels:
        app: kube-test-app
    spec:
      containers:
      - name: kube-test-app
        image: [dockerhub_account]/kube-test-app:1.0.0
        ports:
        - containerPort: 5000

kubectlコマンドでデプロイ

kube-test.ymlを利用してkubectlコマンドでデプロイします。

今回は、kubeディレクトリにkube-test.ymlファイルを作成しているので、kubeディレクトリで実行しています。

kubectl applyコマンドを利用すると新規デプロイ時も変更時も対応できるので便利です。

$ cd kube

$ kubectl apply -f kube-test.yml
service/kube-test-nginx created
service/kube-test-app created
deployment.apps/kube-test-nginx created
deployment.apps/kube-test-app created

実行結果確認

kubectl get all コマンドや kubectl get pod コマンドなどで確認するとPodが実行されているか確認できます。(StatusがRunningになっているかどうか)

以下は、 kube get all コマンドの実行結果です。

$ kubectl get all
NAME                                   READY   STATUS    RESTARTS   AGE
pod/kube-test-app-bf6f459f-7wt42       1/1     Running   0          3m21s
pod/kube-test-nginx-6ffff769b5-rhg5g   1/1     Running   0          3m22s

NAME                      TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/kube-test-app     ClusterIP   10.104.185.76   <none>        5000/TCP       3m22s
service/kube-test-nginx   NodePort    10.109.8.40     <none>        80:30081/TCP   3m22s
service/kubernetes        ClusterIP   10.96.0.1       <none>        443/TCP        7d20h

NAME                              READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/kube-test-app     1/1     1            1           3m22s
deployment.apps/kube-test-nginx   1/1     1            1           3m22s

NAME                                         DESIRED   CURRENT   READY   AGE
replicaset.apps/kube-test-app-bf6f459f       1         1         1       3m22s
replicaset.apps/kube-test-nginx-6ffff769b5   1         1         1       3m22s

今回は、nginxのサービスをNodePortを利用して30081ポートを指定しているので、ブラウザで以下のURLにアクセスすると「Hello World」と表示されます。

http://[kubernetesクラスタのIP]:30081

 

kubernetesクラスタのIPがわからない場合は、以下のコマンドで確認できます。

$ kubectl cluster-info
Kubernetes master is running at https://192.168.1.12:6443
KubeDNS is running at https://192.168.1.12:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

まとめ

docker-composeはプログラミング開発時のローカル環境構築には便利ですが、本番運用ではkubernetesやAWSのECSのようなオーケストレーションサービスが必要になってきます。

今回は、docker-composeで行っていたことを取り敢えずkubernetesで実現してみることを目標にしてみましたので、実際に運用する場合とは異なるかもしれませんが、無事動作させることができました。

また、kubernetes側をNodePortを利用して公開しましたが、次回はIngressを利用して構築してみたいと思います。