이 글은 짜깁기 스타일로 설명이 깔끔한 문장을 그대로 퍼온 것이 많다.
쿠버네티스를 정의해보면,
그리고 참고 논문에 따르면,
즉, 왜 컨테이너 형태로 마이크로 서비스가 배포되고, 컨테이너 관리가 어떤 측면에서 필요한지 알아본다면, 쿠버네티스를 잘 이해할 수 있다.
모노리틱 아키텍처
마이크로 서비스 아키텍처
Devops는 운영과 개발을 한 팀에서 하는 모델로, 개발과 운영 사이에서 오는 간극을 해결하고 개발된 시스템을 빠르게 배포하고, 운영 과정에서 얻은 노하우를 개발에 반영해서 시장의 요구 사항에 빠르게 반응하는데 그 목적을 둔다.
위에서 언급한 마이크로 서비스 아키텍처에 따라, 각 팀 별로 독립적인 어플리케이션 및 데이터베이스를 개발하므로, 이에 대한 배포 및 운영 또한 직접하는 편이 효율적이다.
개발과 운영을 모두 각 팀에서 맡아서 함에도 불구하고, Devops 엔지니어 및 SRE(System Reliability Engineer) 등과 같이 기존의 운영팀이 하던 일은 여전히 남아 있다. 왜 그럴까?
위의 그림과 같이 Devops 팀은, 시스템을 실행할 수 있는 런타임 인프라를 개발 배포하고, 런타임 시스템에 대한 모니터링과 로깅을 제공하며, 이 시스템에 자동으로 배포할 수 있는 CI/CD 플랫폼을 구축한다. 이렇게 개발된 플랫폼에 개발팀은 개발된 시스템을 스스로 배포하고 운영하는 모델이다.
이러한 플랫폼을 지원하기 위해서는 벤더 종속적이지 않고, 개발자가 손쉽게 운영 및 접근할 수 있는 인프라 관리 기술이 필요한데, 이런 기술로 많이 언급되는 기술이 컨테이너이다.
컨테이너에 대해 좀더 자세히 살펴보자.
리눅스 컨테이너(LXC)는 운영체제 수준의 가상화 기술로 리눅스 커널을 공유하면서 프로세스를 격리된 환경에서 실행하는 기술입니다.
보통의 컨테이너는 리눅스 컨테이너를 말한다. 왜냐하면, 컨테이너 기술의 배경이 되었던 것이 바로 리눅스 가상화 기술이기 때문이다. (물론, 현재는 Docker 팀과 마이크로소프트의 협업으로 Windosw 가상화도 가능하다.)
리눅스 컨테이너 동작 원리,
리눅스 컨테이너로 실행된 프로세스는 커널을 공유하지만, 리눅스 네임스페이스(Linux namespaces), 컨트롤 그룹(cgroup), 루트 디렉터리 격리 등의 커널 기능을 활용해 격리되어 실행됩니다. 이러한 격리 기술 덕분에 호스트 머신에게는 프로세스로 인식되지만, 컨테이너 관점에서는 마치 독립적인 환경을 가진 가상 머신처럼 보입니다. 간단하게 보면 file system만 가상화를 이루고 있다.
가상머신은 운영체제 위에 하드웨어를 에뮬레이션하고 그 위에 운영체제를 올리고 프로세스를 실행하는 반면에, 컨테이너는 하드웨어 에뮬레이션 없이 커널을 공유해서 바로 프로세스를 실행한다.
Namespaces → 서로가 충돌하지 않고 독립적인 공간으로써 작동하도록 하는 리눅스 커널의 기능
cgroups (Control Groups) → 자원에 대한 제어를 가능하게 해주는 리눅스 커널의 기능
아래의 리소스들을 제어할 수 있다.
/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 코드 기반으로 공부를 해볼 예정이다.
쿠버네티스는 다음을 제공한다.
각각의 기능이 왜 존재하고 쓰이는 상황은, 책 공부를 통해 예시 기반으로 차후 정리 예정.
쿠버네티스를 배포하면 클러스터를 얻는다.
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
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