Root /ArchiveAbout
()

Docker: Writing The Basic Dockerfile

Docker: Writing The Basic Dockerfile
Docker: Writing The Basic Dockerfile

Docker is an environment to run small isolated virtual machines called containers.

Container are stateless, which means they don't store the state of the application or data created during the runtime. Any changes made to filesystem within a container are temporary and will be lost forever if you stop, restart or remove container! Stateless design ensures that containers are lightweight, portable, and self-sufficient and you can start or move between servers without affecting the application's functionality or causing any discrepancies in data.

Container should be run from a snapshot of working application with all the environment, dependencies and application itself included in it. This snapshot has name of docker image. Script containing steps required to build a Docker container image called Dockerfile. The script might operate a very limited set of commands.

List of available Dockerfile commands:

  1. FROM: Specifies the base image to build the new container image from.
  2. RUN: Executes commands in a new layer on top of the current image and commits the results.
  3. COPY: Copies files or directories from the host system into the container.
  4. ADD: Similar to COPY, but also allows fetching remote files or unpacking archives directly into the image.
  5. CMD: Specifies the default command to run when the container is started.
  6. ENTRYPOINT: Specifies the command that will always be executed when the container starts, regardless of the CMD instruction.
  7. ENV: Sets environment variables in the container.
  8. EXPOSE: Indicates the network ports the container will listen on at runtime.
  9. VOLUME: Creates a mount point in the container for persistent or shared data.
  10. WORKDIR: Sets the working directory for subsequent instructions in the Dockerfile.

To build a Docker container image from a Dockerfile, you use the docker build command followed by a build context (usually a local directory containing the Dockerfile). Once the image is built, you can run containers from it using the docker run command.

Creating a basic Dockerfile

Lets create a basic Docker image with NodeJS. First create a file called Dockerfile (without extension). Open it with any text editor (I always recommend VSCode) and place the following:

# Use the latest official Node.js runtime as a parent image
FROM node:latest

# Set the working directory to /app
WORKDIR /app

# Copy the package.json file into the container at /app
COPY package.json /app

# Install any needed packages specified in package.json
RUN npm install

# Copy the rest of the application code into the container at /app
COPY . /app

# Make port 3000 available to the world outside this container
EXPOSE 3000

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

Save the file and try to build the image:

docker build -t your-image-name .

You should have an error:

docker build -t your-image-name .
[+] Building 1.7s (8/10)

 => [internal] load build definition from Dockerfile
 => [internal] load metadata for docker.io/library/node:latest
 => [auth] library/node:pull token for registry-1.docker.io
 => [internal] load build context
 => => transferring context: 539B
 => [1/5] FROM docker.io/library/node:latest@sha256:242d81ad2a91353ac3a5ed3598582acb4a9a7761b16c60524b949a1603707848
 => => sha256:4e271ab266a4b76874c8e7c8aa431a8dbe636af19fa5e76403e3acbd9b8d3450 2.21kB / 2.21kB
 => CACHED [2/5] WORKDIR /app
 => ERROR [3/5] COPY package.json /app

 > [3/5] COPY package.json /app:
failed to compute cache key: "/package.json" not found: not found

This is because we don't have package.json file in our project. Let's create missing files:

{
  "name": "node-app",
  "version": "1.0.0",
  "description": "A simple Node.js app",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "^4.17.1"
  }
}
const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(port, () => {
  console.log(`Node app listening at http://localhost:${port}`);
});

Now try to build the image again:

docker build -t your-image-name .

You should see something like this:

$ docker build -t your-image-name .

[+] Building 53.0s (11/11) FINISHED
 => [internal] load build definition from Dockerfile
 => => transferring dockerfile: 545B
 => [internal] load .dockerignore
 => => transferring context: 2B
 => [internal] load metadata for docker.io/library/node:latest
 => [auth] library/node:pull token for registry-1.docker.io
 => [1/5] FROM docker.io/library/node:latest@sha256:d9fd72fa4cc310fd57cf5febd50be1304375b4f7b2945784127cad85c7cd1100
 => => resolve docker.io/library/node:latest@sha256:d9fd72fa4cc310fd57cf5febd50be1304375b4f7b2945784127cad85c7cd1100
 => => sha256:d9fd72fa4cc310fd57cf5febd50be1304375b4f7b2945784127cad85c7cd1100 1.21kB / 1.21kB
 => => sha256:7dd206bea61ff3e3b54be1c20b58d8475ddd6f89df176146ddb7a2fd2c747ea2 24.03MB / 24.03MB
 => => sha256:980cf5b26237081e7e05515f385cd8eb270cf41fd13538a146c473040b10abd8 2.00kB / 2.00kB
 => => sha256:250e9c100ea2ad5745b8c69edef0c3dcf9b2cbb055fb7dbd4fe7fb3f9fa45bae 7.24kB / 7.24kB
 => => sha256:d52e4f012db158bb7c0fe215b98af1facaddcbaee530efd69b1bae07d597b711 49.55MB / 49.55MB
 => => sha256:2320f9be4a9c605d1ac847cf67cec42b91484a7cf7c94996417a0c7c316deadc 64.11MB / 64.11MB
 => => sha256:6e5565e0ba8dfce32b9049f21ceeb212946e0bb810d94cbd2db94ca61082f657 211.00MB / 211.00MB
 => => sha256:5f1526a28cf91707a0af45c5624979f67215b1330efd66c05a0f94839c96f0bc 3.37kB / 3.37kB
 => => extracting sha256:d52e4f012db158bb7c0fe215b98af1facaddcbaee530efd69b1bae07d597b711
 => => sha256:d89b7e3d6e3496e51a2a10dfea2cbf36ba8501b95bb374489b955e39e4d9ffdc 47.52MB / 47.52MB
 => => extracting sha256:7dd206bea61ff3e3b54be1c20b58d8475ddd6f89df176146ddb7a2fd2c747ea2
 => => extracting sha256:2320f9be4a9c605d1ac847cf67cec42b91484a7cf7c94996417a0c7c316deadc
 => => sha256:8831f71a931426419aadda667fd199559851d94658edf73c637801f675fbc463 2.27MB / 2.27MB
 => => sha256:beef33e6bd26ef8d9ecfb29760a482aab3272d855db9e4d0c0fd4131d54ef827 449B / 449B
 => => extracting sha256:6e5565e0ba8dfce32b9049f21ceeb212946e0bb810d94cbd2db94ca61082f657
 => => extracting sha256:5f1526a28cf91707a0af45c5624979f67215b1330efd66c05a0f94839c96f0bc
 => => extracting sha256:d89b7e3d6e3496e51a2a10dfea2cbf36ba8501b95bb374489b955e39e4d9ffdc
 => => extracting sha256:8831f71a931426419aadda667fd199559851d94658edf73c637801f675fbc463
 => => extracting sha256:beef33e6bd26ef8d9ecfb29760a482aab3272d855db9e4d0c0fd4131d54ef827
 => [internal] load build context
 => => transferring context: 1.08kB
 => [2/5] WORKDIR /app
 => [3/5] COPY package.json /app
 => [4/5] RUN npm install
 => [5/5] COPY . /app
 => exporting to image
 => => exporting layers
 => => writing image sha256:b2e188b4e3b71679c15312e4aa16224c0e1af7881b9f9e07e8b58a6dc228c9d0
 => => naming to docker.io/library/your-image-name

This app.js file creates a simple Express web application with a single route that returns a "Hello World!" message. The application listens on port 3000.

Running the container

Now that you have a Docker image, you can run it as a container. To do this, you use the docker run command. The -p option maps port 3000 in the container to port 3000 on the host system. The -d option runs the container in detached mode, leaving the container running in the background. The --name option assigns a name to the container. The name is used in subsequent commands to refer to the container.

docker run -p 3000:3000 -d --name my-app your-image-name

You can see the running container by using the docker ps command:

docker ps

If you visit http://localhost:3000 in a web browser, you should see the "Hello World!" message.

You can see the logs from the container by using the docker logs command:

docker logs my-app

You can stop the container by using the docker stop command:

docker stop my-app

You can remove the container by using the docker rm command:

docker rm my-app

You can remove the image by using the docker rmi command:

docker rmi your-image-name

Conclusion

In this article, you learned how to create a Docker image from a Dockerfile and run a container from it. You also learned how to stop and remove the container and image.