diff --git a/aws-graviton-migration/POWER.md b/aws-graviton-migration/POWER.md index df854dd8..ae06838b 100644 --- a/aws-graviton-migration/POWER.md +++ b/aws-graviton-migration/POWER.md @@ -1,6 +1,6 @@ --- name: "graviton-migration-power" -displayName: "Graviton Migration Power" +displayName: "Plan and Migration to Graviton Power" description: "Analyzes source code to identify compatibilities with Graviton processors(Arm64 architecture). Generates reports with incompatibilities and provides suggestions for minimal required and recommended versions for language runtimes and dependency libraries." keywords: ["ec2", "graviton", "arm", "migration", "porting", "dependencies", "compatibilities", "arm64", "aarch64"] author: "AWS" @@ -10,110 +10,310 @@ author: "AWS" ## Overview -The Graviton Migration Power helps developers migrate workloads to AWS Graviton processors (Arm64 architecture). It analyzes source code for known code patterns and dependency libraries to identify compatibilities with Graviton processors, generates reports highlighting detected compatibility issues (manual review recommended), and provides actionable suggestions for minimal required and recommended versions for both language runtimes and dependency libraries. +Migrates codebases from x86 to AWS Graviton (Arm64). Scans Dockerfiles, dependency manifests, and source code to identify architecture-specific issues, then provides actionable fixes. -### What This Power Does +--- + +## Migration Workflow + +Copy this checklist and track progress: + +``` +Migration Progress: +- [ ] Step 1: Verify prerequisites (Docker running) +- [ ] Step 2: Check Dockerfiles for ARM64 base image compatibility +- [ ] Step 3: Verify packages installed in Dockerfiles +- [ ] Step 4: Check dependency manifests (requirements.txt, pom.xml, go.mod, package.json) +- [ ] Step 5: Determine language and run migrate_ease_scan +- [ ] Step 6: Generate migration report +- [ ] Step 7: Get user confirmation before making changes +- [ ] Step 8: Apply changes +``` + +### Step 1: Verify Prerequisites + +**CRITICAL**: Docker must be running. If not, stop and inform the user. + +```bash +docker ps +``` + +If this fails, do NOT proceed. Tell the user to start Docker Desktop. + +### Step 2: Check Dockerfiles for Base Image Compatibility + +For each Dockerfile found in the project: + +1. Extract the base image (e.g., `FROM eclipse-temurin:17-jdk-alpine`) +2. Run `check_image` or `skopeo` to verify ARM64 support +3. If image lacks ARM64 support → find a multi-arch alternative + +**Validation**: Confirm the replacement image supports BOTH `amd64` AND `arm64` before recommending it. + +### Step 3: Verify Packages Installed in Dockerfiles + +For each package installed via `apt-get`, `apk`, `yum`, or `pip` inside Dockerfiles: + +1. Send to `knowledge_base_search`: "Is [package] compatible with Arm architecture?" +2. If incompatible → suggest compatible version or alternative + +### Step 4: Check Dependency Manifests + +**Python projects** (requirements.txt, setup.py, pyproject.toml): +→ Check each package with `knowledge_base_search` +→ Focus on packages with native C extensions -The goal is to migrate a codebase from x86 to Arm. Use the MCP server tools to help you with this. Check for x86-specific dependencies (build flags, intrinsics, libraries, etc) and change them to Arm architecture equivalents, help identify compatibility issues and suggests optimizations for Arm architecture. Look at Dockerfiles, versionfiles, and other dependencies, compatibility, and optimize performance. +**Java projects** (pom.xml, build.gradle): +→ Check native dependencies (Netty classifiers, JNI libraries) +→ Look for architecture-specific classifiers like `linux-x86_64` -Steps to follow: -* Look in all Dockerfiles and use the check_image and/or skopeo tools to verify Arm compatibility, changing the base image if necessary. -* Look at the packages installed by the Dockerfile and send each package to the knowledge_base_search tool to check each package for Arm compatibility. If a package is not compatible, change it to a compatible version. When invoking the tool, explicitly ask "Is [package] compatible with Arm architecture?" where [package] is the name of the package. -* Look at the contents of any requirements.txt files line-by-line and send each line to the knowledge_base_search tool to check each package for Arm compatibility. If a package is not compatible, change it to a compatible version. -* Look at the codebase that you have access to, and determine what the language used is. -* Run the migrate_ease_scan tool on the codebase, using the appropriate language scanner based on what language the codebase uses. -* Provide an analysis report with complete dependency analysis, migration recommendations and optimizations for AWS Graviton processor -* Get a confirmation with user before proceeding with the code changes +**Go projects** (go.mod): +→ Check CGO dependencies +→ Look for platform-specific build tags + +**JavaScript/Node.js** (package.json): +→ Check native addons (node-gyp based packages) +→ Look for platform-specific optional dependencies + +**C/C++** (Makefile, CMakeLists.txt): +→ Check for x86 intrinsics, inline assembly +→ Look for architecture-specific compiler flags + +### Step 5: Run migrate_ease_scan + +Determine the primary language, then run the appropriate scanner: + +| Language | Scanner | +|----------|---------| +| Python | `python` | +| Java | `java` | +| Go | `go` | +| JavaScript | `js` | +| C/C++ | `cpp` | + +**Validation**: Review scan results for false positives before including in report. + +### Step 6: Generate Migration Report + +Use the report template below. Do NOT proceed to changes without user review. + +### Step 7: Get User Confirmation + +Present the report and explicitly ask: "Would you like me to apply these changes?" + +**Do NOT modify any files until the user confirms.** + +### Step 8: Apply Changes + +Apply changes one file at a time. After each file, briefly state what was changed. --- -## Onboarding +## Report Template -### Prerequisites +Use this structure for all migration reports: + +```markdown +# Graviton Migration Report + +## Summary +[One paragraph: project type, total issues found, severity breakdown (critical/warning/info)] -Before using the Graviton Migration Power, ensure the following are installed and running: +## Docker Images -#### Required Tools +| Image | Source File | ARM64 Support | Action Required | +|-------|------------|---------------|-----------------| +| `image:tag` | Dockerfile | ❌ / ✅ | Replace with `image:tag` / None | -1. **Docker Desktop**: Required for running the Arm MCP server and migration assessment tools - - Verify installation: `docker --version` - - Ensure Docker daemon is running: `docker ps` - - **CRITICAL**: If Docker is not installed or not running, DO NOT proceed with migration assessment +## Dependencies -2. **Git** (optional but recommended): For scanning remote repositories - - Verify installation: `git --version` +| Package | Version | File | ARM64 Compatible | Action | +|---------|---------|------|------------------|--------| +| `pkg` | x.y.z | requirements.txt | ❌ / ✅ | Upgrade to x.y.z / None | -### Step 1: Validate MCP Server Connection +## Source Code Scan Results -The power uses the Arm MCP server running in a Docker container. Test the connection: +**Scanner used**: [language] scanner +**Files scanned**: [count] +**Issues found**: [count] +[List each issue with file path, line number if available, and recommended fix] + +## Recommendations (Priority Order) + +1. [Critical] ... +2. [Critical] ... +3. [Warning] ... +4. [Info] ... + +## Build Instructions + +### Multi-Architecture Docker Build ```bash -# The MCP server should auto-start when you use the power -# If you encounter issues, verify Docker is running -docker ps +docker buildx create --name multiarch --use --bootstrap +docker buildx build -t your-registry/app:latest \ + --platform linux/amd64,linux/arm64 \ + --push . ``` +``` + +--- + +## Examples + +### Example: Checking a Docker Image + +**Input**: Dockerfile contains `FROM eclipse-temurin:17-jdk-alpine` + +**Steps**: +1. Run `check_image` on `eclipse-temurin:17-jdk-alpine` +2. Result: x86_64 only ❌ +3. Run `check_image` on `eclipse-temurin:17-jdk` +4. Result: supports amd64, arm64 ✅ + +**Report entry**: +| `eclipse-temurin:17-jdk-alpine` | Dockerfile | ❌ | Replace with `eclipse-temurin:17-jdk` | + +### Example: Checking a Python Dependency + +**Input**: requirements.txt contains `grpcio==1.48.0` + +**Steps**: +1. Query `knowledge_base_search`: "Is grpcio compatible with Arm architecture?" +2. Result: grpcio 1.48.0+ provides ARM64 wheels ✅ -### Step 2: Understand Available Tools +**Report entry**: +| `grpcio` | 1.48.0 | requirements.txt | ✅ | None | -This power provides access to several specialized tools: +### Example: Netty Native Transport (Java) -- **migrate-ease scan**: Scans codebases for Arm compatibility issues (C++, Python, Go, JS, Java) -- **skopeo**: Inspects container images remotely for architecture support -- **knowledge base search**: Searches Arm documentation for migration guidance -- **check image**: Quick Docker image architecture verification -- **mca (Machine Code Analyzer)**: Analyzes assembly code performance predictions +**Input**: pom.xml contains: +```xml + + io.netty + netty-transport-native-epoll + linux-x86_64 + +``` + +**Steps**: +1. Identify x86_64-specific classifier +2. Query `knowledge_base_search`: "Is netty-transport-native-epoll compatible with Arm architecture?" +3. Result: Needs additional `linux-aarch_64` classifier + +**Fix**: Add alongside existing: +```xml + + io.netty + netty-transport-native-epoll + linux-aarch_64 + +``` --- -## Steering Files +## Degrees of Freedom -This power has the following steering files for detailed workflow guidance: +**Low freedom (follow exactly)**: +- The checklist order (Steps 1-8) +- Never modify files without user confirmation (Step 7) +- Always verify Docker is running before proceeding +- Use the report template structure -- **karpenter.md** — Guides detection and migration of Karpenter configurations (NodePool, EC2NodeClass) to use Graviton ARM64 instances. Covers gradual rollout with taints/tolerations, instance family mappings, and post-migration cleanup. +**High freedom (adapt to context)**: +- How to phrase knowledge_base_search queries +- Which alternative images to suggest (pick best multi-arch option) +- How to organize findings within report sections +- Level of detail in recommendations based on project complexity --- -## License & Legal +## Troubleshooting -### Power License +### Docker Not Running + +**Symptom**: MCP server fails to start, tools return connection errors +**Fix**: Tell user to start Docker Desktop and retry + +### Scan Timeout + +**Symptom**: `migrate_ease_scan` hangs or returns timeout error +**Cause**: Large codebase or slow Docker volume mount +**Fix**: Try scanning a subdirectory, or suggest user increase Docker resource limits + +### Image Not Found + +**Symptom**: `check_image` or `skopeo` returns "not found" +**Cause**: Private registry requiring authentication, or typo in image name +**Fix**: Verify image name is correct. For private images, inform user that manual verification is needed. -This power is provided by AWS and is subject to the AWS Customer Agreement and applicable AWS service terms. +### Knowledge Base Returns No Results -### MCP Server Licenses +**Symptom**: `knowledge_base_search` returns empty or irrelevant results +**Fix**: Rephrase query. Try: "Is [package] [version] compatible with ARM64 aarch64?" +If still no results, note the package as "unknown compatibility - manual verification recommended" -This power uses the following MCP server and tools: +### Scan Reports False Positives -- **arm-mcp** (`armswdev/arm-mcp:latest`): Docker container providing Arm migration tools - - Distributed via Docker Hub by Arm - - Contains multiple open-source tools with their respective licenses - - For complete license information, see: https://github.com/arm/mcp/blob/main/LICENSE +**Symptom**: `migrate_ease_scan` flags code that is actually architecture-neutral +**Fix**: Review each finding. If the flagged code doesn't use architecture-specific features (intrinsics, inline assembly, platform-specific paths), mark as false positive in the report. +--- + +## Onboarding + +### Prerequisites + +1. **Docker Desktop**: Required for the Arm MCP server + - Verify: `docker --version` + - Confirm running: `docker ps` + +2. **Git** (optional): For scanning remote repositories + - Verify: `git --version` + +### Available Tools + +| Tool | Purpose | +|------|---------| +| `migrate_ease_scan` | Scan codebase for Arm compatibility issues | +| `skopeo` | Inspect container images for architecture support | +| `knowledge_base_search` | Search Arm documentation for package compatibility | +| `check_image` | Quick Docker image architecture check | +| `mca` | Machine Code Analyzer for assembly performance | + +--- -### Third-Party Dependencies +## Steering Files + +- **karpenter.md** — Karpenter NodePool/EC2NodeClass migration to Graviton instances +- **build-pipelines.md** — CI/CD pipeline adjustments for multi-arch builds + +--- -This power requires Docker to run the MCP server container. Docker is subject to its own licensing terms. See: https://www.docker.com/legal/docker-subscription-service-agreement/ +## License & Legal -### Usage Terms +### Power License -By using this power, you acknowledge that: -- You are responsible for compliance with all applicable licenses -- Code analysis is performed using open-source tools within a Docker container -- You should review and comply with individual tool licenses for production use +Provided by AWS, subject to the AWS Customer Agreement and applicable service terms. + +### MCP Server + +- **arm-mcp** (`armlimited/arm-mcp:latest`): Docker container with Arm migration tools + - License: https://github.com/arm/mcp/blob/main/LICENSE + +### Third-Party + +- Docker: https://www.docker.com/legal/docker-subscription-service-agreement/ --- ## Additional Resources - AWS Graviton Technical Guide: https://github.com/aws/aws-graviton-getting-started -- Arm Architecture Reference: Available through knowledge base search -- Migration Tools Documentation: Included in MCP server responses +- Arm Architecture Reference: Available via `knowledge_base_search` --- -## Power Metadata - -**Version**: 1.1 -**Author**: AWS -**Supported Languages**: C++, Python, Go, JavaScript, Java -**Container Runtime**: Docker required -**MCP Server**: arm-mcp (Docker-based). License information, see: https://github.com/arm/mcp/blob/main/LICENSE +**Supported Languages**: C++, Python, Go, JavaScript, Java +**Container Runtime**: Docker required +**MCP Server**: arm-mcp (Docker-based) diff --git a/aws-graviton-migration/steering/build-pipelines.md b/aws-graviton-migration/steering/build-pipelines.md new file mode 100644 index 00000000..ae496026 --- /dev/null +++ b/aws-graviton-migration/steering/build-pipelines.md @@ -0,0 +1,420 @@ +# Build Pipeline Migration to Graviton (ARM64) + +## Contents +- [Workflow Checklist](#workflow-checklist) +- [Decision Tree: Which Sections Apply](#decision-tree-which-sections-apply) +- [Detection](#detection) +- [Migration Steps](#migration-steps) + - [1. Multi-Architecture Container Builds](#1-multi-architecture-container-builds) + - [2. Dockerfile Adjustments](#2-dockerfile-adjustments) + - [3. Build Runner Configuration](#3-build-runner-configuration) + - [4. Dual-Architecture Testing](#4-dual-architecture-testing) + - [5. Native Compilation Flags](#5-native-compilation-flags) + - [6. Post-Migration Cleanup](#6-post-migration-cleanup) +- [Example: Full Before → After](#example-full-before--after) +- [Common Pitfalls](#common-pitfalls) +- [Troubleshooting](#troubleshooting) +- [References](#references) + +--- + +## Workflow Checklist + +Copy and track progress: + +``` +Build Pipeline Migration Progress: +- [ ] Step 1: Detect CI/CD configs and architecture-specific settings +- [ ] Step 2: Verify base images are multi-arch +- [ ] Step 3: Update Docker builds to produce multi-arch images +- [ ] Step 4: Update Dockerfile for architecture-agnostic deps +- [ ] Step 5: Configure ARM64 build runner (if needed) +- [ ] Step 6: Test build output on ARM64 +- [ ] Step 7: Update build matrix for dual-arch testing (optional) +- [ ] Step 8: Post-migration cleanup +``` + +**Steps 1-4 are mandatory.** Steps 5-7 depend on your CI system and project type. Step 8 happens after full migration. + +--- + +## Decision Tree: Which Sections Apply + +Not every section applies to every project. Use this to focus: + +**What CI system?** +- GitHub Actions → [Multi-Arch Builds (GitHub)](#github-actions) + [Runner Config (GitHub)](#github-actions-self-hosted-arm64-runner) +- AWS CodeBuild → [Multi-Arch Builds (CodeBuild)](#aws-codebuild) +- GitLab CI → [Runner Config (GitLab)](#gitlab-ci-arm64-runner-tag) +- Jenkins → [Runner Config (Jenkins)](#jenkins-arm64-agent-label) +- Other / shell scripts → [Multi-Arch Builds (CLI)](#docker-buildx-with-remote-native-builders) + +**Does the project compile native code (C/C++, Rust, Go with CGO)?** +- Yes → Read [Native Compilation Flags](#5-native-compilation-flags) +- No → Skip that section + +**Does the Dockerfile download architecture-specific binaries?** +- Yes → Read [Dockerfile Adjustments](#2-dockerfile-adjustments) +- No → Skip that section + +--- + +## Detection + +Scan the workspace for: + +- CI/CD config files: `.github/workflows/*.yml`, `.gitlab-ci.yml`, `Jenkinsfile`, `buildspec.yml`, `.circleci/config.yml`, `bitbucket-pipelines.yml` +- Build scripts referencing `x86_64`, `amd64`, `linux/amd64` +- Docker build commands without `--platform` or with hardcoded `linux/amd64` +- Container image tags with architecture suffixes (`:latest-amd64`) +- Build matrices that only include x86 targets +- Terraform/CloudFormation provisioning build infra on x86 instance types +- Makefiles or shell scripts with architecture-conditional logic + +--- + +## Migration Steps + +### 1. Multi-Architecture Container Builds + +Produce images that work on both x86 and Graviton. Always use native ARM64 build runners rather than QEMU emulation (5-10x slower, unreliable builds). + +#### GitHub Actions + +Separate native runners per architecture, merged into one manifest: + +```yaml +jobs: + build-amd64: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-buildx-action@v3 + - uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64 + push: true + tags: ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ env.TAG }}-amd64 + + build-arm64: + runs-on: [self-hosted, linux, arm64] + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-buildx-action@v3 + - uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/arm64 + push: true + tags: ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ env.TAG }}-arm64 + + manifest: + needs: [build-amd64, build-arm64] + runs-on: ubuntu-latest + steps: + - uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + - run: | + docker manifest create ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ env.TAG }} \ + ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ env.TAG }}-amd64 \ + ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ env.TAG }}-arm64 + docker manifest push ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ env.TAG }} +``` + +#### Docker Buildx with Remote Native Builders + +For CLI, Jenkinsfile, or shell scripts: + +```bash +docker buildx create --name multiarch-builder --platform linux/amd64 --node builder-amd64 unix:///var/run/docker.sock +docker buildx create --name multiarch-builder --append --platform linux/arm64 --node builder-arm64 ssh://user@arm64-host +docker buildx use multiarch-builder + +docker buildx build \ + --platform linux/amd64,linux/arm64 \ + --tag ${REGISTRY}/${IMAGE}:${TAG} \ + --push . +``` + +#### AWS CodeBuild + +Use a Graviton-based environment for native ARM64 builds: + +```json +{ + "environment": { + "type": "ARM_CONTAINER", + "image": "aws/codebuild/amazonlinux2-aarch64-standard:3.0", + "computeType": "BUILD_GENERAL1_LARGE" + } +} +``` + +For multi-arch: use separate CodeBuild projects (x86 + Graviton), then create a manifest in a third step. + +**Validation gate**: After building, verify the manifest contains both architectures: + +```bash +docker manifest inspect ${REGISTRY}/${IMAGE}:${TAG} | grep architecture +# Should show both "amd64" and "arm64" +``` + +### 2. Dockerfile Adjustments + +#### Use Multi-Arch Base Images + +```dockerfile +# Good: official images are multi-arch +FROM python:3.12-slim +FROM node:20-alpine +FROM amazoncorretto:21 + +# Bad: architecture-specific tags +# FROM amd64/python:3.12-slim +``` + +**Validation gate**: Use `check_image` or `skopeo` to verify the base image supports `linux/arm64`. + +#### Handle Architecture-Specific Binaries + +Use Docker's built-in `TARGETARCH` variable: + +```dockerfile +ARG TARGETARCH +RUN if [ "$TARGETARCH" = "arm64" ]; then \ + curl -L https://example.com/tool-arm64.tar.gz -o tool.tar.gz; \ + else \ + curl -L https://example.com/tool-amd64.tar.gz -o tool.tar.gz; \ + fi && \ + tar -xzf tool.tar.gz -C /usr/local/bin/ +``` + +`TARGETARCH` is automatically set by Docker Buildx during multi-platform builds. + +### 3. Build Runner Configuration + +Only needed if your CI uses self-hosted runners and you need native ARM64 builds. + +#### GitHub Actions (Self-Hosted ARM64 Runner) + +```yaml +jobs: + build-arm64: + runs-on: [self-hosted, linux, arm64] + steps: + - uses: actions/checkout@v4 + - run: make build +``` + +#### GitLab CI (ARM64 Runner Tag) + +```yaml +build: + tags: + - arm64 + script: + - make build +``` + +#### Jenkins (ARM64 Agent Label) + +```groovy +pipeline { + agent { label 'arm64' } + stages { + stage('Build') { + steps { + sh 'make build' + } + } + } +} +``` + +### 4. Dual-Architecture Testing + +Run tests on both architectures during migration to catch issues early. + +```yaml +# GitHub Actions matrix example +jobs: + test: + strategy: + matrix: + arch: [amd64, arm64] + include: + - arch: amd64 + runner: ubuntu-latest + - arch: arm64 + runner: [self-hosted, linux, arm64] + runs-on: ${{ matrix.runner }} + steps: + - uses: actions/checkout@v4 + - run: make test +``` + +**Validation gate**: Both architecture builds must pass before merging. + +### 5. Native Compilation Flags + +**Only applies if the project compiles native code (C/C++, Rust, Go with CGO).** + +Check for: +- Hardcoded `-march=x86-64` or `-mtune` flags +- x86 SIMD intrinsics (`SSE`, `AVX`) → need ARM equivalents (`NEON`, `SVE`) +- Assembly files (`.s`, `.asm`) with x86 instructions +- `GOARCH=amd64` hardcoded in build scripts + +#### Go + +```bash +# On ARM64 runner, GOARCH defaults to arm64 +go build -o app + +# For cross-build (prefer native builds instead): +GOOS=linux GOARCH=arm64 go build -o app-arm64 +``` + +#### Rust + +```bash +# On ARM64 runner, default target is aarch64 +cargo build --release +``` + +### 6. Post-Migration Cleanup + +After all workloads run on Graviton: + +- Remove `linux/amd64` from build targets if x86 no longer needed +- Remove architecture-conditional logic from Dockerfiles +- Switch build infrastructure to Graviton instances for cost savings +- Consolidate per-architecture jobs into single ARM64-native jobs + +--- + +## Example: Full Before → After + +### Before: GitHub Actions (x86-only) + +```yaml +name: Build and Push +on: push + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: docker build -t myregistry/myapp:latest . + - run: docker push myregistry/myapp:latest +``` + +### After: GitHub Actions (multi-arch with native runners) + +```yaml +name: Build and Push (Multi-Arch) +on: push + +jobs: + build-amd64: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-buildx-action@v3 + - uses: docker/login-action@v3 + with: + registry: myregistry + - uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64 + push: true + tags: myregistry/myapp:latest-amd64 + + build-arm64: + runs-on: [self-hosted, linux, arm64] + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-buildx-action@v3 + - uses: docker/login-action@v3 + with: + registry: myregistry + - uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/arm64 + push: true + tags: myregistry/myapp:latest-arm64 + + manifest: + needs: [build-amd64, build-arm64] + runs-on: ubuntu-latest + steps: + - uses: docker/login-action@v3 + with: + registry: myregistry + - run: | + docker manifest create myregistry/myapp:latest \ + myregistry/myapp:latest-amd64 \ + myregistry/myapp:latest-arm64 + docker manifest push myregistry/myapp:latest +``` + +--- + +## Common Pitfalls + +| Issue | Symptom | Fix | +|-------|---------|-----| +| Missing multi-arch manifest | `exec format error` at runtime | Build per-arch on native runners, create manifest | +| x86-only binary downloads in Dockerfile | Build fails on ARM64 | Use `TARGETARCH` to select correct binary | +| Hardcoded `amd64` in image tags | Wrong image pulled on Graviton nodes | Use multi-arch tags or manifest lists | +| Native extensions not compiled for ARM64 | Import errors or segfaults | Rebuild native deps on ARM64 build runner | +| Shared build cache across architectures | Corrupt or wrong-arch artifacts | Separate build caches by architecture | +| QEMU emulation used instead of native runners | Builds 5-10x slower, intermittent failures | Use native ARM64 runners or remote builders | + +--- + +## Troubleshooting + +### Build Fails with "exec format error" + +**Symptom**: Docker build step fails immediately with `exec format error` +**Cause**: Trying to run x86 binary on ARM64 runner (or vice versa) without emulation +**Fix**: Ensure you're building with `--platform` matching the runner architecture, or use native runners per arch. + +### ARM64 Runner Not Available + +**Symptom**: Job stays queued with "waiting for runner" +**Cause**: No self-hosted ARM64 runner registered, or runner is offline +**Fix**: +1. For GitHub Actions: Set up a self-hosted runner on a Graviton instance +2. For CodeBuild: Use `ARM_CONTAINER` environment type +3. For GitLab: Register a runner with the `arm64` tag on a Graviton instance + +### Multi-Arch Manifest Push Fails + +**Symptom**: `docker manifest push` returns authentication or "not found" error +**Cause**: Per-arch images not pushed yet, or registry login expired between jobs +**Fix**: Ensure per-arch images are pushed before the manifest job runs. Re-authenticate in the manifest job. + +### Native Dependency Compilation Fails on ARM64 + +**Symptom**: `pip install` or `npm install` fails with compilation errors on ARM64 runner +**Cause**: Package has C extensions that need ARM64-specific build tools +**Fix**: +1. Install build essentials: `apt-get install build-essential` +2. Check if a pre-built ARM64 wheel/binary exists (newer version may have it) +3. If no ARM64 support exists, flag as migration blocker + +--- + +## References + +- [AWS Graviton Getting Started — CI/CD](https://github.com/aws/aws-graviton-getting-started/blob/main/containers.md) +- [Docker Multi-Platform Builds](https://docs.docker.com/build/building/multi-platform/) +- [AWS CodeBuild ARM Support](https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-compute-types.html) +- [GitHub Actions Runner Images](https://github.com/actions/runner-images) diff --git a/aws-graviton-migration/steering/karpenter.md b/aws-graviton-migration/steering/karpenter.md index 667151b9..1c54f6f2 100644 --- a/aws-graviton-migration/steering/karpenter.md +++ b/aws-graviton-migration/steering/karpenter.md @@ -1,27 +1,70 @@ # Karpenter Configuration Migration to Graviton (ARM64) -This steering file guides the detection and migration of Karpenter configurations to use AWS Graviton (ARM64) instances. +## Contents +- [Workflow Checklist](#workflow-checklist) +- [Detection](#detection) +- [Migration Strategy](#migration-strategy) +- [Instance Family Mappings](#instance-family-mappings) +- [Example: Full Before → After](#example-full-before--after) +- [Key Checks](#key-checks) +- [Troubleshooting](#troubleshooting) +- [References](#references) + +--- + +## Workflow Checklist + +Copy and track progress: + +``` +Karpenter Migration Progress: +- [ ] Step 1: Detect Karpenter configs in workspace +- [ ] Step 2: Verify container images support ARM64 +- [ ] Step 3: Create dedicated Graviton NodePool (with taint) +- [ ] Step 4: Add tolerations to target workloads +- [ ] Step 5: Validate pods schedule on Graviton nodes +- [ ] Step 6: Pin validated workloads to ARM64 (nodeSelector) +- [ ] Step 7: Post-migration cleanup (remove taints, delete old NodePool) +``` + +**Steps 1-4 are mandatory in order.** Steps 5-7 are iterative — repeat per workload until all are migrated. + +--- ## Detection -When analyzing a workspace for Karpenter configurations, look for: +Scan the workspace for these indicators: - YAML files containing `apiVersion: karpenter.sh/v1` or `karpenter.sh/v1beta1` - Resources of `kind: NodePool` and `kind: EC2NodeClass` -- Existing `kubernetes.io/arch` requirements set to `amd64` only -- Instance family requirements using x86-only families (e.g., `m5`, `c5`, `r5`) -- Any `nodeSelector` or `tolerations` in workload manifests referencing architecture -- Helm values files with architecture or instance-type settings for Karpenter +- `kubernetes.io/arch` requirements set to `amd64` only +- Instance family requirements using x86-only families (`m5`, `c5`, `r5`, `t3`) +- Workload manifests with `nodeSelector` or `tolerations` referencing architecture +- Helm values files with instance-type or architecture settings + +--- ## Migration Strategy -Follow a gradual rollout approach: +### Step 1: Detect Existing Configuration + +Identify all NodePool resources. Note current instance families and architecture constraints. + +### Step 2: Verify Image Compatibility -### 1. Create a Dedicated Graviton NodePool +**Before creating the Graviton NodePool**, verify ALL container images support `linux/arm64`: +- Application images +- Sidecar containers (service mesh proxies, logging agents) +- DaemonSets +- Init containers -Create a separate NodePool for Graviton nodes rather than modifying the existing x86 NodePool. This gives independent control over instance selection and rollout pace. +Use `check_image` or `skopeo` tools to verify each image. -Example Graviton NodePool: +**Validation gate**: Do NOT proceed to Step 3 until all images are confirmed ARM64-compatible. If any image lacks support, flag it in the migration report and resolve first. + +### Step 3: Create a Dedicated Graviton NodePool + +Create a **separate** NodePool with a taint. This allows gradual rollout without disrupting existing workloads. ```yaml apiVersion: karpenter.sh/v1 @@ -55,9 +98,11 @@ spec: values: ["4"] ``` -### 2. Add Tolerations to Workloads +**Validation gate**: Run `kubectl get nodepool graviton` to confirm it's created. Check Karpenter controller logs for errors. + +### Step 4: Add Tolerations to Workloads -For each workload being migrated, add a toleration for the Graviton taint: +For each workload being migrated, add the toleration: ```yaml spec: @@ -66,9 +111,21 @@ spec: operator: Exists ``` -### 3. Force Scheduling on Graviton (After Validation) +### Step 5: Validate Scheduling -Once a workload is validated on ARM64, pin it to Graviton nodes: +**Validation gate**: Confirm pods are running on Graviton nodes before proceeding. + +```bash +# Check pod is on an ARM64 node +kubectl get pod -o jsonpath='{.spec.nodeName}' | xargs kubectl get node -o jsonpath='{.metadata.labels.kubernetes\.io/arch}' +# Expected: arm64 +``` + +If pods stay Pending or CrashLoop, see [Troubleshooting](#troubleshooting). + +### Step 6: Pin Validated Workloads to Graviton + +Once a workload is validated on ARM64, add `nodeSelector` to lock it in: ```yaml spec: @@ -79,31 +136,174 @@ spec: operator: Exists ``` -### 4. Post-Migration Cleanup +### Step 7: Post-Migration Cleanup + +After ALL workloads are migrated and stable: -After all workloads are migrated: +1. Remove the `graviton-migration` taint from the Graviton NodePool +2. Remove tolerations and nodeSelectors from workload specs +3. Delete the old x86-only NodePool -- Remove the `graviton-migration` taint from the Graviton NodePool -- Remove tolerations and nodeSelectors from workload specs -- Delete the old x86-only NodePool +--- -## Common x86 to Graviton Instance Family Mappings +## Instance Family Mappings | x86 Family | Graviton Equivalent | Notes | |------------|-------------------|-------| -| m5, m6i | m6g, m7g | General purpose | -| c5, c6i | c6g, c7g | Compute optimized | -| r5, r6i | r6g, r7g | Memory optimized | -| t3 | t4g | Burstable | +| m5, m6i | m6g, m7g | General purpose | +| c5, c6i | c6g, c7g | Compute optimized | +| r5, r6i | r6g, r7g | Memory optimized | +| t3 | t4g | Burstable | + +--- + +## Example: Full Before → After + +### Before (x86-only NodePool) + +```yaml +apiVersion: karpenter.sh/v1 +kind: NodePool +metadata: + name: default +spec: + template: + spec: + nodeClassRef: + group: karpenter.k8s.aws + kind: EC2NodeClass + name: default + requirements: + - key: karpenter.sh/capacity-type + operator: In + values: ["on-demand"] + - key: kubernetes.io/arch + operator: In + values: ["amd64"] + - key: node.kubernetes.io/instance-type + operator: In + values: ["m5.large", "m5.xlarge", "c5.large"] +``` + +### After (Graviton NodePool added alongside) + +```yaml +# Existing x86 NodePool (keep during migration) +apiVersion: karpenter.sh/v1 +kind: NodePool +metadata: + name: default +spec: + template: + spec: + nodeClassRef: + group: karpenter.k8s.aws + kind: EC2NodeClass + name: default + requirements: + - key: karpenter.sh/capacity-type + operator: In + values: ["on-demand"] + - key: kubernetes.io/arch + operator: In + values: ["amd64"] + - key: node.kubernetes.io/instance-type + operator: In + values: ["m5.large", "m5.xlarge", "c5.large"] +--- +# New Graviton NodePool +apiVersion: karpenter.sh/v1 +kind: NodePool +metadata: + name: graviton +spec: + disruption: + consolidationPolicy: WhenEmptyOrUnderutilized + consolidateAfter: 1m + template: + spec: + expireAfter: 720h + nodeClassRef: + group: karpenter.k8s.aws + kind: EC2NodeClass + name: default + taints: + - key: graviton-migration + effect: NoSchedule + requirements: + - key: karpenter.sh/capacity-type + operator: In + values: ["on-demand", "spot"] + - key: kubernetes.io/arch + operator: In + values: ["arm64"] + - key: node.kubernetes.io/instance-type + operator: In + values: ["m7g.large", "m7g.xlarge", "c7g.large"] +``` + +### Workload Deployment (migrated) + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-app +spec: + template: + spec: + nodeSelector: + kubernetes.io/arch: arm64 + tolerations: + - key: graviton-migration + operator: Exists + containers: + - name: app + image: my-registry/my-app:latest # Must support linux/arm64 +``` + +--- ## Key Checks -- Verify all container images support `linux/arm64` (multi-arch or ARM64-specific) -- Check sidecar containers (service mesh proxies, logging agents) for ARM64 support +- Verify ALL container images support `linux/arm64` before creating the NodePool +- Check sidecar containers (service mesh proxies, logging agents) - Check DaemonSets for ARM64 compatibility -- Validate any init containers also have ARM64 images -- Use the `check_image` or `skopeo` tools from the Graviton Migration Power to verify image architecture support -- Run `migrate_ease_scan` on application source code to detect architecture-specific code +- Validate init containers have ARM64 images +- Use `check_image` or `skopeo` tools to verify image architecture support +- Run `migrate_ease_scan` on application source code if architecture-specific code is suspected + +--- + +## Troubleshooting + +### Pods Stay Pending After Adding Toleration + +**Symptom**: Pod remains in `Pending` state, no node provisioned. + +**Causes & Fixes**: +1. **NodePool not creating nodes** — Check Karpenter controller logs: `kubectl logs -n karpenter -l app.kubernetes.io/name=karpenter` +2. **Instance type unavailable** — Widen the instance family list or add more AZs +3. **Missing toleration** — Verify the pod spec has the exact taint key match + +### CrashLoopBackOff on Graviton Nodes + +**Symptom**: Pod starts on ARM64 node but crashes immediately. + +**Causes & Fixes**: +1. **Image is x86-only** — `exec format error` in logs. Rebuild image for ARM64 or use multi-arch image. +2. **Native dependency missing ARM64 build** — Check container logs for missing `.so` files. Rebuild the dependency on ARM64. +3. **Architecture-specific code path** — Run `migrate_ease_scan` on the source code. + +### Wrong Nodes Selected + +**Symptom**: Pods land on x86 nodes instead of Graviton. + +**Causes & Fixes**: +1. **Toleration added but no nodeSelector** — Without `nodeSelector`, the scheduler may still pick x86 nodes. Add `nodeSelector: kubernetes.io/arch: arm64` to force ARM64. +2. **Karpenter consolidation moved pods** — Check if consolidation policy is moving pods back. Ensure the x86 NodePool doesn't accept the workload. + +--- ## References