From 9c7e653ca85d6b9f885cbf0990033013cb28c851 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Fri, 6 Sep 2024 21:21:39 +0200 Subject: [PATCH] k8s operator example and dev docs. Ensuring our container for our autoinstrumentation distribution loads correctly using the OpenTelemetry k8s Operator --- Elastic.OpenTelemetry.sln | 8 ++ examples/Example.AspNetCore.Mvc/Dockerfile | 25 ++++ .../Example.AspNetCore.Mvc.csproj | 7 + examples/k8s/README.md | 128 ++++++++++++++++++ examples/k8s/elastic-otel-dotnet.yml | 39 ++++++ examples/k8s/my-dotnet-application.yml | 12 ++ 6 files changed, 219 insertions(+) create mode 100644 examples/Example.AspNetCore.Mvc/Dockerfile create mode 100644 examples/k8s/README.md create mode 100644 examples/k8s/elastic-otel-dotnet.yml create mode 100644 examples/k8s/my-dotnet-application.yml diff --git a/Elastic.OpenTelemetry.sln b/Elastic.OpenTelemetry.sln index 58b5a84..a94c588 100644 --- a/Elastic.OpenTelemetry.sln +++ b/Elastic.OpenTelemetry.sln @@ -47,6 +47,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoInstrumentation.Integra EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elastic.OpenTelemetry.AutoInstrumentation", "src\Elastic.OpenTelemetry.AutoInstrumentation\Elastic.OpenTelemetry.AutoInstrumentation.csproj", "{B1CA9165-89D9-4D6E-AFEF-5434A8D8A672}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "k8s", "k8s", "{21E61166-2640-4E38-8108-1BA510C07110}" + ProjectSection(SolutionItems) = preProject + examples\k8s\elastic-otel-dotnet.yml = examples\k8s\elastic-otel-dotnet.yml + examples\k8s\my-dotnet-application.yml = examples\k8s\my-dotnet-application.yml + examples\k8s\README.md = examples\k8s\README.md + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -130,6 +137,7 @@ Global {F3AA76EC-C7D8-42DA-947D-4376B6562772} = {4E95C87B-655B-4BC3-8F2A-DF06B7AAB7E9} {782E4DC1-8186-4BAC-B2F4-89E6DF22A4DD} = {AAD39891-0B70-47FA-A212-43E1AAE5DF56} {B1CA9165-89D9-4D6E-AFEF-5434A8D8A672} = {E622CFF2-C6C4-40FB-BE42-7C4F2B38B75A} + {21E61166-2640-4E38-8108-1BA510C07110} = {4E95C87B-655B-4BC3-8F2A-DF06B7AAB7E9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {573B2B5F-8CBB-4D52-A55A-4E65E282AAFB} diff --git a/examples/Example.AspNetCore.Mvc/Dockerfile b/examples/Example.AspNetCore.Mvc/Dockerfile new file mode 100644 index 0000000..ddf5378 --- /dev/null +++ b/examples/Example.AspNetCore.Mvc/Dockerfile @@ -0,0 +1,25 @@ +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +USER $APP_UID +WORKDIR /app +EXPOSE 8080 +EXPOSE 8081 + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["examples/Example.AspNetCore.Mvc/Example.AspNetCore.Mvc.csproj", "examples/Example.AspNetCore.Mvc/"] +COPY ["src/Elastic.OpenTelemetry/Elastic.OpenTelemetry.csproj", "src/Elastic.OpenTelemetry/"] +COPY ["examples/ServiceDefaults/ServiceDefaults.csproj", "examples/ServiceDefaults/"] +RUN dotnet restore "examples/Example.AspNetCore.Mvc/Example.AspNetCore.Mvc.csproj" +COPY . . +WORKDIR "/src/examples/Example.AspNetCore.Mvc" +RUN dotnet build "Example.AspNetCore.Mvc.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "Example.AspNetCore.Mvc.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Example.AspNetCore.Mvc.dll"] diff --git a/examples/Example.AspNetCore.Mvc/Example.AspNetCore.Mvc.csproj b/examples/Example.AspNetCore.Mvc/Example.AspNetCore.Mvc.csproj index ac48547..837362b 100644 --- a/examples/Example.AspNetCore.Mvc/Example.AspNetCore.Mvc.csproj +++ b/examples/Example.AspNetCore.Mvc/Example.AspNetCore.Mvc.csproj @@ -4,6 +4,7 @@ net8.0 enable enable + Linux @@ -15,4 +16,10 @@ + + + .dockerignore + + + diff --git a/examples/k8s/README.md b/examples/k8s/README.md new file mode 100644 index 0000000..833d3b8 --- /dev/null +++ b/examples/k8s/README.md @@ -0,0 +1,128 @@ +# Run Elastic Distribution of OpenTelemetry .NET on k8s + +The following documents how to auto instrument using the OpenTelemetry k8s Operator. + +First create a namespace for your k8s deployment: + +```bash +kubectl create namespace my-dotnet-ns +``` + +Next up we'll set our Elastic Cloud endpoint and key as k8s secrets. + +```bash +kubectl create secret generic elastic-otel -my-dotnet-ns \ + "--from-literal=endpoint=" \ + "--from-literal=apiKey=Authorization=Bearer " +``` + +next create an `Instrumentation` resource by creating an [`elastic-otel-dotnet.yml`](elastic-otel-dotnet.yml) file. + +Then apply it to create it in our namespace + +```bash +kubectl apply -f elastic-otel-dotnet.yml -n my-dotnet-ns +``` + +We then edit the namespace to make sure our Instrumentation annotations get applied always: + +```bash +kubectl edit namespace my-dotnet-ns +``` +ensure the following `instrumentation` gets added under `metadata>annotations` + +```yml +apiVersion: v1 +kind: Namespace +metadata: + annotations: + instrumentation.opentelemetry.io/inject-dotnet: elastic-otel-dotnet +``` + +we can now create our pod containing our dotnet image. + +To add your containerized image create a new `my-dotnet-application.yml` file + +```yml +apiVersion: v1 +kind: Pod +metadata: + name: my-dotnet-application + namespace: my-dotnet-application + labels: + app: my-dotnet-application +spec: + containers: + - image: _YOUR_APPLICATIONS_DOCKER_URL_ + imagePullPolicy: Always + name: my-dotnet-application +``` + +We can then spin up this pod by applying the template + +```bash +kubectl apply -f my-dotnet-application.yml -n my-dotnet-ns +``` + +Once spun up we can query the logs with + +```bash +kubectl logs my-dotnet-application -n my-dotnet-ns +``` + +It should print the Elastic Distribution of OpenTelemetry .NET preamble + +```log +[2024-09-06 18:49:36.011][00001][------][Information] Elastic Distribution of OpenTelemetry .NET: 1.0.0-alpha.6.1 +``` + +TIP: you can expose this pod locally to your host using: + +```bash +kubectl port-forward -n my-dotnet-ns pods/my-dotnet-application 8081:8080 +``` + +Here we forward the container port `8080` to your local port `8081` allowing you to browse your application. + + + + +### Use a local image as pod image + +Useful when developing. + +```bash +docker build . -t asp-net-example -f examples/Example.AspNetCore.Mvc/Dockerfile +minikube image load asp-net-example:latest --daemon +``` + +This ensures minikube can resolve `asp-net-example:latest`, you can now update your +application spec section to: + +```yml +spec: + containers: + - image: asp-net-example:latest + imagePullPolicy: Never + name: asp-net-example +``` + +NOTE: make sure `imagePullPolicy` is set to `Never` + + +### Debug deployments + +The `describe` command is great to validate the init-container ran and exposed +all the necessary environment variables. + +```bash +kubectl describe pod my-dotnet-application -n my-dotnet-ns +``` + +You can use `exec` to inspect the container to see if it matches your expectations. +```log +kubectl exec my-dotnet-application -n my-dotnet-ns -- ls -la /otel-auto-instrumentation-dotnet +``` + + + diff --git a/examples/k8s/elastic-otel-dotnet.yml b/examples/k8s/elastic-otel-dotnet.yml new file mode 100644 index 0000000..f157bd7 --- /dev/null +++ b/examples/k8s/elastic-otel-dotnet.yml @@ -0,0 +1,39 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: Instrumentation +metadata: + name: elastic-otel-dotnet + namespace: my-dotnet-application +spec: + env: + - name: OTEL_EXPORTER_OTLP_ENDPOINT + valueFrom: + secretKeyRef: + name: elastic-otel + key: endpoint + exporter: + endpoint: $OTEL_EXPORTER_OTLP_ENDPOINT + propagators: + - tracecontext + - baggage + - b3 + sampler: + type: parentbased_traceidratio + argument: "1.0" + dotnet: + image: docker.elastic.co/observability/elastic-otel-dotnet:edge + env: + - name: OTEL_EXPORTER_OTLP_HEADERS + valueFrom: + secretKeyRef: + name: elastic-otel + key: apiKey + - name: OTEL_LOG_LEVEL + value: "info" + - name: ELASTIC_OTEL_LOG_TARGETS + value: "file;stdout" + - name: OTEL_DOTNET_AUTO_LOG_DIRECTORY + value: "/otel-auto-instrumentation-dotnet" + - name: OTEL_DOTNET_AUTO_RULE_ENGINE_ENABLED + value: "false" + + diff --git a/examples/k8s/my-dotnet-application.yml b/examples/k8s/my-dotnet-application.yml new file mode 100644 index 0000000..d2b8f71 --- /dev/null +++ b/examples/k8s/my-dotnet-application.yml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Pod +metadata: + name: my-dotnet-application + namespace: my-dotnet-application + labels: + app: my-dotnet-application +spec: + containers: + - image: asp-net-example:latest + imagePullPolicy: Never + name: asp-net-example