原文連接html
最近在捯飭一個前端性能上報分析的項目,前端由 react
全家桶,打包部署公司有專門的發佈系統,這塊就沒什麼顧慮。前端
前端團隊的後端沒有什麼規範或通用流程,就想本身先技術選型從0到1,決定使用 egg + mongodb
,後續也許會追加 nginx + redis + Kafka
相關配置。node
docker
!
整體目標不外乎幾點:簡單、快速、安全。react
docker
的優點。
CI
。
PS:搜索搜的頭禿,如下不少關鍵知識點收集自 google + github issue + Stack Overflow + docker 官方文檔 + 英文博客。英文有障礙真是影響效率。nginx
接下來講說實踐中遇到的問題。git
步驟:github
換臺電腦或協同其餘小夥伴開發時,得把你的動做重複一遍。不一樣的操做系統和下的不一樣 node 或 db 版本,都有可能致使系統運行不起來。web
你確定聽過這句話:個人電腦上是好的啊。redis
約束不了團隊衆多軟件的安裝和版本的控制,要求安裝必定範圍的 docker
仍是簡單的吧。mongodb
例如啓動 mongodb 服務
docker run -p 27017:27017 -v <LocalDirectoryPath>:/data/db --name docker_mongodb -d mongo:4.2.6
複製代碼
這裏咱們啓動了一個 mongo 最新穩定版本的 docker 容器。簡單說明下:
run
運行鏡像,本地沒有會自動拉取。
-p
端口映射,本地 27017 映射容器 27017 端口,就能夠經過訪問本地端口而訪問 docker mongo 的服務了。
-v
本地
<LocalDirectoryPath>
映射容器目錄
/data/db
,用來持久化數據庫,否則容器刪除數據也丟失了。
--name
給容器取個名字,匿名容器能夠經過
docker container prune
刪除。
-d
後臺運行
mongo:4.2.6
docker hub官方鏡像:版本
接下來本地啓動跟無 docker 效果是同樣的。
編寫 Dockerfile
文件
# 基於的基礎鏡像
FROM node:12.16.3
# 踩坑1:注意目錄使用前,保證存在 RUN mkdir -p /usr/src/egg # 注意不要用 cd,須要瞭解 docker 分層構建的概念,須要改變上下文的 pwd,使用 WORKDIR WORKDIR /usr/src/egg # 複製 Dockerfile 同級內容到容器的 /usr/src/egg 目錄下 COPY . . # 安裝 npm 包 RUN npm install # 暴露端口,這裏只是聲明便於理解維護,實際映射使用須要 -p xxx:7001 EXPOSE 7001 # 啓動容器後默認執行的命令行 CMD [ "npm", "run", "start" ] 複製代碼
編寫 .dockerignore
文件
node_modules
npm-debug.log
.idea
複製代碼
忽略 node_modules
:
docker build -t node:egg .
複製代碼
docker images
複製代碼
REPOSITORY TAG IMAGE ID CREATED SIZE
node egg ae65b8012120 28 seconds ago 1.12GB
複製代碼
docker run -p 7001:7001 -d node:egg
複製代碼
docker ps # 查看運行容器,獲取CONTAINER ID,-a 能夠查看全部包括中止的容器
複製代碼
docker logs b0d0c3df5eed
複製代碼
docker exec -it b0d0c3df5eed bash
du -a -d 1 -h # 查看容器目錄文件大小
複製代碼
踩坑2:在 docker 中運行記得把 package.json
中的 egg-scripts start --daemon
中的 --daemon
刪掉。 須要理解前臺、後臺運行進程的概念,docker 中的 shell 腳本必須之前臺方式運行。
上文看到,可能源代碼就幾百K,鏡像包卻超過1G。看看能有哪些優化手段。
也許你並不須要 docker node 提供完整的例如 bash、git 等工具。只須要基本的 node 運行環境便可,則可使用 alpine
鏡像。
- FROM node:12.16.3
+ FROM node:12.16.3-alpine
複製代碼
- RUN npm install
+ # 無關運行的開發依賴包都該歸屬 devDependencies
+ RUN npm install --production
複製代碼
docker build -t node:simplfy .
複製代碼
REPOSITORY TAG IMAGE ID CREATED SIZE
node simplfy 8ccafec91d90 28 seconds ago 132MB
複製代碼
鏡像包從 1.12G
降到了 132MB
,node_modules
從 214MB
降到了 44.5M
。
踩坑3:alpine
鏡像容器不支持 bash
。
你若是須要 bash、git
能夠這麼作 issue
RUN apk update && apk upgrade && \
apk add --no-cache bash git openssh
複製代碼
或不想臃腫你的鏡像 issue,其實你可使用 sh
docker exec -it container_id sh
複製代碼
在無 docker 本地開發時,你可使用 egg-mongoose
這樣鏈接數據庫
`mongodb://127.0.0.1/your-database`
複製代碼
使用 docker 後,容器的 127.0.0.1
或 localhost
與你本地的環境是不通的。
有兩種方式鏈接 docker mongo:
mongodb://192.1.2.3/your-database
例如在 Dockerfile
中設置真實IP
# 設置數據庫IP
ENV docker_db=192.1.2.3
複製代碼
鏈接 url
`mongodb://${process.env.docker_db}/your-database`
複製代碼
對於每個開發都得不停更換網絡IP,對開發不友好。
思考:
node_modules
衝突?
鏡像包還面臨一個存儲問題,不當心發到開源 docker hub 倉庫,可能致使源碼泄漏。
自建倉庫?
大多數教程一上來,必然或大篇章都是 Dockerfile
構建鏡像。介於以上種種,能不能換種思路,放棄構建 image。
docker-compose
用來編排多容器的啓動部署。
docker-compose.yml
文件。
version: "3"
services: db: image: mongo:4.2.6 # 鏡像:版本 environment: - MONGO_INITDB_ROOT_USERNAME=super # 默認開啓受權,並建立超管用戶 mongo -u godis -p godis@admin --authenticationDatabase admin - MONGO_INITDB_ROOT_PASSWORD=xxx # 超管密碼,敏感數據也可使用 `secrets`,不贅述。 - MONGO_INITDB_DATABASE=admin # *.js 中默認的數據庫 volumes: - ./init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro - ./mongo-volume:/data/db ports: - "27017:27017" restart: always 複製代碼
簡單說明:
version: "3"
,不是指你的應用配置版本,而是指 docker 支持的版本,詳情說明。
MONGO_INITDB_ROOT_USERNAME
、MONGO_INITDB_ROOT_PASSWORD
環境變量用來開啓受權,docker 自動建立一個數據庫超管角色。
docker mongo 啓動容器時會執行 /docker-entrypoint-initdb.d/
中的 *.js
腳本,例如這裏 init-mongo.js
來初始化數據庫角色。
MONGO_INITDB_DATABASE
數據庫就是 *.js
中默認的 db
對象,這裏指向 admin
。
./mongo-volume:/data/db
映射目錄或卷,持久化數據庫文件。
init-mongo.js
// https://stackoverflow.com/questions/42912755/how-to-create-a-db-for-mongodb-container-on-start-up
// 分別在 user、staff 數據庫上建立訪問角色。
// 這裏 db 是 MONGO_INITDB_DATABASE 指定的數據庫
db.getSiblingDB('user')
.createUser(
{
user: 'user',
pwd: 'xx',
roles: [ 'readWrite', 'dbAdmin' ],
}
);
db.getSiblingDB('staff') .createUser( { user: 'staff', pwd: 'yy', roles: [ 'readWrite', 'dbAdmin' ], } ); 複製代碼
secrets
文件,node 也讀取改文件?),有點麻煩,讀者有更好的方案還望不吝賜教。
services:
...
server:
image: node:12.16.3-alpine
depends_on:
- db
volumes:
- ./:/usr/src/egg
environment:
- NODE_ENV=production
- docker_db=db
working_dir: /usr/src/egg
command: /bin/sh -c "npm i && npm run start" # not works: npm i && npm run start and not support bash
ports:
- "7001:7001"
volumes: nodemodules: 複製代碼
說明:
depends_on
表示依賴的容器,docker 會等待依賴項先啓動。
volumes
映射本地目錄到容器,這樣本地修改了也能影響到容器。
environment
能夠在
process.env
拿到。
working_dir
設置
pwd
,不像
Dockerfile
,不存在是會自動建立。
command
啓動容器後執行的命令行。
踩坑4:command: npm i && npm run start
不支持 &&
。alpine
鏡像不支持 bash
,egg-bin dev
會報錯 Error: Cannot find module '/bin/bash'
。
environment:
...
- docker_db=db # db 就是 services 中定義 mongo 的名稱
複製代碼
`mongodb://${process.env.docker_db}/your-database`
複製代碼
大部分教程都是用 links
來解決,但官方不推薦並準備廢棄。推薦使用 networks
。這裏並無配置 networks
。 這是由於 docker 會默認建立名稱爲 projectname_default
的 networks
,用來 docker-compose
容器間的通訊。
node_modules
?
services:
...
server:
image: node:12.16.3-alpine
depends_on:
- db
volumes:
+ - nodemodules:/usr/src/egg/node_modules
- ./:/usr/src/egg
environment:
- NODE_ENV=production
- docker_db=db
working_dir: /usr/src/egg
command: /bin/sh -c "npm i && npm run start" # not works: npm i && npm run start and not support bash
ports:
- "7001:7001"
+ volumes: + nodemodules: 複製代碼
docker-compose.yml
文件目錄下文件運行
docker-compose
。
docker-compose up -d
複製代碼
volumes:
- :/usr/src/egg/node_modules
- ./:/usr/src/egg
複製代碼
docker-compose
文件來區分環境,例如開發時,沒有必要每次啓動時執行一次
npm i
。
建立 docker-compose.notinstall.yml
文件
version: "3"
services: server: environment: - NODE_ENV=development # 覆蓋 - NEW_ENV=add # 新增 command: npm run start # 覆蓋 複製代碼
npm i
帶來的消耗。若是你使用匿名卷,則
node_modules
每一個容器相互獨立沒法共享,致使報錯。
docker-compose -f docker-compose.yml -f docker-compose.notinstall.yml up -d
複製代碼
更多多文件使用,查看文檔 Share Compose configurations between files and projects
最後藉助 package.json scripts
優化記憶命令行。
開發部署問題暫時告一段落,項目還在開發當中,線上運行一段時間後再來分享。水平有限,有錯誤歡迎指出,有更好的建議也歡迎補充。