본문 바로가기

더 나은 엔지니어가 되기 위해/읽고 쓰며 정리하기

쿠버네티스(kubernetes) 기본 개념 정리

조대협님 블로그의 쿠버네티스 시리즈 를 공부하며 정리한 글입니다.
자세한 내용은 생략하고 핵심만 담았습니다.


1. 쿠버네티스란?

컨테이너 운영환경에서, 컨테이너들을 적절하게 매니징하는 솔루션
예를 들어, 서비스 요청이 많아지는 경우 이에 대한 부하를 줄이기 위해 노드 수를 동적으로 늘림.
대표적으로 다음과 같은 역할을 함.

  • 스케쥴링
    • 컨테이너별 자원 최적화
    • 적절한 위치(클러스터 내 노드)에 배포
  • 정상/비정상 상태 체크 및 재기동
  • 컨테이너 리소스 모니터링
  • 컨테이너 동적으로 삭제 관리

이러한 일들을 자동화하게끔 하는 솔루션임.


2. 기본 개념 이해

1) 구조와 오브젝트

  • 클러스터 구조

    • 마스터 / 머신 노드가 존재.
    • 마스터는 하나, 머신은 여러개
  • 쿠버네티스 오브젝트

    • 기본적으로 오브젝트와 컨트롤러로 구성

    • 오브젝트

      • yaml 이나 json 으로 정의된 스펙에 따라 만들어지는 객체
    • 예를 들면 다음과 같이 스펙을 정의

    •  

      # pod_example.yaml
      apiVersion: v1
      kind: Pod
      metadata:
        name: nginx
      spec:
        containers:
        - name: nginx
          image: nginx:1.7.9
          ports:
          - containerPort: 8090
    • kubectl create -f pod_example.yaml 로 오브젝트 생성

2) 기본 오브젝트 종류

  • Pod

    • 가장 기본적인 배포 단위
    • 컨테이너들을 포함하고 있음
  • Volume

    • 컨테이너의 외부 디스크
    • 컨테이너가 재 실행되어도 Volume 을 사용하면 파일 유지
  • Service

    • 여러 개의 Pod 들을 서비스할 때, 현재 요청이 어느 Pod 으로 갈지 선택하는 오브젝트
    • 부하가 많을 때 이를 분산시키는 로드밸런서 역할
  • Controller

    • 여러 개의 Pod 배포를 적절하게 관리하는 오브젝트
    • Pod 들을 생성, 삭제함.
      • Replication Controller
      • Replication Set
    • Deployment
  • 그 외 개념

    • 네임 스페이스

      • 한 클러스터 내에 논리적인 분리 단위
      • 예를 들면 namespace:billingnamespace:commerce 는 같은 클러스터 내에 있지만 논리적으로 분리됨
      • 한 클러스터 자원을 가지고 개발 / 운영 / 테스트 식으로 나눌 수 있음.
    • 라벨

      • 리소스를 선택하는데 사용됨

      • 예를 들면 Pod 안에 다음과 같이 라벨을 달 수 있음.

      • kind: Pod
        ...
        metadata:
          labels:
            app: myapp
      • selector 를 사용하여 리소르를 선택함

      • 예를 들어, Service 는 다음과 같이 Pod 을 선택함

      • kind: Service
        ...
        spec:
          selector:
            app: myapp

3. 아키텍처

쿠버네티스 내부 구조는 크게 마스터와 노드로 분리될 수 있다.

1) 마스터

쿠버네티스 클러스터 전체를 컨트롤하는 시스템이다.
다음과 같이 구성되어 있다.

  • API 서버
    • 모든 명령과 통신을 REST API 로 제공
  • Etcd
    • 분산형 키-밸류 스토어로, 쿠버네티스 클러스터 상태나 설정 정보 저장
  • 스케쥴러
    • Pod, Service 등 각 리소스를 적절한 노드에 할당
  • 컨트롤러 매니저
    • 컨트롤러들을 생산, 배포 등 관리
  • DNS
    • 동적으로 생성되는 Pod, Service 등의 IP 를 담는 DNS

2) 노드

마스터에 의해 명령을 받고 실제 서비스하는 컴포넌트다.
다음과 같이 구성되어 있다.

  • Kubelet
    • 마스터 API 서버와 통신하는 노드 에이전트
  • Kube-proxy
    • 노드로 오는 네트워크 트래픽을 적절한 컨테이너로 라우팅
    • 네트워크 트래픽 프록시 등 노드-마스터간 네트워크 통신 관리
  • Container runtime
    • 컨테이너를 실행 (ex. dcoker)
  • cAdvisor
    • 각 노드 내 모니터링 에이전트
    • 노드 내 컨테이너들의 상태, 성능 수집하여 마스터 API서버에 전달

4. 기본 오브젝트

대표적인 기본 오브젝트들을 조금 더 자세히 살펴본다.

1) Pod

쿠버네티스에서 가장 기본적인 배포 단위다.
하나 이상의 컨테이너들을 포함한다.

예를 들어 다음과 같이 yaml 포맷으로 정의할 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.7.9
    ports:
    - containerPort: 8090

Pod 은 다음과 같은 특징을 지닌다.

  • Pod 내 컨테이너들은 같은 IP 를 가지고, Port 를 공유한다.
  • Pod 내 컨테이너들끼리는 Volume 을 공유할 수 있다.

2) Controller

Controller 의 주 역할은 Pod 을 생성, 관리하는 것이다.
예를 들어 다음과 같이 yaml 포맷으로 정의할 수 있다.

apiVersion: v1
kind: ReplicationController
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    app: nginx
  template:
    metadata:
      name: nginx
      labels:
        app: nginx
      spec:
        containers:
          - name: nginx
            image: nginx
            ports:
            - containerPort: 80

종류는 다음과 같다.

  • ReplicationController (RC)
    • 지정된 숫자로 Pod 을 기동 시키고 관리한다.
    • selector, replicas, template 을 기본적으로 정의해야 한다.
      • selector : label 기준으로 어떤 Pod 들을 관리할지 정의한다.
      • replicas : 선택된 Pod 들을 몇 개의 인스턴스로 띄울지 정의한다.
      • template : Pod 을 추가로 기동할 때, 어떤 Pod 을 만들지 정의한다.
  • ReplicaSet
    • ReplicationController 는 Equailty 기반 Selector 를 사용하는 반면
    • ReplicaSet 는 Set 기반 Selector 를 사용한다.
  • Deployment
    • ReplicationController 와 ReplicaSet 을 좀더 추상화한 개념
    • 실제 배포할 때는 이 컨트롤러를 주로 사용
  • DaemonSet
    • Pod 이 각각의 노드에서 하나씩만 돌게 한다. (균등하게 배포)
    • 보통 서버 모니터링이나 로그 수집 용도
    • 모든 노드가 아닌 특정 노드들만 선택할 수도 있다.
  • Job
    • 한번 실행되고 끝나는 Pod 을 관리한다.
    • Job 컨트롤러가 종료되면 Pod 도 같이 종료한다.
    • 컨테이너에서 Job 을 수행하기 위한 별도의 command 를 준다.
    • Job command 의 성공 여부를 받아 재실행 또는 종료여부를 결정한다.
  • CronJob
    • 주기적으로 돌아야하는(배치) Pod 을 관리한다.
    • 별도의 schedule 을 정의해아 한다.
  • Stateful
    • DB 와 같이 상태를 가지는 Pod 을 관리한다.

3) Volume

Volume 은 Pod 에 종속된 디스크다.
따라서 Pod 내 여러 컨테이너들이 공유해서 사용할 수 있다.

예를 들면 다음과 같이 Pod 을 정의할 때 volumes 를 통해 정의할 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: shared-volumes 
spec:
  containers:
  - name: redis
    image: redis
    volumeMounts:
    - name: shared-storage
      mountPath: /data/shared
  - name: nginx
    image: nginx
    volumeMounts:
    - name: shared-storage
      mountPath: /data/shared
  volumes:
  - name : shared-storage
    emptyDir: {}

종류는 다음과 같다.

  • 임시 디스크 (Pod 단위 공유)
    • emptyDir
      • Pod 이 생성되고 삭제될 때, 같이 생성되고 삭제되는 임시 디스크
      • 생성 당시에는 아무 것도 없는 빈 상태
      • 물리 디스크(노드), 메모리에 저장
  • 로컬 디스크 (노드 단위 공유)
    • hostPath
      • emptyDir 와 같은 컨셉이지만, 공유 범위가 노드라는 점만 다름
  • 네트워크 디스크
    • gitRepo (지금은 deprecated 라고 한다.)
      • 생성시에 지정된 git repo 를 clone 한 후, 디스크 생성
      • emptyDir -> git clone 이라보면 됨
    • 그 외 클라우드 서비스 별로 더 있음.

PV, PVC, Dynamic Provisioning 부분은 정리에서 생략함.

4) Service

Service 는 같은 어플리케이션을 운용하는 Pod 간의 로드 밸런싱 역할을 한다.
또, 동적으로 생성되는 Pod 들의 동적 IP 와 달리 Service 는 지정된 IP 로 생성가능하다.
(즉 Pod 접근은 어려운 반면, Service 접근은 더 쉬움)

다음과 같은 기능들이 있다.

  • 멀티 포트 지원
    • 예를 들어 80 -> 8080 으로, 443 -> 8082 로 가도록 한번에 설정할 수 있다.
  • 로드 밸런싱
    • 부하(트래픽)를 여러 Pod 으로 분산한다.
    • Pod 은 기본적으로 랜덤하게 선택된다.
  • IP 주소 할당 방식과 연동 서비스에 따른 Type
    • Cluster IP
      • 디폴트 값
      • 서비스에 클러스터 내부 IP 를 할당
      • 즉 클러스터 내부 접근 O, 외부 접근 X
    • Load Balancer
      • 외부 IP 를 가지고 있는 로드밸런서를 할당
      • 즉 외부 접근 O
    • NodePort
      • 클러스터 내 노드의 ip:port 로도 접근가능하게 함
      • ex. curl 10.146.0.10:30036
      • 10.146.0.10 는 노드의 ip 고, 30036 는 NodePort 로 설정한 포트임
    • ExternalName
      • 외부 서비스를 쿠네터네스 내부에서 호출하고자 할 때 사용
      • 모든 Pod 들은 Cluster IP 를 가지고 있기 때문에, 외부에서도 접근이 가능함.
      • 요청 -> (외부 서비스) -> 클러스터 내 쿠버네티스. 일종의 프록시 역할
  • headless 서비스
    • 서비스 디스커버리 솔루션을 제공하는 경우, 서비스의 IP 가 필요 없음
    • clusterIP: None 으로 주면 된다.
  • External IP
    • 서비스에 별도의 외부 IP 를 지정해줄 수 있음

3) Ingress

Ingress 는 api 게이트 웨이, 즉 url 기반 라우팅 역할을 한다.
Service 앞에 붙는다.

예를 들어, /user 로 들어오는 트래픽은 service A 에,
/products 로 들어오는 트래픽은 service B 로 라우팅 시켜준다.

Ingress 를 Service 앞에 달아두면, Service 는 NodePort 타입으로 선언되어야 한다.


* 실습

실제로 이 개념들이 어떻게 적용되어 환경을 구성하는지 살펴본다.
여기서는 여러 컨테이너들을 담고있는 Pod 을 여러개를 만들어보고 이를 쿠버네티스로 관리하는 환경을 실습한다.
자세한 실습과정은 원본 링크를 참고하고, 여기서는 그 과정만 간략히 정리해보겠다.

먼저 쿠버네티스 활용 전 준비과정은 다음과 같다.

  1. 컨테이너 이미지를 만든다.
  2. 클러스터(GCP Kubernetes Cluster)를 준비한다. (클라우드 - 로컬 연동 및 인증과정 포함)
  3. 이미지 저장소(GCP Container Registry)에 컨테이너 이미지를 담아둔다.

이후 쿠버네티스에서 실제로 Pod 을 기동하는 것은 다음과 같이 한다.

  1. ReplicationController 를 정의하는 .yaml 를 작성한다.
  2. 여기에는 Pod 내에 어떤 컨테이너가 들어갈지를 포함한다.
  3. 이후 쿠버네티스 cli 명령어로 ReplicationController 를 기동시킨다.
  4. 결과로 Controller 와 Pod 들이 생겨난다.
  5. Service 도 정의한 후 쿠버네티스 cli 명령어로 기동시킨다.
  6. 이제 외부에서 (로컬 클라이언트) 쿠버네티스로 띄운 Pod(컨테이너들)에 접근할 수 있다.

5. HealthCheck

HealthCheck 는 각 컨테이너 상태를 주기적으로 문제가 있는지 체크하는 기능이다.
문제가 있으면 재시작하거나, 서비스에서 제외한다.

1) 방법

  • Liveness probe
    • 응답이 없으면 컨테이나 자동 재시작
    • kubelet 을 통해서 재시작함.
  • Readiness probe
    • 서비스가 일시적으로 작동 불가인 경우 서비스에서 제외

2) 방식

  • Commnad probe
    • cat /tmp/healthy 실행. 성공 시 0 리턴 -> 정상 판단
  • HTTP probe
    • GET 요청 -> status 200 시 정상 판단
  • TCP probe
    • TCP 연결 시도 -> 연결 성공하면 정상 판단

6. 배포

실제로 운영환경에서 어떻게 Pod 들을 배포하는지 살펴본다.

1) 배포 방식

  • 롤링 업데이트
    • 일반적으로 많이 사용하는 배포 패턴
    • 새 배전을 배포하면서, 새 인스턴스를 하나씩 늘려가고,
      기존 버전은 하나씩 줄여나가는 방식
    • 기존 버전과 새 버전이 동시에 존재할 수 있다.
    • 시스템을 무장애로 업데이트 할 수 있다.
    • 새 버전용 컨트롤러와, 기존 버전용 컨트롤러 두개가 필요하다.
  • Deployment
    • 쿠버네티스에서 일반적으로 사용하는 배포 패턴
    • 롤링 업데이트를 좀 더 자동화한 객체 (컨트롤러 개념에 포함된다.)
    • 컨트롤러를 따로 정의하지 않고, Deployment 객체만 만든다.
    • Deployment 객체가 자동으로 롤링업데이트를 해준다.

2) Deployment

사실상 지금까지 정리한 오브젝트들을 Deployment 를 통해서 배포하는 듯 하다.
yaml 파일로, 예를 들어 다음과 같이 정의할 수 있다.

apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: hello-deployment
spec:
  replicas: 3
  minReadySeconds: 5
  selector:
    matchLabels:
      app: hello-deployment
  template:
    metadata:
      name: hello-deployment-pod
      labels:
        app: hello-deployment
    spec:
      containers:
      - name: hello-deployment
        image: gcr.io/terrycho-sandbox/deployment:v1
        imagePullPolicy: Always
        ports:
        - containerPort: 8080

7. ConfigMap

ConfigMap 은 설정, 환경 변수들을 담는 객체다.
Pod 을 생성할 때 이를 넣어줄 수 있다.
예를 들어 개발/운영에 따라 환경 변수값이 다른 경우, ConfigMap 을 활용할 수 있다.

ConfigMap 을 활용하는 방법은 크게 3가지가 있다.

  • 문자(Literal)
  • 파일
  • 디스크 볼륨 마운트

1) 문자로(Literal)

.yamldata 부분에 key : value 형태로 설정 변수들을 담을 수 있다.

# hello-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: hello-cm
data:
  myName: heumsi # key : value 형태로 저장
  email: heumsi@socar.kr

이후 Deployment 에서 다음과 같이 container env 내에 ConfigMap 객체를 설정해줘야 한다.

apiVersion: v1
kind: Deployment
spec:
  ...
  template:
    ...
    spec:
      containers:
      - name: cm
        ...
        env:
        - name: MY_NAME
          valueFrom:
            configMapKeyRef:
               name: hello-cm
               key: myName

이제 이 설정 값은 컨테이너 앱 내에서 다음과 같이 사용할 수 있다.

var os = require('os');
console.log(" my name is "+process.env.MY_NAME+ "\n"))

2) 파일

다음과 같이 파일을 생성할 수 있다.

# profile.properties
myName=heumsi
email=heumsi@socar.kr

위 Deployment 정의에서 일부만 다음과 같이 수정하면 된다.

        env:
        - name: MY_NAME
          valueFrom:
            configMapKeyRef:
               name: cm-file
               key: profile.properties

다만 이 때 사용자측에서 process.env.MY_NAME 을 얻어오면 profile.properties 를 key/value 형태가 아닌 스트링으로 받아온다.

3) 디스크 볼륨 마운트

예를 들어 /config/ 라는 디렉토리에 설정 변수들을 넣는다고 하면 Deployment 정의를 다음과 같이 설정하면 된다.
volumeconfigMap 으로 정의한다.

apiVersion: v1
kind: Deployment
spec:
  ...
  template:
    ...
    spec:
      containers:
      - name: cm
        ...
        volumeMounts:
          - name: config-profile
            mountPah: /tmp/config
      volume:
        - name: config-profile
          coinfigMap:
            name: cm-file

이제 해당 콘테이너 내에 /config/profile.properties 가 생기게 된다.


8. Secret

ConfigMap 은 일반적인 환경설정 변수를 담는다면,
Secret 은 보안이 중요한 설정 변수들을 담는 객체다.

  • 하나의 Secret 의 메모리 사이즈는 최대 1M 다.
  • 메모리에 저장되기 때문에 꼭 필요한 정보만 담도록 해야한다.
  • 키/값 형태이지만 값은 base64 로 인코딩해야한다.
    • base64 인코딩을 쓰는 이유는, 문자열 뿐만 아니라 일반적인 바이너리 파일을 담을 수 있도록 하기 위함

1) 사용법

여기서는 파일로 마운트하는 방법만 살펴본다.
먼저 다음과 같이 두 파일이 있다.

# user.property
heumsi
# password.property
mypassword

다음 명령어로 Secret 을 만든다.

kubectl create secret generic db-password --from-file=./user.property  --from-file=./password.property

genric 타입의 db-password 라는 이름의 Secret 을 만든 것이다.
이 때 파일의 이름이 키가되고, 파일의 내용이 값이 된다.
파일 마운트 방식은 값을 자동으로 base64 인코딩 한다.

Secret 을 담는 Volume 을 별도로 지정하자.
Deployment 정의에서 spec 부분에 다음을 추가하자.

spec:
  volumes:
  - name: db-password
    secret:
      secretName: db-password
      defaultMode: 0600 # 0600 permission (rw-------). default 는 0644 다.

이제 위 Deployment 를 배포하고 나서 해당 팟의 /tmp/db-password 로 들어가면 user.propertypassword.property 이 생성되어 있는 걸 볼 수 있다.


9. 마무리

여기서는 기본적으로 알아야하는 개념들 위주로 살펴보았다.
간단한 사용법과 어떻게 돌아가는지 정도? 감만 잡으려고 한다.

보안, 모니터링 등은 추후에 필요한 경우 다시 살펴볼 예정.
다음에는 쿠버네티스 관련 서적을 읽고 좀 더 심도깊게 살펴보아야 겠음.


* 참고

  • kubeTM Blog 에 가면 각 .yaml 에 대한 내용들을 간단히 살펴볼 수 있다.