Install the .NET Library on Docker Windows Container
Before you start
Do not run other APM libraries alongside the SolarWinds Observability .NET Library. Remove other APM libraries from your .NET application before using the SolarWinds Observability .NET Library.
Install the .NET Library
To create a Docker Windows Container with the SolarWinds Observability .NET Library, install the library with the command line.
-
Create a file called
conf.inf
containing the following text, and place it in the same directory as your installer.[Setup] service_key=YourServiceKey apm_collector=YourSolarWindsApmCollectorEndpoint
Modify the following text:
-
Replace
YourServiceKey
with the Service key you are using to identify your account and the service being instrumented.The Service key should be in the form of
YourApiToken:YourServiceName
. ReplaceYourApiToken
with the SolarWinds Observability API token (ingestion type) generated for this service, and replaceYourServiceName
with your chosen name for this service. See API Tokens.The service name is also called the entity's service ID in SolarWinds Observability SaaS. The service name can also be seen in the Overview tab in the service entity's Entity Explorer details view. Editing the display name of the service entity in SolarWinds Observability SaaS does not affect the Service key.
-
Replace
YourSolarWindsApmCollectorEndpoint
with the correct SolarWinds APM collector endpoint for your organization.The
apm_collector
setting is optional and ensures the correct endpoint URI is used when sending data to the SolarWinds APM collector. To determine the correct endpoint for your SolarWinds APM collector, see Data centers and endpoint URIs.
The service key can also be passed to SolarWinds Observability .NET Library by defining the
SW_APM_SERVICE_KEY
environment variable when starting container. -
-
To simplify escaping commands, change the default escape character to a backtick (
`
). Add the following line to the beginning of the dockerfile.# escape=`
-
Add the
conf.inf
and installer files to the dockerfile with the following commands.ADD ./SolarWindsAPM_DotNetAgent_Setup.exe c:\SolarWindsAPM_DotNetAgent_Setup.exe ADD ./conf.inf c:\conf.inf
-
Set PowerShell as default shell with the following command.
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"] `
-
Install SolarWinds Observability .NET Library by entering the following command in the command prompt.
SolarWindsAPM_DotNetAgent_Setup.exe [/SILENT | /VERYSILENT] /LOADINF="conf.inf" /COMPONENTS="IISOnly[,NonIISApplications,IISCoreApplication,NonIISCoreApplication]" [/CLOSEAPPLICATIONS | /NOCLOSEAPPLICATIONS]
Square brackets indicate optional parameters.
The pipe character separates multiple choices within an option.
For
/COMPONENTS
see Install the .NET Library on Docker Windows Container for all available values. For example:/COMPONENTS="IISOnly,NonIISApplications"
Launch and wait for installer process to finish:
RUN $installerDir = $env:SystemDrive; ` cd $installerDir ; ` $processStartInfo = New-Object System.Diagnostics.ProcessStartInfo ; ` $arguments = '/SILENT /CLOSEAPPLICATIONS /LOADINF="conf.inf" /COMPONENTS="IISOnly" /LOG="installer.log"' ; ` $installer = $installerDir + '\SolarWindsAPM_DotNetAgent_Setup.exe' ; ` $processStartInfo.FileName = $installer ; ` $processStartInfo.Arguments = $arguments ; ` $process = New-Object System.Diagnostics.Process ; ` $process.StartInfo = $processStartInfo ; ` $process.Start() | Out-Null ; ` $process.WaitForExit() ; ` $result = $process.ExitCode ; ` echo "result: $result" ; ` if ($result -ne 0) { ` echo "Failed to install .NET Library" ; ` exit $result; ` } ; ` echo ".NET Library installed." ;
-
If you are instrumenting applications running inside IIS, run the following commands to download and copy certificate to the
C:\cert
directory.RUN $certRoot = $env:SystemDrive + '\cert\' ; ` mkdir $certRoot ; ` Invoke-WebRequest https://curl.se/ca/cacert.pem -OutFile "$certRoot\cacert.pem" ;
-
Run the following command to add the
SW_APM_TRUSTEDPATH
environment variable to instruct the SolarWinds Observability .NET Library to load certificate. If Windows is installed on a different system drive, then updateC:\
to match the system drive returned by$env:SystemDrive
.ENV SW_APM_TRUSTEDPATH='C:\cert\cacert.pem'
-
If instrumenting an .NET Framework applications running inside IIS, add the
COMPLUS_LoaderOptimization
environment variable with the following command.ENV COMPLUS_LoaderOptimization=1
-
(Optional) List properties for HttpModule installed by SolarWinds Observability .NET Library with the following command.
RUN Import-Module WebAdministration ; ` $modules = (Get-WebConfiguration //modules -PSPath iis:).collection ; ` $modules | foreach {if ($_.name -eq '"SolarWindsAPM"') { $_.attributes | select name, value}} ;
When using the Docker Windows Container to create an Azure App Service, the environment variables declared inside the dockerfile must be set inside the Application Settings. To configure the .NET Library, navigate to the Application Settings in the configuration settings and add the SW_APM_TRUSTEDPATH
and COMPLUS_LoaderOptimization
application global environment variables.
See the following example dockerfiles for web applications running inside IIS.
# escape=`
FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2019
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
# Set environment variables
ENV COMPLUS_LoaderOptimization=1 `
SW_APM_TRUSTEDPATH='c:\cert\cacert.pem'
# Download and copy certificate under c:\cert directory
RUN $certRoot = $env:SystemDrive + '\cert\' ; `
mkdir $certRoot ; `
Invoke-WebRequest https://curl.se/ca/cacert.pem -OutFile "$certRoot\cacert.pem" ;
# Copy installer
ADD ./SolarWindsAPM_DotNetAgent_Setup.exe c:/SolarWindsAPM_DotNetAgent_Setup.exe
ADD ./conf.inf c:/conf.inf
# Install .NET Library
RUN $installerDir = $env:SystemDrive; `
cd $installerDir ; `
$processStartInfo = New-Object System.Diagnostics.ProcessStartInfo ; `
$arguments = '/SILENT /CLOSEAPPLICATIONS /LOADINF="conf.inf" /COMPONENTS="IISOnly" /LOG="installer.log"' ; `
$installer = $installerDir + '\SolarWindsAPM_DotNetAgent_Setup.exe' ; `
$processStartInfo.FileName = $installer ; `
$processStartInfo.Arguments = $arguments ; `
$process = New-Object System.Diagnostics.Process ; `
$process.StartInfo = $processStartInfo ; `
$process.Start() | Out-Null ; `
$process.WaitForExit() ; `
$result = $process.ExitCode ; `
echo "result: $result" ; `
if ($result -ne 0) { `
echo "Failed to install .NET Library" ; `
exit $result; `
} ; `
echo ".NET Library installed" ;
# List attributes for IIS HttpModule installed by .NET Library
RUN Import-Module WebAdministration ; `
$modules = (Get-WebConfiguration //modules -PSPath iis:).collection ; `
$modules | foreach {if ($_.name -eq '"SolarWindsAPM"') { $_.attributes | select name, value}} ;
# Copy .NET Framework 4.8 Web application
COPY webapp/ /inetpub/wwwroot/webapp/
# Set application pool as DefaultAppPool to allow passing of environment variables from ServiceMonit.exe to IIS worker process
RUN Import-Module WebAdministration ; `
Get-WebSite -Name 'Default Web Site' | Remove-WebSite -Confirm:$false -Verbose ; `
Remove-WebAppPool -Name "DefaultAppPool" -Confirm:$false -Verbose ; `
$wwwRoot = $env:SystemDrive + '\inetpub\wwwroot' ; `
$appName = 'webapp' ; `
$appPoolName = 'DefaultAppPool' ; `
$runtimeVersion = 'v4.0' ; `
$identity = 'ApplicationPoolIdentity' ; `
$pipelineMode = 'Integrated' ; `
$bindings=@( @{protocol='http' ; bindingInformation='*:80:'} ) ; `
New-Item -Path "IIS:\AppPools" -Name "$appPoolName" -Type AppPool ; `
Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name managedRuntimeVersion -Value "$runtimeVersion" ; `
Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name processModel -Value @{'identitytype' = $identity} ; `
Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name managedPipelineMode -Value "$pipelineMode" ; `
New-Website -Name $appName -ApplicationPool $appPoolName -PhysicalPath $wwwRoot\$appName ; `
Set-ItemProperty -Path "IIS:\Sites\$appName" -Name bindings -Value $bindings ; `
Start-IISSite -Name "$appName" ;
# Expose ports
EXPOSE 80
WORKDIR /inetpub/wwwroot
# escape=`
FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2019
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
# Set environment variables
ENV SW_APM_TRUSTEDPATH='c:\cert\cacert.pem'
# Install ASP.NET Core Hosting Module for a ASP.NET Core 6.0 application. Update download link for a different ASP.NET Core version.
RUN Invoke-WebRequest https://download.visualstudio.microsoft.com/download/pr/d7124775-38c9-460f-a269-7bc131b3dfbf/7f60bcc6030e408cf11a935d5451ffa4/dotnet-hosting-6.0.20-win.exe -OutFile c:\dotnet-hosting-win.exe ; `
$appRoot = $env:SystemDrive ; `
cd $appRoot ; `
$processStartInfo = New-Object System.Diagnostics.ProcessStartInfo ; `
$arguments = '/install /quiet /norestart /log install.hosting.log' ; `
$installer = $appRoot + '\dotnet-hosting-win.exe' ; `
$processStartInfo.FileName = $installer ; `
$processStartInfo.Arguments = $arguments ; `
$process = New-Object System.Diagnostics.Process ; `
$process.StartInfo = $processStartInfo ; `
$process.Start() | Out-Null ; `
$process.WaitForExit() ; `
$result = $process.ExitCode ; `
echo "result: $result" ; `
if ($result -ne 0) { `
echo "Failed to install ASP.NET Core Hosting Bundle." ; `
exit $result; `
} ; `
echo "ASP.NET Core Hosting Bundle installed." ; `
Remove-Item -Force C:\dotnet-hosting-win.exe; `
Remove-Item -Force -Recurse $env:Temp\*
# Check that AspNetCoreModuleV2 IIS Module is installed
RUN Import-Module WebAdministration ; `
$modules = (Get-WebConfiguration //modules -PSPath iis:).collection ; `
$modules | foreach {if ($_.name -eq '"AspNetCoreModuleV2"') { $_.attributes | select name, value}} ;
# Install certificate
RUN $certRoot = $env:SystemDrive + '\cert\' ; `
mkdir $certRoot ; `
Invoke-WebRequest https://curl.se/ca/cacert.pem -OutFile "$certRoot\cacert.pem" ;
# Copy installer and configuration file
COPY ./SolarWindsAPM_DotNetAgent_Setup.exe c:/SolarWindsAPM_DotNetAgent_Setup.exe
COPY ./conf.inf c:/conf.inf
# Install .NET Library
RUN $appRoot = $env:SystemDrive ; `
cd $appRoot ; `
$processStartInfo = New-Object System.Diagnostics.ProcessStartInfo ; `
$arguments = '/SILENT /CLOSEAPPLICATIONS /LOADINF="conf.inf" /COMPONENTS="IISCoreApplications" /LOG="installer.log"' ; `
$installer = $appRoot + '\SolarWindsAPM_DotNetAgent_Setup.exe' ; `
$processStartInfo.FileName = $installer ; `
$processStartInfo.Arguments = $arguments ; `
$process = New-Object System.Diagnostics.Process ; `
$process.StartInfo = $processStartInfo ; `
$process.Start() | Out-Null ; `
$process.WaitForExit() ; `
$result = $process.ExitCode ; `
echo "result: $result" ; `
if ($result -ne 0) { `
echo "Failed to install .NET Library" ; `
exit $result; `
} ; `
echo ".NET Library installed." ;
# List attributes for IIS HttpModule installed by .NET Library
RUN Import-Module WebAdministration ; `
$modules = (Get-WebConfiguration //modules -PSPath iis:).collection ; `
$modules | foreach {if ($_.name -eq '"SolarWindsAPM"') { $_.attributes | select name, value}} ;
# Copy ASP.NET Core 6.0 application
COPY webapp/ /inetpub/wwwroot/webapp/
# Set application pool as DefaultAppPool to allow passing of environment variables from ServiceMonit.exe to IIS worker process
RUN Import-Module WebAdministration ; `
Get-WebSite -Name 'Default Web Site' | Remove-WebSite -Confirm:$false -Verbose ; `
Remove-WebAppPool -Name "DefaultAppPool" -Confirm:$false -Verbose ; `
$wwwRoot = $env:SystemDrive + '\inetpub\wwwroot' ; `
$appName = 'webapp' ; `
$appPoolName = 'DefaultAppPool' ; `
$runtimeVersion = '' ; `
$identity = 'ApplicationPoolIdentity' ; `
$pipelineMode = 'Integrated' ; `
$bindings=@( @{protocol='http' ; bindingInformation='*:80:'} ) ; `
New-Item -Path "IIS:\AppPools" -Name "$appPoolName" -Type AppPool ; `
Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name managedRuntimeVersion -Value "$runtimeVersion" ; `
Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name processModel -Value @{'identitytype' = $identity} ; `
Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name managedPipelineMode -Value "$pipelineMode" ; `
New-Website -Name $appName -ApplicationPool $appPoolName -PhysicalPath $wwwRoot\$appName ; `
Set-ItemProperty -Path "IIS:\Sites\$appName" -Name bindings -Value $bindings ; `
Start-IISSite -Name "$appName" ;
# Expose ports
EXPOSE 80
WORKDIR /inetpub/wwwroot
Install the .NET Framework web application
Before you start
The web application must use the default application pool name DefaultAppPool
. If any other name is used for the application pool, the process ServiceMonitor.exe
does not pass the environment variables that are declared when the container is started. As a result, environment variables, such as SW_APM_SERVICE_KEY
, passed in with the option -e
when the docker container is started are not seen by the SolarWinds Observability .NET Library.
See the Microsoft IIS Service Monitor readme, as well as the Windows service installed in docker container not using Environment Variables closed issue for more information.
Set up the web application
-
Copy the application binaries to a folder inside the
C:\inetpub\wwwroot\webapp
directory.COPY app/ /inetpub/wwwroot/webapp/
-
Run the following command to delete the
Default Web Site
web site and the default application poolDefaultAppPool
.RUN Import-Module WebAdministration ; ` Get-WebSite -Name 'Default Web Site' | Remove-WebSite -Confirm:$false -Verbose ; ` Remove-WebAppPool -Name "DefaultAppPool" -Confirm:$false -Verbose ;
-
Run the following command to create a new application pool named
DefaultAppPool
to be used by the web application. The application is copied to theC:\inetpub\wwwroot\webapp
directory.RUN Import-Module WebAdministration ; ` $wwwRoot = $env:SystemDrive + '\inetpub\wwwroot' ; ` $appName = 'webapp' ; ` $appPoolName = 'DefaultAppPool' ; ` $runtimeVersion = 'v4.0' ; ` $identity = 'ApplicationPoolIdentity' ; ` $pipelineMode = 'Integrated' ; ` $bindings=@( @{protocol='http' ; bindingInformation='*:80:'} ) ; ` New-Item -Path "IIS:\AppPools" -Name "$appPoolName" -Type AppPool ; ` Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name managedRuntimeVersion -Value "$runtimeVersion" ; ` Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name processModel -Value @{'identitytype' = $identity} ; ` Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name managedPipelineMode -Value "$pipelineMode" ; ` New-Website -Name $appName -ApplicationPool $appPoolName -PhysicalPath $wwwRoot\$appName ; ` Set-ItemProperty -Path "IIS:\Sites\$appName" -Name bindings -Value $bindings ; ` Start-IISSite -Name "$appName" ;
Install the ASP.NET Core web application
Before you start
The web application must use the default application pool name DefaultAppPool
. If any other name is used for the application pool, the process ServiceMonitor.exe
does not pass the environment variables that are declared when the container is started. As a result, environment variables, such as SW_APM_SERVICE_KEY
, passed in with the option -e
when the docker container is started are not seen by the SolarWinds Observability .NET Library.
See the Microsoft IIS Service Monitor readme, as well as the Windows service installed in docker container not using Environment Variables closed issue for more information.
Set up the web application
-
Run the following commands to install the ASP.NET Core Hosting Module for an ASP.NET Core 6.0 application.
If you are using a different ASP.NET Core version, replace the download link in the first line of the command with the correct download link for your ASP.NET Core version.
RUN Invoke-WebRequest https://download.visualstudio.microsoft.com/download/pr/d7124775-38c9-460f-a269-7bc131b3dfbf/7f60bcc6030e408cf11a935d5451ffa4/dotnet-hosting-6.0.20-win.exe -OutFile c:\dotnet-hosting-win.exe ; ` $appRoot = $env:SystemDrive ; ` cd $appRoot ; ` $processStartInfo = New-Object System.Diagnostics.ProcessStartInfo ; ` $arguments = '/install /quiet /norestart /log install.hosting.log' ; ` $installer = $appRoot + '\dotnet-hosting-win.exe' ; ` $processStartInfo.FileName = $installer ; ` $processStartInfo.Arguments = $arguments ; ` $process = New-Object System.Diagnostics.Process ; ` $process.StartInfo = $processStartInfo ; ` $process.Start() | Out-Null ; ` $process.WaitForExit() ; ` $result = $process.ExitCode ; ` echo "result: $result" ; ` if ($result -ne 0) { ` echo "Failed to install ASP.NET Core Hosting Bundle." ; ` exit $result; ` } ; ` echo "ASP.NET Core Hosting Bundle installed." ; ` Remove-Item -Force C:\dotnet-hosting-win.exe; ` Remove-Item -Force -Recurse $env:Temp\* # Check that AspNetCoreModuleV2 IIS Module is installed RUN Import-Module WebAdministration ; ` $modules = (Get-WebConfiguration //modules -PSPath iis:).collection ; ` $modules | foreach {if ($_.name -eq '"AspNetCoreModuleV2"') { $_.attributes | select name, value}} ;
-
Copy the application binaries to a folder inside the
C:\inetpub\wwwroot\webapp
directory.COPY webapp/ /inetpub/wwwroot/webapp/
-
Run the following command to delete the
Default Web Site
web site and the default application poolDefaultAppPool
.RUN Import-Module WebAdministration ; ` Get-WebSite -Name 'Default Web Site' | Remove-WebSite -Confirm:$false -Verbose ; ` Remove-WebAppPool -Name "DefaultAppPool" -Confirm:$false -Verbose ;
-
Run the following command to create a new application pool named
DefaultAppPool
to be used by the web application. The application is copied to theC:\inetpub\wwwroot\webapp
directory.The runtime for the application pool should be set to
No Managed Code
.RUN Import-Module WebAdministration ; ` $wwwRoot = $env:SystemDrive + '\inetpub\wwwroot' ; ` $appName = 'webapp' ; ` $appPoolName = 'DefaultAppPool' ; ` $runtimeVersion = '' ; ` $identity = 'ApplicationPoolIdentity' ; ` $pipelineMode = 'Integrated' ; ` $bindings=@( @{protocol='http' ; bindingInformation='*:80:'} ) ; ` New-Item -Path "IIS:\AppPools" -Name "$appPoolName" -Type AppPool ; ` Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name managedRuntimeVersion -Value "$runtimeVersion" ; ` Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name processModel -Value @{'identitytype' = $identity} ; ` Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name managedPipelineMode -Value "$pipelineMode" ; ` New-Website -Name $appName -ApplicationPool $appPoolName -PhysicalPath $wwwRoot\$appName ; ` Set-ItemProperty -Path "IIS:\Sites\$appName" -Name bindings -Value $bindings ; ` Start-IISSite -Name "$appName" ;
Troubleshooting
Set debug log level inside configuration file
To aid in troubleshooting issues, set the debug log level for SolarWinds Observability SaaS inside the image.
RUN $installDir = $env:SW_APM_HOME_DOTNET ; `
$configFile = $installDir + '\solarwinds_apm.config' ; `
$xml = [xml](Get-Content $configFile) ; `
$newNode = $xml.CreateElement('"add"') ; `
$xmlAtt = $xml.CreateAttribute('"key"') ; `
$xmlAtt.value = '"LogLevel"' ; `
$newNode.Attributes.Append($xmlAtt) ; `
$xmlAtt = $xml.CreateAttribute('"value"') ; `
$xmlAtt.value = '"Debug"' ; `
$newNode.Attributes.Append($xmlAtt); `
$xml.configuration.appSettings.AppendChild($newNode) ; `
$xml.Save($configFile);
The scripts are not supported under any SolarWinds support program or service. The scripts are provided AS IS without warranty of any kind. SolarWinds further disclaims all warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The risk arising out of the use or performance of the scripts and documentation stays with you. In no event shall SolarWinds or anyone else involved in the creation, production, or delivery of the scripts be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the scripts or documentation.