はじめに
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の設定は以下になります。
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ドメインソケットによるリクエストの送信ができます。
なお、送信に利用している nc
は yum install nc
でインストールしています。
echo 'GET /' | nc -U /var/sock/nginx.sock
このときの /var/sock
フォルダは、受信側の nginx
コンテナと Volume
で共有しています。
ケース②ポッド間(同一ネームスペース内)
ネームスペース k8s-communication
内にあるサービス server
を使ってリクエストを送信します。
curl server
使用するネームサーバーの情報は以下のようになっています。
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.local
か cluster.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
でのネームサーバーの情報は以下の通りです。
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の設備はしっかりとしているので、ルールさえ押さえておけば、固定値で簡単に扱えます。
通信方法に怖がらず、何を作りたいかに合わせてモノを設計していきましょう!!