diff --git a/content/learning-paths/servers-and-cloud-computing/cobalt/1-create-cobalt-vm.md b/content/learning-paths/servers-and-cloud-computing/cobalt/1-create-cobalt-vm.md new file mode 100644 index 0000000000..2007ec1a24 --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/cobalt/1-create-cobalt-vm.md @@ -0,0 +1,25 @@ +--- +title: Create the Cobalt 100 virtual machine +weight: 2 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Use the Azure Portal to deploy a Cobalt 100 VM + +Azure Cobalt 100 VMs are a part of the ['D' family of Azure VMs](https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/general-purpose/d-family). To deploy one, follow these steps: + +1. Sign in to the [Azure Portal](https://portal.azure.com/). +2. Select **Create a resource → Compute → Virtual machine**. +3. Complete the *Basics* tab: + ![Azure Portal – Basics tab for the VM wizard#center](images/create-cobalt-vm.png) + The Dpsv6-series are powered by Cobalt 100. Selecting Standard_D4ps_v6 will give you a Cobalt VM with 4 physical cores. you can change the 4 to another value if you want a different number of cores. +4. Upload your public SSH key or generate a new one in the wizard. +5. Disallow public inbound ports for now. +5. Accept the defaults on the *Disks* tab. +6. On the *Networking* tab ensure that a **Public IP** is selected. You will need it to connect later. Leave the NSG settings as *Basic* for now. + +Click **Review + create** followed by **Create**. Azure now deploys the VM and the automatically-generated Network Security Group (NSG). Provisioning takes ~2 minutes. + +Navigate to the **Deployment in progress** pane or open the **Notifications** panel to track progress. When the deployment succeeds proceed to the next step to expose an inbound port. diff --git a/content/learning-paths/servers-and-cloud-computing/cobalt/2-open-port.md b/content/learning-paths/servers-and-cloud-computing/cobalt/2-open-port.md new file mode 100644 index 0000000000..f446d15df1 --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/cobalt/2-open-port.md @@ -0,0 +1,24 @@ +--- +title: Open an inbound port in the Network Security Group +weight: 3 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Allow external traffic to TCP ports 22 (SSH) and 8080 + +Every new virtual machine created through the Azure wizard is associated with a **Network Security Group (NSG)**. An NSG acts like a stateful firewall – if no rule explicitly allows traffic, Azure blocks it. + +In this step you will open port 22 for SSH, as well as port 8080 so that a web application running on the VM is reachable from your IP for testing. Substitute a different port if required by your workload, or a different IP range if you'd like broader accessibility. + +1. In the Azure Portal open the newly created VM resource and click **Networking → Network settings** in the left nav. +2. Select the **Network security group**. +3. In the left nav click **Settings → Inbound security rules** +4. Click **Add** at the top of the screen. +5. Fill in the form, specifying **My IP address** as the source and 22 as the destination port: + ![Add inbound security rule with source of my IP and destination port 22#center](images/create-nsg-rule.png) + +To open port 8080, follow steps 4 and 5 again, but instead choose port 8080 for the destination port. + +You have now opened ports 22 and 8080 to your IP. In the next step you will verify connectivity. \ No newline at end of file diff --git a/content/learning-paths/servers-and-cloud-computing/cobalt/3-verify-connectivity.md b/content/learning-paths/servers-and-cloud-computing/cobalt/3-verify-connectivity.md new file mode 100644 index 0000000000..b16db0151c --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/cobalt/3-verify-connectivity.md @@ -0,0 +1,47 @@ +--- +title: Verify connectivity to the Cobalt 100 VM +weight: 4 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Connect over SSH and test the open port + +1. On the **Overview** page for the VM copy the **Public IP address**. +2. Open a terminal on your local machine and SSH to the VM (replace *azureuser* if you chose a different admin username): + +```bash +ssh -i [path to your pem file] azureuser@[public IP] +``` + +Where `[public IP]` is your public IP and `[path to your pem file]` is the path to your SSH key file. + +Accept the prompt to add the host to *known_hosts* the first time you connect. + +### Start a simple HTTP server + +If you do not already have an application listening on TCP 8080 you can start one temporarily: + +```bash +sudo apt update -y && sudo apt install -y python3 +python3 -m http.server 8080 +``` + +Leave this terminal open – the server runs in the foreground. + +### Test from your local machine + +In a second local terminal run `curl` to confirm you can reach the server through the NSG rule you created: + +```bash +curl http://[public IP]:8080 +``` + +Where `[public IP]` is your public IP. + +You should see an HTML directory listing (or your application response). Receiving a response verifies that TCP 8080 is open and the VM is reachable from the public internet. + +Terminate the Python server when you are finished testing (press `Ctrl + C`). + +You now have an Arm-based Cobalt 100 VM with an exposed port 8080 that you can use to run any test server. To learn about optimizing .NET workloads on Cobalt, check out [Migrating a .NET application to Azure Cobalt](../../dotnet-migration/). \ No newline at end of file diff --git a/content/learning-paths/servers-and-cloud-computing/cobalt/_index.md b/content/learning-paths/servers-and-cloud-computing/cobalt/_index.md new file mode 100644 index 0000000000..04cd277fa8 --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/cobalt/_index.md @@ -0,0 +1,54 @@ +--- +title: Create an Azure Cobalt 100 VM + +minutes_to_complete: 10 + +who_is_this_for: Developers and DevOps engineers who need an Arm-based virtual machine on Azure and want to expose an application port to the internet. + +learning_objectives: + - Deploy an Arm-based Cobalt 100 virtual machine (VM) on Microsoft Azure. + - Connect to the Cobalt 100 VM using SSH. + - Open an inbound TCP port in the associated Network Security Group (NSG). + - Verify external connectivity to the newly opened port. + +prerequisites: + - A Microsoft Azure subscription with permission to create resources + - Basic familiarity with SSH + +author: Joe Stech + +### Tags +# Tagging metadata, see the Learning Path guide for the allowed values +skilllevels: Introductory +subjects: Containers and Virtualization +arm_ips: + - Neoverse +tools_software_languages: + - Azure Portal + - Azure CLI +operatingsystems: + - Linux + + +further_reading: + - resource: + title: Azure Cobalt 100 VM documentation + link: https://learn.microsoft.com/azure/virtual-machines/cobalt-100 + type: Documentation + - resource: + title: Azure Virtual Machines overview + link: https://learn.microsoft.com/azure/virtual-machines/ + type: Documentation + - resource: + title: Configure Azure network security group rules + link: https://learn.microsoft.com/azure/virtual-network/security-overview + type: Documentation + + + +### FIXED, DO NOT MODIFY +# ================================================================================ +weight: 1 # _index.md always has weight of 1 to order correctly +layout: "learningpathall" # All files under learning paths have this same wrapper +learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. +--- diff --git a/content/learning-paths/servers-and-cloud-computing/cobalt/_next-steps.md b/content/learning-paths/servers-and-cloud-computing/cobalt/_next-steps.md new file mode 100644 index 0000000000..c3db0de5a2 --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/cobalt/_next-steps.md @@ -0,0 +1,8 @@ +--- +# ================================================================================ +# FIXED, DO NOT MODIFY THIS FILE +# ================================================================================ +weight: 21 # Set to always be larger than the content in this path to be at the end of the navigation. +title: "Next Steps" # Always the same, html page title. +layout: "learningpathall" # All files under learning paths have this same wrapper for Hugo processing. +--- diff --git a/content/learning-paths/servers-and-cloud-computing/cobalt/images/create-cobalt-vm.png b/content/learning-paths/servers-and-cloud-computing/cobalt/images/create-cobalt-vm.png new file mode 100644 index 0000000000..ccbbb5d78d Binary files /dev/null and b/content/learning-paths/servers-and-cloud-computing/cobalt/images/create-cobalt-vm.png differ diff --git a/content/learning-paths/servers-and-cloud-computing/cobalt/images/create-nsg-rule.png b/content/learning-paths/servers-and-cloud-computing/cobalt/images/create-nsg-rule.png new file mode 100644 index 0000000000..7e97b89bad Binary files /dev/null and b/content/learning-paths/servers-and-cloud-computing/cobalt/images/create-nsg-rule.png differ diff --git a/content/learning-paths/servers-and-cloud-computing/dotnet-migration/1-create-orchardcore-app.md b/content/learning-paths/servers-and-cloud-computing/dotnet-migration/1-create-orchardcore-app.md new file mode 100644 index 0000000000..86e5d7d5fb --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/dotnet-migration/1-create-orchardcore-app.md @@ -0,0 +1,76 @@ +--- +title: Create a basic OrchardCore application +weight: 2 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +# Create a basic OrchardCore application + +In this section, you will learn how to create and compile a basic [OrchardCore](https://github.com/OrchardCMS/OrchardCore) CMS application, as an example of a popular Linux-based .NET workload. OrchardCore is a modular and multi-tenant application framework built with ASP.NET Core, which can be used to create content-driven websites. + +## Step 1: Set up your development environment + +1. Launch an Azure Cobalt instance running Ubuntu 24.04, and open port 8080 to the internet. For instructions on how to do this, see the [Create an Azure Cobalt 100 VM](../../cobalt) Learning Path. + +2. **Install .NET SDK**: + +```bash +wget https://packages.microsoft.com/config/ubuntu/24.04/packages-microsoft-prod.deb +sudo dpkg -i packages-microsoft-prod.deb +sudo apt-get update +sudo apt-get install -y dotnet-sdk-8.0 +``` + +3. **Verify installations**: + +```bash +dotnet --version +``` + +## Step 2: Install the OrchardCore Templates + +To start building an OrchardCore application, you need to install the OrchardCore templates. Open your terminal and run the following command: + +```bash +dotnet new -i OrchardCore.ProjectTemplates::1.0.0-rc2-13450 +``` + +This command installs the OrchardCore project templates, which you will use to create a new application. + +## Step 3: Create a new OrchardCore application + +1. **Create a new project**: Use the `dotnet` CLI to create a new OrchardCore application. + +```bash +dotnet new occms -n MyOrchardCoreApp +``` + + This command creates a new OrchardCore CMS application in a directory named `MyOrchardCoreApp`. + +2. **Navigate to the project directory**: + +```bash +cd MyOrchardCoreApp +``` + +## Step 4: Run the OrchardCore application + +1. **Build the application**: Compile the application using the following command: + +```bash +dotnet build +``` + +2. **Run the application**: Start the application with: + +```bash +dotnet run --urls http://0.0.0.0:8080 +``` + +3. **Access the application**: Open a web browser and navigate to `http://[instance IP]:8080` to see your OrchardCore application in action, where `[instance IP]` is the public IP of your Azure Cobalt instance. + +4. **Configure the application as a blog** In the resulting configuration page, + +You have successfully created and run a basic OrchardCore CMS application. In the next sections, you will learn how to integrate a C shared library into your .NET application and explore performance optimizations for Arm architecture. \ No newline at end of file diff --git a/content/learning-paths/servers-and-cloud-computing/dotnet-migration/2-add-shared-c-library.md b/content/learning-paths/servers-and-cloud-computing/dotnet-migration/2-add-shared-c-library.md new file mode 100644 index 0000000000..3850c79245 --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/dotnet-migration/2-add-shared-c-library.md @@ -0,0 +1,118 @@ +--- +title: Add a simple C shared library to your .NET OrchardCore application +weight: 3 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Introduction + +In this section, you will learn how to integrate a simple C shared library into your .NET OrchardCore application. This process involves creating a C library, compiling it, and then using it within your .NET application. This integration can help you leverage existing C code and libraries, enhancing the functionality and performance of your application. + + +## Step 1: Create a C shared library + +First, you need to create a simple C shared library. This library will contain a function that you will call from your .NET application. + +1. Create a new file named `mylib.c` with the following content: + +```c +#include + +void greet() { + printf("Hello from the C library!\n"); +} +``` + +2. Compile the C file into a shared library: + +```bash +gcc -shared -o libmylib.so -fPIC mylib.c +``` + + This will generate a shared library file (`libmylib.so`). + +## Step 2: Use the C library in your .NET application + +Now that you have a shared library, you can use it in your .NET application. + +1. In your OrchardCore application, create a new class file named `NativeMethods.cs`: + +```csharp +using System; +using System.Runtime.InteropServices; + +public static class NativeMethods +{ + [DllImport("mylib", EntryPoint = "greet")] + public static extern void Greet(); +} +``` + +2. Call the `Greet` method from your application. For example, you can add the following code to your main program or a controller: + +```csharp +using OrchardCore.Logging; + +var builder = WebApplication.CreateBuilder(args); + +builder.Host.UseNLogHost(); + +builder.Services + .AddOrchardCms() + // // Orchard Specific Pipeline + // .ConfigureServices( services => { + // }) + // .Configure( (app, routes, services) => { + // }) +; + +var app = builder.Build(); + +Console.WriteLine("Calling native greet..."); // NEW INTEROP LINE +NativeMethods.Greet(); // NEW INTEROP LINE + +if (!app.Environment.IsDevelopment()) +{ + app.UseExceptionHandler("/Error"); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); +} + +app.UseHttpsRedirection(); +app.UseStaticFiles(); + +app.UseOrchardCore(); + +app.Run(); +``` + +3. Ensure that dotnet can find your shared library: + +```bash +export LD_LIBRARY_PATH=$(pwd):$LD_LIBRARY_PATH +``` + +## Step 3: Run your application + +Now when you run `dotnet run`, you will see + +```bash +Calling native greet... +Hello from the C library! +``` + +in the logging output. + +## Compiling for Arm + +If you are compiling for Arm directly on Azure Cobalt, the compiler understands what default processor optimizations it should use, and you can compile as done in Step 1 above. However, if you are cross-compiling in your build pipeline, you should specify `-mcpu=neoverse-n2 -O3` when running the cross-compiler: + +```bash +aarch64-linux-gnu-gcc -mcpu=neoverse-n2 -O3 -shared -o libmylib.so -fPIC mylib.c +``` + +The `-mcpu=neoverse-n2` flag specifies the Cobalt architecture, and `-O3` ensures that maximum optimizations are completed (including SIMD opimizations). + +In the next section, you will explore how to make your build architecture agnostic with the anyCPU feature. diff --git a/content/learning-paths/servers-and-cloud-computing/dotnet-migration/3-any-cpu.md b/content/learning-paths/servers-and-cloud-computing/dotnet-migration/3-any-cpu.md new file mode 100644 index 0000000000..e26c529c81 --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/dotnet-migration/3-any-cpu.md @@ -0,0 +1,59 @@ +--- +title: Run a .NET OrchardCore application on Arm and x86 +weight: 4 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +# Run a .NET OrchardCore application on Arm and x86 + +In this section, you will learn how to configure and run your OrchardCore application on both Arm and x86 architectures using the .NET AnyCPU configuration. This approach allows your application to be architecture agnostic, providing flexibility and ease of deployment across different hardware platforms. + +The AnyCPU feature has been around since .NET 2, but it's current incarnation was released in .NET Framework 4.5. + +## Configure the project for AnyCPU + +To make your OrchardCore application architecture agnostic, you need to configure it to use the AnyCPU platform target. This allows the .NET runtime to choose the appropriate architecture at runtime. + +1. Open your OrchardCore project in your preferred IDE. +2. Locate the `.csproj` file for your project. +3. Modify the `` element to `AnyCPU`: + +```xml + + AnyCPU + +``` + +4. Save the changes to the `.csproj` file. + +## Build once, run anywhere + +```bash +dotnet build -c Release +``` + +4. Run the application: + +```bash +dotnet run --urls http://0.0.0.0:8080 +``` + +Your application should now be runnable on any architecture. All you have to do is copy the `MyOrchardCoreApp` directory to any computer with the .NET 8 Framework installed and run + +```bash +dotnet ./bin/Release/net8.0/MyOrchardCoreApp.dll --urls http://0.0.0.0:8080 +``` + +From within the `MyOrchardCoreApp` directory. + +## Benefits of architecture agnostic applications + +By configuring your application to be architecture agnostic, you gain several benefits: + +- **Flexibility**: Deploy your application on a wide range of devices without modification. +- **Efficiency**: Reduce the need for maintaining separate builds for different architectures. +- **Scalability**: Easily scale your application across different hardware platforms. + +This approach ensures that your OrchardCore application can run seamlessly on both Arm and x86 architectures. \ No newline at end of file diff --git a/content/learning-paths/servers-and-cloud-computing/dotnet-migration/4-dotnet-versions.md b/content/learning-paths/servers-and-cloud-computing/dotnet-migration/4-dotnet-versions.md new file mode 100644 index 0000000000..da8dba7f64 --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/dotnet-migration/4-dotnet-versions.md @@ -0,0 +1,75 @@ +--- +title: Evaluate .NET versions for performance on Arm +weight: 5 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +# Evaluate .NET versions for performance on Arm + +Understanding which versions perform best and the features they offer can help you make informed decisions when developing applications for Arm-based systems. + +.NET has evolved significantly over the years, with each version introducing new features and performance improvements. Here, we will focus on the key versions that have notable performance implications for Arm architecture. + +## .NET Core 3.1 (end-of-life 2022) + +.NET Core 3.1 was the first LTS with meaningful Arm64 support. Highlights were: + +- Initial JIT (Just-In-Time) optimizations for Arm64 (but the bulk of Arm throughput work arrived in .NET 5). +- Faster garbage collection thanks to refinements to the background GC mode. +- Initial set of Arm64 hardware intrinsics (AdvSIMD, AES, CRC32) exposed in `System.Runtime.Intrinsics`. + +## .NET 5 (end-of-life 2022) + +With .NET 5 Microsoft started the “one .NET” unification. Even though it had only 18 months of support, it delivered notable Arm gains: + +- Cross-gen2 shipped, delivering better Arm64 code quality (but only became the default in .NET 6). +- Single-file application publishing (with optional IL-trimming) simplified deployment to Arm edge devices. +- Major ASP.NET Core throughput wins on Arm64 (Kestrel & gRPC) compared with .NET Core 3.1. + +## .NET 6 (end-of-life 2024) + +.NET 6 laid the foundation for the modern performance story on Arm64: + +- Tiered PGO entered preview, combining tiered compilation with profile-guided optimisation. +- Better scalability on many-core Arm servers thanks to the new ThreadPool implementation. +- First-class support for Apple M1, enabling full .NET development on Arm-based macOS, as well as for Windows Arm64. + + +## .NET 7 (end-of-life 2024) + +.NET 7 was an STS (Standard-Term Support) release which is now out of support, but it pushed the performance envelope and is therefore interesting from a historical perspective. Key highlights were: + +- General-availability of Native AOT publishing for console applications, producing self-contained, very small binaries with fast start-up on Arm64. +- Dynamic PGO (Profile-Guided Optimisation) and On-Stack Replacement became the default, letting the JIT optimise the hottest code paths based on real run-time data. +- New Arm64 hardware intrinsics (e.g. SHA-1/SHA-256, AES, CRC-32) exposed through System.Runtime.Intrinsics, enabling high-performance crypto workloads. + +## .NET 8 (current LTS – support until November 2026) + +.NET 8 is the current Long-Term Support release and should be your baseline for new production workloads. Important Arm-related improvements include: + +- Native AOT support for ASP.NET Core, trimming enhancements and even smaller self-contained binaries, translating into faster cold-start for containerised Arm services. +- Further JIT tuning for Arm64 delivering single- to low-double-digit percentage throughput wins in real-world benchmarks. +- Smaller base container images (`mcr.microsoft.com/dotnet/aspnet:8.0` and `…/runtime:8.0`) thanks to a redesigned layering strategy, particularly beneficial on Arm where network bandwidth is often at a premium. +- Garbage-collector refinements that reduce pause times on highly-threaded, many-core servers. + +## .NET 9 + +.NET 9 is still in preview, so features may change, but public builds already show promising Arm-centric updates: + +- PGO is now enabled for release builds by default and its heuristics have been retuned for Arm workloads, yielding notable throughput improvements with zero developer effort. +- The JIT has started to exploit Arm v9 instructions such as SVE2 where hardware is available, opening the door to even wider SIMD operations. +- C# 13 and F# 8 previews ship with the SDK, bringing useful productivity improvements fully supported on Arm devices. + +Although .NET 9 will receive only 18 months of support, it is an excellent choice when you need the very latest performance improvements or want to trial new language/runtime capabilities ahead of the next LTS. + +## .NET 10 (preview – next LTS) + +.NET 10 is still in preview and will likely change prior to it's GA release, but it will be the next LTS version of .NET. + +- Extended SVE2 and new SME (Scalable Matrix Extension) intrinsics to unlock efficient implementation of large-scale numerical algorithms and on-device AI inference on Arm v9. +- C# 14 is expected to ship alongside .NET 10, bringing additional compile-time metaprogramming features that can reduce boilerplate on resource-constrained Arm edge devices. + +Because .NET 10 is still in preview, you should validate any assumptions about feature availability against the latest preview builds and roadmap updates. + diff --git a/content/learning-paths/servers-and-cloud-computing/dotnet-migration/_index.md b/content/learning-paths/servers-and-cloud-computing/dotnet-migration/_index.md new file mode 100644 index 0000000000..dac07dd8a5 --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/dotnet-migration/_index.md @@ -0,0 +1,51 @@ +--- +title: Migrating a .NET application to Azure Cobalt + +minutes_to_complete: 25 + +who_is_this_for: .NET developers who want to take advantage of the cost and performance benefits of Azure Cobalt processors. + +learning_objectives: + - Create and compile a basic OrchardCore CMS application + - Add a simple C shared library to your .NET application + - Learn about anyCPU hardware agnostic builds + - Gain intuition about performance of different .NET versions + +prerequisites: + - An Azure account + - Installation of .NET 8 + - gcc installed + +author: Joe Stech + +### Tags +skilllevels: Advanced +subjects: Performance and Architecture +armips: + - Neoverse +tools_software_languages: + - .NET + - OrchardCore + - C +operatingsystems: + - Linux + + +further_reading: + - resource: + title: OrchardCore documentation + link: https://docs.orchardcore.net/ + type: documentation + - resource: + title: .NET documentation + link: https://docs.microsoft.com/en-us/dotnet/ + type: documentation + + + +### FIXED, DO NOT MODIFY +# ================================================================================ +weight: 1 # _index.md always has weight of 1 to order correctly +layout: "learningpathall" # All files under learning paths have this same wrapper +learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. +--- diff --git a/content/learning-paths/servers-and-cloud-computing/dotnet-migration/_next-steps.md b/content/learning-paths/servers-and-cloud-computing/dotnet-migration/_next-steps.md new file mode 100644 index 0000000000..c3db0de5a2 --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/dotnet-migration/_next-steps.md @@ -0,0 +1,8 @@ +--- +# ================================================================================ +# FIXED, DO NOT MODIFY THIS FILE +# ================================================================================ +weight: 21 # Set to always be larger than the content in this path to be at the end of the navigation. +title: "Next Steps" # Always the same, html page title. +layout: "learningpathall" # All files under learning paths have this same wrapper for Hugo processing. +---