首页 / 资讯中心 / 文章详情

Docker入门:镜像、容器与仓库核心概念一次讲透

Docker入门:镜像、容器与仓库核心概念一次讲透 ★ FEATURED ARTICLE
第一次接触Docker十个人里有八个会先被名字搞懵镜像、容器、仓库、Dockerfile、Compose、Swarm……一堆术语排着队等你认脸。更尴尬的是不少教程一上来就让你敲命令容器确实跑起来了可一问你镜像和容器到底啥关系又答不上来。这篇我就不按那个套路走先花点篇幅把认知框架搭好再用最少的命令带你亲手跑通两个应用最后把新手踩得最多的几个坑一次性排掉。内容定位是入门第一篇完全零基础可以看断断续续用过Docker但概念模糊的朋友也可以当一次概念校准。1. 先搞清楚Docker到底解决了什么问题1.1 你大概率经历过的环境地狱我在带人的时候开场必讲一个场景你在自己电脑上开发了一个网站本地跑得顺风顺水代码一交给同事立刻翻车。这个依赖版本不对那个系统库缺失还有人说我这装的是另一个版本的运行时。你过去一看好家伙光是把开发环境对齐就花了整整一下午。这个现象有一个专门的名字叫环境地狱或者依赖地狱。传统解决方案是虚拟机——装一个完整的操作系统把所有环境配置好然后整包分发。虚拟机能解决问题但代价非常大镜像文件动辄好几个GB启动要等好几分钟对硬盘和内存的占用更是夸张。相当于为了吃一碗泡面专门买了一整套厨房设备吃完还得收拾半天。1.2 Docker的思路只打包应用和它的随身物品Docker的思路完全不一样。它不打包整个操作系统只打包你的应用、应用依赖的运行库、配置文件、环境变量以及启动时需要执行的命令。这些内容被打进一个标准化的集装箱里在任何装有Docker引擎的机器上都能以完全一致的方式运行。打个比方虚拟机是搬家公司把你整个家连家具带装修整体搬过去Docker则是给你一个行李箱应用需要的所有随身物品都装进去走到哪里只要有统一的行李箱支架就能随时打开用。这个行李箱支架就是Docker引擎。因为只打包应用和必要依赖Docker镜像通常只有几十到几百MB启动快资源占用小分发也方便。1.3 镜像、容器、仓库三个必须分清的词很多新手死磕命令之前先被这三个词绕晕了。我平时用的对照是这样的概念通俗类比一句话说明镜像Image安装光盘/模板只读的、打包好的应用快照包含代码、环境和配置容器Container运行中的程序实例镜像运行起来后的实体可以启动、停止、删除仓库Registry应用商店集中存放镜像的地方最常用的是Docker Hub三者的关系最标准的理解是镜像是一个静态模板容器是模板运行出来的动态实例。一个镜像可以同时启动多个容器就像一套火锅底料可以煮出好几锅火锅你改了容器里的文件不会改动底层的镜像你删掉容器镜像还在本地随时能再启动一个。提示镜像本身是分层的、只读的运行容器时会额外叠加一层可写的容器层。这就是容器里改了文件但镜像不受影响的根本原因。2. 把核心概念用大白话再拆一遍2.1 镜像就是安装包但它是分层安装的你可以把镜像理解成一个特殊的安装包但它不是一次性把文件全塞进去而是一层一层叠起来的。每个构建步骤生成一层这一层只记录和上一层的差异。这样设计有两个好处一是拉取镜像时如果本地已经有一部分层只需要下载缺失的层二是多个镜像可以共用底层节省磁盘空间。比如你基于同一个基础镜像构建了十几个应用这些应用共享同一个底层磁盘上只会存一份底层数据。这就是为什么Docker镜像能在几百MB的体量内装下完整的应用环境而传统虚拟机动不动就是几GB。2.2 容器就是一个被隔离的进程千万别把容器想象成轻量虚拟机虽然行业里为了科普经常这么说但严格来讲并不准确。容器的本质是一个运行在宿主机上的进程只不过这个进程拥有自己独立的文件系统、网络栈、进程空间。它看到的世界是镜像打包好的内容而不是宿主机完整的环境。正因为容器本质上只是进程所以它启动极快——不需要引导操作系统的过程。这也是Docker和虚拟机体验上最明显的差异虚拟机是分钟级启动容器是秒级、甚至毫秒级启动。2.3 仓库、镜像名和版本标签从仓库拉取镜像时你看到的完整写法通常长这样docker pull nginx:1.25-alpine其中nginx是镜像名1.25-alpine是标签也就是版本号。镜像名前面还可以加仓库地址比如docker.io/library/nginx不过默认情况下Docker会自动补全所以日常使用中只写镜像名加标签就够了。新手最容易踩的坑是不加标签。比如docker pull nginx拉到的其实是最新的官方镜像但谁的电脑里存的是哪个版本无法保证。团队协作或生产环境里不写明确标签等于埋雷。我个人的习惯是任何镜像都要带标签哪怕写nginx:latest也要明确这是你刻意选择的。2.4 容器和虚拟机的对比把两者的差异摊开看会更直观对比项虚拟机容器隔离级别硬件级虚拟化操作系统进程级隔离资源占用高每个VM一套完整系统低共用宿主机内核启动速度分钟级秒级镜像大小通常数GB通常几十MB到几百MB一致性依赖虚拟机模板的维护镜像即环境天然一致这里要注意容器因为共享宿主机内核所以内核相关的隔离能力比虚拟机弱一些。对绝大多数应用场景来说完全够用但如果你的需求涉及强隔离或运行不同内核的系统那还是得考虑虚拟机方案。工具没有绝对的好坏只有适不适合。3. 环境搭建与第一个容器3.1 不同平台的安装方式安装Docker这件事不同平台差别很大我说一下最常见的三种情况。Linux服务器上装Docker最简单用官方脚本一条命令搞定。装完后记得把当前用户加入docker用户组不然每条命令都要加sudo新人在这里卡得最多。Windows安装Docker Desktop它会自动帮你配置好底层虚拟化环境。新版默认使用WSL2后端体验比较顺滑缺点是比较吃内存建议电脑至少16GB内存再开。macOS同样装Docker Desktop分Intel芯片和Apple芯片两种安装包一定要选对型号选错了装完跑不起来。3.2 验证安装是否成功装完以后打开终端执行docker version能同时看到Client和Server两段版本信息说明安装成功Docker守护进程已经正常启动了。如果只看到Client、看不到Server多半是Docker Desktop没启动或者服务没起来。再执行一条docker info能看到容器数量、镜像数量、存储驱动等系统信息。这条命令也是排查环境异常的第一道工具。3.3 跑起你的第一个容器验证环境没问题后来跑第一个容器docker run hello-world这条命令会经历一个完整流程本机没有hello-world镜像于是Docker自动去仓库拉取然后创建容器、运行程序最后程序输出一段欢迎信息后退出。别看它简单它用最短的路径演示了拉取镜像—创建容器—运行程序的完整链路。看到输出后用docker ps -a查看一下你会发现刚才的容器状态是Exited。这很正常hello-world这个镜像的任务就是打印一段信息然后退出它本来就不是常驻服务。理解这一点很重要容器的生命周期取决于容器内的主进程主进程退出容器随之结束。3.4 镜像拉取失败的常见原因第一次拉镜像很可能遇到超时或者拉不动。常见原因有三个网络波动、镜像仓库地址访问慢、DNS解析异常。最直接的验证方式是换一个网络环境再试一次。如果网络本身没问题但就是慢可以考虑配置镜像加速器。它的原理很简单Docker拉镜像时会先去配置好的镜像仓库下载而不是每次都从默认的海外仓库拉取。配置方式是在Docker的配置文件中添加registry-mirrors字段然后重启Docker服务。不同平台配置文件路径略有差异Linux下通常是/etc/docker/daemon.json。注意配置镜像加速器只能解决拉取慢的问题不能解决镜像不存在或网络完全不通的问题。如果换了好几个加速器都拉不动重点检查宿主机的网络连通性和代理设置。4. 实操用Nginx跑通第一个Web服务4.1 拉取并启动Nginx光跑hello-world还不够有感觉我们直接上一个真实场景用Nginx跑一个Web服务。先把镜像拉到本地docker pull nginx:latest然后启动容器docker run -d -p 8080:80 --name web-demo nginx:latest命令拆开看-d表示后台运行-p 8080:80是端口映射--name web-demo给容器起个名字最后是镜像名。执行完以后浏览器访问http://localhost:8080就能看到Nginx的默认欢迎页。第一次看到页面出来的时候那种我把一个服务装进了集装箱的实感会很强烈。这个例子的意义在于你只用了不到十秒钟就部署了一个完整的Web服务器。放在以前你得先装Nginx、配服务、处理端口防火墙没有十分钟下不来。4.2 端口映射到底是怎么一回事很多人第一次看到-p 8080:80会犯迷糊为什么是8080为什么又是80这里要讲清楚一个关键认知容器内部有一套自己的网络空间和宿主机是隔离的。Nginx在容器内部监听的是80端口但你在宿主机上并不能直接访问容器内部的80需要通过端口映射把宿主机的某个端口桥接到容器的80端口。写法的规则是宿主机端口:容器端口。访问http://localhost:8080时请求先进到宿主机的8080端口再由Docker转发给容器的80端口。右侧的80端口基本不用改它取决于应用实际监听的端口左侧的8080可以随意选只要不跟宿主机已有端口冲突。技巧本地调试时左侧端口默认习惯用8080、8081、3000这类好记的端口。但要记住左侧端口本质上是宿主机资源同一台机器上不能有两个容器同时占用同一个左侧端口。4.3 常用容器生命周期命令容器跑起来后你马上会用到这些命令。我把它们分组整理# 查看运行中的容器 docker ps # 查看所有容器包括已停止的 docker ps -a # 停止容器 docker stop web-demo # 启动已停止的容器 docker start web-demo # 重启容器 docker restart web-demo # 删除容器必须先停止 docker rm web-demo # 强制删除运行中的容器 docker rm -f web-demo其中docker rm -f是新手的后悔药它直接终止并删除正在运行的容器。但要注意-f是不可逆的容器一旦删除里面产生的数据就没了。后面我会单独强调数据持久化的问题这里先记住这个风险点。4.4 进入容器内部看一看容器是隔离的怎么查看容器内部的情况用docker execdocker exec -it web-demo /bin/bash这个命令的意思是在web-demo容器里执行/bin/bash-it表示交互式终端。进去以后你会发现自己变成了一个缩小版的Linux环境里面只有Nginx运行所需的文件和工具。可以看看熟悉的目录结构ls /usr/share/nginx/html/ cat /etc/nginx/nginx.conf看完用exit退出。这个命令在排查问题时非常重要因为很多问题需要进容器内部才能定位。4.5 容器删除后数据去哪了再强调一次容器删除后容器层里的所有数据都会消失。这包括你写入的文件、安装的软件、产生的日志。这一点和传统服务器差异很大传统服务器上文件写在硬盘里删了进程数据还在容器里进程和数据是绑在一起的。解决思路是使用数据卷Volume把数据挂载到宿主机上让容器和数据的生命周期解耦。数据卷的具体用法我会在入门第二篇详细展开这里先建立一个意识凡是需要长期保存的数据都不能只放在容器内部。5. 用Dockerfile构建自己的第一个镜像5.1 准备一个最简单的前端页面拉取现成镜像只是第一步真正体现出Docker威力的是构建自定义镜像。我们做一个最简单的场景把自己写的静态页面打包成镜像。先创建一个项目目录写一个index.htmlmkdir docker-demo cd docker-demo!DOCTYPE html html head meta charsetutf-8 title我的第一个Docker镜像/title /head body h1Hello, Docker!/h1 p这个页面跑在容器里。/p /body /html5.2 编写Dockerfile接下来创建一个文件叫Dockerfile这就是构建镜像的配方。请记住Dockerfile的文件名必须是DockerfileDocker会默认在构建目录里找它。FROM nginx:latest COPY index.html /usr/share/nginx/html/ EXPOSE 80三行指令逐一说清楚FROM指定基础镜像。你的镜像要基于哪个镜像来构建这里用的是官方Nginx镜像。COPY把宿主机当前目录下的index.html复制到镜像内的指定路径。这个路径就是Nginx存放网站文件的默认目录。EXPOSE声明容器内应用监听的端口。注意它只起声明作用真正让外部访问还是要靠运行时的-p映射。5.3 构建并运行在docker-demo目录下执行docker build -t my-web:v1 .-t my-web:v1给镜像命名并打上版本标签末尾的.表示使用当前目录作为构建上下文。构建完成后查看一下镜像docker images能看到my-web:v1出现。然后启动它docker run -d -p 8081:80 --name my-demo my-web:v1浏览器访问http://localhost:8081就能看到你自己写的页面了。从写文件到页面上线整个过程不超过两分钟。这就是容器化部署最直观的体验环境配置跟着镜像走代码交付即环境交付。5.4 常用Dockerfile指令补充入门阶段先认识下面这组指令就够用了指令作用示例FROM指定基础镜像FROM node:18RUN在构建时执行命令RUN apt-get update apt-get install -y curlCOPY复制宿主机文件到镜像COPY app.py /app/app.pyADD复制文件支持解压和远程URLADD package.tar.gz /app/WORKDIR设置工作目录WORKDIR /appENV设置环境变量ENV APP_PORT3000CMD指定容器启动时执行的命令CMD [python, app.py]EXPOSE声明暴露端口EXPOSE 80这里还需要提醒一个细节RUN和CMD形似但完全不同。RUN是构建镜像时执行的它的结果会写入镜像层CMD是容器启动时执行的它不写入镜像层。搞混这两者经常导致构建时明明没问题一运行容器就秒退的情况。6. 新手必经的坑与排查思路6.1 端口被占用启动容器时报错提示driver failed programming external connectivity on endpoint这就是端口冲突。排查方法很简单先看已经有谁占用了端口docker ps如果宿主机的8081被其他容器占了换成新端口重新启动即可。如果宿主机的端口被非Docker进程占了找到那个进程并处理或者干脆换端口。我个人的习惯是开发环境多试几个高位端口避开常见的8080、9090。6.2 拉取镜像失败或超时报错信息通常是dial tcp ... connect: connection timed out。第一步先确认网络是否通最简单的办法是执行docker pull时换一个小镜像试试。如果小镜像能拉下来说明网络没问题问题出在特定镜像上。第二步检查是否配置了加速器。加速器配置完记得重启Docker服务否则不生效。如果配置后仍然拉不动很可能加速器节点本身不稳定换一个再试。千万不要一次配置多个加速器配置项是数组但服务端只会按顺序尝试配置过多反而会拖慢首轮查找。6.3 容器启动后立刻退出这是新人遇到最多的诡异现象容器一启动就退出了docker ps里根本看不到只能用docker ps -a看到状态是Exited。遇到这种情况第一反应一定要是查日志。执行docker logs container_name日志里会直接告诉你原因。绝大多数情况下问题出在启动命令上。容器内部没有常驻前台进程主进程跑完就退出容器自然就结束了。比如镜像是nginx但你用CMD echo hello覆盖了它的默认启动命令echo输完就退出容器也就退出了。记住一个黄金法则让容器进程保持前台运行不要用类似nohup ... 的方式把主进程放到后台。容器是为前台进程设计的任何让主进程后台化的操作都会导致容器秒退。6.4 日志查看与进入容器日志是排查一切问题的第一现场# 查看全部日志 docker logs container_name # 实时跟随日志输出 docker logs -f container_name # 只看最后100行 docker logs --tail 100 container_name如果日志看不出问题再进容器内部排查docker exec -it container_name /bin/bash进容器后先看进程列表ps aux再看应用配置文件路径是否正确。这套组合拳能解决至少80%的入门阶段问题。6.5 入门阶段命令速查表最后给你一张速查表遇到操作不确定时拿出来看一眼想做的事命令拉取镜像docker pull 镜像名:标签查看本地镜像docker images运行容器docker run -d -p 宿主机端口:容器端口 --name 名字 镜像名:标签查看运行中容器docker ps查看所有容器docker ps -a停止容器docker stop 名字删除容器docker rm 名字或docker rm -f 名字查看容器日志docker logs 名字进入容器docker exec -it 名字 /bin/bash查看镜像构建历史docker history 镜像名结尾说点实在的。我带过不少新手发现入门阶段最大的障碍其实不是命令而是心智模型。只要想清楚镜像是一个只读模板容器是模板的运行实例仓库是镜像的存储库这三者关系后面学什么都是一马平川。这篇教程写的所有操作我都建议你亲手敲一遍尤其是构建自定义镜像那部分哪怕只是改一行标题文字也会让你对镜像即环境这句话有完全不同的体会。入门第一篇到这里就收尾了。数据卷、网络模式、Compose多容器编排这些内容会在后面的教程里逐个展开。你先把今天这篇里的命令跑熟把概念理顺后续内容上手会快得多。
阅读完成 · 觉得有帮助?
咨询建站