ansible-playbook 详解

Overview

Ansible 是一个开源的基于 OpenSSH 的自动化配置管理工具。可以用它来配置系统、部署软件和编排更高级的 IT 任务,比如持续部署或零停机更新。

一、ansible 简介

Ansible 的主要目标是简单和易用,并且它还高度关注安全性和可靠性。基于这样的目标,Ansible 适用于开发人员、系统管理员、发布工程师、IT 经理,以及介于两者之间的所有人。Ansible 适合管理几乎所有的环境,从拥有少数实例的小型环境到有数千个实例的企业环境

1.1 ansible 变量优先级如下

  1. command line values (eg "-u user")
  2. role defaults
  3. inventory file or script group vars
  4. inventory group_vars/all
  5. playbook group_vars/all
  6. inventory group_vars/*
  7. playbook group_vars/*
  8. inventory file or script host vars
  9. inventory host_vars/*: inventory 下面的 hosts_vars 目录下的变量优先级大于 group_vars 目录下的
  10. playbook host_vars/*
  11. host facts / cached set_facts
  12. play vars
  13. play vars_prompt
  14. play vars_files: vars_files 优先级大于同级别的 vars 字段(play 内定义)
  15. role vars (defined in role/vars/main.yml): role 里面的 vars 目录下的定义变量
  16. block vars (only for tasks in block)
  17. task vars (only for the task): task 里面的 vars 字段优先级别比较高(本 task)
  18. include_vars
  19. set_facts / registered vars: 比较常用 set_facts 更改一些后面要使用的变量,全局生效;registered vars 一般连续的 task 用的比较多
  20. role (and include_role) params
  21. include params
  22. extra vars (always win precedence): 执行 ansible-playbook 命令时候传入的 extra vars 级别最高,高于一切

1.2 变量的作用范围(Scoping variables)

  1. Global: this is set by config, environment variables and the command line
  2. Play: each play and contained structures, vars entries (vars; vars_files; vars_prompt), role defaults and vars.
  3. Host: variables directly associated to a host, like inventory, include_vars, facts or registered task outputs

Ansible 1.2 及以上的版本中,group_vars/ 和 host_vars/ 目錄可放在 inventory 目錄下,或是 playbook 目錄下. 如果兩個目錄下都存在,那麼 playbook 目錄下的配置會覆蓋 inventory 目錄的配置(对应解释上分 4-10 条变量优先级)

1.3 ansible 目录结构最佳实践

  • 结构如下:
 1├── playbooks.yml
 2├── inventory(inventory下可以任意加子目录进行分组)
 3|   ├── group_vars ├── all ├── *.yml
 4|   |              ├── *.yml
 5|   |              └── *
 6|   ├── host_vars
 7|   |
 8│   └── hosts.ini
 9|
10├── roles
11│   ├── common
12│   │   ├── files
13│   │   ├── handlers
14│   │   ├── meta
15│   │   ├── templates
16│   │   ├── tasks
17│   │   │   └── main.yml
18│   │   └── vars
19|   |
20│   ├── others
21│       ├── files
22│       ├── handlers
23│       ├── meta
24│       ├── templates
25│       ├── tasks
26│       │   └── main.yml
27│       └── vars
28└── *.yml
  1. 使用 roles 管理不同的安装模块
  2. 使用 inventory/host.ini 管理操作主机,对不同主机进行分组
  3. 使用 group_vars 管理基本的默认变量,playbook 中使用 set_facts 设置需要的变量,默认不想被修改的变量写在 roles/vars/main.yml
  4. 每个 task 都要写 name
  5. tags 写在 role 阶段

1.4 ansible-playbook roles 初始化行为规划

  1. 如果角色 /xxx/tasks/main.yaml 存在,则其中列出的任务将添加到任务中,否则将不会添加任务中
  2. 如果角色 /xxx/handlers/main.yaml 存在,则其中列出的处理程序将添加到任务中,否则将不会添加任务中
  3. 如果角色 /xxx/vars/main.yml 存在,则其中列出的处理程序将添加到任务中,否则将不会添加任务中
  4. 如果角色 /xxx/defaults/main.yml 存在,则其中列出的处理程序将添加到任务中,否则将不会添加任务中
  5. 如果角色 /xxx/meta/main.yml 存在,则其中列出的任何角色依赖项将添加到角色列表(1.3 及更高版本)
  6. 任何副本,脚本,模板或包含任务(在角色中)都可以引用 roles / x / {files,templates,tasks} /(dir 取决于任务)中的文件,而无需相对或绝对地路径化它们

1.5 ansible-playbook 执行顺序

  1. pre_tasks 游戏中定义的任何内容
  2. 列出的每个角色将依次执行。将首先运行角色中定义的任何角色依赖项,但需遵循标记过滤和条件。roles meta/main.yml
  3. tasks 游戏中定义的任何内容
  4. post_tasks 游戏中定义的任何内容

二、kubespray 部署集群 playbook 解读

github 地址:https://github.com/kubernetes-sigs/kubespray

  1$ gc https://github.com/kubernetes-sigs/kubespray
  2$ cd kubespray
  3
  4# 查看部署集群playbook
  5$ cat cluster.yml
  6---
  7# 检查ansible版本
  8- name: Check ansible version
  9  import_playbook: ansible_version.yml
 10
 11# 检查inventory中组的版本,进行适配旧版本的group名称
 12- name: Ensure compatibility with old groups
 13  import_playbook: legacy_groups.yml
 14
 15# 堡垒机配置(没有跳过)
 16- hosts: bastion[0]
 17  gather_facts: False
 18  environment: "{{ proxy_disable_env }}"
 19  roles:
 20    - { role: kubespray-defaults }
 21    - { role: bastion-ssh-config, tags: ["localhost", "bastion"] }
 22
 23# 默认linear,每个主机的单个task执行完成会等待其他都完成后再执行下个任务,设置free可不等待其他主机,继续往下执行(看起来会比较乱)
 24# linear策略即线性执行策略,线性执行策略指主机组内所有主机完成一个任务后才继续下一个任务的执行,在执行一个任务时,如果某个主机先执行完则会等待其他主机执行结束。说直白点就是第一个任务在指定的主机都执行完,再进行第二个任务的执行,第二个任务在指定的主机都执行完后,再进行第三个任务的执行…… 以此类推。
 25# free策略即自由策略,即在一个play执行完之前,每个主机都各顾各的尽可能快的完成play里的所有任务,而不会因为其他主机没执行完任务而等待,不受线性执行策略那样的约束。所以这种策略的执行结果给人感觉是无序的甚至是杂乱无章的,而且每次执行结果的task显示顺序很可能不一样
 26
 27# 此阶段对于etcd的每一个节点一个一个的进行kubespray-defaults(设置一些fallback_ip、noproxy的set_fact), bootstrap-os(装一些yum源,基础包,改一些主机名等操作)
 28- hosts: k8s_cluster:etcd
 29  strategy: linear
 30  any_errors_fatal: "{{ any_errors_fatal | default(true) }}"
 31  gather_facts: false
 32  environment: "{{ proxy_disable_env }}"
 33  roles:
 34    - { role: kubespray-defaults }
 35    - { role: bootstrap-os, tags: bootstrap-os}
 36
 37- name: Gather facts
 38  tags: always
 39  import_playbook: facts.yml
 40
 41# 此阶段对于etcd的每一个节点一个一个的进行kubespray-defaults(设置一些fallback_ip、noproxy的set_fact)、kubernetes/preinstall(预先配置一些环境,比如禁用SWAP、配置替换resolvconf,设置一些cni的bin路径等、创建/检查一些安装目录、期间对于特定文件触发handler重启相关服务、配置systemd-resolved、修改/etc/hosts文件等)、container-engine(选择容器runtime,进行安装、配置、reload等)、
 42# download(模块用于下载所有有需要的镜像,先在ansible执行机缓存,之后再下载所有需要的二进制包,包括kubeadm需要的、kubernetes集群需要的等等)
 43- hosts: k8s_cluster:etcd
 44  gather_facts: False
 45  any_errors_fatal: "{{ any_errors_fatal | default(true) }}"
 46  environment: "{{ proxy_disable_env }}"
 47  roles:
 48    - { role: kubespray-defaults }
 49    - { role: kubernetes/preinstall, tags: preinstall }
 50    - { role: "container-engine", tags: "container-engine", when: deploy_container_engine|default(true) }
 51    - { role: download, tags: download, when: "not skip_downloads" }
 52
 53# 此阶段开始执行etcd role,先进行kubespray-defaults(设置一些fallback_ip、noproxy的set_fact)、再进行etcd(检查、创建etcd证书,部署etcd集群,添加etcd节点,检查etcd状态,创建etcd执行用户等...)
 54- hosts: etcd
 55  gather_facts: False
 56  any_errors_fatal: "{{ any_errors_fatal | default(true) }}"
 57  environment: "{{ proxy_disable_env }}"
 58  roles:
 59    - { role: kubespray-defaults }
 60    - role: etcd
 61      tags: etcd
 62      vars:
 63        etcd_cluster_setup: true
 64        etcd_events_cluster_setup: "{{ etcd_events_cluster_enabled }}"
 65      when: not etcd_kubeadm_enabled| default(false)
 66
 67# 设置etcd_cluster_setup、etcd_events_cluster_setup为 false,主要是将k8s cluster中的机器,分发配置信息etcd的证书, 用于后续与etcd集群交互
 68- hosts: k8s_cluster
 69  gather_facts: False
 70  any_errors_fatal: "{{ any_errors_fatal | default(true) }}"
 71  environment: "{{ proxy_disable_env }}"
 72  roles:
 73    - { role: kubespray-defaults }
 74    - role: etcd
 75      tags: etcd
 76      vars:
 77        etcd_cluster_setup: false
 78        etcd_events_cluster_setup: false
 79      when: not etcd_kubeadm_enabled| default(false)
 80
 81# 此阶段执行kubernetes/nodes role,先进行kubespray-defaults(设置一些fallback_ip、noproxy的set_fact), 再进行kubernetes/node(检查docker cgroup, 配置一些参数等、拷贝一些数据,比如cni目录下的,等等、通过镜像挂载的方式配置安装kubelet、部署apiserver的负载均衡、确保预留nodePort端口范围,通过sysctl修改内核参数、确认kube-proxy ipvs需要的内核模块已开启)
 82- hosts: k8s_cluster
 83  gather_facts: False
 84  any_errors_fatal: "{{ any_errors_fatal | default(true) }}"
 85  environment: "{{ proxy_disable_env }}"
 86  roles:
 87    - { role: kubespray-defaults }
 88    - { role: kubernetes/node, tags: node }
 89
 90# 此阶段部署配置kube_control_plane机器,先进行kubespray-defaults(设置一些fallback_ip、noproxy的set_fact)、
 91# kubernetes/control-plane(删除control-plane静态文件,删除旧的master容器、下载好kubectl命令行工具、配置kubectl命令行工具自动补全、在kubeadm-setup.yml初始化Initialize first master,创建kubeadm的24h token,然后include_tasks: kubeadm-secondary.yml添加其他的master到集群中、检查etcd证书过期时间等后续检查、更新apiserver客户端的连接地址为fix后的api负载均衡地址、renew集群的证书们, 使用kubeadm renew的方式)
 92# kubernetes/client(建立/etc/kubernetes架构目录,拷贝kubeconfig到node并配置好ansible执行机上面的k8s client环境)
 93# kubernetes-apps/cluster_roles(通过配置好的kubectl执行,Apply workaround to allow all nodes with cert O=system:nodes to register, 这样之后的所有node加入申请都会自动审批通过...)
 94- hosts: kube_control_plane
 95  gather_facts: False
 96  any_errors_fatal: "{{ any_errors_fatal | default(true) }}"
 97  environment: "{{ proxy_disable_env }}"
 98  roles:
 99    - { role: kubespray-defaults }
100    - { role: kubernetes/control-plane, tags: master }
101    - { role: kubernetes/client, tags: client }
102    - { role: kubernetes-apps/cluster_roles, tags: cluster-roles }
103
104# 此阶段部署配置cluster里面的其他普通node节点,先进行kubespray-defaults(设置一些fallback_ip、noproxy的set_fact)、
105# 之后kubernetes/kubeadm(检查kubelet配置文件是否存在、检查kubelet的ca.crt证书是否存在、创建kubeadm join的token、更新kubelet中的地址为负载均衡的apiserver地址、重启kube-proxy的pods)、
106# kubernetes/node-label(根据变量中的{{ role_node_labels + inventory_node_labels }}给node打上对应的标签,通过kubectl label nodes)
107# network_plugin(根据变量决定要部署哪种网络插件,举例cilium的话就是check.yml检查cilium参数,install.yml渲染安装cilium需要的yaml文件到node、apply.yml执行kubectl部署网络插件到集群中)
108- hosts: k8s_cluster
109  gather_facts: False
110  any_errors_fatal: "{{ any_errors_fatal | default(true) }}"
111  environment: "{{ proxy_disable_env }}"
112  roles:
113    - { role: kubespray-defaults }
114    - { role: kubernetes/kubeadm, tags: kubeadm}
115    - { role: kubernetes/node-label, tags: node-label }
116    - { role: network_plugin, tags: network }
117
118# 如果网络插件部署的是calico,还需要到宿主机上面执行一些操作,这里我们选择cilium就跳过了
119- hosts: calico_rr
120  gather_facts: False
121  any_errors_fatal: "{{ any_errors_fatal | default(true) }}"
122  environment: "{{ proxy_disable_env }}"
123  roles:
124    - { role: kubespray-defaults }
125    - { role: network_plugin/calico/rr, tags: ['network', 'calico_rr'] }
126
127# windows master的额外配置,这里跳过, 一般不用
128- hosts: kube_control_plane[0]
129  gather_facts: False
130  any_errors_fatal: "{{ any_errors_fatal | default(true) }}"
131  environment: "{{ proxy_disable_env }}"
132  roles:
133    - { role: kubespray-defaults }
134    - { role: win_nodes/kubernetes_patch, tags: ["master", "win_nodes"] }
135
136# 这阶段就是在kube_control_plane也就是有权限kubectl的机器上面安装后续应用了,不是每个role都需要走,比如是否部署ingress_controller就由"ingress_nginx_enabled"管理,kubernetes-apps下有个meta/main.yml文件,根据参数选择安装不同的app,流程无非就是下载包,或者传输yaml文件之后kubectl apply
137- hosts: kube_control_plane
138  gather_facts: False
139  any_errors_fatal: "{{ any_errors_fatal | default(true) }}"
140  environment: "{{ proxy_disable_env }}"
141  roles:
142    - { role: kubespray-defaults }
143    - { role: kubernetes-apps/external_cloud_controller, tags: external-cloud-controller }
144    - { role: kubernetes-apps/network_plugin, tags: network }
145    - { role: kubernetes-apps/policy_controller, tags: policy-controller }
146    - { role: kubernetes-apps/ingress_controller, tags: ingress-controller }
147    - { role: kubernetes-apps/external_provisioner, tags: external-provisioner }
148    - { role: kubernetes-apps, tags: apps }
149
150# 这阶段是最后收尾阶段完善集群的dns,host文件,resolv文件里面的格式化对应项等, 至此kubernetes集群通过ansible kubespray安装完毕
151- hosts: k8s_cluster
152  gather_facts: False
153  any_errors_fatal: "{{ any_errors_fatal | default(true) }}"
154  environment: "{{ proxy_disable_env }}"
155  roles:
156    - { role: kubespray-defaults }
157    - { role: kubernetes/preinstall, when: "dns_mode != 'none' and resolvconf_mode == 'host_resolvconf'", tags: resolvconf, dns_late: true }

1.2 实际操作使用 ansible-playbook 部署单节点集群

部署参考文档:https://kubespray.io/#/、https://kubespray.io/#/docs/downloads

 1$ sudo apt-get install python3-pip
 2$ cd kubespray
 3$ sudo pip3 install -r requirements.txt
 4# 看下面两个文件有没有要删除的东西
 5$ vim inventory/mycluster/group_vars/all/all.yml
 6$ vim inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
 7# download缓存相关配置可以查看链接:https://kubespray.io/#/docs/downloads
 8# 配置免密sudo,配置免密ssh节点
 9$ vim /etc/sudoers
10vagrant ALL=NOPASSWD:ALL
11
12# 开始部署集群
13$ ansible-playbook -i inventory/k8sdemo/inventory.ini  --become --become-user=root cluster.yml
14# 部署结果无报错
15...
16PLAY RECAP *******************************************************************************************************************************************************
17localhost                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
18node1                      : ok=650  changed=177  unreachable=0    failed=0    skipped=1031 rescued=0    ignored=2
19...
20# 查看集群状态
21$ kubectl get nodes
22NAME    STATUS   ROLES                  AGE     VERSION
23node1   Ready    control-plane,master   3m49s   v1.22.2
24$ kubectl get cs
25Warning: v1 ComponentStatus is deprecated in v1.19+
26NAME                 STATUS    MESSAGE             ERROR
27scheduler            Healthy   ok
28controller-manager   Healthy   ok
29etcd-0               Healthy   {"health":"true"}
30$ kubectl get pods -A
31NAMESPACE     NAME                              READY   STATUS    RESTARTS   AGE
32kube-system   cilium-dtgnn                      1/1     Running   0          3m16s
33kube-system   cilium-operator-66576fd87-bklpc   1/1     Running   0          3m15s
34kube-system   coredns-8474476ff8-85qkx          1/1     Running   0          3m9s
35kube-system   coredns-8474476ff8-xtmst          0/1     Pending   0          2m45s
36kube-system   dns-autoscaler-7df78bfcfb-gsqjb   1/1     Running   0          3m5s
37kube-system   kube-apiserver-node1              1/1     Running   0          4m
38kube-system   kube-controller-manager-node1     1/1     Running   1          4m
39kube-system   kube-proxy-h2lgj                  1/1     Running   0          3m16s
40kube-system   kube-scheduler-node1              1/1     Running   1          4m
41kube-system   nodelocaldns-xnvv7                1/1     Running   0          3m4s

参考链接:
ansible 变量详解
ansible 变量优先级官方原文