使用 [EKS] [Terraform] 和 external-dns 在配置文件中进行记录注册
这是什么?
只需要在配置文件中编写想要注册的记录,使用external-dns会自动将记录与Ingress的URL进行关联,并将记录注册到Route53。
我使用了Terraform来实现在AWS上进行配置的部分。我在很大程度上参考了先人留下的文章,可能是一种再创作的内容,但我也整理了一些与先人们不同的实现部分。
构成
我认为许多示例都使用了Service中的external-dns,但在本例中,我们使用了它来处理Ingress。
Terraform所需准备之物
使用kubectl_manifest资源在TF内定义了k8s资源。如果要使用WAF、安全组和ACM等,将其作为变量传递应该能简化构建过程。但是,这里唯一的困难是k8s和TF工作的边界变得有些模糊…
eks.tf 可以被简化为:
云原生技术(EKS).tf
resource "kubectl_manifest" "ingress_yaml" {
yaml_body = <<-EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: default
name: ${var.env}-ingress
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: instance
alb.ingress.kubernetes.io/wafv2-acl-arn: ${data.terraform_remote_state.waf_acmのoutputs} // WAF-ARN
alb.ingress.kubernetes.io/security-groups: ${aws_security_group.ingressのsg.id} // SG-ARN
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
alb.ingress.kubernetes.io/certificate-arn: ACMのARN
alb.ingress.kubernetes.io/ssl-redirect: "443"
external-dns.alpha.kubernetes.io/hostname: "${var.env}-domain name" // 使用したいレコードの名前
spec:
rules:
- host: ${var.env}-domain name // 使用したいレコードの名前
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service
port:
number: 80
EOF
}
// remote_stateを使っている場合、以下の記載も必要
data "terraform_remote_state" "waf_acm" {
backend = "s3"
config = {
bucket = "バケット名"
key = "tfstateの名前"
region = "リージョン"
}
}
.tfvars是一个文件扩展名。
另外,在tfvars文件中我已经定义了以下变量。这将根据环境自动命名,所以我在上面使用了${var.env}进行分隔。
env = "環境名"
外部_dns.tf
resource "kubernetes_service_account" "external_dns" {
metadata {
name = "external-dns"
namespace = "default"
annotations = {
"eks.amazonaws.com/role-arn" : "${aws_iam_role.external_dns_role.arn}"
}
}
}
resource "kubernetes_cluster_role" "external_dns" {
metadata {
name = "${var.env}-external-dns"
}
rule {
api_groups = [""]
resources = ["services", "endpoints", "pods"]
verbs = ["get", "watch", "list"]
}
rule {
api_groups = ["extensions", "networking.k8s.io"]
resources = ["ingresses"]
verbs = ["get", "watch", "list"]
}
rule {
api_groups = [""]
resources = ["nodes"]
verbs = ["list", "watch"]
}
}
resource "kubernetes_cluster_role_binding" "external_dns_viewer" {
metadata {
name = "${var.env}-external-dns-viewer"
}
role_ref {
api_group = "rbac.authorization.k8s.io"
kind = "ClusterRole"
name = kubernetes_cluster_role.external_dns.metadata[0].name
}
subject {
kind = "ServiceAccount"
name = kubernetes_service_account.external_dns.metadata[0].name
namespace = "default"
}
}
data "aws_iam_policy_document" "external_dns_assume_role_policy" {
statement {
actions = ["sts:AssumeRoleWithWebIdentity"]
effect = "Allow"
condition {
test = "StringEquals"
variable = "${replace(module.cluster.oidc_provider, "https://", "")}:sub"
values = ["system:serviceaccount:default:external-dns"]
}
principals {
identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/${module.cluster.oidc_provider}"]
type = "Federated"
}
}
}
resource "aws_iam_role" "external_dns_role" {
name = "${var.env}-external-dns-role"
assume_role_policy = data.aws_iam_policy_document.external_dns_assume_role_policy.json
}
resource "aws_iam_role_policy_attachment" "external_dns_policy_attachment" {
policy_arn = aws_iam_policy.external_dns_policy.arn
role = aws_iam_role.external_dns_role.name
}
resource "aws_iam_policy" "external_dns_policy" {
name = "${var.env}-external-dns-policy"
policy = file("external_dns.jsonのPATH")
}
我正在创建IAM策略并将其附加到角色上。我将策略的JSON文件与其他文件分开。我为角色添加了环境名称,并将其与服务账号关联。
外部_dns.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"route53:ChangeResourceRecordSets"
],
"Resource": [
"arn:aws:route53:::hostedzone/*"
]
},
{
"Effect": "Allow",
"Action": [
"route53:ListHostedZones",
"route53:ListResourceRecordSets"
],
"Resource": [
"*"
]
}
]
}
在Kubernetes一侧准备的东西
外部_dns.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
spec:
strategy:
type: Recreate
selector:
matchLabels:
app: external-dns
template:
metadata:
labels:
app: external-dns
spec:
serviceAccountName: external-dns
containers:
- name: external-dns
image: k8s.gcr.io/external-dns/external-dns:v0.10.2
args:
- --source=ingress
- --domain-filter=ホストゾーンの名前 # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
- --provider=aws
- --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization
- --aws-zone-type=public
- --registry=txt
- --txt-owner-id=ホストゾーンのID
securityContext:
fsGroup: 65534 # For ExternalDNS to be able to read Kubernetes and AWS token files
我們在這裡提供了在TF中創建的服務帳戶的名稱。
适用顺序: 应用的次序
基于本次设计的构造,感觉稍微有点麻烦,但还是写下来吧。
1. 进行 Terraform 应用
部署 external-dns.yaml。
3.删除 Ingress 并重新部署。
如果external-dns生成了这样的日志,并且在Route53上成功注册了记录。
kubectl logs "external-dns Podの名前" -f
~中略~
time="hogehoge" level=info msg="Applying provider record filter for domains: [”ホストゾーンの名前"]"
time="hogehoge" level=info msg="Desired change: CREATE "使用したいレコードの名前" A [Id: /hostedzone/"ホストゾーンのID"]"
time="hogehoge" level=info msg="Desired change: CREATE "使用したいレコードの名前" TXT [Id: /hostedzone/"ホストゾーンのID"]"
time="hogehoge" level=info msg="2 record(s) in zone ”ホストゾーンの名前". [Id: /hostedzone/"ホストゾーンのID"] were successfully updated"
curl https://"使用したいレコードの名前
で期待通りの反応が返ってくるか確認
考试前准备工作
我会记录遇到的错误和解决方法。
external-dns Pod一直無法正常啟動,一直重複執行重新啟動的動作。
kubectl get pod | grep dns
external-dns-hogehoge 0/1 CrashLoopBackOff 38 3h32m
kubectl logs external-dns-hogehoge -f
~中略~
time="hogehoge" level=fatal msg="failed to sync *v1.Ingress: context deadline exceeded"
参考了以下内容后,我认为服务帐户和ClusterRoleBinding存在问题。
以下是对两个网址的原文的中文释义:
1. https://github.com/kubernetes-sigs/external-dns/issues/2407:
外部DNS GitHub问题页面的编号为2407。
2. https://stackoverflow.com/questions/71407994/kubernetes-error-context-deadline-exceeded:
Kubernetes错误报告中的错误代码为”context deadline exceeded”。
因此,我已经修复并确认了两者都在同一个命名空间中。
resource "kubernetes_service_account" "external_dns" {
metadata {
name = "external-dns"
namespace = "default" // ココ
annotations = {
"eks.amazonaws.com/role-arn" : "${aws_iam_role.external_dns_role.arn}"
}
}
}
resource "kubernetes_cluster_role_binding" "external_dns_viewer" {
metadata {
name = "${var.env}-external-dns-viewer"
}
role_ref {
api_group = "rbac.authorization.k8s.io"
kind = "ClusterRole"
name = kubernetes_cluster_role.external_dns.metadata[0].name
}
subject {
kind = "ServiceAccount"
name = kubernetes_service_account.external_dns.metadata[0].name
namespace = "default" // ココ
}
}
由于使用了–domain-filter参数而不是使用主机区域名称,我们对其进行了修正,使用了要使用的记录名称。
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
spec:
strategy:
type: Recreate
selector:
matchLabels:
app: external-dns
template:
metadata:
labels:
app: external-dns
spec:
serviceAccountName: external-dns
containers:
- name: external-dns
image: k8s.gcr.io/external-dns/external-dns:v0.10.2
args:
- --source=ingress
- --domain-filter=ホストゾーンの名前 // ココ
- --provider=aws
- --policy=upsert-only
- --aws-zone-type=public
- --registry=txt
- --txt-owner-id=ホストゾーンのID
securityContext:
fsGroup: 65534
然后问题得以解决。
参考
以下内容的中文本地化表述
・https://aws.amazon.com/jp/premiumsupport/knowledge-center/eks-set-up-externaldns/
・https://github.com/kubernetes-sigs/external-dns
・https://qiita.com/guile/items/d41399f579d1a62634c0
・https://developer.mamezou-tech.com/containers/k8s/tutorial/ingress/external-dns/
・https://tech.polyconseil.fr/external-dns-helm-terraform.html
・https://febc-yamamoto.hatenablog.jp/entry/2018/12/28/172830
・Internal expert on the subject.
我已经参考了您给我的意见。非常感谢。