diff --git a/trace/cloud-trace-demo-app-opentelemetry/README.md b/trace/cloud-trace-demo-app-opentelemetry/README.md new file mode 100644 index 00000000000..01e5ed251a0 --- /dev/null +++ b/trace/cloud-trace-demo-app-opentelemetry/README.md @@ -0,0 +1,74 @@ +# cloud-trace-demo-app-opentelemetry + +Open this demo app in [Google Cloud Shell](https://cloud.google.com/shell/docs/). This includes necessary tools. + + +[![Open Cloud Trace Demo APP in Cloud Shell](http://gstatic.com/cloudssh/images/open-btn.svg)](https://console.cloud.google.com/cloudshell/open?cloudshell_git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=trace/cloud-trace-demo-app/README.md&cloudshell_tutorial=trace/cloud-trace-demo-app/README.md) + +#### Demo Requirements +If you are using Cloud Shell, skip to the next section. + +1. Install gcloud +2. Install kubectl +3. Install docker + + +#### Create a GKE cluster + +4. Enable Google Cloud and set up region and zone. + + `gcloud init` +5. Enable the GKE API & billing: + + `gcloud services enable container.googleapis.com` +6. Create a GKE cluster named "cloud-trace-demo", replacing `your-gcp-zone` below +with the +[GCP Zone](https://cloud.google.com/compute/docs/regions-zones) closest in proximity to you: + + ``` + gcloud container clusters create cloud-trace-demo\` + --num-nodes 1 \ + --enable-basic-auth \ + --issue-client-certificate \ + --zone your-gcp-zone + ``` +7. Verify that you have access to the cluster: + + `kubectl get nodes` + +#### Deploy The Cloud Trace Demo App + +8. Build and tag the docker image for demo app: + + `docker build -t gcr.io/${PROJECT_ID}/cloud-trace-demo:v1 .` +9. Deploy resource to the cluster: + + `kubectl apply -f deployment.yaml` +10. Track the status of the deployment: + + `kubectl get deployments` + + Deployment is complete when all of the available deployments are ready. +11. Run the following command to see the pods the deployment created: + + `kubectl get pods` + +#### Deploy The Cloud Trace Demo Service + +12. Create the cloud trace demo service: + + `kubectl apply -f service.yaml` +13. Get the services IP address by running the following command: + + `kubectl get services` +14. Send a curl request to the EXTERNAL_IP, replacing `EXTERNAL_IP` with the external IP address found +in step 13: + + `curl EXTERNAL_IP` +15. Visit [Trace List](https://console.cloud.google.com/traces/list) to check traces generated. + Click on any trace in the graph to see the Waterfall View. + + ![Screenshot](example-trace.png) +16. Clean up GKE cluster/pods/services: + + `gcloud container clusters delete cloud-trace-demo` diff --git a/trace/cloud-trace-demo-app-opentelemetry/app/Dockerfile b/trace/cloud-trace-demo-app-opentelemetry/app/Dockerfile new file mode 100644 index 00000000000..f27cd3e1f35 --- /dev/null +++ b/trace/cloud-trace-demo-app-opentelemetry/app/Dockerfile @@ -0,0 +1,13 @@ +# Use the official lightweight Python image. +# https://hub.docker.com/_/python +FROM python:3.7-slim + +# Copy local code to the container image. +ENV APP_HOME /app +WORKDIR $APP_HOME +COPY . ./ +COPY requirements.txt /app/ +RUN pip install Flask gunicorn +RUN pip install -r requirements.txt + +CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 app:app diff --git a/trace/cloud-trace-demo-app-opentelemetry/app/app.py b/trace/cloud-trace-demo-app-opentelemetry/app/app.py new file mode 100644 index 00000000000..98d2085e47d --- /dev/null +++ b/trace/cloud-trace-demo-app-opentelemetry/app/app.py @@ -0,0 +1,57 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +A sample app demonstrating CloudTraceSpanExporter +""" +import random +import time + +# [START trace_demo_imports] +import flask + +from opentelemetry import trace +from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import SimpleExportSpanProcessor + +# [END trace_demo_imports] + +app = flask.Flask(__name__) + + +def configure_exporter(exporter): + trace.set_tracer_provider(TracerProvider()) + + trace.get_tracer_provider().add_span_processor(SimpleExportSpanProcessor(exporter)) + + +tracer = trace.get_tracer(__name__) + + +@app.route("/") +def template_test(): + # Sleep for a random time to imitate a random processing time + time.sleep(random.uniform(0, 0.5)) + + with tracer.start_as_current_span("span1"): + with tracer.start_as_current_span("span2"): + with tracer.start_as_current_span("span3"): + print("Hello world from Cloud Trace Exporter!") + + return "Hello World" + + +if __name__ == "__main__": + app.run(debug=True, host="0.0.0.0", port=8080) + configure_exporter(CloudTraceSpanExporter()) diff --git a/trace/cloud-trace-demo-app-opentelemetry/app/app_test.py b/trace/cloud-trace-demo-app-opentelemetry/app/app_test.py new file mode 100644 index 00000000000..8d4456f5aa7 --- /dev/null +++ b/trace/cloud-trace-demo-app-opentelemetry/app/app_test.py @@ -0,0 +1,27 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +A sample app demonstrating Stackdriver Trace +""" +import mock + +import app + + +def test_traces(): + exporter = mock.Mock() + app.configure_exporter(exporter) + client = app.app.test_client() + resp = client.get("/") + assert resp.status_code == 200 diff --git a/trace/cloud-trace-demo-app-opentelemetry/app/deployment.yaml b/trace/cloud-trace-demo-app-opentelemetry/app/deployment.yaml new file mode 100644 index 00000000000..1cf1c055da5 --- /dev/null +++ b/trace/cloud-trace-demo-app-opentelemetry/app/deployment.yaml @@ -0,0 +1,25 @@ +# This file configures the hello-world app which serves public web traffic. +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: cloud-trace-demo +spec: + replicas: 1 + selector: + matchLabels: + app: trace-demo + template: + metadata: + labels: + app: trace-demo + spec: + containers: + - name: trace-demo-app + # Replace $GCLOUD_PROJECT with your project ID + image: gcr.io/$GCLOUD_PROJECT/cloud-trace-demo:latest + # This app listens on port 8080 for web traffic by default. + ports: + - containerPort: 8080 + env: + - name: PORT + value: "8080" \ No newline at end of file diff --git a/trace/cloud-trace-demo-app-opentelemetry/app/requirements-test.txt b/trace/cloud-trace-demo-app-opentelemetry/app/requirements-test.txt new file mode 100644 index 00000000000..41c4d511053 --- /dev/null +++ b/trace/cloud-trace-demo-app-opentelemetry/app/requirements-test.txt @@ -0,0 +1,2 @@ +pytest==5.3.2 +mock==3.0.5 diff --git a/trace/cloud-trace-demo-app-opentelemetry/app/requirements.txt b/trace/cloud-trace-demo-app-opentelemetry/app/requirements.txt new file mode 100644 index 00000000000..337722abe56 --- /dev/null +++ b/trace/cloud-trace-demo-app-opentelemetry/app/requirements.txt @@ -0,0 +1,15 @@ +Flask==1.1.2 +opentelemetry-api==0.9b0 +opentelemetry-auto-instrumentation==0.8b0 +opentelemetry-exporter-cloud-trace==0.9b0 +opentelemetry-ext-flask==0.8b0 +opentelemetry-ext-grpc==0.9b0 +opentelemetry-ext-jaeger==0.8b0 +opentelemetry-ext-requests==0.8b0 +opentelemetry-ext-wsgi==0.8b0 +opentelemetry-sdk==0.9b0 +grpcio==1.29.0 +httpretty==1.0.2 +google-cloud-monitoring==1.0.0 +google-cloud-trace==0.23.0 + diff --git a/trace/cloud-trace-demo-app-opentelemetry/app/service.yaml b/trace/cloud-trace-demo-app-opentelemetry/app/service.yaml new file mode 100644 index 00000000000..1af9fd16227 --- /dev/null +++ b/trace/cloud-trace-demo-app-opentelemetry/app/service.yaml @@ -0,0 +1,14 @@ +# The hello service provides a load-balancing proxy over the hello-app +# pods. By specifying the type as a 'LoadBalancer', Kubernetes Engine will +# create an external HTTP load balancer. +apiVersion: v1 +kind: Service +metadata: + name: trace-demo +spec: + type: LoadBalancer + selector: + app: trace-demo + ports: + - port: 80 + targetPort: 8080 \ No newline at end of file diff --git a/trace/cloud-trace-demo-app-opentelemetry/example-trace.png b/trace/cloud-trace-demo-app-opentelemetry/example-trace.png new file mode 100644 index 00000000000..84da008e82c Binary files /dev/null and b/trace/cloud-trace-demo-app-opentelemetry/example-trace.png differ diff --git a/trace/cloud-trace-demo-app-opentelemetry/setup.sh b/trace/cloud-trace-demo-app-opentelemetry/setup.sh new file mode 100755 index 00000000000..754e22f7ea1 --- /dev/null +++ b/trace/cloud-trace-demo-app-opentelemetry/setup.sh @@ -0,0 +1,53 @@ +#!/bin/bash +################## Set up service a ########################### + +echo "Creating service a" +kubectl apply -f app/demo-service-a.yaml + +################## Set up service b ########################### +echo "Fetching the external IP of service a" +endpoint="" +for run in {1..20} +do + echo "Attempt #${run} to fetch the external IP of service a..." + sleep 5 + endpoint=`kubectl get svc cloud-trace-demo-a -ojsonpath='{.status.loadBalancer.ingress[0].ip}'` + if [[ "$endpoint" != "" ]]; then + break + fi +done + +if [[ "$endpoint" == "" ]]; then + echo "Unable to get external IP for service cloud-trace-demo-a" + exit 1 +fi + +echo "Passing external IP for the first service ${endpoint} to the second service template" +sed "s/{{ endpoint }}/${endpoint}/g" app/demo-service-b.yaml.template > app/demo-service-b.yaml +kubectl apply -f app/demo-service-b.yaml +rm app/demo-service-b.yaml + +################## Set up service c ########################### +echo "Fetching the external IP of service b" +endpoint="" +for run in {1..20} +do + echo "Attempt #${run} to fetch the external IP of service b..." + sleep 5 + endpoint=`kubectl get svc cloud-trace-demo-b -ojsonpath='{.status.loadBalancer.ingress[0].ip}'` + if [[ "$endpoint" != "" ]]; then + break + fi +done + +if [[ "$endpoint" == "" ]]; then + echo "Unable to get external IP for service cloud-trace-demo-a" + exit 1 +fi + +echo "Passing external IP for the service b ${endpoint} to the service c" +sed "s/{{ endpoint }}/${endpoint}/g" app/demo-service-c.yaml.template > app/demo-service-c.yaml +kubectl apply -f app/demo-service-c.yaml +rm app/demo-service-c.yaml + +echo "Successfully deployed all services"