***Ingress Network**는 Service의 Node 들간에 **Load Balancing**을 하는 Overlay Network 이다.
***Docker Swarm**의 모든 Node가 노출된 Port로 요청을 받게되면, 해당 요청을 IPVS라는 모듈로 전달한다.
* IPVS는 해당 Service에 참여하는 모든 IP 주소를 추적하고, 그 중 하나를 선택한 뒤 요청을 해당 경로로 Routing 한다.
* Igreess Network는 Docker Swarm을 Init하거나 Join할 때 **자동으로 생성**된다.
# host
**Host 모드**는 docker0 interface를 사용하지 않고 **Host OS의 네트워크 자원을 사용**한다. bridge 형식으로 사용하지 않기 때문에 **brctl show docker0** 명령으로 검색해도 interfaces에 나타나지 않으며, Container에서 Port를 노출하지 않는 경우 사용할 수가 없으며, `보안상 권장하지 않는 방법`이다.
# None
컨테이너가 LoopBack(127.0.0.1)을 제외한 어떠한 네트워크도 사용하지 않는다. 이는 격리가 필요할 경우 사용된다.
# Bridge
Docker에서 `기본적으로 설정되는 Network 방식`이다. 호스트에서 다음과 같이 **docker network inspect bridge** 명령을 통해 상세정보를 확인해본다.
* 이 IP는 DHCP를 통해 할당받는 것은 아니며, docker 내부 로직에 의해서 자동으로 할당받는 것이다.
* docker0는 일반적인 Interface가 아니며, Virtual Etherent Bridge이다.
**docker0**는 Container가 통신하기 위한 `가상 Linux Bridge`이다. Bridge는 기본적으로 Layer2 통신 기반이며, 만약 Container가 생성되면, 이 Bridge에 Container의 Interface가 하나씩 Binding되는 형태이다. 따라서 **Container가 외부로 통신할 때는 무조건 docker0 Interface를 거쳐야 한다.**
Container Network 방식은 `같은 호스트에 있는 컨테이너들 끼리의 통신`을 제공하는 방법이다. 컨테이너 간의 통신은 내부 도커 네트워크를 통해서 이루어지며, 통신은 호스트의 네트워크로 노출되지 않는다는것을 의미한다.
**--link *CONTAINER*:*ALIAS*** 로 설정하며, *CONTAINER* 는 링크 컨테이너(연결되는 컨테이너), *ALIAS*는 마스터 컨테이너(시작되는 컨테이너)의 내부에서 링크 컨테이너를 참조할 때 사용하는 로컬 이름을 설정 한다.
# Docker Container의 외부통신 구조
Docker host에 Container가 배포되면, **각 Container**에는 격리된 네트워크 환경(namespace)이 제공된다. **이 네트워크 환경은 오로지 각각의 Container 만을 위한 네트워크 환경**이다. 각 Container에는 통신을 위한 인터페이스도 새롭게 할당되며, mac 주소와 private IP도 부여받게 된다.
각 Container 들의 인터페이스는 자신들이 상주하고 있는 Docker host와 통신을 위해 Linux Bridge 방식으로 Binding 되어 있다. 따라서 같은 Docker host 내에 배포된 Container 들 사이에는 각자 할당받은 Private IP를 이용해서 자유롭게 통신이 가능하다.
## Container Port를 외부로 노출(expose)
Container를 생성하면 **기본적으로 외부와 통신이 불가능한 상태**이다. 따라서 외부와 통신을 위해서는 Container를 외부로 노출할 Port를 지정해야 한다. 노출할 Port를 지정하는 방법은 **Container를 생성할 때 -p 옵션을 사용**하면 된다.
```bash
# docker run -d -p 8000:80 --name hongsnet-nginx-1.16
```
만약 docker-compose를 사용할 경우에는 다음과 같이 기술한다.
```bash
version: '3'
services:
hongsnet-nginx-1.16:
image: hongsnet-nginx-1.16:latest
#restart: always
hostname: 'nginx116.hongsnet.net'
container_name: hongsnet-nginx-1.16
ports:
- "8000:80"
- "8443:443"
```
위 명령대로 실행하면, **외부에서 Docker 호스트의 8000 포트로 요청되면 컨테이너의 8000번 포트로 해당 요청을 forwarding 하겠다는 의미**이다.
아래와 같이 컨테이너 상태를 살펴보면, 8000 -> 80 포트로 forwarding 되어 있는 것을 볼 수 있다.
```bash
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
78f248e1817f hongsnet-nginx-1.16:latest "nginx -g 'daemon ..." 27 minutes ago Up 27 minutes 0.0.0.0:8000->80/tcp, 0.0.0.0:8443->443/tcp hongsnet-nginx-1.16
# netstat -lnp |grep 8000
tcp6 0 0 :::8000 :::* LISTEN 8182/docker-proxy-c
```
## docker-proxy
그런데 위 8000번 포트를 Listen 하는 프로세스는 **docker-proxy** 이다. 이 프로세스의 **목적**은 Docker **호스트로 들어온 요청을 컨테이너로 넘기는 것 뿐**이다. docker-proxy는 Kernel이 아닌, userland에서 수행되기 때문에 Kernel과 상관없이 host가 받은 패킷을 그대로 컨테이너의 port로 넘긴다.
컨테이너를 시작할 때 port를 외부로 노출하도록 설정하게 되면, Docker 호스트에는 **docker-proxy**라는 프로세스가 생성된다.
**docker-proxy 프로세스**는 컨테이너의 port를 노출하도록 설정한 수 만큼, 추가로 프로세스가 생성된다(run process per port). 만약 하나의 포트를 오픈하는 두 개의 컨테이너를 생성한다면, docker-proxy는 두 개가 생성된다. 또한 한 개의 컨테이너에 두 개의 포트에 대해서 외부로 노출하도록 설정한다면, 마찬가지로 docker-proxy 프로세스는 두 개가 생성된다.
하지만 실제로는 Docker host로 요청이 들어온 패킷이 컨테이너로 전달되는 것은 docker-proxy와는 무관하게 Docker **호스트**의 **iptables에 의해서 동작**된다. 즉, docker-proxy 프로세스를 kill 하더라도, 외부에서 들어오는 요청이 컨테이너로 전달되는데는 문제가 없다는 의미이다.
## iptables를 이용한 DNAT
먼저 Docker 호스트의 iptables를 확인해 보자.
```bash
# iptables -t nat -L -n
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
DOCKER all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
DOCKER all -- 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
위의 iptables 설정내역을 보면, 먼저 **Docker 호스트에 들어온 패킷이 PREROUTING chain을 통해 DOCKER Chain으로 전달**되고, **Docker chain에서는 DNAT로 2222번 포트로 들어온 요청을 172.19.0.2 IP를 가진, 컨테이너의 22번 포트로 포워딩**되는 것을 알 수 있다.
반대로 **컨테이너의 외부로 나갈때**는 POSTROUTING chain을 거쳐, **MASQUERADE 되어 외부로 나간다.**