如何在 Ubuntu 16.04 上使用 LXD 通过 Nginx 和 HAProxy 托管多个网站

介绍

一个 Linux 容器是由使用 Linux 内核安全功能(如命名空间和控制组)与其他系统隔离的过程的组合,它类似于虚拟机,但更轻量级;你没有执行额外的内核或模拟硬件,这意味着你可以轻松地在同一个服务器上创建多个容器. 使用 Linux 容器,你可以运行整个操作系统的多个实例,限制在同一个服务器上,或将你的应用程序和其依赖组合成一个容器,而不会影响整个系统。

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

我们可以使用 LXD来创建和管理这些容器。

在本教程中,您将使用LXD在相同的服务器上安装两个基于 Nginx的网站,每一个都限制在自己的容器上。然后您将安装HAProxy在第三个容器中,该容器将作为反向代理程序。

前提条件

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

步骤 1 – 将用户添加到lxd

使用非根用户帐户登录到服务器. 我们将使用非根用户帐户来执行所有容器管理任务. 要做到这一点,您必须先将该用户添加到lxd组中。

1sudo usermod --append --groups lxd sammy

退出服务器并再次登录,以便您的新的 SSH 会话将与新组成员更新。

步骤 2 – 配置 LXD

LXD 需要正确配置才能使用它 最重要的配置决定是存储容器的存储备份类型 LXD 推荐的存储备份是 ZFS 文件系统,要么存储在预分配的文件中,要么使用 Block Storage

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

安装后,您已经准备好初始化 LXD. 在初始化过程中,您将被要求指定 ZFS 存储后端的详细信息. 以下有两个部分,取决于您是否想要使用预分配的文件或区块存储。

选择1 – 使用预分配的文件

按照以下步骤配置 LXD 以使用预分配文件存储容器. 首先,执行以下命令开始 LXD 初始化过程:

1sudo lxd init

我们将选择所有默认值,包括预分配文件的建议大小,称为 loop 设备:

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

建议的大小从您的服务器的可用磁盘空间自动计算。

一旦设备已配置,您将配置网络设置,我们将在下一个可选部分之后探索。

选择2 – 使用区块存储

如果您要使用区块存储,您需要找到指向您创建的区块存储量的设备,以便在 LXD 配置中指定它。

通过查看命令来定义音量来查找设备。 具体来说,请寻找在sudo mkfs.ext4 -F命令中指定的路径。 下图显示了音量的一个示例。

The config instructions show the device for the created block storage.

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

一旦您确定了音量,请返回终端并发出以下命令,开始LXD初始化过程。

1sudo lxd init

您将被介绍一系列问题. 如下输出所示,回答问题:

1[secondary_label Output]
2Name of the storage backend to use (dir or zfs) [default=zfs]: zfs
3Create a new ZFS pool (yes/no) [default=yes]? yes
4Name of the new ZFS pool [default=lxd]: lxd

当您被提示使用现有区块设备时,选择并提供到您的设备的路径:

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

然后将默认值用于剩余的问题:

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

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

配置网络

初始化过程将向我们展示一系列如下图所示的屏幕,允许我们为容器配置网络桥梁,以便他们可以获得私人IP地址,彼此通信,并访问互联网。

LXD networking configuration

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

一旦您完成了网络配置,您已经准备好创建容器。

步骤三:创建集装箱

我们已经成功配置了 LXD. 我们已经指定了存储后端的位置,并为任何新创建的容器配置了默认网络。

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

1lxc list

您将看到以下输出:

1[secondary_label Output of the "lxd list" command]
2Generating a client certificate. This may take a minute...
3If this is your first time using LXD, you should also run: sudo lxd init
4To start your first container, try: lxc launch ubuntu:16.04
5
6+------+-------+------+------+------+-----------+
7| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
8+------+-------+------+------+------+-----------+

由于这是lxc命令首次与LXD超视图进行通信,所以输出告诉我们,该命令会自动创建一个客户端证书,以便与LXD进行安全的通信。

让我们创建三个容器,我们将为每个Web服务器创建一个,并为反向代理商创建第三个容器。

我们将使用lxc launch命令创建并启动一个名为web1的Ubuntu 16.04(ubuntu:x)容器。在ubuntu:x中的x是Xenial的第一个字母的缩写,Ubuntu 16.04的代码名称ubuntu:是LXD图像预配置库的标识符。

<$>[注] **注:您可以通过运行lxc 图像列表 ubuntu:和其他分布来找到所有可用的 Ubuntu 图像的完整列表,并通过运行lxc 图像列表图像:

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

1lxc launch ubuntu:x web1
2lxc launch ubuntu:x web2
3lxc launch ubuntu:x haproxy

因为这是我们第一次创建一个集装箱,所以第一个命令会从互联网下载集装箱图像并将其本地缓存。

在这里,您可以看到创建容器web1的样本输出。

1[secondary_label Output]
2Creating web1
3Retrieving image: 100%
4Starting web1

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

1lxc list

输出显示了每个容器的名称、当前状态、IP地址、类型以及是否有截图。

 1[label Output]
 2+---------+---------+-----------------------+------+------------+-----------+
 3|  NAME   |  STATE  |         IPV4          | IPV6 |    TYPE    | SNAPSHOTS |
 4+---------+---------+-----------------------+------+------------+-----------+
 5| haproxy | RUNNING | 10.10.10.10 (eth0)    |      | PERSISTENT | 0         |
 6+---------+---------+-----------------------+------+------------+-----------+
 7| web1    | RUNNING | 10.10.10.100 (eth0)   |      | PERSISTENT | 0         |
 8+---------+---------+-----------------------+------+------------+-----------+
 9| web2    | RUNNING | 10.10.10.200 (eth0)   |      | PERSISTENT | 0         |
10+---------+---------+-----------------------+------+------------+-----------+

请注意容器名称及其相应的IPv4地址,您将需要它们来配置您的服务。

步骤 4 – 配置 Nginx 容器

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

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

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

--字符串表示lxc的命令参数应该停留在那里,其余的行将作为在容器内部执行的命令传递。

<$>[注] 注: 如果您需要作为 root连接到容器,则可以使用命令 lxc exec web1 -- /bin/bash

一旦进入容器,我们的壳提示现在看起来如下。

1[secondary_label Output]
2[environment second]
3ubuntu@web1:~$

容器中的这个 ubuntu用户有预配置的sudo访问,并且可以运行sudo命令而无需提供密码。这个壳被限制在容器的边界内。

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

1[environment second]
2sudo apt-get update
3sudo apt-get install nginx

让我们编辑这个网站的默认网页,并添加一些文本,清楚地表明这个网站是在web1容器中托管的。

1[environment second]
2sudo nano /var/www/html/index.nginx-debian.html

对文件进行以下更改:

 1[label Edited file /var/www/html/index.nginx-debian.html]
 2<!DOCTYPE html>
 3<html>
 4<head>
 5<title>Welcome to nginx on LXD container web1!</title>
 6<style>
 7    body {
 8        width: 35em;
 9        margin: 0 auto;
10        font-family: Tahoma, Verdana, Arial, sans-serif;
11    }
12</style>
13</head>
14<body>
15<h1>Welcome to nginx on LXD container web1!</h1>
16<p>If you see this page, the nginx web server is successfully installed and
17working. Further configuration is required.</p>
18...

我们在两个地方编辑了该文件,并特别添加了文本LXD container web1

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

1[environment second]
2logout

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

让我们使用‘curl’来测试容器中的网页服务器是否正常工作,我们需要先前显示的网页容器的IP地址。

1curl http://10.10.10.100/

产量应该是:

 1[secondary_label Output of "curl http://10.10.10.100/" command]
 2<!DOCTYPE html>
 3<html>
 4<head>
 5<title>Welcome to nginx on LXD container web1!</title>
 6<style>
 7    body {
 8        width: 35em;
 9        margin: 0 auto;
10        font-family: Tahoma, Verdana, Arial, sans-serif;
11    }
12</style>
13</head>
14<body>
15<h1>Welcome to nginx on LXD container web1!</h1>
16<p>If you see this page, the nginx web server is successfully installed and
17working. Further configuration is required.</p>
18...

测试第二个容器,使用弯曲命令和其IP地址来验证它也设置正确. 随着两个容器配置,我们可以继续设置HAProxy。

步骤 5 – 配置 HAProxy 容器

我们将在这些容器前设置 HAProxy 作为代理程序。如果您需要更多关于此功能的背景,请参阅教程 HAProxy 和负载平衡概念的介绍。我们将根据我们使用的域名导向每个容器的流量。在下面的配置示例中,我们将使用域名example.com,这是一个 专门保留域作为本教程的文档。我们将把第一个网站提供给主机名称 example.comwww.example.com。第二个网站将位于 www2.example.com。 取代您的域名而不是这些域名。

登入「haproxy」容器:

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

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

1[environment fourth]
2sudo apt-get update
3sudo apt-get install haproxy

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

1[environment fourth]
2sudo nano /etc/haproxy/haproxy.cfg

我们将添加forwardfor选项,以便我们保留Web客户端的真实源IP,我们将添加http-server-close选项,这允许重新使用会话和更低的延迟。

 1[environment fourth]
 2[label /etc/haproxy/haproxy.conf]
 3global
 4...
 5defaults
 6    log	global
 7    mode	http
 8    option	httplog
 9    option	dontlognull
10    option 	forwardfor
11    option 	http-server-close
12    timeout connect 5000
13    timeout client 50000
14    timeout server 50000
15...

接下来,我们将配置前端以指向我们的两个后端容器. 添加一个名为www_frontend的新前端部分,看起来像这样:

 1[environment fourth]
 2[label /etc/haproxy/haproxy.conf]
 3frontend www_frontend
 4    bind *:80		# Bind to port 80 (www) on the container
 5
 6    # It matches if the HTTP Host: field mentions any of the hostnames (after the '-i').
 7    acl host_web1 hdr(host) -i example.com www.example.com
 8    acl host_web2 hdr(host) -i web2.example.com
 9
10    # Redirect the connection to the proper server cluster, depending on the match.
11    use_backend web1_cluster if host_web1
12    use_backend web2_cluster if host_web2

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

然后我们定义了两个新的后端部分,一个为每个Web服务器,并分别命名它们为web1_clusterweb2_cluster

 1[environment fourth]
 2[label /etc/haproxy/haproxy.conf]
 3backend web1_cluster
 4    balance leastconn
 5    # We set the X-Client-IP HTTP header. This is useful if we want the web server to know the real client IP.
 6    http-request set-header X-Client-IP %[src]
 7    # This backend, named here "web1", directs to container "web1.lxd" (hostname).
 8    server web1 web1.lxd:80 check
 9
10backend web2_cluster
11    balance leastconn
12    http-request set-header X-Client-IP %[src]
13    server web2 web2.lxd:80 check

平衡选项表示负荷平衡策略. 在这种情况下,我们选择了最少的连接数量。 http-request选项设置了一个HTTP标题与实际的Web客户端IP。如果我们没有设置这个标题,网络服务器将记录HAProxy IP地址作为所有连接的源IP,这使得分析您的流量来源更为困难。

LXD 為容器提供 DNS 伺服器,因此「web1.lxd」會解決與「web1」容器相關的 IP. 其他容器有自己的主機名稱,例如「web2.lxd」和「haproxy.lxd」。

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

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

1[environment fourth]
2/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -c

产量应该是

1[environment fourth]
2[label Output]
3Configuration file is valid

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

1[environment fourth]
2sudo systemctl reload haproxy

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

1[environment fourth]
2logout

我们已经配置了 HAProxy 作为一个反向代理,将其在端口 `80 上接收的任何连接转发到其他两个容器中的相应 Web 服务器。

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

这会向 HAProxy 提出请求,并设置一个 HTTP 主机 标题,HAProxy 应该使用它来重定向连接到相应的 Web 服务器。

产量应该是

 1[secondary_label Output of "curl --verbose --header 'Host: web2.example.com' http://10.10.10.10" command]
 2...
 3> GET / HTTP/1.1
 4> Host: web2.example.com
 5> User-Agent: curl/7.47.0
 6> Accept: */*
 7> 
 8...
 9< 
10<!DOCTYPE html>
11<html>
12<head>
13<title>Welcome to nginx on LXD container web2!</title>
14<style>
15...

HAProxy正确理解了请求,并将其转发到web2容器中,在那里,Web服务器服务于我们之前编辑的默认索引页面,并显示了文本在LXD容器上Web2

步骤 6 – 将接入连接传送到 HAProxy 容器

谜题的最后一部分是将反向代理连接到互联网,我们需要设置我们的服务器,将它可能从互联网接收的任何连接从端口80传送到haproxy容器。

HAProxy 安装在容器中,默认情况下是无法从互联网上访问的。 为了解决此问题,我们将创建一个iptables规则来传输连接。

iptables命令需要两个IP地址:服务器的公共IP地址(your_server_ip)和haproxy容器的私人IP地址(your_haproxy_ip),您可以通过lxc列表命令获取。

运行此命令来创建规则:

1sudo 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,这是Dropplets上的默认公共接口
  • -p TCP 表示我们正在使用 TCP 协议
  • -d your_server_ip/32 指明规则的目的地 IP 地址
  • --dport 80: 指明目的地端口
  • -j DNAT 表示我们想要执行跳跃到目的地 NAT (DNAT).(_

了解有关 IPTables 的更多信息,请参阅 How the Iptables Firewall WorksIPtables Essentials: Common Firewall Rules and Commands

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

1sudo apt-get install iptables-persistent

安装包时,您将被要求保存当前的 iptables 规则. 接受并保存所有当前的 'iptables' 规则。

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

要测试这两个 Web 服务器是否实际上可以从 Internet 访问,请使用弯曲命令从本地计算机访问它们:

1[environment local]
2curl --verbose --header 'Host: example.com' 'http://your_server_ip'
3curl --verbose --header 'Host: web2.example.com' 'http://your_server_ip'

这些命令将 HTTP 连接到服务器的公共 IP 地址,并添加一个 HTTP 标题字段与 `-- header' 选项,HAProxy 将用来处理请求,就像您在步骤 5 中所做的。

以下是第一个curl命令的输出:

 1[secondary_label Output]
 2*   Trying your_server_ip...
 3* Connected to your_server_ip (your_server_ip) port 80 (#0)
 4> GET / HTTP/1.1
 5> Host: example.com
 6> User-Agent: curl/7.47.0
 7> Accept: */*
 8> 
 9< HTTP/1.1 200 OK
10< Server: nginx/1.10.0 (Ubuntu)
11...
12<!DOCTYPE html>
13<html>
14<head>
15<title>Welcome to nginx on LXD container web1!</title>
16<style>
17    body {
18...

以下是第二个curl命令的输出:

 1[secondary_label Output]
 2*   Trying your_server_ip...
 3* Connected to your_server_ip (your_server_ip) port 80 (#0)
 4> GET / HTTP/1.1
 5> Host: web2.example.com
 6> User-Agent: curl/7.47.0
 7> Accept: */*
 8> 
 9< HTTP/1.1 200 OK
10< Server: nginx/1.10.0 (Ubuntu)
11...
12<!DOCTYPE html>
13<html>
14<head>
15<title>Welcome to nginx on LXD container web2!</title>
16<style>
17    body {
18...

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

结论

您已经设置了两个网站,每一个在自己的容器中,HAProxy导向流量. 您可以复制此过程,以配置更多网站,每一个仅限于自己的容器。

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

最后,LXD提供了拍摄集装箱完整状态的快照的能力,这使得更轻松地创建备份并在稍后重新滚动集装箱,此外,如果我们在两个不同的服务器上安装LXD,则可以连接它们并通过互联网迁移服务器之间的集装箱。

Published At
Categories with 技术
comments powered by Disqus