ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Docker从入门到实战:镜像、容器、Compose编排与排错

2026/9/19 15:11:20 拓冰建站 浏览量
Docker从入门到实战:镜像、容器、Compose编排与排错 如果你做过开发或者运维最近两年应该没少听人聊Docker。不管是部署个人项目、搭建中间件环境还是搞微服务集群Docker几乎成了绕不开的基础设施。今天这篇就从一个实际使用的角度把Docker的概念、安装、常用命令、镜像构建和编排部署完整过一遍你跟着操作完基本就能上手日常的开发部署工作了。这篇文章适合几类人看刚接触Docker、打算把它用在个人项目或者公司环境里的同学已经装好Docker但只会docker run想知道镜像、容器、数据卷、Compose这些东西到底怎么配合的人以及被Windows下Docker Desktop各种启动报错折磨过的朋友。全文不绕弯子直接按“先弄明白原理再动手实操最后排坑”的顺序推进。1. Docker到底是什么为什么大家都在用1.1 镜像、容器、仓库三件套先说透很多教程一上来就让敲命令结果docker run是跑通了可遇到问题完全不知道从哪排查。根子上还是概念没立住。Docker最核心的三个东西镜像Image、容器Container、仓库Registry。镜像可以理解成一个“只读模板”里面打包了应用代码、运行时环境、依赖库和配置文件相当于一个完整的文件系统快照。容器则是镜像运行起来之后的实例每个容器之间相互隔离有自己的文件系统、网络和进程空间。仓库就是存放镜像的地方最常用的公共仓库是Docker Hub也可以自建私有仓库。我习惯用一个类比镜像好比装修图纸加全套建材容器就是按照图纸盖出来的房子仓库则是建材市场。同一张图纸能盖出无数间一模一样的房子同一个镜像也能启动任意多个互不干扰的容器。理解了这三者的关系后面所有操作都有方向了从仓库拉镜像用镜像启动容器在容器里运行应用再把改好的代码做成新镜像推向仓库。整条链路就是这么个闭环。1.2 Docker和虚拟机到底差在哪很多人问我有虚拟机了为什么还要用Docker两者最直观的区别在于“虚拟化层”。虚拟机里跑的是一个完整的操作系统包括内核而Docker容器直接共享宿主机的操作系统内核只是通过Linux内核的命名空间和控制组技术做了隔离。这意味着容器没有Guest OS那层额外开销启动速度是秒级的资源占用远低于虚拟机。同样一台2核4G的机器可能跑两三个虚拟机就喘了但跑几十个容器都很轻松。但这并不代表虚拟机没有价值。在需要强隔离的场合比如多租户环境、不同操作系统混跑虚拟机依然是更合适的选择。Docker适合的场景是同内核平台下的大量应用部署你要在Windows宿主机上跑Linux容器底层实际上也要借助WSL2或Hyper-V虚拟化出一个Linux环境。所以两者是互补关系不是替代关系。1.3 Docker是怎么跑起来的架构里藏着排错线索Docker采用的是C/S架构。你敲的docker命令是客户端真正干活的是一系列后台组件。现在新版本Docker采用shim containerd架构containerd负责管理容器生命周期runc负责真正启动进程这些组件协同工作。为什么懂这个有实际价值因为排错需要。Windows下Docker Desktop启动报错很多时候是WSL2的内核没更新或者虚拟化平台没开启这和Docker引擎本身无关。Linux下docker服务启动失败大概率是containerd或docker.socket出了问题。知道这些组件之间的关系排查思路就清晰了先看服务状态再看组件日志而不是盲目重装。2. 动手前的环境准备不同平台的安装与配置2.1 Windows系统安装Docker Desktop先说Windows。现在主流方案是安装Docker Desktop然后选择WSL 2后端或Hyper-V后端。个人推荐WSL 2启动更快、资源占用更合理磁盘性能也比Hyper-V好。安装步骤不复杂提前把下面几件事搞定在BIOS里开启硬件虚拟化Intel VT-x或AMD-V任务管理器里的“虚拟化”一栏显示“已启用”才算数。打开“启用或关闭Windows功能”勾选“适用于Linux的Windows子系统”和“虚拟机平台”。以管理员身份打开PowerShell执行wsl --update把WSL内核更新到最新版之后wsl --status确认是WSL 2。下载Docker Desktop安装包按提示装完重启电脑。安装完成打开Docker Desktop等右下角鲸鱼图标稳定不变说明引擎已经就绪。这时候在PowerShell或CMD里执行docker version能看到客户端和服务器版本信息只有Server端也正常输出才代表真的装好了。2.2 Windows下安装失败的常见原因Docker Desktop在Windows上的报错很有代表性我单独拎出来说。最常见的两个一是“Docker Desktop failed to start because virtualization support wasnt detected”。这个报错含义很明确检测不到虚拟化支持。要么BIOS没开要么Windows的虚拟机平台功能没启用。照着上面的前置条件检查一遍基本能解决。二是“Failed to connect to the Docker API at npipe:////./pipe/docker-desktop-linux”。英文很长核心意思是客户端连不上Docker引擎。先打开Docker Desktop等引擎完全启动再执行命令。如果引擎一直起不来就去“资源”选项卡看WSL集成把发行版勾上或者执行wsl --shutdown后重开Docker Desktop再试。2.3 Linux安装DockerUbuntu和CentOS两条路线Linux下安装Docker更像服务器上的常规操作。Ubuntu/Debian系用apt安装CentOS/RHEL系用yum或dnf安装。Ubuntu推荐用官方仓库sudo apt update sudo apt install -y ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod ar /etc/apt/keyrings/docker.gpg echo deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release echo $VERSION_CODENAME) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-pluginCentOS可以走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.io docker-compose-plugin装完启动服务sudo systemctl enable --now docker执行sudo docker run hello-world验证能正常打印“Hello from Docker!”说明环境没问题。老版本的Docker建议尽早升级很多新特性和安全修复都依赖新引擎CentOS 7尤其需要注意旧版本兼容性。2.4 Linux下忘记配置免sudo权限很多人在Linux上装完Docker执行命令老是提示Permission denied或Got permission denied while trying to connect to the Docker daemon socket。原因很简单Docker引擎监听在/var/run/docker.sock这个Unix套接字上这个文件归属root用户和docker组普通用户不在docker组里自然没权限。解决办法是把当前用户加入docker组sudo usermod -aG docker $USER执行完需要重新登录或者执行newgrp docker让组权限生效。如果当前用户拿来做日常开发和维护这个操作很推荐。但在生产环境要慎重因为加入docker组等同于获得root权限Docker守护进程本身不受常规文件权限约束这属于安全上的选择自己权衡。2.5 镜像加速器配置解决下载慢Docker Hub在国内访问经常不稳定实测下载大镜像时速度可能只有几十KB/s。配置镜像加速器能让拉取速度有明显提升。Linux下修改/etc/docker/daemon.json{ registry-mirrors: [ https://docker.m.daocloud.io, https://dockerproxy.com, https://docker.mirrors.ustc.edu.cn ] }改完重启Dockersudo systemctl restart dockerWindows Docker Desktop用户在Settings - Docker Engine里修改同样的JSON然后Apply Restart。配置生效后拉镜像时可以留意速度变化一般提升非常明显。需要注意的是第三方加速地址有可能失效遇到拉取报错就换一个或查最新的公开加速地址。3. 高频命令与第一次容器运行3.1 镜像管理的几个命令镜像操作是最基础的部分常用命令不多但每个都要理解docker pull nginx:1.25 docker images docker rmi nginx:1.25 docker tag nginx:1.25 myregistry/nginx:v1 docker push myregistry/nginx:v1docker pull从仓库拉取镜像docker images查看本地已有镜像docker rmi删除镜像docker tag给镜像打标签docker push推送到远程仓库。tag命令容易被新手忽略但它是正式推送镜像到私有仓库前必须掌握的命令。比如你要把本地的某个镜像推到自己公司的Harbor仓库就必须打成带仓库地址的完整标签。3.2 容器生命周期启动、停止、删除、进入容器操作是使用频率最高的部分。最基本的几个docker run -d --name my-nginx -p 8080:80 nginx:1.25 docker ps docker ps -a docker stop my-nginx docker start my-nginx docker restart my-nginx docker rm my-nginx docker exec -it my-nginx /bin/bashdocker run的参数解释一下-d表示后台运行--name给容器起名字-p 8080:80把宿主机的8080端口映射到容器的80端口。docker ps只看正在运行的容器docker ps -a看所有容器。docker exec -it my-nginx /bin/bash是进入容器内部操作这是调试时最重要的命令之一。有个小技巧容器名字是唯一的重复启动同名的容器会报错建议在开发和测试阶段养成起有意义名称的习惯避免后期容器多了靠查id管理那场面非常痛苦。3.3 数据卷与挂载容器删了数据不能丢容器本身是无状态的。你往容器里写的文件一旦容器被删掉就全没了。要持久化数据需要用到数据卷volume或绑定挂载bind mount。数据卷由Docker管理用起来简单docker run -d --name mysql8 -p 3306:3306 -e MYSQL_ROOT_PASSWORD123456 -v mysql-data:/var/lib/mysql mysql:8.0这里的-v mysql-data:/var/lib/mysql表示把数据卷mysql-data挂载到容器内/var/lib/mysql目录。就算容器删了数据卷里的数据还在下次重新启动时用同一个卷名挂上去即可。绑定挂载是把宿主机的一个目录直接挂进容器docker run -d --name my-nginx -p 8080:80 -v /data/html:/usr/share/nginx/html nginx:1.25这样宿主机/data/html里的文件改动会实时反映到容器内开发调试热更新场景非常有用。生产环境建议用数据卷因为数据卷的数据生命周期和容器解耦是持久化数据的最佳选择。3.4 日志查看与资源监控容器跑起来之后看日志是日常最多的操作docker logs my-nginx docker logs -f --tail 100 my-nginx docker stats docker inspect my-nginxdocker logs -f是实时跟踪日志--tail 100表示只看最近100行。docker stats实时查看所有容器的CPU、内存、网络和磁盘占用排查性能问题时的第一手里资源。docker inspect输出容器的详细配置信息网络IP、挂载点、环境变量都在里面。3.5 高权限容器配置备忘有些应用要在容器里做比较底层的事情需要提升权限。比如跑一些监控类服务或者需要动态加载内核模块的工具。启动容器时可以加--privileged参数给容器尽可能大的权限。docker run -it --privileged --name demo ubuntu bash但要提醒一句--privileged已经是Linux安全模型里很高风险的一个选项不要随便在生产环境使用。能用cap-add精确授权就尽量用cap-add比如--cap-add SYS_ADMIN这种细粒度授权方式比一把梭的--privileged安全得多。3.6 数据拷出拷入容器和宿主机之间拷贝文件用docker cpdocker cp my-nginx:/etc/nginx/nginx.conf ./nginx.conf docker cp ./app.jar my-app:/tmp/app.jar这个命令在调试配置、备份文件时很常用。虽然生产环境不推荐这种交互式操作但作为应急手段比登录容器后手动安装工具复制文件靠谱多了。3.7 清理临时文件用Docker时间长了系统里会堆满各种悬空镜像、停止的容器、没用的数据卷和构建缓存。清理命令docker system df docker system prune -af docker volume prune docker builder prunedocker system df先看一下磁盘占用情况心中有数再清理。docker system prune -af会删除所有停止的容器、未被使用的网络、悬空镜像和构建缓存。注意-a参数会连同没有标签的镜像一起删谨慎使用。docker volume prune清理未被任何容器引用的数据卷这个命令我一般手动执行因为数据卷里经常有需要保留的数据。4. 用Dockerfile构建自己的镜像4.1 Dockerfile核心指令速查光会拉别人做好的镜像远远不够实际工作里更多是把自己的应用打包成镜像。Dockerfile就是描述“如何构建镜像”的脚本文件。核心指令不多掌握这几个就能覆盖绝大多数场景指令作用示例FROM指定基础镜像FROM python:3.11-slimWORKDIR设置工作目录WORKDIR /appCOPY拷贝宿主机文件到镜像COPY requirements.txt .RUN构建时执行命令RUN pip install -r requirements.txtEXPOSE声明容器对外端口EXPOSE 8000CMD容器启动时执行的命令CMD [python, app.py]ENV设置环境变量ENV APP_ENVproduction每条指令都会生成一个镜像层。层越多镜像越大构建越慢。所以Dockerfile的写法直接影响镜像质量和构建效率。4.2 用Python项目演示一遍完整构建我用一个最简Python Flask应用演示。项目目录结构/app app.py requirements.txt Dockerfileapp.pyfrom flask import Flask app Flask(__name__) app.route(/) def hello(): return hello docker if __name__ __main__: app.run(host0.0.0.0, port8000)requirements.txtflask3.0.0DockerfileFROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8000 CMD [python, app.py]构建命令docker build -t my-flask-app:1.0 .docker build -t指定镜像名和标签最后的点表示构建上下文是当前目录。构建时Docker会把整个上下文文件发送给守护进程所以别在包含大量文件的目录下构建会非常慢。启动容器docker run -d -p 8000:8000 --name flask-demo my-flask-app:1.0访问http://localhost:8000看到“hello docker”说明构建成功。4.3 多阶段构建给镜像减减肥镜像体积直接影响分发和启动速度。Python这种项目直接打包基础镜像可能就有1GB以上其中很大一部分是编译工具、中间文件。多阶段构建能有效解决这个问题。FROM python:3.11-slim AS builder WORKDIR /app COPY requirements.txt . RUN pip install --prefix/install -r requirements.txt FROM python:3.11-slim WORKDIR /app COPY --frombuilder /install /usr/local COPY . . EXPOSE 8000 CMD [python, app.py]第一个阶段安装依赖第二个阶段只拷贝编译好的文件不包含构建期间的冗余数据。这样最终镜像能小很多。构建时先写出能跑的版本再做体积优化是一套稳妥的推进路径。4.4 用IDEA打包并推送镜像不少同学是在IDEA里写Java项目想在IDEA里直接把Spring Boot应用打包成Docker镜像。步骤也很直接安装IDEA的Docker插件。Settings - Plugins搜索Docker并安装。打开Settings - Build, Execution, Deployment - Docker添加Docker连接。Windows下选Docker for Windows自动识别引擎远程服务器则填TCP地址。项目根目录放好Dockerfile右键Dockerfile选择“Create Dockerfile”或“Build Image”。构建完成后在Services窗口的Docker节点下能看到镜像和容器。如果是远程服务器上的Docker需要在服务器上给docker.service加上-H tcp://0.0.0.0:2375并且设置TLS加密。裸奔的2375端口很危险相当于把服务器管理权限直接暴露在公网里生产环境绝对不要这样搞。愿意折腾的话更推荐用docker context连接远程引擎。4.5 .dockerignore避免把临时文件打进镜像和.gitignore类似.dockerignore能指定构建上下文中需要排除的文件。项目目录里如果有node_modules、data、logs、.git这些大目录不加排除规则的话每次构建都会把它们整个发送给守护进程既慢又臃肿。.git node_modules logs data *.md构建时排查镜像突然变大的问题第一件事就是检查.dockerignore配置。5. 用Docker Compose编排多容器5.1 为什么单容器跑完还要学Compose实际项目很少有单容器完成的。一个常规后端服务至少需要应用容器加一个MySQL或Redis可能还有消息队列、日志服务。手动一个个docker run启动再互相配置网络非常繁琐且容易出错。Docker Compose就是解决多容器编排的工具用一个YAML文件定义整个服务栈一条命令全量启动。5.2 docker-compose.yml核心结构一个最基础的Compose文件长这样version: 3.8 services: web: build: . ports: - 8000:8000 depends_on: - db db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: 123456 MYSQL_DATABASE: myapp volumes: - mysql-data:/var/lib/mysql volumes: mysql-data:参数含义一目了然。services下定义各个服务build指定Dockerfile位置ports做端口映射environment设置环境变量volumes声明数据卷depends_on声明服务依赖关系。Compose常用命令docker compose up -d docker compose down docker compose ps docker compose logs -f web docker compose exec web bashup -d启动全部服务down停止并删除容器但不会删数据卷。logs -f跟踪指定服务的日志调试时非常高效。exec进入某个服务的容器内部。5.3 快速部署一套MySQL 8.0 Redis主从热词里有个“Docker安装Redis主从”和“Docker安装MySQL8.0并使用”这两个是高频需求。我用Compose把它们组合起来演示。version: 3.8 services: mysql: image: mysql:8.0 container_name: mysql8 restart: always environment: MYSQL_ROOT_PASSWORD: root123456 MYSQL_DATABASE: testdb TZ: Asia/Shanghai ports: - 3306:3306 command: - --character-set-serverutf8mb4 - --collation-serverutf8mb4_unicode_ci volumes: - mysql-data:/var/lib/mysql redis-master: image: redis:7-alpine container_name: redis-master restart: always ports: - 6380:6379 command: [redis-server, --appendonly, yes] redis-slave: image: redis:7-alpine container_name: redis-slave restart: always ports: - 6381:6379 command: [redis-server, --slaveof, redis-master, 6379] depends_on: - redis-master volumes: mysql-data:MySQL容器需要注意字符集问题。默认字符集在中文场景经常乱码所以我在command参数里显式指定了utf8mb4。Redis主从的关键是--slaveof redis-master 6379这个配置让从节点自动连接主节点完成复制redis-master是Compose网络内的服务名Compose会自动处理服务发现不需要写IP地址。启动命令docker compose up -d验证方式docker exec -it redis-slave redis-cli info replication看到role:slave和master_link_status:up说明主从配置成功。5.4 微服务项目实践搜索热词里还有“Docker部署微服务项目”。微服务场景下服务数量多、依赖复杂手动管理根本不现实。Compose能通过环境变量和网络别名让服务之间互相发现。比如网关服务要访问用户服务和订单服务配置文件里直接写http://user-service:8080Compose网络内会自动解析到对应的容器IP。Java微服务项目在Compose里通常需要配合构建插件。先用Maven或Gradle打包出可执行jar包然后在Dockerfile里用FROM openjdk:17-jdk-slim作为基础镜像COPY进去ENTRYPOINT执行java -jar。镜像搭建好之后Compose负责把它们编排成一台完整的微服务环境。搜索热词里还有“ruoyi radius docker”RuoYi这种开源后台管理系统本身是多模块的后端项目不少人在集成环境中通过Docker化来快速拉起配套中间件。这个方向思路是通的但结论不展开细讲重点还是掌握基础编排能力。6. 常见问题与排查技巧实录Docker用久了会发现真正的坑往往不是操作命令不熟而是各种环境问题、配置问题、权限问题交织在一起。我把自己踩过的坑和解决过的典型问题整理成表按频率排序表现常见原因排查思路Docker Desktop无法启动提示virtualization support was not detectedBIOS虚拟化未开启、Windows虚拟机平台未启用检查BIOS、启用WSL和虚拟机平台、更新WSL内核提示failed to connect to the docker api at npipeDocker客户端连不上引擎打开Docker Desktop等待引擎就绪、检查WSL集成docker服务启动失败containerd或docker.socket异常、端口冲突systemctl status docker查看状态、journalctl -xeu docker查看日志提示Permission denied while trying to connect当前用户不在docker组usermod -aG docker $USER后重新登录镜像拉取慢或卡住网络问题配置镜像加速器、检查DNS容器一启动就退出启动命令错误、应用崩溃、端口占用docker logs看日志、docker inspect看退出码容器内时间不对容器默认UTC时区启动时加-e TZAsia/Shanghai或-v /etc/localtime:/etc/localtime:ro磁盘空间满了镜像堆积、数据卷膨胀docker system df、docker system prune清理排查时按这个顺序走先确认守护进程活着没有docker info再确认容器状态docker ps -a最后看日志docker logs。这一套下来80%的问题都能定位。剩下的就是具体应用的逻辑问题了那就得看应用本身的日志输出。几个特别值得注意的细节docker run执行后容器立刻退出新手最常见的原因是前台进程没有持续运行。比如跑Ubuntu镜像如果不指定-it参数也不指定命令容器启动后进程直接结束容器就退出。解决办法是运行时加-it保持交互或者跑一个类似tail -f /dev/null的长驻命令。这不是Bug而是Docker“容器生命周期绑定前台进程”的设计。端口冲突也特别常见。跑MySQL时报Error starting userland proxy: listen tcp4 0.0.0.0:3306: bind: address already in use说明宿主机3306端口已经被占了。很多Windows用户自己装了本地的MySQL服务这和Docker容器端口冲突。解决办法是停掉本地服务或者改Docker映射端口比如-p 3307:3306访问时就用3307。解决Docker引擎连接不上的问题Windows下有个顺序先检查任务管理器里的虚拟机平台是否开启再wsl --shutdown重启WSL最后重启Docker Desktop多数情况下能恢复。如果docker version能看到Server信息但容器列表是空的多半是连错引擎了检查一下DOCKER_HOST环境变量有没有被污染。无论Windows环境还是Linux环境遇到诡异问题先检查环境变量再考虑重装这个习惯能省很多时间。最后说点实在的这篇的实操部分我尽量还原了从入门到能干活的全过程但Docker本身值得探索的东西还很多镜像仓库Harbor、Kubernetes集群、CI/CD流水线、资源配额、安全加固每一个方向都是独立的知识体系。建议不要一次吃太胖先把常用命令练到肌肉记忆用Compose把手头的项目编排起来再逐步往集群方向走。我个人在实战中体会最深的一点点经验是别为了省事跳过概念直接抄命令花一个下午把镜像、容器、数据卷、网络这四件事彻底搞明白比刷一百个示例命令都值得。遇到问题永远先看日志日志比任何文档都能告诉你真相。Docker已经从一个开发工具长成了运维基础设施趁早掌握不会亏。