用 docker 一键部署前后端项目
ZhenHe17 opened this issue · comments
大家都比较反感复杂的环境配置,这里分享一下怎么用 docker 一键配置环境。
先上代码:
示例项目,技术栈:docker nginx egg mysql react
Docker
Docker 属于一种操作系统层面的虚拟化技术。传统虚拟机技术是虚拟出一套硬件,硬件上虚拟操作系统,在该系统上运行应用进程。而 Docker 创建的容器没有自己的内核,而且也没有进行硬件虚拟,极大的简化了容器创建和维护的成本。
Docker 核心概念中有: 镜像(image) 容器(container),它们之间的关系有点像编程中 类 和 实例 的关系,镜像是静态的,可以被提前创建好。容器有着自己的运行状态,允许进入容器,操作容器,容器运行在一个隔离的环境里。
不熟悉 docker 的同学可以先看 Docker — 从入门到实践
Docker Compose
Docker Compose 是 Docker 官方项目,用来定义和运行多个 Docker 容器的应用。通过编写 docker-compose.yml 配置文件,快速的部署分布式应用。文件中通常配置:镜像、端口映射、文件映射、容器间的依赖等。
可以参考 Docker compose 官方文档
部署前端工程
对于前后端分离的项目,部署前端工程通常比较简单。这里把打包好的静态资源用nginx映射好即可。
# ./docker-compose.yml
nginx:
build:
context: ./webapp
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/cert:/etc/nginx/cert
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
networks:
- backend
restart: always
docker-compose.yml 里主要配置了
- build: -> context: ./webapp 指定镜像通过 ./webapp/Dockerfile 生成
- ports -> "80:80" 访问本地 localhost 80端口即可访问容器内80端口
- volumes: -> ./nginx/... 将本地文件映射到容器内,直接修改本地文件就可以修改容器里运行的nginx配置
# ./webapp/Dockerfile
FROM node:12.10.0-alpine as client
RUN mkdir /opt/webapp
WORKDIR /opt/webapp
COPY ./package.json .
RUN npm install --production --registry=https://registry.npm.taobao.org
COPY . .
RUN npm run build
FROM nginx
WORKDIR /usr/app/
COPY --from=client /opt/webapp/build/ /usr/share/nginx/html/
./webapp/Dockerfile 配置分两个阶段:第一个阶段依赖是 node,主要是安装项目依赖 npm install 并打包 npm run build。第二个阶段依赖 nginx,把阶段一打包好的静态资源放进 nginx 目录下。
到这一步为止,前端工程就部署好了,运行 docker-compose up 后可以在 localhost/ 下访问。
部署后端工程
EGG
先把 egg 的工程跑起来
# ./docker-compose.yml
fe_article_server:
build:
context: ./server
ports:
- "7001:7001"
depends_on:
- db
- redis
networks:
- backend
配置:
- build: -> context: ./server 指定镜像通过 ./server/Dockerfile 生成
- ports -> "7001:7001" 本地端口映射,这个无关紧要,后面会通过 nginx 代理它
- depends_on: -> 它依赖 db和redis 这两个服务。稍后会展开 mysql 和 redis 的配置
# ./webapp/Dockerfile
FROM node:12.10.0-alpine
RUN apk --update --no-cache add tzdata bash curl \
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone \
&& apk del tzdata
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY ./package.json /usr/src/app/
RUN npm install --production --registry=https://registry.npm.taobao.org
COPY . /usr/src/app
COPY wait-for-it.sh /
CMD /wait-for-it.sh db:3306 -- npm run start
这里和 webapp 相似,RUN apk ... 设置了上海时区、npm install 安装项目依赖、npm run start 启动项目,egg 默认运行在 7001 端口。
MySQL
我们直接使用官方镜像,只需要配置 docker-compose.yml 不需要 Dockerfile
# ./docker-compose.yml
db:
image: mysql:5.7
ports:
- 3306:3306
environment:
- MYSQL_DATABASE=article
- MYSQL_USER=worker
- MYSQL_PASSWORD=wptworker
- MYSQL_ROOT_PASSWORD=wptroot
- TZ=UTC
volumes:
- ./db/conf.d:/etc/mysql/conf.d:ro
- ./db/logs:/var/log/mysql:rw
- ./db/initdb.d:/docker-entrypoint-initdb.d:ro
networks:
- backend
主要配置了 mysql 的默认数据库、账号密码、配置文件映射。 docker-compose up 后数据库直接启动在3306端口,可以用命令行或者图形化工具连接管理数据库
Redis
redis 和 mysql 大体相同
# ./docker-compose.yml
redis:
build: ./redis
ports:
- "6379:6379"
volumes:
- ./redis/redis.conf/:/usr/local/etc/redis.conf
- ./redis/data:/usr/local/redis/data
- ./redis/redis.log:/usr/local/redis/redis.log
restart: always
container_name: redis
networks:
- backend
# ./redis/Dockerfile
FROM redis
CMD [ "redis-server", "/usr/local/etc/redis.conf" ]
在 Dockerfile 中使用了官方镜像,加了一行配置指定了 redis.conf 的位置,这个路径已经在 yml 文件中映射好了。
踩坑
Dockerfile
COPY ./package.json .
RUN npm install --production --registry=https://registry.npm.taobao.org
COPY . .
RUN npm run build
这里我们 COPY 了两次。
COPY . .
RUN npm install --production --registry=https://registry.npm.taobao.org
RUN npm run build
COPY 一次虽然可以,但是 docker 构建镜像时,是会分层缓存的。一旦目录下有任何修改,这三条指令就会重新执行,之前的缓存会无效。
而先 COPY ./package.json . 则不同,只要项目依赖不变,再次构建时,npm install 就会使用之前的缓存,大家应该知道,安装依赖这一步是比较慢的。
node-sass
如果安装 node-sass 出了问题,可以直接用镜像 blairguk/node-sass-alpine:8.11.0 代替 node:10-alpine 镜像。
容器内访问宿主机的 localhost
nginx代理很可能需要代理本机上的其他端口。虽然你可以直接读本机 IP 来访问宿主机,但是这就和当前环境绑定了。这很不 docker ,笔者在这里用 qoomon/docker-host 镜像,配置好 networks 以后,访问 dockerhost(或者你起的服务名) 即可访问宿主机 localhost。附上配置:
# ./docker-compose.yml
dockerhost:
image: qoomon/docker-host
cap_add: [ 'NET_ADMIN', 'NET_RAW' ]
restart: on-failure
networks:
- dev
总结
在我们使用 docker 之前,在新环境部署项目通常都比较繁杂比较痛苦。mysql、nginx、redis、前后端项目挨个安装挨个配置。而现在只需要 docker-compose up 🚀🚀🚀只能说爽的一比了。
当然 docker 除了部署便利,在分布式和虚拟机技术上也有着极大的价值,有兴趣的同学好好学呀 ^ ^