我们已经知道,基于集群可实现服务器横向扩展slace out。可是,当上百台主机集合成共同为一个站点提供web页面访问的形式时,怎么管理整个集群体呢?
一些小伙伴大概听说过pxe等可实现自动安装系统。可是,要我们一台一台主机的开机,一个个的载入引导文件,当这个主机基数足够庞大时,那也着实不是一件轻松的事情。这样的工作做上个个把月,也就不用在运维界混了。
诶,不要怂。阿拉今天这不就要介绍这么一个可以帮我们实现批量化主机命令管理的工具了嘛!
试想一下,ansible用在年底扩容促销时简直无疑与一件大杀器。

但是,这样个玩意,也是有使用前提的。拿尼,只是运维界的业内规范而已————标准化、自动化、架构优化、过程优化。
哈哈,理想状态下的运维部门果然应该是如下的形式吧!

理想状态下的运维部门:
1、只有少数类型的硬件,便于管理
2、系统版本统一
3、目录结构规范
4、运维工程师水平层次高
5、无历史包袱
6、同一软件只有单一版本
7、同一类操作可自动化批量执行
8、工作效率高,无大量重复机械化操作
9、开发自己上线,运维比较轻松

然而,现实往往是只会让人吐血。不过还是暂行镇静,身体是自己的,且行且珍惜。所以,遇到下面的情形时,一定要平静心绪,摒弃兴奋,告诉自己憋激动。挑战即机遇,不过咱也得先做好不是!

目前多数公司遇到的运维问题现状:
1、硬件选型多样化
2、系统多版本并存
3、目录结构混乱
4、运维人员水平参差不齐
5、历史遗留问题多
6、同一软件出现多个版本
7、无法批量化操作
8、工作效率低,故障率高
9、项目上线操作繁琐

至于大施拳脚的方式,那就仁者见仁智者见智了。各位都是运维界的精英,到时候说不定阿拉还有求到各位的地方,就希望大佬们到时候不吝赐教啦!
说起来以上也差不多就是咱运维的工作内容了,嗯,列举出来就是:

日常运维工作中的重要事件:
1、添加监控,收集系统指标
2、对日志进行归档以及管理
3、数据备份于恢复
4、对计划任务进行管理
5、对软件包进行部署和管理
6、对脚本进行批量执行
7、对文件进行批量复制和移动
8、设置文件或者目录对应的权限
9、关闭和启动服务
10、对代码程序进行上下线

企业里,在日常工作中不断优化系统架构和部署的合理性是一项长期而持续的工作。嗯,与诸君共勉!

生活真是不易又刺激啊!嘛,阿拉现在要说的,是基于标准化之后的系统环境之后的部分。哈哈。

ansible简介

安装

这个简单。配置好epel源的话直接yum install ansible就哦了。
另外ansible是python编写的。如果没pip,需先安装pip.yum可直接安装:yum install python-pip。

操作方式

Ansible系统由控制主机对被管节点的操作方式可分为两类,即ad-hoc和playbook。简单来说,ad-hoc是命令行管理,实现功能单一。playbook类似脚本,可通过ad-hoc命令堆砌的形式实现整体的功能,嘛,当成脚本理解是没错的。要说不同,就是这个命令行和脚本的语法有点怪了。

主配置文件

[root@cet7 ansible]# ls /etc/ansible -l
-rw-r--r-- 1 root root 14420 Dec 4 14:30 ansible.cfg
-rw-r--r-- 1 root root 1057 Dec 4 11:57 hosts
drwxr-xr-x 2 root root 6 Jan 17 2017 roles

  • [ ] 主配置文件里的日志开启下就可以直接使用了。
  • [ ] hoste文件定义主机,即ansible服务器所辖的节点。可按其功能定义不同的组,通过组来发出命令。
  • [ ] roles文件夹以结构化的形式定义出ansible执行命令所需的各因素。
  • [ ] 可以直接在此目录下定义.yml的独立playbook文件。也可与roles下定义的变量等相关联。

配置的详细信息下面还有专门的介绍。

命令执行过程

1、加载自己的配置文件 默认/etc/ansible/ansible.cfg
2、查找对应的主机配置文件,如hosts,找到要执行的主机或者组
3、加载自己对应的模块文件,如command
4、通过ansible将模块或命令生成对应的临时py文件(python格式),并将该文件传输至远程服务器
5、对应执行用户的家目录下的.ansible/tmp/XXX/XXX.PY文件
6、给文件+x执行
7、执行并返回结果
8、删除临时py文件,sleep 0退出

Ansible配置

主配置文件ansible.cfg

ansible有许多参数,下面列出常用的参数:

1.inventory

这个参数表示资源清单inventory文件的位置,资源清单就是一些Ansible需要连接管理的主机列表。这个参数的配置实例如下:
inventory = /etc/ansible/hosts

2.library

Ansible的操作动作,无论是本地或远程,都使用一小段代码来执行,这小段代码称为模块,这个library参数就是指向存放Ansible模块的目录。配置实例如下:
library = /usr/share/ansible
Ansible支持多个目录方式,只要用冒号(:)隔开就可以,同时也会检查当前执行playbook位置下的./library目录。

3.forks

设置默认情况下Ansible最多能有多少个进程同时工作, 从Ansible 1.3开始,fork数量默认
自动设置为主机数量或者潜在的主机数量,默认设置最多5个进程并行处理。具体需要设置多少个,可以根据控制主机的性能和被管节点的数量来确定,可能是50或100。默认值5是非常保守的值,配置实例如下:
forks = 5

4.sudo_user

这是设置默认执行命令的用户,也可以在playbook中重新设置这个参数。配置实例如下:
sudo_user = root

5.remote_port

这是指定连接被管节点的管理端口,默认是22。除非设置了特殊的SSH端口,不然这个参数一般是 不需要修改的。配置实例如下:
remote_port = 22

6.host_key_checking

这是设置是否检查SSH主机的密钥。可以设置为True或False,关闭后第一次连接没有提示。配置实例:
host_key_checking = False

7.timeout

这是设置SSH连接的超时间隔,单位是秒。配置实例如下:
timeout = 60

8.log_path

Ansible系统默认是不记录日志的,如果想把Ansible系统的输出记录到日志文件中,需要设置log_path来指定一个存储Ansible日志的文件。配置实例如下:
log_path = /var/log/ansible.log
另外需要注意,执行Ansible的用户需要有写入日志的权限,模块将会调用被管节点的syslog来记录

主机清单设置文件hosts

/etc/ansible/hosts定义方式:

1、直接指明主机地址或主机名:

green.example.com
blue.example.com
192.168.100.1
192.168.100.10

2、定义一个主机组[组名]把地址或主机名加进去

[web]
node1.magedu.com
node2.magedu.com
#组成员可以使用通配符来匹配,如172.17.7.[101:1066]表示匹配从172.17.30.101——172.17.30.106的主机

Ansible命令集

1.ansible: Ansibe AD-Hoc临时命令执行工具,常用于临时命令的执行

命令格式:
ansible [-f forks] [-m module_name] [-a args]
-a 跟模块的参数,如果执行默认COMMAND的模块,即是命令参数
-C 只是测试一下会改变什么内容,不会真正去执行
-f 并行任务数。需指定为一个整数,默认是5
-i 指定库存主机文件的路径,默认为/etc/ansible/hosts
-m 指定执行模块的名字,默认使用 command 模块
-S su,用 su 命令
-R 指定SU的用户,默认是root用户
-s sudo
-U sudo到哪个用户,默认为 root
-T 指定SSH默认超时时间,默认是10S
-t 将日志内容保存在该输出目录,结果保存在一个文件中在每台主机上
-u 远程用户,默认是root用户
-v,-vv,-vvv 输出ansible的版本及详细信息

2.ansible-doc: Ansible模块功能查看工具

-l: 获取模块信息
-s: MOD_NAME 获取指定模块的使用帮助
-v: 显示ansible-doc的版本号查看模块列表

3.ansible-galaxy: 下载/上传优秀代码或Roles模块的官网平台,基于网络的
4.ansible-playbook: Ansible定制自动化的任务集编排工具
5.ansible-pull: Ansible远程执行命令的工具,拉取配置而非推送配置(使用较少,海量机器时使用,对运维的架构能力要求较高)
6.ansible-vault: Ansible 文件加密工具
7.ansible-console: Ansible基于Linux Consoble界面可与用户交互的命令执行工具

Ansible使用

Ansible配置公私钥

虽然ansible支持其他主机认证方式,但是我们最常用的的还是基于秘钥的认证:
1、首先生成秘钥

ssh-keygen -t rsa -P ''

2、然后向主机分发秘钥:

ssh-copy-id root@ #@后面跟主机名或者IP地址

3、如果出现以下情况:

# ssh-copy-id -i ~/.ssh/id_rsa.pub 10.1.6.72
-bash: ssh-copy-id: command not found

请尝试:
yum -y install openssh-clientsansible

Ansible常用模块

1.ping:主机连通性测试:

[root@desperadochn ~]# ansible all -m ping
192.168.253.138 | SUCCESS => { “changed”: false, “ping”: “pong” }
192.168.253.137 | SUCCESS => { "changed": false, "ping": 

2.command:在远程主机执行命令;不支持|管道命令

  • [ ] 命令模块接受命令名称,后面是空格分隔的列表参数。给定的命令将在所有选定的节点上执行。它不会通过shell进行处理,比如$HOME和操作符如”小于”<“,”>”, “|”, “;”,”&”‘ 工作(需要使用(shell)模块实现这些功能)。
  • [ ] 该模块可使用的子选项如下(定义在-a后):
    chdir # 在执行命令之前,先切换到该目录
    creates # 一个文件名,当这个文件存在,则该命令不执行,可以用来做判断
    executable # 切换shell来执行命令,需要使用命令的绝对路径
    free_form # 要执行的Linux指令,一般使用Ansible的-a参数代替。
    removes # 一个文件名,这个文件不存在,则该命令不执行,与creates相反的判断
[root@cet7 ansible]# ansible web -m command -a 'ls'  
node1.magedu.com | SUCCESS | rc=0 >>
172.17.30.102
5
anaconda-ks.cfg
bin
hello.jpg
initial-setup-ks.cfg
mha4mysql-manager-0.56-0.el6.noarch.rpm
mha4mysql-node-0.56-0.el6.noarch.rpm
nohup.out

node2.magedu.com | SUCCESS | rc=0 >>
anaconda-ks.cfg
bin
crontab
FLUSH
initial-setup-ks.cfg
mha4mysql-manager-0.56-0.el6.noarch.rpm
mha4mysql-node-0.56-0.el6.noarch.rpm
UNLOCK

[root@cet7 ansible]# ansible web -m command -a 'chdir=/app ls'
node1.magedu.com | SUCCESS | rc=0 >>
fastdfs
mogilefs
zabbix-release-3.4-1.el7.centos.noarch.rpm

node2.magedu.com | SUCCESS | rc=0 >>
fastdfs
zabbix
zabbix-release-3.4-1.el7.centos.noarch.rpm

3、shell模块在远程主机上调用shell解释器运行命令,支持shell的各种功能,例如管道等

[root@cet7 ansible]# ansible web -m shell -a 'cat /etc/passwd|grep "root"'
node1.magedu.com | SUCCESS | rc=0 >>
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

node2.magedu.com | SUCCESS | rc=0 >>
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

4.copy:复制文件到远程主机,可以改权限等
相关选项如下:
backup:在覆盖之前,将源文件备份,备份文件包含时间信息。有两个选项:yes|no
content:用于替代“src”,可以直接设定指定文件的值
dest:必选项。要将源文件复制到的远程主机的绝对路径,如果源文件是一个目录,那么该路径也必须是个目录
directory_mode:递归设定目录的权限,默认为系统默认权限
force:如果目标主机包含该文件,但内容不同,如果设置为yes,则强制覆盖,如果为no,则只有当目标主机的目标位置不存在该文件时,才复制。默认为yes
others:所有的file模块里的选项都可以在这里使用
src:被复制到远程主机的本地文件,可以是绝对路径,也可以是相对路径。如果路径是一个目录,它将递归复制。在这种情况下,如果路径使用“/”来结尾,则只复制目录里的内容,如果没有使用“/”来结尾,则包含目录在内的整个内容全部复制,类似于rsync。
用法:
(1) 复制文件

-a "src= dest= "
[root@cet7 ansible]# ansible web -m copy -a 'dest=/etc/nginx/nginx.conf src=/tmp/nginx.conf backup=yes'             
node1.magedu.com | SUCCESS => {
    "backup_file": "/etc/nginx/nginx.conf.98474.2017-12-05@19:08:38~", 
    "changed": true, 
    "checksum": "c5001a957cc53286f448b5519183f21c3ac82a90", 
    "dest": "/etc/nginx/nginx.conf", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "2ca5097c8b871a904650b483da85cc65", 
    "mode": "0644", 
    "owner": "root", 
    "size": 2466, 
    "src": "/root/.ansible/tmp/ansible-tmp-1512472114.1-112119450637656/source", 
    "state": "file", 
    "uid": 0
}
node2.magedu.com | SUCCESS => {
    "backup_file": "/etc/nginx/nginx.conf.126834.2017-12-05@19:08:39~", 
    "changed": true, 
    "checksum": "c5001a957cc53286f448b5519183f21c3ac82a90", 
    "dest": "/etc/nginx/nginx.conf", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "2ca5097c8b871a904650b483da85cc65", 
    "mode": "0644", 
    "owner": "root", 
    "secontext": "system_u:object_r:httpd_config_t:s0", 
    "size": 2466, 
    "src": "/root/.ansible/tmp/ansible-tmp-1512472118.6-139130457070354/source", 
    "state": "file", 
    "uid": 0
}

(2) 给定内容生成文件

-a "content= dest= "
[root@cet7 ansible]# ansible web -m copy -a 'content="hello jone" dest=/root/a.txt backup=yes'
node1.magedu.com | SUCCESS => {
    "changed": true, 
    "checksum": "dc302a377c9890ad3ea20b0d252d8c06e437ff97", 
    "dest": "/root/a.txt", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "de66aa1fe06344d982e2e7448cbdfb20", 
    "mode": "0644", 
    "owner": "root", 
    "size": 10, 
    "src": "/root/.ansible/tmp/ansible-tmp-1512472257.6-80857257348609/source", 
    "state": "file", 
    "uid": 0
}
node2.magedu.com | SUCCESS => {
    "changed": true, 
    "checksum": "dc302a377c9890ad3ea20b0d252d8c06e437ff97", 
    "dest": "/root/a.txt", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "de66aa1fe06344d982e2e7448cbdfb20", 
    "mode": "0644", 
    "owner": "root", 
    "secontext": "system_u:object_r:admin_home_t:s0", 
    "size": 10, 
    "src": "/root/.ansible/tmp/ansible-tmp-1512472258.3-112721288787897/source", 
    "state": "file", 
    "uid": 0
}
[root@cet7 ansible]# ansible web -m shell -a 'cat /root/a.txt'
node1.magedu.com | SUCCESS | rc=0 >>
hello jone

node2.magedu.com | SUCCESS | rc=0 >>
hello jone

5、其他模块

(1) file模块: Sets attributes of files
用法:
<1> 创建链接文件:*path= src= state=link
<2> 修改属性:path= owner= mode= group=
<3> 创建目录:path= state=directory

注意:state属性的可用值:file,directory,link,hard,touch,absent

(2) get_url模块: Downloads files from HTTP, HTTPS, or FTP to node

*url=
*dest=
sha256sum=
owner, group, mode

(3) git模块:Deploy software (or files) from git checkouts

                    repo=
                    dest=
                    version=

(4) deploy_helper模块:Manages some of the steps common in deploying projects.

(5) haproxy模块:Enable, disable, and set weights for HAProxy backend servers using socket commands.

backend=
host=
state=
weight=

(6) cron 模块:Manage cron.d and crontab entries.

minute=
day=
month=
weekday=
hour=
job=
*name=
state=(present:创建\|absent:删除)

(7) hostname模块:Manage hostname

name=

(8) pip模块:Manages Python library dependencies.

name=
state=
version=

(9) npm模块:Manage node.js packages with npm

name=
state=
version=

(10) yum模块:Manages packages with the `yum' package manager

name=(程序包名称,可以带版本号)
state=
    present, latest, installed
    absent, removed
  • [ ] 其它的包管理工具:apt(debian), zypper(suse), dnf(fedora), rpm, dpkg, ...

(11) service模块:管理服务

*name=
state=
    started
    stopped
    restarted
enabled=
runlevel=

(12) setup模块:获取facters,即获取系统相关的各项参数

Playbook

Playbook是YAML语言写成的剧本,但其本质仍是模板调用。剧本用来执行一系列命令,类似脚本。

YAML语言以缩进区分级别,以-区分键值,所以次语言对格式要求很高。

比如以下:
用命令行写的远程安装redis的命令转化为playbook的格式即是:

ansible node1.magedu.com -m yum -a "name=redis state=latest"
vim first.yaml
---
- hosts: node1.magedu.com
  remote_user: root
  tasks: 
  - name: install redis
    yum: name=redis state=latest

hosts: 后跟的主机,需在/etc/ansible/hosts里事先定义;
remote_user: 远程主机执行命令的用户身份;
tasks: 顾名思义,任务列表;
name: 一个name代表一个任务,后跟任务名,实际作用为区分所执行的任务;
yum: 调用的模块。后跟的内容对应命令行-a后的内容。

单个任务而言显然是命令行更高效。单要是一长串的任务playbook的优势就明显了。比如安装后启动redis。

vim second.yaml
---
- hosts: node1.magedu.com
  remote_user: root
  tasks: 
  - name: install redis
    yum: name=redis state=latest
  - name: start redis
    service: name=redis start=started

一个稍微全面的playbook如下,里面还有我们尚未提及的内容:

vim third.yaml
---
- hosts: all
  remote_user: root
  tasks: 
  - name: install redis
    yum: name=redis state=latest
  - name: copy config file
    copy: src=/root/playbook/redis.conf dest=/etc/redis.conf owner=redis
    notify: restart redis
    tags: configfile
  - name: start redis
    service: name=redis start=started

    handlers: 
  - name: restart redis
    service: name=redis state=restarted

notify: 定义触发任务。这里指copy config file任务执行成功会触发执行restart redis任务;
handlers: 由特定条件触发才会执行相对应的任务;
tags: 为任务添加标签,可调用单独执行一个命令,如: ansible-playbook -t configfile third.yaml,意为只执行标签为configfile的任务,即只拷贝redis的配置文件。

对应此,我们详细说下playbook。

运行playbook的方式:

(1) 测试

  • [ ] ansible-playbook --check: 只检测可能会发生的改变,但不真正执行操作;
  • [ ] ansible-playbook --list-hosts: 列出运行任务的主机;
  • [ ] ansible-playbook --list-tasks: 列出要运行的任务列表
  • [ ] ansible-playbook --syntax-check: 语法检查

(2) 运行

  • [ ] ansible-playbook xx.yaml

playbook的基础组件:

Hosts:运行指定任务的目标主机;
remoute_user: 在远程主机上执行任务的用户;
    sudo_user:
tasks:任务列表
    模块,模块参数;
    格式:
        (1) action: module arguments
        (2) module: arguments

        注意:shell和command模块后面直接跟命令,而非key=value类的参数列表;

       (1) 某任务的状态在运行后为changed时,可通过“notify”通知给相应的handlers;
       (2) 任务可以通过"tags"打标签,而后可在ansible-playbook命令上使用-t指定进行调用;

handlers:
    任务,在特定条件下触发;
    接收到其它任务的通知时被触发;
notify: HANDLER TASK NAME

variables:
    (1) facts:可直接调用;
        注意:可使用setup模块直接获取目标主机的facters;
    (2) 用户自定义变量:

        (a) ansible-playbook命令的命令行中的
            -e VARS, --extra-vars=VARS                      

        (b) 在playbook中定义变量的方法:
            vars:
            - var1: value1
            - var2: value2

            变量引用:{{ variable }}

    (3) 通过roles传递变量;
    (4) Host Inventory
        (a) 用户自定义变量
            (i) 向不同的主机传递不同的变量;
                IP/HOSTNAME  varaiable=value var2=value2
            (ii) 向组中的主机传递相同的变量;
                [groupname:vars]
                variable=value

        (b) invertory参数
            用于定义ansible远程连接目标主机时使用的参数,而非传递给playbook的变量;
                ansible_ssh_host
                ansible_ssh_port
                ansible_ssh_user
                ansible_ssh_pass
                ansbile_sudo_pass
                            ...

补充模块:
    setup模块:

    template模块:基于模板方式生成一个文件复制到远程主机
        *src=
        *dest=
        owner=
        group=
        mode=

模板:templates
    文本文件,嵌套有脚本(使用模板编程语言编写)
        Jinja2:
            字面量:
                字符串:使用单引号或双引号;
                数字:整数,浮点数;
                列表:[item1, item2, ...]
                元组:(item1, item2, ...)
                字典:{key1:value1, key2:value2, ...}
                布尔型:true/false

                算术运算:
                    +, -, *, /, //, %, **

                比较操作:
                    ==, !=, >, >=, <, <=

                逻辑运算:
                    and, or, not 

        示例: 
        - hosts: websrvs
        remote_user: root
          tasks:
            - name: install nginx
            yum: name=nginx state=present
            - name: install conf file
            template: src=files/nginx.conf.j2 dest=/etc/nginx/nginx.conf
            notify: restart nginx
            tags: instconf
            - name: start nginx service
            service: name=nginx state=started
            handlers:
            - name: restart nginx
            service: name=nginx state=restarted                 

            模板配置文件 :nginx.conf.j2
            worker_processes {{ ansible_processor_vcpus - 1 }};
            listen {{ http_port }};
            server_name 

条件测试:
    when语句:在task中使用,jinja2的语法格式
        tasks: 
        - name: install conf file to centos7
          template: src=files/nginx.conf.c7.j2
          when: ansible_distribution_major_version == "7"
        - name: install conf file to centos6
          template: src=files/nginx.conf.c6.j2
          when: ansible_distribution_major_version == "6"               

循环:迭代,需要重复执行的任务;
    对迭代项的引用,固定变量名为”item“
    而后,要在task中使用with_items给定要迭代的元素列表;
        列表方法:
            字符串
            字典

    - name: install some packages
      yum: name={{ item }} state=present
      with_items:
      - nginx
      - memcached
      - php-fpm

    - name: add some groups
      group: name={{ item }} state=present
      with_items:
      - group11
      - group12
      - group13
    - name: add some users
      user: name={{ item.name }} group={{ item.group }} state=present
      with_items:
      - { name: 'user11', group: 'group11' }
      - { name: 'user12', group: 'group12' }
      - { name: 'user13', group: 'group13' }

角色(roles):
    角色集合:
        roles/
            mysql/
            httpd/
            nginx/
            memcached/

    每个角色,以特定的层级目录结构进行组织:
        mysql/
            files/ :存放由copy或script模块等调用的文件;
            templates/:template模块查找所需要模板文件的目录;
            tasks/:至少应该包含一个名为main.yml的文件;其它的文件需要在此文件中通过include进行包含;
            handlers/:至少应该包含一个名为main.yml的文件;其它的文件需要在此文件中通过include进行包含;
            vars/:至少应该包含一个名为main.yml的文件;其它的文件需要在此文件中通过include进行包含;
            meta/:至少应该包含一个名为main.yml的文件,定义当前角色的特殊设定及其依赖关系;其它的文件需要在此文件中通过include进行包含;
            default/:设定默认变量时使用此目录中的main.yml文件;

    在playbook调用角色方法1:
        - hosts: websrvs
          remote_user: root
          roles:
          - mysql
          - memcached
          - nginx

    在playbook调用角色方法2:传递变量给角色
        - hosts: 
          remote_user:
          roles:
          - { role: nginx, username: nginx }
            键role用于指定角色名称;后续的k/v用于传递变量给角色;

        还可以基于条件测试实现角色调用;
        roles:
        - { role: nginx, when: "ansible_distribution_major_version == '7' " }

ansible-vcs:
    https://github.com/andrewrothstein/ansible-vcs