Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 83 additions & 31 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
@@ -1,36 +1,88 @@
trigger:
- main
- main

pool:
vmImage: 'ubuntu-latest'
vmImage: "ubuntu-latest"

variables:
nodeVersion: '18.x'

steps:
# Install Node
- task: NodeTool@0
inputs:
versionSpec: '$(nodeVersion)'
displayName: 'Install Node.js'

# Build Backend
- script: |
cd backend
npm install
npm test || echo "No tests"
displayName: 'Backend Build & Test'

# Build Frontend
- script: |
cd frontend
npm install
npm install -g @angular/cli
ng build --configuration=production
displayName: 'Angular Build'

# Publish Artifact
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: 'frontend/dist'
ArtifactName: 'angular-build'
dockerRegistryServiceConnection: "ACR-SERVICE-CONNECTION"
kubernetesServiceConnection: "AKS-SERVICE-CONNECTION"
imageRepository: "angular-nodejs-example"
containerRegistry: "myacr.azurecr.io"
dockerfilePath: "$(Build.SourcesDirectory)/Dockerfile"
tag: "$(Build.BuildId)"
namespace: "dev"

stages:
- stage: Build
displayName: Build and Push
jobs:
- job: Build
displayName: Build and Push Image
steps:
- task: Docker@2
displayName: Build and push image
inputs:
containerRegistry: "$(dockerRegistryServiceConnection)"
repository: "$(imageRepository)"
command: "buildAndPush"
Dockerfile: "$(dockerfilePath)"
tags: |
$(tag)

- stage: Deploy
displayName: Deploy to AKS
dependsOn: Build
jobs:
- job: Deploy
displayName: Deploy manifests
steps:
- task: Kubernetes@1
displayName: Ensure namespace
inputs:
connectionType: "Kubernetes Service Connection"
kubernetesServiceEndpoint: "$(kubernetesServiceConnection)"
command: "apply"
useConfigurationFile: true
configuration: "k8s/namespace.yaml"

- task: Kubernetes@1
displayName: Apply deployment
inputs:
connectionType: "Kubernetes Service Connection"
kubernetesServiceEndpoint: "$(kubernetesServiceConnection)"
namespace: "$(namespace)"
command: "apply"
useConfigurationFile: true
configuration: "k8s/deployment.yaml"

- task: Kubernetes@1
displayName: Apply service
inputs:
connectionType: "Kubernetes Service Connection"
kubernetesServiceEndpoint: "$(kubernetesServiceConnection)"
namespace: "$(namespace)"
command: "apply"
useConfigurationFile: true
configuration: "k8s/service.yaml"

- task: Kubernetes@1
displayName: Apply ingress
inputs:
connectionType: "Kubernetes Service Connection"
kubernetesServiceEndpoint: "$(kubernetesServiceConnection)"
namespace: "$(namespace)"
command: "apply"
useConfigurationFile: true
configuration: "k8s/ingress.yaml"

- task: Kubernetes@1
displayName: Update image
inputs:
connectionType: "Kubernetes Service Connection"
kubernetesServiceEndpoint: "$(kubernetesServiceConnection)"
namespace: "$(namespace)"
command: "set"
arguments: >-
image deployment/angular-nodejs-example
angular-nodejs-example=$(containerRegistry)/$(imageRepository):$(tag)
106 changes: 106 additions & 0 deletions docs/aks-cicd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# AKS CI/CD + Managed Prometheus/Grafana (Dev)

This guide sets up Azure DevOps CI/CD, Azure Container Registry (ACR), Azure Kubernetes Service (AKS), and Azure Managed Prometheus/Grafana for a dev environment sized for ~10 users.

## Architecture

- Azure DevOps Pipeline builds and pushes the Docker image to ACR.
- AKS pulls the image and runs the deployment in the `dev` namespace.
- Azure Managed Prometheus/Grafana monitors AKS and the workload.

## Prerequisites

- Azure subscription with **Owner** or **Contributor** access.
- Azure DevOps project with permissions to create service connections.
- Azure CLI, kubectl, and Helm installed locally (for provisioning).

## Suggested Sizing (Dev / ~10 Users)

- AKS node pool: **2x Standard_B2s** (2 vCPU, 4 GB RAM each)
- Kubernetes deployment replicas: **2** (defined in `k8s/deployment.yaml`)

## Provisioning (Example)

> Replace names and regions as needed.

```bash
# Resource group
az group create --name rg-angular-nodejs-dev --location eastus

# Container registry
az acr create --resource-group rg-angular-nodejs-dev --name myacr --sku Basic

# AKS cluster
az aks create \
--resource-group rg-angular-nodejs-dev \
--name aks-angular-nodejs-dev \
--node-count 2 \
--node-vm-size Standard_B2s \
--enable-managed-identity \
--attach-acr myacr

# Fetch kubeconfig
az aks get-credentials --resource-group rg-angular-nodejs-dev --name aks-angular-nodejs-dev
```

## Enable Azure Managed Prometheus/Grafana

```bash
# Enable Azure Monitor for containers (includes managed Prometheus)
az aks enable-addons \
--resource-group rg-angular-nodejs-dev \
--name aks-angular-nodejs-dev \
--addons monitoring

# Create a Managed Grafana instance
az grafana create \
--name grafana-angular-nodejs-dev \
--resource-group rg-angular-nodejs-dev
```

> In Grafana, connect to the managed Prometheus workspace created by the AKS monitoring addon.

## Azure DevOps Service Connections

Create the following service connections:

1. **ACR Service Connection**
- Type: Docker Registry
- Registry: `myacr.azurecr.io`
- Name used in pipeline: `ACR-SERVICE-CONNECTION`

2. **AKS Service Connection**
- Type: Kubernetes
- Cluster: `aks-angular-nodejs-dev`
- Namespace: `dev`
- Name used in pipeline: `AKS-SERVICE-CONNECTION`

Update the values in `azure-pipelines.yml` if you choose different names.

## Pipeline Overview

- **Build stage**: Docker build and push to ACR.
- **Deploy stage**: Apply namespace, deployment, service, ingress, and set image tag.

## Kubernetes Manifests

- `k8s/namespace.yaml`
- `k8s/deployment.yaml`
- `k8s/service.yaml`
- `k8s/ingress.yaml`

### Update the ingress host

Set your DNS host in `k8s/ingress.yaml`:

```yaml
host: angular-nodejs-example.dev.example.com
```

If you are using a public domain, create a DNS A record pointing to the AKS ingress public IP.

## Optional Enhancements

- Add an HPA for CPU-based autoscaling.
- Add TLS with cert-manager for HTTPS.
- Add Azure Monitor alerts for latency and error rate.
47 changes: 47 additions & 0 deletions k8s/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: angular-nodejs-example
namespace: dev
labels:
app: angular-nodejs-example
spec:
replicas: 2
selector:
matchLabels:
app: angular-nodejs-example
template:
metadata:
labels:
app: angular-nodejs-example
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "3080"
spec:
containers:
- name: angular-nodejs-example
image: myacr.azurecr.io/angular-nodejs-example:latest
ports:
- containerPort: 3080
env:
- name: NODE_ENV
value: "production"
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
readinessProbe:
httpGet:
path: /
port: 3080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /
port: 3080
initialDelaySeconds: 15
periodSeconds: 20
19 changes: 19 additions & 0 deletions k8s/ingress.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: angular-nodejs-example
namespace: dev
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- host: angular-nodejs-example.dev.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: angular-nodejs-example
port:
number: 80
6 changes: 6 additions & 0 deletions k8s/namespace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: dev
labels:
environment: dev
15 changes: 15 additions & 0 deletions k8s/service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: angular-nodejs-example
namespace: dev
labels:
app: angular-nodejs-example
spec:
type: ClusterIP
selector:
app: angular-nodejs-example
ports:
- name: http
port: 80
targetPort: 3080