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)
|
## [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)
|
[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")
|
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 {
|
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}
|
client := &http.Client{Timeout: 5 * time.Second}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
You can install Traefik with the following flavors:
|
You can install Traefik with the following flavors:
|
||||||
|
|
||||||
* [Use the official Docker image](./#use-the-official-docker-image)
|
* [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)
|
* [Use the binary distribution](./#use-the-binary-distribution)
|
||||||
* [Compile your binary from the sources](./#compile-your-binary-from-the-sources)
|
* [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).
|
* 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.
|
* 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
|
## Use the Binary Distribution
|
||||||
|
|
||||||
Grab the latest binary from the [releases](https://github.com/containous/traefik/releases) page.
|
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"
|
```bash tab="CLI"
|
||||||
--entryPoints.web.address=":80"
|
--entryPoints.web.address=:80
|
||||||
--entryPoints.websecure.address=":443"
|
--entryPoints.websecure.address=:443
|
||||||
# ...
|
# ...
|
||||||
--certificatesResolvers.sample.acme.email="your-email@your-domain.org"
|
--certificatesResolvers.sample.acme.email=your-email@your-domain.org
|
||||||
--certificatesResolvers.sample.acme.storage="acme.json"
|
--certificatesResolvers.sample.acme.storage=acme.json
|
||||||
# used during the challenge
|
# used during the challenge
|
||||||
--certificatesResolvers.sample.acme.httpChallenge.entryPoint=web
|
--certificatesResolvers.sample.acme.httpChallenge.entryPoint=web
|
||||||
```
|
```
|
||||||
|
@ -156,8 +156,8 @@ when using the `HTTP-01` challenge, `certificatesResolvers.sample.acme.httpChall
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--entryPoints.web.address=":80"
|
--entryPoints.web.address=:80
|
||||||
--entryPoints.websecure.address=":443"
|
--entryPoints.websecure.address=:443
|
||||||
# ...
|
# ...
|
||||||
--certificatesResolvers.sample.acme.httpChallenge.entryPoint=web
|
--certificatesResolvers.sample.acme.httpChallenge.entryPoint=web
|
||||||
```
|
```
|
||||||
|
@ -312,7 +312,7 @@ certificatesResolvers:
|
||||||
|
|
||||||
```bash tab="CLI"
|
```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
|
#### Wildcard Domains
|
||||||
|
@ -342,7 +342,7 @@ As described in [Let's Encrypt's post](https://community.letsencrypt.org/t/stagi
|
||||||
|
|
||||||
```bash tab="CLI"
|
```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
|
# Required
|
||||||
#
|
#
|
||||||
--certificatesResolvers.sample.acme.email="test@traefik.io"
|
--certificatesResolvers.sample.acme.email=test@traefik.io
|
||||||
|
|
||||||
# File or key used for certificates storage.
|
# File or key used for certificates storage.
|
||||||
#
|
#
|
||||||
# Required
|
# Required
|
||||||
#
|
#
|
||||||
--certificatesResolvers.sample.acme.storage="acme.json"
|
--certificatesResolvers.sample.acme.storage=acme.json
|
||||||
|
|
||||||
# CA server to use.
|
# CA server to use.
|
||||||
# Uncomment the line to use Let's Encrypt's staging server,
|
# Uncomment the line to use Let's Encrypt's staging server,
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
# Optional
|
# Optional
|
||||||
# Default: "https://acme-v02.api.letsencrypt.org/directory"
|
# 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.
|
# KeyType to use.
|
||||||
#
|
#
|
||||||
|
@ -75,7 +75,7 @@
|
||||||
# Optional
|
# Optional
|
||||||
# Default: empty
|
# 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.
|
# 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"
|
```bash tab="CLI"
|
||||||
--entryPoints.web.address=":80"
|
--entryPoints.web.address=:80
|
||||||
--entryPoints.websecure.address=":443"
|
--entryPoints.websecure.address=:443
|
||||||
--certificatesResolvers.sample.acme.email: your-email@your-domain.org
|
--certificatesResolvers.sample.acme.email=your-email@your-domain.org
|
||||||
--certificatesResolvers.sample.acme.storage: acme.json
|
--certificatesResolvers.sample.acme.storage=acme.json
|
||||||
--certificatesResolvers.sample.acme.httpChallenge.entryPoint: web
|
--certificatesResolvers.sample.acme.httpChallenge.entryPoint=web
|
||||||
```
|
```
|
||||||
|
|
||||||
## Traefik Logs
|
## Traefik Logs
|
||||||
|
@ -744,9 +744,9 @@ There is no more log configuration at the root level.
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--logLevel="DEBUG"
|
--logLevel=DEBUG
|
||||||
--traefikLog.filePath="/path/to/traefik.log"
|
--traefikLog.filePath=/path/to/traefik.log
|
||||||
--traefikLog.format="json"
|
--traefikLog.format=json
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! info "v2"
|
!!! info "v2"
|
||||||
|
@ -768,9 +768,9 @@ There is no more log configuration at the root level.
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--log.level="DEBUG"
|
--log.level=DEBUG
|
||||||
--log.filePath="/path/to/traefik.log"
|
--log.filePath=/path/to/traefik.log
|
||||||
--log.format="json"
|
--log.format=json
|
||||||
```
|
```
|
||||||
|
|
||||||
## Tracing
|
## Tracing
|
||||||
|
@ -794,12 +794,12 @@ Traefik v2 retains OpenTracing support. The `backend` root option from the v1 is
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.backend="jaeger"
|
--tracing.backend=jaeger
|
||||||
--tracing.servicename="tracing"
|
--tracing.servicename=tracing
|
||||||
--tracing.jaeger.localagenthostport="12.0.0.1:6831"
|
--tracing.jaeger.localagenthostport=12.0.0.1:6831
|
||||||
--tracing.jaeger.samplingparam="1.0"
|
--tracing.jaeger.samplingparam=1.0
|
||||||
--tracing.jaeger.samplingserverurl="http://12.0.0.1:5778/sampling"
|
--tracing.jaeger.samplingserverurl=http://12.0.0.1:5778/sampling
|
||||||
--tracing.jaeger.samplingtype="const"
|
--tracing.jaeger.samplingtype=const
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! info "v2"
|
!!! info "v2"
|
||||||
|
@ -827,11 +827,11 @@ Traefik v2 retains OpenTracing support. The `backend` root option from the v1 is
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.servicename="tracing"
|
--tracing.servicename=tracing
|
||||||
--tracing.jaeger.localagenthostport="12.0.0.1:6831"
|
--tracing.jaeger.localagenthostport=12.0.0.1:6831
|
||||||
--tracing.jaeger.samplingparam="1.0"
|
--tracing.jaeger.samplingparam=1.0
|
||||||
--tracing.jaeger.samplingserverurl="http://12.0.0.1:5778/sampling"
|
--tracing.jaeger.samplingserverurl=http://12.0.0.1:5778/sampling
|
||||||
--tracing.jaeger.samplingtype="const"
|
--tracing.jaeger.samplingtype=const
|
||||||
```
|
```
|
||||||
|
|
||||||
## Metrics
|
## Metrics
|
||||||
|
@ -852,7 +852,7 @@ For a basic configuration, the [metrics configuration](../observability/metrics/
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.prometheus.buckets=[0.1,0.3,1.2,5.0]
|
--metrics.prometheus.buckets=[0.1,0.3,1.2,5.0]
|
||||||
--metrics.prometheus.entrypoint="traefik"
|
--metrics.prometheus.entrypoint=traefik
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! info "v2"
|
!!! info "v2"
|
||||||
|
@ -878,7 +878,7 @@ For a basic configuration, the [metrics configuration](../observability/metrics/
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.prometheus.buckets=[0.1,0.3,1.2,5.0]
|
--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
|
## 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"
|
```bash tab="CLI"
|
||||||
--checknewversion=false
|
--checknewversion=false
|
||||||
--sendanonymoususage=true
|
--sendanonymoususage=true
|
||||||
--loglevel="DEBUG"
|
--loglevel=DEBUG
|
||||||
--insecureskipverify=true
|
--insecureskipverify=true
|
||||||
--rootcas="/mycert.cert"
|
--rootcas=/mycert.cert
|
||||||
--maxidleconnsperhost=200
|
--maxidleconnsperhost=200
|
||||||
--providersthrottleduration="2s"
|
--providersthrottleduration=2s
|
||||||
--allowminweightzero=true
|
--allowminweightzero=true
|
||||||
--debug=true
|
--debug=true
|
||||||
--defaultentrypoints="web","web-secure"
|
--defaultentrypoints=web,web-secure
|
||||||
--keeptrailingslash=true
|
--keeptrailingslash=true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -961,9 +961,9 @@ Each root item has been moved to a related section or removed.
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--global.checknewversion=true
|
--global.checknewversion=true
|
||||||
--global.sendanonymoususage=true
|
--global.sendanonymoususage=true
|
||||||
--log.level="DEBUG"
|
--log.level=DEBUG
|
||||||
--serverstransport.insecureskipverify=true
|
--serverstransport.insecureskipverify=true
|
||||||
--serverstransport.rootcas="/mycert.cert"
|
--serverstransport.rootcas=/mycert.cert
|
||||||
--serverstransport.maxidleconnsperhost=42
|
--serverstransport.maxidleconnsperhost=42
|
||||||
--providers.providersthrottleduration=42
|
--providers.providersthrottleduration=42
|
||||||
```
|
```
|
||||||
|
|
|
@ -61,7 +61,7 @@ accessLog:
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
# Configuring a buffer of 100 lines
|
# Configuring a buffer of 100 lines
|
||||||
--accesslog=true
|
--accesslog=true
|
||||||
--accesslog.filepath="/path/to/access.log"
|
--accesslog.filepath=/path/to/access.log
|
||||||
--accesslog.bufferingsize=100
|
--accesslog.bufferingsize=100
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -104,11 +104,11 @@ accessLog:
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
# Configuring Multiple Filters
|
# Configuring Multiple Filters
|
||||||
--accesslog=true
|
--accesslog=true
|
||||||
--accesslog.filepath="/path/to/access.log"
|
--accesslog.filepath=/path/to/access.log
|
||||||
--accesslog.format="json"
|
--accesslog.format=json
|
||||||
--accesslog.filters.statuscodes="200, 300-302"
|
--accesslog.filters.statuscodes=200,300-302
|
||||||
--accesslog.filters.retryattempts
|
--accesslog.filters.retryattempts
|
||||||
--accesslog.filters.minduration="10ms"
|
--accesslog.filters.minduration=10ms
|
||||||
```
|
```
|
||||||
|
|
||||||
### Limiting the Fields
|
### Limiting the Fields
|
||||||
|
@ -164,14 +164,14 @@ accessLog:
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
# Limiting the Logs to Specific Fields
|
# Limiting the Logs to Specific Fields
|
||||||
--accesslog=true
|
--accesslog=true
|
||||||
--accesslog.filepath="/path/to/access.log"
|
--accesslog.filepath=/path/to/access.log
|
||||||
--accesslog.format="json"
|
--accesslog.format=json
|
||||||
--accesslog.fields.defaultmode="keep"
|
--accesslog.fields.defaultmode=keep
|
||||||
--accesslog.fields.names.ClientUsername="drop"
|
--accesslog.fields.names.ClientUsername=drop
|
||||||
--accesslog.fields.headers.defaultmode="keep"
|
--accesslog.fields.headers.defaultmode=keep
|
||||||
--accesslog.fields.headers.names.User-Agent="redact"
|
--accesslog.fields.headers.names.User-Agent=redact
|
||||||
--accesslog.fields.headers.names.Authorization="drop"
|
--accesslog.fields.headers.names.Authorization=drop
|
||||||
--accesslog.fields.headers.names.Content-Type="keep"
|
--accesslog.fields.headers.names.Content-Type=keep
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "Available Fields"
|
??? info "Available Fields"
|
||||||
|
|
|
@ -30,7 +30,7 @@ log:
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
# Writing Logs to a File
|
# Writing Logs to a File
|
||||||
--log.filePath="/path/to/traefik.log"
|
--log.filePath=/path/to/traefik.log
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `format`
|
#### `format`
|
||||||
|
@ -53,8 +53,8 @@ log:
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
# Writing Logs to a File, in JSON
|
# Writing Logs to a File, in JSON
|
||||||
--log.filePath="/path/to/traefik.log"
|
--log.filePath=/path/to/traefik.log
|
||||||
--log.format="json"
|
--log.format=json
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `level`
|
#### `level`
|
||||||
|
@ -72,7 +72,7 @@ log:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--log.level="DEBUG"
|
--log.level=DEBUG
|
||||||
```
|
```
|
||||||
|
|
||||||
## Log Rotation
|
## Log Rotation
|
||||||
|
|
|
@ -35,7 +35,7 @@ metrics:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.datadog.address="127.0.0.1:8125"
|
--metrics.datadog.address=127.0.0.1:8125
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `addEntryPointsLabels`
|
#### `addEntryPointsLabels`
|
||||||
|
|
|
@ -35,7 +35,7 @@ metrics:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.influxdb.address="localhost:8089"
|
--metrics.influxdb.address=localhost:8089
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `protocol`
|
#### `protocol`
|
||||||
|
@ -57,7 +57,7 @@ metrics:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.influxdb.protocol="udp"
|
--metrics.influxdb.protocol=udp
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `database`
|
#### `database`
|
||||||
|
@ -69,17 +69,17 @@ InfluxDB database used when protocol is http.
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
[metrics]
|
[metrics]
|
||||||
[metrics.influxDB]
|
[metrics.influxDB]
|
||||||
database = ""
|
database = "db"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
metrics:
|
metrics:
|
||||||
influxDB:
|
influxDB:
|
||||||
database: ""
|
database: "db"
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.influxdb.database=""
|
--metrics.influxdb.database=db
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `retentionPolicy`
|
#### `retentionPolicy`
|
||||||
|
@ -91,17 +91,17 @@ InfluxDB retention policy used when protocol is http.
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
[metrics]
|
[metrics]
|
||||||
[metrics.influxDB]
|
[metrics.influxDB]
|
||||||
retentionPolicy = ""
|
retentionPolicy = "two_hours"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
metrics:
|
metrics:
|
||||||
influxDB:
|
influxDB:
|
||||||
retentionPolicy: ""
|
retentionPolicy: "two_hours"
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.influxdb.retentionPolicy=""
|
--metrics.influxdb.retentionPolicy=two_hours
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `username`
|
#### `username`
|
||||||
|
@ -113,17 +113,17 @@ InfluxDB username (only with http).
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
[metrics]
|
[metrics]
|
||||||
[metrics.influxDB]
|
[metrics.influxDB]
|
||||||
username = ""
|
username = "john"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
metrics:
|
metrics:
|
||||||
influxDB:
|
influxDB:
|
||||||
username: ""
|
username: "john"
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.influxdb.username=""
|
--metrics.influxdb.username=john
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `password`
|
#### `password`
|
||||||
|
@ -135,17 +135,17 @@ InfluxDB password (only with http).
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
[metrics]
|
[metrics]
|
||||||
[metrics.influxDB]
|
[metrics.influxDB]
|
||||||
password = ""
|
password = "secret"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
metrics:
|
metrics:
|
||||||
influxDB:
|
influxDB:
|
||||||
password: ""
|
password: "secret"
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.influxdb.password=""
|
--metrics.influxdb.password=secret
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `addEntryPointsLabels`
|
#### `addEntryPointsLabels`
|
||||||
|
|
|
@ -113,8 +113,8 @@ metrics:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--entryPoints.metrics.address=":8082"
|
--entryPoints.metrics.address=:8082
|
||||||
--metrics.prometheus.entryPoint="metrics"
|
--metrics.prometheus.entryPoint=metrics
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `manualRouting`
|
#### `manualRouting`
|
||||||
|
|
|
@ -35,7 +35,7 @@ metrics:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.statsd.address="localhost:8125"
|
--metrics.statsd.address=localhost:8125
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `addEntryPointsLabels`
|
#### `addEntryPointsLabels`
|
||||||
|
|
|
@ -35,7 +35,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.datadog.localAgentHostPort="127.0.0.1:8126"
|
--tracing.datadog.localAgentHostPort=127.0.0.1:8126
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `debug`
|
#### `debug`
|
||||||
|
@ -79,7 +79,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.datadog.globalTag="sample"
|
--tracing.datadog.globalTag=sample
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `prioritySampling`
|
#### `prioritySampling`
|
||||||
|
|
|
@ -35,7 +35,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.haystack.localAgentHost="127.0.0.1"
|
--tracing.haystack.localAgentHost=127.0.0.1
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `localAgentPort`
|
#### `localAgentPort`
|
||||||
|
@ -79,7 +79,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.haystack.globalTag="sample:test"
|
--tracing.haystack.globalTag=sample:test
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `traceIDHeaderName`
|
#### `traceIDHeaderName`
|
||||||
|
@ -101,7 +101,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.haystack.traceIDHeaderName="sample"
|
--tracing.haystack.traceIDHeaderName=sample
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `parentIDHeaderName`
|
#### `parentIDHeaderName`
|
||||||
|
@ -123,7 +123,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.haystack.parentIDHeaderName="sample"
|
--tracing.haystack.parentIDHeaderName=sample
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `spanIDHeaderName`
|
#### `spanIDHeaderName`
|
||||||
|
@ -168,5 +168,5 @@ tracing:
|
||||||
|
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.haystack.baggagePrefixHeaderName="sample"
|
--tracing.haystack.baggagePrefixHeaderName=sample
|
||||||
```
|
```
|
||||||
|
|
|
@ -35,7 +35,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.instana.localAgentHost="127.0.0.1"
|
--tracing.instana.localAgentHost=127.0.0.1
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `localAgentPort`
|
#### `localAgentPort`
|
||||||
|
@ -86,5 +86,5 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.instana.logLevel="info"
|
--tracing.instana.logLevel=info
|
||||||
```
|
```
|
||||||
|
|
|
@ -39,7 +39,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.jaeger.samplingServerURL="http://localhost:5778/sampling"
|
--tracing.jaeger.samplingServerURL=http://localhost:5778/sampling
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `samplingType`
|
#### `samplingType`
|
||||||
|
@ -61,7 +61,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.jaeger.samplingType="const"
|
--tracing.jaeger.samplingType=const
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `samplingParam`
|
#### `samplingParam`
|
||||||
|
@ -89,7 +89,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.jaeger.samplingParam="1.0"
|
--tracing.jaeger.samplingParam=1.0
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `localAgentHostPort`
|
#### `localAgentHostPort`
|
||||||
|
@ -111,7 +111,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.jaeger.localAgentHostPort="127.0.0.1:6831"
|
--tracing.jaeger.localAgentHostPort=127.0.0.1:6831
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `gen128Bit`
|
#### `gen128Bit`
|
||||||
|
@ -159,7 +159,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.jaeger.propagation="jaeger"
|
--tracing.jaeger.propagation=jaeger
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `traceContextHeaderName`
|
#### `traceContextHeaderName`
|
||||||
|
@ -182,7 +182,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.jaeger.traceContextHeaderName="uber-trace-id"
|
--tracing.jaeger.traceContextHeaderName=uber-trace-id
|
||||||
```
|
```
|
||||||
|
|
||||||
### `collector`
|
### `collector`
|
||||||
|
@ -206,7 +206,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```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`
|
#### `user`
|
||||||
|
@ -229,7 +229,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.jaeger.collector.user="my-user"
|
--tracing.jaeger.collector.user=my-user
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `password`
|
#### `password`
|
||||||
|
@ -252,5 +252,5 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.jaeger.collector.password="my-password"
|
--tracing.jaeger.collector.password=my-password
|
||||||
```
|
```
|
||||||
|
|
|
@ -52,7 +52,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.serviceName="traefik"
|
--tracing.serviceName=traefik
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `spanNameLimit`
|
#### `spanNameLimit`
|
||||||
|
|
|
@ -35,7 +35,7 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.zipkin.httpEndpoint="http://localhost:9411/api/v2/spans"
|
--tracing.zipkin.httpEndpoint=http://localhost:9411/api/v2/spans
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `sameSpan`
|
#### `sameSpan`
|
||||||
|
@ -101,5 +101,5 @@ tracing:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.zipkin.sampleRate="0.2"
|
--tracing.zipkin.sampleRate=0.2
|
||||||
```
|
```
|
||||||
|
|
|
@ -55,8 +55,8 @@ ping:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--entryPoints.ping.address=":8082"
|
--entryPoints.ping.address=:8082
|
||||||
--ping.entryPoint="ping"
|
--ping.entryPoint=ping
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `manualRouting`
|
#### `manualRouting`
|
||||||
|
|
|
@ -7,6 +7,9 @@ A Story of Labels & Containers
|
||||||
|
|
||||||
Attach labels to your containers and let Traefik do the rest!
|
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"
|
!!! 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!
|
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"
|
```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
|
--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
|
- 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
|
## 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
|
## Provider Configuration
|
||||||
|
|
||||||
|
@ -108,51 +232,10 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```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.
|
See the sections [Docker API Access](#docker-api-access) and [Docker Swarm API Access](#docker-api-access_1) for more information.
|
||||||
|
|
||||||
??? 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.
|
|
||||||
|
|
||||||
??? example "Using the docker.sock"
|
??? example "Using the docker.sock"
|
||||||
|
|
||||||
|
@ -186,7 +269,7 @@ Traefik requires access to the docker socket to get its dynamic configuration.
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```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"
|
```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`
|
### `swarmModeRefreshSeconds`
|
||||||
|
|
||||||
|
@ -375,19 +458,19 @@ _Optional, Default=""_
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
[providers.docker]
|
[providers.docker]
|
||||||
constraints = "Label(`a.label.name`, `foo`)"
|
constraints = "Label(`a.label.name`,`foo`)"
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
providers:
|
providers:
|
||||||
docker:
|
docker:
|
||||||
constraints: "Label(`a.label.name`, `foo`)"
|
constraints: "Label(`a.label.name`,`foo`)"
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```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"
|
```bash tab="CLI"
|
||||||
--providers.kubernetescrd.endpoint="http://localhost:8080"
|
--providers.kubernetescrd.endpoint=http://localhost:8080
|
||||||
```
|
```
|
||||||
|
|
||||||
The Kubernetes server endpoint as URL.
|
The Kubernetes server endpoint as URL.
|
||||||
|
@ -66,7 +66,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetescrd.token="mytoken"
|
--providers.kubernetescrd.token=mytoken
|
||||||
```
|
```
|
||||||
|
|
||||||
Bearer token used for the Kubernetes client configuration.
|
Bearer token used for the Kubernetes client configuration.
|
||||||
|
@ -89,7 +89,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetescrd.certauthfilepath="/my/ca.crt"
|
--providers.kubernetescrd.certauthfilepath=/my/ca.crt
|
||||||
```
|
```
|
||||||
|
|
||||||
Path to the certificate authority file.
|
Path to the certificate authority file.
|
||||||
|
@ -115,7 +115,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetescrd.namespaces="default,production"
|
--providers.kubernetescrd.namespaces=default,production
|
||||||
```
|
```
|
||||||
|
|
||||||
Array of namespaces to watch.
|
Array of namespaces to watch.
|
||||||
|
@ -164,7 +164,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```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.
|
Value of `kubernetes.io/ingress.class` annotation that identifies Ingress objects to be processed.
|
||||||
|
@ -190,7 +190,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetescrd.throttleDuration="10s"
|
--providers.kubernetescrd.throttleDuration=10s
|
||||||
```
|
```
|
||||||
|
|
||||||
## Further
|
## Further
|
||||||
|
|
|
@ -67,7 +67,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```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.
|
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"
|
```bash tab="CLI"
|
||||||
--providers.kubernetesingress.token="mytoken"
|
--providers.kubernetesingress.token=mytoken
|
||||||
```
|
```
|
||||||
|
|
||||||
Bearer token used for the Kubernetes client configuration.
|
Bearer token used for the Kubernetes client configuration.
|
||||||
|
@ -122,7 +122,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetesingress.certauthfilepath="/my/ca.crt"
|
--providers.kubernetesingress.certauthfilepath=/my/ca.crt
|
||||||
```
|
```
|
||||||
|
|
||||||
Path to the certificate authority file.
|
Path to the certificate authority file.
|
||||||
|
@ -171,7 +171,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetesingress.namespaces="default,production"
|
--providers.kubernetesingress.namespaces=default,production
|
||||||
```
|
```
|
||||||
|
|
||||||
Array of namespaces to watch.
|
Array of namespaces to watch.
|
||||||
|
@ -220,7 +220,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```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.
|
Value of `kubernetes.io/ingress.class` annotation that identifies Ingress objects to be processed.
|
||||||
|
@ -249,7 +249,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetesingress.ingressendpoint.hostname="foo.com"
|
--providers.kubernetesingress.ingressendpoint.hostname=foo.com
|
||||||
```
|
```
|
||||||
|
|
||||||
Hostname used for Kubernetes Ingress endpoints.
|
Hostname used for Kubernetes Ingress endpoints.
|
||||||
|
@ -273,7 +273,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```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.
|
IP used for Kubernetes Ingress endpoints.
|
||||||
|
@ -297,7 +297,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetesingress.ingressendpoint.publishedservice="foo-service"
|
--providers.kubernetesingress.ingressendpoint.publishedservice=foo-service
|
||||||
```
|
```
|
||||||
|
|
||||||
Published Kubernetes Service to copy status from.
|
Published Kubernetes Service to copy status from.
|
||||||
|
@ -320,7 +320,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.kubernetesingress.throttleDuration="10s"
|
--providers.kubernetesingress.throttleDuration=10s
|
||||||
```
|
```
|
||||||
|
|
||||||
## Further
|
## Further
|
||||||
|
|
|
@ -74,8 +74,8 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.marathon.basic.httpbasicauthuser="foo"
|
--providers.marathon.basic.httpbasicauthuser=foo
|
||||||
--providers.marathon.basic.httpbasicpassword="bar"
|
--providers.marathon.basic.httpbasicpassword=bar
|
||||||
```
|
```
|
||||||
|
|
||||||
Enables Marathon basic authentication.
|
Enables Marathon basic authentication.
|
||||||
|
@ -98,7 +98,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.marathon.dcosToken="xxxxxx"
|
--providers.marathon.dcosToken=xxxxxx
|
||||||
```
|
```
|
||||||
|
|
||||||
DCOSToken for DCOS environment.
|
DCOSToken for DCOS environment.
|
||||||
|
@ -123,7 +123,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```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"
|
```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.
|
Marathon server endpoint.
|
||||||
|
@ -223,19 +223,19 @@ _Optional, Default=""_
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
[providers.marathon]
|
[providers.marathon]
|
||||||
constraints = "Label(`a.label.name`, `foo`)"
|
constraints = "Label(`a.label.name`,`foo`)"
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
providers:
|
providers:
|
||||||
marathon:
|
marathon:
|
||||||
constraints: "Label(`a.label.name`, `foo`)"
|
constraints: "Label(`a.label.name`,`foo`)"
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```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"
|
```bash tab="CLI"
|
||||||
--providers.marathon.responseHeaderTimeout="66s"
|
--providers.marathon.responseHeaderTimeout=66s
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -532,7 +532,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.marathon.responseHeaderTimeout="10s"
|
--providers.marathon.responseHeaderTimeout=10s
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -104,7 +104,7 @@ providers:
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```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"
|
```bash tab="CLI"
|
||||||
--providers.rancher.prefix="/test"
|
--providers.rancher.prefix=/test
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -221,19 +221,19 @@ _Optional, Default=""_
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
[providers.rancher]
|
[providers.rancher]
|
||||||
constraints = "Label(`a.label.name`, `foo`)"
|
constraints = "Label(`a.label.name`,`foo`)"
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
providers:
|
providers:
|
||||||
rancher:
|
rancher:
|
||||||
constraints: "Label(`a.label.name`, `foo`)"
|
constraints: "Label(`a.label.name`,`foo`)"
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```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
|
--providers.rancher.intervalPoll=false
|
||||||
|
|
||||||
# Prefix used for accessing the Rancher metadata service
|
# Prefix used for accessing the Rancher metadata service
|
||||||
--providers.rancher.prefix="/latest"
|
--providers.rancher.prefix=/latest
|
||||||
|
|
|
@ -18,4 +18,4 @@ providers:
|
||||||
intervalPoll: false
|
intervalPoll: false
|
||||||
|
|
||||||
# Prefix used for accessing the Rancher metadata service
|
# 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.writeTimeout=42
|
||||||
--entryPoints.name.transport.respondingTimeouts.idleTimeout=42
|
--entryPoints.name.transport.respondingTimeouts.idleTimeout=42
|
||||||
--entryPoints.name.proxyProtocol.insecure=true
|
--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.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
|
### Forwarded Header
|
||||||
|
|
|
@ -151,7 +151,7 @@ http:
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
# Listen on port 8081 for incoming requests
|
# 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
|
# Enable the file provider to define routers / middlewares / services in a file
|
||||||
--providers.file.filename=dynamic_conf.toml
|
--providers.file.filename=dynamic_conf.toml
|
||||||
|
|
|
@ -82,7 +82,7 @@ Attach labels to your containers and let Traefik do the rest!
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```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
|
--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.
|
See [entry points](../routers/index.md#entrypoints) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.entrypoints=web,websecure"
|
- "traefik.http.routers.myrouter.entrypoints=ep1,ep2"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.middlewares`"
|
??? 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.
|
Registers a port.
|
||||||
Useful when the container exposes multiples ports.
|
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
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.server.port=8080"
|
- "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.
|
See [entry points](../routers/index.md#entrypoints) for more information.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"traefik.http.routers.myrouter.entrypoints": "web,websecure"
|
"traefik.http.routers.myrouter.entrypoints": "ep1,ep2"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.middlewares`"
|
??? 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.
|
See [entry points](../routers/index.md#entrypoints) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.entrypoints=web,websecure"
|
- "traefik.http.routers.myrouter.entrypoints=ep1,ep2"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.middlewares`"
|
??? 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"
|
```bash tab="CLI"
|
||||||
## Static configuration
|
## Static configuration
|
||||||
--entryPoints.web.address=":80"
|
--entryPoints.web.address=:80
|
||||||
--entryPoints.mysql.address=":3306"
|
--entryPoints.mysql.address=:3306
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuring HTTP Routers
|
## 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"
|
```bash tab="CLI"
|
||||||
## Static configuration
|
## Static configuration
|
||||||
--entrypoints.web.address=":80"
|
--entrypoints.web.address=:80
|
||||||
--entrypoints.websecure.address=":443"
|
--entrypoints.websecure.address=:443
|
||||||
--entrypoints.other.address=":9090"
|
--entrypoints.other.address=:9090
|
||||||
```
|
```
|
||||||
|
|
||||||
??? example "Listens to Specific EntryPoints"
|
??? 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"
|
```bash tab="CLI"
|
||||||
## Static configuration
|
## Static configuration
|
||||||
--entrypoints.web.address=":80"
|
--entrypoints.web.address=:80
|
||||||
--entrypoints.websecure.address=":443"
|
--entrypoints.websecure.address=:443
|
||||||
--entrypoints.other.address=":9090"
|
--entrypoints.other.address=:9090
|
||||||
```
|
```
|
||||||
|
|
||||||
### Rule
|
### 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"
|
```bash tab="CLI"
|
||||||
## Static configuration
|
## Static configuration
|
||||||
--entrypoints.web.address=":80"
|
--entrypoints.web.address=:80
|
||||||
--entrypoints.websecure.address=":443"
|
--entrypoints.websecure.address=:443
|
||||||
--entrypoints.other.address=":9090"
|
--entrypoints.other.address=:9090
|
||||||
```
|
```
|
||||||
|
|
||||||
??? example "Listens to Specific Entry Points"
|
??? 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"
|
```bash tab="CLI"
|
||||||
## Static configuration
|
## Static configuration
|
||||||
--entrypoints.web.address=":80"
|
--entrypoints.web.address=:80
|
||||||
--entrypoints.websecure.address=":443"
|
--entrypoints.websecure.address=:443
|
||||||
--entrypoints.other.address=":9090"
|
--entrypoints.other.address=:9090
|
||||||
```
|
```
|
||||||
|
|
||||||
### Rule
|
### Rule
|
||||||
|
|
|
@ -32,7 +32,7 @@ api: {}
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="CLI"
|
```yaml tab="CLI"
|
||||||
--entryPoints.web.address=":80"
|
--entryPoints.web.address=:80
|
||||||
--providers.file.filename=dynamic_conf.toml
|
--providers.file.filename=dynamic_conf.toml
|
||||||
--api.insecure=true
|
--api.insecure=true
|
||||||
```
|
```
|
||||||
|
@ -153,7 +153,7 @@ api: {}
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="CLI"
|
```yaml tab="CLI"
|
||||||
--entryPoints.websecure.address=":4443"
|
--entryPoints.websecure.address=:4443
|
||||||
# For secure connection on backend.local
|
# For secure connection on backend.local
|
||||||
--serversTransport.rootCAs=./backend.cert
|
--serversTransport.rootCAs=./backend.cert
|
||||||
--providers.file.filename=dynamic_conf.toml
|
--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/opencontainers/runc v1.0.0-rc8 // indirect
|
||||||
github.com/opentracing/basictracer-go v1.0.0 // indirect
|
github.com/opentracing/basictracer-go v1.0.0 // indirect
|
||||||
github.com/opentracing/opentracing-go v1.1.0
|
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/openzipkin/zipkin-go v0.2.1
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/philhofer/fwd v1.0.0 // indirect
|
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/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 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
|
||||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
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.5 h1:ZCnq+JUrvXcDVhX/xRolRBZifmabN1HcS1wrPSvxhrU=
|
||||||
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/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.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 h1:noL5/5Uf1HpVl3wNsfkZhIKbSWCVi5jgqkONNx8PXcA=
|
||||||
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
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))
|
err = try.Request(frontendHealthReq, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
||||||
c.Assert(err, checker.IsNil)
|
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 singleton *HealthCheck
|
||||||
var once sync.Once
|
var once sync.Once
|
||||||
|
|
||||||
// BalancerHandler includes functionality for load-balancing management.
|
// Balancer is the set of operations required to manage the list of servers in a
|
||||||
type BalancerHandler interface {
|
// load-balancer.
|
||||||
ServeHTTP(w http.ResponseWriter, req *http.Request)
|
type Balancer interface {
|
||||||
Servers() []*url.URL
|
Servers() []*url.URL
|
||||||
RemoveServer(u *url.URL) error
|
RemoveServer(u *url.URL) error
|
||||||
UpsertServer(u *url.URL, options ...roundrobin.ServerOption) 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
|
// 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.
|
// necessary for the health check package. This makes it easier for the tests.
|
||||||
type metricsRegistry interface {
|
type metricsRegistry interface {
|
||||||
|
@ -49,7 +55,7 @@ type Options struct {
|
||||||
Transport http.RoundTripper
|
Transport http.RoundTripper
|
||||||
Interval time.Duration
|
Interval time.Duration
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
LB BalancerHandler
|
LB Balancer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opt Options) String() string {
|
func (opt Options) String() string {
|
||||||
|
@ -146,18 +152,18 @@ func (hc *HealthCheck) checkBackend(ctx context.Context, backend *BackendConfig)
|
||||||
enabledURLs := backend.LB.Servers()
|
enabledURLs := backend.LB.Servers()
|
||||||
var newDisabledURLs []backendURL
|
var newDisabledURLs []backendURL
|
||||||
// FIXME re enable metrics
|
// FIXME re enable metrics
|
||||||
for _, disableURL := range backend.disabledURLs {
|
for _, disabledURL := range backend.disabledURLs {
|
||||||
// FIXME serverUpMetricValue := float64(0)
|
// 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",
|
logger.Warnf("Health check up: Returning to server list. Backend: %q URL: %q Weight: %d",
|
||||||
backend.name, disableURL.url.String(), disableURL.weight)
|
backend.name, disabledURL.url.String(), disabledURL.weight)
|
||||||
if err = backend.LB.UpsertServer(disableURL.url, roundrobin.Weight(disableURL.weight)); err != nil {
|
if err = backend.LB.UpsertServer(disabledURL.url, roundrobin.Weight(disabledURL.weight)); err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
}
|
}
|
||||||
// FIXME serverUpMetricValue = 1
|
// FIXME serverUpMetricValue = 1
|
||||||
} else {
|
} else {
|
||||||
logger.Warnf("Health check still failing. Backend: %q URL: %q Reason: %s", backend.name, disableURL.url.String(), err)
|
logger.Warnf("Health check still failing. Backend: %q URL: %q Reason: %s", backend.name, disabledURL.url.String(), err)
|
||||||
newDisabledURLs = append(newDisabledURLs, disableURL)
|
newDisabledURLs = append(newDisabledURLs, disabledURL)
|
||||||
}
|
}
|
||||||
// FIXME labelValues := []string{"backend", backend.name, "url", backendurl.url.String()}
|
// FIXME labelValues := []string{"backend", backend.name, "url", backendurl.url.String()}
|
||||||
// FIXME hc.metrics.BackendServerUpGauge().With(labelValues...).Set(serverUpMetricValue)
|
// FIXME hc.metrics.BackendServerUpGauge().With(labelValues...).Set(serverUpMetricValue)
|
||||||
|
@ -177,7 +183,7 @@ func (hc *HealthCheck) checkBackend(ctx context.Context, backend *BackendConfig)
|
||||||
weight = 1
|
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 {
|
if err := backend.LB.RemoveServer(enableURL); err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -281,3 +287,38 @@ func (lb *LbStatusUpdater) UpsertServer(u *url.URL, options ...roundrobin.Server
|
||||||
}
|
}
|
||||||
return err
|
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.
|
// LogData is the data captured by the middleware so that it can be logged.
|
||||||
type LogData struct {
|
type LogData struct {
|
||||||
Core CoreLogData
|
Core CoreLogData
|
||||||
Request http.Header
|
Request request
|
||||||
OriginResponse http.Header
|
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 {
|
type handlerParams struct {
|
||||||
logDataTable *LogData
|
logDataTable *LogData
|
||||||
crr *captureRequestReader
|
|
||||||
crw *captureResponseWriter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handler will write each request and its response to the access log.
|
// 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() {
|
go func() {
|
||||||
defer logHandler.wg.Done()
|
defer logHandler.wg.Done()
|
||||||
for handlerParams := range logHandler.logHandlerChan {
|
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(),
|
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))
|
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)
|
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 {
|
if h.config.BufferingSize > 0 {
|
||||||
h.logHandlerChan <- handlerParams{
|
h.logHandlerChan <- handlerParams{
|
||||||
logDataTable: logDataTable,
|
logDataTable: logDataTable,
|
||||||
crr: crr,
|
|
||||||
crw: crw,
|
|
||||||
}
|
}
|
||||||
} else {
|
} 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.
|
// 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
|
core := logDataTable.Core
|
||||||
|
|
||||||
retryAttempts, ok := core[RetryAttempts].(int)
|
retryAttempts, ok := core[RetryAttempts].(int)
|
||||||
|
@ -272,23 +280,22 @@ func (h *Handler) logTheRoundTrip(logDataTable *LogData, crr *captureRequestRead
|
||||||
retryAttempts = 0
|
retryAttempts = 0
|
||||||
}
|
}
|
||||||
core[RetryAttempts] = retryAttempts
|
core[RetryAttempts] = retryAttempts
|
||||||
|
core[RequestContentSize] = logDataTable.Request.count
|
||||||
|
|
||||||
if crr != nil {
|
status := logDataTable.DownstreamResponse.status
|
||||||
core[RequestContentSize] = crr.count
|
core[DownstreamStatus] = status
|
||||||
}
|
|
||||||
|
|
||||||
core[DownstreamStatus] = crw.Status()
|
|
||||||
|
|
||||||
// n.b. take care to perform time arithmetic using UTC to avoid errors at DST boundaries.
|
// 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))
|
totalDuration := time.Now().UTC().Sub(core[StartUTC].(time.Time))
|
||||||
core[Duration] = totalDuration
|
core[Duration] = totalDuration
|
||||||
|
|
||||||
if h.keepAccessLog(crw.Status(), retryAttempts, totalDuration) {
|
if h.keepAccessLog(status, retryAttempts, totalDuration) {
|
||||||
core[DownstreamContentSize] = crw.Size()
|
size := logDataTable.DownstreamResponse.size
|
||||||
|
core[DownstreamContentSize] = size
|
||||||
if original, ok := core[OriginContentSize]; ok {
|
if original, ok := core[OriginContentSize]; ok {
|
||||||
o64 := original.(int64)
|
o64 := original.(int64)
|
||||||
if crw.Size() != o64 && crw.Size() != 0 {
|
if size != o64 && size != 0 {
|
||||||
core[GzipRatio] = float64(o64) / float64(crw.Size())
|
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.OriginResponse, fields, "origin_")
|
||||||
h.redactHeaders(logDataTable.DownstreamResponse, fields, "downstream_")
|
h.redactHeaders(logDataTable.DownstreamResponse.headers, fields, "downstream_")
|
||||||
|
|
||||||
h.mu.Lock()
|
h.mu.Lock()
|
||||||
defer h.mu.Unlock()
|
defer h.mu.Unlock()
|
||||||
|
|
|
@ -192,6 +192,7 @@ func TestLoggerJSON(t *testing.T) {
|
||||||
Format: JSONFormat,
|
Format: JSONFormat,
|
||||||
},
|
},
|
||||||
expected: map[string]func(t *testing.T, value interface{}){
|
expected: map[string]func(t *testing.T, value interface{}){
|
||||||
|
RequestContentSize: assertFloat64(0),
|
||||||
RequestHost: assertString(testHostname),
|
RequestHost: assertString(testHostname),
|
||||||
RequestAddr: assertString(testHostname),
|
RequestAddr: assertString(testHostname),
|
||||||
RequestMethod: assertString(testMethod),
|
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")
|
reqAcMethod := req.Header.Get("Access-Control-Request-Method")
|
||||||
reqAcHeaders := req.Header.Get("Access-Control-Request-Headers")
|
|
||||||
originHeader := req.Header.Get("Origin")
|
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,
|
// If the request is an OPTIONS request with an Access-Control-Request-Method header,
|
||||||
// and Access-Control-Request-Headers headers, and Origin headers,
|
// and Origin headers, then it is a CORS preflight request,
|
||||||
// then it is a CORS preflight request,
|
|
||||||
// and we need to build a custom response: https://www.w3.org/TR/cors/#preflight-request
|
// and we need to build a custom response: https://www.w3.org/TR/cors/#preflight-request
|
||||||
if s.headers.AccessControlAllowCredentials {
|
if s.headers.AccessControlAllowCredentials {
|
||||||
rw.Header().Set("Access-Control-Allow-Credentials", "true")
|
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"},
|
"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 {
|
for _, test := range testCases {
|
||||||
|
|
|
@ -34,7 +34,11 @@ type entryPointMiddleware struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *entryPointMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
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))
|
span, req, finish := e.StartSpanf(req, ext.SpanKindRPCServerEnum, "EntryPoint", []string{e.entryPoint, req.Host}, " ", ext.RPCServerOption(spanCtx))
|
||||||
defer finish()
|
defer finish()
|
||||||
|
|
|
@ -193,7 +193,7 @@ func (p *Provider) parseMetadataSourcedRancherData(ctx context.Context, stacks [
|
||||||
}
|
}
|
||||||
|
|
||||||
service := rancherData{
|
service := rancherData{
|
||||||
Name: service.Name + "/" + stack.Name,
|
Name: service.Name + "_" + stack.Name,
|
||||||
State: service.State,
|
State: service.State,
|
||||||
Labels: service.Labels,
|
Labels: service.Labels,
|
||||||
Port: servicePort,
|
Port: servicePort,
|
||||||
|
|
|
@ -40,7 +40,7 @@ func NewManager(configs map[string]*runtime.ServiceInfo, defaultRoundTripper htt
|
||||||
metricsRegistry: metricsRegistry,
|
metricsRegistry: metricsRegistry,
|
||||||
bufferPool: newBufferPool(),
|
bufferPool: newBufferPool(),
|
||||||
defaultRoundTripper: defaultRoundTripper,
|
defaultRoundTripper: defaultRoundTripper,
|
||||||
balancers: make(map[string][]healthcheck.BalancerHandler),
|
balancers: make(map[string]healthcheck.Balancers),
|
||||||
configs: configs,
|
configs: configs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,11 @@ type Manager struct {
|
||||||
metricsRegistry metrics.Registry
|
metricsRegistry metrics.Registry
|
||||||
bufferPool httputil.BufferPool
|
bufferPool httputil.BufferPool
|
||||||
defaultRoundTripper http.RoundTripper
|
defaultRoundTripper http.RoundTripper
|
||||||
balancers map[string][]healthcheck.BalancerHandler
|
// 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
|
configs map[string]*runtime.ServiceInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,14 +96,14 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string, respons
|
||||||
}
|
}
|
||||||
case conf.Weighted != nil:
|
case conf.Weighted != nil:
|
||||||
var err error
|
var err error
|
||||||
lb, err = m.getLoadBalancerWRRServiceHandler(ctx, serviceName, conf.Weighted, responseModifier)
|
lb, err = m.getWRRServiceHandler(ctx, serviceName, conf.Weighted, responseModifier)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conf.AddError(err, true)
|
conf.AddError(err, true)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
case conf.Mirroring != nil:
|
case conf.Mirroring != nil:
|
||||||
var err error
|
var err error
|
||||||
lb, err = m.getLoadBalancerMirrorServiceHandler(ctx, serviceName, conf.Mirroring, responseModifier)
|
lb, err = m.getMirrorServiceHandler(ctx, conf.Mirroring, responseModifier)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conf.AddError(err, true)
|
conf.AddError(err, true)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -113,7 +117,7 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string, respons
|
||||||
return lb, nil
|
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)
|
serviceHandler, err := m.BuildHTTP(ctx, config.Service, responseModifier)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -134,7 +138,7 @@ func (m *Manager) getLoadBalancerMirrorServiceHandler(ctx context.Context, servi
|
||||||
return handler, nil
|
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
|
// TODO Handle accesslog and metrics with multiple service name
|
||||||
if config.Sticky != nil && config.Sticky.Cookie != nil {
|
if config.Sticky != nil && config.Sticky.Cookie != nil {
|
||||||
config.Sticky.Cookie.Name = cookie.GetName(config.Sticky.Cookie.Name, serviceName)
|
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 {
|
for serviceName, balancers := range m.balancers {
|
||||||
ctx := log.With(context.Background(), log.Str(log.ServiceName, serviceName))
|
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
|
// TODO Should all the services handle healthcheck? Handle different types
|
||||||
service := m.configs[serviceName].LoadBalancer
|
service := m.configs[serviceName].LoadBalancer
|
||||||
|
|
||||||
// Health Check
|
// Health Check
|
||||||
var backendHealthCheck *healthcheck.BackendConfig
|
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)
|
log.FromContext(ctx).Debugf("Setting up healthcheck for service %s with %s", serviceName, *hcOpts)
|
||||||
|
|
||||||
hcOpts.Transport = m.defaultRoundTripper
|
hcOpts.Transport = m.defaultRoundTripper
|
||||||
|
@ -224,7 +225,7 @@ func (m *Manager) LaunchHealthCheck() {
|
||||||
healthcheck.GetHealthCheck().SetBackendsConfiguration(context.Background(), backendConfigs)
|
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 == "" {
|
if hc == nil || hc.Path == "" {
|
||||||
return nil
|
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(
|
err := opentracing.GlobalTracer().Inject(
|
||||||
span.Context(),
|
span.Context(),
|
||||||
opentracing.HTTPHeaders,
|
opentracing.HTTPHeaders,
|
||||||
HTTPHeadersCarrier(r.Header))
|
opentracing.HTTPHeadersCarrier(r.Header))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.FromContext(r.Context()).Error(err)
|
log.FromContext(r.Context()).Error(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,13 @@ import { APP } from '../_helpers/APP'
|
||||||
const apiBase = '/http'
|
const apiBase = '/http'
|
||||||
|
|
||||||
function getAllRouters (params) {
|
function getAllRouters (params) {
|
||||||
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}`)
|
|
||||||
.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}`)
|
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
||||||
.then(body => {
|
.then(body => {
|
||||||
|
const total = body.data ? body.data.length : 0
|
||||||
console.log('Success -> HttpService -> getAllRouters', body.data)
|
console.log('Success -> HttpService -> getAllRouters', body.data)
|
||||||
// TODO - suggestion: add the total-pages in api response to optimize the query
|
// TODO - suggestion: add the total-pages in api response to optimize the query
|
||||||
return { data: body.data || [], total }
|
return { data: body.data || [], total }
|
||||||
})
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRouterByName (name) {
|
function getRouterByName (name) {
|
||||||
|
@ -24,16 +21,13 @@ function getRouterByName (name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAllServices (params) {
|
function getAllServices (params) {
|
||||||
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}`)
|
|
||||||
.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}`)
|
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
||||||
.then(body => {
|
.then(body => {
|
||||||
|
const total = body.data ? body.data.length : 0
|
||||||
console.log('Success -> HttpService -> getAllServices', body.data)
|
console.log('Success -> HttpService -> getAllServices', body.data)
|
||||||
// TODO - suggestion: add the total-pages in api response to optimize the query
|
// TODO - suggestion: add the total-pages in api response to optimize the query
|
||||||
return { data: body.data || [], total }
|
return { data: body.data || [], total }
|
||||||
})
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getServiceByName (name) {
|
function getServiceByName (name) {
|
||||||
|
@ -45,16 +39,13 @@ function getServiceByName (name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAllMiddlewares (params) {
|
function getAllMiddlewares (params) {
|
||||||
return APP.api.get(`${apiBase}/middlewares?search=${params.query}&status=${params.status}`)
|
|
||||||
.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}`)
|
return APP.api.get(`${apiBase}/middlewares?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
||||||
.then(body => {
|
.then(body => {
|
||||||
|
const total = body.data ? body.data.length : 0
|
||||||
console.log('Success -> HttpService -> getAllMiddlewares', body.data)
|
console.log('Success -> HttpService -> getAllMiddlewares', body.data)
|
||||||
// TODO - suggestion: add the total-pages in api response to optimize the query
|
// TODO - suggestion: add the total-pages in api response to optimize the query
|
||||||
return { data: body.data || [], total }
|
return { data: body.data || [], total }
|
||||||
})
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMiddlewareByName (name) {
|
function getMiddlewareByName (name) {
|
||||||
|
|
|
@ -3,16 +3,13 @@ import { APP } from '../_helpers/APP'
|
||||||
const apiBase = '/tcp'
|
const apiBase = '/tcp'
|
||||||
|
|
||||||
function getAllRouters (params) {
|
function getAllRouters (params) {
|
||||||
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}`)
|
|
||||||
.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}`)
|
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
||||||
.then(body => {
|
.then(body => {
|
||||||
|
const total = body.data ? body.data.length : 0
|
||||||
console.log('Success -> HttpService -> getAllRouters', body.data)
|
console.log('Success -> HttpService -> getAllRouters', body.data)
|
||||||
// TODO - suggestion: add the total-pages in api response to optimize the query
|
// TODO - suggestion: add the total-pages in api response to optimize the query
|
||||||
return { data: body.data || [], total }
|
return { data: body.data || [], total }
|
||||||
})
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRouterByName (name) {
|
function getRouterByName (name) {
|
||||||
|
@ -24,16 +21,13 @@ function getRouterByName (name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAllServices (params) {
|
function getAllServices (params) {
|
||||||
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}`)
|
|
||||||
.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}`)
|
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
|
||||||
.then(body => {
|
.then(body => {
|
||||||
|
const total = body.data ? body.data.length : 0
|
||||||
console.log('Success -> HttpService -> getAllServices', body.data)
|
console.log('Success -> HttpService -> getAllServices', body.data)
|
||||||
// TODO - suggestion: add the total-pages in api response to optimize the query
|
// TODO - suggestion: add the total-pages in api response to optimize the query
|
||||||
return { data: body.data || [], total }
|
return { data: body.data || [], total }
|
||||||
})
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getServiceByName (name) {
|
function getServiceByName (name) {
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<div class="text-subtitle2">RULE</div>
|
<div class="text-subtitle2">RULE</div>
|
||||||
<q-chip
|
<q-chip
|
||||||
dense
|
dense
|
||||||
class="app-chip app-chip-rule">
|
class="app-chip app-chip-wrap app-chip-rule">
|
||||||
{{ data.rule }}
|
{{ data.rule }}
|
||||||
</q-chip>
|
</q-chip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
<div class="text-subtitle2">NAME</div>
|
<div class="text-subtitle2">NAME</div>
|
||||||
<q-chip
|
<q-chip
|
||||||
dense
|
dense
|
||||||
class="app-chip app-chip-name">
|
class="app-chip app-chip-wrap app-chip-name">
|
||||||
{{ data.name }}
|
{{ data.name }}
|
||||||
</q-chip>
|
</q-chip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -66,7 +66,7 @@
|
||||||
dense
|
dense
|
||||||
clickable
|
clickable
|
||||||
@click.native="$router.push({ path: `/${protocol}/services/${getServiceId()}`})"
|
@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 }}
|
{{ data.service }}
|
||||||
</q-chip>
|
</q-chip>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</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="row items-start no-wrap">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="text-subtitle2">Pass Host Header</div>
|
<div class="text-subtitle2">Pass Host Header</div>
|
||||||
|
@ -54,6 +54,19 @@
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</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" />
|
<q-separator v-if="sticky" />
|
||||||
<StickyServiceDetails v-if="sticky" :sticky="sticky" :dense="dense"/>
|
<StickyServiceDetails v-if="sticky" :sticky="sticky" :dense="dense"/>
|
||||||
</q-scroll-area>
|
</q-scroll-area>
|
||||||
|
|
Loading…
Reference in a new issue