はじめに
前回は、StatefulSetと永続ボリューム(PV/PVC)を使い、データが消えないDBコンテナを起動しました。
しかし、データベースはアプリケーションから接続されて初めて価値があります。そのため今回はアプリコンテナとDBコンテナの接続方法を学びます。
前提条件
- Kubernetesで、Pod同士をどうやって通信させるのか仕組みを知りたい方
- DBコンテナを立てることはできたが使用出来ていない状態の方
アプリコンテナとDBコンテナを接続するための基本概念
コンテナ同士を接続する方法はいくつかありますが、Kubernetesでは推奨される接続方法が決まっており、単純な接続方法ではうまくいきません。特にDB接続では、ポート番号や認証情報(Secret)をアプリコンテナに適切に渡す必要があるほか、DBコンテナの動的なスケーリングや再起動によるIPアドレスの変動が接続安定性の課題となります。
このような固有の課題を解決するためには、Kubernetesのネットワーク層におけるServiceやDNSの仕組みを理解することが重要です。この記事では、DB接続における安定性を向上させるために、ServiceとDNSの利用方法に焦点を当て、その役割や実践的な活用法を解説します。
直接接続の問題点
最も単純な接続方法は、DBコンテナ(Pod)のIPアドレスを調べ、アプリの設定ファイルに直接記述する方式です。しかし、この方法には致命的な問題があります。Podは再起動するとIPアドレスが変わってしまうという点です。ノードの障害、設定変更による再デプロイ、負荷分散による再スケジューリングなどの問題が発生すると、古いPodは破棄され、新しいPodが作られます。この時、IPアドレスは変わってしまいます。
直接接続ではDBが再起動するたびにアプリの設定ファイルを書き換え、再起動しなければならず手間がかかります。
Serviceの仕組み
Kubernetesでは、PodのIPアドレスが変動する問題を解決するために「Service」というリソースを利用します。Serviceは一意の名前を持ち、内部DNSを介してアクセス可能です。これにより固定的なDNS名を提供することができ、アプリケーションはこのDNS名を使用して安定した通信を実現できます。
KubernetesのDNSの仕組み
Serviceを利用することで、PodのIPアドレスが変わる問題を解決できます。しかし、ServiceのIPアドレスをアプリケーションに設定する手間が残ります。この問題を解決するのがKubernetesのDNSです。通常、DNSはドメイン名とIPアドレスを対応させる役割を持っています。一方、Kubernetesクラスターには専用のDNSサーバーが標準で組み込まれており、Service名とIPアドレスを対応づける機能を提供します。Kubernetesでは、Serviceを作成すると、そのサービス名とIPアドレスの対応が自動的にDNSサーバーに登録されます。
この仕組みによりKubernetesでのPod間の接続は完全修飾ドメイン名(<Service名>.<Namespace名>.svc.cluster.local)を使用して実現できます。サービス名とネームスペース名を指定することで、Pod間通信が可能になります。
また、本記事ではDNSの仕組みの理解のために完全修飾ドメイン名を使用していますが、同じネームスペース内に存在するアプリケーションからServiceに接続する際にはサービス名だけで接続が出来ます。
YAMLファイルの作成例
これまで、アプリコンテナとデータベースコンテナの接続方法について解説しました。ここからは、具体的なYAMLファイルの例を示し、ServicesやKubernetesのDNSをどのように設定するのかについて解説します。
DBコンテナ側の設定
DBコンテナの設定(接続されるコンテナ)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: "mysql-service"
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: mysql # (A) Serviceはこのラベルを基準にPodを探す。
template:
metadata:
labels:
app.kubernetes.io/name: mysql # (A) Serviceに見つけてもらうためのラベル。
spec:
containers:
- name: mysql-container
image: mysql:8.0
env:
# ... 省略 ...
ports:
- containerPort: 3306 # (B) このPodが待ち受けるポート。ServiceのtargetPortと一致させる。
volumeClaimTemplates:
- metadata:
name: mysql-storage
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "standard"
resources:
requests:
storage: 1Gi
Serviceの設計(接続の窓口)
apiVersion: v1
kind: Service
metadata:
name: mysql-service # KubernetesのDNSに登録される名前。この名前を使って他のコンテナからアクセス可能になる。
spec:
selector:
app.kubernetes.io/name: mysql # (A) このラベルを持つPodを探す。
ports:
- protocol: TCP
port: 3306 # クライアントが接続するポート番号。
targetPort: 3306 # (B) Podのコンテナが待ち受けているポート。StatefulSetのcontainerPortと一致させる。
- metadata.name: mysql-service :サービス名がKubernetesのDNSに登録されます。このサービス名とネームスペース名があれば他のコンテナから接続ができます。
- selector:Serviceが接続先のPodを決定するためのラベルセレクターです。ここでは、`app.kubernetes.io/name: mysql` というラベルが付けられたPodを対象とします。このラベルセレクターを使用してServiceは指定されたラベルを持つすべてのPodに接続します。
- port: 3306:クライアントが接続するポート番号です。アプリケーションはこのポートに向けて通信します。
- targetPort: 3306:Serviceが受け取った通信を転送するPodの内部ポート番号です。ここでは3306 番ポートに転送されます。
アプリコンテナ側の設定
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
labels:
app: wordpress # Podの識別用ラベル。
spec:
# ... 省略 ...
template:
metadata:
labels:
app: wordpress
spec:
containers:
- image: wordpress:6.2.1-apache
name: wordpress
env:
- name: WORDPRESS_DB_HOST
value: mysql-service.default.svc.cluster.local # 完全修飾ドメイン名を使用してDNSで解決する。
# ... 省略(DBのパスワードやユーザーの設定) ...
- name: DB_PORT
value: "3306"
- value: mysql-service:Serviceの名前を元にDNSを使ってIPアドレスを解決し、そのIPアドレスに対して接続を試みるための設定です。
おわりに
今回は、ServiceとDNSを活用したコンテナ同士の接続方法について解説しました。これらの技術を利用することで、PodのIPアドレスが変動しても影響を受けることなく、安定した接続を実現できます。
次回からは、Kubernetes内部のデータベースコンテナを外部に公開する方法について、2回にわたり解説していきます。
参考文献
https://kubernetes.io/docs/concepts/services-networking/service/
https://kubernetes.io/ja/docs/concepts/services-networking/dns-pod-service/
https://kubernetes.io/docs/tutorials/stateful-application/mysql-wordpress-persistent-volume/

