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: