中文文档
原理 Ansible的名字来自小说<安德的游戏>是一个跨越时空的通讯工具
Ansible本质上只是一个框架,用python开发,实际通过它的库实现功能,其中有三个关键库:
Paramiko: Python对ssh的实现 PyYaml: 解析和生成yaml jinjia2: 用于模板的生成,在使用 template
模块自动生成文件时特别有用 特性:
基于python和ssh,无需agent 安全,基于openssh 幂等
: 一个任务执行一遍和执行n遍效果一样,不会因重复执行而带来意料之外的效果安装使用 1 2 yum -y install epel-release yum -y install ansible
配置ssh –> 定义inventory –> 执行ansible命令/执行playbook
Inventory Ansible管理的主机清单配置文件, 默认地址: /etc/ansible/hosts
ansible可以同时操作一个组的多台主机
组和主机之间的关系通过inventory文件配置
可以同时使用多个inventory文件
INI格式 也支持yaml
格式, 个人觉得ini
更清晰易读.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 server1.example.com[web] server2.example.com server3.example.com[database] db[01:10] .example.com[loadbalancer] lb.example.com:8080 ansible_connection =ssh ansible_ssh_user=myuser
定义属于整个组的变量 1 2 3 4 5 6 7 [atlanta] host1 host2[atlanta:vars] ntp_server =ntp.atlanta.example.comproxy =proxy.atlanta.example.com
把一个组作为另一个组的子成员 1 2 3 4 5 6 7 8 9 10 11 12 [webservers] web1.example.com web2.example.com[dbservers] db1.example.com db2.example.com[linux:children] webservers dbservers
分文件保存变量 ansible>1.4
才有的功能
在inventory文件中保存所有变量并不是最佳实践,以独立文件
的方式保存是更优选择.这些文件应该使用yaml
格式配置.
假设有一个主机名为: football
, 它同时属于两个组: foo
和bar
,那么以下配置文件中的变量都可以为它所用
1 2 3 4 5 # /etc/ansible/group_vars/<group_name> # /etc/ansible/host_vars/<hostname_name> /etc/ansible/group_vars/foo /etc/ansible/group_vars/bar /etc/ansible/host_vars/football
另外,还可以为一个主机一个组创建一个以主机名或组名命名的目录,目录中创建多个文件,目录中的文件的变量都会被读取为主机或组的变量.
1 2 3 # /etc/ansible/group_vars/<group_name>/<any_file_name> /etc/ansible/group_vars/raleigh/db_settings /etc/ansible/group_vars/raleigh/cluster_settings
Tip1: ansible1.2+,group_vars
和host_vars
可以放在inventory目录下/etc/ansible/hosts
或是playbook目录下,其中palybook优先级更高. Tips: 把inventory文件和变量文件都放入git中管理,是推荐做法 inventory参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ansible_ssh_host #将要连接的远程主机名.与你想要设定的主机的别名不同的话,可通过此变量设置. ansible_ssh_port #ssh端口号.如果不是默认的端口号,通过此变量设置. ansible_ssh_user #默认的 ssh 用户名 ansible_ssh_pass #ssh 密码(这种方式并不安全,我们强烈建议使用 --ask-pass 或 SSH 密钥) ansible_sudo_pass # sudo 密码(这种方式并不安全,我们强烈建议使用 --ask-sudo-pass) ansible_sudo_exe (new in version 1.8) #sudo 命令路径(适用于1.8及以上版本) ansible_connection #与主机的连接类型.比如:local, ssh 或者 paramiko. Ansible 1.2 以前默认使用 paramiko.1.2 以后默认使用 'smart','smart' 方式会根据是否支持 ControlPersist, 来判断'ssh' 方式是否可行. ansible_ssh_private_key_file #ssh 使用的私钥文件.适用于有多个密钥,而你不想使用 SSH 代理的情况. ansible_shell_type #目标系统的shell类型.默认情况下,命令的执行使用 'sh' 语法,可设置为 'csh' 或 'fish'. ansible_python_interpreter # 目标主机的 python 路径.适用于的情况: 系统中有多个 Python, 或者命令路径不是"/usr/bin/python",比如 \*BSD, 或者 /usr/bin/python# 不是 2.X 版本的 Python.我们不使用 "/usr/bin/env" 机制,因为这要求远程用户的路径设置正确,且要求 "python" 可执行程序名不可为 python以外的名字(实际有可能名为python26). ansible_[ruby|perl]_interpreter# 与 ansible_python_interpreter 的工作方式相同,可设定如 ruby 或 perl 的路径....
一个配置例子
1 2 3 4 some_host ansible_ssh_port =2222 ansible_ssh_user=manager aws_host ansible_ssh_private_key_file =/home/example/.ssh/aws.pem freebsd_host ansible_python_interpreter =/usr/local/bin/python ruby_module_host ansible_ruby_interpreter =/usr/bin/ruby.1.9 .3
动态Inventory 动态生成或发现目标主机的清单,并不是预先固定写在一个静态文件里面.允许ansible与云服务提供商,虚拟化平台或其他API驱动的服务集成,从而实时获取和更新主机信息.也可以收用kubernetes模块与k8s集群集成.
Ansible ad-hoc命令 其实就是命令行执行ansible命令
1 2 3 4 5 6 7 8 9 10 11 12 ansible --help -a MODULE_ARGS -C #dry run模式,测试 -f FORK #ansible一般用来管控多台主机,此选项定义ansible分批发送管控请求,一次多少台 --list-hosts #列出本次操作会对哪些主机执行,不真正执行 --syntax-check #语法检查 -t TREE #将日志输出到指定目录 -u UESERNAME #指明使用哪个用户名连接目标主机 -b,--become #sudo切换root --become-user=USERNAME #指定sudo的runas用户,默认root -K #提示输入sudo时的口令 -k #提示输入ssh连接密码
用法
1 ansible HOST-PATTERN -m MOD_NAME -a MOD_ARGS -f FORKS
Pattern PATTERN
是指如何指定要操作的主机,可以是主机名,也可以是组名,除此以为还有很多高级写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 # invertory内的所有主机 all *# IP或多个主机名(":" 分割),支持"*" 通配 one.example.com one.example.com:two.example.com 192.168.1.50 192.168.1.*# 多个组(":" 分割) webservers:dbservers# 组排队 webservers:!phoenix # 执行的主机必须隶属webservers组但同时不在phoenix组 webservers:dbservers:&staging:!phoenix # ‘webservers’ 和 ‘dbservers’ 两个组中隶属于 ‘staging’ 组并且不属于 ‘phoenix’ 组的机器才执行命令# 还可以通过-e传变量 webservers:!{{excluded}}:&{{required}}# 组编号选主机 webservers[0] webservers[0-25]# 支持通配和正则,通配何以和组混用 one*.com:dbservers ~(web|db).*\.example\.com# --limit 排除目标 ansible-playbook site.yml --limit datacenter2# ansible1.2后支持从文件读取hosts,使用"@" ansible-playbook site.yml --limit @retry_hosts.txt
支持与或非:
模块 -m
指定使用的模块,-a
指定模块参数.ansible的具体功能都由这些模块提供.下面列举一些常用模块和常用参数.
1 2 ansible-doc -l #列出可用模块 ansible-doc -s MOD_NAME #列出模块可用参数和playbook片段
group 创建删除组
args
:
gid
*name: 自定义组名
state
present: 存在,即创建 absent: 不存在,即删除 system: 是否创建系统用户组
1 ansible all -m group -a "gid=3000 name=mygrp state=present system=no"
user 创建删除用户
args
:
copy 以并行的方式同时 SCP 大量的文件到多台机器
args
:
*dest: 目标路径
*src:源路径,支持相对/绝对地址
源是目录,默认会递归复制
源以”/“结尾,只复制目录内的内容
源不以”/“结尾,则把目录本身也复制过去
owner: 属主
group: 属组
mode: 权限
remote_src: 从远程源复制到主机
1 2 3 ansible all -m copy -a "src=/etc/fstab dest=/tmp/fstab.ansible mode=600" ansible all -m copy -a "src=/etc/pam.d/ dest=/tmp/" ansible all -m copy -a "content='hi there\n' dest=/tmp/hi.txt"
fetch 从远端复制文件到本机
file 创建或删除文件/目录
command 执行给定的命令,但不会使用目标主机
shell环境,因此不支持shell特有的语法和通配符.
command模块不是幂等的
,重复执行会失败.
比如你用command模块创建文件夹,多次执行,就会失败.command模块有相关的参数比如create
或remove
去处理这种情况.
当然也可以选择其他幂等的module去处理有不幂等风险的操作.
shell 使用目标主机的shell环境
执行命令,这意味着它支持管道,通配,重定向等shell特有的操作,以及可以使用环境变量.
1 ansible all -m shell -a "echo mageedu | passwd --stdin testuser" # 用shell就会成功
cron 设定周期性任务,实际上就是操作目标主机的crontab
name: 指定任务名称, 默认为none
day
hour
job
state=present | absent
1 2 ansible all -m cron -a "minute=*/3 job='/usr/sbin/update 172.16.0.1 &> /dev/null'" ansible all -m cron -a "minute=*/3 job='/usr/sbin/update 172.16.0.1 &> /dev/null' name=None state=absent"
yum/dnf 包管理程序
一般redhat 8及以上使用dnf
redhat 8以下使用yum
service 通用的服务管理模块,它会检测系统使用的是哪一种服务管理方式(sysvinit, upstart, systemd等),并尝试用正确的方式来广利服务.
当然,针对不同的服务管理方式,ansible都有对应模块,如果不清楚,或者通用功能就行,就使用service就好了
script 复制本地脚本到目标主机运行,不幂等
配置文件 /etc/ansible/ansible.cfg
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [defaults] host_key_checking = False log_path =/var/log/ansible.log module_name = command
ansible命令执行过程 加载配置文件/etc/ansible/ansible.cfg 加载自己对应的模块文件 通过ansible将模块命令生成临时的py文件并将该文件传输到目标主机对应执行用户的$HOME/.ansible/tmp/ansible-tmp-数字/XXX.py 给文件+x 执行并返回结果 删除临时py文件, sleep 0 退出 执行结果:
绿色: 成功应用变更
黄色: 成功但无变更
红色: 失败
ansible-playbook ad-hoc与playbook就类似shell命令与shell脚本的关系.
playbook使用yaml编写.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 - name: Enhanced Playbook Example hosts: web_servers become: yes become_method: sudo remote_user: admin tasks: - name: Install Apache yum: name: httpd state: present notify: - Restart Apache tags: - installation - name: Copy Apache Configuration File copy: src: /path/to/conf/httpd.conf dest: /etc/httpd/conf/httpd.conf notify: - Restart Apache tags: - configuration handlers: - name: Restart Apache service: name: httpd state: restarted tags: - handlers
其实就是命令调用模块改为yaml写法.
notify和handlers notify是触发器,handlers是响应器.
如果notify所在的task没有触发变更,notify所定义的handler也不会被触发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 --- - name: 确保 web 服务正确运行 hosts: web_servers become: true tasks: - name: 将配置文件拷贝到远程服务器 copy: src: /src/webserver.conf dest: /etc/webserver.conf notify: - 重启 web 服务 handlers: - name: 重启 web 服务 service: name: webserver state: restarted
1 2 3 4 # 指执行有这个tag的任务 ansible-playbook install_apache.yml --tags "configuration"# 跳过执行有这个tag的任务 ansible-playbook install_apache.yml --skip-tags "configuration"
常用参数 1 2 3 4 5 6 -C #dry run --syntax-check #语法检查 -t #指定调用对应标签的任务 --list-hosts --list-tags --list-tasks
Variables vars
定义变量,{{var_name}}
调用变量
Fact ansible的setup模块获取的主机自带的系统信息
1 2 3 4 5 6 7 yum -y install facter# 获取本机的系统信息,setup模块获取的也就是这些信息 facter -p# 查看setup模块参数 ansible-doc -s setup# 获取facts并通过filter参数过滤想要的fact ansible all -i inventory.ini -m setup -a 'filter=ansible_*_mb'
这种类型的变量直接调用即可.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 - name: Collect only certain facts hosts: all tasks: - name: Gather only memory related facts setup: filter: ansible_*_mb - name: Print out the total memory debug: msg: "Total memory on this machine is {{ ansible_memtotal_mb }} MB" - name: Check if a directory exists ansible.builtin.stat: path: "/a/specific/path" register: result - name: Display results if directory exists debug: msg: "The directory exists!" when: result.stat.exists
用户自定义 命令行定义
1 ansible-playbook -e pkgname=memcached test.yaml
playbook中定义
1 2 3 vars: - var1: value1 - var2: value2
通过roles传递 通过 Invertory传递 前面有说过Invertory文件可以定义变量,但是要注意跟inventory参数的区分
template模块 主要用来动态生成配置文件.
用于基于Jinja2模板语言处理模板文件。Jinja2模板中可以包含变量和表达式,这些变量将在Ansible运行时根据具体的上下文环境进行替换。template
模块主要用于生成配置文件或其他类型的文本文件,并且将这些文件复制到远程主机上.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 - name: Configure nginx hosts: webservers vars: http_port: 80 max_clients: 200 tasks: - name: Create nginx configuration file from template template: src: /srv/http/nginx.conf.j2 dest: /etc/nginx/nginx.conf owner: root group: root mode: '0644'
task高级用法 条件测试 关键字: when
1 2 3 4 5 6 7 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
, with_item
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 - 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: - group1 - group2 - name: add some users user: name={{ item.name }} group={{ item.group }} state=present with_items: - { name: 'user1' , group: 'group1' }- { name: 'user2' , group: 'group2' }
新版本: loop
,item
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 - name: Add several users ansible.builtin.user: name: "{{ item }} " state: present groups: "wheel" loop: - testuser1 - testuser2 - testuser3 --- - name: Ensure several users have specific properties ansible.builtin.user: name: "{{ item.key }} " state: present groups: "{{ item.value.groups }} " uid: "{{ item.value.uid }} " loop: "{{ users | dict2items }} " vars: users: alice: uid: 1001 groups: "wheel" bob: uid: 1002 groups: "staff"
role 是一种组织playbook的方式,可以把复杂的任务拆分成更小的,可复用的文件.类似python的模块,role也可以让ansible代码更加模块化,更容易管理和服用.
1 2 # 创建role ansible-galaxy init my_role
目录架构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 my_role/ ├── defaults │ └── main.yml # 定义变量默认值 ├── files # 存放role需要复制到目标主机的源文件 ├── handlers │ └── main.yml # 存放handlers任务,只有被notify触发才会执行 ├── meta │ └── main.yml # 定义role的元数据(作者信息, 支持平台, 依赖关系等) ├── README.md # 项目README ├── tasks │ └── main.yml # 定义role要执行的主要任务列表,当角色被引用,这里的任务将会按序执行 ├── templates # jinjia2模板文件 ├── tests # 用于存放测试role的文件 │ ├── inventory # 列出测试用的主机 │ └── test.yml # 测试用playbook (ansible-playbook -i tests/inventory tests/test.yml) └── vars └── main.yml # 定义变量
meta/main.yaml 例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 galaxy_info: author: your_name description: Example role for web server setup company: Your Company (optional) license: MIT (或你选择的其他许可证) min_ansible_version: 2.9 (或其他最小版本要求) platforms: - name: EL versions: - 7 - 8 - name: Ubuntu versions: - xenial - bionic - focal galaxy_tags: - web - server - httpd dependencies: - role: other_role - { role: conditional_role , when: "some_condition|bool" , var1: "value1" , var2: "value2" }
default/main.yaml 列出kv定义变量和默认值即可
1 2 3 nginx_user: www-data nginx_http_port: 80 nginx_configure_default_site: true
handlers/main.yaml 1 2 3 4 5 6 7 8 9 10 11 - name: restart nginx service: name: nginx state: restarted become: true - name: reload nginx service: name: nginx state: reloaded become: true
task/main.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 - name: Install nginx apt: name: nginx state: present become: true - name: Configure nginx template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf notify: - restart nginx - name: Ensure nginx is running (and enable it at boot) service: name: nginx state: started enabled: true become: true
vars/main.yaml 1 2 3 4 5 6 7 nginx_version: "1.14.2" nginx_core_module_settings: worker_processes: "auto" worker_connections: 1024 multi_accept: "on" nginx_event_module_settings: use: "epoll"
playbook引用 1 2 3 4 5 6 - hosts: webservers become: yes roles: - common - nginx - application
完整的目录架构 一个完整的playbook+role的典型目录架构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 playbook-directory/ │ ├── ansible.cfg # Ansible 配置文件(可选)不存在则使用默认的/etc/ansible/ansible.cfg ├── inventory # 存储库存信息的文件或目录 (可选) 不存在则使用默认的/etc/ansible/hosts │ ├── group_vars/ │ └── group1 # 组1的变量 │ ├── host_vars/ │ └── hostname # 特定主机的变量 │ ├── roles/ │ ├── role1/ # 第一个 role │ │ ├── defaults/ │ │ │ └── main.yml # role 的默认变量 │ │ ├── handlers/ │ │ │ └── main.yml # role 的 handlers │ │ ├── meta/ │ │ │ └── main.yml # role 的元数据 │ │ ├── tasks/ │ │ │ └── main.yml # role 的任务列表 │ │ ├── templates/ │ │ │ └── template.j2 # Jinja2 模版文件 │ │ └── vars/ │ │ └── main.yml # role 的其他变量 │ │ │ └── role2/ # 第二个 role │ ├── defaults/ │ ├── handlers/ │ ├── meta/ │ ├── tasks/ │ ├── templates/ │ └── vars/ │ ├── site.yml # 主 playbook 文件 ├── webservers.yml # 针对 web 服务器的 playbook └── databases.yml # 针对数据库服务器的 playbook
import_playbook playbook可以通过import来调用,这就有了上面的主playbook
例如site.yml可以这么写
1 2 - import_playbook: webservers.yml - import_playbook: databases.yml
ansible会按次序执行webservers.yml和databases.yml两个playbook
ansible-galaxy 官网
在 Ansible 中,共享和使用别人创建的 roles 是一个常见的做法,用于促进可重用性和合作。Ansible 的主要方式是通过 Ansible Galaxy,它是一个共享和寻找 Ansible role 的中心化平台。
1 ansible-galaxy install geerlingguy.apache -p /path/to/role
requirements.yaml 与python一样,playbook目录可以创建一个requirements.yaml
,里面列出需要的role,通过命令一次安装
1 ansible-galaxy install -r requirements.yml
1 2 3 - src: geerlingguy.apache version: 1.0 .0