原文连接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
优化记忆命令行。
开发部署问题暂时告一段落,项目还在开发当中,线上运行一段时间后再来分享。水平有限,有错误欢迎指出,有更好的建议也欢迎补充。