使用 GitOps 实现 MinIO 防灾

Disaster Proof MinIO with GitOps

想象一下,您花费无数时间完善您的Docker Swarm 设置,精心设计每个服务,并调整您的 CI/CD 管道以实现无缝自动化。现在,想象一下这个精心调整的系统被重置到原点,不是因为严重故障或安全漏洞,而是因为数据中心工程师出于好奇按下硬件上的神奇“恢复出厂设置”按钮。这不是一个 DevOps 恐怖故事的开端,而是一个证明了精心设计的存储库和 GitOps 原则带来的弹性和快速恢复能力的证明。

在本文中,我们将深入探讨自动化和冗余(DevOps 的核心)如何在重置可能导致灾难的情况下成为救星。我将分享 Docker、GitHub Actions 和 MinIO 的组合,以及意外的真实世界试验,如何突显了可靠且可重复的构建和部署流程的重要性。

通过这次经历,我们将探索版本控制配置的强大功能,以及将镜像推送到多个注册表的先见之明,确保即使在系统完全擦除的情况下,恢复也只需几个命令即可完成。加入我,一起探索 DevOps 中的弹性深度,说明您如何也能保护您的系统免受生活可能带来的意外。

屏幕截图 - 图 1:Docker 的恢复出厂设置对话框

冗余和自动化的重要性

冗余和自动化是现代 DevOps 实践中的基础支柱,对于维持连续性和确保高可用性至关重要。将 Docker 镜像双重推送到Docker HubGitHub 容器注册表 (GHCR.io) 的战略决策,源于对风险管理和可访问性的权衡考虑。

Docker Hub作为主要的工件注册表,提供了广泛的集成和公共可见性,用于广泛的分发和协作。它是用于立即检索和部署工件的首选注册表。但是,依赖单个注册表是一个漏洞;任何服务中断都可能停止部署和更新,从而导致潜在的停机时间。

相比之下,GHCR.io默认情况下为 Docker 镜像提供了一个安全、私有的存储解决方案,符合对某些部署工件进行访问控制的需求。此注册表成为一个战略备份,确保即使 Docker Hub 遭到破坏或不可用,也可以立即回退,而不会对 CI/CD 管道造成任何中断。

通过 GitHub Actions 自动化镜像推送到两个注册表。它确保最新的构建始终可从任一来源进行部署,有效地防止服务中断。这种有条理的冗余不是为了创建不必要的副本,而是为了以最少的恢复时间做好应急准备。

在存储库所在的 GitHub 上构建,同时推送到两个注册表的逻辑很简单:它提供了一种无缝的自动化方式来维护所有镜像的私有备份,同时保持公共注册表最新。这种双重方法是一种主动措施——精心设计的 DevOps 流程必须将其核心包含战略冗余。

增强存储库的弹性

为了追求弹性的 DevOps 环境,持续改进至关重要。对原始存储库进行的增强既具有反思性,也具有预防性,旨在不仅满足恢复需求,而且简化未来任何部署需求的流程。

首先,工作流配置经过改进,每个作业和步骤都经过定义以确保效率和可靠性。为此,.github/workflows 目录中添加了用于多平台构建的新工作流,扩展了存储库的自动化功能。此设置不仅有助于一致的部署,还确保应用程序的最新版本随时可以从 Docker Hub 或 GHCR.io 中提取。

屏幕截图 - 图 2:为优先考虑本地环境中的 CI/CD 而创建的 GitHub 存储库的屏幕截图

cda-deploy-to-swarm存储库中,结构经过精心设计,以实现快速导航和易用性。该目录包括

  • .github/workflows 文件夹,其中包含 CI/CD 自动化脚本。在这里,YAML 文件定义了在推送和拉取请求时运行的操作,包括构建和推送操作以及部署编排。
  • minio 目录包含为 MinIO 服务定制的配置和 Docker Compose 文件,以便快速且标准化地设置对象存储功能。
  • nginx 目录设置了用于部署 NGINX Web 服务器的配置,这对于处理 HTTP 请求和充当反向代理至关重要。
  • weaviate 目录,其中包含部署 Weaviate 向量搜索引擎的必要元素,利用 Docker Compose 进行服务定义和部署。
  • 存储库根目录的核心是docker-compose.yaml 文件,它定义了多容器应用程序服务,并将各个组件整合在一起形成一个有机的整体。

每个改进和结构决策都是为了创建一个存储库,该存储库不仅能够从中断中恢复,而且还能作为在 Docker Swarm 环境中部署服务的模块化、可扩展的基础。存储库结构的清晰度和组织性对此至关重要,允许快速识别组件和配置,从而减少恢复时间并促进高效管理部署过程。有关本文中提到的特定文件的详细信息,请访问 MinIO blog-assets存储库。

使用 GitHub Actions 和 Docker Compose 实现高效的 CI/CD

在我们的 DevOps 工作流中,GitHub Actions 在自动化 Docker 镜像(包括我们的自定义 MinIO 和 Weaviate 服务)的构建过程中发挥着关键作用。这些操作配置为在特定事件(例如推送到主分支)时触发,确保每个更改都无缝集成到我们的部署管道中。

以下是流程展开方式

  1. GitHub Actions 工作流:在对存储库进行提交后,我们的 GitHub Actions 工作流将启动。这些工作流包含用于使用我们的自定义 Dockerfile 构建 Docker 镜像并将其推送到注册表的作业。这些镜像被适当地标记,例如,cdaprod/cda-minio:latest
  2. Docker Compose 用于部署:在构建和推送过程之后,docker-compose.yaml 文件将用于部署。它配置为从注册表中提取最新镜像,以便在 Docker Swarm 环境中部署服务。

docker-compose.yaml文件包含如下服务定义

version: '3.8'

services:
  minio:
    image: cdaprod/cda-minio:latest
    # Other configurations ...

 weaviate:
    image: cdaprod/cda-weaviate:latest
    # Other configurations ...

 nginx:
    image: cdaprod/cda-nginx:latest
    # Other configurations ...

networks:
  app_network:
    driver: overlay

# Other definitions like volumes and secrets…

这种方法通过解耦镜像构建和服务部署过程,极大地简化了我们的部署策略。这也意味着我们不需要为不同的环境或场景创建多个 docker-compose 文件。

通过利用这种 CI/CD 策略,我们能够维护服务定义的单一事实来源,并确保我们的部署过程尽可能地简化和高效。我们的部署变成了一个自托管运行器执行来自 GitHub Actions 工作流的docker-compose up 命令的问题,Docker Compose 处理其余部分——提取最新镜像、启动服务并确保它们完全按照我们定义的方式进行配置。

举例说明使用 MinIO 的自定义镜像构建

为了进一步阐明我们的 CI/CD 管道的构建过程,让我们更仔细地看看 MinIO 服务镜像的自定义。我们的 GitHub Actions 工作流负责根据官方的minio/minio:latest 构建此镜像,并根据我们的需求进行额外的配置。

这是/minio/Dockerfile,它概述了构建步骤

# Start with the official MinIO image as a base
ARG BASE_IMAGE=minio/minio:latest
FROM $BASE_IMAGE


# Copy over the custom entrypoint script
COPY entrypoint.sh /entrypoint.sh
# Ensure the script is executable
RUN chmod +x /entrypoint.sh


# Expose the ports MinIO will use
EXPOSE 9000 9001


# Use the custom entrypoint script to start MinIO
ENTRYPOINT ["/entrypoint.sh"]

构建 MinIO 容器,通过入口点 bash 脚本配置它

使用入口点脚本增强自定义

自定义过程并不止于Dockerfile。为了完全根据我们的运营需求定制 MinIO 服务,我们利用了一个entrypoint.sh脚本。此脚本充当容器启动时的命令中心,执行预定义的命令,从而在默认设置之外配置 MinIO 实例。

使用entrypoint.sh脚本的优势在于它能够执行运行时配置。这些配置对于动态设置我们的环境至关重要,确保我们的 MinIO 服务不仅仅是一个普通的部署,而是一个根据我们的用例定制的部署。

这种将Dockerfileentrypoint.sh脚本结合使用的方法,使我们能够构建不仅考虑了应用程序和服务,而且在部署后即可在我们的独特生态系统中运行的容器。

#!/bin/sh
set -e

# Start MinIO in the background
minio server /data --console-address ":9001" &

# Wait for the MinIO server to start
sleep 5

# Configure MinIO with the mc (MinIO Client) tool
mc alias set myminio http://localhost:9000 ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD}

# Before creating buckets, check if they already exist
if ! mc ls myminio/weaviate-backups; then
  mc mb myminio/weaviate-backups
fi

if ! mc ls myminio/cda-datasets; then
  mc mb myminio/cda-datasets
fi

# Keep the container running
tail -f /dev/null

启动 MinIO 控制台,设置 mc 别名,并在未找到时创建存储桶

Dockerfile指定官方 MinIO 镜像作为基础镜像,然后添加我们自定义的entrypoint.sh脚本。此脚本至关重要,因为它在 MinIO 启动后运行其他命令来配置 MinIO,例如使用 MinIO 客户端 (mc) 创建必要的存储桶。

在我们的工作流触发的镜像构建阶段,我们使用此Dockerfile创建cdaprod/cda-minio:latest镜像。此镜像封装了我们的自定义设置和脚本,docker-compose.yaml文件稍后将使用它在我们的 Docker Swarm 中部署 MinIO 服务。

minio/minio:latestcdaprod/cda-minio:latest之间的区别很小。前者是来自 MinIO 官方 Docker Hub 仓库的未经修改的基础镜像,而后者是我们自定义的版本,通过我们的 CI/CD 工作流构建和维护,确保任何特定配置和脚本都内置在我们将部署的镜像中。

深入了解工作流

两个 GitHub Actions 工作流,“构建和推送 Docker 镜像”和“部署服务”,构成了存储库中的核心自动化机制。以下是每个工作流的详细说明。

构建和推送 Docker 镜像工作流

此工作流在任何推送到main分支或pull_request时触发,以确保最新的代码提交会生成更新的 Docker 镜像。

name: Build and Push Docker Images

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v2

    - name: Login to Docker Hub
      uses: docker/login-action@v2
      with:
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_TOKEN }}

    - name: Build and push custom MinIO image
      uses: docker/build-push-action@v3
      with:
        context: ./minio
        file: ./minio/Dockerfile
        push: true
        tags: cdaprod/cda-minio:latest
        platforms: linux/amd64,linux/arm64

    - name: Build and push custom Weaviate image
      uses: docker/build-push-action@v3
      with:
        context: ./weaviate
        file: ./weaviate/Dockerfile
        push: true
        tags: cdaprod/cda-weaviate:latest
        platforms: linux/amd64,linux/arm64

构建容器化镜像并推送到 Docker Hub

作业 build-and-push 在 GitHub Actions 提供的最新 Ubuntu 运行器上执行。工作流包含以下步骤

  1. 检出代码:actions/checkout@v3操作用于克隆存储库,以便工作流可以访问其内容。
  2. 设置 Docker Buildx:docker/setup-buildx-action@v2用于安装和配置 Buildx,它扩展了 Docker,使其能够构建多平台镜像、更高级的构建指标和不同的输出选项,同时利用 Docker 的 buildkit 库。
  3. 登录 Docker Hub:此步骤使用docker/login-action@v2登录 Docker Hub,引用DOCKERHUB_USERNAMEDOCKERHUB_TOKEN密钥进行身份验证。这些密钥安全地存储在存储库设置中,并且不会在工作流文件中公开。
  4. 构建和推送自定义 MinIO 镜像:利用docker/build-push-action@v3从 minio 上下文目录中指定的Dockerfile构建 MinIO 镜像。它被标记然后推送到 Docker Hub,确保镜像可用于部署。platforms 参数指定镜像应支持的 CPU 架构,确保跨不同硬件设置的兼容性。
  5. 构建和推送自定义 Weaviate 镜像:类似地,此步骤用于构建和推送 Weaviate 镜像,并提供适当的标记和多架构支持。
屏幕截图 - 图 3:成功构建和推送 Docker 镜像工作流执行。

部署服务工作流

此部署工作流是 CI/CD 管道的“持续部署”方面的必要条件,在构建和推送 Docker 镜像工作流在 main 分支上成功完成时启动。

name: Deploy Services

on:
  workflow_run:
    workflows: ["Build and Push Docker Images"]
    branches: [main]
    types:
      - completed
  workflow_dispatch:

jobs:
  deploy:
    runs-on: self-hosted
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2

      - name: Log in to Docker Hub
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Deploy to Docker Swarm
        run: |
          docker stack deploy -c docker-compose.yaml cda_stack

在“构建和推送 Docker 镜像”工作流成功后使用 docker-compose.yaml 部署

  1. 检出存储库:与构建工作流类似,它首先检出代码。
  2. 设置 Docker Buildx:尽管 Buildx 设置不用于部署,但出于一致性和将来使用的考虑,如果在堆栈部署过程中需要多节点构建,它仍然包含在内。
  3. 登录 Docker Hub:确保登录 Docker Hub,以确保运行器可以顺利拉取部署所需的镜像。
  4. 部署应用程序堆栈:此关键步骤使用单个docker-compose.yaml部署完整的应用程序堆栈。它将 MinIO、Weaviate 和 NGINX 的服务定义和配置组合到一个统一的部署命令中。环境变量和密钥在 GitHub 密钥中安全地管理。

部署工作流将代码更改转换为实时服务,并通过 GitHub 密钥处理敏感数据。它展示了部署复杂服务堆栈的能力,遵循基础设施即代码的原则,并且旨在随着应用程序基础设施的增长而发展。

屏幕截图 - 图 4:展示了部署服务工作流在构建过程之后成功激活的情况。
屏幕截图 - 图 4 展示了工作流的通过状态徽章,在构建过程和部署之后。

主动应对 DevOps 韧性

在 DevOps 世界中,系统的稳健性通常只在危机时刻才会凸显出来。最近的一起事件证明了我们实践中嵌入的力量;一次无意中发起的系统范围内的重置导致所有正在运行的服务都停止了运行。但这并非灾难,而是一个绝佳的机会,可以验证我们在 MinIO 提倡的基础设施的韧性。

我们的存储库结构具有前瞻性地设计,利用 Docker Compose 和 GitHub 密钥来协调我们的服务环境。这种战略性设置简化了恢复过程,将原本可能很复杂的恢复变成了一系列简单、自动化的步骤

  1. 初始化:我们自托管的运行器已经针对此类意外情况进行了配置,并已启动,为恢复奠定了基础。
  2. 同步:最新的服务代码直接从我们的存储库中获取,保证只有最新的配置在使用。
  3. 配置:作为 GitHub 密钥安全存储的环境变量被无缝集成,无需手动设置。
  4. 编排:服务的部署通过 Docker Compose 执行自动完成,由 GitHub Actions 工作流引导,精确地恢复了整套服务。

在此基础设施即代码的演示中,我们的服务迅速恢复到其运行状态,突显了我们的 DevOps 方法的有效性。

构建弹性系统的关键见解

从意外的系统重置中快速恢复突出了几个对现代软件开发和部署至关重要的见解

  • 自动化至关重要:自动化恢复过程可最大程度地减少停机时间并消除人为错误,从而对系统故障做出快速可靠的响应。
  • 统一的事实来源:GitOps 整合了基础设施和应用程序管理,简化了更改跟踪并能够在必要时轻松回滚。
  • 为持久性设计:基础设施的弹性是有意的,通过精心规划和实施 CI/CD 和 IaC 最佳实践来构建。

在 MinIO,这些原则不仅仅是理论上的——它们是我们工作流程中嵌入的实用措施,确保我们的系统和服务天生具有弹性。对于开发人员和 DevOps 专业人员来说,信息很明确:投资这些实践就是在投资基础设施的未来稳定性和成功。我们共同的经验充分说明了我们以优雅和高效的方式管理中断的能力。

通过将我们的基础设施编码并像对待应用程序代码一样小心谨慎地对待它,我们可以大幅减少在灾难面前重建和重新部署服务所需的时间和精力。此外,我们还强调了全面文档和定期自动备份的重要性。虽然强大的 CI/CD 管道可以促进快速恢复,但它不能替代维护全面的文档和可靠的代码、配置和数据备份。

请在Slack上联系我们并给我们留言。让我们一起继续这场对话吧!