资讯动态

Docker入门到实践:镜像、容器与部署核心概念详解

发布时间:2026/10/15 17:57:10 来源:尧图企业网站定制
“初识Docker”这个标题看起来像个入门笔记但真正上手之后你会发现Docker这玩意儿解决的是所有开发者都绕不开的一个老问题“在我机器上跑得好好的怎么到你这儿就不行了”环境不一致、依赖冲突、部署流程繁琐这些问题只要你做过一次正经的部署就一定踩过坑。Docker的核心思路其实特别朴素把应用连同它的运行环境一起打包成一个标准化的“集装箱”无论这个箱子被搬到哪台机器上里面跑起来的东西都一模一样。本文不打算写得像官方文档那样又长又全而是从一个实际使用者的角度把镜像、容器、仓库这些核心概念讲明白再带你亲手跑通第一个容器、写出第一份Dockerfile最后把我在实际使用中踩过的坑和排查思路一并整理出来。如果你正准备学Docker或者学了一堆理论还没动手这篇文章应该能帮你把这条线串起来。1. 先弄明白Docker到底解决了什么问题很多新手上来就敲命令敲完也没搞懂自己在干什么过几天全忘了。所以我一直建议学Docker之前先想清楚它为什么存在后面那些命令自然就好理解了。1.1 环境不一致的痛做过部署的都懂我最早被Docker圈粉是因为一次项目上线。当时本地开发一切正常代码提交后部署到测试服务器直接报依赖缺失好不容易装好了依赖又发现服务器上的基础库版本和本地不一样程序能编译但运行时行为有点怪。后来同事帮忙排查才发现是环境变量、系统时区、字符集这些“看不见的东西”在作怪。这种问题的根源在于传统部署方式里应用和运行环境是分离的。代码是你的但环境是机器“自带”的每台机器的基础环境都可能差异巨大。Docker的思路是换个角度——把应用、配置、依赖、基础操作系统全部塞进一个镜像里然后以容器的方式运行。容器里的进程看到的操作系统、库文件、环境变量完完全全来自镜像本身和宿主机关系不大。打个比方传统部署像搬家时只搬家具到了新房子才发现门框尺寸不对、插座位置不对Docker则是把整间屋子打包搬走里面是什么样到了哪儿还是什么样。这个“环境一致性”就是Docker最核心的价值。1.2 和虚拟机比Docker强在哪接触过虚拟机的同学可能会说虚拟机也能做到环境隔离啊为什么还要用Docker这个问题当年我也困惑过。简单来说虚拟机是硬件级别的隔离每个虚拟机里都要装一个完整的操作系统启动要几分钟资源占用动辄几个G而Docker是操作系统级别的隔离多个容器共享宿主机的操作系统内核各自只运行需要的进程启动时间秒级资源开销小得多。我看过一张对比表印象很深一个虚拟机镜像动不动几个G而一个精简的Docker镜像可能只有几十M。同样是跑一个Nginx虚拟机要分配固定内存和CPUDocker则按需使用。当然虚拟机在隔离安全性上更彻底两者定位不同但日常开发和微服务场景下Docker的轻量、灵活和快速扩容是虚拟机比不了的。这里要特意说明我讲Docker强在轻量和快速不代表虚拟机没用。生产环境里很多人会把两者结合用——宿主机上装虚拟机做隔离虚拟机里再跑Docker做应用编排。不要把技术当成单选题关键是理解每种工具擅长解决什么问题。2. 镜像、容器、仓库这三个概念搞懂Docker你就入门一半Docker的文档铺开来看概念一大堆但核心其实就是三个词镜像Image、容器Container和仓库Repository。我在带新人时经常说把这三个概念用生活里的例子想通后面学啥都快。2.1 镜像打包好的“标准包裹”镜像可以理解成一个只读的、分层打包的模板。它里面包含了运行某个应用所需的一切基础操作系统文件、代码、运行时环境、依赖库、配置文件。你可以把镜像想象成一个“标准包裹”里面的内容在任何支持Docker的机器上拆开表现完全一致。每个镜像是由一层一层的文件系统叠加而成的这一层每一层都是只读的。这种分层设计带来一个巨大的好处多个镜像可以共享底层的基础层比如你拉取不同版本的Nginx镜像它们底层的某个基础镜像层可能是同一个Docker会自动复用不用重复下载。镜像层被设计成可复用的“乐高积木”也是镜像能比虚拟机镜像小很多的原因之一。平时我们常说的“拉镜像”就是从远程仓库把镜像下载到本地。你执行docker images就能看到本地已有的镜像列表包括镜像名、标签、镜像ID和大小。标签Tag特别重要比如nginx:latest和nginx:1.25前者是最新版本后者是固定版本。生产环境里我强烈建议不要用latest而是锁定到一个确定的版本号否则某天拉取后基础组件升级可能导致运行行为变化。2.2 容器跑起来的镜像实例如果镜像是“标准包裹”那容器就是“包裹被拆开并运行起来的状态”。容器是一个进程级的隔离环境有自己的文件系统、网络、进程空间但它本质上还是宿主机上的进程只不过借助Docker的隔离能力在独立空间里运行。容器和镜像的关系最常用的类比是“类和实例”你可以由一个镜像启动多个容器每个容器之间互不影响。同一个Nginx镜像启动3个容器分别映射到宿主机不同的端口就相当于在同一个系统里跑3个互不干扰的Web服务。这也是Docker在做多环境、多服务部署时特别方便的原因。比较关键的一点是容器可以被启动、停止、删除但容器的写入层是临时的。容器被删除后容器内产生的文件、数据默认都会丢失。这个问题很多人第一次踩坑时没注意后面我会专门讲数据卷怎么解决。2.3 仓库镜像的“网盘”与“分发站”仓库是存放和分发镜像的地方。个人电脑上是一堆本地镜像而大家共享的镜像主要存在远程仓库里。官方公共仓库就是大家最常用的Docker Hub里面有大量官方维护的基础镜像比如nginx、redis、mongo这些基本能覆盖常见的开发需求。仓库还分为公开仓库和私有仓库。公司内部通常会架设一套私有仓库存放自己构建的应用镜像避免外部网络依赖也便于权限管理。推送和拉取镜像的命令也很直观docker push把本地镜像上传docker pull从仓库下载。这里有个容易混淆的点仓库Repository和注册服务器Registry不是一个概念。注册服务器是存放多个仓库的服务而仓库是某个具体镜像的集合比如Docker Hub是一个Registry里面nginx这个Repository存放了不同标签的nginx镜像。我最初学的时候也绕了一圈搞清楚之后看命令就没那么晕了。3. 实操从零跑起第一个容器概念讲再多不敲一遍命令等于没学。这一节带你把Docker环境准备好亲手动起来第一个容器把最常用的命令过一遍。3.1 安装Docker各平台怎么选Docker的安装方式随系统不同差别还挺大。如果你用的是Linux比如Ubuntu或者CentOS直接用官方源安装docker-ce即可注意不同系统版本的源配置不太一样建议参考官方文档操作不要照搬网上一条命令。如果你用的是macOS推荐安装Docker Desktop。它自带图形界面能管理容器、镜像、卷等资源对新手很友好。Windows也一样Docker Desktop在Windows上需要WSL2支持安装时按提示启用即可。装完之后建议顺手验证一下环境docker version docker info能正常输出版本信息和系统信息说明Docker引擎已经在运行了。如果docker info提示Cannot connect to the Docker daemon多半是Docker服务没启动Linux下可以用systemctl start docker手动拉起来。在Docker Desktop里则要看状态栏图标是否常亮。3.2 第一个容器hello-world与nginx装好Docker之后几乎所有教程的第一条命令都是docker run hello-world这条命令会自动从远程仓库拉取hello-world镜像然后启动一个容器打印一段欢迎信息就退出。这个镜像非常小但足以验证整条链路——拉取镜像、创建容器、启动进程、输出日志——是否正常。看完hello-world我建议马上跑一个有点实际用途的东西。用Nginx起一个Web服务试试这一步能把很多核心概念串起来docker run -d --name my-nginx -p 8080:80 nginx:1.25拆开看这条命令-d表示后台运行--name给容器取个名字叫my-nginx-p 8080:80表示把宿主机的8080端口映射到容器内的80端口nginx:1.25指定镜像和标签。跑完之后在浏览器里访问http://localhost:8080能看到欢迎页就说明成功了。我自己第一次跑通时专门在容器里放了个测试页面然后修改容器内文件再访问宿主机的端口那一刻才真正理解“容器内的端口通过映射暴露给宿主机”是什么意思。建议你也动手试一下比单纯看命令理解深刻得多。3.3 常用命令速查够用就好Docker命令很多但日常开发常用的其实就那么些。我把最常用的整理了一张速查表适合贴在手边目的命令示例说明查看本地镜像docker images显示镜像列表、标签、大小查看运行中容器docker ps加-a可以查看所有容器包括已退出启动容器docker run -d --name app -p 8080:80 nginx后台运行并映射端口进入容器docker exec -it app bash进入容器内的命令行界面查看容器日志docker logs app跟踪日志加-f参数停止容器docker stop app给容器发停止信号删除容器docker rm app先停止再删除或加-f强制删除删除镜像docker rmi nginx:1.25删除本地某个镜像构建镜像docker build -t my-app:1.0 .在当前目录根据Dockerfile构建查看资源占用docker stats实时查看容器CPU、内存等我想特别提醒一件事docker exec -it 是进入容器的常用方式但进入容器后修改的东西默认不会“固化”进镜像。如果你改了容器里的配置然后删除了这个容器改动就全部丢了。正确的做法是把改动写进Dockerfile重新构建镜像或者用数据卷挂载外部配置后面章节会细说。4. 构建自己的镜像把应用装进“集装箱”前面跑别人的镜像只能算“用Docker”。等你要把自己的应用部署到容器里就得学会“构建镜像”——这步才是真正把Docker用起来的标志。4.1 Dockerfile入门FROM、WORKDIR、COPY、RUN构建镜像的核心就是写Dockerfile。简单来说Dockerfile是一个文本文件里面按顺序写了一条条指令Docker引擎会逐条执行最终生成一个镜像。就像做饭时照着菜谱一步步操作每步操作都留下一个可复用的中间结果。先看一个最简单的Dockerfile长什么样FROM alpine:3.18 WORKDIR /app COPY index.html . RUN echo build complete CMD [sh, -c, echo hello from container]逐条解释一下。FROM指定基础镜像alpine是一个很小的Linux发行版适合做基础环境WORKDIR设置工作目录之后的命令默认都在这个目录下执行如果目录不存在会自动创建COPY是把当前构建目录下的index.html复制到镜像的工作目录里RUN是在构建过程中执行命令常用于安装依赖、编译代码CMD是容器启动后默认执行的命令。写Dockerfile时有个常见的坑把本地的一些无关文件也复制进镜像。比如项目里有.git目录、node_modules目录如果COPY整个目录进镜像不仅镜像体积巨大还可能泄露一些不必要的文件。解决办法是写一个.dockerignore文件语法和.gitignore类似把不需要的路径排除掉。4.2 CMD与ENTRYPOINT的区别初学Dockerfile最容易混淆的两个指令就是CMD和ENTRYPOINT。我举个通俗的例子CMD像是“默认的选项”启动容器时可以很方便地覆盖它ENTRYPOINT像是“固定的入口程序”容器启动后一定会执行这个程序而CMD里的内容可以作为参数传给它。实际应用中镜像的类型决定了用哪个指令更多。如果构建的是类似nginx这种“开箱即用”的服务镜像通常CMD就够了如果构建的是自定义的脚本工具希望容器启动后执行固定命令、再接受外部参数那就适合用ENTRYPOINT配合CMD。一个印象很深的例子之前我用一个基础镜像跑定时任务脚本一开始把启动脚本写在CMD里结果在docker run时多传了一个参数后脚本根本没执行。后来改成ENTRYPOINT [python, main.py] CMD [--modedev]这样启动容器时如果不带参数就按默认的--modedev执行带参数则会被追加到python main.py后面。两种指令的定位彻底搞清楚后这类问题就再也没困扰过我。4.3 一个小案例把一个静态站点做成镜像光说理论不够我带你完整做一个静态站点的镜像。假设你有一个包含index.html的静态网站目录想用Nginx来托管它可以这样操作新建一个目录里面放两个文件。第一个是index.html内容随便写点东西。第二个是Dockerfile内容如下FROM nginx:1.25 COPY index.html /usr/share/nginx/html/ EXPOSE 80 CMD [nginx, -g, daemon off;]在目录里执行构建命令docker build -t my-static-site:1.0 .最后的点号表示以当前目录为构建上下文。构建成功后运行docker run -d -p 8080:80 my-static-site:1.0浏览器访问http://localhost:8080看到的就是你写的页面。EXPOSE指令只是声明容器会监听80端口实际能不能从宿主机访问仍然要看运行时有没有-p做映射这点很多新手容易误解。如果想把自定义配置也带进去一般是用COPY把nginx.conf复制到镜像的/etc/nginx/目录里或者用数据卷挂载。改Dockerfile重新构建的好处是配置固化适合一个环境一个配置的情况。5. 数据、网络与运维容器跑起来之后的事容器跑起来只是开始真正麻烦的是接下来的数据和运维问题。这里面的坑几乎每个用过Docker一段时间的人都会踩一遍。5.1 数据卷容器删了数据不能丢前面反复强调过容器的写入层是临时的容器删除后数据就没了。但很多应用产生的重要数据比如数据库文件、上传的图片、日志文件都必须要持久化。解决方案就是数据卷Volume。数据卷的本质是容器和宿主机之间共享的一块存储空间。常见用法有两种一种是把宿主机某个目录直接挂载进容器叫绑定挂载bind mount适合开发时实时映射代码另一种是创建Docker托管的具名卷named volume数据由Docker管理位置对用户透明更适合生产环境。我自己用绑定挂载时最典型的场景是开发阶段把项目代码直接挂载进容器这样改代码不用重新构建镜像容器内立刻能同步到。命令是这样docker run -d --name my-app -v /path/to/code:/app -p 8080:80 my-app-image一个必须注意的细节是权限。如果宿主机当前用户和容器内运行进程的用户不是同一个uid可能会出现容器内“没权限写文件”的报错。我见过不少新手在这一步卡住简单的处理方式是在构建镜像时指定一个和工作目录匹配的用户或者挂载时注意目录权限设置。5.2 端口映射与容器网络容器默认处在一个隔离的网络空间里宿主机访问不到容器内的服务需要做端口映射。前面用的-p 8080:80就是最典型的映射方式含义是宿主机8080端口接收的流量会转发到容器内的80端口。除了简单的端口映射Docker还提供了网络模型可以创建自定义网络并让多个容器通过容器名互相通信。这在微服务和分布式场景里极其重要。举个简单的例子如果你有应用容器和数据库容器与其让应用容器用localhost去连数据库不如把两个容器加入同一个自定义网络应用里直接写数据库的容器名就能访问。Docker的bridge网络是默认的网络类型适合单机多个容器的互联。如果只是本地学习和单机部署掌握默认网络和自定义网络的创建就够了。集群编排那些复杂的网络模型等真的用到的时候再研究也不迟。5.3 容器生命周期管理与日志启动容器的时候给容器加上--restart策略可以指定退出时Docker的行为。比较常用的是--restart unless-stopped意思是除非手动停止否则容器退出后会自动重启。在服务器上跑服务时这个策略特别有用进程崩了能自动拉起来省了很多人工干预。查看日志是排查容器问题的最基本手段。docker logs命令能查看容器标准输出的日志加-f可以实时跟踪。不少你写进程序里的print或console.log其实都进了容器的标准输出日志所以容器化部署时尽量保持日志输出到stdout方便统一采集和管理。还有一个运维细节容易被忽略容器时间默认是UTC时区和本地时间差了8小时。特别是涉及日志时间和定时任务的应用一定要在镜像构建阶段就设置好时区比如在Dockerfile里加ENV TZAsia/Shanghai否则你排查日志时看到的时间错位非常容易误判问题。6. 常见问题与排查技巧实录最后这部分我把实际使用中频率最高的一批问题集中整理一下。这些问题不一定出现在官方文档的醒目位置但几乎每个项目里都可能碰到。6.1 镜像拉取慢、拉不下来怎么办镜像拉取慢可能是新手遇到最多的第一个大问题尤其是第一次拉取比较大的镜像时体验和下载大文件卡住差不多。解决方案之一是配置镜像加速源把docker pull拉取的远端仓库切换成更快的公共镜像站点。修改Docker配置文件里的registry-mirrors字段然后重启Docker服务即可。我要特别说一句加速源只能解决部分网络问题如果你的公司有内部仓库或者你构建的是私有镜像加速源反而会造成奇怪的错误届时需要临时绕过或调整配置。遇到拉取报错时先看错误信息的完整内容不要一上来就重试。6.2 容器秒退、端口起不来怎么查容器启动后立刻退出的原因是多方面的最常见的是容器内的主进程执行完就结束了。比如你的Dockerfile里CMD写的是一个脚本脚本执行完没有常驻进程容器自然就退出了。排查时先看docker logs看看容器退出前打印了什么内容这一步能排除大部分问题。端口起不来的情况通常是宿主机上已经有程序占用了你映射的端口。用docker ps查看容器状态再用ss -lnt或netstat -tlnp查看端口监听情况。如果你映射8080端口发现已经被占用换一个端口或者停掉占用进程即可。容器运行正常但访问不通还要检查防火墙有没有放开相应端口很多“容器起不来”其实是防火墙拦住了。6.3 时区、权限、日志占满磁盘这些“小毛病”除了上面两类大问题还有一些“小毛病”很考验人。容器时区不对就是其中之一解决办法在构建阶段设置TZ环境变量或者挂载宿主机的localtime文件。日志占满磁盘也是常见的隐蔽问题如果容器打印日志特别多长期运行会把磁盘写满。解决思路是在Docker启动参数里加上日志大小限制或者在日志系统层面做轮转。权限问题我前面提过再补充一个实用场景把宿主机目录挂载进容器后容器里创建的文件在宿主机上看到的所有者是root你本地用户可能没法直接删除。这种情况下用unprivileged用户运行容器或者在删除时加上sudo具体看你的系统配置。问题不大但第一次遇到时确实会愣一下。7. 写在结尾Docker给我最大的启发我个人在使用Docker中最深的体会是它并没有发明什么全新的技术而是把操作系统已有的隔离、分层、网络能力组合成了一个非常顺手的工具。就像集装箱不是新发明的运输方式但它标准化了货物运输的流程彻底改变了整个行业。对新手来说学Docker不需要一上来就把所有命令背全也不需要急着研究集群编排和复杂的网络模型。先把镜像、容器、仓库三个概念吃透亲手跑一个服务再把自己的应用做成一个镜像最后搞清楚数据怎么持久化这条线走完你就已经超过很多“只会docker run”的人了。最后再分享一个小技巧如果你在服务器上长期跑容器记得定期执行docker image prune清理没有用的悬空镜像以及docker container prune清理已经退出的容器。服务器磁盘空间宝贵这个小习惯能避免很多“磁盘莫名满了”的麻烦。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑