Ansible是一个基于Python的开源部署工具,依赖ssh实现模块化部署管理。兼容windows和linux平台。推送Playbook进行远程节点快速部署,易于上手,适合中小规模快速部署。

Ansible与Chef,Saltstack不同。Chef是Ruby语言编写,C/S架构,配置需要依赖GIT,Recipy脚本编写规范需要一定的编程经验。

Saltstack也是Python语言编写的CS架构,模块化配置管理,YAML脚本编写规范,适合大规模集群部署。他的优点是内部含有一个异步服务器,可以为客户端加快文件服务速度。

Ansible轻量级无客户端,有客户端就会要求服务器提供链接端口,无形中就会占用内存和CPU,而且如果这台机器安装了java服务,那么提供的这个端口就可能存在安全隐患。

Ansible开源免费,学习成本低,可以快速上手。他讲自己的YAML脚本命名为Playbook
,这样就给大家定义了一个统一的编写框架, 使我们可以遵循官方的说明与语法文档对我们的产品逻辑进行编写。

Ansible晚上的模块化扩展功能,支持目前主流的开发场景。具有强大的稳定性和兼容性,得益于Linux默认的Python和ssh。

Ansible有着活跃的社区。

Ansible的安装

在CentOS7系统中安装。因为Ansible是Python开发的,所以需要先安装Python。我们首先要保证这里安装的Python只会提供给Ansible使用,而不会给其他工具使用,这样保证安全问题。

这里推荐使用pythonenv去隔离python3.6环境,可以单独划分一个python给Ansible2.5使用。

首先创建Linux虚拟机,CentOS Ansible。ssh登录之后开始安装。

首先配置系统,需要关闭CentOS7系统下的放火墙。

systemctl stop firewalld
# 禁止开机启动
systemctl disable firewalld

关闭强制访问安全策略。

vi /etc/sysconfig/selinux

SELINUX=disabled # enforcing -> disabled
# 重启
reboot

稍等片刻重新链接主机

# 查看是否禁用成功
getenforce

下载并安装python。需要保证系统已经安装了gcc和zlib,没有的话先安装。

# 安装gcc
yum install -y gcc
# 安装zlib
yum -y install zlib*

wget http://www.python.org/ftp/python/3.6.5/Python-3.6.5.tar.xz

tar xf Python-3.6.5.tar.xz

cd Python-3.6.5

./configure --prefix=/usr/local/ --with-ensurepip=install --enable-shared LDFLAGS="-Wl,-rpath /usr/local/lib"

make && make altinstall

查看pip3.6路径

which pip3.6
# 设置软连接
ln -s /usr/local/bin/pip3.6 /usr/local/bin/pip

安装virtualenv工具, 使用国内镜像。

pip install virtualenv -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com

创建Ansible系统账户,并且安装Py3.6版本的virtualenv实例。

useradd deploy

su - deploy

virtualenv -p /usr/local/bin/python3.6 .py3-a2.5-env

git源码安装ansible2.5版本。

cd /home/deploy/.py3-a2.5-env
# 查看git,如果没有安装git需要安装git su - root    yum -y install git nss curl
which git
# git安装之后切换回deploy
su - deploy

安装ansible, 首先clone源代码到本地

git clone https://github.com/ansible/ansible.git
# 加载py3.6环境
source /home/deploy/.py3-a2.5-env/bin/activate
# 安装
pip install paramiko PyYAML jinja2

将下载的源代码移动到虚拟环境下。

mv ansible .py3-a2.5-env/

cd .py3-a2.5-env/ansible/

git checkout stable-2.5

source /home/deploy/.py3-a2.5-env/ansible/hacking/env-setup -q

验证是否加载安装完成

ansible --version

Playbooks入门和编写规范

Playbooks是ansiable的编排语言框架,它本身的简单易读的语法结构以及丰富的内建模块非常方便我们编写远程部署策略。

Playbooks基础的文件格式为yml文件格式。inventory存放server详细清单目录,roles保存要部署的详细任务列表。testbox表示详细任务, main住任务文件。deploy任务入口文件。

inventory/
    testenv
roles/
    testbox/
        tasks/
            main.yml
deploy.yml

testenv由上下两部分组成,testservers表示server组列表,也就是要部署的服务器的列表,值为目标部署服务器的主机名。testservers表示目标主机使用的参数列表。

[testservers]
test.example.com

[testservers:vars]
server_name=test.example.com
user=root
output=/root/test.txt

上面的意思是给test.example.com这个主机定义了server_name, user以及output参数。

main.yml住任务文件用来保存特性roles下面具体执行的任务。这里会保存一个或多个task作为任务,task一般由两部分组成,任务名称和执行的脚本,脚本通常调用内建模块编排执行逻辑。

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

这里打印的user,server_name和output就是前面testenv定义的值。

入口文件deploy.yml,他直接和ansible对话。这里的host对应testenv中的testservers。gather_facts获取Server基本信息,remote_user指定目标服务器系统用户,roles是进入roles/testbox任务目录进行执行。

- hosts: "testservers"
  gather_facts: true
  remote_user: root
  roles:
    - testbox

因为ansible是使用ssh作为通信协议,为了保证正常的通信,需要配置ansible主机与目标主机的秘钥认证,保证无需密码即可访问目标主机。

Ansible服务端创建ssh本地秘钥

ssh-keygen -t rsa

Ansible服务器端建立与目标部署机器的秘钥认证。将ansible服务器的公钥传输到目标服务器中,可以实现直接连接。

ssh-copy-id -i /home/deploy/.ssh/id_rsa.pub root@test.example.com

部署到testenv环境

ansible-playbook -i inventory/testenv ./deploy.yml

实时演示一下。 我们需要创建一个testbox虚拟机主机。同样安装CentOS7。我们尝试通过ansible主机将文件远程传输至testbox虚拟主机。

首先ssh登录到ansible主机上,然后切换到ansible2.5版本。

su - deploy 
source .py3-a2.5-env/bin/activate
source .py3-a2.5-env/ansible/hacking/env-setup -q
ansible-playbook --version

编写playbooks框架,

mkdir test_playbooks
cd test_playbooks
mkdir inventory
mkdir roles
cd inventory/
vi testenv

编辑testenv

[testservers]
test.example.com

[testservers:vars]
server_name=test.example.com
user=root
output=/root/test.txt

创建main.yml

cd ..
cd roles/
mkdir testbox
cd testbox
mkdir tasks
cd tesks/
vi main.yml

编辑main.yml, 添加一个测试任务,这里shell前是两个空格。

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

返回路径,回到test_palybooks

cd ../../..

创建deploy.yml文件

vi deploy.yml

编辑这个文件, 告诉ansible使用host任务,并且在目标主机中获取主机信息,并且使用目标主机的root账户读写权限。最后告诉ansible执行roles下的testbox任务。

- hosts: "testservers"
  gather_facts: true
  remote_user: root
  roles:
    - testbox

使用tree查看一下目录结构

tree .

这样文件就配置好了,我们接下来要配置一下秘钥认证。

# 返回root命令行
su - root
# 编辑dns
vi /etc/hosts

添加dns文件。

xx.xx.xx.xx test.example.com

返回到deploy命令行

exit

创建ssh秘钥认证对, 一路回车。

ssh-keygen -t rsa
# cat ~/.ssh/id_*.pub | ssh  root@test.example.com 'cat >> .ssh/authorized_keys'
ssh-copy-id-i ~/.ssh/id_rsa.pub root@test.example.com

这样就可以不需要密码直接连接到test.example.com服务器了,这里在ansible的deploy目录中。

执行入口文件。

cd test_playbooks/

ansible-playbook -i inventory/testenv ./deploy.yml

执行完成。可以去目标主机查看状态。创建了一个test.txt文件并且将数据写到了文件中。

ssh root@test.example.com
ls
cat test.txt

常用模块

Ansible的模块是由Ansible对特定部署操作脚本打包封装后的成品,可以利用该模块成品直接利用编写PlayBooks,这样就大大简化了我们日常工作中对部署脚本的编写逻辑,从而方便后期去维护管理。

  1. File模块

用来在目标主机上创建文件或目录,并对其赋予响应的系统权限。file调用的是file模块,这里定义在目标主机上创建一个root/foo.txt文件。state表示要创建文件,mode表示文件0755权限。

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

实现Ansible服务端到目标主机的文件传送。

- name: copy a file
  copy: 'remote_src=no src=reles/testbox/files/foo.sh' dest=/root/foo.sh mode=0644 force=yes
  1. State模块

获取远程文件状态信息,并将这个信息保存在环境变量下,用于使用。保存到script_stat变量中。

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

用来在Ansibles输出下打印数据

- debug: msg=foo.sh exists
  when: script_stat.stat.exists
  1. Command/Shell模块

用来执行Linux目标主机命令行,Shell会调用/bin/bash, 可以使用系统变量。Command不可以, 推荐使用shell。

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

- name: run the script
  shell: "echo 'test' > /root/test.txt"
  1. Template

实现Ansible服务端到目标主机的jinja2模板传统,会利用这个功能编写app配置文件,例如nginx配置文件。

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

调用目标主机系统包管理工具(yum, apt),根据定义的安装包的名称进行配置安装。

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

- name: ensure nginx is at the latest version
  apt: pkg=nginx state=latest
  1. Service模块

管理目标主机的系统服务

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

利用上面的介绍编写一个完整的ansible playbooks脚本文件。

- 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=reles/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

命令行连接至ansible服务器当中,连接至deploy用户中。启动py3.6的虚拟环境。加载ansible2.5版本。

su -deploy

source .py3-a2.5-env/bin/activate

source .py3-a2.5-env/ansible/hacking/env-setup -q

ansible-playbook --version

首先需要进入到目标主机进行基本的模块配置,保证随后的任务可以正常运行。

创建两个用户foo和deploy,创建一个nginx目录。

useradd foo
useradd deploy
mkdir /etc/nginx

安装nginx yum源,保证可以安装。

rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm

在ansible主机中,进入到test_playbooks文件夹。编辑roles/testbox/tasks/main.yml这个文件

vi roles/testbox/tasks/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'

执行这个任务, 测试刚刚编写的任务。

ansible-playbook -i inventory/testenv ./deploy.yml

执行之后可以在目标服务器找到这个文件。接着我们演示file

mkdir roles/testbox/files/
vi roles/testbox/files/foo.sh

添加内容

echo "this is a test script"
vi roles/testbox/tasks/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=reles/testbox/files/foo.sh' dest=/root/foo.sh mode=0644 force=yes

执行这个任务, 测试刚刚编写的任务。

ansible-playbook -i inventory/testenv ./deploy.yml

接着演示exists

- 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=reles/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
ansible-playbook -i inventory/testenv ./deploy.yml

接着我们添加一个模块任务,在添加模块任务之前,需要添加几个参数到testenv这个文件中

vi inventory/testenv
[testservers]
test.example.com

[testservers: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

接着需要在roles下面的testbox中添加templates目录。

mkdir roles/testbox/templates

vi roles/testbox/templates/nginx.conf.j2
# For more information on configuration, see:
user    {{ user }};
worker_processes    {{ worker_processes }};

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

pid  /var/run/nginx.pid;

evnets {
  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;

  keepalive_timeout 65;

  server {
    listen  {{ port }} default_server;

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

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

    error_page    500 502 503 504 /50x.html;
    location = /50x.html {
      root    /usr/share/nginx/html
    }
  }
}
vi roles/testbox/tasks/main.yml

使用template将本地模板编译传送至远程,然后在使用yum安装nginx,使用service任务启动远程的nginx服务。

- 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=reles/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: 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

开始测试。

ansible-playbook -i inventory/testenv ./deploy.yml

使用远程查看命令。

ssh root@test.example.com ps -ef | grep nginx