Skip to content

Commit 46eb3eb

Browse files
first pass
1 parent 5427ed4 commit 46eb3eb

7 files changed

Lines changed: 97 additions & 55 deletions

File tree

content/learning-paths/servers-and-cloud-computing/dotnet-migration-nopcommerce/2-environment-setup.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
---
2-
title: Environment setup
2+
title: Set up an Arm-based environment
33
weight: 2
44

55
### FIXED, DO NOT MODIFY
66
layout: learningpathall
77
---
88

9-
# Environment setup
9+
## Set up a Microsoft Azure virtual machine powered by Cobalt 100
1010

1111
Set up an Arm Cobalt environment first, then keep your toolchain and runtime configuration stable across all test runs. This creates a reliable Arm baseline for migration and tuning work.
1212

13-
Start with the official [Azure Cobalt setup guide](https://learn.arm.com/learning-paths/servers-and-cloud-computing/cobalt/) and complete VM provisioning there first.
13+
Start with the [Azure Cobalt Learning Path](https://learn.arm.com/learning-paths/servers-and-cloud-computing/cobalt/) and complete VM provisioning.
1414

1515
This path was validated with Ubuntu 24.04 LTS on Azure Cobalt in `westus2` (example VM size: `Standard_D2ps_v6`, 2 vCPUs).
1616

17-
## Install tools on Cobalt VM
17+
### Install tools on Cobalt VM
1818

1919
Install the toolchain on the Cobalt VM before building and testing. The commands install the Linux packages used throughout this Learning Path: source control, HTTP validation, JSON inspection, Python for the endpoint tester, package archive inspection, Docker, and the .NET 9 SDK from the Ubuntu backports feed.
2020

@@ -46,11 +46,11 @@ aarch64
4646
Docker version ...
4747
```
4848

49-
If you are upgrading from an older .NET version before migrating to Cobalt, you can use [GitHub Copilot modernization for .NET](https://learn.microsoft.com/dotnet/core/porting/github-copilot-app-modernization/overview) to assess the project and guide the upgrade. In Visual Studio Code, open Copilot Chat and use `@modernize-dotnet`; in Visual Studio, use the Modernize action from Solution Explorer.
49+
If you're upgrading from an older .NET version before migrating to Cobalt, use [GitHub Copilot modernization for .NET](https://learn.microsoft.com/dotnet/core/porting/github-copilot-app-modernization/overview) to assess the project and guide the upgrade. In Visual Studio Code, open Copilot Chat and use `@modernize-dotnet`; in Visual Studio, use the Modernize action from Solution Explorer.
5050

51-
## PostgreSQL prerequisite for nopCommerce install
51+
#### Enable citext to satisfy PostgreSQL prerequisite
5252

53-
nopCommerce defaults to SQL Server, but this learning path uses PostgreSQL for Arm validation. For PostgreSQL installs, `citext` is required before migration/installation. Without it, installer migrations fail with `type "citext" does not exist` (captured in local test artifacts).
53+
nopCommerce defaults to SQL Server, but you'll use PostgreSQL for Arm validation. For PostgreSQL installs, you need `citext` before migration or installation. Without it, installer migrations fail with `type "citext" does not exist` (captured in local test artifacts).
5454

5555
Create PostgreSQL and enable `citext` before running the installer. The database runs in Docker on the Cobalt VM so the nopCommerce app can connect to `127.0.0.1:5432` during the local validation phase. Replace the password value before running the command.
5656

@@ -70,3 +70,9 @@ docker exec nop-postgres psql -U nop -d nopcommerce \
7070
```
7171

7272
The `docker exec` command should print `CREATE EXTENSION`. If it prints `NOTICE: extension "citext" already exists, skipping`, the database is already prepared and you can continue.
73+
74+
## What you've accomplished and what's next
75+
76+
You've set up an Arm-based environment by creating a Microsoft Azure VM powered by Cobalt 100 and installing the toolchain necessary for migration.
77+
78+
Next, you'll create a reproducable baseline to use for benchmarking.

content/learning-paths/servers-and-cloud-computing/dotnet-migration-nopcommerce/3-checkout-build-baseline.md

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
---
2-
title: Build and baseline
2+
title: Create an Arm baseline before optimization
33
weight: 3
44

55
### FIXED, DO NOT MODIFY
66
layout: learningpathall
77
---
88

9-
# Build and baseline
9+
## Build and baseline
1010

1111
Create a reproducible Arm baseline before optimization work. This page pins source, verifies a clean build, and captures a representative endpoint baseline so later changes can be measured against a known control.
1212

13-
## Clone and pin the source
13+
### Clone and pin the source
1414

15-
Pinning the exact tag and commit avoids silent drift in dependencies and behavior. Run these commands on the Cobalt VM. The later commands assume you are in the `nopCommerce` repository root unless a section says otherwise.
15+
Pinning the exact tag and commit avoids silent drift in dependencies and behavior. Run the following commands on the Cobalt virtual machine (VM):
1616

1717
```bash
1818
git clone https://github.com/nopSolutions/nopCommerce.git
@@ -21,16 +21,17 @@ git fetch --tags --prune
2121
git checkout release-4.90.3
2222
git rev-parse --short=10 HEAD # expect 9beda11c42
2323
```
24+
The later commands assume you're in the `nopCommerce` repository root unless stated otherwise.
2425

2526
If you open a new terminal later, return to the same repository root before running commands:
2627

2728
```bash
2829
cd ~/nopCommerce # replace with your clone path if different
2930
```
3031

31-
## Restore and build on Arm
32+
### Restore and build on Arm
3233

33-
Run the restore and build on the Arm-based VM to establish a native Arm baseline. Restoring first separates package resolution problems from compilation problems, and `--no-restore` makes the build validate the already restored dependency graph.
34+
Run restore and build on the Arm-based VM to establish a native Arm baseline. Restoring first separates package resolution problems from compilation problems, and `--no-restore` makes the build validate the already restored dependency graph.
3435

3536
```bash
3637
# Restore dependencies first so build failures are easier to triage.
@@ -40,7 +41,7 @@ dotnet restore src/Presentation/Nop.Web/Nop.Web.csproj
4041
dotnet build src/Presentation/Nop.Web/Nop.Web.csproj -c Release --no-restore
4142
```
4243

43-
## Start and install nopCommerce
44+
### Start and install nopCommerce
4445

4546
Start the app locally and complete installer setup with PostgreSQL. Run this command in one terminal and leave it running; `dotnet run` hosts the web application and keeps the terminal attached to the server logs.
4647

@@ -73,9 +74,9 @@ curl -s -o /dev/null -w 'install=%{http_code}\n' http://127.0.0.1:5000/install
7374

7475
Expected after successful install: `root=200`, `install=302`.
7576

76-
## Baseline methodology
77+
### Run the baseline
7778

78-
Do not benchmark `/install`. Baseline stable storefront paths first, then add cart and attribute-change routes only after you know the valid product IDs, attribute IDs, request method, and anti-forgery token behavior for your installed store.
79+
Don't benchmark `/install`. Baseline stable storefront paths first, then add cart and attribute-change routes only after you know the valid product IDs, attribute IDs, request method, and anti-forgery token behavior for your installed store.
7980

8081
- `/`
8182
- `/search/`
@@ -211,7 +212,7 @@ python3 test_nopcommerce_endpoints.py \
211212

212213
The command prints the per-route summary and writes the raw measurements to `arm_before.json`. Keep that JSON file unchanged; it is the baseline artifact used by the tuning step.
213214

214-
## Baseline quality rules
215+
#### Baseline quality rules
215216

216217
Use these rules before you compare any optimization result:
217218

@@ -221,3 +222,11 @@ Use these rules before you compare any optimization result:
221222
- Capture raw JSON for every run and compare medians, not single outliers.
222223

223224
This baseline process becomes the control for every later tuning or code-change decision.
225+
226+
## What you've accomplished and what's next
227+
228+
You've now created a baseline before migrating the application.
229+
230+
Next, you'll make note of dependencies before migration.
231+
232+

content/learning-paths/servers-and-cloud-computing/dotnet-migration-nopcommerce/4-dependency-mapping-manifests.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
11
---
2-
title: Audit dependencies
2+
title: Run dependency discovery before migration
33
weight: 4
44

55
### FIXED, DO NOT MODIFY
66
layout: learningpathall
77
---
88

9-
# Audit dependencies
9+
## Audit application ependencies
1010

11-
Run dependency discovery before migration changes so you understand direct references, transitive risk, and hidden native payloads. This avoids late surprises when deploying to Arm.
11+
Run dependency discovery before migration changes so you understand direct references, transitive risk, and hidden native payloads. By doing so, you'll avoid late surprises when deploying to Arm.
1212

13-
### 1. Map direct references
13+
### Map direct references
1414

1515
Start from the `nopCommerce` repository root. Project-level references show what the app explicitly depends on before NuGet expands the graph. The command searches project files under `src` and prints file names and line numbers so you can jump directly to the owning project.
1616

1717
```bash
1818
rg -n "<PackageReference|<ProjectReference" src
1919
```
2020

21-
### 2. List transitive dependencies
21+
### List transitive dependencies
2222

2323
Enumerate the full dependency graph for the web entry point. Transitive packages often carry architecture-sensitive constraints, so review the output for runtime-specific package names, native library packages, image processing libraries, database providers, and platform-specific assets.
2424

2525
```bash
2626
dotnet list src/Presentation/Nop.Web/Nop.Web.csproj package --include-transitive
2727
```
2828

29-
### 3. Generate an SBOM
29+
### Generate an SBOM
3030

3131
Generate an SBOM so you can track all components, versions, and exposure surface as a first-class migration artifact. While not strictly necessary for migration purposes, this is a best practice that will save your team time down the road. You can also give this SBOM to an LLM to extract insights about your codebase for you.
3232

@@ -40,7 +40,7 @@ dotnet-CycloneDX src/Presentation/Nop.Web/Nop.Web.csproj -o sbom/ -rs -F Json
4040

4141
The `-rs` option includes project references, and `-F Json` writes JSON output that is easier to diff in CI. If tool installation is blocked, treat `dotnet list --include-transitive` plus `*.deps.json` evidence as a temporary fallback, not the final state.
4242

43-
### 4. Inspect package internals for native payloads
43+
### Inspect package internals for native payloads
4444

4545
Inspect package contents directly when a dependency looks architecture-sensitive. NuGet package folders are usually lower case on Linux, so set `PACKAGE_ID` to the folder name under `~/.nuget/packages` and `PACKAGE_VERSION` to the version you want to inspect.
4646

@@ -57,7 +57,7 @@ unzip -l "$(basename "$NUPKG")" | rg "runtimes/|native/"
5757

5858
If the command prints `runtimes/linux-arm64`, the package already carries an Arm Linux asset. If it prints only `linux-x64`, `win-x64`, or another non-Arm runtime, trace whether that asset is used by nopCommerce before you treat the dependency as portable. If there is no output, the package does not advertise runtime or native payload directories in the `.nupkg`.
5959

60-
## Dependency cascade rule
60+
### Dependency cascade rule
6161

6262
Treat dependencies as a chain, not isolated items:
6363

@@ -66,3 +66,9 @@ Treat dependencies as a chain, not isolated items:
6666
- Library B is architecture-sensitive
6767

6868
You must resolve B first, then validate A, then validate the app.
69+
70+
## What you've accomplished and what's next
71+
72+
You've reviewed application dependencies to understand what can change when you migrate to Arm.
73+
74+
Next, you'll explore options to containerize the application.

content/learning-paths/servers-and-cloud-computing/dotnet-migration-nopcommerce/5-containerization.md

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
---
2-
title: Containerize the application
2+
title: Choose between options to containerize the application
33
weight: 5
44

55
### FIXED, DO NOT MODIFY
66
layout: learningpathall
77
---
88

9-
# Containerize the application
9+
## Containerize the application
1010

11-
Containerization should preserve reproducibility across architectures. This page shows practical paths for Dockerfile-based builds and .NET SDK container publish, with guardrails for multi-architecture delivery.
11+
Containerization should preserve reproducibility across architectures. The following are practical paths for Dockerfile-based builds and .NET SDK container publish, with guardrails for multi-architecture delivery.
1212

13-
### 1. Dockerfile path
13+
Use the Dockerfile workflow as the default migration path for nopCommerce until your SDK-published image has the same Linux runtime dependencies and smoke-test results. Use SDK publish when you want MSBuild-owned image metadata and have a repeatable plan for the native packages that the Dockerfile previously installed.
14+
15+
### Dockerfile path
1416

1517
Run a Dockerfile build with `buildx` from the repository root when you want to preserve the upstream nopCommerce Dockerfile behavior. This is the safest first containerization path because the Dockerfile installs runtime packages used by nopCommerce on Linux, including globalization and image-processing dependencies.
1618

@@ -32,7 +34,7 @@ docker buildx build --platform linux/amd64,linux/arm64 -t "$IMAGE" --push .
3234
docker buildx imagetools inspect "$IMAGE"
3335
```
3436

35-
### 2. .NET SDK publish path (single architecture)
37+
### Single architecture .NET SDK publish path
3638

3739
Use SDK publish when you want tighter integration with .NET build settings and fewer custom Docker steps. For nopCommerce, treat this as a validation path until you have confirmed that the generated image includes the Linux native packages your store needs. SDK publish creates the container image, but it does not run `apk add` or `apt-get install` steps like a Dockerfile does.
3840

@@ -55,7 +57,7 @@ docker image inspect nopcommerce:arm64-test --format '{{.Architecture}}'
5557

5658
The expected output is `arm64`.
5759

58-
### 3. .NET SDK multi-arch path
60+
### Multi-arch .NET SDK publish path
5961

6062
Define runtime identifiers and multi-arch container runtime identifiers in the project file, then publish once. Add this metadata only after you have decided where nopCommerce's Linux runtime dependencies will live. If you use SDK publish for production, move those dependencies into a custom base image or another repeatable image-build step before replacing the Dockerfile workflow.
6163

@@ -78,16 +80,21 @@ dotnet publish src/Presentation/Nop.Web/Nop.Web.csproj -c Release /t:PublishCont
7880

7981
This is the scalable path for one image definition across both architectures, but the generated image still needs the same smoke test as the Dockerfile image: start the container, connect it to PostgreSQL, and verify storefront routes before you promote it.
8082

81-
## SDK version guardrail
83+
#### SDK version guardrail
8284

8385
Before choosing the SDK publish path for multi-architecture images, check the SDK version used by CI and by developer workstations:
8486

8587
```bash
8688
dotnet --version
8789
```
8890

89-
If you cannot use a .NET SDK version that supports multi-RID container publishing, or if SDK publish does not produce the multi-architecture image index you need, use the `docker buildx build --platform linux/amd64,linux/arm64` workflow instead. Multi-RID container publishing starts with .NET SDK versions 8.0.405, 9.0.102, and 9.0.2xx. The nopCommerce `release-4.90.3` source pins SDK `9.0.100` in `global.json` with feature-band roll-forward, so confirm the actual SDK selected by CI is new enough before relying on SDK multi-arch publish.
91+
If you can't use a .NET SDK version that supports multi-RID container publishing, or if SDK publish does not produce the multi-architecture image index you need, use the `docker buildx build --platform linux/amd64,linux/arm64` workflow instead.
9092

91-
## Recommendation
93+
Multi-RID container publishing starts with .NET SDK versions `8.0.405`, `9.0.102`, and `9.0.2xx`. The nopCommerce `release-4.90.3` source pins SDK `9.0.100` in `global.json` with feature-band roll-forward, so confirm the actual SDK selected by CI is new enough before relying on SDK multi-arch publish.
9294

93-
Use the Dockerfile workflow as the default migration path for nopCommerce until your SDK-published image has the same Linux runtime dependencies and smoke-test results. Use SDK publish when you want MSBuild-owned image metadata and have a repeatable plan for the native packages that the Dockerfile previously installed.
95+
96+
## What you've accomplished and what's next
97+
98+
You've learned about different containerization paths for the .NET application and containerized the app.
99+
100+
Next, you'll tune application performance.

content/learning-paths/servers-and-cloud-computing/dotnet-migration-nopcommerce/6-deploy-verify-cobalt.md

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
---
2-
title: Performance tuning on Cobalt
2+
title: Tune the performance of the .NET application on Azure
33
weight: 6
44

55
### FIXED, DO NOT MODIFY
66
layout: learningpathall
77
---
88

9-
# Performance tuning on Cobalt
9+
## Apply performance tuning changes
1010

1111
Treat tuning as an experiment pipeline, not a one-shot tweak. On Arm, apply changes in a controlled sequence, measure with fixed workloads, and keep only optimizations that repeatedly improve your target metrics.
1212

13-
## Tuned run configuration
13+
### Configure a tuned run
1414

1515
Start with a conservative runtime profile that is commonly effective for server workloads. Keep these settings outside the application code at first so you can switch profiles without rebuilding.
1616

@@ -35,7 +35,7 @@ dotnet run -c Release --no-build --urls http://0.0.0.0:5000
3535

3636
Increase `DOTNET_ThreadPool_ForceMinWorkerThreads` only when traces or load-test data show thread-pool starvation. Keep `DOTNET_EnableDiagnostics=0` for final measurement runs only; remove it when you need `dotnet-trace`, `dotnet-counters`, or profiler-based evidence.
3737

38-
### Optional spin-wait experiment for .NET 8, 9, and 10
38+
#### (Optional) Run spin-wait experiment for .NET 8, 9, and 10
3939

4040
If the workload burns CPU while waiting for short-lived thread-pool work, test disabling the thread-pool unfair semaphore spin limit:
4141

@@ -45,7 +45,7 @@ export DOTNET_ThreadPool_UnfairSemaphoreSpinLimit=0
4545

4646
Treat this as an experiment, not a default. Turning off spin waiting can reduce wasted CPU on small instances or oversubscribed containers, but it can also increase wake-up latency and reduce peak throughput. Validate it separately from the base tuned profile by changing only this variable between trials.
4747

48-
### Tiered PGO and ReadyToRun optimize different phases
48+
### Optimize tiered PGO and ReadyToRun
4949

5050
`DOTNET_TieredPGO=1` and `DOTNET_ReadyToRun=1` are not the same optimization:
5151

@@ -73,7 +73,7 @@ jq '.summary' arm_before.json
7373
jq '.summary' arm_after.json
7474
```
7575

76-
## Validation rules for credible tuning gains
76+
### Validation rules for credible tuning gains
7777

7878
Use these rules before adopting a tuning profile:
7979

@@ -83,15 +83,21 @@ Use these rules before adopting a tuning profile:
8383
- Require improvement in both throughput and p95 latency, not just one.
8484
- Set a minimum practical threshold (for example, >=5% median gain) before rollout.
8585

86-
## Instance-size tradeoffs
86+
#### Instance-size tradeoffs
8787

8888
Do not assume the same profile wins on every Azure Cobalt VM size. Smaller instances often benefit from lower CPU burn and predictable p95 latency. Larger instances often benefit from higher concurrency, but can expose GC, database, and shared-resource contention. Compare throughput, p95/p99 latency, CPU utilization, and error rate for each instance size before keeping a tuning profile.
8989

90-
## Interpretation
90+
### Considerations for interpreting metric improvements
91+
92+
When interpeting metric improvements, consider the following:
9193

9294
- If only one metric improves while p95 or error rate regresses, do not treat the change as a win.
9395
- If run-to-run variation is near the observed delta, treat it as noise.
94-
- Keep architecture-specific profiles separate; do not force one profile onto all architectures.
96+
- Keep architecture-specific profiles separate. Don't force one profile onto all architectures.
97+
98+
## What you've accomplished and what's next
99+
100+
You've applied performance tuning changes to the .NET application.
95101

102+
Next, you'll use the Arm MCP Server to optimize application performance.
96103

97-
Use this page as a tuning workflow template, then validate with your production-like traffic profile before rollout.

0 commit comments

Comments
 (0)