作为Write for DOnations计划的一部分,作者选择了“编码女孩”来接受捐赠。
介绍
Docker是在生产环境中运行 Web 应用程序的一种有效方式,但您可能希望在同一个 Docker 主机上运行多个应用程序。在这种情况下,您需要设置反向代理。这是因为你只是想揭露港口80
和443
世界其他地区。
Traefik是一个 Docker 感知的反向代理,包括一个监控仪表板。Traefik v1 已经被广泛使用了一段时间,你可以按照这个早期的教程来安装 Traefik v1)。但是在本教程中,您将安装和配置 Traefik v2,其中包含很多不同之处。
Traefik v1 和 v2 之间的最大区别是前端和后端被删除,它们的组合功能分布在路由器、中间件和服务之间。以前,后端负责修改请求并将该请求发送到任何应该处理它的地方。Traefik v2 通过引入可以在将请求发送到服务之前修改请求的中间件来提供更多的关注点分离。中间件可以更轻松地指定可能被许多不同路由使用的单个修改步骤,以便它们可以被重用(例如 HTTP 基本身份验证,您将在后面看到)。路由器还可以使用许多不同的中间件。
在本教程中,您将配置 Traefik v2 以将请求路由到两个不同的 Web 应用程序容器:一个WordPress容器和一个Adminer容器,每个容器都与一个MySQL数据库通信。您将使用Let’s Encrypt将 Traefik 配置为通过 HTTPS 提供所有服务。
先决条件
要完成本教程,您将需要以下内容:
- 一台带有 sudo 非 root 用户和防火墙的Ubuntu 20.04 服务器。您可以按照我们的Ubuntu 20.04 初始服务器设置指南进行设置。
- 安装在服务器上的码头工人,您可以通过以下完成步骤1和2中如何安装和使用码头工人在Ubuntu 20.04。
- 使用如何在 Ubuntu 20.04 上安装 Docker Compose 的步骤 1中的说明安装 Docker Compose。
- 一个域和三个 A 记录 、和。每个都应该指向您服务器的 IP 地址。您可以通过阅读DigitalOcean 的域和 DNS 文档来了解如何将域指向 DigitalOcean Droplets 。在本教程中,在配置文件和示例中替换您的域。
db-admin.your_domain
blog.your_domain
monitor.your_domain
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 服务器,您将创建两个名为traefik.toml
和traefik_dynamic.toml
使用 TOML 格式的新配置文件。TOML是一种类似于 INI 文件的配置语言,但是是标准化的。这些文件让我们配置Traefik服务器和各种集成,或者providers
,你想使用。在本教程中,你将使用三个Traefik的可用提供商:api
,docker
,和acme
。最后一个acme
支持使用 Let’s Encrypt 的 TLS 证书。
traefik.toml
使用nano
或您喜欢的文本编辑器创建和打开:
- nano traefik.toml
首先,您要使用entryPoints
配置文件的部分指定 Traefik 应该侦听的端口。你想要两个是因为你想监听端口80
和443
。让我们称这些为web
(port 80
) 和websecure
(port 443
)。
添加以下配置:
[entryPoints]
[entryPoints.web]
address = ":80"
[entryPoints.web.http.redirections.entryPoint]
to = "websecure"
scheme = "https"
[entryPoints.websecure]
address = ":443"
请注意,您还会自动重定向要通过 TLS 处理的流量。
接下来,配置 Traefik api
,它使您可以访问 API 和仪表板界面。标题[api]
就是你所需要的,因为仪表板默认是启用的,但你暂时是明确的。
添加以下代码:
...
[api]
dashboard = true
要完成保护您要使用 Let’s Encrypt 生成有效 TLS 证书的 Web 请求的请求。Traefik v2 支持开箱即用的 Let’s Encrypt,您可以通过创建类型的证书解析器来配置它acme
。
现在让我们使用 name 配置您的证书解析器lets-encrypt
:
...
[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 收到的信息存储在名为 .json 的 JSON 文件中acme.json
。
该acme.tlsChallenge
部分允许我们指定 Let’s Encrypt 如何验证证书。您正在配置它以提供文件作为端口挑战的一部分443
。
最后,您需要配置 Traefik 以与 Docker 一起使用。
添加以下配置:
...
[providers.docker]
watch = true
network = "web"
该docker
供应商能够Traefik作为在Docker容器的前代理。您已经watch
为web
网络上的新容器配置了提供程序,您将很快创建这些容器。
我们的最终配置使用file
提供程序。使用 Traefik v2,静态和动态配置不能混合和匹配。为了解决这个问题,您将使用traefik.toml
定义您的静态配置,然后将您的动态配置保存在另一个文件中,您将称之为traefik_dynamic.toml
. 在这里,您使用file
提供程序告诉 Traefik 它应该从不同的文件中读取动态配置。
添加以下file
提供程序:
- [providers.file]
- filename = "traefik_dynamic.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
.
您需要在它们自己的文件中保存的动态配置值是中间件和路由器。要将仪表板置于密码之后,您需要自定义 API 的路由器并配置一个中间件来处理 HTTP 基本身份验证。让我们从设置中间件开始。
中间件是在每个协议的基础上配置的,并且由于您使用的是 HTTP,因此您会将其指定为从http.middlewares
. 接下来是您的中间件的名称,以便您以后可以引用它,然后是它的中间件类型,basicAuth
在本例中就是这种类型。让我们调用您的中间件simpleAuth
。
创建并打开一个名为 的新文件traefik_dynamic.toml
:
- nano traefik_dynamic.toml
添加以下代码。您将在此处粘贴htpasswd
命令的输出:
[http.middlewares.simpleAuth.basicAuth]
users = [
"admin:$apr1$ruca84Hq$mbjdMZBAG.KWn7vfN/SNK/"
]
要为 api 配置路由器,您将再次链接协议名称,但不是使用http.middlewares
,而是使用http.routers
后跟路由器名称。在这种情况下,它api
提供了自己的命名路由器,您可以使用该[http.routers.api]
部分进行配置。您还将通过rule
使用主机匹配设置密钥、要使用的入口点websecure
和要包含的中间件来配置您计划与仪表板一起使用的域simpleAuth
。
添加以下配置:
...
[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
入口点以强制所有请求的安全连接。
注意这里的最后三行配置服务、启用 tls 和配置certResolver
为"lets-encrypt"
。服务是确定最终处理请求的位置的最后一步。该api@internal
服务是一个内置服务,位于您公开的 API 之后。就像路由器和中间件一样,可以在此文件中配置服务,但您不需要这样做来实现您想要的结果。
您完成的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 运行的应用程序一起使用。
创建一个名为 的新 Docker 网络web
:
- docker network create web
当 Traefik 容器启动时,您将把它添加到这个网络中。然后你可以稍后向这个网络添加额外的容器,以便 Traefik 代理到。
接下来,创建一个空文件来保存您的 Let’s Encrypt 信息。您将其共享到容器中,以便 Traefik 可以使用它:
- touch acme.json
如果容器内的 root 用户对它具有唯一的读写访问权限,Traefik 将只能使用该文件。为此,请锁定权限,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 代理。
第 3 步 – 使用 Traefik 注册容器
运行 Traefik 容器后,您就可以在其后面运行应用程序了。让我们在 Traefik 后面启动以下容器:
- 使用官方 WordPress 图像的博客。
- 使用官方 Adminer 映像的数据库管理服务器。
您将使用docker-compose.yml
文件通过 Docker Compose 管理这两个应用程序。
docker-compose.yml
在编辑器中创建并打开文件:
- nano docker-compose.yml
将以下行添加到文件中以指定您将使用的版本和网络:
version: "3"
networks:
web:
external: true
internal:
external: false
您使用 Docker Compose 版本,3
因为它是 Compose 文件格式的最新主要版本。
要让 Traefik 识别您的应用程序,它们必须是同一网络的一部分,并且由于您手动创建了网络,因此您可以通过指定网络名称web
和设置external
为true
. 然后定义另一个网络,以便可以将公开的容器连接到不会通过 Traefik 公开的数据库容器。你会称这个网络为internal
。
接下来,您将一次定义每个services
。让我们从blog
容器开始,您将基于官方 WordPress 图像。将此配置添加到文件底部:
...
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
`
)
为您的容器创建一个新路由器,然后指定用于确定请求是否与此容器匹配的路由规则。traefik.routers.custom_name.tls=true
指定此路由器应使用 TLS。traefik.routers.custom_name.tls.certResolver=lets-encrypt
指定lets-encrypt
应使用您之前创建的证书解析程序来获取此路由的证书。traefik.port
指定 Traefik 应该用于将流量路由到此容器的公开端口。
使用此配置,通过端口80
或443
域发送到 Docker 主机的所有流量都将路由到容器。blog.your_domain
blog
你将这个容器分配给两个不同的网络,这样 Traefik 就可以通过web
网络找到它,并且它可以通过网络与数据库容器进行internal
通信。
最后,depends_on
key 告诉 Docker Compose 这个容器需要在它的依赖项运行后启动。由于 WordPress 需要一个数据库才能运行,因此您必须mysql
在启动容器之前运行您的blog
容器。
接下来,配置 MySQL 服务:
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 容器:
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 检查请求的主机。如果它匹配 的模式,Traefik 将通过端口将流量路由到容器。traefik.http.routers.adminer.rule=Host(
`
db-admin.your_domain
`
)
db-admin.your_domain
adminer
8080
您完成的docker-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 管理仪表板填充时观看它。
如果您探索路由器部分你会发现路由器adminer
和blog
使用TLS配置:
导航到,替换为您的域。您将被重定向到 TLS 连接,您现在可以完成 WordPress 设置:blog.your_domain
your_domain
现在通过在浏览器中访问来访问 Adminer ,再次替换为您的域。该容器未暴露于外界,但集装箱具有通过访问它多克尔网络,他们共享使用容器名称作为主机名。db-admin.your_domain
your_domain
mysql
adminer
internal
mysql
在Adminer登录界面,输入root
了用户名,进入mysql
了服务器,并输入您设置的值MYSQL_ROOT_PASSWORD
的密码。将数据库留空。现在按登录。
登录后,您将看到管理员用户界面。
这两个站点现在都在运行,您可以使用仪表板来密切关注您的应用程序。monitor.your_domain
结论
在本教程中,您将 Traefik v2 配置为将请求代理到 Docker 容器中的其他应用程序。
Traefik 在应用程序容器级别的声明性配置使得配置更多服务变得容易,并且traefik
当您将新应用程序添加到代理流量时无需重新启动容器,因为 Traefik 会立即通过其监控的 Docker 套接字文件注意到更改。
要了解有关使用 Traefik v2 可以做什么的更多信息,请转到官方 Traefik 文档。