{"id":49562618,"url":"https://github.com/himanshu2604/acquisitions-gitops","last_synced_at":"2026-05-03T10:44:15.097Z","repository":{"id":352024653,"uuid":"1209492535","full_name":"himanshu2604/acquisitions-gitops","owner":"himanshu2604","description":"Kubernetes manifests and Helm values for the Acquisitions API — managed by ArgoCD with automatic drift detection and self-healing","archived":false,"fork":false,"pushed_at":"2026-04-20T14:08:37.000Z","size":13,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-03T10:44:10.727Z","etag":null,"topics":["argocd","devops","gitops","grafana","helm","k8s-manifests","kube-prometheus-stack","kubernetes","portfolio","prometheus"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/himanshu2604.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-13T13:35:57.000Z","updated_at":"2026-04-20T14:09:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/himanshu2604/acquisitions-gitops","commit_stats":null,"previous_names":["himanshu2604/acquisitions-gitops"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/himanshu2604/acquisitions-gitops","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/himanshu2604%2Facquisitions-gitops","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/himanshu2604%2Facquisitions-gitops/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/himanshu2604%2Facquisitions-gitops/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/himanshu2604%2Facquisitions-gitops/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/himanshu2604","download_url":"https://codeload.github.com/himanshu2604/acquisitions-gitops/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/himanshu2604%2Facquisitions-gitops/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32566444,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-03T06:36:36.687Z","status":"ssl_error","status_checked_at":"2026-05-03T06:36:09.306Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["argocd","devops","gitops","grafana","helm","k8s-manifests","kube-prometheus-stack","kubernetes","portfolio","prometheus"],"created_at":"2026-05-03T10:44:14.438Z","updated_at":"2026-05-03T10:44:15.089Z","avatar_url":"https://github.com/himanshu2604.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# 🔄 Acquisitions GitOps\n\n### Kubernetes manifests and Helm values for the Acquisitions API  \nManaged by **ArgoCD** — Git is the only source of truth\n\n\u003cbr/\u003e\n\n![ArgoCD](https://img.shields.io/badge/ArgoCD-EF7B4D?style=for-the-badge\u0026logo=argo\u0026logoColor=white)\n![Kubernetes](https://img.shields.io/badge/Kubernetes-326CE5?style=for-the-badge\u0026logo=kubernetes\u0026logoColor=white)\n![Helm](https://img.shields.io/badge/Helm-0F1689?style=for-the-badge\u0026logo=helm\u0026logoColor=white)\n![Prometheus](https://img.shields.io/badge/Prometheus-E6522C?style=for-the-badge\u0026logo=prometheus\u0026logoColor=white)\n![Grafana](https://img.shields.io/badge/Grafana-F46800?style=for-the-badge\u0026logo=grafana\u0026logoColor=white)\n\n\u003cbr/\u003e\n\n\u003e ⚠️ **This repo contains no application code.**  \n\u003e It contains only Kubernetes YAML manifests and Helm values.  \n\u003e Application code lives in [acquisitions-api](https://github.com/himanshu2604/acquisitions-api).\n\n\u003c/div\u003e\n\n---\n\n## 📌 Table of Contents\n\n- [Why a Separate Repo?](#-why-a-separate-repo)\n- [How It Works](-#-how-it-works)\n- [Repository Structure](#-repository-structure)\n- [ArgoCD Applications](#-argocd-applications)\n- [App Manifests](#-app-manifests)\n- [Monitoring Stack](#-monitoring-stack)\n- [Prometheus + Grafana Monitoring Diagram](-#-Prometheus-+-Grafana-Monitoring-Diagram)\n- [Setting Up from Scratch](#-setting-up-from-scratch)\n- [How Deployments Happen](#-how-deployments-happen)\n- [Drift Detection](-#-drift-detection)\n\n---\n\n## 🤔 Why a Separate Repo?\n\nThis is the **two-repo GitOps pattern**. It is the standard approach used in production GitOps setups, and the reason it exists is important to understand.\n\n**The problem with putting manifests in the app repo:**\n\nIf your Kubernetes manifests live in the same repo as your code, your CI pipeline ends up running `kubectl apply` directly. This means:\n\n- The cluster state is controlled by a CI runner — an ephemeral, stateless process\n- If someone manually changes something in the cluster, there's no automatic correction\n- You can't tell the difference between a code change and an infrastructure change\n- Rolling back infrastructure requires rolling back code too\n\n**The GitOps solution:**\n\nBy keeping manifests in a separate repo, ArgoCD continuously watches **this repo** and ensures the cluster always reflects what is committed here. The CI pipeline in `acquisitions-api` never touches the cluster directly — it only updates the image tag in this repo, and ArgoCD handles the actual deployment.\n\n| | Traditional CI/CD | GitOps (this approach) |\n|---|---|---|\n| Who deploys? | CI runner (`kubectl apply`) | ArgoCD (continuous reconciliation) |\n| Source of truth | CI pipeline | Git repository (this repo) |\n| Manual cluster changes | Persist silently | Auto-reverted within minutes |\n| Audit trail | CI logs | Git commit history |\n| Rollback | Re-run pipeline | `git revert` + push |\n\n---\n\n## ⚙️ How It Works\n\n\u003cimg width=\"2012\" height=\"863\" alt=\"diagram-export-4-20-2026-5_52_48-PM\" src=\"https://github.com/user-attachments/assets/1f052780-8d89-418b-9bc4-2f72496a959e\" /\u003e\n\n\n---\n\n## 📁 Repository Structure\n\n```\nacquisitions-gitops/\n│\n├── app/                          # Acquisitions API — Kubernetes manifests\n│   ├── namespace.yaml            # Creates the \"acquisitions\" namespace\n│   ├── configmap.yaml            # Non-sensitive env vars (NODE_ENV, PORT)\n│   ├── secret.yaml               # Sensitive values (DATABASE_URL, JWT_SECRET)\n│   ├── deployment.yaml           # 3 replicas with rolling update strategy\n│   └── service.yaml              # NodePort service exposing the API\n│\n└── monitoring/                   # Prometheus + Grafana — Helm values\n    ├── namespace.yaml            # Creates the \"monitoring\" namespace\n    ├── prometheus-values.yaml    # kube-prometheus-stack Helm configuration\n    └── alert-rules.yaml          # PrometheusRule — crash, CPU, error alerts\n```\n\n---\n\n## 🔵 ArgoCD Applications\n\nThere are **two ArgoCD Applications** watching this repo:\n\n### 1. acquisitions-api\n- **Watches:** `app/` folder in this repo\n- **Deploys to:** `acquisitions` namespace\n- **Sync policy:** Automatic + SelfHeal\n- **What it manages:** The API deployment, service, configmap, secret\n\n### 2. monitoring\n- **Watches:** `monitoring/` folder in this repo for Helm values\n- **Helm chart:** `kube-prometheus-stack` from prometheus-community\n- **Deploys to:** `monitoring` namespace\n- **Sync policy:** Automatic + SelfHeal\n- **What it manages:** Prometheus, Grafana, Alertmanager, node-exporter, kube-state-metrics\n\nBoth applications have `selfHeal: true`, which means ArgoCD will automatically revert any manual changes made to the cluster that deviate from what is defined in this repo.\n\n---\n\n## 📦 App Manifests\n\n### deployment.yaml\n\nKey production settings used:\n\n```yaml\nreplicas: 3                  # Always 3 replicas running\n\nstrategy:\n  type: RollingUpdate\n  maxSurge: 1               # Spin up 1 extra pod before killing old ones\n  maxUnavailable: 0         # Never drop below 3 during an update\n\nlivenessProbe:              # Restarts the pod if it becomes unresponsive\n  httpGet:\n    path: /health\n\nreadinessProbe:             # Removes pod from load balancer during startup\n  httpGet:\n    path: /health\n\nresources:\n  requests:\n    memory: \"128Mi\"         # Minimum guaranteed to the pod\n    cpu: \"100m\"\n  limits:\n    memory: \"256Mi\"         # Maximum the pod can use\n    cpu: \"500m\"\n```\n\n\u003e The image tag in `deployment.yaml` is automatically updated by the CI  \n\u003e pipeline in `acquisitions-api` on every successful push to `main`.  \n\u003e It is tagged with the full Git commit SHA for full traceability.\n\n### Namespaces\n\n| Namespace | Contents |\n|---|---|\n| `acquisitions` | API pods, service, configmap, secret |\n| `monitoring` | Prometheus, Grafana, Alertmanager, exporters |\n| `argocd` | ArgoCD itself (installed separately) |\n\n---\n\n## 📊 Monitoring Stack\n\nPrometheus and Grafana are deployed via the `kube-prometheus-stack` Helm chart, with custom values defined in `monitoring/prometheus-values.yaml`.\n\n---\n\n## Prometheus + Grafana Monitoring Diagram\n\n\u003cimg width=\"1382\" height=\"807\" alt=\"diagram-export-4-20-2026-5_53_38-PM\" src=\"https://github.com/user-attachments/assets/c92c84dc-6c9c-4677-bc2a-4986f20cc412\" /\u003e\n\n---\n\n### What gets deployed\n\n| Component | Purpose |\n|---|---|\n| **Prometheus** | Scrapes metrics from all targets every 15 seconds |\n| **Grafana** | Dashboards and visualisation |\n| **Alertmanager** | Routes alerts to Slack |\n| **node-exporter** | Host-level metrics (CPU, memory, disk per node) |\n| **kube-state-metrics** | Kubernetes object metrics (pod health, replica counts) |\n\n### Custom scrape target\n\nPrometheus is configured to scrape the Acquisitions API directly:\n\n```yaml\nadditionalScrapeConfigs:\n  - job_name: 'acquisitions-api'\n    static_configs:\n      - targets:\n          - 'acquisitions-api-service.acquisitions.svc.cluster.local:80'\n    metrics_path: '/metrics'\n    scrape_interval: 15s\n```\n\n### Alert rules (`alert-rules.yaml`)\n\n| Alert Name | Condition | Severity | Action |\n|---|---|---|---|\n| `AcquisitionsPodCrashLooping` | Pod restarts \u003e 3 in 2 min | Critical | Slack notification |\n| `AcquisitionsHighCPU` | CPU \u003e 80% for 5 min | Warning | Slack notification |\n| `AcquisitionsHighErrorRate` | 5xx error rate \u003e 5% for 2 min | Critical | Slack notification |\n\n---\n\n## 🚀 Setting Up from Scratch\n\nIf you are cloning this repo to set up your own cluster, follow these steps.\n\n### Prerequisites\n\n- A running Kubernetes cluster (Minikube, k3d, or cloud)\n- `kubectl` configured to point to your cluster\n- `helm` installed\n- ArgoCD installed in the `argocd` namespace\n\n### Step 1 — Install ArgoCD (if not already installed)\n\n```bash\nkubectl create namespace argocd\nkubectl apply -n argocd \\\n  -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml\n\n# Wait for ArgoCD to be ready\nkubectl wait --for=condition=Ready pod \\\n  -l app.kubernetes.io/name=argocd-server \\\n  -n argocd --timeout=300s\n```\n\n### Step 2 — Create the API secrets\n\n```bash\n# Base64 encode your values\necho -n \"your_database_url\" | base64\necho -n \"your_jwt_secret\"   | base64\necho -n \"your_arcjet_key\"   | base64\necho -n \"your_cookie_secret\" | base64\n\n# Edit app/secret.yaml with your encoded values\n# Then apply it directly (secrets are not auto-synced for security)\nkubectl apply -f app/secret.yaml\n```\n\n### Step 3 — Create ArgoCD Application for the API\n\n```bash\nkubectl apply -f - \u003c\u003c'EOF'\napiVersion: argoproj.io/v1alpha1\nkind: Application\nmetadata:\n  name: acquisitions-api\n  namespace: argocd\nspec:\n  project: default\n  source:\n    repoURL: https://github.com/himanshu2604/acquisitions-gitops\n    targetRevision: HEAD\n    path: app\n  destination:\n    server: https://kubernetes.default.svc\n    namespace: acquisitions\n  syncPolicy:\n    automated:\n      prune: true\n      selfHeal: true\n    syncOptions:\n      - CreateNamespace=true\nEOF\n```\n\n### Step 4 — Create ArgoCD Application for monitoring\n\n```bash\nhelm repo add prometheus-community \\\n  https://prometheus-community.github.io/helm-charts\nhelm repo update\n\nkubectl apply -f - \u003c\u003c'EOF'\napiVersion: argoproj.io/v1alpha1\nkind: Application\nmetadata:\n  name: monitoring\n  namespace: argocd\nspec:\n  project: default\n  sources:\n    - repoURL: https://prometheus-community.github.io/helm-charts\n      chart: kube-prometheus-stack\n      targetRevision: \"58.0.0\"\n      helm:\n        valueFiles:\n          - $values/monitoring/prometheus-values.yaml\n    - repoURL: https://github.com/himanshu2604/acquisitions-gitops\n      targetRevision: HEAD\n      ref: values\n  destination:\n    server: https://kubernetes.default.svc\n    namespace: monitoring\n  syncPolicy:\n    automated:\n      prune: true\n      selfHeal: true\n    syncOptions:\n      - CreateNamespace=true\n      - ServerSideApply=true\nEOF\n```\n\n### Step 5 — Verify everything is running\n\n```bash\n# Check API pods\nkubectl get pods -n acquisitions\n# Expected: 3 pods in Running state\n\n# Check monitoring pods\nkubectl get pods -n monitoring\n# Expected: prometheus, grafana, alertmanager, node-exporter, kube-state-metrics\n\n# Check ArgoCD applications\nkubectl get applications -n argocd\n# Expected: acquisitions-api   Synced   Healthy\n#           monitoring          Synced   Healthy\n```\n\n### Step 6 — Access the dashboards\n\n```bash\n# Grafana\nkubectl port-forward svc/kube-prometheus-stack-grafana \\\n  -n monitoring 3001:80\n# Open: http://localhost:3001  (admin / admin123change)\n\n# ArgoCD UI\nkubectl port-forward svc/argocd-server \\\n  -n argocd 8080:443\n# Open: https://localhost:8080\n\n# Prometheus\nkubectl port-forward svc/kube-prometheus-stack-prometheus \\\n  -n monitoring 9090:9090\n# Open: http://localhost:9090\n```\n\n---\n\n## 🔄 How Deployments Happen\n\nYou **never need to touch this repo manually** for normal deployments.\n\nWhen a developer pushes code to `acquisitions-api`:\n\n```\n1. GitHub Actions runs the CI pipeline\n2. Lint, test, Trivy scan, SonarCloud all pass\n3. New Docker image pushed to Docker Hub with commit SHA tag\n4. CI bot clones this repo\n5. Runs: sed -i \"s|image: .*/acquisitions-api:.*|image: user/acquisitions-api:NEW_SHA|\" app/deployment.yaml\n6. Commits: \"ci: bump image to abc1234\"\n7. Pushes to this repo\n8. ArgoCD detects the new commit within ~3 minutes\n9. ArgoCD applies the updated deployment.yaml to the cluster\n10. Kubernetes performs a rolling update — zero downtime\n```\n\n**To manually trigger a sync:**\n\n```bash\n# Using ArgoCD CLI\nargocd app sync acquisitions-api\n\n# Or in the ArgoCD UI — click \"Sync\" on the application\n```\n\n**To roll back to a previous version:**\n\n```bash\n# Find the commit you want to go back to\ngit log --oneline\n\n# Revert the image tag commit\ngit revert \u003ccommit-hash\u003e\ngit push\n\n# ArgoCD will automatically deploy the reverted version\n```\n\n---\n\n## 🛡️ Drift Detection\n\nBoth ArgoCD applications have `selfHeal: true`.\n\nThis means if anyone manually changes the cluster state — for example by running `kubectl scale`, `kubectl edit`, or `kubectl delete` — ArgoCD will:\n\n1. Detect the divergence between Git (desired state) and the cluster (actual state)\n2. Mark the application as `OutOfSync`\n3. Automatically apply the Git state back to the cluster within ~3 minutes\n4. Mark the application as `Synced` again\n\n**This was tested by:**\n\n```bash\n# Manually scaling down to simulate an accident\nkubectl scale deployment acquisitions-api \\\n  --replicas=1 -n acquisitions\n\n# ArgoCD detected drift and restored 3 replicas automatically\n# Time to self-heal: ~3 minutes\n# Human intervention required: zero\n```\n\nThis is why Git is the source of truth, not someone's terminal.\n\n---\n\n## 📄 License\n\nThis project is for educational and portfolio purposes.\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\n**🔗 Application repo:** [acquisitions-api](https://github.com/himanshu2604/acquisitions-api) — Node.js API + CI pipeline\n\n*Built by Himanshu as a DevOps portfolio project*\n\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhimanshu2604%2Facquisitions-gitops","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhimanshu2604%2Facquisitions-gitops","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhimanshu2604%2Facquisitions-gitops/lists"}