#k8s集群部署# #Ansible#

前期手动部署了k8s集群,过程比较复杂,为了提高集群部署的效率,所以决定做一下自动化部署,为此,最近学习了ansible,同时也进一步巩固一下k8s基础知识。

刚完成了基础环境配置部分,记录一下,如有建议请不吝赐教。我使用的操作系统是RockyLinux 9,最小化安装,集群为3master+2node。

1、服务管理

在部署期间,可以暂时关闭防火墙,以减少因端口问题产生的问题(生产环境中,完成部署后,要开启防火墙,按需开放端口);

- name: Stop and disable firewalld
  ansible.builtin.systemd: name=firewalld state=stopped enabled=no

另外需要关闭SELinux,这是Linux内核的强制访问控制安全模块,主要用来限制进程对文件、端口、资源的访问,K8s + Docker/containerd + CNI 网络插件,很容易和 SELinux 策略冲突,导致容器无法挂载目录、端口无法绑定、socket无法访问、容器启动失败等问题,生产环境可以配置SELinux策略+容器securityContext。

- name: Disable SELinux immediately
  ansible.builtin.command: setenforce 0
  changed_when: true
  ignore_errors: yes

- name: Disable SELinux permanently
  ansible.builtin.selinux: state=disabled

- name: Disable SELinux in /etc/sysconfig/selinux
  ansible.builtin.replace:
    path: /etc/sysconfig/selinux
    regexp: '^SELINUX=enforcing'
    replace: 'SELINUX=disabled'
  ignore_errors: yes

一般Linux中会有dnsmasq服务,属于轻量dns缓存+dhcp服务,其端口(53)会与k8s集群的CoreDNS端口冲突,也要关闭。

- name: Stop and disable dnsmasq
  ansible.builtin.systemd:
    name: dnsmasq
    state: stopped
    enabled: no

本集群中日志方面使用的是rsyslog服务,原因如下:①默认预装,不用额外安装,节点零侵入;②轻量、高性能、低资源占用;③稳定可靠、配置简单;④完美适配k8s容器日志架构,不依赖容器运行时日志驱动;⑤方便对接各种日志平台。

- name: Enable and start rsyslog
  ansible.builtin.systemd:
    name: rsyslog
    state: started
    enabled: yes

2、关闭SWAP

SWAP本质是从磁盘取一部分空间,作为“低速内存”,当内存不足时缓解内存压力,但磁盘速度比内存慢上万倍,而k8s集群的硬性需求是高并发、低延迟,一旦使用SWAP,Pod响应延迟将会暴增、接口超时,相关网络组件(kube-proxy、Calico、CoreDNS)对延迟和内存及其敏感……据说k8s官方设计之初就假设所有容器只使用物理内存,不依赖磁盘交换,在新版集群中,kubelet启动会硬性校验/proc/meminfo,如果SWAP不为0,则直接启动失败。

- name: Disable swap immediately
  ansible.builtin.command: swapoff -a
  changed_when: true
  ignore_errors: yes

- name: Comment out swap entries in fstab
  ansible.builtin.replace:
    path: /etc/fstab
    regexp: '^([^#].*swap)'
    replace: '#\1'
    backup: yes

- name: Set vm.swappiness to 0
  ansible.builtin.sysctl:
    name: vm.swappiness
    value: '0'
    state: present
    reload: yes

3、基础软件和系统方面配置

3.1 host解析
- name: Configure /etc/hosts
  ansible.builtin.blockinfile:
    path: /etc/hosts
    block: |
      10.0.1.31 k8s-master01
      10.0.1.32 k8s-master02
      10.0.1.33 k8s-master03
      10.0.1.41 k8s-node01
      10.0.1.42 k8s-node02
    marker: "# {mark} ANSIBLE MANAGED BLOCK"
    backup: yes
3.2 配置yum源
#配置yum源:注释mirrorlist
- name: Comment out mirrorlist in repo files
  ansible.builtin.replace:
    path: "{{ item }}"
    regexp: '^mirrorlist='
    replace: '#mirrorlist='
    backup: yes
  loop: "{{ lookup('fileglob', '/etc/yum.repos.d/*.repo') }}"

#配置yum源:替换为阿里云baseurl
- name: Replace baseurl with Aliyun mirror
  ansible.builtin.replace:
    path: "{{ item }}"
    regexp: '^#baseurl=http://dl\.rockylinux\.org/\$contentdir'
    replace: 'baseurl=https://mirrors.aliyun.com/rockylinux'
    backup: yes
  loop: "{{ lookup('fileglob', '/etc/yum.repos.d/*.repo') }}"

#更新缓存
- name: Update yum cache
  ansible.builtin.command: yum makecache
  changed_when: true
3.3 安装基础软件

这里将epel跟其他软件分开安装了,因为这个epel-release 是一个特殊的 RPM,它会创建 /etc/yum.repos.d/epel*.repo 仓库文件。如果把它和依赖 EPEL 源的包(如 ntpsec)放在同一个 dnf 任务里,第一次执行时 dnf 可能还没刷新到新仓库,导致找不到包而报错。

- name: Install epel-release repository
  ansible.builtin.dnf:
    name: epel-release
    state: present

- name: Install base packages
  ansible.builtin.dnf:
    name:
    - git
    - vim
    - wget
    - jq
    - psmisc
    - net-tools
    - telnet
    - device-mapper-persistent-data
    - lvm2
    - yum-utils
    - ntpsec
    - ipvsadm
    - ipset
    - sysstat
    - conntrack
    - libseccomp
    state: present
    enablerepo: epel
    update_cache: yes
3.4 系统升级

基础软件安装完后,做一次系统升级

- name: Upgrade all packages
  ansible.builtin.dnf:
    name: '*'
    state: latest
    update_cache: yes
3.5 配置时区与时间同步
- name: Set timezone to Shanghai
  ansible.builtin.file:
    src: /usr/share/zoneinfo/Asia/Shanghai
    dest: /etc/localtime
    state: link
    force: yes

- name: Set timezone file
  ansible.builtin.copy:
    content: "Asia/Shanghai\n"
    dest: /etc/timezone
    mode: '0644'

- name: Sync time with ntpdate
  ansible.builtin.command: /usr/sbin/ntpdate time2.aliyun.com
  changed_when: true

- name: Add ntpdate to crontab
  ansible.builtin.cron:
    name: "sync time with aliyun ntp"
    minute: "*/5"
    job: "/usr/sbin/ntpdate time2.aliyun.com"
3.6 系统资源配置

blockinfile模块功能:①在指定文件中插入/更新一整块多行文本;②通过marker标记区块,幂等执行(同一个操作执行多次,结果保持一致);③只修改标记范围内的内容,不影响其他原有配置。

/etc/security/limits.conf是Linux PAM资源限制配置文件,控制系统用户/进程的:最大打开文件数、最大进程数、内存锁定等限制。

*表示对所有用户生效(含root、普通用户、容器进程);soft表示软限制,系统默认上限,进程可临时调高至hard值;hard表示硬限制,为最高上限,进程无法突破。

nofile:最大可打开文件句柄数

nproc:最大用户进程数

memlock:锁定内存,设置为unlimited,不限制锁定内存大小,防止关键进程内存被换入SWAP。

- name: Configure system limits
  ansible.builtin.blockinfile:
    path: /etc/security/limits.conf
    block: |
      * soft nofile 65536
      * hard nofile 131072
      * soft nproc 65535
      * hard nproc 655350
      * soft memlock unlimited
      * hard memlock unlimited
    marker: "# {mark} ANSIBLE MANAGED BLOCK"
    backup: yes
3.7 k8s网络相关内核模块配置

第一部分:

modprobe:等价系统命令modprobe 模块名

overlay:容器叠加网络模块,负责容器跨主机通信,Flannel、Calico等主流CNI都依赖

br_netfilter:网桥网络过滤模块,开启iptables对网桥流量的转发/过滤;

以上两个模块是容器网络的基础,不加载则CNI插件无法正常工作。

第二部分 :

kube-proxy有两种模式:iptables(默认)和IPVS(高性能负载均衡)

临时加载IPVS全套调度模块,保证kube-proxy切换到IPVS模式可以正常运行。

第三部分:

路径/etc/modules-load.d/*.conf是systemd标准目录,其中为内核模块列表,开机自动加载,前两部分是modprobe临时加载(立即生效,重启后丢失),这里是永久生效(重启后由systemd-modules-load读取配置后再加载)。

第四部分:

systemd-modules-load是systemd自带服务,专门负责开机读取/etc/modules-load.d/下的配置、自动加载内核模块。

#加载k8s网络相关内核模块
- name: Load overlay and br_netfilter modules
  ansible.builtin.modprobe:
    name: "{{ item }}"
    state: present
  loop:
    - overlay
    - br_netfilter

- name: Load ipvs kernel modules
  ansible.builtin.modprobe:
    name: "{{ item }}"
    state: present
  loop:
    - ip_vs
    - ip_vs_rr
    - ip_vs_wrr
    - ip_vs_sh
    - nf_conntrack
#配置开机自动加载的模块(ipvs.conf)
- name: Create ipvs modules load config
  ansible.builtin.copy:
    dest: /etc/modules-load.d/ipvs.conf
    content: |
      ip_vs
      ip_vs_lc
      ip_vs_wlc
      ip_vs_rr
      ip_vs_wrr
      ip_vs_lblc
      ip_vs_lblcr
      ip_vs_dh
      ip_vs_sh
      ip_vs_fo
      ip_vs_nq
      ip_vs_sed
      ip_vs_ftp
      ip_vs_sh
      nf_conntrack
      ip_tables
      ip_set
      xt_set
      ipt_set
      ipt_rpfilter
      ipt_REJECT
      ipip
    mode: '0644'
- name: Enable systemd-modules-load service
  ansible.builtin.systemd:
    name: systemd-modules-load
    state: started
    enabled: yes
  ignore_errors: yes

3.8 内核优化配置

第一部分:

/etc/sysctl.d/是systemd管理的内核参数目录,目录下所有.conf文件都会被统一加载,永久生效。

单独建立k8s.conf文件,和系统配置解耦,便于维护、排查、回滚。

第二部分:

生效所有sysctl配置

- name: Configure sysctl for Kubernetes
  ansible.builtin.copy:
    dest: /etc/sysctl.d/k8s.conf
    content: |
      #网络转发
      net.ipv4.ip_forward = 1
      net.bridge.bridge-nf-call-iptables = 1
      net.bridge.bridge-nf-call-ip6tables = 1
      fs.may_detach_mounts = 1
      net.ipv4.conf.all.route_localnet = 1
      #内存管理
      vm.overcommit_memory = 1
      vm.panic_on_oom = 0
      #文件描述符、inotify
      fs.inotify.max_user_watches = 89100  #值偏低,建议改为524288
      fs.file-max = 52706963
      fs.nr_open = 52706963
      #连接追踪
      net.netfilter.nf_conntrack_max = 2310720
      #tcp保活与孤儿连接回收
      net.ipv4.tcp_keepalive_time = 600
      net.ipv4.tcp_keepalive_probes = 3
      net.ipv4.tcp_keepalive_intvl = 15
      net.ipv4.tcp_max_orphans = 327680
      net.ipv4.tcp_orphan_retries = 3
      #高并发与syn flood防护
      net.ipv4.tcp_max_tw_buckets = 36000
      net.ipv4.tcp_tw_reuse = 1
      net.ipv4.tcp_syncookies = 1
      net.ipv4.tcp_max_syn_backlog = 16384
      net.ipv4.tcp_timestamps = 0
      net.core.somaxconn = 16384
    mode: '0644'
- name: Apply sysctl configuration
  ansible.builtin.command: sysctl --system
  changed_when: true

3.8 master01节点克隆源码

可以在master01上提前准备Calico、krm、metrics-server的源码。

- name: Clone k8s-ha-install repository on master01
  ansible.builtin.git:
    repo: https://xxxx
    dest: /root/
  when: inventory_hostname == (groups['master'] | first)

4、其他说明

Ansible 执行每个任务后会报告三种状态之一:

ok(绿色):已处于期望状态,无需修改

changed(黄色):执行了修改操作

failed(红色):执行失败

在上述配置中很多地方都使用了changed_when: true,表示无论实际有没有发生变化,都显示为 changed(黄色)。原因是:比如command 模块只能执行命令,它无法判断其命令是否真的修改了 某服务的状态(可能本来已经修改了)。默认情况下,command 模块每次都会返回 changed,但 Ansible 有时会提示你确认这一点。显式写上 changed_when: true 就是告诉 Ansible:“我知道这个命令会改变系统状态,每次都报 changed 即可”。

更多推荐