나만의 쿠버네티스 간단 개념 정리

shinychan95·2021년 6월 21일
0
post-thumbnail

글 쓰는 목적

  • 지난 학기 과제 연구로 수행한 쿠버네티스 관련 지식을 정리한다.
  • 과제연구 중 불명확하게 진행한 부분과 미해결 부분을 해결한다.
    (컨테이너 배포 방식 자체에 대해 까막눈이라 일단 보류)
    - ConfigMap을 통해 컨테이너 기동에 필요한 환경 설정 파일 정의한다. (기동 시 컨테이너 내부로 환경 설정 파일 이동)
    - ClusterRole을 통해 프로메테우스 컨테이너가 쿠버네티스 API에 접근할 수 있는 권한을 부여한다.
    - 프로메테우스를 배포 후 외부 접속 과정에서 오류.

 

참고 자료

이 글은 짜깁기 스타일로 설명이 깔끔한 문장을 그대로 퍼온 것이 많다.

 

먼저 마이크로 서비스 및 컨테이너 개념부터

왜 갑자기 마이크로 서비스?

쿠버네티스를 정의해보면,

  • 자동으로 컨테이너를 관리하는 여러 가지 도구 중 사실상 표준이 된 플랫폼

그리고 참고 논문에 따르면,

  • Individual microservices are commonly delivered as so called containers.

즉, 왜 컨테이너 형태로 마이크로 서비스가 배포되고, 컨테이너 관리가 어떤 측면에서 필요한지 알아본다면, 쿠버네티스를 잘 이해할 수 있다.

 

마이크로 서비스 아키텍처

모노리틱 아키텍처

그림 출처

  • 하나의 큰 서버에 하나의 어플리케이션이 배포되는 구조
  • 데이터베이스 또한 하나의 데이터베이스에 모든 데이터가 저장되는 구조
  • 중앙 관리된 구조이기에 통제가 편하고, 같은 솔루션을 사용한다는 장점이 있다.

마이크로 서비스 아키텍처

그림 출처

  • 비즈니스 기능마다 어플리케이션과 데이터베이스 서버를 분리하는 방식
  • 서비스 개발하는 조직이 다를 경우, 독립적 기획, 개발, 운영의 권한을 가지게 되어 의존성 없이 효율적 개발 가능
  • 단점은 기술의 표준을 통제하기가 어렵고, API 또한 표준화 시키기 어려우며, 서비스가 증가할수록 연계가 복잡해져서 장애 발생 시 대처가 어렵다.

 

Devops 개념 및 런타임 인프라

Devops는 운영과 개발을 한 팀에서 하는 모델로, 개발과 운영 사이에서 오는 간극을 해결하고 개발된 시스템을 빠르게 배포하고, 운영 과정에서 얻은 노하우를 개발에 반영해서 시장의 요구 사항에 빠르게 반응하는데 그 목적을 둔다.

위에서 언급한 마이크로 서비스 아키텍처에 따라, 각 팀 별로 독립적인 어플리케이션 및 데이터베이스를 개발하므로, 이에 대한 배포 및 운영 또한 직접하는 편이 효율적이다.

개발과 운영을 모두 각 팀에서 맡아서 함에도 불구하고, Devops 엔지니어 및 SRE(System Reliability Engineer) 등과 같이 기존의 운영팀이 하던 일은 여전히 남아 있다. 왜 그럴까?

  • Devops는 개발팀이 개발/배포/운영을 모두 담당하는 셀프 서비스 모델이다. 셀프 서비스를 하기 위해서는 인프라가 플랫폼화 되어 있어야 한다.

그림 출처

위의 그림과 같이 Devops 팀은, 시스템을 실행할 수 있는 런타임 인프라를 개발 배포하고, 런타임 시스템에 대한 모니터링과 로깅을 제공하며, 이 시스템에 자동으로 배포할 수 있는 CI/CD 플랫폼을 구축한다. 이렇게 개발된 플랫폼에 개발팀은 개발된 시스템을 스스로 배포하고 운영하는 모델이다.

그림 출처

 

컨테이너와 컨테이너 관리 솔루션

이러한 플랫폼을 지원하기 위해서는 벤더 종속적이지 않고, 개발자가 손쉽게 운영 및 접근할 수 있는 인프라 관리 기술이 필요한데, 이런 기술로 많이 언급되는 기술이 컨테이너이다.

컨테이너에 대해 좀더 자세히 살펴보자.

그림 출처

리눅스 컨테이너(LXC)는 운영체제 수준의 가상화 기술로 리눅스 커널을 공유하면서 프로세스를 격리된 환경에서 실행하는 기술입니다.

보통의 컨테이너는 리눅스 컨테이너를 말한다. 왜냐하면, 컨테이너 기술의 배경이 되었던 것이 바로 리눅스 가상화 기술이기 때문이다. (물론, 현재는 Docker 팀과 마이크로소프트의 협업으로 Windosw 가상화도 가능하다.)

리눅스 컨테이너 동작 원리,

리눅스 컨테이너로 실행된 프로세스는 커널을 공유하지만, 리눅스 네임스페이스(Linux namespaces), 컨트롤 그룹(cgroup), 루트 디렉터리 격리 등의 커널 기능을 활용해 격리되어 실행됩니다. 이러한 격리 기술 덕분에 호스트 머신에게는 프로세스로 인식되지만, 컨테이너 관점에서는 마치 독립적인 환경을 가진 가상 머신처럼 보입니다. 간단하게 보면 file system만 가상화를 이루고 있다.

그림 출처

 

컨테이너 기술

가상머신은 운영체제 위에 하드웨어를 에뮬레이션하고 그 위에 운영체제를 올리고 프로세스를 실행하는 반면에, 컨테이너는 하드웨어 에뮬레이션 없이 커널을 공유해서 바로 프로세스를 실행한다.

Namespaces → 서로가 충돌하지 않고 독립적인 공간으로써 작동하도록 하는 리눅스 커널의 기능

  • mnt (파일시스템 마운트): 호스트 파일시스템에 구애받지 않고 독립적으로 파일시스템을 마운트하거나 언마운트 가능
  • pid (프로세스): 독립적인 프로세스 공간을 할당
  • net (네트워크): namespace간에 network 충돌 방지 (중복 포트 바인딩 등)
  • ipc (SystemV IPC): 프로세스간의 독립적인 통신통로 할당
  • uts (hostname): 독립적인 hostname 할당
  • user (UID): 독립적인 사용자 할당

cgroups (Control Groups) → 자원에 대한 제어를 가능하게 해주는 리눅스 커널의 기능

아래의 리소스들을 제어할 수 있다.

  • 메모리
  • CPU
  • I/O
  • 네트워크
  • device 노드(/dev/)

즉, 리눅스 시스템에서 바로 echo hello world 명령어를 실행하는 경우, 호스트 환경(루트 파일 시스템)에서 프로세스가 실행됩니다. 하지만, 컨테이너를 이용한 경우, 커널을 동일하게 사용하지만 파일 시스템을 다르게 사용한다.

> uname -a

# Linux bb0a9b851dbd 4.15.0-70-generic #79-Ubuntu SMP Tue Nov 12 10:36:11 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

> cat /etc/*-release

# DISTRIB_ID=Ubuntu
# DISTRIB_RELEASE=18.04
# DISTRIB_CODENAME=bionic
# DISTRIB_DESCRIPTION="Ubuntu 18.04.3 LTS"
# ...

컨테이너가 서로 다른 파일 시스템을 가질 수 있는 이유는 이미지(파일의 집합)를 루트 파일 시스템으로 강제로 인식시켜 프로세스를 실행하기 때문이다.

만들면서 이해하는 도커(Docker) 이미지: 도커 이미지 빌드 원리와 OverlayFS

위 글을 읽어보면, 도커 이미지(nginx:latest)가 레이어로 구성된 방식과 레이어 중 이미지의 베이스에 해당하는 debian:buster-slim 이미지가 있음을 알 수 있다. 리눅스 운영체제의 기본적인 디렉터리 구성을 확인할 수 있다.

 

그러면 그냥 리눅스 커널을 사용하는 프로세스 아닌가?

PID를 확인해보면, 호스트 상에서 실행중인 셸의 PID는 5673입니다. 그런데 컨테이너로 실행한 bash 셸의 PID는 1번이다. 일반적으로 리눅스에서 1번 프로세스는 init 프로세스로 특별한 의미를 가지고 있습니다. 실제로 호스트 상에서 pstree를 실행해보면 모든 프로세스가 1번 프로세스(systemd)에 물려있는 것을 확인할 수 있습니다.

컨테이너는 프로세스지만, 프로세스라고 부르기보다는 컨테이너라고 부르는 데는 이유가 있는 법입니다. 컨테이너는 (주로) 리눅스 커널에 포함된 프로세스 격리 기술들을 사용해서 생성된 특별한 프로세스입니다.

 

결론적으로,

Containers are lightweight packages of software that contains everything it needs to run, such as the code, libraries, and dependencies.

컨테이너는 리눅스 커널은 공유하지만 파일 시스템 가상화를 통해 격리되어 프로세스로써 동작하는 것이다. 또한 격리 기술을 통해 자체 CPU 점유, 메모리, 프로세스 공간 등도 가지게 된다. 그리고 독자적인 파일 시스템에 위 정의에서 나온 코드, 라이브러리, 의존성 등이 포함될 것이다.
(이건 확실하지 않다. 아마 이미지 레이어 중 하나에 포함되지 않을까)

 

 

쿠버네티스

쿠버네티스의 특징

선언적 API,

컨테이너가 어떤 상태이길 원하는지만 쿠버네티스에 설정하면 지속해서 컨테이너의 상태를 확인한다. 그리고 설정한 상태가 아니라면 그것에 맞게 맞춘다는 개념이다.

→ 이런 선언전 API 개발하는 법에 배우고 싶지만, 우선 Docker 코드 기반으로 공부를 해볼 예정이다.

 

쿠버네티스가 제공하는 기능

쿠버네티스는 다음을 제공한다.

  • 서비스 디스커버리와 로드 밸런싱 쿠버네티스는 DNS 이름을 사용하거나 자체 IP 주소를 사용하여 컨테이너를 노출할 수 있다. 컨테이너에 대한 트래픽이 많으면, 쿠버네티스는 네트워크 트래픽을 로드밸런싱하고 배포하여 배포가 안정적으로 이루어질 수 있다.
  • 스토리지 오케스트레이션 쿠버네티스를 사용하면 로컬 저장소, 공용 클라우드 공급자 등과 같이 원하는 저장소 시스템을 자동으로 탑재 할 수 있다.
  • 자동화된 롤아웃과 롤백 쿠버네티스를 사용하여 배포된 컨테이너의 원하는 상태를 서술할 수 있으며 현재 상태를 원하는 상태로 설정한 속도에 따라 변경할 수 있다. 예를 들어 쿠버네티스를 자동화해서 배포용 새 컨테이너를 만들고, 기존 컨테이너를 제거하고, 모든 리소스를 새 컨테이너에 적용할 수 있다.
  • 자동화된 빈 패킹(bin packing) 컨테이너화된 작업을 실행하는데 사용할 수 있는 쿠버네티스 클러스터 노드를 제공한다. 각 컨테이너가 필요로 하는 CPU와 메모리(RAM)를 쿠버네티스에게 지시한다. 쿠버네티스는 컨테이너를 노드에 맞추어서 리소스를 가장 잘 사용할 수 있도록 해준다.
  • 자동화된 복구(self-healing) 쿠버네티스는 실패한 컨테이너를 다시 시작하고, 컨테이너를 교체하며, '사용자 정의 상태 검사'에 응답하지 않는 컨테이너를 죽이고, 서비스 준비가 끝날 때까지 그러한 과정을 클라이언트에 보여주지 않는다.
  • 시크릿과 구성 관리 쿠버네티스를 사용하면 암호, OAuth 토큰 및 SSH 키와 같은 중요한 정보를 저장하고 관리 할 수 있다. 컨테이너 이미지를 재구성하지 않고 스택 구성에 시크릿을 노출하지 않고도 시크릿 및 애플리케이션 구성을 배포 및 업데이트 할 수 있다.

각각의 기능이 왜 존재하고 쓰이는 상황은, 책 공부를 통해 예시 기반으로 차후 정리 예정.

 

쿠버네티스 구성 요소

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

그림 출처

그림 출처

Architecture 요소에 대해 읽어보긴 했지만, 추상적으로 알아봤자 무슨 도움이 있을까. 우선 넘어간다.

 

쿠버네티스 오브젝트

쿠버네티스 오브젝트 는 쿠버네티스 시스템에서 영속성을 가지는 오브젝트이다. 쿠버네티스는 클러스터의 상태를 나타내기 위해 이 오브젝트를 이용한다. 구체적으로 말하자면, 다음같이 기술할 수 있다.

  • 어떤 컨테이너화된 애플리케이션이 동작 중인지 (그리고 어느 노드에서 동작 중인지)
  • 그 애플리케이션이 이용할 수 있는 리소스
  • 그 애플리케이션이 어떻게 재구동 정책, 업그레이드, 그리고 내고장성과 같은 것에 동작해야 하는지에 대한 정책

쿠버네티스 오브젝트는 하나의 "의도를 담은 레코드"이다. 오브젝트를 생성하게 되면, 쿠버네티스 시스템은 그 오브젝트 생성을 보장하기 위해 지속적으로 작동할 것이다.

 

쿠버네티스 기본 네 가지 오브젝트

쿠버네티스 내에는 기본이 되는 구성단위인 네 가지 오프젝트(Pod, Service, Volume, Namespace)가 존재한다. Pod는 쿠버네티스에서 가장 기본적인 배포 단위로, 컨테이너를 포함하는 단위이다. 쿠버네티스의 특징중의 하나는 컨테이너를 개별적으로 하나씩 배포하는 것이 아니라 Pod 라는 단위로 배포하는데, Pod는 하나 이상의 컨테이너를 포함한다.

그림 출처

Service는 위와 같이 여러 Pod(object)에 접근할 수 있는 하나의 IP로써, 본질적으로는 로드 밸런서 역할을 한다. Pod는 한군데에 고정해서 실행되지 않고, 클러스터 안을 옮겨 다니게 때문에 Service를 필요로 한다.

Volume은 Pod가 기동할때 디폴트로, 컨테이너마다 로컬 디스크를 생성해서 기동되는데, 이 로컬 디스크의 경우에는 영구적이지 못하다. 쿠버네티스는 이러한 한계를 극복하기 위해 PV(Persistent Volume)과 PVC(Persistent Volume Claim)을 지원한다. 전반적인 과정은, 먼저 사용자는 물리 디스크를 생성하고, 이를 PV로 선언한다. 그리고 PVC를 생성하고, Pod를 생성하면서 PVC를 바인딩해주면 영구적인 저장소를 사용할 수 있게 된다. Namespace는 간단히 소개하면 쿠버네티스 클러스터내의 논리적인 분리단위에 해당한다.

그림 출처

마지막으로 이해해야 할 부분은 컨트롤러이다. 앞서 소개한 기본적인 네 가지 오브젝트로 어플리케이션을 배포할 수 있다. 하지만, 컨트롤러를 통해 오브젝트를 더 편리하고 효율적으로 생성 및 관리한다. 컨트롤러는 Replication Controller (aka RC), Replication Set, DaemonSet, Job, StatefulSet, Deployment 들이 있다.

그림 출처

 

데이터베이스 예시로 쿠버네티스 배포 이해하기

# application/mysql/mysql-pv.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  • 먼저 mysql 관련 Service 및 Deployment 컨트롤러를 배포하기 전에, 영구적인 저장소에 대한 오브젝트를 배포해야 한다.
  • 내부를 살펴보면,
    • apiVersion - 이 오브젝트를 생성하기 위해 사용하고 있는 쿠버네티스 API 버전이 어떤 것인지
    • kind - 어떤 종류의 오브젝트를 생성하고자 하는지
    • metadata - 이름 문자열, UID, 그리고 선택적인 네임스페이스를 포함하여 오브젝트를 유일하게 구분지어 줄 데이터
    • spec - 오브젝트에 대해 어떤 상태를 의도하는지

오브젝트 spec에 대한 정확한 포맷은 모든 쿠버네티스 오브젝트마다 다르고, 그 오브젝트 특유의 중첩된 필드를 포함한다.

# mysql-deployment.yaml
apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
  - port: 3306
  selector:
    app: mysql
  clusterIP: None
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:5.6
        name: mysql
        env:
          # Use secret in real usage
        - name: MYSQL_ROOT_PASSWORD
          value: password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim
  • Deployment 컨트롤러 및 Service 오브젝트에 대한 배포 파일이다.
  • 해당 배포 파일을 보면, volume에 대한 명세(persistentVolumeClaim)과 mysql이 mount할 volume에 대한 명세를 적어준다.
profile
개발자로 일하는 김찬영입니다.

0개의 댓글