본문 바로가기
Kubernetes/Kubernetes Advanced Networking Study

컨테이너 격리 & 도커 네트워크

by isn`t 2022. 1. 31.

1. fork & exec


fork : 메모리 공간을 새로 할당해서 별도로 자식 프로세스 생성

exec : 기존 메모리 공간을 그대로 사용하여 새로운 프로세스가 덮어씌움

리눅스에서는 명령어 실행시 기본적으로 fork방식

2. 데몬&데몬프로세스


데몬 : 지속적인 서비스 요청을 처리하기 위해 백그라운드에서 계속 실행되고 있는 프로세스

데몬 프로세스 : 데몬을 실행하기 위해 동작한 뒤 자기 자신을 죽이면서 데몬 프로세스를 고아로 만듦

이렇게 고아가 된 데몬은 자동으로 init 프로세스가 데려가서 init 프로세스 바로 아래에 위치함.

  • 데몬이 실행되는 두 가지 방법

standalone 방식 : 부팅 시에 실행되어 메모리에 상주하면서 들어오는 요청을 처리함. 웹서버, 메일서버 같이 빈번한 요청 처리가 필요한 데몬은 대부분 standalone 방식이며, 메모리에 올라와있기 때문에 ps로 확인 가능.

inetd 방식 : 클라이언트의 요청이 들어왔을 때만 동작하고, 동작 후에는 자동으로 종료됨. 메모리 관리가 용이함.

  • 데몬 관련 디렉토리

/etc/rc.d/init.d : 시스템에서 제공되는 서비스에 한해 시작, 중지시킬 수 있는 스크립트가 들어있음. service start 와 같은 명령을 실행하면 이 디렉토리의 데몬에서 제공하는 스크립트가 동작함.

  • /etc/rc.d/rc0.d~rc6.d

부팅 시 init.d의 스크립트는 init 프로세스가 데몬을 실행할 때 호출됨. 이 외에 런레벨에 따라 읽어들일 수 있는 프로세스가 위 디렉토리에 의해 나누어짐.

3. 컨테이너 vs 가상머신


컨테이너는 host OS 커널을 공유하며, 이를 약한 격리(weak isolation)라 한다.

가상머신 : 하이퍼바이저가 하드웨어를 에뮬레이션하고 그 위에 가상 OS를 만들어 실행하는 하드웨어 가상화 방식을 사용한다. 때문에 개별 VM은 오버헤드가 크고 동작이 무겁다. 하드웨어 레벨에서부터 가상화가 이루어지므로 고립성은 더 좋다.

컨테이너 : 하드웨어 에뮬레이션 없이 애플리케이션 실행에 필요한 파일들만 패키징하고, host의 커널을 공유해서 프로세스 형태로 컨테이너를 실행하므로 오버헤드가 적고 가볍다. 때문에 배포와 복제가 용이하다.

4. pivot-root, namespace, cgroup


컨테이너는 가상화된 공간을 생성하기 위해 리눅스 기능 pivot-root, namespace, cgroup을 사용한다.

4-1. pivot-root


mount1
이미지 출처 : https://zbvs.tistory.com/14

MNT ns를 clone하면, clone 이후 시점의 mount point는 복제되지 않지만 clone 시점까지의 mount point가 clone된 MNT ns로 똑같이 복제된다. 따라서 Host에 mount 되어있는 경로에 child MNT ns에서도 접근이 가능해진다. 이러한 상황에서 child MNT ns의 root(/) mount point를 pivot-root가 재설정한다.

mount2
이미지 출처 : https://zbvs.tistory.com/14

docker에서 bind mount를 지원하지만, 일단 컨테이너가 실행된 후에는 host와 container간 마운트는 MNT ns와 pivot-root의 특성에 의해 성립될 수 없다. host의 디렉토리와 container의 디렉토리를 아무리 마운트 해봤자 이는 parent MNT ns에서 마운트 한 것일 뿐 child MNT ns에서는 parent에 있는 mount point를 볼 수 없다.

따라서 child MNT ns를 clone하기 전에 parent에서 마운트할 경로를 생성하고 이를 bind mount한 후, pivot-root 처리하면 child MNT ns의 root가 격리되고 별도의 file system을 가진 것처럼 보이게 된다.

원래는 chroot를 이용했는데, chroot는 탈옥이 가능하다는 점 때문에 점차 pivot-root로 넘어감. chroot는 루트 디렉토리를 변경함.

*bind mount

host의 디렉토리를 마운트 시키는 기능

4-2. namespace


동일한 시스템에서 별개의 독립된 공간을 격리된 환경에서 운영하는 가상화 기술

namespace 종류

  • mount ns
  • network ns
  • pid ns
  • user(uid,gid 격리) ns
  • ipc(프로세스간 통신 격리) ns
  • uts(hostname 격리) ns

/var/run/netns

root@ubuntu-focal:~# tree /var/run/netns
/var/run/netns
├── BLUE
└── RED

0 directories, 2 files

root@ubuntu-focal:/var/run/netns# ls
BLUE  RED
root@ubuntu-focal:/var/run/netns# file BLUE
BLUE: empty

현재 netns 상태를 /var/run/netns 를 조회하여 확인할 수 있다. 실제로 확인해보면 netns 이름으로 된 빈 파일만 존재한다. 파일을 까보고 싶었는데 비어있어서 내용을 확인할 수는 없었다.


(그래도 궁금해서 찾아봄..)

/var/run/netns

netns가 생성/제거될 때 ip command는 생성된 netns에 대한 bind mount point를 /var/run/netns 경로에 생성함으로써, 이 네임스페이스에 어떤 프로세스가 돌고 있지 않다고 하더라도 네임스페이스가 사라진다거나 하지 않게 된다.

ip netns 명령이 동작할 때 참조하는 경로가 /var/run/netns가 되는데, 가령 network namespace를 listing 할 때, 이 경로의 파일을 읽어서 network namespace 목록을 보여준다.

4-3. cgroup


control group의 약자로 프로세스 단위로 다음의 리소스를 제어할 수 있다. 단일 프로세스 뿐 아니라 여러 프로세스를 그룹 단위로 묶어서도 제어가 가능하다.

  • CPU
  • MEM
  • Network
  • I/O
  • Device

이것이 linux container를 구성하기 위한 기본 요소이며, LXC는 하나의 컨테이너에 여러개의 애플리케이션을 띄울 수도 있는 구조임. 그러나 LXC를 모티브로 한 도커 컨테이너는 MSA를 지향하며 1container-1application 구조를 권장하고 있음.(쉬운 재사용, 유연성, 서로간의 간섭 제거)

또하나의 차이는 LXC는 캡쳐가 불가능하다는 것. docker는 이미지 기반으로 동작하기 때문에 초기 이미지에 세팅된 대로 언제든지 환경을 쉽게 복제 가능.

현재의 docker는 0.9버전 이후로 LXC 기반에서 벗어나서 libcontainer라는 자체 라이브러리를 사용함

*libcontainer : 리눅스 커널 기능인 가상화를 직접 사용할 수 있게 해주는 라이브러리. 이후에 이 프로젝트가 OCI로 넘어가면서 runc가 됨.

5. host와 container의 커널 버전이 달라도 컨테이너가 동작할 수 있는 이유


container는 host의 커널을 공유한다고 했다. 그런데 centos 위에 ubuntu 컨테이너가 올라가거나, windows에서 kali 컨테이너를 올릴 수 있는 이유는? 즉, 컨테이너와 호스트의 커널 버전이 달라도 동작 가능한 이유는?

  • linux kernel은 linux foundation에서 개발하고 있기 때문에 대부분의 리눅스간의 커널은 호환될 것으로 생각됨
  • CPU 어셈블리어 셋인 ISA는 CPU 아키텍처마다 다름. 그래서 OS 레벨에는 ISA와 연동되는 ABI가 있는데 이 ABI가 호환 가능하기 때문에 host와 container의 커널 버전이 달라도 올라갈 수 있음.
  • 제조사에 따라(레드햇 등) ABI는 직접 개발해서 넣는 경우도 있음
  • 따라서 애플리케이션이 별도 개발된 ABI를 사용하는 경우, 동작하지 않을 수 있음
  • 다만, 호스트 커널 버전이 낮고 컨테이너의 커널 버전이 높으면 정상동작하지 않을 것 같기도 함
  • ubuntu 등의 경우는 docker hub에서도 cpu 아키텍처별 이미지를 따로 제공하고 있음. host의 CPU 아키텍처가 다른 경우 컨테이너가 정상 동작하지 않음.

6. Docker network mode


6-1. None

외부 통신을 위한 네트워크를 사용하지 않음. lo 인터페이스만 존재함.

6-2. Bridged

가상의 L2 스위치인 bridge를 통과해야 하며, 컨테이너 외부로 나가기 위해서 NAT가 일어나야 함. 이 단계에서 iptables가 관여하며, PREROUTING, POSTROUTING의 NAT Chains 변경이 필요함. 이와 같은 부가 작업 때문에 오버헤드가 생기고 레이턴시가 있는 편임.

iptables
iptables에는 정책만 세우게 되고, 그 정책이 실제로 적용되어서 차단/허용이 수행되는 것은 kernel 레벨의 Netfilter 프레임워크에 의해 이루어진다. 이 Netfilter 프레임워크를 백엔드에 두고 사용 가능한 어플리케이션 중 하나가 iptables이다. 만약 kernel 레벨이 아닌 user레벨에서 이 동작이 이루어지면 훨씬 느려졌을 것.

6-3. Host Mode

포트포워딩만 발생하는 구조. 컨테이너가 80포트에서 httpd를 동작시키고 있다면 host ip의 80으로 request가 들어왔을 때 container에서 응답함. 당연하게도 컨테이너간 포트는 중복되면 안 됨.

6-4. MacVLAN

Bridged 모드와 유사하게 Macvlan bridge를 타게 되지만, Macvlan bridge는 통신에 관여하는 정도가 현저히 적음. 성능 효율적으로 외부 통신이 가능함. 컨테이너의

6-5. IPVLAN

Macvlan bridge과 마찬가지로 IPVLAN Mode 역시 통신에 관여하는 정도가 현저히 적고 성능 효율적으로 외부 통신이 가능함. k8s의 cni 대부분이 모두 이 모드를 기반으로 동작함.

7. macvlan vs ipvlan


간단히 요약만 하자면, macvlan은 host의 네트워크 인터페이스를 가상화시켜서 여러 개의 mac 주소를 만들고 컨테이너가 하나씩 가져다 쓰게 하는 방식.

ipvlan은 똑같은데 가상화 대상이 ip일 뿐.

macvlan의 경우 스위치에 최대 mac 주소 제한이 있는 경우 부적합함. DHCP와 같이 고유한 mac 주소를 요구하는 경우 사용. child interface간의 통신은 가능하나 parent-child간의 통신은 불가능함.

*macvlan 사용시 promiscuous mode를 켜줘야 함. parent interface의 시점에서 child interface의 mac은 자신의 mac이 아니기 때문에 출발지 mac이 자신의 mac이 아니더라도 내보낼 수 있도록 하는 설정임.

ipvlan의 경우 반대로 최대 mac 주소 제한이 있는 경우에 사용. 단, DHCP같이 고유한 mac주소를 요구하는 경우는 부적합함.

그래서 이게 어디에 쓰이냐.

multus

aws eks의 multus cni를 적용하면 기본 인터페이스는 aws vpc cni가, 두번째 인터페이스는 ipvlan을 이용한 multus cni가 구성된다. vpc cni로 갈 용도의 트래픽과, multus cni로 갈 용도의 트래픽을 구분할 수 있게 된다.

ipvlan과 macvlan의 자세한 동작방식은 아래 Reference에.

8. Reference


[1] Overlay FS in Linuxhttps://zbvs.tistory.com/14

[2] docker macvlanhttps://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=alice_k106&logNo=220984112963

[3] docker ipvlanhttps://this1.tistory.com/entry/Docker-container를-외부에서-직접-접근해보자

[4] https://ipwithease.com/macvlan-vs-ipvlan-understand-the-difference/ : macvlan vs ipvlan
[5] [network-namespaces] https://blogs.igalia.com/dpino/2016/04/10/network-namespaces/
[6] /var/run/netns
[7] iptables
[8] [docker network 방식] https://docs.docker.com/network/
[9] Oracle VM Virtual box network 방식 정리