Ansible 起步指南
這是一篇關於 Ansible 的速成課程,你可以用作小項目的模板,或者幫你深入了解這個神奇的工具。閱讀了本指南之後,你將對自動化服務器配置、部署等有足夠的了解。
Ansible 是什麼,為什麼你該了解?
Ansible 簡單的說是一個配置管理係統configuration management system。你隻需要可以使用 ssh 訪問你的服務器或設備就行。它也不同於其他工具,因為它使用推送的方式,而不是像 puppet 或 chef 那樣使用拉取的方式。你可以將代碼部署到任意數量的服務器上,配置網絡設備或在基礎架構中自動執行任何操作。
前置要求
假設你使用 Mac 或 Linux 作為你的工作站,Ubuntu Trusty 作為你的服務器,並有一些安裝軟件包的經驗。此外,你的計算機上將需要以下軟件。所以,如果你還沒有它們,請先安裝:
- Virtualbox
- Vagrant
- Mac 用戶:Homebrew
情景
我們將模擬 2 個連接到 MySQL 數據庫的 Web 應用程序服務器。Web 應用程序使用 Rails 5 和 Puma。
準備
Vagrantfile
為這個項目創建一個文件夾,並將下麵的內容保存到名為 Vagrantfile 的文件。
VMs = [[ "web1", "10.1.1.11"],[ "web2", "10.1.1.12"],[ "dbserver", "10.1.1.21"],]Vagrant.configure(2) do |config|VMs.each { |vm|config.vm.define vm[0] do |box|box.vm.box = "ubuntu/trusty64"box.vm.network "private_network", ip: vm[1]box.vm.hostname = vm[0]box.vm.provider "virtualbox" do |vb|vb.memory = "512"endend}end
配置你的虛擬網絡
我們希望我們的虛擬機能互相交互,但不要讓流量流出到真實的網絡,所以我們將在 Virtualbox 中創建一個僅主機(HOST-Only)的網絡適配器。
- 打開 Virtualbox
- 轉到 Preferences
- 轉到 Network
- 單擊 Host-Only
- 單擊添加網絡
- 單擊 Adapter
- 將 IPv4 設置為
10.1.1.1,IPv4 網絡掩碼:255.255.255.0 - 單擊 “OK”
測試虛擬機及虛擬網絡
在終端中,在存放 Vagrantfile 的項目目錄中,輸入下麵的命令:
vagrant up
它會創建你的虛擬機,因此會花費一會時間。輸入下麵的命令並驗證輸出內容以檢查是否已經工作:
$ vagrant statusCurrent machine states:web1 running (virtualbox)web2 running (virtualbox)master running (virtualbox)This environment represents multiple VMs. The VMs are all listedabove with their current state. For more information about a specificVM, run `vagrant status NAME`.
現在使用 vagrant 的用戶名和密碼 ,按 Vagrantfile 中的 IP 登錄其中一台虛擬機,這將驗證虛擬機並將它們的密鑰添加到你的已知主機(known_hosts)文件中。
ssh vagrant@10.1.1.11 # password is `vagrant`ssh vagrant@10.1.1.12ssh vagrant@10.1.1.21
恭喜你!現在你已經有可以實驗的服務器了。下麵的剩下的部分!
安裝 Ansible
對於 Mac 用戶:
$ brew install ansible
對於 Ubuntu 用戶:
$ sudo apt install ansible
確保你使用了ansible 最近的版本 2.1 或者更高的版本:
$ ansible --versionansible 2.1.1.0
清單
Ansible 使用清單文件來了解要使用的服務器,以及如何將它們分組以並行執行任務。讓我們為這個項目創建我們的清單文件 inventory,並將它放在與 Vagrantfile 相同的文件夾中:
[all:children]websdb[all:vars]ansible_user=vagrantansible_ssh_pass=vagrant[webs]web1 ansible_host=10.1.1.11web2 ansible_host=10.1.1.12[db]dbserver ansible_host=10.1.1.21
-
[all:children]定義一個組的組(all) -
[all:vars]定義屬於組all的變量 -
[webs]定義一個組,就像[db]一樣 - 文件的其餘部分隻是主機的聲明,帶有它們的名稱和 IP
- 空行表示聲明結束
現在我們有了一個清單,我們可以從命令行開始使用 ansible,指定一個主機或一個組來執行命令。以下是檢查與服務器的連接的命令示例:
$ ansible -i inventory all -m ping
-
-i指定清單文件 -
all指定要操作的服務器或服務器組 -
-m' 指定一個 ansible 模塊,在這種情況下為ping`
下麵是命令輸出:
dbserver | SUCCESS => {"changed": false,"ping": "pong"}web1 | SUCCESS => {"changed": false,"ping": "pong"}web2 | SUCCESS => {"changed": false,"ping": "pong"}
服務器以不同的順序響應,這隻取決於誰先響應,但是這個沒有關係,因為 ansible 獨立保持每台服務器的狀態。
你也可以使用另外一個選項來運行任何命令:
-a <command>
$ ansible -i inventory all -a uptimeweb1 | SUCCESS | rc=0 >>21:43:27 up 25 min, 1 user, load average: 0.00, 0.01, 0.05dbserver | SUCCESS | rc=0 >>21:43:27 up 24 min, 1 user, load average: 0.00, 0.01, 0.05web2 | SUCCESS | rc=0 >>21:43:27 up 25 min, 1 user, load average: 0.00, 0.01, 0.05
這是隻有一台服務器的另外一個例子:
$ ansible -i inventory dbserver -a "df -h /"dbserver | SUCCESS | rc=0 >>Filesystem Size Used Avail Use% Mounted on/dev/sda1 40G 1.4G 37G 4% /
劇本
劇本(playbook)隻是個 YAML 文件,它將清單文件中的服務器組與命令關聯。在 ansible 中的對於關鍵字是 tasks,它可以是一個預期的狀態、shell 命令或許多其它的選項。有關 ansible 可做的所有事情列表,可以查看所有模塊的列表。
下麵是一個運行 shell 命令的劇本示例,將其保存為 playbook1.yml:
---- hosts: alltasks:- shell: uptime
-
---是 YAML 文件的開始 -
- hosts:指定要使用的組 -
tasks:標記任務列表的開始 -
- shell:指定第一個任務使用 shell 模塊 - 記住:YAML 需要縮進結構,確保你始終遵循劇本中的正確結構
用下麵的命令運行它:
$ ansible-playbook -i inventory playbook1.ymlPLAY [all] *********************************************************************TASK [setup] *******************************************************************ok: [web1]ok: [web2]ok: [dbmaster]TASK [command] *****************************************************************changed: [web1]changed: [web2]changed: [dbmaster]PLAY RECAP *********************************************************************dbmaster : ok=2 changed=1 unreachable=0 failed=0web1 : ok=2 changed=1 unreachable=0 failed=0web2 : ok=2 changed=1 unreachable=0 failed=0
正如你所見,ansible 運行了 2 個任務,而不是隻有劇本中的一個。TASK [setup] 是一個隱式任務,它會首先運行以捕獲服務器的信息,如主機名、IP、發行版和更多詳細信息,然後可以使用這些信息運行條件任務。
還有最後的 PLAY RECAP,其中 ansible 顯示了運行了多少個任務以及每個對應的狀態。在我們的例子中,因為我們運行了一個 shell 命令,ansible 不知道結果的狀態,它被認為是 changed。
安裝軟件
我們將使用 apt 在我們的服務器上安裝軟件,因為我們需要 root 權限,所以我們必須使用 become 語句,將這個內容保存在 playbook2.yml 中並運行它(ansible-playbook playbook2.yml):
---- hosts: websbecome_user: rootbecome: truetasks:- apt: name=git state=present
有一些語句可以應用於 ansible 中所有模塊;一個是 name 語句,可以讓我們輸出關於正在執行的任務的更具描述性的文本。要使用它,保持任務內容一樣,但是添加 name :描述性文本 作為第一行,所以我們以前的文本將改成:
---- hosts: websbecome_user: rootbecome: truetasks:- name: This task will make sure git is present on the systemapt: name=git state=present
使用 with_items
當你要處理一個列表時,比如要安裝的項目和軟件包、要創建的文件,可以用 ansible 提供的with_items。下麵是我們如何在 playbook3.yml 中使用它,同時添加一些我們已經知道的其他語句:
---- hosts: allbecome_user: rootbecome: truetasks:- name: Installing dependenciesapt: name={{item}} state=presentwith_items:- git- mysql-client- libmysqlclient-dev- build-essential- python-software-properties
使用 template 和 vars
vars 是一個定義變量語句,可以在 task 語句或 template 文件中使用。 Jinja2 是 Ansible 中使用的模板引擎,但是關於它你不需要學習很多。在你的劇本中定義變量,如下所示:
---- hosts: allvars:- secret_key: VqnzCLdCV9a3jK- path_to_vault: /opt/very/deep/pathtasks:- name: Setting a configuration file using templatetemplate: src=myconfig.j2 dest={{path_to_vault}}/app.conf
正如你看到的,我可以使用 {{path_to_vault}} 作為劇本的一部分,但也因為我使用了 template語句,我可以使用 myconfig.j2 中的任何變量,該文件必須存在一個名為 templates 的子文件夾中。你項目樹應該如下所示:
├── Vagrantfile├── inventory├── playbook1.yml├── playbook2.yml└── templates└── myconfig.j2
當 ansible 找到一個 template 語句後它會在 templates 文件夾內查找,並將把被 {{ 和 }} 括起來的變量展開來。
示例模板:
this is just an example vault_dir: {{path_to_vault}} secret_password: {{secret_key}}
即使你不擴展變量你也可以使用 template。考慮到將來會添加所以我先做了。比如創建一個hosts.j2 模板並加入主機名和 IP。
10.1.1.11 web110.1.1.12 web210.1.1.21 dbserver
這裏要用像這樣的語句:
- name: Installing the hosts file in all serverstemplate: src=hosts.j2 dest=/etc/hosts mode=644
shell 命令
你應該盡量使用模塊,因為 Ansible 可以跟蹤任務的狀態,並避免不必要的重複,但有時 shell 命令是不可避免的。 對於這些情況,Ansible 提供兩個選項:
其他有用的模塊
- apt_repository - 在 Debian 係的發行版中添加/刪除包倉庫
- yum_repository - 在 RedHat 係的發行版中添加/刪除包倉庫
- service - 啟動/停止/重新啟動/啟用/禁用服務
- git - 從 git 服務器部署代碼
- unarchive - 從 Web 或本地源解開軟件包
隻在一台服務器中運行任務
Rails 使用 migrations 來逐步更改數據庫,但由於你有多個應用程序服務器,因此這些遷移任務不能被分配為組任務,而我們隻需要一個服務器來運行遷移。在這種情況下,當使用 run_once時,run_once 將分派任務到一個服務器,並直到這個任務完成繼續下一個任務。你隻需要在你的任務中設置 run_once:true。
- name: 'Run db:migrate'shell: cd {{appdir}};rails db:migraterun_once: true
會失敗的任務
通過指定 ignore_errors:true,你可以運行可能會失敗的任務,但不會影響劇本中剩餘的任務完成。這是非常有用的,例如,當刪除最初並不存在的日誌文件時。
- name: 'Delete logs'shell: rm -f /var/log/nginx/errors.logignore_errors: true
放到一起
現在用我們先前學到的,這裏是每個文件的最終版:
Vagrantfile:
VMs = [[ "web1", "10.1.1.11"],[ "web2", "10.1.1.12"],[ "dbserver", "10.1.1.21"],]Vagrant.configure(2) do |config|VMs.each { |vm|config.vm.define vm[0] do |box|box.vm.box = "ubuntu/trusty64"box.vm.network "private_network", ip: vm[1]box.vm.hostname = vm[0]box.vm.provider "virtualbox" do |vb|vb.memory = "512"endend}end
inventory:
[all:children]websdb[all:vars]ansible_user=vagrantansible_ssh_pass=vagrant[webs]web1 ansible_host=10.1.1.11web2 ansible_host=10.1.1.12[db]dbserver ansible_host=10.1.1.21
templates/hosts.j2:
10.1.1.11 web110.1.1.12 web210.1.1.21 dbserver
templates/my.cnf.j2:
[client]port = 3306socket = /var/run/mysqld/mysqld.sock[mysqld_safe]socket = /var/run/mysqld/mysqld.socknice = 0[mysqld]server-id = 1user = mysqlpid-file = /var/run/mysqld/mysqld.pidsocket = /var/run/mysqld/mysqld.sockport = 3306basedir = /usrdatadir = /var/lib/mysqltmpdir = /tmplc-messages-dir = /usr/share/mysqlskip-external-lockingbind-address = 0.0.0.0key_buffer = 16Mmax_allowed_packet = 16Mthread_stack = 192Kthread_cache_size = 8myisam-recover = BACKUPquery_cache_limit = 1Mquery_cache_size = 16Mlog_error = /var/log/mysql/error.logexpire_logs_days = 10max_binlog_size = 100M[mysqldump]quickquote-namesmax_allowed_packet = 16M[mysql][isamchk]key_buffer = 16M!includedir /etc/mysql/conf.d/
final-playbook.yml:
- hosts: allbecome_user: rootbecome: truetasks:- name: 'Install common software on all servers'apt: name={{item}} state=presentwith_items:- git- mysql-client- libmysqlclient-dev- build-essential- python-software-properties- name: 'Install hosts file'template: src=hosts.j2 dest=/etc/hosts mode=644- hosts: dbbecome_user: rootbecome: truetasks:- name: 'Software for DB server'apt: name={{item}} state=presentwith_items:- mysql-server- percona-xtrabackup- mytop- mysql-utilities- name: 'MySQL config file'template: src=my.cnf.j2 dest=/etc/mysql/my.cnf- name: 'Restart MySQL'service: name=mysql state=restarted- name: 'Grant access to web app servers'shell: echo 'GRANT ALL PRIVILEGES ON *.* TO "root"@"%" WITH GRANT OPTION;FLUSH PRIVILEGES;'|mysql -u root mysql- hosts: websvars:- appdir: /opt/dummyappbecome_user: rootbecome: truetasks:- name: 'Add ruby-ng repo'apt_repository: repo='ppa:brightbox/ruby-ng'- name: 'Install rails software'apt: name={{item}} state=presentwith_items:- ruby-dev- ruby-all-dev- ruby2.2- ruby2.2-dev- ruby-switch- libcurl4-openssl-dev- libssl-dev- zlib1g-dev- nodejs- name: 'Set ruby to 2.2'shell: ruby-switch --set ruby2.2- name: 'Install gems'shell: gem install bundler rails- name: 'Kill puma if running'shell: file /run/puma.pid >/dev/null && kill `cat /run/puma.pid` 2>/dev/nullignore_errors: True- name: 'Clone app repo'git:repo=https://github.com/c0d5x/rails_dummyapp.gitdest={{appdir}}version=stagingforce=yes- name: 'Run bundler'shell: cd {{appdir}};bundler- name: 'Run db:setup'shell: cd {{appdir}};rails db:setuprun_once: true- name: 'Run db:migrate'shell: cd {{appdir}};rails db:migraterun_once: true- name: 'Run rails server'shell: cd {{appdir}};rails server -b 0.0.0.0 -p 80 --pid /run/puma.pid -d
放在你的環境中
將這些文件放在相同的目錄,運行下麵的命令打開你的開發環境:
vagrant upansible-playbook -i inventory final-playbook.yml
部署新的代碼
確保修改了代碼並推送到了倉庫中。接下來,確保你 git 語句中使用了正確的分支:
- name: 'Clone app repo'git:repo=https://github.com/c0d5x/rails_dummyapp.gitdest={{appdir}}version=stagingforce=yes
作為一個例子,你可以修改 version 字段為 master,再次運行劇本:
ansible-playbook -i inventory final-playbook.yml
檢查所有的 web 服務器上的頁麵是否已更改:https://10.1.1.11 或 https://10.1.1.12。將其更改為version = staging 並重新運行劇本並再次檢查頁麵。
你還可以創建隻包含與部署相關的任務的替代劇本,以便其運行更快。
接下來是什麼 ?!
這隻是可以做的很小一部分。我們沒有接觸角色role、過濾器filter、調試等許多其他很棒的功能,但我希望它給了你一個良好的開始!所以,請繼續學習並使用它。
原文發布時間為:2017-01-12
本文來自雲棲社區合作夥伴“Linux中國”
最後更新:2017-05-31 10:32:00