Skip to content

Commit aff13f8

Browse files
committed
part 12 clarifications
1 parent b9dbdb4 commit aff13f8

File tree

2 files changed

+81
-36
lines changed

2 files changed

+81
-36
lines changed

src/content/12/en/part12a.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,13 @@ The material is built around [Docker](https://www.docker.com/), a set of product
8585

8686
As the install instructions depend on your operating system, you will have to find the correct install instructions from the link below. Note that they may have multiple different options for your operating system.
8787

88-
8988
- [Get Docker](https://docs.docker.com/get-docker/)
9089

9190
Now that that headache is hopefully over, let's make sure that our versions match. Yours may have a bit higher numbers than here:
9291

9392
```bash
9493
$ docker -v
95-
Docker version 20.10.5, build 55c4c88
94+
Docker version 25.0.3, build 4debf41
9695
```
9796

9897
### Containers and images
@@ -152,7 +151,7 @@ The second row shows the organisation name, "library" where it will get the imag
152151

153152
The 3rd and 5th rows only show the status. But the 4th row may be interesting: each image has a unique digest based on the <i>layers</i> from which the image is built. In practice, each step or command that was used in building the image creates a unique layer. The digest is used by Docker to identify that an image is the same. This is done when you try to pull the same image again.
154153

155-
So the result of using the command was a pull and then output information about the **image**. After that, the status told us that a new version of hello-world:latest was indeed downloaded. You can try pulling the image with _docker image pull hello-world_ and see what happens.
154+
So the result of using the command was a pull and then output information about the **image**. After that, the status told us that a new version of <i>hello-world:latest</i> was indeed downloaded. You can try pulling the image with _docker image pull hello-world_ and see what happens.
156155

157156
The following output was from the container itself. It also explains what happened when we ran _docker container run hello-world_.
158157

@@ -331,13 +330,13 @@ Install Node while inside the container and run the index file with _node /usr/s
331330
The instructions for installing Node are sometimes hard to find, so here is something you can copy-paste:
332331
333332
```bash
334-
curl -sL https://deb.nodesource.com/setup_16.x | bash
333+
curl -sL https://deb.nodesource.com/setup_20.x | bash
335334
apt install -y nodejs
336335
```
337336
338337
You will need to install the _curl_ into the container. It is installed in the same way as you did with _nano_.
339338
340-
After the installation, ensure that you can run your code inside the container with command
339+
After the installation, ensure that you can run your code inside the container with the command
341340
342341
```
343342
root@b8548b9faec3:/# node /usr/src/app/index.js
@@ -397,7 +396,7 @@ Next, let's skip installing Node altogether. There are plenty of useful Docker i
397396
By the way, the _container run_ accepts _--name_ flag that we can use to give a name for the container.
398397
399398
```bash
400-
$ docker container run -it --name hello-node node:16 bash
399+
$ docker container run -it --name hello-node node:20 bash
401400
```
402401
403402
Let us create a directory for the code inside the container:

src/content/12/en/part12b.md

Lines changed: 76 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ If you did not already, create a directory on your machine and create a file cal
2323

2424
inside that Dockerfile we will tell the image three things:
2525

26-
- Use the node:16 as the base for our image
26+
- Use the [node:20](https://hub.docker.com/_/node) as the base for our image
2727
- Include the index.js inside the image, so we don't need to manually copy it into the container
2828
- When we run a container from the image, use Node to execute the index.js file.
2929

@@ -32,7 +32,7 @@ The wishes above will translate into a basic Dockerfile. The best location to pl
3232
The resulting <i>Dockerfile</i> looks like this:
3333

3434
```Dockerfile
35-
FROM node:16
35+
FROM node:20
3636

3737
WORKDIR /usr/src/app
3838

@@ -41,11 +41,11 @@ COPY ./index.js ./index.js
4141
CMD node index.js
4242
```
4343

44-
FROM instruction will tell Docker that the base for the image should be node:16. COPY instruction will copy the file <i>index.js</i> from the host machine to the file with the same name in the image. CMD instruction tells what happens when _docker run_ is used. CMD is the default command that can then be overwritten with the parameter given after the image name. See _docker run --help_ if you forgot.
44+
FROM instruction will tell Docker that the base for the image should be node:20. COPY instruction will copy the file <i>index.js</i> from the host machine to the file with the same name in the image. CMD instruction tells what happens when _docker run_ is used. CMD is the default command that can then be overwritten with the parameter given after the image name. See _docker run --help_ if you forgot.
4545

4646
The WORKDIR instruction was slipped in to ensure we don't interfere with the contents of the image. It will guarantee all of the following commands will have <i>/usr/src/app</i> set as the working directory. If the directory doesn't exist in the base image, it will be automatically created.
4747

48-
If we do not specify a WORKDIR, we risk overwriting important files by accident. If you check the root (_/_) of the node:16 image with _docker run node:16 ls_, you can notice all of the directories and files that are already included in the image.
48+
If we do not specify a WORKDIR, we risk overwriting important files by accident. If you check the root (_/_) of the node:20 image with _docker run node:20 ls_, you can notice all of the directories and files that are already included in the image.
4949

5050
Now we can use the command _docker build_ to build an image based on the Dockerfile. Let's spice up the command with one additional flag: _-t_, this will help us name the image:
5151

@@ -55,7 +55,7 @@ $ docker build -t fs-hello-world .
5555
...
5656
```
5757

58-
So the result is "Docker please build with tag (you may think the tag to be the name of the resulting image) fs-hello-world the Dockerfile in this directory". You can point to any Dockerfile, but in our case, a simple dot will mean the Dockerfile in <i>this</i> directory. That is why the command ends with a period. After the build is finished, you can run it with _docker run fs-hello-world_:
58+
So the result is "Docker please build with tag (you may think the tag to be the name of the resulting image) <i>fs-hello-world</i> the Dockerfile in this directory". You can point to any Dockerfile, but in our case, a simple dot will mean the Dockerfile in <i>this</i> directory. That is why the command ends with a period. After the build is finished, you can run it with _docker run fs-hello-world_:
5959

6060
```bash
6161
$ docker run fs-hello-world
@@ -109,7 +109,7 @@ Containerizing that should be relatively easy based on the previous example.
109109
Let's place the following Dockerfile at the root of the project:
110110

111111
```Dockerfile
112-
FROM node:16
112+
FROM node:20
113113

114114
WORKDIR /usr/src/app
115115

@@ -118,24 +118,22 @@ COPY . .
118118
CMD DEBUG=playground:* npm start
119119
```
120120

121-
Let's build the image from the Dockerfile with a command, _docker build -t express-server ._ and run it with _docker run -p 3123:3000 express-server_. The _-p_ flag will inform Docker that a port from the host machine should be opened and directed to a port in the container. The format for is _-p host-port:application-port_.
121+
Let's build the image from the Dockerfile and then run it:
122122

123123
```bash
124-
$ docker run -p 3123:3000 express-server
125-
126-
127-
> node ./bin/www
128-
129-
Tue, 29 Jun 2021 10:55:10 GMT playground:server Listening on port 3000
124+
docker build -t express-server .
125+
docker run -p 3123:3000 express-server
130126
```
131127

132-
> If yours doesn't work, skip to the next section. There is an explanation why it may not work even if you followed the steps correctly.
128+
The _-p_ flag in the run command will inform Docker that a port from the host machine should be opened and directed to a port in the container. The format for is _-p host-port:application-port_.
133129

134130
The application is now running! Let's test it by sending a GET request to [http://localhost:3123/](http://localhost:3123/).
135131

136-
Shutting it down is a headache at the moment. Use another terminal and _docker kill_ command to kill the application. The _docker kill_ will send a kill signal (SIGKILL) to the application to force it to shut down. It needs the name or id of the container as an argument.
132+
> If yours doesn't work, skip to the next section. There is an explanation why it may not work even if you followed the steps correctly.
133+
134+
Shutting the app down is a headache at the moment. Use another terminal and _docker kill_ command to kill the application. The _docker kill_ will send a kill signal (SIGKILL) to the application to force it to shut down. It needs the name or the id of the container as an argument.
137135

138-
By the way, when using id as the argument, the beginning of the ID is enough for Docker to know which container we mean.
136+
By the way, when using the id as the argument, the beginning of the ID is enough for Docker to know which container we mean.
139137

140138
```bash
141139
$ docker container ls
@@ -165,10 +163,10 @@ node_modules
165163
Dockerfile
166164
```
167165

168-
However, in our case the .dockerignore isn't the only thing required. We will need to install the dependencies during the build step. The _Dockerfile_ changes to:
166+
However, in our case, the .dockerignore isn't the only thing required. We will need to install the dependencies during the build step. The _Dockerfile_ changes to:
169167

170168
```Dockerfile
171-
FROM node:16
169+
FROM node:20
172170

173171
WORKDIR /usr/src/app
174172

@@ -194,7 +192,7 @@ So in short: _ci_ creates reliable builds, while _install_ is the one to use whe
194192
As we are not installing anything new during the build step, and we don't want the versions to suddenly change, we will use _ci_:
195193

196194
```Dockerfile
197-
FROM node:16
195+
FROM node:20
198196

199197
WORKDIR /usr/src/app
200198

@@ -216,7 +214,7 @@ Now the Dockerfile should work again, try it with _docker build -t express-serve
216214
We set an environment variable _DEBUG=playground:*_ during CMD for the npm start. However, with Dockerfiles we could also use the instruction ENV to set environment variables. Let's do that:
217215

218216
```Dockerfile
219-
FROM node:16
217+
FROM node:20
220218

221219
WORKDIR /usr/src/app
222220

@@ -245,7 +243,7 @@ Snyk has a great list of 10 best practices for Node/Express containerization. Re
245243
One big carelessness we have left is running the application as root instead of using a user with lower privileges. Let's do a final fix to the Dockerfile:
246244

247245
```Dockerfile
248-
FROM node:16
246+
FROM node:20
249247

250248
WORKDIR /usr/src/app
251249

@@ -270,9 +268,9 @@ CMD npm start
270268

271269
The repository you cloned or copied in the [first exercise](/en/part12/introduction_to_containers#exercise-12-1) contains a todo-app. See the todo-app/todo-backend and read through the README. We will not touch the todo-frontend yet.
272270

273-
Step 1. Containerize the todo-backend by creating a <i>todo-app/todo-backend/Dockerfile</i> and building an image.
271+
- Step 1. Containerize the todo-backend by creating a <i>todo-app/todo-backend/Dockerfile</i> and building an image.
274272

275-
Step 2. Run the todo-backend image with the correct ports open. Make sure the visit counter increases when used through a browser in http://localhost:3000/ (or some other port if you configure so)
273+
- Step 2. Run the todo-backend image with the correct ports open. Make sure the visit counter increases when used through a browser in http://localhost:3000/ (or some other port if you configure so)
276274

277275
Tip: Run the application outside of a container to examine it before starting to containerize.
278276

@@ -363,7 +361,7 @@ You can use _-f_ flag to specify a <i>file</i> to run the Docker Compose command
363361
docker compose -f docker-compose.dev.yml up
364362
```
365363

366-
Now that we may have multiple it's useful.
364+
Now that we may have multiple compose files, it's useful.
367365

368366
Now start the MongoDB with _docker compose -f docker-compose.dev.yml up -d_. With _-d_ it will run it in the background. You can view the output logs with _docker compose -f docker-compose.dev.yml logs -f_. There the _-f_ will ensure we <i>follow</i> the logs.
369367

@@ -450,23 +448,71 @@ you may have a read permission problem. They are not uncommon when dealing with
450448

451449
Now starting the Express application with the correct environment variable should work:
452450

451+
### Still problems?
452+
453+
For some reason, the initialization of Mongo has caused problems for many.
454+
455+
If the app does not work and you still end up with the following error
456+
457+
```bash
458+
/Users/mluukkai/dev/fs-ci-lokakuu/repo/todo-app/todo-backend/node_modules/mongodb/lib/cmap/connection.js:272
459+
callback(new MongoError(document));
460+
^
461+
MongoError: command find requires authentication
462+
at MessageStream.messageHandler (/Users/mluukkai/dev/fs-ci-lokakuu/repo/todo-app/todo-backend/node_modules/mongodb/lib/cmap/connection.js:272:20)
463+
```
464+
465+
run these commands:
466+
453467
```bash
454-
$ MONGO_URL=mongodb://the_username:the_password@localhost:3456/the_database npm run dev
468+
docker compose -f docker-compose.dev.yml down --volumes
469+
docker image rm mongo
455470
```
456471

457-
Let's check that the http://localhost:3000/todos returns all todos. It should return the two todos we initialized. We can and should use Postman to test the basic functionality of the app, such as adding or deleting a todo.
472+
After these, try to start Mongo again.
473+
474+
If the problem persists, let us drop the idea of a volume altogether and copy the initialization script to a custom image. Create the following <i>Dockerfile</i> to the directory <i>todo-app/todo-backend/mongo</i>
475+
476+
```Dockerfile
477+
FROM mongo
478+
479+
COPY ./mongo-init.js /docker-entrypoint-initdb.d/
480+
```
481+
482+
Build it to an image with the command
483+
484+
```bash
485+
docker build -t initialized-mongo .
486+
```
487+
488+
Change now the <i>docker-compose.dev.yml</i> to use the new image:
489+
490+
```yml
491+
mongo:
492+
image: initialized-mongo # highlight-line
493+
ports:
494+
- 3456:27017
495+
environment:
496+
MONGO_INITDB_ROOT_USERNAME: root
497+
MONGO_INITDB_ROOT_PASSWORD: example
498+
MONGO_INITDB_DATABASE: the_database
499+
```
500+
501+
Now the app should finally work.
458502
459503
### Persisting data with volumes
460504
461-
By default, containers are not going to preserve our data. When you close the Mongo container you may or may not be able to get the data back.
505+
By default, database containers are not going to preserve our data. When you close the database container you <i>may or may not</i> be able to get the data back.
462506
463-
This is a rare case in which it does preserve the data as the developers who made the Docker image for Mongo have defined a volume to be used: [https://github.com/docker-library/mongo/blob/cb8a419053858e510fc68ed2d69415b3e50011cb/4.4/Dockerfile#L113](https://github.com/docker-library/mongo/blob/cb8a419053858e510fc68ed2d69415b3e50011cb/4.4/Dockerfile#L113) This line will instruct Docker to preserve the data in those directories.
507+
> Mongo is actually a rare case in which the container indeed does preserve the data. This happens, since the developers who made the Docker image for Mongo have defined a volume to be used. [This line](https://github.com/docker-library/mongo/blob/cb8a419053858e510fc68ed2d69415b3e50011cb/4.4/Dockerfile#L113) in the Dockerfile will instruct Docker to preserve the data in a volume.
464508
465509
There are two distinct methods to store the data:
466510
- Declaring a location in your filesystem (called [bind mount](https://docs.docker.com/storage/bind-mounts/))
467511
- Letting Docker decide where to store the data ([volume](https://docs.docker.com/storage/volumes/))
468512
469-
I prefer the first choice in most cases whenever you <i>really</i> need to avoid deleting the data. Let's see both in action with docker compose:
513+
The first choice is preferable in most cases whenever one <i>really</i> needs to avoid the data being deleted.
514+
515+
Let's see both in action with Docker compose. Let us start with <i>bind mount</i>:
470516
471517
```yml
472518
services:
@@ -485,7 +531,7 @@ services:
485531
486532
The above will create a directory called *mongo\_data* to your local filesystem and map it into the container as _/data/db_. This means the data in _/data/db_ is stored outside of the container but still accessible by the container! Just remember to add the directory to .gitignore.
487533
488-
A similar outcome can be achieved with a named volume:
534+
A similar outcome can be achieved with a <i>named volume</i>:
489535
490536
```yml
491537
services:

0 commit comments

Comments
 (0)