CodeAlchemy

Jotting one man's journey through software development, programming, and technology


Project maintained by pablogarciaprado Hosted on GitHub Pages — Theme by mattgraham

◀️ Home

Docker

Hello world

docker run hello-world

This simple container returns Hello from Docker! to your screen. While the command is simple, notice in the output the number of steps it performed. The Docker daemon searched for the hello-world image, didn’t find the image locally, pulled the image from a public registry called Docker Hub, created a container from that image, and ran the container for you.

(Command Output):

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
9db2ca6ccae0: Pull complete
Digest: sha256:4b8ff392a12ed9ea17784bd3c9a8b1fa3299cac44aca35a85c90c5e3c7afacdc
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.
...

Run the following command to take a look at the container image it pulled from Docker Hub:

docker images

(Command Output):

REPOSITORY     TAG      IMAGE ID       CREATED       SIZE
hello-world   latest    feb5d9fea6a5   14 months ago   13.3kB

This is the image pulled from the Docker Hub public registry. The Image ID is in SHA256 hash format—this field specifies the Docker image that’s been provisioned. When the Docker daemon can’t find an image locally, it will by default search the public registry for the image.

If you run docker run hello-worldagain, the Docker daemon finds the image in your local registry and runs the container from that image. It doesn’t have to pull the image from Docker Hub.

You can look at the running containers by running the following command:

docker ps

In order to see all containers, including ones that have finished executing, run:

docker ps -a

This shows you the Container ID, a UUID generated by Docker to identify the container, and more metadata about the run. The container Names are also randomly generated but can be specified with docker run --name [container-name] hello-world.

Build

How to build a Docker image that’s based on a simple node application.

Execute the following command to create and switch into a folder named test

mkdir test && cd test

This is a Dockerfile example:

# Use an official Node runtime as the parent image
FROM node:lts

# Set the working directory in the container to /app
WORKDIR /app

# Copy the current directory contents into the container at /app
ADD . /app

# Make the container's port 80 available to the outside world
EXPOSE 80

# Run app.js using node when the container launches
CMD ["node", "app.js"]

This file instructs the Docker daemon on how to build your image.

Note: Spend some time reviewing the Dockerfile command references to understand each line of the Dockerfile. In Unix/Linux terminology, a daemon is a background process that starts at boot or on demand and runs without direct user interaction. Think of it like a butler — always running in the background, ready to fulfill your requests.

The following is a node application example. A simple HTTP server that listens on port 80 and returns “Hello World”.

const http = require("http");

const hostname = "0.0.0.0";
const port = 80;

const server = http.createServer((req, res) => {
	res.statusCode = 200;
	res.setHeader("Content-Type", "text/plain");
	res.end("Hello World\n");
});

server.listen(port, hostname, () => {
	console.log("Server running at http://%s:%s/", hostname, port);
});

process.on("SIGINT", function () {
	console.log("Caught interrupt signal and will exit");
	process.exit();
});

Next, we build the image:

docker build -t node-app:0.1 .

The “.” means current directory, so you need to run this command from within the directory that has the Dockerfile

The -t is to name and tag an image with the name:tag syntax. The name of the image is node-app and the tag is 0.1. The tag is highly recommended when building Docker images. If you don’t specify a tag, the tag will default to latest and it becomes more difficult to distinguish newer images from older ones.

Run

Use this code to run containers based on the image we built:

docker run -p 4000:80 --name my-app node-app:0.1

The --name flag allows you to name the container if you like. The -p instructs Docker to map the host’s port 4000 to the container’s port 80. Now you can reach the server at http://localhost:4000. Without port mapping, you would not be able to reach the container at localhost.

To test the server:

curl http://localhost:4000

The container will run as long as the initial terminal is running. If you want the container to run in the background (not tied to the terminal’s session), you need to specify the -d flag.

Close the initial terminal and then run the following command to stop and remove the container:

docker stop my-app && docker rm my-app

Now run the following command to start the container in the background:

docker run -p 4000:80 --name my-app -d node-app:0.1
docker ps

Notice the container is running in the output. You can look at the logs by executing docker logs [container_id].

You don’t have to write the entire container ID, as long as the initial characters uniquely identify the container. For example, you can execute docker logs 17b if the container ID is 17bcaca6f....

Debug

Let’s go over some debugging practices. If you want to follow the log’s output as the container is running, use the -f option.

docker logs -f [container_id]

Sometimes you will want to start an interactive Bash session inside the running container. You can use docker exec to do this.

docker exec -it [container_id] bash

The -it flags let you interact with a container by allocating a pseudo-tty and keeping stdin open. Notice bash ran in the WORKDIR directory (/app) specified in the Dockerfile. From here, you have an interactive shell session inside the container to debug.

(Command Output)

root@xxxxxxxxxxxx:/app#

To exit the Bash session:

exit

To can examine a container’s metadata in Docker by using:

docker inspect [container_id]

(Command Output)

[
    {
        "Id": "xxxxxxxxxxxx....",
        "Created": "2017-08-07T22:57:49.261726726Z",
        "Path": "node",
        "Args": [
            "app.js"
        ],
...

Use --format to inspect specific fields from the returned JSON. For example:

docker inspect --format='' [container_id]

Publish

Now we’re going to push the image to the Google Artifact Registry. To push images to your private registry hosted by Artifact Registry, you need to tag the images with a registry name. The format is <regional-repository>-docker.pkg.dev/my-project/my-repo/my-image.

You must create a repository before you can push any images to it. Pushing an image can’t trigger creation of a repository and the Cloud Build service account does not have permissions to create repositories.

gcloud artifacts repositories create my-repository --repository-format=docker --location="REGION" --description="Docker repository"

Before you can push or pull images, configure Docker to use the Google Cloud CLI to authenticate requests to Artifact Registry. To set up authentication to Docker repositories in the region REGION, run the following command in Cloud Shell:

gcloud auth configure-docker "REGION"-docker.pkg.dev

The command updates your Docker configuration. You can now connect with Artifact Registry in your Google Cloud project to push and pull images.

Let’s push the container to Artifact Registry. Change into the directory with your Dockerfile. Run the command to tag node-app:0.2

docker build -t "REGION"-docker.pkg.dev/"PROJECT_ID"/my-repository/node-app:0.2 .

And push the image to Artifact Registry.

docker push "REGION"-docker.pkg.dev/"PROJECT_ID"/my-repository/node-app:0.2

Test

We pull the image and run it.

docker run -p 4000:80 -d "REGION"-docker.pkg.dev/"PROJECT_ID"/my-repository/node-app:0.2

And run a curl against the running container.

curl http://localhost:4000