用 docker 一键部署前后端项目

4,159 阅读5分钟

大家都比较反感复杂的环境配置,这里分享一下怎么用 docker 一键配置环境。

先上代码:

示例项目,技术栈:docker nginx egg mysql react

原文链接 欢迎 star ~

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官方文档