最近工做須要,開發時須要用到 Docker。這篇文章從零開始演示幾個 Demo,若是你以前沒接觸過 Docker,能夠一步步跟着操做,加深對 docker 的理解。html
多圖,請儘可能在💻上觀看前端
本文由 DJI 前端開發工程師 HelKyle 原創並分享於掘金,如需轉載請註明出處node
不管你所處的公司大或小,多多少少都遇到開發環境和生產環境不一致的問題。有些開發者用 Windows,有些開發者用 Mac,而生產環境可能用的是 Linux,同時跑着多個應用,每一個應用依賴的 node 版本版本不一致,不一樣服務可能還佔用相同的端口。因而咱們經常聽到這樣的疑問:「本地明明是好的啊,爲何到線上就不行了?」nginx
(我本身的親身經歷:很早之前開發前端項目,我須要在本地搭建 LEMP 環境,照着教程搗鼓好幾天,一行前端代碼都沒有寫。後來還由於一些「莫名其妙」的問題,反反覆覆重裝了好幾回。)git
而使用了 Docker 以後呢,咱們能經過配置文件一條命令快速構建環境,而且能夠作到和其餘服務隔離,互不影響,經過例子來說解。github
這篇文章執行的環境是 macOS 10.14.1, Docker version 18.09.0web
鏡像是以一些列歷史操做疊加而成的,對鏡像的每一次操做都會產生新的只讀層,比方說你往容器寫入內容,提交,再把它移除,則會產生兩個歷史只讀層,有點像 git commits,而容器能夠理解問爲鏡像歷史層 + 可寫層的可運行系統,在可寫層作任何操做都沒問題,不提交的話,容器被刪除後相應的改動也會丟失,有點像 git 暫存區。redis
(我的理解,不必定對)docker
Mac 安裝 dockerbash
brew cask install docker
複製代碼
其餘環境安裝 Docker 查看 這裏。
安裝完成以後,執行 hello-world 試一下。
$ docker run hello-world
複製代碼
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest: sha256:2557e3c07ed1e38f26e389462d03ed943586f744621577a99efb77324b0fe535
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
複製代碼
這條命令會鏈接本地 Docker 服務,Docker 服務檢測到本地沒有 hello-world 鏡像,因而去 Docker 鏡像市場下載這個鏡像,而後建立新的容器,運行特定的命令,輸出 Hello from Docker!
這篇文章不講解 docker 有哪些基礎命令,直接從案例入手。
docker run -d -p 80:80 --restart=always nginx:latest
複製代碼
參數說明: run
啓動某個鏡像 -d
讓容器在後臺運行 -p
指定端口映射,宿主機的80端口映射到容器的80端口 --restart
重啓模式,設置 always,每次啓動 docker 都會啓動 nginx 容器。
因爲我本地沒有 nginx:latest 的鏡像,一樣會先去鏡像市場下載。啓動完成打開 http://localhost:80 就能立馬看到 nginx 的歡迎頁面。
若是想修改歡迎頁面,能夠進入到容器內修改頁面。
docker exec -it 4591552a4185 bash
複製代碼
參數說明:
exec
對容器執行某些操做
-it
讓容器能夠接受標準輸入並分配一個僞tty
4591552a4185
是剛剛啓動的 nginx 容器惟一標記
bash
指定交互的程序爲 bash
nginx 默認文件路徑是 /usr/share/nginx/html/index.html
,直接用 echo 寫入內容便可。
echo '<h1>Hello Docker<h1/>' > /usr/share/nginx/html/index.html
複製代碼
ctrl + D 退出容器,從新訪問 localhost:80 便可看到 Hello Docker。
每次修改內容都須要手動進入容器,太過繁瑣,而且👆提到了,對容器的直接修改不會持久保存,若是容器被刪,數據也會跟着丟失。
(因爲以前的 demo 已經佔用了 80 端口,我們先 kill 掉它。)
docker kill 4591552a4185
複製代碼
Docker 提供數據掛載的功能,便可以指定容器內的某些路徑映射到宿主機器上,修改命令,添加 -v
參數,啓動新的容器。
docker run -d -p 80:80 -v ~/docker-demo/nginx-htmls:/usr/share/nginx/html/ --restart=always nginx:latest
複製代碼
啓動成功以後,docker 會幫你生成目錄 ~/docker-demo/nginx-htmls
,如今裏面什麼都沒有,添加個 index.html。
再次打開 http://localhost:80
, 一樣能看到 hello docker。
接着咱們來用 Node + Redis + Docker 作一個 PV 展現的 DEMO。
運行命令:
docker run -d -p 6379:6379 -v ~/docker-demo/redis:/data redis:latest
複製代碼
啓動一個 redis 容器,並將數據持久化到 ~/docker-demo/redis
目錄。(考慮性能,redis 並不會實時寫入數據到磁盤)
用 koa 啓動一個 node server,並鏈接 redis , 每次訪問 / 都給計數器加一。
const Redis = require('ioredis');
const Koa = require('koa');
const Router = require('koa-router');
const router = new Router();
const app = new Koa();
const redis = new Redis(`redis://127.0.0.1:6379/0`);
router.get('/', async (ctx, next) => {
await next();
await redis.incr('pv');
const current = await redis.get('pv');
ctx.body = `current pv: ${current}`;
});
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(3000);
複製代碼
訪問 http://localhost:3000,就能看到輸出結果。
推薦使用 medis 可視化查看 redis 數據。
OK,開發環境完成功能開發,交付運維上線。咱們假設生成環境會啓動四個 node 服務,一個 redis 服務,和一個 nginx 作負載。
這時候須要把咱們的 node 服務也構建成鏡像,新增 dockerfile 文件。
# 基於最新的 node 鏡像
FROM node:latest
# 複製當前目錄下全部文件到目標鏡像 /app/ 目錄下
COPY . /app/
# 修改工做目錄
WORKDIR /app/
# yarn 一下,安裝依賴
RUN ["yarn"]
# 啓動 node server
ENTRYPOINT ["node", "index.js"]
複製代碼
更多 dockerfile 指令看這裏
能夠在本地構建一下,運行命令 docker build . --tag=pv
,而後經過 docker images
就能看到剛剛構建的新鏡像。
繼續往下走,編排一組容器,docker 官方提供了 docker-compose 工具。在項目目錄下新增 docker-compose.yml
文件。
# 使用 docker-compose 2.2 版本
version: "2.2"
# 定義 services
services:
redis:
image: redis:latest
volumes:
- "~/docker-demo/pv/data/:/data/"
web:
# 放大4倍,也就會有四個 node server
scale: 4
build: .
# 新增環境變量
environment:
- REDIS_HOST=redis://redis:6379/0
# 依賴關係
depends_on:
- redis
nginx:
image: nginx:latest
depends_on:
- web
- redis
ports:
- 80:80
volumes:
- "./default.conf:/etc/nginx/conf.d/default.conf"
複製代碼
更多 docker-compsoe 指令看這裏
service web 新增環境變量 REDIS_HOST=redis://redis:6379/0 是給 ioredis 連接用的,對應的要修改 js 文件。
const redis = new Redis(process.env.REDIS_HOST);
複製代碼
redis://redis:6379/0
第一個 redis 是協議,第二個 redis 是 service host。service 之間能夠經過 host 互相通訊。
複製 nginx 容器下的 default.conf 文件出來修改
upstream web {
server pv_web_1:3000;
server pv_web_2:3000;
server pv_web_3:3000;
server pv_web_4:3000;
}
server {
#...
location / {
proxy_pass http://web;
}
#...
}
複製代碼
新增上游服務 web,這裏的 pv 是個人項目文件名,web 是 docker-compose 文件中定義的 service name, 1 - 4 則是 scale 出來 docker 自動給定的序號。啓動起來以後,nginx 訪問 http://pv_web_1:3000
的請求就會到達第一個 web 容器。
萬事具有,let's compose up!
好了,如今訪問 http://localhost:80
到目前爲止,咱們已經把應用部署完成,每次訪問 pv 數量自動加一,而且通過 nginx 負載均衡,會隨機打到不一樣的容器上面。🎉🎉🎉
這篇文章演示了和前端相關的一些 docker 操做,從中咱們能夠看到其對於軟件的開發,測試,部署都帶來了極大的便利。文中的內容僅僅是冰山一角,更全面的學習能夠看Docker — 從入門到實踐這本書。
示例也只能做爲學習使用,請不要直接用在生產環境。同時部份內容是我的理解,沒有權威性,深刻學習 Docker ,你應該去看詳細的文檔。
本文由 DJI 前端開發工程師 HelKyle 原創並分享於掘金,如需轉載請註明出處