클러스터 IP로 외부에 노출된 서비스에 Calico 정책 적용하기
Calico Open Source로 BGP를 통해 Kubernetes Service ClusterIP를 노출하고, 네트워크 정책으로 접근 가능한 대상을 제한하는 방법을 안내해요.
출처: 문서
본문
큰 그림
BGP로 클러스터 밖에 광고되는 clusterIP에 대한 접근을 제어해요.
가치
Calico 네트워크 정책은 표준 Kubernetes Services를 사용해 클러스터 내 서비스를 외부 클라이언트에 다음과 같은 방식으로 노출할 수 있게 해줘요:
- Kubernetes nodeports에 정책 적용하기
- BGP를 통한 cluster IP 사용 (이 문서에서 설명)
개념
클러스터 IP를 클러스터 밖에 광고하기
cluster IP는 Kubernetes Service를 대표하는 가상 IP 주소예요. 각 호스트의 Kube Proxy는 clusterIP를 서비스를 뒷받침하는 파드 중 하나의 파드 IP로 변환하며, 리버스 프록시이자 로드 밸런서 역할을 해요.
Cluster IP는 원래 Kubernetes 클러스터 내에서 사용하도록 설계됐어요. Calico는 Cluster IP를 외부에 광고할 수 있게 해줘요 — 그래서 외부 클라이언트가 클러스터 내부에서 호스팅되는 서비스에 접근할 때 Cluster IP를 사용할 수 있어요. 즉 Calico ingress 정책은 다음 위치 중 하나 또는 둘 다에서 적용될 수 있어요:
- clusterIP로 향하는 트래픽이 클러스터에 처음 들어오는 호스트 인터페이스
- 백엔드 파드의 파드 인터페이스
트래픽 라우팅: local 모드와 cluster 모드
Calico는 외부 트래픽을 노드 로컬 엔드포인트로 보낼지 클러스터 전체 엔드포인트로 보낼지 제어하는 Kubernetes service external traffic policy를 구현해요. 다음 표는 이 설정들의 주요 차이를 정리한 거예요. 기본값은 cluster 모드예요.
| Service setting | Traffic is load balanced... | Pros and cons | Required service type |
|---|---|---|---|
| externalTrafficPolicy: Cluster(default) | Across all nodes in the cluster | Equal distribution of traffic among all pods running a service. Possible unnecessary network hops between nodes for ingress external traffic.When packets are rerouted to pods on another node, traffic is SNAT'd (source network address translation). Destination pod can see the proxying node's IP address rather than the actual client IP. | ClusterIP |
| externalTrafficPolicy: Local | Across the nodes with the endpoints for the service | Avoids extra hops so better for apps that ingress a lot external traffic. Traffic is not SNAT'd so actual client IPs are preserved. Traffic distributed among pods running a service may be imbalanced. | LoadBalancer (for cloud providers), or NodePort (for node's static port) |
시작하기 전에...
Calico가 BGP로 cluster IP를 광고하도록 구성하세요.
방법
어떤 모드를 사용할지는 목표와 리소스에 따라 달라요. 운영 관점에서 local 모드는 정책이 단순해지지만 특정 시나리오에서 로드 밸런싱이 고르지 않을 수 있어요. cluster 모드는 clusterIP 관리, SNAT, 특정 IP 주소를 참조하는 정책 작성에 더 많은 작업이 필요하지만 항상 고른 로드 밸런싱을 얻을 수 있어요.
외부에 노출된 cluster IP 보호하기, local 모드
local 모드에서는 외부 트래픽의 원본 출발지 주소가 보존되므로 표준 Calico 네트워크 정책으로 직접 정책을 정의할 수 있어요.
- Kubernetes Service와 같은 파드 집합을 선택하는 Calico NetworkPolicies 또는 GlobalNetworkPolicies를 만들어요.
- 외부 트래픽을 허용하는 규칙을 추가해요.
- 원한다면 클러스터 내 트래픽을 허용하는 규칙을 추가해요.
외부에 노출된 cluster IP 보호하기, cluster 모드
다음 단계에서 GlobalNetworkPolicy와 HostEndpoints를 정의해요.
1단계: Kubernetes Service 매니페스트 확인하기
Kubernetes Service 매니페스트에 clusterIP가 명시적으로 나열되어 있는지 확인해요. 다음 단계의 정책에 clusterIP가 필요하므로 Kubernetes가 자동으로 할당하게 두면 안 돼요.
2단계: 호스트 인터페이스에 글로벌 네트워크 정책 만들기
이 단계에서는 모든 호스트 엔드포인트를 선택하는 GlobalNetworkPolicy를 만들어요. 이 정책은 cluster IP에 대한 접근을 제어하고 클러스터 밖의 승인되지 않은 클라이언트가 접근하는 것을 막아줘요. 그러면 호스트는 승인된 트래픽만 포워딩해요.
cluster IP에 대한 외부 트래픽을 허용하도록 정책 설정하기
각 clusterIP에 대한 외부 트래픽을 허용하는 규칙을 추가해요. 다음 예시는 두 clusterIP로의 연결을 허용해요. applyOnForward와 preDNAT 규칙을 꼭 추가하세요.
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: allow-cluster-ips
spec:
selector: k8s-role == 'node'
types:
- Ingress
applyOnForward: true
preDNAT: true
ingress:
# Allow 50.60.0.0/16 to access Cluster IP A
- action: Allow
source:
nets:
- 50.60.0.0/16
destination:
nets:
- 10.20.30.40/32 Cluster IP A
# Allow 70.80.90.0/24 to access Cluster IP B
- action: Allow
source:
nets:
- 70.80.90.0/24
destination:
nets:
- 10.20.30.41/32 Cluster IP B
pod CIDR로 향하는 트래픽을 허용하는 규칙 추가하기
이 규칙이 없으면 정책이 포워딩 트래픽에 적용되기 때문에 정상적인 파드 간 트래픽이 차단돼요.
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: allow-to-pods
spec:
selector: k8s-role == 'node'
types:
- Ingress
applyOnForward: true
preDNAT: true
ingress:
# Allow traffic forwarded to pods
- action: Allow
destination:
nets:
- 192.168.0.0/16 Pod CIDR
모든 호스트 엔드포인트로 향하는 트래픽을 허용하는 규칙 추가하기
또는 Kubernetes와 Calico를 포함한 특정 호스트 트래픽을 허용하는 규칙을 추가할 수도 있어요. 이 규칙이 없으면 정상적인 호스트 트래픽이 차단돼요.
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: allow-traffic-hostendpoints
spec:
selector: k8s-role == 'node'
types:
- Ingress
# Allow traffic to the node (not nodePorts, TCP) (not nodePorts, TCP)
- action: Allow
protocol: TCP
destination:
selector: k8s-role == 'node'
notPorts: ["30000:32767"] #nodePort range
# Allow traffic to the node (not nodePorts, TCP) (not nodePorts, UDP)
- action: Allow
protocol: UDP
destination:
selector: k8s-role == 'node'
notPorts: ["30000:32767"] #nodePort range
3단계: 파드를 선택하는 글로벌 네트워크 정책 만들기
이 단계에서는 Kubernetes Service와 같은 파드 집합을 선택하는 GlobalNetworkPolicy를 만들어요. 호스트 엔드포인트가 서비스 포트에 접근할 수 있도록 허용하는 규칙을 추가해요.
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: allow-nodes-svc-a
spec:
selector: k8s-svc == 'svc-a'
types:
- Ingress
ingress:
- action: Allow
protocol: TCP
source:
selector: k8s-role == 'node'
destination:
ports: [80, 443]
- action: Allow
protocol: UDP
source:
selector: k8s-role == 'node'
destination:
ports: [80, 443]
4단계: (선택) 클러스터 내 트래픽이 서비스에 접근할 수 있도록 허용하는 네트워크 정책 또는 글로벌 네트워크 정책 만들기
5단계: HostEndpoints 만들기
clusterIP 트래픽을 받을 각 호스트의 인터페이스에 대해 HostEndpoints를 만들어요. 2단계(pod CIDR로 향하는 트래픽을 허용하는 규칙 추가하기)의 정책과 3단계의 규칙에 선택되도록 라벨을 붙이는 것을 잊지 마세요.
앞선 예시 정책에서는 이러한 HostEndpoints를 식별하기 위해 k8s-role: node 라벨을 사용했어요.
추가 리소스
더 알아보기 (Learn more)
cluster 모드에서는 preDNAT + applyOnForward 조합의 GlobalNetworkPolicy로 포워딩 단계에서 접근을 걸러내고, local 모드에서는 원본 클라이언트 IP가 보존되므로 일반 정책만으로 충분해요. Service 매니페스트에 clusterIP를 명시해 두는 것도 잊지 마세요.