如何在Ubuntu20.04上使用Traefikv2作为Docker容器的反向代理
作为 Write for DOnations 计划的一部分,作者选择了 Girls Who Code 来接受捐赠。
介绍
Docker 可以是在生产环境中运行 Web 应用程序的有效方式,但您可能希望在同一个 Docker 主机上运行多个应用程序。 在这种情况下,您需要设置反向代理。 这是因为您只想向世界其他地方公开端口 80
和 443
。
Traefik 是一个 Docker 感知反向代理,包括一个监控仪表板。 Traefik v1 已经被广泛使用了一段时间,你可以按照这个早期的教程安装 Traefik v1)。 但在本教程中,您将安装和配置 Traefik v2,其中包含很多不同之处。
Traefik v1 和 v2 之间最大的区别是 前端 和 后端 被删除,它们的组合功能分布在 路由器 、 中间件 、和 服务 。 以前,后端负责对请求进行修改并将该请求发送到应该处理它的任何地方。 Traefik v2 通过引入可以在将请求发送到服务之前修改请求的中间件来提供更多的关注点分离。 中间件可以更容易地指定可能被许多不同路由使用的单个修改步骤,以便它们可以被重用(例如 HTTP Basic Auth,您将在后面看到)。 路由器还可以使用许多不同的中间件。
在本教程中,您将配置 Traefik v2 以将请求路由到两个不同的 Web 应用程序容器:一个 Wordpress 容器和一个 Adminer 容器,每个容器都与一个 MySQL 通信数据库。 您将使用 Let's Encrypt 将 Traefik 配置为通过 HTTPS 提供所有服务。
先决条件
要完成本教程,您将需要以下内容:
- 一台 Ubuntu 20.04 服务器,带有 sudo 非 root 用户和防火墙。 您可以按照我们的 Ubuntu 20.04 初始服务器设置指南 进行设置。
- Docker 安装在您的服务器上,您可以按照 如何在 Ubuntu 20.04 上安装和使用 Docker 的 Steps 1 和 2 来完成。
- 使用 如何在 Ubuntu 20.04 上安装 Docker Compose 的 Step 1 中的说明安装 Docker Compose。
- 一个域和三个 A 记录,
db-admin.your_domain
、blog.your_domain
和monitor.your_domain
。 每个都应指向您服务器的 IP 地址。 您可以通过阅读 DigitalOcean 的域和 DNS 文档 了解如何将域指向 DigitalOcean Droplets。 在本教程中,将您的域替换为配置文件和示例中的your_domain
。
第 1 步 — 配置和运行 Traefik
Traefik 项目有一个 官方 Docker 镜像,因此您将使用它在 Docker 容器中运行 Traefik。
但是在启动并运行 Traefik 容器之前,您需要创建一个配置文件并设置一个加密密码,以便您可以访问监控仪表板。
您将使用 htpasswd
实用程序来创建此加密密码。 首先,安装实用程序,它包含在 apache2-utils
包中:
sudo apt-get install apache2-utils
然后使用 htpasswd
生成密码。 将 secure_password
替换为您希望用于 Traefik 管理员用户的密码:
htpasswd -nb admin secure_password
程序的输出将如下所示:
Outputadmin:$apr1$ruca84Hq$mbjdMZBAG.KWn7vfN/SNK/
您将在 Traefik 配置文件中使用此输出来为 Traefik 运行状况检查和监控仪表板设置 HTTP 基本身份验证。 复制整个输出行,以便以后粘贴。
要配置 Traefik 服务器,您将使用 TOML 格式创建两个名为 traefik.toml
和 traefik_dynamic.toml
的新配置文件。 TOML 是一种类似于 INI 文件的配置语言,但已标准化。 这些文件让我们配置 Traefik 服务器和您想要使用的各种集成 或 providers
。 在本教程中,您将使用 Traefik 的三个可用提供程序:api
、docker
和 acme
。 最后一个,acme
,支持使用 Let's Encrypt 的 TLS 证书。
使用 nano
或您喜欢的文本编辑器创建并打开 traefik.toml
:
nano traefik.toml
首先,您要使用配置文件的 entryPoints
部分指定 Traefik 应侦听的端口。 你想要两个,因为你想监听端口 80
和 443
。 我们称它们为 web
(端口 80
)和 websecure
(端口 443
)。
添加以下配置:
traefik.toml
[entryPoints] [entryPoints.web] address = ":80" [entryPoints.web.http.redirections.entryPoint] to = "websecure" scheme = "https" [entryPoints.websecure] address = ":443"
请注意,您还会自动重定向要通过 TLS 处理的流量。
接下来,配置 Traefik api
,让您可以访问 API 和仪表板界面。 [api]
的标题就是您所需要的,因为仪表板随后默认启用,但您暂时要明确。
添加以下代码:
traefik.toml
... [api] dashboard = true
要完成保护您的 Web 请求,您需要使用 Let's Encrypt 生成有效的 TLS 证书。 Traefik v2 支持 Let's Encrypt 开箱即用,您可以通过创建 acme
类型的 证书解析器 来配置它。
现在让我们使用名称 lets-encrypt
配置您的证书解析器:
traefik.toml
... [certificatesResolvers.lets-encrypt.acme] email = "your_email@your_domain" storage = "acme.json" [certificatesResolvers.lets-encrypt.acme.tlsChallenge]
此部分称为 acme
,因为 ACME 是用于与 Let's Encrypt 通信以管理证书的协议名称。 Let's Encrypt 服务需要使用有效的电子邮件地址进行注册,因此要让 Traefik 为您的主机生成证书,请将 email
密钥设置为您的电子邮件地址。 然后指定将从 Let's Encrypt 收到的信息存储在名为 acme.json
的 JSON 文件中。
acme.tlsChallenge
部分允许我们指定 Let's Encrypt 如何验证证书。 您正在将其配置为通过端口 443
提供文件作为挑战的一部分。
最后,您需要配置 Traefik 以使用 Docker。
添加以下配置:
traefik.toml
... [providers.docker] watch = true network = "web"
docker
提供程序使 Traefik 能够在 Docker 容器前充当代理。 您已将提供程序配置为 watch
用于 web
网络上的新容器,您将很快创建该网络。
我们的最终配置使用 file
提供程序。 使用 Traefik v2,静态和动态配置无法混合搭配。 为了解决这个问题,您将使用 traefik.toml
来定义您的静态配置,然后将您的动态配置保存在另一个文件中,您将称之为 traefik_dynamic.toml
。 在这里,您使用 file
提供程序来告诉 Traefik 它应该从不同的文件中读取动态配置。
添加以下 file
提供程序:
traefik.toml
[providers.file] filename = "traefik_dynamic.toml"
您完成的 traefik.toml
将如下所示:
traefik.toml
[entryPoints] [entryPoints.web] address = ":80" [entryPoints.web.http.redirections.entryPoint] to = "websecure" scheme = "https" [entryPoints.websecure] address = ":443" [api] dashboard = true [certificatesResolvers.lets-encrypt.acme] email = "your_email@your_domain" storage = "acme.json" [certificatesResolvers.lets-encrypt.acme.tlsChallenge] [providers.docker] watch = true network = "web" [providers.file] filename = "traefik_dynamic.toml"
保存并关闭文件。
现在让我们创建 traefik_dynamic.toml
。
您需要保存在自己的文件中的动态配置值是 middlewares 和 routers。 要将仪表板放在密码后面,您需要自定义 API 的 路由器 并配置 中间件 来处理 HTTP 基本身份验证。 让我们从设置中间件开始。
中间件是基于每个协议配置的,由于您使用的是 HTTP,因此您将其指定为链接到 http.middlewares
的部分。 接下来是中间件的名称,以便您以后可以引用它,然后是中间件的类型,在本例中为 basicAuth
。 让我们称您的中间件为 simpleAuth
。
创建并打开一个名为 traefik_dynamic.toml
的新文件:
nano traefik_dynamic.toml
添加以下代码。 您将在此处粘贴 htpasswd
命令的输出:
traefik_dynamic.toml
[http.middlewares.simpleAuth.basicAuth] users = [ "admin:$apr1$ruca84Hq$mbjdMZBAG.KWn7vfN/SNK/" ]
要为 api 配置路由器,您将再次链接协议名称,但不是使用 http.middlewares
,而是使用 http.routers
后跟路由器名称。 在这种情况下,api
提供了自己的命名路由器,您可以使用 [http.routers.api]
部分对其进行配置。 您还可以通过使用主机匹配设置 rule
键、使用 websecure
的入口点以及包括 [ 的中间件来配置计划与仪表板一起使用的域X193X]。
添加以下配置:
traefik_dynamic.toml
... [http.routers.api] rule = "Host(`monitor.your_domain`)" entrypoints = ["websecure"] middlewares = ["simpleAuth"] service = "api@internal" [http.routers.api.tls] certResolver = "lets-encrypt"
web
入口点处理端口 80
,而 websecure
入口点使用端口 443
进行 TLS/SSL。 您自动将端口 80
上的所有流量重定向到 websecure
入口点,以强制所有请求的安全连接。
注意这里的最后三行配置 service,启用 tls,并将 certResolver
配置为 "lets-encrypt"
。 服务是确定最终在何处处理请求的最后一步。 api@internal
服务是一个内置服务,位于您公开的 API 后面。 就像路由器和中间件一样,服务可以在这个文件中配置,但你不需要这样做来达到你想要的结果。
您完成的 traefik_dynamic.toml
文件将如下所示:
traefik_dynamic.toml
[http.middlewares.simpleAuth.basicAuth] users = [ "admin:$apr1$ruca84Hq$mbjdMZBAG.KWn7vfN/SNK/" ] [http.routers.api] rule = "Host(`monitor.your_domain`)" entrypoints = ["websecure"] middlewares = ["simpleAuth"] service = "api@internal" [http.routers.api.tls] certResolver = "lets-encrypt"
保存文件并退出编辑器。
有了这些配置,您现在将启动 Traefik。
第 2 步 - 运行 Traefik 容器
在这一步中,您将为代理创建一个 Docker 网络以与容器共享。 然后,您将访问 Traefik 仪表板。 Docker 网络是必需的,以便您可以将它与使用 Docker Compose 运行的应用程序一起使用。
创建一个名为 web
的新 Docker 网络:
docker network create web
Traefik 容器启动时,您会将其添加到此网络。 然后,您可以稍后将其他容器添加到此网络,以供 Traefik 代理。
接下来,创建一个空文件来保存您的 Let's Encrypt 信息。 您将把它分享到容器中,以便 Traefik 可以使用它:
touch acme.json
Traefik 只有在容器内的 root 用户对它具有唯一的读写权限时才能使用这个文件。 为此,请锁定 acme.json
上的权限,以便只有文件的所有者具有读取和写入权限。
chmod 600 acme.json
一旦文件被传递给 Docker,所有者将自动更改为容器内的 root 用户。
最后,使用以下命令创建 Traefik 容器:
docker run -d \ -v /var/run/docker.sock:/var/run/docker.sock \ -v $PWD/traefik.toml:/traefik.toml \ -v $PWD/traefik_dynamic.toml:/traefik_dynamic.toml \ -v $PWD/acme.json:/acme.json \ -p 80:80 \ -p 443:443 \ --network web \ --name traefik \ traefik:v2.2
这个命令有点长。 让我们分解一下。
您使用 -d
标志在后台将容器作为守护进程运行。 然后,您将 docker.sock
文件共享到容器中,以便 Traefik 进程可以侦听容器的更改。 您还将 traefik.toml
和 traefik_dynamic.toml
配置文件以及 acme.json
共享到容器中。
接下来,将 Docker 主机的端口 :80
和 :443
映射到 Traefik 容器中的相同端口,以便 Traefik 接收到服务器的所有 HTTP 和 HTTPS 流量。
您将容器的网络设置为 web
,并将容器命名为 traefik
。
最后,您为此容器使用 traefik:v2.2
映像,这样您就可以保证您运行的版本与本教程所针对的版本完全不同。
Docker 镜像的 ENTRYPOINT 是从镜像创建容器时始终运行的命令。 在这种情况下,命令是容器内的 traefik
二进制文件。 您可以在启动容器时向该命令传递其他参数,但您已在 traefik.toml
文件中配置了所有设置。
容器启动后,您现在可以访问仪表板来查看容器的运行状况。 您还可以使用此仪表板来可视化 Traefik 已注册的路由器、服务和中间件。 您可以尝试通过将浏览器指向 https://monitor.your_domain/dashboard/
来访问监控仪表板(需要结尾的 /
)。
系统将提示您输入用户名和密码,即 admin 和您在步骤 1 中配置的密码。
登录后,您将看到 Traefik 界面:
您会注意到已经注册了一些路由器和服务,但这些是 Traefik 随附的以及您为 API 编写的路由器配置。
现在,您的 Traefik 代理正在运行,并且您已将其配置为与 Docker 一起使用并监控其他容器。 在下一步中,您将启动一些容器供 Traefik 进行代理。
第三步——用 Traefik 注册容器
随着 Traefik 容器的运行,您就可以在它后面运行应用程序了。 让我们在 Traefik 后面启动以下容器:
- 使用官方WordPress图片的博客。
- 使用官方Adminer镜像的数据库管理服务器。
您将使用 docker-compose.yml
文件通过 Docker Compose 管理这两个应用程序。
在编辑器中创建并打开 docker-compose.yml
文件:
nano docker-compose.yml
将以下行添加到文件中以指定您将使用的版本和网络:
码头工人-compose.yml
version: "3" networks: web: external: true internal: external: false
您使用 Docker Compose 版本 3
因为它是 Compose 文件格式的最新主要版本。
为了让 Traefik 识别您的应用程序,它们必须属于同一网络,并且由于您手动创建了网络,因此您可以通过指定 web
的网络名称并将 external
设置为 [ X211X]。 然后定义另一个网络,以便可以将暴露的容器连接到不会通过 Traefik 暴露的数据库容器。 您将调用此网络 internal
。
接下来,您将定义每个 services
,一次一个。 让我们从 blog
容器开始,您将基于官方 WordPress 图像。 将此配置添加到文件底部:
码头工人-compose.yml
... services: blog: image: wordpress:4.9.8-apache environment: WORDPRESS_DB_PASSWORD: labels: - traefik.http.routers.blog.rule=Host(`blog.your_domain`) - traefik.http.routers.blog.tls=true - traefik.http.routers.blog.tls.certresolver=lets-encrypt - traefik.port=80 networks: - internal - web depends_on: - mysql
environment
键允许您指定将在容器内设置的环境变量。 通过不为 WORDPRESS_DB_PASSWORD
设置值,您是在告诉 Docker Compose 从您的 shell 中获取该值并在您创建容器时传递它。 在启动容器之前,您将在 shell 中定义此环境变量。 这样您就不会将密码硬编码到配置文件中。
labels
部分是您为 Traefik 指定配置值的地方。 Docker 标签本身不会做任何事情,但 Traefik 会读取这些标签,因此它知道如何处理容器。 以下是每个标签的作用:
traefik.http.routers.adminer.rule=Host(```
blog.your_domain
```)
creates a new 路由器 for your container and then specifies the routing rule used to determine if a request matches this container.traefik.routers.custom_name.tls=true
指定此路由器应使用 TLS。traefik.routers.custom_name.tls.certResolver=lets-encrypt
指定应使用您之前创建的名为lets-encrypt
的证书解析器来获取此路由的证书。traefik.port
指定 Traefik 应该用来将流量路由到此容器的公开端口。
使用此配置,在 blog.your_domain
域的 80
或 443
端口上发送到 Docker 主机的所有流量都将路由到 blog
容器。
您将此容器分配给两个不同的网络,以便 Traefik 可以通过 web
网络找到它,并且它可以通过 internal
网络与数据库容器通信。
最后,depends_on
键告诉 Docker Compose,该容器需要在 其依赖项运行后启动 。 由于 WordPress 需要数据库才能运行,因此您必须在启动 blog
容器之前运行 mysql
容器。
接下来,配置 MySQL 服务:
码头工人-compose.yml
services: ... mysql: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: networks: - internal labels: - traefik.enable=false
您正在为此容器使用官方 MySQL 5.7 映像。 您会注意到您再次使用没有值的 environment
项目。 MYSQL_ROOT_PASSWORD
和 WORDPRESS_DB_PASSWORD
变量需要设置为相同的值,以确保您的 WordPress 容器可以与 MySQL 通信。 您不想将 mysql
容器暴露给 Traefik 或外部世界,因此您只需将此容器分配给 internal
网络。 由于 Traefik 可以访问 Docker 套接字,默认情况下该进程仍会为 mysql
容器公开路由器,因此您将添加标签 traefik.enable=false
以指定 Traefik 不应公开此容器.
最后,定义Adminer容器:
码头工人-compose.yml
services: ... adminer: image: adminer:4.6.3-standalone labels: - traefik.http.routers.adminer.rule=Host(`db-admin.your_domain`) - traefik.http.routers.adminer.tls=true - traefik.http.routers.adminer.tls.certresolver=lets-encrypt - traefik.port=8080 networks: - internal - web depends_on: - mysql
该容器基于官方的 Adminer 镜像。 此容器的 network
和 depends_on
配置与您用于 blog
容器的配置完全匹配。
traefik.http.routers.adminer.rule=Host(```
db-admin.your_domain
```)
行告诉 Traefik 检查请求的主机。 如果它匹配 db-admin.your_domain
的模式,Traefik 将通过端口 8080
将流量路由到 adminer
容器。
您完成的 docker-compose.yml
文件将如下所示:
码头工人-compose.yml
version: "3" networks: web: external: true internal: external: false services: blog: image: wordpress:4.9.8-apache environment: WORDPRESS_DB_PASSWORD: labels: - traefik.http.routers.blog.rule=Host(`blog.your_domain`) - traefik.http.routers.blog.tls=true - traefik.http.routers.blog.tls.certresolver=lets-encrypt - traefik.port=80 networks: - internal - web depends_on: - mysql mysql: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: networks: - internal labels: - traefik.enable=false adminer: image: adminer:4.6.3-standalone labels: labels: - traefik.http.routers.adminer.rule=Host(`db-admin.your_domain`) - traefik.http.routers.adminer.tls=true - traefik.http.routers.adminer.tls.certresolver=lets-encrypt - traefik.port=8080 networks: - internal - web depends_on: - mysql
保存文件并退出文本编辑器。
接下来,在 shell 中为 WORDPRESS_DB_PASSWORD
和 MYSQL_ROOT_PASSWORD
变量设置值:
export WORDPRESS_DB_PASSWORD=secure_database_password export MYSQL_ROOT_PASSWORD=secure_database_password
用您想要的数据库密码替换 secure_database_password
。 请记住对 WORDPRESS_DB_PASSWORD
和 MYSQL_ROOT_PASSWORD
使用相同的密码。
设置这些变量后,使用 docker-compose
运行容器:
docker-compose up -d
现在在填充时观看 Traefik 管理仪表板。
如果您浏览 Routers 部分,您会发现配置了 TLS 的 adminer
和 blog
路由器:
导航到 blog.your_domain
,将 your_domain
替换为您的域。 您将被重定向到 TLS 连接,您现在可以完成 WordPress 设置:
现在通过在浏览器中访问 db-admin.your_domain
来访问 Adminer,再次将 your_domain
替换为您的域。 mysql
容器不暴露给外界,但 adminer
容器可以通过他们使用 [X172X 共享的 internal
Docker 网络访问它] 容器名称作为主机名。
在管理员登录屏幕上,为 用户名 输入 root
,为 Server 输入 mysql
,然后输入您为 MYSQL_ROOT_PASSWORD
设置的值] 用于 密码 。 将 Database 留空。 现在按登录。
登录后,您将看到管理员用户界面。
两个站点现在都在工作,您可以使用 monitor.your_domain
的仪表板来关注您的应用程序。
结论
在本教程中,您将 Traefik v2 配置为将请求代理到 Docker 容器中的其他应用程序。
Traefik 在应用程序容器级别的声明式配置使得配置更多服务变得容易,并且当您向代理流量添加新应用程序时无需重新启动 traefik
容器,因为 Traefik 通过 Docker 套接字文件立即注意到更改是监控。
要了解更多关于 Traefik v2 可以做什么的信息,请前往 官方 Traefik 文档 。