1. Background
(1) Container vs VM (Virtual Machine)
- Process 격리의 Container vs Devcie 가상화의 VM
(2) Linux Container 란?
- 운영체제 수준의 가상화 기술
- Linux namespace, cgroup, chroot, AUFS 등의 커널 기능을 활용해 App Process를 Host OS와 격리하여 실행
- 커널을 공유하기 때문에 실행 속도가 빠르고 성능 손실이 거의 없음
- Container Runtime?
- Container를 실행하고 관리하기 위한 도구
- 변화: LXC → Docker → CRI-O
(3) Container의 특징
- 운영체제 수준의 가상화
- VM과 달리 하드웨어 가상화 없이 리눅스 커널을 공유하여 Process를 격리
- 커널 및 게스트 OS 관리를 위한 기능이 불필요
- 빠른 속도와 효율성
- Container 별로 요구되는 최소 자원이 매우 작기 때문에 (게스트 OS가 없기 때문) 실행 속도가 빠르고, 실행 가능한 process 수 만큼 container 생성 가능
- Depedency 및 버전 관리의 용이성
- Process 실행에 요구되는 dependency package 들은 filesystem으로 정의되며, 이를 통해 배포되어 있는 컨테이너들의 형상 관리가 용이함
- 독립적인 환경
- Container는 독자적인 filesystem에서 동작함으로 다른 container 환경 혹은 실제 Host 환경에 영향을 끼치지 않음
(4) Container 주요 기술
- Linux Namespace
- Process 별 사용 가능한 computing resource를 제한하는 Linux 커널 기능
- Control Group (cgroup)
- Process들의 computing resource 사용을 제한하고 격리시키는 Linux 커널 기능
- Root 디렉토리 격리 (chroot)
- '/'가 아닌 특정 경로를 process의 root 디렉토리로 변경하는 기술
- Linux Capabilities
- Root 권한을 세분화하여 process 별로 적용할 수 있도록 지원하는 기술
- Union Mount
- 한 directory에 여러 파일 시스템을 마운트할 때, 내용을 합치는 기술 (AUFS, OverlayFS)
(5) Kubernetes 란?
- Container Orchestration Tool (e.g. Docker Swarm, Docker Compose, Kubernetes, Apache Mesos)
- 대규모 클러스터 환경에서 컨테이너를 배포, 확장, 관리하기 위한 기술/도구
- Container를 서비스로 제공 시, 필요한 추가 기능 제공 (Load Balancer, Storage Orchestration, Self-Healing 등)
- 여러 개의 physical machine을 포함하는 서버 관리
2. Pod란?
(1) Pod 소개
- 파드는 쿠버네티스 애플리케이션의 기본 실행 단위
- 한 파드 내에 여러개의 컨테이너가 존재할 수 있음.
- 여러개의 컨테이너는 localhost로 통신하나 컨테이너끼리 같은 port를 사용할 수 없음
- 파드의 휘발성이기 때문에 삭제되고 재 생성될 때마다 IP가 바뀜
(2) Infra Container
- 파드가 실행될 때 파드의 인프라를 만들어주는 컨테이너로, Pause container, sandbox container 등으로도 표현
- Kubernetes 가 내부적으로 dockerd, containerd 등의 런타임을 사용해서 컨테이너를 파드화 시키는데, 이를 위한 추상화를 제공
- 파드 내부에 있는 모든 컨테이너의 상위 컨테이너(parent container) 역할을 수행
- 파드 내부에서 컨테이너 간의 네임스페이스(네트워크 네임스페이스)를 공유하여 컨테이너 간 통신이 가능하게 유지
- 프로세스 네임스페이스를 공유하여 각 파드의 1번 PID 역할을 수행
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define STRINGIFY(x) #x
#define VERSION_STRING(x) STRINGIFY(x)
#ifndef VERSION
#define VERSION HEAD
#endif
static void sigdown(int signo) {
psignal(signo, "Shutting down, got signal");
exit(0);
}
static void sigreap(int signo) {
while (waitpid(-1, NULL, WNOHANG) > 0)
;
}
int main(int argc, char **argv) {
int i;
for (i = 1; i < argc; ++i) {
if (!strcasecmp(argv[i], "-v")) {
printf("pause.c %s\n", VERSION_STRING(VERSION));
return 0;
}
}
if (getpid() != 1)
/* Not an error because pause sees use outside of infra containers. */
fprintf(stderr, "Warning: pause should be the first process\n");
if (sigaction(SIGINT, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
return 1;
if (sigaction(SIGTERM, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
return 2;
if (sigaction(SIGCHLD, &(struct sigaction){.sa_handler = sigreap,
.sa_flags = SA_NOCLDSTOP},
NULL) < 0)
return 3;
for (;;)
pause();
fprintf(stderr, "Error: infinite loop terminated\n");
return 42;
}
- C언어로 되어 있음 (Not Golang)
- pause() 를 통해 별도 Signal이 올 때까지 sleep 상태로 대기하고 있음
- 자식 프로세스가 종료되었을 때 "SIGCHLD" 이벤트가 발생하고 waidpid(-1) 함수를 통해 해당 자식 프로세스를 메모리 상에서 free시킨다 → 좀비 프로세스를 거둬들이는 역할 수행
(3) Pod 생성/삭제/실행
apiVersion: v1 # 오브젝트의 API 버전
kind: Pod # 리소스 종류
metadata: # 라벨, 주석, 이름 등 부가 정보 입력
name: my-nginx-pod
labels:
app: kubernetes-simple-pod
spec: # 리소스 생성을 위한 자세한 정보 입력
containers:
- name: my-nginx-container
image: nginx:latest
ports:
- containerPort: 80
protocol: TCP
Pod 생성
$ kubectl apply -f nginx-pod.yaml
Pod 삭제
$ kubectl delete pods my-nginx-pods
Pod 실행
# -it: --stdin(standard input, 표준 입력) --tty (teletypewriter) 옵션의 약자로 표준 입력을 명령줄 인터페이스로 작성
# --: 더블대시는 옵션을 끝을 알려주어서 파드 명령과 옵션을 구분
$ kubectl exec -it my-nginx-pods -- bash