如何在Ubuntu16.04上使用LXD使用Nginx和HAProxy托管多个网站

来自菜鸟教程
跳转至:导航、​搜索

介绍

Linux 容器 是一组进程,通过使用 Linux 内核安全功能(例如命名空间和控制组)与系统的其余部分隔离。 它是一个类似于虚拟机的结构,但它更轻量级; 您没有运行额外内核或模拟硬件的开销。 这意味着您可以轻松地在同一台服务器上创建多个容器。 使用 Linux 容器,您可以在同一台服务器上运行多个受限的整个操作系统实例,或者将您的应用程序及其依赖项捆绑在一个容器中,而不会影响系统的其余部分。

例如,假设您有一台服务器,并且为您的客户设置了多个服务,包括网站。 在传统安装中,每个网站都是 Apache 或 Nginx Web 服务器的同一实例的虚拟主机。 但是对于 Linux 容器,每个网站都将配置在自己的容器中,并带有自己的 Web 服务器。

我们可以使用 LXD 来创建和管理这些容器。 LXD 提供管理程序服务来管理容器的整个生命周期。

在本教程中,您将使用 LXD 在同一台服务器上安装两个基于 Nginx 的网站,每个网站都限制在自己的容器中。 然后,您将在将充当反向代理的第三个容器中安装 HAProxy。 然后,您将流量路由到 HAProxy 容器,以使这两个网站都可以从 Internet 访问。

先决条件

要完成本教程,您需要以下内容:

第 1 步 — 将您的用户添加到 lxd

使用非 root 用户帐户登录服务器。 我们将使用这个非用户帐户来执行所有容器管理任务。 为此,您必须先将此用户添加到 lxd 组。 使用以下命令执行此操作:

sudo usermod --append --groups lxd sammy

注销服务器并重新登录,以便您的新 SSH 会话将使用新的组成员身份进行更新。 登录后,您可以开始配置 LXD。

第 2 步 — 配置 LXD

LXD 需要正确配置才能使用。 最重要的配置决策是用于存储容器的存储后端类型。 LXD 推荐的存储后端是 ZFS 文件系统,存储在预先分配的文件中或使用 Block Storage 存储。 要在 LXD 中使用 ZFS 支持,请安装 zfsutils-linux 包:

sudo apt-get update
sudo apt-get install zfsutils-linux

安装后,您就可以初始化 LXD。 在初始化期间,系统会提示您指定 ZFS 存储后端的详细信息。 接下来有两个部分,具体取决于您是要使用预分配的文件还是块存储。 根据您的情况执行适当的步骤。 指定存储机制后,您将为容器配置网络选项。

选项 1 – 使用预分配文件

按照以下步骤将 LXD 配置为使用预分配的文件来存储容器。 首先,执行以下命令启动 LXD 初始化过程:

sudo lxd init

系统将提示您提供几条信息,如以下输出所示。 我们将选择所有默认值,包括预分配文件的建议大小,称为 循环设备

OutputName of the storage backend to use (dir or zfs) [default=zfs]: zfs
Create a new ZFS pool (yes/no) [default=yes]? yes
Name of the new ZFS pool [default=lxd]: lxd
Would you like to use an existing block device (yes/no) [default=no]? no
Size in GB of the new loop device (1GB minimum) [default=15]: 15
Would you like LXD to be available over the network (yes/no) [default=no]? no
Do you want to configure the LXD bridge (yes/no) [default=yes]? yes
Warning: Stopping lxd.service, but it can still be activated by:
  lxd.socket
LXD has been successfully configured.

建议的大小是根据服务器的可用磁盘空间自动计算的。

配置设备后,您将配置网络设置,我们将在下一个可选部分之后进行探讨。

选项 2 – 使用块存储

如果你打算使用块存储,你需要找到指向你创建的块存储卷的设备,以便在 LXD 的配置中指定它。 进入DigitalOcean控制面板l中的Volumes标签,找到你的音量,点击弹出的More,然后点击Config指令

通过查看格式化卷的命令找到设备。 具体来说,查找 sudo mkfs.ext4 -F 命令中指定的路径。 下图显示了卷的示例。 您只需要带下划线的部分:

在这种情况下,卷名是 /dev/disk/by-id/scsi-0D0_Volume_volume-fra1-01,尽管您的可能不同。

识别卷后,返回终端并发出以下命令以开始 LXD 初始化过程。

sudo lxd init

您将收到一系列问题。 回答以下输出中显示的问题:

OutputName of the storage backend to use (dir or zfs) [default=zfs]: zfs
Create a new ZFS pool (yes/no) [default=yes]? yes
Name of the new ZFS pool [default=lxd]: lxd

当系统提示您使用现有的块设备时,选择 yes 并提供设备的路径:

Output of the "lxd init" commandWould you like to use an existing block device (yes/no) [default=no]? yes
Path to the existing block device: /dev/disk/by-id/scsi-0DO_Volume_volume-fra1-01

然后对其余问题使用默认值:

Output of the "lxd init" commandWould you like LXD to be available over the network (yes/no) [default=no]? no
Do you want to configure the LXD bridge (yes/no) [default=yes]? yes
Warning: Stopping lxd.service, but it can still be activated by:
  lxd.socket
LXD has been successfully configured.

该过程完成后,您将配置网络。

配置网络

初始化过程将向我们展示一系列如下图所示的屏幕,让我们为容器配置网络桥接器,以便它们可以获取私有 IP 地址、相互通信并访问 Internet。

使用每个选项的默认值,但当询问有关 IPv6 网络时,请选择 No,因为我们不会在本教程中使用它。

完成网络配置后,您就可以创建容器了。

第三步——创建容器

我们已经成功配置了 LXD。 我们已经指定了存储后端的位置,并为任何新创建的容器配置了默认网络。 我们已经准备好创建和管理一些容器,我们将使用 lxc 命令来完成。

让我们尝试我们的第一个命令,它列出了可用的已安装容器:

lxc list

您将看到以下输出:

Output of the "lxd list" commandGenerating a client certificate. This may take a minute...
If this is your first time using LXD, you should also run: sudo lxd init
To start your first container, try: lxc launch ubuntu:16.04

+------+-------+------+------+------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+------+-------+------+------+------+-----------+

由于这是 lxc 命令第一次与 LXD 管理程序通信,因此输出让我们知道该命令自动创建了一个客户端证书,用于与 LXD 进行安全通信。 然后,它显示了一些有关如何启动容器的信息。 最后,该命令显示了一个空的容器列表,这是预期的,因为我们还没有创建任何容器。

让我们创建三个容器。 我们将为每个 Web 服务器创建一个,并为反向代理创建第三个容器。 反向代理的目的是将来自 Internet 的传入连接定向到容器中正确的 Web 服务器。

我们将使用 lxc launch 命令创建并启动一个名为 web1 的 Ubuntu 16.04 (ubuntu:x) 容器。 ubuntu:x中的x是Ubuntu 16.04代号Xenial首字母的快捷方式。 ubuntu: 是 LXD 映像的预配置存储库的标识符。

注意:您可以通过运行 lxc image list ubuntu: 和其他发行版通过运行 lxc image list images: 找到所有可用 Ubuntu 映像的完整列表。


执行以下命令来创建容器:

lxc launch ubuntu:x web1
lxc launch ubuntu:x web2
lxc launch ubuntu:x haproxy

因为这是我们第一次创建容器,所以第一个命令是从网上下载容器镜像并缓存到本地。 接下来的两个容器的创建速度将大大加快。

在这里您可以看到创建容器 web1 的示例输出。

OutputCreating web1
Retrieving image: 100%
Starting web1

现在我们已经创建了三个空的 vanilla 容器,让我们使用 lxc list 命令来显示有关它们的信息:

lxc list

输出显示一个表格,其中包含每个容器的名称、当前状态、IP 地址、类型以及是否拍摄了快照。

输出

+---------+---------+-----------------------+------+------------+-----------+
|  NAME   |  STATE  |         IPV4          | IPV6 |    TYPE    | SNAPSHOTS |
+---------+---------+-----------------------+------+------------+-----------+
| haproxy | RUNNING | 10.10.10.10 (eth0)    |      | PERSISTENT | 0         |
+---------+---------+-----------------------+------+------------+-----------+
| web1    | RUNNING | 10.10.10.100 (eth0)   |      | PERSISTENT | 0         |
+---------+---------+-----------------------+------+------------+-----------+
| web2    | RUNNING | 10.10.10.200 (eth0)   |      | PERSISTENT | 0         |
+---------+---------+-----------------------+------+------------+-----------+

记下容器名称及其对应的 IPv4 地址。 您将需要它们来配置您的服务。

第 4 步 — 配置 Nginx 容器

让我们连接到 web1 容器并配置第一个 Web 服务器。

要连接,我们使用 lxc exec 命令,它采用容器名称和要执行的命令。 执行以下命令连接容器:

lxc exec web1 -- sudo --login --user ubuntu

-- 字符串表示 lxc 的命令参数应该停在那里,该行的其余部分将作为要在容器内执行的命令传递。 命令为sudo --login --user ubuntu,为容器内预配置的账号ubuntu提供登录shell。

注意:如果需要以root身份连接容器,可以使用命令lxc exec web1 -- /bin/bash代替。


进入容器后,我们的 shell 提示现在如下所示。

Outputubuntu@web1:~$

容器中的这个 ubuntu 用户预先配置了 sudo 访问权限,并且可以在不提供密码的情况下运行 sudo 命令。 这个外壳被限制在容器的范围内。 我们在这个 shell 中运行的任何东西都留在容器中,无法逃逸到主机服务器。

让我们更新容器内 Ubuntu 实例的包列表并安装 Nginx:

sudo apt-get update
sudo apt-get install nginx

让我们编辑该站点的默认网页并添加一些文本,明确表明该站点托管在 web1 容器中。 打开文件/var/www/html/index.nginx-debian.html

sudo nano /var/www/html/index.nginx-debian.html

对文件进行以下更改:

编辑文件 /var/www/html/index.nginx-debian.html

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx on LXD container web1!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx on LXD container web1!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
...

我们在两个地方编辑了文件,并专门添加了文本on LXD container web1。 保存文件并退出编辑器。

现在退出容器并返回到主机服务器:

logout

web2 容器重复此过程。 登录,安装 Nginx,然后编辑文件 /var/www/html/index.nginx-debian.html 以提及 web2。 然后退出 web2 容器。

让我们使用 curl 来测试容器中的 Web 服务器是否正常工作。 我们需要前面显示的 Web 容器的 IP 地址。

curl http://10.10.10.100/

输出应该是:

Output of "curl http://10.10.10.100/" command<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx on LXD container web1!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx on LXD container web1!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
...

也测试第二个容器,使用 curl 命令及其 IP 地址来验证它是否也设置正确。 配置好两个容器后,我们可以继续设置 HAProxy。

第 5 步 — 配置 HAProxy 容器

我们将在这些容器前面设置 HAProxy 作为代理。 如果您需要有关其工作原理的更多背景知识,请查看教程 HAProxy 和负载平衡概念简介 。 我们将根据我们使用的域名将流量引导到每个容器。 我们将在下面的配置示例中使用域example.com,它是像本教程这样的文档的特殊保留域。 我们将在主机名 example.comwww.example.com 上提供第一个网站。 第二个网站将位于 www2.example.com。 用您自己的域名代替这些域名。

登录haproxy容器:

lxc exec haproxy -- sudo --login --user ubuntu

更新安装包列表并安装 HAProxy:

sudo apt-get update
sudo apt-get install haproxy

安装完成后,我们可以配置 HAProxy。 HAProxy 的配置文件位于 /etc/haproxy/haproxy.cfg。 使用您喜欢的文本编辑器打开文件。

sudo nano /etc/haproxy/haproxy.cfg

首先,我们将对 defaults 部分进行一些修改。 我们将添加 forwardfor 选项,以便我们保留 Web 客户端的真实源 IP,我们将添加 http-server-close 选项,它可以实现会话重用并降低延迟。

/etc/haproxy/haproxy.conf

global
...
defaults
    log global
    mode    http
    option  httplog
    option  dontlognull
    option    forwardfor
    option    http-server-close
    timeout connect 5000
    timeout client  50000
    timeout server  50000
...

接下来,我们将配置前端指向我们的两个后端容器。 添加一个名为 www_frontend 的新 frontend 部分,如下所示:

/etc/haproxy/haproxy.conf

frontend www_frontend
    bind *:80      # Bind to port 80 (www) on the container

    # It matches if the HTTP Host: field mentions any of the hostnames (after the '-i').
    acl host_web1 hdr(host) -i example.com www.example.com
    acl host_web2 hdr(host) -i web2.example.com

    # Redirect the connection to the proper server cluster, depending on the match.
    use_backend web1_cluster if host_web1
    use_backend web2_cluster if host_web2

acl 命令匹配 Web 服务器的主机名,并将请求重定向到相应的 backend 部分。

然后我们定义两个新的 backend 部分,一个用于每个 Web 服务器,并将它们分别命名为 web1_clusterweb2_cluster。 将以下代码添加到文件中以定义后端:

/etc/haproxy/haproxy.conf

backend web1_cluster
    balance leastconn
    # We set the X-Client-IP HTTP header. This is useful if we want the web server to know the real client IP.
    http-request set-header X-Client-IP %[src]
    # This backend, named here "web1", directs to container "web1.lxd" (hostname).
    server web1 web1.lxd:80 check

backend web2_cluster
    balance leastconn
    http-request set-header X-Client-IP %[src]
    server web2 web2.lxd:80 check

balance 选项表示负载平衡策略。 在这种情况下,我们选择最少的连接数。 http-request 选项使用真实的 Web 客户端 IP 设置 HTTP 标头。 如果我们不设置此标头,Web 服务器会将 HAProxy IP 地址记录为所有连接的源 IP,从而更难以分析您的流量来自何处。 server 选项指定服务器的任意名称(web1),后跟服务器的主机名和端口…

LXD 为容器提供 DNS 服务器,因此 web1.lxd 解析为与 web1 容器关联的 IP。 其他容器有自己的主机名,例如 web2.lxdhaproxy.lxd

check 参数告诉 HAPRoxy 在 Web 服务器上执行健康检查以确保它可用。

要测试配置是否有效,请运行以下命令:

/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -c

输出应该是

输出

Configuration file is valid

让我们重新加载 HAProxy 以便它读取新配置。

sudo systemctl reload haproxy

现在退出容器以返回主机。

logout

我们已将 HAProxy 配置为充当反向代理,它将在端口 80 上接收到的任何连接转发到其他两个容器中的相应 Web 服务器。 让我们测试一下 haproxy 是否确实设法将请求转发到正确的 Web 容器。 执行这个命令:

curl --verbose --header 'Host: web2.example.com' http://10.10.10.10

这会向 HAProxy 发出请求并设置 HTTP host 标头,HAProxy 应使用该标头将连接重定向到相应的 Web 服务器。

输出应该是

Output of "curl --verbose --header 'Host: web2.example.com' http://10.10.10.10" command...
> GET / HTTP/1.1
> Host: web2.example.com
> User-Agent: curl/7.47.0
> Accept: */*
> 
...
< 
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx on LXD container web2!</title>
<style>
...

HAProxy 正确理解请求并将其转发到 web2 容器。 在那里,Web 服务器提供我们之前编辑的默认索引页面,并显示文本 on LXD container web2。 现在让我们将外部请求路由到 HAProxy,以便全世界都可以访问我们的网站。

第 6 步 — 将传入连接转发到 HAProxy 容器

最后一个难题是将反向代理连接到 Internet。 我们需要设置我们的服务器,将它可能在端口 80 上从 Internet 接收到的任何连接转发到 haproxy 容器。

HAProxy 安装在容器中,默认情况下无法从 Internet 访问。 为了解决这个问题,我们将创建一个 iptables 规则来转发连接。

iptables 命令需要两个 IP 地址:服务器的公共 IP 地址(your_server_ip)和 haproxy 容器的私有 IP 地址(your_haproxy_ip) ,您可以使用 lxc list 命令获取。

执行此命令以创建规则:

sudo iptables -t nat -I PREROUTING -i eth0 -p TCP -d your_server_ip/32 --dport 80 -j DNAT --to-destination your_haproxy_ip:80

以下是命令的分解方式:

  • -t nat 指定我们正在使用 nat 表。
  • -I PREROUTING 指定我们将规则添加到 PREROUTING 链。
  • -i eth0 指定接口 eth0,这是 Droplets 上默认的公共接口。
  • -p TCP 表示我们正在使用 TCP 协议。
  • -d your_server_ip/32 指定规则的目标 IP 地址。
  • --dport 80:指定目的端口。
  • -j DNAT 表示我们要跳转到目标 NAT (DNAT)。
  • --to-destination your_haproxy_ip:80 表示我们希望请求转到带有 HAProxy 的容器的 IP 地址。

Iptables 防火墙的工作原理IPtables Essentials:通用防火墙规则和命令 中了解有关 IPTables 的更多信息。

最后,为了保存这个 iptables 命令以便在重新启动后重新应用它,我们安装了 iptables-persistent 包:

sudo apt-get install iptables-persistent

安装包时,会提示保存当前的 iptables 规则。 接受并保存所有当前的 iptables 规则。

如果您已经设置了两个 FQDN,那么您应该能够使用您的 Web 浏览器连接到每个网站。 试试看。

要测试这两个 Web 服务器是否可以从 Internet 访问,请使用 curl 命令从本地计算机访问每个服务器,如下所示:

curl --verbose --header 'Host: example.com' 'http://your_server_ip'
curl --verbose --header 'Host: web2.example.com' 'http://your_server_ip'

这些命令与服务器的公共 IP 地址建立 HTTP 连接,并添加一个带有 --header 选项的 HTTP 标头字段,HAProxy 将使用该选项来处理请求,就像您在第 5 步中所做的那样。

这是第一个 curl 命令的输出:

Output*   Trying your_server_ip...
* Connected to your_server_ip (your_server_ip) port 80 (#0)
> GET / HTTP/1.1
> Host: example.com
> User-Agent: curl/7.47.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: nginx/1.10.0 (Ubuntu)
...
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx on LXD container web1!</title>
<style>
    body {
...

这是第二个 curl 命令的输出:

Output*   Trying your_server_ip...
* Connected to your_server_ip (your_server_ip) port 80 (#0)
> GET / HTTP/1.1
> Host: web2.example.com
> User-Agent: curl/7.47.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: nginx/1.10.0 (Ubuntu)
...
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx on LXD container web2!</title>
<style>
    body {
...

在这两种情况下,都会显示正确的网站。

结论

您已经设置了两个网站,每个网站都在自己的容器中,并使用 HAProxy 引导流量。 您可以复制此过程以配置更多网站,每个网站都限制在自己的容器中。

您还可以在新容器中添加 MySQL,然后安装 WordPress 之类的 CMS 来运行每个网站。 您还可以使用此过程来支持旧版本的软件。 例如,如果 CMS 的安装需要 PHP5 等旧版软件,那么您可以在容器中安装 Ubuntu 14.04 (lxc launch ubuntu:t),而不是尝试降级 Ubuntu 16.04 上可用的包管理器版本。

最后,LXD 提供了对容器的完整状态进行快照的能力,这使得在以后创建备份和回滚容器变得容易。 此外,如果我们将 LXD 安装在两台不同的服务器上,则可以将它们连接起来并通过 Internet 在服务器之间迁移容器。