클러스터의 모든 파드는 고유한 IP 주소를 갖는다.
이는 즉 파드간 연결을 명시적으로 만들 필요가 없으며
또한 컨테이너 포트를 호스트 포트에 매핑할 필요가 거의 없음을 의미한다.
이로 인해 포트 할당, 네이밍, 서비스 디스커버리,
로드 밸런싱,
애플리케이션 구성, 마이그레이션 관점에서 파드를 VM 또는 물리 호스트처럼 다룰 수 있는 깔끔하고
하위 호환성을 갖는 모델이 제시된다.
쿠버네티스는 모든 네트워킹 구현에 대해 다음과 같은 근본적인 요구사항을 만족할 것을 요구한다.
(이를 통해, 의도적인 네트워크 분할 정책을 방지)
노드 상의 에이전트(예: 시스템 데몬, kubelet)는 해당 노드의 모든
파드와 통신할 수 있다.
참고: 파드를 호스트 네트워크에서 구동하는 것도 지원하는 플랫폼(예:
리눅스)에 대해서는, 파드가 노드의 호스트 네트워크에 연결되어 있을 때에도 파드는 여전히
NAT 없이 모든 노드의 모든 파드와 통신할 수 있다.
이 모델은 전반적으로 덜 복잡할 뿐만 아니라,
무엇보다도 VM에 있던 앱을 컨테이너로 손쉽게 포팅하려는 쿠버네티스 요구사항을 만족시킬 수 있다.
작업을 기존에는 VM에서 실행했었다면, VM은 IP주소를 가지며 프로젝트 내의 다른 VM과 통신할 수 있었을 것이다.
이는 동일한 기본 모델이다.
쿠버네티스 IP 주소는 파드 범주에 존재하며,
파드 내의 컨테이너들은 IP 주소, MAC 주소를 포함하는 네트워크 네임스페이스를 공유한다.
이는 곧 파드 내의 컨테이너들이 각자의 포트에 localhost로 접근할 수 있음을 의미한다.
또한 파드 내의 컨테이너들이 포트 사용에 있어 서로 협조해야 하는데,
이는 VM 내 프로세스 간에도 마찬가지이다.
이러한 모델은 "파드 별 IP" 모델로 불린다.
이것이 어떻게 구현되는지는 사용하는 컨테이너 런타임의 상세사항이다.
노드 자체의 포트를 파드로 포워드하도록 요청하는 것도 가능하지만(호스트 포트라고 불림),
이는 매우 비주류적인(niche) 동작이다.
포워딩이 어떻게 구현되는지도 컨테이너 런타임의 상세사항이다.
파드 자체는 호스트 포트 존재 유무를 인지할 수 없다.
쿠버네티스를 사용하면 익숙하지 않은 서비스 디스커버리 메커니즘을 사용하기 위해 애플리케이션을 수정할 필요가 없다.
쿠버네티스는 파드에게 고유한 IP 주소와 파드 집합에 대한 단일 DNS 명을 부여하고,
그것들 간에 로드-밸런스를 수행할 수 있다.
동기
쿠버네티스 파드는 클러스터 목표 상태(desired state)와
일치하도록 생성되고 삭제된다. 파드는 비영구적 리소스이다.
만약 앱을 실행하기 위해 디플로이먼트를 사용한다면,
동적으로 파드를 생성하고 제거할 수 있다.
각 파드는 고유한 IP 주소를 갖지만, 디플로이먼트에서는
한 시점에 실행되는 파드 집합이
잠시 후 실행되는 해당 파드 집합과 다를 수 있다.
이는 다음과 같은 문제를 야기한다. ("백엔드"라 불리는) 일부 파드 집합이
클러스터의 ("프론트엔드"라 불리는) 다른 파드에 기능을 제공하는 경우,
프론트엔드가 워크로드의 백엔드를 사용하기 위해,
프론트엔드가 어떻게 연결할 IP 주소를 찾아서 추적할 수 있는가?
서비스 로 들어가보자.
서비스 리소스
쿠버네티스에서 서비스는 파드의 논리적 집합과 그것들에 접근할 수 있는
정책을 정의하는 추상적 개념이다. (때로는 이 패턴을
마이크로-서비스라고 한다.) 서비스가 대상으로 하는 파드 집합은 일반적으로
셀렉터가 결정한다.
서비스 엔드포인트를 정의하는 다른 방법에 대한 자세한 내용은
셀렉터가 없는 서비스를 참고한다.
예를 들어, 3개의 레플리카로 실행되는 스테이트리스 이미지-처리 백엔드를
생각해보자. 이러한 레플리카는 대체 가능하다. 즉, 프론트엔드는 그것들이 사용하는 백엔드를
신경쓰지 않는다. 백엔드 세트를 구성하는 실제 파드는 변경될 수 있지만,
프론트엔드 클라이언트는 이를 인식할 필요가 없으며, 백엔드 세트 자체를 추적해야 할 필요도
없다.
서비스 추상화는 이러한 디커플링을 가능하게 한다.
클라우드-네이티브 서비스 디스커버리
애플리케이션에서 서비스 디스커버리를 위해 쿠버네티스 API를 사용할 수 있는 경우,
매치되는 엔드포인트슬라이스를
API 서버에 질의할 수 있다.
쿠버네티스는 서비스의 파드가 변경될 때마다 서비스의 엔드포인트슬라이스를 업데이트한다.
네이티브 애플리케이션이 아닌 (non-native applications) 경우, 쿠버네티스는 애플리케이션과 백엔드 파드 사이에 네트워크 포트 또는 로드
밸런서를 배치할 수 있는 방법을 제공한다.
서비스 정의
쿠버네티스의 서비스는 파드와 비슷한 REST 오브젝트이다. 모든 REST 오브젝트와
마찬가지로, 서비스 정의를 API 서버에 POST하여
새 인스턴스를 생성할 수 있다.
서비스 오브젝트의 이름은 유효한
RFC 1035 레이블 이름이어야 한다.
예를 들어, 각각 TCP 포트 9376에서 수신하고
app.kubernetes.io/name=MyApp 레이블을 가지고 있는 파드 세트가 있다고 가정해 보자.
이것은 서로 다른 포트 번호를 통해 가용한 동일 네트워크 프로토콜이 있고,
단일 구성 이름을 사용하는 서비스 내에 혼합된 파드가 존재해도 가능하다.
이를 통해 서비스를 배포하고 진전시키는 데 많은 유연성을 제공한다.
예를 들어, 클라이언트를 망가뜨리지 않고,
백엔드 소프트웨어의 다음 버전에서 파드가 노출시키는 포트 번호를 변경할 수 있다.
이 서비스에는 셀렉터가 없으므로, 매칭되는 엔드포인트슬라이스
(및 레거시 엔드포인트) 오브젝트가 자동으로 생성되지 않는다.
엔드포인트슬라이스 오브젝트를 수동으로 추가하여,
서비스를 실행 중인 네트워크 주소 및 포트에 서비스를 수동으로 매핑할 수 있다. 예시는 다음과 같다.
apiVersion:discovery.k8s.io/v1kind:EndpointSlicemetadata:name:my-service-1# 관행적으로, 서비스의 이름을# 엔드포인트슬라이스 이름의 접두어로 사용한다.labels:# "kubernetes.io/service-name" 레이블을 설정해야 한다.# 이 레이블의 값은 서비스의 이름과 일치하도록 지정한다.kubernetes.io/service-name:my-serviceaddressType:IPv4ports:- name:''# 9376 포트는 (IANA에 의해) 잘 알려진 포트로 할당되어 있지 않으므로# 이 칸은 비워 둔다.appProtocol:httpprotocol:TCPport:9376endpoints:- addresses:- "10.4.5.6"# 이 목록에 IP 주소를 기재할 때 순서는 상관하지 않는다.- "10.1.2.3"
커스텀 엔드포인트슬라이스
서비스를 위한 엔드포인트슬라이스 오브젝트를 생성할 때,
엔드포인트슬라이스 이름으로는 원하는 어떤 이름도 사용할 수 있다.
네임스페이스 내의 각 엔드포인트슬라이스 이름은 고유해야 한다.
해당 엔드포인트슬라이스에 kubernetes.io/service-name레이블을 설정하여
엔드포인트슬라이스를 서비스와 연결할 수 있다.
참고:
엔드포인트 IP는 루프백(loopback) (IPv4의 경우 127.0.0.0/8, IPv6의 경우 ::1/128), 또는
링크-로컬 (IPv4의 경우 169.254.0.0/16와 224.0.0.0/24, IPv6의 경우 fe80::/64)이 되어서는 안된다.
엔드포인트 IP 주소는 다른 쿠버네티스 서비스의 클러스터 IP일 수 없는데,
kube-proxy는 가상 IP를
목적지(destination)로 지원하지 않기 때문이다.
직접 생성했거나 직접 작성한 코드에 의해 생성된 엔드포인트슬라이스를 위해,
endpointslice.kubernetes.io/managed-by
레이블에 사용할 값을 골라야 한다.
엔드포인트슬라이스를 관리하는 컨트롤러 코드를 직접 작성하는 경우,
"my-domain.example/name-of-controller"와 같은 값을 사용할 수 있다.
써드파티 도구를 사용하는 경우, 도구의 이름에서 대문자는 모두 소문자로 바꾸고
공백 및 다른 문장 부호는 하이픈(-)으로 대체한 문자열을 사용한다.
kubectl과 같은 도구를 사용하여 직접 엔드포인트슬라이스를 관리하는 경우,
"staff" 또는 "cluster-admins"와 같이 이러한 수동 관리를 명시하는 이름을 사용한다.
쿠버네티스 자체 컨트롤 플레인이 관리하는 엔드포인트슬라이스를 가리키는
"controller"라는 예약된 값은 사용하지 말아야 한다.
셀렉터가 없는 서비스에 접근하기
셀렉터가 없는 서비스에 접근하는 것은 셀렉터가 있는 서비스에 접근하는 것과 동일하게 동작한다.
셀렉터가 없는 서비스 예시에서, 트래픽은
엔드포인트슬라이스 매니페스트에 정의된 두 엔드포인트 중 하나로 라우트된다(10.1.2.3:9376 또는 10.4.5.6:9376으로의 TCP 연결).
ExternalName 서비스는 셀렉터가 없고
대신 DNS 이름을 사용하는 특이 케이스 서비스이다.
자세한 내용은 이 문서 뒷부분의 ExternalName 섹션을 참조한다.
엔드포인트슬라이스
기능 상태:Kubernetes v1.21 [stable]
엔드포인트슬라이스는
특정 서비스의 하위(backing) 네트워크 엔드포인트 부분집합(슬라이스)을 나타내는 오브젝트이다.
쿠버네티스 클러스터는 각 엔드포인트슬라이스가 얼마나 많은 엔드포인트를 나타내는지를 추적한다.
한 서비스의 엔드포인트가 너무 많아 역치에 도달하면,
쿠버네티스는 빈 엔드포인트슬라이스를 생성하고 여기에 새로운 엔드포인트 정보를 저장한다.
기본적으로, 쿠버네티스는 기존의 모든 엔드포인트슬라이스가
엔드포인트를 최소 100개 이상 갖게 되면 새 엔드포인트슬라이스를 생성한다.
쿠버네티스는 새 엔드포인트가 추가되어야 하는 상황이 아니라면
새 엔드포인트슬라이스를 생성하지 않는다.
쿠버네티스 API에서,
엔드포인트(Endpoints)(리소스 명칭이 복수형임)는
네트워크 엔드포인트의 목록을 정의하며,
일반적으로 트래픽이 어떤 파드에 보내질 수 있는지를 정의하기 위해 서비스가 참조한다.
엔드포인트 대신 엔드포인트슬라이스 API를 사용하는 것을 권장한다.
용량 한계를 넘어선 엔드포인트
쿠버네티스는 단일 엔드포인트(Endpoints) 오브젝트에 포함될 수 있는 엔드포인트(endpoints)의 수를 제한한다.
단일 서비스에 1000개 이상의 하위(backing) 엔드포인트가 있으면,
쿠버네티스는 엔드포인트 오브젝트의 데이터를 덜어낸다(truncate).
서비스는 하나 이상의 엔드포인트슬라이스와 연결될 수 있기 때문에,
하위 엔드포인트 1000개 제한은 기존(legacy) 엔드포인트 API에만 적용된다.
쿠버네티스의 일반적인 이름과 마찬가지로, 포트 이름은
소문자 영숫자와 - 만 포함해야 한다. 포트 이름은
영숫자로 시작하고 끝나야 한다.
예를 들어, 123-abc 와 web 은 유효하지만, 123_abc 와 -web 은 유효하지 않다.
자신의 IP 주소 선택
서비스 생성 요청시 고유한 클러스터 IP 주소를 지정할 수
있다. 이를 위해, .spec.clusterIP 필드를 설정한다. 예를 들어,
재사용하려는 기존 DNS 항목이 있거나, 특정 IP 주소로 구성되어
재구성이 어려운 레거시 시스템인 경우이다.
선택한 IP 주소는 API 서버에 대해 구성된 service-cluster-ip-range
CIDR 범위 내의 유효한 IPv4 또는 IPv6 주소여야 한다.
유효하지 않은 clusterIP 주소 값으로 서비스를 생성하려고 하면, API 서버는
422 HTTP 상태 코드를 리턴하여 문제점이 있음을 알린다.
서비스 디스커버리하기
쿠버네티스는 서비스를 찾는 두 가지 기본 모드를 지원한다. - 환경
변수와 DNS
환경 변수
파드가 노드에서 실행될 때, kubelet은 각 활성화된 서비스에 대해 환경 변수 세트를 추가한다.
{SVCNAME}_SERVICE_HOST 및 {SVCNAME}_SERVICE_PORT 환경 변수가 추가되는데,
이 때 서비스 이름은 대문자로, 하이픈(-)은 언더스코어(_)로 변환하여 사용한다.
또한 도커 엔진의 "레거시 컨테이너 연결" 기능과
호환되는 변수(makeLinkVariables 참조)도
지원한다.
예를 들어, TCP 포트 6379를 개방하고
클러스터 IP 주소 10.0.0.11이 할당된 서비스 redis-primary는,
다음 환경 변수를 생성한다.
서비스에 접근이 필요한 파드가 있고, 환경 변수를
사용해 포트 및 클러스터 IP를 클라이언트 파드에 부여하는
경우, 클라이언트 파드가 생성되기 전에 서비스를 만들어야 한다.
그렇지 않으면, 해당 클라이언트 파드는 환경 변수를 생성할 수 없다.
DNS 만 사용하여 서비스의 클러스터 IP를 검색하는 경우, 이 순서
이슈에 대해 신경 쓸 필요가 없다.
DNS
애드-온을 사용하여 쿠버네티스
클러스터의 DNS 서비스를 설정할 수(대개는 필수적임) 있다.
CoreDNS와 같은, 클러스터-인식 DNS 서버는 새로운 서비스를 위해 쿠버네티스 API를 감시하고
각각에 대한 DNS 레코드 세트를 생성한다. 클러스터 전체에서 DNS가 활성화된 경우
모든 파드는 DNS 이름으로 서비스를 자동으로
확인할 수 있어야 한다.
예를 들면, 쿠버네티스 네임스페이스 my-ns에 my-service라는
서비스가 있는 경우, 컨트롤 플레인과 DNS 서비스가 함께 작동하여
my-service.my-ns에 대한 DNS 레코드를 만든다. my-ns 네임 스페이스의 파드들은
my-service(my-service.my-ns 역시 동작함)에 대한 이름 조회를
수행하여 서비스를 찾을 수 있어야 한다.
다른 네임스페이스의 파드들은 이름을 my-service.my-ns으로 사용해야 한다. 이 이름은
서비스에 할당된 클러스터 IP로 변환된다.
쿠버네티스는 또한 알려진 포트에 대한 DNS SRV (서비스) 레코드를 지원한다.
my-service.my-ns 서비스에 프로토콜이 TCP로 설정된 http라는 포트가 있는 경우,
IP 주소와 http에 대한 포트 번호를 검색하기 위해 _http._tcp.my-service.my-ns 에 대한
DNS SRV 쿼리를 수행할 수 있다.
쿠버네티스 DNS 서버는 ExternalName 서비스에 접근할 수 있는 유일한 방법이다.
DNS 파드와 서비스에서
ExternalName 검색에 대한 자세한 정보를 찾을 수 있다.
헤드리스(Headless) 서비스
때때로 로드-밸런싱과 단일 서비스 IP는 필요치 않다. 이 경우,
"헤드리스" 서비스라는 것을 만들 수 있는데, 명시적으로
클러스터 IP (.spec.clusterIP)에 "None"을 지정한다.
쿠버네티스의 구현에 묶이지 않고, 헤드리스 서비스를 사용하여
다른 서비스 디스커버리 메커니즘과 인터페이스할 수 있다.
헤드리스 서비스의 경우, 클러스터 IP가 할당되지 않고, kube-proxy가
이러한 서비스를 처리하지 않으며, 플랫폼에 의해 로드 밸런싱 또는 프록시를
하지 않는다. DNS가 자동으로 구성되는 방법은 서비스에 셀렉터가 정의되어 있는지
여부에 달려있다.
셀렉터가 있는 경우
셀렉터를 정의하는 헤드리스 서비스의 경우, 쿠버네티스 컨트롤 플레인은
쿠버네티스 API 내에서 엔드포인트슬라이스 오브젝트를 생성하고,
서비스 하위(backing) 파드들을 직접 가리키는
A 또는 AAAA 레코드(IPv4 또는 IPv6 주소)를 반환하도록 DNS 구성을 변경한다.
셀렉터가 없는 경우
셀렉터를 정의하지 않는 헤드리스 서비스의 경우,
쿠버네티스 컨트롤 플레인은 엔드포인트슬라이스 오브젝트를 생성하지 않는다.
하지만, DNS 시스템은 다음 중 하나를 탐색한 뒤 구성한다.
ExternalName 이외의 모든 서비스 타입에 대해,
서비스의 활성(ready) 엔드포인트의 모든 IP 주소에 대한 DNS A / AAAA 레코드
IPv4 엔드포인트에 대해, DNS 시스템은 A 레코드를 생성한다.
IPv6 엔드포인트에 대해, DNS 시스템은 AAAA 레코드를 생성한다.
서비스 퍼블리싱 (ServiceTypes)
애플리케이션 중 일부(예: 프론트엔드)는 서비스를 클러스터 밖에
위치한 외부 IP 주소에 노출하고 싶은 경우가 있을 것이다.
쿠버네티스 ServiceTypes는 원하는 서비스 종류를 지정할 수 있도록 해 준다.
Type 값과 그 동작은 다음과 같다.
ClusterIP: 서비스를 클러스터-내부 IP에 노출시킨다.
이 값을 선택하면 클러스터 내에서만 서비스에 도달할 수 있다.
이것은 서비스의 type을 명시적으로 지정하지 않았을 때의 기본값이다.
NodePort: 고정 포트 (NodePort)로 각 노드의 IP에
서비스를 노출시킨다. 노드 포트를 사용할 수 있도록 하기 위해,
쿠버네티스는 type: ClusterIP인 서비스를 요청했을 때와 마찬가지로
클러스터 IP 주소를 구성한다.
LoadBalancer: 클라우드 공급자의 로드 밸런서를 사용하여
서비스를 외부에 노출시킨다.
ExternalName: 값과 함께 CNAME 레코드를 리턴하여,
서비스를 externalName 필드의 내용(예:foo.bar.example.com)에 매핑한다.
어떠한 종류의 프록시도 설정되지 않는다.
참고:ExternalName 유형을 사용하려면 kube-dns 버전 1.7 또는
CoreDNS 버전 0.0.8 이상이 필요하다.
type 필드는 중첩(nested) 기능으로 설계되어, 각 단계는 이전 단계에 더해지는 형태이다.
이는 모든 클라우드 공급자에 대해 엄격히 요구되는 사항은 아니다(예:
Google Compute Engine에서는 type: LoadBalancer가 동작하기 위해 노드 포트를 할당할 필요가 없지만,
다른 클라우드 공급자 통합 시에는 필요할 수 있음).
엄격한 중첩이 필수 사항은 아니지만, 서비스에 대한 쿠버네티스 API 디자인은 이와 상관없이 엄격한 중첩 구조를 가정한다.
인그레스를 사용하여 서비스를 노출시킬 수도 있다.
인그레스는 서비스 유형은 아니지만, 클러스터의 진입점 역할을 한다.
동일한 IP 주소로 여러 서비스를 노출시킬 수 있기 때문에
라우팅 규칙을 단일 리소스로 통합할 수 있다.
NodePort 유형
type 필드를 NodePort로 설정하면, 쿠버네티스 컨트롤 플레인은
--service-node-port-range 플래그로 지정된 범위에서 포트를 할당한다 (기본값 : 30000-32767).
각 노드는 해당 포트 (모든 노드에서 동일한 포트 번호)를 서비스로 프록시한다.
서비스는 할당된 포트를 .spec.ports[*].nodePort 필드에 나타낸다.
NodePort를 사용하면 자유롭게 자체 로드 밸런싱 솔루션을 설정하거나,
쿠버네티스가 완벽하게 지원하지 않는 환경을 구성하거나,
하나 이상의 노드 IP를 직접 노출시킬 수 있다.
NodePort 서비스에 대해, 쿠버네티스는 포트를 추가로
할당한다(서비스의 프로토콜에 매치되도록 TCP, UDP, SCTP 중 하나).
클러스터의 모든 노드는 할당된 해당 포트를 리슨하고
해당 서비스에 연결된 활성(ready) 엔드포인트 중 하나로 트래픽을 전달하도록 자기 자신을 구성한다.
적절한 프로토콜(예: TCP) 및 적절한 포트(해당 서비스에 할당된 대로)로
클러스터 외부에서 클러스터의 아무 노드에 연결하여 type: NodePort 서비스로 접근할 수 있다.
포트 직접 선택하기
특정 포트 번호를 원한다면, nodePort 필드에 값을 명시할 수 있다.
컨트롤 플레인은 해당 포트를 할당해 주거나 또는
해당 API 트랜젝션이 실패했다고 알려줄 것이다.
이는 사용자 스스로 포트 충돌의 가능성을 고려해야 한다는 의미이다.
또한 유효한(NodePort용으로 사용할 수 있도록 구성된 범위 내의)
포트 번호를 사용해야 한다.
다음은 NodePort 값을 명시하는(이 예시에서는 30007)
type: NodePort 서비스에 대한 예시 매니페스트이다.
apiVersion:v1kind:Servicemetadata:name:my-servicespec:type:NodePortselector:app.kubernetes.io/name:MyAppports:# 기본적으로 그리고 편의상 `targetPort` 는 `port` 필드와 동일한 값으로 설정된다.- port:80targetPort:80# 선택적 필드# 기본적으로 그리고 편의상 쿠버네티스 컨트롤 플레인은 포트 범위에서 할당한다(기본값: 30000-32767)nodePort:30007
type: NodePort 서비스를 위한 커스텀 IP 주소 구성
NodePort 서비스 노출에 특정 IP 주소를 사용하도록
클러스터의 노드를 설정할 수 있다.
각 노드가 여러 네트워크(예: 애플리케이션 트래픽용 네트워크 및
노드/컨트롤 플레인 간 트래픽용 네트워크)에 연결되어 있는 경우에 이러한 구성을 고려할 수 있다.
포트를 프록시하기 위해 특정 IP를 지정하려면, kube-proxy에 대한
--nodeport-addresses 플래그 또는
kube-proxy 구성 파일의
동등한 nodePortAddresses 필드를
특정 IP 블록으로 설정할 수 있다.
이 플래그는 쉼표로 구분된 IP 블록 목록(예: 10.0.0.0/8, 192.0.2.0/25)을 사용하여
kube-proxy가 로컬 노드로 고려해야 하는 IP 주소 범위를 지정한다.
예를 들어, --nodeport-addresses=127.0.0.0/8 플래그로 kube-proxy를 시작하면,
kube-proxy는 NodePort 서비스에 대하여 루프백(loopback) 인터페이스만 선택한다.
--nodeport-addresses의 기본 값은 비어있는 목록이다.
이것은 kube-proxy가 NodePort에 대해 사용 가능한 모든 네트워크 인터페이스를 고려해야 한다는 것을 의미한다.
(이는 이전 쿠버네티스 릴리스와도 호환된다).
참고: 이 서비스는 <NodeIP>:spec.ports[*].nodePort와 .spec.clusterIP:spec.ports[*].port로 표기된다.
kube-proxy에 대한 --nodeport-addresses 플래그 또는 kube-proxy 구성 파일의 동등한 필드가 설정된 경우,
<NodeIP> 는 노드 IP를 필터링한다.
로드밸런서 유형
외부 로드 밸런서를 지원하는 클라우드 공급자 상에서, type
필드를 LoadBalancer로 설정하면 서비스에 대한 로드 밸런서를 프로비저닝한다.
로드 밸런서의 실제 생성은 비동기적으로 수행되고,
프로비저닝된 밸런서에 대한 정보는 서비스의
.status.loadBalancer 필드에 발행된다.
예를 들면 다음과 같다.
외부 로드 밸런서의 트래픽은 백엔드 파드로 전달된다.
클라우드 공급자는 로드 밸런싱 방식을 결정한다.
일부 클라우드 공급자는 loadBalancerIP를 지정할 수 있도록 허용한다. 이 경우, 로드 밸런서는
사용자 지정 loadBalancerIP로 생성된다. loadBalancerIP 필드가 지정되지 않으면,
임시 IP 주소로 loadBalancer가 설정된다. loadBalancerIP를 지정했지만
클라우드 공급자가 이 기능을 지원하지 않는 경우, 설정한 loadbalancerIP 필드는
무시된다.
type: LoadBalancer인 서비스를 구현하기 위해,
쿠버네티스는 일반적으로 type: NodePort 서비스를 요청했을 때와 동일한 변경사항을 적용하면서 시작한다.
그런 다음 cloud-controller-manager 컴포넌트는
할당된 해당 NodePort로 트래픽을 전달하도록 외부 로드 밸런서를 구성한다.
알파 기능으로서, 로드 밸런스된 서비스가 NodePort 할당을
생략하도록 구성할 수 있는데,
이는 클라우드 공급자의 구현이 이를 지원할 때에만 가능하다.
참고:
Azure 에서 사용자 지정 공개(public) 유형 loadBalancerIP를 사용하려면, 먼저
정적 유형 공개 IP 주소 리소스를 생성해야 한다. 이 공개 IP 주소 리소스는
클러스터에서 자동으로 생성된 다른 리소스와 동일한 리소스 그룹에 있어야 한다.
예를 들면, MC_myResourceGroup_myAKSCluster_eastus이다.
기본적으로 로드밸런서 서비스 유형의 경우 둘 이상의 포트가 정의되어 있을 때 모든
포트는 동일한 프로토콜을 가져야 하며 프로토콜은 클라우드 공급자가
지원하는 프로토콜이어야 한다.
MixedProtocolLBService 기능 게이트(v1.24에서 kube-apiserver에 대해 기본적으로 활성화되어 있음)는
둘 이상의 포트가 정의되어 있는 경우에 로드밸런서 타입의 서비스에 대해 서로 다른 프로토콜을 사용할 수 있도록 해 준다.
참고: 로드밸런서 서비스 유형에 사용할 수 있는 프로토콜 세트는 여전히 클라우드 제공 업체에서 정의한다.
클라우드 제공자가 혼합 프로토콜을 지원하지 않는다면 이는 단일 프로토콜만을 제공한다는 것을 의미한다.
로드밸런서 NodePort 할당 비활성화
기능 상태:Kubernetes v1.24 [stable]
type=LoadBalancer 서비스에 대한 노드 포트 할당을 선택적으로 비활성화할 수 있으며,
이는 spec.allocateLoadBalancerNodePorts 필드를 false로 설정하면 된다.
노드 포트를 사용하지 않고 트래픽을 파드로 직접 라우팅하는 로드 밸런서 구현에만 사용해야 한다.
기본적으로 spec.allocateLoadBalancerNodePorts는 true이며 로드밸런서 서비스 유형은 계속해서 노드 포트를 할당할 것이다.
노드 포트가 할당된 기존 서비스에서 spec.allocateLoadBalancerNodePorts가 false로 설정된 경우 해당 노드 포트는 자동으로 할당 해제되지 않는다.
이러한 노드 포트를 할당 해제하려면 모든 서비스 포트에서 nodePorts 항목을 명시적으로 제거해야 한다.
로드 밸런서 구현 클래스 지정
기능 상태:Kubernetes v1.24 [stable]
spec.loadBalancerClass 필드를 설정하여 클라우드 제공자가 설정한 기본값 이외의 로드 밸런서 구현을 사용할 수 있다.
기본적으로, spec.loadBalancerClass 는 nil 이고,
클러스터가 클라우드 제공자의 로드밸런서를 이용하도록 --cloud-provider 컴포넌트 플래그를 이용하여 설정되어 있으면
LoadBalancer 유형의 서비스는 클라우드 공급자의 기본 로드 밸런서 구현을 사용한다.
spec.loadBalancerClass 가 지정되면, 지정된 클래스와 일치하는 로드 밸런서
구현이 서비스를 감시하고 있다고 가정한다.
모든 기본 로드 밸런서 구현(예: 클라우드 공급자가 제공하는
로드 밸런서 구현)은 이 필드가 설정된 서비스를 무시한다.
spec.loadBalancerClass 는 LoadBalancer 유형의 서비스에서만 설정할 수 있다.
한 번 설정하면 변경할 수 없다.
spec.loadBalancerClass 의 값은 "internal-vip" 또는
"example.com/internal-vip" 와 같은 선택적 접두사가 있는 레이블 스타일 식별자여야 한다.
접두사가 없는 이름은 최종 사용자를 위해 예약되어 있다.
내부 로드 밸런서
혼재된 환경에서는 서비스의 트래픽을 동일한 (가상) 네트워크 주소 블록 내로
라우팅해야 하는 경우가 있다.
수평 분할 DNS 환경에서는 외부와 내부 트래픽을 엔드포인트로 라우팅 할 수 있는
두 개의 서비스가 필요하다.
내부 로드 밸런서를 설정하려면, 사용 중인 클라우드 서비스 공급자에 따라
다음의 어노테이션 중 하나를 서비스에 추가한다.
버전 1.3.0 부터, 이 어노테이션의 사용은 ELB에 의해 프록시되는 모든 포트에 적용되며
다르게 구성할 수 없다.
AWS의 ELB 접근 로그
AWS ELB 서비스의 접근 로그를 관리하기 위한 몇 가지 어노테이션이 있다.
service.beta.kubernetes.io/aws-load-balancer-access-log-enabled 어노테이션은
접근 로그의 활성화 여부를 제어한다.
service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval 어노테이션은
접근 로그를 게시하는 간격을 분 단위로 제어한다. 5분 또는 60분의
간격으로 지정할 수 있다.
service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name 어노테이션은
로드 밸런서 접근 로그가 저장되는 Amazon S3 버킷의 이름을
제어한다.
service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix 어노테이션은
Amazon S3 버킷을 생성한 논리적 계층을 지정한다.
metadata:name:my-serviceannotations:# 로드 밸런서의 접근 로그 활성화 여부를 명시.service.beta.kubernetes.io/aws-load-balancer-access-log-enabled:"true"# 접근 로그를 게시하는 간격을 분 단위로 제어. 5분 또는 60분의 간격을 지정.service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval:"60"# 로드 밸런서 접근 로그가 저장되는 Amazon S3 버킷의 이름 명시.service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name:"my-bucket"# Amazon S3 버킷을 생성한 논리적 계층을 지정. 예: `my-bucket-prefix/prod`service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix:"my-bucket-prefix/prod"
AWS의 연결 드레이닝(Draining)
Classic ELB의 연결 드레이닝은
service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabled 어노테이션을
"true"값으로 설정하여 관리할 수 있다. service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeout 어노테이션을
사용하여 인스턴스를 해제하기 전에,
기존 연결을 열어 두는 목적으로 최대 시간을 초 단위로
설정할 수도 있다.
이하는 클래식 엘라스틱 로드 밸런서(Classic Elastic Load Balancers)를 관리하기 위한 다른 어노테이션이다.
metadata:name:my-serviceannotations:# 로드 밸런서가 연결을 닫기 전에, 유휴 상태(연결을 통해 전송 된 # 데이터가 없음)의 연결을 허용하는 초단위 시간service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout:"60"# 로드 밸런서에 교차-영역(cross-zone) 로드 밸런싱을 사용할 지 여부를 지정service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled:"true"# 쉼표로 구분된 key-value 목록은 ELB에# 추가 태그로 기록됨service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags:"environment=prod,owner=devops"# 백엔드가 정상인 것으로 간주되는데 필요한 연속적인# 헬스 체크 성공 횟수이다. 기본값은 2이며, 2와 10 사이여야 한다.service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold:""# 백엔드가 비정상인 것으로 간주되는데 필요한# 헬스 체크 실패 횟수이다. 기본값은 6이며, 2와 10 사이여야 한다.service.beta.kubernetes.io/aws-load-balancer-healthcheck-unhealthy-threshold:"3"# 개별 인스턴스의 상태 점검 사이의# 대략적인 간격 (초 단위). 기본값은 10이며, 5와 300 사이여야 한다.service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval:"20"# 헬스 체크 실패를 의미하는 무 응답의 총 시간 (초 단위)# 이 값은 service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval# 값 보다 작아야한다. 기본값은 5이며, 2와 60 사이여야 한다.service.beta.kubernetes.io/aws-load-balancer-healthcheck-timeout:"5"# 생성된 ELB에 설정할 기존 보안 그룹(security group) 목록.# service.beta.kubernetes.io/aws-load-balancer-extra-security-groups 어노테이션과 달리,# 이는 이전에 ELB에 할당된 다른 모든 보안 그룹을 대체하며,# '해당 ELB를 위한 고유 보안 그룹 생성'을 오버라이드한다.# 목록의 첫 번째 보안 그룹 ID는 인바운드 트래픽(서비스 트래픽과 헬스 체크)이# 워커 노드로 향하도록 하는 규칙으로 사용된다.# 여러 ELB가 하나의 보안 그룹 ID와 연결되면, 1줄의 허가 규칙만이# 워커 노드 보안 그룹에 추가된다.# 즉, 만약 여러 ELB 중 하나를 지우면, 1줄의 허가 규칙이 삭제되어, 같은 보안 그룹 ID와 연결된 모든 ELB에 대한 접속이 막힌다.# 적절하게 사용되지 않으면 이는 다수의 서비스가 중단되는 상황을 유발할 수 있다.service.beta.kubernetes.io/aws-load-balancer-security-groups:"sg-53fae93f"# 생성된 ELB에 추가할 추가 보안 그룹 목록# 이 방법을 사용하면 이전에 생성된 고유 보안 그룹이 그대로 유지되므로,# 각 ELB가 고유 보안 그룹 ID와 그에 매칭되는 허가 규칙 라인을 소유하여# 트래픽(서비스 트래픽과 헬스 체크)이 워커 노드로 향할 수 있도록 한다.# 여기에 기재되는 보안 그룹은 여러 서비스 간 공유될 수 있다.service.beta.kubernetes.io/aws-load-balancer-extra-security-groups:"sg-53fae93f,sg-42efd82e"# 로드 밸런서의 대상 노드를 선택하는 데# 사용되는 키-값 쌍의 쉼표로 구분된 목록service.beta.kubernetes.io/aws-load-balancer-target-node-labels:"ingress-gw,gw-name=public-api"
AWS의 네트워크 로드 밸런서 지원
기능 상태:Kubernetes v1.15 [beta]
AWS에서 네트워크 로드 밸런서를 사용하려면, nlb 값이 설정된 service.beta.kubernetes.io/aws-load-balancer-type 어노테이션을 사용한다.
참고: NLB는 특정 인스턴스 클래스에서만 작동한다. 지원되는 인스턴스 유형 목록은 엘라스틱 로드 밸런싱에 대한
AWS 문서
를 참고한다.
클래식 엘라스틱 로드 밸런서와 달리, 네트워크 로드 밸런서 (NLB)는
클라이언트의 IP 주소를 노드로 전달한다. 서비스의 .spec.externalTrafficPolicy가
Cluster로 설정되어 있으면, 클라이언트의 IP 주소가 종단 파드로
전파되지 않는다.
.spec.externalTrafficPolicy를 Local로 설정하면, 클라이언트 IP 주소가
종단 파드로 전파되지만, 트래픽이 고르지 않게
분배될 수 있다. 특정 로드밸런서 서비스를 위한 파드가 없는 노드는 자동 할당된
.spec.healthCheckNodePort에 의해서 NLB 대상 그룹의
헬스 체크에 실패하고 트래픽을 수신하지 못하게 된다.
클라이언트 트래픽이 NLB 뒤의 인스턴스에 도달하기 위해, 노드 보안
그룹은 다음 IP 규칙으로 수정된다.
규칙
프로토콜
포트
IP 범위
IP 범위 설명
헬스 체크
TCP
NodePort(s) (.spec.healthCheckNodePort for .spec.externalTrafficPolicy = Local)
Subnet CIDR
kubernetes.io/rule/nlb/health=<loadBalancerName>
클라이언트 트래픽
TCP
NodePort(s)
.spec.loadBalancerSourceRanges (defaults to 0.0.0.0/0)
kubernetes.io/rule/nlb/client=<loadBalancerName>
MTU 탐색
ICMP
3,4
.spec.loadBalancerSourceRanges (defaults to 0.0.0.0/0)
kubernetes.io/rule/nlb/mtu=<loadBalancerName>
네트워크 로드 밸런서에 접근할 수 있는 클라이언트 IP를 제한하려면,
loadBalancerSourceRanges를 지정한다.
spec:loadBalancerSourceRanges:- "143.231.0.0/16"
참고:.spec.loadBalancerSourceRanges가 설정되어 있지 않으면, 쿠버네티스는
0.0.0.0/0에서 노드 보안 그룹으로의 트래픽을 허용한다. 노드에 퍼블릭 IP 주소가
있는 경우, 비(non)-NLB 트래픽도 해당 수정된 보안 그룹의
모든 인스턴스에 도달할 수 있다.
아래 표시된 것처럼 TKE에서 클라우드 로드 밸런서를 관리하기 위한 다른 어노테이션이 있다.
metadata:name:my-serviceannotations:# 지정된 노드로 로드 밸런서 바인드service.kubernetes.io/qcloud-loadbalancer-backends-label:key in (value1, value2)# 기존 로드 밸런서의 IDservice.kubernetes.io/tke-existed-lbid:lb-6swtxxxx# 로드 밸런서 (LB)에 대한 사용자 지정 매개 변수는 아직 LB 유형 수정을 지원하지 않음service.kubernetes.io/service.extensiveParameters:""# LB 리스너의 사용자 정의 매개 변수service.kubernetes.io/service.listenerParameters:""# 로드 밸런서 유형 지정# 유효 값 : 클래식 (클래식 클라우드 로드 밸런서) 또는 애플리케이션 (애플리케이션 클라우드 로드 밸런서)service.kubernetes.io/loadbalance-type:xxxxx# 퍼블릭 네트워크 대역폭 청구 방법 지정# 유효 값: TRAFFIC_POSTPAID_BY_HOUR (트래픽 별) 및 BANDWIDTH_POSTPAID_BY_HOUR (대역폭 별)service.kubernetes.io/qcloud-loadbalancer-internet-charge-type:xxxxxx# 대역폭 값 지정 (값 범위 : [1,2000] Mbps).service.kubernetes.io/qcloud-loadbalancer-internet-max-bandwidth-out:"10"# 이 어느테이션이 설정되면, 로드 밸런서는 파드가# 실행중인 노드만 등록하고, 그렇지 않으면 모든 노드가 등록됨service.kubernetes.io/local-svc-only-bind-node-with-pod:true
ExternalName 유형
ExternalName 유형의 서비스는 my-service 또는 cassandra와 같은 일반적인 셀렉터에 대한 서비스가 아닌,
DNS 이름에 대한 서비스에 매핑한다. spec.externalName 파라미터를 사용하여 이러한 서비스를 지정한다.
예를 들면, 이 서비스 정의는 prod 네임 스페이스의
my-service 서비스를 my.database.example.com에 매핑한다.
참고: ExternalName은 IPv4 주소 문자열을 허용하지만, IP 주소가 아닌 숫자로 구성된 DNS 이름을 허용한다.
IPv4 주소와 유사한 ExternalName은 CoreDNS 또는 ingress-nginx에 의해 확인되지 않는데, ExternalName은
정식(canonical) DNS 이름을 지정하기 때문이다. IP 주소를 하드 코딩하려면,
헤드리스(headless) 서비스 사용을 고려한다.
my-service.prod.svc.cluster.local 호스트를 검색하면, 클러스터 DNS 서비스는
my.database.example.com 값의 CNAME 레코드를 반환한다. my-service에 접근하는 것은
다른 서비스와 같은 방식으로 작동하지만, 리다이렉션은 프록시 또는
포워딩을 통하지 않고 DNS 수준에서 발생한다는 중요한
차이점이 있다. 나중에 데이터베이스를 클러스터로 이동하기로 결정한 경우, 해당
파드를 시작하고 적절한 셀렉터 또는 엔드포인트를 추가하고,
서비스의 유형(type)을 변경할 수 있다.
경고:
HTTP 및 HTTPS를 포함한, 몇몇 일반적인 프로토콜에 ExternalName을 사용하는 것은 문제가 있을 수 있다.
ExternalName을 사용하는 경우, 클러스터 내부의 클라이언트가 사용하는 호스트 이름(hostname)이
ExternalName이 참조하는 이름과 다르다.
호스트 이름을 사용하는 프로토콜의 경우, 이러한 차이로 인해 오류가 발생하거나 예기치 않은 응답이 발생할 수 있다.
HTTP 요청에는 오리진(origin) 서버가 인식하지 못하는 Host : 헤더가 있다.
TLS 서버는 클라이언트가 연결된 호스트 이름과 일치하는 인증서를 제공할 수 없다.
하나 이상의 클러스터 노드로 라우팅되는 외부 IP가 있는 경우, 쿠버네티스 서비스는 이러한
externalIPs에 노출될 수 있다. 서비스 포트에서 외부 IP (목적지 IP)를 사용하여 클러스터로 들어오는 트래픽은
서비스 엔드포인트 중 하나로 라우팅된다. externalIPs는 쿠버네티스에 의해 관리되지 않으며
클러스터 관리자에게 책임이 있다.
서비스 명세에서, externalIPs는 모든 ServiceTypes와 함께 지정할 수 있다.
아래 예에서, 클라이언트는 "80.11.12.10:80"(외부 IP:포트)로 "my-service"에 접근할 수 있다.
URI, 호스트네임, 경로 등과 같은 웹 개념을 이해하는 프로토콜-인지형(protocol-aware configuration) 설정 메커니즘을 이용하여 HTTP (혹은 HTTPS) 네트워크 서비스를 사용 가능하게 한다. 인그레스 개념은 쿠버네티스 API를 통해 정의한 규칙에 기반하여 트래픽을 다른 백엔드에 매핑할 수 있게 해준다.
기능 상태:Kubernetes v1.19 [stable]
클러스터 내의 서비스에 대한 외부 접근을 관리하는 API 오브젝트이며, 일반적으로 HTTP를 관리함.
인그레스는 부하 분산, SSL 종료, 명칭 기반의 가상 호스팅을 제공할 수 있다.
용어
이 가이드는 용어의 명확성을 위해 다음과 같이 정의한다.
노드(Node): 클러스터의 일부이며, 쿠버네티스에 속한 워커 머신.
클러스터(Cluster): 쿠버네티스에서 관리되는 컨테이너화 된 애플리케이션을 실행하는 노드 집합. 이 예시와 대부분의 일반적인 쿠버네티스 배포에서 클러스터에 속한 노드는 퍼블릭 인터넷의 일부가 아니다.
에지 라우터(Edge router): 클러스터에 방화벽 정책을 적용하는 라우터. 이것은 클라우드 공급자 또는 물리적 하드웨어의 일부에서 관리하는 게이트웨이일 수 있다.
클러스터 네트워크(Cluster network): 쿠버네티스 네트워킹 모델에 따라 클러스터 내부에서 통신을 용이하게 하는 논리적 또는 물리적 링크 집합.
서비스: 레이블 셀렉터를 사용해서 파드 집합을 식별하는 쿠버네티스 서비스. 달리 언급하지 않으면 서비스는 클러스터 네트워크 내에서만 라우팅 가능한 가상 IP를 가지고 있다고 가정한다.
인그레스란?
인그레스는 클러스터 외부에서 클러스터 내부
서비스로 HTTP와 HTTPS 경로를 노출한다.
트래픽 라우팅은 인그레스 리소스에 정의된 규칙에 의해 컨트롤된다.
다음은 인그레스가 모든 트래픽을 하나의 서비스로 보내는 간단한 예시이다.
인그레스는 외부에서 서비스로 접속이 가능한 URL, 로드 밸런스 트래픽, SSL / TLS 종료 그리고 이름-기반의 가상 호스팅을 제공하도록 구성할 수 있다. 인그레스 컨트롤러는 일반적으로 로드 밸런서를 사용해서 인그레스를 수행할 책임이 있으며, 트래픽을 처리하는데 도움이 되도록 에지 라우터 또는 추가 프런트 엔드를 구성할 수도 있다.
인그레스에는 apiVersion, kind, metadata 및 spec 필드가 명시되어야 한다.
인그레스 오브젝트의 이름은 유효한
DNS 서브도메인 이름이어야 한다.
설정 파일의 작성에 대한 일반적인 내용은 애플리케이션 배포하기, 컨테이너 구성하기, 리소스 관리하기를 참조한다.
인그레스는 종종 어노테이션을 이용해서 인그레스 컨트롤러에 따라 몇 가지 옵션을 구성하는데,
그 예시는 재작성-타겟 어노테이션이다.
서로 다른 인그레스 컨트롤러는 서로 다른 어노테이션을 지원한다.
지원되는 어노테이션을 확인하려면 선택한 인그레스 컨트롤러의 설명서를 검토한다.
인그레스 사양
에는 로드 밸런서 또는 프록시 서버를 구성하는데 필요한 모든 정보가 있다. 가장 중요한 것은,
들어오는 요청과 일치하는 규칙 목록을 포함하는 것이다. 인그레스 리소스는 HTTP(S) 트래픽을
지시하는 규칙만 지원한다.
ingressClassName을 생략하려면, 기본 인그레스 클래스가
정의되어 있어야 한다.
몇몇 인그레스 컨트롤러는 기본 IngressClass가 정의되어 있지 않아도 동작한다.
예를 들어, Ingress-NGINX 컨트롤러는 --watch-ingress-without-class플래그를 이용하여 구성될 수 있다.
하지만 아래에 나와 있는 것과 같이 기본 IngressClass를 명시하는 것을
권장한다.
인그레스 규칙
각 HTTP 규칙에는 다음의 정보가 포함된다.
선택적 호스트. 이 예시에서는, 호스트가 지정되지 않기에 지정된 IP 주소를 통해 모든 인바운드
HTTP 트래픽에 규칙이 적용 된다. 만약 호스트가 제공되면(예,
foo.bar.com), 규칙이 해당 호스트에 적용된다.
경로 목록 (예, /testpath)에는 각각 service.name 과
service.port.name 또는 service.port.number 가 정의되어 있는 관련
백엔드를 가지고 있다. 로드 밸런서가 트래픽을 참조된 서비스로
보내기 전에 호스트와 경로가 모두 수신 요청의 내용과
일치해야 한다.
백엔드는 서비스 문서 또는 사용자 정의 리소스 백엔드에 설명된 바와 같이
서비스와 포트 이름의 조합이다. 규칙의 호스트와 경로가 일치하는 인그레스에 대한
HTTP(와 HTTPS) 요청은 백엔드 목록으로 전송된다.
defaultBackend 는 종종 사양의 경로와 일치하지 않는 서비스에 대한 모든 요청을 처리하도록 인그레스
컨트롤러에 구성되는 경우가 많다.
DefaultBackend
규칙이 없는 인그레스는 모든 트래픽을 단일 기본 백엔드로 전송하며,
.spec.defaultBackend는 이와 같은 경우에 요청을 처리할 백엔드를 지정한다.
defaultBackend 는 일반적으로 인그레스 컨트롤러의 구성 옵션이며,
인그레스 리소스에 지정되어 있지 않다.
.spec.rules 가 명시되어 있지 않으면,
.spec.defaultBackend 는 반드시 명시되어 있어야 한다.
defaultBackend 가 설정되어 있지 않으면, 어느 규칙에도 해당되지 않는 요청의 처리는 인그레스 컨트롤러의 구현을 따른다(이러한
경우를 어떻게 처리하는지 알아보려면 해당 인그레스 컨트롤러 문서를 참고한다).
만약 인그레스 오브젝트의 HTTP 요청과 일치하는 호스트 또는 경로가 없으면, 트래픽은
기본 백엔드로 라우팅 된다.
리소스 백엔드
Resource 백엔드는 인그레스 오브젝트와 동일한 네임스페이스 내에 있는
다른 쿠버네티스 리소스에 대한 ObjectRef이다. Resource 는 서비스와
상호 배타적인 설정이며, 둘 다 지정하면 유효성 검사에 실패한다. Resource
백엔드의 일반적인 용도는 정적 자산이 있는 오브젝트 스토리지 백엔드로 데이터를
수신하는 것이다.
인그레스의 각 경로에는 해당 경로 유형이 있어야 한다. 명시적
pathType 을 포함하지 않는 경로는 유효성 검사에 실패한다. 지원되는
경로 유형은 세 가지이다.
ImplementationSpecific: 이 경로 유형의 일치 여부는 IngressClass에 따라
달라진다. 이를 구현할 때 별도 pathType 으로 처리하거나, Prefix 또는 Exact
경로 유형과 같이 동일하게 처리할 수 있다.
Exact: URL 경로의 대소문자를 엄격하게 일치시킨다.
Prefix: URL 경로의 접두사를 / 를 기준으로 분리한 값과 일치시킨다.
일치는 대소문자를 구분하고,
요소별로 경로 요소에 대해 수행한다.
모든 p 가 요청 경로의 요소별 접두사가 p 인 경우
요청은 p 경로에 일치한다.
참고: 경로의 마지막 요소가 요청 경로에 있는 마지막
요소의 하위 문자열인 경우에는 일치하지 않는다(예시: /foo/bar 는
/foo/bar/baz 와 일치하지만, /foo/barbaz 와는 일치하지 않는다).
예제
종류
경로
요청 경로
일치 여부
Prefix
/
(모든 경로)
예
Exact
/foo
/foo
예
Exact
/foo
/bar
아니오
Exact
/foo
/foo/
아니오
Exact
/foo/
/foo
아니오
Prefix
/foo
/foo, /foo/
예
Prefix
/foo/
/foo, /foo/
예
Prefix
/aaa/bb
/aaa/bbb
아니오
Prefix
/aaa/bbb
/aaa/bbb
예
Prefix
/aaa/bbb/
/aaa/bbb
예, 마지막 슬래시 무시함
Prefix
/aaa/bbb
/aaa/bbb/
예, 마지막 슬래시 일치함
Prefix
/aaa/bbb
/aaa/bbb/ccc
예, 하위 경로 일치함
Prefix
/aaa/bbb
/aaa/bbbxyz
아니오, 문자열 접두사 일치하지 않음
Prefix
/, /aaa
/aaa/ccc
예, /aaa 접두사 일치함
Prefix
/, /aaa, /aaa/bbb
/aaa/bbb
예, /aaa/bbb 접두사 일치함
Prefix
/, /aaa, /aaa/bbb
/ccc
예, / 접두사 일치함
Prefix
/aaa
/ccc
아니오, 기본 백엔드 사용함
Mixed
/foo (Prefix), /foo (Exact)
/foo
예, Exact 선호함
다중 일치
경우에 따라 인그레스의 여러 경로가 요청과 일치할 수 있다.
이 경우 가장 긴 일치하는 경로가 우선하게 된다. 두 개의 경로가
여전히 동일하게 일치하는 경우 접두사(prefix) 경로 유형보다
정확한(exact) 경로 유형을 가진 경로가 사용 된다.
호스트네임 와일드카드
호스트는 정확한 일치(예: "foo.bar.com") 또는 와일드카드(예:
"* .foo.com")일 수 있다. 정확한 일치를 위해서는 HTTP host 헤더가
host 필드와 일치해야 한다. 와일드카드 일치를 위해서는 HTTP host 헤더가
와일드카드 규칙의 접미사와 동일해야 한다.
.spec.parameters 필드만 설정하고 .spec.parameters.scope 필드는 지정하지 않거나,
.spec.parameters.scope 필드를 Cluster로 지정하면,
인그레스클래스는 클러스터 범위의 리소스를 참조한다.
파라미터의 kind(+apiGroup)는
클러스터 범위의 API (커스텀 리소스일 수도 있음) 를 참조하며,
파라미터의 name은
해당 API에 대한 특정 클러스터 범위 리소스를 가리킨다.
예시는 다음과 같다.
---apiVersion:networking.k8s.io/v1kind:IngressClassmetadata:name:external-lb-1spec:controller:example.com/ingress-controllerparameters:# 이 인그레스클래스에 대한 파라미터는 "external-config-1" 라는# ClusterIngressParameter(API 그룹 k8s.example.net)에 기재되어 있다.# 이 정의는 쿠버네티스가 # 클러스터 범위의 파라미터 리소스를 검색하도록 한다.scope:ClusterapiGroup:k8s.example.netkind:ClusterIngressParametername:external-config-1
기능 상태:Kubernetes v1.23 [stable]
.spec.parameters 필드를 설정하고
.spec.parameters.scope 필드를 Namespace로 지정하면,
인그레스클래스는 네임스페이스 범위의 리소스를 참조한다.
사용하고자 하는 파라미터가 속한 네임스페이스를
.spec.parameters 의 namespace 필드에 설정해야 한다.
파라미터의 kind(+apiGroup)는
네임스페이스 범위의 API (예: 컨피그맵) 를 참조하며,
파라미터의 name은
namespace에 명시한 네임스페이스의 특정 리소스를 가리킨다.
네임스페이스 범위의 파라미터를 이용하여,
클러스터 운영자가 워크로드에 사용되는 환경 설정(예: 로드 밸런서 설정, API 게이트웨이 정의)에 대한 제어를 위임할 수 있다.
클러스터 범위의 파라미터를 사용했다면 다음 중 하나에 해당된다.
다른 팀의 새로운 환경 설정 변경을 적용하려고 할 때마다
클러스터 운영 팀이 매번 승인을 해야 한다. 또는,
애플리케이션 팀이 클러스터 범위 파라미터 리소스를 변경할 수 있게 하는
RBAC 롤, 바인딩 등의 특별 접근 제어를
클러스터 운영자가 정의해야 한다.
인그레스클래스 API 자신은 항상 클러스터 범위이다.
네임스페이스 범위의 파라미터를 참조하는 인그레스클래스 예시가
다음과 같다.
---apiVersion:networking.k8s.io/v1kind:IngressClassmetadata:name:external-lb-2spec:controller:example.com/ingress-controllerparameters:# 이 인그레스클래스에 대한 파라미터는 # "external-configuration" 네임스페이스에 있는# "external-config" 라는 IngressParameter(API 그룹 k8s.example.com)에 기재되어 있다.scope:NamespaceapiGroup:k8s.example.comkind:IngressParameternamespace:external-configurationname:external-config
사용중단(Deprecated) 어노테이션
쿠버네티스 1.18에 IngressClass 리소스 및 ingressClassName 필드가 추가되기
전에 인그레스 클래스는 인그레스에서
kubernetes.io/ingress.class 어노테이션으로 지정되었다. 이 어노테이션은
공식적으로 정의된 것은 아니지만, 인그레스 컨트롤러에서 널리 지원되었었다.
인그레스의 최신 ingressClassName 필드는 해당 어노테이션을
대체하지만, 직접적으로 해당하는 것은 아니다. 어노테이션은 일반적으로
인그레스를 구현해야 하는 인그레스 컨트롤러의 이름을 참조하는 데 사용되었지만,
이 필드는 인그레스 컨트롤러의 이름을 포함하는 추가 인그레스 구성이
포함된 인그레스 클래스 리소스에 대한 참조이다.
기본 IngressClass
특정 IngressClass를 클러스터의 기본 값으로 표시할 수 있다. IngressClass
리소스에서 ingressclass.kubernetes.io/is-default-class 를 true 로
설정하면 ingressClassName 필드가 지정되지 않은
새 인그레스에게 기본 IngressClass가 할당된다.
주의: 클러스터의 기본값으로 표시된 IngressClass가 두 개 이상 있는 경우
어드미션 컨트롤러에서 ingressClassName 이 지정되지 않은
새 인그레스 오브젝트를 생성할 수 없다. 클러스터에서 최대 1개의 IngressClass가
기본값으로 표시하도록 해서 이 문제를 해결할 수 있다.
몇몇 인그레스 컨트롤러는 기본 IngressClass가 정의되어 있지 않아도 동작한다.
예를 들어, Ingress-NGINX 컨트롤러는 --watch-ingress-without-class플래그를 이용하여 구성될 수 있다.
하지만 다음과 같이 기본 IngressClass를 명시하는 것을
권장한다.
만약 규칙에 정의된 호스트 없이 인그레스 리소스를 생성하는 경우,
이름 기반 가상 호스트가 없어도 인그레스 컨트롤러의 IP 주소에 대한 웹
트래픽을 일치 시킬 수 있다.
예를 들어, 다음 인그레스는 first.bar.com에 요청된 트래픽을
service1로, second.bar.com는 service2로, 그리고 요청 헤더가 first.bar.com 또는 second.bar.com에 해당되지 않는 모든 트래픽을 service3로 라우팅한다.
TLS 개인 키 및 인증서가 포함된 시크릿(Secret)을
지정해서 인그레스를 보호할 수 있다. 인그레스 리소스는
단일 TLS 포트인 443만 지원하고 인그레스 지점에서 TLS 종료를
가정한다(서비스 및 해당 파드에 대한 트래픽은 일반 텍스트임).
인그레스의 TLS 구성 섹션에서 다른 호스트를 지정하면, SNI TLS 확장을 통해
지정된 호스트이름에 따라 동일한 포트에서 멀티플렉싱
된다(인그레스 컨트롤러가 SNI를 지원하는 경우). TLS secret에는
tls.crt 와 tls.key 라는 이름의 키가 있어야 하고, 여기에는 TLS에 사용할 인증서와
개인 키가 있다. 예를 들어 다음과 같다.
인그레스에서 시크릿을 참조하면 인그레스 컨트롤러가 TLS를 사용하여
클라이언트에서 로드 밸런서로 채널을 보호하도록 지시한다. 생성한
TLS 시크릿이 https-example.foo.com 의 정규화 된 도메인 이름(FQDN)이라고
하는 일반 이름(CN)을 포함하는 인증서에서 온 것인지 확인해야 한다.
참고: 가능한 모든 하위 도메인에 대해 인증서가 발급되어야 하기 때문에
TLS는 기본 규칙에서 작동하지 않는다. 따라서
tls 섹션의 hosts는 rules섹션의 host와 명시적으로 일치해야
한다.
참고: TLS 기능을 제공하는 다양한 인그레스 컨트롤러간의 기능
차이가 있다. 사용자 환경에서의 TLS의 작동 방식을 이해하려면
nginx,
GCE 또는 기타
플랫폼의 특정 인그레스 컨트롤러에 대한 설명서를 참조한다.
로드 밸런싱
인그레스 컨트롤러는 로드 밸런싱 알고리즘, 백엔드 가중치 구성표 등
모든 인그레스에 적용되는 일부 로드 밸런싱
정책 설정으로 부트스트랩된다. 보다 진보된 로드 밸런싱 개념
(예: 지속적인 세션, 동적 가중치)은 아직 인그레스를 통해
노출되지 않는다. 대신 서비스에 사용되는 로드 밸런서를 통해 이러한 기능을
얻을 수 있다.
또한, 헬스 체크를 인그레스를 통해 직접 노출되지 않더라도, 쿠버네티스에는
준비 상태 프로브와
같은 동일한 최종 결과를 얻을 수 있는 병렬 개념이
있다는 점도 주목할 가치가 있다. 컨트롤러 별
설명서를 검토하여 헬스 체크를 처리하는 방법을 확인한다(예:
nginx, 또는
GCE).
인그레스 업데이트
기존 인그레스를 업데이트해서 새 호스트를 추가하려면, 리소스를 편집해서 호스트를 업데이트 할 수 있다.
kubectl describe ingress test
Name: test
Namespace: default
Address: 178.91.123.132
Default backend: default-http-backend:80 (10.8.2.3:8080)
Rules:
Host Path Backends
---- ---- --------
foo.bar.com
/foo service1:80 (10.8.0.90:80)
Annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ADD 35s loadbalancer-controller default/test
클러스터 내의 인그레스가 작동하려면, 인그레스 컨트롤러가 실행되고 있어야 한다. 적어도 하나의 인그레스 컨트롤러를 선택하고 이를 클러스터 내에 설치한다. 이 페이지는 배포할 수 있는 일반적인 인그레스 컨트롤러를 나열한다.
인그레스 리소스가 작동하려면, 클러스터는 실행 중인 인그레스 컨트롤러가 반드시 필요하다.
kube-controller-manager 바이너리의 일부로 실행되는 컨트롤러의 다른 타입과 달리 인그레스 컨트롤러는
클러스터와 함께 자동으로 실행되지 않는다.
클러스터에 가장 적합한 인그레스 컨트롤러 구현을 선택하는데 이 페이지를 사용한다.
프로젝트로서 쿠버네티스는 AWS, GCE와
nginx 인그레스 컨트롤러를 지원하고 유지한다.
추가 컨트롤러
참고:
이 섹션은 쿠버네티스에 필요한 기능을 제공하는 써드파티 프로젝트와 관련이 있다. 쿠버네티스 프로젝트 작성자는 써드파티 프로젝트에 책임이 없다. 이 페이지는 CNCF 웹사이트 가이드라인에 따라 프로젝트를 알파벳 순으로 나열한다. 이 목록에 프로젝트를 추가하려면 변경사항을 제출하기 전에 콘텐츠 가이드를 읽어본다.
하나의 클러스터 내에 인그레스 클래스를 이용하여 여러 개의 인그레스 컨트롤러를 배포할 수 있다.
.metadata.name 필드를 확인해둔다. 인그레스를 생성할 때 인그레스 오브젝트(IngressSpec v1 참고)의 ingressClassName 필드에 해당 이름을 명시해야 한다. ingressClassName은 이전 어노테이션 방식의 대체 수단이다.
인그레스에 대한 인그레스 클래스를 설정하지 않았고, 클러스터에 기본으로 설정된 인그레스 클래스가 정확히 하나만 존재하는 경우, 쿠버네티스는 클러스터의 기본 인그레스 클래스를 인그레스에 적용한다.
인그레스 클래스에 ingressclass.kubernetes.io/is-default-class 어노테이션을 문자열 값 "true"로 설정하여, 해당 인그레스 클래스를 기본으로 설정할 수 있다.
이상적으로는 모든 인그레스 컨트롤러가 이 사양을 충족해야 하지만,
다양한 인그레스 컨트롤러는 약간 다르게 작동한다.
엔드포인트슬라이스 API는 서비스가 대규모 백엔드를 처리할 수 있도록 확장할 수 있게 해주고, 클러스터가 정상적인 백엔드의 리스트를 효율적으로 업데이트 할 수 있도록 쿠버네티스가 사용하는 메커니즘이다.
기능 상태:Kubernetes v1.21 [stable]
쿠버네티스의 엔드포인트슬라이스 API 는 쿠버네티스 클러스터 내의 네트워크 엔드포인트를
추적하는 방법을 제공한다. 엔드포인트슬라이스는 엔드포인트보다 더 유동적이고, 확장 가능한
대안을 제안한다.
엔드포인트슬라이스 API
쿠버네티스에서 엔드포인트슬라이스는 일련의 네트워크 엔드포인트에 대한
참조를 포함한다. 쿠버네티스 서비스에 셀렉터가 지정되면 컨트롤 플레인은 자동으로
엔드포인트슬라이스를 생성한다. 이 엔드포인트슬라이스는
서비스 셀렉터와 매치되는 모든 파드들을 포함하고 참조한다. 엔드포인트슬라이스는
프로토콜, 포트 번호 및 서비스 이름의 고유한 조합을 통해 네트워크 엔드포인트를
그룹화한다.
엔드포인트슬라이스 오브젝트의 이름은 유효한
DNS 서브도메인 이름이어야 한다.
예를 들어, 여기에 example 쿠버네티스 서비스가 소유하는 엔드포인트슬라이스
객체 샘플이 있다.
기본적으로, 컨트롤 플레인은 각각 100개 이하의 엔드포인트를
갖도록 엔드포인트슬라이스를
생성하고 관리한다. --max-endpoints-per-slicekube-controller-manager
플래그를 사용하여, 최대 1000개까지 구성할 수 있다.
엔드포인트슬라이스는 내부 트래픽을 라우트하는 방법에 대해
kube-proxy에
신뢰할 수 있는 소스로 역할을 할 수 있다.
주소 유형
엔드포인트슬라이스는 다음 주소 유형을 지원한다.
IPv4
IPv6
FQDN (전체 주소 도메인 이름)
각 엔드포인트슬라이스 객체는 특정한 IP 주소 유형을 나타낸다.
IPv4와 IPv6를 사용할 수 있는 서비스가 있을 경우,
최소 두개의 엔드포인트슬라이스 객체가 존재한다(IPv4를 위해 하나, IPv6를 위해 하나).
컨디션
엔드포인트슬라이스 API는 컨슈머에게 유용한 엔드포인트에 대한 조건을 저장한다.
조건은 ready, serving 및 Terminating 세 가지가 있다.
Ready
ready는 파드의 Ready 조건에 매핑되는 조건이다. Ready 조건이 True로 설정된 실행 중인 파드는
이 엔드포인트슬라이스 조건도 true로 설정되어야 한다. 호환성의
이유로, 파드가 종료될 때 ready는 절대 true가 되면 안 된다. 컨슈머는 serving 조건을 참조하여
파드 종료 준비 상태(readiness)를 검사해야 한다.
이 규칙의 유일한 예외는 spec.publishNotReadyAddresses가 true로 설정된 서비스이다.
이러한 서비스의 엔드 포인트는 항상 ready조건이 true로 설정된다.
Serving
기능 상태:Kubernetes v1.22 [beta]
serving은 종료 상태를 고려하지 않는다는 점을 제외하면 ready 조건과 동일하다.
엔드포인트슬라이스 API 컨슈머는 파드가 종료되는 동안 파드 준비 상태에 관심이 있다면
이 조건을 확인해야 한다.
참고:serving은 ready와 거의 동일하지만 ready의 기존 의미가 깨지는 것을 방지하기 위해 추가되었다.
엔드포인트를 종료하기 위해 ready가 true 일 수 있다면 기존 클라이언트에게는 예상치 못한 일이 될 수 있다.
역사적으로 종료된 엔드포인트는 처음부터 엔드포인트 또는 엔드포인트슬라이스 API에 포함되지 않았기 때문이다.
이러한 이유로 ready는 엔드포인트 종료를 위해 alwaysfalse이며,
클라이언트가 ready에 대한 기존 의미와 관계없이 파드 종료 준비 상태를
추적 할 수 있도록 v1.20에 새로운 조건 serving이 추가되었다.
Terminating
기능 상태:Kubernetes v1.22 [beta]
종료(Terminating)는 엔드포인트가 종료되는지 여부를 나타내는 조건이다.
파드의 경우 삭제 타임 스탬프가 설정된 모든 파드이다.
토폴로지 정보
엔드포인트슬라이스 내의 각 엔드 포인트는 관련 토폴로지 정보를 포함할 수 있다.
토폴로지 정보에는 엔드 포인트의 위치와 해당 노드 및
영역에 대한 정보가 포함된다. 엔드포인트슬라이스의 다음의 엔드 포인트별
필드에서 사용할 수 있다.
*nodeName - 이 엔드 포인트가 있는 노드의 이름이다.
*zone - 이 엔드 포인트가 있는 영역이다.
참고:
v1 API에서는, 전용 필드 nodeName 및 zone 을 위해 엔드 포인트별
topology 가 효과적으로 제거되었다.
EndpointSlice 리소스의 endpoint 필드에 임의의 토폴로지 필드를 설정하는 것은
더 이상 사용되지 않으며 v1 API에서 지원되지 않는다.
대신, v1 API는 개별 nodeName 및 zone 필드 설정을 지원한다.
이러한 필드는 API 버전 간에 자동으로 번역된다.
예를 들어, v1beta1 API의 topology 필드에 있는 "topology.kubernetes.io/zone" 키 값은
v1 API의 zone 필드로 접근할 수 있다.
관리
대부분의 경우, 컨트롤 플레인(특히, 엔드포인트슬라이스
컨트롤러)는
엔드포인트슬라이스 오브젝트를 생성하고 관리한다. 다른 엔티티나 컨트롤러가 추가
엔드포인트슬라이스 집합을 관리하게 할 수 있는 서비스 메시 구현과 같이
엔드포인트슬라이스에 대한 다양한 다른 유스케이스가 있다.
여러 엔티티가 서로 간섭하지 않고 엔드포인트슬라이스를
관리할 수 있도록 쿠버네티스는 엔드포인트슬라이스를 관리하는
엔티티를 나타내는 endpointslice.kubernetes.io/managed-by레이블을
정의한다.
엔드포인트슬라이스 컨트롤러는 관리하는 모든 엔드포인트슬라이스에 레이블의 값으로
endpointslice-controller.k8s.io 를 설정한다. 엔드포인트슬라이스를
관리하는 다른 엔티티도 이 레이블에 고유한 값을 설정해야 한다.
소유권
대부분의 유스케이스에서, 엔드포인트슬라이스 오브젝트가 엔드포인트를
추적하는 서비스가 엔드포인트슬라이스를 소유한다. 이 소유권은 각 엔드포인트슬라이스의 소유자
참조와 서비스에 속한 모든 엔드포인트슬라이스의 간단한 조회를 가능하게 하는
kubernetes.io/service-name 레이블로 표시된다.
엔드포인트슬라이스 미러링
경우에 따라, 애플리케이션이 사용자 지정 엔드포인트 리소스를 생성한다. 이러한
애플리케이션이 엔드포인트와 엔드포인트슬라이스 리소스에 동시에 쓸 필요가 없도록
클러스터의 컨트롤 플레인은 대부분의 엔드포인트 리소스를
해당 엔드포인트슬라이스에 미러링한다.
컨트롤 플레인은 다음을 제외하고 엔드포인트 리소스를 미러링한다.
엔드포인트 리소스에는 endpointslice.kubernetes.io/skip-mirror 레이블이
true 로 설정되어 있다.
엔드포인트 리소스에는 control-plane.alpha.kubernetes.io/leader
어노테이션이 있다.
해당 서비스 리소스가 존재하지 않는다.
해당 서비스 리소스에 nil이 아닌 셀렉터가 있다.
개별 엔드포인트 리소스는 여러 엔드포인트슬라이스로 변환될 수 있다.
엔드포인트 리소스에 여러 하위 집합이 있거나 여러 IP 제품군(IPv4 및 IPv6)이 있는
엔드포인트가 포함된 경우 변환이 일어난다. 하위 집합 당 최대 1000개의 주소가
엔드포인트슬라이스에 미러링된다.
엔드포인트슬라이스의 배포
각 엔드포인트슬라이스에는 리소스 내에 모든 엔드포인트가 적용되는
포트 집합이 있다. 서비스에 알려진 포트를 사용하는 경우 파드는
동일하게 알려진 포트에 대해 다른 대상 포트 번호로 끝날 수 있으며 다른
엔드포인트슬라이스가 필요하다. 이는 하위 집합이 엔드포인트와 그룹화하는
방식의 논리와 유사하다.
컨트롤 플레인은 엔드포인트슬라이스를 최대한 채우려고 노력하지만,
적극적으로 재조정하지는 않는다. 로직은 매우 직관적이다.
기존 엔드포인트슬라이스에 대해 반복적으로, 더 이상 필요하지 않는 엔드포인트를
제거하고 변경에 의해 일치하는 엔드포인트를 업데이트 한다.
첫 번째 단계에서 수정된 엔드포인트슬라이스를 반복해서
필요한 새 엔드포인트로 채운다.
추가할 새 엔드포인트가 여전히 남아있으면, 이전에 변경되지 않은
슬라이스에 엔드포인트를 맞추거나 새로운 것을 생성한다.
중요한 것은, 세 번째 단계는 엔드포인트슬라이스를 완벽하게 전부 배포하는 것보다
엔드포인트슬라이스 업데이트 제한을 우선시한다. 예를 들어, 추가할 새 엔드포인트가
10개이고 각각 5개의 공간을 사용할 수 있는 엔드포인트 공간이 있는 2개의
엔드포인트슬라이스가 있는 경우, 이 방법은 기존 엔드포인트슬라이스
2개를 채우는 대신에 새 엔드포인트슬라이스를 생성한다. 다른 말로, 단일
엔드포인트슬라이스를 생성하는 것이 여러 엔드포인트슬라이스를 업데이트하는 것 보다 더 선호된다.
각 노드에서 kube-proxy를 실행하고 엔드포인트슬라이스를 관찰하면,
엔드포인트슬라이스에 대한 모든 변경 사항이 클러스터의 모든 노드로 전송되기
때문에 상대적으로 비용이 많이 소요된다. 이 방법은 여러 엔드포인트슬라이스가
가득 차지 않은 결과가 발생할지라도, 모든 노드에 전송해야 하는
변경 횟수를 의도적으로 제한하기 위한 것이다.
실제로는, 이러한 이상적이지 않은 분배는 드물 것이다. 엔드포인트슬라이스
컨트롤러에서 처리하는 대부분의 변경 내용은 기존 엔드포인트슬라이스에
적합할 정도로 적고, 그렇지 않은 경우 새 엔드포인트슬라이스가
필요할 수 있다. 디플로이먼트의 롤링 업데이트도 모든 파드와 해당
교체되는 엔드포인트에 대해서 엔드포인트슬라이스를
자연스럽게 재포장한다.
중복 엔드포인트
엔드포인트슬라이스 변경의 특성으로 인해, 엔드포인트는 동시에 둘 이상의
엔드포인트슬라이스에 표시될 수 있다. 이는 다른 엔드포인트슬라이스 오브젝트에
대한 변경 사항이 다른 시간에서의 쿠버네티스 클라이언트 워치(watch)/캐시에
도착할 수 있기 때문에 자연스럽게 발생한다.
참고:
엔드포인트슬라이스 API의 클라이언트는 반드시 서비스에 연관된 모든 존재하는 엔드포인트슬라이스에 대해
반복하고, 고유한 각 네트워크 엔드포인트들의 완전한 리스트를 구성해야 한다. 엔드포인트는 다른
엔드포인트슬라이스에서 중복될 수 있음에 유의한다.
엔드포인트 집계와 중복 제거 방법에 대한 레퍼런스 구현은 kube-proxy 의
EndpointSliceCache 구현에서 찾을 수 있다.
엔드포인트와 비교
기존 엔드포인트 API는 쿠버네티스에서 네트워크 엔드포인트를 추적하는
간단하고 직접적인 방법을 제공했다. 쿠버네티스 클러스터와
서비스가 더 많은 수의 백엔드 파드로
더 많은 트래픽을 처리하고 전송하는 방향으로 성장함에 따라, 이 API의 한계가 더욱 눈에 띄게
되었다.
특히나, 많은 수의 네트워크 엔드포인트로 확장하는 것에
어려움이 있었다.
서비스에 대한 모든 네트워크 엔드포인트가 단일 엔드포인트
객체에 저장되기 때문에 이러한 엔드포인트 객체들이 상당히 커지는 경우도 있었다. 안정적인
서비스(오랜 기간 동안 같은 엔드포인트 세트)의 경우 영향은
비교적 덜 눈에 띄지만, 여전히 쿠버네티스의 일부 사용 사례들은 잘 처리되지 않았다.
서비스가 많은 백엔드 엔드포인트를 가지고
워크로드가 자주 증가하거나, 새로운 변경사항이 자주 롤 아웃 될 경우,
해당 서비스의 단일 엔드포인트 객체에 대한 각 업데이트는
쿠버네티스 클러스터 컴포넌트 사이(컨트롤 플레인 내, 그리고 노드와 API 서버 사이)에
상당한 네트워크 트래픽이 발생할 것임을 의미했다.
이러한 추가 트래픽은 또한 CPU 사용 관점에서도 굉장한 비용을 발생시켰다.
엔드포인트슬라이스 사용 시, 단일 파드를 추가하거나 삭제하는 작업은 (다수의 파드를 추가/삭제하는 작업과 비교했을 때)
해당 변경을 감시하고 있는 클라이언트에 동일한 _수_의 업데이트를 트리거한다.
하지만, 파드 대규모 추가/삭제의 경우 업데이트 메시지의 크기는 훨씬 작다.
엔드포인트슬라이스는 듀얼 스택 네트워킹과 토폴로지 인식 라우팅과 같은
새로운 기능에 대한 혁신을 가능하게 했다.
IP 주소 또는 포트 수준(OSI 계층 3 또는 4)에서 트래픽 흐름을 제어하려는 경우, 네트워크 정책은 클러스터 내의 트래픽 흐름뿐만 아니라 파드와 외부 간의 규칙을 정의할 수 있도록 해준다. 클러스터는 반드시 네트워크 정책을 지원하는 네트워크 플러그인을 사용해야 한다.
IP 주소 또는 포트 수준(OSI 계층 3 또는 4)에서 트래픽 흐름을 제어하려는 경우, 클러스터의 특정 애플리케이션에 대해 쿠버네티스 네트워크폴리시(NetworkPolicy) 사용을 고려할 수 있다. 네트워크폴리시는 파드가 네트워크 상의 다양한 네트워크 "엔티티"(여기서는 "엔티티"를 사용하여 쿠버네티스에서 특별한 의미로 사용되는 "엔드포인트" 및 "서비스"와 같은 일반적인 용어가 중의적으로 표현되는 것을 방지함)와 통신할 수 있도록 허용하는 방법을 지정할 수 있는 애플리케이션 중심 구조이다. 네트워크폴리시는 한쪽 또는 양쪽 종단이 파드인 연결에만 적용되며, 다른 연결에는 관여하지 않는다.
파드가 통신할 수 있는 엔티티는 다음 3개의 식별자 조합을 통해 식별된다.
허용되는 다른 파드(예외: 파드는 자신에 대한 접근을 차단할 수 없음)
허용되는 네임스페이스
IP 블록(예외: 파드 또는 노드의 IP 주소와 관계없이 파드가 실행 중인 노드와의 트래픽은 항상 허용됨)
pod- 또는 namespace- 기반의 네트워크폴리시를 정의할 때, 셀렉터를 사용하여 셀렉터와 일치하는 파드와 주고받는 트래픽을 지정한다.
한편, IP 기반의 네트워크폴리시가 생성되면, IP 블록(CIDR 범위)을 기반으로 정책을 정의한다.
전제 조건
네트워크 정책은 네트워크 플러그인으로 구현된다. 네트워크 정책을 사용하려면 네트워크폴리시를 지원하는 네트워킹 솔루션을 사용해야만 한다. 이를 구현하는 컨트롤러 없이 네트워크폴리시 리소스를 생성해도 아무런 효과가 없기 때문이다.
파드 격리의 두 가지 종류
파드 격리에는 이그레스에 대한 격리와 인그레스에 대한 격리의 두 가지 종류가 있다. 이들은 어떤 연결이 성립될지에 대해 관여한다. 여기서 "격리"는 절대적인 것이 아니라, "일부 제한이 적용됨"을 의미한다. 반대말인 "이그레스/인그레스에 대한 비격리"는 각 방향에 대해 제한이 적용되지 않음을 의미한다. 두 종류의 격리(또는 비격리)는 독립적으로 선언되며, 두 종류 모두 파드 간 연결과 연관된다.
기본적으로, 파드는 이그레스에 대해 비격리되어 있다. 즉, 모든 아웃바운드 연결이 허용된다. 해당 파드에 적용되면서 policyTypes에 "Egress"가 있는 NetworkPolicy가 존재하는 경우, 파드가 이그레스에 대해 격리된다. 이러한 정책은 파드의 이그레스에 적용된다고 말한다. 파드가 이그레스에 대해 격리되면, 파드에서 나가는 연결 중에서 파드의 이그레스에 적용된 NetworkPolicy의 egress 리스트에 허용된 연결만이 허용된다. egress 리스트 각 항목의 효과는 합산되어 적용된다.
기본적으로, 파드는 인그레스에 대해 비격리되어 있다. 즉, 모든 인바운드 연결이 허용된다. 해당 파드에 적용되면서 policyTypes에 "Ingress"가 있는 NetworkPolicy가 존재하는 경우, 파드가 인그레스에 대해 격리된다. 이러한 정책은 파드의 인그레스에 적용된다고 말한다. 파드가 인그레스에 대해 격리되면, 파드로 들어오는 연결 중에서 파드의 인그레스에 적용된 NetworkPolicy의 ingress 리스트에 허용된 연결만이 허용된다. ingress 리스트 각 항목의 효과는 합산되어 적용된다.
네트워크 폴리시가 충돌하는 경우는 없다. 네트워크 폴리시는 합산되어 적용된다. 특정 파드의 특정 방향에 대해 하나 또는 여러 개의 폴리시가 적용되면, 해당 파드의 해당 방향에 대해 허용된 연결은 모든 폴리시가 허용하는 연결의 합집합이다. 따라서, 판별 순서는 폴리시 결과에 영향을 미치지 않는다.
송신 파드에서 수신 파드로의 연결이 허용되기 위해서는, 송신 파드의 이그레스 폴리시와 수신 파드의 인그레스 폴리시가 해당 연결을 허용해야 한다. 만약 어느 한 쪽이라도 해당 연결을 허용하지 않으면, 연결이 되지 않을 것이다.
참고: 선택한 네트워킹 솔루션이 네트워킹 정책을 지원하지 않으면 클러스터의 API 서버에 이를 POST 하더라도 효과가 없다.
필수 필드들: 다른 모든 쿠버네티스 설정과 마찬가지로 네트워크폴리시 에는
apiVersion, kind, 그리고 metadata 필드가 필요하다. 구성 파일
작업에 대한 일반적인 정보는
컨피그맵을 사용하도록 파드 구성하기,
그리고 오브젝트 관리 를 본다.
spec: 네트워크폴리시 사양에는 지정된 네임스페이스에서 특정 네트워크 정책을 정의하는데 필요한 모든 정보가 있다.
podSelector: 각 네트워크폴리시에는 정책이 적용되는 파드 그룹을 선택하는 podSelector 가 포함된다. 예시 정책은 "role=db" 레이블이 있는 파드를 선택한다. 비어있는 podSelector 는 네임스페이스의 모든 파드를 선택한다.
policyTypes: 각 네트워크폴리시에는 Ingress, Egress 또는 두 가지 모두를 포함할 수 있는 policyTypes 목록이 포함된다. policyTypes 필드는 선택한 파드에 대한 인그레스 트래픽 정책, 선택한 파드에 대한 이그레스 트래픽 정책 또는 두 가지 모두에 지정된 정책의 적용 여부를 나타낸다. 만약 네트워크폴리시에 policyTypes 가 지정되어 있지 않으면 기본적으로 Ingress 가 항상 설정되고, 네트워크폴리시에 Egress 가 있으면 이그레스 규칙이 설정된다.
ingress: 각 네트워크폴리시에는 화이트리스트 ingress 규칙 목록이 포함될 수 있다. 각 규칙은 from 과 ports 부분과 모두 일치하는 트래픽을 허용한다. 예시 정책에는 단일 규칙이 포함되어 있는데 첫 번째 포트는 ipBlock 을 통해 지정되고, 두 번째는 namespaceSelector 를 통해 그리고 세 번째는 podSelector 를 통해 세 가지 소스 중 하나의 단일 포트에서 발생하는 트래픽과 일치 시킨다.
egress: 각 네트워크폴리시에는 화이트리스트 egress 규칙이 포함될 수 있다. 각 규칙은 to 와 ports 부분과 모두 일치하는 트래픽을 허용한다. 예시 정책에는 단일 포트의 트래픽을 10.0.0.0/24 의 모든 대상과 일치시키는 단일 규칙을 포함하고 있다.
따라서 예시의 네트워크폴리시는 다음과 같이 동작한다.
인그레스 및 이그레스 트래픽에 대해 "default" 네임스페이스에서 "role=db"인 파드를 격리한다(아직 격리되지 않은 경우).
(인그레스 규칙)은 "role=db" 레이블을 사용하는 "default" 네임스페이스의 모든 파드에 대해서 TCP 포트 6379로의 연결을 허용한다. 인그레스을 허용 할 대상은 다음과 같다.
"role=frontend" 레이블이 있는 "default" 네임스페이스의 모든 파드
네임스페이스와 "project=myproject" 를 레이블로 가지는 모든 파드
172.17.0.0–172.17.0.255 와 172.17.2.0–172.17.255.255 의 범위를 가지는 IP 주소(예: 172.17.0.0/16 전체에서 172.17.1.0/24 를 제외)
(이그레스 규칙)은 "role=db" 레이블이 있는 "default" 네임스페이스의 모든 파드에서 TCP 포트 5978의 CIDR 10.0.0.0/24 로의 연결을 허용한다.
from 배열에 두 개의 요소가 포함되어 있으며, 로컬 네임스페이스에 레이블을 role=client 로 가지는 파드 또는 네임스페이스에 레이블을 user=alice 로 가지는 파드의 연결을 허용한다.
의심스러운 경우, kubectl describe 를 사용해서 쿠버네티스가 정책을 어떻게 해석하는지 확인해본다.
ipBlock: 인그레스 소스 또는 이그레스 대상으로 허용할 IP CIDR 범위를 선택한다. 파드 IP는 임시적이고 예측할 수 없기에 클러스터 외부 IP이어야 한다.
클러스터 인그레스 및 이그레스 매커니즘은 종종 패킷의 소스 또는 대상 IP의 재작성을
필요로 한다. 이러한 상황이 발생하는 경우, 네트워크폴리시의 처리 전 또는 후에
발생한 것인지 정의되지 않으며, 네트워크 플러그인, 클라우드 공급자,
서비스 구현 등의 조합에 따라 동작이 다를 수 있다.
인그레스 사례에서의 의미는 실제 원본 소스 IP를 기준으로 들어오는 패킷을
필터링할 수 있는 반면에 다른 경우에는 네트워크폴리시가 작동하는
"소스 IP"는 LoadBalancer 또는 파드가 속한 노드 등의 IP일 수 있다.
이그레스의 경우 파드에서 클러스터 외부 IP로 다시 작성된 서비스 IP로의 연결은
ipBlock 기반의 정책의 적용을 받거나 받지 않을 수 있다는 것을 의미한다.
기본 정책
기본적으로 네임스페이스 정책이 없으면 해당 네임스페이스의 파드에 대한 모든 인그레스와 이그레스 트래픽이 허용된다. 다음 예시에서는 해당 네임스페이스의 기본 동작을
변경할 수 있다.
기본적으로 모든 인그레스 트래픽 거부
모든 파드를 선택하지만 해당 파드에 대한 인그레스 트래픽은 허용하지 않는 네트워크폴리시를 생성해서 네임스페이스에 대한 "기본" 인그레스 격리 정책을 생성할 수 있다.
이렇게 하면 다른 네트워크폴리시에서 선택하지 않은 파드도 인그레스 또는 이그레스 트래픽을 허용하지 않는다.
SCTP 지원
기능 상태:Kubernetes v1.20 [stable]
안정된 기능으로, 기본 활성화되어 있다. 클러스터 수준에서 SCTP를 비활성화하려면, 사용자(또는 클러스터 관리자)가 API 서버에 --feature-gates=SCTPSupport=false,… 를 사용해서 SCTPSupport기능 게이트를 비활성화해야 한다.
해당 기능 게이트가 활성화되어 있는 경우, 네트워크폴리시의 protocol 필드를 SCTP로 지정할 수 있다.
참고: SCTP 프로토콜 네트워크폴리시를 지원하는 CNI 플러그인을 사용하고 있어야 한다.
위 규칙은 대상 포트가 32000에서 32768 사이에 있는 경우,
네임스페이스 default 에 레이블이 role=db 인 모든 파드가
TCP를 통해 10.0.0.0/24 범위 내의 모든 IP와 통신하도록 허용한다.
이 필드를 사용할 때 다음의 제한 사항이 적용된다.
endPort 필드는 port 필드보다 크거나 같아야 한다.
endPort 는 port 도 정의된 경우에만 정의할 수 있다.
두 포트 모두 숫자여야 한다.
참고: 클러스터가 네트워크폴리시 명세의 endPort 필드를 지원하는
CNI 플러그인을 사용해야 한다.
만약 네트워크 플러그인이
endPort 필드를 지원하지 않는데 네트워크폴리시의 해당 필드에 명시를 하면,
그 정책은 port 필드에만 적용될 것이다.
이름으로 네임스페이스 지정
기능 상태:Kubernetes 1.22 [stable]
쿠버네티스 컨트롤 플레인은 NamespaceDefaultLabelName기능 게이트가 활성화된 경우
모든 네임스페이스에 변경할 수 없는(immutable) 레이블 kubernetes.io/metadata.name 을 설정한다.
레이블의 값은 네임스페이스 이름이다.
네트워크폴리시는 일부 오브젝트 필드가 있는 이름으로 네임스페이스를 대상으로 지정할 수 없지만, 표준화된 레이블을 사용하여
특정 네임스페이스를 대상으로 지정할 수 있다.
네트워크 정책으로 할 수 없는 것(적어도 아직은 할 수 없는)
쿠버네티스 1.28부터 다음의 기능은 네트워크폴리시 API에 존재하지 않지만, 운영 체제 컴포넌트(예: SELinux, OpenVSwitch, IPTables 등) 또는 Layer 7 기술(인그레스 컨트롤러, 서비스 메시 구현) 또는 어드미션 컨트롤러를 사용하여 제2의 해결책을 구현할 수 있다. 쿠버네티스의 네트워크 보안을 처음 사용하는 경우, 네트워크폴리시 API를 사용하여 다음의 사용자 스토리를 (아직) 구현할 수 없다는 점에 유의할 필요가 있다.
내부 클러스터 트래픽이 공통 게이트웨이를 통과하도록 강제한다(서비스 메시나 기타 프록시와 함께 제공하는 것이 가장 좋을 수 있음).
TLS와 관련된 모든 것(이를 위해 서비스 메시나 인그레스 컨트롤러 사용).
노드별 정책(이에 대해 CIDR 표기법을 사용할 수 있지만, 특히 쿠버네티스 ID로 노드를 대상으로 지정할 수 없음).
이름으로 서비스를 타겟팅한다(그러나, 레이블로 파드나 네임스페이스를 타겟팅할 수 있으며, 이는 종종 실행할 수 있는 해결 방법임).
타사 공급사가 이행한 "정책 요청"의 생성 또는 관리.
모든 네임스페이스나 파드에 적용되는 기본 정책(이를 수행할 수 있는 타사 공급사의 쿠버네티스 배포본 및 프로젝트가 있음).
고급 정책 쿼리 및 도달 가능성 도구.
네트워크 보안 이벤트를 기록하는 기능(예: 차단되거나 수락된 연결).
명시적으로 정책을 거부하는 기능(현재 네트워크폴리시 모델은 기본적으로 거부하며, 허용 규칙을 추가하는 기능만 있음).
루프백 또는 들어오는 호스트 트래픽을 방지하는 기능(파드는 현재 로컬 호스트 접근을 차단할 수 없으며, 상주 노드의 접근을 차단할 수 있는 기능도 없음).
워크로드는 DNS를 사용하여 클러스터 내의 서비스들을 발견할 수 있다; 이 페이지는 이것이 어떻게 동작하는지를 설명한다.
쿠버네티스는 파드와 서비스를 위한 DNS 레코드를 생성한다. 사용자는 IP 주소 대신에
일관된 DNS 네임을 통해서 서비스에 접속할 수 있다.
쿠버네티스는 DNS를 설정 하기 위해 사용되는
파드와 서비스에 대한 정보를 발행한다. Kubelet은 실행 중인 컨테이너가
IP가 아닌 이름으로 서비스를 검색할 수 있도록 파드의 DNS를 설정한다.
클러스터 내의 서비스에는 DNS 네임이 할당된다.
기본적으로 클라이언트 파드의 DNS 검색 리스트는 파드 자체의 네임스페이스와
클러스터의 기본 도메인을 포함한다.
서비스의 네임스페이스
DNS 쿼리는 그것을 생성하는 파드의 네임스페이스에 따라 다른 결과를 반환할 수
있다. 네임스페이스를 지정하지 않은 DNS 쿼리는 파드의 네임스페이스에
국한된다. DNS 쿼리에 네임스페이스를 명시하여 다른 네임스페이스에 있는 서비스에 접속한다.
예를 들어, test 네임스페이스에 있는 파드를 생각해보자. data 서비스는
prod 네임스페이스에 있다.
이 경우, data 에 대한 쿼리는 파드의 test 네임스페이스를 사용하기 때문에 결과를 반환하지 않을 것이다.
data.prod 로 쿼리하면 의도한 결과를 반환할 것이다. 왜냐하면
네임스페이스가 명시되어 있기 때문이다.
DNS 쿼리는 파드의 /etc/resolv.conf 를 사용하여 확장될 수 있을 것이다. Kubelet은
각 파드에 대해서 파일을 설정한다. 예를 들어, data 만을 위한 쿼리는
data.test.svc.cluster.local 로 확장된다. search 옵션의 값은
쿼리를 확장하기 위해서 사용된다. DNS 쿼리에 대해 더 자세히 알고 싶은 경우,
resolv.conf 설명 페이지.를 참고한다.
요약하면, test 네임스페이스에 있는 파드는, prod 네임스페이스에 있는 data 파드를 가리키는 도메인 이름인
data.prod 또는 data.prod.svc.cluster.local 을 성공적으로 해석할 수 있다.
DNS 레코드
어떤 오브젝트가 DNS 레코드를 가지는가?
서비스
파드
다음 섹션은 지원되는 DNS 레코드의 종류 및 레이아웃에 대한 상세
내용이다. 혹시 동작시킬 필요가 있는 다른 레이아웃, 네임, 또는 쿼리는
구현 세부 사항으로 간주되며 경고 없이 변경될 수 있다.
최신 명세 확인을 위해서는,
쿠버네티스 DNS-기반 서비스 디스커버리를 본다.
서비스
A/AAAA 레코드
"노멀"(헤드리스가 아닌) 서비스는 서비스의 IP family 혹은 IP families 에 따라
my-svc.my-namespace.svc.cluster-domain.example
형식의 이름을 가진 DNS A 또는 AAAA 레코드가 할당된다. 이는 서비스의 클러스터
IP로 해석된다.
헤드리스 서비스
(클러스터 IP가 없는) 또한 my-svc.my-namespace.svc.cluster-domain.example
형식의 이름을 가진 DNS A 또는 AAAA 레코드가 할당된다. 노멀 서비스와는 달리
이는 서비스에 의해 선택된 파드들의 IP 집합으로 해석된다.
클라이언트는 해석된 IP 집합에서 IP를 직접 선택하거나 표준 라운드로빈을
통해 선택할 수 있다.
SRV 레코드
SRV 레코드는 노멀 서비스 또는 헤드리스 서비스에 속하는 네임드 포트를 위해 만들어졌다.
각각의 네임드 포트에 대해서 SRV 레코드는 다음과 같은 형식을 갖는다.
_port-name._port-protocol.my-svc.my-namespace.svc.cluster-domain.example.
정규 서비스의 경우, 이는 포트 번호와 도메인 네임으로 해석된다:
my-svc.my-namespace.svc.cluster-domain.example.
헤드리스 서비스의 경우, 서비스를 지원하는 각 파드에 대해 하나씩 복수 응답으로 해석되며 이 응답은 파드의
포트 번호와 도메인 이름을 포함한다.
hostname.my-svc.my-namespace.svc.cluster-domain.example.
파드가 생성되면 hostname(파드 내부에서 확인된 것 처럼)은
해당 파드의 metadata.name 값이 된다.
파드 스펙(Pod spec)에는 선택적 필드인 hostname이 있다. 이 필드는
다른 호스트네임을 지정할 수 있다. hostname 필드가 지정되면, 파드의 이름보다
파드의 호스트네임이 우선시된다.(역시 파드 내에서 확인된 것 처럼) 예를 들어,
spec.hostname 필드가 "my-host"로 설정된 파드는
호스트네임이 "my-host"로 설정된다.
또한, 파드 스펙에는 선택적 필드인 subdomain이 있다. 이 필드는 파드가 네임스페이스의
서브 그룹의 일부임을 나타낼 수 있다. 예를 들어 "my-namespace" 네임스페이스에서, spec.hostname 필드가
"foo"로 설정되고, spec.subdomain 필드가 "bar"로 설정된 파드는
전체 주소 도메인 네임(FQDN)을 가지게 된다.
"foo.bar.my-namespace.svc.cluster-domain.example" (다시 한 번, 파드 내부 에서
확인된 것 처럼).
파드와 동일한 네임스페이스 내에 같은 서브도메인 이름을 가진
헤드리스 서비스가 있다면, 클러스터의 DNS 서버는
파드의 전체 주소 호스트네임(fully qualified hostname)인 A 또는 AAAA 레코드를 반환한다.
예시:
apiVersion:v1kind:Servicemetadata:name:busybox-subdomainspec:selector:name:busyboxclusterIP:Noneports:- name:foo# 단일 포트 서비스에 이름은 필수사항이 아니다.port:1234---apiVersion:v1kind:Podmetadata:name:busybox1labels:name:busyboxspec:hostname:busybox-1subdomain:busy-subdomaincontainers:- image:busybox:1.28command:- sleep- "3600"name:busybox---apiVersion:v1kind:Podmetadata:name:busybox2labels:name:busyboxspec:hostname:busybox-2subdomain:busy-subdomaincontainers:- image:busybox:1.28command:- sleep- "3600"name:busybox
위의 주어진 서비스 "busybox-subdomain"과 spec.subdomain이
"busybox-subdomain"으로 설정된 파드에서, 첫번째 파드는 다음과 같은 자기 자신의 FQDN을 확인하게 된다.
"busybox-1.busybox-subdomain.my-namespace.svc.cluster-domain.example". DNS는
위 FQDN에 대해 파드의 IP를 가리키는 A 또는 AAAA 레코드를 제공한다. "busybox1"와
"busybox2" 파드 모두 각 파드의 주소 레코드를 갖게 된다.
참고: A 와 AAAA 레코드는 파드의 이름으로 생성되지 않기 때문에
파드의 A 또는 AAAA 레코드를 생성하기 위해서는 hostname 필드를 작성해야 한다.
hostname 필드는 없고 subdomain 필드만 있는 파드는 파드의 IP 주소를 가리키는 헤드리스 서비스의
A 또는 AAAA 레코드만 생성할 수 있다. (busybox-subdomain.my-namespace.svc.cluster-domain.example)
또한 서비스에서 publishNotReadyAddresses=True 를 설정하지 않았다면, 파드가 준비 상태가 되어야 레코드를 가질 수 있다.
파드의 setHostnameAsFQDN 필드
기능 상태:Kubernetes v1.22 [stable]
파드가 전체 주소 도메인 이름(FQDN)을 갖도록 구성된 경우,
해당 호스트네임은 짧은 호스트네임이다.
예를 들어, 전체 주소 도메인 이름이 busybox-1.busybox-subdomain.my-namespace.svc.cluster-domain.example 인 파드가 있는 경우,
기본적으로 해당 파드 내부의 hostname 명령어는 busybox-1 을 반환하고
hostname --fqdn 명령은 FQDN을 반환한다.
파드 명세에서 setHostnameAsFQDN: true 를 설정하면, kubelet은 파드의 FQDN을 해당 파드 네임스페이스의 호스트네임에 기록한다. 이 경우, hostname 과 hostname --fqdn 은 모두 파드의 FQDN을 반환한다.
참고:
리눅스에서, 커널의 호스트네임 필드(struct utsname 의 nodename 필드)는 64자로 제한된다.
파드에서 이 기능을 사용하도록 설정하고 FQDN이 64자보다 길면, 시작되지 않는다. 파드는 파드 호스트네임과 클러스터 도메인에서 FQDN을 구성하지 못한다거나, FQDN long-FDQN 이 너무 길다(최대 64자, 70자 요청인 경우)와 같은 오류 이벤트를 생성하는 Pending 상태(kubectl 에서 표시하는 ContainerCreating)로 유지된다. 이 시나리오에서 사용자 경험을 개선하는 한 가지 방법은 사용자가 최상위 레벨을 오브젝트(예를 들어, 디플로이먼트)를 생성할 때 FQDN 크기를 제어하기 위해 어드미션 웹훅 컨트롤러를 생성하는 것이다.
파드의 DNS 정책
DNS 정책은 파드별로 설정할 수 있다.
현재 쿠버네티스는 다음과 같은 파드별 DNS 정책을 지원한다.
이 정책들은 파드 스펙의 dnsPolicy 필드에서 지정할 수 있다.
"Default": 파드는 파드가 실행되고 있는 노드로부터
네임 해석 설정(the name resolution configuration)을 상속받는다.
자세한 내용은 관련 논의에서
확인할 수 있다.
"ClusterFirst": "www.kubernetes.io"와 같이 클러스터 도메인 suffix 구성과
일치하지 않는 DNS 쿼리는 DNS 서버에 의해 업스트림 네임서버로 전달된다.
클러스터 관리자는 추가 스텁-도메인(stub-domain)과 업스트림 DNS 서버를 구축할 수 있다.
그러한 경우 DNS 쿼리를 어떻게 처리하는지에 대한 자세한 내용은
관련 논의에서
확인할 수 있다.
"ClusterFirstWithHostNet": hostNetwork에서 running 상태인 파드의 경우 DNS 정책인
"ClusterFirstWithHostNet"을 명시적으로 설정해야 한다. 그렇지 않으면,
hostNetwork와 "ClusterFirst"로 실행되고 있는 파드는
"Default" 정책으로 동작한다.
dnsConfig 필드는 선택적이고, dnsPolicy 세팅과 함께 동작한다.
이때, 파드의 dnsPolicy의 값이 "None"으로 설정되어 있어야
dnsConfig 필드를 지정할 수 있다.
사용자는 dnsConfig 필드에서 다음과 같은 속성들을 지정할 수 있다.
nameservers: 파드의 DNS 서버가 사용할 IP 주소들의 목록이다.
파드의 dnsPolicy가 "None" 으로 설정된 경우에는
적어도 하나의 IP 주소가 포함되어야 하며,
그렇지 않으면 이 속성은 생략할 수 있다.
nameservers에 나열된 서버는 지정된 DNS 정책을 통해 생성된 기본 네임 서버와 합쳐지며
중복되는 주소는 제거된다.
searches: 파드의 호스트네임을 찾기 위한 DNS 검색 도메인의 목록이다.
이 속성은 생략이 가능하며,
값을 지정한 경우 나열된 검색 도메인은 지정된 DNS 정책을 통해 생성된 기본 검색 도메인에 합쳐진다.
병합 시 중복되는 도메인은 제거되며,
쿠버네티스는 최대 6개의 검색 도메인을 허용하고 있다.
options: name 속성(필수)과 value 속성(선택)을 가질 수 있는 오브젝트들의 선택적 목록이다.
이 속성의 내용은 지정된 DNS 정책에서 생성된 옵션으로 병합된다.
이 속성의 내용은 지정된 DNS 정책을 통해 생성된 옵션으로 합쳐지며,
병합 시 중복되는 항목은 제거된다.
쿠버네티스는 탐색 도메인 리스트가 32개를 넘거나 모든 탐색 도메인의 길이가 2048자를
초과할 때까지 DNS Config에 자체적인 제한을 하지 않는다.
이 제한은 노드의 resolver 설정 파일, 파드의 DNS Config,
그리고 합쳐진 DNS Config에 각각 적용된다.
참고:
이전 버전의 일부 컨테이너 런타임은 DNS 탐색 도메인 수에 대해
자체적인 제한을 가지고 있을 수 있다. 컨테이너 런타임 환경에 따라
많은 수의 DNS 검색 도메인을 갖는 파드는
pending 상태로 고착될 수 있다.
containerd v1.5.5 혹은 그 이전 그리고 CRI-O v1.21 혹은 그 이전에서 이러한 문제가
발생하는 것으로 알려졌다.
윈도우 노드에서 DNS 해석(resolution)
ClusterFirstWithHostNet은 윈도우 노드에서 구동 중인 파드에는 지원되지 않는다.
윈도우는 .를 포함한 모든 네임(주소)을 FQDN으로 취급하여 FQDN 해석을 생략한다.
윈도우에는 여러 DNS 해석기가 사용될 수 있다. 이러한 해석기는
각자 조금씩 다르게 동작하므로, 네임 쿼리 해석을 위해서
Resolve-DNSName
파워쉘(powershell) cmdlet을 사용하는 것을 추천한다.
리눅스에는 DNS 접미사 목록이 있는데, 이는 네임이 완전한 주소가 아니어서 주소
해석에 실패한 경우 사용한다.
윈도우에서는 파드의 네임스페이스(예: mydns.svc.cluster.local)와 연계된
하나의 DNS 접미사만 가질 수 있다. 윈도우는 이러한 단일 접미사 통해 해석될 수 있는
FQDNs, 서비스, 또는 네트워크 네임을 해석할 수 있다. 예를 들어, default에
소속된 파드는 DNS 접미사 default.svc.cluster.local를 가진다.
윈도우 파드 내부에서는 kubernetes.default.svc.cluster.local와
kubernetes를 모두 해석할 수 있다. 그러나, 일부에만 해당(partially qualified)하는 네임(kubernetes.default 또는
kubernetes.default.svc)은 해석할 수 없다.
--node-cidr-mask-size-ipv4|--node-cidr-mask-size-ipv6 IPv4의 기본값은 /24 이고 IPv6의 기본값은 /64 이다.
kube-proxy:
--cluster-cidr=<IPv4 CIDR>,<IPv6 CIDR>
kubelet:
--cloud-provider가 명시되지 않았다면 관리자는 해당 노드에 듀얼 스택
.status.addresses를 수동으로 설정하기 위해 쉼표로 구분된 IP 주소 쌍을 --node-ip 플래그로 전달할 수 있다.
해당 노드의 파드가 HostNetwork 모드로 실행된다면,
파드는 이 IP 주소들을 자신의 .status.podIPs 필드에 보고한다.
노드의 모든 podIPs는 해당 노드의 .status.addresses 필드에 의해 정의된
IP 패밀리 선호사항을 만족한다.
참고:
IPv4 CIDR의 예: 10.244.0.0/16 (자신의 주소 범위를 제공하더라도)
IPv6 CIDR의 예: fdXY:IJKL:MNOP:15::/64 (이 형식으로 표시되지만, 유효한
주소는 아니다. RFC 4193을 확인한다.)
서비스의 주소 계열은 기본적으로 첫 번째 서비스 클러스터 IP 범위의 주소 계열로 설정된다.
(--service-cluster-ip-range 플래그를 통해 kube-apiserver에 구성)
서비스를 정의할 때 선택적으로 이중 스택으로 구성할 수 있다. 원하는 동작을 지정하려면 .spec.ipFamilyPolicy 필드를
다음 값 중 하나로 설정한다.
SingleStack: 단일 스택 서비스. 컨트롤 플레인은 첫 번째로 구성된 서비스
클러스터 IP 범위를 사용하여 서비스에 대한 클러스터 IP를 할당한다.
PreferDualStack:
서비스에 IPv4 및 IPv6 클러스터 IP를 할당한다.
RequireDualStack: IPv4 및 IPv6 주소 범위 모두에서 서비스 .spec.ClusterIPs를 할당한다.
.spec.ipFamilies 배열의 첫 번째 요소의 주소 계열을 기반으로
.spec.ClusterIPs 목록에서 .spec.ClusterIP를 선택한다.
단일 스택에 사용할 IP 계열을 정의하거나 이중 스택에 대한 IP 군의
순서를 정의하려는 경우, 서비스에서 옵션 필드
.spec.ipFamilies를 설정하여 주소 군을 선택할 수 있다.
참고:.spec.ipFamilies 필드는 이미 존재하는 서비스에 .spec.ClusterIP를
재할당할 수 없기 때문에 변경할 수 없다. .spec.ipFamilies를 변경하려면
서비스를 삭제하고 다시 생성한다.
.spec.ipFamilies를 다음 배열 값 중 하나로 설정할 수 있다.
["IPv4"]
["IPv6"]
["IPv4","IPv6"] (이중 스택)
["IPv6","IPv4"] (이중 스택)
나열한 첫 번째 군은 레거시.spec.ClusterIP 필드에 사용된다.
이중 스택 서비스 구성 시나리오
이 예제는 다양한 이중 스택 서비스 구성 시나리오의 동작을 보여준다.
새로운 서비스에 대한 이중 스택 옵션
이 서비스 사양은 .spec.ipFamilyPolicy를 명시적으로 정의하지 않는다.
이 서비스를 만들 때 쿠버네티스는 처음 구성된 service-cluster-ip-range에서
서비스에 대한 클러스터 IP를 할당하고 .spec.ipFamilyPolicy를
SingleStack으로 설정한다. (셀렉터가 없는 서비스 및
헤드리스 서비스와
같은 방식으로 동작한다.)
이 서비스 사양은 .spec.ipFamilyPolicy에 PreferDualStack을
명시적으로 정의한다. 이중 스택 클러스터에서 이 서비스를 생성하면 쿠버네티스는
서비스에 대해 IPv4 및 IPv6 주소를 모두 할당한다. 컨트롤 플레인은 서비스의
.spec을 업데이트하여 IP 주소 할당을 기록한다. 필드 .spec.ClusterIPs는
기본 필드이며 할당된 IP 주소를 모두 포함한다. .spec.ClusterIP는 값이
.spec.ClusterIPs에서 계산된 보조 필드이다.
.spec.ClusterIP 필드의 경우 컨트롤 플레인은 첫 번째 서비스 클러스터 IP
범위와 동일한 주소 계열의 IP 주소를 기록한다.
단일 스택 클러스터에서 .spec.ClusterIPs 및 .spec.ClusterIP 필드는
모두 하나의 주소만 나열한다.
이중 스택이 활성화된 클러스터에서 .spec.ipFamilyPolicy에 RequireDualStack을
지정하면 PreferDualStack과 동일하게 작동한다.
이 서비스 사양은 .spec.ipFamilies에 IPv6과 IPv4를 명시적으로 정의하고
.spec.ipFamilyPolicy에 PreferDualStack을 정의한다. 쿠버네티스가 .spec.ClusterIPs에
IPv6 및 IPv4 주소를 할당할 때 .spec.ClusterIP는 .spec.ClusterIPs 배열의
첫 번째 요소이므로 IPv6 주소로 설정되어 기본값을 재정의한다.
이 예제는 서비스가 이미 있는 클러스터에서 이중 스택이 새로 활성화된
경우의 기본 동작을 보여준다. (기존 클러스터를 1.21 이상으로 업그레이드하면
이중 스택이 활성화된다.)
클러스터에서 이중 스택이 활성화된 경우 기존 서비스 (IPv4 또는 IPv6)는 컨트롤 플레인이
.spec.ipFamilyPolicy를 SingleStack으로 지정하고
.spec.ipFamilies를 기존 서비스의 주소 계열로 설정한다.
기존 서비스 클러스터 IP는 .spec.ClusterIPs에 저장한다.
클러스터에서 이중 스택이 활성화된 경우, 셀렉터가 있는 기존
헤드리스 서비스는
.spec.ClusterIP가 None이라도 컨트롤 플레인이 .spec.ipFamilyPolicy을
SingleStack으로 지정하고 .spec.ipFamilies는 첫 번째 서비스
클러스터 IP 범위(kube-apiserver에 대한 --service-cluster-ip-range 플래그를 통해 구성)의 주소 계열으로
지정한다.
서비스를 단일 스택에서 이중 스택으로 변경하려면 원하는 대로 .spec.ipFamilyPolicy를
SingleStack에서 PreferDualStack 또는 RequireDualStack으로 변경한다.
이 서비스를 단일 스택에서 이중 스택으로 변경하면 쿠버네티스는 누락된 주소 계열의
것을 배정하므로 해당 서비스는 이제 IPv4와 IPv6 주소를 갖는다.
.spec.ipFamilyPolicy를 SingleStack에서 PreferDualStack으로 업데이트하는 서비스 사양을 편집한다.
이전:
spec:ipFamilyPolicy:SingleStack
이후:
spec:ipFamilyPolicy:PreferDualStack
서비스를 이중 스택에서 단일 스택으로 변경하려면 .spec.ipFamilyPolicy를
PreferDualStack에서 또는 RequireDualStack을 SingleStack으로 변경한다.
이 서비스를 이중 스택에서 단일 스택으로 변경하면 쿠버네티스는 .spec.ClusterIPs
배열의 첫 번째 요소 만 유지하고 .spec.ClusterIP를 해당 IP 주소로 설정하고
.spec.ipFamilies를 .spec.ClusterIPs의 주소 계열로 설정한다.
셀렉터가 없는 헤드리스 서비스
셀렉터가 없는 서비스 및 .spec.ipFamilyPolicy가
명시적으로 설정되지 않은 경우 .spec.ipFamilyPolicy 필드의 기본값은
RequireDualStack 이다.
로드밸런서 서비스 유형
서비스에 이중 스택 로드밸런서를 프로비저닝하려면
.spec.type 필드를 LoadBalancer로 설정
.spec.ipFamilyPolicy 필드를 PreferDualStack 또는 RequireDualStack으로 설정
참고: 이중 스택 LoadBalancer 유형 서비스를 사용하려면 클라우드 공급자가
IPv4 및 IPv6로드 밸런서를 지원해야 한다.
이그레스(Egress) 트래픽
비공개로 라우팅할 수 있는 IPv6 주소를 사용하는 파드에서 클러스터 외부 대상
(예: 공용 인터넷)에 도달하기 위해 이그레스 트래픽을 활성화하려면 투명 프록시 또는
IP 위장과 같은 메커니즘을 통해 공개적으로 라우팅한 IPv6 주소를 사용하도록 파드를 활성화해야 한다.
ip-masq-agent
프로젝트는 이중 스택 클러스터에서 IP 위장을 지원한다.
윈도우에 있는 쿠버네티스는 싱글 스택(single-stack) "IPv6-only" 네트워킹을 지원하지 않는다. 그러나, 싱글 패밀리(single-family)
서비스로 되어 있는 파드와 노드에 대해서는 듀얼 스택(dual-stack) IPv4/IPv6 네트워킹을
지원한다.
l2bridge 네트워크로 IPv4/IPv6 듀얼 스택 네트워킹을 사용할 수 있다.
참고: 윈도우에서 오버레이 (VXLAN) 네트워크은 듀얼 스택 네트워킹을 지원하지 않는다.
토폴로지 인지 힌트 는 트래픽이 발생한 존 내에서 네트워크 트래픽을 유지하도록 처리하는 메커니즘을 제공한다. 클러스터의 파드간 동일한 존의 트래픽을 선호하는 것은 안전성, 성능(네트워크 지연 및 처리량) 혹은 비용 측면에 도움이 될 수 있다.
기능 상태:Kubernetes v1.23 [beta]
토폴로지 인지 힌트(Topology Aware Hints) 는 클라이언트가 엔드포인트(endpoint)를 어떻게 사용해야 하는지에 대한 제안을 포함시킴으로써
토폴로지 인지 라우팅을 가능하게 한다.
이러한 접근은 엔드포인트슬라이스(EndpointSlice) 또는 엔드포인트(Endpoints) 오브젝트의 소비자(consumer)가 이용할 수 있는 메타데이터를 추가하며,
이를 통해 해당 네트워크 엔드포인트로의 트래픽이 근원지에 더 가깝게 라우트될 수 있다.
예를 들어, 비용을 줄이거나 네트워크 성능을 높이기 위해,
인접성을 고려하여 트래픽을 라우트할 수 있다.
동기(motivation)
쿠버네티스 클러스터가 멀티-존(multi-zone) 환경에 배포되는 일이 점점 많아지고 있다.
토폴로지 인지 힌트 는 트래픽이 발생한 존 내에서 트래픽을 유지하도록 처리하는 메커니즘을 제공한다.
이러한 개념은 보통 "토폴로지 인지 라우팅"이라고 부른다.
서비스의 엔드포인트를 계산할 때,
엔드포인트슬라이스 컨트롤러는 각 엔드포인트의 토폴로지(지역(region) 및 존)를 고려하여,
엔드포인트가 특정 존에 할당되도록 힌트 필드를 채운다.
그러면 kube-proxy와 같은
클러스터 구성 요소는 해당 힌트를 인식하고,
(토폴로지 상 가까운 엔드포인트를 사용하도록) 트래픽 라우팅 구성에 활용한다.
토폴로지 인지 힌트 사용하기
service.kubernetes.io/topology-aware-hints 어노테이션을 auto로 설정하여
서비스에 대한 토폴로지 인지 힌트를 활성화할 수 있다.
이는 엔드포인트슬라이스 컨트롤러가 안전하다고 판단되는 경우 토폴로지 힌트를 설정하도록 지시하는 것이다.
명심할 점은, 이를 수행한다고 해서 힌트가 항상 설정되는 것은 아니라는 것이다.
동작 방법
이 기능을 동작시키는 요소는
엔드포인트슬라이스 컨트롤러와 kube-proxy 두 구성요소로 나눠져 있다.
이 섹션에서는 각 구성요소가 어떻게 이 기능을 동작시키는지에 대한 고차원 개요를 제공한다.
엔드포인트슬라이스 컨트롤러
엔드포인트슬라이스 컨트롤러는 이 기능이 활성화되어 있을 때
엔드포인트슬라이스에 힌트를 설정하는 일을 담당한다.
컨트롤러는 각 존에 일정 비율의 엔드포인트를 할당한다.
이 비율은 해당 존에 있는 노드의
할당 가능한(allocatable) CPU 코어에 의해 결정된다.
예를 들어, 한 존에 2 CPU 코어가 있고 다른 존에 1 CPU 코어만 있는 경우,
컨트롤러는 2 CPU 코어가 있는 존에 엔드포인트를 2배 할당할 것이다.
kube-proxy 구성요소는 엔드포인트슬라이스 컨트롤러가 설정한 힌트를 기반으로
자신이 라우팅하는 엔드포인트를 필터링한다.
대부분의 경우, 이는 kube-proxy가 동일 존 내에서 트래픽을 엔드포인트로 라우팅할 수 있음을 뜻한다.
때때로 컨트롤러는 존 사이에 보다 균등한 엔드포인트 분배를 위해 다른 존으로부터 엔드포인트를 할당하기도 한다.
이러한 경우 일부 트래픽은 다른 존으로 라우팅될 것이다.
보호 규칙
쿠버네티스 컨트롤 플레인과 각 노드의 kube-proxy는
토폴로지 인지 힌트를 사용하기 전에 몇 가지 보호 규칙을 적용한다.
이들이 만족되지 않으면, kube-proxy는 존에 상관없이
클러스터의 모든 곳으로부터 엔드포인트를 선택한다.
엔드포인트 수가 충분하지 않음: 존의 숫자보다 엔드포인트의 숫자가 적으면,
컨트롤러는 어떤 힌트도 할당하지 않을 것이다.
균형잡힌 할당이 불가능함: 일부 경우에, 존 간 균형잡힌 엔드포인트 할당이 불가능할 수 있다.
예를 들어, zone-a가 zone-b보다 2배 큰 상황에서,
엔드포인트가 2개 뿐이라면,
zone-a에 할당된 엔드포인트는 zone-b에 할당된 엔드포인트보다 2배의 트래픽을 수신할 것이다.
컨트롤러는 이 "예상 과부하" 값을 각 존에 대해
허용 가능한 임계값보다 작게 낮출 수 없는 경우에는 힌트를 할당하지 않는다.
명심할 점은, 이것이 실시간 피드백 기반이 아니라는 것이다.
개별 엔드포인트가 과부하 상태로 바뀔 가능성도 여전히 있다.
하나 이상의 노드에 대한 정보가 불충분함:topology.kubernetes.io/zone 레이블이 없는 노드가 있거나
할당 가능한 CPU 값을 보고하지 않는 노드가 있으면,
컨트롤 플레인은 토폴로지 인지 엔드포인트를 설정하지 않으며
이로 인해 kube-proxy는 존 별로 엔드포인트를 필터링하지 않는다.
하나 이상의 엔드포인트에 존 힌트가 없음: 이러한 상황이 발생하면,
kube-proxy는 토폴로지 인지 힌트로부터의 또는 토폴로지 인지 힌트로의 전환이 진행 중이라고 가정한다.
이 상태에서 서비스의 엔드포인트를 필터링하는 것은 위험할 수 있으므로
kube-proxy는 모든 엔드포인트를 사용하는 모드로 전환된다.
힌트에 존이 기재되지 않음: kube-proxy가 실행되고 있는 존을 향하는 힌트를 갖는 엔드포인트를
하나도 찾지 못했다면,
모든 존에서 오는 엔드포인트를 사용하는 모드로 전환된다.
이러한 경우는 기존에 있던 클러스터에 새로운 존을 추가하는 경우에 발생할 가능성이 가장 높다.
제약사항
토폴로지 인지 힌트는 서비스의 externalTrafficPolicy 또는
internalTrafficPolicy가 Local로 설정된 경우에는 사용되지 않는다.
동일 클러스터의 서로 다른 서비스들에 대해 두 기능 중 하나를 사용하는 것은 가능하며,
하나의 서비스에 두 기능 모두를 사용하는 것은 불가능하다.
이러한 접근 방법은 존의 일부분에서
많은 트래픽이 발생하는 서비스에는 잘 작동하지 않을 것이다.
대신, 들어오는 트래픽이
각 존 내 노드 용량에 대략 비례한다고 가정한다.
엔드포인트슬라이스 컨트롤러는 각 존 내의 비율을 계산할 때
준비되지 않은(unready) 노드는 무시한다.
이 때문에 많은 노드가 준비되지 않은 상태에서는 의도하지 않은 결과가 나타날 수도 있다.
엔드포인트슬라이스 컨트롤러는 각 존 내의 비율을 배포하거나 계산할 때
톨러레이션은 고려하지 않는다.
서비스를 구성하는 파드가 클러스터의 일부 노드에만 배치되어 있는 경우,
이러한 상황은 고려되지 않을 것이다.
오토스케일링 기능과는 잘 동작하지 않을 수 있다.
예를 들어, 하나의 존에서 많은 트래픽이 발생하는 경우,
해당 존에 할당된 엔드포인트만 트래픽을 처리하고 있을 것이다.
이로 인해 Horizontal Pod Autoscaler가
이 이벤트를 감지하지 못하거나,
또는 새롭게 추가되는 파드가 다른 존에 추가될 수 있다.
쿠버네티스는 리눅스 및 윈도우 노드를 지원한다.
단일 클러스터 내에 두 종류의 노드를 혼합할 수 있다.
이 페이지에서는 윈도우 운영 체제에서의 네트워킹에 대한 개요를 제공한다.
윈도우에서의 컨테이너 네트워킹
윈도우 컨테이너에 대한 네트워킹은
CNI 플러그인을 통해 노출된다.
윈도우 컨테이너는 네트워킹과 관련하여 가상 머신과 유사하게 작동한다.
각 컨테이너에는 Hyper-V 가상 스위치(vSwitch)에 연결된 가상 네트워크 어댑터(vNIC)가 있다.
호스트 네트워킹 서비스(HNS)와 호스트 컴퓨팅 서비스(HCS)는 함께 작동하여
컨테이너를 만들고 컨테이너 vNIC을 네트워크에 연결한다.
HCS는 컨테이너 관리를 담당하는 반면
HNS는 다음과 같은 네트워킹 리소스 관리를 담당한다.
가상 네트워크(vSwitch 생성 포함)
엔드포인트 / vNIC
네임스페이스
정책(패킷 캡슐화, 로드 밸런싱 규칙, ACL, NAT 규칙 등)
윈도우 HNS(호스트 네트워킹 서비스)와 가상 스위치는
네임스페이스를 구현하고 파드 또는 컨테이너에 필요한 가상 NIC을 만들 수 있다.
그러나 DNS, 라우트, 메트릭과 같은 많은 구성은
리눅스에서와 같이 /etc/ 내의 파일이 아닌 윈도우 레지스트리 데이터베이스에 저장된다.
컨테이너의 윈도우 레지스트리는 호스트 레지스트리와 별개이므로
호스트에서 컨테이너로 /etc/resolv.conf를 매핑하는 것과 같은 개념은 리눅스에서와 동일한 효과를 갖지 않는다.
해당 컨테이너의 컨텍스트에서 실행되는 윈도우 API를 사용하여 구성해야 한다.
따라서 CNI 구현에서는 파일 매핑에 의존하는 대신
HNS를 호출하여 네트워크 세부 정보를 파드 또는 컨테이너로 전달해야 한다.
네트워크 모드
윈도우는 L2bridge, L2tunnel, Overlay, Transparent 및 NAT의 다섯 가지 네트워킹 드라이버/모드를 지원한다.
윈도우와 리눅스 워커 노드가 있는 이기종 클러스터에서는
윈도우와 리눅스 모두에서 호환되는 네트워킹 솔루션을 선택해야 한다.
윈도우에서 다음과 같은 out-of-tree 플러그인이 지원되며,
어떠한 경우에 각 CNI를 사용하면 좋은지도 소개한다.
네트워크 드라이버
설명
컨테이너 패킷 수정
네트워크 플러그인
네트워크 플러그인 특성
L2bridge
컨테이너는 외부 vSwitch에 연결된다. 컨테이너는 언더레이 네트워크에 연결된다. 하지만 인그레스/이그레스시에 재작성되기 때문에 물리적 네트워크가 컨테이너 MAC을 학습할 필요가 없다.
MAC은 호스트 MAC에 다시 쓰여지고, IP는 HNS OutboundNAT 정책을 사용하여 호스트 IP에 다시 쓰여질 수 있다.
Azure-CNI를 사용하면 컨테이너를 Azure vNET과 통합할 수 있으며, Azure Virtual Network에서 제공하는 기능 집합을 활용할 수 있다. 예를 들어, Azure 서비스에 안전하게 연결하거나 Azure NSG를 사용한다. azure-cni 예제를 참고한다.
Overlay
컨테이너에는 외부 vSwitch에 연결된 vNIC이 제공된다. 각 오버레이 네트워크는 사용자 지정 IP 접두사로 정의된 자체 IP 서브넷을 가져온다. 오버레이 네트워크 드라이버는 VXLAN 캡슐화를 사용한다.
win-overlay는 가상 컨테이너 네트워크를 호스트의 언더레이에서 격리하려는 경우(예: 보안 상의 이유로) 사용해야 한다. 데이터 센터의 IP에 제한이 있는 경우, (다른 VNID 태그가 있는) 다른 오버레이 네트워크에 IP를 재사용할 수 있다. 이 옵션을 사용하려면 Windows Server 2019에서 KB4489899가 필요하다.
외부 vSwitch가 필요하다. 컨테이너는 논리적 네트워크(논리적 스위치 및 라우터)를 통해 파드 내 통신을 가능하게 하는 외부 vSwitch에 연결된다.
패킷은 GENEVE 또는 STT 터널링을 통해 캡슐화되는데, 동일한 호스트에 있지 않은 파드에 도달하기 위한 터널링을 한다. 패킷은 ovn 네트워크 컨트롤러에서 제공하는 터널 메타데이터 정보를 통해 전달되거나 삭제된다. NAT는 north-south 통신(데이터 센터와 클라이언트, 네트워크 상의 데이터 센터 외부와의 통신)을 위해 수행된다.
이 플러그인은 자동 노드 서브넷 임대 할당과 HNS 네트워크 생성을 위해
윈도우의 Flannel 데몬(Flanneld)과 함께 작동할 수 있도록
참조 CNI 플러그인(win-overlay, win-bridge) 중 하나에 대한 위임을 지원한다.
이 플러그인은 자체 구성 파일 (cni.conf)을 읽고,
이를 FlannelD가 생성한 subnet.env 파일의 환경 변수와 결합한다.
이후 이를 네트워크 연결을 위한 참조 CNI 플러그인 중 하나에 위임하고,
노드-할당 서브넷을 포함하는 올바른 구성을 IPAM 플러그인(예: host-local)으로 보낸다.
노드, 파드 및 서비스 오브젝트의 경우,
TCP/UDP 트래픽에 대해 다음 네트워크 흐름이 지원된다.
파드 → 파드(IP)
파드 → 파드(이름)
파드 → 서비스(Cluster IP)
파드 → 서비스(PQDN, 단 "."이 없는 경우에만)
파드 → 서비스(FQDN)
파드 → External(IP)
파드 → External(DNS)
노드 → 파드
파드 → 노드
IP 주소 관리 (IPAM)
The following IPAM options are supported on Windows:
Set service.spec.externalTrafficPolicy를 "Local"로 설정하고 kube-proxy에 DSR을 활성화한다.
경고:
목적지 노드가 Windows Server 2022를 실행 중인 경우, 오버레이 네트워킹에서 NodePort 서비스에 문제가 있음이 알려져 있다.
이 이슈를 완전히 피하려면, 서비스에 externalTrafficPolicy: Local를 설정한다.
KB5005619 또는 그 이상이 설치된 Windows Server 2022의 경우, l2bridge 네트워크에서 파드 간 연결성에 문제가 있음이 알려져 있다.
이 이슈를 우회하고 파드 간 연결성을 복구하기 위해, kube-proxy의 WinDSR 기능을 비활성화할 수 있다.
win-overlay, win-bridge, Azure-CNI 플러그인을 통해 ICMP 프로토콜을 사용하는 아웃바운드 통신.
특히, 윈도우 데이터 플레인(VFP)은
ICMP 패킷 치환을 지원하지 않는다. 이것은 다음을 의미한다.
동일한 네트워크 내의 목적지로 전달되는 ICMP 패킷(예: ping을 통한 파드 간 통신)은
예상대로 제한 없이 작동한다.
TCP/UDP 패킷은 예상대로 제한 없이 작동한다.
원격 네트워크를 통과하도록 지정된 ICMP 패킷(예: ping을 통한 파드에서 외부 인터넷으로의 통신)은
치환될 수 없으므로 소스로 다시 라우팅되지 않는다.
TCP/UDP 패킷은 여전히 치환될 수 있기 때문에 ping <destination>을
curl <destination>으로 대체하여 외부와의 연결을 디버깅할 수 있다.
다른 제한도 존재한다.
윈도우 참조 네트워크 플러그인 win-bridge와 win-overlay는
현재 CHECK 구현 누락으로 인해
CNI 사양 v0.4.0을 구현하지 않는다.
Flannel VXLAN CNI는 윈도우에서 다음과 같은 제한이 있다.
노드-파드 연결은 Flannel v0.12.0(또는 그 이상) 상의 로컬 파드에서만 가능하다.
Flannel은 VNI 4096 및 UDP 4789 포트만 사용하도록 제한된다.
이 파라미터에 대한 더 자세한 사항은
공식 Flannel VXLAN 백엔드 문서를 참조한다.
10 - 서비스 내부 트래픽 정책
클러스터 내의 두 파드가 통신을 하려고 하고 두 파드가 동일한 노드에서 실행되는 경우, _서비스 내부 트래픽 정책_을 사용하여 네트워크 트래픽을 해당 노드 안에서 유지할 수 있다. 클러스터 네트워크를 통한 왕복 이동을 피하면 안전성, 성능 (네트워크 지연 및 처리량) 혹은 비용 측면에 도움이 될 수 있다.
기능 상태:Kubernetes v1.26 [stable]
서비스 내부 트래픽 정책 을 사용하면 내부 트래픽 제한이 트래픽이 시작된
노드 내의 엔드포인트로만 내부 트래픽을 라우팅하도록 한다.
여기서 "내부" 트래픽은 현재 클러스터의 파드로부터 시작된 트래픽을 지칭한다.
이를 통해 비용을 절감하고 성능을 개선할 수 있다.
서비스 내부 트래픽 정책 사용
서비스의
.spec.internalTrafficPolicy를 Local로 설정하여 내부 전용 트래픽 정책을 활성화 할 수 있다.
이것은 kube-proxy가 클러스터 내부 트래픽을 위해 노드 내부 엔드포인트로만 사용하도록 한다.
참고: 지정된 서비스에 대한 엔드포인트가 없는 노드의 파드인 경우에
서비스는 다른 노드에 엔드포인트가 있더라도 엔드포인트가 없는 것처럼 작동한다.
(이 노드의 파드에 대해서)
다음 예제는 서비스의 .spec.internalTrafficPolicy를 Local로
설정하는 것을 보여 준다:
kube-proxy는 spec.internalTrafficPolicy 의 설정에 따라서 라우팅되는
엔드포인트를 필터링한다.
이것을 Local로 설정하면, 노드 내부 엔드포인트만 고려한다.
이 설정이 Cluster(기본)이거나 설정되지 않았다면 모든 엔드포인트를 고려한다.
쿠버네티스에서 서비스는 파드 집합에서
실행되는 애플리케이션을 노출하는 추상적인 방법이다. 서비스는
(type: ClusterIP인 서비스를 통해) 클러스터-범위의 가상 IP 주소를 가진다.
클라이언트는 해당 가상 IP 주소로 접속할 수 있으며, 쿠버네티스는 해당 서비스를 거치는 트래픽을
서비스 뒷단의 파드들에게 로드 밸런싱한다.
어떻게 서비스 클러스터IP가 할당되는가?
쿠버네티스가 서비스에 가상 IP를 할당해야 할 때,
해당 요청분에는 두가지 방법 중 하나가 수행된다.
동적 할당
클러스터의 컨트롤 플레인이 자동으로 type: ClusterIP 서비스의 설정된 IP 범위 내에서 가용 IP 주소를 선택한다.
정적 할당
서비스용으로 설정된 IP 범위 내에서 사용자가 IP 주소를 선택한다.
클러스터 전체에 걸쳐, 모든 서비스의 ClusterIP는 항상 고유해야 한다.
이미 할당된 특정 ClusterIP로 서비스를 생성하려고 하면
에러가 발생한다.
왜 서비스 클러스터 IP를 예약해야 하는가?
때로는 클러스터의 다른 컴포넌트들이나 사용자들이 사용할 수 있도록 이미 알려진 IP 주소를 통해
서비스를 실행하고 싶을 것이다.
가장 좋은 방법은 클러스터의 DNS 서비스이다.
가벼운 관례로 일부 쿠버네티스 설치 관리자들은 DNS 서비스의 10번째 주소를 서비스 IP 범위로 지정한다.
클러스터의 서비스 IP 범위가 10.96.0.0/16 인 상황에서 DNS 서비스 IP를 10.96.0.10 으로 지정하고 싶다면,
아래와 같이 서비스를 생성해야 할 것이다.
하지만 이전에도 설명했듯이, IP 주소 10.96.0.10는 예약된 상태가 아니다. 만약 다른 서비스들이
그 전에 동적 할당으로 생성되었거나, 동시에 생성되었다면, 해당 서비스들이 IP를 할당받았을 수도 있다.
따라서, 충돌 에러로 인해 DNS 서비스를 생성하지 못할 것이다.
어떻게 하면 서비스 클러스터IP 충돌을 피할 수 있는가?
쿠버네티스에 구현된 할당 전략은
서비스에 클러스터IP 할당의 충돌 위험을 줄여준다.
ClusterIP 범위는 16 이상 256 미만의 단계적인 차수를 나타내는
min(max(16, cidrSize / 16), 256) 공식을 기반으로 나누어져 있다.
동적 IP 할당은 기본값으로 위쪽 밴드를 사용한다. 위쪽 범위가 모두 소진되었다면 아래쪽 밴드를
사용할 것이다. 이것은 사용자로 하여금 아래쪽 밴드의 정적 할당을 낮은 충돌 확률로 수행할 수 있게
해준다.
예시
예시 1
아래 예시는 서비스 IP 주소들로 10.96.0.0/24 (CIDR 표기법) IP 주소 범위를
사용한다.
범위 크기: 28 - 2 = 254
밴드 오프셋: min(max(16, 256/16), 256) = min(16, 256) = 16
정적 밴드 시작점: 10.96.0.1
정적 밴드 끝점: 10.96.0.16
범위 끝점: 10.96.0.254
예시 2
아래 예시는 서비스 IP 주소들로 10.96.0.0/20 (CIDR 표기법) IP 주소 범위를
사용한다.
범위 크기: 212 - 2 = 4094
밴드 오프셋: min(max(16, 4096/16), 256) = min(256, 256) = 256
정적 밴드 시작점: 10.96.0.1
정적 밴드 끝점: 10.96.1.0
범위 끝점: 10.96.15.254
예시 3
아래 예시는 서비스 IP 주소들로 10.96.0.0/16 (CIDR 표기법) IP 주소 범위를
사용한다.
범위 크기: 216 - 2 = 65534
밴드 오프셋: min(max(16, 65536/16), 256) = min(4096, 256) = 256
정적 밴드 시작점: 10.96.0.1
정적 밴드 끝점: 10.96.1.0
범위 끝점: 10.96.255.254
참고: 이 기능, 특히 알파 topologyKeys API는 쿠버네티스 v1.21부터
더 이상 사용되지 않는다.
쿠버네티스 v1.21에 도입된 토폴로지 인지 힌트는
유사한 기능을 제공한다.
서비스 토폴로지 를 활성화 하면 서비스는 클러스터의 노드 토폴로지를
기반으로 트래픽을 라우팅한다. 예를 들어, 서비스는 트래픽을
클라이언트와 동일한 노드이거나 동일한 가용성 영역에 있는 엔드포인트로
우선적으로 라우팅되도록 지정할 수 있다.
소개
기본적으로 ClusterIP 또는 NodePort 서비스로 전송된 트래픽은 서비스의
모든 백엔드 주소로 라우팅될 수 있다. 쿠버네티스 1.7을 사용하면 트래픽을 수신한
동일한 노드에서 실행 중인 파드로 "외부(external)" 트래픽을 라우팅할 수
있다. ClusterIP 서비스의 경우, 라우팅에 대한 동일한 노드 기본 설정이
불가능했다. 또한 동일한 영역 내의 엔드 포인트에 대한 라우팅을 선호하도록
클러스터를 구성할 수도 없다.
서비스에 topologyKeys 를 설정하면, 출발 및 대상 노드에 대한
노드 레이블을 기반으로 트래픽을 라우팅하는 정책을 정의할 수 있다.
소스와 목적지 사이의 레이블 일치를 통해 클러스터 운영자는
서로 "근접(closer)"하거나 "먼(father)" 노드 그룹을 지정할 수 있다.
자신의 요구 사항에 맞는 메트릭을 나타내는 레이블을 정의할 수 있다.
예를 들어, 퍼블릭 클라우드에서는 지역 간의 트래픽에는 관련 비용이 발생(지역 내
트래픽은 일반적으로 그렇지 않다)하기 때문에, 네트워크 트래픽을 동일한 지역 내에 유지하는 것을
선호할 수 있다. 다른 일반적인 필요성으로는 데몬셋(DaemonSet)이 관리하는
로컬 파드로 트래픽을 라우팅하거나, 대기 시간을 최소화하기 위해
동일한 랙 상단(top-of-rack) 스위치에 연결된 노드로 트래픽을
유지하는 것이 있다.
서비스 토폴로지 사용하기
만약 클러스터에서 ServiceTopology기능 게이트가 활성화된 경우, 서비스 사양에서
topologyKeys 필드를 지정해서 서비스 트래픽 라우팅을 제어할 수 있다. 이 필드는
이 서비스에 접근할 때 엔드포인트를 정렬하는데 사용되는 노드
레이블의 우선 순위 목록이다. 트래픽은 첫 번째 레이블 값이 해당 레이블의
발신 노드 값과 일치하는 노드로 보내진다. 만약 노드에 서비스와 일치하는
백엔드가 없는 경우, 두 번째 레이블을 그리고 더 이상의
레이블이 남지 않을 때까지 고려한다.
만약 일치하는 것을 못찾는 경우에는, 서비스에 대한 백엔드가 없었던 것처럼
트래픽이 거부될 것이다. 즉, 엔드포인트는 사용 가능한 백엔드가 있는 첫 번째
토폴로지 키를 기반으로 선택된다. 만약 이 필드가 지정되고 모든 항목에
클라이언트의 토폴로지와 일치하는 백엔드가 없는 경우, 서비스에는 해당 클라이언트에
대한 백엔드가 없기에 연결에 실패해야 한다. 특수한 값인 "*" 은 "모든 토폴로지"를
의미하는데 사용될 수 있다. 이 캐치 올(catch-all) 값을 사용하는 경우
목록의 마지막 값으로만 타당하다.
만약 topologyKeys 가 지정되지 않거나 비어있는 경우 토폴로지 제약 조건이 적용되지 않는다.
호스트 이름, 영역 이름 그리고 지역 이름으로 레이블이 지정된 노드가 있는
클러스터가 있다고 생각해 보자. 그러고 나면, 서비스의 topologyKeys 값을 설정해서 다음과 같이 트래픽을
전달할 수 있다.
동일한 노드의 엔드포인트에만 해당하고, 엔드포인트가 노드에 없으면 실패한다:
["kubernetes.io/hostname"].
동일한 노드의 엔드포인트를 선호하지만, 동일한 영역의 엔드포인트로 대체
한 후 동일한 지역으로 대체되고, 그렇지 않으면 실패한다: ["kubernetes.io/hostname", "topology.kubernetes.io/zone", "topology.kubernetes.io/region"].
예를 들어 데이터 위치가 중요한 경우에 유용할 수 있다.
동일한 영역이 선호되지만, 이 영역 내에 사용할 수 있는 항목이 없는 경우에는
사용가능한 엔드포인트로 대체된다:
["topology.kubernetes.io/zone", "*"].
제약들
서비스 토폴로지는 externalTrafficPolicy=Local 와 호환되지 않으므로
서비스는 이 두 가지 기능을 함께 사용할 수 없다. 동일한 서비스가 아닌
같은 클러스터의 다른 서비스라면 이 기능을 함께 사용할
수 있다.
유효한 토폴로지 키는 현재 kubernetes.io/hostname,
topology.kubernetes.io/zone 그리고 topology.kubernetes.io/region 로
제한되어 있지만, 앞으로 다른 노드 레이블로 일반화 될 것이다.
토폴로지 키는 유효한 레이블 키이어야 하며 최대 16개의 키를 지정할 수 있다.
만약 캐치 올(catch-all) 값인 "*" 를 사용한다면 토폴로지 키들의 마지막 값이어야
한다.
예시들
다음은 서비스 토폴로지 기능을 사용하는 일반적인 예시이다.
노드 로컬 엔드포인트만
노드 로컬 엔드포인트로만 라우팅하는 서비스이다. 만약 노드에 엔드포인트가 없으면 트레픽이 드롭된다.