> ## Documentation Index
> Fetch the complete documentation index at: https://docs.akria.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Day 30 Pt.3 | 终极交付与毕业指南

> 使用 Docker Compose 编排全栈监控系统，完成职业进阶

<Info>
  **实战收官**：代码写完了，但这只是开发的结束，运维的开始。今天我们将把 Python Master 和 MySQL 数据库装进 Docker 容器，并使用 Docker Compose 进行**微服务编排**。最后，我们将进行全链路联调，验证这个分布式系统的运作。
</Info>

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="book" size={26} color="#059669" /> 学习内容 (30 mins)</span>

<AccordionGroup>
  <Accordion title="1. Docker Compose 编排 (15 mins)" icon="cubes-stacked">
    **为什么不写 Dockerfile？**

    * Dockerfile 只能定义一个镜像（比如 Master）。
    * 但我们的系统需要 Master + MySQL 配合工作。
    * **Compose 的价值**: 它像一个剧本（YAML），定义了所有角色（Services）、它们之间的关系（Depends\_on）、网络（Networks）和数据卷（Volumes）。
  </Accordion>

  <Accordion title="2. 容器健康检查 (Healthcheck) (15 mins)" icon="heart-pulse">
    **启动顺序的坑**

    * **问题**: 如果你在 Compose 里写了 `depends_on: db`，Docker 只会保证 db 容器启动了就启动 master。但 db 容器启动不代表 MySQL 进程准备好接客了（可能还在初始化数据）。这会导致 Master 连接由于 Connection Refused 崩溃。
    * **解决**: 使用 `healthcheck`。Docker 会定期 ping 数据库，直到数据库返回 "I'm OK"，由于配置了 `condition: service_healthy`，Master 才会正式启动。这是一个生产级的最佳实践。
  </Accordion>
</AccordionGroup>

***

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="code" size={26} color="#059669" /> 交付任务 (90 mins)</span>

<Steps>
  <Step title="步骤 1: 容器化 Master 服务">
    在 `master/` 目录下编写 `Dockerfile`。

    <CodeGroup>
      ```dockerfile master/Dockerfile theme={null}
      # 1. 基础镜像：使用 Python 3.9 轻量版
      FROM python:3.9-slim

      # 2. 设置工作目录
      WORKDIR /app

      # 3. 依赖层缓存优化
      # 先只拷贝 requirements.txt，这样如果代码变了但依赖没变，这层 cached 可以复用
      COPY requirements.txt .

      # 4. 安装依赖
      # --no-cache-dir 减小镜像体积，不要缓存 pip 包
      RUN pip install --no-cache-dir -r requirements.txt

      # 5. 拷贝业务代码
      COPY . .

      # 6. 声明端口（仅文档作用）
      EXPOSE 8000

      # 7. 启动命令
      CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
      ```
    </CodeGroup>
  </Step>

  <Step title="步骤 2: 编写编排文件">
    在项目根目录 `~/final-project/` 创建 `docker-compose.yaml`。

    <CodeGroup>
      ```yaml docker-compose.yaml theme={null}
      version: '3.8'

      services:
        # --- 数据库服务 ---
        db:
          image: mysql:8.0
          container_name: monitor_db
          environment:
            MYSQL_ROOT_PASSWORD: root
            MYSQL_DATABASE: monitor_system
          ports:
            - "3306:3306"
          volumes:
            # 数据持久化：防止容器重启数据丢失
            - mysql_data:/var/lib/mysql
          # 健康检查：每 10s ping 一次 mysql，成功 5 次才算健康
          healthcheck:
            test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
            interval: 10s
            timeout: 5s
            retries: 5

        # --- Master 控制面 ---
        master:
          build: ./master # 指向 Dockerfile 所在目录
          container_name: monitor_master
          ports:
            - "8000:8000"
          depends_on:
            # 关键：等待数据库健康了再启动
            db:
              condition: service_healthy
          environment:
            # 服务发现：Host 直接写 Service Name 'db'
            - DB_URL=mysql+aiomysql://root:root@db/monitor_system

      volumes:
        mysql_data:
      ```
    </CodeGroup>
  </Step>

  <Step title="步骤 3: 全链路联调 (The Final Run)">
    这是见证奇迹的时刻。

    <CodeGroup>
      ```bash 启动与测试 theme={null}
      # 1. 一键启动后端设施
      # -d 后台运行
      # --build 强制重新构建镜像（防止代码更新了镜像没更）
      docker-compose up -d --build

      # 2. 查看日志确保正常
      # 这一步非常重要，一定要看到 "Uvicorn running on..."
      docker-compose logs -f master

      # 3. 启动本地 Agent (模拟业务机器)
      # 打开一个新的终端窗口
      cd agent && go run main.go
      ```
    </CodeGroup>

    **验证检查清单**:

    * [ ] Master 日志：显示 `CREATE TABLE` 相关 SQL，无报错。
    * [ ] Agent 终端：持续输出 `✅ Sent`。
    * [ ] 浏览器：访问 `http://localhost:8000/api/v1/top`，能看到刚刚上报的数据（ID 应该是递增的）。
    * [ ] 数据流转：CPU 负载变化时，API 返回的值也跟着变化。

    **常见错误**：

    <Warning>
      * ❌ **Access denied for user 'root'**: 检查 docker-compose.yaml 里的密码和 Master 代码里的连接串密码是否一致。
      * ❌ **Connection Refused**: 可能是数据库还没启动完成 Master 就抢跑了。检查 `healthcheck` 配置是否生效。
      * ❌ **Docker 网络不通**: 确保 master 和 db 在同一个 compose 文件里，或者手动指定了 network。
    </Warning>
  </Step>
</Steps>

***

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="lightbulb" size={26} color="#059669" /> 拓展任务 (30 mins)</span>

<CardGroup cols={2}>
  <Card title="Agent 要不要容器化？" icon="box">
    **思考**：为什么我们不把 Agent 也写进 docker-compose？

    **答案**：
    不建议。监控 Agent 主要是采集宿主机的 CPU/磁盘。如果放在容器里，默认看到的是容器内的环境。如果要采集宿主机，需要挂载 `/proc`、`/sys` 并开启 `pid: host` 模式，这反而增加了部署复杂度。对于 Agent，直接跑二进制最简单。
  </Card>

  <Card title="可视化进阶" icon="chart-pie">
    **挑战**：JSON 数据不好看，老板要看图表。

    **任务**：在 compose 里加一个 Grafana 服务。配置 MySQL 数据源，直接读 `monitor_system` 库，画一个 CPU 曲线图。这就真的变成商业级产品了。
  </Card>
</CardGroup>

***

## 🎓 30 天毕业致辞：你的蜕变

<CardGroup cols={1}>
  <Card title="Congratulations! 🎉" color="#10b981">
    在过去的 30 天里，你完成了从“脚本小子”到“全栈运维开发”的蜕变：

    1. **Week 1 (基石)**: 掌握 Shell 自动化与 SQL 数据设计。
    2. **Week 2 (Python)**: 学会用 Python 处理复杂逻辑与 ChatOps。
    3. **Week 3 (API)**: 掌握 FastAPI 构建标准 Web 服务。
    4. **Week 4 (Go)**: 突破性能瓶颈，掌握高并发与二进制分发。
    5. **Week 5 (Fusion)**: 亲手缝合了这一切，构建了一个完整的分布式监控系统。

    **Keep building, keep learning.** 未来的架构师，加油！
  </Card>
</CardGroup>

***

<CardGroup cols={2}>
  <Card title="回顾本周" href="/training/30daystudy/week-5">
    第 05 周 | 终极融合
  </Card>

  <Card title="回到首页" href="/training/30daystudy/overview">
    查看完整知识图谱
  </Card>
</CardGroup>
