example.com
cloudflare
管理域名的 DNS80/443
已打开docker network create traefik_net
Traefik 和 containers(容器)需要在同一网络上。Compose 会自动创建一个,但事实是隐藏的,以后可能会发生混乱。最好仅创建自己的网络并将其设置为每个 compose 文件中的默认网络。
使用 docker network inspect traefik_net
查看连接到该网络的容器
该文件包含所谓的静态 traefik 配置。
在此基本示例中,只有很少的关于 settings(设置)的解释。
因为 exposedbydefault 被设置为 false,标签 "traefik.enable=true"
将需要用于应该由 traefik 路由的容器。
这个文件将通过 bind mount 传递给 docker 容器,这将在我们为 traefik 使用 docker-compose.yml 时完成。
traefik.yml
## STATIC CONFIGURATION
log:
level: INFO
api:
insecure: true
dashboard: true
entryPoints:
web:
address: ":80"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
稍后,当 traefik 容器运行时,使用命令 docker logs traefik
, 检查是否有提示:"Configuration loaded from file: /traefik.yml"
。您不想成为对 traefik.yml 进行更改的 moron
,并且它什么也不做,因为实际上并未使用该文件。
.env
它包含一些环境变量。
域名,API密钥,IP地址,密码…… 无论是针对一种情况的特定情况,还是针对另一种情况的不同情况,所有这些都理想地放在了这里。运行 docker-compose up
命令时,这些变量可用于 docker-compose。
这使得编写文件可以更自由地从一个系统移动到另一个系统,并且可以对 .env
文件进行更改, 因此,在大型编写文件或类似文件中忘记更改某些主机规则中的域名的错误可能性更小。
.env
MY_DOMAIN=example.com
DEFAULT_NETWORK=traefik_net
命令 docker-compose config
显示了填充变量后的 compose 状态。
这些变量仅在容器的初始构建期间填写。如果一个 env 变量也应该在运行的容器中可用,则需要在 compose 文件的 environment
部分声明它。
这是一个简单的典型 compose 文件。
端口 80 已映射,因为我们希望 traefik 负责端口上的内容-将其用作 entrypoint(入口点)。端口 8080 用于 traefik 显示信息的仪表板。需要挂载 docker.sock,因此它实际上可以完成与 docker 交互的工作。 traefik.yml
的挂载是给出静态 traefik 配置的原因。默认网络被设置为第一步中创建的网络,因为它将在所有其他 compose 文件中设置。
traefik-docker-compose.yml
version: "3.7"
services:
traefik:
image: "traefik:v2.1"
container_name: "traefik"
hostname: "traefik"
ports:
- "80:80"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./traefik.yml:/traefik.yml:ro"
networks:
default:
external:
name: $DEFAULT_NETWORK
docker-compose -f traefik-docker-compose.yml up -d
将会启动 traefik 容器。
traefik 正在运行,您可以在 ip:8080 上检查它,在那里您可以得到仪表板。
也可以用 docker logs traefik
检查日志。
通常你会看到指南只有一个名为 docker-compose.yml
文件。其中包含多个服务/容器。然后只需 docker-compose up -d
就可以开始了。当所有都是一个 compose 时,你甚至不需要费心去定义网络。但这一次,我更喜欢在学习新东西时采取小而独立的步骤。这就是为什么使用自定义命名的 docker-compose 文件,因为它允许更容易的分离。
您在教程中还可以看到没有提及 traefik.yml,而东西只是使用 traefik 的命令或标签从 docker-compose 传递的。
如: command: --api.insecure=true --providers.docker
但是这样一来,组合文件看起来会更加混乱,您仍然无法从那里进行任何操作,有时您仍然需要 traefik.yml。
所以...现在,使用结构良好的可读 traefik.yml
在 traefik 应该路由的容器上添加标签
以下是 whoami,nginx,apache,portainer 的示例。
- "traefik.enable=true"
启用 traefik
- "traefik.http.routers.whoami.entrypoints=web"
定义名为 whoami
的路由,该路由在入口点 Web(端口80)上侦听
- "traefik.http.routers.whoami.rule=Host(
whoami.$MY_DOMAIN
)"
定义此 whoami
路由规则,特别是当 url 等于 whoami.example.com
(域名来自 .env
文件)时, 这意味着路由可以完成其工作并将其路由到服务。
不需要其他任何东西,traefik 可以从这些标签来自 docker 容器的上下文中了解其余信息。
whoami-docker-compose.yml
version: "3.7"
services:
whoami:
image: "containous/whoami"
container_name: "whoami"
hostname: "whoami"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.entrypoints=web"
- "traefik.http.routers.whoami.rule=Host(`whoami.$MY_DOMAIN`)"
networks:
default:
external:
name: $DEFAULT_NETWORK
nginx-docker-compose.yml
version: "3.7"
services:
nginx:
image: nginx:latest
container_name: nginx
hostname: nginx
labels:
- "traefik.enable=true"
- "traefik.http.routers.nginx.entrypoints=web"
- "traefik.http.routers.nginx.rule=Host(`nginx.$MY_DOMAIN`)"
networks:
default:
external:
name: $DEFAULT_NETWORK
apache-docker-compose.yml
version: "3.7"
services:
apache:
image: httpd:latest
container_name: apache
hostname: apache
labels:
- "traefik.enable=true"
- "traefik.http.routers.apache.entrypoints=web"
- "traefik.http.routers.apache.rule=Host(`apache.$MY_DOMAIN`)"
networks:
default:
external:
name: $DEFAULT_NETWORK
portainer-docker-compose.yml
version: "3.7"
services:
portainer:
image: portainer/portainer
container_name: portainer
hostname: portainer
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- portainer_data:/data
labels:
- "traefik.enable=true"
- "traefik.http.routers.portainer.entrypoints=web"
- "traefik.http.routers.portainer.rule=Host(`portainer.$MY_DOMAIN`)"
networks:
default:
external:
name: $DEFAULT_NETWORK
volumes:
portainer_data:
docker-compose -f whoami-docker-compose.yml up -d
docker-compose -f nginx-docker-compose.yml up -d
docker-compose -f apache-docker-compose.yml up -d
docker-compose -f portainer-docker-compose.yml up -d
停止所有容器运行:docker stop $(docker ps -q)
当 url 应该瞄准其他东西而不是 docker 容器。
所需要的是一个能够捕获某些 url 并将其路由到某些 IP 的 router。前面的示例显示了如何在端口 80 上捕获任何 url,但是没有人告诉它在符合规则的情况下该怎么做。Traefik 只是知道,因为它是在容器的上下文中使用标签完成的, 而且由于 docker 在 traefik.yml
中被设置为提供程序。
对于这种 “在某些 IP 上发送流量”,需要 traefik 服务, 并且要定义 traefik 服务,需要一个新的提供程序, 即 file provider - 只是一个(fucking stupid)文件,告诉 traefik 该怎么做。
某种常见的做法是将 traefik.yml
本身设置为 file provider,以便进行处理。
在提供者下有一个新的 file
部分,并设置了 traefik.yml
本身。
然后添加动态配置的东西。
一个名为 route-to-local-ip
的 router,有一个简单的子域主机名规则。符合该规则(在本例中为确切的网址 test.example.com
)的内容将发送到 loadbalancer 服务, 该服务会将其路由到特定的 IP 和特定的端口。
traefik.yml
## STATIC CONFIGURATION
log:
level: INFO
api:
insecure: true
dashboard: true
entryPoints:
web:
address: ":80"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
file:
filename: "traefik.yml"
## DYNAMIC CONFIGURATION
http:
routers:
route-to-local-ip:
rule: "Host(`test.example.com`)"
service: route-to-local-ip-service
priority: 1000
entryPoints:
- web
services:
route-to-local-ip-service:
loadBalancer:
servers:
- url: "http://10.0.19.5:80"
路由的优先级设置为 1000,这是一个非常高的值,超过了任何其他可能的路由。
不幸的是 .env
变量在这里不起作用,否则主机规则中的域名和 IP 将来自变量。因此,抬起头来,您肯定会忘记更改这些。
测试是否有效
docker-compose -f traefik-docker-compose.yml up -d
任何容器的身份验证中间件的示例。
包含 username:passwords 对,htpasswd 格式
在下面的示例中,密码 krakatoa
设置为下面所有3个帐户
users_credentials
me:$apr1$L0RIz/oA$Fr7c.2.6R1JXIhCiUI1JF0
admin:$apr1$ELgBQZx3$BFx7a9RIxh1Z0kiJG0juE/
bastard:$apr1$gvhkVK.x$5rxoW.wkw1inm9ZIfB0zs1
traefik-docker-compose.yml
version: "3.7"
services:
traefik:
image: "traefik:v2.1"
container_name: "traefik"
hostname: "traefik"
ports:
- "80:80"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./traefik.yml:/traefik.yml:ro"
- "./users_credentials:/users_credentials:ro"
networks:
default:
external:
name: $DEFAULT_NETWORK
auth-middleware
的新中间件附加到已存在的 whoami
路由器上。无需在此处安装 users_credentials
,它是 traefik 所需的文件, 这些标签是一种将信息传递给 traefik 的方式,它应该在容器的上下文中进行操作。
whoami-docker-compose.yml
version: "3.7"
services:
whoami:
image: "containous/whoami"
container_name: "whoami"
hostname: "whoami"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.entrypoints=web"
- "traefik.http.routers.whoami.rule=Host(`whoami.$MY_DOMAIN`)"
- "traefik.http.routers.whoami.middlewares=auth-middleware"
- "traefik.http.middlewares.auth-middleware.basicauth.usersfile=/users_credentials"
networks:
default:
external:
name: $DEFAULT_NETWORK
nginx-docker-compose.yml
version: "3.7"
services:
nginx:
image: nginx:latest
container_name: nginx
hostname: nginx
labels:
- "traefik.enable=true"
- "traefik.http.routers.nginx.entrypoints=web"
- "traefik.http.routers.nginx.rule=Host(`nginx.$MY_DOMAIN`)"
- "traefik.http.routers.nginx.middlewares=auth-middleware"
- "traefik.http.middlewares.auth-middleware.basicauth.usersfile=/users_credentials"
networks:
default:
external:
name: $DEFAULT_NETWORK
docker-compose -f traefik-docker-compose.yml up -d
docker-compose -f whoami-docker-compose.yml up -d
docker-compose -f nginx-docker-compose.yml up -d
LE
- Let's Encrypt. 提供免费证书的服务Certificate
- 存储在服务器文件中的加密密钥,允许进行加密通信并确认身份ACME
- 一种协议(精确商定的通信方式),以协商来自 LE 的证书。它是 traefik 的一部分。DNS
- 互联网上的服务器,将域名转换为 IP 地址Traefik 使用 ACME 向 LE 请求特定域的证书,如 example.com
。LE 用一些随机生成的文本来回答,然后 traefik 把这些文本放在服务器的特定位置。然后,LE 向 DNS 互联网服务器询问 example.com
,结果指向了某个 IP 地址。LE 通过端口 80/443 查找该 IP 地址,查找包含该随机文本的文件。
如果存在,那么这证明了要求证书的人都控制了服务器和域,因为它显示了对 DNS 记录的控制权。证书已颁发,有效期为 3 个月,traefik 将在少于 30 天的时间内自动尝试续订。
现在我们来看看该怎么做。
该文件将存储证书以及有关证书的所有信息。
touch acme.json && chmod 600 acme.json
在 entrypoint 部分中,新的 entrypoint 被添加为 websecure,端口 443
certificatesResolvers 是一个配置部分,它告诉 traefik 如何使用 acme resolver 获取证书。
certificatesResolvers:
lets-encr:
acme:
#caServer: https://acme-staging-v02.api.letsencrypt.org/directory
storage: acme.json
email: whatever@gmail.com
httpChallenge:
entryPoint: web
lets-encr
,并使用 acmeacme.json
这就是 acme 所需要的一切
traefik.yml
## STATIC CONFIGURATION
log:
level: INFO
api:
insecure: true
dashboard: true
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
certificatesResolvers:
lets-encr:
acme:
#caServer: https://acme-staging-v02.api.letsencrypt.org/directory
storage: acme.json
email: whatever@gmail.com
httpChallenge:
entryPoint: web
注意:acme.json 不是 :ro
- 只读
traefik-docker-compose.yml
version: "3.7"
services:
traefik:
image: "traefik:v2.1"
container_name: "traefik"
hostname: "traefik"
env_file:
- .env
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./traefik.yml:/traefik.yml:ro"
- "./acme.json:/acme.json"
networks:
default:
external:
name: $DEFAULT_NETWORK
与第一章中的纯 HTTP 相比,它只是将路由器的入口点从 web
更改为 websecure
, 并将名为 lets-encr
的证书解析器分配给现有路由器
whoami-docker-compose.yml
version: "3.7"
services:
whoami:
image: "containous/whoami"
container_name: "whoami"
hostname: "whoami"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.rule=Host(`whoami.$MY_DOMAIN`)"
- "traefik.http.routers.whoami.tls.certresolver=lets-encr"
networks:
default:
external:
name: $DEFAULT_NETWORK
nginx-docker-compose.yml
version: "3.7"
services:
nginx:
image: nginx:latest
container_name: nginx
hostname: nginx
labels:
- "traefik.enable=true"
- "traefik.http.routers.nginx.entrypoints=websecure"
- "traefik.http.routers.nginx.rule=Host(`nginx.$MY_DOMAIN`)"
- "traefik.http.routers.nginx.tls.certresolver=lets-encr"
networks:
default:
external:
name: $DEFAULT_NETWORK
稍等一分钟
容器现在只能在 https 上工作并且具有 greenlock
检查 acme.json 的内容
如果要重新开始,请删除acme.json
LE
- Let's Encrypt. 提供免费证书的服务Certificate
- 存储在服务器文件中的加密密钥,允许进行加密通信并确认身份ACME
- 一种协议(精确商定的通信方式),以协商来自 LE 的证书。它是 traefik 的一部分。DNS
- 互联网上的服务器,将域名转换为 IP 地址Traefik 使用 ACME 向 LE 请求特定域的证书,如 example.com
。LE 用一些随机生成的文本来回答,然后 traefik 把这些文本放在服务器的特定位置。然后,LE 向 DNS 互联网服务器询问 example.com
,结果指向了某个 IP 地址。LE 通过端口 80/443 查找该 IP 地址,查找包含该随机文本的文件。
如果存在,那么这证明了要求证书的人都控制了服务器和域,因为它显示了对 DNS 记录的控制权。证书已颁发,有效期为 3 个月,traefik 将在少于 30 天的时间内自动尝试续订。
与 httpChallenge 相比的好处是能够使用通配符证书。这些是验证所有子域 *.example.com
的证书 另外,无需打开任何端口。
但 traefik 需要能够对 DNS 记录进行自动更改,因此需要管理网站 DNS 的人对此提供支持。这就是为什么选择 cloudflare。
现在我们来看看该怎么做。为所有规划的子域添加类型 A DNS 记录
[whoami, nginx, *] 是示例子域,每个子域都应有一个指向 traefik IP 的 A 记录。
touch acme.json && chmod 600 acme.json
在 entrypoint 部分中,新的 entrypoint 被添加为 websecure,端口 443
certificatesResolvers 是一个配置部分,它告诉 traefik 如何使用 acme resolver 获取证书。
certificatesResolvers:
lets-encr:
acme:
#caServer: https://acme-staging-v02.api.letsencrypt.org/directory
email: whatever@gmail.com
storage: acme.json
dnsChallenge:
provider: cloudflare
resolvers:
- "1.1.1.1:53"
- "8.8.8.8:53"
lets-encr
,并使用 acmeacme.json
在这个例子中是 cloudflare。每个提供程序在 .env 文件中需要不同名称的环境变量, 但这是稍后的内容,这里只需要提供程序的名称
traefik.yml
## STATIC CONFIGURATION
log:
level: INFO
api:
insecure: true
dashboard: true
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
certificatesResolvers:
lets-encr:
acme:
#caServer: https://acme-staging-v02.api.letsencrypt.org/directory
email: whatever@gmail.com
storage: acme.json
dnsChallenge:
provider: cloudflare
resolvers:
- "1.1.1.1:53"
- "8.8.8.8:53"
.env
文件中添加所需的变量我们知道根据支持的提供商列表添加哪些变量
对于 cloudflare 变量是
CF_API_EMAIL
- cloudflare loginCF_API_KEY
- global api key.env
MY_DOMAIN=example.com
DEFAULT_NETWORK=traefik_net
CF_API_EMAIL=whateverbastard@gmail.com
CF_API_KEY=8d08c87dadb0f8f0e63efe84fb115b62e1abc
注意:acme.json 不是 :ro
- 只读
traefik-docker-compose.yml
version: "3.7"
services:
traefik:
image: "traefik:v2.1"
container_name: "traefik"
hostname: "traefik"
env_file:
- .env
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./traefik.yml:/traefik.yml:ro"
- "./acme.json:/acme.json"
networks:
default:
external:
name: $DEFAULT_NETWORK
与第一章中简单的 http 相比
web
切换到 websecure
lets-encr
的证书解析器.env
文件中提取的whoami-docker-compose.yml
version: "3.7"
services:
whoami:
image: "containous/whoami"
container_name: "whoami"
hostname: "whoami"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.rule=Host(`whoami.$MY_DOMAIN`)"
- "traefik.http.routers.whoami.tls.certresolver=lets-encr"
- "traefik.http.routers.whoami.tls.domains[0].main=whoami.$MY_DOMAIN"
networks:
default:
external:
name: $DEFAULT_NETWORK
nginx-docker-compose.yml
version: "3.7"
services:
nginx:
image: nginx:latest
container_name: nginx
hostname: nginx
labels:
- "traefik.enable=true"
- "traefik.http.routers.nginx.entrypoints=websecure"
- "traefik.http.routers.nginx.rule=Host(`nginx.$MY_DOMAIN`)"
- "traefik.http.routers.nginx.tls.certresolver=lets-encr"
- "traefik.http.routers.nginx.tls.domains[0].main=nginx.$MY_DOMAIN"
networks:
default:
external:
name: $DEFAULT_NETWORK
docker-compose -f traefik-docker-compose.yml up -d
docker-compose -f whoami-docker-compose.yml up -d
docker-compose -f nginx-docker-compose.yml up -d
很公平
因此,对于通配符,这些标签将加入 traefik compose。
lets-encr
证书解析器,它在 traefik.yml 中定义同样,您确实需要 * .example.com
和 example.com
在 DNS 控制面板中设置为 A 记录,指向 traefik 的 IP
traefik-docker-compose.yml
version: "3.7"
services:
traefik:
image: "traefik:v2.1"
container_name: "traefik"
hostname: "traefik"
env_file:
- .env
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./traefik.yml:/traefik.yml:ro"
- "./acme.json:/acme.json"
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.tls.certresolver=lets-encr"
- "traefik.http.routers.traefik.tls.domains[0].main=*.$MY_DOMAIN"
- "traefik.http.routers.traefik.tls.domains[0].sans=$MY_DOMAIN"
networks:
default:
external:
name: $DEFAULT_NETWORK
现在,如果容器想作为子域进行访问,则只需要一个具有 url 规则的常规路由器,位于 443 端口入口点,并使用相同的 lets-encr
证书解析器
whoami-docker-compose.yml
version: "3.7"
services:
whoami:
image: "containous/whoami"
container_name: "whoami"
hostname: "whoami"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.rule=Host(`whoami.$MY_DOMAIN`)"
- "traefik.http.routers.whoami.tls.certresolver=lets-encr"
networks:
default:
external:
name: $DEFAULT_NETWORK
nginx-docker-compose.yml
version: "3.7"
services:
nginx:
image: nginx:latest
container_name: nginx
hostname: nginx
labels:
- "traefik.enable=true"
- "traefik.http.routers.nginx.entrypoints=websecure"
- "traefik.http.routers.nginx.rule=Host(`nginx.$MY_DOMAIN`)"
- "traefik.http.routers.nginx.tls.certresolver=lets-encr"
networks:
default:
external:
name: $DEFAULT_NETWORK
这是apache,但这次运行在裸域 example.com
上
apache-docker-compose.yml
version: "3.7"
services:
apache:
image: httpd:latest
container_name: apache
hostname: apache
labels:
- "traefik.enable=true"
- "traefik.http.routers.apache.entrypoints=websecure"
- "traefik.http.routers.apache.rule=Host(`$MY_DOMAIN`)"
- "traefik.http.routers.apache.tls.certresolver=lets-encr"
networks:
default:
external:
name: $DEFAULT_NETWORK
http 停止使用 https 设置,最好将 http(80) 重定向到 https(443)。
Traefik 有专门的中间件 — redirectscheme。
当 traefik.yml
本身设置为文件提供程序时,可以在动态部分的 traefik.yml
中的多个位置声明此重定向。
或在任何正在运行的容器中使用标签,此示例在 traefik compose 中进行操作。
- "traefik.enable=true"
在这个 traefik 容器上启用 traefik,不是说这里需要到服务的典型路由,而是说没有它其他标签就不能工作
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
创建一个名为 redirect-to-https
的新中间件,输入 “redirectscheme” 并为其分配方案 https
。
- "traefik.http.routers.redirect-https.rule=hostregexp(`{host:.+}`)"
创建一个名为 redirect-https
的新路由,并使用一个正则表达式规则来捕获所有传入请求
- "traefik.http.routers.redirect-https.entrypoints=web"
声明此路由器在哪个入口点上侦听 - Web(端口80)
- "traefik.http.routers.redirect-https.middlewares=redirect-to-https"
将新创建的 redirectscheme 中间件分配给此新创建的路由。
traefik-docker-compose.yml
version: "3.7"
services:
traefik:
image: "traefik:v2.1"
container_name: "traefik"
hostname: "traefik"
env_file:
- .env
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./traefik.yml:/traefik.yml:ro"
- "./acme.json:/acme.json"
labels:
- "traefik.enable=true"
## DNS CHALLENGE
- "traefik.http.routers.traefik.tls.certresolver=lets-encr"
- "traefik.http.routers.traefik.tls.domains[0].main=*.$MY_DOMAIN"
- "traefik.http.routers.traefik.tls.domains[0].sans=$MY_DOMAIN"
## HTTP REDIRECT
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
- "traefik.http.routers.redirect-https.rule=hostregexp(`{host:.+}`)"
- "traefik.http.routers.redirect-https.entrypoints=web"
- "traefik.http.routers.redirect-https.middlewares=redirect-to-https"
networks:
default:
external:
name: $DEFAULT_NETWORK
我是为少。