Repository navigation
07 K8s
Finally we will work on migrating our go-task to a Kubernetes cluster using Kind for local development and Helm for package management.
Kind allows you to run a local Kubernetes cluster using Docker containers, making it ideal for development and testing.
-
Install Kind:
- Download the Kind binary for your operating system from the official releases: https://github.com/kubernetes-sigs/kind/releases
- Make sure the binary is executable and available in your PATH.
-
Create a Kind Cluster:
kind create cluster --name go-task-cluster
- This will create a local Kubernetes cluster named
go-task-cluster. - You can verify the cluster is running using
kubectl cluster-info.
- This will create a local Kubernetes cluster named
-
Verify kubectl context:
kubectl config get-contexts
- Make sure the context for your Kind cluster is set as the current context. You can switch contexts using
kubectl config use-context <context-name>.
- Make sure the context for your Kind cluster is set as the current context. You can switch contexts using
Helm is a package manager for Kubernetes that helps you define, install, and manage applications.
-
Download Helm:
- Download the Helm binary for your OS from https://helm.sh/docs/intro/install/
- Make the binary executable and add it to your PATH.
-
Verify Installation:
helm version
I push the images to my public registry so we save some time.
A Helm chart is a collection of files that describe a Kubernetes application. Create a Helm chart for each of your microservices:
-
Create a Chart: To Create a chart, we use helm create command which will generate a basic helm chart, but since we have a special setup, we will work on our own copy.
helm create user-service
-
Modify Chart Files:
-
values.yaml: Define the configuration values for your service, including image names, ports, environment variables, resource limits, etc. -
templates/: This directory contains Kubernetes resource definition templates (YAML files) that will be used to create deployments, services, configmaps, etc. You'll need to modify these templates to match your service's requirements.
-
-
user-service/values.yaml:service: name: user-service commandPort: "3001" queryPort: "3000" image: commandRepo: rfashwal/go-task-user-command-service queryRepo: rfashwal/go-task-user-query-service tag: latest pullPolicy: Always environment: USER_SERVICE_COMMAND_NAME: user-command-service USER_SERVICE_COMMAND_PORT: "3001" USER_SERVICE_QUERY_NAME: user-query-service USER_SERVICE_QUERY_PORT: "3000" USER_SERVICE_DB_HOST: mysql.default.svc.cluster.local USER_SERVICE_DB_PORT: "3306" USER_SERVICE_DB_NAME: user_tracker USER_SERVICE_NATS_PORT: "4222" USER_SERVICE_NATS_SERVICE: nats.default.svc.cluster.local resources: {}
-
user-service/templates/deployment.yaml:apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "user-service.fullname" . }}-command labels: {{- include "user-service.labels" . | nindent 4 }} spec: replicas: 1 selector: matchLabels: {{- include "user-service.selectorLabels" . | nindent 6 }} template: metadata: {{- with .Values.podAnnotations }} annotations: {{- toYaml . | nindent 8 }} {{- end }} labels: {{- include "user-service.selectorLabels" . | nindent 8 }} spec: containers: - name: {{ include "user-service.name" . }}-command image: "{{ .Values.image.commandRepo }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - containerPort: {{ .Values.service.commandPort }} envFrom: - configMapRef: name: {{ include "user-service.fullname" . }}-command-config - secretRef: name: {{ include "user-service.fullname" . }}-secret resources: {{- toYaml .Values.resources | nindent 12 }} --- apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "user-service.fullname" . }}-query labels: {{- include "user-service.labels" . | nindent 4 }} spec: replicas: 1 selector: matchLabels: {{- include "user-service.selectorLabels" . | nindent 6 }} template: metadata: {{- with .Values.podAnnotations }} annotations: {{- toYaml . | nindent 8 }} {{- end }} labels: {{- include "user-service.selectorLabels" . | nindent 8 }} spec: containers: - name: {{ include "user-service.name" . }}-query image: "{{ .Values.image.queryRepo }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - containerPort: {{ .Values.service.queryPort }} envFrom: - configMapRef: name: {{ include "user-service.fullname" . }}-query-config - secretRef: name: {{ include "user-service.fullname" . }}-secret resources: {{- toYaml .Values.resources | nindent 12 }}
-
user-service/templates/services.yaml:apiVersion: v1 kind: Service metadata: name: {{ include "user-service.fullname" . }}-command-service labels: {{- include "user-service.labels" . | nindent 4 }} spec: type: ClusterIP ports: - port: {{ .Values.service.commandPort }} targetPort: {{ .Values.service.commandPort }} protocol: TCP name: http-command selector: {{- include "user-service.selectorLabels" . | nindent 4 }} --- apiVersion: v1 kind: Service metadata: name: {{ include "user-service.fullname" . }}-query-service labels: {{- include "user-service.labels" . | nindent 4 }} spec: type: ClusterIP ports: - port: {{ .Values.service.queryPort }} targetPort: {{ .Values.service.queryPort }} protocol: TCP name: http-query selector: {{- include "user-service.selectorLabels" . | nindent 4 }}
-
user-service/templates/cm.yaml:apiVersion: v1 kind: ConfigMap metadata: name: {{ include "user-service.fullname" . }}-command-config data: USER_SERVICE_DB_HOST: {{ .Values.environment.USER_SERVICE_DB_HOST }} USER_SERVICE_DB_PORT: "{{ .Values.environment.USER_SERVICE_DB_PORT }}" USER_SERVICE_DB_NAME: {{ .Values.environment.USER_SERVICE_DB_NAME }} USER_SERVICE_NATS_SERVICE: {{ .Values.environment.USER_SERVICE_NATS_SERVICE }} USER_SERVICE_NATS_PORT: "{{ .Values.environment.USER_SERVICE_NATS_PORT }}" USER_SERVICE_SERVICE_NAME: {{ .Values.environment.USER_SERVICE_COMMAND_NAME }} USER_SERVICE_SERVICE_PORT: "{{ .Values.environment.USER_SERVICE_COMMAND_PORT }}" --- apiVersion: v1 kind: ConfigMap metadata: name: {{ include "user-service.fullname" . }}-query-config data: USER_SERVICE_DB_HOST: {{ .Values.environment.USER_SERVICE_DB_HOST }} USER_SERVICE_DB_PORT: "{{ .Values.environment.USER_SERVICE_DB_PORT }}" USER_SERVICE_DB_NAME: {{ .Values.environment.USER_SERVICE_DB_NAME }} USER_SERVICE_SERVICE_NAME: {{ .Values.environment.USER_SERVICE_QUERY_NAME }} USER_SERVICE_SERVICE_PORT: "{{ .Values.environment.USER_SERVICE_QUERY_PORT }}"
-
user-service/templates/_helpers.tpl
{{/* Expand the name of the chart. */}} {{- define "user-service.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). If release name contains chart name it will be used as a full name. */}} {{- define "user-service.fullname" -}} {{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} {{- $name := default .Chart.Name .Values.nameOverride }} {{- if contains $name .Release.Name }} {{- .Release.Name | trunc 63 | trimSuffix "-" }} {{- else }} {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} {{- end }} {{- end }} {{- end }} {{/* Create chart name and version as used by the chart label. */}} {{- define "user-service.chart" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Common labels */}} {{- define "user-service.labels" -}} helm.sh/chart: {{ include "user-service.chart" . }} {{ include "user-service.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }} {{/* Selector labels */}} {{- define "user-service.selectorLabels" -}} app.kubernetes.io/name: {{ include "user-service.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} {{/* Create the name of the service account to use */}} {{- define "user-service.serviceAccountName" -}} {{- if .Values.serviceAccount.create }} {{- default (include "user-service.fullname" .) .Values.serviceAccount.name }} {{- else }} {{- default "default" .Values.serviceAccount.name }} {{- end }} {{- end }}
- Install Required Charts:
First we need to install mysql and nats charts
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add nats https://nats-io.github.io/k8s/helm/charts/
helm install mysql bitnami/mysql
helm install nats nats/nats-
Fetsh mysql password and create secret:
MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace default mysql -o jsonpath="{.data.mysql-root-password}" | base64 -d) kubectl create secret generic user-service-secret \ --from-literal=USER_SERVICE_DB_USER=root \ --from-literal=USER_SERVICE_DB_PASS=$MYSQL_ROOT_PASSWORD kubectl create secret generic task-service-secret \ --from-literal=TASK_SERVICE_DB_USER=root \ --from-literal=TASK_SERVICE_DB_PASS=$MYSQL_ROOT_PASSWORD
-
Init DB
-
Access mysql instance
kubectl run mysql-client --rm --tty -i --restart='Never' --image docker.io/bitnami/mysql:8.4.3-debian-12-r5 --namespace default --env MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD --command -- bash
-
Access mysql CLI
mysql -h mysql.default.svc.cluster.local -uroot -p"$MYSQL_ROOT_PASSWORD" -
Create the DBs
CREATE DATABASE IF NOT EXISTS user_tracker; USE user_tracker; CREATE TABLE IF NOT EXISTS users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(100) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL ); CREATE DATABASE IF NOT EXISTS task_tracker; USE task_tracker; CREATE TABLE IF NOT EXISTS tasks ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, assignee_id INT NULL, title VARCHAR(255) NOT NULL, description TEXT, status VARCHAR(50) );
-
Install User Service
helm install user-service deploy/kubernetes/user-service/.
-
-
Port-Forwarding: If you're using Kind, you can use
kubectl port-forwardto access services locally.kubectl port-forward service/user-service-query-service 3000:3000
- Ingress: For production deployments, you would typically set up an Ingress controller to expose your services externally.
We successfully deployed deployed our first service on a local k8s cluster, we can check and deploy other services with our final solution including all charts in the below branch
git checkout 07-complete
helm install notification-service deploy/kubernetes/notification-service/.
helm install task-service deploy/kubernetes/task-service/.