Information in this document may be out of date

This document has an older update date than the original, so the information it contains may be out of date. If you're able to read English, see the English version for the most up-to-date information: Extend Service IP Ranges

서비스 IP 범위 확장

기능 상태: Kubernetes v1.33 [stable] (enabled by default: true)

이 문서는 클러스터에 할당된 기존 서비스 IP 범위를 확장하는 방법을 설명한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.29.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

서비스 IP 범위 확장

쿠버네티스 클러스터에서 MultiCIDRServiceAllocator 기능 게이트를 활성화한 kube-apiserver를 사용하고 networking.k8s.io/v1beta1 API 그룹이 활성화된 경우, kubernetes라는 잘 알려진 이름을 가진 ServiceCIDR 오브젝트를 생성하며, 이는 kube-apiserver의 --service-cluster-ip-range 명령줄 인자 값에 기반하여 IP 주소 범위를 지정한다.

kubectl get servicecidr
NAME         CIDRS          AGE
kubernetes   10.96.0.0/28   17d

잘 알려진 kubernetes 서비스는 파드에 kube-apiserver 엔드포인트를 노출시키며, 기본 ServiceCIDR 범위에서 첫 번째 IP 주소를 계산하고 해당 IP 주소를 클러스터 IP로 사용한다.

kubectl get service kubernetes
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   17d

이 경우 기본 서비스는 해당 IPAdress 오브젝트와 연결된 ClusterIP 10.96.0.1을 사용한다.

kubectl get ipaddress 10.96.0.1
NAME        PARENTREF
10.96.0.1   services/default/kubernetes

ServiceCIDR는 파이널라이저로 보호되어 서비스 ClusterIP가 고아 상태로 남는 것을 방지한다. 파이널라이저(finalizer)는 다른 서브넷에 해당 IPAddress가 포함되어 있거나 해당 서브넷에 속한 IPAddress가 전혀 없는 경우에만 제거된다.

서비스에 사용 가능한 IP 수 확장

사용자가 서비스에 사용 가능한 주소 수를 늘려야 하는 경우가 있는데, 이전에는 서비스 범위를 늘리는 작업이 데이터 손실을 초래할 수 있는 파괴적인 작업이었다. 이 새로운 기능을 통해 사용자는 사용할 수 있는 주소 수를 늘리기 위해 단순히 새로운 ServiceCIDR을 추가하면 된다.

새로운 ServiceCIDR 추가

서비스에 10.96.0.0/28 대역을 사용하는 클러스터에서는 2^(32-28) - 2 = 14개의 IP 주소만 사용할 수 있다. kubernetes.default 서비스는 항상 생성되므로, 이 예시에서는 실제로 13개의 서비스만 만들 수 있다.

for i in $(seq 1 13); do kubectl create service clusterip "test-$i" --tcp 80 -o json | jq -r .spec.clusterIP; done
10.96.0.11
10.96.0.5
10.96.0.12
10.96.0.13
10.96.0.14
10.96.0.2
10.96.0.3
10.96.0.4
10.96.0.6
10.96.0.7
10.96.0.8
10.96.0.9
error: failed to create ClusterIP service: Internal error occurred: failed to allocate a serviceIP: range is full

서비스에서 사용할 수 있는 IP 주소 수를 늘리려면, IP 주소 범위를 확장하거나 새 IP 주소 범위를 추가하는 새 ServiceCIDR을 생성하면 된다.

cat <EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1beta1
kind: ServiceCIDR
metadata:
  name: newcidr1
spec:
  cidrs:
  - 10.96.0.0/24
EOF
servicecidr.networking.k8s.io/newcidr1 created

이렇게 하면 새 범위에서 ClusterIP를 할당받는 새로운 서비스를 생성할 수 있다.

for i in $(seq 13 16); do kubectl create service clusterip "test-$i" --tcp 80 -o json | jq -r .spec.clusterIP; done
10.96.0.48
10.96.0.200
10.96.0.121
10.96.0.144

ServiceCIDR 삭제

해당 ServiceCIDR에 의존하는 IPAddresses가 존재하는 경우 해당 ServiceCIDR을 삭제할 수 없다.

kubectl delete servicecidr newcidr1
servicecidr.networking.k8s.io "newcidr1" deleted

쿠버네티스는 이러한 종속 관계를 추적하기 위해 ServiceCIDR에 파이널라이저를 사용한다.

kubectl get servicecidr newcidr1 -o yaml
apiVersion: networking.k8s.io/v1beta1
kind: ServiceCIDR
metadata:
  creationTimestamp: "2023-10-12T15:11:07Z"
  deletionGracePeriodSeconds: 0
  deletionTimestamp: "2023-10-12T15:12:45Z"
  finalizers:
  - networking.k8s.io/service-cidr-finalizer
  name: newcidr1
  resourceVersion: "1133"
  uid: 5ffd8afe-c78f-4e60-ae76-cec448a8af40
spec:
  cidrs:
  - 10.96.0.0/24
status:
  conditions:
  - lastTransitionTime: "2023-10-12T15:12:45Z"
    message: There are still IPAddresses referencing the ServiceCIDR, please remove
      them or create a new ServiceCIDR
    reason: OrphanIPAddress
    status: "False"
    type: Ready

ServiceCIDR 삭제를 막고 있는 IP 주소를 포함하는 서비스를 제거함으로써

for i in $(seq 13 16); do kubectl delete service "test-$i" ; done
service "test-13" deleted
service "test-14" deleted
service "test-15" deleted
service "test-16" deleted

컨트롤 플레인이 해당 제거를 감지한다. 이어서, 컨트롤 플레인은 파이널라이저를 삭제하므로, 삭제 대기 상태였던 ServiceCIDR이 실제로 제거된다.

kubectl get servicecidr newcidr1
Error from server (NotFound): servicecidrs.networking.k8s.io "newcidr1" not found

쿠버네티스 ServiceCIDR 정책

클러스터 관리자는 클러스터 내에서 ServiceCIDR 리소스의 생성과 수정을 제어하는 정책을 구현할 수 있다. 이를 통해 서비스에 사용되는 IP 주소 범위를 중앙에서 관리하고 의도치 않거나 충돌하는 구성을 방지할 수 있다. 쿠버네티스는 이러한 규칙을 강제하기 위해 Validating Admission Policy와 같은 메커니즘을 제공한다.

Validating Admission Policy로 무단 ServiceCIDR 생성/수정 방지

클러스터 관리자는 허용할 수 있는 범위를 제한하거나, 클러스터 서비스 IP 범위에 대한 변경을 완전히 차단하고자 하는 상황이 있을 수 있다.

특정 범위로 ServiceCIDR 제한

다음은 allowed로 지정한 범위의 서브넷인 경우에만 ServiceCIDR을 생성할 수 있도록 허용하는 ValidatingAdmissionPolicy 예시이다. (예를 들어, 이 정책에서는 cidrs: ['10.96.1.0/24'] 또는 cidrs: ['2001:db8:0:0:ffff::/80', '10.96.0.0/20']를 가진 ServiceCIDR은 허용되지만, cidrs: ['172.20.0.0/16']를 가진 ServiceCIDR은 허용되지 않는다.) 이 정책을 복사하여 환경에 맞게 allowed 값을 변경해 사용할 수 있다.

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: "servicecidrs.default"
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:   ["networking.k8s.io"]
      apiVersions: ["v1","v1beta1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["servicecidrs"]
  matchConditions:
  - name: 'exclude-default-servicecidr'
    expression: "object.metadata.name != 'kubernetes'"
  variables:
  - name: allowed
    expression: "['10.96.0.0/16','2001:db8::/64']"
  validations:
  - expression: "object.spec.cidrs.all(newCIDR, variables.allowed.exists(allowedCIDR, cidr(allowedCIDR).containsCIDR(newCIDR)))"
  # For all CIDRs (newCIDR) listed in the spec.cidrs of the submitted ServiceCIDR
  # object, check if there exists at least one CIDR (allowedCIDR) in the `allowed`
  # list of the VAP such that the allowedCIDR fully contains the newCIDR.
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: "servicecidrs-binding"
spec:
  policyName: "servicecidrs.default"
  validationActions: [Deny,Audit]

자체 검증 expression을 작성하려면 CEL 문서를 참고하면 된다.

ServiceCIDR API 사용 전면 제한

아래 예시는 기본 "kubernetes" ServiceCIDR을 제외하고, 새로운 ServiceCIDR 범위 생성을 제한하기 위해 ValidatingAdmissionPolicy와 해당 바인딩을 사용하는 방법을 보여준다.

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: "servicecidrs.deny"
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:   ["networking.k8s.io"]
      apiVersions: ["v1","v1beta1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["servicecidrs"]
  validations:
  - expression: "object.metadata.name == 'kubernetes'"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: "servicecidrs-deny-binding"
spec:
  policyName: "servicecidrs.deny"
  validationActions: [Deny,Audit]
최종 수정 August 25, 2025 at 8:07 PM PST: docs(ko): Translate extend-service-ip-ranges into Korean (669b4a92fb)