これはなに
タイトルのとおり、Kubernetesクラスター外のIPアドレスに対してルーティングしてくれるServiceオブジェクトを作る方法を書きます。
KubernetesのServiceにはExternalNameというタイプがありまして、これを使うと、外部のサービスにDNS名でアクセスすることができます。これに対して、このエントリーでやりたいことは、IPアドレスを直に指定してアクセスするということです。
そんなことやりたいときなんてあります?と思われるかもしれませんが、自分の場合たまたまあったんです…。
TL;DR
Selectorの無いClusterIPタイプのServiceを作って、さらにEndpointオブジェクトでアクセス先のサービスのIPを指定すれば良いです。
(もっといいやり方があったら教えてください)
2018/09/06 追記:
ExternalNameでもIPを扱えるとのご指摘を頂いていますので、まずはそちらの方法を試したほうが良いと思います。ご指摘いただいている内容については本記事のコメント欄を参照ください。
やってみる
例えばクラスターの外部に、123.123.123.123というIPアドレスでMySQLが立っているとします。この場合、以下のような内容のmanifestで、ServiceとEndpointを作成します。
後でも書きますが、ServiceにSelectorが指定されていないところがポイントです。
apiVersion: v1
kind: Service
metadata:
name: mysqldb
spec:
type: ClusterIP
ports:
- protocol: TCP
port: 3306
---
apiVersion: v1
kind: Endpoints
metadata:
name: mysqldb
subsets:
- addresses:
# FIXME: use the right IP
- ip: 123.123.123.123
ports:
- protocol: TCP
port: 3306
このmanifestをクラスターにkubectl apply
します。
% kubectl apply -f ./mysql-external.yaml
service/mysqldb created
endpoints/mysqldb created
結果を確認すると、こんな感じになります。
% kubectl get service,endpoints
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4m
service/mysqldb ClusterIP 10.100.129.108 <none> 3306/TCP 2m
NAME ENDPOINTS AGE
endpoints/kubernetes 192.168.39.97:8443 4m
endpoints/mysqldb 123.123.123.123:3306 2m
Endpointsというオブジェクトは、Serviceに対して実際のリクエストの送信先を決めているオブジェクトで、Selecterを指定してServiceを作ると(多くの場合そうしていると思います)自動で生成されているものです。
今回は、Endpointsの定義を明示して、指定したIPアドレスが送信先となるようにしています。こうすることで外部のIPアドレスにリクエストを届けてくれるServiceオブジェクトができあがります。
試しにService詳細情報を表示してみます。
% kubectl describe service mysqldb
Name: mysqldb
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"mysqldb","namespace":"default"},"spec":{"ports":[{"port":3306,"protocol":"TCP"...
Selector: <none>
Type: ClusterIP
IP: 10.100.129.108
Port: <unset> 3306/TCP
TargetPort: 3306/TCP
Endpoints: 123.123.123.123:3306
Session Affinity: None
Events: <none>
Endpointというところに、上で指定したIPアドレスが設定されていることがわかります。
では、このServiceを使って、クラスターの中からMySQLに接続してみます。
以下のコマンドで、クラスター内にmysql-clientが入ったコンテナ(Pod)を上げて、DBに接続してみます。
% kubectl run -it --rm --image=mysql:8.0.3 --restart=Never mysql-client -- mysql -hmysqldb -uroot -ppassword
If you don't see a command prompt, try pressing enter.
...(中略)...
mysql>
この例は、ユーザーはroot、パスワードはpasswordとなっている想定です。また、-hはホスト名やIPアドレスを指定するオプションですが、上で作成したServiceの名前を指定すればOKです。ServiceがDBリクエストを送ってくれていることになります。
クライアントのコンテナから見ると、Serviceオブジェクト経由でアクセスするだけなので、クラスター内の他のPodにアクセスするのとやることは全く同じです。クライアントは何も変更せずに、Serviceオブジェクトの変更によって接続先を切り替えられるので、開発環境ではクラスター内にコンテナ化されたDBを配置してそこにつなぐなど、色々な使い方が考えられそうです。
クライアントが上がった後は、好きなようにクエリできます。
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| test |
+--------------------+
5 rows in set (0.27 sec)
以上で、Kubernetesクラスター外のIPアドレスに対してルーティングしてくれるServiceを作り、それを利用して外部のDBにアクセすることが出来ました。
繰り返しになりますが、他にいい方法があったらぜひコメントください。
参考リンク
- 公式ドキュメントのSelectorのないServiceオブジェクトに関する記述: