Create Services and connect to the Database
As nothing in Kubernetes stays forever (Pods are immutable, even nodes get rebuilt when updating in many cloud environments), using IP adresses for inter-pod communication is not a viable option. Kubernetes typically has DNS built in, which makes it a better choice. As we've seen in the previous step, Pod names in Deployments also have a unique part in it, which is also not a good option when our application should communicate with its database. Service objects are the method of choice if we want to expose our workload, either inside or outside the cluster. The most common Service types are:
- ClusterIP: Expose the Service in the cluster. The Service will be reachable via the Name
<service-name>.<namespace>.svc.cluster.local - NodePort: Expose the Service on a certain port on the Nodes which makes it possible to access the Service externally. Typically, you won't directly expose a Service via a NodePort, but this is often used when using LoadBalancers.
- LoadBalancer: Utilizes an external LoadBalancer to expose the Service. To use this, you need a controller inside the cluster which knows where and how to create the LoadBalancer.
In this Lab, we'll proceed keeping it simple and use a ClusterIP, which is also the default when not specifying a Service Type.
To create a Service for our already deployed MariaDB, there is also a command that helps you to create the manifest for it:
kubectl expose deployment mariadb --port 3306 -oyaml --dry-run=client > mariadb-service.yaml
Similar to the commands we used before, this will create a file containing the object definition for our MariaDB. The contents of the file should look like this:
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: mariadb
name: mariadb
spec:
ports:
- port: 3306
protocol: TCP
targetPort: 3306
selector:
app: mariadb
status:
loadBalancer: {}
When taking a closer look on the manifest, you might notice that there is no static connection to a Pod name or an IP address in a Service. The only thing used to connect the Service to Pods are the labels (app=mysql) on our Pods.
After you created the Service manifest, let's apply it on our Cluster again:
kubectl apply -f mariadb-service.yaml
As in our previous steps, this will apply the manifest and create the object in the cluster. You can verify this using the command:
kubectl get services
Now, you should see a Service called "mariadb". The Service object alone, does not necessarily mean that everything is working. There is a second command, that can show you if the Service has endpoints (Pods) attached to it. This can be shown using the command:
kubectl get endpoints
This command also shows the Service as well as the IP addresses and ports of the Pods connected to it. If a Pod gets created or deleted, the list of endpoints will change as the link between Service and Pods is made via the labels.
Finally, we will configure our application Deployment to use this database. To achieve this, open the learning-tracker-deployment.yaml again and add the following environment variables to our app container:
- DB_TYPE: "mysql"
- DB_USER: "app"
- DB_PASSWORD: "my_password"
- DB_HOST: "mariadb"
Finally, your Deployment manifest should look like this:
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: learning-tracker
name: learning-tracker
namespace: learning-tracker
spec:
replicas: 1
selector:
matchLabels:
app: learning-tracker
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: learning-tracker
spec:
containers:
- image: ghcr.io/tsclabs-eu/learning-tracker:v1.0.5
name: learning-tracker
resources: {}
env:
- name: LOG_OUTPUT
value: "console"
- name: DB_TYPE
value: "mysql"
- name: DB_USER
value: "app"
- name: DB_PASSWORD
value: "my_password"
- name: DB_HOST
value: "mariadb"
status: {}
This environment variables configure the application to use the MariaDB database we created before through the Service we configured. Notice, that the hostname is only mariadb in this case, which works because our app and database are in the same namespace. If they would be spread across multiple namespaces, you'd need to use mariadb.<namespace> or mariadb.<namespace>.svc.cluster.local to access it.
Now that everything is configured, let's apply the configuration again and port-forward to our workload:
kubectl apply -f learning-tracker-deployment.yaml
kubectl port-forward deploy learning-tracker 8080:3000
Afterward, you should see the application when connecting to http://localhost:8080/ in your browser.
You exposed your database inside the cluster and connected your application to it
As our app and database are separated now, it is possible to scale our application horizontally. Let's explore this in the next step.
