home..

컨테이너 네트워크

mont-kim Docker Container LXC

컨테이너 네트워크 & IPTables

위에 기술했던대로 컨테이너는 네트워크 네임스페이스로 호슽트와 네트워크 격리환경을 구성합니다.

리눅스에서 방화벽 기능을 제공하는 IPTables는 호스트와 컨테이너의 통신에 관여를 합니다,

컨테이너 네트워크 네임스페이스간 통신

Untitled

top1.png

두개의 이미지에 보이는 호스트 내의 격리된 두개의 Namespace 간 통신을 테스트합니다.

# 터미널1~3 관리자
sudo su -
whoami

# veth (가상 이더넷 디바이스) 생성, man ip-link
ip link add veth0 type veth peer name veth1

# 네트워크 네임스페이스 생성, 확인
ip netns add RED
ip netns add BLUE

ip netns list

# veth0  RED 네트워크 네임스페이스로 옮김
ip link set veth0 netns RED
ip netns list

# veth1  BLUE 네트워크 네임스페이스로 옮김
ip link set veth1 netns BLUE
ip netns list

# veth0, veth1 상태 활성화(state UP)
ip netns exec RED ip link set veth0 up
ip netns exec BLUE ip link set veth1 up

ip netns exec RED ip -c a
ip netns exec BLUE ip -c a

# veth0, veth1  IP 설정
ip netns exec RED ip addr add 11.11.11.2/24 dev veth0
ip netns exec RED ip -c a
ip netns exec BLUE ip addr add 11.11.11.3/24 dev veth1
ip netns exec BLUE ip -c a

image.png


# 터미널1 (RED 11.11.11.2)
nsenter --net=/var/run/netns/RED

# 터미널3 (BLUE 11.11.11.3)
nsenter --net=/var/run/netns/BLUE

# ping 통신 확인 11.11.11.2 -> 11.11.11.3
tcpdump -i veth1

# 터미널1 (RED 11.11.11.2)
ping 11.11.11.3 -c 1

# 삭제
ip netns delete RED
ip netns delete BLUE

image.png

컨테이너 네트워크 Bridge 통신

images/KANS/week1/part3/Untitled1.png

# 네트워크 네임스페이스  veth 생성
ip netns add RED
ip link add reth0 type veth peer name reth1
ip link set reth0 netns RED
ip netns add BLUE
ip link add beth0 type veth peer name beth1
ip link set beth0 netns BLUE

# br0 브리지 생성
ip link add br0 type bridge

# reth1 beth1  br0 연결
ip link set reth1 master br0
ip link set beth1 master br0

nsenter --net=/var/run/netns/RED
## 현재 네트워크 네임스페이스 정보 확인
ip netns identify $$
RED

nsenter --net=/var/run/netns/BLUE
## 현재 네트워크 네임스페이스 정보 확인
ip netns identify $$
BLUE

# ping 통신 테스트
# 터미널1 (RED 11.11.11.2) >> ping WHY FAIL?
ping -c 1 11.11.11.3

실패원인 분석:

CleanShot 2024-08-25 at 17.30.31.png

iptables -t filter -S | grep ‘-P’ -P INPUT ACCEPT -P FORWARD DROP -P OUTPUT ACCEPT

에서 확인한 값으로는 Docker가 설치되면서 Default policy로 DROP 설정을 했기때문.

Untitled

Allow 설정후 Ping 가는것을 확인 할 수 있습니다.

 iptables -t filter -I DOCKER-USER -j ACCEPT

Host의 Bridge와 Blue 에서 모두 패킷이 수집됩니다.

컨테이너 → 호스트 & 외부(인터넷) 통신

호스트에 RED/BLUE와 통신 가능한 IP 설정 및 라우팅 추가, iptables NAT 를 통하여 통신 과정을 알아봅니다.

스크린샷 2021-10-14 오후 5.51.19.png

위에서 했던것과 동일한 설정을 다시 합니다.

ip netns add RED
ip link add reth0 netns RED type veth peer name reth1

ip netns add BLUE
ip link add beth0 netns BLUE type veth peer name beth1

ip link add br0 type bridge
ip link set reth1 master br0
ip link set beth1 master br0

ip netns exec RED ip addr add 11.11.11.2/24 dev reth0
ip netns exec BLUE ip addr add 11.11.11.3/24 dev beth0

ip netns exec RED ip link set reth0 up; ip link set reth1 up;
ip netns exec BLUE ip link set beth0 up;  ip link set beth1 up;
ip link set br0 up

# iptables -t filter -A FORWARD -s 11.11.11.0/24 -j ACCEPT
iptables -t filter -I DOCKER-USER -j ACCEPT

호스트에서 RED 나 BLUE 로 ping 통신 → RED 에서 외부로 통신

images/KANS/week1/part3/Untitled3.png

HOST→ RED

터미널2 (호스트) » br0 에 IP 추가

# 터미널1 (RED 11.11.11.2)
nsenter --net=/var/run/netns/RED

# 터미널2 (호스트) >> 호스트에서 RED  통신이 안되는 이유가 무엇일까요?
ping -c 1 11.11.11.2 # FAIL
# br0  IP 추가(라우팅 정보)
ip addr add 11.11.11.1/24 dev br0

# 터미널3 (호스트)
tcpdump -i br0 -n

RED → HOST


# 터미널1 (RED 11.11.11.2)
ip route add default via 11.11.11.1

# 터미널1 (RED 11.11.11.2)
ping -c 1 192.168.50.10

ping -c 1 8.8.8.8 ## FAIL

## POSTROUTING : 라우팅 Outbound or 포워딩 트래픽에 의해 트리거되는 netfilter hook
## POSTROUTING 에서는 SNAT(Source NAT) 설정
# 호스트 설정
iptables -t nat -A POSTROUTING -s 11.11.11.0/24 -j MASQUERADE

ping -c 1 8.8.8.8
# 터미널3 (BLUE 11.11.11.3)
nsenter --net=/var/run/netns/BLUE # 주의,  실행  아래 진행  
ip route add default via 11.11.11.1
ping -c 1 8.8.8.8
exit

# 삭제
ip netns delete RED
ip netns delete BLUE
ip link delete br0

iptables -t filter -D DOCKER-USER -j ACCEPT
iptables -t nat -D POSTROUTING -s 11.11.11.0/24 -j MASQUERADE

Docker Network

도커 네트워크모드

도커의 기본 네트워크모드로는 Bridge, Host, None

별도의 네트워크 플러그인으로는 macvlan, ipvlan, overlay 모드가 존재합니다.

docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
5d4ec64ed746   bridge    bridge    local
71da3212e9dc   host      host      local
90a304de3e67   none      null      local

docker info | grep Network
  Network: bridge host ipvlan macvlan null overlay
  • Host 는 호스트의 환경을 그대로 사용. 애플리케이션 별도 포트 포워딩 없이 바로 서비스 가능. 컨테이너의 호스트 이름도 호스트 머신의 이름과 동일.
  • None 는 말 그래도 아무런 네트워크를 쓰지 않는 것. 외부와의 연결이 단절됨. 컨테이너 내부에 lo 인터페이스만 존재.

[http://www.abusedbits.com/2016/09/docker-host-networking-modes.html](http://www.abusedbits.com/2016/09/docker-host-networking-modes.html)

http://www.abusedbits.com/2016/09/docker-host-networking-modes.html

Bridge

O'REILLY - Networking and Kubernetes 책

O’REILLY - Networking and Kubernetes 책

  • 도커에서 기본적으로 쓸 수 있는 네트워크 확인, 컨테이너 기본 생성 시 자동으로 docker0 브리지를 사용
  • 기본 172.17.0.0/16 대역을 컨테이너가 사용, 대역 변경 설정 가능
  • 도커는 IPtables 의 PREROUTING POSTROUTING 의 NAT Chains 를 변경한다
    • 컨테이너 → 외부 : POSTROUTING 의 SNAT 처리
    • 외부 → 컨테이너(Exposed services ports) : PREROUTING 에서 DNAT 처리
# 도커 네트워크 모드 확인
docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
a22960517f11        bridge              bridge              local
def31be9614b        host                host                local
f0bf6fd89f14        none                null                local

# 도커 네트워크(플러그인) 정보 확인
docker info
docker info | grep Network

# 도커 bridge 상세 정보 확인 = docker inspect --type network bridge 동일
# 아래 "Gateway": "172.17.0.1" 정보가 출력되지 않을 경우에는 systemctl restart docker 입력 후 다시 확인
docker network inspect bridge | jq
[
    {
        "Name": "bridge",
        "Id": "7c0902c8e4521b25b6a1233b018886256c8f877fed6a088a1a97e54804135613",
        "Created": "2021-10-14T12:22:52.869292205Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

# 브릿지 확인
brctl show
bridge name 	bridge id		       STP enabled	interfaces
docker0	    	8000.024229821c4f	 no

# 네트워크 인터페이스 확인
ip -c addr show docker0
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:29:82:1c:4f brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever

# SNAT 정책 확인
iptables -t nat -S
iptables -t nat -S | grep MASQUERADE
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

# 라우팅 확인
ip -c route | grep docker0
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown

여기서 생성되는 docker default bridge 대역이 172.17.0.1 이라는것을 주의하자

https://zerone-code.tistory.com/16

간혹가다 이런경우가 발생되기도 한다.

한줄요약 : Nginx Proxy Server를 구축했으나, 172.17 대역의 packet drop이 발생되어 확인결과 proxy server 구축시 설치한 docker 때문에 경로를 못찾던 현상.

하지만 이 주의사항과 동일하게, docker에서는 매번 모든 포트에 대한 맵핑을 수동으로 지어줘야하고, 추상화가 너무 덜 되어있어 불친절하다는 느낌을 강하게 받네요

Host 모드

호스트의 환경을 그대로 사용 가능. 호스트 드라이버의 네트워크는 별도로 생성할 필요 없이 기존의 host 라는 이름의 네트워크를 사용 - 링크 링크2

  • 컨테이너의 호스트 이름도 호스트 머신의 이름과 동일. 네트워크도 동일. 애플리케이션 별도 포트 포워딩 없이 바로 서비스 할 수 있음.
# 컨테이너 실행
docker run --rm -d --network host --name my_nginx nginx

# HostConfig.NetworkMode "host" , Config.ExposedPorts "80/tcp" , NetworkSettings.Networks "host" 확인
docker inspect my_nginx

# curl 접속 확인
curl -s localhost | grep -o '<title>.*</title>'

# 호스트에서 tcp 80 listen 확인
ss -tnlp
State            Recv-Q           Send-Q                     Local Address:Port                       Peer Address:Port           Process
LISTEN           0                511                              0.0.0.0:80                              0.0.0.0:*               users:(("nginx",pid=3694,fd=7),("nginx",pid=3693,fd=7),("nginx",pid=3649,fd=7))

# 추가 실행 시도
docker run -d network host --name my_nginx_2 nginx

# 확인
docker ps -a
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS                     PORTS     NAMES
369c40cf6dfd   nginx     "/docker-entrypoint.…"   7 seconds ago   Exited (1) 3 seconds ago             my_nginx_2
...

# 삭제
docker container stop my_nginx

Kubernetes에서도 모니터링 시스템에서의 node-exporter 같은경우들이 HOST 모드를 이용합니다.

  hostNetwork: true
  hostPID: true

이런 설정들을 이용해 한단계의 Hop을 줄인 Direct 접근을통해 경량화 네트워킹, 컴퓨팅 환경을 마운트하기도 합니다.

kube-prometheus-stack 여러쌍 구성시 host port중복으로 하나씩 수정했던 기억이 나는군요…

컨테이너 외부노출

설정 및 확인 : bridge mode - 링크

[https://youtu.be/SJFO2w5Q2HI](https://youtu.be/SJFO2w5Q2HI)

https://youtu.be/SJFO2w5Q2HI

# nginx:alpine 웹 컨테이너 3대 실행
# -p 옵션은 컨테이너 포트를 호스트 포트와 바인딩 연결 [호스트의 포트]:[컨테이너의 포트]
## -p 80 만 사용 시 호스트 포트 중 하나(랜덤)과 컨테이너의 80포트와 연결
## -p 여러개 사용하여 여러개 포트 개방
docker run -d --name=web1 -p 10001:80 --rm nginx:alpine
docker run -d --name=web2 -p 10002:80 --rm nginx:alpine
docker run -d --name=web3 -p 10003:80 --rm nginx:alpine

# 컨테이너 확인
docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED              STATUS              PORTS                                     NAMES
5747f508fe0b   nginx:alpine   "/docker-entrypoint.…"   About a minute ago   Up About a minute   0.0.0.0:10003->80/tcp, :::10003->80/tcp   web3
222390d43aa5   nginx:alpine   "/docker-entrypoint.…"   About a minute ago   Up About a minute   0.0.0.0:10002->80/tcp, :::10002->80/tcp   web2
b10d8e163c77   nginx:alpine   "/docker-entrypoint.…"   About a minute ago   Up About a minute   0.0.0.0:10001->80/tcp, :::10001->80/tcp   web1

# iptables 정보 확인
iptables -t nat -S | grep :80
	-A DOCKER ! -i docker0 -p tcp -m tcp --dport 10001 -j DNAT --to-destination 172.17.0.2:80
	-A DOCKER ! -i docker0 -p tcp -m tcp --dport 10002 -j DNAT --to-destination 172.17.0.3:80
	-A DOCKER ! -i docker0 -p tcp -m tcp --dport 10003 -j DNAT --to-destination 172.17.0.4:80

iptables -t filter -S
	-A FORWARD -o docker0 -j DOCKER
	-A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j ACCEPT
	-A DOCKER -d 172.17.0.3/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j ACCEPT
	-A DOCKER -d 172.17.0.4/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j ACCEPT
  ...

# 외부(자신의 PC)에서 접속(curl 혹은 웹브라우저)
curl -s 192.168.50.10:10001 | grep -o '<title>.*</title>'
curl -s 192.168.50.10:10002 | grep -o '<title>.*</title>'
curl -s 192.168.50.10:10003 | grep -o '<title>.*</title>'

# 연결 정보 확인
conntrack -L
iptables -t nat -L -n -v

# 삭제
docker stop $(docker ps -a -q)
© 2024 mont kim   •  Powered by Soopr   •  Theme  Moonwalk