|
| 1 | +--- |
| 2 | +# reviewers: |
| 3 | +# - thockin |
| 4 | +# - dwinship |
| 5 | +min-kubernetes-server-version: v1.29 |
| 6 | +title: 서비스 IP 범위 확장 |
| 7 | +content_type: task |
| 8 | +--- |
| 9 | + |
| 10 | +<!-- overview --> |
| 11 | +{{< feature-state feature_gate_name="MultiCIDRServiceAllocator" >}} |
| 12 | + |
| 13 | +이 문서는 클러스터에 할당된 기존 서비스 IP 범위를 확장하는 방법을 설명한다. |
| 14 | + |
| 15 | + |
| 16 | +## {{% heading "prerequisites" %}} |
| 17 | + |
| 18 | +{{< include "task-tutorial-prereqs.md" >}} |
| 19 | + |
| 20 | +{{< version-check >}} |
| 21 | + |
| 22 | +{{< note >}} |
| 23 | +이 기능은 이전 버전에서도 사용할 수 있지만, v1.33부터 안정 버전(GA)으로 제공되며 공식 지원한다. |
| 24 | +{{< /note >}} |
| 25 | + |
| 26 | +<!-- steps --> |
| 27 | + |
| 28 | +## 서비스 IP 범위 확장 |
| 29 | + |
| 30 | +쿠버네티스 클러스터에서 `MultiCIDRServiceAllocator` |
| 31 | +[기능 게이트](/ko/docs/reference/command-line-tools-reference/feature-gates/)를 |
| 32 | +활성화한 kube-apiserver를 사용하고 `networking.k8s.io/v1beta1` API 그룹이 활성화된 경우, |
| 33 | +`kubernetes`라는 잘 알려진 이름을 가진 ServiceCIDR 오브젝트를 생성하며, |
| 34 | +이는 kube-apiserver의 `--service-cluster-ip-range` 명령줄 인자 값에 기반하여 IP 주소 범위를 지정한다. |
| 35 | + |
| 36 | +```sh |
| 37 | +kubectl get servicecidr |
| 38 | +``` |
| 39 | + |
| 40 | +``` |
| 41 | +NAME CIDRS AGE |
| 42 | +kubernetes 10.96.0.0/28 17d |
| 43 | +``` |
| 44 | + |
| 45 | +잘 알려진 `kubernetes` 서비스는 파드에 kube-apiserver 엔드포인트를 노출시키며, |
| 46 | +기본 ServiceCIDR 범위에서 첫 번째 IP 주소를 계산하고 |
| 47 | +해당 IP 주소를 클러스터 IP로 사용한다. |
| 48 | + |
| 49 | +```sh |
| 50 | +kubectl get service kubernetes |
| 51 | +``` |
| 52 | + |
| 53 | +``` |
| 54 | +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE |
| 55 | +kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 17d |
| 56 | +``` |
| 57 | + |
| 58 | +이 경우 기본 서비스는 해당 IPAdress 오브젝트와 연결된 ClusterIP 10.96.0.1을 사용한다. |
| 59 | + |
| 60 | +```sh |
| 61 | +kubectl get ipaddress 10.96.0.1 |
| 62 | +``` |
| 63 | + |
| 64 | +``` |
| 65 | +NAME PARENTREF |
| 66 | +10.96.0.1 services/default/kubernetes |
| 67 | +``` |
| 68 | + |
| 69 | +ServiceCIDR는 {{<glossary_tooltip text="파이널라이저" term_id="finalizer">}}로 보호되어 |
| 70 | +서비스 ClusterIP가 고아 상태로 남는 것을 방지한다. 파이널라이저(finalizer)는 다른 서브넷에 해당 IPAddress가 포함되어 있거나 |
| 71 | +해당 서브넷에 속한 IPAddress가 전혀 없는 경우에만 제거된다. |
| 72 | + |
| 73 | +## 서비스에 사용 가능한 IP 수 확장 |
| 74 | + |
| 75 | +사용자가 서비스에 사용 가능한 주소 수를 늘려야 하는 경우가 있는데, |
| 76 | +이전에는 서비스 범위를 늘리는 작업이 데이터 손실을 초래할 수 있는 파괴적인 작업이었다. |
| 77 | +이 새로운 기능을 통해 사용자는 사용할 수 있는 주소 수를 늘리기 위해 단순히 새로운 ServiceCIDR을 추가하면 된다. |
| 78 | + |
| 79 | +### 새로운 ServiceCIDR 추가 |
| 80 | + |
| 81 | +서비스에 10.96.0.0/28 대역을 사용하는 클러스터에서는 2^(32-28) - 2 = 14개의 |
| 82 | +IP 주소만 사용할 수 있다. `kubernetes.default` 서비스는 항상 생성되므로, 이 예시에서는 |
| 83 | +실제로 13개의 서비스만 만들 수 있다. |
| 84 | + |
| 85 | +```sh |
| 86 | +for i in $(seq 1 13); do kubectl create service clusterip "test-$i" --tcp 80 -o json | jq -r .spec.clusterIP; done |
| 87 | +``` |
| 88 | + |
| 89 | +``` |
| 90 | +10.96.0.11 |
| 91 | +10.96.0.5 |
| 92 | +10.96.0.12 |
| 93 | +10.96.0.13 |
| 94 | +10.96.0.14 |
| 95 | +10.96.0.2 |
| 96 | +10.96.0.3 |
| 97 | +10.96.0.4 |
| 98 | +10.96.0.6 |
| 99 | +10.96.0.7 |
| 100 | +10.96.0.8 |
| 101 | +10.96.0.9 |
| 102 | +error: failed to create ClusterIP service: Internal error occurred: failed to allocate a serviceIP: range is full |
| 103 | +``` |
| 104 | + |
| 105 | +서비스에서 사용할 수 있는 IP 주소 수를 늘리려면, |
| 106 | +IP 주소 범위를 확장하거나 새 IP 주소 범위를 추가하는 새 ServiceCIDR을 생성하면 된다. |
| 107 | + |
| 108 | +```sh |
| 109 | +cat <EOF | kubectl apply -f - |
| 110 | +apiVersion: networking.k8s.io/v1beta1 |
| 111 | +kind: ServiceCIDR |
| 112 | +metadata: |
| 113 | + name: newcidr1 |
| 114 | +spec: |
| 115 | + cidrs: |
| 116 | + - 10.96.0.0/24 |
| 117 | +EOF |
| 118 | +``` |
| 119 | + |
| 120 | +``` |
| 121 | +servicecidr.networking.k8s.io/newcidr1 created |
| 122 | +``` |
| 123 | + |
| 124 | +이렇게 하면 새 범위에서 ClusterIP를 할당받는 새로운 서비스를 생성할 수 있다. |
| 125 | + |
| 126 | +```sh |
| 127 | +for i in $(seq 13 16); do kubectl create service clusterip "test-$i" --tcp 80 -o json | jq -r .spec.clusterIP; done |
| 128 | +``` |
| 129 | + |
| 130 | +``` |
| 131 | +10.96.0.48 |
| 132 | +10.96.0.200 |
| 133 | +10.96.0.121 |
| 134 | +10.96.0.144 |
| 135 | +``` |
| 136 | + |
| 137 | +### ServiceCIDR 삭제 |
| 138 | + |
| 139 | +해당 ServiceCIDR에 의존하는 IPAddresses가 존재하는 경우 해당 ServiceCIDR을 삭제할 수 없다. |
| 140 | + |
| 141 | +```sh |
| 142 | +kubectl delete servicecidr newcidr1 |
| 143 | +``` |
| 144 | + |
| 145 | +``` |
| 146 | +servicecidr.networking.k8s.io "newcidr1" deleted |
| 147 | +``` |
| 148 | + |
| 149 | +쿠버네티스는 이러한 종속 관계를 추적하기 위해 ServiceCIDR에 파이널라이저를 사용한다. |
| 150 | + |
| 151 | +```sh |
| 152 | +kubectl get servicecidr newcidr1 -o yaml |
| 153 | +``` |
| 154 | + |
| 155 | +```yaml |
| 156 | +apiVersion: networking.k8s.io/v1beta1 |
| 157 | +kind: ServiceCIDR |
| 158 | +metadata: |
| 159 | + creationTimestamp: "2023-10-12T15:11:07Z" |
| 160 | + deletionGracePeriodSeconds: 0 |
| 161 | + deletionTimestamp: "2023-10-12T15:12:45Z" |
| 162 | + finalizers: |
| 163 | + - networking.k8s.io/service-cidr-finalizer |
| 164 | + name: newcidr1 |
| 165 | + resourceVersion: "1133" |
| 166 | + uid: 5ffd8afe-c78f-4e60-ae76-cec448a8af40 |
| 167 | +spec: |
| 168 | + cidrs: |
| 169 | + - 10.96.0.0/24 |
| 170 | +status: |
| 171 | + conditions: |
| 172 | + - lastTransitionTime: "2023-10-12T15:12:45Z" |
| 173 | + message: There are still IPAddresses referencing the ServiceCIDR, please remove |
| 174 | + them or create a new ServiceCIDR |
| 175 | + reason: OrphanIPAddress |
| 176 | + status: "False" |
| 177 | + type: Ready |
| 178 | +``` |
| 179 | +
|
| 180 | +ServiceCIDR 삭제를 막고 있는 IP 주소를 포함하는 서비스를 제거함으로써 |
| 181 | +
|
| 182 | +```sh |
| 183 | +for i in $(seq 13 16); do kubectl delete service "test-$i" ; done |
| 184 | +``` |
| 185 | + |
| 186 | +``` |
| 187 | +service "test-13" deleted |
| 188 | +service "test-14" deleted |
| 189 | +service "test-15" deleted |
| 190 | +service "test-16" deleted |
| 191 | +``` |
| 192 | + |
| 193 | +컨트롤 플레인이 해당 제거를 감지한다. 이어서, 컨트롤 플레인은 파이널라이저를 삭제하므로, |
| 194 | +삭제 대기 상태였던 ServiceCIDR이 실제로 제거된다. |
| 195 | + |
| 196 | +```sh |
| 197 | +kubectl get servicecidr newcidr1 |
| 198 | +``` |
| 199 | + |
| 200 | +``` |
| 201 | +Error from server (NotFound): servicecidrs.networking.k8s.io "newcidr1" not found |
| 202 | +``` |
| 203 | + |
| 204 | +## 쿠버네티스 ServiceCIDR 정책 |
| 205 | + |
| 206 | +클러스터 관리자는 클러스터 내에서 ServiceCIDR 리소스의 |
| 207 | +생성과 수정을 제어하는 정책을 구현할 수 있다. 이를 통해 서비스에 |
| 208 | +사용되는 IP 주소 범위를 중앙에서 관리하고 의도치 않거나 충돌하는 |
| 209 | +구성을 방지할 수 있다. 쿠버네티스는 이러한 규칙을 강제하기 위해 |
| 210 | +Validating Admission Policy와 같은 메커니즘을 제공한다. |
| 211 | + |
| 212 | +### Validating Admission Policy로 무단 ServiceCIDR 생성/수정 방지 |
| 213 | + |
| 214 | +클러스터 관리자는 허용할 수 있는 범위를 제한하거나, |
| 215 | +클러스터 서비스 IP 범위에 대한 변경을 완전히 차단하고자 |
| 216 | +하는 상황이 있을 수 있다. |
| 217 | + |
| 218 | +{{< note >}} |
| 219 | +기본 "kubernetes" ServiceCIDR는 클러스터의 일관성을 유지하기 위해 |
| 220 | +kube-apiserver에 의해 생성되며, 클러스터가 동작하기 위해 필요하므로 |
| 221 | +항상 허용되어야 한다. `ValidatingAdmissionPolicy`가 기본 ServiceCIDR을 |
| 222 | +제한하지 않도록 하려면, 다음과 같이 절을 추가하면 된다. |
| 223 | + |
| 224 | +```yaml |
| 225 | + matchConditions: |
| 226 | + - name: 'exclude-default-servicecidr' |
| 227 | + expression: "object.metadata.name != 'kubernetes'" |
| 228 | +``` |
| 229 | +
|
| 230 | +아래 예시에서처럼 적용할 수 있다. |
| 231 | +{{</ note >}} |
| 232 | +
|
| 233 | +#### 특정 범위로 ServiceCIDR 제한 |
| 234 | +
|
| 235 | +다음은 `allowed`로 지정한 범위의 서브넷인 경우에만 ServiceCIDR을 생성할 수 있도록 |
| 236 | +허용하는 `ValidatingAdmissionPolicy` 예시이다. (예를 들어, 이 정책에서는 |
| 237 | +`cidrs: ['10.96.1.0/24']` 또는 `cidrs: ['2001:db8:0:0:ffff::/80', '10.96.0.0/20']`를 |
| 238 | +가진 ServiceCIDR은 허용되지만, `cidrs: ['172.20.0.0/16']`를 |
| 239 | +가진 ServiceCIDR은 허용되지 않는다.) 이 정책을 복사하여 환경에 맞게 |
| 240 | +`allowed` 값을 변경해 사용할 수 있다. |
| 241 | + |
| 242 | +```yaml |
| 243 | +apiVersion: admissionregistration.k8s.io/v1 |
| 244 | +kind: ValidatingAdmissionPolicy |
| 245 | +metadata: |
| 246 | + name: "servicecidrs.default" |
| 247 | +spec: |
| 248 | + failurePolicy: Fail |
| 249 | + matchConstraints: |
| 250 | + resourceRules: |
| 251 | + - apiGroups: ["networking.k8s.io"] |
| 252 | + apiVersions: ["v1","v1beta1"] |
| 253 | + operations: ["CREATE", "UPDATE"] |
| 254 | + resources: ["servicecidrs"] |
| 255 | + matchConditions: |
| 256 | + - name: 'exclude-default-servicecidr' |
| 257 | + expression: "object.metadata.name != 'kubernetes'" |
| 258 | + variables: |
| 259 | + - name: allowed |
| 260 | + expression: "['10.96.0.0/16','2001:db8::/64']" |
| 261 | + validations: |
| 262 | + - expression: "object.spec.cidrs.all(newCIDR, variables.allowed.exists(allowedCIDR, cidr(allowedCIDR).containsCIDR(newCIDR)))" |
| 263 | + # For all CIDRs (newCIDR) listed in the spec.cidrs of the submitted ServiceCIDR |
| 264 | + # object, check if there exists at least one CIDR (allowedCIDR) in the `allowed` |
| 265 | + # list of the VAP such that the allowedCIDR fully contains the newCIDR. |
| 266 | +--- |
| 267 | +apiVersion: admissionregistration.k8s.io/v1 |
| 268 | +kind: ValidatingAdmissionPolicyBinding |
| 269 | +metadata: |
| 270 | + name: "servicecidrs-binding" |
| 271 | +spec: |
| 272 | + policyName: "servicecidrs.default" |
| 273 | + validationActions: [Deny,Audit] |
| 274 | +``` |
| 275 | +
|
| 276 | +자체 검증 `expression`을 작성하려면 |
| 277 | +[CEL 문서](https://kubernetes.io/docs/reference/using-api/cel/)를 참고하면 된다. |
| 278 | + |
| 279 | +#### ServiceCIDR API 사용 전면 제한 |
| 280 | + |
| 281 | +아래 예시는 기본 "kubernetes" ServiceCIDR을 제외하고, |
| 282 | +새로운 ServiceCIDR 범위 생성을 제한하기 위해 `ValidatingAdmissionPolicy`와 해당 바인딩을 사용하는 방법을 보여준다. |
| 283 | + |
| 284 | +```yaml |
| 285 | +apiVersion: admissionregistration.k8s.io/v1 |
| 286 | +kind: ValidatingAdmissionPolicy |
| 287 | +metadata: |
| 288 | + name: "servicecidrs.deny" |
| 289 | +spec: |
| 290 | + failurePolicy: Fail |
| 291 | + matchConstraints: |
| 292 | + resourceRules: |
| 293 | + - apiGroups: ["networking.k8s.io"] |
| 294 | + apiVersions: ["v1","v1beta1"] |
| 295 | + operations: ["CREATE", "UPDATE"] |
| 296 | + resources: ["servicecidrs"] |
| 297 | + validations: |
| 298 | + - expression: "object.metadata.name == 'kubernetes'" |
| 299 | +--- |
| 300 | +apiVersion: admissionregistration.k8s.io/v1 |
| 301 | +kind: ValidatingAdmissionPolicyBinding |
| 302 | +metadata: |
| 303 | + name: "servicecidrs-deny-binding" |
| 304 | +spec: |
| 305 | + policyName: "servicecidrs.deny" |
| 306 | + validationActions: [Deny,Audit] |
| 307 | +``` |
0 commit comments