Skip to content

Commit 791790f

Browse files
Merge pull request #3473 from anupras-mohapatra-arm/servers-and-cloud-computing
Migrating nopCommerce app to Arm on Azure LP review
2 parents a841a5c + 114dff1 commit 791790f

7 files changed

Lines changed: 216 additions & 109 deletions

File tree

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

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
11
---
2-
title: Environment setup
2+
title: Set up an Arm-based environment
3+
description: Set up an Ubuntu Azure Cobalt-based virtual machine with Docker, .NET 9, and PostgreSQL so you can build and validate nopCommerce on Arm.
34
weight: 2
45

56
### FIXED, DO NOT MODIFY
67
layout: learningpathall
78
---
89

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

11-
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.
12+
Set up an Arm Azure 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.
1213

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.
14+
Start with the [Azure Cobalt Learning Path](/learning-paths/servers-and-cloud-computing/cobalt/) and complete VM provisioning.
1415

15-
This path was validated with Ubuntu 24.04 LTS on Azure Cobalt in `westus2` (example VM size: `Standard_D2ps_v6`, 2 vCPUs).
16+
The Learning Path was validated with Ubuntu 24.04 LTS on Azure Cobalt in `westus2` (example VM size: `Standard_D2ps_v6`, 2 vCPUs).
1617

17-
## Install tools on Cobalt VM
18+
### Install tools on the Azure virtual machine
1819

19-
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.
20+
Install the toolchain on the Azure VM before building and testing.
21+
22+
The following 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:
2023

2124
```bash
2225
sudo apt-get update -y
@@ -30,13 +33,14 @@ sudo apt-get install -y dotnet-sdk-9.0
3033

3134
The Docker group change applies to new login sessions. Sign out and sign back in before running Docker without `sudo`, or run `newgrp docker` in the current terminal before continuing.
3235

33-
Confirm architecture and tool versions before proceeding. On Azure Cobalt, the Linux machine architecture should be `aarch64`, and the .NET SDK should be a 9.0.x release.
36+
Confirm architecture and tool versions before proceeding:
3437

3538
```bash
3639
uname -m
3740
dotnet --version
3841
docker --version
3942
```
43+
On Azure Cobalt, the Linux machine architecture should be `aarch64`, and the .NET SDK should be a `9.0.x` release.
4044

4145
The output is similar to:
4246

@@ -46,13 +50,15 @@ aarch64
4650
Docker version ...
4751
```
4852

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.
53+
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**.
54+
55+
### Enable citext to satisfy PostgreSQL prerequisite
5056

51-
## PostgreSQL prerequisite for nopCommerce install
57+
nopCommerce defaults to SQL Server, but you'll use PostgreSQL for Arm validation. For PostgreSQL installs, you need `citext` before migration and installation. Without it, installer migrations fail with `type "citext" does not exist` (captured in local test artifacts).
5258

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).
59+
Create PostgreSQL and enable `citext` before running the installer. The database runs in Docker on the VM so the nopCommerce app can connect to `127.0.0.1:5432` during the local validation phase.
5460

55-
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.
61+
Replace the password value before running the following commands:
5662

5763
```bash
5864
export NOP_POSTGRES_PASSWORD='replace-with-a-strong-password'
@@ -70,3 +76,9 @@ docker exec nop-postgres psql -U nop -d nopcommerce \
7076
```
7177

7278
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.
79+
80+
## What you've accomplished and what's next
81+
82+
You've set up an Arm-based environment by creating a Microsoft Azure VM powered by Cobalt 100 with Docker, .NET 9, and PostgreSQL ready for nopCommerce validation.
83+
84+
Next, you'll create a reproducible baseline to use for benchmarking.

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

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
---
2-
title: Build and baseline
2+
title: Create a nopCommerce endpoint benchmark baseline on Arm
3+
description: Build a pinned nopCommerce release on Arm and run a repeatable endpoint benchmark to create a baseline for later tuning.
34
weight: 3
45

56
### FIXED, DO NOT MODIFY
67
layout: learningpathall
78
---
89

9-
# Build and baseline
10+
## Build nopCommerce and create an endpoint baseline
1011

11-
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.
12+
Create a reproducible Arm baseline before optimization work.
1213

13-
## Clone and pin the source
14+
You'll pin the source, verify a clean build, and capture a representative endpoint baseline so you can measure later changes against a known control.
1415

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.
16+
### Clone and pin the source
17+
18+
Pinning the exact tag and commit avoids silent drift in dependencies and behavior.
19+
20+
Run the following commands on the Arm-based virtual machine (VM):
1621

1722
```bash
1823
git clone https://github.com/nopSolutions/nopCommerce.git
@@ -21,16 +26,17 @@ git fetch --tags --prune
2126
git checkout release-4.90.3
2227
git rev-parse --short=10 HEAD # expect 9beda11c42
2328
```
29+
Later commands in the section assume you're in the `nopCommerce` repository root unless stated otherwise.
2430

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

2733
```bash
2834
cd ~/nopCommerce # replace with your clone path if different
2935
```
3036

31-
## Restore and build on Arm
37+
### Restore and build on Arm
3238

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.
39+
Run restore and build on the Arm-based VM to establish a native Arm baseline:
3440

3541
```bash
3642
# Restore dependencies first so build failures are easier to triage.
@@ -39,15 +45,19 @@ dotnet restore src/Presentation/Nop.Web/Nop.Web.csproj
3945
# Build release binaries without re-restoring packages.
4046
dotnet build src/Presentation/Nop.Web/Nop.Web.csproj -c Release --no-restore
4147
```
48+
Restoring first separates package resolution problems from compilation problems, and `--no-restore` makes the build validate the already restored dependency graph.
4249

43-
## Start and install nopCommerce
50+
### Start and install nopCommerce
4451

45-
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.
52+
Start the app locally and complete installer setup with PostgreSQL.
53+
54+
Run the following command in one terminal and leave it running:
4655

4756
```bash
4857
cd src/Presentation/Nop.Web
4958
dotnet run -c Release --no-build --urls http://0.0.0.0:5000
5059
```
60+
`dotnet run` hosts the web application and keeps the terminal attached to the server logs.
5161

5262
In a browser, open `http://<cobalt-public-ip>:5000` or use an SSH tunnel to reach `http://127.0.0.1:5000`. On the nopCommerce install page, select PostgreSQL and use the database settings from the previous section:
5363

@@ -71,11 +81,11 @@ curl -s -o /dev/null -w 'root=%{http_code}\n' http://127.0.0.1:5000/
7181
curl -s -o /dev/null -w 'install=%{http_code}\n' http://127.0.0.1:5000/install
7282
```
7383

74-
Expected after successful install: `root=200`, `install=302`.
84+
The outputs after a successful install should be: `root=200`, `install=302`.
7585

76-
## Baseline methodology
86+
### Run the endpoint baseline benchmark
7787

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.
88+
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.
7989

8090
- `/`
8191
- `/search/`
@@ -84,7 +94,7 @@ Do not benchmark `/install`. Baseline stable storefront paths first, then add ca
8494
- Product and category pages from your sample data
8595
- Cart or checkout endpoints from a recorded production-like workflow
8696

87-
Create the endpoint tester in the repository root. The script uses only the Python standard library, submits requests with fixed concurrency, treats non-2xx and non-3xx responses as errors, and writes raw samples plus per-route summaries to JSON.
97+
Create the endpoint tester in the repository root:
8898

8999
```python {file_name="test_nopcommerce_endpoints.py"}
90100
#!/usr/bin/env python3
@@ -198,6 +208,8 @@ if __name__ == "__main__":
198208
main()
199209
```
200210

211+
The script uses only the Python standard library, submits requests with fixed concurrency, treats non-2xx and non-3xx responses as errors, and writes raw samples plus per-route summaries to JSON.
212+
201213
Run the endpoint tester from the repository root while the app is still running in the first terminal:
202214

203215
```bash
@@ -209,15 +221,22 @@ python3 test_nopcommerce_endpoints.py \
209221
--json-out arm_before.json
210222
```
211223

212-
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.
224+
The command prints the per-route summary and writes the raw measurements to `arm_before.json`. Keep that JSON file unchanged because it's the baseline artifact used in the tuning step.
213225

214-
## Baseline quality rules
226+
### Baseline quality rules
215227

216228
Use these rules before you compare any optimization result:
217229

218-
- Run at least 3 baseline trials with identical parameters.
219-
- Keep endpoint order fixed per run (not randomized) when comparing before vs after.
230+
- Run at least three baseline trials with identical parameters.
231+
- Keep endpoint order fixed per run (not randomized) when comparing before and after.
220232
- Keep database state and seeded data identical across runs.
221233
- Capture raw JSON for every run and compare medians, not single outliers.
222234

223235
This baseline process becomes the control for every later tuning or code-change decision.
236+
237+
## What you've accomplished and what's next
238+
239+
You've now pinned the nopCommerce source, verified the build, installed the app, and captured `arm_before.json` as your endpoint baseline.
240+
241+
Next, you'll audit dependencies before migration.
242+
Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,54 @@
11
---
2-
title: Audit dependencies
2+
title: Generate an SBOM and run .NET dependency discovery before migration
3+
description: Audit .NET dependencies, generate a CycloneDX SBOM, and inspect native package payloads before migrating nopCommerce to Arm.
34
weight: 4
45

56
### FIXED, DO NOT MODIFY
67
layout: learningpathall
78
---
89

9-
# Audit dependencies
10+
## Audit application dependencies
1011

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.
12+
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.
1213

13-
### 1. Map direct references
14+
### Map direct references
1415

15-
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.
16+
Start from the `nopCommerce` repository root. Project-level references show what the app explicitly depends on before NuGet expands the graph.
17+
18+
Run the following command to search project files under `src` and print file names and line numbers, so you can jump directly to the owning project:
1619

1720
```bash
1821
rg -n "<PackageReference|<ProjectReference" src
1922
```
2023

21-
### 2. List transitive dependencies
24+
### List transitive dependencies
2225

23-
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.
26+
Enumerate the full dependency graph for the web entry point:
2427

2528
```bash
2629
dotnet list src/Presentation/Nop.Web/Nop.Web.csproj package --include-transitive
2730
```
31+
Transitive packages often carry architecture-sensitive constraints. Review the output for runtime-specific package names, native library packages, image processing libraries, database providers, and platform-specific assets.
2832

29-
### 3. Generate an SBOM
33+
### Generate an SBOM
3034

31-
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.
35+
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'll save time. You can also give this SBOM to an LLM to extract insights about your codebase.
3236

3337
The CycloneDX tool is installed as a .NET global tool. Add `~/.dotnet/tools` to `PATH` in the current shell so the `dotnet-CycloneDX` command is available immediately after installation.
3438

39+
To generate an SBOM, run the following command:
40+
3541
```bash
3642
dotnet tool install --global CycloneDX
3743
export PATH="$PATH:$HOME/.dotnet/tools"
3844
dotnet-CycloneDX src/Presentation/Nop.Web/Nop.Web.csproj -o sbom/ -rs -F Json
3945
```
4046

41-
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.
47+
The `-rs` option includes project references, and `-F Json` writes JSON output that's 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.
4248

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

45-
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.
51+
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:
4652

4753
```bash
4854
PACKAGE_ID="replace-with-package-id"
@@ -55,14 +61,16 @@ cd /tmp/nupkg-audit
5561
unzip -l "$(basename "$NUPKG")" | rg "runtimes/|native/"
5662
```
5763

58-
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`.
64+
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's no output, the package doesn't advertise runtime or native payload directories in the `.nupkg`.
65+
66+
### Dependency cascade rule
67+
68+
Treat dependencies as a chain, not isolated items.
5969

60-
## Dependency cascade rule
70+
Assume the app depends on library A, library A depends on library B, library B is architecture-sensitive. In this scenario, you must resolve B first, then validate A, then validate the app.
6171

62-
Treat dependencies as a chain, not isolated items:
72+
## What you've accomplished and what's next
6373

64-
- App depends on library A
65-
- Library A depends on library B
66-
- Library B is architecture-sensitive
74+
You've now mapped direct and transitive dependencies, generated an SBOM, and checked packages for native payloads that could affect Arm deployment.
6775

68-
You must resolve B first, then validate A, then validate the app.
76+
Next, you'll explore options to containerize and migrate the application.

0 commit comments

Comments
 (0)