如何在 Ubuntu 14.04 上使用 Terraform 部署 Node.js 应用程序

一个文章从

介绍

在管弦乐工具的帮助下,DevOps的专业人士可以通过利用几个API的呼叫来部署一个堆. [Terraform] (https://www.terraform.io/)是一个非常简单而强大的工具,它允许您将您的堆栈写作代码,然后通过使用[Git](https://git-scm.com/]输入定义文件来共享并保持更新. Terraform由HashiCorp,流行开源工具的作者所创造,如Vagrant,PackerConsul等.

Terraform 提供了一种通用的配置来启动您的基础设施,从物理和虚拟服务器到电子邮件和 DNS 提供商。

此教程为您演示了如何在Ubuntu 14.04上使用 [DigitalOcean] (https://digitalocean.com), [Terraform] (https://www.terraform.io/), [Cloud-init] (https://cloudinit.readthedocs.io/en/latest/), [PM2] (http://pm2.keymetrics.io/) 设置一个功能完备,复杂的应用环境. 作为我们的例子应用程序,我们将使用Cabin,一个开源反应Redux. 由GetStream.io开发的Node.js应用程序. 最终产出将是一个功能丰富,可扩展的社交网络应用!

您将使用Terraform开始使用预定义配置部署 Cabin,然后深入探索该配置,以便您了解它是如何工作的。

如果您只对在您的 DigitalOcean 服务器上安装 Terraform 感兴趣,请参阅 如何使用 Terraform 与 DigitalOcean

前提条件

要跟随这个教程,你需要:

  • 一个 2 GB Ubuntu 14.04 服务器,您将在本教程中与 Terraform 创建。 * Git 客户端安装在您的本地机器上。 * 一个 Facebook 帐户,因此您可以创建一个 Facebook 应用程序,因为 Cabin 使用 Facebook 进行登录。 * 一个域名,如 `cabin.example.com;您将将该域名指向您在步骤 4 中获得的 IPv4 地址,您将需要这个网站在 Facebook 中的 URL。

虽然这不是一个要求,但本教程假定你已经完成了(http://cabin.getstream.io/)。你需要几个供应商的API密钥和设置,这对于Cabin在生产中工作是必要的,因为它们在Cabin的功能中起着不可或缺的作用。

您仍然可以使用 Terraform 提供和部署 Cabin 应用程序,但在您配置所有所需组件之前,该应用程序将无法使用。

有关这些服务的更多信息,请自由访问Stream的以下博客帖子:

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

克隆从 GitHub到您在本地计算机上所选择的目录。我们正在使用Mac,并假设您也是。

首先,导航到您的家庭目录。

1cd ~

然后使用git来克隆存储库:

1git clone https://github.com/GetStream/stream-react-example.git

这将示例应用程序克隆到一个名为stream-react-example的新文件夹中,然后导航到包含 Cabin 的 Terraform 项目的stream-react-example/terraform/do/cabin文件夹中。

1cd stream-react-example/terraform/do/cabin

我们会用这个文件夹做一些工作,但首先,让我们设置Terraform。

步骤2 - 安装Terraform

为了在 OSX 上进行简单的安装,您可以使用 Homebrew发出以下命令来安装 Terraform:

1brew install terraform

或者,您可以从 http://terraform.io 下载 Terraform. 一旦您下载它,请将其提供给您的命令路径,如下所示。

1PATH=location/of/terraform:$PATH

如果您希望這個變更是永久的,請在 OSX 上編輯檔案 ~/.bash_profile 並添加下列行:

1[label ~/.bash_profile]
2export PATH=location/of/terraform:$PATH

接下来,要检查Terraform是否正确安装,请运行以下命令:

1terraform

您将看到以下输出,显示Terraform的选项:

 1[secondary_label Output]
 2usage: terraform [--version] [--help] <command> [<args>]
 3
 4Available commands are:
 5    apply Builds or changes infrastructure
 6    destroy Destroy Terraform-managed infrastructure
 7    fmt Rewrites config files to canonical format
 8    get Download and install modules for the configuration
 9    graph Create a visual graph of Terraform resources
10    init Initializes Terraform configuration from a module
11    output Read an output from a state file
12    plan Generate and show an execution plan
13    push Upload this Terraform module to Atlas to run
14    refresh Update local state file against real resources
15    remote Configure remote state storage
16    show Inspect Terraform state or plan
17    taint Manually mark a resource for recreation
18    untaint Manually unmark a resource as tainted
19    validate Validates the Terraform files
20    version Prints the Terraform version

在Terraform能够启动您的基础设施之前,我们需要配置两件事:

  1. DigitalOcean Token 2. SSH 密钥对

所以我们先来照顾一下DigitalOcean代币。

步骤 2 — 配置数字海洋访问令牌

Terraform 需要您的 DigitalOcean 访问代币才能使用 DigitalOcean API。

登录您的DigitalOcean帐户并点击 API 链接,然后点击** Generate New Token** 按钮,请确保检查** Write Access** 。用户界面将显示一个新的访问密钥,您应该将其复制到您的剪辑板,因为如果您重新访问页面,密钥将无法看到。

现在,用你最喜欢的文本编辑器打开variables.tf文件,并找到token部分:

1[label variables.tf]
2variable "token" {
3  description = "DO Token"
4}

添加一个新的行,开始于文本 default =,并包括您的DigitalOcean API代币。

1[label variables.tf]
2variable "token" {
3  description = "DO Token"
4  default = "57eaa5535910eae8e9359c0bed4161c895c2a40284022cbd2240..."
5}

保存并关闭文件。

现在,让我们配置Terraform以使用我们的SSH密钥对。

步骤 3 – 添加您的 SSH 密钥对

Terraform需要一个SSH密钥连接到我们的服务器,一旦它被创建,这样它就可以安装包和部署应用程序。

看看你的~/.ssh目录,看看你是否已经有一个密钥对:

1ls -al ~/.ssh

最有可能的是,您至少有一个由私钥和公钥组成的密钥对,例如,您可能有id_rsa.pubid_rsa

<$>[警告] 警告 :如果您的现有密钥对已经与您的 DigitalOcean帐户相关联,则需要使用 DigitalOcean 仪表板删除它,或生成新的密钥对,以避免冲突。

如果你没有任何密钥对,或者你拥有的密钥已经与你的DigitalOcean帐户相关联,那么请参阅DigitalOcean关于设置SSH密钥的教程来设置一个。

您需要将.pub 文件的内容粘贴到.variables.tf 文件中,就像您使用 API 代码一样. 如果您在 Mac 上,您可以通过发出以下命令将 SSH 公钥复制到剪辑板中:

1pbcopy < ~/.ssh/your_key.pub

您还可以使用命令将公共密钥的内容显示到屏幕上,并将其手动复制到剪辑板:

1cat  ~/.ssh/your_key.pub

然后在编辑器中打开variables.tf文件,并将 SSH 公钥文件的内容添加到sshkey设置中:

1[label variables.tf]
2variable "sshkey" {
3  description = "Public ssh key (for Cabin user)"
4  default = "ssh-rsa AAAAB3NzaC1yc2EAAAADA...== [email protected]"
5}

完成此步骤后,保存并退出文件。

如果您已经生成了一个新密钥用于使用 Terraform 和 DigitalOcean,则需要运行这两个命令,以便使用新的密钥而不是默认密钥:

1eval "$(ssh-agent -s)"
2ssh-add ~/.ssh/your_id_rsa

您可能需要每次打开新壳执行此操作,如果您正在使用替代密钥对。

现在,您已经为 Terraform 提供了所需的变量,您已经准备好创建您的服务器并与 Terraform 部署您的应用程序。

步骤4 - 运行地形

这里来了有趣的部分!让我们看看我们要建立的基础设施 Terraform将为我们做很多工作,从设置我们的服务器到部署我们的应用程序。

1terraform plan

这个命令的输出是相当可言的,所以试着专注于以下陈述:

 1[secondary_label Output]
 2+ digitalocean_droplet.cabin-web
 3...
 4+ digitalocean_floating_ip.cabin-web-ip
 5...
 6+ digitalocean_ssh_key.cabin-ssh-key
 7...
 8+ template_file.pm2_processes_conf
 9...
10+ template_file.userdata_web
11...

在一行开始时的+符号意味着资源将被创建。前缀为digitalocean的资源是在DigitalOcean上创建的资源。

<$>[警告] 警告 我们不对在您的实例(s)或第三方服务在线时可能产生的费用负责。命令 terraform apply 将创建具有 2GB RAM (~$0.03/hour) 的 Droplet 和 DigitalOcean 免费提供的保留 IP。

现在是时候运行Terraform并在您的Droplet上旋转 Cabin了。

1terraform apply

短时间后,你会看到Terraform印刷如下:

 1[secondary_label Output]
 2Apply complete! Resources: 6 added, 0 changed, 0 destroyed.
 3
 4The state of your infrastructure has been saved to the path
 5below. This state is required to modify and destroy your
 6infrastructure, so keep it safe. To inspect the complete state
 7use the `terraform show` command.
 8
 9State path: terraform.tfstate
10
11Expected output:
12
13  web_ipv4 = 111.111.111.111

web_ipv4是您可以使用访问 Droplet 的保留 IP 地址。

使用您看到的 web_ipv4 的值登录新创建的 Droplet:

1ssh cabin@your_value_for_web_ipv4

您也可以使用命令

1terraform output web_ipv4

显示与该值相关的 IP 地址,如果您错过了。

您将在登录时看到这个欢迎消息:

1_____ _ _
2  / ____|    | |   (_)
3 | |     __ _| |__ _ _ __
4 | |    / _` | '_ \| | '_ \
5 | |___| (_| | |_) | | | | |
6  \_____\__,_|_.__/|_|_| |_|
7
8Initializing Cabin. Please wait... (up 1 minute) | CTRL+C to interrupt

您可能需要等待数分钟,DigitalOcean 才能提供该实例,以及cloud-init 才能安装所需的 Cabin 包。

 1Cabin initialized!
 2Check running processes...
 3┌──────────┬────┬──────┬───────┬────────┬─────────┬────────┬─────────────┬──────────┐
 4│ App name │ id │ mode │ pid   │ status │ restart │ uptime │ memory      │ watching │
 5├──────────┼────┼──────┼───────┼────────┼─────────┼────────┼─────────────┼──────────┤
 6│ api      │ 0  │ fork │ 14105 │ online │ 0       │ 36s    │ 75.898 MB   │  enabled │
 7│ app      │ 1  │ fork │ 14112 │ online │ 0       │ 36s    │ 34.301 MB   │  enabled │
 8│ www      │ 2  │ fork │ 14119 │ online │ 0       │ 36s    │ 50.414 MB   │  enabled │
 9└──────────┴────┴──────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘
10 Use `pm2 show <id|name>` to get more details about an app

一旦 Cabin 已启动并启动,请将您的移动浏览器指向 http://your_value_for_web_ipv4。 Cabin 已启动,您应该看到一个加载屏幕,但这就等到我们对服务器上的代码做出一些更改为止。

步骤 5 — (可选) 配置客舱

我们必须配置Facebook和其他几个服务,如果我们想让Cabin完全运行。

首先,您需要使用有效的域名创建一个 Facebook 应用程序,例如 cabin.example.com’ 将其映射到在安装过程中生成的 web_ipv4地址. 将记录添加到您的 DNS 或添加到您的/etc/hosts' 文件中,将您的域名映射到 IP 地址。

要创建 Facebook 应用程序,请遵循以下步骤:

  1. 联合国 访问[https://developers.facebook.com/docs/apps/register# step-by-step-guide] (https://developers.facebook.com/docs/apps/register step-by-step-guide).
  2. 登录到Facebook。 3个 点击Add a New App 。 4.四. 输入您的应用程序名称( 如 ` Cabin - My 示例 App' )。 5 (韩语). 输入您的 联系电子邮件
  3. 对于Category ,使用下放菜单为应用程序选择一个类别. 就我们来说,这是活生生的
  4. 联合国 点击 Create App ID 按钮.
  5. 联合国 如有需要,请填写封面.
  6. 国家 复制应用程序 爱德 这将是一个在屏幕上方找到的数字值 。 你很快就会需要的 10个 从左侧栏选择 Dashboard 。 11个 在标题下从Facebook SDK开始 ,点击** Choose A平台** 。 12个 选择 为平台服务。 13个 定位Site URL** 字段并输入`http://cabin.example.com'。
  7. 点击以后 。 .

如果你遇到问题,你可以遵循 这个步骤的指南. 如果你陷入困境,有一个很棒的文章来调试你的应用程序设置在Facebook上,它可以找到 这里

一旦你有你的appID,你需要更换服务器上的默认appID设置。

因此,请确保您已登录到您的服务器. 如果您没有登录,请重新登录:

1ssh cabin@your_value_for_web_ipv4

登录后,打开文件 ~/stream-react-example/app/views/index.ejs:

1nano ~/stream-react-example/app/views/index.ejs

将默认appId更改为由Facebook提供的。

1[label strea-react-example/app/views/index.ejs]
2FB.init({
3    appId   : 'your_facebook_app_id',
4    xfbml   : true,
5    version : 'v2.6',
6    status  : true,
7    cookie  : true,
8})

保存此文件并关闭它。

接下来,您需要知道 Cabin 的数据库密码,该密码是 Terraform 在创建服务器时生成的。

1grep DB_PASSWORD processes.yml

复制此密码,您很快就会需要它。

env.sh文件是您为 Cabin 所依赖的各种提供商和服务输入您的身份证件的地方,该文件将这些身份证件放入环境变量中,然后由应用程序读取。

打开env.sh:

1nano env.sh

你会看到以下内容:

 1[secondary_label Output]
 2export NODE_ENV=production
 3export JWT_SECRET=ABC123
 4export DB_USERNAME=cabin
 5export DB_HOST=localhost
 6export DB_PASSWORD=VALUE
 7export DB_PORT=3306
 8export MAPBOX_ACCESS_TOKEN=ADD_VALUE_HERE
 9export S3_KEY=ADD_VALUE_HERE
10export S3_SECRET=ADD_VALUE_HERE
11export S3_BUCKET=ADD_VALUE_HERE
12export STREAM_APP_ID=ADD_VALUE_HERE
13export STREAM_KEY=ADD_VALUE_HERE
14export STREAM_SECRET=ADD_VALUE_HERE
15export ALGOLIA_APP_ID=ADD_VALUE_HERE
16export ALGOLIA_SEARCH_ONLY_KEY=ADD_VALUE_HERE
17export ALGOLIA_API_KEY=ADD_VALUE_HERE
18export KEEN_PROJECT_ID=ADD_VALUE_HERE
19export KEEN_WRITE_KEY=ADD_VALUE_HERE
20export KEEN_READ_KEY=ADD_VALUE_HERE
21export IMGIX_BASE_URL=https://react-example-app.imgix.net/uploads
22export API_URL=http://localhost:8000

正如您所看到的,此文件导出了一大堆环境变量,其中包含有关 Cabin 需要的各种服务的信息。

以下是这些设置的快速分解:

  1. 联合国 NODE_ENV :Node.js将运行的环境. (制作将提高速度)。
  2. JWT_SECRET :在API和Web(app)接口之间为JSON Web Token认证保密. 3个 DB_USERNAME :数据库的用户名. 4.四. DB_HOST :数据库主机名. 5 (韩语). DB_PASWORD :数据库的密码,你刚刚通过查看`processes.yml'来查看。
  3. 国家 DB_PORT :数据库端口(默认端口3306 for MySQL).
  4. 联合国 MAPBOQACESS_TOKEN :地图框(用于映射相片位置)的访问符.
  5. 联合国 S3_KEY :亚马逊S3密钥用于图像存储.
  6. 国家 S3_SECRET :亚马逊S3秘密用于图像存储. 10个 S3_BUKET :亚马逊S3桶用于图像存储. 确保这桶存在。 11个 STREAM_APP_ID :流出应用ID. 确保在与此ID相关的应用程序中存在所有所需的饲料组. 12个 STREAM_KEY :流动API键. 13个 (原始内容存档于2017-10-10) (英语). Stream_SECRET **: Stream App secret. 14个 ALGOLIA_APP_ID :阿尔戈利亚应用代号以进行搜索. 15个 ALGOLIA_SEARCH_ONLY_KEY :阿尔戈利亚只搜索密钥. 16号. ALGOLIA_API_KEY :用于搜索的Algolia API密钥. 17岁 KEEN_PROJECT_ID : Keen tracking project id (for stats). 18岁。 KEEN_WRITE_KEY : Keen tracking write key (for stats). (原始内容存档于2017-09-31).
    1. KEEN_READ_KEY : Keen tracking read key (for stats). (原始内容存档于2013-10-12). 20号. IMGIX_BASE_URL : Imgix Base URL (用于渲染特定大小的照片). 21岁 API_URL :本应用程序用于其API的URL. 您需要从 localhost 更改为 cabin. example.com 等指向您的IP地址的域名。 .

有关所参照的环境变量和服务的更多细节,请访问下面的博客帖子,并确保您已按规定的方式配置每个应用程序:

一旦配置了所有提供商,请在env.sh文件中输入数据库的密码和提供商的值。

退出并保存 env.sh 文件,然后源文件,将值加载到 Cabin 将使用的环境值:

1source ./env.sh

接下来,您需要运行webpack命令。Webpack 是一个 JavaScript 构建工具,用于管理 Cabin 的前端代码。Webpack 将基于您刚刚更改的env.sh文件设置的值来再生 JavaScript 和 CSS 文件。

1cd app

然后运行webpack命令来重建前端JavaScript文件,将部分供应商代币注入前端代码。

1webpack --progress --color

您将看到以下结果:

 1[secondary_label Output]
 2Hash: 64dcb6ef9b46a0243a8c  
 3Version: webpack 1.13.1
 4Time: 21130ms
 5                  Asset Size Chunks Chunk Names
 6     ./public/js/app.js 2.22 MB 0  [emitted]  app
 7./public/css/styles.css 23 kB 0  [emitted]  app
 8   [0] multi app 28 bytes {0} [built]
 9    + 685 hidden modules
10Child extract-text-webpack-plugin:
11        + 2 hidden modules
12Child extract-text-webpack-plugin:
13        + 2 hidden modules

有了设置,运行PM2,重新加载所有应用程序流程,以确保所有组件使用新设置:

1pm2 restart all
 1[secondary_label Output]
 2[PM2] Applying action restartProcessId on app [all](ids: 0,1,2)
 3[PM2] [api](0) ✓
 4[PM2] [app](1) ✓
 5[PM2] [www](2) ✓
 6┌──────────┬────┬──────┬───────┬────────┬─────────┬────────┬─────────────┬──────────┐
 7│ App name │ id │ mode │ pid   │ status │ restart │ uptime │ memory      │ watching │
 8├──────────┼────┼──────┼───────┼────────┼─────────┼────────┼─────────────┼──────────┤
 9│ api      │ 0  │ fork │ 30834 │ online │ 516     │ 0s     │ 39.027 MB   │  enabled │
10│ app      │ 1  │ fork │ 30859 │ online │ 9       │ 0s     │ 22.504 MB   │  enabled │
11│ www      │ 2  │ fork │ 30880 │ online │ 9       │ 0s     │ 19.746 MB   │  enabled │
12└──────────┴────┴──────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘

就这样吧!您现在可以从您的远程服务器中退出。

1exit

最后,请在浏览器中再次访问http://your_value_for_web_ipv4,以查看该网站。此处将显示带有登录Facebook的链接的封面图像。一旦登录,您将能够稍后探索该应用程序。

<$>[注意] PM2 管理 Cabin 的流程,它可以是一个很好的工具来帮助您调试问题. 您可以使用pm2 列表查看应用程序组件的状态,并使用pm2 日志查看应用程序的日志流,这可以帮助您诊断任何配置错误。

现在让我们挖掘到使这种部署成为可能的Terraform配置。

步骤6 - 探索配置砖

那么,这一切是如何工作的?让我们看看我们克隆到本地机器的存储库中的文件,虽然在本节中你没有什么可以修改的,但你仍然应该在自己的机器上跟进,这样你就可以感受到这些零件如何相匹配。

Terraform 项目分为多个文件和目录,以保持应用程序的清洁和易于理解. 我们已将我们的所有 DigitalOcean 文件放置在库的 terraform/do 目录中,其结构如下:

 1[label terraform folder]
 2do
 3└── cabin
 4    ├── files
 5       ├── cabin-web-nginx.conf
 6       └── cabin_mysql_init.sh
 7    ├── main.tf
 8    ├── outputs.tf
 9    ├── templates
10       ├── processes.tpl
11       └── web.tpl
12    └── variables.tf

让我们看看上面的文件,从main.tf开始,在你最喜欢的文本编辑器中打开。

我们做的第一件事是告诉Terraform我们将使用哪个云提供商。

1[label main.tf]
2provider "DigitalOcean" {
3  token = "${var.token}"
4}

您可以在 [Terraform 文档] 中找到支持的提供商的完整列表(https://www.terraform.io/docs/providers/index.html)。

变量配置

Terraform 允许你定义变量,这意味着你可以为你的部署设置默认值。这样你就不用在整个配置中每次输入细节或硬代码值。

看看「variables.tf」,我们已经定义了运行 Cabin 应用程序所需的变量位置。

1[label variables.tf]
2variable "token" {
3  description = "DO Token"
4}
5
6variable "region" {
7  description = "DO Region"
8}

为了帮助您更好地了解变量是如何在Terraform中处理的,让我们通过上面的例子。

对于区域变量,我们指定了默认值. 如果您不指定默认值,Terraform 会提示您选择一个,如下示例所示:

1[secondary_label Output]
2terraform plan
3var.token
4  DO Token
5
6  Enter a value:

例如,如果您想要指定不同的区域,您可以使用var参数运行 Terraform:

1terraform -var 'region=ams3' apply

这将忽略任何已配置的设置。

下载设置

在main.tf 中,我们告诉Terraform在DigitalOcean上提供Droplet,默认情况下,我们部署了一台具有以下特点的服务器:

1[label main.tf]
2resource "digitalocean_droplet" "cabin-web" {
3  image = "ubuntu-14-04-x64"
4  name = "cabin-web"
5  region = "${var.region}"
6  size = "2gb"
7  ssh_keys = [ "${digitalocean_ssh_key.cabin-ssh-key.id}" ]
8  user_data = "${template_file.userdata_web.rendered}"
9}

我们正在创建一个新的DigitalOcean Droplet,具有2GB的RAM,名为 cabin-web ,并使用图像** ubuntu-14-04-x64** . 通过查看上述资源定义,您可以看到很容易改变服务器的图像和大小。

用户数据和云启动

好吧,那么什么是)或 Puppet

cloud-init程序嵌入到许多Linux发行版中,它有一个小套指令,允许您执行简单的任务,如添加用户,管理组,创建文件,并运行脚本或壳命令,具有根特权。

让我们深入user_data属性,以便您更好地了解它是什么:

1[label main.tf]
2resource "digitalocean_droplet" "cabin-web" {
3  ...
4  user_data = "${template_file.userdata_web.rendered}"
5}

我们的目标是启动一个新的 Droplet 与 Cabin up 并运行,并让云起起为我们处理重型起重。

 1[label main.tf]
 2resource "template_file" "userdata_web" {
 3  template = "${file("${path.module}/templates/web.tpl")}"
 4
 5  vars {
 6    userdata_sshkey = "${var.sshkey}"
 7    userdata_nginx_conf = "${base64encode(file("${path.module}/files/cabin-web-nginx.conf"))}"
 8    userdata_mysql_init = "${base64encode(file("${path.module}/files/cabin_mysql_init.sh"))}"
 9    userdata_pm2_conf = "${base64encode("${template_file.pm2_processes_conf.rendered}")}"
10    userdata_env = "${base64encode("${template_file.env.rendered}")}"
11    userdata_motd = "${base64encode(file("${path.module}/files/motd"))}"
12    userdata_motd_script = "${base64encode(file("${path.module}/files/motd.sh"))}"
13    userdata_giturl = "${var.git_url}"
14    userdata_index = "${base64encode(file("${path.module}/files/index.html"))}"
15  }
16}

Terraform 提供了允许您转换文本的功能,我们可以使用此功能通过阅读文件将值注入模板,然后将内容转换为 Base64 编码的字符串,以便通过 API 调用进行传输。

此特定部分为 Templates/web.tpl 模板准备数据,该模板包含在服务器上执行的所有设置和命令。

让我们通过web.tpl文件行走,看看它做什么。

第一部分设置了初始用户并禁用了 root 访问:

 1[label templates/web.tpl]
 2#cloud-config
 3users:
 4  - name: cabin
 5    groups: sudo
 6    sudo: ['ALL=(ALL) NOPASSWD:ALL']
 7    shell: /bin/bash
 8    home: /home/cabin
 9    lock_passwd: true
10    ssh-authorized-keys:
11      - ${userdata_sshkey}
12
13disable_root: true

web.tpl中的第一个句子必须是# cloud-config。如果你忘记添加这个句子,则cloud-init不会接收配置,并且给定的命令不会在目标实例上执行。

本节中的命令如下:

  • cabin用户添加到系统中以获准成为超级用户 * lock-passwd: true 拒绝密码验证,因此cabin用户将需要使用 SSH 密钥验证来访问服务器 * ssh-authorized-keys 将用户的 ssh-key 安装到 authorized_keys 文件中。

请记住,${userdata_sshkey}是我们在main.tf中调用模板时设置的变量。

接下来,我们会安装MySQL、Nginx、Git以及我们对应用程序所需的其他软件包:

1package_update: true
2packages:
3 - mysql-server-5.6
4 - libmysqlclient-dev
5 - iptables-persistent
6 - git
7 - nginx
8 - npm
9 - pwgen

安装具有云端的包的最简单的方法是利用模块来安装给定的包列表,该模块使用默认包管理器进行分发,因为我们正在使用Ubuntu,这个过程将安装具有apt的包。

接下来,我们将一些文件写入文件系统,使用我们作为文件内容传递给模板的数据:

 1write_files:
 2 - encoding: b64
 3   content: ${userdata_nginx_conf}
 4   path: /tmp/cabin-web.conf
 5 - encoding: b64
 6   content: ${userdata_pm2_conf}
 7   path: /tmp/processes.yml
 8 - encoding: b64
 9   content: ${userdata_mysql_init}
10   path: /tmp/cabin_mysql_init.sh
11   permissions: '0554'

本节利用write_file模块来创建文件.在上面的示例中,我们正在创建以下文件:

  • cabin-web.conf 包含 NGINX 配置. * processes.yml 被 PM2 用来处理 Node.js 流程. * cabin_mysql_init.sh 是用于初始化 MySQL 数据库的自定义脚本。

请记住,当我们将数据传输到模板时,我们将其编码为 Base64。

在下一节中,我们使用runcmd模块运行一些壳命令以使用iptables创建防火墙规则:

 1runcmd:
 2 - iptables -A INPUT -i lo -j ACCEPT
 3 - iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
 4 - iptables -A INPUT -p tcp --dport ssh -j ACCEPT
 5 - iptables -A INPUT -p tcp --dport 80 -j ACCEPT
 6 - iptables -A INPUT -p tcp --dport 8000 -j ACCEPT
 7 - iptables -A INPUT -p tcp --dport 3000 -j ACCEPT
 8 - iptables -A INPUT -j DROP
 9 - iptables -A OUTPUT -j ACCEPT
10 - invoke-rc.d iptables-persistent save
11

然后代码使用iptables-persistent来使防火墙配置可用,如果实例重新启动。

在设置防火墙规则后,执行其他设置和启动 Cabin 命令:

 1- apt-get update --fix-missing
 2 - curl -sL https://deb.nodesource.com/setup_5.x | bash && apt-get install -y nodejs
 3 - npm install pm2 webpack -g
 4 - cd /home/cabin && sudo -u cabin git clone ${userdata_giturl}
 5 - mv /tmp/env.sh /home/cabin/stream-react-example/env.sh
 6 - cd /home/cabin/stream-react-example/api && sudo -u cabin npm install
 7 - cd /home/cabin/stream-react-example/app && sudo -u cabin npm install
 8 - cd /home/cabin/stream-react-example/www && sudo -u cabin npm install
 9 - chown cabin.cabin /home/cabin/stream-react-example/env.sh && /home/cabin/stream-react-example/env.sh
10 - mv /tmp/processes.yml /home/cabin/stream-react-example/processes.yml
11 - chown cabin.cabin /home/cabin/stream-react-example/processes.yml
12 - /tmp/cabin_mysql_init.sh
13 - cd /home/cabin/stream-react-example && sudo -u cabin pm2 start processes.yml
14 - mv /tmp/cabin-web.conf /etc/nginx/sites-available/cabin-web
15 - rm /etc/nginx/sites-enabled/default
16 - ln -s /etc/nginx/sites-available/cabin-web /etc/nginx/sites-enabled
17 - service nginx reload

所有这些命令都是用 root 特权执行的,并且只会在第一个启动时发生,如果你重新启动机器,‘runcmd’不会再次执行。

现在你已经了解了Terraform的更多信息,让我们探索如何处理基础设施的生命周期。

步骤7 - 管理堆栈的生命周期

Terraform 可以保存你的堆栈的状态,更新你的堆栈,摧毁它,并部署代码更改。

您可能已经注意到,在运行terraform application后,在cabin目录中创建了一个名为terraform.tfstate的文件。

这个文件非常重要,因为它包含了在 DigitalOcean 上创建的实际资源的引用,基本上,这个文件告诉 Terraform 它所管理的资源的标识符。

如果您再次執行「terraform 應用」,Terraform 將不會重新啟動並刪除您所創建的一切。相反,它只會執行尚未完成的部分。

更改 Droplet 配置

例如,如果您需要更改数据中心或区域,或增加您的Droplet使用的内存以容纳更多的流量,Terraform使这两个任务非常容易。

您可以通过运行terraform 应用命令来调整 Droplet 区域,并取代区域droplet_size变量,从而让 Terraform 知道现有 Droplet 需要被摧毁,并且需要提供新的 Droplet 以满足要求。

<$>[警告] 警告 :** Terraform将垃圾现有的Droplet** . 鉴于您在与您的应用程序相同的服务器上运行您的MySQL数据库,** 这也将垃圾您的MySQL数据。 ** 为了避免这一点,我们建议在此步骤之前执行数据库导出,或者更好地说,在专用Droplet上运行您的MySQL数据库(https://andsky.com/tech/tutorials/how-to-install-mysql-on-ubuntu-14-04)。

如果要更改持有 Droplet 的区域或数据中心,请执行以下命令:

1terraform apply -var "region=sfo2"

随着用户群的增长,您可能需要更改Droplet大小以适应额外的流量。

1terraform apply -var "droplet_size=4gb"

Droplet 将被删除并更换为新的 Droplet,并重新部署和配置应用程序。

破坏堆栈

关于Terraform的一个惊人的事情是,它处理了堆栈的整个生命周期,你可以轻松地通过运行一个简单的Terraform命令(摧毁)来摧毁你所建造的。

1terraform destroy

然后,Terraform会提示您确认您实际上想要摧毁所有资源:

1[secondary_label Output]
2Do you really want to destroy?
3  Terraform will delete all your managed infrastructure.
4  There is no undo. Only 'yes' will be accepted to confirm.
5
6  Enter a value: yes

一旦Terraform完成,最终的输出将如下:

 1[secondary_label Output]
 2digitalocean_droplet.cabin-web: Destroying...
 3digitalocean_droplet.cabin-web: Still destroying... (10s elapsed)
 4digitalocean_droplet.cabin-web: Destruction complete
 5digitalocean_ssh_key.cabin-ssh-key: Destroying...
 6template_file.userdata_web: Destroying...
 7template_file.userdata_web: Destruction complete
 8template_file.pm2_processes_conf: Destroying...
 9template_file.pm2_processes_conf: Destruction complete
10digitalocean_ssh_key.cabin-ssh-key: Destruction complete
11
12Apply complete! Resources: 0 added, 0 changed, 5 destroyed.

正如你所看到的,所有的资源都被摧毁了。

部署新版本的代码

如果您对您的代码库进行更改,您将需要将更改带到服务器,几乎没有停机时间. 我们在我们的服务器上安装了PM2,它将为我们处理重量级的起重。

PM2 會聽取應用程式中的檔案系統變更。為了執行新版本的代碼,只需將 SSH 傳入 Droplet 並在包含應用程式的目錄中發出「git pull」命令。

例如,如果有新的 Cabin 版本,并且您想将最新版本的代码部署到服务器上,则会登录到您的服务器:

1ssh cabin@your_value_for_web_ipv4

然后,在服务器上,导航到包含 Cabin 应用程序的文件夹:

1cd ~/stream-react-example

最后下载最新版本。

1git pull

一旦新代码启用,您的应用程序将自动重新启动,访问者将看到最新版本。

1pm2 restart all

所有组件将重新启动。

结论

使用DigitalOcean、Terraform、Cloud-init 和 PM2,您已成功为 Cabin 建立了生产环境。

在使用 Terraform 时,您的整个基础设施都被存储为代码,这使您的团队能够轻松跟踪变化和协作,并使您能够轻松地对基础设施进行大规模的更改。

Published At
Categories with 技术
comments powered by Disqus