Files
pos-system/docs/en/guides/kubernetes-local.md
Ho Ngoc Hai 9ba4a478ee feat(docs): Enhance deployment and development guides with improved clarity and structure
- Updated Mermaid diagrams in the deployment and development guides for better visual representation and consistency.
- Improved formatting and clarity in the Kubernetes local deployment and IAM migration guides, including detailed workflows and troubleshooting sections.
- Enhanced the Vietnamese documentation to align with the English version, ensuring consistency across guides.
- Added quick tips and common issues sections to facilitate user navigation and understanding.
2026-01-08 17:10:06 +07:00

11 KiB

Local Kubernetes Deployment Guide

Last Updated: 2026-01-05 Difficulty: Intermediate Duration: 30-45 minutes

Workflow

graph TD
 Start([ Start]) --> EnvPrep[1⃣ Environment Prep<br/>Enable K8s in Docker Desktop]
 EnvPrep --> BuildImg[2⃣ Build Docker Image<br/>docker build -t service:local]
 BuildImg --> LoadImg[3⃣ Load Image to Cluster<br/>kind load docker-image]
 LoadImg --> Secrets[4⃣ Configure Secrets<br/>kubectl create secret]
 Secrets --> Deploy[5⃣ Deploy Service<br/>kubectl apply -f manifests]
 Deploy --> Verify[6⃣ Verify Deployment<br/>kubectl get pods]
 Verify --> Test[7⃣ Test Service<br/>Port Forward & Curl]
 Test --> End([ Complete])

 subgraph Deployment[" Deployment Resources"]
 Deploy --> |Apply| ConfigMap[ConfigMap<br/>Environment Config]
 Deploy --> |Apply| K8sDeployment[Deployment<br/>Pod Template]
 Deploy --> |Apply| Service[Service<br/>LoadBalancer/NodePort]
 end

 %% Start/End nodes - Success Green
 style Start fill:#004d40,stroke:#00bfa5,stroke-width:3px,color:#e0f2f1
 style End fill:#1b5e20,stroke:#66bb6a,stroke-width:3px,color:#e8f5e9

 %% Workflow steps - Dark theme with distinct colors
 style EnvPrep fill:#263238,stroke:#546e7a,stroke-width:2px,color:#eceff1
 style BuildImg fill:#3e2723,stroke:#8d6e63,stroke-width:2px,color:#efebe9
 style LoadImg fill:#1a237e,stroke:#5c6bc0,stroke-width:2px,color:#e8eaf6
 style Secrets fill:#b71c1c,stroke:#ef5350,stroke-width:2px,color:#ffebee
 style Deploy fill:#004d40,stroke:#26a69a,stroke-width:2px,color:#e0f2f1
 style Verify fill:#1565c0,stroke:#42a5f5,stroke-width:2px,color:#e3f2fd
 style Test fill:#4a148c,stroke:#ab47bc,stroke-width:2px,color:#f3e5f5

 %% Deployment resources subgraph
 style Deployment fill:#1a1a1a,stroke:#424242,stroke-width:2px,color:#e0e0e0
 style ConfigMap fill:#263238,stroke:#78909c,stroke-width:1px,color:#cfd8dc
 style K8sDeployment fill:#263238,stroke:#78909c,stroke-width:1px,color:#cfd8dc
 style Service fill:#263238,stroke:#78909c,stroke-width:1px,color:#cfd8dc

Overview

This guide details how to deploy the IAM Service (or any microservice in the GoodGo ecosystem) to a local Kubernetes cluster using Docker Desktop on macOS.

Important: This guide assumes you are using Docker Desktop with Kubernetes enabled. If you are using Minikube or plain Kind, the steps might differ slightly (especially the image loading part).

1. Prerequisites

Software

  • Docker Desktop 4.0+: Download Link
  • Kubernetes must be enabled in settings.
  • kubectl CLI: Command-line tool for interacting with K8s.
brew install kubectl
  • kind CLI: Required to load images into the cluster if using Kind backend explicitely.
brew install kind
  • pnpm 8+: Project package manager.
npm install -g pnpm

Knowledge

  • Basic understanding of Kubernetes concepts: Pod, Deployment, Service, Secret, ConfigMap.
  • Familiarity with basic Docker commands (docker build, docker images).
  • Ability to navigate and run commands in the Terminal.

2. Environment Preparation

2.1 Enable Kubernetes in Docker Desktop

  1. Open Docker Desktop.
  2. Click the Settings () icon.
  3. Select the Kubernetes tab.
  4. Check Enable Kubernetes.
  5. Select Show system containers (advanced) for easier debugging (optional).
  6. Click Apply & Restart.
  7. Wait 2-3 minutes until the Kubernetes icon in the bottom corner turns green.

2.2 Verify Kubernetes Connection

Check if kubectl is connected to the correct context:

# Check current context
kubectl config current-context
# Expected Output: docker-desktop

# List all nodes in the cluster
kubectl get nodes
# Expected Output:
# NAME STATUS ROLES AGE VERSION
# docker-desktop Ready control-plane 10m v1.29.1

3. Build Docker Image

We need to build the service image before deploying. Taking iam-service as an example.

# Navigate to the kubernetes deployment directory
cd deployments/local/kubernetes

# Build the Docker image from the root context
# Note: -f points to service Dockerfile, context is root (../../..)
docker build -t iam-service:local -f ../../../services/iam-service/Dockerfile ../../..

# Verify the image was built successfully
docker images | grep iam-service
# Expected Output:
# iam-service local [IMAGE_ID] [SIZE] [CREATED]

4. Load Image into Cluster

** IMPORTANT**: Docker Desktop can use different backends. If you are running Kubernetes inside Docker Desktop, sometimes it doesn't immediately see local images if using a kind node underneath.

If you are using Kind (Kubernetes in Docker) separately or a specific Docker Desktop config, you need to load the image:

# Load image into kind cluster (if using kind explicitly)
kind load docker-image iam-service:local --name desktop

# Validating image presence (optional, hard with Docker Desktop K8s directly)

Tip: With default Docker Desktop, building the local image (docker build ...) is usually automatically available to Docker Desktop's K8s cluster. This loading step is mainly for those using kind CLI to create separate clusters.

5. Configure Secrets & ConfigMap

Kubernetes environments need sensitive environment variables (Secrets) and general configuration (ConfigMap).

5.1 Create Secrets (Manually)

You can run a script or the following commands to create secrets securely.

# Create a dedicated namespace for local testing
kubectl create namespace iam-local

# Generate random secrets and store in Kubernetes
kubectl create secret generic iam-service-secrets \
 --from-literal=DATABASE_URL="postgresql://user:password@host.docker.internal:5432/iam_db?schema=public" \
 --from-literal=JWT_SECRET="$(openssl rand -base64 32)" \
 --from-literal=JWT_REFRESH_SECRET="$(openssl rand -base64 32)" \
 --from-literal=ENCRYPTION_KEY="$(openssl rand -base64 32)" \
 -n iam-local

# Verify secrets creation
kubectl get secrets -n iam-local

Note on host.docker.internal: On macOS, for a K8s pod to connect to PostgreSQL running on the host machine (or another container via port mapping), we use host.docker.internal.

5.2 ConfigMap

The iam-service-configmap.yaml file typically contains non-sensitive variables like NODE_ENV, LOG_LEVEL.

# Apply ConfigMap
kubectl apply -f iam-service-configmap.yaml -n iam-local

6. Deploy Service

Now we will deploy the main resources.

# Apply Deployment manifest
kubectl apply -f iam-service-deployment.yaml -n iam-local

# Apply Service manifest (LoadBalancer/NodePort)
kubectl apply -f iam-service-service.yaml -n iam-local

7. Verify & Debug

After deployment, ensure the Pod is stable (Running).

7.1 Check Pods

# Get all pods in the namespace
kubectl get pods -n iam-local

# Expected Output:
# NAME READY STATUS RESTARTS AGE
# iam-service-68994fdc79-gh2mj 1/1 Running 0 30s

7.2 View Detailed Logs

If Status is not Running (e.g., CrashLoopBackOff or ImagePullBackOff), check logs:

# Stream logs from the pod
kubectl logs -f -n iam-local -l app=iam-service

# Describe pod to see events (pull error, mounts, scheduling)
kubectl describe pod -n iam-local -l app=iam-service

7.3 Common Errors

  1. ImagePullBackOff:
  • Reason: K8s cannot find iam-service:local image.
  • Fix: Ensure imagePullPolicy: IfNotPresent or Never in local deployment yaml. If using Kind, remember to run kind load.
  1. CrashLoopBackOff:
  • Reason: Runtime error, usually unable to connect to Database.
  • Fix: Check DATABASE_URL in Secret. Ensure Postgres is running and accessible from K8s (use host.docker.internal).
  1. Pending Service:
  • Reason: LoadBalancer type on local sometimes hangs pending IP.
  • Fix: Not a problem, we can use port-forward or access via localhost.

8. Test Service Access

To access the service from your local machine, the safest way is port-forward.

# Port forward from local port 5002 to service port 80
kubectl port-forward svc/iam-service 5002:80 -n iam-local

# Terminal will hang and show: Forwarding from 127.0.0.1:5002 -> 8000

Open another terminal and test:

# Test Health Check
curl http://localhost:5002/health/live
# Response: {"status":"ok", ...}

# View Swagger/OpenAPI docs (if enabled)
open http://localhost:5002/api-docs

9. Cleanup

When done, delete resources to free up capacity.

# Delete the namespace (removes all resources within)
kubectl delete namespace iam-local

Quick Tips

Common Issues & Solutions

Issue Symptoms Solution
ImagePullBackOff Pod stuck in ImagePullBackOff Set imagePullPolicy: Never in deployment YAML
Run kind load docker-image if using Kind
CrashLoopBackOff Pod keeps restarting Check logs with kubectl logs
Verify DATABASE_URL in secrets
Ensure DB is accessible via host.docker.internal
Pending LoadBalancer Service External-IP shows <pending> Normal on local—use kubectl port-forward instead
Connection Refused Can't connect after port-forward Check pod is Running (not CrashLoopBackOff)
Verify correct port mapping

Essential Commands Reference

# Debugging
kubectl get pods -n iam-local -w # Watch pods in real-time
kubectl describe pod <POD_NAME> -n iam-local # Detailed pod info + events
kubectl logs -f <POD_NAME> -n iam-local # Stream logs
kubectl exec -it <POD_NAME> -n iam-local -- /bin/sh # Shell into container

# Image Management
docker build -t service:local -f Dockerfile . # Build image
kind load docker-image service:local --name desktop # Load to Kind cluster
docker images | grep service # List local images

# Quick Deployment
kubectl apply -f deployment.yaml -n iam-local # Deploy single manifest
kubectl apply -f . -n iam-local # Deploy all files in directory
kubectl rollout restart deployment/iam-service -n iam-local # Force pod restart

# Cleanup
kubectl delete pod <POD_NAME> -n iam-local --force # Force delete stuck pod
kubectl delete namespace iam-local --force --grace-period=0 # Force delete namespace

Visual Status Indicators

  • Running - Service is healthy
  • Pending - Waiting for resources
  • CrashLoopBackOff - Service keeps failing (check logs!)
  • ContainerCreating - Pod starting up
  • Terminating - Pod shutting down

Pro Tips

  1. ** Fast Rebuild**: For quick iteration, use kubectl rollout restart deployment/iam-service -n iam-local after rebuilding image
  2. ** Watch Mode**: Use -w flag to watch resources update in real-time
  3. ** YAML Validation**: Run kubectl apply --dry-run=client -f file.yaml to validate before applying
  4. ** Label Filtering**: Use -l app=iam-service to filter resources by label instead of typing pod names

References