One of the tenants of building Docker containers is to keep them small and lean. In some cases, this can limit your troubleshooting options as the containers won't have many of the common Linux networking tools as part of their image. While not ideal, it is sometimes nice to have a container image that has these tools installed so that you can troubleshoot the network from the container perspective. In this chapter, we'll review how to build a Docker image specifically for this purpose.
In this recipe, we'll be using a single Docker network host. It is assumed that Docker is installed and in its default configuration. You'll also need root-level access in order to inspect and change the hosts networking and firewall configuration.
A Docker image is built by defining a Dockerfile. The Dockerfile defines what base image to use as well as commands to run inside of the container. In my example, I'll define the Dockerfile as follows:
FROM ubuntu:16.04 MAINTAINER Jon Langemak [email protected] RUN apt-get update && apt-get install -y apache2 net-tools inetutils-ping curl dnsutils vim ethtool tcpdump ADD index.html /var/www/html/index.html ENV APACHE_RUN_USER www-data ENV APACHE_RUN_GROUP www-data ENV APACHE_LOG_DIR /var/log/apache2 ENV APACHE_PID_FILE /var/run/apache2/apache2.pid ENV APACHE_LOCK_DIR /var/run/apache2 RUN mkdir -p /var/run/apache2 RUN chown www-data:www-data /var/run/apache2 EXPOSE 80 CMD ["/usr/sbin/apache2", "-D", "FOREGROUND"]
The goal of this image is twofold. First, I wanted to be able to run the container in detached mode and have it offer a service. This would allow me to define the container and verify that things such as port publishing were working off the host. This container image provides me with a known good container that will publish a service on port 80
. For this purpose, we're using Apache to host a simple index page.
The index file is pulled into the image at build time and can be customized by you. I use a simple HTML page, index.html
, that shows big red font such as this:
<body> <html> <h1><span style="color:#FF0000;font-size:72px;">Test Web Server - Running on port 80</span> </h1> </body> </html>
Second, the image has a lot of network tools installed as part of the image. You'll notice that I'm installing the following packages:
net-tools
: This provides network utilities to view and configure interfacesinetutils-ping
: This provides ping functionalitycurl
: This is to pull files from other network endpointsdnsutils
: This is to resolve DNS names and other DNS tracingethtool
: This is to get information and stats from interfacestcpdump
: This is to do packet capture from within the containerIf you define this Dockerfile, as well as it's required supporting files (an index page), you can build the image as follows:
sudo docker build -t <tag name for image> <path files ('.' If local)>
Docker will then process the Dockerfile and, if successful, it will generate a docker image
file, which you can then push to your container registry of choice for consumption on other hosts with docker pull
.
Once built, you can run it and verify that the tools are working as expected. Having ethtool
within the container means that we can easily determine the host-side VETH end of the VETH pair:
user@docker1:~$ docker run -dP --name nettest jonlangemak/net_tools user@docker1:~$ docker exec -it nettest /bin/bash root@2ef59fcc0f60:/# ethtool -S eth0 NIC statistics: peer_ifindex: 5 root@2ef59fcc0f60:/#
We can also perform local tcpdump
actions to verify traffic reaching the container:
root@2ef59fcc0f60:/# tcpdump -qnn -i eth0 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes 15:17:43.442243 IP 10.20.30.41.54974 > 172.17.0.3.80: tcp 0 15:17:43.442286 IP 172.17.0.3.80 > 10.20.30.41.54974: tcp 0
As your use cases change, you can modify the Dockerfile to make it more specific to your own use cases. Being able to troubleshoot from within the container can be a big help when diagnosing connectivity issues.