1 - HostAliases로 파드의 /etc/hosts 항목 추가하기

파드의 /etc/hosts 파일에 항목을 추가하는 것은 DNS나 다른 방법들이 적용되지 않을 때 파드 수준의 호스트네임 해석을 제공한다. PodSpec의 HostAliases 항목을 사용하여 이러한 사용자 정의 항목들을 추가할 수 있다.

HostAliases를 사용하지 않은 수정은 권장하지 않는데, 이는 호스트 파일이 kubelet에 의해 관리되고, 파드 생성/재시작 중에 덮어쓰여질 수 있기 때문이다.

기본 호스트 파일 내용

파드 IP가 할당된 Nginx 파드를 시작한다.

kubectl run nginx --image nginx
pod/nginx created

파드 IP를 확인해보자.

kubectl get pods --output=wide
NAME     READY     STATUS    RESTARTS   AGE    IP           NODE
nginx    1/1       Running   0          13s    10.200.0.4   worker0

호스트 파일의 내용은 아래와 같을 것이다.

kubectl exec nginx -- cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
fe00::0	ip6-mcastprefix
fe00::1	ip6-allnodes
fe00::2	ip6-allrouters
10.200.0.4	nginx

기본적으로, hosts 파일은 localhost와 자기 자신의 호스트네임과 같은 IPv4와 IPv6 상용구들만 포함하고 있다.

hostAliases를 사용하여 추가 항목들 추가하기

기본 상용구 이외에, 추가 항목들을 hosts 파일에 추가할 수 있다. 예를 들어, foo.local, bar.local127.0.0.1로, foo.remote, bar.remote10.1.2.3로 해석될 수 있도록, .spec.hostAliases 항목에서 정의하여 파드에 HostAliases를 추가하면 가능하다.

apiVersion: v1
kind: Pod
metadata:
  name: hostaliases-pod
spec:
  restartPolicy: Never
  hostAliases:
  - ip: "127.0.0.1"
    hostnames:
    - "foo.local"
    - "bar.local"
  - ip: "10.1.2.3"
    hostnames:
    - "foo.remote"
    - "bar.remote"
  containers:
  - name: cat-hosts
    image: busybox:1.28
    command:
    - cat
    args:
    - "/etc/hosts"

다음을 실행하여 해당 구성으로 파드를 실행할 수 있다.

kubectl apply -f https://k8s.io/examples/service/networking/hostaliases-pod.yaml
pod/hostaliases-pod created

파드의 세부 정보를 검토하여 IPv4 주소와 상태를 확인해보자.

kubectl get pod --output=wide
NAME                           READY     STATUS      RESTARTS   AGE       IP              NODE
hostaliases-pod                0/1       Completed   0          6s        10.200.0.5      worker0

hosts 파일 내용은 아래와 같다.

kubectl logs hostaliases-pod
# Kubernetes-managed hosts file.
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
fe00::0	ip6-mcastprefix
fe00::1	ip6-allnodes
fe00::2	ip6-allrouters
10.200.0.5	hostaliases-pod

# Entries added by HostAliases.
127.0.0.1	foo.local	bar.local
10.1.2.3	foo.remote	bar.remote

가장 마지막에 추가 항목들이 정의되어 있는 것을 확인할 수 있다.

왜 Kubelet이 호스트 파일을 관리하는가?

컨테이너가 이미 시작되고 난 뒤에 컨테이너 런타임이 hosts 파일을 수정하는 것을 방지하기 위해, Kubelet이 파드의 각 컨테이너의 hosts 파일을 관리한다. 역사적으로, 쿠버네티스는 컨테이너 런타임으로 계속 도커 엔진을 사용해 왔으며, 각 컨테이너가 시작된 뒤에 도커 엔진이 /etc/hosts 파일을 수정할 수 있었다.

현재 쿠버네티스는 다양한 컨테이너 런타임을 사용할 수 있으며, kubelet이 각 컨테이너 내의 hosts 파일을 관리하므로 어떤 컨테이너 런타임을 사용하는지에 상관없이 동일한 결과를 얻을 수 있다.

2 - IPv4/IPv6 이중 스택 검증

이 문서는 IPv4/IPv6 이중 스택이 활성화된 쿠버네티스 클러스터들을 어떻게 검증하는지 설명한다.

시작하기 전에

  • 이중 스택 네트워킹을 위한 제공자 지원 (클라우드 제공자 또는 기타 제공자들은 라우팅 가능한 IPv4/IPv6 네트워크 인터페이스를 제공하는 쿠버네티스 노드들을 제공해야 한다.)
  • 이중 스택 네트워킹을 지원하는 네트워크 플러그인
  • 이중 스택 활성화 클러스터
쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.23.

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

어드레싱 검증

노드 어드레싱 검증

각각의 이중 스택 노드는 단일 IPv4 블록 및 단일 IPv6 블록을 할당받아야 한다. IPv4/IPv6 파드 주소 범위를 다음 커맨드를 실행하여 검증한다. 샘플 노드 이름을 클러스터 내 검증된 이중 스택 노드로 대체한다. 본 예제에서, 노드 이름은 k8s-linuxpool1-34450317-0 이다.

kubectl get nodes k8s-linuxpool1-34450317-0 -o go-template --template='{{range .spec.podCIDRs}}{{printf "%s\n" .}}{{end}}'
10.244.1.0/24
2001:db8::/64

단일 IPv4 블록과 단일 IPv6 블록이 할당되어야 한다.

노드가 IPv4 및 IPv6 인터페이스를 가지고 있는지 검증한다. 노드 이름을 클러스터의 검증된 노드로 대체한다. 본 예제에서 노드 이름은 k8s-linuxpool1-34450317-0 이다.

kubectl get nodes k8s-linuxpool1-34450317-0 -o go-template --template='{{range .status.addresses}}{{printf "%s: %s\n" .type .address}}{{end}}'
Hostname: k8s-linuxpool1-34450317-0
InternalIP: 10.0.0.5
InternalIP: 2001:db8:10::5

파드 어드레싱 검증

파드가 IPv4 및 IPv6 주소를 할당받았는지 검증한다. 파드 이름을 클러스터에서 검증된 파드로 대체한다. 본 예제에서 파드 이름은 pod01 이다.

kubectl get pods pod01 -o go-template --template='{{range .status.podIPs}}{{printf "%s\n" .ip}}{{end}}'
10.244.1.4
2001:db8::4

status.podIPs fieldPath를 통한 다운워드(downward) API로 파드 IP들을 검증할 수도 있다. 다음 스니펫은 컨테이너 내 MY_POD_IPS 라는 환경 변수를 통해 파드 IP들을 어떻게 노출시킬 수 있는지 보여준다.

        env:
        - name: MY_POD_IPS
          valueFrom:
            fieldRef:
              fieldPath: status.podIPs

다음 커맨드는 컨테이너 내 MY_POD_IPS 환경 변수의 값을 출력한다. 해당 값은 파드의 IPv4 및 IPv6 주소를 나타내는 쉼표로 구분된 목록이다.

kubectl exec -it pod01 -- set | grep MY_POD_IPS
MY_POD_IPS=10.244.1.4,2001:db8::4

파드의 IP 주소는 또한 컨테이너 내 /etc/hosts 에 적힐 것이다. 다음 커맨드는 이중 스택 파드의 /etc/hosts 에 cat을 실행시킨다. 출력 값을 통해 파드의 IPv4 및 IPv6 주소 모두 검증할 수 있다.

kubectl exec -it pod01 -- cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1    localhost
::1    localhost ip6-localhost ip6-loopback
fe00::0    ip6-localnet
fe00::0    ip6-mcastprefix
fe00::1    ip6-allnodes
fe00::2    ip6-allrouters
10.244.1.4    pod01
2001:db8::4    pod01

서비스 검증

.spec.ipFamilyPolicy 를 명시적으로 정의하지 않은 다음의 서비스를 생성한다. 쿠버네티스는 처음 구성된 service-cluster-ip-range 에서 서비스에 대한 클러스터 IP를 할당하고 .spec.ipFamilyPolicySingleStack 으로 설정한다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app.kubernetes.io/name: MyApp
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80

kubectl 을 사용하여 서비스의 YAML을 확인한다.

kubectl get svc my-service -o yaml

이 서비스에서 .spec.ipFamilyPolicySingleStack 으로 설정하고 .spec.clusterIP 를 kube-controller-manager의 --service-cluster-ip-range 플래그를 통해 설정된 첫 번째 구성 범위에서 IPv4 주소로 설정한다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
  namespace: default
spec:
  clusterIP: 10.0.217.164
  clusterIPs:
  - 10.0.217.164
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - port: 80
    protocol: TCP
    targetPort: 9376
  selector:
    app.kubernetes.io/name: MyApp
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

.spec.ipFamilies 의 첫 번째 배열 요소로 IPv6 을 명시적으로 정의하는 다음 서비스를 생성한다. Kubernetes는 service-cluster-ip-range로 구성된 IPv6 범위에서 서비스용 클러스터 IP를 할당하고 .spec.ipFamilyPolicySingleStack 으로 설정한다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app.kubernetes.io/name: MyApp
spec:
  ipFamilies:
  - IPv6
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80

kubectl 를 사용하여 서비스의 YAML을 확인한다.

kubectl get svc my-service -o yaml

이 서비스에서 .spec.ipFamilyPolicySingleStack 으로 설정하고 .spec.clusterIP 를 kube-controller-manager의 --service-cluster-ip-range 플래그를 통해 설정된 IPv6 범위에서 IPv6 주소로 설정한다.

apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/name: MyApp
  name: my-service
spec:
  clusterIP: 2001:db8:fd00::5118
  clusterIPs:
  - 2001:db8:fd00::5118
  ipFamilies:
  - IPv6
  ipFamilyPolicy: SingleStack
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app.kubernetes.io/name: MyApp
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

PreferDualStack.spec.ipFamilyPolicy 을 명시적으로 정의하는 다음 서비스를 생성한다. 쿠버네티스는 IPv4 및 IPv6 주소를 모두 할당하고 (이 클러스터에는 이중 스택을 사용하도록 설정되었으므로) .spec.ipFamilies 배열에 있는 첫 번째 요소의 주소 계열을 기반으로.spec.ClusterIP 목록에서 .spec.ClusterIPs 를 선택한다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app.kubernetes.io/name: MyApp
spec:
  ipFamilyPolicy: PreferDualStack
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80

서비스가 kubectl describe 를 사용하여 IPv4 및 IPv6 주소 블록에서 클러스터 IP를 가져오는지 확인한다. 그런 다음 IP 및 포트를 통해 서비스에 대한 접속을 확인할 수 있다.

kubectl describe svc -l app.kubernetes.io/name: MyApp
Name:              my-service
Namespace:         default
Labels:            app.kubernetes.io/name: MyApp
Annotations:       <none>
Selector:          app.kubernetes.io/name: MyApp
Type:              ClusterIP
IP Family Policy:  PreferDualStack
IP Families:       IPv4,IPv6
IP:                10.0.216.242
IPs:               10.0.216.242,2001:db8:fd00::af55
Port:              <unset>  80/TCP
TargetPort:        9376/TCP
Endpoints:         <none>
Session Affinity:  None
Events:            <none>

이중 스택 로드 밸런싱 서비스 생성

만약 클라우드 제공자가 IPv6 기반 외부 로드 밸런서 구성을 지원한다면 .spec.ipFamilyPolicyPreferDualStack.spec.ipFamilies 배열의 첫 번째 요소로 IPv6LoadBalancer 로 설정된 type 필드를 사용하여 다음 서비스를 생성한다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app.kubernetes.io/name: MyApp
spec:
  ipFamilyPolicy: PreferDualStack
  ipFamilies:
  - IPv6
  type: LoadBalancer
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80

Check the Service:

kubectl get svc -l app.kubernetes.io/name: MyApp

서비스가 IPv6 주소 블록에서 CLUSTER-IP 주소 및 EXTERNAL-IP 주소를 할당받는지 검증한다. 그리고 나서 IP 및 포트로 서비스 접근이 가능한지 검증할 수 있다.

NAME         TYPE           CLUSTER-IP            EXTERNAL-IP        PORT(S)        AGE
my-service   LoadBalancer   2001:db8:fd00::7ebc   2603:1030:805::5   80:30790/TCP   35s

3 - 서비스 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]

4 - 쿠버네티스 기본 서비스 CIDR 재구성

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

이 문서는 클러스터에 할당된 기본 서비스 IP 범위를 재구성하는 방법을 다룬다.

시작하기 전에

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

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

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

쿠버네티스 기본 서비스 CIDR 재구성

이 문서는 쿠버네티스 클러스터 내 서비스 IP 주소 범위를 관리하는 방법을 설명하며, 이는 클러스터가 서비스에 대해 지원하는 IP 패밀리에도 영향을 준다.

서비스 ClusterIPs에 사용 가능한 IP 패밀리는 kube-apiserver의 --service-cluster-ip-range 플래그에 의해 결정된다. 서비스 IP 주소 할당에 대해 더 잘 이해하려면 가상 IP 및 서비스 프록시 문서를 참고한다.

쿠버네티스 1.33부터, 클러스터에 대해 설정된 서비스 IP 패밀리는 kubernetes라는 이름의 ServiceCIDR 오브젝트로 반영된다. kubernetes ServiceCIDR 오브젝트는 최초로 실행된 kube-apiserver 인스턴스가 자신에게 설정된 --service-cluster-ip-range 플래그를 기반으로 생성한다. 일관된 클러스터 동작을 보장하기 위해, 모든 kube-apiserver 인스턴스는 동일한 --service-cluster-ip-range 값으로 설정되어야 하며, 이는 기본 쿠버네티스 ServiceCIDR 오브젝트와 일치해야 한다.

쿠버네티스 서비스 CIDR 재구성 유형

서비스 CIDR 재구성은 다음과 같은 시나리오로 구분할 수 있다.

  • 기존 서비스 CIDR 확장: 이는 kube-apiserver를 재구성할 필요 없이 새 ServiceCIDR 오브젝트를 추가하여 동적으로 수행할 수 있다. 자세한 내용은 [서비스 IP 범위 확장] (https://kubernetes.io/ko/docs/tasks/network/extend-service-ip-ranges/) 문서를 참고한다.

  • 단일 스택에서 기본 서비스 CIDR을 유지한 채 이중 스택으로 전환: 이는 원래의 IP 패밀리를 기본으로 유지하면서 보조 IP 패밀리(IPv4 전용 클러스터에 IPv6 추가, 또는 IPv6 전용 클러스터에 IPv4 추가)를 도입하는 것을 의미한다. 이를 위해 kube-apiserver 구성을 업데이트해야 하며 이 추가된 IP 패밀리를 처리해야 하는 다양한 클러스터 컴포넌트들도 함께 수정해야 한다. 이러한 컴포넌트에는 kube-proxy, CNI 또는 네트워크 플러그인, 서비스 메시 구현체, DNS 서비스 등이 포함되지만 이에 국한되지는 않는다.

  • 이중 스택에서 기본 서비스 CIDR을 유지한 채 단일 스택으로 전환: 이는 이중 스택 클러스터에서 보조 IP 패밀리를 제거하고, 원래의 기본 IP 패밀리를 유지한 채 단일 IP 패밀리로 되돌리는 것을 의미한다. 컴포넌트를 새 IP 패밀리에 맞게 재구성하는 것뿐 아니라, 제거된 IP 패밀리를 사용하도록 명시적으로 설정된 서비스들을 처리해야 할 수도 있다.

  • 기본 서비스 CIDR를 변경하는 경우: 기본 ServiceCIDR 전체를 교체하는 것은 복잡한 작업이다. 새로운 ServiceCIDR이 기존 범위와 겹치지 않는다면 [기존 서비스들의 재번호 부여와 kubernetes.default 서비스의 변경] (#illustrative-reconfiguration-steps)이 필요하다. 기본 IP 패밀리 자체가 변경되는 경우에는 훨씬 더 복잡해지며, 새로운 기본 IP 패밀리에 맞추기 위해 (kubelet, 네트워크 플러그인 등) 여러 클러스터 구성 요소를 변경해야 할 수도 있다.

기본 서비스 CIDR 교체를 위한 수동 작업

기본 서비스 CIDR을 재구성하려면 클러스터 운영자, 관리자, 또는 클러스터 라이프사이클을 관리하는 소프트웨어가 수행하는 수동 절차가 필요하다. 일반적으로 다음과 같은 작업을 포함한다.

  1. kube-apiserver 구성 업데이트: 새로운 IP 범위로 --service-cluster-ip-range 플래그를 수정한다.
  2. 네트워크 컴포넌트 재구성: 이는 중요한 단계이며, 구체적인 절차는 사용 중인 네트워크 컴포넌트에 따라 다르다. 구성 파일 업데이트, 에이전트 파드 재시작, 또는 새 서비스 CIDR과 파드의 원하는 IP 패밀리 구성을 처리하도록 컴포넌트를 업데이트하는 작업이 포함될 수 있다. 일반적으로 kube-proxy와 같은 쿠버네티스 서비스 구현체, 구성된 네트워크 플러그인, 그리고 서비스 메시 컨트롤러와 DNS 서버와 같은 다른 네트워킹 컴포넌트가 이에 해당하며, 이들이 새 IP 패밀리 구성에서 트래픽을 올바르게 처리하고 서비스 디스커버리를 수행할 수 있어야 한다.
  3. 기존 서비스 관리: 이전 CIDR에서 할당된 서비스 IP가 새로 구성된 범위에 속하지 않는 경우 이를 처리해야 한다. 방법으로는 (다운타임과 새 IP 할당을 수반하는) 재생성 또는 더 복잡한 재구성 전략이 있다.
  4. 내부 쿠버네티스 서비스 재생성: 기본 IP 패밀리가 변경되었거나 다른 네트워크로 교체된 경우, kubernetes.default 서비스는 새 서비스 CIDR에서 IP를 할당받도록 삭제 후 재생성해야 한다.

재구성 단계 예시

다음 단계들은 기본 서비스 CIDR을 완전히 교체하고 kubernetes.default 서비스 재생성에 초점을 맞춘 제어된 재구성 절차를 설명한다.

  1. 초기 --service-cluster-ip-range 값으로 kube-apiserver를 시작한다.
  2. 이 범위에서 IP를 할당받는 초기 서비스를 생성한다.
  3. 재구성을 위한 임시 대상으로 새로운 서비스 CIDR을 추가한다.
  4. kubernetes 기본 서비스 CIDR을 삭제 대상으로 표시한다(기존 IP와 파이널라이저(finalizer)로 인해 삭제 대기 상태로 남는다). 이렇게 하면 이전 범위에서 새 할당이 이루어지는 것을 방지한다.
  5. 기존 서비스를 재생성한다. 이제 새 임시 서비스 CIDR에서 IP가 할당된다.
  6. 새 서비스 CIDR로 설정된 kube-apiserver를 재시작하고 이전 인스턴스를 종료한다.
  7. kubernetes.default 서비스를 삭제한다. 새 kube-apiserver가 이를 새 서비스 CIDR 내에서 재생성한다.

다음 내용