跨平台容器化迁移:SpringBoot+MySQL+Redis实践

发布时间:2026/10/20 18:14:32
跨平台容器化迁移:SpringBoot+MySQL+Redis实践
简介面向需要将Spring Boot应用容器化并跨Windows与Linux环境部署的Java开发及运维人员这份docx实操笔记完整梳理了从编写Dockerfile、构建镜像、运行容器到将镜像打包迁移到Linux平台的整套流程同时覆盖MySQL容器化、端口映射、数据卷挂载和网络连通性排查等常见问题。包体共1个文件为docx图文教程压缩包大小约2.48MB步骤分明适合边看边练。已有134人学习下载实用性已得到验证。文档基于实际项目场景展开不仅给出Dockerfile基础语法与docker build、docker run、docker logs等命令用法还总结了访问宿主机数据库的多种解决方案、容器网络调试技巧、Docker Compose管理多容器应用以及将Windows中构建好的镜像迁移至Linux后重新部署的具体注意事项能够帮助读者少走弯路快速实现Spring Boot项目的跨平台容器化交付。无论是个人开发环境还是团队协作交付都能直接参考使用。1. 一次真实的交付场景从Windows开发机到Linux服务器的容器化搬运我这边接到过一个很典型的活儿项目在Windows上开发得好好的SpringBoot服务、MySQL、Redis全部跑在本机一放到现场的Linux服务器上就开始出幺蛾子——要么JDK版本不对要么MySQL配置连不上要么Redis密码没配上。折腾一圈下来时间全耗在环境对齐上。后来我换了个思路既然项目已经用Docker在Windows上跑通了那就干脆把整个运行环境打包成镜像连带着数据库、缓存一起容器化最后用docker save把镜像导出来搬到Linux上用docker load一键还原。整个过程走下来部署时间从半天缩到了十分钟而且再也不用担心环境差异这种玄学问题。这篇方案就按我当时的完整操作链路来写覆盖SpringBoot镜像构建、MySQL与Redis容器化、跨平台镜像迁移以及最后怎么验收和答辩。适合正在做部署类课设的同学也适合被环境问题折磨的从业者直接照搬。2. Windows本机构建Dockerfile编写与SpringBoot镜像的完整链路2.1 Dockerfile设计为什么选openjdk:8-jdk-alpine作为基础镜像在项目根目录创建Dockerfile之前先把整个镜像的构成想清楚。我的SpringBoot项目用的是JDK 8打包产物是jar包所以基础镜像选openjdk:8-jdk-alpine。这个镜像基于Alpine Linux体积比标准的openjdk:8小很多对于只跑一个Java进程的场景完全够用。不需要装操作系统全家桶也没有多余的Shell工具这正好符合“镜像越小攻击面越小传输越快”的原则。# 使用OpenJDK的基础镜像 FROM openjdk:8-jdk-alpine # 设置工作目录 WORKDIR /app # 将项目中的jar包复制到镜像中并且在镜像中的/app工作目录下重命名为app.jar COPY target/打好的应用包.jar app.jar # 暴露应用端口 EXPOSE 8080 # 启动应用 ENTRYPOINT [java, -jar, /app/app.jar]这份Dockerfile有四个关键设计点。第一WORKDIR /app把容器内的工作目录固定下来后面所有相对路径都从这出发避免容器启动时找不到文件的尴尬。第二COPY指令把宿主机的jar包复制进镜像并重命名为app.jar这样镜像内文件名固定后续不管宿主机的包名怎么变都不影响容器启动逻辑。第三EXPOSE 8080只是文档化声明真正让端口对外可访问靠的是docker run时的-p参数。第四ENTRYPOINT用数组形式写每个参数单独用引号包住这样不会因为路径里有空格导致启动命令被拆断——这是我踩过坑之后强制自己养成的习惯。2.2 构建与运行docker build和docker run的参数细节Dockerfile写好后在Windows上打开PowerShell导航到项目根目录必须和Dockerfile同级执行构建命令。注意命令最后有一个.,这个点不是可有可无的它代表构建上下文是当前目录Docker会把当前目录下的文件都作为构建上下文发送给守护进程。我在第一次做的时候漏掉了这个点结果报错找不到Dockerfile后来才意识到这个细节。# 在项目根目录下执行最后的.不能省 docker build -t springboot-app .构建过程会拉取基础镜像、执行COPY指令最后生成一个名为springboot-app的镜像。构建完成后执行docker images能看到镜像列表里有这条记录。接着运行容器# -d后台运行-p 8080:8080映射端口--name指定容器名 docker run -d -p 8080:8080 --name springboot-app-container springboot-app这里的-d和-p是两个高频参数。-d让容器在后台运行不占用当前终端-p 8080:8080将容器的8080端口映射到宿主机的8080端口格式是宿主机端口:容器端口。如果你本机的8080已经被别的服务占用可以改成-p 8081:8080这样就能通过http://localhost:8081访问应用。运行后执行docker logs springboot-app-container查看启动日志日志出现“Started Application in xx seconds”说明启动成功浏览器访问http://localhost:8080做接口验证。2.3 数据库连不上的三种解法从网络模式到容器名互访容器跑起来后最常见的问题就是连不上数据库。我第一次遇到时也愣了一下明明Windows宿主机能连MySQL容器里却报Communications link failure。后来理清了根源Docker容器默认跑在桥接网络里它是一个独立的网络命名空间容器的localhost不是宿主机的localhost。解法一是在docker run指令里加--nethost,让容器共享宿主机的网络栈docker run -d -p 8080:8080 --name springboot-app-container --nethost springboot-app但说实话这个方案意义不大。第一--nethost在Windows上支持有限第二数据库要么在远程服务器要么已经容器化共享主机模式解决不了跨机器的访问问题。解法二是在容器里用docker exec -it进入后先ping一下数据库所在IP或者用telnet探测3306端口是否通docker exec -it springboot-app-container ping 数据库IP docker exec -it springboot-app-container telnet 数据库IP 3306能ping通说明网络层是通的接下来在项目的application.yml里把数据库地址改成这个IP重新构建镜像并运行即可。解法三最彻底把MySQL也容器化放入同一个自定义网络让容器之间用容器名互访。这正好引出下一章的内容。3. 依赖服务容器化MySQL与Redis的持久化部署和容器网络打通3.1 MySQL容器化四目录挂载与端口冲突处理数据库部署到Docker时最大的顾虑是数据安全问题——容器删了数据是不是就没了解决办法是挂载目录。把宿主机的目录映射到容器内的数据目录容器随便删数据留在宿主机上。我在Windows上的习惯是先把挂载目录建好mkdir D:\development_sources\docker\mysql\log, D:\development_sources\docker\mysql\data, D:\development_sources\docker\mysql\conf, D:\development_sources\docker\mysql\mysql-files这四个目录各司其职log存日志data存数据文件conf放自定义配置文件mysql-files用于导入导出文件传输。目录建好后执行容器创建命令docker run -p 3307:3306 --restartalways --name mysqldoc \ -v D:\development_sources\docker\mysql\log:/var/log/mysql \ -v D:\development_sources\docker\mysql\data:/var/lib/mysql \ -v D:\development_sources\docker\mysql\conf:/etc/mysql \ -v D:\development_sources\docker\mysql\mysql-files:/var/lib/mysql-files \ -e MYSQL_ROOT_PASSWORDroot \ -d mysql:8.0.24用3307:3306而不是3306:3306是因为宿主机上已经有一个MySQL实例占用了3306端口。这种情况在开发机上非常普遍换端口是最直接的解法。--restartalways是个很实用的参数它设定了容器的重启策略Docker守护进程启动时或容器异常退出时都会自动拉起容器省去了手工维护的烦恼。-e MYSQL_ROOT_PASSWORDroot用于设置root用户的初始密码这是MySQL官方镜像约定的环境变量。挂载目录的价值在于假如某天容器坏了docker rm删掉旧容器后再用同样的挂载命令新建一个容器数据文件原封不动业务无感知。“容器是临时的数据是永久的”这个理念是从这次操作后才真切体会到的。3.2 Redis容器化AOF持久化与访问密码Redis相对MySQL简单单目录挂载即可。我把数据目录建在D:\development_sources\docker\redis\data然后一条命令完成创建docker run -p 6380:6379 --restartalways --name redis \ -v D:\development_sources\docker\redis\data:/data \ -d redis redis-server --appendonly yes --requirepass redis宿主机6379端口早已被本地Redis占用这是和MySQL相同的端口冲突套路映射到6380。redis-server --appendonly yes开启AOF持久化每个写命令都会追加到文件末尾Redis重启后能根据该文件重新构建数据集不会丢数据。--requirepass redis设置连接密码生产环境无论如何都要设置密码开发环境也建议加否则局域网内别人可以直接连上来操作。数据落盘后用docker exec -it redis /bin/bash进入容器执行redis-cli -a redis即可验证密码能否通过。平时的连接工具如果连不上优先检查两件事端口映射是否生效、密码是否正确别急着怀疑网络。3.3 容器网络打通自定义网络与容器名互访有了MySQL和Redis两个容器后SpringBoot容器要怎么访问它们我采用的做法是创建一个自定义网络dev_net,然后把所有容器都接入这个网络。Docker内置的bridge网络不支持容器名DNS解析自定义网络则可以这是最根本的区别。# 创建自定义网络 docker network create dev_net # 将MySQL和Redis接入网络 docker network connect dev_net mysqldoc docker network connect dev_net redisSpringBoot容器创建时加上--netdev_net参数三个容器就在同一个自定义网络里了。此时在application.yml里数据库地址不再写localhost或127.0.0.1,而是直接写容器名mysqldoc,Redis地址写redis。Docker内置DNS会把容器名解析成对应IP省去手工维护IP的麻烦。这时候还要注意MySQL的用户权限问题。容器里的MySQL默认的root用户仅允许localhost访问从其他容器连接时会报Access denied。解决方案是创建一个允许指定IP或%通配符访问的用户# 进入MySQL容器 docker exec -it mysqldoc mysql -uroot -p # 创建用户%表示任意IP都可以用该账号连接 CREATE USER root% IDENTIFIED BY root; # 刷新权限使配置生效 FLUSH PRIVILEGES;这里有个细节CREATE USER root%创建的是一个与localhost独立的账号密码可以不一样。如果你在Navicat里连接失败先检查是网络层不通还是权限层被拒这两个问题的排查路径完全不同。4. 镜像跨平台迁移docker save打包与Linux环境镜像导入4.1 镜像导出docker save的正确打开方式Windows上的容器全部跑通后接下来要解决的是“怎么把这套环境搬到Linux上”。最简单粗暴也最可靠的方式是直接导出镜像文件。docker save命令把镜像完整打包成tar文件携带全部元数据、分层信息和标签信息# Windows上执行docker需保持运行状态 docker save -o springboot-app.tar springboot-app docker save -o mysqlimage.tar mysql:8.0.24 docker save -o myredisimage.tar redis注意-o参数指定输出的文件名最后一个参数是镜像名。如果你本地镜像名带了版本tag比如mysql:8.0.24导出时一定要带上完整tag否则会提示找不到镜像。我最初只写mysql结果Docker默认去找mysql:latest标签报错说没有这个tag这就是一个容易翻车的小坑。tar文件的大小和镜像大小直接相关通常SpringBoot镜像在200MB左右MySQL镜像在500MB左右。上传前先确认磁盘空间是否充足我用FinalShell连接Linux后直接拖拽上传到自定义目录/root/loadimages下比走命令行scp直观得多。4.2 镜像导入Linux上docker load与验证Linux上安装好Docker后安装步骤见第5章进入镜像存放目录执行加载命令# 进入镜像目录 cd /root/loadimages # 加载镜像 docker load -i springboot-app.tar docker load -i mysqlimage.tar docker load -i myredisimage.tardocker load过程会输出每一层的加载信息最后提示Loaded image: springboot-app:latest。三条命令执行完后用docker images验证镜像是否全部导入成功。这一步要注意docker load导入的是镜像而非容器容器相关的配置端口映射、挂载目录、重启策略、网络配置不会跟着迁移需要在Linux上重新执行docker run命令进行容器创建。我习惯的做法是把Windows上创建容器时的docker run命令复制到一个.sh脚本里换到Linux后直接改路径执行这样能最大程度保证参数一致。4.3 镜像迁移中容易忽略的版本一致性检查镜像能从Windows搬到Linux并不意味着一定能正常跑起来。这里有个“跨平台黑匣子”问题Windows版本的Docker Desktop底层是虚拟机Linux上是原生容器两者内核不一致。所以镜像里的基础镜像是openjdk:8-jdk-alpineAlpine Linux运行在Linux的Docker上完全没有问题但如果是Windows容器镜像如mcr.microsoft.com/dotnet/framework就只能在Windows上运行。迁移完成后我建议做一次“三连查”第一查docker images确认镜像都存在第二查docker ps确认容器都正常运行第三查应用日志确认数据库和Redis连接都成功。日志查询命令是docker logs 容器名如果有连接报错就回到第3章的网络方案排查。这一步看着繁琐但能在第一时间暴露问题比等症状爆发再去翻日志划算得多。5. Linux部署避坑指南防火墙、yum源与容器网络的排查重灾区5.1 环境准备防火墙关闭、yum源替换与Docker安装Linux环境我这边用的是CentOS 7内核版本3.10完全满足Docker的运行要求。安装Docker之前先把环境清理干净——关闭防火墙是开发环境的第一优先级因为容器端口映射后防火墙的规则会拦住外部访问排查起来非常痛苦# 关闭防火墙 systemctl stop firewalld # 禁用防火墙开机不启动 systemctl disable firewalld这一步只建议在开发环境做生产环境绝对不能这样操作而是应该逐端口放行。接着更新yum包管理器这里必须区分两个命令yum -y update升级所有包同时升级软件和系统内核yum -y upgrade只升级包不升级内核。做部署环境时我通常用upgrade,避免系统内核升级引入不必要的变量。# 安装依赖工具 yum install -y yum-utils device-mapper-persistent-data lvm2 # 设置阿里云yum源 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 查看可安装版本 yum list docker-ce --showduplicates | sort -r # 安装指定版本 yum -y install docker-ce-18.03.1.ce # 启动并设置开机自启 systemctl start docker systemctl enable docker # 查看版本验证安装 docker version选用阿里云镜像源而不是官方源是因为国内网络环境下官方源经常拉取超时。版本我选的是18.03.1这个版本相对稳定社区资料也多。安装完成后务必执行docker version确认Client和Server两端的版本信息都正常显示Server端正常才算Docker真正跑起来了。这一步很关键Client装好了但Server没启动是新手常见的问题。5.2 避坑一镜像构建时报错“COPY failed: no such file or directory”现象执行docker build -t springboot-app .时报错提示找不到target/打好的应用包.jar。原因两种可能。一是构建上下文中根本没有这个文件——项目没有先执行mvn package打包出jar包二是PowerShell当前路径没有切换到项目根目录Docker构建时寻找的是当前目录下的相对路径文件。解决先执行ls target/或dir target\确认jar包存在再确认当前所在目录包含Dockerfile文件。建议每次构建前强制走一遍mvn clean package→ 确认jar包生成 →docker build。这个顺序不允许跳步跳了必翻车。5.3 避坑二容器端口映射后宿主机端口已被占用现象执行docker run -p 3306:3306创建MySQL容器时报错Bind for 0.0.0.0:3306 failed: port is already allocated。原因宿主机上已经有进程占用了3306端口。我的开发机上有本地安装的MySQL服务Redis同理占用了6379。解决把宿主机的映射端口换掉比如MySQL的-p 3307:3306,Redis用-p 6380:6379。这里的逻辑是应用容器访问数据库时连接的是容器的3306端口SpringBoot配置里写的是服务端口宿主机端口只是外部访问入口。只要应用容器和MySQL容器在同一网络内应用容器直接连mysqldoc:3306即可宿主机用3307做管理端连接完全够用。5.4 避坑三容器内jar包能启动但数据库始终连不上现象SpringBoot容器启动日志显示Cannot create PoolableConnectionFactory数据库驱动报连接超时或拒绝访问。原因三层问题由浅入深排查。第一层是网络不通——应用容器和数据库容器不在同一个网络第二层是主机地址写错——配置用了localhost而不是容器名第三层是权限不足——MySQL没有为用户授权远程访问。我当时的问题是第二层和第三层同时存在。解决先docker network connect dev_net 应用容器把所有容器拉入同一网络再把application.yml里的地址改成容器名最后执行第3章的CREATE USER授权语句。如果还不行用docker exec -it 应用容器名 ping 数据库容器名验证网络通到哪一层就改到哪一层逐层排除比瞎猜效率高得多。5.5 避坑四docker save后镜像tag信息丢失现象docker load -i加载完成后docker images里镜像的REPOSITORY和TAG显示为none:none。原因导出时指定的镜像名不完整。比如docker save -o mysqlimage.tar mysql本地的镜像实际标签是mysql:8.0.24save命令找不到精确匹配的镜像就只导出了镜像ID的文件。解决导出前先用docker images确认完整标签save时写全mysql:8.0.24。如果已经导出了无tag镜像可以在load后用docker tag 镜像ID mysql:8.0.24手动补上标签。此后我定了一条规矩凡是save必须写完整“镜像名:版本号”。6. 验收与答辩从功能验证到演示文稿的一次性通过技巧6.1 六步验收清单每个环节都有明确的预期结果部署完不能直接说“搞定了”得有一份能说服自己和导师的验收清单。我的验收流程是固定的六步每一步都有具体命令和预期结果。验收项操作命令预期结果镜像完整性docker images三个镜像都存在tag正确容器运行状态docker ps -a所有容器STATUS为Up端口监听状态ss -tlnp | grep -E 8080|3307|6380端口有监听进程应用接口访问curl http://localhost:8080/api/health返回业务正常状态码数据库连接验证docker exec -it mysqldoc mysql -uroot -p -e show databases;能列出业务数据库日志无异常docker logs 应用容器名 --tail 100无ERROR级别报错这套清单做完基本能确认整个链路是通的。在答辩或交付时直接展示这个表格比口头说“我试过了没问题”有说服力得多。6.2 答辩怎么讲把“容器化部署”讲成一条完整交付逻辑答辩时老师问得最多的三个问题是为什么用Docker、数据怎么保证不丢、容器之间怎么通信。我建议按下面这个逻辑组织回答而不是逐个背命令。为什么用Docker解决环境一致性问题——代码在Windows上能跑到Linux上不能跑本质是JDK版本、系统依赖、网络配置不一致Docker把整个运行环境打包成镜像环境跟着镜像走到哪都一样。数据怎么保证不丢通过-v参数把容器的数据目录挂载到宿主机目录容器本身是无状态的删掉重建不会影响数据MySQL的data目录和Redis的appendonly文件都落盘在宿主机上。容器之间怎么通信用自定义网络dev_net配合Docker的DNS解析容器直接通过容器名互访不用关心IP变化。这套架构也可以用Docker Compose来管理用docker-compose.yml把服务编排起来一条docker-compose up -d就能拉起整个环境。答完这三个问题整场答辩的骨架也就立起来了。此后我每次做容器化部署的项目都会强制走一遍“构建→运行→迁移→验收”的完整流程每一步的日志和命令都保留记录。这个习惯帮我避免了很多次临时抱佛脚的尴尬也希望这套方案在你做部署类项目时能派上用场少踩几个我踩过的坑。本文还有配套的精品资源点击获取