YouTube2Text

Docker Tutorial for Beginners — Transcript

by codebasics · 20,758 words · 2,964 segments · language en · Watch on YouTube

Full transcript

  1. 0:00If you ask me to name one skill that is
  2. 0:02common among software engineers, AI
  3. 0:03engineers and field deployed engineers,
  4. 0:05then that skill is Docker. As per Stack
  5. 0:08Overflow developer survey 2026, Docker
  6. 0:11is at number one position in cloud
  7. 0:14category. So clearly it is most required
  8. 0:17skill by the developer.
  9. 0:20Even this little guy agrees. He's 50
  10. 0:23years old developer and he has seen a
  11. 0:25lot of tools come and go. In this
  12. 0:28beginner friendly tutorial, we will
  13. 0:30learn Docker through simple explanations
  14. 0:32and hands-on practice. We will start
  15. 0:34with Docker fundamentals. We'll run
  16. 0:36Docker commands. Then we'll create a
  17. 0:39container for a simple Python
  18. 0:40application and at the end we will have
  19. 0:43a complete rag application that we are
  20. 0:46going to dockerize. So if you are
  21. 0:48planning to become AI engineer or if
  22. 0:50you're a software engineer leveling up
  23. 0:51your game, you will understand some
  24. 0:54serious docker fundamentals towards the
  25. 0:57end. We will also provide you the PDF
  26. 1:00cheat sheet and some useful Docker
  27. 1:02resources. Joining with me is Sidan who
  28. 1:04is a Google developer expert and AI
  29. 1:06engineer who has been using Docker for
  30. 1:08more than 5 years in product based
  31. 1:10companies. He also teaches with me in
  32. 1:12our AI engineering cohort.
  33. 1:15And of course, we have baby Yoda who
  34. 1:17will be supervising all of us.
  35. 1:20And how about we introduce a story of
  36. 1:23Atlake Technologies where Peter has
  37. 1:25built a rag chatboard. Bruce wants
  38. 1:27client demo. And things are about to get
  39. 1:30interesting.
  40. 1:32It is 11:47 at night, the night before
  41. 1:35the big demo. Bruce, the CEO, has just
  42. 1:38one question. Does it run? And Peter
  43. 1:41says yes, but on his machine. So let's
  44. 1:44rewind a bit. Meet Peter Pande. He's an
  45. 1:47engineer at Atlake Technologies and he
  46. 1:50has built a rag chatboard for a telecom
  47. 1:52client. Data plans, coverage, customer
  48. 1:55policies, you can ask it anything. So
  49. 1:58Bruce does, does the unlimited plan
  50. 2:00include international roaming and it
  51. 2:03nails it. Now Bruce wants the same demo
  52. 2:06running on the client server by
  53. 2:08tomorrow. Peter is thinking, how hard
  54. 2:11can it be? He sends the code and the
  55. 2:14requirements file over to Miles Mishra,
  56. 2:16the deployment guy, and that's it. Done.
  57. 2:20Or so he thinks, because the app won't
  58. 2:24even start. First, it's the wrong Python
  59. 2:27version. They fix that. Then there is a
  60. 2:29system library Peter installed weeks ago
  61. 2:32and completely forgot about it. So, they
  62. 2:35fixed that, too. And the config file, it
  63. 2:38is still sitting on Peter's laptop,
  64. 2:40relaxing. And that's how we got here.
  65. 2:44Peter says the line, "Every engineer has
  66. 2:46said it at least once. It works on my
  67. 2:50machine." And Bruce has a plan. Ship the
  68. 2:52laptop to every client. That's when Tony
  69. 2:55Sharma, the engineering leader, walks in
  70. 2:58with his chai and spots the problem
  71. 3:01right away. Peter sent the rice, but he
  72. 3:04forgot the masala. The code is just the
  73. 3:06rice. The app also needs the right
  74. 3:09Python version, the installed libraries,
  75. 3:11the system dependencies, and the
  76. 3:14configuration. And none of that made the
  77. 3:16trip. Then Miles has an idea. What if
  78. 3:19they pack the app with everything it
  79. 3:21needs? The right Python, the packages,
  80. 3:24the system libraries, all in one box.
  81. 3:26You build it once and you reuse it. No
  82. 3:29more rebuilding environments by hand. So
  83. 3:32how do you actually build that box?
  84. 3:34Docker. Tony's plan is simple. Build the
  85. 3:37image, supply the config, and test it on
  86. 3:40the deployment machine. All before the
  87. 3:42demo. But Peter wants to understand it
  88. 3:44first. So Miles starts small with a
  89. 3:47simple fast API app. So let's join them,
  90. 3:51build our first Docker image, and work
  91. 3:53our way up to the Telecom chatboard.
  92. 3:59This one meme summarizes the entire
  93. 4:02story really well. This is how Docker
  94. 4:04was born. This reminds me of my time at
  95. 4:07Bloomberg where we used to face one
  96. 4:09particular problem. The problem was
  97. 4:11something like this. We will have one
  98. 4:13team, let's say earning estimates team
  99. 4:15where I was working and we'll have a
  100. 4:17developer moan who will be working on
  101. 4:19program A. Let's say calculating
  102. 4:22consensus estimates and that program
  103. 4:25needs pandas 2.0 and let's say MongoDB
  104. 4:298.0. So he will test these things out on
  105. 4:32his dev machine. He will install
  106. 4:33everything, make sure things are looking
  107. 4:35good and when everything is good, he
  108. 4:39will copy this program to a production
  109. 4:42machine. So let's say this is our
  110. 4:43production machine. We used to have this
  111. 4:45machine called GU generic server and
  112. 4:48that will have global packages. Okay. So
  113. 4:50let's say this has pandas 2.0, MongoDB
  114. 4:548.0, moan's program is working. Okay.
  115. 4:57Now you can have another team. Let's say
  116. 5:00a developer called Rita. She's having
  117. 5:03this program B and she needs same kind
  118. 5:06of packages and dependencies. So now she
  119. 5:09will copy her program B to this
  120. 5:12production server. Now both of these
  121. 5:14programs are using global packages which
  122. 5:16are installed on this production machine
  123. 5:18and things are working okay. But at some
  124. 5:20point let's say Arita who is let's say
  125. 5:23in a equity team she's writing this
  126. 5:25total return calculation program and to
  127. 5:28speed things up or for something else
  128. 5:30she needs MongoDB 9.0 version. So now
  129. 5:33she will go and update let's say pandas
  130. 5:363.0 zero, right? She will update these
  131. 5:38global packages and many times it will
  132. 5:40break program A. This was a recurring
  133. 5:43theme. To solve this problem, we created
  134. 5:46a docker-l like system basically which
  135. 5:49is a container. And here you put this
  136. 5:53entire program and all its dependencies
  137. 5:57into a container. Okay? And then you
  138. 6:01move this entire container here. So you
  139. 6:05move let's say this entire container
  140. 6:08here and then Rita will do the same
  141. 6:11thing. So Rita has her program B okay
  142. 6:15and her program needs pandas 3.0
  143. 6:19and MongoDB 9.0 zero. She has her own
  144. 6:23container. We used to call this B
  145. 6:25package Bloomberg package but the idea
  146. 6:27is same as container and they're using
  147. 6:30the same operating system but the
  148. 6:32dependencies are now contained into that
  149. 6:35particular package and that solve many
  150. 6:38problems because things are more
  151. 6:39reproducible more predictable this way.
  152. 6:45Now for anyone learning docker a natural
  153. 6:50question arises what is the difference
  154. 6:51between container virtual environment
  155. 6:53and virtual machine. Those who have
  156. 6:56worked in python know about python
  157. 6:59virtual environment. Here what we do is
  158. 7:04we uh package all the dependencies for
  159. 7:08your program. So let's say my program
  160. 7:10needs pandas fast API all these packages
  161. 7:14you can put all of them in a common
  162. 7:18virtual environment
  163. 7:20and then the Python installation the
  164. 7:24actual installation you are using it
  165. 7:25from your host installation so this is a
  166. 7:28computer on which let's say I have
  167. 7:30installed Python let's say 310
  168. 7:34okay so then in my virtual environment
  169. 7:37this Python executable is a sim link.
  170. 7:40It's just a link or maybe a lightweight
  171. 7:43copy if it is Windows. But if you want
  172. 7:46to use 3.14 in your virtual environment,
  173. 7:49you can't use it. You are still reliant
  174. 7:51on this. Okay. So the host operating
  175. 7:54system is still same. You can have three
  176. 7:56different program running with three
  177. 7:58different virtual environment. So Sidan,
  178. 8:00do you think this can solve the problem
  179. 8:02that we were facing earlier?
  180. 8:05only partially because this will only
  181. 8:07isolate the environment that is related
  182. 8:09to Python. But what about other
  183. 8:11packages? Let's say what about other
  184. 8:14dependencies like let's say radius or we
  185. 8:17saw in your previous example. What
  186. 8:19about those? We still need to isolate
  187. 8:21them.
  188. 8:22>> So this won't work.
  189. 8:24>> Okay. And another one I can think about
  190. 8:26is the Python, right? So let's say one
  191. 8:28program needs Python 3.10, the other one
  192. 8:31needs Python 3.14. virtual environment
  193. 8:34doesn't solve that problem. So for that
  194. 8:38maybe we can use a different system
  195. 8:41called virtual machine.
  196. 8:44Here you have your host operating system
  197. 8:47on which you can have this hypervisor
  198. 8:50layer on top of which you can create an
  199. 8:54entire
  200. 8:56machine basically. So this is a virtual
  201. 8:59Linux machine with its own OS. So watch
  202. 9:02this.
  203. 9:03So you have your own OS, you have your
  204. 9:05own Python packages. If you want to have
  205. 9:08your own radius, here you can have your
  206. 9:10own radius, MongoDB, everything. And
  207. 9:13here you have app code. So this entire
  208. 9:16machine is kind of encapsulated. And if
  209. 9:20you want to have a different machine,
  210. 9:21let's say Windows machine, you can have
  211. 9:23that too, right? Because here it is
  212. 9:26running Windows C OS. Okay? And this is
  213. 9:29the approach that many cloud providers
  214. 9:31such as AWS follows, right? They have a
  215. 9:34one big machine on which they will spin
  216. 9:36off their virtual machines. So now
  217. 9:40our problem of separate Python
  218. 9:43installation, you know, RadiusB having
  219. 9:45different versions all of that is
  220. 9:47solved. So SAN, what do you think about
  221. 9:49this? I think we are going way overboard
  222. 9:52here because I had to ship my
  223. 9:55application code with its dependencies.
  224. 9:58But now it's like I'm taking my whole
  225. 10:00computer instead of just the app and the
  226. 10:03dependencies and pushing it somewhere on
  227. 10:06the cloud. Now this is going to take a
  228. 10:08lot of time to build. That is one thing.
  229. 10:11Other thing is this is also very
  230. 10:12resource intensive because now you have
  231. 10:14a separate operating system that needs
  232. 10:17to run just for your application. Don't
  233. 10:19you think it's like too much?
  234. 10:21>> Yeah, this sounds like too much. This
  235. 10:25more sounds more like I have a very
  236. 10:26small table and I am using a truck to
  237. 10:30move that table, right? It's too much.
  238. 10:33The size of this virtual machines can be
  239. 10:36in gigabytes. They are very uh resource
  240. 10:40intensive. Let's say you want to upgrade
  241. 10:42your OS. Now, you have to go to these
  242. 10:44machines and uh go through all that
  243. 10:46upgrade uh process and so on. Okay. So,
  244. 10:49so you are absolutely right. This is too
  245. 10:51much. So, then let's go to the next
  246. 10:55solution which is basically Docker.
  247. 10:57Okay. So, in Docker we have shared host
  248. 11:02basically shared host kernel. So,
  249. 11:04operating system is common. On top of
  250. 11:08that you have something called a Docker
  251. 11:11engine and on top of that you have a
  252. 11:13Docker container. In this container, you
  253. 11:17have application code, you have your
  254. 11:19dependencies,
  255. 11:21you have your own Python installation.
  256. 11:23Okay? So, if I need 3.10, 3.14, I can
  257. 11:27have it in my own container. I'm not
  258. 11:30referring it to some global installation
  259. 11:32on my host kernel. I also have system
  260. 11:35libraries, CUDA, lip, pq, whatever
  261. 11:38system libraries that you need. Okay.
  262. 11:41And then if I need MongoDB or radius, I
  263. 11:45have separate containers for that. Okay.
  264. 11:48So radius is its own container. MongoDB
  265. 11:50is it own container. And that way this
  266. 11:53entire system can
  267. 11:56package my dependencies into one unit.
  268. 12:00At the same time, it keeps things
  269. 12:02lightweight. So what do you think about
  270. 12:04this?
  271. 12:05>> Yeah, this looks like a good solution.
  272. 12:08>> Yeah. So now instead of using a truck I
  273. 12:12am using my car to move a small table
  274. 12:15that I have. Okay. So this particular
  275. 12:17diagram
  276. 12:19compares the three approaches. So as we
  277. 12:22saw virtual environment is only
  278. 12:26dependencies. Containers are
  279. 12:28dependencies plus system libraries plus
  280. 12:32uh other components in your system. You
  281. 12:35know radius, MongoDB, whatever you need
  282. 12:37it.
  283. 12:38Whereas virtual machine is an entire
  284. 12:41operating system with all the bells and
  285. 12:44whistles. If I have to use the analogy,
  286. 12:47here's the analogy. Virtual environment
  287. 12:49is like a room. Container is like a flat
  288. 12:52and virtual machine is a house.
  289. 12:58Enough of the talk folks. Let's install
  290. 13:01Docker. Now before installing Docker,
  291. 13:04you need Windows Subsystem for Linux. If
  292. 13:07you are using Windows, I'm on Windows so
  293. 13:10I have to install this. If you are on
  294. 13:12Linux, then it already has Linux
  295. 13:15operating system. So you don't need
  296. 13:17this. Okay. So for Windows, what you do
  297. 13:20is you open PowerShell, which is like
  298. 13:22the command prompt essentially. And here
  299. 13:26you just run WSL install. Okay? And that
  300. 13:31will install it. Now in my case, of
  301. 13:33course, I have already installed this so
  302. 13:35it's not going to work. But if you have
  303. 13:37not installed this then it will go ahead
  304. 13:40and install it. So Windows Subsystem for
  305. 13:42Linux is basically a way to run Linux
  306. 13:45operating system on top of Windows
  307. 13:48because Docker runs on Linux. Okay,
  308. 13:50that's why we need this. Now install
  309. 13:52Docker in Google. You will say install
  310. 13:54Docker desktop. You go to this
  311. 13:57particular link. You see Docker desktop
  312. 14:00setup installed. You choose your
  313. 14:01operating system and then in my case
  314. 14:04it's Windows. So I will click on this.
  315. 14:07It will download it. I have already
  316. 14:09downloaded it. Okay. So what you do is
  317. 14:13just double click and follow common
  318. 14:16sense folks. We live in the era of AI.
  319. 14:19If you're stuck, ask your friend chat
  320. 14:22GPT or Claude and just get it done.
  321. 14:25Folks, you don't need to be spoonfed for
  322. 14:27this. Once Docker is installed, you will
  323. 14:30run a command prompt and verify it is
  324. 14:34installed. So you can just run docker
  325. 14:37version and see this means that my
  326. 14:41docker is installed.
  327. 14:46Now that my installation looks good, how
  328. 14:48about we download the docker image for
  329. 14:52python and we try it out. So far you
  330. 14:55might have tried Python by installing it
  331. 14:57locally but now we will try it through
  332. 15:01docker. So let's see how that looks. Now
  333. 15:04from where we are going to download it
  334. 15:06actually there is this website called
  335. 15:09hub.docker.com docker.com.
  336. 15:11This is like a git repository of all the
  337. 15:13Docker images. And if you search here
  338. 15:17for Python,
  339. 15:19I will see Python's official repository.
  340. 15:24Okay. So see Docker's official image 1
  341. 15:27billion plus downloads. So I'm going to
  342. 15:30download uh some image. So let's see. So
  343. 15:33it has all these tags. So tags are
  344. 15:36basically versions of the Docker image.
  345. 15:38Okay. And docker image is more like a
  346. 15:40class. So you download the image on the
  347. 15:42disk. And when you start the container
  348. 15:45that is called container. It is like an
  349. 15:47object of the class. So image is a
  350. 15:49blueprint. It's a class and container is
  351. 15:52an object or instance of that class.
  352. 15:55Okay. So here I see all these tags.
  353. 15:58Okay. So maybe let me see which one I
  354. 16:02want to download. So for Python,
  355. 16:05how about I download let's say this one
  356. 16:093.13
  357. 16:11slim. Okay. So this is the image that I
  358. 16:15want to download and the command for
  359. 16:18that is going to be docker pull. Okay.
  360. 16:22So docker pull and then
  361. 16:25what do you want to download? So python
  362. 16:27image. Okay. And then 3.13
  363. 16:31slim. So we'll go over what slim means
  364. 16:33but basically you are downloading 3.13
  365. 16:36version of Python image
  366. 16:39and it will now via internet it will
  367. 16:42download it locally on your computer.
  368. 16:45See it is just pulling this thing. So
  369. 16:47now it is available in your local
  370. 16:49docker. But how do I see it exactly?
  371. 16:53Well you can just run docker images
  372. 16:56command and in this images. So let me
  373. 17:00just make it clear. You see I'm seeing
  374. 17:03this Python's uh slim version. So slim
  375. 17:06version is a little bit trim down
  376. 17:08version. If you download a full Python
  377. 17:10image with all the binaries etc. it it
  378. 17:13it's going to be big you know 1 GB
  379. 17:15something like that very big so I want a
  380. 17:18lighter version of Python. So that's why
  381. 17:20I have downloaded slim which is 181 MGB.
  382. 17:24So these are all the images that I have.
  383. 17:27Now let's run this Python using this
  384. 17:30docker image. So you will use this
  385. 17:32command called docker run. Okay. So
  386. 17:36docker run and then name of the image
  387. 17:403.13
  388. 17:41slim.
  389. 17:43Now see when you run it like this
  390. 17:45nothing is happening because it is
  391. 17:46starting Python and it is exiting
  392. 17:49immediately. So if you want it to run in
  393. 17:52interactive mode you have to say it, you
  394. 17:55know. So this is interactive mode and
  395. 17:57now see I'm running my Python code. So
  396. 18:00here I can write all my Python code.
  397. 18:03This interpreter is essentially coming
  398. 18:06from this particular docker image that
  399. 18:09we just ran. Other than the command line
  400. 18:12interface you can access all of this via
  401. 18:15user interface as well.
  402. 18:18For that here you can type docker
  403. 18:22desktop. So docker desktop is the
  404. 18:24application that we installed and here
  405. 18:27you will see all these images. You see
  406. 18:30this image 3.13 slim. This is what you
  407. 18:34downloaded and if you want to run it you
  408. 18:37can click run here and it will run it or
  409. 18:40see in the container I have that image
  410. 18:43running here. See you can stop it from
  411. 18:45here as well. You can start and stop it
  412. 18:47from here too. But see here if I start
  413. 18:50it will start and then stop because we
  414. 18:53are not running it in the interactive
  415. 18:55mode. Okay, this needs to run in the
  416. 18:58interactive mode. So this is a simple
  417. 18:59demonstration of how you can download
  418. 19:03Python's image and run it. Now let's
  419. 19:06download a different image. How about
  420. 19:08So docker pull Okay. So
  421. 19:12here if I go to the website
  422. 19:17and if I say you will find this
  423. 19:24uh image. Okay. And in that there is
  424. 19:28this tag called latest. Latest refers to
  425. 19:31the latest version of this image. Right.
  426. 19:34So we are going to just download latest.
  427. 19:36If you don't specify anything it will
  428. 19:38download latest. So let's do this. So I
  429. 19:41will say docker pull
  430. 19:44And by the way this is going to be a bit
  431. 19:47of a bigger image. It's 1 GB. So if you
  432. 19:50don't have that much space on your
  433. 19:51computer then you can just watch it. I
  434. 19:53mean we are not going to actively use
  435. 19:54MongoDB later on. I I just want to show
  436. 19:57how you download the bigger images and
  437. 20:00how you play with it. Okay. All right.
  438. 20:02So this image is downloaded. Now when I
  439. 20:04run docker images I see two images. You
  440. 20:08see I see one is another one is
  441. 20:13python. Okay. Now let's run this
  442. 20:18image. So for this you can just say
  443. 20:21docker run Simple. Whatever image
  444. 20:24you downloaded you just say docker run
  445. 20:26and it will run that. It will run
  446. 20:30that. Meaning it is starting
  447. 20:33database. Now see my database has
  448. 20:35started. But one thing I'm noticing is
  449. 20:37it has blocked my terminal. See now I'm
  450. 20:40not able to type any other command. So I
  451. 20:43will exit out of this and what I will do
  452. 20:45instead is I will run same command but I
  453. 20:48will say
  454. 20:50hyphen d which means detached mode.
  455. 20:53Detach mode means it's now running it in
  456. 20:55the background. Okay. And I can now work
  457. 21:00in my command line. But how do I see all
  458. 21:02the processes that we are running? file
  459. 21:04you just say docker ps. Okay, so that
  460. 21:08will show you
  461. 21:10see this is like a Linux command write
  462. 21:13processes and it will show you the
  463. 21:14process and the name of the process is
  464. 21:17objective query. It is just giving this
  465. 21:20fancy name but it is essentially uh your
  466. 21:23container that is running. So
  467. 21:26hopefully now you're understanding
  468. 21:27difference between the images and the
  469. 21:30container. Image is something like a
  470. 21:32class. It is on a disk and container is
  471. 21:35like a running process. It is running
  472. 21:37right now. Okay. And you can see the
  473. 21:39same here also. See images. I have two
  474. 21:43Python and And container I have
  475. 21:47multiple. See I I I ran all this
  476. 21:49container before. Let me just delete
  477. 21:51this. I I'm just deleting the container.
  478. 21:53Okay. I'm not deleting the image. So
  479. 21:56image wise only is running. uh
  480. 21:59sorry container wise only is
  481. 22:02running and image wise I have two uh
  482. 22:05particular images okay one good practice
  483. 22:09is to start uh this container with a
  484. 22:13specific name so I can say something
  485. 22:15like docker
  486. 22:18minus d see let's use the same command
  487. 22:20right so this is the command that you
  488. 22:21use you can give name here you can say
  489. 22:24my okay so when you do that What
  490. 22:28is happening actually is now we are
  491. 22:31running two different containers. So
  492. 22:34image is same it's but you are
  493. 22:37running two containers one is my
  494. 22:40the first one is objective query that's
  495. 22:42a default name that it gave and if you
  496. 22:45look at container here you see these are
  497. 22:48the two see two okay so let me uh delete
  498. 22:53this objective query I don't need this
  499. 22:55so I have only one container running my
  500. 22:58okay images are still two now if
  501. 23:01you want to stop my what you and
  502. 23:04say is docker stop my Okay, you
  503. 23:08have to give this name because can
  504. 23:11be running uh as multiple instances. You
  505. 23:14want to stop this particular instance.
  506. 23:16So you are stopping that container. And
  507. 23:20now when you do ps see that has
  508. 23:22disappeared. This one we deleted from
  509. 23:24the UI. Okay. And if you want to start
  510. 23:26that again you can say docker start my
  511. 23:30So now it has started it. Okay.
  512. 23:34So first time when you ran it, you ran
  513. 23:37it through run command. You gave it a
  514. 23:40name. When you do stop my it will
  515. 23:42stop it. But that my name is still
  516. 23:44registered. So when you say start again,
  517. 23:47it knows what to start. Okay. So here
  518. 23:52it is my And now let me start a
  519. 23:55different one. I will use the run
  520. 23:57command. And instead of my I'll
  521. 23:59say your Okay. My Your
  522. 24:00And then now we have two of them
  523. 24:04running right. So two processes.
  524. 24:07One is my another one is your
  525. 24:09This is a learning tutorial. But
  526. 24:12in real life, you might want to start
  527. 24:14two instances with two different
  528. 24:16authentications or let's say two
  529. 24:19different databases really. You know you
  530. 24:21have one dev database, one staging
  531. 24:23database. You can start multiple
  532. 24:26databases and all these databases have
  533. 24:30particular container ID. You see this is
  534. 24:32the container ID and the image is same
  535. 24:35again image is same but these two
  536. 24:38are two different containers you know
  537. 24:41two different docker containers that you
  538. 24:43are running and here it is showing you
  539. 24:45the status this is up this is up here it
  540. 24:48is showing you the port name is your
  541. 24:50my now I can say docker
  542. 24:55stop my okay so I'm stopping my
  543. 24:57your is still running and
  544. 25:00when you Say docker ps. Now it is
  545. 25:02showing only your But if you say
  546. 25:06same command with hyphen a it will show
  547. 25:10you my as well. See and in my
  548. 25:14it will say exited 12 second ago.
  549. 25:18So let's understand the difference. When
  550. 25:20you say run, you are starting a
  551. 25:23container with that name for the first
  552. 25:26time. when you say stop you are stopping
  553. 25:30that particular container but this name
  554. 25:32is still in the memory so that if you
  555. 25:35want to start again I can say docker
  556. 25:38start my and I'll be able to start
  557. 25:40it
  558. 25:41and p-en a is the command that you use
  559. 25:45to see all those container instances
  560. 25:49with their names so that if you want to
  561. 25:51start and stop you can do it so okay so
  562. 25:54right now if I want to start docker
  563. 25:56start my it will work because the
  564. 26:00name is in the registry but let's say if
  565. 26:03I want to say docker start yoda
  566. 26:07then it will not work because that name
  567. 26:10is not registered basically it's not in
  568. 26:13the list of known docker containers
  569. 26:16already so this one you have to run you
  570. 26:20have to just say docker run minusd
  571. 26:24minus name yoda
  572. 26:26then only later on you can do start and
  573. 26:29stop. Without that you can't do it. So I
  574. 26:31hope you understand it. Many people get
  575. 26:33confused between run and start and stop
  576. 26:35commands. Okay. Now we have see if you
  577. 26:40run this command ps a we have two
  578. 26:44containers. One is stopped already one
  579. 26:47is still running. Now let's say my
  580. 26:50container I want to remove from this
  581. 26:52list. For that you can simply say docker
  582. 26:56rm my
  583. 26:59and that will remove that from the list
  584. 27:02so that when you now
  585. 27:06type this it does not show up. So now
  586. 27:10when I say docker start my it's
  587. 27:13not going to work because it's not in
  588. 27:15the list of known names. All right. Now
  589. 27:18how do you remove the image? Let's say I
  590. 27:20want to remove the MongoDB image
  591. 27:23completely. Well, there are two ways.
  592. 27:26One way is through the UI. You go to
  593. 27:28images. You see this nice little delete
  594. 27:32icon. It say this image is going to be
  595. 27:35deleted. Or you say docker rm image. See
  596. 27:40there was rm command to remove the
  597. 27:42docker container. rm i. I stand for
  598. 27:45image. Remove image. Okay. and
  599. 27:50conflict unable to delete must be
  600. 27:54forced. So this other container is still
  601. 27:56running right. So we have to stop it. So
  602. 27:58I will say docker stop your
  603. 28:02because your is still running. And
  604. 28:04then uh I have nothing running. Okay.
  605. 28:08Okay. Let me just remove it. So let me
  606. 28:10do a complete cleanup. So docker rm
  607. 28:13yarmong.
  608. 28:17And now I have nothing running and I can
  609. 28:20say docker
  610. 28:23rm images okay
  611. 28:27and that has deleted it. So now when I
  612. 28:29say images I don't see it and from the
  613. 28:32UI also it is gone. If you have let's
  614. 28:36say 10 images you don't want to delete
  615. 28:38them step by step. You want to delete
  616. 28:40everything. In that case you can say
  617. 28:42docker system prune a. Be very careful
  618. 28:46when you are using this command because
  619. 28:48this is like delete all. Okay, delete
  620. 28:50star and that's it. See now when I say
  621. 28:54docker images I don't see anything. My
  622. 28:57docker is basically now blank. See I
  623. 28:59don't have anything.
  624. 29:03>> Let's now talk about docker
  625. 29:04architecture. It's a client server
  626. 29:07architecture where you have a docker
  627. 29:10client. So whatever commands that we
  628. 29:12were running in the command prompt that
  629. 29:13is essentially your docker client. It is
  630. 29:17a lightweight CLI function which you use
  631. 29:21it to send commands to the server. Okay.
  632. 29:24The server is actually the docker back
  633. 29:27end. It's called docker demon. The
  634. 29:30process name is docker d. Docker demon.
  635. 29:33D stands for demon. And through rest API
  636. 29:37the client will send commands to docker
  637. 29:39demon. Now docker demon is the program
  638. 29:42that creates and runs the containers. If
  639. 29:46you are creating your own images you
  640. 29:49know building new images which we'll see
  641. 29:51in a bit that is also done by docker
  642. 29:54demon. It's essentially the docker back
  643. 29:56end. Basically client is just
  644. 29:58lightweight. whatever commands we are
  645. 30:00just running that is a client basically
  646. 30:02and behind the scene it is sending this
  647. 30:05rest api commands to the demon okay and
  648. 30:10then when you say something like docker
  649. 30:12pull it is getting all these images from
  650. 30:16the docker registry which is on the
  651. 30:18cloud so that docker hub that we saw
  652. 30:20which has so many different images for
  653. 30:23different kind of softwares and such
  654. 30:25that is basically your cloud repository
  655. 30:28it's GitHub from where you are pulling
  656. 30:31your Docker image locally and Docker
  657. 30:34engine is nothing but the demon rest API
  658. 30:37and CLI. It is essentially a package
  659. 30:40which includes all these components. If
  660. 30:42you are looking at your Windows task
  661. 30:44manager
  662. 30:46uh here you will find Docker Desktop,
  663. 30:50right? So this is essentially the client
  664. 30:53and the back end actually is your docker
  665. 30:58demon process is running inside the WSL
  666. 31:02which is Windows subsystem for Linux.
  667. 31:05Docker D process is something we won't
  668. 31:07be able to see because it is running
  669. 31:09inside that WSL. But if you are running
  670. 31:13docker on Linux then you will be able to
  671. 31:16see this process. Okay. So just say ps
  672. 31:19and try to find this process docker d if
  673. 31:22you're running Linux on windows just
  674. 31:25know that docker d is running inside
  675. 31:28this uh windows subsystem for linux.
  676. 31:34Now let's go over docker hub very
  677. 31:36quickly. You can use some other
  678. 31:38repository as well but this is the
  679. 31:41official hub with many images which are
  680. 31:44available. Now when you search for any
  681. 31:47image let's say postgrade right so here
  682. 31:50you are finding this postgrace official
  683. 31:53image so there is this tag called
  684. 31:55official image and there are other tags
  685. 31:58too so if you explore here
  686. 32:01on the left hand side you will see
  687. 32:03docker official image harden image and
  688. 32:05so on okay so for postgress the thing
  689. 32:09that we saw so let me just show you so
  690. 32:11this posgress right this one you see
  691. 32:15this gray in badge docker official
  692. 32:17image. So this is an official image
  693. 32:19which has been approved by a docker
  694. 32:21community. Okay, but this image may have
  695. 32:26vulnerabilities. So look at this. It has
  696. 32:29some 31 high severity vulnerabilities.
  697. 32:33Okay, look at this. You see this?
  698. 32:36Although it's an official image but many
  699. 32:39people use this. You see 1 billion plus
  700. 32:41downloads because this is a complete
  701. 32:43package. It comes with all the bells and
  702. 32:45whistles, all the tools. Okay. The other
  703. 32:48option that you have is let's say I I
  704. 32:52click on docker harden image and when I
  705. 32:55look at this one, this one has almost
  706. 32:58like zero critical severity. Okay. So
  707. 33:01what has happened is you take the
  708. 33:05official image basically okay or base
  709. 33:07post SQL image and you do a thorough
  710. 33:10security review. you remove unnecessary
  711. 33:13things. So many things are stripped off
  712. 33:16from the main actual image. So when you
  713. 33:20use this harden image, you get better
  714. 33:23security, you see, but you may not get
  715. 33:27all the tools, all the bells and
  716. 33:29whistles that you get with the official
  717. 33:31image. So there is a trade-off. What
  718. 33:34people do is when they want to use
  719. 33:36docker in the production you know people
  720. 33:39care about security they will use
  721. 33:40hardened image and then anything extra
  722. 33:44that you need you get it externally
  723. 33:45basically. Okay. So for production use
  724. 33:48cases people use harden image because
  725. 33:51vulnerabilities are lower and for
  726. 33:53development use cases they may use the
  727. 33:56official images. So just spend some time
  728. 33:59exploring this. The UI is pretty
  729. 34:01intuitive. any kind of package you want
  730. 34:03to search using categories you can
  731. 34:05search see I want to look at all the
  732. 34:07docker images for message cues and here
  733. 34:10is showing me kafka rabbit mq and so on
  734. 34:16the typical workflow for dockerizing any
  735. 34:18kind of application looks something like
  736. 34:20this let's say you have this app and
  737. 34:22this app could be written in any
  738. 34:24language Python Java NodeJS does not
  739. 34:27matter to dockerize this you first
  740. 34:29create a docker file that will contain
  741. 34:31in all the instructions that are
  742. 34:33required to package this app into a
  743. 34:36docker image. We will use this docker
  744. 34:38image to start up a container which is
  745. 34:40nothing but a special kind of process
  746. 34:42with its own file system. Our
  747. 34:44application will get loaded into this
  748. 34:47special container and it will execute in
  749. 34:50the isolated environment provided by the
  750. 34:52container and this docker image that we
  751. 34:54are talking about it will contain
  752. 34:56everything that is required to run our
  753. 34:58app. for example, slim operating system.
  754. 35:01At first glance, it might seem like
  755. 35:03Docker is building an entire OS. In
  756. 35:05reality, it doesn't. Each container
  757. 35:08includes just the user space files it
  758. 35:10needs and borrows the kernel from the
  759. 35:12host machine. It could contain the
  760. 35:14runtime dependencies of the language
  761. 35:16that you're running in. Could be Python,
  762. 35:18could be Java. It could also contain the
  763. 35:21third party applications that are
  764. 35:23required to run your app. the
  765. 35:25application files themselves like your
  766. 35:28py files, your java files that you have
  767. 35:30written as a programmer and any kind of
  768. 35:33environment variables or configurations
  769. 35:35that are required to execute your app.
  770. 35:38Let's see this practically by
  771. 35:40dockerizing our own application and
  772. 35:42learn docker by building a docker file
  773. 35:44ourselves.
  774. 35:45>> Let's get started. I'm in my
  775. 35:47anti-gravity editor and I've created a
  776. 35:49very simple fast API application. You
  777. 35:52can see this is a simple server with
  778. 35:55only two endpoints. One is the get
  779. 35:57homepage and the other one is get health
  780. 36:00status.
  781. 36:02And they respond with a very simple
  782. 36:03JSON.
  783. 36:05And they're using only two dependencies.
  784. 36:08One is fast API, the other one is UVCON.
  785. 36:12We are going to dockerize this
  786. 36:13application. Before we dockerize it, let
  787. 36:16me run it and show it to you. Now we
  788. 36:19need to remember and keep track of all
  789. 36:21the steps that we are following to start
  790. 36:23up our app. Here the first thing that I
  791. 36:25need to do is I need to go into this
  792. 36:29particular directory. So I will cd into
  793. 36:33hello API and then I need to install all
  794. 36:37the dependencies that are present in
  795. 36:40this requirements.txt file. For that I
  796. 36:43can use pip. So I'm going to write pip
  797. 36:48install -ash r. I can specify the
  798. 36:51requirements file.
  799. 36:53This will install all the dependencies
  800. 36:55like fast API uon everything. My
  801. 36:58dependencies are installed
  802. 37:00and then I can use uvicon to run this
  803. 37:04particular server.
  804. 37:06So, uicorn
  805. 37:09and main which is my file name main
  806. 37:12colon app and then I can specify the
  807. 37:16port where I want to start up the
  808. 37:18server. So port typically it is 8,000.
  809. 37:24When I run this you can see my server is
  810. 37:26started. I can go to this link and you
  811. 37:31see in the homepage I'm getting this
  812. 37:33hello from inside a container. It is not
  813. 37:35containerized now, but we're going to
  814. 37:37containerize it soon. Then when I go to
  815. 37:40the docs, you can see the swagger doc,
  816. 37:44it is hit API. It should be high API
  817. 37:47actually.
  818. 37:48And then in the root, you can see when I
  819. 37:53execute it, it will reply with hello
  820. 37:55from inside the container and also the
  821. 37:58health end. whatever steps we have
  822. 38:00followed to execute and run this server.
  823. 38:03These are the same steps that we need to
  824. 38:05mention in our docker file to create our
  825. 38:07package. So let me close this and open
  826. 38:12up our project folder.
  827. 38:15I'm just going to copy this
  828. 38:18and create a new folder. Let me rename
  829. 38:21it to docker. And here I'm going to
  830. 38:24create a special file. Okay, this
  831. 38:27special file will be called let me
  832. 38:29delete this pi cache as well. We don't
  833. 38:32need it. All right, I have my new
  834. 38:34application folder here. I'm going to
  835. 38:37create a new file and this file will be
  836. 38:40called a docker file. I know they have
  837. 38:43kept the name very intuitive
  838. 38:45and this file will not have any kind of
  839. 38:47extensions. You simply call it docker
  840. 38:49file and you can see VS code or
  841. 38:51anti-gravity is able to identify it in
  842. 38:54this docker file. The very first thing
  843. 38:56that we need to mention is the base
  844. 38:58image from which we will start building
  845. 39:00our image. So the base image for this
  846. 39:03application will be a Python one. We can
  847. 39:06specify from
  848. 39:09and also the name of the base image.
  849. 39:12In our case, it is going to be Python
  850. 39:163.12
  851. 39:19slim. With this instruction, Docker will
  852. 39:21pull the image of Python 3.12 Slim and
  853. 39:25it will use it as a base image to
  854. 39:29package our application.
  855. 39:31Now the next thing that we can do is
  856. 39:34specify a working directory inside the
  857. 39:37container environment. As I told you
  858. 39:39already that every container will have
  859. 39:41its own file system. We need to specify
  860. 39:43the specific directory in which Docker
  861. 39:46will open up our application. So we
  862. 39:49specify it by work there. This simply
  863. 39:53stands for working directory. And I can
  864. 39:56specify the working directory as / app.
  865. 39:59What this will do internally is we all
  866. 40:02know docker runs in a Linux environment,
  867. 40:04right? So it will use the mkdir
  868. 40:08command and using this mkdir command it
  869. 40:11will create the app folder and then it
  870. 40:14will go to the app folder using cd. It
  871. 40:16will do cd app. Both of these things are
  872. 40:19done internally by Docker whenever you
  873. 40:21are specifying this work command. Okay,
  874. 40:24this thing is asked in the interview.
  875. 40:25That's why I'm just letting you know.
  876. 40:27So, Docker has created the working
  877. 40:29directory and it is now in that working
  878. 40:31directory. What are we going to do now?
  879. 40:33The very first thing that Docker
  880. 40:35requires is the application files. So,
  881. 40:39we need to copy all these application
  882. 40:40files like main.py or requirements.txt
  883. 40:43txt into the docker file system. For
  884. 40:47that, we can use the copy instruction
  885. 40:50and specify the current root folder that
  886. 40:54we want to copy. We want to copy this
  887. 40:56into wherever docker is right now. So,
  888. 41:00it is already in the app folder. We do
  889. 41:02not need to specify app. We can just
  890. 41:04specify dot. This will copy everything
  891. 41:07everything that is in the current
  892. 41:08working directory. You can see the
  893. 41:10current working directory is this one.
  894. 41:12Hello API Docker. It will copy all of
  895. 41:14these files into Docker's own file
  896. 41:17system. Now we have already CD into our
  897. 41:22working directory. Next thing that we
  898. 41:24need to do is the same thing that we did
  899. 41:26to run our app which is installing all
  900. 41:28the dependencies that are present in
  901. 41:30requirements. TXT. So I can use the run
  902. 41:35instruction which simply says that
  903. 41:38docker run this particular command that
  904. 41:41I'm specifying. So I can specify the
  905. 41:43command as pip install
  906. 41:47-ash r and I can specify it is
  907. 41:51requirements dot txt in this app folder.
  908. 41:56You see we have copied it already in the
  909. 41:58app folder. It will be present in the
  910. 42:00app folder. We are just going to use the
  911. 42:01same requirements file to download all
  912. 42:03the dependencies for our application
  913. 42:05inside docker's environment. And then we
  914. 42:08can finally specify the command that it
  915. 42:11needs to run whenever our container gets
  916. 42:14started. So remember everything that we
  917. 42:17have done so far will run only once
  918. 42:19while we are building the image. Once
  919. 42:22the image is built it is going to follow
  920. 42:24all these instructions if there is any
  921. 42:26change. Okay. If there is no change, it
  922. 42:28is only going to run this command.
  923. 42:31Whenever we use the start command with
  924. 42:33docker, whenever we say docker start the
  925. 42:36container name, it is going to use this
  926. 42:37command that we are going to specify.
  927. 42:39And this command is going to be the
  928. 42:42uvicon command. So I can specify uon
  929. 42:47everything that we said main app and I
  930. 42:50can also specify host here. I want the
  931. 42:53host to be local host, right? So I can
  932. 42:56write 0.0.0.0.
  933. 42:59I can also specify the port here. So my
  934. 43:01port is port the value is 8,000. So I
  935. 43:05will write 8,000. And with this my
  936. 43:07docker file is complete. It can now
  937. 43:09build an image and start a container for
  938. 43:12it. So let's do it in our command line.
  939. 43:14I'm going to clear up all this. We can
  940. 43:16use the docker build command to build
  941. 43:19our image. But make sure before you run
  942. 43:21this, you should have Docker desktop
  943. 43:24open. Okay, if you're using Windows
  944. 43:27specifically, you should ensure that
  945. 43:29your Docker engine is running.
  946. 43:30Otherwise, this won't work. You can see
  947. 43:32currently I do not have any images. So,
  948. 43:35we are going to see what this build
  949. 43:37command does, what changes happen in
  950. 43:39this docker desktop. So, I can run
  951. 43:44docker build and then I need to specify
  952. 43:47a tag. I need to give a name to the
  953. 43:50image that I'm building here. The tag
  954. 43:51that I'm going to give is let's say
  955. 43:54hello API and I need to specify which
  956. 43:59directory it needs to take as a
  957. 44:02reference for building this particular
  958. 44:03image. We are already in this zero we
  959. 44:07are in zero docker API. Let let us go to
  960. 44:11the one the one where our docker file
  961. 44:13lives. So I can go to cd one this and I
  962. 44:19can run the same command. So docker
  963. 44:22build-d
  964. 44:24which is our tag. Our tag is hello API.
  965. 44:28And then I can specify dot. This dot
  966. 44:31will say that use this folder the folder
  967. 44:34where I am in as a reference to package
  968. 44:37our app. So let me press enter. This
  969. 44:40should start building our image. You can
  970. 44:42see our image has started building and
  971. 44:44you can see that it is running
  972. 44:46everything command by command. So it is
  973. 44:48first pulling in this Python 3.12 slim
  974. 44:52from docker hub and then it is going to
  975. 44:54run each of these instructions that we
  976. 44:56have written to build our image. You
  977. 44:57will see that it will not run this cmd.
  978. 45:00Okay, you see it only executed until
  979. 45:02this run command. It is not executing
  980. 45:05the cmd. The cmd will run when we start
  981. 45:07up our container. Let's investigate if
  982. 45:10our image has been created or not. So I
  983. 45:12can do that by using docker image ls.
  984. 45:16You can see our hello API latest has
  985. 45:19already been created and it is using 148
  986. 45:23MBs. We have only two files main and
  987. 45:25requirements. Why do you think it is
  988. 45:27consuming 148 MBs? It is because we have
  989. 45:31already mentioned Docker will package
  990. 45:33everything that your application needs
  991. 45:36to run. So it has downloaded Python and
  992. 45:41the underlying OS which could be Alpine
  993. 45:43or some very minimal OS that is required
  994. 45:46to run your app. It would also be all
  995. 45:49the application files and apart from the
  996. 45:52application files all the dependencies
  997. 45:54that are being downloaded from pip. So
  998. 45:57fast API u any other dependencies that
  999. 46:00fast API and uicorn also need will also
  1000. 46:04be bundled into this package. Okay, that
  1001. 46:06is why you see 148 MB here.
  1002. 46:09Now, let's start up our container and
  1003. 46:13see it in action. Before that, let me
  1004. 46:15also show you what change has occurred
  1005. 46:18here. You can see in the image, I see
  1006. 46:21hello API, right? So, my image is
  1007. 46:24created and it is registered. Now, I can
  1008. 46:26use the docker run command to start up
  1009. 46:29our container. So, docker
  1010. 46:33run and then I need to specify the name
  1011. 46:36of the image. Let me call it hello API
  1012. 46:40docker. I also need to specify the port
  1013. 46:42and map it so that the port that is
  1014. 46:45present inside the container. I said
  1015. 46:48docker will create an isolated
  1016. 46:49environment. So, it also creates an
  1017. 46:50isolated network. So, I specify the port
  1018. 46:53that is running inside the container and
  1019. 46:55map it to a port in my computer. So this
  1020. 46:58is the port in my computer. I'm mapping
  1021. 47:00it with the port that is running inside
  1022. 47:02the docker container. All right. And
  1023. 47:04then I can specify the name of the
  1024. 47:06image. In our case, it is hello API.
  1025. 47:09When I press enter, you will see that it
  1026. 47:12will run this cmd that we have
  1027. 47:15mentioned. Okay. And this command is
  1028. 47:16nothing but uicon main col app host and
  1029. 47:20port. So when I follow this link, when I
  1030. 47:24open it, it's not running. But when I go
  1031. 47:28to local host, you will see that it is
  1032. 47:30running. So 0.0.0 is not recognized.
  1033. 47:34When I specify local host, it will run.
  1034. 47:36All right. And when I stop this
  1035. 47:39container, you can see when I will stop
  1036. 47:42this container. Right now I'm not using
  1037. 47:45a demon like dash d attribute.
  1038. 47:49But when I stop this container, you can
  1039. 47:50see server has stopped, right?
  1040. 47:53Application shutdown complete. Let me go
  1041. 47:56back to my chrome and you can see when I
  1042. 48:00refresh this won't be reachable. See
  1043. 48:04site can't be reached. So this was
  1044. 48:06running through my docker container
  1045. 48:09and let me run it again. Maybe let me
  1046. 48:11start it again. I don't need to use the
  1047. 48:13run command now. Let me clear this. I
  1048. 48:16can run docker start
  1049. 48:19hello API docker. And you see it has
  1050. 48:22started the hello API docker. When I go
  1051. 48:24to VS Code again, you can see that it is
  1052. 48:27accessible. All right. When I open
  1053. 48:30Docker desktop
  1054. 48:32and click on containers, you see that
  1055. 48:34this container is running. I can also
  1056. 48:36see the logs. You saw we have hit the
  1057. 48:40homepage and all of that is registered
  1058. 48:42here. Congratulations. You have just
  1059. 48:44containerized your first application. In
  1060. 48:47the next few sections, we will dig
  1061. 48:48deeper into Docker and understand more
  1062. 48:50of its functionalities. Peter's first
  1063. 48:52Docker image is built and his little API
  1064. 48:55is running inside a container. He's
  1065. 48:57already eyeing the chatboard, but Miles
  1066. 49:00stops him. Not so fast. They still need
  1067. 49:04faster builds, data that survives when a
  1068. 49:07container gets replaced, and containers
  1069. 49:10that can talk to each other. After all,
  1070. 49:13the demo is tomorrow. And Bruce is glad
  1071. 49:16someone remembers.
  1072. 49:21>> In this section, I want to talk about
  1073. 49:23layers in Docker. Let me show you
  1074. 49:25something. I am opening up my terminal
  1075. 49:28and I've just run this docker prune
  1076. 49:31command to clear all my Docker
  1077. 49:33artifacts. I'm going to build my image
  1078. 49:36again using this docker build-d
  1079. 49:39API do command.
  1080. 49:42Notice when I'm building it, docker will
  1081. 49:46run all of these commands that I've
  1082. 49:48written line by line. So currently it is
  1083. 49:51running this from docker and it is
  1084. 49:54installing this python 3.12 slim image.
  1085. 49:59After this is done, you see it ran this
  1086. 50:02work then it it copied all the artifacts
  1087. 50:04and then it is running pip install
  1088. 50:06requirements.txt.
  1089. 50:08This means that Docker is not packaging
  1090. 50:11our application in one go into an image.
  1091. 50:14What it is doing is it is building this
  1092. 50:16image layer by layer and each
  1093. 50:19instruction that we have provided in our
  1094. 50:21docker file contributes to each new
  1095. 50:23layer in this image. At the end, it also
  1096. 50:25adds a new layer that is for anything
  1097. 50:29that changes in the runtime. Okay. So
  1098. 50:33here you can see we have four commands
  1099. 50:35that are building the layers which is
  1100. 50:37why like our docker image will have five
  1101. 50:39layers in total four from each of these
  1102. 50:42and the fifth one which is the runtime
  1103. 50:45layer that docker adds and the reason
  1104. 50:47docker does this layering is because of
  1105. 50:49a feature called layer caching. Let me
  1106. 50:52show you something. So you just saw that
  1107. 50:55I built this image. Let me run the same
  1108. 50:58command and build this image again.
  1109. 51:01You will see it ran very fast. You see
  1110. 51:05the cache output here. This means that
  1111. 51:08Docker will first see if I have changed
  1112. 51:10anything in any of these layers. For
  1113. 51:13example, if I change the version, then
  1114. 51:16Docker will redo the step and then it
  1115. 51:19will do all the steps after the step
  1116. 51:22that is changed. Similarly, if I change
  1117. 51:25anything, if I change anything in my
  1118. 51:29code, you can see this copy layer would
  1119. 51:33have changed. And because this copy
  1120. 51:36layer would have changed, Docker will
  1121. 51:38cache these two commands. So, it will
  1122. 51:41still run this cached command, but
  1123. 51:46anything after copy, it will redo. Okay?
  1124. 51:49So it will redo the copy and then it
  1125. 51:51will redo this runpip install
  1126. 51:53requirements.txt and it will redo adding
  1127. 51:55this command. And this is why because we
  1128. 51:57did not change anything. The second
  1129. 51:59build that we did was a lot faster than
  1130. 52:02our first build. You might have noticed
  1131. 52:04that in the first build it was
  1132. 52:07installing all the dependencies. It was
  1133. 52:09downloading this Python 3.12 slim. In
  1134. 52:13the second build it did not do any of
  1135. 52:14those things. This is why it is very
  1136. 52:17important for us to also notice how we
  1137. 52:22have structured our docker file. Okay,
  1138. 52:26the order of instructions in the docker
  1139. 52:28file matter a lot because of layer
  1140. 52:31caching. I will show you with an
  1141. 52:33example. If I change anything in main,
  1142. 52:36let's say I'm just going to add a very
  1143. 52:40simple comment here like health
  1144. 52:44endpoint. And because I have changed my
  1145. 52:47main file, you see this copy layer is
  1146. 52:50affected.
  1147. 52:52And
  1148. 52:53when I create a new image now you will
  1149. 52:58see that this copy command will run
  1150. 53:00again and all the commands after it will
  1151. 53:03run again which means the pip install
  1152. 53:05will also run again.
  1153. 53:08You see copy ran and now pip install is
  1154. 53:11running. This took a lot longer than
  1155. 53:14whatever time we spent last time when we
  1156. 53:17built the second image. Right? Do you
  1157. 53:19think this is efficient? I don't think
  1158. 53:21so. Why? Because we did not change
  1159. 53:24anything in the requirements file. Still
  1160. 53:27all our dependencies got installed again
  1161. 53:30which is not a good thing because this
  1162. 53:32will stall our build process. While
  1163. 53:34building your docker file you need to
  1164. 53:37ensure that the order of instruction is
  1165. 53:39in such a way that whichever layer is
  1166. 53:43going to change the most is in a later
  1167. 53:46stage so that all the previous layers
  1168. 53:48can be cached. All right. So, let's do
  1169. 53:52something smart here. Let's implement
  1170. 53:54layer caching for our Docker file here.
  1171. 53:57So, I'm going to create a copy for this
  1172. 54:00new section and we are going to update
  1173. 54:03that Docker file. I can rename it to
  1174. 54:07All right, I have copied all the code
  1175. 54:10into this new folder and I'm going to
  1176. 54:12modify the Docker file here. Okay, so
  1177. 54:15that when you access this repository,
  1178. 54:18you can see the whole history of its
  1179. 54:20evolution.
  1180. 54:22Let's see what we want to do here. Our
  1181. 54:24main goal is to ensure that
  1182. 54:27installations only happen when something
  1183. 54:30is changed in requirements. .txt and
  1184. 54:33they do not happen if anything else
  1185. 54:36changes in the code because code changes
  1186. 54:38will happen very regularly compared to
  1187. 54:41change in any kind of requirement. For
  1188. 54:43that we need to ensure that this runs
  1189. 54:46before our copy command. So let me cut
  1190. 54:50it from here and we will paste it after
  1191. 54:54setting up our working directory. But
  1192. 54:56there is a problem here. I will not have
  1193. 54:58access to requirements.txt until
  1194. 55:01everything is copied. So there is a
  1195. 55:04workar around. We can copy only the
  1196. 55:05requirements.txt file in our app folder.
  1197. 55:08This way I will have requirements.txt
  1198. 55:11txt file and my pip install will execute
  1199. 55:14successfully. Try to understand what we
  1200. 55:15are doing. We are first copying the
  1201. 55:18requirements.txt file then installing
  1202. 55:21all the dependencies and then copying
  1203. 55:24all the code. So this layer comes very
  1204. 55:26later on. I will try to do the same
  1205. 55:27thing. So let us open up. We're going to
  1206. 55:31cd onto our new directory. So cd
  1207. 55:35to layer caching.
  1208. 55:38And here I'm just going to run this
  1209. 55:41command. Okay, I'm going to build my
  1210. 55:43container again. Since this is a new
  1211. 55:45one, everything will run. All right, you
  1212. 55:49see the layers have changed. So it all
  1213. 55:52ran. Now let me change something in the
  1214. 55:54code.
  1215. 55:57This is our code. Let me quickly just
  1216. 56:02add another comment here. I will call
  1217. 56:05this root
  1218. 56:07endpoint
  1219. 56:10and let's see how this change affects
  1220. 56:13our docker build.
  1221. 56:16I'm going to build again
  1222. 56:21and you'll see everything was cached. So
  1223. 56:25even the pip installation that earlier
  1224. 56:28ran again because of a code change did
  1225. 56:30not run this time and only the copy
  1226. 56:33command ran here. So this is the benefit
  1227. 56:37of layer caching. I would like to talk
  1228. 56:39about something. These days AI can
  1229. 56:42easily write these docker files. But
  1230. 56:45what is important is having this
  1231. 56:47intuition through understanding of all
  1232. 56:51of these basic concepts that whether
  1233. 56:53this file AI has generated is optimized
  1234. 56:56or not. Okay. Which is why it is very
  1235. 56:59important to learn all of these
  1236. 57:02fundamentals and built on top of it.
  1237. 57:07In this section, we are going to
  1238. 57:09understand Docker volumes with a tiny
  1239. 57:12experiment. Before that, I would like to
  1240. 57:14show you some upgrades that we have made
  1241. 57:16to our project and added some moving
  1242. 57:18parts so that it is easier to understand
  1243. 57:21Docker volumes. Let's look at the code.
  1244. 57:23All I've done here is I have added a
  1245. 57:26file and I've added a counter that will
  1246. 57:29read this file and update the count of
  1247. 57:33visits that we make to this particular
  1248. 57:35API every time we hit this endpoint.
  1249. 57:38What it is doing is it is making the
  1250. 57:41directory if it does not exist. Then it
  1251. 57:44is counting the number of visits that we
  1252. 57:47have taken. So this will be one if there
  1253. 57:49were no visits earlier. And then it is
  1254. 57:52writing the number of count in the file
  1255. 57:55and saving it. Every time we hit it, you
  1256. 57:57see this read count is being executed.
  1257. 57:59Which means that the file will be
  1258. 58:01fetched. The current count will be read
  1259. 58:04and then it will be incremented every
  1260. 58:06time we open this particular API up. Let
  1261. 58:10me run this and show it to you again. So
  1262. 58:13I have opened up this API. I'm running
  1263. 58:17it using Docker again.
  1264. 58:20When I click on execute,
  1265. 58:22you will see that the counter is one.
  1266. 58:26When I execute again, counter is two.
  1267. 58:29Let me execute it three more times. And
  1268. 58:33the counter is five. When I go back to
  1269. 58:35my code, you will see that this is being
  1270. 58:38run in a Docker container. So, let me
  1271. 58:42stop it. I'm going to stop it and I'm
  1272. 58:47going to start this container again and
  1273. 58:49the name is hello API docker. So hello
  1274. 58:53API docker.
  1275. 58:56When I run it again and I go back to
  1276. 59:00this particular URL, let me refresh it.
  1277. 59:03So if I do control R and refresh it and
  1278. 59:07try out this count again, you will see
  1279. 59:10that the count is six. which means that
  1280. 59:12whatever changes I have made so far into
  1281. 59:15my container, it is getting stored. Let
  1282. 59:17us do another experiment. I'm going to
  1283. 59:19stop it and then I'm going to remove
  1284. 59:21this container. All I need to do is rm-f
  1285. 59:24and remove it. Let me start this
  1286. 59:26container again. And when I do this,
  1287. 59:30I can refresh.
  1288. 59:33And when I execute,
  1289. 59:36you see the counter is beginning at one.
  1290. 59:40Which means that all the data that we
  1291. 59:42had stored earlier is now deleted
  1292. 59:45just because we deleted our container.
  1293. 59:48And this should not happen because I'll
  1294. 59:49tell you in a typical developer
  1295. 59:51workflow, containers are very
  1296. 59:53disposable. Let's say you encountered
  1297. 59:56any bug. You will fix that bug in the
  1298. 59:58code. You will rebuild an image and then
  1299. 1:00:00you will restart the container. or there
  1300. 1:00:03is some runtime issue that occurred and
  1301. 1:00:05the container stopped. In that case, the
  1302. 1:00:08orchestration platform that you are
  1303. 1:00:09using, it could be Kubernetes or
  1304. 1:00:11something else, it will restart the
  1305. 1:00:13container on its own automatically. Then
  1306. 1:00:15again, your data will be gone. This is a
  1307. 1:00:18problem and this is exactly what volumes
  1308. 1:00:21solve. A volume will sit outside the
  1309. 1:00:23Docker file system and it will help you
  1310. 1:00:25persist all the data that is required.
  1311. 1:00:27For example, any media files that the
  1312. 1:00:29user uploads, any of the database
  1313. 1:00:32records that you want to save, or any
  1314. 1:00:35kind of log files, application logs that
  1315. 1:00:37you want to store for observability. All
  1316. 1:00:40of those things will live in this volume
  1317. 1:00:42outside the Docker container. Okay? And
  1318. 1:00:46to implement volumes, you don't really
  1319. 1:00:48need to make any changes in the Docker
  1320. 1:00:49file itself, but you need to make some
  1321. 1:00:53change in the command that you are
  1322. 1:00:55using. All right? So let me show you the
  1323. 1:00:57command. I'm going to go back in my
  1324. 1:01:00anti-gravity
  1325. 1:01:02and here I'm going to paste the typical
  1326. 1:01:05command that we run whenever we start
  1327. 1:01:08our docker container.
  1328. 1:01:10Okay, here I can use this dash v flag
  1329. 1:01:16and specify my volume. So first I need
  1330. 1:01:19to specify the folder in my directory in
  1331. 1:01:22my local file system where I'm going to
  1332. 1:01:25persist this volume data. I can call it
  1333. 1:01:29counter data folder and I can map it
  1334. 1:01:33with the folder where the docker file
  1335. 1:01:35system is storing this data. That folder
  1336. 1:01:39is / data. Once I do this, I will start
  1337. 1:01:44my API. And you can see the counter
  1338. 1:01:48shell start with wherever we left off.
  1339. 1:01:52So let me refresh this. And after
  1340. 1:01:55refreshing,
  1341. 1:01:57you can see when I call this execute
  1342. 1:01:59this counter,
  1343. 1:02:01it is 9. Let me execute it again, it is
  1344. 1:02:0510,
  1345. 1:02:0711. Let me go back and delete this
  1346. 1:02:10container. So
  1347. 1:02:13I'm going to
  1348. 1:02:16delete it. My container is deleted. I
  1349. 1:02:19can docker ps to confirm there is no
  1350. 1:02:22container running. Then
  1351. 1:02:25I can run this command again with the
  1352. 1:02:27volume and you will see that it will
  1353. 1:02:30begin again with 11. Okay. So let me go
  1354. 1:02:34back. I will do CtrlR to refresh
  1355. 1:02:38everything.
  1356. 1:02:40And when I execute, you see this should
  1357. 1:02:42be 12. It is 12. See? So our data
  1358. 1:02:45persist now using volume. 13 14. Yeah.
  1359. 1:02:50So this is how we implement volume in
  1360. 1:02:53Docker and they are used to persist data
  1361. 1:02:56that you still want to persist even if
  1362. 1:02:58the container restart or you delete the
  1363. 1:03:00container and you build a new one. All
  1364. 1:03:03right. In the next section, we will
  1365. 1:03:05learn about something called multi-stage
  1366. 1:03:07builds, which is another very
  1367. 1:03:09interesting concept for optimizing any
  1368. 1:03:11Docker build. Let's take a moment to
  1369. 1:03:13thank today's sponsor, Open Router.
  1370. 1:03:16Which model is your AI feature actually
  1371. 1:03:19using in production? And is anyone even
  1372. 1:03:21using it? Most teams ship an AI feature
  1373. 1:03:24and fly blind. No visibility into usage,
  1374. 1:03:28no idea if a newer model would be
  1375. 1:03:30better. Open Router fixes this with app
  1376. 1:03:33attribution. Add one header to your
  1377. 1:03:36request and your app shows up in Open
  1378. 1:03:38Router's public rankings daily, weekly,
  1379. 1:03:42and monthly alongside a full analytics
  1380. 1:03:45dashboard for your usage and token
  1381. 1:03:47trends. You also get the full model
  1382. 1:03:49catalog to compare against filterable by
  1383. 1:03:53price, context, and benchmarks all on
  1384. 1:03:56one API. 500 plus models automatic
  1385. 1:03:59fallback pass through pricing. We use
  1386. 1:04:02this to see which models our own tools
  1387. 1:04:05actually lean on instead of assuming.
  1388. 1:04:08Check out Open Router using the link in
  1389. 1:04:10the description that is open router.ai.
  1390. 1:04:12Now let's go back to our video.
  1391. 1:04:17>> In the last two sections, we learned how
  1392. 1:04:19to speed up our build time using layer
  1393. 1:04:21caching and we also learned how to
  1394. 1:04:24persist our data using the volume. In
  1395. 1:04:26this section, we are going to talk about
  1396. 1:04:28technique that can make our build times
  1397. 1:04:30even faster. Before we actually
  1398. 1:04:33understand what it is, let's look at the
  1399. 1:04:35code and understand the problem that we
  1400. 1:04:37have. So, I have my anti-gravity ID open
  1401. 1:04:41here and I've updated the code a little.
  1402. 1:04:44Let me walk you through it. Now, I have
  1403. 1:04:46my main.py, but I also have test
  1404. 1:04:49main.py. And this is what a typical dev
  1405. 1:04:52workflow looks like. You will have some
  1406. 1:04:55dependencies that are required for your
  1407. 1:04:58actual application and there are some
  1408. 1:05:00test dependencies that you may require
  1409. 1:05:03during dev time or to ensure that
  1410. 1:05:06everything is working correctly in your
  1411. 1:05:09application. I also have some
  1412. 1:05:10requirements dev.txt
  1413. 1:05:13which contains pyest and httpx and I
  1414. 1:05:16have requirements.txt that are the
  1415. 1:05:18actual dependencies that I require in
  1416. 1:05:20runtime of my application. And all of
  1417. 1:05:23this change also reflects in my docker
  1418. 1:05:25file. So when I open my docker file, you
  1419. 1:05:28can see that we are using this 3.12
  1420. 1:05:30slim. I'm creating the work there. Then
  1421. 1:05:33I'm copying all the dependencies from
  1422. 1:05:35requirement.txt
  1423. 1:05:37and I'm also installing them. After that
  1424. 1:05:40I'm copying my requirements.txt in my
  1425. 1:05:43docker file system and I'm using pip to
  1426. 1:05:46install all the dependencies in that
  1427. 1:05:48file system. Then I'm copying the
  1428. 1:05:51requirement dev.t pxt in my docker file
  1429. 1:05:54system and I'm installing all of this
  1430. 1:05:57into my docker container. After that I'm
  1431. 1:06:00copying all the code files. Then I'm
  1432. 1:06:03running this pi test main.py
  1433. 1:06:06and this will run all these tests that
  1434. 1:06:09I've written here. Right? It will test
  1435. 1:06:12the root endpoint. It will test the help
  1436. 1:06:14endpoint. And if both of these endpoint
  1437. 1:06:16pass then only my build will be
  1438. 1:06:19successful.
  1439. 1:06:21All right. And then I've specified the
  1440. 1:06:23command that it should run to start up
  1441. 1:06:24my container. You can notice that we
  1442. 1:06:27don't really need these dependencies
  1443. 1:06:30during runtime. And we do not even need
  1444. 1:06:32to do this during runtime because these
  1445. 1:06:35are all the dependencies that we had to
  1446. 1:06:37ensure that our development workflow is
  1447. 1:06:40smooth. And this is the typical case in
  1448. 1:06:42any production scenario. We have to
  1449. 1:06:44optimize this docker file. We can do
  1450. 1:06:46this by using something called
  1451. 1:06:48multi-stage builds. The idea is pretty
  1452. 1:06:50straightforward when it comes to
  1453. 1:06:52multi-stage builds. All we are saying is
  1454. 1:06:55we are going to split this whole docker
  1455. 1:06:57file into two different stages. The
  1456. 1:07:00first stage is going to be the build
  1457. 1:07:02stage where all the heavy lifting will
  1458. 1:07:03happen where we are going to download
  1459. 1:07:06all the dev dependencies. We are going
  1460. 1:07:08to run these tests and then we are going
  1461. 1:07:11to have the runtime build. In the
  1462. 1:07:14runtime build, we are only going to pick
  1463. 1:07:16up whatever we need to run the
  1464. 1:07:19application successfully. So let's
  1465. 1:07:20understand this by using some hands-on.
  1466. 1:07:23Here I have my docker file. I'm not
  1467. 1:07:24going to change anything but I'm just
  1468. 1:07:26going to calculate how much space the
  1469. 1:07:28current build will take.
  1470. 1:07:31Let me open up our terminal and I'm
  1471. 1:07:35going to use the docker
  1472. 1:07:38build command and I'm going to use the
  1473. 1:07:40tag of API single because this is a
  1474. 1:07:44single stage build right so single and
  1475. 1:07:47then dot let's see
  1476. 1:07:50it is building it and the build is
  1477. 1:07:54complete let me see what is the size of
  1478. 1:07:58the image so I will write docker
  1479. 1:08:01And you can see it is currently 150 MB.
  1480. 1:08:04So using our multi-stage build we are
  1481. 1:08:06going to see how much we can reduce
  1482. 1:08:07this. It is not going to be much because
  1483. 1:08:10we have already kept our requirements
  1484. 1:08:12very lean but still there should be some
  1485. 1:08:15improvement.
  1486. 1:08:16So what are we going to do? How are we
  1487. 1:08:19going to implement the multi-stage build
  1488. 1:08:20here? The very first thing that we need
  1489. 1:08:22to do is we need to name our build
  1490. 1:08:24stages. The first stage can be named as
  1491. 1:08:28builder
  1492. 1:08:30and we are going to use the as
  1493. 1:08:32instruction to give it a name. So I'm
  1494. 1:08:35going to call it builder. And then here
  1495. 1:08:37I can add a prefix. So I need to ensure
  1496. 1:08:41that whatever runtime dependencies I
  1497. 1:08:44install in this build stage, I should be
  1498. 1:08:46able to copy them into the runtime
  1499. 1:08:49stage. Okay? Because I do not want to
  1500. 1:08:51reinstall everything again. So that is
  1501. 1:08:54what I'm going to do. I am simply
  1502. 1:08:58going to add a prefix here that will
  1503. 1:09:02ensure that all my dependencies are in a
  1504. 1:09:05specific folder. I can name it install
  1505. 1:09:08maybe. And with this I will ensure that
  1506. 1:09:11all the dependencies that I need during
  1507. 1:09:13runtime are installed in this specific
  1508. 1:09:16folder inside Docker's file system.
  1509. 1:09:20And then let's see what else we can do.
  1510. 1:09:23This is going to remain the same. We
  1511. 1:09:25need it to run our test. And we also
  1512. 1:09:27need all the files. So you see that the
  1513. 1:09:31test domain file is importing app from
  1514. 1:09:35main. So we still need our app even in
  1515. 1:09:38the build stage. Okay. And we have this
  1516. 1:09:42piest test main.py-v.
  1517. 1:09:45We going to keep it, but we are going to
  1518. 1:09:47append the command. Let's see what the
  1519. 1:09:49command would look like. All I'm going
  1520. 1:09:51to do here is
  1521. 1:09:54I am going to
  1522. 1:09:57use the Python version that we have
  1523. 1:10:00installed in this install path. All
  1524. 1:10:02right. Through this what I'm ensuring is
  1525. 1:10:06that all the runtime dependencies are
  1526. 1:10:09also available to my Python. Okay.
  1527. 1:10:13with this run command. Our builder stage
  1528. 1:10:17is complete. So I can create a new stage
  1529. 1:10:21now and this stage can be named as a
  1530. 1:10:25runtime stage. Now remember every time
  1531. 1:10:28we are starting out a new stage we need
  1532. 1:10:31to start with another base image for us
  1533. 1:10:34the base image is going to be this and I
  1534. 1:10:37can name it runtime. All right. And here
  1535. 1:10:40I'm going to copy a few things from this
  1536. 1:10:43builder stage. Before that I will also
  1537. 1:10:46specify that the directory name here
  1538. 1:10:49should be app. So I'm going to use copy
  1539. 1:10:52and then I can use this from tag. Okay.
  1540. 1:10:56So I can use this from tag. And from
  1541. 1:11:01the name from which I'm going to copy is
  1542. 1:11:04called builder. And from builder what am
  1543. 1:11:06I going to copy? I am going to copy this
  1544. 1:11:10install folder. You see because we have
  1545. 1:11:12mentioned prefix install all these
  1546. 1:11:15libraries that are required for runtime
  1547. 1:11:18are going to be stored here. So I'm just
  1548. 1:11:20going to take all of these libraries
  1549. 1:11:24from the install folder and I'm going to
  1550. 1:11:29add them in my user local folder. Okay.
  1551. 1:11:35So what this will do is it will ensure
  1552. 1:11:38that all my dependencies are stored here
  1553. 1:11:41and then I can copy all my runtime code
  1554. 1:11:45from my file system. You can see I only
  1555. 1:11:49need the main.py here. So I'm only going
  1556. 1:11:52to copy main.py.
  1557. 1:11:55So I'll copy this main.py
  1558. 1:11:59into my app folder which is dot. Okay.
  1559. 1:12:04Once I do this, I can run the same
  1560. 1:12:07command and my build is ready. So you
  1561. 1:12:10can see there are two stages. How do we
  1562. 1:12:12know there are two stages? Because we
  1563. 1:12:14have used two base images. All right,
  1564. 1:12:17you can see this is the first one. This
  1565. 1:12:18is the second one. Let me add a comment
  1566. 1:12:22here. So I will say that this is stage
  1567. 1:12:24runtime. And let me add another comment.
  1568. 1:12:27It is always a good practice to document
  1569. 1:12:29your stages, right? So I'm just simply
  1570. 1:12:34going to add a comment saying that this
  1571. 1:12:37is stage builder. Okay, we can now run
  1572. 1:12:41our build command. So you see the image
  1573. 1:12:45is 150 earlier.
  1574. 1:12:48Let me clear this up.
  1575. 1:12:51I'm going to run this build command but
  1576. 1:12:53now I'm going to call it multi-stage.
  1577. 1:12:55Okay, let's see. So my build has
  1578. 1:12:57started.
  1579. 1:12:59Dependencies are getting installed. See
  1580. 1:13:01dependency got installed
  1581. 1:13:04and you see there are eight layers in
  1582. 1:13:06the builder image only two layers in the
  1583. 1:13:10runtime image. Okay, you can see all the
  1584. 1:13:12layers here. Now let us compare the
  1585. 1:13:14sizes. Let's see if the multi-stage
  1586. 1:13:17build has actually affected the size of
  1587. 1:13:19our image or not. So I can run docker
  1588. 1:13:23images and you can see that the
  1589. 1:13:26multi-stage build
  1590. 1:13:28is using
  1591. 1:13:30somewhat of a less amount of disk.
  1592. 1:13:34So you can see I'm building smaller
  1593. 1:13:36images here. Now our dependencies were
  1594. 1:13:40very lean. Okay, but this is really
  1595. 1:13:43useful in production scenarios where you
  1596. 1:13:45have bulked up dev dependencies that you
  1597. 1:13:47do not want in your runtime and using
  1598. 1:13:50multi-stage builds, you can keep your
  1599. 1:13:52image size leaner and your build time
  1600. 1:13:56faster. See you in the next section
  1601. 1:13:58where I'm going to talk about networks
  1602. 1:14:00in Docker. To understand networks in
  1603. 1:14:02Docker, let's do a very tiny experiment.
  1604. 1:14:06I have my anti-gravity open here and
  1605. 1:14:09I've opened up the terminal.
  1606. 1:14:11So let's build the same image that we
  1607. 1:14:15built earlier but with a twist. So you
  1608. 1:14:17see this is the command that we have
  1609. 1:14:19been using so far where we are creating
  1610. 1:14:22our container
  1611. 1:14:24and we are using this port mapping thing
  1612. 1:14:26right we use this -p right now we just
  1613. 1:14:29thought okay in our docker file we have
  1614. 1:14:32mentioned
  1615. 1:14:34port 8000 that is why we are just
  1616. 1:14:37mapping these things together right so
  1617. 1:14:40that is what we did earlier let me
  1618. 1:14:43remove it and then execute this run
  1619. 1:14:49command. So let me explain you the whole
  1620. 1:14:51anatomy of this run command.
  1621. 1:14:54We have docker run. Then I've specified
  1622. 1:14:57name of the container. Okay. After that
  1623. 1:15:00I have done this for volume mapping. And
  1624. 1:15:03then I have used this image name image
  1625. 1:15:06tag as API multistage. This command
  1626. 1:15:08simply says that use the API multi-stage
  1627. 1:15:11image to start up a container and give
  1628. 1:15:14it the name of API with volume mapped to
  1629. 1:15:19this particular folder. Okay, that is
  1630. 1:15:21what this is doing. I have removed the
  1631. 1:15:24-p port mapping flag here. Let's run
  1632. 1:15:28this and see what happens. Okay, it
  1633. 1:15:30seems like there is already a container
  1634. 1:15:33named API. So, I'm just going to remove
  1635. 1:15:35it. Let me remove this API container
  1636. 1:15:39and run it again. You see I do not have
  1637. 1:15:42this dash p flag here. When I execute
  1638. 1:15:46this, you will see that this get
  1639. 1:15:48started. But if I follow this link,
  1640. 1:15:54this is not opening. But this was not
  1641. 1:15:56opening earlier as well because I think
  1642. 1:15:58Windows does not understand 0.0.0.
  1643. 1:16:02Let's go to local host and see. Wait a
  1644. 1:16:04minute. This is also not working. Why is
  1645. 1:16:07it not working? Let's try again. Again,
  1646. 1:16:12we have the same issue.
  1647. 1:16:14When I go back to my terminal, I stop
  1648. 1:16:18this container.
  1649. 1:16:20And then
  1650. 1:16:22when I rerun it by adding this
  1651. 1:16:28-p, this time I'm going to do something
  1652. 1:16:30different. This time I'm going to name
  1653. 1:16:32this as 9,000
  1654. 1:16:35colon
  1655. 1:16:378,000. Okay,
  1656. 1:16:40let's see. Now
  1657. 1:16:43again there is already an API container.
  1658. 1:16:45So let me remove it and
  1659. 1:16:49create this container again. Remember
  1660. 1:16:539,000 col 8,000 is what we have written
  1661. 1:16:57for port mapping.
  1662. 1:16:59Let's go back
  1663. 1:17:01to our browser and try it again.
  1664. 1:17:07Again, failure. Let me change this to
  1665. 1:17:119,000.
  1666. 1:17:15We got a response.
  1667. 1:17:17When I go to docs, you will see I still
  1668. 1:17:20get a response. I can open the count
  1669. 1:17:23endpoint, try it out, and execute. And
  1670. 1:17:28you can see count is one. I will execute
  1671. 1:17:31again. Count is two. Again count is
  1672. 1:17:34three. This is working. But why did it
  1673. 1:17:38not work for port 8,000? Let me go back
  1674. 1:17:41to 8,000.
  1675. 1:17:43See, I'm getting an error. The point is
  1676. 1:17:47what I want to tell you or explain you
  1677. 1:17:49with this experiment is we need to map
  1678. 1:17:53the port of our local host with the port
  1679. 1:17:58which is running inside the container
  1680. 1:18:00because internally Docker runs in an
  1681. 1:18:04isolated process which means that Docker
  1682. 1:18:07has its own network, its own local host,
  1683. 1:18:10its own ports and until we use this port
  1684. 1:18:14mapping to expose
  1685. 1:18:15its internal ports we cannot access it.
  1686. 1:18:20All right. So even though Docker is
  1687. 1:18:22running it in its internal port 8,000 we
  1688. 1:18:26will not be able to access it until we
  1689. 1:18:28use this port mapping. That is why we
  1690. 1:18:30were using this flag ever since the
  1691. 1:18:32beginning.
  1692. 1:18:36I wanted to make our project a bit more
  1693. 1:18:38realistic for this particular section
  1694. 1:18:41which is why I have changed the
  1695. 1:18:43architecture a bit. Now I have
  1696. 1:18:46introduced radius in our system. Radius
  1697. 1:18:49will act like our database so that
  1698. 1:18:52whenever the counter is increased that
  1699. 1:18:54data is being stored in this radius
  1700. 1:18:57server. So we have to spin up radius as
  1701. 1:18:59a separate container. Fast API will be a
  1702. 1:19:01container in itself and both of them
  1703. 1:19:03will communicate every time somebody
  1704. 1:19:05executes the counter endpoint. All
  1705. 1:19:07right, let me show you how this is
  1706. 1:19:09reflected in the code itself. I'm back
  1707. 1:19:11in my anti-gravity ID and I've opened up
  1708. 1:19:14this project folder. Let's start with
  1709. 1:19:17the change that we have made here. I'm
  1710. 1:19:20not using requirements.txt anymore. I'm
  1711. 1:19:22not using pip as our package manager.
  1712. 1:19:24Now we have shifted to something called
  1713. 1:19:26UV which is a lot faster than pip and
  1714. 1:19:30this is what most of the people most of
  1715. 1:19:33the developers they use in industry
  1716. 1:19:36level project because uv is featurerich
  1717. 1:19:39and it is a lot faster than pip. It also
  1718. 1:19:42creates a log file that will provide the
  1719. 1:19:45specific wheels and the specific
  1720. 1:19:48binaries for all the dependencies that
  1721. 1:19:50you have installed. So you can see we
  1722. 1:19:52only have fast API, radius and uicon but
  1723. 1:19:56the log file will contain all the other
  1724. 1:19:58dependencies as well that these three
  1725. 1:20:00are dependent on. Then I have made some
  1726. 1:20:03minor changes in the code where I'm
  1727. 1:20:05first connecting with the radius client.
  1728. 1:20:07So radius will have its own server. It
  1729. 1:20:10will also run in my local host. I'm
  1730. 1:20:13using this radius client from radius to
  1731. 1:20:16connect with that server and then I'm
  1732. 1:20:19just incrementing the counter here.
  1733. 1:20:21Okay, this is a very basic change.
  1734. 1:20:23Radius is not the interest of this
  1735. 1:20:25course. I'm just introducing it as a new
  1736. 1:20:28service and that's all you need to know
  1737. 1:20:30about it. Let's look at the changes that
  1738. 1:20:33occur in our docker file.
  1739. 1:20:36You can see that we are still using this
  1740. 1:20:393.12 slim but now we do not have pip as
  1741. 1:20:43our package manager. We need to install
  1742. 1:20:45UV. There are two ways to do it. One I
  1743. 1:20:48go to the UV page. Okay. And you can see
  1744. 1:20:55it has this installation guide. One way
  1745. 1:20:58is I can come here. I can copy this
  1746. 1:21:01command and I can execute it using the
  1747. 1:21:05run instruction in my docker file. But
  1748. 1:21:07this will be a longer task because I
  1749. 1:21:10will first download the installer. The
  1750. 1:21:12installer will then get executed. It
  1751. 1:21:15will create the binaries and it will be
  1752. 1:21:17a longer thing. But what UV has done for
  1753. 1:21:19us is they provide us with the
  1754. 1:21:23containers directly.
  1755. 1:21:25So if I go to this particular URL, I can
  1756. 1:21:29just search for it on GitHub as well and
  1757. 1:21:31you will see that in the UV repository
  1758. 1:21:33in the packages
  1759. 1:21:35they provide the image of UV. So I can
  1760. 1:21:39directly access this binary
  1761. 1:21:41you see docker pull gcr is like github
  1762. 1:21:44container repository
  1763. 1:21:46in there they have provided the uv
  1764. 1:21:48binaries. So I can directly copy this
  1765. 1:21:50and build my image from it. So that's
  1766. 1:21:52what we have done here. We have accessed
  1767. 1:21:57this UV image, the binary from this
  1768. 1:21:59image
  1769. 1:22:01and we have copied the UV folder from
  1770. 1:22:06here to our user local bin in the file
  1771. 1:22:10system of our docker image. Okay. Then I
  1772. 1:22:14have created the work there app. I'm
  1773. 1:22:17setting up this UV project environment.
  1774. 1:22:19This is similar to the prefix flag that
  1775. 1:22:23we added in pip. So all the dependencies
  1776. 1:22:26will be stored in this optv
  1777. 1:22:30because I want to keep the dependencies
  1778. 1:22:32out of this / app. Once this is done, I
  1779. 1:22:36copy the pyro.l file which contains the
  1780. 1:22:40list of our dependencies
  1781. 1:22:42and I copy it in the file system of my
  1782. 1:22:46docker along with uv.lo. UV.lock clock
  1783. 1:22:48is also present here. So I'm copying
  1784. 1:22:50both this file and this file in my
  1785. 1:22:53docker file system. Then just like we do
  1786. 1:22:57pip install in uv we do uv sync and then
  1787. 1:23:01I'm saying that do not install any dev
  1788. 1:23:04dependencies and no install project.
  1789. 1:23:07Okay, I'm adding the appropriate flags
  1790. 1:23:09here. Then I'm copying all the files all
  1791. 1:23:13these files into my docker file system.
  1792. 1:23:16I'm setting the environment path as this
  1793. 1:23:19because this is where all my binaries
  1794. 1:23:21are installed. All my binaries like UV,
  1795. 1:23:24all the packages that I'm going to
  1796. 1:23:26install, all of those are installed
  1797. 1:23:28right here. After that, I annotate this
  1798. 1:23:33expose. This is like a documentation of
  1799. 1:23:36the ports that you are trying to expose.
  1800. 1:23:38Okay? So, I'm just mentioning that I I
  1801. 1:23:41will expose port 8,000 through this app.
  1802. 1:23:44Once this is done, we can build this
  1803. 1:23:46container.
  1804. 1:23:48And to run it, we will also require
  1805. 1:23:53the radius container. So let me show you
  1806. 1:23:56how to run this app step by step. And it
  1807. 1:23:59is going to be a long journey. I want to
  1808. 1:24:01show you the pain before actually
  1809. 1:24:04showing you how we can ease out that
  1810. 1:24:06pain with Docker Compose. So let me copy
  1811. 1:24:10the commands one by one and run them.
  1812. 1:24:13The very first thing that we need to do
  1813. 1:24:14is we need to create a network between
  1814. 1:24:19both our containers so that they are
  1815. 1:24:21able to communicate with each other. I'm
  1816. 1:24:23going to use the network command to
  1817. 1:24:25create a network with the name demonet.
  1818. 1:24:28When I press enter, you see that the
  1819. 1:24:30network is created. Now I can also
  1820. 1:24:33create a volume for my radius. All
  1821. 1:24:36right. So I can use this docker volume
  1822. 1:24:40create radius data. This will create our
  1823. 1:24:43volume and this volume will be present
  1824. 1:24:47in our docker desktop as well. So you
  1825. 1:24:50can see in volumes you will find this
  1826. 1:24:53radius data. You see this is radius
  1827. 1:24:55data. Counter data is also there. This
  1828. 1:24:58is something we created one day ago. You
  1829. 1:24:59can ignore all the rest. This is not
  1830. 1:25:02needed but just focus on this radius
  1831. 1:25:05data. Okay. And then I need to run a
  1832. 1:25:10radius server. To run this radius
  1833. 1:25:12server, I can pull it from docker hub
  1834. 1:25:17and then run it. Or I can directly use
  1835. 1:25:20this run command with the exact image
  1836. 1:25:23that I require. So I'm going to use this
  1837. 1:25:25radius 7 alpine image. And these are all
  1838. 1:25:29the same commands that you have learned
  1839. 1:25:31so far. I'm just specifying the network
  1840. 1:25:33that this radius container it needs to
  1841. 1:25:36run on the demonet that we just created.
  1842. 1:25:39It needs to use this radius data as the
  1843. 1:25:41volume
  1844. 1:25:43and I'm going to name it radius server.
  1845. 1:25:46Okay. And up and only yes means this
  1846. 1:25:48this will only get updated. All right.
  1847. 1:25:51So let me press enter. And you can see
  1848. 1:25:53it is pulling the image of radius
  1849. 1:25:57and it is going to start my container.
  1850. 1:25:59All right. Let's docker
  1851. 1:26:03ps. And you can see my radius container
  1852. 1:26:06is running. Once my radius container is
  1853. 1:26:08running, then only I can start up this
  1854. 1:26:12container. Why? Because you can see my
  1855. 1:26:16application depends on radius. All
  1856. 1:26:18right. So radius is running. I can clear
  1857. 1:26:23this out and I can say docker build-d.
  1858. 1:26:28I'm going to name it API v3. Okay, I'm
  1859. 1:26:32going to name it API v3 using colon. You
  1860. 1:26:35can specify the version and this needs
  1861. 1:26:38to happen
  1862. 1:26:41in wait let me go to the docker compose
  1863. 1:26:44and here I need to run that docker
  1864. 1:26:48build- t api col v3
  1865. 1:26:52and then I can specify the folder. So
  1866. 1:26:56this is the folder five docker compose.
  1867. 1:26:59When I press enter, you can see that my
  1868. 1:27:03container is built now. And once this is
  1869. 1:27:05built, I can start it. All I will do is
  1870. 1:27:10I'm going to run this command
  1871. 1:27:13of docker run. So let me clear this up.
  1872. 1:27:16And because my image has been built, I
  1873. 1:27:19can start up my container now. And you
  1874. 1:27:22can see I'm going to use this particular
  1875. 1:27:24command here where I've specified docker
  1876. 1:27:28run-d.
  1877. 1:27:29I'm giving it the name API and the
  1878. 1:27:32network name is demonet. Then I have
  1879. 1:27:35done the port mapping to port 8,000. And
  1880. 1:27:38then the radius host is supposed to be
  1881. 1:27:41radius. I'm supposed to provide the name
  1882. 1:27:44of the container that is running in
  1883. 1:27:46order to make that network successful.
  1884. 1:27:49And then I specified API v3 here. Okay,
  1885. 1:27:53this is the image that we are going to
  1886. 1:27:56use to run our container. When I press
  1887. 1:27:59enter, you can see my container is
  1888. 1:28:02started. So when I see docker ps, you
  1889. 1:28:05will see that the container is running.
  1890. 1:28:08Let us go to local host and test it. All
  1891. 1:28:11right. So give me a second while I pull
  1892. 1:28:14up the browser. And I need to change the
  1893. 1:28:18port to 8,000. Right? You can see with
  1894. 1:28:21port 8,000 I still have my root and
  1895. 1:28:25count and health endpoint. So I can
  1896. 1:28:28click on this try out and when I execute
  1897. 1:28:32you will see we have a new counter and
  1898. 1:28:34now this is stored in radius. All right.
  1899. 1:28:37So let's execute it few more times and
  1900. 1:28:39you can see it is all being stored into
  1901. 1:28:43radius. But do you see how painful it
  1902. 1:28:46was with just two containers? Now
  1903. 1:28:48imagine that instead of having these two
  1904. 1:28:51containers, let's say I have something
  1905. 1:28:56that does not look like these two
  1906. 1:28:58containers, but something that looks
  1907. 1:29:01like this. This is the architecture
  1908. 1:29:03diagram of an e-commerce platform. And
  1909. 1:29:07this is what they typically look like.
  1910. 1:29:09You may have multiple different services
  1911. 1:29:11written in multiple different languages.
  1912. 1:29:14They can use different kind of services
  1913. 1:29:17and containers for them like they can
  1914. 1:29:19use Kafka container, rabbit MQ, we just
  1915. 1:29:22saw radis. There can also be database
  1916. 1:29:24containers like Postgress, MongoDB
  1917. 1:29:27and some for observability. Do you think
  1918. 1:29:31it will be easy to run and start up
  1919. 1:29:35everything one by one and follow all the
  1920. 1:29:38trail of commands that we did to just
  1921. 1:29:42start up the application? No, it is
  1922. 1:29:44going to be very painful. That is why
  1923. 1:29:46the solution to this is provided by
  1924. 1:29:48something called docker compose. We are
  1925. 1:29:52going to see docker compose in detail by
  1926. 1:29:55writing a compose file for the
  1927. 1:29:57application that we just built. All
  1928. 1:30:00right. So let's get started with the
  1929. 1:30:02hands-on for docker compose. The very
  1930. 1:30:05first thing we need to do is we need to
  1931. 1:30:07create a compose file. And before this
  1932. 1:30:10by the way since I'm using windows
  1933. 1:30:13docker compose is packed with this
  1934. 1:30:15docker desktop. Okay I have docker
  1935. 1:30:17compose builtin since I downloaded this
  1936. 1:30:20docker desktop and installed it locally
  1937. 1:30:22in my computer. If you have done so on
  1938. 1:30:25Windows or in Mac, you will already have
  1939. 1:30:28Docker Compose. But in case you're using
  1940. 1:30:31a Linux machine, you need to install
  1941. 1:30:33Docker Compose. And you can just search
  1942. 1:30:37for Docker Compose install.
  1943. 1:30:41And you will say how to install Docker
  1944. 1:30:44Compose for Linux. You can just follow
  1945. 1:30:47the steps here, right? You can follow
  1946. 1:30:49the steps for Ubuntu. Uh you will see a
  1947. 1:30:52certain commands. We can follow this,
  1948. 1:30:54execute them and your docker compose
  1949. 1:30:56will be installed. A good way to check
  1950. 1:31:00if you already have docker compose or
  1951. 1:31:02not is to just run docker
  1952. 1:31:08compose
  1953. 1:31:10version. And you can see I already have
  1954. 1:31:14docker compose installed in my computer.
  1955. 1:31:17You can check it. If it is not there,
  1956. 1:31:19please follow the steps to install it.
  1957. 1:31:21it is going to be very easy. If you're
  1958. 1:31:23using something like anti-gravity or
  1959. 1:31:24cloud code, it's even easier. You can
  1960. 1:31:27ask your coding assistants to help you
  1961. 1:31:29with it. Right? So once you have your
  1962. 1:31:32docker compose installed,
  1963. 1:31:35you can create a new file and this new
  1964. 1:31:38file can be named as compose dot yaml.
  1965. 1:31:44All right, I press enter. My
  1966. 1:31:47compose.yamel
  1967. 1:31:49is ready. You can see that docker is
  1968. 1:31:50picking it up. The very first thing that
  1969. 1:31:52we need to do is we need to give a name
  1970. 1:31:55to this compost file. So I'm going to
  1971. 1:31:57call it hello API. And then I can list
  1972. 1:31:59down all the services that are required
  1973. 1:32:02to run this particular app. I can use
  1974. 1:32:04this services command. And the first
  1975. 1:32:08service that I'm going to run is radius.
  1976. 1:32:11All right. Now let me open up the
  1977. 1:32:14terminal and show you how to write a
  1978. 1:32:17compos file. You can see that this is
  1979. 1:32:19the command that we executed to start up
  1980. 1:32:21our radis container. Right? So that is
  1981. 1:32:24the same thing that I need to do here.
  1982. 1:32:26This by the way is just the name. Okay,
  1983. 1:32:29I can also call it database or something
  1984. 1:32:31else. But for the sake of naming, I'm
  1985. 1:32:35going to call it radius itself. Then you
  1986. 1:32:38need to specify the specific image that
  1987. 1:32:41you want to pull when this particular
  1988. 1:32:44container runs. So the image that I want
  1989. 1:32:46to pull you can see name is radius here
  1990. 1:32:49that's why I have given radius. Next is
  1991. 1:32:52the image I wanted to pull. So I need to
  1992. 1:32:55write image and the image name here is
  1993. 1:32:59this. It is radius alpine. I copy it and
  1994. 1:33:04I paste it here. All right. You can see
  1995. 1:33:08radius alpine is the image name. And
  1996. 1:33:11then the next thing that I need to do is
  1997. 1:33:15I need to run any kind of command. If
  1998. 1:33:17there is any kind of command that I need
  1999. 1:33:19to pass to this image to start up the
  2000. 1:33:22container, then I can specify that
  2001. 1:33:25command as well. So you can see the
  2002. 1:33:26command here is radius server append
  2003. 1:33:30only. Yes. Okay. This is the command
  2004. 1:33:33that we have specified. So I can specify
  2005. 1:33:37the command here. This is equivalent to
  2006. 1:33:40you know the run that we had. So this
  2007. 1:33:42will also execute this command in my
  2008. 1:33:45container. So I just copied it and I'll
  2009. 1:33:48paste it here. Okay. After this you can
  2010. 1:33:51see that we have also specified volume.
  2011. 1:33:54We have done some volume mapping and
  2012. 1:33:57network as well. We can do the same
  2013. 1:33:58here. We can specify the volume like
  2014. 1:34:01this. You can use the volumes. And in
  2015. 1:34:04the volumes you see that we have this
  2016. 1:34:06radius data colis. I'm going to copy it
  2017. 1:34:09and I'm going to paste it here. Ignore
  2018. 1:34:12uh this Ctrl C because of copy this came
  2019. 1:34:16up but I hope this is making sense.
  2020. 1:34:18Basically you take all the command that
  2021. 1:34:21you have used to start up any container
  2022. 1:34:24any dependent services and then and you
  2023. 1:34:27just convert them into this YAML
  2024. 1:34:29structure that docker compose expect. So
  2025. 1:34:31there are some configurations that you
  2026. 1:34:33provide whenever you use the docker run
  2027. 1:34:36command to start up a container. Those
  2028. 1:34:38are the same configurations that you
  2029. 1:34:41need to provide in this YAML compose
  2030. 1:34:44YAML file. We can build the network
  2031. 1:34:45later on. The next thing that we need to
  2032. 1:34:48do is we need to specify our fast API
  2033. 1:34:52container. So we called it API and the
  2034. 1:34:55image that we used for this API
  2035. 1:34:58container
  2036. 1:34:59is we use the build command
  2037. 1:35:03in the same directory where this compost
  2038. 1:35:05file is living. So you can see it is
  2039. 1:35:08living in this docker compose that's
  2040. 1:35:09where we went and we ran our docker
  2041. 1:35:11build do command. Right? So that's what
  2042. 1:35:14we are specifying here as well. After
  2043. 1:35:17that is there anything else that we did?
  2044. 1:35:20So let's open up our API command. And
  2045. 1:35:24you can see that we have specified some
  2046. 1:35:26environment variables. - E stands for
  2047. 1:35:28environment variables. We have also
  2048. 1:35:30specified this port mapping. We have
  2049. 1:35:33also specified the name which is API.
  2050. 1:35:36Right? So these things we need to create
  2051. 1:35:38here as well. I can specify the port
  2052. 1:35:40mapping here. And what I'm going to do
  2053. 1:35:43is I'm just going to copy paste the port
  2054. 1:35:46that we get. So we are going to use the
  2055. 1:35:49port as an environment variable here. So
  2056. 1:35:51the default value for this environment
  2057. 1:35:53is 8,000. All right. So we are mapping
  2058. 1:35:55it with 8,000. And then we can also
  2059. 1:35:58specify some environment variables that
  2060. 1:36:00we have used. So in this - e we have
  2061. 1:36:02used this radius host. So I can specify
  2062. 1:36:05environment. And you can see name and
  2063. 1:36:07value. name here is radius
  2064. 1:36:11host and value here is radius which is
  2065. 1:36:16going to be the name of the container
  2066. 1:36:18that is that our container is dependent
  2067. 1:36:20upon since we are running on a bridge
  2068. 1:36:23network. Okay, docker has multiple types
  2069. 1:36:25of network. We are using a bridge
  2070. 1:36:26network here and we also need to specify
  2071. 1:36:30if there are any dependencies for this
  2072. 1:36:32particular container. Yes, it is
  2073. 1:36:35dependent on this radius container. I
  2074. 1:36:38can specify depends on the service name
  2075. 1:36:42that it is dependent on is radius. All
  2076. 1:36:45right. So after this I can also specify
  2077. 1:36:50uh restart and stop or something else if
  2078. 1:36:52it needs to restart or anything. But no
  2079. 1:36:55the final thing that we need to do is
  2080. 1:36:57specify the volume. Volumes will be
  2081. 1:37:00radis data. All right, with this our
  2082. 1:37:03compost file is ready and we can do this
  2083. 1:37:05for as many services as we have. In the
  2084. 1:37:08diagram that I showed you, there will be
  2085. 1:37:10a compost file with all of these
  2086. 1:37:13services listed
  2087. 1:37:15like this. Okay, and the respective
  2088. 1:37:18configurations that they want to run
  2089. 1:37:21successfully. With this, our Docker
  2090. 1:37:24compos file is ready. Let me show you
  2091. 1:37:26how to execute it. Now to execute it,
  2092. 1:37:30I'm first going to remove all the
  2093. 1:37:32containers that are lying right now. So
  2094. 1:37:35you can see if I do docker ps, you see
  2095. 1:37:37we still have radius and api running. So
  2096. 1:37:42let me remove them first. To remove
  2097. 1:37:44them, I can docker
  2098. 1:37:46rm-f
  2099. 1:37:48and I can specify the name which is api
  2100. 1:37:51and radius. Right? So api and radius are
  2101. 1:37:54deleted. Let me see. I do not have
  2102. 1:37:56anything lingering here. So I can clear
  2103. 1:37:59it. And let me also delete the volume. I
  2104. 1:38:01want to delete everything. All the
  2105. 1:38:02networks, everything. I want to start
  2106. 1:38:04from scratch using this docker compose.
  2107. 1:38:07So this is all done. My docker is clean.
  2108. 1:38:11Now what I can do now is execute this
  2109. 1:38:14compost file. And to execute this
  2110. 1:38:16compost file, you simply docker compose
  2111. 1:38:20up. And then we need to specify whether
  2112. 1:38:22we need to build images first before
  2113. 1:38:24running or not. So we need to build our
  2114. 1:38:26image. That's why I will say docker
  2115. 1:38:29compos app dash built. Once I execute
  2116. 1:38:32this you will see okay I'm getting this
  2117. 1:38:34error. This is because it expects a
  2118. 1:38:36correct yl and you see I do not have any
  2119. 1:38:39space between this which is why it
  2120. 1:38:42failed. It needs to be an array when I
  2121. 1:38:46run it now. So this needed a space.
  2122. 1:38:48Okay. when I run it again.
  2123. 1:38:51Oh, even ports.
  2124. 1:38:54All right, folks. Let me ensure that the
  2125. 1:38:56YAML file is correct. Yeah, it is
  2126. 1:38:58correct. It also needed a space. This
  2127. 1:39:00should work. Now, I had also missed a
  2128. 1:39:03semicolon here. So, after adding the
  2129. 1:39:04semicolon,
  2130. 1:39:06you can see that my image has started
  2131. 1:39:10building. After running the docker build
  2132. 1:39:11command, it is pulling the radius image
  2133. 1:39:13because we just cleaned up everything.
  2134. 1:39:15Radius image is pulled. Now it is
  2135. 1:39:18building our fast API server and it is
  2136. 1:39:21pulling the Python images. You can see
  2137. 1:39:23it is now pulling the UV from this
  2138. 1:39:26GitHub container repository. UV binaries
  2139. 1:39:28are pulled and our application is
  2140. 1:39:32started. You can see our application is
  2141. 1:39:34started. I can now follow this link. So
  2142. 1:39:38let me go back
  2143. 1:39:42and you see my server is started now.
  2144. 1:39:45It will start from scratch because
  2145. 1:39:48I remember deleting the volume as well.
  2146. 1:39:51So when I execute you see count is one
  2147. 1:39:54and it is running on radius. And this
  2148. 1:39:57time we have not started all the
  2149. 1:39:59containers individually. We just
  2150. 1:40:01mentioned everything
  2151. 1:40:04in our docker compose file and we
  2152. 1:40:07started them all together at once. All
  2153. 1:40:10right. This is the beauty of docker
  2154. 1:40:11compose. With this section, we are ready
  2155. 1:40:14to now dockerize our rack chatbot. And
  2156. 1:40:18in that rack chatbot as well, when we
  2157. 1:40:21dockerize it, we are also going to build
  2158. 1:40:23another compost file. So stay tuned. See
  2159. 1:40:26you in the next section.
  2160. 1:40:30Finally, the time has come to dockerize
  2161. 1:40:32this telecom chatbot and help Peter
  2162. 1:40:35deploy his application. This is the
  2163. 1:40:37chatbot that we're going to dockerize.
  2164. 1:40:40And I'm not going to go into the
  2165. 1:40:41specifics of how this was implemented.
  2166. 1:40:43If you're really interested to learn
  2167. 1:40:45that, we will link the video somewhere
  2168. 1:40:48in the description or above. You can
  2169. 1:40:50check it out. And if you want the
  2170. 1:40:53specific code, you will find the
  2171. 1:40:54repository in the description. So let's
  2172. 1:40:57look at an overview of how this is
  2173. 1:41:00implemented.
  2174. 1:41:01You can see we are using Chromma DB
  2175. 1:41:03here, which is a vector database.
  2176. 1:41:07And this could be a separate service on
  2177. 1:41:09its own. And usually in production
  2178. 1:41:12scenarios that is how you do you have a
  2179. 1:41:14separate service for your DB. So this is
  2180. 1:41:18the first service that we will be
  2181. 1:41:20dockerizing. And then if you see then
  2182. 1:41:22this particular rack chatbot is using an
  2183. 1:41:25embedding model that is running locally
  2184. 1:41:28via hugging face. Again embedding models
  2185. 1:41:32like this are very resource intensive.
  2186. 1:41:35So it is always recommended that you run
  2187. 1:41:37them in a separate service. Okay. So
  2188. 1:41:39that it is not interfering with the main
  2189. 1:41:42chatbot.
  2190. 1:41:44So we can create a separate service for
  2191. 1:41:47this embedding model as well. And that
  2192. 1:41:49is what we are going to do. We are using
  2193. 1:41:53Quen. I think Quen is outdated now. So
  2194. 1:41:56we'll be using GP OSS. That is one
  2195. 1:41:58change I will do. But this is being
  2196. 1:42:00served by Gro API. So we do not need to
  2197. 1:42:02interfere with this. This will just be
  2198. 1:42:04like any other API call. So we have
  2199. 1:42:06three services now. One is the embedding
  2200. 1:42:09model that we are going to deploy. Then
  2201. 1:42:12we have our rack chatbot and we have our
  2202. 1:42:15chrom. Luckily for chroma, chroma
  2203. 1:42:17provides us with a docker image. So we
  2204. 1:42:20don't have to build anything from
  2205. 1:42:22scratch. We can use that image in our
  2206. 1:42:24compos file and get started with it. We
  2207. 1:42:26just need to connect it with our
  2208. 1:42:28services. But we need to create a
  2209. 1:42:31separate service for embedding model and
  2210. 1:42:34we need to extract it out of our rag and
  2211. 1:42:36provide this embedding service in form
  2212. 1:42:38of a rest API. That's exactly what we
  2213. 1:42:41have done here in the code. I'm not
  2214. 1:42:43going to show you how to implement it
  2215. 1:42:45from scratch because that won't be
  2216. 1:42:47required. So I'm back in my anti-gravity
  2217. 1:42:50and here I have created a new directory
  2218. 1:42:53for telecom rack. When I expand it, you
  2219. 1:42:56can see that I have already extracted
  2220. 1:42:58the embedding service.
  2221. 1:43:00Let me go through the code once. It is
  2222. 1:43:02very simple. All I have done is we have
  2223. 1:43:08created a fast API server. I have
  2224. 1:43:11implemented a health endpoint just to
  2225. 1:43:13let any service know that okay this is
  2226. 1:43:16healthy and it is reachable. And then in
  2227. 1:43:20this embedding path
  2228. 1:43:23we have simply used the model to encode
  2229. 1:43:25whatever text we are getting in the
  2230. 1:43:27request and then we are converting into
  2231. 1:43:29a numpy array. We are sending it out as
  2232. 1:43:32a list. This is all we have implemented
  2233. 1:43:35here. But you can see we are using
  2234. 1:43:38sentence transformer to load our model
  2235. 1:43:40into the device that is being provided
  2236. 1:43:42by the environment. So device can be
  2237. 1:43:45either CPU or CUDA. If it is CPU it will
  2238. 1:43:47use the CPU. If it is CUDA then it will
  2239. 1:43:51use the GPU. Ideally in a production
  2240. 1:43:53scenario you will implement Docker files
  2241. 1:43:55for both one which contains all the CPU
  2242. 1:43:58dependencies and one that contains all
  2243. 1:44:01the GPU dependencies. So that whenever
  2244. 1:44:04you are deploying it is just one config
  2245. 1:44:06change that you need to make. So we are
  2246. 1:44:09going to do that and we are going to
  2247. 1:44:10implement the docker file for our
  2248. 1:44:13embedding service. First there is one
  2249. 1:44:14thing I would like to discuss here which
  2250. 1:44:16is docker ignore. You might have seen it
  2251. 1:44:19in the previous few sections as well.
  2252. 1:44:23You see, we do not want docker to pick
  2253. 1:44:27up the venv or the pi cache or the bite
  2254. 1:44:31code files that get generated when we
  2255. 1:44:34are running our application locally.
  2256. 1:44:37So we are going to add them in docker
  2257. 1:44:40ignore. It is similar to git ignore like
  2258. 1:44:42for github repositories you have git
  2259. 1:44:44ignore right? So, GitHub will not pick
  2260. 1:44:46up those files for you. Using Docker
  2261. 1:44:49ignore, we are also doing the same. All
  2262. 1:44:52right. So, that is taken care of. Let me
  2263. 1:44:55create a Docker file here. As you know,
  2264. 1:44:58this is also a simple Python project,
  2265. 1:45:00right? This is also I wouldn't call it
  2266. 1:45:02simple, but this is also a Python
  2267. 1:45:05project. So, we can start with from
  2268. 1:45:10and the base image could be Python. So
  2269. 1:45:13it could be
  2270. 1:45:15python
  2271. 1:45:173.12
  2272. 1:45:19slim same one that we used earlier and
  2273. 1:45:23then
  2274. 1:45:25just like in the previous section here
  2275. 1:45:28as well we are using UV so we can copy
  2276. 1:45:32the UV image directly from GitHub
  2277. 1:45:34container repository. That's what I'm
  2278. 1:45:37going to do. So I'm going to use this
  2279. 1:45:39copy instruction to pull the image of UV
  2280. 1:45:42from GitHub container repository and
  2281. 1:45:45then place it in our user local bin UV.
  2282. 1:45:48Then there are a few environment
  2283. 1:45:52variables that we need to add. Before
  2284. 1:45:54that let me create our work div and the
  2285. 1:45:56workde is going to be app and I need to
  2286. 1:45:59pass in some environment variables.
  2287. 1:46:01Okay, let me write env.
  2288. 1:46:04And then I can provide the key and
  2289. 1:46:06value. The first one will be HF
  2290. 1:46:11home. What this environment variable
  2291. 1:46:13will do is you see any kind of model
  2292. 1:46:18that we are going to use. We will
  2293. 1:46:20download it from hugging face. Okay,
  2294. 1:46:22this is hugging face and you can see
  2295. 1:46:25this is the exact model that we are
  2296. 1:46:26going to use which is sentence
  2297. 1:46:28transformer all mini LM L6 V2. To
  2298. 1:46:30download this from hugging face we need
  2299. 1:46:33somewhere to store them right once we
  2300. 1:46:35download them we need some place to
  2301. 1:46:37store these models and that's what we
  2302. 1:46:39are specifying here hfome can be equal
  2303. 1:46:43to / models so we'll know that okay our
  2304. 1:46:47model files live here then I can use
  2305. 1:46:49this for a line break and I can define
  2306. 1:46:52my other environment variables so let me
  2307. 1:46:55define my other environment variables
  2308. 1:46:58the next one that I'm going to define
  2309. 1:47:00line is UV link
  2310. 1:47:04mode is equal to I will say copy you see
  2311. 1:47:08I'm also getting this as a suggestion
  2312. 1:47:10what this will do is soon we are going
  2313. 1:47:14to implement a cache for UV because the
  2314. 1:47:17libraries that we will install for our
  2315. 1:47:20models for loading our models for
  2316. 1:47:24running our app those can be really
  2317. 1:47:26heavy for example PyTorch itself self is
  2318. 1:47:30over a GBs right so we do not want to
  2319. 1:47:33install it every time something changes
  2320. 1:47:35in our pyrotoml
  2321. 1:47:38instead what we want to do is let's say
  2322. 1:47:40there are all these dependencies and
  2323. 1:47:43let's say I only updated the version of
  2324. 1:47:45fast API I want that only fast API
  2325. 1:47:50should get installed not all the other
  2326. 1:47:52dependencies that are present here if I
  2327. 1:47:55don't do it then all the other
  2328. 1:47:57dependencies will be installed if I
  2329. 1:47:59Don't add at the cache that we are going
  2330. 1:48:00to do in some time. UV link mode copy
  2331. 1:48:03just tells UV to do not download
  2332. 1:48:06anything from scratch but copy it from
  2333. 1:48:08the cache. So that is done. The next one
  2334. 1:48:11is path that we have been adding earlier
  2335. 1:48:15as well in the previous sections. So
  2336. 1:48:17path will allow us to find whatever
  2337. 1:48:20commands we are going to run. So all the
  2338. 1:48:22dependencies like uvcon will be stored
  2339. 1:48:24in our app.vev V env when we use u sync
  2340. 1:48:27path will just let us find those
  2341. 1:48:30dependencies easily. I've specified my
  2342. 1:48:33environment variables. The next thing
  2343. 1:48:35that I need to do is a heavy job. You
  2344. 1:48:38see that we have learned in previous
  2345. 1:48:41section when we discussed about layer
  2346. 1:48:43caching that docker will cache all the
  2347. 1:48:47layers
  2348. 1:48:48from top to bottom and anytime there is
  2349. 1:48:52some change in some layer it will run
  2350. 1:48:54everything below it from scratch.
  2351. 1:48:57We need to download a model and I can
  2352. 1:49:00show you in hugging face that these
  2353. 1:49:03model files are big. Okay. So you see 90
  2354. 1:49:08MB and we are using a smaller model but
  2355. 1:49:11let's say you use some other embedding
  2356. 1:49:13model like quen or something bigger
  2357. 1:49:16every time you change something in your
  2358. 1:49:18code that model might get downloaded
  2359. 1:49:20just because you place the code to
  2360. 1:49:23download it later. So the way we manage
  2361. 1:49:25this is by downloading the model during
  2362. 1:49:29build time and packaging it together
  2363. 1:49:32with our image. So that is what we are
  2364. 1:49:34going to do. We are going to download
  2365. 1:49:36the model right here itself. So that
  2366. 1:49:40even if we change any dependencies, even
  2367. 1:49:43if we change any code, our model does
  2368. 1:49:45not get downloaded every time we build
  2369. 1:49:48or every time we start our container.
  2370. 1:49:50I'm going to add some code here that
  2371. 1:49:52will download our model using the run
  2372. 1:49:55instruction from docker. And you can see
  2373. 1:49:58this is the code that will help us
  2374. 1:50:02download our model. UV pip install d-
  2375. 1:50:07system hugging face_hub.
  2376. 1:50:10What this is doing is it is asking UV to
  2377. 1:50:14install hugging face hub as a system
  2378. 1:50:17dependency. So you know this will run in
  2379. 1:50:20an isolated system. UV is saying that
  2380. 1:50:23download hugging face hub as a system
  2381. 1:50:25dependency.
  2382. 1:50:27Once it is downloaded run the following
  2383. 1:50:30lines using Python. Okay, these are all
  2384. 1:50:34Python code and using this Python code
  2385. 1:50:37we are going to download our model. So
  2386. 1:50:39you can see from hugging face hub we are
  2387. 1:50:41importing this snapshot download and
  2388. 1:50:44using this snapshot download you can
  2389. 1:50:45download artifacts from hugging face. To
  2390. 1:50:47download any artifact you need to
  2391. 1:50:49provide its full path. This is the full
  2392. 1:50:51path. When I go back to hugging face you
  2393. 1:50:54will see this is the full path. I can
  2394. 1:50:56copy it. Right.
  2395. 1:50:59So we specified the path of our model
  2396. 1:51:02and then I have specified ignore
  2397. 1:51:04patterns. What this is doing is it is
  2398. 1:51:07letting this snapshot download function
  2399. 1:51:11know that I do not want extensions
  2400. 1:51:15that are ending with all of these in my
  2401. 1:51:18snapshot. Let me take you back to
  2402. 1:51:20hugging face. You can see there are all
  2403. 1:51:22of these different model types. You can
  2404. 1:51:25see this ot then there is onx. We are
  2405. 1:51:28saying to snapshot download function
  2406. 1:51:31that I do not want all of that. I only
  2407. 1:51:34need this pi pytorch model bin and model
  2408. 1:51:37safe tensors file because this is what
  2409. 1:51:39we are going to use to load our models
  2410. 1:51:42as we are using pytorch. That is done.
  2411. 1:51:45Our model will now get downloaded. Next
  2412. 1:51:48thing that we can do is we can install
  2413. 1:51:52our dependencies using uv all the other
  2414. 1:51:54dependencies that we have. To do that I
  2415. 1:51:57first need to use copy and I'm going to
  2416. 1:52:00copy my pi project toml. So pi project
  2417. 1:52:03dottoml
  2418. 1:52:05and also
  2419. 1:52:07uv dot lock file. And where am I going
  2420. 1:52:10to copy it? I'm going to copy it in my
  2421. 1:52:13working directory which is app. This is
  2422. 1:52:16done. Now I can write the code to
  2423. 1:52:20install all the dependency. We are again
  2424. 1:52:22going to use the run instruction and we
  2425. 1:52:25are adding some special parameters here.
  2426. 1:52:28So you can see mount is equals to type
  2427. 1:52:31is equals to cache. So we are saying
  2428. 1:52:33mount type is cache. This is the
  2429. 1:52:35parameter that will help us cache all
  2430. 1:52:38our dependencies. So this thing that I
  2431. 1:52:40just mentioned that let's say I'm
  2432. 1:52:42changing fast API only fast API should
  2433. 1:52:44get downloaded. UV will mount a cache
  2434. 1:52:47directory. Okay, this is the cache
  2435. 1:52:49directory by the way. this root/cache/
  2436. 1:52:52uv it will mount this directory on our
  2437. 1:52:55local file system. This will not be
  2438. 1:52:58stored in the package or in the
  2439. 1:52:59container itself. It will be out of the
  2440. 1:53:02container in our file system. So that
  2441. 1:53:04even if we destroy the container or we
  2442. 1:53:07rebuild our image, this is still going
  2443. 1:53:10to be there. And then I'm going to use
  2444. 1:53:12UV sync frozen. Frozen simply means that
  2445. 1:53:16use the log file as it is. do not make
  2446. 1:53:19any updates to this log file. Okay, this
  2447. 1:53:22ensures that we are downloading the same
  2448. 1:53:24dependencies that we tested on during
  2449. 1:53:27our developer work. Then I'm using this
  2450. 1:53:31node flag. This node flag simply says
  2451. 1:53:33that do not download any kind of dev
  2452. 1:53:35dependencies. And then I'm also saying
  2453. 1:53:39no install project. This simply means
  2454. 1:53:42that only install and download the
  2455. 1:53:46dependencies. do not install the project
  2456. 1:53:48itself because we are not ready for it
  2457. 1:53:50because we we do not have the project
  2458. 1:53:52files yet. Okay, again we did this for
  2459. 1:53:55layer caching. So you see module will
  2460. 1:53:58not get downloaded if my pyro.coml files
  2461. 1:54:00are changed.
  2462. 1:54:02Dependencies will not get installed if
  2463. 1:54:05they are already cached. Only if certain
  2464. 1:54:08version changes in the pyro.coml or if
  2465. 1:54:11we change the dependency or if we add a
  2466. 1:54:14new dependency only those new
  2467. 1:54:16dependencies will be added and nothing
  2468. 1:54:18will get installed even if I change
  2469. 1:54:22something in my code. So now I can copy
  2470. 1:54:25all the files from my local file system
  2471. 1:54:29into docker and I hope you remember that
  2472. 1:54:33we have added this docker ignore files.
  2473. 1:54:35So all of these files that we do not
  2474. 1:54:37want to copy will not get copied. So if
  2475. 1:54:41you have any test files that you do not
  2476. 1:54:42want to copy in your image, you can
  2477. 1:54:44mention those files here as well. After
  2478. 1:54:47copy I can specify the command. All
  2479. 1:54:50right. So let me copy the command which
  2480. 1:54:54is also same. I'm just taking reference
  2481. 1:54:58of the previous sections. You can see uv
  2482. 1:55:02con main col app host local host and
  2483. 1:55:05port 801. We have changed the port here
  2484. 1:55:08because we will give uh this port to
  2485. 1:55:12chroma or our other service. With this
  2486. 1:55:15the docker file for CPU is ready. This
  2487. 1:55:18is CPU specific docker file. There are
  2488. 1:55:20some changes that we would need to make
  2489. 1:55:22into this docker file to make it GPU
  2490. 1:55:26compliant. So let me copy this docker
  2491. 1:55:30file. Okay, we are going to copy the
  2492. 1:55:31same docker file here it says
  2493. 1:55:35in the same directory.
  2494. 1:55:38No, I don't want to replace it. So let
  2495. 1:55:40me rename it to
  2496. 1:55:42docker file dot GPU and then I'm just
  2497. 1:55:47going to drop it here in embedding
  2498. 1:55:48service. Okay. So you see we have two
  2499. 1:55:51docker files now. Both have different
  2500. 1:55:53versions. One is for CPU and one is for
  2501. 1:55:56GPU. Let's see what all changes we need
  2502. 1:55:58to make here. The very first change is
  2503. 1:56:02because
  2504. 1:56:03we are going to run this on a GPU. We do
  2505. 1:56:07not want to run it on plain Python.
  2506. 1:56:10Okay. We also need apart from Python, we
  2507. 1:56:12also need some CUDA dependencies.
  2508. 1:56:15Remember CUDA library is a library is a
  2509. 1:56:19system package that will allow us to
  2510. 1:56:22interact with the GPU Nvidia GPUs right
  2511. 1:56:26so we need to use
  2512. 1:56:29a CUDA based system a CUDA based image
  2513. 1:56:33as our base image so that is what I'm
  2514. 1:56:36going to do so you can find these
  2515. 1:56:38PyTorch dependencies these PyTorch base
  2516. 1:56:41images on docker hub and you can use
  2517. 1:56:43them as your base image
  2518. 1:56:46Next thing is u install. All of these
  2519. 1:56:48things are going to remain same but
  2520. 1:56:50there is one environment variable that
  2521. 1:56:52we will add. Okay. So the environment
  2522. 1:56:54variable that we are going to add here
  2523. 1:56:59is
  2524. 1:57:01this one.
  2525. 1:57:03We call it device. Okay. And here the
  2526. 1:57:07device is supposed to be CUDA. Now why
  2527. 1:57:09CUDA? because CUDA specifies that okay,
  2528. 1:57:13I want to run this program on a GPU. All
  2529. 1:57:16right, this is a flag. This is going to
  2530. 1:57:18be a flag for PyTorch or Sentence
  2531. 1:57:22Transformer, any libraries that we are
  2532. 1:57:23using that use the GPU for running this
  2533. 1:57:27application. All right. Also, I'm going
  2534. 1:57:30to use another environment variable
  2535. 1:57:33here, which is UV brake system packages
  2536. 1:57:36equals 1. Which means that if there are
  2537. 1:57:38any system packages that are installed,
  2538. 1:57:40feel free to add on to them or update
  2539. 1:57:44them or whatever based on our pipro.com.
  2540. 1:57:48File. Okay. So, we are done with this.
  2541. 1:57:50We can keep everything else as it is. So
  2542. 1:57:53our implementation of the docker files
  2543. 1:57:57for our embedding service is done and
  2544. 1:58:00I'll tell you for the rack service it is
  2545. 1:58:02very easy.
  2546. 1:58:04Why? Because rack service is not very
  2547. 1:58:08different from all the other services
  2548. 1:58:10that we have used so far. I'm going to
  2549. 1:58:13add a docker file in our rack service as
  2550. 1:58:17well.
  2551. 1:58:20And in this docker file we are going to
  2552. 1:58:23use from this is also python 32
  2553. 1:58:29slim. Yeah this is the one that we are
  2554. 1:58:32going to use and I also need my UV. So
  2555. 1:58:38we can add UV here. I need to specify my
  2556. 1:58:42work there which is going to be app.
  2557. 1:58:45Also app is just for convention. You can
  2558. 1:58:48give it any other name as well. You can
  2559. 1:58:50give it rack or telecom chatbot. Up to
  2560. 1:58:53you. But you have to be consistent at
  2561. 1:58:56other places. All right. I'm also adding
  2562. 1:58:59a few environment variables. For
  2563. 1:59:00example, UV link copy. This is something
  2564. 1:59:02that we saw when we were adding the
  2565. 1:59:06docker file for embedding service and
  2566. 1:59:08also the path. Then we have Python
  2567. 1:59:11unbuffered
  2568. 1:59:13equals 1 which means that I do not want
  2569. 1:59:16Python to wait or buffer while printing
  2570. 1:59:20the log statements. I wanted to print it
  2571. 1:59:23as they occur. This enables you know
  2572. 1:59:25this makes debugging easier.
  2573. 1:59:28And then after our environment variables
  2574. 1:59:32are set we already have UV. So we can
  2575. 1:59:35start installing our dependencies. We
  2576. 1:59:38can use copy
  2577. 1:59:40same pi dot
  2578. 1:59:44terminal uv dot lock and also our
  2579. 1:59:48destination which is dot. Now I can use
  2580. 1:59:51u sync to install all these dependencies
  2581. 1:59:54that we have. So I'm going to use the
  2582. 1:59:58run command and using the run command
  2583. 2:00:00you see we have done the same thing. We
  2584. 2:00:02have used d-mount type cache target is
  2585. 2:00:07root cache uv and same kind of
  2586. 2:00:10configuration that we used earlier. We
  2587. 2:00:13could very well copy the docker command
  2588. 2:00:14and just add uh like remove all the
  2589. 2:00:18model download related stuff and use it.
  2590. 2:00:21So here as well I'm going to copy
  2591. 2:00:26everything that we have all the packages
  2592. 2:00:28that we have into this docker instance
  2593. 2:00:32and then we are using streamllet to
  2594. 2:00:36build our rack chatbot. You can see this
  2595. 2:00:39is going to use streamllet. It has
  2596. 2:00:41streamlit as a dependency. So we are
  2597. 2:00:44going to use the streamlit run command
  2598. 2:00:46in our cmd instruction. So you can see
  2599. 2:00:49cmd run source/app.py
  2600. 2:00:53this is our entry point into the
  2601. 2:00:55application. You can see app.py is our
  2602. 2:00:57entry point into the application. After
  2603. 2:01:00this is done we are providing the server
  2604. 2:01:02address server port and we are keeping
  2605. 2:01:05the server headless equals true. So with
  2606. 2:01:08this our docker files are ready. Next
  2607. 2:01:12thing that we need to do is we are not
  2608. 2:01:15going to run each file one by one like a
  2609. 2:01:17caveman. Instead we are going to utilize
  2610. 2:01:21the technique that we learned in the
  2611. 2:01:22previous section which is we are going
  2612. 2:01:24to create docker compose files so that
  2613. 2:01:28our docker compose files contains the
  2614. 2:01:30list of all the services that we are
  2615. 2:01:34going to run. So I will just name it
  2616. 2:01:37compose dot yl
  2617. 2:01:41and we know the drill how to build our
  2618. 2:01:44compos file. The very first thing that
  2619. 2:01:47we are going to start with is going to
  2620. 2:01:50be the name and the name here is going
  2621. 2:01:54to be telecom
  2622. 2:01:56rag and then I can specify all the
  2623. 2:02:00services that I want here. So I'm going
  2624. 2:02:03to do it quickly because we already saw
  2625. 2:02:06how to write a compos file and then I'm
  2626. 2:02:09not going to write each and every line
  2627. 2:02:11here because we have already seen this
  2628. 2:02:14in the previous section. But I'll
  2629. 2:02:16explain you whatever we are adding right
  2630. 2:02:19here. Okay. So I'm adding the embedding
  2631. 2:02:22service first. And in the build command,
  2632. 2:02:25we are providing the name of the folder
  2633. 2:02:28that we need to build. In the previous
  2634. 2:02:30section, we did not do this. We just
  2635. 2:02:32specified dot because that was the
  2636. 2:02:34folder where our docker file was there.
  2637. 2:02:37But here you see the embedded service is
  2638. 2:02:40inside this embedding service directory.
  2639. 2:02:43And we have our docker file here. Okay.
  2640. 2:02:46So that's what we are going to do here.
  2641. 2:02:48And this compos file is by the way for
  2642. 2:02:51the CPU. That is why we are naming the
  2643. 2:02:53image. This is the image tag that we are
  2644. 2:02:55giving. It is token embeddings colon CPU
  2645. 2:03:00and it can use maximum 3GBs of memory.
  2646. 2:03:03That is what we are specifying in the
  2647. 2:03:04memory limit. Next one that we can build
  2648. 2:03:07is chroma. So I'm just going to name it
  2649. 2:03:11chroma. And this chroma will use the
  2650. 2:03:15image that I'm going to describe as
  2651. 2:03:18follows. And we will also add some
  2652. 2:03:21volume here. So let me specify the
  2653. 2:03:24image.
  2654. 2:03:26Okay, this is the Chroma DB image that
  2655. 2:03:29will be pulled from DockerHub. All
  2656. 2:03:32right, then I'm specifying the volume
  2657. 2:03:34here.
  2658. 2:03:37And with this, my configuration for
  2659. 2:03:39Chroma is ready. Exactly. That's it
  2660. 2:03:43because Chromad is already providing us
  2661. 2:03:45with this image. And the final one which
  2662. 2:03:48is our rag app that is going to be a bit
  2663. 2:03:53longer. So let me go step by step here.
  2664. 2:03:56Okay. Here as well let me add the known
  2665. 2:04:00things that we are anyways going to add.
  2666. 2:04:03You can see the very first thing that we
  2667. 2:04:05need is the build context. You know that
  2668. 2:04:08we want to build this dot / rag app
  2669. 2:04:12folder. That's what I have specified
  2670. 2:04:14here. This is a short hand for providing
  2671. 2:04:16the context. And then I'm giving this
  2672. 2:04:18image the tag of telecom rag app v1. And
  2673. 2:04:24I have specified the memory limit to be
  2674. 2:04:272 GBs. Then we need to map the ports. So
  2675. 2:04:32I can specify the ports here. It is 8501
  2676. 2:04:35col 8501. These are the ports where
  2677. 2:04:38streamllet will be running. And after
  2678. 2:04:41specifying the ports, we also need to
  2679. 2:04:44specify the environment variables that
  2680. 2:04:46we need to pick up for this application.
  2681. 2:04:50And you see that in rag app, we already
  2682. 2:04:53have this env file. And you can see what
  2683. 2:04:57this file contains. I hope you remember
  2684. 2:05:00from the GitHub repo right here that we
  2685. 2:05:04are using grock for inference. Okay? And
  2686. 2:05:08because we are using grock for
  2687. 2:05:09inference, we will need this environment
  2688. 2:05:13variable which is grock API key. So to
  2689. 2:05:16get this environment variable, we need
  2690. 2:05:18to specify from where we will be picking
  2691. 2:05:21up this environment variable. All right.
  2692. 2:05:24So that we can specify using this env
  2693. 2:05:28file. We can also use environment to
  2694. 2:05:30directly provide it uh while running the
  2695. 2:05:33container. But since we already have the
  2696. 2:05:35file, I am adding this environment file
  2697. 2:05:38here. Okay. So in this environment file,
  2698. 2:05:41the very first thing that we need to add
  2699. 2:05:43is the path of where this is stored. And
  2700. 2:05:47this is stored in rag app/
  2701. 2:05:52dot env. We have specified the path and
  2702. 2:05:56we are saying that this is required.
  2703. 2:05:59Okay. If this is not provided then this
  2704. 2:06:02will cause an error. I seem to be
  2705. 2:06:05missing tab here. Okay, our environment
  2706. 2:06:08file is ready. Next thing that we going
  2707. 2:06:11to do is we are going to add some
  2708. 2:06:14environment variables for Chroma. So you
  2709. 2:06:16will see in the source code wherever we
  2710. 2:06:19are ingesting something. So let's say
  2711. 2:06:21wherever we are ingesting our FAQs or
  2712. 2:06:25something like this, we need this chrom
  2713. 2:06:27host and port ID so that the chroma
  2714. 2:06:31client can connect with it. You see we
  2715. 2:06:34have this chromtp client. This needs to
  2716. 2:06:37connect to this host and port. So we
  2717. 2:06:41need to provide that as well. I'm going
  2718. 2:06:45to use all of this environments to
  2719. 2:06:47provide that and we'll discuss this
  2720. 2:06:49environment variable in a while. So see
  2721. 2:06:52we are specifying the embedding host
  2722. 2:06:54which is our embedding service col
  2723. 2:06:5780001. This is the port where our
  2724. 2:06:59embedding service is running. And since
  2725. 2:07:02all of these services will be running in
  2726. 2:07:05our docker environment.
  2727. 2:07:07We can use these docker network namings.
  2728. 2:07:10Okay. So we can use the service name
  2729. 2:07:13itself. The service name is embeddings.
  2730. 2:07:15http/
  2731. 2:07:16embeddings colon the port number where
  2732. 2:07:19it is running as chroma host we can
  2733. 2:07:22specify the name of the service which is
  2734. 2:07:24chroma and as port we can specify 8,000
  2735. 2:07:27because that is where chroma is going to
  2736. 2:07:29run after that we need to provide the
  2737. 2:07:32services that our rag app service
  2738. 2:07:35depends on so we can specify it depends
  2739. 2:07:39on embeddings
  2740. 2:07:41okay and it also depends on
  2741. 2:07:46chroma. All right. So these are the
  2742. 2:07:48services that it depends on and we can
  2743. 2:07:51mention it here. All right. Now we can
  2744. 2:07:55specify the volume. You see we are using
  2745. 2:07:58the volumes only for chroma. So this is
  2746. 2:08:00what we can specify here as well. Okay.
  2747. 2:08:04So in my volumes I am using chroma data.
  2748. 2:08:09So let me copy the name chroma data.
  2749. 2:08:11Okay. And this has to be a mapping. So
  2750. 2:08:13colon with this our compos file is ready
  2751. 2:08:16for the CPU. Let me keep it open
  2752. 2:08:19actually and I'm going to open up the
  2753. 2:08:21terminal and I'm going to run this
  2754. 2:08:25compose file. So first I need to go to
  2755. 2:08:28this particular directory which is
  2756. 2:08:30telecom rack. This is where our compose
  2757. 2:08:34file is. Now I can use docker compose
  2758. 2:08:39dash
  2759. 2:08:40compose up dash build command.
  2760. 2:08:46You can see it is pulling the chrom
  2761. 2:08:48image. It will complete all the build
  2762. 2:08:50steps and it will start up the
  2763. 2:08:52container. So let's wait for it to
  2764. 2:08:54complete.
  2765. 2:08:55>> A few moments later.
  2766. 2:08:57>> All right. After waiting for a couple of
  2767. 2:08:59minutes, my applications are up and
  2768. 2:09:03running.
  2769. 2:09:04You can see this is the embedding. This
  2770. 2:09:08is the rag app. And we also have our
  2771. 2:09:12chroma with a beautiful logo. The
  2772. 2:09:14applications are all up. So I can go to
  2773. 2:09:16localhost
  2774. 2:09:188501.
  2775. 2:09:20I can follow this link. You see our high
  2776. 2:09:24API already running. And you can see our
  2777. 2:09:26telecom support assistant is ready here.
  2778. 2:09:30Let's test it out with some query. So,
  2779. 2:09:34let's say why my bill is higher. No,
  2780. 2:09:37this is not a good one. Uh, I was
  2781. 2:09:39charged for roaming. How do I enable
  2782. 2:09:41Wi-Fi calling? This seems like a good
  2783. 2:09:43one, but I'm going to tell you this is
  2784. 2:09:45not going to work. We going to discuss
  2785. 2:09:48why, but
  2786. 2:09:50keep watching. I'm sorry I do not have
  2787. 2:09:52enough information in the provided
  2788. 2:09:54resource. This is happening because we
  2789. 2:09:58have built our image but we have not
  2790. 2:10:02ingested the documents that it requires.
  2791. 2:10:05So let me show you in the code itself.
  2792. 2:10:08You can see everything was successful.
  2793. 2:10:12But when I open up and I'm sorry if let
  2794. 2:10:17me close this. when I open up
  2795. 2:10:21my source code here, you can see there
  2796. 2:10:24are these ingest modules that I need to
  2797. 2:10:27run. Okay, these modules that will
  2798. 2:10:33ingest all of the documents that are
  2799. 2:10:35present in my data set. You see the
  2800. 2:10:37telecom guide which is a PDF, FAQs,
  2801. 2:10:40everything that is present in this
  2802. 2:10:42ticket DB. We want all of these to be
  2803. 2:10:45ingested into our vector store which is
  2804. 2:10:47our Chromma DB so that they work. And
  2805. 2:10:50these kind of cases are very common in
  2806. 2:10:51the industry where you have a service
  2807. 2:10:54and you need to ingest or you need to
  2808. 2:10:57add in a few resources or there are some
  2809. 2:11:02data that you need to some seed data
  2810. 2:11:04that you need to upload. For all of
  2811. 2:11:07those cases, we can use the docker
  2812. 2:11:09compose run command. And I'm going to
  2813. 2:11:12show you how. So you saw all the ingest
  2814. 2:11:16files that we have. We are going to run
  2815. 2:11:19them one by one. In the source in the
  2816. 2:11:21ingest, you see FAQ, PDF and tickets. We
  2817. 2:11:25are going to run all of these files one
  2818. 2:11:28by one so that they can ingest the
  2819. 2:11:31relevant data. And I can use this
  2820. 2:11:34particular docker command to do this.
  2821. 2:11:36Here we are saying docker compose run- r
  2822. 2:11:42which means that close this container as
  2823. 2:11:44soon as this command runs. So we are
  2824. 2:11:46temporarily opening up this container
  2825. 2:11:48and then in the rag app use this
  2826. 2:11:53particular command which is python-m
  2827. 2:11:55src.est.faq.
  2828. 2:11:58This will simply run the faq.py in this
  2829. 2:12:01docker environment. Okay. And once this
  2830. 2:12:04file execution completes, it will exit
  2831. 2:12:06out of this environment. So let me
  2832. 2:12:11press enter and run this. Okay, I'm in
  2833. 2:12:14the docker tutorial. So I need to cd
  2834. 2:12:17into
  2835. 2:12:18the telecom rag. And here I can run
  2836. 2:12:22this. All right, you see docker compose
  2837. 2:12:25run
  2838. 2:12:27and this is running. This will ingest
  2839. 2:12:29all the PDFs into my chrom. You see
  2840. 2:12:34everything is stored in chromb. Now
  2841. 2:12:36instead of faq let me clear this. I can
  2842. 2:12:40also run rest of the files. One is
  2843. 2:12:43called tickets. So this will ingest
  2844. 2:12:45everything that is present in the ticket
  2845. 2:12:47db into chromb.
  2846. 2:12:50Okay. Let me run another one which is
  2847. 2:12:54called PDF. Okay. So PDF. This will
  2848. 2:12:59ingest the PDF guide that we had.
  2849. 2:13:02So this may take some time. Okay, it has
  2850. 2:13:06ingested 37 chunks into Chrom. So with
  2851. 2:13:09this you will see that all of the data
  2852. 2:13:14is ingested now and my app should be
  2853. 2:13:18ready to answer me. Let me go back to
  2854. 2:13:21the app. I can clear all the
  2855. 2:13:23conversations
  2856. 2:13:25and when I ask it about how do I enable
  2857. 2:13:29Wi-Fi calling? Now it is giving me the
  2858. 2:13:31exact response from the guides. Okay.
  2859. 2:13:34And if I ask how do I activate
  2860. 2:13:36international roaming? You can see if I
  2861. 2:13:39ask it my phone SIM is not detected
  2862. 2:13:41after a restart. So you can see it is
  2863. 2:13:43answering all these questions based on
  2864. 2:13:45the data that we have provided and
  2865. 2:13:48ingested into the vector DB. Our telecom
  2866. 2:13:51rack chatbot is working. It is also
  2867. 2:13:54deployable now thanks to docker. I hope
  2868. 2:13:57Bruce will be happy.
  2869. 2:14:01All right, but let's not get very happy
  2870. 2:14:03yet because we have only developed the
  2871. 2:14:06docker compos file for our CPU. We are
  2872. 2:14:10not running the model in our GPU. To run
  2873. 2:14:12it in a GPU, we need to create another
  2874. 2:14:15docker compos file that I've already
  2875. 2:14:17created here for you. Let me explain
  2876. 2:14:21what I've done here. I'm going to name
  2877. 2:14:23it compose.gpu.yamel.
  2878. 2:14:26And here this docker file is created to
  2879. 2:14:30override the original one. Okay. So
  2880. 2:14:33whatever service I'm going to override
  2881. 2:14:35through this particular YAML file. I
  2882. 2:14:39need to name the same service right
  2883. 2:14:41here. So you can see I've named it
  2884. 2:14:43embeddings. Let me open up the original
  2885. 2:14:45file side by side as well. You can see
  2886. 2:14:48this service is called embeddings. And
  2887. 2:14:51here as well we have named it embedded.
  2888. 2:14:53But here in the context we are
  2889. 2:14:55specifying that this needs to use the
  2890. 2:14:58docker file GPU that we created. Okay.
  2891. 2:15:02So here is the docker file GPU that we
  2892. 2:15:06have created and it is very specific to
  2893. 2:15:09all the CUDA dependencies that we need
  2894. 2:15:11here. By the way, there is one thing
  2895. 2:15:14that we had to add here which we missed
  2896. 2:15:17when we were updating it when we copy
  2897. 2:15:20pasted the original one and appended it
  2898. 2:15:23to BGPU native. You can see since we are
  2899. 2:15:28already installing torch as a system
  2900. 2:15:31dependency here we need to add no
  2901. 2:15:35install package and specify torch. So
  2902. 2:15:38this will ensure that it is not
  2903. 2:15:40reinstalling torch for us and we have
  2904. 2:15:43also specified that okay use like system
  2905. 2:15:45side packages whatever environment
  2906. 2:15:48you're using for system side packages
  2907. 2:15:50use that environment only. So with these
  2908. 2:15:52two our torch dependency will be
  2909. 2:15:54installed which is really required to
  2910. 2:15:57run our model on a GPU and then we have
  2911. 2:15:59specified that docker file here we are
  2912. 2:16:02naming it telecom embedding col GPU and
  2913. 2:16:05we have increased the memory limit to
  2914. 2:16:076GB because CUDA needs more memory to
  2915. 2:16:10run. Then in the environment we have
  2916. 2:16:12specified the device to be CUDA which
  2917. 2:16:15will ensure that this runs in a GPU and
  2918. 2:16:19we have also provided this deploy
  2919. 2:16:21instruction where we have said that you
  2920. 2:16:26ensure that Nvidia driver and CUDA
  2921. 2:16:29capabilities are present in whatever
  2922. 2:16:32resources this particular image is being
  2923. 2:16:36deployed to. If these resources do not
  2924. 2:16:40meet then this image will fail. Okay,
  2925. 2:16:43this docker file will not start up. So
  2926. 2:16:45with this our compose files are ready
  2927. 2:16:48and let me show you how to run it using
  2928. 2:16:52docker compose. So we are still going to
  2929. 2:16:54use docker compose. We're going to write
  2930. 2:16:57docker compose and we need to specify
  2931. 2:17:00the file that we want to refer here. So
  2932. 2:17:03the file we have two files. one is
  2933. 2:17:06compost.l yl. Okay, compose dot yl and
  2934. 2:17:12the other file that we have so another f
  2935. 2:17:15and another file that we have is compose
  2936. 2:17:18gpu.yamel. Then we can write up and
  2937. 2:17:21specify the build command. So I've
  2938. 2:17:24already run this because this takes a
  2939. 2:17:25lot of time. It will install all the
  2940. 2:17:28cuda dependencies. It will build the
  2941. 2:17:30image again.
  2942. 2:17:32So because of layer caching, this should
  2943. 2:17:34not take more time here. It should just
  2944. 2:17:37use the cacheed layers and it should
  2945. 2:17:41start up my application. You can see my
  2946. 2:17:44application is started now. It is
  2947. 2:17:46loading the weights using PyTorch. It
  2948. 2:17:50has loaded and my embedding service is
  2949. 2:17:52also ready here. And with this, our
  2950. 2:17:55solution is ready to get deployed.
  2951. 2:17:57Congratulations for whatever you have
  2952. 2:17:58built so far. Remember the homework and
  2953. 2:18:00try it out. Okay? Try multi-stage
  2954. 2:18:02builds. Try all the other capabilities
  2955. 2:18:04of Docker that we talked about and
  2956. 2:18:06mention in the comments what you found
  2957. 2:18:08interesting.
  2958. 2:18:14The next morning on the client server,
  2959. 2:18:17Bruce asks the same roaming question and
  2960. 2:18:19gets the right answer. The green drains
  2961. 2:18:22from his face. It works on their machine
  2962. 2:18:25now. Well, any machine with Docker
  2963. 2:18:28really. and Peter's laptop. It gets to
  2964. 2:18:32stay home.

About this transcript

This page contains the full transcript of Docker Tutorial for Beginners by codebasics, generated from the public captions YouTube serves with the video. The transcript has 20,758 words across 2,964 segments, with the original timestamps preserved so you can click any line to jump to that moment in the embedded player.

What you can do with it

Use the transcript to take notes, quote the speaker, build a study guide, generate a summary with ChatGPT or Claude via the YouTube Summary tool, or export it as a timed subtitle file with YouTube to SRT. You can also re-open it in the transcriber to choose a caption track available on the video.

Free YouTube transcript tool

YouTube2Text is a free YouTube transcript generator — no signup, no daily limit. Paste any YouTube link and get the full transcript instantly, with timestamps, click-to-jump, caption language selection, AI prompts for ChatGPT, Claude, and Gemini, and exports to TXT, SRT, VTT, or Markdown.