Docker container with ip address on local network
- Magnus Therning
Shortly after I started my new job in August I put an instance of gitlab on a server and started promoting using it over the git-repos-on-an-NFS-share that had been used thus far. Thanks to the Docker image of Gitlab CE it only took about 5 minutes to have a running instance – setting up authentication via LDAP only took slightly longer. So far I’ve been running it on non-standard ports, just like the guide suggests, but I’ve gotten to the point where I’d like to make it a bit more official. That means I’d like to get it off non-standard ports, but I do really like running it in Docker. In short, I need to give the Docker image an ip address on the local network.
Unfortunately Docker doesn’t make it easy to do that, but I found an article on four ways to connect a docker container to a local network. What follows is my take on the instruction for how to use a macvlan
device to achieve it.
An image for testing
First I looked around for a simple echo server to run in my testing image:
module Main
where
import Control.Concurrent
import Control.Monad
import Network.Socket
import System.IO
srvPort :: PortNumber
= 2048
srvPort
main :: IO ()
= withSocketsDo $ do
main <- socket AF_INET Stream defaultProtocol
newSocket ReuseAddr 1
setSocketOption newSocket $ SockAddrInet srvPort iNADDR_ANY
bindSocket newSocket 2
listen newSocket
runServer echo newSocket
runServer :: (String -> String) -> Socket -> IO()
= forever $ do
runServer f s <- accept s
(usableSocket,_) $ interactWithSocket f usableSocket
forkIO
interactWithSocket :: (String -> String) -> Socket -> IO()
= do
interactWithSocket f s <- socketToHandle s ReadWriteMode
handle $ f <$> hGetLine handle >>= hPutStrLn handle
forever
echo :: String -> String
= ("R: " ++) echo
Run it and test it by pointing netcat to port 2048.
Once that was built I put together a Dockerfile that uses Debian Jessie as a base and copies in the echo server (I based it on another image):
FROM debian:8.2
MAINTAINER Magnus Therning <magnus@therning.org>
RUN TERM=vt220 apt-get update && \
TERM=vt220 DEBIAN_FRONTEND=noninteractive apt-get -y install \
apt-utils \
dialog \
libgmp10 \
&& true
RUN useradd -G users -m -s /bin/bash myuser && \
echo "root:root" | chpasswd
USER myuser
COPY echosrv /home/myuser/
CMD bash --login
I popped the Dockerfile and the binary into a folder srv/
so I can build the image using
docker build --rm --tag=nw srv $
and then started using
docker run --rm --interactive --tty --name=echo nw /home/myuser/echosrv $
A handy alias
The following alias will turn out to be very handy indeed
alias docker-pid="docker inspect --format '{{ .State.Pid }}'" $
The network setup
The local network is 192.168.1.0/24
and I found an unused address, 192.168.1.199
that I decided to use for my experiment. The gateway is on 192.168.1.1
.
First create the macvlan
device:
sudo ip link add mybridge link enp0s25 type macvlan mode bridge $
Then put it into the network namespace of the running container, and bring it up:
sudo ip link set netns $(docker-pid echo) mybridge
$ sudo nsenter -t $(docker-pid echo) -n ip link set mybridge up $
Now the device need its address and the routing has to be set up:
sudo nsenter -t $(docker-pid echo) -n ip route del default
$ sudo nsenter -t $(docker-pid echo) -n ip addr add 192.168.1.199/24 dev mybridge
$ sudo nsenter -t $(docker-pid echo) -n ip route add default via 192.168.1.1 dev mybridge $
That’s it, now the docker container is reachable on 192.168.1.199
. Well, it’s reachable on that ip from any computer on the network except for the host. In order for the host to be able to reach it add another route:
sudo ip route add 192.168.1.199 dev docker0 $
DHCP
If you’d rather have the container on an ip handed out by a DHCP server, then you replace the three commands for setting ip and route with:
sudo nsenter -t $(docker-pid echo) -n -- dhclient -d maclan $