DNS
Overview
DNS names provide a standard way to make services accessible on the public internet. In Kubernetes applications, DNS names are most often used with Ingress objects to map incoming requests to an external Load Balancer to workloads running inside the cluster.
A DNS name is represented as a Resource of type dns
.
A TLS certificate is represented by a Resource of type tls-cert
.
By default, Humanitec will automatically generate dynamic DNS name under the .newapp.io
domain along with a valid SSL certificate. However, it is possible to configure different DNS names that will get picked up in different environments.
DNS names
A Resource Definition can be used to define how new DNS names should be generated. In most cases, this involves creating a subdomain to an existing domain. Humanitec provides 3 drivers which can be used to dynamically generate DNS names as required on existing domains:
DNS - Wildcard
This Driver assumes that a wildcard record exists in the appropriate zone file resolving all DNS requests on any subdomain to be directed to the cluster Load Balancer. The Driver will generate new unique subdomains and requires a static wildcard TLS certificate.
By definition, this Driver can only be used for environments in a single cluster as the wildcard Zonefile record can only point to a single IP.
For more information, see DNS Wildcard
DNS - Cloudflare
This Driver adds records to a Cloudflare Zonefile pointing at the Load Balancer for the relevant cluster. The Driver will generate new unique subdomains.
This Driver can be used with environments running on different clusters.
For more information, see DNS Cloudflare
DNS - Route53
This Driver adds records to a Route53 Hosted Zone pointing at the Load Balancer for the relevant cluster. The Driver will generate new unique subdomains.
This Driver can be used with environments running on different clusters.
For more information, see Route53
Static DNS
The echo
Driver type can be used to define static DNS names to be used for ingress.
As each workload that needs to be exposed requires a different DNS name the resource matching criteria used must exactly match the relevant workload.
Here is an example of the API call to create a Static Resource Definition where a workload called frontend-service
in the production
environment and will be exposed with the DNS name app.topicshare.com
:
- Create a file defining the Resource Definition:
cat << EOF > fe-dns-name.yaml
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: fe-dns-name
entity:
name: fe-dns-name
type: dns
driver_type: humanitec/echo
driver_inputs:
values:
host: app.topicshare.com
criteria:
- app_id: topic-share
env_id: production
res_id: workloads.frontend-service
EOF
- Use the
humctl create
command to create the Resource Definition in the Organization defined by your configured context:
humctl create -f fe-dns-name.yaml
rm fe-dns-name.yaml
curl https://api.humanitec.io/orgs/${HUMANITEC_ORG}/resources/defs \
-X POST \
-H "Authorization: Bearer ${HUMANITEC_TOKEN}" \
-H "Content-Type: application/json" \
--data-binary '
{
"id": "fe-dns-name",
"type": "dns",
"driver_type": "humanitec/echo",
"driver_inputs": {
"values": {
"host": "app.topicshare.com"
}
},
"criteria": [
{
"app_id": "topic-share",
"env_id": "production",
"res_id": "workloads.frontend-service"
}
]
}'
where ${HUMANITEC_TOKEN}
is an appropriate authentication token .
For information on TLS, see the TLS Certificates in the Security section.
Obtain the DNS name for a Deployment
Use these commands to obtain the effective DNS name for a Deployment:
- From the Application overview , select the name of the Application you’d like to get the DNS for.
- On the Status or Deployments tabs, select the Last deployment and then choose View Workload.
- The route specified for each DNS of the Workload is specified under Ingress.
You can get the DNS name of a Workload by running the following command with yq
:
humctl get active-resources \
--org ${HUMANITEC_ORG} \
--app ${APP_ID} \
--env ${ENV_ID} -o yaml \
| yq -r '.[] | select(.metadata.type == "dns") | .status.resource.host'
Likewise, you can perform the query using jq
:
humctl get active-resources \
--org ${HUMANITEC_ORG} \
--app ${APP_ID} \
--env ${ENV_ID} -o json \
| jq -r '.[] | select(.metadata.type == "dns") | .status.resource.host'
Where the following environment variables are set:
Variable | Example | Description |
---|---|---|
HUMANITEC_ORG |
my-org |
The name of your Humanitec Organization. |
APP_ID |
my-app |
The ID of your Humanitec Application. |
ENV_ID |
my-env |
The ID of your Humanitec Environment. |
The output will be similar to the following:
randomappname.newapp.io
For more information about implement Ingress in your Score file, see Routes.
Custom Resource Type for dns
When introducing a Custom Resource Type for dns
, you will also need to supply your own Resource Definition for the ingress
Type using a Driver other than the humanitec/ingress
Driver. That Driver relies on the built-in dns
Resource Type and will fail to properly locate the dns
Resources. Specifically, the ingress
Default Resource Definition also uses that Driver and cannot be used anymore.
To create a replacement for the default ingress
Resource Definition, use the ingress-default
example from the Template Driver ingress examples and adjust all references to the dns
Resource to use your Custom Type, like so:
host: ${resources['ORG_ID/dns'].outputs.host}
routePaths: ${resources['ORG_ID/dns<route'].outputs.path}
routePorts: ${resources['ORG_ID/dns<route'].outputs.port}
routeServices: ${resources['ORG_ID/dns<route'].outputs.service}
Default ingress (YAML format)
ingress-default.yaml
(view on GitHub )
:
# This Resource Definition provisions the equivalent of the humanitec/ingress driver with tls
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: default-ingress
entity:
name: default-ingress
type: ingress
driver_type: humanitec/template
driver_inputs:
values:
templates:
manifests: |
{{- /*
Only generate an ingress manifest if there are any routes defined.
*/ -}}
{{- if gt (len .driver.values.routePaths ) 0 -}}
ingress.yaml:
location: namespace
data:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
{{- if hasKey .driver.values "annotations" }}
annotations:
{{- range $k, $v := .driver.values.annotations }}
{{ $k | toRawJson }}: {{ $v | toRawJson }}
{{- end}}
{{- end}}
{{- if hasKey .driver.values "labels" }}
labels:
{{- range $k, $v := .driver.values.labels }}
{{ $k | toRawJson }}: {{ $v | toRawJson }}
{{- end}}
{{- end}}
name: {{ .id }}-ingress
spec:
{{- if .driver.values.class }}
ingressClassName: {{ .driver.values.class | toRawJson }}
{{- end }}
rules:
- host: {{ .driver.values.host | toRawJson }}
http:
paths:
{{- /*
We are guaranteed that .driver.values.routePaths is non-zero in
length from the top level if statement, so we don't need
to deal with the empty condition.
*/ -}}
{{- range $index, $path := .driver.values.routePaths }}
- path: {{ $path | toRawJson }}
pathType: {{ $.driver.values.path_type | default "Prefix" | toRawJson }}
backend:
service:
name: {{ index $.driver.values.routeServices $index | toRawJson }}
port:
number: {{ index $.driver.values.routePorts $index }}
{{- end }}
tls:
- hosts:
- {{ .driver.values.host | toRawJson }}
secretName: {{ .driver.values.tlsSecretName | toRawJson }}
{{- end -}}
outputs: |
id: {{ .id }}-ingress
# The host will be used from the dns resource with the same
# ResID and Class as this ingress.
host: ${resources.dns.outputs.host}
# These 3 selectors are guaranteed to return JSON arrays.
# They will all be empty if there are no routes referencing this.
routePaths: ${resources.dns<route.outputs.path}
routePorts: ${resources.dns<route.outputs.port}
routeServices: ${resources.dns<route.outputs.service}
tlsSecretName: ${resources.tls-cert.outputs.tls_secret_name}
# The following fields can be set based on the documented driver inputs
# for humanitec/ingress.
# Uncomment and add values.
# annotations: {}
# class: nginx
# labels: {}
# path_type: Prefix
Default ingress (Terraform format)
ingress-default.tf
(view on GitHub )
:
resource "humanitec_resource_definition" "default-ingress" {
driver_type = "humanitec/template"
id = "default-ingress"
name = "default-ingress"
type = "ingress"
driver_inputs = {
values_string = jsonencode({
"templates" = {
"manifests" = <<END_OF_TEXT
{{- /*
Only generate an ingress manifest if there are any routes defined.
*/ -}}
{{- if gt (len .driver.values.routePaths ) 0 -}}
ingress.yaml:
location: namespace
data:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
{{- if hasKey .driver.values "annotations" }}
annotations:
{{- range $k, $v := .driver.values.annotations }}
{{ $k | toRawJson }}: {{ $v | toRawJson }}
{{- end}}
{{- end}}
{{- if hasKey .driver.values "labels" }}
labels:
{{- range $k, $v := .driver.values.labels }}
{{ $k | toRawJson }}: {{ $v | toRawJson }}
{{- end}}
{{- end}}
name: {{ .id }}-ingress
spec:
{{- if .driver.values.class }}
ingressClassName: {{ .driver.values.class | toRawJson }}
{{- end }}
rules:
- host: {{ .driver.values.host | toRawJson }}
http:
paths:
{{- /*
We are guaranteed that .driver.values.routePaths is non-zero in
length from the top level if statement, so we don't need
to deal with the empty condition.
*/ -}}
{{- range $index, $path := .driver.values.routePaths }}
- path: {{ $path | toRawJson }}
pathType: {{ $.driver.values.path_type | default "Prefix" | toRawJson }}
backend:
service:
name: {{ index $.driver.values.routeServices $index | toRawJson }}
port:
number: {{ index $.driver.values.routePorts $index }}
{{- end }}
tls:
- hosts:
- {{ .driver.values.host | toRawJson }}
secretName: {{ .driver.values.tlsSecretName | toRawJson }}
{{- end -}}
END_OF_TEXT
"outputs" = "id: {{ .id }}-ingress\n"
}
"host" = "$${resources.dns.outputs.host}"
"routePaths" = "$${resources.dns<route.outputs.path}"
"routePorts" = "$${resources.dns<route.outputs.port}"
"routeServices" = "$${resources.dns<route.outputs.service}"
"tlsSecretName" = "$${resources.tls-cert.outputs.tls_secret_name}"
})
}
}