Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rest service to microservices demo #14

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .signadot/sandbox-rest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: "@{name}"
spec:
description: Adds REST API to the baseline sandbox
cluster: ms-cluster
labels:
branch: "rest"
forks:
- forkOf:
kind: Deployment
name: restservice
namespace: default
customizations:
images:
- image: {docker_username}/{docker_repo}:@{rest-tag}
defaultRouteGroup: # CLI v0.3.7+ required (see sandbox specification for details)
endpoints:
- name: rest-endpoint
target: http://restservice.default.svc:5051
- name: frontend-endpoint
target: http://frontend.default.svc:80
68 changes: 68 additions & 0 deletions kubernetes-manifests/restservice.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: restservice
spec:
selector:
matchLabels:
app: restservice
template:
metadata:
labels:
app: restservice
annotations:
sidecar.signadot.com/inject: rest
spec:
serviceAccountName: default
containers:
- name: server
image: restservice
ports:
- containerPort: 5051
# readinessProbe:
# exec:
# command: ["/bin/grpc_health_probe", "-addr=:5050"]
# livenessProbe:
# exec:
# command: ["/bin/grpc_health_probe", "-addr=:5050"]
env:
- name: PORT
value: "5051"
- name: PRODUCT_CATALOG_SERVICE_ADDR
value: "productcatalogservice:3550"
- name: SHIPPING_SERVICE_ADDR
value: "shippingservice:50051"
- name: PAYMENT_SERVICE_ADDR
value: "paymentservice:50051"
- name: EMAIL_SERVICE_ADDR
value: "emailservice:5000"
- name: CURRENCY_SERVICE_ADDR
value: "currencyservice:7000"
- name: CART_SERVICE_ADDR
value: "cartservice:7070"
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: CACHE_USER_THRESHOLD
value: "35000"
resources:
requests:
cpu: 100m
memory: 32Mi
limits:
cpu: 200m
memory: 32Mi
---
apiVersion: v1
kind: Service
metadata:
name: restservice
spec:
type: ClusterIP
selector:
app: restservice
ports:
- name: rest
port: 5051
targetPort: 5051
11 changes: 6 additions & 5 deletions skaffold.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ apiVersion: skaffold/v1beta2
kind: Config
build:
artifacts:
# image tags are relative; to specify an image repo (e.g. GCR), you
# must provide a "default repo" using one of the methods described
# here:
# https://skaffold.dev/docs/concepts/#image-repository-handling
# image tags are relative; to specify an image repo (e.g. GCR), you
# must provide a "default repo" using one of the methods described
# here:
# https://skaffold.dev/docs/concepts/#image-repository-handling
- image: adservice
context: src/adservice
- image: cartservice
Expand All @@ -28,6 +28,8 @@ build:
context: src/recommendationservice
- image: shippingservice
context: src/shippingservice
- image: restservice
context: src/restservice
tagPolicy:
gitCommit: {}
local:
Expand All @@ -36,4 +38,3 @@ deploy:
kubectl:
manifests:
- ./kubernetes-manifests/**.yaml

1 change: 1 addition & 0 deletions src/restservice/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
vendor/
19 changes: 19 additions & 0 deletions src/restservice/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM golang:1.17-alpine as builder
RUN apk add --no-cache ca-certificates git
WORKDIR /src

# restore dependencies
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN go build -gcflags='-N -l' -o /restservice .

FROM alpine as release
RUN apk add --no-cache ca-certificates
#RUN GRPC_HEALTH_PROBE_VERSION=v0.3.6 && \
# wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \
# chmod +x /bin/grpc_health_probe
COPY --from=builder /restservice /restservice
EXPOSE 5051
ENTRYPOINT ["/restservice"]
95 changes: 95 additions & 0 deletions src/restservice/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# checkout service

The **checkout** service provides cart management, and order placement functionality.

## OpenTelemetry instrumentation

### Initialization
The OpenTelemetry SDK is initialized in `main` using the `initOtelTracing` function
```
func initOtelTracing(ctx context.Context, log logrus.FieldLogger) *sdktrace.TracerProvider {
endpoint := os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
if endpoint == "" {
endpoint = "opentelemetry-collector:4317"
}

// Set GRPC options to establish an insecure connection to an OpenTelemetry Collector
// To establish a TLS connection to a secured endpoint use:
// otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, ""))
opts := []otlptracegrpc.Option{
otlptracegrpc.WithEndpoint(endpoint),
otlptracegrpc.WithInsecure(),
}

// Create the exporter
exporter, err := otlptrace.New(ctx, otlptracegrpc.NewClient(opts...))
if err != nil {
log.Fatal(err)
}

// Specify the TextMapPropagator to ensure spans propagate across service boundaries
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.Baggage{}, propagation.TraceContext{}))

// Set standard attributes per semantic conventions
res := resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("checkout"),
)

// Create and set the TraceProvider
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(res),
)
otel.SetTracerProvider(tp)

return tp
}
```

You should call `TraceProvider.shutdown()` when your service is shutdown to ensure all spans are exported.
This service makes that call as part of a deferred function in `main`
```
// Initialize OpenTelemetry Tracing
ctx := context.Background()
tp := initOtelTracing(ctx, log)
defer func() { _ = tp.Shutdown(ctx) }()
```

### gRPC instrumentation
This service receives gRPC requests, which are instrumented in the `main` function as part of the gRPC server creation.
```
// create gRPC server with OpenTelemetry instrumentation on all incoming requests
srv := grpc.NewServer(
grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor(otelgrpc.WithTracerProvider(otel.GetTracerProvider()))),
grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor(otelgrpc.WithTracerProvider(otel.GetTracerProvider()))),
)
```

This service will issue several outgoing gRPC calls, which are all instrumented within their respective handlers.
```go
// add OpenTelemetry instrumentation to outgoing gRPC requests
conn, err := grpc.DialContext(ctx, cs.shippingSvcAddr,
grpc.WithInsecure(),
grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor(otelgrpc.WithTracerProvider(otel.GetTracerProvider()))),
grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor(otelgrpc.WithTracerProvider(otel.GetTracerProvider()))))
```

### Baggage
This service makes use of information from Baggage, which originate from an upstream service (frontend).
The `PlaceOrder` function makes use of Baggage to both read, and set Members on it.
Reading userid and requestid from Baggage
```go
// get userID and requestsID from Tracing Baggage
bags := baggage.FromContext(ctx)
userID := bags.Member("userid").Value()
requestID := bags.Member("requestID").Value()
```

Setting orderid into Baggage
```go
// Add orderid to Tracing Baggage
orderIDMember, _ := baggage.NewMember("orderid", orderID.String())
bags, _ = bags.SetMember(orderIDMember)
ctx = baggage.ContextWithBaggage(ctx, bags)
```
Loading