ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

CentOS7+VMware+Docker+Jenkins全链路部署Vue与SpringBoot

CentOS7+VMware+Docker+Jenkins全链路部署Vue与SpringBoot 1. 为什么这条路径至今“全网唯一”不是没人试过而是没人敢写全你搜“Centos7 Docker Jenkins 部署”出来的结果大概率是四段割裂的教程第一篇教VMware装系统第二篇讲Docker基础命令第三篇配Jenkins流水线第四篇分别跑Vue和Springboot——每篇都对合起来却跑不通。我去年带三个新人搭测试环境卡在Jenkins拉取Git仓库后构建失败整整三天最后发现是Centos7默认SELinux策略拦截了Docker socket挂载而所有教程里那句“关闭SELinux”被藏在“环境准备”的第17行小字里连加粗都没有。这不是技术问题是信息断层。真正让这条路径成为“全网唯一”的是它踩准了生产环境部署的四个硬约束操作系统层必须稳定Centos7、虚拟化入口必须可复现VMware、容器编排必须零侵入Docker、CI/CD链路必须端到端闭环Jenkins驱动VueSpringboot。市面上90%的教程只解决单点比如用Docker Compose跑MysqlRedis但没告诉你Jenkins怎么把Vue打包产物自动注入Nginx容器或者教Springboot打成jar包丢进Docker却漏掉Vue静态资源跨域时Nginx反向代理的header透传配置。更隐蔽的坑在于版本耦合Centos7.9内核3.10.0Docker 20.10.24是最后一个官方支持该内核的版本而Jenkins 2.414要求Java 11但Springboot 3.x又强制Java 17——这些版本锁链不亲手跑三遍根本意识不到。所以这篇不是“手把手教你”而是还原一个真实运维工程师从裸机到上线的完整决策链。我会明确告诉你在VMware里给Centos7分配2核4G内存时为什么Swap分区必须设为2G不是1G也不是4GDocker安装后执行systemctl enable docker前为什么要先modprobe overlay否则后续所有容器启动都会报“no such device”Jenkins配置Vue项目时npm run build生成的dist目录必须通过COPY --chownnginx:nginx指令注入容器而不是简单ADD否则Nginx进程无权读取文件Springboot的application-prod.yml里数据库URL写成jdbc:mysql://mysql:3306/db?useSSLfalseserverTimezoneAsia/Shanghai其中mysql这个host名是Docker网络里服务名不是IP——这点99%的教程用127.0.0.1糊弄过去导致容器间根本连不通。现在开始我们按真实工作流推进从VMware里点亮第一台Centos7虚拟机开始到Vue页面弹出“Hello Springboot”结束。中间所有命令、配置、截图级细节全部来自我线上环境的实操日志。2. VMware里的Centos7不是装完就完事而是要为Docker埋下伏笔2.1 网络模式选桥接还是NAT一个决定后续所有端口映射的生死VMware Workstation 16.2.3新建虚拟机时网络适配器默认是NAT模式。这看起来省事——虚拟机自动获取192.168.199.x网段IP宿主机能ping通。但当你在Centos7里装好Docker再用docker run -p 8080:8080暴露Springboot端口时会发现宿主机浏览器打不开http://192.168.199.100:8080。原因很简单NAT模式下VMware只做了源地址转换SNAT没做目的地址转换DNAT。宿主机发往虚拟机IP的请求VMware根本不知道该转发给哪个容器。必须切到桥接模式Bridged。操作路径虚拟机设置 → 网络适配器 → 桥接到物理网卡。这里有个致命细节桥接后Centos7获取的IP是和宿主机同网段的比如宿主机是192.168.1.100虚拟机就是192.168.1.101但如果你的路由器DHCP池只分配到192.168.1.100虚拟机可能拿不到IP。此时要手动配置静态IP# 编辑网卡配置文件Centos7默认网卡名是ens33用ip addr确认 vi /etc/sysconfig/network-scripts/ifcfg-ens33关键参数修改BOOTPROTOstatic ONBOOTyes IPADDR192.168.1.101 NETMASK255.255.255.0 GATEWAY192.168.1.1 DNS1114.114.114.114提示GATEWAY必须填路由器IP不能填宿主机IP。我曾因填错导致虚拟机可以上网但无法访问宿主机共享文件夹折腾两小时才发现是网关指向错误。重启网络服务systemctl restart network验证ping -c 3 www.baidu.com通且ping -c 3 192.168.1.100宿主机IP也通才算成功。这步做完后续Docker容器的-p端口映射才能被宿主机直接访问。2.2 Centos7最小化安装后的五项必改配置Centos7选择“Minimal Install”看似干净实则埋了五个雷第一雷firewalld默认开启且放行端口逻辑反直觉Docker启动容器时会自动修改iptables规则但firewalld的zone机制会覆盖这些规则。现象docker run -p 3306:3306 mysql:8.0后宿主机连不上3306。解决方案不是关firewalld生产环境严禁而是把Docker网桥加入trusted zone# 查看Docker网桥IP段通常是172.17.0.0/16 ip addr show docker0 # 将该网段加入trusted zone firewall-cmd --permanent --zonetrusted --add-source172.17.0.0/16 firewall-cmd --reload第二雷swap分区大小影响Docker内存限制Centos7安装向导默认创建1G swap但Docker容器内存限制如-m 2g依赖cgroup v1的memory subsystem而swap过小会导致容器OOM时直接被kill而非优雅降级。实测Springboot应用堆内存设为1Gswap必须≥2G才能避免频繁OOM。扩容命令# 查看当前swap swapon --show # 关闭现有swap swapoff /swapfile # 创建2G新swap文件比原1G多1G dd if/dev/zero of/swapfile bs1G count2 # 设置权限并启用 chmod 600 /swapfile mkswap /swapfile swapon /swapfile # 写入fstab永久生效 echo /swapfile none swap sw 0 0 /etc/fstab第三雷SELinux策略阻止Docker挂载宿主机目录docker run -v /home/jenkins:/var/jenkins_home jenkins/jenkins:lts会报错“Permission denied”。这是因为Centos7默认SELinux策略禁止容器进程写入宿主机目录。解决方案不是setenforce 0禁用SELinux而是打标签# 给Jenkins目录打selinux标签 semanage fcontext -a -t svirt_sandbox_file_t /home/jenkins(/.*)? restorecon -Rv /home/jenkins第四雷yum源慢得像拨号上网Centos7默认baseurl指向国外镜像。替换为阿里云源cd /etc/yum.repos.d/ sed -i s/mirrorlist/#mirrorlist/g CentOS-Base.repo sed -i s|#baseurlhttp://mirror.centos.org|baseurlhttps://mirrors.aliyun.com|g CentOS-Base.repo yum clean all yum makecache第五雷时间不同步引发Jenkins证书错误VMware虚拟机时间漂移严重Jenkins用HTTPS连接GitLab时会因证书时间戳校验失败报错。必须启用chrony时间同步yum install chrony -y systemctl enable chronyd systemctl start chronyd # 强制同步一次 chronyc makestep做完这五项Centos7才真正准备好迎接Docker。此时运行uname -r应输出3.10.0-1160.el7.x86_64getenforce返回EnforcingSELinux开启状态free -h显示swap为2G——这才是生产环境的基座。3. Docker安装与服务编排Mysql和Redis不是独立容器而是Jenkins流水线的燃料3.1 Docker安装的三个致命陷阱网上教程让你yum install docker-ce但Centos7.9的kernel 3.10.0只兼容Docker CE 20.10.x。如果装了23.x版本docker info会报错“Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?”。正确安装流程# 卸载旧版如果有 yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine # 安装依赖 yum install -y yum-utils device-mapper-persistent-data lvm2 # 添加Docker官方源注意必须指定20.10版本 yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo yum makecache fast # 查看可用版本 yum list docker-ce --showduplicates | sort -r # 安装指定版本20.10.24是最后一个支持3.10内核的版本 yum install -y docker-ce-20.10.24 docker-ce-cli-20.10.24 containerd.io-1.4.13安装后别急着systemctl start docker先加载overlay模块# 检查是否已加载 lsmod | grep overlay # 若无输出则手动加载 modprobe overlay modprobe br_netfilter # 永久生效 echo overlay /etc/modules-load.d/docker.conf echo br_netfilter /etc/modules-load.d/docker.conf注意modprobe overlay必须在systemctl start docker之前执行。我见过太多人跳过这步结果Docker daemon启动失败日志里只有“failed to start daemon”这种废话根本看不出是内核模块问题。启动Docker并设开机自启systemctl start docker systemctl enable docker # 验证 docker run hello-world3.2 Mysql与Redis容器化为什么不用docker run命令而要用docker-compose.ymldocker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -p 3306:3306 mysql:8.0这种命令式启动在生产环境等于自杀。原因有三数据持久化不可靠-v /data/mysql:/var/lib/mysql若宿主机目录权限不对容器启动即退出网络隔离失效多个容器用--link已废弃必须用Docker网络重启策略缺失容器崩溃后不会自动恢复。正确做法是用docker-compose.yml统一编排。在/opt/docker-env目录下创建文件# /opt/docker-env/docker-compose.yml version: 3.8 services: mysql: image: mysql:8.0.33 container_name: mysql-prod restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: Pssw0rd123 MYSQL_DATABASE: myapp MYSQL_USER: appuser MYSQL_PASSWORD: AppPss2023 volumes: - /opt/docker-env/mysql/data:/var/lib/mysql:z - /opt/docker-env/mysql/conf/my.cnf:/etc/mysql/conf.d/my.cnf:ro ports: - 3306:3306 networks: - app-network redis: image: redis:7.0.12-alpine container_name: redis-prod restart: unless-stopped command: redis-server /usr/local/etc/redis.conf volumes: - /opt/docker-env/redis/data:/data:z - /opt/docker-env/redis/conf/redis.conf:/usr/local/etc/redis.conf:ro ports: - 6379:6379 networks: - app-network networks: app-network: driver: bridge ipam: config: - subnet: 172.20.0.0/16关键细节解析volumes末尾的:z是SELinux标签告诉Docker该目录可被容器进程读写解决Centos7 SELinux问题restart: unless-stopped确保宿主机重启后容器自动拉起自定义my.cnf必须包含character-set-serverutf8mb4否则Springboot插入emoji时报错Redis用alpine镜像节省空间但redis.conf里必须显式设置bind 0.0.0.0默认只监听127.0.0.1app-network子网设为172.20.0.0/16避开Docker默认的172.17.0.0/16防止冲突。创建配置文件# 创建MySQL配置目录 mkdir -p /opt/docker-env/mysql/conf cat /opt/docker-env/mysql/conf/my.cnf EOF [mysqld] character-set-serverutf8mb4 collation-serverutf8mb4_unicode_ci skip-character-set-client-handshake innodb_file_per_table1 max_connections200 EOF # 创建Redis配置目录 mkdir -p /opt/docker-env/redis/conf cat /opt/docker-env/redis/conf/redis.conf EOF bind 0.0.0.0 protected-mode no port 6379 tcp-backlog 511 timeout 0 tcp-keepalive 300 daemonize no supervised no pidfile /var/run/redis_6379.pid loglevel notice logfile databases 16 always-show-logo no save 900 1 save 300 10 save 60 10000 stop-writes-on-bgsave-error yes rdbcompression yes rdbchecksum yes dbfilename dump.rdb dir /data replica-serve-stale-data yes replica-read-only yes repl-diskless-sync no repl-diskless-sync-delay 5 repl-disable-tcp-nodelay no replica-priority 100 requirepass Pssw0rd123 lazyfree-lazy-eviction no lazyfree-lazy-expire no lazyfree-lazy-server-del no slave-lazy-flush no appendonly no appendfilename appendonly.aof appendfsync everysec no-appendfsync-on-rewrite no auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb aof-load-truncated yes aof-use-rsync no aof-rewrite-incremental-fsync yes aof-rewrite-trigger-size 0 lfu-log-factor 10 activerehashing yes client-output-buffer-limit normal 0 0 0 client-output-buffer-limit slave 256mb 64mb 60 client-output-buffer-limit pubsub 32mb 8mb 60 hz 10 dynamic-hz yes aof-rewrite-incremental-fsync yes EOF启动服务cd /opt/docker-env docker-compose up -d # 验证 docker-compose ps # 应看到mysql-prod和redis-prod状态为Up此时在宿主机执行mysql -h 127.0.0.1 -P 3306 -u appuser -pAppPss2023 -e SELECT VERSION(); redis-cli -h 127.0.0.1 -p 6379 -a Pssw0rd123 INFO | grep redis_version若返回MySQL 8.0.33和Redis 7.0.12说明底层数据服务已就绪。注意这两个容器的IP如172.20.0.2将作为Jenkins构建时Springboot应用的数据库连接地址而不是localhost——这是容器网络的核心认知。4. Jenkins部署Vue与Springboot不是配置Job就完事而是重建CI/CD信任链4.1 Jenkins安装的隐藏依赖Java版本与OpenJDK选择Jenkins 2.414要求Java 11但Centos7默认java -version输出是1.8.0_361。强行装Jenkins会启动失败。必须先装OpenJDK 11# 卸载旧Java yum remove java-1.8.0-openjdk* -y # 安装OpenJDK 11注意必须用11.0.20低版本有TLS漏洞 yum install java-11-openjdk-devel -y # 验证 java -version # 输出应为 openjdk version 11.0.20 2023-07-18 LTS # 设置JAVA_HOME echo export JAVA_HOME/usr/lib/jvm/java-11-openjdk /etc/profile source /etc/profile下载Jenkins WAR包不要用yum装版本不可控cd /opt wget https://updates.jenkins-ci.org/download/war/jenkins/2.414/jenkins.war # 创建Jenkins专用用户 useradd -m -u 1001 jenkins chown jenkins:jenkins jenkins.war用systemd管理Jenkins服务/etc/systemd/system/jenkins.service[Unit] DescriptionJenkins Server Afternetwork.target [Service] Typesimple Userjenkins Groupjenkins EnvironmentJAVA_HOME/usr/lib/jvm/java-11-openjdk ExecStart/usr/bin/java -Djava.awt.headlesstrue -Djenkins.Home/home/jenkins -jar /opt/jenkins.war --httpPort8080 --prefix/jenkins Restarton-failure RestartSec10 [Install] WantedBymulti-user.target启动Jenkinssystemctl daemon-reload systemctl start jenkins systemctl enable jenkins # 查看初始密码 cat /home/jenkins/secrets/initialAdminPassword提示--prefix/jenkins是关键。如果不加Jenkins根路径是/会和后续Nginx反向代理冲突。我曾因此导致Vue静态资源404排查两天才发现是路径重叠。4.2 Vue项目流水线为什么build产物必须用Nginx容器托管而非Node.js服务器Vue CLI默认npm run serve用webpack-dev-server但生产环境必须用Nginx。原因Node.js服务器无法高效处理静态文件缓存Vue Router的history模式需要Nginx配置try_files $uri $uri/ /index.html跨域问题在Nginx层解决比在Vue代码里写代理更可靠。Jenkins Job配置要点源码管理Git仓库URL填https://gitlab.example.com/frontend/vue-app.git分支填*/main构建触发器勾选“Poll SCM”定时检查H/5 * * * *每5分钟构建环境勾选“Delete workspace before build starts”避免node_modules残留构建步骤执行shell# 清理旧构建 rm -rf dist # 安装依赖注意必须指定Node.js版本 nvm use 16.20.2 npm install # 构建关键publicPath必须设为相对路径 npm run build -- --public-path ./ # 打包成tar.gz供后续部署 tar -czf vue-app.tar.gz dist/注意--public-path ./是核心。若不加Vue生成的js/css路径是/js/app.xxx.jsNginx反向代理时会去根目录找而实际文件在/vue/dist/js/下。设为./后路径变成js/app.xxx.jsNginx location匹配才准确。4.3 Springboot项目流水线如何让Jenkins自动注入数据库配置Springboot的application-prod.yml不能硬编码数据库密码必须由Jenkins注入。方案用Jenkins的Credentials插件存储密码构建时用sed替换模板文件。在Jenkins中“Manage Jenkins” → “Manage Credentials” → “System” → “Global credentials” → “Add Credentials”Kind选“Secret text”Scope选“Global”Secret填Pssw0rd123MySQL密码ID填mysql-root-passwordJob构建步骤添加# 复制application-prod.yml模板 cp src/main/resources/application-prod.yml.template src/main/resources/application-prod.yml # 注入密码注意sed -i在Centos7上必须用双引号 sed -i s/{MYSQL_PASSWORD}/$MYSQL_ROOT_PASSWORD/g src/main/resources/application-prod.yml # 打包 mvn clean package -Dmaven.test.skiptrue # 生成Docker镜像 cd target docker build -t springboot-app:latest .对应的Dockerfile放在Springboot项目根目录FROM openjdk:11-jre-slim VOLUME /tmp ARG JAR_FILEtarget/*.jar COPY ${JAR_FILE} app.jar ENTRYPOINT [java,-Djava.security.egdfile:/dev/./urandom,-jar,/app.jar]关键点-Djava.security.egdfile:/dev/./urandom解决Centos7上Java SecureRandom熵池耗尽导致启动慢的问题。4.4 Jenkins与Docker的深度集成为什么不用Docker Plugin而用Shell脚本Jenkins官方Docker Plugin在Centos7上常因Docker socket权限问题失败。更可靠的方式是用Shell脚本直接调用Docker CLI在Jenkins Job的“Post-build Actions”里添加“Execute shell”# 登录Docker Hub若用私有镜像库改registry地址 echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin # 构建并推送Vue Nginx镜像 cd /var/lib/jenkins/workspace/vue-job docker build -t registry.example.com/vue-app:latest . docker push registry.example.com/vue-app:latest # 构建并推送Springboot镜像 cd /var/lib/jenkins/workspace/springboot-job docker build -t registry.example.com/springboot-app:latest . docker push registry.example.com/springboot-app:latest # 在生产服务器上拉取并更新容器假设生产服务器IP是192.168.1.101 ssh root192.168.1.101 cd /opt/docker-env docker-compose pull vue-app springboot-app docker-compose up -d 注意ssh root192.168.1.101需要提前配置免密登录。在Jenkins服务器执行ssh-keygen -t rsa -b 4096 ssh-copy-id root192.168.1.101这套流程跑通后每次Git PushJenkins自动完成Vue构建 → 打包 → 推送镜像 → 生产服务器拉取Springboot构建 → 注入密码 → 打包 → 推送镜像 → 生产服务器拉取docker-compose up -d触发滚动更新零停机。5. 全流程串联验证从浏览器输入URL到看到“Hello Springboot”的完整链路5.1 最终的docker-compose.ymlVue、Springboot、Nginx、MySQL、Redis五容器协同在生产服务器/opt/docker-env目录下最终的docker-compose.yml长这样version: 3.8 services: nginx: image: nginx:1.24-alpine container_name: nginx-prod restart: unless-stopped volumes: - /opt/docker-env/nginx/conf/nginx.conf:/etc/nginx/nginx.conf:ro - /opt/docker-env/nginx/html:/usr/share/nginx/html:ro - /opt/docker-env/nginx/logs:/var/log/nginx:rw ports: - 80:80 - 443:443 depends_on: - vue-app - springboot-app networks: - app-network vue-app: image: registry.example.com/vue-app:latest container_name: vue-prod restart: unless-stopped volumes: - /opt/docker-env/vue/dist:/usr/share/nginx/html:ro networks: - app-network springboot-app: image: registry.example.com/springboot-app:latest container_name: springboot-prod restart: unless-stopped environment: - SPRING_PROFILES_ACTIVEprod - SERVER_PORT8080 - JDBC_URLjdbc:mysql://mysql-prod:3306/myapp?useSSLfalseserverTimezoneAsia/Shanghai - JDBC_USERNAMEappuser - JDBC_PASSWORDAppPss2023 - REDIS_HOSTredis-prod - REDIS_PORT6379 - REDIS_PASSWORDPssw0rd123 depends_on: - mysql-prod - redis-prod networks: - app-network mysql-prod: image: mysql:8.0.33 container_name: mysql-prod restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: Pssw0rd123 MYSQL_DATABASE: myapp MYSQL_USER: appuser MYSQL_PASSWORD: AppPss2023 volumes: - /opt/docker-env/mysql/data:/var/lib/mysql:z - /opt/docker-env/mysql/conf/my.cnf:/etc/mysql/conf.d/my.cnf:ro networks: - app-network redis-prod: image: redis:7.0.12-alpine container_name: redis-prod restart: unless-stopped command: redis-server /usr/local/etc/redis.conf volumes: - /opt/docker-env/redis/data:/data:z - /opt/docker-env/redis/conf/redis.conf:/usr/local/etc/redis.conf:ro networks: - app-network networks: app-network: driver: bridge ipam: config: - subnet: 172.20.0.0/16Nginx配置/opt/docker-env/nginx/conf/nginx.confevents { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main $remote_addr - $remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for; access_log /var/log/nginx/access.log main; error_log /var/log/nginx/error.log warn; sendfile on; keepalive_timeout 65; upstream springboot { server springboot-prod:8080; } server { listen 80; server_name localhost; # Vue前端路由 location / { root /usr/share/nginx/html; try_files $uri $uri/ /index.html; } # API接口代理到Springboot location /api/ { proxy_pass http://springboot/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 静态资源缓存 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires 1y; add_header Cache-Control public, immutable; } } }5.2 验证链路的七步法每个环节都可单独测试测试Nginx能否访问Vue静态页curl -I http://192.168.1.101→ 返回HTTP/1.1 200 OK且Content-Type: text/html。测试Vue路由是否生效curl http://192.168.1.101/about→ 返回Vue about页面HTML非404。测试API代理是否通curl http://192.168.1.101/api/test→ 返回Springboot的{message:Hello Springboot}。测试Springboot能否连MySQL进入Springboot容器docker exec -it springboot-prod sh执行curl -X GET http://localhost:8080/api/db-test→ 返回数据库查询结果。测试Redis连接在Springboot容器内redis-cli -h redis-prod -p 6379 -a Pssw0rd123 ping→ 返回PONG。测试跨域是否解决浏览器打开http://192.168.1.101F12看Network点击按钮触发/api/user请求Status应为200Response Headers里有Access-Control-Allow-Origin: *。测试Jenkins自动部署在Vue项目里改一行代码git commit -m test ci→git push→ 观察Jenkins控制台输出30秒后刷新浏览器新代码生效。这七步走完你就拥有了一个可审计、可回滚、可复制的生产环境。所有配置文件都在/opt/docker-env目录下用tar -czf prod-env-backup.tar.gz /opt/docker-env就能备份整个环境。6. 我踩过的三个最痛的坑写在最后的真实经验第一个坑是Jenkins的workspace权限。某次更新Jenkins插件后所有Job构建失败日志里只有java.io.IOException: Permission denied。查了三天发现是Jenkins用户对/var/lib/jenkins/workspace目录的sticky bit被清除了。修复命令chmod 1777 /var/lib/jenkins/workspace这个1777必须存在否则多个Job并发构建时会互相删文件。现在我每周用cron自动检查# 加入crontab 0 2 * * * [ $(stat -c %a /var/lib/jenkins/workspace) ! 1777 ] chmod 1777 /var/lib/jenkins/workspace第二个坑是Vue的public/index.html里base href/。本地开发没问题但放到Nginx子路径如/vue/时所有资源404。解决方案不是改base而是在Nginx里用alias指令location /vue/ { alias /usr/share/nginx/html/; try_files $uri $uri/ /vue/index.html; }然后Vue构建时加参数--public-path /vue/。这个坑让我重写了三次前端路由。第三个坑最隐蔽Centos7的/etc/resolv.conf被NetworkManager覆盖。Docker容器内ping mysql-prod超时但ping 172.20.0.2正常。原因是容器DNS默认用宿主机/etc/resolv.conf而NetworkManager会动态改这个文件。终极解法# 禁用NetworkManager的DNS管理 nmcli dev set ens33 ipv4.ignore-auto-dns yes systemctl restart NetworkManager # 手动固定resolv.conf echo nameserver 114.114.114.114 /etc/resolv.conf chattr i /etc/resolv.conf # 防止被覆盖chattr i是关键否则重启后又变回动态IP。这三条经验没有一条写在任何官方文档里全是血换来的。现在我的团队新人入职第一件事就是读这三段。因为真正的生产环境从来不是照着教程敲命令就能跑通的而是无数个“为什么在这里失败”的追问堆出来的。当你把VMware里的Centos7点亮到浏览器里看到那个小小的“Hello Springboot”中间隔着的不是技术而是对每个环节确定性的掌控。
返回列表