Jump to the commands sections
What is docker?#
Docker is an open platform for developing, shipping, and running applications.
Docker lets you encapsulate your application along with all its system dependencies into a single executable that can be shipped predictably into servers or clusters; or as development environment for other developers using different Operating Systems.
If you are familiar with Virtual Machines (such as Virtual Box) or Vagrant then you should feel comfortable enough with Docker.
With Docker you can easily create something very similar to a headless virtual machine. You can install system dependencies, configure networks and ports, setup databases, fetch your code and start your app with a simple configuration file i.e. Dockerfile.
Today it’s very common for IaaS and PaaS providers to provide first class support for Docker containers. Examples of these providers are Google Cloud Compute Engine, Microsoft Azure and Amazon AWS among others. You can use Kubernetes to manage a cluster composed of dozens of Docker containers in these providers and scale easily so long as you can afford it.
Lets take a simple Nodejs / Front End application, what does it need to run?
-
System Dependencies:
-
System configurations:
- npm/yarn repositories.
- OS networks, users, groups, etc.
- System optimizations such as removing any OS components not needed for running your app.
-
build/run steps:
- fetching your app’s code
- installing dependencies (i.e. via
yarn install
ornpm install
) - building (if necessary)
- starting your app
We will a cover an example of this later.
Docker lets you express all these necessary steps that make up a single deliverable application.
The way you express these necessary steps is a Dockerfile and the result of building your Dockerfile is an Image that can be instantiated and executed, this executable is called a Container.
What is an image?#
An image is a read-only template with instructions for creating a Docker container.
You use a Dockerfile to define an image.
Your Dockerfile is an ordered list of steps necessary to run your app. Each line will generate an intermediate image that Docker will cache for performance reasons, so the order of your steps is important.
This is a rather minimal but typical Dockerfile for a NodeJs app:
# This is our base community provided image,
# with some minimal distribution of linux
# that comes with NodeJs v10 (and npm) already installed
FROM node:12
# Use this directory _inside_ your Docker container
# as the base directory of all the next actions
WORKDIR /usr/src/app
# Copy your app's code from the host (your computer) to the container.
# This assumes a typical setup in which the Dockerfile
# is in the root of your project
COPY package.json ./
# Install dependencies
RUN npm install
# Copy the rest of your App's source files
COPY . .
# Tell Docker that your app will be working in the port 8080
# so we should open it up to the outside
EXPOSE 8080
# Run your app.
# CMD signals Docker that this is the thing that we want to run,
# if this executable fails then the whole container will exit.
CMD [ "node", "server.js" ]
Notice how we install dependencies before copying the App’s code, this assumes that the dependencies will change less frequently that the App code so Docker might be able to use the already cached intermediate image up to that point.
Building images means processing the list of instructions and sort of render the final image so that it is ready to be instantiated into a running container.
IMPORTANT: Images are just instructions, when you run them you turn them into Containers.
What is a container?#
A container is a runnable instance of an image.
After you built your final image you can then instantiate it into an executable, this executable is called a container and it is treated by your host OS as just another process.
Take a moment to think about the big leap of the final statement, this means that by using Docker you have encapsulated an entire Operating System (OS), dependencies, configurations, startup commands, etc into a single runnable process in your host Operating System. Nice, right?
Image vs Container: an analogy#
You can think of an image as a traditional Object Oriented Programming (OOP) Class, and a container as an instance. You can have multiple running instances of an image (that is usually how you scale your app inside a cluster), with slightly different parameters (Environment Variables mostly).
Images
can inherit from other images
to compose them in useful and meaningful ways.
Docker repositories (Docker Hub)#
Docker Hub repositories allow you share container images with your team, customers, or the Docker community at large.
Docker Hub is an analogue solution to things such as NPM to distribute NodeJs packages, Cargo to distribute Rust crates (pieces of code), Maven to distribute Java Jars (pieces of code), etc.
Docker hub also has the concept of registry similar to NPM where you can use the default Docker hub registry or your company’s own. The default Docker hub registry is where most of the base images, such as NodeJs, Nginx, Java, etc live.
In the Dockerfile
we dissected before, the first line is FROM node:12
and
tells docker to use the base image node:12
(NodeJs version 12) that is stored
in a Docker hub registry, by default it will use Docker’s registry.
The first time you try to build this image you will notice that Docker downloads the base image and store it in cache.
We cover the basic commands in the next section.
Essential commands#
This assumes you have docker installed in your system (Linux or Macos)
Building#
Build your docker image (from a Dockerfile in the current directory).
Docker will output the Image Id
if everything goes well. This will be used
to instantiate the image later.
docker build .
# equivalent to
docker build ./Dockerfile
Use tags
to easily reference the built image, this replaced the need to use Image Id
docker build -t mySuperApp .
Force docker to re-build your image
. Typically this is used when you changed something
in your Dockerfile, or the base image has changed.
docker build --no-cache .
Check the built images in your system
docker image ls
All the build commands output the docker Image id
which is something you can use when
instantiating them into containers
, but using a tag
is a much more human friendly way.
Docker hub#
Push your most recently built image to docker hub using the tag as identifier. Tags can have a complex form to keep track of versions and such. Some common patterns are:
name-of-my-app:LATEST
: the latest version of your app, will keep changing as new versions come.name-of-my-app:VERSION
: a given version of your app.nodejs:10
: the base NodeJs v10 image.
docker push $TAG
Running#
Instantiate an image
either by Image Id
or by tag
docker run $IMAGE_ID
docker run $TAG
Debugging#
These commands are very useful for debugging your container, image or Application.
Instantiate the image
into a container
and replace the CMD
in the Dockerfile for
an interactive shell you can play around with. This is particularly useful when your app
fails to start, you can ssh into the container and manually start the app or make some debugging.
docker run -ti $IMAGEID_OR_TAG bash
Run a container
that uses your host’s (your machine) App directory and files.
This allows you to use your regular development tools such as IDEs or Text Editors
to edit your container
’s files in place. This is particularly useful when debugging
your App running inside a container.
$PWD
is your process working directory or the directory you are currently located in your shell instance,
in this case we assume is your App’s root directory.
Your container WORKDIR
should be the one you set in your Dockerfile.
docker run -ti -v $PWD:your-container-workdir $IMAGEID_OR_TAG bash
Note if you get an error about bash
not being found then try using /bin/bash
SSH into an already running container.
Useful for debugging a partially running container
or app
or
for tweaking some details of your container
or app
.
Most of the time I use the previous ones since I find them more useful.
docker exec -ti $CONTAINER_ID /bin/bash
Managing#
What are the current running containers
?
docker ps
Kill a running container
(get the $CONTAINER_ID
from the above output).
docker kill $CONTAINER_ID
Check a container
’s logs.
docker logs $CONTAINER_ID
Inspect a container
(debug information about volumes, networks, ports, etc about a given container).
In this case we get the container
’s IP Address.
docker inspect \
--format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' \
$CONTAINER_ID
Check the docs for a more complete coverage.
Closing#
This is the basic knowledge I think every Front End developer should have as a minimum, given the fact that Docker has such a wide adoption. The commands are commands I use on a daily basis in my day job.
Even if you are not a Front End developer but simply new to this technology I think you will be able to take value (and pride) from learning the basics.
There is a lot to learn about Docker and you can base your whole career specializing on Docker and its parent discipline: DevOps.