手当たり次第に書くんだ

飽きっぽいのは本能

Calico の BGP 構成を Kubernetes Manifest だけで管理する – routed network と LoadBalancer IPAM の設計

Kubernetes の CNI として Calico を利用する場合、Ansible や Helm のテンプレートを経由して設定を生成する方法があります。しかし、Calico の設定の多くは Kubernetes API 上の Custom Resource として表現できます。設定内容がそれほど多くない環境では、さらにテンプレートエンジンを重ねるよりも、Calico の Custom Resource を通常の Kubernetes Manifest として管理した方が構成を読みやすくなります。

この記事では、Dual Stack、BGP 有効、IP-in-IP なし、VXLAN なし、Pod 通信の NAT なし、LoadBalancer 用 IPPool の NAT なし、Node-to-Node Mesh なし、外部 BGP Peer への経路広告、Calico LoadBalancer IPAM の利用を前提に、Calico の基本的な Manifest を整理します。狙いは、Kubernetes のネットワークを Overlay や NAT に隠さず、外部ネットワーク側で Prefix を扱う routed network として設計することです。

ここで重要なのは、Manifest を単なる設定ファイルとして扱わないことです。`encapsulation: None`、`natOutgoing: Disabled`、`nodeToNodeMeshEnabled: false` のような値は、個別のパラメーターではなく、トンネルを使わず、NAT せず、BGP topology を明示的に設計するという判断の表現です。

Manifest は役割ごとに分ける

Calico の構成を Kubernetes Manifest として管理する場合でも、一つの巨大な YAML にまとめる必要はありません。役割ごとにファイルを分けると、どの Resource がどの設計判断を表しているのかをレビューしやすくなります。

calico/
├── config/
│   ├── 10-installation.yaml
│   ├── 20-apiserver.yaml
│   ├── 30-kube-controllers.yaml
│   ├── 40-loadbalancer-pools.yaml
│   ├── 50-bgp-peers.yaml
│   └── 60-bgp-configuration.yaml
├── patches/
│   └── control-plane-label.patch.yaml
└── examples/
    └── 70-service-loadbalancer.yaml
ファイル主な役割
10-installation.yamlCalico Network の基本方針、Pod CIDR、dataplane、encapsulation、NAT の定義
20-apiserver.yamlCalico API Server の有効化
30-kube-controllers.yamlLoadBalancer IPAM の割り当て条件
40-loadbalancer-pools.yamlLoadBalancer Service 用 IPPool
50-bgp-peers.yaml外部ルーターまたは Route Reflector との BGP Peer
60-bgp-configuration.yamlクラスタ全体の BGP 設定、Service CIDR、LoadBalancer CIDR の広告

ファイルを分ける目的は、管理を細かくすることではありません。Installation、IPPool、BGPPeer、BGPConfiguration の責務を分け、レビュー時に設計意図を追いやすくするためです。

Installation で routed network の前提を明示する

まず Calico Operator が管理する `Installation` を定義します。ここでは BGP を有効にし、Linux dataplane には nftables を指定し、Pod 用の IPv4 / IPv6 IPPool では encapsulation と NAT を無効化します。

apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
  name: default
spec:
  registry: quay.io/
  calicoNetwork:
    bgp: Enabled
    linuxDataplane: Nftables
    ipPools:
      - cidr: <POD_CIDR_IPV4>
        blockSize: <POD_BLOCK_SIZE_IPV4>
        encapsulation: None
        natOutgoing: Disabled
        nodeSelector: all()
      - cidr: <POD_CIDR_IPV6>
        blockSize: <POD_BLOCK_SIZE_IPV6>
        encapsulation: None
        natOutgoing: Disabled
        nodeSelector: all()

この構成では、Pod Network に対して IP-in-IP も VXLAN も使わず、Pod 通信の NAT も行いません。Pod CIDR の到達性は、Calico BGP と外部ネットワークのルーティングによって確保します。既存環境を eBPF dataplane から nftables dataplane へ変更する場合には、単純な Manifest 適用とは別に移行手順が必要です。この記事は既存 eBPF 環境からの切替手順ではなく、Manifest として表現する構成要素の整理です。

Calico API Server を使う

Calico Resource を Kubernetes API と統合した形で扱う場合は、Calico API Server を有効化します。これにより、Calico の Custom Resource を Kubernetes 側の管理対象として扱いやすくなります。

apiVersion: operator.tigera.io/v1
kind: APIServer
metadata:
  name: default
spec: {}

LoadBalancer IPAM は明示要求に限定する

Calico LoadBalancer IPAM を使用する場合、`KubeControllersConfiguration` で LoadBalancer Controller の割り当て方針を指定します。ここでは `assignIPs: RequestedServicesOnly` とし、すべての `type: LoadBalancer` Service へ自動で Calico の IP を割り当てない構成にします。

apiVersion: projectcalico.org/v3
kind: KubeControllersConfiguration
metadata:
  name: default
spec:
  controllers:
    loadBalancer:
      assignIPs: RequestedServicesOnly

この設定は、複数の LoadBalancer 実装を併用する環境で特に重要です。Calico が扱う Service と、別の LoadBalancer Controller が扱う Service を分けたい場合、明示的に Calico を要求した Service だけを対象にした方が運用上の衝突を避けやすくなります。

LoadBalancer 用 IPPool でも NAT とトンネルを使わない

LoadBalancer Service に割り当てるアドレス範囲は、Pod 用 IPPool とは別に定義します。ここでも `ipipMode: Never`、`vxlanMode: Never`、`natOutgoing: false` を明示し、LoadBalancer 用アドレスについても NAT や Overlay に依存しない設計にします。

apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
  name: loadbalancer-ipv4
spec:
  cidr: <LOADBALANCER_CIDR_IPV4>
  blockSize: <LOADBALANCER_BLOCK_SIZE_IPV4>
  ipipMode: Never
  vxlanMode: Never
  natOutgoing: false
  nodeSelector: all()
  assignmentMode: Automatic
  allowedUses:
    - LoadBalancer
---
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
  name: loadbalancer-ipv6
spec:
  cidr: <LOADBALANCER_CIDR_IPV6>
  blockSize: <LOADBALANCER_BLOCK_SIZE_IPV6>
  ipipMode: Never
  vxlanMode: Never
  natOutgoing: false
  nodeSelector: all()
  assignmentMode: Automatic
  allowedUses:
    - LoadBalancer

`allowedUses` を `LoadBalancer` に限定することで、この Pool を Pod IP 用ではなく LoadBalancer IP 用として扱います。Pod 用 Prefix と Service 公開用 Prefix を分けると、外部ルーター側の経路制御、Prefix filter、障害時の切り分けも設計しやすくなります。

外部ルーターとの BGP Peer を Resource として定義する

Calico Node が経路を交換する外部ルーターや Route Reflector は、`BGPPeer` として定義します。IPv4 と IPv6 の Peer を分けて Resource 化しておくと、経路交換の対象や障害調査時の確認点を明確にできます。

apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
  name: upstream-router-ipv4
spec:
  peerIP: <BGP_PEER_IPV4>
  asNumber: <BGP_PEER_AS>
---
apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
  name: upstream-router-ipv6
spec:
  peerIP: <BGP_PEER_IPV6>
  asNumber: <BGP_PEER_AS>

複数のルーターと Peer を張る場合は、Resource を追加します。ここで重要なのは、Peer の数を増やすこと自体ではなく、Calico Node、外部ルーター、Route Reflector、AS 設計の関係を明確にすることです。

BGPConfiguration で Mesh と広告 Prefix を定義する

クラスタ全体の BGP 設定は `BGPConfiguration` に定義します。ここでは Node-to-Node Mesh を無効化し、クラスタの AS 番号、Service CIDR、LoadBalancer CIDR の広告をまとめて表現します。

apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
  name: default
spec:
  nodeToNodeMeshEnabled: false
  asNumber: <CALICO_LOCAL_AS>
  serviceClusterIPs:
    - cidr: <SERVICE_CIDR_IPV4>
    - cidr: <SERVICE_CIDR_IPV6>
  serviceLoadBalancerAggregation: Enabled
  serviceLoadBalancerIPs:
    - cidr: <LOADBALANCER_CIDR_IPV4>
    - cidr: <LOADBALANCER_CIDR_IPV6>

`nodeToNodeMeshEnabled: false` は、Calico Node 同士で自動的な full-mesh BGP Session を構成しないという意味です。外部ルーターや Route Reflector を中心に BGP topology を設計する場合に使えます。ただし、Mesh を無効化しただけで Node 間到達性が保証されるわけではありません。

特に iBGP を使う場合、通常の iBGP Speaker は、ある iBGP Peer から学習した経路を別の iBGP Peer へそのまま再広告しません。全 Peer が同一 AS で Node-to-Node Mesh を無効化するなら、Route Reflector、eBGP、または適切な BGP topology のいずれかが成立しているかを確認する必要があります。

Service CIDR と LoadBalancer CIDR を広告する

`serviceClusterIPs` を設定すると、Kubernetes Service CIDR を Calico BGP から広告できます。これにより、クラスタ外部のルーターが Service CIDR への経路を学習できる構成を作れます。ただし、Service CIDR を広告することと、どの Service を外部から到達可能にするかは別の設計です。Service 種別、NetworkPolicy、Firewall、外部ルーターの経路制御を合わせて考える必要があります。

LoadBalancer Service のアドレスについては、`serviceLoadBalancerAggregation: Enabled` と `serviceLoadBalancerIPs` を設定します。Calico LoadBalancer IPAM で割り当てたアドレス範囲を、BGP によって外部へ広告するためです。構成としては、クライアントから外部ルーターまたは Fabric を経由し、Calico Node、LoadBalancer Service、Pod へ到達する流れになります。

Prefix広告の目的注意点
Pod CIDRPod 間通信や外部から Pod Network への到達性を確保するUnderlay 側で Pod CIDR を正しくルーティングできる必要がある
Service CIDRClusterIP を外部ネットワークから到達可能にする構成を作る全 Service を公開する設計ではないため、制御面を別途設計する
LoadBalancer CIDRLoadBalancer Service 用 IP を外部へ広告するCalico IPAM、Service 定義、外部経路制御の整合が必要になる

Service 側で Calico の利用を明示する

Calico LoadBalancer IPAM を利用する Service では、Calico による割り当てを明示します。固定 IP を指定するか、Pool から自動割り当てするかは、Calico のバージョン、運用方式、アドレス管理方針に合わせて決めます。

apiVersion: v1
kind: Service
metadata:
  name: example
  namespace: default
  annotations:
    projectcalico.org/loadBalancerIPs: '["<LOADBALANCER_IP>"]'
spec:
  type: LoadBalancer
  loadBalancerClass: calico
  selector:
    app: example
  ports:
    - name: http
      port: 80
      targetPort: 8080
      protocol: TCP

`assignIPs: RequestedServicesOnly` と組み合わせる場合、Service 側で Calico の利用を明示することが重要です。LoadBalancer 実装を複数持つ環境では、この明示がないと、どの Controller がどの Service を扱うべきかが曖昧になります。

Control Plane Node は label と taint を分けて見る

Control Plane Node に Workload を配置しない構成では、Node label や taint と Calico の設定を合わせて管理する必要があります。たとえば Node label は通常の Kubernetes patch として管理できます。

apiVersion: v1
kind: Node
metadata:
  name: <CONTROL_PLANE_NODE>
  labels:
    node-role.kubernetes.io/control-plane: 

ただし、Node の scheduler 制御には label だけでなく taint も関係します。label は役割や選択条件を表し、taint は Pod を配置させない制約として働きます。実際のクラスタポリシーに合わせて、どの Node が経路広告だけを担うのか、どの Node に Workload を載せるのかを分けて考える必要があります。

さらに、外部 LoadBalancer の対象 Node から外すかどうかは、scheduler の label や taint とは別に確認する必要があります。Kubernetes には node.kubernetes.io/exclude-from-external-load-balancers という well-known label があり、この label を付けた Node は、外部 LoadBalancer が利用する backend servers の一覧から除外できます。

kubectl label nodes <node-name> node.kubernetes.io/exclude-from-external-load-balancers=true

Control Plane Node を Workload 配置対象から外すだけなら taint が主な制御点になります。しかし、LoadBalancer Service の経路や backend としてその Node を扱うかどうかは別問題です。Calico LoadBalancer IPAM や BGP 広告を設計する場合、Control Plane Node が経路広告、Service 到達性、外部 LoadBalancer の backend のどこに関与するのかを分けて整理する必要があります。

適用前に server dry-run で検証する

Manifest を直接管理する場合でも、いきなり変更する必要はありません。Kubernetes API Server による検証を先に実施し、Resource とフィールドが受け付けられるかを確認します。

kubectl --context "$CTX" apply \
  --server-side \
  --dry-run=server \
  -f calico/config/

問題がなければ実際に適用します。ここでも、Manifest の適用に成功したことと、BGP Session が確立し、外部ルーターの RIB / FIB に経路が入っていることは別の確認項目です。

kubectl --context "$CTX" apply -f calico/config/

Resource と経路交換を分けて確認する

適用後は、Kubernetes Resource と実際の BGP 状態を分けて確認します。Kubernetes API 上の Resource が作成されていることは必要条件ですが、それだけで外部ネットワークとの経路交換が成立したとは言えません。

kubectl --context "$CTX" get installation,apiserver -A
kubectl --context "$CTX" get ippool
kubectl --context "$CTX" get bgpconfiguration
kubectl --context "$CTX" get bgppeer
kubectl --context "$CTX" get kubecontrollersconfiguration
確認対象確認内容
Calico ResourceInstallation、IPPool、BGPPeer、BGPConfiguration が意図した値で存在するか
BGP SessionCalico Node と Peer Router の Session が Established か
広告 PrefixPod CIDR、Service CIDR、LoadBalancer CIDR が意図どおり広告されているか
Router RIB / FIB外部ルーターが経路を学習し、転送経路として使えるか
疎通Pod、Service、LoadBalancer IP への通信が IPv4 / IPv6 で成立するか

Calico 側の Resource が正常でも、外部ルーターの Prefix filter、AS 設計、Route Reflector、ECMP、Firewall、MTU、Failure detection、Route convergence が合っていなければ、実際の到達性は成立しません。Manifest の検証とネットワークの検証は、必ず別の確認項目として扱うべきです。

この構成で設計意図を Manifest に残す

今回の構成では、Kubernetes Network をできるだけ通常の routed network として扱います。IP-in-IP は使わず、VXLAN も使わず、Pod NAT と LoadBalancer NAT も使いません。Dual Stack の Pod / Service / LoadBalancer Prefix を Calico BGP で外部ルーターや Fabric へ広告し、Underlay 側で通常の L3 Route として扱います。

項目方針
IP-in-IP使用しない
VXLAN使用しない
Pod NAT使用しない
LoadBalancer NAT使用しない
BGP使用する
Dual StackIPv4 / IPv6 の両方を設計対象にする

この設計では、Calico の Manifest だけで完結するわけではありません。BGP AS、Route Reflector、ECMP、Prefix filter、IPv4 / IPv6 reachability、MTU、Firewall、Failure detection、Route convergence も合わせて設計する必要があります。だからこそ、Manifest には実装値だけでなく、どのネットワーク設計を採用しているのかが読み取れる形で残す意味があります。

まとめ

Calico の設定は Kubernetes Custom Resource として表現できるため、必ずしも Ansible やテンプレートエンジンを利用する必要はありません。比較的小規模で設定内容が明確な構成であれば、Git で Calico Manifest を管理し、server dry-run で検証し、`kubectl apply` で反映する単純な管理方法でも十分に成立します。

特に `encapsulation: None`、`natOutgoing: Disabled`、`ipipMode: Never`、`vxlanMode: Never`、`nodeToNodeMeshEnabled: false` のような値を Manifest に明示しておけば、トンネルを使用しない、NAT を使用しない、BGP routing を利用するという設計意図を Manifest から読み取れます。

ただし、Manifest が正しいことと、ネットワーク全体が正しいことは同じではありません。Calico の Resource、BGP Session、外部ルーターの経路、Firewall、MTU、障害時の収束まで確認して初めて、routed network としての Kubernetes が成立します。Manifest を単なる設定ファイルではなく、ネットワーク設計結果の明示的な表現として管理することが、この方法の大きな利点です。

参考書籍

参考資料

関連する記事

Calico と Kubernetes ネットワークの関連記事
Calico の BGP 構成を Kubernetes Manifest だけで管理する – routed network と LoadBalancer IPAM の設計

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

トップへ戻る