이 섹션의 다중 페이지 출력 화면임. 여기를 클릭하여 프린트.

이 페이지의 일반 화면으로 돌아가기.

컴퓨트, 스토리지 및 네트워킹 익스텐션

1 - 네트워크 플러그인

쿠버네티스의 네트워크 플러그인은 몇 가지 종류가 있다.

  • CNI 플러그인: 상호 운용성을 위해 설계된 컨테이너 네트워크 인터페이스(CNI) 명세를 준수한다.
  • 쿠버네티스는 CNI 명세의 v0.4.0 릴리스를 따른다.
  • Kubenet 플러그인: bridgehost-local CNI 플러그인을 사용하여 기본 cbr0 구현한다.

설치

kubelet에는 단일 기본 네트워크 플러그인과 전체 클러스터에 공통된 기본 네트워크가 있다. 플러그인은 시작할 때 플러그인을 검색하고, 찾은 것을 기억하며, 파드 라이프사이클에서 적절한 시간에 선택한 플러그인을 실행한다(CRI는 자체 CNI 플러그인을 관리하므로 도커에만 해당됨). 플러그인 사용 시 명심해야 할 두 가지 Kubelet 커맨드라인 파라미터가 있다.

  • cni-bin-dir: Kubelet은 시작할 때 플러그인에 대해 이 디렉터리를 검사한다.
  • network-plugin: cni-bin-dir 에서 사용할 네트워크 플러그인. 플러그인 디렉터리에서 검색한 플러그인이 보고된 이름과 일치해야 한다. CNI 플러그인의 경우, 이는 "cni"이다.

네트워크 플러그인 요구 사항

파드 네트워킹을 구성하고 정리하기 위해 NetworkPlugin 인터페이스를 제공하는 것 외에도, 플러그인은 kube-proxy에 대한 특정 지원이 필요할 수 있다. iptables 프록시는 분명히 iptables에 의존하며, 플러그인은 컨테이너 트래픽이 iptables에 사용 가능하도록 해야 한다. 예를 들어, 플러그인이 컨테이너를 리눅스 브릿지에 연결하는 경우, 플러그인은 net/bridge/bridge-nf-call-iptables sysctl을 1 로 설정하여 iptables 프록시가 올바르게 작동하는지 확인해야 한다. 플러그인이 리눅스 브리지를 사용하지 않는 경우(그러나 Open vSwitch나 다른 메커니즘과 같은 기능을 사용함) 컨테이너 트래픽이 프록시에 대해 적절하게 라우팅되도록 해야 한다.

kubelet 네트워크 플러그인이 지정되지 않은 경우, 기본적으로 noop 플러그인이 사용되며, net/bridge/bridge-nf-call-iptables=1 을 설정하여 간단한 구성(브릿지가 있는 도커 등)이 iptables 프록시에서 올바르게 작동하도록 한다.

CNI

CNI 플러그인은 Kubelet에 --network-plugin=cni 커맨드라인 옵션을 전달하여 선택된다. Kubelet은 --cni-conf-dir(기본값은 /etc/cni/net.d)에서 파일을 읽고 해당 파일의 CNI 구성을 사용하여 각 파드의 네트워크를 설정한다. CNI 구성 파일은 CNI 명세와 일치해야 하며, 구성에서 참조하는 필수 CNI 플러그인은 --cni-bin-dir(기본값은 /opt/cni/bin)에 있어야 한다.

디렉터리에 여러 CNI 구성 파일이 있는 경우, kubelet은 이름별 알파벳 순으로 구성 파일을 사용한다.

구성 파일에 지정된 CNI 플러그인 외에도, 쿠버네티스는 최소 0.2.0 버전의 표준 CNI lo 플러그인이 필요하다.

hostPort 지원

CNI 네트워킹 플러그인은 hostPort 를 지원한다. CNI 플러그인 팀이 제공하는 공식 포트맵(portmap) 플러그인을 사용하거나 portMapping 기능이 있는 자체 플러그인을 사용할 수 있다.

hostPort 지원을 사용하려면, cni-conf-dirportMappings capability 를 지정해야 한다. 예를 들면 다음과 같다.

{
  "name": "k8s-pod-network",
  "cniVersion": "0.3.0",
  "plugins": [
    {
      "type": "calico",
      "log_level": "info",
      "datastore_type": "kubernetes",
      "nodename": "127.0.0.1",
      "ipam": {
        "type": "host-local",
        "subnet": "usePodCidr"
      },
      "policy": {
        "type": "k8s"
      },
      "kubernetes": {
        "kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
      }
    },
    {
      "type": "portmap",
      "capabilities": {"portMappings": true}
    }
  ]
}

트래픽 셰이핑 지원

실험적인 기능입니다

CNI 네트워킹 플러그인은 파드 수신 및 송신 트래픽 셰이핑도 지원한다. CNI 플러그인 팀에서 제공하는 공식 대역폭(bandwidth) 플러그인을 사용하거나 대역폭 제어 기능이 있는 자체 플러그인을 사용할 수 있다.

트래픽 셰이핑 지원을 활성화하려면, CNI 구성 파일 (기본값 /etc/cni/net.d)에 bandwidth 플러그인을 추가하고, 바이너리가 CNI 실행 파일 디렉터리(기본값: /opt/cni/bin)에 포함되어 있는지 확인한다.

{
  "name": "k8s-pod-network",
  "cniVersion": "0.3.0",
  "plugins": [
    {
      "type": "calico",
      "log_level": "info",
      "datastore_type": "kubernetes",
      "nodename": "127.0.0.1",
      "ipam": {
        "type": "host-local",
        "subnet": "usePodCidr"
      },
      "policy": {
        "type": "k8s"
      },
      "kubernetes": {
        "kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
      }
    },
    {
      "type": "bandwidth",
      "capabilities": {"bandwidth": true}
    }
  ]
}

이제 파드에 kubernetes.io/ingress-bandwidthkubernetes.io/egress-bandwidth 어노테이션을 추가할 수 있다. 예를 들면 다음과 같다.

apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubernetes.io/ingress-bandwidth: 1M
    kubernetes.io/egress-bandwidth: 1M
...

kubenet

Kubenet은 리눅스에서만 사용할 수 있는 매우 기본적이고, 간단한 네트워크 플러그인이다. 그 자체로는, 크로스-노드 네트워킹 또는 네트워크 정책과 같은 고급 기능을 구현하지 않는다. 일반적으로 노드 간, 또는 단일 노드 환경에서 통신을 위한 라우팅 규칙을 설정하는 클라우드 제공자와 함께 사용된다.

Kubenet은 cbr0 라는 리눅스 브리지를 만들고 각 쌍의 호스트 끝이 cbr0 에 연결된 각 파드에 대한 veth 쌍을 만든다. 쌍의 파드 끝에는 구성 또는 컨트롤러 관리자를 통해 노드에 할당된 범위 내에서 할당된 IP 주소가 지정된다. cbr0 에는 호스트에서 활성화된 일반 인터페이스의 가장 작은 MTU와 일치하는 MTU가 지정된다.

플러그인에는 몇 가지 사항이 필요하다.

  • 표준 CNI bridge, lohost-local 플러그인은 최소 0.2.0 버전이 필요하다. Kubenet은 먼저 /opt/cni/bin 에서 검색한다. 추가 검색 경로를 제공하려면 cni-bin-dir 을 지정한다. 처음 검색된 디렉터리가 적용된다.
  • 플러그인을 활성화하려면 Kubelet을 --network-plugin=kubenet 인수와 함께 실행해야 한다.
  • Kubelet은 --non-masquerade-cidr=<clusterCidr> 인수와 함께 실행하여 이 범위 밖 IP로의 트래픽이 IP 마스커레이드(masquerade)를 사용하도록 해야 한다.
  • --pod-cidr kubelet 커맨드라인 옵션 또는 --allocate-node-cidrs=true --cluster-cidr=<cidr> 컨트롤러 관리자 커맨드라인 옵션을 통해 노드에 IP 서브넷을 할당해야 한다.

MTU 사용자 정의 (kubenet 사용)

최상의 네트워킹 성능을 얻으려면 MTU를 항상 올바르게 구성해야 한다. 네트워크 플러그인은 일반적으로 합리적인 MTU를 유추하려고 시도하지만, 때로는 로직에 따라 최적의 MTU가 지정되지 않는다. 예를 들어, 도커 브리지나 다른 인터페이스에 작은 MTU가 지정되어 있으면, kubenet은 현재 해당 MTU를 선택한다. 또는 IPSEC 캡슐화를 사용하는 경우, MTU를 줄여야 하며, 이 계산은 대부분의 네트워크 플러그인에서 범위를 벗어난다.

필요한 경우, network-plugin-mtu kubelet 옵션을 사용하여 MTU를 명시 적으로 지정할 수 있다. 예를 들어, AWS에서 eth0 MTU는 일반적으로 9001이므로, --network-plugin-mtu=9001 을 지정할 수 있다. IPSEC를 사용하는 경우 캡슐화 오버헤드를 허용하도록 --network-plugin-mtu=8873 과 같이 IPSEC을 줄일 수 있다.

이 옵션은 네트워크 플러그인에 제공된다. 현재 kubenet만 network-plugin-mtu 를 지원한다.

용법 요약

  • --network-plugin=cni--cni-bin-dir(기본값 /opt/cni/bin)에 있는 실제 CNI 플러그인 바이너리와 --cni-conf-dir(기본값 /etc/cni/net.d)에 있는 CNI 플러그인 구성과 함께 cni 네트워크 플러그인을 사용하도록 지정한다.
  • --network-plugin=kubenet/opt/cni/bin 또는 cni-bin-dir 에 있는 CNI bridge, lohost-local 플러그인과 함께 kubenet 네트워크 플러그인을 사용하도록 지정한다.
  • 현재 kubenet 네트워크 플러그인에서만 사용하는 --network-plugin-mtu=9001 은 사용할 MTU를 지정한다.

2 - 장치 플러그인

GPU, NIC, FPGA, InfiniBand 및 공급 업체별 설정이 필요한 유사한 리소스를 위한 플러그인을 구현하는데 쿠버네티스 장치 플러그인 프레임워크를 사용한다.
FEATURE STATE: Kubernetes v1.10 [beta]

쿠버네티스는 시스템 하드웨어 리소스를 Kubelet에 알리는 데 사용할 수 있는 장치 플러그인 프레임워크를 제공한다.

공급 업체는 쿠버네티스 자체의 코드를 커스터마이징하는 대신, 수동 또는 데몬셋으로 배포하는 장치 플러그인을 구현할 수 있다. 대상이 되는 장치에는 GPU, 고성능 NIC, FPGA, InfiniBand 어댑터 및 공급 업체별 초기화 및 설정이 필요할 수 있는 기타 유사한 컴퓨팅 리소스가 포함된다.

장치 플러그인 등록

kubelet은 Registration gRPC 서비스를 노출시킨다.

service Registration {
	rpc Register(RegisterRequest) returns (Empty) {}
}

장치 플러그인은 이 gRPC 서비스를 통해 kubelet에 자체 등록할 수 있다. 등록하는 동안, 장치 플러그인은 다음을 보내야 한다.

  • 유닉스 소켓의 이름.
  • 빌드된 장치 플러그인 API 버전.
  • 알리려는 ResourceName. 여기서 ResourceName확장된 리소스 네이밍 체계vendor-domain/resourcetype 의 형식으로 따라야 한다. (예를 들어, NVIDIA GPU는 nvidia.com/gpu 로 알려진다.)

성공적으로 등록하고 나면, 장치 플러그인은 kubelet이 관리하는 장치 목록을 전송한 다음, kubelet은 kubelet 노드 상태 업데이트의 일부로 해당 자원을 API 서버에 알리는 역할을 한다. 예를 들어, 장치 플러그인이 kubelet에 hardware-vendor.example/foo 를 등록하고 노드에 두 개의 정상 장치를 보고하고 나면, 노드 상태가 업데이트되어 노드에 2개의 "Foo" 장치가 설치되어 사용 가능함을 알릴 수 있다.

그러고 나면, 사용자가 컨테이너 명세에 있는 장치를 요청할 수 있다. 다만, 다른 종류의 리소스를 요청하는 것이므로 다음과 같은 제한이 있다.

  • 확장된 리소스는 정수(integer) 형태만 지원되며 오버커밋(overcommit) 될 수 없다.
  • 컨테이너간에 장치를 공유할 수 없다.

쿠버네티스 클러스터가 특정 노드에서 hardware-vendor.example/foo 리소스를 알리는 장치 플러그인을 실행한다고 가정해 보자. 다음은 데모 워크로드를 실행하기 위해 이 리소스를 요청하는 파드의 예이다.

---
apiVersion: v1
kind: Pod
metadata:
  name: demo-pod
spec:
  containers:
    - name: demo-container-1
      image: k8s.gcr.io/pause:2.0
      resources:
        limits:
          hardware-vendor.example/foo: 2
#
# 이 파드는 2개의 hardware-vendor.example/foo 장치가 필요하며
# 해당 요구를 충족할 수 있는 노드에만
# 예약될 수 있다.
#
# 노드에 2개 이상의 사용 가능한 장치가 있는 경우
# 나머지는 다른 파드에서 사용할 수 있다.

장치 플러그인 구현

장치 플러그인의 일반적인 워크플로우에는 다음 단계가 포함된다.

  • 초기화. 이 단계에서, 장치 플러그인은 공급 업체별 초기화 및 설정을 수행하여 장치가 준비 상태에 있는지 확인한다.

  • 플러그인은 다음의 인터페이스를 구현하는 호스트 경로 /var/lib/kubelet/device-plugins/ 아래에 유닉스 소켓과 함께 gRPC 서비스를 시작한다.

    service DevicePlugin {
      	    // GetDevicePluginOptions는 장치 관리자와 통신할 옵션을 반환한다.
          rpc GetDevicePluginOptions(Empty) returns (DevicePluginOptions) {}
    
      		// ListAndWatch는 장치 목록 스트림을 반환한다.
      	    // 장치 상태가 변경되거나 장치가 사라질 때마다, ListAndWatch는
      	    // 새 목록을 반환한다.
          rpc ListAndWatch(Empty) returns (stream ListAndWatchResponse) {}
    
      			// 컨테이너를 생성하는 동안 Allocate가 호출되어 장치
      			// 플러그인이 장치별 작업을 실행하고 Kubelet에 장치를
      			// 컨테이너에서 사용할 수 있도록 하는 단계를 지시할 수 있다.
          rpc Allocate(AllocateRequest) returns (AllocateResponse) {}
    
          // GetPreferredAllocation은 사용 가능한 장치 목록에서 할당할
      			// 기본 장치 집합을 반환한다. 그 결과로 반환된 선호하는 할당은
      			// devicemanager가 궁극적으로 수행하는 할당이 되는 것을 보장하지
      			// 않는다. 가능한 경우 devicemanager가 정보에 입각한 할당 결정을
      			// 내릴 수 있도록 설계되었다.
          rpc GetPreferredAllocation(PreferredAllocationRequest) returns (PreferredAllocationResponse) {}
    
          // PreStartContainer는 등록 단계에서 장치 플러그인에 의해 표시되면 각 컨테이너가
      			// 시작되기 전에 호출된다. 장치 플러그인은 장치를 컨테이너에서 사용할 수 있도록 하기 전에
      			// 장치 재설정과 같은 장치별 작업을 실행할 수 있다.
          rpc PreStartContainer(PreStartContainerRequest) returns (PreStartContainerResponse) {}
    }
    
  • 플러그인은 호스트 경로 /var/lib/kubelet/device-plugins/kubelet.sock 에서 유닉스 소켓을 통해 kubelet에 직접 등록한다.

  • 성공적으로 등록하고 나면, 장치 플러그인은 서빙(serving) 모드에서 실행되며, 그 동안 플러그인은 장치 상태를 모니터링하고 장치 상태 변경 시 kubelet에 다시 보고한다. 또한 gRPC 요청 Allocate 를 담당한다. Allocate 하는 동안, 장치 플러그인은 GPU 정리 또는 QRNG 초기화와 같은 장치별 준비를 수행할 수 있다. 작업이 성공하면, 장치 플러그인은 할당된 장치에 접근하기 위한 컨테이너 런타임 구성이 포함된 AllocateResponse 를 반환한다. kubelet은 이 정보를 컨테이너 런타임에 전달한다.

kubelet 재시작 처리

장치 플러그인은 일반적으로 kubelet의 재시작을 감지하고 새로운 kubelet 인스턴스에 자신을 다시 등록할 것으로 기대된다. 현재의 구현에서, 새 kubelet 인스턴스는 시작될 때 /var/lib/kubelet/device-plugins 아래에 있는 모든 기존의 유닉스 소켓을 삭제한다. 장치 플러그인은 유닉스 소켓의 삭제를 모니터링하고 이러한 이벤트가 발생하면 다시 자신을 등록할 수 있다.

장치 플러그인 배포

장치 플러그인을 데몬셋, 노드 운영 체제의 패키지 또는 수동으로 배포할 수 있다.

표준 디렉터리 /var/lib/kubelet/device-plugins 에는 특권을 가진 접근이 필요하므로, 장치 플러그인은 특권을 가진 보안 컨텍스트에서 실행해야 한다. 장치 플러그인을 데몬셋으로 배포하는 경우, 플러그인의 PodSpec에서 /var/lib/kubelet/device-plugins볼륨으로 마운트해야 한다.

데몬셋 접근 방식을 선택하면 쿠버네티스를 사용하여 장치 플러그인의 파드를 노드에 배치하고, 장애 후 데몬 파드를 다시 시작하고, 업그레이드를 자동화할 수 있다.

API 호환성

쿠버네티스 장치 플러그인 지원은 베타 버전이다. 호환되지 않는 방식으로 안정화 전에 API가 변경될 수 있다. 프로젝트로서, 쿠버네티스는 장치 플러그인 개발자에게 다음 사항을 권장한다.

  • 향후 릴리스에서 변경 사항을 확인하자.
  • 이전/이후 버전과의 호환성을 위해 여러 버전의 장치 플러그인 API를 지원한다.

최신 장치 플러그인 API 버전의 쿠버네티스 릴리스로 업그레이드해야 하는 노드에서 DevicePlugins 기능을 활성화하고 장치 플러그인을 실행하는 경우, 이 노드를 업그레이드하기 전에 두 버전을 모두 지원하도록 장치 플러그인을 업그레이드한다. 이 방법을 사용하면 업그레이드 중에 장치 할당이 지속적으로 작동한다.

장치 플러그인 리소스 모니터링

FEATURE STATE: Kubernetes v1.15 [beta]

장치 플러그인에서 제공하는 리소스를 모니터링하려면, 모니터링 에이전트가 노드에서 사용 중인 장치 셋을 검색하고 메트릭과 연관될 컨테이너를 설명하는 메타데이터를 얻을 수 있어야 한다. 장치 모니터링 에이전트에 의해 노출된 프로메테우스 지표는 쿠버네티스 Instrumentation 가이드라인을 따라 pod, namespacecontainer 프로메테우스 레이블을 사용하여 컨테이너를 식별해야 한다.

kubelet은 gRPC 서비스를 제공하여 사용 중인 장치를 검색하고, 이러한 장치에 대한 메타데이터를 제공한다.

// PodResourcesLister는 kubelet에서 제공하는 서비스로, 노드의 포드 및 컨테이너가
// 사용한 노드 리소스에 대한 정보를 제공한다.
service PodResourcesLister {
    rpc List(ListPodResourcesRequest) returns (ListPodResourcesResponse) {}
    rpc GetAllocatableResources(AllocatableResourcesRequest) returns (AllocatableResourcesResponse) {}
}

List 엔드포인트는 실행 중인 파드의 리소스에 대한 정보를 제공하며, 독점적으로 할당된 CPU의 ID, 장치 플러그인에 의해 보고된 장치 ID, 이러한 장치가 할당된 NUMA 노드의 ID와 같은 세부 정보를 함께 제공한다. 또한, NUMA 기반 머신의 경우, 컨테이너를 위해 예약된 메모리와 hugepage에 대한 정보를 포함한다.

// ListPodResourcesResponse는 List 함수가 반환하는 응답이다.
message ListPodResourcesResponse {
    repeated PodResources pod_resources = 1;
}

// PodResources에는 파드에 할당된 노드 리소스에 대한 정보가 포함된다.
message PodResources {
    string name = 1;
    string namespace = 2;
    repeated ContainerResources containers = 3;
}

// ContainerResources는 컨테이너에 할당된 리소스에 대한 정보를 포함한다.
message ContainerResources {
    string name = 1;
    repeated ContainerDevices devices = 2;
    repeated int64 cpu_ids = 3;
    repeated ContainerMemory memory = 4;
}

// ContainerMemory는 컨테이너에 할당된 메모리와 hugepage에 대한 정보를 포함한다.
message ContainerMemory {
    string memory_type = 1;
    uint64 size = 2;
    TopologyInfo topology = 3;
}

// 토폴로지는 리소스의 하드웨어 토폴로지를 설명한다.
message TopologyInfo {
        repeated NUMANode nodes = 1;
}

// NUMA 노드의 NUMA 표현
message NUMANode {
        int64 ID = 1;
}

// ContainerDevices는 컨테이너에 할당된 장치에 대한 정보를 포함한다.
message ContainerDevices {
    string resource_name = 1;
    repeated string device_ids = 2;
    TopologyInfo topology = 3;
}

GetAllocatableResources는 워커 노드에서 처음 사용할 수 있는 리소스에 대한 정보를 제공한다. kubelet이 APIServer로 내보내는 것보다 더 많은 정보를 제공한다.

// AllocatableResourcesResponses에는 kubelet이 알고 있는 모든 장치에 대한 정보가 포함된다.
message AllocatableResourcesResponse {
    repeated ContainerDevices devices = 1;
    repeated int64 cpu_ids = 2;
    repeated ContainerMemory memory = 3;
}

ContainerDevices 는 장치가 어떤 NUMA 셀과 연관되는지를 선언하는 토폴로지 정보를 노출한다. NUMA 셀은 불분명한(opaque) 정수 ID를 사용하여 식별되며, 이 값은 kubelet에 등록할 때 장치 플러그인이 보고하는 것과 일치한다.

gRPC 서비스는 /var/lib/kubelet/pod-resources/kubelet.sock 의 유닉스 소켓을 통해 제공된다. 장치 플러그인 리소스에 대한 모니터링 에이전트는 데몬 또는 데몬셋으로 배포할 수 있다. 표준 디렉터리 /var/lib/kubelet/pod-resources 에는 특권을 가진 접근이 필요하므로, 모니터링 에이전트는 특권을 가진 ​​보안 컨텍스트에서 실행해야 한다. 장치 모니터링 에이전트가 데몬셋으로 실행 중인 경우, 해당 장치 모니터링 에이전트의 PodSpec에서 /var/lib/kubelet/pod-resources볼륨으로 마운트해야 한다.

PodResourcesLister service 를 지원하려면 KubeletPodResources 기능 게이트를 활성화해야 한다. 이것은 쿠버네티스 1.15부터 기본으로 활성화되어 있으며, 쿠버네티스 1.20부터는 v1 상태이다.

토폴로지 관리자로 장치 플러그인 통합

FEATURE STATE: Kubernetes v1.18 [beta]

토폴로지 관리자는 Kubelet 컴포넌트로, 리소스를 토폴로지 정렬 방식으로 조정할 수 있다. 이를 위해, 장치 플러그인 API가 TopologyInfo 구조체를 포함하도록 확장되었다.

message TopologyInfo {
	repeated NUMANode nodes = 1;
}

message NUMANode {
    int64 ID = 1;
}

토폴로지 관리자를 활용하려는 장치 플러그인은 장치 ID 및 장치의 정상 상태와 함께 장치 등록의 일부로 채워진 TopologyInfo 구조체를 다시 보낼 수 있다. 그런 다음 장치 관리자는 이 정보를 사용하여 토폴로지 관리자와 상의하고 리소스 할당 결정을 내린다.

TopologyInfonil(기본값) 또는 NUMA 노드 목록인 nodes 필드를 지원한다. 이를 통해 NUMA 노드에 걸쳐있을 수 있는 장치 플러그인을 게시할 수 있다.

장치 플러그인으로 장치에 대해 채워진 TopologyInfo 구조체의 예는 다음과 같다.

pluginapi.Device{ID: "25102017", Health: pluginapi.Healthy, Topology:&pluginapi.TopologyInfo{Nodes: []*pluginapi.NUMANode{&pluginapi.NUMANode{ID: 0,},}}}

장치 플러그인 예시

다음은 장치 플러그인 구현의 예이다.

다음 내용