☰
容器技术与应用实战:从镜像构建到微服务编排的落地路线图
2026/9/29 12:17:54 网站建设 项目流程

简介:这是一份面向微服务开发学习者与企业内训场景的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 Engine,Windows 上装 Docker Desktop,装完用下面这条命令验证:

docker version docker info

docker 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 prune

docker 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_ENABLED=0 go build -o /out/app . # 运行阶段,只拷贝编译产物 FROM alpine:3.19 WORKDIR /app COPY --from=builder /out/app . EXPOSE 8080 CMD ["./app"]

逻辑说明:AS builder给构建阶段命名,运行阶段用COPY --from=builder只取产物。CGO_ENABLED=0关闭 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 -v

5.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-V,BIOS 里虚拟化没开,或者 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 --from=builder /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:MaxRAMPercentage=75控制堆占比;Spring Boot 应用开启懒加载减少启动时扫描。数据库容器首次启动慢是初始化数据目录,用命名卷持久化后第二次启动就快了。

验证镜像是否真的瘦了,别只看docker images的数字,用docker history <镜像名>看每一层大小,找出最大的那层针对性优化。我自己的习惯是每次改完 Dockerfile 都跑一遍docker history,哪层突然变大一眼就能看出来。这套流程踩过几次坑之后基本就固化了:先多阶段,再精简基础镜像,最后合并 RUN 并清缓存,三步下来镜像体积通常能砍掉七成以上。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询