kubernetesで冗長化されたReactサーバーの各ホストネームを取得して表示する方法

# 問題：Reactサーバのホスト名を取得する方法は？
あなたは以下のようにkubernetesを使ってReactサーバーのレプリカを生成し、冗長化しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367429/66c150e0-f84a-17e4-5b35-f1e2f616cb19.png)

**さて、同じIPアドレスにアクセスしても、実際には違うPodのReactサーバーにアクセスしていることを確認するために、各Reactサーバーのホストネームをレンダリング時に表示したいと思います。**

こんなとき、どうすれば良いでしょう？？
ちなみに、クライアントサイドで以下のコードで取得できるのは、今回取得したいものではないです。

```JavaScript
const hostname = window.location.hostname;
console.log(`Hostname: ${hostname}`);
```

例えば、Qiitaのページを開いているときに、ブラウザの開発者ツールを開いて以上を実行すると、以下が表示されます。
```shell
Hostname: qiita.com
```
これは、FQDNであり実際にはロードバランサーやプロキシーサーバのアドレスを示しています。実際にReactを動かしているサーバーやポッドのホスト名ではないですね。


（クライアントサイドレンダリングの）Reactのコードはブラウザ上で実行されていますので、クライアントサーバーのコードに最初から工夫がなされていなければ、ブラウザからでは、どうやったってポッドに振り分けられる前までのアドレスしか分かりません。


# 作戦：Pod起動時にホストネームをテキストファイルに書き込む
では、どうすれば、Reactサーバが置かれているPodのホストネームを取得できるのでしょうか？

泥臭いながら、考えられる作戦として「ポッド作成時にホストネームをテキストファイルに書き込み、それを読み込むようにする」があります。

具体的には以下です。

1. Reactアプリケーションをコンテナ化し、kubenetesのPod内でコンテナをデプロイする
1. コンテナ起動時に、ホストネームを記述したhostname.txtを生成し、/react-app/public/以下に保存
1. Reactのコンポーネントレンダリング時に、hostname.txtを読み込み、記述されたホストネームを表示

以下でその具体的手順を紹介します。


## 1. テキストファイルテキストを読み込むロジック
あとでPublicフォルダにホストネームが書かれたテキストファイルを置くので、先にそれを読み込み表示するロジックを作成しておきます。
```JavaScript
function App() {
  const [ hostname, setHostname] = useState("");
  const getHostname = async () => {
    const reponse = await fetch(`${process.env.PUBLIC_URL}/hostname.txt`);
    const text = await reponse.text();
    setHostname(text);
    return text;
  };
  useEffect(
    () => {getHostname();}, [hostname]
  );
  return (
      <div>{`pod name : ${hostname}`}</div>
    )

}
```

## 2. アプリケーションのコンテナ化
`create-react-app`コマンドなどでReactのアプリケーションを作ったあと、package.jsonなどがあるのと同じ階層に、以下のDocerfileとstartup.sh(コンテナ起動時に実行したいコマンドをまとめて書くためのファイル)を作成します。

```Dockerfile:Docerfile
FROM node:16.13.0-alpine AS builder

WORKDIR /usr/local/app

COPY . .

RUN yarn --frozen-lockfile && \
    yarn build

FROM nginx:1.20-alpine

COPY --from=builder /usr/local/app/build /usr/share/nginx/html

COPY startup.sh /startup.sh
RUN chmod 744 /startup.sh

CMD ["/startup.sh"]
```

以下のstartup.shが、コンテナ起動時に実行されますが、見ての通り、一行目でホストネームをhostname.txtへと書き込んでいます。
```shell:startup.sh
hostname >> /usr/share/nginx/html/hostname.txt
nginx -g "daemon off;"
```

次に、コンテナイメージをビルドしていきます。
コマンドは以下です。（DockerHubにプッシュしますので、先にDockerHubにサインアップしておく必要があります。以下、あなたのアカウント名をyournameとします。）

```shell
docker build --tag {yourname}/react-app:latest --no-cache . 
docker run --name react-app --rm --publish 3000:80 {yourname}/react-app:latest
docker push {yourname}/react-app:latest
```

ビルドしたイメージのDockerHubへUploadも終わり、これで準備は完了です。


## 3. kubenetesでPodを3つ作成し、Reactアプリケーションを冗長化する
kubernetesでPodを3つ作るには、以下のyamlファイルを作成します。ローカルで実行していますので、ここではminikube使っています。

```yaml:deployment-service-react.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: react-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: react-app
  template:
    metadata:
      labels:
        app: react-app
    spec:
      containers:
      - name: react
        image: {yourname}/react-app  # Edit
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: react-app
spec:
  type: NodePort
  selector:
    app: react-app
  ports:
    - port: 3000
      targetPort: 80
      protocol: TCP
      name: react-app
```

そして、以下のコマンドを実行します。
```shell
minikube start
kubectl apply --filename ./deployment-service-react.yaml
minikube service reputation-checker-react
```
で、アプリのデプロイとアドレスの公開までできます。

以下↓に自分の環境で実行したときのスクリーンショットを貼ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367429/259ca307-76e2-5aa8-10e7-41553d145033.png)
クロームのシークレットウィンドウを用いて同じアドレスにアクセスすると、表示されるPodの名前が異なることが分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367429/631d28d6-8125-8db0-44f0-e2c980ff18d0.png)



# まとめ
さて、以上の手順を経ることで、KubenetesでReactサーバが複数のPodに置かれ冗長化されたときの、各Reactサーバが置かれているPodのホスト名を表示することができることを示しました。

実際問題、ホストネームを表示したいときなんてかなり稀ですが、どうしても確認したくなった時は以上をお試しください。