확장성 계획


이 페이지에서는 확장 가능한 GKE 클러스터를 설계하는 일반적인 권장사항을 설명합니다. 모든 클러스터 및 워크로드에 이 권장사항을 적용하여 최적의 성능을 얻을 수 있습니다. 이 권장사항은 대규모 확장을 계획 중인 클러스터에 특히 중요합니다. 인프라 프로비저닝을 담당하는 관리자와 Kubernetes 구성요소와 워크로드를 준비하는 개발자를 대상으로 한 권장사항입니다.

확장성이란 무엇인가요?

Kubernetes 클러스터에서 확장성이란 서비스 수준 목표(SLO) 내에서 확장할 수 있는 클러스터의 능력을 의미합니다. Kubernetes에는 자체 SLO 집합도 있습니다.

Kubernetes는 복잡한 시스템이며 여러 가지 요인에 따라 확장성이 달라집니다. 이러한 요소에는 노드 풀의 노드 유형과 수, 노드 풀의 유형과 수, 사용 가능한 포드 수, 리소스가 포드에 할당되는 방법, 서비스 또는 서비스 이면의 백엔드 수 등이 포함됩니다.

가용성 권장사항

리전 또는 영역의 제어 영역 선택

구조상의 차이로 인해 리전 클러스터가 고가용성에 더 적합합니다. 리전 클러스터는 한 리전의 여러 컴퓨팅 영역에 여러 제어 영역이 있으며, 영역 클러스터는 단일 컴퓨팅 영역에 하나의 제어 영역이 있습니다.

영역 클러스터를 업그레이드하는 경우 제어 영역 VM에 다운타임이 발생하여 업그레이드가 완료될 때까지 Kubernetes API를 사용할 수 없습니다.

리전 클러스터에서는 IP 순환, 제어 영역 VM 업그레이드, 클러스터 또는 노드 풀 크기 조정과 같은 클러스터 유지보수 중에 제어 영역을 계속 사용할 수 있습니다. 리전 클러스터를 업그레이드하는 경우 순차적 업그레이드 중에 3개의 제어 영역 VM 중 2개가 항상 실행되므로 Kubernetes API를 계속 사용할 수 있습니다. 마찬가지로 단일 영역이 중단되어도 리전 제어 영역에서 다운타임이 발생하지 않습니다.

그러나 가용성이 높은 리전 클러스터에는 몇 가지 단점이 있습니다.

  • 클러스터 구성의 변경사항이 영역 클러스터의 단일 제어 영역 대신 리전 클러스터의 모든 제어 영역에 전파되어야 하므로 적용되는 데 시간이 더 오래 걸립니다.

  • 리전 클러스터는 영역 클러스터만큼 자주 생성하거나 업그레이드할 수 없습니다. 용량 부족이나 기타 일시적인 문제로 인해 영역 중 하나에서 VM을 만들 수 없는 경우 클러스터를 만들거나 업그레이드할 수 없습니다.

영역 클러스터와 리전 클러스터는 장단점이 서로 다르므로 사용 사례도 다릅니다.

  • 가용성이 크게 중요하지 않은 경우 클러스터를 빠르게 만들거나 업그레이드하려면 영역 클러스터를 사용합니다.
  • 가용성이 유연성보다 중요한 경우 리전 클러스터를 사용합니다.

클러스터가 생성된 후에는 클러스터를 변경할 수 없으므로 클러스터 유형은 신중하게 선택합니다. 변경하는 대신 새 클러스터를 만든 다음 트래픽을 해당 클러스터로 마이그레이션해야 합니다. 클러스터 간에 프로덕션 트래픽을 마이그레이션할 수는 있지만 대규모로는 어렵습니다.

멀티 영역 또는 단일 영역 노드 풀 선택

가용성을 높이려면 Kubernetes 제어 영역과 노드를 여러 영역에 분산시켜야 합니다. GKE는 단일 영역과 멀티 영역이라는 두 가지 노드 풀을 제공합니다.

가용성이 높은 애플리케이션을 배포하려면 여러 영역에 노드를 균등하게 배포하는 멀티 영역 노드 풀을 사용하여 한 리전의 여러 컴퓨팅 영역에 워크로드를 분산시킵니다.

모든 노드가 동일한 영역에 있는 경우 해당 영역에 도달할 수 없으면 포드를 예약할 수 없습니다. 멀티 영역 노드 풀을 사용할 경우 몇 가지 단점이 있습니다.

  • GPU는 특정 영역에서만 사용할 수 있습니다. 해당 리전의 모든 영역에서 GPU를 사용하는 것이 불가능할 수도 있습니다.

  • 단일 리전 내에서 영역 간 왕복 지연 시간은 단일 영역 내의 리소스 간 왕복 지연 시간보다 클 수 있습니다. 대부분의 워크로드에서는 이러한 차이가 중요하지 않습니다.

  • 동일한 리전의 영역 간 이그레스 트래픽 가격은 Compute Engine 가격 책정 페이지에서 확인할 수 있습니다.

확장 권장사항

기본 인프라

Kubernetes 워크로드에는 네트워킹, 컴퓨팅, 스토리지가 필요합니다. 포드를 실행하기에 충분한 CPU와 메모리를 제공해야 합니다. 하지만 GKE 클러스터의 성능과 확장성에 영향을 줄 수 있는 기본 인프라 매개변수가 더 있습니다.

클러스터 네트워킹

VPC 기반 클러스터를 사용하는 것이 네트워킹 기본값이며 새 GKE 클러스터를 설정할 때 권장되는 옵션입니다. VPC 기반 클러스터는 더 큰 워크로드, 더 많은 수의 노드, 기타 이점을 허용합니다.

이 모드의 VPC 네트워크에는 모든 포드 IP 주소를 위한 보조 범위가 있습니다. 그런 다음 각 노드에 자체 포드 IP 주소의 보조 범위 슬라이스가 할당됩니다. 이를 통해 VPC 네트워크는 커스텀 경로에 의존하지 않고 pod로 트래픽을 라우팅하는 방법을 기본적으로 이해할 수 있습니다. 단일 VPC 네트워크에는 최대 15,000개의 VM이 있을 수 있습니다.

지원 중단되고 1,500개 이하의 노드를 지원하는 또 다른 방법은 경로 기반 클러스터를 사용하는 것입니다. 경로 기반 클러스터는 대규모 워크로드에는 적합하지 않습니다. VPC 경로 할당량을 소비하고 VPC 기반 네트워킹의 다른 이점이 없습니다. 각 새 노드의 VPC 네트워크에 있는 라우팅 테이블에 새 커스텀 경로를 추가하는 방식으로 작동합니다.

비공개 클러스터

일반 GKE 클러스터에서 모든 노드는 공개 IP 주소를 갖습니다. 비공개 클러스터에서 노드는 인터넷에 대한 인바운드 및 아웃바운드 연결로부터 노드를 격리하는 내부 IP 주소만 갖습니다. GKE는 VPC 네트워크 피어링을 사용하여 Kubernetes API 서버를 실행하는 VM을 클러스터 나머지 부분과 연결합니다. 이렇게 하면 트래픽이 비공개 IP 주소를 사용하여 라우팅되므로 GKE 제어 영역과 노드 간의 처리량이 증가합니다.

비공개 클러스터를 사용하면 노드가 인터넷에 노출되지 않는다는 추가 보안 이점이 있습니다.

클러스터 부하 분산

GKE 인그레스 및 Cloud Load Balancing은 클러스터 외부와 공개 인터넷에 Kubernetes 워크로드를 노출하도록 부하 분산기를 구성하고 배포합니다. GKE 인그레스 및 서비스 컨트롤러는 GKE 워크로드를 대신하여 전달 규칙, URL 맵, 백엔드 서비스, 네트워크 엔드포인트 그룹 등의 객체를 배포합니다. 이러한 각 리소스에는 고유한 할당량 및 한도가 있으며 이러한 한도는 GKE에도 적용됩니다. 특정 Cloud Load Balancing 리소스가 할당량에 도달하면 지정된 인그레스 또는 서비스가 올바르게 배포되지 않고 리소스의 이벤트에 오류가 표시됩니다.

다음 표에서는 GKE 인그레스 및 서비스 사용 시 확장 한도를 설명합니다.

부하 분산기 클러스터당 노드 한도
내부 패스 스루 네트워크 부하 분산기
외부 패스 스루 네트워크 부하 분산기 영역당 노드 1,000개
외부 애플리케이션 부하 분산기
내부 애플리케이션 부하 분산기 노드 한도 없음

확장해야 하는 경우 Google Cloud 영업팀에 문의하여 이 한도를 늘리세요.

DNS

GKE의 서비스 검색은 클러스터 내에서 실행되는 포드에 DNS 변환을 제공하는 중앙 리소스인 kube-dns를 통해 제공됩니다. 이로 인해 매우 큰 클러스터 또는 요청 로드가 많은 워크로드에서 병목 현상이 발생할 수 있습니다. GKE는 클러스터의 크기에 따라 kube-dns를 자동 확장하여 용량을 늘립니다. 이 용량으로도 충분하지 않으면 GKE는 NodeLocal DNSCache를 사용하여 각 노드에서 DNS 쿼리의 분산 로컬 확인을 제공합니다. 이는 쿼리에 로컬로 응답하는 각 GKE 노드에 로컬 DNS 캐시를 제공하여 로드를 분산하고 응답 시간을 단축합니다.

VPC 기반 클러스터의 IP 주소 관리

VPC 기반 클러스터는 세 가지 IP 주소 범위를 사용합니다.

  • 노드 서브넷 기본 범위: 기본값은 /20(IP 주소 4,092개)입니다.
  • 포드 서브넷 보조 범위: 기본값은 /14(IP 주소 262,144개)입니다. 하지만 포드 서브넷을 구성할 수 있습니다.
  • 서비스 서브넷 보조 범위: 기본값은 /20(주소 4,096개)입니다. 하지만 이 서비스 서브넷을 만든 후에는 이 범위를 변경할 수 없습니다.

자세한 내용은 VPC 기반 클러스터의 IP 주소 범위를 참조하세요.

IP 주소 제한사항 및 추천:

  • 노드 한도: 노드 한도는 노드별로 기본 및 포드 IP 주소 모두에 따라 결정됩니다. 새 노드를 프로비저닝하려면 노드 및 포드 IP 주소 범위 모두에 주소가 충분하게 있어야 합니다. 기본적으로 포드 IP 주소 제한사항으로 인해 노드 1,024개만 만들 수 있습니다.
  • 노드당 포드 한도: 기본적으로 노드당 포드 한도는 포드 110개입니다. 하지만 효율적으로 사용할 수 있도록 노드당 포드 수를 줄여 더 작은 포드 CIDR을 구성할 수 있습니다.
  • RFC 1918 이상으로 확장: RFC 1918에서 정의한 비공개 공간 내에서 사용 가능한 것보다 많은 IP 주소가 필요한 경우 더욱 유연해지도록 비RFC 1918 비공개 주소 또는 PUPI를 사용하는 것이 좋습니다.
  • 서비스 및 포드의 보조 IP 주소 범위: 기본적으로 서비스 4,096개를 구성할 수 있습니다. 하지만 서비스 서브넷 범위를 선택하여 더 많은 서비스를 구성할 수 있습니다. 만든 후에는 보조 범위를 수정할 수 없습니다. 클러스터를 만들 때는 예상되는 확장 규모를 충분히 감당할 수 있는 범위를 선택해야 합니다. 하지만 나중에 연속되지 않은 멀티 포드 CIDR을 사용하여 포드에 더 많은 IP 주소를 추가할 수 있습니다. 자세한 내용은 포드의 IP 주소 여유 공간 부족을 참조하세요.

자세한 내용은 노드 제한 범위GKE로 마이그레이션할 때 IP 주소 계획을 참조하세요.

성능 향상을 위한 노드 구성

GKE 노드는 일반 Google Cloud 가상 머신입니다. 코어 수나 디스크 크기 등 일부 매개변수는 GKE 클러스터의 성능에 영향을 줄 수 있습니다.

포드 초기화 시간 줄이기

워크로드가 요청할 때 이미지 스트리밍을 사용하여 데이터를 대상 컨테이너 이미지에서 스트리밍하면 초기화 시간이 단축됩니다.

이그레스 트래픽

Google Cloud에서는 인스턴스에 할당된 머신 유형과 코어 수가 네트워크 용량을 결정합니다. 최대 이그레스 대역폭은 1Gbps~32Gbps이고 기본 e2-medium-2 머신의 최대 이그레스 대역폭은 2Gbps입니다. 대역폭 한도에 대한 상세 설명은 공유 코어 머신 유형을 참조하세요.

IOPS 및 디스크 처리량

Google Cloud에서 영구 디스크의 크기는 디스크의 IOPS 및 처리량을 결정합니다. GKE는 일반적으로 Persistent Disk를 부팅 디스크로 사용하고 Kubernetes의 영구 볼륨을 백업합니다. 디스크 크기를 늘리면 IOPS와 처리량이 모두 특정 한도까지 증가합니다.

각 영구 디스크 쓰기 작업은 가상 머신 인스턴스의 누적 네트워크 이그레스 한도에 기여합니다. 따라서 디스크, 특히 SSD의 IOPS 성능은 디스크 크기뿐만 아니라 인스턴스의 vCPU 수에 따라서도 달라집니다. 하위 코어 VM의 쓰기 IOPS 한도는 쓰기 처리량에 대한 네트워크 이그레스 한도로 인해 더 낮습니다.

가상 머신 인스턴스의 CPU 수가 충분하지 않으면 애플리케이션이 IOPS 한도에 가까워지지 않습니다. 일반적으로 예상 트래픽의 2,000~2,500 IOPS당 사용 가능한 CPU가 1개씩 있어야 합니다.

대용량 또는 다수의 디스크가 필요한 워크로드는 단일 VM에 연결할 수 있는 PD 수의 한도를 고려해야 합니다. 일반 VM의 경우 이 한도는 총 크기가 64TB인 디스크 128개이며, 공유 코어 VM의 한도는 총 크기가 3TB인 PD 16개입니다. 이 한도는 Kubernetes가 아닌 Google Cloud가 적용합니다.

제어 영역 측정항목 모니터링

사용 가능한 제어 영역 측정항목을 사용하여 모니터링 대시보드를 구성합니다. 제어 영역 측정항목을 사용하여 클러스터 상태를 관찰하고, 클러스터 구성 변경 결과를 관찰하거나(예: 추가 워크로드 또는 서드 파티 구성요소 배포), 문제를 해결할 수 있습니다.

모니터링할 가장 중요한 측정항목 중 하나는 Kubernetes API의 지연 시간입니다. 이는 지연 시간이 증가하는 경우 시스템에 과부하가 발생했음을 나타냅니다. 대량의 데이터를 전송하는 LIST 호출은 작은 요청보다 지연 시간이 훨씬 길어질 수 있습니다.

서드 파티 허용 웹훅의 응답성이 느려져 Kubernetes API 지연 시간이 늘어날 수도 있습니다. 측정항목을 사용하여 웹훅의 지연 시간을 측정하면 이러한 일반적인 문제를 감지할 수 있습니다.

Kubernetes 개발자 권장사항

주기적 목록 대신 목록 및 감시 패턴 사용

Kubernetes 개발자는 다음 요구사항이 있는 구성요소를 만들어야 할 수 있습니다.

  • 구성요소는 일부 Kubernetes 객체 목록을 주기적으로 검색해야 합니다.
  • 구성요소는 여러 인스턴스에서 실행되어야 합니다(DaemonSet의 경우 각 노드에도 실행).

이러한 구성요소는 주기적으로 검색된 객체의 상태가 변경되지 않더라도 kube-apiserver에서 부하 급증을 생성할 수 있습니다.

가장 간단한 방법은 주기적 LIST 호출을 사용하는 것입니다. 하지만 이렇게 하면 모든 객체를 메모리에 로드하여 직렬화하고 매번 전송해야 하므로 호출자와 서버 모두 비효율적이고 비용이 많이 드는 접근 방식입니다. LIST 요청을 과도하게 사용하면 제어 영역에 과부하가 발생하거나 이러한 요청이 과도하게 제한될 수 있습니다.

LIST 호출에서 resourceVersion=0 parameter를 설정하여 구성요소를 개선할 수 있습니다. 그러면 kube-apiserver에서 메모리 내 객체 캐시를 사용하여 kube-apiserver와 etcd 데이터베이스 및 관련 처리 간의 내부 상호작용 수를 줄일 수 있습니다.

반복 가능한 LIST 호출을 피하고 목록 및 감시 패턴으로 바꾸는 것이 좋습니다. 객체를 한 번 나열한 후 Watch API를 사용하여 상태의 증분 변경을 가져옵니다. 이 방법은 처리 시간을 줄이고 주기적인 LIST 호출에 비해 트래픽을 최소화합니다. 객체가 변경되지 않으면 추가 부하가 생성되지 않습니다.

Go 언어를 사용하는 경우 이 패턴을 구현하는 Go 패키지의 SharedInformerSharedInformerFactory를 확인합니다.

감시 및 목록에서 생성된 불필요한 트래픽 제한

Kubernetes는 내부에서 감시를 사용하여 객체 업데이트에 대한 알림을 전송합니다. 정기적 LIST 호출보다 리소스가 적게 필요한 감시에서도 대규모 클러스터에서 감시를 처리하는 것은 클러스터 리소스의 상당 부분을 차지하고 클러스터 성능에 영향을 줄 수 있습니다. 가장 큰 부정적 영향은 여러 위치에서 자주 변경되는 객체를 관찰하는 감시의 생성으로 인해 발생합니다. 예를 들어 모든 노드에서 실행되는 구성요소의 모든 포드에 대한 데이터를 관찰하는 경우입니다. 클러스터에 서드 파티 코드 또는 확장 프로그램을 설치하면 내부적으로 이러한 감시를 만들 수 있습니다.

다음 권장사항을 따르는 것이 좋습니다.

  • 감시 및 LIST 호출에 의해 발생하는 불필요한 처리 및 트래픽을 줄입니다.
  • 여러 장소에서 자주 변경되는 객체(예: DaemonSets)를 관찰하는 감시를 만들지 않습니다.
  • (적극 권장) 단일 노드에서 필요한 데이터를 감시하고 처리하는 중앙 컨트롤러를 만듭니다.
  • 객체의 하위 집합만 감시합니다. 예를 들어 각 노드의 kubelet은 동일한 노드에 예약된 포드만 관찰합니다.
  • 대량의 감시 또는 LIST 호출을 수행하여 클러스터 성능에 영향을 줄 수 있는 서드 파티 구성요소 또는 확장 프로그램을 배포하지 마세요.

Kubernetes 객체 매니페스트 크기 제한

대규모 워크로드 크기 조정 또는 업데이트와 같이 높은 포드 처리량으로 빠른 작업이 필요한 경우 포드 매니페스트 크기를 최소한으로, 가급적 10KiB 미만으로 유지하는 것이 좋습니다.

Kubernetes는 리소스 매니페스트를 etcd에 저장합니다. 목록 및 감시 패턴을 사용할 때를 포함하여 리소스가 검색될 때마다 전체 매니페스트가 전송됩니다.

매니페스트 크기에는 다음과 같은 제한사항이 있습니다.

  • etcd의 각 객체의 최대 크기: 약 1.5MiB입니다.
  • 클러스터의 모든 etcd 객체에 대한 총 할당량: 사전 구성된 할당량 크기는 6GiB입니다. 여기에는 최근 150초 동안의 클러스터 기록에 있는 모든 객체의 모든 업데이트가 포함된 변경 로그가 포함됩니다.
  • 트래픽이 많은 기간 동안의 제어 영역 성능: 매니페스트 크기가 클수록 API 서버의 부하가 증가합니다.

드물게 처리되는 단일 객체의 경우 일반적으로 매니페스트 크기가 1.5MiB 미만이면 문제가 되지 않습니다. 그러나 매우 큰 워크로드의 포드와 같이 수많은 객체가 빈번하게 처리되는 경우 매니페스트 크기가 10KiB를 초과하면 API 호출 지연 시간이 증가하고 전반적인 성능이 저하될 수 있습니다. 특히 목록 및 감시가 매니페스트 크기의 영향을 많이 받을 수 있습니다. API 서버 트래픽이 많은 기간에는 최근 150초 동안의 버전 수가 빠르게 누적될 수 있으므로 etcd 할당량에도 문제가 발생할 수 있습니다.

포드의 매니페스트 크기를 줄이기 위해 Kubernetes ConfigMap을 활용하여 구성의 일부, 특히 클러스터의 여러 포드가 공유하는 부분을 저장할 수 있습니다. 예를 들어 환경 변수는 워크로드의 모든 포드에서 공유되는 경우가 많습니다.

ConfigMap 객체가 포드만큼 많고 크기가 크며 자주 처리된다면 ConfigMap 객체에서도 유사한 문제가 발생할 수 있습니다. 구성의 일부를 추출하는 방법은 전체 트래픽이 감소할 때 가장 유용합니다.

기본 서비스 계정 자동 마운트 사용 중지

포드에서 실행되는 로직이 Kubernetes API에 액세스할 필요가 없는 경우 관련 보안 비밀과 감시가 생성되지 않도록 서비스 계정의 기본 자동 마운트를 사용 중지해야 합니다.

서비스 계정을 지정하지 않고 포드를 만들면 Kubernetes가 다음 작업을 자동으로 수행합니다.

  • 기본 서비스 계정을 포드에 할당합니다.
  • 서비스 계정 사용자 인증 정보를 포드의 보안 비밀로 마운트합니다.
  • 마운트된 모든 보안 비밀에 대해 kubelet은 감시를 만들어 모든 노드에 있는 해당 보안 비밀의 변경사항을 관찰합니다.

대규모 클러스터에서 이러한 작업은 kube-apiserver에 상당한 부하를 발생시킬 수 있는 수천 개의 불필요한 감시를 나타냅니다.

API 요청에 JSON 대신 프로토콜 버퍼 사용

Kubernetes API 개념에 설명된 대로 확장성이 뛰어난 구성요소를 구현할 때는 프로토콜 버퍼를 사용합니다.

Kubernetes REST API는 JSON 및 프로토콜 버퍼를 객체의 직렬화 형식으로 지원합니다. JSON은 기본적으로 사용되지만, 프로토콜 버퍼는 CPU 집약도가 낮은 프로세스를 필요로 하고 네트워크를 통해 더 적은 데이터를 전송하므로 규모에 맞게 더 효율적입니다. JSON 처리와 관련된 오버헤드는 대용량 데이터를 나열할 때 시간 초과를 초래할 수 있습니다.

다음 단계