# 技术方案
通过 docker compose
进行部署
# 项目依赖的第三方容器
- redis7
- mysql8
# 编写 Dockerfile
由于后端我们使用的 Midway
框架,它是由 ts
编写的,部署时需要将 ts
文件构建成 js
文件,所以这里会走一个 npm run build
构建命令;
如果你用的是其它 node.js
框架,比如 express
可能并不需要 build
这一阶段
FROM node:12 AS build
WORKDIR /app
COPY . .
RUN npm install --registry=https://registry.npmmirror.com
RUN npm run build
FROM node:12-alpine
WORKDIR /app
COPY /app/dist ./dist
COPY /app/bootstrap.js ./
COPY /app/package.json ./
# 在docker container 中不能自动识别宿主机的时区,可通过安装tzdata软件包,配置TZ环境变量识别正确时区.
RUN apk add --no-cache tzdata
ENV TZ="Asia/Shanghai"
# 移除开发依赖,构建正式包的时候只安装 dependencies 的依赖
RUN npm install --production --registry=https://registry.npmmirror.com
# 如果端口更换,这边可以更新一下
EXPOSE 7001
CMD ["npm", "run", "start"]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# docker 体积优化策略
合理使用
COPY
命令,COPY
命令是会对文件进行差异化对比的,只要文件没有发生变化,那么就会使用上一层的镜像缓存- 当然
ADD
也可以,但是COPY
和ADD
还是有本质区别的, docker 文档 (opens new window)有对这两个命令有所解释
- 当然
多阶段构建
- 通过多阶段构建,减少构建体积
- 多阶段构建最明显的好处是,最终打出的镜像只包含编译产物而不包含编译过程中的依赖等冗余文件,可大大减小最终镜像的体积
# 编写docker-compose.yml
version: '3'
services:
redis:
image: redis:7-alpine
restart: always
volumes:
- ./home/redis/data:/data
environment:
TZ: Asia/Shanghai
expose:
- 6379
mysql:
image: mysql:8
restart: always
volumes:
- ./home/mysql/data:/var/lib/mysql
expose:
- 3306
command: --default-authentication-plugin=mysql_native_password
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
env_file:
- ./.env
environment:
- TZ=Asia/Shanghai
- MYSQL_DATABASE=tutulist_admin
- MYSQL_ROOT_USER=root
- MYSQL_ROOT_PASSWORD=tutulist
app:
container_name: app_server
build: .
ports:
- '7001:7001'
env_file:
- ./.env
restart: always
depends_on:
- mysql
- redis
links:
- redis
- mysql
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 命令说明
文档上啥都有 文档地址 (opens new window)
# image
指定使用的镜像名称,redis 我们使用的是 alpine 版本,大小只有 28.4 MB
# restart
指定容器退出后的重启策略为始终重启
# volumes
数据卷所挂载路径设置,可以持久化的保存数据;
拿 mysql 为例,我们并不希望 mysql 容器关闭后,存储在 mysql 中存储的数据就丢了,这时我们可以将映射一个宿主机的目录,将数据存储在宿主机上
# expose
暴露端口,但不映射到宿主机,只被连接的服务访问;
不映射到宿主机也有缺点
- 就是调试不方便,拿 MySQL 为例,不映射端口到宿主机,我们就无法在宿主机使用 navicat 连接这个数据库查看数据
# environment
设置环境变量
# env_file
从文件中获取环境变量,就是我们的那个 .env
# build
指定 Dockerfile 所在文件夹的路径
# ports
暴露端口信息
格式
- 宿主机端口:容器端口
# container_name
指定容器名称
# links
默认情况下,服务之间可使用服务名称相互访问, links
允许我们定义一个别名,从而使用该别名访问其他服务
比如,之前我们在 config.dev.ts
中需要制定具体的 host
名称,现在我们就可以直接使用别名访问
# depends_on
解决容器的依赖、启动先后的问题
比如我们的 app
容器,它依赖于 mysql
和 redis
两个容器 ,只有它们先启动了,我们 app 业务容器才能对外提供服务
# 启动并不代表就能提供服务
- 拿我们的电脑的日常开机来讲,启动 -> 加载进度条 -> 展示界面,从启动到展示界面这段过程中,我们的电脑是不能给我们提供服务的
- 为了解决 这个问题 (opens new window),我们就需要用到另一个工具 wait-for-it (opens new window)
# 使用 restart: always 解决此问题
在这里我没有使用 wait-for-it
这个脚本,而是配置了 restart:always
我们的 app
业务容器连接不上 mysql
是会报错的,并且 app
业务容器也会停止运行,而我们配置 restart:always
后,每次报错停止运行后,都会重启容器,直至mysql
容器能给 app
容器提供服务。
# 其它方案
condition: service_completed_successfully (opens new window)
# 配置项目
# 创建 .dockerignore
文件
它的作用和 git
的 .gitignore
类似,我们直接将 .gitignore
内容复制进去即可
# 更改项目配置文件
根据环境的不同,midway
会加载不同的配置文件,在这里我们以 build
为例, 它会将 config.default.ts
和 config.prod.ts
配置进行合并,详情见 midway 文档 (opens new window)
# config.default.ts
所有的环境都会走这个文件,一般也会作为 开发环境 的默认配置文件。
如果在生产环境中走另一套配置,只需要在 config.prod.ts
中再添加一份配置即可,如果配置相同,它会将 config.default
的配置进行覆盖
import { MidwayConfig } from '@midwayjs/core';
export default {
keys: '1659760140542_6750',
koa: {
globalPrefix: '/api',
port: 7001,
},
jwt: {
secret: 'xxxxxxxxxxxxxxxxx', // fs.readFileSync('xxxxx.key')
expiresIn: '2d', // https://github.com/vercel/ms
refreshExpiresIn: '4d',
},
typeorm: {
dataSource: {
default: {
type: 'mysql',
host: 'mysql',
port: 3306,
synchronize: true,
database: process.env.DATABASE_NAME,
username: process.env.DATABASE_USERNAME,
password: process.env.DATABASE_PASSWORD,
entities: ['/entity'],
},
},
},
redis: {
client: {
port: 6379,
host: 'redis', // Redis host
db: 0,
},
},
} as MidwayConfig;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 服务器部署
# 安装 node、npm、git
sudo yum install –y nodejs
sudo yum install -y npm
sudo yum install -y git
2
3
# 下载代码到服务器
git clone
拉取代码。
ls-a
查看是否存在 .env
文件,可能会被 .gitignore
给忽略掉了,没有就 touch .env
创建一个,将项目中的环境变量 copy
进去
# 运行项目
关于 docker compose 更多命令 (opens new window)
docker compose up -d
# 查看运行状态,是否是 running 状态
docker compose ps
# 修改服务器防火墙
将 7001 端口暴露出来
# 测试是否能访问
# 更改服务器 nginx 配置
添加 /api/
开头的配置并配置代理,让前端可以通过 api
进行访问
location /api/ {
proxy_pass http://43.138.82.165:7001;
}
2
3
重启 nginx 容器
docker restart nginx
# 代码更新,重新部署
当我们的业务代码发生变更时,需要重新构建
git pull
- 拉取最新代码
docker compose up -d --build app --no-deps
- --build:构建(重新构建)项目中的服务容器。
- [service name]:在 docker-compose.yml 文件中为我们的业务容器定义的服务名称 app
- --no-deps:不启动与该 service 关联的其它容器。
以上的方式还是要登录服务器,并在服务器上手敲命令。
在下一篇中,我们会借助 gitlab ci/cd
和 gitlab webhooks
最终将这一流程实现自动化