服务搭建 04|Docker 入门:与传统部署对照,为什么运维都在转向容器

发布时间:2026/10/14 3:30:46

服务搭建 04|Docker 入门:与传统部署对照,为什么运维都在转向容器 一个 Python 小工具在我机器上跑得好好的。发给同事——ModuleNotFoundError。装上缺的包再发——版本不对。好不容易把版本对齐了换第三台机器又崩。那时候我的「部署文档」是一份二十来行的 README装 Python、装这几个包、改这个路径、注意那个版本……每次部署都像重新赌一次赌赢了是运气。后来第一次用 Docker心里只有一句话为什么没人早点告诉我。这篇不追新概念就把「容器到底是什么、和我直接装有什么不一样、我怎么把自己的东西装进去」讲清楚。〇、先花两分钟三个词把容器说明白Docker 的世界里只有三个核心词先把它们分清词是什么类比镜像 image一个「装好环境的系统快照」应用 依赖 配置全打包在一起一张光盘 / 一个安装包容器 container镜像跑起来之后的那个实例用光盘启动起来、正在运行的系统仓库 registry放镜像的地方默认是 Docker Hub应用商店一个镜像可以起很多个容器容器之间互不影响。删掉容器镜像还在删掉镜像容器也起不来了。然后是和虚拟机的关系——这是最大的误解来源虚拟机容器隔离方式完整的操作系统自带内核共享宿主机内核只隔离进程和文件系统启动时间几十秒秒级甚至毫秒体积几个 GB几十 MB隔离强度强内核级弱同一内核数量一台跑几个一台跑几十上百个一句话记住容器不是「小虚拟机」它是「被隔离起来的一个进程」。之所以轻、快代价就是隔离没虚拟机那么硬——它是便利工具不是安全边界。别拿容器当沙箱跑不可信的代码。图示虚拟机每台自带完整 OS 内核vs 容器共享宿主内核只隔离进程的层次对照一、安装练手的话用系统仓库那个就够Ubuntu 上有两个来源方式命令特点系统仓库版sudo apt install -y docker.io简单够用版本稍旧官方源版加源后装docker-ce版本新多几步配源家用、练手、学习装docker.io就行一句区别官方版主要赢在版本新功能没差到影响入门。sudo apt install -y docker.io sudo systemctl enable --now docker docker --version sudo docker run hello-world最后那条会拉一个测试镜像跑一下看到Hello from Docker!就说明整条链路通了。我这台机器从 Ubuntu 26.04 仓库里装到的版本是Docker version 29.1.3, build 29.1.3-0ubuntu4.1——和自己加官方源装的docker-ce相比版本号确实会旧一些但入门完全够用。不过国内环境下这一步常常先给你一闷棍。我实测时docker run hello-world直接失败Unable to find image hello-world:latest locally docker: Error response from daemon: failed to resolve reference docker.io/library/hello-world:latest: failed to do request: Head https://registry-1.docker.io/v2/library/hello-world/manifests/latest: dial tcp 103.228.130.27:443: connect: connection refused这不是 Docker 装坏了是连不上 Docker Hub 的仓库registry-1.docker.io服务装好了、在跑、socket 也在唯独镜像拉不回来。怎么确认是网络问题而不是安装问题docker --version能正常回话、systemctl status docker是 active就说明 Docker 本体没毛病去配下面的镜像加速或者换个能连通的源。接下来是第一个必踩的权限坑。直接敲docker ps多半报permission denied while trying to connect to the docker API at unix:///var/run/docker.sock顺带记一个版本差异旧版 Docker 这句话写的是Docker daemon socket新版我实测的 29.x改成了docker API。措辞变了意思和修法一模一样——看到docker API别以为自己装错了东西。再看一眼那个 socket 文件本身的权限一眼就懂srw-rw---- 1 root docker 0 Oct 12 11:16 /var/run/docker.socks开头说明它是 socket 文件不是普通文件属主root、属组docker权限rw-rw----的意思是只有 root 和 docker 组成员能读写——你不在docker组里可不就 permission denied 么。原因/var/run/docker.sock只有 root 和docker组能读写而你不在 docker 组里。修法sudo usermod -aG docker $USER # 然后重新登录退出再进或重开一个终端 id | grep docker # 看到 docker 组才算生效注意这里有个坑组变更只对新会话生效当前这个终端里怎么试都还是报错——所以必须重新登录和用户权限那篇里usermod -G少写-a是同类坑。另一个提醒进了 docker 组≈有了 root 权限这是公认的取舍。国内拉镜像慢的话配个镜像加速/etc/docker/daemon.json{ registry-mirrors: [https://docker.m.daocloud.io] }改完sudo systemctl restart docker。加速站换得勤以当时能用的为准。二、六个命令先跑通命令作用docker pull nginx拉镜像从仓库下载到本地docker images看本地有哪些镜像docker run ...用镜像启动容器最核心的一个docker ps -a看容器加了-a才能看到已退出的docker logs 容器看容器日志docker exec -it 容器 bash进容器内部看一眼外加三个清理用的docker stop 容器 # 停止 docker start 容器 # 启动已有的不是新建 docker rm 容器 # 删除容器要先停 docker rmi 镜像 # 删除镜像口诀run 起步、ps 看状态、logs 排错、exec 进去看、rm 清理。这里有个高频困惑先点破docker ps看不到刚run的容器多半是两种原因——① 忘了-d前台跑着占着你的终端② 容器已经退出了用docker ps -a就能看到它躺在 Exited 状态。三、第一次实战nginx 容器 端口映射 数据卷docker run -d --name web -p 8080:80 nginx这一行把每个参数拆开看-d后台运行detach不占终端--name web给容器起个名字不起名会给个随机名很快你就分不清谁是谁-p 8080:80宿主机端口:容器端口顺序不能反nginx用哪个镜像-p那个顺序是必须记死的左边是你从浏览器访问的端口宿主机右边是容器内应用真正监听的端口nginx 默认 80。写反了就是「明明起来了浏览器却连不上」。验证docker ps curl -I http://localhost:8080http://localhost:8080就能看到 nginx 欢迎页了。然后挂一个数据卷让它读你本机的文件docker run -d --name web \ -p 8080:80 \ -v /srv/html:/usr/share/nginx/html:ro \ nginx-v 宿主机路径:容器路径:ro—— 把本机的/srv/html挂到容器里的网站根目录:ro表示容器只能读不能改。为什么必须挂卷因为容器的文件系统是临时的——容器一删里面写的所有东西跟着没。要数据留下来数据库文件、上传的图片、日志就得挂在宿主机上。这是新手最容易忽略、也最容易造成损失的一条。清理docker rm -f web # -f 能强删正在运行的容器四、打包自己的应用Dockerfile 逐行讲假设你有个 Python 应用。写一个名叫Dockerfile的文件没有后缀FROM python:3.12-slim # 从哪个基础镜像开始自带 Python你不用装 WORKDIR /app # 之后所有命令都在 /app 目录下执行 COPY requirements.txt . # 先把依赖清单拷进去 RUN pip install -r requirements.txt # 装依赖构建时执行结果固化进镜像 COPY . . # 再把代码拷进去 EXPOSE 8000 # 声明容器用哪个端口文档性质映射还得靠 -p CMD [python, app.py] # 容器启动时跑的进程最关键的一处区别一定要分清指令执行时机特点RUN构建镜像时执行一次结果被固化进镜像比如装好的依赖CMD每次启动容器时执行只保留最后一条进程结束容器就结束EXPOSE 8000也常被误解它只是声明「这个容器打算用 8000」真正对外映射还是靠run时的-p 8000:8000。不写EXPOSE也能跑写了EXPOSE不写-p也访问不到。顺手加一个.dockerignore别把垃圾打进镜像venv/ .git/ __pycache__/ *.log五、build 与 rundocker build -t myapp:v1 . # 构建-t 打标签最后那个 . 别漏表示当前目录 docker run -d --name myapp -p 8000:8000 myapp:v1 docker logs -f myapp # 跟着看日志排错第一站镜像标签的:v1是版本号自己做实验时建议每次都加——不加会默认latest很快你就分不清哪个镜像对应哪版代码了。六、多容器docker compose提一句真实应用往往是好几个容器一起Web 数据库 缓存。一个个docker run太累用 Compose 一个文件描述services: web: build: . ports: - 8000:8000 db: image: postgres:16 environment: POSTGRES_PASSWORD: secret volumes: - pgdata:/var/lib/postgresql/data volumes: pgdata:docker compose up -d # 按文件全部起起来 docker compose down # 全部停掉并删除 docker compose logs -f # 一起看日志入门阶段知道有这么个东西就行等单容器玩熟了再深入。七、和传统部署摆一起看维度传统直接在机器上装容器环境一致性每台机器都得手动对齐容易「我这能跑你那不行」镜像里全是定死的到哪都一样依赖冲突两个应用要不同版本的 Python打架各自独立互不影响迁移重装一遍、重配一遍把镜像拉过去就行回滚手工退版本麻烦换回旧标签的镜像秒级占用省直接跑在系统上多一层开销很小学习成本低多一套概念和命令结论不是「容器全面碾压」而是它把「环境差异」这个最常见的故障源一次性消灭了。八、五个常见坑坑症状原因 / 修法容器一起就退出docker ps看不到ps -a显示 Exited(0)CMD启动的是前台进程吗进程结束容器就结束和 systemd 的Typesimple同理端口被占port is already allocated宿主机那个端口有别人在用ss -tlnp \| grep 8080数据没了容器删了里面的数据也删了没挂-v卷。数据必须落在宿主机上permission denied ... docker.sock普通用户敲 docker 全失败加 docker 组 重新登录exec进不去exec报容器不在运行容器已经退出了先logs看为什么速查表文末收藏版# ---- 安装与权限 ---- sudo apt install -y docker.io sudo systemctl enable --now docker sudo docker run hello-world # 验证 sudo usermod -aG docker $USER 重新登录 # 普通用户免 sudo组变更要新会话 # ---- 六个必会命令 ---- docker pull nginx docker images docker run -d --name web -p 8080:80 nginx # -d 后台 / --name 命名 / -p 宿主机:容器 docker ps -a # -a 才看得到已退出的 docker logs -f web docker exec -it web bash # ---- 挂数据卷不挂 删容器就没数据---- docker run -d --name web -p 8080:80 -v /srv/html:/usr/share/nginx/html:ro nginx # ---- Dockerfile 六个指令 ---- # FROM 基础镜像 / WORKDIR 工作目录 / COPY 拷文件 / # RUN 构建时执行固化进镜像/ EXPOSE 声明端口 / CMD 启动时执行前台 docker build -t myapp:v1 . docker run -d --name myapp -p 8000:8000 myapp:v1 # ---- 清理 ---- docker stop web ; docker rm web ; docker rmi nginx docker rm -f web # 强删运行中的 # ---- 心法 ---- # 容器不是小虚拟机是被隔离的「一个进程」便利工具不是安全边界 # 容器文件系统是临时的 → 要留数据必须挂 -v # CMD 必须是前台进程否则容器秒退 # 进了 docker 组 ≈ 有 root 权限取舍知道就好一句话总结Docker 解决的不是「怎么跑」而是「在哪台机器上都跑得一样」。入门只要记住三件事——镜像/容器/仓库三词分清、-p 宿主机:容器顺序别反、要留数据必须挂-v。
延伸阅读

更多相关文章

2026/10/14 3:25:46

`Volatile.Read` 是否是问题原因

1. Volatile.Read 是否是问题原因 Volatile.Read 是一种“带内存可见性保证的读取”。 Action<int, string>? handler Volatile.Read(ref _faultRunLightRedSolidHandler);它主要保证&#xff1a; 其他线程刚通过 Interlocked.Exchange 注册的新处理器能够被及时看见…

2026/10/14 3:25:46

两千年历史 GDP 如何做到可复现?关键是估计来源标识

打开一个数据说明页时&#xff0c;“国家—年份—数值”的整齐结构很容易让人默认这是一份逐年官方实测。但时间跨度越长&#xff0c;这种默认越危险。DeepSData 的公开页面介绍&#xff0c;Maddison Project Database 覆盖长期人均 GDP 与人口估计&#xff0c;字段包括用于水平…

2026/10/14 4:25:50

Stata矩阵计算入门:从matrix命令到Mata的进阶指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 4:25:50

考研数据结构复试高频考点、手写代码与面试应对策略

初试成绩出来之后&#xff0c;很多同学会陷入一种“不知道该干什么”的状态。刷题吧&#xff0c;好像复试不考这么细&#xff1b;看论文吧&#xff0c;又不知道从哪看起&#xff1b;真要准备机试&#xff0c;又发现手生的厉害。我自己当年备考“考研数据结构复试题目整理”的时…

2026/10/14 4:25:50

考虑主从配电网分布式优化的ADMM算法Matlab实现与串并行对比

最近在推进一个关于主从配电网分布式优化控制的课题时&#xff0c;核心目标是用交替方向乘子法&#xff08;ADMM&#xff09;把主网和多个配电子网之间的协同优化拆开做&#xff0c;整个算法在Matlab里落地&#xff0c;并对比了串行和并行两种执行机制下的收敛行为。从模型搭建…

2026/10/14 4:25:50

一次GC频繁问题排查:从JFR到代码优化的完整实践

大概一个多月前&#xff0c;我接到一条让我头疼不已的告警&#xff1a;某个订单查询接口的 P99 延迟从平时的 80ms 一路飙到 800ms 以上&#xff0c;CPU 使用率每隔几分钟就像过山车一样冲到接近 100%。一开始我照例查了流量、数据库慢查询、第三方依赖&#xff0c;全都没有异常…

2026/10/14 4:20:49

在ESP32上构建应用商店:模块化固件架构与动态加载实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 5:19:27

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”&#xff0c;而是它给出一段看起来合理的说明&#xff0c;程序却从中猜错优先级。通俗做法是&#xff1a;要求模型只交 JSON&#xff08;JavaScript Object Notation&#xff0c;轻量数据格式&#xff09;&#xff0c;再让代码验证它。Gem…

2026/10/13 17:09:18

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里&#xff0c;有A同学跑来问我&#xff1a;选什么毕设题目最稳妥&#xff0c;既能让评审老师觉得工作量够&#xff0c;又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/14 2:32:55

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友&#xff0c;在看完一堆“Hello World”和基础组件之后&#xff0c;大概率都会撞上同一堵墙&#xff1a;StatefulWidget 里那堆 initState、build、dispose 方法&#xff0c;到底什么时候被调用&#xff1f;为什么顺序是那样&#xff1f;在里面到…

2026/10/14 0:00:33

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景&#xff0c;也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间&#xff0c;确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中&#xff0c;这类问题直接关系到飞行安全和机…

2026/10/14 0:00:33

macOS/iOS高精度计时:mach_absolute_time原理与实战

1. 项目概述&#xff1a;为什么mach_absolute_time是 macOS/iOS 时间测量的“黄金标准”在 macOS 和 iOS 开发中&#xff0c;我见过太多人用CFAbsoluteTimeGetCurrent()或NSDate.timeIntervalSinceReferenceDate做性能打点&#xff0c;结果在高精度场景下被系统时钟跳变、NTP 同…

2026/10/14 0:00:33

SCORM课件演示与测试全解析:从API通信到自动化验证

简介&#xff1a;这是一套遵循SCORM标准的交互式课件演示与测试资源&#xff0c;面向E-Learning课程开发者、LMS平台测试人员及在线教学从业者&#xff0c;用于验证课件在不同学习管理系统中的识别、传递与跟踪能力。压缩包共28个文件&#xff0c;约12.07MB&#xff0c;以swf动…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑