Docker从零安装与核心命令指南:Ubuntu、CentOS与二进制方案
开头先从实际痛点切入做开发这几年我见过太多时间浪费在“装环境”这件事上。一个项目要装 Nginx、MySQL、Redis、Python 环境每台机器都折腾一遍版本对不上、依赖冲突、系统差异最后能跑起来全靠运气。Docker 把应用和它依赖的整个运行环境一起打包成镜像用容器跑起来之后同一台服务器上可以同时存在多个互不干扰的运行时这就是大家说的“环境一致”和“资源隔离”。这篇文章不带任何废话从零开始讲清楚两件事一是 Ubuntu、CentOS、二进制这三种 Docker 安装方式二是镜像和容器最常用的核心命令。你不用有基础照着做就能把 Docker 用起来适合刚入门或者在本地/服务器上折腾环境的同学。1. 先搞明白Docker 到底解决了什么问题1.1 环境一致性与资源隔离很多人第一次听到 Docker第一反应是“虚拟机”。它其实不是虚拟机。虚拟机模拟的是整台硬件设备里面要装完整操作系统启动慢、占资源。容器跑在宿主机的内核之上只打包了应用运行所需的文件系统、依赖库和应用代码启动只要秒级本身几乎不占额外资源。你可以把镜像理解成一个“独立小房间的装修模板”容器就是按这个模板造出来的房间房间之间靠墙壁隔开但大家共用同一栋大楼的水电结构。这里有两层核心概念。一是环境一致性同一个镜像在你的笔记本里跑是什么样在云服务器上跑就是什么样不会再出现“我本地明明好的一上服务器就报错”。二是资源隔离Docker 在底层用 namespace 隔离进程、网络、文件系统用 cgroups 限制 CPU、内存、磁盘 IO。简单说你起了一个容器这个容器只能看到它自己那部分资源访问不到宿主机和别的容器的内部内容这是很多生产环境敢把多套服务放同一台机器上的底气。1.2 镜像、容器、仓库三个词吃透 DockerDocker 的世界里最核心的三个概念是镜像、容器、仓库。我习惯用一个类比镜像好比“安装光盘”里面存好了应用及其所有运行条件容器是“光盘装出来的软件实例”可以启动、停止、删除仓库则是存放光盘的地方Docker Hub 就是最大的公共仓库。常见流程是这样的从仓库拉取镜像docker pull基于镜像创建容器docker run容器跑起来后就是一个独立进程。你可以同时基于同一个镜像创建多个容器互不影响。比如拉一个 nginx 镜像用同一个镜像起三个容器分别映射到 8080、8081、8082 三个端口就相当于在一台机器上跑三个网站入口这在以前要么装三份要么折腾端口和系统服务非常麻烦。1.3 安装方案怎么选在线包管理器还是二进制安装 Docker 主要有三条路Ubuntu 用 apt、CentOS 用 yum这是大多数人最推荐的方式好处是自动处理依赖卸载也干净还有一种二进制安装直接从官网下载静态包解压适合离线环境、内网服务器或者对系统版本有特殊要求的场景另外 Docker Desktop 是图形化方案主要用于个人电脑本地的开发调试。我见过不少新手一上来就挑最难的方式硬啃结果卡在依赖上半天。其实选择很简单能连外网的系统优先用 apt 或 yum没法连外网、要求完全离线安装、或者想自己掌控安装路径就选二进制。下面是三种方式的完整实操。2. Ubuntu 安装 Docker官方源与国内源实测2.1 系统要求与旧版本清理安装前先确认系统版本。用lsb_release -a或cat /etc/os-release查看。Docker 官方支持 Ubuntu 的多个长期支持版本比如 Ubuntu 20.04 LTS、22.04 LTS、24.04 LTS。如果你的系统是 18.04 这种老版本也能装但建议先升一下系统省得后面内核和存储驱动出问题。如果你以前装过 Docker或者系统里残留了旧包先清理干净。这一步很多人忽略结果装新版本时出现包冲突或者 dockerd 起不来我踩过好多次。执行下面的命令sudo apt remove docker docker-engine docker.io containerd runc注意这个命令只删旧包不会动/var/lib/docker里的已有镜像和容器数据。如果你确定旧数据不要了可以手动删掉这个目录但我建议第一次清理时先不管它避免误删。2.2 用 apt 安装 Docker Engine最简单可靠的方式是添加 Docker 官方 apt 源。先装依赖工具sudo apt update sudo apt install -y ca-certificates curl gnupg lsb-release接着导入官方 GPG 密钥并添加源。网上很多教程把密钥直接写到某个固定文件官方现在的推荐做法是放到/etc/apt/keyrings目录更清晰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再写入 apt 源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这条命令稍微复杂拆开解释arch$(dpkg --print-architecture)是自动获取系统架构比如 amd64 或 arm64$(. /etc/os-release echo $VERSION_CODENAME)是自动读取系统版本代号比如 jammy、noble。这样你不用手动填版本号一条命令搞定。之后更新源并安装sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin这里顺便装了 compose 插件后面编排多个容器会用到。安装完成后先不急着用把服务启动起来sudo systemctl start docker sudo systemctl enable dockerenable的作用是设置开机自启服务器上一定要做否则重启后就只剩眼泪了。最后验证docker --version docker run hello-worldrun hello-world会拉取一个极小的测试镜像跑起来打印一段欢迎信息说明整套链路没问题。2.3 让普通用户免 sudo 运行 Docker刚装完你会发现直接敲docker ps会报权限错误提示无法连接到 Docker 守护进程。这是因为 Docker 的 socket 默认只给 root 用户访问权限。每次命令都加 sudo 很反人类正确做法是把当前用户加入 docker 组sudo usermod -aG docker $USER改完必须重新登录或者执行newgrp docker让组权限立即生效。之后就再也不用 sudo 了。这里提醒一句把用户加进 docker 组相当于授予该用户几乎等同 root 的权限因为用户可以挂载宿主机目录、执行特权容器。个人开发机无所谓但生产服务器上一定要控制加入 docker 组的用户名单这是很多人没意识到的安全点。2.4 Ubuntu 下最容易踩的三个坑第一个坑是 apt 源更新失败。这通常是因为/etc/apt/sources.list.d/docker.list里写入了错误的系统版本代号或者 GPG 密钥权限不对。排查方式很简单先cat /etc/apt/sources.list.d/docker.list确认内容再用sudo apt update看具体报错大部分问题都能定位到源地址或密钥。第二个坑是磁盘不足。Docker 默认数据目录在/var/lib/docker镜像、容器日志都会写到这里Ubuntu 系统盘如果只有 20G很快会被填满。建议装完就先确认磁盘大小df -h /var/lib/docker。如果不够可以通过配置/etc/docker/daemon.json里的>cat /etc/centos-release uname -r内核版本至少在 3.10 以上CentOS 7 默认内核通常满足要求。CentOS 8 和 Stream 版本操作方式基本一致但要注意 CentOS 8 配置仓库时可能出现 mirrorlist 相关的网络报错需要针对 mirror 地址做额外处理。还有一点CentOS 不像 Ubuntu 有那么多版本文档很多教程写的是老式 docker-io 源那个包非常旧最早不要用直接走 Docker 官方源。3.2 配置 yum 仓库并安装安装yum-utils提供yum-config-manager工具sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo添加完仓库后建议先清一次缓存再装sudo yum makecache fast sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin这里 CMake 慢没关系耐心等。安装过程中如果提示 GPG 密钥确认按 y 接受即可。装完后用rpm -qa | grep docker确认安装的版本正常会看到 docker-ce、docker-ce-cli、containerd.io 三个主要包。3.3 启动服务与配置开机自启CentOS 上是标准的 systemd 服务管理方式sudo systemctl start docker sudo systemctl enable docker systemctl status docker看到状态为active (running)就算成功了。再验证一次客户端和服务端版本是否一致sudo docker version注意看 Server 部分的版本号如果显示Cannot connect to the Docker daemon说明守护进程没起来去查日志journalctl -xu docker。多数是配置问题或 SELinux 拦截继续往下看。3.4 CentOS 特有的几个坑CentOS 上最经典的坑就是防火墙和 SELinux。新装 CentOS 默认开启 firewalldDocker 端口映射做好后你从外部访问宿主机 IP 的端口会发现一直不通。我测过几次解决方案是要么在 firewalld 里放行端口要么干脆关掉 firewalld。生产环境按需放行端口测试环境图省事可以关sudo systemctl stop firewalld sudo systemctl disable firewalldSELinux 配置非常严格也可能导致容器内进程无法访问挂载目录。临时验证方法是setenforce 0如果这样容器就正常了说明是 SELinux 的问题。生产环境不要直接关 SELinux建议用chcon调整文件上下文或者调整 Docker 的 SELinux 配置。另一个坑是--add-repo之后源地址失效。CentOS 7 的生命周期结束时间一到Docker 官方仓库可能不再提供新的 CentOS 7 包。解决办法是把源地址改为 archive 备份地址或者直接转向二进制安装哪种方式不要过分依赖 yum 仓库。4. 离线二进制安装 Docker一条命令外的完整方案4.1 什么时候需要二进制安装二进制安装指从官网下载 Docker 静态二进制压缩包直接解压到系统路径使用不经过任何包管理器。这个方法很适合三类场景内网环境无法访问外部源包管理器仓库里找不到合适的版本或者你希望完全掌控 Docker 的安装目录和升级方式。我第一次在项目里这么装是因为客户服务器在隔离网络yum 源也换成内部镜像了但内部镜像里没有 docker-ce 包只能下载 tar 包拷进去。静态包的好处是依赖极少。Docker 官方把整体编译成一个包包含了 dockerd、docker、containerd、runc 等全部组件解压即用。缺点是后续的升级、卸载、开机自启都得自己写配置不适合完全没动手经验的新手。4.2 下载解压与文件说明去 Docker 官网下载页面找到静态二进制包或者直接在能联网的机器上用命令下载wget https://download.docker.com/linux/static/stable/x86_64/docker-27.3.1.tgzx86_64对应普通 Intel/AMD 服务器aarch64对应 ARM 架构别下错了。解压tar xzvf docker-27.3.1.tgz生成一个docker目录里面包含dockerd、docker、containerd、containerd-shim-runc-v2、ctr、runc等二进制文件。把全部文件复制到系统 PATH 目录sudo cp docker/* /usr/bin/这个时候你敲docker --version已经能出结果了但直接运行还会报错因为缺少 systemd 服务文件没法开机自启也没法以服务方式常驻。接下来手动配置。4.3 手写 systemd 服务文件我们需要创建两个服务文件docker.service和docker.socket。这里给出一个在生产环境验证过的版本sudo cat /etc/systemd/system/docker.service EOF [Unit] DescriptionDocker Application Container Engine Documentationhttps://docs.docker.com Afternetwork-online.target firewalld.service containerd.service Wantsnetwork-online.target [Service] Typenotify ExecStart/usr/bin/dockerd ExecReload/bin/kill -s HUP $MAINPID LimitNOFILE1048576 LimitNPROCinfinity LimitCOREinfinity TasksMaxinfinity TimeoutStartSec0 Delegateyes KillModeprocess Restartalways StartLimitBurst3 StartLimitInterval60s [Install] WantedBymulti-user.target EOF解释几个关键项Typenotify表示 dockerd 启动完成会通知 systemd这样systemctl start docker才能准确判断启动成功LimitNOFILE1048576调大了文件描述符上限容器多了之后不会出现无法打开文件的情况Delegateyes是关键中的关键把 CPU、内存等资源控制委托给 Docker否则 cgroups 资源限制可能失效。然后启用服务sudo systemctl daemon-reload sudo systemctl start docker sudo systemctl enable docker再执行sudo docker run hello-world。如果这一步成功二进制安装就算真正完成了。注意这里跑到 hello-world 需要能访问 Docker Hub如果你的环境完全离线这一步会卡在 pull 阶段这是正常的不意味着安装失败你只需要后续手动把镜像导入进环境即可。4.4 升级与卸载的注意事项二进制安装的升级非常简单下载新版本包解压停掉 Docker复制覆盖二进制文件启动服务。完整命令流程sudo systemctl stop docker cp docker/* /usr/bin/ sudo systemctl start docker升级前建议先备份/var/lib/docker尤其是生产服务器上跑着大量容器时。卸载也更粗暴删掉二进制文件和服务文件清掉数据目录sudo systemctl stop docker sudo rm /usr/bin/dockerd /usr/bin/containerd /usr/bin/docker /usr/bin/runc sudo rm /etc/systemd/system/docker.service sudo rm -rf /var/lib/docker最后一步会清掉所有镜像和容器数据谨慎操作。5. 镜像操作pull、images、rmi、prune 这些命令怎么用5.1 拉取镜像前先搞懂标签的含义镜像是 Docker 里最基础的单位。命令行第一个高频操作是拉取镜像docker pull 镜像名镜像名一般格式是仓库地址/作者/镜像名:标签。比如nginx:1.25其中nginx是镜像名1.25是标签表示具体版本。如果你不写标签默认拉取latest标签。这里我强烈建议显式指定版本标签因为latest是会变的几个月后再跑同一个命令拉下来可能已经不是同一个镜像生产环境的可复现性会大打折扣。5.2 查看与删除镜像看本地有哪些镜像docker images # 等价 docker image ls输出列包含仓库名、标签、镜像 ID、创建时间和大小。镜像 ID 是 64 位哈希的前 12 位删除时可以用它代替完整镜像名。删除一个镜像docker rmi nginx:1.25如果镜像被容器占用会报错提示先删除容器。两种解决办法先删容器再删镜像或者用docker rm -f强制删容器。另外镜像删除时只删除指定标签对应的层多个标签可能共享底层镜像层删除一个标签不会影响另一个标签引用的底层内容。5.3 清理悬空镜像与构建缓存用了一段时间 Docker本地会积累一堆“悬空镜像”。悬空镜像是指没有任何标签引用、也不再被任何容器使用的镜像层通常来自反复重新构建镜像。这些内容白白占用磁盘清理命令docker image prune加上-a参数会清理所有未被容器引用的镜像包括有标签但不再使用的docker image prune -a我建议定期执行一次配合docker system df看看磁盘使用概况。很多服务器磁盘饱满其实就是镜像和容器日志堆出来的而不是真实业务数据。5.4 镜像导入导出和私有仓库离线环境之间的镜像搬运最常用的是保存与导入。把镜像保存成一个 tar 文件docker save -o nginx.tar nginx:1.25另一台机器上导入docker load -i nginx.tarsave保留的是完整的镜像信息和所有层比export容器更可靠。另一个方案是搭建私有仓库用docker tag给镜像打上私有仓库地址后docker pushdocker tag nginx:1.25 registry.example.com/nginx:1.25 docker push registry.example.com/nginx:1.25这里registry.example.com需要替换为你自己的仓库地址。小团队内部用这种方式分发镜像最方便不用到处拷 tar 包。6. 容器操作run、exec、logs、ps 的核心用法6.1 run 命令的完整参数拆解docker run是容器操作里最重要的命令新手最容易在上面犯迷糊。最基本的形式docker run -d --name web -p 8080:80 nginx:1.25选项拆解-d表示后台运行不占用终端--name web给容器命名后续操作都靠名字指代-p 8080:80把宿主机的 8080 端口映射到容器的 80 端口外部访问宿主机IP:8080就相当于访问容器内 Nginx 的 80 端口。如果没有-p容器内服务虽然起来了但宿主机访问不到。还有其他常用选项我再列一组-it交互模式进入容器配合bash使用。--rm容器退出时自动删除适合临时测试。-e设置环境变量比如-e TZAsia/Shanghai。--network host容器直接用宿主机网络不需要端口映射这时候要注意端口冲突。-v挂载数据卷后面详细说。--restartalways容器异常退出时自动重启生产环境必加。6.2 进入正在运行的容器exec 和 attach容器跑起来后想进去看环境、调试程序有两个命令exec和attach。我强烈推荐用execdocker exec -it web bash这个命令在容器内启动一个新的 bash 进程适合执行命令、安装调试工具、查看目录结构。退出时用exit不会影响容器本身容器继续后台运行。attach则不同它会直接连接到容器的主进程退出时往往会连带着终止容器因为主进程收到 EOF 就退出了。所以平时调试优先用 exec少用 attach。6.3 端口映射与数据卷端口映射解决的是外部访问问题数据卷解决的是容器重建后数据丢失问题。容器内部是临时的删掉容器容器里写的文件全没了。通过数据卷把宿主机目录挂载进容器docker run -d --name web -p 8080:80 -v /home/user/nginx/html:/usr/share/nginx/html nginx:1.25冒号前面是宿主机目录后面是容器内目录。这样你在宿主机上修改文件容器里立刻能看到删掉容器重跑一条命令数据还在宿主机上。另一种是命名卷docker volume create html docker run -d --name web -v html:/usr/share/nginx/html nginx:1.25命名卷由 Docker 管理目录位置在/var/lib/docker/volumes下适合保存数据库数据等不需要手动访问的内容。日常开发中MySQL、Redis 这类有状态服务我建议务必用命名卷否则容器删掉数据就没了这种事故我见过不止一次。6.4 查看容器状态、日志与资源占用查看运行中的容器docker ps加-a查看所有容器包括已停止的。输出里有容器 ID、镜像、命令、状态、端口映射、名称。查看某个容器详细信息docker inspect web输出是 JSON包含网络配置、挂载点、环境变量等所有细节排查问题时信息量很大。看日志docker logs web docker logs -f web-f的意思是 follow持续跟踪日志输出相当于tail -f调试时很有用。资源占用实时查看docker stats输出显示每个容器的 CPU、内存、网络 IO、磁盘 IO按下 CtrlC 退出。docker stats --no-stream可以只看一次快照适合写脚本监控。6.5 停止、重启与删除容器的正确姿势容器生命周期操作docker stop web # 优雅停止给进程一些时间处理退出 docker start web # 启动一个已停止的容器 docker restart web # 重启 docker pause web # 暂停所有进程不释放资源 docker unpause web删除容器的正确姿势docker rm web docker rm -f web # 强制删除运行中的容器docker stop默认给容器 10 秒做退出处理时间太短可以把-t参数加上比如docker stop -t 30 web。生产环境建议用这种优雅退出方式避免数据库进程强制被杀导致数据损坏。7. 一个完整的实战流程用 Docker 部署 Nginx7.1 五条命令跑起一个网站说了这么多命令不如完整跑一个实际例子。场景是在一台空服务器上部署一个最简单的 Nginx 网站。拉镜像docker pull nginx:1.25创建并启动容器docker run -d --name web -p 8080:80 --restartalways nginx:1.25此时已经能访问了。浏览器打开http://服务器IP:8080看到 Nginx 欢迎页。再补充两条常用命令docker ps docker logs -f webdocker ps确认容器运行状态logs -f实时看访问日志按 CtrlC 退出日志跟踪。整个部署流程加起来就是这 5 条命令没有系统包依赖没有编译安装这就是 Docker 最直观的优势。7.2 修改页面并持久化数据默认的 Nginx 页面在容器内要自定义页面就得挂数据卷。先在宿主机建目录mkdir -p /data/nginx/html echo h1Hello Docker, from host/h1 /data/nginx/html/index.html停掉并删除旧容器用挂载方式重新启动docker stop web docker rm web docker run -d --name web -p 8080:80 -v /data/nginx/html:/usr/share/nginx/html nginx:1.25刷新页面就能看到宿主机上写好的内容。之后再改页面直接改宿主机文件就行容器不用动。这在实际使用中非常重要结构化的配置、代码、静态资源都应该放在宿主机或命名卷里容器只负责运行。7.3 容器故障排查的基本套路容器起不来或者访问异常时我的排查顺序是固定的。第一步看状态docker ps -a看容器是否在运行状态列如果总是Exited说明进程崩溃或启动失败。第二步看日志docker logs web找最后的报错这是定位问题的关键。第三步看启动命令docker inspect web检查环境变量、端口映射和挂载配置是否写错。第四步查端口监听在宿主机上用ss -lntp | grep 8080确认端口真的被 dockerd 的映射规则监听。这几步能解决九成问题。剩下的一成通常和系统配置有关比如防火墙没放行端口、挂载目录权限不对、容器内进程需要特定内核参数等这类问题就要结合前文提到的 SELinux、firewalld、系统日志来排查。8. Docker Desktop 与日常使用建议8.1 Docker Desktop 适合谁Docker Desktop 是官方提供的图形化桌面版支持 Windows 和 macOS。它也底层跑的是 Linux 虚拟机只不过帮你把环境都封装好了装完点图标就能用界面里能看容器列表、日志、停止启动。这个方案和个人开发场景挺搭尤其是在 Windows 上做开发、但目标环境是 Linux 服务器的同学本地用 Docker Desktop 调试部署到服务器时直接用同一套镜像能省掉不少环境不一致的麻烦。但注意一点Docker Desktop 在 Windows 上依赖 WSL2 或 Hyper-V老机器没有开启虚拟化支持时安装会失败。如果你只需要在 Linux 服务器上跑完全不必装 Desktop用 apt 或 yum 安装反而更省资源。Desktop 更适合个人电脑上的本地开发。8.2 开发者日常的推荐工作流我用 Docker 几年总结出的最舒服的工作流是这样的开发阶段用 Docker Desktop 或者 Linux 本机 Docker 起中间件MySQL、Redis、RabbitMQ 都以容器方式运行应用代码跑在本地通过-p 3306:3306这类映射连进容器里的服务。交付阶段把应用打包成镜像构建好之后 push 到仓库服务器上直接 pull 下来跑。数据库这类有状态服务在测试环境也用容器但数据必须挂到命名卷里。配置管理之类的临时任务用--rm跑一次性容器用完自动消失不污染宿主机。这套流程的好处是你本地调试的镜像和线上跑的镜像完全一致不会再出现“本地能跑线上不行”的经典问题。8.3 我实践中的几个小技巧最后分享几个很小但能提效的习惯。第一个是给容器加--restartalways尤其是 MySQL、Redis、Nginx 这类常驻服务保证重启机器后自动恢复不用手动去起。第二个是控制镜像体积多阶段构建、清理 apt 缓存、不要什么调试工具都装进生产镜像镜像小体积会让 pull 速度明显变快。第三个是善用docker system prune清理磁盘但注意这个会把暂时不用的数据全清掉执行前最好先看一眼。第四个是启动容器时都写上--name没有名字的容器只能通过一长串容器 ID 操作非常痛苦。8.4 对我来说Docker 改变了什么我个人在项目里用了 Docker 之后最大的感受不是命令背得多熟而是心态变了。以前配置环境总是提心吊胆生怕把系统搞坏现在随便起容器、删容器大不了重新拉一个镜像几分钟又是一条好汉。这种“可丢弃、可重建”的思维才是最值钱的东西。希望你不要急着背命令先把一个 Nginx 容器从头跑到尾体验一下从镜像到容器的完整流程后面所有东西都会顺很多。