学Docker最绕不开的就是那一堆指令。我遇到很多朋友折腾了半天Docker下载倒是搞定了结果一上来就被docker run这一串参数搞得晕头转向。尤其是从Windows环境入门的朋友装个Docker Desktop就够呛好不容易装好了准备部署个MySQL、Redis打开命令行又不知道该敲什么。今天这篇我就把Docker从安装到实战的指令体系彻底捋一遍按真实使用场景拆解每一步讲清楚为什么要这么敲而不是甩你一本命令手册。这篇内容适合刚接触容器、想快速上手的老手也适合那些被各种报错折磨到怀疑人生的新手。我尽量用大白话和实际案例来拆解把常见的坑提前给你踩平。不用死记硬背理解了底层逻辑指令就是个查字典的事。1. 安装与启动先让Docker跑起来很多人以为安装Docker就是下载个安装包双击就完事了但实际操作中服务器和Windows桌面端的玩法完全不同。这一部分最核心的目标就一个让Docker的守护进程daemon跑起来并且能正常接收你的指令。1.1 不同平台的安装指令差异如果你用的是Linux服务器腾讯云、阿里云或者自建的CentOS/Ubuntu安装方式差别很大千万别混用。CentOS系统下我强烈建议不要直接用yum install docker因为默认源里的版本太老。正确操作是先安装yum-utils工具然后添加Docker官方源的仓库地址再安装社区版引擎。这一步骤里最关键的指令是sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install -y docker-ce docker-ce-cli containerd.ioUbuntu则稍微简单点但也是建议走官方源。有些教程让你直接apt install docker.io那个版本虽然能用但往往滞后遇到新版镜像特性时会踩坑。所以如果你追求稳定和功能完整还是建议添加官方GPG密钥和仓库后再安装。这里有个细节安装完成后记得把当前用户加入docker用户组否则每次敲docker ps都要加sudo极其影响使用体验sudo usermod -aG docker $USER newgrp dockerWindows用户基本就是下载Docker Desktop安装包这个没太多指令可讲但有个关键点新版Docker Desktop默认基于WSL2后端在安装前最好先跑一遍wsl --status确认子系统状态正常。安装完以后Docker Desktop托盘图标变绿命令行里docker version能同时看到客户端和服务端版本号就算成功了。1.2 验证安装与启动管理的核心指令装完第一件事就是验证。我在服务器上最常用的验证指令是sudo docker run hello-world。这个指令会自动拉取一个极小的测试镜像然后创建一个临时容器成功运行后输出一段欢迎文本。如果能看到这段输出说明客户端、守护进程、镜像仓库之间的整条链路都是通的后面基本不会出现什么莫名其妙的环境问题。启动管理的指令序列也要熟悉。Linux服务器上Docker安装后不会自动启动需要手动控制sudo systemctl start docker sudo systemctl enable docker sudo systemctl status docker我的建议是把enable和start一起跑这样服务器重启后Docker能自动拉起。别小看这一步很多人在生产环境重启后容器全丢了其实不是容器没了是Docker服务根本没起来。查看状态时看到active (running)就说明守护进程跑起来了。还有一个容易忽略的验证点配置镜像加速器。国内云服务器直接拉取官方镜像经常超时因为Docker Hub的访问链路慢。像阿里云、腾讯云都有免费的个人加速器地址配置方法是在/etc/docker/daemon.json里写一行registry-mirrors配置然后重启Docker。这个操作能直接避免后面80%的拉取超时问题是安装阶段最值得花1分钟做的事情。1.3 Windows下Virtualization报错的实战排查Windows用户最常踩的坑是Docker Desktop启动时弹virtualization support not detected或者类似的虚拟化未开启提示。这个报错字面意思很直接Docker Desktop需要硬件虚拟化支持但系统没让它用上。排查步骤一般是按顺序来的。先打开任务管理器切到“性能”页签看右下角“虚拟化”这一项是不是“已启用”。如果是“已禁用”那就要进BIOS里找Intel VT-x或AMD-V的开关主板厂商不同选项位置不同但基本都在Advanced或CPU Configuration菜单里。开启后保存重启再来。如果任务管理器显示虚拟化已经启用但还是报这个错那问题多半出在Windows功能没开全。控制面板里找到“启用或关闭Windows功能”把“适用于Linux的Windows子系统”和“虚拟机平台”两项勾上重启电脑后再启动Docker Desktop。还有一种情况是WSL2内核版本太旧可以在命令行里跑wsl --update更新内核。实测下来按照这个排查顺序90%以上的虚拟化报错都能解决。2. 基础指令体系从镜像到容器的生命周期Docker的指令体系看着多其实逻辑非常清晰。只要抓住一条主线——把镜像想成“建筑图纸”把容器想成“按图纸盖出来的房子”——大部分指令你就知道该去哪里找答案了。镜像负责定义容器负责运行数据卷是保险柜网络是连接各栋房子的道路。2.1 容器全生命周期的指令对照表我把最常用的操作分成了四类整理成一张速查表虽然网上类似的表很多但这份完全是我根据自己的使用频率筛出来的那些一年也用不到一次的冷门指令就没有列进来。用途指令示例使用频率注意事项拉取镜像docker pull mysql:8.0极高指定标签tag别裸拉latest列出镜像docker images极高加-a可看中间层镜像运行容器docker run -d -p 3306:3306 mysql:8.0极高核心参数后面细讲查看容器docker ps极高加-a列出已退出容器进入容器docker exec -it 容器名 bash极高容器内没bash就改用sh查看日志docker logs -f 容器名极高排错第一指令停止容器docker stop 容器名高注意是优雅停止删除容器docker rm 容器名高先停后删加-f强制删除镜像docker rmi 镜像ID中有容器引用时删不掉资源统计docker stats中实时看CPU内存占用查看配置docker inspect 容器名中输出超级长可配合过滤拷贝文件docker cp 文件 容器:路径低调试时才用平时用挂载这张表的核心逻辑是所有指令都围绕镜像和容器两个对象打转操作前先搞清楚操作的是哪一类对象。2.2 run指令的黄金参数端口、数据卷、环境变量docker run是整个Docker体系里最庞大、也最容易劝退新手的指令。它的参数多得吓人但真正决定你是否能完成日常任务的其实就那几个黄金参数。端口映射-p参数是必须理解透彻的第一个。-p 3307:3306的意思是把宿主机的3307端口和容器的3306端口打通。为什么要这么绕因为容器有自己独立的网络命名空间宿主机上访问不到容器内的端口。你可以把宿主机想象成小区大门容器是里面的住户-p就是给每户配一个专属门牌号。注意宿主机这边端口可以随便换但容器那边端口必须是软件本身监听的端口比如MySQL是3306Redis是6379GitLab是80或443。数据卷-v参数解决的是数据持久化问题。容器一旦被删除里面的所有数据就跟着消失了这对数据库来说是灾难。-v mysql-data:/var/lib/mysql的意思是把宿主机上名为mysql-data的目录实际存储在/var/lib/docker/volumes/下面挂载到容器的/var/lib/mysql路径这样哪怕容器删了重建数据还躺在宿主机上。我强烈建议所有跑数据库的容器都加上-v否则你迟早会经历一次“一夜回到解放前”的惨痛教训。环境变量-e参数则是传递给容器内应用配置的主要途径。比如MySQL镜像就是通过-e MYSQL_ROOT_PASSWORD123456来设定初始密码的Redis镜像通过-e可以传一些启动参数。这个设计很巧妙它让同一个镜像能通过不同环境变量适配不同场景而不需要修改镜像本身。我自己的习惯是敏感信息不要直接写在命令行里而是用--env-file指定一个配置文件这样不容易被history命令泄露。2.3 镜像搬运与清理save、load、prune镜像相关的高频指令中除了pull和images搬运和清理也是日常刚需。在内网环境或者需要批量部署时docker save和docker load就是救命稻草。docker save -o mysql.tar mysql:8.0可以把镜像打包成一个tar文件拷到另一台机器上后执行docker load -i mysql.tar就能导入。这个操作在实际工作中非常常见尤其是有些服务器不允许直接访问外网只能通过离线包来同步镜像。清理相关的指令则是每一个developers都应该掌握的卫生习惯。docker system df可以查看所有资源的占用情况镜像、容器、数据卷、构建缓存加起来能吓你一跳。docker system prune会清理所有已停止的容器、悬空的镜像和未使用的网络但要注意这个指令默认不会删除被容器引用的镜像。如果磁盘实在告急可以加-a参数连未被使用的镜像一起清掉但风险是下一步pull时又要重新下载。构建缓存也要定期清docker builder prune能释放经常高达几个G的缓存空间这在磁盘紧张的服务器上效果立竿见影。3. 典型业务场景的指令组合拳光懂指令不落地等于纸上谈兵。这一节我用三个最常见也最典型的业务场景来讲透部署MySQL 8.0、搭建Redis主从、部署GitLab。这三个案例覆盖了端口映射、数据卷挂载、配置挂载、容器间通信、容器内执行命令这五大核心技能点。3.1 从零部署MySQL 8.0的完整指令流部署MySQL 8.0是搜索热词里最高频的需求也是大多数新手第一次真正用起来Docker的场景。这里面的坑特别多我给你捋一个完整流程。第一步拉镜像docker pull mysql:8.0。注意这里一定要带8.0这个标签因为MySQL镜像的latest标签在某个时期指向的还是5.7不带标签直接拉很容易装成老版本。镜像本来不小加上配置了国内的加速器一般几十秒到几分钟就能拉完。第二步启动容器我推荐使用这条指令docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDYourPassword123 \ -v /data/mysql:/var/lib/mysql \ mysql:8.0这条指令里的每个参数都有讲究。-d让容器在后台运行不加的话你的终端会被容器的日志霸屏--name给容器起个名字后续操作就不用记一长串容器ID了-p 3306:3306把宿主机3306口接到容器3306口-e指定root密码-v把数据持久化到宿主机/data/mysql目录。第三步验证容器状态。docker ps看看MySQL容器的状态是不是Up然后docker logs mysql8看看日志里有没有ready for connections这句话。这里有个坑要提醒你MySQL首次初始化需要大概20到60秒启动初期容器状态是Up但连不上这时查日志是最可靠的判断方式不要急着排查什么网络问题。最后一步进入容器操作。docker exec -it mysql8 mysql -uroot -p可以直接进入容器内的MySQL客户端。如果只是想看看容器内部的结构docker exec -it mysql8 bash能打开一个shell。注意如果提示找不到bash说明这个基于发行版的镜像里没有bash改用sh就行。这里有个特别容易出问题的地方如果你改了环境变量里的密码容器重启后密码并不会跟着变。MySQL镜像的环境变量只在数据目录初始化时生效后续密码修改只能通过SQL或者配置变更。所以第一次启动时密码一定要想清楚否则后面改起来很麻烦。3.2 Redis主从部署的指令组合Redis主从的搭建过程比MySQL更体现容器思维。很多新手拿着物理机的思路去搭集群结果被网络配置折磨得欲哭无泪。其实在Docker里搭Redis主从是一个天然的容器间通信练习。先拉镜像docker pull redis:7.0。然后创建自定义网络docker network create redis-net为什么要先创建网络因为容器间通信需要在一个自定义网络里才能用容器名直接访问。默认的bridge网络不支持通过容器名互相解析自定义网络自带DNS解析能力。这条指令能让你省掉后面百分之八十的烦恼。接着启动主节点docker run -d --name redis-master \ --network redis-net \ -p 6379:6379 \ redis:7.0 --appendonly yes启动从节点的指令docker run -d --name redis-slave \ --network redis-net \ -p 6380:6379 \ redis:7.0 --replicaof redis-master 6379这套操作的核心理解点是--network redis-net让两个容器进了同一个内网从节点里的--replicaof redis-master 6379直接用容器名来定位主节点而不是IP地址。因为Docker内部DNS会把容器名解析成对应的内网IP这种方式比手动写IP可靠得多。容器重建后IP变了但名字不变配置也就永远正确。启动后验证主从状态docker exec -it redis-slave redis-cli info replication看到role:slave和master_link_status:up就说明主从关系建立成功。这个场景如果你能独立跑通那Docker的容器网络概念就已经入了一大半。3.3 GitLab和青龙等其他常见部署的注意事项除了MySQL和Redis搜索热词里还频繁出现GitLab和青龙面板。这两个服务的Docker部署指令逻辑类似但各有各的坑我挑重点讲一点。GitLab部署最核心的是把配置目录持久化挂载到宿主机上因为它包含仓库数据、数据库和配置文件。启动命令一般长这样docker run -d --name gitlab \ -p 80:80 -p 2222:22 \ -v /data/gitlab/config:/etc/gitlab \ -v /data/gitlab/logs:/var/log/gitlab \ -v /data/gitlab/data:/var/opt/gitlab \ gitlab/gitlab-ce:latest这里有个隐藏得很深的坑GitLab容器启动很慢有时要几分钟甚至十几分钟才能完全可访问你以为卡死了其实它内部在做初始化配置。还有一个是SSH端口映射问题很多人用了-p 2222:22后后续做Git操作时发现端口对不上需要在GitLab的配置里把gitlab_shell[ssh_port]改成2222否则克隆地址里不会有端口号。青龙面板的部署相对简单但它依赖管理的核心奥义是依赖安装在容器内部容器一旦重建就全部丢失。所以部署青龙时除了标准的数据卷挂载还得养成一个好习惯——把常用依赖的安装命令收集成一个脚本容器重建后二分钟就能全部恢复。部署指令大体是拉whyour/qinglong镜像、映射端口、挂载/ql/data目录。这个案例给我们的通用启示是容器本质上是不可变的任何内部软件层面的修改最好都能转成基础设施的能力比如通过数据卷、配置文件或初始化脚本而不是手动进容器去改。4. 进阶编排docker compose把多条指令合成一条当你部署的容器越来越多每次启动都要敲一长串docker run参数你很快就会厌倦。这时候docker compose的出现简直是救星。它把多个容器的启动配置写进一个YAML文件一句docker compose up -d就能全部拉起。4.1 docker compose相比docker run的核心优势docker compose解决的不只是指令长度问题更重要的是它把基础设施配置代码化了。以前你部署一个微服务手敲10条docker run指令下次换台机器还得重新敲一遍而且有个参数敲错了自己还发现不了。现在一个docker-compose.yml文件扔过去docker compose up -d就完事。它另一个核心优势是编排依赖关系。比如说你的应用依赖数据库和缓存如果先启动应用再启动数据库应用往往会报连接失败先退出。compose提供了depends_on配置来调整启动顺序虽然它只控制启动顺序不保证服务内部就绪但配合健康检查healthcheck配置可以做到真正的完整启动依赖。我用下来最舒服的一点是compose默认会为项目创建独立的网络所有服务自动加入这个网络服务名就是相互访问的地址不需要手动创建网络这一大块踩坑点直接消失了。Docker Compose的安装很简单新版Docker Engine已经内置了docker compose插件旧版需要单独下载二进制文件。验证方式是在终端里敲docker compose version有版本输出就说明装好了。4.2 一个可以直接抄作业的compose配置案例我拿一个前后端分离项目举个例子包含MySQL、Redis和一个Spring Boot应用。这个配置我改一改就能用在多数实际项目里version: 3.8 services: mysql: image: mysql:8.0 container_name: app-mysql environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: app_db ports: - 3306:3306 volumes: - mysql-data:/var/lib/mysql healthcheck: test: [CMD, mysqladmin, ping, -h, localhost] interval: 10s timeout: 5s retries: 5 redis: image: redis:7.0 container_name: app-redis ports: - 6379:6379 backend: build: ./backend container_name: app-backend ports: - 8080:8080 depends_on: mysql: condition: service_healthy redis: condition: service_started environment: DB_HOST: mysql DB_PORT: 3306 REDIS_HOST: redis volumes: mysql-data:这个配置里的核心知识点有三个。第一是depends_on后面的condition配置它配合MySQL的healthcheck实现“数据库健康了才启动应用”。第二是backend里的环境变量直接写了DB_HOST: mysql因为compose网络内服务名就是主机名。第三是最后的volumes声明让具名数据卷能被compose管理生命周期。实际操作时在项目目录下执行docker compose up -d启动全部服务docker compose ps查看状态docker compose logs -f backend看指定服务日志docker compose down停止并部分清理。就是这么简单整套编排就能运转起来。要注意docker compose down默认不会删具名数据卷所以数据是安全的这个设计非常贴心。4.3 compose常见的使用误区我在实际工作中见过不少人在compose上踩坑最典型的有三个。第一个误区是YAML格式问题。name:前后多了一个空格或者ports列表里的缩进不对都可能让整个文件解析失败。好在docker compose config指令可以校验配置文件格式执行后如果有语法错误会直接提示这个指令在正式启动前先跑一遍是文虫不错的选择。注意是docker compose config不是config本身。新手最容易在这里碰到缩进两格还是四格的问题我的建议是全文统一使用两个空格缩进。第二个误区是depends_on被误以为保证服务就绪。默认的depends_on只控制容器创建顺序不等待服务可用。这是新手最容易慢理解的地方——比如depends_on: mysql然后应用还是连接失败。解决办法就是我上面写的加上健康检查和condition: service_healthy这个组合才能做到真正意义上的等待就绪。第三个误区是compose文件里频繁使用build而不缓存基础层。每次执行docker compose up时如果build的上下文目录里文件变动频繁会重新构建整个镜像。优化思路是尽量把依赖安装命令写在Dockerfile靠前的位置利用Docker的层缓存机制让业务代码之外的部分不会反复重建。5. 问题排查与经验沉淀指令之外的实战功夫Docker用多了你会发现真正让你生产力爆发的不是背了多久指令而是面对问题时能用正确的顺序和组合拳快速定位。这一部分我把我私藏的排查思路和常见问题的应对方案分享出来。5.1 docker网络不通的排查思路网络不通是我被问得最多的问题没有之一。不管是-p端口映射后宿主机访问不了还是容器A访问不了容器B基本的排查思路是固定的。先查容器状态。docker ps -a看目标容器是不是Up状态如果显示Exited了那问题根本不在网络先去查日志。日志就用docker logs --tail 100 容器名拉最近100行看原因。容器是状态正常再查端口映射。docker port 容器名能列出所有端口映射关系确认宿主机端口和容器端口是否对应上了。然后你可以在宿主机上试一下curl 127.0.0.1:宿主机端口如果能通问题就出在宿主机的防火墙或云安全组上。这个定位很重要因为很多云服务器默认安全组不放行你新开的端口和Docker本身一点关系没有。如果是容器A访问容器B不通而且两个容器不在同一个自定义网络里那几乎肯定是网络隔离导致。解决办法是创建自定义网络然后把两个容器都加入这个网络通过容器名互相访问。docker network ls查看所有网络docker network inspect 网络名能看网络里有哪些容器排查起来非常直观。5.2 日志与inspect指令读懂的技巧docker logs是最常用的排错手段-f参数可以像tail -f一样实时跟踪日志输出。当容器内部应用启动失败时日志里通常会给出真正的原因。比如MySQL容器密码初始化失败、Redis配置文件语法错误等日志都会明明白白写出来。docker inspect是另一种深层次的排查手段它输出容器的完整配置信息包括环境变量、网络IP、挂载卷、启动命令等。这个输出非常长所以一般配合一些简单的方法来定位关键信息。如果在Windows PowerShell里可以用docker inspect 容器名 | Select-String IPAddress在Linux环境则习惯用docker inspect 容器名 | grep IPAddress。我想强调一个细节容器里查不到探测工具的应对思路。比如你进容器之后想跑curl发现找不到这个命令。这是常见情况因为官方镜像为了精简体积不会装调试工具。你可以用docker exec直接改敲宿主机的工具链或者加载一个网络调试容器加入同一网络比如docker run --rm --network 容器所在网络 nicolaka/netshoot这个容器里装了全套网络调试工具用完即删非常方便。5.3 常见问题速查表报错信息常见原因解决方案port is already allocated宿主机端口被占用换宿主机端口或docker ps找占用容器No space left on device磁盘满了或inode耗尽docker system prune清理检查overlay2目录Cannot connect to the Docker daemon守护进程没起systemctl start docker并确认运行状态Get https://registry-1.docker.io/v2/: EOF镜像仓库拉取超时配置镜像加速器或重试OCI runtime exec failed容器内没有目标命令把bash换成sh看看镜像基础发行版exit code 137容器被OOM杀掉或手动killdocker stats看内存调整容器资源限制pull access denied镜像名或标签写错了核实仓库名和版本标签确认是否私有仓库这张表是我日常碰到的频率最高的六类问题。处理这类问题的通用原则是所有线上变更一定要谨慎先弄懂问题根因再动手而不是盲目重启。很多人遇到port is already allocated就直接乱改端口其实先查清楚是谁占了端口更重要因为那可能是你另一个服务的正常端口。5.4 资源治理别让Docker拖垮你的磁盘容器环境跑久了磁盘占用会越来越吓人。我见过一台闲置的docker宿主机一年没清理磁盘被悬空镜像和日志撑满。治理资源的第一步是正确使用docker system df这个指令会告诉你镜像、容器、数据卷、构建缓存各自占了多大空间。第二步是用好清理策略。docker system prune清理停止的容器、悬空的网络和悬空的镜像这个操作相对安全不影响正在使用的资源。docker system prune -a连没有被任何容器使用的镜像全部一起清掉释放的空间最大但是下次要用时得重新拉取。docker builder prune专门清理构建缓存如果频繁打包镜像我特别推荐因为构建缓存常常悄悄占用几十个G。日志也是个隐形杀手。容器日志默认无限增长如果应用本身输出量大几天就能写满磁盘。有效做法是在docker run参数里加日志限制比如--log-opt max-size10m --log-opt max-file3或者去/etc/docker/daemon.json里配全局日志轮转。这个配置我强烈建议在一开始就做好否则删日志的时候你会庆幸没有更晚设置。资深使用者的几点忠告用Docker这几年我最大的体会是指令本身不值得背值得背的是它背后的模型思维。镜像定义状态容器是运行的虚像数据卷负责留存网络负责互联——这四样东西的组合就能搭出几乎所有应用的运行环境。每当你不知道用什么参数时问问自己我要操作的是镜像、容器、数据卷还是网络答案自然就出来了。最后再分享一个我个人的小习惯。我会把工作中常用的docker指令整理成一个脚本文件每加一个项目就多记一条。比如部署MySQL、Redis那几条标准的docker run我根本不会去记忆完整写法翻脚本复制粘贴改参数就够了。就算哪天换了新电脑只要这个脚本还在部署环境的效率不会损失太多。这也算是Docker实践中最实在的一条经验了。