🌎

Linkerd による kubernetes クラスタ間通信

2024/10/05に公開

概要

Linkerd について

Linkerd は kubernetes でサービスメッシュを実現するための proxy です。

https://linkerd.io/2-edge/overview/

kubernetes 用のサービスメッシュでは Istio が有名ですが、linkerd も CNCF の Graduate project となっているためプロジェクトの成熟度は高く使用ユーザーも多いことが伺えます。Github Star は 2024/10 時点で約 10.6 k 程度。

linkerd は各 pod に proxy の役割を果たす proxy コンテナを inject し、proxy コンテナが linkerd の control plane と通信することでサービスメッシュを実現します(lstio における envoy と同じような感じ)。その他にも ドキュメント に記載されているように様々な機能がありますが、今回はこの中の Multi-cluster communication の機能を試してみます。

Linkerd におけるクラスタ間通信

linkerd では接続対象のクラスタの k8s service リソースを接続元のクラスタに複製し、この複製されたサービスを経由して接続先クラスタにアクセスする構成によりクラスタ間の通信を実現します(ドキュメントでは mirroringservice mirror 等と呼ばれている)。複製されたサービスの通信先は対象クラスタ上に存在する外部アクセス可能な Gateway となっており、これを経由して対象の svc, pod に接続しに行きます。そしてこの通信を制御するのが linkerd の micro proxy コンテナ (linkerd-proxy) となっています。linkerd ではこのような Gateway を経由してクラスタ間通信を実現する Hierarchical Network mode と、 gateway を使用せずクラスタの POD 間で直接通信を行う Flat Network mode (Pod-to-Pod 通信) もサポートされています。

画像
Hierarchical Network mode と Flat Network mode の構成 (Multi-cluster communication より引用)

https://linkerd.io/2-edge/features/multicluster/

まずは Hierarchical Network mode によるクラスタ間通信の動作を確認していきます。

構築の準備

実行環境

クラスタ間通信のためには 2 つ以上のクラスタが必要となるので、ローカル環境の openstack 上に独立したネットワークを作成し、各ネットワーク内に以下のようにクラスタを構築します。linkerd のドキュメントでは各クラスタを east, west としているのでここでのクラスタ名も合わせます。

画像
実行環境の構成図

ノード名や ip address は以下。

クラスタ名 ノード名 ip address floating ip kubernetes version
east k8s-m1 10.0.0.40 192.168.3.131 1.30.0
east k8s-w1 10.0.0.41 192.168.3.132 1.30.0
west kata-m1 10.0.0.30 192.168.3.206 1.30.0
west kata-w1 10.0.0.31 192.168.3.207 1.30.0

各ネットワークの CIDR は一致していますがネットワーク自体が独立しているため、各ノードの IP アドレスではお互いに通信することができない構成にしています。ただ openstack 側で floating IP を付加しているため、floating ip 経由ではアクセスできるようになっています。
今回は openstack 上のネットワークで上記の構成としていますが、同様のネットワーク構成であればパブリッククラウド上でも同様に検証できるかと思います(要確認)。

クラスタ間通信の前提条件

クラスタ間通信に必要な環境や前提条件は Requirements に書いてあるので見ていきます。

Two clusters.

これは 2 つのクラスタを用意すれば ok

A control plane installation in each cluster that shares a common trust anchor. If you have an existing installation, see the trust anchor bundle documentation to understand what is required.

2 つのクラスタでそれぞれ linkerd をインストールする際に共通の trust anchor を指定する必要があります。後述の手順の中で実行します。

Each of these clusters should be configured as kubectl contexts.

各クラスタを操作できるように kubeconfig 内に各クラスタの context を追加する必要があります。

Elevated privileges on both clusters. We’ll be creating service accounts and granting extended privileges, so you’ll need to be able to do that on your test clusters.

クラスタの Link (後述) を実行する際に Service Account などが作成されるので、リソースを作成できる権限が必要になります。今回はテストなのでクラスタの admin 権限で操作しますが、必要に応じて適切な権限を設定します。

Support for services of type LoadBalancer in the east cluster. Check out the documentation for your cluster provider or take a look at inlets. This is what the west cluster will use to communicate with east via the gateway.

クラスタ間通信で使用される Gateway は k8s svc リソースの LoadBalancer type として作成されますが、クラスタ外から通信できるように EXTERNAL-IP を設定する必要があります。なので上記の条件は EXTERNAL-IP を付加できるような provider が必要であることを示しています。
今回の検証では openstack 上に構築しているため、cloud-provider-openstack を使って openstack のロードバランサー (octavia) から IP アドレスを付加するようにします。ネットワーク構成によっては MetalLB とかで代用できる他、パブリッククラウド上で構築する場合は各クラウドが提供するロードバランサーとの連携機能が利用できます。

構築

クラスタ間通信までの流れは大まかに以下のようになります。

  1. Trust Anchor Bundle の作成
  2. linkerd CRD のインストール
  3. linkerd (control plane) のインストール
  4. linkerd viz のインストール(多分任意)
  5. linkerd multicluster のインストール
  6. クラスタの Link

基本的には Installing Multi-cluster Components の手順に沿って実行します。内容がやや被っていますが Multi-cluster communication にも上記より詳しめの手順やデモアプリを使った検証が記載されているので適宜参照。

Trust Anchor Bundle の作成

マルチクラスタ間の通信では共有の trust anchor certificate を利用して通信を暗号化するため、まずアプリケーションの証明書を作成する必要があります。これは実際にクラスタ間で linkerd-proxy が mTLS 通信を行う際に使用される模様。
証明書は openssl や step で作成することができますが、ドキュメントでは step で作成する手順 が記載されているのでそのまま実行します。step のインストールは Install step を参照。

CA 証明証を作成。

step certificate create root.linkerd.cluster.local ca.crt ca.key \
--profile root-ca --no-password --insecure

issuer 証明書とキーペアを作成。

step certificate create identity.linkerd.cluster.local issuer.crt issuer.key \
--profile intermediate-ca --not-after 8760h --no-password --insecure \
--ca ca.crt --ca-key ca.key

これにより以下の証明書、キーが作成されます。

ca.crt
ca.key
issuer.crt
issuer.key

Linkerd のインストール

Linkerd の各コンポーネントは helm でインストールできます。

https://linkerd.io/2-edge/tasks/install-helm/

east クラスタ上で以下を実行していきます。まずリポジトリを追加。

helm repo add linkerd-edge https://helm.linkerd.io/edge

後でいろいろ使うので linkerd の CLI をインストールします。手順は Step 1: Install the CLI にあるもので ok。

curl --proto '=https' --tlsv1.2 -sSfL https://run.linkerd.io/install-edge | sh

# Add path to .zshrc
echo 'export PATH=$HOME/.linkerd2/bin:$PATH' >> ~/.zshrc
source ~/.zshrc

次に linkerd CRD をインストールする必要があります。CRD も helm でインストールできますが、手元で試してみるとエラーが発生してうまくいかなかっため、ここでは linkerd CLI から直接インストールします。

# これでインストールできるはずだが以下のエラーとなる
$ helm install linkerd-crds linkerd-edge/linkerd-crds -n linkerd --create-namespace

Error: INSTALLATION FAILED: Unable to continue with install: CustomResourceDefinition "grpcroutes.gateway.networking.k8s.io" in namespace "" exists and cannot be imported into the current release: invalid ownership metadata; label validation error: missing key "app.kubernetes.io/managed-by": must be set to "Helm"; annotation validation error: missing key "meta.helm.sh/release-name": must be set to "linkerd-crds"; annotation validation error: missing key "meta.helm.sh/release-namespace": must be set to "linkerd"
CLI でインストール
linkerd install --crds | kubectl apply -f -

次に linkerd control plane をインストール。これにより linkerd control plane を構築するための pod 等が作成され、任意の pod に linkerd proxy を注入できるようになります。インストール時に先程作成した TrustAnchors, issuer のファイルを指定。

helm install linkerd-control-plane \
  -n linkerd \
  --set-file identityTrustAnchorsPEM=ca.crt \
  --set-file identity.issuer.tls.crtPEM=issuer.crt \
  --set-file identity.issuer.tls.keyPEM=issuer.key \
  linkerd-edge/linkerd-control-plane --create-namespace

次に linkerd-viz をインストール。これにより linkerd 用の dashboard やモニタリング関連の pod が作成されます。クラスタ間通信ではおそらく使用されませんが、ドキュメントに記載があるのでインストールしておきます。

helm install linkerd-viz -n linkerd-viz --create-namespace linkerd-edge/linkerd-viz

次に multicluster をインストール

linkerd multicluster install | kubectl apply -f -
helm で入れる場合
 helm install linkerd-multicluster -n linkerd-multicluster --create-namespace linkerd-edge/linkerd-multicluster

インストールによって linkerd-multicluster namespace が作成され、Gateway pod や svc などが作成されます。svc は type: LoadBalancer で作成されるので外部の provider 等によって EXTERNAL-IP が付加される必要があります。今回の検証では openstack 側から 外部アクセス可能な ip が付加されるように設定しているため、 EXTERNAL-IP として 192.168.3.179 が設定されました。

$ k get svc
NAME              TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                         AGE
linkerd-gateway   LoadBalancer   10.111.187.50   192.168.3.179   4143:30681/TCP,4191:30778/TCP   3h51m

east クラスタ上で上記の設定が終わったら west クラスタでも同様に multicluster のインストールまで実行します。

クラスタ間通信を行うためにはクラスタの link を実行する必要があります。link では link 元のクラスタ名、link 先の context 名が必要となるので事前に確認。

$ k config get-contexts
CURRENT   NAME   CLUSTER   AUTHINFO        NAMESPACE
*         east   east       admin           default
          west   sub-k8s   sub-k8s-admin   test

linkerd multicluster link コマンドで各クラスタを link するためのマニフェストを作成します。ここでは east のクラスタ名は east としているので --cluster-name east を指定してコマンドを実行、マニフェストが出力されれば ok

linkerd multicluster link --cluster-name east

これを west クラスタで apply することで link が完了します。

$ linkerd multicluster link --cluster-name east | kubectl --context=west apply -f -

secret/cluster-credentials-east created
secret/cluster-credentials-east created
link.multicluster.linkerd.io/east created
clusterrole.rbac.authorization.east.io/linkerd-service-mirror-access-local-resources-east created
clusterrolebinding.rbac.authorization.east.io/linkerd-service-mirror-access-local-resources-east created
role.rbac.authorization.east.io/linkerd-service-mirror-read-remote-creds-east created
rolebinding.rbac.authorization.east.io/linkerd-service-mirror-read-remote-creds-east created
serviceaccount/linkerd-service-mirror-east created
deployment.apps/linkerd-service-mirror-east created
service/probe-gateway-east created

各クラスタで linkerd multicluster check を実行して link が正常に完了したか確認。例えば以下では west クラスタが east クラスタの api server に接続できずにエラーとなっています。

$ linkerd multicluster check
linkerd-multicluster
--------------------
√ Link CRD exists
√ Link resources are valid
        * east
× remote cluster access credentials are valid
            * failed to connect to API for cluster: [east]: Get "https://10.0.0.40:6443/version?timeout=30s": dial tcp 10.0.0.40:6443: connect: no route to host
    see https://linkerd.io/2/checks/#l5d-smc-target-clusters-access for hints
× clusters share trust anchors
    Problematic clusters:
    * east: unable to fetch anchors: Get "https://10.0.0.40:6443/api/v1/namespaces/linkerd/configmaps/linkerd-config?timeout=30s": dial tcp 10.0.0.40:6443: connect: no route to host
    see https://linkerd.io/2/checks/#l5d-multicluster-clusters-share-anchors for hints
√ service mirror controller has required permissions
        * east
√ service mirror controllers are running
        * east
× probe services able to communicate with all gateway mirrors
        probe-gateway-east.linkerd-multicluster mirrored from cluster [east] has no endpoints
    see https://linkerd.io/2/checks/#l5d-multicluster-gateways-endpoints for hints
√ multicluster extension proxies are healthy
√ multicluster extension proxies are up-to-date
√ multicluster extension proxies and cli versions match

Status check results are ×

link では実行したクラスタ内の kubeconfig を読み取ってマニフェストに出力するため、kubeconfig 内の api server のアドレスはクラスタ外部から接続可能な ip address に指定する必要があります。実際に linkerd multicluster link --cluster-name east 実行時のマニフェストを見てみると data.kubeconfig に kubeconfig を base64 encode したものが書いてあります。base64 decode すると中身を確認可能。

kubeconfig 内の cluster.server を外部接続できる ip address に修正した上で再度 link を実行。west クラスタ側で linkerd multicluster check を実行してエラーが出なければ ok。

$ linkerd --context=west multicluster check
linkerd-multicluster
--------------------
√ Link CRD exists
√ Link resources are valid
        east
√ remote cluster access credentials are valid
        east
√ clusters share trust anchors
        east
√ service mirror controller has required permissions
        east
√ service mirror controllers are running
        east
√ probe services able to communicate with all gateway mirrors
        east
√ all mirror services have endpoints
√ all mirror services are part of a Link
√ multicluster extension proxies are healthy
√ multicluster extension proxies are up-to-date
√ multicluster extension proxies and cli versions match

Status check results are √

クラスタの link が正常に完了すると、マニフェストを kubectl apply クラスタ(ここでは west)側で links.multicluster.linkerd.io リソースが作成されます。このリソースでは接続先クラスタの情報や Gateway の external ip などの情報が含まれています。

$ k describe links.multicluster.linkerd.io east
Name:         east
Namespace:    linkerd-multicluster
Labels:       <none>
Annotations:  <none>
API Version:  multicluster.linkerd.io/v1alpha1
Kind:         Link
Metadata:
  Creation Timestamp:  2024-09-29T10:58:25Z
  Generation:          1
  Resource Version:    205716
  UID:                 456d4cff-b6bc-4d18-823a-2fd162ed64ec
Spec:
  Cluster Credentials Secret:  cluster-credentials-east
  Gateway Address:             192.168.3.179
  Gateway Identity:            linkerd-gateway.linkerd-multicluster.serviceaccount.identity.linkerd.cluster.local
  Gateway Port:                4143
  Probe Spec:
    Failure Threshold:  3
    Path:               /ready
    Period:             3s
    Port:               4191
    Timeout:            30s
  Remote Discovery Selector:
    Match Labels:
      mirror.linkerd.io/exported:  remote-discovery
  Selector:
    Match Labels:
      mirror.linkerd.io/exported:    true
  Target Cluster Domain:             cluster.local
  Target Cluster Linkerd Namespace:  linkerd
  Target Cluster Name:               east
Events:                              <none>

ちなみにこの状態では west → east は link されていますが east → west の link は完了していない状態なので、east クラスタ側で link リソースは作成されていません。

east クラスタ側
$ k get links.multicluster.linkerd.io
No resources found in linkerd-multicluster namespace.

east → west の link も行うには上記と同様に west クラスタ側で生成したマニフェストを east クラスタ側で kubectl apply する必要があります。

$ linkerd --context=west multicluster link --cluster-name sub-k8s | kubectl --context=east apply -f -

クラスタ間通信の動作確認

クラスタの Link まで完了したので、後はクラスタ間通信に使用する service リソースに mirror.linkerd.io/exported: "true" のラベルを設定して公開することで他クラスタから通信できるようになります。Multi-cluster communication ではデモアプリを使って動作確認を行っているのでこれを使ってもいいですが、今後の拡張性も含めてここではシンプルな nginx pod を使って検証します。
また、linkerd では pod 内に inject された linker-proxy コンテナが通信を制御するので、nginx pod 内にコンテナを入れる必要があります。これは linkerd inject コマンドで入れるか namespace に annotation を設定する ことで追加できるので、ここでは後者にします。
上記のリソースをまとめて作成するためのマニフェストは以下。

nginx.yml
---
apiVersion: v1
kind: Namespace
metadata:
  name: test-linkerd
  annotations:
    linkerd.io/inject: enabled

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: test-linkerd
  labels:
    mirror.linkerd.io/exported: "true"
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

---
kind: Service
apiVersion: v1
metadata:
  name: nginx-test
  namespace: test-linkerd
  labels:
    mirror.linkerd.io/exported: "true"
    app: nginx
spec:
  selector:
    app: nginx
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP

これを kubectl apply -f nginx.yml で east クラスタ側にデプロイします。クラスタ内に mirror.linkerd.io/exported: "true" のラベルが設定された svc が作成されると、link されているクラスタに通信用の mirror svc が作成されます。
実際やってみると svc を作成した namespace が link 先クラスタにないと作成されないようなので、west クラスタにも test-linkerd namespace を作成。すると west クラスタに [元サービス名]-[クラスタ名] という名前のサービスが自動で作成されていることが確認できます。

# east クラスタ
$ k get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
nginx-test   ClusterIP   10.108.193.135   <none>        80/TCP    20h

$ ctx west
Switched to context "west".

# west クラスタ
$ k get svc
NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
nginx-test-east   ClusterIP   10.100.61.162   <none>        80/TCP    20h

west クラスタ側で endpoint も同時に作成され、宛先には east 側の gateway サービスに設定された external-ip 192.168.3.179 が設定されます。

$ k get endpoints -o yaml
apiVersion: v1
items:
- apiVersion: v1
  kind: Endpoints
  metadata:
    annotations:
      mirror.linkerd.io/remote-gateway-identity: linkerd-gateway.linkerd-multicluster.serviceaccount.identity.linkerd.cluster.local
      mirror.linkerd.io/remote-svc-fq-name: nginx-test.test-linkerd.svc.cluster.local
    creationTimestamp: "2024-09-29T12:53:02Z"
    labels:
      mirror.linkerd.io/cluster-name: east
      mirror.linkerd.io/mirrored-service: "true"
    name: nginx-test-east
    namespace: test-linkerd
    resourceVersion: "222971"
    uid: 9e9893c8-6086-42e9-b9ec-774efa1ffa0b
  subsets:
  - addresses:
    - ip: 192.168.3.179
    ports:
    - port: 4143
      protocol: TCP
kind: List
metadata:
  resourceVersion: ""

実際に west → east クラスタへのクラスタ間通信ができるか確認するために、west クラスタにも linkerd-proxy を設定した pod を作成します。pod については通信確認できれば何でもいいので先程の nginx マニフェストを流用します。

nginx.yml
---
apiVersion: v1
kind: Namespace
metadata:
  name: test-linkerd
  annotations:
    linkerd.io/inject: enabled

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: test-linkerd
  labels:
    mirror.linkerd.io/exported: "true"
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

pod を作成して kubectl exec で nginx コンテナに接続、nginx-test-east サービスに向けて curl を実行します。

$ k get svc
NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
nginx-test-east   ClusterIP   10.100.61.162   <none>        80/TCP    20h

$ k exec -it nginx-7745d7cf5c-h9gp2 -c nginx -- bash

root@nginx-7745d7cf5c-h9gp2:/# curl nginx-test-east

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
root@nginx-7745d7cf5c-h9gp2:/#

nginx のレスポンスが返ってきているので正常に east クラスタの nginx pod に接続できていること確認できます。east クラスタの pod 側のログを見てみると、west クラスタの nginx pod の ip アドレス 10.244.1.57 からの通信がアクセスログに記録されています。

east 側の pod log
$ k logs nginx-d556bf558-lhc25 nginx
10.244.1.57 - - [29/Sep/2024:12:47:24 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.88.1" "-"

このときの経路は west クラスタの nginx pod → west クラスタの nginx-test-east svc → east クラスタの Gateway svc → east クラスタの nginx svc → east クラスタの nginx pod という経路になっています。


経路の概要

また、west クラスタ側の pod から定期的に curl を実行してみます。

while true; do curl nginx-test-east; sleep 1;done

このときの通信を east クラスタ側の linkerd viz の web で確認すると、gateway から対象の nginx pod に向けた通信が発生していることが確認できます。ちなみに prometheus, tap は linkerd-viz インストール時に作成される pod に対応。


east クラスタの linkerd web

一方で west クラスタ側の linkerd web もみてみると、具体的な宛先は表示されませんが 192.168.3.179 への通信が発生していることが確認できます。これは east クラスタの gateway の EXTERNAL-IP となっているので、こちら側からも nginx pod → gateway に向けた通信が発生していることが確認できます。


west クラスタの linkerd web

このように service mirror によって作成される svc にアクセスすることでクラスタを超えて pod に通信することが可能となります。
ちなみにクラスタ間の通信は pod に注入された linkerd-proxy を介して行われるため、linkerd-proxy が注入されていない pod からクラスタ間通信を行うとするとうまくいきません。

linkerd-proxy のない pod から curl を実行した結果
$ curl nginx-test-east -v
*   Trying 10.100.61.162:80...
* Connected to nginx-test-east (10.100.61.162) port 80 (#0)
> GET / HTTP/1.1
> Host: nginx-test-east
> User-Agent: curl/7.88.1
> Accept: */*
>
* Empty reply from server
* Closing connection 0
curl: (52) Empty reply from server

Pod-to-Pod 通信

linkerd のクラスタ間通信ではもう一つのモードとして Gateway を使用せず、各クラスタの Pod 間で直接通信を行う Flat network mode (Pod-to-Pod 通信) がサポートされています。Gateway を使用する方法と比較すると以下のメリットがあるようです。

  • Gateway を経由しないのでレイテンシが改善する。
  • Gateway を使う方法では LoadBalancer type のサービスに EXTERNAL-IP を設定するため、パブリッククラウドでは各種ロードバランサー等と組み合わせて使用する必要がある。Pod-to-Pod 通信ではこの部分をまるごと削除できるのでその分のコストが削減できる。
  • 通信間で workload identity が保持される。workload identity に関してはこのあたりに記事がある。

Pod-to-Pod 通信では Gateway を使用しないので EXTERNAL-IP が設定可能という前提条件はありませんが、代わりにクラスタ上の Pod が別クラスタの Pod に直接アクセス可能な Flat Network 上に各クラスタが構築されているという条件が必要になります。例えば AWS では pod にサブネット内の IP アドレスが割り当てられており、その IP アドレスにアクセスすると pod に到達できるような構成が該当します。
Flat Network では linkerd を使わなくても Pod 間で直接通信可能な状態になっているので、わざわざ linkerd を使ってクラスタ間通信する必要はないのでは?という疑問が湧いてきますが、Enterprise multi-cluster at scale: supporting flat networks in Linkerd にこれに対する回答が書いてあります。要は通常の k8s クラスタでlinkerd を使う動機と同様で、linkerd のセキュリティや可観測性を享受できるメリットがあるから、とのこと。

If pods are routable, why use Linkerd? For exactly the same reasons you’re using it within the cluster: to provide the security, reliability, and observability guarantees beyond what a baseline TCP connection provides.


上記の検証で使用した east, west クラスタではネットワークが分離されているので、ここでは west クラスタと同じネットワーク上に新しく north クラスタを設定します。

クラスタ名 ノード名 ip address floating ip kubernetes version
west kata-m1 10.0.0.30 192.168.3.206 1.30.0
west kata-w1 10.0.0.31 192.168.3.207 1.30.0
north linkerd-m1 10.0.0.32 192.168.3.208 1.30.0

pod-to-pod 通信の手順は以下に書いてあります。

https://linkerd.io/2-edge/tasks/pod-to-pod-multicluster/

multicluster install の際に --gateway=false を指定することで、gateway が作成されず pod-to-pod 通信のモードになります。

linkerd multicluster install --gateway=false | kubectl apply -f -
helm の場合
helm install linkerd-multicluster -n linkerd-multicluster --create-namespace linkerd-edge/linkerd-multicluster \
    --set gateway.enabled=false

クラスタの Link は gateway のときと同様ですが、こちらも --gateway=false を指定します。

linkerd --context west multicluster link --cluster-name=west --gateway=false | kubectl --context north apply -f -

動作確認のため west クラスタに以下の nginx pod, svc を作成します。

  • ローカルの環境では Flat network を構築するのが難しいので、ここでは pod で hostNetwork: true を指定して Pod の ip アドレスに node の ip アドレスに指定する。
  • サービスを公開するため、svc に mirror.linkerd.io/exported: remote-discovery のラベルを設定
---
apiVersion: v1
kind: Namespace
metadata:
  name: pod-to-pod-linkerd
  annotations:
    linkerd.io/inject: enabled

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: pod-to-pod-linkerd
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      hostNetwork: true
      containers:
      - name: nginx
        image: nginx:
        ports:
        - containerPort: 80

---
kind: Service
apiVersion: v1
metadata:
  name: nginx-test
  namespace: pod-to-pod-linkerd
  labels:
    mirror.linkerd.io/exported: remote-discovery
    app: nginx
spec:
  selector:
    app: nginx
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP

north クラスタには同じ namespace を作成

apiVersion: v1
kind: Namespace
metadata:
  name: pod-to-pod-linkerd
  annotations:
    linkerd.io/inject: enabled

作成された nginx pod は node と同じ ip アドレス 10.0.0.31 が割り当てられており、north クラスタからもアクセス可能な状態になっています。

NAME                     READY   STATUS    RESTARTS   AGE   IP          NODE      NOMINATED NODE   READINESS GATES
nginx-7b49b57ff8-gr7ls   1/1     Running   0          19m   10.0.0.31   kata-w1   <none>           <none>

# north クラスタノードから curl を実行
$ curl 10.0.0.31
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

north クラスタでは上記の pod にアクセスするための mirroring service が作成されます。svc 名は gateway のときと同じで [元サービス名]-[link クラスタ名] になります。

north クラスタ内
$ k get svc
NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
nginx-test-west   ClusterIP   10.108.191.96   <none>        80/TCP    22m

north クラスタに検証用の pod を作成し、先ほどと同様に north クラスタ内の svc にアクセスすると west クラスタの pod に到達します。

$ k exec -it nginx-6cfb64b7c5-p69bf -c nginx -- bash

root@nginx-6cfb64b7c5-p69bf:/# curl nginx-test-west
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

このときの west クラスタ側の nginx のログでは、上記の north クラスタの nginx pod が起動したノードの IP アドレス 10.0.0.32 からのアクセスログが記録されます。

10.0.0.32 - - [03/Oct/2024:16:55:03 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.88.1" "-"

Discussion