From Docker to KubernetesSchritt 8 von 10: Create Services and connect to the Database
Alle Labs

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.

🎉Inter-service communication

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.