Docker is becoming increasingly popular among software developers as a container platform. Containers package software in a format that can run isolated on a host operating system. Bundled with only essential libraries and settings, Docker renders lightweight, efficient, self-contained systems that run identically wherever deployed. In this cheat sheet, we'll take a use-case oriented approach: building the image, starting it, and in the end, stopping it and cleaning up after ourselves.
Creating your container
To follow along with the commands, I've borrowed an application with its Dockerfile from my repository representing an ideal Node.js Docker workflow.
# Dockerfile.short FROM node:12-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY . . EXPOSE 3000 CMD npm start
The application is an Express web server that responds to incoming HTTP requests.
const express = require('express'); const port = process.env.PORT || 3000; const app = express(); app.get('/', (req, res) => res.send('Hello World!')); app.listen(port, () => console.log(`App listening on port ${port}!`));
We'll build and run this file with the upcoming commands.
docker build -f <docker filename> -t <image name> <build context>
Pulls the base image, builds the image inside Dockerfile.short
, and names it as express
. We can use this name when running the image. The name can contain a tag (express:1
): by default, it gets the latest
tag. Specifying the name of the Dockerfile
is only necessary when it defers from Dockerfile
. The dot at the end tells that the build context is the current directory.
docker build -f Dockerfile.short -t express .
Sending build context to Docker daemon 180.7kB Step 1/7 : FROM node:12-alpine 12-alpine: Pulling from library/node c9b1b535fdd9: Pull complete 750cdd924064: Pull complete 2078ab7cf9df: Pull complete 02f523899354: Pull complete Digest: sha256:e280e51eaa6e626e4df58a5c1f141e453807c30596179330992c55a0bf4114ca Status: Downloaded newer image for node:12-alpine ---> afd897e3184b Step 2/7 : WORKDIR /app ---> Running in c8f379e36c32 Removing intermediate container c8f379e36c32 ---> a11ced1bd480 Step 3/7 : COPY package*.json ./ ---> e811deacf584 Step 4/7 : RUN npm ci --only=production ---> Running in 401bdc088d44 added 50 packages in 1.395s Removing intermediate container 401bdc088d44 ---> 644c8661eff7 Step 5/7 : COPY . . ---> 270057bb701a Step 6/7 : EXPOSE 3000 ---> Running in cd9d70daad58 Removing intermediate container cd9d70daad58 ---> 4c6eb54071d1 Step 7/7 : CMD npm start ---> Running in fc2a7b3e7e11 Removing intermediate container fc2a7b3e7e11 ---> d85b87f880e3 Successfully built d85b87f880e3 Successfully tagged express:latest
docker images
Shows all the runnable images on the host machine. We'll see our application with the name express
.
REPOSITORY TAG IMAGE ID CREATED SIZE express latest d85b87f880e3 3 minutes ago 87.6MB node 12-alpine afd897e3184b 3 days ago 85.2MB
docker run -p <host port>:<container port> <image name>
Runs the built image and connects the containers port with the host machines port (-p 3000:3000
). The result is the application is available on http://localhost:3000
. If we don't want to block the terminal, we can run the image in detached mode -d
. If we're going to have live-reload on file changes, for example, with Nodemon, we can attach the local folder as a volume -v $(pwd):/app
.
docker run -p 3000:3000 -v $(pwd):/app express
docker ps -a
Lists the running containers. After docker run
, we'll see the application here.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ec40c8347a43 express "docker-entrypoint.s…" 6 seconds ago Up 5 seconds 0.0.0.0:3000->3000/tcp hardcore_vaughan
The -a
flag shows all the containers, not just the running ones.
docker logs -f -t <image id>
Shows the standard output with timestamps from the running container.
docker logs -f -t ec40c8347a43
2020-02-10T18:22:07.672710300Z 2020-02-10T18:22:07.672788300Z > node-docker-workflow@1.0.0 start /app 2020-02-10T18:22:07.672825700Z > node src/index.js 2020-02-10T18:22:07.672850600Z 2020-02-10T18:22:07.917358100Z App listening on port 3000!
The -f
flag tells the command to listen for new logs. If all the records are too much, you can restrict it to only some lines --tail 100
.
Publishing the image
Our application is running fine on our local computer, but we can't deploy it anywhere. For deployment, we have to publish it to a repository. For demonstration purposes, we'll use the default DockerHub repository.
docker login
Logs in to the repository, by default to Dockerhub. We need a Dockerhub account before this to work. You can do the registration here.
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one. Username: blacksonic Password: Login Succeeded
docker tag <source image name>:<target tag name> <target image name>:<target tag name>
Names the image with a tag that we will use when uploading the image.
docker tag express:latest blacksonic/express:latest
We have to prefix the image name before uploading it to Dockerhub (blacksonic is my username).
docker push <image name>:<tag name>
Uploads the image to the repository. You can see the uploaded image now on the site https://hub.docker.com/r/blacksonic/express.
docker push blacksonic/express:latest
The push refers to repository [docker.io/blacksonic/express] d93ac2ab321f: Pushed 5216338b40a7: Pushed latest: digest: sha256:8b418f814535e24906fcb412f8e564ced393e4976586d710bbff60b5fdb2d11c size: 1993
Cleaning it up
We've started and pushed our container successfully. It's time to clean up after ourselves.
docker stop <container id>
Stops the running container. The container won't be deleted.
docker stop ec40c8347a43
docker rm <container id>
Deletes the container, you won't be able to start it again.
docker rm ec40c8347a43
docker rmi <image id>
Deletes the image: you'll have to build it again to start it as a container.
docker rmi d85b87f880e3
docker stop $(docker ps -a -q)
Stops all processes from running containers. After this, docker ps -a
will return empty results.
docker rm $(docker ps -a -q)
Removes all the containers.
docker rmi $(docker images -q)
Removes all the images. After this, docker images
will return empty results.
docker image prune --all
Removes all unused images. Unused means no running containers exist based on the image.
Summary
Encountering containerization can be hard for the first time, but can be simplified by knowing the right commands which can tell what's happening. With an example configuration, you can try them, and you realize that developing with containers can be as easy as doing it on your local machine.
Top comments (2)
Awesome, thanks. It's is nice overview for the most used commands.
Just want to mention that some commands are actually the old syntax. Yes, they are shorter but I prefer the newer syntax, since they make more sense to me and are more descriptive. Examples:
Maybe you want to mention that 🆒
Thanks for the feedback, obviously more descriptive than the old ones 👍