2018年1月22日

Ansible

目前在 DevOps 設定管理的工具中,以 Puppe、Chef、Salt、Ansible 四者最有名,而 Puppe 跟 Chef 兩者是以 ruby 開發, Salt 與 Ansible 都是以 python 開發的,在 20162017 DevOps 的統計中,Ansible 自 2015 由 10% 上升至 20% 及 21%,chef 及 puppet 在 2017 的比例有下降一些,但還是比較多人使用的工具。

Ansible is Simple IT Automation 最簡單的 IT 自動化工具,包含 自動化部署APP、自動化管理配置、自動化的持續交付、自動化的(AWS)雲服務管理。ansible 是利用 paramiko 開發的,paramiko是一個純Python實現的ssh協議庫。因此不需要在遠端主機上安裝client或agents,因為 ansible 單純地是以 ssh 來和遠程主機進行通訊。

在 Ender's Game 裡面,Ender 是利用一個的超光速即時的通訊系統,在後端指揮中心,遠端下達指令給前方的戰機及戰艦,這個系統被稱為 Ansible,用途是 instantaneous communication across any distance。因此 Ansible 就跟 devops 的目標一致,這樣的工具就是要讓隱身在後面的工程師,個個都像 Ender 一樣,一呼百應而且沒有絲毫的遲疑,當然瞬間的成敗也由 Ender 獨力承擔。

安裝

ansible 對於管理端的主機,稱為 control machine,必須要安裝 python 2.6+,被管理(託管)的主機,稱為 managed node,要安裝 sshd,也要安裝 python 2.6+。

在 macos 安裝 ansible 可使用 macports 或是 homebrew

sudo port install ansible

當然也可以因應不同 OS 用 yum, apt, pip, deb 進行安裝

安裝測試環境

可用 VM 來進行測試,目前可選用 virtualbox 或是 docker

  • vagrant

必須安裝 vagrant 及 virtualbox,可選用 vagrant 官方提供的 VM

mkdir vagrant_centos7
cd vagrant_centos7

vagrant init geerlingguy/centos7
vagrant up
# 關機
vagrant halt

# 重開機
vagrant reload

# ssh
vagrant ssh

# 移除
vagrant destory

取得 VM 的 ssh 設定

$ vagrant ssh-config
Host default
  HostName 127.0.0.1
  User vagrant
  Port 2222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile /Users/charley/VirtualBoxVMs/vagrant/.vagrant/machines/default/virtualbox/private_key
  IdentitiesOnly yes
  LogLevel FATAL

ansible.cfg 設定檔

$ vi ansible.cfg
[defaults]

inventory = hosts
remote_user = vagrant
private_key_file = .vagrant/machines/default/virtualbox/private_key
host_key_checking = False

ansible 會依照這四個順序尋找適合的 ansible.cfg

* ANSIBLE_CONFIG 環境變數
* 目前工作目錄的 ansible.cfg
* 使用者 Home 目錄的 .ansible.cfg
* 系統設定 /etc/ansible/ansible.cfg (如果是用macports 安裝則是在 /opt/local/etc/ansible/ansible.cfg)

hosts 設定檔

$ vi hosts
server1  ansible_ssh_host=127.0.0.1  ansible_ssh_port=2222

[local]
server1

利用 ansible echo 一段文字

$ ansible all -m command -a 'echo Hello World on Vagrant.'
server1 | SUCCESS | rc=0 >>
Hello World on Vagrant.
  • docker

準備一個有安裝了 sshd 的 docker VM,將 10022 對應到 TCP 22 (ssh),因為 CentOS 7 預設會安裝 python 2.7.5,就不需要處理 python 的安裝問題。

docker run -d \
 -p 10022:22\
 --sysctl net.ipv6.conf.all.disable_ipv6=1\
 -e "container=docker" --privileged=true -v /sys/fs/cgroup:/sys/fs/cgroup --name atest centosssh /usr/sbin/init

如果直接用 ssh client 連線

ssh root@localhost -p 10022

在另一個新的工作目錄,建立 ansible.cfg 設定檔

$ vi ansible.cfg
[defaults]

inventory = hosts
remote_user = root
host_key_checking = False

建立 hosts 設定檔

$ vi hosts
server1  ansible_ssh_host=127.0.0.1  ansible_ssh_port=10022 ansible_ssh_pass=max168kit

[local]
server1

測試 echo 指令

$ ansible all -m command -a 'echo Hello World on Docker.'
server1 | SUCCESS | rc=0 >>
Hello World on Docker.

使用 ansible

有兩種方式使用 ansible,分別是 ad-hoc command 及 playbook

  • ad-hoc command

一次只能使用一個指令,像是在 console mode,一次下達一個指令

$ ansible all -m ping
server1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

$ ansible all -m command -a "echo Hello World"
server1 | SUCCESS | rc=0 >>
Hello World
  • playbook

像是 shell script 一樣,可組合多個指令,這是一份使用 YAML 格式製作的文件。通常一個 playbook 中會有多個 Play, Task, Module

  1. Play: 某個特定的工作,裡面會包含多個 Task

  2. Task: 為了實現 Play,每一個 Play 都會有一個 Task 列表,每一個 Task 在所有對應的主機上都執行完成後,才會進行下一個 Task

  3. Module: 每一個 Task 的目的是執行一個 Module,Module 也就是 ansible 提供的一些操作指令,可到 Module Index 文件查詢可以使用的 Modules

以下是一個簡單的 hello world playbook,裡面有一個 Play: say 'hello world',兩個 Tasks,第一個 Task 使用了 command 這個 Module,第二個 Task 使用 debug

vi hello_world.yml

---

- name: say 'hello world'
  hosts: all
  tasks:

    - name: echo 'hello world'
      command: echo 'hello world'
      register: result

    - name: print stdout
      debug:
        msg: "{{ result.stdout }}"

執行 playbook

$ ansible-playbook hello_world.yml

PLAY [say 'hello world'] ***************************************************************************

TASK [Gathering Facts] *****************************************************************************
ok: [server1]

TASK [echo 'hello world'] **************************************************************************
changed: [server1]

TASK [print stdout] ********************************************************************************
ok: [server1] => {
    "msg": "hello world"
}

PLAY RECAP *****************************************************************************************
server1                    : ok=3    changed=1    unreachable=0    failed=0

常用的 Modules

- name: install the latest version of Apache
  yum:
    name: httpd
    state: latest

- name: remove the Apache package
  yum:
    name: httpd
    state: absent

- name: upgrade all packages
  yum:
    name: '*'
    state: latest

- name: upgrade all packages, excluding kernel & foo related packages
  yum:
    name: '*'
    state: latest
    exclude: kernel*,foo*

- name: install the 'Development tools' package group
  yum:
    name: "@Development tools"
    state: present
  • command 遠端執行某個 shell command,不支援變數 > , >, |, ; 和 & ,如果需要這些功能,要改用 shell
- name: Reboot at now
  command: /sbin/shutdown -r now
  
- name: create .ssh directory
  command: mkdir .ssh creates=.ssh/

- name: cat /etc/passwd
  command: cat passwd
  args:
    chdir: /etc


- name: check files number
  shell: ls /home/docker/ | wc -l

- name: kill all python process
  shell: kill -9 $(ps aux | grep python | awk '{ print $2 }')
  • copy 將 local file 傳送到遠端機器
- name: copy ssh public key to remote node
  copy:
    src: files/id_rsa.pub
    dest: /home/docker/.ssh/authorized_keys
    owner: docker
    group: docker
    mode: 0644
    
- name: copy nginx vhost and backup the original
  copy:
    src: files/ironman.conf
    dest: /etc/nginx/sites-available/default
    owner: root
    group: root
    mode: 0644
    backup: yes
  • file 遠端建立和刪除檔案、目錄、links
- name: touch a file, and set the permissions
  file:
    path: /etc/motd
    state: touch
    mode: "u=rw,g=r,o=r"

- name: create a directory, and set the permissions
  file:
    path: /home/docker/.ssh/
    state: directory
    owner: docker
    mode: "700"

- name: create a symlink file
  file:
    src: /tmp
    dest: /home/docker/tmp
    state: link
  • lineinfile 可用正規表示式對檔案進行插入或取代文字,類似 sed
- name: remove sudo permission of docker
  lineinfile:
    dest: /etc/sudoers
    state: absent
    regexp: '^docker'

- name: set localhost as 127.0.0.1
  lineinfile:
    dest: /etc/hosts
    regexp: '^127\.0\.0\.1'
    line: '127.0.0.1 localhost'
    owner: root
    group: root
    mode: 0644
- name: start nginx service
  service:
    name: nginx
    state: started

- name: stop nginx service
  service:
    name: nginx
    state: stopped

- name: restart network service
  service:
    name: network
    state: restarted
    args: eth0    
  • stat 檢查檔案狀態
- name: check the 'vimrc' target exists
  stat:
    path: /home/docker/.vimrc
  register: stat_vimrc

- name: touch vimrc
  file:
    path: /home/docker/.vimrc
    state: touch
          mode: "u=rw,g=r,o=r"
  when: stat_vimrc.stat.exists == false

- name: Use md5sum to calculate checksum
  stat:
    path: /path/to/something
    checksum_algorithm: md5sum

References

Red Hat併購DevOps新秀Ansible

YAML wiki

Ansible 自動化部署工具

Chef 自動化部署工具

現代 IT 人一定要知道的 Ansible 自動化組態技巧

ansible Getting Started

Ansible中文權威指南

七分鐘掌握 Ansible 核心觀念

Ansible for Devops

自動化工具——ansible中文指南