為什麼我們要用 docker ?
過去的我們,當業務發展需要部署新的應用時,DevOps 小夥伴通常會去買一台服務器,但是卻不知道這個新應用具體需要多高的配置,往往都會造成資源浪費。
當虛擬機出現後,它可以讓我們在一台服務器上運行多個應用,但是卻有一個缺陷。每個 VM 需要運行一整個的操作系統。每個 OS 又需要 CPU、RAM 等等,需要打補丁、安裝證書,這些反過來又增加了成本和彈性。
Google 在很久之前就開始使用容器模型來解決 VM 模式的弊端。簡單來説容器模型允許我們在同一台主機上運行多個容器,而且共用主機的 CPU、RAM 等資源。
那麼它對開發者來説意味着是麼呢?
它可以保證對所有的開發者和服務器來説,我們的工作環境都是一致的。比如: 生產環境、仿真環境、測試環境。
任何人都可以分分鐘配置好項目,無需亂搞配置、安裝庫和設置依賴。
簡單來説,docker 是一個平台,它允許我們使用容器來開發、部署、運行應用程序。
讓我們退一步來看,容器系統在物理上是什麼樣子的,以及與 VM 有什麼區別。
可以看出來,宿主機的資源在容器化的使用後是共享的,但是在 VM 中卻被分割開了。
接下來,我們來深入一些。
如何使用 docker ?
為此我們需要先熟悉一些術語。
Docker image: 它是一個可執行文件,包含了運行一個應用程序的操作系統配置和所有的庫。它有多個層疊在一起,並表示為單個對象。docker image 是通過 docker file 來創建的,我們稍後再講。
Docker Container: 它是 docker image 的一個運行實例。同一個 docker image 可以有多個運行的 container。
容器化 Node.js 應用
我們來嘗試容器化一個簡單的 node.js 應用,然後創建一個 image:
你的 Node.js 應用
先創建一個 my-node-app 文件夾,
mkdir my-node-app
cd my-node-app
然後創建一個 index.js 來啓動一個 node server:
// 我們用 require 引入 express
var express = require('express')
var app = express()
// 對根 URL 做一個響應
app.get('/', function (req, res) {
res.send('Hello World!')
})
// 讓服務器監聽 8081 端口
app.listen(8081, function () {
console.log('app listening on port 8081!')
})
然後我們創建一個 package.json 文件,可以通過 npm init -y 來快速生成:
{
"name": "helloworld",
"version": "1.0.0",
"description": "Dockerized node.js app",
"main": "index.js",
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.16.4"
}
}
到這一步我們甚至不需要 express 或者 npm 安裝在自己的機器,因為 dockerfile 可以為我們配置和安裝這些依賴。
DockerFile
讓我們創建一個 dockerfile,然後保存到 my-node-app 文件夾。這個文件沒有擴展名,它的名字就叫作 Dockerfile,這是裏面的內容:
# Dockerfile
FROM node:8
WORKDIR /app
COPY package.json /app
RUN npm install
COPY . /app
EXPOSE 8081
CMD node index.js
下面解釋一下里面的命令:
FROM node:8 -- 從 docker hub 拉取 node.js docker 鏡像,可以在這裏找到 node 的鏡像:https://hub.docker.com/_/node/
WORKDIR /app -- 設置鏡像中的工作目錄,可以與下面的命令一起使用: COPY,RUN 和 CMD
COPY package.json /app -- 將 package.json 從宿主機的 my-node-app 目錄複製到了鏡像中的 /app 目錄
RUN npm install -- 在鏡像中運行此命令來安裝 node 包
COPY . /app -- 複製 my-node-app 目錄中的所有文件到鏡像中的 /app 目錄
EXPOSE 8081 -- 這條命令告訴 container 要暴露一個端口號,這個端口號正是我們在 index.js 中寫的那個。默認情況下,容器會忽略對它所有的請求。
構建 Docker 鏡像
注意看啦~ 打開控制枱,到 my-node-app 目錄下,執行以下命令:
# Build a image docker build -t <image-name> <relative-path-to-your-dockerfile>
docker build -t hello-world .
這條命令在我們宿主機創建了一個 hello-world 鏡像
-t 用來為我們的鏡像指定一個名字,這裏就是 hello-world
. 是用來指明 docker file 的路徑,由於我們已經在 my-node-app 中,所以路徑用 . 就可以了
你可以在控制枱看到類似於以下的輸出:
Sending build context to Docker daemon 4.096kB
Step 1/7 : FROM node:8
---> 4f01e5319662
Step 2/7 : WORKDIR /app
---> Using cache
---> 5c173b2c7b76
Step 3/7 : COPY package.json /app
---> Using cache
---> ceb27a57f18e
Step 4/7 : RUN npm install
---> Using cache
---> c1baaf16812a
Step 5/7 : COPY . /app
---> 4a770927e8e8
Step 6/7 : EXPOSE 8081
---> Running in 2b3f11daff5e
Removing intermediate container 2b3f11daff5e
---> 81a7ce14340a
Step 7/7 : CMD node index.js
---> Running in 3791dd7f5149
Removing intermediate container 3791dd7f5149
---> c80301fa07b2
Successfully built c80301fa07b2
Successfully tagged hello-world:latest
可以看到,它根據 docker file 中的命令依次運行,然後輸出了一個 docker 鏡像。當你第一次運行的時候可能會需要一些時間,下次就可以使用緩存來加快速度了。現在我們來看下剛才 build 的鏡像:
# Get a list of images on your host
docker images
這個命令會顯示在你電腦上存在的 docker 鏡像。其中會有一條:
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest c80301fa07b2 22 minutes ago 896MB
運行 Docker 容器
既然我們已經創建了鏡像,下面我們就從這個鏡像運行一個 docker 容器:
# Default command for this is docker container run <image-name>
docker container run -p 4000:8081 hello-world
這條命令用來創建和運行一個 docker 容器
-p 4000:8081 -- 是一個發佈(publish)標識,它將本機的 4000 端口映射到了容器中的 8081 端口。現在所有對本機 4000 端口的訪問,都會被容器中的 8081 端口監聽。
hello-world -- 這個名字就是剛才用 docker build 命令時指定的鏡像名稱。
你將會得到以下輸出:
app listening on port 8081!
如果你需要進入容器並且掛載一個 bash 終端,可以運行:
# Enter the container
docker exec -ti <container id> /bin/bash
為了檢查我們的容器是否運行,打開另一個命令行,然後輸入:
docker ps
可以看到以下輸出:
CONTAINER ID IMAGE COMMAND CREATED
`<container id>` hello-world "/bin/sh -c 'node in…" 11 seconds ago
STATUS PORTS NAMES
Up 11 seconds 0.0.0.0:4000->8081/tcp some-random-name
這裏可以看我們從 hello-world 鏡像創建的容器,以及它的 <container id> ,它正在運行,並且監聽了 8081 端口號。
現在我們這個簡單的 Node.js 應用就已經完全容器化了。你可以在瀏覽器訪問 http://localhost:4000 ,應該可以看到以下畫面:
看,是不是很簡單哈哈~
原文鏈接
歡迎關注我的公眾號:碼力全開(codingonfire)