Một bài báo từ Suối

Giới thiệu

Với sự trợ giúp của các công cụ dàn nhạc, các chuyên gia DevOps có thể triển khai một ngăn xếp bằng cách tận dụng một vài cuộc gọi API. Terraform là một công cụ rất đơn giản nhưng mạnh mẽ cho phép bạn viết ngăn xếp của bạn dưới dạng mã, sau đó chia sẻ nó và giữ nó cập nhật bằng cách cam kết các tệp định nghĩa bằng cách sử dụng Git. Terraform được tạo bởi HashiCorp, các tác giả của các công cụ nguồn mở phổ biến như Vagrant, Người đóng góivà Lãnh sự.

Terraform cung cấp một cấu hình chung để khởi chạy cơ sở hạ tầng của bạn, từ các máy chủ vật lý và ảo đến các nhà cung cấp email và DNS. Khi được khởi chạy, Terraform sẽ thay đổi một cách an toàn và hiệu quả cơ sở hạ tầng khi cấu hình phát triển.

Hướng dẫn này cho bạn thấy cách thiết lập môi trường cho một chức năng đầy đủ, tinh vi Node.js ứng dụng sử dụng DigitalOcean, Terraform, Cloud-initvà PM2 trên Ubuntu 14.04. Như ứng dụng ví dụ của chúng tôi, chúng tôi sẽ sử dụng Cabin, một nguồn mở Phản ứng & Redux Ứng dụng Node.js được phát triển bởi GetStream.io. Kết quả cuối cùng sẽ là một ứng dụng mạng xã hội đa dạng, có khả năng mở rộng!

Bạn sẽ bắt đầu bằng cách sử dụng Terraform để triển khai Cabin bằng cách sử dụng một cấu hình được xác định trước. Sau đó, bạn sẽ đi sâu vào cấu hình đó để bạn có thể làm quen với cách nó hoạt động.

Nếu bạn chỉ quan tâm đến việc cài đặt Terraform trên máy chủ DigitalOcean của mình, vui lòng xem Cách sử dụng Terraform với DigitalOcean.

Điều kiện tiên quyết

Để làm theo hướng dẫn này, bạn sẽ cần:

  • Một máy chủ Ubuntu 14.04 2 GB, mà bạn sẽ tạo trong hướng dẫn này với Terraform.
  • Các Git khách hàng được cài đặt trên máy cục bộ của bạn.
  • Một tài khoản Facebook, vì vậy bạn có thể tạo một Ứng dụng Facebook, vì Cabin sử dụng Facebook để đăng nhập.
  • Một tên miền như cabin.example.com; bạn sẽ trỏ tên miền này đến địa chỉ IPv4 bạn sẽ có trong Bước 4, và bạn sẽ cần điều này cho URL của trang web trong Facebook.

Mặc dù không phải là yêu cầu, hướng dẫn này giả định bạn đã hoàn thành Dòng hướng dẫn về luồng của luồng. Bạn sẽ cần các khóa API và các thiết lập cho một số nhà cung cấp cần thiết cho Cabin để làm việc trong sản xuất, vì chúng đóng một vai trò không thể thiếu trong chức năng của Cabin.

Nếu bạn không có được các khóa này, hướng dẫn này sẽ vẫn hoạt động. Bạn vẫn có thể sử dụng Terraform để cung cấp và triển khai ứng dụng Cabin, nhưng ứng dụng sẽ không thể sử dụng được cho đến khi bạn cấu hình tất cả các thành phần cần thiết của nó.

Để biết thêm thông tin về các dịch vụ này, vui lòng truy cập các bài đăng trên blog sau đây từ Luồng:

  • Suối
  • Imgix
  • Sắc sảo
  • Algolia
  • Mapbox

Bước 1 - Lấy ứng dụng ví dụ

Sao chép ứng dụng ví dụ Cabin từ GitHub vào một thư mục bạn chọn trên máy cục bộ của bạn. Chúng tôi đang sử dụng máy Mac và giả sử bạn cũng vậy.

Đầu tiên, điều hướng đến thư mục chính của bạn.

cd ~

Sau đó sử dụng git để sao chép kho lưu trữ:

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

Việc này sẽ sao chép ứng dụng mẫu vào một thư mục mới có tên stream-react-example. Điều hướng đến stream-react-example/terraform/do/cabin thư mục chứa dự án Terraform của Cabin.

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

Chúng tôi sẽ làm việc với thư mục này trong một chút. Nhưng trước tiên, hãy thiết lập Terraform.

Bước 2 - Cài đặt Terraform

Để cài đặt đơn giản trên OSX, bạn có thể cài đặt Terraform bằng cách sử dụng Homebrew bằng cách đưa ra lệnh sau:

brew install terraform

Ngoài ra, bạn có thể tải xuống Terraform từ http://terraform.io. Khi bạn tải xuống, hãy làm cho nó có sẵn cho đường dẫn lệnh của bạn, như được hiển thị bên dưới.

PATH=location/of/terraform:$PATH

Điều này tạm thời thêm Terraform vào đường dẫn của bạn. Nếu bạn muốn thay đổi này là vĩnh viễn, hãy chỉnh sửa tệp ~/.bash_profile trên OSX và thêm dòng này:

~/.bash_profile

export PATH=location/of/terraform:$PATH

Tiếp theo, để kiểm tra xem Terraform đã được cài đặt đúng chưa, hãy chạy lệnh sau:

terraform

Bạn sẽ thấy kết quả sau, hiển thị các tùy chọn của Terraform:

Outputusage: 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

Trước khi Terraform có thể bắt đầu cơ sở hạ tầng của bạn, chúng ta cần cấu hình hai thứ:

  1. Mã số DigitalOcean
  2. Cặp khóa SSH

Vì vậy, chúng ta hãy chăm sóc mã số DigitalOcean trước.

Bước 2 - Định cấu hình Mã thông báo truy cập kỹ thuật số

Terraform cần mã thông báo truy cập DigitalOcean của bạn để sử dụng API DigitalOcean.

Đăng nhập vào tài khoản DigitalOcean của bạn và nhấp vào API liên kết. Sau đó nhấp vào Tạo mã thông báo mới nút. Hãy chắc chắn kiểm tra Ghi truy cập. Giao diện người dùng sẽ hiển thị khóa truy cập mới mà bạn sẽ sao chép vào khay nhớ tạm của mình, vì khóa sẽ không hiển thị nếu bạn truy cập lại trang.

Bây giờ hãy mở tệp variables.tf với trình soạn thảo văn bản yêu thích của bạn và xác định vị trí token phần:

variables.tf

variable "token" {
  description = "DO Token"
}

Thêm một dòng mới bắt đầu bằng văn bản default = và bao gồm mã thông báo API DigitalOcean của bạn. Nhớ bao quanh mã thông báo bằng dấu ngoặc kép.

variables.tf

variable "token" {
  description = "DO Token"
  default = "57eaa5535910eae8e9359c0bed4161c895c2a40284022cbd2240..."
}

Lưu và đóng tập tin.

Bây giờ chúng ta hãy cấu hình Terraform để sử dụng cặp khóa SSH của chúng ta.

Bước 3 - Thêm cặp khóa SSH của bạn

Terraform cần một khóa SSH để kết nối với máy chủ của chúng tôi khi nó được tạo, vì vậy nó có thể cài đặt các gói và triển khai ứng dụng.

Nhìn vào của bạn ~/.ssh thư mục để xem bạn đã có một cặp khóa chưa:

ls -al ~/.ssh

Rất có thể, bạn có ít nhất một cặp khóa bao gồm khóa riêng và khóa công cộng. Ví dụ, bạn có thể có id_rsa.pubid_rsa.

Cảnh báo: Nếu cặp khóa hiện tại của bạn đã được liên kết với tài khoản DigitalOcean của bạn, bạn sẽ cần phải loại bỏ nó bằng cách sử dụng bảng điều khiển DigitalOcean hoặc tạo một khóa mới để tránh xung đột.

Nếu bạn không có bất kỳ cặp khóa nào, hoặc nếu khóa bạn có đã được liên kết với tài khoản DigitalOcean của bạn, thì hãy xem xét Hướng dẫn về thiết lập khóa SSH của DigitalOcean để thiết lập một.

Bạn cần phải dán nội dung của .pub tập tin vào variables.tf tệp, giống như bạn đã làm với mã thông báo API. Nếu bạn đang sử dụng máy Mac, bạn có thể sao chép khóa công khai SSH của mình vào khay nhớ tạm bằng cách phát lệnh sau:

pbcopy < ~/.ssh/your_key.pub

Bạn cũng có thể hiển thị nội dung của khóa công khai trên màn hình bằng cat lệnh và sao chép nó vào clipboard của bạn theo cách thủ công:

cat  ~/.ssh/your_key.pub

Sau đó mở tệp variables.tf trong trình soạn thảo của bạn và thêm nội dung của tệp khóa công khai SSH vào sshkey cài đặt:

variables.tf

variable "sshkey" {
  description = "Public ssh key (for Cabin user)"
  default = "ssh-rsa AAAAB3NzaC1yc2EAAAADA...== nick@getstream.io"
}

Khi bạn đã hoàn thành bước này, hãy lưu và thoát tệp.

Nếu bạn đã tạo khóa mới để sử dụng với Terraform và DigitalOcean, bạn sẽ cần phải chạy hai lệnh này để khóa mới của bạn sẽ được sử dụng thay vì khóa mặc định của bạn:

eval "$(ssh-agent -s)"

ssh-add ~/.ssh/your_id_rsa

Bạn có thể cần phải chạy nó mỗi khi bạn mở một shell mới nếu bạn đang sử dụng một cặp khóa thay thế.

Bây giờ bạn đã cung cấp cho Terraform các biến mà nó cần, bạn đã sẵn sàng để tạo ra máy chủ của mình và triển khai ứng dụng của bạn với Terraform.

Bước 4 - Chạy Terraform

Đến phần thú vị! Chúng ta hãy xem xét cơ sở hạ tầng mà chúng ta sẽ xây dựng. Terraform sẽ làm rất nhiều việc cho chúng tôi, từ việc thiết lập máy chủ của chúng tôi để triển khai ứng dụng của chúng tôi. Chúng ta có thể có Terraform cho chúng ta thấy chính xác những gì nó sẽ làm với lệnh sau:

terraform plan

Đầu ra của lệnh này khá dài dòng, vì vậy hãy cố gắng tập trung vào các câu lệnh sau:

Output+ 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
...

Biểu tượng â € œ + ở đầu dòng có nghĩa là tài nguyên sẽ được tạo. Các tài nguyên được bắt đầu bằng digitalocean là các tài nguyên sẽ được tạo trên DigitalOcean. Trong trường hợp cụ thể này, Terraform sẽ tạo ra một Droplet, một IP nổi và sẽ thêm khóa SSH của chúng ta.

Cảnh báo Chúng tôi không chịu trách nhiệm về các khoản phí có thể tích luỹ trong khi các trường hợp của bạn hoặc các dịch vụ của bên thứ ba trực tuyến. Lệnh terraform apply sẽ tạo ra một giọt với 2GB RAM (~ $ 0,03 / giờ) và một IP nổi mà DigitalOcean cung cấp miễn phí. Để biết số chính xác, hãy kiểm tra lại giá cập nhật trên trang web của DigitalOcean.

Bây giờ là lúc để chạy Terraform và quay lên Cabin trên Droplet của bạn.

terraform apply

Sau một thời gian ngắn, bạn sẽ thấy Terraform in ra như sau:

OutputApply 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 là địa chỉ IP nổi mà bạn có thể sử dụng để truy cập vào các giọt.

Đăng nhập vào Droplet mới được tạo bằng cách sử dụng giá trị bạn thấy web_ipv4:

ssh cabin@your_value_for_web_ipv4

Bạn cũng có thể sử dụng lệnh

terraform output web_ipv4

để hiển thị địa chỉ IP được liên kết với giá trị đó nếu bạn bỏ lỡ nó.

Bạn sẽ thấy thông báo chào mừng này khi bạn đăng nhập:

   _____      _     _
  / ____|    | |   (_)
 | |     __ _| |__  _ _ __
 | |    / _` | '_ \| | '_ \
 | |___| (_| | |_) | | | | |
  \_____\__,_|_.__/|_|_| |_|

Initializing Cabin. Please wait... (up 1 minute) | CTRL+C to interrupt

Bạn có thể cần phải đợi vài phút để DigitalOcean cung cấp cá thể và cho cloud-init để cài đặt các gói cần thiết cho Cabin. Nhưng khi đã sẵn sàng, bạn sẽ thấy điều này:

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

Khi Cabin khởi động và chạy, hãy trỏ trình duyệt trên thiết bị di động của bạn đến http://your_value_for_web_ipv4. Cabin đang hoạt động và bạn sẽ thấy màn hình tải. Nhưng đó là như xa như chúng tôi sẽ nhận được cho đến khi chúng tôi thực hiện một số thay đổi mã trên máy chủ.

Bước 5 - (Tùy chọn) Cấu hình Cabin

Ứng dụng Cabin được triển khai nhưng không thể sử dụng được. Chúng ta phải cấu hình Facebook và một số dịch vụ khác nếu chúng ta muốn Cabin hoạt động hoàn toàn.

Trước tiên, bạn sẽ cần phải tạo một ứng dụng Facebook bằng cách sử dụng một tên miền hợp lệ, như cabin.example.com được ánh xạ tới web_ipv4 địa chỉ được tạo trong quá trình cài đặt. Thêm bản ghi vào DNS của bạn hoặc thêm mục nhập vào /etc/hosts tệp ánh xạ miền của bạn tới địa chỉ IP.

Để tạo ứng dụng Facebook, hãy làm theo các bước sau:

  1. Chuyến thăm https://developers.facebook.com/docs/apps/register#step-by-step-guide.
  2. Đăng nhập vào Facebook.
  3. Trong Ứng dụng của tôi, nhấp Thêm ứng dụng mới.
  4. Nhập tên cho ứng dụng của bạn (ví dụ: Cabin - My Example App).
  5. Nhập của bạn địa chỉ email liên lạc.
  6. Dành cho thể loại, sử dụng trình đơn thả xuống để chọn danh mục cho ứng dụng. Trong trường hợp của chúng tôi, đó là Phong cách sống.
  7. Nhấn vào Tạo ID ứng dụng nút.
  8. Nếu được yêu cầu, hãy hoàn tất hình ảnh xác thực.
  9. Sao chép appId. Nó sẽ là một giá trị số được tìm thấy ở phía trên cùng của màn hình. Bạn sẽ cần điều đó ngay.
  10. Chọn bảng điều khiển từ thanh bên trái.
  11. Dưới tiêu đề Bắt đầu với Facebook SDK, nhấp chuột Chọn một nền tảng.
  12. Chọn Web cho nền tảng.
  13. Xác định vị trí URL trang web lĩnh vực và nhập http://cabin.example.com.
  14. Nhấp chuột Kế tiếp.

Nếu bạn gặp phải vấn đề, bạn có thể theo dõi hướng dẫn từng bước này. Nếu bạn gặp khó khăn, có một bài viết tuyệt vời về gỡ lỗi cài đặt ứng dụng của bạn trên Facebook, có thể tìm thấy đây.

Một khi bạn có appID bạn sẽ cần phải thay thế mặc định appID đặt trên máy chủ.

Vì vậy, hãy đảm bảo rằng bạn đã đăng nhập vào máy chủ của mình. Nếu bạn không đăng nhập, hãy đăng nhập lại với:

ssh cabin@your_value_for_web_ipv4

Sau khi đăng nhập, hãy mở tệp ~/stream-react-example/app/views/index.ejs:

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

Thay đổi mặc định appId với cái được cung cấp bởi Facebook.

strea-react-example/app/views/index.ejs

FB.init({
    appId   : 'your_facebook_app_id',
    xfbml   : true,
    version : 'v2.6',
    status  : true,
    cookie  : true,
})

Lưu tệp này và đóng nó lại.

Tiếp theo, bạn sẽ cần phải biết mật khẩu cơ sở dữ liệu cho Cabin, được tạo ra bởi Terraform khi nó tạo ra máy chủ. Để có được giá trị này, gõ lệnh sau:

grep DB_PASSWORD processes.yml

Sao chép mật khẩu này; bạn sẽ cần nó ngay.

Tập tin env.sh là nơi bạn sẽ nhập thông tin đăng nhập của mình cho các nhà cung cấp và dịch vụ khác nhau mà Cabin phụ thuộc vào. Tệp này đặt các thông tin đăng nhập này vào các biến môi trường, sau đó được ứng dụng đọc. Đây là một biện pháp phòng ngừa an ninh, vì nó giữ mật khẩu và chìa khóa của Git.

Mở env.sh:

nano env.sh

Bạn sẽ thấy nội dung sau:

Outputexport 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

Như bạn có thể thấy, tệp này xuất một loạt các biến môi trường chứa thông tin về các dịch vụ khác nhau mà Cabin cần. Để Cabin hoạt động trong quá trình sản xuất, bạn cần phải điền vào tất cả các giá trị này.

Dưới đây là bảng phân tích nhanh về các cài đặt này:

  1. NODE_ENV: Môi trường mà Node.js sẽ chạy. (Sản xuất sẽ cung cấp tăng cường tốc độ).
  2. JWT_SECRET: Xác thực bí mật để xác thực Mã thông báo JSON Web giữa giao diện API và Web (ứng dụng).
  3. DB_USERNAME: Tên người dùng cho cơ sở dữ liệu.
  4. DB_HOST: Tên máy chủ cơ sở dữ liệu.
  5. DB_PASSWORD: Mật khẩu cho cơ sở dữ liệu mà bạn vừa xem bằng cách xem processes.yml.
  6. DB_PORT: Cổng cơ sở dữ liệu (cổng mặc định 3306 cho MySQL).
  7. MAPBOX_ACCESS_TOKEN: Mã thông báo truy cập cho MapBox (để lập bản đồ vị trí ảnh).
  8. S3_KEY: Amazon S3 khóa để lưu trữ hình ảnh.
  9. S3_SECRET: Amazon S3 bí mật để lưu trữ hình ảnh.
  10. S3_BUCKET: Thùng chứa S3 của Amazon để lưu trữ hình ảnh. Đảm bảo rằng nhóm này tồn tại.
  11. SUỐI_APP_ID: ID ứng dụng luồng. Đảm bảo rằng tất cả các nhóm nguồn cấp dữ liệu bắt buộc tồn tại trong ứng dụng được liên kết với ID này.
  12. STREAM_KEY: Khóa API luồng.
  13. STREAM_SECRET: Luồng ứng dụng bí mật.
  14. ALGOLIA_APP_ID: Id ứng dụng Algolia cho tìm kiếm.
  15. ALGOLIA_SEARCH_ONLY_KEY: Algolia chỉ tìm kiếm khóa để tìm kiếm.
  16. ALGOLIA_API_KEY: Khóa API Algolia cho tìm kiếm.
  17. KEEN_PROJECT_ID: Keen theo dõi id dự án (cho số liệu thống kê).
  18. KEEN_WRITE_KEY: Keen theo dõi viết phím (cho số liệu thống kê).
  19. KEEN_READ_KEY: Keen theo dõi khóa đọc (cho số liệu thống kê).
  20. IMGIX_BASE_URL: URL cơ sở Imgix (để hiển thị ảnh ở kích thước cụ thể).
  21. API_URL: URL được ứng dụng này sử dụng cho API của ứng dụng. Bạn sẽ cần phải thay đổi điều này từ localhost đến tên miền trỏ đến địa chỉ IP của bạn, chẳng hạn như cabin.example.com.

Để biết thêm chi tiết về các biến môi trường và dịch vụ tham chiếu, hãy truy cập các bài đăng trên blog sau và đảm bảo bạn đã định cấu hình từng ứng dụng như được chỉ định:

  • Suối
  • Imgix
  • Sắc sảo
  • Algolia
  • Mapbox

Khi bạn đã định cấu hình tất cả các nhà cung cấp, hãy nhập mật khẩu cho cơ sở dữ liệu của bạn và các giá trị cho các nhà cung cấp trong env.sh tập tin.

Thoát và lưu env.sh tập tin. Sau đó, nguồn tập tin, tải các giá trị vào các giá trị môi trường mà Cabin sẽ sử dụng:

source ./env.sh

Tiếp theo, bạn sẽ cần chạy webpack chỉ huy. Webpack là một công cụ xây dựng JavaScript quản lý mã lối vào cho Cabin. Webpack sẽ tạo lại các tệp JavaScript và CSS dựa trên các giá trị do env.sh tệp bạn vừa thay đổi. Vì vậy, thay đổi thành app danh mục:

cd app

Và sau đó chạy webpack lệnh để xây dựng lại các tệp JavaScript front-end. Thao tác này sẽ đưa một số mã của nhà cung cấp vào mã front-end.

webpack --progress --color

Bạn sẽ thấy kết quả sau:

OutputHash: 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

Với các thiết lập tại chỗ, hãy chạy PM2 để tải lại tất cả các quy trình ứng dụng để đảm bảo rằng tất cả các thành phần đều sử dụng các cài đặt mới:

pm2 restart all

Output[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 │
└──────────┴────┴──────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘

Đó là nó! Bây giờ bạn có thể đăng xuất khỏi máy chủ từ xa của mình.

exit

Cuối cùng, hãy truy cập http://your_value_for_web_ipv4 trong trình duyệt của bạn một lần nữa để xem trang web. Thao tác này sẽ hiển thị ảnh bìa có liên kết để đăng nhập vào Facebook. Sau khi đăng nhập, bạn sẽ có thể khám phá ứng dụng sau này.

PM2 quản lý các quy trình cho Cabin, và nó có thể là một công cụ tuyệt vời để giúp bạn gỡ lỗi các vấn đề. Bạn có thể dùng pm2 list để xem trạng thái của các thành phần của ứng dụng và pm2 logs để xem luồng nhật ký cho ứng dụng, có thể giúp bạn chẩn đoán mọi lỗi cấu hình.

Bây giờ chúng ta hãy thâm nhập vào cấu hình Terraform đã thực hiện việc triển khai này.

Bước 6 - Khám phá các ô cấu hình

Vậy tất cả điều này hoạt động như thế nào? Hãy xem các tệp trong kho lưu trữ mà chúng tôi đã nhân bản đến máy cục bộ của mình. Trong khi không có gì để bạn sửa đổi trong phần này, bạn vẫn nên làm theo trên máy của riêng bạn để bạn có thể cảm nhận được cách các mảnh ghép lại với nhau.

Dự án Terraform được chia thành nhiều tệp và thư mục để giữ cho ứng dụng luôn sạch sẽ và dễ hiểu. Chúng tôi đã đặt tất cả các tệp DigitalOcean của chúng tôi vào trong terraform/do thư mục của kho lưu trữ, có cấu trúc sau:

terraform folder

do
└── cabin
    ├── files
    │   ├── cabin-web-nginx.conf
    │   └── cabin_mysql_init.sh
    ├── main.tf
    ├── outputs.tf
    ├── templates
    │   ├── processes.tpl
    │   └── web.tpl
    └── variables.tf

Hãy xem các tệp ở trên, bắt đầu bằng main.tf. Mở nó trong trình soạn thảo văn bản yêu thích của bạn.

Điều đầu tiên chúng ta làm là cho Terraform biết nhà cung cấp đám mây nào chúng ta sẽ sử dụng.

main.tf

provider "DigitalOcean" {
  token = "${var.token}"
}

Việc xác định nhà cung cấp DigitalOcean đơn giản như vậy. Bạn có thể tìm thấy danh sách đầy đủ các nhà cung cấp được hỗ trợ trong Tài liệu Terraform.

Cấu hình biến

Terraform cho phép bạn xác định các biến, có nghĩa là bạn có thể thiết lập mặc định cho việc triển khai của bạn. Bằng cách đó, bạn sẽ không phải nhập chi tiết mỗi lần hoặc các giá trị mã hóa cứng trong suốt cấu hình của bạn. Hãy xem xét cách thiết lập các biến để triển khai trên DigitalOcean.

Hãy xem variables.tf, vị trí mà chúng tôi đã xác định các biến cần thiết để chạy ứng dụng Cabin.

variables.tf

variable "token" {
  description = "DO Token"
}

variable "region" {
  description = "DO Region"
}

Để giúp bạn hiểu rõ hơn cách các biến được xử lý bên trong Terraform, hãy xem qua ví dụ trên.

Đối với biến vùng, chúng tôi đã chỉ định giá trị mặc định. Nếu bạn không chỉ định một giá trị mặc định, Terraform sẽ nhắc bạn một giá trị, như trong ví dụ sau:

Outputterraform plan
var.token
  DO Token

  Enter a value:

Bạn cũng có thể cung cấp các biến khi bạn chạy terraform apply. Ví dụ: nếu bạn muốn chỉ định một vùng khác, bạn có thể chạy Terraform bằng var tranh luận:

terraform -var 'region=ams3' apply

Điều này sẽ ghi đè bất kỳ cài đặt được định cấu hình nào.

Thiết lập giọt

Trong main.tf chúng ta nói với Terraform để cung cấp một Droplet trên DigitalOcean. Theo mặc định, chúng tôi triển khai máy chủ với các đặc điểm sau:

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}"
}

Chúng tôi đang tạo ra một DigitalOcean Droplet mới với 2GB RAM được gọi là cabin-webvà sử dụng hình ảnh ubuntu-14-04-x64. Bằng cách xem định nghĩa tài nguyên ở trên, bạn có thể thấy rằng thật dễ dàng để thay đổi hình ảnh và kích thước của máy chủ.

Dữ liệu người dùng & Cloud-Init

Được rồi, vậy chính xác là gì user-data? Đó là cách dễ nhất để gửi lệnh và hướng dẫn đến một cá thể đám mây tại thời điểm khởi động. Đã cặp đôi với cloud-init, nó trở thành một cách mạnh mẽ để cấu hình cá thể của bạn mà không tận dụng các ứng dụng của bên thứ ba không cần thiết như Đầu bếp hoặc là Con rối.

Các cloud-init chương trình được nhúng trong nhiều bản phân phối Linux. Nó có một bộ hướng dẫn nhỏ cho phép bạn thực hiện các tác vụ đơn giản như thêm người dùng, quản lý nhóm, tạo tệp và chạy tập lệnh hoặc lệnh shell với đặc quyền root.

Hãy đi sâu vào user_data để bạn hiểu rõ hơn về nó là gì:

main.tf

resource "digitalocean_droplet" "cabin-web" {
  ...
  user_data = "${template_file.userdata_web.rendered}"
}

Mục tiêu của chúng tôi là bắt đầu một Droplet mới với Cabin và chạy, và có cloud-init xử lý việc nâng hạng nặng cho chúng tôi. Các user_data lĩnh vực trỏ đến một tập tin mẫu, sử dụng một biến điểm để tuyên bố khác trong 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 cung cấp các hàm cho phép bạn chuyển đổi văn bản. Chúng ta có thể sử dụng tính năng này để chèn các giá trị vào các mẫu bằng cách đọc các tệp và sau đó chuyển đổi nội dung thành các chuỗi được mã hóa Base64 để chúng có thể được truyền qua các cuộc gọi API.

Phần cụ thể này chuẩn bị dữ liệu cho mẫu templates/web.tpl chứa tất cả các cài đặt và lệnh để thực thi trên máy chủ.

Hãy đi qua web.tpl và xem nó làm gì.

Phần đầu tiên thiết lập người dùng ban đầu và vô hiệu hóa quyền truy cập root:

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

Tuyên bố đầu tiên trong web.tpl cần phải #cloud-config. Nếu bạn quên thêm điều này, cloud-init sẽ không nhận cấu hình và các lệnh đã cho sẽ không được thực hiện trên cá thể đích.

Các lệnh trong phần này làm như sau:

  • thêm cabin người dùng vào hệ thống với một khoản trợ cấp để trở thành siêu người dùng
  • lock-passwd: true từ chối xác thực mật khẩu, vì vậy cabin người dùng sẽ cần phải sử dụng xác thực khóa SSH để truy cập máy chủ.
  • ssh-authorized-keys cài đặt khóa ssh của người dùng vào tệp authorized_keys.
  • disable_root: true được sử dụng để vô hiệu hóa quyền truy cập SSH dưới dạng root

Nhớ lấy ${userdata_sshkey} là một biến được đặt khi chúng ta gọi mẫu trong main.tf.

Tiếp theo, chúng tôi cài đặt MySQL, Nginx, Git và các gói khác mà chúng tôi cần cho ứng dụng của mình:

package_update: true
packages:
 - mysql-server-5.6
 - libmysqlclient-dev
 - iptables-persistent
 - git
 - nginx
 - npm
 - pwgen

Cách dễ nhất để cài đặt các gói với cloud-init bằng cách tận dụng mô-đun Gói để cài đặt danh sách các gói nhất định. Mô-đun này sử dụng trình quản lý gói mặc định cho phân phối. Vì chúng tôi đang sử dụng Ubuntu, quá trình này sẽ cài đặt các gói với apt.

Tiếp theo, chúng tôi viết một số tệp vào hệ thống tệp, sử dụng dữ liệu mà chúng tôi đã chuyển vào mẫu dưới dạng nội dung tệp:

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'

Phần này thúc đẩy write_file để tạo tệp. Trong ví dụ trên, chúng tôi đang tạo các tệp sau:

  • cabin-web.conf chứa cấu hình NGINX.
  • processes.yml được sử dụng bởi PM2 để xử lý các quy trình Node.js.
  • cabin_mysql_init.sh là một kịch bản tùy chỉnh được sử dụng để khởi tạo cơ sở dữ liệu MySQL.

Hãy nhớ rằng khi chúng tôi chuyển dữ liệu vào mẫu, chúng tôi đã mã hóa nó dưới dạng Base64. Chúng tôi chỉ định mã hóa khi chúng tôi viết các tệp để nội dung có thể được giải mã.

Trong phần tiếp theo, chúng tôi sử dụng runcmd mô-đun để chạy một số lệnh shell để tạo quy tắc tường lửa bằng 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
…

Mã sau đó sử dụng iptables-persistent để làm cho cấu hình tường lửa có sẵn trong trường hợp khởi động lại cá thể.

Sau khi các quy tắc tường lửa được đặt ra, phần còn lại của các lệnh để thiết lập và khởi động Cabin được thực thi:

 - 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

Tất cả các lệnh này được thực thi với các đặc quyền root, và chỉ xảy ra ở lần khởi động đầu tiên. Nếu bạn khởi động lại máy, runcmd sẽ không được thực hiện lại.

Bây giờ bạn đã tìm hiểu thêm về Terraform, hãy khám phá cách xử lý vòng đời của cơ sở hạ tầng của bạn.

Bước 7 - Quản lý vòng đời của Stack

Terraform giúp bạn có thể lưu trạng thái của ngăn xếp, cập nhật ngăn xếp, phá hủy nó và triển khai các thay đổi mã.

Bạn có thể nhận thấy rằng sau khi bạn chạy terraform apply, một tệp có tên terraform.tfstate được tạo trong cabin danh mục.

Tệp này rất quan trọng vì nó chứa các tham chiếu đến các tài nguyên thực tế được tạo trên DigitalOcean. Về cơ bản, tệp này cho Terraform biết số nhận dạng của tài nguyên mà nó quản lý.

Nếu bạn chạy terraform apply một lần nữa, Terraform sẽ không bắt đầu lại và xóa sạch mọi thứ bạn đã tạo ra. Thay vào đó, nó sẽ chỉ làm những phần chưa hoàn thành. Vì vậy, nếu quá trình của bạn không thành công do vấn đề về mạng hoặc vấn đề về API, bạn có thể giải quyết các vấn đề và chạy lại lệnh. Terraform sẽ nhận nơi nó rời đi.

Thay đổi cấu hình giọt

Bạn cũng có thể dùng terraform apply để thay đổi cấu hình của Droplet. Ví dụ: nếu bạn cần thay đổi trung tâm dữ liệu hoặc vùng hoặc tăng bộ nhớ mà Droplet của bạn sử dụng để chứa thêm lưu lượng truy cập, Terraform sẽ làm cho cả hai nhiệm vụ trở nên cực kỳ dễ dàng.

Bạn có thể điều chỉnh vùng Droplet bằng cách chạy terraform apply lệnh và ghi đè regiondroplet_size biến. Điều này cho phép Terraform biết rằng các Droplet hiện có cần phải bị phá hủy và một Droplet mới cần phải được cung cấp để đáp ứng các yêu cầu.

Cảnh báo: Terraform sẽ dọn sạch Droplet hiện tại. Cho rằng bạn đang chạy cơ sở dữ liệu MySQL của bạn trên cùng một máy chủ như ứng dụng của bạn, điều này cũng sẽ làm hỏng dữ liệu MySQL của bạn.. Để tránh điều này, chúng tôi khuyên bạn nên thực hiện xuất cơ sở dữ liệu trước bước này hoặc tốt hơn, chạy cơ sở dữ liệu MySQL của bạn trên một giọt nhỏ chuyên dụng.

Nếu bạn muốn thay đổi khu vực hoặc trung tâm dữ liệu chứa Giọt của bạn, hãy thực hiện lệnh sau:

terraform apply -var "region=sfo2"

Và, khi cơ sở người dùng của bạn phát triển, bạn có thể cần phải thay đổi kích thước Giọt để phù hợp với lưu lượng truy cập bổ sung. Bạn có thể làm điều đó với droplet_size biến như thế này:

terraform apply -var "droplet_size=4gb"

Các giọt sẽ được gỡ bỏ và thay thế bằng một cái mới, và các ứng dụng sẽ được redeployed và cấu hình.

Phá hủy ngăn xếp

Một trong những điều tuyệt vời về Terraform là nó xử lý toàn bộ vòng đời của ngăn xếp. Bạn có thể dễ dàng phá hủy những gì bạn đã xây dựng bằng cách chạy một lệnh Terraform đơn giản (tiêu diệt).

terraform destroy

Terraform sau đó sẽ nhắc bạn xác nhận rằng bạn thực sự muốn tiêu diệt tất cả các tài nguyên:

OutputDo 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

Khi Terraform hoàn tất, kết quả cuối cùng sẽ trông giống như sau:

Outputdigitalocean_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.

Như bạn có thể thấy, tất cả các tài nguyên đã bị phá hủy.

Triển khai các phiên bản mã mới

Trong trường hợp bạn thực hiện thay đổi cho codebase của mình, bạn sẽ cần phải nhận các thay đổi cho máy chủ với ít hoặc không có thời gian chết. Chúng tôi có PM2 cài đặt trên máy chủ của chúng tôi, và nó sẽ xử lý việc nâng hạng nặng cho chúng tôi.

PM2 lắng nghe những thay đổi hệ thống tập tin trong ứng dụng. Để chạy phiên bản mã mới hơn của bạn, chỉ cần SSH vào Droplet và phát hành git pull lệnh trong thư mục chứa ứng dụng. Điều này sẽ hướng dẫn máy chủ rút từ kho lưu trữ của bạn. Khi các tập tin thay đổi, PMZ sẽ tự động khởi động lại quá trình Node.

Ví dụ: nếu có phiên bản Cabin mới và bạn muốn triển khai phiên bản mã mới nhất cho máy chủ, bạn sẽ đăng nhập vào máy chủ của mình:

ssh cabin@your_value_for_web_ipv4

Sau đó, trên máy chủ, điều hướng đến thư mục chứa ứng dụng Cabin:

cd ~/stream-react-example

Và cuối cùng kéo phiên bản mới nhất xuống.

git pull

Khi mã mới được đặt đúng chỗ, ứng dụng của bạn sẽ tự động khởi động lại và khách truy cập sẽ thấy phiên bản mới nhất. Nếu vì lý do nào đó PM2 không bắt được thay đổi, hãy khởi động lại mọi thứ theo cách thủ công

pm2 restart all

và tất cả các thành phần sẽ khởi động lại.

Phần kết luận

Sử dụng DigitalOcean, Terraform, Cloud-init và PM2, bạn đã thiết lập thành công môi trường sản xuất cho Cabin.

Khi sử dụng Terraform, tất cả cơ sở hạ tầng của bạn được lưu trữ dưới dạng mã. Điều này giúp nhóm của bạn dễ dàng theo dõi các thay đổi và cộng tác. Nó cũng cho phép bạn thực hiện các thay đổi cơ sở hạ tầng lớn một cách dễ dàng.