安装及配置 ansible

Ansilbe 管理员节点和远程主机节点通过 SSH 协议进行通信。所以 Ansible 配置的时候只需要保证从 Ansible 管理节点通过 SSH 能够连接到被管理的远程的远程节点。
每一台被 ansible 远程管理的主机,都需要配置基于 key 的 ssh 连接

安装 ansible

  1. # Readhat/CentOS Linux, Ansible 目前放在 epel 源中
  2. yum install -y epel-release
  3. yum install -y ansible
  4. # Mac
  5. brew install ansible

安装 ansible 在管理节点,那么,需要配置管理节点->远程主机之间基于 key 的免密访问:

  1. # 生成 ssh key,如果你之前配置过 Git,那么已经生成过一个了了
  2. ssh-keygen -t rsa -C "649168982@qq.com"
  3. # 拷贝 ssh key 至远程主机,下次管理节点就可以免密访问远程主机了
  4. ssh-copy-id remote_user@remote_server
  5. # ssh 的时候不会提示是否保存 key
  6. ssh-keyscan remote_servers >> ~/.ssh/known_hosts

这时候在管理节点登录远程节点时,就不会输入密码也不会提醒你存储key,之前写过一篇文章,记录设置免密登录的文章Linux 双向 SSH 免密登录

  1. ssh remote_user@remote_server

Host Inventory 主机清单

Host Inventory 是配置文件,用来告诉Ansible需要管理哪些主机。并且把这些主机根据按需分类。
可以根据用途分类:数据库节点,服务节点,nginx 节点、构建机器节点
默认的配置文件在:/etc/ansible/hosts,可以通过 -i 参数指定配置文件的,参考问题:

  1. 环境变量 ANSIBLE_CONFIG 所指向的位置
  2. 当前目录下的 ansible.cfg
  3. HOME 目录下的配置文件 ~/.ansible.cfg
  4. /etc/ansible/ansible.cfg

本文 ansible.cfg:

  1. [defaults]
  2. # 这个参数表示主机清单 inventory 文件的位置
  3. inventory = ./inventory
  4. # 并发连接数,默认为5
  5. forks = 5
  6. remote_user = root
  7. # 设置默认执行命令的用户,~~sudo_user~~ 参数在 ansbile 2.8 版本将会作废的
  8. become_user = root
  9. #指定一个存储ansible日志的文件(默认不记录日志)
  10. log_path = ./ansible.log

指定用户名除了在 ansible.cfg 中指定 remote_user 之外,还可以:

  • inventory 文件中指定 ansible_useransible_ssh_user
  • 使用 -u 参数指定用户名

更多 ansbile.cfg 的配置参考链接

inventory 文件

  1. inventory: ['ɪnv(ə)nt(ə)rɪ] 美 ['ɪnvəntɔri]
  2. n. 存货,存货清单;详细目录

Ansible 可同时操作属于一个组的多台主机,组和主机之间的关系通过 inventory 文件配置。
inventory:

  1. [centos]
  2. 192.168.3.43
  3. [centos:vars]
  4. ansible_ssh_user=root
  • 方括号[]中是组名,用于对主机进行分类,便于对不同主机进行个别的管理
  • 一个主机可以属于不同的组
  • [组名:vars] 定义了「组的变量」

拓展:
可以把一个组作为另一个组的子成员,以及分配变量给整个组使用. 这些变量可以给 /usr/bin/ansible-playbook 使用,但不能给 /usr/bin/ansible 使用

  1. [atlanta]
  2. host1
  3. host2
  4. [raleigh]
  5. host2
  6. host3
  7. [southeast:children]
  8. atlanta
  9. raleigh

Inventory 参数可以控制 ansible 与远程主机的交互方式:

  1. ansible_ssh_host
  2. 将要连接的远程主机名.与你想要设定的主机的别名不同的话,可通过此变量设置.
  3. ansible_ssh_port
  4. ssh端口号.如果不是默认的端口号,通过此变量设置.
  5. ansible_ssh_user
  6. 默认的 ssh 用户名
  7. ansible_ssh_pass
  8. ssh 密码(这种方式并不安全,我们强烈建议使用 --ask-pass SSH 密钥)
  9. ansible_sudo_pass
  10. sudo 密码(这种方式并不安全,我们强烈建议使用 --ask-sudo-pass)
  11. ansible_sudo_exe (new in version 1.8)
  12. sudo 命令路径(适用于1.8及以上版本)
  13. ansible_connection
  14. 与主机的连接类型.比如:local, ssh 或者 paramiko. Ansible 1.2 以前默认使用 paramiko.1.2 以后默认使用 'smart','smart' 方式会根据是否支持 ControlPersist, 来判断'ssh' 方式是否可行.
  15. ansible_ssh_private_key_file
  16. ssh 使用的私钥文件.适用于有多个密钥,而你不想使用 SSH 代理的情况.
  17. ansible_shell_type
  18. 目标系统的shell类型.默认情况下,命令的执行使用 'sh' 语法,可设置为 'csh' 'fish'.
  19. ansible_python_interpreter
  20. 目标主机的 python 路径.适用于的情况: 系统中有多个 Python, 或者命令路径不是"/usr/bin/python",比如 \*BSD, 或者 /usr/bin/python
  21. 不是 2.X 版本的 Python.我们不使用 "/usr/bin/env" 机制,因为这要求远程用户的路径设置正确,且要求 "python" 可执行程序名不可为 python以外的名字(实际有可能名为python26).
  22. ansible_python_interpreter 的工作方式相同,可设定如 ruby perl 的路径....

参考

  1. 使用Ansilbe命令行管理主机(Ad-hoc command)
  2. 使用Ansilbe脚本语言管理主机(脚本语言Playbook)

本文就先从 ad-hoc 方式开始学习。
ansible -h 中将命令行工具叫做 Ad-Hoc Commands,格式是:

  1. Usage: ansible <host-pattern> [options]

ad hoc——临时的,在ansible中是指需要快速执行,并且不需要保存的命令。说白了就是执行简单的命令——一条命令。对于复杂的命令后面会说playbook。关于命令中使用的模块,之后专门详细再做功课,这里先简单意会入门吧。
我的目录初始化时的组成:

  1. .
  2. ├── ansible.cfg
  3. └── inventory

这里的 host-pattern 既可以是一个具体的主机 IP,也可以是 inventory 中的组名。
常用的命令选项示例:

  • ansible all --list-hosts 列出所有主机的 IP,这里的 all 不是组名,而是只 inventory 中所有的主机

在进入下面的小节学习相关模块简单用法之前,咱们先学习一个命令,这对于今后的使用很有帮助:
Copy

  1. $ ansible-doc <module_name>

批量执行 ssh 授信 - authorized——key 模块

在未进行批量授信之前,可以使用 --ask-pass 参数来进行认证,例如:ansible all -m ping --ask-pass。但是这样并不方便,可以通过将 ansible server 机器上的公钥复制到远程主机 ~/.ssh/authorized_keys 文件中,实现免密登录。机器很多时,这样做就比较麻烦了,可以用如下模块实现:

  1. ansible centos -m authorized_key -a "user=root key='{{ lookup('file', '/Users/michael/.ssh/id_rsa.pub') }}' path=/root/.ssh/authorized_keys manage_dir=yes" --ask-pass

检查 ansbile 安装环境 - ping 模块

ping 模块用于测试 ansible server 到 inventory 主机的连通性,这是官网的介绍ping – Try to connect to host, verify a usable python and return pong on success

  1. $ ansible -i inventory centos -m ping -u root
  2. 192.168.3.43 | SUCCESS => {
  3. "changed": false,
  4. "ping": "pong"
  5. }
  • -i 指定了 inventory 文件,后面要接着目标主机名,不然会报错
  • -m 后面接使用的 module
  • -u 只以什么用户登录到目标主机

下面实例中,没有加目标主机名,就报错了:

  1. ansible -i inventory centos -m ping -u root
  2. ERROR! Missing target hosts

下面错误示例,指定的用户名 hh 无法免密登录到目标主机:
Copy

  1. $ ansible -i inventory centos -m ping -u hh
  2. 192.168.3.43 | UNREACHABLE! => {
  3. "changed": false,
  4. "msg": "Failed to connect to the host via ssh: hh@192.168.3.43: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).\r\n",
  5. "unreachable": true
  6. }

此时,我在这篇文章中看到了这样的用法也可以指定登录远程主机的用户名,并且,优先级比 -u 或者在 ansible.cfg 中指定都高:

  1. [centos]
  2. 192.168.3.43
  3. [centos:vars]
  4. ansible_ssh_user=michael

在远程主机执行命令

command 模块

command模块为ansible默认模块,不指定-m参数时,使用的就是command模块;comand模块比较简单,常见的命令都可以使用,但其命令的执行不是通过shell执行的,所以,像这些 “<”, “>”, “|”, and “&”操作都不可以,当然,也就不支持管道;
在远程主机上执行命令 pwd

  1. $ ansible centos -a 'pwd'
  2. 192.168.3.43 | CHANGED | rc=0 >>
  3. /home/michael

缺点:不支持管道,就没法批量执行命令

  • 这里没有再用 -i 参数指定 inventory 文件,因为我运行 ansible --version 时,看到了它识别到了我当前目录下的 ansible.cfg 文件
  • centos 就是必不可少的参数 host-pattern,让我们复习一下用法 ansible <host-pattern> [options]
  • -a/--args 模块参数

    shell模块

    使用shell模块,在远程命令通过/bin/sh来执行;所以,我们在终端输入的各种命令方式,都可以使用; 但是我们自己定义在.bashrc/.bash_profile中的环境变量shell模块由于没有加载,所以无法识别;
    如果需要使用自定义的环境变量,就需要在最开始,执行加载自定义脚本的语句;
    对shell模块的使用可以分成两块:
    1.如果待执行的语句少,可以直接写在一句话中:

    1. $ ansible all -m shell -a ". ~/.bashrc;ps -fe |grep sa_q"
    2. 192.168.3.43 | CHANGED | rc=0 >>
    3. root 2849 2844 2 14:58 pts/0 00:00:00 /bin/sh -c . ~/.bashrc;ps -fe |grep sa_q
    4. root 2866 2849 0 14:58 pts/0 00:00:00 grep sa_q

    2.如果在远程待执行的语句比较多,可写成一个脚本,通过 copy 模块传到远端,然后再执行;但这样就又涉及到两次ansible调用;对于这种需求,ansible已经为我们考虑到了,script模块就是干这事的。

    script模块

    使用 scripts 模块可以在本地写一个脚本,在远程服务器上执行(远程服务器不需要python环境):

    1. ansible all -m script -a "./test-20190224.sh"
  • test-20190224.sh 位于当前目录下,表示在所有的远程主机上执行 test.sh 脚本,省略了先把文件复制过去的步骤

参考

  • 简书-macos环境下ansible学习笔记

    文件传输 - copy 模块

    拷贝本地文件 /Users/michael/Code/05-Github-Own/ansible-learn/file_mbp.txt 至远程主机的 /tmp 目录下:

    1. ansible centos -m copy -a "src=//Users/michael/Code/05-Github-Own/ansible-learn/file_mbp.txt dest=/tmp/ [owner=root group=root mode=0755]"`
    2. ansible centos -m copy -a "src=//Users/michael/Code/05-Github-Own/ansible-learn/file_mbp.txt dest=/tmp/centos.txt [owner=root group=root mode=0755]"

    上面 []中的内容表示非必须。这个 copy 模块挺智能,如果目标没有写文件名,那么传过去的文件就同名,如果自定义了文件名,就进行了重命名:

    安装包 - yum 模块

    1. $ ansible centos -m yum -a "name=lrzsz state=latest"
    2. 192.168.3.43 | SUCCESS => {
    3. "ansible_facts": {
    4. "pkg_mgr": "yum"
    5. },
    6. "changed": false,
    7. "msg": "",
    8. "rc": 0,
    9. "results": [
    10. "All packages providing lrzsz are up to date",
    11. ""
    12. ]
    13. }
  • yum 模块来安装软件包

  • name 指定安装的软件包的名字
  • state指定安装软件包时的行为,它有几个选项值,
    • installed/present 它俩是等价的,表示如果远程主机上有这个包了,则不重新安装了;
    • latest 顾名思义,会去安装最新版的,这个对于生产是比较危险的,有可能因此破坏了生产的环境

Yum 模块

state 值得具体区别,参考了这个问题:

  • What is the difference between two “state” option values, “present” and “installed”, available in Ansible’s yum module?

    添加用户 - user 模块

    1. ansible centos -m user -a "name=foo password=<crypted password here>"
    2. //移除用户
    3. ansible all -m user -a 'name=foo state=absent'

    下载 Git 仓库 - git 模块

    1. $ ansible centos -m git -a "repo=https://github.com/Michael728/my-config-files.git dest=/tmp/my-config-files version=HEAD"
    2. 192.168.3.43 | CHANGED => {
    3. "after": "6faf55b17a1d7c25dfda6f6197839deaa2aa2bd5",
    4. "before": null,
    5. "changed": true
    6. }
  • 使用 git 模块下载了代码库默认分支的最新版本

  • repo 是必选参数
  • dest 也是必选参数,指定代码库的下载路径

代码库地址除了选择用 https 链接之外,还可以用 ssh 地址,事实上,公司中大多使用 ssh 地址会更方便:

  1. ansible centos -m git -a "repo=git@github.com:Michael728/my-config-files.git dest=/tmp/my-config-files version=HEAD accept_hostkey=yes"

远程主机首次使用 git clone 命令时,是需要手动接受 knows_hosts 的,这时候加上 accept_hostkey=yes 可以避免出错
参考:

  • git – Deploy software (or files) from git checkouts

    启动服务 - service 模块

    1. # 启动服务
    2. ansible centos -m service -a "name=httpd state=started"
    3. # 重启服务,效果类似 stopped+started,如果服务已经停止的,执行完,会重启
    4. ansible centos -m service -a "name=httpd state=restarted"
    5. # 重载服务,不会中断服务,如果服务之前未启动,那么会启动服务,如果启动了不一定会使用新的配置文件,还是推荐重启
    6. ansible centos -m service -a "name=httpd state=reloaded"
    7. # 停止服务
    8. ansible centos -m service -a "name=httpd state=stopped"

    state 有几个可选值(Choices: reloaded, restarted, started, stopped)[Default: (null)]

  • started'/stopped` are idempotent[幂等] actions that will not run commands unless necessary.

  • restarted will always bounce the service.
  • reloaded will always reload. At least one of state and enabled are required. Note that reloaded will start the service if it is not already started, even if your chosen init system wouldn’t normally.

参考

  • Nginx reload和restart区别

    并行执行重启

    1. $ ansible centos -a "/sbin/reboot" -f 10
  • -f 参数会 fork 出 10 个子进程,以并行的方式

这个命令虽然显示是出现问题了,但是我发现虚拟机确实重启了:

  1. $ ansible all -a "reboot" -f 6
  2. 192.168.3.43 | UNREACHABLE! => {
  3. "changed": false,
  4. "msg": "Failed to connect to the host via ssh: Shared connection to 192.168.3.43 closed.\r\n",
  5. "unreachable": true
  6. }

查看远程主机的全部系统信息 - setup 模块#

  1. ansible all -m setup
  2. # 通过 filter 获取某一个 fact 变量
  3. ansible all -m setup -a 'filter=ansible_*mb'

总结

可以发现,ansible Ad-Hoc 的命令使用比较简单,适用于不复杂的场景,如果需要实现复杂的任务,那么就需要通过 ansible-playbook 的方式执行了,下一篇文章中学习它。

参考

service 模块的 restarted reload 区别?