Ansible使用入门
思维导图:
1. 运维工作简介
1.1 运维简述
1.1.1 运维工作
- 运维工作的核心任务:
- 发布、变更、故障处理
- 系统安装(物理机、虚拟机)--> 程序包安装、配置、服务启动 --> 批量操作 --> 程序发布 --> 监控
- 系统安装(物理机、虚拟机)
- 程序安装、配置、服务启动
- 批量操作(批量运行命令)
- 程序发布
- 监控
预发布验证:
- 新版本的代码先发布到服务器(跟线上环境配置完全相同,只是未接入到调度器)
程序发布:
- 不能影响用户体验
- 系统不能停机
- 不能导致系统故障或造成系统完全不可用
1.1.2 灰度发布
发布路径:
- /webapp/tuangou-1.1
- /web/app/tuangou
- /webapp/tuangou-1.2
在调度器上下线一批主机(maintanance) --> 关闭服务 --> 部署新版本的应用程序 --> 启动服务 --> 在调度器上启用这一批服务器
- 通过调度器将线上的一批服务器标记为down模式(软关闭,比如将权重调为0)(maintanance)
- 关闭相应服务
- 部署新版本的应用程序至目标位置
- 启动相关应用
- 调度主机上线
自动化灰度发布:脚本、发布平台
- 灰度发布:
- 基于主机
- 基于用户
一些专业名词:
- CI:持续集成
- CD:持续交付
- CD:持续部署
- 以上三个过程如果能够串联起来自动执行,就叫:DevOps
1.1.3 运维工具的分类
- agent:
- puppet、func
- agentless:
- ansible、fabric
- ssh
1.1.4 监控工具
不允许没有被监控的系统上线
- 监控数据采集:
- 用户行为日志
- 服务器性能
- 运行数据报告
- 监控管理:
- 异常报警
- 失效转移
- 自动优雅降级
1.1.5 运维工具的层次
- OS Provisioning:系统安装
- 物理机:PXE、Cobbler
- 虚拟机(云环境下):Image、Templates
- Configuration:
- puppet(ruby)
- saltstack(python)
- chef
- cfengine
- Command and Control:
- func
- ansible(python)
- fabric
1.1.6 运维工具图示
1.2 持续集成、持续交付、持续部署
1.2.1 集成、部署、交付
集成:
- 指软件个人研发的部分向软件整体部分交付,以便尽早发现个人开发部分的问题
部署:
- 代码尽快向可运行的开发/测试环节交付,以便尽早测试
交付:
- 指研发尽快向客户交付,以便尽早发现生产环节中存在的问题
- 如果等到所有东西都完成了才向下个环节交付,导致所有的问题只能在最后才爆发出来,解决成本巨大
持续:
- 每完成一个完整的部分,就向下个环节交付,发现问题可以马上调整,问题不会放大到其他部分和后面的环节
1.2.2 CI、CD
持续集成(CI):
- 开发人员提交了新代码之后,立刻进行构建、(单元)测试
- 根据测试结果,我们可以确定新代码和原有代码能否正确的集成在一起
持续交付(CD):
- 在持续集成的基础上,将集成后的代码部署到更贴近真实运行环境中(类生产环境)
- 如果代码没问题,可以继续手动部署到生产环境中
持续部署(CD):
- 在持续交付的基础上,把部署到生产环境的过程自动化
2. Ansible简介&模块详解
2.1 Ansible的安装&基本结构
2.1.1 ansible简介
- 模块化,调用特定的模块,完成特定的任务
- 基于python语言实现,由Paramiko、PyYAML和Jinja2三个关键模块
- 部署简单,agentless
- 主从模式
- 支持自定义模块
- 支持Playbook
- 幂等性
2.1.2 ansible的安装
- 安装:直接yum安装(epel、ansible)
- 配置文件:
- 配置文件:/etc/ansible/ansible.cfg
- 主机清单:/etc/ansible/hosts
- 在这个文件中定义要控制的主机
- 主程序:
- ansible
- ansible-playbook
- ansible-doc
2.1.3 ansible的使用
准备操作:
- ansible在使用前要先在/etc/ansible/hosts中定义要控制的主机
- 还要将Ansible Server的ssh公钥分发到各被管控节点上:
- ssh-keygen -t rsa -f ~/.ssh/id_rsa -N ""
- ssh-copy-id root@10.0.0.205
- ssh-copy-id root@10.0.0.206
- ssh-copy-id root@10.0.0.207
- ssh-copy-id root@10.0.0.208
- 然后就可以在主控server上进行控制了
ansible的简单使用格式:
- ansible HOST-PATTERN -m MOD_NAME -a MOD_ARGS -f FORKS -C -u USERNAME
- -m:指明模块
- -a:指明调用的模块参数
- -f:一批管控多少主机,这里的FORKS也可以在/etc/ansible/ansible.cfg文件中定义,可以将这个值改大点
- -C:--check,干跑,而不真正执行
- -u:指明用户名
- -c:指明连接方式,默认是smart,自动选择
- 使用示例:
- ansible 10.0.0.207 -m ping
- 测试10.0.0.207主机的连通性
- ansible all -m ping
- 测试在/etc/ansible/hosts中定义的所有主机的连通性
- ansible 10.0.0.207 -m ping
ansible的工作图示:
2.2 Ansible常用模块详解
2.2.1 获取模块列表
- ansible-doc -l
- 列出ansible所有支持的模块
- ansible-doc -s GROUP_NAME
- 查看对应模块的用法说明,例如ansible-doc -s group 查看group模块的用法说明
- ansible使用要点:
- 定义所期望的目标状态
- 操作必须是幂等的(所谓幂等就是指重复数次的结果是相同的)
2.2.2 group
- 作用:
- 管理用户组
- 模块参数:
- name=指定该用户组的组名
- gid=指定该用户组的gid
- system=指定该组是否为系统组,yes表示是,no表示不是系统组
- state=指定目标状态,present表示要创建出来,absent就表示要删除它
- 使用示例:
- 创建一个gid为3000的名叫mygrp的非系统组
- ansible all -m group -a "gid=3000 name=mygrp state=present system=no"
- 删除刚刚创建的那个组:
- ansible all -m group -a "gid=3000 name=mygrp state=absent system=no"
- 创建一个gid为3000的名叫mygrp的非系统组
2.2.3 user
- 作用:
- 管理用户账号
- 模块参数:
- name=指定用户名
- uid=指定用户uid
- group=指定用户的主组
- groups=指定用户的附加组
- home=指定用户家目录
- shell=指定用户登录的shell
- comment=指定对该用户的描述
- system=指定用户是否为系统用户yes或者no
- state=present或者absent
- 使用示例:
- ansible all -m user -a "uid=5000 name=mytestuser state=present groups=mygrp shell=/bin/bash"
2.2.4 copy
- 作用:
- 复制文件
- 从本机到目标主机,或者从远程主机到目标主机
- 模块参数:
- dest=指定的目标路径,如果源是一个目录,则目标必须是目录
- src=指定的源路径,如果源是目录,则默认就会做递归复制,这里如果结尾带了/ 则表示只复制目录中的内容,不带斜线则表示复制所有
- remote_src=可以指定远程的源路径
- owner=指定目标文件的属主
- group=指定目标文件的属组
- mode=指定目标文件的权限
- content=不使用src拷贝文件时,使用content直接指定文件内容(src和content必须有其一)
- 注意:
- 如果不指定属主属组,它是默认不变的,以哪个用户复制就以哪个用户来生成,
- 如果自己指明用户,要确保目标主机存在那个用户
- 使用示例:
- ansible all -m copy -a "src=/etc/fstab dest=/tmp/fstab mode=600"
- 也可以使用content生成文件:
- ansible all -m copy -a "content='hello world\n' dest=/tmp/hello.txt"
2.2.5 fetch
- 作用:
- 从远程主机上复制文件到本地
- 使用参数:
- dest=指定目标路径
- src=指定源路径
- fail_on_missing 远程主机如果没有文件则报错退出(在ansible2.4中已经默认就是yes了)
- 拉取过来之后会在本地保存成一个嵌套目录:
- ansible 10.0.0.205 -m fetch -a "src=~/test.txt dest=/tmp/"
- 在本地存储为:/tmp/10.0.0.205/root/test.txt
2.2.6 command
- 作用:
- 直接在远程主机上执行命令
- 注意:
- 对于这个模块,-a中的命令不用写成键值对形式,直接指定命令即可
- command模块使用时,-a中引号内的内容不使用shell来解析,所以要指明shell来解析(貌似指明了executable也没用)
- 使用参数:
- chdir=切换到指定的目录下去执行命令
- 注意:chdir不是幂等的,也就是说执行成功之后再执行就会失败
- executable=由哪个shell命令发起执行程序,可以指明一个新的shell
- chdir=切换到指定的目录下去执行命令
- 使用示例:
- 在每个远程主机上执行ifconfig命令:
- ansible all -m command -a "ifconfig"
- 切换目录后执行命令:
- ansible all -m command -a "chdir=/tmp mkdir hello.dir"
- 在每个远程主机上执行ifconfig命令:
2.2.7 shell
- 作用:
- 真正的执行shell命令的模块,可以识别命令行中的众多字符
- 注意:
- 这个模块可以实现众多真正shell命令行中的功能
- 使用参数及用法与command相同
- 识别shell命令行中的元字符的两种方法:
- ansible 10.0.0.205 -m shell -a "/bin/bash -c 'ls -alh /tmp/wzhhg'"
- ansible 10.0.0.205 -m shell -a "executable=/bin/bash ls -alh /tmp/wzhhg"
2.2.8 file
- 作用:
- 创建文件,真正意义上来说是修改文件属性的
- 使用示例:
- 在指定的路径下创建一个目录:
- ansible all -m file -a "path=/tmp/testhello.dir state=directory"
- 在指定的路径下创建一个文件:
- ansible all -m file -a "path=/tmp/testhello.txt state=file"
- 创建一个符号链接文件:
- ansible all -m file -a "src=/tmp/fstab path=/tmp/fstab.link state=link"
- 在指定的路径下创建一个目录:
2.2.9 cron
- 作用:
- 定义任务计划
- 使用参数:
- day=、hour=、minute=、month=、weekday= 指定的间隔时间,默认使用的是*,为空表示是*
- name=指明任务的名称,如果不指定名字,默认为None,删除的时候要指定名字,否则删不掉
- user=任务为哪个用户的
- state=任务是添加还是删除,present表示添加,absent表示删除,如果不写,默认就是present
- job=指明任务
- 使用示例:
- 每隔3分钟同步一次时间:(这里虽好指定name,以便以后方便调用)
- ansible all -m cron -a "minute=*/3 job='/usr/sbin/update 10.0.0.203 &> /dev/null'"
- 删除刚刚添加的任务:
- ansible all -m cron -a "minute=*/3 job='/usr/sbin/update 10.0.0.203 &> /dev/null' name=None state=absent"
- 每隔3分钟同步一次时间:(这里虽好指定name,以便以后方便调用)
2.2.10 yum
- 作用:
- 安装程序包
- 使用参数:
- name=指明程序包名
- state=安装还是卸载,present、installed、latest都表示安装,absent、removed表示卸载
- disable_gpg_check:安装的过程中禁用密钥检测
- disablerepo=指明安装过程中禁用的某个仓库
- enablerepo=指明安装过程中开启的某个仓库
- 使用示例:
- 安装httpd包:ansible all -m yum -a "name=httpd state=installed"
2.2.11 service
- 作用:
- 管理服务
- 使用参数:
- name=指明服务命令,如果是CentOS7可以不用加.service
- enabled=是否设置为开机自启动,yes或者no
- runlevel=在哪些级别下开机自启动
- state=服务是启动还是关闭,started表示启动,stopped表示关闭,restarted表示重启,reloaded表示平滑重载
- pattern=指明的匹配格式,如果匹配到了这里指定的字符串就表示成功
- 使用示例:
- 启动nginx服务:ansible all -m service -a "name=nginx state=started"
2.2.12 script
- 作用:
- 执行脚本,自动把本地的脚本复制到远程主机上,并在远程主机上执行
- 使用参数:
- 直接指明脚本
- 使用示例:
- 将脚本复制到远程主机上并执行脚本
- ansible all -m script -a "/tmp/test.sh"
- 将脚本复制到远程主机上并执行脚本
2.2.13 ping
- 使用示例:
- ansible all -m ping --list-hosts
- 注意:
- 这里使用all表示ansible的hosts文件中定义的所有主机,这里也可以直接指定主机,或者用正则表达式匹配
- 这里使用--list-hosts可以列出适配出来的主机,不真正执行
3. Ansible之playbook详解
3.1 playbook简介
3.1.1 YAML格式简介
- 让每一台主机要执行的任务保存在一个文件中,这个文件组织成YAML格式
- YAML:是一个可读性高,用来表达数据序列的格式,它其实是一种标记语言
- Playbook:YAML格式,任务(task)
- 基本数据结构:
- 标量、数组、关联数组
3.1.2 Playbook的核心元素
- Hosts:主机(谁负责来唱这出戏)
- Tasks:任务列表(要唱哪几出戏)
- Variables:变量
- Templates:包含了模板语法的文本文件
- Handlers:由特定条件触发的任务,要触发handlers,只需要在某个任务上加上notify
- Roles:角色
3.1.3 playbook的基础组件
- hosts:运行指定任务的目标主机
- remote_user:在远程主机上执行任务的用户(在目标主机上以哪种用户的身份执行命令)
- sudo_user:sudo到哪个用户去执行
- tasks:任务列表
- 模块,模块参数
- 格式:
- action:module arguments
- module:arguments
- 注意:shell和command模块后面直接跟命令,而非键值对的参数列表
3.1.4 运行playbook的方式
测试操作:
- ansible-playbook --check
- 只检测可能会发生的改变,但不真正执行操作,干跑
- ansible-playbook --list-hosts
- 列出运行任务的主机
- ansible-playbook --syntax-check:
- 检查语法,真正的检查语法
ansible-playbook:
- -C:--check,检查语法,这里是干跑一遍
- --list-hosts:列出相关主机
- --list-tasks:列出所有任务
- --list-tags:列出所有标签
- --syntax-check:检查语法
特别注意:
- 如果有一个任务在某一主机上停止了,则所有任务在所有主机上都无法完成
- 它是按任务分派给所有主机,先执行第一个,再执行第二个,再执行第三个,一次类推
3.2 playbook文件详解
3.2.1 定义playbook文件
- hosts: all remote_user: root # 以什么用户去执行任务 tasks: # 指定任务列表 - name: install redis yum: name=redis state=latest - name: copy config file copy: src=/root/playbooks/redis.conf dest=/etc/redis.conf notify: restart redis # 触发名称为restart redis的任务,该任务在handlers中定义 tags: configfile # 这里对该任务加上了名为configfile的标签 - name: start redis service: name=redis state=started enabled=true handlers: - name: restart redis service: name=redis state=restarted
注意:
- handlers只会在所有任务执行完成后执行。而且即使被通知了很多次,它也只会执行一次
后续操作:
- 然后语法检查:
- ansible-playbook --syntax-check first.yaml
- 然后干跑一遍:
- ansible-playbook -C first.yaml
- 然后直接执行:
- ansible-playbook first.yaml
定义标签:
- 可以在playbook的yaml配置文件中的特定任务中加上一个标签,然后在执行时只调用这一个标签
- 使用:ansible-playbook -t configfile second.yaml 这里是只执行加上了configfile标签的任务
3.2.2 handlers简介
- 作用:任务,特定条件下触发的任务
- 说明:收到其他任务的通知时被触发:
- 如:notify: restart redis , 触发名称为restart redis的任务,该任务在handlers中定义
- handlers只会在所有任务执行完成后执行,而且即使被通知了很多次,它也只会执行一次
3.2.3 variables详解
1) facts:可直接调用的变量
- 说明:可以使用setup模块直接获取目标主机的facters
- setup模块:
- 作用:用来收集每一个被ansible管控的主机之上相关的环境变量
- 使用:ansible 10.0.0.206 -m setup
- 查看用法:ansible-doc -s setup
- 在yaml文件中调用变量:(将变量置于双花括号内以调用)
-
- hosts: all remote_user: root tasks: - name: copy file copy: content={{ ansible_env }} dest=/tmp/ansible.env # 双花括号以调用变量
-
2) ansible-playbook命令的命令行中自定义变量
- 在yaml文件中定义:
-
- hosts: all remote_user: root tasks: - name: install {{ pkgname }} yum: name={{ pkgname }} state=latest
-
- 在命令行中使用-e参数进行传递变量:
- ansible-playbook -e pkgname=memcached forth.yaml 将memcached赋值给变量package
3) Host Inventory
用户自定义变量:
- 向不同的主机传递不同的变量:(在ansible的hosts文件中定义)
- 格式:IP/HOSTNAME varaiable=value var2=value2
- 示例:
[websrvs] 172.16.0.67 http_port=8080 my_num=10080 # 添加两个自定义变量 172.16.0.68
- 说明:
- 这里定义的变量在playbook当中(也就是yaml文件中)是可以直接引用的
- 这里定义的值在模板中也是可以直接变量引用的
- 向组中的主机传递相同的变量:(在ansible的hosts文件中定义)
- 格式:
- [ groupname:vars ]
- variable=value
- 示例:
[websrvs] 172.16.0.67 172.16.0.68 [websrvs:vars] http_port=8080 my_num=10080 # 表示在websrvs组中的所有主机上都定义上这里面所定义的两个变量
- 格式:
invertory参数:用于定义ansible远程连接目标主机时使用的参数,而非传递给playbook的变量
- 定义的位置:直接写在ansible的hosts文件中远程主机的IP地址后面
- ansible_ssh_host=使用远程主机另外的地址来连接
- ansible_ssh_prot=使用其他的端口来连接远程主机
- ansible_ssh_user=使用其他的用户登录,不写默认是root
- ansible_ssh_pass
- ansible_sudo_pass
- 使用示例:
- 172.16.0.67 ansible_ssh_port=22122 ansible_ssh_user=hgzero
3.2.4 templates模板
说明:文本文件,嵌套有脚本(使用模板编程语言编写)
Jinja2:
- 字面量:
- 字符串:使用单引号或者双引号
- 数字:整数,浮点数
- 列表:[ item1, item2, ...]
- 元组:( item1, tiem2, ...)
- 字典:{ key1:value, key2:value, ...}
- 布尔型:true/false
- 算术运算:
- +, -, *, / , //, %, **
- 比较运算:
- ==, !=, >, >=, <, <=
- 逻辑运算:
- and,or,not
template模块:基于模板方式生成一个文件复制到远程主机
- 使用参数:
- src=模板文件
- dest=基于模板生成以后的数据流,保存下来生成一个文件
- owner=属主
- group=属组
- mode=权限
在配置文件中内嵌一个变量:
- vim /root/playbooks/redis.conf.j2
- bind {{ ansible_eno16777736.ipv4.address }}
- 说明:可以用点号调用某个变量一个子集下的一个子集...
- 在yaml文件中定义:
- hosts: 172.16.0.67 remote_user: root tasks: - name: install config file template: src=/root/playbooks/redis.conf.j2 dest=/tmp/redis.conf
3.3 playbook高级用法
3.3.1 条件测试
- when语句:在task中使用,jinja2的语法格式
- 示例:
- hosts: websrvs remote_user: root tasks: - name: install httpd yum: name=httpd state=latest when: ansible_os_family == "RedHat" # 可以用when关键字对变量进行判断 - name: install apache2 apt: name=apache2 state=latest when: ansible_os_family == "Debian"
3.3.2 循环、迭代操作
- 使用:
- 需要重复执行的任务,对迭代项的应用,固定变量名为"item"
- 而后,要在task中使用with_items给定要迭代的元素列表
- 列表方法:
- 字符串迭代:
- name: install {{ item}} package yum: name={{ itme }} state=present # 迭代with_items中的每个字符串 with_items: - nginx - memcached - php-fpm
- 字典迭代:
- name: add some users user: name={{ item.name }} group={{ item.group }} state=present # 迭代with_items中的每个字典 with_items: - { name: 'user11', group: 'group11' } - { name: 'user12', group: 'group12' } - { name: 'user13', group: 'group13' }
- 字符串迭代:
4. Ansible之role角色详解
4.1 角色(roles)简介
1) 描述:自包含的所需要的各种文件的集合
2) 角色的集合
roles/ mysql/ httpd/ nginx/ memcached/
3) 每个角色,以特定的层级目录结构进行组织(至少要有一个task,其他都是可选的)
mysql/ files/:存放由copy或script模块等调用的文件 templates/:template模块查找所需要模板文件的目录 tasks/:至少应该包含一个名为main.yml的文件,其他的文件需要在此文件中通过include进行包含 vars/:至少应该包含一个名为main.yml的文件,其他的文件需要在此文件中通过include进行包含 meta/:至少应该包含一个名为main.yml的文件,定义当前角色的特殊设定及其依赖关系 其他的文件需要在此文件中通过include进行包含 default/:设定默认变量时使用此目录中的main.yml文件
- 注意:ansible的role的存放位置:应该放在/etc/ansible/ansible.cfg中指定的路径中
4.2 创建角色
1) 创建基本角色目录集:
- mkdir -pv /etc/ansible/roles/nginx/{files,templates,tasks,vars,handlers,meta,default}
2) 编辑创建工作任务tasks文件:
vim /etc/ansible/roles/nginx/tasks/main.yml - name: install nginx yum: name=nginx state=latest when: ansible_os_family == "RedHat" - name: install conf template: src=vhost1.conf.j2 dest=/etc/nginx/conf.d/vhost1.conf tags: conf # 为配置任务打个标签 notify: restart nginx # 触发handlers中定义的restart nginx - name: install site home directory file: path={{ ngxroot }} state=directory # 这里调用的变量被定义在了vars目录下 - name: install index page copy: src=index.html dest={{ ngxroot }}/ - name: start nginx service: name=nginx state=started
3) 编辑创建模板templates文件:
vim roles/nginx/templates/vhost1.conf.j2 server { listen 8080; server_name {{ ansible_fqdn }}; location / { root "/ngin" } }
4) 编辑创建handlers文件:
vim roles/nginx/handlers/main.yml - name: restart nginx # 这里定义的内容会被notify触发 service: name=nginx state=restarted
5) 编辑创建变量vars文件:
vim roles/nginx/vars/main.yml ngxroot: /ngxdata/vhost1 # 定义变量要用字典的格式,是yaml格式的字典
6) 编辑创建files文件:
vim roles/nginx/files/index.html
<h1>Vhost1</h1>
7) 编辑创建主执行文件:
vim nginx.yml - hosts: websrvs remote_user: root roles: - nginx
8) 执行:
- 直接执行:
- ansible-playbook nginx.yml
- 只修改配置文件:
- ansible-playbook -t conf nginx.yml