Kubernetes: FlaggerのCanaryリソースでhostsフィールドは省略できる

## TL;DR

FlaggerのCanaryリソースでは、**`hosts`フィールドを省略しても問題なく動作します。**

Gateway APIと連携する場合でも、`hosts`フィールドを指定せずに`gatewayRefs`のみを設定すれば、HTTPRouteが正常に生成され、カナリアリリースが正常に機能します。

## 背景と目的

Flaggerを使ったカナリアリリースでGateway APIと連携する場合、通常は以下のように`hosts`フィールドを指定します：

```yaml
spec:
  service:
    port: 9898
    targetPort: 9898
    hosts:
      - podinfo.example.com
    gatewayRefs:
      - name: gateway-name
        namespace: gateway-namespace
```

しかし、`hosts`フィールドが必須なのか、省略した場合どうなるのかが明確ではありませんでした。この記事では、`hosts`フィールドを省略した場合の動作を検証します。

## 環境

検証には以下の環境を使用しました：

- Kubernetes: Kind クラスター
- Gateway API: Envoy Gateway
- Flagger: 最新版

## 検証手順

以下の手順で検証を行います。

### 1. Gatewayリソースの作成

まず、基本的なGatewayリソースを作成します：

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: eg
spec:
  gatewayClassName: eg
  listeners:
    - name: http
      protocol: HTTP
      port: 80
```

```bash
kubectl apply -f gateway.yaml
```

### 2. 初期バージョンのDeploymentを作成

テスト用のアプリケーション（podinfo）をデプロイします：

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: podinfo
  labels:
    app: podinfo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: podinfo
  template:
    metadata:
      labels:
        app: podinfo
    spec:
      containers:
      - name: podinfo
        image: stefanprodan/podinfo:6.0.0
        imagePullPolicy: IfNotPresent
        ports:
        - name: http
          containerPort: 9898
          protocol: TCP
        resources:
          limits:
            cpu: 500m
            memory: 256Mi
          requests:
            cpu: 100m
            memory: 64Mi
        livenessProbe:
          httpGet:
            path: /healthz
            port: 9898
          initialDelaySeconds: 5
          timeoutSeconds: 5
        readinessProbe:
          httpGet:
            path: /readyz
            port: 9898
          initialDelaySeconds: 5
          timeoutSeconds: 5
```

```bash
kubectl apply -f deployment.yaml
```

### 3. hostsフィールドを省略したCanaryリソースを作成

ここが本検証の核心部分です。`hosts`フィールドを省略したCanaryリソースを作成します：

```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
    # hosts フィールドなし
    gatewayRefs:
      - name: eg
        namespace: default  # 実際の名前空間に合わせて変更してください
  analysis:
    interval: 1s
    threshold: 5
    maxWeight: 50
    stepWeight: 10
```

```bash
kubectl apply -f canary.yaml
```

### 4. Canaryリソースの状態確認

Canaryリソースが正常に初期化されたことを確認します：

```bash
kubectl get canary podinfo
```

出力例：
```
NAME      STATUS        WEIGHT   LASTTRANSITIONTIME
podinfo   Initialized   0        2025-04-15T07:05:52Z
```

また、HTTPRouteが正常に生成されたことを確認します：

```bash
kubectl get httproute podinfo
```

出力例：
```
NAME      HOSTNAMES   AGE
podinfo               2m
```

注目すべき点は、`HOSTNAMES`が空であることです。これは`hosts`フィールドを省略したため、特定のホスト名が設定されていないことを示しています。

### 5. HTTPリクエストの確認

Envoy Gatewayが生成したサービスを特定し、そこにリクエストを送信します：

```bash
# サービス名を取得
SERVICE_NAME=$(kubectl get service -n envoy-gateway-system -l gateway.envoyproxy.io/owning-gateway-namespace=default -o jsonpath='{.items[0].metadata.name}')
echo "Service name: ${SERVICE_NAME}"

# curlを使用してHTTPリクエストを送信
kubectl run curl-test --image=curlimages/curl --restart=Never -i --rm -- \
  curl -v http://${SERVICE_NAME}.envoy-gateway-system.svc.cluster.local
```

出力例（一部抜粋）：
```
Service name: envoy-default-eg-xxxxxxxx
{
  "hostname": "podinfo-primary-xxxxxxxxxx-xxxxx",
  "version": "6.0.0",
  "message": "greetings from podinfo v6.0.0",
  ...
}
```

これにより、`hosts`フィールドを省略しても、Gateway経由でアプリケーションにアクセスできることが確認できました。

### 6. バージョンアップデート

次に、Deploymentを更新して新バージョンをデプロイします：

```bash
kubectl patch deployment podinfo -p '{"spec":{"template":{"spec":{"containers":[{"name":"podinfo","image":"stefanprodan/podinfo:6.1.0"}]}}}}'
```

### 7. カナリアリリースの進行確認

Flaggerがカナリアリリースを進行させていることを確認します：

```bash
kubectl get canary podinfo -w
```

出力例：
```
NAME      STATUS      WEIGHT   LASTTRANSITIONTIME
podinfo   Progressing 0        2025-04-15T07:06:10Z
podinfo   Progressing 10       2025-04-15T07:06:20Z
podinfo   Progressing 20       2025-04-15T07:06:30Z
...
podinfo   Promoting   50       2025-04-15T07:07:00Z
...
podinfo   Succeeded   0        2025-04-15T07:07:20Z
```

### 8. 更新後のHTTPリクエスト確認

最後に、更新後のバージョンにアクセスできることを確認します：

```bash
# サービス名を取得
SERVICE_NAME=$(kubectl get service -n envoy-gateway-system -l gateway.envoyproxy.io/owning-gateway-namespace=default -o jsonpath='{.items[0].metadata.name}')

# curlを使用してHTTPリクエストを送信
kubectl run curl-test-after-update --image=curlimages/curl --restart=Never -i --rm -- \
  curl -v http://${SERVICE_NAME}.envoy-gateway-system.svc.cluster.local
```

出力例（一部抜粋）：
```
{
  "hostname": "podinfo-primary-xxxxxxxxxx-xxxxx",
  "version": "6.1.0",
  "message": "greetings from podinfo v6.1.0",
  ...
}
```

## 検証結果

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

1. `hosts`フィールドを省略してもCanaryリソースの作成が成功する
2. HTTPRouteが正常に生成され、Acceptedステータスになる
3. Gateway経由でアプリケーションにアクセスできる
4. デプロイメント更新後、カナリアリリースが正常に進行し、最終的にプロモーションが完了する
5. 更新後の新バージョンにもちゃんとHTTPでアクセスできる

これにより、FlaggerのCanaryリソースでは`hosts`フィールドは必須ではなく、省略しても問題なく動作することが確認できました。

## まとめ

FlaggerでGateway APIと連携する場合、`hosts`フィールドは省略可能です。特定のホスト名でルーティングする必要がない場合は、`gatewayRefs`のみを指定することで、シンプルな設定でカナリアリリースを実現できます。

## 参考

- この記事で紹介した検証内容を実行できるリポジトリ

https://github.com/suinplayground/kubernetes-playground/tree/main/flagger/01-hostnameless

## 採用情報

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

https://craftsman-software.com/recruit