Kubernetes、入口Nginx、证书管理器和外部DNS

2020-08-20 02:35:32

Kubernetes之所以伟大,是因为它的工具和实用程序生态系统可以满足运行和公开服务的许多需求。除了核心的Kubernetes功能(运行容器、在服务后面抽象它们并使用入口发布它们)之外,开源的入口-nginx、cert-manager和外部-DNS项目分别为您在Kubernetes中运行的服务提供入口功能、TLS证书和维护DNS记录。换言之,Kubernetes、Enress-nginx、cert-manager和External-DNS这四个开源项目为在Internet上安全地提供您的服务提供了完整的解决方案。本文将详细介绍如何使用带有工作负载标识的Google Kubernetes Engine(GKE)集群和Google Cloud DNS来设置这些项目以便协同工作。

本指南遵循安全性、可伸缩性和可靠性方面的最佳实践,创建了一个生产就绪的Kubernetes解决方案。因此,它的体系结构比简单的探索性环境更复杂。如果您正在寻找一个可以玩耍的“玩具”环境,我会推荐用于inress-nginx、cert-manager和外部DNS的文档站点,或者各种其他在线教程。

拿一杯咖啡舒舒服服地喝,我们将深入挖掘细节。

由于我们计划使用GKE工作负载标识,因此我们将完成创建项目和GKE集群的整个过程。如果您已经有一个Google Cloud Platform(GCP)项目和您满意的GKE集群配置,您可以跳过本部分。如果您想了解如何构建生产就绪的GKE解决方案,请继续阅读。

您需要一个具有活动帐单帐户的Google Cloud Platform帐户来创建所需的资源。创建这些资源可能需要花钱。您还需要在系统上安装gcloud和kubectl命令行实用程序。

我们将创建三个独立的GCP项目,一个用于Kubernetes集群,一个用于DNS,另一个用于保护Kubernetes机密的密钥管理服务(KMS)。在下面的第一个命令中,将基变量设置为唯一的值,因为GCP项目必须具有全局唯一的标识符。

$base=blog-k8s$env=Production$user=$(gcloud config get-value account)$labels=";env=$env,Purpose=$base,user=${user%@*}";$对于群集DNS KMS中的ext;执行\gcloud项目创建";$base-$ext";--labels=";$labels";\--name=";Kubernetes$ext";

创建项目后,我们需要将项目链接到现有的帐单帐户。您可以将shell变量BILLING设置为您的GCP计费帐户ID,或运行以下命令以使用您的GCP帐户中的第一个活动计费帐户。

$用于群集DNS KMS;执行\gCloud测试版计费项目link";$base-$ext";--billing-account=";$billing";;\Done。

$gcloud services enable--project=";$base-cluster";{容器,iam凭据}.googleapis.com$gcloud services enable--project=";$base-dns";dns.googleapis.com$gcloud services enable--project=";$base-kms";cloudkms.googleapis.com。

我们需要几个GCP服务帐户来为Kubernetes集群节点和工作负载提供访问GCP API所需的凭据。所有这些服务帐户都将在群集项目中创建。节点服务账号需要访问权限才能写入日志以及写入和查看集群项目中的指标。我们希望使用通配符证书,因此证书管理器必须配置为使用DNS-01质询来验证所有权。因此,证书管理器服务帐户需要能够在DNS项目中创建DNS记录。外部DNS服务帐户还需要能够管理DNS项目中的DNS记录。

首先,我们将创建每个服务帐户并在shell变量中捕获其电子邮件地址。

$node_sa_name=";kubernetes$base node";$gcloud IAM服务帐户创建";sa-node-$base";\--display-name=";$node_sa_name";--project=";$base-cluster";$node_sa_email=$(gcloud IAM服务-帐户列表--project=";$base-cluster";\--format=。DisplayName:$node_sa_name";)$cert_sa_name=";kubernetes$base cert-mananger";$gcloud IAM服务-帐户创建";sa-cert-$base";\--display-name=";$cert_sa_name";--project=";$base-cluster";$cert_sa_email=$(gcloud IAM服务-。--filter=";displayName:$cert_sa_name";)$edns_sa_name=";kubernetes$base外部-dns";$gcloud iam服务-帐户创建";sa-edns-$base";\--显示名称=";$edns_sa_name";--project=";$base-cluster";$edns_sa_email=$(gcloud iam服务-帐户列表--项目=";$base-cluster"。值(电子邮件)';--filter=";displayName:$edns_sa_name";)。

创建服务帐户后,我们将所需的IAM角色从适当的项目绑定到每个服务帐户。首先,我们将日志和度量角色绑定到节点服务帐户。

$用于监视中的角色。metricWriter monitor.viewer logging.logWriter;执行\g云项目Add-iam-policy-binding";$base-cluster";\--member=";serviceAccount:$node_sa_email";--Role=";Roles/$Role";;\Done。

接下来,我们将DNS项目中的DNS管理员角色绑定到cert-manager和外部DNS服务帐户。

$for sa_email";$cert_sa_email";$edns_sa_email";执行\gCloud Projects Add-IAM-policy-binding";$base-dns";\--Member=";serviceAccount:$sa_email";--Role=Roles/dns.admin;\Done。

最后,我们将cert-manager和外部DNS GCP服务帐户绑定到各自的Kubernetes工作负载,即在GCP中链接GCP服务帐户和Kubernetes服务帐户。

$gcloud IAM服务-帐户添加-iam-策略绑定";$cert_sa_email";\--member=";serviceAccount:$base-cluster.svc.id.goog[cert-manager/cert-manager]";\--角色=角色/iam.workloadIdentityUser--项目=$base-cluster$gcloud IAM服务-帐户添加-iam-策略绑定";$edns_sa_email";\--成员=";ServiceAccount:$base-cluster.svc.id.goog[external-dns/external-dns]";\--角色=角色/iam.workloadIdentityUser--项目=$BASE-CLUSTER。

创建有效的DNS区域有两个步骤:创建区域和注册域和名称服务器。我们使用以下命令在Google Cloud DNS中创建区域,将domain的值设置为您的DNS区域的完全限定名称。

您应该已经拥有这个域名,或者能够购买它。这通常是要花钱的。为了避免注册域名时所需的时间和金钱,您可以使用您已经注册的域名的子域。例如,如果您拥有my.com,则无需注册新域即可使用k8s.my.com。

$domain=k8s.atm.io.$zon=${domain%.};zone=${zon//./-}$gcloud DNS Managed-Zones创建";$zone";--DNS-name=";$domain";\--description=";$base$domain DNS";--DNSSEC-state=ON--Visibility=public\--labels=";$labels";--project=。

您下一步需要做什么取决于您的域名和域名注册商。如果您尚未注册您正在使用的域,则需要进行注册。一旦注册了域,您就需要为该域配置DNS。此命令将输出域的名称服务器。

如果您的域名已向注册商注册,您将向域名注册商提供域名服务器。如果您正在使用已注册的域的子域,您将在该域的DNS区域中为您的子域创建NS记录。请咨询您的域的注册商/DNS提供商,以了解如何添加NS记录。

下面的命令将输出DS记录,即DNSSEC配置,您可以提供您的注册商。

$ksk=$(gcloud DNS DNS密钥列表--zone=$zone--project=";$base-dns";\--filter=type=keySigning--format=';value(Id)';|head-n 1)$gcloud DNS-key描述";$ksk";--zone=";$zone";\--format=';value(DS_Record()&#。

再次向您的域的注册商/DNS提供商查询,以了解如何添加此记录。

我们将使用Google Cloud KMS对Kubernetes etcd数据库中的Kubernetes密钥进行加密。首先,我们在KMS项目中创建密钥环和密钥加密密钥。

$region=us-center 1$gcloud KMS密钥环创建";ring-$base";--location=";$region";--project=";$base-kms";$gcloud KMS密钥创建";key-$base";--keyring=";ring-$base";--Purpose=Encryption\--Labels=";--location=&。

接下来,我们授予集群项目中的GKE服务帐户访问新创建的密钥的权限。

$project_id=$(gcloud项目描述";$base-cluster";--格式=';值(ProjectNumber)';)$gke_sa=service-$project_id@container-engine-robot.iam.gserviceaccount.com$gcloud KMS密钥添加-iam-策略绑定";密钥-$base";--keyring=";环-$base";\--成员=";服务帐户:$gke_sa";--role=roles/cloudkms.cryptoKeyEncrypterDecrypter\--位置="。$region";--project=";$base-kms";

我们将为Kubernetes集群创建专用的私有网络(VPC)网络,将其与Internet和项目中的任何其他资源隔离。

$gCloud计算网络子网创建";子网-$base";\--network=";net-$base";--range=10.0.0.0/22\--description=";Kubernetes子网$base";\--enable-private-ip-google-access--Purpose=private\--region=";$region";--project=";$base-cluster";\--辅助范围。SVC=10.0.16.0/20,Pod=10.12.0.0/14";

由于我们要创建一个具有私有节点的GKE集群,因此必须为我们的子网创建一个NAT,以便我们的工作负载可以访问Internet。即使您的工作负载不需要访问Internet,集群节点也需要它来下载cert-manager和inress-nginx的Docker映像。在创建NAT之前,我们使用以下命令创建其路由器。

$gCloud计算路由器NAT创建";NAT-$BASE";--路由器=";路由器-$BASE";\--AUTO-ALLOCATE-NAT-EXTERNAL-IPS--Region=";$Region";\--nat-custom-subnet-ip-ranges=";subnet-$base,子网-$BASE:服务,子网-$BASE:POD";\--PROJECT=";$BASE-CLUSTER";

所有支持基础设施就绪后,我们现在可以创建Kubernetes集群了。我们将创建一个区域私有GKE集群,利用主授权网络、自动伸缩、自动修复、使用常规发布通道的自动升级、工作负载标识、网络策略、运行Container优化操作系统的屏蔽节点和使用Containerd运行时。我们没有启用HttpLoadBalance附加组件,因为我们使用的是inress-nginx。

$key_id=projects/$base-kms/locations/$region/keyRings/ring-$base/cryptoKeys/key-$base$mCIDR=172.19.13.32/28$gcloud测试版容器群集创建";gke-$base";\--启用-自动修复-启用-自动升级\--元数据禁用-遗留端点=TRUE\--标签=";$Labels";--节点-标签=";$Label";\-Tags=";Kubernetes-Worker,$base,$env,${user%@。\--启用自动缩放--服务帐户=";$NODE_SA_EMAIL";\--workload-metadata-from-node=GKE_METADATA_SERVER\--屏蔽完整性监控--屏蔽-安全启动\--附加=水平PodAutoscaling,网络策略,节点本地DNS\--数据库加密密钥=";$KEY_ID";--NO-ENABLE-BASIC-AUTH\--ENABLE-IP-ALIAS--NO-ENABLE-旧版授权\--ENABLE-NETWORK-POLICATION--ENABLE-屏蔽节点\-ENABLE-STARDIVER-KUBERNETES\--identity-namespace=";$base-cluster.svc.id.goog";\--IMAGE-TYPE=COS_CONTAINERD--无问题--客户端证书\--MACHINE-TYPE=e2-STANDARD-2--MAX-NODES=3--MIN-NODES=1\-NETWORK=";NET-$BASE";--SUBNET=";子网-$BASE";\--释放通道=常规--启用-主授权网络\--主授权网络=";$(cURL-s https://icanhazip.com/)/32";\--启用-专用节点--主-IPV4-CIDR=";$MCIDR";\--maintenance-window-start=2000-01-01T22:00:00Z\--Maintenance-Window-End=2000-01-02T05:00:00Z\--maintenance-window-recurrence=';FREQ=WEEKLY;BYDAY=SA,SU';\--Region=";$Region";--PROJECT=";$BASE-CLUSTER";

以上命令需要几分钟才能完成。它配置仅包含运行命令$(cURL https://icanhazip.com/)/32.)的计算机的外部IP地址的单个主授权网络。如果您需要从其它系统访问Kubernetes API(例如使用kubectl),您可以通过向--master-Authorized-Networks命令行选项参数添加其他逗号分隔的CIDR块来添加其他网络。有关此选项和其他选项的更多信息,请参见集群创建手册页。

接下来,获取Kubernetes集群凭据并配置本地客户端(包括kubectl)以使用它们。

默认情况下,GCP不会授予您对刚创建的群集的管理访问权限。运行以下命令以授予您的GCP用户对GKE群集的管理访问权限。

我们将遵循GKE上的inress-nginx的标准安装过程,并进行一些调整。在部署inress-nginx之前,我们将创建一个GCP外部IP地址。这将允许入口nginx控制器服务的负载均衡器以及我们的服务在升级、迁移等过程中拥有稳定的IP地址。

$gcloud计算地址创建";ip-nginx-$base";\--description=";inress-nginx服务负载均衡器IP";\--network-tier=premium--region=";$region";--project=";$base-cluster";$ip=$(gcloud计算地址描述";ip-nginx-$base";\-format=';value。--project=";$base-cluster";)。

Inress-nginx的最新版本包括验证WebHook端点。此WebHook向Kubernetes API注册自身,以便在使用所有入口资源规范创建或更新入口之前对其进行验证。此端点侦听端口8443,并且必须可以从Kubernetes API服务器访问。由于默认防火墙规则不允许从API服务器访问端口8443上的节点,因此我们添加了一个允许访问的防火墙规则。

$gCloud Computer Firewall-Rules create";fw-nginx-$base";\--Allow=tcp:8443--description=";inress-nginx webhoke";\--direction=inress--network=";NET-$base";--Priority=1,000\--源范围=";$mCIDR";--target-service-accounts=";$node_sa_email";\--项目=";$BASE-CLUSTER";

要创建inress-nginx资源,我们可以使用它的标准YAML规范,但需要做一个修改:将inress-nginx-Controller负载均衡器服务的IP地址显式设置为我们上面创建的地址。我们将使用yq命令行实用程序和一些awk添加loadBalancerIP属性,然后使用kubectl创建资源。

$in_yaml=inress-nginx.yaml$curl-slo";$in_yaml";https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.34.1/deploy/static/provider/cloud/deploy.yaml$yq w\-d$(awk';/^Kind:/{Kind=$2;d++}\/^名称:入口-nginx-控制器$/{if(Kind==#34;服务";){打印d-1;退出}}';";$in_YAML";)\";$in_YAML";';spec.loadBalancerIP';";$IP";|kubectl Apply-f-$rm";$in_YAML";

与ingress-nginx类似,我们可以使用cert-manager的标准安装说明,只是增加了一点:将工作负载标识注释添加到cert-manager服务帐户。这是将kubernetes服务帐户cert-manager与我们在上面DNS项目中绑定到DNS admin角色的gcp服务帐户链接起来的最后一步,即链接gcp服务帐户和GKE中的kubernetes服务帐户。

$cm_yaml=cert-manager er.yaml$curl-slo";$cm_yaml";\https://github.com/jetstack/cert-manager/releases/download/v0.16.1/cert-manager.yaml$yq w\-d$(awk';/^Kind:/{Kind=$2;d++}\/^名称:证书管理器$/{if(Kind=#34;服务帐户";){打印d-1;退出}}';";$cm_YAYA。)\";$cm_yaml";';metadata.annotations.";iam.gke.io/gcp-service-account";';";$cert_sa_email";|\kubectl Apply-f-$rm";$cm_yaml";

当使用kubectl将外部DNS部署到Kubernetes集群时,外部DNS不提供一组标准的资源规范供使用。幸运的是,它确实提供了使用Google Cloud DNS和inress-nginx部署外部DNS的示例资源规范,我们可以根据自己的目的进行修改。从示例开始,我们将添加一个外部DNS名称空间,将工作负载标识注释添加到服务帐户,并使用我们的GCP项目和域的值更新容器参数。我们已经在GitHub上提供了一组经过适当修改的资源规范,因此我们将以此为起点,然后使用我们的GCP DNS项目、域和外部DNS GCP服务帐户电子邮件地址对其进行修改。

$cURL-SL https://raw.githubusercontent.com/atomist-blogs/iac-gke/main/k8s/external-dns.yaml|\sed-e#34;/gcp-service-account:/s/:.*/:$edns_sa_email/";\-e";/domain-filter=/s/=.*/=${domain%.}/";\-e";/google-project=/s/=.*/=$base-dns/";\-e";/txt-owner-id=/s/=.*/=$base/";|kubectl应用-f-。

这需要接受很多东西,但是现在您有了一个生产就绪的Kubernetes集群,能够安全地公开您的服务。我希望您更好地理解Kubernetes、GKE、Enress-nginx、cert-manager、外部DNS、工作负载标识,以及它们是如何协同工作来帮助您在Internet上安全地使用工作负载的。如果在经历了所有这些之后,您认为手动创建所有这些资源是疯狂的,我同意这一点。这里是一个利用Pulumi的基础设施即代码存储库,因此您可以使用一个命令来管理所有这些资源:ATOM-BLOGS/IAC-GKE。一旦您的资源在Kubernetes中启动并运行,请确保使用Atomist监视它们。如果您有任何问题或改进建议,请不要犹豫与我联系。I';m@dd在Kubernetes和GCP Slake工作区中,或者您可以在ATOM-BLOGS/IAC-GKE中创建问题。感谢您读完这篇文章!

获取您所需的知识和灵感,以做好您的工作并交付优秀的软件。