ZB-050-02springboot三种部署方式

java程序的部署

基于这个项目 https://github.com/slTrust/spring_ecosystemspring程序的部署分支

  • 你也可以跟着我从头到尾写 从master 开分支即可

项目的依赖

  • 依赖 mysql
  • 依赖 redis
  • nginx
    • 监听80端口
    • 负载均衡器,访问我们的三种部署方式
      • maven exec
      • jar
      • docker

三种方式部署

  • maven exec
  • jar 方式
  • docker 方式

项目先跑起来 按照readme.md即可

项目默认用的数据库是 h2 数据库,我们要换成 mysql

application.properties 里修改连接串

1
spring.datasource.url=jdbc:mysql://localhost:3306/match

由于已经按照 maven exec 插件 所以可以命令行启动

1
mvn compile exec:exec

直接报错 Unable to connect to localhost:6379 因为没开 redis

1
2
# docker 启动 redis
docker run -p 6379:6379 --name some-redis -d redis

再次运行 mvn compile exec:exec,报错 mysql 不存在

docker 启动 mysql 并持久化数据目录

1
2
3
4
5
6
7
8
9
# 这样你需要一个 数据库客户端连接之后 创建数据库 xdml
docker run -v "$PWD/data":/var/lib/mysql --name my-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -p 3306:3306 -d mysql
# 客户端 连上它
create database `match`;
# 这里有个坑 match 是 mysql的关键字
# 所以涉及 match 的地方要这样 `match` 本项目包含它的地方有 V1_CreateTables.sql 和 MyBatis 的 MyMapper.xml 里

# 参数指定连接的数据库
docker run -v "$PWD/data":/var/lib/mysql --name my-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -e MYSQL_DATABASE=match -p 3306:3306 -d mysql

pom.xml 里添加 mysql 的 maven依赖 ,修改 flyway的连接信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>

<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>6.0.7</version>
<configuration>
<url>jdbc:mysql://localhost:3306/match</url>
<user>root</user>
<password>my-secret-pw</password>
</configuration>
</plugin>

运行 数据库迁移命令

1
mvn flyway:migrate

继续运行项目mvn compile exec:exec 报错 Driver org.h2.Driver claims to not accept
jdbcUrl

意思是你的 driver 是 h2的 不是 mysql的

修改 连接信息

1
2
3
4
5
6
7
8
9
10
11
12
13
# 搜索 java mysql driver class
com.mysql.cj.jdbc.Driver


# 修改后的 application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/match
spring.datasource.username=root
spring.datasource.password=my-secret-pw
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
mybatis.config-location=classpath:db/mybatis/config.xml
spring.aop.proxy-target-class=true
spring.redis.host=localhost
spring.redis.port=6379

在此运行 mvn compile exec:exec 成功!!!

  • 这样就完成了第一种方式的部署

本机做域名映射

1
2
3
4
5
6
vi /etc/hosts
# 在里面添加

127.0.0.1 abc.com

# 此时你可以通过访问 http://abc.com:8080/rank2 达到我们的效果

nginx 做负载均衡

1
2
3
4
5
6
7
8
9
10
11
12
13
docker run --name my-custom-nginx-container -v /host/path/nginx.conf:/etc/nginx/nginx.conf:ro -d nginx

# 参数解释
# --name 就是docker里你这个应用的名字 可以不要
# -v 非常重要 这里是指定 nginx 配置的目录
# -d 代表使用那个镜像

我们要额外添加一个参数 ,防止 nginx挂了

--restart=always

# 完整命令 指定的是 当前目录的 nginx.conf文件
docker run --restart=always -v `pwd`/nginx.conf:/etc/nginx/nginx.conf:ro -d nginx

不懂 nginx的配置,没关系 googlenginx load balancer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
http {
upstream myapp1 {
server abc.com:8080;
server abc.com:8081;
server abc.com:8082;
}

server {
listen 80;

location / {
proxy_pass http://myapp1;
}
}
}

# 意思是
nginx监听 80端口
并把所有80端口访问转发到 http://myapp1 的应用
myapp1 就是开始定义的服务端口
# 实际上这里是有错的,为了加深记忆故意写错的
# server abc.com:8080; mvn exec方式
# server abc.com:8081; jar方式
# server abc.com:8082; docker方式

启动 nginx 运行 ,注意在你定义的 nginx.conf目录里执行

1
docker run  --restart=always -v `pwd`/nginx.conf:/etc/nginx/nginx.conf:ro -d nginx

通过docker ps -a 查看我们启动的应用,发现 nginx 一直在重启

  • 原因是 --restart=always 导致它只要失败一直重启

docker ngxin的排错

1
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
# 查看 nginx的 容器id 形如 xxx
docker ps

# 看日志
docker logs 容器id

# 日志显示
no "events" section in configuration

# google 搜索这个信息

https://stackoverflow.com/questions/54481423/nginx-startup-prompt-emerg-no-events-section-in-configuration

得到答案 添加 events { }

nginx.conf文件

events { }
http {
upstream myapp1 {
server abc.com:8080;
server abc.com:8081;
server abc.com:8082;
}

server {
listen 80;

location / {
proxy_pass http://myapp1;
}
}
}

重新启动 nginx

1
2
# 注意杀掉之前的 容器 / 还有你当前所在目录
docker run --restart=always -v `pwd`/nginx.conf:/etc/nginx/nginx.conf:ro -d nginx

尝试访问 abc.com

  • 因为 nginx监听 80端口 所以可以省略
  • 但是似乎失败了
    • 那就查看有没有监听我们的 80端口
    • 我们有犯错了,docker nginx的 80端口和 宿主机的80端口是隔离的,我们需要端口映射

运行 docker run --restart=always -vpwd/nginx.conf:/etc/nginx/nginx.conf:ro -p 80:80 -d nginx

访问 abc.com 得到了 502 bad gateway错误

  • 继续看log *1 no live upstreams while connecting to upstream, client: 172.17.0.1
    • 意思是上游的应用 没启动。
    • 继续看 nginx.conf,也感觉没问题
    • 进入 容器的内部看问题 docker exec -it 容器id bash
  • 原因是 容器内部是找不到 abc.com
    • 你肯定会疑问 不是在本机host里改了 域名映射吗?
    • 答案是 你的本机和 docker容器内部完全是八杆子打不着的两个计算机

docker常见问题 解决思路,灵魂三问

  • 当前发生问题在那台计算机里面,在这台计算机里有我需要的资源吗,它能访问到配置文件吗,它能访问这个host吗?
  • 这种问题有两种方式,一个是 把host或者文件 做映射
  • 一个是用 IP

于是你这样改了 nginx.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 实际上还是错的,因为这是 docker 的 127.0.0.1 仍然不是你本机的 ip
events { }
http {
upstream myapp1 {
server 127.0.0.1:8080;
server abc.com:8081;
server abc.com:8082;
}

server {
listen 80;

location / {
proxy_pass http://myapp1;
}
}
}

# 所以应该通过你的局域网ip才对
#查看你的ip
ifconfig
inet 开头的

继续修改 nginx.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
events { }
http {
upstream myapp1 {
server 192.168.31.176:8080;
server 192.168.31.176:8081;
server 192.168.31.176:8082;
}

server {
listen 80;

location / {
proxy_pass http://myapp1;
}
}
}

此时 访问 abc.com 成功

这样我们就完成了nginx的配置,nginx监听80端口把它接收到的请求转发到后端的8080端口

疑问,nginx 的转发什么时候转发到其他一个 端口上

官网已经说过了

1
2
3
4
5
The following load balancing mechanisms (or methods) are supported in nginx:

- round-robin — requests to the application servers are distributed in a round-robin fashion,
- least-connected — next request is assigned to the server with the least number of active connections,
- ip-hash — a hash-function is used to determine what server should be selected for the next request (based on the client’s IP address).

通过 jar方式启动你的应用

1
2
3
4
5
# 打包你的应用
mvn package

# jar方式启动,结果报错了, 因为 8080 端口被占用了(exec方式)
java -jar target/gs-spring-boot-0.1.0.jar

如何给 springboot 更换端口

  • google springboot change port
    • 这个答案说 修改配置文件(add application.properties) 或者 加一个-Dserver.port=8090 参数
    • 注意!!! 此时我们不能通过修改配置文件 因为 exec 的方式依赖那个配置文件

启动时 添加参数指定端口

1
java -Dserver.port=8081 -jar target/gs-spring-boot-0.1.0.jar

通过 docker方式启动spring

  • 根目录新建Dockerfile
1
2
3
4
5
6
7
8
9
10
11
FROM java:openjdk-8u111-alpine

RUN mkdir /app

WORKDIR /app

COPY target/gs-spring-boot-0.1.0.jar /app

EXPOSE 8080

CMD [ "java", "-jar", "gs-spring-boot-0.1.0.jar" ]
  • 生成镜像 项目根目录 docker build . 启动成功后得到一个 id 形如0931ab63f672
  • 启动应用(注意端口映射) docker run -p 8082:8080 0931ab63f672
  • 继续访问abc.com/rank2 访问失败了,redis 6379有问题
    • docker里的 spring访问的localhost是 docker的localhost

如何把数据库或者一些配置信息传递进 docker启动的 springboot程序里呢?

spring通过外部导入配置

spring 加载配置(application.properties)的顺序是

1
2
3
4
5
6
SpringApplication will load properties from application.properties files in the following locations and add them to the Spring Environment:

1. A /config subdir of the current directory
2. The current directory
3. A classpath /config package
4. The classpath root

文件映射

  • 新建 src/main/resources/deploy/application.properties
  • 内容仅仅把 localhost改成你的 ip地址
1
2
3
4
5
6
7
8
spring.datasource.url=jdbc:mysql://192.168.31.176:3306/match
spring.datasource.username=root
spring.datasource.password=my-secret-pw
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
mybatis.config-location=classpath:db/mybatis/config.xml
spring.aop.proxy-target-class=true
spring.redis.host=192.168.31.176
spring.redis.port=6379
  • 进入这个目录
  • docker 添加 -v
1
docker run -v `pwd`/application.properties:/app/config/application.properties -p 8082:8080 0931ab63f672

至此,我们就完成了三种方式部署 springboot程序

项目说明

项目地址

依赖内容

  • docker
    • redis
    • mysql
    • java
    • nginx
  • idea

项目启动

1
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
# 00修改 本地 host
vi /etc/hosts
# 添加
127.0.0.1 abc.com

# 01启动redis
docker run -p 6379:6379 --name some-redis -d redis

# 02启动 mysql
docker run -v "$PWD/data":/var/lib/mysql --name my-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -p 3306:3306 -d mysql

# 03通过数据库客户端连接进入 并创建 match数据库
create database `match`;

# 04初始化数据,项目根目录运行
mvn flyway:migrate

# 05启动 nginx,进入项目 src/main/resources/deploy 目录执行如下命令
# 注意ip地址 要更好为你的电脑的 ip地址
docker run --restart=always -v `pwd`/nginx.conf:/etc/nginx/nginx.conf:ro -p 80:80 -d nginx

# 06exec方式启动 项目根目录运行
mvn compile exec:exec

# 07jar方式启动 项目
# 项目根目录运行 打包命令
mvn compile
# 运行 jar命令 注意指定端口
java -Dserver.port=8081 -jar target/gs-spring-boot-0.1.0.jar

# 08 docker方式启动 项目根目录运行
# 构建镜像,会生成一个 镜像id 你也可以通过参数给镜像命名
docker build .
# 进入 src/main/resources/deploy目录 用外部的 配置文件 覆盖 你打包jar文件的 配置
# 把 application.properties 文件的 ip 修改为你的ip
# 根据刚才生成的镜像id启动应用,注意目录在 根目录的 src/main/resources/deploy里运行
docker run -v `pwd`/application.properties:/app/config/application.properties -p 8082:8080 0931ab63f672

# 09访问 abc.com