[譯] 以 Chef 和 Ansible 為例快速入門伺服器配置
作者 | Stephen Mann
譯者 | 無明
伺服器配置(Server Provisioning): 如何在我們的環境中安裝和配置軟體
伺服器配置
在開始介紹現代化的工具之前,我們來看看最基本且經過實戰考驗的伺服器配置工具:shell 指令碼。在 Chef、Ansible 或 Puppet 出現之前,很多運營團隊使用 Bash 來配置伺服器(在 Windows 上則使用 Shell/">PowerShell 指令碼)。
例如,如果想在執行 Ubuntu 的 Amazon EC2 例項上安裝 Nginx,可以使用以下指令碼(install-nginx.sh):
#!/bin/sh ssh -t ubuntu@$1 sudo apt-get upgrade ssh -t ubuntu@$1 sudo apt-get -y install nginx
我們可以使用 shell 指令碼來配置伺服器上的所有東西。據我所知,所有主流的配置工具都使用了基於安全傳輸層(如 SSH)的 shell 命令或 PowerShell(Chef 可能是個例外)。即使你使用了配置工具,在某些時候也需要用到指令碼。因此,當你開始使用配置工具(如 Chef 或 Ansible)時,學習如何使用基本的 shell 指令碼也會為你帶來很多好處。
你可能會問自己,為什麼在 shell 指令碼已經可以完成所有工作的同時還要學習配置工具?很多環境已經使用 shell 指令碼進行伺服器配置,那麼為什麼要使用配置工具代替它們?
首先, shell 指令碼通常使用的是宣告性語法 。shell 指令碼通過執行命令序列來安裝軟體, 而配置工具只需要指定伺服器應該安裝哪些軟體 ,這樣就可以使用相同的程式碼在不同的作業系統上、使用不同的包管理器以及指定不同的版本來安裝和配置相同的軟體。
其次, 配置工具通常會提供用於組織基礎設施的方式 。雖然使用 shell 指令碼也可以做到這一點,但配置工具通常會提供更簡潔明瞭的方案。因為是行業標準,開發人員可以更輕鬆地找出 QA 環境中哪些伺服器執行 RabbitMQ。
第三,每個主要的配置工具都有一個蓬勃發展的社群,他們構建可複用的模組來安裝大多數開源軟體。你可以直接在模組配置中指定記憶體限制,而不需要記住 Postgres 配置檔案在哪裡,這樣可以節省很多時間。
當然,原因還有很多,這裡就不一一例舉了。儘管學習曲線有點陡峭,但學習配置工具仍然是值得的。 與 shell 指令碼相比,配置工具更容易使用,便於思考,也更容易維護 。
關於命名
學習使用 Chef(伺服器配置工具)的前幾周給我留下了深刻的印象。入門指南展示瞭如何建立一個“recipe”,其中包含安裝或配置軟體的說明,我能夠理解這種比喻背後的含義。recipe 必須存在於“cookbook”中,這是有道理的。然後你在“kitchen”裡測試 cookbook,但我開始有點懷疑了。
這種比喻有點令人感到困惑,於是我決定去看一下其他工具,如 Ansible。Ansible 文件的第一頁介紹了“playbook”的概念,而 playbook 包含一系列“play”。
那麼,這些問題很重要嗎?當然很重要了,因為 在學習配置工具之前,你應該知道,它們很有可能會引入大量令人費解的術語 。即使是為了完成基本的任務,你也必須重新學習很多術語。如果你是剛開始學習配置工具,我強烈建議你隨時寫下這些術語定義,你還有很多東西要學。
每個軟體開發人員都會為現有的單詞建立不同的含義,他們甚至還會發明一些單詞,比如“uninitialize”和“unregister”。這已經成為軟體開發的一部分。
我會盡量用大家熟悉的術語來解釋這些工具。
配置管理
你決定使用花哨的配置工具在遠端伺服器上安裝 Nginx。在開始設定資料庫備份節點前,一切都很順利。你已經編寫了 SQL/">MySQL 主伺服器的配置檔案,但是你不太確定如何配置 MySQL 從伺服器的內部 DNS 地址。這個時候配置管理就派上用場了。
在設定伺服器時,最好可以將應用程式視為由兩部分組成:不可變部分(通常是程式碼或編譯的二進位制檔案)和可變部分(通常是配置檔案或環境變數)。大部分由社群建立的模組預設情況下會安裝二進位制檔案,並提供儘可能合理的配置,而且會為我們暴露出一些屬性,方便對其進行覆蓋。
這些屬性通常包含特定於使用者環境的值。大多數配置工具都為使用者提供了一種機制,通過模板將特定於環境的值插入到配置檔案中,或直接插入到環境變數中。
你可以使用配置工具提供的配置管理來配置 MySQL 主伺服器的配置檔案,然後在其中配置從伺服器。
Secret 管理
這樣就可以解決上述的問題,但後來發現,你必須上傳 AWS 憑證才能讓 MySQL 從伺服器訪問 S3。你知道不能直接將這些憑證提交到程式碼庫中,因此這些憑證只能存在於你的機器和 NSA 伺服器上。
這個時候你需要的是 Secret 管理。
與自動化領域的所有東西一樣,你也有很多管理祕鑰的可選項。谷歌提供了一項名為 KMS 的服務,AWS 也提供了一項名為 Secret Manager 的服務,Chef 提供了加密資料包,Hashicorp 提供了一款名為 Vault 的產品,Ansible 也有一款名為 Vault 的產品。除了 KMS 會對字串進行加密之外,所有這些工具都提供了相同的功能:保護對加密祕鑰的訪問(這些祕鑰被用在配置管理中)。
有好幾次,我不小心將祕鑰提交到了程式碼庫。這類事情一直在發生,而且非常危險。
切勿以明文形式儲存 API 金鑰或憑證。
可以使用 Secret 管理解決方案來儲存這些資料,然後將其繫結到配置工具中。
一個簡單的例子:Chef
首先需要安裝 Chef Development Kit(ChefDK)。
如前所述,我們需要一個 recipe 來安裝 Nginx。出於教學的目的,我們將從頭開始建立它,而不是從社群的 cookbook 中撈一個出來。
我們需要建立一個 cookbook。cookbook 通常存在於 cookbooks
目錄中,在專案的根目錄執行以下命令:
mkdir cookbooks
現在讓我們建立一個 cookbook,用於放置我們的新 recipe:
chef generate cookbook cookbooks/application
這個命令在 cookbooks/application
目錄中建立了很多檔案,我們關心的是 cookbooks/application/recipes/default.rb
這個檔案。這個檔案包含了預設的 recipe,我們將安裝 Nginx 的命令放到這個檔案中。
apt_update package 'nginx' cookbook_file '/var/www/html/index.html' do source 'index.html' owner 'www-data' group 'www-data' mode '0755' action :create end
這個檔案中的前兩個命令將執行你期望的操作:
-
apt_update
更新你的 aptitude 包。 -
package ‘nginx’
使用作業系統預設包管理器安裝nginx
包(在這個示例中,它使用的是 aptitude)。
最後一個命令將 cookbooks/application/files/index.html
拷貝成遠端伺服器上的 /var/www/html/index.html
,並設定檔案的許可權,讓 Nginx 伺服器可以訪問它。
這個檔案還不存在,所以需要建立它。首先要建立 檔案
目錄:
mkdir cookbooks/application/files
然後建立檔案 cookbooks/application/files/index.html
,其中包含以下內容:
<html lang="en-us"> <head> <title>Hello, World!</title> </head> <body> Chef has landed. </body> </html>
更新 packer.json
,加入 Chef 相關配置:
{ "builders": [{ "type": "amazon-ebs", "region": "us-east-1", "source_ami": "ami-04169656fea786776", "instance_type": "t2.small", "ssh_username": "ubuntu", "ami_name": "Ubuntu 16.04 Nginx - {{timestamp}}", "tags": { "Image": "application" } }], "provisioners": [{ "type": "chef-solo", "cookbook_paths": ["cookbooks"], "run_list": ["recipe[application]"] }] }
我們對之前的 packer.json
進行了兩處更改。
首先,我們為 AMI 添加了一個 Image
標籤。我們之前從 Packer 的輸出中複製 AMI ID,並貼上到 Terraform 程式碼中。這不是一個可維護的解決方案,因為 AMI ID 會經常發生變化,而且我們不應該在每次發生變化時都要將更改推送到儲存庫中。相反,我們使用 Terraform 的 data
資源來動態讀取 AMI ID(使用 Image=application
查詢最新的 AMI)。
其次,我們使用 chef-solo
替換了 shell
。我們告訴它在哪裡可以找到 cookbooks 目錄,以及要執行哪個 recipe。預設情況下, run_list
中的 recipe[COOKBOOK]
條目將執行 recipes/default.rb
。我們也可以顯式指定 explicity: recipe [COOKBOOK::RECIPE]
來覆蓋預設行為。由於我們的 recipe 儲存在 recipes/default.rb
中,所以將使用預設行為。
現在開始構建我們的 AMI:
packer build packer.json
我們的新 AMI 有一個 Image
標籤,現在修改 terraform.tf
中硬編碼的 AMI,讓它通過標籤來查詢 AMI。
將以下內容新增到 terraform.tf
中:
data "aws_ami" "web" { most_recent = true owners = ["self"] filter { name = "tag:Image" values = ["application"] } }
現在使用 aws_ami.web resource
輸出的 ID 替換 aws_instance.web1
和 aws_instance.web2
resource 中的 AMI ID:
resource "aws_instance" "web1" { ami= "${data.aws_ami.web.id}" availability_zone= "us-east-1a" instance_type= "t2.small" vpc_security_group_ids = ["${aws_security_group.application.id}"] subnet_id= "${aws_subnet.private1.id}" } resource "aws_instance" "web2" { ami= "${data.aws_ami.web.id}" availability_zone= "us-east-1b" instance_type= "t2.small" vpc_security_group_ids = ["${aws_security_group.application.id}"] subnet_id= "${aws_subnet.private2.id}" }
執行下面的命令建立 Chef 配置的伺服器,然後啟動瀏覽器,開啟地址為負載均衡器的域名:
terraform plan -out terraform.plan terraform apply "terraform.plan" open "http://$(terraform output dns)"
你應該能夠在開啟的瀏覽器頁面上看到:Chef has landed!
一個簡單的例子:Ansible
讓我們使用 Ansible 來構建這個相同的示例。首先需要安裝 Ansible(https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-the-control-machine)。
Ansible 將安裝和配置說明組織到 tasks
中,然後將 tasks
組織到 playbook
中。讓我們為 playbook 建立一個目錄結構。
mkdir playbook mkdir playbook/files
這並不是組織 Ansible playbook 的最佳實踐。因為我們的用例很簡單,所以使用了簡化版本。如果你對 Ansible 感興趣,應該根據官方提供的建議來構建 playbook(https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html)。
在 playbook/application.yml
中建立 playbook,內容如下:
--- - hosts: all gather_facts: False become: yes pre_tasks: - name: Install Python 2.7 raw: test -e /usr/bin/python || (apt -y update && apt install -y python-minimal) - hosts: applications become: yes tasks: - name: Install Nginx apt: name: nginx state: present update_cache: yes - name: Update contents of index.html copy: src: index.html dest: /var/www/html/index.html owner: www-data group: www-data mode: 0755
這個 playbook 檔案包含配置我們的伺服器所需的所有信息。現在讓我們來討論一下它的結構。
每個 playbook 包含一個“play”列表,每個 play 包含一個“tasks”列表,task 用於安裝和配置軟體。我們的 playbook 包含兩個 play。第一個 play 在 Ubuntu 上安裝 Python 2.7(用於執行 Ansible)。第二個 play 安裝和配置 Nginx。
我們在每個 play 的根節點配置了兩個引數: hosts
和 become
。 hosts
引數告訴 Ansible 應該在哪臺機器上執行 playbook(“all”表示在所有機器上執行)。 become:yes
表示 Ansible 將通過 sudo 執行所有命令,否則將會出現很多許可權錯誤。
play 的第一個 task 負責安裝和配置 Nginx,它將更新 aptitude 快取,並確保 nginx
包存在。如果已經安裝了 nginx
包,這個命令將不執行任何操作。
第二個 task 將 files/index.html
拷貝到遠端伺服器上,併為其分配正確的許可權。
這個檔案還不存在,所以讓我們建立它。將以下內容加入到 playbook/files/index.html
中:
<html lang="en-us"> <head> <title>Hello, World!</title> </head> <body> Ansible has landed. </body> </html>
這就是我們配置 Ansible 所需的全部內容。現在讓 Packer 使用這個配置。使用以下內容更新 packer.json
:
{ "builders": [{ "type": "amazon-ebs", "region": "us-east-1", "source_ami": "ami-04169656fea786776", "instance_type": "t2.small", "ssh_username": "ubuntu", "ami_name": "Ubuntu 16.04 Nginx - {{timestamp}}", "tags": { "Image": "application" } }], "provisioners": [{ "type": "ansible", "playbook_file": "./playbook/application.yml", "host_alias": "applications" }] }
我們只修改了使用 Ansible 作為配置器,需要提供一個指向 playbook 檔案的路徑,我們將其設定為 ./playbook/application.yml
。我們可以看到用於安裝 Nginx 的 play 頂部有一行: hosts: applications
。這是我們用來告訴 Ansible 需要安裝應用程式的主機別名。我們需要告訴 Packer 我們正在為其中一個主機構建映像,所以我們將 host_alias
屬性設定為 applications
。
執行下面的命令來建立 Ansible 配置的伺服器,然後啟動瀏覽器,開啟地址為負載均衡器的域名:
packer build packer.json terraform plan -out terraform.plan terraform apply "terraform.plan" open "http://$(terraform output dns)"
你應該可以在開啟的瀏覽器頁面上看到:Ansible has landed!
英文原文:
http://stephenmann.io/post/a-brief-introduction-to-provisioning/
活動推薦
AIOps 是發展的必然趨勢?在 AI 的學習上要做哪些儲備?落地 AIOps 到底有哪些門檻?如何使用知識圖譜定位故障?AIOps 時代,人才結構和團隊結構應該如何升級?運維如何助力產品運營?
InfoQ 主辦的第四屆 CNUTCon 全球運維技術大會,邀請了 Twitter、RIOT Games、BAT、華為等國內外一線大廠,全方位、多角度向參會者闡述智慧運維時代的新技術、新思想、新實踐。
目前,大會 8 折限時優惠,立減 720 元,團購更優惠!掃描下方二維碼或點選閱讀原文了解,有任何問題歡迎諮詢 Joy 小同學,電話:13269078023(微信同號)。