Service、Ingress、NetworkPolicy
目標
Serviceの3種類のポートを区別して指定し、named port・ヘッドレス・StatefulSetのDNS・Ingressのルーティング・NetworkPolicyの許可リストをマニフェストとして書けるようになります。
なぜ重要なのか
Serviceで出るミスは、ほぼ2つだけです。セレクターがPodのラベルと違うことと、3つのポートを混同することです。セレクターがずれると、エラーなくエンドポイントが空になり、クライアントには接続拒否だけが見えます。kubectl get endpointsが空かどうかを確認する習慣1つで、時間の大半を節約できます。
ポートは方向で覚えると混乱しません。portは入ってくる側(クライアントがServiceに接続するポート)、targetPortは出ていく側(ServiceがPodに接続するポート)、nodePortは外から入ってくる側(ノードで開くポート)です。
ヘッドレスServiceは、ロードバランシングをあきらめる選択です。仮想IPを作らず、DNSがPodのIPリストをそのまま返し、StatefulSetとペアにするとPodごとに固有の名前ができます。クライアントが特定のインスタンスを指名する必要があるワークロード(DBのプライマリ、シャード)に使います。
NetworkPolicyは、デフォルトは許可、ポリシーが付くと許可リストという2つの文に要約されます。そのため実務では、まずネームスペース全体にデフォルトの遮断を設定し、必要な通信だけを開けます。ポリシー同士は互いを上書きせず、和集合として加算される点も覚えておいてください。
ステップ
- ネームスペース
ckad-netを作成してください。Deploymentfrontend(レプリカ2、ラベルapp=frontend、イメージnginx:1.27、コンテナポート80に名前http)と、Servicefrontend(ClusterIP、port: 80、targetPort: 80、セレクターapp=frontend)を作成してください。 - Deployment
backend(レプリカ2、ラベルapp=backend、イメージnginx:1.27、コンテナポート8080に名前api)と、Servicebackend(ClusterIP、port: 80、targetPort: apiを名前で指定、セレクターapp=backend)を作成してください。 - Service
frontend-npを作成してください。タイプはNodePort、port: 80、targetPort: 80、nodePort: 30080、セレクターはapp=frontendです。 - ヘッドレスService
cache-hs(clusterIP: None、port: 6379、セレクターapp=cache)とStatefulSetcache(レプリカ3、serviceName: cache-hs、ラベルapp=cache、イメージnginx:1.27)を作成してください。そして、インデックス0のPodの完全なDNS名を、/root/ckad-net/dns.txtに1行で書いてください。 - Ingress
shopを作成してください。ingressClassName: nginx、ホストshop.ckad.local、パス/(pathTypePrefix) → Servicefrontendのポート80、パス/api(pathTypePrefix) → Servicebackendのポート80にします。 - NetworkPolicy
backend-allow-frontendを作成してください。対象はapp=backendのPod、policyTypes: [Ingress]、許可する送信元はapp=frontendのPod、ポートはTCPの8080です。 - NetworkPolicy
default-deny-ingressを作成してください。podSelector: {}(ネームスペース全体)、policyTypes: [Ingress]、ingressルールは空のままにします。この時点で、Servicebackendのセレクターが実際にPodを2つ以上選んでいるかを、kubectl get pods -n ckad-net -l app=backendで確認してください。
参考
kubectl create deployment frontend --image=nginx:1.27 --replicas=2 -n ckad-net --dry-run=client -o yamlでひな形を出力し、portsに名前を付けます。kubectl expose deployment frontend --port=80 --target-port=80 -n ckad-netは、セレクターを自動で埋めてくれます。- ステップ4のDNS名の形式:
<파드이름>.<서비스이름>.<네임스페이스>.svc.cluster.local(プレースホルダーはPod名、Service名、ネームスペースです) - よくある間違い1: NetworkPolicyで
podSelector(適用対象)とfrom[].podSelector(許可する送信元)を取り違えることです。 - よくある間違い2:
nodePortを30000未満で指定することです。デフォルトで許可される範囲は30000–32767です。 - よくある間違い3:
targetPortに名前を書くとき、Serviceではなくコンテナのポート名を書く必要があるということです。 - この環境にはIngressコントローラーと実際のCNIデータプレーンがないため、ルーティングとポリシーが実際に動作するかは確認できません。オブジェクトを正確に書くことが目標です。
DeploymentとClusterIP Serviceを作成する
ネームスペースckad-netを作成してください。Deployment frontend(レプリカ2、ラベルapp=frontend、イメージnginx:1.27、コンテナポート80に名前http)と、Service frontend(ClusterIP、port: 80、targetPort: 80、セレクターapp=frontend)を作成してください。
kubectl expose deploymentを使うか、Serviceを直接書きます。Serviceのセレクターが、Podのラベルとまったく同じでなければ、エンドポイントが付きません。コンテナのポートに名前を付けておくと、次のステップが楽です。
named portでtargetPortを接続する
Deployment backend(レプリカ2、ラベルapp=backend、イメージnginx:1.27、コンテナポート8080に名前api)と、Service backend(ClusterIP、port: 80、targetPort: apiを名前で指定、セレクターapp=backend)を作成してください。
コンテナのports[].nameに付けた名前を、ServiceのtargetPortに文字列として書きます。名前は15文字以下の小文字/数字/ハイフンである必要があります。
NodePortで外部に公開する
Service frontend-npを作成してください。タイプはNodePort、port: 80、targetPort: 80、nodePort: 30080、セレクターはapp=frontendです。
タイプをNodePortにして、nodePortを直接指定します。許可される範囲は30000–32767で、指定しなければ自動で割り当てられます。portとtargetPortはそのまま残ります。
ヘッドレスServiceとStatefulSetを作成する
ヘッドレスService cache-hs(clusterIP: None、port: 6379、セレクターapp=cache)とStatefulSet cache(レプリカ3、serviceName: cache-hs、ラベルapp=cache、イメージnginx:1.27)を作成してください。そして、インデックス0のPodの完全なDNS名を、/root/ckad-net/dns.txtに1行で書いてください。
ヘッドレスはclusterIP: Noneです。StatefulSetのserviceNameにそのService名を書いて初めて、PodごとのDNS名ができます。名前の規則は、Pod名・Service名・ネームスペースの順にドットでつなぎます。
Ingressでホストとパスのルーティングを設定する
Ingress shopを作成してください。ingressClassName: nginx、ホストshop.ckad.local、パス/(pathType Prefix) → Service frontendのポート80、パス/api(pathType Prefix) → Service backendのポート80にします。
networking.k8s.io/v1で、バックエンドはservice.nameとservice.port.numberで指定します。各パスにはpathTypeが必須です。ルールはrules[].hostとrules[].http.paths[]の構造です。
NetworkPolicyでフロントエンドだけを許可する
NetworkPolicy backend-allow-frontendを作成してください。対象はapp=backendのPod、policyTypes: [Ingress]、許可する送信元はapp=frontendのPod、ポートはTCPの8080です。
podSelectorはポリシーが適用される対象を、ingress[].from[].podSelectorは許可する送信元を指します。2つを取り違えやすいので注意してください。ポートも一緒に絞ります。
デフォルト遮断ポリシーとセレクターを点検する(総合)
NetworkPolicy default-deny-ingressを作成してください。podSelector: {}(ネームスペース全体)、policyTypes: [Ingress]、ingressルールは空のままにします。この時点で、Service backendのセレクターが実際にPodを2つ以上選んでいるかを、kubectl get pods -n ckad-net -l app=backendで確認してください。
空のpodSelector: {}は、ネームスペースのすべてのPodを意味します。ここにingressルールを1つも置かないと、デフォルトの遮断になります。そして、Serviceのセレクターが実際のPodを選んでいるかを、kubectl get pods -lで直接確認します。