This page explains your options to install the Python Agent in a containerized application.

When deploying the Python Agent in a containerized application, you need to deploy both the Dynamic Languages Proxy and the Python Agent:

Deploy the Dynamic Languages Proxy

The Dynamic Languages Proxy acts as a communication gateway between the Python Agent running in the application process and the AppDynamics Controller.

You can use one of two available options to deploy the Dynamic Languages Proxy, either the:

  • Sidecar Container (recommended): This option follows the best practice of one process per container. The Dynamic Language Proxy runs in a sidecar container separate from the application container, or
  • Application Container: This option uses the default configuration to run the proxy in the same container as the application.

Deploy Dynamic Languages Proxy in a Sidecar Container (recommended)

AppDynamics recommends following the best practice of maintaining one process per container.

  1. To use a sidecar container to run the Dynamic Languages Proxy, add the --use-manual-proxy parameter when starting the Python application.

    pyagent run --use-manual-proxy python ./app.py
    BASH

    The --use-manual-proxy parameter prevents the Python Agent agent from starting the Dynamic Languages Proxy as a separate process in the same application container, which is the default behavior.

  2. Configure the deployed sidecar container to run the proxy. This Kubernetes deployment spec snippet shows how to define a second container named proxy that uses the dl-proxy image published on Docker Hub.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: mypython-app
    spec:
        <...>
        spec:
          containers:
            - name: mypython-app
              image: myrepo/python-app-with-appd:v1
              <...>
            - name: proxy
              image: docker.io/appdynamics/dl-proxy:latest
              imagePullPolicy: Always
              env:
                - name: APPDYNAMICS_DEBUG_LOG
                  value: "on"
                - name: APPDYNAMICS_LOGGING_LEVEL
                  value: "debug"
                - name: APPDYNAMICS_TCP_COMM_HOST
                  value: "0.0.0.0"
                - name: APPDYNAMICS_TCP_COMM_PORT
                  value: "9091"
                - name: APPDYNAMICS_TCP_PORT_RANGE
                  value: "10000-10100"
              ports:
                - containerPort: 9091
                  protocol: TCP
              resources:
         ...
    YML

Deploy Dynamic Languages Proxy in the Application Container

If you prefer to run the proxy as a separate process in the same container as the application, omit the --use-manual-proxy parameter when starting the Python application.

pyagent run python ./app.py
BASH

The Python Agent automatically starts a separate process for the proxy in the same container. Avoid adding a sidecar container to the deployment so only the application container is defined.

Deploy the Python Agent in the Application Container

To instrument a Python application, the required Python Agent packages must be deployed to the application container and loaded by the application at startup. The available options are:

  • Use a Dockerfile: This option uses a Dockerfile to deploy the Python Agent in the application Docker image at build time.
  • Use Init Containers: This option uses Kubernetes init containers to deploy the Python Agent into the application container when the application starts up. Using the init containers option does not require any changes to the application Docker image.

For each of these options, the steps assume the use of a sidecar to run the Dynamic Languages Proxy and that you have kubectl installed.

Once you have deployed the Python Agent, see Validate the Python Agent Install.

Note that Cluster Agent auto-instrumentation is not supported for Python applications running in Kubernetes clusters where the Cluster Agent is installed. See Container Installation Options.

Use a Dockerfile

This option applies to containers running in Docker and Kubernetes.

This option uses a Dockerfile to deploy the Python Agent in the Docker image at build time. The built Docker image must contain the application and the Python Agent. 

To deploy the agent in the application image during the image build:

  1. Copy the Application Files to the Image
  2. Perform the PIP Install
  3. Copy the Controller Certs to the Image (on-premises Controller only)
  4. Use Pyagent to Run the Application
  5. Set the Python Agent Environment Variables
  6. Validate the Python Agent Install

Copy the Application Files to the Image

Edit the Dockerfile to copy the application folder, and set up the requirements and start script:

COPY mypythonapp/ /app/
WORKDIR /app
COPY requirements.txt .
RUN chmod +x ./app.py
EXPOSE 8080
TEXT

Perform the PIP Install

Run the pip install command as described in Install the Python Agent. Specify the version of the latest appdynamics release from the AppDynamics Python project. For example:

RUN pip install -U appdynamics==21.12.2.4693 -r requirements.txt
TEXT

Copy the Controller Certs to the Image (On-Premises Controller Only)

For Python Agents communicating with an on-premises Controller, edit the Dockerfile to copy the cacerts file containing the on-premises certs to the image and set the APPDYNAMICS_CONTROLLER_SSL_CERTFILE environment variable. 

For example:

COPY ./onprem-cacerts /opt/appdynamics/cacerts
ENV APPDYNAMICS_CONTROLLER_SSL_CERTFILE=/opt/appdynamics/cacerts
BASH

Use Pyagent to Run the Application

Use pyagent to run the application based on the use of Flask or Django frameworks. See Install the Python Agent for additional support of WSGI frameworks. Include the --use-manual-proxy parameter to prevent the Dynamic Languages Proxy from starting in the application container.

CMD pyagent run --use-manual-proxy python ./app.py
TEXT

A complete Dockerfile Pyagent example:

FROM python:3.6

# Use latest version from https://pypi.org/project/appdynamics/#history
ENV APPD_AGENT_VERSION=21.12.2.4693

COPY mypythonapp/ /app/
WORKDIR /app
RUN chmod +x ./app.py

EXPOSE 8080

RUN pip install -U appdynamics==${APPD_AGENT_VERSION} -r requirements.txt

CMD pyagent run --use-manual-proxy python ./app.py
TEXT

Set the Python Agent Environment Variables

Note that APPDYNAMICS_TCP_COMM_PORT should be set to an available port in the application and proxy container and is used for the agent to proxy communication. See TCP Mode Configuration.

How you set the Python Agent environment variables depends on how you deploy the image. For a Docker deployment, set the agent environment variables in the Dockerfile, or external file you supply, as a parameter to docker run. For example:

ENV APPDYNAMICS_TCP_COMM_PORT=9091
ENV APPDYNAMICS_AGENT_APPLICATION_NAME=<value>
ENV APPDYNAMICS_AGENT_TIER_NAME=<value>
ENV APPDYNAMICS_AGENT_REUSE_NODE_NAME=true
ENV APPDYNAMICS_AGENT_REUSE_NODE_NAME_PREFIX=<value>
ENV APPDYNAMICS_AGENT_ACCOUNT_NAME=<value>
ENV APPDYNAMICS_AGENT_ACCOUNT_ACCESS_KEY=<value>
ENV APPDYNAMICS_CONTROLLER_HOST_NAME=<value>
ENV APPDYNAMICS_CONTROLLER_PORT=<value>
ENV APPDYNAMICS_CONTROLLER_SSL_ENABLED=<value>
TEXT

For Kubernetes applications, set these environment variables using configmaps, secrets, and the deployment spec as described in Best Practices to Configure Agents in Kubernetes.

  1. Use a configmap to set the agent environment variables that are shared across Python applications in a namespace, as shown in this appd-python-config.yaml example:

    apiVersion: v1
    data:
      APPDYNAMICS_TCP_COMM_PORT: "9091"
      APPDYNAMICS_AGENT_APPLICATION_NAME: "<value>"
      APPDYNAMICS_AGENT_REUSE_NODE_NAME: "<value>"
      APPDYNAMICS_AGENT_ACCOUNT_NAME: "<value>"
      APPDYNAMICS_CONTROLLER_HOST_NAME: "<value>"
      APPDYNAMICS_CONTROLLER_PORT: "<value>"
      APPDYNAMICS_CONTROLLER_SSL_ENABLED: "<value>"
    kind: ConfigMap
    metadata:
      name: appd-python-config 
    YML
  2. Apply the configmap to the application namespace:

    $ kubectl -n <app-ns> apply -f appd-python-config.yaml 
    BASH
  3. Update the deployment spec to reference the configmap:

    spec:
      containers:
      - name: python-app
        envFrom:
        - configMapRef:
            name: appd-python-config
     ...
    YML
  4. Create a secret for the Controller access key using kubectl:

    $ kubectl -n <app-ns> create secret generic appd-agent-secret --from-literal=access-key=<access-key>
    BASH
  5. Update the deployment spec to reference the secret:

    spec:
      containers:
      - name: python-app
        env:
        - name: APPDYNAMICS_AGENT_ACCOUNT_ACCESS_KEY
          valueFrom:
            secretKeyRef:
              name: appd-agent-secret
              key: access-key
     ...
    YML
  6. Set the application-specific tier name environment variable APPDYNAMICS_AGENT_TIER_NAME in the deployment spec:

    spec:
      containers:
      - name: python-app
        env:
        - name: APPDYNAMICS_AGENT_TIER_NAME
          value: python-service
        - name: APPDYNAMICS_AGENT_REUSE_NODE_NAME_PREFIX
          value: python-service
     ...
    YML
  7. Add the proxy container

    spec:
      containers:
        ...
        - name: proxy
          image: appdynamics/dl-proxy:latest
          imagePullPolicy: Always
          env:
            - name: APPDYNAMICS_DEBUG_LOG
              value: "on"
            - name: APPDYNAMICS_LOGGING_LEVEL
              value: "debug"
            - name: APPDYNAMICS_TCP_COMM_HOST
              value: "0.0.0.0"
            - name: APPDYNAMICS_TCP_COMM_PORT
              value: "9091"
            - name: APPDYNAMICS_TCP_PORT_RANGE
              value: "10000-10100"
          ports:
            - containerPort: 9091
              protocol: TCP
          resources:
            limits:
              cpu: 500m
              memory: 900M
            requests:
              cpu: 400m
              memory: 600M
     ...
    YML

    Below is a complete example of a Kubernetes Deployment spec:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: mypython-app
    spec:
      selector:
        matchLabels:
          name: mypython-app
      replicas: 1
      template:
        metadata:
          labels:
            name: mypython-app
        spec:
          containers:
            - name: mypython-app
              image: myrepo/python-app-with-appd:v1
              imagePullPolicy: Always
              env:
              - name: APPDYNAMICS_AGENT_TIER_NAME
                value: mypython-app
              - name: APPDYNAMICS_AGENT_REUSE_NODE_NAME_PREFIX
                value: python-service
              - name: APPDYNAMICS_AGENT_ACCOUNT_ACCESS_KEY
                valueFrom:
                  secretKeyRef:
                    key: access-key
                    name: appd-agent-secret
              envFrom:
                - configMapRef:
                    name: appd-python-config
              ports:
              - containerPort: 8080
            - name: proxy
              image: appdynamics/dl-proxy:latest
              imagePullPolicy: Always
              env:
                - name: APPDYNAMICS_DEBUG_LOG
                  value: "on"
                - name: APPDYNAMICS_LOGGING_LEVEL
                  value: "debug"
                - name: APPDYNAMICS_TCP_COMM_HOST
                  value: "0.0.0.0"
                - name: APPDYNAMICS_TCP_COMM_PORT
                  value: "9091"
                - name: APPDYNAMICS_TCP_PORT_RANGE
                  value: "10000-10100"
              ports:
                - containerPort: 9091
                  protocol: TCP
              resources:
                limits:
                  cpu: 500m
                  memory: 900M
                requests:
                  cpu: 400m
                  memory: 600M
          restartPolicy: Always
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: mypython-app
    spec:
      selector:
        name: mypython-app
      ports:
      - name: "8080"
        port: 8080
        targetPort: 8080
      type: LoadBalancer
    status:
      loadBalancer: {}
    YML

Use Init Containers

This option applies to containers running in Kubernetes.

This option uses Kubernetes init containers to install the Python Agent into the application container when the application starts up. The Use of init containers requires that the application image is built without the Python Agent. The benefit of this option is that it does not require changes to the application image.

The init container provides a startup script that is used to override the default application startup command. It runs pip to install the Python Agent dependencies in the application container and then starts the application based on the configured APP_ENTRY_POINT environment variable.

To instrument a Python application using init containers:

  1. Build the Application Image Without the Python Agent
  2. Add the Init Container to the Deployment Spec
  3. Set the Python Agent Environment Variables
  4. Validate the Python Agent Install

Build the Application Image Without the Python Agent

The following example is Dockerfile that builds an application image without including the Python Agent dependency.

FROM python:3.6

COPY mypythonapp/ /app/
WORKDIR /app
RUN chmod +x ./app.py

EXPOSE 8080

RUN pip install -r requirements.txt

CMD pyagent run python ./app.py
TEXT

Add the Init Container to the Deployment Spec

The deployment spec below illustrates the changes required to use an init container:

  • Line 16: The appd-python-init init container is defined and copies the contents of the init container to the application container.
  • Line 22: The init container references the python-agent-init image from Docker Hub.
  • Line 78: A volume mount is defined to share the contents of the init container with the application container.
  • Line 30: The application startup command is overridden to run the script run-with-agent.sh, which is copied from the init container.

  • Line 34: APP_ENTRY_POINT and APPDYNAMICS_AGENT_VERSION are defined on the application container to specify the app entry point and agent version used by run-with-agent.sh. APPDYNAMICS_AGENT_VERSION has the format a.b.c.d and is based on the version number listed on the PyPi AppDynamics release page.

    python-init-container.yaml

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: mypython-app-init
    spec:
      selector:
        matchLabels:
          name: mypython-app-init
      replicas: 1
      template:
        metadata:
          labels:
            name: mypython-app-init
        spec:
          initContainers:
          - name: appd-python-init
            command:
            - cp
            - -r
            - /opt/appdynamics/.
            - /opt/temp
            image: docker.io/appdynamics/python-agent-init:1.0
            imagePullPolicy: Always
            volumeMounts:
            - mountPath: /opt/temp
              name: appd-python-init
          containers:
            - name: mypython-app-init
              image: myrepo/python-app-no-appd:v1
              command: ["/bin/sh"]
              args: ["-c", "/opt/appdynamics-python/run-with-agent.sh"]
              imagePullPolicy: Always
              env:
              - name: APP_ENTRY_POINT
                value: "python /app/app.py"
              - name: APPDYNAMICS_AGENT_VERSION
                # Use latest version from https://pypi.org/project/appdynamics/#history
                value: 21.12.2.4693
              - name: APPDYNAMICS_AGENT_TIER_NAME
                value: mypython-app-init
              - name: APPDYNAMICS_AGENT_ACCOUNT_ACCESS_KEY
                valueFrom:
                  secretKeyRef:
                    key: access-key
                    name: appd-agent-secret
              envFrom:
                - configMapRef:
                    name: appd-python-config
              ports:
              - containerPort: 8080
              volumeMounts:
                - mountPath: /opt/appdynamics-python
                  name: appd-python-init
            - name: proxy
              image: appdynamics/dl-proxy:latest
              imagePullPolicy: Always
              env:
                - name: APPDYNAMICS_DEBUG_LOG
                  value: "on"
                - name: APPDYNAMICS_LOGGING_LEVEL
                  value: "debug"
                - name: APPDYNAMICS_TCP_COMM_HOST
                  value: "0.0.0.0"
                - name: APPDYNAMICS_TCP_COMM_PORT
                  value: "9091"
                - name: APPDYNAMICS_TCP_PORT_RANGE
                  value: "10000-10100"
              ports:
                - containerPort: 9091
                  protocol: TCP
              resources:
                limits:
                  cpu: 500m
                  memory: 900M
                requests:
                  cpu: 400m
                  memory: 600M
          volumes:
            - name: appd-python-init
              emptyDir: {}
          restartPolicy: Always
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: mypython-app-init
    spec:
      selector:
        name: mypython-app-init
      ports:
      - name: "8080"
        port: 8080
        targetPort: 8080
      type: LoadBalancer
    status:
      loadBalancer: {}
    YML


Set the Python Agent Environment Variables

When using an init container, set the Python Agent environment variables for Kubernetes as described in Use a Dockerfile.

Validate the Python Agent Install

Once you have deployed the Python application with the Python Agent and generated load on the application, the application should appear in the AppDynamics Controller based on the configured application, tier, and node names. To troubleshoot communication issues between the agent and the proxy, or proxy and Controller log on to the application and proxy containers. Note that logs will not appear until the application process receives requests.

# check that the app and proxy containers are running
kubectl -n <app ns> get pods
NAME                                 READY   STATUS    RESTARTS   AGE
mypython-app-7687956c77-q5z85        2/2     Running   0          40s

# if both containers aren't running, check events regarding image pulls, etc
kubectl -n <app ns> get events

# check the Python Agent logs in the application container for issues communicating with the proxy running in the sidecar
kubectl -n <app ns> exec -it <pod name> -c <app container name> /bin/bash
cd /tmp/appd/logs
cat <app-node names>.log

# check the proxy logs for issues communicating with the AppDynamics Controller
kubectl -n <app ns> exec -it <pod name> -c proxy /bin/bash
cd /tmp/appd/logs/<app folder>
cat agent.<timestamp>.log
BASH