当前位置: 首页 > news >正文

基于Rocky Linux 9的容器化部署方案,使用Alpine镜像实现轻量化

一、准备工作

# 安装Docker
sudo dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf install -y docker-ce docker-ce-cli containerd.io
sudo systemctl enable --now docker
# 创建项目目录结构
mkdir -p ~/container-project/{haproxy,web-app,mysql,redis,elk,zabbix,rsync}
cd ~/container-project
​
# 将软件包放到对应目录
cp ROOT.war web-app/
cp Discuz_X3.5_SC_UTF8_20250205.zip web-app/
cp apache-tomcat-8.5.40.tar.gz web-app/
# 创建Docker网络
docker network create --subnet=10.15.0.0/24 app-net

二、部署应用容器

# 构建Web应用镜像
# 创建Dockerfile
cat > web-app/Dockerfile <<'EOF'
FROM alpine:3.18
# 安装基础组件
RUN apk add --no-cache nginx php81 php81-fpm php81-mysqli php81-gd openjdk11-jre tzdata unzip

# 配置时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# 部署Discuz
RUN mkdir -p /var/www/discuz
COPY Discuz_X3.5_SC_UTF8_20250205.zip .
RUN unzip Discuz_X3.5_SC_UTF8_20250205.zip -d /var/www/discuz && \chown -R nginx:nginx /var/www/discuz

#部署Tomcat应用
COPY apache-tomcat-8.5.40.tar.gz .
RUN tar -xzf apache-tomcat-8.5.40.tar.gz -C /opt && \ln -s /opt/apache-tomcat-8.5.40 /opt/tomcat && \rm /opt/tomcat/webapps/ROOT -rf
COPY ROOT.war /opt/tomcat/webapps/

# 配置Nginx
COPY nginx.conf /etc/nginx/http.d/default.conf

# 启动脚本
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
​
ENTRYPOINT ["/entrypoint.sh"]
EOF
​#创建Nginx配置
cat > web-app/nginx.conf <<'EOF'
server {listen 80;server_name _;root /var/www/discuz/upload;index index.php index.html;
​location / {try_files $uri $uri/ /index.php?$args;}
​location ~ \.php$ {include fastcgi_params;fastcgi_pass 127.0.0.1:9000;fastcgi_index index.php;fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;}
​# Tomcat反向代理location /shop {proxy_pass http://localhost:8080/shop;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;}
}
EOF
​# 创建启动脚本
cat > web-app/entrypoint.sh <<'EOF'
#!/bin/sh
​
# 启动PHP-FPM
php-fpm81 &
​
# 启动Tomcat
/opt/tomcat/bin/catalina.sh start
​
# 启动Nginx
nginx -g 'daemon off;'
EOF
​
# 构建镜像
docker build -t web-app:alpine web-app/
​# 启动两个实例
docker run -d --name web-01 --hostname web-01 \--network app-net --ip 10.15.0.10 \-v discuz-data:/var/www/discuz/upload/data \web-app:alpine
​
docker run -d --name web-02 --hostname web-02 \--network app-net --ip 10.15.0.11 \-v discuz-data:/var/www/discuz/upload/data \web-app:alpine

三、部署负载均衡器

# 创建HAProxy配置
cat > haproxy/haproxy.cfg <<'EOF'
globaldaemonmaxconn 4000
​
defaultsmode httptimeout connect 5000mstimeout client 50000mstimeout server 50000ms
​
frontend http-inbind *:80acl host_discuz hdr(host) -i discuz.example.comacl host_shop hdr(host) -i shop.example.comuse_backend discuz if host_discuzuse_backend shop if host_shop
​
backend discuzbalance roundrobinserver web-01 web-01:80 checkserver web-02 web-02:80 check
​
backend shopbalance roundrobinserver web-01 web-01:8080 checkserver web-02 web-02:8080 check
EOF
​# 启动两个HAProxy实例
docker run -d --name haproxy-01 --hostname haproxy-01 \--network app-net --ip 10.15.0.20 \-p 80:80 \-v $PWD/haproxy:/usr/local/etc/haproxy:ro \haproxy:alpine
​
docker run -d --name haproxy-02 --hostname haproxy-02 \--network app-net --ip 10.15.0.21 \-p 8080:80 \-v $PWD/haproxy:/usr/local/etc/haproxy:ro \haproxy:alpine

四、部署数据库

# 创建MySQL主库
docker run -d --name mysql-01 --hostname mysql-01 \--network app-net --ip 10.15.0.30 \-e MYSQL_ROOT_PASSWORD=your_root_password \-e MYSQL_DATABASE=discuz \-e MYSQL_USER=discuz \-e MYSQL_PASSWORD=discuz_password \-v mysql-data:/var/lib/mysql \mysql:8.0 \--server-id=1 \--log-bin=mysql-bin \--binlog-format=row

# 创建MySQL从库
docker run -d --name mysql-02 --hostname mysql-02 \--network app-net --ip 10.15.0.31 \-e MYSQL_ROOT_PASSWORD=your_root_password \-v mysql-data-slave:/var/lib/mysql \mysql:8.0 \--server-id=2

# 配置主从复制
docker exec mysql-01 mysql -uroot -pyour_root_password -e \
"CREATE USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY 'repl_password';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';"
​
docker exec mysql-02 mysql -uroot -pyour_root_password -e \
"CHANGE MASTER TOMASTER_HOST='mysql-01',MASTER_USER='repl',MASTER_PASSWORD='repl_password',MASTER_AUTO_POSITION=1;
START SLAVE;"
##注意!如果出现错误
MySQL 主从复制错误解决方案:GTID_MODE=OFF
​
错误原因分析
ERROR 1777 (HY000): 
CHANGE REPLICATION SOURCE TO SOURCE_AUTO_POSITION = 1 cannot be executed 
because @@GLOBAL.GTID_MODE = OFF.
错误表明:
1. GTID模式未启用:MASTER_AUTO_POSITION=1 需要启用GTID(全局事务标识符)
2. 配置冲突:尝试使用基于GTID的复制,但服务器未开启GTID功能
​
完整解决方案(分步操作)
​
步骤1:在主库(mysql-01)启用GTID
​
docker exec mysql-01 mysql -uroot -pyour_root_password -e "
SET GLOBAL ENFORCE_GTID_CONSISTENCY = ON;
SET GLOBAL GTID_MODE = OFF_PERMISSIVE;
SET GLOBAL GTID_MODE = ON_PERMISSIVE;
SET GLOBAL GTID_MODE = ON;
"
​
​
步骤2:在从库(mysql-02)启用GTID
​
docker exec mysql-02 mysql -uroot -pyour_root_password -e "
STOP SLAVE;
SET GLOBAL ENFORCE_GTID_CONSISTENCY = ON;
SET GLOBAL GTID_MODE = OFF_PERMISSIVE;
SET GLOBAL GTID_MODE = ON_PERMISSIVE;
SET GLOBAL GTID_MODE = ON;
"
​
​
步骤3:重新配置主从复制
​
docker exec mysql-02 mysql -uroot -pyour_root_password -e "
STOP SLAVE;
RESET MASTER;  # 清除现有GTID信息
CHANGE MASTER TOMASTER_HOST='mysql-01',MASTER_USER='repl',MASTER_PASSWORD='repl_password',MASTER_AUTO_POSITION=1;
START SLAVE;
"
​
​
步骤4:验证配置
​
# 在主库查看GTID状态
docker exec mysql-01 mysql -uroot -pyour_root_password -e "SHOW GLOBAL VARIABLES LIKE 'gtid_mode';"
​
# 在从库检查复制状态
docker exec mysql-02 mysql -uroot -pyour_root_password -e "SHOW SLAVE STATUS\G" | grep -E 'Slave_IO_Running|Slave_SQL_Running|Gtid'
​
​
替代方案(不使用GTID)
​
如果不想启用GTID,使用传统复制方式:
# 1. 在主库获取binlog位置
MASTER_STATUS=$(docker exec mysql-01 mysql -uroot -pyour_root_password -e "SHOW MASTER STATUS" | awk 'NR==2')
LOG_FILE=$(echo $MASTER_STATUS | awk '{print $1}')
LOG_POS=$(echo $MASTER_STATUS | awk '{print $2}')
​
# 2. 在从库配置传统复制
docker exec mysql-02 mysql -uroot -pyour_root_password -e "
STOP SLAVE;
CHANGE MASTER TOMASTER_HOST='mysql-01',MASTER_USER='repl',MASTER_PASSWORD='repl_password',MASTER_LOG_FILE='$LOG_FILE',MASTER_LOG_POS=$LOG_POS;
START SLAVE;
"
​
​
永久配置GTID模式
​
在my.cnf配置文件中添加(所有节点):
[mysqld]
# GTID 配置
gtid_mode = ON
enforce_gtid_consistency = ON
​
# 复制配置
server-id = 1        # 主库设置为1,从库设置为2
log-bin = mysql-bin
binlog_format = ROW
​
​
重启MySQL服务使配置永久生效:
docker restart mysql-01 mysql-02
​
​
验证成功的输出示例
​
# 主库GTID状态
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| gtid_mode     | ON    |
+---------------+-------+
​
# 从库复制状态Slave_IO_Running: YesSlave_SQL_Running: YesRetrieved_Gtid_Set: 3a9b7d7a-1a60-11ee-8e2a-0242ac110002:1-5Executed_Gtid_Set: 3a9b7d7a-1a60-11ee-8e2a-0242ac110002:1-5Auto_Position: 1
​
​
故障排查表
​
错误现象 可能原因 解决方案
​
Slave_IO_Running: No 网络不通/权限不足 检查防火墙和复制用户权限
​
Slave_SQL_Running: No SQL执行错误 STOP SLAVE; SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; START SLAVE;
​
GTID不一致 数据不同步 RESET MASTER; 后重建复制
​
无法启用GTID 存在不支持GTID的事务 检查enforce_gtid_consistency错误日志
​
关键注意事项
​
1. 启用GTID前:确保所有事务都支持GTID(避免使用CREATE TABLE...SELECT)
2. 混合环境:如果从旧版本升级,需先设置OFF_PERMISSIVE过渡
3. 数据一致性:启用GTID前建议全量备份
4. Docker持久化:将配置写入/etc/mysql/conf.d/gtid.cnf确保容器重启后生效
​
完成上述步骤后,您的MySQL主从复制应该能正常运行,且不再出现GTID_MODE=OFF的错误。

五、部署Redis高可用

# Redis主节点
docker run -d --name redis-01 --hostname redis-01 \--network app-net --ip 10.15.0.40 \-v redis-data:/data \redis:alpine --appendonly yes
​
# Redis从节点
docker run -d --name redis-02 --hostname redis-02 \--network app-net --ip 10.15.0.41 \redis:alpine --replicaof redis-01 6379
​
# Redis Sentinel
docker run -d --name redis-sentinel --hostname redis-sentinel \--network app-net --ip 10.15.0.42 \-v $PWD/redis/sentinel.conf:/sentinel.conf \redis:alpine \redis-sentinel /sentinel.conf
​
# 创建哨兵配置
cat > redis/sentinel.conf <<'EOF'
port 26379
sentinel monitor mymaster redis-01 6379 1
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 10000
sentinel auth-pass mymaster your_redis_password
EOF

六、部署ELK日志系统

# Elasticsearch
docker run -d --name elasticsearch --hostname elasticsearch \--network app-net --ip 10.15.0.50 \-e "discovery.type=single-node" \-v es-data:/usr/share/elasticsearch/data \elasticsearch:7.1.1
​
# Kibana
docker run -d --name kibana --hostname kibana \--network app-net --ip 10.15.0.51 \-e ELASTICSEARCH_HOSTS=http://elasticsearch:9200 \kibana:7.1.1
​
# Filebeat配置
mkdir -p elk/filebeat
cat > elk/filebeat/filebeat.yml <<'EOF'
filebeat.inputs:
- type: logpaths:- /logs/*.log
output.elasticsearch:hosts: ["elasticsearch:9200"]
EOF
​
# Filebeat服务
docker run -d --name filebeat --hostname filebeat \--network app-net --ip 10.15.0.52 \-v /var/log/containers:/logs:ro \-v $PWD/elk/filebeat:/usr/share/filebeat:ro \docker.elastic.co/beats/filebeat:7.17.21

七、其他服务

# Zabbix监控
docker run -d --name zabbix --hostname zabbix \--network app-net --ip 10.15.0.60 \-e DB_SERVER_HOST=mysql-01 \-e MYSQL_USER=zabbix \-e MYSQL_PASSWORD=zabbix_password \zabbix/zabbix-server-mysql:alpine
​
# Rsync备份
mkdir -p rsync/conf
cat > rsync/conf/rsyncd.conf <<'EOF'
[backup]
path = /backup
comment = Backup directory
read only = false
EOF
​
docker run -d --name rsync --hostname rsync \--network app-net --ip 10.15.0.70 \-v $PWD/rsync/conf:/etc/rsyncd:ro \-v backup-data:/backup \alpine:3.18 \sh -c "echo 'sync_user:$(openssl passwd -1 password)' > /etc/rsyncd.secrets && rsync --daemon"

八、最终验证

# 检查容器状态
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Networks}}"
​# 测试Discuz访问
curl -H "Host: discuz.example.com" http://localhost
​# 测试电商应用
curl -H "Host: shop.example.com" http://localhost:8080/shop
​# 检查MySQL主从
docker exec mysql-02 mysql -uroot -pyour_root_password -e "SHOW SLAVE STATUS\G"
# 检查Redis主从
docker exec redis-02 redis-cli -a your_redis_password info replication

部署说明

  1. 轻量化设计

    • 所有容器基于Alpine镜像,平均大小<50MB

    • 使用共享数据卷(discuz-data、mysql-data等)实现持久化存储

  2. 网络配置

    • 使用自定义网络app-net(10.15.0.0/24)

    • 容器间通过主机名直接通信

  3. 关键端口

    • 80:HAProxy前端入口

    • 8080:电商应用备用端口

    • 5601:Kibana访问端口(需额外映射)

  4. 数据备份

    • Rsync服务提供/backup目录同步

    • 添加定时备份脚本示例:

      docker exec rsync sh -c "rsync -av /backup/ sync_user@backup-server::backup_module"
  5. 扩展建议

    # 垂直扩展(增加资源限制)
    
    docker update --cpus 2 --memory 1g web-01
    ​
    # 水平扩展(增加Web实例)
    
    docker run -d --name web-03 --hostname web-03 \--network app-net --ip 10.15.0.12 \web-app:alpine

重要提示:实际部署需替换所有密码占位符(your_root_password、discuz_password等)并配置正确的域名解析。生产环境建议添加TLS加密和防火墙规则。

针对 Docker 创建指定子网后导致 Rocky Linux 9 远程连接断开的问题,以下是详细的解决方案和原因分析:


问题原因分析

  1. 网段冲突 --subnet=192.168.100.0/24 可能与主机的物理网络接口(如 ens192)或已有 VPN/DHCP 分配的地址冲突,触发路由混乱。

  2. Docker 网络接管默认路由 创建新网络时,Docker 可能修改了 default 网关路由,导致流量被错误导向容器的虚拟网络。

  3. NetworkManager 干扰 Rocky Linux 9 默认启用的 NetworkManager 可能将 Docker 接口误识别为新物理设备,重写路由规则。


解决方案步骤 (需物理/控制台登录)

步骤 1:通过控制台访问 Rocky Linux
  • 物理服务器:直接连接显示器和键盘。

    • 使用虚拟化管理控制台

    • 例如在 KVM 上:virt-manager → 选择虚拟机 → 打开控制台

  • 虚拟机 (KVM/VirtualBox/VMware)

步骤 2:检查网络状态
ip route show default    # 查看默认路由是否指向异常网卡(如 docker0 或 br-xxx)
nmcli device status      # 检查 NetworkManager 是否托管了虚拟接口
步骤 3:恢复网络连接
# 1. 临时删除冲突的 Docker 网络接口
sudo ip link delete docker0 2>/dev/null   # 删除默认网桥
sudo ip link delete br-$(docker network inspect linux_net -f '{{.Id}}' | cut -c1-12) 2>/dev/null
​# 2. 重置路由表 (若默认路由丢失)
sudo ip route add default via 192.168.100.1 dev ens192  # 替换为你的网关和网卡名
# 3. 重启网络服务(保险方案)
sudo nmcli networking off && sudo nmcli networking on
步骤 4:删除问题 Docker 网络
sudo docker network rm linux_net

避免问题的正确做法

  1. 使用非冲突子网 选择与主机局域网不同的私有地址段:

    docker network create --subnet=10.15.20.0/24 linux_net  # 改用保留地址
  2. 禁止 Docker 修改 iptables (谨慎使用) 仅适用于受控环境,在 /etc/docker/daemon.json 中添加:

    { "iptables": false }

    重启 Docker:sudo systemctl restart docker

  3. 为容器固定 IP (避免随机冲突) 启动容器时显式指定 IP:

    docker run --network linux_net --ip 10.15.20.100 nginx

关键预防措施

建议方案操作命令/配置适用场景
隔离子网--subnet=172.28.0.0/16所有新网络创建
固定主机路由sudo nmcli connection modify ens192 +ipv4.routes "192.168.100.0/24 172.20.0.1"NetworkManager 环境
限制 Docker 权限dockerd --iptables=false安全要求高的生产环境

📌 注意:操作完成后通过 ip aip route 确认主网卡(如 ens192)的 IP 和网关恢复正常,再尝试远程连接。


快速诊断命令参考

# 查看当前路由优先级
ip rule show
​
# 检查 NAT 规则是否劫持流量
sudo iptables -t nat -L POSTROUTING -nv
​
# 定位被 NetworkManager 托管的异常接口
nmcli dev | grep -e veth -e br-

执行上述步骤后,Rocky Linux 9 的网络应恢复正常。远程连接中断本质是网络堆栈配置被 Docker 意外覆盖,修复核心在于清除冲突接口并锁定主机路由表规则。

http://www.dtcms.com/a/328640.html

相关文章:

  • 企业高性能web服务器(3)
  • Linux学习-应用软件编程(文件IO)
  • 【科研绘图系列】R语言绘制特定区域颜色标记散点图
  • Pytest项目_day13(usefixture方法、params、ids)
  • 【不动依赖】Kali Linux 2025.2 中安装mongosh
  • 【数据结构】二叉树详细解析
  • 安路Anlogic FPGA下载器的驱动安装与测试教程
  • C++联合体的定义
  • 数据结构 二叉树(2)堆
  • 带宽受限信道下的数据传输速率计算:有噪声与无噪声场景
  • C++方向知识汇总(四)
  • PyCATIA高级建模技术:等距平面、点云重命名与棱柱体创建的工业级实现
  • 基于Java与Vue搭建的供应商询报价管理系统,实现询价、报价、比价全流程管理,功能完备,提供完整可运行源码
  • Python训练营打卡Day30-文件的规范拆分和写法
  • 树与二叉树
  • NY198NY203美光固态闪存NY215NY216
  • 串口通信学习
  • Xshell远程连接Ubuntu 24.04.2 LTS虚拟机
  • 模型 霍特林法则
  • 自动驾驶 HIL 测试:构建 “以假乱真” 的实时数据注入系统
  • 【JavaEE】多线程之线程安全(上)
  • 学习嵌入式的第十八天——Linux——文件编程
  • nexus-集成prometheus监控指标
  • 力扣面试150题--爬楼梯 打家劫舍 零钱兑换 最长递增子序列
  • DDD之工程结构(7)
  • 数据库规范化:消除冗余与异常的核心法则
  • 用 Spring 思维快速上手 DDD——以 Kratos 为例的分层解读
  • 当赞美来敲门:优雅接纳的艺术
  • 在线免VIP的动漫网站
  • 【沧海拾昧】使用LibUsbDotNet进行Windows/Ubuntu跨平台串口管理