Istio Ingress Controller

This task describes how to configure Istio to expose a service outside of the service mesh cluster. In a Kubernetes environment, the Kubernetes Ingress Resources allows users to specify services that should be exposed outside the cluster. However, the Ingress Resource specification is very minimal, allowing users to specify just hosts, paths and their backing services. The following are the known limitations of Istio ingress:

  1. Istio supports standard Kubernetes Ingress specification without annotations. There is no support for ingress.kubernetes.io annotations in the Ingress resource specifications. Any annotation other than kubernetes.io/ingress.class: istio will be ignored.
  2. Regular expressions in paths are not supported.
  3. Fault injection at the Ingress is not supported.

Before you begin

  • Setup Istio by following the instructions in the Installation guide.

  • Make sure your current directory is the istio directory.

  • Start the httpbin sample, which will be used as the destination service to be exposed externally.

    If you installed the Istio-Initializer, do

    kubectl apply -f samples/httpbin/httpbin.yaml
    

    Without the Istio-Initializer:

    kubectl apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml)
    

Configuring ingress (HTTP)

  1. Create a basic Ingress Resource for the httpbin service

    cat <<EOF | kubectl create -f -
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: simple-ingress
      annotations:
        kubernetes.io/ingress.class: istio
    spec:
      rules:
      - http:
          paths:
          - path: /status/.*
            backend:
              serviceName: httpbin
              servicePort: 8000
    EOF
    

    /.* is a special Istio notation that is used to indicate a prefix match, specifically a rule match configuration of the form (prefix: /).

Verifying ingress

  1. Determine the ingress URL:

    • If your cluster is running in an environment that supports external load balancers, use the ingress’ external address:

      kubectl get ingress simple-ingress -o wide
      
      NAME             HOSTS     ADDRESS                 PORTS     AGE
      simple-ingress   *         130.211.10.121          80        1d
      
      export INGRESS_HOST=130.211.10.121
      
    • If load balancers are not supported, use the ingress controller pod’s hostIP:

      kubectl -n istio-system get po -l istio=ingress -o jsonpath='{.items[0].status.hostIP}'
      
      169.47.243.100
      

      along with the istio-ingress service’s nodePort for port 80:

      kubectl -n istio-system get svc istio-ingress
      
      NAME            CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
      istio-ingress   10.10.10.155   <pending>     80:31486/TCP,443:32254/TCP   32m
      
      export INGRESS_HOST=169.47.243.100:31486
      
  2. Access the httpbin service using curl:

    curl -I http://$INGRESS_HOST/status/200
    
    HTTP/1.1 200 OK
    Server: meinheld/0.6.1
    Date: Thu, 05 Oct 2017 21:23:17 GMT
    Content-Type: text/html; charset=utf-8
    Access-Control-Allow-Origin: *
    Access-Control-Allow-Credentials: true
    X-Powered-By: Flask
    X-Processed-Time: 0.00105214118958
    Content-Length: 0
    Via: 1.1 vegur
    Connection: Keep-Alive
    
  3. Access any other URL that has not been explicitly exposed. You should see a HTTP 403

    curl -I http://$INGRESS_HOST/headers
    
    HTTP/1.1 403 FORBIDDEN
    Server: meinheld/0.6.1
    Date: Thu, 05 Oct 2017 21:24:47 GMT
    Content-Type: text/html; charset=utf-8
    Access-Control-Allow-Origin: *
    Access-Control-Allow-Credentials: true
    X-Powered-By: Flask
    X-Processed-Time: 0.000759840011597
    Content-Length: 0
    Via: 1.1 vegur
    Connection: Keep-Alive
    

Configuring secure ingress (HTTPS)

  1. Generate keys if necessary

    A private key and certificate can be created for testing using OpenSSL.

    openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /tmp/tls.key -out /tmp/tls.crt -subj "/CN=foo.bar.com"
    
  2. Update the secret using kubectl

    kubectl create -n istio-system secret tls istio-ingress-certs --key /tmp/tls.key --cert /tmp/tls.crt
    
  3. Create the Ingress Resource for the httpbin service

    cat <<EOF | kubectl create -f -
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: secure-ingress
      annotations:
        kubernetes.io/ingress.class: istio
    spec:
      tls:
        - secretName: istio-ingress-certs # currently ignored
      rules:
      - http:
          paths:
          - path: /status/.*
            backend:
              serviceName: httpbin
              servicePort: 8000
          - path: /delay/.*
            backend:
              serviceName: httpbin
              servicePort: 8000
    EOF
    

    Set the INGRESS_HOST to point to the ip address and the port number of the ingress service as shown earlier.

    Note: Envoy currently only allows a single TLS secret in the ingress since SNI is not yet supported. That means that the secret name field in ingress resource is not used, and the secret must be called istio-ingress-certs in istio-system namespace.

  4. Access the secured httpbin service using curl:

    curl -I -k https://$INGRESS_HOST/status/200
    

Using Istio Routing Rules with Ingress

Istio’s routing rules can be used to achieve a greater degree of control when routing requests to backend services. For example, the following route rule sets a 4s timeout for all calls to the httpbin service on the /delay URL.

   cat <<EOF | istioctl create -f -
   apiVersion: config.istio.io/v1alpha2
   kind: RouteRule
   metadata:
     name: status-route
   spec:
     destination:
       name: httpbin
     match:
       # Optionally limit this rule to istio ingress pods only
       source:
         name: istio-ingress
         labels:
           istio: ingress
       request:
         headers:
           uri:
             prefix: /delay/ #must match the path specified in ingress spec
                 # if using prefix paths (/delay/.*), omit the .*.
                 # if using exact match, use exact: /status
     route:
     - weight: 100
     httpReqTimeout:
       simpleTimeout:
         timeout: 4s
  EOF

If you were to make a call to the ingress with the URL http://$INGRESS_HOST/delay/10, you will find that the call returns in 4s instead of the expected 10s delay.

You can use other features of the route rules such as redirects, rewrites, routing to multiple versions, regular expression based match in HTTP headers, websocket upgrades, timeouts, retries, etc. Please refer to the routing rules for more details.

Note 1: Fault injection does not work at the Ingress

Note 2: When matching requests in the routing rule, use the same exact path or prefix as the one used in the Ingress specification.

Understanding ingresses

Ingresses provide gateways for external traffic to enter the Istio service mesh and make the traffic management and policy features of Istio available for edge services.

The servicePort field in the Ingress specification can take a port number (integer) or a name. The port name must follow the Istio port naming conventions (e.g., grpc-*, http2-*, http-*, etc.) in order to function properly. The name used must match the port name in the backend service declaration.

In the preceding steps we created a service inside the Istio service mesh and showed how to expose both HTTP and HTTPS endpoints of the service to external traffic. We also showed how to control the ingress traffic using an Istio route rule.

Cleanup

  1. Remove the secret and Ingress Resource definitions.

    kubectl delete ingress simple-ingress secure-ingress 
    kubectl delete -n istio-system secret istio-ingress-certs
    
  2. Shutdown the httpbin service.

    kubectl delete -f samples/httpbin/httpbin.yaml
    

Further reading