在五台虚拟机中,将手工搭建的 Keepalived+Nginx+Tomcat+MySQL 高可用架构,用 Docker Compose 实现标准化部署。
前言
之前我花了近一个月的时间,手工搭建了一套基于 Keepalived、Nginx、Tomcat、MySQL 的高可用集群,并成功将各组件容器化。但每次启动仍需要手动执行 docker run 命令,环境迁移时也容易遗漏参数。最近我将这套架构统一用 Docker Compose 管理,实现了"一键启动",这里记录下配置过程,希望对你有帮助。
一、项目架构回顾
整套集群运行在五台 Redhat 虚拟机上,各主机角色及 IP 如下:
主机 IP 地址 服务组件 MySQL 192.168.211.136 MySQL 5.7 Tomcat1 192.168.211.128 Tomcat 11 + JSP 应用 Tomcat2 192.168.211.133 Tomcat 11 + JSP 应用 Master Nginx 192.168.211.134 Nginx + Keepalived (主) Backup Nginx 192.168.211.135 Nginx + Keepalived (备)
· VIP:192.168.211.100,由 Keepalived 管理,用于对外统一入口。 · Nginx:作为反向代理,将请求轮询转发给两台 Tomcat。 · Tomcat:提供 JSP 页面,通过 JDBC 连接 MySQL 展示数据。 · MySQL:存储服务器状态数据。
容器化后,Keepalived 继续运行在宿主机,其他所有服务都跑在 Docker 容器中,且用 Docker Compose 统一管理。
二、Docker Compose 文件编写
1、MySQL 主机(192.168.211.136)
创建目录 /opt/mysql,放入 docker-compose.yml:
services:
mysql:
image: mysql:5.7
container_name: mysql
restart: always
network_mode: host
environment:
MYSQL_ROOT_PASSWORD: AppPass123!
volumes:
- /var/lib/mysql:/var/lib/mysql
- /etc/localtime:/etc/localtime:ro
说明:
· network_mode: host:让容器直接使用宿主机网络,这样其他主机可以通过 192.168.211.136:3306 访问 MySQL。 · 数据卷挂载:将宿主机原有的数据库目录(/var/lib/mysql)挂载进去,保证数据持久化。 · 时区挂载:确保容器时间与宿主机一致。
启动命令:
cd /opt/mysql
docker-compose up -d
2、Tomcat 主机
分别在 Tomcat1 和 Tomcat2 上创建 /opt/tomcat1 和 /opt/tomcat2 目录,各放一个 docker-compose.yml。
Tomcat1 的 docker-compose.yml:
services:
tomcat1:
image: my-tomcat:1.7 # 你的 Tomcat 镜像
container_name: tomcat1
restart: always
ports:
- "8081:8080"
volumes:
- /etc/localtime:/etc/localtime:ro
Tomcat2 的 docker-compose.yml 只需将容器名改为 tomcat2,其他相同:
services:
tomcat2:
image: my-tomcat:1.7
container_name: tomcat2
restart: always
ports:
- "8081:8080"
volumes:
- /etc/localtime:/etc/localtime:ro
说明:
· 映射 8081:8080:避免与宿主机其他服务冲突,同时方便 Nginx 配置。 · 镜像 my-tomcat:1.7 是我提前构建好的,包含 JSP 应用和 JDBC 驱动。
启动:
cd /opt/tomcat1
docker-compose up -d
# 在 Tomcat2 上同样操作
3、Nginx 主机(Master 和 Backup)
Nginx 需要反向代理到两台 Tomcat,因此我们构建了专用镜像 my-nginx:1.1,其中已包含自定义配置文件 tomcat.conf(内容如下):
upstream web {
server 192.168.211.128:8081;
server 192.168.211.133:8081;
}
server {
listen 80;
server_name 192.168.211.100;
location / {
proxy_pass http://web;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
在 Master 和 Backup 主机上,分别创建 /opt/nginx-master 和 /opt/nginx-backup 目录,放入相同的 docker-compose.yml:
services:
nginx:
image: my-nginx:1.1
container_name: nginx
restart: always
network_mode: host
volumes:
- /etc/localtime:/etc/localtime:ro
说明:
· network_mode: host:让 Nginx 容器直接监听宿主机的 80 端口,便于与 Keepalived 配合。 · 启动前务必停止宿主机上的 nginx 服务,避免端口冲突:
systemctl stop nginx
systemctl disable nginx
systemctl mask nginx
pkill -9 nginx
启动:
cd /opt/nginx-master
docker-compose up -d
# Backup 主机同样操作
三、验证与测试
1、检查容器状态
在各主机上执行 docker-compose ps,所有容器应显示 Up 状态。
2、测试 MySQL 连接
在任意 Tomcat 主机上:
curl http://192.168.211.128:8081/testdb.jsp
应显示数据库中的表格数据。
3、测试反向代理
直接访问 Master IP:
curl http://192.168.211.134
应返回 Tomcat 页面。多次刷新,观察轮询到两台 Tomcat。
4、测试 VIP 高可用
访问 VIP:
curl http://192.168.211.100
同样能正常返回。停止 Master 上的 Nginx 容器(docker stop nginx),等待几秒,VIP 应漂移到 Backup,再次访问 VIP 依然正常。
四、踩坑记录
· MySQL 数据目录权限:容器内 MySQL 用户 UID=999,宿主机数据目录需 chown 999:999 /var/lib/mysql,否则容器无法写入。 · Tomcat 驱动缺失:记得在 Dockerfile 中添加 MySQL 驱动 JAR,否则 testdb.jsp 会报 ClassNotFoundException。 · Nginx 配置不生效:检查容器内 /etc/nginx/conf.d/ 是否存在 tomcat.conf,若没有则重新构建镜像或挂载配置文件。 · 端口冲突:启动 Nginx 容器前,务必彻底停止宿主机 nginx 服务,并用 ss -tlnp 确认 80 端口未被占用。 · YAML 缩进:Docker Compose 对缩进敏感,务必使用空格,不要用 Tab。
五、总结
通过 Docker Compose,我们将原本分散在五台虚拟机上的手工部署,变成了每个主机上一个简单明了的 YAML 文件。现在,无论是重启服务器,还是在新环境中重建,只需执行 docker-compose up -d 即可一键启动所有容器(Keepalived 仍需手动启动,但可用 systemd 管理)。
这种标准化方式不仅减少了人为失误,还让整个架构变得可移植、可复现。接下来,我会继续引入 Prometheus 监控和 Grafana 可视化,并尝试用 Kubernetes 进行更高级的编排。