【IBM Cloud k8s検証メモ】同じPOD内の2つのコンテナ間で連携する方法


Dockerfile を書くベスト・プラクティス」の中では、「ほとんどの場合、1つのコンテナの中で1つのプロセスだけ実行すべきです。アプリケーションを複数のコンテナに分離することは、水平スケールやコンテナの再利用を簡単にします。サービスとサービスに依存関係がある場合は、 コンテナのlinkを使います。」[1] とあるが、k8sには、このlinkの機能が提供されれいない。 唯一近いものでは、「Communicate Between Containers in the Same Pod Using a Shared Volume」[2]しか発見できなかった。

そこで、同一POD内で、2つのコンテナが通信する場合の方法についてのメモです。

Pod について

Podの概要について、見直してみると、次の様に記述されていました。[4]
「Podは、アプリケーションコンテナ(場合によっては複数のコンテナ)、ストレージリソース、一意のネットワークIP、およびコンテナの実行方法を管理するオプションをカプセル化します。 Podは、Kubernetesのアプリケーションの単一のインスタンスです。単一のコンテナか、緊密に結合され、リソースを共有する少数のコンテナで構成されています。」

この表現を PHPのアプリケーション実行環境の一部に置き換えると、次の様な図になります。 この中で、今回注目するのは、赤色矢印の部分の実現方法です。

解決策

それぞれのコンテナにIPアドレスが付与されるのではなく、Podで同じIPアドレスとなるのであれば、それと同時にlocalhostも共有していることになります。 つまり、Web Container の localhostに、AP Container の php-fpm もポートを開いてリクエスト待ちしていることになります。

実際に AP Container に入って、net-toolsをインストールして、netstat で調べると、Web Container の TCP 80番ポートが見えています。

$ kubectl exec -it frontend-deploy-1665229993-12gd0 -c app bash
root@frontend-deploy-1665229993-12gd0:/var/www# netstat -natp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      6/nginx: master pro
tcp6       0      0 :::9000                 :::*                    LISTEN      13/php-fpm.conf)

そこで、nginx側の設定で、もともと、dockerコマンドの --link オプションで 接続先コンテナを指定した部分を、次の様にlocalhostへ変更することで、接続が可能になります。

/etc/nginx/conf.d/default.conf
server {
    listen 80;
    index index.php index.html;
    root /var/www;

    location / {
        try_files $uri /index.php?$args;
    }

    location ~ \.php$ {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass localhost:9000;    <--- ここの部分は、--link で与えた名前からlocalhostへ変更する
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

k8sのyamlファイル

次のファイルが、kubectl へ与える Deployment のYAMLファイルの該当部分です。 永続ボリュームを共有する部分以外は、設定する必要はありません。

s061_web_app_deployment.ymlのspec部分抜粋
    spec:
      containers:
      - name: app
        image: registry.ng.bluemix.net/takara/app:v1
        env:
        - name: DB_USER
          value: root
        - name: DB_PASSWORD
          value: root
        resources: {}
        volumeMounts:
        - mountPath: /var/www
          name: www-vol
          subPath: www
      - name: web
        image: registry.ng.bluemix.net/takara/web:v2
        ports:
        - containerPort: 80
        resources: {}
        volumeMounts:
        - mountPath: /var/www
          name: www-vol
          subPath: www
      restartPolicy: Always
      volumes:
      - name: www-vol
        persistentVolumeClaim:
          claimName: mysql-claim1

参考資料

[1] Dockerfile を書くベスト・プラクティス http://docs.docker.jp/engine/userguide/eng-image/dockerfile_best-practice.html
[2] Communicate Between Containers in the Same Pod Using a Shared Volume https://kubernetes.io/docs/tasks/access-application-cluster/communicate-containers-same-pod-shared-volume/
[3] The containers in same pod shares the localhost, so you need not link containers, just use localhost:containerPort. https://stackoverflow.com/questions/27103763/kubernetes-configuration-to-link-containers#comment65978824_31768361
[4] Pod Overview https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/