[{"data":1,"prerenderedAt":355},["ShallowReactive",2],{"project-cloudops":3},{"id":4,"title":5,"body":6,"description":332,"duration":333,"extension":334,"featured":150,"github":335,"image":336,"live":337,"meta":338,"navigation":150,"order":129,"path":339,"role":337,"seo":340,"status":341,"stem":342,"team_size":337,"tech":343,"type":352,"year":353,"__hash__":354},"projects\u002Fproject\u002Fcloudops.md","CloudOps: Azure GitOps Platform",{"type":7,"value":8,"toc":324},"minimark",[9,14,18,22,25,59,63,68,86,96,100,105,116,228,232,239,320],[10,11,13],"h2",{"id":12},"the-problem","The Problem",[15,16,17],"p",{},"In traditional deployments, infrastructure code, backend logic, and frontend assets are heavily entangled. A minor frontend change might trigger a massive infrastructure validation pipeline, slowing down delivery. Furthermore, \"Day-2 Operations\"—such as scaling, monitoring, and managing registry credentials—are often handled manually. Storing static passwords for container registries inside Kubernetes clusters introduces critical security vulnerabilities and maintenance nightmares when credentials rotate.",[10,19,21],{"id":20},"my-solution","My Solution",[15,23,24],{},"I engineered a comprehensive GitOps monorepo that strictly decouples the application lifecycle from the infrastructure lifecycle, fully deployed on Microsoft Azure.",[26,27,28,36,42,48],"ul",{},[29,30,31,35],"li",{},[32,33,34],"strong",{},"Path-Based CI\u002FCD Pipelines:"," Configured GitHub Actions to dynamically trigger independent workflows (Frontend, Backend, Infrastructure) based exclusively on which directories were modified in the commit.",[29,37,38,41],{},[32,39,40],{},"Zero-Credential Infrastructure:"," Provisioned Azure Kubernetes Service (AKS) and Azure Container Registry (ACR) via Terraform, utilizing SystemAssigned Managed Identities for seamless, passwordless image pulling.",[29,43,44,47],{},[32,45,46],{},"Internal DNS Routing:"," Designed the Kubernetes topology to eliminate unnecessary public endpoints. The Go (Gin) backend and PostgreSQL database run purely on internal ClusterIPs, protected behind an Nginx reverse proxy.",[29,49,50,53,54,58],{},[32,51,52],{},"Proactive Observability:"," Deployed the ",[55,56,57],"code",{},"kube-prometheus-stack"," via Helm, configuring custom Prometheus alerting rules and Grafana dashboards to proactively monitor node resources and pod health.",[10,60,62],{"id":61},"technical-deep-dive","Technical Deep Dive",[64,65,67],"h3",{"id":66},"architecture-decisions","Architecture Decisions",[15,69,70,73,74,77,78,81,82,85],{},[32,71,72],{},"Why Path-Based CI\u002FCD in a Monorepo?","\nA monorepo provides a single source of truth, but running ",[55,75,76],{},"terraform plan"," every time a developer updates a Go API endpoint wastes compute minutes and delays deployments. By utilizing GitHub Actions ",[55,79,80],{},"paths"," filtering, pushing code to ",[55,83,84],{},"applications\u002Fbackend\u002F**"," strictly triggers the Go build and AKS deployment process, completely isolating it from the frontend and infrastructure pipelines.",[15,87,88,91,92,95],{},[32,89,90],{},"Why SystemAssigned Managed Identities?","\nInstead of manually creating Kubernetes Secrets to store ACR passwords (which can be leaked or expire), I configured Terraform to grant the AKS cluster's managed identity the ",[55,93,94],{},"AcrPull"," role. This allows the cluster to authenticate to the container registry natively via Azure's IAM layer, achieving a true zero-secret deployment state.",[64,97,99],{"id":98},"key-features-i-built","Key Features I Built",[101,102,104],"h4",{"id":103},"_1-secure-internal-routing-nginx-reverse-proxy","1. Secure Internal Routing (Nginx Reverse Proxy)",[15,106,107,108,111,112,115],{},"To minimize the attack surface and reduce cloud load balancer costs, only the frontend Nginx pod is exposed externally. I configured Nginx to act as an API gateway, dynamically routing ",[55,109,110],{},"\u002Fapi\u002F*"," traffic to the backend via Kubernetes' internal DNS (",[55,113,114],{},"backend-service","), ensuring the Go API remains completely hidden from the public internet.",[117,118,123],"pre",{"className":119,"code":120,"language":121,"meta":122,"style":122},"language-nginx shiki shiki-themes github-light github-dark","# applications\u002Ffrontend\u002Fnginx.k8s.conf\nserver {\n    listen 80;\n\n    # Serve static frontend files\n    location \u002F {\n        root \u002Fusr\u002Fshare\u002Fnginx\u002Fhtml;\n        index index.html;\n    }\n\n    # Proxy API traffic securely through Kubernetes internal DNS\n    location \u002Fapi\u002F {\n        proxy_pass http:\u002F\u002Fbackend-service:5000\u002F;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n    }\n}\n","nginx","",[55,124,125,133,139,145,152,158,164,170,176,182,187,193,199,205,211,217,222],{"__ignoreMap":122},[126,127,130],"span",{"class":128,"line":129},"line",1,[126,131,132],{},"# applications\u002Ffrontend\u002Fnginx.k8s.conf\n",[126,134,136],{"class":128,"line":135},2,[126,137,138],{},"server {\n",[126,140,142],{"class":128,"line":141},3,[126,143,144],{},"    listen 80;\n",[126,146,148],{"class":128,"line":147},4,[126,149,151],{"emptyLinePlaceholder":150},true,"\n",[126,153,155],{"class":128,"line":154},5,[126,156,157],{},"    # Serve static frontend files\n",[126,159,161],{"class":128,"line":160},6,[126,162,163],{},"    location \u002F {\n",[126,165,167],{"class":128,"line":166},7,[126,168,169],{},"        root \u002Fusr\u002Fshare\u002Fnginx\u002Fhtml;\n",[126,171,173],{"class":128,"line":172},8,[126,174,175],{},"        index index.html;\n",[126,177,179],{"class":128,"line":178},9,[126,180,181],{},"    }\n",[126,183,185],{"class":128,"line":184},10,[126,186,151],{"emptyLinePlaceholder":150},[126,188,190],{"class":128,"line":189},11,[126,191,192],{},"    # Proxy API traffic securely through Kubernetes internal DNS\n",[126,194,196],{"class":128,"line":195},12,[126,197,198],{},"    location \u002Fapi\u002F {\n",[126,200,202],{"class":128,"line":201},13,[126,203,204],{},"        proxy_pass http:\u002F\u002Fbackend-service:5000\u002F;\n",[126,206,208],{"class":128,"line":207},14,[126,209,210],{},"        proxy_set_header Host $host;\n",[126,212,214],{"class":128,"line":213},15,[126,215,216],{},"        proxy_set_header X-Real-IP $remote_addr;\n",[126,218,220],{"class":128,"line":219},16,[126,221,181],{},[126,223,225],{"class":128,"line":224},17,[126,226,227],{},"}\n",[101,229,231],{"id":230},"_2-automated-day-2-operational-scripts","2. Automated Day-2 Operational Scripts",[15,233,234,235,238],{},"To ensure rapid incident response, I wrote abstraction scripts for complex disaster recovery scenarios. Instead of operators memorizing complex ",[55,236,237],{},"kubectl"," rollout commands during high-stress outages, they can execute a single automated script to instantly revert deployments to their last known stable state.",[117,240,244],{"className":241,"code":242,"language":243,"meta":122,"style":122},"language-bash shiki shiki-themes github-light github-dark","# scripts\u002Frollback.sh\necho \"Initiating rollback for $COMPONENT...\"\nkubectl rollout undo deployment\u002F$COMPONENT -n $NAMESPACE\n\necho \"Waiting for rollback to complete...\"\nkubectl rollout status deployment\u002F$COMPONENT -n $NAMESPACE\n","bash",[55,245,246,252,269,292,296,303],{"__ignoreMap":122},[126,247,248],{"class":128,"line":129},[126,249,251],{"class":250},"sJ8bj","# scripts\u002Frollback.sh\n",[126,253,254,258,262,266],{"class":128,"line":135},[126,255,257],{"class":256},"sj4cs","echo",[126,259,261],{"class":260},"sZZnC"," \"Initiating rollback for ",[126,263,265],{"class":264},"sVt8B","$COMPONENT",[126,267,268],{"class":260},"...\"\n",[126,270,271,274,277,280,283,286,289],{"class":128,"line":141},[126,272,237],{"class":273},"sScJk",[126,275,276],{"class":260}," rollout",[126,278,279],{"class":260}," undo",[126,281,282],{"class":260}," deployment\u002F",[126,284,285],{"class":264},"$COMPONENT ",[126,287,288],{"class":256},"-n",[126,290,291],{"class":264}," $NAMESPACE\n",[126,293,294],{"class":128,"line":147},[126,295,151],{"emptyLinePlaceholder":150},[126,297,298,300],{"class":128,"line":154},[126,299,257],{"class":256},[126,301,302],{"class":260}," \"Waiting for rollback to complete...\"\n",[126,304,305,307,309,312,314,316,318],{"class":128,"line":160},[126,306,237],{"class":273},[126,308,276],{"class":260},[126,310,311],{"class":260}," status",[126,313,282],{"class":260},[126,315,285],{"class":264},[126,317,288],{"class":256},[126,319,291],{"class":264},[321,322,323],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":122,"searchDepth":135,"depth":135,"links":325},[326,327,328],{"id":12,"depth":135,"text":13},{"id":20,"depth":135,"text":21},{"id":61,"depth":135,"text":62,"children":329},[330,331],{"id":66,"depth":141,"text":67},{"id":98,"depth":141,"text":99},"A production-grade GitOps monorepo deploying a Go\u002FNginx microservices architecture on a Terraform-provisioned Azure Kubernetes Service (AKS) cluster, featuring decoupled CI\u002FCD and zero-credential security.","4 weeks","md","https:\u002F\u002Fgithub.com\u002Fszuryuu\u002Fcloudops","\u002Fimages\u002Fprojects\u002Fterraform.png",null,{},"\u002Fproject\u002Fcloudops",{"title":5,"description":332},"Completed","project\u002Fcloudops",[344,345,346,347,348,349,350,351],"Azure","AKS","Terraform","GitHub Actions","Go (Gin)","Nginx","PostgreSQL","Prometheus","Solo Project","2026","EkFyCLGiN8uyjJi8YcVn-m0h2mejVpS5tU18FkPj9lU",1776582962959]