ansible笔记(15):变量(二)

前一篇文章中已经初步的总结了变量的一些使用方法,这篇文章我们继续,只不过,这篇文章所涉及到的内容需要借助两个模块,所以在详细的总结变量的相关使用方法之前,会先描述一下这两个模块的用法。

当我们运行一个playbook时,默认都会运行一个名为”[Gathering Facts]”的任务,前文中已经大致的介绍过这个默认的任务,ansible通过”[Gathering Facts]”这个默认任务收集远程主机的相关信息(例如远程主机的IP地址,主机名,系统版本,硬件配置等信息),其实,这些被收集到的远程主机信息会保存在对应的变量中,当我们想要使用这些信息时,我们可以获取对应的变量,从而使用这些信息。

如果想要查看”[Gathering Facts]”任务收集的信息内容,我们可以借助一个模块:setup模块

当执行playbook时,playbook其实就是自动调用了setup模块从而执行了”[Gathering Facts]”任务,所以我们可以通过手动执行setup模块查看”[Gathering Facts]”任务收集到的信息,示例如下

ansible test70 -m setup

上述ad-hoc命令表示收集test70主机的相关信息,执行上述命令后,远程主机test70的相关信息将会输出到ansible主机的控制台上,返回的信息的格式是json格式,我的返回信息如下。

注:由于返回的信息比较多,此处为了方便示例,我将部分内容删除(或折叠省略)了,所以如下返回信息并不完全,只用于示意。

test70 | SUCCESS =>
{
    "ansible_facts":{
        "ansible_all_ipv4_addresses":[
            "192.168.122.1",
            "192.168.1.106",
            "10.1.1.70",
            "172.16.66.70"
        ],
        "ansible_all_ipv6_addresses":Array[2],
        "ansible_apparmor":Object{...},
        "ansible_architecture":"x86_64",
        "ansible_bios_date":"05/19/2017",
        "ansible_bios_version":"6.00",
        "ansible_cmdline":Object{...},
        "ansible_date_time":Object{...},
        "ansible_default_ipv4":Object{...},
        "ansible_default_ipv6":Object{...},
        "ansible_device_links":Object{...},
        "ansible_devices":Object{...},
        "ansible_distribution":"CentOS",
        "ansible_distribution_file_parsed":true,
        "ansible_distribution_file_path":"/etc/redhat-release",
        "ansible_distribution_file_variety":"RedHat",
        "ansible_distribution_major_version":"7",
        "ansible_distribution_release":"Core",
        "ansible_distribution_version":"7.4.1708",
        "ansible_dns":Object{...},
        "ansible_domain":"",
        "ansible_effective_group_id":0,
        "ansible_effective_user_id":0,
        "ansible_ens33":Object{...},
        "ansible_ens34":Object{...},
        "ansible_ens35":{
            "active":true,
            "device":"ens35",
            "features":{
                "busy_poll":"off [fixed]",
                "fcoe_mtu":"off [fixed]",
                "generic_receive_offload":"on",
                "generic_segmentation_offload":"on",
                "highdma":"off [fixed]",
                "hw_tc_offload":"off [fixed]",
                "l2_fwd_offload":"off [fixed]",
                "large_receive_offload":"off [fixed]",
                "loopback":"off [fixed]",
                "netns_local":"off [fixed]",
                "ntuple_filters":"off [fixed]",
                "receive_hashing":"off [fixed]",
                "rx_all":"off",
                "rx_checksumming":"off",
                "rx_fcs":"off",
                "rx_vlan_filter":"on [fixed]",
                "rx_vlan_offload":"on",
                "rx_vlan_stag_filter":"off [fixed]",
                "rx_vlan_stag_hw_parse":"off [fixed]",
                "scatter_gather":"on",
                "tcp_segmentation_offload":"on",
                "tx_checksum_fcoe_crc":"off [fixed]",
                "tx_checksum_ip_generic":"on",
                "tx_checksum_ipv4":"off [fixed]",
                "tx_checksum_ipv6":"off [fixed]",
                "tx_checksum_sctp":"off [fixed]",
                "tx_checksumming":"on",
                "tx_fcoe_segmentation":"off [fixed]",
                "tx_gre_csum_segmentation":"off [fixed]",
                "tx_gre_segmentation":"off [fixed]",
                "tx_gso_partial":"off [fixed]",
                "tx_gso_robust":"off [fixed]",
                "tx_ipip_segmentation":"off [fixed]",
                "tx_lockless":"off [fixed]",
                "tx_mpls_segmentation":"off [fixed]",
                "tx_nocache_copy":"off",
                "tx_scatter_gather":"on",
                "tx_scatter_gather_fraglist":"off [fixed]",
                "tx_sctp_segmentation":"off [fixed]",
                "tx_sit_segmentation":"off [fixed]",
                "tx_tcp6_segmentation":"off [fixed]",
                "tx_tcp_ecn_segmentation":"off [fixed]",
                "tx_tcp_mangleid_segmentation":"off",
                "tx_tcp_segmentation":"on",
                "tx_udp_tnl_csum_segmentation":"off [fixed]",
                "tx_udp_tnl_segmentation":"off [fixed]",
                "tx_vlan_offload":"on [fixed]",
                "tx_vlan_stag_hw_insert":"off [fixed]",
                "udp_fragmentation_offload":"off [fixed]",
                "vlan_challenged":"off [fixed]"
            },
            "hw_timestamp_filters":[


            ],
            "ipv4":{
                "address":"10.1.1.70",
                "broadcast":"10.1.1.255",
                "netmask":"255.255.255.0",
                "network":"10.1.1.0"
            },
            "ipv6":[
                {
                    "address":"fe80::250:56ff:fe25:5fb0",
                    "prefix":"64",
                    "scope":"link"
                }
            ],
            "macaddress":"00:50:56:25:5f:b0",
            "module":"e1000",
            "mtu":1500,
            "pciid":"0000:02:03.0",
            "promisc":false,
            "speed":1000,
            "timestamping":[
                "tx_software",
                "rx_software",
                "software"
            ],
            "type":"ether"
        },
        "ansible_env":Object{...},
        "ansible_fips":false,
        "ansible_form_factor":"Other",
        "ansible_fqdn":"test70",
        "ansible_hostname":"test70",
        "ansible_interfaces":Array[6],
        "ansible_kernel":"3.10.0-693.el7.x86_64",
        "ansible_lo":Object{...},
        "ansible_local":Object{...},
        "ansible_lsb":Object{...},
        "ansible_lvm":Object{...},
        "ansible_machine":"x86_64",
        "ansible_machine_id":"f6d15ac15f624d3db89e843639a52cc0",
        "ansible_memfree_mb":1121,
        "ansible_memory_mb":{
            "nocache":{
                "free":1467,
                "used":356
            },
            "real":{
                "free":1121,
                "total":1823,
                "used":702
            },
            "swap":{
                "cached":0,
                "free":1023,
                "total":1023,
                "used":0
            }
        },
        "ansible_memtotal_mb":1823,
        "ansible_mounts":Array[2],
        "ansible_nodename":"test70",
        "ansible_os_family":"RedHat",
        "ansible_pkg_mgr":"yum",
        "ansible_processor":Array[6],
        "ansible_processor_cores":2,
        "ansible_processor_count":1,
        "ansible_processor_threads_per_core":1,
        "ansible_processor_vcpus":2,
        "ansible_product_name":"VMware Virtual Platform",
        "ansible_product_serial":"VMware-56 4d 0d 63 80 3f 29 b4-f0 e2 1b 7a ff 01 a5 9e",
        "ansible_product_uuid":"630D4D56-3F80-B429-F0E2-1B7AFF01A59E",
        "ansible_product_version":"None",
        "ansible_python":Object{...},
        "ansible_python_version":"2.7.5",
        "ansible_real_group_id":0,
        "ansible_real_user_id":0,
        "ansible_selinux":Object{...},
        "ansible_selinux_python_present":true,
        "ansible_service_mgr":"systemd",
        "ansible_swapfree_mb":1023,
        "ansible_swaptotal_mb":1023,
        "ansible_system":"Linux",
        "ansible_system_capabilities":Array[37],
        "ansible_system_capabilities_enforced":"True",
        "ansible_system_vendor":"VMware, Inc.",
        "ansible_uptime_seconds":31658,
        "ansible_user_dir":"/root",
        "ansible_user_gecos":"root",
        "ansible_user_gid":0,
        "ansible_user_id":"root",
        "ansible_user_shell":"/bin/bash",
        "ansible_user_uid":0,
        "ansible_userspace_architecture":"x86_64",
        "ansible_userspace_bits":"64",
        "ansible_virbr0":Object{...},
        "ansible_virbr0_nic":Object{...},
        "ansible_virtualization_role":"guest",
        "ansible_virtualization_type":"VMware",
        "gather_subset":Array[1],
        "module_setup":true
    },
    "changed":false
}

返回信息如上,是一个json格式的字符串,为了方便你阅读,ansible已经将格式化后的json信息返回到了控制台中,返回的信息很全面,比如:

“ansible_all_ipv4_addresses”表示远程主机中的所有ipv4地址,从其对应的值可以看出,test70主机上一共有4个ipv4地址。

“ansible_distribution”表示远程主机的系统发行版,从其对应的值可以看出test70主机的系统发行版为centos

“ansible_distribution_version”表示远程主机的系统版本号,从其对应的值与 “ansible_distribution” 的值可以看出test70主机的系统版本为centos7.4

“ansible_ens35″表示远程主机ens35网卡的相关信息,细心如你一定也发现了,我还有两个名为”ens33″和”ens34″的网卡,只不过为了方便示例,这两个网卡的信息被我省略了。

“ansible_memory_mb”表示远程主机的内存配置信息。

返回的信息的确很多,很全面,但是,并不是每一次我们都需要看这么多信息,如果你只是想查看某一类信息,你可以通过关键字对信息进行过滤,比如,我只是想要查看远程主机的内存配置信息,那么我可以使用如下命令

ansible test70 -m setup -a 'filter=ansible_memory_mb'

上述命令表示通过”ansible_memory_mb”关键字对返回信息进行过滤,如你所见,通过setup模块的filter参数可以指定需要过滤的关键字,这样ansible就只会将”ansible_memory_mb”的相关信息返回,返回如下

test70 | SUCCESS => {
   "ansible_facts": {
       "ansible_memory_mb": {
           "nocache": {
               "free": 1467,
               "used": 356
           },
           "real": {
               "free": 1119,
               "total": 1823,
               "used": 704
           },
           "swap": {
               "cached": 0,
               "free": 1023,
               "total": 1023,
               "used": 0
           }
       }
   },
   "changed": false
}

这样就精简很多了,因为精准的返回了你需要的信息,我知道,有的朋友可能跟我一样,记性不好,所以通常记不住准确的关键字,所以我们可以使用通配符,进行相对模糊的过滤,示例如下

asible test70 -m setup -a "filter=*mb*"

上述命令表示返回所有包含mb的关键字对应的信息,返回信息如下

test70 | SUCCESS => {
   "ansible_facts": {
       "ansible_memfree_mb": 1140,
       "ansible_memory_mb": {
           "nocache": {
               "free": 1475,
               "used": 348
           },
           "real": {
               "free": 1140,
               "total": 1823,
               "used": 683
           },
           "swap": {
               "cached": 0,
               "free": 1023,
               "total": 1023,
               "used": 0
           }
       },
       "ansible_memtotal_mb": 1823,
       "ansible_swapfree_mb": 1023,
       "ansible_swaptotal_mb": 1023
   },
   "changed": false
}

其实,除了这些信息以外,我们还能够在远程主机中写入一些自定义的信息,这些自定义信息也可以被setup模块收集到。

那么,我们应该在哪里定义这些信息呢?该怎样定义这些信息呢?

ansible默认会去目标主机的/etc/ansible/facts.d目录下查找主机中的自定义信息,并且规定,自定义信息需要写在以”.fact”为后缀的文件中,同时,这些以”.fact”为后缀的文件中的内容需要是INI格式或者是json格式的。

那么,我们来创建一个测试文件,测试文件路径为test70主机的/etc/ansible/facts.d/testinfo.fact,在文件中写入如下INI格式的信息。

[root@test70 facts.d]# cat testinfo.fact
[testmsg]
msg1=This is the first custom test message
msg2=This is the second custom test message

如上所示,上述内容是一段INI风格的内容,我在”[testmsg]”配置段中配置了两条自定义信息,msg1与msg2。

当然,我们也可以使用json格式进行配置,比如在/etc/ansible/facts.d/testinfo.fact文件中写入如下配置,如下配置与上述配置的效果是相同的,只是书写格式不同。

{
   "testmsg":{
       "msg1":"This is the first custom test message",
       "msg2":"This is the second custom test message"
   }
}

通过上述方式,我们可以在目标主机的本地自定义信息,这些在远程主机本地自定义的信息被称为”local facts”,当我们运行setup模块时,远程主机的”local facts”信息也会被收集,我们可以通过”ansible_local”关键字过滤远程主机的”local facts”信息,示例命令如下

ansible test70 -m setup -a "filter=ansible_local"

上述命令返回的信息如下

test70 | SUCCESS => {
   "ansible_facts": {
       "ansible_local": {
           "testinfo": {
               "testmsg": {
                   "msg1": "This is the first custom test message",
                   "msg2": "This is the second custom test message"
               }
           }
       }
   },
   "changed": false
}

之前说过,当setup收集远程主机的”local facts”时,默认会查找远程主机的/etc/ansible/facts.d目录,如果你把”local facts”信息文件放在了其他自定义路径,在使用setup模块时,需要使用”fact_path”参数指定对应的路径,假设,我把”.fact”文件放在了目标主机的”/testdir”目录下,示例命令如下

ansible test70 -m setup -a 'fact_path=/testdir'

其实,setup模块返回的这些信息都存在了对应的变量中,我们可以通过引用变量从而使用对应的信息,但是别急,我们先来了解一下另外一个模块,这个模块叫”debug模块”。

见名知义,debug模块的作用就是帮助我们进行调试的,debug模块可以帮助我们把信息输出到ansible控制台上,以便我们能够定位问题。

那么我们先来看一个debug模块的playbook小示例,如下

---
- hosts: test70
  remote_user: root
  tasks:
  - name: touch testfile
    file:
      path: /testdir/testfile
      state: touch
  - name: debug demo
    debug:
      msg: this is debug info,The test file has been touched

上例中,我们先在test70主机上touch了对应的文件,然后,利用debug模块在控制台中输出了我们想要显示的信息,如你所见,debug模块的msg参数可以指定我们想要输出的信息,上述playbook表示touch完对应的文件以后,在ansible控制台中输出我们指定的信息,那么我们运行一下这个测试剧本,看一下效果,如下

未分类

如图所示,自定义信息已经输出在ansible控制台中。

debug模块除了能够使用msg参数输出自定义的信息,还能够直接输出变量中的信息,通过debug模块直接输出变量信息需要使用var参数,示例如下

---
- hosts: test70
  remote_user: root
  vars:
    testvar: value of test variable
  tasks:
  - name: debug demo
    debug:
      var: testvar

上例虽然连接到了test70远程主机,但是并没有对test70做任何操作,只是在playbook中定义了一个变量,并且通过debug的var参数输出了这个变量的内容,只是为了单纯的演示debug模块的var参数的使用方法,上述playbook的执行效果如下

未分类

变量的名称以及变量的值都输出到了屏幕上,这个功能可以帮助我们调试playbook中变量,让我们了解变量的值是否符合我们的要求。

当然,使用debug的msg参数时也可以引用变量的值,这样我们自定义的信息就更加灵活了,示例如下。

---
- hosts: test70
  remote_user: root
  vars:
    testvar: testv
  tasks:
  - name: debug demo
    debug:
      msg: "value of testvar is : {{testvar}}"

上例中的msg自定义信息中引用了testvar变量的值

注:上例中msg的值需要使用引号引起,因为{{testvar}}变量前包含”冒号”,如果不使用引号会报语法错误。

上例输出效果如下

未分类

setup模块与debug模块了解完了,现在绕回一开始的话题,playbook在运行时默认都会运行”[Gathering Facts]”任务,”[Gathering Facts]”任务会收集远程主机的相关信息,这些信息会保存在对应的变量中,我们在playbook中可以使用这些变量,从而利用这些信息,那么我们怎样在playbook获取到这些变量的值呢?在setup模块的示例中,我们可以通过”ansible_memory_mb”关键字获取远程主机的内存信息,其实,”ansible_memory_mb”就是一个变量名,换句话说就是,我们可以在playbook中直接引用名为”ansible_memory_mb”的变量,从而获取到远程主机的内存信息,示例如下

---
- hosts: test70
  remote_user: root
  tasks:
  - name: debug demo
    debug:
      msg: "Remote host memory information: {{ansible_memory_mb}}"

上例执行效果如下

未分类

如图所示,我们自定义的信息中包含了远程主机的内存信息,同时被输出了,只是格式上没有手动执行setup模块返回的信息格式易读,手动执行setup模块获取到的内存信息返回如下

test70 | SUCCESS => {
   "ansible_facts": {
       "ansible_memory_mb": {
           "nocache": {
               "free": 1487,
               "used": 336
           },
           "real": {
               "free": 1151,
               "total": 1823,
               "used": 672
           },
           "swap": {
               "cached": 0,
               "free": 1023,
               "total": 1023,
               "used": 0
           }
       }
   },
   "changed": false
}

如上述返回信息所示,”ansible_memory_mb”中其实包含了 “nocache”、”real”、 “swap”三个部分的信息,如果我们只想获得”real”部分的信息,在playbook中引用变量时可以使用如下两种语法。

语法一示例:
debug:
     msg: "Remote host memory information : {{ansible_memory_mb.real}}"
语法二示例:
debug:
     msg: "Remote host memory information : {{ansible_memory_mb['real']}}"
上述两种语法前文中已经进行过示例,此处不再赘述。

其实,这些远程主机的变量信息不仅仅能够用于输出,我们通常会获取到这些信息以后,对这些信息的值进行判断,判断是否符合我们的要求,然后再执行下一步动作,比如,先获取到远程主机的系统发行版信息,然后判断发行版是centos6还是centos7,如果是centos6,我们就将准备好的A文件拷贝到远程主机中,如果是centos7,我们就将准备好的B文件拷贝到远程主机中,不过由于我们还没有总结条件判断的相关使用方法,所以此处就不进行示例了,这篇文章就先总结到这里,希望能够对你有所帮助。

ansible笔记(14):变量(一)

在ansible中使用变量,能让我们的工作变得更加灵活,在ansible中,变量的使用方式有很多种,我们慢慢聊。

先说说怎样定义变量,变量名应该由字母、数字、下划线组成,变量名需要以字母开头,ansible内置的关键字不能作为变量名。

由于之前的几篇文章都是在通过剧本举例,所以我们先聊聊怎样在playbook中使用变量。

如果我们想要在某个play中定义变量,可以借助vars关键字,示例如下

---
- hosts: test70
  vars:
    testvar1: testfile
  remote_user: root
  tasks:
  - name: task1
    file:
      path: /testdir/{{ testvar1 }}
      state: touch

上例中,先使用vars关键字,表示在当前play中进行变量的相关设置。

vars关键字的下一级定义了一个变量,变量名为testvar1,变量值为testfile

当我们需要使用testvar1的变量值时,则需要引用这个变量,如你所见,使用”{{变量名}}”可以引用对应的变量。

也可以定义多个变量,示例如下。

vars:
  testvar1: testfile
  testvar2: testfile2

除了使用上述语法,使用YAML的块序列语法也可以定义变量,示例如下

vars:
  - testvar1: testfile
  - testvar2: testfile2

在定义变量时,还能够以类似”属性”的方式定义变量,示例如下

---
- hosts: test70
  remote_user: root
  vars:
    nginx:
      conf80: /etc/nginx/conf.d/80.conf
      conf8080: /etc/nginx/conf.d/8080.conf
  tasks:
  - name: task1
    file:
      path: "{{nginx.conf80}}"
      state: touch
  - name: task2
    file:
      path: "{{nginx.conf8080}}"
      state: touch

如上例所示,我定义了两个变量,两个变量的值对应两个nginx配置文件路径

  vars:
    nginx:
      conf80: /etc/nginx/conf.d/80.conf
      conf8080: /etc/nginx/conf.d/8080.conf

当我们需要引用这两个变量时,有两种语法可用

语法一

"{{nginx.conf80}}"

语法二

"{{nginx['conf8080']}}"

这样使用变量在逻辑上比较清晰,可以看出conf80与conf8080都属于nginx相关的配置。

细心如你一定发现了,上例中,我在引用变量时使用了双引号,而在本文的第一个示例中引用变量时却没有使用双引号,这是因为,第一个示例中的变量在被引用时,并没有处于”开头的位置”,第一个示例中变量被引用时如下

path: /testdir/{{ testvar1 }}

当file模块的path参数引用对应的变量时,先写入了’/testdir/’,然后才引用了”testvar1″变量,{{ testvar1 }}并没有处于”开头的位置”,换句话说就是,{{ testvar1 }}前面还有字符串’/testdir/’

而在上述后面的示例中引用变量时,变量被引用时如下,处于”开头的位置”

path: "{{nginx.conf80}}"

这种情况下,我们引用变量时必须使用双引号引起被引用的变量,否则会报语法错误。

其实,上述情况也有例外

前文中有描述过,当在playbook中为模块的参数赋值时,可以使用”冒号”,也可以使用”等号”,当使用”等号”为模块的参数赋值时,则不用考虑引用变量时是否使用”引号”的问题,示例如下

---
- hosts: test70
  remote_user: root
  vars:
    nginx:
      conf80: /etc/nginx/conf.d/80.conf
      conf8080: /etc/nginx/conf.d/8080.conf
  tasks:
  - name: task1
    file:
      path={{nginx.conf80}}
      state=touch
  - name: task2
    file:
      path={{nginx['conf8080']}}
      state=touch

除了能够在playbook中直接定义变量,我们还可以在某个文件中定义变量,然后再在playbook中引入对应的文件,引入文件后,playbook

即可使用文件中定义的变量,你可能会问,为什么要多此一举呢?这是因为在某些工作场景中这样做很有用,比如,你想要让别人阅读你的playbook,却不想让别人看到某些值,可以使用这种办法,因为别人在阅读playbook时,只能看到引入的变量名,但是看不到变量对应的值,这种将变量分离到某个文件中的做法叫做”变量文件分离”,”变量文件分离”除了能够隐藏某些值,还能够让你将不同类的信息放在不同的文件中,并且让这些信息与剧本主体分开。

先来看看”变量文件分离”的一些小例子

首先,我们来定义一个专门用来存放nginx相关变量的文件(文件名为nginx_vars.yml),在文件中定义变量时,不要使用vars关键字,直接定义变量即可,定义变量的语法与在playbook中定义变量的几种语法相同

语法一示例:
  testvar1: testfile
  testvar2: testfile2
语法二示例:
  - testvar1: testfile
  - testvar2: testfile2
语法三示例:
nginx:
  conf80: /etc/nginx/conf.d/80.conf
  conf8080: /etc/nginx/conf.d/8080.conf

你可以选择你觉得较为舒适的语法定义变量,如下所示,直接在nginx_vars.yml文件中定义变量即可。

# cat nginx_vars.yml
nginx:
  conf80: /etc/nginx/conf.d/80.conf
  conf8080: /etc/nginx/conf.d/8080.conf

在nginx_vars.yml中定义完相关变量后,即可在playbook中引入文件中的变量,在playbook中引入包含变量的文件时,需要使用”vars_files”关键字,被引入的文件需要以”- “开头,以YAML中块序列的语法引入,示例如下

---
- hosts: test70
  remote_user: root
  vars_files:
  - /testdir/ansible/nginx_vars.yml
  tasks:
  - name: task1
    file:
      path={{nginx.conf80}}
      state=touch
  - name: task2
    file:
      path={{nginx['conf8080']}}
      state=touch

上例中使用”vars_files”关键字引入了对应的变量文件,然后使用了文件中定义的变量。

上例中”vars_files”关键字只引入了一个变量文件,也可以引入多个变量文件,每个被引入的文件都需要以”- “开头,示例如下

  vars_files:
  - /testdir/ansible/nginx_vars.yml
  - /testdir/ansible/other_vars.yml

“vars”关键字和”vars_files”关键字可以同时使用,如下

  vars:
  - conf90: /etc/nginx/conf.d/90.conf
  vars_files:
  - /testdir/ansible/nginx_vars.yml

除了上述总结,ansible还有一些其他的关于变量的使用方法,暂且放到下一篇文章吧,希望这篇文章能够帮助到你,加油~

ansible笔记(13):tags的用法

这篇文章会介绍playbook中tags的用法。

你写了一个很长的playbook,其中有很多的任务,这并没有什么问题,不过在实际使用这个剧本时,你可能只是想要执行其中的一部分任务而已,或者,你只想要执行其中一类任务而已,而并非想要执行整个剧本中的全部任务,这个时候我们该怎么办呢?我们可以借助tags实现这个需求。

见名知义,tags可以帮助我们对任务进行’打标签’的操作,当任务存在标签以后,我们就可以在执行playbook时,借助标签,指定执行哪些任务,或者指定不执行哪些任务了,这样说可能不够直观,我们来看一个小示例(为了方便示例此处只写3个任务进行举例)。

---
- hosts: test70
  remote_user: root
  tasks:
  - name: task1
    file:
      path: /testdir/t1
      state: touch
    tags: t1
  - name: task2
    file: path=/testdir/t2
          state=touch
    tags: t2
  - name: task3
    file: path=/testdir/t3
          state=touch
    tags: t3

如上例所示,上例的play中有3个task,每个task都有对应的tags,为了方便示例,我只是简单的把tags的值写成了t1、t2、t3,在实际的使用中,我们应该让tags的值能够见名知义,现在每个task都有了标签,假如在执行上述playbook时,我们只想执行task2,该怎样执行呢?我们可以使用如下命令

ansible-playbook --tags=t2 testtag.yml

如你所见,可以使用–tags选项指定某个标签,当指定标签后,只有标签对应的任务会被执行,其他任务都不会被执行,执行上述命令后,只有task2会执行,因为task2的标签值为t2,task1和task3都不会执行,这样就达到了只执行playbook中部分任务的目的。

借助标签,除了能够指定”需要执行的任务”,还能够指定”不执行的任务”,示例命令如下。

ansible-playbook --skip-tags='t2' testtag.yml

我们可以使用 –skip-tags选项指定”不执行的任务”,执行上述命令后,task1和task3会执行,task2不会执行,因为我们已经在命令中指定了’跳过’标签t2所对应的任务,相当于使用了’排除法’,t2对应的任务被排除了,其他任务都会执行。

除了使用上例中的语法指定标签,还能够使用下例中的两种语法指定标签的值。

---
- hosts: test70
  remote_user: root
  tasks: 
  - name: task1
    file: 
      path: /testdir/t1
      state: touch
    tags:
      - t1
  - name: task2
    file: path=/testdir/t2
          state=touch
    tags: ['t2']

之前描述的三种语法都可以指定标签,不过上例中,每个任务只有一个标签,其实,我们可以为每个任务添加多个标签,三种语法添加多个标签的示例如下

语法一:
tags:
 - testtag
 - t1

语法二:
tags: tag1,t1

语法三:
tags: ['tagtest','t2']

上述示例的语法一使用了YAML块序列的语法格式指定多个标签,语法二与语法三都是在原来语法的基础上,使用’逗号’隔开多个标签。

如下例所示,不同的任务可以使用相同的标签。

---
- hosts: test70
  remote_user: root
  tasks:
  - name: install httpd package
    tags: httpd,package
    yum:
      name=httpd
      state=latest

  - name: start up httpd service
    tags: httpd,service
    service:
      name: httpd
      state: started

上例中每个任务都有多个标签,而且上例中两个任务都有一个共同的标签,就是httpd标签,所以,当我们执行’ansible-playbook –tags=httpd testhttpd.yml’,上述两个任务都会执行。

上例的play中的所有任务都有共同的httpd标签,像这种情况,我们可以把httpd标签提取出来,写在play中,示例如下。

---
- hosts: test70
  remote_user: root
  tags: httpd
  tasks:
  - name: install httpd package
    tags: ['package']
    yum:
      name=httpd
      state=latest

  - name: start up httpd service
    tags:
      - service
    service:
      name: httpd
      state: started

当tags写在play中而非task中时,play中的所有task会继承当前play中的tags,而上例中,两个任务都会继承httpd标签,同时还有拥有自己的标签。

在调用标签时,也可以一次性指定多个标签,调用多个标签需要用逗号隔开,命令如下

ansible-playbook --tags package,service testhttpd.yml

在调用标签之前,如果你想要概览一下playbook中都有哪些标签,可以使用 ‘ –list-tags’ 选项,示例如下

ansible-playbook --list-tags testhttpd.yml

其实,ansible还预置了5个特殊tag,这5个特殊tag分别为

always

never(2.5版本中新加入的特殊tag)

tagged

untagged

all

当我们把任务的tags的值指定为always时,那么这个任务就总是会被执行,除非你使用’–skip-tags’选项明确指定不执行对应的任务,这样说可能不容易理解,不如看个小示例,示例如下

---
- hosts: test70
  remote_user: root
  tasks:
  - name: task1
    file:
      path: /testdir/t1
      state: touch
    tags:
      - t1
  - name: task2
    file: path=/testdir/t2
          state=touch
    tags: ['t2']
  - name: task3
    file: path=/testdir/t3
          state=touch
    tags: t3,always

上例中,task3的标签有两个,t3和always,那么我们来执行一下这个playbook,假设,我只想运行上述playbook中标签为t1的任务,那么我会执行如下图中的命令

未分类

如图所示,在执行上述playbook时,我只指定了’t1’,正常情况下应该只执行’t1’对应的任务,也就是应该只执行task1,但是实际上执行了task1和task3,这是因为task3的标签的值包含always关键字,所以即使task3对应的标签没有被调用,task3也会执行,这就是always的作用。

如果你不想执行标签中包含always的任务,你可以使用’–skip-tags’选项明确指定跳过它们,仍然以上例的playbook为例,假设我们就是不想执行task3,我们可以执行如下命令

ansible-playbook --skip-tags always testtag.yml

但是需要注意,如果上述play中有多个任务都有always标签,那么上述命令将会跳过所有包含always标签的任务,如果上例中的play中的多个任务都有always标签,则可以使用如下命令只跳过task3,其他带有always标签的任务不会跳过,前提是task3有除了always以外的自定义标签。

ansible-playbook --skip-tags t3 testtag.yml

在2.5版本的ansible中,引入了新的特殊标签 ‘never’,从字面上理解,never的作用应该与always正好相反,由于我当前使用的ansible版本为2.4(还没有引入never标签),所以当指定任务的标签为never时,貌似被ansible当做了自定义标签,所以如果你安装了2.5版本的ansible,可以尝试一下never标签的作用,由于还没有实际使用过2.5版本,所以此处暂时不进行示例。

剩余的三个特殊标签分别为 tagged、untagged、all

这三个特殊标签并非像always一样,always作为标签值存在,而这三个特殊标签则是在调用标签时使用,示例如下

ansible-playbook --tags tagged testtag.yml

上述命令表示只执行有标签的任务,没有任何标签的任务不会被执行。

ansible-playbook --skip-tags tagged testtag.yml

上述命令表示跳过包含标签的任务,即使对应的任务包含always标签,也会被跳过。

ansible-playbook --tags untagged testtag.yml

上述命令表示只执行没有标签的任务,但是如果某些任务包含always标签,那么这些任务也会被执行。

ansible-playbook --skip-tags untagged testtag.yml

上述命令表示跳过没有标签的任务。

特殊标签all表示所有任务会被执行,不用指定,默认情况下就是使用这个标签。

关于标签的使用就先总结到这里,希望能够对你有所帮助~

ansible笔记(10):初识ansible playbook

前文中,我们介绍了一些ansible的常用模块,聪明如你,一定已经掌握了这些模块的使用方法。

那么现在,我们来想象一个工作场景,看看怎样把之前的知识点应用到这个工作场景中。

假设,我们想要在test70主机上安装nginx并启动,我们可以在ansible主机中执行如下3条命令

ansible test70 -m yum_repository -a 'name=aliEpel description="alibaba EPEL" baseurl=https://mirrors.aliyun.com/epel/$releaseverServer/$basearch/'
ansible test70 -m yum -a 'name=nginx disable_gpg_check=yes enablerepo=aliEpel'
ansible test70 -m service -a "name=nginx state=started"

我们通过上述3条命令,先确定配置了对应的yum源,然后使用yum模块安装了nginx,最后使用service模块启动了nginx,最终达到了我们的目的。

但是在实际的工作环境中,我们可能需要经常在新主机上安装nginx,难道每次有新的服务器加入工作环境,我们都要修改上述3条命令中的主机名并且重新将每一条命令执行一遍吗?这样似乎有些麻烦,肯定有更好的办法,没错,我们可以将上述命令写成脚本,每次修改一些变量,然后执行脚本就行了,这样似乎方便了不少,而ansible天生就提供了这种类似”脚本”的功能,在ansible中,类似”脚本”的文件被称作”剧本”,’剧本’的英文名称为’playbook’,我们只需要将要做的事情编写成playbook,把不同的模块按照顺序编排在剧本中,ansible就会按照剧本一步一步的执行,最终达到我们的目的,虽然playbook的功能与脚本类似,但是剧本并不是简单的将ad-hoc命令按照顺序堆砌在一个可执行文件中,编写剧本需要遵循YAML语法,如果你没有接触过YAML语法,不用害怕,坚持看完后面的示例,熟悉一些固定套路以后,你也可以自己编写playbook。

那么怎样编写playbook呢?我们先从一个简单的示例开始吧~

首先,我们需要创建一个YAML格式的playbook文件。

playbook文件以”.yaml”或者”.yml”作为文件名后缀,此处我们创建一个名为”test.yml”的剧本文件。

在编写剧本之前,我们先来回顾两个简单的ad-hoc命令,比如如下两条命令

ansible test70 -m ping
ansible test70 -m file -a "path=/testdir/test state=directory"

上述命令表示使用ping模块去ping主机test70,然后再用file模块在test70主机上创建目录,那么,如果把上述命令转换成playbook的表现形式,该如何书写呢?示例如下

(此处先进行示例,后文会说明怎样执行playbook)

未分类

如上所示,第一行使用三个横杠作为开始,在YAML语法中,”—“表示文档开始。

第二行使用”- “作为开头(注意:横杠后面有空格),如果你了解过YAML语法,那么你一定知道YAML使用”- “表示一个块序列的节点,如果你不了解YAML语法,不用在此处纠结,先这么写,写的多了,自然会理解,从上例可以看出,”- “后面使用hosts关键字指定了要操作的主机,hosts关键字对应的值为test70,表示我们要在test70主机上进行操作,”hosts: test70″是一个键值对,注意,在YAML语法中使用冒号映射键值对时,’冒号’后面必须有’空格’,这也是语法,没有为什么,记住就好,如果你想要一次性在多台主机上进行操作,可以同时写多个主机,每台主机使用逗号隔开,比如’hosts: test70,test61’,如果你在清单中对主机进行了分组,也可以使用组名。

第三行,使用remote_user关键字可以指定在进行远程操作时使用哪个用户进行操作,’remote_user: root’表示test70的root用户进行操作,上图中,remote_user关键字与hosts关键字对齐,表示它们是平级的,之前的文章中提到过,在YAML语法中进行缩进时,不能使用tab键进行缩进,必须使用空格,所以,为了兼容使用tab键进行缩进的使用习惯,可以将vim编辑器设置为自动将tab转成空格。

第四行,使用tasks关键字指明要进行操作的任务列表,之后的行都属于tasks键值对中的值。

之后的行都属于tasks任务列表中的任务,可以看出,整个任务列表一共有两个任务组成,每个任务都以”- “开头,每个任务都有自己的名字,任务名使用name关键字进行指定,第一个任务使用ping模块,使用ping模块时没有指定任何参数。第二个任务使用file模块,使用file模块时,指定了path参数与state参数的值。

好了,test.yml已经编写完成了,但是,我们还没有运行这个剧本,运行剧本需要使用’ansible-playbook’命令,示例如下

未分类

如上图所示,playbook执行后返回了一些信息,这些信息是这次剧本运行的概况。

‘PLAY [test70]’表示这次运行的playbook中有一个’play’是针对test70这台主机运行的,一个’playbook’是由一个或多个’play’组成的,这样说可能不太容易理解,那么我们打个比方,一个’剧本’是由一个或多个’桥段’组成的,每个桥段都有不同的场景、人物、故事,所有的桥段组合在一起,组成一个完整的剧本,剧本就是playbook,桥段就是play,而上例中,整个剧本中只有一个桥段,也就是说,上例的playbook中,我们只写了一个play,在下文中我们会举例说明怎样书写多个play,到时候你会更加明白到底什么是所谓的’play’,当然,’桥段’只是我自己为了方便理解给’play’起的中文名,官方名称只叫”play”。

从上述信息可以看出,仅有的这个play是针对test70运行的,这个play一共包含三个任务,第一个任务的名字叫做’Gathering Facts’,第二个任务的名字叫做’Ping the host’,第三个任务的名字叫做’make directory test’,看到此处你会发现,我们在playbook中明明只写了两个任务,为什么最后执行时却有三个任务呢?这是因为,每个play在执行时,都会先执行一个默认任务,这个默认任务就是’Gathering Facts’,’Gathering Facts’任务会收集当前play对应的目标主机的相关信息,收集完这些基础信息后,才会执行我们指定的任务,由于上例中,hosts的值只有test70一个主机,所以这个play只针对test70运行,所以’Gathering Facts’这个任务只收集了test70的相关信息,执行完默认任务后,开始执行’Ping the host’任务和’make directory test’任务,由于在执行playbook之前,/testdir/test目录已经存在于test70主机中,所以’make directory test’任务返回的信息是绿色的,如果对应的目录并不存在,’make directory test’任务返回的信息应该是黄色的,这是因为幂等性的缘故,前文已经解释过,此处不再赘述。

当playbook中的所有play执行完毕后,在返回信息的’PLAY RECAP’中可以对所有目标主机的执行情况进行’回顾’,由于我们的目标主机只有test70一台,所以只看到了test70的执行概括信息。

我们已经执行了一个playbook,这个playbook中只有一个play,我们也可以在这个playbook中多写几个play,示例如下

未分类

如上图所示,上例中有多个play,第一个play针对test70执行,这个play会执行两个任务。

第二个play针对test61和test70执行,第二个play只包含一个任务,即在对应的目标主机上创建/tfile文件

第三个play针对test60和test61执行,细心如你一定发现了,当你需要在play中指定多个主机时,有两种语法可以使用,第二个play和第三个play中的语法都可以指定多个主机,第三个play也只包含一个任务,即在test60和test61主机上创建zsythink用户。

多个play写完以后,我们来运行一下剧本试试。

未分类

如下图所示,每个play执行时都会显示当前play对应的目标主机,并且每个play执行时都会先执行默认的facts任务,收集对应目标主机的信息,当所有play都执行完毕后,会出现’PLAY RECP’,相当于一个小的报告信息,看到这里,你应该已经理解了到底什么是所谓的’play’了吧。

再次强调一遍,如果你并不是很熟悉YAML语法,你可以不用过于深究YAML语法,只需要死记硬背,记住某些”固定套路”即可编写playbook,写的多了,自然会对这些语法有所理解的。

如果你的playbook写完了,但是你不能确定playbook文件中是否存在语法错误,那么你可以使用如下命令对playbook进行语法检查。

ansible-playbook --syntax-check /testdir/ansible/test.yml

执行上述命令后,如果只返回了playbook的名称,就表示没有语法错误。

除了对playbook进行语法测试,我们还能够’模拟执行’playbook,’模拟执行’并不是真正的执行,只是’假装’执行一下,playbook中的任务并不会真正在目标主机中运行,所以你可以放心大胆的进行模拟,使用如下命令即可模拟运行playbook,模拟运行功能可以帮助我们’预估’playbook是否能够正常执行。

ansible-playbook --check test.yml

注意:使用上述命令进行’模拟’时,一些任务可能会报错,这可能是因为报错的任务在执行时需要依赖之前的其他任务的完成结果,但是因为是’模拟’执行,所以之前的任务并不会真正的执行,既然之前的任务没有真正的执行,自然不会产生对应的结果,所以后面的任务就报错了,也就是说,我们并不能完全以’模拟’的反馈结果作为playbook是否能够正常运行的判断依据,只能通过’模拟’大概的’预估’一下而已。

通过这篇文章可以初步的了解playbook,希望能够对你有所帮助。

ansible笔记(12):handlers的用法

这篇文章会介绍playbook中handlers的用法。

不过在开始介绍它们之前,我们先来描述一个工作场景。

当我们修改了某些程序的配置文件以后,有可能需要重启应用程序,以便能够使新的配置生效,那么,如果使用playbook来实现这个简单的功能,该怎样编写playbook呢?

我们来试试,此处我们使用nginx作为示例,虽然nginx可以使用’nginx -s reload’命令重载配置,但是此处的示例中并不会使用这个命令,而是用nginx类比那些需要重启生效的应用。

假设我们想要将nginx中的某个server的端口从8080改成8088,并且在修改配置以后重启nginx,那么我们可以编写如下剧本。

---
- hosts: test70
  remote_user: root
  tasks:
  - name: Modify the configuration
    lineinfile:
      path=/etc/nginx/conf.d/test.zsythink.net.conf
      regexp="listen(.*) 8080 (.*)"
      line="listen1 8088 2"
      backrefs=yes
      backup=yes
  - name: restart nginx
    service:
      name=nginx
      state=restarted

上述play表示修改test70主机的/etc/nginx/conf.d/test.zsythink.net.conf配置文件,将监听端口8080改为监听端口8088,端口修改完成后,重启服务。

在执行这个playbook之前,我们先来确认一下test70主机的8080端口是否被监听

未分类

可以看到test70主机上的8080正常被监听,那么现在我们来执行一下上述playbook,看一下执行效果

执行后可以看到,play中的两个任务都被正常执行了,如下图所示

未分类

此时再次查看test70主机的端口号,已经从8080改为8088,如下图所示

未分类

这样没有任何问题,与我们预期的一样,端口号从8080修改为8088,重启了服务

那么,我们再来重复执行一遍上述playbook试试,看看会出现什么情况,重复执行效果如下

未分类

如上图所示,当我们再次执行同样的playbook时,由于配置文件中的端口号已经是8088,所以,任务”Modify the configuration”的状态为OK(换句话说,这个任务并没有在远程主机进行任何实际操作),这是由于ansible的幂等性造成的(前文已经对幂等性做出了解释,此处不再赘述),因为目标状态与我们预期的状态一致,所以ansible并没有做任何改动,这是完全正常的,从上图可以看出,任务”restart nginx”也正常的执行了,而且是”真正的”执行了,换句话说就是它的确重启了对应的nginx服务,对远程主机进行了实际的操作。

第二次运行剧本的过程似乎没有什么问题,但是仔细想想,又有些不妥,因为我们重启服务的目的是为了在修改配置文件以后使新的配置生效,而第二次运行剧本的这种情况下,我们并没有真正修改服务器配置,因为服务器配置本来 就与我们预期的一致,但是,在没有修改配置的情况下,仍然重启了服务,这种重启是不需要的,我们想要达到的效果是,如果配置文件发生了改变,则重启服务,如果配置文件并没有被真正的修改,则不对服务进行任何操作,这种情况下,我们该怎们办呢?

handlers就是来解决这种问题的,此处我们先大概的描述一下handlers的概念,后面会给出示例,你可以把handlers理解成另一种tasks,handlers是另一种’任务列表’,handlers中的任务会被tasks中的任务进行”调用”,但是,被”调用”并不意味着一定会执行,只有当tasks中的任务”真正执行”以后(真正的进行实际操作,造成了实际的改变),handlers中被调用的任务才会执行,如果tasks中的任务并没有做出任何实际的操作,那么handlers中的任务即使被’调用’,也并不会执行。这样说似乎不容易被理解,我们来写一个小示例,示例如下。

---
- hosts: test70
  remote_user: root
  tasks:
  - name: Modify the configuration
    lineinfile:
      path=/etc/nginx/conf.d/test.zsythink.net.conf
      regexp="listen(.*) 8080(.*)"
      line="listen1 80882"
      backrefs=yes
      backup=yes
    notify:
      restart nginx

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

如上例所示,我们使用handlers关键字,指明哪些任务可以被’调用’,之前说过,handlers是另一种任务列表,你可以把handlers理解成另外一种tasks,你可以理解成它们是’平级’的,所以,handlers与tasks是’对齐’的(缩进相同),上例中的handlers中只有一个任务,这个任务的名称为”restart nginx”,之前也说明过,handlers中的任务需要被tasks中的任务调用,那么上例中,”restart nginx”被哪个任务调用了呢?很明显,”restart nginx”被”Modify the configuration”调用了,没错,如你所见,我们使用notify关键字’调用’handlers中的任务,或者说,通过notify关键字’通知’handlers中的任务,所以,综上所述,上例中的play表示,如果”Modify the configuration”真正的修改了配置文件(实际的操作),那么则执行”restart nginx”任务,如果”Modify the configuration”并没有进行任何实际的改动,则不执行”restart nginx” ,这就是handlers的作用,聪明如你肯定已经明白了,动手执行一下上述playbook试试吧。

handlers是另一种任务列表,所以handlers中可以有多个任务,被tasks中不同的任务notify,示例如下

---
- hosts: test70
  remote_user: root
  tasks:
  - name: make testfile1
    file: path=/testdir/testfile1
          state=directory
    notify: ht2
  - name: make testfile2
    file: path=/testdir/testfile2
          state=directory
    notify: ht1

  handlers:
  - name: ht1
    file: path=/testdir/ht1
          state=touch
  - name: ht2
    file: path=/testdir/ht2
          state=touch

如上例所示,tasks与handlers都是任务列表,只是handlers中的任务被tasks中的任务notify罢了,那么我们来执行一下上述playbook,如下图所示

未分类

从上图可以看出,handler执行的顺序与handler在playbook中定义的顺序是相同的,与”handler被notify”的顺序无关。

如上图所示,默认情况下,所有task执行完毕后,才会执行各个handler,并不是执行完某个task后,立即执行对应的handler,如果你想要在执行完某些task以后立即执行对应的handler,则需要使用meta模块,示例如下

---
- hosts: test70
  remote_user: root
  tasks:
  - name: task1
    file: path=/testdir/testfile
          state=touch
    notify: handler1
  - name: task2
    file: path=/testdir/testfile2
          state=touch
    notify: handler2

  - meta: flush_handlers

  - name: task3
    file: path=/testdir/testfile3
          state=touch
    notify: handler3

  handlers:
  - name: handler1
    file: path=/testdir/ht1
          state=touch
  - name: handler2
    file: path=/testdir/ht2
          state=touch
  - name: handler3
    file: path=/testdir/ht3
          state=touch

如上例所示,我在task1与task2之后写入了一个任务,我并没有为这个任务指定name属性,这个任务使用meta模块,meta任务是一种特殊的任务,meta任务可以影响ansible的内部运行方式,上例中,meta任务的参数值为flush_handlers,”meta: flush_handlers”表示立即执行之前的task所对应handler,什么意思呢?意思就是,在当前meta任务之前,一共有两个任务,task1与task2,它们都有对应的handler,当执行完task1与task2以后,立即执行对应的handler,而不是像默认情况那样在所有任务都执行完毕以后才能执行各个handler,那么我们来实际运行一下上述剧本,运行结果如下

未分类

正如上图所示,meta任务之前的任务task1与task2在进行了实际操作以后,立即运行了对应的handler1与handler2,然后才运行了task3,在所有task都运行完毕后,又逐个将剩余的handler根据情况进行调用。

聪明如你一定想到了,如果想要每个task在实际操作后都立马执行对应handlers,则可以在每个任务之后都添加一个meta任务,并将其值设置为flush_handlers 所以,我们可以依靠meta任务,让handler的使用变得更加灵活,快动手试试吧。

我们还可以在一个task中一次性notify多个handler,怎样才能一次性notify多个handler呢?你可能会尝试将多个handler使用相同的name,但是这样并不可行,因为当多个handler的name相同时,只有一个handler会被执行,所以,我们并不能通过这种方式notify多个handler,如果想要一次notify多个handler,则需要借助另一个关键字,它就是’listen’,你可以把listen理解成”组名”,我们可以把多个handler分成”组”,当我们需要一次性notify多个handler时,只要将多个handler分为”一组”,使用相同的”组名”即可,当notify对应的值为”组名”时,”组”内的所有handler都会被notify,这样说可能还是不容易理解,我们来看个小示例,示例如下

---
- hosts: test70
  remote_user: root
  tasks:
  - name: task1
    file: path=/testdir/testfile
          state=touch
    notify: handler group1

  handlers:
  - name: handler1
    listen: handler group1
    file: path=/testdir/ht1
          state=touch
  - name: handler2
    listen: handler group1
    file: path=/testdir/ht2
          state=touch

如上例所示,handler1与handler2的listen的值都是handler group1,当task1中notify的值为handler group1时,handler1与handler2都会被notify,还是很方便的。

关于handlers的使用就先总结到这里,希望能够对你有所帮助。

ansible笔记(11):初识ansible playbook(二)

前文中,我们已经编写了 一个简单的剧本,这篇文章继续了解一下playbook的一些基础。

有前文作为基础,如下示例是非常容易理解的:

---
- hosts: test70
  remote_user: root
  tasks:
  - name: make testfile
    file:
      path: /testdir/testfile
      state: touch
      mode: 0700

上例中有一个play,这个play针对test70主机运行,这个play的任务列表中只有一个任务,这个任务就是调用file模块,确保/testdir/testfile文件存在并且testfile文件的权限为0700,把上例中的任务列表部分单独截取出来,如下所示

tasks:
- name: make testfile
  file:
    path: /testdir/testfile
    state: touch
    mode: 0700

正如你所看到的,”path: /testdir/testfile” 表示为file模块的path参数赋值,我们使用”冒号”(冒号后有空格)对参数赋值。

其实,除了这种使用冒号的方式,我们还可以使用如下格式为模块的参数赋值

tasks:
- name: make testfile
  file: path=/testdir/testfile state=touch mode=0700

如上所示,我们调用file模块时,设置了三个参数,path参数、state参数、mode参数,为参数赋值时,使用了”等号”,每个参数之间使用空格隔开,这种格式也是完全正确的,如果你在使用一个模块时设置的参数比较多,那么使用上述格式设置参数时,这些参数可能会”挤在一行”里面,你也可以把它们分成多行去写,如下例所示

tasks:
- name: make testfile
  file: path=/testdir/testfile
        state=touch mode=0700

即使把多个参数分行写,也需要注意缩进。

上述书写格式都是0.8版本以后的ansible推荐的书写格式,在0.8版本之前,使用action关键字调用模块,示例如下:

tasks:
- name: make testfile
  action: file path=/testdir/testfile state=touch mode=0700

如上例所示,使用action关键字调用对应的模块,在当前版本中(博客中的ansible版本为2.4)仍然兼容这种语法

在之前的示例中,我们对每个任务都指定了对应的名称,即每个task都有对应的name,当我们省略name时,默认以当前任务调用的模块的名称作为任务的名称,不过建议不要省略name,因为当任务存在name时,可读性比较高。

在编写任务时,我习惯将name属性写在任务的开头,当然,任务的各个属性并没有严格的顺序要求,如下两种写法的效果是相同的。

写法一:
tasks:
- name: make testfile
  file: path=/testdir/testfile
        state=touch
        mode=0700

写法二:
tasks:
- file: path=/testdir/testfile
        state=touch
        mode=0700
  name: make testfile

各属性顺序虽然没有要求,但是仍然需要严格按照缩进进行对齐。

关于这些基础,就先暂时总结到这里,希望能够对你有所帮助。

ansible笔记(9):常用模块之包管理模块

前文介绍了各种类型的模块,这篇文章来介绍一下linux中的包管理模块。

yum_repository模块

yum_repository模块可以帮助我们管理远程主机上的yum仓库。

此处我们介绍一些yum_repository模块的常用参数,你可以先对这些参数有一个大概了解,然后再看小示例。

name参数 :必须参数,用于指定要操作的唯一的仓库ID,也就是”.repo”配置文件中每个仓库对应的”中括号”内的仓库ID

baseurl参数 :此参数用于设置yum仓库的baseurl

description参数 :此参数用于设置仓库的注释信息,也就是”.repo”配置文件中每个仓库对应的”name字段”对应的内容。

file参数 :此参数用于设置仓库的配置文件名称,即设置”.repo”配置文件的文件名前缀,在不使用此参数的情况下,默认以name参数的仓库ID作为”.repo”配置文件的文件名前缀,同一个’.repo’配置文件中可以存在多个yum源

enabled参数 :此参数用于设置是否激活对应的yum源,此参数默认值为yes,表示启用对应的yum源,设置为no表示不启用对应的yum源。

gpgcheck参数 :此参数用于设置是否开启rpm包验证功能,默认值为no,表示不启用包验证,设置为yes表示开启包验证功能。

gpgcakey参数 :当gpgcheck参数设置为yes时,需要使用此参数指定验证包所需的公钥

state参数 :默认值为present,当值设置为absent时,表示删除对应的yum源

yum_repository模块的ad-hoc示例命令如下:

使用如下命令在test70主机上设置ID为aliEpel 的yum源,仓库配置文件路径为/etc/yum.repos.d/aliEpel.repo

ansible test70 -m yum_repository -a 'name=aliEpel description="alibaba EPEL" baseurl=https://mirrors.aliyun.com/epel/$releaseverServer/$basearch/'

使用如下命令在test70主机上设置ID为aliEpel 的yum源,仓库配置文件路径为/etc/yum.repos.d/alibaba.repo

ansible test70 -m yum_repository -a 'name=aliEpel description="alibaba EPEL" baseurl=https://mirrors.aliyun.com/epel/$releaseverServer/$basearch/ file=alibaba'

使用如下命令在test70主机上设置ID为local 的yum源,但是不启用它(local源使用系统光盘镜像作为本地yum源,以便测试举例,所以baseurl中的值以file:///开头)

ansible test70 -m yum_repository -a 'name=local baseurl=file:///media description="local cd yum" enabled=no'

使用如下命令在test70主机上设置ID为local的yum源,开启包验证功能,并指定验证包所需的公钥位置为/media/RPM-GPG-KEY-CentOS-7

ansible test70 -m yum_repository -a 'name=local baseurl=file:///media description="local cd yum" gpgcheck=yes gpgcakey=file:///media/RPM-GPG-KEY-CentOS-7'

删除/etc/yum.repos.d/alibaba.repo配置文件中的aliEpel源

ansible test70 -m yum_repository -a 'file=alibaba name=aliEpel state=absent'

yum模块

yum模块可以帮助我们在远程主机上通过yum源管理软件包。

此处我们介绍一些yum模块的常用参数,你可以先对这些参数有一个大概了解,然后再看小示例。

name参数 :必须参数,用于指定需要管理的软件包,比如nginx

state参数 :用于指定软件包的状态 ,默认值为present,表示确保软件包已经安装,除了present,其他可用值有installed、latest、absent、removed,其中installed与present等效,latest表示安装yum中最新的版本,absent和removed等效,表示删除对应的软件包。

disable_gpg_check参数 :用于禁用对rpm包的公钥gpg验证,默认值为no,表示不禁用验证,设置为yes表示禁用验证,即不验证包,直接安装,在对应的yum源没有开启gpg验证的情况下,需要将此参数的值设置为yes,否则会报错而无法进行安装。

enablerepo参数 :用于指定安装软件包时临时启用的yum源,假如你想要从A源中安装软件,但是你不确定A源是否启用了,你可以在安装软件包时将此参数的值设置为yes,即使A源的设置是未启用,也可以在安装软件包时临时启用A源。

disablerepo参数 :用于指定安装软件包时临时禁用的yum源,某些场景下需要此参数,比如,当多个yum源中同时存在要安装的软件包时,你可以使用此参数临时禁用某个源,这样设置后,在安装软件包时则不会从对应的源中选择安装包。

enablerepo参数和disablerepo参数可以同时使用

yum模块的ad-hoc示例命令如下:

确保test70主机上通过yum源安装了nginx(对应yum源未开启gpg验证,所以需要设置disable_gpg_check=yes),如下三条命令的效果相同

ansible test70 -m yum -a 'name=nginx disable_gpg_check=yes'
ansible test70 -m yum -a 'name=nginx state=present disable_gpg_check=yes'
ansible test70 -m yum -a 'name=nginx state=installed disable_gpg_check=yes'

确保test70主机上安装了yum源中最新版本的nginx

ansible test70 -m yum -a 'name=nginx state=latest disable_gpg_check=yes'

确保test70主机上通过yum源安装的nginx被卸载了

ansible test70 -m yum -a 'name=nginx state=absent'
ansible test70 -m yum -a 'name=nginx state=removed'

在test70主机上安装telnet时不确定local源是否启用,使用enablerepo=local确保临时启用local源

ansible test70 -m yum -a 'name=telnet disable_gpg_check=yes enablerepo=local'

在test70主机上安装telnet时,确定多个源中都有telnet,但是不想从local源中安装,所以在安装时临时禁用local源

ansible test70 -m yum -a 'name=telnet disable_gpg_check=yes disablerepo=local'

ansible笔记(8):常用模块之系统类模块(二)

这篇文章会继续介绍一些常用的系统类操作模块,可与前文结合在一起阅读。

user模块

user模块可以帮助我们管理远程主机上的用户,比如创建用户、修改用户、删除用户、为用户创建密钥对等操作。

此处我们介绍一些user模块的常用参数,你可以先对这些参数有一个大概了解,然后再看小示例。

name参数 :必须参数,用于指定要操作的用户名称,可以使用别名user。

group参数 :此参数用于指定用户所在的基本组

gourps参数 :此参数用于指定用户所在的附加组,注意,如果说用户已经存在并且已经拥有多个附加组,那么如果想要继续添加新的附加组,需要结合append参数使用,否则在默认情况下,当再次使用groups参数设置附加组时,用户原来的附加组会被覆盖。

append参数 :如果用户原本就存在多个附加组,那么当使用groups参数设置附加组时,当前设置会覆盖原来的附加组设置,如果不想覆盖原来的附加组设置,需要结合append参数,将append设置为yes,表示追加附加组到现有的附加组设置,append默认值为no。

shell参数 :此参数用于指定用户的默认shell

uid参数 :此参数用于指定用户的uid号

expires参数 :此参数用于指定用户的过期时间,相当于设置/etc/shadow文件中的的第8列,比如,你想要设置用户的过期日期为2018年12月31日,那么你首先要获取到2018年12月31日的unix时间戳,使用命令”date -d 2018-12-31 +%s”获取到的时间戳为1546185600,所以,当设置expires=1546185600时,表示用户的过期时间为2018年12月31日0点0分,设置成功后,查看远程主机的/etc/shadow文件,对应用户的第八列的值将变成17895(表示1970年1月1日到2018年12月31日的天数,unix时间戳的值会自动转换为天数,我们不用手动的进行换算),目前此参数只支持在Linux和FreeBSD系统中使用。

comment参数 :此参数用于指定用户的注释信息

state参数 :此参数用于指定用户是否存在于远程主机中,可选值有present、absent,默认值为present,表示用户需要存在,当设置为absent时表示删除用户。

remove参数 :当state的值设置为absent时,表示要删除远程主机中的用户,但是在删除用户时,不会删除用户的家目录等信息,这是因为remoove参数的默认值为no,如果设置为yes,在删除用户的同时,会删除用户的家目录,当state=absent并且remove=yes时,相当于执行”userdel –remove”命令

password参数 :此参数用于指定用户的密码,但是这个密码不能是明文的密码,而是一个对明文密码”加密后”的字符串,相当于/etc/shadow文件中的密码字段,是一个对明文密码进行哈希后的字符串,你可以在python的命令提示符下输入如下命令,生成明文密码对应的加密字符串。

import crypt; crypt.crypt(‘666666’)

输入上述命令后,即可得到明文密码666666对应的加密字符串。

update_password参数:此参数有两个值可选,always和on_create,当此参数的值设置为always时表示,如果password参数设置的值与用户当前的加密过的密码字符串不一致,则直接更新用户的密码,默认值即为always,但是当此参数设置为on_create时,如果password参数设置的值与用户当前的加密过的密码字符串不一致,则不会更新用户的密码字符串,保持之前的密码设定,如果是新创建的用户,即使此参数设置为on_create,也会将用户的密码设置为password参数对应的值。

generate_ssh_key参数:此参数默认值为no,如果设置为yes,表示为对应的用户生成ssh密钥对,默认在用户家目录的./ssh目录中生成名为id_rsa的私钥和名为id_rsa.pub的公钥,如果同名的密钥已经存在与对应的目录中,原同名密钥并不会被覆盖(不做任何操作),如果你对ssh密钥还不是特别了解,可以参考如下文章

http://www.zsythink.net/archives/2375

ssh_key_file参数:当generate_ssh_key参数的值为yes时,使用此参数自定义生成ssh私钥的路径和名称,对应公钥会在同路径下生成,公钥名以私钥名开头,以”.pub”结尾。

ssh_key_comment参数:当generate_ssh_key参数的值为yes时,在创建证书时,使用此参数设置公钥中的注释信息,但是如果同名的密钥对已经存在,则并不会修改原来的注释信息,即不做任何操作,当不指定此参数时,默认的注释信息为”ansible-generated on 远程主机的主机名”

ssh_key_passphrase参数:当generate_ssh_key参数的值为yes时,在创建证书时,使用此参数设置私钥的密码,但是如果同名的密钥对已经存在,则并不会修改原来的密码,即不做任何操作

ssh_key_type参数:当generate_ssh_key参数的值为yes时,在创建证书时,使用此参数设置密钥对的类型,默认密钥类型为rsa,但是如果同名的密钥对已经存在,并不会对同名密钥做任何操作

user模块的ad-hoc示例命令如下:

在test70主机上创建名为zsy的用户,如果用户已经存在,则不进行任何操作。

ansible test70 -m user -a 'name=zsy'

在test70主机上删除名为zsy的用户,但是不会删除zsy用户的家目录

ansible test70 -m user -a 'name=zsy state=absent'

在test70主机上删除名为zsy的用户,同时会删除zsy用户的家目录等信息

ansible test70 -m user -a 'name=abc state=absent remove=yes'

指定test70主机上的zsy用户的主组为zsythink,zsythink组需要提前存在,当不使用group设置主组时,默认主组与用户名相同。

ansible test70 -m user -a "name=zsy group=zsythink"

指定test70主机上的zsy用户的附加组为zsythink,zsythink组需要提前存在,当不使用groups设置附属组时,默认附加组与用户名相同,注意,为了保险起见,在不知道用户原来的附加组设定的情况下,最好将append参数设置为yes,我们也可以一次性设置多个附加组,附加组之间用逗号隔开,比如groups=zsy,zsythink,root 示例命令如下

ansible test70 -m user -a "name=zsy groups=zsythink append=yes"

指定test70主机上的zsy用户使用/bin/csh作为默认shell

ansible test70 -m user -a "name=zsy shell=/bin/csh"

指定test70主机上的zsy用户的uid为2002

ansible test70 -m user -a "name=zsy uid=2002"

指定test70主机上的zsy用户的过期时间为2018年12月31日,使用”date -d 2018-12-31 +%s”命令可以获取到对应日期的unix时间戳

ansible test70 -m user -a 'name=zsy expires=1546185600'

指定test70主机上的zsy用户的注释信息

ansible test70 -m user -a 'name=zsy comment="www.zsythink.net"'

将test70主机上的zsy用户的密码设置为666666

首先生成666666的加密字符串

[root@test71 ~]# python;
Python 2.7.5 (default, Aug  4 2017, 00:39:18)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import crypt; crypt.crypt('666666')
'$6$ygRbo7Fj.mMU2KY0$OEqihCCn5UfOsvMyzPNPBgx3bzAtwrOFyFvacgUmA374XOAEtUCrdjbW5Ip.Zqo491o3kD5I.HaC9nLhh6x741'

使用生成的密码字符串设置用户密码

ansible test70 -m user -a ' name=zsy password="$6$ygRbo7Fj.mMU2KY0$OEqihCCn5UfOsvMyzPNPBgx3bzAtwrOFyFvacgUmA374XOAEtUCrdjbW5Ip.Zqo491o3kD5I.HaC9nLhh6x741" '

如下命令表示设置test70主机上的zsy用户的密码,但是如果用户当前的加密字符串与命令中设置的加密字符串不一致,则不进行密码更新的操作。

ansible test70 -m user -a 'name=zsy password="$6$a.ofrhIWn4gJGbi0$i6Xhr.F/YyhMe2UCodydwyF952bP4DOf0qYcGE8aK.EsgOR/GKU0Oy9Ov6oIH3RIJ9BnhvoVR9ozflmUJgxhL0" update_password=on_create'

为test70上的zsy用户生成ssh密钥对,默认在用户家目录的./ssh目录中生成名为id_rsa的私钥和名为id_rsa.pub的公钥,如果已经存在同名密钥,并不会覆盖原来的密钥,即不做任何操作。

ansible test70 -m user -a 'name=zsy generate_ssh_key=yes'

为test70上的zsy用户生成ssh密钥对,密钥对生成在/opt目录下,私钥名为id_rsa_zsy,公钥名为id_rsa_zsy.pub

ansible test70 -m user -a 'name=zsy generate_ssh_key=yes ssh_key_file=/opt/id_rsa_zsy'

为test70上的zsy用户生成ssh密钥对,同时指定公钥中的注释信息为”www.zsythink.net”,此参数只能在创建密钥时使用才会生效,并不能操作同名的老密钥

ansible test70 -m user -a 'name=zsy generate_ssh_key=yes ssh_key_comment="www.zsythink.net"'

为test70上的zsy用户生成ssh密钥对,同时指定私钥的密码为123456,此参数只能在创建密钥时使用才会生效,并不能操作同名的老密钥

ansible test70 -m user -a 'name=zsy generate_ssh_key=yes ssh_key_passphrase="123456"'

为test70上的zsy用户生成ssh密钥对,同时指定密钥对的类型为dsa,当不显式指定密钥类型时,默认类型为rsa,此参数只能在创建密钥时使用才会生效,并不能操作同名的老密钥

ansible test70 -m user -a 'name=zsy generate_ssh_key=yes ssh_key_type=dsa'

group模块

group模块可以帮助我们管理远程主机上的组。

此处我们介绍一些group模块的常用参数,你可以先对这些参数有一个大概了解,然后再看小示例。

name参数 :必须参数,用于指定要操作的组名称。

state参数 :用于指定组的状态,两个值可选,present,absent,默认为present,设置为absent表示删除组。

gid参数 :用于指定组的gid

group模块的ad-hoc示例命令如下:

确保test70主机中存在名为zsythink的组

ansible test70 -m group -a ' name=zsythink'

删除test70主机中存在名为zsythink的组,删除成功的前提是不能有用户把被删除的组当成主组。

ansible test70 -m group -a ' name=zsythink state=absent'

确保test70主机中存在名为zsythink的组,并且确定zsythink组的组id为1008

ansible test70 -m group -a 'name=zsythink gid=1008'

ansible笔记(7):常用模块之系统类模块

前文中,我们介绍了一些常用的文件类模块和命令类模块,这篇文章中我们会介绍一些常用的系统类操作模块。

cron模块

cron模块可以帮助我们管理远程主机中的计划任务,功能相当于crontab命令。

在了解cron模块的参数之前,先写出一些计划任务的示例,示例如下

#示例1

5 1 * * * echo test

#示例2

1 1 */3 * * echo test

#示例3

@reboot echo test

#示例4

@hourly echo test

上述示例1表示每天的1点5分输出test字符

上述示例2表示每3天执行一次计划任务,于当天的1点1分执行,具体任务为输出test字符

上述示例3表示每次系统启动后需要执行一次计划任务,具体任务为输出test字符

上述示例4表示每小时执行一次计划任务,具体任务 为输出test字符

根据上述示例,可以更好的了解cron模块的参数

cron模块通常使用的参数如下,你可以先大概的了解一下这些参数,然后再结合后面的示例去理解:

minute参数 :此参数用于设置计划任务中分钟设定位的值,比如,上述示例1中分钟设定位的值为5,即minute=5,当不使用此参数时,分钟设定位的值默认为”*”

hour参数 :此参数用于设置计划任务中小时设定位的值,比如,上述示例1中小时设定位的值为1,即hour=1,当不使用此参数时,小时设定位的值默认为”*”

day参数 :此参数用于设置计划任务中日设定位的值,当不使用此参数时,日设定位的值默认为”*”

month参数 :此参数用于设置计划任务中月设定位的值,当不使用此参数时,月设定位的值默认为”*”

weekday参数 :此参数用于设置计划任务中周几设定位的值,当不使用此参数时,周几设定位的值默认为”*”

special_time参数 :在上述示例3与示例4中,计划任务的时间设定格式为@reboot或者@hourly,@reboot表示重启时执行,@hourly表示每小时执行一次,相当于设置成”0 0 * * *” ,这种@开头的时间设定格式则需要使用special_time参数进行设置,special_time参数的可用值有reboot(重启后)、yearly(每年)、annually(每年,与yearly相同)、monthly(每月)、weekly(每周)、daily(每天)、hourly(每时)。

注意:当上述时间单位设定参数都未指定时,计划任务的时间设定默认会被设定为”* * * * *”,这样表示每秒都会执行一次计划任务,所以,在使用cron模块时,我们应该确定对应的时间参数设置正确。

user参数 :此参数用于设置当前计划任务属于哪个用户,当不使用此参数时,默认为管理员用户

job参数 :此参数用于指定计划的任务中需要实际执行的命令或者脚本,比如上例中的”echo test”命令。

name参数 :此参数用于设置计划任务的名称,计划任务的名称会在注释中显示,当不指定计划任务的名称时,ansible会默认为计划任务加入注释,注释的内容为#Ansible: None,假设指定计划任务的名称为test,那么注释的内容为#Ansible: test,在一台机器中,计划任务的名称应该具有唯一性,方便我们以后根据名称修改或删除计划任务。

state参数 :当计划任务有名称时,我们可以根据名称修改或删除对应的任务,当删除计划任务时,需要将state的值设置为absent

disabled参数 :当计划任务有名称时,我们可以根据名称使对应的任务”失效”(注释掉对应的任务),注意,使用此参数时,除了需要指定任务的名称,还需要同时指定任务的job以及任务的时间设定,而且任务的时间设定必须和对应任务完全相同,否则在注释任务的同时,任务的时间设定会被修改,除非你确定这样做,如果你不明白这段话的意思,可以参考下文中的示例。

backup参数 :如果此参数的值设置为yes,那么当修改或者删除对应的计划任务时,会先对计划任务进行备份,然后再对计划任务进行修改或者删除,cron模块会在远程主机的/tmp目录下创建备份文件,以crontab开头并且随机加入一些字符,具体的备份文件名称会在返回信息的backup_file字段中看到,推荐将此此参数设置为yes。

cron模块的ad-hoc示例命令如下:

在test70主机上创建计划任务,任务名称为”test crontab”,任务于每天1点5分执行,任务内容为输出test字符

ansible test70 -m cron -a " name='test crontab' minute=5 hour=1 job='echo test' "

执行上述命令后,在test70主机中root用户下会有如下计划任务被创建

#Ansible: test crontab

5 1 * * * echo test

在test70主机上创建计划任务,任务名称为”crontab day test”,任务每3天执行一次,于执行当天的1点1分开始执行,任务内容为输出test字符

ansible test70 -m cron -a " name='crontab day test' minute=1 hour=1 day=*/3 job='echo test' "

执行上述命令后,在test70主机中root用户下会有如下计划任务被创建

#Ansible: crontab day test

1 1 */3 * * echo test

在test70主机上创建计划任务,任务名称为”test special time”,任务将在重启时执行,任务内容为输出test字符

ansible test70 -m cron -a " name='test special time' special_time=reboot job='echo test' "

执行上述命令后,在test70主机中root用户下会有如下计划任务被创建

#Ansible: test special time

@reboot echo test

在test70主机上创建计划任务,任务名称为”test special time”,任务将在重启时执行,任务内容为输出test字符

ansible test70 -m cron -a " name='test special time' special_time=reboot job='echo test' "

执行上述命令后,在test70主机中root用户下会有如下计划任务被创建

#Ansible: test special time

@reboot echo test

由于”test special time”已经存在,所以,当我们再次操作同名的任务时,ansible将会认为是修改原来的任务。

在”test special time”已经存在的情况下,执行如下命令,原计划任务会被修改,因为启用了backup,所以任务在修改前会被备份。

ansible test70 -m cron -a " name='test special time' special_time=hourly job='echo test' backup=yes "

执行上述命令后,从返回信息的backup_file字段中可以看到备份文件的远程主机中的位置,原来的”test special time”任务会变成如下设定

#Ansible: test special time

@hourly echo test

任务”test special time”已经存在于test70主机中,如果我们想要删除这个计划任务,可以执行如下命令,删除任务的同时可以进行备份。

ansible test70 -m cron -a " name='test special time' state=absent backup=yes "

命令执行后,从返回信息的backup_file字段中可以看到备份文件的远程主机中的位置

默认操作root用户的计划任务,如果想要操作远程主机中其他用户的计划任务,可以指定要操作的用户

ansible test70 -m cron -a "user=zsy name='test special time' special_time=hourly job='echo test'"

上述命令执行后,可以在远程主机中使用crontab -lu zsy查看对应的计划任务。

之前已经创建了名称为test crontab的计划任务,如果我们想要暂时注释这个计划任务,可以使用如下命令,但是需要注意,在注释任务时,所有设定需要 跟原设定保持一致,否则计划任务的设置将会发生改变,示例如下

比如,我们想要将crontab day test这个任务注释掉,则需要使用如下命令,注意,最好与backup参数同时使用

ansible test70 -m cron -a " name='crontab day test' minute=1 hour=1 day=*/3 job='echo test'  disabled=yes backup=yes"

执行上述命令后,对应的计划任务将会被注释,如下

#Ansible: crontab day test

#1 1 */3 * * echo test

如果你在使用disabled参数时,设置了错误的时间,那么对应任务被注释的同时,时间设定也会发生改变,比如,如果你执行了如下命令

ansible test70 -m cron -a " name='crontab day test' minute=55 job='echo test'  disabled=yes backup=yes"

那么对应任务被注释的同时,同时还会进行如下设置

#Ansible: crontab day test

#55 * * * * echo test

如果你忘记了任何时间设定,那么在任务被注释时,还会被设置为默认的时间设定,也就是 “* * * * *”

所以,在使用disabled参数时,最后结合backup参数一起使用,万一一时大意,还有回旋的余地。

service模块

service模块可以帮助我们管理远程主机上的服务,比如,启动或停止远程主机中的nginx服务。

注意:假如你想要管理远程主机中的某个服务,那么这个服务必须能被 BSD init, OpenRC, SysV, Solaris SMF, systemd, upstart 中的任意一种所管理,否则service模块也无法管理远程主机的对应服务,这样说可能不容易理解,那么我们换个方式来解释,假设你在使用centos6,那么你的centos6中的nginx则必须能够通过”service nginx start”启动,如果你的nginx无法通过”service nginx start”进行启动,那么它将同样无法通过ansible的service模块启动,假设你在使用centos7,那么你的centos7中的nginx则必须能够通过”systemctl start nginx”启动,如果它无法通过”systemctl start nginx”进行启动,那么它将同样无法通过ansible的service模块进行启动,centos6中默认通过sysv管理服务,centos7中默认通过systemd管理服务,如果你的服务无法通过 BSD init, OpenRC, SysV, Solaris SMF, systemd, upstart 中的任意一种所管理,那么它也无法被ansible的service模块管理。

service模块通常使用的参数如下,你可以先大概的了解一下这些参数,然后再结合后面的示例去理解:

name参数 :此参数用于指定需要操作的服务名称,比如nginx

state参数 :此参数用于指定服务的状态,比如,我们想要启动远程主机中的nginx,则可以将state的值设置为started,如果想要停止远程主机中的服务,则可以将state的值设置为stopped,此参数的可用值有started、stopped、restarted、reloaded。

enabled参数 :此参数用于指定是否将服务设置为开机 启动项,设置为yes表示将对应服务设置为开机启动,设置为no表示不会开机启动。

service模块的ad-hoc示例命令如下:

将test70中的nginx服务处于启动状态

ansible test70 -m service -a "name=nginx state=started"

将test70中的nginx服务处于停止状态

ansible test70 -m service -a "name=nginx state=stopped"

将test70中的nginx服务被设置为开机自动启动项

ansible test70 -m service -a " name='nginx' enabled=yes"

ansible笔记(6):常用模块之命令类模块

前文中,我们介绍了一些常用的文件类操作的模块,这篇文章中我们会介绍一些常用的命令类操作的模块。

command模块

command模块可以帮助我们在远程主机上执行命令

注意:使用command模块在远程主机中执行命令时,不会经过远程主机的shell处理,在使用command模块时,如果需要执行的命令中含有重定向、管道符等操作时,这些符号也会失效,比如”<“, “>”, “|”, “;” 和 “&” 这些符号,如果你需要这些功能,可以参考后面介绍的shell模块,还有一点需要注意,如果远程节点是windows操作系统,则需要使用win_command模块。

此处我们介绍一些command模块的常用参数,你可以先对这些参数有一个大概了解,然后再看小示例。

free_form参数 :必须参数,指定需要远程执行的命令,需要说明一点,free_form参数与其他参数并不相同,在之前的模块示例中,如果想要使用一个参数,那么则需要为这个参数赋值,举个例子,之前的示例模块中,大多都有path参数,当我们需要指定要操作的文件时,通常需要对path参数赋值,比如,path=/testdir/test,表示我们想要操作/testdir/test文件,但是free_form参数则不同,”free_form”并不是一个”实际存在”的参数名,比如,当我们想要在远程主机上执行ls命令时,我们并不需要写成”free_form=ls” ,这样写反而是错误的,因为并没有任何参数的名字是free_form,当我们想要在远程主机中执行ls命令时,直接写成ls即可,这就是free_form参数的含义,因为command模块的作用是执行命令,所以,任何一个可以在远程主机上执行的命令都可以被称为free_form,如果你还是不明白,看下面的小示例就行了。

chdir参数 : 此参数的作用就是指定一个目录,在执行对应的命令之前,会先进入到chdir参数指定的目录中。

creates参数 :看到creates,你可能会从字面上理解这个参数,但是使用这个参数并不会帮助我们创建文件,它的作用是当指定的文件存在时,就不执行对应命令,比如,如果/testdir/test文件存在,就不执行我们指定的命令。

removes参数 :与creates参数的作用正好相反,它的作用是当指定的文件不存在时,就不执行对应命令,比如,如果/testdir/tests文件不存在,就不执行我们指定的命令,此参数并不会帮助我们删除文件

对应上述参数的ad-hoc示例命令如下:

使用如下命令,表示在test70主机上执行ls命令,因为我使用的是root用户,所以默认情况下,ls出的结果是test70主机中root用户家目录中的文件列表。

ansible test70 -m command -a "ls"


chdir参数表示执行命令之前,会先进入到指定的目录中,所以如下命令表示查看test70主机上/testdir目录中的文件列表

ansible test70 -m command -a “chdir=/testdir ls”


如下命令表示/testdir/test文件如果存在于远程主机中,则不执行对应命令,如果不存在,才执行"echo test"命令

ansible test70 -m command -a “creates=/testdir/test echo test”

如下命令表示/testdir/test文件如果不存在于远程主机中,则不执行对应命令,如果存在,才执行”echo test”命令

ansible test70 -m command -a "removes=/testdir/test echo test"

shell模块

shell模块可以帮助我们在远程主机上执行命令,与command模块不同的是,shell模块在远程主机中执行命令时,会经过远程主机上的/bin/sh程序处理。

学习此模块之前,请先参考本文中的command模块。

此处我们介绍一些shell模块的常用参数。

free_form参数 :必须参数,指定需要远程执行的命令,但是并没有具体的一个参数名叫free_form,具体解释参考command模块。

chdir参数 : 此参数的作用就是指定一个目录,在执行对应的命令之前,会先进入到chdir参数指定的目录中。

creates参数 :使用此参数指定一个文件,当指定的文件存在时,就不执行对应命令,可参考command模块中的解释。

removes参数 :使用此参数指定一个文件,当指定的文件不存在时,就不执行对应命令,可参考command模块中的解释。

executable参数 :默认情况下,shell模块会调用远程主机中的/bin/sh去执行对应的命令,通常情况下,远程主机中的默认shell都是bash,如果你想要使用其他类型的shell执行命令,则可以使用此参数指定某种类型的shell去执行对应的命令,指定shell文件时,需要使用绝对路径。

shell模块中chdir、creates、removes参数的作用与command模块中的作用都是相同的,此处不再举例。

使用shell模块可以在远程服务器上执行命令,它支持管道与重定向等符号。

ansible test70 -m shell -a "chdir=/testdir echo test > test"

如果你想要执行的命令需要csh解析,那么可以指定使用csh在远程主机上执行对应的命令,比如在如下示例中,我们使用csh的语法定义了一个数字类型的变量TestNum,然后将TestNum变量的值重定向到了/testdir/TestNumFile,在bash中,@符号不能用于定义变量,所以,可以使用executable指定需要的shell类型。

ansible test70 -m shell -a 'executable=/bin/csh @ TestNum=666 ; echo $TestNum > /testdir/TestNumFile'

script模块

script模块可以帮助我们在远程主机上执行ansible主机上的脚本,也就是说,脚本一直存在于ansible主机本地,不需要手动拷贝到远程主机后再执行。

学习此模块之前,请先参考本文中的command模块。

此处我们介绍一些script模块的常用参数,你可以先对这些参数有一个大概了解,然后再看小示例。

free_form参数 :必须参数,指定需要执行的脚本,脚本位于ansible主机本地,并没有具体的一个参数名叫free_form,具体解释参考command模块。

chdir参数 : 此参数的作用就是指定一个远程主机中的目录,在执行对应的脚本之前,会先进入到chdir参数指定的目录中。

creates参数 :使用此参数指定一个远程主机中的文件,当指定的文件存在时,就不执行对应脚本,可参考command模块中的解释。

removes参数 :使用此参数指定一个远程主机中的文件,当指定的文件不存在时,就不执行对应脚本,可参考command模块中的解释。

上述参数对应的ad-hoc示例命令如下:

如下命令表示ansible主机中的/testdir/atest.sh脚本将在test70主机中执行,执行此脚本之前,会先进入到test70主机中的/opt目录

ansible test70 -m script -a "chdir=/opt /testdir/atest.sh"

如下命令表示,如果test70主机中的/opt/testfile文件已经存在,ansible主机中的/testdir/atest.sh脚本将不会在test70主机中执行,反之则执行。

ansible test70 -m script -a "creates=/opt/testfile /testdir/atest.sh"

如下命令表示,如果test70主机中的/opt/testfile文件不存在,ansible主机中的/testdir/atest.sh脚本将不会在test70主机中执行,反之则执行。

ansible test70 -m script -a "removes=/opt/testfile /testdir/atest.sh"