Merge branch 'v2.0' into v2.1
This commit is contained in:
commit
89919dbe36
52 changed files with 677 additions and 336 deletions
26
CHANGELOG.md
26
CHANGELOG.md
|
@ -1,3 +1,29 @@
|
|||
## [v2.0.6](https://github.com/containous/traefik/tree/v2.0.6) (2019-12-02)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v2.0.5...v2.0.6)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Update go-acme/lego to 3.2.0 ([#5839](https://github.com/containous/traefik/pull/5839) by [kolaente](https://github.com/kolaente))
|
||||
- **[cli,healthcheck]** Uses, if it exists, the ping entry point provided in the static configuration ([#5867](https://github.com/containous/traefik/pull/5867) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[healthcheck]** Healthcheck managed for all related services ([#5860](https://github.com/containous/traefik/pull/5860) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[logs,middleware]** Do not give responsewriter or its headers to asynchronous logging goroutine ([#5840](https://github.com/containous/traefik/pull/5840) by [mpl](https://github.com/mpl))
|
||||
- **[middleware]** X-Forwarded-Proto must not skip the redirection. ([#5836](https://github.com/containous/traefik/pull/5836) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** fix: location header rewrite. ([#5835](https://github.com/containous/traefik/pull/5835) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Remove Request Headers CORS Preflight Requirement ([#5903](https://github.com/containous/traefik/pull/5903) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[rancher]** Change service name in rancher provider to make webui service details view work ([#5895](https://github.com/containous/traefik/pull/5895) by [SantoDE](https://github.com/SantoDE))
|
||||
- **[tracing]** Fix extraction for zipkin tracing ([#5920](https://github.com/containous/traefik/pull/5920) by [jcchavezs](https://github.com/jcchavezs))
|
||||
- **[webui]** Web UI: Avoid unnecessary duplicated api calls ([#5884](https://github.com/containous/traefik/pull/5884) by [matthieuh](https://github.com/matthieuh))
|
||||
- **[webui]** Web UI: Avoid some router properties to overflow their container ([#5872](https://github.com/containous/traefik/pull/5872) by [matthieuh](https://github.com/matthieuh))
|
||||
- **[webui]** Web UI: Fix displayed tcp service details ([#5868](https://github.com/containous/traefik/pull/5868) by [matthieuh](https://github.com/matthieuh))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** doc: fix wrong acme information ([#5837](https://github.com/containous/traefik/pull/5837) by [ldez](https://github.com/ldez))
|
||||
- **[docker,docker/swarm]** Add Swarm section to the Docker Provider Documentation ([#5874](https://github.com/containous/traefik/pull/5874) by [dduportal](https://github.com/dduportal))
|
||||
- **[docker]** Update router entrypoint example ([#5766](https://github.com/containous/traefik/pull/5766) by [woto](https://github.com/woto))
|
||||
- **[k8s/helm]** Mention the experimental Helm Chart in the installation section of documentation ([#5879](https://github.com/containous/traefik/pull/5879) by [dduportal](https://github.com/dduportal))
|
||||
- doc: remove double quotes on CLI flags. ([#5862](https://github.com/containous/traefik/pull/5862) by [ldez](https://github.com/ldez))
|
||||
- Fixed spelling error ([#5834](https://github.com/containous/traefik/pull/5834) by [blakebuthod](https://github.com/blakebuthod))
|
||||
- Add back the security section from v1 ([#5832](https://github.com/containous/traefik/pull/5832) by [pascalandy](https://github.com/pascalandy))
|
||||
|
||||
## [v2.1.0-rc2](https://github.com/containous/traefik/tree/v2.0.4) (2019-11-15)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v2.0.0-rc1...v2.1.0-rc2)
|
||||
|
||||
|
|
|
@ -51,9 +51,14 @@ func Do(staticConfiguration static.Configuration) (*http.Response, error) {
|
|||
return nil, errors.New("please enable `ping` to use health check")
|
||||
}
|
||||
|
||||
pingEntryPoint, ok := staticConfiguration.EntryPoints["traefik"]
|
||||
ep := staticConfiguration.Ping.EntryPoint
|
||||
if ep == "" {
|
||||
ep = "traefik"
|
||||
}
|
||||
|
||||
pingEntryPoint, ok := staticConfiguration.EntryPoints[ep]
|
||||
if !ok {
|
||||
return nil, errors.New("missing `ping` entrypoint")
|
||||
return nil, fmt.Errorf("ping: missing %s entry point", ep)
|
||||
}
|
||||
|
||||
client := &http.Client{Timeout: 5 * time.Second}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
You can install Traefik with the following flavors:
|
||||
|
||||
* [Use the official Docker image](./#use-the-official-docker-image)
|
||||
* [(Experimental) Use the Helm Chart](./#use-the-helm-chart)
|
||||
* [Use the binary distribution](./#use-the-binary-distribution)
|
||||
* [Compile your binary from the sources](./#compile-your-binary-from-the-sources)
|
||||
|
||||
|
@ -24,6 +25,70 @@ For more details, go to the [Docker provider documentation](../providers/docker.
|
|||
* Docker images are based from the [Alpine Linux Official image](https://hub.docker.com/_/alpine).
|
||||
* All the orchestrator using docker images could fetch the official Traefik docker image.
|
||||
|
||||
## Use the Helm Chart
|
||||
|
||||
!!! warning "Experimental Helm Chart"
|
||||
|
||||
Please note that the Helm Chart for Traefik v2 is still experimental.
|
||||
|
||||
The Traefik Stable Chart from
|
||||
[Helm's default charts repository](https://github.com/helm/charts/tree/master/stable/traefik) is still using [Traefik v1.7](https://docs.traefik.io/v1.7).
|
||||
|
||||
Traefik can be installed in Kubernetes using the v2.0 Helm chart from <https://github.com/containous/traefik-helm-chart>.
|
||||
|
||||
Ensure that the following requirements are met:
|
||||
|
||||
* Kubernetes 1.14+
|
||||
* Helm version 2.x is [installed](https://v2.helm.sh/docs/using_helm/) and initialized with Tiller
|
||||
|
||||
Retrieve the latest chart version from the repository:
|
||||
|
||||
```bash
|
||||
# Retrieve Chart from the repository
|
||||
git clone https://github.com/containous/traefik-helm-chart
|
||||
```
|
||||
|
||||
And install it with the `helm` command line:
|
||||
|
||||
```bash
|
||||
helm install ./traefik-helm-chart
|
||||
```
|
||||
|
||||
!!! tip "Helm Features"
|
||||
|
||||
All [Helm features](https://v2.helm.sh/docs/using_helm/#using-helm) are supported.
|
||||
For instance, installing the chart in a dedicated namespace:
|
||||
|
||||
```bash tab="Install in a Dedicated Namespace"
|
||||
# Install in the namespace "traefik-v2"
|
||||
helm install --namespace=traefik-v2 \
|
||||
./traefik-helm-chart
|
||||
```
|
||||
|
||||
??? example "Installing with Custom Values"
|
||||
|
||||
You can customize the installation by specifying custom values,
|
||||
as with [any helm chart](https://v2.helm.sh/docs/using_helm/#customizing-the-chart-before-installing).
|
||||
{: #helm-custom-values }
|
||||
|
||||
The values are not (yet) documented, but are self-explanatory:
|
||||
you can look at the [default `values.yaml`](https://github.com/containous/traefik-helm-chart/blob/master/values.yaml) file to explore possibilities.
|
||||
|
||||
Example of installation with logging set to `DEBUG`:
|
||||
|
||||
```bash tab="Using Helm CLI"
|
||||
helm install --namespace=traefik-v2 \
|
||||
--set="logs.loglevel=DEBUG" \
|
||||
./traefik-helm-chart
|
||||
```
|
||||
|
||||
```yml tab="With a custom values file"
|
||||
# File custom-values.yml
|
||||
## Install with "helm install --values=./custom-values.yml ./traefik-helm-chart
|
||||
logs:
|
||||
loglevel: DEBUG
|
||||
```
|
||||
|
||||
## Use the Binary Distribution
|
||||
|
||||
Grab the latest binary from the [releases](https://github.com/containous/traefik/releases) page.
|
||||
|
|
|
@ -47,11 +47,11 @@ You can configure Traefik to use an ACME provider (like Let's Encrypt) for autom
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--entryPoints.web.address=":80"
|
||||
--entryPoints.websecure.address=":443"
|
||||
--entryPoints.web.address=:80
|
||||
--entryPoints.websecure.address=:443
|
||||
# ...
|
||||
--certificatesResolvers.sample.acme.email="your-email@your-domain.org"
|
||||
--certificatesResolvers.sample.acme.storage="acme.json"
|
||||
--certificatesResolvers.sample.acme.email=your-email@your-domain.org
|
||||
--certificatesResolvers.sample.acme.storage=acme.json
|
||||
# used during the challenge
|
||||
--certificatesResolvers.sample.acme.httpChallenge.entryPoint=web
|
||||
```
|
||||
|
@ -156,8 +156,8 @@ when using the `HTTP-01` challenge, `certificatesResolvers.sample.acme.httpChall
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--entryPoints.web.address=":80"
|
||||
--entryPoints.websecure.address=":443"
|
||||
--entryPoints.web.address=:80
|
||||
--entryPoints.websecure.address=:443
|
||||
# ...
|
||||
--certificatesResolvers.sample.acme.httpChallenge.entryPoint=web
|
||||
```
|
||||
|
@ -312,7 +312,7 @@ certificatesResolvers:
|
|||
|
||||
```bash tab="CLI"
|
||||
# ...
|
||||
--certificatesResolvers.sample.acme.dnsChallenge.resolvers:="1.1.1.1:53,8.8.8.8:53"
|
||||
--certificatesResolvers.sample.acme.dnsChallenge.resolvers:=1.1.1.1:53,8.8.8.8:53
|
||||
```
|
||||
|
||||
#### Wildcard Domains
|
||||
|
@ -342,7 +342,7 @@ As described in [Let's Encrypt's post](https://community.letsencrypt.org/t/stagi
|
|||
|
||||
```bash tab="CLI"
|
||||
# ...
|
||||
--certificatesResolvers.sample.acme.caServer="https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
--certificatesResolvers.sample.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
# ...
|
||||
```
|
||||
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
#
|
||||
# Required
|
||||
#
|
||||
--certificatesResolvers.sample.acme.email="test@traefik.io"
|
||||
--certificatesResolvers.sample.acme.email=test@traefik.io
|
||||
|
||||
# File or key used for certificates storage.
|
||||
#
|
||||
# Required
|
||||
#
|
||||
--certificatesResolvers.sample.acme.storage="acme.json"
|
||||
--certificatesResolvers.sample.acme.storage=acme.json
|
||||
|
||||
# CA server to use.
|
||||
# Uncomment the line to use Let's Encrypt's staging server,
|
||||
|
@ -19,7 +19,7 @@
|
|||
# Optional
|
||||
# Default: "https://acme-v02.api.letsencrypt.org/directory"
|
||||
#
|
||||
--certificatesResolvers.sample.acme.caServer="https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
--certificatesResolvers.sample.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
|
||||
# KeyType to use.
|
||||
#
|
||||
|
@ -75,7 +75,7 @@
|
|||
# Optional
|
||||
# Default: empty
|
||||
#
|
||||
--certificatesResolvers.sample.acme.dnsChallenge.resolvers="1.1.1.1:53,8.8.8.8:53"
|
||||
--certificatesResolvers.sample.acme.dnsChallenge.resolvers=1.1.1.1:53,8.8.8.8:53
|
||||
|
||||
# Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready.
|
||||
#
|
||||
|
|
|
@ -718,11 +718,11 @@ with the path `/admin` stripped, e.g. to `http://<IP>:<port>/`. In this case, yo
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--entryPoints.web.address=":80"
|
||||
--entryPoints.websecure.address=":443"
|
||||
--certificatesResolvers.sample.acme.email: your-email@your-domain.org
|
||||
--certificatesResolvers.sample.acme.storage: acme.json
|
||||
--certificatesResolvers.sample.acme.httpChallenge.entryPoint: web
|
||||
--entryPoints.web.address=:80
|
||||
--entryPoints.websecure.address=:443
|
||||
--certificatesResolvers.sample.acme.email=your-email@your-domain.org
|
||||
--certificatesResolvers.sample.acme.storage=acme.json
|
||||
--certificatesResolvers.sample.acme.httpChallenge.entryPoint=web
|
||||
```
|
||||
|
||||
## Traefik Logs
|
||||
|
@ -744,9 +744,9 @@ There is no more log configuration at the root level.
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--logLevel="DEBUG"
|
||||
--traefikLog.filePath="/path/to/traefik.log"
|
||||
--traefikLog.format="json"
|
||||
--logLevel=DEBUG
|
||||
--traefikLog.filePath=/path/to/traefik.log
|
||||
--traefikLog.format=json
|
||||
```
|
||||
|
||||
!!! info "v2"
|
||||
|
@ -768,9 +768,9 @@ There is no more log configuration at the root level.
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--log.level="DEBUG"
|
||||
--log.filePath="/path/to/traefik.log"
|
||||
--log.format="json"
|
||||
--log.level=DEBUG
|
||||
--log.filePath=/path/to/traefik.log
|
||||
--log.format=json
|
||||
```
|
||||
|
||||
## Tracing
|
||||
|
@ -794,12 +794,12 @@ Traefik v2 retains OpenTracing support. The `backend` root option from the v1 is
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.backend="jaeger"
|
||||
--tracing.servicename="tracing"
|
||||
--tracing.jaeger.localagenthostport="12.0.0.1:6831"
|
||||
--tracing.jaeger.samplingparam="1.0"
|
||||
--tracing.jaeger.samplingserverurl="http://12.0.0.1:5778/sampling"
|
||||
--tracing.jaeger.samplingtype="const"
|
||||
--tracing.backend=jaeger
|
||||
--tracing.servicename=tracing
|
||||
--tracing.jaeger.localagenthostport=12.0.0.1:6831
|
||||
--tracing.jaeger.samplingparam=1.0
|
||||
--tracing.jaeger.samplingserverurl=http://12.0.0.1:5778/sampling
|
||||
--tracing.jaeger.samplingtype=const
|
||||
```
|
||||
|
||||
!!! info "v2"
|
||||
|
@ -827,11 +827,11 @@ Traefik v2 retains OpenTracing support. The `backend` root option from the v1 is
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.servicename="tracing"
|
||||
--tracing.jaeger.localagenthostport="12.0.0.1:6831"
|
||||
--tracing.jaeger.samplingparam="1.0"
|
||||
--tracing.jaeger.samplingserverurl="http://12.0.0.1:5778/sampling"
|
||||
--tracing.jaeger.samplingtype="const"
|
||||
--tracing.servicename=tracing
|
||||
--tracing.jaeger.localagenthostport=12.0.0.1:6831
|
||||
--tracing.jaeger.samplingparam=1.0
|
||||
--tracing.jaeger.samplingserverurl=http://12.0.0.1:5778/sampling
|
||||
--tracing.jaeger.samplingtype=const
|
||||
```
|
||||
|
||||
## Metrics
|
||||
|
@ -852,7 +852,7 @@ For a basic configuration, the [metrics configuration](../observability/metrics/
|
|||
|
||||
```bash tab="CLI"
|
||||
--metrics.prometheus.buckets=[0.1,0.3,1.2,5.0]
|
||||
--metrics.prometheus.entrypoint="traefik"
|
||||
--metrics.prometheus.entrypoint=traefik
|
||||
```
|
||||
|
||||
!!! info "v2"
|
||||
|
@ -878,7 +878,7 @@ For a basic configuration, the [metrics configuration](../observability/metrics/
|
|||
|
||||
```bash tab="CLI"
|
||||
--metrics.prometheus.buckets=[0.1,0.3,1.2,5.0]
|
||||
--metrics.prometheus.entrypoint="metrics"
|
||||
--metrics.prometheus.entrypoint=metrics
|
||||
```
|
||||
|
||||
## No More Root Level Key/Values
|
||||
|
@ -908,14 +908,14 @@ Each root item has been moved to a related section or removed.
|
|||
```bash tab="CLI"
|
||||
--checknewversion=false
|
||||
--sendanonymoususage=true
|
||||
--loglevel="DEBUG"
|
||||
--loglevel=DEBUG
|
||||
--insecureskipverify=true
|
||||
--rootcas="/mycert.cert"
|
||||
--rootcas=/mycert.cert
|
||||
--maxidleconnsperhost=200
|
||||
--providersthrottleduration="2s"
|
||||
--providersthrottleduration=2s
|
||||
--allowminweightzero=true
|
||||
--debug=true
|
||||
--defaultentrypoints="web","web-secure"
|
||||
--defaultentrypoints=web,web-secure
|
||||
--keeptrailingslash=true
|
||||
```
|
||||
|
||||
|
@ -961,9 +961,9 @@ Each root item has been moved to a related section or removed.
|
|||
```bash tab="CLI"
|
||||
--global.checknewversion=true
|
||||
--global.sendanonymoususage=true
|
||||
--log.level="DEBUG"
|
||||
--log.level=DEBUG
|
||||
--serverstransport.insecureskipverify=true
|
||||
--serverstransport.rootcas="/mycert.cert"
|
||||
--serverstransport.rootcas=/mycert.cert
|
||||
--serverstransport.maxidleconnsperhost=42
|
||||
--providers.providersthrottleduration=42
|
||||
```
|
||||
|
@ -1029,7 +1029,7 @@ As the dashboard access is now secured by default you can either:
|
|||
[api]
|
||||
|
||||
[providers.file]
|
||||
filename = "/dynamic-conf.toml"
|
||||
filename = "/dynamic-conf.toml"
|
||||
|
||||
##---------------------##
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ accessLog:
|
|||
```bash tab="CLI"
|
||||
# Configuring a buffer of 100 lines
|
||||
--accesslog=true
|
||||
--accesslog.filepath="/path/to/access.log"
|
||||
--accesslog.filepath=/path/to/access.log
|
||||
--accesslog.bufferingsize=100
|
||||
```
|
||||
|
||||
|
@ -104,11 +104,11 @@ accessLog:
|
|||
```bash tab="CLI"
|
||||
# Configuring Multiple Filters
|
||||
--accesslog=true
|
||||
--accesslog.filepath="/path/to/access.log"
|
||||
--accesslog.format="json"
|
||||
--accesslog.filters.statuscodes="200, 300-302"
|
||||
--accesslog.filepath=/path/to/access.log
|
||||
--accesslog.format=json
|
||||
--accesslog.filters.statuscodes=200,300-302
|
||||
--accesslog.filters.retryattempts
|
||||
--accesslog.filters.minduration="10ms"
|
||||
--accesslog.filters.minduration=10ms
|
||||
```
|
||||
|
||||
### Limiting the Fields
|
||||
|
@ -164,14 +164,14 @@ accessLog:
|
|||
```bash tab="CLI"
|
||||
# Limiting the Logs to Specific Fields
|
||||
--accesslog=true
|
||||
--accesslog.filepath="/path/to/access.log"
|
||||
--accesslog.format="json"
|
||||
--accesslog.fields.defaultmode="keep"
|
||||
--accesslog.fields.names.ClientUsername="drop"
|
||||
--accesslog.fields.headers.defaultmode="keep"
|
||||
--accesslog.fields.headers.names.User-Agent="redact"
|
||||
--accesslog.fields.headers.names.Authorization="drop"
|
||||
--accesslog.fields.headers.names.Content-Type="keep"
|
||||
--accesslog.filepath=/path/to/access.log
|
||||
--accesslog.format=json
|
||||
--accesslog.fields.defaultmode=keep
|
||||
--accesslog.fields.names.ClientUsername=drop
|
||||
--accesslog.fields.headers.defaultmode=keep
|
||||
--accesslog.fields.headers.names.User-Agent=redact
|
||||
--accesslog.fields.headers.names.Authorization=drop
|
||||
--accesslog.fields.headers.names.Content-Type=keep
|
||||
```
|
||||
|
||||
??? info "Available Fields"
|
||||
|
|
|
@ -30,7 +30,7 @@ log:
|
|||
|
||||
```bash tab="CLI"
|
||||
# Writing Logs to a File
|
||||
--log.filePath="/path/to/traefik.log"
|
||||
--log.filePath=/path/to/traefik.log
|
||||
```
|
||||
|
||||
#### `format`
|
||||
|
@ -53,8 +53,8 @@ log:
|
|||
|
||||
```bash tab="CLI"
|
||||
# Writing Logs to a File, in JSON
|
||||
--log.filePath="/path/to/traefik.log"
|
||||
--log.format="json"
|
||||
--log.filePath=/path/to/traefik.log
|
||||
--log.format=json
|
||||
```
|
||||
|
||||
#### `level`
|
||||
|
@ -72,7 +72,7 @@ log:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--log.level="DEBUG"
|
||||
--log.level=DEBUG
|
||||
```
|
||||
|
||||
## Log Rotation
|
||||
|
|
|
@ -35,7 +35,7 @@ metrics:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.datadog.address="127.0.0.1:8125"
|
||||
--metrics.datadog.address=127.0.0.1:8125
|
||||
```
|
||||
|
||||
#### `addEntryPointsLabels`
|
||||
|
|
|
@ -35,7 +35,7 @@ metrics:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.influxdb.address="localhost:8089"
|
||||
--metrics.influxdb.address=localhost:8089
|
||||
```
|
||||
|
||||
#### `protocol`
|
||||
|
@ -57,7 +57,7 @@ metrics:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.influxdb.protocol="udp"
|
||||
--metrics.influxdb.protocol=udp
|
||||
```
|
||||
|
||||
#### `database`
|
||||
|
@ -69,17 +69,17 @@ InfluxDB database used when protocol is http.
|
|||
```toml tab="File (TOML)"
|
||||
[metrics]
|
||||
[metrics.influxDB]
|
||||
database = ""
|
||||
database = "db"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
metrics:
|
||||
influxDB:
|
||||
database: ""
|
||||
database: "db"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.influxdb.database=""
|
||||
--metrics.influxdb.database=db
|
||||
```
|
||||
|
||||
#### `retentionPolicy`
|
||||
|
@ -91,17 +91,17 @@ InfluxDB retention policy used when protocol is http.
|
|||
```toml tab="File (TOML)"
|
||||
[metrics]
|
||||
[metrics.influxDB]
|
||||
retentionPolicy = ""
|
||||
retentionPolicy = "two_hours"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
metrics:
|
||||
influxDB:
|
||||
retentionPolicy: ""
|
||||
retentionPolicy: "two_hours"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.influxdb.retentionPolicy=""
|
||||
--metrics.influxdb.retentionPolicy=two_hours
|
||||
```
|
||||
|
||||
#### `username`
|
||||
|
@ -113,17 +113,17 @@ InfluxDB username (only with http).
|
|||
```toml tab="File (TOML)"
|
||||
[metrics]
|
||||
[metrics.influxDB]
|
||||
username = ""
|
||||
username = "john"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
metrics:
|
||||
influxDB:
|
||||
username: ""
|
||||
username: "john"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.influxdb.username=""
|
||||
--metrics.influxdb.username=john
|
||||
```
|
||||
|
||||
#### `password`
|
||||
|
@ -135,17 +135,17 @@ InfluxDB password (only with http).
|
|||
```toml tab="File (TOML)"
|
||||
[metrics]
|
||||
[metrics.influxDB]
|
||||
password = ""
|
||||
password = "secret"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
metrics:
|
||||
influxDB:
|
||||
password: ""
|
||||
password: "secret"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.influxdb.password=""
|
||||
--metrics.influxdb.password=secret
|
||||
```
|
||||
|
||||
#### `addEntryPointsLabels`
|
||||
|
|
|
@ -113,8 +113,8 @@ metrics:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--entryPoints.metrics.address=":8082"
|
||||
--metrics.prometheus.entryPoint="metrics"
|
||||
--entryPoints.metrics.address=:8082
|
||||
--metrics.prometheus.entryPoint=metrics
|
||||
```
|
||||
|
||||
#### `manualRouting`
|
||||
|
|
|
@ -35,7 +35,7 @@ metrics:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--metrics.statsd.address="localhost:8125"
|
||||
--metrics.statsd.address=localhost:8125
|
||||
```
|
||||
|
||||
#### `addEntryPointsLabels`
|
||||
|
|
|
@ -35,7 +35,7 @@ tracing:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.datadog.localAgentHostPort="127.0.0.1:8126"
|
||||
--tracing.datadog.localAgentHostPort=127.0.0.1:8126
|
||||
```
|
||||
|
||||
#### `debug`
|
||||
|
@ -79,7 +79,7 @@ tracing:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.datadog.globalTag="sample"
|
||||
--tracing.datadog.globalTag=sample
|
||||
```
|
||||
|
||||
#### `prioritySampling`
|
||||
|
|
|
@ -35,7 +35,7 @@ tracing:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.haystack.localAgentHost="127.0.0.1"
|
||||
--tracing.haystack.localAgentHost=127.0.0.1
|
||||
```
|
||||
|
||||
#### `localAgentPort`
|
||||
|
@ -79,7 +79,7 @@ tracing:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.haystack.globalTag="sample:test"
|
||||
--tracing.haystack.globalTag=sample:test
|
||||
```
|
||||
|
||||
#### `traceIDHeaderName`
|
||||
|
@ -101,7 +101,7 @@ tracing:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.haystack.traceIDHeaderName="sample"
|
||||
--tracing.haystack.traceIDHeaderName=sample
|
||||
```
|
||||
|
||||
#### `parentIDHeaderName`
|
||||
|
@ -123,7 +123,7 @@ tracing:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.haystack.parentIDHeaderName="sample"
|
||||
--tracing.haystack.parentIDHeaderName=sample
|
||||
```
|
||||
|
||||
#### `spanIDHeaderName`
|
||||
|
@ -168,5 +168,5 @@ tracing:
|
|||
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.haystack.baggagePrefixHeaderName="sample"
|
||||
--tracing.haystack.baggagePrefixHeaderName=sample
|
||||
```
|
||||
|
|
|
@ -35,7 +35,7 @@ tracing:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.instana.localAgentHost="127.0.0.1"
|
||||
--tracing.instana.localAgentHost=127.0.0.1
|
||||
```
|
||||
|
||||
#### `localAgentPort`
|
||||
|
@ -86,5 +86,5 @@ tracing:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.instana.logLevel="info"
|
||||
--tracing.instana.logLevel=info
|
||||
```
|
||||
|
|
|
@ -39,7 +39,7 @@ tracing:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.jaeger.samplingServerURL="http://localhost:5778/sampling"
|
||||
--tracing.jaeger.samplingServerURL=http://localhost:5778/sampling
|
||||
```
|
||||
|
||||
#### `samplingType`
|
||||
|
@ -61,7 +61,7 @@ tracing:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.jaeger.samplingType="const"
|
||||
--tracing.jaeger.samplingType=const
|
||||
```
|
||||
|
||||
#### `samplingParam`
|
||||
|
@ -89,7 +89,7 @@ tracing:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.jaeger.samplingParam="1.0"
|
||||
--tracing.jaeger.samplingParam=1.0
|
||||
```
|
||||
|
||||
#### `localAgentHostPort`
|
||||
|
@ -111,7 +111,7 @@ tracing:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.jaeger.localAgentHostPort="127.0.0.1:6831"
|
||||
--tracing.jaeger.localAgentHostPort=127.0.0.1:6831
|
||||
```
|
||||
|
||||
#### `gen128Bit`
|
||||
|
@ -159,7 +159,7 @@ tracing:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.jaeger.propagation="jaeger"
|
||||
--tracing.jaeger.propagation=jaeger
|
||||
```
|
||||
|
||||
#### `traceContextHeaderName`
|
||||
|
@ -182,7 +182,7 @@ tracing:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.jaeger.traceContextHeaderName="uber-trace-id"
|
||||
--tracing.jaeger.traceContextHeaderName=uber-trace-id
|
||||
```
|
||||
|
||||
### `collector`
|
||||
|
@ -206,7 +206,7 @@ tracing:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.jaeger.collector.endpoint="http://127.0.0.1:14268/api/traces?format=jaeger.thrift"
|
||||
--tracing.jaeger.collector.endpoint=http://127.0.0.1:14268/api/traces?format=jaeger.thrift
|
||||
```
|
||||
|
||||
#### `user`
|
||||
|
@ -229,7 +229,7 @@ tracing:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.jaeger.collector.user="my-user"
|
||||
--tracing.jaeger.collector.user=my-user
|
||||
```
|
||||
|
||||
#### `password`
|
||||
|
@ -252,5 +252,5 @@ tracing:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.jaeger.collector.password="my-password"
|
||||
--tracing.jaeger.collector.password=my-password
|
||||
```
|
||||
|
|
|
@ -52,7 +52,7 @@ tracing:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.serviceName="traefik"
|
||||
--tracing.serviceName=traefik
|
||||
```
|
||||
|
||||
#### `spanNameLimit`
|
||||
|
|
|
@ -35,7 +35,7 @@ tracing:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.zipkin.httpEndpoint="http://localhost:9411/api/v2/spans"
|
||||
--tracing.zipkin.httpEndpoint=http://localhost:9411/api/v2/spans
|
||||
```
|
||||
|
||||
#### `sameSpan`
|
||||
|
@ -101,5 +101,5 @@ tracing:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--tracing.zipkin.sampleRate="0.2"
|
||||
--tracing.zipkin.sampleRate=0.2
|
||||
```
|
||||
|
|
|
@ -55,8 +55,8 @@ ping:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--entryPoints.ping.address=":8082"
|
||||
--ping.entryPoint="ping"
|
||||
--entryPoints.ping.address=:8082
|
||||
--ping.entryPoint=ping
|
||||
```
|
||||
|
||||
#### `manualRouting`
|
||||
|
|
|
@ -7,6 +7,9 @@ A Story of Labels & Containers
|
|||
|
||||
Attach labels to your containers and let Traefik do the rest!
|
||||
|
||||
Traefik works with both [Docker (standalone) Engine](https://docs.docker.com/engine/)
|
||||
and [Docker Swarm Mode](https://docs.docker.com/engine/swarm/).
|
||||
|
||||
!!! tip "The Quick Start Uses Docker"
|
||||
If you haven't already, maybe you'd like to go through the [quick start](../getting-started/quick-start.md) that uses the docker provider!
|
||||
|
||||
|
@ -64,7 +67,7 @@ Attach labels to your containers and let Traefik do the rest!
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.docker.endpoint="tcp://127.0.0.1:2375"
|
||||
--providers.docker.endpoint=tcp://127.0.0.1:2375
|
||||
--providers.docker.swarmMode=true
|
||||
```
|
||||
|
||||
|
@ -80,15 +83,136 @@ Attach labels to your containers and let Traefik do the rest!
|
|||
- traefik.http.services.my-container-service.loadbalancer.server.port=8080
|
||||
```
|
||||
|
||||
!!! important "Labels in Docker Swarm Mode"
|
||||
While in Swarm Mode, Traefik uses labels found on services, not on individual containers.
|
||||
|
||||
Therefore, if you use a compose file with Swarm Mode, labels should be defined in the `deploy` part of your service.
|
||||
This behavior is only enabled for docker-compose version 3+ ([Compose file reference](https://docs.docker.com/compose/compose-file/#labels-1)).
|
||||
|
||||
## Routing Configuration
|
||||
|
||||
See the dedicated section in [routing](../routing/providers/docker.md).
|
||||
When using Docker as a [provider](https://docs.traefik.io/providers/overview/),
|
||||
Trafik uses [container labels](https://docs.docker.com/engine/reference/commandline/run/#set-metadata-on-container--l---label---label-file) to retrieve its routing configuration.
|
||||
|
||||
See the list of labels in the dedicated [routing](../routing/providers/docker.md) section.
|
||||
|
||||
### Routing Configuration with Labels
|
||||
|
||||
By default, Traefik watches for [container level labels](https://docs.docker.com/config/labels-custom-metadata/) on a standalone Docker Engine.
|
||||
|
||||
When using Docker Compose, labels are specified by the directive
|
||||
[`labels`](https://docs.docker.com/compose/compose-file/#labels) from the
|
||||
["services" objects](https://docs.docker.com/compose/compose-file/#service-configuration-reference).
|
||||
|
||||
!!! tip "Not Only Docker"
|
||||
Please note that any tool like Nomad, Terraform, Ansible, etc.
|
||||
that is able to define a Docker container with labels can work
|
||||
with Traefik & the Docker provider.
|
||||
|
||||
### Port Detection
|
||||
|
||||
Traefik retrieves the private IP and port of containers from the Docker API.
|
||||
|
||||
Ports detection works as follows:
|
||||
|
||||
- If a container [exposes](https://docs.docker.com/engine/reference/builder/#expose) only one port,
|
||||
then Traefik uses this port for private communication.
|
||||
- If a container [exposes](https://docs.docker.com/engine/reference/builder/#expose) multiple ports,
|
||||
or does not expose any port, then you must manually specify which port Traefik should use for communication
|
||||
by using the label `traefik.http.services.<service_name>.loadbalancer.server.port`
|
||||
(Read more on this label in the dedicated section in [routing](../routing/providers/docker.md#port)).
|
||||
|
||||
### Docker API Access
|
||||
|
||||
Traefik requires access to the docker socket to get its dynamic configuration.
|
||||
|
||||
You can specify which Docker API Endpoint to use with the directive [`endpoint`](#endpoint).
|
||||
|
||||
!!! warning "Security Note"
|
||||
|
||||
Accessing the Docker API without any restriction is a security concern:
|
||||
If Traefik is attacked, then the attacker might get access to the underlying host.
|
||||
{: #security-note }
|
||||
|
||||
As explained in the Docker documentation: ([Docker Daemon Attack Surface page](https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface)):
|
||||
|
||||
!!! quote
|
||||
[...] only **trusted** users should be allowed to control your Docker daemon [...]
|
||||
|
||||
??? success "Solutions"
|
||||
|
||||
Expose the Docker socket over TCP, instead of the default Unix socket file.
|
||||
It allows different implementation levels of the [AAA (Authentication, Authorization, Accounting) concepts](https://en.wikipedia.org/wiki/AAA_(computer_security)), depending on your security assessment:
|
||||
|
||||
- Authentication with Client Certificates as described in ["Protect the Docker daemon socket."](https://docs.docker.com/engine/security/https/)
|
||||
- Authorize and filter requests to restrict possible actions with [the TecnativaDocker Socket Proxy](https://github.com/Tecnativa/docker-socket-proxy).
|
||||
- Authorization with the [Docker Authorization Plugin Mechanism](https://docs.docker.com/engine/extend/plugins_authorization/)
|
||||
- Accounting at networking level, by exposing the socket only inside a Docker private network, only available for Traefik.
|
||||
- Accounting at container level, by exposing the socket on a another container than Traefik's.
|
||||
With Swarm mode, it allows scheduling of Traefik on worker nodes, with only the "socket exposer" container on the manager nodes.
|
||||
- Accounting at kernel level, by enforcing kernel calls with mechanisms like [SELinux](https://en.wikipedia.org/wiki/Security-Enhanced_Linux), to only allows an identified set of actions for Traefik's process (or the "socket exposer" process).
|
||||
|
||||
??? info "More Resources and Examples"
|
||||
- ["Paranoid about mounting /var/run/docker.sock?"](https://medium.com/@containeroo/traefik-2-0-paranoid-about-mounting-var-run-docker-sock-22da9cb3e78c)
|
||||
- [Traefik and Docker: A Discussion with Docker Captain, Bret Fisher](https://blog.containo.us/traefik-and-docker-a-discussion-with-docker-captain-bret-fisher-7f0b9a54ff88)
|
||||
- [KubeCon EU 2018 Keynote, Running with Scissors, from Liz Rice](https://www.youtube.com/watch?v=ltrV-Qmh3oY)
|
||||
- [Don't expose the Docker socket (not even to a container)](https://www.lvh.io/posts/dont-expose-the-docker-socket-not-even-to-a-container/)
|
||||
- [A thread on Stack Overflow about sharing the `/var/run/docker.sock` file](https://news.ycombinator.com/item?id=17983623)
|
||||
- [To DinD or not to DinD](https://blog.loof.fr/2018/01/to-dind-or-not-do-dind.html)
|
||||
- [Traefik issue GH-4174 about security with Docker socket](https://github.com/containous/traefik/issues/4174)
|
||||
- [Inspecting Docker Activity with Socat](https://developers.redhat.com/blog/2015/02/25/inspecting-docker-activity-with-socat/)
|
||||
- [Letting Traefik run on Worker Nodes](https://blog.mikesir87.io/2018/07/letting-traefik-run-on-worker-nodes/)
|
||||
- [Docker Socket Proxy from Tecnativa](https://github.com/Tecnativa/docker-socket-proxy)
|
||||
|
||||
## Docker Swarm Mode
|
||||
|
||||
To enable Docker Swarm (instead of standalone Docker) as a configuration provider,
|
||||
set the [`swarmMode`](#swarmmode) directive to `true`.
|
||||
|
||||
### Routing Configuration with Labels
|
||||
|
||||
While in Swarm Mode, Traefik uses labels found on services, not on individual containers.
|
||||
|
||||
Therefore, if you use a compose file with Swarm Mode, labels should be defined in the
|
||||
[`deploy`](https://docs.docker.com/compose/compose-file/#labels-1) part of your service.
|
||||
|
||||
This behavior is only enabled for docker-compose version 3+ ([Compose file reference](https://docs.docker.com/compose/compose-file)).
|
||||
|
||||
### Port Detection
|
||||
|
||||
Docker Swarm does not provide any [port detection](#port-detection) information to Traefik.
|
||||
|
||||
Therefore you **must** specify the port to use for communication by using the label `traefik.http.services.<service_name>.loadbalancer.server.port`
|
||||
(Check the reference for this label in the [routing section for Docker](../routing/providers/docker.md#port)).
|
||||
|
||||
### Docker API Access
|
||||
|
||||
Docker Swarm Mode follows the same rules as Docker [API Access](#docker-api-access).
|
||||
|
||||
As the Swarm API is only exposed on the [manager nodes](https://docs.docker.com/engine/swarm/how-swarm-mode-works/nodes/#manager-nodes), you should schedule Traefik on the Swarm manager nodes by default,
|
||||
by deploying Traefik with a [constraint](https://success.docker.com/article/using-contraints-and-labels-to-control-the-placement-of-containers) on the node's "role":
|
||||
|
||||
```shell tab="With Docker CLI"
|
||||
docker service create \
|
||||
--constraint=node.role==manager \
|
||||
#... \
|
||||
```
|
||||
|
||||
```yml tab="With Docker Compose"
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
traefik:
|
||||
# ...
|
||||
deploy:
|
||||
placement:
|
||||
constraints:
|
||||
- node.role == manager
|
||||
```
|
||||
|
||||
!!! tip "Scheduling Traefik on Worker Nodes"
|
||||
|
||||
Following the guidelines given in the previous section ["Docker API Access"](#docker-api-access),
|
||||
if you expose the Docker API through TCP, then Traefik can be scheduled on any node if the TCP
|
||||
socket is reachable.
|
||||
|
||||
Please consider the security implications by reading the [Security Note](#security-note).
|
||||
|
||||
A good example can be found on [Bret Fisher's repository](https://github.com/BretFisher/dogvscat/blob/master/stack-proxy-global.yml#L124).
|
||||
|
||||
## Provider Configuration
|
||||
|
||||
|
@ -108,51 +232,10 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.docker.endpoint="unix:///var/run/docker.sock"
|
||||
--providers.docker.endpoint=unix:///var/run/docker.sock
|
||||
```
|
||||
|
||||
Traefik requires access to the docker socket to get its dynamic configuration.
|
||||
|
||||
??? warning "Security Notes"
|
||||
|
||||
Depending on your context, accessing the Docker API without any restriction can be a security concern: If Traefik is attacked, then the attacker might get access to the Docker (or Swarm Mode) backend.
|
||||
|
||||
As explained in the Docker documentation: ([Docker Daemon Attack Surface page](https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface)):
|
||||
|
||||
`[...] only **trusted** users should be allowed to control your Docker daemon [...]`
|
||||
|
||||
!!! tip "Improved Security"
|
||||
|
||||
[TraefikEE](https://containo.us/traefikee) solves this problem by separating the control plane (connected to Docker) and the data plane (handling the requests).
|
||||
|
||||
??? info "Resources about Docker's Security"
|
||||
|
||||
- [KubeCon EU 2018 Keynote, Running with Scissors, from Liz Rice](https://www.youtube.com/watch?v=ltrV-Qmh3oY)
|
||||
- [Don't expose the Docker socket (not even to a container)](https://www.lvh.io/posts/dont-expose-the-docker-socket-not-even-to-a-container/)
|
||||
- [A thread on Stack Overflow about sharing the `/var/run/docker.sock` file](https://news.ycombinator.com/item?id=17983623)
|
||||
- [To DinD or not to DinD](https://blog.loof.fr/2018/01/to-dind-or-not-do-dind.html)
|
||||
|
||||
??? tip "Security Compensation"
|
||||
|
||||
Expose the Docker socket over TCP, instead of the default Unix socket file.
|
||||
It allows different implementation levels of the [AAA (Authentication, Authorization, Accounting) concepts](https://en.wikipedia.org/wiki/AAA_(computer_security)), depending on your security assessment:
|
||||
|
||||
- Authentication with Client Certificates as described in ["Protect the Docker daemon socket."](https://docs.docker.com/engine/security/https/)
|
||||
- Authorization with the [Docker Authorization Plugin Mechanism](https://docs.docker.com/engine/extend/plugins_authorization/)
|
||||
- Accounting at networking level, by exposing the socket only inside a Docker private network, only available for Traefik.
|
||||
- Accounting at container level, by exposing the socket on a another container than Traefik's.
|
||||
With Swarm mode, it allows scheduling of Traefik on worker nodes, with only the "socket exposer" container on the manager nodes.
|
||||
- Accounting at kernel level, by enforcing kernel calls with mechanisms like [SELinux](https://en.wikipedia.org/wiki/Security-Enhanced_Linux), to only allows an identified set of actions for Traefik's process (or the "socket exposer" process).
|
||||
|
||||
??? info "Additional Resources"
|
||||
|
||||
- [Traefik issue GH-4174 about security with Docker socket](https://github.com/containous/traefik/issues/4174)
|
||||
- [Inspecting Docker Activity with Socat](https://developers.redhat.com/blog/2015/02/25/inspecting-docker-activity-with-socat/)
|
||||
- [Letting Traefik run on Worker Nodes](https://blog.mikesir87.io/2018/07/letting-traefik-run-on-worker-nodes/)
|
||||
- [Docker Socket Proxy from Tecnativa](https://github.com/Tecnativa/docker-socket-proxy)
|
||||
|
||||
!!! info "Traefik & Swarm Mode"
|
||||
To let Traefik access the Docker Socket of the Swarm manager, it is mandatory to schedule Traefik on the Swarm manager nodes.
|
||||
See the sections [Docker API Access](#docker-api-access) and [Docker Swarm API Access](#docker-api-access_1) for more information.
|
||||
|
||||
??? example "Using the docker.sock"
|
||||
|
||||
|
@ -186,7 +269,7 @@ Traefik requires access to the docker socket to get its dynamic configuration.
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.docker.endpoint="unix:///var/run/docker.sock"
|
||||
--providers.docker.endpoint=unix:///var/run/docker.sock
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@ -311,7 +394,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.docker.defaultRule="Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
||||
--providers.docker.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@ -343,7 +426,7 @@ providers:
|
|||
# ...
|
||||
```
|
||||
|
||||
Activates the Swarm Mode.
|
||||
Activates the Swarm Mode (instead of standalone Docker).
|
||||
|
||||
### `swarmModeRefreshSeconds`
|
||||
|
||||
|
@ -375,19 +458,19 @@ _Optional, Default=""_
|
|||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.docker]
|
||||
constraints = "Label(`a.label.name`, `foo`)"
|
||||
constraints = "Label(`a.label.name`,`foo`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
docker:
|
||||
constraints: "Label(`a.label.name`, `foo`)"
|
||||
constraints: "Label(`a.label.name`,`foo`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.docker.constraints="Label(`a.label.name`, `foo`)"
|
||||
--providers.docker.constraints=Label(`a.label.name`,`foo`)
|
||||
# ...
|
||||
```
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.kubernetescrd.endpoint="http://localhost:8080"
|
||||
--providers.kubernetescrd.endpoint=http://localhost:8080
|
||||
```
|
||||
|
||||
The Kubernetes server endpoint as URL.
|
||||
|
@ -66,7 +66,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.kubernetescrd.token="mytoken"
|
||||
--providers.kubernetescrd.token=mytoken
|
||||
```
|
||||
|
||||
Bearer token used for the Kubernetes client configuration.
|
||||
|
@ -89,7 +89,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.kubernetescrd.certauthfilepath="/my/ca.crt"
|
||||
--providers.kubernetescrd.certauthfilepath=/my/ca.crt
|
||||
```
|
||||
|
||||
Path to the certificate authority file.
|
||||
|
@ -115,7 +115,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.kubernetescrd.namespaces="default,production"
|
||||
--providers.kubernetescrd.namespaces=default,production
|
||||
```
|
||||
|
||||
Array of namespaces to watch.
|
||||
|
@ -164,7 +164,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.kubernetescrd.ingressclass="traefik-internal"
|
||||
--providers.kubernetescrd.ingressclass=traefik-internal
|
||||
```
|
||||
|
||||
Value of `kubernetes.io/ingress.class` annotation that identifies Ingress objects to be processed.
|
||||
|
@ -190,7 +190,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.kubernetescrd.throttleDuration="10s"
|
||||
--providers.kubernetescrd.throttleDuration=10s
|
||||
```
|
||||
|
||||
## Further
|
||||
|
|
|
@ -67,7 +67,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.kubernetesingress.endpoint="http://localhost:8080"
|
||||
--providers.kubernetesingress.endpoint=http://localhost:8080
|
||||
```
|
||||
|
||||
The Kubernetes server endpoint as URL, which is only used when the behavior based on environment variables described below does not apply.
|
||||
|
@ -99,7 +99,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.kubernetesingress.token="mytoken"
|
||||
--providers.kubernetesingress.token=mytoken
|
||||
```
|
||||
|
||||
Bearer token used for the Kubernetes client configuration.
|
||||
|
@ -122,7 +122,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.kubernetesingress.certauthfilepath="/my/ca.crt"
|
||||
--providers.kubernetesingress.certauthfilepath=/my/ca.crt
|
||||
```
|
||||
|
||||
Path to the certificate authority file.
|
||||
|
@ -171,7 +171,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.kubernetesingress.namespaces="default,production"
|
||||
--providers.kubernetesingress.namespaces=default,production
|
||||
```
|
||||
|
||||
Array of namespaces to watch.
|
||||
|
@ -220,7 +220,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.kubernetesingress.ingressclass="traefik-internal"
|
||||
--providers.kubernetesingress.ingressclass=traefik-internal
|
||||
```
|
||||
|
||||
Value of `kubernetes.io/ingress.class` annotation that identifies Ingress objects to be processed.
|
||||
|
@ -249,7 +249,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.kubernetesingress.ingressendpoint.hostname="foo.com"
|
||||
--providers.kubernetesingress.ingressendpoint.hostname=foo.com
|
||||
```
|
||||
|
||||
Hostname used for Kubernetes Ingress endpoints.
|
||||
|
@ -273,7 +273,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.kubernetesingress.ingressendpoint.ip="1.2.3.4"
|
||||
--providers.kubernetesingress.ingressendpoint.ip=1.2.3.4
|
||||
```
|
||||
|
||||
IP used for Kubernetes Ingress endpoints.
|
||||
|
@ -297,7 +297,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.kubernetesingress.ingressendpoint.publishedservice="foo-service"
|
||||
--providers.kubernetesingress.ingressendpoint.publishedservice=foo-service
|
||||
```
|
||||
|
||||
Published Kubernetes Service to copy status from.
|
||||
|
@ -320,7 +320,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.kubernetesingress.throttleDuration="10s"
|
||||
--providers.kubernetesingress.throttleDuration=10s
|
||||
```
|
||||
|
||||
## Further
|
||||
|
|
|
@ -74,8 +74,8 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.marathon.basic.httpbasicauthuser="foo"
|
||||
--providers.marathon.basic.httpbasicpassword="bar"
|
||||
--providers.marathon.basic.httpbasicauthuser=foo
|
||||
--providers.marathon.basic.httpbasicpassword=bar
|
||||
```
|
||||
|
||||
Enables Marathon basic authentication.
|
||||
|
@ -98,7 +98,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.marathon.dcosToken="xxxxxx"
|
||||
--providers.marathon.dcosToken=xxxxxx
|
||||
```
|
||||
|
||||
DCOSToken for DCOS environment.
|
||||
|
@ -123,7 +123,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.marathon.defaultRule="Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
||||
--providers.marathon.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@ -182,7 +182,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.marathon.endpoint="http://10.241.1.71:8080,10.241.1.72:8080,10.241.1.73:8080"
|
||||
--providers.marathon.endpoint=http://10.241.1.71:8080,10.241.1.72:8080,10.241.1.73:8080
|
||||
```
|
||||
|
||||
Marathon server endpoint.
|
||||
|
@ -223,19 +223,19 @@ _Optional, Default=""_
|
|||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.marathon]
|
||||
constraints = "Label(`a.label.name`, `foo`)"
|
||||
constraints = "Label(`a.label.name`,`foo`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
marathon:
|
||||
constraints: "Label(`a.label.name`, `foo`)"
|
||||
constraints: "Label(`a.label.name`,`foo`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.marathon.constraints="Label(`a.label.name`, `foo`)"
|
||||
--providers.marathon.constraints=Label(`a.label.name`,`foo`)
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@ -389,7 +389,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.marathon.responseHeaderTimeout="66s"
|
||||
--providers.marathon.responseHeaderTimeout=66s
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@ -532,7 +532,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.marathon.responseHeaderTimeout="10s"
|
||||
--providers.marathon.responseHeaderTimeout=10s
|
||||
# ...
|
||||
```
|
||||
|
||||
|
|
|
@ -104,7 +104,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.rancher.defaultRule="Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
||||
--providers.rancher.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@ -209,7 +209,7 @@ providers:
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.rancher.prefix="/test"
|
||||
--providers.rancher.prefix=/test
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@ -221,19 +221,19 @@ _Optional, Default=""_
|
|||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.rancher]
|
||||
constraints = "Label(`a.label.name`, `foo`)"
|
||||
constraints = "Label(`a.label.name`,`foo`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
rancher:
|
||||
constraints: "Label(`a.label.name`, `foo`)"
|
||||
constraints: "Label(`a.label.name`,`foo`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.rancher.constraints="Label(`a.label.name`, `foo`)"
|
||||
--providers.rancher.constraints=Label(`a.label.name`,`foo`)
|
||||
# ...
|
||||
```
|
||||
|
||||
|
|
|
@ -17,4 +17,4 @@
|
|||
--providers.rancher.intervalPoll=false
|
||||
|
||||
# Prefix used for accessing the Rancher metadata service
|
||||
--providers.rancher.prefix="/latest"
|
||||
--providers.rancher.prefix=/latest
|
||||
|
|
|
@ -18,4 +18,4 @@ providers:
|
|||
intervalPoll: false
|
||||
|
||||
# Prefix used for accessing the Rancher metadata service
|
||||
prefix: "/latest"
|
||||
prefix: /latest
|
||||
|
|
|
@ -128,9 +128,9 @@ You can define them using a toml file, CLI arguments, or a key-value store.
|
|||
--entryPoints.name.transport.respondingTimeouts.writeTimeout=42
|
||||
--entryPoints.name.transport.respondingTimeouts.idleTimeout=42
|
||||
--entryPoints.name.proxyProtocol.insecure=true
|
||||
--entryPoints.name.proxyProtocol.trustedIPs="127.0.0.1,192.168.0.1"
|
||||
--entryPoints.name.proxyProtocol.trustedIPs=127.0.0.1,192.168.0.1
|
||||
--entryPoints.name.forwardedHeaders.insecure=true
|
||||
--entryPoints.name.forwardedHeaders.trustedIPs="127.0.0.1,192.168.0.1"
|
||||
--entryPoints.name.forwardedHeaders.trustedIPs=127.0.0.1,192.168.0.1
|
||||
```
|
||||
|
||||
### Forwarded Header
|
||||
|
|
|
@ -151,7 +151,7 @@ http:
|
|||
|
||||
```bash tab="CLI"
|
||||
# Listen on port 8081 for incoming requests
|
||||
--entryPoints.web.address=":8081"
|
||||
--entryPoints.web.address=:8081
|
||||
|
||||
# Enable the file provider to define routers / middlewares / services in a file
|
||||
--providers.file.filename=dynamic_conf.toml
|
||||
|
|
|
@ -82,7 +82,7 @@ Attach labels to your containers and let Traefik do the rest!
|
|||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.docker.endpoint="tcp://127.0.0.1:2375"
|
||||
--providers.docker.endpoint=tcp://127.0.0.1:2375
|
||||
--providers.docker.swarmMode=true
|
||||
```
|
||||
|
||||
|
@ -165,7 +165,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
|
|||
See [entry points](../routers/index.md#entrypoints) for more information.
|
||||
|
||||
```yaml
|
||||
- "traefik.http.routers.myrouter.entrypoints=web,websecure"
|
||||
- "traefik.http.routers.myrouter.entrypoints=ep1,ep2"
|
||||
```
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.middlewares`"
|
||||
|
@ -247,7 +247,8 @@ you'd add the label `traefik.http.services.<name-of-your-choice>.loadbalancer.pa
|
|||
Registers a port.
|
||||
Useful when the container exposes multiples ports.
|
||||
|
||||
Mandatory for Docker Swarm.
|
||||
Mandatory for Docker Swarm (see the section ["Port Detection with Docker Swarm"](../../providers/docker.md#port-detection_1)).
|
||||
{: #port }
|
||||
|
||||
```yaml
|
||||
- "traefik.http.services.myservice.loadbalancer.server.port=8080"
|
||||
|
|
|
@ -67,7 +67,7 @@ For example, to change the routing rule, you could add the label ```"traefik.htt
|
|||
See [entry points](../routers/index.md#entrypoints) for more information.
|
||||
|
||||
```json
|
||||
"traefik.http.routers.myrouter.entrypoints": "web,websecure"
|
||||
"traefik.http.routers.myrouter.entrypoints": "ep1,ep2"
|
||||
```
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.middlewares`"
|
||||
|
|
|
@ -72,7 +72,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
|
|||
See [entry points](../routers/index.md#entrypoints) for more information.
|
||||
|
||||
```yaml
|
||||
- "traefik.http.routers.myrouter.entrypoints=web,websecure"
|
||||
- "traefik.http.routers.myrouter.entrypoints=ep1,ep2"
|
||||
```
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.middlewares`"
|
||||
|
|
|
@ -78,8 +78,8 @@ In the process, routers may use pieces of [middleware](../../middlewares/overvie
|
|||
|
||||
```bash tab="CLI"
|
||||
## Static configuration
|
||||
--entryPoints.web.address=":80"
|
||||
--entryPoints.mysql.address=":3306"
|
||||
--entryPoints.web.address=:80
|
||||
--entryPoints.mysql.address=:3306
|
||||
```
|
||||
|
||||
## Configuring HTTP Routers
|
||||
|
@ -140,9 +140,9 @@ If you want to limit the router scope to a set of entry points, set the `entryPo
|
|||
|
||||
```bash tab="CLI"
|
||||
## Static configuration
|
||||
--entrypoints.web.address=":80"
|
||||
--entrypoints.websecure.address=":443"
|
||||
--entrypoints.other.address=":9090"
|
||||
--entrypoints.web.address=:80
|
||||
--entrypoints.websecure.address=:443
|
||||
--entrypoints.other.address=:9090
|
||||
```
|
||||
|
||||
??? example "Listens to Specific EntryPoints"
|
||||
|
@ -198,9 +198,9 @@ If you want to limit the router scope to a set of entry points, set the `entryPo
|
|||
|
||||
```bash tab="CLI"
|
||||
## Static configuration
|
||||
--entrypoints.web.address=":80"
|
||||
--entrypoints.websecure.address=":443"
|
||||
--entrypoints.other.address=":9090"
|
||||
--entrypoints.web.address=:80
|
||||
--entrypoints.websecure.address=:443
|
||||
--entrypoints.other.address=:9090
|
||||
```
|
||||
|
||||
### Rule
|
||||
|
@ -700,9 +700,9 @@ If you want to limit the router scope to a set of entry points, set the entry po
|
|||
|
||||
```bash tab="CLI"
|
||||
## Static configuration
|
||||
--entrypoints.web.address=":80"
|
||||
--entrypoints.websecure.address=":443"
|
||||
--entrypoints.other.address=":9090"
|
||||
--entrypoints.web.address=:80
|
||||
--entrypoints.websecure.address=:443
|
||||
--entrypoints.other.address=:9090
|
||||
```
|
||||
|
||||
??? example "Listens to Specific Entry Points"
|
||||
|
@ -764,9 +764,9 @@ If you want to limit the router scope to a set of entry points, set the entry po
|
|||
|
||||
```bash tab="CLI"
|
||||
## Static configuration
|
||||
--entrypoints.web.address=":80"
|
||||
--entrypoints.websecure.address=":443"
|
||||
--entrypoints.other.address=":9090"
|
||||
--entrypoints.web.address=:80
|
||||
--entrypoints.websecure.address=:443
|
||||
--entrypoints.other.address=:9090
|
||||
```
|
||||
|
||||
### Rule
|
||||
|
|
|
@ -32,7 +32,7 @@ api: {}
|
|||
```
|
||||
|
||||
```yaml tab="CLI"
|
||||
--entryPoints.web.address=":80"
|
||||
--entryPoints.web.address=:80
|
||||
--providers.file.filename=dynamic_conf.toml
|
||||
--api.insecure=true
|
||||
```
|
||||
|
@ -153,7 +153,7 @@ api: {}
|
|||
```
|
||||
|
||||
```yaml tab="CLI"
|
||||
--entryPoints.websecure.address=":4443"
|
||||
--entryPoints.websecure.address=:4443
|
||||
# For secure connection on backend.local
|
||||
--serversTransport.rootCAs=./backend.cert
|
||||
--providers.file.filename=dynamic_conf.toml
|
||||
|
|
2
go.mod
2
go.mod
|
@ -67,7 +67,7 @@ require (
|
|||
github.com/opencontainers/runc v1.0.0-rc8 // indirect
|
||||
github.com/opentracing/basictracer-go v1.0.0 // indirect
|
||||
github.com/opentracing/opentracing-go v1.1.0
|
||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.4
|
||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5
|
||||
github.com/openzipkin/zipkin-go v0.2.1
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/philhofer/fwd v1.0.0 // indirect
|
||||
|
|
4
go.sum
4
go.sum
|
@ -454,8 +454,8 @@ github.com/opentracing/basictracer-go v1.0.0 h1:YyUAhaEfjoWXclZVJ9sGoNct7j4TVk7l
|
|||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.4 h1:bzTJRoOZEN7uI1gq594S5HhMYNSud4FKUEwd4aFbsEI=
|
||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.4/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
|
||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 h1:ZCnq+JUrvXcDVhX/xRolRBZifmabN1HcS1wrPSvxhrU=
|
||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/openzipkin/zipkin-go v0.2.1 h1:noL5/5Uf1HpVl3wNsfkZhIKbSWCVi5jgqkONNx8PXcA=
|
||||
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
[global]
|
||||
checkNewVersion = false
|
||||
sendAnonymousUsage = false
|
||||
|
||||
[log]
|
||||
level = "DEBUG"
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.web1]
|
||||
address = ":8000"
|
||||
[entryPoints.web2]
|
||||
address = ":9000"
|
||||
|
||||
[api]
|
||||
insecure = true
|
||||
|
||||
[providers.file]
|
||||
filename = "{{ .SelfFilename }}"
|
||||
|
||||
## dynamic configuration ##
|
||||
|
||||
[http.routers]
|
||||
[http.routers.router1]
|
||||
entryPoints = ["web1"]
|
||||
service = "service1"
|
||||
rule = "Host(`test.localhost`)"
|
||||
|
||||
[http.routers.router2]
|
||||
entryPoints = ["web2"]
|
||||
service = "service1"
|
||||
rule = "Host(`test.localhost`)"
|
||||
|
||||
[http.services]
|
||||
[http.services.service1.loadBalancer]
|
||||
[http.services.service1.loadBalancer.healthcheck]
|
||||
path = "/health"
|
||||
interval = "1s"
|
||||
timeout = "0.9s"
|
||||
[[http.services.service1.loadBalancer.servers]]
|
||||
url = "http://{{.Server1}}:80"
|
|
@ -205,3 +205,69 @@ func (s *HealthCheckSuite) TestPortOverload(c *check.C) {
|
|||
err = try.Request(frontendHealthReq, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// Checks if all the loadbalancers created will correctly update the server status
|
||||
func (s *HealthCheckSuite) TestMultipleRoutersOnSameService(c *check.C) {
|
||||
file := s.adaptFile(c, "fixtures/healthcheck/multiple-routers-one-same-service.toml", struct {
|
||||
Server1 string
|
||||
}{s.whoami1IP})
|
||||
defer os.Remove(file)
|
||||
|
||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||
defer display(c)
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`test.localhost`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Set whoami health to 200 to be sure to start with the wanted status
|
||||
client := &http.Client{}
|
||||
statusOkReq, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBuffer([]byte("200")))
|
||||
c.Assert(err, checker.IsNil)
|
||||
_, err = client.Do(statusOkReq)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// check healthcheck on web1 entrypoint
|
||||
healthReqWeb1, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/health", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
healthReqWeb1.Host = "test.localhost"
|
||||
err = try.Request(healthReqWeb1, 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// check healthcheck on web2 entrypoint
|
||||
healthReqWeb2, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:9000/health", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
healthReqWeb2.Host = "test.localhost"
|
||||
|
||||
err = try.Request(healthReqWeb2, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Set whoami health to 500
|
||||
statusInternalServerErrorReq, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBuffer([]byte("500")))
|
||||
c.Assert(err, checker.IsNil)
|
||||
_, err = client.Do(statusInternalServerErrorReq)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Verify no backend service is available due to failing health checks
|
||||
err = try.Request(healthReqWeb1, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.Request(healthReqWeb2, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Change one whoami health to 200
|
||||
statusOKReq1, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBuffer([]byte("200")))
|
||||
c.Assert(err, checker.IsNil)
|
||||
_, err = client.Do(statusOKReq1)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Verify health check
|
||||
err = try.Request(healthReqWeb1, 3*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.Request(healthReqWeb2, 3*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
|
|
@ -25,14 +25,20 @@ const (
|
|||
var singleton *HealthCheck
|
||||
var once sync.Once
|
||||
|
||||
// BalancerHandler includes functionality for load-balancing management.
|
||||
type BalancerHandler interface {
|
||||
ServeHTTP(w http.ResponseWriter, req *http.Request)
|
||||
// Balancer is the set of operations required to manage the list of servers in a
|
||||
// load-balancer.
|
||||
type Balancer interface {
|
||||
Servers() []*url.URL
|
||||
RemoveServer(u *url.URL) error
|
||||
UpsertServer(u *url.URL, options ...roundrobin.ServerOption) error
|
||||
}
|
||||
|
||||
// BalancerHandler includes functionality for load-balancing management.
|
||||
type BalancerHandler interface {
|
||||
ServeHTTP(w http.ResponseWriter, req *http.Request)
|
||||
Balancer
|
||||
}
|
||||
|
||||
// metricsRegistry is a local interface in the health check package, exposing only the required metrics
|
||||
// necessary for the health check package. This makes it easier for the tests.
|
||||
type metricsRegistry interface {
|
||||
|
@ -49,7 +55,7 @@ type Options struct {
|
|||
Transport http.RoundTripper
|
||||
Interval time.Duration
|
||||
Timeout time.Duration
|
||||
LB BalancerHandler
|
||||
LB Balancer
|
||||
}
|
||||
|
||||
func (opt Options) String() string {
|
||||
|
@ -146,18 +152,18 @@ func (hc *HealthCheck) checkBackend(ctx context.Context, backend *BackendConfig)
|
|||
enabledURLs := backend.LB.Servers()
|
||||
var newDisabledURLs []backendURL
|
||||
// FIXME re enable metrics
|
||||
for _, disableURL := range backend.disabledURLs {
|
||||
for _, disabledURL := range backend.disabledURLs {
|
||||
// FIXME serverUpMetricValue := float64(0)
|
||||
if err := checkHealth(disableURL.url, backend); err == nil {
|
||||
if err := checkHealth(disabledURL.url, backend); err == nil {
|
||||
logger.Warnf("Health check up: Returning to server list. Backend: %q URL: %q Weight: %d",
|
||||
backend.name, disableURL.url.String(), disableURL.weight)
|
||||
if err = backend.LB.UpsertServer(disableURL.url, roundrobin.Weight(disableURL.weight)); err != nil {
|
||||
backend.name, disabledURL.url.String(), disabledURL.weight)
|
||||
if err = backend.LB.UpsertServer(disabledURL.url, roundrobin.Weight(disabledURL.weight)); err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
// FIXME serverUpMetricValue = 1
|
||||
} else {
|
||||
logger.Warnf("Health check still failing. Backend: %q URL: %q Reason: %s", backend.name, disableURL.url.String(), err)
|
||||
newDisabledURLs = append(newDisabledURLs, disableURL)
|
||||
logger.Warnf("Health check still failing. Backend: %q URL: %q Reason: %s", backend.name, disabledURL.url.String(), err)
|
||||
newDisabledURLs = append(newDisabledURLs, disabledURL)
|
||||
}
|
||||
// FIXME labelValues := []string{"backend", backend.name, "url", backendurl.url.String()}
|
||||
// FIXME hc.metrics.BackendServerUpGauge().With(labelValues...).Set(serverUpMetricValue)
|
||||
|
@ -177,7 +183,7 @@ func (hc *HealthCheck) checkBackend(ctx context.Context, backend *BackendConfig)
|
|||
weight = 1
|
||||
}
|
||||
}
|
||||
logger.Warnf("Health check failed: Remove from server list. Backend: %q URL: %q Weight: %d Reason: %s", backend.name, enableURL.String(), weight, err)
|
||||
logger.Warnf("Health check failed, removing from server list. Backend: %q URL: %q Weight: %d Reason: %s", backend.name, enableURL.String(), weight, err)
|
||||
if err := backend.LB.RemoveServer(enableURL); err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
|
@ -281,3 +287,38 @@ func (lb *LbStatusUpdater) UpsertServer(u *url.URL, options ...roundrobin.Server
|
|||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Balancers is a list of Balancers(s) that implements the Balancer interface.
|
||||
type Balancers []Balancer
|
||||
|
||||
// Servers returns the servers url from all the BalancerHandler
|
||||
func (b Balancers) Servers() []*url.URL {
|
||||
var servers []*url.URL
|
||||
for _, lb := range b {
|
||||
servers = append(servers, lb.Servers()...)
|
||||
}
|
||||
|
||||
return servers
|
||||
}
|
||||
|
||||
// RemoveServer removes the given server from all the BalancerHandler,
|
||||
// and updates the status of the server to "DOWN".
|
||||
func (b Balancers) RemoveServer(u *url.URL) error {
|
||||
for _, lb := range b {
|
||||
if err := lb.RemoveServer(u); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpsertServer adds the given server to all the BalancerHandler,
|
||||
// and updates the status of the server to "UP".
|
||||
func (b Balancers) UpsertServer(u *url.URL, options ...roundrobin.ServerOption) error {
|
||||
for _, lb := range b {
|
||||
if err := lb.UpsertServer(u, options...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -116,7 +116,18 @@ type CoreLogData map[string]interface{}
|
|||
// LogData is the data captured by the middleware so that it can be logged.
|
||||
type LogData struct {
|
||||
Core CoreLogData
|
||||
Request http.Header
|
||||
Request request
|
||||
OriginResponse http.Header
|
||||
DownstreamResponse http.Header
|
||||
DownstreamResponse downstreamResponse
|
||||
}
|
||||
|
||||
type downstreamResponse struct {
|
||||
headers http.Header
|
||||
status int
|
||||
size int64
|
||||
}
|
||||
|
||||
type request struct {
|
||||
headers http.Header
|
||||
count int64
|
||||
}
|
||||
|
|
|
@ -47,8 +47,6 @@ func (n noopCloser) Close() error {
|
|||
|
||||
type handlerParams struct {
|
||||
logDataTable *LogData
|
||||
crr *captureRequestReader
|
||||
crw *captureResponseWriter
|
||||
}
|
||||
|
||||
// Handler will write each request and its response to the access log.
|
||||
|
@ -122,7 +120,7 @@ func NewHandler(config *types.AccessLog) (*Handler, error) {
|
|||
go func() {
|
||||
defer logHandler.wg.Done()
|
||||
for handlerParams := range logHandler.logHandlerChan {
|
||||
logHandler.logTheRoundTrip(handlerParams.logDataTable, handlerParams.crr, handlerParams.crw)
|
||||
logHandler.logTheRoundTrip(handlerParams.logDataTable)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
@ -162,7 +160,12 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http
|
|||
StartLocal: now.Local(),
|
||||
}
|
||||
|
||||
logDataTable := &LogData{Core: core, Request: req.Header}
|
||||
logDataTable := &LogData{
|
||||
Core: core,
|
||||
Request: request{
|
||||
headers: req.Header,
|
||||
},
|
||||
}
|
||||
|
||||
reqWithDataTable := req.WithContext(context.WithValue(req.Context(), DataTableKey, logDataTable))
|
||||
|
||||
|
@ -205,16 +208,21 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http
|
|||
core[ClientUsername] = usernameIfPresent(reqWithDataTable.URL)
|
||||
}
|
||||
|
||||
logDataTable.DownstreamResponse = crw.Header()
|
||||
logDataTable.DownstreamResponse = downstreamResponse{
|
||||
headers: crw.Header().Clone(),
|
||||
status: crw.Status(),
|
||||
size: crw.Size(),
|
||||
}
|
||||
if crr != nil {
|
||||
logDataTable.Request.count = crr.count
|
||||
}
|
||||
|
||||
if h.config.BufferingSize > 0 {
|
||||
h.logHandlerChan <- handlerParams{
|
||||
logDataTable: logDataTable,
|
||||
crr: crr,
|
||||
crw: crw,
|
||||
}
|
||||
} else {
|
||||
h.logTheRoundTrip(logDataTable, crr, crw)
|
||||
h.logTheRoundTrip(logDataTable)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,7 +272,7 @@ func usernameIfPresent(theURL *url.URL) string {
|
|||
}
|
||||
|
||||
// Logging handler to log frontend name, backend name, and elapsed time.
|
||||
func (h *Handler) logTheRoundTrip(logDataTable *LogData, crr *captureRequestReader, crw *captureResponseWriter) {
|
||||
func (h *Handler) logTheRoundTrip(logDataTable *LogData) {
|
||||
core := logDataTable.Core
|
||||
|
||||
retryAttempts, ok := core[RetryAttempts].(int)
|
||||
|
@ -272,23 +280,22 @@ func (h *Handler) logTheRoundTrip(logDataTable *LogData, crr *captureRequestRead
|
|||
retryAttempts = 0
|
||||
}
|
||||
core[RetryAttempts] = retryAttempts
|
||||
core[RequestContentSize] = logDataTable.Request.count
|
||||
|
||||
if crr != nil {
|
||||
core[RequestContentSize] = crr.count
|
||||
}
|
||||
|
||||
core[DownstreamStatus] = crw.Status()
|
||||
status := logDataTable.DownstreamResponse.status
|
||||
core[DownstreamStatus] = status
|
||||
|
||||
// n.b. take care to perform time arithmetic using UTC to avoid errors at DST boundaries.
|
||||
totalDuration := time.Now().UTC().Sub(core[StartUTC].(time.Time))
|
||||
core[Duration] = totalDuration
|
||||
|
||||
if h.keepAccessLog(crw.Status(), retryAttempts, totalDuration) {
|
||||
core[DownstreamContentSize] = crw.Size()
|
||||
if h.keepAccessLog(status, retryAttempts, totalDuration) {
|
||||
size := logDataTable.DownstreamResponse.size
|
||||
core[DownstreamContentSize] = size
|
||||
if original, ok := core[OriginContentSize]; ok {
|
||||
o64 := original.(int64)
|
||||
if crw.Size() != o64 && crw.Size() != 0 {
|
||||
core[GzipRatio] = float64(o64) / float64(crw.Size())
|
||||
if size != o64 && size != 0 {
|
||||
core[GzipRatio] = float64(o64) / float64(size)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -305,9 +312,9 @@ func (h *Handler) logTheRoundTrip(logDataTable *LogData, crr *captureRequestRead
|
|||
}
|
||||
}
|
||||
|
||||
h.redactHeaders(logDataTable.Request, fields, "request_")
|
||||
h.redactHeaders(logDataTable.Request.headers, fields, "request_")
|
||||
h.redactHeaders(logDataTable.OriginResponse, fields, "origin_")
|
||||
h.redactHeaders(logDataTable.DownstreamResponse, fields, "downstream_")
|
||||
h.redactHeaders(logDataTable.DownstreamResponse.headers, fields, "downstream_")
|
||||
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
|
|
@ -192,6 +192,7 @@ func TestLoggerJSON(t *testing.T) {
|
|||
Format: JSONFormat,
|
||||
},
|
||||
expected: map[string]func(t *testing.T, value interface{}){
|
||||
RequestContentSize: assertFloat64(0),
|
||||
RequestHost: assertString(testHostname),
|
||||
RequestAddr: assertString(testHostname),
|
||||
RequestMethod: assertString(testMethod),
|
||||
|
|
|
@ -221,13 +221,11 @@ func (s *Header) processCorsHeaders(rw http.ResponseWriter, req *http.Request) b
|
|||
}
|
||||
|
||||
reqAcMethod := req.Header.Get("Access-Control-Request-Method")
|
||||
reqAcHeaders := req.Header.Get("Access-Control-Request-Headers")
|
||||
originHeader := req.Header.Get("Origin")
|
||||
|
||||
if reqAcMethod != "" && reqAcHeaders != "" && originHeader != "" && req.Method == http.MethodOptions {
|
||||
if reqAcMethod != "" && originHeader != "" && req.Method == http.MethodOptions {
|
||||
// If the request is an OPTIONS request with an Access-Control-Request-Method header,
|
||||
// and Access-Control-Request-Headers headers, and Origin headers,
|
||||
// then it is a CORS preflight request,
|
||||
// and Origin headers, then it is a CORS preflight request,
|
||||
// and we need to build a custom response: https://www.w3.org/TR/cors/#preflight-request
|
||||
if s.headers.AccessControlAllowCredentials {
|
||||
rw.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
|
|
|
@ -275,6 +275,25 @@ func TestCORSPreflights(t *testing.T) {
|
|||
"Access-Control-Allow-Headers": {"origin,X-Forwarded-For"},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "No Request Headers Preflight",
|
||||
header: NewHeader(emptyHandler, dynamic.Headers{
|
||||
AccessControlAllowMethods: []string{"GET", "OPTIONS", "PUT"},
|
||||
AccessControlAllowOrigin: "*",
|
||||
AccessControlAllowHeaders: []string{"origin", "X-Forwarded-For"},
|
||||
AccessControlMaxAge: 600,
|
||||
}),
|
||||
requestHeaders: map[string][]string{
|
||||
"Access-Control-Request-Method": {"GET", "OPTIONS"},
|
||||
"Origin": {"https://foo.bar.org"},
|
||||
},
|
||||
expected: map[string][]string{
|
||||
"Access-Control-Allow-Origin": {"*"},
|
||||
"Access-Control-Max-Age": {"600"},
|
||||
"Access-Control-Allow-Methods": {"GET,OPTIONS,PUT"},
|
||||
"Access-Control-Allow-Headers": {"origin,X-Forwarded-For"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
|
|
|
@ -34,7 +34,11 @@ type entryPointMiddleware struct {
|
|||
}
|
||||
|
||||
func (e *entryPointMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
spanCtx, _ := e.Extract(opentracing.HTTPHeaders, tracing.HTTPHeadersCarrier(req.Header))
|
||||
spanCtx, err := e.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
|
||||
if err != nil {
|
||||
log.FromContext(middlewares.GetLoggerCtx(req.Context(), "tracing", entryPointTypeName)).
|
||||
Debugf("Failed to extract the context: %v", err)
|
||||
}
|
||||
|
||||
span, req, finish := e.StartSpanf(req, ext.SpanKindRPCServerEnum, "EntryPoint", []string{e.entryPoint, req.Host}, " ", ext.RPCServerOption(spanCtx))
|
||||
defer finish()
|
||||
|
|
|
@ -193,7 +193,7 @@ func (p *Provider) parseMetadataSourcedRancherData(ctx context.Context, stacks [
|
|||
}
|
||||
|
||||
service := rancherData{
|
||||
Name: service.Name + "/" + stack.Name,
|
||||
Name: service.Name + "_" + stack.Name,
|
||||
State: service.State,
|
||||
Labels: service.Labels,
|
||||
Port: servicePort,
|
||||
|
|
|
@ -40,7 +40,7 @@ func NewManager(configs map[string]*runtime.ServiceInfo, defaultRoundTripper htt
|
|||
metricsRegistry: metricsRegistry,
|
||||
bufferPool: newBufferPool(),
|
||||
defaultRoundTripper: defaultRoundTripper,
|
||||
balancers: make(map[string][]healthcheck.BalancerHandler),
|
||||
balancers: make(map[string]healthcheck.Balancers),
|
||||
configs: configs,
|
||||
}
|
||||
}
|
||||
|
@ -51,8 +51,12 @@ type Manager struct {
|
|||
metricsRegistry metrics.Registry
|
||||
bufferPool httputil.BufferPool
|
||||
defaultRoundTripper http.RoundTripper
|
||||
balancers map[string][]healthcheck.BalancerHandler
|
||||
configs map[string]*runtime.ServiceInfo
|
||||
// balancers is the map of all Balancers, keyed by service name.
|
||||
// There is one Balancer per service handler, and there is one service handler per reference to a service
|
||||
// (e.g. if 2 routers refer to the same service name, 2 service handlers are created),
|
||||
// which is why there is not just one Balancer per service name.
|
||||
balancers map[string]healthcheck.Balancers
|
||||
configs map[string]*runtime.ServiceInfo
|
||||
}
|
||||
|
||||
// BuildHTTP Creates a http.Handler for a service configuration.
|
||||
|
@ -92,14 +96,14 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string, respons
|
|||
}
|
||||
case conf.Weighted != nil:
|
||||
var err error
|
||||
lb, err = m.getLoadBalancerWRRServiceHandler(ctx, serviceName, conf.Weighted, responseModifier)
|
||||
lb, err = m.getWRRServiceHandler(ctx, serviceName, conf.Weighted, responseModifier)
|
||||
if err != nil {
|
||||
conf.AddError(err, true)
|
||||
return nil, err
|
||||
}
|
||||
case conf.Mirroring != nil:
|
||||
var err error
|
||||
lb, err = m.getLoadBalancerMirrorServiceHandler(ctx, serviceName, conf.Mirroring, responseModifier)
|
||||
lb, err = m.getMirrorServiceHandler(ctx, conf.Mirroring, responseModifier)
|
||||
if err != nil {
|
||||
conf.AddError(err, true)
|
||||
return nil, err
|
||||
|
@ -113,7 +117,7 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string, respons
|
|||
return lb, nil
|
||||
}
|
||||
|
||||
func (m *Manager) getLoadBalancerMirrorServiceHandler(ctx context.Context, serviceName string, config *dynamic.Mirroring, responseModifier func(*http.Response) error) (http.Handler, error) {
|
||||
func (m *Manager) getMirrorServiceHandler(ctx context.Context, config *dynamic.Mirroring, responseModifier func(*http.Response) error) (http.Handler, error) {
|
||||
serviceHandler, err := m.BuildHTTP(ctx, config.Service, responseModifier)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -134,7 +138,7 @@ func (m *Manager) getLoadBalancerMirrorServiceHandler(ctx context.Context, servi
|
|||
return handler, nil
|
||||
}
|
||||
|
||||
func (m *Manager) getLoadBalancerWRRServiceHandler(ctx context.Context, serviceName string, config *dynamic.WeightedRoundRobin, responseModifier func(*http.Response) error) (http.Handler, error) {
|
||||
func (m *Manager) getWRRServiceHandler(ctx context.Context, serviceName string, config *dynamic.WeightedRoundRobin, responseModifier func(*http.Response) error) (http.Handler, error) {
|
||||
// TODO Handle accesslog and metrics with multiple service name
|
||||
if config.Sticky != nil && config.Sticky.Cookie != nil {
|
||||
config.Sticky.Cookie.Name = cookie.GetName(config.Sticky.Cookie.Name, serviceName)
|
||||
|
@ -200,15 +204,12 @@ func (m *Manager) LaunchHealthCheck() {
|
|||
for serviceName, balancers := range m.balancers {
|
||||
ctx := log.With(context.Background(), log.Str(log.ServiceName, serviceName))
|
||||
|
||||
// TODO aggregate
|
||||
balancer := balancers[0]
|
||||
|
||||
// TODO Should all the services handle healthcheck? Handle different types
|
||||
service := m.configs[serviceName].LoadBalancer
|
||||
|
||||
// Health Check
|
||||
var backendHealthCheck *healthcheck.BackendConfig
|
||||
if hcOpts := buildHealthCheckOptions(ctx, balancer, serviceName, service.HealthCheck); hcOpts != nil {
|
||||
if hcOpts := buildHealthCheckOptions(ctx, balancers, serviceName, service.HealthCheck); hcOpts != nil {
|
||||
log.FromContext(ctx).Debugf("Setting up healthcheck for service %s with %s", serviceName, *hcOpts)
|
||||
|
||||
hcOpts.Transport = m.defaultRoundTripper
|
||||
|
@ -224,7 +225,7 @@ func (m *Manager) LaunchHealthCheck() {
|
|||
healthcheck.GetHealthCheck().SetBackendsConfiguration(context.Background(), backendConfigs)
|
||||
}
|
||||
|
||||
func buildHealthCheckOptions(ctx context.Context, lb healthcheck.BalancerHandler, backend string, hc *dynamic.HealthCheck) *healthcheck.Options {
|
||||
func buildHealthCheckOptions(ctx context.Context, lb healthcheck.Balancer, backend string, hc *dynamic.HealthCheck) *healthcheck.Options {
|
||||
if hc == nil || hc.Path == "" {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
package tracing
|
||||
|
||||
import "net/http"
|
||||
|
||||
// HTTPHeadersCarrier custom implementation to fix duplicated headers
|
||||
// It has been fixed in https://github.com/opentracing/opentracing-go/pull/191
|
||||
type HTTPHeadersCarrier http.Header
|
||||
|
||||
// Set conforms to the TextMapWriter interface.
|
||||
func (c HTTPHeadersCarrier) Set(key, val string) {
|
||||
h := http.Header(c)
|
||||
h.Set(key, val)
|
||||
}
|
||||
|
||||
// ForeachKey conforms to the TextMapReader interface.
|
||||
func (c HTTPHeadersCarrier) ForeachKey(handler func(key, val string) error) error {
|
||||
for k, vals := range c {
|
||||
for _, v := range vals {
|
||||
if err := handler(k, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -134,7 +134,7 @@ func InjectRequestHeaders(r *http.Request) {
|
|||
err := opentracing.GlobalTracer().Inject(
|
||||
span.Context(),
|
||||
opentracing.HTTPHeaders,
|
||||
HTTPHeadersCarrier(r.Header))
|
||||
opentracing.HTTPHeadersCarrier(r.Header))
|
||||
if err != nil {
|
||||
log.FromContext(r.Context()).Error(err)
|
||||
}
|
||||
|
|
|
@ -3,15 +3,12 @@ import { APP } from '../_helpers/APP'
|
|||
const apiBase = '/http'
|
||||
|
||||
function getAllRouters (params) {
|
||||
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}`)
|
||||
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
||||
.then(body => {
|
||||
const total = body.data ? body.data.length : 0
|
||||
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
||||
.then(body => {
|
||||
console.log('Success -> HttpService -> getAllRouters', body.data)
|
||||
// TODO - suggestion: add the total-pages in api response to optimize the query
|
||||
return { data: body.data || [], total }
|
||||
})
|
||||
console.log('Success -> HttpService -> getAllRouters', body.data)
|
||||
// TODO - suggestion: add the total-pages in api response to optimize the query
|
||||
return { data: body.data || [], total }
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -24,15 +21,12 @@ function getRouterByName (name) {
|
|||
}
|
||||
|
||||
function getAllServices (params) {
|
||||
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}`)
|
||||
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
||||
.then(body => {
|
||||
const total = body.data ? body.data.length : 0
|
||||
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
||||
.then(body => {
|
||||
console.log('Success -> HttpService -> getAllServices', body.data)
|
||||
// TODO - suggestion: add the total-pages in api response to optimize the query
|
||||
return { data: body.data || [], total }
|
||||
})
|
||||
console.log('Success -> HttpService -> getAllServices', body.data)
|
||||
// TODO - suggestion: add the total-pages in api response to optimize the query
|
||||
return { data: body.data || [], total }
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -45,15 +39,12 @@ function getServiceByName (name) {
|
|||
}
|
||||
|
||||
function getAllMiddlewares (params) {
|
||||
return APP.api.get(`${apiBase}/middlewares?search=${params.query}&status=${params.status}`)
|
||||
return APP.api.get(`${apiBase}/middlewares?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
||||
.then(body => {
|
||||
const total = body.data ? body.data.length : 0
|
||||
return APP.api.get(`${apiBase}/middlewares?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
||||
.then(body => {
|
||||
console.log('Success -> HttpService -> getAllMiddlewares', body.data)
|
||||
// TODO - suggestion: add the total-pages in api response to optimize the query
|
||||
return { data: body.data || [], total }
|
||||
})
|
||||
console.log('Success -> HttpService -> getAllMiddlewares', body.data)
|
||||
// TODO - suggestion: add the total-pages in api response to optimize the query
|
||||
return { data: body.data || [], total }
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -3,15 +3,12 @@ import { APP } from '../_helpers/APP'
|
|||
const apiBase = '/tcp'
|
||||
|
||||
function getAllRouters (params) {
|
||||
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}`)
|
||||
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
||||
.then(body => {
|
||||
const total = body.data ? body.data.length : 0
|
||||
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
||||
.then(body => {
|
||||
console.log('Success -> HttpService -> getAllRouters', body.data)
|
||||
// TODO - suggestion: add the total-pages in api response to optimize the query
|
||||
return { data: body.data || [], total }
|
||||
})
|
||||
console.log('Success -> HttpService -> getAllRouters', body.data)
|
||||
// TODO - suggestion: add the total-pages in api response to optimize the query
|
||||
return { data: body.data || [], total }
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -24,15 +21,12 @@ function getRouterByName (name) {
|
|||
}
|
||||
|
||||
function getAllServices (params) {
|
||||
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}`)
|
||||
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
||||
.then(body => {
|
||||
const total = body.data ? body.data.length : 0
|
||||
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
||||
.then(body => {
|
||||
console.log('Success -> HttpService -> getAllServices', body.data)
|
||||
// TODO - suggestion: add the total-pages in api response to optimize the query
|
||||
return { data: body.data || [], total }
|
||||
})
|
||||
console.log('Success -> HttpService -> getAllServices', body.data)
|
||||
// TODO - suggestion: add the total-pages in api response to optimize the query
|
||||
return { data: body.data || [], total }
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<div class="text-subtitle2">RULE</div>
|
||||
<q-chip
|
||||
dense
|
||||
class="app-chip app-chip-rule">
|
||||
class="app-chip app-chip-wrap app-chip-rule">
|
||||
{{ data.rule }}
|
||||
</q-chip>
|
||||
</div>
|
||||
|
@ -39,7 +39,7 @@
|
|||
<div class="text-subtitle2">NAME</div>
|
||||
<q-chip
|
||||
dense
|
||||
class="app-chip app-chip-name">
|
||||
class="app-chip app-chip-wrap app-chip-name">
|
||||
{{ data.name }}
|
||||
</q-chip>
|
||||
</div>
|
||||
|
@ -66,7 +66,7 @@
|
|||
dense
|
||||
clickable
|
||||
@click.native="$router.push({ path: `/${protocol}/services/${getServiceId()}`})"
|
||||
class="app-chip app-chip-service">
|
||||
class="app-chip app-chip-wrap app-chip-service">
|
||||
{{ data.service }}
|
||||
</q-chip>
|
||||
</div>
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-section v-if="data.loadBalancer">
|
||||
<q-card-section v-if="data.loadBalancer && $route.meta.protocol !== 'tcp'">
|
||||
<div class="row items-start no-wrap">
|
||||
<div class="col">
|
||||
<div class="text-subtitle2">Pass Host Header</div>
|
||||
|
@ -54,6 +54,19 @@
|
|||
</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section v-if="data.loadBalancer.terminationDelay">
|
||||
<div class="row items-start no-wrap">
|
||||
<div class="col">
|
||||
<div class="text-subtitle2">Termination Delay</div>
|
||||
<q-chip
|
||||
dense
|
||||
class="app-chip app-chip-name">
|
||||
{{ data.loadBalancer.terminationDelay }} ms
|
||||
</q-chip>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-separator v-if="sticky" />
|
||||
<StickyServiceDetails v-if="sticky" :sticky="sticky" :dense="dense"/>
|
||||
</q-scroll-area>
|
||||
|
|
Loading…
Reference in a new issue