分发脚本 ansible

虽然也可以自己写 Shell 实现分发脚本,但由于缺乏运维系统概念,混乱、零碎的写法总是让人难受,一接触到 ansible 就知道很难回去了。

Ansible 是一个用来批量管理部署远程主机上服务的工具,它通过 SSH 协议实现管理节点与远程节点之间的通信,所以首先需要完成 SSH 配置

需要注意的是 Ansible 需要开启公钥认证,也就是添加到 known_hosts 中,对于大批量的操作可以禁用该行为:

vim /etc/ansible/ansible.cfg
# 修改以下内容
host_key_checking = False # 取消注释

install

虽然可以很简单的直接使用 yum 安装open in new window,但由于 ansible 是一个 python 编写的工具,这样就会导致 ansible 依赖于全局的 python,当全局依赖发生变化的时候可能导致 ansible 出现冲突或潜在的安全问题,所以需要隔离 ansible 与其他应用。

1. 关闭限制

systemctl stop firewalld
systemctl disable firewalld
systemctl status firewalld # 验证

vim /etc/sysconfig/selinux
# 修改以下内容
SELINUX=disabled

reboot
getenforce # 验证 Disabled

2. 安装 pythonopen in new window

wget https://www.python.org/ftp/python/3.6.9/Python-3.6.9.tar.xz
tar xf Python-3.6.9.tar.xz
cd Python-3.6.9
./configure --prefix=/usr/local --with-ensurepip=install --enable-shared LDFLAGS="-Wl,-rpath /usr/local/lib"
make && make altinstall
which pip3.6
ln -s /usr/local/bin/pip3.6 /usr/local/bin/pip # 建立软连接
pip install virtualenv

3. 创建实例

需要创建一个 ansible 用户,并在该系统用户下创建一个 python3.6 版本的 virtualenv 实例。

useradd deploy
su - deploy
virtualenv -p /usr/local/bin/python3.6 .py3-a2.8-env # 创建实例
source /home/deploy/.py3-a2.8-env/bin/activate
pip install paramiko PyYAML  jinja2 # 安装依赖,保持顺序

4. 安装 ansible

官网open in new window推荐的安装方式,当下载速度极慢时的解决方案

git clone https://gitee.com/shanyuhai123/ansible.git
mv ansible .py3-a2.8-env/
cd .py3-a2.8-env/ansible
git branch -a # 查看分支
git checkout stable-2.8
source /home/deploy/.py3-a2.8-env/ansible/hacking/env-setup -q
ansible --version # 验证

5. 启用 ansible

shutdown -r now # 重启

su - deploy
source .py3-a2.8-env/bin/activate # 加载 py3
source .py3-a2.8-env/ansible/hacking/env-setup -q # 加载 ansible
ansible --version # 验证

inventory

Ansible 可同事操作属于一个组的多台主机,组和主机之间的关系通过 inventoryopen in new window 文件配置。默认的文件路径为 /etc/ansible/hosts,除默认文件外,还可以使用多个 inventory 文件,当然也可以从云上拉取配置信息。

1. INI-like

mail.example.com

[t1]
192.168.1.11
192.168.1.12

[web]
www[01:50].example.com

[db]
db-[a:f].example.com

[webservers]
foo.example.com
bar.example.com

[dbservers]
one.example.com
two.example.com
three.example.com:5309

2. YAML

all:
  hosts:
    mail.example.com:
  children:
    webservers:
      hosts:
        foo.example.com:
        bar.example.com:
    dbservers:
      hosts:
        one.example.com:
        two.example.com:
        three.example.com:

3. Inheriting variable values

在这种情况下自己书写规则 INI-like 风格更方便一些,但是接手他人的时候更希望看到 YAML 风格,更一目了然。

  • INI-like

    [atlanta]
    host1
    host2
    
    [raleigh]
    host2
    host3
    
    [southeast:children]
    atlanta
    raleigh
    
    [southeast:vars]
    some_server=foo.southeast.example.com
    halon_system_timeout=30
    self_destruct_countdown=60
    escape_pods=2
    
    [usa:children]
    southeast
    northeast
    southwest
    northwest
    
  • YAML

    all:
      children:
        usa:
          children:
            southeast:
              children:
                atlanta:
                  hosts:
                    host1:
                    host2:
                raleigh:
                  hosts:
                    host2:
                    host3:
              vars:
                some_server: foo.southeast.example.com
                halon_system_timeout: 30
                self_destruct_countdown: 60
                escape_pods: 2
            northeast:
            northwest:
            southwest:
    

4. 参数

参数说明
ansible_connection连接类型到主机。
ansible_host要连接的主机的名称。
ansible_port连接端口号。
ansible_user连接到主机时要使用的用户名。
ansible_password身份验证的密码。
ansible_ssh_private_key_filessh使用的私钥文件。
ansible_ssh_common_args此设置始终附加到sftp,scp和ssh的默认命令行。
ansible_sftp_extra_args此设置始终附加到默认的sftp命令行。
ansible_scp_extra_args此设置始终附加到默认scp命令行。
ansible_ssh_extra_args此设置始终附加到默认的ssh命令行。
ansible_ssh_pipelining确定是否使用SSH流水线。
ansible_ssh_executable此设置将覆盖使用系统ssh的默认行为。
ansible_shell_type目标系统的shell类型。
ansible_python_interpreter目标主机python路径。

ad-hoc

ad-hoc 可以帮助完成临时任务,很多时候并不需要 Ansible 的强大力量(playbook)。

命令格式:

ansible <pattern_goes_here> -m <module_name> -a <arguments>

仅列出部分示例。

1. commandopen in new window

Execute commands on targets。

实践:

ansible t1 -m command -a "free -m"
ansible t1 -m command -a "df -h"
ansible t1 -m command -a "ls /root"
ansible t1 -m command -a "cat redhat-release"

2. shellopen in new window

Execute shell commands on targets。

实践:

ansible t1 -m shell -a "echo hello > /tmp/tmp.txt"
ansible t1 -m shell -a "cat /tmp/tmp.txt" # 验证

3. copyopen in new window

Copy files to remote locations。

实践:

ansible t1 -m copy -a "src=/root/shell dest=/tmp"
ansible t1 -m shell -a "ls /tmp" # 验证
ansible t1 -m shell -a "wc -l /tmp/shell" # 验证

4. scriptopen in new window

Runs a local script on a remote node after transferring it。

实践:

echo "echo hello" > /tmp/hello.sh
cat /tmp/hello.sh # 验证
ansible t1 -m script -a "/tmp/hello.sh" 

5. fileopen in new window

Manage files and file properties。

6. yumopen in new window

Manages packages with the yum package manager。

实践:

ansible webservers -m command -a "free -m" # 验证

ansible webservers -m yum -a "name=nginx state=installed"
ansible webservers -m command -a "nginx -v" # 验证 nginx version: nginx/1.12.2
# 可以注意到并非是最新版 nginx
# 参照示例 https://docs.shanyuhai.top/os/centos/install-the-latest-version-of-nginx.html
ansible webservers -m yum -a "name=yum-utils state=installed"
ansible webservers -m command -a "ll /etc/yum.repos.d/nginx.repo" # 验证
vim /etc/yum.repos.d/nginx.repo
cat /etc/yum.repos.d/nginx.repo # 验证
# 追加内容参考示例
ansible webservers -m copy -a "src=/etc/yum.repos.d/nginx.repo dest=/etc/yum.repos.d/nginx.repo"
ansible webservers -m command -a "cat /etc/yum.repos.d/nginx.repo" # 验证
ansible webservers -m command -a "yum-config-manager --enable nginx-mainline"
ansible webservers -m yum -a "name=nginx state=installed" # 更新
# installed 测试时未能更新,可能需要 present 参数
# 不推荐使用 yum 卸载
ansible webservers -m command -a "nginx -v" # 验证 

7. systemdopen in new window

Manage services。

实践:

# 最好将服务写完整
ansible t1 -m systemd -a "name=crond.service  enabled=yes state=started"
ansible t1 -m command -a "systemctl status crond" # 验证

8. cronopen in new window

Manage cron.d and crontab entries。

实践:

ansible webservers -m cron -a "name='sync time' minute=00 hour=00 job='/usr/sbin/ntpdate time.nist.gov > /dev/null 2>&1'" 
ansible webservers -m command -a "crontab -l" # 验证

# 删除任务
ansible webservers -m cron -a "name='sync time' state=absent" 
ansible webservers -m command -a "crontab -l" # 验证

playbooks

相对于 ad-hoc,playbooks 则强大的多,当然也复杂得多。playbook 由一个或多个 plays 组成,在 play 中,一组机器被映射为定义好的角色,其内容被称为 tasks(任务),在基本层次的应用中,一个任务是一个对 ansible 模块的调用。

对于如何良好的实践这些内容,有一些整套的 playbooksopen in new window

1. one play

---
- hosts: webservers
  vars:
    http_port: 80
    max_clients: 200
  remote_user: root
  tasks:
  - name: ensure apache is at the latest version
    yum:
      name: httpd
      state: latest
  - name: write the apache config file
    template:
      src: /srv/httpd.j2
      dest: /etc/httpd.conf
    notify:
    - restart apache
  - name: ensure apache is running
    service:
      name: httpd
      state: started
  handlers:
    - name: restart apache
      service:
        name: httpd
        state: restarted

2. multiple plays

---
- hosts: webservers
  remote_user: root

  tasks:
  - name: ensure apache is at the latest version
    yum:
      name: httpd
      state: latest
  - name: write the apache config file
    template:
      src: /srv/httpd.j2
      dest: /etc/httpd.conf

- hosts: databases
  remote_user: root

  tasks:
  - name: ensure postgresql is at the latest version
    yum:
      name: postgresql
      state: latest
  - name: ensure that postgresql is started
    service:
      name: postgresql
      state: started

3. Directory Layout

production                # inventory file for production servers
staging                   # inventory file for staging environment

group_vars/
   group1.yml             # here we assign variables to particular groups
   group2.yml
host_vars/
   hostname1.yml          # here we assign variables to particular systems
   hostname2.yml

library/                  # if any custom modules, put them here (optional)
module_utils/             # if any custom module_utils to support modules, put them here (optional)
filter_plugins/           # if any custom filter plugins, put them here (optional)

site.yml                  # master playbook
webservers.yml            # playbook for webserver tier
dbservers.yml             # playbook for dbserver tier

roles/
    common/               # this hierarchy represents a "role"
        tasks/            #
            main.yml      #  <-- tasks file can include smaller files if warranted
        handlers/         #
            main.yml      #  <-- handlers file
        templates/        #  <-- files for use with the template resource
            ntp.conf.j2   #  <------- templates end in .j2
        files/            #
            bar.txt       #  <-- files for use with the copy resource
            foo.sh        #  <-- script files for use with the script resource
        vars/             #
            main.yml      #  <-- variables associated with this role
        defaults/         #
            main.yml      #  <-- default lower priority variables for this role
        meta/             #
            main.yml      #  <-- role dependencies
        library/          # roles can also include custom modules
        module_utils/     # roles can also include custom module_utils
        lookup_plugins/   # or other types of plugins, like lookup in this case

    webtier/              # same kind of structure as "common" was above, done for the webtier role
    monitoring/           # ""
    fooapp/               # ""

备用方案:

inventories/
   production/
      hosts               # inventory file for production servers
      group_vars/
         group1.yml       # here we assign variables to particular groups
         group2.yml
      host_vars/
         hostname1.yml    # here we assign variables to particular systems
         hostname2.yml

   staging/
      hosts               # inventory file for staging environment
      group_vars/
         group1.yml       # here we assign variables to particular groups
         group2.yml
      host_vars/
         stagehost1.yml   # here we assign variables to particular systems
         stagehost2.yml

library/
module_utils/
filter_plugins/

site.yml
webservers.yml
dbservers.yml

roles/
    common/
    webtier/
    monitoring/
    fooapp/