资讯详情

Docker入门完整指南:从安装到运行第一个容器

📅 2026/10/14 4:28:40 | 华诺云谱 👁 阅读
Docker入门完整指南:从安装到运行第一个容器
如果你是想正儿八经把 Docker入门 这条路走通而不是在社区里翻几十个帖子拼出零碎命令那这篇文章就是按“从安装到运行第一个容器”这条完整链路写的先把概念讲透再把每一步命令落地最后把常见的坑挨个点名。我当初被Docker吸引是因为实在受够了环境不一致的苦——同事电脑上跑得好好的服务换个机器就各种缺依赖几天的活儿被环境问题吃掉大半。后来用Docker固化整个运行环境才真正体会到什么叫“一次构建处处运行”。这篇文章不假设你有任何基础但也不啰嗦适合刚接触Docker、想在最短时间内把它跑起来并理解背后原理的开发者。1. 为什么是Docker它到底解决了什么问题1.1 环境一致性最实在的痛点写代码半年以上的同学多多少少都被“环境问题”折磨过本地跑得好好的服务提交到测试环境就起不来测试觉得没问题上线又是另一个样。要深究起来原因并不复杂——一个应用能正常运行依赖的是系统版本、运行时版本、动态库、路径配置、环境变量甚至系统时区的叠加组合任何一个环节有差异都可能翻车。Docker的解决思路很直接把应用连同它需要的运行环境一起打包成一个镜像。镜像里固定了基础操作系统层、依赖库、代码和启动命令容器启动时就在这套固化好的环境里运行和宿主机上装了什么、缺了什么基本没关系。我现在给同事交接项目直接给一个镜像名加一条docker run命令对方拉下来就能跑省掉了一大段“如果缺什么库先装一下”的对话。这也是Docker和虚拟机最本质的区别虚拟机虚拟的是整台硬件设备每个虚拟机都有一套完整的操作系统而容器共享宿主机内核只在自己的用户空间内做隔离。所以容器启动快、占用资源小一个普通的云主机能跑几十个容器但跑几个虚拟机就吃力了。1.2 三个核心概念镜像、容器、仓库很多新手看Docker资料第一关就被三个概念搞晕。其实用生活里的例子特别好懂镜像Image可以理解成装机用的系统镜像盘里面已经刻好了操作系统、软件、代码和启动脚本。它是只读的本身不能执行只是静态文件集合。容器Container镜像的运行实例。把镜像盘放进光驱启动之后跑起来的那个“活的系统”就是容器。同一个镜像可以同时启动多个容器彼此互不干扰。仓库Registry存放镜像的地方。最常用的是公共镜像仓库相当于镜像的应用商店企业里也可以自建私有仓库放内部的不希望外泄的镜像。对应这三个概念有三条最核心的命令后面所有操作几乎都围绕它们展开docker pull 镜像名:标签 # 从仓库拉取镜像到本地 docker run 镜像名:标签 # 用镜像创建并启动容器 docker push 镜像名:标签 # 把本地镜像推送到仓库标签tag可以理解成版本号例如nginx:1.27和nginx:1.26就是两个不同版本。不写标签时默认是latest即最新版但这个默认值有时候会坑人后面具体讲到的时候我再展开。1.3 什么时候该用什么时候别硬上Docker不是万能药我曾见过有人为了跑一个简单脚本强行套上容器结果折腾了一下午纯属给自己加戏。以下几个场景用Docker收益非常大Web应用和服务端程序前后端项目极其依赖运行时版本和依赖库容器化之后部署和回滚都变成一条命令的事。中间件数据库、消息队列、缓存服务等中间件用Docker启动几分钟就能拉起一个干净的实例测试、联调、临时环境都方便。持续集成与自动化测试在流水线里用同样的镜像跑测试保证构建、测试、生产环境完全一致。反过来以下几种情况建议先用宿主机直接跑写个只有几百行的脚本工具、纯计算任务、需要直连特殊硬件外设的应用。尤其注意GPU直通虽然在Linux上已有NVIDIA容器工具支持但配置复杂度对新人不友好入门阶段别硬碰。2. 安装Docker各平台实操记录2.1 安装前需要理解的结构Docker装好之后实际由两个部分组成客户端docker命令和守护进程dockerd。日常执行的docker命令其实是客户端向守护进程发送请求由守护进程真正去拉镜像、启容器。理解这一点有助于排查一类很常见的报错即Permission denied或cannot connect to the Docker daemon多半是客户端连不上守护进程而不是Docker没装好。安装Docker的方式主流有两种用包管理器安装或者用官方提供的一键安装脚本。一键脚本虽然方便但它会从网络下载并自动执行如果你对系统安全和可追溯性有要求我更推荐自己在机器上执行包管理器安装方式每个步骤可控出了错也知道卡在哪一步。2.2 Linux系统安装实录我自己主要用Debian系的Linux发行版下面以Debian系的安装过程为例。RHEL系的思路完全一样只是几个命令的包名稍有不同。先把旧版本清理干净sudo apt-get remove docker docker-engine docker.io containerd runc上面这些包可能没装报未安装是正常的继续往下就行。然后安装依赖工具让apt能够通过HTTPS使用软件源sudo apt-get update sudo apt-get install ca-certificates curl gnupg接下来是配置软件源。这一步很多人会忽略直接用默认源也能装但下载速度可能很慢。需要先添加Docker官方提供的GPG密钥再写入软件源列表sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/你的版本代号/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg echo deb [archamd64 signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/你的版本代号 稳定版分支 | sudo tee /etc/apt/sources.list.d/docker.list sudo apt-get update注意上面的“你的版本代号”和“稳定版分支”需要替换成具体的系统版本标识。如果你觉得写软件源比较麻烦使用系统自带的包管理器直接安装Docker也是可行的区别是版本可能旧一些但入门阶段完全够用。软件源配好之后安装Docker引擎和命令行工具sudo apt-get install docker-ce docker-ce-cli containerd.io装完启动守护进程并设置开机自启sudo systemctl enable docker sudo systemctl start docker这里有个小细节enable是设置开机自启start是立即启动。两个都执行一下否则这次能用重启机器之后又连不上守护进程。2.3 Windows和macOS的安装要点Windows上安装Docker现在主流的方案是先用好WSL 2Linux子系统再安装Docker Desktop。实际上Docker Desktop在Windows下仍然是借助一个轻量虚拟机跑Linux内核来完成容器运行WSL 2只是让这个支撑更加顺畅。安装过程基本是图形界面一路下一步但有两个常见的坑一是WSL 2需要开启如果没开安装完启动Docker会提示内核版本或虚拟化不支持二是BIOS里的虚拟化技术选项如果关闭WSL 2根本起不来。macOS的情况类似Docker Desktop基于HyperKit虚拟化框架对系统版本有一定要求太旧的系统安装新版会直接拒绝。装完之后顶部菜单栏会出现Docker的小图标等它变成绿色守护进程才就绪。提示Windows和macOS上的Docker Desktop均需要登录才能正常使用注册一个账号即可免费的个人版足够入门学习使用。2.4 验证安装跑通第一条链路无论哪个平台装完第一步都是用测试镜像验证整条链路是否通畅docker run hello-worldhello-world是一个极小的测试镜像它就干一件事输出一段欢迎语然后退出。如果能看到那段英文说明恭喜你的客户端、守护进程、镜像拉取和容器启动整条链路已经全部打通了。实际的输出大概是下面这样Hello from Docker! This message shows that your installation appears to be working correctly.我在实际教学中发现有相当比例的新手卡在这一步报错信息五花八门。如果输出报错多数情况是守护进程没起来或者当前用户没有权限具体排查方法在第6章里专门写。3. 运行第一个容器从Hello World到理解生命周期3.1 一条命令背后发生了什么docker run hello-world看似只有一条命令背后其实是一串连环动作。理解这个过程比记住命令本身更重要客户端检查本地是否有hello-world:latest这个镜像。本地没有客户端告诉守护进程去公共镜像仓库拉取。守护进程下载镜像到本地磁盘。守护进程根据镜像创建一个容器并启动它。容器执行镜像里定义的默认命令输出欢迎语。命令执行完毕容器退出但容器本身还在状态变为 exited。注意最后一条容器退出不代表被删除。很多新手发现docker ps看不到容器就以为出了什么问题其实是docker ps默认只显示运行中的容器用docker ps -a才能看到全部容器包括那些已经退出的。这条细节我估计能帮一半以上的新手省掉“我是不是删错东西了”的恐慌。3.2 镜像和容器的基础管理命令掌握了上面几个命令已经能应付大量日常场景了。我把最常用的命令整理成一张速查表操作命令说明查看本地镜像docker images列出所有已下载的镜像关注TAG列查看运行中的容器docker ps只看正在运行的实例查看所有容器docker ps -a包括已退出的容器删除容器docker rm 容器ID删除前需要先停止容器删除镜像docker rmi 镜像名如果有容器还在用它会报错查看容器详情docker inspect 容器ID输出JSON格式的完整配置查看容器日志docker logs 容器ID排查容器内部问题非常有用这里有个容易踩坑的细节删除镜像前必须确保没有未删除的容器还在引用它哪怕是已退出的容器也不行。遇到image is being used by stopped container的报错时用docker rm 那个容器的ID先清掉容器再删镜像就可以了。3.3 跑一个真正的服务Nginx实战看完了Hello World跑一个实际的服务会让你对Docker的威力有更直观的感受。我会以Nginx为例它是最经典的入门服务。第一次跑的时候不妨先想一个问题容器里的Nginx监听了80端口我们怎么访问它答案是端口映射。宿主机上的流量不能直接进到容器里需要把宿主机的某个端口映射到容器的80端口上用-p参数指定docker run -d --name my-web -p 8080:80 nginx拆解一下这条命令-d表示后台运行--name my-web给容器起个名字方便管理-p 8080:80把宿主机的8080端口映射到容器内的80端口。执行完浏览器打开http://localhost:8080应该能看到Nginx的默认欢迎页。如果想验证容器之间确实隔离可以再启动一个Nginx映射到8081端口docker run -d --name my-web-2 -p 8081:80 nginx两个Nginx在同一个宿主机的不同端口上跑互不影响这就体现了容器的隔离能力。用完把容器停掉并删除别让它一直占着资源docker stop my-web my-web-2 docker rm my-web my-web-23.4 进到容器里面看看有时候需要进入容器内排查问题或者执行一些命令比如看目录结构。用exec命令可以进到运行中的容器里开启一个交互式终端docker exec -it my-web bash-it是-i和-t的组合含义是保持标准输入打开并分配一个伪终端。进入之后你就是一个运行在容器里的root用户可以执行ls、cat之类的命令。注意这里和虚拟机里的登录完全不同——容器里没有systemd没有init进程很多在宿主机上习惯的服务管理命令都用不了。出来的时候直接输入exit即可容器会继续运行因为主进程并没有停止。如果误用了docker exec -it my-web nginx这类把容器主进程杀掉的操作容器就会退出新手操作时留意一下。4. 再进一步用Dockerfile定制自己的镜像4.1 一个最小可用的Dockerfile光会用现成镜像不算真正的Docker入门至少得会写一个简单的 Dockerfile。下面这个例子是我用来配合文章演示的一个用Python标准库写的最小HTTP服务不需要额外安装任何依赖包。目录下两个文件一个是应用代码app.py一个是构建脚本Dockerfile。先看app.py用http.server内置模块返回一行文字from http.server import HTTPServer, BaseHTTPRequestHandler class Handler(BaseHTTPRequestHandler): def do_GET(self): body bHello from container self.send_response(200) self.send_header(Content-Length, str(len(body))) self.end_headers() self.wfile.write(body) HTTPServer((0.0.0.0, 8000), Handler).serve_forever()再看DockerfileFROM python:3.12-slim WORKDIR /app COPY app.py . EXPOSE 8000 CMD [python, app.py]逐行解释一下。FROM指定基础镜像python:3.12-slim是官方Python运行时的一个精简版体积比完整版小不少。WORKDIR设置容器内的工作目录后续命令都会在这个目录下执行。COPY把本地的app.py复制进镜像里。EXPOSE只是声明容器内的服务端口相当于文档作用真正发布端口还是在docker run时用-p指定。CMD定义容器启动时执行的命令注意它是JSON数组格式这种写法不会经过shell解析可以避免一些引号转义的坑。4.2 构建镜像和发布端口在 Dockerfile 所在目录执行docker build -t my-hello:1.0 .-t指定镜像名字和标签my-hello:1.0就是镜像名加版本号。最后的点表示构建上下文路径也就是把哪个目录的内容送给守护进程作为构建上下文。这里有一个新手容易犯的错误把整个项目根目录作为上下文传到守护进程连目录下的视频、压缩包也被一起带着走构建又慢又占空间。解决办法稍后说。构建完成后用自己写的镜像启动容器docker run -d --name my-hello-app -p 8000:8000 my-hello:1.0 curl http://localhost:8000看到Hello from container就说明你自定义的镜像已经能正常工作了。4.3 构建缓存和镜像瘦身的方向多次构建同一个项目时Docker会使用层缓存只要某一行没有变化就不会重复执行。这既是效率工具也容易造成困惑——明明改了代码重新构建却还是旧版本在跑很可能就是COPY没感知到文件变化或者缓存机制导致的。要避免缓存带来的困惑有几个实操经验把不经常变化的依赖安装步骤放在Dockerfile靠前的位置把频繁改动的代码复制放在后面这样能最大化利用缓存。加.dockerignore文件和.gitignore类似在里面写下不需要进入构建上下文的目录比如__pycache__、.git、node_modules能从源头上减少发送给守护进程的数据量。尽量用精简基础镜像python:3.12-slim比python:3.12小很多alpine系列更小但需要注意部分软件包在精简系统上编译会比较麻烦。镜像体积直接影响拉取和启动速度。一个被我反复使用的优化套路是同一层里通过连接多条命令并在最后清理不需要的缓存文件从而避免产生多余的镜像层。更进阶的多阶段构建等真正需要的时候再深入研究也不迟。5. 容器里的数据怎么办5.1 一个需要时刻记在心里的认知容器不持久容器是“易碎品”。我之前见过有人在一个正在运行的容器里用docker exec往里面存配置文件然后重启容器数据全没了跑来问怎么回事。原因很简单容器本身是可丢弃的重新创建、重新启动之后它用的还是镜像里那份原始文件系统你在运行时写入的任何内容都会消失。这其实是一个设计特性它逼迫你把“应用状态”和“应用本身”分开。代码写在镜像里数据放到容器外面容器就可以随时被销毁、重建、迁移而不担心丢失数据。5.2 数据卷把数据放到容器外Docker给出的方案是数据卷Volume。它本质上是在宿主机文件系统上开辟一块独立区域挂载到容器内的指定路径。容器对挂载目录的读写都会直接落在宿主机上所以删除容器不影响数据。数据卷分三种常见形式我整理一下区别类型挂载方式适合场景匿名卷docker run -v /容器内路径 镜像不需要指定宿主机位置由Docker管理具名卷docker run -v 卷名:/容器内路径 镜像需要固定名字方便多个容器共享数据绑定挂载docker run -v /宿主机路径:/容器内路径 镜像直接映射宿主机某个目录调试最直观5.3 让容器里的文件能直接被宿主机修改继续拿Nginx举例。如果想把容器里的默认网页目录映射到宿主机的一个目录让我改宿主机的文件就能直接生效可以用绑定挂载docker run -d --name web-mounted -p 8082:80 -v /home/我自己的网页目录:/usr/share/nginx/html nginx接着在宿主机那个目录里新建一个index.html内容随便写点什么刷新浏览器就能看到新内容。这种方式在本地开发调试时非常顺手改完代码不用重新构建镜像容器里代码和宿主机实时同步。关于数据最后提醒一个常见误区别把数据库的数据文件放在容器内部而无脑跑。数据库状态必须保存到具名卷或者绑定挂载里否则容器一删数据没了。我身边已经不止一个人吃过这个亏。6. 常见问题与排查技巧实录6.1 报错“Cannot connect to the Docker daemon”这个报错我在各种初学者群里见过无数次原因通常有两个。一是守护进程没启动尤其是在Linux用systemd管理的环境里安装后忘了执行systemctl start docker二是当前用户不在docker用户组里客户端访问守护进程时权限不足。第二个问题的解法是把当前用户加入docker组然后重新登录sudo usermod -aG docker 你的用户名注意加入docker组相当于把系统的root权限授权给了这个用户因为docker本身的能力就是执行任意根级操作。生产环境要非常谨慎地管理哪些用户能进docker组个人学习机无所谓但要有这个安全意识。6.2 拉取镜像速度慢或者超时如果网络状况不佳镜像拉取可能慢到让人抓狂。最直接的解决办法是给守护进程配置一张加速用的镜像站列表。在/etc/docker/daemon.json里写入下面的内容{ registry-mirrors: [https://docker.mirrors.example.com] }保存之后重启Docker服务再拉镜像就有明显改善。需要注意的是不同加速站点质量和稳定性差别很大建议多准备几个备选选延迟最低且下载速度稳定的。6.3 端口被占用宿主机和容器端口是两套体系运行docker run -p 8080:80时如果报bind: address already in use说明宿主机的8080端口已经被占用。宿主机端口和容器端口是两套独立体系容器内80端口随便用宿主机上每个端口却有且只能绑定一个进程。处理方法很简单换一个宿主机端口比如-p 8090:80。或者找到占用进程并停掉用sudo lsof -i :8080查看是哪个进程占用。6.4 容器启动后立刻退出这个问题的排查路径很固定。先用docker ps -a找到退出的容器ID再看日志docker logs 容器ID日志通常能直接说明失败原因比如应用启动报错、命令不存在、端口冲突等。还有一个新手非常容易踩的点把CMD写成后台启动命令比如CMD [python, app.py]写成CMD [sh, -c, python app.py ]。容器的主进程一旦退出或转为后台执行容器就会立刻停止运行。容器要维持运行前台必须有一个一直挂着的进程。6.5 容器时间不对和日志无限增长容器默认使用UTC时区所以日志和文件时间会与本地时间相差8小时。解决方式之一是在运行容器时通过-e TZAsia/Shanghai传入时区环境变量许多官方镜像都能自动识别。日志无限增长也是个隐藏很深的坑。如果某个容器疯狂打日志宿主机磁盘会被写满。建议在/etc/docker/daemon.json里加一段日志轮转配置{ log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 } }意思是单个日志文件最大10MB最多保留3个文件超出后自动轮转清理。这一行配置能避免绝大多数的“云主机磁盘突然被占满”事故。结尾真要说有什么心得我觉得Docker入门最大的门槛不在命令本身而在思维方式。刚接触时我老把容器当虚拟机维护升级依赖、改配置都在容器里操作结果每次重新创建就回到解放前。后来想通了容器是一次性的镜像才是资产状态放到数据卷代码固化进镜像动态的东西交给外部存储。当你的操作习惯换成这套思路很多之前“玄学”的问题一下就顺了。另一个建议是别急着背命令把docker run、docker build、docker exec、docker logs这几条反复用熟再配合docker inspect查询细节足够覆盖日常80%的需求。剩下的边用边查文档慢慢就熟了。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑