출처 : https://tech.kakaoenterprise.com/154 [카카오엔터프라이즈 기술블로그 Tech&(테크앤):티스토리]
[컨테이너 인터널 #1] 컨테이너 톺아보기
시작하며 안녕하세요. 카카오엔터프라이즈에서 검색 서비스를 개발하고 있는 검색클라우드기술파트의 Sam(김삼영)입니다. 지난 글 서비스 개발자를 위한 컨테이너 뽀개기 (a.k.a 컨테이너 인터널
tech.kakaoenterprise.com
(전체적인 글 및 실습은 위 주소를 많이 참고 하였으며 더 자세한 글은 위 주소에서 직접 읽어보시길 강력히 추천합니다!)
chroot로 컨테이너를 구현을 하면 잘 되는 것 같지만 몇 가지 이슈들이 존재합니다.
1. 탈옥이 가능하다. chroot의 목적은 프로세스를 특정 디렉터리에 가두기 위한 것인데, 탈옥이 가능하다는 한계가 있어 실제 컨테이너에서는 쓰이지 않습니다.
2. 격리되지 않는다. 호스트 상의 다른 프로세스들이 루트 권한만 있으면 chroot 경로에 접근할 수 있고, chroot한 프로세스도 “ps”, “mount” 등 시스템 바이너리만 복사하면 다른 프로세스 및 시스템 정보들을 볼 수 있습니다. (“보인다"는 것은 곧 격리되지 않음을 의미)
3. 루트 권한 제어가 필요하다. chroot 명령을 쓰려면 루트 권한이 필요하고 , chroot로 실행하는 프로세스도 루트 권한을 가지게 됩니다. 보안 측면에서 컨테이너를 비롯한 어떤 프로세스든 루트 권한을 가지게 하는 것은 매우 위험합니다.
4. 리소스 제한이 되지 않는다. chroot로 실행하더라도 호스트의 리소스를 제한 없이 사용할 수 있어 CPU나 Memory, IO, 네트워크 등의 사용량을 제한하지 못합니다.
5. 중복문제가 있다. 서로 다른 이미지라 하더라도 시스템 바이너리, 웹서버, DB, 그 밖의 관련 라이브러리 등 자주 사용되는 파일들이 중복될 가능성이 높습니다.
탈옥 문제 해결 - pivot_root
pivot_root는 먼저 간단히 얘기하자면 루트 파일시스템 자체를 바꿔버립니다. 컨테이너가 애초에 안전하게 호스트와 다른 별도의 루트 파일시스템을 가지려면 "마운트 네임 스페이스"(특정 프로세스에 대해 마운트를 호스트와 격리하는 것)가 필요합니다.
정리를 하는데 도저히 이해가 가지 않아서 chatgpt에게 쉽게 비유해달라고 해서 나온 말은 다음과 같습니다.
컨테이너와 호스트를 두 개의 방으로 생각해볼 수 있어요.
- 호스트는 우리가 원래 살고 있던 큰 방이라고 가정해봅시다. 이 방에는 여러 물건들이 있는데, 이 물건들은 모두가 공용으로 사용할 수 있죠.
- 이제, 컨테이너는 우리가 새로 만든 작은 방입니다. 하지만 이 작은 방은 특별해요. 우리는 이 방 안에서 새로운 가구와 물건들을 둘 수 있지만, **큰 방(호스트)**에 있는 사람들은 이 방에서 어떤 일이 일어나는지 전혀 알 수 없어요.
마운트 네임스페이스 비유:
- 마운트 네임스페이스는 작은 방의 문이 잠겨있고, 방 안에서 무슨 일이 일어나는지 바깥에서는 볼 수 없도록 만드는 것과 비슷합니다.
- 방 안에서 물건을 바꾸거나 추가해도, 큰 방에서는 그걸 볼 수 없습니다. 작은 방 안에서만 그 변화가 적용됩니다.
pivot_root 비유:
- pivot_root는 방의 바닥을 바꾸는 것과 같아요. 예를 들어, 작은 방의 바닥을 바꿔도 큰 방의 바닥은 그대로입니다.
- 즉, 컨테이너(작은 방) 안에서는 바닥을 새로 깔고 그 위에서 새로운 환경을 만들지만, 큰 방(호스트)에서는 아무런 변화가 없는 거죠.
결과적으로, 작은 방(컨테이너)은 전혀 독립적인 공간으로, 그 안에서 일어나는 변화는 큰 방(호스트)에 영향을 미치지 않게 됩니다.
pivot_root 를 사용하면 chroot의 탈옥 문제를 해결할 수 있고, 컨테이너는 전용 루트파일시스템을 가지게 됩니다. 컨테이너가 전용 루트 파일시스템을 가짐으로써 “이미지"에 꾸려진 파일, 설정 등 살림살이도 풀어놓고 자유롭게 파일시스템도 마운트할 수 있게 됩니다.
2,3 의 격리 안됨 및 루트 권한의 문제
chroot는 프로세스의 격리가 전혀 되지 않아, 프로세스 간에 서로 영향을 주고받을 수 있다는 문제가 있습니다.
이 문제를 해결하기 위해 2002년 리눅스 2.4.19 버전에 kernel feature로 처음 포함된 네임스페이스(위키)는 프로세스에 격리된 환경과 리소스를 제공해 이러한 문제를 해결할 수 있게 되었습니다. 전용 파일시스템 마운트, uts(명패), ipc(직통전화), pid(족보), 전용 네트워크 구성, cgroups(계량기), user(루트 권한) 등 다양한 네임스페이스로 문제를 해결합니다.
1. 마운트 네임스페이스 (CLONE_NEWNS)
- 문제 해결: 파일시스템의 마운트 포인트를 프로세스 간에 격리.
- 예시: 컨테이너가 독립된 파일시스템을 사용하여, 다른 컨테이너나 호스트의 파일시스템 변경이 영향을 미치지 않도록 합니다.
2. UTS 네임스페이스 (CLONE_NEWUTS)
- 문제 해결: 호스트 이름과 도메인 이름을 격리.
- 예시: 각 컨테이너가 자신의 호스트 이름을 설정할 수 있도록 하여, 컨테이너별로 독립적인 호스트 환경을 만듭니다.
3. IPC 네임스페이스 (CLONE_NEWIPC)
- 문제 해결: 프로세스 간 통신(IPC) 자원(메시지 큐, 세마포어, 공유 메모리 등)을 격리.
- 예시: 컨테이너 내의 프로세스가 사용하는 IPC 자원이 다른 컨테이너나 호스트와 충돌하지 않도록 합니다.
4. PID 네임스페이스 (CLONE_NEWPID)
- 문제 해결: 프로세스 ID를 격리.
- 예시: 각 컨테이너가 자신의 프로세스 트리를 가지며, 다른 컨테이너의 프로세스를 볼 수 없게 합니다. 이를 통해 프로세스가 컨테이너 내부에 갇혀 있게 합니다.
5. cgroup 네임스페이스 (CLONE_NEWCGROUP)
- 문제 해결: 컨테이너별로 시스템 자원(CPU, 메모리 등)을 제어하고 격리.
- 예시: 각 컨테이너가 독립적으로 시스템 자원을 관리하고, 한 컨테이너가 과도하게 자원을 사용해도 다른 컨테이너에 영향을 주지 않도록 합니다.
6. 네트워크 네임스페이스 (CLONE_NEWNET)
- 문제 해결: 네트워크 자원을 격리.
- 예시: 컨테이너가 자신의 네트워크 인터페이스, 라우팅 테이블, 포트 등을 가지고, 네트워크 설정이 다른 컨테이너나 호스트에 영향을 미치지 않게 합니다.
7. USER 네임스페이스 (CLONE_NEWUSER)
- 문제 해결: 사용자와 그룹 ID를 격리.
- 예시: 컨테이너 내부에서는 루트 권한을 가지지만, 호스트에서는 일반 사용자 권한만 가지도록 설정해 보안성을 강화합니다.
리소스 무제한 문제?
chroot은 컨테이너가 사용하는 호스트 리소스를 제한할 방법이 없습니다.
하지만 Cgroups을 이용하면 CPU, Memory 등의 리소스 사용을 제한할 수 있습니다.
cgroup 네임스페이스가 cgroup 파일시스템 격리를 통해 컨테이너에서 함부로 호스트나 다른 컨테이너의 cgroup을 건드리지 못하도록 하였다면, Cgroups(Control Groups)는 cgroup 파일시스템을 통해서 호스트의 하드웨어 자원을 "그룹"별로 관리하는 모듈입니다.
Cgroups(Control Groups)는 컨테이너나 프로세스가 사용하는 **시스템 리소스(CPU, 메모리, 네트워크, 디스크 I/O 등)**를 제어하고 제한하는 중요한 메커니즘입니다. 이를 비유해서 설명하자면, 마치 각 세대에 전기, 수도, 가스 등의 자원을 공급하고 제한하는 방식과 비슷합니다.
쉽게 비유하자면,
- 세대(그룹): 각 세대는 하나의 컨테이너나 프로세스 그룹을 의미합니다.
- 전기, 수도, 가스(리소스): 각 세대에 할당된 CPU, 메모리, 네트워크, 디스크 등의 시스템 자원을 비유합니다.
- 공급량 제한(Cgroup 제어): 세대별로 전기나 가스 사용량에 제한을 두듯이, Cgroups는 각 프로세스 그룹이 사용할 수 있는 리소스를 제어합니다. 이를 통해 한 세대가 자원을 과도하게 사용해도 다른 세대에는 영향을 미치지 않게 합니다.
주요 리소스와 Cgroups 관리 방식:
- CPU: 각 프로세스가 사용할 수 있는 CPU의 양을 제한하여 특정 그룹이 CPU를 독점하지 못하게 합니다.
- 메모리: 프로세스가 사용할 수 있는 메모리의 양을 제한해, 메모리 과다 사용으로 인해 시스템이 다운되지 않도록 방지합니다.
- 네트워크: 네트워크 대역폭을 그룹별로 관리하여 한 그룹이 네트워크를 과도하게 사용해도 다른 그룹에 영향을 주지 않게 합니다.
- 디스크 I/O: 디스크 사용량을 제한하여 한 그룹이 디스크에 과부하를 걸어 시스템 성능에 영향을 주지 않도록 합니다.
Cgroups는 각 자원을 관리하는데 매우 유용하며, 이를 통해 호스트 자원을 효율적으로 분배하고 프로세스 간의 리소스 경쟁을 방지할 수 있습니다. Cgroups 네임스페이스와 결합하면, 한 컨테이너가 다른 컨테이너나 호스트의 자원에 접근하지 못하게 격리하는 역할도 합니다.
앞선 글에서 지금까지 해온 과정들이 모두 도커가 만들어준 컨테이너를 만드는 과정이라고 생각하면 될 것 같다. 직접 실습 및 공부를 해보면서 느낀 것은 컨테이너가 그냥 뚝딱 만들어지는 것이 아닌 여러 과정 및 고려사항들이 복잡하게 있고 그것을 편리하게 해주는 도커를 보면서 왜 도커를 써야하는가에 대한 질문의 답을 어느정도 해소 시켜준 것 같다.
다음 글 또한 카카오엔터프라이즈에 올라온 컨테이너 파일시스템에 대해서 정독하고 자세히 알아보는 시간을 갖겠습니다!