How to use our free cluster to provide a memorable URL for a free APEX app, rather than their autogenerated word soup.
Table of contents
Open Table of contents
Background
Within our free tier we get access to a free APEX workspace. I created a workspace and used one of the demo apps to have something to test with. After 7 days of no use you get an email alert that it will be paused and deleted without any use. The URL you get given is a bit of generated word soup, that is not easy to remember to be able to access the app and reset the clock.
Reading the Oracle documentation, there is the ability to use a custom URL, but this needs another load balancer and you only get 1 free one and we are using it for our K8s cluster.
So, can we use our cluster to provide a vanity URL?
How to connect to something not in the K8s cluster, from inside the K8s cluster
ExternalNames, these are an object that we can use in the cluster to provide it a way to know where to route our traffic.
The URL for my APEX workspace is g2f2e11ad187d04-apex.adb.uk-london-1.oraclecloudapps.com
, so I will provide it that, then I can reuse these same rules and patterns with any app in the workspace.
Below I am creating a resource in the external-services
namespace pointing to that URL.
apiVersion: v1
kind: Service
metadata:
name: apex
namespace: external-services
spec:
type: ExternalName
externalName: g2f2e11ad187d04-apex.adb.uk-london-1.oraclecloudapps.com
But First, Update the Traefik Config
By default Traefik deployed by Helm is set to not allow routing to ExternalNames, both via the CRD and normal Ingress. In the helm values, look for allowExternalNameServices
and update it to true
.
Routing to the external name
This took a lot of faffing about with. Most of the examples online I could find were using a (possibly old) different CRD and when I tried to use it my Traefik did not pick it up. I also tried to use a normal Ingress but Traefik did not pick this up either. In the end I final got the following to work. I will try to explain it as best I can.
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: apex
namespace: external-services
spec:
entryPoints:
- websecure
routes:
- match: Host(`apex.smale.cloud`)
kind: Rule
services:
- name: apex
passHostHeader: false
namespace: external-services
port: 443
The top 4 liens are standard for this type of resource and sets it to our namespace.
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: apex
namespace: external-services
In the spec, we are using the websecure
entrypoint, which is HTTPS. We are setting the match rule to be a subdomain (and I have a wildcard match on my DNS for *.smale.cloud) to be routed to the cluster. The passHostHeader
set to false
stops our domain name being forwarded to the APEX URL, that caused it to error as it was not known. We are pointing it at our ExternalName and giving it a port. In the docs for IngressRoute - scroll down for ExternalName where it gives some more details on how to match them up.
spec:
entryPoints:
- websecure
routes:
- match: Host(`apex.smale.cloud`)
kind: Rule
services:
- name: apex
passHostHeader: false
namespace: external-services
port: 443
With both of these, we are able to go to https://apex.smale.cloud/ords/r/dev/apex-pwa-reference/home and it will be proxied to https://g2f2e11ad187d04-apex.adb.uk-london-1.oraclecloudapps.com/ords/r/dev/apex-pwa-reference/home without the user knowing. This does have one drawback, you need to still know the path to get to the app.
Path Redirection
To enable us to visit a short URL (something like apex.smale.cloud/pwa
) and have it redirect to the full URL we can add another routes match and some middleware.
IngressRoute
Update the above IngressRoute to look like the following:
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: apex
namespace: external-services
spec:
entryPoints:
- websecure
routes:
- match: Host(`apex.smale.cloud`) && PathPrefix(`/pwa`)
kind: Rule
middlewares:
- name: apex-pwa-redirect
services:
- name: apex
passHostHeader: false
namespace: external-services
port: 443
- match: Host(`apex.smale.cloud`)
kind: Rule
services:
- name: apex
passHostHeader: false
namespace: external-services
port: 443
I think the more specific matcher needs to go first, or it might be internally that Traefik uses the more specific one first, but what we are doing here is adding a PathPrefix to the route and defining some middleware that should handle the redirection.
Middleware
We can now define the middleware to redirect any URL that has a path of /pwa
to the host with the /pwa
replaced with the home route of the APEX app.
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: apex-pwa-redirect
namespace: external-services
spec:
redirectRegex:
regex: ^(.*)/pwa$
replacement: ${1}/ords/r/dev/apex-pwa-reference/home
Conclusion
So there you have it, you can now go to https://apex.smale.cloud/pwa, and get taken to the example PWA app that I have deployed to my APEX workspace, without needing any word soup URLs. This will also help with brand recognition should that be needed too.