说明

  • Ansible 主要通过 ssh 来控制多台机器配置管理,部署应用等,工作流如下

    image-20240406152402368

  • Ansible 与 k8s 可见 Ansible Vs. Kubernetes

    • Ansible 没有状态的概念,只会根据设定的命令进行执行,直到完成或遇到错误退出
    • k8s 维护了不同 pod 的状态,有一整套管理机制

    The differences between these two products are profound. Ansible is an IT automation tool that deploys software, configures systems, and organizes more complex IT functions such as rolling updates or continuous deployments. On the other hand, Kubernetes is a system designed to orchestrate Docker containers. It manages workloads and uses nodes to handle scheduling to make sure that their condition matches the users’ expectations.

    In other words, Ansible deploys changes to hosts, while Kubernetes manages containers and keeps them working properly.

    Ansible is an excellent useful tool for front-end developers, particularly in situations where some programming is required. Kubernetes is best suited to developing larger apps.

    Based on the properties of both tools, it’s like comparing apples to oranges. Granted, they’re both DevOps tools that handle configuration management, but the purposes for which they’re used have minimal overlap.

  • Ansible 与 salt 可见 Ansible vs. Salt: What you need to knowAnsible vs Salt – What’s the Difference? (Pros and Cons)

    • Ansible 是 agentless,只需要在 master 安装相应的工具即可。salt 是 agent-based
    • Ansible 只有一个 master。salt 可有多个,故障后自动换 master
  • Ansible 基础概念可见 Ansible concepts

安装

前置要求

  1. master 与 slave 都需要安装 openssh-server
  2. master 与 slave 间配置了免密登录,可以设置两个 key,一个用于用户平常登录(可设置 passphrase),一个用户 Ansible 发布(不设置 passphrase),免密登录操作可见 ssh使用说明
  3. 测试容器可见: https://github.com/cv-programmer/ansible-learn

Ansible 安装

  • master 机器: pip install ansible,或者使用包管理工具安装,如 Ubuntu 下可使用 apt install ansible
  • slave 机器不需要操作

简单使用

  • 假设 master IP 为 172.28.0.2,并有两台 slave 机器,IP 分别为 172.28.0.3 和 172.28.0.4

  • 设置的私钥为 ~/.ssh/ansible

  • 编写 ansible inventory 文件

    1
    2
    3
    # ~/ansible-example
    172.28.0.3
    172.28.0.4
  • 在 master 机器执行以下命令,即可测试连通性

    1
    ansible all --key-file ~/.ssh/ansible -i ~/ansible-example -m ping 

    image-20240413161935052

  • 配置文件简化操作,通过在默认配置文件中设置,可以减少命令行输入,此时只需要输入 ansible all -m ping 即可,可以加上 interpreter_python = /usr/bin/python3.11 解决告警

    1
    2
    3
    4
    # ansible.cfg
    [defaults]
    inventory = ansible-example
    private_key_file = ~/.ssh/ansible

    image-20240413162950807

  • 安装 git,ansible all -m tdnf name=git

    image-20240413202639884

常用命令

  • 检查连通性: ansible all -m ping
  • 查看配置文件中的 slave: ansible all --list-hosts
  • 查看连接 slave信息: ansible all -m gather_facts,如果查看特定 host,后跟 --limit [ip]
  • 安装软件: ansible all -m [package-manager] name=[software]。其中package-manager是目标 Host 包管理工具,如果是 Ubuntu,则使用 aptCentos使用 yumphoton使用tdnf
    • 如果权限不足,需要加上--become --ask-become-pass

playbook

  • 使用 yaml 来定义一系列行为,简化操作成本

包管理

安装

  • 如以下用来安装 git: ansible-playbook playbook.yml

    1
    2
    3
    4
    5
    6
    # playbook.yml
    - hosts: all
    tasks:
    - name: install package
    tdnf:
    name: git

    image-20240413213323748

    • 多次执行时,按理会变成 ignored = 1,但仍会出现 changed = 1,可通过以下方式来解决,即预先判断,再执行安装操作。可能原因如下,但暂未找到官方文档

      GPT: 当使用 Ansible 执行 tdnf 安装软件时,即使软件已经安装过,changed 值仍然可能显示为1。这是因为 tdnf 模块在每次运行时检查软件包的状态,并记录状态更改,而不考虑软件是否已经安装

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      # 示例运行,单软件包
      - hosts: all
      tasks:
      - name: Check if package is installed
      shell: tdnf info git
      register: package_check
      changed_when: false
      failed_when: false
      ignore_errors: true

      - name: Install package
      tdnf:
      name: git
      when: package_check.rc != 0

      image-20240414112450696

  • 安装多个软件

    1
    2
    3
    4
    5
    6
    7
    - hosts: all
    tasks:
    - name: install package
    tdnf:
    name:
    - git
    - tmux

卸载

1
2
3
4
5
6
- hosts: all
tasks:
- name: install package
tdnf:
name: git
state: absent

when

1
2
3
4
5
6
- hosts: all
tasks:
- name: install package
tdnf:
name: git
when: ansible_distribution == 'VMware Photon OS'
  • 当 slave 存在多种架构的机器时,使用同一个模块安装软件不可行,此时使用 when来区分不同的 task => tdnf不支持 when=> 缩进错了

    image-20240414165032177

  • 缩进错误时,如下

    1
    2
    3
    4
    5
    6
    - hosts: all
    tasks:
    - name: install package
    tdnf:
    name: git
    when: ansible_distribution == 'VMware Photon OS'

    image-20240414153839631

  • 比如,slave 机器存在 centos 和 Ubuntu 时,apt只适用于 Ubuntu,Centos 则需要使用 yum。可参见 Ansible - Only do action if on specific distribution (Debian, Ubuntu, CentOS or RHEL) or distribution version (ubuntu precise, ubuntu trusty)

    • 系统版本可通过 /etc/os-release中 ID 确定
    • ansible_distribution 可通过 ansible all -m gather_facts --limit 172.28.0.3 | grep ansible_distribution

参数化

  • 将不同系统的安装都汇总到一个 task 中,简化操作(name 使用变量是因为不同系统的叫法不同,比如 apache2 和 httpd)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# playbook.yml
# package 根据系统自动选择, python3 好像需要指定管理工具,否则出现以下报错
# The Python 2 bindings for rpm are needed for this module. If you require Python 3 support use the `dnf` Ansible module instead.. The Python 2 yum module is needed for this module. If you require Python 3 support use the `dnf` Ansible module instead."
- hosts: all
tasks:
- name: install package
package:
name:
- "{{ git_package }}"
- "{{ tmux_package }}"
state: latest

# inventory 即 之前的 ansible-example
172.28.0.3 git_package=git tmux_package=tmux
172.28.0.4 git_package=git tmux_package=tmux

分组 inventory

  • 根据不同的作用,可以将 slave 机器进行分组,便于后续的管理,比如 webserver, db 等
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
26
27
28
29
# playbook.yml
# package 根据系统自动选择
# 如果需要前置任务,可用 pre_tasks
- hosts: all
tasks:
- name: install git
tdnf:
name:
- git
- hosts: web_servers
tasks:
- name: install httpd
tdnf:
name:
- httpd
- hosts: db_servers
tasks:
- name: install tmux
tags: asdf
tdnf:
name:
- tmux

# inventory 即 之前的 ansible-example
[web_servers]
172.28.0.3

[db_servers]
172.28.0.4

image-20240414195721790

标签化 tags

  • 给 task 添加 tag,然后配合 ansible-playbook --tags [tag],即可执行对于 tag 的任务,多个 tag 使用 --tags "aa,bb"
  • ansible-playbook --list-tags 可查看已配置的 tag
  • always 为保留字,不指定时,也会执行
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
26
27
28
29
# playbook.yml
- hosts: all
tasks:
- name: install git
tags: always
tdnf:
name:
- git
- hosts: web_servers
tasks:
- name: install httpd
tags: core
tdnf:
name:
- httpd
- hosts: db_servers
tasks:
- name: install tmux
tags: asdf
tdnf:
name:
- tmux

# inventory
[web_servers]
172.28.0.3

[db_servers]
172.28.0.4

image-20240414201753872

文件管理

本地文件 copy

  • 通过 ansible 向 slave 下发文件,文件需要位于 playbook.yml 同级目录 files中(相对路径默认)=> 实际是与 task 同级
  • dest 末尾不带 /,即为复制过去后的名字,否则为路径
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
26
27
28
29
30
31
32
33
34
35
36
37
# playbook.yml
- hosts: all
tasks:
- name: install git
tags: always
tdnf:
name:
- git
- hosts: web_servers
tasks:
- name: install httpd
tags: core
tdnf:
name:
- httpd
- name: copy file
tags: sometag
copy:
src: a.txt
dest: /root/hello
owner: root
group: root
mode: 0644
- hosts: db_servers
tasks:
- name: install tmux
tags: asdf
tdnf:
name:
- tmux

# inventory
[web_servers]
172.28.0.3

[db_servers]
172.28.0.4

网络文件 + 解压 unarchive

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
26
27
28
29
30
31
32
33
34
35
36
37
38
# playbook.yml
- hosts: all
tasks:
- name: install git
tags: always
tdnf:
name:
- git
- hosts: web_servers
tasks:
- name: install httpd
tags: core
tdnf:
name:
- httpd
- hosts: db_servers
tasks:
- name: install unzip
tags: asdf
tdnf:
name:
- unzip
- name: install terraform
tags: asdf
unarchive:
src: https://releases.hashicorp.com/terraform/0.12.28/terraform_0.12.28_linux_amd64.zip
dest: /usr/local/bin
remote_src: yes
owner: root
group: root
mode: 0755

# inventory
[web_servers]
172.28.0.3

[db_servers]
172.28.0.4

服务管理 service

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
26
27
28
29
30
31
32
33
34
35
# playbook.yml
- hosts: all
tasks:
- name: install git
tags: always
tdnf:
name:
- git
- hosts: web_servers
tasks:
- name: install httpd
tags: core
tdnf:
name:
- httpd
- name: start httpd
tags: core
service:
name: httpd
state: started
enabled: yes
- hosts: db_servers
tasks:
- name: install unzip
tags: asdf
tdnf:
name:
- unzip

# inventory
[web_servers]
172.28.0.3

[db_servers]
172.28.0.4

文件内容修改 lineinfile

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# playbook.yml
- hosts: all
tasks:
- name: install git
tags: always
tdnf:
name:
- git
- hosts: web_servers
tasks:
- name: install httpd
tags: core
tdnf:
name:
- httpd
- name: start httpd
tags: core
service:
name: httpd
state: started
enabled: yes
- name: change email address
tags: core
lineinfile:
path: /etc/httpd/conf/httpd.conf
regexp: '^ServerAdmin'
line: ServerAdmin [email protected]
register: httpdsth
- name: restart httpd
tags: core
service:
name: httpd
state: restarted
when: httpdsth.changed
- hosts: db_servers
tasks:
- name: install unzip
tags: asdf
tdnf:
name:
- unzip

# inventory
[web_servers]
172.28.0.3

[db_servers]
172.28.0.4

用户管理

新增用户 user

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
26
27
28
29
30
31
32
33
34
# playbook.yml
- hosts: all
tasks:
- name: install git
tags: always
tdnf:
name:
- git
- name: create foo user
tags: always
user:
name: foo
groups: root
- hosts: web_servers
tasks:
- name: install httpd
tags: core
tdnf:
name:
- httpd
- hosts: db_servers
tasks:
- name: install unzip
tags: asdf
tdnf:
name:
- unzip

# inventory
[web_servers]
172.28.0.3

[db_servers]
172.28.0.4

用户权限 authorized_key

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# playbook.yml
- hosts: all
tasks:
- name: install git
tags: always
tdnf:
name:
- git
- name: create foo user
tags: always
user:
name: foo
groups: root
- name: add ssh key for foo
tags: always
authorized_key:
user: foo
key: "[replace by public key]"
- name: add sudoers file for foo
tags: always
copy:
src: sudoer_foo
dest: /etc/sudoers.d/foo
owner: root
group: root
mode: 0440
- hosts: web_servers
tasks:
- name: install httpd
tags: core
tdnf:
name:
- httpd
- hosts: db_servers
tasks:
- name: install unzip
tags: asdf
tdnf:
name:
- unzip

# inventory
[web_servers]
172.28.0.3

[db_servers]
172.28.0.4

# files/sudoer_foo
foo ALL=(ALL) NOPASSWD: ALL

roles

  • files 同级创建 roles文件夹

  • roles 每个文件夹代表一个 role,里面定义每个 role 的 tasks,如 roles/web_servers/tasks/*.yml 代表 web_servers 所需要执行的任务

  • tree

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    .
    ├── ansible.cfg
    ├── inventory
    ├── playbook.yml
    └── roles
    ├── base
    │   └── tasks
    │   └── main.yml
    ├── db_servers
    │   └── tasks
    │   └── main.yml
    └── web_servers
    ├── files
    │   └── a.txt
    └── 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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# playbook.yml
- hosts: all
roles:
- base

- hosts: web_servers
roles:
- web_servers

- hosts: db_servers
roles:
- db_servers

# roles/base/tasks/main.yml
- name: install git
tags: always
tdnf:
name:
- git

# roles/web_servers/tasks
- name: install httpd
tags: core
tdnf:
name:
- httpd
- name: copy file
tags: sometag
copy:
src: a.txt
dest: /root/hello
owner: root
group: root
mode: 0644

# roles/db_servers/tasks
- name: install unzip
tags: asdf
tdnf:
name:
- unzip

# inventory
[web_servers]
172.28.0.3

[db_servers]
172.28.0.4

host variables and handlers

host_vars

  • 用来存放 slave host 配置信息的文件,即 inventory 中 IP 中对应机器的特殊配置,如安装的包名等,本质就是 参数化

  • tree

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    .
    ├── ansible.cfg
    ├── host_vars
    │   ├── 172.28.0.3.yml
    │   └── 172.28.0.4.yml
    ├── inventory
    ├── playbook.yml
    └── roles
    ├── base
    │   └── tasks
    │   └── main.yml
    ├── db_servers
    │   └── tasks
    │   └── main.yml
    └── web_servers
    ├── files
    │   └── a.txt
    └── tasks
    └── main.yml

handlers

  • register 配合 when 的优化,register 当有多个但是某个未变化时,when 中的 task 不会触发,而 handlers 是有变化即会触发任务

image-20240421115559993

image-20240421115625124

image-20240421115737848

  • tree

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    .
    ├── ansible.cfg
    ├── handlers
    │   └── main.yml
    ├── host_vars
    │   ├── 172.28.0.3.yml
    │   └── 172.28.0.4.yml
    ├── inventory
    ├── playbook.yml
    └── roles
    ├── base
    │   └── tasks
    │   └── main.yml
    ├── db_servers
    │   └── tasks
    │   └── main.yml
    └── web_servers
    ├── files
    │   └── a.txt
    └── tasks
    └── main.yml

template

  • roles/[role]/tasks 同级别创建 templates 文件夹,用于存放模板文件。即 *.j2 文件

  • 如下表示将 sshd_config 作为模板,保证在相同分发版本的不同 Linux 机器上配置保持一致。具体文件可见 ansible-learn/ansible-template

    1. 配置模板

      1
      2
      # roles/base/templates/sshd_config_photon.j2
      AllowUsers {{ ssh_user }}
    2. 添加配置信息

      1
      2
      3
      # host_vars/172.28.0.3.yml
      ssh_user: "foo bar"
      ssh_template_file: "sshd_config_photon.j2"
    3. 添加 task,即指定从 template 生成文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      # roles/base/tasks/main.yml
      - name: generate sshd_config file from template
      tags: ssh
      template:
      src: "{{ ssh_template_file }}"
      dest: /etc/ssh/sshd_config
      owner: root
      group: root
      mode: 0644
      notify: restart_sshd
    4. 添加 handlers

      1
      2
      3
      4
      5
      # roles/base/handlers/main.yml
      - name: restart_sshd
      service:
      name: sshd
      state: restarted

未解决

参考