用Ansible部署服务器续篇

说明

主要是不想让上一篇太长了,把一些相对次要的东西放在这篇里补充一下,其实有了上一篇的基础,大部分内容都可以通过官方文档查到。

模板

在上篇里说到,安装系统时更新软件源有个问题就是软件源配置文件如果写死的,就只能用于指定版本的系统,如果需要灵活配置就需要使用模板。

这里是一个例子。

把原来的files/sources.list改为templates/sources.list.j2,内容如下:

{% if ansible_distribution == 'Debian' %}
deb http://{{ source_mirror }}/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} main contrib non-free
deb http://{{ source_mirror }}/{{ ansible_distribution | lower }} {{ ansible_distribution_release }}-updates main contrib non-free
deb http://{{ source_mirror }}/{{ ansible_distribution | lower }} {{ ansible_distribution_release }}-backports main contrib non-free
deb http://{{ source_mirror }}/{{ ansible_distribution | lower }}-security {{ ansible_distribution_release }}/updates main contrib non-free
deb-src http://{{ source_mirror }}/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} main contrib non-free
deb-src http://{{ source_mirror }}/{{ ansible_distribution | lower }} {{ ansible_distribution_release }}-updates main contrib non-free
deb-src http://{{ source_mirror }}/{{ ansible_distribution | lower }} {{ ansible_distribution_release }}-backports main contrib non-free
deb-src http://{{ source_mirror }}/{{ ansible_distribution | lower }}-security {{ ansible_distribution_release }}/updates main contrib non-free
{% else %}
deb http://{{ source_mirror }}/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} main restricted universe multiverse
deb http://{{ source_mirror }}/{{ ansible_distribution | lower }} {{ ansible_distribution_release }}-updates main restricted universe multivers
deb http://{{ source_mirror }}/{{ ansible_distribution | lower }} {{ ansible_distribution_release }}-backports main restricted universe multiverse
deb http://{{ source_mirror }}/{{ ansible_distribution | lower }} {{ ansible_distribution_release }}-security main restricted universe multiverse
{% endif %}

其中ansible_开头的是一些系统变量,ansible初始化时取值。这在docker角色定义里说过了,只是那个是用于tasks里,这里用在模板文件里,因为都是用Jinja2引擎,所以用法是一样的。

其中还有一个地方是if语句,这也是Jinja2的语法,因为Debian和Ubuntu的源链接格式不太一样。

source_mirror则是一个自定义的变量,可以放在group_vars/all里,也可以放在inventory/hosts里,除了这两个还有很多地方可以放,其中一个地方是roles/debian/defaults/main.yml,这里可以放角色变量的默认值:

source_mirror: 'mirrors.163.com'

现在需要把tasks里的步骤改一下:

- name: copy sources.list
  template:
    src: sources.list.j2
    dest: /etc/apt/sources.list
    owner: root
    group: root
    mode: '0644'

修改的地方就两个:一个是把copy改成template,一个是把源文件改成带j2的。

条件检测

如果我们需要在更新源之前备份一下,并且防止备份被重复覆盖,就需要先检测一下文件是否存在,可以这样操作:

- name: Check sources.list.orig
  stat: path=/etc/apt/sources.list.orig
  register: stat_sources_orig

- name: Backup sources.list
  command: mv /etc/apt/sources.list /etc/apt/sources.list.orig
  when: not stat_sources_orig.stat.exists

- name: Copy sources.list
  template:
    src: sources.list.j2
    dest: /etc/apt/sources.list
    owner: root
    group: root
    mode: '0644'

先注册一个检测器,然后在备份操作中检查一下检测结果,如果检测结果为false则跳过本操作。

包管理器

之前的例子都是以debian/ubuntu为例的,用的都是apt包管理器,如果要用CentOS的yum或Alpine的apk之类呢?

Ansible有通用方法,比如安装一个Nginx:

- name: install nginx
  action: >
    {{ ansible_pkg_mgr }} pkg=nginx state=latest update_cache=no install_recommends=no

使用非root用户连接

假设现在是用非root用户(比如raptor)连接到服务器,需要sudo才能操作。那么可以在tasks/main.yml在每个操作中指定:

- name: xxx
  become: yes
  become_method: sudo
  xxxx:
    ...

但这样很麻烦,因为每个操作都要加。简单的办法就是使用全局变量,比如在inventory/hosts[myserver:vars]里加上:

ansible_become: yes
ansible_become_method: sudo

如果sudo需要密码,则需要在运行ansbile_playbook时加上--ask-become-pass

如果某些操作需要使用当前登录用户,则在相应的操作里加上:

- name: xxx
  become: no
  xxxx:
    ...

或者以root用户登录时,需要切换到别的用户上操作,则同样可以用:

- name: xxx
  become: yes
  become_user: raptor
  xxxx:
    ...

循环

比如设置语言的时候,如果我们要设置多种语言,当然可以写两个同样的操作,但这样效率就低了,因为有可能类似的操作很多——比如修改多个系统参数之类。

所以这时可以用循环:

- name: Ensure locales exists
  locale_gen:
    name: "{{ item.name }}"
    state: present
  with_items:
    - { name: 'en_US.UTF-8' }
    - { name: 'zh_CN.UTF-8' }

表示同时设置两种语言。注意:要使用双引号

文件修改

比如修改SSH的设置,编辑/etc/ssh/sshd_config

- name: Update sshd config
  lineinfile:
    path: /etc/ssh/sshd_config
    regexp: "{{ item.regexp }}"
    line: "{{ item.line }}"
    insertbefore: "{{ item.insertbefore }}"
  with_items:
    - { regexp: '^PermitRootLogin', line: 'PermitRootLogin no', insertbefore: '#PermitRootLogin' }
    - { regexp: '^PasswordAuthentication', line: 'PasswordAuthentication no', insertbefore: '#PasswordAuthentication' }

这里将修改PermitRootLogin禁止root用户登录,修改PasswordAuthentication关闭密码登录(只能使用证书登录)。注意,这里的循环使用了三个变量:regexp, line, insertbefore。

修改的原理就是:找到匹配regexp的行,替换为line的内容,如果没找到,就插入在insertbefore的前面,如果insertbefore的内容不存在,则插在文件的末尾。

重启服务(handlers)

正如我们所知,修改过配置文件必须重启服务才能生效,当然这个可以通过一个command操作进行,但更好的办法是用异步通知,在所有任务执行完后统一重启。

方法是在角色下创建handlers/main.yml

---
- name: Restart sshd
  service:
    name: sshd
    state: restarted

然后在需要重启地方加上:

- name: Update sshd config
  lineinfile:
    ...
  notify:
    Restart sshd

即可。

推送到[go4pro.org]