Miroslav Zagorac | 70230c6 | 2020-12-09 16:54:31 +0100 | [diff] [blame] | 1 | ----------------------------------------- |
| 2 | The HAProxy OpenTracing filter (OT) |
| 3 | Version 1.0 |
| 4 | ( Last update: 2020-12-10 ) |
| 5 | ----------------------------------------- |
| 6 | Author : Miroslav Zagorac |
| 7 | Contact : mzagorac at haproxy dot com |
| 8 | |
| 9 | |
| 10 | SUMMARY |
| 11 | -------- |
| 12 | |
| 13 | 0. Terms |
| 14 | 1. Introduction |
| 15 | 2. Build instructions |
| 16 | 3. Basic concepts in OpenTracing |
| 17 | 4. OT configuration |
| 18 | 4.1. OT scope |
| 19 | 4.2. "ot-tracer" section |
| 20 | 4.3. "ot-scope" section |
| 21 | 4.4. "ot-group" section |
| 22 | 5. Examples |
| 23 | 5.1 Benchmarking results |
| 24 | 6. OT CLI |
| 25 | 7. Known bugs and limitations |
| 26 | |
| 27 | |
| 28 | 0. Terms |
| 29 | --------- |
| 30 | |
| 31 | * OT: The HAProxy OpenTracing filter |
| 32 | |
| 33 | OT is the HAProxy filter that allows you to send data to distributed |
| 34 | tracing systems via the OpenTracing API. |
| 35 | |
| 36 | |
| 37 | 1. Introduction |
| 38 | ---------------- |
| 39 | |
| 40 | Nowadays there is a growing need to divide a process into microservices and |
| 41 | there is a problem of monitoring the work of the same process. One way to |
| 42 | solve this problem is to use distributed tracing service in a central location, |
| 43 | the so-called tracer. |
| 44 | |
| 45 | OT is a feature introduced in HAProxy 2.4. This filter enables communication |
| 46 | via the OpenTracing API with OpenTracing compatible servers (tracers). |
| 47 | Currently, tracers that support this API include Datadog, Jaeger, LightStep |
| 48 | and Zipkin. |
| 49 | |
| 50 | The OT filter was primarily tested with the Jaeger tracer, while configurations |
| 51 | for both Datadog and Zipkin tracers were also set in the test directory. |
| 52 | |
| 53 | The OT filter is a standard HAProxy filter, so what applies to others also |
| 54 | applies to this one (of course, by that I mean what is described in the |
| 55 | documentation, more precisely in the doc/internals/filters.txt file). |
| 56 | |
| 57 | The OT filter activation is done explicitly by specifying it in the HAProxy |
| 58 | configuration. If this is not done, the OT filter in no way participates |
| 59 | in the work of HAProxy. |
| 60 | |
| 61 | As for the impact on HAProxy speed, this is documented with several tests |
| 62 | located in the test directory, and the result is found in the README-speed-* |
| 63 | files. In short, the speed of operation depends on the way it is used and |
| 64 | the complexity of the configuration, from an almost immeasurable impact to |
| 65 | a significant deceleration (5x and more). I think that in some normal use |
| 66 | the speed of HAProxy with the filter on will be quite satisfactory with a |
| 67 | slowdown of less than 4% (provided that no more than 10% of requests are |
| 68 | sent to the tracer, which is determined by the keyword 'rate-limit'). |
| 69 | |
| 70 | The OT filter allows intensive use of ACLs, which can be defined anywhere in |
| 71 | the configuration. Thus, it is possible to use the filter only for those |
| 72 | connections that are of interest to us. |
| 73 | |
| 74 | |
| 75 | 2. Build instructions |
| 76 | ---------------------- |
| 77 | |
| 78 | OT is the HAProxy filter and as such is compiled together with HAProxy. |
| 79 | |
| 80 | To communicate with some OpenTracing compatible tracer, the OT filter uses the |
| 81 | OpenTracing C Wrapper library (which again uses the OpenTracing CPP library). |
| 82 | This means that we must have both libraries installed on the system on which |
| 83 | we want to compile or use HAProxy. |
| 84 | |
| 85 | Instructions for compiling and installing both required libraries can be |
| 86 | found at https://github.com/haproxytech/opentracing-c-wrapper . |
| 87 | |
| 88 | Also, to use the OT filter when running HAProxy we need to have an OpenTracing |
| 89 | plugin for the tracer we want to use. We will return to this later, in |
| 90 | section 5. |
| 91 | |
| 92 | The OT filter can be more easily compiled using the pkg-config tool, if we |
| 93 | have the OpenTracing C Wrapper library installed so that it contains pkg-config |
| 94 | files (which have the .pc extension). If the pkg-config tool cannot be used, |
| 95 | then the path to the directory where the include files and libraries are |
| 96 | located can be explicitly specified. |
| 97 | |
| 98 | Below are examples of the two ways to compile HAProxy with the OT filter, the |
| 99 | first using the pkg-congfig tool and the second explicitly specifying the path |
| 100 | to the OpenTracing C Wrapper include and library. |
| 101 | |
| 102 | Note: prompt '%' indicates that the command is executed under a unprivileged |
| 103 | user, while prompt '#' indicates that the command is executed under the |
| 104 | root user. |
| 105 | |
| 106 | Example of compiling HAProxy using the pkg-congfig tool (assuming the |
| 107 | OpenTracing C Wrapper library is installed in the /opt directory): |
| 108 | |
| 109 | % PKG_CONFIG_PATH=/opt/lib/pkgconfig make USE_OT=1 TARGET=linux-glibc |
| 110 | |
| 111 | The OT filter can also be compiled in debug mode as follows: |
| 112 | |
| 113 | % PKG_CONFIG_PATH=/opt/lib/pkgconfig make USE_OT=1 OT_DEBUG=1 TARGET=linux-glibc |
| 114 | |
| 115 | HAProxy compilation example explicitly specifying path to the OpenTracing C |
| 116 | Wrapper include and library: |
| 117 | |
| 118 | % make USE_OT=1 OT_INC=/opt/include OT_LIB=/opt/lib TARGET=linux-glibc |
| 119 | |
| 120 | In case we want to use debug mode, then it looks like this: |
| 121 | |
| 122 | % make USE_OT=1 OT_DEBUG=1 OT_INC=/opt/include OT_LIB=/opt/lib TARGET=linux-glibc |
| 123 | |
| 124 | If the library we want to use is not installed on a unix system, then a locally |
| 125 | installed library can be used (say, which is compiled and installed in the user |
| 126 | home directory). In this case instead of /opt/include and /opt/lib the |
| 127 | equivalent paths to the local installation should be specified. Of course, |
| 128 | in that case the pkg-config tool can also be used if we have a complete |
| 129 | installation (with .pc files). |
| 130 | |
| 131 | last but not least, if the pkg-config tool is not used when compiling, then |
| 132 | HAProxy executable may not be able to find the OpenTracing C Wrapper library |
| 133 | at startup. This can be solved in several ways, for example using the |
| 134 | LD_LIBRARY_PATH environment variable which should be set to the path where the |
| 135 | library is located before starting the HAProxy. |
| 136 | |
| 137 | % LD_LIBRARY_PATH=/opt/lib /path-to/haproxy ... |
| 138 | |
| 139 | Another way is to add RUNPATH to HAProxy executable that contains the path to |
| 140 | the library in question. |
| 141 | |
| 142 | % make USE_OT=1 OT_RUNPATH=1 OT_INC=/opt/include OT_LIB=/opt/lib TARGET=linux-glibc |
| 143 | |
| 144 | After HAProxy is compiled, we can check if the OT filter is enabled: |
| 145 | |
| 146 | % ./haproxy -vv | grep opentracing |
| 147 | --- command output ---------- |
| 148 | [ OT] opentracing |
| 149 | --- command output ---------- |
| 150 | |
| 151 | |
| 152 | 3. Basic concepts in OpenTracing |
| 153 | --------------------------------- |
| 154 | |
| 155 | Basic concepts of OpenTracing can be read on the OpenTracing documentation |
| 156 | website https://opentracing.io/docs/overview/. |
| 157 | |
| 158 | Here we will list only the most important elements of distributed tracing and |
| 159 | these are 'trace', 'span' and 'span context'. Trace is a description of the |
| 160 | complete transaction we want to record in the tracing system. A span is an |
| 161 | operation that represents a unit of work that is recorded in a tracing system. |
| 162 | Span context is a group of information related to a particular span that is |
| 163 | passed on to the system (from service to service). Using this context, we can |
| 164 | add new spans to already open trace (or supplement data in already open spans). |
| 165 | |
| 166 | An individual span may contain one or more tags, logs and baggage items. |
| 167 | The tag is a key-value element that is valid for the entire span. Log is a |
| 168 | key-value element that allows you to write some data at a certain time, it |
| 169 | can be used for debugging. A baggage item is a key-value data pair that can |
| 170 | be used for the duration of an entire trace, from the moment it is added to |
| 171 | the span. |
| 172 | |
| 173 | |
| 174 | 4. OT configuration |
| 175 | -------------------- |
| 176 | |
| 177 | In order for the OT filter to be used, it must be included in the HAProxy |
| 178 | configuration, in the proxy section (frontend / listen / backend): |
| 179 | |
| 180 | frontend ot-test |
| 181 | ... |
| 182 | filter opentracing [id <id>] config <file> |
| 183 | ... |
| 184 | |
| 185 | If no filter id is specified, 'ot-filter' is used as default. The 'config' |
| 186 | parameter must be specified and it contains the path of the file used to |
| 187 | configure the OT filter. |
| 188 | |
| 189 | |
| 190 | 4.1 OT scope |
| 191 | ------------- |
| 192 | |
| 193 | If the filter id is defined for the OT filter, then the OT scope with |
| 194 | the same name should be defined in the configuration file. In the same |
| 195 | configuration file we can have several defined OT scopes. |
| 196 | |
| 197 | Each OT scope must have a defined (only one) "ot-tracer" section that is |
| 198 | used to configure the operation of the OT filter and define the used groups |
| 199 | and scopes. |
| 200 | |
| 201 | OT scope starts with the id of the filter specified in square brackets and |
| 202 | ends with the end of the file or when a new OT scope is defined. |
| 203 | |
| 204 | For example, this defines two OT scopes in the same configuration file: |
| 205 | [my-first-ot-filter] |
| 206 | ot-tracer tracer1 |
| 207 | ... |
| 208 | ot-group group1 |
| 209 | ... |
| 210 | ot-scope scope1 |
| 211 | ... |
| 212 | |
| 213 | [my-second-ot-filter] |
| 214 | ... |
| 215 | |
| 216 | |
| 217 | 4.2. "ot-tracer" section |
| 218 | ------------------------- |
| 219 | |
| 220 | Only one "ot-tracer" section must be defined for each OT scope. |
| 221 | |
| 222 | There are several keywords that must be defined for the OT filter to work. |
| 223 | These are 'config' which defines the configuration file for the OpenTracing |
| 224 | API, and 'plugin' which defines the OpenTracing plugin used. |
| 225 | |
| 226 | Through optional keywords can be defined ACLs, logging, rate limit, and groups |
| 227 | and scopes that define the tracing model. |
| 228 | |
| 229 | |
| 230 | ot-tracer <name> |
| 231 | A new OT with the name <name> is created. |
| 232 | |
| 233 | Arguments : |
| 234 | name - the name of the tracer section |
| 235 | |
| 236 | |
| 237 | The following keywords are supported in this section: |
| 238 | - mandatory keywords: |
| 239 | - config |
| 240 | - plugin |
| 241 | |
| 242 | - optional keywords: |
| 243 | - acl |
| 244 | - debug-level |
| 245 | - groups |
| 246 | - [no] log |
| 247 | - [no] option disabled |
| 248 | - [no] option dontlog-normal |
| 249 | - [no] option hard-errors |
| 250 | - rate-limit |
| 251 | - scopes |
| 252 | |
| 253 | |
| 254 | acl <aclname> <criterion> [flags] [operator] <value> ... |
| 255 | Declare or complete an access list. |
| 256 | |
| 257 | To configure and use the ACL, see section 7 of the HAProxy Configuration |
| 258 | Manual. |
| 259 | |
| 260 | |
| 261 | config <file> |
| 262 | 'config' is one of the two mandatory keywords associated with the OT tracer |
| 263 | configuration. This keyword sets the path of the configuration file for the |
| 264 | OpenTracing tracer plugin. To set the contents of this configuration file, |
| 265 | it is best to look at the documentation related to the OpenTracing tracer we |
| 266 | want to use. |
| 267 | |
| 268 | Arguments : |
| 269 | file - the path of the configuration file |
| 270 | |
| 271 | |
| 272 | debug-level <value> |
| 273 | This keyword sets the value of the debug level related to the display of |
| 274 | debug messages in the OT filter. The 'debug-level' value is binary, ie |
| 275 | a single value bit enables or disables the display of the corresponding |
| 276 | debug message that uses that bit. The default value is set via the |
| 277 | FLT_OT_DEBUG_LEVEL macro in the include/config.h file. Debug level value |
| 278 | is used only if the OT filter is compiled with the debug mode enabled, |
| 279 | otherwise it is ignored. |
| 280 | |
| 281 | Arguments : |
| 282 | value - binary value ranging from 0 to 255 (8 bits) |
| 283 | |
| 284 | |
| 285 | groups <name> ... |
| 286 | A list of "ot-group" groups used for the currently defined tracer is declared. |
| 287 | Several groups can be specified in one line. |
| 288 | |
| 289 | Arguments : |
| 290 | name - the name of the OT group |
| 291 | |
| 292 | |
| 293 | log global |
| 294 | log <addr> [len <len>] [format <fmt>] <facility> [<level> [<minlevel>]] |
| 295 | no log |
| 296 | Enable per-instance logging of events and traffic. |
| 297 | |
| 298 | To configure and use the logging system, see section 4.2 of the HAProxy |
| 299 | Configuration Manual. |
| 300 | |
| 301 | |
| 302 | option disabled |
| 303 | no option disabled |
| 304 | Keyword which turns the operation of the OT filter on or off. By default |
| 305 | the filter is on. |
| 306 | |
| 307 | |
| 308 | option dontlog-normal |
| 309 | no option dontlog-normal |
| 310 | Enable or disable logging of normal, successful processing. By default, |
| 311 | this option is disabled. For this option to be considered, logging must |
| 312 | be turned on. |
| 313 | |
| 314 | See also: 'log' keyword description. |
| 315 | |
| 316 | |
| 317 | option hard-errors |
| 318 | no option hard-errors |
| 319 | During the operation of the filter, some errors may occur, caused by |
| 320 | incorrect configuration of the tracer or some error related to the operation |
| 321 | of HAProxy. By default, such an error will not interrupt the filter |
| 322 | operation for the stream in which the error occurred. If the 'hard-error' |
| 323 | option is enabled, the operation error prohibits all further processing of |
| 324 | events and groups in the stream in which the error occurred. |
| 325 | |
| 326 | |
| 327 | plugin <file> |
| 328 | 'plugin' is one of the two mandatory keywords associated with the OT tracer |
| 329 | configuration. This keyword sets the path of the OpenTracing tracer plugin. |
| 330 | |
| 331 | Arguments : |
| 332 | file - the name of the plugin used |
| 333 | |
| 334 | |
| 335 | rate-limit <value> |
| 336 | This option allows limiting the use of the OT filter, ie it can be influenced |
| 337 | whether the OT filter is activated for a stream or not. Determining whether |
| 338 | or not a filter is activated depends on the value of this option that is |
| 339 | compared to a randomly selected value when attaching the filter to the stream. |
| 340 | By default, the value of this option is set to 100.0, ie the OT filter is |
| 341 | activated for each stream. |
| 342 | |
| 343 | Arguments : |
| 344 | value - floating point value ranging from 0.0 to 100.0 |
| 345 | |
| 346 | |
| 347 | scopes <name> ... |
| 348 | This keyword declares a list of "ot-scope" definitions used for the currently |
| 349 | defined tracer. Multiple scopes can be specified in the same line. |
| 350 | |
| 351 | Arguments : |
| 352 | name - the name of the OT scope |
| 353 | |
| 354 | |
| 355 | 4.3. "ot-scope" section |
| 356 | ------------------------ |
| 357 | |
| 358 | Stream processing begins with filter attachment, then continues with the |
| 359 | processing of a number of defined events and groups, and ends with filter |
| 360 | detachment. The "ot-scope" section is used to define actions related to |
| 361 | individual events. However, this section may be part of a group, so the |
| 362 | event does not have to be part of the definition. |
| 363 | |
| 364 | |
| 365 | ot-scope <name> |
| 366 | Creates a new OT scope definition named <name>. |
| 367 | |
| 368 | Arguments : |
| 369 | name - the name of the OT scope |
| 370 | |
| 371 | |
| 372 | The following keywords are supported in this section: |
| 373 | - acl |
| 374 | - baggage |
| 375 | - event |
| 376 | - extract |
| 377 | - finish |
| 378 | - inject |
| 379 | - log |
| 380 | - span |
| 381 | - tag |
| 382 | |
| 383 | |
| 384 | acl <aclname> <criterion> [flags] [operator] <value> ... |
| 385 | Declare or complete an access list. |
| 386 | |
| 387 | To configure and use the ACL, see section 7 of the HAProxy Configuration |
| 388 | Manual. |
| 389 | |
| 390 | |
| 391 | baggage <name> <sample> ... |
| 392 | Baggage items allow the propagation of data between spans, ie allow the |
| 393 | assignment of metadata that is propagated to future children spans. |
| 394 | This data is formatted in the style of key-value pairs and is part of |
| 395 | the context that can be transferred between processes that are part of |
| 396 | a server architecture. |
| 397 | |
| 398 | This kewyord allows setting the baggage for the currently active span. The |
| 399 | data type is always a string, ie any sample type is converted to a string. |
| 400 | The exception is a binary value that is not supported by the OT filter. |
| 401 | |
| 402 | See the 'tag' keyword description for the data type conversion table. |
| 403 | |
| 404 | Arguments : |
| 405 | name - key part of a data pair |
| 406 | sample - sample expression (value part of a data pair), at least one |
| 407 | sample must be present |
| 408 | |
| 409 | |
| 410 | event <name> [{ if | unless } <condition>] |
| 411 | Set the event that triggers the 'ot-scope' to which it is assigned. |
| 412 | Optionally, it can be followed by an ACL-based condition, in which case it |
| 413 | will only be evaluated if the condition is true. |
| 414 | |
| 415 | ACL-based conditions are executed in the context of a stream that processes |
| 416 | the client and server connections. To configure and use the ACL, see |
| 417 | section 7 of the HAProxy Configuration Manual. |
| 418 | |
| 419 | Arguments : |
| 420 | name - the event name |
| 421 | condition - a standard ACL-based condition |
| 422 | |
| 423 | Supported events are (the table gives the names of the events in the OT |
| 424 | filter and the corresponding equivalent in the SPOE filter): |
| 425 | |
| 426 | -------------------------------------|------------------------------ |
| 427 | the OT filter | the SPOE filter |
| 428 | -------------------------------------|------------------------------ |
| 429 | on-client-session-start | on-client-session |
| 430 | on-frontend-tcp-request | on-frontend-tcp-request |
| 431 | on-http-wait-request | - |
| 432 | on-http-body-request | - |
| 433 | on-frontend-http-request | on-frontend-http-request |
| 434 | on-switching-rules-request | - |
| 435 | on-backend-tcp-request | on-backend-tcp-request |
| 436 | on-backend-http-request | on-backend-http-request |
| 437 | on-process-server-rules-request | - |
| 438 | on-http-process-request | - |
| 439 | on-tcp-rdp-cookie-request | - |
| 440 | on-process-sticking-rules-request | - |
| 441 | on-client-session-end | - |
| 442 | on-server-unavailable | - |
| 443 | -------------------------------------|------------------------------ |
| 444 | on-server-session-start | on-server-session |
| 445 | on-tcp-response | on-tcp-response |
| 446 | on-http-wait-response | - |
| 447 | on-process-store-rules-response | - |
| 448 | on-http-response | on-http-response |
| 449 | on-server-session-end | - |
| 450 | -------------------------------------|------------------------------ |
| 451 | |
| 452 | |
| 453 | extract <name-prefix> [use-vars | use-headers] |
| 454 | For a more detailed description of the propagation process of the span |
| 455 | context, see the description of the keyword 'inject'. Only the process |
| 456 | of extracting data from the carrier is described here. |
| 457 | |
| 458 | Arguments : |
| 459 | name-prefix - data name prefix (ie key element prefix) |
| 460 | use-vars - data is extracted from HAProxy variables |
| 461 | use-headers - data is extracted from the HTTP header |
| 462 | |
| 463 | |
| 464 | Below is an example of using HAProxy variables to transfer span context data: |
| 465 | |
| 466 | --- test/ctx/ot.cfg -------------------------------------------------------- |
| 467 | ... |
| 468 | ot-scope client_session_start_2 |
| 469 | extract "ot_ctx_1" use-vars |
| 470 | span "Client session" child-of "ot_ctx_1" |
| 471 | ... |
| 472 | ---------------------------------------------------------------------------- |
| 473 | |
| 474 | |
| 475 | finish <name> ... |
| 476 | Closing a particular span or span context. Instead of the name of the span, |
| 477 | there are several specially predefined names with which we can finish certain |
| 478 | groups of spans. So it can be used as the name '*req*' for all open spans |
| 479 | related to the request channel, '*res*' for all open spans related to the |
| 480 | response channel and '*' for all open spans regardless of which channel they |
| 481 | are related to. Several spans and/or span contexts can be specified in one |
| 482 | line. |
| 483 | |
| 484 | Arguments : |
| 485 | name - the name of the span or context context |
| 486 | |
| 487 | |
| 488 | inject <name-prefix> [use-vars] [use-headers] |
| 489 | In OpenTracing, the transfer of data related to the tracing process between |
| 490 | microservices that are part of a larger service is done through the |
| 491 | propagation of the span context. The basic operations that allow us to |
| 492 | access and transfer this data are 'inject' and 'extract'. |
| 493 | |
| 494 | 'inject' allows us to extract span context so that the obtained data can |
| 495 | be forwarded to another process (microservice) via the selected carrier. |
| 496 | 'inject' in the name actually means inject data into carrier. Carrier is |
| 497 | an interface here (ie a data structure) that allows us to transfer tracing |
| 498 | state from one process to another. |
| 499 | |
| 500 | Data transfer can take place via one of two selected storage methods, the |
| 501 | first is by adding data to the HTTP header and the second is by using HAProxy |
| 502 | variables. Only data transfer via HTTP header can be used to transfer data |
| 503 | to another process (ie microservice). All data is organized in the form of |
| 504 | key-value data pairs. |
| 505 | |
| 506 | No matter which data transfer method you use, we need to specify a prefix |
| 507 | for the key element. All alphanumerics (lowercase only) and underline |
| 508 | character can be used to construct the data name prefix. Uppercase letters |
| 509 | can actually be used, but they will be converted to lowercase when creating |
| 510 | the prefix. |
| 511 | |
| 512 | Arguments : |
| 513 | name-prefix - data name prefix (ie key element prefix) |
| 514 | use-vars - HAProxy variables are used to store and transfer data |
| 515 | use-headers - HTTP headers are used to store and transfer data |
| 516 | |
| 517 | |
| 518 | Below is an example of using HTTP headers and variables, and how this is |
| 519 | reflected in the internal data of the HAProxy process. |
| 520 | |
| 521 | --- test/ctx/ot.cfg -------------------------------------------------------- |
| 522 | ... |
| 523 | ot-scope client_session_start_1 |
| 524 | span "HAProxy session" root |
| 525 | inject "ot_ctx_1" use-headers use-vars |
| 526 | ... |
| 527 | ---------------------------------------------------------------------------- |
| 528 | |
| 529 | - generated HAProxy variable (key -> value): |
| 530 | txn.ot_ctx_1.uberDtraceDid -> 8f1a05a3518d2283:8f1a05a3518d2283:0:1 |
| 531 | |
| 532 | - generated HTTP header (key: value): |
| 533 | ot_ctx_1-uber-trace-id: 8f1a05a3518d2283:8f1a05a3518d2283:0:1 |
| 534 | |
| 535 | Because HAProxy does not allow the '-' character in the variable name (which |
| 536 | is automatically generated by the OpenTracing API and on which we have no |
| 537 | influence), it is converted to the letter 'D'. We can see that there is no |
| 538 | such conversion in the name of the HTTP header because the '-' sign is allowed |
| 539 | there. Due to this conversion, initially all uppercase letters are converted |
| 540 | to lowercase because otherwise we would not be able to distinguish whether |
| 541 | the disputed sign '-' is used or not. |
| 542 | |
| 543 | Thus created HTTP headers and variables are deleted when executing the |
| 544 | 'finish' keyword or when detaching the stream from the filter. |
| 545 | |
| 546 | |
| 547 | log <name> <sample> ... |
| 548 | This kewyord allows setting the log for the currently active span. The |
| 549 | data type is always a string, ie any sample type is converted to a string. |
| 550 | The exception is a binary value that is not supported by the OT filter. |
| 551 | |
| 552 | See the 'tag' keyword description for the data type conversion table. |
| 553 | |
| 554 | Arguments : |
| 555 | name - key part of a data pair |
| 556 | sample - sample expression (value part of a data pair), at least one |
| 557 | sample must be present |
| 558 | |
| 559 | |
| 560 | span <name> [<reference>] |
| 561 | Creating a new span (or referencing an already opened one). If a new span |
| 562 | is created, it can be a child of the referenced span, follow from the |
| 563 | referenced span, or be root 'span'. In case we did not specify a reference |
| 564 | to the previously created span, the new span will become the root span. |
| 565 | We need to pay attention to the fact that in one trace there can be only |
| 566 | one root span. In case we have specified a non-existent span as a reference, |
| 567 | a new span will not be created. |
| 568 | |
| 569 | Arguments : |
| 570 | name - the name of the span being created or referenced (operation |
| 571 | name) |
| 572 | reference - span or span context to which the created span is referenced |
| 573 | |
| 574 | |
| 575 | tag <name> <sample> ... |
| 576 | This kewyord allows setting a tag for the currently active span. The first |
| 577 | argument is the name of the tag (tag ID) and the second its value. A value |
| 578 | can consist of one or more data. If the value is only one data, then the |
| 579 | type of that data depends on the type of the HAProxy sample. If the value |
| 580 | contains more data, then the data type is string. The data conversion table |
| 581 | is below: |
| 582 | |
| 583 | HAProxy sample data type | the OpenTracing data type |
| 584 | --------------------------+--------------------------- |
| 585 | NULL | NULL |
| 586 | BOOL | BOOL |
| 587 | INT32 | INT64 |
| 588 | UINT32 | UINT64 |
| 589 | INT64 | INT64 |
| 590 | UINT64 | UINT64 |
| 591 | IPV4 | STRING |
| 592 | IPV6 | STRING |
| 593 | STRING | STRING |
| 594 | BINARY | UNSUPPORTED |
| 595 | --------------------------+--------------------------- |
| 596 | |
| 597 | Arguments : |
| 598 | name - key part of a data pair |
| 599 | sample - sample expression (value part of a data pair), at least one |
| 600 | sample must be present |
| 601 | |
| 602 | |
| 603 | 4.4. "ot-group" section |
| 604 | ------------------------ |
| 605 | |
| 606 | This section allows us to define a group of OT scopes, that is not activated |
| 607 | via an event but is triggered from TCP or HTTP rules. More precisely, these |
| 608 | are the following rules: 'tcp-request', 'tcp-response', 'http-request', |
| 609 | 'http-response' and 'http-after-response'. These rules can be defined in the |
| 610 | HAProxy configuration file. |
| 611 | |
| 612 | |
| 613 | ot-group <name> |
| 614 | Creates a new OT group definition named <name>. |
| 615 | |
| 616 | Arguments : |
| 617 | name - the name of the OT group |
| 618 | |
| 619 | |
| 620 | The following keywords are supported in this section: |
| 621 | - scopes |
| 622 | |
| 623 | |
| 624 | scopes <name> ... |
| 625 | 'ot-scope' sections that are part of the specified group are defined. If |
| 626 | the mentioned 'ot-scope' sections are used only in some OT group, they do |
| 627 | not have to have defined events. Several 'ot-scope' sections can be |
| 628 | specified in one line. |
| 629 | |
| 630 | Arguments : |
| 631 | name - the name of the 'ot-scope' section |
| 632 | |
| 633 | |
| 634 | 5. Examples |
| 635 | ------------ |
| 636 | |
| 637 | Several examples of the OT filter configuration can be found in the test |
| 638 | directory. A brief description of the prepared configurations follows: |
| 639 | |
| 640 | cmp - the configuration very similar to that of the spoa-opentracing project. |
| 641 | It was made to compare the speed of the OT filter with the |
| 642 | implementation of distributed tracing via spoa-opentracing application. |
| 643 | |
| 644 | sa - the configuration in which all possible events are used. |
| 645 | |
| 646 | ctx - the configuration is very similar to the previous one, with the only |
| 647 | difference that the spans are opened using the span context as a span |
| 648 | reference. |
| 649 | |
| 650 | fe be - a slightly more complicated example of the OT filter configuration |
| 651 | that uses two cascaded HAProxy services. The span context between |
| 652 | HAProxy processes is transmitted via the HTTP header. |
| 653 | |
| 654 | empty - the empty configuration in which the OT filter is initialized but |
| 655 | no event is triggered. It is not very usable, except to check the |
| 656 | behavior of the OT filter in the case of a similar configuration. |
| 657 | |
| 658 | |
| 659 | In order to be able to collect data (and view results via the web interface) |
| 660 | we need to install some of the supported tracers. We will use the Jaeger |
| 661 | tracer as an example. Installation instructions can be found on the website |
| 662 | https://www.jaegertracing.io/download/. For the impatient, here we will list |
| 663 | how the image to test the operation of the tracer system can be installed |
| 664 | without much reading of the documentation. |
| 665 | |
| 666 | # docker pull jaegertracing/all-in-one:latest |
| 667 | # docker run -d --name jaeger -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \ |
| 668 | -p 5775:5775/udp -p 6831:6831/udp -p 6832:6832/udp -p 5778:5778 \ |
| 669 | -p 16686:16686 -p 14268:14268 -p 9411:9411 jaegertracing/all-in-one:latest |
| 670 | |
| 671 | The last command will also initialize and run the Jaeger container. If we |
| 672 | want to use that container later, it can be started and stopped in the classic |
| 673 | way, using the 'docker container start/stop' commands. |
| 674 | |
| 675 | |
| 676 | In order to be able to use any of the configurations from the test directory, |
| 677 | we must also have a tracer plugin in that directory (all examples use the |
| 678 | Jaeger tracer plugin). The simplest way is to download the tracer plugin |
| 679 | using the already prepared shell script get-opentracing-plugins.sh. |
| 680 | The script accepts one argument, the directory in which the download is made. |
| 681 | If run without an argument, the script downloads all plugins to the current |
| 682 | directory. |
| 683 | |
| 684 | % ./get-opentracing-plugins.sh |
| 685 | |
| 686 | After that, we can run one of the pre-configured configurations using the |
| 687 | provided script run-xxx.sh (where xxx is the name of the configuration being |
| 688 | tested). For example: |
| 689 | |
| 690 | % ./run-sa.sh |
| 691 | |
| 692 | The script will create a new log file each time it is run (because part of the |
| 693 | log file name is the start time of the script). |
| 694 | |
| 695 | Eh, someone will surely notice that all test configurations use the Jaeger |
| 696 | tracing plugin that cannot be downloaded using the get-opentracing-plugins.sh |
| 697 | script. Unfortunately, the latest precompiled version that can be downloaded |
| 698 | is 0.4.2, for newer ones only the source code can be found. Version 0.4.2 has |
| 699 | a bug that can cause the operation of the OT filter to get stuck, so it is |
| 700 | better not to use this version. Here is the procedure by which we can compile |
| 701 | a newer version of the plugin (in our example it is 0.5.0). |
| 702 | |
| 703 | Important note: the GCC version must be at least 4.9 or later. |
| 704 | |
| 705 | % wget https://github.com/jaegertracing/jaeger-client-cpp/archive/v0.5.0.tar.gz |
| 706 | % tar xf v0.5.0.tar.gz |
| 707 | % cd jaeger-client-cpp-0.5.0 |
| 708 | % mkdir build |
| 709 | % cd build |
| 710 | % cmake -DCMAKE_INSTALL_PREFIX=/opt -DJAEGERTRACING_PLUGIN=ON -DHUNTER_CONFIGURATION_TYPES=Release -DHUNTER_BUILD_SHARED_LIBS=OFF .. |
| 711 | % make |
| 712 | |
| 713 | After the plugin is compiled, it will be in the current directory. The name |
| 714 | of the plugin is libjaegertracing_plugin.so. |
| 715 | |
| 716 | |
| 717 | 5.1. Benchmarking results |
| 718 | -------------------------- |
| 719 | |
| 720 | To check the operation of the OT filter, several different test configurations |
| 721 | have been made which are located in the test directory. The test results of |
| 722 | the same configurations (with the names README-speed-xxx, where xxx is the name |
| 723 | of the configuration being tested) are also in the directory of the same name. |
| 724 | |
| 725 | All tests were performed on the same debian 9.13 system, CPU i7-4770, 32 GB RAM. |
| 726 | For the purpose of testing, the thttpd web server on port 8000 was used. |
| 727 | Testing was done with the wrk utility running via run-xxx.sh scripts; that is, |
| 728 | via the test-speed.sh script that is run as follows: |
| 729 | |
| 730 | % ./test-speed.sh all |
| 731 | |
| 732 | The above mentioned thttpd web server is run from that script and it should be |
| 733 | noted that we need to have the same installed on the system (or change the path |
| 734 | to the thttpd server in that script if it is installed elsewhere). |
| 735 | |
| 736 | Each test is performed several times over a period of 5 minutes per individual |
| 737 | test. The only difference when running the tests for the same configuration |
| 738 | was in changing the 'rate-limit' parameter (and the 'option disabled' option), |
| 739 | which is set to the following values: 100.0, 50.0, 10.0, 2.5 and 0.0 percent. |
| 740 | Then a test is performed with the OT filter active but disabled for request |
| 741 | processing ('option disabled' is included in the ot.cfg configuration). In |
| 742 | the last test, the OT filter is not used at all, ie it is not active and does |
| 743 | not affect the operation of HAProxy in any way. |
| 744 | |
| 745 | |
| 746 | 6. OT CLI |
| 747 | ---------- |
| 748 | |
| 749 | Via the HAProxy CLI interface we can find out the current status of the OT |
| 750 | filter and change several of its settings. |
| 751 | |
| 752 | All supported CLI commands can be found in the following way, using the |
| 753 | socat utility with the assumption that the HAProxy CLI socket path is set |
| 754 | to /tmp/haproxy.sock (of course, instead of socat, nc or other utility can |
| 755 | be used with a change in arguments when running the same): |
| 756 | |
| 757 | % echo "help" | socat - UNIX-CONNECT:/tmp/haproxy.sock | grep flt-ot |
| 758 | --- command output ---------- |
| 759 | flt-ot debug [level] : set the OT filter debug level (default: get current debug level) |
| 760 | flt-ot disable : disable the OT filter |
| 761 | flt-ot enable : enable the OT filter |
| 762 | flt-ot soft-errors : turning off hard-errors mode |
| 763 | flt-ot hard-errors : enabling hard-errors mode |
| 764 | flt-ot logging [state] : set logging state (default: get current logging state) |
| 765 | flt-ot rate [value] : set the rate limit (default: get current rate value) |
| 766 | flt-ot status : show the OT filter status |
| 767 | --- command output ---------- |
| 768 | |
| 769 | 'flt-ot debug' can only be used in case the OT filter is compiled with the |
| 770 | debug mode enabled. |
| 771 | |
| 772 | |
| 773 | 7. Known bugs and limitations |
| 774 | ------------------------------ |
| 775 | |
| 776 | The name of the span context definition can contain only letters, numbers and |
| 777 | characters '_' and '-'. Also, all uppercase letters in the name are converted |
| 778 | to lowercase. The character '-' is converted internally to the 'D' character, |
| 779 | and since a HAProxy variable is generated from that name, this should be taken |
| 780 | into account if we want to use it somewhere in the HAProxy configuration. |
| 781 | The above mentioned span context is used in the 'inject' and 'extract' keywords. |
| 782 | |
| 783 | Let's look a little at the example test/fe-be (configurations are in the |
| 784 | test/fe and test/be directories, 'fe' is here the abbreviation for frontend |
| 785 | and 'be' for backend). In case we have the 'rate-limit' set to a value less |
| 786 | than 100.0, then distributed tracing will not be started with each new HTTP |
| 787 | request. It also means that the span context will not be delivered (via the |
| 788 | HTTP header) to the backend HAProxy process. The 'rate-limit' on the backend |
| 789 | HAProxy must be set to 100.0, but because the frontend HAProxy does not send |
| 790 | a span context every time, all such cases will cause an error to be reported |
| 791 | on the backend server. Therefore, the 'hard-errors' option must be set on the |
| 792 | backend server, so that processing on that stream is stopped as soon as the |
| 793 | first error occurs. Such cases will slow down the backend server's response |
| 794 | a bit (in the example in question it is about 3%). |