-
Notifications
You must be signed in to change notification settings - Fork 198
Add: New deploy-to-aks skill #1827
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Tom Gamble (gambtho)
wants to merge
5
commits into
microsoft:main
from
gambtho:feature/deploy-to-aks-skill
Closed
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
5e82d4a
feat: add deploy-to-aks skill
gambtho 5224f65
fix: address PR review feedback for deploy-to-aks skill
gambtho d41e500
fix: address remaining PR review feedback for deploy-to-aks skill
gambtho e2d539f
fix: harden deploy.yml workflow correctness
gambtho 0d05e7c
fix: address Copilot review comments on namespace, Dockerfile, and te…
gambtho File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -347,3 +347,4 @@ x86/ | |
| dashboard/.azure/ | ||
| dashboard/dist/ | ||
| dashboard/**/dist/ | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| --- | ||
| name: deploy-to-aks | ||
| license: MIT | ||
| metadata: | ||
| author: Microsoft | ||
| version: "1.0.0" | ||
| description: "Use when deploying a web application or API to an existing Azure Kubernetes Service cluster. Detects framework, generates Dockerfile and Kubernetes manifests, validates against AKS Deployment Safeguards, and deploys with verification. WHEN: deploy to AKS, deploy app to Kubernetes, containerize for AKS, deploy to existing AKS cluster, generate K8s manifests for Azure, set up CI/CD for AKS, migrate app to AKS, deploy container to Azure, I have a Django/Express/Spring Boot app and want to run it on AKS, my AKS deployment is failing safeguard checks." | ||
| --- | ||
|
|
||
| # Deploy to AKS | ||
|
|
||
| Deploy applications to an existing AKS cluster with production-grade artifacts. Detects the framework, generates Dockerfile + K8s manifests, validates against Deployment Safeguards, and deploys — with minimal questions. | ||
|
|
||
| ## When to Use This Skill | ||
|
|
||
| **Use this skill when:** | ||
| - You want to deploy an existing web application or API to an AKS cluster | ||
| - You need to containerize an app for Kubernetes and generate deployment manifests | ||
| - Your AKS deployment is failing Deployment Safeguard checks (DS001–DS013) and you need guidance | ||
| - You want to set up or improve CI/CD pipelines for AKS deployments | ||
| - You're migrating an application from another platform to AKS | ||
|
|
||
| **Do NOT use this skill for:** | ||
| - Provisioning or creating a new AKS cluster (use a separate provisioning skill) | ||
| - Deploying to non-AKS compute targets (Web Apps, Container Apps, etc.) | ||
| - Managing cluster infrastructure, scaling policies, or node pools | ||
| - Performing Kubernetes cluster administration tasks (RBAC, networking policies, etc.) | ||
|
|
||
| ## MCP Tools | ||
|
|
||
| | Tool | Purpose | Required | | ||
| |------|---------|----------| | ||
| | `azure-documentation` | Fetch Azure documentation and configuration references | Yes | | ||
| | Terminal commands | Execute `kubectl`, `az`, `docker`, `gh` CLI commands | Yes | | ||
|
|
||
| ## Error Handling | ||
|
|
||
| | Error | Likely Cause | Recovery | | ||
| |-------|--------------|----------| | ||
| | Safeguard validation failure (DS001–DS013) | Manifest violates deployment best practices (missing resource limits, security policies, etc.) | Review the safeguard checklist in `references/safeguards.md`, apply recommended fixes, re-validate | | ||
| | Image push fails to ACR | ACR not attached to cluster or authentication token expired | Run `az acr login --name <registry>`, verify ACR attachment with `az aks check-acr`, retry push | | ||
| | `kubectl apply` fails | Manifest syntax error or unsupported API version | Check manifest YAML syntax, verify API version compatibility with cluster Kubernetes version using `kubectl api-resources` | | ||
| | Pod CrashLoopBackOff | Application fails to start (missing env vars, config, port mismatch) | Check logs with `kubectl logs <pod>`, verify health endpoint config, ensure all required environment variables are set in ConfigMap/Secrets | | ||
| | Workload Identity auth failure | OIDC not configured or service account not mapped | Follow `references/workload-identity.md` to set up federated identity credentials and service account annotations | | ||
| | Deployment rollout stuck | Resource quota exceeded or image pull failure | Check `kubectl describe deployment`, verify resource requests fit quota, ensure image pull secrets are configured, check node readiness | | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - An existing AKS cluster | ||
| - Azure CLI authenticated (`az login`) | ||
| - `kubectl` configured for the target cluster | ||
|
|
||
| ## Workflow | ||
|
|
||
|
gambtho marked this conversation as resolved.
|
||
| Follow the quick deploy workflow in `phases/quick-deploy.md`. The workflow has 5 sections: | ||
|
|
||
| 1. **Detection** — scan project for framework/port/health endpoints; detect AKS cluster, ACR, routing mode | ||
| 2. **File Generation** — generate Dockerfile + K8s manifests from templates | ||
| 3. **Safeguards Validation** — validate manifests against AKS Deployment Safeguards DS001-DS013 | ||
| 4. **Deploy** — build image, push to ACR, apply manifests | ||
| 5. **Verify** — confirm pods running, external IP available, health check passing | ||
|
|
||
| ## Quick Reference | ||
|
|
||
| | Property | Value | | ||
| |----------|-------| | ||
| | Best for | Deploying apps to an existing AKS cluster | | ||
| | MCP Tools | `azure-documentation` | | ||
| | CLI | `az acr build`, `kubectl apply`, `kubectl rollout status` | | ||
| | Related skills | azure-kubernetes (cluster provisioning), azure-diagnostics (troubleshooting) | | ||
|
|
||
| ## Workflow Quick Reference | ||
|
|
||
| | Step | Read | Also load | | ||
| |------|------|-----------| | ||
| | Quick Deploy | `phases/quick-deploy.md` | `references/detection.md`, `knowledge-packs/frameworks/<detected>.md` (if exists), `references/safeguards.md`, `references/workload-identity.md`, `references/rollback.md` (on failure) | | ||
|
|
||
|
gambtho marked this conversation as resolved.
|
||
| ## References | ||
|
|
||
| Load these on-demand based on workflow phase: | ||
|
|
||
| - [detection.md](./references/detection.md) — framework, port, and health endpoint detection tables | ||
| - [safeguards.md](./references/safeguards.md) — AKS Deployment Safeguards DS001-DS013 checklist | ||
| - [workload-identity.md](./references/workload-identity.md) — Azure Workload Identity setup for AKS pods | ||
| - [rollback.md](./references/rollback.md) — recovery procedures for deployment failures | ||
|
|
||
| ## Knowledge Packs | ||
|
|
||
| After detecting the framework, load the matching pack from `knowledge-packs/frameworks/` if available. Packs provide framework-specific Dockerfile patterns, health endpoints, database config, and writable path requirements. | ||
|
|
||
| Available: `spring-boot`, `express`, `nextjs`, `fastapi`, `django`, `nestjs`, `aspnet-core`, `go`, `flask` | ||
|
|
||
| ## Templates | ||
|
|
||
| Templates are starting points — replace `<angle-bracket>` placeholders with detected values. | ||
|
|
||
| | Category | Directory | Files | | ||
| |----------|-----------|-------| | ||
| | Dockerfiles | `templates/dockerfiles/` | node, python, java, go, dotnet, rust (+ matching `.dockerignore` per language) | | ||
| | K8s manifests | `templates/k8s/` | namespace, deployment, service, ingress, gateway, httproute, hpa, pdb, serviceaccount, configmap, networkpolicy | | ||
| | CI/CD | `templates/github-actions/` | deploy.yml | | ||
| | Diagrams | `templates/mermaid/` | architecture-diagram, summary-dashboard | | ||
|
gambtho marked this conversation as resolved.
|
||
|
|
||
|
gambtho marked this conversation as resolved.
|
||
| ## Execution Model | ||
|
|
||
| - **Generate artifacts automatically** — Dockerfiles, manifests, workflows | ||
| - **Execute CLI commands only with confirmation** — `az`, `docker`, `kubectl`, `gh` | ||
| - **Detect before create** — check for existing Dockerfiles, manifests, CI/CD | ||
| - **Validate before replace** — improve what exists rather than overwriting | ||
|
|
||
| ## Key Principles | ||
|
|
||
| - ONE concept per turn — never overload the developer | ||
| - Sensible defaults — Ingress (Web App Routing), Workload Identity, 2 replicas | ||
| - Teach while fixing — when auto-fixing Safeguard violations, explain why | ||
238 changes: 238 additions & 0 deletions
238
plugin/skills/deploy-to-aks/knowledge-packs/frameworks/aspnet-core.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,238 @@ | ||
| # ASP.NET Core Knowledge Pack | ||
|
|
||
| > **Applies to:** Projects detected with `*.csproj` containing `Microsoft.NET.Sdk.Web` or referencing `Microsoft.AspNetCore.*` packages | ||
|
|
||
| --- | ||
|
|
||
| ## Dockerfile Patterns | ||
|
|
||
| ### Multi-stage build with project-file-first NuGet restore | ||
|
|
||
| Copying only `*.csproj` and restoring before copying source ensures NuGet restore is cached unless dependencies change: | ||
|
|
||
| ```dockerfile | ||
| # Build stage | ||
| FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build | ||
| WORKDIR /app | ||
| COPY *.csproj ./ | ||
| RUN dotnet restore | ||
| COPY . . | ||
| RUN dotnet publish -c Release -o /app/publish | ||
|
|
||
| # Runtime stage | ||
| FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS runtime | ||
| WORKDIR /app | ||
| ENV DOTNET_EnableDiagnostics=0 \ | ||
| DOTNET_RUNNING_IN_CONTAINER=true | ||
| COPY --from=build /app/publish . | ||
| USER app | ||
| EXPOSE 8080 | ||
| ENTRYPOINT ["dotnet", "<app-name>.dll"] | ||
| ``` | ||
|
|
||
| ### Key points | ||
|
|
||
| - **Base image:** `mcr.microsoft.com/dotnet/aspnet` is the official Microsoft runtime image — minimal and supported | ||
| - **Alpine variant** reduces image size by ~60% compared to the Debian-based tag | ||
| - **Project-file-first copy** (`COPY *.csproj`) means `dotnet restore` layer is cached until dependencies change | ||
| - **Non-root user** (`app`, uid 1654) is built into .NET 8+ images — no need to create one manually; satisfies DS004 | ||
| - **`DOTNET_EnableDiagnostics=0`** disables diagnostic pipes that require writable paths not available in read-only filesystems | ||
| - **`DOTNET_RUNNING_IN_CONTAINER=true`** signals the runtime to optimize for container environments (GC, thread pool) | ||
|
|
||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## Health Endpoints | ||
|
|
||
| ASP.NET Core has built-in health check middleware via `Microsoft.Extensions.Diagnostics.HealthChecks`: | ||
|
|
||
| | Endpoint | Purpose | Probe Type | | ||
| |----------|---------|-----------| | ||
| | `/healthz` | Overall health | `livenessProbe` | | ||
| | `/ready` | Dependency readiness | `readinessProbe` | | ||
|
|
||
| ### Required configuration | ||
|
|
||
| In `Program.cs`: | ||
|
|
||
| ```csharp | ||
| var builder = WebApplication.CreateBuilder(args); | ||
|
|
||
| // Register health checks | ||
| builder.Services.AddHealthChecks() | ||
| .AddNpgSql(builder.Configuration.GetConnectionString("DefaultConnection")!, | ||
| name: "postgresql", | ||
| tags: new[] { "ready" }); | ||
|
|
||
| var app = builder.Build(); | ||
|
|
||
| // Map health endpoints | ||
| app.MapHealthChecks("/healthz", new HealthCheckOptions | ||
| { | ||
| Predicate = _ => false // No dependency checks for liveness | ||
| }); | ||
|
|
||
| app.MapHealthChecks("/ready", new HealthCheckOptions | ||
| { | ||
| Predicate = check => check.Tags.Contains("ready") | ||
| }); | ||
| ``` | ||
|
|
||
| The `AspNetCore.HealthChecks.NpgSql` NuGet package provides the PostgreSQL health check. Install with: | ||
|
|
||
| ```bash | ||
| dotnet add package AspNetCore.HealthChecks.NpgSql | ||
| ``` | ||
|
|
||
| ### Probe configuration in Deployment manifest | ||
|
|
||
| ```yaml | ||
| livenessProbe: | ||
| httpGet: | ||
| path: /healthz | ||
| port: 8080 | ||
| initialDelaySeconds: 5 | ||
| periodSeconds: 15 | ||
| timeoutSeconds: 3 | ||
| failureThreshold: 3 | ||
| readinessProbe: | ||
| httpGet: | ||
| path: /ready | ||
| port: 8080 | ||
| initialDelaySeconds: 5 | ||
| periodSeconds: 10 | ||
| timeoutSeconds: 3 | ||
| failureThreshold: 3 | ||
| ``` | ||
|
|
||
| **Note:** ASP.NET Core apps start significantly faster than JVM-based frameworks — `initialDelaySeconds: 5` is typically sufficient. | ||
|
|
||
| --- | ||
|
|
||
| ## Database Profiles | ||
|
|
||
| ASP.NET Core uses configuration providers and Entity Framework Core for database access: | ||
|
|
||
| | Configuration Source | Activation | Typical Usage | | ||
| |---------------------|------------|---------------| | ||
| | `appsettings.json` | Default | Local dev with SQLite or LocalDB | | ||
| | `appsettings.Production.json` | `ASPNETCORE_ENVIRONMENT=Production` | Production connection strings | | ||
| | Environment variables | Always override file config | AKS deployments | | ||
|
|
||
| ### Environment variables for PostgreSQL on AKS | ||
|
|
||
| ```yaml | ||
| env: | ||
| - name: ASPNETCORE_ENVIRONMENT | ||
| value: Production | ||
| - name: ConnectionStrings__DefaultConnection | ||
| value: "Host={{PG_SERVER_NAME}}.postgres.database.azure.com;Database={{DB_NAME}};Username={{IDENTITY_NAME}};Ssl Mode=Require" | ||
| ``` | ||
|
|
||
| The double-underscore (`__`) in `ConnectionStrings__DefaultConnection` maps to the `:` separator in .NET configuration — `ConnectionStrings:DefaultConnection`. | ||
|
|
||
| ### Workload Identity with Azure.Identity | ||
|
|
||
| See `references/workload-identity.md` for connection patterns. Requires `Azure.Identity` and `Npgsql.EntityFrameworkCore.PostgreSQL` packages. | ||
|
|
||
| ### ConfigMap pattern | ||
|
|
||
| ```yaml | ||
| apiVersion: v1 | ||
| kind: ConfigMap | ||
| metadata: | ||
| name: {{APP_NAME}}-config | ||
| data: | ||
| ASPNETCORE_ENVIRONMENT: "Production" | ||
| ConnectionStrings__DefaultConnection: "Host={{PG_SERVER_NAME}}.postgres.database.azure.com;Database={{DB_NAME}};Ssl Mode=Require" | ||
| DOTNET_EnableDiagnostics: "0" | ||
| DOTNET_RUNNING_IN_CONTAINER: "true" | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Writable Paths (DS012 Compliance) | ||
|
|
||
| When `readOnlyRootFilesystem: true` is set, ASP.NET Core needs `/tmp` writable: | ||
|
|
||
| - **Data Protection keys** are written to a local directory by default for key persistence | ||
| - **Temporary files** from multipart uploads and response buffering use `/tmp` | ||
| - **Entity Framework** compiled models may write to temp directories | ||
|
|
||
| ### Required volume mount | ||
|
|
||
| ```yaml | ||
| volumes: | ||
| - name: tmp | ||
| emptyDir: {} | ||
| containers: | ||
| - name: app | ||
| volumeMounts: | ||
| - name: tmp | ||
| mountPath: /tmp | ||
| ``` | ||
|
|
||
| ### Data Protection key persistence | ||
|
|
||
| By default, ASP.NET Core Data Protection stores encryption keys in-memory when no persistent path is available, meaning keys are lost on pod restart. This breaks authentication cookies and anti-forgery tokens across pod restarts or in multi-replica deployments. | ||
|
|
||
| For production, persist keys to Azure Blob Storage: | ||
|
|
||
| ```csharp | ||
| builder.Services.AddDataProtection() | ||
| .PersistKeysToAzureBlobStorage("<connection-string>", "<container>", "<blob-name>") | ||
| .ProtectKeysWithAzureKeyVault(new Uri("<key-vault-uri>"), new DefaultAzureCredential()); | ||
| ``` | ||
|
|
||
| Alternatively, mount a PVC at a known path and configure: | ||
|
|
||
| ```csharp | ||
| builder.Services.AddDataProtection() | ||
| .PersistKeysToFileSystem(new DirectoryInfo("/keys")); | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Resource Sizing | ||
|
|
||
| ASP.NET Core on the .NET runtime is efficient but needs moderate memory for the CLR. | ||
|
|
||
| | Resource | Request | Limit | | ||
| |----------|---------|-------| | ||
| | CPU | 200m | 500m | | ||
| | Memory | 256Mi | 512Mi | | ||
|
|
||
| --- | ||
|
|
||
| ## Port Configuration | ||
|
|
||
| - **Default port:** 8080 (since .NET 8; previously 80 in .NET 7 and earlier) | ||
| - **Env var override:** `ASPNETCORE_URLS=http://+:8080` or `ASPNETCORE_HTTP_PORTS=8080` | ||
| - **Code override:** `builder.WebHost.UseUrls("http://+:8080")` in `Program.cs` | ||
|
|
||
| The port change from 80 to 8080 in .NET 8 aligns with non-root container best practices — port 80 requires elevated privileges. | ||
|
|
||
| --- | ||
|
|
||
| ## Build Commands | ||
|
|
||
| | Variant | Build Command | Output | | ||
| |---------|---------------|--------| | ||
| | Framework-dependent | `dotnet publish -c Release -o ./publish` | `./publish/<app-name>.dll` — requires .NET runtime on target | | ||
| | Self-contained | `dotnet publish -c Release --self-contained -o ./publish` | `./publish/<app-name>` — includes .NET runtime | | ||
| | Single-file | `dotnet publish -c Release --self-contained -p:PublishSingleFile=true -o ./publish` | Single executable binary | | ||
|
|
||
| The `-c Release` flag enables compiler optimizations and disables debug symbols — always use it for production builds. | ||
|
|
||
| --- | ||
|
|
||
| ## Common Issues on AKS | ||
|
|
||
| | Issue | Symptom | Fix | | ||
| |-------|---------|-----| | ||
| | Kestrel bound to port 80 | `CrashLoopBackOff` — permission denied binding to port 80 as non-root | Set `ASPNETCORE_HTTP_PORTS=8080` or upgrade to .NET 8+ which defaults to 8080 | | ||
| | Data Protection keys lost on restart | Users logged out after pod restart, anti-forgery token validation failures | Persist keys to Azure Blob Storage or a PVC — do not rely on in-memory default | | ||
| | EF Core migrations not applied | `NpgsqlException: relation "..." does not exist` | Run `dotnet ef database update` as an init container or at startup with `Database.Migrate()` | | ||
| | Image too large (>500MB) | Slow pulls, high ACR storage | Use self-contained + trimmed publish with `runtime-deps` Alpine base image | | ||
| | HTTPS redirect loop behind gateway | Infinite 307/308 redirects, `ERR_TOO_MANY_REDIRECTS` | Disable HTTPS redirection in `Program.cs` when behind a TLS-terminating gateway — configure `ForwardedHeaders` middleware instead | |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Markdown tables in this skill are written with leading double pipes (
|| ... |), which renders as an extra empty column and is inconsistent with the rest of the repo’s markdown table formatting (single leading|). Update these tables to standard markdown table syntax (single|column separators) for correct rendering and readability (this pattern appears multiple times throughout the skill docs).