1.1 Ansible介绍
Ansible是一种IT自动化工具。它可以配置系统,部署软件以及协调更高级的IT任务,例如持续部署,滚动更新。Ansible适用于管理企业IT基础设施,从具有少数主机的小规模到数千个实例的企业环境。Ansible也是一种简单的自动化语言,可以完美地描述IT应用程序基础结构。
具备以下三个特点:
- 简单:减少学习成本
- 强大:协调应用程序生命周期
- 无代理:可预测,可靠和安全
使用文档: https://docs.ansible.com/
安装Ansible:yum install ansible -y
- Inventory:Ansible管理的主机信息,包括IP地址、SSH端口、账号、密码等
- Modules:任务均有模块完成,也可以自定义模块,例如经常用的脚本。
- Plugins:使用插件增加Ansible核心功能,自身提供了很多插件,也可以自定义插件。例如connection插件,用于连接目标主机。
- Playbooks:“剧本”,模块化定义一系列任务,供外部统一调用。Ansible核心功能。
1.2 主机清单
# vim /etc/ansible/hosts
[webservers]
alpha.example.org
beta.example.org
172.16.1.81
www[001:006].example.com
[dbservers]
db01.intranet.mydomain.net
db02.intranet.mydomain.net
10.0.0.81
10.0.0.82
db-[99:101]-node.example.com
1.3 命令行使用
ad-hoc命令可以输入内容,快速执行某个操作,但不希望留存记录。
ad-hoc命令是理解Ansible和在学习playbooks之前需要掌握的基础知识。
一般来说,Ansible的真正能力在于剧本。
1 连接远程主机认证
开启/etc/ansible/ansible.cfg配置文件中的host_key_checking = False参数,关闭远程主机ssh指纹认证,实现免交互。
https://docs.ansible.com/ansible/2.9/reference_appendices/config.html
https://docs.ansible.com/ansible/2.9/user_guide/connection_details.html
(1) Ansible 通过SSH 协议与远程机器通信。默认情况下,Ansible 使用本机 OpenSSH 并使用您当前的用户名连接到远程计算机,就像 SSH 一样
。
(2) 优先级
https://docs.ansible.com/ansible/2.9/reference_appendices/general_precedence.html
Ansible 提供了四种控制其行为的来源。按照从最低(最容易覆盖)到最高(覆盖所有其他)的优先级顺序,类别是:
- 配置设置
- 命令行选项
- 剧本关键词
- 变量
1) 变量优先级:
命令行变量( -e ) > playbook变量 > inventory主机变量 > inventory组变量
第一优先级:全局范围的变量(例如:执行临时命令时指定的变量)。 第二优先级:playbook 里的变量。 第三优先级:主机范围里的变量(例如:清单中的服务器或服务器组) 第四优先级:清单中的服务器优先级高于清单中的服务器组
2) 配置设置优先级:
ANSIBLE_CONFIG(如果设置了环境变量) > ./ansible.cfg(在当前目录中) > ~/.ansible.cfg(当前用户家目录中) > /etc/ansible/ansible.cfg
检查当前使用的是哪个配置文件:ansible --version 命令中显示config file配置。
3) 连接远端主机在命令行使用 -u -k 的优先级
inventory > playbook > ad-hoc(命令行)
(3) 主机清单
https://docs.ansible.com/ansible/2.9/user_guide/intro_inventory.html#inventory
https://docs.ansible.com/ansible/2.9/reference_appendices/YAMLSyntax.html#yaml-syntax
SSH密码认证:
[webservers]
172.16.1.81 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass="123456"
172.16.1.82:22 ansible_ssh_user=root ansible_ssh_pass="123456"
SSH密钥对认证:
# ssh-keygen
# ssh-copy-id <目标主机用户名>@<目标主机>
[webservers]
172.16.1.81 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_key=/root/.ssh/id_rsa
172.16.1.82:22 ansible_ssh_user=root
也可以ansible.cfg在配置文件中指定:
[defaults]
private_key_file = /root/.ssh/id_rsa # 默认路径
2 常用选项
选项 | 描述 |
-C, --check | 运行检查,不执行任何操作 |
-e EXTRA_VARS,--extra-vars=EXTRA_VARS | 设置附加变量 key=value |
-u REMOTE_USER, --user=REMOTE_USER | SSH连接用户,默认None |
-k, --ask-pass | SSH连接用户密码 |
-b, --become | 提权,默认root |
-K, --ask-become-pass | 提权密码 |
3 命令行使用
https://docs.ansible.com/ansible/2.9/user_guide/intro_adhoc.html
ansible all -m ping
ansible all -m shell -a "ls /root" -u root -k
ansible webservers -m copy –a "src=/etc/hosts dest=/tmp/hosts"
1.4 常用模块
ansible-doc –l 查看所有模块
ansible-doc –s copy 查看模块文档
模块文档:https://docs.ansible.com/ansible/2.9/modules/modules_by_category.html
1 shell
https://docs.ansible.com/ansible/2.9/modules/shell_module.html#shell-module
在目标主机执行shell命令。
- name: 将命令结果输出到指定文件
shell: somescript.sh >> somelog.txt
- name: 切换目录执行命令
shell:
cmd: ls -l | grep log
chdir: somedir/
- name: 编写脚本
shell: |
if [ 0 -eq 0 ]; then
echo yes > /tmp/result
else
echo no > /tmp/result
fi
args:
executable: /bin/bash
2 copy
https://docs.ansible.com/ansible/2.9/modules/copy_module.html#copy-module
将文件复制到远程主机。
- name: 拷贝文件
copy:
src: /srv/myfiles/foo.conf
dest: /etc/foo.conf
owner: foo
group: foo
mode: u=rw,g=r,o=r
# mode: u+rw,g-wx,o-rwx
# mode: '0644'
backup: yes
3 file
管理文件和文件属性。
https://docs.ansible.com/ansible/2.9/modules/file_module.html#file-module
- name: 创建目录
file:
path: /etc/some_directory
state: directory
#state: link
#state: hard
#state: touch
owner: foo
group: foo
mode: '0755'
- name: 删除文件
file:
path: /etc/foo.txt
state: absent
- name: 递归删除目录
file:
path: /etc/foo
state: absent
touch: 表示创建文件
4 yum
软件包管理。
https://docs.ansible.com/ansible/2.9/modules/yum_module.html#yum-module
- name: 安装最新版apache
yum:
name: httpd
state: latest
- name: 安装列表中所有包
yum:
name:
- nginx
- postgresql
- postgresql-server
state: present
- name: 卸载apache包
yum:
name: httpd
state: absent
- name: 更新所有包
yum:
name: '*'
state: latest
- name: 安装nginx来自远程repo
yum:
name: http://nginx.org/packages/rhel/7/x86_64/RPMS/nginx-1.14.0-1.el7_4.ngx.x86_64.rpm
# name: /usr/local/src/nginx-release-centos-6-0.el6.ngx.noarch.rpm
state: present
present,latest:表示安装
absent:表示卸载
5 service/systemd
管理服务。
https://docs.ansible.com/ansible/2.9/modules/service_module.html#service-module
控制远程主机上的服务。支持的初始化系统包括 BSD init、OpenRC、SysV、Solaris SMF、systemd、upstart。
- name: 服务管理
service:
name: etcd
state: started
#state: stopped
#state: restarted
#state: reloaded
- name: 设置开机启动
service:
name: httpd
enabled: yes
https://docs.ansible.com/ansible/2.9/modules/systemd_module.html#systemd-module
控制远程主机上的 systemd 服务。
- name: 服务管理
systemd:
name: etcd
state: restarted
enabled: yes
daemon_reload: yes
6 unarchive
https://docs.ansible.com/ansible/2.9/modules/unarchive_module.html#unarchive-module
- name: 解压
unarchive:
src=test.tar.gz
dest=/tmp
7 debug
执行过程中打印语句。
https://docs.ansible.com/ansible/2.9/modules/debug_module.html#debug-module
# 打印每个主机的地址
- debug: msg="{{ inventory_hostname }}"
- debug: var=inventory_hostname
- debug:
msg: System "{{ inventory_hostname }}"
- debug:
var: inventory_hostname
- name: Get hostname
debug: msg="{{ inventory_hostname }}"
- name: Get hostname
debug: var=inventory_hostname
- name: Get hostname
debug:
msg:
- "{{ inventory_hostname }}"
- name: 显示主机已知的所有变量
debug:
var: hostvars[inventory_hostname]
#verbosity: 4
(1) hostvars
hostvars[]是一个字典,包括ansible默认变量、inventory变量、远程主机信息变量,[]内填写的是远程主机的连接方式(IP地址或DNS可以解析的
名称)。要获得远程主机信息变量需要设置gather_facts: true参数(默认是true)。
(2) inventory_hostname
inventory_hostname是在ansible清单文件中配置连接远程主机的变量(例如:/etc/ansible/hosts),它可以是IP地址或DNS可以解析的名称。
例:
---
- hosts: webservers
vars:
http_port: 80
server_name: www.lc.com
remote_user: root
gather_facts: false
tasks:
- name: Get hostname
debug:
msg:
- "{{ hostvars[inventory_hostname].inventory_hostname }}"
- "{{ inventory_hostname }}"
(3) ansible_hostname
ansible_hostname是远程主机信息变量。
例:
---
- hosts: webservers
vars:
http_port: 80
server_name: www.lc.com
remote_user: root
gather_facts: true
tasks:
- name: Get hostname
debug:
msg:
- "{{ hostvars[inventory_hostname].ansible_hostname }}"
- "{{ ansible_hostname }}"
(4) {{ }} 或 var 表示引用变量
1.5 Playbook
Playbooks是Ansible的配置,部署和编排语言。他们可以描述您希望在远程机器做哪些事或者描述IT流程中一系列步骤。使用易读的YAML格式组织Playbook文件。
如果Ansible模块是您工作中的工具,那么Playbook就是您的使用说明书,而您的主机资产文件就是您的原材料。
与adhoc任务执行模式相比,Playbooks使用ansible是一种完全不同的方式,并且功能特别强大。
https://docs.ansible.com/ansible/2.9/user_guide/playbooks.html
---
- hosts: webservers
vars:
http_port: 80
server_name: www.lc.com
remote_user: root
gather_facts: false
tasks:
- name: 安装nginx最新版
yum: name=nginx state=latest
- name: 写入nginx配置文件
template: src=/srv/nginx.j2 dest=/etc/nginx/nginx.conf
notify:
- restart nginx
- name: 确保nginx正在运行
service: name=nginx state=started enabled=yes
handlers:
- name: restart nginx
service: name=nginx state=restarted
# ansible-playbook -i /etc/ansible/hosts nginx.yaml
1 主机和用户
https://docs.ansible.com/ansible/2.9/user_guide/become.html
---
- hosts: webservers
remote_user: lc
become: yes
become_user: root
ansible-playbook nginx.yaml -u lc -k -b -K
2 定义变量
变量是应用于多个主机的便捷方式; 实际在主机执行之前,变量会对每个主机添加,然后在执行中引用。
- 命令行传递
-e VAR=VALUE
- 主机变量与组变量
在Inventory中定义变量。
[webservers]
172.16.1.81 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass="123456" hostname=web1
172.16.1.82:22 ansible_ssh_user=root ansible_ssh_pass="123456" hostname=web2
[webservers:vars]
hostname=web3
- 单文件存储
Ansible中的首选做法是不将变量存储在Inventory中。除了将变量直接存储在Inventory文件之外,主机和组变量还可以存储在相对于Inventory文件的单个文件中。Ansible 通过搜索相对于inventory file or the playbook file的路径来加载主机组变量文件。
https://docs.ansible.com/ansible/2.9/user_guide/intro_inventory.html#inventory
组变量:
group_vars 存放的是组变量目录
group_vars/all.yml 表示所有主机有效,等同于[all:vars]
grous_vars/etcd.yml 表示etcd组主机有效,等同于[etcd:vars]
# vi /etc/ansible/group_vars/all.yml
work_dir: /data
# vi /etc/ansible/host_vars/webservers.yml
nginx_port: 80
- hosts: webservers
vars:
http_port: 80
server_name: www.lc.com
- Register变量
- shell: /usr/bin/uptime
register: result
- debug: msg="{{result.stdout}}"
- debug: var=result.stdout
3 任务列表
每个play包含一系列任务。这些任务按照顺序执行,在play中,所有主机都会执行相同的任务指令。play目的是将选择的主机映射到任务。
tasks:
- name: 安装nginx最新版
yum: pkg=nginx state=latest
4 语法检查与调试
语法检查:ansible-playbook --check /path/to/playbook.yaml
测试运行,不实际操作:ansible-playbook -C /path/to/playbook.yaml
debug模块在执行期间打印语句,对于调试变量或表达式,而不必停止play。与'when:'指令一起调试更佳。
- debug: msg="{{group_names}}"
- name: 主机名
debug:
msg: "{{inventory_hostname}}"
5 任务控制
如果你有一个大的剧本,那么能够在不运行整个剧本的情况下运行特定部分可能会很有用。
tasks:
- name: 安装nginx最新版
yum: pkg=nginx state=latest
tags: install
- name: 写入nginx配置文件
template: src=/srv/nginx.j2 dest=/etc/nginx/nginx.conf
tags: config
使用:
# ansible-playbook example.yml --tags "install"
# ansible-playbook example.yml --tags "install,config"
# ansible-playbook example.yml --skip-tags "install"
6 流程控制
条件:
https://docs.ansible.com/ansible/2.9/user_guide/playbooks_conditionals.html
tasks:
- name: 只在172.16.1.81运行任务
debug: msg="{{ansible_default_ipv4.address}}"
when: ansible_default_ipv4.address == '172.16.1.81'
循环:
https://docs.ansible.com/ansible/2.9/user_guide/playbooks_loops.html
tasks:
- name: 批量创建用户
user: name={{ item }} state=present groups=wheel
with_items:
- testuser1
- testuser2
- name: 批量拷贝
copy: src={{ item }} dest=/tmp
with_fileglob:
- "/tmp/*.txt"
常用循环语句:
语句 | 描述 |
with_items | 标准循环 |
with_fileglob | 遍历目录文件 |
with_dict | 遍历字典 |
7 模板
https://docs.ansible.com/ansible/2.9/user_guide/playbooks_templating.html
vars:
domain: "www.lc.com"
tasks:
- name: 写入nginx配置文件
template: src=/srv/nginx.j2 dest=/etc/nginx/conf.d/server.conf
# nginx.j2
{% set domain_name = domain %}
upstream web {
{% for host in groups['webservers'] %}
server {{ hostvars[host].inventory_hostname }}
{{ endfor }}
}
server {
listen 80;
server_name {{ domain_name }};
location / {
root /usr/share/html;
}
}
在jinja里使用ansible变量直接{{ }}引用。使用ansible变量赋值jinja变量不用{{ }}引用。
定义变量:
{% set local_ip = inventory_hostname %}
条件和循环:
{% set list=['one', 'two', 'three'] %}
{% for i in list %}
{% if i == 'two' %}
-> two
{% elif loop.index == 3 %}
-> 3
{% else %}
{{ i }}
{% endif %}
{% endfor %}
例如:生成连接etcd字符串
{% for host in groups['etcd'] %}
https://{{ hostvars[host].inventory_hostname }}:2379
{% if not loop.last %},{% endif %}
{% endfor %}
里面也可以用ansible的变量。
1.6 Roles
Roles是基于已知文件结构自动加载某些变量文件,任务和处理程序的方法。按角色对内容进行分组,适合构建复杂的部署环境。
https://docs.ansible.com/ansible/2.9/user_guide/playbooks_reuse_roles.html
1 定义Roles
Roles目录结构:
site.yml
webservers.yml
fooservers.yml
roles/
common/
tasks/
handlers/
files/
templates/
vars/
defaults/
meta/
webservers/
tasks/
defaults/
meta/
tasks
- -包含角色要执行的任务的主要列表。
handlers
- -包含处理程序,此角色甚至在此角色之外的任何地方都可以使用这些处理程序。
defaults
- -角色的默认变量
vars
- -角色的其他变量
files
- -包含可以通过此角色部署的文件。
templates
- -包含可以通过此角色部署的模板。
meta
- -为此角色定义一些元数据。请参阅下面的更多细节。
1 创建Roles目录结构的步骤
(1) 创建一个目录
# mkdir -p /root/ansible-install-k8s/
# cd /root/ansible-install-k8s/
(2) 创建组变量目录,用于存放组变量文件,和hosts清单文件在同级目录下
# mkdir -p group_vars/
(3) 创建主机清单文件
# touch hosts
(4) 创建roles分组目录
# mkdir -p roles/
(5) 创建roles分组目录的入口文件
# touch multi-master-deploy.yml
(6) 在roles分组目录下创建具体的roles
# mkdir -p roles/master/{tasks,files,templates}
# 创建该roles的执行入口文件
# touch roles/master/tasks/main.yml
(7) 注
1) 可以使用命令生成roles,然后删除不需要的
# ansible-galaxy init roles/master
2) 创建ansible.cfg文件,执行时当前目录下的生效
# touch ansible.cfg
# tree ansible-install-k8s/
ansible-install-k8s/
├── group_vars
├── hosts
├── multi-master-deploy.yml
└── roles
└── master
├── files
├── tasks
│ └── main.yml
└── templates
6 directories, 3 files
2 角色控制
---
- name: 0.系统初始化
gather_facts: false
hosts: all
roles:
- common
tags: common
- name: 1.自签证书
gather_facts: false
hosts: localhost
roles:
- tls
tags: tls
1.7 部署步骤
注:确保所有节点系统时间一致
操作系统要求:CentOS7.x_x64
所有节点root密码保持一致
示例参考:https://github.com/ansible/ansible-examples
1 熟悉二进制部署K8S步骤
- 服务器规划
角色 | IP | 组件 |
k8s-master1 | 172.16.1.81 | kube-apiserver kube-controller-manager kube-scheduler kubectl etcdkube-proxy kubeletdocker |
k8s-master2 | 172.16.1.82 | kube-apiserver kube-controller-manager kube-scheduler kubectl etcdkube-proxy kubeletdocker |
k8s-node1 | 172.16.1.83 | etcd kube-proxy kubeletdocker |
k8s-node2 | 172.16.1.84 | kube-proxy kubelet docker |
Load Balancer(Master) | 172.16.1.81 VIP: 172.16.1.80 | nginx keepalived |
Load Balancer(Backup) | 172.16.1.82 | nginx keepalived |
- 系统初始化
- 关闭selinux,firewalld
- 关闭swap
- 时间同步
- 写hosts
- Etcd集群部署
- 生成etcd证书
- 部署三个etcd集群
- 查看集群状态
- 部署Master
- 生成apiserver证书
- 部署apiserver、controller-manager和scheduler组件
- 启动TLS Bootstrapping
- 部署Node
- 安装Docker
- 部署kubelet和kube-proxy
- 在Master上允许为新Node颁发证书
- 授权apiserver访问kubelet
- 部署插件(准备好镜像)
- Calico
- Web UI
- CoreDNS
- Ingress Controller
- Master高可用
- 增加Master节点(与Master1一致)
- 部署Nginx负载均衡器
- Nginx+Keepalived高可用
- 修改Node连接VIP
2 Roles组织K8S各组件部署解析
编写建议:
- 梳理流程和Roles结构
- 如果配置文件有不固定内容,使用jinja渲染
- 人工干预改动的内容应统一写到一个文件中
3 找一台服务器安装Ansible
# yum install epel-release -y
# yum install ansible -y
4 下载所需文件
下载Ansible部署文件:
# git clone https://github.com/HyjyLc/ansible-install-k8s
# cd ansible-install-k8s
下载准备好软件包(包含所有涉及文件和镜像,比较大),解压到/root目录:
云盘链接:https://pan.baidu.com/s/1uCLylsj1-W2HigS_Tn9b5g 提取码:bicc
# tar zxf binary_pkg.tar.gz
5 修改Ansible文件
修改hosts文件,根据规划修改对应IP和名称。
# vi hosts
...
修改group_vars/all.yml文件,修改软件包目录和证书可信任IP。
# vim group_vars/all.yml
software_dir: '/root/binary_pkg'
...
cert_hosts:
k8s:
etcd:
6 架构图
单Master架构
多Master架构
7 部署命令
单Master版:
# ansible-playbook -i hosts single-master-deploy.yml -uroot -k
多Master版:
# ansible-playbook -i hosts multi-master-deploy.yml -uroot -k
本文档使用多Master版本部署
8 查看集群节点
# kubectl get node
NAME STATUS ROLES AGE VERSION
k8s-master1 Ready <none> 1m v1.20.4
k8s-master2 Ready <none> 1m v1.20.4
k8s-node1 Ready <none> 53s v1.20.4
k8s-node2 Ready <none> 1m v1.20.4
9 其他
(1) 部署控制
如果安装某个阶段失败,可针对性测试.
例如:只运行部署插件
# ansible-playbook -i hosts single-master-deploy.yml -uroot -k --tags addons
(2) 节点扩容
1)修改hosts,添加新节点ip
# vi hosts
...
[newnode]
172.16.1.85 node_name=k8s-node3
2)执行部署
# ansible-playbook -i hosts add-node.yml -uroot -k
(3) 所有HTTPS证书存放路径
部署产生的证书都会存放到目录“ansible-install-k8s-master/ssl”,一定要保存好,后面还会用到~
(4) 连接api的地址(nginx vip地址)
kube-controller-manager.kubeconfig
kube-scheduler.kubeconfig
kube-proxy.kubeconfig
kubelet.kubeconfig
(5) 连接本地api-server地址(也可以改为nginx vip地址)
/root/.kube/config