I have heard many things about containerization and have run into many situations where I thought âwow it would be so much easier if I could âcopy and pasteâ everything remotely related to this project onto this other computer and have it run exactly the same.â Yeah, I think itâs time to learn about Docker.
3 Fundamental Elements
Docker functions in a very comparable way to virtual machines.
The Docker File (DNA)
At a glance: Code that tells Docker how to build an image
How it manifests: Itâs a file in your project called Dockerfile
with a series of commands, notably FROM
to specify the base image, and RUN
to call terminal commands during the build process such as installing dependencies, and CMD
which specifies a default command to run when a container is started from the image.
Create the image file using docker build -t myapp:latest ./
. The -t
option specifies a tag formatted as name:tag
(tag is latest
by default). ./
is the path to the dockerfile.
The Image
An immutable snapshot of your software (i.e. read only), along with all of its dependencies down to the operating system level. Can be used to spin up multiple containers.
The Container
Your actual software running in the real world (i.e. an instance of the image).
Bring an image to life by using docker run myapp
.
Container
Some commands:
docker run
docker ps
: view running containers, use-a
to show alldocker stop <id>
: send stop signal (SIGTERM)docker kill <id>
: send kill signal (SIGKILL) when unresponsivedocker rm <id>
: remove the container
Run a container from an image using docker run
.
docker run -d -p hostport:containerport namespace/name:tag
docker run -d -p 8965:80 docker/getting-started:latest
docker run --name postgresql -p 5432:5432 -e POSTGRES_PASSWORD=mypassword -d bitnami/postgresql:latest
-d
: run in detached mode, container runs in background and terminal can still be used-p
: exposes a port in the container and maps it to a port on the host machine-e
: adds an environment variable with the specified value--name
: optionally assign a name to the containernamespace/name:tag
the last argument is the path to the image on Docker hub which will be downloaded, a tag can optionally be added to specify the version (otherwise latest)
Volumes
Changes made in a container (like modifying files) will not affect the image, when a container is restarted it will go back to its original state. Persistent state can be utilized through the use of storage volumesâa file system that lives outside of the container.
Create one using docker volume create <name>
. Make sure it worked docker volume ls
. Get more info using docker volume inspect <name>
. When using docker run
use the -v ghost-vol:/var/lib/ghost
option to mount the volume.
Exec
Usually when a container is deployed, you just let it do its thing. However, you can still interface with it similar to ssh by using docker exec <id> <command>
.
In the event you want a live shell rather than one-off commands, we can use -it
and run the shell at /bin/sh
like so docker exec -it <id> /bin/sh
.
Dockerfiles
âDockerizeâ a project by adding a Dockerfile
file to the root of the project which can be built into an image. First, weâll want to specify a base image using the FROM
keywordâthis is required. Then weâll use some combination of the following commands (not comprehensive):
COPY <src> <dst>
: Copy files/folders from host machine into the docker imageRUN <cmd> (&& <cmd> ...)
: Executes commands at build time (chain using&&
)CMD
: Executes command at runtime, i.e. when the container starts (there can only be oneCMD
, last one takes precedence)EXPOSE <port>
: Declares what ports the container will listen on at runtime (does not publish a port on the host machine, however)
The (usual) Dockerfile process
- Create
Dockerfile
- Add
FROM
command to specify base image- IF PRECOMPILED: Copy files with
COPY
into the image - IF DEPENDENCIES NEEDED: Use
RUN
commands to set up the image as if it were a fresh machine to run your server, andCOPY
anything thatâs needed
- IF PRECOMPILED: Copy files with
- Run server with
CMD
- Build the image with
docker build . -t myserver:latest
- Run the image with
docker run -p <port>:<port> myserver
The (sort of) deployment process
- Write code â Commit to Git â Push to GitHub â PR into main â Approve PR â Merge PR
- Upon merge, an automated script (like GitHub actions) builds the code
- The automated script builds a new docker image
- The automated script pushes the new image to a registry
- The server that runs the containers (like a Kubernetes cluster) is informed about the new version and pulls it, shutting down the old container and spinning up a new one.
Tags
Not much to say here, generally speaking a tag will always be either a semantic version like 0.1.1
or latest
. Usually weâll always build with a semantic versioning tag but also push to latest
which will overwrite the previous latest. Like so:
docker build -t company/product:5.4.6 -t company/product:latest .
docker push company/product --all-tags