原味地址:记一次 SpringBoot + Docker 内部容器通信失败问题 - 林子宸的博客
年底了,最近公司项目颇忙,又加上服务器到期了,没有续费(穷),所以重新购买了一个阿里的服务器,但因为域名是在腾讯云办理的,又需要将域名转到阿里旗下,前前后后的,耽误了不少时间,所以最近一直没有更新博客。(给懒找几个借口)。
在之前服务器上,所有的服务都是原生部署的,考虑到以后会经常更换新的服务器,每次配置都需要花大把的时间,所以本次及以后就通过 docker
容器来部署项目,并通过 docker compose
来管理容器,这样就方便多了。嗯,说干就干。
docker-compose.yml
通过百度 + CV大法,啪,很快啊,就将需要的配置 CV 过来了。
version: "3"
services:
# 给SpringBoot起一个自定义的服务名
myblog:
# 将来 Dockerfile 构建的镜像名及版本号
image: myblog:1.1
# 指定容器名称
container_name: myblog
# 开放端口
ports:
- "9090:9090"
# 配置容器连接的自定义网络,跟mysql、redis 属于同一个网络
networks:
- app_net
# 容器启动时依赖于以下服务
depends_on:
- mysql
- redis
# mysql服务
mysql:
image: mysql:5.7
ports:
- "13306:3306"
volumes:
- /mydocker/mysql/data:/var/lib/mysql
- /mydocker/mysql/conf.d:/etc/mysql/conf.d
environment:
MYSQL_ROOT_PASSWORD: 'root'
networks:
- app_net
# redis 服务
redis:
image: redis:6.0
ports:
- "16379:6379"
volumes:
- /mydocker/redis/data:/data
- /mydocker/redis/redis.conf:/usr/local/etc/redis/redis.conf
networks:
- app_net
command: redis-server /usr/local/etc/redis/redis.conf
# nginx 服务
nginx:
image: nginx:1.22
volumes:
- /mydocker/nginx/nginx.conf:/etc/nginx/nginx.conf
- /mydocker/nginx/default.conf:/etc/nginx/conf.d/default.conf
- /mydocker/nginx/html:/usr/share/nginx/html
- /usr/local/app:/usr/local/app
ports:
- "80:80"
networks:
- app_net
networks:
app_net:
上面将 mysql 的 3306
端口映射到 13306
,redis 的 6379
映射到 16379
的,就是因为端口映射,导致后面找了半天的 BUG…
application.yml
使用 docker-compose 的好处之一就是在项目的配置文件中,连接第三方服务的 ip 不用固定写死了,直接通过 docker 服务名来连接,这样即使以后容器更换了 ip,我们也无需再去改动项目配置了,nice。又是一顿 CV。
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://mysql/blog?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=UTC
username: root
password: root
redis:
host: redis
port: 16379
上面 mysql 和 redis 的 host 地址不再使用固定ip了,这就很香。
部署
接下来的步骤就很简单了,CV大法编写 Dockerfile
,打 jar
包,上传服务器,build docker image,开放端口,最后通过 docker-compose up 一键启动,看日志:
nice!!启动成功,测一下 mysql 和 redis 的连接情况,通过 navicat 和 RedisDesktopManager 分别连接一下,发现世间最美的单词 successful
,nice,激动的心,颤抖的手,眼看即将成功了,最后一步,访问项目:
嗯?报错了,连不上端口 16379 的 redis 服务,小问题,不就是连接不上嘛,查配置呗。
排查
查 docker-compose.yml ,是不是配置错误了,容器卷挂错了,检查一通,没问题。
查 防火墙,16379端口也已开放,已重启,没问题。
查 redis.conf ,是不是配置有问题,但是 RedisDesktopManager 都连接成功了,没问题。
查 application.yml,是不是写的有问题,发现端口啥的用的也是映射后的端口,貌似也没问题。
查 docker network inspect,是不是redis没有连接到自定义网络,也没有问题。
什么情况,而且经过测试,项目连 mysql 可以,但唯独 redis 连接不上,嘿!奇了怪了。查百度,谷歌,也没有搜索相关的情况。
不要灰心,百度不到,问群友,毕竟加了那么多技术扯皮群,群里个个都是人才,说话又好听,超爱那里的。
把相关问题描述,问题截图往群里一扔,虽然是大半夜,但还是有群友给出了回复:
因为项目里用的是 jedis 的连接池,所以我又把 jedis 改回来原来的 lettuce,重新打包构建启动,看日志,嘿,不管用:
然后群友又回复了,会不会java启的时候,redis还没起来,但是 docker-compose.yml 中已经配置了 depends_on
,所以这个情况应该也不存在。还是未能解决。
询问未果,最终还是在网上找到了一篇帖子,原来 在docker网络中运行所有内容 (redis、replica和spring) 时,应该使用端口6379而不是映射端口
隧将项目的 application.yml
中 redis 的端口,从 16379
改回原 6379
,再打包构建测试,nice,终于可以了。
总结
docker 内部容器通信时,应该使用默认端口。如果外部服务通信,需要使用映射的端口。
以上结论来自个人测试,如有问题,欢迎指出。