iT邦幫忙

0

Docker - Making Real Projects with Docker

  • 分享至 

  • xImage
  •  

上一張介紹了如何 build 一個客製化的 image 與 build image 未遇到的問題與步驟,接著要來 build 一個真正的專案,雖然說是真正的專案但也只是一個簡易版的 NodeJS Web Server,不過方法與流程都大同小異,所以可以透過這個步驟創建出各種不同的 image。
img


NodeJS Server Setup

首先先創建一個新的資料夾並命名為 simpleNodeServer 並進到資料夾中

mkdir simpleNodeServer
cd simpleNodeServer

接著創建一個 package.json file,並對他進行編輯

touch package.json
vim 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.ts
vim 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 Dockerfile
vim Dockerfile
FROM alpine

RUN npm install

CMD ["npm", "start"]
  • FROM: 透過 base image 創建一個新的 Image
  • RUN: 創建 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
https://ithelp.ithome.com.tw/upload/images/20220724/20124767QBoohvoQ7Z.png


Base Image Issues

會造成這樣的原因是因為我們使用的 base image 是一個非常小的 Image,所以他並不包含能夠執行 npm install 的內容,所以應該要使用帶有可以執行 npm installbase image

FROM node:14-alpine

RUN npm install

CMD ["npm", "start"]

https://ithelp.ithome.com.tw/upload/images/20220724/20124767iicqolujQy.png

透過使用可以執行 npm install 的 base image 便可以將 NodeJS Server build 出來,接著利用 image id 創建一個 Container 吧。

docker run -it 15c18aa74f8507a785e96a45f17b54b34d365d8774c99e7d239da45a3aecbdfb

https://ithelp.ithome.com.tw/upload/images/20220724/20124767hasRCR3Ubg.png

還是出錯了!這次出錯的問題在於當在創建 image 時所執行的 npm install 他需要有 package.json 才能夠正常執行,而我們創建的 image 中並沒有 package.json file,所以導致錯誤。


A Few Missing Files

所以我們需要將我們需要附加在的檔案加在 Image 中,這樣在創建 Image 時才能夠完整的執行指令,這時就需要利用 COPY 指令複製檔案到 Image 中。
https://ithelp.ithome.com.tw/upload/images/20220724/20124767kIu2Jwxf5h.png

利用 COPY 指令將我們機器上面的某個檔案(相對路徑)複製到 Container 中的某個位置上,所以我們需要將我們機器中的所有檔案都複製到 Container 中

FROM node:14-alpine

COPY ./ ./
RUN npm install

CMD ["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 them

docker 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 是否有東西

https://ithelp.ithome.com.tw/upload/images/20220724/20124767vwQULNwRUr.png


Container Port Mapping

簡單來說我們在 Container 中執行了 npm start 確實是有在 [localhost](http://localhost) 3000 成功創建一個 Server,但這個 localhost 3000 是在 Container 中的 Port ,所以我們本機的 localhost 無法成功連結到 Container 的 localhost
https://ithelp.ithome.com.tw/upload/images/20220724/20124767otzQD7FJ6W.png

所以我們要做的就是將我們本機的 Port 3000 連結到 Container 內部的 Port 3000,這樣都瀏覽器向 localhost: 3000 發出請求時,就可以連接到 Container 中的 Port 3000。
https://ithelp.ithome.com.tw/upload/images/20220724/201247674LsstT9hpd.png

所以可以使用 docker run 中的其中一個 Option
https://ithelp.ithome.com.tw/upload/images/20220724/20124767gVOEkL8G0M.png

利用 -p option 將本機指定的 Port 連接到 Container 中指定的 Port

docker run -it -p 3000:3000 09de30e6003ab9adb4de287dbf8ffa72201941bf537681c10f5c5742e2c427c6

https://ithelp.ithome.com.tw/upload/images/20220724/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_jepsen

docker 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-alpine

WORKDIR /usr/app

COPY ./ ./
RUN npm install

CMD ["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_khayyam

docker 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 install
COPY ./ ./

Reference


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言