ZhenHe17 / blog

个人博客,希望能让各位看官有所收获,喜欢可以 star || watch ^_^ 🎉

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

用 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 除了部署便利,在分布式和虚拟机技术上也有着极大的价值,有兴趣的同学好好学呀 ^ ^

参考文章

Docker — 从入门到实践
Docker官方文档