ローカル環境でDocker(docker-compose)とKubernetes(minikube)を接続するtips
背景
機械学習のクラウドサービスを提供しているアダコテックでは、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側
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側
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