在 Kubernetes 上使用 MinIO 分布式集群进行 CI/CD 部署

在 Kubernetes 上使用 MinIO 分布式集群进行 CI/CD 部署

欢迎来到我们的 MinIO 和 CI/CD 系列的第三部分也是最后一部分。到目前为止,我们已经讨论了CI/CD 概念的基础知识以及如何构建 MinIO 工件以及如何在开发中测试它们。在这篇博文中,我们将重点关注持续交付和 MinIO。我们将向您展示如何使用基础架构即代码在生产环境中部署 MinIO 集群,以确保任何人都可以读取安装的资源并对任何更改应用版本控制。

MinIO 用途广泛,几乎可以安装在任何环境中。MinIO 符合多个用例,让开发人员在笔记本电脑上拥有与他们在生产环境中使用我们讨论的 CI/CD 概念和管道相同的环境。我们之前向您展示了如何将 MinIO 安装为docker 容器,甚至安装为systemd 服务今天我们将向您展示如何使用 Operator 在生产 Kubernetes 集群中以分布式模式部署 MinIO。我们将首先使用 Terraform 部署基础设施,然后我们将部署所需的 MinIO 资源。

MinIO 网络

首先,我们将使用 Terraform 构建我们的基础设施启动和运行所需的基本网络。我们将建立一个具有 3 种基本常用网络类型的 VPC 网络。在该网络中,我们将启动一个 Kubernetes 集群,我们可以在其中部署我们的 MinIO 工作负载。我们的 Terraform 模块的结构看起来像这样

modules
├── eks
│   ├── main.tf
│   ├── outputs.tf
│   └── variables.tf
└── vpc
    ├── main.tf
    ├── outputs.tf
    └── variables.tf

https://github.com/minio/blog-assets/tree/main/ci-cd-deploy/terraform/aws/modules

为了使 VPC 具有不同的网络,每个子网都需要一个唯一的非重叠子网。这些子网被分成 CIDR 块。对于少数几个,这很容易计算,但对于像我们这里这样的许多子网,Terraform 提供了一个方便的功能,可以cidrsubnet()根据我们提供的更大的子网为我们拆分子网,在这种情况下10.0.0.0/16

variable "minio_aws_vpc_cidr_block" {
  description = "AWS VPC CIDR block"
  type        = string
  default     = "10.0.0.0/16"
}

variable "minio_aws_vpc_cidr_newbits" {
  description = "AWS VPC CIDR new bits"
  type        = number
  default     = 4
}

vpc/variables.tf#L1-L11

在 Terraform 中定义 VPC 资源。创建的任何子网都将基于此 VPC。

resource "aws_vpc" "minio_aws_vpc" {

  cidr_block           = var.minio_aws_vpc_cidr_block
  instance_tenancy     = "default"
  enable_dns_hostnames = true

}

vpc/main.tf#L1-L7

设置 3 个不同的网络:公共、私有和隔离。

带互联网网关的公共网络 (IGW) 将通过公共 IP 和互联网网关进行入站和出站互联网访问。

variable "minio_public_igw_cidr_blocks" {
  type = map(number)
  description = "Availability Zone CIDR Mapping for Public IGW subnets"

  default = {
    "us-east-1b" = 1
    "us-east-1d" = 2
    "us-east-1f" = 3
  }
}

vpc/variables.tf#L15-L24

aws_subnet资源将循环 3 次,在公共 VPC 中创建 3 个子网

resource "aws_subnet" "minio_aws_subnet_public_igw" {

  for_each = var.minio_public_igw_cidr_blocks

  vpc_id            = aws_vpc.minio_aws_vpc.id
  cidr_block        = cidrsubnet(aws_vpc.minio_aws_vpc.cidr_block, var.minio_aws_vpc_cidr_newbits, each.value)
  availability_zone = each.key

  map_public_ip_on_launch = true
}

resource "aws_route_table" "minio_aws_route_table_public_igw" {

  vpc_id = aws_vpc.minio_aws_vpc.id

}

resource "aws_route_table_association" "minio_aws_route_table_association_public_igw" {

  for_each       = aws_subnet.minio_aws_subnet_public_igw

  subnet_id      = each.value.id
  route_table_id = aws_route_table.minio_aws_route_table_public_igw.id
}

resource "aws_internet_gateway" "minio_aws_internet_gateway" {

  vpc_id = aws_vpc.minio_aws_vpc.id

}

resource "aws_route" "minio_aws_route_public_igw" {
  route_table_id         = aws_route_table.minio_aws_route_table_public_igw.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.minio_aws_internet_gateway.id
}

vpc/main.tf#L11-L46

具有 NAT 网关 (NGW) 的专用网络将具有出站网络访问权限,但没有入站网络访问权限,具有私有 IP 地址和 NAT 网关。

variable "minio_private_ngw_cidr_blocks" {
  type = map(number)
  description = "Availability Zone CIDR Mapping for Private NGW subnets"

  default = {
    "us-east-1b" = 4
    "us-east-1d" = 5
    "us-east-1f" = 6
  }
}

vpc/variables.tf#L26L-L35

aws_subnet资源将循环 3 次,在私有 VPC 中创建 3 个子网

resource "aws_subnet" "minio_aws_subnet_private_ngw" {

  for_each = var.minio_private_ngw_cidr_blocks

  vpc_id            = aws_vpc.minio_aws_vpc.id
  cidr_block        = cidrsubnet(aws_vpc.minio_aws_vpc.cidr_block, var.minio_aws_vpc_cidr_newbits, each.value)
  availability_zone = each.key
}

resource "aws_route_table" "minio_aws_route_table_private_ngw" {

  for_each = var.minio_private_ngw_cidr_blocks

  vpc_id = aws_vpc.minio_aws_vpc.id
}

resource "aws_route_table_association" "minio_aws_route_table_association_private_ngw" {

  for_each = var.minio_private_ngw_cidr_blocks

  subnet_id      = aws_subnet.minio_aws_subnet_private_ngw[each.key].id
  route_table_id = aws_route_table.minio_aws_route_table_private_ngw[each.key].id
}

resource "aws_eip" "minio_aws_eip_nat" {

  for_each = var.minio_private_ngw_cidr_blocks

  vpc = true
}

resource "aws_nat_gateway" "minio_aws_nat_gateway" {

  for_each = var.minio_private_ngw_cidr_blocks

  subnet_id     = aws_subnet.minio_aws_subnet_public_igw[each.key].id
  allocation_id = aws_eip.minio_aws_eip_nat[each.key].id

  depends_on    = [aws_internet_gateway.minio_aws_internet_gateway]
}

resource "aws_route" "minio_aws_route_private_ngw" {

  for_each = var.minio_private_ngw_cidr_blocks

  route_table_id         = aws_route_table.minio_aws_route_table_private_ngw[each.key].id
  destination_cidr_block = "0.0.0.0/0"
  nat_gateway_id         = aws_nat_gateway.minio_aws_nat_gateway[each.key].id
}

vpc/main.tf#L50-L98

最后,我们创建了一个既没有出站也没有入站互联网访问的隔离和气隙网络。这个网络是完全气隙的,只有一个私有 IP 地址。

variable "minio_private_isolated_cidr_blocks" {
  type = map(number)
  description = "Availability Zone CIDR Mapping for Private isolated subnets"

  default = {
    "us-east-1b" = 7
    "us-east-1d" = 8
    "us-east-1f" = 9
  }
}

vpc/variables.tf#L37-L46

aws_subnet资源将循环 3 次,在隔离/气隙 VPC 中创建 3 个子网

resource "aws_subnet" "minio_aws_subnet_private_isolated" {

  for_each = var.minio_private_isolated_cidr_blocks

  vpc_id            = aws_vpc.minio_aws_vpc.id
  cidr_block        = cidrsubnet(aws_vpc.minio_aws_vpc.cidr_block, var.minio_aws_vpc_cidr_newbits, each.value)
  availability_zone = each.key
}

resource "aws_route_table" "minio_aws_route_table_private_isolated" {

  vpc_id = aws_vpc.minio_aws_vpc.id

}

resource "aws_route_table_association" "minio_aws_route_table_association_private_isolated" {

  for_each = aws_subnet.minio_aws_subnet_private_isolated

  subnet_id      = each.value.id
  route_table_id = aws_route_table.minio_aws_route_table_private_isolated.id
}

vpc/main.tf#L102-L123

MinIO Kubernetes 集群

创建一个 Kubernetes 集群,我们将在其上部署我们的 MinIO 集群。将由minio_aws_eks_cluster_subnet_ids我们将创建的 VPC 提供。稍后,我们将展示如何在部署阶段将所有这些拼接在一起。

variable "minio_aws_eks_cluster_subnet_ids" {
  description = "AWS EKS Cluster subnet IDs"
  type        = list(string)
}

variable "minio_aws_eks_cluster_name" {
  description = "AWS EKS Cluster name"
  type        = string
  default     = "minio_aws_eks_cluster"
}

variable "minio_aws_eks_cluster_endpoint_private_access" {
  description = "AWS EKS Cluster endpoint private access"
  type        = bool
  default     = true
}

variable "minio_aws_eks_cluster_endpoint_public_access" {
  description = "AWS EKS Cluster endpoint public access"
  type        = bool
  default     = true
}

variable "minio_aws_eks_cluster_public_access_cidrs" {
  description = "AWS EKS Cluster public access cidrs"
  type        = list(string)
  default     = ["0.0.0.0/0"]
}

eks/variables.tf#L1-L28

注意:在生产环境中,您可能不希望公开访问 Kubernetes API 端点,因为这可能会成为一个安全问题,因为它会打开对集群的控制。

您还需要几个角色来确保 Kubernetes 集群可以通过我们创建的网络正确通信,这些角色在eks/main.tf#L1-L29中定义。Kubernetes集群定义如下

resource "aws_eks_cluster" "minio_aws_eks_cluster" {
  name = var.minio_aws_eks_cluster_name
  role_arn = aws_iam_role.minio_aws_iam_role_eks_cluster.arn

  vpc_config {
    subnet_ids              = var.minio_aws_eks_cluster_subnet_ids
    endpoint_private_access = var.minio_aws_eks_cluster_endpoint_private_access
    endpoint_public_access  = var.minio_aws_eks_cluster_endpoint_public_access
    public_access_cidrs     = var.minio_aws_eks_cluster_public_access_cidrs
  }

  depends_on = [
    aws_iam_role.minio_aws_iam_role_eks_cluster,
  ]

}

eks/main.tf#L31-L46

集群接收由诸如 之类的命令发出的 API 请求kubectl,但除此之外还有更多 - 工作负载需要在某处进行调度。这是需要 Kubernetes 集群节点组的地方。下面,我们定义节点组名称、实例类型和所需的组大小。由于我们有 3 个可用区,我们将为每个可用区创建 3 个节点。

variable "minio_aws_eks_node_group_name" {
  description = "AWS EKS Node group name"
  type        = string
  default     = "minio_aws_eks_node_group"
}

variable "minio_aws_eks_node_group_instance_types" {
  description = "AWS EKS Node group instance types"
  type        = list(string)
  default     = ["t3.large"]
}

variable "minio_aws_eks_node_group_desired_size" {
  description = "AWS EKS Node group desired size"
  type        = number
  default     = 3
}

variable "minio_aws_eks_node_group_max_size" {
  description = "AWS EKS Node group max size"
  type        = number
  default     = 5
}

variable "minio_aws_eks_node_group_min_size" {
  description = "AWS EKS Node group min size"
  type        = number
  default     = 1
}

eks/variables.tf#L30-L58

您需要几个角色来确保 Kubernetes 节点组可以正常通信,这些角色在eks/main.tf#L48-L81中定义。Kubernetes节点组(workers)定义如下:

resource "aws_eks_node_group" "minio_aws_eks_node_group" {
  cluster_name    = aws_eks_cluster.minio_aws_eks_cluster.name
  node_group_name = var.minio_aws_eks_node_group_name
  node_role_arn   = aws_iam_role.minio_aws_iam_role_eks_worker.arn
  subnet_ids      = var.minio_aws_eks_cluster_subnet_ids
  instance_types  = var.minio_aws_eks_node_group_instance_types

  scaling_config {
    desired_size = var.minio_aws_eks_node_group_desired_size
    max_size     = var.minio_aws_eks_node_group_max_size
    min_size     = var.minio_aws_eks_node_group_min_size
  }

  depends_on = [
    aws_iam_role.minio_aws_iam_role_eks_worker,
  ]

}

eks/main.tf#L83-L100

此配置将在我们配置的 3 个 VPC 网络中的任何一个中启动一个带有工作节点的控制平面。kubectl get no集群启动后,我们稍后会显示输出。

MinIO 部署

到目前为止,我们以代码形式拥有所有必要的基础设施。接下来,我们将部署这些资源并创建我们将部署 MinIO 的集群。

使用以下命令安装 Terraform

brew install terraform

使用以下命令安装 aws CLI

brew install awscli

使用以下策略创建 AWS IAM 用户。注意创建用户后的AWS_ACCESS_KEY_ID和。AWS_SECRET_ACCESS_KEY


Screen Shot 2022-11-07 at 9.27.15 AM.png


为 AWS 设置环境变量,因为它们将被 terraform 和awscli.

$ export AWS_ACCESS_KEY_ID=<access_key>
$ export AWS_SECRET_ACCESS_KEY=<secret_key>

$ export AWS_ACCESS_KEY_ID=<access_key>
$ export AWS_SECRET_ACCESS_KEY=<secret_key>

使用以下结构hello_world在同一目录中创建一个名为的文件夹modules

.

├── hello_world

│   ├── main.tf

│   ├── outputs.tf

│   ├── terraform.tfvars

│   └── variables.tf

├── modules

│   ├── eks

│   └── vpc


https://github.com/minio/blog-assets/tree/main/ci-cd-deploy/terraform/aws/hello_world

创建一个名为的文件terraform.tfvars并设置以下变量

hello_minio_aws_region  = "us-east-1"

创建一个名为的文件main.tf并初始化 terraform AWS 提供程序和 S3 后端。请注意,S3 存储桶需要事先存在。我们正在使用 S3 后端来存储状态,以便它可以在开发人员和 CI/CD 流程之间共享,而无需尝试在整个组织中保持本地状态同步。

terraform {
  required_version = ">= 1.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.31.0"
    }
  }

  backend "s3" {
    bucket = "aj-terraform-bucket"
    key    = "tf/aj/mo"
    region = "us-east-1"
  }

}

provider "aws" {
  region  = var.hello_minio_aws_region
}

你好_世界/main.tf#L1-L21

不支持将后端存储桶和键设置为变量,因此需要对这些值进行硬编码。

从中调用VPC模块main.tf并命名hello_minio_aws_vpc

module "hello_minio_aws_vpc" {
  source = "../modules/vpc"

  minio_aws_vpc_cidr_block   = var.hello_minio_aws_vpc_cidr_block
  minio_aws_vpc_cidr_newbits = var.hello_minio_aws_vpc_cidr_newbits

  minio_public_igw_cidr_blocks       = var.hello_minio_public_igw_cidr_blocks
  minio_private_ngw_cidr_blocks      = var.hello_minio_private_ngw_cidr_blocks
  minio_private_isolated_cidr_blocks = var.hello_minio_private_isolated_cidr_blocks

}

你好_世界/main.tf#L23-L33

这些是 vpc 模块需要的变量

hello_minio_aws_vpc_cidr_block = "10.0.0.0/16"

hello_minio_aws_vpc_cidr_newbits = 4

hello_minio_public_igw_cidr_blocks = {
  "us-east-1b" = 1
  "us-east-1d" = 2
  "us-east-1f" = 3
}

hello_minio_private_ngw_cidr_blocks = {
  "us-east-1b" = 4
  "us-east-1d" = 5
  "us-east-1f" = 6
}

hello_minio_private_isolated_cidr_blocks = {
  "us-east-1b" = 7
  "us-east-1d" = 8
  "us-east-1f" = 9
}



你好_世界/terraform.tfvars#L3-L22

创建 VPC 后,下一步就是创建 Kubernetes 集群。我们将从 VPC 创建中使用的唯一值是minio_aws_eks_cluster_subnet_ids我们将使用 VPC 创建的私有子网

module "hello_minio_aws_eks_cluster" {
  source = "../modules/eks"

  minio_aws_eks_cluster_name                    = var.hello_minio_aws_eks_cluster_name
  minio_aws_eks_cluster_endpoint_private_access = var.hello_minio_aws_eks_cluster_endpoint_private_access
  minio_aws_eks_cluster_endpoint_public_access  = var.hello_minio_aws_eks_cluster_endpoint_public_access
  minio_aws_eks_cluster_public_access_cidrs     = var.hello_minio_aws_eks_cluster_public_access_cidrs
  minio_aws_eks_cluster_subnet_ids              = values(module.hello_minio_aws_vpc.minio_aws_subnet_private_ngw_map)
  minio_aws_eks_node_group_name                 = var.hello_minio_aws_eks_node_group_name
  minio_aws_eks_node_group_instance_types       = var.hello_minio_aws_eks_node_group_instance_types
  minio_aws_eks_node_group_desired_size         = var.hello_minio_aws_eks_node_group_desired_size
  minio_aws_eks_node_group_max_size             = var.hello_minio_aws_eks_node_group_max_size
  minio_aws_eks_node_group_min_size             = var.hello_minio_aws_eks_node_group_min_size

}

你好_世界/main.tf#L37-L51

这些是 EKS 模块所需的变量

hello_minio_aws_eks_cluster_name = "hello_minio_aws_eks_cluster"
hello_minio_aws_eks_cluster_endpoint_private_access = true
hello_minio_aws_eks_cluster_endpoint_public_access = true
hello_minio_aws_eks_cluster_public_access_cidrs = ["0.0.0.0/0"]
hello_minio_aws_eks_node_group_name = "hello_minio_aws_eks_node_group"
hello_minio_aws_eks_node_group_instance_types = ["t3.large"]
hello_minio_aws_eks_node_group_desired_size = 3
hello_minio_aws_eks_node_group_max_size = 5
hello_minio_aws_eks_node_group_min_size = 1

你好_世界/terraform.tfvars#L24-L32

最后我们将应用配置。仍在hello_world目录中时运行以下terraform命令。启动和运行整个基础架构大约需要 15-20 分钟。最后,您应该会看到类似于以下的输出:

$ terraform init


…TRUNCATED…


$ terraform apply


…TRUNCATED…


hello_minio_aws_eks_cluster_name = "hello_minio_aws_eks_cluster"
hello_minio_aws_eks_cluster_region = "us-east-1"

…TRUNCATED…

Finished: SUCCESS


更新您的--kubeconfig默认配置以使用我们刚刚使用命令创建的集群aws eks和可从先前--region--name输出中获得。

$ aws eks --region us-east-1 update-kubeconfig \
    --name hello_minio_aws_eks_cluster

检查以验证您可以获得节点列表

$ kubectl get no
NAME                           STATUS   ROLES    AGE    VERSION
ip-10-0-105-186.ec2.internal   Ready    <none>   3d8h   v1.23.9-eks-ba74326
ip-10-0-75-92.ec2.internal     Ready    <none>   3d8h   v1.23.9-eks-ba74326
ip-10-0-94-57.ec2.internal     Ready    <none>   3d8h   v1.23.9-eks-ba74326

接下来,安装 EBS 驱动程序以便挂载 gp2 PVC。我们使用 gp2,因为这是 AWS 支持的默认存储类。

使用用于 AWS 秘密的相同凭据设置凭据awscli

kubectl create secret generic aws-secret \
    --namespace kube-system \
    --from-literal "key_id=${AWS_ACCESS_KEY_ID}" \
    --from-literal "access_key=${AWS_SECRET_ACCESS_KEY}"

应用 EBS 驱动程序资源:

$ kubectl apply -k "github.com/kubernetes-sigs/aws-ebs-csi-driver/deploy/kubernetes/overlays/stable/?ref=release-1.12"

您的 Kubernetes 集群现在应该准备就绪。

现在我们准备部署 MinIO。首先,克隆 MinIO 存储库

$ git clone https://github.com/minio/operator.git

由于这是 AWS,我们需要storageClassNamegp2打开以下文件并将所有引用更新storageClassName: standardstorageClassName: gp2每个 MinIO 租户都有自己的tenant.yaml包含 storageClassName 配置。根据您使用的租户,请务必相应地更新 storageClassName。

$ vim ./operator/examples/kustomization/base/tenant.yaml

将资源应用到 Kubernetes 以安装 MinIO

$ kubectl apply -k operator/resources

$ kubectl apply -k operator/examples/kustomization/tenant-lite

等待至少 5 分钟让资源出现,然后验证 MinIO 是否已启动并正在运行。

$ kubectl -n tenant-lite get po -o wide
NAME                                           READY   STATUS    RESTARTS      AGE   IP            NODE                           NOMINATED NODE   READINESS GATES
storage-lite-log-0                             1/1     Running   0             17m   10.0.94.169   ip-10-0-94-57.ec2.internal     <none>           <none>
storage-lite-log-search-api-66f7db97f5-j268m   1/1     Running   3 (17m ago)   17m   10.0.93.40    ip-10-0-94-57.ec2.internal     <none>           <none>
storage-lite-pool-0-0                          1/1     Running   0             17m   10.0.88.36    ip-10-0-94-57.ec2.internal     <none>           <none>
storage-lite-pool-0-1                          1/1     Running   0             17m   10.0.104.48   ip-10-0-105-186.ec2.internal   <none>           <none>
storage-lite-pool-0-2                          1/1     Running   0             17m   10.0.71.81    ip-10-0-75-92.ec2.internal     <none>           <none>
storage-lite-pool-0-3                          1/1     Running   0             17m   10.0.94.183   ip-10-0-94-57.ec2.internal     <none>           <none>
storage-lite-prometheus-0                      2/2     Running   0             15m   10.0.85.181   ip-10-0-94-57.ec2.internal     <none>           <none>

如果您注意到上面的输出,每个输出都storage-lite-pool-在不同的工作节点上。其中两个共享同一个节点,因为我们有 3 个节点,但这没关系,因为我们只有 3 个可用性区域 (AZ)。基本上在 3 个 AZ 和 4 个 MinIO pod 中有 3 个节点,每个 pod 有 2 个 PVC,这反映在8 Online下面的状态中。

$ kubectl -n tenant-lite logs storage-lite-pool-0-0

…TRUNCATED…

Status:         8 Online, 0 Offline.
API: https://minio.tenant-lite.svc.cluster.local
Console: https://10.0.88.36:9443 https://127.0.0.1:9443

Documentation: https://min.io/docs/minio/linux/index.html

您将需要 MinIO 控制台的 TCP 端口;在这种情况下是9443

$ kubectl -n tenant-lite get svc | grep -i console
storage-lite-console   ClusterIP   172.20.26.209   <none>        9443/TCP   6s

有了这些信息,我们就可以设置 Kubernetes 端口转发。我们39443为主机选择了端口,但这可以是任何端口,只要确保在通过 Web 浏览器访问控制台时使用相同的端口即可。

$ kubectl -n tenant-lite port-forward svc/storage-lite-console 39443:9443

Forwarding from 127.0.0.1:39443 -> 9443

Forwarding from [::1]:39443 -> 9443

使用以下凭据通过网络浏览器访问 MinIO 操作员控制台:

网址:https://localhost:39443

用户:minio

密码:minio123


Screen Shot 2022-11-07 at 9.59.27 AM.png


您现在拥有分布式 MinIO 集群的完整生产设置。以下是使用 Jenkins 实现自动化的方法

这是文本格式的执行shell命令

export PATH=$PATH:/usr/local/bin

cd ci-cd-deploy/terraform/aws/hello_world/
terraform init
terraform plan
terraform apply -auto-approve

最后的想法

在过去的几篇 CI/CD 系列博客中,我们向您展示了 MinIO 的敏捷性和灵活性。您可以使用 Packer 将其构建到任何您想要的东西中,并将其部署到任何需要的 VM 或 Kubernetes 集群中。这允许您的开发人员在他们的开发环境中尽可能接近生产基础设施,同时利用强大的安全功能,例如服务器端对象加密管理 IAM 策略以限制对存储桶的访问。

在生产环境中,您可能希望将 IAM 用户限制为特定策略,但这实际上取决于您的用例。出于演示目的,我们通过广泛的策略使事情变得简单,但在生产中,您可能希望将其缩小到特定资源和用户组。在稍后的博客中,我们将展示一些关于如何为不同可用区和区域设计基础设施的最佳实践。

您是否也想尝试使用kubectlJenkins 自动化该部分而不是手动应用?让我们知道您使用我们的教程构建了哪种类型的管道来跨多云规划、部署、扩展和保护 MinIO,并通过我们的Slack与我们联系并分享您的管道



上一篇 下一篇