簡介
Docker 是一個容器平台,它是一個輕量級、虛擬化、可移植、軟體定義的標準化環境。它允許軟體與運行在實體主機上的其他軟體隔離運行。Docker 是軟體開發中持續開發與整合(CI/CD)方面的一個關鍵組成部分。它提供了一個 虛擬機的輕量級替代方案,並讓開發人員能夠享受分散式應用程式架構。若要深入了解 Docker 生態系統的概覽,請參閱這篇文章.
使用 Docker 建置應用程式的過程始於開發人員為其應用程式建立映像檔。然後,該映像檔將被部署在容器中。映像檔包含應用程式的定義組件,例如應用程式程式碼、函式庫、設定檔、環境變數和執行環境。映像檔標準化了容器內部的環境,賦予了容器化可移植性的特性。 Node.js 是一個開源、跨平台的後端 JavaScript 執行環境,可以在網頁瀏覽器之外執行 JavaScript 程式碼。它建立在 Chrome 的 V8 JavaScript 引擎. Express.js 是一個運行在 Node.js 之上的極簡後端 JavaScript 框架。
在本教學中,我們將為一個運行在 Express 框架上的網站建立映像檔。我們將使用 Bootstrap(一個前端函式庫)來讓前端外觀更好看。建立映像檔後,我們將建置一個容器並將其推送到 Docker Hub。Docker Hub 允許開發人員託管容器化應用程式,以便輕鬆部署到任何 Docker 環境。一旦您的容器託管在 Docker Hub 上,我們將拉取它並建置另一個實際用於提供我們網站服務的映像檔。
先決條件
這將是一個動手實作的教學。您應該建立一個能夠讓您跟著操作的環境。
- 您應該擁有一個 已安裝的 Ubuntu 20.04 作為您的初始作業環境,並 建立一個具有 sudo 權限的非 root 使用者。使用該非 root 使用者登入並繼續以下步驟。
- 您需要安裝 Docker。請按照我們關於 如何在 Ubuntu 上安裝和操作 Docker 的教學中的步驟 1、2、3 和 4。這適用於任何 Ubuntu 發行版。
- 如果您還沒有 Docker Hub 帳戶,請建立一個。您可以點擊此連結獲取 Docker Hub 快速入門指南.
- 安裝 Node.js 和 NPM。NPM 是一個 JavaScript 套件管理器。您可以參考 這些關於安裝 Node 和 npm 的說明.
步驟 1:設定應用程式依賴項
在建立映像檔之前,您需要建立應用程式的原始碼。應用程式原始碼包括將複製到容器中的程式碼、靜態內容和依賴項。首先在非 root 使用者的家目錄中為您的專案建立一個目錄。我們將其命名為 node_express,但您可以自由使用您喜歡的目錄名稱:
|
1 |
mkdir node_express |
接下來,進入此目錄:
|
1 |
cd node_express |
這將是您的應用程式根目錄。一個 node.js 應用程式需要根資料夾中包含一個 package.json 檔案。Npm 使用此檔案來確定您的應用程式需要哪些依賴項。輸入以下指令來建立此檔案:
|
1 |
nano package.json |
之後,將以下程式碼片段新增到該檔案中。您可以根據需要更新名稱、作者、描述和進入點檔案:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ "name": "node-express-docker-image", "version": "1.0.0", "description": "Nodejs Express Docker Image 範例", "author": "hackins", "main": "index.js", "license": "ISC", "keywords": [ "nodejs", "express", "bootstrap" ], "dependencies": { "express": "^4.17.1" } } |
如您所見,此檔案指定了專案名稱、版本、作者以及共享應用程式程式碼所依據的授權條款。建議為您的專案使用簡短且具描述性的名稱,以避免在 npm registry 中重複。我們為該專案指定了 ISC license,它允許免費複製、修改或分發應用程式程式碼。
最重要的是,您應該注意檔案中的以下指令:
- “
main」:此指令指定了應用程式的進入點,我們將其設定為 index.js。我們很快就會建立這個檔案。 - “
dependencies」:此指令指定了當我們執行npm指令時,將從 npm registry 提取的應用程式相依性。在我們的案例中,我們需要 Express 4.17.1 及以上版本。
您現在可以按下 Ctrl + O 儲存檔案。然後,按下 Ctrl + X 關閉檔案。接下來,我們將透過執行以下指令來安裝相依性:
|
1 |
npm install |
此指令會安裝 package.json 檔案中指定的應用程式相依性,並放入 node_modules 目錄中。這些目錄在您首次執行該指令時已自動建立。安裝好應用程式相依性後,您現在可以開始新增應用程式程式碼。
步驟 2:新增您的應用程式程式碼檔案
我們將建立一個基本的食譜網站,內容由 allrecipes 提供。應用程式的主要進入點是 index.js 檔案。我們將新增一個 views 目錄,用來存放專案的各個頁面和靜態資產。該網站將有一個到達頁面,其中包含介紹性資訊和一些食譜的連結。
我們的到達頁面程式碼將放在 home.html 檔案中。首先,輸入以下指令來建立 index.js 檔案:
|
1 |
nano index.js |
新增以下程式碼,這會匯入並建立一個 Express 應用程式。它還指定了 Router 物件、基礎目錄以及此應用程式將提供服務的連接埠:
|
1 2 3 4 5 6 |
const express = require('express'); const app = express(); const router = express.Router(); const path = __dirname + '/views/'; const port = 8090; |
require 是一個載入模組的 JavaScript 函式。在此案例中,我們正在載入 express 模組。然後,我們將使用匯入的模組來建立 express 和 router 物件。router 物件透過回應 HTTP 方法呼叫來執行應用程式的路由功能,隨著教學課程的進行,這些呼叫將會新增到此物件中。
我們也設定了 path 和 port。path 常數定義了程式碼的基礎目錄。在我們的案例中,它是專案根目錄下的 views 子目錄。而 port 指定了 express 應用程式應該接聽的連接埠,在我們的範例中,我們將其設定為 8090.
一旦有了這些常數,我們就可以使用 router 物件來為應用程式指定一些路由。將以下程式碼新增至 index.js 檔案以指定路由:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
router.use(function (req,res,next) { console.log('/' + req.method); next(); }); router.get('/', function(req,res){ res.sendFile(path + 'home.html'); }); router.get('/lasagna', function(req,res){ res.sendFile(path + 'lasagna.html'); }); router.get('/guacamole', function(req,res){ res.sendFile(path + 'guacamole.html'); }); router.get('/banana-bread', function(req,res){ res.sendFile(path + 'banana_bread.html'); }); |
您可以使用 add middleware 到路由,透過使用 router.use 函式。在此案例中,我們新增了一個函式,在將路由器的請求傳遞給應用程式路由之前先記錄它們。對應用程式基礎路徑發送 GET 請求將會傳回 home.html 頁面。然後,我們為三個食譜添加了頁面,這些頁面也將使用對特定食譜頁面的 GET 請求來檢索。
最後,添加以下程式碼以掛載 router 中介軟體和應用程式靜態資產。此外,告訴 express 應用程式監聽連接埠 8090:
|
1 2 3 4 5 6 |
app.use(express.static(path)); app.use('/', router); app.listen(port, function () { console.log('Nodejs Express Example App listening on port ' + port) }) |
您完整的 index.js 檔案應該像這樣:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
const express = require('express'); const app = express(); const router = express.Router(); const path = __dirname + '/views/'; const port = 8090; router.use(function (req,res,next) { console.log('/' + req.method); next(); }); router.get('/', function(req,res){ res.sendFile(path + 'home.html'); }); router.get('/lasagna', function(req,res){ res.sendFile(path + 'lasagna.html'); }); router.get('/guacamole', function(req,res){ res.sendFile(path + 'guacamole.html'); }); router.get('/banana-bread', function(req,res){ res.sendFile(path + 'banana_bread.html'); }); app.use(express.static(path)); app.use('/', router); app.listen(port, function () { console.log('Nodejs Express Example App listening on port ' + port) }) |
您現在可以儲存並關閉檔案。下一步是將靜態網頁新增至 views 目錄。首先輸入以下指令來建立該目錄:
|
1 |
mkdir views |
輸入以下指令以開啟 home.html 首頁檔案:
|
1 |
nano views/home.html |
將以下程式碼新增至檔案中。此程式碼會匯入 Bootstrap,並向網站訪客提供一些關於此網站主題的資訊:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
<!DOCTYPE html> <html lang="zh-Hant"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors"> <meta name="generator" content="Hugo 0.80.0"> <title>超讚食譜</title> <!-- Bootstrap 核心 CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"> <link href="css/custom.css" rel="stylesheet"> </head> <body> <nav class="navbar navbar-dark bg-dark navbar-static-top navbar-expand-md"> <div class="container"> <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">切換導覽</span> </button> <a class="navbar-brand" href="#">超讚食譜</a> <div class="collapse navbar-collapse justify-content-center" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav justify-content-center"> <li class="active nav-item"> <a href="/" class="nav-link">首頁</a> </li> <li class="nav-item"> <a href="/lasagna" class="nav-link">千層麵</a> </li> <li class="nav-item"> <a href="/guacamole" class="nav-link">酪梨醬</a> </li> <li class="nav-item"> <a href="/banana-bread" class="nav-link">香蕉麵包</a> </li> </ul> </div> </div> </nav> <main> <section class="py-5 text-center container"> <div class="row py-lg-5"> <div class="col-lg-6 col-md-8 mx-auto"> <h1 class="fw-light">超棒食譜</h1> <p class="lead text-muted"> 從這些超棒的食譜中尋找並分享日常烹飪靈感。探索食譜、廚師、 影片,以及根據您喜愛的食物和關注的朋友推薦的製作方法。 <br /> <em>(這沒什麼特別的,這只是用於我們的示範 node-express-docker 映像檔應用程式)</em> </p> </div> </div> </section> </main> </body> </html> |
除了匯入 Bootstrap 之外,該頁面還新增了一個基本的 導覽選單 ,以協助我們在頁面之間切換並返回首頁。我們還新增了一行來匯入我們的自訂 CSS 檔案:
|
1 |
<link href="css/custom.css" rel="stylesheet"> |
稍後我們將使用此檔案為應用程式新增自訂樣式。現在,讓我們為食譜建立三個頁面。我們首先從建立 千層麵 頁面開始。使用以下指令以 nano 編輯器開啟該檔案:
|
1 |
nano views/lasagna.html |
在開啟的檔案中,新增以下程式碼。此檔案將匯入 Bootstrap、custom.css 檔案、指定導覽選單並提供一些千層麵食譜資訊:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
<!DOCTYPE html> <html lang="zh-Hant"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content="Mark Otto, Jacob Thornton 和 Bootstrap 貢獻者"> <meta name="generator" content="Hugo 0.80.0"> <title>千層麵食譜</title> <!-- Bootstrap 核心 CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"> <link href="css/custom.css" rel="stylesheet"> </head> <body> <nav class="navbar navbar-dark bg-dark navbar-static-top navbar-expand-md"> <div class="container"> <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">切換導覽</span> </button> <a class="navbar-brand" href="#">超棒食譜</a> <div class="collapse navbar-collapse justify-content-center" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav justify-content-center"> <li class="active nav-item"> <a href="/" class="nav-link">首頁</a> </li> <li class="nav-item"> <a href="/lasagna" class="nav-link">千層麵</a> </li> <li class="nav-item"> <a href="/guacamole" class="nav-link">酪梨醬</a> </li> <li class="nav-item"> <a href="/banana-bread" class="nav-link">香蕉麵包</a> </li> </ul> </div> </div> </nav> <main> <section class="py-5 text-center container bg-light"> <div class="row py-lg-5"> <div class="col-lg-6 col-md-8 mx-auto"> <h1 class="fw-light">最棒的千層麵食譜</h1> <p class="lead text-muted"> 這是您做過最美味的千層麵。 <br /> <em>(別太認真,這只是我們 demo node-express-docker 映像檔應用程式用的)</em> </p> <h3>食材</h3> <ul class="list-group"> <li class="list-group-item">1 磅甜義大利香腸</li> <li class="list-group-item">¾ 磅瘦牛絞肉</li> <li class="list-group-item">½ 杯洋蔥碎</li> <li class="list-group-item">2 瓣大蒜,壓碎</li> <li class="list-group-item">1 罐 (28 盎司) 碎番茄</li> <li class="list-group-item">2 罐 (6 盎司) 番茄糊</li> <li class="list-group-item">2 罐 (6.5 盎司) 番茄醬</li> <li class="list-group-item">½ 杯水</li> <li class="list-group-item">2 大匙白糖</li> <li class="list-group-item">1 ½ teaspoons dried basil leaves</li> <li class="list-group-item">½ 茶匙茴香子</li> <li class="list-group-item">1 茶匙義式綜合香料</li> <li class="list-group-item">1 ½ 茶匙鹽(分次使用),或依個人口味調整</li> <li class="list-group-item">¼ 茶匙黑胡椒粉</li> <li class="list-group-item">4 大匙新鮮巴西里碎</li> <li class="list-group-item">12 片千層麵麵皮</li> <li class="list-group-item">16 盎司瑞可達起司</li> <li class="list-group-item">1 顆雞蛋</li> <li class="list-group-item">¾ 磅莫札瑞拉起司,切片</li> <li class="list-group-item">¾ 杯帕瑪森起司粉</li> </ul> </div> </div> </section> </main> </body> </html> |
讓我們按照相同的步驟來為 酪梨醬 食譜頁面建立檔案。執行以下指令以 nano 開啟檔案:
|
1 |
nano views/guacamole.html |
然後將此程式碼新增至檔案中:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
<!DOCTYPE html> <html lang="zh-Hant"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors"> <meta name="generator" content="Hugo 0.80.0"> <title>酪梨醬食譜</title> <!-- Bootstrap 核心 CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"> <link href="css/custom.css" rel="stylesheet"> </head> <body> <nav class="navbar navbar-dark bg-dark navbar-static-top navbar-expand-md"> <div class="container"> <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">切換導覽</span> </button> <a class="navbar-brand" href="#">超棒食譜</a> <div class="collapse navbar-collapse justify-content-center" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav justify-content-center"> <li class="active nav-item"> <a href="/" class="nav-link">首頁</a> </li> <li class="nav-item"> <a href="/lasagna" class="nav-link">千層麵</a> </li> <li class="nav-item"> <a href="/guacamole" class="nav-link">酪梨醬</a> </li> <li class="nav-item"> <a href="/banana-bread" class="nav-link">香蕉麵包</a> </li> </ul> </div> </div> </nav> <main> <section class="py-5 text-center container bg-light"> <div class="row py-lg-5"> <div class="col-lg-6 col-md-8 mx-auto"> <h1 class="fw-light">最佳酪梨醬食譜</h1> <p class="lead text-muted"> 您可以根據自己的口味,將這款酪梨沙拉製作成細滑或帶顆粒的口感。 <br /> <em>(沒什麼特別的,這只是用於我們的 demo node-express-docker 映像檔應用程式)</em> </p> <h3>食材</h3> <ul class="list-group"> <li class="list-group-item">3 顆酪梨 - 去皮、去籽並壓碎</li> <li class="list-group-item">1 顆萊姆,榨汁</li> <li class="list-group-item">1 茶匙鹽</li> <li class="list-group-item">½ 杯洋蔥丁</li> <li class="list-group-item">3 大匙切碎的新鮮香菜</li> <li class="list-group-item">2 顆羅馬番茄(梅子番茄),切丁</li> <li class="list-group-item">1 茶匙蒜末</li> <li class="list-group-item">1 捏卡宴辣椒粉(可選)</li> </ul> </div> </div> </section> </main> </body> </html> |
最後,讓我們建立 banana_bread.html 檔案,輸入以下指令:
|
1 |
nano views/banana_bread.html |
然後,將以下 HTML 程式碼新增至檔案中:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors"> <meta name="generator" content="Hugo 0.80.0"> <title>香蕉麵包食譜</title> <!-- Bootstrap 核心 CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"> <link href="css/custom.css" rel="stylesheet"> </head> <body> <nav class="navbar navbar-dark bg-dark navbar-static-top navbar-expand-md"> <div class="container"> <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">切換導覽</span> </button> <a class="navbar-brand" href="#">超棒食譜</a> <div class="collapse navbar-collapse justify-content-center" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav justify-content-center"> <li class="active nav-item"> <a href="/" class="nav-link">首頁</a> </li> <li class="nav-item"> <a href="/lasagna" class="nav-link">千層麵</a> </li> <li class="nav-item"> <a href="/guacamole" class="nav-link">酪梨醬</a> </li> <li class="nav-item"> <a href="/banana-bread" class="nav-link">香蕉麵包</a> </li> </ul> </div> </div> </nav> <main> <section class="py-5 text-center container bg-light"> <div class="row py-lg-5"> <div class="col-lg-6 col-md-8 mx-auto"> <h1 class="fw-light">最棒的香蕉香蕉麵包食譜</h1> <p class="lead text-muted"> 為什麼要對香蕉風味妥協?這款香蕉麵包濕潤美味,帶有濃郁的香蕉香氣!親朋好友都非常喜歡我的食譜,並說這是目前為止最棒的!烤過後更美味!!請享用! <br /> <em>(沒什麼特別的,這只是用於我們的 demo node-express-docker 映像檔應用程式)</em> </p> <h3>食材</h3> <ul class="list-group"> <li class="list-group-item">2 杯中筋麵粉</li> <li class="list-group-item">1 茶匙小蘇打粉</li> <li class="list-group-item">¼ 茶匙鹽</li> <li class="list-group-item">½ 杯奶油</li> <li class="list-group-item">¾ 杯黑糖</li> <li class="list-group-item">2 顆蛋,打散</li> <li class="list-group-item">2⅓ 杯熟透的香蕉泥</li> </ul> </div> </div> </section> </main> </body> </html> |
現在,我們已經建立了所有頁面。如果您還記得,我們需要新增 css/custom.css 檔案。輸入以下指令來建立目錄:
|
1 |
mkdir views/css |
然後使用以下指令在 nano 編輯器中建立並開啟檔案:
|
1 |
nano views/css/custom.css |
您可以根據需要新增更多 CSS 程式碼來設計您的網站樣式。為求簡潔,讓我們將以下程式碼片段新增到檔案中:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.bd-placeholder-img { font-size: 1.125rem; text-anchor: middle; -webkit-user-select: none; -moz-user-select: none; user-select: none; } @media (min-width: 768px) { .bd-placeholder-img-lg { font-size: 3.5rem; } } |
完成後儲存並關閉檔案。
由於我們現在已經安裝了應用程式原始碼和專案依賴項,您可以啟動應用程式。
我們已將應用程式設定為接聽連接埠 8090,請執行以下指令以指示防火牆允許流量通過此連接埠。如果您指定了不同的連接埠,請替換指令中的連接埠號碼:
|
1 |
sudo ufw allow 8090 |
現在,您可以啟動應用程式。但首先,請執行以下指令以確保您位於專案根目錄中:
|
1 |
cd ~/node_express |
使用 node index.js 啟動應用程式。如果您指定了不同的進入點,請將其替換為您的進入點:
|
1 |
node index.js |
如果您將瀏覽器導向至 http://your_public_server_ip:8090,您將會看到如定義的食譜首頁:
您可以在導覽列中看到各個食譜的連結。讓我們點擊一些。下面是 Lasagna 食譜頁面:
而這裡是 Guacamole 食譜頁面:
至此,您已建立應用程式並測試其運作是否符合預期。您可以按下 Ctrl + C 結束伺服器,然後繼續建立 Dockerfile。Dockerfile 有助於擴充性,因為它可以在需要時重新建立應用程式的執行個體。
步驟 3:建立 Dockerfile
Docker 在建置映像檔時會讀取 Dockerfile 中指定的指令。它指定了應用程式的執行階段環境。因此,它能幫助開發人員避免相依性或變更執行階段版本所產生的差異。輸入以下指令來建立 Dockerfile:
|
1 |
nano Dockerfile |
Docker 映像檔是使用多個相互建構的映像檔層建立的。您首先要新增一個基礎映像檔,作為應用程式的起點。
由於應用程式預期在 node.js 環境中執行,我們將首先新增 node:10-alpine 映像檔 用於 node.js。目前在我們撰寫本教學課程時,這是 推薦的 Node.js LTS 版本。我們選擇這個特定的映像檔,是因為它衍生自 Alpine Linux 專案。因此,它將有助於將我們的映像檔大小保持在最小。在 Docker Hub Node 映像檔頁面 下有幾種映像檔變體,您可以根據自己的需求進行選擇。
新增以下程式碼,使用 FROM 指令來設定應用程式的基礎映像檔:
|
1 |
FROM node:10-alpine |
此映像檔包含 Node.js 和 npm。每個 Dockerfile 都必須以 FROM 指令開始。Docker node 映像檔預設帶有一個非 root 的 node 使用者,您可以使用它來以 root 身份執行您的應用程式容器。 Docker 安全性建議 不要以 root 身份執行容器,並將權限限制為僅執行其資源所需的權限。
在這種情況下,我們將使用 node 使用者的家目錄作為應用程式的工作目錄,以及容器內的使用者。您可以查看這份 Docker Node 映像檔最佳做法 指南以取得更多資訊。
我們將在 node_modules 內建立 /home/node 子目錄以及 app 目錄,以協助簡化應用程式程式碼的權限。建立這些目錄可確保當我們在容器內部本機執行 npm install 指令時,它們具有正確的權限。建立目錄後,您必須將其所有權設定給 node 使用者。我們將在 Dockerfile 中加入以下行來執行此操作:
|
1 |
mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app |
然後,您將透過新增以下行來設定工作目錄:
|
1 |
WORKDIR /home/node/app |
最好一律設定 WORKDIR ,這樣 Docker 就不必預設建立一個。
新增以下行以複製 package.json 和 package-lock.json 檔案:
|
1 |
COPY package*.json ./ |
建議在執行 npm install 或複製應用程式原始碼之前新增 COPY 指令。這能讓您利用 Docker 的快取機制。在建置過程中,Docker 會檢查是否為每個指令快取了一個圖層。這意味著如果您沒有變更 package.json 檔案,則 Docker 將使用現有的映像檔層,並避免重新安裝 node modules,從而加快建置過程。
在執行 npm install 之前,新增以下行以將使用者切換為 node ,以確保所有應用程式檔案和 node_modules 目錄都歸非 root 的 node 使用者所有:
|
1 |
USER node |
我們的容器現在已準備好執行 npm install 指令。將以下行新增至 Dockerfile:
|
1 |
RUN npm install |
安裝好 node_modules 後,新增以下行,這將指示 Docker 將應用程式程式碼複製到容器上的應用程式目錄中,並具有正確的權限和所有權,即非 root 的 node 使用者:
|
1 |
COPY --chown=node:node . . |
最後一步是公開容器上的連接埠 8090 ,正如我們在入口 index.js 檔案中所定義的:
|
1 2 |
EXPOSE 8090 CMD [ "node", "index.js" ] |
EXPOSE 設定在執行階段將開啟容器上的哪些連接埠。 CMD 執行啟動應用程式的指令,在此例中為 node index.js。
Dockerfile 中只能有一個 CMD 指令,因為只有最後一個會生效。請查看 Dockerfile 參考文件,以獲取您可以使用 Dockerfile 執行的操作列表。
您完整的 Dockerfile 應該像這樣:
|
1 2 3 4 5 6 7 8 9 |
FROM node:10-alpine RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app WORKDIR /home/node/app COPY package*.json ./ USER node RUN npm install COPY --chown=node:node . . EXPOSE 8090 CMD [ "node", "index.js" ] |
您現在可以儲存並關閉該檔案。
接下來您要執行的是新增 .dockerignore 檔案。就像 .gitignore 檔案一樣,.dockerignore 指定了專案目錄中哪些檔案和目錄不應複製到容器中。
使用 nano 編輯器開啟檔案:
|
1 |
nano .dockerignore |
在檔案中新增以下幾行:
|
1 2 3 4 |
node_modules npm-debug.log Dockerfile .dockerignore |
如果使用的是 git 存放庫,那麼您還應該新增 .git 目錄和 .gitignore 檔案。儲存並關閉檔案。
如果一切順利,現在是時候使用 docker build 指令來建置應用程式映像檔了。您可以將 –t 旗標新增至 docker build 指令,以便為映像檔標記一個易記的名稱,而不是使用 Docker 預設設定的隨機字串。我們也將把映像檔推送到 Docker Hub,因此最好在標籤中包含您的 Docker Hub 使用者名稱。
我們將使用 nodejs-express-image 作為標籤名稱。您可以自由選擇自己喜歡的標籤名稱。以下是建置映像檔的指令:
|
1 |
sudo docker build -t your_dockerhub_username/nodejs-express-image . |
請記住將 your_dockerhub_username 替換為您實際的 Docker Hub 使用者名稱。末尾的 . (點) 指定建置上下文為目前目錄。
建置過程需要一到兩分鐘。完成後,輸入指令以檢查您的映像檔:
|
1 |
sudo docker images |
您應該會看到類似以下的內容:
請記住,我們已將 your_dockerhub_username 替換為實際的使用者名稱。
確認您的映像檔已建置完成後,您現在可以使用 docker run 來建立容器。將包含以下旗標:
-p:發布容器上的連接埠並將其對應到主機系統上的連接埠。為了示範目的,我們將在主機系統上使用連接埠 80。但是,如果您在該連接埠上運行了其他程序,請根據需要隨時進行修改。深入了解 Docker 文件中的連接埠繫結.-d:用於分離模式。允許容器在背景繼續運行。--name:您可以使用此旗標來設定一個易記的名稱,而不是讓 Docker 分配一個隨機字串。
建置容器的指令如下。請適當替換您的 Docker Hub 使用者名稱:
|
1 |
sudo docker run --name nodejs-express-image -p 80:8090 -d your_dockerhub_username/nodejs-express-image |
等待容器建置並開始運行。您可以使用此指令來檢查所有正在運行的容器:
|
1 |
sudo docker ps |
您應該會看到類似以下的輸出:
如輸出所示,容器現在正在運行。如果您在瀏覽器中造訪伺服器的公共 IP 位址(不含連接埠),您就可以在瀏覽器中查看它。您的首頁將會載入:
您已成功使用 Docker 部署了 Node Express 靜態網站。讓我們看看如何將此映像檔推送到 Docker Hub,以便將來使用和擴充。
步驟 4:使用 Docker 映像檔存放庫
您可以將映像檔推送到 Docker Hub 等映像檔登錄庫,並將其儲存以供日後使用、與其他開發人員共享或用於擴充您的容器。我們可以將建立的映像檔推送到 Docker Hub,並使用它來重新建立容器。
使用以下指令登入您的 Docker Hub 帳戶。請將其替換為您實際的 Docker Hub 使用者名稱:
|
1 |
sudo docker login -u your_dockerhub_username |
系統提示時輸入您的密碼。登入後,系統會在您的使用者家目錄中建立一個 ~/.docker/config.json 檔案,其中包含您的 Docker Hub 憑證。
設定完成後,輸入以下指令將映像檔推送到 Docker Hub,並指定您先前建置映像檔時設定的標籤:
|
1 |
sudo docker push your_dockerhub_username/nodejs-express-image |
此指令會將 Docker 映像檔推送到您的 Docker Hub 帳戶。如果您造訪您的帳戶,您可以看到最近推送的映像檔:
我們可以透過銷毀目前的應用程式容器,並使用存放庫中的映像檔重新建置它,來測試映像檔存放庫的實用性。
輸入以下指令來列出您目前的容器:
|
1 |
sudo docker ps |
您應該會看到類似以下的輸出:
請注意輸出中列出的 CONTAINER ID,複製它,並使用以下指令停止您的容器,請將 ID 替換為您的 ID:
|
1 |
sudo docker stop 1bb2d65279bb |
輸入以下指令以列出系統中所有可用的 Docker 映像檔:
|
1 |
sudo docker images –a |
輸出將顯示您的映像檔名稱、Node.js 映像檔以及建置過程中的其他映像檔。
輸入以下指令以移除映像檔,包括未使用的或懸空的映像檔:
|
1 |
sudo docker system prune |
輸入 y 以確認。這會移除已停止的容器和映像檔。如果您列出它們,您將在輸出中看到一個空列表:
現在,您已經移除了執行應用程式的容器以及映像檔本身。若要深入了解如何 透過我們的教學課程來移除 Docker 容器、映像檔和資料卷.
我們現在可以透過先使用以下指令從 Docker Hub 拉取映像檔來重新建立整個過程。請適當地替換您的 Docker Hub 使用者名稱:
|
1 |
sudo docker pull your_dockerhub_username/nodejs-express-image |
再次使用以下指令列出您的 Docker 映像檔:
|
1 |
sudo docker images |
您應該會在輸出中看到該映像檔:
您現在可以使用來自 步驟 3 的指令重新建置您的容器。當然,請在適當的地方替換您的 Docker Hub 使用者名稱:
|
1 |
sudo docker run --name nodejs-express-image -p 80:8090 -d your_dockerhub_username/nodejs-express-image |
列出您的容器以確認它已重新建置:
|
1 |
sudo docker ps |
您應該會看到類似的輸出:
在瀏覽器中,導覽至您伺服器的公用 IP 位址,您應該會看到您的應用程式正在執行。
結論
如果您已按照本教學課程進行到此處,您現在已經擁有一個使用 Express 和 Bootstrap 製作並使用 Docker 部署的靜態網站。您使用靜態網站檔案建置了 Docker 映像檔,並使用該映像檔建立了容器。接著,您將映像檔推送到 Docker 映像檔登錄庫,Docker Hub,使其可用於日後使用或擴充。為了測試映像檔登錄庫的使用,您銷毀了映像檔和容器,從登錄庫拉取了映像檔,並重新建置了容器。
本教學課程說明了如何部署 Node.js 應用程式。如果您想了解如何使用不同的網頁開發技術棧,我們有一篇關於在 Nginx 上使用 Docker Compose 部署 Laravel 應用程式.
如需更多關於利用 Docker 的資源,請參考以下教學課程:
祝您運算愉快!











留言
目前尚無留言。成為第一個留言的人吧。