diff --git a/.env b/.env
index 3cec066378..8a6d1ed77c 100644
--- a/.env
+++ b/.env
@@ -15,6 +15,10 @@ OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=cumulative
FRONTEND_PORT=8080
FRONTEND_ADDR=frontend:${FRONTEND_PORT}
+# Envoy
+FRONTEND_HOST=frontend
+ENVOY_PORT=80
+
# Redis
REDIS_PORT=6379
REDIS_ADDR=redis-cart:${REDIS_PORT}
@@ -52,6 +56,7 @@ SHIPPING_SERVICE_ADDR=shippingservice:${SHIPPING_SERVICE_PORT}
FEATURE_FLAG_SERVICE_PORT=8081
FEATURE_FLAG_SERVICE_ADDR=featureflagservice:${FEATURE_FLAG_SERVICE_PORT}
+FEATURE_FLAG_SERVICE_HOST=feature-flag-service
FEATURE_FLAG_GRPC_SERVICE_PORT=50053
FEATURE_FLAG_GRPC_SERVICE_ADDR=featureflagservice:${FEATURE_FLAG_GRPC_SERVICE_PORT}
diff --git a/.github/.mlc_config.json b/.github/.mlc_config.json
index 346efed04a..c2ff8d8545 100644
--- a/.github/.mlc_config.json
+++ b/.github/.mlc_config.json
@@ -1,8 +1,7 @@
{
"ignorePatterns": [
+ {"pattern": "^http://localhost"},
{"pattern": "^http://localhost:3000"},
- {"pattern": "^http://localhost:8080"},
- {"pattern": "^http://localhost:8081"},
{"pattern": "^http://localhost:8089"},
{"pattern": "^http://localhost:9090"},
{"pattern": "^http://localhost:16686"},
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c4b8e563f4..3bc34455f6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -124,3 +124,5 @@ significant modifications will be credited to OpenTelemetry Authors.
([#455](https://github.com/open-telemetry/opentelemetry-demo/pull/455))
* Update cartservice Dockerfile to support ARM64
([#439](https://github.com/open-telemetry/opentelemetry-demo/pull/439))
+* Add Envoy as reverse proxy for all user-facing services
+([#508](https://github.com/open-telemetry/opentelemetry-demo/pull/508))
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 43c1ed660b..50c2e4176a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -91,11 +91,11 @@ docker compose up -d
Once the images are built and containers are started you can access:
-- Webstore:
+- Webstore:
- Jaeger:
- Prometheus:
- Grafana:
-- Feature Flags UI:
+- Feature Flags UI:
- Load Generator UI:
## Create Your First Pull Request
diff --git a/docker-compose.yml b/docker-compose.yml
index cde395319a..9b8832bb3a 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -181,7 +181,7 @@ services:
memory: 200M
restart: always
ports:
- - "${FRONTEND_PORT}:${FRONTEND_PORT}"
+ - "${FRONTEND_PORT}"
environment:
- PORT=${FRONTEND_PORT}
- FRONTEND_ADDR
@@ -210,7 +210,28 @@ services:
- shippingservice
logging: *logging
- # Load generator
+ frontendproxy:
+ image: ${IMAGE_NAME}:${IMAGE_VERSION}-frontend-envoy
+ build:
+ context: ./
+ dockerfile: ./src/frontendProxy/Dockerfile
+ container_name: frontend-proxy
+ volumes:
+ - ./src/frontendproxy/envoy.yaml.tmpl:/etc/envoy/envoy.yaml.tmpl
+ ports:
+ - "${ENVOY_PORT}:${ENVOY_PORT}"
+ environment:
+ - PUBLIC_OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
+ - FRONTEND_PORT
+ - FRONTEND_HOST
+ - FEATURE_FLAG_SERVICE_PORT
+ - FEATURE_FLAG_SERVICE_HOST
+ - ENVOY_PORT
+ - ENVOY_UID=0
+ depends_on:
+ - frontend
+ command: /bin/sh -c "envsubst < /etc/envoy/envoy.yaml.tmpl > /etc/envoy/envoy.yaml && envoy -c /etc/envoy/envoy.yaml;"
+
loadgenerator:
image: ${IMAGE_NAME}:${IMAGE_VERSION}-loadgenerator
container_name: load-generator
@@ -389,7 +410,7 @@ services:
memory: 160M
restart: always
ports:
- - "${FEATURE_FLAG_SERVICE_PORT}:${FEATURE_FLAG_SERVICE_PORT}" # Feature Flag Service UI
+ - "${FEATURE_FLAG_SERVICE_PORT}" # Feature Flag Service UI
- "${FEATURE_FLAG_GRPC_SERVICE_PORT}" # Feature Flag Service gRPC API
environment:
- FEATURE_FLAG_SERVICE_PORT
@@ -398,6 +419,7 @@ services:
- OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=grpc
- OTEL_SERVICE_NAME=featureflagservice
- DATABASE_URL=ecto://ffs:ffs@ffs_postgres:5432/ffs
+ - FEATURE_FLAG_SERVICE_PATH_ROOT="/feature"
depends_on:
ffs_postgres:
condition: service_healthy
diff --git a/docs/current_architecture.md b/docs/current_architecture.md
index f63c08c657..38e0bd0d0d 100644
--- a/docs/current_architecture.md
+++ b/docs/current_architecture.md
@@ -15,6 +15,7 @@ checkoutservice(Checkout Service):::golang
currencyservice(Currency Service):::cpp
emailservice(Email Service):::ruby
frontend(Frontend):::javascript
+frontendproxy(Frontend Proxy
(Envoy)):::cpp
loadgenerator([Load Generator]):::python
paymentservice(Payment Service):::javascript
productcatalogservice(Product Catalog Service):::golang
@@ -24,7 +25,9 @@ shippingservice(Shipping Service):::rust
featureflagservice(Feature Flag Service):::erlang
featureflagstore[(Feature Flag Store
(PostgreSQL DB))]
-Internet -->|HTTP| frontend
+Internet -->|HTTP| frontendproxy
+frontendproxy -->|HTTP| frontend
+frontendproxy -->|HTTP| featureflagservice
loadgenerator -->|HTTP| frontend
checkoutservice --->|gRPC| cartservice --> cache
diff --git a/docs/docker_deployment.md b/docs/docker_deployment.md
index 4c16bc3ea7..b01f875688 100644
--- a/docs/docker_deployment.md
+++ b/docs/docker_deployment.md
@@ -39,11 +39,11 @@ source. It may take more than 20 minutes to build if the flag is omitted.
Once the images are built and containers are started you can access:
-- Webstore:
+- Webstore:
- Jaeger:
- Prometheus:
- Grafana:
-- Feature Flags UI:
+- Feature Flags UI:
- Load Generator UI:
## Bring your own backend
diff --git a/src/featureflagservice/config/config.exs b/src/featureflagservice/config/config.exs
index e70c3d6415..da7d0c8e89 100644
--- a/src/featureflagservice/config/config.exs
+++ b/src/featureflagservice/config/config.exs
@@ -12,7 +12,7 @@ config :featureflagservice,
# Configures the endpoint
config :featureflagservice, FeatureflagserviceWeb.Endpoint,
- url: [host: "localhost"],
+ url: [host: "localhost", path: "/feature"],
render_errors: [view: FeatureflagserviceWeb.ErrorView, accepts: ~w(html json), layout: false],
pubsub_server: Featureflagservice.PubSub,
live_view: [signing_salt: "T88WPl/Q"]
diff --git a/src/frontend/README.md b/src/frontend/README.md
index c188410e74..af153c3f46 100755
--- a/src/frontend/README.md
+++ b/src/frontend/README.md
@@ -24,3 +24,8 @@ from the root folder.
It will start all of the required backend services
and within the container simply run `npm run dev`.
After that the app should be available at .
+
+## Collector Config
+
+The app looks for a cookie named 'otelCollectorUrl' and gets its value on page
+load. This cookie key + value needs to be set by a reverse proxy.
diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json
index c549a72f85..6650a2fcd9 100644
--- a/src/frontend/package-lock.json
+++ b/src/frontend/package-lock.json
@@ -22,6 +22,7 @@
"@opentelemetry/sdk-trace-node": "1.7.0",
"@opentelemetry/sdk-trace-web": "1.7.0",
"@types/styled-components": "5.1.25",
+ "cookies-next": "^2.1.1",
"currency-symbol-map": "5.1.0",
"dotenv": "16.0.1",
"dotenv-expand": "8.0.3",
@@ -3175,6 +3176,11 @@
"resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.5.tgz",
"integrity": "sha512-v6LCdKfK6BwcqMo+wYW05rLS12S0ZO0Fl4w1h4aaZMD7bqT3gVUns6FvLJKGZHQmYn3SX55JWGpziwJRwVgutA=="
},
+ "node_modules/@types/cookie": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
+ "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
+ },
"node_modules/@types/cookies": {
"version": "0.7.7",
"resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.7.tgz",
@@ -4382,6 +4388,29 @@
"node": ">= 0.6"
}
},
+ "node_modules/cookies-next": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/cookies-next/-/cookies-next-2.1.1.tgz",
+ "integrity": "sha512-AZGZPdL1hU3jCjN2UMJTGhLOYzNUN9Gm+v8BdptYIHUdwz397Et1p+sZRfvAl8pKnnmMdX2Pk9xDRKCGBum6GA==",
+ "dependencies": {
+ "@types/cookie": "^0.4.1",
+ "@types/node": "^16.10.2",
+ "cookie": "^0.4.0"
+ }
+ },
+ "node_modules/cookies-next/node_modules/@types/node": {
+ "version": "16.11.68",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.68.tgz",
+ "integrity": "sha512-JkRpuVz3xCNCWaeQ5EHLR/6woMbHZz/jZ7Kmc63AkU+1HxnoUugzSWMck7dsR4DvNYX8jp9wTi9K7WvnxOIQZQ=="
+ },
+ "node_modules/cookies-next/node_modules/cookie": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/core-js-pure": {
"version": "3.23.5",
"dev": true,
@@ -10637,6 +10666,11 @@
"resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.5.tgz",
"integrity": "sha512-v6LCdKfK6BwcqMo+wYW05rLS12S0ZO0Fl4w1h4aaZMD7bqT3gVUns6FvLJKGZHQmYn3SX55JWGpziwJRwVgutA=="
},
+ "@types/cookie": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
+ "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
+ },
"@types/cookies": {
"version": "0.7.7",
"resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.7.tgz",
@@ -11453,6 +11487,28 @@
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
},
+ "cookies-next": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/cookies-next/-/cookies-next-2.1.1.tgz",
+ "integrity": "sha512-AZGZPdL1hU3jCjN2UMJTGhLOYzNUN9Gm+v8BdptYIHUdwz397Et1p+sZRfvAl8pKnnmMdX2Pk9xDRKCGBum6GA==",
+ "requires": {
+ "@types/cookie": "^0.4.1",
+ "@types/node": "^16.10.2",
+ "cookie": "^0.4.0"
+ },
+ "dependencies": {
+ "@types/node": {
+ "version": "16.11.68",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.68.tgz",
+ "integrity": "sha512-JkRpuVz3xCNCWaeQ5EHLR/6woMbHZz/jZ7Kmc63AkU+1HxnoUugzSWMck7dsR4DvNYX8jp9wTi9K7WvnxOIQZQ=="
+ },
+ "cookie": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA=="
+ }
+ }
+ },
"core-js-pure": {
"version": "3.23.5",
"dev": true
diff --git a/src/frontend/package.json b/src/frontend/package.json
index 8fd8c543fa..21854639b7 100644
--- a/src/frontend/package.json
+++ b/src/frontend/package.json
@@ -25,6 +25,7 @@
"@opentelemetry/sdk-trace-node": "1.7.0",
"@opentelemetry/sdk-trace-web": "1.7.0",
"@types/styled-components": "5.1.25",
+ "cookies-next": "^2.1.1",
"currency-symbol-map": "5.1.0",
"dotenv": "16.0.1",
"dotenv-expand": "8.0.3",
diff --git a/src/frontend/pages/_app.tsx b/src/frontend/pages/_app.tsx
index d5d5e21c29..90edd55a3b 100755
--- a/src/frontend/pages/_app.tsx
+++ b/src/frontend/pages/_app.tsx
@@ -6,8 +6,12 @@ import CartProvider from '../providers/Cart.provider';
import { ThemeProvider } from 'styled-components';
import Theme from '../styles/Theme';
import FrontendTracer from '../utils/telemetry/FrontendTracer';
+import { getCookie } from 'cookies-next';
-if (typeof window !== 'undefined') FrontendTracer();
+if (typeof window !== 'undefined') {
+ const collector = getCookie('otelCollectorUrl')?.toString() || '';
+ FrontendTracer(collector);
+}
const queryClient = new QueryClient();
diff --git a/src/frontend/utils/telemetry/FrontendTracer.ts b/src/frontend/utils/telemetry/FrontendTracer.ts
index 2ede67d2e6..217e23074f 100644
--- a/src/frontend/utils/telemetry/FrontendTracer.ts
+++ b/src/frontend/utils/telemetry/FrontendTracer.ts
@@ -7,7 +7,7 @@ import { Resource } from '@opentelemetry/resources';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
-const FrontendTracer = async () => {
+const FrontendTracer = async (collectorString: string) => {
const { ZoneContextManager } = await import('@opentelemetry/context-zone');
const provider = new WebTracerProvider({
@@ -19,7 +19,7 @@ const FrontendTracer = async () => {
provider.addSpanProcessor(
new SimpleSpanProcessor(
new OTLPTraceExporter({
- url: process.env.NEXT_PUBLIC_OTEL_EXPORTER_OTLP_TRACES_ENDPOINT || 'http://localhost:4318/v1/traces',
+ url: collectorString || 'http://localhost:4318/v1/traces',
})
)
);
diff --git a/src/frontendProxy/Dockerfile b/src/frontendProxy/Dockerfile
new file mode 100644
index 0000000000..4cc4de7815
--- /dev/null
+++ b/src/frontendProxy/Dockerfile
@@ -0,0 +1,2 @@
+FROM envoyproxy/envoy-dev:8c202194ac6a2cb781eb6ce27d924b379b1e787f
+RUN apt-get update && apt-get install -y gettext-base && apt-get clean && rm -rf /var/lib/apt/lists/*
diff --git a/src/frontendProxy/envoy.yaml.tmpl b/src/frontendProxy/envoy.yaml.tmpl
new file mode 100644
index 0000000000..6269332f97
--- /dev/null
+++ b/src/frontendProxy/envoy.yaml.tmpl
@@ -0,0 +1,74 @@
+static_resources:
+ listeners:
+ - address:
+ socket_address:
+ address: 0.0.0.0
+ port_value: ${ENVOY_PORT}
+ filter_chains:
+ - filters:
+ - name: envoy.filters.network.http_connection_manager
+ typed_config:
+ "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+ codec_type: AUTO
+ stat_prefix: ingress_http
+ route_config:
+ name: local_route
+ virtual_hosts:
+ - name: frontend
+ domains:
+ - "*"
+ routes:
+ - match: { prefix: "/feature" }
+ route: { cluster: featureflag, prefix_rewrite: "/" }
+ - match: { prefix: "/" }
+ route: { cluster: frontend }
+ response_headers_to_add:
+ - header:
+ key: "Set-Cookie"
+ value:
+ "otelCollectorUrl=${PUBLIC_OTEL_EXPORTER_OTLP_TRACES_ENDPOINT};"
+ http_filters:
+ - name: envoy.filters.http.router
+ typed_config:
+ "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
+
+ clusters:
+ - name: frontend
+ type: STRICT_DNS
+ lb_policy: ROUND_ROBIN
+ load_assignment:
+ cluster_name: frontend
+ endpoints:
+ - lb_endpoints:
+ - endpoint:
+ address:
+ socket_address:
+ address: ${FRONTEND_HOST}
+ port_value: ${FRONTEND_PORT}
+ - name: featureflag
+ type: STRICT_DNS
+ lb_policy: ROUND_ROBIN
+ load_assignment:
+ cluster_name: featureflag
+ endpoints:
+ - lb_endpoints:
+ - endpoint:
+ address:
+ socket_address:
+ address: ${FEATURE_FLAG_SERVICE_HOST}
+ port_value: ${FEATURE_FLAG_SERVICE_PORT}
+
+admin:
+ address:
+ socket_address:
+ address: 0.0.0.0
+ port_value: 10000
+layered_runtime:
+ layers:
+ - name: static_layer_0
+ static_layer:
+ envoy:
+ resource_limits:
+ listener:
+ example_listener_name:
+ connection_limit: 10000
diff --git a/src/otelcollector/otelcol-config.yml b/src/otelcollector/otelcol-config.yml
index 40ee9792be..809f2578ee 100644
--- a/src/otelcollector/otelcol-config.yml
+++ b/src/otelcollector/otelcol-config.yml
@@ -5,7 +5,8 @@ receivers:
http:
cors:
allowed_origins:
- - "http://localhost:8080"
+ - "http://*"
+ - "https://*"
otlp/spanmetrics:
protocols:
grpc: