Ansible 学习笔记

几年前,还是大二的我在学习 TDD 的时候打算把 Fabric 和 Ansible 一起学了,结果看到 Fabric 不支持 Python3,Ansible 对 Python3.5 的支持有缺陷,处于某种洁癖就止步不前。现在想想自己当时高一用的不也是 Python 2,怎么现在对 Python 2 有了莫名的抵触。现在 Ansible 对 Python3 的支持非常好,Fabric3也有了,而我只有对自己的却步感到无尽的自责。

编译安装 Python

  1. 下载安装包到本地后进行配置
    # ./configure --prefix=/usr/local \
                       --with-ensurepip=install \
                       --enable-shared \
                       DLFLAGS="-wl, -rpath /usr/local/lib"
    
  2. 进行编译安装并区分之前安装的版本
    # make && make altinstall
    
  3. 设置软连接
    # ln -sf /usr/local/bin/pip3.6 /usr/local/bin/pip
    
  4. Python37 安装时,还需要提供相应依赖
    # Before make
    $ yum install libffi-devel -y
    # After Make
    $ cp libpython3.7m.so.1.0  /usr/lib64
    

安装 Ansible

Ansible 的安装有两种方法:Yum 和 Git。这里推荐使用 Git,有利于版本切换。
  1. 克隆 Ansible 仓库到本地
    # git clone git://github.com/ansible/ansible.git
    
  2. 创建 Ansible 用户,使用 Virtualenv 创建对应的 Python 环境
    # useradd deploy && su deploy
    
  3. 安装 Ansible 的相关依赖
    $ pip install paramiko PyYAML Jinja2
    
  4. 切换 Ansible 版本为 2.5 (可选,Git安装的特性)
    $ git checkout stable-2.5
    
  5. 启动 Ansible (重启系统后失效)
    $ source ansible/hacking/env-setup -q
    

Ansible 密钥认证

其实就是使用 SSH 的密钥认证登录方式,方便 Ansible 操作目标主机。
# su deploy
# ssh-keygen -t rsa
# ssh-copy-id -i /home/deploy/.ssh/id_rsa.pub root@target.host.address
ssh-copy-id 命令不可用,还需安装相关软件:
# yum -y install openssh-clients

Ansible 主机清单

Ansible 所有定义的主机与组规则可存放在当前目录下,也可以写在 /etc/Ansible/hosts 里。指定范例,供之后的 Playbooks 调用。
  • INI 格式范例:
    [host_cluster_A]
    host1.alias ansible_port=22 ansible_host=192.0.2.50
    host[02:50].example.com ansible_connection=ssh
    [host_cluster_B]
    ntp_server=ntp.atlanta.example.com
    proxy=proxy.atlanta.example.com
    
  • 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 :
    

Ansible 配置文件

在下列表中,从上往下依次检查,检查到哪个可用就用哪个,后面直接忽视。(官方配置文件示例),Ansible 的其他配置文件同理。
  1. ANSIBLE_CONFIG 环境变量,可定义配置文件的位置
  2. ./ansible.cfg 存在于当前工作目录
  3. ~/.ansible.cfg 存在于当前用户的 HOME 目录
  4. /etc/ansible/ansible.cfg 默认目录

Ansible Playbook 文件结构

Ansible Task
    ├─deploy.yml ( playbook 任务入口文件)
    ├─inventory ( Server 详细清单目录)
    |       └─taskenv ( 具体清单与变量声明文件 )
    └─roles ( 任务列表 )
        └─taskbox ( 详细目录 )
            ├─tasks
            |     └─main.yml ( 主任务文件 )
            └─templates ( 用于传递参数的模板文件 )

Ansible Playbook 常用模块

  1. File 模块:在目标主机上创建文件或目录,并赋予其系统权限(属性:path, state, mode, owner, group)
  2. Copy 模块:实现服务器端到目标主机的文件传送(属性:remote_src, src, dest, mode, force)
  3. Stat 模块:获取远程文件的状态信息(属性:path; 方法:register)
  4. Debug 模块:调试并将结果在 Ansible 上输出 (方法:debug, when)
  5. Command / Shell 模块:执行命令,推荐使用后者(属性:Command, Shell)
  6. Template 模块:实现服务器端到目标主机 Jinja2 模板传送(属性:src, dest)
  7. Packaging 模块:调用目标主机包管理工具进行安装(方法:yum, apt; 属性:pkg, state)
  8. Service 模块:管理目标主机系统服务(属性:name, state)

taskenv 文件编写用例(NginX)

[NginX]
test.example.com

[NginX:vars]
server_name=test.example.com
user=root
output=/root/test.txt
server_name=test.example.com
port=80
user=deploy
worker_processes=4
max_open_file=65505
root=/www

Jinja2 模板文件编写用例(NginX)

user              {{ user }};  
worker_processes  {{ worker_processes }};  

error_log  /var/log/nginx/error.log;  

pid        /var/run/nginx.pid;  

events {  
    worker_connections  {{ max_open_file }};  
}  


http {  
    include       /etc/nginx/mime.types;  
    default_type  application/octet-stream;  

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '  
                      '$status $body_bytes_sent "$http_referer" '  
                      '"$http_user_agent" "$http_x_forwarded_for"';  

    access_log  /var/log/nginx/access.log  main;  

    sendfile        on;  
    #tcp_nopush     on;  

    #keepalive_timeout  0;  
    keepalive_timeout  65;  

    gzip  on;  

    # Load config files from the /etc/nginx/conf.d directory  
    # The default server is in conf.d/default.conf  
    #include /etc/nginx/conf.d/*.conf;  
    server {  
        listen       {{ port }} default_server;  
        server_name  {{ server_name }};  

        charset utf-8;  

        #access_log  logs/host.access.log  main;  

        location / {  
            root   {{ root }};  
            index  index.html index.htm;  
        }  

        error_page  404              /404.html;  
        location = /404.html {  
            root   /usr/share/nginx/html;  
        }  

        # redirect server error pages to the static page /50x.html  
        #  
        error_page   500 502 503 504  /50x.html;  
        location = /50x.html {  
            root   /usr/share/nginx/html;  
        }  

    }  

}

Main.yml 编写用例

简单用法

- name: Print server name and user to remote testbox
  shell: "echo 'Currently {{ user }} is logining {{ server_name }}' > {{ output }}"

- name: create a file
  file: 'path=/root/foo.txt state=touch mode=0755 owner=foo group=foo'

- name: copy a file
  copy: 'remote_src=no src=roles/testbox/files/foo.sh dest=/root/foo.sh mode=0644 force=yes'

- name: check if foo.sh exists
  stat: 'path=/root/foo.sh'
  register: script_stat

- debug: msg="foo.sh exists"
  when: script_stat.stat.exists

- name: run the script
  command: 'sh /root/foo.sh'

- name: write the nginx config file
  template: src=roles/testbox/templates/nginx.conf.j2 dest=/etc/nginx/nginx.conf

- name: ensure nginx is at the latest version
  yum: pkg=nginx state=latest

- name: start nginx service
  service: name=nginx state=started

配置安装 NginX

- name: Disable system firewall
  service: name=firewalld state=stopped

- name: Disable SELINUX
  selinux: state=disabled

- name: setup nginx yum source
  yum: pkg=epel-release state=latest

- name: write then nginx config file
  template: src=roles/nginx/templates/nginx.conf.j2 dest=/etc/nginx/nginx.conf

- name: create nginx root folder
  file: 'path={{ root }} state=directory owner={{ user }} group={{ user }} mode=0755'

- name: copy index.html to remote
  copy: 'remote_src=no src=roles/nginx/files/index.html dest=/www/index.html mode=0755'

- name: restart nginx service
  service: name=nginx state=restarted

- name: run the health check locally
  shell: "sh roles/nginx/files/health_check.sh {{ server_name }}"
  delegate_to: localhost
  register: health_status

- debug: msg="{{ health_status.stdout }}"

部署 WordPress

- name: Update yum dependency
  shell: 'yum update -y warn=False'

- name: Disable system firewall
  service: name=firewalld state=stopped

- name: Disable SELINX
  selinux: state=disabled

- name: Setup epel yum source for nginx and mariadb(mysql)
  yum: pkg=epel-release state=latest

- name: Setup webtatic yum source for php-fpm
  yum: name=https://mirror.webtatic.com/yum/el7/webtatic-release.rpm

- name: Ensure nginx is at the latest version
  yum: pkg=nginx state=latest

- name: Write the nginx config file
  template: src=roles/wordpress/templates/nginx.conf.j2 dest=/etc/nginx/nginx.conf

- name: Create nginx root folder
  file: 'path={{ root }} state=directory owner={{ user }} group={{ user }} mode=0755'

- name: Copy info.php to remote
  copy: 'remote_src=no src=roles/wordpress/files/info.php dest=/data/www/info.php mode=0755'

- name: Restart nginx service
  service: name=nginx state=restarted

- name: Setup php-fpm
  command: 'yum install -y php70w php70w-fpm php70w-common php70w-mysql php70w-gd php70w-xml php70w-mbstring php70w-mcrypt warn=False'

- name: Restart php-fpm service
  service: name=php-fpm state=restarted

- name: Copy php-fpm config file to remote
  copy: 'remote_src=no src=roles/wordpress/files/www.conf dest=/etc/php-fpm.d/www.conf mode=0755 owner={{ user }} group={{ user }} force=yes'

- name: Restart php-fpm service
  service: name=php-fpm state=restarted

- name: Run the health check locally
  shell: "sh roles/wordpress/files/health_check.sh {{ server_name }} {{ port }}"
  delegate_to: localhost
  register: health_status

- debug: msg="{{ health_status.stdout }}"

- name: Setup mariadb(mysql)
  command: "yum install -y mariadb mariadb-server warn=False"

- name: Backup current www folder
  shell: 'mv {{ root }} {{ backup_to }}'

- name: Close git ssl verification
  shell: 'git config --global http.sslVerify false'

- name: Clone WordPress repo to remote
  git: "repo=https://{{ gitlab_user | urlencode }}:{{ gitlab_pass | urlencode }}@gitlab.example.com/root/Wordpress-project.git dest=/data/www version={{ branch }}"
  when: project == 'wordpress'

- name: Change www folder permission
  file: "path=/data/www mode=0755 owner={{ user }} group={{ user }}"

参考资料

评论