FlaggerはDeploymentと名前が異なるServiceは管理下に置かない

Flaggerは通常、Deploymentと同じ名前のServiceを作成します。もしも既存の同名Serviceがある場合、Flaggerはそれを管理下に置き(=リコンサイル対象とする)、spec.selector.appなどを書き換えることが知られています。

では、ServiceがDeploymentと関連しつつも、名前が異なる場合はどうなるでしょうか。Flaggerはそれを認識し、管理下に置くのでしょうか。それとも管理することはないのでしょうか。検証してみました。

## 検証環境

- Kubernetes: Kind v1.32.2
- Flagger: v1.41.0
- Gateway API: v1.2.0
- Envoy Gateway: v1.3.2

## 検証方法

1. 検証環境のセットアップ
   - Kindクラスタを作成
   - Gateway API (v1.2.0)をインストール
   - Cert Manager (v1.17.1)をインストール
   - Envoy Gateway (v1.3.2)をインストール
   - GatewayClassとGatewayを作成
   - Flagger (v1.41.0)をインストール

   <details>
   <summary>セットアップ手順の詳細</summary>

   1. Kindクラスタの作成
      ```sh
      kind create cluster
      ```
   2. Gateway API（v1.2.0）のインストール
      ```sh
      kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml
      ```
   3. Cert Manager（v1.17.1）のインストール
      ```sh
      kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.17.1/cert-manager.yaml
      kubectl wait --for=condition=available --timeout=60s deployment/cert-manager -n cert-manager
      kubectl wait --for=condition=available --timeout=60s deployment/cert-manager-webhook -n cert-manager
      kubectl wait --for=condition=available --timeout=60s deployment/cert-manager-cainjector -n cert-manager
      ```
   4. Envoy Gateway（v1.3.2）のインストール
      ```sh
      kubectl apply --server-side -f https://github.com/envoyproxy/gateway/releases/download/v1.3.2/install.yaml
      kubectl wait --for=condition=available --timeout=600s deployment/envoy-gateway -n envoy-gateway-system
      ```
   5. GatewayClassとGatewayの作成
      ```sh
      kubectl apply -f manifests/gatewayclass.yaml
      kubectl wait --for=condition=accepted --timeout=60s gatewayclass/eg
      kubectl apply -f manifests/gateway.yaml
      kubectl wait --for=condition=programmed --timeout=60s gateway/eg
      ```

      #### gatewayclass.yaml
      ```yaml
      apiVersion: gateway.networking.k8s.io/v1
      kind: GatewayClass
      metadata:
        name: eg
      spec:
        controllerName: gateway.envoyproxy.io/gatewayclass-controller
        parametersRef:
          group: gateway.envoyproxy.io
          kind: EnvoyProxy
          name: clusterip-config
          namespace: envoy-gateway-system
      ---
      apiVersion: gateway.envoyproxy.io/v1alpha1
      kind: EnvoyProxy
      metadata:
        name: clusterip-config
        namespace: envoy-gateway-system
      spec:
        provider:
          type: Kubernetes
          kubernetes:
            envoyService:
              type: ClusterIP
      ```

      #### gateway.yaml
      ```yaml
      apiVersion: gateway.networking.k8s.io/v1
      kind: Gateway
      metadata:
        name: eg
      spec:
        gatewayClassName: eg
        listeners:
          - name: http
            protocol: HTTP
            port: 80
      ```
   6. Flagger（v1.41.0）のインストール
      ```sh
      kubectl apply -f https://raw.githubusercontent.com/fluxcd/flagger/v1.41.0/artifacts/flagger/crd.yaml
      kubectl get ns flagger-system || kubectl create ns flagger-system
      helm repo add flagger https://flagger.app
      helm upgrade -i flagger flagger/flagger \
        --version 1.41.0 \
        --namespace flagger-system \
        --set prometheus.install=false \
        --set meshProvider=gatewayapi:v1 \
        --set metricsServer=none
      ```
   </details>
2. 以下のリソースをデプロイ:
   - Deployment名: `podinfo` 
   - Service名: `podinfo-different-name` (Deployment名と異なる)
   - Canary名: `podinfo` (Deployment名と同じ)

   ```sh
   kubectl apply -f manifests/deployment.yaml
   kubectl apply -f manifests/service.yaml
   kubectl apply -f manifests/canary.yaml
   ```

   <details>
   <summary>デプロイしたマニフェストファイルの内容</summary>

   ### deployment.yaml
   ```yaml
   apiVersion: apps/v1
   kind: Deployment
   metadata:
     name: podinfo
   spec:
     replicas: 2
     selector:
       matchLabels:
         app: podinfo
     template:
       metadata:
         labels:
           app: podinfo
       spec:
         containers:
         - name: podinfo
           image: stefanprodan/podinfo:latest
           ports:
           - containerPort: 9898
   ```

   ### service.yaml
   ```yaml
   apiVersion: v1
   kind: Service
   metadata:
     name: podinfo-different-name
   spec:
     selector:
       app: podinfo
     ports:
     - port: 9898
       targetPort: 9898
   ```

   ### canary.yaml
   ```yaml
   apiVersion: flagger.app/v1beta1
   kind: Canary
   metadata:
     name: podinfo
   spec:
     targetRef:
       apiVersion: apps/v1
       kind: Deployment
       name: podinfo
     progressDeadlineSeconds: 60
     service:
       port: 9898
       targetPort: 9898
       gatewayRefs:
         - name: eg
           namespace: default
     analysis:
       interval: 1s
       threshold: 5
       maxWeight: 50
       stepWeight: 10
   ```
   </details>

## 検証結果

### デプロイ後のServiceリソース

```sh
$ kubectl get services
NAME                     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
kubernetes               ClusterIP   10.96.0.1       <none>        443/TCP    8m30s
podinfo                  ClusterIP   10.96.126.169   <none>        9898/TCP   5m39s
podinfo-canary           ClusterIP   10.96.20.177    <none>        9898/TCP   5m49s
podinfo-different-name   ClusterIP   10.96.141.217   <none>        9898/TCP   6m12s
podinfo-primary          ClusterIP   10.96.130.137   <none>        9898/TCP   5m49s
```

注意: `podinfo` サービス（Deploymentと同名）は、Canaryリソースが作成されてから少し遅れて作成されます。

### 元のService（podinfo-different-name）

```yaml
apiVersion: v1
kind: Service
metadata:
  # ownerReferencesなし - Flaggerに管理されていない
  name: podinfo-different-name
  namespace: default
spec:
  # セレクタは変更されていない
  selector:
    app: podinfo
  ports:
  - port: 9898
    targetPort: 9898
```

### Flaggerが作成したpodinfo-primaryサービス

```yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: podinfo-primary
  name: podinfo-primary
  namespace: default
  ownerReferences:
  - apiVersion: flagger.app/v1beta1
    blockOwnerDeletion: true
    controller: true
    kind: Canary
    name: podinfo
    uid: c388a7b6-6dc9-4aa9-b9f1-e55ad8f31575
spec:
  selector:
    app: podinfo-primary
  ports:
  - name: http
    port: 9898
    targetPort: 9898
```

### Flaggerが作成したpodinfo-canaryサービス

```yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: podinfo-canary
  name: podinfo-canary
  namespace: default
  ownerReferences:
  - apiVersion: flagger.app/v1beta1
    blockOwnerDeletion: true
    controller: true
    kind: Canary
    name: podinfo
    uid: c388a7b6-6dc9-4aa9-b9f1-e55ad8f31575
spec:
  selector:
    app: podinfo
  ports:
  - name: http
    port: 9898
    targetPort: 9898
```

### Flaggerが作成したDeploymentと同名のサービス（podinfo）

```yaml
apiVersion: v1
kind: Service
metadata:
  annotations:
    helm.toolkit.fluxcd.io/driftDetection: disabled
    kustomize.toolkit.fluxcd.io/reconcile: disabled
  labels:
    app: podinfo
  name: podinfo
  namespace: default
  ownerReferences:
  - apiVersion: flagger.app/v1beta1
    blockOwnerDeletion: true
    controller: true
    kind: Canary
    name: podinfo
    uid: c388a7b6-6dc9-4aa9-b9f1-e55ad8f31575
spec:
  selector:
    app: podinfo-primary  # プライマリにトラフィックをルーティング
  ports:
  - name: http
    port: 9898
    targetPort: 9898
```

## 結論

検証の結果、以下のことが確認できました：

1. Flaggerは、Deployment名と異なる名前のServiceを管理対象として認識しない
   - Deployment `podinfo` と一致しない名前の Service `podinfo-different-name` は、Flaggerによって制御・変更されなかった
2. Flaggerは独自のサービスを新たに作成する
   - Canaryリソースをデプロイすると、Flaggerは以下の3つのServiceを作成した：
     - `podinfo-primary` - プライマリバージョンのPodにトラフィックを送るサービス
     - `podinfo-canary` - カナリアバージョンのPodにトラフィックを送るサービス
     - `podinfo` - Deploymentと同名のサービス（少し遅れて作成）。これが実際のエンドユーザートラフィックを受け取り、内部的には`podinfo-primary`にルーティングする
   - これらのServiceはすべてFlaggerによって管理され、ownerReferencesが設定されている
3. 元のServiceは変更されない
   - `podinfo-different-name` Serviceは、Flaggerによって変更されることなく元の状態のまま残っている
   - セレクタやその他の設定も変更されていない
4. Flaggerの内部メカニズム
   - Flaggerは、Deploymentと同名のサービス（`podinfo`）を作成してエンドユーザートラフィックを受け取り、内部的に`podinfo-primary`と`podinfo-canary`の間でトラフィックを制御する
   - この制御により、段階的にトラフィックをカナリアバージョンに移行するCanaryデプロイメントを実現している

この検証から、Flaggerはデプロイメント名と一致しないサービスを認識せず、管理下に置かないことが明らかになりました。これにより、Flaggerによる自動管理を避けたい場合は、Deployment名とは異なる名前でServiceを作成することが有効な戦略であることが確認できました。

## お知らせ

弊社Craftsman Softwareでは、Kubernetesやクラウドネイティブに興味のあるエンジニアを募集しています！詳細は[採用ページ](https://craftsman-software.com/recruit)をご覧ください。

https://craftsman-software.com/recruit