From 3e01671d731ae21f1d5ff4361f0f1d931402b088 Mon Sep 17 00:00:00 2001 From: Haishi2016 Date: Tue, 8 Oct 2019 09:23:20 -0700 Subject: [PATCH] concept doc updates (#74) * concept doc updates * fix typo --- concepts/README.md | 67 ++++++---- concepts/state-management/state-management.md | 4 +- concepts/terminology/terminology.md | 3 - concepts/tracing-logging/tracing-logging.md | 42 +++++- howto/diagnose-with-tracing/Readme.md | 120 ++++++++++++++++++ .../setup-development-environment.md | 3 - .../setup-kubernetes-environment.md | 3 - images/tracing.png | Bin 0 -> 18158 bytes 8 files changed, 208 insertions(+), 34 deletions(-) delete mode 100644 concepts/terminology/terminology.md create mode 100644 howto/diagnose-with-tracing/Readme.md delete mode 100644 howto/diagnose-with-tracing/setup-development-environment.md delete mode 100644 howto/diagnose-with-tracing/setup-kubernetes-environment.md create mode 100644 images/tracing.png diff --git a/concepts/README.md b/concepts/README.md index b817eb6bb..34ba4be3e 100644 --- a/concepts/README.md +++ b/concepts/README.md @@ -1,22 +1,45 @@ -# Dapr concepts - -This directory contains various Dapr concepts. The goal of these documents is to expand your knowledge on the [Dapr spec](https://github.com/dapr/spec). - - -## Core Concepts - -* [Terminology](./terminology/terminology.md) -* [Bindings](./bindings/Readme.md) -* [Pub-sub](./publish-subscribe-messaging/Readme.md) -* [Secrets](./components/secrets.md) -* [State](./state-management/state-management.md) -* [Tracing](./tracing-logging/tracing-logging.md) - -## Actors - -* [Overview](./actor/actor_overview.md) -* [Features](./actor/actors_features.md) - -## Extensibility - -* [Redis](./components/redis.md) +# Dapr concepts + +This directory contains various Dapr concepts. The goal of these documents is to expand your knowledge on the [Dapr spec](https://github.com/dapr/spec). + +## Core Concepts + +* [**Bindings**](./bindings/Readme.md) + + A binding provides defines a bi-directional connection to an external cloud/on-premise service or system. Dapr allows you to invoke the external service through the standard Dapr binding API, and it allows your application to be triggered by events snet by the connected service. + +* **Components** + + Dapr uses a modular design, in which functionalities are grouped and delivered by a number of *components*, such as [pub-sub](./components/redis.md) and [secrets](./components/secrets.md). Many of the components are pluggable so that you can swap out the default implemenation with your custom implementations. + +* [**Distributed Tracing**](./tracing-logging/tracing-logging.md) + + Distirbuted tracing collects and aggregates trace events by transactions. It allows you to trace the entire call chain across multiple services. Dapr integrates with [OpenTelemetry](https://opentelemetry.io/) for distributed tracing and metrics collection. + +* [**Pub-sub**](./publish-subscribe-messaging/pub-sub-messaging.md) + + Pub-sub is a loosely coupled messaging pattern where senders (or publishers) publishes messages to a topic, to which subscribers subscribe. Dapr natively supports the pub-sub pattern. + +* [**Secrets**](./components/secrets.md) + + In Dapr, a secret is any piece of private information that you want to guard against unwanted users. Dapr offers a simple secret API and integrates with secret stores such as Azure Key Vault and Kubernetes secret stores to store the secrets. + +* [**State**](./state-management/state-management.md) + + Application state is anything an application wants to perserve beyound a single session. Dapr allows pluggable state stores behind a key/value-based state API. + +* [Terminology](./terminology/terminology.md) +* [Bindings](./bindings/Readme.md) +* [Pub-sub](./publish-subscribe-messaging/Readme.md) +* [Secrets](./components/secrets.md) +* [State](./state-management/state-management.md) +* [Tracing](./tracing-logging/tracing-logging.md) + +## Actors + +* [Overview](./actor/actor_overview.md) +* [Features](./actor/actors_features.md) + +## Extensibility + +* [Redis](./components/redis.md) \ No newline at end of file diff --git a/concepts/state-management/state-management.md b/concepts/state-management/state-management.md index 85715f72b..ac80baca8 100644 --- a/concepts/state-management/state-management.md +++ b/concepts/state-management/state-management.md @@ -83,5 +83,7 @@ SELECT AVG(value) FROM StateTable WHERE Id LIKE '--*-temper ## References * [Spec: Dapr state managment specification](https://github.com/dapr/spec/blob/master/state.md) * [Spec: Dapr actors specification](https://github.com/dapr/spec/blob/master/actors.md) -* [How-to: Query Redis store](../../howto/query-state-store/query-redis-store.md) +* [How-to: Set up Azure Cosmos DB store](../../howto/setup-state-store/setup-azure-cosmosdb.md) * [How-to: Query Azure Cosmos DB store](../../howto/query-state-store/query-cosmosdb-store.md) +* [How-to: Set up Redis store](../../howto/setup-state-store/setup-redis.md) +* [How-to: Query Redis store](../../howto/query-state-store/query-redis-store.md) diff --git a/concepts/terminology/terminology.md b/concepts/terminology/terminology.md deleted file mode 100644 index 808829791..000000000 --- a/concepts/terminology/terminology.md +++ /dev/null @@ -1,3 +0,0 @@ -# documentation - -Content for this file to be added diff --git a/concepts/tracing-logging/tracing-logging.md b/concepts/tracing-logging/tracing-logging.md index 808829791..3e5f6ca34 100644 --- a/concepts/tracing-logging/tracing-logging.md +++ b/concepts/tracing-logging/tracing-logging.md @@ -1,3 +1,41 @@ -# documentation +# Distributed tracing + +Dapr uses OpenTelemetry (previously known as OpenCensus) for distributed traces and metrics collection. OpenTelemetry supports various backends including [Azure Monitor](https://azure.microsoft.com/en-us/services/monitor/), [Datadog](https://www.datadoghq.com), [Instana](https://www.instana.com), [Jaeger](https://www.jaegertracing.io/), [SignalFX](https://www.signalfx.com/), [Stackdriver](https://cloud.google.com/stackdriver), [Zipkin](https://zipkin.io) and others. + +![Tracing](../../images/tracing.png) + +# Tracing Design + +Dapr adds a HTTP/grPC middleware to the Dapr sidecar. The middleware intercepts all Dapr and application traffic and automatically injects correlation IDs to trace distributed transactions. This design has several benifits: + +* No need for code instrumentation. All traffic is automatically traced (with configurable tracing levels). +* Consistent tracing behavior across microservices. Tracing a configured and managed on Dapr sidecar so that it remains consistent across services made by different teams and potentially written in different programming languages. +* Configurable and extensible. By leveraging OpenTelemetry, Dapr tracing can be configured to work with popular tracing backends, including custom backends a customer may have. + +# Correlation ID + +For HTTP requests, Dapr injects a **X-Correlation-ID** header to requests. For gRPC calls, Dapr inserts a **X-Correlation-ID** as a field of a **header** metadata. When a request arrives without an correlation ID, Dapr creates a new one. Otherwise, it passes the correlation ID along the call chain. + +# Configuration + +Dapr tracing is configured by a configuration file (in local mode) or a Kubernetes configuration object (in Kubernetes mode). For example, to define a Zipkin exporter, define the following configuration object: + +```yaml +apiVersion: actions.io/v1alpha1 +kind: Configuration +metadata: + name: zipkin +spec: + tracing: + enabled: true + exporterType: zipkin + exporterAddress: "http://zipkin.default.svc.cluster.local:9411/api/v2/spans" + expandParams: true + includeBody: true +``` + +Please see the [References](#references) section for more details on how to configure tracing on local environment and Kubernetes environment. + +# References +* [How-to: set up distributed tracing](../../howto/diagnose-with-tracing/readme.md) -Content for this file to be added diff --git a/howto/diagnose-with-tracing/Readme.md b/howto/diagnose-with-tracing/Readme.md new file mode 100644 index 000000000..b5f0e9273 --- /dev/null +++ b/howto/diagnose-with-tracing/Readme.md @@ -0,0 +1,120 @@ +# Set up distributed tracing + +Dapr integrates seamlessly with OpenTelemetry for telemtry and tracing. It is recommended to run Dapr with tracing enabled for any production scenario. Since Dapr uses OpenTelemetry, you can configure various exporters for tracing and telemtry data based on your environment, whether it is running in the cloud or on-premises. + +## How to configure distributed tracing with Zipkin on Kubernetes + +The following steps will show you how to configure Dapr to send distributed tracing data to Zipkin running as a container in your Kubernetes cluster, and how to view them. + + +### Setup + +First, deploy Zipkin: + +``` +kubectl run zipkin --image openzipkin/zipkin --port 9411 +``` + +Create a Kubernetes Service for the Zipkin pod: + +``` +kubectl expose deploy zipkin --type ClusterIP --port 9411 +``` + +Next, create the following YAML file locally: + +``` +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: zipkin +spec: + tracing: + enabled: true + exporterType: zipkin + exporterAddress: "http://zipkin.default.svc.cluster.local:9411/api/v2/spans" + expandParams: true + includeBody: true +``` + +Finally, deploy the Dapr configuration: + +``` +kubectl apply -f config.yaml +``` + +In order to enable this configuration for your Dapr sidecar, add the following annotation to your pod spec template: + +``` +annotations: + dapr.io/config: "zipkin" +``` + +That's it! your sidecar is now configured for use with Open Census and Zipkin. + +### Viewing Tracing Data + +To view traces, connect to the Zipkin Service and open the UI: + +``` +kubectl port-forward svc/zipkin 9411:9411 +``` + +On your browser, go to ```http://localhost:9411``` and you should see the Zipkin UI. + +![zipkin](../../images/zipkin_ui.png) + +## How to configure distributed tracing with Zipkin when running in stand-alone mode + +For standalone mode, create an Dapr Configuration CRD file locally and reference it with the Dapr CLI. + +1. Create the following YAML file: + +``` +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: zipkin +spec: + tracing: + enabled: true + exporterType: zipkin + exporterAddress: "http://localhost:9411/api/v2/spans" + expandParams: true + includeBody: true +``` + +2. Launch Zipkin using Docker: + +``` +docker run -d -p 9411:9411 openzipkin/zipkin +``` + +3. Launch Dapr with the `--config` param: + +``` +dapr run --app-id mynode --app-port 3000 --config ./cofig.yaml node app.js +``` + +## Tracing Configuration + +The `tracing` section under the `Configuration` spec contains the following properties: + +``` +tracing: + enabled: true + exporterType: zipkin + exporterAddress: "" + expandParams: true + includeBody: true +``` + +The following table lists the different properties. + +Property | Type | Description +---- | ------- | ----------- +enabled | bool | Set tracing to be enabled or disabled +exporterType | string | Name of the Open Census exporter to use. For example: Zipkin, Azure Monitor, etc +exporterAddress | string | URL of the exporter +expandParams | bool | When true, expands parameters passed to HTTP endpoints +includeBody | bool | When true, includes the request body in the tracing event diff --git a/howto/diagnose-with-tracing/setup-development-environment.md b/howto/diagnose-with-tracing/setup-development-environment.md deleted file mode 100644 index 808829791..000000000 --- a/howto/diagnose-with-tracing/setup-development-environment.md +++ /dev/null @@ -1,3 +0,0 @@ -# documentation - -Content for this file to be added diff --git a/howto/diagnose-with-tracing/setup-kubernetes-environment.md b/howto/diagnose-with-tracing/setup-kubernetes-environment.md deleted file mode 100644 index 808829791..000000000 --- a/howto/diagnose-with-tracing/setup-kubernetes-environment.md +++ /dev/null @@ -1,3 +0,0 @@ -# documentation - -Content for this file to be added diff --git a/images/tracing.png b/images/tracing.png new file mode 100644 index 0000000000000000000000000000000000000000..21e6afd207affdfffa6a4492ae7282f9d35646a2 GIT binary patch literal 18158 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