如何部署在Ubuntu 14.04 Node.js的一个应用程序中使用Terraform

学会使用Terraform,很简单,但功能强大的工具,可以让你写你的栈的代码。在本教程中,您将部署的Node.js应用DigitalOcean然后探索Terraform是如何工作的,所以你可以建立自己的配置。

介绍

随着业务流程工具的帮助下,DevOps的专业人士可以通过利用几个API调用部署一个。 Terraform是一个非常简单,但功能强大的工具,可以让你写你的代码,然后分享,使它保持最新通过承诺使用定义文件GIT中 。 Terraform是通过创建HashiCorp ,流行的开源工具 。 Terraform提供了通用配置来启动基础架构,从物理和虚拟服务器到电子邮件和DNS提供程序。一旦启动,Terraform将随着配置的变化安全,高效地更改基础架构。 本教程介绍了如何建立一个环境,一个功能齐全,精良的Node.js使用应用程序DigitalOceanTerraform云初始化PM2在Ubuntu 14.04。 正如我们的示例应用程序,我们将使用 ,一个开源阵营终极版的Node.js应用程序开发的GetStream.io 。最终的输出将是一个功能丰富,可扩展的社交网络应用程序! 您将首先使用Terraform使用预定义配置部署Cabin。然后,您将深入了解该配置,以便您可以熟悉它的工作原理。 如果你只在你DigitalOcean服务器上安装Terraform感兴趣,请参阅如何与DigitalOcean使用Terraform

先决条件

要遵循本教程,您需要:
  • 一个2 GB的Ubuntu 14.04服务器,您将在本教程中使用Terraform创建。
  • Git的安装在本地计算机上的客户端。
  • 一个Facebook帐户,所以你可以创建一个Facebook应用程序,因为Cabin使用Facebook登录。
  • 域如cabin.example.com ;您会将此网域指向您将在第4步中获取的IPv4地址,并且您需要在Facebook中的网站网址。
虽然不是必需的,本教程假设你已经完成Stream的小屋系列教程 。您需要为Cabin在生产环境中工作所必需的几个提供者的API密钥和设置,因为它们在Cabin的功能中起着不可或缺的作用。 如果你没有获得这些键,本教程仍然可以工作。您仍然可以使用Terraform配置和部署Cabin应用程序,但是在配置其所有必需组件之前,该应用程序将不可用。 有关这些服务的其他信息,请随时访问以下来自Stream的博文:

第1步 - 获取示例应用程序

克隆从机舱示例应用程序的GitHub到您选择的本地机器上的某个目录。我们使用Mac,并假设你也是。 首先,导航到您的主目录。
cd ~
然后使用git克隆库:
git clone https://github.com/GetStream/stream-react-example.git
该克隆示例应用程序名为新文件夹stream-react-example 。 导航到stream-react-example/terraform/do/cabin ,其中包含客舱的Terraform项目文件夹。
cd stream-react-example/terraform/do/cabin
我们将使用这个文件夹有点。但首先,让我们设置Terraform。

第2步 - 安装Terraform

有关OSX简单安装,您可以使用安装Terraform 自制发出以下命令:
brew install terraform
或者,您也可以下载Terraform http://terraform.io 。下载后,将其提供给您的命令路径,如下所示。
PATH=location/of/terraform:$PATH
这会临时将Terraform添加到您的路径中。如果你想这种变化是永久性的,编辑文件~/.bash_profile的OSX和添加此行:
〜/ .bash_profile
export PATH=location/of/terraform:$PATH
接下来,要检查Terraform是否已正确安装,请运行以下命令:
terraform
您将看到以下输出,显示Terraform的选项:
usage: terraform [--version] [--help] <command> [<args>]

Available commands are:
    apply       Builds or changes infrastructure
    destroy     Destroy Terraform-managed infrastructure
    fmt         Rewrites config files to canonical format
    get         Download and install modules for the configuration
    graph       Create a visual graph of Terraform resources
    init        Initializes Terraform configuration from a module
    output      Read an output from a state file
    plan        Generate and show an execution plan
    push        Upload this Terraform module to Atlas to run
    refresh     Update local state file against real resources
    remote      Configure remote state storage
    show        Inspect Terraform state or plan
    taint       Manually mark a resource for recreation
    untaint     Manually unmark a resource as tainted
    validate    Validates the Terraform files
    version     Prints the Terraform version
在Terraform启动您的基础设施之前,我们需要配置两个东西:
  1. 数字海洋令牌
  2. SSH密钥对
所以,让我们先处理DigitalOcean令牌。

第2步 - 配置DigitalOcean接入令牌

Terraform需要您的DigitalOcean访问令牌才能使用DigitalOcean API。 登录到您的帐户DigitalOcean并点击链接API。 然后点击生成新令牌按钮。 一定要检查的写入权限 。用户界面将显示一个新的访问密钥,您应该将其复制到剪贴板,因为如果您再次访问该页面,该密钥将不可见。 现在打开文件variables.tf用你喜欢的文本编辑器,找到token部分:
变量
variable "token" {
  description = "DO Token"
}
开始添加与文本换行default = ,包括您的DigitalOcean API令牌。记住用引号括住令牌。
变量
variable "token" {
  description = "DO Token"
  default = "57eaa5535910eae8e9359c0bed4161c895c2a40284022cbd2240..."
}
保存并关闭文件。 现在让我们配置Terraform使用我们的SSH密钥对。

第3步 - 添加您的SSH密钥对

Terraform需要一个SSH密钥才能连接到我们的服务器,所以它可以安装软件包并部署应用程序。 看在你~/.ssh目录中看到,如果你已经有一个密钥对:
ls -al ~/.ssh
很可能,您至少有一个密钥对由私钥和公钥组成。例如,你可能有id_rsa.pubid_rsa警告 :如果您现有的密钥对已与您的DigitalOcean帐户关联,则需要使用DigitalOcean仪表板将其删除,或生成一个新的,以避免冲突。 如果您没有任何密钥对,或者如果你有钥匙已经与您DigitalOcean帐户相关联,那么请看看DigitalOcean的教程上设置SSH密钥来设置一个。 您需要将内容粘贴.pub文件到variables.tf文件,就像你与API令牌一样。如果您使用的是Mac,则可以通过发出以下命令将SSH公钥复制到剪贴板:
pbcopy < ~/.ssh/your_key.pub
您还可以显示公钥的内容与画面cat命令,并将其手动复制到剪贴板:
cat  ~/.ssh/your_key.pub
然后打开该文件variables.tf在编辑器中,并添加您的SSH公钥文件的内容sshkey设置:
变量
variable "sshkey" {
  description = "Public ssh key (for Cabin user)"
  default = "ssh-rsa AAAAB3NzaC1yc2EAAAADA...== nick@getstream.io"
}
完成此步骤后,保存并退出文件。 如果您生成了用于Terraform和DigitalOcean的新密钥,则需要运行这两个命令,以便使用新密钥而不是默认密钥:
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/your_id_rsa
如果您使用备用密钥对,则可能需要在每次打开新shell时运行此命令。 现在,您已经向Terraform提供了所需的变量,您就可以创建服务器并使用Terraform部署您的应用程序。

第4步 - 运行Terraform

这里来了有趣的部分!让我们来看看我们将要构建的基础设施。 Terraform将为我们做许多工作,从设置我们的服务器到部署我们的应用程序。我们可以让Terraform告诉我们它将如何使用以下命令:
terraform plan
此命令的输出非常详细,因此请尝试关注以下语句:
+ digitalocean_droplet.cabin-web
...
+ digitalocean_floating_ip.cabin-web-ip
...
+ digitalocean_ssh_key.cabin-ssh-key
...
+ template_file.pm2_processes_conf
...
+ template_file.userdata_web
...
行开头的“+”符号表示将创建资源。前缀资源digitalocean是将DigitalOcean创建的资源。在这种特殊情况下,Terraform将创建一个Droplet,一个浮动IP,并将添加我们的SSH密钥。 警告我们是不负责的,而你的实例(S)或第三方服务在线,可能会产生费用。 该命令terraform apply将创建具有2GB的RAM(〜$为0.03 /小时)和DigitalOcean提供免费的浮动IP一Droplet。 对于确切的数字,请仔细检查DigitalOcean网站上的更新价格。 现在是时候运行Terraform并在你的Droplet上旋转Cabin。
terraform apply
短时间后,您将看到Terraform打印出以下内容:
Apply complete! Resources: 6 added, 0 changed, 0 destroyed.

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path: terraform.tfstate

Expected output:

  web_ipv4 = 111.111.111.111
web_ipv4是你可以用它来接入到Droplet浮动IP地址。 登录到新创建的Droplet用你看到的值web_ipv4
ssh cabin@your_value_for_web_ipv4
您也可以使用命令
terraform output web_ipv4
以显示与该值相关联的IP地址(如果您错过了该值)。 您将在登录时看到此欢迎消息:
   _____      _     _
  / ____|    | |   (_)
 | |     __ _| |__  _ _ __
 | |    / _` | '_ \| | '_ \
 | |___| (_| | |_) | | | | |
  \_____\__,_|_.__/|_|_| |_|

Initializing Cabin. Please wait... (up 1 minute) | CTRL+C to interrupt
您可能需要等待几分钟DigitalOcean到规定的实例和cloud-init安装需要的包舱。但一旦准备好了,你会看到:
Cabin initialized!
Check running processes...
┌──────────┬────┬──────┬───────┬────────┬─────────┬────────┬─────────────┬──────────┐
│ App name │ id │ mode │ pid   │ status │ restart │ uptime │ memory      │ watching │
├──────────┼────┼──────┼───────┼────────┼─────────┼────────┼─────────────┼──────────┤
│ api      │ 0  │ fork │ 14105 │ online │ 0       │ 36s    │ 75.898 MB   │  enabled │
│ app      │ 1  │ fork │ 14112 │ online │ 0       │ 36s    │ 34.301 MB   │  enabled │
│ www      │ 2  │ fork │ 14119 │ online │ 0       │ 36s    │ 50.414 MB   │  enabled │
└──────────┴────┴──────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘
 Use `pm2 show <id|name>` to get more details about an app

一旦机舱启动并运行,将您的手机浏览器http:// your_value_for_web_ipv4 。客舱是活的,你应该看到一个加载屏幕。但是,只要我们对服务器上的代码进行一些更改,我们就会得到。

第5步 - (可选)配置机柜

已部署了Cabin应用程序,但它尚无法使用。如果我们想让Cabin完全运行,我们必须配置Facebook和其他一些服务。 首先,你需要创建使用有效的域名Facebook的应用程序,像cabin.example.com被映射到web_ipv4这是在安装过程中生成的地址。 一个记录添加到您的DNS或条目添加到您的/etc/hosts ,你的域名映射到IP地址的文件。 要创建Facebook应用程序,请按照下列步骤操作:
  1. 访问https://developers.facebook.com/docs/apps/register#step-by-step-guide
  2. 登录Facebook。
  3. 在我的应用程序,单击添加新的应用程序
  4. 为您的应用程序(例如输入一个名称Cabin - My Example App )。
  5. 输入您的联系人电子邮件
  6. 对于A ,使用下拉菜单选择应用程序的类别。 在我们的例子,它的生活方式
  7. 单击创建的App ID按钮。
  8. 如果需要,请填写验证码。
  9. 复制appId 。它将是在屏幕顶部找到的数值。你很快就需要了。
  10. 从左边栏中选择信息中心
  11. 开始使用Facebook的SDK的标题,点击选择的平台
  12. 选择Web上的平台。
  13. 找到站点URL字段,然后输入http://cabin.example.com
  14. 单击下一步
如果你遇到问题,你可以按照这个一步一步的指导 。 如果您遇到问题,有关于调试Facebook上的应用程序设置,可以找到一个伟大的文章在这里 。 一旦你有你appID你需要替换默认appID在服务器上设置。 因此,请确保您已登录到服务器。如果您不是,请重新登入:
ssh cabin@your_value_for_web_ipv4
登录后,打开文件~/stream-react-example/app/views/index.ejs
nano ~/stream-react-example/app/views/index.ejs
更改默认appId与被Facebook所提供的之一。
strea-react-example / app / views / index.ejs
FB.init({
    appId   : 'your_facebook_app_id',
    xfbml   : true,
    version : 'v2.6',
    status  : true,
    cookie  : true,
})
保存此文件并将其关闭。 接下来,您需要知道Cabin的数据库密码,这是Terraform在创建服务器时生成的。要获取此值,请键入以下命令:
grep DB_PASSWORD processes.yml
复制此密码;你很快就会需要它。 该文件env.sh是在这里您可以输入您的凭据的各种提供商和服务,客舱依赖。此文件将这些凭据放入环境变量中,然后由应用程序读取。这是一个安全预防措施,因为它保持密码和密钥出Git。 打开env.sh
nano env.sh
您将看到以下内容:
export NODE_ENV=production
export JWT_SECRET=ABC123
export DB_USERNAME=cabin
export DB_HOST=localhost
export DB_PASSWORD=VALUE
export DB_PORT=3306
export MAPBOX_ACCESS_TOKEN=ADD_VALUE_HERE
export S3_KEY=ADD_VALUE_HERE
export S3_SECRET=ADD_VALUE_HERE
export S3_BUCKET=ADD_VALUE_HERE
export STREAM_APP_ID=ADD_VALUE_HERE
export STREAM_KEY=ADD_VALUE_HERE
export STREAM_SECRET=ADD_VALUE_HERE
export ALGOLIA_APP_ID=ADD_VALUE_HERE
export ALGOLIA_SEARCH_ONLY_KEY=ADD_VALUE_HERE
export ALGOLIA_API_KEY=ADD_VALUE_HERE
export KEEN_PROJECT_ID=ADD_VALUE_HERE
export KEEN_WRITE_KEY=ADD_VALUE_HERE
export KEEN_READ_KEY=ADD_VALUE_HERE
export IMGIX_BASE_URL=https://react-example-app.imgix.net/uploads
export API_URL=http://localhost:8000
正如你所看到的,这个文件导出了一堆环境变量,它们保存了Cabin需要的各种服务的信息。为了让Cabin在生产环境中工作,您需要填写所有这些值。 以下是这些设置的快速细分:
  1. NODE_ENV:这Node.js的将运行环境(产量将提供一个速度提升)。
  2. JWT_SECRET:身份验证秘密的API和Web(APP)接口之间的JSON网络令牌认证。
  3. DB_USERNAME:数据库的用户名。
  4. DB_HOST:数据库主机名。
  5. DB_PASSWORD:数据库,你只要看一眼看到的密码processes.yml
  6. DB_PORT:数据库端口(用于MySQL默认端口3306)。
  7. MAPBOX_ACCESS_TOKEN:访问令牌MapBox(映射照片位置)。
  8. S3_KEY:Amazon S3的键图像存储。
  9. S3_SECRET:Amazon S3的秘密图像存储。
  10. S3_BUCKET:Amazon S3的桶中进行图像存储。请确保此存储桶已存在。
  11. Stream 应用程序 ID:Stream应用程序ID。确保所有必需的Feed组存在于与此ID相关联的应用程序中。
  12. STREAM_KEY:Stream API密钥。
  13. STREAM_SECRET:Stream应用程序的秘密。
  14. ALGOLIA_APP_ID:Algolia应用程序ID搜索。
  15. ALGOLIA_SEARCH_ONLY_KEY:Algolia只搜索键搜索。
  16. ALGOLIA_API_KEY:搜索Algolia API密钥。
  17. KEEN_PROJECT_ID:敏锐跟踪项目的ID(统计)。
  18. KEEN_WRITE_KEY:敏锐跟踪写入键(统计)。
  19. KEEN_READ_KEY:敏锐跟踪阅读键(统计)。
  20. IMGIX_BASE_URL:Imgix基本URL(在特定尺寸的照片渲染)。
  21. API_URL:此应用程序的API使用的URL。 你需要从改变这个localhost为指向您的IP地址,例如域cabin.example.com
有关引用的环境变量和服务的更多详细信息,请访问以下博客文章,并确保您已按指定配置每个应用程序: 配置完所有的供应商中,输入数据库的密码,并在提供者中的值env.sh文件。 退出并保存env.sh文件。然后源文件,将值加载到Cabin将使用的环境值:
source ./env.sh
接下来,你需要运行webpack命令。 Webpack是一个JavaScript构建工具,用于管理Cabin的前端代码。 WebPACK中会根据该设置的值再生JavaScript和CSS文件env.sh你刚刚更改的文件。 因此,切换到app的目录:
cd app
然后运行webpack命令重建前端JavaScript文件。这将把一些提供程序令牌注入前端代码。
webpack --progress --color
您将看到以下输出:
Hash: 64dcb6ef9b46a0243a8c  
Version: webpack 1.13.1
Time: 21130ms
                  Asset     Size  Chunks             Chunk Names
     ./public/js/app.js  2.22 MB       0  [emitted]  app
./public/css/styles.css    23 kB       0  [emitted]  app
   [0] multi app 28 bytes {0} [built]
    + 685 hidden modules
Child extract-text-webpack-plugin:
        + 2 hidden modules
Child extract-text-webpack-plugin:
        + 2 hidden modules
在设置就绪后,运行PM2以重新加载所有应用程序进程,以确保所有组件都使用新设置:
pm2 restart all
[PM2] Applying action restartProcessId on app [all](ids: 0,1,2)
[PM2] [api](0) ✓
[PM2] [app](1) ✓
[PM2] [www](2) ✓
┌──────────┬────┬──────┬───────┬────────┬─────────┬────────┬─────────────┬──────────┐
│ App name │ id │ mode │ pid   │ status │ restart │ uptime │ memory      │ watching │
├──────────┼────┼──────┼───────┼────────┼─────────┼────────┼─────────────┼──────────┤
│ api      │ 0  │ fork │ 30834 │ online │ 516     │ 0s     │ 39.027 MB   │  enabled │
│ app      │ 1  │ fork │ 30859 │ online │ 9       │ 0s     │ 22.504 MB   │  enabled │
│ www      │ 2  │ fork │ 30880 │ online │ 9       │ 0s     │ 19.746 MB   │  enabled │
└──────────┴────┴──────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘
而已!您现在可以注销远程服务器。
exit
最后,访问http:// your_value_for_web_ipv4再次在浏览器中看到的网站。这将显示一个封面图片,其中包含登录Facebook的链接。登录后,您可以稍后浏览该应用。 PM2管理Cabin的进程,它可以是一个伟大的工具,帮助您调试问题。您可以使用pm2 list来查看应用程序的组件和状态pm2 logs查看日志的应用程序,它可以帮助您诊断任何配置错误Stream。 现在让我们深入了解Terraform配置,使这个部署成为可能。

第6步 - 探索配置块

那么这一切如何工作?让我们看看我们克隆到本地机器的存储库中的文件。虽然在本节中没有任何内容可供您修改,但您仍应遵循自己的机器,以便了解这些部分如何组合在一起。 Terraform项目分为多个文件和目录,以保持应用程序的清洁和易于理解。我们已在我们所有的DigitalOcean文件的内部terraform/do存储库,它具有如下结构的目录:
terraform文件夹
do
└── cabin
    ├── files
    │   ├── cabin-web-nginx.conf
    │   └── cabin_mysql_init.sh
    ├── main.tf
    ├── outputs.tf
    ├── templates
    │   ├── processes.tpl
    │   └── web.tpl
    └── variables.tf
让我们来看看上面的文件,从main.tf 。在您喜欢的文本编辑器中打开它。 我们做的第一件事是告诉Terraform我们将使用什么云提供商。
main.tf
provider "DigitalOcean" {
  token = "${var.token}"
}
定义DigitalOcean提供商就是这么简单。你可以找到支持的供应商的完整列表Terraform文档

变量配置

Terraform允许您定义变量,这意味着您可以为部署设置默认值。这样,您不必每次都输入详细信息或在整个配置中输入硬编码值。让我们来看看如何在DigitalOcean上设置部署的变量。 看看variables.tf ,在这里我们定义必要的变量来运行应用程序客舱的位置。
变量
variable "token" {
  description = "DO Token"
}

variable "region" {
  description = "DO Region"
}
为了帮助您更好地了解在Terraform中如何处理变量,让我们通过上面的例子。 对于region变量,我们指定了一个默认值。如果未指定默认值,Terraform将提示您输入默认值,如以下示例所示:
terraform plan
var.token
  DO Token

  Enter a value:
当您运行,你还可以提供变量terraform apply 。 例如,如果你想指定不同的区域,您可以用运行Terraform var参数:
terraform -var 'region=ams3' apply
这将覆盖所有已配置的设置。

Droplet设置

main.tf我们告诉Terraform置备于DigitalOcean一个Droplet。默认情况下,我们部署具有以下特性的服务器:
main.tf
resource "digitalocean_droplet" "cabin-web" {
  image = "ubuntu-14-04-x64"
  name = "cabin-web"
  region = "${var.region}"
  size = "2gb"
  ssh_keys = [ "${digitalocean_ssh_key.cabin-ssh-key.id}" ]
  user_data = "${template_file.userdata_web.rendered}"
}
我们正在创造一个新的DigitalOceanDroplet与2GB的RAM称为机舱的网络 ,并使用图像的ubuntu-14-04-64。通过查看上面的资源定义,您可以看到,很容易更改服务器的图像和大小。

用户数据和云初始化

好吧,那么到底什么是user-data ? 这是在启动时向云实例发送命令和指令的最简单的方法。 再加上cloud-init ,它成为配置您的实例,不需要利用不必要的第三方应用程序就像一个强大的方式ChefPuppet 。 在cloud-init程序嵌入在许多Linux发行版。它有一个小的指令集,让您执行简单的任务,如添加用户,管理组,创建文件,以及以root权限运行脚本或shell命令。 让我们潜入user_data属性,让你有更好的理解它是什么:
main.tf
resource "digitalocean_droplet" "cabin-web" {
  ...
  user_data = "${template_file.userdata_web.rendered}"
}
我们的目标是开始与客舱新的Droplet和运行,并有cloud-init处理繁重的我们。 该user_data字段指向一个模板文件,使用可变指向另一声明main.tf
main.tf
resource "template_file" "userdata_web" {
  template = "${file("${path.module}/templates/web.tpl")}"

  vars {
    userdata_sshkey = "${var.sshkey}"
    userdata_nginx_conf = "${base64encode(file("${path.module}/files/cabin-web-nginx.conf"))}"
    userdata_mysql_init = "${base64encode(file("${path.module}/files/cabin_mysql_init.sh"))}"
    userdata_pm2_conf = "${base64encode("${template_file.pm2_processes_conf.rendered}")}"
    userdata_env = "${base64encode("${template_file.env.rendered}")}"
    userdata_motd = "${base64encode(file("${path.module}/files/motd"))}"
    userdata_motd_script = "${base64encode(file("${path.module}/files/motd.sh"))}"
    userdata_giturl = "${var.git_url}"
    userdata_index = "${base64encode(file("${path.module}/files/index.html"))}"
  }
}
Terraform提供了允许您转换文本的功能。我们可以使用此功能通过读取文件,然后将内容转换为Base64编码的字符串,以便可以通过API调用传输值注入模板。 这个特定部分准备数据的模板templates/web.tpl它包含所有的设置和命令在服务器上执行。 让我们走过web.tpl文件,看看它做什么。 第一部分设置初始用户并禁用根访问:
templates / web.tpl
#cloud-config
users:
  - name: cabin
    groups: sudo
    sudo: ['ALL=(ALL) NOPASSWD:ALL']
    shell: /bin/bash
    home: /home/cabin
    lock_passwd: true
    ssh-authorized-keys:
      - ${userdata_sshkey}

disable_root: true
在第一次声明web.tpl必须是#cloud-config 。 如果你忘了加这个, cloud-init不会拿起配置和给定的命令将不会对目标实例执行。 本节中的命令执行以下操作:
  • 添加cabin用户对系统的使用授权,成为超级用户
  • lock-passwd: true否认密码验证,所以cabin用户需要使用SSH密钥认证访问服务器。
  • ssh-authorized-keys安装用户的SSH密钥到authorized_keys文件。
  • disable_root: true用于禁用SSH访问以根用户身份
请记住, ${userdata_sshkey}当我们调用模板中所设定的变量main.tf 。 接下来,我们安装MySQL,Nginx,Git和我们的应用程序需要的其他包:
package_update: true
packages:
 - mysql-server-5.6
 - libmysqlclient-dev
 - iptables-persistent
 - git
 - nginx
 - npm
 - pwgen
安装使用包的最简单方法cloud-init是通过利用封装模块安装特定的软件包列表。 此模块使用分发的默认包管理器。 由于我们使用Ubuntu,这一过程将安装软件包与apt 。 接下来,我们将一些文件写入文件系统,使用我们作为文件内容传递到模板的数据:
write_files:
 - encoding: b64
   content: ${userdata_nginx_conf}
   path: /tmp/cabin-web.conf
 - encoding: b64
   content: ${userdata_pm2_conf}
   path: /tmp/processes.yml
 - encoding: b64
   content: ${userdata_mysql_init}
   path: /tmp/cabin_mysql_init.sh
   permissions: '0554'
本节利用了write_file模块来创建文件。在上面的示例中,我们创建以下文件:
  • cabin-web.conf包含NGINX配置。
  • processes.yml使用PM2处理Node.js的过程。
  • cabin_mysql_init.sh是用于初始化MySQL数据库的自定义脚本。
请记住,当我们将数据传递给模板时,我们将其编码为Base64。我们在写入文件时指定编码,以便可以解码内容。 在下一节中,我们使用runcmd模块运行一些shell命令用来创建防火墙规则iptables
runcmd:
 - iptables -A INPUT -i lo -j ACCEPT
 - iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
 - iptables -A INPUT -p tcp --dport ssh -j ACCEPT
 - iptables -A INPUT -p tcp --dport 80 -j ACCEPT
 - iptables -A INPUT -p tcp --dport 8000 -j ACCEPT
 - iptables -A INPUT -p tcp --dport 3000 -j ACCEPT
 - iptables -A INPUT -j DROP
 - iptables -A OUTPUT -j ACCEPT
 - invoke-rc.d iptables-persistent save
…
然后,代码使用iptables-persistent ,使在该实例重启事件提供的防火墙配置。 防火墙规则就位后,将执行设置和启动Cabin的其余命令:
 - apt-get update --fix-missing
 - curl -sL https://deb.nodesource.com/setup_5.x | bash && apt-get install -y nodejs
 - npm install pm2 webpack -g
 - cd /home/cabin && sudo -u cabin git clone ${userdata_giturl}
 - mv /tmp/env.sh /home/cabin/stream-react-example/env.sh
 - cd /home/cabin/stream-react-example/api && sudo -u cabin npm install
 - cd /home/cabin/stream-react-example/app && sudo -u cabin npm install
 - cd /home/cabin/stream-react-example/www && sudo -u cabin npm install
 - chown cabin.cabin /home/cabin/stream-react-example/env.sh && /home/cabin/stream-react-example/env.sh
 - mv /tmp/processes.yml /home/cabin/stream-react-example/processes.yml
 - chown cabin.cabin /home/cabin/stream-react-example/processes.yml
 - /tmp/cabin_mysql_init.sh
 - cd /home/cabin/stream-react-example && sudo -u cabin pm2 start processes.yml
 - mv /tmp/cabin-web.conf /etc/nginx/sites-available/cabin-web
 - rm /etc/nginx/sites-enabled/default
 - ln -s /etc/nginx/sites-available/cabin-web /etc/nginx/sites-enabled
 - service nginx reload
所有这些命令都以root权限执行, 只有在第一个启动发生 。 如果你重新启动机器, runcmd将不再执行。 现在,您已经了解了Terraform的更多信息,让我们了解如何处理基础架构的生命周期。

第7步 - 管理的生命周期

Terraform可以保存的状态,更新,销毁和部署代码更改。 您可能已经注意到,在运行后terraform apply ,一个名为terraform.tfstate在创建cabin目录。 此文件非常重要,因为它包含对DigitalOcean上创建的实际资源的引用。基本上,这个文件告诉Terraform它管理的资源的标识符。 如果您运行terraform apply再次,Terraform不会重新开始,并消灭你已经创造了一切。相反,它只会做还没有完成的部分。因此,如果您的进程在中间由于网络问题或API问题失败,您可以解决这些问题,然后再次运行该命令。 Terraform将在它停下来的地方拾起。

更改Droplet配置

您还可以使用terraform apply来改变Droplet的配置。例如,如果您需要更改数据中心或区域,或增加Droplet使用的内存以便容纳更多流量,Terraform使这两个任务非常容易。 您可以通过运行调节Droplet区域terraform apply命令,并覆盖regiondroplet_size变量。这使Terraform知道现有的Droplet需要被销毁,并且需要配置新的Droplet以满足要求。 警告 :Terraform将毁掉现有的Droplet 假设你在同一台服务器作为应用程序上运行你的MySQL数据库, 这也将毁掉你的MySQL数据。为了避免这种情况,我们建议您先到了这一步,或者更好的执行数据库导出, 运行你的MySQL数据库在专用Droplet 如果要更改保存Droplet的区域或数据中心,请执行以下命令:
terraform apply -var "region=sfo2"
而且,随着用户群的增加,您可能需要更改Droplet大小以适应额外的流量。你可以做到这一点与droplet_size像这样的变量:
terraform apply -var "droplet_size=4gb"
Droplet将被删除并替换为新的Droplet,并且应用程序将被重新部署和配置。

销毁

关于Terraform的一个惊人的事情是它处理的整个生命周期。你可以通过运行一个简单的Terraform命令(destroy)来轻松地破坏你所构建的。
terraform destroy
Terraform将提示您确认您确实要销毁所有资源:
Do you really want to destroy?
  Terraform will delete all your managed infrastructure.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes
Terraform完成后,最终输出将如下所示:
digitalocean_droplet.cabin-web: Destroying...
digitalocean_droplet.cabin-web: Still destroying... (10s elapsed)
digitalocean_droplet.cabin-web: Destruction complete
digitalocean_ssh_key.cabin-ssh-key: Destroying...
template_file.userdata_web: Destroying...
template_file.userdata_web: Destruction complete
template_file.pm2_processes_conf: Destroying...
template_file.pm2_processes_conf: Destruction complete
digitalocean_ssh_key.cabin-ssh-key: Destruction complete

Apply complete! Resources: 0 added, 0 changed, 5 destroyed.
正如你所看到的,所有的资源都被破坏了。

部署新版本的代码

如果您对代码库进行更改,则需要将更改更新到服务器,几乎没有停机时间。我们在我们的服务器上安装了PM2,它将为我们处理繁重的工作。 PM2监听应用程序中的文件系统更改。为了运行代码,只需通过SSH进入Droplet的新版本,并发出git pull在包含应用程序的目录命令。这将指示服务器从您的存储库中拉取。当文件更改时,PMZ将自动重新启动Node进程。 例如,如果有一个新版本的Cabin,并且您想要将最新版本的代码部署到服务器,那么您将登录到您的服务器:
ssh cabin@your_value_for_web_ipv4
然后,在服务器上,导航到包含Cabin应用程序的文件夹:
cd ~/stream-react-example
最后拉下最新版本。
git pull
新代码就位后,您的应用会自动重新启动,访问者将看到最新的版本。如果由于某种原因PM2没有捕获到更改,用手动重新启动
pm2 restart all
并且所有组件将重新启动。

结论

使用DigitalOcean,Terraform,Cloud-init和PM2,您已经成功地为Cabin设置了生产环境。 使用Terraform时,所有基础架构都将作为代码存储。这样,您的团队就可以轻松跟踪更改并进行协作。它还使您能够相对轻松地进行大型基础结构更改。