SE3353-assignment10

简单总结一下这次作业。使用Docker-Compose封装SpringBoot应用、Nginx、MySQL、MongoDB和Redis。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
任务要求:
请你根据上课内容,针对你在E-BookStore项目中的数据库设计,完成下列任务:

1.请你参照课程样例,构建你的E-BookStore的集群,它应该至少包含 1 个nginx实例(负载均衡) + 1 个Redis实例(存储session) + 2 个Tomcat后端实例。(4分)

2.所使用的框架不限,例如可以不使用nginx而选用其他负载均衡器,或不使用Redis而选用其他缓存工具。

3.参照上课演示的案例,将上述系统实现容器化部署,即负载均衡器(或注册中心和Gateway)、缓存和服务集群都在容器中部署。 (1分)

– 请提交一份Word文档,详细叙述你的实现方式;并提交你的工程代码。

评分标准:

– 能够正确地部署和运行上述系统,在验收时需当面演示。

– 部署方案不满足条件或无法正确运行,则视情况扣分。

​ 整体框架可以参考这个博客,但是真正debug起来前前后后也是花了一两周时间,只能说还是docker用得太少了,以及docker集群中的错误定位在没有经验的情况下还是非常痛苦的,包括思维的“连续性”这件事情,因为每次拾起来这个任务总需要花费一些建立去retrieve之前的记忆,但每次能够挤出来的时间又是碎片时间,导致retrieve记忆的效率很低。相关代码在这个仓库

SpringBoot的Docker化

​ 也就是把一个Spring Boot工程打包成Docker镜像,这件事情网上有各种各样奇奇怪怪的教程让我去安装什么插件,配置什么端口。这些都是不必要的事情。首先明确我们整个过程打包出来的就只有一个单纯的jar包,这个jar包是平台无关的,我们只需要根据这个jar包封装成一个docker镜像并且启动起来实例就可以了。所以其实我们只需要在bookstore_backend-1.0-SNAPSHOT.jar的相同目录下新建一个Dockerfile文件,写入如下代码:

1
2
3
4
5
6
7
8
9
10
11
#基于哪个镜像
FROM java:8

# 拷贝文件到容器,也可以直接写成ADD microservice-discovery-eureka-0.0.1-SNAPSHOT.jar /app.jar
ADD bookstore_backend-1.0-SNAPSHOT.jar /app.jar

# 开放9090端口
EXPOSE 9090

# 配置容器启动后执行的命令
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

MySQL的Docker部署

​ 理论上这是一个非常非常非常常见的应用场景,但是就是在这一步卡了我非常久非常久的时间。

​ 首先是,因为是从容器中启动MySQL,最开始容器中是没有数据库的,所以需要有一个初始化过程。所以先要从我们原先的生产环境中把数据库拿出来,这里就直接使用这篇博客提到的全量备份方法:

1
mysqldump -uroot -p123123 --databases bookstore>./init/init.sql

​ 但是根据教程把在docker-compose中绑定- ./init:/docker-entrypoint-initdb.d后,启动以后还是没有bookstore数据库。根据这篇回答,在command中添加–init-file /docker-entrypoint-initdb.d/init.sql,这才让我真正能够在启动容器的时候初始化我的数据库。

​ 接下来就是需要让我们的SpringBook容器真正连上这个容器中的MySQL,这才算是打通了集群化部署的工作流程。

​ 总体踩过的坑如下:

  1. 在docker-compose的MySQL的environment中,必须设置MYSQL_ROOT_HOST: ‘%’,根据MySQL官网的叙述,如果不设置这一条,MySQL容器只会允许‘root‘@’localhost’,也就是容器内部的查询请求通过,但是我们配置出来的MySQL显然是需要让内网的别的容器访问的。

  2. 在docker-compose的MySQL的environment的MYSQL_ROOT_PASSWORD中,似乎不支持纯数字密码。(至少我设置123456登录不上去,设置为纯字母密码才可以)。

  3. 在docker-compose的MySQL的environment中,可能需要设置MYSQL_DATABASE。这个变量指定了我们在创建镜像时需要创建的数据库,如果我们还设置了MYSQL_USER,会同时授予User访问这个数据库的权限。

  4. 在Spring Boot的application.properties中,如果我们之前设置的是访问路径是‘jdbc:mysql://localhost:3306/bookstore?autoReconnect=true&useSSL=false‘,我们需要把localhost修改为docker-compose中service的名字,否则这样SpringBoot会调用localhost自己容器中的3306端口,但是它自己的容器里是没有MySQL的。

    在Debug过程中,其实看到最多的是如下的报错信息:

    1
    2
    3
    4
    5
    6
    java.sql.SQLNonTransientConnectionException: Could not create connection to database server. Attempted reconnect 3 times. Giving up.
    #此处省略几十行
    at com.bookstore.Application.main(Application.java:123) ~[classes/:na]
    Caused by: com.mysql.cj.exceptions.CJException: Access denied for user 'root'@'localhost' (using password: YES)
    #此处省略几十行
    ... 50 common frames omitted

​ 现在回过头来看其实当时主要还是看到那么多行报错一直在搜第一行的错误原因,而中间这一段才是真正的原因,也就是本机尝试登录root账户失败,那显然就是密码错误。

​ 总体的和MySQL相关的配置文件如下:

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
app_db:
image: mysql:8.0.23

hostname: testt
command:
# MySQL8的密码验证方式默认是 caching_sha2_password,但是很多的连接工具还不支持该方式
# 就需要手动设置下mysql的密码认证方式为以前的 mysql_native_password 方式
--default-authentication-plugin=mysql_native_password
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
--init-file /docker-entrypoint-initdb.d/init.sql #attention here
# docker的重启策略:在容器退出时总是重启容器,但是不考虑在Docker守护进程启动时就已经停止了的容器
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: root_password # root用户的密码
MYSQL_DATABASE: bookstore
MYSQL_ROOT_HOST: '%'

ports:
- 3306:3306
expose:
- 3306
volumes:
- ./db:/var/lib/mysql
- ./conf:/etc/mysql/conf.d
- ./log:/logs
- ./init:/docker-entrypoint-initdb.d

其余部分的容器化

​ 其他的部分(包括MongoDB,Nginx,Redis,Neo4j)的容器部署大差不差,这些一共加起来可能也就花了半小时时间。主要还是配置第一个MySQL的时候踩了太多太多的坑。

​ 最后放一些结果图。

​ 我们可以看到Nginx确实把请求轮询分发给了两台Server。

​ 并且在响应的结果中,我们确实看到了MySQL和MongoDB组装成功响应。

Author

Kami-code

Posted on

2021-12-18

Updated on

2021-12-22

Licensed under

Comments