介绍
你已经写了你的应用程序,现在你需要部署它。你可以创建一个生产环境并将你的应用程序设置在虚拟机上,但当它变得流行时,你如何扩展它?你如何推出新版本?负载平衡怎么样?最重要的是,你如何确定配置是正确的?我们可以自动化所有这一切来节省很多时间。
在本教程中,我们将向您展示如何在 Salt Cloud 地图文件中定义您的应用程序,包括使用自定义 Salt Grains 将角色分配给您的服务器,并动态配置反向代理。
在本教程结束时,您将有两个基本的应用程序服务器,一个具有动态构建配置的Nginx反向代理,以及在几分钟内扩展您的应用程序的能力。
前提条件
要遵循本教程,您将需要:
- One 1 GB CentOS 7 Droplet. 本教程中的所有命令将作为 root 执行,所以您不需要创建一个 sudo 非 root 用户。
- 一个 SSH 密钥在 Droplet 上为 root 用户(即创建在 Droplet 上的新密钥对)。 将这个 SSH 密钥添加到 DigitalOcean 控制面板中,以便您可以使用它从主 Droplet 登录到其他 DigitalOcean Droplets。 您可以使用 如何使用 SSH 密钥与 DigitalOcean Droplets 教程以提供指示。 请注意您在 DigitalOcean 控制面板中分配给密钥的名称。 在本教程中,我们使用盐主 - 根密钥的名
步骤1:安装盐和盐云
要开始,您需要在您的服务器上安装和配置 Salt Cloud. 对于本教程,我们只会使用 Salt bootstrap 脚本。
首先,绘制 Salt bootstrap 脚本来安装 Salt。
1wget -O install_salt.sh https://bootstrap.saltstack.com
运行 Salt bootstrap 脚本. 我们使用-M
旗帜来安装salt-master
。
1sh install_salt.sh -M
虽然 Salt Cloud 代码库已合并到核心 Salt 项目中,但它仍然为 CentOS 单独包装。 幸运的是,install_salt 脚本为我们配置了 repos,所以我们可以用 yum 安装salt-cloud
:
1yum install salt-cloud
现在我们可以检查 Salt Cloud 的版本,以确认成功安装:
1salt-cloud --version
你应该看到这样的输出:
1[label salt-cloud --version output]
2salt-cloud 2015.5.3 (Lithium)
请注意, Salt 处于滚动释放周期,因此您的版本可能略有不同。
步骤 2 – 配置盐云
在本节中,我们将配置盐云连接到DigitalOcean,并为我们的Droplets定义一些配置文件。
配置 DigitalOcean Provider 文件
在 Salt Cloud 中, provider 文件 是您定义新 VM 将被创建的地方。
使用nano
或您最喜欢的文本编辑器创建和打开 DigitalOcean 提供商文件。
1nano /etc/salt/cloud.providers.d/digital_ocean.conf
插入下面的文本,将红色变量替换为您的,即服务器 IP 和访问代码,以及 SSH 密钥名称和文件,如果您定制了它们。
1[label /etc/salt/cloud.providers.d/digital_ocean.conf]
2### /etc/salt/cloud.providers.d/digital_ocean.conf ###
3######################################################
4do:
5 provider: digital_ocean
6 minion:
7 master: your_server_ip
8
9 # DigitalOcean Access Token
10 personal_access_token: your_access_token
11
12 # This is the name of your SSH key in your Digital Ocean account
13 # as it appears in the control panel.
14 ssh_key_name: salt-master-root-key
15
16 # This is the path on disk to the private key for your Digital Ocean account
17 ssh_key_file: /root/.ssh/id_rsa
您需要锁定 SSH 密钥文件上的权限,否则 SSH 将拒绝使用它,假设您的默认位置是 `/root/.ssh/id_rsa',您可以通过:
1chmod 600 /root/.ssh/id_rsa
配置可部署服务器的配置文件
在 Salt Cloud 中, _profiles 是与提供商相关的单个 VM 描述(例如DigitalOcean上的 512 MB Ubuntu VM
).这些在 `/etc/salt/cloud.profiles.d’ 目录中定义。
创建和打开一个档案文件。
1nano /etc/salt/cloud.profiles.d/digital_ocean.conf
请将下列内容插入文件中,无需更改:
1[label /etc/salt/cloud.profiles.d/digital_ocean.conf]
2### /etc/salt/cloud.profiles.d/digital_ocean.conf ###
3#####################################################
4
5ubuntu_512MB_ny3:
6 provider: do
7 image: ubuntu-14-04-x64
8 size: 512MB
9 location: nyc3
10 private_networking: True
11
12ubuntu_1GB_ny3:
13 provider: do
14 image: ubuntu-14-04-x64
15 size: 1GB
16 location: nyc3
17 private_networking: True
保存和关闭文件. 此文件定义了两个配置文件:
- 具有 512 MB 内存的 Ubuntu 14.04 VM,部署在纽约 3 地区。
- 具有 1 GB 内存的 Ubuntu 14.04 VM,部署在纽约 3 地区。
如果您想使用 Ubuntu 14.04 以外的图像,您可以使用 Salt Cloud 列出 DigitalOcean 上所有可用的图像名称,使用以下命令:
1salt-cloud --list-images do
这将显示所有标准的DigitalOcean图像,以及您使用快照工具在帐户中保存的自定义图像。您可以将我们在提供商文件中使用的图像名称或区域替换为来自此列表的不同图像名称。
通过快速查询测试您的配置。
1salt-cloud -Q
你应该看到如下的一些东西。
1[label Example salt-cloud -Q output]
2[INFO ] salt-cloud starting
3do:
4 ----------
5 digital_ocean:
6 ----------
7 centos-salt:
8 ----------
9 id:
10 2806501
11 image_id:
12 6372108
13 public_ips:
14 192.241.247.229
15 size_id:
16 63
17 state:
18 active
这意味着Salt Cloud正在与您的DigitalOcean帐户交谈,您已配置了两个基本配置文件。
步骤三:写一个简单的地图文件
地图文件是一个YAML文件,列出了您想要创建的配置文件和服务器数量,我们将从一个简单的地图文件开始,然后在下一节中建立。
使用上述配置文件,假设您想要两个 1 GB 应用程序服务器,其中有一个单一的 512 MB 反向代理. 我们将创建一个名为 /etc/salt/cloud.maps.d/do-app-with-rproxy.map
的地图文件,并定义应用程序。
首先,创建文件:
1nano /etc/salt/cloud.maps.d/do-app-with-rproxy.map
请输入以下文本,无需更改:
1[label /etc/salt/cloud.maps.d/do-app-with-rproxy.map]
2### /etc/salt/cloud.maps.d/do-app-with-rproxy.map ####
3######################################################
4ubuntu_512MB_ny3:
5 - nginx-rproxy
6
7ubuntu_1GB_ny3:
8 - appserver-01
9 - appserver-02
这就是它! 它就像一个地图文件一样简单。 继续部署这些服务器:
1salt-cloud -m /etc/salt/cloud.maps.d/do-app-with-rproxy.map
一旦命令完成,用快速的 ping 确认成功:
1salt '*' test.ping
你应该看到以下:
1[label salt '*' test.ping
2appserver-01:
3 True
4appserver-02:
5 True
6nginx-rproxy:
7 True
一旦您在地图文件中成功创建了 VM,删除它们同样容易:
1salt-cloud -d -m /etc/salt/cloud.maps.d/do-app-with-rproxy.map
但是,一定要小心使用这个命令!它会删除该地图文件中指定的 all VMs。
第四步 - 写一个更现实的地图文件
该地图文件工作得很好,但即使是一个壳脚本也可以旋转一组 VM. 我们需要的是定义我们的应用程序的足迹. 让我们回到我们的地图文件并添加一些更多东西;再次打开地图文件。
1nano /etc/salt/cloud.maps.d/do-app-with-rproxy.map
删除文件的以前内容,并将下列内容放入文件中,无需更改:
1[label /etc/salt/cloud.maps.d/do-app-with-rproxy.map]
2### /etc/salt/cloud.maps.d/do-app-with-rproxy.map ###
3#####################################################
4ubuntu_512MB_ny3:
5 - nginx-rproxy:
6 minion:
7 mine_functions:
8 network.ip_addrs:
9 interface: eth0
10 grains:
11 roles: rproxy
12ubuntu_1GB_ny3:
13 - appserver-01:
14 minion:
15 mine_functions:
16 network.ip_addrs:
17 interface: eth0
18 grains:
19 roles: appserver
20 - appserver-02:
21 minion:
22 mine_functions:
23 network.ip_addrs:
24 interface: eth0
25 grains:
26 roles: appserver
现在我们在某个地方! 看起来很多,但我们只添加了两件事. 让我们来谈谈两个添加:‘mine_functions’部分和‘grains’部分。
我们已经告诉 Salt Cloud 修改这些 VM 的 Salt Minion 配置,并添加一些自定义 谷物. 具体来说,谷物给了反向代理 VM 的rproxy
角色,并给了应用服务器的appserver
角色。
mine_functions
也将添加到 Salt Minion 配置中,它指示 Minion 将在 eth0 上找到的 IP 地址发送回 Salt Master 以存储在 Salt Mine。
步骤五:定义反向代理
我们现在有一个共同的任务:安装反向代理网络服务器并配置它. 对于本教程,我们将使用 Nginx 作为反向代理。
编写 Nginx 盐国
是时候把我们的手弄脏了,写一些盐状态。首先,做默认的盐状态树的位置:
1mkdir /srv/salt
导航到该目录并为 nginx 创建另一个目录:
1cd /srv/salt
2mkdir /srv/salt/nginx
进入该目录,并使用您最喜欢的编辑器创建一个名为rproxy.sls
的新文件:
1cd /srv/salt/nginx
2nano /srv/salt/nginx/rproxy.sls
将下列内容放入该文件中,无需更改:
1[label /srv/salt/nginx/rproxy.sls]
2### /srv/salt/nginx/rproxy.sls ###
3##################################
4
5### Install Nginx and configure it as a reverse proxy, pulling the IPs of
6### the app servers from the Salt Mine.
7
8nginx-rproxy:
9 # Install Nginx
10 pkg:
11 - installed
12 - name: nginx
13 # Place a customized Nginx config file
14 file:
15 - managed
16 - source: salt://nginx/files/awesome-app.conf.jin
17 - name: /etc/nginx/conf.d/awesome-app.conf
18 - template: jinja
19 - require:
20 - pkg: nginx-rproxy
21 # Ensure Nginx is always running.
22 # Restart Nginx if the config file changes.
23 service:
24 - running
25 - enable: True
26 - name: nginx
27 - require:
28 - pkg: nginx-rproxy
29 - watch:
30 - file: nginx-rproxy
31 # Restart Nginx for the initial installation.
32 cmd:
33 - run
34 - name: service nginx restart
35 - require:
36 - file: nginx-rproxy
这个国家做的是如下:
- 安装 Nginx.
- 将我们的自定义配置文件放入
/etc/nginx/conf.d/awesome-app.conf
. - 确保 Nginx 运行。
我们的盐状态简单地安装 Nginx 并放下 config 文件;真正有趣的内容在 config 中。
编写 Nginx 反向代理配置文件
让我们为我们的 config 文件创建另一个目录:
1mkdir /srv/salt/nginx/files
2cd /srv/salt/nginx/files
然后打开 config 文件:
1nano /srv/salt/nginx/files/awesome-app.conf.jin
不要修改,除非您 不使用私人网络;在这种情况下,如列中所示,更改1
为0
:
1[label /srv/salt/nginx/files/awesome-app.conf.jin]
2### /srv/salt/nginx/files/awesome-app.conf.jin ###
3##################################################
4
5### Configuration file for Nginx to act as a
6### reverse proxy for an app farm.
7
8# Define the app servers that we're in front of.
9upstream awesome-app {
10 {% for server, addrs in salt['mine.get']('roles:appserver', 'network.ip_addrs', expr_form='grain').items() %}
11 server {{ addrs[0] }}:1337;
12 {% endfor %}
13}
14
15# Forward all port 80 http traffic to our app farm, defined above as 'awesome-app'.
16server {
17 listen 80;
18 server_name {{ salt['network.ip_addrs']()[1] }}; # <-- change the '1' to '0' if you're not using
19 # DigitalOcean's private networking.
20
21 access_log /var/log/nginx/awesome-app.access.log;
22 error_log /var/log/nginx/awesome-app.error.log;
23
24 ## forward request to awesome-app ##
25 location / {
26 proxy_pass http://awesome-app;
27 proxy_set_header Host $host;
28 proxy_set_header X-Real-IP $remote_addr;
29 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
30 }
31}
我们使用.jin 扩展来告诉自己文件包含 Jinja 模板. Jinja 模板允许我们将少量逻辑放入我们的文本文件中,这样我们就可以动态地生成配置细节。
此配置文件指示 Nginx 接收所有端口 80 HTTP 流量并将其传送到我们的应用程序农场. 它有两个部分:一个上游(我们的应用程序农场)和配置作为用户和我们的应用程序农场之间的代理。
让我们谈谈上游. 一个正常的,非模板上游指定了IP的集合. 然而,我们不知道我们的小伙伴的IP地址将是什么,直到它们存在,我们不会手动编辑配置文件(否则,没有理由使用盐!)
还记得我们地图文件中的‘mine_function’ 行吗?小伙子们正在把他们的 IP 交给盐大师来存储,仅仅是为了这样的机会。
1[label Jinja excerpt]
2{% for server, addrs in salt['mine.get']('roles:appserver', 'network.ip_addrs', expr_form='grain').items() %}
这是 Jinja 中的前环,运行任意 Salt 函数. 在这种情况下,它正在运行 mine.get
。
roles:appserver
- 这表示只从具有appserver
角色的小伙子那里获取细节。network.ip_addrs
- 这是我们想要从矿井中获取的数据。我们在我们的地图文件中也指定了这一点。
按照这个循环,变量 `{addrs}} 包含一个 IP 地址列表(即使它只是一个地址)。
至于服务器的名称:
1server_name {{ salt['network.ip_addrs']()[0] }};
这是与盐矿调用相同的技巧(在Jinja中调用盐函数)。它更简单。它是调用 network.ip_addrs
并采取返回列表的第一个元素。
步骤六:定义应用程序农场
让我们创建一个小Node.js应用程序,它只是报告服务器的IP(所以我们可以确认我们正在到达两台机器)。
创建一个名为wesome-app
的新目录,然后移动到那里。
1mkdir -p /srv/salt/awesome-app
2cd /srv/salt/awesome-app
创建一个名为app.sls
的新应用状态文件。
1nano /srv/salt/awesome-app/app.sls
将下列内容放入文件中,无需更改:
1[label /srv/salt/awesome-app/app.sls]
2### /srv/salt/awesome-app/app.sls ###
3#####################################
4
5### Install Nodejs and start a simple
6### web application that reports the server IP.
7
8install-app:
9 # Install prerequisites
10 pkg:
11 - installed
12 - names:
13 - node
14 - npm
15 - nodejs-legacy # workaround for Debian systems
16 # Place our Node code
17 file:
18 - managed
19 - source: salt://awesome-app/files/app.js
20 - name: /root/app.js
21 # Install the package called 'forever'
22 cmd:
23 - run
24 - name: npm install forever -g
25 - require:
26 - pkg: install-app
27
28run-app:
29 # Use 'forever' to start the server
30 cmd:
31 - run
32 - name: forever start app.js
33 - cwd: /root
这个国家档案执行如下:
- 安裝「nodejs」、「npm」和「nodejs-legacy」套件。
- 添加將成為我們簡單的應用程式的 JavaScript 檔案。
- 使用 NPM 安裝
Forever
。
现在创建(小)应用程序代码:
1mkdir /srv/salt/awesome-app/files
2cd /srv/salt/awesome-app/files
创建文件:
1nano /srv/salt/awesome-app/files/app.js
请将以下内容列入其中,无需更改:
1[label /srv/salt/awesome-app/files/app.js]
2/* /srv/salt/awesome-app/files/app.js
3
4 A simple Node.js web application that
5 reports the server's IP.
6 Shamefully stolen from StackOverflow:
7 http://stackoverflow.com/questions/10750303/how-can-i-get-the-local-ip-address-in-node-js
8*/
9
10var os = require('os');
11var http = require('http');
12
13http.createServer(function (req, res) {
14 var interfaces = os.networkInterfaces();
15 var addresses = [];
16 for (k in interfaces) {
17 for (k2 in interfaces[k]) {
18 var address = interfaces[k][k2];
19 if (address.family == 'IPv4' && !address.internal) {
20 addresses.push(address.address)
21 }
22 }
23 }
24
25 res.writeHead(200, {'Content-Type': 'text/plain'});
26 res.end(JSON.stringify(addresses));
27}).listen(1337, '0.0.0.0');
28console.log('Server listening on port 1337');
这是一个简单的 Node.js 服务器,它只做了一件事:在端口 1337 上接受 HTTP 请求,并使用服务器的 IP 响应。
在此时刻,您应该有一个看起来如下的文件结构:
1[label File structure]
2/srv/salt
3 ├── awesome-app
4 │ ├── app.sls
5 │ └── files
6 │ └── app.js
7 └── nginx
8 ├── rproxy.sls
9 └── files
10 └── awesome-app.conf.jin
第七步:部署应用程序
剩下的只是部署应用程序。
使用盐云部署服务器
运行以前的相同部署命令,现在将使用我们已经创建的所有配置。
1salt-cloud -m /etc/salt/cloud.maps.d/do-app-with-rproxy.map
等待 Salt Cloud 完成;这将需要一段时间。一旦返回,通过 ping 应用程序服务器来确认成功的部署:
1salt -G 'roles:appserver' test.ping
你应该看到:
1[label App server ping output]
2appserver-02:
3 True
4appserver-01:
5 True
使用反向 proxy:
1salt -G 'roles:rproxy' test.ping
你应该看到:
1[label Reverse proxy ping output]
2nginx-rproxy:
3 True
一旦你有你的VM,是时候让他们工作了。
构建应用程序
接下来,发出 Salt 命令来自动构建应用程序农场和反向代理。
创建 app 农场:
1salt -G 'roles:appserver' state.sls awesome-app.app
将有公平的输出量,但它应该以以下方式结束:
1Summary
2------------
3Succeeded: 6 (changed=6)
4Failed: 0
5------------
6Total states run: 6
创建反向 proxy:
1salt -G 'roles:rproxy' state.sls nginx.rproxy
再次,将有一个公平的产量,以以下为结尾:
1Summary
2------------
3Succeeded: 4 (changed=4)
4Failed: 0
5------------
6Total states run: 4
那么,这里刚刚发生了什么?
第一個命令(與應用程式伺服器的命令)採取了我們之前寫的鹽狀態,並在兩個應用程式伺服器上執行它,這導致兩台具有相同配置的機器執行相同版本的代碼。
第二个命令(反向代理)执行了我们为 Nginx 编写的盐状态. 它安装了 Nginx 和配置文件,在 config 文件中动态填写了我们应用程序农场的 IP。
一旦这些 Salt 运行完毕,您可以测试以确认成功部署. 查找您的反向代理的 IP:
1salt -G 'roles:rproxy' network.ip_addrs
如果您在 Droplet 上使用私人网络,您可能会获得两个 IP。
将公共 IP 插入您的浏览器并访问网页! 点击更新几次以确认 Nginx 实际上是在您构建的两个应用程序服务器之间进行代理。
如果你得到相同的IP,尽管更新,它可能是由于浏览器缓存。你可以尝试使用弯曲
,而不是显示 Nginx在你的应用程序服务器之间进行代理。
1curl http://ip-of-nginx-rproxy
我们可以进一步采取几个步骤,并通过 OverState完全自动部署应用程序,这将使我们能够构建一个单一的命令,告诉Salt先构建,例如,应用程序服务器,然后继续构建反向代理,从而保证我们构建过程的顺序。
步骤八 – 扩展(可选)
使用 Salt 的目的在于自动化您的构建过程;使用 Salt Cloud 和地图文件的目的在于轻松扩展您的部署。
1[label /etc/salt/cloud.maps.d/do-app-with-rproxy.map]
2### /etc/salt/cloud.maps.d/do-app-with-rproxy.map ###
3#####################################################
4ubuntu_512MB_ny3:
5 - nginx-rproxy:
6 minion:
7 mine_functions:
8 network.ip_addrs:
9 interface: eth0
10 grains:
11 roles: rproxy
12ubuntu_1GB_ny3:
13- appserver-01:
14 minion:
15 mine_functions:
16 network.ip_addrs:
17 interface: eth0
18 grains:
19 roles: appserver
20- appserver-02:
21 minion:
22 mine_functions:
23 network.ip_addrs:
24 interface: eth0
25 grains:
26 roles: appserver
27- appserver-03:
28 minion:
29 mine_functions:
30 network.ip_addrs:
31 interface: eth0
32 grains:
33 roles: appserver
34- appserver-04:
35 minion:
36 mine_functions:
37 network.ip_addrs:
38 interface: eth0
39 grains:
40 roles: appserver
完成此更新后,您将重新运行 salt-cloud 命令和步骤 6 中的两个 salt 命令。
1salt-cloud -m /etc/salt/cloud.maps.d/do-app-with-rproxy.map
2salt -G 'roles:appserver' state.sls awesome-app.app
3salt -G 'roles:rproxy' state.sls nginx.rproxy
现有服务器不会受到重复 Salt 运行的影响,新的服务器将被构建为 spec, Nginx 配置将更新以开始向新的应用服务器路由流量。
结论
部署一个仅仅报告服务器的IP的应用程序并不非常有用。幸运的是,这种方法不限于Node.js应用程序。
如果你想采用这个框架来部署自己的应用程序,你只需要自动化在服务器上安装你的应用程序的任务(通过脚本或使用盐状态),并用自己的自动化代替我们的惊人的应用程序示例。
正如 Salt 在您的 Droplets 上自动化流程一样, Salt Cloud 在您的云中自动化流程。