過去的咱們,當業務發展須要部署新的應用時,DevOps 小夥伴一般會去買一臺服務器,可是殊不知道這個新應用具體須要多高的配置,每每都會形成資源浪費。html
當虛擬機出現後,它可讓咱們在一臺服務器上運行多個應用,可是卻有一個缺陷。每一個 VM 須要運行一整個的操做系統。每一個 OS 又須要 CPU、RAM 等等,須要打補丁、安裝證書,這些反過來又增長了成本和彈性。node
Google 在好久以前就開始使用容器模型來解決 VM 模式的弊端。簡單來講容器模型容許咱們在同一臺主機上運行多個容器,並且共用主機的 CPU、RAM 等資源。docker
那麼它對開發者來講意味着是麼呢?
它能夠保證對全部的開發者和服務器來講,咱們的工做環境都是一致的。好比: 生產環境、仿真環境、測試環境。express
任何人均可以分分鐘配置好項目,無需亂搞配置、安裝庫和設置依賴。npm
簡單來講,docker 是一個平臺,它容許咱們使用容器來開發、部署、運行應用程序。
讓咱們退一步來看,容器系統在物理上是什麼樣子的,以及與 VM 有什麼區別。json
能夠看出來,宿主機的資源在容器化的使用後是共享的,可是在 VM 中卻被分割開了。瀏覽器
接下來,咱們來深刻一些。緩存
爲此咱們須要先熟悉一些術語。bash
Docker image: 它是一個可執行文件,包含了運行一個應用程序的操做系統配置和全部的庫。它有多個層疊在一塊兒,並表示爲單個對象。docker image 是經過 docker file 來建立的,咱們稍後再講。服務器
Docker Container: 它是 docker image 的一個運行實例。同一個 docker image 能夠有多個運行的 container。
咱們來嘗試容器化一個簡單的 node.js 應用,而後建立一個 image:
先建立一個 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,而後保存到 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
中寫的那個。默認狀況下,容器會忽略對它全部的請求。
注意看啦~ 打開控制檯,到 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 容器:
# 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)