Docker实战手册:从镜像容器到部署避坑指南

📅 发布时间:2026/9/14 15:07:31
Docker实战手册:从镜像容器到部署避坑指南
做后端开发这几年Docker基本上已经成了我服务器上的标配。刚接触那会儿我还在为一台机器上同时跑MySQL、Redis、Nginx而费劲折腾环境版本直到把Docker彻底跑通之后才真正体会到什么叫“镜像拉下来、容器起起来、应用就活了”。这份手册算是我对自己过去几年Docker使用经验的一次系统整理从安装部署、核心概念到常用命令、数据持久化、网络通信再到MySQL、Redis、GitLab、青龙面板这类真实应用场景以及镜像打包和IDEA集成基本覆盖了日常开发中会碰到的绝大多数问题。不管你是刚准备在Windows上装Docker Desktop的新手还是已经在Linux服务器上用了一阵、想系统补一补Compose和多容器编排的开发者这份内容应该都能帮上忙。我的原则是少讲空泛概念多放可以直接复制的命令和踩坑记录。文里所有场景都是我在真实环境里跑过的有些坑反复踩了好几轮所以我会把排查思路一并写出来免得你再绕远路。1. Docker到底是什么先把它和虚拟机捋清楚1.1 镜像、容器、仓库这三件套Docker最核心的三个概念就是镜像、容器和仓库。很多新手一开始容易把这三者混在一起我习惯用一个生活化的类比来解释镜像相当于一个“安装包加运行环境的快照”容器是这个快照被启动之后的运行实例。你可以把镜像理解成程序里的“类”容器则是“对象”同一个镜像可以启动出无数个互不干扰的容器。仓库则是存放镜像的地方最常用的就是Docker Hub当你执行docker pull时就是从这类仓库里把镜像拉到本机。镜像本身还有一个很有意思的特性分层文件系统。你看docker pull拉取的时候输出里会显示类似“Pull complete”的一层一层的进度这是因为Docker镜像由很多只读层叠加而成。每次修改Dockerfile、重新构建镜像只有变化的层会被重建没变的层可以直接复用。这也是为什么多个镜像同时存在时磁盘占用并不等于所有镜像体积之和底层相同的层会被共享。理解这一点对后面理解构建缓存、优化镜像体积都很有帮助。1.2 为什么大家都在用Docker它到底解决了什么问题在没有Docker之前部署一个Web应用是很痛苦的。开发机上代码跑得好好的到服务器上一部署环境变量不对、依赖版本冲突、系统库缺失能折腾一个通宵。Docker把应用连同它的运行环境一起打包成镜像容器起来之后应用看到的“操作系统视图”是完全一致的这就把“环境不一致”的问题从根上消掉了。和虚拟机相比Docker的容器是直接共享宿主机的操作系统内核不需要为每个容器都装一套完整的内核所以启动速度是毫秒到秒级资源开销也小得多。你在一台4核8G的机器上跑十几个容器是很正常的事但用虚拟机这么跑基本就卡死了。再加上Docker生态里的编排工具、Compose、镜像仓库它已经成了现代服务端部署的事实标准。接下来我就从安装讲起先把环境跑通。2. 环境安装与第一道坎先把Docker跑起来2.1 Linux下的安装Ubuntu和CentOS的常见路径Linux下装Docker是最顺滑的。Ubuntu上我一般推荐用官方脚本直接装省得手动配GPG key和软件源curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh如果不方便用官方脚本也可以直接从Ubuntu的软件源装sudo apt update sudo apt install -y docker.io sudo systemctl enable --now dockerCentOS这边老版本的用yum install docker-ce新版本的用dnf核心步骤是先装yum-utils再添加Docker官方仓库最后安装docker-ce。装完之后一定要执行一下验证命令sudo systemctl status docker sudo docker run hello-world能打印出Hello from Docker!说明守护进程正常运行容器也能正常拉取镜像、启动运行。我经常会遇到一种情况就是docker命令执行时报“Cannot connect to the Docker daemon”但服务明明已经启动了。这种基本都是权限问题把当前用户加到docker组里就能解决命令是sudo usermod -aG docker $USER加完必须要重新登录终端才生效。2.2 Windows下Docker Desktop安装与VT虚拟化报错排查Windows上装Docker走的是Docker Desktop。这个工具本质上是用一个轻量级的Linux虚拟机来跑Docker引擎所以你本机的虚拟化支持必须开着否则就会遇到那一长串典型的报错Docker Desktop failed to start because virtualisation support wasnt detected...这个报错我见过太多次了尤其是老机器或者刚装完系统、BIOS设置没调过的情况。解决思路按顺序排查就好打开任务管理器切到“性能”选项卡看右下角的“虚拟化”是不是显示“已启用”。如果显示“已禁用”就得重启进BIOS找到Intel Virtualization Technology或AMD-V/SVM之类的选项打开。不同主板位置不一样搜一下主板型号加VT就能找到路径。确认BIOS开启之后再去控制面板的“启用或关闭Windows功能”勾选“Hyper-V”和“适用于Linux的Windows子系统”确认后重启。如果还不行用管理员权限打开PowerShell执行bcdedit /set hypervisorlaunchtype auto然后重启。装完Docker Desktop之后尽量把WSL2也更新到最新在PowerShell里执行wsl --update。最后启动Docker Desktop如果任务栏图标不再报红、docker version能正常输出版本信息说明这关过了。这整个排查过程我建议从任务管理器开始因为只有两秒能快速定位问题在BIOS层还是Windows功能层。2.3 macOS和其他平台Apple Silicon的架构注意点macOS同样用Docker Desktop安装包下载下来拖进Applications就行。但如果你用的是Apple Silicon的Mac需要注意芯片是arm64架构拉取镜像时有些老镜像只提供x86_64版本Docker Desktop可以通过模拟运行但性能会打折。遇到no matching manifest for linux/arm64这类报错时可以显式指定平台拉取docker pull --platform linux/amd64 nginx:1.25 docker run --platform linux/amd64 -d nginx:1.25不过这只是兼容性方案生产环境还是尽量选择同时提供多架构的镜像比如nginx、redis、mysql这些主流镜像都有官方多架构支持直接用默认命令拉就行。3. 镜像管理拉取、搜索、保存与清理的正确姿势3.1 常用镜像操作命令镜像管理是使用Docker频率最高的一类操作。我平时最常用的命令基本就是下面这一组docker search nginx # 搜索仓库里的镜像能看到名称、描述、星数、是否官方 docker pull nginx:1.25 # 拉取指定tag的镜像不写tag默认是latest docker images # 查看本地镜像列表 docker image inspect nginx:1.25 # 查看镜像详情比如环境变量、端口声明、架构 docker tag nginx:1.25 myrepo/base/nginx:1.25 # 给镜像打一个新的tag docker rmi nginx:1.25 # 删除镜像如果镜像正被容器使用会报错 docker image prune -a # 清理所有未被容器引用的悬空镜像有一条命令我建议你养成习惯就是不要裸用latest标签。latest看起来方便但它是一个流动的指针今天拉的和三个月后拉的可能是完全不同的版本。我在测试环境吃过一次亏某天重新部署latest指向的新版本改了默认配置直接导致服务起不来。所以生产环境里镜像我都是固定到具体版本号比如nginx:1.25.3而不是nginx:latest。3.2 镜像备份与迁移save和load对于离线环境、内网环境或者私有部署场景docker save和docker load是很好用的工具。比如有一台可以联网的跳板机先把镜像拉下来打成tar包传到内网服务器上再load进去docker save nginx:1.25 -o nginx-1.25.tar docker load -i nginx-1.25.tar需要注意save和export的区别。save保存的是完整镜像包含它的历史层和元数据load回来之后仍然是完整的镜像可以用来直接run也可以继续打tag、push到仓库。export则是把正在运行的容器导出成一个文件系统tar包它不包含镜像的层结构导入之后是一个新的镜像但历史记录没了。绝大多数场景下你需要的都是save而不是export。3.3 访问私有镜像仓库除了Docker Hub实际项目里更常用的是私有仓库。轻量的方案是用registry镜像自建一个docker run -d --name registry -p 5000:5000 -v /data/docker/registry:/var/lib/registry registry:2然后给镜像打上仓库地址的tag再推送docker tag nginx:1.25 192.168.1.100:5000/nginx:1.25 docker push 192.168.1.100:5000/nginx:1.25 docker pull 192.168.1.100:5000/nginx:1.25如果你是HTTP方式的私有仓库Docker默认不允许非HTTPS仓库需要在/etc/docker/daemon.json里加一行insecure-registries: [192.168.1.100:5000]然后重启Docker生效。这个配置在测试环境很常用但生产环境还是建议用HTTPS或者云厂商的镜像仓库服务。4. 容器生命周期管理启动、进入、日志与清理4.1 创建和运行容器参数到底该怎么写docker run是使用频率最高的命令但它的参数特别多新手经常被吓到。我拆开来讲其实就几类前台还是后台、什么名字、端口怎么映射、数据怎么挂、环境变量怎么传、崩溃怎么重启、资源怎么限制。一个比较典型的启动Nginx的完整命令长这样docker run -d \ --name nginx-web \ -p 8080:80 \ -v /data/nginx/html:/usr/share/nginx/html:ro \ -e NGINX_HOSTexample.com \ --restart unless-stopped \ nginx:1.25逐个解释一下。-d表示后台运行不加的话终端会被容器日志占住--name是给容器起名字起个有意义的名称比用随机ID管理方便太多-p 8080:80表示把宿主机的8080端口映射到容器的80端口-v是把宿主机目录挂载进容器:ro表示只读防止容器里误修改宿主机文件-e是设置环境变量--restart unless-stopped表示容器崩溃或被守护进程重启时自动拉起但你手动docker stop停止的容器不会强行拉起来这是最推荐的重启策略。容器启动之后docker ps查看运行中的容器docker ps -a查看所有容器含已退出。如果你发现容器一直处于Exited状态最快的定位方式是用docker logs查看启动日志这比瞎猜原因高效得多。4.2 进入容器的两种方式exec和attach需要进容器内部排查问题时我一般用docker exec -it 容器名 bash。这个-it是两个参数-i保持标准输入打开-t分配一个伪终端组合在一起才能得到一个可交互的shell。容器里没有bash的情况下可以用sh代替。attach和exec的区别很多人分不清楚。attach是直接连接到容器的主进程的输入输出上当你使用CtrlC时可能直接把容器主进程给终止了。exec则是新建一个进程再进入不会影响主进程。所以日常调试我强烈建议只用exec不要用attach除非你非常明确自己在做什么。4.3 日志、进程和资源监控日志查看是排查问题的利器docker logs -f --tail 200 nginx-web-f是持续跟踪输出--tail 200是只显示最后200行。脚本里如果要拿最后几行日志做判断加个--tail 1就行。还有docker top可以快速看容器内进程docker stats则是一个交互式的资源监控面板能实时显示每个容器的CPU、内存、网络、磁盘IO占用比登录容器之后再free、top要直观很多。我在排查容器内存配置不合理的问题时基本都是靠docker stats先扫一遍。容器用久了会产生很多已退出的僵尸容器和悬挂镜像我的清理习惯是一周一次docker container prune docker image prune -a docker system df # 看看到底占了多少空间5. 数据持久化别让你的数据随容器一起消失5.1 三种挂载方式分别用在什么场景很多新手刚接触Docker时都会踩一个大坑容器删了里面的MySQL数据全没了。这是因为容器本身是“临时”的删除容器时容器内写入的文件系统层也会被清掉。正确的做法是把数据放到宿主机上通过挂载方式让容器读写这样无论容器怎么重建数据都还在。Docker提供了三种挂载方式。第一种是volume由Docker自己管理目录推荐存数据库文件这类数据第二种是bind mount直接把宿主机某个绝对路径挂给容器适合挂配置文件、代码目录第三种是tmpfs直接在内存里读写适合临时缓存重启容器数据就丢。volume的操作命令docker volume create mysql-data docker volume ls docker volume inspect mysql-data # 查看volume实际在宿主机上的路径 docker run -d --name mysql8 -v mysql-data:/var/lib/mysql mysql:8.0bind mount的写法就是把卷名换成宿主机路径像前面Nginx的例子/var/lib/mysql:/data/nginx/html。需要注意的是bind mount要求宿主机路径必须存在如果路径不存在Docker会自动创建一个目录但归属用户是root有时候权限会埋坑。5.2 数据备份与恢复的通用套路volume数据都在宿主机上备份的思路就很直接了。我用的方法是启动一个临时容器把待备份的volume挂进去再用tar打包docker run --rm \ -v mysql-data:/data \ -v /backup:/backup \ alpine tar czf /backup/mysql-data-$(date %Y%m%d).tar.gz -C /data .恢复就是反向操作docker run --rm \ -v mysql-data:/data \ -v /backup:/backup \ alpine tar xzf /backup/mysql-data-20240101.tar.gz -C /data--rm表示容器退出后自动删除这种一次性的备份容器用完即走不会在系统里留垃圾。实际做数据库备份时我还是推荐在业务低峰期配合mysqldump这类逻辑备份一起做物理备份和逻辑备份双保险光靠挂载目录复制还是不够稳。6. 网络与端口容器与容器之间怎么通信6.1 端口映射的原理与限制容器默认运行在一个内部网段里宿主机外的流量进不来。so才有-p 8080:80这种端口映射它本质上是宿主机上做了一个端口转发宿主机监听8080端口收到流量后转给容器的80端口。需要注意-p第一个端口是宿主机端口如果被占用会有“port is already allocated”的报错解决办法无非是换一个宿主端口或者先排查谁占用了这个端口ss -lntp | grep 8080如果希望随机分配宿主端口可以用-p 80这样Docker会自动选一个可用端口通过docker ps就能看到被映射到哪个端口。6.2 自定义bridge网络与容器名通信容器之间通信尤其是容器A要连接容器B的时候最推荐的做法是建一个自定义bridge网络然后用容器名互相访问。默认的bridge网络也支持容器名解析但效果不稳定自定义网络自带内置DNS解析容器名可以直接当主机名用。举个例子我要让一个Java后端容器连接容器里的MySQL只需要这样docker network create app-net docker run -d --name mysql8 --network app-net -e MYSQL_ROOT_PASSWORDroot123 mysql:8.0 docker run -d --name java-app --network app-net -p 8080:8080 myapp:1.0这种情况下Java应用里的数据库连接地址直接写成jdbc:mysql://mysql8:3306/xxx就行不需要关心MySQL容器实际拿到的是什么IP。因为自定义网络会自动处理容器间的DNS解析这比在代码里写死IP强太多容器重建IP变了也不需要改配置。6.3 外部访问管道的理解端口映射的四层链路我建议你记一下客户端访问宿主机IP和映射端口宿主机把流量转发到容器IP和容器端口容器里的进程监听对应端口完成响应后原路返回。所以排查访问不通时就按这条链路逐段检查先看宿主机端口有没有监听再看容器内进程有没有监听最后看防火墙或安全组有没有放行宿主机端口。7. 用Compose一键编排MySQL 8.0和Redis主从的实战7.1 docker-compose.yml到底在编排什么如果你的项目只需要跑一个容器用docker run是够的。但真实项目往往是MySQL加Redis加应用再加Nginx一条条docker run命令写出来又长又容易漏参数这时候就需要Docker Compose。Compose用YAML文件描述整套应用的服务、网络和卷一条命令全部拉起来。新版Docker Compose已经不需要在YAML里写version字段了直接用docker compose命令注意中间有个空格。一个基础的服务编排长这样services: nginx: image: nginx:1.25 container_name: nginx-web ports: - 8080:80 volumes: - /data/nginx/html:/usr/share/nginx/html:ro restart: unless-stopped然后执行docker compose up -d整套服务就在后台跑起来了。docker compose ps查看运行状态docker compose logs -f跟踪所有服务的日志docker compose down -v会停掉服务并删除网络和卷-v慎用会删数据。7.2 用Compose部署MySQL 8.0并配置外部访问MySQL 8.0部署有一个容易踩坑的点默认认证插件和很多老客户端不兼容。8.0默认用的是caching_sha2_password如果你的客户端是5.x时代的驱动去连会报认证失败。下面这份compose配置我加上了utf8mb4字符集、Asia/Shanghai时区以及初始化脚本目录直接用就行services: mysql8: image: mysql:8.0 container_name: mysql8 restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: root123456 TZ: Asia/Shanghai command: - --character-set-serverutf8mb4 - --collation-serverutf8mb4_unicode_ci ports: - 3306:3306 volumes: - /data/docker/mysql8/data:/var/lib/mysql - /data/docker/mysql8/conf:/etc/mysql/conf.d - /data/docker/mysql8/init:/docker-entrypoint-initdb.d启动之后用Navicat或命令行连接测试mysql -h127.0.0.1 -P3306 -uroot -proot123456如果你把宿主机3306端口暴露到了公网一定不要用root和这种弱密码我见过很多云服务器MySQL被勒索的案例基本就是弱密码加3306端口开放惹的祸。更好的做法是在安全组里限制来源IP或者干脆不映射宿主机端口只让应用容器通过内网访问。7.3 Redis主从一条命令拉起来主从集群Redis主从在Docker里做非常简单因为只需要在启动命令里指定主库地址。下面这份compose配置主库监听6379从库监听6380从库通过容器名redis-master自动发现主库services: redis-master: image: redis:7.0 container_name: redis-master restart: unless-stopped command: redis-server --appendonly yes --requirepass redis123 ports: - 6379:6379 volumes: - /data/docker/redis-master/data:/data redis-slave: image: redis:7.0 container_name: redis-slave restart: unless-stopped command: redis-server --slaveof redis-master 6379 --masterauth redis123 --requirepass redis123 depends_on: - redis-master ports: - 6380:6379 volumes: - /data/docker/redis-slave/data:/data需要注意两点。第一从库的--slaveof参数填的是redis-master这是compose网络里的服务名不是IP容器启动后会自动解析。第二主库设置了密码从库必须带--masterauth和主库密码一致否则同步会失败。验证主从是否正常可以进入从库容器执行redis-cli -a redis123 info replication看role:slave和master_link_status:up这两项。我在实际运维里还碰到过一个坑就是Redis数据恢复时从库如果先启动、还没同步到主库数据后续可能会本地有旧数据导致主从不一致所以生产环境尽量让从库依赖主库完全启动之后再启动。8. 构建自己的镜像从Dockerfile到IDEA一键打包8.1 Dockerfile核心指令一篇文章搞懂真实项目里更多时候你不是直接拉现成镜像而是要把自己的应用打成镜像。最基础的一个Java Spring Boot应用的Dockerfile大概长这样FROM openjdk:8-jre-alpine ENV TZAsia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime echo $TZ /etc/timezone COPY target/demo.jar /app/demo.jar EXPOSE 8080 ENTRYPOINT [java, -jar, /app/demo.jar]逐行解释一下。FROM是基础镜像alpine版本的镜像体积很小适合做运行环境ENV和RUN是用来设置时区COPY把宿主机target目录下的jar包复制进镜像EXPOSE只是声明容器内进程监听的端口并不真的发布端口ENTRYPOINT是容器启动时执行的命令。这里用ENTRYPOINT而不是CMD是因为ENTRYPOINT更稳定docker run后面传递的参数会追加到ENTRYPOINT后面而CMD会被后面的参数直接替换掉。构建镜像docker build -t demo:1.0 .-t是镜像名加tag最后的.是构建上下文路径表示把当前目录传给Docker守护进程作为上下文。这个上下文不是你Dockerfile里写的路径而是构建过程中所有文件复制的来源范围所以一个体积很大的目录会导致build非常慢。8.2 .dockerignore别把target目录塞进构建上下文很多项目构建慢的元凶就是把target、node_modules这类目录一起传进去了。解决办法是在项目根目录创建.dockerignore文件内容和.gitignore类似target/ node_modules/ .git/ *.log这个文件的作用和.gitignore一样让Docker构建时排除指定路径尤其是Java项目的target目录里面动辄几百MB的jar包和临时文件不排除的话每次build都慢得想哭。8.3 IDEA里一键打包镜像可视化构建的便捷操作IDEA自带Docker插件配置好之后可以在IDE里直接构建镜像、运行容器对日常联调非常方便。配置分三步打开IDEA设置搜索Docker点击加号添加Docker配置。如果你用的是Docker Desktop选择Docker for Windows或Docker for Mac即可IDEA会自动识别本机Docker守护进程如果你用的是远程服务器上的Docker则需要填TCP地址和证书。配置好之后打开项目里的Dockerfile文件编辑区域旁边会有一个绿色的运行箭头点开可以选择Build Image on Docker。弹出的对话框里填好镜像名和tagIDEA就会执行构建并把构建日志显示在下方控制台。构建完成后可以在IDEA的Docker面板里右键启动容器配置端口映射、环境变量、数据卷挂载省得每次docker run写一大串。我自己用下来的体会是IDEA的Docker集成适合开发环境快速验证但生产发布我还是习惯走命令行加CI流水线因为可重复性更强也更容易被自动化流程接管。9. 进阶场景GitLab和青龙这类复杂应用的部署思路9.1 GitLab注意内存第一次启动要多等几分钟GitLab是一个非常典型的、直接用Docker跑复杂应用的例子。以前手动装GitLab要做一大堆依赖和配置用Docker之后一条命令就可以起一个实例docker run -d \ --name gitlab \ --restart unless-stopped \ -p 8929:80 \ -v /data/docker/gitlab/config:/etc/gitlab \ -v /data/docker/gitlab/logs:/var/log/gitlab \ -v /data/docker/gitlab/data:/var/opt/gitlab \ gitlab/gitlab-ce:latestGitLab这个镜像特别吃内存官方推荐是4G以上内存低于这个值启动过程中很容易出现各种不可描述的报错或者一直卡在启动阶段。第一次启动需要执行初始化可能要等三到五分钟才能通过浏览器访问不要把日志里短暂的报错当成异常耐心看日志里的“GitLab is running!”提示就好。访问地址是http://宿主机IP:8929初始root账号的密码存放在容器里的/etc/gitlab/initial_root_password文件中临时进容器查看docker exec -it gitlab cat /etc/gitlab/initial_root_password用这个密码登录之后第一时间改成自己的密码然后关闭注册功能这是安全上的基础操作。我在生产环境遇到过GitLab容器升级后数据目录权限变了导致起不来的情况所以升级前一定要先备份整个数据目录。9.2 青龙面板一个小而全的定时任务管理器青龙面板是一个在很多技术爱好者中间特别流行的自动化任务管理面板它本身也是一个Docker容器部署方式非常简单docker run -d \ --name qinglong \ --restart unless-stopped \ -p 5700:5700 \ -v /data/docker/qinglong/data:/ql/data \ whyour/qinglong:latest面板默认端口5700浏览器访问之后第一次会让你设置初始化账号密码。它的核心价值在于可视化的脚本管理、定时任务调度和依赖管理。依赖管理这一块特别重要青龙里运行的脚本往往依赖各种Python、Node.js包面板自带的“依赖管理”功能可以安装常用依赖但容器重建之后这些依赖会全面丢失。所以持久化目录/ql/data一定要挂载好脚本、配置、数据库都在里面重建容器后数据不丢唯一需要重新做的就是再装一遍依赖。其实你可以把依赖项整理成一个脚本在面板里通过“新建脚本”的方式重复执行这样即使容器换了也能快速恢复环境。10. 常见问题排查与避坑技巧实录10.1 高频报错速查表我把这几年在Docker使用中最常遇到的报错和排查结论整理成一张表方便你按图索骥报错信息或现象可能原因处理方式Cannot connect to the Docker daemon当前用户不在docker组守护进程没启动sudo usermod -aG docker $USER后重新登录systemctl start dockerDocker Desktop failed to start because virtualisation support wasnt detectedBIOS未开启虚拟化Hyper-V或WSL2未启用检查任务管理器虚拟化状态BIOS开VT-x/AMD-V启用Windows功能并重启port is already allocated宿主机端口被占用ss -lntp | grep 端口号释放端口或换映射端口no matching manifest for linux/arm64当前架构没有对应镜像docker pull --platform linux/amd64或换多架构镜像exec format error镜像架构与宿主机架构不匹配检查镜像架构实现在龙芯等非x86/arm平台需要QEMU模拟或专用镜像dial unix /var/run/docker.sock: connect: permission denied用户权限不足加入docker组或sudo执行命令容器时间比本地时间慢8小时容器内时区不是Asia/Shanghai在Dockerfile或compose里设置TZAsia/Shanghai并挂载localtime容器启动后立刻退出主进程日志出错或者前台进程没挂住docker logs查看原因确保主进程前台运行-v挂载目录里文件为空或权限报错挂载了空目录覆盖了镜像内已有文件用命名volume或先复制容器内文件再挂载10.2 架构差异化场景龙芯这类非主流CPU平台怎么用这几年国产化硬件越来越常见我在龙芯机器上也装过Docker。LoongArch架构目前最头疼的问题不是Docker本身而是镜像生态很多热门镜像根本没有loongarch64版本直接docker pull就会报no matching manifest for linux/loong64。目前的通用思路有两个一是用QEMU用户态模拟运行x86_64镜像性能会有一定损耗二是寻找第三方编译的LoongArch版本镜像或者干脆源码构建。实际使用中我倾向于尽可能减少依赖把应用的非关键组件换到有原生支持的镜像上否则维护成本会比较高。10.3 我个人的几个使用习惯文章最后分享几个我坚持了几年的Docker使用习惯。第一所有容器的数据目录都集中放在/data/docker下按容器名建子目录比如/data/docker/mysql8/data、/data/docker/redis-master/data这样备份和清理都非常直观不用到处找卷数据在哪。第二所有需要长时间运行的容器restart策略一律是unless-stoppedCoreDNS、GitLab、MySQL、Redis这些基础组件即使服务器重启了也能自动恢复。第三镜像tag固定版本号不用latest。第四容器里要改文件时优先考虑挂载配置文件而不是直接进容器改因为容器是无状态的直接改的东西在容器重建后一定消失。这套使用流程是我从最早在测试环境玩Docker到现在维护一套多节点应用服务的完整积累。Docker的学习曲线其实不算陡关键是理解“镜像、容器、网络、数据卷”这四个支柱再配合多写多试大部分问题都能在日志里找到答案。我踩过最深的坑基本都在数据持久化和权限上所以这两块内容我特意写得多一些希望看到这里的你少走一次弯路。