Introduction
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.[1]
Containers/Docker
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. [2]
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.[3]
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.
Dockerfile
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 Docker.standalone
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 anyRUN
,CMD
,ENTRYPOINT
andADD
instructions.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
: TheRUN
instruction 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 theDockerfile
. 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 ofCMD
is to define a command that is executed at runtime.
For more information on the available instructions take a look at Dockerfile Reference.
Dockerignore
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
Docker Build
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 .
- The
--tag <name:tag>
will specify the image name and optionally a tag. - The
--file <dockerfile path>
will specify the Dockerfile filename name. - The
.
at the end is to look for theDockerfile
in the current directory and its "context" (folders and files needed to build the image). Though we have manually specified theDockerfile.standalone
file, 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
...
Docker Run
Interactive Mode
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:
- The
--rm
option will automatically remove the container when it exits. - The
-it
flags are short for--interactive
and--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
CMD
instruction, which is to run/bin/bash
.
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 pwd
and ls
.
Here is an example of docker run
:
$ 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
$
Detached Mode
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
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 /bin/bash
on container01
.
$ 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.
Docker Start/Stop
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.
For example:
$ 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.
Docker Commit
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 intro-netautomation:test
.
Let us create a new container from this image and check the contents of the /app
directory.
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.
Coming Up
In the next part of this series we will dive into the integration of Docker with VS Code. See you there...
References
"Visual Studio Code - Code Editing. Redefined." https://code.visualstudio.com/. Accessed 24 Feb. 2020. ↩︎
"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. ↩︎