도커와 쿠버네티스의 개요 Ch.02
in Devops on Kubernetes
컨테이너의 이해
쿠버네티스를 사용하기 위해 필요한 컨테이너 지식을 공부해보자.
쿠버네티스를 위해 알아둬야할 도커 지식
일반적인 배포 상황에서 애플리케이션을 빌드할 때 마다 다른 라이브러리를 사용하게 될 가능성이 있다.
오픈 소스 라이브러리의 사용은 개발 생산성을 높이지만 소프트웨어의 안정성을 유지하기는 어렵다. (버전이 올라가서 특정 기능이 동작하지 않는다던가 하는 상황)
도커는 이러한 문제를 컨테이너를 통해 해결하였다.
도커와 관련된 기초 지식 중 k8s를 위해 필요한 부분을 중점적으로 살펴보자
컨테이너를 사용하는 이유
컨테이너를 사용하는 이유는 다음과 같다.
1. 인프라의 사용률 향상
하나의 물리 서버나 가상 서버 위에 여러 컨테이너를 운영할 수 있다.
CPU와 메모리 사용률을 높여 하드웨어를 효율적으로 운영할 수 있다.
2. 빠른 기동 시간
컨테이너의 기동 시간은 가상 서버나 물리 서버의 기동 시간보다 훨씬 빠르다.
OS, Application, middleware 등 다양한 이미지를 손쉽게 얻을 수 있다.
설치 작업과 설정 작업이 줄어든다.
네트워크, 볼륨(외부 저장)을 소프트웨어 정의 오브젝트로 작성할 수 있다.
3. 불변 실행 환경
애플리케이션 실행에 필요한 소프트웨어를 모두 포함하여 컨테이너를 작성할 수 있다.
개발 환경과 운영 환경의 차이를 줄일 수 있다.
컨테이너를 조합하여 시스템을 구성함으로써 특정 서버 환경에 대한 종속성을 없앨 수 있다.
가상서버와 컨테이너의 차이점
가상 서버는 한 대의 머신 위에서 여러 대의 가상 서버를 구동할 수 있다.
가상 서버 구동 시간은 직접 돌려보면 알겠지만 엄청 느리다.
가상화 소프트웨어를 Hypervisor라 부르며 VMware, VirtualBox등이 있다.
컨테이너는 하나의 리눅스 프로세서가 마치 전용 서버에서 동작하듯이 분리한다.
이는 리눅스 커널의 네임스페이스와 컨트롤 그룹(cgroup)이라는 기술을 기반으로 한다.
컨테이너와 가상서버를 수치적으로 비교하면 아래와 같다.
특징 | 가상서버 | 컨테이너 |
---|---|---|
이미지 크기(CentOS 7.4 기준) | 최소 1.54GB | 최소 0.20GB |
메모리 사용량 | 기본 640MB | 기본 512MB |
벤치 마크 성능 비교 | 65%(Xen HVM 가상 서버 기준) | 90% |
OS 기동 시간 | 분 단위 | 초 단위 |
Window나 Mac에서 컨테이너를 돌리려면 리눅스 커널을 필요한데 이를 위한 가상 서버가 필요하다.
Docker CE
(Community Edition 18.06 이후)를 Mac이나 Window에서 구동하기 위해선 각각의 하이퍼바이저 위에서 LinuxKit
이 기동하며 그 위에서 컨테이너의 런타임인 containerd
가 기동한다.
Window 10에서는 Hyper-V
(가상화 시스템 이름)를 사용하며 Mac에서는 Hypervisor framework를 기반으로 한 HyperKit
을 사용한다.
LinuxKit
은 컨테이너를 실행하기 위한 경량의 리눅스 서브시스템으로 도커,IBM,리눅스 파운데이션,마이크로소프트,ARM,휴렛 패커드, 인텔과 같이 이름 들으면 다 알법한 회사들이 만들었다.
가까운 미래에는 LinuxKit
으로 표준화된 리눅스 커널을 통해 애플리케이션이 컨테이너 위에서 실행되는 것이 당연한 시대가 이미 왔다고 해도 과언이 아니다.
[ 가상 서버 위에 Docker를 구동하면 보안에 더 안전하다는 장점이 있다. ]
도커의 아키텍처
리눅스 커널이 제공하는 기능을 활용하면 자체 컨테이너를 만드는 것도 가능하다.
그러나 자체 컨테이너는 다른 커뮤니티에서 개발한 컨테이너를 재사용하는 것이 어렵고 공유하기도 어렵다.
도커는 개발자가 컨테이너를 이용해 개발 생산성을 높일 수 있도록 컨테이너를 Build(작성), Ship(이동), Run(구동)할 수 있는 기능들을 제공한다.
이는 도커 데몬 서버와 클라이언트인 도커 커맨드, 그리고 이미지의 보관소인 레지스트리로 구성된다.
도커 컨테이너
컨테이너는 하나의 프로세스이다.
리눅스의 네임스페이스나 컨트롤 그룹(Cgroups)를 통해 다른 프로세스들과 완전히 독립되어 실행되는 프로세스인 것이다.
컨테이너란 실행 가능한 이미지의 인스턴스라고 할 수 있다.
도커 컨테이너는 고유한 IP 주소를 가지는 하나의 독립된 서버처럼 동작한다.
도커 컨테이너는 stop으로 정지와 rm으로 삭제가 가능한데, 삭제되기 전까지 기동했을 때의 실행 옵션과 로그를 간직한다.
컨테이너가 stop 상태에서 다시 구동할 때는 IP 주소가 새롭게 할당된다.
도커 레지스트리
도커 레지스트리는 컨테이너의 이미지가 보관되는 곳이다.
도커는 기본적으로 docker hub를 사용한다.
레지스트리와 레포지토리는 이름이 비슷하여 헷갈리기 쉬운데, 레지스트리는 리포지터를 여러 개 가지는 보관 서비스이다.
리포지터리는 하나의 이미지에 대해 태그를 사용해 다양한 출시 버전을 보관한다.
레지스트리는 크게 퍼블릭 레지스트리, 클라우드 레지스트리, 비공개 레지스트리로 나뉜다.
레지스트리와 쿠버네티스의 관계
레지스트리는 쿠버네티스가 pod에서 컨테이너를 구동할 때 이미지를 받아오기 위해 반드시 필요한 서비스이다.
도커와 쿠버네티스의 연동
쿠버네티스는 도커를 컨테이너의 런타임 환경으로 사용한다. (12월 3일 까지는… 그랬다 이는 이후에 더 자세히 다룸)
쿠버네티스를 설치할 때 제일 먼저 도커를 설치해야 하는 것도 이 때문이다.
쿠버네티스와 도커의 연동은 다음과 같다.
도커 데몬 프로세스인 dockerd
와 연동하여 동작하는 containerd
프로세스는 원래 도커 기업이 개발하였다.
이후 2017년 3월 런타임 표준화를 위해 도커에서부터 CNCF(Cloud Native Computing Foundation)으로 기증되어 개발되었다.
이후 containerd
는 다양한 플랫폼 위에서 동작하는 업계 표준 코어 컨테이너 런타임으로 간결하고 높은 이식성을 목표로 개발되었다.
containerd
는 Docker CE 17.3에서 버전 0.2.3이 도입되었고 Docker CE 17.12에서는 1.1이 되었다.
이를 통해 이미지 보관 및 전송, 컨테이너 실행, 볼륨과 네트워크 연결과 같은 컨테이너의 라이프 사이클을 호스트에서 완전히 관리할 수 있게 되었다.
containerd
1.1버전 이후부터는 CRI(Container Runtime Interface)에 대응하여 네이티브 kubelet도 연동할 수 있게 되었다.
containerd
는 OCI(Open Container Initiative)의 표준 사양에 준하는 컨테이너 런타임 runC를 사용한다.
CRI를 통해 컨테이너 실행 요청을 받으면 containerd
는 containerd-shim
을 만든다.
(shim이란 틈을 메워서 높이나 수평을 맞추기 위한 끼움쇠를 의미하며, 여기서는 containerd
와 runC의 틈을 메워준다고 볼 수 있다.)
runC는 컨테이너를 띄운 후 바로 종료되며 이어서 containerd-shim
이 프로세스로 남게 된다.
이와 같이 내부 표준화가 진행됨에 따라 향후 쿠버네티스는의 컨테이너 실행 환경은 도커 설치를 필수로 하지 않게 되어, 보다 심플하고 경량으로 고속화될 수 있는 방향으로 개발이 진행되고 있고 한다.
(그런데 그 일이 실제로 일어났습니다. Kubernetes is deprecating Docker)
컨테이너를 위한 기술과 표준
컨테이너를 구성하는 기반 기술들에 대해 간략히 살펴보도록 하자.
리눅스 표준 규격과 리눅스 ABI(Application Binary Interface)
도커를 사용하면 다양한 리눅스 배포판을 기반으로 한 컨테이너 실행이 가능해진다.
CentOS, 데비안 9, 우분투 18.04를 OS로 하는 컨테이너들은 그들이 각각 사용하는 커널의 버전이 다 다르다.
그런데 Docker CE에서 이들 리눅스 배포판을 실행하면 컨테이너 커널은 4.9.93-linuxkit-aufs가 사용된다.
어떻게 리눅스 배포판과 커널 버전이 달라도 동작할까?
그 이유는 LBS(Linux Base Standard)가 소스 코드를 컴파일한 시점에 호환성 있는 머신 코드를 생성하도록 ISO 규격으로 표준화되어있기 때문이다.
또한 리눅스 ABI로 인해 리눅스 커널의 버전이 올라가도 유저 공간에서 동작하는 바이너리(머신 코드) 레벨의 호환성은 유지된다.
리눅스 커널 기술
네임스페이스(Namespace)
네임스페이스는 리눅스 커널에 사용된 기술로 컨테이너가 하나의 독립된 서버와 같이 동작할 수 있게 하는 기술이다.
네임스페이스를 사용하면 특정 프로세스를 다른 프로세스로부터 분리시켜 같은 네임스페이스 내에서만 접근할 수 있도록 제한할 수 있다.
네임스페이스 | 의미 | 역할 |
---|---|---|
pid | PID:Prodcess ID | 리눅스 커널의 프로세스 ID 분리 |
net | NET: Networking | 네트워크 인터페이스(NET) 분리 |
ipc | IPC:Inter Process Communication | 프로세스 간 통신 접근 관리 |
mnt | MNT:mount | 파일 시스템의 마운트 관리 |
uts | UTS:Unix Timesharing System | 커널과 버전 식별자 분리 |
컨트롤 그룹(cgroup)
도커는 리눅스 커널의 cgroup을 사용한다.
cgroup은 프로세스별로 CPU 시간이나 메모리 사용량과 같은 자원을 감시하고 제한한다.
유니온 파일 시스템(UnionFS)
UnionFS
는 다른 파일 시스템에서 파일이나 디렉터리를 투과적으로 겹쳐서 하나의 일관적인 파일 시스템으로 사용할 수 있게 한다.
도커에서는 UnionFS
의 여러 구현체(aufs, bufs, overlay2)중 하나를 선택할 수 있다.
예전에는 aufs였는데 Docker CE 17.12에서는 보다 빠르게 동작하고 구조가 간단한 overlay2가 사용된다고 한다.
OCI(Open Container Initiative)
컨테이너 표준 사양을 책정하기 위해 만들어진 단체의 이름이다.
설립 초기에는 사실상 Docker가 표준이었는데 CentOS가 또 다른 표준화를 진행하고 있어 업계의 표준화가 필요했다.
OCI의 설립에는 도커를 비롯한 CoreOS, 구글, IBM, 레드햇, AWS, VMware, HP, EMC, Pivotal, Microsoft, 리눅스 파운데이션 등이 주요 멤버로 참여했다. (라인업 화려…)
이 OCI의 표준 사양에 맞춰 도커가 구현한 것이 바로 컨테이너 런타임 runC
이다.
마무리
총 7개로 이번 장을 요약할 수 있다.
컨테이너를 사용하면 오픈 소스를 효율적으로 사용하여 애플리케이션 개발이 가능하며 안정적인 배포가 가능하다.
컨테이너를 사용하는 의의 2-1. 인프라 사용 효율 향상 2-2. 빠른 가동 시간 2-3. 불변 실행 환경
가상 서버와 비교했을 때 컨테이너의 장점 3-1. 경량 3-2. 빠른 기동 3-3. 이식성
도커의 아키텍처는 클라이언트/서버 모델을 따르며, 서버인 도커 데몬이 클라이언트인 도커 커맨드로부터 요청을 받아 동작한다.
컨테이너의 이미지를 보존하는 레지스트리는 개발한 컨테이너 이미지를 쿠버네티스에서 실행하기 위한 중간 저장고와 같은 존재이다.
쿠버네티스 스택은 예전부터 주로 도커를 런타임 환경으로 사용했는데, CIR로 컨테이너 실행 환경을 연동하는 식으로 발전해오고 있으며 현재 1.20 버전 이후부터는 도커를 컨테이너 런타임으로써 사용하지 않는다고 한다.
컨테이너 내의 실행파일은 리눅스의 표준 규격 LBS와 리눅스 ABI에 의해 실행이 보증된다. 그리고 컨테이너는 OCI가 정하는 업계 표준을 지킴으로써 이식성이 확보된다.