当前位置: 首页 > news >正文

云原生核心技术 (4/12): Docker 进阶:镜像优化实战与 Docker Compose 揭秘

摘要

在本篇进阶教程中,我们将直面 Docker 镜像体积过大的普遍痛点,并学习两大核心优化策略:选择更小的基础镜像和使用多阶段构建 (Multi-stage builds)。通过一个真实的 Go 语言 Web 应用案例,你将亲眼见证如何将一个数百MB的镜像优化到不足10MB。接着,我们将揭秘 Docker Compose,一个能通过简单的 YAML 文件定义和管理多容器应用的编排利器。你将学会编写 docker-compose.yml 文件,实现一键启动、管理和连接一个包含 Web 应用和 Redis 缓存的复杂服务组合,从而大幅提升开发和测试效率。


引言:从“能用”到“好用”

在上一篇文章中,我们成功地将一个简单的 Web 应用打包成了 Docker 镜像并运行了起来。这非常棒,你已经掌握了 Docker 的基础工作流。但是,在真实的生产环境中,“能用”只是起点,“好用”才是我们的追求。

你是否思考过这些问题:

  • “我构建的镜像是不是太大了?每次上传和下载都要等好久。”
  • “如果我的应用依赖数据库、缓存等多个服务,每次都要手动 docker run 好几个容器,还要处理网络连接,太麻烦了!”

如果你有这些困惑,那么恭喜你,这篇文章就是为你准备的。今天,我们将学习两大 Docker 进阶神技:镜像优化多容器编排 (Docker Compose),让你的 Docker 实践能力再上一个新台阶!


一、镜像优化黄金法则:让你的镜像“瘦身”90%

臃肿的镜像不仅浪费存储空间,更会拖慢 CI/CD 流水线中的构建、推送和拉取速度。下面我们通过一个 Go 语言的例子,来实战镜像优化。

为什么用 Go? 因为 Go 是编译型语言,它能完美地展示编译环境和运行环境分离所带来的巨大优化效果。这个思想同样适用于 Java, C++, Rust 等其他编译型语言。

1. 准备一个简单的 Go Web 应用

在你的工作区创建一个新文件夹,比如 go-docker-optimize,并在其中创建 main.go 文件:

// main.go
package mainimport ("fmt""net/http"
)func helloHandler(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello, Optimized Docker Image!")
}func main() {http.HandleFunc("/", helloHandler)fmt.Println("Server started at :8080")http.ListenAndServe(":8080", nil)
}
2. “臃肿”的初版 Dockerfile

一个新手可能会这样写 Dockerfile

# Dockerfile.bad
FROM golang:1.19  # 使用官方的 Go 镜像作为基础,它很大 (约 800MB)WORKDIR /appCOPY . .# 在镜像中编译 Go 应用
RUN go build -o main .EXPOSE 8080CMD ["./main"]

让我们构建它:

docker build -t go-app:bad -f Dockerfile.bad .
docker images go-app:bad

你会发现,这个镜像的大小可能在 800-900MB 左右!太可怕了。这是因为它包含了完整的 Go SDK、编译器和各种工具链,而我们运行应用时,其实只需要那个小小的、编译好的二进制文件。

3. 优化策略一:选择更小的基础镜像

一个简单的改进是使用基于 Alpine Linux 的 Go 镜像,它体积更小。

# Dockerfile.better
FROM golang:1.19-alpine
# ... 其他内容不变 ...

构建后你会发现,镜像体积可能降到了 300-400MB 左右。有进步,但还不够!

4. 优化策略二:多阶段构建 (Multi-stage builds) - 终极武器!

这是镜像优化的核心技巧。我们可以在同一个 Dockerfile 中定义多个构建阶段。第一个阶段(我们称之为 builder)使用包含完整工具链的镜像来编译应用,第二个阶段则使用一个极小的基础镜像(比如 alpinescratch),只从第一个阶段复制我们真正需要的编译结果。

修改你的 Dockerfile (可以就叫 Dockerfile 了),写入以下内容:

# ===== Stage 1: Build =====
# 使用官方的 Go 镜像作为构建环境,并给它起个别名 'builder'
FROM golang:1.19-alpine AS builder# 设置工作目录
WORKDIR /app# 复制 Go 模块文件并下载依赖 (如果你的项目有 go.mod)
# COPY go.mod ./
# COPY go.sum ./
# RUN go mod download# 复制所有源码
COPY . .# 编译应用,-o 指定输出文件名。CGO_ENABLED=0 是为了静态编译,避免依赖 C 库。
RUN CGO_ENABLED=0 go build -o main .# ===== Stage 2: Run =====
# 使用一个极度精简的基础镜像
FROM alpine:latest# 设置工作目录
WORKDIR /app# 从 'builder' 阶段的 /app/main 路径,复制编译好的二进制文件到当前阶段的 /app/ 目录
COPY --from=builder /app/main .# 暴露端口
EXPOSE 8080# 启动命令
CMD ["./main"]

现在,让我们用这个终极版 Dockerfile 来构建:

docker build -t go-app:optimized .
docker images go-app:optimized

查看镜像大小,你会惊喜地发现,它的大小只有 10-15MB

我们实现了超过 98% 的体积缩减! 这就是多阶段构建的威力。它完美地将“开发编译环境”和“生产运行环境”隔离开,只保留了运行应用所必需的最小集合。


二、多容器编排:Docker Compose 闪亮登场

现在,我们的应用镜像已经足够“苗条”了。但现代应用很少是孤立存在的,它们通常需要与数据库(如 MySQL)、缓存(如 Redis)等服务交互。

如果手动管理这些容器,你需要:

  1. docker run redis
  2. docker run mysql (并配置一堆环境变量)
  3. docker run my-app (并想办法让它连接到 redis 和 mysql 容器)

这个过程繁琐且容易出错。Docker Compose 就是为了解决这个问题而生的。它允许你使用一个 YAML 文件 (docker-compose.yml) 来定义和运行一个多容器的 Docker 应用。

1. 案例升级:Web 应用 + Redis 缓存

让我们修改一下之前的 Node.js 应用,让它能连接到 Redis。

a. 安装 Node.js 的 Redis 客户端:
在你的 my-docker-app 文件夹(第一篇文章创建的)中,打开终端,运行:

npm install redis

这会生成 package.jsonpackage-lock.json 文件。

b. 修改 app.js:

// app.js
const http = require('http');
const { createClient } = require('redis');const client = createClient({// 'redis' 是我们在 docker-compose.yml 中定义的服务名url: 'redis://redis:6379' 
});client.on('error', (err) => console.log('Redis Client Error', err));const hostname = '0.0.0.0';
const port = 3000;const server = http.createServer(async (req, res) => {res.statusCode = 200;res.setHeader('Content-Type', 'text/plain');try {// 每次访问,计数器加 1const counter = await client.incr('visitor_count');res.end(`Hello, Docker Compose! You are visitor #${counter}.\n`);} catch (err) {res.statusCode = 500;res.end('Failed to connect to Redis.\n');}
});async function startServer() {await client.connect();server.listen(port, hostname, () => {console.log(`Server running at http://${hostname}:${port}/`);});
}startServer();

c. 更新 Dockerfile 以安装依赖:

# Dockerfile for Node.js
FROM node:18-alpine
WORKDIR /app# 复制 package.json 和 package-lock.json
COPY package*.json ./# 安装生产环境依赖
RUN npm ci --only=production# 复制应用代码
COPY . .EXPOSE 3000
CMD ["node", "app.js"]
2. 编写 docker-compose.yml

现在是见证奇迹的时刻。在项目根目录下,创建 docker-compose.yml 文件:

# docker-compose.yml
version: '3.8' # 指定 compose 文件版本services: # 定义一组服务web: # 服务名,可以自定义,比如叫 'app'build: . # 指定构建上下文,即使用当前目录下的 Dockerfile 来构建镜像ports:- "8000:3000" # 端口映射,和 docker run -p 效果一样depends_on: # 声明依赖关系- redisredis: # 定义另一个服务,名为 'redis'image: "redis:alpine" # 直接使用 Docker Hub 上的官方 redis 镜像

这个文件清晰地定义了我们的应用栈:

  • 一个名为 web 的服务,它通过当前目录的 Dockerfile 构建,并将主机的 8000 端口映射到容器的 3000 端口。
  • 一个名为 redis 的服务,它直接使用 redis:alpine 官方镜像。
  • web 服务 depends_on redis,这保证了 redis 容器会先于 web 容器启动。
  • 关键点:在同一个 docker-compose 网络中,容器之间可以直接通过服务名 (redis) 作为主机名进行通信!这就是为什么 app.js 中可以使用 redis://redis:6379
3. 一键启动!

现在,只需要一条命令,就可以启动整个应用栈:

docker-compose up

(新版本的 Docker Desktop 可能推荐使用 docker compose up,中间没有 -)

你会看到 Docker Compose 先拉取 Redis 镜像,然后构建你的 web 应用镜像,最后依次启动 redisweb 两个容器。

打开浏览器访问 http://localhost:8000,刷新几次页面,你会看到访客计数不断增加!

常用 Docker Compose 命令:

  • docker-compose up -d: 后台启动并运行。
  • docker-compose down: 停止并移除所有相关的容器和网络。
  • docker-compose ps: 查看由 compose 管理的容器状态。
  • docker-compose logs -f web: 实时查看 web 服务的日志。

总结与预告

今天,你的 Docker 技能实现了质的飞跃:

  • 你掌握了通过多阶段构建等技巧,将镜像体积大幅优化的能力,这是生产环境必备的技能。
  • 你学会了使用 Docker Compose 来定义和管理多容器应用,告别了繁琐的手动 docker run,极大地提升了开发和测试效率。

至此,我们在单台机器上管理容器已经游刃有余。但是,当应用需要部署到由多台服务器组成的集群上,需要考虑跨主机的服务发现、负载均衡、故障自愈时,Docker 和 Docker Compose 就显得力不从心了。

这时,我们需要一个更强大的“容器舰队总司令”。

下一篇预告:【云原生核心技术 (5/12): 从 Docker 到 K8s——为什么你需要 Kubernetes 这个“容器管家”?】

我们将正式开启 Kubernetes (K8s) 的大门,理解它诞生的背景、核心价值和基本架构,为你解释为什么在云原生时代,K8s 是不可或缺的。准备好迎接容器编排之王吧!

相关文章:

  • uniapp+vue2+h5图片下载保存,微信浏览器、非微信浏览器
  • spark数据处理练习题番外篇【上】
  • Spring Boot集成Mina的Socket资源管理:从稳定通信到高性能优化
  • Windows上SSH连接Ubuntu失败
  • XWPFTemplate生成word
  • 置信水平、置信区间
  • 一体系数据平台的进化:基于阿里云 EMR Serverless Spark的持续演进
  • ESP32读取DHT11温湿度数据
  • 带eachers的html转word
  • 笔记 操作系统复习
  • 小程序的工具库-miniprogram-licia
  • AWS S3 SDK FOR JAVA 基本使用及如何兼容七牛云
  • 云计算——弹性云服务器(ECS)和裸金属服务器(BMS)
  • 小程序中的状态管理库-mobx-miniprogram
  • CentOS下的运维监控Grafana部署
  • 云计算——弹性云计算器(ECS)
  • 小程序动画性能提升指南:CSS硬件加速与JavaScript动画框架对比
  • Docker 运行 Kafka 带 SASL 认证教程
  • CARSIM-车速、油门、刹车练习
  • 破界协同:解锁电商平台混合云架构的双引擎效能
  • 如何用使用好wordpress/肇庆seo排名外包
  • 做图的模板下载网站有哪些/百度seo搜索排名
  • 做代购有哪些网站有哪些/香港旺道旺国际集团
  • wordpress 查看版本/企业seo培训
  • 销售网站建设公司/上海疫情最新情况
  • wordpress破解key/百度seo排名培训 优化