閱讀280 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Ansible 起步指南

這是一篇關於 Ansible 的速成課程,你可以用作小項目的模板,或者幫你深入了解這個神奇的工具。閱讀了本指南之後,你將對自動化服務器配置、部署等有足夠的了解。

Ansible 是什麼,為什麼你該了解?

Ansible 簡單的說是一個配置管理係統configuration management system。你隻需要可以使用 ssh 訪問你的服務器或設備就行。它也不同於其他工具,因為它使用推送的方式,而不是像 puppet 或 chef 那樣使用拉取的方式。你可以將代碼部署到任意數量的服務器上,配置網絡設備或在基礎架構中自動執行任何操作。

前置要求

假設你使用 Mac 或 Linux 作為你的工作站,Ubuntu Trusty 作為你的服務器,並有一些安裝軟件包的經驗。此外,你的計算機上將需要以下軟件。所以,如果你還沒有它們,請先安裝:

情景

我們將模擬 2 個連接到 MySQL 數據庫的 Web 應用程序服務器。Web 應用程序使用 Rails 5 和 Puma。

準備

Vagrantfile

為這個項目創建一個文件夾,並將下麵的內容保存到名為 Vagrantfile 的文件。


  1. VMs = [
  2. [ "web1", "10.1.1.11"],
  3. [ "web2", "10.1.1.12"],
  4. [ "dbserver", "10.1.1.21"],
  5. ]
  6. Vagrant.configure(2) do |config|
  7. VMs.each { |vm|
  8. config.vm.define vm[0] do |box|
  9. box.vm.box = "ubuntu/trusty64"
  10. box.vm.network "private_network", ip: vm[1]
  11. box.vm.hostname = vm[0]
  12. box.vm.provider "virtualbox" do |vb|
  13. vb.memory = "512"
  14. end
  15. end
  16. }
  17. end

配置你的虛擬網絡

我們希望我們的虛擬機能互相交互,但不要讓流量流出到真實的網絡,所以我們將在 Virtualbox 中創建一個僅主機(HOST-Only)的網絡適配器。

  1. 打開 Virtualbox
  2. 轉到 Preferences
  3. 轉到 Network
  4. 單擊 Host-Only
  5. 單擊添加網絡
  6. 單擊 Adapter
  7. 將 IPv4 設置為 10.1.1.1,IPv4 網絡掩碼:255.255.255.0
  8. 單擊 “OK”

測試虛擬機及虛擬網絡

在終端中,在存放 Vagrantfile 的項目目錄中,輸入下麵的命令:


  1. vagrant up

它會創建你的虛擬機,因此會花費一會時間。輸入下麵的命令並驗證輸出內容以檢查是否已經工作:


  1. $ vagrant status
  2. Current machine states:
  3. web1 running (virtualbox)
  4. web2 running (virtualbox)
  5. master running (virtualbox)
  6. This environment represents multiple VMs. The VMs are all listed
  7. above with their current state. For more information about a specific
  8. VM, run `vagrant status NAME`.

現在使用 vagrant 的用戶名和密碼 ,按 Vagrantfile 中的 IP 登錄其中一台虛擬機,這將驗證虛擬機並將它們的密鑰添加到你的已知主機(known_hosts)文件中。


  1. ssh vagrant@10.1.1.11 # password is `vagrant`
  2. ssh vagrant@10.1.1.12
  3. ssh vagrant@10.1.1.21

恭喜你!現在你已經有可以實驗的服務器了。下麵的剩下的部分!

安裝 Ansible

對於 Mac 用戶:


  1. $ brew install ansible

對於 Ubuntu 用戶:


  1. $ sudo apt install ansible

確保你使用了ansible 最近的版本 2.1 或者更高的版本:


  1. $ ansible --version
  2. ansible 2.1.1.0

清單

Ansible 使用清單文件來了解要使用的服務器,以及如何將它們分組以並行執行任務。讓我們為這個項目創建我們的清單文件 inventory,並將它放在與 Vagrantfile 相同的文件夾中:


  1. [all:children]
  2. webs
  3. db
  4. [all:vars]
  5. ansible_user=vagrant
  6. ansible_ssh_pass=vagrant
  7. [webs]
  8. web1 ansible_host=10.1.1.11
  9. web2 ansible_host=10.1.1.12
  10. [db]
  11. dbserver ansible_host=10.1.1.21
  • [all:children] 定義一個組的組(all
  • [all:vars] 定義屬於組 all 的變量
  • [webs] 定義一個組,就像 [db] 一樣
  • 文件的其餘部分隻是主機的聲明,帶有它們的名稱和 IP
  • 空行表示聲明結束

現在我們有了一個清單,我們可以從命令行開始使用 ansible,指定一個主機或一個組來執行命令。以下是檢查與服務器的連接的命令示例:


  1. $ ansible -i inventory all -m ping
  • -i 指定清單文件
  • all 指定要操作的服務器或服務器組
  • -m' 指定一個 ansible 模塊,在這種情況下為ping`

下麵是命令輸出:


  1. dbserver | SUCCESS => {
  2. "changed": false,
  3. "ping": "pong"
  4. }
  5. web1 | SUCCESS => {
  6. "changed": false,
  7. "ping": "pong"
  8. }
  9. web2 | SUCCESS => {
  10. "changed": false,
  11. "ping": "pong"
  12. }

服務器以不同的順序響應,這隻取決於誰先響應,但是這個沒有關係,因為 ansible 獨立保持每台服務器的狀態。

你也可以使用另外一個選項來運行任何命令:

  • -a <command>

  1. $ ansible -i inventory all -a uptime
  2. web1 | SUCCESS | rc=0 >>
  3. 21:43:27 up 25 min, 1 user, load average: 0.00, 0.01, 0.05
  4. dbserver | SUCCESS | rc=0 >>
  5. 21:43:27 up 24 min, 1 user, load average: 0.00, 0.01, 0.05
  6. web2 | SUCCESS | rc=0 >>
  7. 21:43:27 up 25 min, 1 user, load average: 0.00, 0.01, 0.05

這是隻有一台服務器的另外一個例子:


  1. $ ansible -i inventory dbserver -a "df -h /"
  2. dbserver | SUCCESS | rc=0 >>
  3. Filesystem Size Used Avail Use% Mounted on
  4. /dev/sda1 40G 1.4G 37G 4% /

劇本

劇本(playbook)隻是個 YAML 文件,它將清單文件中的服務器組與命令關聯。在 ansible 中的對於關鍵字是 tasks,它可以是一個預期的狀態、shell 命令或許多其它的選項。有關 ansible 可做的所有事情列表,可以查看所有模塊的列表

下麵是一個運行 shell 命令的劇本示例,將其保存為 playbook1.yml


  1. ---
  2. - hosts: all
  3. tasks:
  4. - shell: uptime
  • --- 是 YAML 文件的開始
  • - hosts:指定要使用的組
  • tasks:標記任務列表的開始
  • - shell:指定第一個任務使用 shell 模塊
  • 記住:YAML 需要縮進結構,確保你始終遵循劇本中的正確結構

用下麵的命令運行它:


  1. $ ansible-playbook -i inventory playbook1.yml
  2. PLAY [all] *********************************************************************
  3. TASK [setup] *******************************************************************
  4. ok: [web1]
  5. ok: [web2]
  6. ok: [dbmaster]
  7. TASK [command] *****************************************************************
  8. changed: [web1]
  9. changed: [web2]
  10. changed: [dbmaster]
  11. PLAY RECAP *********************************************************************
  12. dbmaster : ok=2 changed=1 unreachable=0 failed=0
  13. web1 : ok=2 changed=1 unreachable=0 failed=0
  14. web2 : 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):


  1. ---
  2. - hosts: webs
  3. become_user: root
  4. become: true
  5. tasks:
  6. - apt: name=git state=present

有一些語句可以應用於 ansible 中所有模塊;一個是 name 語句,可以讓我們輸出關於正在執行的任務的更具描述性的文本。要使用它,保持任務內容一樣,但是添加 name :描述性文本 作為第一行,所以我們以前的文本將改成:


  1. ---
  2. - hosts: webs
  3. become_user: root
  4. become: true
  5. tasks:
  6. - name: This task will make sure git is present on the system
  7. apt: name=git state=present

使用 with_items

當你要處理一個列表時,比如要安裝的項目和軟件包、要創建的文件,可以用 ansible 提供的with_items。下麵是我們如何在 playbook3.yml 中使用它,同時添加一些我們已經知道的其他語句:


  1. ---
  2. - hosts: all
  3. become_user: root
  4. become: true
  5. tasks:
  6. - name: Installing dependencies
  7. apt: name={{item}} state=present
  8. with_items:
  9. - git
  10. - mysql-client
  11. - libmysqlclient-dev
  12. - build-essential
  13. - python-software-properties

使用 template 和 vars

vars 是一個定義變量語句,可以在 task 語句或 template 文件中使用。 Jinja2 是 Ansible 中使用的模板引擎,但是關於它你不需要學習很多。在你的劇本中定義變量,如下所示:


  1. ---
  2. - hosts: all
  3. vars:
  4. - secret_key: VqnzCLdCV9a3jK
  5. - path_to_vault: /opt/very/deep/path
  6. tasks:
  7. - name: Setting a configuration file using template
  8. template: src=myconfig.j2 dest={{path_to_vault}}/app.conf

正如你看到的,我可以使用 {{path_to_vault}} 作為劇本的一部分,但也因為我使用了 template語句,我可以使用 myconfig.j2 中的任何變量,該文件必須存在一個名為 templates 的子文件夾中。你項目樹應該如下所示:


  1. ├── Vagrantfile
  2. ├── inventory
  3. ├── playbook1.yml
  4. ├── playbook2.yml
  5. └── templates
  6. └── myconfig.j2

當 ansible 找到一個 template 語句後它會在 templates 文件夾內查找,並將把被 {{ 和 }} 括起來的變量展開來。

示例模板:


  1. this is just an example vault_dir: {{path_to_vault}} secret_password: {{secret_key}}

即使你不擴展變量你也可以使用 template。考慮到將來會添加所以我先做了。比如創建一個hosts.j2 模板並加入主機名和 IP。


  1. 10.1.1.11 web1
  2. 10.1.1.12 web2
  3. 10.1.1.21 dbserver

這裏要用像這樣的語句:


  1. - name: Installing the hosts file in all servers
  2. template: src=hosts.j2 dest=/etc/hosts mode=644

shell 命令

你應該盡量使用模塊,因為 Ansible 可以跟蹤任務的狀態,並避免不必要的重複,但有時 shell 命令是不可避免的。 對於這些情況,Ansible 提供兩個選項:

  • command:直接運行一個命令,沒有環境變量或重定向(|<> 等)
  • shell:運行 /bin/sh 並展開變量和支持重定向

其他有用的模塊

  • apt_repository - 在 Debian 係的發行版中添加/刪除包倉庫
  • yum_repository - 在 RedHat 係的發行版中添加/刪除包倉庫
  • service - 啟動/停止/重新啟動/啟用/禁用服務
  • git - 從 git 服務器部署代碼
  • unarchive - 從 Web 或本地源解開軟件包

隻在一台服務器中運行任務

Rails 使用 migrations 來逐步更改數據庫,但由於你有多個應用程序服務器,因此這些遷移任務不能被分配為組任務,而我們隻需要一個服務器來運行遷移。在這種情況下,當使用 run_once時,run_once 將分派任務到一個服務器,並直到這個任務完成繼續下一個任務。你隻需要在你的任務中設置 run_once:true


  1. - name: 'Run db:migrate'
  2. shell: cd {{appdir}};rails db:migrate
  3. run_once: true

會失敗的任務

通過指定 ignore_errors:true,你可以運行可能會失敗的任務,但不會影響劇本中剩餘的任務完成。這是非常有用的,例如,當刪除最初並不存在的日誌文件時。


  1. - name: 'Delete logs'
  2. shell: rm -f /var/log/nginx/errors.log
  3. ignore_errors: true

放到一起

現在用我們先前學到的,這裏是每個文件的最終版:

Vagrantfile


  1. VMs = [
  2. [ "web1", "10.1.1.11"],
  3. [ "web2", "10.1.1.12"],
  4. [ "dbserver", "10.1.1.21"],
  5. ]
  6. Vagrant.configure(2) do |config|
  7. VMs.each { |vm|
  8. config.vm.define vm[0] do |box|
  9. box.vm.box = "ubuntu/trusty64"
  10. box.vm.network "private_network", ip: vm[1]
  11. box.vm.hostname = vm[0]
  12. box.vm.provider "virtualbox" do |vb|
  13. vb.memory = "512"
  14. end
  15. end
  16. }
  17. end

inventory


  1. [all:children]
  2. webs
  3. db
  4. [all:vars]
  5. ansible_user=vagrant
  6. ansible_ssh_pass=vagrant
  7. [webs]
  8. web1 ansible_host=10.1.1.11
  9. web2 ansible_host=10.1.1.12
  10. [db]
  11. dbserver ansible_host=10.1.1.21

templates/hosts.j2:


  1. 10.1.1.11 web1
  2. 10.1.1.12 web2
  3. 10.1.1.21 dbserver

templates/my.cnf.j2


  1. [client]
  2. port = 3306
  3. socket = /var/run/mysqld/mysqld.sock
  4. [mysqld_safe]
  5. socket = /var/run/mysqld/mysqld.sock
  6. nice = 0
  7. [mysqld]
  8. server-id = 1
  9. user = mysql
  10. pid-file = /var/run/mysqld/mysqld.pid
  11. socket = /var/run/mysqld/mysqld.sock
  12. port = 3306
  13. basedir = /usr
  14. datadir = /var/lib/mysql
  15. tmpdir = /tmp
  16. lc-messages-dir = /usr/share/mysql
  17. skip-external-locking
  18. bind-address = 0.0.0.0
  19. key_buffer = 16M
  20. max_allowed_packet = 16M
  21. thread_stack = 192K
  22. thread_cache_size = 8
  23. myisam-recover = BACKUP
  24. query_cache_limit = 1M
  25. query_cache_size = 16M
  26. log_error = /var/log/mysql/error.log
  27. expire_logs_days = 10
  28. max_binlog_size = 100M
  29. [mysqldump]
  30. quick
  31. quote-names
  32. max_allowed_packet = 16M
  33. [mysql]
  34. [isamchk]
  35. key_buffer = 16M
  36. !includedir /etc/mysql/conf.d/

final-playbook.yml


  1. - hosts: all
  2. become_user: root
  3. become: true
  4. tasks:
  5. - name: 'Install common software on all servers'
  6. apt: name={{item}} state=present
  7. with_items:
  8. - git
  9. - mysql-client
  10. - libmysqlclient-dev
  11. - build-essential
  12. - python-software-properties
  13. - name: 'Install hosts file'
  14. template: src=hosts.j2 dest=/etc/hosts mode=644
  15. - hosts: db
  16. become_user: root
  17. become: true
  18. tasks:
  19. - name: 'Software for DB server'
  20. apt: name={{item}} state=present
  21. with_items:
  22. - mysql-server
  23. - percona-xtrabackup
  24. - mytop
  25. - mysql-utilities
  26. - name: 'MySQL config file'
  27. template: src=my.cnf.j2 dest=/etc/mysql/my.cnf
  28. - name: 'Restart MySQL'
  29. service: name=mysql state=restarted
  30. - name: 'Grant access to web app servers'
  31. shell: echo 'GRANT ALL PRIVILEGES ON *.* TO "root"@"%" WITH GRANT OPTION;FLUSH PRIVILEGES;'|mysql -u root mysql
  32. - hosts: webs
  33. vars:
  34. - appdir: /opt/dummyapp
  35. become_user: root
  36. become: true
  37. tasks:
  38. - name: 'Add ruby-ng repo'
  39. apt_repository: repo='ppa:brightbox/ruby-ng'
  40. - name: 'Install rails software'
  41. apt: name={{item}} state=present
  42. with_items:
  43. - ruby-dev
  44. - ruby-all-dev
  45. - ruby2.2
  46. - ruby2.2-dev
  47. - ruby-switch
  48. - libcurl4-openssl-dev
  49. - libssl-dev
  50. - zlib1g-dev
  51. - nodejs
  52. - name: 'Set ruby to 2.2'
  53. shell: ruby-switch --set ruby2.2
  54. - name: 'Install gems'
  55. shell: gem install bundler rails
  56. - name: 'Kill puma if running'
  57. shell: file /run/puma.pid >/dev/null && kill `cat /run/puma.pid` 2>/dev/null
  58. ignore_errors: True
  59. - name: 'Clone app repo'
  60. git:
  61. repo=https://github.com/c0d5x/rails_dummyapp.git
  62. dest={{appdir}}
  63. version=staging
  64. force=yes
  65. - name: 'Run bundler'
  66. shell: cd {{appdir}};bundler
  67. - name: 'Run db:setup'
  68. shell: cd {{appdir}};rails db:setup
  69. run_once: true
  70. - name: 'Run db:migrate'
  71. shell: cd {{appdir}};rails db:migrate
  72. run_once: true
  73. - name: 'Run rails server'
  74. shell: cd {{appdir}};rails server -b 0.0.0.0 -p 80 --pid /run/puma.pid -d

放在你的環境中

將這些文件放在相同的目錄,運行下麵的命令打開你的開發環境:


  1. vagrant up
  2. ansible-playbook -i inventory final-playbook.yml

部署新的代碼

確保修改了代碼並推送到了倉庫中。接下來,確保你 git 語句中使用了正確的分支:


  1. - name: 'Clone app repo'
  2. git:
  3. repo=https://github.com/c0d5x/rails_dummyapp.git
  4. dest={{appdir}}
  5. version=staging
  6. force=yes

作為一個例子,你可以修改 version 字段為 master,再次運行劇本:


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

  上一篇:go  《Spring實戰(第4版)》——1.4 Spring的新功能
  下一篇:go  如何在 Docker 中設置 Go 並部署應用