Ingress

This section describes how to expose a service to the internet by defining Ingress rules.

What is Ingress?

Ingress allows to expose a service to the internet by defining its HTTP layer address. Ingress settings include:

  • TLS certification
  • host name
  • path endpoint (optional)
  • service and service port

The Ingress services, when detecting a new or modified Ingress entry, will create/update the DNS record for the defined hostname, will update the load balancer to use a TLS certificate and route the requests to the cluster nodes, and will define the routes that find the right service based on the hostname and the path.

More details about the general Ingress in Kubernetes can be found in the official Ingress Resources.

How to setup Ingress?

Let’s assume that we have a deployment with label application=test-app, providing an API service on port 8080 and an admin UI on port 8081. In order to make them accessible from the internet, we need to create a service first.

Create a service

The service definition looks like this, create it in the apply directory as service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: test-app-service
  labels:
    application: test-app-service
spec:
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080
    name: main-port
  - port: 8081
    protocol: TCP
    targetPort: 8081
    name: admin-ui-port
  selector:
    application: test-app

Note that we didn’t define the type of the service. This means that the service type will be the default ClusterIP, and will be accessible only from inside the cluster.

Create the Ingress rules

Let’s assume that we want to access this API and admin UI from the internet with the base URL https://test-app.playground.zalan.do, and we want to access the UI on the path /admin while all other endpoints should be directed to the API. We can create the following Ingress entry in the apply directory as ingress.yaml:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: test-app
spec:
  rules:
  - host: test-app.playground.zalan.do
    http:
      paths:
      - backend:
          serviceName: test-app-service
          servicePort: main-port
      - path: /admin
        backend:
          serviceName: test-app-service
          servicePort: admin-ui-port

Once the changes were applied by the pipeline, the API and the admin UI should be accessible at https://test-app.playground.zalan.do and https://test-app.playground.zalan.do/admin. (If the load balancer and/or the DNS entry are newly created, it can take ~1 minute for everything to be ready.) Already provisioned X509 Certificate (IAM and ACM) will be found and matched automatically for your Ingress resource.

Manually selecting a certificate

The right certificate is usually discovered automatically, but there might be occasions where the SSL certificate ID (ARN) needs to be specified manually (e.g. if a CNAME in another account points to our Ingress). Let’s assume we want to hard code our certificate that is used in the ALB to terminate TLS for https://test-app.playground.zalan.do/. We can create the following Ingress entry in the apply directory as ingress.yaml:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: test-app
  annotations:
    zalando.org/aws-load-balancer-ssl-cert: <certificate ARN>
spec:
  rules:
  - host: test-app.playground.zalan.do
    http:
      paths:
      - backend:
          serviceName: test-app-service
          servicePort: main-port

Certificate ARN

In the above template, the token <certificate ARN> is meant to be replaced with the ARN of a valid certificate available for your account. You can find the right certificate in one of the following two ways:

1. For standard IAM certificates:

aws iam list-server-certificates

… should display something like this:

{
    "ServerCertificateMetadataList": [
        {
            "ServerCertificateId": "ABCDEFGHIJKLMNOPFAKE1",
            "ServerCertificateName": "self-signed-cert1",
            "Expiration": "2026-12-13T08:31:06Z",
            "Path": "/",
            "Arn": "arn:aws:iam::123456789012:server-certificate/self-signed-cert1",
            "UploadDate": "2016-12-15T08:48:03Z"
        },
        {
            "ServerCertificateId": "ABCDEFGHIJKLMNOPFAKE2",
            "ServerCertificateName": "self-signed-cert2",
            "Expiration": "2026-12-13T08:51:22Z",
            "Path": "/",
            "Arn": "arn:aws:iam::123456789012:server-certificate/self-signed-cert2",
            "UploadDate": "2016-12-15T08:51:41Z"
        },
        {
            "ServerCertificateId": "ABCDEFGHIJKLMNOPFAKE3",
            "ServerCertificateName": "teapot-zalan-do",
            "Expiration": "2023-05-11T00:00:00Z",
            "Path": "/",
            "Arn": "arn:aws:iam::123456789012:server-certificate/teapot-zalan-do",
            "UploadDate": "2016-05-12T12:26:52Z"
        }
    ]
}

…where you want to use the Arn values.

2. For Amazon Certificate Manager (ACM) certificates:

aws acm list-certificates

…should print something like this:

{
    "CertificateSummaryList": [
        {
            "CertificateArn": "arn:aws:acm:eu-central-1:123456789012:certificate/12345678-1234-1234-1234-123456789012",
            "DomainName": "teapot.zalan.do"
        },
        {
            "CertificateArn": "arn:aws:acm:eu-central-1:123456789012:certificate/12345678-1234-1234-1234-123456789012",
            "DomainName": "*.teapot.zalan.do"
        }
    ]
}

…where you want to use the CertificateArn values.

Alternatives

You can expose an application with its own load balancer, described in the TLS Termination and DNS. The two methods can live next to each other, but they need to have separate service definitions (due to the different service types).