클러스터 운영

클러스터를 운영하기 위한 공통 태스크를 배운다.

1 - kubeadm으로 관리하기

1.1 - kubeadm을 사용한 인증서 관리

기능 상태: Kubernetes v1.15 [stable]

kubeadm으로 생성된 클라이언트 인증서는 1년 후에 만료된다. 이 페이지는 kubeadm으로 인증서 갱신을 관리하는 방법을 설명하며, kubeadm 인증서 관리와 관련된 다른 작업에 대해서도 다룬다.

시작하기 전에

쿠버네티스의 PKI 인증서와 요구 조건에 익숙해야 한다.

사용자 정의 인증서 사용

기본적으로, kubeadm은 클러스터를 실행하는 데 필요한 모든 인증서를 생성한다. 사용자는 자체 인증서를 제공하여 이 동작을 무시할 수 있다.

이렇게 하려면, --cert-dir 플래그 또는 kubeadm ClusterConfigurationcertificatesDir 필드에 지정된 디렉터리에 배치해야 한다. 기본적으로 /etc/kubernetes/pki 이다.

kubeadm init 을 실행하기 전에 지정된 인증서와 개인 키(private key) 쌍이 존재하면, kubeadm은 이를 덮어 쓰지 않는다. 이는 예를 들어, 기존 CA를 /etc/kubernetes/pki/ca.crt/etc/kubernetes/pki/ca.key 에 복사할 수 있고, kubeadm은 이 CA를 사용하여 나머지 인증서에 서명한다는 걸 의미한다.

외부 CA 모드

ca.key 파일이 아닌 ca.crt 파일만 제공할 수도 있다(이는 다른 인증서 쌍이 아닌 루트 CA 파일에만 사용 가능함). 다른 모든 인증서와 kubeconfig 파일이 있으면, kubeadm은 이 조건을 인식하고 "외부 CA" 모드를 활성화한다. kubeadm은 디스크에 CA 키없이 진행한다.

대신, --controllers=csrsigner 사용하여 controller-manager를 독립적으로 실행하고 CA 인증서와 키를 가리킨다.

PKI 인증서와 요구 조건은 외부 CA를 사용하도록 클러스터 설정에 대한 지침을 포함한다.

인증서 만료 확인

check-expiration 하위 명령을 사용하여 인증서가 만료되는 시기를 확인할 수 있다.

kubeadm certs check-expiration

출력 결과는 다음과 비슷하다.

CERTIFICATE                EXPIRES                  RESIDUAL TIME   CERTIFICATE AUTHORITY   EXTERNALLY MANAGED
admin.conf                 Dec 30, 2020 23:36 UTC   364d                                    no
apiserver                  Dec 30, 2020 23:36 UTC   364d            ca                      no
apiserver-etcd-client      Dec 30, 2020 23:36 UTC   364d            etcd-ca                 no
apiserver-kubelet-client   Dec 30, 2020 23:36 UTC   364d            ca                      no
controller-manager.conf    Dec 30, 2020 23:36 UTC   364d                                    no
etcd-healthcheck-client    Dec 30, 2020 23:36 UTC   364d            etcd-ca                 no
etcd-peer                  Dec 30, 2020 23:36 UTC   364d            etcd-ca                 no
etcd-server                Dec 30, 2020 23:36 UTC   364d            etcd-ca                 no
front-proxy-client         Dec 30, 2020 23:36 UTC   364d            front-proxy-ca          no
scheduler.conf             Dec 30, 2020 23:36 UTC   364d                                    no

CERTIFICATE AUTHORITY   EXPIRES                  RESIDUAL TIME   EXTERNALLY MANAGED
ca                      Dec 28, 2029 23:36 UTC   9y              no
etcd-ca                 Dec 28, 2029 23:36 UTC   9y              no
front-proxy-ca          Dec 28, 2029 23:36 UTC   9y              no

이 명령은 /etc/kubernetes/pki 폴더의 클라이언트 인증서와 kubeadm이 사용하는 KUBECONFIG 파일(admin.conf, controller-manager.confscheduler.conf)에 포함된 클라이언트 인증서의 만료/잔여 기간을 표시한다.

또한, kubeadm은 인증서가 외부에서 관리되는지를 사용자에게 알린다. 이 경우 사용자는 수동으로 또는 다른 도구를 사용해서 인증서 갱신 관리를 해야 한다.

자동 인증서 갱신

kubeadm은 컨트롤 플레인 업그레이드 동안 모든 인증서를 갱신한다.

이 기능은 가장 간단한 유스케이스를 해결하기 위해 설계되었다. 인증서 갱신에 대해 특별한 요구 사항이 없고 쿠버네티스 버전 업그레이드를 정기적으로(매 1년 이내 업그레이드 수행) 수행하는 경우, kubeadm은 클러스터를 최신 상태로 유지하고 합리적으로 보안을 유지한다.

인증서 갱신에 대해 보다 복잡한 요구 사항이 있는 경우, --certificate-renewal=falsekubeadm upgrade apply 또는 kubeadm upgrade node 와 함께 사용하여 기본 동작이 수행되지 않도록 할 수 있다.

수동 인증서 갱신

kubeadm certs renew 명령을 사용하여 언제든지 인증서를 수동으로 갱신할 수 있다.

이 명령은 /etc/kubernetes/pki 에 저장된 CA(또는 프론트 프록시 CA) 인증서와 키를 사용하여 갱신을 수행한다.

명령을 실행한 후에는 컨트롤 플레인 파드를 재시작해야 한다. 이는 현재 일부 구성 요소 및 인증서에 대해 인증서를 동적으로 다시 로드하는 것이 지원되지 않기 때문이다. 스태틱(static) 파드는 API 서버가 아닌 로컬 kubelet에서 관리되므로 kubectl을 사용하여 삭제 및 재시작할 수 없다. 스태틱 파드를 다시 시작하려면 /etc/kubernetes/manifests/에서 매니페스트 파일을 일시적으로 제거하고 20초를 기다리면 된다 (KubeletConfiguration structfileCheckFrequency 값을 참고한다). 파드가 매니페스트 디렉터리에 더 이상 없는 경우 kubelet은 파드를 종료한다. 그런 다음 파일을 다시 이동할 수 있으며 또 다른 fileCheckFrequency 기간이 지나면, kubelet은 파드를 생성하고 구성 요소에 대한 인증서 갱신을 완료할 수 있다.

kubeadm certs renew 는 다음의 옵션을 제공한다.

쿠버네티스 인증서는 일반적으로 1년 후 만료일에 도달한다.

  • --csr-only 는 실제로 인증서를 갱신하지 않고 인증서 서명 요청을 생성하여 외부 CA로 인증서를 갱신하는 데 사용할 수 있다. 자세한 내용은 다음 단락을 참고한다.

  • 모든 인증서 대신 단일 인증서를 갱신할 수도 있다.

쿠버네티스 인증서 API를 사용하여 인증서 갱신

이 섹션에서는 쿠버네티스 인증서 API를 사용하여 수동 인증서 갱신을 실행하는 방법에 대한 자세한 정보를 제공한다.

서명자 설정

쿠버네티스 인증 기관(Certificate Authority)은 기본적으로 작동하지 않는다. cert-manager와 같은 외부 서명자를 설정하거나, 빌트인 서명자를 사용할 수 있다.

빌트인 서명자는 kube-controller-manager의 일부이다.

빌트인 서명자를 활성화하려면, --cluster-signing-cert-file--cluster-signing-key-file 플래그를 전달해야 한다.

새 클러스터를 생성하는 경우, kubeadm 구성 파일을 사용할 수 있다.

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
controllerManager:
  extraArgs:
    cluster-signing-cert-file: /etc/kubernetes/pki/ca.crt
    cluster-signing-key-file: /etc/kubernetes/pki/ca.key

인증서 서명 요청(CSR) 생성

쿠버네티스 API로 CSR을 작성하려면 CertificateSigningRequest 생성을 본다.

외부 CA로 인증서 갱신

이 섹션에서는 외부 CA를 사용하여 수동 인증서 갱신을 실행하는 방법에 대한 자세한 정보를 제공한다.

외부 CA와 보다 효과적으로 통합하기 위해 kubeadm은 인증서 서명 요청(CSR)을 생성할 수도 있다. CSR은 클라이언트의 서명된 인증서에 대한 CA 요청을 나타낸다. kubeadm 관점에서, 일반적으로 온-디스크(on-disk) CA에 의해 서명되는 모든 인증서는 CSR로 생성될 수 있다. 그러나 CA는 CSR로 생성될 수 없다.

인증서 서명 요청(CSR) 생성

kubeadm certs renew --csr-only 로 인증서 서명 요청을 만들 수 있다.

CSR과 함께 제공되는 개인 키가 모두 출력된다. --csr-dir 로 사용할 디텍터리를 전달하여 지정된 위치로 CSR을 출력할 수 있다. --csr-dir 을 지정하지 않으면, 기본 인증서 디렉터리(/etc/kubernetes/pki)가 사용된다.

kubeadm certs renew --csr-only 로 인증서를 갱신할 수 있다. kubeadm init 과 마찬가지로 출력 디렉터리를 --csr-dir 플래그로 지정할 수 있다.

CSR에는 인증서 이름, 도메인 및 IP가 포함되지만, 용도를 지정하지는 않는다. 인증서를 발행할 때 올바른 인증서 용도를 지정하는 것은 CA의 책임이다.

선호하는 방법으로 인증서에 서명한 후, 인증서와 개인 키를 PKI 디렉터리(기본적으로 /etc/kubernetes/pki)에 복사해야 한다.

인증 기관(CA) 순환(rotation)

Kubeadm은 CA 인증서의 순환이나 교체 기능을 기본적으로 지원하지 않는다.

CA의 수동 순환이나 교체에 대한 보다 상세한 정보는 CA 인증서 수동 순환 문서를 참조한다.

서명된 kubelet 인증서 활성화하기

기본적으로 kubeadm에 의해서 배포된 kubelet 인증서는 자가 서명된(self-signed) 것이다. 이것은 metrics-server와 같은 외부 서비스의 kubelet에 대한 연결은 TLS로 보안되지 않음을 의미한다.

제대로 서명된 인증서를 얻기 위해서 신규 kubeadm 클러스터의 kubelet을 구성하려면 다음의 최소 구성을 kubeadm init 에 전달해야 한다.

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
serverTLSBootstrap: true

만약 이미 클러스터를 생성했다면 다음을 따라 이를 조정해야 한다.

  • kube-system 네임스페이스에서 kubelet-config-1.30 컨피그맵을 찾아서 수정한다. 해당 컨피그맵에는 kubelet 키가 KubeletConfiguration 문서를 값으로 가진다. serverTLSBootstrap: true 가 되도록 KubeletConfiguration 문서를 수정한다.
  • 각 노드에서, serverTLSBootstrap: true 필드를 /var/lib/kubelet/config.yaml 에 추가한다. 그리고 systemctl restart kubelet 로 kubelet을 재시작한다.

serverTLSBootstrap: true 필드는 kubelet 인증서를 이용한 부트스트랩을 certificates.k8s.io API에 요청함으로써 활성화할 것이다. 한 가지 알려진 제약은 이 인증서들에 대한 CSR(인증서 서명 요청)들이 kube-controller-manager - kubernetes.io/kubelet-serving의 기본 서명자(default signer)에 의해서 자동으로 승인될 수 없다는 점이다. 이것은 사용자나 제 3의 컨트롤러의 액션을 필요로 할 것이다.

이 CSR들은 다음을 통해 볼 수 있다.

kubectl get csr
NAME        AGE     SIGNERNAME                        REQUESTOR                      CONDITION
csr-9wvgt   112s    kubernetes.io/kubelet-serving     system:node:worker-1           Pending
csr-lz97v   1m58s   kubernetes.io/kubelet-serving     system:node:control-plane-1    Pending

이를 승인하기 위해서는 다음을 수행한다.

kubectl certificate approve <CSR-name>

기본적으로, 이 인증서는 1년 후에 만기될 것이다. Kubeadm은 KubeletConfiguration 필드의 rotateCertificatestrue 로 설정한다. 이것은 만기가 다가오면 인증서를 위한 신규 CSR 세트가 생성되는 것을 의미하며, 해당 순환(rotation)을 완료하기 위해서는 승인이 되어야 한다는 것을 의미한다. 더 상세한 이해를 위해서는 인증서 순환를 확인한다.

만약 이 CSR들의 자동 승인을 위한 솔루션을 찾고 있다면 클라우드 제공자와 연락하여 대역 외 메커니즘(out of band mechanism)을 통해 노드의 신분을 검증할 수 있는 CSR 서명자를 가지고 있는지 문의하는 것을 추천한다.

써드파티 커스텀 컨트롤러도 사용될 수 있다.

이러한 컨트롤러는 CSR의 CommonName과 요청된 IPs 및 도메인 네임을 모두 검증하지 않는 한, 보안이 되는 메커니즘이 아니다. 이것을 통해 악의적 행위자가 kubelet 인증서(클라이언트 인증)를 사용하여 아무 IP나 도메인 네임에 대해 인증서를 요청하는 CSR의 생성을 방지할 수 있을 것이다.

추가 사용자를 위한 kubeconfig 파일 생성하기

클러스터 생성 과정에서, kubeadm은 Subject: O = system:masters, CN = kubernetes-admin 값이 설정되도록 admin.conf의 인증서에 서명한다. system:masters는 인증 계층(예: RBAC)을 우회하는 획기적인 슈퍼유저 그룹이다. admin.conf를 추가 사용자와 공유하는 것은 권장하지 않는다!

대신, kubeadm kubeconfig user 명령어를 사용하여 추가 사용자를 위한 kubeconfig 파일을 생성할 수 있다. 이 명령어는 명령줄 플래그와 kubeadm 환경 설정 옵션을 모두 인식한다. 생성된 kubeconfig는 stdout으로 출력되며, kubeadm kubeconfig user ... > somefile.conf 명령어를 사용하여 파일에 기록될 수 있다.

다음은 --config 플래그와 함께 사용될 수 있는 환경 설정 파일 예시이다.

# example.yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
# kubeconfig에서 타겟 "cluster"로 사용될 것이다.
clusterName: "kubernetes"
# kubeconfig에서 클러스터의 "server"(IP 또는 DNS 이름)로 사용될 것이다.
controlPlaneEndpoint: "some-dns-address:6443"
# 클러스터 CA 키 및 인증서가 이 로컬 디렉토리에서 로드될 것이다.
certificatesDir: "/etc/kubernetes/pki"

이러한 항목들이 사용하고자 하는 클러스터의 상세 사항과 일치하는지 확인한다. 기존 클러스터의 환경 설정을 보려면 다음 명령을 사용한다.

kubectl get cm kubeadm-config -n kube-system -o=jsonpath="{.data.ClusterConfiguration}"

다음 예시는 appdevs 그룹의 새 사용자 johndoe를 위해 24시간동안 유효한 인증서와 함께 kubeconfig 파일을 생성할 것이다.

kubeadm kubeconfig user --config example.yaml --org appdevs --client-name johndoe --validity-period 24h

다음 예시는 1주일간 유효한 관리자 크리덴셜을 갖는 kubeconfig 파일을 생성할 것이다.

kubeadm kubeconfig user --config example.yaml --client-name admin --validity-period 168h

1.2 - kubeadm 클러스터 업그레이드

이 페이지는 kubeadm으로 생성된 쿠버네티스 클러스터를 1.29.x 버전에서 1.30.x 버전으로, 1.30.x 버전에서 1.30.y(여기서 y > x) 버전으로 업그레이드하는 방법을 설명한다. 업그레이드가 지원되지 않는 경우 마이너 버전을 건너뛴다. 더 자세한 정보는 버전 차이(skew) 정책을 참고한다.

이전 버전의 kubeadm을 사용하여 생성된 클러스터 업그레이드에 대한 정보를 보려면, 이 페이지 대신 다음의 페이지들을 참고한다.

추상적인 업그레이드 작업 절차는 다음과 같다.

  1. 기본 컨트롤 플레인 노드를 업그레이드한다.
  2. 추가 컨트롤 플레인 노드를 업그레이드한다.
  3. 워커(worker) 노드를 업그레이드한다.

시작하기 전에

  • 릴리스 노트를 주의 깊게 읽어야 한다.
  • 클러스터는 정적 컨트롤 플레인 및 etcd 파드 또는 외부 etcd를 사용해야 한다.
  • 데이터베이스에 저장된 앱-레벨 상태와 같은 중요한 컴포넌트를 반드시 백업한다. kubeadm upgrade 는 워크로드에 영향을 미치지 않고, 쿠버네티스 내부의 컴포넌트만 다루지만, 백업은 항상 모범 사례일 정도로 중요하다.
  • 스왑을 비활성화해야 한다.

추가 정보

  • 아래의 지침은 업그레이드 과정 중 언제 각 노드를 드레인해야 하는지를 제시한다. kubelet에 대해 마이너 버전 업그레이드를 하는 경우, 먼저 업그레이드할 노드(들)을 드레인해야 한다. 컨트롤 플레인 노드의 경우, CoreDNS 파드 또는 다른 중요한 워크로드를 실행 중일 수 있다. 더 많은 정보는 노드 드레인하기를 참조한다.
  • 컨테이너 사양 해시 값이 변경되므로, 업그레이드 후 모든 컨테이너가 다시 시작된다.
  • kubelet이 업그레이드된 이후 kubelet 서비스가 성공적으로 재시작되었는지 확인하려면, systemctl status kubelet 명령을 실행하거나, journalctl -xeu kubelet 명령을 실행하여 서비스 로그를 확인할 수 있다.
  • 클러스터를 재구성하기 위해 kubeadm upgrade 시에 kubeadm 구성 API 종류를 명시하여 --config 플래그를 사용하는 것은 추천하지 않으며 예상치 못한 결과를 초래할 수 있다. 대신 kubeadm 클러스터 재구성하기를 참조한다.

업그레이드할 버전 결정

OS 패키지 관리자를 사용하여 쿠버네티스의 최신 패치 릴리스 버전(1.30)을 찾는다.

apt update
apt-cache madison kubeadm
# 목록에서 최신 버전(1.30)을 찾는다
# 1.30.x-00과 같아야 한다. 여기서 x는 최신 패치이다.

yum list --showduplicates kubeadm --disableexcludes=kubernetes
# 목록에서 최신 버전(1.30)을 찾는다
# 1.30.x-0과 같아야 한다. 여기서 x는 최신 패치이다.

컨트롤 플레인 노드 업그레이드

컨트롤 플레인 노드의 업그레이드 절차는 한 번에 한 노드씩 실행해야 한다. 먼저 업그레이드할 컨트롤 플레인 노드를 선택한다. /etc/kubernetes/admin.conf 파일이 있어야 한다.

"kubeadm upgrade" 호출

첫 번째 컨트롤 플레인 노드의 경우

  • kubeadm 업그레이드

     # 1.30.x-00에서 x를 최신 패치 버전으로 바꾼다.
     apt-mark unhold kubeadm && \
     apt-get update && apt-get install -y kubeadm=1.30.x-00 && \
     apt-mark hold kubeadm
    

    # 1.30.x-0에서 x를 최신 패치 버전으로 바꾼다.
    yum install -y kubeadm-1.30.x-0 --disableexcludes=kubernetes
    

  • 다운로드하려는 버전이 잘 받아졌는지 확인한다.

    kubeadm version
    
  • 업그레이드 계획을 확인한다.

    kubeadm upgrade plan
    

    이 명령은 클러스터를 업그레이드할 수 있는지를 확인하고, 업그레이드할 수 있는 버전을 가져온다. 또한 컴포넌트 구성 버전 상태가 있는 표를 보여준다.

  • 업그레이드할 버전을 선택하고, 적절한 명령을 실행한다. 예를 들면 다음과 같다.

    # 이 업그레이드를 위해 선택한 패치 버전으로 x를 바꾼다.
    sudo kubeadm upgrade apply v1.30.x
    

    명령이 완료되면 다음을 확인해야 한다.

    [upgrade/successful] SUCCESS! Your cluster was upgraded to "v1.30.x". Enjoy!
    
    [upgrade/kubelet] Now that your control plane is upgraded, please proceed with upgrading your kubelets if you haven't already done so.
    
  • CNI 제공자 플러그인을 수동으로 업그레이드한다.

    CNI(컨테이너 네트워크 인터페이스) 제공자는 자체 업그레이드 지침을 따를 수 있다. 애드온 페이지에서 사용하는 CNI 제공자를 찾고 추가 업그레이드 단계가 필요한지 여부를 확인한다.

    CNI 제공자가 데몬셋(DaemonSet)으로 실행되는 경우 추가 컨트롤 플레인 노드에는 이 단계가 필요하지 않다.

다른 컨트롤 플레인 노드의 경우

첫 번째 컨트롤 플레인 노드와 동일하지만 다음을 사용한다.

sudo kubeadm upgrade node

아래 명령 대신 위의 명령을 사용한다.

sudo kubeadm upgrade apply

kubeadm upgrade plan 을 호출하고 CNI 공급자 플러그인을 업그레이드할 필요가 없다.

노드 드레인

  • 스케줄 불가능(unschedulable)으로 표시하고 워크로드를 축출하여 유지 보수할 노드를 준비한다.

    # <node-to-drain>을 드레인하는 노드의 이름으로 바꾼다.
    kubectl drain <node-to-drain> --ignore-daemonsets
    

kubelet과 kubectl 업그레이드

  • 모든 컨트롤 플레인 노드에서 kubelet 및 kubectl을 업그레이드한다.

    # replace x in 1.30.x-00의 x를 최신 패치 버전으로 바꾼다
    apt-mark unhold kubelet kubectl && \
    apt-get update && apt-get install -y kubelet=1.30.x-00 kubectl=1.30.x-00 && \
    apt-mark hold kubelet kubectl
    

    # 1.30.x-0에서 x를 최신 패치 버전으로 바꾼다
    yum install -y kubelet-1.30.x-0 kubectl-1.30.x-0 --disableexcludes=kubernetes
    

  • kubelet을 다시 시작한다.

    sudo systemctl daemon-reload
    sudo systemctl restart kubelet
    

노드 uncordon

  • 노드를 스케줄 가능(schedulable)으로 표시하여 노드를 다시 온라인 상태로 전환한다.

    # <node-to-uncordon>을 드레인하려는 노드의 이름으로 바꾼다.
    kubectl uncordon <node-to-uncordon>
    

워커 노드 업그레이드

워커 노드의 업그레이드 절차는 워크로드를 실행하는 데 필요한 최소 용량을 보장하면서, 한 번에 하나의 노드 또는 한 번에 몇 개의 노드로 실행해야 한다.

kubeadm 업그레이드

  • 모든 워커 노드에서 kubeadm을 업그레이드한다.

    # 1.30.x-00의 x를 최신 패치 버전으로 바꾼다
    apt-mark unhold kubeadm && \
    apt-get update && apt-get install -y kubeadm=1.30.x-00 && \
    apt-mark hold kubeadm
    

    # 1.30.x-0에서 x를 최신 패치 버전으로 바꾼다
    yum install -y kubeadm-1.30.x-0 --disableexcludes=kubernetes
    

"kubeadm upgrade" 호출

  • 워커 노드의 경우 로컬 kubelet 구성을 업그레이드한다.

    sudo kubeadm upgrade node
    

노드 드레인

  • 스케줄 불가능(unschedulable)으로 표시하고 워크로드를 축출하여 유지 보수할 노드를 준비한다.

    # <node-to-drain>을 드레인하려는 노드 이름으로 바꾼다.
    kubectl drain <node-to-drain> --ignore-daemonsets
    

kubelet과 kubectl 업그레이드

  • kubelet 및 kubectl을 업그레이드한다.

    # 1.30.x-00의 x를 최신 패치 버전으로 바꾼다
    apt-mark unhold kubelet kubectl && \
    apt-get update && apt-get install -y kubelet=1.30.x-00 kubectl=1.30.x-00 && \
    apt-mark hold kubelet kubectl
    

    # 1.30.x-0에서 x를 최신 패치 버전으로 바꾼다
    yum install -y kubelet-1.30.x-0 kubectl-1.30.x-0 --disableexcludes=kubernetes
    

  • kubelet을 다시 시작한다.

    sudo systemctl daemon-reload
    sudo systemctl restart kubelet
    

노드에 적용된 cordon 해제

  • 스케줄 가능(schedulable)으로 표시하여 노드를 다시 온라인 상태로 만든다.
# <node-to-uncordon>을 노드의 이름으로 바꾼다.
kubectl uncordon <node-to-uncordon>

클러스터 상태 확인

모든 노드에서 kubelet을 업그레이드한 후 kubectl이 클러스터에 접근할 수 있는 곳에서 다음의 명령을 실행하여 모든 노드를 다시 사용할 수 있는지 확인한다.

kubectl get nodes

모든 노드에 대해 STATUS 열에 Ready 가 표시되어야 하고, 버전 번호가 업데이트되어 있어야 한다.

장애 상태에서의 복구

예를 들어 kubeadm upgrade 를 실행하는 중에 예기치 못한 종료로 인해 업그레이드가 실패하고 롤백하지 않는다면, kubeadm upgrade 를 다시 실행할 수 있다. 이 명령은 멱등성을 보장하며 결국 실제 상태가 선언한 의도한 상태인지 확인한다.

잘못된 상태에서 복구하기 위해, 클러스터가 실행 중인 버전을 변경하지 않고 kubeadm upgrade apply --force 를 실행할 수도 있다.

업그레이드하는 동안 kubeadm은 /etc/kubernetes/tmp 아래에 다음과 같은 백업 폴더를 작성한다.

  • kubeadm-backup-etcd-<date>-<time>
  • kubeadm-backup-manifests-<date>-<time>

kubeadm-backup-etcd 는 컨트롤 플레인 노드에 대한 로컬 etcd 멤버 데이터의 백업을 포함한다. etcd 업그레이드가 실패하고 자동 롤백이 작동하지 않으면, 이 폴더의 내용을 /var/lib/etcd 에서 수동으로 복원할 수 있다. 외부 etcd를 사용하는 경우 이 백업 폴더는 비어있다.

kubeadm-backup-manifests 는 컨트롤 플레인 노드에 대한 정적 파드 매니페스트 파일의 백업을 포함한다. 업그레이드가 실패하고 자동 롤백이 작동하지 않으면, 이 폴더의 내용을 /etc/kubernetes/manifests 에서 수동으로 복원할 수 있다. 어떤 이유로 특정 컴포넌트의 업그레이드 전 매니페스트 파일과 업그레이드 후 매니페스트 파일 간에 차이가 없는 경우, 백업 파일은 기록되지 않는다.

작동 원리

kubeadm upgrade apply 는 다음을 수행한다.

  • 클러스터가 업그레이드 가능한 상태인지 확인한다.
    • API 서버에 접근할 수 있다
    • 모든 노드가 Ready 상태에 있다
    • 컨트롤 플레인이 정상적으로 동작한다
  • 버전 차이(skew) 정책을 적용한다.
  • 컨트롤 플레인 이미지가 사용 가능한지 또는 머신으로 가져올 수 있는지 확인한다.
  • 컴포넌트 구성에 버전 업그레이드가 필요한 경우 대체 구성을 생성하거나 사용자가 제공한 것으로 덮어 쓰기한다.
  • 컨트롤 플레인 컴포넌트 또는 롤백 중 하나라도 나타나지 않으면 업그레이드한다.
  • 새로운 CoreDNSkube-proxy 매니페스트를 적용하고 필요한 모든 RBAC 규칙이 생성되도록 한다.
  • API 서버의 새 인증서와 키 파일을 작성하고 180일 후에 만료될 경우 이전 파일을 백업한다.

kubeadm upgrade node 는 추가 컨트롤 플레인 노드에서 다음을 수행한다.

  • 클러스터에서 kubeadm ClusterConfiguration 을 가져온다.
  • 선택적으로 kube-apiserver 인증서를 백업한다.
  • 컨트롤 플레인 컴포넌트에 대한 정적 파드 매니페스트를 업그레이드한다.
  • 이 노드의 kubelet 구성을 업그레이드한다.

kubeadm upgrade node 는 워커 노드에서 다음을 수행한다.

  • 클러스터에서 kubeadm ClusterConfiguration 을 가져온다.
  • 이 노드의 kubelet 구성을 업그레이드한다.

1.3 - 윈도우 노드 업그레이드

기능 상태: Kubernetes v1.18 [beta]

이 페이지는 kubeadm으로 생성된 윈도우 노드를 업그레이드하는 방법을 설명한다.

시작하기 전에

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

쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: 1.17. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

워커 노드 업그레이드

kubeadm 업그레이드

  1. 윈도우 노드에서, kubeadm을 업그레이드한다.

    # 1.30.0을 사용 중인 쿠버네티스 버전으로 변경한다.
    curl.exe -Lo <kubeadm.exe을 저장할 경로> "https://dl.k8s.io/v1.30.0/bin/windows/amd64/kubeadm.exe"
    

노드 드레인

  1. 쿠버네티스 API에 접근할 수 있는 머신에서, 스케줄 불가능한 것으로 표시하고 워크로드를 축출하여 유지 보수할 노드를 준비한다.

    # <node-to-drain>을 드레이닝하려는 노드 이름으로 바꾼다
    kubectl drain <node-to-drain> --ignore-daemonsets
    

    다음과 비슷한 출력이 표시되어야 한다.

    node/ip-172-31-85-18 cordoned
    node/ip-172-31-85-18 drained
    

kubelet 구성 업그레이드

  1. 윈도우 노드에서, 다음의 명령을 호출하여 새 kubelet 구성을 동기화한다.

    kubeadm upgrade node
    

kubelet 및 kube-proxy 업그레이드

  1. 윈도우 노드에서, kubelet을 업그레이드하고 다시 시작한다.

    stop-service kubelet
    curl.exe -Lo <kubelet.exe을 저장할 경로> "https://dl.k8s.io/v1.30.0/bin/windows/amd64/kubelet.exe"
    restart-service kubelet
    
  2. 윈도우 노드에서, kube-proxy를 업그레이드하고 다시 시작한다.

    stop-service kube-proxy
    curl.exe -Lo <kube-proxy.exe을 저장할 경로> "https://dl.k8s.io/v1.30.0/bin/windows/amd64/kube-proxy.exe"
    restart-service kube-proxy
    

노드에 적용된 cordon 해제

  1. 쿠버네티스 API에 접근할 수 있는 머신에서, 스케줄 가능으로 표시하여 노드를 다시 온라인으로 가져온다.

    # <node-to-drain>을 노드의 이름으로 바꾼다
    kubectl uncordon <node-to-drain>
    

1.4 - 리눅스 노드 업그레이드

이 페이지는 kubeadm으로 생성된 리눅스 노드를 업그레이드하는 방법을 설명한다.

시작하기 전에

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

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

워커 노드 업그레이드

kubeadm 업그레이드

kubeadm을 업그레이드한다.

# 1.30.x-00 에서 x 에 최신 버전을 넣는다.
apt-mark unhold kubeadm && \
apt-get update && apt-get install -y kubeadm=1.30.x-00 && \
apt-mark hold kubeadm

# 1.30.x-00 에서 x 에 최신 버전을 넣는다.
yum install -y kubeadm-1.30.x-0 --disableexcludes=kubernetes

"kubeadm upgrade" 호출

  • 워커 노드의 경우 로컬 kubelet 구성을 업그레이드한다.

    sudo kubeadm upgrade node
    

노드 드레인

  • 노드를 스케줄 불가능한 것으로 표시하고 워크로드를 축출하여 유지 보수할 노드를 준비한다.

    # <node-to-drain> 에 드레인하려는 노드의 이름을 넣는다.
    kubectl drain <node-to-drain> --ignore-daemonsets
    

kubelet과 kubectl 업그레이드

  • kubelet과 kubectl 업그레이드.

    # 1.30.x-00 에서 x 에 최신 버전을 넣는다.
    apt-mark unhold kubelet kubectl && \
    apt-get update && apt-get install -y kubelet=1.30.x-00 kubectl=1.30.x-00 && \
    apt-mark hold kubelet kubectl
    

    # 1.30.x-00 에서 x 에 최신 버전을 넣는다.
    yum install -y kubelet-1.30.x-0 kubectl-1.30.x-0 --disableexcludes=kubernetes
    

  • kubelet을 재시작한다.

    sudo systemctl daemon-reload
    sudo systemctl restart kubelet
    

노드에 적용된 cordon 해제

  • 스케줄 가능으로 표시하여 노드를 다시 온라인으로 가져온다.

    # <node-to-uncordon> 에 노드의 이름을 넣는다.
    kubectl uncordon <node-to-uncordon>
    

다음 내용

2 - 도커심으로부터 마이그레이션

이 섹션은 도커심에서 다른 컨테이너 런타임으로 마이그레이션할 때에 알아야 할 정보를 제공한다.

쿠버네티스 1.20에서의 도커심 사용 중단(deprecation) 발표 이후, 이것이 다양한 워크로드와 쿠버네티스 설치에 어떻게 영향을 미칠지에 대한 질문이 많았다. 도커심 제거 FAQ는 관련된 문제를 더 잘 이해할 수 있도록 도움을 준다.

도커심은 쿠버네티스 릴리스 v1.24부터 제거되었다. 컨테이너 런타임으로 도커 엔진을 통한 도커심을 사용하는 상황에서 v1.24로 업그레이드하려는 경우, 다른 런타임으로 마이그레이션하거나 다른 방법을 찾아 도커 엔진 지원을 받는 것이 좋다. 선택 가능한 옵션은 컨테이너 런타임 섹션에서 확인한다. 마이그레이션 중 문제를 마주한다면 문제를 보고하면 좋다. 이를 통해 문제를 시기적절하게 해결할 수 있으며, 클러스터도 도커심 제거에 대비할 수 있다.

클러스터는 두 종류 이상의 노드들을 포함할 수 있지만 이는 일반적인 구성은 아니다.

다음 작업을 통해 마이그레이션을 수행할 수 있다.

다음 내용

  • 도커심의 사용 중단 및 제거에 대한 논의를 추적하는 깃허브 이슈가 있다.
  • 도커심에서 마이그레이션하는 것에 관한 결함이나 다른 기술적 문제를 발견한다면, 쿠버네티스 프로젝트에 이슈를 남길 수 있다.

2.1 - 도커 엔진 노드를 도커심에서 cri-dockerd로 마이그레이션하기

이 페이지는 도커 엔진 노드가 도커심 대신 cri-dockerd를 사용하도록 마이그레이션하는 방법을 보여 준다. 다음 시나리오에서는 아래 단계를 따라야 한다.

  • 도커심 사용은 중단하고 싶지만, 쿠버네티스의 컨테이너 실행에는 여전히 도커 엔진을 사용하기를 원하는 경우
  • 쿠버네티스 버전 v1.30로 업그레이드를 원하고 기존 클러스터가 도커심을 사용하는 경우. 이러한 경우에는 도커심을 다른 것으로 대체해야 하며 cri-dockerd도 선택지 중 하나이다.

도커심 제거에 관하여 더 배우려면, FAQ page를 읽어보자.

cri-dockerd란 무엇인가?

쿠버네티스 1.23 이하에서는 도커심 이라는 이름의 쿠버네티스 내장 구성요소를 사용하여 도커 엔진을 쿠버네티스 컨테이너 런타임으로 사용할 수 있었다. 도커심 구성 요소는 쿠버네티스 1.24 릴리스에서 제거되었지만, 대신 서드 파티 대체제 cri-dockerd를 사용할 수 있다. cri-dockerd 어댑터를 사용하면 컨테이너 런타임 인터페이스(Container runtime interface, CRI)를 통해 도커 엔진을 사용할 수 있다.

컨테이너 런타임으로 도커 엔진을 계속 사용할 수 있도록 cri-dockerd로 마이그레이션하려는 경우 영향을 받는 각각의 노드에 아래 내용을 진행해야 한다.

  1. cri-dockerd를 설치한다.
  2. 노드를 통제(cordon)하고 비운다(drain).
  3. cri-dockerd를 사용하도록 kubelet를 설정한다.
  4. kubelet을 재시작한다.
  5. 노드가 정상(healthy)인지 확인한다.

중요하지 않은(non-critical) 노드에서 먼저 테스트한다.

cri-dockerd로 마이그레이션하려는 각 노드에 대해 아래 단계를 수행해야 한다.

시작하기 전에

노드의 통제(Cordon)와 비우기(drain)

  1. 새로운 파드를 노드에 스케줄링하는 것을 막기 위해 노드를 통제한다.

    kubectl cordon <NODE_NAME>
    

    <NODE_NAME> 부분에 노드의 이름을 입력한다.

  2. 실행 중인 파드를 안전하게 축출하기 위해 노드를 비운다.

    kubectl drain <NODE_NAME> \
        --ignore-daemonsets
    

cri-dockerd를 사용하도록 kubelet 설정

아래의 단계는 kubeadm 도구를 사용하여 생성된 클러스터에 적용된다. 다른 도구를 사용했다면, 해당 도구에 대한 환경 설정 방법을 참고하여 kubelet 환경 설정을 수정해야 한다.

  1. 영향 받는 각 노드의 /var/lib/kubelet/kubeadm-flags.env를 연다.
  2. --container-runtime-endpoint 플래그를 unix:///var/run/cri-dockerd.sock로 수정한다.

kubeadm 도구는 노드의 소켓을 컨트롤 플레인의 Node 오브젝트의 어노테이션으로 저장한다. 영향을 받는 각 노드의 해당 소켓을 수정하려면 다음을 따른다.

  1. Node 오브젝트의 YAML 표현식을 편집한다.

    KUBECONFIG=/path/to/admin.conf kubectl edit no <NODE_NAME>
    

    각 인자는 다음과 같이 입력한다.

    • /path/to/admin.conf: kubectl 환경 설정 파일(admin.conf)의 경로.
    • <NODE_NAME>: 수정을 원하는 노드의 이름.
  2. kubeadm.alpha.kubernetes.io/cri-socket의 값 /var/run/dockershim.sockunix:///var/run/cri-dockerd.sock로 변경한다.

  3. 변경을 저장한다. Node 오브젝트는 저장 시 업데이트된다.

kubelet 재시작

systemctl restart kubelet

노드가 정상(healthy)인지 확인

노드가 cri-dockerd 엔드포인트를 사용하는지 확인하려면, 사용 런타임 찾기 지침을 따른다. kubelet의 --container-runtime-endpoint 플래그는 unix:///var/run/cri-dockerd.sock 이어야 한다.

노드 통제 해제(Uncordon)

노드에 파드를 스케줄 하도록 통제를 해제한다.

kubectl uncordon <NODE_NAME>

다음 내용

3 - 메모리, CPU 와 API 리소스 관리

3.1 - 네임스페이스에 대한 기본 메모리 요청량과 상한 구성

한 네임스페이스에 메모리 리소스 상한의 기본값을 정의하며, 이를 통해 미리 설정한 메모리 리소스 상한이 해당 네임스페이스의 새로운 파드에 설정되도록 한다.

이 페이지는 네임스페이스에 대한 기본 메모리 요청량(request) 및 상한(limit)을 구성하는 방법을 보여준다.

쿠버네티스 클러스터를 여러 네임스페이스로 나눌 수 있다. 기본 메모리 상한이 설정되어 있는 네임스페이스에 파드를 생성했는데, 해당 파드의 모든 컨테이너에 메모리 상한이 명시되어 있지 않다면, 컨트롤 플레인이 해당 컨테이너에 기본 메모리 상한을 할당한다.

쿠버네티스는 이 문서의 뒷부분에서 설명하는 특정 조건에서 기본 메모리 요청량을 할당한다.

시작하기 전에

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

클러스터에 네임스페이스를 생성할 수 있는 권한이 있어야 한다.

클러스터의 각 노드에는 최소 2GiB의 메모리가 있어야 한다.

네임스페이스 생성

이 연습에서 생성한 리소스가 클러스터의 다른 리소스와 격리되도록 네임스페이스를 생성한다.

kubectl create namespace default-mem-example

리밋레인지(LimitRange)와 파드 생성

다음은 예시 리밋레인지에 대한 매니페스트이다. 이 매니페스트는 기본 메모리 요청량 및 기본 메모리 상한을 지정한다.

apiVersion: v1
kind: LimitRange
metadata:
  name: mem-limit-range
spec:
  limits:
  - default:
      memory: 512Mi
    defaultRequest:
      memory: 256Mi
    type: Container

default-mem-example 네임스페이스에 리밋레인지를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults.yaml --namespace=default-mem-example

이제 파드를 default-mem-example 네임스페이스에 생성하고, 해당 파드의 어떤 컨테이너도 자체 메모리 요청량(request)과 상한(limit)을 명시하지 않으면, 컨트롤 플레인이 해당 컨테이너에 메모리 요청량의 기본값(256 MiB)과 상한의 기본값(512 MiB)을 지정한다.

다음은 컨테이너가 하나인 파드의 매니페스트이다. 해당 컨테이너는 메모리 요청량과 상한을 지정하지 않는다.

apiVersion: v1
kind: Pod
metadata:
  name: default-mem-demo
spec:
  containers:
  - name: default-mem-demo-ctr
    image: nginx

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults-pod.yaml --namespace=default-mem-example

파드에 대한 자세한 정보를 본다.

kubectl get pod default-mem-demo --output=yaml --namespace=default-mem-example

출력 결과는 파드의 컨테이너에 256MiB의 메모리 요청량과 512MiB의 메모리 상한이 있음을 나타낸다. 이것은 리밋레인지에 의해 지정된 기본값이다.

containers:
- image: nginx
  imagePullPolicy: Always
  name: default-mem-demo-ctr
  resources:
    limits:
      memory: 512Mi
    requests:
      memory: 256Mi

파드를 삭제한다.

kubectl delete pod default-mem-demo --namespace=default-mem-example

컨테이너 상한은 지정하고, 요청량을 지정하지 않으면 어떻게 되나?

다음은 컨테이너가 하나인 파드의 매니페스트이다. 해당 컨테이너는 메모리 상한은 지정하지만, 요청량은 지정하지 않는다.

apiVersion: v1
kind: Pod
metadata:
  name: default-mem-demo-2
spec:
  containers:
  - name: default-mem-demo-2-ctr
    image: nginx
    resources:
      limits:
        memory: "1Gi"

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults-pod-2.yaml --namespace=default-mem-example

파드에 대한 자세한 정보를 본다.

kubectl get pod default-mem-demo-2 --output=yaml --namespace=default-mem-example

출력 결과는 컨테이너의 메모리 요청량이 메모리 상한과 일치하도록 설정되었음을 보여준다. 참고로 컨테이너에는 기본 메모리 요청량의 값인 256Mi가 할당되지 않았다.

resources:
  limits:
    memory: 1Gi
  requests:
    memory: 1Gi

컨테이너의 요청량은 지정하고, 상한을 지정하지 않으면 어떻게 되나?

다음은 컨테이너가 하나인 파드의 예시 매니페스트이다. 해당 컨테이너는 메모리 요청량은 지정하지만, 상한은 지정하지 않는다.

apiVersion: v1
kind: Pod
metadata:
  name: default-mem-demo-3
spec:
  containers:
  - name: default-mem-demo-3-ctr
    image: nginx
    resources:
      requests:
        memory: "128Mi"

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults-pod-3.yaml --namespace=default-mem-example

파드 사양을 확인한다.

kubectl get pod default-mem-demo-3 --output=yaml --namespace=default-mem-example

출력을 보면 컨테이너의 매니페스트에 명시한 값대로 컨테이너의 메모리 요청량이 설정된 것을 알 수 있다. 해당 컨테이너의 메모리 상한은 512 MiB로 설정되며, 이는 네임스페이스의 메모리 상한 기본값과 일치한다.

resources:
  limits:
    memory: 512Mi
  requests:
    memory: 128Mi

기본 메모리 상한 및 요청량에 대한 동기

네임스페이스에 리소스 쿼터가 설정되어 있는 경우, 메모리 상한에 기본값을 설정하는 것이 좋다. 다음은 리소스 쿼터가 네임스페이스에 적용하는 세 가지 제한 사항이다.

  • 네임스페이스에서 실행되는 모든 파드에 대해, 모든 컨테이너에 메모리 상한이 있어야 한다. (파드의 모든 컨테이너에 대해 메모리 상한을 지정하면, 쿠버네티스가 파드 내의 컨테이너의 상한을 합산하여 파드-수준 메모리 상한을 추론할 수 있다.)
  • 메모리 상한은 해당 파드가 스케줄링될 노드에 리소스 예약을 적용한다. 해당 네임스페이스의 모든 파드에 대해 예약된 메모리 총량이 지정된 상한을 초과하지 않아야 한다.
  • 해당 네임스페이스의 모든 파드가 실제로 사용하고 있는 메모리의 총량 또한 지정된 상한을 초과하지 않아야 한다.

리밋레인지를 추가할 때에는 다음을 고려해야 한다.

컨테이너를 갖고 있는 해당 네임스페이스의 파드가 자체 메모리 상한을 지정하지 않았다면, 컨트롤 플레인이 해당 컨테이너에 메모리 상한 기본값을 적용하며, 해당 파드는 메모리 리소스쿼터가 적용된 네임스페이스에서 실행되도록 허용될 수 있다.

정리

네임스페이스를 삭제한다.

kubectl delete namespace default-mem-example

다음 내용

클러스터 관리자를 위한 문서

앱 개발자를 위한 문서

3.2 - 네임스페이스에 대한 기본 CPU 요청량과 상한 구성

한 네임스페이스에 CPU 리소스 상한의 기본값을 정의하며, 이를 통해 미리 설정한 CPU 리소스 상한이 해당 네임스페이스의 새로운 파드에 설정되도록 한다.

이 페이지는 네임스페이스에 대한 기본 CPU 요청량(request) 및 상한(limit)을 구성하는 방법을 보여준다.

쿠버네티스 클러스터를 여러 네임스페이스로 나눌 수 있다. 기본 CPU 상한이 설정되어 있는 네임스페이스에 파드를 생성했는데, 해당 파드의 모든 컨테이너에 CPU 상한이 명시되어 있지 않다면, 컨트롤 플레인이 해당 컨테이너에 기본 CPU 상한을 할당한다.

쿠버네티스는 기본 CPU 사용량을 할당하는데, 이는 이 페이지의 이후 부분에서 설명될 특정 조건 하에서만 수행된다.

시작하기 전에

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

클러스터에 네임스페이스를 생성할 수 있는 권한이 있어야 한다.

쿠버네티스에서 “1.0 CPU”가 무엇을 의미하는지 익숙하지 않다면, CPU의 의미를 참조한다.

네임스페이스 생성

이 연습에서 생성한 리소스가 클러스터의 나머지와 격리되도록 네임스페이스를 생성한다.

kubectl create namespace default-cpu-example

리밋레인지(LimitRange)와 파드 생성

다음은 예시 리밋레인지에 대한 매니페스트이다. 이 매니페스트는 기본 CPU 요청량 및 기본 CPU 상한을 지정한다.

apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-limit-range
spec:
  limits:
  - default:
      cpu: 1
    defaultRequest:
      cpu: 0.5
    type: Container

default-cpu-example 네임스페이스에 리밋레인지를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-defaults.yaml --namespace=default-cpu-example

이제 파드를 default-cpu-example 네임스페이스에 생성하고, 해당 파드의 어떤 컨테이너도 자체 CPU 요청량(request)과 상한(limit)을 명시하지 않으면, 컨트롤 플레인이 해당 컨테이너에 CPU 요청량의 기본값(0.5)과 상한의 기본값(1)을 지정한다.

다음은 컨테이너가 하나인 파드의 매니페스트이다. 해당 컨테이너는 CPU 요청량과 상한을 지정하지 않는다.

apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo
spec:
  containers:
  - name: default-cpu-demo-ctr
    image: nginx

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-defaults-pod.yaml --namespace=default-cpu-example

파드의 사양을 확인한다.

kubectl get pod default-cpu-demo --output=yaml --namespace=default-cpu-example

출력을 보면 파드 내 유일한 컨테이너의 CPU 요청량이 500m cpu("500 밀리cpu"로 읽을 수 있음)이고, CPU 상한이 1 cpu임을 알 수 있다. 이것은 리밋레인지에 의해 지정된 기본값이다.

containers:
- image: nginx
  imagePullPolicy: Always
  name: default-cpu-demo-ctr
  resources:
    limits:
      cpu: "1"
    requests:
      cpu: 500m

컨테이너 상한은 지정하고, 요청량을 지정하지 않으면 어떻게 되나?

다음은 컨테이너가 하나인 파드의 매니페스트이다. 해당 컨테이너는 CPU 상한은 지정하지만, 요청량은 지정하지 않는다.

apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo-2
spec:
  containers:
  - name: default-cpu-demo-2-ctr
    image: nginx
    resources:
      limits:
        cpu: "1"

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-defaults-pod-2.yaml --namespace=default-cpu-example

생성한 파드의 명세를 확인한다.

kubectl get pod default-cpu-demo-2 --output=yaml --namespace=default-cpu-example

출력 결과는 컨테이너의 CPU 요청량이 CPU 상한과 일치하도록 설정되었음을 보여준다. 참고로 컨테이너에는 CPU 요청량의 기본값인 0.5 cpu가 할당되지 않았다.

resources:
  limits:
    cpu: "1"
  requests:
    cpu: "1"

컨테이너의 요청량은 지정하고, 상한을 지정하지 않으면 어떻게 되나?

다음은 컨테이너가 하나인 파드의 예시 매니페스트이다. 해당 컨테이너는 CPU 요청량은 지정하지만, 상한은 지정하지 않는다.

apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo-3
spec:
  containers:
  - name: default-cpu-demo-3-ctr
    image: nginx
    resources:
      requests:
        cpu: "0.75"

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-defaults-pod-3.yaml --namespace=default-cpu-example

생성한 파드의 명세를 확인한다.

kubectl get pod default-cpu-demo-3 --output=yaml --namespace=default-cpu-example

출력을 보면 파드 생성 시 명시한 값대로 컨테이너의 CPU 요청량이 설정된 것을 알 수 있다(다시 말해, 매니페스트와 일치한다). 그러나, 해당 컨테이너의 CPU 상한은 1 cpu로 설정되며, 이는 네임스페이스의 CPU 상한 기본값이다.

resources:
  limits:
    cpu: "1"
  requests:
    cpu: 750m

CPU 상한 및 요청량의 기본값에 대한 동기

네임스페이스에 리소스 쿼터가 설정되어 있는 경우, CPU 상한에 대해 기본값을 설정하는 것이 좋다. 다음은 CPU 리소스 쿼터가 네임스페이스에 적용하는 두 가지 제한 사항이다.

  • 네임스페이스에서 실행되는 모든 파드에 대해, 모든 컨테이너에 CPU 상한이 있어야 한다.
  • CPU 상한은 해당 파드가 스케줄링될 노드에 리소스 예약을 적용한다. 해당 네임스페이스의 모든 파드에 대해 예약된 CPU 총량이 지정된 상한을 초과하지 않아야 한다.

리밋레인지를 추가할 때에는 다음을 고려해야 한다.

컨테이너를 갖고 있는 해당 네임스페이스의 파드가 자체 CPU 상한을 지정하지 않았다면, 컨트롤 플레인이 해당 컨테이너에 CPU 상한 기본값을 적용하며, 해당 파드는 CPU 리소스쿼터가 적용된 네임스페이스에서 실행되도록 허용될 수 있다.

정리

네임스페이스를 삭제한다.

kubectl delete namespace default-cpu-example

다음 내용

클러스터 관리자를 위한 문서

앱 개발자를 위한 문서

3.3 - 네임스페이스에 대한 메모리의 최소 및 최대 제약 조건 구성

한 네임스페이스 내에서 메모리 리소스 제한의 유효한 범위를 정의하며, 이를 통해 해당 네임스페이스의 새로운 파드가 미리 설정한 범위 안에 들어오도록 한다.

이 페이지는 네임스페이스에서 실행되는 컨테이너가 사용하는 메모리의 최솟값과 최댓값을 설정하는 방법을 보여준다. 리밋레인지(LimitRange) 오브젝트에 최소 및 최대 메모리 값을 지정한다. 파드가 리밋레인지에 의해 부과된 제약 조건을 충족하지 않으면, 네임스페이스에서 생성될 수 없다.

시작하기 전에

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

클러스터에 네임스페이스를 생성할 수 있는 권한이 있어야 한다.

클러스터의 각 노드에는 파드가 사용할 수 있는 메모리가 최소 1GiB 이상 있어야 한다.

네임스페이스 생성

이 연습에서 생성한 리소스가 클러스터의 나머지와 격리되도록 네임스페이스를 생성한다.

kubectl create namespace constraints-mem-example

리밋레인지와 파드 생성

다음은 리밋레인지의 예시 매니페스트이다.

apiVersion: v1
kind: LimitRange
metadata:
  name: mem-min-max-demo-lr
spec:
  limits:
  - max:
      memory: 1Gi
    min:
      memory: 500Mi
    type: Container

리밋레인지를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints.yaml --namespace=constraints-mem-example

리밋레인지에 대한 자세한 정보를 본다.

kubectl get limitrange mem-min-max-demo-lr --namespace=constraints-mem-example --output=yaml

출력 결과는 예상대로 메모리의 최소 및 최대 제약 조건을 보여준다. 그러나 참고로 리밋레인지의 구성 파일에 기본값(default)을 지정하지 않아도 자동으로 생성된다.

  limits:
  - default:
      memory: 1Gi
    defaultRequest:
      memory: 1Gi
    max:
      memory: 1Gi
    min:
      memory: 500Mi
    type: Container

이제 constraints-mem-example 네임스페이스에 파드를 생성할 때마다, 쿠버네티스는 다음 단계를 수행한다.

  • 해당 파드의 어떤 컨테이너도 자체 메모리 요청량(request)과 상한(limit)을 명시하지 않으면, 컨트롤 플레인이 해당 컨테이너에 메모리 요청량과 상한의 기본값(default)을 지정한다.

  • 해당 파드의 모든 컨테이너의 메모리 요청량이 최소 500 MiB 이상인지 확인한다.

  • 해당 파드의 모든 컨테이너의 메모리 요청량이 1024 MiB(1 GiB)를 넘지 않는지 확인한다.

다음은 컨테이너가 하나인 파드의 매니페스트이다. 파드 명세 내에, 파드의 유일한 컨테이너는 600 MiB의 메모리 요청량 및 800 MiB의 메모리 상한을 지정하고 있다. 이는 리밋레인지에 의해 부과된 최소 및 최대 메모리 제약 조건을 충족시킨다.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo
spec:
  containers:
  - name: constraints-mem-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
      requests:
        memory: "600Mi"

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod.yaml --namespace=constraints-mem-example

파드가 실행 중이고 컨테이너의 상태가 정상인지 확인한다.

kubectl get pod constraints-mem-demo --namespace=constraints-mem-example

파드에 대한 자세한 정보를 본다.

kubectl get pod constraints-mem-demo --output=yaml --namespace=constraints-mem-example

출력을 보면 파드의 컨테이너의 메모리 요청량이 600 MiB이고 메모리 상한이 800 MiB임을 알 수 있다. 이는 리밋레인지에 의해 해당 네임스페이스에 부과된 제약 조건을 만족시킨다.

resources:
  limits:
     memory: 800Mi
  requests:
    memory: 600Mi

파드를 삭제한다.

kubectl delete pod constraints-mem-demo --namespace=constraints-mem-example

최대 메모리 제약 조건을 초과하는 파드 생성 시도

다음은 컨테이너가 하나인 파드의 매니페스트이다. 컨테이너는 800MiB의 메모리 요청량과 1.5GiB의 메모리 상한을 지정하고 있다.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-2
spec:
  containers:
  - name: constraints-mem-demo-2-ctr
    image: nginx
    resources:
      limits:
        memory: "1.5Gi"
      requests:
        memory: "800Mi"

파드 생성을 시도한다.

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-2.yaml --namespace=constraints-mem-example

결과를 보면 파드가 생성되지 않은 것을 확인할 수 있으며, 이는 해당 파드가 정의하고 있는 컨테이너가 허용된 것보다 더 많은 메모리를 요청하고 있기 때문이다.

Error from server (Forbidden): error when creating "examples/admin/resource/memory-constraints-pod-2.yaml":
pods "constraints-mem-demo-2" is forbidden: maximum memory usage per Container is 1Gi, but limit is 1536Mi.

최소 메모리 요청량을 충족하지 않는 파드 생성 시도

다음은 컨테이너가 하나인 파드의 매니페스트이다. 컨테이너는 100MiB의 메모리 요청량과 800MiB의 메모리 상한을 지정하고 있다.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-3
spec:
  containers:
  - name: constraints-mem-demo-3-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
      requests:
        memory: "100Mi"

파드 생성을 시도한다.

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-3.yaml --namespace=constraints-mem-example

결과를 보면 파드가 생성되지 않은 것을 확인할 수 있으며, 이는 해당 파드가 정의하고 있는 컨테이너가 지정된 최저 메모리 요청량보다도 낮은 메모리 요청량을 지정하고 있기 때문이다.

Error from server (Forbidden): error when creating "examples/admin/resource/memory-constraints-pod-3.yaml":
pods "constraints-mem-demo-3" is forbidden: minimum memory usage per Container is 500Mi, but request is 100Mi.

메모리 요청량 또는 상한을 지정하지 않은 파드 생성

다음은 컨테이너가 하나인 파드의 매니페스트이다. 해당 컨테이너는 메모리 요청량과 상한을 지정하지 않는다.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-4
spec:
  containers:
  - name: constraints-mem-demo-4-ctr
    image: nginx

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-4.yaml --namespace=constraints-mem-example

파드에 대한 자세한 정보를 본다.

kubectl get pod constraints-mem-demo-4 --namespace=constraints-mem-example --output=yaml

출력을 보면 파드의 유일한 컨테이너에 대한 메모리 요청량이 1 GiB이고 메모리 상한도 1 GiB이다. 이 컨테이너는 어떻게 이런 값을 얻었을까?

resources:
  limits:
    memory: 1Gi
  requests:
    memory: 1Gi

파드가 해당 컨테이너에 대해 메모리 요청량과 상한을 지정하지 않았으므로, 클러스터가 리밋레인지로부터 메모리의 요청량과 상한 기본값을 적용하였다.

이는 곧 파드 정의에서 이 값들을 볼 수 있음을 의미한다. kubectl describe 명령을 사용하여 확인할 수 있다.

# 출력에서 "Requests:" 섹션을 확인한다
kubectl describe pod constraints-mem-demo-4 --namespace=constraints-mem-example

이 시점에서, 파드는 실행 중일 수도 있고 아닐 수도 있다. 이 태스크의 전제 조건은 노드에 최소 1GiB의 메모리가 있어야 한다는 것이다. 각 노드에 1GiB의 메모리만 있는 경우, 노드에 할당할 수 있는 메모리가 1GiB의 메모리 요청량을 수용하기에 충분하지 않을 수 있다. 메모리가 2GiB인 노드를 사용하는 경우에는, 메모리가 1GiB 요청량을 수용하기에 충분할 것이다.

파드를 삭제한다.

kubectl delete pod constraints-mem-demo-4 --namespace=constraints-mem-example

메모리의 최소 및 최대 제약 조건 적용

리밋레인지에 의해 네임스페이스에 부과된 메모리의 최대 및 최소 제약 조건은 파드를 생성하거나 업데이트할 때만 적용된다. 리밋레인지를 변경해도, 이전에 생성된 파드에는 영향을 미치지 않는다.

메모리의 최소 및 최대 제약 조건에 대한 동기

클러스터 관리자는 파드가 사용할 수 있는 메모리 양에 제한을 둘 수 있다. 예를 들면 다음과 같다.

  • 클러스터의 각 노드에는 2GiB의 메모리가 있다. 클러스터의 어떤 노드도 2GiB 이상의 요청량을 지원할 수 없으므로, 2GiB 이상의 메모리를 요청하는 파드를 수락하지 않으려고 한다.

  • 클러스터는 운영 부서와 개발 부서에서 공유한다. 프로덕션 워크로드가 최대 8GiB의 메모리를 소비하도록 하려면, 개발 워크로드를 512MiB로 제한해야 한다. 프로덕션 및 개발을 위해 별도의 네임스페이스를 만들고, 각 네임스페이스에 메모리 제약 조건을 적용한다.

정리

네임스페이스를 삭제한다.

kubectl delete namespace constraints-mem-example

다음 내용

클러스터 관리자를 위한 문서

앱 개발자를 위한 문서

3.4 - 네임스페이스에 대한 CPU의 최소 및 최대 제약 조건 구성

한 네임스페이스 내에서 CPU 리소스 제한의 유효한 범위를 정의하며, 이를 통해 해당 네임스페이스의 새로운 파드가 미리 설정한 범위 안에 들어오도록 한다.

이 페이지는 네임스페이스에서 컨테이너와 파드가 사용하는 CPU 리소스의 최솟값과 최댓값을 설정하는 방법을 보여준다. 리밋레인지(LimitRange) 오브젝트에 CPU의 최솟값과 최댓값을 지정한다. 리밋레인지에 의해 부과된 제약 조건을 파드가 충족하지 않으면, 해당 네임스페이스에 생성될 수 없다.

시작하기 전에

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

클러스터에 네임스페이스를 생성할 수 있는 권한이 있어야 한다.

클러스터의 각 노드는 파드 실행을 위해 적어도 1.0 CPU 이상이 사용 가능해야 한다. 쿠버네티스에서 “1 CPU”가 무엇을 의미하는지 알아보려면 CPU의 의미를 참조한다.

네임스페이스 생성

이 연습에서 생성한 리소스가 클러스터의 나머지와 격리되도록 네임스페이스를 생성한다.

kubectl create namespace constraints-cpu-example

리밋레인지와 파드 생성

다음은 리밋레인지 예제 매니페스트이다.

apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-min-max-demo-lr
spec:
  limits:
  - max:
      cpu: "800m"
    min:
      cpu: "200m"
    type: Container

리밋레인지를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints.yaml --namespace=constraints-cpu-example

리밋레인지에 대한 자세한 정보를 본다.

kubectl get limitrange cpu-min-max-demo-lr --output=yaml --namespace=constraints-cpu-example

출력 결과는 예상대로 CPU의 최소와 최대 제약 조건을 보여준다. 그러나 참고로 리밋레인지에 대한 구성 파일에 기본값을 지정하지 않아도 자동으로 생성된다.

limits:
- default:
    cpu: 800m
  defaultRequest:
    cpu: 800m
  max:
    cpu: 800m
  min:
    cpu: 200m
  type: Container

이제 constraints-cpu-example 네임스페이스에 파드를 생성할 때마다(또는 다른 쿠버네티스 API 클라이언트가 동일한 파드를 생성할 때마다), 쿠버네티스는 다음 단계를 수행한다.

  • 해당 파드의 어떤 컨테이너도 자체 CPU 요청량(request)과 상한(limit)을 명시하지 않으면, 컨트롤 플레인이 해당 컨테이너에 CPU 요청량과 상한의 기본값(default)을 지정한다.

  • 해당 파드의 모든 컨테이너가 200 millicpu 이상의 CPU 요청량을 지정하는지 확인한다.

  • 해당 파드의 모든 컨테이너가 800 millicpu 이하의 CPU 상한을 지정하는지 확인한다.

다음은 컨테이너가 하나인 파드의 매니페스트이다. 컨테이너 매니페스트는 500 millicpu의 CPU 요청량 및 800 millicpu의 CPU 상한을 지정하고 있다. 이는 이 네임스페이스의 리밋레인지에 의해 부과된 CPU의 최소와 최대 제약 조건을 충족시킨다.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo
spec:
  containers:
  - name: constraints-cpu-demo-ctr
    image: nginx
    resources:
      limits:
        cpu: "800m"
      requests:
        cpu: "500m"

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints-pod.yaml --namespace=constraints-cpu-example

파드가 실행 중이고 컨테이너의 상태가 정상인지 확인한다.

kubectl get pod constraints-cpu-demo --namespace=constraints-cpu-example

파드에 대한 자세한 정보를 본다.

kubectl get pod constraints-cpu-demo --output=yaml --namespace=constraints-cpu-example

출력 결과는 파드 내 유일한 컨테이너의 CPU 요청량이 500 millicpu이고, CPU 상한이 800 millicpu임을 나타낸다. 이는 리밋레인지에 의해 부과된 제약 조건을 만족시킨다.

resources:
  limits:
    cpu: 800m
  requests:
    cpu: 500m

파드 삭제

kubectl delete pod constraints-cpu-demo --namespace=constraints-cpu-example

CPU 최대 제약 조건을 초과하는 파드 생성 시도

다음은 컨테이너가 하나인 파드의 매니페스트이다. 컨테이너는 500 millicpu의 CPU 요청량과 1.5 cpu의 CPU 상한을 지정하고 있다.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-2
spec:
  containers:
  - name: constraints-cpu-demo-2-ctr
    image: nginx
    resources:
      limits:
        cpu: "1.5"
      requests:
        cpu: "500m"

파드 생성을 시도한다.

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints-pod-2.yaml --namespace=constraints-cpu-example

결과를 보면 파드가 생성되지 않은 것을 확인할 수 있으며, 이는 해당 파드가 수용 불가능한 컨테이너를 정의하고 있기 때문이다. 해당 컨테이너가 수용 불가능한 이유는 너무 큰 CPU 상한을 지정하고 있기 때문이다.

Error from server (Forbidden): error when creating "examples/admin/resource/cpu-constraints-pod-2.yaml":
pods "constraints-cpu-demo-2" is forbidden: maximum cpu usage per Container is 800m, but limit is 1500m.

최소 CPU 요청량을 충족하지 않는 파드 생성 시도

다음은 컨테이너가 하나인 파드의 매니페스트이다. 컨테이너는 100 millicpu의 CPU 요청량과 800 millicpu의 CPU 상한을 지정하고 있다.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-3
spec:
  containers:
  - name: constraints-cpu-demo-3-ctr
    image: nginx
    resources:
      limits:
        cpu: "800m"
      requests:
        cpu: "100m"

파드 생성을 시도한다.

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints-pod-3.yaml --namespace=constraints-cpu-example

결과를 보면 파드가 생성되지 않은 것을 확인할 수 있으며, 이는 해당 파드가 수용 불가능한 컨테이너를 정의하고 있기 때문이다. 해당 컨테이너가 수용 불가능한 이유는 지정된 최저 CPU 요청량보다도 낮은 CPU 요청량을 지정하고 있기 때문이다.

Error from server (Forbidden): error when creating "examples/admin/resource/cpu-constraints-pod-3.yaml":
pods "constraints-cpu-demo-3" is forbidden: minimum cpu usage per Container is 200m, but request is 100m.

CPU 요청량 또는 상한을 지정하지 않은 파드 생성

다음은 컨테이너가 하나인 파드의 매니페스트이다. 컨테이너는 CPU 요청량을 지정하지 않았으며, CPU 상한도 지정하지 않았다.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-4
spec:
  containers:
  - name: constraints-cpu-demo-4-ctr
    image: vish/stress

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints-pod-4.yaml --namespace=constraints-cpu-example

파드에 대한 자세한 정보를 본다.

kubectl get pod constraints-cpu-demo-4 --namespace=constraints-cpu-example --output=yaml

출력을 보면 파드의 유일한 컨테이너에 대한 CPU 요청량이 800 millicpu이고, CPU 상한이 800 millicpu이다. 이 컨테이너는 어떻게 이런 값을 얻었을까?

resources:
  limits:
    cpu: 800m
  requests:
    cpu: 800m

컨테이너가 자체 CPU 요청량과 상한을 지정하지 않았으므로, 컨테이너가 이 네임스페이스에 대해 리밋레인지로부터 CPU 요청량과 상한의 기본값을 적용했다.

이 시점에서, 파드는 실행 중일 수도 있고 아닐 수도 있다. 이 태스크의 전제 조건은 노드에 1 CPU 이상 사용 가능해야 한다는 것이다. 각 노드에 1 CPU만 있는 경우, 노드에 할당할 수 있는 CPU가 800 millicpu의 요청량을 수용하기에 충분하지 않을 수 있다. 2 CPU인 노드를 사용하는 경우에는, CPU가 800 millicpu 요청량을 수용하기에 충분할 것이다.

파드를 삭제한다.

kubectl delete pod constraints-cpu-demo-4 --namespace=constraints-cpu-example

CPU의 최소 및 최대 제약 조건의 적용

리밋레인지에 의해 네임스페이스에 부과된 CPU의 최대 및 최소 제약 조건은 파드를 생성하거나 업데이트할 때만 적용된다. 리밋레인지를 변경해도, 이전에 생성된 파드에는 영향을 미치지 않는다.

CPU의 최소 및 최대 제약 조건에 대한 동기

클러스터 관리자는 파드가 사용할 수 있는 CPU 리소스에 제한을 둘 수 있다. 예를 들면 다음과 같다.

  • 클러스터의 각 노드에는 2 CPU가 있다. 클러스터의 어떤 노드도 요청량을 지원할 수 없기 때문에, 2 CPU 이상을 요청하는 파드를 수락하지 않으려고 한다.

  • 클러스터는 프로덕션과 개발 부서에서 공유한다. 프로덕션 워크로드가 최대 3 CPU를 소비하도록 하고 싶지만, 개발 워크로드는 1 CPU로 제한하려고 한다. 프로덕션과 개발을 위해 별도의 네임스페이스를 생성하고, 각 네임스페이스에 CPU 제약 조건을 적용한다.

정리

네임스페이스를 삭제한다.

kubectl delete namespace constraints-cpu-example

다음 내용

클러스터 관리자를 위한 문서

앱 개발자를 위한 문서

3.5 - 네임스페이스에 대한 메모리 및 CPU 쿼터 구성

한 네임스페이스에 대한 총 메모리 및 CPU 자원 상한을 정의한다.

이 페이지는 네임스페이스에서 실행 중인 모든 파드가 사용할 수 있는 총 메모리 및 CPU 양에 대한 쿼터를 설정하는 방법을 보여준다. 리소스쿼터(ResourceQuota) 오브젝트에 쿼터를 지정할 수 있다.

시작하기 전에

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

클러스터에 네임스페이스를 생성할 수 있는 권한이 있어야 한다.

클러스터의 각 노드에는 최소 1GiB의 메모리가 있어야 한다.

네임스페이스 생성

이 연습에서 생성한 리소스가 클러스터의 나머지와 격리되도록 네임스페이스를 생성한다.

kubectl create namespace quota-mem-cpu-example

리소스쿼터 생성

다음은 예시 리소스쿼터 오브젝트에 대한 매니페스트이다.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: mem-cpu-demo
spec:
  hard:
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi

리소스쿼터를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/quota-mem-cpu.yaml --namespace=quota-mem-cpu-example

리소스쿼터에 대한 자세한 정보를 본다.

kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example --output=yaml

리소스쿼터는 이러한 요구 사항을 quota-mem-cpu-example 네임스페이스에 배치한다.

  • 네임스페이스의 모든 파드에 대해, 각 컨테이너에는 메모리 요청량(request), 메모리 상한(limit), CPU 요청량 및 CPU 상한이 있어야 한다.
  • 네임스페이스의 모든 파드에 대한 총 메모리 요청량은 1GiB를 초과하지 않아야 한다.
  • 네임스페이스의 모든 파드에 대한 총 메모리 상한은 2GiB를 초과하지 않아야 한다.
  • 네임스페이스의 모든 파드에 대한 총 CPU 요청량은 1 cpu를 초과해서는 안된다.
  • 네임스페이스의 모든 파드에 대한 총 CPU 상한은 2 cpu를 초과해서는 안된다.

쿠버네티스에서 “1 CPU”가 무엇을 의미하는지 알아보려면 CPU의 의미를 참조한다.

파드 생성

다음은 예시 파드에 대한 매니페스트이다.

apiVersion: v1
kind: Pod
metadata:
  name: quota-mem-cpu-demo
spec:
  containers:
  - name: quota-mem-cpu-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
        cpu: "800m"
      requests:
        memory: "600Mi"
        cpu: "400m"

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/quota-mem-cpu-pod.yaml --namespace=quota-mem-cpu-example

파드가 실행 중이고 파드의 (유일한) 컨테이너의 상태가 정상인지 확인한다.

kubectl get pod quota-mem-cpu-demo --namespace=quota-mem-cpu-example

다시 한 번, 리소스쿼터에 대한 자세한 정보를 본다.

kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example --output=yaml

출력 결과는 쿼터와 사용된 쿼터를 함께 보여준다. 파드의 메모리와 CPU 요청량 및 상한이 쿼터를 초과하지 않은 것을 볼 수 있다.

status:
  hard:
    limits.cpu: "2"
    limits.memory: 2Gi
    requests.cpu: "1"
    requests.memory: 1Gi
  used:
    limits.cpu: 800m
    limits.memory: 800Mi
    requests.cpu: 400m
    requests.memory: 600Mi

jq 도구가 설치되어 있으면, (JSONPath를 사용하여) used 값만을 질의 하고, 정돈된 상태로 출력할 수 있다. 예시는 다음과 같다.

kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example -o jsonpath='{ .status.used }' | jq .

두 번째 파드 생성 시도

다음은 두 번째 파드에 대한 매니페스트이다.

apiVersion: v1
kind: Pod
metadata:
  name: quota-mem-cpu-demo-2
spec:
  containers:
  - name: quota-mem-cpu-demo-2-ctr
    image: redis
    resources:
      limits:
        memory: "1Gi"
        cpu: "800m"
      requests:
        memory: "700Mi"
        cpu: "400m"

매니페스트에서, 파드의 메모리 요청량이 700MiB임을 알 수 있다. 사용된 메모리 요청량과 이 새 메모리 요청량의 합계가 메모리 요청량 쿼터를 초과함에 유의한다(600 MiB + 700 MiB > 1 GiB).

파드 생성을 시도한다.

kubectl apply -f https://k8s.io/examples/admin/resource/quota-mem-cpu-pod-2.yaml --namespace=quota-mem-cpu-example

두 번째 파드는 생성되지 않는다. 출력 결과는 두 번째 파드를 생성하면 메모리 요청량의 총 합계가 메모리 요청량 쿼터를 초과함을 보여준다.

Error from server (Forbidden): error when creating "examples/admin/resource/quota-mem-cpu-pod-2.yaml":
pods "quota-mem-cpu-demo-2" is forbidden: exceeded quota: mem-cpu-demo,
requested: requests.memory=700Mi,used: requests.memory=600Mi, limited: requests.memory=1Gi

토론

이 연습에서 보았듯이, 리소스쿼터를 사용하여 네임스페이스에서 실행 중인 모든 파드에 대한 메모리 요청량의 총 합계를 제한할 수 있다. 메모리 상한, CPU 요청량 및 CPU 상한의 총 합계를 제한할 수도 있다.

네임스페이스 내의 총 자원을 관리하는 것 대신, 개별 파드 또는 파드 내의 컨테이너별로 제한하고 싶을 수도 있다. 이러한 종류의 제한을 걸려면, 리밋레인지(LimitRange)를 사용한다.

정리

네임스페이스를 삭제한다.

kubectl delete namespace quota-mem-cpu-example

다음 내용

클러스터 관리자를 위한 문서

앱 개발자를 위한 문서

3.6 - 네임스페이스에 대한 파드 쿼터 구성

한 네임스페이스 내에 만들 수 있는 파드의 수를 제한한다.

이 페이지는 네임스페이스에서 실행할 수 있는 총 파드 수에 대한 쿼터를 설정하는 방법을 보여준다. 리소스쿼터(ResourceQuota) 오브젝트에 쿼터를 지정할 수 있다.

시작하기 전에

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

클러스터에 네임스페이스를 생성할 수 있는 권한이 있어야 한다.

네임스페이스 생성

이 실습에서 생성한 리소스가 클러스터의 나머지와 격리되도록 네임스페이스를 생성한다.

kubectl create namespace quota-pod-example

리소스쿼터 생성

다음은 예시 리소스쿼터 오브젝트에 대한 매니페스트이다.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: pod-demo
spec:
  hard:
    pods: "2"

리소스쿼터를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/quota-pod.yaml --namespace=quota-pod-example

리소스쿼터에 대한 자세한 정보를 본다.

kubectl get resourcequota pod-demo --namespace=quota-pod-example --output=yaml

출력 결과는 네임스페이스에 두 개의 파드 쿼터가 있고, 현재 파드가 없음을 보여준다. 즉, 쿼터 중 어느 것도 사용되지 않았다.

spec:
  hard:
    pods: "2"
status:
  hard:
    pods: "2"
  used:
    pods: "0"

다음은 디플로이먼트(Deployment)에 대한 예시 매니페스트이다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-quota-demo
spec:
  selector:
    matchLabels:
      purpose: quota-demo
  replicas: 3
  template:
    metadata:
      labels:
        purpose: quota-demo
    spec:
      containers:
      - name: pod-quota-demo
        image: nginx

매니페스트에서, replicas: 3 은 쿠버네티스가 모두 동일한 애플리케이션을 실행하는 세 개의 새로운 파드를 만들도록 지시한다.

디플로이먼트를 생성한다.

kubectl apply -f https://k8s.io/examples/admin/resource/quota-pod-deployment.yaml --namespace=quota-pod-example

디플로이먼트에 대한 자세한 정보를 본다.

kubectl get deployment pod-quota-demo --namespace=quota-pod-example --output=yaml

출력을 보면 디플로이먼트가 3개의 레플리카를 정의하고 있음에도, 앞서 설정한 쿼터로 인해 2개의 파드만 생성되었음을 보여준다.

spec:
  ...
  replicas: 3
...
status:
  availableReplicas: 2
...
lastUpdateTime: 2021-04-02T20:57:05Z
    message: 'unable to create pods: pods "pod-quota-demo-1650323038-" is forbidden:
      exceeded quota: pod-demo, requested: pods=1, used: pods=2, limited: pods=2'

리소스 선택

이 예제에서는 총 파드 수를 제한하는 리소스쿼터를 정의하였다. 하지만, 다른 종류의 오브젝트의 총 수를 제한할 수도 있다. 예를 들어, 한 네임스페이스에 존재할 수 있는 크론잡의 총 수를 제한할 수 있다.

정리

네임스페이스를 삭제한다.

kubectl delete namespace quota-pod-example

다음 내용

클러스터 관리자를 위한 문서

앱 개발자를 위한 문서

4 - 네트워크 폴리시 제공자(Network Policy Provider) 설치

4.1 - 네트워크 폴리시로 캘리코(Calico) 사용하기

이 페이지는 쿠버네티스에서 캘리코(Calico) 클러스터를 생성하는 몇 가지 빠른 방법을 살펴본다.

시작하기 전에

클라우드지역 클러스터 중에 어디에 배포할지 결정한다.

구글 쿠버네티스 엔진(GKE)에 캘리코 클러스터 생성하기

사전요구사항: gcloud.

  1. 캘리코로 GKE 클러스터를 시작하려면, --enable-network-policy 플래그를 추가한다.

    문법

    gcloud container clusters create [클러스터_이름] --enable-network-policy
    

    예시

    gcloud container clusters create my-calico-cluster --enable-network-policy
    
  2. 배포를 확인하기 위해, 다음 커맨드를 이용하자.

    kubectl get pods --namespace=kube-system
    

    캘리코 파드는 calico로 시작한다. 각각의 상태가 Running임을 확인하자.

kubeadm으로 지역 캘리코 클러스터 생성하기

Kubeadm을 이용해서 15분 이내에 지역 단일 호스트 캘리코 클러스터를 생성하려면, 캘리코 빠른 시작을 참고한다.

다음 내용

클러스터가 동작하면, 쿠버네티스 네트워크 폴리시(NetworkPolicy)를 시도하기 위해 네트워크 폴리시 선언하기를 따라 할 수 있다.

4.2 - 네트워크 폴리시로 실리움(Cilium) 사용하기

이 페이지는 어떻게 네트워크 폴리시(NetworkPolicy)로 실리움(Cilium)를 사용하는지 살펴본다.

실리움의 배경에 대해서는 실리움 소개를 읽어보자.

시작하기 전에

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

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

기본 시험을 위해 실리움을 Minikube에 배포하기

실리움에 쉽게 친숙해지기 위해 Minikube에 실리움을 기본적인 데몬셋으로 설치를 수행하는 실리움 쿠버네티스 시작하기 안내를 따라 해볼 수 있다.

Minikube를 시작하려면 최소 버전으로 >= v1.5.2 이 필요하고, 다음의 실행 파라미터로 실행한다.

minikube version
minikube version: v1.5.2
minikube start --network-plugin=cni

minikube의 경우 CLI 도구를 사용하여 실리움을 설치할 수 있다. 그러기 위해서, 먼저 다음과 같은 명령을 사용하여 최신 버전의 CLI를 다운로드 한다.

curl -LO https://github.com/cilium/cilium-cli/releases/latest/download/cilium-linux-amd64.tar.gz

이후 다음과 같은 명령을 사용하여 다운받은 파일을 /usr/local/bin 디렉토리에 압축을 푼다.

sudo tar xzvfC cilium-linux-amd64.tar.gz /usr/local/bin
rm cilium-linux-amd64.tar.gz

위의 명령을 실행한 후, 이제 다음 명령으로 실리움(Cilium)을 설치할 수 있다.

cilium install

그런 다음 실리움은 자동으로 클러스터를 구성을 감지하고 성공적인 설치를 위해 적절한 구성요소를 만들고 설치한다. 구성요소는 다음과 같다.

  • 시크릿 cilium-ca의 인증 기관(CA) 및 Hubble (실리움의 관측 가능성 계층)에 대한 인증서.
  • 서비스 어카운트.
  • 클러스터 롤.
  • 컨피그맵.
  • 에이전트 데몬셋 및 오퍼레이터 디플로이먼트.

설치 후, cilium status 명령으로 실리움 디플로이먼트의 전체 상태를 볼 수 있다. status 명령으로 예상 출력을 참조한다. 여기.

시작하기 안내서의 나머지 부분은 예제 애플리케이션을 이용하여 L3/L4(예, IP 주소 + 포트) 모두의 보안 정책뿐만 아니라 L7(예, HTTP)의 보안 정책을 적용하는 방법을 설명한다.

실리움을 실 서비스 용도로 배포하기

실리움을 실 서비스 용도의 배포에 관련한 자세한 방법은 실리움 쿠버네티스 설치 안내를 살펴본다. 이 문서는 자세한 요구사항, 방법과 실제 데몬셋 예시를 포함한다.

실리움 구성요소 이해하기

실리움으로 클러스터를 배포하면 파드가 kube-system 네임스페이스에 추가된다. 파드의 목록을 보려면 다음을 실행한다.

kubectl get pods --namespace=kube-system -l k8s-app=cilium

다음과 유사한 파드의 목록을 볼 것이다.

NAME           READY   STATUS    RESTARTS   AGE
cilium-kkdhz   1/1     Running   0          3m23s
...

cilium 파드는 클러스터 각 노드에서 실행되며, 리눅스 BPF를 사용해서 해당 노드의 파드에 대한 트래픽 네트워크 폴리시를 적용한다.

다음 내용

클러스터가 동작하면, 실리움으로 쿠버네티스 네트워크 폴리시를 시도하기 위해 네트워크 폴리시 선언하기를 따라 할 수 있다. 재미있게 즐기고, 질문이 있다면 실리움 슬랙 채널을 이용하여 연락한다.

4.3 - 네트워크 폴리시로 큐브 라우터(Kube-router) 사용하기

이 페이지는 네트워크 폴리시(NetworkPolicy)로 큐브 라우터(Kube-router)를 사용하는 방법을 살펴본다.

시작하기 전에

운영 중인 쿠버네티스 클러스터가 필요하다. 클러스터가 없다면, Kops, Bootkube, Kubeadm 등을 이용해서 클러스터를 생성할 수 있다.

큐브 라우터 애드온 설치하기

큐브 라우터 애드온은 갱신된 모든 네트워크 폴리시 및 파드에 대해 쿠버네티스 API 서버를 감시하고, 정책에 따라 트래픽을 허용하거나 차단하도록 iptables 규칙와 ipset을 구성하는 네트워크 폴리시 컨트롤러와 함께 제공된다. 큐브 라우터 애드온을 설치하는 큐브 라우터를 클러스터 인스톨러와 함께 사용하기 안내서를 따라해 봅니다.

다음 내용

큐브 라우터 애드온을 설치한 후에는, 쿠버네티스 네트워크 폴리시를 시도하기 위해 네트워크 폴리시 선언하기를 따라 할 수 있다.

4.4 - 네트워크 폴리시로 로마나(Romana)

이 페이지는 네트워크 폴리시(NetworkPolicy)로 로마나(Romana)를 사용하는 방법을 살펴본다.

시작하기 전에

kubeadm 시작하기의 1, 2, 3 단계를 완료하자.

kubeadm으로 로마나 설치하기

Kubeadm을 위한 컨테이너화된 설치 안내서를 따른다.

네트워크 폴리시 적용하기

네트워크 폴리시를 적용하기 위해 다음 중에 하나를 사용하자.

다음 내용

로마나를 설치한 후에는, 쿠버네티스 네트워크 폴리시를 시도하기 위해 네트워크 폴리시 선언하기를 따라 할 수 있다.

4.5 - 네트워크 폴리시로 위브넷(Weave Net) 사용하기

이 페이지는 네트워크 폴리시(NetworkPolicy)로 위브넷(Weave Net)를 사용하는 방법을 살펴본다.

시작하기 전에

쿠버네티스 클러스터가 필요하다. 맨 땅에서부터 시작하기를 위해서 kubeadm 시작하기 안내서를 따른다.

Weave Net 애드온을 설치한다

애드온을 통한 쿠버네티스 통합하기 가이드를 따른다.

쿠버네티스의 위브넷 애드온은 쿠버네티스의 모든 네임스페이스의 네크워크 정책 어노테이션을 자동으로 모니터링하며, 정책에 따라 트래픽을 허용하고 차단하는 iptables 규칙을 구성하는 네트워크 폴리시 컨트롤러와 함께 제공된다.

설치 시험

위브넷이 동작하는지 확인한다.

다음 커맨드를 입력한다.

kubectl get pods -n kube-system -o wide

출력은 다음과 유사하다.

NAME                                    READY     STATUS    RESTARTS   AGE       IP              NODE
weave-net-1t1qg                         2/2       Running   0          9d        192.168.2.10    worknode3
weave-net-231d7                         2/2       Running   1          7d        10.2.0.17       worknodegpu
weave-net-7nmwt                         2/2       Running   3          9d        192.168.2.131   masternode
weave-net-pmw8w                         2/2       Running   0          9d        192.168.2.216   worknode2

위브넷 파드를 가진 각 노드와 모든 파드는 Running이고 2/2 READY이다(2/2는 각 파드가 weaveweave-npc를 가지고 있음을 뜻한다).

다음 내용

위브넷 애드온을 설치하고 나서, 쿠버네티스 네트워크 폴리시를 시도하기 위해 네트워크 폴리시 선언하기를 따라 할 수 있다. 질문이 있으면 슬랙 #weave-community 이나 Weave 유저그룹에 연락한다.

5 - 인증서

클라이언트 인증서로 인증을 사용하는 경우 easyrsa, openssl 또는 cfssl 을 통해 인증서를 수동으로 생성할 수 있다.

easyrsa

easyrsa 는 클러스터 인증서를 수동으로 생성할 수 있다.

  1. easyrsa3의 패치 버전을 다운로드하여 압축을 풀고, 초기화한다.

    curl -LO https://dl.k8s.io/easy-rsa/easy-rsa.tar.gz
    tar xzf easy-rsa.tar.gz
    cd easy-rsa-master/easyrsa3
    ./easyrsa init-pki
    
  2. 새로운 인증 기관(CA)을 생성한다. --batch 는 자동 모드를 설정한다. --req-cn 는 CA의 새 루트 인증서에 대한 일반 이름(Common Name (CN))을 지정한다.

    ./easyrsa --batch "--req-cn=${MASTER_IP}@`date +%s`" build-ca nopass
    
  3. 서버 인증서와 키를 생성한다.

    --subject-alt-name 인자로 API 서버에 접근이 가능한 IP와 DNS 이름을 설정한다. MASTER_CLUSTER_IP 는 일반적으로 API 서버와 컨트롤러 관리자 컴포넌트에 대해 --service-cluster-ip-range 인자로 지정된 서비스 CIDR의 첫 번째 IP이다. --days 인자는 인증서가 만료되는 일 수를 설정하는 데 사용된다. 또한, 아래 샘플에서는 cluster.local 을 기본 DNS 도메인 이름으로 사용하고 있다고 가정한다.

    ./easyrsa --subject-alt-name="IP:${MASTER_IP},"\
    "IP:${MASTER_CLUSTER_IP},"\
    "DNS:kubernetes,"\
    "DNS:kubernetes.default,"\
    "DNS:kubernetes.default.svc,"\
    "DNS:kubernetes.default.svc.cluster,"\
    "DNS:kubernetes.default.svc.cluster.local" \
    --days=10000 \
    build-server-full server nopass
    
  4. pki/ca.crt, pki/issued/server.crt 그리고 pki/private/server.key 를 디렉터리에 복사한다.

  5. API 서버를 시작하는 파라미터에 다음과 같이 추가한다.

    --client-ca-file=/yourdirectory/ca.crt
    --tls-cert-file=/yourdirectory/server.crt
    --tls-private-key-file=/yourdirectory/server.key
    

openssl

openssl 은 클러스터 인증서를 수동으로 생성할 수 있다.

  1. ca.key를 2048bit로 생성한다.

    openssl genrsa -out ca.key 2048
    
  2. ca.key에 따라 ca.crt를 생성한다(인증서 유효 기간을 사용하려면 -days를 사용한다).

    openssl req -x509 -new -nodes -key ca.key -subj "/CN=${MASTER_IP}" -days 10000 -out ca.crt
    
  3. server.key를 2048bit로 생성한다.

    openssl genrsa -out server.key 2048
    
  4. 인증서 서명 요청(Certificate Signing Request (CSR))을 생성하기 위한 설정 파일을 생성한다.

    파일에 저장하기 전에 꺾쇠 괄호(예: <MASTER_IP>)로 표시된 값을 실제 값으로 대체한다(예: csr.conf). MASTER_CLUSTER_IP 의 값은 이전 하위 섹션에서 설명한 대로 API 서버의 서비스 클러스터 IP이다. 또한, 아래 샘플에서는 cluster.local 을 기본 DNS 도메인 이름으로 사용하고 있다고 가정한다.

    [ req ]
    default_bits = 2048
    prompt = no
    default_md = sha256
    req_extensions = req_ext
    distinguished_name = dn
    
    [ dn ]
    C = <국가(country)>
    ST = <도(state)>
    L = <시(city)>
    O = <조직(organization)>
    OU = <조직 단위(organization unit)>
    CN = <MASTER_IP>
    
    [ req_ext ]
    subjectAltName = @alt_names
    
    [ alt_names ]
    DNS.1 = kubernetes
    DNS.2 = kubernetes.default
    DNS.3 = kubernetes.default.svc
    DNS.4 = kubernetes.default.svc.cluster
    DNS.5 = kubernetes.default.svc.cluster.local
    IP.1 = <MASTER_IP>
    IP.2 = <MASTER_CLUSTER_IP>
    
    [ v3_ext ]
    authorityKeyIdentifier=keyid,issuer:always
    basicConstraints=CA:FALSE
    keyUsage=keyEncipherment,dataEncipherment
    extendedKeyUsage=serverAuth,clientAuth
    subjectAltName=@alt_names
    
  5. 설정 파일을 기반으로 인증서 서명 요청을 생성한다.

    openssl req -new -key server.key -out server.csr -config csr.conf
    
  6. ca.key, ca.crt 그리고 server.csr을 사용해서 서버 인증서를 생성한다.

    openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
        -CAcreateserial -out server.crt -days 10000 \
        -extensions v3_ext -extfile csr.conf -sha256
    
  7. 인증서 서명 요청을 확인한다.

    openssl req  -noout -text -in ./server.csr
    
  8. 인증서를 확인한다.

    openssl x509  -noout -text -in ./server.crt
    

마지막으로, API 서버 시작 파라미터에 동일한 파라미터를 추가한다.

cfssl

cfssl 은 인증서 생성을 위한 또 다른 도구이다.

  1. 아래에 표시된 대로 커맨드 라인 도구를 다운로드하여 압축을 풀고 준비한다.

    사용 중인 하드웨어 아키텍처 및 cfssl 버전에 따라 샘플 명령을 조정해야 할 수도 있다.

    curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssl_1.5.0_linux_amd64 -o cfssl
    chmod +x cfssl
    curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssljson_1.5.0_linux_amd64 -o cfssljson
    chmod +x cfssljson
    curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssl-certinfo_1.5.0_linux_amd64 -o cfssl-certinfo
    chmod +x cfssl-certinfo
    
  2. 아티팩트(artifact)를 보유할 디렉터리를 생성하고 cfssl을 초기화한다.

mkdir cert
cd cert
../cfssl print-defaults config > config.json
../cfssl print-defaults csr > csr.json
  1. CA 파일을 생성하기 위한 JSON 설정 파일을 ca-config.json 예시와 같이 생성한다.

    {
      "signing": {
        "default": {
          "expiry": "8760h"
        },
        "profiles": {
          "kubernetes": {
            "usages": [
              "signing",
              "key encipherment",
              "server auth",
              "client auth"
            ],
            "expiry": "8760h"
          }
        }
      }
    }
    
  2. CA 인증서 서명 요청(CSR)을 위한 JSON 설정 파일을 ca-csr.json 예시와 같이 생성한다. 꺾쇠 괄호로 표시된 값을 사용하려는 실제 값으로 변경한다.

    {
      "CN": "kubernetes",
      "key": {
        "algo": "rsa",
        "size": 2048
      },
      "names":[{
        "C": "국가<country>",
        "ST": "도<state>",
        "L": "시<city>",
        "O": "조직<organization>",
        "OU": "조직 단위<organization unit>"
      }]
    }
    
  3. CA 키(ca-key.pem)와 인증서(ca.pem)을 생성한다.

    ../cfssl gencert -initca ca-csr.json | ../cfssljson -bare ca
    
  4. API 서버의 키와 인증서를 생성하기 위한 JSON 구성파일을 server-csr.json 예시와 같이 생성한다. 꺾쇠 괄호 안의 값을 사용하려는 실제 값으로 변경한다. MASTER_CLUSTER_IP 는 이전 하위 섹션에서 설명한 API 서버의 클러스터 IP이다. 아래 샘플은 기본 DNS 도메인 이름으로 cluster.local 을 사용한다고 가정한다.

    {
      "CN": "kubernetes",
      "hosts": [
        "127.0.0.1",
        "<MASTER_IP>",
        "<MASTER_CLUSTER_IP>",
        "kubernetes",
        "kubernetes.default",
        "kubernetes.default.svc",
        "kubernetes.default.svc.cluster",
        "kubernetes.default.svc.cluster.local"
      ],
      "key": {
        "algo": "rsa",
        "size": 2048
      },
      "names": [{
         "C": "<국가(country)>",
         "ST": "<도(state)>",
         "L": "<시(city)>",
         "O": "<조직(organization)>",
         "OU": "<조직 단위(organization unit)>"
      }]
    }
    
  5. API 서버 키와 인증서를 생성하면, 기본적으로 server-key.pemserver.pem 파일에 각각 저장된다.

    ../cfssl gencert -ca=ca.pem -ca-key=ca-key.pem \
         --config=ca-config.json -profile=kubernetes \
         server-csr.json | ../cfssljson -bare server
    

자체 서명된 CA 인증서의 배포

클라이언트 노드는 자체 서명된 CA 인증서를 유효한 것으로 인식하지 않을 수 있다. 비-프로덕션 디플로이먼트 또는 회사 방화벽 뒤에서 실행되는 디플로이먼트의 경우, 자체 서명된 CA 인증서를 모든 클라이언트에 배포하고 유효한 인증서의 로컬 목록을 새로 고칠 수 있다.

각 클라이언트에서, 다음 작업을 수행한다.

sudo cp ca.crt /usr/local/share/ca-certificates/kubernetes.crt
sudo update-ca-certificates
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d....
done.

인증서 API

certificates.k8s.io API를 사용하여 클러스터에서 TLS 인증서 관리에 설명된 대로 인증에 사용할 x509 인증서를 프로비전 할 수 있다.

6 - 쿠버네티스 API를 사용하여 클러스터에 접근하기

이 페이지는 쿠버네티스 API를 사용하여 클러스터에 접근하는 방법을 보여준다.

시작하기 전에

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

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

쿠버네티스 API에 접근

kubectl을 사용하여 처음으로 접근

쿠버네티스 API에 처음 접근하는 경우, 쿠버네티스 커맨드 라인 도구인 kubectl 을 사용한다.

클러스터에 접근하려면, 클러스터 위치를 알고 접근할 수 있는 자격 증명이 있어야 한다. 일반적으로, 시작하기 가이드를 통해 작업하거나, 다른 사람이 클러스터를 설정하고 자격 증명과 위치를 제공할 때 자동으로 설정된다.

다음의 명령으로 kubectl이 알고 있는 위치와 자격 증명을 확인한다.

kubectl config view

많은 예제는 kubectl 사용에 대한 소개를 제공한다. 전체 문서는 kubectl 매뉴얼에 있다.

REST API에 직접 접근

kubectl은 API 서버 찾기와 인증을 처리한다. curl 이나 wget 과 같은 http 클라이언트 또는 브라우저를 사용하여 REST API에 직접 접근하려는 경우, API 서버를 찾고 인증할 수 있는 여러 가지 방법이 있다.

  1. 프록시 모드에서 kubectl을 실행한다(권장). 이 방법은 저장된 API 서버 위치를 사용하고 자체 서명된 인증서를 사용하여 API 서버의 ID를 확인하므로 권장한다. 이 방법을 사용하면 중간자(man-in-the-middle, MITM) 공격이 불가능하다.
  2. 또는, 위치와 자격 증명을 http 클라이언트에 직접 제공할 수 있다. 이 방법은 프록시를 혼란스럽게 하는 클라이언트 코드와 동작한다. 중간자 공격으로부터 보호하려면, 브라우저로 루트 인증서를 가져와야 한다.

Go 또는 Python 클라이언트 라이브러리를 사용하면 프록시 모드에서 kubectl에 접근할 수 있다.

kubectl 프록시 사용

다음 명령은 kubectl을 리버스 프록시로 작동하는 모드에서 실행한다. API 서버 찾기와 인증을 처리한다.

다음과 같이 실행한다.

kubectl proxy --port=8080 &

자세한 내용은 kubectl 프록시를 참고한다.

그런 다음 curl, wget 또는 브라우저를 사용하여 API를 탐색할 수 있다.

curl http://localhost:8080/api/

출력은 다음과 비슷하다.

{
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

kubectl 프록시 없이 접근

다음과 같이 인증 토큰을 API 서버에 직접 전달하여 kubectl 프록시 사용을 피할 수 있다.

grep/cut 방식을 사용한다.

# .KUBECONFIG에 여러 콘텍스트가 있을 수 있으므로, 가능한 모든 클러스터를 확인한다.
kubectl config view -o jsonpath='{"Cluster name\tServer\n"}{range .clusters[*]}{.name}{"\t"}{.cluster.server}{"\n"}{end}'

# 위의 출력에서 상호 작용하려는 클러스터의 이름을 선택한다.
export CLUSTER_NAME="some_server_name"

# 클러스터 이름을 참조하는 API 서버를 가리킨다.
APISERVER=$(kubectl config view -o jsonpath="{.clusters[?(@.name==\"$CLUSTER_NAME\")].cluster.server}")

# 기본 서비스 어카운트용 토큰을 보관할 시크릿을 생성한다.
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: default-token
  annotations:
    kubernetes.io/service-account.name: default
type: kubernetes.io/service-account-token
EOF

# 토큰 컨트롤러가 해당 시크릿에 토큰을 기록할 때까지 기다린다.
while ! kubectl describe secret default-token | grep -E '^token' >/dev/null; do
  echo "waiting for token..." >&2
  sleep 1
done

# 토큰 값을 얻는다
TOKEN=$(kubectl get secret default-token -o jsonpath='{.data.token}' | base64 --decode)

# TOKEN으로 API 탐색
curl -X GET $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure

출력은 다음과 비슷하다.

{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

위의 예는 --insecure 플래그를 사용한다. 이로 인해 MITM 공격이 발생할 수 있다. kubectl이 클러스터에 접근하면 저장된 루트 인증서와 클라이언트 인증서를 사용하여 서버에 접근한다. (~/.kube 디렉터리에 설치된다.) 클러스터 인증서는 일반적으로 자체 서명되므로, http 클라이언트가 루트 인증서를 사용하도록 하려면 특별한 구성이 필요할 수 있다.

일부 클러스터에서, API 서버는 인증이 필요하지 않다. 로컬 호스트에서 제공되거나, 방화벽으로 보호될 수 있다. 이에 대한 표준은 없다. 쿠버네티스 API에 대한 접근 제어는 클러스터 관리자로서 이를 구성하는 방법에 대해 설명한다. 이러한 접근 방식은 향후 고 가용성 지원과 충돌할 수 있다.

API에 프로그래밍 방식으로 접근

쿠버네티스는 공식적으로 Go, Python, Java, dotnet, JavaScriptHaskell 용 클라이언트 라이브러리를 지원한다. 쿠버네티스 팀이 아닌 작성자가 제공하고 유지 관리하는 다른 클라이언트 라이브러리가 있다. 다른 언어에서 API에 접근하고 인증하는 방법에 대해서는 클라이언트 라이브러리를 참고한다.

Go 클라이언트

  • 라이브러리를 얻으려면, 다음 명령을 실행한다. go get k8s.io/client-go@kubernetes-<kubernetes-version-number> 어떤 버전이 지원되는지를 확인하려면 https://github.com/kubernetes/client-go/releases를 참고한다.
  • client-go 클라이언트 위에 애플리케이션을 작성한다.

Go 클라이언트는 kubectl CLI가 API 서버를 찾아 인증하기 위해 사용하는 것과 동일한 kubeconfig 파일을 사용할 수 있다. 이 예제를 참고한다.

package main

import (
  "context"
  "fmt"
  "k8s.io/apimachinery/pkg/apis/meta/v1"
  "k8s.io/client-go/kubernetes"
  "k8s.io/client-go/tools/clientcmd"
)

func main() {
  // kubeconfig에서 현재 콘텍스트를 사용한다
  // path-to-kubeconfig -- 예를 들어, /root/.kube/config
  config, _ := clientcmd.BuildConfigFromFlags("", "<path-to-kubeconfig>")
  // clientset을 생성한다
  clientset, _ := kubernetes.NewForConfig(config)
  // 파드를 나열하기 위해 API에 접근한다
  pods, _ := clientset.CoreV1().Pods("").List(context.TODO(), v1.ListOptions{})
  fmt.Printf("There are %d pods in the cluster\n", len(pods.Items))
}

애플리케이션이 클러스터 내의 파드로 배치된 경우, 파드 내에서 API 접근을 참고한다.

Python 클라이언트

Python 클라이언트를 사용하려면, 다음 명령을 실행한다. pip install kubernetes 추가 설치 옵션은 Python Client Library 페이지를 참고한다.

Python 클라이언트는 kubectl CLI가 API 서버를 찾아 인증하기 위해 사용하는 것과 동일한 kubeconfig 파일을 사용할 수 있다. 이 예제를 참고한다.

from kubernetes import client, config

config.load_kube_config()

v1=client.CoreV1Api()
print("Listing pods with their IPs:")
ret = v1.list_pod_for_all_namespaces(watch=False)
for i in ret.items:
    print("%s\t%s\t%s" % (i.status.pod_ip, i.metadata.namespace, i.metadata.name))

Java 클라이언트

Java 클라이언트를 설치하려면, 다음을 실행한다.

# java 라이브러리를 클론한다
git clone --recursive https://github.com/kubernetes-client/java

# 프로젝트 아티팩트, POM 등을 설치한다
cd java
mvn install

어떤 버전이 지원되는지를 확인하려면 https://github.com/kubernetes-client/java/releases를 참고한다.

Java 클라이언트는 kubectl CLI가 API 서버를 찾아 인증하기 위해 사용하는 것과 동일한 kubeconfig 파일을 사용할 수 있다. 이 예제를 참고한다.

package io.kubernetes.client.examples;

import io.kubernetes.client.ApiClient;
import io.kubernetes.client.ApiException;
import io.kubernetes.client.Configuration;
import io.kubernetes.client.apis.CoreV1Api;
import io.kubernetes.client.models.V1Pod;
import io.kubernetes.client.models.V1PodList;
import io.kubernetes.client.util.ClientBuilder;
import io.kubernetes.client.util.KubeConfig;
import java.io.FileReader;
import java.io.IOException;

/**
 * 쿠버네티스 클러스터 외부의 애플리케이션에서 Java API를 사용하는 방법에 대한 간단한 예
 *
 * <p>이것을 실행하는 가장 쉬운 방법: mvn exec:java
 * -Dexec.mainClass="io.kubernetes.client.examples.KubeConfigFileClientExample"
 *
 */
public class KubeConfigFileClientExample {
  public static void main(String[] args) throws IOException, ApiException {

    // KubeConfig의 파일 경로
    String kubeConfigPath = "~/.kube/config";

    // 파일시스템에서 클러스터 외부 구성인 kubeconfig 로드
    ApiClient client =
        ClientBuilder.kubeconfig(KubeConfig.loadKubeConfig(new FileReader(kubeConfigPath))).build();

    // 전역 디폴트 api-client를 위에서 정의한 클러스터 내 클라이언트로 설정
    Configuration.setDefaultApiClient(client);

    // CoreV1Api는 전역 구성에서 디폴트 api-client를 로드
    CoreV1Api api = new CoreV1Api();

    // CoreV1Api 클라이언트를 호출한다
    V1PodList list = api.listPodForAllNamespaces(null, null, null, null, null, null, null, null, null);
    System.out.println("Listing all pods: ");
    for (V1Pod item : list.getItems()) {
      System.out.println(item.getMetadata().getName());
    }
  }
}

dotnet 클라이언트

dotnet 클라이언트를 사용하려면, 다음 명령을 실행한다. dotnet add package KubernetesClient --version 1.6.1 추가 설치 옵션은 dotnet Client Library 페이지를 참고한다. 어떤 버전이 지원되는지를 확인하려면 https://github.com/kubernetes-client/csharp/releases를 참고한다.

dotnet 클라이언트는 kubectl CLI가 API 서버를 찾아 인증하기 위해 사용하는 것과 동일한 kubeconfig 파일을 사용할 수 있다. 이 예제를 참고한다.

using System;
using k8s;

namespace simple
{
    internal class PodList
    {
        private static void Main(string[] args)
        {
            var config = KubernetesClientConfiguration.BuildDefaultConfig();
            IKubernetes client = new Kubernetes(config);
            Console.WriteLine("Starting Request!");

            var list = client.ListNamespacedPod("default");
            foreach (var item in list.Items)
            {
                Console.WriteLine(item.Metadata.Name);
            }
            if (list.Items.Count == 0)
            {
                Console.WriteLine("Empty!");
            }
        }
    }
}

JavaScript 클라이언트

JavaScript 클라이언트를 설치하려면, 다음 명령을 실행한다. npm install @kubernetes/client-node 어떤 버전이 지원되는지를 확인하려면 https://github.com/kubernetes-client/javascript/releases를 참고한다.

JavaScript 클라이언트는 kubectl CLI가 API 서버를 찾아 인증하기 위해 사용하는 것과 동일한 kubeconfig 파일을 사용할 수 있다. 이 예제를 참고한다.

const k8s = require('@kubernetes/client-node');

const kc = new k8s.KubeConfig();
kc.loadFromDefault();

const k8sApi = kc.makeApiClient(k8s.CoreV1Api);

k8sApi.listNamespacedPod('default').then((res) => {
    console.log(res.body);
});

Haskell 클라이언트

어떤 버전이 지원되는지를 확인하려면 https://github.com/kubernetes-client/haskell/releases를 참고한다.

Haskell 클라이언트는 kubectl CLI가 API 서버를 찾아 인증하기 위해 사용하는 것과 동일한 kubeconfig 파일을 사용할 수 있다. 이 예제를 참고한다.

exampleWithKubeConfig :: IO ()
exampleWithKubeConfig = do
    oidcCache <- atomically $ newTVar $ Map.fromList []
    (mgr, kcfg) <- mkKubeClientConfig oidcCache $ KubeConfigFile "/path/to/kubeconfig"
    dispatchMime
            mgr
            kcfg
            (CoreV1.listPodForAllNamespaces (Accept MimeJSON))
        >>= print

다음 내용

7 - DNS 서비스 사용자 정의하기

이 페이지는 클러스터 안에서 사용자의 DNS 파드(Pod) 를 설정하고 DNS 변환(DNS resolution) 절차를 사용자 정의하는 방법을 설명한다.

시작하기 전에

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

클러스터는 CoreDNS 애드온을 구동하고 있어야 한다.

쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.12. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

소개

DNS는 애드온 관리자클러스터 애드온을 사용하여 자동으로 시작되는 쿠버네티스 내장 서비스이다.

CoreDNS를 디플로이먼트(Deployment)로 실행하고 있을 경우, 일반적으로 고정 IP 주소를 갖는 쿠버네티스 서비스로 노출된다. Kubelet 은 --cluster-dns=<dns-service-ip> 플래그를 사용하여 DNS 확인자 정보를 각 컨테이너에 전달한다.

DNS 이름에도 도메인이 필요하다. 사용자는 kubelet 에 있는 --cluster-domain=<default-local-domain> 플래그를 통하여 로컬 도메인을 설정할 수 있다.

DNS 서버는 정방향 조회(A 및 AAAA 레코드), 포트 조회(SRV 레코드), 역방향 IP 주소 조회(PTR 레코드) 등을 지원한다. 더 자세한 내용은 서비스 및 파드용 DNS를 참고한다.

만약 파드의 dnsPolicydefault 로 지정되어 있는 경우, 파드는 자신이 실행되는 노드의 이름 변환(name resolution) 구성을 상속한다. 파드의 DNS 변환도 노드와 동일하게 작동해야 한다. 그 외에는 알려진 이슈를 참고한다.

만약 위와 같은 방식을 원하지 않거나, 파드를 위해 다른 DNS 설정이 필요한 경우, 사용자는 kubelet 의 --resolv-conf 플래그를 사용할 수 있다. 파드가 DNS를 상속받지 못하도록 하기 위해 이 플래그를 ""로 설정한다. DNS 상속을 위해 /etc/resolv.conf 이외의 파일을 지정할 경우 유효한 파일 경로를 설정한다.

CoreDNS

CoreDNS는 dns 명세를 준수하며 클러스터 DNS 역할을 할 수 있는, 범용적인 권한을 갖는 DNS 서버이다.

CoreDNS 컨피그맵(ConfigMap) 옵션

CoreDNS는 모듈형이자 플러그인이 가능한 DNS 서버이며, 각 플러그인들은 CoreDNS에 새로운 기능을 부가한다. CoreDNS 서버는 CoreDNS 구성 파일인 Corefile을 관리하여 구성할 수 있다. 클러스터 관리자는 CoreDNS Corefile에 대한 컨피그맵을 수정하여 해당 클러스터에 대한 DNS 서비스 검색 동작을 변경할 수 있다.

쿠버네티스에서 CoreDNS는 아래의 기본 Corefile 구성으로 설치된다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health {
            lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
            pods insecure
            fallthrough in-addr.arpa ip6.arpa
            ttl 30
        }
        prometheus :9153
        forward . /etc/resolv.conf
        cache 30
        loop
        reload
        loadbalance
    }    

Corefile의 구성은 CoreDNS의 아래 플러그인을 포함한다.

  • errors: 오류가 표준 출력(stdout)에 기록된다.
  • health: CoreDNS의 상태(healthy)가 http://localhost:8080/health 에 기록된다. 이 확장 구문에서 lameduck 은 프로세스를 비정상 상태(unhealthy)로 만들고, 프로세스가 종료되기 전에 5초 동안 기다린다.
  • ready: 8181 포트의 HTTP 엔드포인트가, 모든 플러그인이 준비되었다는 신호를 보내면 200 OK 를 반환한다.
  • kubernetes: CoreDNS가 쿠버네티스의 서비스 및 파드의 IP를 기반으로 DNS 쿼리에 대해 응답한다. 해당 플러그인에 대한 세부 사항은 CoreDNS 웹사이트에서 확인할 수 있다.
    • ttl 을 사용하면 응답에 대한 사용자 정의 TTL 을 지정할 수 있으며, 기본값은 5초이다. 허용되는 최소 TTL은 0초이며, 최대값은 3600초이다. 레코드가 캐싱되지 않도록 할 경우, TTL을 0으로 설정한다.
    • pods insecure 옵션은 kube-dns 와의 하위 호환성을 위해 제공된다.
    • pods verified 옵션을 사용하여, 일치하는 IP의 동일 네임스페이스(Namespace)에 파드가 존재하는 경우에만 A 레코드를 반환하게 할 수 있다.
    • pods disabled 옵션은 파드 레코드를 사용하지 않을 경우 사용된다.
  • prometheus: CoreDNS의 메트릭은 프로메테우스 형식(OpenMetrics 라고도 알려진)의 http://localhost:9153/metrics 에서 사용 가능하다.
  • forward: 쿠버네티스 클러스터 도메인에 없는 쿼리들은 모두 사전에 정의된 리졸버(/etc/resolv.conf)로 전달된다.
  • cache: 프론트 엔드 캐시를 활성화한다.
  • loop: 간단한 전달 루프(loop)를 감지하고, 루프가 발견되면 CoreDNS 프로세스를 중단(halt)한다.
  • reload: 변경된 Corefile을 자동으로 다시 로드하도록 한다. 컨피그맵 설정을 변경한 후에 변경 사항이 적용되기 위하여 약 2분정도 소요된다.
  • loadbalance: 응답에 대하여 A, AAAA, MX 레코드의 순서를 무작위로 선정하는 라운드-로빈 DNS 로드밸런서이다.

사용자는 컨피그맵을 변경하여 기본 CoreDNS 동작을 변경할 수 있다.

CoreDNS를 사용하는 스텁 도메인(Stub-domain)과 업스트림 네임서버(nameserver)의 설정

CoreDNS는 포워드 플러그인을 사용하여 스텁 도메인 및 업스트림 네임서버를 구성할 수 있다.

예시

만약 클러스터 운영자가 10.150.0.1 에 위치한 Consul 도메인 서버를 가지고 있고, 모든 Consul 이름의 접미사가 .consul.local 인 경우, CoreDNS에서 이를 구성하기 위해 클러스터 관리자는 CoreDNS 컨피그맵에서 다음 구문을 생성한다.

consul.local:53 {
    errors
    cache 30
    forward . 10.150.0.1
}

모든 비 클러스터의 DNS 조회가 172.16.0.1 의 특정 네임서버를 통과하도록 할 경우, /etc/resolv.conf 대신 forward 를 네임서버로 지정한다.

forward .  172.16.0.1

기본 Corefile 구성에 따른 최종 컨피그맵은 다음과 같다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
        }
        prometheus :9153
        forward . 172.16.0.1
        cache 30
        loop
        reload
        loadbalance
    }
    consul.local:53 {
        errors
        cache 30
        forward . 10.150.0.1
    }    

다음 내용

8 - kubelet 이미지 자격 증명 공급자 구성하기

kubelet의 이미지 자격 증명 공급자 플러그인을 구성한다.
기능 상태: Kubernetes v1.26 [stable]

쿠버네티스 v1.20부터 kubelet은 exec 플러그인을 사용하여 컨테이너 이미지 레지스트리에 대한 자격 증명(credential)을 동적으로 검색할 수 있다. kubelet과 exec 플러그인은 쿠버네티스 버전 API를 사용하여 stdio(stdin, stdout, stderr)를 통해 통신한다. kubelet은 플러그인을 통해 정적 자격 증명을 디스크에 저장하는 대신 컨테이너 레지스트리에 대한 자격 증명을 동적으로 요청할 수 있다. 예를 들어, 플러그인은 로컬 메타데이터 서버와 통신하여 kubelet에 의해 풀(pulled) 된 이미지에 대한 단기 유효(short-lived) 자격 증명을 검색할 수 있다.

아래 중 하나에 해당하면 이 기능의 사용을 고려해도 좋다.

  • 레지스트리에 대한 인증 정보를 찾기 위해 클라우드 공급자 서비스에 대한 API 호출이 필요할 때.
  • 자격 증명 만료 시간이 짧아 새 자격 증명 요청이 자주 필요할 때.
  • 레지스트리 자격 증명을 디스크 또는 이미지 풀 시크릿에 저장하는 것을 허용하지 않을 때.

이 가이드는 kubelet의 이미지 자격 증명 공급자 플러그인 메커니즘을 구성하는 방법을 보여 준다.

시작하기 전에

  • kubelet 자격 증명 공급자 플러그인을 지원하는 노드로 구성된 쿠버네티스 클러스터가 필요하다. 이 기능은 쿠버네티스 1.30에서 사용 가능하다. 쿠버네티스 v1.24 및 v1.25에는 베타 기능으로 포함되었으며, 기본적으로 활성화되어 있다.
  • 자격 증명 공급자 exec 플러그인에 대한 구현체(implementation)가 필요하다. 이를 위해 자체 플러그인을 구축하거나 클라우드 공급자가 제공하는 플러그인을 사용할 수 있다.
쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.26. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

노드에 플러그인 설치하기

자격 증명 공급자 플러그인은 kubelet에 의해 실행될 실행 가능한 바이너리이다. 클러스터의 모든 노드에 플러그인 바이너리가 있고 알려진 디렉터리에 저장됐는지 확인한다. 이후 kubelet 플래그를 구성할 때 해당 디렉터리가 필요하다.

kubelet 구성하기

이 기능을 사용하려면 kubelet에 두 개의 플래그가 설정돼야 한다.

  • --image-credential-provider-config - 자격 증명 공급자 플러그인 구성 파일 경로.
  • --image-credential-provider-bin-dir - 자격 증명 공급자 플러그인 바이너리 파일이 있는 디렉터리 경로.

kubelet 자격 증명 공급자 구성

kubelet은 --image-credential-provider-config로 전달된 구성 파일을 읽고, 컨테이너 이미지에 대해 어떤 exec 플러그인을 호출할지 결정한다. ECR-based 플러그인을 사용하는 경우 사용하게 될 수 있는 구성 파일의 예:

apiVersion: kubelet.config.k8s.io/v1
kind: CredentialProviderConfig
# providers 필드는 kubelet이 활성화할 자격 증명 공급자 헬퍼 플러그인의 목록을 나타낸다. 
# 단일 이미지에 대해 복수 공급자가 매치될 수도 있으며, 
# 이러한 경우 모든 공급자의 자격 증명이 kubelet으로 리턴된다. 
# 단일 이미지에 대해 복수 공급자가 호출된 경우, 결과가 합산된다. 
# 공급자가 중복되는(overlapping) 인증 키를 리턴한 경우, 이 목록의 위쪽에 위치하는 공급자로부터의 값이 사용된다.
providers:
  # name 필드는 자격 증명 공급자를 구분하기 위한 필수 필드이다. 
  # 이 이름은 kubelet이 인식하는 공급자 실행 파일의 이름과 일치해야 한다. 
  # 해당 실행 파일은 kubelet의 bin 디렉토리에 존재해야 한다(--image-credential-provider-bin-dir 플래그로 설정).
  - name: ecr
    # matchImages 필드는 각 이미지에 대해 이 공급자가 활성화되어야 하는지를 
    # 판단하기 위한 문자열의 목록을 나타내는 필수 필드이다. 
    # kubelet이 요청한 이미지가 다음 문자열 중 하나와 매치되면, 
    # 해당 플러그인이 활성화되어 자격 증명을 제공할 수 있게 된다. 
    # 이미지 태그 문자열은 저장소(registry) 도메인 및 URL 경로를 포함해야 한다.
    #
    # matchImages의 각 항목은 패턴을 나타내며, 포트와 경로를 포함할 수 있다. 
    # 도메인 자리에 글롭(glob)도 사용할 수 있으나, 포트와 경로에는 사용할 수 없다. 
    # 글롭은 '*.k8s.io' 또는 'k8s.*.io'와 같이 서브도메인 형태로 사용하거나, 'k8s.*'와 같이 최상위 도메인 형태로 사용할 수 있다. 
    # 'app*.k8s.io'와 같이 서브도메인의 일부를 매칭하는 것도 지원된다. 
    # 각 글롭은 단일 서브도메인 분할만을 매칭할 수 있으므로, `*.io`는 `*.k8s.io`에 매치되지 **않는다**.
    #
    # 다음 사항이 모두 만족될 때에만 image와 matchImage가 매치되었다고 판단한다.
    # - 양쪽의 도메인 파트 수가 동일하고, 각 파트가 매치됨
    # - imageMatch의 URL 경로가 타겟 이미지 URL 경로의 접두사임
    # - imageMatch가 포트를 포함하면, 이미지 쪽에 기재된 포트와 매치됨
    #
    # matchImages 예시는 다음과 같다.
    # - 123456789.dkr.ecr.us-east-1.amazonaws.com
    # - *.azurecr.io
    # - gcr.io
    # - *.*.registry.io
    # - registry.io:8080/path
    matchImages:
      - "*.dkr.ecr.*.amazonaws.com"
      - "*.dkr.ecr.*.amazonaws.cn"
      - "*.dkr.ecr-fips.*.amazonaws.com"
      - "*.dkr.ecr.us-iso-east-1.c2s.ic.gov"
      - "*.dkr.ecr.us-isob-east-1.sc2s.sgov.gov"
    # defaultCacheDuration 필드는 캐시 기간이 플러그인 응답에 명시되지 않은 경우에 
    # 해당 플러그인이 자격 증명을 인메모리 캐시에 보관할 기본 기간을 지정한다. 이 필드는 필수이다.
    defaultCacheDuration: "12h"
    # apiVersion 필드는 CredentialProviderRequest를 실행할 때 기재될 입력 버전을 지정하는 필수 필드이다. 
    # 응답 CredentialProviderResponse는 입력과 동일한 인코딩 버전을 사용해야 한다. 현재 지원되는 값은 다음과 같다.
    # - credentialprovider.kubelet.k8s.io/v1
    apiVersion: credentialprovider.kubelet.k8s.io/v1
    # args 필드는 커맨드를 실행할 때 전달할 인자를 지정하는 필드이다.
    # 이 필드는 선택사항이다.
    args:
      - get-credentials
    # env 필드는 프로세스에 노출할 추가적인 환경 변수를 기재하는 필드이다. 
    # 이들은 호스트의 환경 변수 및 
    # client-go가 플러그인에 인자를 전달하기 위해 사용하는 변수와 합산된다.
    # 이 필드는 선택사항이다.
    env:
      - name: AWS_PROFILE
        value: example_profile

providers 필드는 kubelet에서 사용되는 활성화된 플러그인의 목록으로, 각 항목에는 몇 가지 필수 필드가 있다.

  • name: --image-credential-provider-bin-dir로 전달된 디렉터리에 존재하는 실행 가능한 바이너리의 이름과 반드시 일치해야 하는 플러그인의 이름.
  • matchImages: 이 공급자를 호출할지 결정하기 위해 이미지를 대조하는 데 사용되는 문자열 목록. 아래의 더 많은 내용 참조.
  • defaultCacheDuration: 플러그인에 의해 캐시 기간이 지정되지 않으면, kubelet이 메모리에 자격 증명을 캐시하는 기본 기간.
  • apiVersion: kubelet과 exec 플러그인이 통신할 때 사용하는 API 버전.

각 자격 증명 공급자에게 인수(arg) 및 환경 변수도 선택적으로 제공할 수 있다. 플러그인에 필요한 인수 및 환경 변수 집합을 확인하려면 해당 플러그인 구현자에게 문의하는 것이 좋다.

이미지 매칭 구성

kubelet은 각 자격 증명 공급자에 대한 matchImages 필드를 사용해 파드가 사용하고 있는 특정 이미지에 대해 플러그인을 호출할지 결정한다. Globs는 도메인에서 사용할 수 있지만 포트나 경로에서는 사용할 수 없다. Glob은 *.k8s.io이나 k8s.*.io 같은 서브도메인과 k8s.*와 같은 최상위 도메인으로 지원된다. 또한, app*.k8s.io와 같은 부분 서브도메인을 매칭하는 것도 지원된다. 각 Glob은 단일 하위 도메인 세그먼트만 일치할 수 있으므로 *.io*.k8s.io과 일치하지 않는다.

아래와 같은 경우 이미지 이름과 matchImage 항목 사이에 매치가 존재한다.

  • 둘 다 동일한 수의 도메인 파트를 포함하고 있으며 각 파트가 매치한다.
  • 매치 이미지의 URL 경로는 대상 이미지 URL 경로의 접두사여야 한다.
  • imageMatch에 포트가 포함되어 있으면 해당 포트는 이미지에서도 매치해야 한다.

matchImages 패턴의 예시 값은 아래와 같다.

  • 123456789.dkr.ecr.us-east-1.amazonaws.com
  • *.azurecr.io
  • gcr.io
  • *.*.registry.io
  • foo.registry.io:8080/path

다음 내용

9 - 구성 파일을 통해 Kubelet 파라미터 설정하기

커맨드 라인 플래그 대신 디스크 상의 구성 파일을 통해 Kubelet의 구성 파라미터 하위 집합을 설정할 수 있다.

구성 파일을 통해 파라미터를 제공하는 것은 노드 배포 및 구성 관리를 간소화하기 때문에 권장되는 접근 방식이다.

구성 파일 만들기

파일을 통해 구성할 수 있는 Kubelet 구성의 하위 집합은 KubeletConfiguration 구조체에 의해 정의된다.

구성 파일은 이 구조체의 파라미터를 반드시 JSON 또는 YAML로 표현한 파일이어야 한다. Kubelet이 파일에 읽기 권한이 있는지 확인한다.

다음은 이러한 파일에 대한 예시를 보여준다.

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
address: "192.168.0.8",
port: 20250,
serializeImagePulls: false,
evictionHard:
    memory.available:  "200Mi"

이 예제에서, Kubelet은 192.168.0.8 IP 주소와 20250 포트에서 동작하고, 이미지를 병렬로 가져오고, 사용 가능 메모리가 200Mi 아래로 떨어지면 파드를 축출하도록 구성되어 있다. 플래그에 의해 재정의(overridden)되지 않는한, 다른 모든 Kubelet 구성은 기본 제공 기본값으로 유지된다. 구성 파일과 동일한 값을 대상으로 하는 커맨드 라인 플래그는 해당 값을 재정의 한다.

구성 파일을 통해 구성된 Kubelet 프로세스 시작하기

Kubelet의 구성 파일 경로로 설정된 --config 플래그를 사용하여 Kubelet을 시작하면 Kubelet이 이 파일에서 구성을 불러온다.

구성 파일과 동일한 값을 대상으로 하는 커맨드 라인 플래그는 해당 값을 재정의한다는 점을 유의한다. 이렇게 하면 커맨드 라인 API와의 이전 버전과의 호환성을 보장할 수 있다.

Kubelet 구성 파일의 상대 파일 경로는 Kubelet 구성 파일의 위치를 기준으로 확인되는 반면, 커맨드 라인 플래그의 상대 경로는 Kubelet의 현재 작업 디렉터리를 기준으로 확인된다는 점에 유의한다.

일부 기본값은 커맨드 라인 플래그와 Kubelet 구성 파일 간에 다르다는 점에 유의한다. --config가 제공되고 명령줄을 통해 값을 지정하지 않은 경우, KubeletConfiguration 버전의 기본값이 적용된다. 위 예제의 버전은 kubelet.config.k8s.io/v1beta1이다.

다음 내용

10 - 기본 스토리지클래스(StorageClass) 변경하기

이 페이지는 특별한 요구사항이 없는 퍼시스턴트볼륨클레임(PersistentVolumeClaim)의 볼륨을 프로비저닝 하는데 사용되는 기본 스토리지 클래스를 변경하는 방법을 보여준다.

시작하기 전에

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

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

왜 기본 스토리지 클래스를 변경하는가?

설치 방법에 따라, 사용자의 쿠버네티스 클러스터는 기본으로 표시된 기존 스토리지클래스와 함께 배포될 수 있다. 이 기본 스토리지클래스는 특정 스토리지 클래스가 필요하지 않은 퍼시스턴트볼륨클레임에 대해 스토리지를 동적으로 프로비저닝 하기 위해 사용된다. 더 자세한 내용은 퍼시스턴트볼륨클레임 문서를 보자.

미리 설치된 기본 스토리지클래스가 사용자의 예상되는 워크로드에 적합하지 않을수도 있다. 예를 들어, 너무 가격이 높은 스토리지를 프로비저닝 해야할 수도 있다. 이런 경우에, 기본 스토리지 클래스를 변경하거나 완전히 비활성화 하여 스토리지의 동적 프로비저닝을 방지할 수 있다.

기본 스토리지클래스를 삭제하는 경우, 사용자의 클러스터에서 구동 중인 애드온 매니저에 의해 자동으로 다시 생성될 수 있으므로 정상적으로 삭제가 되지 않을 수도 있다. 애드온 관리자 및 개별 애드온을 비활성화 하는 방법에 대한 자세한 내용은 설치 문서를 참조하자.

기본 스토리지클래스 변경하기

  1. 사용자의 클러스터에 있는 스토리지클래스 목록을 조회한다.

    kubectl get storageclass
    

    결과는 아래와 유사하다.

    NAME                 PROVISIONER               AGE
    standard (default)   kubernetes.io/gce-pd      1d
    gold                 kubernetes.io/gce-pd      1d
    

    기본 스토리지클래스는 (default) 로 표시되어 있다.

  2. 기본 스토리지클래스를 기본값이 아닌 것으로 표시한다.

    기본 스토리지클래스에는 storageclass.kubernetes.io/is-default-class 의 값이 true 로 설정되어 있다. 다른 값이거나 어노테이션이 없을 경우 false 로 처리된다.

    스토리지클래스를 기본값이 아닌 것으로 표시하려면, 그 값을 false 로 변경해야 한다.

    kubectl patch storageclass standard -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
    

    여기서 standard 는 사용자가 선택한 스토리지클래스의 이름이다.

  3. 스토리지클래스를 기본값으로 표시한다.

    이전 과정과 유사하게, 어노테이션을 추가/설정해야 한다. storageclass.kubernetes.io/is-default-class=true.

    kubectl patch storageclass gold -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
    

    최대 1개의 스토리지클래스를 기본값으로 표시할 수 있다는 것을 알아두자. 만약 2개 이상이 기본값으로 표시되면, 명시적으로 storageClassName 가 지정되지 않은 PersistentVolumeClaim 은 생성될 수 없다.

  4. 사용자가 선택한 스토리지클래스가 기본값으로 되어 있는지 확인한다.

    kubectl get storageclass
    

    결과는 아래와 유사하다.

    NAME             PROVISIONER               AGE
    standard         kubernetes.io/gce-pd      1d
    gold (default)   kubernetes.io/gce-pd      1d
    

다음 내용

11 - 네임스페이스를 사용해 클러스터 공유하기

이 페이지는 네임스페이스를 살펴보고, 작업하고, 삭제하는 방법에 대해 다룬다. 또한 쿠버네티스 네임스페이스를 사용해 클러스터를 세분화하는 방법에 대해서도 다룬다.

시작하기 전에

네임스페이스 보기

  1. 아래 명령어를 사용해 클러스터의 현재 네임스페이스를 나열한다.
kubectl get namespaces
NAME          STATUS    AGE
default       Active    11d
kube-system   Active    11d
kube-public   Active    11d

쿠버네티스를 시작하면 세 개의 초기 네임스페이스가 있다.

  • default 다른 네임스페이스가 없는 오브젝트를 위한 기본 네임스페이스
  • kube-system 쿠버네티스 시스템에서 생성된 오브젝트의 네임스페이스
  • kube-public 이 네임스페이스는 자동으로 생성되며 모든 사용자(미인증 사용자를 포함)가 읽을 수 있다. 이 네임스페이스는 일부 리소스를 공개적으로 보고 읽을 수 있어야 하는 경우에 대비하여 대부분이 클러스터 사용을 위해 예약돼 있다. 그러나 이 네임스페이스의 공개적인 성격은 관례일 뿐 요구 사항은 아니다.

아래 명령을 실행해 특정 네임스페이스에 대한 요약 정보를 볼 수 있다.

kubectl get namespaces <name>

자세한 정보를 보는 것도 가능하다.

kubectl describe namespaces <name>
Name:           default
Labels:         <none>
Annotations:    <none>
Status:         Active

No resource quota.

Resource Limits
 Type       Resource    Min Max Default
 ----               --------    --- --- ---
 Container          cpu         -   -   100m

이러한 세부 정보에는 리소스 한도(limit) 범위 뿐만 아니라 리소스 쿼터(만약 있다면)까지 모두 표시된다.

리소스 쿼터는 네임스페이스 내 리소스의 집계 사용량을 추적하며, 네임스페이스에서 사용할 수 있는 하드(Hard) 리소스 사용 제한을 클러스터 운영자가 정의할 수 있도록 해준다.

제한 범위는 하나의 엔티티(entity)가 하나의 네임스페이스에서 사용할 수 있는 리소스 양에 대한 최대/최소 제약 조건을 정의한다.

어드미션 컨트롤: 리밋 레인지(Limit Range)를 참조하자.

네임스페이스는 다음 두 상태 중 하나에 있을 수 있다.

  • Active 네임스페이스가 사용 중이다.
  • Terminating 네임스페이스가 삭제 중이므로 새 오브젝트에 사용할 수 없다.

자세한 내용은 API 레퍼런스의 네임스페이스를 참조한다.

새 네임스페이스 생성하기

  1. my-namespace.yaml이라는 YAML 파일을 생성하고 아래 내용을 작성한다.

    apiVersion: v1
    kind: Namespace
    metadata:
      name: <insert-namespace-name-here>
    

    다음 명령을 실행한다.

    kubectl create -f ./my-namespace.yaml
    
  2. 아래 명령으로 네임스페이스를 생성할 수도 있다.

    kubectl create namespace <insert-namespace-name-here>
    

네임스페이스의 이름은 유효한 DNS 레이블이어야 한다.

옵션 필드인 finalizer는 네임스페이스가 삭제 될 때 관찰자가 리소스를 제거할 수 있도록 한다. 존재하지 않는 파이널라이저(finalizer)를 명시한 경우 네임스페이스는 생성되지만 사용자가 삭제하려 하면 Terminating 상태가 된다.

파이널라이저에 대한 자세한 내용은 네임스페이스 디자인 문서에서 확인할 수 있다.

네임스페이스 삭제하기

다음 명령을 실행해 네임스페이스를 삭제한다.

kubectl delete namespaces <insert-some-namespace-name>

삭제는 비동기적이므로 삭제 후 한동안은 네임스페이스의 상태가 Terminating으로 보일 것이다.

쿠버네티스 네임스페이스를 사용해 클러스터 세분화하기

  1. 기본 네임스페이스 이해하기

    기본적으로 쿠버네티스 클러스터는 클러스터에서 사용할 기본 파드, 서비스, 그리고 디플로이먼트(Deployment) 집합을 가지도록 클러스터를 프로비저닝 할 때 기본 네임스페이스를 인스턴스화한다.

    새 클러스터가 있다고 가정하고 아래 명령을 수행하면 사용 가능한 네임스페이스를 볼 수 있다.

    kubectl get namespaces
    
    NAME      STATUS    AGE
    default   Active    13m
    
  2. 새 네임스페이스 생성하기

    이 예제에서는 내용을 저장할 쿠버네티스 네임스페이스를 추가로 두 개 생성할 것이다.

    개발과 프로덕션 유스케이스에서 공유 쿠버네티스 클러스터를 사용하는 조직이 있다고 가정하자.

    개발 팀은 애플리케이션을 구축하고 실행하는데 사용하는 파드, 서비스, 디플로이먼트의 목록을 볼 수 있는 공간을 클러스터에 유지하려 한다. 이 공간에서는 쿠버네티스 리소스가 자유롭게 추가 및 제거되고, 누가 리소스를 수정할 수 있는지 없는지에 대한 제약이 완화돼 빠른 개발이 가능해진다.

    운영 팀은 운영 사이트를 실행하는 파드, 서비스, 디플로이먼트 집합을 조작할 수 있는 사람과 그렇지 않은 사람들에 대해 엄격한 절차를 적용할 수 있는 공간을 클러스터에 유지하려 한다.

    이 조직이 따를 수 있는 한 가지 패턴은 쿠버네티스 클러스터를 development(개발)production(운영)이라는 두 개의 네임스페이스로 분할하는 것이다.

    우리의 작업을 보존하기 위해 새로운 네임스페이스 두 개를 만들자.

    kubectl을 사용해 development 네임스페이스를 생성한다.

    kubectl create -f https://k8s.io/examples/admin/namespace-dev.json
    

    그런 다음 kubectl을 사용해 production 네임스페이스를 생성한다.

    kubectl create -f https://k8s.io/examples/admin/namespace-prod.json
    

    제대로 생성이 되었는지 확인하기 위해 클러스터 내의 모든 네임스페이스를 나열한다.

    kubectl get namespaces --show-labels
    
    NAME          STATUS    AGE       LABELS
    default       Active    32m       <none>
    development   Active    29s       name=development
    production    Active    23s       name=production
    
  3. 네임스페이스마다 파드 생성

    쿠버네티스 네임스페이스는 클러스터의 파드, 서비스 그리고 디플로이먼트의 범위를 제공한다.

    하나의 네임스페이스와 상호 작용하는 사용자는 다른 네임스페이스의 내용을 볼 수 없다.

    이를 보여주기 위해 development 네임스페이스에 간단한 디플로이먼트와 파드를 생성하자.

    kubectl create deployment snowflake --image=registry.k8s.io/serve_hostname  -n=development --replicas=2
    

    단순히 호스트명을 제공해주는 snowflake라는 파드의 개수를 2개로 유지하는 디플로이먼트를 생성하였다.

    kubectl get deployment -n=development
    
    NAME         READY   UP-TO-DATE   AVAILABLE   AGE
    snowflake    2/2     2            2           2m
    
    kubectl get pods -l app=snowflake -n=development
    
    NAME                         READY     STATUS    RESTARTS   AGE
    snowflake-3968820950-9dgr8   1/1       Running   0          2m
    snowflake-3968820950-vgc4n   1/1       Running   0          2m
    

    개발자들은 production 네임스페이스의 내용에 영향을 끼칠 걱정 없이 하고 싶은 것을 할 수 있으니 대단하지 않은가.

    이제 production 네임스페이스로 전환해 한 네임스페이스의 리소스가 다른 네임스페이스에서는 어떻게 숨겨지는지 보자.

    production 네임스페이스는 비어있어야 하며 아래 명령은 아무 것도 반환하지 않아야 한다.

    kubectl get deployment -n=production
    kubectl get pods -n=production
    

    프로덕션이 가축 키우는 것을 좋아하듯이, 우리도 production 네임스페이스에 cattle(가축)이라는 이름의 파드를 생성한다.

    kubectl create deployment cattle --image=registry.k8s.io/serve_hostname -n=production
    kubectl scale deployment cattle --replicas=5 -n=production
    
    kubectl get deployment -n=production
    
    NAME         READY   UP-TO-DATE   AVAILABLE   AGE
    cattle       5/5     5            5           10s
    
    kubectl get pods -l app=cattle -n=production
    
    NAME                      READY     STATUS    RESTARTS   AGE
    cattle-2263376956-41xy6   1/1       Running   0          34s
    cattle-2263376956-kw466   1/1       Running   0          34s
    cattle-2263376956-n4v97   1/1       Running   0          34s
    cattle-2263376956-p5p3i   1/1       Running   0          34s
    cattle-2263376956-sxpth   1/1       Running   0          34s
    

지금 쯤이면 사용자가 한 네임스페이스에 생성한 리소스는 다른 네임스페이스에서 숨겨져 있어야 한다는 것을 잘 알고 있을 것이다.

쿠버네티스 정책 지원이 발전함에 따라, 이 시나리오를 확장해 각 네임스페이스에 서로 다른 인증 규칙을 제공하는 방법을 보이도록 하겠다.

네임스페이스의 사용 동기 이해하기

단일 클러스터는 여러 사용자 및 사용자 그룹(이하 '사용자 커뮤니티')의 요구를 충족시킬 수 있어야 한다.

쿠버네티스 네임스페이스 는 여러 프로젝트, 팀 또는 고객이 쿠버네티스 클러스터를 공유할 수 있도록 지원한다.

이를 위해 다음을 제공한다.

  1. 이름에 대한 범위
  2. 인증과 정책을 클러스터의 하위 섹션에 연결하는 메커니즘

여러 개의 네임스페이스를 사용하는 것은 선택 사항이다.

각 사용자 커뮤니티는 다른 커뮤니티와 격리된 상태로 작업할 수 있기를 원한다.

각 사용자 커뮤니티는 다음을 가진다.

  1. 리소스 (파드, 서비스, 레플리케이션 컨트롤러(replication controller) 등
  2. 정책 (커뮤니티에서 조치를 수행할 수 있거나 없는 사람)
  3. 제약 조건 (해당 커뮤니티에서는 어느 정도의 쿼터가 허용되는지 등)

클러스터 운영자는 각 사용자 커뮤니티 마다 네임스페이스를 생성할 수 있다.

네임스페이스는 다음을 위한 고유한 범위를 제공한다.

  1. (기본 명명 충돌을 방지하기 위해) 명명된 리소스
  2. 신뢰할 수 있는 사용자에게 관리 권한 위임
  3. 커뮤니티 리소스 소비를 제한하는 기능

유스케이스는 다음을 포함한다.

  1. 클러스터 운영자로서 단일 클러스터에서 여러 사용자 커뮤니티를 지원하려 한다.
  2. 클러스터 운영자로서 클러스터 분할에 대한 권한을 해당 커뮤니티의 신뢰할 수 있는 사용자에게 위임하려 한다.
  3. 클러스터 운영자로서 클러스터를 사용하는 다른 커뮤니티에 미치는 영향을 제한하기 위해 각 커뮤니티가 사용할 수 있는 리소스의 양을 제한하고자 한다.
  4. 클러스터 사용자로서 다른 사용자 커뮤니티가 클러스터에서 수행하는 작업과는 별도로 사용자 커뮤니티와 관련된 리소스와 상호 작용하고 싶다.

네임스페이스와 DNS 이해하기

서비스를 생성하면 상응하는 DNS 엔트리(entry)가 생성된다. 이 엔트리는 <서비스-이름><네임스페이스=이름>.svc.cluster.local 형식을 갖는데, 컨테이너가 <서비스-이름>만 갖는 경우에는 네임스페이스에 국한된 서비스로 연결된다. 이 기능은 개발, 스테이징 및 프로덕션과 같이 여러 네임스페이스 내에서 동일한 설정을 사용할 때 유용하다. 네임스페이스를 넘어서 접근하려면 전체 주소 도메인 이름(FQDN)을 사용해야 한다.

다음 내용

12 - 네트워크 폴리시(Network Policy) 선언하기

이 문서는 사용자가 쿠버네티스 네트워크폴리시 API를 사용하여 파드(Pod)가 서로 통신하는 방법을 제어하는 네트워크 폴리시를 선언하는데 도움을 준다.

시작하기 전에

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

쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.8. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

네트워크 폴리시를 지원하는 네트워크 제공자를 구성하였는지 확인해야 한다. 다음과 같이 네트워크폴리시를 지원하는 많은 네트워크 제공자들이 있다.

nginx 디플로이먼트(Deployment)를 생성하고 서비스(Service)를 통해 노출하기

쿠버네티스 네트워크 폴리시가 어떻게 동작하는지 확인하기 위해서, nginx 디플로이먼트를 생성한다.

kubectl create deployment nginx --image=nginx
deployment.apps/nginx created

nginx 라는 이름의 서비스를 통해 디플로이먼트를 노출한다.

kubectl expose deployment nginx --port=80
service/nginx exposed

위 명령어들은 nginx 파드에 대한 디플로이먼트를 생성하고, nginx 라는 이름의 서비스를 통해 디플로이먼트를 노출한다. nginx 파드와 디플로이먼트는 default 네임스페이스(namespace)에 존재한다.

kubectl get svc,pod
NAME                        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
service/kubernetes          10.100.0.1    <none>        443/TCP    46m
service/nginx               10.100.0.16   <none>        80/TCP     33s

NAME                        READY         STATUS        RESTARTS   AGE
pod/nginx-701339712-e0qfq   1/1           Running       0          35s

다른 파드에서 접근하여 서비스 테스트하기

사용자는 다른 파드에서 새 nginx 서비스에 접근할 수 있어야 한다. default 네임스페이스에 있는 다른 파드에서 nginx 서비스에 접근하기 위하여, busybox 컨테이너를 생성한다.

kubectl run busybox --rm -ti --image=busybox:1.28 -- /bin/sh

사용자 쉘에서, 다음의 명령을 실행한다.

wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
remote file exists

nginx 서비스에 대해 접근 제한하기

access: true 레이블을 가지고 있는 파드만 nginx 서비스에 접근할 수 있도록 하기 위하여, 다음과 같은 네트워크폴리시 오브젝트를 생성한다.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: access-nginx
spec:
  podSelector:
    matchLabels:
      app: nginx
  ingress:
  - from:
    - podSelector:
        matchLabels:
          access: "true"

네트워크폴리시 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.

서비스에 정책 할당하기

kubectl을 사용하여 위 nginx-policy.yaml 파일로부터 네트워크폴리시를 생성한다.

kubectl apply -f https://k8s.io/examples/service/networking/nginx-policy.yaml
networkpolicy.networking.k8s.io/access-nginx created

access 레이블이 정의되지 않은 서비스에 접근 테스트

올바른 레이블이 없는 파드에서 nginx 서비스에 접근하려 할 경우, 요청 타임 아웃이 발생한다.

kubectl run busybox --rm -ti --image=busybox:1.28 -- /bin/sh

사용자 쉘에서, 다음의 명령을 실행한다.

wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
wget: download timed out

접근 레이블을 정의하고 다시 테스트

사용자는 요청이 허용되도록 하기 위하여 올바른 레이블을 갖는 파드를 생성한다.

kubectl run busybox --rm -ti --labels="access=true" --image=busybox:1.28 -- /bin/sh

사용자 쉘에서, 다음의 명령을 실행한다.

wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
remote file exists

13 - 노드에 대한 확장 리소스 알리기

이 페이지는 노드의 확장 리소스를 지정하는 방법을 보여준다. 확장 리소스를 통해 클러스터 관리자는 쿠버네티스에게 알려지지 않은 노드-레벨 리소스를 알릴 수 있다.

시작하기 전에

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

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

노드의 이름을 확인한다

kubectl get nodes

이 연습에 사용할 노드 중 하나를 선택한다.

노드 중 하나에 새로운 확장 리소스를 알린다

노드에서 새로운 확장 리소스를 알리려면, 쿠버네티스 API 서버에 HTTP PATCH 요청을 보낸다. 예를 들어, 노드 중 하나에 4개의 동글(dongle)이 있다고 가정한다. 다음은 노드에 4개의 동글 리소스를 알리는 PATCH 요청의 예이다.

PATCH /api/v1/nodes/<your-node-name>/status HTTP/1.1
Accept: application/json
Content-Type: application/json-patch+json
Host: k8s-master:8080

[
  {
    "op": "add",
    "path": "/status/capacity/example.com~1dongle",
    "value": "4"
  }
]

참고로 쿠버네티스는 동글이 무엇인지 또는 동글이 무엇을 위한 것인지 알 필요가 없다. 위의 PATCH 요청은 노드에 동글이라고 하는 네 가지 항목이 있음을 쿠버네티스에 알려준다.

쿠버네티스 API 서버에 요청을 쉽게 보낼 수 있도록 프록시를 시작한다.

kubectl proxy

다른 명령 창에서 HTTP PATCH 요청을 보낸다. <your-node-name> 을 노드의 이름으로 바꾼다.

curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "add", "path": "/status/capacity/example.com~1dongle", "value": "4"}]' \
http://localhost:8001/api/v1/nodes/<your-node-name>/status

출력은 노드가 4개의 동글 용량을 가졌음을 나타낸다.

"capacity": {
  "cpu": "2",
  "memory": "2049008Ki",
  "example.com/dongle": "4",

노드의 정보를 확인한다.

kubectl describe node <your-node-name>

다시 한 번, 출력에 동글 리소스가 표시된다.

Capacity:
 cpu:  2
 memory:  2049008Ki
 example.com/dongle:  4

이제, 애플리케이션 개발자는 특정 개수의 동글을 요청하는 파드를 만들 수 있다. 컨테이너에 확장 리소스 할당하기를 참고한다.

토론

확장 리소스는 메모리 및 CPU 리소스와 비슷하다. 예를 들어, 노드에서 실행 중인 모든 컴포넌트가 공유할 특정 양의 메모리와 CPU가 노드에 있는 것처럼, 노드에서 실행 중인 모든 컴포넌트가 특정 동글을 공유할 수 있다. 또한 애플리케이션 개발자가 특정 양의 메모리와 CPU를 요청하는 파드를 생성할 수 있는 것처럼, 특정 동글을 요청하는 파드를 생성할 수 있다.

확장 리소스는 쿠버네티스에게 불투명하다. 쿠버네티스는 그것들이 무엇인지 전혀 모른다. 쿠버네티스는 노드에 특정 개수의 노드만 있다는 것을 알고 있다. 확장 리소스는 정수로 알려야 한다. 예를 들어, 노드는 4.5개의 동글이 아닌, 4개의 동글을 알릴 수 있다.

스토리지 예제

노드에 800GiB의 특별한 종류의 디스크 스토리지가 있다고 가정한다. example.com/special-storage와 같은 특별한 스토리지의 이름을 생성할 수 있다. 그런 다음 특정 크기, 100GiB의 청크로 알릴 수 있다. 이 경우, 노드에는 example.com/special-storage 유형의 8가지 리소스가 있다고 알린다.

Capacity:
 ...
 example.com/special-storage: 8

이 특별한 스토리지에 대한 임의 요청을 허용하려면, 1바이트 크기의 청크로 특별한 스토리지를 알릴 수 있다. 이 경우, example.com/special-storage 유형의 800Gi 리소스를 알린다.

Capacity:
 ...
 example.com/special-storage:  800Gi

그런 다음 컨테이너는 최대 800Gi의 임의 바이트 수의 특별한 스토리지를 요청할 수 있다.

정리

다음은 노드에서 동글 알림을 제거하는 PATCH 요청이다.

PATCH /api/v1/nodes/<your-node-name>/status HTTP/1.1
Accept: application/json
Content-Type: application/json-patch+json
Host: k8s-master:8080

[
  {
    "op": "remove",
    "path": "/status/capacity/example.com~1dongle",
  }
]

쿠버네티스 API 서버에 요청을 쉽게 보낼 수 있도록 프록시를 시작한다.

kubectl proxy

다른 명령 창에서 HTTP PATCH 요청을 보낸다. <your-node-name>을 노드의 이름으로 바꾼다.

curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "remove", "path": "/status/capacity/example.com~1dongle"}]' \
http://localhost:8001/api/v1/nodes/<your-node-name>/status

동글 알림이 제거되었는지 확인한다.

kubectl describe node <your-node-name> | grep dongle

(출력이 보이지 않아야 함)

다음 내용

애플리케이션 개발자를 위한 문서

클러스터 관리자를 위한 문서

14 - 서비스 디스커버리를 위해 CoreDNS 사용하기

이 페이지는 CoreDNS 업그레이드 프로세스와 kube-dns 대신 CoreDNS를 설치하는 방법을 보여준다.

시작하기 전에

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

쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.9. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

CoreDNS 소개

CoreDNS는 쿠버네티스 클러스터의 DNS 역할을 수행할 수 있는, 유연하고 확장 가능한 DNS 서버이다. 쿠버네티스와 동일하게, CoreDNS 프로젝트도 CNCF가 관리한다.

사용자는 기존 디플로이먼트인 kube-dns를 교체하거나, 클러스터를 배포하고 업그레이드하는 kubeadm과 같은 툴을 사용하여 클러스터 안의 kube-dns 대신 CoreDNS를 사용할 수 있다.

CoreDNS 설치

Kube-dns의 배포나 교체에 관한 매뉴얼은 CoreDNS GitHub 프로젝트에 있는 문서를 확인하자.

CoreDNS로 이관하기

Kubeadm을 사용해 기존 클러스터 업그레이드하기

쿠버네티스 버전 1.21에서, kubeadm은 DNS 애플리케이션으로서의 kube-dns 지원을 제거했다. kubeadm v1.30 버전에서는, DNS 애플리케이션으로 CoreDNS만이 지원된다.

kube-dns를 사용 중인 클러스터를 업그레이드하기 위하여 kubeadm 을 사용할 때 CoreDNS로 전환할 수 있다. 이 경우, kubeadmkube-dns 컨피그맵(ConfigMap)을 기반으로 스텁 도메인(stub domain), 업스트림 네임 서버의 설정을 유지하며 CoreDNS 설정("Corefile")을 생성한다.

CoreDNS 업그레이드하기

쿠버네티스에서의 CoreDNS 버전 페이지에서, 쿠버네티스 각 버전에 대해 kubeadm이 설치하는 CoreDNS의 버전을 확인할 수 있다.

CoreDNS만 업그레이드하고 싶거나 커스텀 이미지를 사용하고 싶은 경우, CoreDNS를 수동으로 업그레이드할 수 있다. 가이드라인 및 따라해보기를 참고하여 부드러운 업그레이드를 수행할 수 있다. 클러스터를 업그레이드할 때 기존 CoreDNS 환경 설정("Corefile")을 보존했는지 확인한다.

kubeadm 도구를 사용하여 클러스터를 업그레이드하는 경우, kubeadm이 자동으로 기존 CoreDNS 환경 설정을 보존한다.

CoreDNS 튜닝하기

리소스 활용이 중요한 경우, CoreDNS 구성을 조정하는 것이 유용할 수 있다. 더 자세한 내용은 CoreDNS 스케일링에 대한 설명서를 확인한다.

다음 내용

CoreDNS 환경 설정("Corefile")을 수정하여 kube-dns보다 더 많은 유스케이스를 지원하도록 CoreDNS를 구성할 수 있다. 더 많은 정보는 CoreDNS의 kubernetes 플러그인 문서를 참고하거나, CoreDNS 블로그의 쿠버네티스를 위한 커스텀 DNS 엔트리를 확인한다.

15 - 스토리지 사용량 제한

이 예제는 네임스페이스(namespace)에서 사용되는 스토리지의 용량을 제한하는 방법을 보여준다.

예제에서는 다음과 같은 리소스가 사용된다. 리소스쿼터(ResourceQuota), 리밋레인지(LimitRange), 그리고 퍼시스턴트볼륨클레임(PersistentVolumeClaim).

시작하기 전에

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

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

시나리오: 스토리지 사용량 제한하기

클러스터 관리자는 사용자를 대표하여 클러스터를 운영하고 , 비용을 제어하기 위해 단일 네임스페이스에서 사용할 수 있는 스토리지의 크기를 제어하려고 한다.

관리자는 다음을 제한하려고 한다.

  1. 네임스페이스에 있는 퍼시스턴트볼륨클레임의 수
  2. 각 클레임(claim)이 요청할 수 있는 스토리지의 용량
  3. 네임스페이스가 가질 수 있는 누적 스토리지 용량

스토리지 요청을 제한하기 위한 리밋레인지(LimitRange)

네임스페이스에 리밋레인지(LimitRange)을 추가하면 스토리지 요청 크기가 최소 및 최대값으로 설정된다. 스토리지는 퍼시스턴트 볼륨 클레임(Persistent Volume Claim)을 통해 요청하게 된다. 제한 범위를 적용하는 어드미션 컨트롤러(Admission Controller)는 관리자가 설정한 값보다 높거나 낮은 PVC를 거부한다.

이 예제에서, 10Gi의 스토리지를 요청하는 PVC는 2Gi인 최대값을 초과하기 때문에 거부된다.

apiVersion: v1
kind: LimitRange
metadata:
  name: storagelimits
spec:
  limits:
  - type: PersistentVolumeClaim
    max:
      storage: 2Gi
    min:
      storage: 1Gi

스토리지 요청에 대한 최솟값은 해당 스토리지의 제공자가 최소값을 특정하여 요구하는 경우 사용한다. 예를 들어, AWS EBS 볼륨을 사용할 때는 최소 1Gi를 요청해야 한다.

PVC 수와 누적 스토리지 용량을 제한하는 스토리지쿼터(StorageQuota)

관리자는 네임스페이스의 PVC 수와 해당 PVC의 누적 용량을 제한할 수 있다. 최대값을 초과하는 새 PVC는 거부된다.

이 예제에서 네임스페이스의 6번째 PVC는 최대 카운트 5를 초과하기 때문에 거부된다. 또한, 위의 2Gi 최대 한계(max limit)와 결합된 5Gi 최대 할당량(maximum quota)은 각각 2Gi를 갖는 3개의 PVC를 가질 수 없다. 그것은 5Gi로 한도가 정해진 네임스페이스에 대해 6Gi의 요청이 될 것이다.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: storagequota
spec:
  hard:
    persistentvolumeclaims: "5"
    requests.storage: "5Gi"

요약

리밋레인지(LimitRange)을 지정하면 요청된 스토리지 양을 제한할 수 있으며 리소스쿼터(ResourceQuota)는 네임스페이스에 클레임(claim)수와 누적 스토리지 용량을 효과적으로 제한할 수 있다. 클러스터 관리자는 어느 프로젝트도 할당량을 초과하는 위험이 없도록 클러스터의 스토리지 예산을 계획할 수 있다.

16 - 중요한 애드온 파드 스케줄링 보장하기

API 서버, 스케줄러 및 컨트롤러 매니저와 같은 쿠버네티스 주요 컴포넌트들은 컨트롤 플레인 노드에서 동작한다. 반면, 애드온들은 일반 클러스터 노드에서 동작한다. 이러한 애드온들 중 일부(예: 메트릭 서버, DNS, UI)는 클러스터 전부가 정상적으로 동작하는 데 필수적일 수 있다. 만약, 필수 애드온이 축출되고(수동 축출, 혹은 업그레이드와 같은 동작으로 인한 의도하지 않은 축출) pending 상태가 된다면, 클러스터가 더 이상 제대로 동작하지 않을 수 있다. (사용률이 매우 높은 클러스터에서 해당 애드온이 축출되자마자 다른 대기중인 파드가 스케줄링되거나 다른 이유로 노드에서 사용할 수 있는 자원량이 줄어들어 pending 상태가 발생할 수 있다)

유의할 점은, 파드를 중요(critical)로 표시하는 것은 축출을 완전히 방지하기 위함이 아니다. 이것은 단지 파드가 영구적으로 사용할 수 없게 되는 것만을 방지하기 위함이다. 중요로 표시한 스태틱(static) 파드는 축출될 수 없다. 반면, 중요로 표시한 일반적인(non-static) 파드의 경우 항상 다시 스케줄링된다.

파드를 중요(critical)로 표시하기

파드를 중요로 표시하기 위해서는, 해당 파드에 대해 priorityClassName을 system-cluster-critical이나 system-node-critical로 설정한다. system-node-critical은 가장 높은 우선 순위를 가지며, 심지어 system-cluster-critical보다도 우선 순위가 높다.

17 - 쿠버네티스 API 활성화 혹은 비활성화하기

이 페이지는 클러스터 컨트롤 플레인의 특정한 API 버전을 활성화하거나 비활성화하는 방법에 대해 설명한다.

API 서버에 --runtime-config=api/<version> 커맨드 라인 인자를 사용함으로서 특정한 API 버전을 활성화하거나 비활성화할 수 있다. 이 인자에 대한 값으로는 콤마로 구분된 API 버전의 목록을 사용한다. 뒤쪽에 위치한 값은 앞쪽의 값보다 우선적으로 사용된다.

runtime-config 커맨드 라인 인자에는 다음의 두 개의 특수 키를 사용할 수도 있다.

  • api/all: 사용할 수 있는 모든 API를 선택한다.
  • api/legacy: 레거시 API만을 선택한다. 여기서 레거시 API란 명시적으로 사용이 중단된 모든 API를 가리킨다.

예를 들어서, v1을 제외한 모든 API 버전을 비활성화하기 위해서는 kube-apiserver--runtime-config=api/all=false,api/v1=true 인자를 사용한다.

다음 내용

kube-apiserver 컴포넌트에 대한 더 자세한 내용은 다음의 문서 를 참고한다.

18 - 쿠버네티스 클러스터에서 sysctl 사용하기

기능 상태: Kubernetes v1.21 [stable]

이 문서는 쿠버네티스 클러스터에서 sysctl 인터페이스를 사용하여 커널 파라미터를 어떻게 구성하고, 사용하는지를 설명한다.

시작하기 전에

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

일부 단계에서는 실행 중인 클러스터의 kubelet에서 커맨드 라인 옵션을 재구성할 필요가 있다.

모든 sysctl 파라미터 나열

리눅스에서 sysctl 인터페이스는 관리자들이 런타임에 커널 파라미터를 수정할 수 있도록 허용한다. 파라미터는 /proc/sys 가상 파일 시스템을 통해 이용할 수 있다. 파라미터는 다음과 같은 다양한 서브 시스템을 포함한다.

  • 커널 (공통 접두사: kernel.)
  • 네트워크 (공통 접두사: net.)
  • 가상 메모리 (공통 접두사: vm.)
  • MDADM (공통 접두사: dev.)
  • 더 많은 서브 시스템은 커널 문서에 설명되어 있다.

모든 파라미터 리스트를 가져오려면 다음 명령을 실행한다.

sudo sysctl -a

unsafe sysctl 활성화하기

sysctl은 safe sysctl과 unsafe sysctl로 구성되어 있다. safe sysctl은 적절한 네임스페이스 뿐만 아니라 동일한 노드의 파드 사이에 고립 되어야 한다. 즉, 하나의 파드에 safe sysctl을 설정한다는 것은 다음을 의미한다.

  • 노드의 다른 파드에 영향을 미치지 않아야 한다
  • 노드의 상태를 손상시키지 않아야 한다
  • CPU 또는 메모리 리소스가 파드의 리소스 제한에 벗어나는 것을 허용하지 않아야 한다

아직까지 대부분 네임스페이스된 sysctl은 safe sysctl로 고려되지 않았다. 다음 sysctl은 safe 명령을 지원한다.

  • kernel.shm_rmid_forced,
  • net.ipv4.ip_local_port_range,
  • net.ipv4.tcp_syncookies,
  • net.ipv4.ping_group_range (쿠버네티스 1.18 이후),
  • net.ipv4.ip_unprivileged_port_start (쿠버네티스 1.22 이후).

kubelet이 더 고립된 방법을 지원하면 추후 쿠버네티스 버전에서 확장될 것이다.

모든 safe sysctl은 기본적으로 활성화된다.

모든 unsafe sysctl은 기본적으로 비활성화되고, 노드별 기본 클러스터 관리자에 의해 수동으로 메뉴얼로 허용되어야 한다. unsafe sysctl이 비활성화된 파드는 스케줄링되지만, 시작에 실패한다.

위의 경고를 염두에 두고 클러스터 관리자는 고성능 또는 실시간 애플리케이션 조정과 같은 매우 특수한 상황에 대해 특정 unsafe sysctl을 허용할 수 있다. unsafe sysctl은 kubelet 플래그를 사용하여 노드별로 활성화된다. 예를 들면, 다음과 같다.

kubelet --allowed-unsafe-sysctls \
  'kernel.msg*,net.core.somaxconn' ...

Minikube의 경우, extra-config 플래그를 통해 이 작업을 수행할 수 있다.

minikube start --extra-config="kubelet.allowed-unsafe-sysctls=kernel.msg*,net.core.somaxconn"...

네임스페이스 sysctl만 이 방법을 사용할 수 있다.

파드에 대한 sysctl 설정

수많은 sysctl은 최근 리눅스 커널에서 네임스페이스 되어 있다. 이는 노드의 각 파드에 대해 개별적으로 설정할 수 있다는 것이다. 쿠버네티스의 파드 securityContext를 통해 네임스페이스 sysctl만 구성할 수 있다.

다음 sysctls는 네임스페이스로 알려져 있다. 이 목록은 이후 버전의 Linux 커널에서 변경될 수 있다.

  • kernel.shm*,
  • kernel.msg*,
  • kernel.sem,
  • fs.mqueue.*,
  • net.* 아래의 파라미터는 컨테이너 네트워킹 네임스페이스에서 설정할 수 있다. 그러나 예외가 존재한다. (예, net.netfilter.nf_conntrack_maxnet.netfilter.nf_conntrack_expect_max는 컨테이너 네트워킹 네임스페이스에서 설정되지만, 네임스페이스가 없다.)

네임스페이스가 없는 sysctl은 node-level sysctl이라고 부른다. 이를 설정해야 한다면, 각 노드의 OS에서 수동으로 구성하거나 특권있는 컨테이너의 데몬셋을 사용하여야 한다.

네임스페이스 sysctl을 구성하기 위해서 파드 securityContext를 사용한다. securityContext는 동일한 파드의 모든 컨테이너에 적용된다.

이 예시는 safe sysctl kernel.shm_rmid_forced와 두 개의 unsafe sysctl인 net.core.somaxconnkernel.msgmax 를 설정하기 위해 파드 securityContext를 사용한다. 스펙에 따르면 safe sysctl과 unsafe sysctl 간 차이는 없다.

apiVersion: v1
kind: Pod
metadata:
  name: sysctl-example
spec:
  securityContext:
    sysctls:
    - name: kernel.shm_rmid_forced
      value: "0"
    - name: net.core.somaxconn
      value: "1024"
    - name: kernel.msgmax
      value: "65536"
  ...

특별한 sysctl 설정이 있는 노드를 클러스터 내에서 _tainted_로 간주하고 sysctl 설정이 필요한 노드에만 파드를 예약하는 것이 좋다. 이를 구현하려면 쿠버네티스 테인트(taint)와 톨러레이션(toleration) 기능 을 사용하는 것이 좋다.

unsafe sysctl을 명시적으로 활성화하지 않은 노드에서 unsafe sysctl을 사용하는 파드가 시작되지 않는다. node-level sysctl과 마찬가지로 테인트와 톨러레이션 특징 또는 노드 테인트를 사용하여 해당 파드를 오른쪽 노드에 스케줄하는 것을 추천한다.

19 - 클러스터 업그레이드

이 페이지는 쿠버네티스 클러스터를 업그레이드하기 위해 따라야 할 단계에 대한 개요를 제공한다.

클러스터를 업그레이드하는 방법은 초기 배포 방법에 의존적이며, 배포 이후 관련된 변경 사항에도 의존적일 수 있다.

고수준으로 살펴 본, 수행 단계

  • 컨트롤 플레인 업그레이드하기
  • 클러스터 내부의 노드를 업그레이드하기
  • kubectl과 같은 클라이언트 업그레이드하기
  • 새로운 쿠버네티스 버전에 동반되는 API 변화에 기반하여, 매니페스트 또는 다른 리소스를 조정하기

시작하기 전에

기존 클러스터가 존재해야 한다. 이 페이지는 쿠버네티스 1.29에서 쿠버네티스 1.30로 업그레이드하는 것에 관해 다룬다. 클러스터에서 쿠버네티스 1.29을 실행하지 않는 경우 업그레이드하려는 쿠버네티스 버전에 대한 설명서를 참고한다.

업그레이드 방법

kubeadm

클러스터가 kubeadm 도구를 사용하여 배포된 경우 클러스터 업그레이드 방법에 대한 자세한 내용은 kubeadm 클러스터 업그레이드를 참조한다.

클러스터를 업그레이드한 후에는 kubectl 최신 버전을 설치해야 한다.

수동 배포

다음 순서에 따라 컨트롤 플레인을 수동으로 업데이트해야 한다.

  • etcd (모든 인스턴스)
  • kube-apiserver (모든 컨트롤 플레인 호스트)
  • kube-controller-manager
  • kube-scheduler
  • cloud controller manager (사용하는 경우)

이 때 kubectl 최신 버전을 설치 해야 한다.

클러스터의 각 노드에 대해 해당 노드를 드레인(drain) 한 다음 1.30 kubelet을 사용하는 새 노드로 바꾸거나 해당 노드의 kubelet을 업그레이드하고 노드를 다시 가동한다.

다른 방식의 배포

사용한 클러스터 배포 도구에 따라 해당 배포 도구가 제공하는 문서를 통하여, 유지 관리를 위해 권장되는 설정 단계를 확인한다.

업그레이드 후 작업

클러스터의 스토리지 API 버전 전환

클러스터에서 활성화된 쿠버네티스 리소스를 클러스터 내부적으로 표현(representation)하기 위해서 etcd로 직렬화된 객체는 특정 버전의 API를 사용하여 작성된다.

지원되는 API가 변경되면 이러한 개체를 새 API에서 다시 작성해야 할 수 있다. 이렇게 하지 않으면 결국 더 이상 디코딩할 수 없거나 쿠버네티스 API 서버에서 사용할 수 없는 리소스가 된다.

영향을 받는 각 객체를 지원되는 최신 API를 사용하여 가져온(fetch) 다음, 해당 API를 사용하여 다시 쓴다.

매니페스트 업그레이드

새로운 쿠버네티스 버전으로 업그레이드하면 새로운 API를 제공할 수 있다.

kubectl convert 명령을 사용하여 서로 다른 API 버전 간에 매니페스트를 변환할 수 있다. 예시:

kubectl convert -f pod.yaml --output-version v1

kubectl 도구는 pod.yaml의 내용을 kind를 파드(변경되지 않음, unchanged)로 설정하는 매니페스트로 대체하고, 수정된 apiVersion으로 대체한다.

장치 플러그인

클러스터가 장치 플러그인을 실행 중이고 노드를 최신 장치 플러그인 API 버전이 있는 쿠버네티스 릴리스로 업그레이드해야 하는 경우, 업그레이드 중에 장치 할당이 계속 성공적으로 완료되도록 하려면 장치 플러그인을 업그레이드해야 한다.

API 호환성Kubelet 장치 매니저 API 버전을 참조한다.

20 - 클러스터에서 DNS 서비스 오토스케일

이 페이지는 쿠버네티스 클러스터에서 DNS 서비스의 오토스케일링을 구성하고 활성화하는 방법을 보여준다.

시작하기 전에

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

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

  • 이 가이드는 노드가 AMD64 또는 인텔 64 CPU 아키텍처를 사용한다고 가정한다.

  • Kubernetes DNS가 활성화되어 있는지 확인한다.

DNS 수평 오토스케일링이 이미 활성화되어 있는지 확인

kube-system 네임스페이스 에서 클러스터의 디플로이먼트를 나열한다.

kubectl get deployment --namespace=kube-system

출력은 다음과 유사하다.

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
...
dns-autoscaler            1/1     1            1           ...
...

해당 출력에서 "dns-autoscaler"가 표시되면, DNS 수평 오토스케일링이 이미 활성화되어 있다는 의미이므로, 오토스케일링 파라미터 조정으로 건너뛰면 된다.

DNS 디플로이먼트 이름 가져오기

kube-system 네임스페이스에서 클러스터의 DNS 디플로이먼트를 나열한다.

kubectl get deployment -l k8s-app=kube-dns --namespace=kube-system

출력은 이와 유사하다.

NAME      READY   UP-TO-DATE   AVAILABLE   AGE
...
coredns   2/2     2            2           ...
...

DNS 서비스용 디플로이먼트가 표시되지 않으면, 이름으로 찾을 수 있다.

kubectl get deployment --namespace=kube-system

그리고 coredns 또는 kube-dns라는 디플로이먼트를 찾는다.

스케일 대상은

Deployment/<your-deployment-name>

이며, 여기서 <your-deployment-name>는 DNS 디플로이먼트의 이름이다. 예를 들어, DNS용 디플로이먼트 이름이 coredns인 경우, 스케일 대상은 Deployment/coredns이다.

DNS 수평 오토스케일링 활성화

이 섹션에서는 새로운 디플로이먼트를 만든다. 디플로이먼트의 파드는 cluster-proportional-autoscaler-amd64 이미지 기반의 컨테이너를 실행한다.

다음의 내용으로 dns-horizontal-autoscaler.yaml라는 파일을 만든다.

kind: ServiceAccount
apiVersion: v1
metadata:
  name: kube-dns-autoscaler
  namespace: kube-system
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: system:kube-dns-autoscaler
rules:
  - apiGroups: [""]
    resources: ["nodes"]
    verbs: ["list", "watch"]
  - apiGroups: [""]
    resources: ["replicationcontrollers/scale"]
    verbs: ["get", "update"]
  - apiGroups: ["apps"]
    resources: ["deployments/scale", "replicasets/scale"]
    verbs: ["get", "update"]
# 아래 문제가 해결되면 configmaps 규칙을 제거한다.
# kubernetes-incubator/cluster-proportional-autoscaler#16
  - apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["get", "create"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: system:kube-dns-autoscaler
subjects:
  - kind: ServiceAccount
    name: kube-dns-autoscaler
    namespace: kube-system
roleRef:
  kind: ClusterRole
  name: system:kube-dns-autoscaler
  apiGroup: rbac.authorization.k8s.io

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kube-dns-autoscaler
  namespace: kube-system
  labels:
    k8s-app: kube-dns-autoscaler
    kubernetes.io/cluster-service: "true"
spec:
  selector:
    matchLabels:
      k8s-app: kube-dns-autoscaler
  template:
    metadata:
      labels:
        k8s-app: kube-dns-autoscaler
    spec:
      priorityClassName: system-cluster-critical
      securityContext:
        seccompProfile:
          type: RuntimeDefault
        supplementalGroups: [ 65534 ]
        fsGroup: 65534
      nodeSelector:
        kubernetes.io/os: linux
      containers:
      - name: autoscaler
        image: registry.k8s.io/cpa/cluster-proportional-autoscaler:1.8.4
        resources:
            requests:
                cpu: "20m"
                memory: "10Mi"
        command:
          - /cluster-proportional-autoscaler
          - --namespace=kube-system
          - --configmap=kube-dns-autoscaler
          # 타겟은 cluster/addons/dns/kube-dns.yaml.base와 동기화된 상태로 유지해야 한다.
          
          - --target=<SCALE_TARGET>
          # 클러스터가 대규모 노드(많은 코어가 있는)를 사용하는 경우, "coresPerReplica"가 우선시해야 한다.
          # 작은 노드를 사용하는 경우, "nodesPerReplica"가 우선시해야 한다.
          - --default-params={"linear":{"coresPerReplica":256,"nodesPerReplica":16,"preventSinglePointFailure":true,"includeUnschedulableNodes":true}}
          - --logtostderr=true
          - --v=2
      tolerations:
      - key: "CriticalAddonsOnly"
        operator: "Exists"
      serviceAccountName: kube-dns-autoscaler

파일에서, <SCALE_TARGET>을 사용자의 스케일 대상으로 변경한다.

구성 파일이 포함된 디렉토리로 이동하고, 디플로이먼트를 만들기 위해 다음의 커맨드를 입력한다.

kubectl apply -f dns-horizontal-autoscaler.yaml

성공적인 커맨드의 출력은 다음과 같다.

deployment.apps/dns-autoscaler created

DNS 수평 오토스케일링이 활성화되었다.

DNS 오토스케일링 파라미터 조정

dns-autoscaler 컨피그맵이 있는지 확인한다.

kubectl get configmap --namespace=kube-system

출력은 다음과 유사하다.

NAME                  DATA      AGE
...
dns-autoscaler        1         ...
...

컨피그맵에서 데이터를 수정한다.

kubectl edit configmap dns-autoscaler --namespace=kube-system

다음에 해당하는 줄을 찾는다.

linear: '{"coresPerReplica":256,"min":1,"nodesPerReplica":16}'

필요에 따라서 해당 필드를 수정한다. "min" 필드는 최소 DNS 백엔드 수를 나타낸다. 실제 백엔드의 수는 이 방정식을 사용하여 계산된다.

replicas = max( ceil( cores × 1/coresPerReplica ) , ceil( nodes × 1/nodesPerReplica ) )

coresPerReplicanodesPerReplica 값은 모두 부동 소수점이니 주의한다.

해당 아이디어는 클러스터가 코어가 많은 노드를 사용하는 경우, coresPerReplica의 영향을 더 크게 만들고, 코어 수가 적은 노드를 사용하는 경우 nodesPerReplica의 영향을 더 크게 만드는 것이다.

그밖에 다른 스케일링 패턴도 지원한다. cluster-proportional-autoscaler를 참고한다.

DNS 수평 오토스케일링 비활성화

DNS 수평 오토스케일링을 조정하기 위해 몇 가지 옵션이 있다. 사용할 옵션은 조건에 따라 다르다.

옵션 1: dns-autoscaler 디플로이먼트를 레플리카 0개로 축소

이 옵션은 모든 상황에서 작동한다. 다음 커맨드를 입력한다.

kubectl scale deployment --replicas=0 dns-autoscaler --namespace=kube-system

출력은 다음과 같다.

deployment.apps/dns-autoscaler scaled

레플리카 수가 0인지 확인한다.

kubectl get rs --namespace=kube-system

출력은 DESIRED 및 CURRENT 열에 0으로 보여준다.

NAME                                 DESIRED   CURRENT   READY   AGE
...
dns-autoscaler-6b59789fc8            0         0         0       ...
...

옵션 2: dns-autoscaler 디플로이먼트를 삭제

이 옵션은 dns-autoscaler가 자체적으로 제어되는 경우 작동하며, 아무도 이것을 재-생성하지 않음을 의미한다.

kubectl delete deployment dns-autoscaler --namespace=kube-system

출력은 다음과 같다.

deployment.apps "dns-autoscaler" deleted

옵션 3: 마스터노드에서 dns-autoscaler 매니페스트 파일을 삭제

이 옵션은 dns-autoscaler가 (사용 중단되(deprecated)) 애드온 매니저 의 제어를 받고 마스터 노드에 쓰기 권한이 있는 경우 작동한다.

마스터 노드에 로그인하고 해당 매니페스트 파일을 삭제한다. 이 dns-autoscaler의 일반 경로는 다음과 같다.

/etc/kubernetes/addons/dns-horizontal-autoscaler/dns-horizontal-autoscaler.yaml

매니페스트 파일이 삭제된 후, 애드온 매니저는 dns-autoscaler 디플로이먼트를 삭제한다.

DNS 수평 오토스케일링 작동 방식 이해

  • cluster-proportional-autoscaler 애플리케이션은 DNS 서비스와 별도로 배포된다.

  • 오토스케일러 파드는 클러스터의 노드 및 코어 수에 대해 쿠버네티스 API 서버를 폴링(poll)하는 클라이언트를 실행한다.

  • 의도한 레플리카 수는 주어진 스케일링 파라미터로 계산하고 예약 가능한 노드 및 코어를 기반으로 DNS 백엔드에 적용한다.

  • 스케일링 파마리터와 데이터 포인트는 컨피그맵을 통해 자동 오토스케일러에게 제공된다.그리고, 의도한 최근 스케일링 파라미터로 최신 상태가 되도록 폴링 간격에 따라 파라미터 표를 갱신한다.

  • 오토스케일러 파드를 다시 빌드 또는 재 시작하지 않고도 스케일링 파라미터를 변경할 수 있다.

  • 오토스케일러는 linearladder 두 가지 제어 패턴을 지원하는 컨트롤러 인터페이스를 제공한다.

다음 내용

21 - 클러스터에서 캐스케이딩 삭제 사용

이 페이지에서는 가비지 수집 중 클러스터에서 사용할 캐스케이딩 삭제 타입을 지정하는 방법을 보여준다.

시작하기 전에

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

또한 다양한 타입들의 캐스케이딩 삭제를 실험하려면 샘플 디플로이먼트를 생성할 필요가 있다. 각 타입에 대해 디플로이먼트를 다시 생성해야 할 수도 있다.

파드에서 소유자 참조 확인

파드에서 ownerReferences 필드가 존재하는지 확인한다.

kubectl get pods -l app=nginx --output=yaml

출력은 다음과 같이 ownerReferences 필드를 가진다.

apiVersion: v1
    ...
    ownerReferences:
    - apiVersion: apps/v1
      blockOwnerDeletion: true
      controller: true
      kind: ReplicaSet
      name: nginx-deployment-6b474476c4
      uid: 4fdcd81c-bd5d-41f7-97af-3a3b759af9a7
    ...

포그라운드(foreground) 캐스케이딩 삭제 사용

기본적으로 쿠버네티스는 종속 오브젝트를 삭제하기 위해서 백그라운드 캐스케이딩 삭제를 사용한다. 클러스터를 실행하는 쿠버네티스 버전에 따라 kubectl 또는 쿠버네티스 API를 사용해 포그라운드 캐스케이딩 삭제로 전환할 수 있다. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

kubectl 또는 쿠버네티스 API를 사용해 포그라운드 캐스케이딩 삭제로 오브젝트들을 삭제할 수 있다.

kubectl 사용

다음 명령어를 실행한다.

kubectl delete deployment nginx-deployment --cascade=foreground

쿠버네티스 API 사용

  1. 로컬 프록시 세션을 시작한다.

    kubectl proxy --port=8080
    
  2. 삭제를 작동시키기 위해 curl을 사용한다.

    curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/deployments/nginx-deployment \
        -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \
        -H "Content-Type: application/json"
    

    출력에는 다음과 같이 foregroundDeletion 파이널라이저(finalizer)가 포함되어 있다.

    "kind": "Deployment",
    "apiVersion": "apps/v1",
    "metadata": {
        "name": "nginx-deployment",
        "namespace": "default",
        "uid": "d1ce1b02-cae8-4288-8a53-30e84d8fa505",
        "resourceVersion": "1363097",
        "creationTimestamp": "2021-07-08T20:24:37Z",
        "deletionTimestamp": "2021-07-08T20:27:39Z",
        "finalizers": [
          "foregroundDeletion"
        ]
        ...
    

백그라운드 캐스케이딩 삭제 사용

  1. 샘플 디플로이먼트를 생성한다.
  2. 클러스터를 실행하는 쿠버네티스 버전에 따라 디플로이먼트를 삭제하기 위해 kubectl 또는 쿠버네티스 API를 사용한다. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

kubectl 또는 쿠버네티스 API를 사용해 백그라운드 캐스케이딩 삭제로 오브젝트들을 삭제할 수 있다.

쿠버네티스는 기본적으로 백그라운드 캐스케이딩 삭제를 사용하므로, --cascade 플래그 또는 propagationPolicy 인수 없이 다음 명령을 실행해도 같은 작업을 수행한다.

kubectl 사용

다음 명령어를 실행한다.

kubectl delete deployment nginx-deployment --cascade=background

쿠버네티스 API 사용

  1. 로컬 프록시 세션을 시작한다.

    kubectl proxy --port=8080
    
  2. 삭제를 작동시키기 위해 curl을 사용한다.

    curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/deployments/nginx-deployment \
        -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Background"}' \
        -H "Content-Type: application/json"
    

    출력은 다음과 유사하다.

    "kind": "Status",
    "apiVersion": "v1",
    ...
    "status": "Success",
    "details": {
        "name": "nginx-deployment",
        "group": "apps",
        "kind": "deployments",
        "uid": "cc9eefb9-2d49-4445-b1c1-d261c9396456"
    }
    

소유자 오브젝트 및 종속된 고아(orphan) 오브젝트 삭제

기본적으로, 쿠버네티스에 오브젝트를 삭제하도록 지시하면 컨트롤러는 종속 오브젝트들도 제거한다. 클러스터를 실행하는 쿠버네티스 버전에 따라 kubectl 또는 쿠버네티스 API를 사용해 종속 오브젝트를 쿠버네티스 고아로 만들 수 있다. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

kubectl 사용

다음 명령어를 실행한다.

kubectl delete deployment nginx-deployment --cascade=orphan

쿠버네티스 API 사용

  1. 로컬 프록시 세션을 시작한다.

    kubectl proxy --port=8080
    
  2. 삭제를 작동시키기 위해 curl을 사용한다.

    curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/deployments/nginx-deployment \
        -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \
        -H "Content-Type: application/json"
    

    출력에는 다음과 같이 finalizers 필드에 orphan이 포함되어 있다.

    "kind": "Deployment",
    "apiVersion": "apps/v1",
    "namespace": "default",
    "uid": "6f577034-42a0-479d-be21-78018c466f1f",
    "creationTimestamp": "2021-07-09T16:46:37Z",
    "deletionTimestamp": "2021-07-09T16:47:08Z",
    "deletionGracePeriodSeconds": 0,
    "finalizers": [
      "orphan"
    ],
    ...
    

디플로이먼트가 관리하는 파드들이 계속 실행 중인지 확인할 수 있다.

kubectl get pods -l app=nginx

다음 내용

22 - 퍼시스턴트볼륨 반환 정책 변경하기

이 페이지는 쿠버네티스 퍼시트턴트볼륨(PersistentVolume)의 반환 정책을 변경하는 방법을 보여준다.

시작하기 전에

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

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

왜 퍼시스턴트볼륨 반환 정책을 변경하는가?

퍼시스턴트볼륨은 "Retain(보존)", "Recycle(재활용)", "Delete(삭제)" 를 포함한 다양한 반환 정책을 갖는다. 동적으로 프로비저닝 된 퍼시스턴트볼륨의 경우 기본 반환 정책은 "Delete" 이다. 이는 사용자가 해당 PersistentVolumeClaim 을 삭제하면, 동적으로 프로비저닝 된 볼륨이 자동적으로 삭제됨을 의미한다. 볼륨에 중요한 데이터가 포함된 경우, 이러한 자동 삭제는 부적절 할 수 있다. 이 경우에는, "Retain" 정책을 사용하는 것이 더 적합하다. "Retain" 정책에서, 사용자가 퍼시스턴트볼륨클레임을 삭제할 경우 해당하는 퍼시스턴트볼륨은 삭제되지 않는다. 대신, Released 단계로 이동되어, 모든 데이터를 수동으로 복구할 수 있다.

퍼시스턴트볼륨 반환 정책 변경하기

  1. 사용자의 클러스터에서 퍼시스턴트볼륨을 조회한다.

    kubectl get pv
    

    결과는 아래와 같다.

    NAME                                       CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM             STORAGECLASS     REASON    AGE
    pvc-b6efd8da-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Delete          Bound     default/claim1    manual                     10s
    pvc-b95650f8-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Delete          Bound     default/claim2    manual                     6s
    pvc-bb3ca71d-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Delete          Bound     default/claim3    manual                     3s
    

    이 목록은 동적으로 프로비저닝 된 볼륨을 쉽게 식별할 수 있도록 각 볼륨에 바인딩 되어 있는 퍼시스턴트볼륨클레임(PersistentVolumeClaim)의 이름도 포함한다.

  2. 사용자의 퍼시스턴트볼륨 중 하나를 선택한 후에 반환 정책을 변경한다.

    kubectl patch pv <your-pv-name> -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
    

    여기서 <your-pv-name> 는 사용자가 선택한 퍼시스턴트볼륨의 이름이다.

  3. 선택한 PersistentVolume이 올바른 정책을 갖는지 확인한다.

    kubectl get pv
    

    결과는 아래와 같다.

    NAME                                       CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM             STORAGECLASS     REASON    AGE
    pvc-b6efd8da-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Delete          Bound     default/claim1    manual                     40s
    pvc-b95650f8-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Delete          Bound     default/claim2    manual                     36s
    pvc-bb3ca71d-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Retain          Bound     default/claim3    manual                     33s
    

    위 결과에서, default/claim3 클레임과 바인딩 되어 있는 볼륨이 Retain 반환 정책을 갖는 것을 볼 수 있다. 사용자가 default/claim3 클레임을 삭제할 경우, 볼륨은 자동으로 삭제 되지 않는다.

다음 내용

레퍼런스