Docker – Introduction covered the evolution, the need for Docker and the reason why it is widely used. This post will be a deep dive into the concepts of Docker and it’s practical use cases.
This post covers everything required to create an image and run an application using docker. The OS I have chosen Windows for all the examples, mainly because it is quite challenging. Working with docker on Windows is not as seamless as it is on Linux/Unix OS. Hence I have included a lot of workaround hoping it will be of help those using Windows.
You can install from the above link by following a few simple instructions. Once done, add the following to the “PATH” variable. You can verify if it works, with “docker –version” command.
C:\Program Files\Docker\Docker\resources\bin C:\ProgramData\DockerDesktop\version-bin
Let us begin
Now, we will create a simple python program that prints “hello world” and name it main.py.
Dockerfile is a text document that contains all the instructions required to build an image. It should be named exactly as “Dockerfile” and placed inside the application folder. Most commonly used commands in a dockerfile are:
FROM python ENV <> RUN <> COPY <> CMD <> WORKDIR <>
- Dockerfile always starts with a FROM command that specifies the pre-existing image (present on dockerhub) to import from.
- ENV is for setting environment variables in the container. This is optional and usually moved to the Docker compose file
- RUN to execute linux commands
- COPY – executes on the host. Can copy files from the host to the container.
- CMD – is the entry point command for the application execution
- WORKDIR – points to the directory in the container, where the application code exists.
Create a “Dockerfile” with these three lines:
FROM python:3.7-alpine COPY helloWorld.py run.py CMD ["python","run.py"]
- The first line will set up the environment by installing python. 3.7-alpine which refers to the already available Image on docker hub. You can find more such Images on hub.docker.com. This step takes time when the container is built for the first time as the installations happen. Subsequently, it should take lesser time.
- The next lines copies the contents of the local file helloWorld.py onto the file on the newly created Image. Here the file name is run.py, but the name can be anything.
- The third line specifies the Command that is to be run on the Container to execute the program in the file, just copied.
Now, run the following commands to first build the image and then create a container. Replace <sample-image> with a name of your own. You should now see “Hello World!!!” printed on the console.
docker build -t <sample-image> . docker run <sample-image>
Working with Environment variables
We previously saw how to execute a simple print statement on a docker container. Let’s now see a little more complex example where we pass an environment variable from local as an argument while building the container. This will set the environment variable in the container image. We will verify the same by printing the environment variable in the container.
import os def main(): print(os.environ.get('testenv')) main()
FROM python:3.7-alpine COPY main.py run.py ARG x ENV testenv=$x CMD ["python","run.py"]
Let us set an environment variable in our local machine. Command for windows:
D:\Projects\DockerSample>set whatdoing="docker" D:\Projects\DockerSample>echo %whatdoing% "docker"
Here’s how we run the build command by passing an argument:
docker build --build-arg x=%whatdoing% -t sample-image . docker run sample-image
The above should print “docker”. %whatdoing% is an env variable in the local machine. In the Dockerfile, we receive an argument “ARG x” and set the same as the value for the environment variable “testenv”. Our python program “main.py” is fetching the environment variable “testenv” and prints the value of the same.
Hence, we can not only install various packages onto the docker container but also set all the required environment variables before-hand, in order to run an application on the newly created container.
Here are some frequently used commands that can come handy while working with docker images and containers:
- Get an image from Docker hub: (Example - redis) docker pull <name> - List all the images: docker images - List only image IDs: docker images -q (q stands for quiet. Suppresses other columns) - Create a container of a given image: docker run <image> - Run a container in detached mode: docker run -d <image> - Remove a docker image by ID docker rmi <Image ID> - List the docker containers in running state: (PS - Process State) docker ps - List all containers irrespective of their state: docker ps -a - Restart a container: docker stop <container-id> docker start <container-id> - Bind a port of your host to a port of the container while creating a container: docker run -p <host-port>:<container-port> <image> - View container logs: (you can also stream the logs using -f option) docker logs <container-id/name> -f - Specify a name for the container: docker run --name inamedthis <image> - Get the terminal of the running container: docker exec -it <container-id/name> /bin/bash - List the current networks: docker network ls - Create a new docker network: docker network create <name>
Cleaning container images:
Here’s an important workaround while working with Windows. Time and again, I ran into issues with the container not reflecting the current state. This was due to old containers not being stopped and removed correctly before creating new ones when they had the same name. Here’s a batch file that clears old containers:
@ECHO OFF FOR /f "tokens=*" %%i IN ('docker ps -aq') DO docker stop %%i FOR /f "tokens=*" %%i IN ('docker ps -aq') DO docker rm %%i
You can put the above in a file and add to the path variable so that you can use it whenever you are creating/debugging containers multiple times.
In the next post we will see how we can run multiple services on docker using Docker Compose and also host an application on docker.