1
00:00:02,080 --> 00:00:04,220
To build this custom image,

2
00:00:04,220 --> 00:00:07,280
we need to go to the folder that contains our code.

3
00:00:07,280 --> 00:00:10,350
And in there we need to create a new file.

4
00:00:10,350 --> 00:00:12,480
A file named Dockerfile.

5
00:00:13,710 --> 00:00:17,910
This is a special name which will be identified by Docker.

6
00:00:17,910 --> 00:00:20,320
Now to get the best possible support

7
00:00:20,320 --> 00:00:22,570
for writing such Docker files,

8
00:00:22,570 --> 00:00:25,670
I recommend that in Visual Studio Code,

9
00:00:25,670 --> 00:00:28,840
you go to the extensions area,

10
00:00:28,840 --> 00:00:31,230
and there you search for Docker

11
00:00:31,230 --> 00:00:34,410
and you install this Docker extension

12
00:00:34,410 --> 00:00:36,200
which I already have here.

13
00:00:36,200 --> 00:00:39,180
Make sure you install this Docker extension

14
00:00:39,180 --> 00:00:41,870
because this will help you with writing Docker

15
00:00:41,870 --> 00:00:43,950
called Docker instructions.

16
00:00:43,950 --> 00:00:47,390
It's not a must have but it will make your life easier.

17
00:00:47,390 --> 00:00:50,750
And thereafter you can go back to the explorer view.

18
00:00:50,750 --> 00:00:52,200
So now we got this Dockerfile

19
00:00:53,300 --> 00:00:56,350
and what do we put into this file now?

20
00:00:56,350 --> 00:01:00,750
This file will contain the instructions for Docker

21
00:01:00,750 --> 00:01:05,069
that we wanna execute when we build our own image.

22
00:01:05,069 --> 00:01:09,170
So it contains the setup instructions for our own image.

23
00:01:09,170 --> 00:01:13,890
And typically here you start with the FROM instruction.

24
00:01:13,890 --> 00:01:16,530
All caps FROM.

25
00:01:16,530 --> 00:01:19,980
This allows you to build your image

26
00:01:19,980 --> 00:01:22,377
up on another base image.

27
00:01:22,377 --> 00:01:24,590
And this is what you typically do.

28
00:01:24,590 --> 00:01:25,980
Theoretically of course

29
00:01:25,980 --> 00:01:29,010
you could build a Docker image from scratch,

30
00:01:29,010 --> 00:01:31,070
but you always want some kind

31
00:01:31,070 --> 00:01:33,610
of operating system layer in there,

32
00:01:33,610 --> 00:01:37,920
one some kind of other tool, which your code needs.

33
00:01:37,920 --> 00:01:41,430
So therefore here I wanna build up on the node image.

34
00:01:41,430 --> 00:01:44,263
And I can do this with, from node,

35
00:01:45,140 --> 00:01:47,800
just entering the image name of an image

36
00:01:47,800 --> 00:01:50,420
which either exists on your system,

37
00:01:50,420 --> 00:01:54,370
or under that name on Docker hub.

38
00:01:54,370 --> 00:01:57,150
And this image now exists on Docker hub.

39
00:01:57,150 --> 00:02:00,430
And actually since we already executed a container based

40
00:02:00,430 --> 00:02:03,280
on this image at the moment it also exists

41
00:02:03,280 --> 00:02:06,460
on our local machine because when we ran a container

42
00:02:06,460 --> 00:02:09,539
based on this Docker hub image for the first time,

43
00:02:09,539 --> 00:02:13,120
this image was downloaded and cached locally.

44
00:02:13,120 --> 00:02:15,680
So now this is basically both a local

45
00:02:15,680 --> 00:02:17,590
and a Docker hub image.

46
00:02:17,590 --> 00:02:19,660
The most important thing is though

47
00:02:19,660 --> 00:02:21,270
that it will be recognized.

48
00:02:21,270 --> 00:02:25,470
That node is a name Docker will be able to find

49
00:02:25,470 --> 00:02:28,123
that there will be an image named node.

50
00:02:29,470 --> 00:02:33,460
So now we're telling Docker, hey, in my own image,

51
00:02:33,460 --> 00:02:37,560
I wanna start by pulling in that node image,

52
00:02:37,560 --> 00:02:38,963
and then I wanna continue.

53
00:02:39,870 --> 00:02:43,450
So that's not the first step, as a next step,

54
00:02:43,450 --> 00:02:45,380
we wanna tell Docker

55
00:02:45,380 --> 00:02:49,710
which files that live here on our local machine

56
00:02:49,710 --> 00:02:51,820
should go into the image.

57
00:02:51,820 --> 00:02:55,080
And for Docker get to copy command.

58
00:02:55,080 --> 00:02:58,710
And here a very simple instruction we could execute,

59
00:02:58,710 --> 00:03:01,530
is copy.dot.

60
00:03:01,530 --> 00:03:03,210
Now what does this mean?

61
00:03:03,210 --> 00:03:06,750
You basically specify two paths here.

62
00:03:06,750 --> 00:03:10,620
The first path is to path outside of the container,

63
00:03:10,620 --> 00:03:13,920
outside of the image where the files leave

64
00:03:13,920 --> 00:03:16,003
that should be copied into the image.

65
00:03:16,860 --> 00:03:19,300
And if you just specify a dot here

66
00:03:19,300 --> 00:03:22,800
that basically tells Docker that it's the same folder

67
00:03:22,800 --> 00:03:25,180
that contains the Dockerfile.

68
00:03:25,180 --> 00:03:27,090
Excluding the Dockerfile though.

69
00:03:27,090 --> 00:03:29,630
So in this case dot would tell Docker

70
00:03:29,630 --> 00:03:32,270
this first dot would tell Docker

71
00:03:32,270 --> 00:03:35,340
that all the folders, sub folders

72
00:03:35,340 --> 00:03:39,323
and files here in this project should be copied into DMH.

73
00:03:40,241 --> 00:03:44,690
And now the second dot is the path inside of the image

74
00:03:44,690 --> 00:03:46,833
where those files should be stored.

75
00:03:48,030 --> 00:03:50,810
Every image and for also every container

76
00:03:50,810 --> 00:03:52,730
created based on an image,

77
00:03:52,730 --> 00:03:55,920
has its own internal file system

78
00:03:55,920 --> 00:03:57,500
which is totally detached

79
00:03:57,500 --> 00:04:00,520
from your file system on your machine.

80
00:04:00,520 --> 00:04:04,530
It's hidden away inside of the Docker container.

81
00:04:04,530 --> 00:04:07,280
And actually here it is a good idea to not use

82
00:04:07,280 --> 00:04:12,280
the root folder, the root entry in your Docker container

83
00:04:12,350 --> 00:04:15,480
but some sub folder which is totally up to you.

84
00:04:15,480 --> 00:04:17,149
You can name it however you want.

85
00:04:17,149 --> 00:04:20,700
And here I will name this slash app.

86
00:04:20,700 --> 00:04:25,700
Now all the files here in the same folder as the Dockerfile

87
00:04:25,860 --> 00:04:28,410
and all the sub folders there as well,

88
00:04:28,410 --> 00:04:33,170
will be copied into an app folder inside of the container.

89
00:04:33,170 --> 00:04:35,830
And this folder will simply be created

90
00:04:35,830 --> 00:04:39,043
in the image and container if it doesn't exist yet.

91
00:04:40,050 --> 00:04:41,773
So that's one key step.

92
00:04:43,020 --> 00:04:47,920
Now, as a next step, we need to run NPM install, right?

93
00:04:47,920 --> 00:04:49,660
Because that is what we had to do

94
00:04:49,660 --> 00:04:51,560
outside of the container as well.

95
00:04:51,560 --> 00:04:56,020
For node applications, we had to run NPM install

96
00:04:56,020 --> 00:04:59,080
in order to install all the dependencies

97
00:04:59,080 --> 00:05:00,803
of our node application.

98
00:05:01,930 --> 00:05:04,060
And you also have an instruction for that,

99
00:05:04,060 --> 00:05:05,610
which you can give to Docker.

100
00:05:05,610 --> 00:05:09,520
You can tell it to after copying all the local files

101
00:05:09,520 --> 00:05:14,520
into the image, you want to run a command in the image.

102
00:05:14,670 --> 00:05:17,063
In this case, NPM install.

103
00:05:18,040 --> 00:05:20,330
However, there is a gotcha here.

104
00:05:20,330 --> 00:05:23,260
By default, all those commands

105
00:05:23,260 --> 00:05:26,880
will be executed in the working directory

106
00:05:26,880 --> 00:05:29,540
off your Docker container and image.

107
00:05:29,540 --> 00:05:34,060
And by default, that working directory is the root folder

108
00:05:34,060 --> 00:05:36,750
in that container file system.

109
00:05:36,750 --> 00:05:40,400
Since I'm copying my code into the app folder here

110
00:05:40,400 --> 00:05:42,770
I actually want to run NPM install

111
00:05:42,770 --> 00:05:45,700
inside of the app folder as well.

112
00:05:45,700 --> 00:05:47,550
And a convenient way of telling Docker

113
00:05:47,550 --> 00:05:51,290
that all commands should be executed in that folder,

114
00:05:51,290 --> 00:05:55,270
is that you set another instruction here

115
00:05:55,270 --> 00:05:57,210
before you copy everything.

116
00:05:57,210 --> 00:06:00,210
And that's the workdir instruction

117
00:06:00,210 --> 00:06:03,820
for a setting the working directory of the Docker container

118
00:06:03,820 --> 00:06:06,400
and setting this to slash app.

119
00:06:06,400 --> 00:06:09,760
And this tells Docker that all the subsequent commands

120
00:06:09,760 --> 00:06:13,920
will be executed from inside that folder.

121
00:06:13,920 --> 00:06:16,130
Which makes sense because that is

122
00:06:16,130 --> 00:06:18,170
where we will have our code later.

123
00:06:18,170 --> 00:06:23,170
And now as a side node, given the fact that we now did set

124
00:06:23,400 --> 00:06:26,170
the working directory to slash app,

125
00:06:26,170 --> 00:06:29,810
we could also change copy to copy

126
00:06:29,810 --> 00:06:33,410
everything from the path to Docker file is in,

127
00:06:33,410 --> 00:06:37,670
to just dot or dot slash which basically means

128
00:06:37,670 --> 00:06:40,680
due to current working directory

129
00:06:40,680 --> 00:06:43,320
inside of our Docker container.

130
00:06:43,320 --> 00:06:47,310
Since we changed that working directory to slash app

131
00:06:47,310 --> 00:06:52,310
not just to run, but also copy will execute relative

132
00:06:52,480 --> 00:06:54,280
to this working directory.

133
00:06:54,280 --> 00:06:58,810
So now inside of the container internal file system

134
00:06:58,810 --> 00:07:03,810
this relative path now points at slash app.

135
00:07:03,860 --> 00:07:05,720
But we can also be more explicit here

136
00:07:05,720 --> 00:07:10,510
and set this to the absolute slash app path like this.

137
00:07:10,510 --> 00:07:14,240
And I'm a fan of doing that since this makes it very clear

138
00:07:14,240 --> 00:07:16,740
where we're going to copy our files,

139
00:07:16,740 --> 00:07:20,350
and we don't have to guess or look into that file

140
00:07:20,350 --> 00:07:24,020
to see what the current working directory is.

141
00:07:24,020 --> 00:07:26,060
Of course that is a simple file.

142
00:07:26,060 --> 00:07:28,500
It's easy to see what the workdir is,

143
00:07:28,500 --> 00:07:30,560
but if this would be a more complex file,

144
00:07:30,560 --> 00:07:31,990
it could be harder.

145
00:07:31,990 --> 00:07:34,160
And therefore I personally prefer

146
00:07:34,160 --> 00:07:37,260
setting this to slash app here as well.

147
00:07:37,260 --> 00:07:38,260
Now with that,

148
00:07:38,260 --> 00:07:41,120
we have a lot of important setup instructions.

149
00:07:41,120 --> 00:07:45,130
The last instruction is that when all of that is done,

150
00:07:45,130 --> 00:07:48,360
we want to start our server.

151
00:07:48,360 --> 00:07:51,290
For that we could add run node server

152
00:07:51,290 --> 00:07:55,130
but this would actually be incorrect node server.js.

153
00:07:55,130 --> 00:07:58,210
This would be incorrect because this would be executed

154
00:07:58,210 --> 00:08:01,610
whenever this image is being built.

155
00:08:01,610 --> 00:08:05,270
All these here are instructions to Docker

156
00:08:05,270 --> 00:08:07,480
for setting up the image.

157
00:08:07,480 --> 00:08:11,100
Now keep in mind the image should be the template

158
00:08:11,100 --> 00:08:12,270
for the container.

159
00:08:12,270 --> 00:08:14,960
The image is not what you run in the end,

160
00:08:14,960 --> 00:08:18,790
you run a container based on an image.

161
00:08:18,790 --> 00:08:21,310
And therefore with this command,

162
00:08:21,310 --> 00:08:24,760
we would try to start the server in the image,

163
00:08:24,760 --> 00:08:27,950
so in the template, but that's not what we want.

164
00:08:27,950 --> 00:08:31,200
We want to install all the dependencies there, yes,

165
00:08:31,200 --> 00:08:33,960
we want to have all the code in there, yes,

166
00:08:33,960 --> 00:08:36,580
but we only wanna start a server

167
00:08:36,580 --> 00:08:40,260
if we start a container based on an image.

168
00:08:40,260 --> 00:08:44,020
Also so that if we start multiple containers

169
00:08:44,020 --> 00:08:46,260
on one and the same image,

170
00:08:46,260 --> 00:08:49,410
we also start multiple node servers.

171
00:08:49,410 --> 00:08:51,870
So therefore here we have another instruction

172
00:08:51,870 --> 00:08:55,443
and that's the CMD instruction, which stands for a command.

173
00:08:56,310 --> 00:09:00,550
The difference to run is that this will now not be executed

174
00:09:00,550 --> 00:09:02,540
when the image is created,

175
00:09:02,540 --> 00:09:06,563
but when a container is started based on the image.

176
00:09:07,450 --> 00:09:08,560
And that's what we want.

177
00:09:08,560 --> 00:09:11,483
Then we want to run our node server.

178
00:09:12,830 --> 00:09:16,550
However, for CMD, the syntax is a bit differently.

179
00:09:16,550 --> 00:09:20,170
Here we wanna pass an array you could say,

180
00:09:20,170 --> 00:09:22,500
and then we have two strings in there

181
00:09:22,500 --> 00:09:24,943
where we split our command like this.

182
00:09:25,940 --> 00:09:28,510
So that's how we now tell Docker

183
00:09:28,510 --> 00:09:30,490
that whenever a container is created

184
00:09:30,490 --> 00:09:33,840
based on that image, we use the node command,

185
00:09:33,840 --> 00:09:37,040
which exists inside of that container

186
00:09:37,040 --> 00:09:39,043
to run the server js file.

187
00:09:39,910 --> 00:09:42,290
Now if we want to try to run it like this,

188
00:09:42,290 --> 00:09:45,650
we would not be able to see our application though

189
00:09:45,650 --> 00:09:47,980
for one key reason.

190
00:09:47,980 --> 00:09:52,980
This node web server listens on port 80.

191
00:09:53,060 --> 00:09:57,160
And I mentioned and emphasized multiple times already

192
00:09:57,160 --> 00:09:59,780
that a Docker container is isolated.

193
00:09:59,780 --> 00:10:03,460
It's isolated from our local environment.

194
00:10:03,460 --> 00:10:08,250
And as a result, it also has its own internal network.

195
00:10:08,250 --> 00:10:10,890
And when we listen to port 80

196
00:10:10,890 --> 00:10:14,290
in the node application inside of our container,

197
00:10:14,290 --> 00:10:17,650
the container does not expose that port

198
00:10:17,650 --> 00:10:19,760
to our local machine.

199
00:10:19,760 --> 00:10:22,410
So we won't be able to listen on the port

200
00:10:22,410 --> 00:10:27,140
just because something's listening inside of a container.

201
00:10:27,140 --> 00:10:30,980
Therefore in the Docker file after setting everything up,

202
00:10:30,980 --> 00:10:33,280
before specifying the command,

203
00:10:33,280 --> 00:10:36,110
which should always be the last instruction

204
00:10:36,110 --> 00:10:37,990
in your Dockerfile,

205
00:10:37,990 --> 00:10:42,140
we can add to the expose instruction to let Docker know

206
00:10:42,140 --> 00:10:44,750
that when this container is started,

207
00:10:44,750 --> 00:10:49,750
we wanna expose a certain port to our local system.

208
00:10:50,360 --> 00:10:53,670
So to our machine here which will run this container.

209
00:10:53,670 --> 00:10:55,530
And then we'll be able to run

210
00:10:55,530 --> 00:10:58,133
the container (indistinct) we listen on this port.

211
00:10:59,620 --> 00:11:02,520
Now with that, we finished our Dockerfile

212
00:11:02,520 --> 00:11:06,350
with all the setup instructions for a Docker image.

213
00:11:06,350 --> 00:11:10,130
Now let's see how we can utilize this custom image.

214
00:11:10,130 --> 00:11:12,123
build it and how we can run it.

