欢迎来到我们的 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
使用以下命令安装 aws CLI
使用以下策略创建 AWS IAM 用户。 注意 创建用户后的 AWS_ACCESS_KEY_ID和。 AWS_SECRET_ACCESS_KEY
为 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> 3d 8h v1 .23.9 -eks-ba74326 ip -10-0-75-92. ec2.internal Ready <none> 3d 8h v1 .23.9 -eks-ba74326 ip -10-0-94-57. ec2.internal Ready <none> 3d 8h 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,我们需要 storageClassName将 gp2. 打开以下文件并将所有引用更新 storageClassName: standard为 storageClassName: 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 17 m 10.0.94.169 ip -10-0-94-57. ec2.internal <none> <none> storage-lite-log-search-api -66f 7db97f5-j268m 1 / 1 Running 3 ( 17 m ago) 17 m 10.0.93.40 ip -10-0-94-57. ec2.internal <none> <none> storage-lite-pool -0-0 1 / 1 Running 0 17 m 10.0.88.36 ip -10-0-94-57. ec2.internal <none> <none> storage-lite-pool -0-1 1 / 1 Running 0 17 m 10.0.104.48 ip -10-0-105-186. ec2.internal <none> <none> storage-lite-pool -0-2 1 / 1 Running 0 17 m 10.0.71.81 ip -10-0-75-92. ec2.internal <none> <none> storage-lite-pool -0-3 1 / 1 Running 0 17 m 10.0.94.183 ip -10-0-94-57. ec2.internal <none> <none> storage-lite-prometheus -0 2 / 2 Running 0 15 m 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
您现在拥有分布式 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 与我们联系并分享您的管道 !