Docker が動作しているホストのHostnameを表示するNginxコンテナ

## はじめに

Kubernetesで作業をしているときに、ブラウザで接続しているコンテナは、どのWorkerで動いているのか気になったため作成しました。
Workerをわざと停止して、接続先を確認する時に使っています。

## 動作確認した環境

- Raspberry Pi 4
- Ubuntu 20.04
- Docker 19.03.13

## イメージ

[https://hub.docker.com/r/yasthon/nginx-display-hostname](https://hub.docker.com/r/yasthon/nginx-display-hostname) にアップしています。

## 動かしてみる

ここでは、ホスト名「dockerhost.example.jp」の環境で実行しています。

```bash
$ docker pull yasthon/nginx-display-hostname
$ docker container run -d -v /etc/hostname:/usr/share/nginx/html/hostname -p 80:80 yasthon/nginx-display-hostname
$ curl http://localhost/index.sh
<html><head>
<title>dockerhost.example.jp</title>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
</head><body>
HOSTNAME : dockerhost.example.jp
</body></html>
```

ブラウザからも確認します。
```http://[DockerホストのIPアドレス]/index.sh```にアクセスします。

```
HOSTNAME : dockerhost.example.jp
```

## 動作

コンテナの起動時に```/etc/hostname``` をmountしています。
Nginxからshを動かして、hostnameを読んでいます。

## Dockerfile

ローカルでビルドする時に使うファイルです。

```Dockerfile:Dockerfile
FROM debian:buster-slim

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

COPY index.sh /usr/share/nginx/html/

RUN apt-get update && \
  apt-get install -y --no-install-recommends nginx fcgiwrap && \
  apt-get autoclean && \
  rm -rf /var/lib/apt/lists/* && \
  echo "daemon off;" >> /etc/nginx/nginx.conf && \
  rm -f /etc/nginx/sites-enabled/default && \
  chmod 744 /usr/share/nginx/html/index.sh && \
  chown www-data:www-data /usr/share/nginx/html/index.sh

CMD /etc/init.d/fcgiwrap start && nginx

```

普通にOSへインストールした時は簡単に動作したのに、コンテナで実行したら問題が発生しました。

- Nginxのdefault rootは```/usr/share/nginx/html/```だと思いこんでいたら、```/var/www/html```を参照していました。コンテナの用途が限定的なので、調査はせずに```/etc/nginx/sites-enabled/default```を削除することで解決しました。
- ```fcgiwrap.socket```が無かったです。CMDに ```/etc/init.d/fcgiwrap start ```を追加してコンテナ起動時に作成しています。
- permissionエラーでました。```chmod```と```chown```を追加しました。
- DockerHubからpullしたNginx Official イメージでは、思うように動作できなかったです。パッケージでインストールを行い動作させました。


## default.conf

Nginxの設定ファイルです。

```bash:default.conf
server {
  listen       80;
  server_name  localhost;
  location ~ \.sh$ {
    root   /usr/share/nginx/html/;
    include           /etc/nginx/fastcgi_params;
    fastcgi_index     index.sh;
    fastcgi_param     SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    fastcgi_pass      unix:/var/run/fcgiwrap.socket;
  }
}

```

Dockerfile に比べて、何の問題もなく。

## index.sh

mountしたhostnameファイルを読み込んで表示します。

```bash:index.sh
#!/bin/sh

host_name=$(cat hostname)

echo "Content-type:text/html"
echo ""
echo "<html><head>"
echo "<title>${host_name}</title>"
echo '<meta http-equiv="Content-type" content="text/html;charset=UTF-8">'
echo "</head><body>"
echo "HOSTNAME : ${host_name}"
echo "</body></html>"

```

同じく、何の問題もなく。

## ローカルでビルドする

イメージ名を「nginx-display-hostname」にしています。
3つのファイルを同じディレクトリに置いて、```docker build```を実行します。

```bash
$ docker image build -t nginx-display-hostname .
$ docker container run -d -v /etc/hostname:/usr/share/nginx/html/hostname -p 80:80 nginx-display-hostname
$ curl http://localhost/index.sh
<html><head>
<title>dockerhost.example.jp</title>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
</head><body>
HOSTNAME : dockerhost.example.jp
</body></html>
```

ブラウザからも確認します。
```http://[DockerホストのIPアドレス]/index.sh```にアクセスします。

```
HOSTNAME : dockerhost.example.jp
```

## Kubernetes上での動作

volumeMountsとvolumesを使用して、```/etc/hostname```をmountする必要があります。

```yaml:sample.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: nginx-prod # namespace の名前
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment # deployment の名前(replicaset の名前もこれになる)
  namespace: nginx-prod # namespace の名前
spec:
  selector:
    matchLabels: # ラベルがマッチしたPodを対象とするreplicasetの作成
      app: nginx-pod
  replicas: 2
  template: # Pod のテンプレート
    metadata:
      name: nginx-pod # Pod の名前
      namespace: nginx-prod # Pod のnamespace の名前
      labels: # Pod のラベル
        app: nginx-pod
    spec:
      containers: # コンテナの設定
        - name: nginx-container # コンテナの名前
          image: yasthon/nginx-display-hostname # イメージの名前
          env:
            - name: nginx-container
          ports:
            - containerPort: 80 # コンテナのポート
          volumeMounts:
            - name: file-hostname
              mountPath: /usr/share/nginx/html/hostname
      volumes:
        - name: file-hostname
          hostPath:
            path: /etc/hostname
```

Podを起動します。

```bash
$ kubectl apply -f sample.yaml
namespace/nginx-prod created
deployment.apps/nginx-deployment created
```

Podの名前を確認します。

```bash
$ kubectl get all -n nginx-prod
NAME                                    READY   STATUS    RESTARTS   AGE
pod/nginx-deployment-7ff4cc65cd-6r4qv   1/1     Running   0          3m52s
pod/nginx-deployment-7ff4cc65cd-djm9r   1/1     Running   0          3m52s

NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx-deployment   2/2     2            2           3m53s

NAME                                          DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-deployment-7ff4cc65cd   2         2         2       3m53s
```

Podの詳細情報でIPを確認します。

```bash
$ kubectl describe pod/nginx-deployment-7ff4cc65cd-djm9r -n nginx-prod | grep ^IP:
IP:           10.244.1.5
```

Worker上から、確認したIPへアクセスします。

```bash
$ curl http://10.244.1.5/index.sh
<html><head>
<title>worker.example.jp</title>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
</head><body>
HOSTNAME : worker.example.jp
</body></html>
```

Pod(nginx-deployment-7ff4cc65cd-6r4qv)は、ホスト worker.example.jp で起動していることが確認できました。
クラスター外からブラウザでアクセスする場合は、Serviceが必要になります。

## 最後に

すぐに作れるだろう思い作り始めたのですが、予想外に時間がかかりました。
必要としている方のお役に立てれば幸いです。