Managing Docker Apps With Kubernetes Ingress Controller
Alvin Lee
Think back to when your development team made the switch to Dockerized containers. What was once an application requiring multiple services on virtual machines transitioned to an application consisting of multiple, tidy Docker containers. While the result was a streamlined system, the transition likely was daunting.
Now, it’s time for another transformational leap: moving from a single set of containers to a highly available, orchestrated deployment of replica sets using Kubernetes. This may seem like a massive transformation, but you can reduce the bumps in the road by using an open source ingress controller to simplify your task and provide plugins that you can customize based on your need.
In this tutorial, we’ll start with a Dockerized application made up of three containers: a web server, a database and a key-value store. We will walk through how to deploy this application with Kubernetes (K8s) using Kong’s Kubernetes Ingress Controller (documentation) to expose the container’s ports for external access. Lastly, we’ll get familiar with how to set up some Kong plugins with the ingress controller.
Core Concepts
Before we dive in, let’s look briefly at some core concepts for our walkthrough.
Docker
Docker is often used in platform as a service (PaaS) offerings, approaches application development by isolating individual pieces of an application into containers. Each container is a standardized unit of software that can run on its own.
For example, a Postgresql database pegged to a specific version can run entirely within its own Docker container. The container is standard and runs on any developer’s machine. There are no longer questions like, “Why does the query work on my machine but not on your machine? What’s your environment setup?” When you run your application services within Docker containers, you ensure that everybody runs the same application within the same environment.
Kubernetes (K8s)
Applications quickly progressed from single Docker containers to a composition of multiple containers working together. One might use an application like Docker Compose to deploy a multiple container application.
However, the next step of the progression is to orchestrate multiple replicas of the same application as a cluster, distributing the load across replica nodes within the cluster and providing fallback nodes in case a single application node fails. Today’s de facto standard for this orchestration and management is Kubernetes. Many cloud service providers—including AWS, Azure and Google Cloud—offer Kubernetes.
Kong Kubernetes Ingress Controller
Ingress is a critical part of K8s, managing external access to the services inside of a Kubernetes cluster. Within a cluster, the web server container may talk to the database container, but what good is it if the external world can’t talk to the web server? In K8s, communication with the external world requires an ingress controller. The open source Kong Kubernetes Ingress Controller wraps around Kong Gateway and Kong’s various plugins to play this critical role.
The Basic Use Case
In our basic use case, we have an application composed of a web server (NGINX), a database (PostgreSQL) and a key-value store (Redis). This application typically runs as three Docker containers. We need to transition this application to K8s, but we need to set up an ingress to access our services from outside our K8s cluster.
Our Mini-Project Approach
For our mini-project walkthrough, we’re going to take this approach:
On your local machine, you’ll need to be comfortable working at the command line with the following tools installed:
kubectl — for running configuration commands on K8s clusters
Google Cloud SDK — for connecting to your GKE cluster, making it your kubectl context.
Are you ready to dive into containers and clusters? Here we go!
Step 1: Create a GKE Cluster
Assuming you have set up your Google Cloud Platform account, navigate to the Console and create a new project through the project list drop-down in the upper left:
Choose a name for your project (for example: k8s-with-kong) and create it. Working within that project, navigate through the left menu sidebar to find “Kubernetes Engine → Clusters.”
On the resulting page, click on the “Enable” button to use GKE with your project. This process might take one to two minutes for Google to start everything up for your project. After that, you’ll find yourself on the clusters page for GKE. Click on “Create.”
Choose to configure a “Standard” cluster. Set a name for your cluster, along with a region.
Next, in the left menu bar, find “NODE POOLS” and click on “default-pool:”
For the node pool, set the size to 1. We’ll keep the resource usage for our cluster small since this is just a demo mini-project.
Click “Create” at the bottom of the page.
Your K8s cluster will take a few minutes to spin up.
Use gcloud to configure cluster access for kubectl
With our GKE cluster up and running, we want to set up access to our cluster through kubectl on our local machine. To do this, we follow the simple steps on this GKE documentation page.
~$ gcloud init
# Follow the prompts to login to your Google Cloud account.
# Choose your cloud project (in our example: k8s-with-kong)
Do you want to configure a default Compute Region and Zone? (Y/n)? n
Next, we’ll generate a kubeconfig entry to run our kubectl commands against our GKE cluster. For this step, you will need the cluster name and region you specified when creating your cluster:
~$ gcloud container clusters get-credentials my-application --region=us-central1
Fetching cluster endpoint and auth data.
kubeconfig entry generated for my-application.
With that, we can start running commands through kubectl to configure our deployment.
Step 2: Deploy Application Through kubectl
To deploy our application, we will need to create a deployment.yml file and a service.yml file. The deployment.yml file should look like this:
Our deployment will run a single replica of our application, which consists of three containers. We have an nginx web server, which we will call server. The server’s container port of interest is port 80.
Next, we have a Postgresql database running in a container called postgres. The default user for this database container image is postgres. We’ll also set the password for that user to postgres. The database container’s port is 5432.
Lastly, we have our Redis key-value store, which will expose port 6379 on the container. On startup of the Redis server, we’ll set the password to redis.
With our deployment configuration in place, let’s apply this to our cluster:
~/project$ kubectl apply -f deployment.yml
deployment.apps/my-app created
After a few minutes, you can verify that your deployment is up:
~/project$ kubectl get deployment my-app
NAME READY UP-TO-DATE AVAILABLE AGE
my-app 1/111 3m15s
~/project$ kubectl get pods
NAME READY STATUS RESTARTS AGE
my-app-55db7c65f5-qjxs8 3/3 Running 0 4m16
~/project$ kubectl logs my-app-55db7c65f5-qjxs8 server
...
Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Configuration complete; ready for start up
~/project$ kubectl logs my-app-55db7c65f5-qjxs8 postgres
... database system is ready to accept connections
~/project$ kubectl logs my-app-55db7c65f5-qjxs8 redis
... Ready to accept connections
Next, we need to configure service.yml so that the ports on our cluster’s containers are accessible from outside the cluster.
Here, we are configuring a K8s Service, mapping incoming ports on our pod to target ports on the individual containers in a pod. For simplicity, we’ll use the same values for port and targetPort. If we can get a request to port 80 on our K8s pod, that request will be sent to port 80 of the server container (our nginx container). Similarly, requests to port 5432 will be mapped to port 5432 on our postgres container, while requests to port 6379 will map to port 6379 on our redis container.
Let’s update our cluster with this new service configuration:
~/project$ kubectl apply -f service.yml
service/my-app created
After a moment, we can check that our configuration is in place:
This is all good and fine, but you might notice from looking at the endpoint IP addresses that—while our ports are all exposed and mapped—we’re still working within a private network. We need to expose our entire K8s cluster to the outside world. For this, we need an ingress controller. Enter Kong.
Configuring Kong’s Ingress Controller is fairly straightforward. Let's go through the steps one at a time.
Deploy Kong Kubernetes Ingress Controller to GKE
Taking our cues from Kong’s documentation page on Kong Ingress Controller and GKE, we first need to create a ClusterRoleBinding to have proper admin access for some of the GKE cluster configurations we’re going to do momentarily. Create a file called gke-role-binding.yml with the following content:
~/project$ kubectl apply -f gke-role-binding.yml
clusterrolebinding.rbac.authorization.k8s.io/cluster-admin-user created
Next, we deploy the Ingress Controller, using the deployment and service configuration file that Kong has custom written and made available at https://bit.ly/k4k8s.
~/project$ kubectl apply -f https://bit.ly/k4k8snamespace/kong created
...
service/kong-proxy created
service/kong-validation-webhook created
deployment.apps/ingress-kong created
Check that the Ingress Controller deployed. It might take a minute for the kong-proxy EXTERNAL_IP to be provisioned:
~/project$ kubectl get services -n kong
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kong-proxy LoadBalancer 10.72.130.21434.71.43.980:32594/TCP,443:30982/TCP 71s
kong-validation-webhook ClusterIP 10.72.130.92 <none> 443/TCP 70s
Then, we set up an environment variable, PROXY_IP, to hold the IP address associated with the Kong proxy.
~/project$ export PROXY_IP=$(kubectl get -o \
jsonpath="{.status.loadBalancer.ingress[0].ip}" \
service -n kong kong-proxy)
~/project$ echo $PROXY_IP
34.71.43.9 # Your IP address will differ
~/project$ curl -i $PROXY_IP
HTTP/1.1404 Not Found
Date: Tue,18 May 202104:51:48 GMT
Content-Type: application/json; charset=utf-8Connection: keep-alive
Content-Length:48X-Kong-Response-Latency:1Server: kong/2.3.3{"message":"no Route matched with those values"}
Excellent. Our Kong Kubernetes Ingress Controller has deployed, and it is reachable at the PROXY_IP. It just needs to be configured for proper request routing.
Add an ingress to map HTTP requests to the web server
Next, let’s configure the Kong Kubernetes Ingress Controller to listen for HTTP requests to the root / path, then map those requests to port 80 of our K8s Service (which maps that request to port 80 of the NGINX server container). We’ll create a file called http-ingress.yml:
It works! We’ve successfully configured our Kong Kubernetes Ingress Controller to take HTTP requests and map them through our K8s Service and onto our NGINX container.
What if we want to talk to a container and port through a TCP connection rather than an HTTP request? For this, we’ll use Kong’s custom TCPIngress.
Add TCPIngress to map connection requests
By default, the Kong Proxy service set up through the Kong Kubernetes Ingress Controller listens on ports 80 and 443. This is why we were able to configure our Ingress to map HTTP requests to our NGINX server since HTTP requests go to port 80 by default.
For our use case, we need Kong to listen for TCP traffic on several additional ports that we’ll use for Postgres and Redis connections. Earlier, we deployed the Kong Kubernetes Ingress Controller by applying the custom configuration that Kong provided at https://bit.ly/k4k8s. Now, we want to apply two patches to that configuration to accommodate our specific TCP streaming needs.
First, create a file called patch-kong-deployment.yml, containing the following:
Here, we’re configuring Kong’s TCP stream to listen on ports 11111 (which we’ll use for postgres connections) and 22222 (which we’ll use for Redis connections). Next, create a file called patch-kong-service.yml, containing the following:
This patch modifies the K8s Service related to Kong Kubernetes Ingress Controller, exposing the ports that we need. Now, we apply these two patches:
~/project$ kubectl patch deploy -n kong ingress-kong \
--patch-file=patch-kong-deployment.yml
deployment.apps/ingress-kong patched
~/project$ kubectl patch service -n kong kong-proxy \
--patch-file=patch-kong-service.yml
service/kong-proxy patched
Now that we’ve patched Kong to listen for TCP connections on the proper ports, let’s configure our TCPIngress resource. Create a file called tcp-ingress.yml:
This configuration listens for TCP traffic on port 11111. It forwards that traffic to our K8s Service at port 5432. As you may recall, that Service maps port 5432 traffic to the postgres container at port 5432. Similarly, our TCPIngress forwards traffic on port 22222 to our Service’s port 6379, which subsequently reaches the redis container at port 6379.
Let’s apply this configuration:
~/project$ kubectl apply -f tcp-ingress.yml
tcpingress.configuration.konghq.com/my-app created
That should be everything. Now, let’s test.
~/project$ psql -h $PROXY_IP -p 11111 -U postgres
Password for user postgres: postgrespassword
psql (13.2 (Ubuntu 13.2-1.pgdg16.04+1))
Type "help" for help.
postgres=#
We were able to connect to the postgres container! Now, let’s try Redis:
~/project$ redis-cli -h $PROXY_IP -p 22222 -a redispassword
34.71.43.9:22222>
We’re in! We’ve successfully configured Kong Kubernetes Ingress Controller to map our HTTP requests to the web server and our TCP connections to the database and key-value store. At this point, you should have quite a foundation for tailoring the Kong Kubernetes Ingress Controller for your own business needs.
Before we wrap up our walkthrough, let’s experiment a bit by integrating some plugins with our Ingress Controller.
Step 4: Integrating Plugins With the Ingress Controller
Certificate Management and HTTPS
We’ll start by configuring our Ingress Controller to use cert-manager, which manages the deployment of SSL certificates. This will enable our NGINX web server to be accessible via HTTPS.
Install cert-manager to GKE cluster
To install cert-manager, we follow the steps outlined on the Kubernetes documentation page. The documentation steps mention creating a ClusterRoleBinding for cluster admin access if you are using GKE. However, we already did this earlier in our walkthrough.
Next, we install the CustomResourceDefinition to our cluster with cert-manager, and then we verify the installation:
~/project$ kubectl apply -f \
https://github.com/jetstack/cert-manager/releases/download/v1.3.1/cert-manager.yaml
~/project$ kubectl get pods --namespace cert-manager
NAME READY STATUS RESTARTS AGE
cert-manager-7c5c945df9-5rvj5 1/1 Running 0 1m
cert-manager-cainjector-7c67689588-n7db6 1/1 Running 0 1m
cert-manager-webhook-5759dc48f-cfwd6 1/1 Running 0 1m
Set up the domain name to point to kong-proxy IP
You’ll recall that we stored our kong-proxy IP address as PROXY_IP. Assuming you have control over a domain name, add a DNS record that resolves your domain name to PROXY_IP. For this example, I’m adding an A record to my domain (codingplus.coffee) that resolves the subdomain kong-k8s.codingplus.coffee to my kong-proxy IP address.
With our subdomain resolving properly, we need to modify our http-ingress.yml file to specify a host for HTTP requests rather than just use an IP address. You will, of course, use the domain name that you have configured:
Next, we create a ClusterIssuer resource for our cert-manager. Create a file called cluster-issuer.yml, with the following content (replace with your own email address):
Let’s apply the updated http-ingress.yml manifest:
~/project$ kubectl get certificates
NAME READY SECRET AGE
my-ssl-cert-secret True my-ssl-cert-secret 24s
Our certificate has been provisioned. Now, we can send requests using HTTPS:
~/project$ curl https://kong-k8s.codingplus.coffee<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
Step 5: Adding Plugins
Integrate Kong’s HTTP Log plugin
Next, let’s configure our Ingress Controller to use a Kong plugin. We’ll go with the HTTP Log plugin, which logs requests and responses to a separate HTTP server.
Create a Mockbin to receive log data
We’ll use Mockbin, which gives us an endpoint to tell our plugin to send its data. At Mockbin, go through the simple steps for creating a new bin. You’ll end up with a unique URL for your bin.
Create HTTP Log Plugin resource
Create a file called http-log-plugin.yml with the following content. Make sure to use your own Mockbin endpoint URL:
~/project$ kubectl apply -f http-log-plugin.yml
kongplugin.configuration.konghq.com/add-http-log-plugin created
Update Ingress manifest
Next, we’ll update http-ingress.yml again, making sure that our Ingress Controller knows to use our new plugin as it handles HTTP requests to the Nginx server:
We can check the request history for our bin at Mockbin. We see our most recent request posted to Mockbin, along with data about our request in the Mockbin request body:
It looks like our HTTP Log plugin is up and running!
Integrate Kong’s Correlation ID Plugin
Lastly, we’ll integrate one more Kong plugin: Correlation ID. This plugin appends a unique value (typically a UUID) to the headers for every request. First, we create the Correlation ID Plugin resource. Create a file called correlation-id-plugin.yml:
And again, we check our Mockbin history for the latest request. This time, when we look closely at the headers, we see my-unique-id, which comes from our Correlation ID plugin.
Success! Our Correlation ID plugin is working!
Conclusion
We’ve covered a lot of ground in this walkthrough. We started with a simple application consisting of three Docker containers. Step by step, we deployed our containers with Kubernetes, and we deployed the open source Kong Kubernetes Ingress Controller to manage external access to our cluster’s containers. Lastly, we further tailored our Ingress Controller by integrating cert-manager for HTTPS support and a few Kong plugins.
With that, you now have a comprehensive foundation for deploying your Dockerized application to Kubernetes with the help of Kong’s Kubernetes Ingress Controller. You’re well equipped to customize your deployment according to your own business application needs. 👍
If you have any additional questions, post them on Kong Nation. To stay in touch, join the Kong Community.
You may find these other Kubernetes tutorials helpful:
As Kubernetes has become the de facto orchestration platform for deploying cloud native applications , networking and traffic management have emerged as pivotal challenges when managing access to services and infrastructure. The core Kubernetes Ing
Peter Barnard
Getting Started With Kong Istio Gateway on Kubernetes With Kiali for Observability
Have you ever found yourself in a situation where all your service mesh services are running in Kubernetes, and now you need to expose them to the outside world securely and reliably? Ingress management is essential for your configuration and ope
While monitoring is an important part of any robust application deployment, it can also seem overwhelming to get a full application performance monitoring (APM) stack deployed. In this post, we'll see how operating a Kubernetes environment using the
Joseph Caudle
Kubernetes Ingress gRPC Example With a Dune Quote Service
APIs come in all different shapes and forms. In this tutorial, I'll show you a K8s Ingress gRPC example. I’ll explain how to deploy a gRPC service to Kubernetes and provide external access to the service using Kong's Kubernetes Ingress Controller.
Viktor Gamov
Using Kong Kubernetes Ingress Controller as an API Gateway
In this first section, I'll provide a quick overview of the business case and the tools you can use to create a Kubernetes ingress API gateway. If you're already familiar, you could skip ahead to the tutorial section or watch the video at the bott
Containerization and orchestration are becoming increasingly popular. According to a recent survey conducted by Market Watch, the global container market will exceed $5 billion by 2026. In 2019, that number was under 1 billion. These statistics sh
Michael Heap
Implement a Canary Release with Kong for Kubernetes and Consul
From the Kong API Gateway perspective, using Consul as its Service Discovery infrastructure is one of the most well-known and common integration use cases. With this powerful combination more flexible and advanced routing policies can be implemented
Kong
Ready to see Kong in action?
Get a personalized walkthrough of Kong's platform tailored to your architecture, use cases, and scale requirements.