容器技术与应用实战:从镜像构建到微服务编排的落地路线图
发布时间:2026/9/29 12:18:18 锦皓数字建站

简介这是一份面向微服务开发学习者与企业内训场景的Docker容器技术课件属于云计算与云原生系列课程中的第二讲适合具备一定Linux基础的初中级开发人员、架构师及培训讲师使用。课件围绕容器技术展开涵盖Docker基础概念、镜像与容器操作、Dockerfile镜像制作、数据卷与网络管理、底层技术原理以及Docker Compose服务编排等核心模块重点讲解Images与Container对象操作、Dockerfile构建镜像及Compose编排等难点内容并延伸至Namespace隔离、Cgroup资源限制、Union FS文件系统等底层设计思想。资源包共1个pptx文件大小约12.06MB内容结构完整、无需修改可直接用于自学或企业培训。目前已有277人学习下载配合整套微服务与分布式课件能帮助读者系统掌握容器化部署与编排能力为后续K8S与SpringCloud学习打下基础。1. 容器技术与应用从一份 PPT 标题拆出的落地路线图很多人第一次接触容器技术是从一份名为《容器技术与应用》的课件开始的。标题看着像纯理论但真正落到工作里它对应的是一串很具体的问题镜像怎么构建才不臃肿、容器启动后网络为什么不通、微服务拆完怎么在本地联调、Docker Desktop 在 Windows 上装完为什么起不来。容器不是虚拟机它靠命名空间做资源隔离、靠联合文件系统做分层镜像这两点决定了后面所有的操作习惯和踩坑方式。这份内容适合两类人一类是要把单体应用拆成微服务、需要一套可复现运行环境的后端工程师另一类是刚接手 CI/CD、被要求把构建产物容器化的运维或全栈。接下来按「概念立住 → 本地跑通 → 镜像与网络 → 微服务落地 → 排错 → 进阶技巧」的顺序推进每一步都给可抄的命令和参数。2. 容器、镜像与微服务先把三个概念的关系理清2.1 镜像、容器、仓库到底谁是谁镜像是一个只读模板容器是镜像跑起来之后的那个可写实例仓库是存放镜像的地方。三者关系可以用一句话记住镜像负责「长什么样」容器负责「正在跑」仓库负责「从哪来」。镜像采用分层结构每一条 Dockerfile 指令生成一层层与层之间共享所以多个镜像如果基于同一个基础镜像磁盘上只存一份底层。这也是为什么拉取一个基于ubuntu的镜像时日志里会显示某些层Already exists——不是重复下载是复用。容器在镜像之上加了一个可写层所有运行时产生的文件改动都落在这个可写层里。容器一删可写层就没了这就是「容器无状态」这个说法的来源。理解这一点后面挂载数据卷、做持久化就不会觉得是多余操作。微服务和容器的关系是微服务是一种架构拆分方式容器是承载每个微服务进程最顺手的运行单元。一个微服务一个容器独立构建、独立部署、独立扩缩这才是容器在微服务场景里真正的价值而不是「把单体塞进一个容器里假装微服务」。2.2 为什么选容器而不是虚拟机虚拟机模拟的是硬件每台虚机跑一个完整操作系统内核启动要几十秒内存开销按 GB 算。容器共享宿主机内核只隔离进程视角启动在秒级甚至毫秒级内存开销按 MB 算。这个差异直接决定了部署密度和弹性速度。对比项虚拟机容器隔离级别硬件级独立内核进程级共享内核启动时间数十秒秒级以内单机密度几个到十几个几十到上百个镜像体积GB 级MB 级常见适用场景强隔离、异构系统微服务、快速迭代选型结论很直接需要跑不同内核版本、或者安全隔离要求极高的场景用虚拟机应用交付、微服务、CI/CD 流水线用容器。两者不是替代关系生产上常见的是虚机里跑容器。2.3 本地跑通第一个容器的完整命令先确认环境。Linux 上装 Docker EngineWindows 上装 Docker Desktop装完用下面这条命令验证docker version docker infodocker version看客户端和服务端是否都通如果只显示 Client 没有 Server说明守护进程没起来。docker info看存储驱动、Cgroup 版本、可用内存这几个信息在排查问题时经常要用。拉一个基础镜像并跑起来# 拉取 ubuntu 22.04 镜像 docker pull ubuntu:22.04 # 以交互模式启动容器退出后自动删除 docker run -it --rm ubuntu:22.04 /bin/bash # 在容器内执行命令后直接退出 docker run --rm ubuntu:22.04 echo hello container-it是-i保持标准输入加-t分配伪终端交互式调试必加。--rm让容器退出即删避免留下一堆停止状态的容器占名字。ubuntu:22.04里的22.04是标签不写标签默认拉latest生产上强烈建议写死版本否则某天基础镜像更新会导致构建结果不可复现。查看和管理容器# 查看运行中的容器 docker ps # 查看所有容器包括已停止的 docker ps -a # 停止并删除指定容器 docker stop 容器ID docker rm 容器ID # 清理所有停止的容器 docker container prunedocker ps默认只显示运行中的很多人第一次找不到自己刚退出的容器就是漏了-a。容器 ID 支持前缀匹配输入前三四位就够了不用复制完整哈希。3. 镜像构建与 Dockerfile把应用打包成可复现的产物3.1 Dockerfile 常用指令与执行顺序Dockerfile 是从上往下逐条执行的每条指令生成一层。常用指令有FROM、WORKDIR、COPY、RUN、ENV、EXPOSE、CMD、ENTRYPOINT。写 Dockerfile 的核心原则是把变化频率低的内容放前面变化频率高的放后面这样能最大化利用构建缓存。一个典型的 Java 应用 Dockerfile# 基础镜像写死版本保证可复现 FROM openjdk:17-jdk-slim # 设置工作目录后续指令都在这个目录下执行 WORKDIR /app # 先只拷贝依赖描述文件利用缓存 COPY pom.xml . RUN mvn dependency:go-offline -B # 再拷贝源码并构建 COPY src ./src RUN mvn package -DskipTests # 暴露端口仅作文档说明 EXPOSE 8080 # 容器启动命令 CMD [java, -jar, target/app.jar]逻辑说明先COPY pom.xml再RUN mvn dependency:go-offline是为了让依赖下载单独成层。只要pom.xml没变这一层就命中缓存改源码时不会重新下载依赖。如果一上来就COPY . .任何文件改动都会让依赖层失效构建时间翻几倍。参数说明openjdk:17-jdk-slim是精简版 JDK 镜像比完整版小几百 MB-B让 Maven 用批处理模式日志更干净-DskipTests跳过测试测试应该在 CI 的独立阶段跑不该塞进镜像构建。3.2 多阶段构建把镜像体积压下来Java、Go、前端项目构建时需要编译器和依赖运行时只需要产物。多阶段构建让构建环境和运行环境分离最终镜像只保留运行所需内容。# 构建阶段 FROM golang:1.21 AS builder WORKDIR /src COPY . . RUN CGO_ENABLED0 go build -o /out/app . # 运行阶段只拷贝编译产物 FROM alpine:3.19 WORKDIR /app COPY --frombuilder /out/app . EXPOSE 8080 CMD [./app]逻辑说明AS builder给构建阶段命名运行阶段用COPY --frombuilder只取产物。CGO_ENABLED0关闭 CGO生成静态链接的二进制才能在 alpine 这种没有 glibc 的镜像里跑。参数说明alpine基础镜像只有几 MB但用的是 musl libc和 glibc 不完全兼容遇到问题可以换成debian:stable-slim。多阶段构建后Go 应用镜像通常能压到 20MB 以内Java 应用也能从 800MB 降到 200MB 左右。3.3 构建、打标签与推送的完整流程# 构建镜像并打标签 docker build -t myapp:1.0.0 . # 查看本地镜像 docker images # 给镜像打上仓库地址标签 docker tag myapp:1.0.0 registry.example.com/team/myapp:1.0.0 # 推送到镜像仓库 docker push registry.example.com/team/myapp:1.0.0逻辑说明-t指定镜像名和标签.是构建上下文路径Docker 会把该目录下所有文件打包发给守护进程。如果目录里有大量无关文件构建会变慢用.dockerignore排除。参数说明标签建议用语义化版本或 Git commit 短哈希不要只用latest。latest在多人协作时会导致「我本地能跑服务器上跑的是另一个版本」这类问题。推送前先docker login登录仓库。注意构建上下文过大会拖慢构建.dockerignore里至少排除.git、node_modules、target、*.log。4. 容器网络与数据卷让容器能连上、数据不丢4.1 四种网络模式与选型Docker 默认提供 bridge、host、none、container 四种网络模式。默认是 bridge容器通过虚拟网桥和宿主机通信有独立 IP。# 查看网络列表 docker network ls # 创建自定义 bridge 网络 docker network create mynet # 启动两个容器加入同一网络 docker run -d --name redis --network mynet redis:7 docker run -d --name app --network mynet myapp:1.0.0 # 在 app 容器内测试连通性 docker exec -it app ping redis逻辑说明自定义 bridge 网络内置 DNS容器之间可以直接用容器名互相访问不用记 IP。默认 bridge 网络没有这个能力只能靠--link已废弃或 IP所以生产上建议自建网络。参数说明--network mynet指定网络docker exec进入运行中的容器执行命令。host 模式容器直接用宿主机网络栈性能最好但端口会冲突none 模式没有网络适合纯计算任务。4.2 端口映射与常见网络不通排查# 把宿主机 8080 映射到容器 80 docker run -d -p 8080:80 --name web nginx:alpine # 查看端口映射 docker port web逻辑说明-p 宿主机端口:容器端口顺序不能反。容器内服务监听的是容器端口外部访问走宿主机端口。网络不通的排查顺序先docker ps确认容器在跑再docker exec进容器curl localhost:端口确认服务本身正常然后docker port看映射对不对最后从宿主机curl localhost:宿主机端口。如果容器内通、宿主机不通多半是映射写反或防火墙拦截。4.3 数据卷与目录读写权限容器可写层随容器删除而消失需要持久化的数据必须挂载出来。# 命名卷由 Docker 管理存储位置 docker run -d --name mysql -v mysql-data:/var/lib/mysql mysql:8.0 # 绑定挂载把宿主机目录挂进容器 docker run -d --name web -v /data/html:/usr/share/nginx/html:ro nginx:alpine # 查看卷信息 docker volume ls docker volume inspect mysql-data逻辑说明命名卷适合数据库这类由 Docker 统一管理的场景绑定挂载适合开发时把本地代码挂进容器。:ro表示只读防止容器误改宿主机文件。参数说明绑定挂载时容器内进程的 UID 要和宿主机目录属主匹配否则会报权限拒绝。常见做法是启动时用--user $(id -u):$(id -g)指定用户或者提前chown宿主机目录。这是「docker 容器怎么赋予目录读写权限」这类问题最常见的根因。5. 微服务场景下的容器编排与联调5.1 用 Docker Compose 一次拉起整套依赖微服务本地联调最烦的是依赖多数据库、缓存、消息队列。一个个docker run又长又容易漏参数Compose 用一个 YAML 文件描述整套环境。version: 3.9 services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: demo ports: - 3306:3306 volumes: - mysql-data:/var/lib/mysql healthcheck: test: [CMD, mysqladmin, ping, -h, localhost] interval: 10s retries: 5 redis: image: redis:7 ports: - 6379:6379 app: build: . depends_on: mysql: condition: service_healthy environment: SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/demo ports: - 8080:8080 volumes: mysql-data:逻辑说明depends_on配合condition: service_healthy让 app 等 mysql 健康检查通过再启动避免应用启动时数据库还没就绪导致连接失败。服务之间用服务名当主机名mysql:3306里的mysql就是服务名。参数说明healthcheck的interval是检查间隔retries是失败重试次数。数据库首次初始化较慢retries给到 5 到 10 比较稳。启动和查看# 后台启动全部服务 docker compose up -d # 查看服务状态 docker compose ps # 查看某个服务日志 docker compose logs -f app # 停止并删除容器保留卷 docker compose down # 连卷一起删 docker compose down -v5.2 微服务拆分的容器化边界微服务拆分不是越细越好。容器化之后每个服务一个镜像拆分粒度直接决定构建次数、部署复杂度和网络调用量。经验做法是按业务能力拆一个服务对应一个限界上下文不要按技术分层拆比如把 DAO 拆一个服务、Service 拆一个服务那样只会把一次本地调用变成一次网络调用。容器化时每个服务要独立可构建、独立可配置。配置通过环境变量注入不要写死在镜像里。镜像里只放代码和运行时配置、密钥、证书通过挂载或环境变量传入。这样同一个镜像能在开发、测试、生产环境复用。5.3 服务间调用与启动顺序联调微服务联调常见问题是启动顺序和注册发现。用 Compose 时服务名就是 DNS 名直接配http://服务名:端口即可。如果用了注册中心要确保注册中心先起来其他服务再注册。# 进入 app 容器测试对 mysql 的连通性 docker compose exec app sh -c nc -zv mysql 3306 # 查看容器间网络 docker network inspect 项目名_default逻辑说明nc -zv测试 TCP 连通性比 ping 更能反映端口是否可达。docker network inspect能看到每个容器分配到的 IP 和别名排查 DNS 解析问题很有用。参数说明Compose 默认创建的网络名是「项目目录名_default」项目名可以用-p指定。如果服务间调用超时先确认在同一个网络里再确认端口对。6. 容器落地避坑五条血泪经验6.1 镜像越构建越大现象每次构建镜像体积都在涨从几百 MB 涨到几个 GB。原因RUN指令里下载的临时文件、包管理器缓存没清理且都留在了同一层。解决把下载、安装、清理写在同一条RUN里用连接最后删缓存。Debian 系用rm -rf /var/lib/apt/lists/*Alpine 用rm -rf /var/cache/apk/*。更好的做法是多阶段构建构建工具根本不进最终镜像。6.2 容器启动就退出现象docker run -d之后docker ps看不到容器docker ps -a显示Exited (0)。原因容器的主进程执行完就退出了容器生命周期跟着主进程走。如果CMD是一个前台不常驻的命令容器自然就停。解决确保CMD或ENTRYPOINT启动的是前台进程。nginx 要用nginx -g daemon off;不要用service nginx start。排查时先docker logs 容器ID看输出。6.3 数据卷权限拒绝现象容器内进程报Permission denied日志里是写文件失败。原因容器内进程 UID 和宿主机挂载目录属主不一致。解决要么在 Dockerfile 里创建对应用户并chown要么启动时--user指定 UID要么提前把宿主机目录属主改成容器内用户。数据库镜像尤其常见MySQL 官方镜像默认用mysql用户挂载目录属主必须是 999。6.4 Docker Desktop 在 Windows 上起不来现象Docker Desktop 启动卡住提示virtualization support not detected或failed to start。原因Windows 上 Docker Desktop 依赖 WSL2 或 Hyper-VBIOS 里虚拟化没开或者 WSL2 没装。解决进 BIOS 打开虚拟化Intel VT-x / AMD-V在「启用或关闭 Windows 功能」里勾选「虚拟机平台」和「适用于 Linux 的 Windows 子系统」命令行执行wsl --update更新内核。装完重启再开 Docker Desktop。6.5 容器网络不通但配置看着没错现象容器内能 ping 通外网但访问另一个容器失败。原因两个容器不在同一个自定义网络里或者用了默认 bridge 网络没有 DNS。解决docker network inspect确认两个容器在同一个网络不在就docker network connect接进去或者重建时统一--network。默认 bridge 网络不支持容器名解析这是最容易忽略的一点。7. 进阶技巧镜像瘦身与启动加速的实操镜像瘦身最有效的三招按收益排序多阶段构建、选精简基础镜像、合并 RUN 层并清理缓存。多阶段构建前面讲过这里补一个前端项目的例子因为前端镜像最容易失控。# 构建阶段 FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # 运行阶段用 nginx 托管静态文件 FROM nginx:alpine COPY --frombuilder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80逻辑说明npm ci比npm install更适合 CI它严格按package-lock.json安装结果可复现且更快。构建产物dist拷进 nginx 镜像node 和 node_modules 都不进最终镜像体积能从 1GB 降到 50MB 以内。参数说明node:20-alpine是精简版如果构建时遇到原生模块编译问题换成node:20-slim。nginx.conf单独拷贝方便改配置不用重新构建。启动加速方面容器启动慢通常卡在应用初始化不是容器本身。几个可调的点JVM 应用加-XX:UseContainerSupport让 JVM 正确识别容器内存限制避免按宿主机内存算堆大小用-XX:MaxRAMPercentage75控制堆占比Spring Boot 应用开启懒加载减少启动时扫描。数据库容器首次启动慢是初始化数据目录用命名卷持久化后第二次启动就快了。验证镜像是否真的瘦了别只看docker images的数字用docker history 镜像名看每一层大小找出最大的那层针对性优化。我自己的习惯是每次改完 Dockerfile 都跑一遍docker history哪层突然变大一眼就能看出来。这套流程踩过几次坑之后基本就固化了先多阶段再精简基础镜像最后合并 RUN 并清缓存三步下来镜像体积通常能砍掉七成以上。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。