💻

ローカル環境でDocker(docker-compose)とKubernetes(minikube)を接続するtips

2022/02/13に公開

背景

機械学習のクラウドサービスを提供しているアダコテックでは、MLOpsの一環としてKubernetesクラスタの構築に取り組んでいます。
既にdockerコンテナベース(ECS)で稼働しているサービスがあり、

既存サービス(ECS) <-- API通信(http) --> 機械学習基盤(Kubernetes)

といったように既存サービスからAPI通信を使って機械学習基盤と接続して連携することにしています。

こうしたアプリケーションを開発する場合、自分のPC上で環境が再現できるとスムーズです。こういうときにどうしたらよいかという話をします。

実現手段

既存サービス(docker)と機械学習基盤(minikube)を起動させるのはコマンド1つでできるのでとても簡単ですが、問題はお互いをどうやって通信させるかです。
通信させるにはお互いがルーティング可能であり、かつIPアドレスもしくはドメイン名を知っている必要があります。まともにやろうとすると結構大変ですが、

minikubeをdocker driverで起動してminikube Nodeをdockerコンテナとしてdocker-compose側から見えるようにする

ことによって簡便に実現できます。

実行手順

  • minikubeをdocker driverで起動する
  • 既存のdockerコンテナをminikubeと同じネットワークに所属させる
  • minikubeのサービスはNodePortで公開する

実験

本当にお互いがdockerネットワーク上で見えることをサンプルで確認してみましょう。サンプルではminikubeとdocker-composeでechoサーバを立ち上げてみます。次のバージョンで動作確認をしています。

環境

  • ホストOS Ubuntu 20.04
  • minikube v1.24.0
  • docker 20.10.12
  • docker-compose 1.28.0

minikube側

sample.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: echo
          image: k8s.gcr.io/echoserver:1.4
          ports:
            - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: sample-nodeport
spec:
  type: NodePort
  selector:
    app: sample-app
  ports:
  - port: 8080
    name: "http-port"
    protocol: "TCP"
    targetPort: 8080
    nodePort: 30080

実行コマンド

$ minikube start --driver=docker
$ kubectl apply -f sample.yaml

docker-compose側

docker-compose.yaml
version: "3.7"
services:
  sample:
    image: k8s.gcr.io/echoserver:1.4
    tty: true
networks:
  default:
    external:
      name: minikube

このようにnetworkとしてminikubeという名前の外部ネットワークを利用することで、コンテナをminikubeと同じネットワーク上に所属させることができます。(minikubeの設定でネットワーク名を変更することもできますがデフォルトではminikubeという名前になります)

実行コマンド

$ docker-compose up -d

この状態で、dockerコンテナ側からminikube上のechoサーバが30080ポートで見えています。また、minikube上ではsampleコンテナが8080ポートで見えています。

確認作業

※表示は一例です。

minikube側

$ kubectl get service

NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
kubernetes        ClusterIP   10.96.0.1      <none>        443/TCP          89d
sample-nodeport   NodePort    10.96.21.182   <none>        8080:30080/TCP   17m

$ kubectl get pod

NAME                                 READY   STATUS    RESTARTS   AGE
sample-deployment-86cc64f858-v9wmn   1/1     Running   0          105m

$ kubectl exec --stdin --tty sample-deployment-86cc64f858-v9wmn -- /bin/bash

root@sample-deployment-86cc64f858-v9wmn:/# curl http://sample:8080/ -v
*   Trying 192.168.49.3...
* Connected to sample (192.168.49.3) port 8080 (#0)
> GET / HTTP/1.1
> Host: sample:8080
> User-Agent: curl/7.47.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: nginx/1.10.0
< Date: Sun, 13 Feb 2022 07:50:31 GMT
< Content-Type: text/plain
< Transfer-Encoding: chunked
< Connection: keep-alive
< 
CLIENT VALUES:
client_address=192.168.49.2
command=GET
real path=/
query=nil
request_version=1.1
request_uri=http://sample:8080/

SERVER VALUES:
server_version=nginx: 1.10.0 - lua: 10001

HEADERS RECEIVED:
accept=*/*
host=sample:8080
user-agent=curl/7.47.0
BODY:
* Connection #0 to host sample left intact
-no body in request-

docker-compose側

$ kubectl get service

NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
kubernetes        ClusterIP   10.96.0.1      <none>        443/TCP          89d
sample-nodeport   NodePort    10.96.21.182   <none>        8080:30080/TCP   17m

$ docker-compose exec sample sh

# curl http://minikube:30080/ -v
*   Trying 192.168.49.2...
* Connected to minikube (192.168.49.2) port 30080 (#0)
> GET / HTTP/1.1
> Host: minikube:30080
> User-Agent: curl/7.47.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: nginx/1.10.0
< Date: Sun, 13 Feb 2022 07:47:41 GMT
< Content-Type: text/plain
< Transfer-Encoding: chunked
< Connection: keep-alive
< 
CLIENT VALUES:
client_address=172.17.0.1
command=GET
real path=/
query=nil
request_version=1.1
request_uri=http://minikube:8080/

SERVER VALUES:
server_version=nginx: 1.10.0 - lua: 10001

HEADERS RECEIVED:
accept=*/*
host=minikube:30080
user-agent=curl/7.47.0
BODY:
* Connection #0 to host minikube left intact
-no body in request-# 

このように、dockerコンテナ上からhttp://minikube:30080/、minikube上からhttp://sample:8080/にアクセスできることを確認できました。

さいごに

今回はKubernetesとDockerの開発環境を簡易的な方法で接続するtipsを紹介しました。
ただし、この環境はあくまでPC上でアプリケーションの動作確認をするためのもので、商用利用にたえうるものでありません。プロダクション環境は然るべき形(ECSやEKS)で運用しましょう。一例ですが、弊社プロダクト環境ではではIngress+External DNSを使いサービスディスカバリを実現しています。

Discussion