说起来很可笑,总说自己是 SRE,但是 Salt,ansible 这种东西还没玩溜呢。平时的运维和发布一般都是工单,要走一些审批流程,所以也没啥场景去用这些。今天部署一个东西,才发现自己已经重复在机器上编译 Python 这种事情干了三次了,好,是时候将这些操作自动化了。
之所以选择 ansible,是因为它是通过 ssh 协议的,不必再客户端上面安装任何东西,不依赖其他 broker 数据库等,ansible 项目本身就是 Python 脚本,所以安装和使用就非常方便。
体验下来,发现 ansible 被人吐槽“慢”真是一点都不委屈,实在太慢了。
我主要用的是 role 的功能来将平时要做的事情在服务器上组织起来。
一个 Compile Python 的 Role
ansible 提供了 role 来组织你的脚本,role 其实是 playbook 通过文件结构将不同的部分组织,用的时候在 playbook 中 include 就可以了。
下面以一个编译 Python 的 role 为例。
首先它的目录结构如下:
1 2 3 4 5 6 7 8 9 |
➜ cluster git:(master) tree . ├── roles │ └── compile_python37 │ ├── tasks │ │ └── main.yml │ └── vars │ └── main.yml └── tools.yaml |
在 roles 文件夹里面建了一个 compile_python37
的文件夹,一个文件夹就是一个 role,然后按照 role 规定的文件夹结构在这个 role 中新建相关的比如 tasks
。
tools.yaml
中的内容如下,就是调用一下 role:
1 2 3 4 5 6 |
--- - hosts: '{{target}}' remote_user: admin roles: - compile_python37 |
compile_python37/tasks/main.yml
中的内容如下:
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 |
--- - name: yum yum: name={{item}} state=latest become_method: sudo become: yes with_items: "{{ python_yum }}" - name: unarchive unarchive: src: "{{ python_url }}" dest: "{{ python_tmp }}" remote_src: True - name: configure and make shell: cd "{{ python_dir }}" && ./configure --with-ensurepip=install --prefix=/usr/local/python && make -j2 && sudo make install - file: src: '{{ item.src }}' dest: '{{ item.dest }}' state: link become_method: sudo become: yes with_items: - { src: '/usr/local/python/bin/python3', dest: '/usr/local/bin/python' } - { src: '/usr/local/python/bin/pip3' , dest: '/usr/local/bin/pip' } |
可以看到用到了很多 "{{ xxx }}"
这样的东西,这相当于一些变量,我们将固定的操作写在 tasks
里面,将变量 vars
里面,这样之后如果要修改一些诸如解压地址的时候,就可以直接编辑 vars
了。
unarchive
可以直接写远程的 url,解压到本地的一个位置。
with_items
其实是循环,会循环 value 中所有的值,将它们当做 item
来执行一次命令。比如第一个 yum
命令会对 "{{ python_yum }}"
中的每一个值都执行一次。
compile_python37/vars/main.yml
的内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
--- python_tmp: /tmp python_url: https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tgz python_dir: /tmp/Python-3.7.1 python_yum: - wget - zlib-devel - bzip2-devel - openssl-devel - ncurses-devel - sqlite-devel - readline-devel - tk-devel - gcc - gcc-c++ - libffi-devel |
可以看到就是上面 tasks 需要的变量。
搞定,另外 galaxy 这里有一些大家写的 role,类似像一个 docker hub。不过我试了几个,直接用都有问题……
最后一点 Tips,单字母的命令放着不用太浪费了,推荐一下几个 alias:
1 2 3 4 5 |
alias a=ansible alias h=htop alias d=docker alias v=vagrant alias ap=ansible-playbook |
再来一点 Tips,ansible 默认的输出实在是太反人类了,是一个压缩后的 json,换行符都没了,变成了一坨,编译命令、yum 安装命令的输出根本没法看了。找了一圈之后从一个 issue 里面才发现解决方案:设置 stdout_callback = debug
就好了,会以格式化的 json 来显示。也可以设置成 minimal 或 yaml,我觉得 yaml 比较好看。可以通过环境变量设置或者设置文件来设置。看这点赞数,颇有点“如何退出 Vim”的意味啊,为啥不弄成默认设置呢?
看到同事写的,直接用COPY模块,将下载/解压等操作卸载shell脚本中,然后执行脚本就OK了;本文中利用了各个模块来做这个事儿。感觉前者也是个方便的方法
也是可行的,我之前也是写的 compile_python.sh 脚本,但不是长久之计。因为 shell 在后期维护、编辑文件(你得用 sed 吧)方面不太好,并且 ansible 是可以重复执行的,shell 如果你对自己的脚本不自信,就不能保证重复执行一定没有副作用。
详细的比较可以看下这篇文章,shell 用的好好地,为什么得用 ansible 是很多人会想的问题:https://hvops.com/articles/ansible-vs-shell-scripts/
可能表述没清楚,我的意思是,我们也是用的ansible,只不过是将一些操作步骤写在了shell文件中,然后通过ansible执行定义的操作:将shell文件拷贝到指定host上,然后执行shell脚本。每次新来了机器,需要初始化,就执行对应的ansible任务。
比如,需要安装wget,就没用你这里的yum模块去做安装的操作。
PS:个人感觉同事的这种ansible工程写法,不是很符合推荐的做法吧……
是不太好,这样的话相当于只把 ansible 当做一个执行引擎了。ansible 的思想是每一步操作是幂等的,多次执行都会得到一样的结果,如果都写成 shell 就失去这个特性了。其实 ansible 的这些 module 学习起来也没有很大成本。
Pingback: Side Project 成本最小化运维 | 卡瓦邦噶!
错字:会以格式化的 josn 来显示
另外“这个issue”的链接指向文档而非 github ,是否也是不小心呢
感谢,typo 已经改正。但是 issue 的链接我也找不到了,只好删了。
Pingback: SRE 线上操作指南 | 卡瓦邦噶!