brunch

You can make anything
by writing

- C.S.Lewis -

by Dayone Feb 17. 2020

[EKS 입문] 컨테이너 알아보기

EKS를 이해하기 위한 여정 1


컨테이너를 다룰 줄 압니다?


저는 컨테이너에 대한 이해가 높고, 컨테이너를 다룰 줄 압니다.

제가 회사를 취직하려고 이곳저곳 원서를 넣을 때 꼭 넣었던 말입니다. 이렇게 적은 이유는 도커(Docker)를 다뤄본 적이 있고, 실제로 서비스에 적용해서 운영을 해본 적이 있었기 때문입니다. 그런데 실제로 컨테이너에 대해서 자세하게 공부를 해보지는 못했습니다. 그래서 면접을 보는데 컨테이너에 대해서 설명을 하지 못하였습니다. 다행히 회사에서 Amazon EKS(Elastic Kubernetes Service)를 사용할 기회가 생겨서 이번 기회에 처음부터 차근차근 쌓아보기로 했습니다.


컨테이너부터 다시 공부해보자



독립성과 이식성


컨테이너가 각광을 받은 가장 큰 이유는 독립성과 이식성에 있습니다. 독립성이란 같은 호스트에서 작동하는 컨테이너라고 할지라도 서로간 영향을 받지 않는 독립적인 프로세스임을 의미합니다. 따라서 같은 구성의 컨테이너를 여러개 띄우더라도 각자 서로 다른 공간이라는 것입니다. 이러한 특성 때문에 이식성도 상당히 좋습니다. 각각 독립적으로 돌아가기 때문에 다른 호스트에 쉽게 옮길 수 있습니다.


그러면 컨테이너는 어떻게 독립성을 유지할까요?


컨테이너는 운영체제에서 돌아가는 프로세스입니다. 프로세스는 네임스페이스(Namespace)라는 것을 가지고 작업을 진행합니다. 기본적으로 리눅스는 하나의 네임스페이스를 가지고 있고, 별다른 설정을 하지 않으면 그 위에서 동작하는 프로세스는 전부 같은 네임스페이스 아래에서 작동합니다. 그래서 리눅스에서 프로세스를 조회해보면 모든 프로세스를 다 볼 수 있습니다. 


그런데, 도커를 써보시면 아시겠지만 컨테이너 내에서 프로세스를 조회하면 해당 컨테이너에서 작동하는 프로세스들만 보입니다. 아래 사진의 왼쪽은 저의 맥북(호스트)에서 직접 ps -ef 명령어를 통해 프로세스 리스트를 나타낸 사진입니다. 제가 10개만 뽑아서 이렇게 보이지만 grep을 빼면 모든 프로세스를 전부 보실 수 있습니다. 반면 오른쪽은 맥북에 직접 컨테이너를 올려서 내부 프로세스 리스트를 확인한 사진입니다. 보시면 전체 2개 밖에 없습니다. 

(왼쪽) 호스트 프로세스 리스트, (오른쪽)새로 생성한 컨테이너 내부의 프로세스 리스트

다시 맥북으로 돌아와서 프로세스를 살펴보면 위의 사진처럼 컨테이너가 프로세스로 떠 있는 것을 보실 수 있습니다. 



네임스페이스(Namespace)


위의 예시에서처럼  자신이 사용하고 있는 리소스에 대한 지표를 제한해서 볼 수 있도록 해주는 것이 바로 네임스페이스입니다. 결국 독립적으로 공간이 분리된 것처럼 만들어주는 것이 바로 네임스페이스인 셈입니다. 네임스페이스에는 다양한 종류가 있습니다.


| 네임스페이스의 종류

1. pid 네임스페이스

- 같은 pid 네임스페이스를 가진 프로세스끼리 프로세스의 아이디(pid)를 구분합니다. 모든 pid 네임스페이스는 1부터 시작하며, 1번 프로세스가 죽게 되면 해당 네임스페이스에 있는 모든 프로세스가 종료됩니다. 결국 같은 프로세스라고 할지라도 어떤 pid네임스페이스에 있냐에 따라 서로 다른 pid를 가질 수 있습니다.


2. net 네임스페이스

- 같은 net 네임스페이스에 있는 프로세스끼리 같은 네트워크 스택(Routing table, iptables rules, sockets, network interface 등)을 공유합니다. 컨테이너에서는 eth0가 net 네임스페이스입니다. 


3. mnt 네임스페이스

- 각 프로세스는 자신의 root 파일시스템을 가지고 있습니다. 뿐만 아니라, /tmp, /proc, /sys와 같이 공통적으로 필요한 파일시스템도 mnt 네임스페이스마다 따로 분리됩니다. 


4. IPC 네임스페이스

- IPC 네임스페이스가 다른 프로세스는 서로 다른 IPC Semaphores, IPC message queue, IPC shared memory 등을 가질 수 있습니다. 


5. User 네임스페이스

- User마다 User ID를 가지게 되는데, 컨테이너에서 생성한 User ID는 0번부터 고유 번호를 가지게 됩니다. 이 UID는 host에서는 다른 UID와 매핑됩니다. 



컨트롤 그룹(cgroup)


이렇게 독립적으로 컨테이너를 운영하게 되면 결국 호스트의 제한된 리소스를 나눠서 쓰는 모양새가 됩니다. 이 때 하나의 컨테이너가 CPU를 전부 다 잡아먹으면 다른 프로세스는 손해를 보게됩니다. 그래서 리눅스는 컨트롤 그룹을 통해서 각 컨테이너마다 사용할 수 있는 리소스의 양을 제한합니다.


| cgroup의 종류

1. CPU cgroup

- cgroup마다 사용자/시스템의 CPU 사용 시간, CPU 당 사용량 등을 추적합니다. CPU cgroup을 통해서 각 프로세스의 cpu 사용 자원을 제한할 수 있습니다.


2. cpuset cgroup

- 그룹에게 특정 CPU만 사용하도록 고정할 수 있습니다. 또한 특정 어플리케이션을 위해서 CPU를 reserve할 수도 있습니다. 


3. blkio cgroup

- 각 그룹의 I/O 사용량을 추적합니다. Block Device별로 구분하여 추적하며, Throttle을 설정할 수 있습니다. 


4. device cgroup

- 각 그룹별로 Device Node에 할 수 있는 작업을 관리합니다. 디바이스 읽기/쓰기/생성에 대한 권한 또한 제한할 수 있습니다. 


5. memory cgroup

- 각 그룹별로 사용하고 있는 메모리(페이지)를 추적합니다. 각 페이지는 하나의 그룹에서 카운팅되는데, 이는 공유 페이지의 경우에도 해당합니다. 예를 들어 Group A 와 B가 같은 Page를 사용하고 있다면, A의 메모리 사용량에만 카운팅되고 B에는 카운팅되지 않습니다. 이후에 A 그룹의 프로세스가 다 죽거나 더이상 해당 메모리를 사용하지 않으면, B그룹의 메모리 사용량으로 카운팅됩니다. 각 그룹은 메모리 사용량에 대한 제한을 가집니다.


위에서 간략히 설명한 cgroup말고도 다양한 cgroup 들이 존재합니다. 컨테이너라는 기술을 통해서 자원을 서로 격리할 수 있는 것은 결국 이 namespace와 cgroup 기능 덕분입니다. LXC, LXD 등과 같은 리눅스 컨테이너 또한 이 기능들을 활용하여 만들어졌으며, 컨테이너 플랫폼으로는 가장 유명한 도커 역시 비슷합니다. 이렇게 컨테이너가 어떤 원리를 통해서 만들어지는지 알아보았는데, 사실 컨테이너를 사용하면서 namespace와 cgroup을 찾아서 보는 사람은 별로 없을 것 같습니다.


대신, 컨테이너를 공부하는 사람들 대부분은 도커 컨테이너 기술에 관심을 가지고 있습니다. 이는 도커가 컨테이너 플랫폼 중에서는 가장 널리 쓰이고 있으며, docker hub라는 이미지 저장 공간을 통해서 손쉽게 컨테이너를 사용할 수 있도록 만들었기 때문입니다.


그러면 지금부터는 도커에 대해서 알아보도록 하겠습니다.


 


Docker가 유명한 이유


컨테이너 기술은 오래전부터 존재했지만, 도커가 나오기 이전에는 활발히 사용되지 않았습니다. 도커는 컨테이너를 여러 컴퓨터에 쉽게 이식할 수 있도록 해준 최초의 컨테이너 시스템입니다. 도커 이미지를 활용하여 어플리케이션, 라이브러리 등을 패키징하면 도커 agent가 돌아가는 환경이면 어디서나 같은 어플리케이션을 만들 수 있습니다. 이를 통해서 개발자들은 개발환경과 운영환경에서 동일한 조건의 어플리케이션을 올릴 수 있게 되었으며, 이식성이 좋아 서로 다른 OS에서도 동일하게 작동하기 때문에 OS 환경에 대한 구속성도 많이 해제되었습니다. 


도커 컨테이너에서 알아두어야할 개념은 아래와 같습니다.


1. 이미지 : 어플리케이션과 환경을 패키지로 묶어서 저장한 파일을 의미합니다. 이미지에는 파일시스템 뿐만 아니라 이미지를 실행하기 위한 실행파일 경로의 정보도 포함되어 있습니다. 이미지는 여러 레이어로 구성되어 있습니다. 따라서 같은 레이어의 이미지를 활용해서 서로 다른 이미지를 새로 만들 수 있는데, 이 때 같은 레이어인 경우에는 변경된 부분만 새로운 레이어로 만들어서 쌓는 구조입니다. 이러한 구조 덕분에 같은 이미지 레이어를 여러번 저장할 필요가 사라지고, 이미지 캐시를 적극적으로 활용할 수 있게 되었습니다.


2. 레지스트리 : 도커 이미지를 저장하는 저장소입니다. 대표적으로는 Docker Hub가 있습니다. Docker Hub에서는 다른 사람이 만든 공개 이미지가 저장되어 있어 손쉽게 이미지를 다운로드 받아서 쓸 수 있습니다. 공개하고 싶지 않은 이미지의 경우에는 private으로 설정하여 허가되지 않은 사용자의 접근을 제한할 수 있습니다.


3. 컨테이너 :  도커 agent가 설치된 환경에서 도커 이미지를 사용하여 생성한 리눅스 컨테이너입니다. 도커 컨테이너는 호스트의 프로세스 중 하나이고, 호스트의 다른 프로세스와 완전히 격리됩니다. 



간단한 개념들이라서 이해하기 어렵지 않을 것이라고 생각합니다. 여기서 하나 흥미로운 점은 같은 이미지를 사용하여 여러 컨테이너를 생성하더라도 각각 서로 다른 컨테이너라는 것입니다. 당연한 소리 같지만, 여기에는 이미지 레이어와 Copy On Write(CoW)에 대한 기술이 숨어 있습니다.


이미지 레이어와 Copy on Write


처음에 이미지를 통해서 컨테이너를 생성하면, 해당 컨테이너는 해당 이미지에 포함된 파일들을 읽을 수 있습니다. 서로 다른 컨테이너라고 할지라도 같은 이미지에서 생성되었다면 하나의 파일을 공유해서 읽는데, 이는 이미지 레이어가 읽기 전용이기 때문에 가능합니다. 동일한 이미지에서 생성된 컨테이너는 같은 이미지 레이어를 공유하지만, 각자 서로 다른 쓰기 가능 레이어를 가지게 됩니다. 즉, 각 컨테이너마다 실행된 수정사항들은 쓰기 가능 레이어에 쓰여집니다. 그런데 기본 이미지 레이어에 수정을 가하는 경우에는 수정을 하는 시점에서 복사본이 최상위 레이어에 생성되고 프로세스는 복사본에 작업을 진행합니다. 이것을 Copy On Write이라고 합니다. 

출처 : The Docker Eco System ( Written by Walid Ashraf )




마치며...


본 글에서는 컨테이너에 대해서 기본적인 내용들을 알아보았습니다. 도커 이미지를 다운로드 받아서 컨테이너를 생성하는 실습은 다른 블로그에도 많이 있기 때문에 생략하였습니다. [EKS 입문] 시리즈의 목표는 Amazon EKS를 이해하는 것입니다. 이 여정을 위한 첫 발걸음을 이제 내딛게 되었습니다. 다음 편에서는 쿠버네티스에 대해서 알아보도록 하겠습니다.



## 잘못된 내용은 피드백주시면 더 좋은 글로 보답하겠습니다.

작가의 이전글 네트워크 커널 튜닝

매거진 선택

키워드 선택 0 / 3 0

댓글여부

afliean
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari
브런치 시작하기

카카오계정으로 간편하게 가입하고
좋은 글과 작가를 만나보세요

카카오계정으로 시작하기
다른 SNS로 가입하셨나요?