Skip to content

Instantly share code, notes, and snippets.

@nerdalert
Last active March 14, 2017 07:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nerdalert/055f0562c90bd0796172 to your computer and use it in GitHub Desktop.
Save nerdalert/055f0562c90bd0796172 to your computer and use it in GitHub Desktop.
Libnetwork Feature Parity

Libnetwork Feature Parity Testing/Tutorial with Existing Docker Networking

The following parameters are global options that are set via $DOCKER_OPTS variable typically in the file /etc/default/docker. Once you start the Docker daemon, if you want to modify any of those parameters it requires a restart of the daemon:

-b BRIDGE or --bridge=BRIDGE 
--bip=CIDR
--fixed-cidr 
--fixed-cidr-v6
-H SOCKET... or --host=SOCKET...
--icc=true|false 
--ip=IP_ADDRESS 
--ipv6=true|false
--ip-forward=true|false
--iptables=true|false 
--mtu=BYTES

There are two networking options that can be supplied either at startup or when docker run is executed. If set in the Docker configuration /etc/default/docker it is now a global option rather then a per-container configuration.

--dns=IP_ADDRESS... 
--dns-search=DOMAIN... 

Lastly the following are per-container configurations only when docker run is invoked.

-h HOSTNAME or --hostname=HOSTNAME
--link=CONTAINER_NAME_or_ID:ALIAS 
--net=bridge|none|container:NAME_or_ID|host 
--mac-address=MACADDRESS...
-p SPEC or --publish=SPEC
-P or --publish-all=true|false 
-b BRIDGE , --bridge = BRIDGE
--bip = CIDR

General Configuration of the Daemon

Note: If your Docker Host OS is using SysV init rather then Systemd replace the systemctl commands with service. More on that holy war here

systemctl start docker
# Failed to start docker.service: Unit docker.service is masked.

systemctl unmask docker
# Removed symlink /etc/systemd/system/docker.service.

systemctl unmask docker
# Removed symlink /etc/systemd/system/docker.service.
systemctl start docker
# root@deb-143:/home/brent# ps -eaf | grep dock
# root      5176     1  0 00:40 ?        00:00:00 /usr/bin/docker -d -p /var/run/docker.pid

Add a custom docker network bridge (--bridge=)

systemctl stop docker
ip link set dev docker0 down
brctl delbr docker0
iptables -t nat -F POSTROUTING

At this point you don't have the docker0 default bridge anymore

brctl show
# bridge name	bridge id		STP enabled	interfaces

Next setup a custom network bridge:

# Create our own bridge

brctl addbr bridge0
ip addr add 192.168.5.1/24 dev bridge0
ip link set dev bridge0 up

# View the Layer 2 portion of the new bridge

brctl show

# (output)
# bridge name	bridge id		STP enabled	interfaces
# bridge0		8000.000000000000	no

# Confirming that our bridge is up and running

ip addr show bridge0

# (output)
# 32: bridge0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
#    link/ether 0a:6d:18:0c:e9:00 brd ff:ff:ff:ff:ff:ff
#    inet 192.168.5.1/24 scope global bridge0
#       valid_lft forever preferred_lft forever
#    inet6 fe80::86d:18ff:fe0c:e900/64 scope link
#       valid_lft forever preferred_lft forever

# Tell Docker about it and restart (on Ubuntu)
#
# This adds lines to the file /etc/default/docker that
# makes the configuration persistent in between reboots.
# e.g. 'copy run start' || 'write mem' for netops folks
# 
# Once you changed the file, start the docker service 
# back up using the systemd client command (systemctl)

echo 'DOCKER_OPTS="-b=bridge0"' >> /etc/default/docker

# Start the  docker daemon back up
systemctl start docker

# Confirming new outgoing NAT masquerade is set up

iptables -t nat -L -n

# ...
# Chain POSTROUTING (policy ACCEPT)
# target     prot opt source               destination
# MASQUERADE  all  --  192.168.5.0/24      0.0.0.0/0

Notice now when you look at the docker proc. It references the bridge OPT modification:

ps -eaf | grep dock
# root       1201      1  0 02:10 ?        00:00:00 /usr/bin/docker -d -H fd:// -b=bridge0

Next lets start a host:

# In the container, get the output of the 'ip address' command and look for the eth0 address.

ip addr
#1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
#    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
#    inet 127.0.0.1/8 scope host lo
#       valid_lft forever preferred_lft forever
#    inet6 ::1/128 scope host
#       valid_lft forever preferred_lft forever
#34: eth0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
#    link/ether 02:42:0a:06:d4:2a brd ff:ff:ff:ff:ff:ff
#    inet 172.17.0.1/16 scope global eth0
#       valid_lft forever preferred_lft forever
#    inet6 fe80::42:aff:fe06:d42a/64 scope link
#       valid_lft forever preferred_lft forever

# or you can save some output and hotkey this:
ip -4 -o addr show dev eth0 |awk '{split($4,a,"/") ;print a[1]}'

# (output)
# 172.17.0.1

Lets run a container now. As pointed out earlier, the default bridge to use is specified in the configuration file that the docker daemon reads DOCKER_OPTS from, not passed from the docker run command.

For example, run docker run -it --rm ubuntu echo hello worlds will execute echo hello worlds in the new container and exit deleting the container in the process. Note, the --rm parameter which means the container will be removed after the process it is asked to run completes. In this case it was the echo command (echo hello worlds).

docker ps
# CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS               NAMES
# 7ac0d1c11dbe        ubuntu              "/bin/bash"         About a minute ago   Up About a minute                       focused_bardeen

docker inspect 7ac0d1c11dbe

#[
#{
#    "Id": "7ac0d1c11dbe8a98ec1f596bb9bde7799768fd18685a121f2263f07c48b6d669",
#    "Created": "2015-05-16T22:43:59.779306509Z",
#    "Path": "/bin/bash",
#    "Args": [],
#    "Config": {
#        "Hostname": "7ac0d1c11dbe",
#        "Domainname": "",
#        "User": "",
#        "AttachStdin": true,
#        "AttachStdout": true,
#        "AttachStderr": true,
#        "PortSpecs": null,
#        "ExposedPorts": null,
#        "Tty": true,
#        "OpenStdin": true,
#        "StdinOnce": true,
#        "Env": null,
#        "Cmd": [
#            "/bin/bash"
#        ],
#        "Image": "ubuntu",
#        "Volumes": null,
#        "WorkingDir": "",
#        "Entrypoint": null,
#        "NetworkDisabled": false,
#        "MacAddress": "",
#        "OnBuild": null,
#        "Labels": {}
#    },
#    "State": {
#        "Running": true,
#        "Paused": false,
#        "Restarting": false,
#        "OOMKilled": false,
#        "Dead": false,
#        "Pid": 10441,
#        "ExitCode": 0,
#        "Error": "",
#        "StartedAt": "2015-05-16T22:44:00.203181126Z",
#        "FinishedAt": "0001-01-01T00:00:00Z"
#    },
#    "Image": "07f8e8c5e66084bef8f848877857537ffe1c47edd01a93af27e7161672ad0e95",
#    "NetworkSettings": {
#        "Bridge": "bridge0",
#        "Gateway": "192.168.5.1",
#        "GlobalIPv6Address": "",
#        "GlobalIPv6PrefixLen": 0,
#        "IPAddress": "192.168.5.2",
#        "IPPrefixLen": 24,
#        "IPv6Gateway": "",
#        "LinkLocalIPv6Address": "",
#        "LinkLocalIPv6PrefixLen": 0,
#        "MacAddress": "02:42:4f:2f:45:57",
#        "PortMapping": null,
#        "Ports": {},
#        "SecondaryIPAddresses": null,
#        "SecondaryIPv6Addresses": null,
#        "SandboxKey": "/var/run/netns/7ac0d1c11dbe",
#        "NetworkID": "30b2ea4e443226a426b1618ceb954c30dcae9f0df88ba5da942033b0678a8793",
#        "EndpointID": "384038aa3baed05d2da4f1f1e2eea68b3b4d3d753604e193826a2271bd86c636",
#        "HairpinMode": false
#    },
#    "ResolvConfPath": "/var/lib/docker/containers/7ac0d1c11dbe8a98ec1f596bb9bde7799768fd18685a121f2263f07c48b6d669/resolv.conf",
#    "HostnamePath": "/var/lib/docker/containers/7ac0d1c11dbe8a98ec1f596bb9bde7799768fd18685a121f2263f07c48b6d669/hostname",
#    "HostsPath": "/var/lib/docker/containers/7ac0d1c11dbe8a98ec1f596bb9bde7799768fd18685a121f2263f07c48b6d669/hosts",
#    "LogPath": "/var/lib/docker/containers/7ac0d1c11dbe8a98ec1f596bb9bde7799768fd18685a121f2263f07c48b6d669/7ac0d1c11dbe8a98ec1f596bb9bde7799768fd18685a121f2263f07c48b6d669-json.log",
#    "Name": "/focused_bardeen",
#    "RestartCount": 0,
#    "Driver": "aufs",
#    "ExecDriver": "native-0.2",
#    "MountLabel": "",
#    "ProcessLabel": "",
#    "Volumes": {},
#    "VolumesRW": {},
#    "AppArmorProfile": "",
#    "ExecIDs": null,
#    "HostConfig": {
#        "Binds": null,
#        "ContainerIDFile": "",
#        "LxcConf": [],
#        "Memory": 0,
#        "MemorySwap": 0,
#        "CpuShares": 0,
#        "CpuPeriod": 0,
#        "CpusetCpus": "",
#        "CpusetMems": "",
#        "CpuQuota": 0,
#        "BlkioWeight": 0,
#        "OomKillDisable": false,
#        "Privileged": false,
#        "PortBindings": {},
#        "Links": null,
#        "PublishAllPorts": false,
#        "Dns": null,
#        "DnsSearch": null,
#        "ExtraHosts": null,
#        "VolumesFrom": null,
#        "Devices": [],
#        "NetworkMode": "bridge",
#        "IpcMode": "",
#        "PidMode": "",
#        "CapAdd": null,
#        "CapDrop": null,
#        "RestartPolicy": {
#            "Name": "no",
#            "MaximumRetryCount": 0
#        },
#        "SecurityOpt": null,
#        "ReadonlyRootfs": false,
#        "Ulimits": null,
#        "LogConfig": {
#            "Type": "json-file",
#            "Config": {}
#        },
#        "CgroupParent": ""
#    }
#}
#]

You can pass a name with the docker run like so:

docker run -it --rm --name="SOME_NAME" ubuntu /bin/bash

docker ps
# CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
# d8d2027d6883        ubuntu              "/bin/bash"         5 seconds ago       Up 4 seconds                            SOME_NAME

Next start a service and expose a port mapping (PAT):

docker run --rm -p 80:80 -it nginx

In another window on the host get the IP address of the container:


docker inspect --format "{{ .NetworkSettings.IPAddress }}" 0000a6bfa465
192.168.5.10

Hit the new nginx service you just started. Keep in mind unless the bridge subnet is routed/flooded/forwarded/etc in some fashion it will only be reachable via the local docker host.

curl 192.168.5.10:80

# 192.168.5.1 - - [16/May/2015:22:58:48 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.42.1" "-"

Now notice the differences in the containers metadata with a port exposed:

docker inspect $(docker ps -ql)

#[
#{
#    "Id": "0000a6bfa4650acd686cb63b70bc33dfcbd6e8bb23eaaff1ef43dff21983faa8",
#    "Created": "2015-05-16T22:58:01.787299191Z",
#    "Path": "nginx",
#    "Args": [
#        "-g",
#        "daemon off;"
#    ],
#    "Config": {
#        "Hostname": "0000a6bfa465",
#        "Domainname": "",
#        "User": "",
#        "AttachStdin": true,
#        "AttachStdout": true,
#        "AttachStderr": true,
#        "PortSpecs": null,
#        "ExposedPorts": {
#            "443/tcp": {},
#            "80/tcp": {}
#        },
#        "Tty": true,
#        "OpenStdin": true,
#        "StdinOnce": true,
#        "Env": [
#            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
#            "NGINX_VERSION=1.9.0-1~jessie"
#        ],
#        "Cmd": [
#            "nginx",
#            "-g",
#            "daemon off;"
#        ],
#        "Image": "nginx",
#        "Volumes": {
#            "/var/cache/nginx": {}
#        },
#        "WorkingDir": "",
#        "Entrypoint": null,
#        "NetworkDisabled": false,
#        "MacAddress": "",
#        "OnBuild": null,
#        "Labels": {}
#    },
#    "State": {
#        "Running": true,
#        "Paused": false,
#        "Restarting": false,
#        "OOMKilled": false,
#        "Dead": false,
#        "Pid": 10921,
#        "ExitCode": 0,
#        "Error": "",
#        "StartedAt": "2015-05-16T22:58:02.159142682Z",
#        "FinishedAt": "0001-01-01T00:00:00Z"
#    },
#    "Image": "42a3cf88f3f0cce2b4bfb2ed714eec5ee937525b4c7e0a0f70daff18c3f2ee92",
#    "NetworkSettings": {
#        "Bridge": "bridge0",
#        "Gateway": "192.168.5.1",
#        "GlobalIPv6Address": "",
#        "GlobalIPv6PrefixLen": 0,
#        "IPAddress": "192.168.5.10",
#        "IPPrefixLen": 24,
#        "IPv6Gateway": "",
#        "LinkLocalIPv6Address": "",
#        "LinkLocalIPv6PrefixLen": 0,
#        "MacAddress": "02:42:f7:b2:e4:16",
#        "PortMapping": null,
#        "Ports": {
#            "443/tcp": [
#                {
#                    "HostIp": "0.0.0.0",
#                    "HostPort": "32769"
#                }
#            ],
#            "80/tcp": [
#                {
#                    "HostIp": "0.0.0.0",
#                    "HostPort": "80"
#                }
#            ]
#        },
#        "SecondaryIPAddresses": null,
#        "SecondaryIPv6Addresses": null,
#        "SandboxKey": "/var/run/netns/0000a6bfa465",
#        "NetworkID": "30b2ea4e443226a426b1618ceb954c30dcae9f0df88ba5da942033b0678a8793",
#        "EndpointID": "da4a5d449238e97d1e6e949e570672de4f022ecb6c77ce114c58f7aa0ca9dbe3",
#        "HairpinMode": false
#    },
#    "ResolvConfPath": "/var/lib/docker/containers/0000a6bfa4650acd686cb63b70bc33dfcbd6e8bb23eaaff1ef43dff21983faa8/resolv.conf",
#    "HostnamePath": "/var/lib/docker/containers/0000a6bfa4650acd686cb63b70bc33dfcbd6e8bb23eaaff1ef43dff21983faa8/hostname",
#    "HostsPath": "/var/lib/docker/containers/0000a6bfa4650acd686cb63b70bc33dfcbd6e8bb23eaaff1ef43dff21983faa8/hosts",
#    "LogPath": "/var/lib/docker/containers/0000a6bfa4650acd686cb63b70bc33dfcbd6e8bb23eaaff1ef43dff21983faa8/0000a6bfa4650acd686cb63b70bc33dfcbd6e8bb23eaaff1ef43dff21983faa8-json.log",
#    "Name": "/sad_sammet",
#    "RestartCount": 0,
#    "Driver": "aufs",
#    "ExecDriver": "native-0.2",
#    "MountLabel": "",
#    "ProcessLabel": "",
#    "Volumes": {
#        "/var/cache/nginx": "/var/lib/docker/vfs/dir/8800dcd8ea7597a76a5ffb3fc62f37587d3f0b1d973558a02917b90365b5297e"
#    },
#    "VolumesRW": {
#        "/var/cache/nginx": true
#    },
#    "AppArmorProfile": "",
#    "ExecIDs": null,
#    "HostConfig": {
#        "Binds": null,
#        "ContainerIDFile": "",
#        "LxcConf": [],
#        "Memory": 0,
#        "MemorySwap": 0,
#        "CpuShares": 0,
#        "CpuPeriod": 0,
#        "CpusetCpus": "",
#        "CpusetMems": "",
#        "CpuQuota": 0,
#        "BlkioWeight": 0,
#        "OomKillDisable": false,
#        "Privileged": false,
#        "PortBindings": {
#            "80/tcp": [
#                {
#                    "HostIp": "",
#                    "HostPort": "80"
#                }
#            ]
#        },
#        "Links": null,
#        "PublishAllPorts": false,
#        "Dns": null,
#        "DnsSearch": null,
#        "ExtraHosts": null,
#        "VolumesFrom": null,
#        "Devices": [],
#        "NetworkMode": "bridge",
#        "IpcMode": "",
#        "PidMode": "",
#        "CapAdd": null,
#        "CapDrop": null,
#        "RestartPolicy": {
#            "Name": "no",
#            "MaximumRetryCount": 0
#        },
#        "SecurityOpt": null,
#        "ReadonlyRootfs": false,
#        "Ulimits": null,
#        "LogConfig": {
#            "Type": "json-file",
#            "Config": {}
#        },
#        "CgroupParent": ""
#    }
#}
#]

Errors restarting the daemon

If when you restart your machine and the bridge you created was not reinitialized in the Linux init configs, you need to recreate the bridges or put them in your OS network conf. E.g. for example /etc/network/interfaces or /etc/sysconfig/network-scripts/ifcfg-<name>

FEEDBACK: Should this be initialized by default in future releases? That said, I like that the daemon aborts rather then start broken containers.

The error will look like so:

May 16 19:58:36 deb8-150 docker[586]: time="2015-05-16T19:58:36.778741276-04:00" level=fatal msg="Error starting daemon: Error initializing network controller: Error creating default \"bridge\" network: bridge device with non default name bridge0 must be created manually"
May 16 19:58:36 deb8-150 systemd[1]: docker.service: main process exited, code=exited, status=1/FAILURE
May 16 19:58:36 deb8-150 systemd[1]: Unit docker.service entered failed state.
May 16 20:01:49 deb8-150 docker[608]: time="2015-05-16T20:01:49.338579035-04:00" level=info msg="[graphdriver] using prior storage driver \"aufs\""
May 16 20:01:49 deb8-150 docker[608]: time="2015-05-16T20:01:49.351917230-04:00" level=warning msg="/!\\ DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\"
May 16 20:01:49 deb8-150 docker[608]: time="2015-05-16T20:01:49.352432670-04:00" level=info msg="Listening for HTTP on tcp (0.0.0.0:2375)"
May 16 20:01:49 deb8-150 docker[608]: time="2015-05-16T20:01:49.427094152-04:00" level=fatal msg="Error starting daemon: Error initializing network controller: Error creating default \"bridge\" network: bridge device with non default name bridge0 must be created manually"

To resolve that issue, redefine the bridge specified in the /etc/default/docker

brctl addbr bridge0
ip addr add 192.168.5.1/24 dev bridge0
ip link set dev bridge0 up

Specifying a specific an external address mapping (--ip=)

ps -eaf | grep dock
root      1131     1  0 20:22 ?        00:00:00 /usr/bin/docker -d -H tcp://0.0.0.0:2375 -H fd:// --ip=192.168.5.20
docker run --rm -p 80:80 -it nginx
Error response from daemon: Cannot start container a6a67158adc97a84e7408664cdf8c735db139ab7307be89d62ce92a4abdee220: Error starting userland proxy: listen tcp 192.168.5.20:32777: bind: cannot assign requested address

Add a sub-interface to the iface w/ the desired network you want a secondary on. TODO: get the syntax/instructions for iproute2

ifconfig eth0:1 192.168.5.20  netmask 255.255.255.0 up

Now test the service:

curl 192.168.5.20:80

# <!DOCTYPE html>
# <html>
# <head>
# <title>Welcome to nginx!</title>
# <style>

Specifying a specific an DNS address mapping (--ip= and --dns-search=)

These can be added to the docker run command also. This is handy if the change is not desired globally be default.

# Add the following to /etc/default/docker (restart the daemon to enable the change)
DOCKER_OPTS="--dns=8.8.8.8 "

ps -eaf | grep docker
# root      2100     1  1 00:11 ?        00:00:00 /usr/bin/docker -d -H fd:// --dns=8.8.8.8

docker run --rm -it ubuntu /bin/bash
cat /etc/resolv.conf
# nameserver 8.8.8.8
# search localdomain

If you want to change the search domain, simply pop that into the DOCKER_OPTS ENV like so (bridge section isn't required, just showing compatabilities):

# Add the following to /etc/default/docker (restart the daemon to enable the change)
DOCKER_OPTS="-b=bridge0 --dns=8.8.8.8 --dns-search=foobahs"

ps -eaf | grep docker
# root      2270     1  1 00:34 ?        00:00:00 /usr/bin/docker -d -H fd:// -b=bridge0 --dns=8.8.8.8 --dns-search=foobahs

docker run --rm -it ubuntu /bin/bash

cat /etc/resolv.conf
# nameserver 8.8.8.8
# search foobahs

Specify a containers mac-address at runtime (--mac-address=)

Specify the mac address with the ``docker runcommand using the --mac-address=`. That can be used without changing anything in `/etc/default/docker`.

docker run --rm -it  --mac-address=00:0c:29:58:FF:12  ubuntu /bin/bash

Verify the mac in the container

root@1a61c3349ca1:/# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
19: eth0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 00:0c:29:58:ff:12 brd ff:ff:ff:ff:ff:ff
root@1a61c3349ca1:/# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=127 time=22.6 ms

Specify a block of IP addresses to be used (--fixed-cidr)

If you want to block out a range of addresses that are a subset of the allocated bock you can do so with the following in /etc/default/docker:

DOCKER_OPTS="--fixed-cidr=192.168.5.128/28 -b=bridge0 "

Notice the fixed ipv4 prefix falls within the subnet assigned to the bridge:

ip add show bridge0

link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
inet 192.168.5.1/24 scope global bridge0

Full output:

docker run --rm -it  --mac-address=00:0c:29:58:FF:12  ubuntu /bin/bash
root@b6b692d26f90:/# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
21: eth0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 00:0c:29:58:ff:12 brd ff:ff:ff:ff:ff:ff
    inet 192.168.5.129/24 scope global eth0

Specify a Custom MTU

To specify a custom global MTU for all docker containers on the host modify DOCKER_OPTS

DOCKER_OPTS="--mtu=8192"

This will modify both the docker0 bridge and the veth pair

Here are the docker host links:

6: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8192 qdisc noqueue state UP mode DEFAULT group default
    link/ether 0e:4f:43:25:81:de brd ff:ff:ff:ff:ff:ff
30: veth7996fe4: <BROADCAST,UP,LOWER_UP> mtu 8192 qdisc pfifo_fast master docker0 state UP mode DEFAULT group default qlen 1000
    link/ether 0e:4f:43:25:81:de brd ff:ff:ff:ff:ff:ff

The other side of the veth pair that is renamed to eth0 (by default) with the custom MTU

root@316895e0e52c:/# ip link show eth0
29: eth0: <BROADCAST,UP,LOWER_UP> mtu 8192 qdisc noqueue state UP mode DEFAULT group default
    link/ether 02:42:42:fa:c5:ce brd ff:ff:ff:ff:ff:ff

IPv6 with a Custom Bridge (--ipv6=true -b=bridge0)

By default if you specify v6 support Docker provisions what is commonly referred to as a 'dual stack' meaning the host has both an IPv4 address and an IPv6 address.

The DOCKER_OPTS config for this example is as follows:

DOCKER_OPTS="-b=bridge0 --mtu=8192 --ipv6=true"

The Docker host side of the veth pair looks like this:

ip a show bridge0
3: bridge0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8192 qdisc noqueue state UP group default
    link/ether 4a:c5:20:ab:04:1a brd ff:ff:ff:ff:ff:ff
    inet 192.168.5.1/24 scope global bridge0
       valid_lft forever preferred_lft forever
    inet6 fe80::1/64 scope link
       valid_lft forever preferred_lft forever
    inet6 fe80::189c:73ff:fe9d:d11e/64 scope link
       valid_lft forever preferred_lft forever

The docker container side of the pair is:

31: eth0: <BROADCAST,UP,LOWER_UP> mtu 8192 qdisc noqueue state UP group default
    link/ether 02:42:c9:72:2e:3a brd ff:ff:ff:ff:ff:ff
    inet 192.168.5.2/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::242:c972:2e3a/64 scope link
       valid_lft forever preferred_lft forever
    inet6 fe80::42:c9ff:fe72:2e3a/64 scope link
       valid_lft forever preferred_lft forever

Default docker0 (--ipv6=true) support

Note: when you are switching between a custom bridge and the default docker0 profiles, you currently need to destroy containers using the previous configuration. The daemon will refuse to start if it detects a collision like this in the configuration at runtime. This is actually a positive choice in the docker logic because the alternative would be to spin up a container that appears to be functioning but in reality is in a broken state.

The DOCKER_OPTS config for this example is as follows:

DOCKER_OPTS="--ipv6=true"

ip a show docker0
6: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 5a:4f:12:b4:93:ac brd ff:ff:ff:ff:ff:ff
    inet 172.17.42.1/16 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::1/64 scope link
       valid_lft forever preferred_lft forever
    inet6 fe80::182e:1dff:fe64:cf3f/64 scope link
       valid_lft forever preferred_lft forever

The docker container side of the veth pair

ip a show eth0
33: eth0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:5c:b3:29:a2 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::242:5cb3:29a2/64 scope link
       valid_lft forever preferred_lft forever
    inet6 fe80::42:5cff:feb3:29a2/64 scope link
       valid_lft forever preferred_lft forever

Lastly for testing v6 connectivity simply use the ping6 utility that is installed by default in most of the popular Linux images in the Docker Library.

root@167585be85a9:/# ping6 -c 2 fe80::182e:1dff:fe64:cf3f
PING fe80::182e:1dff:fe64:cf3f (fe80::182e:1dff:fe64:cf3f): 56 data bytes
64 bytes from fe80::182e:1dff:fe64:cf3f%eth0: icmp_seq=0 ttl=64 time=0.083 ms
64 bytes from fe80::182e:1dff:fe64:cf3f%eth0: icmp_seq=1 ttl=64 time=0.057 ms
--- fe80::182e:1dff:fe64:cf3f ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.057/0.070/0.083/0.000 ms

Set a custom hostname with Docker Run

Simply pass the --hostname=FOO option when you create a container:

docker run -it --rm -p 80:80 --hostname=ponyrides  nginx /bin/bash
root@ponyrides:

This will update your /etc/hosts config file also:

root@ponyrides:/# cat /etc/hosts
172.17.0.2	ponyrides

Systemd Config with Docker

Locate the systemd docker.service unit file on the Docker host. On Ubuntu or Debian it is located at: /lib/systemd/system/docker.service

ExecStart is the main one you will probably edit. As long as $DOCKER_OPTS is in there as a value the /etc/default/docker file will get read at initialization and added to the process exec. You can add it directly here is you so desire.

Note, on Ubuntu Vivid 15.04 I still need to manually kill -9 the docker process because Systemd just sits there in a state like so:

systemctl status docker
● docker.service - Docker Application Container Engine
   Loaded: loaded (/lib/systemd/system/docker.service; enabled)
   Active: deactivating (stop-sigterm) since Sat 2015-05-16 18:35:47 EDT; 1min 0s ago
     Docs: http://docs.docker.com
 Main PID: 10312 (docker)
   CGroup: /system.slice/docker.service
           └─10312 /usr/bin/docker -d -H tcp://0.0.0.0:2375 -H fd:// -b=bridge0

It will finally time out and get killed with sig but hey its systemd ><


[Unit]
Description=Docker Application Container Engine
Documentation=http://docs.docker.com
After=network.target docker.socket
Requires=docker.socket

[Service]
EnvironmentFile=-/etc/default/docker
ExecStart=/usr/bin/docker -d -H fd:// $DOCKER_OPTS
MountFlags=slave
LimitNOFILE=1048576
LimitNPROC=1048576
LimitCORE=infinity

[Install]
WantedBy=multi-user.target

The above Systemd unit file coupled with the following '/etc/default/docker'

# Docker Upstart and SysVinit configuration file

# Customize location of Docker binary (especially for development testing).
#DOCKER="/usr/local/bin/docker"

# Use DOCKER_OPTS to modify the daemon startup options.
#DOCKER_OPTS="--dns 8.8.8.8 --dns 8.8.4.4"

# If you need Docker to use an HTTP proxy, it can also be specified here.
#export http_proxy="http://127.0.0.1:3128/"

# This is also a handy place to tweak where Docker's temporary files go.
#export TMPDIR="/mnt/bigdrive/docker-tmp"
DOCKER_OPTS="-b=bridge0 -H tcp://0.0.0.0:2375"

Result in a running process of this:

ps -eaf | grep docker
# root     10375     1  0 18:39 ?        00:00:00 /usr/bin/docker -d -H tcp://0.0.0.0:2375 -H fd:// -b=bridge0 -H tcp://0.0.0.0:2375

and a systemd status docker of this:

systemctl status docker
● docker.service - Docker Application Container Engine
   Loaded: loaded (/lib/systemd/system/docker.service; enabled)
   Active: active (running) since Sat 2015-05-16 18:39:25 EDT; 5s ago
     Docs: http://docs.docker.com
 Main PID: 10375 (docker)
   CGroup: /system.slice/docker.service
           └─10375 /usr/bin/docker -d -H tcp://0.0.0.0:2375 -H fd:// -b=bridge0 -H tcp://0.0.0.0:2375

Note I have to DOCKER_OPTS parameters added along with the default configuration DOCKER_OPTS="-b=bridge0 -H tcp://0.0.0.0:2375".

-b=bridge0 instructs docker to use bridge0 rather then the default docker0 bridge.

-H tcp://0.0.0.0:2375 binds the remote API to port 2375 on all IP addresses for remote docker cli execution of the docker host.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment