Docker - współdzielenie sieci
Mało znanym wariantem w Dockerze jest współdzielenie przestrzeni nazw interfejsów sieciowych. Mówiąc nieco prościej - sytuacja w której 2 różne kontenery od strony użytkownika używają tego samego “wirtualnego” interfejsu sieciowego.
To, że nie jest to wariant dobrze znany i opisany w sumie nie powinno dziwić - to, że możemy związać dowolny port w kontenerze z jakimś portem hosta w większości wypadków jest mechanizmem wystarczającym.
Do czego to w takim razie użyć? Pracując z sieciami wykorzystującymi Tailscale lub Netbird-a trafiłem jednak na sytuację w której było dla mnie niezbędne żeby uruchomiona w kontenerze usługa korzystała z tego samego interfejsu co Tailscale / Netbird (a tym samym była dostępne w ramach Tailscale / Netbirda), a korzystanie z sieci hosta (network_mode: host
- co jest dośc popularnym rozwiązaniem) nie wchodziło w grę gdyż wymagane przez usługę porty były już zajęte.
Macvlan wraz z dodatkowymi opcjami, które daje network_mode
uratowały sytuację - można w nich podać nazwę konkretnej usługi / kontenera z której interfejsu chcemy skorzystać. Wystarczy postąpić bardzo podobnie jak w przypadku trybu host
, tyle że wpisując tam skąd “pożyczamy” interfejs sieciowy np.
network_mode: service:<NAZWA_USŁUGI>
Działa to bez problemu z plikami compose - co można zobaczyć np. na poniższym przykładzie (w którym darowałem już sobie macvlan):
networks:
shared-net:
services:
nginx1:
container_name: nginx1
image: nginx:latest
environment:
- NGINX_PORT=6080
- SERVER_NAME=nginx1
networks:
- shared-net
volumes:
- ./nginx-conf.template:/etc/nginx/conf.d/nginx-conf.template
command: /bin/bash -c "envsubst < /etc/nginx/conf.d/nginx-conf.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"
nginx2:
container_name: nginx2
image: nginx:latest
environment:
- NGINX_PORT=7080
- SERVER_NAME=nginx2
network_mode: service:nginx1
volumes:
- ./nginx-conf.template:/etc/nginx/conf.d/nginx-conf.template
command: /bin/bash -c "envsubst < /etc/nginx/conf.d/nginx-conf.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"
Kontener nginx2
korzysta w tym wypadku z interfejsu sieciowego usługi nginx1
.
Chcąc uruchomić powyższy przykład i przetestować jak to działa należy stworzyć plik nginx-conf.template
o np. takiej zawartości:
server {
listen ${NGINX_PORT};
listen [::]:${NGINX_PORT};
server_name ${SERVER_NAME};
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
Po uruchomieniu poprzez docker compose up -d
można otworzyć bash-a w jednym z kontenerów poprzez:
docker container exec -it nginx2 bash
Do sprawdzenia czy w ramach localhost
mamy dostępnego nginx zarówno na porcie 6080 (z kontenera nginx1) jak i 7080 (z kontenera nginx2) można skorzystać z curl-a:
curl http://localhost:6080
curl http://localhost:7080
Znacznie lepszą informację da jednak użycie docker container inspect
z poziomu hosta:
docker container inspect nginx2
Warto zwrócić uwagę na sekcję “NetworkMode” i “NetworkSettings”. W moim wypadku w pierwszej było widać:
"NetworkMode": "container:cd15bb65e8af98ab0a383269277b7e3710b3d85189869742202884cce4f46f66"
Z kolei ta druga zawiała puste wpisy - co tym lepiej pokazuje, że faktycznie kontener nginx2 nie ma “własnego” interfejsu sieciowego.
Odnośniki
https://docs.docker.com/reference/compose-file/services/
https://docs.docker.com/reference/compose-file/services/#network_mode