Skip to content

Commit

Permalink
Merge pull request #80 from lightstep/otel-enhancements-2
Browse files Browse the repository at this point in the history
Update Services for OpenTelemetry (round 2)
  • Loading branch information
smithclay authored Jan 26, 2021
2 parents 26b5f62 + 8bcaca1 commit 2d2ec5b
Show file tree
Hide file tree
Showing 19 changed files with 589 additions and 677 deletions.
9 changes: 2 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,8 @@ Find **Protocol Buffers Descriptions** at the [`./pb` directory](./pb).
- **[gRPC](https://grpc.io):** Microservices use a high volume of gRPC calls to
communicate to each other.
- **[Istio](https://istio.io):** Application works on Istio service mesh.
- **[OpenCensus](https://opencensus.io/) Tracing:** Most services are
instrumented using OpenCensus trace interceptors for gRPC/HTTP.
- **[Stackdriver APM](https://cloud.google.com/stackdriver/):** Many services
are instrumented with **Profiling**, **Tracing** and **Debugging**. In
addition to these, using Istio enables features like Request/Response
**Metrics** and **Context Graph** out of the box. When it is running out of
Google Cloud, this code path remains inactive.
- **[OpenTelemetry](https://opentelemetry.lightstep.com/) Tracing:** Most services are
instrumented using OpenTelemetry trace interceptors for gRPC/HTTP.
- **[Skaffold](https://skaffold.dev):** Application
is deployed to Kubernetes with a single command using Skaffold.
- **Synthetic Load Generation:** The application demo comes with a background
Expand Down
4 changes: 3 additions & 1 deletion kubernetes-manifests/emailservice.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ spec:
value: "8080"
- name: ENABLE_PROFILER
value: "0"
- name: LIGHTSTEP_ACCESS_TOKEN
- name: LS_SERVICE_NAME
value: emailservice
- name: LS_ACCESS_TOKEN
valueFrom:
secretKeyRef:
name: lightstep-credentials
Expand Down
4 changes: 3 additions & 1 deletion kubernetes-manifests/recommendationservice.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ spec:
value: "productcatalogservice:3550"
- name: ENABLE_PROFILER
value: "0"
- name: LIGHTSTEP_ACCESS_TOKEN
- name: LS_SERVICE_NAME
value: recommendationservice
- name: LS_ACCESS_TOKEN
valueFrom:
secretKeyRef:
name: lightstep-credentials
Expand Down
347 changes: 162 additions & 185 deletions src/currencyservice/package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/currencyservice/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"async": "^1.5.2",
"google-protobuf": "^3.14.0",
"grpc": "^1.24.4",
"lightstep-opentelemetry-launcher-node": "^0.13.0",
"lightstep-opentelemetry-launcher-node": "^0.13.1",
"pino": "^5.6.2",
"request": "^2.87.0",
"xml2js": "^0.4.19"
Expand Down
5 changes: 2 additions & 3 deletions src/emailservice/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@ WORKDIR /email_server
# get packages
COPY requirements.txt .
RUN pip install -r requirements.txt
RUN pip install ls-trace
RUN pip install opentracing
RUN pip install opentelemetry-launcher

# Add the application
COPY . .

EXPOSE 8080
ENTRYPOINT [ "ls-trace-run", "python", "email_server.py" ]
ENTRYPOINT [ "opentelemetry-instrument", "python", "email_server.py" ]
13 changes: 1 addition & 12 deletions src/emailservice/email_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,9 @@
from logger import getJSONLogger
logger = getJSONLogger('emailservice-client')

from opencensus.trace.tracer import Tracer
from opencensus.trace.exporters import stackdriver_exporter
from opencensus.trace.ext.grpc import client_interceptor

try:
exporter = stackdriver_exporter.StackdriverExporter()
tracer = Tracer(exporter=exporter)
tracer_interceptor = client_interceptor.OpenCensusClientInterceptor(tracer, host_port='0.0.0.0:8080')
except:
tracer_interceptor = client_interceptor.OpenCensusClientInterceptor()

def send_confirmation_email(email, order):
channel = grpc.insecure_channel('0.0.0.0:8080')
channel = grpc.intercept_channel(channel, tracer_interceptor)
channel = grpc.intercept_channel(channel)
stub = demo_pb2_grpc.EmailServiceStub(channel)
try:
response = stub.SendOrderConfirmation(demo_pb2.SendOrderConfirmationRequest(
Expand Down
70 changes: 2 additions & 68 deletions src/emailservice/email_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,50 +14,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from ddtrace import tracer
from ddtrace.propagation.b3 import B3HTTPPropagator

tracer.configure(
http_propagator=B3HTTPPropagator,
hostname=os.environ['LIGHTSTEP_HOST'],
port=os.environ['LIGHTSTEP_PORT'],
https=os.environ['LIGHTSTEP_PLAINTEXT'] is not "true"
)
tracer.set_tags(
{
"lightstep.service_name": "emailservice",
"lightstep.access_token": os.getenv("LIGHTSTEP_ACCESS_TOKEN"),
}
)

from concurrent import futures
import argparse
import sys
import time
import grpc
from jinja2 import Environment, FileSystemLoader, select_autoescape, TemplateError
from google.api_core.exceptions import GoogleAPICallError

import demo_pb2
import demo_pb2_grpc
from grpc_health.v1 import health_pb2
from grpc_health.v1 import health_pb2_grpc

from opencensus.trace.exporters import stackdriver_exporter
from opencensus.trace.ext.grpc import server_interceptor
from opencensus.trace.samplers import always_on

# import googleclouddebugger
import googlecloudprofiler

try:
sampler = always_on.AlwaysOnSampler()
exporter = stackdriver_exporter.StackdriverExporter(
project_id=os.environ.get('GCP_PROJECT_ID'),
transport=AsyncTransport)
tracer_interceptor = server_interceptor.OpenCensusServerInterceptor(sampler, exporter)
except:
tracer_interceptor = server_interceptor.OpenCensusServerInterceptor()

# try:
# googleclouddebugger.enable(
Expand Down Expand Up @@ -121,7 +90,7 @@ def SendOrderConfirmation(self, request, context):

try:
EmailService.send_email(self.client, email, confirmation)
except GoogleAPICallError as err:
except err:
context.set_details("An error occurred when sending the email.")
print(err.message)
context.set_code(grpc.StatusCode.INTERNAL)
Expand All @@ -140,8 +109,7 @@ def Check(self, request, context):
status=health_pb2.HealthCheckResponse.SERVING)

def start(dummy_mode):
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10),
interceptors=(tracer_interceptor,))
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
service = None
if dummy_mode:
service = DummyEmailService()
Expand All @@ -161,41 +129,7 @@ def start(dummy_mode):
except KeyboardInterrupt:
server.stop(0)

def initStackdriverProfiling():
project_id = None
try:
project_id = os.environ["GCP_PROJECT_ID"]
except KeyError:
# Environment variable not set
pass

for retry in range(1,4):
try:
if project_id:
googlecloudprofiler.start(service='email_server', service_version='1.0.0', verbose=0, project_id=project_id)
else:
googlecloudprofiler.start(service='email_server', service_version='1.0.0', verbose=0)
logger.info("Successfully started Stackdriver Profiler.")
return
except (BaseException) as exc:
logger.info("Unable to start Stackdriver Profiler Python agent. " + str(exc))
if (retry < 4):
logger.info("Sleeping %d to retry initializing Stackdriver Profiler"%(retry*10))
time.sleep (1)
else:
logger.warning("Could not initialize Stackdriver Profiler after retrying, giving up")
return

if __name__ == '__main__':
logger.info('starting the email service in dummy mode.')
try:
enable_profiler = os.environ["ENABLE_PROFILER"]
if enable_profiler != "1":
raise KeyError()
else:
initStackdriverProfiling()
except KeyError:
logger.info("Skipping Stackdriver Profiler Python agent initialization. Set environment variable ENABLE_PROFILER=1 to enable.")


start(dummy_mode = True)
15 changes: 9 additions & 6 deletions src/emailservice/requirements.in
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
google-api-core==1.6.0
grpcio-health-checking==1.12.1
grpcio==1.16.1
jinja2==2.10
opencensus[stackdriver]==0.1.10
opentelemetry-launcher==0.17b0
python-json-logger==0.1.9
google-cloud-profiler==1.0.8
grpcio-health-checking==1.13.0
grpcio~=1.27

# generated from: opentelemetry-bootstrap -a requirements
opentelemetry-instrumentation-flask>=0.8b0
opentelemetry-instrumentation-grpc>=0.8b0
opentelemetry-instrumentation-jinja2>=0.8b0
opentelemetry-instrumentation-sqlite3>=0.11b0
132 changes: 104 additions & 28 deletions src/emailservice/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,109 @@
# This file is autogenerated by pip-compile
# To update, run:
#
# pip-compile --output-file requirements.txt requirements.in
# pip-compile --output-file=requirements.txt requirements.in
#
cachetools==3.0.0 # via google-auth
certifi==2018.11.29 # via requests
chardet==3.0.4 # via requests
google-api-core[grpc]==1.6.0
google-api-python-client==1.7.8 # via google-cloud-profiler
google-auth-httplib2==0.0.3 # via google-api-python-client, google-cloud-profiler
google-auth==1.6.2 # via google-api-core, google-api-python-client, google-auth-httplib2, google-cloud-profiler
google-cloud-core==0.29.1 # via google-cloud-trace
google-cloud-profiler==1.0.8
google-cloud-trace==0.20.2 # via opencensus
googleapis-common-protos==1.5.5 # via google-api-core
grpcio-health-checking==1.12.1
grpcio==1.16.1
httplib2==0.12.1 # via google-api-python-client, google-auth-httplib2
idna==2.8 # via requests
jinja2==2.10
markupsafe==1.1.0 # via jinja2
opencensus[stackdriver]==0.1.10
protobuf==3.6.1 # via google-api-core, google-cloud-profiler, googleapis-common-protos, grpcio-health-checking
pyasn1-modules==0.2.3 # via google-auth
pyasn1==0.4.5 # via pyasn1-modules, rsa
backoff==1.10.0
# via opentelemetry-exporter-otlp
click==7.1.2
# via flask
environs==9.3.0
# via opentelemetry-launcher
flask==1.1.2
# via opentelemetry-instrumentation-flask
googleapis-common-protos==1.52.0
# via opentelemetry-exporter-otlp
grpcio-health-checking==1.13.0
# via -r requirements.in
grpcio==1.35.0
# via
# -r requirements.in
# grpcio-health-checking
# opentelemetry-exporter-otlp
# opentelemetry-instrumentation-grpc
itsdangerous==1.1.0
# via flask
jinja2==2.11.2
# via
# flask
# opentelemetry-instrumentation-jinja2
markupsafe==1.1.0
# via jinja2
marshmallow==3.10.0
# via environs
opentelemetry-api==0.17b0
# via
# opentelemetry-exporter-otlp
# opentelemetry-instrumentation
# opentelemetry-instrumentation-dbapi
# opentelemetry-instrumentation-flask
# opentelemetry-instrumentation-grpc
# opentelemetry-instrumentation-jinja2
# opentelemetry-instrumentation-sqlite3
# opentelemetry-instrumentation-system-metrics
# opentelemetry-instrumentation-wsgi
# opentelemetry-launcher
# opentelemetry-propagator-b3
# opentelemetry-sdk
opentelemetry-exporter-otlp==0.17b0
# via opentelemetry-launcher
opentelemetry-instrumentation-dbapi==0.17b0
# via opentelemetry-instrumentation-sqlite3
opentelemetry-instrumentation-flask==0.17b0
# via -r requirements.in
opentelemetry-instrumentation-grpc==0.17b0
# via -r requirements.in
opentelemetry-instrumentation-jinja2==0.17b0
# via -r requirements.in
opentelemetry-instrumentation-sqlite3==0.17b0
# via -r requirements.in
opentelemetry-instrumentation-system-metrics==0.17b0
# via opentelemetry-launcher
opentelemetry-instrumentation-wsgi==0.17b0
# via opentelemetry-instrumentation-flask
opentelemetry-instrumentation==0.17b0
# via
# opentelemetry-instrumentation-dbapi
# opentelemetry-instrumentation-flask
# opentelemetry-instrumentation-jinja2
# opentelemetry-instrumentation-sqlite3
# opentelemetry-instrumentation-wsgi
# opentelemetry-launcher
opentelemetry-launcher==0.17b0
# via -r requirements.in
opentelemetry-propagator-b3==0.17b0
# via opentelemetry-launcher
opentelemetry-proto==0.17b0
# via opentelemetry-exporter-otlp
opentelemetry-sdk==0.17b0
# via
# opentelemetry-exporter-otlp
# opentelemetry-instrumentation-grpc
# opentelemetry-instrumentation-system-metrics
# opentelemetry-launcher
protobuf==3.14.0
# via
# googleapis-common-protos
# grpcio-health-checking
# opentelemetry-proto
psutil==5.7.3
# via opentelemetry-instrumentation-system-metrics
python-dotenv==0.15.0
# via environs
python-json-logger==0.1.9
pytz==2018.9 # via google-api-core
requests==2.21.0 # via google-api-core, google-cloud-profiler
rsa==4.0 # via google-auth
six==1.12.0 # via google-api-core, google-api-python-client, google-auth, grpcio, protobuf
uritemplate==3.0.0 # via google-api-python-client
urllib3==1.24.2 # via requests
# via -r requirements.in
six==1.12.0
# via
# grpcio
# protobuf
werkzeug==1.0.1
# via flask
wrapt==1.12.1
# via
# opentelemetry-instrumentation
# opentelemetry-instrumentation-dbapi
# opentelemetry-instrumentation-jinja2
# opentelemetry-instrumentation-sqlite3

# The following packages are considered to be unsafe in a requirements file:
# setuptools
7 changes: 3 additions & 4 deletions src/paymentservice/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,16 @@ const { lightstep } = require('lightstep-opentelemetry-launcher-node');
const VERSION = require('./package.json').version;

const sdk = lightstep.configureOpenTelemetry({
serviceVersion: VERSION
serviceVersion: VERSION,
logLevel: 'debug'
});

const path = require('path');
sdk.start().then(() => {
const HipsterShopServer = require('./server');

const PORT = process.env['PORT'];
const PROTO_PATH = path.join(__dirname, '/proto/');

const server = new HipsterShopServer(PROTO_PATH, PORT);
const server = new HipsterShopServer(PROTO_PATH, PORT, sdk);

server.listen();
});
Loading

0 comments on commit 2d2ec5b

Please sign in to comment.