[우아한테크코스] 9월 29일 TIL

4 minute read

[infra] kubernetes

지금도 좋은데 쿠버네티스를 사용하고 싶은 이유?

컨테이너 단위로 관리하니 서로 다른 컨테이너 간 영향을 끼치지 않고 os를 새로 시작할 필요도 없다.

  1. 무중단 배포
  2. health checking
  3. Reverse proxy 감지
  4. 컨테이너 scale-out
  5. 여러 node에서 관리
  6. ip 중복 방지

master node의 관리 하에 다양한 컨테이너를 구동한다.

서버의 리소스가 원하는 상태로 알아서 관리하며 컨테이너 증설 시 내장 컨트롤러가 최적의 노드를 찾아 배치하고 자동으로 리소스 확장이 가능하다.

kubernetes

모든 리소스는 오브젝트의 형태로 관리된다. Orchestration!!!

각 오브젝트를 원하는 상태로 유지하는데, yml로 관리할 수 있다.

k8s compoent

node

Worker Node

pod 존재

  • kubelet

    pod 내 컨테이너 실행을 직접 관리할 뿐만 아니라 마스터와 워커 노드 간의 통신 역할도 담당하는 에이전트

    node 상태, Podspec에 맞춰 Pod들이 정상적으로 동작하는지 확인, cAdvisor를 통해 정보 수집

  • kube-proxy

    가상 네트워크 관리

  • DNS pod

    ip가 중복인 경우 각 service endpoint를 아는 dns pod가 worker node에 존재

구성요소

  • Cluster

    cluster 내 Ingress 내 Service 내 Pod들로 구성

  • Pod

    컨테이너의 집합으로 컨테이너 실행을 담당한다.

    컨테이너 어플리케이션을 배포하기 위한 기본 단위로 컨테이너의 생성, 운영, 제거 등을 관리한다.

    보통 하나의 pod에 하나의 컨테이너(온전한 어플리케이션)만 실행한다. 하지만 설정 리로더나 로그 수집 등을 위한 추가 컨테이너가 포함될 수 있고 이를 사이드카 컨테이너라고 부른다. 이렇게 만들어진 하나의 포드가 완전한 애플리케이션으로 동작하는 것이다.

    언제든지 삭제될 수 있다.

    pod의 ip는 클러스터 내부에서만 접근할 수 있다.(docker -p 옵션 없는거나 마찬가지)

    여러 리눅스 네임스페이스를 공유하는 여러 컨테이너들을 추상화하기 위해 pod라는 개념 사용한다.

    예를 들어 ubuntu, nginx를 하나의 컨테이너에서 띄웠다면 ubuntu의 로컬호스트에서 nginx 서버로의 접근이 가능하다. 포드 내의 컨테이너들이 네트워크 네임스페이스 같은 리눅스 네임스페이스를 공유한다는 말의 의미이다. 도커의 네트워크처럼 쿠버네티스의 포드도 동일한 네트워크 환경을 가지며 공유한다.

  • Replicaset

    복제 담당으로 pod의 갯수가 부족할 경우 복제해서 생성하는 일을 담당한다.

    정해진 수의 동일한 포드가 항상 실행되도록 관리한다.

    노드 장애 등의 이유로 포드를 사용할 수 없으면 다른 노드에서 포드를 다시 생성한다.

    라벨 셀렉터로 pod와 replicaset은 연결되어 있다. spec.matchLabel에 정의된 라벨을 통해 생성할 포드 찾는다(포드에도 metadata.labels에 정의되어 있음)

  • Deployment

    replicaset과 pod의 정보를 정의하는 오브젝트로 replicaset의 상위 오브젝트라 deployment가 생성되면 replicaset도 함께 생성된다.

    배포 담당으로 배포되고 있는상태를 확인하고 replicaset의 변경 사항을 저장하는 revision을 남겨 롤백할 수 있다.

    rolling update, scale out이 가능하다.

  • Service

    deployment를 통해 생성된 포드에 접근하도록 도와주는 Endpoint 담당 (docker -p 옵션처럼 포트를 연결하고 외부에 노출시켜줌)

    여러 개의 포드에 쉽게 접근할 수 있도록 고유한 도메인 이름 부여, 요청을 분산하는 로드밸런서 기능 수행, aws의 로드밸런서, 클러스터 노드의 포트를 통해 포드를 외부로 노출

    하나의 애플리케이션 묶음으로 Pod를 그룹핑하고 라벨로 분류

    Pod가 새로 생성되더라도 service를 통해 동일 도메인으로 접근 가능

    <서비스 이름>.<네임스페이스>.svc.cluster.local

    서비스가 생성되면 고유한 IP를 할당받아 클러스터에서 IP나 서비스 이름으로 포드 접근 가능

    1. ClusterIP

      쿠버네티스 내부에서만 포드들에 접근할 때

      Yaml selector로부터 특정 라벨 포드를 서비스에 연결 -> 접근할 때 사용하는 포트 targetPort로부터 get -> clusterIP 생성되면 내부 IP 할당 -> 클러스터 내에서 ip나 서비스명으로 접근 가능

    2. NodePort

      포드에 접근할 수 있는 포트를 클러스터의 모든 노드에 동일하게 개방, 외부에서 포드에 접근 가능

      nodePort에 들어온 요청은 서비스에 연결된 포드 중 하나로 라우팅, 내부에서도 clusterIP처럼 접근 가능

    3. LoadBalancer

      플랫폼에서 제공하는 로드 밸런서를 동적으로 프로비저닝해 포드에 연결, 외부 접근 가능

      서비스 생성되면 aws elb에 추가됨, 인스턴스에 노드들 할당되어 있음

      서비스 생성됨과 동시에 nodePort에 따른(랜덤) 포트 개방, 로드밸런서로 요청이 들어오면 워커 노드 중 하나로 전달되고 거기서 또 포드 하나로 전달되어 처리됨(다른 노드의 포드로 갈 수 있음) -> externalTrafficPolicy: Local로 해결 가능하지만 요청이 고르게 분산되지 않을 수 있음

    4. ExternalName

      외부 시스템과 연동해야할 때

      서비스가 외부 도메인을 가리킬 수 있음

      spec.type: ExternalName

      spec.externalName: ${외부도메인}

  • Ingress

    service의 nodePort + loadBalancer를 합쳐논 것과 같다.

    외부에서 들어온 요청을 어떠한 서비스로 라우팅해주고, 같은 ip에 대해 다른 도메인 이름으로 요청이 들어왔을 때 가상 호스트 기반의 요청 처리, ssl/tls 보안 연결 처리의 역할을 수행한다.

    외부의 요청을 대신 응답해주는 Reverse Proxy의 역할을 담당한다.

    각 디플로이먼트에 대해 일일히 설정을 적용할 필요 없이 외부 요청에 대한 처리를 편리하게 관리할 수 있다.

Master Node

Worker Node들 관리

  • Api server

    k8s(쿠버네티스)의 모든 구성 요소는 api server와 통신

    etcd와 통신할 수 있는 유일한 요소

  • etcd

    위에서 언급한 쿠버네티스가 바라는 오브젝트(pod, service, replicaset)들을 k-v 형태로 저장한다.

    etcd 인스턴스는 홀수로 구성해야 한다. 과반 투표를 하는 RAFT 알고리즘을 사용하기 때문에

  • scheduler

    pod를 어느 node에 배치할지 판단하는 역할

    스케줄될 수 있는 모든 노드의 목록과 affinity의 명세 확인 후 우선순위 지정

    node가 요청을 이행할 수 있는지, 리소스가 부족하지 않은지, node가 PodSpec의 Node Selector에 맞는 라벨을 가질 수 잇는지, port가 이미 사용되고 있는지, 볼륨 마운트 되는지 확인

명령어 실행 시

  1. kubectl을 이용해 api server에 요청
  2. 인증/권한승인/승인제어 등의 확인절차
  3. api server가 object를 적절히 변형(mutate)
  4. api server가 유효성 검사(validate)
  5. etcd에 저장
  6. 응답

image

1. deploy 생성 요청 -> etcd 기록
2. deployment controller가 감지 후 replicaset 생성 명령 -> etcd 기록
3. replicaset controller가 감지 후 pod 생성 명령 -> etcd 기록
4. scheduler가 감지 후 workernode에 pod 생성 명령 -> etcd 기록
5. kubelet가 node에 할당될 pod 할당을 감지하고 pod의 container 실행, api 서버에 반환

Network

  1. 컨테이너를 생성하면 veth 생성되고 컨테이너 내 eth0과 연결
  2. veth들은 docke0을 통해 컨테이너들간 통신 가능
  3. 컨테이너는 gateway인 docker0을 거쳐 외부와 통신

image

Pod 내 container들은 하나의 veth0을 공유하며 외부에서 같은 ip로 접근해 port로 컨테이너 구분

pod 내 pod의 init process인 pause 컨테이너를 생성하는데 이는 SIGTERM 명령 전까지 Sleep 상태로 존재

Pod 내 컨테이너 생성 시 $ docker run --net=container:{container_id} -d {image_name}로 생성

구성

쿠버네티스에서 사용할 수 있는 오브젝트 구경: kubecttl api-resources

그 오브젝트 설명이 보고싶다: kubectl explain pod

마스터 노드: api-server, controller manager, scheduler, coreDNS

모든 노드: kube-proxy, calico, flannel (오버레이 네트워크 구성)

네임스페이스

리소스를 논리적으로 구분할 수 있도록 하는 오브젝트로 포드, 레플리카셋, 디플로이먼트, 서비스와 같은 리소스들이 묶여있는 하나의 가상 공간

아무것도 설정하지 않으면 kubectl get pods --namespace default로 확인할 수 있다.

쿠버네티스 내부적으로 동작하는 pod들은 kube-system에 있다.

같은 네임스페이스 내의 서비스에 접근할 때에는 클러스터 내부에서 서비스 이름을 통해 접근할 수 있지만 다른 네임스페이스에서 접근할 때는 ${서비스 이름}.${네임스페이스이름}으로 접근해야 한다.

  • yaml 파일 생성하기
apiVersion: v1
kind: Namespace
metadata:
	name: production
  • 특정 네임스페이스에 리소스 생성하기

리소스 yaml 파일 내 metadata.namespace: ${NAMESPACE_NAME} 추가

설정값 포드에 전달하기

  1. yaml 파일에 환경변수 직접 하드 코딩 하기

    spec.env.~~

  2. Configmap, secret 사용하기