1
0
mirror of https://github.com/openshift/openshift-docs.git synced 2026-02-05 12:46:18 +01:00
Files
openshift-docs/cloud_experts_tutorials/cloud-experts-external-dns.adoc
2025-10-07 12:26:32 -04:00

400 lines
11 KiB
Plaintext

:_mod-docs-content-type: ASSEMBLY
[id="cloud-experts-external-dns"]
= Tutorial: Deploying the External DNS Operator on {product-title}
include::_attributes/attributes-openshift-dedicated.adoc[]
:context: cloud-experts-external-dns
toc::[]
//Mobb content metadata
//Brought into ROSA product docs 2023-09-20
//---
//date: '2021-06-10'
//title: External DNS for ROSA custom domain
//weight: 1
//tags: ["AWS", "ROSA"]
//authors:
// - Chris Kang
// - Dustin Scott
//---
The External DNS Operator deploys and manages `ExternalDNS` to provide the name resolution for services and routes from the external DNS provider, like Amazon Route 53, to {product-title} clusters. In this tutorial, we will deploy and configure the External DNS Operator with a secondary ingress controller to manage DNS records in Amazon Route 53.
[IMPORTANT]
====
The `External DNS` Operator does not support STS using IAM Roles for Service Accounts (IRSA) and uses long-lived Identity Access Management (IAM) credentials instead. This tutorial will be updated when the Operator supports STS.
====
[id="cloud-experts-external-dns-prerequisites"]
== Prerequisites
//I have not substituted ROSA in the instance below because this is a specific mention of HCP in a Classic only Tutorial.
* A {product-title} cluster
+
[NOTE]
====
{rosa-title} is not supported at this time.
====
+
* A user account with `cluster-admin` privileges
* The OpenShift CLI (`oc`)
* The Amazon Web Services (AWS) CLI (`aws`)
* A unique domain, such as `apps.example.com`
* An Amazon Route 53 public hosted zone for the above domain
[id="cloud-experts-external-dns-environment-setup"]
== Setting up your environment
. Configure the following environment variables:
+
[source,terminal]
----
$ export DOMAIN=<apps.example.com> <1>
$ export AWS_PAGER=""
$ export CLUSTER=$(oc get infrastructure cluster -o=jsonpath="{.status.infrastructureName}" | sed 's/-[a-z0-9]\{5\}$//')
$ export REGION=$(oc get infrastructure cluster -o=jsonpath="{.status.platformStatus.aws.region}")
$ export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
$ export SCRATCH="/tmp/${CLUSTER}/external-dns"
$ mkdir -p ${SCRATCH}
----
<1> Replace with the custom domain you want to use for the `IngressController`.
+
. Ensure all fields output correctly before moving to the next section:
+
[source,terminal]
----
$ echo "Cluster: ${CLUSTER}, Region: ${REGION}, AWS Account ID: ${AWS_ACCOUNT_ID}"
----
+
[NOTE]
====
The "Cluster" output from the previous command may be the name of your cluster, the internal ID of your cluster, or the cluster's domain prefix. If you prefer to use another identifier, you can manually set this value by running the following command:
[source,terminal]
----
$ export CLUSTER=my-custom-value
----
====
[id="secondary_ingress_controller_setup_{context}"]
== Secondary ingress controller setup
Use the following procedure to deploy a secondary ingress controller using a custom domain.
.Prerequisites
* A unique domain, such as `apps.example.com`
* A wildcard or SAN TLS certificate configured with the custom domain selected above (`CN=*.apps.example.com`)
.Procedure
. Create a new TLS secret from a private key and a public certificate, where `fullchain.pem` is your full wildcard certificate chain (including any intermediaries) and `privkey.pem` is your wildcard certificate's private key:
+
[source,terminal]
----
$ oc -n openshift-ingress create secret tls external-dns-tls --cert=fullchain.pem --key=privkey.pem
----
. Create a new `IngressController` resource:
+
[source,terminal]
----
$ cat << EOF | oc apply -f -
apiVersion: operator.openshift.io/v1
kind: IngressController
metadata:
name: external-dns-ingress
namespace: openshift-ingress-operator
spec:
domain: ${DOMAIN}
defaultCertificate:
name: external-dns-tls
endpointPublishingStrategy:
loadBalancer:
dnsManagementPolicy: Unmanaged
providerParameters:
aws:
type: NLB
type: AWS
scope: External
type: LoadBalancerService
EOF
----
+
[WARNING]
====
This `IngressController` example will create an internet accessible Network Load Balancer (NLB) in your AWS account. To provision an internal NLB instead, set the `.spec.endpointPublishingStrategy.loadBalancer.scope` parameter to `Internal` before creating the `IngressController` resource.
====
+
. Verify that your custom domain IngressController has successfully created an external load balancer:
+
[source,terminal]
----
$ oc -n openshift-ingress get service/router-external-dns-ingress
----
+
.Example output
[source,terminal]
----
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
router-external-dns-ingress LoadBalancer 172.30.71.250 a4838bb991c6748439134ab89f132a43-aeae124077b50c01.elb.us-east-1.amazonaws.com 80:32227/TCP,443:30310/TCP 43s
----
[id="cloud-experts-external-dns-prepare-aws-account"]
== Preparing your AWS account
. Retrieve the Amazon Route 53 public hosted zone ID:
+
[source,terminal]
----
$ export ZONE_ID=$(aws route53 list-hosted-zones-by-name --output json \
--dns-name "${DOMAIN}." --query 'HostedZones[0]'.Id --out text | sed 's/\/hostedzone\///')
----
+
. Prepare a document with the necessary DNS changes to enable DNS resolution for the canonical domain of the Ingress Controller:
+
[source,terminal]
----
$ NLB_HOST=$(oc -n openshift-ingress get service/router-external-dns-ingress -ojsonpath="{.status.loadBalancer.ingress[0].hostname}")
$ cat << EOF > "${SCRATCH}/create-cname.json"
{
"Comment":"Add CNAME to ingress controller canonical domain",
"Changes":[{
"Action":"CREATE",
"ResourceRecordSet":{
"Name": "router-external-dns-ingress.${DOMAIN}",
"Type":"CNAME",
"TTL":30,
"ResourceRecords":[{
"Value": "${NLB_HOST}"
}]
}
}]
}
EOF
----
+
The External DNS Operator uses this canonical domain as the target for CNAME records.
+
. Submit your changes to Amazon Route 53 for propagation:
+
[source,terminal]
----
aws route53 change-resource-record-sets \
--hosted-zone-id ${ZONE_ID} \
--change-batch file://${SCRATCH}/create-cname.json
----
+
. Create an AWS IAM Policy document that allows the `External DNS` Operator to update _only_ the custom domain public hosted zone:
+
[source,terminal]
----
$ cat << EOF > "${SCRATCH}/external-dns-policy.json"
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"route53:ChangeResourceRecordSets"
],
"Resource": [
"arn:aws:route53:::hostedzone/${ZONE_ID}"
]
},
{
"Effect": "Allow",
"Action": [
"route53:ListHostedZones",
"route53:ListResourceRecordSets"
],
"Resource": [
"*"
]
}
]
}
EOF
----
+
. Create an AWS IAM user:
+
[source,terminal]
----
$ aws iam create-user --user-name "${CLUSTER}-external-dns-operator"
----
. Attach the policy:
+
[source,terminal]
----
$ aws iam attach-user-policy --user-name "${CLUSTER}-external-dns-operator" --policy-arn $POLICY_ARN
----
+
[NOTE]
====
This will be changed to STS using IRSA in the future.
====
. Create AWS keys for the IAM user:
+
[source,terminal]
----
$ SECRET_ACCESS_KEY=$(aws iam create-access-key --user-name "${CLUSTER}-external-dns-operator")
----
. Create static credentials:
+
[source,terminal]
----
$ cat << EOF > "${SCRATCH}/credentials"
[default]
aws_access_key_id = $(echo $SECRET_ACCESS_KEY | jq -r '.AccessKey.AccessKeyId')
aws_secret_access_key = $(echo $SECRET_ACCESS_KEY | jq -r '.AccessKey.SecretAccessKey')
EOF
----
[id="cloud-experts-external-dns-install-external-dns-operator"]
== Installing the External DNS Operator
. Create a new project:
+
[source,terminal]
----
$ oc new-project external-dns-operator
----
. Install the `External DNS` Operator from the software catalog:
+
[source,terminal]
----
$ cat << EOF | oc apply -f -
apiVersion: operators.coreos.com/v1
kind: OperatorGroup
metadata:
name: external-dns-group
namespace: external-dns-operator
spec:
targetNamespaces:
- external-dns-operator
---
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: external-dns-operator
namespace: external-dns-operator
spec:
channel: stable-v1.1
installPlanApproval: Automatic
name: external-dns-operator
source: redhat-operators
sourceNamespace: openshift-marketplace
EOF
----
+
. Wait until the `External DNS` Operator is running:
+
[source,terminal]
----
$ oc rollout status deploy external-dns-operator --timeout=300s
----
+
. Create a secret from the AWS IAM user credentials:
+
[source,terminal]
----
$ oc -n external-dns-operator create secret generic external-dns \
--from-file "${SCRATCH}/credentials"
----
+
. Deploy the `ExternalDNS` controller:
+
[source,terminal]
----
$ cat << EOF | oc apply -f -
apiVersion: externaldns.olm.openshift.io/v1beta1
kind: ExternalDNS
metadata:
name: ${DOMAIN}
spec:
domains:
- filterType: Include
matchType: Exact
name: ${DOMAIN}
provider:
aws:
credentials:
name: external-dns
type: AWS
source:
openshiftRouteOptions:
routerName: external-dns-ingress
type: OpenShiftRoute
zones:
- ${ZONE_ID}
EOF
----
. Wait until the controller is running:
+
[source,terminal]
----
$ oc rollout status deploy external-dns-${DOMAIN} --timeout=300s
----
[id="cloud-experts-external-dns-deploy-a-sample-application"]
== Deploying a sample application
Now that the `ExternalDNS` controller is running, you can deploy a sample application to confirm that the custom domain is configured and trusted when you expose a new route.
. Create a new project for your sample application:
+
[source,terminal]
----
$ oc new-project hello-world
----
+
. Deploy a hello world application:
+
[source,terminal]
----
$ oc new-app -n hello-world --image=docker.io/openshift/hello-openshift
----
+
. Create a route for the application specifying your custom domain name:
+
[source,terminal]
----
$ oc -n hello-world create route edge --service=hello-openshift hello-openshift-tls \
--hostname hello-openshift.${DOMAIN}
----
. Check if the DNS record was created automatically by ExternalDNS:
+
[NOTE]
====
It can take a few minutes for the record to appear in Amazon Route 53.
====
+
[source,terminal]
----
$ aws route53 list-resource-record-sets --hosted-zone-id ${ZONE_ID} \
--query "ResourceRecordSets[?Type == 'CNAME']" | grep hello-openshift
----
. Optional: You can also view the TXT records that indicate they were created by ExternalDNS:
+
[source,terminal]
----
$ aws route53 list-resource-record-sets --hosted-zone-id ${ZONE_ID} \
--query "ResourceRecordSets[?Type == 'TXT']" | grep ${DOMAIN}
----
+
. Curl the newly created DNS record to your sample application to verify the hello world application is accessible:
+
[source,terminal]
----
$ curl https://hello-openshift.${DOMAIN}
----
+
.Example output
[source,terminal]
----
Hello OpenShift!
----