iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 14
1
Kubernetes

在地端建置Angular+ASP.NET Core的DevOps環境系列 第 14

day14_Ansible02_playbook

Ansible最重要的就是playbook.yml啦

當我們用Vagrant建好vm後,再來每個vm要安裝的、要設定的就靠Ansible了
本篇參考:https://www.vagrantup.com/docs/provisioning/ansible_intro.html

Vagrantfile就會運行Ansible啦

Vagrant.configure("2") do |config|
  # ansible,在Vagrant host上執行Ansible
  # ansible_local,在Vagrant guest上執行Ansible
  config.vm.provision :ansible do |ansible|
    ansible.playbook = "playbook.yml"
  end
end

ansible.cfg # 告訴 ansible 什麼檔案放哪裡
[defaults]
inventory = hosts # inventory
remote_user = vagrant
private_key_file = ~/.ssh/id_rsa
host_key_checking = false # 不問是否加入 ssh 的 key

hosts # inventory檔,用來定義ansible的Managed node們
[dev] # 情境

ansible_ssh_host: masterNode1
ansible_ssh_port: 22
ansible_ssh_user: vagrant
ansible_ssh_private_key_file = ~/ssh/id_rsa

playbook.yml
有關playbook重要知識,請參考官網文件
https://docs.ansible.com/ansible/latest/user_guide/playbooks_special_topics.html
例如果要保護機敏資訊(連線字串、password…)就看 Using Vault in playbooks

在playbook看到不懂的關鍵字
先到官網的playbook keywords找看看
https://docs.ansible.com/ansible/latest/reference_appendices/playbooks_keywords.html

---
- name: play1 # 一整組都算
  hosts: all
  tasks:
    - name: ensure ntpd is at the latest version # task 1
      yum: pkg=ntp state=latest # module (command、apt、lineinfile)
      notify:
      - restart ntpd
    - name: task 2
      command: echo "Hello World" 
  handlers:
    - name: restart ntpd
      service: name=ntpd state=restarted
# ================play1 end======================
- name: play2 # 
  hosts: all
  tasks:
    - name: ensure ntpd is at the latest version
      yum: pkg=ntp state=latest
      notify:
      - restart ntpd
  handlers:
    - name: restart ntpd
      service: name=ntpd state=restarted
  • 執行ansilbe-palybook
$ ansible-playbook playbook.yml
  • 重新執行一個在已存在的VM上的playbook
$ vagrant provision # 要在Vagrantfile 裡面定義 ansible.playbook = "playbook.yml"

用vagrant跑ansible,應該會自動建立inventory
.vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory
inventory應該是「vm清單」,但其實還可以給一堆參數

# Generated by Vagrant
machine ansible_ssh_host=127.0.0.1 ansible_ssh_port=2222
手動執行ansible,
-i inventory 
-- private-key 指定private key
-u ssh的username

playbook.yml 要作的腳本

$ ansible-playbook -i .vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory --private-key=.vagrant/machines/default/virtualbox/private_key -u vagrant playbook.yml
--verbose modules執行成功的訊息也顯示
--list-hosts 會影響到哪些 hosts

寫得有點亂,接下來…

一起學習官網的範例吧
Ansible的版本變更很快,新舊文件的yml檔的寫也法也差異很大
我在寫此文章的時候是2.6(2018年10月),如果您在看此文章時已是n年後,此篇就沒啥參考價值啦
https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html#basics
簡體中文的(版本舊很多)
https://ansible-tran.readthedocs.io/en/latest/docs/playbooks_roles.html
module可以到這裡查,每個module都還有範例,不怕寫不出來
https://docs.ansible.com/ansible/latest/modules/list_of_all_modules.html

範例01.

# 最上層沒有name,只有1個play。
---
- hosts: webservers
  vars: # 有2個vars
    http_port: 80
    max_clients: 200
  remote_user: root # user account
  tasks: # 有3個taskS
  - name: install the latest version of Apache(如果已是最新就不會再裝)
    yum: # module,應該是受控端有yum package manager的才能用吧
      name: httpd
      state: latest
  - name: write the apache config file
    template:
      src: /srv/httpd.j2
      dest: /etc/httpd.conf
    notify: # 在 task 結束時 triggered(觸發) ,叫 handler 做事情,我也不知怎麼翻譯
    - restart apache 
  - name: ensure apache is running # 確定 httpd 是 started
    service:
      name: httpd
      state: started
  handlers: # 有點像可重複使用的tasks(數個tasks會call的,就抽到handlers)
    - name: restart apache
      service:
        name: httpd
        state: restarted
# 也可以用 listen 一次觸發多個 handler
handlers:
    - name: restart memcached # 如果 handler 的 name 一樣,只會跑1個
      service:
        name: memcached
        state: restarted
      listen: "restart web services" # 1
    - name: restart apache
      service:
        name: apache
        state:restarted
      listen: "restart web services" # 2

tasks:
    - name: restart everything
      command: echo "this task will restart the web services"
      notify: "restart web services" # 跑上面1、2(依順序)

範例02.

---
- hosts: webservers
  remote_user: yourname
  tasks:
    - service:
        name: nginx
        state: started
      become: yes # 啟用升權限 privilege escalation
      become_method: sudo # 如果需要打密碼, ansible-playbook --ask-become-pass (-K)
      # become_user: mongodbAccount # 變成非root的user
      # (例如:你要操作mongodb時,轉成mongodbAccount的帳號)
      become_method: su 
      # 用 su (sudo/su/pbrun/pfexec/doas/dzdo/ksu/runas/machinectl) 預設是sudo
      # become_method 在play或task可使用  蓋掉ansible.cfg設定的值

become小補充:

- name: Run a command as nobody
 command: somecommand
 become: yes
 become_method: su
 become_user: nobody
# 在task或role可使用
 become_flags: '-s /bin/sh' # 當shell是nologin的時候,使用nobody帳號,跑somecommand

在inventory可使用相關連線變數

# webserver 這台主機,使用manager帳號登入,登入後要提權
webserver ansible_user=manager ansible_become=yes
# ansible_become_method (類似become_method), ansible_become_pass設定提權密碼

範例03.

- hosts: all
  order: sorted 
# 執行順序(inventory,reverse_inventory,sorted,reverse_sorted,shufflt)
# 預設是依 inventory 順序執行
  gather_facts: False # 不收集遠端host的變數
  tasks:
    - debug:
        var: inventory_hostname
# action 使用 command 或 shell
    - name: enable selinux
      command: /sbin/setenforce 1
# 下的指令 return ture,或者乎略錯誤 ignore_errors: True
    - name: run this command and ignore the result
      shell: /usr/bin/somecommand || /bin/true
    - name: run this command and ignore the result
      shell: /usr/bin/somecommand
      ignore_errors: True
# 使用變數,定義在vars section
  - name: create a virtual host file for {{ vhost }}
    template:
      src: somefile.j2 # j2樣板語言
      dest: /etc/httpd/conf.d/{{ vhost }}

include、import

---
# possibly saved as tasks/foo.yml
- name: echo wp_user
  shell: echo {{ wp_user }}
- name: placeholder bar
  command: /bin/bar

tasks:
  - include_tasks: tasks/foo.yml
  - include_tasks: wordpress.yml wp_user=timmy # 可以給參數
# 新的語法
  - { include_tasks: wordpress.yml, wp_user: timmy, ssh_keys: [ 'keys/one.txt', 'keys/two.txt' ] }
# 其他給變數的方式
  - include_tasks: wordpress.yml
    vars:
        wp_user: timmy
        some_list_variable:
          - alpha
          - beta
          - gamma
# 也可以在 handlers section 使用 include
handlers:
  - include_tasks: handlers/handlers.yml # import 跟 include 的 差異 我也不知道~~
  - import_tasks: handlers/handlers.yml

# 也可以用一個yml來import多個playbook
- import_playbook: webservers.yml
- import_playbook: databases.yml

Roles

我們不學怎麼封裝Role,只試圖來瞭解Role,進而使用Role

# 目錄結構,範例
site.yml
webservers.yml
fooservers.yml
roles/
   common/ # 名字叫「common」的role
     tasks/ # 執行此 role 時的 main list of tasks
     handlers/
     files/
     templates/
     vars/ # 其他的變數
     defaults/ # 預設變數
     meta/ # meta data
   webservers/ # 名字叫「webservers」的role
     tasks/
     defaults/
     meta/
所以如果有個role叫x,可能會長這樣:
roles / x / tasks / main.yml
roles / x / handlers / main.yml
roles / x / vars / main.yml
roles / x / defaults / main.yml
roles / x / meta / main.yml

在同一個role底下

任何copy、script、template或include tasks都可以用

roles/x/{files,templates,tasks}/ (dir depends on task)
來參考檔案

playbook的執行順序:

1.pre_tasks
2.被觸發的handlers
3.roles裡的role # role的相依關係定義在meta/main.yml (不執行的條件之類的filter,condition)
4.tasks
5.post_tasks

如果yaml檔要支援多種OS,

(最常見的Ubuntu的apt跟CentOS的yum就不一樣了)

通常會寫在tasks/main.yml

# roles/example/tasks/main.yml # 名字叫「example」的role
- name: added in 2.4, previously you used 'include'
  import_tasks: redhat.yml
  when: ansible_os_platform|lower == 'redhat' # 很好懂吧 when 'redhat' 就 import redhat.yml
- import_tasks: debian.yml
  when: ansible_os_platform|lower == 'debian'

# roles/example/tasks/redhat.yml
- yum:
    name: "httpd"
    state: present

# roles/example/tasks/debian.yml
- apt:
    name: "apache2"
    state: present

使用 Roles 起手式

---
- hosts: webservers
  roles:
     - common
     - webservers

Ansible 2.4以後

在 playbook 中可以用 import_role、include_role

---
- hosts: webservers
  tasks:
  - debug:
      msg: "before we run our role"
  - import_role: # static
      name: example
  - include_role: # dynamic
      name: example
  - debug:
      msg: "after we ran our role"

也可以給完整路徑

  roles:
    - role: '/path/to/my/roles/common'

可以指派tags

---
- hosts: webservers
 roles:
  - role: bar
   tags: ["foo"]
   vars: # 給變數
         message: "first"
  # using YAML shorthand, 較新的語法
  - { role: foo, tags: ["bar", "baz"], vars: { message: "second" } }

# 或者這樣寫也可以
---
- hosts: webservers
 tasks:
 - import_role:
   name: foo
  tags:
  - bar
  - baz

Role Dependencies

# roles/myapp/meta/main.yml 定義在meta裡
# Role Dependencies 在 該role之前執行
present
---
dependencies:
  - role: common
    vars:
    some_parameter: 3
  - role: apache
    vars:
    apache_port: 80

官網給一個範例:

# meta/main.yml
---
dependencies:
- role: wheel # 執行順序3
  vars:
  n: 1
- role: wheel
  vars:
  n: 2
- role: wheel  
  vars:
  n: 3
- role: wheel
  vars:
  n: 4

---
dependencies:
- role: tire # 執行順序1
- role: brake # 執行順序2

---
allow_duplicates: true # 設ture,同一個role wheel給不同的參數才會都執行

執行結果:
tire(n=1)
brake(n=1)
wheel(n=1)
tire(n=2)
brake(n=2)
wheel(n=2)
...
car

在Roles裡加入Modules、Plugins

目錄結構會像下面,我們只要會用、看得懂即可

roles/
  my_custom_modules/
    library/
     module1
     module2
roles/
  my_custom_filter/
    filter_plugins
     filter1
     filter2
  • 上例的「Role的搜尋目錄的順序」為:
roles/
/etc/ansible/roles

Error Handling

 tasks:
 - name: Attempt and graceful roll back demo
  block: # 正常跑這個section
   - debug:
     msg: 'I execute normally'
   - command: /bin/false
   - debug:
     msg: 'I never execute, due to the above task failing'
  rescue: # 當block有error時執行
   - debug:
     msg: 'I caught an error'
   - command: /bin/false
   - debug:
     msg: 'I also never execute :-('
  always: # 都會跑
   - debug:
     msg: "This always executes"

===
很不好意思,
今天只有介紹「內功心法」
其實module才是「武功招式」,
結果module只有寫whoami就來不及寫了~
就到(10/29了)

提供一些連結,role遇到什麼就認真查文件囉@@~

module:

whoami

command: whoami
register: check_user # 註冊一個變數variable,把whoami的輸出,存到check_user變數
debug: msg="{{ check_user }}" # 顯示變數{{check_user}},方便除錯

===

大推凍仁哥的文章

module

template


上一篇
day13_Ansible01_安裝及介紹
下一篇
day15_k8s01_kubeadm失敗筆記+文章導讀
系列文
在地端建置Angular+ASP.NET Core的DevOps環境31

尚未有邦友留言

立即登入留言