For my lab, I usually don’t need a specific route pointing to the istio ingress gateway. Neither do I need, for most scenarios, app-specific gateway definitions.

Without gateway injection (available in service mesh 2.3), the routes would have to be created in the same namespace where the “standard” ingress gateway is deployed, or another SMCP-managed gateway.

So, I here’s the setup for a wildcard route and respective istio configs, that is generic, as are my use cases / small apps.

First, allow wildcard subdomains in your ingress controller:

apiVersion: operator.openshift.io/v1
kind: IngressController
metadata:
  name: default
  namespace: openshift-ingress-operator
spec:
  ...
  routeAdmission:
    wildcardPolicy: WildcardsAllowed
  ...

Then, in the namespace where the generic ingress gateway is deployed, create the route:

kind: Route
apiVersion: route.openshift.io/v1
metadata:
  name: mesh-apps
  namespace: istio-system
spec:
  host: wildcard.mesh.apps.ocp4.out-of-my-head.de
  to:
    kind: Service
    name: istio-ingressgateway
    weight: 100
  port:
    targetPort: https
  tls:
    termination: passthrough
  wildcardPolicy: Subdomain

Yes, wildcard is correct. in the UI it should show up as *.mesh.apps....

Now, let’s create the gateway (Istio resource):

kind: Gateway
apiVersion: networking.istio.io/v1beta1
metadata:
  name: mesh-apps-gateway
  namespace: istio-system
spec:
  servers:
    - port:
        number: 8443
        protocol: HTTPS
        name: https
      hosts:
        - '*.mesh.apps.ocp4.out-of-my-head.de'
      tls:
        mode: SIMPLE
        credentialName: le-mesh-apps
  selector:
    istio: ingressgateway

The referenced credential is a secret that’s actually created by a hetzner-dns webhook for the cert-manager. More on that in a later blog post.

OK so, now I’m pretty much all set to just add apps to my mesh. Sample app to the rescue: https://github.com/jeichler/api-endpoint

Let’s deploy it (manifests to get started can also be found in the repo)

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/name: api-endpoint
    app.openshift.io/runtime: quarkus
  name: api-endpoint
spec:
  replicas: 3
  selector:
    matchLabels:
      app.kubernetes.io/name: api-endpoint
  strategy:
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        app.kubernetes.io/name: api-endpoint
        sidecar.istio.io/inject: "true"
    spec:
      affinity:
        podAntiAffinity:
          preferedDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: app.kubernetes.io/name
                    operator: In
                    values:
                      - api-endpoint
              topologyKey: kubernetes.io/hostname
      containers:
      - env:
        - name: KUBERNETES_NAMESPACE
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: QUARKUS_OPTS
          value: -Dquarkus.http.host=0.0.0.0
        - name: QUARKUS_HOME
          value: /home/quarkus/
        image: quay.io/jaeichle/api-endpoint:latest
        name: api-endpoint
        ports:
        - containerPort: 9080
          name: http
          protocol: TCP
        resources:
          requests:
            memory: "64Mi"
            cpu: "25m"
          limits:
            memory: "256Mi"
            cpu: "100m"
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      restartPolicy: Always
      securityContext: {}
      serviceAccountName: api-endpoint
      serviceAccount: api-endpoint
      terminationGracePeriodSeconds: 30

I’ll skip PDBs, service account creation etc. here and keep it short.

The service:

apiVersion: v1
kind: Service
metadata:
  name: api-endpoint
spec:
  ports:
  - name: http
    port: 8080
    protocol: TCP
    targetPort: 9080
  selector:
    app.kubernetes.io/name: api-endpoint

So far, so good.

Now, to expose my app, I don’t need to do anything else than just create a VirtualService.

kind: VirtualService
apiVersion: networking.istio.io/v1beta1
metadata:
  name: api-endpoint-mesh
spec:
  hosts:
    - api-endpoint.mesh.apps.ocp4.out-of-my-head.de
  gateways:
    - istio-system/mesh-apps-gateway
  http:
    - route:
        - destination:
            host: api-endpoint.bookinfo.svc.cluster.local
            port:
              number: 8080
          weight: 100
  tls:
    - match:
        - sniHosts:
            - api-endpoint.mesh.apps.ocp4.out-of-my-head.de
          port: 443
      route:
        - destination:
            host: api-endpoint.bookinfo.svc.cluster.local
            port:
              number: 8080
          weight: 100

After that, just run curl or open one of the endpoints in the browser. /ping being the most lightweight one, which then returns PONG. yay.

Now, that makes exposing new apps in my mesh fairly easy. A simple passthrough route, a gateway, that’s already there. so it’s just the bits I need for my application.