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
Once you have deployed the Python Agent, see Validate the Python Agent Install.
Use a Dockerfile
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:
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
Perform the PIP Install
Run the pip install appdynamics Install the Python Agent. Specify the version of the latest AppDynamics Python project. For example:
RUN pip install -U appdynamics==21.12.2.4693 -r requirements.txt
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 APPDYNAMICS_CONTROLLER_SSL_CERTFILE
For example:
COPY ./onprem-cacerts /opt/appdynamics/cacerts
ENV APPDYNAMICS_CONTROLLER_SSL_CERTFILE=/opt/appdynamics/cacerts
Use Pyagent to Run the Application
Use pyagent --use-manual-proxy Install the Python Agent for additional support of WSGI frameworks. Include the
CMD pyagent run --use-manual-proxy python ./app.py
A complete Dockerfile Pyagent example:
FROM python:3.7
# 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
Set the Python Agent Environment Variables
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
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>
ENV APPDYNAMICS_AGENT_CONTAINER_ENABLED=true
For Kubernetes applications, set these environment variables using configmaps, secrets, and the deployment spec as described in Best Practices to Configure Agents in Kubernetes.
-
Use a configmap appd-python-config.yaml
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
-
Apply the configmap
$ kubectl -n <app-ns> apply -f appd-python-config.yaml
-
Update the deployment spec to reference the configmap
spec: containers: - name: python-app envFrom: - configMapRef: name: appd-python-config ...
-
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>
-
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 ...
-
Set the application-specific tier name environment variable APPDYNAMICS_AGENT_TIER_NAME
spec: containers: - name: python-app env: - name: APPDYNAMICS_AGENT_TIER_NAME value: python-service - name: APPDYNAMICS_AGENT_REUSE_NODE_NAME_PREFIX value: python-service ...
-
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 ...
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: {}
Use Init Containers
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 APP_ENTRY_POINT
To instrument a Python application using init containers:
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.7
COPY mypythonapp/ /app/
WORKDIR /app
RUN chmod +x ./app.py
EXPOSE 8080
RUN pip install -r requirements.txt
CMD pyagent run python ./app.py
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
-
Line 34: APP_ENTRY_POINT APPDYNAMICS_AGENT_VERSION run-with-agent.sh. APPDYNAMICS_AGENT_VERSION PyPi AppDynamics release page.
Use the following code samples depending on your proxy type:
- Dynamic Languages Proxy (Manual)
-
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: "--use-manual-proxy 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: {}] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: pod-reader-binding namespace: default subjects: - kind: ServiceAccount name: default namespace: default roleRef: kind: Role name: pod-reader apiGroup: rbac.authorization.k8s.io
- Default Proxy
-
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 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: {} --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: pod-reader-binding namespace: default subjects: - kind: ServiceAccount name: default namespace: default roleRef: kind: Role name: pod-reader apiGroup: rbac.authorization.k8s.io
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.