Edited at

Kubernetesでのコンポーネント間の通信をまとめる


はじめに

CaaS(Container as a Service)を使った開発は増えてきていることと思います。

私自身もCaaS環境にてリリースを行うプロダクトを複数持っています。

そしてプロダクトを設計していくときに大事なのが、ネットワークですね。

マイクロサービス化していく中で、サービス同士での通信をする機会というのは増えていくと思います。

なので、今回Caas環境のサービスであるKubernetesの通信をまとめます。

なお、この記事に書くにあたり作成したコードは全て walk8243/kubernetes-communication にあります。


まとめる通信

まとめる対象の通信は以下の画像にある通りです。

通信は、緑の四角のコンテナから、青の四角のコンテナに向けて送信します。

まとめる通信の内容


デプロイファイル

Kubernetesにデプロイする際に使用するコードは全て、 walk8243/kubernetes-communication/deploy.yaml にあります。

この記事中では、Kubernetesを扱うことのできる minikube を使っています。

minikube が既にインストールされていることを前提に進めていきますので、インストールしていない場合は Minikubeのインストール - Kubernetes を参考にインストールしてください。


サーバーの基本設定

apiVersion: extensions/v1beta1

kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
template:
spec:
containers:
- name: nginx
image: nginx

上の設定をベースとして、ルートにアクセスされた際、 Hello World! と書かれた index.html を返します。


クライアントの基本設定

apiVersion: v1

kind: Pod
metadata:
name: client
spec:
containers:
- name: shell
image: centos
command:
- /bin/sh
- "-c"
- "-"
args:
- "while :; do sleep 1000; done"

shell というコンテナがあります。このコンテナからサーバーに対してアクセスします。

なお、クライアントコンテナには、以下のようなスクリプトでログインします。

kubectl exec -it client -c shell /bin/bash


実際に通信してみる

使用するコンテナなどをデプロイするためのスクリプトは用意しています。

よろしければGitHubから walk8243/kubernetes-communication をクローンしてお使いください。

# Linuxの場合

sh deploy.sh

# Windowsの場合
.\deploy.cmd

全てのケースにおいて、 Hello World! が返ってくれば通信成功です。


ケース①コンテナ間(同一ポッド内)

同じPodの中で、異なるContainer同士での通信を行います。

ここでは、ポートとUNIXドメインソケットの2つ方法をご紹介致します。

このケースの中でログインするコンテナは、 Deployment によって作成されたコンテナになるので、 Deployment を指定してログインします。

kubectl exec -it deploy/server -c shell --namespace=k8s-communication /bin/bash


ポートで接続


受信方法

ポートを使ったリクエストの受信には、nginxのDockerイメージで用意してくれている80番ポートをそのまま使います。


送信方法

curl コマンドで、リクエストを送信します。

curl localhost


UNIXドメインソケットで接続

UNIXドメインソケットを用いた通信に関しては、 Node.jsでもUNIXドメインソケットを使いたい#unixドメインソケットとは に書いていますので、是非ご覧ください。

以下にような設定で、通信を行うことができます。


受信方法

受信するためのnginxの設定は以下になります。


/etc/nginx/conf.d/socket.conf

server {

listen unix:/var/sock/nginx.sock;
root /usr/share/nginx/html;
index index.html index.htm;
}

なお、 /var/sock/nginx.sock は通信開始時に配置されるので、デプロイ時に用意するのはsockファイルを配置するためのフォルダです。

/var/sock はKubernetesの Volume を使って作成します。

apiVersion: extensions/v1beta1

kind: Deployment
metadata:
name: server
spec:
replicas: 1
template:
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: socket-dir
mountPath: /var/sock
volumes:
- name: socket-dir
emptyDir: {}


送信方法

以下のコマンドを叩くことで、 /var/sock/nginx.sock を使ってUNIXドメインソケットによるリクエストの送信ができます。

なお、送信に利用している ncyum install nc でインストールしています。

echo 'GET /' | nc -U /var/sock/nginx.sock

このときの /var/sock フォルダは、受信側の nginx コンテナと Volume で共有しています。


ケース②ポッド間(同一ネームスペース内)

ネームスペース k8s-communication 内にあるサービス server を使ってリクエストを送信します。

curl server

使用するネームサーバーの情報は以下のようになっています。


/etc/resolv.conf

nameserver 10.96.0.10

search k8s-communication.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

この中の k8s-communication.svc.cluster.local でドメイン解決が行われています。


ケース③ポッド間(ネームスペース違い)

ケース②と同じように curl server としても、リクエストは届きません。

届かない理由は、ドメイン解決ができないことにあります。

なので、ケース②と使用するネームサーバーを変えます。

今回のようなネームスペースが違う場合で使用するネームサーバーは svc.cluster.localcluster.local になります。

svc.cluster.local を使うケースは、 Service に向かって通信を行う場合です。

今回はケース②と同様にネームスペース k8s-communication 内にあるサービス server を使ってリクエストを送信したいため、 svc.cluster.local を使用します。

先ほどのケース②と同じようにネームサーバー k8s-communication.svc.cluster.local を見に行くことができるように、リクエスト送信時のホスト名を少し工夫します。

curl server.k8s-communication

これで、ネームサーバー svc.cluster.local 経由で、ネームサーバー k8s-communication.svc.cluster.local を見に行くことが可能になり、無事リクエストが到達できるようになります。

なお、ネームスペース k8s-communication-sub でのネームサーバーの情報は以下の通りです。


/etc/resolv.conf

nameserver 10.96.0.10

search k8s-communication-sub.svc.cluster.local svc.cluster.local cluster.local
options ndots:5


ケース④サーバー間

ここでは、minikubeの外からの通信を想定しています。

Kubernetesの中に作成したServiceを外部からアクセスできるようにするために、以下のコマンドを使用します。

このコマンドは、デプロイスクリプトの中に含まれています。

minikube service server --namespace=k8s-communication

上記のコマンドを実行後に、以下のコマンドを実行して、リクエスト先のURLを表示してください。

そこで表示されたURLをコピーして、ブラウザなどからアクセスしてください。

minikube service server --namespace=k8s-communication --url=true


おわりに

今回、4パターンの場所からサーバーに向けてリクエストを送信してみました。

近い距離の方が簡単に通信できますが、遠くなっても適切に設定してあげればちゃんと通信することは可能です。

また、Kubernetes内でDNSの設備はしっかりとしているので、ルールさえ押さえておけば、固定値で簡単に扱えます。

通信方法に怖がらず、何を作りたいかに合わせてモノを設計していきましょう!!