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
运行 traefik-docker-compose.yml
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)
Traefik 路由到本地 IP 地址
当 url 应该瞄准其他东西而不是 docker 容器。
定义 file provider,添加所需的路由和服务
所需要的是一个能够捕获某些 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
level: INFO
insecure: true
dashboard: true
entryPoints:
address: ":80"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
file:
filename: "traefik.yml"
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 将来自变量。
因此,抬起头来,您肯定会忘记更改这些。
运行 traefik-docker-compose
测试是否有效
docker-compose -f traefik-docker-compose.yml up -d
Traefik 中间件
任何容器的身份验证中间件的示例。
创建一个新文件 users_credentials
包含 username:passwords 对,htpasswd 格式
在下面的示例中,密码 krakatoa
设置为下面所有3个帐户
users_credentials
me:$apr1$L0RIz/oA$Fr7c.2.6R1JXIhCiUI1JF0
admin:$apr1$ELgBQZx3$BFx7a9RIxh1Z0kiJG0juE/
bastard:$apr1$gvhkVK.x$5rxoW.wkw1inm9ZIfB0zs1
挂载 users_credentials
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
路由器上。
第二个标签为该中间件提供了 basicauth 类型,并告诉它用于认证用户的文件在哪里。
无需在此处安装 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
Let's Encrypt 证书,HTTP challenge
我对流程的理解,简化了。
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 天的时间内自动尝试续订。
现在我们来看看该怎么做。
创建一个具有 600 权限的空 acme.json 文件
该文件将存储证书以及有关证书的所有信息。
touch acme.json && chmod 600 acme.json
在 traefik.yml 中添加 443 入口点和证书解析器
在 entrypoint 部分中,新的 entrypoint 被添加为 websecure,端口 443
certificatesResolvers 是一个配置部分,它告诉 traefik 如何使用 acme resolver 获取证书。
certificatesResolvers:
lets-encr:
acme:
storage: acme.json
email: whatever@gmail.com
httpChallenge:
entryPoint: web
解析器的名称为 lets-encr
,并使用 acme
注释掉了 staging caServer 使 LE 颁发了一个 staging 证书,这是一个无效的证书,不会给绿锁,但没有限制,所以很适合测试。如果它在工作,它会说,我们加密。
Storage 告诉在哪里存储给定的证书 - acme.json
邮件是 LE 发送证书过期通知的地方
httpChallenge 有一个入口点,因此 acme 在端口 80 上执行 http challenge
这就是 acme 所需要的一切
traefik.yml
level: INFO
insecure: true
dashboard: true
entryPoints:
address: ":80"
websecure:
address: ":443"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
certificatesResolvers:
lets-encr:
acme:
storage: acme.json
email: whatever@gmail.com
httpChallenge:
entryPoint: web
暴露/映射端口 443 并将 acme.json 挂载在 traefik-docker-compose.yml 中
注意: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
Let's Encrypt 证书在 cloudflare 上 DNS challenge
我对流程的理解,简化了。
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。
现在我们来看看该怎么做。
为所有规划的子域添加 type A DNS 记录
[whoami, nginx, *] 是示例子域,每个子域都应有一个指向 traefik IP 的 A 记录。
创建一个具有 600 权限的空 acme.json 文件
touch acme.json && chmod 600 acme.json
将 443 入口点和证书解析器添加到 traefik.yml
在 entrypoint 部分中,新的 entrypoint 被添加为 websecure,端口 443
certificatesResolvers 是一个配置部分,它告诉 traefik 如何使用 acme resolver 获取证书。
certificatesResolvers:
lets-encr:
acme:
email: whatever@gmail.com
storage: acme.json
dnsChallenge:
provider: cloudflare
resolvers:
- "1.1.1.1:53"
- "8.8.8.8:53"
解析器的名称为 lets-encr
,并使用 acme
注释掉了 staging caServer 使 LE 颁发了一个 staging 证书,这是一个无效的证书,不会给绿锁,但没有限制,所以很适合测试。如果它在工作,它会说,我们加密。
Storage 告诉在哪里存储给定的证书 - acme.json
邮件是 LE 发送证书过期通知的地方
dnsChallenge 是由一个 provider 指定的,
在这个例子中是 cloudflare。每个提供程序在 .env 文件中需要不同名称的环境变量,
但这是稍后的内容,这里只需要提供程序的名称
解析器是在挑战期间使用的知名 DNS 服务器的 IP
traefik.yml
level: INFO
insecure: true
dashboard: true
entryPoints:
address: ":80"
websecure:
address: ":443"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
certificatesResolvers:
lets-encr:
acme:
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 login
CF_API_KEY
- global api key
MY_DOMAIN=example.com
DEFAULT_NETWORK=traefik_net
CF_API_EMAIL=whateverbastard@gmail.com
CF_API_KEY=8d08c87dadb0f8f0e63efe84fb115b62e1abc
暴露/映射端口 443 并将 acme.json 挂载在 traefik-docker-compose.yml 中
注意: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.example.com,域名是从 .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
DNS 挑战的全部重点就是获取通配符!
因此,对于通配符,这些标签将加入 traefik compose。
与以前一样使用相同的 lets-encr
证书解析器,它在 traefik.yml 中定义
子域(*.example.com)的通配符被设置为要获取证书的主域
裸域(只是简单的example.com)设置为sans(主题备用名称)
同样,您确实需要 * .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 停止使用 https 设置,最好将 http(80) 重定向到 https(443)。
Traefik 有专门的中间件 — redirectscheme。
当 traefik.yml
本身设置为文件提供程序时,可以在动态部分的 traefik.yml
中的多个位置声明此重定向。
或在任何正在运行的容器中使用标签,此示例在 traefik compose 中进行操作。
使用 traefik 中的标签添加新路由和重定向方案
- "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"
- "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"
- "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
我是为少。
微信:uuhells123。
公众号:黑客下午茶。
谢谢点赞支持👍👍👍!