安装ansible
yum方式安装
导入阿里云epel yum源
wget http://mirrors.aliyun.com/repo/Centos-7.repo wget http://mirrors.aliyun.com/repo/epel-7.repo
|
安装repo
yum install ansible python2-pip
|
模块
authorized_key
管理用户authorized_keys实现免密登陆
参数 |
描述 |
comment |
更改公钥注释 |
exclusive |
添加或删除authorized_keys |
key |
指定公钥 |
path |
指定authorized_keys路径 |
state |
如果是present则创建,如果是absent则删除 |
user |
指定用户名称 |
配置节点k8s用户互信
- name: config k8s user account authorized_key: user: k8s key: "{{ lookup('file', '/home/k8s/.ssh/id_rsa.pub') }}" exclusive: yes state: present
|
command模块
参数 |
描述 |
creates |
一个文件名,当该文件存在则命令不执行 |
free_form |
要执行的Linux指令 |
chdir |
在执行指令之前,先切换到该指定的目录 |
removes |
一个文件名,如果文件不存在,则该选项不执行 |
state |
如果是present则创建,如果是absent则删除 |
user |
指定用户名称 |
copy模块
参数
参数 |
描述 |
src |
源文件 |
dest |
目标路径 |
backup |
覆盖前是否备份原文件 |
owner |
设置文件目录的属主 |
group |
设置文件目录的属组 |
mode |
设置文件目录的权限 |
cron模块
参数 |
描述 |
backup |
对远程主机上原任务计划内容修改之前备份 |
day |
日 |
hour |
小时 |
minute |
分钟 |
month |
月 |
weekday |
周 |
job |
要执行的文件,依赖于state=present |
name |
该任务的描述 |
special_time |
指定什么时候执行(参数:reboot、yearly、annually、monthly、weekly、daily、hourly) |
state |
确认该任务计划是创建还是删除 |
user |
以哪个用户的身份执行 |
创建一个计划任务
- name: Create cron job cron: name: cron_test minute: "*/3" hour: "*" day: "*" month: "*" weekday: "*" job: "/usr/sbin/ntpdate cn.ntp.org.cn"
|
docker_container模块
参数 |
可选项 |
描述 |
command |
|
容器启动时执行的命令可以是字符串,也可以是列表 |
hostname |
|
容器hostname |
image |
|
容器镜像 |
name |
|
容器名称 |
env |
|
容器环境变量 |
network_mode |
bridge、host、none |
容器的网络模式 |
pull |
|
下载镜像到本地 |
read_only |
|
挂载容器根文件系统为只读 |
restart |
|
重启容器 |
restart_policy |
no、on-failure、always、unless-stopped |
重启容器策略 |
state |
absent、present、stopped、started |
容器运行状态 |
volume_driver |
|
volume driver |
volumes |
|
容器共享宿主机的目录,例如:volumes: /opt:container:/opt |
创建容器
- name: start docker registry docker_container: name: registry image: registry:2 ports: - 4000:5000 - 80:80 state: started restart_policy: always volumes: - /opt/t2cp:/var/lib/registry - /etc/test:/etc/test
|
重启容器
- name: restart docker registry docker_container: name: registry image: registry:2 state: restarted restart: yes ports: 4000:5000
|
file模块
参数 |
描述 |
group |
定义文件/目录属组 |
owner |
定义文件目录属主 |
mode |
定义文件目录的权限 |
owner |
设置文件目录的属主 |
path |
定义文件目录的路径 |
recurse |
递归设置文件的属性,只对目录有效 |
state |
定义文件状态(state状态有三个选项directory:如果目录不存在则创建、touch:如果文件不存在则创建、absent:删除文件或目录) |
fetch模块
从远程主机复制文件到本地
参数 |
描述 |
src |
源文件 |
dest |
目标文件 |
flat |
如果dest的路径以/结尾保存在相对路径目录下 |
目标节点拉取配置文件到ansible节点
- name: copy ceph config to tmp dir fetch: src: /tmp/ceph.conf dest: /tmp/ceph.conf flat: yes
|
find模块
find模块可以帮助我们在远程主机查找符合条件的文件,类似与linux操作系统find命令。
参数 |
描述 |
paths |
指定文件路径,可以指定多个路径,路径间用逗号隔开 |
recurse |
当 recurse 参数设置为 yes 时,表示在指定目录中递归的查找文件 |
hidden |
当 hidden 参数的值设置为 yes 时,才会查找隐藏文件 |
file_type |
指定查找文件类型,可指定的文件类型有 any、directory、file、link 四种 |
contains |
根据文章内容查找文件,此参数的值为一个正则表达式,find 模块会根据对应的正则表达式匹配文件内容。 |
age |
根据时间范围查找文件,可以使用的单位有秒(s)、分(m)、时(h)、天(d)、星期(w) |
age_stamp |
文件的时间属性有三种,atime、ctime、mtime,当我们根据时间范围查找文件时,可以指定以哪个时间种类 |
size |
指定查找文件的大小,可以使用的单位有 t、g、m、k、b |
group模块
用户组管理
名称 |
值 |
name |
用户名 |
state |
创建或删除 |
groups |
用户组 |
uid |
用户UID |
password |
用户密码 |
home |
指定家目录,需要createhome 为yes |
createhome |
是否创建家目录 |
system |
是否创建为系统用户 |
remove |
当state=absent 时,删除家目录 |
shell |
指定用户shell环境 |
创建用户组
- name: Create a group named acai with a GID of 2016 group: gid: 2016 name: acai
- name: Create user test_user and add to acai group user: name: test_user groups: acai state: present
|
lineinfile模块
该模块用于从一个文件中搜索一行,确保该行存在或删除该行。lineinfile模块主要用于改变一个文件的一行。如果想要改变文件中相似的多行,可以使用replace模块。如果想要插入/更新/删除一个行块,可以使用blockinfile模块。
参数 |
描述 |
backup |
用于创建一个包含时间戳信息的备份文件。以便在错误的修改了文件的时候,能够找回原始的文件。 |
create |
与state=present一起使用。如果指定了这个参数,当要修改的文件不存在的时候,会创建它。否则会报错。 |
dest |
要修改的文件 |
insertbefore |
当regexp不匹配文件中的任何行的时候,会将line参数所指定的行,插入到insertbefore所指定的正则表达式匹配的行中的最后一行的前面,当insertbefore所指定的正则表达式不匹配任何行时,会插入到文件的末尾 |
insertafter |
与insertbefore类似,不同的是,insertbefore会将新行插入到其所指定的正则表达式匹配的行中的最后一行的前面,而insertafter是插入到后面。 |
line |
要插入或者替换的行。如果设置了backrefs参数,那么line中可以包含 位置分组 或 命名分组,lineinfile模块会使用regexp捕获的分组填充它们。 |
mode |
用来指定文件的权限,比如mode=0644 或 mode=’a+x’ |
owner 和 group |
用来指定文件的属主 和 属组 |
regexp |
用于搜索文件中的每一行的正则表达式。对于state=present,这个正则表达式所匹配的行中的最后一行会被替换;对于state=present,会删除所有匹配的行。 |
state |
用于设置 新增或替换一行,还是删除行 |
删除password文件开头为root的行
- name: delete line for root in password lineinfile: dest: '/etc/password' state: absent regexp: '^root'
|
关闭selinux
- name: disable selinux lineinfile: dest: '/etc/selinux/config' regexp: '^SELINUX=.*' line: 'SELINUX=disabled' state: present
|
添加hosts解析
- name: add hosts lineinfile: dest: '/etc/hosts' line: '172.16.1.10\ttest' state: present
|
get_url模块
下载文件到/tmp
- name: download file to /tmp directory get_url: url: 'http://mirrors.aliyun.com/centos/timestamp.txt' dest: '/tmp'
|
mysql_db模块
导入sql
- name: import register.sql for harbor mysql_db: name: "{{ mysql_db_name }}" state: import target: /tmp/registry.sql when: inventory_hostname in groups['harbor_nodes'][0]
|
创建数据库
- name: create database for harbor mysql_db: name: "{{ mysql_db_name }}" state: present run_once: yes
|
mysql_user模块
参数 |
可选值 |
描述 |
append_privs |
|
用户添加权限,不会覆盖原有权限 |
config_file |
|
指定user和passwd配置文件 |
connect_timeout |
|
连接数据库超时时间 |
host |
|
mysql的IP或是主机名称 |
login_host |
|
登陆mysql的主机名称 |
login_password |
|
登陆mysql的用户密码 |
login_user |
|
登陆mysql的用户 |
state |
present/absent |
|
name |
|
用户名 |
password |
|
密码 |
priv |
|
访问权限 |
|
|
|
创建mysql远程用户
- name: create harbor remote user for mysql database mysql_user: name: "{{ mysql_harbor_user }}" password: "{{ mysql_harbor_pass }}" host: "%" priv: "{{ mysql_db_name }}.*:ALL" append_privs: "yes" run_once: yes
|
systemd模块
systemd模块就是使用systemctl来管理centos7系统服务
参数名称 |
可选值 |
备注 |
name |
yes/no |
服务名称 |
enabled |
yes/no |
是否设置开机启动 |
state |
started、stopped、restarted、reload |
对服务执行的动作 |
service模块
参数 |
描述 |
enabled |
是否开机启动 yes/no |
name |
服务名称 |
runlevel |
运行级别 |
sleep |
如果执行了restarted,在stop和start之间沉睡几秒 |
state |
对服务执行启动,停止,重启,重新加载等 |
启动apache服务设置开机自动启动
- name: startup apache service service: name: httpd state: started enabled: yes
|
stat模块
索引文件、目录的状态
返回值
参数 |
可选项 |
备注 |
atime |
|
返回最后一次访问文件的时间 |
attributes |
|
返回属性 |
exists |
|
路径是否存在 |
ischr |
|
返回是否为设备 |
isdir |
|
返回是否为目录 |
islnk |
|
返回是否为链接 |
isuid |
|
返回uid |
mode |
|
返回文件权限 |
mtime |
|
返回最后修改时间 |
readable |
|
返回是否有可读权限 |
size |
|
返回文件字节大小 |
template模块
template使用了Jinjia2格式作为文件模版,进行文档内变量的替换的模块。
参数 |
可选项 |
备注 |
backup |
yes/no |
|
src |
|
文件源地址 |
dest |
|
文件目的地址 |
mode |
|
设置远程节点上的template文件权限。类似Linux中chmod的用法 |
owner |
|
设置远程节点上的template文件所属用户 |
group |
|
设置远程节点上的的template文件的所属用户组 |
创建chrony.conf.j2模版
allow {{ cluster_network }}
|
在templates目录下创建chrony.conf.j2文件并替换以下变量
allow
代表是由ansible引入的一个变量,该变量可在group_vars目录下文件中调用,eg:
cat group_vars/all ---
cluster_network : 192.168.1.0/24
|
当ansible执行该任务后,被copy的配置文件会被替换为cluster_network变量的值。
user模块
参数 |
可选值 |
描述 |
append |
yes/no |
|
comment |
|
|
create_home |
yes/no |
|
expires |
|
|
wait_for模块
当我们启动一个服务或是执行某条shell的时候,你想确认下服务或是shell是否执行成功。这时候就需要使用wait_for模块。
参数 |
描述 |
connect_timeout |
在下一个事情发生前等待连接的时间 |
delay |
延时 |
host |
执行模块的host |
path |
当一个文件存在时,继续下一步 |
port |
监测端口 |
state |
started 当对象是端口的时候确保端口是打开的;对象是文件时,确认文件是否存在 stopped 对象是端口的时候确保端口是关闭的; present 对象是文件的时候确保文件是存在的; absent 对象是文件的时候确保文件是不存在的 |
|
|
检查某个节点端口是否正常访问
- name: check nginx port for yum repo wait_for: port: "{{ yum_port }}" host: "{{ deploy_node_address }}" state: started timeout: 10 run_once: yes
|
yum模块
参数 |
描述 |
enablerepo |
激活yum源 |
name |
要操作软件包的名字,也可以传递一个url和本地路径 |
state |
定义软件包的状态(present:安装、absent:删除、latest:安装最新的) |
安装apache服务
- name: install apache yum: name: httpd state: present
|
ansible filter
当涉及到 Ansible 的 Filter 时,有一些内置 Filter 是很常用的。下面是一些常见的 Filter:
Filter 名称 |
作用 |
abs |
返回一个数字的绝对值 |
basename |
返回一个路径的最后一部分 |
bool |
返回一个布尔值 |
default |
如果变量未定义或为空,则返回一个默认值 |
difference |
返回两个列表之间的差异 |
filesizeformat |
格式化字节大小,如 1MB |
first |
返回列表的第一个元素 |
from_json |
将 JSON 字符串转换为 Python 对象 |
from_yaml |
将 YAML 字符串转换为 Python 对象 |
join |
将列表中的元素连接为一个字符串 |
last |
返回列表的最后一个元素 |
length |
返回列表、字典或字符串的长度 |
lower |
将字符串转换为小写 |
max |
返回列表中的最大值 |
min |
返回列表中的最小值 |
random |
返回列表中的随机元素 |
reject |
返回列表中不满足指定条件的元素 |
replace |
将字符串中的一个子串替换为另一个子串 |
select |
返回列表中满足指定条件的元素 |
sort |
对列表进行排序 |
sum |
返回列表中所有数字的和 |
to_json |
将 Python 对象转换为 JSON 字符串 |
to_yaml |
将 Python 对象转换为 YAML 字符串 |
trim |
移除字符串开头和结尾的空白字符 |
union |
返回两个列表的并集 |
unique |
返回一个列表中的唯一元素 |
upper |
将字符串转换为大写 |
urlencode |
对字符串进行 URL 编码 |
version |
将版本号字符串转换为可比较的形式 |
将字符串开头和结尾的空格去除
- name: debug k8s cluster system id debug: msg: "k8s cluster System Id is: {{ System_Id.stdout|trim }}"
|
替换字符串,将192.168.1.1替换为192-168-1-1
- name: get kube etcd pem for prometheus command: cat /etc/kubernetes/ssl/kube-etcd-{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] | regex_replace('\.', '-') }}.pem register: kube_etcd_pem when: inventory_hostname in groups['cp_control'][0]
|
ansible判断
判断类型 |
描述 |
when |
在任务运行之前用于评估是否需要运行任务 |
assert |
如果表达式不为真,则任务失败 |
failed_when |
当一个任务的返回码符合给定的条件时,将标记任务失败 |
changed_when |
如果命令的执行返回值符合给定的条件,则将结果标记为已更改 |
when
when条件判断:只条满足when的条件时才执行对应的tasks
--- - hosts: master tasks: - name: test host IP address debug: 'msg="{{ ansible_default_ipv4.address }}"' when: ansible_default_ipv4.address == "192.168.1.11"
|
判断chrony配置文件是否存在,如果存在则跳过;如果不存在则复制配置文件
- name: chrony check is exits stat: path=/etc/chrony.conf.bak register: chrony_conf_status - name: backup chrony conf copy: src=/etc/chrony.conf dest=/etc/chrony.conf.bak when: chrony_conf_status.stat.exists == False
|
定义变量判断:
cloudkitty_hashmaps: "{{ cloudkitty_hashmaps_cp if deloy_cp_region is defined and deploy_cp_region|bool else cloudkitty_hashmaps_os }}
|
判断regsiter变量的值,如果两个节点及以上register变量相等,则执行task
- name: mariadb galera cluster first startup shell: galera_new_cluster >>/dev/null ignore_errors: yes when: {{ safe_to_bootstrap.stdout }}|int >=1
|
判断变量cattle_system_ns_state的结果如果为failed则执行create ns cattle-system任务
- name: check cattle-system namespace is exists shell: kubectl get ns|grep cattle-system register: cattle_system_ns_state ignore_errors: True run_once: True
- name: create namespace cattle-system shell: kubectl create namespace cattle-system when: cattle_system_ns_state | failed run_once: True
|
changed_when
先执行task,并对task返回的值进行判断,当满足changed_when指定的条件时说明是执行成功的
--- - hosts: master tasks: - name: check hostname shell: "hostname" changed_when: ansible_hostname == "master01"
|
ansible循环
在Ansible中,循环通常使用loop
关键字实现。Ansible支持多种类型的循环,包括:
循环类型 |
描述 |
loop |
基本循环,用于遍历列表、字典等 |
until |
直到某些条件满足之前循环执行 |
while |
在某些条件满足的情况下循环执行 |
with_* |
与变量一起使用的特殊循环,例如with_items 、with_dict 、with_file 等 |
另外,Ansible还支持嵌套循环和循环控制语句(如break
、continue
等),可以根据具体情况选择使用。
ansible变量
类型 |
描述 |
字符串 |
由单引号、双引号或反斜杠括起来的字符序列,可以包含变量或表达式 |
数字 |
整数或浮点数 |
布尔值 |
True 或 False |
列表 |
由方括号括起来的元素列表,元素可以是任何类型的值,用逗号分隔 |
字典 |
由大括号括起来的键值对,用冒号分隔,键和值之间用等号或空格分隔,键值对之间用逗号分隔 |
None |
表示空值或未定义值的特殊类型 |
Playbooks
Playboos:简单说就是定义一个配置文件,文件中写入你需要安装的服务,配置文件,变量等信息。可以安装事先定义好的机制完成一个任务
yml脚本语法
- 文件开头用
---
开始
- 在冒号之后,必须存在一个空格
- name和模块名称必须对齐
- hosts和tasks必须对齐
- 在书写key和value的时候,不能存在空格
playbooks组成部分
目录名称 |
作用 |
tasks |
包含当前角色的所有任务 |
handlers |
包含当前角色的所有处理程序 |
templates |
包含当前角色需要使用的模板文件 |
files |
包含当前角色需要使用的一些文件,这些文件通常是不需要经过模板处理的,如一些二进制文件等 |
vars |
包含当前角色的变量定义文件 |
defaults |
包含当前角色的默认变量文件 |
meta |
包含当前角色的依赖信息,如依赖哪些其它角色等 |
library |
包含当前角色需要使用的Ansible模块,这些模块通常是需要自行编写的 |
module_utils |
包含当前角色需要使用的Ansible模块工具库 |
lookup_plugins |
包含当前角色需要使用的Ansible查找插件 |
filter_plugins |
包含当前角色需要使用的Ansible过滤器插件 |
test |
包含当前角色需要使用的Ansible测试模块 |
role_name.yml |
包含当前角色的主任务文件,可以是多个yml文件,用来定义角色执行时的流程控制 |
ansible模块开发
检查TCP端口
from ansible.module_utils.basic import AnsibleModule import socket import logging
def main(): module = AnsibleModule( argument_spec=dict( host=dict(type='str', required=True), port=dict(type='int', required=True), timeout=dict(type='int', default=1), ), supports_check_mode=False, no_log=True )
host = module.params['host'] port = module.params['port'] timeout = module.params['timeout']
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(timeout) result = {} try: sock.connect((host, port)) result['msg'] = "Port {} is open on host {}".format(port, host) result['rc'] = 0 result['status'] = 'ok' except socket.error as e: result['msg'] = "Port {} is not open on host {}: ".format(port, host) result['rc'] = 0 result['status'] = 'failed' finally: sock.close()
module.exit_json(**result)
if __name__ == '__main__': main()
|
调用第三方接口处理数据
import requests import hashlib import logging from ansible.module_utils.basic import AnsibleModule
logging.basicConfig(level=logging.DEBUG,format="%(asctime)s %(levelname)s %(message)s")
class API: pass
def main(): module = AnsibleModule( argument_spec=dict( host=dict(type='str', required=True), login_user=dict(type='str', required=True), login_pass=dict(type='str', required=True), name=dict(type='str', required=True) ), supports_check_mode=False, no_log=True ) host = module.params['host'] login_user = module.params['login_user'] login_pass = module.params['login_pass'] name = module.params['name'] api = API(host, login_user, login_pass) data = api.get_conf(name) result = {} result['stdout'] = data module.exit_json(**result)
if __name__ == '__main__': main()
|
案例
生成随机数
- name: Generate Random seed set_fact: seed: "{{ ansible_nodename }}-{{ ansible_host }}"
- name: Generate GID NUM set_fact: gid_num: "{{ range(100) | random(seed=seed) }}"
- name: Format GID set_fact: gid: "{{ 10000 + '{:04d}'.format(gid_num|int)|int }}"
- name: Generate Random GID debug: msg: "gid: {{ gid }}"
|
在配置文件中循环生成主机组ip
{% for host in groups['all']%} {{ hostvars[host]['ansible' + hostvars[host]['api_interface']]['ipv4']['address'] }}:2181{% if not loop.last %},{% endif %}{% endfor %}
|
获取节点IP地址
{{ hostvars[inventory_hostname]['ansible_' + hostvars[inventory_hostname]['api_interface']['ipv4']['address']] }}
|