As the saying goes, "you are only as good as your tools", and this is especially true when it comes to software development and network automation.
Within this article will provide a beginners guide to 2 tools that should be in any network automators toolbox - Docker and Visual Studio Code.
- Docker - A container platform that can be used to create lightweight isolated environments, allowing you to easily separate dependencies between projects.
- Visual Studio Code (VS Code) - An extremely popular free open source IDE, that can reduce development time, due to inbuilt syntax checking, formatting and debugging features.
Figure 1 - VS Code.
Containerised applications are becoming more popular for a number of reasons like application packaging, standardisation, lightweight portability, and ease of use for testing/development.
Unlike a VM (virtual machine), which is a virtualized instance of an OS, a container is a logically isolated slice of an operating system, i.e. unlike a VM each container shares a kernel with other containers and other system libraries. This makes containers extremely lightweight and fast to run.
Figure 2 - Container vs VM Overview. 
There are multiple container platforms available, however in this article we will focus on Docker due to its simplicity and popularity.
There are 2 main components that you must first understand when it comes to Docker - images and containers. Docker images can be considered as nothing more than a template and Docker containers are the running instances created from those templates.
For the purpose of this tutorial I suggest the usage and installation of Docker Desktop for Mac and Windows. To follow along with the examples clone the git repository like so:
$ git clone https://github.com/rickdonato/python-dev-with-docker-and-vscode
Within this repo a simple CLI python script is included that will generate some interface configuration. The other files within this repo will all become clear as we go through the various examples.
An image is built when instructions defined in a
Dockerfile are executed by docker.
This file is usually located under the same directory of your project, in this case under the
python-development-with-docker-and-vscode directory. For this example, I have named it
FROM python:3.8-rc-slim-stretch LABEL version="1.0.0" LABEL description="This an example python-based container" LABEL maintainer="David Flores <@netpanda>" WORKDIR /app COPY . /app RUN pip install -r requirements.txt CMD ["/bin/bash"]
Let's dissect what these instructions mean:
FROM python:3.8-rc-slim-stretch: Specifies the parent image to be used to build this container. This is a python-slim and lightweight image downloaded from Docker Hub. If you want to know how the python image is built and how to use it see their documentation.
LABEL key=value: The 3 lines are specifying key value pairs of information used for the container metadata.
WORKDIR /app: The instruction sets the working directory for any
COPY . /app: This instruction copies new files from
<src>and adds them to the filesystem of the container at the path
<dest>. In this case it will copy the python code, the data variable files and the templates.
RUN pip install -r requirements.txt: The
RUNinstruction will execute any commands in a new layer on top of the current image and commit the results. The resulting committed image will be used for the next step in the
Dockerfile. Since it will trigger a layering process you should use them wisely. In this case it will install the Python application requirements.
CMD [“/bin/bash”]: The main purpose of
CMDis to define a command that is executed at runtime.
For more information on the available instructions take a look at Dockerfile Reference.
Care must be taken on the
COPY instruction, as you may not want to add ALL the files from the python application into the container. May due to the files not being required or due to security reasons. To exclude directories and files from being copied into your container create a file
.dockerignore, defining which files and directories you want to exclude.
**/.git **/.gitignore **/.vscode README.md
With the Dockefile created, an image now needs to be built. For that we use the docker build command.
Make sure you are at the same level as the
Dockerfile location, although you can specify its location with command-line options.
$ docker build --tag intro-netautomation:standalone --file Dockerfile.standalone .
--tag <name:tag>will specify the image name and optionally a tag.
--file <dockerfile path>will specify the Dockerfile filename name.
.at the end is to look for the
Dockerfilein the current directory and its "context" (folders and files needed to build the image). Though we have manually specified the
Dockerfile.standalonefile, with the
.we are also specifying the directory to copy the other project files and folders from.
The result will be a Docker container image named
intro-netautomation with the tag
standalone. To verify the images and general specifications you can run:
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE intro-netautomation standalone 1abe9d092bfe 10 minutes ago 159MB ...
Since this is an “interactive” container application, meaning that you need to connect to it to be able to run the CLI script, there are a couple of flags needed for the docker run command.
$ docker run --rm -it intro-netautomation:standalone
Let's dissect the flags and arguments passed:
--rmoption will automatically remove the container when it exits.
-itflags are short for
--tty. So when then container is run it will take you straight inside of it .This is also known as interactive mode.
intro-netautomation:standalone- this states which image to create a running instance of.
- The last argument is normally composed of the command and arguments to pass to the running container. In this case it is empty and thus using the default parameter defined in the
CMDinstruction, which is to run
When executed it will start the container and present you within the bash shell. Since the python parent image is based on Debian you can run commands like
Here is an example of
$ docker run --rm -it intro-netautomation:standalone root@6924e6a68475:/app# root@6924e6a68475:/app# ls -al total 44 drwxr-xr-x 1 root root 4096 Feb 9 12:51 . drwxr-xr-x 1 root root 4096 Feb 9 13:05 .. drwxr-xr-x 2 root root 4096 Feb 9 12:37 .devcontainer -rw-r--r-- 1 root root 49 Feb 9 12:37 .dockerignore -rw-r--r-- 1 root root 173 Feb 9 12:37 Dockerfile -rw-r--r-- 1 root root 262 Feb 9 12:46 Dockerfile.standalone drwxr-xr-x 2 root root 4096 Feb 9 12:51 build -rw-r--r-- 1 root root 1904 Feb 9 12:37 configurator.py drwxr-xr-x 2 root root 4096 Feb 9 12:37 data -rw-r--r-- 1 root root 14 Feb 9 12:37 requirements.txt drwxr-xr-x 2 root root 4096 Feb 9 12:37 templates
You can see when running the
ls -al no git files or folders are present, this is thanks to the
.dockerignore file. What follows next is an example run of the CLI script.
root@6924e6a68475:/app# python configurator.py data/interfaces_vars.json templates/cisco_interfaces.j2 Created data/conf.txt File! --> ! interface Vlan177 description Lan In-Band Network ip address 10.77.1.68 255.255.255.0 load-interval 5 ! interface Management1 description lab01 - Eth100/1/37 ip address 10.17.17.177 255.255.255.0 load-interval 5 no shutdown ! ! root@6924e6a68475:/app# exit $
Interactive Mode is useful for one-time applications or services that provide some sort of API or callback process to interact with, but that is not always the case.
In this example, since this is a CLI application, it will be best to have the container running in detached mode, i.e in the background. This is achieved by, again, running the
docker run command with the parameters shown before, but with the addition of
-d for detached. You can see more about this option within the following documentation.
$ docker run --name container01 -dit intro-netautomation:standalone e53e4a8e582a753e71b33e7e1c1b7647a72f438260ba0a0cd72fdc3208900b21
This will create the container named
container01 in detached mode. The output generated is its complete ID. You can see the container running and its general parameters with
docker container ls:
$ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e53e4a8e582a intro-netautomation:standalone "/bin/bash" 12 seconds ago Up 11 seconds container01
Docker exec. allows you to run a command in a running container. For example,
docker exec -it container01 /bin/bash, this instructs Docker to run (within interactive mode) the command
$ docker exec -it container01 /bin/bash root@e53e4a8e582a:/app# root@e53e4a8e582a:/app# exit exit $ $ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e53e4a8e582a intro-netautomation:standalone "/bin/bash" 2 minutes ago Up 2 minutes container01
This gives you the ability to have a running container and test it. With an editor like Visual Studio Code you can connect to the container and perform development tasks on it!. More on this later.
With this approach you can treat the container in a VM like manner. You can stop it and start it when you need it. This is done with the
docker stop and
docker start commands.
$ docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e53e4a8e582a intro-netautomation:standalone "/bin/bash" 4 minutes ago Up 4 minutes container01 $ docker stop container01 container01 $ docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e53e4a8e582a intro-netautomation:standalone "/bin/bash" 6 minutes ago Exited (0) 15 seconds ago container01 $ docker start container01 container01 $ docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e53e4a8e582a intro-netautomation:standalone "/bin/bash" 6 minutes ago Up 3 seconds container01
Is important to know that files added or modified inside the container are NOT GOING TO BE DESTROYED, you can test by adding a file on the container directory
/app, stopping the container, and then starting it again. When you check the directory you will see the file still remains.
This makes the container a great lightweight jump server/bastion portable solution when using it with the likes of network simulation tools like GNS3 or EVE-NG.
You can find in this repository some dockerfiles with instructions to build a network-tool centric workstation : GitHub - davidban77/netautomator: Docker specs for a network automation focused workstation.
The last feature/command I want to talk about in this tutorial is the docker commit command. It creates a new image from a container’s changes.
For example, let's say I already added a “test” file under the
/app directory on
container01 and want to create a new image named test.
$ docker commit -m "Adding a test file" container01 intro-netautomation:test sha256:d9e4b1d7519922df673f9d11c672e36cf12fecf7e5aac2654000af25d165cf36 $ $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE intro-netautomation test 38ab5295b57d 3 seconds ago 159MB intro-netautomation standalone 1abe9d092bfe 28 minutes ago 159MB
The new image is tagged
Let us create a new container from this image and check the contents of the
docker run --name container02 -it intro-netautomation:test root@624497b5980b:/app# root@624497b5980b:/app# ls -al total 44 drwxr-xr-x 1 root root 4096 Feb 9 13:22 . drwxr-xr-x 1 root root 4096 Feb 9 13:23 .. drwxr-xr-x 2 root root 4096 Feb 9 12:37 .devcontainer -rw-r--r-- 1 root root 49 Feb 9 12:37 .dockerignore -rw-r--r-- 1 root root 173 Feb 9 12:37 Dockerfile -rw-r--r-- 1 root root 262 Feb 9 12:46 Dockerfile.standalone drwxr-xr-x 2 root root 4096 Feb 9 12:51 build -rw-r--r-- 1 root root 1904 Feb 9 12:37 configurator.py drwxr-xr-x 2 root root 4096 Feb 9 12:37 data -rw-r--r-- 1 root root 14 Feb 9 12:37 requirements.txt drwxr-xr-x 2 root root 4096 Feb 9 12:37 templates -rw-r--r-- 1 root root 0 Feb 9 13:22 test root@624497b5980b:/app# exit
You can see the
test file present at the end.
In the next part of this series we will dive into the integration of Docker with VS Code. See you there...
"Container workflows at Pawsey: Introduction to Docker." https://pawseysc.github.io/container-workflows/01-docker-intro/index.html. Accessed 24 Feb. 2020. ↩︎
"Docker Images vs Containers - All You Need To Know." 16 Oct. 2019, https://www.dockerjet.com/docker-images-vs-containers. Accessed 24 Feb. 2020. ↩︎