用 Ansible 和 yadm 來加速重灌流程

By Shaform, Sat 25 March 2017, in category Notes

ansible, yadm

前言

還記得當年修計算機系統管理時曾經為了做 NFS + NIS 的作業自行開了三台虛擬機設定環境,同時灌三台電腦實在是煞費精力。在此之後雖然比較少遇到此情形,但還是得常常重灌電腦。像是研究所期間,自己的桌機、筆電,以及實驗室的桌機都是自己裝的 Ubuntu,而每次升級病發就會想重灌成新的版本,久了以後慢慢把安裝的東西寫成 Bash scripts,可是還是相當繁瑣。

就在最近的時候,偷聽大大們聊天,得知了 Ansible 這個方便的自動化部屬工具。由於它使用時目的端完全不須安裝任何套件,而是從本地端完全使用 SSH 進去目的端安裝設定軟體,感覺也挺適合個人重灌的需求。於是就花了一點時間研究了一下。在此同時,也得知了 yadm 這個 dotfiles 管理套件,也花了一點時間把原本用 Makefile 來安裝的 dotfiles 改成用 yadm 管理了。

Ansible 設定

實際上 Ansible 的功用是用來自動化部署而不是拿來重灌個人電腦的,所以尋找文件時常常看到許多複雜功能。這份文件只挑些用在重灌上有用的部份。

安裝 Ansible 的方法非常簡單,只要使用 pip install ansible 或者是 sudo apt install ansible 即可。

然後我建立一個資料夾放置設定檔,組織成如下所示:

.
├── ansible.cfg
├── hosts
├── anaconda.yml
├── desktop.yml
└── roles
    ├── anaconda
    │   └── tasks
    │       └── main.yml
    ├── base
    │   └── tasks
    │       └── main.yml
    └── desktop
        └── tasks
            └── main.yml

首先在 ansible.cfg 裡指定用來設定有哪些機器的 hostfile 的位置:

[defaults]
hostfile = hosts

然後在 hosts 裡設定機器位址,例如:

[host1]
192.168.1.2

[host2]
192.168.1.3 ansible_ssh_user=bob ansible_ssh_private_key_file=~/.ssh/bob.pem

[servers]
192.168.1.4
192.168.1.5
192.168.1.6

每個 group 可以設定多個機器,也可以指定使用者名稱和登入方法等等,見:〈Inventory〉

deploy 則是一個簡單的 Bash script,用來方便執行。使用方法為:

./deploy HOST YML

利用 HOST 指定要設定的機器名稱,例如之前設定的 host1 或者 localhostYML 則是要執行的 playbook 設定檔,例如最上層的 anaconda.yml 或者 desktop.yml

#!/bin/bash
HOST=${1:-localhost}
YML=${2:-desktop.yml}
ansible-playbook --ask-become-pass --extra-vars="hosts=$HOST" $YML

desktop.ymlanaconda.yml 則是簡單的 Playbooks,用來指定要執行哪些 tasks。例如我在 desktop.yml 寫要用 root 權限執行 basedesktop 兩個 roles:

---

- hosts: '{{ hosts }}'
  become: true
  become_user: root
  roles:
    - base
    - desktop

anaconda.yml 則寫要用正常使用者權限執行 anaconda 這個 role:

---

- hosts: '{{ hosts }}'
  roles:
    - anaconda

Roles

最後的 roles 資料夾底下把相關的設定項目放在一起,這樣分開設定的好處是,可以根據不同機器的需求指定要執行哪些 roles,此外,也可以用 git submodule 之類的方法直接利用別人寫好的 roles。

先從 base.yml 開始,這個檔案裡我設置了一些基本的系統設定:

---

# 建立 sudoers.d 資料夾,以放置設定檔
- name: mkdir for configure files
  file:
    dest: '{{ item.dest }}'
    owner: root
    group: root
    mode: '{{ item.mode | default("0755") }}'
    state: directory

  with_items:
    - dest: /etc/sudoers.d

# 因為我自己的使用者 umask 設的比較嚴格,避免 sudo 受影響所以加上這個把 sudo 的 umask 轉回來
- name: set sudo umask
  copy:
    dest: '{{ item.dest }}'
    owner: root
    group: root
    mode: '{{ item.mode | default("0644") }}'
    content: '{{ item.content }}'

  with_items:
    - dest: /etc/sudoers.d/umask
      mode: '0400'
      content: |
        # This file is managed by Ansible. DO NOT EDIT.

        Defaults umask_override
        Defaults umask = 0002

# 更新系統
- name: upgrade packages
  apt:
    upgrade: dist
    update_cache: yes

# 安裝常用套件
- name: install core packages
  apt:
    name: '{{ item.name }}'
    state: '{{ item.state | default("present") }}'
    purge: yes

  with_items:
    - name: git-core
    - name: gufw
    - name: htop
    - name: p7zip-full
    - name: p7zip-rar
    - name: screen
    - name: tmux
    - name: yadm

desktop.yml 則用來放個人桌機常用的設定:

---

# 建立資料夾來放之後的設定檔
- name: mkdir for configure files
  file:
    dest: '{{ item.dest }}'
    owner: root
    group: root
    mode: '{{ item.mode | default("0755") }}'
    state: directory

  with_items:
    - dest: /usr/share/lightdm/lightdm.conf.d

# 取消訪客登入
- name: disable guest login
  copy:
    dest: '{{ item.dest }}'
    owner: root
    group: root
    mode: '{{ item.mode | default("0644") }}'
    content: '{{ item.content }}'

  with_items:
    - dest: /usr/share/lightdm/lightdm.conf.d/50-no-guest.conf
      mode: '0400'
      content: |
        # This file is managed by Ansible. DO NOT EDIT.

        [SeatDefaults]
        allow-guest=false

# 安裝一些我常用的軟體
- name: install desktop apps
  apt:
    name: '{{ item.name }}'
    state: '{{ item.state | default("present") }}'
    purge: yes
    update_cache: yes

  with_items:
    - name: ibus-chewing
    - name: keepassx
    - name: libappindicator1 # For Dropbox
    - name: python-gpgme # For Dropbox
    - name: nautilus-dropbox
    - name: workrave

# 安裝 Google Chrome
- name: install Google Chrome
  apt:
    deb: 'https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb'
    update_cache: yes

最後則是安裝 Anaconda 的部份,我會先安裝 Anaconda 2:

---

- name: download Anaconda
  get_url:
    url: 'https://repo.continuum.io/archive/Anaconda2-4.3.1-Linux-x86_64.sh'
    dest: /tmp/Anaconda2-4.3.1-Linux-x86_64.sh
    mode: '0755'

- name: install Anaconda
  command: bash /tmp/Anaconda2-4.3.1-Linux-x86_64.sh -b -p {{ ansible_env.HOME }}/anaconda2
  args:
    creates: '{{ ansible_env.HOME}}/anaconda2'

- name: add Anaconda to PATH
  lineinfile:
    name: '{{ ansible_env.HOME }}/.bashrc'
    line: 'export PATH=$HOME/anaconda2/bin:${PATH}'

然後再進到目的機器額外安裝 Python 3 的環境:

conda create -n py3 python=3.6 anaconda

不過還不確定以上這件事怎麼寫成 Ansible playbook 比較好。

實際執行

假設新安裝的電腦是 host1 的話,那我就執行以下指令來安裝設定:

./deploy host1 desktop.yml

當然也可以直接登入該電腦,然後把 host1 改成 localhost 直接在本地執行。

yadm

至於 yadm 使用起來就非常簡單了。基本上就是一個可以把家目錄直接當成 git repository 的簡單套件。首先安裝:

sudo apt install yadm

然後剛開始可以直接:

yadm init
yadm add ~/.vimrc
yadm add ~/.tmux.conf
# ...
yadm commit -m 'first commit!'

如果要同步已經有的 repository 則是:

yadm clone <url>
# ...
yadm pull

跟正常的 git 一模一樣。

其他比較有用的功能大概是 Boostrap,可以取代我原本用 Makefile 手動打指令的部份。

結語

本次設定檔放在 shaform/experiments/ansible。其他還可以參考: