Link Search Menu Expand Document

1.1 Introduction

For this article I document instrumenting a basic dotnet6 application with a sql backend, hosted as a docker container using the sample eShopOnWeb application.

If this is your first foray into AppDynamics or, you just want to step through some structured self-paced labs, I recommend checking out the self-paced labs Appdynamics on DevNet

While this mostly follows the official documentation noted below - there were a few caveats, hurdles and a bit of trial and error to get things working, hopefully this will save someone time!

1.2 References


1.3 Download the eShop Web github repo

Create the directory for our sandbox:

$ mkdir dotnet6-sandbox

Pull the source code for the eShopOnWeb application:

$ cd dotnet6-sandbox/
$ git init
$ git pull https://github.com/dotnet-architecture/eShopOnWeb.git

1.4 Edit the docker files to install the AppDynamics Agent

As noted in the installation documents - download and unzip the .NET Agent for linux:

$ curl -L -O -H "Authorization: Bearer < blah >" "https://download.appdynamics.com/download/prox/download-file/dotnet-core/22.10.0/AppDynamics-DotNetCore-linux-x64-22.10.0.zip"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:02 --:--:--     0
100 6845k  100 6845k    0     0  1846k      0  0:00:03  0:00:03 --:--:-- 8504k

$ unzip AppDynamics-DotNetCore-linux-x64-22.10.0.zip -d AppDynamics-DotNetCore-linux-x64

Edit the dockerfile with the information needed to connect to the AppDynamics controller:

$ cat src/Web/Dockerfile
# RUN ALL CONTAINERS FROM ROOT (folder with .sln file):
# docker-compose build
# docker-compose up
#
# RUN JUST THIS CONTAINER FROM ROOT (folder with .sln file):
# docker build --pull -t web -f src/Web/Dockerfile .
#
# RUN COMMAND
#  docker run --name eshopweb --rm -it -p 5106:5106 web
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /app

COPY *.sln .
COPY . .
WORKDIR /app/src/Web
RUN dotnet restore

RUN dotnet publish -c Release -o out

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS runtime
WORKDIR /app
COPY --from=build /app/src/Web/out ./

# Optional: Set this here if not setting it from docker-compose.yml
# ENV ASPNETCORE_ENVIRONMENT Development

ENTRYPOINT ["dotnet", "Web.dll"]
RUN mkdir /opt/appdynamics/
COPY AppDynamics-DotNetCore-linux-x64/ /opt/appdynamics/
ENV CORECLR_PROFILER="{57e1aa68-2229-41aa-9931-a6e93bbc64d8}"
ENV CORECLR_ENABLE_PROFILING=1
ENV CORECLR_PROFILER_PATH="/opt/appdynamics/libappdprofiler.so"
ENV APPDYNAMICS_AGENT_APPLICATION_NAME="z_jamie_dotnet_eShopOnWeb"
ENV APPDYNAMICS_AGENT_TIER_NAME="Web_Front_End"
ENV APPDYNAMICS_AGENT_ACCOUNT_NAME="example"
ENV APPDYNAMICS_AGENT_ACCOUNT_ACCESS_KEY="your-access-key"
ENV APPDYNAMICS_CONTROLLER_HOST_NAME="example.saas.appdynamics.com"
ENV APPDYNAMICS_CONTROLLER_PORT=443    
ENV APPDYNAMICS_CONTROLLER_SSL_ENABLED=true   
ENV APPDYNAMICS_AGENT_REUSE_NODE_NAME=true
ENV APPDYNAMICS_AGENT_REUSE_NODE_NAME_PREFIX="jamies-instance" 
ENV LD_LIBRARY_PATH=/opt/appdynamics/dotnet
# variables required to send transaction analytics data
ENV APPDYNAMICS_ANALYTICS_HOST_NAME="example.saas.appdynamics.com"
ENV APPDYNAMICS_ANALYTICS_PORT=443
ENV APPDYNAMICS_ANALYTICS_SSL_ENABLED=true

Edit the docker-compose file, in my case I needed to:

  • Hard code DNS server in the hosts /etc/resolve.conf file (I only allow DNS to Cisco umbrella DNS servers on my home network)
  • Set the network mode to bridge to allow outside (i.e 443 to the AppD controller)
  • Setting the network mode to bridge allows access to the outside network, but breaks connections between containers - set the “links” parameter to allow the front end container to talk to the sql-server container
$ cat docker-compose.yml 
version: '3.4'

services:
  eshopwebmvc:
    image: ${DOCKER_REGISTRY-}eshopwebmvc
    build:
      context: .
      dockerfile: src/Web/Dockerfile
    links:
      - "sqlserver"
    depends_on:
      - "sqlserver"
    dns:
      - 208.67.220.220
      - 208.67.222.222
      - 8.8.8.8
    network_mode: "bridge"
  eshoppublicapi:
    image: ${DOCKER_REGISTRY-}eshoppublicapi
    build:
      context: .
      dockerfile: src/PublicApi/Dockerfile
    depends_on:
      - "sqlserver"
    dns:
      - 208.67.220.220
      - 208.67.222.222
      - 8.8.8.8
    network_mode: "bridge"
  sqlserver:
    image: mcr.microsoft.com/azure-sql-edge
    ports:
      - "1433:1433"
    environment:
      - SA_PASSWORD=@someThingComplicated1234
      - ACCEPT_EULA=Y
    dns:
      - 208.67.220.220
      - 208.67.222.222
      - 8.8.8.8
    network_mode: "bridge"

The directory structure should resemble below - note the locations of the AppDynamics agent directory, docker-compose.yml and Dockerfile:

.
├── AppDynamics-DotNetCore-linux-x64
│   ├── AppDynamics.Agent.netstandard.dll
│   ├── AppDynamics.Agent.OtelSDK.dll
│   ├── AppDynamicsConfig.json.template
│   ├── libappdprofiler_glibc.so
│   ├── libappdprofiler_musl.so
│   ├── libappdprofiler.so
│   └── README.md
├── AppDynamics-DotNetCore-linux-x64-22.10.0.zip
├── CodeCoverage.runsettings
├── docker-compose.bak
├── docker-compose.dcproj
├── docker-compose.override.yml
├── docker-compose.yml <-- Docker-compose referenced above
├── eShopOnWeb.sln
├── global.json
├── LICENSE
├── README.md
├── src
│   ├── ApplicationCore
│   ├── BlazorAdmin
│   ├── BlazorShared
│   ├── Infrastructure
│   ├── PublicApi
│   └── Web
│       ├── Dockerfile <-- DockerFile referenced above
└── tests

1.4 Build and Start the Docker Containers

Let’s try building the docker images:

$ sudo docker-compose build
$ sudo docker-compose up

Check the status of our containers:

$ sudo docker ps
[sudo] password for jamie: 
CONTAINER ID   IMAGE                              COMMAND                  CREATED         STATUS         PORTS                                                 NAMES
5626af813ddc   eshopwebmvc                        "dotnet Web.dll"         2 minutes ago   Up 2 minutes   0.0.0.0:5106->80/tcp, :::5106->80/tcp                 dotnet6-sandbox_eshopwebmvc_1
5243ba9f4447   eshoppublicapi                     "dotnet PublicApi.dll"   2 minutes ago   Up 2 minutes   443/tcp, 0.0.0.0:5200->80/tcp, :::5200->80/tcp        dotnet6-sandbox_eshoppublicapi_1
de9f3931bfb8   mcr.microsoft.com/azure-sql-edge   "/opt/mssql/bin/perm…"   2 minutes ago   Up 2 minutes   1401/tcp, 0.0.0.0:1433->1433/tcp, :::1433->1433/tcp   dotnet6-sandbox_sqlserver_1

Check the network details of our front end web-site container:

$ sudo docker inspect 5626af813ddc | grep IPAdd
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.4",
                    "IPAddress": "172.17.0.4",

You should be able to browse to the site on the above IP address:

Front-End

1.5 Using Apache JMeter to Generate Traffic

Here we use the free Apache JMeter utility to generate traffic to the eshop website.

Download using the link above (note the Java 1.8+ requirement) and run:

$ cd ~/Downloads/apache-jmeter-5.5/
$ cd bin/
$ ./jmeter.sh

I’ve setup some basic HTTP Requests as below. Note the server name or IP and Paths fields:

Front-End

All going well, we’ll now see the application flow map and business transactions populate in the controller

Flow Map: Flow-Map

Business Transactions: Business-Transactions

What have we achieved?

  • Deployed a multi-tiered dotnet6 application on docker
  • Instrumented with AppDynamics for code level visibility and application performance monitoring

More to follow.