Configuring ingress for sidecar charms

As mentioned in The Future of Charmed Operators on Kubernetes there’s a new approach to charming, that’s been available in preview since Juju 2.9-rc7, where the workload container and the charm run in the same pod. Because we’re no longer using pod-spec-set to configure our workloads, we needed a way to be able to configure an ingress resource to make our workload reachable from outside the k8s cluster.

To do this, we’ve created an nginx-ingress-integrator charm. Here’s an example of this charm in action:

# First add our Juju model
juju add-model gunicorn
# Ensure you’re running Juju 2.9-rc11 or later. We'll use the 
# name "ingress" in this model for brevity.
juju deploy nginx-ingress-integrator ingress
# Let’s also deploy a workload that we can relate it to.
git clone -b pebble
cd charm-k8s-gunicorn
charmcraft build
juju deploy ./gunicorn.charm --resource gunicorn-image=gunicorncharmers/gunicorn-app:20.0.4-20.04_edge
# Now relate the two charms
juju relate ingress gunicorn
# Now inspect our ingress resource
microk8s.kubectl describe ingress -n gunicorn

You would then get output similar to the following:

Name:             gunicorn-ingress
Namespace:        gunicorn
Default backend:  default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
  Host          Path  Backends
  ----          ----  --------
                /   gunicorn-service:80 (
Annotations: /
Events:         <none>

Assuming you’re using MicroK8s, you can test this end to end, by installing the ingress controller with microk8s enable ingress. You should then check the ingress class by running microk8s.kubectl describe pod -n ingress | grep ingress-class. In my case this was set to “public” so I ran juju config ingress ingress-class=public, and then I can connect to my application through the ingress as follows:

curl -H "Host: foo.internal"

This charm provides a library that developers can use to easily implement an ingress relation. This can be integrated into your charm by running charmcraft list-lib nginx-ingress-integrator followed by charmcraft fetch-lib charms.nginx_ingress_integrator.v0.ingress. As a trivial example, you would then implement the relations as follows:

from charms.nginx_ingress_integrator.v0.ingress import IngressRequires

# In __init__:
self.ingress = IngressRequires(
        "service-hostname": self.config["external_hostname"],
        "service-port": 80,

# In config-changed handler
self.ingress.update_config({"service_hostname": self.config["external_hostname"]})

The charm supports the following via the relation:

  • Rate limiting (with a whitelist for exclusions by CIDRs)
  • Setting maximum allowed body size for file uploads
  • Configuring retrying of errors against the next server
  • A session cookie to use for cookie-based session affinity, and the age of that cookie
  • The TLS certificate to use for your service if applicable

All of these options can also be configured at deploy time. In addition there’s also an ingress-class option to use, in the case that your cluster has multiple ingress controllers. This allows you to target the correct one.

Please give it a try, and add support for the relation in your sidecar charm, and let me know if you have any issues with it or questions about how it works. I’m looking for early feedback on it, as well as what other features you’d want to be able to support.