From 1083a39dac2e4d36c3db5dd216d67865becdc7c7 Mon Sep 17 00:00:00 2001 From: odidev Date: Tue, 6 Jan 2026 11:26:12 +0000 Subject: [PATCH] Updated Helm learning path to include GKE and Helm-based app deployments Signed-off-by: odidev --- .../helm-on-gcp/_index.md | 20 +- .../helm-on-gcp/benchmarking.md | 2 +- .../helm-on-gcp/gke-cluster-for-helm.md | 134 ++++++++ .../helm-on-gcp/images/nginx-browser.png | Bin 0 -> 46663 bytes .../helm-on-gcp/nginx-helm.md | 172 ++++++++++ .../helm-on-gcp/postgresql-helm.md | 296 ++++++++++++++++++ .../helm-on-gcp/redis-helm.md | 166 ++++++++++ 7 files changed, 784 insertions(+), 6 deletions(-) create mode 100644 content/learning-paths/servers-and-cloud-computing/helm-on-gcp/gke-cluster-for-helm.md create mode 100644 content/learning-paths/servers-and-cloud-computing/helm-on-gcp/images/nginx-browser.png create mode 100644 content/learning-paths/servers-and-cloud-computing/helm-on-gcp/nginx-helm.md create mode 100644 content/learning-paths/servers-and-cloud-computing/helm-on-gcp/postgresql-helm.md create mode 100644 content/learning-paths/servers-and-cloud-computing/helm-on-gcp/redis-helm.md diff --git a/content/learning-paths/servers-and-cloud-computing/helm-on-gcp/_index.md b/content/learning-paths/servers-and-cloud-computing/helm-on-gcp/_index.md index b3968c92bd..b65e279ef3 100644 --- a/content/learning-paths/servers-and-cloud-computing/helm-on-gcp/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/helm-on-gcp/_index.md @@ -1,16 +1,18 @@ --- title: Install and validate Helm on Google Cloud C4A Arm-based VMs -minutes_to_complete: 45 +minutes_to_complete: 60 who_is_this_for: This is an introductory topic intended for developers who want to get hands-on experience using Helm on Linux Arm64 systems, specifically Google Cloud C4A virtual machines powered by Axion processors. learning_objectives: - Provision an Arm-based SUSE Linux Enterprise Server (SLES) virtual machine on Google Cloud (C4A with Axion processors) - - Install Helm and kubectl on a SUSE Arm64 (C4A) instance - - Create and validate a local Kubernetes cluster (KinD) on Arm64 - - Verify Helm functionality by performing install, upgrade, and uninstall workflows + - Install and configure Helm and kubectl on a SUSE Arm64 (C4A) instance + - Create and connect to a Google Kubernetes Engine (GKE) cluster running on Arm-based nodes + - Deploy PostgreSQL, Redis, and NGINX on GKE using official Helm charts + - Validate Helm workflows by performing install, upgrade, rollback, and uninstall operations + - Verify application readiness and service access for PostgreSQL, Redis, and NGINX on GKE - Observe Helm behavior under concurrent CLI operations on an Arm64-based Kubernetes cluster prerequisites: @@ -32,8 +34,11 @@ armips: tools_software_languages: - Helm - Kubernetes - - KinD - kubectl + - GKE + - PostgreSQL + - Redis + - NGINX operatingsystems: - Linux @@ -57,6 +62,11 @@ further_reading: link: https://kubernetes.io/docs/ type: documentation + - resource: + title: Bitnami Helm Charts + link: https://github.com/bitnami/charts + type: documentation + weight: 1 layout: "learningpathall" learning_path_main_page: "yes" diff --git a/content/learning-paths/servers-and-cloud-computing/helm-on-gcp/benchmarking.md b/content/learning-paths/servers-and-cloud-computing/helm-on-gcp/benchmarking.md index e1aa5e051a..4e029ecd4b 100644 --- a/content/learning-paths/servers-and-cloud-computing/helm-on-gcp/benchmarking.md +++ b/content/learning-paths/servers-and-cloud-computing/helm-on-gcp/benchmarking.md @@ -1,6 +1,6 @@ --- title: Benchmark Helm concurrency on a Google Axion C4A virtual machine -weight: 6 +weight: 10 ### FIXED, DO NOT MODIFY layout: learningpathall diff --git a/content/learning-paths/servers-and-cloud-computing/helm-on-gcp/gke-cluster-for-helm.md b/content/learning-paths/servers-and-cloud-computing/helm-on-gcp/gke-cluster-for-helm.md new file mode 100644 index 0000000000..5c46f3d2f3 --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/helm-on-gcp/gke-cluster-for-helm.md @@ -0,0 +1,134 @@ +--- +title: Prepare GKE Cluster for Helm Deployments +weight: 6 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Overview +This section explains how to prepare a **Google Kubernetes Engine (GKE) cluster** for deploying Helm charts. +The prepared GKE cluster is used to deploy the following services using custom Helm charts: + +- PostgreSQL +- Redis +- NGINX + +This setup differs from the earlier KinD-based local cluster, which was intended only for local validation. + +## Prerequisites + +Before starting, ensure the following are already completed: + +- Docker installed +- kubectl installed +- Helm installed +- Google Cloud account available + +If Helm and kubectl are not installed, complete the **Install Helm** section first. + +### Verify kubectl Installation +Confirm that kubectl is available: + +```console +kubectl version --client +``` +You should see an output similar to: +```output +Client Version: version.Info{Major:"1", Minor:"26+", GitVersion:"v1.26.15-dispatcher", GitCommit:"5490d28d307425a9b05773554bd5c037dbf3d492", GitTreeState:"clean", BuildDate:"2024-04-18T22:39:37Z", GoVersion:"go1.21.9", Compiler:"gc", Platform:"linux/arm64"} +Kustomize Version: v4.5.7 +``` + +### Install Google Cloud SDK (gcloud) +The Google Cloud SDK is required to create and manage GKE clusters. + +**Download and extract:** + +```console +wget https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-460.0.0-linux-arm.tar.gz +tar -xvf google-cloud-sdk-460.0.0-linux-arm.tar.gz +``` + +**Install gcloud:** + +```console +./google-cloud-sdk/install.sh +``` +Restart the shell or reload the environment if prompted. + +### Initialize gcloud +Authenticate and configure the Google Cloud CLI: + +```console +./google-cloud-sdk/bin/gcloud init +``` + +During initialization: + +- Log in using a Google account +- Select the correct project +- Choose default settings when unsure + +### Set the Active Project +Ensure the correct GCP project is selected: + +```console +gcloud config set project YOUR_PROJECT_ID +``` + +### Enable Kubernetes API +Enable the required API for GKE: + +```console +gcloud services enable container.googleapis.com +``` + +### Create a GKE Cluster +Create a Kubernetes cluster that will host Helm deployments. + +```console +gcloud container clusters create helm-arm64-cluster \ + --zone us-central1-a \ + --machine-type c4a-standard-4 \ + --num-nodes 2 +``` + +- This creates a standard GKE cluster +- Node count and machine type can be adjusted later +- Arm64 compatibility depends on available node types in the region + +### Configure kubectl Access to GKE +Fetch cluster credentials: + +```console +gcloud container clusters get-credentials helm-arm64-cluster \ + --zone us-central1-a +``` + +### Verify Cluster Access +Confirm Kubernetes access: + +```console +kubectl get nodes +``` + +You should see an output similar to: +```output +NAME STATUS ROLES AGE VERSION +gke-helm-arm64-cluster-default-pool-f4ab8a2d-5h6f Ready 5h54m v1.33.5-gke.1308000 +gke-helm-arm64-cluster-default-pool-f4ab8a2d-5ldp Ready 5h54m v1.33.5-gke.1308000 +``` + +- Nodes in Ready state +- Kubernetes control plane accessible + +### Outcome +At this point: + +- Google Cloud SDK is installed and configured +- GKE cluster is running +- kubectl is connected to the cloud cluster +- Helm is ready to deploy applications on GKE + +The environment is now prepared to deploy Helm charts. + diff --git a/content/learning-paths/servers-and-cloud-computing/helm-on-gcp/images/nginx-browser.png b/content/learning-paths/servers-and-cloud-computing/helm-on-gcp/images/nginx-browser.png new file mode 100644 index 0000000000000000000000000000000000000000..6415e5363d9d0be5b32fa9f8a3e1ec03bfed1db8 GIT binary patch literal 46663 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

9htdnsT59q3{}{2I+g*oqlpXcHn;v@ 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 443/TCP 3h22m +nginx-my-nginx LoadBalancer 34.118.239.19 34.63.103.125 80:31501/TCP 52s +postgres-app-my-postgres ClusterIP 34.118.225.2 5432/TCP 13m +redis-my-redis ClusterIP 34.118.234.155 6379/TCP 6m53s +``` + +**Open in browser:** + +```bash +http:// +``` + +You should see the default NGINX welcome page as shown below: + +![NGINX default welcome page in a web browser on an GCP VM alt-text#center](images/nginx-browser.png) + +### Outcome +This deployment achieves the following: + +- NGINX deployed using a custom Helm chart +- Public access enabled via LoadBalancer +- External IP available for frontend access +- Ready to route traffic to backend services + diff --git a/content/learning-paths/servers-and-cloud-computing/helm-on-gcp/postgresql-helm.md b/content/learning-paths/servers-and-cloud-computing/helm-on-gcp/postgresql-helm.md new file mode 100644 index 0000000000..7e9003091e --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/helm-on-gcp/postgresql-helm.md @@ -0,0 +1,296 @@ +--- +title: PostgreSQL Deployment Using Custom Helm Chart +weight: 7 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + + +## PostgreSQL Deployment Using Custom Helm Chart +This document explains how to deploy **PostgreSQL** on Kubernetes using a **custom Helm chart** with persistent storage. + +### Goal +After completing this guide, the environment will include: +- PostgreSQL running inside Kubernetes +- Persistent storage using PVC +- Secure credentials using Kubernetes Secrets +- Ability to connect using psql +- A clean, reusable Helm chart + +### Prerequisites +Ensure Kubernetes and Helm are working: + +```console +kubectl get nodes +helm version +``` + +If these commands fail, fix them first before continuing. + +### CREATE WORKING DIRECTORY +Creates a dedicated folder to store all Helm charts for microservices. + +```console +mkdir helm-microservices +cd helm-microservices +``` + +### Create Helm Chart +Generates a Helm chart skeleton that will be customized for PostgreSQL. + +```console +helm create my-postgres +``` + +**Directory structure:** + +```text +helm-microservices/ +└── my-postgres/ + ├── Chart.yaml + ├── values.yaml + └── templates/ +``` + +### Clean the chart +The default Helm chart contains several files that are not required for a basic PostgreSQL deployment. Removing these files prevents confusion and template errors. +Inside `my-postgres/templates/`, delete the following: + +- hpa.yaml +- ingress.yaml +- serviceaccount.yaml +- tests/ +- NOTES.txt +- httproute.yaml + +Only PostgreSQL-specific templates will be maintained. + +### Configure values.yaml (Main Configuration File) +`values.yaml` centralizes all configurable settings, including: + +- Container image details +- Database credentials +- Persistent storage configuration + +Replace the entire contents of `my-postgres/values.yaml` with the following: + +```yaml +replicaCount: 1 + +image: + repository: postgres + tag: "15" + pullPolicy: IfNotPresent + +postgresql: + username: admin + password: admin123 + database: mydb + +persistence: + enabled: true + size: 10Gi + mountPath: /var/lib/postgresql + dataSubPath: data +``` + +This matters + +- Ensures consistent configuration +- Avoids Helm template evaluation errors +- Simplifies upgrades and maintenance + +### Create secret.yaml (Database Credentials) +Stores PostgreSQL credentials securely using Kubernetes Secrets. +Create the following file: + +`my-postgres/templates/secret.yaml` + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "my-postgres.fullname" . }} +type: Opaque +stringData: + POSTGRES_USER: {{ .Values.postgresql.username }} + POSTGRES_PASSWORD: {{ .Values.postgresql.password }} + POSTGRES_DB: {{ .Values.postgresql.database }} +``` + +That matters + +- Prevents hard-coding credentials +- Follows Kubernetes security best practices + +### Create pvc.yaml (Persistent Storage) +Requests persistent storage so PostgreSQL data remains available even if the pod restarts. +Create the following file: + +`my-postgres/templates/pvc.yaml` + +```yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "my-postgres.fullname" . }}-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.persistence.size }} +``` + +That matters +- Without a PVC, PostgreSQL data would be lost whenever the pod restarts. + +### deployment.yaml (PostgreSQL Pod Definition) +Defines how PostgreSQL runs inside Kubernetes, including: +- Container image +- Environment variables +- Volume mounts +- Pod configuration + +Replace the existing `my-postgres/templates/deployment.yaml` file completely. + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "my-postgres.fullname" . }} + +spec: + replicas: 1 + selector: + matchLabels: + app: {{ include "my-postgres.name" . }} + + template: + metadata: + labels: + app: {{ include "my-postgres.name" . }} + + spec: + containers: + - name: postgres + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + + ports: + - containerPort: 5432 + + envFrom: + - secretRef: + name: {{ include "my-postgres.fullname" . }} + + env: + - name: PGDATA + value: "{{ .Values.persistence.mountPath }}/{{ .Values.persistence.dataSubPath }}" + + volumeMounts: + - name: postgres-data + mountPath: {{ .Values.persistence.mountPath }} + + volumes: + - name: postgres-data + persistentVolumeClaim: + claimName: {{ include "my-postgres.fullname" . }}-pvc +``` + +- PGDATA avoids the common lost+found directory issue +- Persistent storage is mounted safely +- Secrets inject credentials at runtime + +### service.yaml (Internal Access) +Enables internal cluster communication so other services can connect to PostgreSQL. +Replace `my-postgres/templates/service.yaml` with: + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: {{ include "my-postgres.fullname" . }} +spec: + type: ClusterIP + ports: + - port: 5432 + targetPort: 5432 + selector: + app: {{ include "my-postgres.name" . }} +``` + +**ClusterIP** +- PostgreSQL should remain accessible only inside the Kubernetes cluster. + +### Install PostgreSQL Using Helm + +```console +cd helm-microservices +helm uninstall postgres || true +helm install postgres-app ./my-postgres +``` + +**Check:** + +```console +kubectl get pods +kubectl get pvc +``` + +You should see an output similar to: +```output +NAME READY STATUS RESTARTS AGE +postgres-app-my-postgres-6dbc8759b6-jgpxs 1/1 Running 0 40s + +>kubectl get pvc +NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE +postgres-app-my-postgres-pvc Bound pvc-5f3716df-39bb-4683-990a-c5cd3906fbce 10Gi RWO standard-rwo 33s +``` + +### Test PostgreSQL +Connect to PostgreSQL + +```console +kubectl exec -it -- psql -U admin -d mydb +``` + +You should see an output similar to: +```output +psql (15.15 (Debian 15.15-1.pgdg13+1)) +Type "help" for help. + +mydb=# +``` + +**Run test queries:** + +```psql +CREATE TABLE test (id INT); +INSERT INTO test VALUES (1); +SELECT * FROM test; +``` + +You should see an output similar to: +```output +mydb=# CREATE TABLE test (id INT); +INSERT INTO test VALUES (1); +SELECT * FROM test; +CREATE TABLE +INSERT 0 1 + id +---- + 1 +(1 row) +``` + +### Outcome +You have successfully: + +- Created a custom Helm chart +- Deployed PostgreSQL on Kubernetes +- Enabled persistent storage +- Used Secrets for credentials +- Verified database functionality + diff --git a/content/learning-paths/servers-and-cloud-computing/helm-on-gcp/redis-helm.md b/content/learning-paths/servers-and-cloud-computing/helm-on-gcp/redis-helm.md new file mode 100644 index 0000000000..c193587405 --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/helm-on-gcp/redis-helm.md @@ -0,0 +1,166 @@ +--- +title: Redis Deployment Using Custom Helm Chart +weight: 8 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + + +## Redis Deployment Using Custom Helm Chart +This document explains how to deploy Redis on Kubernetes using a custom Helm chart. + +## Goal +After completing this guide, the environment will include: + +- Redis running on Kubernetes +- Deployment managed using Helm +- Internal access using a ClusterIP Service +- Basic connectivity validation using redis-cli + +### Create Helm Chart +Generates a Helm chart skeleton that will be customized for Redis. + +```console +helm create my-redis +``` + +### Resulting structure + +```text +my-redis/ +├── Chart.yaml +├── values.yaml +└── templates/ +``` + +### Clean Templates +The default Helm chart includes several files that are not required for a basic Redis deployment. Removing them avoids unnecessary complexity and template errors. +Inside `my-redis/templates/`, delete the following: + +- ingress.yaml +- hpa.yaml +- serviceaccount.yaml +- tests/ +- NOTES.txt + +Only Redis-specific templates will be maintained. + +### Configure values.yaml +`values.yaml` stores all configurable parameters, including: + +- Redis image version +- Service type and port +- Replica count + +Replace the entire contents of `my-redis/values.yaml` with: + +```yaml +replicaCount: 1 + +image: + repository: redis + tag: "7" + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 6379 +``` + +That matters + +- Centralizes configuration +- Simplifies future updates +- Prevents Helm template evaluation issues + +### Deployment Definition (deployment.yaml) +Defines how the Redis container runs inside Kubernetes, including: + +- Container image +- Port configuration +- Pod labels and selectors + +Replace the existing `my-redis/templates/deployment.yaml` completely. + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "my-redis.fullname" . }} + +spec: + replicas: 1 + selector: + matchLabels: + app: {{ include "my-redis.name" . }} + + template: + metadata: + labels: + app: {{ include "my-redis.name" . }} + + spec: + containers: + - name: redis + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + ports: + - containerPort: 6379 +``` + +- Redis runs as a single pod +- No persistence is configured (suitable for learning and caching use cases) + +### Service Definition (service.yaml) +Creates an internal Kubernetes service to allow other pods to connect to Redis. +Replace `my-redis/templates/service.yaml` with: + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: {{ include "my-redis.fullname" . }} +spec: + type: ClusterIP + ports: + - port: 6379 + selector: + app: {{ include "my-redis.name" . }} +``` + +**ClusterIP** + +- Redis is intended for internal communication only within the cluster. + +### Install Redis Using Helm +Validates that Redis is running and responding correctly. + +```console +helm install redis ./my-redis +kubectl get svc +kubectl exec -it -- redis-cli ping +``` + +You should see an output similar to: +```output +NAME READY STATUS RESTARTS AGE +postgres-app-my-postgres-6dbc8759b6-jgpxs 1/1 Running 0 6m38s +redis-my-redis-75c88646fb-6lz8v 1/1 Running 0 13s + +>kubectl get svc +redis-my-redis ClusterIP 34.118.234.155 6379/TCP 6m14s + +> kubectl exec -it redis-my-redis-75c88646fb-6lz8v -- redis-cli ping +PONG +``` + +- Redis pod → Running +- Redis service → ClusterIP + +### Outcome +This deployment achieves the following: + +- Redis deployed using a custom Helm chart +- Internal access via Kubernetes Service +- Successful connectivity validation +- Clean and reusable Helm structure Accessible via service name redis