ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Docker 入门:镜像、容器、Compose 一次讲透

Docker 入门:镜像、容器、Compose 一次讲透 Docker 入门镜像、容器、Compose 一次讲透引言装好 Docker 之后下一步就是搞懂它到底在干嘛。不少新手卡在会用命令但不懂概念docker run会敲问镜像和容器有什么区别就答不上来。概念不清后面的 Dockerfile、Compose 全学不踏实遇到问题也不知道从哪查。这篇文章从最核心的三个概念讲起用类比把话说透接着过一遍高频命令然后手把手写一个完整的 Dockerfile 和一个 compose 文件都是能直接跑起来的最后聊几句生产环境的心得。全程不要求你有基础照着敲就行。核心概念镜像、容器、卷、网络先用大白话定个基调镜像像类容器像实例。你写过 Python 就知道类可以 new 出无数个对象镜像同理build 一次可以 run 出无数个容器。对象之间互相隔离容器之间也隔离。概念类比一句话解释镜像 Image类的定义 / 安装包只读模板包含代码、运行时、依赖、配置容器 Container类的实例 / 正在运行的程序镜像跑起来之后的进程可启停、可删除卷 VolumeU 盘容器外的持久化存储容器删了数据还在网络 Network虚拟局域网容器之间通信的通道可自定义隔离仓库 Registry应用商店存放镜像的地方官方的是 Docker Hub三个细节单独强调镜像是分层的。一个镜像由多层只读层叠加而成拉取时公共层可以复用。多个镜像共用同一个基础层时体积小、启动快这就是为什么docker pull有时秒完成——那几层早就有了。容器是无状态的。容器里的文件系统默认是临时的容器一删里面改的东西全没。要持久化就必须挂卷。这个特性常被新手忽略数据丢一次就记住了。镜像不可改只能叠加。想改镜像不是直接编辑而是写 Dockerfile 重新构建出新的镜像。所以改镜像在 Docker 世界里等于重新 build。再补一个理解上的关键点Docker 不是虚拟机。虚拟机里跑着完整的操作系统开销大、启动慢容器共享宿主机内核只有进程级别的隔离所以秒级启动、资源占用低。代价是隔离性不如虚拟机——这也是生产环境容器安全要单独关注的原因。想明白这个区别你判断容器还是虚拟机就有依据了。常用命令这套够你用很久命令不用全背下面这套日常开发足够先复制收藏。# ---- 镜像管理 ----dockerimages# 列出本机镜像dockerpull nginx# 拉取镜像不指定 tag 默认 latestdockerrmi nginx# 删除镜像# ---- 容器生命周期 ----dockerrun-d--nameweb-p8080:80 nginx# 后台运行容器名 web宿主机 8080 映射到容器 80dockerps# 查看运行中的容器dockerps-a# 查看所有容器含已停止dockerstop web# 停止容器dockerstart web# 启动已停止的容器dockerrmweb# 删除容器需先 stopdockerrm-fweb# 强制删除运行中的容器# ---- 日志与进入 ----dockerlogs web# 查看容器日志dockerlogs-fweb# 实时跟踪日志CtrlC 退出dockerexec-itwebbash# 进入容器内部终端dockerexecwebls/usr/share/nginx/html# 在容器内执行单条命令# ---- 清理 ----dockersystemdf# 看磁盘占用dockersystem prune# 清理悬空镜像、停止的容器等谨慎会删东西几个容易记混的参数说清楚-d后台运行--name起名字-p 宿主机端口:容器端口端口映射-it交互式终端。docker run负责创建并启动之后再启停用docker start / stop别反复 run否则会创建出多个容器。再给三个高频补充命令docker cp web:/app/log.txt ./把容器里的文件拷到宿主机docker top web看容器里的进程docker inspect web看容器的完整配置IP、挂载、环境变量都在里面。docker inspect是新手的宝藏命令很多怎么查不到的问题答案都在它输出的 JSON 里。最后提醒一句docker system prune会连带删除所有已停止的容器、悬空镜像和未使用的网络执行前确认没有需要保留的东西。想精准清理加--filter条件或直接按镜像 ID 手动删除。写一个 Dockerfile从零构建一个 Web 服务光看命令不过瘾直接写一个能跑的。场景一个极简的 Python Web 服务用 Flask 返回一句话。先建项目目录和源码mkdirmyappcdmyappapp.pyfromflaskimportFlask appFlask(__name__)app.route(/)defindex():returnHello from Docker!if__name____main__:app.run(host0.0.0.0,port5000)requirements.txtflask3.0.3Dockerfile关键文件每一行都注释了作用# 基础镜像python 3.12 精简版 FROM python:3.12-slim # 设置工作目录后续命令都在这个目录下执行 WORKDIR /app # 先复制依赖清单并安装利用镜像分层缓存改代码时不用重装依赖 COPY requirements.txt . RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple # 复制源码 COPY app.py . # 声明容器监听端口仅说明作用真正映射靠 -p EXPOSE 5000 # 容器启动时执行的命令 CMD [python, app.py]构建并运行# 构建镜像-t 是命名末尾的 . 是构建上下文目录dockerbuild-tmyapp.# 预期输出最后两行# Successfully built 镜像ID# Successfully tagged myapp:latest# 后台运行映射端口dockerrun-d--namemyapp-p5000:5000 myapp# 验证服务curlhttp://localhost:5000# 预期输出# Hello from Docker!# 看日志dockerlogs myapp【此处需补真实截图】常见翻车点docker run之后curl连不上。先docker ps确认容器在跑再docker logs看报错。新手最常见的错误是端口映射写反了——-p 5000:5000左边是宿主机端口右边是容器端口别倒过来。另外改完代码要重新docker build容器里跑的是旧镜像的代码这个也经常有人忽略。docker-compose 实战一个命令起整套服务单容器够用但真实项目往往是Web 数据库起步。Compose 的价值就在这一份 YAML 描述多容器一个命令全部起。docker-compose.yml放在项目根目录services:web:build:.ports:-5000:5000depends_on:-dbenvironment:# 应用代码里读这个环境变量连接数据库DB_HOST:dbdb:image:mysql:8.0environment:MYSQL_ROOT_PASSWORD:root123MYSQL_DATABASE:myapp# 数据持久化MySQL 的数据落到命名卷里volumes:-db_data:/var/lib/mysqlvolumes:db_data:注意两点build: .表示 web 这个服务用当前目录的 Dockerfile 构建db直接拉现成的 mysql 镜像。两个服务在同一个默认网络里靠服务名互相访问——web 里连数据库主机填db而不是 localhost这是新手最容易懵的地方。启动与验证# 后台启动所有服务dockercompose up-d# 预期输出# [] Running 3/3# ✔ Container myapp-web-1 Started# ✔ Container myapp-db-1 Started# 看服务状态dockercomposeps# 实时看某个服务的日志dockercompose logs-fweb# 停止但保留数据dockercompose down# 全部清理连同卷一起删慎用dockercompose down-v【此处需补真实截图】docker compose up -d跑完web 和 db 都起来了。web 里如果有连数据库的代码通过环境变量 DB_HOST 指向 db 服务即可。验证仍然用curl http://localhost:5000。注意新版语法是docker compose空格老版才是docker-compose短横线两条命令别混。compose 排错三板斧docker compose ps看状态docker compose logs看日志docker compose config验证 YAML 语法。最后一条尤其好用缩进写错时它能直接指出问题位置不用对着文档猜。生产环境注意点本地跑通和线上稳是两个世界几个经验直接给别用 latest 标签。不写 tag 就默认 latest每次构建覆盖部署时根本不知道线上跑的是哪一版。构建时固定版本号比如myapp:20260826-v1。别用 root 跑服务。基础镜像默认 root容器一旦被攻破就拿到 root 权限。Dockerfile 里建普通用户再切换成本很低。多用多阶段构建。编译型语言Go、Java先构建再运行最终镜像只留运行产物体积能缩一个数量级拉取和启动都快。数据必须落卷。任何有状态的服务数据库、缓存、上传文件都要挂 volume容器说换就换数据不能跟着没。数据库实例最好连绑定挂载都用上。日志别只靠 docker logs。容器一重启docker logs里之前的日志就翻页了。生产环境把 stdout 日志接到集中式日志平台。基础镜像定期升级。基础镜像的补丁滞后等于裸奔。配合镜像仓库的漏洞扫描把修复计划排进迭代。加健康检查。docker run的--health-cmd或 compose 里的healthcheck让 Docker 定期探测服务是否存活编排系统靠它做自动重启和摘除流量。本地可能用不上一旦上了编排就离不开。写 .dockerignore。构建上下文默认把整个目录打包发给 Dockernode_modules、.git、pycache这些动辄上百 MB 的东西全传过去构建又慢又脏。在项目根目录写一个 .dockerignore规则类似 .gitignore把不该进镜像的都排除掉。还有一个共识一切配置用环境变量或配置文件注入别把密码、密钥写进 Dockerfile。镜像一旦被推送出去里面的秘密就等于公开了。用 compose 的 environment、编排平台的 Secret 都能解决选一个顺手的方式坚持用。实战中的几个高频问题时区问题迟早撞上。官方基础镜像默认 UTC容器里日志时间比本地慢八小时排查问题时对不上时间戳很头疼。轻量解法运行容器加-e TZAsia/Shanghaicompose 里写进 environment 同理重一点的把宿主机/etc/localtime挂进容器。日志、定时任务、数据库写入都依赖时间一开始统一好后面少一堆对时间的活。MySQL 容器初始化 SQL 怎么自动跑mysql 官方镜像首次启动时会按字母序执行/docker-entrypoint-initdb.d/目录下的 .sql 和 .sh 文件compose 里把初始化脚本用 volume 挂进这个目录第一次启动自动建库建表。注意这个机制只在数据目录为空时执行一次想重跑就得删卷重来改脚本再 up。端口冲突的报错也要会读。docker run提示 “port is already allocated”说明宿主机端口被占换个映射端口或者docker ps -a看看是不是有个旧容器占着docker rm掉即可。镜像删除报 “image is being used by container”顺序反了——先删容器再删镜像反过来就被拦。最后提醒一句别把docker exec -it当日常习惯。进容器手改的东西容器一删全没这恰恰是无状态设计的本意。要改配置就改 Dockerfile 重新构建要留数据就挂卷路径走对了才不反复返工。还有个实用小习惯compose 里给数据库这类有状态服务加restart: unless-stopped宿主机重启后服务自动跟着回来省去几次半夜被叫醒的麻烦。自建服务多了之后这个字段几乎是标配。小结Docker 入门就三关概念关镜像/容器/卷/网络、命令关run/ps/logs/exec、实战关Dockerfile Compose。概念想通、命令练熟、把第一个 Dockerfile 和 compose 文件跑起来你就正式入门了。往后遇到环境问题第一反应会变成起个容器试试这个习惯一旦养成开发效率的提升是实实在在的。有什么问题评论区见也可以留言说说你想看的下一个主题。
返回列表