You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/content/12/en/part12a.md
+5-6Lines changed: 5 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -85,14 +85,13 @@ The material is built around [Docker](https://www.docker.com/), a set of product
85
85
86
86
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.
Now that that headache is hopefully over, let's make sure that our versions match. Yours may have a bit higher numbers than here:
92
91
93
92
```bash
94
93
$ docker -v
95
-
Docker version 20.10.5, build 55c4c88
94
+
Docker version 25.0.3, build 4debf41
96
95
```
97
96
98
97
### Containers and images
@@ -152,7 +151,7 @@ The second row shows the organisation name, "library" where it will get the imag
152
151
153
152
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.
154
153
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.
156
155
157
156
The following output was from the container itself. It also explains what happened when we ran _docker container run hello-world_.
158
157
@@ -331,13 +330,13 @@ Install Node while inside the container and run the index file with _node /usr/s
331
330
The instructions for installing Node are sometimes hard to find, so here is something you can copy-paste:
Copy file name to clipboardExpand all lines: src/content/12/en/part12b.md
+76-30Lines changed: 76 additions & 30 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -23,7 +23,7 @@ If you did not already, create a directory on your machine and create a file cal
23
23
24
24
inside that Dockerfile we will tell the image three things:
25
25
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
27
27
- Include the index.js inside the image, so we don't need to manually copy it into the container
28
28
- When we run a container from the image, use Node to execute the index.js file.
29
29
@@ -32,7 +32,7 @@ The wishes above will translate into a basic Dockerfile. The best location to pl
32
32
The resulting <i>Dockerfile</i> looks like this:
33
33
34
34
```Dockerfile
35
-
FROM node:16
35
+
FROM node:20
36
36
37
37
WORKDIR /usr/src/app
38
38
@@ -41,11 +41,11 @@ COPY ./index.js ./index.js
41
41
CMD node index.js
42
42
```
43
43
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.
45
45
46
46
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.
47
47
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.
49
49
50
50
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:
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_:
59
59
60
60
```bash
61
61
$ docker run fs-hello-world
@@ -109,7 +109,7 @@ Containerizing that should be relatively easy based on the previous example.
109
109
Let's place the following Dockerfile at the root of the project:
110
110
111
111
```Dockerfile
112
-
FROM node:16
112
+
FROM node:20
113
113
114
114
WORKDIR /usr/src/app
115
115
@@ -118,24 +118,22 @@ COPY . .
118
118
CMD DEBUG=playground:* npm start
119
119
```
120
120
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:
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
130
126
```
131
127
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_.
133
129
134
130
The application is now running! Let's test it by sending a GET request to [http://localhost:3123/](http://localhost:3123/).
135
131
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.
137
135
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.
139
137
140
138
```bash
141
139
$ docker container ls
@@ -165,10 +163,10 @@ node_modules
165
163
Dockerfile
166
164
```
167
165
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:
169
167
170
168
```Dockerfile
171
-
FROM node:16
169
+
FROM node:20
172
170
173
171
WORKDIR /usr/src/app
174
172
@@ -194,7 +192,7 @@ So in short: _ci_ creates reliable builds, while _install_ is the one to use whe
194
192
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_:
195
193
196
194
```Dockerfile
197
-
FROM node:16
195
+
FROM node:20
198
196
199
197
WORKDIR /usr/src/app
200
198
@@ -216,7 +214,7 @@ Now the Dockerfile should work again, try it with _docker build -t express-serve
216
214
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:
217
215
218
216
```Dockerfile
219
-
FROM node:16
217
+
FROM node:20
220
218
221
219
WORKDIR /usr/src/app
222
220
@@ -245,7 +243,7 @@ Snyk has a great list of 10 best practices for Node/Express containerization. Re
245
243
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:
246
244
247
245
```Dockerfile
248
-
FROM node:16
246
+
FROM node:20
249
247
250
248
WORKDIR /usr/src/app
251
249
@@ -270,9 +268,9 @@ CMD npm start
270
268
271
269
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.
272
270
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.
274
272
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)
276
274
277
275
Tip: Run the application outside of a container to examine it before starting to containerize.
278
276
@@ -363,7 +361,7 @@ You can use _-f_ flag to specify a <i>file</i> to run the Docker Compose command
363
361
docker compose -f docker-compose.dev.yml up
364
362
```
365
363
366
-
Now that we may have multiple it's useful.
364
+
Now that we may have multiple compose files, it's useful.
367
365
368
366
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.
369
367
@@ -450,23 +448,71 @@ you may have a read permission problem. They are not uncommon when dealing with
450
448
451
449
Now starting the Express application with the correct environment variable should work:
452
450
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
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
+
453
467
```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
455
470
```
456
471
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.
458
502
459
503
### Persisting data with volumes
460
504
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.
462
506
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.
464
508
465
509
There are two distinct methods to store the data:
466
510
- Declaring a location in your filesystem (called [bind mount](https://docs.docker.com/storage/bind-mounts/))
467
511
- Letting Docker decide where to store the data ([volume](https://docs.docker.com/storage/volumes/))
468
512
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>:
470
516
471
517
```yml
472
518
services:
@@ -485,7 +531,7 @@ services:
485
531
486
532
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.
487
533
488
-
A similar outcome can be achieved with a named volume:
534
+
A similar outcome can be achieved with a <i>named volume</i>:
0 commit comments