跳至内容

为初学者使用 LXD 构建网站/Web 服务器网络

简介

好的,我们已经有了 关于在 Rocky Linux 上安装 LXD/LXC 的指南,但那是由一个知道自己在做什么的人写的,他想要在他本地网络上的物理机器上构建一个容器化的服务器或应用程序网络。它很棒,我将直接窃取它的部分内容,这样我就不必写那么多。

但是,如果你刚听说 Linux 容器,并且还不了解它们是如何工作的,但你想要托管一些网站,那么本指南适合你。本教程将教你如何在任何系统(包括虚拟专用服务器和云托管)上使用 LXD 和 LXC 托管基本网站。

那么首先,什么是 Linux 容器?对于绝对的初学者来说,它是一种让一台计算机假装成实际上是更多计算机的方式。这些“容器”中的每一个都包含了你选择的某个基本(通常是简化的)版本的系统。你可以将每个容器用作单独的服务器;在一个容器上放置nginx,在另一个容器上放置Apache,甚至使用第三个容器作为数据库服务器。

基本优势在于,如果某个容器内的应用程序或网站遇到严重错误、黑客攻击或其他问题,它不太可能影响服务器的其余部分或其他应用程序和网站。此外,容器非常易于快照、备份和恢复。

在本例中,我们将运行 Rocky Linux,它在我们的“主机”系统之上,这也是 Rocky Linux。

从概念上讲,它类似于以下内容

A graph showing how one computer can pretend to be several

如果你曾经使用过 VirtualBox 来运行一些 Windows 应用程序,它类似于那样,但并不完全相同。与虚拟机不同,Linux 容器不会为每个容器模拟整个硬件环境。相反,它们默认情况下共享一些虚拟设备,用于网络和存储,尽管你可以添加更多虚拟设备。因此,它们所需的开销(处理能力和 RAM)比虚拟机少得多。

对于那些使用 Docker 的人来说(Docker 是另一个基于容器的系统,不是 VM 系统),Linux 容器比你习惯的更持久。每个容器实例中的所有数据都是持久的,你所做的任何更改都是永久性的,除非你恢复到备份。简而言之,关闭容器不会删除你可能引入的任何问题。

具体来说,LXD 是一个命令行应用程序,它可以帮助你设置和管理 Linux 容器。这就是我们今天将在我们的 Rocky Linux 主机服务器上安装的内容。不过,我将经常提及 LXC/LXD,因为有很多旧文档只提到了 LXC,我试图让用户更容易找到像这样更新的指南。

注意

LXD 之前有一个前身应用程序,它也被称为“LXC”。今天,LXC 是技术,而 LXD 是应用程序。

我们将使用它们来创建一个类似于以下环境

A diagram of the intended Linux Container structure

具体来说,我将向你展示如何在你的服务器容器内设置简单的 Nginx 和 Apache Web 服务器,以及使用另一个包含 Nginx 作为反向代理的容器。同样,此设置应该适用于任何环境:从本地网络到虚拟专用服务器。

注意

反向代理是一个程序,它接收来自互联网(或本地网络)的传入连接,并将它们路由到正确的服务器、容器或应用程序。也有一些专门的工具可以完成这项工作,例如 HaProxy……但我发现 Nginx 更加易于使用。

先决条件和假设

  • 熟悉 Linux 命令行界面。如果你要在远程服务器上安装 LXC/LXD,应该知道如何使用 SSH。
  • 一台连接互联网的服务器(物理或虚拟),上面已经运行了 Rocky Linux。
  • 两个指向服务器的域名,使用 A 记录。
    • 两个子域名也可以正常工作。一个带有通配符子域名记录的域名也可以,或者一个自定义 LAN 域名。
  • 一个命令行文本编辑器。nano 可以,micro 是我的最爱,但使用任何让你感到舒适的编辑器。
  • 可以以 root 用户身份按照本教程进行操作,但这并不是一个好主意。在初始安装 LXC/LXD 后,我们将指导你创建专门用于操作 LXD 命令的非特权用户。
  • 现在可以使用 Rocky Linux 镜像作为容器的基础。
  • 如果你不熟悉 Nginx 或 Apache,你需要查看我们的一些其他指南,如果你想启动并运行一个完整的生产服务器。别担心,我会在下面链接这些指南。

设置主机服务器环境

安装 EPEL 仓库

LXD 需要 EPEL(Enterprise Linux 的额外软件包)仓库,可以使用以下命令轻松安装:

dnf install epel-release

安装完成后,检查更新

dnf update

如果在上面的更新过程中有内核更新,请重新启动服务器

安装 snapd

LXD 必须从 Rocky Linux 的 snap* 软件包中安装。为此,我们需要使用以下命令安装 snapd:

dnf install snapd

现在启用 snapd 服务,使其在服务器重启时自动启动,并立即启动它

systemctl enable snapd

然后运行

systemctl start snapd

在继续之前,重新启动服务器。你可以使用 reboot 命令或从你的 VPS/云托管管理面板重新启动。

* snap 是一种打包应用程序的方法,这样应用程序就可以包含它们需要的所有依赖项,并在几乎所有 Linux 系统上运行。

安装 LXD

安装 LXD 需要使用 snap 命令。此时,我们只是安装它,还没有进行任何设置

snap install lxd

如果你在物理(又称“裸机”)服务器上运行 LXD,你可能应该回到另一个指南,阅读那里的“环境设置”部分。那里有很多关于内核和文件系统的信息,以及更多内容。

如果你在虚拟环境中运行 LXD,只需重新启动并继续阅读。

LXD 初始化

现在环境已经全部设置好了,我们准备初始化 LXD。这是一个自动脚本,它会提出一系列问题,以帮助你启动并运行 LXD 实例

lxd init

以下是脚本中的问题和我们的答案,并在必要时进行解释

Would you like to use LXD clustering? (yes/no) [default=no]:

如果你对集群感兴趣,可以在这里进行一些额外的研究here。否则,只需按下“Enter”键接受默认选项。

Do you want to configure a new storage pool? (yes/no) [default=yes]:

接受默认选项。

Name of the new storage pool [default=default]: server-storage

为你的存储池选择一个名称。我喜欢用 LXD 运行的服务器名称来命名它。(存储池本质上是为你的容器预留的一定数量的硬盘空间。)

Name of the storage backend to use (btrfs, dir, lvm, zfs, ceph) [default=zfs]: lvm

上面的问题是关于你想要为存储使用哪种类型的文件系统,默认选项可能因系统上可用的选项而异。如果你在裸机服务器上,并且想使用 ZFS,请再次参考上面链接的指南。

在虚拟环境中,我发现“LVM”运行良好,它通常是我使用的。你可以在下一个问题中接受默认选项。

Create a new LVM pool? (yes/no) [default=yes]:

如果你有特定的硬盘或分区想用来存放整个存储池,请在下面输入“yes”。如果你在 VPS 上完成所有这些操作,你可能必须选择“no”。

Would you like to use an existing empty block device (e.g., a disk or partition)? (yes/no) [default=no]:

Metal As A Service (MAAS) 超出了本文档的范围。接受此处的默认选项。

Would you like to connect to a MAAS server? (yes/no) [default=no]:

以及更多默认选项。一切正常。

Would you like to create a new local network bridge? (yes/no) [default=yes]:

What should the new bridge be called? [default=lxdbr0]: `

What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:

如果你想在 LXD 容器中使用 IPv6,你可以在下一个选项中启用它。这取决于你,但你大多数时候不需要这样做。

What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:

这对于轻松备份服务器是必要的,它可以让你从其他计算机管理 LXD 安装。如果你觉得这些都很好,请在这里输入“yes”

Would you like the LXD server to be available over the network? (yes/no) [default=no]: yes

如果你在最后一个问题中回答了 yes,请在此处使用默认选项

Address to bind LXD to (not including port) [default=all]:

Port to bind LXD to [default=8443]:

现在,系统会要求你输入一个信任密码。这是你从其他计算机和服务器连接到 LXC 主机服务器的方式,因此请使用在你的环境中合理的值来设置它。将此密码保存到安全位置,例如密码管理器。

Trust password for new clients:

Again:

然后从这里开始继续使用默认选项

Would you like stale cached images to be updated automatically? (yes/no) [default=yes]

Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]:

设置用户权限

在继续之前,我们需要创建我们的“lxdadmin”用户,并确保它拥有所需的权限。我们需要“lxdadmin”用户能够使用sudo 来访问 root 命令,并且我们需要它成为“lxd”组的成员。要添加用户并确保它是两个组的成员,请运行

useradd -G wheel,lxd lxdadmin

然后设置密码

passwd lxdadmin

与其他密码一样,请将其保存到安全位置。

设置防火墙

在我们进行其他任何与容器相关的操作之前,你需要能够从外部访问你的代理服务器。如果你的防火墙阻止了端口 80(用于 HTTP/网页流量的默认端口)或端口 443(用于 HTTPS/安全网页流量),那么你将无法执行任何与服务器相关的事情。

另一个 LXD 指南将向你展示如何使用iptables 防火墙来实现这一点,如果你想使用这种方法。我倾向于使用 CentOS 默认防火墙:firewalld。所以这一次,我们就使用它。

firewalld 是通过 firewall-cmd 命令配置的。**我们首先要做的事情是**,在我们打开任何端口之前,确保你的容器可以自动分配 IP 地址

firewall-cmd --zone=trusted --permanent --change-interface=lxdbr0

警告

如果你没有执行最后一步,你的容器将无法正确访问互联网或彼此访问。这非常重要,了解这一点可以让你节省大量的挫败感。

现在,要添加一个新的端口,只需运行以下命令

firewall-cmd --permanent --zone=public --add-port=80/tcp

让我们分解一下

  • -–permanent 标志告诉防火墙确保每次防火墙重启以及服务器本身重启时使用此配置。
  • –-zone=public 告诉防火墙接受来自任何人的传入连接到此端口。
  • 最后,–-add-port=80/tcp 告诉防火墙接受传入的端口 80 连接,只要它们使用传输控制协议,这就是你在这种情况下想要使用的协议。

要重复 HTTPS 流量的过程,只需再次运行该命令,并更改数字。

firewall-cmd --permanent --zone=public --add-port=443/tcp

这些配置在你不强制执行之前不会生效。要做到这一点,请告诉firewalld 重新加载其配置,方法如下

firewall-cmd --reload

现在,有一种可能性很小,即这不起作用。在那些罕见的情况下,让firewalld 按照你的意愿行事,就像关闭它然后再次打开它一样。

systemctl restart firewalld

要确保端口已正确添加,请运行 firewall-cmd --list-all。一个配置正确的防火墙看起来有点像这样(我的本地服务器上还打开了一些额外的端口,请忽略它们)

public (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp9s0
  sources:
  services: cockpit dhcpv6-client ssh
  ports: 81/tcp 444/tcp 15151/tcp 80/tcp 443/tcp
  protocols:
  forward: no
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

这应该是你从防火墙方面需要的一切。

设置容器

实际上管理容器非常容易。只需将其视为能够根据命令创建一整台计算机,并随意启动或停止它。你也可以登录到这台“计算机”,并运行你喜欢的任何命令,就像你在主机服务器上做的那样。

注意

从这里开始,每个命令都应该以 lxdadmin 用户身份运行,或者你决定使用的任何名称,尽管有些命令需要使用sudo 来获得临时 root 权限。

在本教程中,你需要三个容器:反向代理服务器、一个测试 Nginx 服务器和一个测试 Apache 服务器,它们都运行在基于 Rocky 的容器中。

如果由于某种原因你需要一个具有完全权限的容器(你大多数时候不应该这样做),你可以以 root 身份运行所有这些命令。

在本教程中,你需要三个容器

我们将其命名为“proxy-server”(用于将 Web 流量定向到另外两个容器的容器)、“nginx-server”和“apache-server”。是的,我将向你展示如何反向代理到nginxapache 服务器。

我们将首先确定我们要使用哪个镜像作为我们容器的基础。在本教程中,我们只使用 Rocky Linux。例如,使用 Alpine Linux 可以导致更小的容器(如果存储是一个问题),但这超出了本文档的范围。

查找你想要的镜像

以下是用 Rocky Linux 启动容器的快速方法

lxc launch images:rockylinux/8/amd64 my-container

当然,最后的“my-container”部分应该改为你想要的任何容器名称,例如“proxy-server”。如果你的设备是 Raspberry Pi 之类的设备,那么“/amd64”部分应该改为“arm64”。

现在,以下是长版本:要查找你想要的镜像,你可以使用以下命令列出主 LXC 仓库中的所有可用镜像

lxc image list images: | more

然后,只需按下“Enter”键滚动浏览大量的镜像列表,并按下“Control-C”键退出列表查看模式。

或者,你可以简化你的生活,并指定你想要的 Linux 类型,方法如下

lxc image list images: | grep rockylinux

这将打印出一个更短的列表,看起来像这样

| rockylinux/8 (3 more)                    | 4e6beda70200 | yes    | Rockylinux 8 amd64 (20220129_03:44)          | x86_64       | VIRTUAL-MACHINE | 612.19MB  | Jan 29, 2022 at 12:00am (UTC) |
| rockylinux/8 (3 more)                    | c04dd2bcf20b | yes    | Rockylinux 8 amd64 (20220129_03:44)          | x86_64       | CONTAINER       | 127.34MB  | Jan 29, 2022 at 12:00am (UTC) |
| rockylinux/8/arm64 (1 more)              | adc0561d6330 | yes    | Rockylinux 8 arm64 (20220129_03:44)          | aarch64      | CONTAINER       | 124.03MB  | Jan 29, 2022 at 12:00am (UTC) |
| rockylinux/8/cloud (1 more)              | 2591d9716b04 | yes    | Rockylinux 8 amd64 (20220129_03:43)          | x86_64       | CONTAINER       | 147.04MB  | Jan 29, 2022 at 12:00am (UTC) |
| rockylinux/8/cloud (1 more)              | c963253fcea9 | yes    | Rockylinux 8 amd64 (20220129_03:43)          | x86_64       | VIRTUAL-MACHINE | 630.56MB  | Jan 29, 2022 at 12:00am (UTC) |
| rockylinux/8/cloud/arm64                 | 9f49e80afa5b | yes    | Rockylinux 8 arm64 (20220129_03:44)          | aarch64      | CONTAINER       | 143.15MB  | Jan 29, 2022 at 12:00am (UTC) |

创建容器

注意

下面,是创建所有这些容器的快速方法。你可能想要在创建 proxy-server 容器之前等待。我会在下面向你展示一个技巧,可以节省你的时间。

找到你想要的镜像后,请使用上面显示的 lxc launch 命令。要创建本教程中需要的容器,请按顺序运行以下命令(根据需要进行修改)

lxc launch images:rockylinux/8/amd64 proxy-server
lxc launch images:rockylinux/8/amd64 nginx-server
lxc launch images:rockylinux/8/amd64 apache-server

当你运行每个命令时,你应该收到一个通知,说明你的容器已创建甚至已启动。然后,你需要检查所有容器。

运行以下命令查看它们是否都已启动并运行

lxc list

这应该会给你类似以下的输出(不过,如果你选择使用 IPv6,它将包含更多文本)

+---------------+---------+-----------------------+------+-----------+-----------+
|     NAME      |  STATE  |         IPV4          | IPV6 |   TYPE    | SNAPSHOTS |
+---------------+---------+-----------------------+------+-----------+-----------+
| proxy-server  | RUNNING | 10.199.182.231 (eth0) |      | CONTAINER | 0         |
+---------------+---------+-----------------------+------+-----------+-----------+
| nginx-server  | RUNNING | 10.199.182.232 (eth0) |      | CONTAINER | 0         |
+---------------+---------+-----------------------+------+-----------+-----------+
| apache-server | RUNNING | 10.199.182.233 (eth0) |      | CONTAINER | 0         |
+---------------+---------+-----------------------+------+-----------+-----------+

关于容器网络的一句话

因此,在本指南开头链接的另一份指南中,有一个关于如何设置 LXC/LXD 以与 Macvlan 配合使用的完整教程。如果您正在运行本地服务器,并且希望每个容器在本地网络上都有一个可见的 IP 地址,这将特别有用。

当您在 VPS 上运行时,您通常没有这种选择。事实上,您可能只有一个允许您使用的 IP 地址。默认的网络配置旨在适应这种限制;按照我上面指定的步骤回答lxd init问题应该可以解决所有问题。

基本上,LXD 创建了一个名为桥接网络的虚拟网络设备(通常命名为“lxdbr0”),所有容器默认情况下都会连接到该桥接网络。通过它,它们可以通过主机的默认网络设备(以太网、Wi-Fi 或 VPS 提供的虚拟网络设备)连接到互联网。更重要的是,所有容器都可以互相连接。

为了确保这种容器间连接,每个容器都获得一个内部域名。默认情况下,这只是容器的名称加上“.lxd”。因此,“proxy-server”容器对所有其他容器在“proxy-server.lxd”处可用。但这里有一个非常重要的事情需要了解:默认情况下“.lxd”域名只在容器内部可用。

如果您在主机操作系统(或其他任何地方)上运行ping proxy-server.lxd,您将一无所获。这些内部域名稍后会非常有用,不过。

从技术上讲,您可以更改此设置,并使容器的内部域名在主机上可用……但我从未真正弄明白。最好还是将您的反向代理服务器放在容器中,以便您可以轻松地对其进行快照和备份。

管理您的容器

在继续之前,您应该了解一些事情

启动和停止

所有容器都可以根据需要使用以下命令启动、停止和重启

lxc start mycontainer
lxc stop mycontainer
lxc restart mycontainer

即使是 Linux 也需要偶尔重启。您实际上可以使用以下命令同时启动、停止和重启所有容器。

lxc start --all
lxc stop --all
lxc restart --all

restart --all选项对于一些更模糊的临时错误非常有用。

在容器内部执行操作

您可以通过两种方式控制容器内部的操作系统:您可以直接从主机操作系统在容器内部运行命令,或者可以打开一个 shell。

我的意思是,要在容器内部运行命令(例如安装Apache),只需使用lxc exec,如下所示

lxc exec my-container dnf install httpd -y

这将使Apache自行安装,您将在主机的终端上看到命令的输出。

要打开一个 shell(您可以在其中以 root 用户身份运行所需的命令),请使用以下命令

lxc exec my-container bash

如果您像我一样,更看重便利性而不是存储空间,并在所有容器中安装了fish等备用 shell,只需更改命令,如下所示

lxc exec my-container fish

在几乎所有情况下,您都会自动被置于 root 帐户,并在/root目录中。

最后,如果您已经打开了进入容器的 shell,则退出它的方式与退出任何 shell 相同:使用简单的exit命令。

复制容器

现在,如果您有一个希望轻松复制的容器,您无需启动一个全新的容器并再次安装所有基本应用程序。这需要额外的操作,而这些操作是不必要的。只需运行

lxc copy my-container my-other-container

将创建“my-container”的精确副本,命名为“my-other-container”。它可能不会自动启动,因此请对新容器的配置进行必要的更改,然后运行

lxc start my-other-container

此时,您可能希望进行一些更改,例如更改容器的内部主机名等。

配置存储和 CPU 限制

LXC/LXD 通常会定义容器获得多少存储空间,并通常会管理资源,但您可能希望控制这些资源。如果您担心保持容器大小,您可以使用lxc config命令根据需要缩小或扩展容器。

以下命令将在容器上设置 2GB 的“软”限制。软限制实际上更像是一个“最小存储”,如果可用,容器将使用更多存储空间。如往常一样,将“my-container”更改为实际容器的名称。

lxc config set my-container limits.memory 2GB

您可以像这样设置硬限制

lxc config set my-container limits.memory.enforce 2GB

如果您想确保任何给定的容器无法占用服务器所有可用的处理能力,您可以使用以下命令限制它可以访问的 CPU 内核数量。只需根据需要更改末尾的 CPU 内核数量即可。

lxc config set my-container limits.cpu 2

删除容器(以及如何防止这种情况发生)

最后,您可以通过运行以下命令删除容器

lxc delete my-container

如果容器正在运行,您将无法删除它,因此您可以先停止它,或者使用-–force标志跳过此步骤。

lxc delete my-container --force

现在,由于Tab命令完成、用户错误,以及D在大多数键盘上与S相邻,您可能会意外删除容器。

为了防止这种情况,您可以将任何容器设置为“受保护”状态(使删除它们的过程需要额外的步骤),使用以下命令

lxc config set my-container security.protection.delete true

要取消保护容器,只需再次运行该命令,但将“true”更改为“false”。

设置服务器

好的,现在您的容器已启动并运行,是时候安装您需要的内容了。首先,确保所有容器都使用以下命令更新(如果您尚未创建“proxy-server”容器,请跳过它)

lxc exec proxy-server dnf update -y
lxc exec nginx-server dnf update -y
lxc exec apache-server dnf update -y

然后,进入每个容器,并开始工作。

您还需要每个容器的文本编辑器。默认情况下,Rocky Linux 附带vi,但如果您想简化操作,nano也可以。您可以在打开容器之前在每个容器中安装它。

lxc exec proxy-server dnf install nano -y
lxc exec nginx-server dnf install nano -y
lxc exec apache-server dnf install nano -y

在接下来的所有文本编辑器相关命令中,我将使用nano,但您可以使用您喜欢的编辑器。

Apache 网站服务器

为了学习和测试目的,我们将保持简洁。请在下方查看完整 Apache 指南的链接。

首先,打开进入容器的 shell。请注意,默认情况下,容器会将您置于 root 帐户。对于我们的目的来说,这很好,但您可能希望为实际的生产目的创建一个特定的 Web 服务器用户。

lxc exec apache-server bash

登录后,只需以最简单的方式安装Apache

dnf install httpd

现在,您可以从这里开始遵循我们的Apache Web Server 多站点设置指南,但这对于我们的目的来说有点过头了。我们通常不希望在像这样的容器化环境中为多个网站设置 Apache。毕竟,容器的全部意义在于关注点的分离。

此外,SSL 证书将位于代理服务器上,因此我们将保持简单。

安装Apache后,确保它正在运行,并且可以在重启后继续运行

systemctl enable --now httpd

--now标志可以让您跳过启动实际服务器的命令。作为参考,该命令应该是

systemctl start httpd

如果您在服务器主机上安装了curl,您可以使用以下命令确保默认网页正在运行

curl [container-ip-address]

请记住,您可以使用lxc list查看所有容器的 IP。如果您在所有容器中安装了 curl,您可以运行

curl localhost

从代理服务器获取真实用户 IP

现在,您需要执行此步骤来准备 Apache 以使用反向代理。默认情况下,用户实际的 IP 地址不会被 Web 服务器容器中的服务器记录。您希望这些 IP 地址通过,因为某些 Web 应用程序需要用户 IP 来进行诸如审核、禁止和故障排除等操作。

要使访客的 IP 地址绕过代理服务器,您需要两个部分:代理服务器中的正确设置(我们将在后面介绍),以及 Apache 服务器的简单配置文件。

感谢 Linode 和他们自己的 LXD 指南提供的这些配置文件模板。

创建一个新的配置文件

nano /etc/httpd/conf.d/real-ip.conf

并将以下文本添加到其中

RemoteIPHeader X-Real-IP
RemoteIPTrustedProxy proxy-server.lxd

请记住,如果需要,将proxy-server.lxd更改为实际代理容器的名称。现在不要重启 Apache 服务器。我们添加的配置文件可能会导致问题,直到代理服务器启动并运行。

现在退出 shell,让我们开始使用 Nginx 服务器。

注意

虽然这种技术确实有效(您的 Web 应用程序和网站将获得用户的真实 IP),但 Apache 自己的访问日志不会显示正确的 IP。它们通常会显示反向代理所在的容器的 IP。这显然是 Apache 记录信息的方式存在问题。

如果您需要查看 IP 地址,可以检查代理服务器的访问日志,或者检查您正在安装的任何 Web 应用程序的日志。

Nginx 网站服务器

同样,我们将保持简洁。如果您想在生产环境中使用最新(也是推荐)版本的 Nginx,请查看我们的Nginx 安装入门指南。该指南涵盖了完整的安装指南,以及一些配置服务器的最佳实践。

首先,登录到容器的 shell

lxc exec nginx-server bash

使用默认命令安装 Nginx

dnf install nginx

然后,启用并启动 Nginx

dnf enable --now nginx

注意

还记得我说过在创建代理容器之前要等待吗?原因如下:此时,您可以通过离开“nginx-server”容器并将其复制来创建“proxy-server”容器,从而节省一些时间

lxc copy nginx-server proxy-server

确保使用lxc start proxy-server启动代理容器,并将代理端口添加到容器,如下所示。

同样,您可以使用以下命令从主机确保容器正常运行

curl [your-container-ip]

从代理服务器获取真实用户 IP(再次)

这次日志应该可以正常工作。应该。为此,我们在/etc/nginx/conf.d中放置一个非常相似的文件

nano /etc/nginx/conf.d/real-ip.conf

然后在其中放置以下文本

real_ip_header    X-Real-IP;
set_real_ip_from  proxy-server.lxd;

最后,不要重启服务器。同样,该配置文件可能会导致问题,直到代理服务器设置完毕。

反向代理服务器

还记得我说过您需要两个域名或子域名吗?这就是您需要它们的地方。在本教程中,我使用的子域名是

  • apache.server.test
  • nginx.server.test

根据需要在所有文件和说明中更改它们。

如果您从“nginx-server”容器复制了“proxy-server”容器,并且已将代理设备添加到其中,只需进入 shell。如果您之前创建了容器,您需要重复在“proxy-server”容器中安装 Nginx 的所有步骤。

安装完毕且您知道它可以正常运行后,您只需设置几个配置文件,将来自您选择的域名的流量定向到实际的网站服务器。

在执行此操作之前,请确保您可以通过其内部域名访问这两个服务器

curl apache-server.lxd
curl nginx-server.lxd

如果这两个命令在你的终端中加载了默认服务器欢迎页面的 HTML,那么一切都配置正确。

重要步骤: 配置“proxy-server”容器以接收所有传入的服务器流量

再次提醒,你可能想在实际创建代理服务器时再进行此操作,但以下是你需要知道的说明。

还记得我们在防火墙中打开了 80 和 443 端口吗?现在我们要让“proxy-server”容器监听这些端口,并接收所有指向它们的流量。

只需依次运行以下两个命令。

lxc config device add proxy-server myproxy80 proxy listen=tcp:0.0.0.0:80 connect=tcp:127.0.0.1:80
lxc config device add proxy-server myproxy443 proxy listen=tcp:0.0.0.0:443 connect=tcp:127.0.0.1:443

让我们来分解一下。每个命令都在“proxy-server”容器中添加一个虚拟“设备”。这些设备被设置为监听主机操作系统上的 80 和 443 端口,并将它们绑定到容器的 80 和 443 端口。每个设备都需要一个名称,所以我选择“myproxy80”和“myproxy443”。

“listen”选项是主机操作系统上的端口,如果我没记错的话,0.0.0.0 是“lxdbr0”桥接器上主机上的 IP 地址。“connect”选项是连接到的本地 IP 地址和端口。

注意

设置好这些设备后,你应该重新启动所有容器,以确保万无一失。

这些虚拟设备 ideally 应该独一无二。通常最好不要在当前正在运行的另一个容器中添加“myport80”设备;它必须叫其他名字。

同样,一次只能有一个容器监听任何特定主机操作系统的端口。

将流量定向到 Apache 服务器

在“proxy-server”容器中,在/etc/nginx/conf.d/目录下创建一个名为apache-server.conf的配置文件。

nano /etc/nginx/conf.d/apache-server.conf

然后将此测试内容粘贴进去,根据需要更改域名,然后保存。

upstream apache-server {
    server apache-server.lxd:80;
}

server {
    listen 80 proxy_protocol;
    listen [::]:80 proxy_protocol;
    server_name apache.server.test; #< Your domain goes here

    location / {
        proxy_pass http://apache-server;

        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

让我们来稍微分解一下。

  1. upstream部分定义了反向代理将所有流量发送到的确切位置。具体来说,它将流量发送到“apache-server”容器的内部域名:apache-server.lxd
  2. listen开头的两行告诉服务器监听端口 80 上的传入流量,使用代理协议。第一个通过 IPv4,第二个通过 IPv6。
  3. server_name函数接收所有专门发往“apache.server.test”的流量,并通过反向代理进行路由。
  4. proxy-pass函数实际上是将server_name变量捕获的所有流量定向到upstream部分定义的服务器。
  5. proxy_redirect函数显然会干扰反向代理,因此我们确保将其关闭。
  6. 所有proxy-set-header选项都将用户的 IP 地址等信息发送到 Web 服务器。

警告

listen变量中的proxy_protocol部分对于代理服务器正常工作至关重要。切勿遗漏它。

对于每个 LXD/网站配置文件,你都需要相应地更改upstreamserverserver_nameproxy_pass设置。proxy-pass中“http://”后面的文本必须与upstream文本后面的文本匹配。

使用systemctl restart nginx重新加载服务器,然后将浏览器指向你正在使用的域名,而不是apache.server.test。如果你的页面看起来像这样,就意味着你成功了。

A screenshot of the default Rocky Linux Apache welcome page

注意

你可以随意给配置文件命名。我在教程中使用简化的名称,但一些系统管理员建议使用基于实际域名的名称,但要反过来。这是一件基于字母顺序组织的事情。

例如,“apache.server.test”将获得一个名为test.server.apache.conf的配置文件。

将流量定向到 Nginx 服务器

只需重复上述过程。创建一个与之前相同的文件。

nano /etc/nginx/conf.d/nginx-server.conf

添加相应的文本。

upstream nginx-server {
    server rocky-nginx.lxd:80;
}

server {
    listen 80 proxy_protocol;
    listen [::]:80 proxy_protocol;
    server_name nginx.server.test; #< Your domain goes here

    location / {
        proxy_pass http://nginx-server;

        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

再次重新加载代理服务器,将浏览器指向相应的地址,并希望你看到这个页面。

A screenshot of the default Rocky Linux Nginx welcome page

重新启动 Web 服务器容器中的服务器

退出“proxy-server”容器,并使用一个简单的命令重新启动其他两个容器中的服务器。

lxc exec apache-server systemctl restart httpd && lxc exec nginx-server restart nginx

这将把我们创建的“real-ip.conf”文件应用到各自的服务器配置中。

为你的网站获取 SSL 证书

使用 Let's Encrypt 和一个名为 certbot 的小应用程序,最容易获得官方的、正确的 SSL 证书。certbot 会自动检测你的网站,为它们获取 SSL 证书,并配置网站本身。它甚至会每 30 天左右为你更新证书,无需你或 cron 作业进行任何干预。

所有这些都必须从“proxy-server”容器中完成,因此请登录到该 shell。进入后,安装 EPEL 仓库,就像你在主机上做的那样。首先确保容器已更新。

dnf update

然后,添加 EPEL 仓库。

dnf install epel-release

然后,你只需安装 certbot 及其 Nginx 模块即可。

dnf install certbot python3-certbot-nginx

安装完成后,只要你已经配置了几个网站,只需运行。

certbot --nginx

Certbot 会读取你的 Nginx 配置,并确定你有多少个网站以及它们是否需要 SSL 证书。此时,系统会问你几个问题。你是否接受服务条款,你是否需要电子邮件等?

最重要的问题如下。看到此提示时,输入你的电子邮件地址。

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (used for urgent renewal and security notices)
 (Enter 'c' to cancel):

在这里你可以选择哪些网站获取证书。只需按 Enter 键即可为所有网站获取证书。

Which names would you like to activate HTTPS for?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: apache.server.test
2: nginx.server.test
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel):

你将看到一堆确认文本,操作完成。但是,如果你访问你的网站,可能会发现它们无法正常工作。这是因为 certbot 在创建更新的配置时,遗漏了一件非常重要的事情。

进入你的apache-server.confnginx-server.conf文件,找到以下两行。

listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot

是的,它们缺少proxy_protocol设置,这很糟糕。自己添加它。

listen proxy_protocol [::]:443 ssl ipv6only=on; # managed by Certbot
listen proxy_protocol 443 ssl; # managed by Certbot

保存文件,重新启动服务器,你的网站应该可以正常加载,没有任何问题。

注意

  1. 在本教程中,我没有过多地介绍实际 Web 服务器的配置。在生产环境中,你至少应该做的就是更改实际 Web 服务器容器中服务器配置文件中的域名,而不仅仅是代理容器。并且可能在每个容器中设置一个 Web 服务器用户。
  2. 如果你想了解更多关于手动管理 SSL 证书和 SSL 服务器配置的信息,请查看我们关于安装 certbot 和生成 SSL 证书的指南.
  3. 如果你将 Nextcloud 之类的应用程序放在代理后面的 LXD 容器中,则需要进行一些额外的配置(出于安全原因)。

结论

关于 LXC/LXD、容器化、Web 服务器和运行网站,还有很多东西要学,但这实际上应该能让你有一个良好的开端。一旦你了解了所有内容的设置方式以及如何按照自己的喜好进行配置,你甚至可以开始自动化此过程。

你可以使用 Ansible,或者像我一样,只是写一些自定义脚本,运行它们来加快一切速度。你甚至可以创建一些预先安装了你所有喜欢的软件的小型“模板容器”,然后只需复制它们并根据需要扩展它们的存储容量即可。

好了。完成了。我要去玩游戏了。祝你好运!

作者:Ezequiel Bruni

贡献者:Steven Spencer、Ganna Zhyrnova