Skip to content

Commit 228718c

Browse files
ZiweiZhaoleahecoleqqqstuv
authored
Cloud trace demo app (#2716)
* Initial commit for cloud trace demo app * Unsubmodule cloud trace demo app * Remove yaml files for b and c; Addressed comments in setup.sh * Add comments for app.py * Modify cloud shell button * Update README file, the remaining comments need further discussions. * add comment for trace middleware * shorten dockerfile and add comments on app.py * Remove unused makefile * fix indentation and import * More indentation fixes * Attempt to add httppretty to resolve python test failure * Change to python 3.6 * Add opencensus flask requirement to parent dir requirements * Add httppretty dependency * Update readme file * Update readme file * Change image demo name to cloud-trace-demo * Delete unneeded license, update readme file with the correct image name and remove requirements * Refactor directory structure * Resolve comments on readme * Resolve comments on readme * Resolve comments on readme file * Resolve comments on readme file * Update step 10 * Add a screenshot of tracelist page * Replace example image and update Readme file * Update new line * Modify readme file * Update example image Co-authored-by: Leah E. Cole <[email protected]> Co-authored-by: Duke Nguyen <[email protected]>
1 parent 7af14f7 commit 228718c

File tree

15 files changed

+383
-0
lines changed

15 files changed

+383
-0
lines changed

trace/cloud-trace-demo-app/README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# cloud-trace-demo-app
2+
3+
Open this demo app in [Google Cloud Shell](https://cloud.google.com/shell/docs/). This includes necessary tools.
4+
5+
We provide a public image for the services in this demo app. You could also build
6+
your own following steps 4 - 6.
7+
8+
[![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&amp;cloudshell_tutorial=README.md)
9+
10+
#### Demo Requirements
11+
If you are using Cloud Shell, skip to the next section.
12+
13+
1. Install gcloud <https://cloud.google.com/sdk/install>
14+
2. Install kubectl <https://kubernetes.io/docs/tasks/tools/install-kubectl/>
15+
3. Install docker <https://docs.docker.com/install/>
16+
17+
#### Google Container Registry Image Setup
18+
If you are using the provided image, skip to the next section.
19+
20+
4. Get default project id and set environment variable:
21+
`PROJECT_ID=$(gcloud config get-value project)`
22+
5. Build Image:
23+
`docker build -t gcr.io/${PROJECT-ID}/cloud-trace-demo .`
24+
6. Upload Image to Container Registry:
25+
`gcloud docker -- push gcr.io/${PROJECT-ID}/cloud-trace-demo-test:v1`
26+
27+
#### Create a GKE cluster
28+
7. Enable Google Cloud and set up region and zone.
29+
`gcloud init`
30+
8. Enable the GKE API & billing:
31+
`gcloud services enable container.googleapis.com`
32+
9. Create a GKE cluster named "demo":
33+
`gcloud container clusters create demo`
34+
35+
#### Send Requests to See Generated Traces
36+
37+
10. If you are using your own image, please change the image variable in the following files:
38+
* [YAML](./app/demo-service-a.yaml)
39+
* [template B](./app/demo-service-b.yaml.template)
40+
* [template C](./app/demo-service-c.yaml.template)
41+
11. Run setup.sh to apply the YAML files.
42+
`./setup.sh`
43+
12. Send request to the last service:
44+
45+
`curl $(kubectl get svc cloud-trace-demo-a -ojsonpath='{.status.loadBalancer.ingress[0].ip}')`
46+
13. Visit [Trace List](https://pantheon.corp.google.com/traces/list) to check traces generated.
47+
Click on any trace in the graph to see the Waterfall View.
48+
![Screenshot](./example.png)
49+
14. Clean up GKE cluster/pods/services
50+
`gcloud container clusters delete demo`
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
FROM python:3.6.0
2+
3+
RUN pip3 install --upgrade pip
4+
5+
RUN apt-get update \
6+
&& apt-get clean \
7+
&& rm -rf /var/lib/apt/lists/*
8+
9+
COPY requirements.txt /app/
10+
11+
WORKDIR /app
12+
RUN pip3 install -r requirements.txt
13+
14+
ENV APP_HOME /app
15+
WORKDIR $APP_HOME
16+
COPY . ./
17+
18+
ENTRYPOINT ["python"]
19+
CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 main:app

trace/cloud-trace-demo-app/app/app.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Copyright 2020 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""
15+
A sample app demonstrating Stackdriver Trace
16+
"""
17+
18+
from flask import Flask
19+
from opencensus.trace import execution_context
20+
from opencensus.trace.propagation import google_cloud_format
21+
from opencensus.trace.samplers import AlwaysOnSampler
22+
from opencensus.ext.stackdriver.trace_exporter import StackdriverExporter
23+
from opencensus.ext.flask.flask_middleware import FlaskMiddleware
24+
25+
import requests
26+
import argparse
27+
import time
28+
import random
29+
30+
app = Flask(__name__)
31+
32+
propagator = google_cloud_format.GoogleCloudFormatPropagator()
33+
34+
35+
def createMiddleWare(exporter):
36+
# Configure a flask middleware that listens for each request and applies automatic tracing.
37+
# This needs to be set up before the application starts.
38+
middleware = FlaskMiddleware(
39+
app,
40+
exporter=exporter,
41+
propagator=propagator,
42+
sampler=AlwaysOnSampler())
43+
return middleware
44+
45+
46+
@app.route('/')
47+
def template_test():
48+
# Sleep for a random time to imitate a random processing time
49+
time.sleep(random.uniform(0, 0.5))
50+
# Keyword that gets passed in will be concatenated to the final output string.
51+
output_string = app.config['keyword']
52+
# If there is no endpoint, return the output string.
53+
url = app.config['endpoint']
54+
if url == "":
55+
return output_string, 200
56+
# Endpoint is the next service to send string to.
57+
data = {'body': output_string}
58+
trace_context_header = propagator.to_header(execution_context.get_opencensus_tracer().span_context)
59+
response = requests.get(
60+
url,
61+
params=data,
62+
headers={
63+
'X-Cloud-Trace-Context' : trace_context_header}
64+
)
65+
return response.text + app.config['keyword']
66+
67+
68+
if __name__ == "__main__":
69+
parser = argparse.ArgumentParser()
70+
parser.add_argument("--keyword", default="", help="name of the service.")
71+
parser.add_argument("--endpoint", default="", help="endpoint to dispatch appended string, simply respond if not set")
72+
args = parser.parse_args()
73+
app.config['keyword'] = args.keyword
74+
app.config['endpoint'] = args.endpoint
75+
createMiddleWare(StackdriverExporter())
76+
app.run(debug=True, host='0.0.0.0', port=8080)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Copyright 2020 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""
15+
A sample app demonstrating Stackdriver Trace
16+
"""
17+
18+
import app
19+
import mock
20+
import httpretty
21+
22+
23+
def test_send_response():
24+
service_keyword = "Hello"
25+
app.app.testing = True
26+
app.app.config['keyword'] = service_keyword
27+
app.app.config['endpoint'] = ""
28+
client = app.app.test_client()
29+
resp = client.get('/')
30+
assert resp.status_code == 200
31+
assert service_keyword in resp.data.decode('utf-8')
32+
33+
34+
@httpretty.activate
35+
def test_request_url_with_trace_context():
36+
service1_keyword = "World"
37+
service2_url = "http://example.com"
38+
service2_keyword = "Hello"
39+
app.app.testing = True
40+
app.app.config['keyword'] = service1_keyword
41+
app.app.config['endpoint'] = service2_url
42+
43+
def request_callback(request, uri, response_headers):
44+
# Assert that the request is sent with a trace context
45+
assert request.headers.get("X-Cloud-Trace-Context")
46+
return [200, response_headers, service2_keyword]
47+
48+
httpretty.register_uri(httpretty.GET, service2_url, body=request_callback)
49+
exporter = mock.Mock()
50+
app.createMiddleWare(exporter)
51+
52+
client = app.app.test_client()
53+
resp = client.get('/')
54+
assert resp.status_code == 200
55+
# Assert that the response is a concatenation of responses from both services
56+
assert service2_keyword + service1_keyword in resp.data.decode('utf-8')
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: cloud-trace-demo-a
5+
labels:
6+
app: cloud-trace-demo-app-a
7+
spec:
8+
replicas: 1
9+
selector:
10+
matchLabels:
11+
app: cloud-trace-demo-app-a
12+
template:
13+
metadata:
14+
name: cloud-trace-demo-a
15+
labels:
16+
app: cloud-trace-demo-app-a
17+
spec:
18+
containers:
19+
- name: cloud-trace-demo-container
20+
image: gcr.io/cloud-trace-demo/demo-image:latest
21+
command:
22+
- python
23+
args:
24+
- app.py
25+
- --keyword=Hello
26+
ports:
27+
- containerPort: 8080
28+
---
29+
apiVersion: v1
30+
kind: Service
31+
metadata:
32+
name: cloud-trace-demo-a
33+
spec:
34+
selector:
35+
app: cloud-trace-demo-app-a
36+
ports:
37+
- protocol: TCP
38+
port: 80
39+
targetPort: 8080
40+
type: LoadBalancer
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: cloud-trace-demo-b
5+
labels:
6+
app: cloud-trace-demo-app-b
7+
spec:
8+
replicas: 1
9+
selector:
10+
matchLabels:
11+
app: cloud-trace-demo-app-b
12+
template:
13+
metadata:
14+
name: cloud-trace-demo-b
15+
labels:
16+
app: cloud-trace-demo-app-b
17+
spec:
18+
containers:
19+
- name: cloud-trace-demo-container
20+
image: gcr.io/cloud-trace-demo/demo-image:latest
21+
command:
22+
- python
23+
args:
24+
- app.py
25+
- --keyword=world
26+
- --endpoint=http://{{ endpoint }}
27+
ports:
28+
- containerPort: 8080
29+
---
30+
apiVersion: v1
31+
kind: Service
32+
metadata:
33+
name: cloud-trace-demo-b
34+
spec:
35+
selector:
36+
app: cloud-trace-demo-app-b
37+
ports:
38+
- protocol: TCP
39+
port: 80
40+
targetPort: 8080
41+
type: LoadBalancer
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: cloud-trace-demo-c
5+
labels:
6+
app: cloud-trace-demo-app-c
7+
spec:
8+
replicas: 1
9+
selector:
10+
matchLabels:
11+
app: cloud-trace-demo-app-c
12+
template:
13+
metadata:
14+
name: cloud-trace-demo-c
15+
labels:
16+
app: cloud-trace-demo-app-c
17+
spec:
18+
containers:
19+
- name: cloud-trace-demo-container
20+
image: gcr.io/cloud-trace-demo/demo-image:latest
21+
command:
22+
- python
23+
args:
24+
- app.py
25+
- --keyword=!\n
26+
- --endpoint=http://{{ endpoint }}
27+
ports:
28+
- containerPort: 8080
29+
---
30+
apiVersion: v1
31+
kind: Service
32+
metadata:
33+
name: cloud-trace-demo-c
34+
spec:
35+
selector:
36+
app: cloud-trace-demo-app-c
37+
ports:
38+
- protocol: TCP
39+
port: 80
40+
targetPort: 8080
41+
type: LoadBalancer
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
google-cloud-monitoring==0.33.0
2+
google-cloud-trace==0.23.0
3+
opencensus==0.7.5
4+
Flask==1.1.1
5+
opencensus-ext-stackdriver==0.7.2
6+
opencensus-ext-flask==0.7.3
7+
opencensus-context==0.1.1
8+
grpcio==1.25.0
9+
httpretty==0.9.7
236 KB
Loading

trace/cloud-trace-demo-app/setup.sh

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/bin/bash
2+
################## Set up service a ###########################
3+
4+
echo "Creating service a"
5+
kubectl apply -f app/demo-service-a.yaml
6+
7+
################## Set up service b ###########################
8+
echo "Fetching the external IP of service a"
9+
endpoint=""
10+
for run in {1..20}
11+
do
12+
sleep 5
13+
endpoint=`kubectl get svc cloud-trace-demo-a -ojsonpath='{.status.loadBalancer.ingress[0].ip}'`
14+
if [[ "$endpoint" != "" ]]; then
15+
break
16+
fi
17+
done
18+
19+
if [[ "$endpoint" == "" ]]; then
20+
echo "Unable to get external IP for service cloud-trace-demo-a"
21+
exit 1
22+
fi
23+
24+
echo "Passing external IP for the first service ${endpoint} to the second service template"
25+
sed "s/{{ endpoint }}/${endpoint}/g" app/demo-service-b.yaml.template > app/demo-service-b.yaml
26+
kubectl apply -f app/demo-service-b.yaml
27+
rm app/demo-service-b.yaml
28+
29+
################## Set up service c ###########################
30+
echo "Fetching the external IP of the service b"
31+
endpoint=""
32+
for run in {1..20}
33+
do
34+
sleep 5
35+
endpoint=`kubectl get svc cloud-trace-demo-b -ojsonpath='{.status.loadBalancer.ingress[0].ip}'`
36+
if [[ "$endpoint" != "" ]]; then
37+
break
38+
fi
39+
done
40+
41+
if [[ "$endpoint" == "" ]]; then
42+
echo "Unable to get external IP for service cloud-trace-demo-a"
43+
exit 1
44+
fi
45+
46+
echo "Passing external IP for the service b ${endpoint} to the service c"
47+
sed "s/{{ endpoint }}/${endpoint}/g" app/demo-service-c.yaml.template > app/demo-service-c.yaml
48+
kubectl apply -f app/demo-service-c.yaml
49+
rm app/demo-service-c.yaml
50+
51+
echo "Successfully deployed all services"
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)