解决新装 ubuntu 无法运行 ansible 的问题

尽管用 CentOS 的人多,但我是推崇 Ubuntu Server,简洁易用,包管理可靠稳健。

但是,用 ubuntu server xenial 或以上的版本,就会遇到一个新装的系统,无法执行 ansible 的问题。因为 ubuntu xenial 以上版本已经默认使用 python3 了。通常的解决方法就是安装 python-minimal 包。

最近设计一套 ansible 脚本时,预设的环境是离线内网集群,那么如何解决这个问题呢?我编写了一个初始化 playbook 代码。

以下我的 hosts 内容,ubuntu_hosts是空的:

[servers]
centos7 ansible_host=192.168.1.146 ansible_ssh_port=22
ubuntu16 ansible_host=192.168.3.231 ansible_ssh_port=22

[ubuntu_hosts]

以下我的 playbook 内容:

- hosts: all
  become: yes
  gather_facts: no
  tasks:
    - include_role:
        name: init
        tasks_from: check_python.yml

- hosts: all
  become: yes
  roles:
    - init

我们来看看 init 里的 check_python.yml 做了什么动作:

- block:
    - name: testing python2
      raw: test -e /usr/bin/python
      when: ansible_python_interpreter is not defined
  rescue:
    - name: testing python3
      raw: test -e /usr/bin/python3
    - name: set python3 as default python command
      lineinfile:
        path: hosts
        line: '{{ inventory_hostname }} ansible_python_interpreter=/usr/bin/python3'
        insertafter: '[ubuntu_hosts]'
      delegate_to: localhost
      become: no
    - meta: clear_host_errors
    - meta: refresh_inventory
  1. 首先检查是否有 python 命令。

  2. 如果检查失败,那么执行 resuce 部分的 tasks,把这台机器写入到 hosts 文件里,指定使用 python3 作为交互命令。

  3. 清除错误,刷新 inventory 列表。为之后的动作扫清障碍。

这样我们就得到了一个新的 hosts 列表:

[servers]
centos7 ansible_host=192.168.1.146 ansible_ssh_port=22
ubuntu16 ansible_host=192.168.3.231 ansible_ssh_port=22

[ubuntu_hosts]
ubuntu16 ansible_python_interpreter=/usr/bin/python3

以下是输出截图

未分类

原本我是用了个偷懒的方法,直接在 Ubuntu Xenial 上建一个软链 /usr/bin/python 指向 /usr/bin/python3 的,也能解决 ansible 的执行问题。但考虑到这样可能会给以后运行其它 python 应用挖坑,所以还是稍微折腾一下,确保我们不改动系统级别的命令工具。

ansible笔记(4):常用模块之文件操作

在本博客中,ansible是一个系列文章,我们会尽量以通俗易懂的方式总结ansible的相关知识点。

ansible系列博文直达链接:ansible轻松入门系列

“ansible系列”中的每篇文章都建立在前文的基础之上,所以,请按照顺序阅读这些文章,否则有可能在阅读中遇到障碍。

前文中,我们已经介绍了怎样使用模块,而且我们知道,ansible有很多模块,每个模块都有自己的功能,”模块”涉及到的方向比较多,所以对于个人来说,并没有必要了解所有的模块,我们只需要根据实际的业务场景了解相应的模块即可,而且ansible比较贴心,ansible根据模块的功能对这些模块进行了大致的分类,比如,如果某些模块都是操作文件的,就把它们分类到文件类模块中,如果某些模块都是操作数据库的,就把他们分类到数据库类模块中,那么,ansible把模块分为了哪些类呢?你可以参考官方手册,找到答案,模块的分类目录如下
http://docs.ansible.com/ansible/latest/modules_by_category.html

上述链接不仅对模块进行了分类,还给出了每个模块的使用示例,不过这些示例都是编写”剧本(playbook)”的示例,我们还没有介绍过怎样编写剧本,所以暂时不会参考这些示例,前文的示例中,我们一直以命令的方式运行ansible,通过命令的方式调用模块,这种直接在命令行中运行的ansible命令被称作为”ad-hoc命令”,我们可以通过”ad-hoc命令”的方式,快速的了解一个模块。

在前文的示例中,我们已经了解了ping模块与fetch模块,那么这篇文章中,我们来了解一些新的模块,这些模块都是常用的操作文件的模块,不过这篇文章写得并不全面,后续有空会继续补充总结,对等更新的朋友说声见谅,最近比较忙。

copy模块

见名知义,copy模块的作用就是拷贝文件,它与之前介绍的fetch模块类似,不过,fetch模块是从远程主机中拉取文件到ansible

主机,而copy模块是将ansible主机上的文件拷贝到远程主机中。

此处我们介绍一些copy模块的常用参数,然后再给出对应示例。

  • src参数 :用于指定需要copy的文件或目录

  • dest参数 :用于指定文件将被拷贝到远程主机的哪个目录中,dest为必须参数

  • content参数 :当不使用src指定拷贝的文件时,可以使用content直接指定文件内容,src与content两个参数必有其一,否则会报错。

  • force参数 : 当远程主机的目标路径中已经存在同名文件,并且与ansible主机中的文件内容不同时,是否强制覆盖,可选值有yes和no,默认值为yes,表示覆盖,如果设置为no,则不会执行覆盖拷贝操作,远程主机中的文件保持不变。

  • backup参数 : 当远程主机的目标路径中已经存在同名文件,并且与ansible主机中的文件内容不同时,是否对远程主机的文件进行备份,可选值有yes和no,当设置为yes时,会先备份远程主机中的文件,然后再将ansible主机中的文件拷贝到远程主机。

  • owner参数 : 指定文件拷贝到远程主机后的属主,但是远程主机上必须有对应的用户,否则会报错。

  • group参数 : 指定文件拷贝到远程主机后的属组,但是远程主机上必须有对应的组,否则会报错。

  • mode参数 : 指定文件拷贝到远程主机后的权限,如果你想将权限设置为”rw-r–r–“,则可以使用mode=0644表示,如果你想要在user对应的权限位上添加执行权限,则可以使用mode=u+x表示。

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

将ansible主机中/testdir/copytest文件复制到远程主机的/opt目录下

ansible test70 -m copy -a "src=/testdir/copytest dest=/opt/"

在远程主机的/opt目录下生成文件test,test文件中有两行文本,第一行文本为aaa,第二行为bbb,当使用content指定文件内容时,dest参数对应的值必须是一个文件,而不能是一个路径。

ansible test70 -m copy -a 'content="aaanbbbn" dest=/opt/test'

将ansible主机中/testdir/copytest文件复制到远程主机的/opt目录中时,如果远程主机中已经存在/opt/copytest文件,并且文件内容与ansible主机中的copytest文件的内容不一致,则不执行拷贝操作,远程主机中的/opt/copytest文件内容不会被改变。

ansible test70 -m copy -a "src=/testdir/copytest dest=/opt/ force=no"

将ansible主机中/testdir/copytest文件复制到远程主机的/opt目录中时,如果远程主机中已经存在/opt/copytest文件,并且文件内容与ansible主机中的copytest文件的内容不一致,会执行拷贝操作,但是在执行拷贝操作之前,会将远程主机中的原文件重命名,以作备份,然后再进行拷贝操作。

ansible test70 -m copy -a "src=/testdir/copytest dest=/opt/ backup=yes"

拷贝文件时,指定文件的属主,需要注意,远程主机上必须存在对应的用户。

ansible test70 -m copy -a "src=/testdir/copytest dest=/opt/ owner=zsy"

拷贝文件时,指定文件的属组,需要注意,远程主机上必须存在对应的组。

ansible test70 -m copy -a "src=/testdir/copytest dest=/opt/ group=zsy"

拷贝文件时,指定文件的权限

ansible test70 -m copy -a "src=/testdir/copytest dest=/opt/ mode=0640"

file模块

file模块可以帮助我们完成一些对文件的基本操作,比如,创建文件或目录、删除文件或目录、修改文件权限等

此处我们介绍一些file模块的常用参数,然后再给出对应示例。

  • path参数 :必须参数,用于指定要操作的文件或目录,在之前版本的ansible中,使用dest参数或者name参数指定要操作的文件或目录,为了兼容之前的版本,使用dest或name也可以。

  • state参数 :此参数非常灵活,此参数对应的值需要根据情况设定,比如,当我们需要在远程主机中创建一个目录的时候,我们需要使用path参数指定对应的目录路径,假设,我想要在远程主机上创建/testdir/a/b目录,那么我则需要设置path=/testdir/a/b,但是,我们无法从”/testdir/a/b”这个路径看出b是一个文件还是一个目录,ansible也同样无法单单从一个字符串就知道你要创建文件还是目录,所以,我们需要通过state参数进行说明,当我们想要创建的/testdir/a/b是一个目录时,需要将state的值设置为directory,”directory”为目录之意,当它与path结合,ansible就能知道我们要操作的目标是一个目录,同理,当我们想要操作的/testdir/a/b是一个文件时,则需要将state的值设置为touch,当我们想要创建软链接文件时,需将state设置为link,想要创建硬链接文件时,需要将state设置为hard,当我们想要删除一个文件时(删除时不用区分目标是文件、目录、还是链接),则需要将state的值设置为absent,”absent”为缺席之意,当我们想让操作的目标”缺席”时,就表示我们想要删除目标。

  • src参数 :当state设置为link或者hard时,表示我们想要创建一个软链或者硬链,所以,我们必须指明软链或硬链链接的哪个文件,通过src参数即可指定链接源。

  • force参数 : 当state=link的时候,可配合此参数强制创建链接文件,当force=yes时,表示强制创建链接文件,不过强制创建链接文件分为两种情况,情况一:当你要创建的链接文件指向的源文件并不存在时,使用此参数,可以先强制创建出链接文件。情况二:当你要创建链接文件的目录中已经存在与链接文件同名的文件时,将force设置为yes,回将同名文件覆盖为链接文件,相当于删除同名文件,创建链接文件。情况三:当你要创建链接文件的目录中已经存在与链接文件同名的文件,并且链接文件指向的源文件也不存在,这时会强制替换同名文件为链接文件。

  • owner参数 :用于指定被操作文件的属主,属主对应的用户必须在远程主机中存在,否则会报错。

  • group参数 :用于指定被操作文件的属组,属组对应的组必须在远程主机中存在,否则会报错。

  • mode参数:用于指定被操作文件的权限,比如,如果想要将文件权限设置为”rw-r-x—“,则可以使用mode=650进行设置,或者使用mode=0650,效果也是相同的,如果你想要设置特殊权限,比如为二进制文件设置suid,则可以使用mode=4700,很方便吧。

  • recurse参数:当要操作的文件为目录,将recurse设置为yes,可以递归的修改目录中文件的属性。

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

在test70主机上创建一个名为testfile的文件,如果testfile文件已经存在,则会更新文件的时间戳,与touch命令的作用相同。

ansible test70 -m file -a "path=/testdir/testfile state=touch"

在test70主机上创建一个名为testdir的目录,如果testdir目录已经存在,则不进行任何操作。

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

在test70上为testfile文件创建软链接文件,软链接名为linkfile,执行下面命令的时候,testfile已经存在。

ansible test70 -m file -a "path=/testdir/linkfile state=link src=/testdir/testfile"

在test70上为testfile文件创建硬链接文件,硬链接名为hardfile,执行下面命令的时候,testfile已经存在。

ansible test70 -m file -a "path=/testdir/hardfile state=hard src=/testdir/testfile"

在创建链接文件时,如果源文件不存在,或者链接文件与其他文件同名时,强制覆盖同名文件或者创建链接文件,参考上述force参数的解释。

ansible test70 -m file -a "path=/testdir/linkfile state=link src=sourcefile force=yes"

删除远程机器上的指定文件或目录

ansible test70 -m file -a "path=/testdir/testdir state=absent"

在创建文件或目录的时候指定属主,或者修改远程主机上的文件或目录的属主。

ansible test70 -m file -a "path=/testdir/abc state=touch owner=zsy"
ansible test70 -m file -a "path=/testdir/abc owner=zsy"
ansible test70 -m file -a "path=/testdir/abc state=directory owner=zsy"

在创建文件或目录的时候指定属组,或者修改远程主机上的文件或目录的属组。

ansible test70 -m file -a "path=/testdir/abb state=touch group=zsy"
ansible test70 -m file -a "path=/testdir/abb group=zsy"
ansible test70 -m file -a "path=/testdir/abb state=directory group=zsy"

在创建文件或目录的时候指定权限,或者修改远程主机上的文件或目录的权限。

ansible test70 -m file -a "path=/testdir/abb state=touch mode=0644"
ansible test70 -m file -a "path=/testdir/abb mode=0644"
ansible test70 -m file -a "path=/testdir/binfile mode=4700"
ansible test70 -m file -a "path=/testdir/abb state=directory mode=0644"

当操作远程主机中的目录时,同时递归的将目录中的文件的属主属组都设置为zsy。

ansible test70 -m file -a "path=/testdir/abd state=directory owner=zsy group=zsy recurse=yes"

ansible笔记(3):ansible模块的基本使用

在本博客中,ansible是一个系列文章,我们会尽量以通俗易懂的方式总结ansible的相关知识点。

ansible系列博文直达链接:ansible轻松入门系列

“ansible系列”中的每篇文章都建立在前文的基础之上,所以,请按照顺序阅读这些文章,否则有可能在阅读中遇到障碍。

在前文的基础上,我们已经知道,当我们使用ansible完成实际任务时,需要依靠ansible的各个模块,比如,我们想要去ping某主机,则需要使用ping模块,命令如下

ansible all -m ping

前文说过,除了ping模块,ansible还有很多模块可供我们使用,那么ansible都有哪些模块呢?我们可以使用如下命令,查看ansible都有哪些模块。

ansible-doc  -l

执行上述命令后,可以看到ansible中各个模块的名称,以及模块的大概功能,当然,通过”ansible-doc -l”命令获取到的模块信息比较概括,并不是特别详细,如果想要获取到各个模块更加详细的用法,可以使用“ansible-doc -s”命令,比如,我们想要获取ping模块的详细使用方法,则可以使用如下命令查看

ansible-doc -s ping

即使使用“ansible-doc -s ping”命令查看ping模块的信息,得到的信息也是比较少的,这是因为ping模块本来就比较简单,而且ping模块并没有太多参数可用,但是并非所有模块都像ping模块一样简单,有的模块在使用时必须使用参数,比如 fetch 模块,见名知义,fetch为”拿来”之意,当我们需要将受管主机中的文件拉取到ansible主机时,则可以使用此模块,首先,我们可以使用“ansible-doc -s fetch”命令,查看一下fetch模块的用法,如下图所示

未分类

从帮助信息中可以看出,fetch模块的作用就是”Fetches a file from remote nodes”,即”从受管主机中拉取文件”之意,而且fetch模块提供了一些参数供我们使用,我们可用的参数有 dest、fail_on_missing、flat、src、validate_checksum ,如上图所示,返回信息中注释了每个参数的作用。

比如src参数,src参数的作用就是指定从受管主机中拉取哪个文件。

比如dest参数,dest参数的作用就是指定拉取文件到本地以后文件存放的位置。

细心如你一定发现了,在上图中,dest参数和src参数的注释中都包含”(required)”字样,这表示,在使用fetch模块时,dest参数与src参数是必须提供的,如果在使用fetch模块时,没有提供这两个参数,将会报错,想想也对,如果我们想要从远程主机中拉取文件,那么我们必须告诉ansible,从哪里拉取文件,拉取后将文件存放到哪里吧,所以,在学习怎样使用一个模块时,要注意这些必选参数,那么,我们就从fetch模块入手,看看怎样使用带有参数的模块吧~

在开始之前,先来看一下我们的主机清单配置,配置如下

[testA]
test60 ansible_host=10.1.1.60
test61 ansible_host=10.1.1.61

[testB]
test70 ansible_host=10.1.1.70

[test:children]
testA
testB

假如我们想要将testA组中所有主机的/etc/fstab文件拉取到本地,则可以使用如下命令

ansible testA -m fetch -a "src=/etc/fstab dest=/testdir/ansible/"

如上述命令所示,-m选项用于调用指定的模块,”-m fetch”表示调用fetch模块,

-a选项用于传递模块所需要使用的参数, -a “src=/etc/fstab dest=/testdir/ansible/”表示我们在使用fetch模块时,为fetch模块传入了两个参数,src与dest。

那么,我们一起来看一下上述命令的执行效果吧,如下

未分类

从命令的执行结果可以看出,上述命令执行成功了,因为两个主机对应的返回信息都返回了”SUCCESS”字样。

你可能会有疑问,为什么命令执行成功了,返回的信息却是”黄色”的,在我们的印象中,执行成功,返回的信息不应该是”绿色”的吗?这是为什么呢?此处我们暂且不讨论这个话题,后面我们再行解释。

从返回信息可以看出,执行上述ansible命令后,主机test60和主机test61中的文件已经拉取成功,test61的fstab文件被拷贝到了本机的/testdir/ansible目录中,而且,ansible在/testdir/ansible目录中自动创建了目录结构 test61/etc/,由于我们是同时从多台受管主机中拉取相同名称的文件,所以ansible会自动为我们创建各个主机对应的目录,以区分存放不同主机中的同名文件,有没有觉得很方便,很人性化呢~?

之前说过,ansible具有幂等性,幂等性能够保证我们重复的执行一项操作时,得到的结果是相同的,我们再来回顾一下幂等性的概念。

“幂等性”是什么意思呢?举个例子,你想把一个文件拷贝到目标主机的某个目录上,但是你不确定此目录中是否已经存在此文件,当你使用ansible完成这项任务时,就非常简单了,因为如果目标主机的对应目录中已经存在此文件,那么ansible则不会进行任何操作,如果目标主机的对应目录中并不存在此文件,ansible就会将文件拷贝到对应目录中,说白了,ansible是”以结果为导向的”,我们指定了一个”目标状态”,ansible会自动判断,”当前状态”是否与”目标状态”一致,如果一致,则不进行任何操作,如果不一致,那么就将”当前状态”变成”目标状态”,这就是”幂等性”,”幂等性”可以保证我们重复的执行同一项操作时,得到的结果是一样的。

那么我们就来实验一下,看看重复执行相同的ansible命令时,会得到什么效果,效果如下图所示

未分类

从上图可以看出,返回信息仍然包含”SUCCESS”字样,证明ansible命令执行成功,不过很明显,这次的返回信息为”绿色”,而且细心如你一定发现了,这次绿色的返回信息中,”changed”字段的值为false,而之前黄色的返回信息中,”changed”字段的值为true。

当返回信息为绿色时,”changed”为false,表示ansible没有进行任何操作,没有”改变什么”。

当返回信息为黄色时,”changed”为true,表示ansible执行了操作,”当前状态”已经被ansible改变成了”目标状态”。

没错,这就是幂等性的体现,当第一次执行上述命令时,ansible发现当前主机中并没有我们需要的fstab文件,ansible就会按照我们指定的操作,拉取fstab文件,也就是说,ansible”改变”了”当前状态”,将当前”没有fstab文件的状态”变为了”有fstab文件的状态”,当我们再次执行同样的命令时,ansible发现对应文件已经存在与对应目录中,于是ansible并没有做出任何操作,也没有进行任何改变,因为”当前状态”与我们预期的”目标状态”一致,没有必要再做出重复的无用功。

看到这里,你应该已经明白,为什么执行ansible命令时,会返回黄色的成功信息或者绿色的成功信息了吧?我们可以通过返回信息的颜色,更加精准的判断执行命令之前的状态是否与我们预期的一致。

从返回信息中可以看到,当ansible进行fetch操作时,会对对应文件进行哈希计算,算出文件哈希值,也就是说,如果我们改变了文件中的内容,哈希值也将随之发生改变,这个时候,即使对应目录中存在同名的文件,ansible也会判断出两个文件属于不同的文件,因为它们的哈希值并不相同,我们来实验一下,操作如下

未分类

如上图所示,我们在/testdir/ansible/test61/etc/fstab文件的尾部加入一个”空格”,以改变文件内容,然后又执行了fetch命令,我们发现,test61的返回信息为黄色,test60主机的返回信息为绿色,证明ansible已经做出了正确的判断,将修改过的文件替换了,替换为重新拉取的文件。

小结

我们对上文的一些命令进行总结,方便以后回顾

列出ansible所支持的模块

ansible-doc -l

查看模块的详细帮助信息,比如查看fetch模块的帮助

ansible-doc -s fetch

调用模块,比如调用ping模块

ansible all -m ping

调用模块的同时传入模块所需要的参数,以fetch模块为例

ansible 10.1.1.60 -m fetch -a "src=/etc/fstab dest=/testdir/ansible/"

关于ansible模块的基本使用,我们暂时先介绍到这里,希望能够对你有所帮助~

ansible笔记(2):清单配置详解

在本博客中,ansible是一个系列文章,我们会尽量以通俗易懂的方式总结ansible的相关知识点。

ansible系列博文直达链接:ansible轻松入门系列

“ansible系列”中的每篇文章都建立在前文的基础之上,所以,请按照顺序阅读这些文章,否则有可能在阅读中遇到障碍。

上一篇文章介绍了ansible的基本概念,以及相关的基础配置,我们已经知道,如果想要管理受管主机,则需要将受管主机添加到ansible的管理清单中,当安装ansible以后,会提供一个默认的管理清单,即/etc/ansible/hosts文件,今天我们就来详细的聊聊它。

仍然以我们之前的演示环境为例,我们有4台主机,IP如下

  • 10.1.1.71

  • 10.1.1.70

  • 10.1.1.61

  • 10.1.1.60

主机71为ansible主机,同时,主机71的公钥已经配置在了其他3台主机中。

在前文中,我们已经介绍了怎样简单的配置清单,比如通过IP地址的方式配置受管主机,或者通过别名的方式配置受管主机,此处不再赘述,假设,我想要通过ansible管理主机60与主机61,那么我可以在/etc/ansible/hosts中写入如下内容

10.1.1.60

10.1.1.61

配置完成后,即可通过命令管理这两台主机,仍然使用之前的示例命令作为演示。

未分类

如上图所示,我们使用了两条命令,分别去ping主机60和主机61,是没有问题的,其实,我们也可以使用”all”关键字,在一条命令中,一次性的去操作”清单”中的所有主机,示例如下

未分类

是不是很简单,有没有很方便?那么,聪明如你一定会想,我们能不能自定义一些类似”all”这样的关键字呢?

答案是肯定的,清单支持”分组”功能,我们可以将某些主机分为一组,然后通过组名去管理组内的所有主机。

比如,主机60和主机61都属于A模块的服务器,主机70属于B模块的服务器,那么,我们则可以在清单中进行如下配置

未分类

上述配置表示我们定义了两个组,A组和B组,A组中包含主机60与61,B组中包含主机70,经过上述配置后,我们可以通过组名去管理组内的所有主机,示例如下。

未分类

当然,在实际的应用中,我们并不会使用”A”或者”B”这样的名字作为组名,此处是为了演示方便,在实际使用时,组名应该尽量的见名知义

在上例的A组中,两台受管主机的IP地址为10.1.1.60和10.1.1.61 ,细心如你一定发现了,这两台主机的IP地址是连续的,所以,我们可以使用更简洁的方法,配置A组中的受管主机,示例如下

未分类

上例A组中的配置与之前A组中的配置的效果是相同的,不过这种方式更加方便,因为如果你需要配置10台IP地址连续的主机时,只需要一条配置就可以搞定了。

除了使用IP地址,我们也可以使用主机名配置受管主机,当然,使用主机名配置受管主机的前提是ansible主机可以正确解析对应的主机名,比如,我们想要通过主机名配置两台主机,示例如下。

未分类

眼尖的你一定又从上述配置中发现了某种规律,没错,上述主机名中,”dbSrv-“之后的字母是按照字母顺序排列的,所以,上述配置也可以简写为如下模样,它们的效果是相同的。

未分类

通常情况下,我们为了更加的灵活的管理受管主机,可能需要在组内嵌套组。

比如,服务器环境从大类上可以分为”生产环境”和”测试环境”,所以,我们很自然的把主机分成了两组,生产组和测试组,但是,仔细想想,生产环境又包含很多业务模块,比如,A模块生产组、B模块生产组,同理,测试环境中也会有同样的问题,比如A模块测试环境组,B模块测试组,这时,我们就需要更加细化的进行分组,示例如下

未分类

上述示例表示我们配置了3个组,proA组、proB组、pro组,而pro组中包含”子组”,没错,”children”关键字表示当前组中存在子组,pro组的子组就是proA组和proB组,也就是说,当我们操作pro组时,就相当于操作proA组和ProB组中的所有主机,这样分组就能为我们带来一些好处,比如,当我们需要针对生产环境中的所有主机进行操作时,调用pro组即可,当我们需要对生产环境中的某一个模块进行操作时,比如生产环境中的A模块,那么我们只调用proA组即可。

当然,你也可以使用更加”原始”的方法,实现与上述分组相同的效果,示例如下。

未分类

没错,同一主机可以被分配到不同的组中,而这种分组的方式与之前子组的分组方式实现的效果是相同的,但是,这样分组并不能体现出组与组之间的逻辑层级关系,而且,当子组内的主机非常多时,这种原始的方法容易让管理变得”混乱”,所以,当组与组之间存在一定的层级关系时,我们还是推荐使用嵌套组的方式配置组,因为这样做逻辑上更加清晰。

到目前为止,我们一直都在使用INI的配置风格去配置”清单”,其实,/etc/ansible/hosts不仅能够识别INI的配置语法,还能够识别”YAML”的配置语法。

YAML是一种语言,YAML是”YAML Ain’t a Markup Language”的缩写,从YAML的全称可以看出来,YAML并不是一种标记语言,但是,如果你使用过类似XML这种标记语言,那么你可能会很快的学会YAML,与XML相同的是,我们可以使用YAML编写配置文件,而ansible的清单也支持YAML的语法,所以我们可以使用YAML语法编写清单,从而管理受管主机,这样说可能不是特别容易理解,不如先来看一个小例子(没有接触过YAML语法没有关系,先向下看)

如下示例仍然是在/etc/ansible/hosts文件中编写

未分类

上述配置就是使用YAML语法配置的主机清单,非常简单

最上方使用all关键字,all后面有”:”,你一定联想到了,我们之前可以使用all关键字,管理清单中的所有主机,这里的”all:”就是这个含义。

第二行开头使用一个空格作为缩进,使用hosts关键字,表示hosts属于all的下一级,我们可以这样理解,all是默认的一个组,这个组是最大的一个组,当我们需要在组中定义受管主机时,就需要使用到hosts关键字,当我们进行自定义分组时,也需要使用hosts关键字,每个分组指明自己组内的受管主机时,都要使用到hosts关键字,注意,在YAML的语法中,只能使用空格作为缩进,不能使用tab,否则语法上会报错,如果你习惯使用vim编辑文件,同时你又习惯使用tab作为缩进,那么你可以将ansible主机上的vim进行设置,默认将tab转化为空格,这样就能兼容你的使用习惯了,我就是这样设置的。

第三行开头使用两个空格作为缩进,然后指明了主机60的IP地址,没错,主机60的IP地址就是hosts元素下一级的元素

第四行开头使用两个空格作为缩进,然后指明了主机61的IP地址,你一定想明白了,主机60和主机61的层级是相同的,它们是平级的,因为它们的左侧缩进是对齐的。

为了让从来没有接触过YAML的朋友能够更好的理解,在下面的示例中,我们会先给出INI风格的配置,然后使用YAML语法写出同样效果的配置,并进行对比,以方便理解,虽然”清单”能够同时识别这两种语法,但是不建议在一个”清单”中同时混用这两种语法。

此处先列出上述YAML配置以及对应的INI配置

#YAML示例
all:
  hosts:
    10.1.1.60:
    10.1.1.61:

#上例相当于如下INI配置
10.1.1.60
10.1.1.61

那么,我们来扩展一下

#先看一个INI风格的配置,示例如下
10.1.1.61

[test1]
10.1.1.60

[test2]
10.1.1.70
#上述配置表示当前清单中有3台受管主机,主机61不属于任何组,主机60属于test1组,主机70属于test2组

#使用YAML语法进行同等效果的配置如下
#注意,为了使缩进显得更加明显,此处每次缩进使用两个空格
all:
  hosts:
    10.1.1.61:
  children:
    test1:
      hosts:
        10.1.1.60:
    test2:
      hosts:
        10.1.1.70:
#从上例可以看出,当直接在清单中创建组时,需要在all关键字内使用children关键字,而定义每个组时,有必须使用hosts关键字,指明组内的主机

有了上面的基础,我们来看一下当组中嵌套组时,使用YAML语法应该怎样描写

#仍然先写出INI风格的示例以作对比,如下
[proA]
10.1.1.60

[proB]
10.1.1.70

[pro:children]
proA
proB

#对应YAML格式的配置如下
all:
  children:
    pro:
      children:
        proA:
          hosts:
            10.1.1.60:
        proB:
          hosts:
            10.1.1.70:
#上述配置表示,pro组有两个子组,分别为proA组和proB组,而这两个组分别有自己组内的主机。

细心如你,一定已经发现,当我们使用YAML语法配置清单时,无非是使用hosts、children等关键字与我们的自定义名称进行排列组合罢了。

#前文中,我们还介绍了使用别名的方式配置受管主机,INI格式的示例如下

未分类

#同等效果的YAML语法配置如下

未分类

注意:上图中标注的”空格”不可省,这是YAML的语法,省略空格后会报错

其实,我们还可以在清单中配置变量,但是,目前我们并没有实际的演示场景,所以此处先行略过。

更多内容可以参考官网手册,直达链接如下

http://docs.ansible.com/ansible/latest/intro_inventory.html

你可能已经习惯使用INI的语法编辑清单,或者你对YAML并不熟悉,于是你准备放弃学习YAML语法,这样并不可行,因为之后我们会介绍怎样编写ansible的剧本,编写ansible剧本时,只能使用YAML语法,所以,不要放弃YAML,当然,我们也不用过于深究YAML的语法,我们只要记住一些套路(固定格式),就可以编写ansible剧本了,所以不要着急,船到桥头自然直,到时候你自然会掌握这些语法的。

好了,关于”清单”的配置暂时总结到这里,希望能够对你有所帮助。

使用ansible批量格式化并挂载磁盘

初始化安装elasticsearch这类集群系统的时候,往往需要批量操作大量的磁盘。可以通过ansible快速完成这类工作。

假设每台设备有10块盘(sdxxx),要挂载到/dataxxx,elasticsearch数据目录为/dataxxx/es,首先将变量写入vars/main.yaml,

---

disks:
  /dev/sdb: /data1
  /dev/sdc: /data2
  /dev/sdd: /data3
  /dev/sde: /data4
  /dev/sdf: /data5
  /dev/sdg: /data6
  /dev/sdh: /data7
  /dev/sdi: /data8
  /dev/sdj: /data9
  /dev/sdk: /data10

然后,创建任务文件,tasks/main.yaml,

---

- file:
    path: "{{ item.value }}/es"
    state: directory
    owner: elasticsearch
    group: elasticsearch
    mode: 0755
  with_dict: "{{ disks }}"

- name: umount datanode disks
  mount:
    path: "{{ item.value }}"
    state: absent
  with_dict: "{{ disks }}"

- name: format datanode disks
  filesystem: fstype=xfs dev="{{ item.key }}" force=true
  with_dict: "{{ disks }}"

- name: mount datanode disks
  mount:
    path: "{{ item.value }}"
    src: "{{ item.key }}"
    fstype: xfs
    opts: "defaults,noatime,nobarrier"
    state: mounted
  with_dict: "{{ disks }}"

创建目录、umount、format、mount、写入/etc/fstab,一气呵成。

ansible 基本概念,ad-hoc操作

概述

本文描述自动化运维工具 Ansible 的安装及基础使用方法,包含:

  • Centos 下的安装
  • 主机配置
  • Ad-Hoc command(命令行执行)
  • Playbook (任务剧本)

Ansible 和 Saltstack 是目前主流的两个自动化运维工具,都可以用于同时对大量主机进行系统配置,应用部署等工作,利用这种集成化的自动运维工具最大的优势在于运维体系结构的持续可维护性。本文先着眼于 Ansible 的基础使用,不会进行太多扩展,通过本文可以快速上手使用 Ansible。

安装

Ansible 的一大特点是 agentless,可以通过 SSH 来对服务器进行管理,意味着只需要将 Ansible 部署到一台服务器。

Ansible 并非一定使用 ssh 来与服务器进行通信,它也支持 ZeroMq 的扩展。事实上,如果不是总要同时对一个庞大主机群进行操作,通过 ssh 进行通信在效率上的损失还是可以接受的。

在 Centos 系统上,Ansible 可以用过 yum 来进行安装,前提是需要 EPEL 的源。

以 Centos7 为例执行下列命令安装 EPEL 源:

rpm -Uvh http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm

安装 Ansible

yum install ansible

通常情况下,Ansible 可以部署到登录服务器上,登录服务器存放 ssh 私钥,这样在对操作主机群时就无需每次输入密码。

主机配置

Ansible 默认主机配置文件在 /etc/ansible/hosts,你也可以创建新的配置文件来管理主机,如果使用其他主机配置,在执行命令行时就需要通过 -i 参数指定主机配置。

下边是一个主机配置的例子,foo.example.com 这些就是主机的定义,webservers 是主机组的定义,在使用 Ansible 时,可以通过主机组对一组服务器进行操作。

[webservers]
foo.example.com
bar.example.com

Ad-Hoc Command

Ad-Hoc Command 指那些希望立即执行对一组服务器进行操作,而这个操作过程不需要进行保存的方式。例如临时需要对webservers 这组主机下的 /opt/testfile 文件进行删除,而这种操作是没有必要保存成 Playbook (剧本模式,稍后会说到)的。

$ ansible webservers -m command -a "rm -f /opt/testfile" 

参数 -m 指定使用的功能模块是 command,-a 设置模块所需参数队列,对于 command 模块来讲,这个参数就是你需要执行的命令。

事实上,command 模块也有几个参数,当需要指定多个参数时,就需要使用 arg1=value1 arg2=value2 这样的键值对方式指定,当没有指定参数时,在这里实际上使用 command 的是 free_form 参数。

Ansible 默认使用的模块是 command,这意味着上述命令行并不需要显式指定功能模块,可以直接使用下列命令行
ansible webservers -a "rm -f /opt/testfile"

再举个简单的例子,批量文件分发:

$ ansible webservers -m copy -a "src=/etc/hosts dest=/tmp/hosts"

上述命令将本地 /etc/hosts 分发到 webservers 的 /tmp/hosts。

使用 ansible-doc -l 可以查看 Ansible 支持哪些模块,也可以直接前往 这里 去查看。

使用 ansible-doc -s module_name 可以获取模块的使用帮助。

Playbook

Playbook 可以称之为任务剧本,它允许你按照剧本的方式编排需要完成的任务,使用 YAML 的语法格式。

早期,我们可能使用脚本来完成一些流程相对繁多的任务,脚本可以很好的执行,但可读性差。YAML 这种配置性的语法格式则可读性很好,并且对于没有编程基础的运维人员来讲,它也更容易上手。

看一个 playbook 的例子:

---
- hosts: webservers
  remote_user: root
  tasks:
  - name: ensure apache is at the latest version
    yum: name=httpd state=latest
  - name: write the apache config file
    template: src=/srv/httpd.j2 dest=/etc/httpd.conf
    notify:
    - restart apache
  - name: ensure apache is running (and enable it at boot)
    service: name=httpd state=started enabled=yes
  handlers:
    - name: restart apache
      service: name=httpd state=restarted

这个简单的例子完成的任务是部署或更新 webservers 这一组主机的 apache。

  • hosts:定义操作的主机组

  • remote_user:使用的用户

  • task:任务步骤,共分为三步:

    • 1 通过 yum 模块确认 apache 是否安装以及是否是最新版本,如果不是则安装或更新;
    • 2 通过 template 模块来设置配置文件,如果配置有更新则通知 handler 重启 apache;
    • 3 通过 service 模块来判断 apache 是否在运行,如果没有则启动 apache。
  • handlers:事件处理,处理任务中的 notify

Playbook 完成后,执行就可以完成对一组服务器的操作

ansible-playbook playbook.yml 

以上就是 Ansible 的基础使用方法,更多的可以去参考 Ansible 的文档http://docs.ansible.com/ansible/latest/index.html

Docker compose初始化失败问题

问题

今天在Docker Postgresql用户名和密码授权的问题上花了一些时间,问题是:

psql: FATAL:  password authentication failed for user "postgres"

admin的用户名和密码是可以在docker-compose.yml里设置的,通常我们可以配置为:

postgresql:
  image: postgres:latest
  ports:
    - "5434:5432"
  volumes:
    - ./data/pgsql:/var/lib/postgresql/data
    - ./initialize/pgsql:/docker-entrypoint-initdb.d
  environment:
    POSTGRES_USER: postgres
    POSTGRES_DB: postgres
  secrets:
    - pg_superuser_password

某个用户的密码可以在./initialize/pgsql目录的脚本里设置:

#!/bin/bash
set -e
psql -v ON_ERROR_STOP=1 --username "postgres" <<-EOSQL
    CREATE USER user WITH PASSWORD 'the-password';
    ALTER USER user CREATEDB;
EOSQL

只是今天碰巧想修改一下这个密码,所以就把这个脚本里的密码修改了,然后执行命令:

docker-compose up --build -d --force-recreate

而后就一直出现上面的用户授权失败。

原因

刚开始一直认为是可能dockerfile配置得不对,结果花费了些时间。后来突然想到了,PG里数据初始化应该只是第一次做了,后续如果发现/var/lib/postgresql/data里已经有数据了就再也不会重新设置密码,这里是配置volume的,如果还未有重要数据把./data/pgsql删除了即可,或者应该是可以通过attach进入容器通过pg命令修改。

总结

最近在自己工作的项目都完全Docker化,感觉是配置来折腾用起来飞。最近也在做一个重度依赖Docker的项目,所以Docker的文档需要看完,特别是网络和数据存储那块,否则会花费不少时间折腾。

Docker Compose 常用命令

docker-compose 命令

大多数Compose命令都是运行于一个或多个服务的,如果服务没有指定,该命令将会应用到所有服务,如果要获得所有可用信息,使用命令:docker-compose [COMMAND] –help

build
创建或者再建服务
服务被创建后会标记为project_service(比如composetest_db),如果改变了一个服务的Dockerfile或者构建目录的内容,可以使用docker-compose build来重建它

help
显示命令的帮助和使用信息

kill
通过发送SIGKILL的信号强制停止运行的容器,这个信号可以选择性的通过,比如:
docker-compose kill -s SIGKINT

logs
显示服务的日志输出

logs 后面什么都不加,则输出该项目所有服务的日志信息

docker-compose logs SERVICE 则输出该服务的日志信息

port

为端口绑定输出公共信息

ps
显示容器

pull
拉取服务镜像

rm
删除停止的容器

run
在服务上运行一个一次性命令,比如:
docker-compose run web python manage.py shell

scale
设置为一个服务启动的容器数量,数量是以这样的参数形式指定的:service=num,比如:
docker-compose scale web=2 worker=3

start
启动已经存在的容器作为一个服务

stop
停止运行的容器而不删除它们,它们可以使用命令docker-compose start重新启动起来

up
为一个服务构建、创建、启动、附加到容器
连接的服务会被启动,除非它们已经在运行了
默认情况下,docker-compose up会集中每个容器的输出,当存在时,所有的容器会停止,运行docker-compose up -d会在后台启动容器并使它们运行

top

显示容器中运行的进程

选项

–verbose
显示更多输出

–version
显示版本号并退出

-f,–file FILE
指定一个可选的Compose yaml文件(默认:docker-compose.yml)

-p,–project-name NAME
指定可选的项目名称(默认:当前目录名称)

使用 Docker Compose 本地部署基于 Sentinel 的高可用 Redis 集群

说明

项目地址:github.com/TomCzHen/re…

根据官方文档 Redis Sentinel Documentation 中的 Example 2: basic setup with three boxes 示例创建的实例,但因为是单机部署,所以不满足 Redis 实例 与 Sentinel 实例分别处于 3 台机器的要求,因此仅用于开发环境测试与学习。

使用方法

  • 使用 docker-compose up -d 部署运行。

  • 使用 docker-compose pause master 可以模拟对应的 Redis 实例不可用。

  • 使用 docker-compose pause sentinel-1 可以模拟对应的 Sentinel 实例不可用。

  • 使用 docker-compose unpause service_name 将暂停的容器恢复运行。

  • 使用支持 Sentinel 的客户端连接 localhost:62379 进行应用测试。

注:Windows 和 Mac 可能需要修改 Volumes 挂载参数。

注意事项

Sentinel, Docker, NAT, and possible issues

https://redis.io/topics/sentinel#sentinel-docker-nat-and-possible-issues

将容器端口 EXPOSE 时,Sentinel 所发现的 master/slave 连接信息(IP 和 端口)对客户端来说不一定可用。

例如:将 Reids 实例端口 6379 EXPOSE 为 16379, Sentinel 容器使用 LINK 的方式访问 Redis 容器,那么对于 Sentinel 容器 6379 端口是可用的,但对于外部客户端是不可用的。

解决方法是 EXPOSE 端口时保持内外端口一致,或者使用 host 网络运行容器。如果你想使用本项目中的编排文件部署的集群对外部可用,那么只能将 Redis 容器运行在 host 网络之上。

注:实际上 bridge 模式下 Redis 性能也会受到影响。

文件结构

.
├── docker-compose.yaml
├── nginx
│   └── nginx.conf
├── README.md
├── .env
└── sentinel
    ├── docker-entrypoint.sh
    ├── Dockerfile-sentinel
    └── sentinel.conf.sample

Sentinel

镜像使用 Dockerfile-sentinel 构建,运行时根据环境变量生成 sentinel.conf 文件,详细配置说明请查看 sentinel.conf.sample 内容。

docker-entrypoint.sh

使用 Reids 官方镜像中的 docker-entrypoint.sh 脚本修改而来,添加了生成 sentienl.conf 语句。

...
# create sentinel.conf
if [ ! -e ${SENTINEL_CONF_PATH} ]; then
    envsubst < /etc/redis/sentinel.conf.sample > ${SENTINEL_CONF_PATH}
    chown redis:redis /etc/redis/sentinel.conf
fi
...

修改配置 Sentinel 的环境变量后需要重新创建容器才能生效。

可用环境变量

SENTINEL_CONF_PATH=/etc/redis/sentinel.conf
SENTINEL_PORT=26379
SENTINEL_MASTER_NAME=redis-master
SENTINEL_REDIS_IP=127.0.0.1
SENTINEL_REDIS_PORT=6379
SENTINEL_REDIS_PWD=
SENTINEL_QUORUM=2
SENTINEL_DOWN_AFTER=30000
SENTINEL_PARALLEL_SYNCS=1
SENTINEL_FAILOVER_TIMEOUT=180000

docker-compose.yaml

可以使用 docker-compose config 可查看完整的编排内容。

Redis 实例运行参数

详细可用参数请查看官方示例文件 Redis Configuration File Example,需要注意 port 参数需要与编排中的 PORTS 保持一致,或修改编排文件让容器网络使用 host 模式。

由于 master 会被 Sentinel 切换为 slave ,因此最好保持每个 Redis 实例的口令一致。

master:
    image: redis:4.0.8-alpine
    ports:
      - 6379:6379
    volumes:
      - type: volume
        source: master-data
        target: /data
    command: [
      '--requirepass "${REDIS_PWD}"',
      '--masterauth "${REDIS_PWD}"',
      '--maxmemory 512mb',
      '--maxmemory-policy volatile-ttl',
      '--save ""',
    ]

Sentinel 实例运行参数

详细可用参数请查看 sentinel 目录下的 sentinel.conf.sample 文件。由于容器使用的配置文件是运行时根据环境变量生成的,因此使用 environment 进行配置,可用环境变量请查看文档 Sentinel 部分。

最后使用了 Nginx 作为 Sentinel 实例的代理,因此 Sentinel 容器不需要对外访问。

sentinel-1: &sentinel
    build:
      context: ./sentinel
      dockerfile: Dockerfile-sentinel
    image: redis-sentinel:dev
    environment:
      - SENTINEL_REDIS_PWD=${REDIS_PWD}
      - SENTINEL_REDIS_IP=${SENTINEL_MASTER_NAME}
      - SENTINEL_QUORUM=2
      - SENTINEL_DOWN_AFTER=3000
    command: [
      '${SENTINEL_CONF_PATH}',
      '--sentinel'
    ]
    depends_on:
      - master
      - node-1
      - node-2
    links:
      - master:${SENTINEL_MASTER_NAME}
      - node-1
      - node-2
  sentinel-2:
    <<: *sentinel
  sentinel-3:
    <<: *sentinel

Nginx

使用 Nginx 作为 Sentinel 负载均衡以及高可用代理。

  nginx:
    image: nginx:1.13.9-alpine
    ports:
      - 26379:26379
    volumes:
      - type: bind
        source: ./nginx/nginx.conf
        target: /etc/nginx/nginx.conf
        read_only: true
    depends_on:
      - sentinel-1
      - sentinel-2
      - sentinel-3

修改 nginx 目录下的 nginx.conf 进行配置。

...
stream {
    server {
        listen 26379;
        proxy_pass redis_sentinel;
    }

    upstream redis_sentinel {
        server sentinel-1:26379;
        server sentinel-2:26379;
        server sentinel-3:26379;
    }
}
...

你离ELK只有一句docker-compose的距离

未分类

引言

刚接触Elk的时候,我用github.com/deviantony/…,部署了第一个测试环境,这是一个很优秀的项目,几乎没什么配置就可以部署成功。
但有一个问题就是对于一个初学者如此洁净的环境,我完全不知道从何入手,也弄不清这个框架的优势是什么(连个Dashboard样本都没有)。还有 x-pack 的配置,metricbeat 的接入都踩过不少坑,才部署成一个像样的学习环境。之后在写 docker-compose.yml 脚本的时候又是各种踩雷,终于实现了快速一键部署。同时支持 DaoCloud 的 Stack 脚本 持续集成
在这里分享给大家,好像想入坑的同学少走些弯路。

你需要准备什么

一个 docker 环境, 还有…… 没了

注:win 和 macOS 下不支持 docker-metricbeat 的 system 监控,需手动关闭

我要怎么做

在这里看下注意事项

本地部署

$ git clone "https://github.com/wilfordw/docker-elk-example.git"
$ cd docker-elk-example
$ docker-compose up -d

DaoCloud Stack 部署

先下载项目到服务器,复制项目绝对路径

$ git clone "https://github.com/wilfordw/docker-elk-example.git"
$ cd docker-elk-example
$ pwd

把 dao-docker-compose.yml 内容复制进 Stack 的 YAML, 把上面克隆项目的 pwd 替换里面的 /root/app/docker-elk/, 点击部署就可以

想要自己创建镜像也可以,把你创建好的镜像地址替换 yml 里的 image

部署完可以看到什么?

未分类

未分类

未分类

未分类

未分类

未分类

继续更新

目前只做了 System Docker Nginx 的监控案列, 之后会继续集成

  • Metricbeat Mysql 监听
  • Metricbeat NodeJs 监听
  • Metricbeat Golang 监听
  • Metricbeat Kubernetes 监听
  • X-pack 权限解析
  • ELK 集群