docker-composeで作ったnginx + flaskをkubernetes+ingressに移す

docker-composeで作ったものをkubernetesで動かしてみたので備忘録として記事を書いた

# 環境
Windows 10
Docker Desktop for Windows 3.3.1(63152)

# 元のdocker-compose
NginxとFlaskのごく単純なリバースプロキシ
ソースコードはこちら
https://github.com/legacyworld/compose_to_k8s

localhostにアクセスするとこのように表示され
![index.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629456/dc22fbed-15cf-ac17-421d-2fd0d29c5c89.png)

localhost/api/v1/testにアクセスすると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629456/9102412c-8d26-55c3-66df-88d4c159c3c3.png)

と表示されるようになっている。

ファイル構成

```
compose_to_k8s
├─app
│  │  Dockerfile
│  │
│  └─src
│          server.py
│
└─web
    │  Dockerfile
    │  nginx.conf
    │
    └─html
            index.html
```
nginx.confで以下のように記述して分けている
`proxy_pass http://app:8080`でフォワードしている。
フォワード先の`app`はdocker-compose.ymlに記載している

```nginx.conf
http{
  server{
    listen 80;
    location / {
      root   /usr/share/nginx/html;
      index  index.html index.htm;
    }
    location /api/v1/ {
      proxy_request_buffering off;
      proxy_pass http://app:8080;
    }
  }
}
```

docker-compose.ymlはこちら

```docker-compose.yml
version: '3.7'
services:

  web:
    image: legacyworld/compose_to_k8s_web
    build:
      context: ./web
      dockerfile: Dockerfile
    container_name: web
    ports:
      - 80:80
    volumes:
      - ./web/html:/usr/share/nginx/html

  app:
    image: legacyworld/compose_to_k8s_app
    build:
      context: ./app
      dockerfile: Dockerfile
    container_name: app
    ports:
      - 8080:8080
    volumes:
      - ./app/src:/src
```

docker-composeだけだと`image: legacyworld/compose_to_k8s_web`は必須ではないが、Kubernetesでイメージを引っ張ってくるのに必要となるので追加してある

この状態で

```
docker-compose up --build -d
docker-compose push
```

と実行すればlocalhostへのアクセスが出来て、Docker Hubにイメージが保存される。

# Kubernetesのmanifestを作る
上記でnginxがやっていたフォワーディングをIngressにさせるようにする。
nginxは静的ファイル(htmlやjs,cssファイル)の置き場となる。

## Ingress
このページを見ればほぼほぼ書いてある
https://kubernetes.github.io/ingress-nginx/deploy/

このファイルをkubectlでapplyするだけ
https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.45.0/deploy/static/provider/cloud/deploy.yaml
今回はこれをingress_deploy.yamlとしてローカルに保存している

一つ注意点として、このdeployは結構時間がかかる（1分強）
なので下記のようにコマンドを実行してdeployが終わったことを確かめてから次に進む

```
# kubectl wait --namespace ingress-nginx   --for=condition=ready pod   --selector=app.kubernetes.io/component=controller   --timeout=120s
次のメッセージが出るまで待つ
pod/ingress-nginx-controller-6f5454cbfb-mkqlk condition met
```
## Ingressのmanifest

```manifest.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
spec:
  rules:
  - http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx-service
            port:
              number: 80
      - path: /api/v1
        pathType: Prefix
        backend:
          service:
            name: flask-service
            port:
              number: 8080
```

`- path`の部分からがnginx.confに書いていたフォワーディングの部分
KubernetesだとコンテナにServiceを作っていくので、フォワーディング先はservice名となっている
`/`へのアクセスはnginx-service、`/api/v1`へのアクセスはflask-serviceへフォワードされる

## nginxのmanifest
serviceとdeployの2種類の記述が必要

```manifest.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
  selector:
    run: web

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-deploy
spec:
  selector:
    matchLabels:
      run: web
  replicas: 1
  template:
    metadata:
      labels:
        run: web
    spec:
      containers:
      - name: front-end
        image: legacyworld/compose_to_k8s_web:latest
        env:
        - name: PORT
          value: "80"
        ports:
        - containerPort: 80
        volumeMounts:
        - mountPath: /usr/share/nginx/html/
          name: html
      volumes:
      - name: html
        hostPath:
          path: /run/desktop/mnt/host/c/source/compose_to_k8s/web/html
```

serviceはNodePortを指定
`run:web`で紐づけるコンテナを指定

deployは以下の通り
kubertenesでhostpathを使うのはよろしく無いですが、やり方をメモしておくために残してます

- spec->template->spec->containers->image
    - ここから引っ張ってくる。docker-composeで指定したもの
- volumeMounts
    - /usr/share/nginx/htmlを`name:html`のvolumesにマウントする
- hostPath
    - Windowsなのでこんな感じで長くなってしまう
    - `/run/desktop/mnt/host/c` = C:\

## flaskのmanifest

```manifest.yaml
apiVersion: v1
kind: Service
metadata:
  name: flask-service
spec:
  type: NodePort
  ports:
  - port: 8080
    targetPort: 8080
    protocol: TCP
  selector:
    run: app

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-deploy
spec:
  selector:
    matchLabels:
      run: app
  replicas: 1
  template:
    metadata:
      labels:
        run: app
    spec:
      containers:
      - name: back-end
        image: legacyworld/compose_to_k8s_app:latest
        volumeMounts:
        - mountPath: /src
          name: src
      volumes:
      - name: src
        hostPath:
          path: /run/desktop/mnt/host/c/source/compose_to_k8s/app/src
```

# 実行

```
kubectl apply -f manifest.yaml
```
で全く同じ結果が得られます。