도커 컨테이너 까보기 (5) – CNM(Container Networking Model)
이글은 Container Networking Model 에 대한 구현인 libnetwork 의 설계 사상에 대한 글을 번역한 것이다.
Docker 를 구성하는 요소 중에서 network component 를 어떤 목표를 가지고 설계했는지를 말해준다.
Docker network 에 대해 조금 더 깊이 있는 이해를 위해 꼭 필요한 내용이라 생각되어 번역해 보았다.
최대한 쉽고 자연스럽게 내가 이해한대로 의역해보려고 노력했지만 부족한 점이 있다.
추상적인 설계에 대한 내용이기 때문에 어떤 부분은 해당 글 만으로 그 정확한 의미를 파악하기 힘든 부분도 있다.
세세한 모든 부분을 이해하지 못하더라도 전반적인 흐름과 사상을 맛볼 수 있다면 만족스럽다.
이 글은 Docker Architecture 를 어느 정도 이해하고 있는 사람을 대상으로 한다.
docker 를 이용하여 system 을 설계하려는 사람에게 docker network 에 대한 이해는 필수라고 생각한다.
이 문서는 libnetwork 프로젝트의 비전과 목표를 달성하기 위해 libnetwork 가 어떻게 설계되었는지를 설명한다.
개별 릴리스에 대한 요구 사항은 프로젝트 페이지 에서 참조하시라.
목표
libnetwork 프로젝트는 Docker 와 Linux 의 철학을 따를 것이다.
이 철학은 작으면서 잘 모듈화되어 있어 조립(Composable)이 쉽고 독립적으로 잘 동작하는 tool 을 추구한다는 것이다.
Libnetwork 는 컨테이너의 네트워킹에 대해 이러한 Composable(모듈화되고 조립하기 쉬운) 요구를 충족시키는 것을 목표로 한다.
CNM(Container Network Model)
Libnetwork 는 CNM 을 구현한 것이다.
CNM 은 다음의 두 가지 역할을 할 수 있어야 한다.
- Container network 를 제공하는 데 필요한 단계(Step)를 정의
- 다수의 네트워크 드라이버를 지원하기 위해 필요한 추상화를 제공
이를 의역해보면, CNM 이라는 모델을 이용해서 우리는
- container network 를 구성하기 위한 과정(Step)을 명확하고 이해하기 쉽게 개념화, 형식화할 수 있고,
- 추상화된 layer 를 둠으로써 복수의 network driver 를 pluggable 하게 지원할 수 있게 된다. 즉, 필요에 따라 구현 layer 의 driver 들을 얼마든지 갈아끼울 수 있게 한다는 것이다. 사용자는 이러한 하위 driver 교체에 따른 변경이 불필요해야 한다.
CNM은 3 가지 주요 요소로 구성된다.
Sandbox
Sandbox 에는 container 의 네트워크 스택 구성을 포함한다.
네트워크 스택은 container network interface, routing table 및 DNS 설정 관리 등을 포함한다.
(역자주: 즉, container 내에 만들어지는 eth 나 routing table, DNS 설정 등은 모두 Sandbox 에 포함되는 요소들이다.)
Sandbox 는 Linux network namespace 또는 기타 이와 유사한 개념으로 구현될 수 있다.
(역자주: namespace 기술을 통해 ethernet interface 나 각종 network 설정 등이 격리될 수 있으니까.)
Sandbox 에는 다수의 Netwok 에 연결된 다수의 Endpoint 가 포함될 수 있다 .
(위 그림의 두 번째 Sandbox 참조)
Endpoint
Endpoint 는 Sandbox 를 Network 에 연결한다.
Endpoint 는 veth pair(eth-veth pair), Open vSwitch 내부 포트 또는 이와 유사한 것으로 구현될 수 있다.
Endpoint 는 하나의 Network 에만 속할 수 있으며 연결된 경우에는 하나의 Sandbox 에만 속할 수 있다.
Network
Network 는 서로 직접 통신 할 수 있는 Endpoint 그룹이다.
Network 는 Linux bridge, VLAN 등으로 구현될 수 있다.
(역자주: 위의 그림에서 docker0 bridge 가 Network 를 구현한 것에 해당된다.)
네트워크는 다수의 Endpoint 로 구성된다.
CNM 객체들
NetworkController
NetworkController 객체는 libnetwork 의 진입점(Entrypoint)을 제공하여, 사용자(예 : Docker Engine)가 네트워크를 할당하고 관리 할 수 있는 간단한 API를 제공한다.
libnetwork 는 여러 개의 활성 드라이버(Native 및 Remote)를 지원한다.
NetworkController 사용자가 특정 드라이버를 지정된 Network 에 바인딩 할 수 있다.
Driver
Driver 는 사용자가 볼 수 있는 객체는 아니지만 실제 네트워크 구현을 제공한다.
NetworkController 는 특정 Driver 에 대한 옵션/라벨을 설정할 수 있는 API 를 제공한다.
Driver 는 다양한 사용 사례 및 구축 시나리오를 만족시키기 위해 Native Driver(Bridge, Host, None & Overlay 등) 및 Remote Driver(플러그인 제공자로부터)가 모두 지원될 수 있다.
현재 시점에서는, Driver 는 하나의 Network 을 소유하고 IPAM 과 함께 해당 Network 만을 관리한다.
(각각의 docker network 은 하나의 network driver 에 의해서만 instance 화 될 수 있다. Bridge network, Host network 이렇게...)
이는 향후 다양한 네트워크 기능을 처리하는데 복수의 Driver 를 참여시킴으로써 개선될 수 있다.
Network
Network 객체는 (위에서 정의한대로) CNM : Network 의 구현이다.
NetworkController 는 Network 객체를 생성하고 관리하기 위한 API 를 제공한다.
Network 를 생성하거나 업데이트할 때마다 해당 Driver 가 이벤트를 알려 준다.
LibNetwork 는 Network 객체를 추상적인 수준에서 연결성과 분리를 다룬다.
추상적인 수준으로 동일한 네트워크에 속하는 Endpoint 그룹 간의 연결성(Connectivity)을 제공하거나, 나머지 Endpoint 그룹과의 분리(Isolation)를 제공한다.
Driver 는 필요한 연결성과 분리를 제공하는 실제 작업을 수행한다.
연결은 동일한 호스트 내에 있거나 여러 호스트에 걸쳐 있을 수 있다.
그러므로 Network 는 클러스터 내에 글로벌 범위를 가지고 있다.
Endpoint
Endpoint는 Service Endpoint 를 나타낸다.
그것은 네트워크 내의 특정 container 에 의해 노출되는 서비스들에 대해, 동일한 네트워크의 다른 container 들이 제공하는 다른 서비스들과의 연결을 제공한다.
Network 객체는 Endpoint 생성 및 관리를 위한 API를 제공한다.
Endpoint 는 하나의 Network 에만 연결할 수 있다.
Endpoint 생성 호출은 해당 Sandbox 에 대한 리소스 할당을 담당하는 Driver 에 수행된다.
즉, 특정 Sandbox 에 속하는 Endpoint 를 생성하는 일은 해당 Sandbox 의 담당 Driver 가 처리한다는 것이다.
Endpoint 는 하나의 서비스를 나타내는 것이지 특정 container 를 나타내는 것이 아니기 때문에 Endpoint 는 클러스터 내에서 글로벌 범위를 가진다.
Sandbox
Sandbox 객체는 IP 주소, MAC 주소, routes, DNS 항목과 같은 container 의 네트워크 구성을 나타낸다.
사용자가 Network 에 추가할 Endpoint 생성을 요청할 때 Sandbox 개체가 생성된다.
Network 를 처리하는 Driver 는 필요한 네트워크 리소스(예: IP 주소)를 할당하고 SandboxInfo라는 정보를 다시 libnetwork 에 전달하는 역할을 한다.
libnetwork 는 OS 특정 구조(예: 리눅스용 netns)를 사용하여 네트워크 구성을 Sandbox 로 대표되는 container 에 채운다.
Sandbox 는 여러 개의 Endpoint 를 서로 다른 Network 에 연결할 수 있다.
Sandbox 는 특정 호스트의 특정 container 와 연결되어 있기 때문에 컨테이너가 속한 호스트를 나타내는 로컬 범위를 가지고 있다.
CNM 속성(Attributes)
Options
Option 은 사용자의 특정 구성 옵션을 Driver 로 직접 전달하기 위한 일반적이고 유연한 메커니즘을 제공한다.
Option 은 string 으로 표현되는 Key 와 일반 객체(예: Go interface{})로 표현되는 Value 로 이루어진 Key-Value pair 데이터일 뿐이다.
Key 가 net-labels 패키지에 정의된 잘 알려진 Label 중 하나와 일치하는 경우에만, libnetwork 가 Option 상에서 작동한다.
Option 도 아래 설명된 바와 같이 Label 을 포함한다.
일반적으로 (UI에서) Option 은 최종 사용자가 볼 수 없지만 Label 은 볼 수 있다.
Labels
Label 은 Option 과 매우 유사하며 사실 Option 의 일부에 불과하다.
Label 은 일반적으로 최종 사용자가 볼 수 있으며 --labels 옵션을 사용하여 UI 에 명시적으로 표시된다.
Label 은 UI 에서 Driver 로 전달되므로 드라이버가 이를 이용하여 Driver 별 작업(network 에서 IP 주소를 할당하는 subnet 처럼)을 수행할 수 있다.
CNM lifecycle
CNM 사용자(Docker 같은)들은 CNM 객체 및 CNM API 를 통해 자신들이 관리하는 container 를 Network 에 연결한다.
- Driver 는 NetworkController 에 등록한다. 내장(Built-in) Driver 는 libnetwork 내부에 등록하고, Remote Driver 는 플러그인 메커니즘(plugin-mechanism is WIP)을 통해 libnetwork 에 등록한다. 각 Driver 는 특정 networkType 을 처리한다.
- NetworkController 객체는 libnetwork.New() API 를 통해 생성된다. 이는 Network 할당을 관리하고, (선택적으로는) Driver 를 Driver 별 Option 으로 설정하기도 한다.
- Network 는 이름과 networkType 을 parameter 로 제공하여 controller 의 NewNetwork() API 를 사용하여 생성된다. networkType parameter 는 관련 Driver 를 선택할 수 있도록 해주고, 생성된 Network 를 해당 Driver 에 바인딩하는 데 도움이 된다. 이 시점부터, 생성된 Network 에 대한 모든 작업은 그 Driver 에 의해 처리될 것이다.
- controller.NewNetwork() API는 Driver 별 Option 과 Label 을 전달하는 (선택적인) parameter 를 포함하며, Driver 는 이를 목적에 활용할 수 있다.
- network.CreateEndpoint() 를 호출하여 Network 에서 새 Endpoint를 만들 수 있다. 이 API 를 통해 Driver 가 사용할 수 있는 Option parameter 들을 부여할 수 있다. 이러한 'Option' 에는 잘 알려진(well-known) Label 과 Driver 고유 Label 이 포함된다. Driver 는 driver.CreateEndpoint 와 함께 차례로 호출되며, Network 에서 Endpoint 가 생성될 때 IPv4/IPv6 주소를 예약하도록 선택할 수 있다. Driver 는 driverapi 에 정의된 InterfaceInfo 인터페이스를 사용하여 이러한 주소를 할당한다. 기본적으로 서비스 Endpoint 는 네트워크 주소 및 (애플리케이션 컨테이너가 listen 하는) 포트 번호에 불과하기 때문에, Endpoint 가 표시하는 포트와 함께 서비스 정의로써 Endpoint 를 완성하려면 IP / IPv6 가 필요하다.
- container 를 Endpoint 에 attach 하는 데 endpoint.Join()을 사용할 수 있다. 해당 container 에 대해 Sandbox 가 아직 존재하지 않는 경우 Join 작업으로 Sandbox 가 생성된다. Driver 는 같은 container 에 attach 된 다수의 endpoint 들을 구별하기 위해 Sandbox Key 를 사용할 수 이다. 이 API 는 Driver 가 이용할 수 있는 options parameter 를 선택적으로 받아들일 수 있다.
ㅇ LibNetwork 에 대한 직접적인 설계 이슈는 아니지만, Docker 같은 사용자가 container 의 Start() lifecycle 동안 endpoint.Join() 함수를 호출하는 것은 아주 고무적이다. 이는 container 가 작동하기 전에 호출된다.
ㅇ endpoint Join() API 에 대해 자주 묻는 질문 중의 하나는, 왜 endpoint 를 생성하는 API 와 join 하는 API 가 각각 필요하냐는 것이다. 이에 대한 답은 Endpoint가 container 에 의해 지원될 수도 있고 그렇지 않을 수도 있는 service 를 나타낸다는 사실에 기초한다. Endpoint가 생성되면 해당 리소스가 먼저 예약되고, 향후 언제라도 container 가 Endpoint 에 연결될 수 있다는 것이다. 이러한 것에서 우리는 일관된 네트워킹 동작을 얻을 수 있다. - container 가 중지될 때 endpoint.Leave() 함수가 호출된다. Driver 는 Join() 호출 동안 할당된 상태들을 cleanup 할 수 있다. LibNetwork 은 참조하는 마지막 endpoint 가 network 를 leave 할 때 Sandbox 를 삭제할 것이다. 하지만, LibNetwork 는 endpoint 가 존재하는 한은 IP 주소들을 유지하고 container 가 다시 join 할 때 이를 재사용할 수 있다. 이는 container 가 시작(start)되고 중지(stop)될 때 리소스들이 재사용될 수 있다는 것을 말해준다.
- endpoint.Delete()는 network 으로부터 endpoint 를 삭제할 때 사용된다. 이는 endpoint 를 삭제할 뿐 아니라 caching 된 sandbox.Info 를 cleanup 하기도 한다.
- network.Delete() 는 network 을 삭제하기 위해 사용된다. LibNetwork 은 network 에 attach 되어 있는 endpoint 가 존재할 때는 network 를 삭제하는 것을 허용하지 않는다.
Implementation Details
Network & Endpoints
LibNetwork 의 Network 및 Endpoint API는 주로 해당 객체를 관리하고 이를 유지하여 CNM 에서 요구하는 수준의 추상화를 제공한다. CNM 에 약속된 기능을 실현하는 Driver 에 실제 구현을 위임한다. 자세한 내용은 Drivers 섹션을 참조하라.
Sandbox
Libnetwork 는 여러 운영 체제에서 Sandbox 를 구현하기 위한 프레임워크를 제공한다. 우리는 sandbox 패키지에 있는 namespace_linux.go 와 configure_linux.go 를 이용하여 Linux 용 Sandbox 를 구현했다. 각 sandbox 를 위한 Network Namespace 를 생성하고, 이는 host filesystem 의 path 로 구분된다. global namespace 로부터 Sandbox namespace 로 interface 를 이동(move)하는데 Netlink 호출이 이용된다. Netlink 는 namespace 안의 routing table 을 관리하는 데에도 사용된다.
Drivers
API
Driver 는 libnetwork 의 꼭 필요한 확장(extension)이고, 위에서 정의한 모든 LibNetwork API 들에 대한 실제적인 구현을 제공한다. 따라서, 모든 Network 와 Endpoint API 들에 대한 1 대 1 대응들이 존재한다. 이는 다음과 같다.
- driver.config
- driver.CreateNetwork
- driver.DeleteNetwork
- driver.CreateEndpoint
- driver.DeleteEndpoint
- driver.Join
- driver.Leave
이러한 Driver 측의 API 들은 User 용 API 에서 사용하는 name 대신에 유일한 id(networkid, endpointid, ...)를 사용한다. 이 API 들은 여전히 사용 중이고, Multi-host networking 환경에서의 driver 요구 사항을 기반으로 변경이 될 수도 있다.
Driver Semantics
- drive.CreateEndpoint
이 메소드에는 Interface 및 AddInterface 메소드와 함께 EndpointInfo 인터페이스가 전달된다.
Interface 에 의해 반환 된 값이 nil 이 아닌 경우, 드라이버는 그 안에 인터페이스 정보 (예 : 주소 또는 주소를 정적으로 제공된 것으로 취급)를 사용해야하며, 그렇지 않으면 오류를 반환해야 한다. 값이 nil 이면 드라이버는 정확히 하나의 새로운 인터페이스를 할당하고 AddInterface 를 사용하여 기록해야 한다. 실패하면 오류를 반환하라.
Interface가 0 이 아닌 경우 AddInterface 를 사용하는 것은 금지된다.
Implementations
libnetwork 은 다음의 driver 패키지를 포함한다.
- null
- bridge
- overlay
- remote
Null
null 드라이버는 드라이버 API 의 noop 구현으로, 네트워킹이 필요 없는 경우에만 사용된다. 이는 도커의 --net=none 옵션에 호환성을 제공하기 위한 것이다.
Bridge
Bridge Driver 는 Linux bridge 에 기반한 Linux 전용 브리징 구현을 제공한다. 자세한 내용은 Bridge Driver 설명서를 참조하라.
Overlay
Overlay Driver 는 VXLAN 과 같은 overlay network 캡슐화를 사용하여 여러 호스트에 걸쳐 네트워킹을 구현한다. 설계에 대한 자세한 내용은 overlay drive 설계를 참조하라.
Remote
Remote 패키지는 Driver 를 제공하지 않지만 원격 전송을 통해 드라이버를 지원할 수 있는 수단을 제공한다. 이렇게 하면 Driver 가 원하는 언어로 글을 쓸 수 있다. 자세한 내용은 remote driver 설계를 참조하라.
References
DOCKER NETWORK PART OF THE IMPLEMENTATION OF FLOW ANALYSIS (LIBNETWORK SOURCE CODE INTERPRETATION)
libnetwork design
확장성 있고, 이식성 있는 도커 컨테이너 네트워크 설계