Docker - Making Real Projects with Docker

上一张介绍了如何 build 一个客製化的 image 与 build image 未遇到的问题与步骤,接着要来 build 一个真正的专案,虽然说是真正的专案但也只是一个简易版的 NodeJS Web Server,不过方法与流程都大同小异,所以可以透过这个步骤创建出各种不同的 image。
img


NodeJS Server Setup

首先先创建一个新的资料夹并命名为 simpleNodeServer 并进到资料夹中

mkdir simpleNodeServercd simpleNodeServer

接着创建一个 package.json file,并对他进行编辑

touch package.jsonvim package.json
{"devDependencies": {    "@types/express": "^4.17.13",    "@types/node": "^17.0.23",    "nodemon": "^2.0.15",    "ts-node": "^10.7.0",    "typescript": "^4.6.3","express": "^4.17.3"  },"scripts": {    "start": "nodemon index.ts"  },}

接着创建一个 index.ts file 并输入预设的内容以创建一个基本的 nodeJS Server

touch index.tsvim index.ts
import express from 'express';const app = express();const port = 3000;app.get('/', (req, res) => {  res.send('The server is working!');});app.listen(port, () => {  console.log(`server is listening on ${port} !!!`);});

Create Dockerfile

设定完基础的 NodeJS Server 后,接着来设定 Dockerfile

touch Dockerfilevim Dockerfile
FROM alpineRUN npm installCMD ["npm", "start"]
FROM: 透过 base image 创建一个新的 ImageRUN: 创建 Image 时须要执行的命令CMD: 利用这个 Image 创建 Container 后预设所执行的指令

以上面的例子来说,会利用 apline 这个 base image 创建一个 Image,接着在创建 Image 的过程中需要执行 npm install 这个命令,最后当利用这个 Image 创建一个 Container 时会预设执行 npm start 这个指令。

接着使用 docker build . 来将这个 NodeJS Server Image build 起来

docker build .

接着可以看到我们的终端机中出现了错误,主要是因为 npm: not found
http://img2.58codes.com/2024/20124767QBoohvoQ7Z.png


Base Image Issues

会造成这样的原因是因为我们使用的 base image 是一个非常小的 Image,所以他并不包含能够执行 npm install 的内容,所以应该要使用带有可以执行 npm installbase image

FROM node:14-alpineRUN npm installCMD ["npm", "start"]

http://img2.58codes.com/2024/20124767iicqolujQy.png

透过使用可以执行 npm install 的 base image 便可以将 NodeJS Server build 出来,接着利用 image id 创建一个 Container 吧。

docker run -it 15c18aa74f8507a785e96a45f17b54b34d365d8774c99e7d239da45a3aecbdfb

http://img2.58codes.com/2024/20124767hasRCR3Ubg.png

还是出错了!这次出错的问题在于当在创建 image 时所执行的 npm install 他需要有 package.json 才能够正常执行,而我们创建的 image 中并没有 package.json file,所以导致错误。


A Few Missing Files

所以我们需要将我们需要附加在的档案加在 Image 中,这样在创建 Image 时才能够完整的执行指令,这时就需要利用 COPY 指令複製档案到 Image 中。
http://img2.58codes.com/2024/20124767kIu2Jwxf5h.png

利用 COPY 指令将我们机器上面的某个档案(相对路径)複製到 Container 中的某个位置上,所以我们需要将我们机器中的所有档案都複製到 Container 中

FROM node:14-alpineCOPY ./ ./RUN npm installCMD ["npm", "start"]

这样的话当我们使用 image 创建 Container 时就有 package.json file 可以提供给 npm install 指令使用。

docker build .## [+] Building 9.8s (9/9) FINISHED## => [internal] load build definition from Dockerfile                                                                              0.0s## => => transferring dockerfile: 112B                                                                                              0.0s## => [internal] load .dockerignore                                                                                                 0.0s## => => transferring context: 2B                                                                                                   0.0s## => [internal] load metadata for docker.io/library/node:14-alpine                                                                 2.5s## => [auth] library/node:pull token for registry-1.docker.io                                                                       0.0s## => [internal] load build context                                                                                                 0.1s## => => transferring context: 684B                                                                                                 0.1s## => CACHED [1/3] FROM docker.io/library/node:14-alpine@sha256:87641a998f00bee1bad8ad15e00f05ac41d96a7093a6b50c5cf8540dda1b65a6    0.0s## => [2/3] COPY ./ ./                                                                                                              0.0s## => [3/3] RUN npm install                                                                                                         6.7s## => exporting to image                                                                                                            0.3s## => => exporting layers                                                                                                           0.3s## => => writing image sha256:09de30e6003ab9adb4de287dbf8ffa72201941bf537681c10f5c5742e2c427c6                                      0.0s## Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix themdocker run -it 09de30e6003ab9adb4de287dbf8ffa72201941bf537681c10f5c5742e2c427c6## > @ start /## > nodemon index.ts## [nodemon] 2.0.15## [nodemon] to restart at any time, enter `rs`## [nodemon] watching path(s): *.*## [nodemon] watching extensions: ts,json## [nodemon] starting `ts-node index.ts`## server is listening on 3000 !!!

接着我们到网页中看看 localhost: 3000 是否有东西

http://img2.58codes.com/2024/20124767vwQULNwRUr.png


Container Port Mapping

简单来说我们在 Container 中执行了 npm start 确实是有在 [localhost](http://localhost) 3000 成功创建一个 Server,但这个 localhost 3000 是在 Container 中的 Port ,所以我们本机的 localhost 无法成功连结到 Container 的 localhost
http://img2.58codes.com/2024/20124767otzQD7FJ6W.png

所以我们要做的就是将我们本机的 Port 3000 连结到 Container 内部的 Port 3000,这样都浏览器向 localhost: 3000 发出请求时,就可以连接到 Container 中的 Port 3000。
http://img2.58codes.com/2024/201247674LsstT9hpd.png

所以可以使用 docker run 中的其中一个 Option
http://img2.58codes.com/2024/20124767gVOEkL8G0M.png

利用 -p option 将本机指定的 Port 连接到 Container 中指定的 Port

docker run -it -p 3000:3000 09de30e6003ab9adb4de287dbf8ffa72201941bf537681c10f5c5742e2c427c6

http://img2.58codes.com/2024/20124767W9Mh7vBrox.png

这样就可以顺利连结到 localhost: 3000 了,由于是将本机的 Port 连接到 Container 的 Port,所以也可以

docker run -it -p 8080:3000 09de30e6003ab9adb4de287dbf8ffa72201941bf537681c10f5c5742e2c427c6

这样就会变成本机的 Port 8080 连接到 Container 的 Port 3000。


Specifying a Working Directory

我们可以开启另一个终端机看一下透过 NodeJS Server Image 创建的 Container 裏面的结构

docker ps## CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS                    NAMES## b38d0ae47f41   09de30e6003a   "docker-entrypoint.s…"   3 minutes ago   Up 3 minutes   0.0.0.0:3000->3000/tcp   practical_jepsendocker exec -it 00302f0de178 sh/ # ls## Dockerfile         index.ts           opt                run                usr## bin                lib                package-lock.json  sbin               var## dev                media              package.json       srv## etc                mnt                proc               sys## home               node_modules       root               tmp

透过 ls 指令可以看到我们利用 COPY 指令複製的所有档案都放在 Container 的根目录,这样对于大型的 Docker Project 来说会很难管理,所以可以利用 WORKDIR 指令决定要将複製的档案放在哪一个资料夹。

FROM node:14-alpineWORKDIR /usr/appCOPY ./ ./RUN npm installCMD ["npm", "start"]

这将就可以将複製的档案通通存放在 /usr/app 当中了。

在我们 build image 的时候可以多添加一个 option -t ,主要的目的是可以将这个 Image 新增一个 tag,这样当要 run 这个 image 时就可以使用这个 tag 而不用使用 image id。

docker build -t fandix/image/1 .docker run -it -p 3000:3000 fandix/image/1## > @ start /usr/app## > nodemon index.ts## [nodemon] 2.0.15## [nodemon] to restart at any time, enter `rs`## [nodemon] watching path(s): *.*## [nodemon] watching extensions: ts,json## [nodemon] starting `ts-node index.ts`## server is listening on 3000 !!!

接着开启另一个终端机观看一下 Container 的内部状况

docker ps## CONTAINER ID   IMAGE            COMMAND                  CREATED          STATUS          PORTS                    NAMES## 4e6932d216d7   fandix/image/1   "docker-entrypoint.s…"   39 seconds ago   Up 38 seconds   0.0.0.0:3000->3000/tcp   romantic_khayyamdocker exec -it 4e6932d216d7 sh## /usr/app ls## Dockerfile         index.ts           node_modules       package-lock.json  package.json

当我们使用 sh 进入 Container 的 shell 之后,预设的路径是刚刚利用 WORKDIR 指令设定的 /usr/app 而不是一开始的根目录,透过 ls 指令可以看到所有里用 COPY 指令複製的档案都存放在这里,当我们移动到 Container shell 的根目录时可以看到变得非常乾净

## /usr/app cd /ls## bin    etc    lib    mnt    proc   run    srv    tmp    var## dev    home   media  opt    root   sbin   sys    usr

Unnecessary Rebuild

当我们要更改 index.ts 中的内容时,就需要重新 rebuild 一次 image,而 build 每次 image 就需要
一直重複的执行 npm install 指令,但明明我们的 package.json 并没有更改,所以没有必要每次都重新执行 npm install ,这时可以将 build image 的过程分为两部分。

由于只有执行 RUN npm install 这个命令才会需要使用 package.json,所以没有必要在 RUN npm install 之前把所有的档案複製一次,所以在执行 RUN npm install 之前只需要複製 package.json 就好,那么 Dockerfile 就可以更改为

COPY ./package.json ./RUN npm install

接着在做完 RUN npm install 这个指令后,再将其他的档案複製回 Container 中就好,这样就可以确保当更改其他档案的时候,并不会影嚮到 package.json 导致需要重新执行 npm install

COPY ./package.json ./RUN npm installCOPY ./ ./

Reference

Docker and Kubernetes: The Complete Guide

关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章