Kubernetes for application developers(LFD459)

1. 쿠버네티스 구조

1-1. 쿠버네티스란?

  • 컨테이너를 데스크탑에서 실행하는 것은 쉽다. 하지만 여러 호스트에 다운타임 없이 스케일링 하며 배포하고 연결하는 것은 어렵다.

  • 쿠버네티스는 분산 시스템을 탄력적으로 실행하기 위한 프레임워크를 제공한다. 스케줄러와 API 서버를 통해 새로운 오브젝트와 오퍼레이터를 다양한 프로덕션 요구에 맞게 커스터마이징 할 수 있다.

  • 쿠버네티스는 CI/CD 의 필수적인 부분이 될 수 있다. Helm, Jenkins 를 사용해 다양한 환경에서 소프트웨어를 테스트하고 배포하는 일관된 방법을 제공한다.
    그리고 다양한 환경에서 소프트웨어를 배포하는 방법을 제공한다. 쿠버네티스는 업데이트와 롤백을 쉽게 하도록 컨테이너의 라이프 사이클과 인프라 리소스에 연결을 다룬다.

1-2. 쿠버네티스의 구성

  • 전통적으로 모노톨릭한 어플리케이션이 전용 서버에 배포되었다. 트래픽이 증가하면 어플리케이션은 수정되고 더 큰 리소스를 가진 전용 서버로 옮겼다. 트래픽을 처리하기 위한 많은 수정이 어플리케이션에 되었다.

  • 요즘은 큰 서버를 사용하는 대신 쿠버네티스를 활용해 작은 서버 여러 개를 배포한다. 서버에 replicas 로 불리는 여러 마이크로 서비스 형태로 배포한다.
    결합도가 높은 httpd 데몬을 여러 개 가진 아피치 서버 대신 서로의 존재를 모르는 nginx 서버가 자주 사용된다.

  • MSA 구조를 위해 서비스와 API 호출을 사용한다. 서비스는 어플리케이션에서 다른 어플리케이션으로 트래픽을 연결하고 IP 나 다른 정보를 다룬다.
    서비스는 죽게 되면 다른 서비스로 대체되어야 한다.

1-3. 쿠버네티스의 도전 과제

  • 컨테이너(도커)는 개발자가 컨테이너 이미지를 쉽게 만들고 레지스트리를 사용해 공유하고 관리하도록 한다.

  • 그러나 컨테이너를 마이크로 서비스 원리로 스케일링하고 분산 어플리케이션 구조를 짜는 것은 여전히 어렵다.

  • CI 파이프라인으로 컨테이너 이미지를 만들고 테스트해 검증해야 한다. 그리고 컨테이너를 실행하기 위한 클러스터가 필요하다.
    또한 컨테이너를 실행하고 다운 되었을 때 복구할 시스템이 필요하다. 뿐만 아니라 롤백하거나 업데이트도 가능해야한다.

  • 모든 기능들은 유연하고 스케일링 가능해야 하며 네트워크와 스토리지를 쉽게 관리할 수 있어야 한다.
    워커 노드에 컨테이너가 실행되면 네트워크는 리소스와 다른 컨테이너에 연결되어야 한다. 그리고 스토리지를 유지, 재사용하는 원활한 방법을 제공해야 한다.

1-4. Borg

  • 구글은 자신들이 제공하는 어플리케이션을 관리하기 위한 내부 시스템인 Borg 를 15년간 운영한 경험을 통해 쿠버네티스를 만들었다.

1-5 쿠버네티스 구조

components-of-kubernetes

  • 쿠버네티스를 배포하면 클러스터를 얻는다.

  • 쿠버네티스 클러스터는 컨테이너화된 애플리케이션을 실행하는 워커 노드를 포함한다.
    모든 클러스터는 최소 한 개의 워커 노드를 가진다. 워커 노드는 애플리케이션의 구성요소인 파드를 호스트한다.

  • 컨트롤 플레인 노드는 워커 노드와 클러스터 내 파드를 관리한다. CP 노드는 API 서버, 스케줄러, 다양한 기능 그리고 클러스트의 상태와 컨테이너와 네트워크 설정 등의 데이터를 저장할 저장소를 가진다.

  • kubectl 이라는 명령어로 CP 노드의 API 서버에 명령을 내릴 수 있다.

  • kube-scheduler 는 API 요청을 확인하고 새로운 컨테이너를 스케줄해 실행한다.

  • 클러스터 내 각 노드들은 kubelet, kube-proxy 두 개의 컨테이너를 기본으로 실행한다.
    kueblet 은 컨테이너의 컨테이너 설정, 필요한 리소스 다운로드 및 관리 그리고 컨테이너 엔진 동작을 위해 스펙 정보를 받는다.
    kube-proxy 는 컨테이너를 네트워크로 드러내기 위한 방화벽 정책, 네트워크 설정을 관리한다.

  • 쿠버네티스에서 컨테이너를 동작, 관리하는 과정은 이벤트를 통해 동작한다.

1-6. 용어

  • 클러스터는 리소스 공유와 분배를 위해 여러 노드를 묶은 그룹이다.

  • 노드는 쿠버네티스에서 최소 단위의 컴퓨팅 하드웨어 이며, 하나의 개별머신이다. 컨트롤 플레인 노드와 워커 노드 두 종류가 있다. 파드는 노드에서 실행된다.

  • 컨테이너는 하나 이상의 프로세스 모음이다.

  • 파드는 고유한 IP 주소와 DNS 명 그리고 스토리지, 네임 스페이스를 공유하는 한 개 이상의 컨테이너로 구성되어 있다. 일반적으로 한 파드는 한 개의 컨테이너를 가진다.

  • 오케스트레이션은 컨테이너를 배포, 확장하고 관리를 자동화 해주는 도구이다. 오케스트레이션은 오퍼레이터(컨트롤러)로 관리된다.

  • 디플로이먼트 는 로컬 상태가 없는 파드를 실행해 복제된 애플리케이션을 관리하는 API 오브젝트이다.

  • 레플리카셋은 같은 스펙 정보로 여러 파드를 배포한 오퍼레이터이다.

  • 데몬셋(DaemonSet) 은 모든 노드에 1개 씩 생성되고 노드가 추가되면 자동으로 이미지를 추가해 준다. 주로 로그, 모니터링, APM 등이 사용된다.

  • 스테이트풀셋(StatefulSet) 파드들을 특정한 순서로 배포할 때 사용한다.

  • 레이블 오브젝트를 식별하기 위한 태그이다. 오브젝트의 메타데이터 중 하나이다.

  • 어노테이션 오브젝트를 식별 하는데 사용할 수 없다. 오브젝트에 메타데이터를 첨부할 때 사용하는 키-밸류 쌍이다.

  • 네임스페이스는 쿠버네티스에서 하나의 클러스터 내에 리소스 그룹의 격리를 지원하기 위해 사용하는 추상적인 개념이다.

  • 서비스는 파드에서 실행 중인 애플리케이션을 네트워크 서비스로 노출하는 방법을 추상화 한 것이다.

1-7. Control Plane Node

  • 컨트롤 플레인 노드는 클러스터를 위한 여러 서버와 매니저 프로세스를 실행한다. CP 노드는 클러스터에 대한 전반적인 결정을 수행하고 클러스터 이벤트를 감지하고 반응한다.

1-7-1. kube-apiserver

  • 쿠버네티스 클러스터의 내부 및 외부 요청을 처리한다. API 요청은 인증, 권한, 승인 절차를 통해 수행된다.

  • etcd 데이터베이스에 연결되어 있는 유일한 개체이다.

1-7-2. kube-scheduler

  • 파드의 리소스 요구사항과 함께 클러스트의 상태를 고려해 적절한 노드에 파드를 실행한다. 그리고 taints, tolerations, 파드의 레이블을 참고해 파드가 실행될 적정한 노드를 선택한다.

1-7-3. Etcd Database

  • 클러스터의 상태, 설정, 네트워크 및 그 외의 정보를 저장하는 키-값 저장소이다.

  • 쿠버네티스 클러스터 정보를 백업할 때 etcd 데이터베이스 만 백업하면 된다.

1-7-4. kube-controller-manager

  • kube-apiserver 와 상호작용해 클러스터의 상태를 결정한다. 상태가 맞지 않으면 적정한 컨트롤러를 사용해 원하는 상태로 수정한다. kube-control-manager 에는 아래 컨트롤러를 포함한다.

  • 노드 컨트롤러 : 노드가 다운되었을 때 대응한다.

  • 레플리케이션 컨트롤러 : 레플리케이션에 맞는 파드의 수를 유지한다.

  • 엔드포인트 컨트롤러 : 서비스와 파드를 연결하는 역할을 한다.

  • 네임스페이스 컨트롤러 : 네임스페이스를 관리한다.

1-7-5. cloud-controller-manager

  • 클라우드 서비스 회사가 만들어 배포한다. 클라우드 공급자의 API 에 연결하고 해당 클라우드 플랫폼과 상호 작용하는 컴포넌트와 클러스터와 상호 작용하는 컴포넌트를 구별한다.

1-8. Worker Nodes

  • 워커 노드는 kubelet, kube-proxy 그리고 도커와 같은 컨테이너 엔진을 실행한다. 동작 중인 파드를 유지시킨다.

1-8-1. kubelet

  • 각 노드에서 실행되는 에이전트로 컨테이너가 파드에서 실행되게 한다. CP 노드에서 워커 노드에 작업을 요청하는 경우 워커 노드의 kbuelet 이 작업을 실행한다.

1-8-2. kube-proxy

  • 클러스터의 각 노드에서 실행되는 네트워크 프록시로 쿠버네티스 서비스의 구현이다.

  • 노드의 네트워크 규칙(iptable 등)을 관리해 클러스터 외부에서 파드로 또는 클러스터 내부에서 파드 간 통신할 수 있도록 해준다.

1-9. Pods

  • 리눅스 컨테이너를 하나 이상 모아 놓은 것으로 쿠버네티스 애플리케이션의 최소 단위이다.
    리소스를 효율적으로 공유하기 위해 컨테이너를 파드로 그룹화 한다. 같은 파드에 속한 컨테이너는 동일한 컴퓨팅 리소스를 공유한다.

  • 기본적으로 파드 내의 컨테이너는 병렬적으로 실행된다.
    따라서 어떤 컨테이너가 먼저 사용 가능 한지 알 수 없다.
    initContainers 를 사용하면 파드 내 특정 컨테이너가 실행 완료된 후 다른 컨테이너가 실행 되도록 할 수있다.

  • 파드는 랜덤으로 지정되는 한 개의 고유한 IP를 가지고 있다. 그리고 파드 내 여러 컨테이너가 있다면 컨테이너들은 같은 IP 를 공유한다.
    파드 내 컨테이너 간에 통신 하려면 IPC, loopback 인터페이스 또는 파일 시스템 공유를 활용한다.

  • 보통 한 개의 파드에 한 개의 컨테이너를 실행하지만 로깅이나 보안 등 보조하는 태스크를 담당하는 컨테이너를 함께 실행하는 경우가 있다.
    이런 경우 사용하는 패턴을 sidecar 패턴이라고 한다.

  • 파드는 제네레이터를 통해 실행되거나 JSON 이나 YAML 파일을 통해 생성, 삭제될 수 있다. 또는 오퍼레이터/watch-loop 를 통해 실행될 수 있다.
    kubectl run newpod –image=nginx –generator=run-pod/v1
    kubectl create -f newpod.yaml
    kubectl delete -f newpod.yaml

1-10. Services

  • 파드 그룹의 단일 진입점을 제공한다.

  • 여러 파드 사이에서 인바운드 요청을 분산하는 NodePortLoadBalancer 같은 마이크로 서비스이다. 그리고 서비스는 인바운드 요청의 접근 정책을 관리한다.

  • 서비스는 어떤 오브젝트를 연결하는지 알기 위해 레이블 키와 값을 동등 비교 하거나 in, notin, exist 같은 연산자를 사용한다.

  • 서비스는 지정된 IP 로 생성이 가능하고 고유한 DNS 이름을 가질 수 있다.

  • 서비스는 네 가지 타입을 제공한다.
    1) ClusterIP : 파드 그룹의 단일 진입점인 가상 IP 를 생성한다.
    2) NodePort : ClusterIP 를 생성하고 각 워커 노드에 설정한 포트를 연다.
    3) LoadBalancer : NodePort 동작을 포함한다. 그리고 클러스터 외부의 로드밸런서와 NodePort 를 연결해 준다.
    AWS, AZURE 등 클라우드 플랫폼에서 사용할 수 있다.
    4) ExternalName : 클러스터 내부에서 네이밍 서비스를 사용할 수 있도록 지원한다.

1-11. Operators

  • 오퍼레이터는 쿠버네티스 어플리케이션을 패키징, 배포 및 관리하는 방법이다. 현재 상태를 질의하고 스펙과 비교한다. 상태가 스펙과 다른 경우 어떻게 동작할 지 작성된 코드에 맞게 동작한다.

  • 오퍼레이터는 쿠버네티스 API 기능을 확장해 애플리케이션 인스턴스 생성, 설정 및 관리한다.

1-12. Single IP per Pod

  • 파드는 기본적으로 랜덤으로 생성된 고유한 IP 한 개를 가진다.

  • 파드는 컨테이너들과 관련된 데이터 불륨이 함게 위치한 그룹이다. 파드 내 모든 컨테이너들은 같은 네트워크 네임스페이스를 공유한다.

  • Pause 컨테이너는 파드 내부 컨테이너들의 일종의 부모 컨테이너로서 리눅스 네임스페이스를 공유할 수 있도록 해준다.
    그리고 pid 네임스페이스 공유가 설정되었을 때, 파드에서 initProcess(pid 1) 로서의 역할과 좀비 프로세스를 정리하는 기능을 수행한다.

  • 파드 내에서 컨테이너 간에 통신하기 위해 IPC, loopback 인터페이스 또는 공통 파일시스템에 write 하는 방법을 사용한다.

1-13. Networking setup

  • 네트워크 관점에서 파드는 한 개의 VM 이나 물리적 호스트로 보여진다. 따라서 쿠버네티스의 네트워크는 파드에 IP 를 할당하고 모든 노드의 모든 파드 간에 트래픽 분배를 해야한다.

  • 파드는 NAT 없이 모든 노드의 모든 파드와 통신할 수 있다.

  • 쿠버네티스에는 해결해야 할 세 가지 네트워크 이슈가 있다.
    1) 컨테이너 간 통신 2) 파드 간 통신 3) 파드외 외부 간 통신

  • 서비스가 ClusterIP 타입으로 실행되면 가상 IP 를 만들어 파드 그룹의 단일 진입점으로 사용한다.
    서비스의 NodePort 타입은 ClusterIP 를 만들고 각 노드의 파드에 설정된 포트를 ClusterIP 와 함께 연다.
    클라우드 플랫폼 환경에서 로드밸런서가 있는 경우, 서비스는 LoadBalancer 타입으로 실행해 ClusterIP 를 만들고 NodePort 를 만들어 클러스터 외부 로드밸런서와 연결한다.

  • Ingress 컨트롤러는 요청을 적절한 서비스로 전달하는 역할을 한다.

1-14. CNI Network 설정 파일

  • Container Network Interface 는 파드 간 네트워크를 제어할 수 있는 플러그인을 만들기 위한 인터페이스 이다. 쿠버네티스는 kubenet 이라는 단순한 CNI 플러그인을 제공한다.

  • 보통 3rd party 플러그인을 사용하는데 Flannel, Calico 등을 사용한다. 쿠버네티스 네트워크에 오버레이 네트워크를 구성해준다.

  • 오버레이 네트워크는 노드 간의 네트워크 위해 별도의 네트워크 레이어를 구성한다.

1-15. Pod to Pod 통신

k8s_pod_network_basic

  • 파드 내 컨테이너들은 pause 컨테이너가 만든 가상 인터페이스를 통해 호스트의 docker0 브리지 네트워크로 연결된다.

  • 노드로 요청이 도착하면 호스트의 eth0 을 거쳐 docker0 브리지를 통해 veth0 으로 전송된다. 그 다음 veth0 에 연결된 컨테이너의 eth0 로 전달된다.

  • 클러스터 내 각각의 워커 노드에 있는 파드가 서로 통신할 때, 10.100.0.2 워커 노드와 10.100.0.3 워커 노드 내 파드 둘 다 172.17.0.2 로 같은 IP 를 가지기 때문에 문제가 있다.

  • 이 문제를 해결하기 위해 오버레이 네트워크를 지원하는 3rd party CNI 를 사용한다.

  • 각 파드는 오버레이 네트워크 상에서 IP 를 할당 받고 그 IP 를 통해 서로 통신할 수 있다.