Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

35 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿš€ NexOps Platform

A production-grade, self-managed Kubernetes DevSecOps platform built from scratch on Google Cloud Platform.

An enterprise-grade platform engineering workspace designed to showcase zero-trust networking, GitOps continuous delivery, automated vulnerability gates, and multi-environment isolation on a self-managed, VM-based Kubernetes cluster.


Table of Contents


๐Ÿ“‹ Overview

The NexOps Platform is an end-to-end, self-managed cloud and Kubernetes platform built entirely on Google Cloud Platform (GCP). Bypassing managed Kubernetes services like Google Kubernetes Engine (GKE) to demonstrate deep infrastructure and systems-level competence, this project provisions virtual machines on GCP Compute Engine using Terraform, configures host operating systems and bootstraps a lightweight K3s cluster using Ansible, and automates applications and systems software deployment using ArgoCD following GitOps best practices.

Every component in the architecture โ€” from firewalls and subnets to continuous integration, security scanners, monitoring dashboards, and standalone database instances โ€” is codified, versioned, and managed as code. The workload is structured as a modern full-stack application (Go REST API + React SPA Dashboard) deployed into separate isolated namespaces with strict network security policies, utilizing external services like a standalone PostgreSQL database isolated on a private GCP subnet.


โšก Core Capabilities

  • Automated Infrastructure-as-Code (IaC): Dynamic provisioning of GCP VPC networks, subnets, routers, NAT gateways, firewall rules, and compute VMs with remote state stored in Google Cloud Storage (GCS).
  • Automated Configuration Management: Ansible-driven OS tuning, kernel module configuration, swap disabling, and headless PostgreSQL deployment.
  • GitOps Continuous Delivery (CD): Declarative, self-healing application lifecycle management using ArgoCD under an App-of-Apps management pattern.
  • Dynamic CI/CD Security Gates: Interactive Jenkins pipelines running on dynamic Kubernetes agent pods, triggering automated static analysis and vulnerability scans.
  • Zero-Trust Network Hardening: Namespace isolation using Kubernetes NetworkPolicies that block unauthorized cross-namespace lateral traffic.
  • Automated Ingress & TLS Management: Ingress-nginx controller with cert-manager automatically provisioning Let's Encrypt certificates via HTTP-01 challenges.
  • Full-Stack Observability: Cluster and host telemetry gathered via Prometheus, alert routing via Alertmanager, and visualization through Grafana dashboards.

โœจ Key Features

  • Decoupled Database Infrastructure: A dedicated database VM hosted completely outside the Kubernetes cluster on a private IP address, accessible only through a Kubernetes headless Service and custom Endpoints mapping.
  • Conflict-Free Multi-Environment Layout: Ingress host naming configuration managed via Kustomize overlays (staging.nexops.local for staging and app.nexops.local for production), preventing Nginx Ingress webhook hostname conflicts.
  • Privileged DinD Vulnerability Scanning: Shared socket Docker-in-Docker configuration allowing pipelines to run Trivy image scans securely without exposing host credentials.
  • Dynamic Kubernetes Jenkins Agents: Lightweight execution environment where CI build agents are created dynamically as Kubernetes pods and torn down immediately upon job completion.
  • Non-Root Execution Standards: Backend Go containers run under non-root security contexts (UID 1000) to minimize runtime privilege escalation risks.
  • Secure Image Registry Integration: Image pull secrets (gar-pull-secret) configured for automatic authentication with Google Artifact Registry (GAR).

๐Ÿ› ๏ธ Technology Stack

Layer Technology Purpose
Cloud Hosting Google Cloud Platform (GCP) Hosting VPC, compute instances, router, NAT gateway, and DNS zones
Infrastructure as Code Terraform Declarative definition of all GCP network, subnet, firewall, and compute instances
Config Management Ansible Baseline OS hardening, K3s control plane/agent configuration, and PostgreSQL setups
Orchestration K3s (Lightweight Kubernetes) Multi-node self-managed cluster runner (VXLAN backend Flannel, Traefik disabled)
GitOps Engine ArgoCD Declarative, self-healing continuous delivery (CD) following the App-of-Apps pattern
Automation Server Jenkins Dynamic orchestrator running multi-container K8s agent pod runners for CI pipelines
Static Security (SAST) SonarQube Source code static analysis checking for code smells, bugs, and security gates
Vulnerability Scanner Trivy Dual-layer filesystem dependency scanning and container image CVE gate checking
Ingress Controller Nginx Ingress HTTP/S proxy traffic routing for ingress hostnames
TLS/SSL Certificates Cert-Manager + Let's Encrypt Automatic certificate generation and renewal via HTTP-01 challenge challenges
Observability (Metrics) Prometheus Cluster-wide metric collection and Alertmanager paging configuration
Observability (UI) Grafana System telemetry data visualization (Node Exporter, CoreDNS, API dashboards)
Observability (Logs) Loki + Promtail Centralized system log shipping and aggregation
Relational Database PostgreSQL 14 Standalone VM database hosting SonarQube storage and React Go data
Backend API Go (Golang 1.24) RESTful API service running in a secure, non-root Alpine container
Frontend UI React 19 + Vite 8 Glassmorphic dashboard interface compiled via Vite and served via Nginx
Container Registry Google Artifact Registry (GAR) Registry storing backend and frontend Docker builds

๐Ÿ—๏ธ Architecture

The NexOps Platform utilizes a dedicated virtual private cloud (VPC) network in the GCP Jakarta region (asia-southeast2). The control plane and workers are assigned public IP addresses for SSH management and Ingress routing, whereas the database server is completely isolated without a public interface.

Cloud Network and Compute Topology

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                               GOOGLE CLOUD PLATFORM (GCP)                              โ”‚
โ”‚                             Region: asia-southeast2 (Jakarta)                          โ”‚
โ”‚                                                                                        โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚                              VPC: nexops-vpc (10.0.1.0/24)                       โ”‚  โ”‚
โ”‚  โ”‚                                                                                  โ”‚  โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  K8s API (6443)  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚  โ”‚
โ”‚  โ”‚  โ”‚     nexops-master      โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚         nexops-worker          โ”‚  โ”‚  โ”‚
โ”‚  โ”‚  โ”‚      (e2-medium)       โ”‚                  โ”‚        (e2-standard-4)         โ”‚  โ”‚  โ”‚
โ”‚  โ”‚  โ”‚     30 GB SSD Boot     โ”‚โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚         60 GB SSD Boot         โ”‚  โ”‚  โ”‚
โ”‚  โ”‚  โ”‚  External Static IP   โ”‚   K3s Node Join  โ”‚       External Static IP       โ”‚  โ”‚  โ”‚
โ”‚  โ”‚  โ”‚   (Internal: 10.0.1.3) โ”‚                  โ”‚       (Internal: 10.0.1.4)     โ”‚  โ”‚  โ”‚
โ”‚  โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚                  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚  โ”‚  โ”‚
โ”‚  โ”‚  โ”‚  โ”‚   K3s Server     โ”‚  โ”‚                  โ”‚  โ”‚ K3s Agent Node           โ”‚  โ”‚  โ”‚  โ”‚
โ”‚  โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚                  โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค  โ”‚  โ”‚  โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                  โ”‚  โ”‚ Namespaces:              โ”‚  โ”‚  โ”‚  โ”‚
โ”‚  โ”‚               โ”‚                              โ”‚  โ”‚  - argocd                โ”‚  โ”‚  โ”‚  โ”‚
โ”‚  โ”‚               โ”‚ ProxyJump SSH                โ”‚  โ”‚  - devops (Jenkins)      โ”‚  โ”‚  โ”‚  โ”‚
โ”‚  โ”‚               โ–ผ                              โ”‚  โ”‚  - monitoring (Prom/Graf)โ”‚  โ”‚  โ”‚  โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                  โ”‚  โ”‚  - ingress-nginx         โ”‚  โ”‚  โ”‚  โ”‚
โ”‚  โ”‚  โ”‚       nexops-db        โ”‚                  โ”‚  โ”‚  - cert-manager          โ”‚  โ”‚  โ”‚  โ”‚
โ”‚  โ”‚  โ”‚       (e2-small)       โ”‚โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚  โ”‚  - staging (React/Go)    โ”‚  โ”‚  โ”‚  โ”‚
โ”‚  โ”‚  โ”‚     20 GB SSD Boot     โ”‚   Port 5432      โ”‚  โ”‚  - prod (React/Go)       โ”‚  โ”‚  โ”‚  โ”‚
โ”‚  โ”‚  โ”‚     NO External IP     โ”‚   (External DB   โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚  โ”‚  โ”‚
โ”‚  โ”‚  โ”‚   PostgreSQL Server    โ”‚    Endpoint)     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚  โ”‚
โ”‚  โ”‚  โ”‚   (Internal: 10.0.1.2) โ”‚                                                      โ”‚  โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                                      โ”‚  โ”‚
โ”‚  โ”‚                                                                                  โ”‚  โ”‚
โ”‚  โ”‚                             Cloud NAT (Outbound Only)                            โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โš™๏ธ Infrastructure

The underlying infrastructure is defined using modularized Terraform code located in terraform/.

  • VPC Configuration: Custom VPC nexops-vpc and subnet nexops-subnet (10.0.1.0/24). Outbound internet traffic for private nodes is routed through a Cloud NAT gateway backed by a Cloud Router.
  • Firewall Rules:
    • allow-ssh: Permits SSH (Port 22) traffic from public sources to instances tagged with nexops-node.
    • allow-k3s-api: Permits external connections to K3s Control Plane (Port 6443).
    • allow-http-https: Permits ports 80 and 443 inbound to nodes hosting the ingress controllers (nexops-worker).
    • allow-k8s-nodeports: Opens ports 30000-32767 for external service validation testing.
    • allow-vpc-internal: Permits unrestricted communication between all internal IP addresses on the 10.0.1.0/24 subnet.
  • Instance Allocation: GCP VM Instances List
    • nexops-master: Instantiated on an e2-medium machine type with a 30 GB SSD boot disk.
    • nexops-worker: Instantiated on an e2-standard-4 machine type with a 60 GB SSD boot disk.
    • nexops-db: Instantiated on an e2-small machine type with a 20 GB SSD boot disk. No external IP is assigned to this instance.
  • Remote State: Backed by a GCP Cloud Storage bucket named nexops-tfstate-rinemaa-457218.

๐Ÿ› ๏ธ Provisioning

Terraform initializes the cloud fabric and generates deployment configurations.

  1. State Bootstrapping: The gcs backend stores state files under the key terraform/state.
  2. Metadata Injection: Installs SSH keys dynamically for user nexops onto all provisioned hosts.
  3. Execution Snippets: An output snippet ansible_inventory_snippet prints the dynamic IP mappings directly to ansible/inventory/hosts.ini layout format:
    [master]
    nexops-master ansible_host=<master-public-ip> ansible_user=nexops ansible_ssh_private_key_file=~/.ssh/nexops_rsa
    
    [worker]
    nexops-worker ansible_host=<worker-public-ip> ansible_user=nexops ansible_ssh_private_key_file=~/.ssh/nexops_rsa
    
    [db]
    nexops-db ansible_host=10.0.1.2 ansible_user=nexops ansible_ssh_private_key_file=~/.ssh/nexops_rsa ansible_ssh_common_args='-o ProxyJump=nexops@<master-public-ip>'

๐Ÿง OS Management

System configurations and packages are managed by Ansible roles in ansible/roles/.

  • Role: common: Executed across all instances (master, worker, db).
    • Updates APT packages cache and performs a system upgrade.
    • Installs core utilities: curl, wget, git, unzip, jq, htop, vim, nfs-common, ca-certificates, socat, conntrack, ipset.
    • Disables swap space immediately (swapoff -a) and strips swap device mappings from /etc/fstab to comply with Kubernetes runtime standards.
    • Loads required host kernel modules: overlay and br_netfilter for bridge interface networking.
    • Injects sysctl parameters via /etc/sysctl.d/k8s.conf:
      • net.bridge.bridge-nf-call-iptables = 1
      • net.bridge.bridge-nf-call-ip6tables = 1
      • net.ipv4.ip_forward = 1
      • fs.inotify.max_user_watches = 524288
      • fs.inotify.max_user_instances = 512
      • vm.max_map_count = 262144 (required for SonarQube's Elasticsearch module).
    • Configures names inside /etc/hosts for node-to-node host resolution.

โ˜ธ๏ธ Cluster Management

K3s bootstrap steps are managed by the roles k3s-master and k3s-worker in ansible/playbooks/.

  • K3s Server Setup:
    • Installs the control plane on nexops-master with arguments:
      curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server \
        --disable=traefik \
        --flannel-backend=vxlan \
        --node-name=nexops-master \
        --write-kubeconfig-mode=644 \
        --tls-san=<master-public-ip>" sh -
    • Traefik is disabled in favor of an ingress-nginx DaemonSet deployment.
    • Extracts the joining token from /var/lib/rancher/k3s/server/node-token.
    • Downloads the /etc/rancher/k3s/k3s.yaml file, automatically rewriting the localhost IP to the master's public IP address, saving it to ansible/kubeconfig.yaml.
  • K3s Agent Join:
    • Bootstraps nexops-worker, joining it to the master control plane:
      curl -sfL https://get.k3s.io | K3S_URL=https://<master-ip>:6443 \
        K3S_TOKEN=<cluster-token> \
        INSTALL_K3S_EXEC="agent --node-name=nexops-worker" sh -
  • CLI Setup: Installs Helm v3 client on the master node and creates a symbolic link for kubectl.

๐Ÿ“‚ Project Structure

nexops-platform/
โ”œโ”€โ”€ .github/                  # GitHub workflow configurations
โ”œโ”€โ”€ ansible/                  # Ansible playbooks and host setups
โ”‚   โ”œโ”€โ”€ inventory/            # Host inventory definition files
โ”‚   โ”œโ”€โ”€ playbooks/            # Orchestration execution playbooks
โ”‚   โ”‚   โ”œโ”€โ”€ 01-common.yml     # OS baseline setup
โ”‚   โ”‚   โ”œโ”€โ”€ 02-k3s-master.yml # Control plane installation
โ”‚   โ”‚   โ”œโ”€โ”€ 03-k3s-worker.yml # Worker join configuration
โ”‚   โ”‚   โ””โ”€โ”€ 04-postgresql.yml # Standalone database configuration
โ”‚   โ”œโ”€โ”€ roles/                # Modular configuration task definitions
โ”‚   โ”‚   โ”œโ”€โ”€ common/           # Hardening and kernel parameter changes
โ”‚   โ”‚   โ”œโ”€โ”€ k3s-master/       # Master bootstrapping and token retrieval
โ”‚   โ”‚   โ”œโ”€โ”€ k3s-worker/       # Worker registration task definitions
โ”‚   โ”‚   โ””โ”€โ”€ postgresql/       # DB instance configuration and users
โ”‚   โ”œโ”€โ”€ ansible.cfg           # Local Ansible behaviors configuration
โ”‚   โ”œโ”€โ”€ kubeconfig.yaml       # Gitignored cluster client configuration
โ”‚   โ””โ”€โ”€ site.yml              # Root execution entrypoint
โ”œโ”€โ”€ applications/             # Application codebase and K8s manifests
โ”‚   โ”œโ”€โ”€ backend/              # Go RESTful API source files
โ”‚   โ”‚   โ”œโ”€โ”€ Dockerfile        # Multi-stage Docker file using Golang 1.24
โ”‚   โ”‚   โ”œโ”€โ”€ go.mod            # Go module dependencies
โ”‚   โ”‚   โ””โ”€โ”€ main.go           # REST routing and database client setup
โ”‚   โ”œโ”€โ”€ database/             # Schema definitions
โ”‚   โ”‚   โ””โ”€โ”€ init.sql          # DB tables setup script
โ”‚   โ”œโ”€โ”€ frontend/             # React SPA Dashboard codebase
โ”‚   โ”‚   โ”œโ”€โ”€ src/              # React source files (App.jsx, main.jsx)
โ”‚   โ”‚   โ”œโ”€โ”€ Dockerfile        # Nginx-based multi-stage runtime Dockerfile
โ”‚   โ”‚   โ”œโ”€โ”€ index.html        # SPA root element document
โ”‚   โ”‚   โ”œโ”€โ”€ package.json      # Node dependency versions
โ”‚   โ”‚   โ””โ”€โ”€ vite.config.js    # Vite builder config
โ”‚   โ”œโ”€โ”€ k8s/                  # Kubernetes resource configurations
โ”‚   โ”‚   โ”œโ”€โ”€ base/             # Kustomize base deployment files
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ backend.yaml  # Go backend Deployments and Services
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ db-service.yaml# Headless database Service + Endpoints
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ frontend.yaml # React frontend Deployments and Services
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ ingress.yaml  # Default ingress rule definitions
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ kustomization.yaml # Kustomize base setup mapping
โ”‚   โ”‚   โ””โ”€โ”€ overlays/         # Environment specific patches
โ”‚   โ”‚       โ”œโ”€โ”€ prod/         # Production deployment mapping
โ”‚   โ”‚       โ”‚   โ””โ”€โ”€ kustomization.yaml
โ”‚   โ”‚       โ””โ”€โ”€ staging/      # Staging deployment mapping
โ”‚   โ”‚           โ””โ”€โ”€ kustomization.yaml # Overwrites hostnames and secrets
โ”‚   โ””โ”€โ”€ Jenkinsfile           # 7-stage DevSecOps Jenkins pipeline
โ”œโ”€โ”€ argocd/                   # GitOps orchestration resource files
โ”‚   โ”œโ”€โ”€ apps/                 # Application bootstrap manifests
โ”‚   โ”‚   โ”œโ”€โ”€ cert-manager.yaml # Cert-manager App manifest (Helm v1.14.5)
โ”‚   โ”‚   โ”œโ”€โ”€ ingress-nginx.yaml# Nginx Ingress App manifest (Helm 4.10.1)
โ”‚   โ”‚   โ”œโ”€โ”€ jenkins.yaml      # Jenkins App manifest (Helm 5.1.30)
โ”‚   โ”‚   โ”œโ”€โ”€ monitoring.yaml   # Prometheus-Grafana App (Helm 58.6.0)
โ”‚   โ”‚   โ”œโ”€โ”€ prod-app.yaml     # Prod Env App pointing to overlays/prod
โ”‚   โ”‚   โ”œโ”€โ”€ sonarqube.yaml    # Sonarqube App manifest (Helm 10.5.1)
โ”‚   โ”‚   โ””โ”€โ”€ staging-app.yaml  # Staging Env App pointing to overlays/staging
โ”‚   โ””โ”€โ”€ app-of-apps.yaml      # Root Application monitoring argocd/apps/
โ”œโ”€โ”€ kubernetes/               # Cluster bootstrap resource declarations
โ”‚   โ””โ”€โ”€ bootstrap/            # Direct-applied namespaces and issuers
โ”‚       โ”œโ”€โ”€ argocd-ingress.yaml # Exposed HTTPS ingress configurations
โ”‚       โ”œโ”€โ”€ cluster-issuers.yaml# Cert-manager Let's Encrypt configurations
โ”‚       โ””โ”€โ”€ namespaces.yaml   # Namespaces definitions
โ”œโ”€โ”€ security/                 # System security hardening resources
โ”‚   โ””โ”€โ”€ network-policies/     # Strict default-deny NetworkPolicies
โ””โ”€โ”€ README.md                 # Project documentation

๐Ÿ’ป Application Architecture

The sample application deployed on the cluster is a split-tier React dashboard communicating with a Go database API.

                  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                  โ”‚    React Frontend    โ”‚ (React 19 + Vite 8 + Tailwind CSS v4)
                  โ”‚   Served via Nginx   โ”‚ (Exposed on Port 80 inside container)
                  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                             โ”‚
                             โ”‚ REST API Requests (via Ingress)
                             โ–ผ
                  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                  โ”‚      Go Backend      โ”‚ (Golang 1.24 + lib/pq runtime)
                  โ”‚   Runs as Non-Root   โ”‚ (Exposed on Port 8080 inside container)
                  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                             โ”‚
                             โ”‚ JDBC/SQL Connection (Port 5432)
                             โ–ผ
                  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                  โ”‚ PostgreSQL Database  โ”‚ (Hosted on standalone VM: 10.0.1.2)
                  โ”‚  Standalone Instance โ”‚ (Managed outside the cluster)
                  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  • Frontend SPA: React 19 / Tailwind CSS v4 dashboard compiled using Vite. It queries the Go API and renders interactive telemetry cards. Hosted in an nginx:alpine container with static assets copied directly to /usr/share/nginx/html.
  • Backend API: Written in Go (Golang 1.24). Listens on port 8080 and handles REST API routes. Built in a multi-stage golang:1.24-alpine image to output a lightweight binary running in alpine:3.19. Executed under security context properties enforcing a non-privileged system user account (runAsUser: 1000).
  • Database Init: An initialization script applications/database/init.sql configures tables to trace deployment records and monitor check actions.

๐Ÿ”„ How It Works

                                  +-----------------------+
                                  |    Developer Push     |
                                  +-----------+-----------+
                                              |
                                              v
                                  +-----------------------+
                                  |   GitHub Repository   |
                                  +-----------+-----------+
                                              | Webhook
                                              v
                                  +-----------------------+
                                  |   Jenkins Controller  |
                                  +-----------+-----------+
                                              | Spawns Pod
                                              v
    +-----------------------------------------+-----------------------------------------+
    |                                   Dynamic Agent Pod                               |
    |                                                                                   |
    |  +------------------+    +------------------+    +------------------+             |
    |  |  Trivy FS Scan   |--->|  SonarQube SAST  |--->|   Docker Build   |             |
    |  |  (Vulnerability) |    |  (Quality Gate)  |    |  (Build Images)  |             |
    |  +------------------+    +------------------+    +--------+---------+             |
    |                                                           |                       |
    |  +------------------+    +------------------+             v                       |
    |  | Git push tag update |<---|  Push Images GAR  |<---| Trivy Image Scan|          |
    |  | (Writeback Stage)  |    | (Docker Registry)|    |  (Container CVE) |          |
    |  +--------+---------+    +------------------+    +------------------+             |
    +-----------|-----------------------------------------------------------------------+
                |
                v
    +-----------+-----------+
    |   GitHub Repo Updated |
    +-----------+-----------+
                |
                v
    +-----------+-----------+
    |         ArgoCD        |
    +-----------+-----------+
          |           |
          |           +---------------------------------+
          | Auto-Sync                                   | Manual Promotion
          v                                             v
  +-------+-------+                             +-------+-------+
  |    Staging    |                             |   Production  |
  |   Namespace   |                             |   Namespace   |
  +---------------+                             +---------------+

The delivery cycle operates through a strict git-driven pipeline.

  1. Commit Phase: A developer pushes code changes to the GitHub repository.
  2. Pipeline Trigger: GitHub sends a webhook event to the Jenkins controller running in the devops namespace.
  3. Agent Provisioning: Jenkins communicates with the Kubernetes API to launch a dynamic worker pod using a multi-container pod template (jnlp, docker, trivy, sonar-scanner, tools).
  4. Vulnerability Gate: The pipeline runs code quality assessments and checks dependencies for vulnerabilities. If SonarQube quality gates or Trivy container scans identify critical issues, the pipeline aborts immediately.
  5. Publish Phase: Clean Docker images are tagged using the build number and Git commit SHA format (${BUILD_NUMBER}-${GIT_COMMIT.take(7)}) and pushed to the Google Artifact Registry.
  6. GitOps Writeback: Jenkins updates the image tags in the Kustomize base deployment manifests (applications/k8s/base/backend.yaml and frontend.yaml) and commits the changes back to GitHub.
  7. ArgoCD Sync: ArgoCD detects the modification in the Git repository. It syncs changes automatically to the staging namespace, while changes to the prod namespace are queued for manual promotion.

๐Ÿ”€ Data Flow

When a client loads the application dashboard, traffic is routed through the system tiers:

[Client Web Browser] 
       โ”‚ 
       โ–ผ Ingress Traffic (HTTPS Port 443)
[Nginx Ingress Controller (DaemonSet)]
       โ”‚
       โ”œโ”€โ–บ Path: "/" โ”€โ”€โ”€โ”€โ–บ [Service: nexops-frontend:80] โ”€โ”€โ”€โ”€โ–บ [Pods: nexops-frontend] (Nginx static files)
       โ”‚
       โ””โ”€โ–บ Path: "/api" โ”€โ”€โ–บ [Service: nexops-backend:80] โ”€โ”€โ”€โ”€โ–บ [Pods: nexops-backend] (Go API server)
                                                                       โ”‚
                                                                       โ–ผ SQL Connection (Port 5432)
                                                             [Service: nexops-db:5432] (Headless)
                                                                       โ”‚
                                                                       โ–ผ Endpoint Redirection
                                                             [PostgreSQL VM Node (10.0.1.2)]
  1. Host Header Resolution: The client web browser initiates requests targeting https://app.nexops.local (or staging.nexops.local).
  2. Ingress Entrypoint: Ingress controller processes the host header and maps the request paths:
    • / path matches the frontend React service.
    • /api/* path matches the Go backend service.
  3. UI Loading: The web browser downloads the React Single Page Application and runs the dashboard scripts locally.
  4. API Execution: The client browser makes asynchronous queries to /api/info or /api/ready. The ingress controller forwards this traffic to the Go backend pods.
  5. Database Routing: The Go backend initiates SQL requests pointing to nexops-db.default.svc.cluster.local:5432.
  6. External Redirection: The Kubernetes DNS resolves nexops-db to a headless service mapped to endpoints point to the private IP address 10.0.1.2. The internal router directs the TCP traffic across nodes directly to the PostgreSQL instance running on the database VM.

๐ŸŽจ UX Overview

Application Dashboard

The frontend dashboard interface is built to provide an interactive control panel for platform administrators:

  • Glassmorphic Theme: Dark mode design (bg-[#0d0e12]) featuring subtle indigo/purple blurred background blobs, translucent panels, and thin borders (border-slate-800/80).
  • Telemetry Indicators: Includes an API Status Indicator badge in the navigation bar. If the API is unreachable, a pulsing red status dot warns the user; otherwise, a pulsing green indicator shows active operations.
  • Interactive Stack Explorer: Telemetry cards display the systems running on the cluster:
    • Infrastructure: GCP Compute Engine instances configured via Terraform.
    • Configuration: Host configuration via Ansible.
    • Orchestration: Self-managed K3s cluster nodes.
    • CI Pipeline: Jenkins build engine running SonarQube & Trivy.
    • CD GitOps: Automated synchronization with ArgoCD.
    • Observability: Telemetry visualization with Prometheus & Grafana.
    • Application: Go APIs and React SPA runtimes.
  • Manual Control: A refresh button allows users to manually re-evaluate the state of the Go API and database links on demand.

๐Ÿ—๏ธ Continuous Integration (CI)

Jenkins Pipeline View Jenkins Blue Ocean View

Continuous Integration is managed via a declarative Jenkins pipeline script applications/Jenkinsfile executed inside a Kubernetes cloud provider.

  • Dynamic Agent Pod: Configured dynamically using a Pod Template containing:
    • jnlp: The default Jenkins inbound agent handler (jenkins/inbound-agent:latest).
    • docker: A privileged Docker-in-Docker sidecar (docker:dind) with MTU configuration set to 1400.
    • trivy: Aqua Security image scanner (aquasec/trivy:latest) with command overrides.
    • sonar-scanner: SonarQube analysis CLI tool container (sonarsource/sonar-scanner-cli:latest).
    • tools: Lightweight Git operations utility wrapper (alpine/git:latest).
  • Storage Volume: An emptyDir mount named docker-storage is attached to /var/lib/docker in the docker container, ensuring clean container workspaces for every run.
  • Job Options: Implements a 45-minute timeout window and blocks concurrent builds to prevent race conditions during writeback steps.

๐Ÿš€ Continuous Delivery (CD) & GitOps

ArgoCD Application Dashboard

Continuous delivery follows the GitOps pattern managed by ArgoCD.

  • Parent Root Application: The nexops-root application in argocd/app-of-apps.yaml tracks the directory argocd/apps/ inside the Git repository.
  • Self-Healing Mechanics: Automated pruning (prune: true) and self-healing (selfHeal: true) are enabled for all applications. If cluster resources are modified manually, ArgoCD automatically reconciles the state back to the Git source configuration.
  • Environment Promotion Split:
    • Staging (nexops-app-staging): Tracks the path applications/k8s/overlays/staging with automatic sync enabled.
    • Production (nexops-app-prod): Tracks applications/k8s/overlays/prod with automated prune disabled. This requires a manual click or API trigger inside the ArgoCD console to promote changes to production.

๐Ÿ›ก๏ธ DevSecOps Pipeline

SonarQube Code Quality

The DevSecOps pipeline builds safety checks directly into the continuous integration steps.

  1. Trivy File System Scanning: Scans source directories (./applications/backend and ./applications/frontend) for secrets, plain text credentials, or packages containing CVEs. Generates reports archived as build artifacts.
  2. SonarQube Quality Gate: Executes code quality scans targeting the Go API code using the sonar-scanner container. The build process blocks if code coverage drops or SAST alerts are triggered.
  3. Privileged Image Scanning: Since building images inside dynamic Kubernetes pods can lead to nested Docker socket access problems, the pipeline runs Trivy container checks inside the DinD container:
    docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
      aquasec/trivy:latest image --exit-code 1 --severity CRITICAL \
      ${IMAGE_PREFIX}/backend:${IMAGE_TAG}
    If a container image contains a CRITICAL vulnerability, the scanner returns exit code 1, and the pipeline halts before pushing images to the registry.
  4. Registry Authentication: Uses a GCP service account JSON key stored as a Jenkins credential (gcp-gar-service-account-key) to authenticate with the regional Docker repository asia-southeast2-docker.pkg.dev.

๐Ÿ“Š Monitoring & Logging

Grafana Dashboards Gallery

Dashboard Visual Preview
All Dashboards Overview All Dashboards
Kubernetes Cluster Compute Resources Compute Cluster Resources
Kubernetes Compute Resources (Namespace/Pods) Compute Pods Resources
Node Exporter Host Nodes Node Exporter Nodes
CoreDNS Metrics CoreDNS
Kubernetes API Server Telemetry API Server Telemetry

Telemetry operations are managed by Prometheus, Grafana, and Alertmanager deployed via Helm.

  • Prometheus Stack: Bootstrapped via ArgoCD using the Helm chart kube-prometheus-stack (v58.6.0).
    • Prometheus Server: Standardized with a 20 GB Persistent Volume Claim and a 15-day metrics retention window.
    • Grafana Server: Deployed with a 5 GB volume claim. Admin access credentials: nexops-grafana-2024.
    • Dashboards: Features a dynamic sidecar configuration provider that scans namespaces for ConfigMaps tagged with the label grafana_dashboard=1, importing dashboards automatically.
  • Exporters: Installs node-exporter as a DaemonSet to track CPU, memory, disk, and network stats on the VM instances, alongside kube-state-metrics for cluster workload data.
  • Logging: Designed for Loki integration using Promtail agents for container log aggregation.

๐Ÿ—„๏ธ Database Configurations

A dedicated PostgreSQL 14 instance is deployed on the nexops-db VM by the postgresql Ansible role.

  • Service Hardening: The engine is configured to bind to all network interfaces (listen_addresses = '*'), but access is restricted at the host network layer.
  • Host Authentication Restrictions: The file /etc/postgresql/14/main/pg_hba.conf allows database access only to connections originating from the Kubernetes subnet:
    host    all             all             10.0.1.0/24             md5
    
  • System Accounts: Provisions two isolated database user accounts and databases:
    • User sonarqube (Password: sonarqube_secret_pw) owning database sonarqube.
    • User nexops_app (Password: nexops_app_secret_pw) owning database nexops_app.
  • Headless Kubernetes Integration: To connect backend pods to the external database, the cluster maps the host IP using a headless Service and Endpoints manifest:
    apiVersion: v1
    kind: Service
    metadata:
      name: nexops-db
      namespace: default
    spec:
      ports:
        - port: 5432
    ---
    apiVersion: v1
    kind: Endpoints
    metadata:
      name: nexops-db
      namespace: default
    subsets:
      - addresses:
          - ip: 10.0.1.2
        ports:
          - port: 5432

๐Ÿ”Œ API Documentation

The Go API backend exposes four REST endpoints on port 8080.

1. Welcome Info

  • URL: GET /api/
  • Response Code: 200 OK
  • Content-Type: application/json
  • Response Payload:
    {
      "service": "nexops-backend",
      "environment": "staging",
      "version": "1.0.4",
      "status": "healthy",
      "message": "Welcome to NexOps Backend API (Go) โ€” built by Ichramsyah Abdurrachman"
    }

2. Liveness Check

  • URL: GET /api/health
  • Response Code: 200 OK
  • Content-Type: application/json
  • Response Payload:
    {
      "status": "ok"
    }

3. Readiness check (Database Ping)

  • URL: GET /api/ready
  • Response Code: 200 OK (Healthy) or 503 Service Unavailable (Database offline)
  • Content-Type: application/json
  • Success Response:
    {
      "status": "ready",
      "db": "connected"
    }
  • Failure Response:
    {
      "status": "not_ready",
      "db": "unreachable",
      "error": "sql: database is closed"
    }

4. Tech Stack Details

  • URL: GET /api/info
  • Response Code: 200 OK
  • Content-Type: application/json
  • Response Payload:
    {
      "platform": "NexOps Platform",
      "author": "Ichramsyah Abdurrachman",
      "stack": {
        "infra": "Terraform + GCP Compute Engine",
        "config_mgmt": "Ansible",
        "kubernetes": "K3s (self-managed)",
        "ci": "Jenkins + Trivy + SonarQube",
        "cd": "ArgoCD (GitOps)",
        "observability": "Prometheus + Grafana + Loki",
        "application": "React (Frontend) + Go (Backend) + PostgreSQL"
      }
    }

๐Ÿ”‘ Authentication & Secrets Management

The platform manages credentials using a mix of GitOps overrides and runtime Kubernetes secrets.

  • Registry Access Secret: A secret named gar-pull-secret containing base64 registry credentials is provisioned in the application namespaces (staging, prod) to pull images from Google Artifact Registry.
  • Database Credentials: Secrets named nexops-app-db-secret containing PostgreSQL parameters are provisioned inside the target namespaces:
    kubectl create secret generic nexops-app-db-secret \
      --from-literal=db_name=nexops_app \
      --from-literal=db_user=nexops_app \
      --from-literal=db_password=nexops_app_secret_pw \
      -n staging
  • System Admin Credentials:
    • Jenkins: Admin user admin with password nexops-jenkins-2024 (defined via Helm values).
    • Grafana: Admin user admin with password nexops-grafana-2024.
    • ArgoCD: Decoded dynamically from the bootstrap secret argocd-initial-admin-secret.
  • GitOps Keyrings: Injected into Jenkins using credential IDs github-pat (GitHub Personal Access Token) and sonarqube-token (SonarQube client token).

๐Ÿ”Œ Integrations

  • GitHub Integrations: Webhooks connect GitHub pushes to the Jenkins cluster API. Ingress rules route traffic from external triggers directly to the controller.
  • Let's Encrypt / Cert-Manager Integration: The ingress controller communicates with cert-manager ClusterIssuers (letsencrypt-prod / letsencrypt-staging) to initiate ACME HTTP-01 challenges. Cert-manager provisions and stores SSL certificates in Kubernetes TLS secrets.
  • Jenkins Kubernetes Integration: Jenkins uses the Kubernetes plugin to dynamically schedule agent pods. Jenkins controller communicates with the API server (https://kubernetes.default.svc) using a ServiceAccount token.
  • SonarQube Scanner & Server: The scanner container sends analysis files to http://sonarqube-sonarqube.devops.svc.cluster.local:9000 via the internal cluster network.

๐Ÿš€ Installation & Setup

Follow this sequence to install and bootstrap the platform from scratch.

Prerequisites

  • Terraform >= 1.5.0 installed.
  • Ansible >= 2.14 installed.
  • Google Cloud SDK CLI installed and authenticated.
  • Local SSH key pair ~/.ssh/nexops_rsa.

Step 1: Generate SSH Keys

ssh-keygen -t rsa -b 4096 -f ~/.ssh/nexops_rsa -C "nexops@platform"

Step 2: Deploy Infrastructure via Terraform

cd terraform/
terraform init
terraform plan -out=tfplan
terraform apply tfplan

Step 3: Populate Ansible Inventory

Export the generated Ansible config block from Terraform outputs and save it to the inventory file:

terraform output -raw ansible_inventory_snippet > ../ansible/inventory/hosts.ini

Step 4: Run Ansible Playbooks

cd ../ansible/
# Configure common settings, disable swap, configure kernel params
ansible-playbook -i inventory/hosts.ini playbooks/01-common.yml
# Deploy external PostgreSQL instance on DB VM
ansible-playbook -i inventory/hosts.ini playbooks/04-postgresql.yml
# Deploy K3s control plane on Master VM
ansible-playbook -i inventory/hosts.ini playbooks/02-k3s-master.yml
# Register Worker VM as cluster node
ansible-playbook -i inventory/hosts.ini playbooks/03-k3s-worker.yml

Step 5: Verify Cluster Nodes

export KUBECONFIG=$(pwd)/kubeconfig.yaml
kubectl get nodes -o wide

Step 6: Bootstrap ArgoCD and System Tools

Apply core namespaces and boot the App-of-Apps controller:

cd ../
# Create system namespaces
kubectl apply -f kubernetes/bootstrap/namespaces.yaml

# Install ArgoCD
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
kubectl wait --for=condition=available deployment/argocd-server -n argocd --timeout=150s

# Deploy Root application
kubectl apply -f argocd/app-of-apps.yaml

๐Ÿ”ง Configuration

To adapt the platform configuration variables to your environment:

  • Terraform Settings: Modify region, zone, instance sizes, and disk variables inside terraform/variables.tf.
  • Subnet IP Ranges: Modify subnet_cidr inside variables.tf to allocate a different range than 10.0.1.0/24. If modified, update the database host authentication permissions range in ansible/roles/postgresql/tasks/main.yml to match.
  • Domains and Ingresses: To customize host names, update the Ingress definitions under applications/k8s/base/ingress.yaml and the patches in applications/k8s/overlays/staging/kustomization.yaml.

๐Ÿ“ˆ Usage

Retrieve ArgoCD Admin Password

kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d

Extract Kubernetes Configuration

Copy the configuration generated during Ansible execution to local directory config to manage via local tools:

cp ansible/kubeconfig.yaml ~/.kube/config

Access observability dashboards

Port-forward Grafana:

kubectl port-forward service/monitoring-grafana -n monitoring 3000:80

Navigate to http://localhost:3000 (User: admin, Password: nexops-grafana-2024).


๐Ÿšข Deployment

The application features separate deployment overlay options:

  • Base Files: Contains base Kubernetes configuration manifests under applications/k8s/base/. Tracks app.nexops.local (production). Production Sync
  • Staging Patch: Configured inside applications/k8s/overlays/staging/ to patch host names to staging.nexops.local using Kustomize: Staging Sync
    patches:
      - patch: |
          apiVersion: networking.k8s.io/v1
          kind: Ingress
          metadata:
            name: nexops-app
          spec:
            rules:
            - host: staging.nexops.local
  • Deploy Trigger: Commit updates to Git. Staging will run the update pipeline and sync automatically. Production sync is triggered manually within the ArgoCD console for controlled releases.

๐Ÿ’พ DR & Backup

  • State File Isolation: The GCS backend bucket includes versioning configuration to prevent state file corruption.
  • Infrastructure Recovery: If a node fails, Terraform can replace individual compute VMs (terraform apply -replace=module.compute.google_compute_instance.worker).
  • Database Backups: Backup script outputs can be generated on the standalone DB host using a cron job running pg_dump:
    pg_dump -U nexops_app nexops_app > /var/backups/nexops_app_$(date +%F).sql
  • Git Single Source of Truth: The entire state of the cluster (except application secrets) is versioned in Git. If the cluster is lost, running the Ansible playbooks and applying argocd/app-of-apps.yaml restores the exact cluster state.

๐Ÿ”’ Security Posture

  • Namespace Isolation (Zero-Trust): Default deny-all ingress policies restrict container traffic:
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: default-deny-ingress
      namespace: prod
    spec:
      podSelector: {}
      policyTypes:
        - Ingress
  • Ingress Whitelisting: The frontend and backend pods accept ingress traffic only from the ingress-nginx and monitoring namespaces.
  • Process Privilege Reductions: backend container images build as non-root to prevent kernel-level privilege escalation.
  • Database Access Network Limits: The PostgreSQL instance is hosted on a VM with no public IP. Database ports are blocked by GCP firewall rules from any external sources outside the 10.0.1.0/24 subnet.

๐Ÿ” Troubleshooting

1. Ingress Host Conflict / Webhook Rejection

  • Symptom: Applying staging manifests throws validation errors: nginx ingress controller webhook denied admission due to duplicate host.
  • Reason: Standard deployments inside both production and staging namespaces was attempting to use identical host configurations (app.nexops.local).
  • Fix: Apply the Kustomize staging patch applications/k8s/overlays/staging/kustomization.yaml to change the staging host to staging.nexops.local.

2. Trivy Image Scan Docker Socket Errors

  • Symptom: Jenkins build log displays: dial tcp /var/run/docker.sock: connect: no such file or directory.
  • Reason: The Trivy utility scanner container cannot access the local docker runtime loop on the dynamic agent pod.
  • Fix: Run the scanner inside the sidecar docker container mapping the socket explicitly:
    docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy:latest image --exit-code 1 <image>

3. Database Connection Unreachable

  • Symptom: Go backend status shows offline. Check route readiness /api/ready returns: readyz response: database connection unreachable.
  • Reason: Internal cluster pods cannot resolve the VM hostname nexops-db or connection details.
  • Fix: Verify that the headless service and endpoints resource are applied:
    kubectl apply -f applications/k8s/base/db-service.yaml
    This maps the internal K8s domain nexops-db.default.svc.cluster.local to the DB VM IP 10.0.1.2.

๐Ÿ’ฌ FAQ

Why K3s and raw VMs instead of GKE?

Running self-managed K3s nodes on Compute Engine provides a lightweight, cost-effective control plane. It avoids managed GKE costs and provides deep visibility into Kubernetes bootstrapping, host networking, and OS-level configurations.

Why is the PostgreSQL database hosted outside the Kubernetes cluster?

Running stateful databases inside Kubernetes introduces storage management complexity. Decoupling the database onto a dedicated, private VM isolates persistent state, simplifies database administration, and ensures database lifecycle is independent of cluster rebuilds.

How do I check if Cert-Manager successfully issued certificates?

Check certificate resources across namespaces:

kubectl get certificate -A
kubectl describe certificate nexops-app-tls -n prod

๐Ÿ”ฎ Future Improvements

  • Database Migration Pipeline: Integrate Go database migration tools (e.g., golang-migrate) directly into the Jenkins pipeline build stages.
  • High Availability (HA) Control Plane: Configure multiple master nodes with an external database or embedded etcd datastore.
  • Automated SSL Verifier: Write automated testing scripts to check ingress HTTP header redirects and SSL expiry.
  • Automated Backups to GCS: Set up a scheduler daemon on the PostgreSQL host to push SQL dumps directly to secure GCS buckets.

๐Ÿค Contributing

Contributions to the NexOps Platform are welcome!

  1. Fork the repository.
  2. Create a feature branch: git checkout -b feature/amazing-feature.
  3. Commit your changes with descriptive messages: git commit -m 'feat: add database backup cron playbook'.
  4. Push your branch: git push origin feature/amazing-feature.
  5. Open a Pull Request pointing to the main branch.

๐Ÿ“„ License

There is no license file detected in the repository. This project and its codebase are proprietary and private, developed as a portfolio showcase demonstrating self-managed DevSecOps and platform engineering patterns.


๐Ÿ’– Acknowledgements

  • HashiCorp for Terraform IaC tooling.
  • Red Hat for Ansible configuration automation.
  • Rancher Labs for K3s lightweight container orchestration engine.
  • Argo Project for GitOps synchronization tooling.
  • Aqua Security for Trivy vulnerability scanner.
  • SonarSource for SonarQube code quality gate analyzers.
  • The CNCF Community for maintaining Kubernetes, Prometheus, Grafana, and standard cloud-native operators.

About

๐Ÿ”ฅ A production-grade, self-managed Kubernetes DevSecOps platform built from scratch on Google Cloud Platform.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages