Web 平台开发日记

51 阅读16分钟

Web 平台开发日记 - 第一章:从零开始的架构设计与技术选型

系列导读: 本系列记录了个人实战项目的完整开发过程。通过结合自己掌握的技术栈,完整记录从设计、架构、实现到部署的全过程,并总结开发中的经验教训。

项目性质: 个人开源学习项目
当前阶段: 基础框架与基础设施(已完成)


一、项目背景

1.1 项目初心

这是个人实战项目,主要目的是:

  • 🎯 技术实践 - 结合自己掌握的技术栈(Go + Vue 3)进行深度实践
  • 📝 记录过程 - 完整记录从设计、架构、实现到部署的全过程
  • 🧪 测试框架 - 在一个完整的项目中验证各种技术选择的可行性
  • 📚 学习总结 - 通过博客系列分享开发过程中的经验教训

1.2 项目定位

项目名称: Enterprise Web Platform (EWP)
项目性质: 开源、学习、实战
技术栈: Go 1.22 + Vue 3 + MySQL 8.4 + Redis 7
目标: 构建一个具有企业级特性的完整 Web 应用平台

这不是为了商业用途,而是为了在实际开发中深入理解:

  • 如何从零开始设计应用架构
  • 如何做出合理的技术选型决策
  • 如何搭建完整的开发、部署、监控体系
  • 如何编写可维护、可扩展的代码

1.3 核心特点

特点说明
开源免费100% MIT 许可证,无商业限制
技术栈现代采用最新的主流开源技术
完整度高包含前端、后端、基础设施、监控
易于学习清晰的代码结构、详细的文档
可部署性强支持本地开发、单机部署、容器化部署

二、技术选型

技术选型是项目成功的基础。我们的选型过程遵循了"需求驱动"的原则,即:先明确需求,再根据需求评估候选方案

2.1 选型评估框架

我们建立了一个多维度的评估框架:

选型评估 = 需求满足度 + 生态完善度 + 学习成本 + 长期维护性 + 许可证合规性

2.2 后端技术选型

📋 核心需求
  • 支持 RESTful API 快速开发
  • ORM 能力完善(自动迁移、关联查询等)
  • 内置中间件系统(认证、CORS、日志等)
  • 热部署支持(开发阶段)
  • 性能优异
🎯 候选方案评估

方案 A: 使用现成的完整项目模板

优点: ✅ 功能完善,开发快速
优点: ✅ 文档丰富,社区活跃
缺点: ❌ 通常包含授权限制或商业条款
缺点: ❌ 定制空间有限,架构固定
缺点: ❌ 难以理解底层设计细节
结论: 对于学习和深度定制不合适 ✗

方案 B: Go Web 框架选型

我重点对比了三个主流的 Go Web 框架:

指标GinFiberEcho
性能⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
学习曲线简单中等中等
中间件系统⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
社区规模最大中等较小
生产应用广泛快速增长适中
文档质量优秀优秀良好
开发速度极快

Gin 框架深入分析

// 简洁的路由定义
func main() {
    router := gin.Default()
    
    // 基本路由
    router.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{"message": "pong"})
    })
    
    // 路由组
    v1 := router.Group("/api/v1")
    {
        v1.POST("/users", createUser)
        v1.GET("/users/:id", getUser)
    }
    
    router.Run(":8080")
}

选择 Gin 的原因:

  • 🏆 Go 社区中最流行和成熟的框架
  • ⚡ 性能在三者之间均衡(足够快,但不过度优化)
  • 📚 文档最完善,教程和案例最多
  • 🔧 中间件系统完整,易于扩展
  • 🏢 企业级应用最广泛(字节跳动、小米等都在用)
  • 🎯 学习成本最低,对初学者友好
  • 📦 与 GORM、Viper 等库的集成最成熟

Fiber vs Gin:

Fiber 的优势:
  ✅ 性能最高(基于 Fasthttp,并发能力最强)
  ✅ Express.js 风格,对 Node.js 开发者友好
  ✅ 开发速度快,API 简洁直观

Fiber 的劣势:
  ❌ 社区相对较小
  ❌ 文档和教程相对较少
  ❌ 与其他 Go 库的生态集成度不如 Gin
  ❌ 在中等并发下,性能优势不明显

Echo vs Gin:

Echo 的优势:
  ✅ 高性能(与 Gin 相当)
  ✅ 中间件系统完整
  ✅ 数据绑定和验证功能强大

Echo 的劣势:
  ❌ 社区规模和热度不如 Gin
  ❌ 文档质量一般
  ❌ 在国内使用较少,遇到问题难以找到解决方案

最终结论:

对于这个项目,选择 Gin 是最优选择,因为:
1. 最成熟稳定,适合学习和参考
2. 社区最活跃,问题最容易解决
3. 文档最完善,教程资源最丰富
4. 与 Go 生态库的集成最好
5. 对后期维护和扩展最有利

方案 C: 完全自定义框架

优点: ✅ 最大灵活性
缺点: ❌ 开发周期长(6+ 个月)
缺点: ❌ 重复发明轮子
缺点: ❌ 维护成本高,容易埋坑
结论: 对于学习项目不经济 ✗

最终技术栈选择

后端框架: Gin (MIT)
  ✅ Go 生态中最成熟的 Web 框架
  ✅ 完整的中间件支持
  ✅ 高性能且性能均衡
  ✅ 活跃的社区和丰富的教程
  
ORM: GORM v2 (MIT)
  ✅ Go 最完善的 ORM 框架
  ✅ 自动迁移功能(省去手写 SQL)
  ✅ 强大的关联查询能力
  ✅ 企业级生产应用广泛使用
  
认证: golang-jwt/jwt (MIT)
  ✅ JWT 标准实现
  ✅ 支持多种算法
  ✅ 生态成熟
  
授权: Casbin (Apache 2.0 无商业限制)
  ✅ 支持 RBAC、ABAC 等多种模型
  ✅ 灵活的权限定义
  ✅ 高效的策略匹配
📊 后端选型总结
组件选择版本许可证理由
框架Gin1.x+MIT最成熟的 Go Web 框架,极简设计,高性能
ORMGORMv2MIT功能完善,自动迁移,关联支持完整
认证JWT-Go3.x+MITJWT 标准实现,配合自定义中间件
授权Casbin2.x+Apache 2.0灵活的权限模型,支持 RBAC、ABAC
日志Zap1.x+MIT高性能结构化日志,企业级使用广泛
配置Viper1.x+MIT支持热重载,多源配置,灵活强大
验证Validator10.x+MIT强大的结构体验证库,标签式定义
缓存Redis7+BSD高性能内存缓存,会话存储

2.3 前端技术选型

📋 核心需求
  • 现代化的开发体验(热更新、TypeScript 支持)
  • 企业级 UI 组件库
  • 权限管理和动态菜单
  • 完善的管理后台模板
  • 零许可证限制
🎯 候选方案评估

方案 A: Vue Pure Admin(最终选择)✅

Vue Pure Admin 是一款基于 Vue 3、Vite、Element-Plus、TypeScript 等最新技术栈开发的中后台管理系统模板。

技术栈:

  • ✅ Vue 3(核心框架)- 最新的 Vue 版本,Composition API
  • ✅ Element Plus(UI 库)- 国内最流行的企业级组件库
  • ✅ Vite(构建工具)- 极速开发体验和优化的生产构建
  • ✅ Pinia(状态管理)- Vue 3 官方推荐的状态管理库
  • ✅ Vue Router(路由)- 支持动态路由、权限控制
  • ✅ TypeScript - 完整的类型支持,提高代码质量
  • ✅ Tailwind CSS - 实用优先的 CSS 框架

为什么选它:

  • 开箱即用 - 包含登录、权限、菜单、表格等完整的后台管理功能
  • 生产级代码 - 遵循企业级最佳实践,代码规范清晰
  • 快速上手 - 作为后端开发者,可以直接使用框架边开发边学习前端
  • 完整示例 - 提供了丰富的业务组件和使用案例
  • 活跃维护 - 社区活跃,持续更新和改进
  • 灵活定制 - 架构清晰,易于扩展和定制
  • 适合团队协作 - 代码结构规范,新开发者易上手

结论: 对于以后端开发为主、希望快速搭建管理后台的项目,Vue Pure Admin 是最佳选择 ✓


📊 其他前端框架对比
方案技术栈学习成本上手速度定制灵活性适用场景
Vue Pure AdminVue3 + Element Plus + Vite⭐⭐⭐⭐⭐后端为主、快速交付
从零搭建 Vue 3基础库手工组装⭐⭐极高前端专家、深度学习
Ant Design VueAnt Design + Vue 3⭐⭐⭐国际化项目、复杂 UI
Vben AdminAnt Design + Vue 3⭐⭐⭐大型企业应用

关键区别:

  1. Vue Pure Admin vs 从零搭建

    • Vue Pure Admin: 生产级代码,包含完整功能,适合快速交付
    • 从零搭建: 学习价值最高,但开发周期长
  2. Vue Pure Admin vs Ant Design Vue

    • Vue Pure Admin 使用 Element Plus(国内常用)
    • Ant Design Vue 使用 Ant Design(国际化风格)
    • Vue Pure Admin 更轻量,Ant Design 功能更全面
  3. Vue Pure Admin vs Vben Admin

    • Vue Pure Admin: 相对轻量级,易上手
    • Vben Admin: 功能更强大,但复杂度更高
📊 前端选型总结
组件选择版本许可证理由
框架Vue3.4+MIT最新、易学习、生态成熟
UI 库Element Plus2.5+MIT企业级组件库,中文文档完善
构建工具Vite5.x+MIT极速开发、优化的生产构建
状态管理Pinia2.1+MITVue 3 官方推荐
路由Vue Router4.x+MIT支持动态路由、权限控制
HTTP 客户端Axios1.6+MIT功能完善、请求拦截器
管理模板Vue Pure Admin最新MIT开箱即用、生产级代码
CSS 框架Tailwind CSS最新MIT实用优先、响应式设计

2.4 基础设施选型

数据库

MySQL 8.4(升级从 5.7)

原本选择 MySQL 5.7,但开发环境为 Apple Silicon (ARM64)
❌ MySQL 5.7 不支持 ARM64 架构
✅ 升级到 MySQL 8.4 完全支持 ARM64
✅ 8.4 向后兼容 5.7,无迁移成本
✅ 更好的性能和安全特性

Redis 7

✅ 支持 ARM64
✅ 性能改进(Stream 增强等)
✅ 广泛使用的缓存和会话存储
容器化与编排

Podman(替代 Docker)

为什么选择 Podman?

安全优势:

  • ✅ 无需 root 权限(Rootless 容器)- 权限提升风险更低
  • ✅ 无守护进程 - 没有以 root 身份运行的后台服务
  • ✅ 进程隔离模型 - 单一容器出现问题不影响其他容器

运维优势:

  • ✅ 资源消耗少 - 无后台守护进程,内存占用更低
  • ✅ 轻量级 - 适合开发机和边缘部署
  • ✅ Kubernetes 原生支持 - Pod 概念直接映射到 K8s
  • ✅ 100% 开源 - RedHat 维护,无商业许可限制

开发体验:

  • ✅ 与 Docker API 完全兼容 - 所有 Docker 命令无需改动
  • ✅ 支持 Podman Compose - 等同于 Docker Compose
  • ✅ 更清晰的进程模型 - 便于理解容器生命周期

Docker 作为备选方案:

  • ⚠️ 需要 root 权限或 Docker 守护进程 - 提升权限风险
  • ⚠️ 后台守护进程 - 即使不使用也消耗系统资源
  • ⚠️ Docker Desktop 商业许可 - macOS/Windows 有企业许可考虑
  • ✅ 但仍完全支持(make docker-up

实际对比:

对比项PodmanDocker
Root 权限❌ 不需要⚠️ 需要
后台守护进程❌ 无✅ 有
内存占用✅ ~50MB⚠️ ~200MB+
API 兼容性✅ 100%✅ 原生
Pod 支持✅ 原生❌ 需扩展
开源✅ 完全✅ 完全
商业许可✅ 无⚠️ 有考虑

安装:

# macOS
brew install podman podman-compose
podman machine init
podman machine start

# Linux (Ubuntu/Debian)
sudo apt-get install podman podman-compose

# 验证安装
podman --version
podman ps

迁移到 Podman 很简单:

# 如果之前使用 Docker
make docker-down      # 停止 Docker 容器

# 改用 Podman
make podman-up        # 启动 Podman 容器

# 两者的命令完全相同,配置文件也相同
监控与可观测性

Prometheus + Grafana

✅ 开源免费
✅ 业界标准的监控方案
✅ 完全自托管(数据不离开客户基础设施)
✅ 支持本地部署

其他考虑
❌ SaaS 方案(Datadog、New Relic):数据隐私和成本问题
❌ 云原生方案(云平台内置监控):厂商锁定风险
API 网关

Nginx

✅ 业界标准,久经考验
✅ 开源免费
✅ 功能完善(SSL、限流、缓存等)
✅ 高性能低资源消耗

其他考虑
- Kong(更功能丰富但复杂度高)
- Traefik(Kubernetes 原生但不适合初期)

2.5 许可证合规性保证

这是选型中最关键的一环。选型的许可证检查清单:

依赖检查清单
├── 后端依赖
   ├── MIT: Gin, GORM, Zap, Viper, jwt-go, Validator 
   ├── Apache 2.0 (无商业限制): Casbin, Prometheus 
   ├── BSD: Redis 
   └──  GPL: 零个
├── 前端依赖
   ├── MIT: Vue, Vue Router, Pinia, Element Plus, Vite, Axios 
   └──  GPL: 零个
└── 基础设施
    ├── BSD: Nginx, Redis 
    ├── Apache 2.0: Docker, Prometheus 
    └──  GPL: 零个

结论:  100% 许可证合规,零商业限制

三、项目架构:整体设计与模块介绍

3.1 整体架构图

┌─────────────────────────────────────────────────────────────┐
│                     客户端浏览器                              │
│              (Vue 3 + Element Plus SPA)                      │
└────────────────────┬────────────────────────────────────────┘
                     │ HTTPS
                     ▼
┌─────────────────────────────────────────────────────────────┐
│                   Nginx API Gateway                           │
│  • SSL/TLS 终止                                              │
│  • 静态资源服务 (前端 SPA)                                    │
│  • API 请求代理                                              │
│  • 速率限制和安全头                                           │
└────────────────────┬────────────────────────────────────────┘
                     │ HTTP
                     ▼
┌─────────────────────────────────────────────────────────────┐
│              Gin Web 框架 (Go 后端)                           │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ 中间件层                                             │   │
│  │ • CORS 处理                                          │   │
│  │ • JWT 认证                                           │   │
│  │ • Casbin RBAC 授权                                   │   │
│  │ • 结构化日志 (Zap)                                    │   │
│  │ • Prometheus 指标收集                                │   │
│  └──────────────────────────────────────────────────────┘   │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ 路由处理层 (api/v1/*)                                │   │
│  │ • 认证管理 (/auth/login, /auth/logout)             │   │
│  │ • 用户管理 (/users/*, /roles/*)                    │   │
│  │ • 文件管理 (/files/upload, /files/download)        │   │
│  │ • 健康检查 (/health, /ready, /metrics)             │   │
│  └──────────────────────────────────────────────────────┘   │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ 业务逻辑层 (service/*)                               │   │
│  │ • 用户服务                                            │   │
│  │ • 权限服务                                            │   │
│  │ • 文件服务                                            │   │
│  └──────────────────────────────────────────────────────┘   │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ 数据模型层 (model/*)                                 │   │
│  │ • User (用户)                                        │   │
│  │ • Role (角色)                                        │   │
│  │ • Permission (权限)                                  │   │
│  │ • BaseModel (创建时间、更新时间等)                    │   │
│  └──────────────────────────────────────────────────────┘   │
└────────────┬────────────────────────────────┬────────────────┘
             │                                │
             ▼ 读写                           ▼ 缓存
    ┌─────────────────────┐         ┌─────────────────────┐
    │   MySQL 8.4         │         │   Redis 7           │
    │                     │         │                     │
    │ • 业务数据存储        │         │ • 会话存储           │
    │ • 用户认证信息        │         │ • 缓存数据           │
    │ • 权限关系           │         │ • 并发控制           │
    └─────────────────────┘         └─────────────────────┘

3.2 分层架构详解

我们采用了经典的三层架构 + 中间件 设计:

第一层:路由与中间件层 (router/middleware/)

职责:

  • 接收 HTTP 请求
  • 执行跨越多个处理器的操作(认证、日志、限流等)
  • 路由分发

关键文件:

server/
├── router/
│   └── router.go          # 路由注册
├── middleware/
│   ├── cors.go            # 跨域资源共享
│   ├── jwt.go             # JWT 认证 (待实现)
│   ├── rbac.go            # Casbin 授权 (待实现)
│   ├── logger.go           # 结构化日志 (待实现)
│   └── metrics.go          # Prometheus 指标 (待实现)

核心职责是接收 HTTP 请求、应用中间件(CORS、认证、日志等)、分发到对应的处理器。

第二层:业务逻辑层 (service/)

职责:

  • 实现具体的业务逻辑
  • 与数据模型交互
  • 独立于 HTTP 框架(可复用于 gRPC、CLI 等)

关键文件:

server/service/
├── user_service.go        # 用户相关业务逻辑
├── auth_service.go        # 认证相关业务逻辑
├── role_service.go        # 角色权限业务逻辑
└── file_service.go        # 文件上传下载业务逻辑

业务逻辑层独立于 HTTP 框架,包含实际的业务规则:数据验证、密码加密、缓存更新等。

第三层:数据模型层 (model/)

职责:

  • 定义数据结构
  • 与数据库表映射
  • 包含 GORM 标签和验证规则

关键文件:

server/model/
├── base.go                # 基础模型(id、创建时间等)
├── user.go                # 用户模型
├── role.go                # 角色模型
├── permission.go          # 权限模型
└── file.go                # 文件模型

数据模型层使用 GORM 标签定义数据库表结构、关联关系和字段验证规则。

3.3 关键模块深入

📌 认证模块 (JWT)

流程:

  1. 用户登录时,后端验证用户名/密码
  2. 验证成功后,生成包含用户 ID 和权限的 JWT token
  3. 前端将 token 存储在 localStorage
  4. 后续请求在 Authorization Header 中携带 token
  5. 中间件验证 token 的有效性和签名

JWT 实现包括 token 生成(包含用户 ID、用户名、角色)和验证(验证签名和过期时间)。

📌 授权模块 (Casbin RBAC)

RBAC 模型定义 (config/rbac_model.conf):

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

权限规则定义简洁(例如:p, admin, /users/*, *),中间件通过 Casbin 验证用户是否有权访问特定资源。

📌 结构化日志模块 (Zap)

目的:

  • 便于日志聚合和分析
  • 包含请求 ID 便于追踪
  • 性能高效

结构化日志以 JSON 格式输出,包含日志级别、时间戳、调用位置和自定义字段,便于日志聚合和分析。

3.4 前端架构

前端采用 Vue Pure Admin 框架,这是一款基于 Vue 3 + Element Plus + Vite 的企业级管理后台模板。

核心特点:

  • 开箱即用 - 包含完整的登录、权限管理、菜单系统、表格等功能
  • 现代化技术栈 - Vue 3 Composition API、TypeScript、Vite 构建
  • 企业级规范 - 代码结构清晰,遵循最佳实践
  • 易于上手 - 作为后端开发者,可以直接修改和扩展
  • 生产级质量 - 经过验证的架构和最佳实践

项目结构 - Vue Pure Admin 标准结构:

web/
├── src/
│   ├── api/                    # API 服务层 (Axios 请求)
│   ├── views/                  # 页面组件 (登录、仪表板、管理等)
│   ├── components/             # 可复用的业务组件
│   ├── layout/                 # 布局组件 (菜单、顶栏等)
│   ├── router/                 # 路由配置 + 权限控制
│   ├── store/                  # Pinia 状态管理
│   ├── utils/                  # 工具函数 (HTTP、存储等)
│   ├── types/                  # TypeScript 类型定义
│   ├── App.vue                 # 根组件
│   └── main.ts                 # 应用入口
├── index.html                  # HTML 模板
├── vite.config.ts              # Vite 配置
├── tsconfig.json               # TypeScript 配置
└── package.json                # 依赖管理

关键功能:

  • ✅ 权限控制 - 基于角色的访问控制 (RBAC)
  • ✅ 动态菜单 - 支持权限级别的菜单显示/隐藏
  • ✅ 主题切换 - 内置亮色和暗黑主题
  • ✅ 响应式设计 - 支持 PC、平板、手机多种设备
  • ✅ 完整的管理功能 - 用户管理、角色管理、权限管理等

四、项目现状

4.1 开发环境部署

经过第一阶段的开发,我们成功搭建了完整的开发环境:

📦 基础设施启动状态
$ make podman-up
✅ MySQL 8.4:     localhost:3306 (container: ewp-mysql)
✅ Redis 7:       localhost:6379 (container: ewp-redis)
✅ Prometheus:    localhost:9090 (container: ewp-prometheus)
✅ Grafana:       localhost:3000 (container: ewp-grafana)

提示: 所有容器都带有 ewp- 前缀(Enterprise Web Platform 的缩写),方便在本地开发环境中识别和管理。

🚀 后端服务
$ make run-backend
2025-12-24T17:26:19.324+0800  INFO  Server starting...
2025-12-24T17:26:19.354+0800  INFO  Database connected successfully
2025-12-24T17:26:19.360+0800  INFO  Redis connected successfully
✅ Backend:       http://localhost:8888

已实现的 API 端点:

端点方法说明状态
/api/pingGET健康检查
/api/healthGET健康状态
/api/readyGET就绪状态
/metricsGETPrometheus 指标
🎨 前端应用
$ make run-frontend
  VITE v7.1.12  ready in 1016 ms

  ➜  Local:   http://localhost:8848/

4.2 项目效果图

系统现已部署在本地

后端启动日志

后端启动日志截图.png 前端启动日志

前端启动日志截图.png 容器运行状态

容器运行状态.png

4.3 技术债清单

当前已完成的工作中,以下项目需要在后续阶段完善:

  • 单元测试(后端 service 层)
  • 集成测试(API 端点)
  • 前端组件测试
  • 端到端测试 (E2E)
  • 性能基准测试
  • 安全审计
  • 代码覆盖率报告

五、后续开发方向与计划

5.1 开发计划概览

项目按照功能特性分为几个主要阶段进行开发:

Phase 1: 基础框架与基础设施     已完成
  ├─ 项目脚手架和包结构
  ├─ 框架集成(Gin、GORM、Viper 等)
  └─ 基础设施搭建(MySQL、Redis、Prometheus、Grafana)

Phase 2: 认证与授权体系         📝 开发中
  ├─ JWT 认证完整实现
  ├─ Casbin RBAC 权限管理
  ├─ 用户和角色管理 API
  └─ 前端权限控制和动态菜单

Phase 3: 核心功能模块          📝 计划中
  ├─ CRUD 基础操作
  ├─ 文件上传下载
  ├─ 数据导入导出
  └─ 常用工具函数

Phase 4: 企业级特性            📝 计划中
  ├─ 完整的监控体系
  ├─ 结构化日志系统
  ├─ API 文档(Swagger)
  └─ 性能优化

Phase 5: 部署与运维            📝 计划中
  ├─ 单机部署方案
  ├─ 多机部署方案
  ├─ 容器化部署
  └─ 高可用配置

Phase 6: 测试与质量保证        📝 计划中
  ├─ 单元测试
  ├─ 集成测试
  ├─ 性能测试
  └─ 安全审计

Phase 7: 文档与总结            📝 计划中
  ├─ 完整的项目文档
  ├─ 最佳实践总结
  └─ 技术经验分享

这个计划是灵活的,会根据实际情况进行调整。每个阶段的重点是确保代码质量和完整的学习记录。

常见问题

Q: MySQL 连接失败? A: 确保容器已启动 podman ps,检查 config.yaml 中的 host 是否为 localhost

Q: 前端无法连接后端? A: 检查 vite.config.js 中的代理配置,或在浏览器 F12 检查 Network 标签

Q: Podman 命令找不到? A: 使用 pipx install podman-compose 安装(macOS 可用 Homebrew)