도커 컨테이너 까보기 (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 가지 주요 요소로 구성된다.

그림출처: libnetwork design

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 에만 속할 수 있다.

그림출처: medium.com

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 에 연결한다.

  1. Driver 는 NetworkController 에 등록한다. 내장(Built-in) Driver 는 libnetwork 내부에 등록하고, Remote Driver 는 플러그인 메커니즘(plugin-mechanism is WIP)을 통해 libnetwork 에 등록한다. 각 Driver 는 특정 networkType 을 처리한다.
  2. NetworkController 객체는 libnetwork.New() API 를 통해 생성된다. 이는 Network 할당을 관리하고, (선택적으로는) Driver 를 Driver 별 Option 으로 설정하기도 한다.
  3. Network 는 이름과 networkType 을 parameter 로 제공하여 controller 의 NewNetwork() API 를 사용하여 생성된다. networkType parameter 는 관련 Driver 를 선택할 수 있도록 해주고, 생성된 Network 를 해당 Driver 에 바인딩하는 데 도움이 된다. 이 시점부터, 생성된 Network 에 대한 모든 작업은 그 Driver 에 의해 처리될 것이다.
  4. controller.NewNetwork() API는 Driver 별 Option 과 Label 을 전달하는 (선택적인) parameter 를 포함하며, Driver 는 이를 목적에 활용할 수 있다.
  5. 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 가 필요하다.
  6. 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 에 연결될 수 있다는 것이다. 이러한 것에서 우리는 일관된 네트워킹 동작을 얻을 수 있다.
  7. container 가 중지될 때 endpoint.Leave() 함수가 호출된다. Driver 는 Join() 호출 동안 할당된 상태들을 cleanup 할 수 있다. LibNetwork 은 참조하는 마지막 endpoint 가 network 를 leave 할 때 Sandbox 를 삭제할 것이다. 하지만, LibNetwork 는 endpoint 가 존재하는 한은 IP 주소들을 유지하고 container 가 다시 join 할 때 이를 재사용할 수 있다. 이는 container 가 시작(start)되고 중지(stop)될 때 리소스들이 재사용될 수 있다는 것을 말해준다.
  8. endpoint.Delete()는 network 으로부터 endpoint 를 삭제할 때 사용된다. 이는 endpoint 를 삭제할 뿐 아니라 caching 된 sandbox.Info 를 cleanup 하기도 한다.
  9. 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
확장성 있고, 이식성 있는 도커 컨테이너 네트워크 설계

You may also like...