1. 前言
继上次写了一篇 CentOS7中使用docker-compose部署SpringBoot+Redis+MySQL+Nginx 博客后,我把前端页面也加入其中,重新整了一套前后端分离的项目,并且还是使用 docker-compose 部署;值得关注的是,做到了快速部署发布。
2. 源码
GitHub 地址:https://github.com/intomylife/Docker
3. 环境
3.1 开发工具
- 后端: IntelliJ IDEA
- 前端: Visual Studio Code
3.2 其它工具
- 连接服务器: Termius
- 文件传输: Cyberduck
- 连接 MySQL: Navicat Premium
- 连接 Redis: Redis Desktop Manager
3.3 版本号
- 后端: SpringBoot 2.0.3.RELEASE
- 前端: Vue 2.5.2
- 其他: JDK 1.8、Maven 3.3.9、Redis 4.0.14、MySQL 5.7、npm 6.11.3、node 12.11.1、Docker 18.09.5,docker-compose 1.24.1
4. 准备工作
注:准备工作需要按步骤顺序进行
4.1 服务器
4.1.1 需要安装 Docker 以及 docker-compose,可参考:
4.1.2 开启 TCP
注:此操作可能会给服务器带来安全隐患(暴露的2375
端口被攻击)。如果不幸已经被挖矿,没关系,戳这里:服务器被挖矿后的解决思路。
---
1. 创建 /etc/systemd/system/docker.service.d 目录
---
[root@zwc /]# mkdir /etc/systemd/system/docker.service.d
[root@zwc /]# cd /etc/systemd/system/docker.service.d
---
2. 新建 tcp.conf 文件
---
[root@zwc docker.service.d]# vim tcp.conf
---
3. 输入内容,保存退出
---
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375
---
4. 重启服务,使配置生效
---
[root@zwc docker.service.d]# systemctl daemon-reload && systemctl restart docker
---
5. 如果开启了防火墙,得开放 2375 端口
---
[root@zwc docker.service.d]# firewall-cmd --zone=public --add-port=2375/tcp
---
6. 进入阿里云控制台,到安全组中添加 2375 端口
---
4.1.3 TLS 加密
注:当加密后,就不用担心因为暴露了2375
端口被攻击;加密或不加密对于代码的改动量非常小,更多的只是配置操作。
- 首先使用如下
generateCACertificate.sh
脚本在服务器上生成证书文件
## 自动生成证书脚本
#!/bin/bash
## 全局变量
### 密码
PASSPHRASE="987654321"
echo "===================== 开始 ====================="
## 创建需要的目录
mkdir -p /root/ca/my
## 进入到目录
cd /root/ca/my
## 生成 ca 私钥,并设置密码
openssl genrsa -aes256 -passout pass:$PASSPHRASE -out ca-key.pem 4096
## 使用 ca 私钥创建 ca 证书
openssl req -new -x509 -sha256 -passin "pass:$PASSPHRASE" -days 365 -subj "/CN=*" -key ca-key.pem -out ca.pem
## 生成服务器私钥
openssl genrsa -out server-key.pem 4096
## 使用服务器私钥创建服务器证书
openssl req -new -sha256 -subj "/CN=*" -key server-key.pem -out server.csr
## 生成服务器自签证书
openssl x509 -req -days 365 -sha256 -in server.csr -passin "pass:$PASSPHRASE" -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem
## 生成客户端私钥
openssl genrsa -out key.pem 4096
## 使用客户端私钥创建客户端证书
openssl req -new -subj "/CN=client" -key key.pem -out client.csr
## 配置 extendedKeyUsage
sh -c 'echo extendedKeyUsage=clientAuth >> extfile.cnf'
## 生成客户端自签证书
openssl x509 -req -days 365 -sha256 -in client.csr -passin "pass:$PASSPHRASE" -CA ca.pem -CAkey ca-key.pem -CAcreateserial -extfile extfile.cnf -out cert.pem
## 删除多余文件
rm -rf ca.srl client.csr ipWhiteList.cnf server.csr extfile.cnf
## 更改密钥权限
chmod 0400 ca-key.pem server-cert.pem server-key.pem
chmod 0444 ca.pem key.pem cert.pem
echo "===================== 结束 ====================="
- 接下来在步骤
4.1.2 开启 TCP
的基础上做修改
---
1. 编辑 /etc/systemd/system/docker.service.d/tcp.conf 文件,把刚刚生成的证书路径加入其中
---
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd --tlsverify --tlscacert=/root/ca/my/ca.pem --tlscert=/root/ca/my/server-cert.pem --tlskey=/root/ca/my/server-key.pem -H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375
---
2. 重启服务,使配置生效
---
[root@zwc docker.service.d]# systemctl daemon-reload && systemctl restart docker
4.2 本地
注:这里是在 macOS 系统中操作的
4.2.1 安装 Docker
注:vsCode 在构建镜像时需要用到 Docker 命令,所以本地也需要安装 Docker
我是参考菜鸟教程中手动下载安装的,下载得到 .dmg 文件后,拖拽安装就结束了。
------------------ 2020.06.06 更新 ------------------
这时发现,vsCode
更新到了1.45.1
版本后,不需要本地启动docker
也可以推送镜像了。那么本地中4.2.1 安装 Docker
步骤可以跳过了
4.2.2 拷贝服务器生成的证书
- 在服务器的
/root/ca/my
目录中,拷贝ca.pem
、cert.pem
以及key.pem
文件 - 拷贝到本地
Docker
的安装目录中,如我的是/Users/zouwencong/.docker
目录
4.2.3 配置 hosts 文件
- 打开终端,输入
sudo vim /private/etc/hosts
- 输入密码
- 写入内容
xx.xxx.xxx.xxx DockerHost
,xx.xxx.xxx.xxx 就是安装了 Docker 的服务器地址 - 保存退出
4.2.4 配置 settings.xml 文件
- 找到 Maven 目录中的
settings.xml
文件 - 找到
<servers></servers>
节点,配置如下子节点
<server>
<id>docker-hub</id>
<username>your username</username>
<password>your password</password>
<configuration>
<email>your email</email>
</configuration>
</server>
- 保存退出
4.2.5 vsCode 中安装 Docker 插件
- 打开 vsCode
- 点击左侧栏中的组件入口
- 输入
Docker
进行搜索 - 选择第一个,点击
Install
- 下载完成后,根据提示重启 vsCode
4.2.6 vsCode 中连接远程 Docker
- 打开 vsCode
- 点击
首选项
->设置
- 在搜索框中输入
docker:host
- 在搜索结果 Docker: Host 下面的文本框中输入
tcp://DockerHost:2375/
,此处的 DockerHost 就是对应上面准备工作中配置的 hosts 文件 - 在搜索框中输入
docker:tls
- 在搜索结果 Docker: Tls Verify 下面的文本框中输入
1
- 保存设置
4.2.7 vsCode 中连接远程 DockerHub
- 打开 vsCode
- 点击左侧栏中的
Docker
插件 - 点击
Connect Registry...
- 在顶部弹出的框中选择
Docker Hub
- 输入
Docker Hub
用户名,回车 - 输入
Docker Hub
密码,回车 - 连接成功
5. 后端
5.1 搭建
此次搭建只整合了 Redis 和 MySQL,如果对具体整合过程感兴趣,可以前往:
需要注意的地方如下
5.2 配置文件
# 端口
server.port=8080
# 数据源
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.host=127.0.0.1
spring.datasource.url=jdbc:mysql://${spring.datasource.host}:3306/base_db?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=PRC&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
# 打印 sql 日志
logging.level.com.zwc.base.mapper=debug
# Redis 配置
## Redis 数据库索引:默认为 0。Redis 一共有 16 个数据库,索引分别为 0-15。从 Redis 客户端也可以看出,从 db0 ~ db15。
spring.redis.database=2
## Redis 服务器地址
spring.redis.host=127.0.0.1
## Redis 服务器端口
spring.redis.port=6379
## Redis 服务器密码
spring.redis.password=123456789
注:这里专门把 host 都配置为本地 127.0.0.1 环境,因为在使用 docker-compose 服务编排时会用代码主动做处理,这时一套配置文件就足够了。
5.3 提供服务
代码片段
/*
* @ClassName BaseTableService
* @Desc TODO 统计访问次数
* @Date 2019/9/16 14:42
* @Version 1.0
*/
@Transactional
public String comeCounts(String sessionId) {
// 返回数量
Integer resultCount = 1;
// 先从缓存中取访问次数
Object redisComeCounts = redisClient.get(BaseServiceConstant.COME_COUNTS);
// 取出所有 sessionId
List<BaseTable> sessionIdList = new ArrayList<>();
// 访问次数 - 非空判断
if(redisComeCounts != null) {
// 取出所有 sessionId
sessionIdList = JSON.parseObject(redisComeCounts.toString(), new TypeReference<List<BaseTable>>() {});
// 数据库中的数量
Integer mysqlCount = super.count(new QueryWrapper<BaseTable>());
// 计算出返回数量:mysql + redis
resultCount = mysqlCount + sessionIdList.size();
// 判断是否该同步到数据库
if(sessionIdList.size() >= BaseServiceConstant.MAX_COUNTS) {
// 同步到数据库中
if(save(sessionIdList)) {
// 同步成功,清空缓存
sessionIdList = new ArrayList<>();
redisClient.delete(BaseServiceConstant.COME_COUNTS);
}
}
}
// 存入对象
BaseTable baseTable = new BaseTable();
// sessionId
baseTable.setSessionId(sessionId);
// 添加时间
baseTable.setCreateDatetime(new Date());
// 放入集合中
sessionIdList.add(baseTable);
// 存入缓存中
redisClient.set(BaseServiceConstant.COME_COUNTS, JSON.toJSONString(sessionIdList));
try {
// 返回数量
return "ip:" + InetAddress.getLocalHost().getHostAddress() + " / count:" + String.valueOf(resultCount) + " / time:" + sdf.format(new Date());
} catch (UnknownHostException e) {
e.printStackTrace();
// 返回
return "获取地址失败";
}
}
/*
* @ClassName BaseTableService
* @Desc TODO 批量存入数据到数据库
* @Date 2019/9/16 14:49
* @Version 1.0
*/
public boolean save(List<BaseTable> baseTables) {
// 调用批量新增方法
return super.saveBatch(baseTables);
}
主要做的事情及细节有:
- 统计访问次数,同时把 Redis 和 MySQL 都强行用上;在 Redis 中写入了足够的访问次数对象的数量后,会同步到 MySQL,也就是降低了 MySQL 的写 IO 操作。
- 显示当前时间,主要是处理了容器时区问题。
5.4 Dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE
ADD ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
- 注意此文件的路径为 :src/main/docker/
- 此文件是构建 Docker 镜像的核心文件
- FROM openjdk:8-jdk-alpine:基础镜像环境 JDK1.8
- VOLUME /tmp:指定了挂载目录
- ARG JAR_FILE:与工程中配置的 buildArgs 对应,动态获取打包后的名称
- ADD ${JAR_FILE} app.jar:把生成的 jar 包拷贝到 Docker 容器中并命名为 app.jar
- 最后一行是修改 Tomcat 随机数生成方式,加快 Tomcat 启动
5.5 pom.xml 文件
5.5.1 service
代码片段
<!-- 在 properties 下声明相应的版本信息,然后在 dependency 下引用的时候用 ${spring-version} 就可以引入该版本 jar 包了 -->
<properties>
<!-- 编码 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- jdk -->
<java.version>1.8</java.version>
<!-- 上传镜像 -->
<!-- 远程仓库用户名 -->
<docker.image.prefix>intomylife</docker.image.prefix>
<!-- 远程 docker 服务地址 -->
<docker.host>https://DockerHost:2375/</docker.host>
<!-- 指定 docker hub 仓库地址,用户名密码配置在 settings.xml 文件中 -->
<settings.docker.id>docker-hub</settings.docker.id>
<docker.registry.url>https://index.docker.io/v1/</docker.registry.url>
</properties>
- 配置的 <docker.host> 节点中的 DockerHost 对应上面准备工作中配置的 hosts 文件
- 如果你只开启了
TCP
没有TLS
加密,那么此处https
更改为http
;即加密时配置:https://DockerHost:2375/
,未加密时配置:http://DockerHost:2375/
5.5.2 service-core
代码片段
<!-- 插件依赖 -->
<build>
<!-- 打包插件 -->
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- Docker 插件 -->
<plugin>
<!-- 三坐标 -->
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>1.1.1</version>
<!-- 绑定插件执行动作 -->
<executions>
<!-- mav package = mav package + docker:build + docker:push -->
<execution>
<id>build-image</id>
<phase>package</phase>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
<!-- 配置信息 -->
<configuration>
<!-- 远程 docker 服务地址 -->
<dockerHost>${docker.host}</dockerHost>
<!-- 指定 docker hub 仓库地址,用户名密码配置在 settings.xml 文件中 -->
<serverId>${settings.docker.id}</serverId>
<registryUrl>${docker.registry.url}</registryUrl>
<!-- 镜像名称 -->
<imageName>${docker.image.prefix}/${docker.image.name}:${project.version}</imageName>
<!-- Dockerfile 文件的位置 -->
<dockerDirectory>src/main/docker</dockerDirectory>
<!-- 文件资源 -->
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
<!-- 打包后的名称 -->
<buildArgs>
<JAR_FILE>${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
- 配置的 <execution> 节点中的内容是快速部署的关键
6. 前端
6.1 搭建
- 打开终端
- 输入命令
vue
如果不存在则输入命令sudo npm install -g vue-cli
全局安装 vue-cli - 进入到项目预存放目录
- 输入命令
vue init webpack projectName
在当前目录创建一个基于 webpack 的项目 - 接下来就是填写一些关于项目的信息就创建完毕
- 输入命令
cd projectName
进入项目目录 - 输入命令
npm install
安装依赖
需要注意的地方如下
6.2 config/index.js 文件
代码片段
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
'/api' : {
// target: 'https://xxx.com',
target: 'http://127.0.0.1:8080',
changeOrigin: true,
pathRewrite: {
'^/api': '' // api 开头的接口都使用此代理,如果添加了此行代码,那么意思就是在接口中去掉 api
},
}
},
- 配置 proxyTable 解决了开发时跨域问题
6.3 utils/request.js 文件
代码片段
// create an axios instance
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // api 的 base_url
// baseURL: "http://xxx", // api 的 base_url
timeout: 5000 // request timeout
})
- process.env.VUE_APP_BASE_API 读取环境变量,拼接到请求 url 中
- VUE_APP_BASE_API 分别在 config/dev.env.js 和 config/prod.env.js 文件中配置,配置的值为
/api
,也就是上面 config/index.js 中配置的会被拦截的请求前缀
6.4 Dockerfile
FROM node:lts-alpine
# 如果你在国内,这行配置很有必要,不然打包会非常非常慢
RUN npm config set registry https://registry.npm.taobao.org
# install simple http server for serving static content
##RUN npm install -g http-server
# make the 'app' folder the current working directory
WORKDIR /app
# copy both 'package.json' and 'package-lock.json' (if available)
COPY package*.json ./
# install project dependencies
RUN npm install
# copy project files and folders to the current working directory (i.e. 'app' folder)
COPY . /app
# build app for production with minification
##RUN npm run build
# expose port
##EXPOSE 9527
# 加入端口自定义配置,避免与其他K8S容器端口冲突
##CMD [ "http-server","-p","9527", "dist" ]
# 容器启动后执行的命令
CMD [ "npm", "run", "build" ]
- FROM node:lts-alpine:基础镜像环境 node
- 设置国内镜像,拷贝依赖信息文件,安装依赖
- 此时的 Docker 镜像,是 Vue 未打包成静态文件的前端代码;这点很重要,因为在启动 Docker 镜像时,需要把 Vue 打包后的静态文件映射出来,这时的流程就是 启动->映射->Vue 打包;否则如果先生成 Vue 打包后的静态文件,再启动->映射的话,容器中生成的静态文件就会被宿主机的空文件夹给覆盖掉
7. docker-compose.yaml
7.1 文件说明
此文件的默认名称为 docker-compose,后缀名可以为 .yml 也可以为 .yaml。
7.2 version
version: '3'
- 构建文件的语法版本信息。version: ‘3’ 表示使用第三代语法。
7.3 services
version: '3'
services:
service_redis:
...
service_mysql:
...
service_springboot:
...
service_vue:
...
service_nginx:
...
- 包含此工程中所有的服务列表。
- 服务可以是已存在的镜像(本地或远程),也可以是构建出来的镜像;如果其中有需要构建的镜像,则需要一个 Dockerfile 文件。
7.4 service_redis
service_redis:
container_name: container_redis
image: redis:4.0.14
environment:
- TZ=Asia/Shanghai
ports:
- "6379:6379"
volumes:
- ./config/redis/redis.conf:/usr/local/etc/redis/redis.conf
- ./data/redis/:/data/
- ./log/redis/:/var/log/redis/
command: redis-server /usr/local/etc/redis/redis.conf
restart: always
Redis 服务描述:
- service_redis:服务名称,可自定义。
- container_name:容器名称,可自定义;也可不写,那会自动生成,生成规则为 【docker-compose.yaml 文件的父目录名称 + _ + 服务名称 + 从一开始的数字】。
- image:指定镜像来启动容器。此处指定为 Redis 官方镜像,版本为 4.0.14。
- environment:为启动的容器添加环境变量。此处配置了容器的时区。
- ports:端口映射,映射规则为 宿主机端口:容器端口。此处映射 宿主机 6379 端口到 容器 6379 端口。
- volumes:配置映射,映射规则为 宿主机:容器,可以映射文件或目录。此处映射了 配置文件,数据目录以及日志目录。
- command:容器启动后执行的命令。此处命令为 使用配置文件来启动 Redis 容器。
- restart:赋固定值 always,表示如果容器启动失败,会一直尝试重连。
注:redis.conf 配置文件中修改了如下几点
- daemonize no:前台启动,在 Docker 中后台启动 Redis 容器会报错
- requirepass 123456789:设置密码
- # bind 127.0.0.1:注释掉了,使外网可访问
7.5 service_mysql
service_mysql:
container_name: container_mysql
image: mysql:5.7
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: 123456
MYSQL_ROOT_HOST: '%'
ports:
- "3306:3306"
volumes:
- ./config/mysql/my.cnf:/etc/mysql/conf.d/my.cnf
- ./data/mysql/:/var/lib/mysql/
- ./data/init/:/docker-entrypoint-initdb.d/
- ./log/mysql/:/var/log/mysql/
command: [
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_unicode_ci'
]
restart: always
MySQL 服务描述:
- service_mysql:服务名称,可自定义。
- container_name:容器名称,可自定义;也可不写,那会自动生成,生成规则为 【docker-compose.yaml 文件的父目录名称 + _ + 服务名称 + 从一开始的数字】。
- image:指定镜像来启动容器。此处指定为 MySQL 官方镜像,版本为 5.7。
- environment:为启动的容器添加环境变量。此处配置了容器的时区,以及数据库 ROOT 密码和权限。
- ports:端口映射,映射规则为 宿主机端口:容器端口。此处映射 宿主机 3306 端口到 容器 3306 端口。
- volumes:配置映射,映射规则为 宿主机:容器,可以映射文件或目录。此处映射了 配置文件,数据目录,初始化 SQL 目录以及日志目录。
- command:容器启动后执行的命令。此处命令为 设置字符编码。
- restart:赋固定值 always,表示如果容器启动失败,会一直尝试重连。
注:my.cnf 配置文件中有一个需要注意的地方如下
# 开启 bin-log,并指定文件目录和文件名前缀
log-bin=/var/log/mysql/binlog
7.6 service_springboot
service_springboot:
container_name: container_springboot
image: intomylife/docker-compose-service:1.0
environment:
TZ: Asia/Shanghai
spring.datasource.host: service_mysql
spring.redis.host: service_redis
expose:
- "8080"
depends_on:
- service_redis
- service_mysql
restart: always
SpringBoot 服务描述:
- service_springboot:服务名称,可自定义。
- container_name:容器名称,可自定义;也可不写,那会自动生成,生成规则为 【docker-compose.yaml 文件的父目录名称 + _ + 服务名称 + 从一开始的数字】。
- image:指定镜像来启动容器。此处指定为自己上传的后端镜像,版本为 1.0。
- environment:为启动的容器添加环境变量。此处配置了容器的时区;并指定了 MySQL 的 host 为 service_mysql 服务,Redis 的 host 为 service_redis 服务,这两个 host 正是对应 SpringBoot 项目的配置文件(application.properties)中两个 host;这也是上面提到的 用代码主动做处理。(注:由于 MySQL 服务和 Redis 服务都只有一个,所有这里指定服务名和容器名都是可以的)
- expose:暴露容器内端口,不映射到宿主机。因为 SpringBoot 服务会被 Nginx 做代理转发,所以不用暴露并映射到外部。
- depends_on:依赖服务。在整个工程启动时,会先启动依赖服务,再启动当前服务。也就是说,这里 SpringBoot 服务会等待 MySQL 服务和 Redis 服务启动完成后,才会开始启动。
- restart:赋固定值 always,表示如果容器启动失败,会一直尝试重连。
7.7 service_vue
service_vue:
container_name: container_vue
image: intomylife/docker-compose-front:1.0
environment:
- TZ=Asia/Shanghai
volumes:
- ./data/nginx/:/app/dist/
Vue 服务描述:
- service_vue:服务名称,可自定义。
- container_name:容器名称,可自定义;也可不写,那会自动生成,生成规则为 【docker-compose.yaml 文件的父目录名称 + _ + 服务名称 + 从一开始的数字】。
- image:指定镜像来启动容器。此处指定为自己上传的前端镜像,版本为 1.0。
- environment:为启动的容器添加环境变量。此处配置了容器的时区。
- volumes:配置映射,映射规则为 宿主机:容器,可以映射文件或目录。此处就是把 Vue 打包后的静态文件映射出来。
注:此服务的作用就是把 Vue 打包成静态页面,映射到宿主机目录;此服务在打包结束后就会自动停止。
7.8 service_nginx
service_nginx:
container_name: container_nginx
image: nginx:1.8
environment:
- TZ=Asia/Shanghai
ports:
- "8000:8000"
volumes:
- ./config/nginx/nginx.conf:/etc/nginx/nginx.conf
- ./data/nginx/:/usr/share/nginx/html/
- ./log/nginx/:/var/log/nginx/
depends_on:
- service_vue
- service_springboot
restart: always
Nginx 服务描述:
- service_nginx:服务名称,可自定义。
- container_name:容器名称,可自定义;也可不写,那会自动生成,生成规则为 【docker-compose.yaml 文件的父目录名称 + _ + 服务名称 + 从一开始的数字】。
- image:指定镜像来启动容器。此处指定为 Nginx 官方镜像,版本为 1.8。
- environment:为启动的容器添加环境变量。此处配置了容器的时区。
- ports:端口映射,映射规则为 宿主机端口:容器端口。此处映射 宿主机 8000 端口到 容器 8000 端口。
- volumes:配置映射,映射规则为 宿主机:容器,可以映射文件或目录。此处映射了 配置文件,数据目录以及日志目录。
- depends_on:依赖服务。在整个工程启动时,会先启动依赖服务,再启动当前服务。也就是说,这里 Nginx 服务会等待 SpringBoot 服务启动完成后,才会开始启动。
- restart:赋固定值 always,表示如果容器启动失败,会一直尝试重连。
注:
-
volumes 映射的数据目录
./data/nginx/
,就是 service_vue 服务从容器中映射出来的 Vue 打包后的静态文件,container_vue 容器 映射到 宿主机,宿主机 映射到 container_nginx 容器,所以这里还需要在 depends_on 中配置 service_vue 服务等待 Vue 打包结束。这样,启动时静态文件就会直接被映射到 Nginx 的访问目录中。 -
nginx.conf 配置文件中需要注意如下地方
... 省略部分 ...
# 负载均衡
upstream dispense {
## server docker-compose-rapid-deployment_service_springboot_1:8080 weight=1;
## server docker-compose-rapid-deployment_service_springboot_2:8080 weight=2;
server container_springboot:8080;
}
... 省略部分 ...
# 代理静态页面
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
# 代理接口地址
location ^~ /api {
proxy_pass http://dispense/;
}
- 直接访问 Nginx,就会访问到 Vue 打包后的静态文件,也就是前端页面
/api
开头的请求会被转发到 SpringBoot 服务,也就是后端接口;这里也解决了发布后跨域问题/api
与 Vue 中 config/prod.env.js 文件配置的 VUE_APP_BASE_API 对应
8. 使用我的镜像部署发布
8.1 拷贝项目
- 文件具体在 build 目录,可直接下载
- 把 build 目录拷贝到服务器上
- 进入目录
[root@zwc docker-compose-rapid-deployment]# ls -all
total 24
drwxrwxr-x 5 root root 4096 Nov 14 15:44 .
drwxr-xr-x 9 root root 4096 Nov 25 13:40 ..
drwxrwxr-x 5 root root 4096 Nov 14 15:44 config
drwxrwxr-x 6 root root 4096 Nov 14 15:44 data
-rw-rw-r-- 1 root root 2246 Nov 14 15:44 docker-compose.yaml
drwxrwxrwx 5 root root 4096 Nov 14 15:44 log
- 日志目录需要赋值权限:chmod -R 777 log/
- 自动生成证书脚本需要执行权限:chmod +x generateCACertificate.sh
注:generateCACertificate.sh 文件是在准备工作中用到的,保存在build
目录中只是做个备份,跟后面部署发布没有任何关系;所以在查看目录时没有粘贴出来。
8.2 启动
---
1. 启动 Docker
---
[root@zwc docker-compose-rapid-deployment]# systemctl start docker.service
---
2. 使用 tree 命令查看当前目录结构树
---
[root@zwc docker-compose-rapid-deployment]# tree
.
├── config
│ ├── mysql
│ │ └── my.cnf
│ ├── nginx
│ │ └── nginx.conf
│ └── redis
│ └── redis.conf
├── data
│ ├── init
│ │ └── init.sql
│ ├── mysql
│ ├── nginx
│ └── redis
├── docker-compose.yaml
└── log
├── mysql
├── nginx
└── redis
13 directories, 5 files
---
3. 使用 docker-compose up -d 命令后台启动
---
[root@zwc docker-compose-rapid-deployment]# docker-compose up -d
Creating network "docker-compose-rapid-deployment_default" with the default driver
Creating container_redis ... done
Creating container_mysql ... done
Creating container_vue ... done
Creating container_springboot ... done
Creating container_nginx ... done
---
4. 使用 docker ps -a 命令查看容器启动情况,都在正常启动中
---
[root@zwc docker-compose-rapid-deployment]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6682af002b4a nginx:1.8 "nginx -g 'daemon of…" 36 seconds ago Up 33 seconds 80/tcp, 443/tcp, 0.0.0.0:8000->8000/tcp container_nginx
2c9568cc5f46 intomylife/docker-compose-service:1.0 "java -Djava.securit…" 37 seconds ago Up 36 seconds 8080/tcp container_springboot
7371e815f820 mysql:5.7 "docker-entrypoint.s…" 39 seconds ago Up 37 seconds 0.0.0.0:3306->3306/tcp, 33060/tcp container_mysql
0ea3609afea8 redis:4.0.14 "docker-entrypoint.s…" 39 seconds ago Up 37 seconds 0.0.0.0:6379->6379/tcp container_redis
901ec5e4dc2e intomylife/docker-compose-front:1.0 "docker-entrypoint.s…" 40 seconds ago Up 37 seconds container_vue
---
5. 等待一分钟左右,再次使用 docker ps -a 命令查看容器启动情况,发现 container_vue 容器已经自动停止了,也就是正如上面所说,此服务的作用就是把 Vue 打包成静态页面,映射到宿主机目录;此服务在打包结束后就会自动停止。
---
[root@zwc docker-compose-rapid-deployment]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6682af002b4a nginx:1.8 "nginx -g 'daemon of…" 2 minutes ago Up About a minute 80/tcp, 443/tcp, 0.0.0.0:8000->8000/tcp container_nginx
2c9568cc5f46 intomylife/docker-compose-service:1.0 "java -Djava.securit…" 2 minutes ago Up 2 minutes 8080/tcp container_springboot
7371e815f820 mysql:5.7 "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 0.0.0.0:3306->3306/tcp, 33060/tcp container_mysql
0ea3609afea8 redis:4.0.14 "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 0.0.0.0:6379->6379/tcp container_redis
901ec5e4dc2e intomylife/docker-compose-front:1.0 "docker-entrypoint.s…" 2 minutes ago Exited (0) About a minute ago container_vue
---
6. 使用 docker-compose logs service_name 命令查看容器启动日志,这里查看的是 container_vue 容器的日志,跟期望一样,是记录的 Vue 打包过程。
---
[root@zwc docker-compose-rapid-deployment]# docker-compose logs service_vue
Attaching to container_vue
container_vue |
container_vue | > docker-compose-front@1.0.0 build /app
container_vue | > node build/build.js
container_vue |
container_vue | Hash: 50a22d73abcb0aec1ca2
container_vue | Version: webpack 3.12.0
container_vue | Time: 31700ms
container_vue | Asset Size Chunks Chunk Names
container_vue | static/js/vendor.a99313ee7ba8dc25e131.js 154 kB 0 [emitted] vendor
container_vue | static/js/app.fc7a4bfccef2ff584b23.js 10.7 kB 1 [emitted] app
container_vue | static/js/manifest.3ad1d5771e9b13dbdad2.js 858 bytes 2 [emitted] manifest
container_vue | static/css/app.d2400a32d2511dba922aebae4b8a11bb.css 432 bytes 1 [emitted] app
container_vue | static/css/app.d2400a32d2511dba922aebae4b8a11bb.css.map 797 bytes [emitted]
container_vue | static/js/vendor.a99313ee7ba8dc25e131.js.map 766 kB 0 [emitted] vendor
container_vue | static/js/app.fc7a4bfccef2ff584b23.js.map 20.5 kB 1 [emitted] app
container_vue | static/js/manifest.3ad1d5771e9b13dbdad2.js.map 4.97 kB 2 [emitted] manifest
container_vue | index.html 526 bytes [emitted]
container_vue |
container_vue | Build complete.
container_vue |
container_vue | Tip: built files are meant to be served over an HTTP server.
container_vue | Opening index.html over file:// won't work.
container_vue |
---
7. 这时,再使用 tree 命令查看目录结构树,发现 Vue 打包后的静态文件成功被映射出来
---
[root@zwc docker-compose-rapid-deployment]# tree
.
├── config
│ ├── mysql
│ │ └── my.cnf
│ ├── nginx
│ │ └── nginx.conf
│ └── redis
│ └── redis.conf
├── data
│ ├── init
│ │ └── init.sql
│ ├── mysql
│ │ ├── auto.cnf
│ │ ├── base_db
│ │ │ ├── base_table.frm
│ │ │ ├── base_table.ibd
│ │ │ └── db.opt
...省略部分...
│ ├── nginx
│ │ ├── index.html
│ │ └── static
│ │ ├── css
│ │ │ ├── app.d2400a32d2511dba922aebae4b8a11bb.css
│ │ │ └── app.d2400a32d2511dba922aebae4b8a11bb.css.map
│ │ └── js
│ │ ├── app.fc7a4bfccef2ff584b23.js
│ │ ├── app.fc7a4bfccef2ff584b23.js.map
│ │ ├── manifest.3ad1d5771e9b13dbdad2.js
│ │ ├── manifest.3ad1d5771e9b13dbdad2.js.map
│ │ ├── vendor.a99313ee7ba8dc25e131.js
│ │ └── vendor.a99313ee7ba8dc25e131.js.map
│ └── redis
├── docker-compose.yaml
└── log
├── mysql
│ ├── binlog.000001
│ ├── binlog.000002
│ ├── binlog.000003
│ └── binlog.index
├── nginx
│ ├── access.log
│ └── error.log
└── redis
└── redis.log
20 directories, 307 files
8.3 访问
8.3.1 浏览器中访问 Nginx 容器
在浏览器中输入服务器 ip:8000,可看到ip:172.22.0.5 / count:1 / time:2019-11-26 20:46:30
信息。
如果多次访问,可以发现:
- count 数会一直累计
- time 为当前时间
8.3.2 Navicat 连接 MySQL 容器
在 Navicat 中新建 MySQL 连接:
- 主机:服务器 ip
- 端口:3306
- 用户名:root
- 密码:123456
连接成功后,进入到 base_db 库中打开 base_table 表,如果访问超过五次了,这张表里就会有记录的数据了。
8.3.3 rdm 连接 Redis 容器
在 rdm 中新建连接:
- Host:服务器 ip
- Port:6379
- Auth:123456789
连接成功后,进入到 db_2 库中查看key:come_counts
。
8.3.4 注意
到现在,还没有结束。
前面的部署是直接使用我上传的 build 目录,镜像使用我上传到 DockerHub 的镜像,来完成部署;接下来,就得你自己在本地使用开发工具打开 GitHub 上的源码进行。
不过,在后端项目中有个地方,要做更改,就是远程仓库用户名,位置在 docker-compose-service 的 pom.xml 中,原代码片段如下
<!-- 上传镜像 -->
<!-- 远程仓库用户名 -->
<docker.image.prefix>intomylife</docker.image.prefix>
也就仅仅此处需要更改,因为:
- DockerHub 远程库的用户名和密码已经在准备工作中配置到 Maven 的
settings.xml
文件中了 - 远程服务器的地址已经在准备工作中配置到 hosts 文件中了
9. 使用你自己的镜像部署发布
9.1 后端
9.1.1 把多工程项目使用 IntelliJ IDEA 打开
- 把项目源码从 GitHub 中下载到你的本地
- 打开 IntelliJ IDEA
- 点击 File -> Open
- 打开你下载到本地的项目目录
- docker-compose-rapid-deployment -> docker-compose-service(选择打开此目录)
- 打开 service 工程后
- 再次点击 File -> Project Structrue
- 选择 Modules,点击 ‘+’ 符号
- 点击 Import Module
- 还是打开你下载到本地的项目目录
- docker-compose-rapid-deployment -> docker-compose-commons -> pom.xml
- 点击 OK
- 点击 Next,Finish
- 点击 Apply,OK
9.1.2 部署
- 点击
idea
右侧Maven Projects
- 双击
docker-compose-commons
- 双击
Lifecycle
- 双击
install
- 双击
docker-compose-service-core
- 双击
Lifecycle
- 双击
package
- 等待控制台出现如下信息就证明成功把镜像推送到了服务器
Successfully built b9061dc27027
Successfully tagged $YOURNAME/docker-compose-service:1.0
- 到服务器中,查看镜像,发现了刚刚新推送的后端镜像
[root@zwc ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
intomylife/docker-compose-service 1.0 b9061dc27027 9 minutes ago 141MB
9.2 前端
9.2.1 把前端项目使用 Visual Studio Code 打开
- 把项目源码从 GitHub 中下载到你的本地
- 打开 Visual Studio Code
- 点击 文件 -> 打开
- 打开你下载到本地的项目目录
- docker-compose-rapid-deployment -> docker-compose-front(选择打开此目录)
9.2.2 部署
- 点击
vsCode
左侧资源管理器
- 找到
Dockerfile
右键,点击Build Image
- 顶部会弹一个框出来,填写镜像名称和版本信息,如
intomylife/docker-compose-front:1.0
- 回车
- 等待控制台出现如下信息就证明成功把镜像推送到了服务器
Successfully built 5ecd85499f17
Successfully tagged intomylife/docker-compose-front:1.0
- 到服务器中,查看镜像,发现了刚刚新推送的前端镜像
[root@zwc ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
intomylife/docker-compose-front 1.0 5ecd85499f17 15 minutes ago 254MB
9.3 修改 docker-compose.yaml 文件
需要把service_springboot
服务和service_vue
服务的镜像指定成你刚刚推送的镜像名称。
9.4 启动及访问
与上面使用我的镜像部署时启动及访问一致,就不再赘述。
10. 快速部署
10.1 后端中更改部分输出内容
// 返回数量
return "!!!ip:" + InetAddress.getLocalHost().getHostAddress() + " / count:" + String.valueOf(resultCount) + " / time:" + sdf.format(new Date());
加了三个感叹号
10.2 部署后端
- 点击
idea
右侧Maven Projects
- 双击
docker-compose-service-core
- 双击
Lifecycle
- 双击
package
- 等待控制台出现如下信息就证明成功把镜像推送到了服务器
Successfully built 245ee3d4db9c
Successfully tagged intomylife/docker-compose-service:1.0
- 到服务器中,查看镜像,发现了刚刚新推送的镜像,并且还发现了有一个
<none>
的镜像,其实它就是现在正在启动中的 container_springboot 容器的镜像 - 查看正在运行的镜像,发现 container_springboot 容器的镜像名称变成了镜像 ID,而这个镜像 ID 和上面
<none>
的镜像 ID 一样
11. 快速发布
11.1 发布
---
1. 还是在上面启动的目录,使用 docker-compose up -d 命令后台启动,发现只是重启了更改过的容器和更改过的容器的关联容器
---
[root@zwc docker-compose-rapid-deployment]# docker-compose up -d
container_redis is up-to-date
container_mysql is up-to-date
Starting container_vue ... done
Recreating container_springboot ... done
Recreating container_nginx ... done
11.2 访问
在浏览器中输入服务器 ip:8000,可看到ip:172.22.0.5 / count:1 / time:2019-11-26 20:46:30
信息变成了!!!ip:172.22.0.2 / count:8 / time:2019-11-26 21:32:20
,刚刚添加的三个感叹号成功生效。
12. 遗留问题
当服务器TLS
加密后,vsCode 中的 Docker 插件就一直报错证书有问题,不显示服务器上的镜像;如果在 vsCode 的设置中去掉Docker: Host
配置,是可以正常显示本地的镜像列表;或者是服务器不加密,在 vsCode 中的设置添加Docker: Host
配置,去掉Docker: Tls Verify
配置,也是能够正常显示服务器的镜像列表。
但是即使加密后,不显示服务器的镜像列表,还是可以正常推送镜像到服务器中的。
这个问题断断续续用了两周找原因,最终还是没找到解决办法…虽然对本篇博客的部署来说,没有太大影响,但是也是够闹心的。
13. 结语
这篇博客我从准备到写完,大概快一个月了,中间也遇到了各种各样的问题,从中也学到了不少东西。阿,快乐。
希望能够帮助到你
over