我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

Docker入门到实战:镜像构建、容器编排与部署避坑指南

Docker入门到实战:镜像构建、容器编排与部署避坑指南 1. 为什么我建议每个开发者都尽快上手 Docker先说个场景。你本地写得飞起的项目同事拉下来一跑直接报错数据库连不上、依赖版本不对、系统路径不一样。然后你俩开始经典的我这儿没问题啊拉锯战。又或者你新买了一台服务器从装系统到发布上线折腾三四个小时只为了把那几个早就写好却没动过的环境命令再执行一遍。Docker 解决的就是这个环境不一致的老大难问题。它把你的应用连同它依赖的环境一起打包成一个标准化的单元不管在笔记本电脑、公司测试机还是云服务器上都能用同一套方式启动、停止、销毁。这才是它最朴素、也最值钱的价值——环境标准化而不是虚拟化这种模糊的概念。很多人第一次听说 Docker会把它和虚拟机混在一起。虚拟机模拟的是整个硬件层需要在里面装完整操作系统启动慢、占资源、体积动辄几个GB。Docker 不一样它只是把进程关进了一个隔离的单间共用宿主机内核启动只需几百毫秒一个镜像可能连一百兆都不到。你甚至可以把它理解成给每个程序配一套独立的生活用品依赖库、配置文件、运行环境但大家共享同一个厨房操作系统内核。另外Docker 入门门槛真的没有想象中高。你不需要先学会 Linux、不需要懂内核原理只要会几个核心命令就能把自己的第一个服务容器化跑起来。这篇文章的目标就是让你从零开始经历一遍安装 Docker → 理解镜像和容器 → 写 Dockerfile 打包自己的应用 → 跑通一个前后端加数据库的完整项目 → 学会处理数据和容器的关系 → 能自己排查日常问题的完整链路。写代码的同学、做运维的同学、甚至只是偶尔部署个开源项目的同学都可以照着做一遍基本能覆盖工作里八成以上的容器使用场景。提示本文所有命令和配置都在 Linux 环境下验证过但 Windows 和 macOS 的 Docker Desktop 用户在拿到 shell 之后的操作是完全一致的。如果你用的是 Linux直接从第二节看如果你在 Windows 上建议先装好 WSL2 集成这块后面会提一句。2. 安装与验证十分钟让 Docker 在电脑上跑起来2.1 不同系统下的安装方式Linux 环境最有代表性以 Ubuntu 和 CentOS 两类最常见。Ubuntu/Debian 系走 aptsudo apt update sudo apt install -y docker.io sudo systemctl enable docker sudo systemctl start dockerCentOS/RHEL 系走 yumsudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install -y docker-ce docker-ce-cli containerd.io sudo systemctl enable docker sudo systemctl start dockerWindows 和 macOS 用户直接装 Docker Desktop 就行。装完记得在 Settings 里把资源调大一点默认 2GB 内存有时候跑多个容器会吃紧建议 4GB 起步。Windows 下强烈建议把 Use WSL 2 based engine 打开跑起来的性能和稳定性比 Hyper-V 模式好不少尤其是挂载代码目录做热更新的时候。装完以后验证一下守护进程和工作环境docker version docker info docker run hello-world如果docker run hello-world能顺利输出一段欢迎语说明整个链路已经通了。这一步走完你就有了一套能用的容器环境。2.2 镜像源配置国内加速的必做操作跳过这步的人在拉取镜像时百分之百会卡到怀疑人生。Docker 默认从官方源拉镜像在真实网络环境下速度极不稳定安装完第一件事就改镜像源。编辑/etc/docker/daemon.json没有就新建一个{ registry-mirrors: [ https://docker.m.daocloud.io, https://dockerproxy.com ] }保存后重启 Dockersudo systemctl daemon-reload sudo systemctl restart docker改完源再拉镜像速度会有质的提升。这个配置文件不只是管镜像源后面做日志限制、存储驱动调整都在这同一个文件里改先记住它的位置。2.3 给普通用户免 sudo 权限每次执行 docker 命令前面都加 sudo会很烦。把当前用户加入 docker 组就能直接用了sudo usermod -aG docker $USER newgrp docker提示这个操作本质上是把 docker 组的用户权限提升到接近 root。如果你是在多人共用的服务器上给谁加这个组要谨慎但就个人开发机而言这是提升日常体验的必备操作。3. 镜像与容器先搞清这两个核心概念后面才不迷糊很多人学了 Docker 半年一直在用命令但对镜像和容器的关系还是模模糊糊的。这里我用一个最容易理解的类比镜像就是类容器就是实例。镜像是一个只读的模板里面保存了某个应用运行所需的完整环境——操作系统的基础文件、运行时、依赖库、代码、启动命令全部固化进去。它本身不会跑起来只是静静躺在磁盘上。容器则是镜像的一次运行实例启动时 Docker 会在镜像层之上叠加一个可写层进程就在这个可写层里读写数据。同一个镜像可以启动十个容器它们共用镜像文件但彼此的数据和状态是完全隔离的。3.1 镜像的拉取、查看与删除docker pull nginx:latest # 拉取最新版 Nginx 镜像 docker images # 查看本地镜像列表 docker rmi nginx:latest # 删除指定镜像日常操作里docker images和docker rmi用得最勤。本地堆积的悬空镜像-e结尾的一大堆none就是反复构建留下的垃圾用下面这条命令一键清理docker image prune -fdocker pull的时候可以指定版本标签不写默认为latest。但生产环境里强烈建议写死版本号比如nginx:1.25.3否则某天拉到一个不兼容的新版本应用直接起不来。3.2 容器的启动、查看与停止启动容器的基础命令长这样docker run -d --name web -p 8080:80 nginx拆开解释-d后台运行不占用当前终端--name web给容器起个名字方便后续管理-p 8080:80把宿主机的 8080 端口映射到容器的 80 端口nginx使用哪个镜像这条命令执行完Nginx 容器就在后台跑着了访问http://localhost:8080就能看到欢迎页。正因为有了-p这个端口映射你才能从宿主机访问到容器内部的 Nginx 服务——容器内部有自己独立的网络命名空间不映射的话外面根本摸不到它。查看和管理容器的命令docker ps # 只看运行中的容器 docker ps -a # 看所有容器包括已停止的 docker logs web # 看容器日志 docker stop web # 停止容器 docker start web # 启动已存在的容器 docker rm web # 删除容器需先停止 docker exec -it web bash # 进入容器内部执行命令docker exec -it web bash是排查问题的神器。进入容器内部后你可以像登录一台服务器一样查看文件、进程、网络。很多容器镜像为了精简体积里面没有 vi、curl 这类常用工具进去后先apt update apt install curl再查也行但这种改动只存在当前容器层里容器一删就消失。3.3 镜像层与 Union FS 机制每一条 Dockerfile 指令都会生成一个只读的镜像层多个层叠加在一起构成完整镜像。拉取镜像时你会看到Pull complete一行行输出每行就是一层。这种分层设计带来的好处是共享和缓存——同一台机器上基于同一基础镜像构建的不同应用基础层只存一份。我见过很多新人一开始不理解为什么改了一行代码重新构建后面几步几乎是秒完成。这正是层缓存发挥作用前面的层没变就直接复用只重新生成变化的部分。容器运行后在镜像层之上加了一个可写层进程对文件的任何写操作都发生在这层。这也是容器删除后数据丢失的根本原因——可写层随容器销毁而消失。后面讲数据卷的时候你会再和这个机制打一次照面。3.4 镜像和容器概念速查表对比维度镜像Image容器Container本质只读模板文件镜像的运行实例生命周期创建后不变可启停、删除、重新创建数据不可写可写但默认临时类比类 / ISO 安装盘对象 / 安装好的系统记住这张表做实验的时候多对照几次很快就能形成直觉。4. 写在 Dockerfile 里的学问构建自己的第一个应用镜像4.1 一个最简单的 Dockerfile 长什么样用 Python Flask 写一个最简 Web 服务返回一段 JSON# app.py from flask import Flask app Flask(__name__) app.route(/) def index(): return {message: Hello Docker!} if __name__ __main__: app.run(host0.0.0.0, port5000)在同一个目录下创建Dockerfile# 基础镜像python 3.11 精简版 FROM python:3.11-slim # 设置工作目录 WORKDIR /app # 先复制依赖清单并安装充分利用层缓存 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple # 再复制代码 COPY app.py . # 暴露端口 EXPOSE 5000 # 启动命令 CMD [python, app.py]requirements.txt 内容flask2.3.3构建镜像docker build -t my-flask-app ..代表使用当前目录作为构建上下文。构建完成后启动docker run -d --name flask-app -p 5000:5000 my-flask-app访问http://localhost:5000就能看到{message:Hello Docker!}。4.2 每一条指令的设计意图FROM是基础镜像所有构建的起点。选基础镜像有个原则能用 slim 就用 slim能用 alpine 就用 alpine体积越小攻击面越小、拉取越快。普通应用场景下python:3.11-slim比完整版小一半还多。WORKDIR设置工作目录所有后续指令都在这个目录下执行。不写的话默认在根目录很容易把文件散得乱七八糟。COPY requirements.txt和RUN pip install这两行要放在COPY app.py之前这是层缓存利用的精髓。如果先把所有代码 COPY 进去再安装依赖那么代码每次改动都会让序号靠后的依赖安装层缓存失效整个构建过程被迫重走一遍。分开写之后只有 requirements.txt 变动时才会重新执行 pip install日常改代码时构建速度会快非常多。EXPOSE只是声明容器内服务监听哪个端口是个文档性质的指令真正生效的端口映射还是docker run时的-p。所以 EXPOSE 写不写不影响运行但建议写能让人一眼看出这个镜像对外提供什么服务。CMD定义容器启动时的默认命令。注意它和RUN的区别RUN在构建阶段执行用于准备环境CMD在容器启动时执行是真正的进程入口。如果启动时用docker run ... nginx -g daemon off;这种形式追加了命令会覆盖 Dockerfile 里的CMD。4.3 .dockerignore不把垃圾文件烧进镜像没有.dockerignore文件的构建就像往行李箱里塞了一堆不穿的衣服。创建项目根目录下新建一个.dockerignore__pycache__ *.pyc .git .venv venv .env重点说下.env。以前遇到过有人把数据库密码写在.env里没加排除构建镜像后顺手推到仓库整个项目源码都解密了。这种教训一次都不能出宁可多加几行排除规则也别心存侥幸。4.4 进入容器验证一下镜像构建出来以后别急着往前跑先进容器里看看文件是不是都在、进程是不是活着docker exec -it flask-app /bin/bash ls -la curl http://localhost:5000 exit到这里你已经完成了从代码到镜像的完整打包流程。这也是整个 Docker 学习曲线里最核心的一段路后面的网络、数据、编排都是在这条主线上做延伸。5. 实战部署一个前后端加数据库的完整项目单个容器只是开胃菜现实项目通常会涉及多个服务。以最简单的架构为例Nginx 做前端静态文件服务器 反向代理后端用 Python Flask 提供 APIMySQL 存数据。5.1 项目结构和配置准备准备一个目录结构如下docker-demo/ ├── docker-compose.yml ├── backend/ │ ├── Dockerfile │ ├── app.py │ └── requirements.txt └── nginx/ ├── nginx.conf └── index.html后端app.py增加一个读取数据库的接口import pymysql from flask import Flask, jsonify app Flask(__name__) def get_conn(): return pymysql.connect( hostdb, userdemo, passworddemo123, databasedemo_db, charsetutf8mb4 ) app.route(/) def index(): return {message: Hello Docker, with MySQL!} app.route(/users) def users(): conn get_conn() cursor conn.cursor() cursor.execute(SELECT id, name FROM users) rows cursor.fetchall() conn.close() return jsonify([{id: r[0], name: r[1]} for r in rows]) if __name__ __main__: app.run(host0.0.0.0, port5000)requirements.txt 加上pymysql1.1.0。Nginx 配置文件server { listen 80; location / { root /usr/share/nginx/html; index index.html; } location /api/ { proxy_pass http://backend:5000; proxy_set_header Host $host; } }index.html 就写个最简单的页面用fetch调用/api/users并把结果显示在页面上。5.2 docker-compose.yml 的编写手动docker run启动三个容器还要配置网络、设置 MySQL 初始化又容易出错又难维护。docker-compose.yml 可以用一份配置把多个容器的启动方式一次性定义好version: 3.8 services: db: image: mysql:8.0 container_name: demo-db environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: demo_db MYSQL_USER: demo MYSQL_PASSWORD: demo123 ports: - 3306:3306 volumes: - db_data:/var/lib/mysql - ./sql:/docker-entrypoint-initdb.d backend: build: ./backend container_name: demo-backend depends_on: - db ports: - 5000:5000 environment: DB_HOST: db restart: always nginx: image: nginx:1.25 container_name: demo-nginx ports: - 8080:80 volumes: - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf - ./nginx/index.html:/usr/share/nginx/html/index.html depends_on: - backend先看看关键配置项在说什么。volumes把宿主机目录或命名卷挂载进容器修改 nginx 配置无需重新构建这在日常开发里能省大量时间。depends_on控制启动顺序保证数据库先起。restart: always让容器异常退出后自动重启这也是生产环境的标配设置。5.3 SQL 初始化脚本的执行原理./sql:/docker-entrypoint-initdb.d这个挂载很妙。MySQL 官方镜像在首次启动数据目录为空时会按文件名顺序执行/docker-entrypoint-initdb.d下的所有.sql脚本。所以只要在项目里建一个sql目录放入init.sqlCREATE TABLE IF NOT EXISTS users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL ); INSERT INTO users (name) VALUES (Alice), (Bob), (Carol);首次执行docker-compose up时这个脚本就会自动初始化数据库和表数据。注意首次这个词很关键——MySQL 容器的数据卷一旦生成/docker-entrypoint-initdb.d的脚本就不会再执行了。想改初始数据得把数据卷清掉重新初始化这块后面踩坑部分会细讲。5.4 启动并验证整个链路在docker-demo目录下执行docker-compose up -d --build docker-compose ps状态都为running后访问http://localhost:8080页面会显示出数据库里的三个用户姓名。浏览器访问 Nginx → Nginx 反向代理到后端 → 后端查询 MySQL → 返回 JSON → 前端渲染一条完整的请求链路已经通了。手动模拟一下后端故障看看restart: always的效果docker restart demo-backend docker-compose ps容器会在几秒内自动重启服务不中断。这也是容器编排带来的一大好处。6. 数据卷与网络容器世界里最被低估的两个知识点6.1 数据卷让数据活得更久前面提到过容器销毁后可写层里的数据也跟着没了。解决这个问题的标准方案是数据卷Volume。它由 Docker 管理独立于容器的生命周期存在容器删了卷还在。常用命令docker volume create mydata docker volume ls docker volume inspect mydata # 运行容器时挂载卷 docker run -d --name mysql-demo -v mydata:/var/lib/mysql mysql:8.0 # 绑定挂载直接把宿主机的目录映射进去 docker run -d --name nginx-demo -v $(pwd)/html:/usr/share/nginx/html nginx两种方式的区别值得说清楚。命名卷volume由 Docker 管理存放路径不好直接访问但跨容器共享方便、性能更好适合数据库这种需要安全存放数据的场景。绑定挂载bind mount直接映射宿主机目录开发时改代码即时生效适合开发场景但目录受宿主机文件系统影响读写性能不如命名卷。选择建议平时的开发、调试用绑定挂载部署数据库、缓存这类有状态服务用命名卷。6.2 容器网络模型为什么要用服务名通信docker-compose.yml 里的mysql、backend、nginx其实不只是服务名还是同一网络内的 DNS 别名。容器间通过服务名直接通信而不是写死 IP 地址。理解这个设计你才能明白为什么后端代码里数据库地址写的是hostdb而不是localhost或者某个 IP。Compose 创建项目时会自动建一个默认网络该网络内的所有容器共享 DNS 解析容器之间可以用服务名互相访问。这意味着即使 IP 变了也没关系——服务名寻址保证应用配置不会因为网络拓扑变化而失效。手动管多容器网络可以用docker network create my-net docker run -d --name container-a --network my-net nginx docker run -d --name container-b --network my-net nginx6.3 容器网络调试技巧排查容器间通信问题时最常用的手法是进入容器内部用 ping 或 curl 验证对方服务是否可达docker exec -it demo-backend bash ping db curl http://db:3306 exit如果 ping 不通八成是网络配置问题如果 ping 通了但应用连不上多半是端口、认证或权限问题跟网络隔离无关。理清这个排查思路能帮你快速定位问题层级不至于一头扎进容器里乱翻半天。7. 常用调试和运维操作出现问题别慌按这个思路查7.1 日志与进程排查容器出问题最直接的表现是状态不是running用docker ps -a能看到所有容器及其退出状态。排查顺序建议先日志、再进程、再网络。docker logs demo-backend # 查看全部日志 docker logs --tail 100 demo-backend # 只看最后100行 docker logs -f demo-backend # 持续跟踪日志输出EXITED状态说明进程启动后就退出了看日志是最快的方式。比如后端容器启动时连不上数据库日志里会直接报Cant connect to MySQL server on db。你就能快速判断是depends_on还没生效数据库尚未就绪还是密码错误。7.2 进容器内部精细排查日志解决不了的问题就进容器内部看docker exec -it demo-backend bash cat /etc/os-release env # 查看环境变量 ps aux # 查看进程 netstat -tlnp # 查看监听端口遇到curl: command not found也别意外精简镜像里连这个工具都没有。基于同一个镜像做一次临时容器把需要的调试工具装进去docker run -it --rm --entrypoint bash python:3.11-slim apt update apt install -y curl这个--rm标志很实用容器退出时自动删除适合做一次性任务不会在磁盘上留下垃圾容器。7.3 重启策略和容器清理没配restart的容器宿主机一重启全灭。测试环境的容器建议统一加上重启策略生产环境更是必须docker run -d --restart unless-stopped --name web nginx三个常用值no默认不自动重启on-failure非正常退出时重启unless-stopped只要不是手动 stop就一直重启清理系统里堆积的容器、镜像、卷和缓存docker container prune # 删除所有已停止容器 docker image prune # 删除悬空镜像 docker volume prune # 删除未被任何容器使用的卷 docker system df # 查看各项资源占用提示volume prune这步最好先确认存储里没有需要保留的数据再执行它删卷是不可逆的。生产服务器上我见过太多清理一下导致的数据库彻底丢失事件运行命令之前务必确认存储里没有需要保留的数据没有后悔药。7.4 常用调试命令速查表场景常用命令说明看容器状态docker ps -a含已退出容器看日志docker logs -f name持续跟踪输出进容器docker exec -it name bash交互式 shell看端口docker port name容器的实际映射关系看资源占用docker statsCPU、内存、网络实时监控复制文件docker cp name:/path ./local从容器拷出文件8. 踩坑实录这些坑我替你踩过了照着避就行8.1 数据库容器初始化脚本不生效现象明明挂载了init.sql到/docker-entrypoint-initdb.d启动后表却不存在。原因是之前已经用同一个数据卷启动过数据库初始化脚本只在数据目录为空时执行一次。解决方式确认没有需要保留的数据后把卷删掉重建docker-compose down -v docker-compose up -d-v会连带删除数据卷。如果你有用别名如 volume 全名自行创建过卷还需要手动删干净。这是新手最容易反复踩的坑没有之一。8.2 端口占用导致容器启动失败现象docker run -p 8080:80报port is already allocated。宿主机上已经有程序占了 8080。排查方式ss -tlnp | grep 8080找到占用进程后换端口或者停掉占用程序再重新启动容器。顺带一提容器即使启动失败也会残留在docker ps -a里记得清理。8.3 容器内时间不对现象容器日志时间比北京时间慢了 8 小时。基础镜像默认采用 UTC 时区。解决方式运行容器时挂载宿主机时区文件docker run -d -v /etc/localtime:/etc/localtime:ro --name web nginxCompose 里则写成volumes: - /etc/localtime:/etc/localtime:ro8.4 pip 安装依赖特别慢现象构建镜像时pip install卡很久。默认使用官方 PyPI在国内网络环境下体验很不友好。解决方式替换为国内镜像源推荐清华源pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple更完整的做法是在 Dockerfile 里设置全局 pip 源RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple8.5 宿主机重启后所有容器失联现象宿主机一重启没有配置重启策略的容器全部停止。解决方式运行容器时加上--restart unless-stopped或在 Compose 文件里配置restart: unless-stopped。尤其建议对数据库容器配置这个否则系统突发重启时数据还在但服务状态全变了。8.6 容器里访问宿主机服务现象容器内需要访问宿主机上运行的服务直接写localhost连不上。解决方式容器内的 localhost 指的是容器自己不是宿主机。Linux 下可以用172.17.0.1访问宿主机网关地址但在 Mac 和 Windows 的 Docker Desktop 里这个地址往往不是宿主机的真实地址。更通用的方案是使用host.docker.internalDocker Desktop 默认支持Linux 下也可以在docker run时加--add-hosthost.docker.internal:host-gateway。9. Docker Compose 的生产级配置从能跑到能扛9.1 Compose 文件拆分与资源限制Compose 是单机多容器应用编排的事实标准。生产环境里建议用两个 Compose 文件拆分配置version: 3.8 services: backend: build: ./backend deploy: resources: limits: cpus: 0.5 memory: 512M logging: driver: json-file options: max-size: 10m max-file: 3 db: image: mysql:8.0 deploy: resources: limits: memory: 1G healthcheck: test: [CMD, mysqladmin, ping, -h, localhost] interval: 10s timeout: 5s retries: 5healthcheck是生产级服务必须配的东西。depends_on只管启动顺序不保证服务可用——数据库容器起了但 MySQL 还没就绪后端启动照样失败。有了 healthcheck 之后可以在 Compose 文件里把后端服务的depends_on配置升级为services: backend: depends_on: db: condition: service_healthy这样后端容器会在数据库通过健康检查之后才开始启动彻底解决竞态问题。9.2 环境变量管理不要硬编码把配置写死在应用代码里是罪大恶极的。Docker 环境变量的正确做法是通过.env文件或 Compose 配置注入。项目根目录.envMYSQL_ROOT_PASSWORDroot123 MYSQL_DATABASEdemo_db MYSQL_USERdemo MYSQL_PASSWORDdemo123Compose 文件里引用services: db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: ${MYSQL_DATABASE} MYSQL_USER: ${MYSQL_USER} MYSQL_PASSWORD: ${MYSQL_PASSWORD}这样做的好处是代码仓库里提交的是 Compose 模板真实密码只存在于部署机器的.env文件不会泄露到版本库。再次强调.env必须在.gitignore里。9.3 扩缩容与零停机发布使用docker-compose up --scale可以快速把某服务扩展到多个实例docker-compose up -d --scale backend3Nginx 配置里加上负载均衡upstream backend_servers { server backend:5000; server backend:5000; server backend:5000; } server { listen 80; location /api/ { proxy_pass http://backend_servers; } }发布新版本时用 Compose 平滑滚动更新docker-compose up -d --no-deps backend--no-deps表示不重建依赖服务只更新 backend 本身。逐个替换容器配合 healthcheck 和重启策略可以实现简单的零停机发布——单个节点坏掉时其余节点继续服务配套的restart: always又把挂掉的服务拉回来。当然这距离 K8s 级别的滚动发布还有差距但作为单机场景的部署方案已经够用了。10. 镜像瘦身与构建优化从能用升级到高效10.1 多阶段构建一个 Dockerfile 完成编译与精简后端用 Go 或 Java 写的时候编译器体积可能几个 GB但运行只需要最终产物。多阶段构建让你在同一个 Dockerfile 里先用大镜像编译再把产物复制到干净的精简镜像中最终镜像里一个多余的编译工具都不会出现。# 第一阶段编译 FROM golang:1.21 AS builder WORKDIR /app COPY . . RUN CGO_ENABLED0 go build -o myapp main.go # 第二阶段运行 FROM alpine:3.19 WORKDIR /app COPY --frombuilder /app/myapp . CMD [./myapp]第二阶段的COPY --frombuilder从第一阶段拿产物最终镜像只有几 MB。对任何编译型语言的容器化这套模式都是标配做法。10.2 减少层数和清理临时文件Dockerfile 里尽量合并 RUN 指令用串行执行RUN apt-get update \ apt-get install -y --no-install-recommends curl \ rm -rf /var/lib/apt/lists/*--no-install-recommends跳过非必需推荐包最后清空 apt 缓存减少不必要的层体积。用alpine替代debian能进一步瘦身但需要注意部分依赖包可能在 alpine 下编译耗时更长取舍要结合项目实际情况。10.3 基础镜像 Tag 固定与安全检查之前提过的latest陷阱这里再强调一下一定要固定镜像版本号否则某天基础镜像更新后你的镜像可能在毫无感知的情况下发生依赖变化甚至直接起不来。条件允许的话构建完成后用工具扫一遍镜像常见漏洞确认没有引入明显的安全风险再推到仓库。初期不熟悉的时候可以启用 Docker 官方的自动更新提醒功能至少做到镜像变动可感知。11. 下一步该怎么走从 Docker 走向云原生Docker 本身只是容器化工具的起点。单机场景下 Compose 足够但当你面对几十个容器分布在多台机器上、需要自动扩容、滚动发布、自愈、配置管理时Kubernetes 就会进入视野。K8s 的核心概念——Pod、Deployment、Service、Ingress——本质上是把 Docker 容器作为最小调度单元在集群维度重新抽象了一层。迁移到 K8s 的过程通常不是重写应用而是把 docker-compose.yml 的配置转换成对应的 K8s 清单。很多字段语义相近Compose 的services对应 Deployment 或 StatefulSetvolumes对应 PersistentVolumeClaimnetworks对应 Service。掌握 Docker 的思维方式学 K8s 时不会太费劲。在这之前有两条路可以过渡。第一先在本地把 Compose 的用法练熟尤其是环境变量注入、健康检查、资源限制这几个生产要素。第二学一下 container 的容器编排命令K8s 的很多设计思路和它一脉相承。我个人学习过程中最有价值的一个经验是给自己设定一个小目标比如把手上一个原本手工部署的项目完整容器化比刷一百遍文档都管用。从最简单的单服务开始逐渐增加数据库、缓存、队列、消息中间件最后你会发现容器化开始成为你的直觉。第一次踩坑、第一次排查问题时你都是在建立属于自己的容器思维——这种思维比背下所有命令都重要得多。
返回列表