介绍
Linux容器是通过使用Linux内核安全功能(如命名空间和控制组)与系统其余部分隔离的进程分组。 它是一个类似于虚拟机的结构,但它的重量更轻; 您没有运行额外的内核或模拟硬件的开销。 这意味着您可以轻松地在同一台服务器上创建多个容器。 使用Linux容器,您可以运行整个操作系统的多个实例,限制在同一台服务器上,或将应用程序及其依赖项捆绑在容器中,而不会影响系统的其余部分。
例如,假设您有一台服务器,并为您的客户设置了几个服务,包括网站。 在传统的安装中,每个网站都将是Apache或Nginx Web服务器的同一个实例的虚拟主机。 但是使用Linux容器,每个网站都将在自己的容器中配置自己的Web服务器。
我们可以使用LXD来创建和管理这些容器。 LXD提供管理程序服务来管理容器的整个生命周期。
在本教程中,您将使用LXD在同一台服务器上安装两个基于Nginx的网站,每个网站都限于自己的容器。 然后,您将在第三个容器中安装HAProxy,作为反向代理。 然后,您将会将流量路由到HAProxy容器,以使两个网站都可以从Internet访问。
先决条件
要完成本教程,您需要以下内容:
- 一个Ubuntu 16.04服务器,通过遵循Ubuntu 16.04初始服务器安装程序配置 ,具有sudo非root用户和防火墙。
- 两个完全合格的域名(FQDN),每个DNS A记录指向服务器的IP地址。 要配置它,请按照如何使用DigitalOcean设置主机名的教程 。
- 可选地,通过遵循DigitalOcean Block Storage入门教程添加20GB或更多的块存储。 您可以使用它来存储与容器相关的所有数据。
第1步 - 将用户添加到lxd
组
使用非root用户帐户登录服务器。 我们将使用此非用户帐户来执行所有容器管理任务。 要使其正常工作,您必须先将此用户添加到lxd
组。 使用以下命令执行此操作:
sudo usermod --append --groups lxd sammy
退出服务器并重新登录,以便新的SSH会话将使用新的组成员资格进行更新。 登录后,您可以开始配置LXD。
第2步 - 配置LXD
在使用LXD之前,必须正确配置LXD。 最重要的配置决定是用于存储容器的存储后端的类型。 推荐的LXD存储后端是ZFS文件系统,存储在预先分配的文件中或通过使用块存储 。 要在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 - 使用块存储
如果要使用Block Storage,您需要找到指向您创建的块存储卷的设备,以便在LXD的配置中指定它。 转到DigitalOcean控制面板 l 中的卷选项卡,找到您的卷,单击更多弹出窗口,然后单击配置说明 。
通过查看命令格式化卷来找到设备。 具体来说,查找sudo mkfs.ext4 -F
命令中指定的路径。 下图显示了一个卷的示例。 你只需要下划线的部分:
[配置说明显示创建的块存储的设备。](httpss://assets.digitalocean.com/articles/lxd containers ubuntu_1604 / 6rDyC1l.png)
在这种情况下,卷的名称是/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地址,相互通信,并可以访问互联网。
[LXD网络配置](http://assets.digitalocean.com/articles/lxd containers ubuntu_1604 / u9D79uB.png)
使用每个选项的默认值,但是当询问IPv6网络时,选择否 ,因为我们在本教程中不会使用它。
完成网络配置后,您就可以创建容器了。
第3步 - 创建容器
我们已经成功配置了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: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
因为这是我们第一次创建一个容器,所以第一个命令从Internet下载容器映像并将其缓存在本地。 接下来的两个容器将被快速创建。
在这里,您可以看到创建容器web1
的示例输出。
OutputCreating web1
Retrieving image: 100%
Starting web1
现在我们已经创建了三个空的容器,我们使用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
--
string表示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
对文件进行以下更改:
<!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.com
,这是本教程中的文档的特殊保留域 。 我们将在主机名example.com
和www.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
选项,这样可以实现会话重用和更低的延迟。
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
部分,如下所示:
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_cluster
和web2_cluster。 将以下代码添加到文件中以定义后端:
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
选项表示负载balance
策略。 在这种情况下,我们选择的连接数量最少。 http-request
选项设置一个具有真实Web客户端IP的HTTP头。 如果我们没有设置此标题,则Web服务器将记录HAProxy IP地址作为所有连接的源IP,从而更难分析您的流量来源。 server
选项指定服务器( web1
)的任意名称,后跟服务器的主机名和端口。
LXD为容器提供一个DNS服务器,所以web1.lxd
解析为与web1
容器关联的IP。 其他容器有自己的主机名,如web2.lxd
和haproxy.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容器的传入连接
最后一个谜题是将反向代理连接到互联网。 我们需要设置我们的服务器,将其从端口80
上的任何连接转发到haproxy
容器。
HAProxy安装在容器中,默认情况下,无法从Internet访问。 为了解决这个问题,我们将创建一个iptables
规则来转发连接。
iptables
命令需要两个IP地址:服务器的公网IP地址( your_server_ip
)和haproxy
容器的私有IP地址( your_haproxy_ip
),您可以使用lxc list
命令获取该IP地址。
执行此命令创建规则:
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 ,它是Droplet上的默认公共接口。 -
-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防火墙的工作原理和IPtables Essentials:常见的防火墙规则和命令 。
最后,要保存这个iptables
命令,以便在重新启动后重新应用,我们安装iptables-persistent
包:
sudo apt-get install iptables-persistent
安装软件包时,系统将提示您保存当前的iptables规则。 接受并保存所有当前的iptables
规则。
如果您已经设置了两个FQDN,那么您应该可以使用Web浏览器连接到每个网站。 试试看。
要测试两个Web服务器实际上可以从Internet访问,请使用curl
命令从本地计算机访问每个Web服务器:
curl --verbose --header 'Host: example.com' 'http://your_server_ip'
curl --verbose --header 'Host: web2.example.com' 'http://your_server_ip'
这些命令使HTTP连接到服务器的公共IP地址,并使用Hheadroxy将用于处理请求的--header
选项添加HTTP头字段,就像您在第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,那么您可以在容器( lxc launch ubuntu:t
)中安装Ubuntu 14.04,而不是尝试降级Ubuntu 16.04上提供的软件包管理器版本。
最后,LXD能够对容器的完整状态进行快照,这样可以在稍后的时间内轻松创建备份和卷容器。 另外,如果我们在两个不同的服务器上安装LXD,那么可以通过互联网连接他们并在服务器之间迁移容器。