K8S通过Ansible安装集群
K8S通过Ansible安装集群
K8S集群安装可参考https://gitee.com/open-hand/kubeadm-ha.git、https://github.com/easzlab/kubeasz.git
安装高可用集群
git clone https://gitee.com/open-hand/kubeadm-ha.git && cd kubeadm-ha
升级内核,非必需,默认不升级,若需要升级执行如下命令
ansible-playbook -i example/hosts.m-master.ip.ini 00-kernel.yml
hosts.m-master.ip.ini
此文件为配置主机分组及必要变量。需要按需求维护各个分组的主机,例如etcd节点主机、master节点主机、工作节点主机等。ansible将根据配置的各个分组的主机,安装对应分组的Kubernetes组件
; 将所有节点的信息在这里填写
; 第一个字段 为节点内网IP,部署完成后为 kubernetes 节点 nodeName
; 第二个字段 ansible_port 为节点 sshd 监听端口
; 第三个字段 ansible_user 为节点远程登录用户名
; 第四个字段 ansible_ssh_pass 为节点远程登录用户密码
[all]
172.23.17.105 ansible_port=22 ansible_user="root" ansible_ssh_pass="sBzrmwcJwjGLXu&P"; 私有云:
; VIP 负载模式:
; 也就是负载均衡器 + keepalived 模式,比如常用的 haproxy + keepalived。
; 本脚本中负载均衡器有 nginx、openresty、haproxy、envoy 可供选择,设置 lb_mode 即可进行任意切换。
; 设置 lb_kube_apiserver_ip 即表示启用 keepalived,请先与服务器提供部门协商保留一个IP作为 lb_kube_apiserver_ip,
; 一般 lb 节点组中有两个节点就够了,lb节点组中第一个节点为 keepalived 的 master 节点,剩下的都为 backed 节点。
;
; 节点本地负载模式:
; 只启动负载均衡器,不启用 keepalived(即不设置 lb_kube_apiserver_ip),
; 此时 kubelet 链接 apiserver 地址为 127.0.0.1:lb_kube_apiserver_port。
; 使用此模式时请将 lb 节点组置空。
;
; 公有云:
; 不推荐使用 slb 模式,建议直接使用节点本地负载模式。
; 若使用 slb 模式,请先使用节点本地负载模式进行部署,
; 部署成功后再切换至 slb 模式:
; 将 lb_mode 修改为 slb,将 lb_kube_apiserver_ip 设置为购买到的 slb 内网ip,
; 修改 lb_kube_apiserver_port 为 slb 监听端口。
; 再次运行初始化集群脚本即可切换至 slb 模式。
[lb]; 注意etcd集群必须是1,3,5,7...奇数个节点
[etcd]
172.23.17.105[kube-master]
172.23.17.105[kube-worker]
172.23.17.105; 预留组,后续添加master节点使用
[new-master]; 预留组,后续添加worker节点使用
[new-worker]; 预留组,后续添加etcd节点使用
[new-etcd]; 预留组,后续删除worker角色使用
[del-worker]; 预留组,后续删除master角色使用
[del-master]; 预留组,后续删除etcd角色使用
[del-etcd]; 预留组,后续删除节点使用
[del-node];-------------------------------------- 以下为基础信息配置 ------------------------------------;
[all:vars]
; 是否跳过节点物理资源校验,Master节点要求2c2g以上,Worker节点要求2c4g以上
skip_verify_node=false
; kubernetes版本
kube_version="1.21.14"; 容器运行时类型,可选项:containerd,docker;默认 containerd
container_manager="containerd"; 负载均衡器
; 有 nginx、openresty、haproxy、envoy 和 slb 可选,默认使用 nginx
lb_mode="nginx"
; 使用负载均衡后集群 apiserver ip,设置 lb_kube_apiserver_ip 变量,则启用负载均衡器 + keepalived
; lb_kube_apiserver_ip="192.168.56.15"
; 使用负载均衡后集群 apiserver port
lb_kube_apiserver_port="8443"; 网段选择:pod 和 service 的网段不能与服务器网段重叠,
; 若有重叠请配置 `kube_pod_subnet` 和 `kube_service_subnet` 变量设置 pod 和 service 的网段,示例参考:
; 如果服务器网段为:10.0.0.1/8
; pod 网段可设置为:192.168.0.0/18
; service 网段可设置为 192.168.64.0/18
; 如果服务器网段为:172.16.0.1/12
; pod 网段可设置为:10.244.0.0/18
; service 网段可设置为 10.244.64.0/18
; 如果服务器网段为:192.168.0.1/16
; pod 网段可设置为:10.244.0.0/18
; service 网段可设置为 10.244.64.0/18
; 集群pod ip段,默认掩码位 18 即 16384 个ip
kube_pod_subnet="10.244.0.0/18"
; 集群service ip段
kube_service_subnet="10.244.64.0/18"
; 分配给节点的 pod 子网掩码位,默认为 24 即 256 个ip,故使用这些默认值可以纳管 16384/256=64 个节点。
kube_network_node_prefix="24"; node节点最大 pod 数。数量与分配给节点的 pod 子网有关,ip 数应大于 pod 数。
; https://cloud.google.com/kubernetes-engine/docs/how-to/flexible-pod-cidr
kube_max_pods="110"; 集群网络插件,目前支持flannel,calico
network_plugin="calico"; 若服务器磁盘分为系统盘与数据盘,请修改以下路径至数据盘自定义的目录。
; Kubelet 根目录
kubelet_root_dir="/var/lib/kubelet"
; docker容器存储目录
docker_storage_dir="/var/lib/docker"
; containerd容器存储目录
containerd_storage_dir="/var/lib/containerd"
; Etcd 数据根目录
etcd_data_dir="/var/lib/etcd"
- 注意最后设置安装kubelet、docker、etcd、containerd路径配置,需要根据需求规划好目录,分配充足的磁盘空间,否则当磁盘紧张时会自定在节点上打上磁盘压力污点,没有容忍此污点的pod将不能调度到此节点
00-kernel.yml
# 升级所有节点内核
- hosts: allgather_facts: falsetasks:- name: "设置代理服务器环境变量"set_fact:proxy_env:http_proxy: "{{ http_proxy | default ('') }}"HTTP_PROXY: "{{ http_proxy | default ('') }}"https_proxy: "{{ https_proxy | default ('') }}"HTTPS_PROXY: "{{ https_proxy | default ('') }}"no_proxy: "{{ no_proxy | default ('') }}"NO_PROXY: "{{ no_proxy | default ('') }}"no_log: true- hosts: - allroles:- prepare/variables- prepare/base- prepare/kernelenvironment: "{{ proxy_env }}"
-
set_fact:用于在tasks模块中定义变量
-
environment:用于设置环境变量
-
roles:将会执行roles目录下指定目录的tasks
-
variables: 设置变量,可在roles/prepare/variables/defaults/main.yml编辑修改
-
base: 完成变量校验、节点校验、完成基础软件安装。main.yml可以查看task的执行流程
-
kernel:升级系统内核。main.yml可以查看task的执行流程
-
安装高可用Kubernetes集群
执行如下指令
ansible-playbook -i example/hosts.m-master.ip.ini 90-init-cluster.yml
90-init-cluster.yml
# 校验节点软件系统及硬件系统、安装必要基础组件、docker、kubeadm、kubelet、kubectl
- hosts: - allgather_facts: falsetasks:- name: "设置代理服务器环境变量"set_fact:proxy_env:http_proxy: "{{ http_proxy | default ('') }}"HTTP_PROXY: "{{ http_proxy | default ('') }}"https_proxy: "{{ https_proxy | default ('') }}"HTTPS_PROXY: "{{ https_proxy | default ('') }}"no_proxy: "{{ no_proxy | default ('') }}"NO_PROXY: "{{ no_proxy | default ('') }}"no_log: true- hosts: - allroles:- prepare/variables- prepare/base- prepare/container-engine- prepare/kubernetesenvironment: "{{ proxy_env }}"# 进行 apiserver 负载均衡
- hosts: - allroles:- prepare/variables- load-balancer- hosts: - allroles:- prepare/variables- chrony# 安装 etcd,并分发etcd证书到master节点
- hosts: - etcd- new-etcd- kube-master- new-masterroles:- prepare/variables- etcd/certificates- etcd/install# 生成 kubernetes 所需证书证书
- hosts: - kube-master- new-master- kube-worker- new-workerroles:- prepare/variables- kube-certificates# 初始化master节点
- hosts: - kube-master- new-masterroles:- prepare/variables- kube-master# 初始化worker节点
- hosts: - kube-worker- new-workerroles:- prepare/variables- kube-worker# 标记各节点角色
- hosts: - kube-master- kube-worker- new-master- new-workerroles:- prepare/variables- post# 安装基础插件
- hosts: - kube-master[0]roles:- prepare/variables- plugins/network-plugins- plugins/ingress-controller- plugins/metrics-server- plugins/kubernetes-dashboard- plugins/cert-manager
执行roles/prepare/container-engine
tasks目录下main.yml文件说明了执行的流程
- name: 判断 Docker 是否早已安装shell: >systemctl status docker | grep running || echo "not running"register: docker_already_running- name: 判断 containerd 是否早已安装shell: >systemctl status containerd | grep running || echo "not running"register: containerd_already_running- include_tasks: docker/main.ymlwhen: - container_manager == "docker"- '"active" not in docker_already_running.stdout'- '"active" not in containerd_already_running.stdout'- include_tasks: containerd/main.ymlwhen: - container_manager == "containerd"- '"active" not in docker_already_running.stdout'- '"active" not in containerd_already_running.stdout'
-
首先获取docker、containerd服务的运行状态。systemctl status docker的输出作为grep running的输入。如果未运行则显示not running,并注册运行变量
-
根据container_manager及安装状态确定执行docker下任务,还是containerd下任务。when下多个条件列表是and关系。如果目标主机已经安装容器将不会再安装
docker/main.yml
# 系统基础软件环境
- include_tasks: centos.ymlwhen: ansible_distribution in [ 'CentOS','OracleLinux','RedHat' ]- include_tasks: debian.ymlwhen: ansible_distribution in [ 'Ubuntu','Debian' ]# 公共系统参数设置
- include_tasks: common.yml
-
根据不同操作系统执行对应操作系统安装
-
centos.yml
- name: 添加 Docker yum 仓库yum_repository:name: docker-ce-stablefile: docker-cedescription: Docker CE Stable - $basearchbaseurl: "{{ docker_yum_repo }}"enabled: nogpgcheck: nostate: present- name: 安装 Dockeryum:name:- "docker-ce-{{ docker_version }}.ce"state: presentenablerepo: docker-ce-stablewhen: docker_version is version('18.09', '<')- name: 安装 Dockeryum:name:- "docker-ce-{{ docker_version }}"- "docker-ce-cli-{{ docker_version }}"- "containerd.io-{{ containerd_version.split('-')[0] }}"state: presentenablerepo: docker-ce-stablewhen: docker_version is version('18.09', '>=')
-
通过yum_repository模块添加 Docker yum 仓库
-
name:必须参数,指定唯一的仓库ID,state为present或absent时需要设置name参数
-
file:用于设置仓库的配置文件名称,即设置”.repo”配置文件的文件名前缀,在不使用此参数的情况下,默认以 name 参数的仓库ID作为”.repo”配置文件的文件名前缀,同一个”.repo” 配置文件中可以存在多个yum源
-
description:仓库的注释信息
-
baseurl:yum仓库repodata目录的URL,可以是多个,如果设置为多个,需要使用"metalink"和"mirrorlist"参数
-
enabled:是否使用此yum仓库
-
gpgcheck:是否对软件包执行gpg签名检查
-
state:状态,默认的present为安装此yum仓库,absent为删除此yum仓库
-
-
通过yum安装docker
-
如果docker_version小于18.09,则执行docker-ce-{{ docker_version }}.ce安装
- name: 安装 Dockeryum:name:- "docker-ce-{{ docker_version }}.ce"state: presentenablerepo: docker-ce-stablewhen: docker_version is version('18.09', '<')
-
enablerepo:启用源
-
state:状态(absent、installed、latest、present、removed)。present和installed将简单地确保安装所需的包;latest如果指定的程序包不是最新的可用版本,将更新该程序包;absent和removed将删除指定的程序包
-
name:要进行操作的软件包的名字,也可以传递一个url或者一个本地的rpm包的路径
-
-
如果docker_version大于等于18.09,则执行docker-ce-{{ docker_version }}、docker-ce-cli-{{ docker_version }}、containerd.io-{{ containerd_version.split(‘-’)[0] }}
- name: 安装 Dockeryum:name:- "docker-ce-{{ docker_version }}"- "docker-ce-cli-{{ docker_version }}"- "containerd.io-{{ containerd_version.split('-')[0] }}"state: presentenablerepo: docker-ce-stablewhen: docker_version is version('18.09', '>=')
-
-
-
debian.yml
- name: 生成 Docker GPG 公钥copy:src: docker.gpgdest: /tmp/docker.gpgowner: rootmode: 0644- name: 添加 Docker GPG 公钥shell: apt-key add /tmp/docker.gpg- name: 添加 Docker apt 仓库apt_repository:repo: "{{ docker_apt_repo }}"state: present- name: 安装 Dockerapt:name:- "docker-ce={{ docker_version }}~ce~3-0~{{ ansible_distribution | lower }}"state: presentallow_unauthenticated: truewhen: docker_version is version('18.09', '<')- name: 安装 Dockerapt:name:- "docker-ce=5:{{ docker_version }}~3-0~{{ ansible_distribution | lower }}-{{ ansible_distribution_release }}"- "docker-ce-cli=5:{{ docker_version }}~3-0~{{ ansible_distribution | lower }}-{{ ansible_distribution_release }}"- "containerd.io={{ containerd_version }}"state: presentallow_unauthenticated: truewhen: docker_version is version('18.09', '>=')
- 类似于centos.yml,完成docker安装
-
-
common.yml。为安装的docker设置系统参数
-
完成目录创建,如:{{ docker_storage_dir }} 存储目录
-
设置docker开机自启动
-
添加当前用户到Docker用户组
-
containerd/main.yml
类似于上述docker安装流程,根据不同操作系统,使用对应包管理工具执行containerd安装
# 系统基础软件环境
- include_tasks: centos.ymlwhen: ansible_distribution in [ 'CentOS','OracleLinux','RedHat' ]- include_tasks: debian.ymlwhen: ansible_distribution in [ 'Ubuntu','Debian' ]# 公共系统参数设置
- include_tasks: common.yml
执行roles/prepare/kubernetes完成kubernetes安装
根据main.yml文件可知安装kubernetes流程,类似于docker安装流程
# 系统基础软件环境
- include_tasks: centos.ymlwhen: ansible_distribution in [ 'CentOS','OracleLinux','RedHat' ]- include_tasks: debian.ymlwhen: ansible_distribution in [ 'Ubuntu','Debian' ]- name: 配置 kubectl 命令行自动补全shell: kubectl completion bash > /usr/share/bash-completion/completions/kubectl
- 根据操作系统执行kubernetes安装
centos.yml
CentOS、OracleLinux、RedHat执行安装kubernetes流程如下
- name: 添加 Kubernetes yum 仓库yum_repository:name: kubernetesfile: kubernetesdescription: Kubernetesbaseurl: "{{ kubernetes_yum_repo }}"enabled: nogpgcheck: nostate: present- name: 安装 kubeadm kubelet kubectlyum:name: - "kubectl-{{ kube_version }}"- "kubelet-{{ kube_version }}"- "kubeadm-{{ kube_version }}"state: presentenablerepo: kubernetes
-
设置Kubernetes yum仓库
-
安装kubeadm kubelet kubectl
-
用来初始化集群的指令
-
在集群中的每个节点上用来启动 Pod 和容器等
-
用来与集群通信的命令行工具
-
debian.yml
Ubuntu、Debian执行安装kubernetes流程如下
- name: 生成 Kubernetes GPG 公钥copy:src: kubernetes.gpgdest: /tmp/kubernetes.gpgowner: rootmode: 0644- name: 添加 Kubernetes GPG 公钥shell: apt-key add /tmp/kubernetes.gpg- name: 添加 Kubernetes apt 仓库apt_repository:repo: "{{ kubernetes_apt_repo }}"state: present- name: 安装 kubeadm kubelet kubectlapt: name:- "kubectl={{ kube_version }}-00"- "kubelet={{ kube_version }}-00"- "kubeadm={{ kube_version }}-00"state: presentallow_unauthenticated: true
- 类似于centos.yml安装流程,只是改为用apt包管理器安装
apiserver 负载均衡安装
- hosts: - allroles:- prepare/variables- load-balancer
load-balancer
查看main.yml,了解安装流程
- include_tasks: external.ymlwhen: lb_mode == "slb"- include_tasks: internal.ymlwhen: - lb_mode != "slb"- (inventory_hostname in groups['lb']) or (groups['lb']|length == 0 and inventory_hostname in (groups['kube-master'] + groups['kube-worker'] + groups['new-master'] + groups['new-worker']))
-
lb_mode:默认为nginx,支持nginx、openresty、haproxy、envoy、slb
-
对于lb_mode为非slb类型,如果配置lb节点列表,则只在lb节点列表执行internal.yml任务;如果未配置,则在kube-master、kube-worker、new-master、new-worker下节点执行internal.yml任务
####### internal.yml
- name: 判断 Docker 是否早已安装shell: >systemctl status docker | grep running || echo "not running"register: docker_already_running- name: 设置 container_manager_detected 变量set_fact:container_manager_detected: >-{%- if "active" in docker_already_running.stdout -%}docker{%- else -%}containerd{%- endif -%}- name: 创建 load-balancer、kubelet 相关目录file: name: "{{ item }}"state: directorywith_items:- "{{ kubelet_root_dir }}"- /etc/kubernetes/manifests- /etc/kubernetes/plugins/lb-config- /etc/systemd/system/kubelet.service.d- include_tasks: openresty.ymlwhen: lb_mode == "openresty"- include_tasks: nginx.ymlwhen: lb_mode == "nginx"- include_tasks: haproxy.ymlwhen: lb_mode == "haproxy"- include_tasks: envoy.ymlwhen: lb_mode == "envoy"- block:- name: keepalived | 获取网卡名称shell: "ip a|grep '{{ current_host_ip }}/'|awk '{print $NF}'"register: lb_keepalived_interface- name: keepalived | 拉取相关镜像shell: "{{ image_pull_command }} {{ item }}"with_items:- "{{ lb_keepalived_image }}"- name: keepalived | 生成 static pod 文件template: src: keepalived.yaml.j2dest: /etc/kubernetes/manifests/lb-keepalived.yamlowner: rootmode: 0644when: inventory_hostname in groups['lb']- name: 读取 kubelet.conf 文件 stat 信息stat: path: /etc/kubernetes/kubelet.confregister: kubelet_conf_stat- block:- name: 获取 Docker Cgroup Driver 值shell: docker info --format '{{ '{{' }} json .CgroupDriver {{ '}}' }}' | cut -d'"' -f2register: docker_cgroup_driverwhen: container_manager_detected == 'docker'- name: 设置 docker 为容器运行时 kubelet_cgroup_driver_detected 变量set_fact:kubelet_cgroup_driver_detected: >-{{ docker_cgroup_driver.stdout }}when: container_manager_detected == 'docker'- name: 设置 containerd 为容器运行时 kubelet_cgroup_driver_detected 变量set_fact:kubelet_cgroup_driver_detected: >-{%- if containerd_use_systemd_cgroup -%}systemd{%- else -%}cgroupfs{%- endif -%}when: container_manager_detected == 'containerd'- name: 生成临时 kubelet unit 文件template: src: 20-kubelet-override.conf.j2dest: /etc/systemd/system/kubelet.service.d/20-kubelet-override.confowner: rootmode: 0644- name: 重新加载 daemonsystemd:daemon_reload: yes- name: 临时启动 kubelet 以引导 lb 运行service:name: kubeletstate: restartedenabled: yeswhen: - not kubelet_conf_stat.stat.exists- name: 以轮询的方式等待 {{ lb_mode }} 运行完成shell: nc -z -w 3 127.0.0.1 {{ lb_kube_apiserver_port }}register: lb_statusuntil: lb_status.rc == 0retries: 12delay: 5- name: 以轮询的方式等待 keepalived 运行完成shell: nc -z -w 3 {{ kube_apiserver_ip | trim }} {{ lb_kube_apiserver_port }}register: keepalived_statusuntil: keepalived_status.rc == 0retries: 12delay: 5when: inventory_hostname in groups['lb']- block:- name: 移除临时 kubelet unit 文件file: name: /etc/systemd/system/kubelet.service.d/20-kubelet-override.confstate: absent- name: 重新加载 daemonsystemd:daemon_reload: yes- name: 停止临时启动 kubeletservice:name: kubeletstate: stoppedenabled: yeswhen: - not kubelet_conf_stat.stat.exists- inventory_hostname in (groups['kube-master'] + groups['kube-worker'] + groups['new-master'] + groups['new-worker'])
-
创建相关目录:kubelet_root_dir配置kubernetes使用根目录
-
openresty.yml
openresty是一个功能比较全的应用服务器,是基于标准的nginx核心,可以扩展很多第三方模块,是中国人章亦春发起,不管大小厂都会有它的深度用户,web开发人员可以使用lua编程语言,对核心以及各种c模块进行编程,可以利用openresty快速搭建超1万并发高性能web应用系统- name: Openresty lb | 生成 openresty lb 配置文件template:src: openresty/openresty.conf.j2dest: /etc/kubernetes/plugins/lb-config/nginx.confowner: rootmode: 0644register: lb_configuration_result- name: Openresty lb | 拉取相关镜像shell: "{{ image_pull_command }} {{ item }}"with_items:- "{{ lb_openresty_image }}"- "{{ pod_infra_container_image }}"- name: Openresty lb | 生成 openresty lb static pod 文件template:src: openresty/openresty.yaml.j2dest: /etc/kubernetes/manifests/lb-kube-apiserver.yamlowner: rootmode: 0644
-
使用template模块将tasks目录同级templates目录下openresty/openresty.conf.j2输出到节点主机/etc/kubernetes/plugins/lb-config/nginx.conf中
-
如果想了解更多ansible各个模块详细信息可以通过ansible-doc查看,例如 执行 ansible-doc template即可查看template模块详细配置及解释
-
拉取Openresty镜像及pause镜像
-
在Kubernetes中pause容器被当作Pod中所有容器的“父容器”,并为每个业务容器提供以下功能(关于pause大家可以看Kubernetes网络权威指南)
-
在Pod中作为共享Linux namespace(Network、UTS等)的基础
-
启用PID namespace共享,为Pod提供1号进程,并收集Pod内的僵尸进程
-
-
将templates目录下openresty/openresty.yaml.j2输出到/etc/kubernetes/manifests/lb-kube-apiserver.yaml中
-
-
nginx.yml
- name: Nginx lb | 生成 nginx lb 配置文件 template:src: nginx/nginx.conf.j2dest: /etc/kubernetes/plugins/lb-config/nginx.confowner: rootmode: 0644 register: lb_configuration_result- name: Nginx lb | 拉取相关镜像shell: "{{ image_pull_command }} {{ item }}"with_items:- "{{ lb_nginx_image }}"- "{{ pod_infra_container_image }}"- name: Nginx lb | 生成 nginx lb static pod 文件template:src: nginx/nginx.yaml.j2dest: /etc/kubernetes/manifests/lb-kube-apiserver.yamlowner: rootmode: 0644
-
将templates目录下nginx/nginx.conf.j2输出到节点/etc/kubernetes/plugins/lb-config/nginx.conf中
-
拉取nginx镜像和pause镜像
-
将templates目录下nginx/nginx.yaml.j2输出到节点/etc/kubernetes/manifests/lb-kube-apiserver.yaml中
-
-
haproxy.yml
Haproxy 是一个使用C语言编写的自由及开放源代码软件,其提供高可用性、负载均衡,以及基于TCP和HTTP的应用程序代理。安装过程类似上面流程- name: Haproxy | 生成 haproxy 配置文件 template: src: haproxy/haproxy.cfg.j2 dest: /etc/kubernetes/plugins/lb-config/haproxy.cfgowner: rootmode: 0644 register: lb_configuration_result- name: Haproxy | 拉取相关镜像shell: "{{ image_pull_command }} {{ item }}"with_items:- "{{ lb_haproxy_image }}"- "{{ pod_infra_container_image }}"- name: Haproxy | 生成 haproxy static pod 文件template:src: haproxy/haproxy.yaml.j2dest: /etc/kubernetes/manifests/lb-kube-apiserver.yamlowner: rootmode: 0644
-
envoy.yml
- name: Envoy lb | 生成 envoy lb 配置文件 template:src: envoy/envoy.conf.yaml.j2dest: /etc/kubernetes/plugins/lb-config/envoy.yamlowner: rootmode: 0644 register: lb_configuration_result- name: Envoy lb | 拉取相关镜像shell: "{{ image_pull_command }} {{ item }}"with_items:- "{{ lb_envoy_image }}"- "{{ pod_infra_container_image }}"- name: Envoy lb | 生成 envoy lb static pod 文件template:src: envoy/envoy.yaml.j2dest: /etc/kubernetes/manifests/lb-kube-apiserver.yamlowner: rootmode: 0644
-
当前节点在lb分组下主机中时,将会拉取keepalived镜像并设置
chrony
- kubernetes需要集群中的节点时间必须一致,可以使用chrony从网络同步时间
etcd
-
etcd是一致且高可用的键值存储,用于kubernetes所有集群的后台数据库。具体介绍见https://kubernetes.io/zh-cn/docs/tasks/administer-cluster/configure-upgrade-etcd/
-
运行的 etcd 集群个数成员为奇数
-
etcd 是一个 leader-based 分布式系统。确保主节点定期向所有从节点发送心跳,以保持集群稳定
-
确保不发生资源不足。集群的性能和稳定性对网络和磁盘 I/O 非常敏感,任何资源匮乏都会导致心跳超时, 从而导致集群的不稳定。不稳定的情况表明没有选出任何主节点。 在这种情况下,集群不能对其当前状态进行任何更改,这意味着不能调度新的 Pod
-
保持 etcd 集群的稳定对 Kubernetes 集群的稳定性至关重要。 因此,请在专用机器或隔离环境上运行 etcd 集群
-
在生产中运行的 etcd 的最低推荐版本是 3.2.10+
-
使用有限的资源运行 etcd 只适合测试目的。为了在生产中部署,需要先进的硬件配置
-
etcd/certificates
查看main.yml了解整个流程
- name: 在 etcd 节点创建 etcd 相关目录file: name: /etc/kubernetes/pki/etcdstate: directorywhen: inventory_hostname in (groups['etcd'] + groups['new-etcd'])- name: 在 master 节点创建 etcd 证书相关目录file: name: /etc/kubernetes/pki/etcdstate: directorywhen: inventory_hostname in (groups['kube-master'] + groups['new-master'])- block:# 获取密钥状态- include_tasks: certs_stat.yml# 生成证书- include_tasks: generate.ymlwhen: inventory_hostname == groups['etcd'][0]# 分发证书
- include_tasks: distribute.yml
-
etcd、new-etcd分组下主机将创建/etc/kubernetes/pki/etcd目录
-
kube-master、new-master分组下主机将创建etcd正式相关目录
-
在etcd组中第一个主机生成证书密钥
-
certs_stat.yml:查看etcd相关证书状况,并注册成变量
-
generate.yml:判断上一步注册的证书状况。如果证书已经存在则不做处理,如果不存在则创建证书
-
-
distribute.yml
# 分发证书 - name: 获取 etcd 相关证书slurp:src: /etc/kubernetes/pki/etcd/{{ item }}with_items:- ca.crt- ca.key- healthcheck-client.crt- healthcheck-client.key- peer.crt- peer.key- server.crt- server.keyregister: etcd_certsdelegate_to: "{{ groups['etcd'][0] }}"run_once: true- name: 分发 etcd 相关证书到 etcd 节点copy:dest: "{{ item.source }}"content: "{{ item.content | b64decode }}"owner: rootgroup: rootmode: 0644no_log: truewith_items: "{{ etcd_certs.results }}"when:- inventory_hostname != groups['etcd'][0]- inventory_hostname in (groups['etcd'] + groups['new-etcd'])- name: 获取 apiserver etcd client 证书slurp:src: /etc/kubernetes/pki/{{ item }}with_items:- etcd/ca.crt- apiserver-etcd-client.crt- apiserver-etcd-client.keyregister: etcd_client_certsdelegate_to: "{{ groups['etcd'][0] }}"run_once: true- name: 分发 apiserver etcd client 证书到 master 节点copy:dest: "{{ item.source }}"content: "{{ item.content | b64decode }}"owner: rootgroup: rootmode: 0644no_log: truewith_items: "{{ etcd_client_certs.results }}"when:- inventory_hostname != groups['etcd'][0]- inventory_hostname in (groups['kube-master'] + groups['new-master'])
-
slurp模块配置从src指定文件路径拉取文件;delegate_to配置需要执行拉取的文件的主机,这里设置的是etcd分组的第一个组件,也就是创建各种证书的主机,并通过register将拉取的所有文件放入etcd_certs变量中
-
将etcd_certs变量中的个证书复制到除etcd分组第一个节点之外的其它etcd分组节点上
-
从etcd分组的第一个节点获取apiserver etcd client 证书,放入etcd_client_certs变量
-
分发 apiserver etcd client 证书到 master 节点
-
etcd/install
查看main.yml,了解etcd安装流程
- name: 判断 Docker 是否早已安装shell: >systemctl status docker | grep running || echo "not running"register: docker_already_running- name: 设置 container_manager_detected 变量set_fact:container_manager_detected: >-{%- if "active" in docker_already_running.stdout -%}docker{%- else -%}containerd{%- endif -%}- name: 创建 kubelet 相关目录file: name: "{{ item }}"state: directorywith_items:- "{{ kubelet_root_dir }}"- /etc/systemd/system/kubelet.service.d- block:- name: 在 etcd 节点创建 etcd 相关目录file: name: "{{ item }}"state: directoryowner: rootgroup: rootmode: 0700with_items:- "{{ etcd_data_dir }}"- /etc/kubernetes/backup/etcd- /etc/kubernetes/pki/etcd- /etc/kubernetes/manifests- name: 在 etcd 节点拉取相关镜像shell: "{{ image_pull_command }} {{ item }}"with_items:- "{{ etcd_image }}"- "{{ pod_infra_container_image }}"- name: 读取 etcd static pod yaml 文件 stat 信息stat: path: /etc/kubernetes/manifests/etcd-external.yamlregister: etcd_static_pod_stat- name: 生成 etcd static pod 文件template: src: etcd-external.yaml.j2dest: /etc/kubernetes/manifests/etcd-external.yamlowner: rootmode: 0644# when: not etcd_static_pod_stat.stat.existswhen: inventory_hostname in (groups['etcd'] + groups['new-etcd'])- name: 读取 kubelet.conf 文件 stat 信息stat: path: /etc/kubernetes/kubelet.confregister: kubelet_conf_stat- block:- name: 获取 Docker Cgroup Driver 值shell: docker info --format '{{ '{{' }} json .CgroupDriver {{ '}}' }}' | cut -d'"' -f2register: docker_cgroup_driverwhen: container_manager_detected == 'docker'- name: 设置 docker 为容器运行时 kubelet_cgroup_driver_detected 变量set_fact:kubelet_cgroup_driver_detected: >-{{ docker_cgroup_driver.stdout }}when: container_manager_detected == 'docker'- name: 设置 containerd 为容器运行时 kubelet_cgroup_driver_detected 变量set_fact:kubelet_cgroup_driver_detected: >-{%- if containerd_use_systemd_cgroup -%}systemd{%- else -%}cgroupfs{%- endif -%}when: container_manager_detected == 'containerd'- name: 生成临时 kubelet unit 文件template: src: 20-kubelet-override.conf.j2dest: /etc/systemd/system/kubelet.service.d/20-kubelet-override.confowner: rootmode: 0644- name: 重新加载 daemonsystemd:daemon_reload: yes- name: 临时启动 kubelet 以引导 etcd 运行service:name: kubeletstate: restartedenabled: yeswhen: - inventory_hostname in (groups['etcd'] + groups['new-etcd'])- not kubelet_conf_stat.stat.exists- include_tasks: docker.ymlwhen: container_manager_detected == 'docker'- include_tasks: containerd.ymlwhen: container_manager_detected == 'containerd'- block:- name: 移除临时 kubelet unit 文件file: name: /etc/systemd/system/kubelet.service.d/20-kubelet-override.confstate: absent- name: 重新加载 daemonsystemd:daemon_reload: yes- name: 停止临时启动 kubeletservice:name: kubeletstate: stoppedenabled: yeswhen: - inventory_hostname in (groups['kube-master'] + groups['kube-worker'] + groups['new-master'] + groups['new-worker']) - not kubelet_conf_stat.stat.exists- block:- name: 第一个 etcd 节点创建 etcd 备份工具template: src: etcdtools.j2dest: /etc/kubernetes/backup/etcd/etcdtoolsowner: rootmode: 0755- name: 第一个 etcd 节点创建 etcd 定时备份 crontabcron: minute: "0"hour: "{{ etcd_backup_hour }}"name: Backup etcd databasesjob: >/etc/kubernetes/backup/etcd/etcdtools backup- name: 第一个 etcd 节点创建清理 etcd 定时备份文件 crontabcron: minute: "30"hour: "{{ etcd_backup_hour }}"name: Clean etcd databases backup filejob: >/etc/kubernetes/backup/etcd/etcdtools cleanupwhen: inventory_hostname == groups['etcd'][0]
-
判断docker是否安装及判断安装容器管理类型
-
创建kubelet_root_dir目录及kubelet服务文件
-
etcd、new-etcd分组下节点完成etcd_data_dir等目录创建、拉取etcd镜像及pause镜像、将etcd-external.yaml.j2文件内容复制到/etc/kubernetes/manifests/etcd-external.yaml
-
如果/etc/kubernetes/kubelet.conf不存在,则对于容器管理器为docker,设置kubelet_cgroup_driver_detected;对于容器管理器为containerd,设置kubelet_cgroup_driver_detected两者获取方式不同、将20-kubelet-override.conf.j2复制到/etc/systemd/system/kubelet.service.d/20-kubelet-override.conf、重启daemon、重启kubelet
docker.yml
- name: 以轮询的方式等待 etcd 运行完成shell: >docker run --net host -e ETCDCTL_API=3 -v /etc/kubernetes/pki/etcd:/etc/kubernetes/pki/etcd--rm {{ etcd_image }}etcdctl endpoint health--endpoints=https://[127.0.0.1]:2379--cacert=/etc/kubernetes/pki/etcd/ca.crt--key=/etc/kubernetes/pki/etcd/healthcheck-client.key--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crtregister: etcd_statusuntil: etcd_status.rc == 0retries: 12delay: 5when: inventory_hostname in groups['etcd']- name: 校验新添加的 etcd 节点是否已在 etcd 集群中shell: >docker run --net host -e ETCDCTL_API=3 -v /etc/kubernetes/pki/etcd:/etc/kubernetes/pki/etcd--rm {{ etcd_image }}etcdctl member list--endpoints=https://[127.0.0.1]:2379--cacert=/etc/kubernetes/pki/etcd/ca.crt--key=/etc/kubernetes/pki/etcd/healthcheck-client.key--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crtregister: etcd_member_listdelegate_to: "{{ groups['etcd'][0] }}"when: inventory_hostname in groups['new-etcd']- name: 将新添加的 etcd 节点添加到 etcd 集群中shell: >docker run --net host -e ETCDCTL_API=3 -v /etc/kubernetes/pki/etcd:/etc/kubernetes/pki/etcd--rm {{ etcd_image }}etcdctl member add {{ etcd_new_member }}--endpoints=https://[127.0.0.1]:2379--cacert=/etc/kubernetes/pki/etcd/ca.crt--key=/etc/kubernetes/pki/etcd/healthcheck-client.key--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crtregister: etcd_member_adduntil: etcd_member_add.rc == 0retries: 12delay: 5delegate_to: "{{ groups['etcd'][0] }}"when: - inventory_hostname in groups['new-etcd']- inventory_hostname not in etcd_member_list.stdout- name: 以轮询的方式等待 new etcd 运行完成shell: >docker run --net host -e ETCDCTL_API=3 -v /etc/kubernetes/pki/etcd:/etc/kubernetes/pki/etcd--rm {{ etcd_image }}etcdctl endpoint health--endpoints=https://[127.0.0.1]:2379--cacert=/etc/kubernetes/pki/etcd/ca.crt--key=/etc/kubernetes/pki/etcd/healthcheck-client.key--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crtregister: etcd_statusuntil: etcd_status.rc == 0retries: 12delay: 5when: inventory_hostname in groups['new-etcd']
-
轮询的方式等待 etcd 运行完成
-
校验新添加的 etcd 节点是否已在 etcd 集群中
-
将新添加的 etcd 节点添加到 etcd 集群中
-
以轮询的方式等待 new etcd 运行完成
containerd.yml
与docker类似,流程一样只是运行容器指令不同
- name: 以轮询的方式等待 etcd 运行完成shell: >ctr -n k8s.io run --net-host --env ETCDCTL_API=3--mount type=bind,src=/etc/kubernetes/pki/etcd,dst=/etc/kubernetes/pki/etcd,options=rbind:ro--rm {{ etcd_image }} etcd-health-checketcdctl endpoint health--endpoints=https://[127.0.0.1]:2379--cacert=/etc/kubernetes/pki/etcd/ca.crt--key=/etc/kubernetes/pki/etcd/healthcheck-client.key--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crtregister: etcd_statusuntil: etcd_status.rc == 0retries: 12delay: 5when: inventory_hostname in groups['etcd']- name: 校验新添加的 etcd 节点是否已在 etcd 集群中shell: >ctr -n k8s.io run --net-host --env ETCDCTL_API=3--mount type=bind,src=/etc/kubernetes/pki/etcd,dst=/etc/kubernetes/pki/etcd,options=rbind:ro--rm {{ etcd_image }} etcd-list-memberetcdctl member list--endpoints=https://[127.0.0.1]:2379--cacert=/etc/kubernetes/pki/etcd/ca.crt--key=/etc/kubernetes/pki/etcd/healthcheck-client.key--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crtregister: etcd_member_listdelegate_to: "{{ groups['etcd'][0] }}"when: inventory_hostname in groups['new-etcd']- name: 将新添加的 etcd 节点添加到 etcd 集群中shell: >ctr -n k8s.io run --net-host --env ETCDCTL_API=3--mount type=bind,src=/etc/kubernetes/pki/etcd,dst=/etc/kubernetes/pki/etcd,options=rbind:ro--rm {{ etcd_image }} etcd-add-memberetcdctl member add {{ etcd_new_member }}--endpoints=https://[127.0.0.1]:2379--cacert=/etc/kubernetes/pki/etcd/ca.crt--key=/etc/kubernetes/pki/etcd/healthcheck-client.key--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crtregister: etcd_member_adduntil: etcd_member_add.rc == 0retries: 12delay: 5delegate_to: "{{ groups['etcd'][0] }}"when: - inventory_hostname in groups['new-etcd']- inventory_hostname not in etcd_member_list.stdout- name: 以轮询的方式等待 new etcd 运行完成shell: >ctr -n k8s.io run --net-host --env ETCDCTL_API=3--mount type=bind,src=/etc/kubernetes/pki/etcd,dst=/etc/kubernetes/pki/etcd,options=rbind:ro--rm {{ etcd_image }} etcd-health-checketcdctl endpoint health--endpoints=https://[127.0.0.1]:2379--cacert=/etc/kubernetes/pki/etcd/ca.crt--key=/etc/kubernetes/pki/etcd/healthcheck-client.key--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crtregister: etcd_statusuntil: etcd_status.rc == 0retries: 12delay: 5when: inventory_hostname in groups['new-etcd']
生成 kubernetes 所需证书
查看main.yml了解生成证书流程
- name: 创建 kubernetes 证书存放目录file: name: "{{ item }}"state: directorywith_items:- /etc/kubernetes/pki- /var/lib/kubelet/pkiwhen: - inventory_hostname in (groups['kube-master'] + groups['new-master'] + groups['kube-worker'] + groups['new-worker'])- block:# 获取密钥状态- include_tasks: certs_stat.yml# 生成公共证书- include_tasks: common.ymlwhen: inventory_hostname == groups['kube-master'][0]# 分发证书
- include_tasks: distribute.ymlwhen: - inventory_hostname in (groups['kube-master'] + groups['new-master'] + groups['kube-worker'] + groups['new-worker'])- name: 读取 kubelet.conf 文件 stat 信息stat: path: /etc/kubernetes/kubelet.confregister: kubelet_conf_statwhen: - inventory_hostname in (groups['kube-master'] + groups['new-master'] + groups['kube-worker'] + groups['new-worker'])- include_tasks: kubeconfig.ymlwhen: - kubelet_conf_stat.stat.exists- inventory_hostname in (groups['kube-master'] + groups['new-master'] + groups['kube-worker'] + groups['new-worker'])
-
创建 kubernetes 证书存放目录
-
检查kube-master分组第一个节点下,证书的状态,如果证书没有被创建,则创建证书
-
将证书分发到其它节点
-
创建admin.conf 配置文件
-
创建 scheduler.conf 配置文件
-
创建 controller-manager.conf 配置文件
-
创建{{ ansible_env.PWD }}/.kube、{{ansible_env.HOME}}/.kube目录
-
将/etc/kubernetes/admin.conf文件内容输出输出到{{ ansible_env.PWD | default(‘/root’) }}/.kube、{{ansible_env.HOME | default(‘/root’) }}/.kube目录下config文件中
-
设置config文件权限
-
刷新节点kubelet-client证书
-
刷新节点kubelet.conf 配置的证书
初始化master节点
- name: 判断 Docker 是否早已安装shell: >systemctl status docker | grep running || echo "not running"register: docker_already_running- name: 设置 container_manager_detected 变量set_fact:container_manager_detected: >-{%- if "active" in docker_already_running.stdout -%}docker{%- else -%}containerd{%- endif -%}- name: 创建 kubernetes 相关目录file: name: "{{ item }}"state: directorywith_items:- "{{ kubelet_root_dir }}"- "{{ audit_policy_file | dirname }}"- /etc/kubernetes/pki- /etc/kubernetes/config- /etc/kubernetes/manifests- /var/log/kubernetes/audit- /usr/share/bash-completion/completions- name: 读取 kubelet.conf 文件 stat 信息stat: path: /etc/kubernetes/kubelet.confregister: kubelet_conf_stat# 生成 kubeadm 配置
- include_tasks: kubeadm-config.yml- name: 创建 EncryptionConfiguration 配置文件template:src: secrets-encryption.yaml.j2dest: /etc/kubernetes/pki/secrets-encryption.yamlowner: rootgroup: rootmode: 0644- name: 创建 apiserver 审计日志策略配置文件template:src: apiserver-audit-policy.yaml.j2dest: "{{ audit_policy_file }}"owner: rootgroup: rootmode: 0644when: kubernetes_audit|default(false)# 初始化第一个 master 节点
- include_tasks: master-init.ymlwhen: - not kubelet_conf_stat.stat.exists- inventory_hostname == groups['kube-master'][0]# 初始化其他 master 节点
- include_tasks: master-join.ymlwhen: - not kubelet_conf_stat.stat.exists- inventory_hostname != groups['kube-master'][0]- inventory_hostname in (groups['kube-master'] + groups['new-master'])- name: 确认 kubelet 配置是否有修改template:src: kubelet-config.v1beta1.yaml.j2dest: /var/lib/kubelet/config.yamlowner: rootmode: 0644register: configuration_result- name: 启动/重启 kubeletservice:name: kubeletstate: restartedenabled: yeswhen: configuration_result.changed
-
创建kubernetes 相关目录
-
读取kubelet.conf 文件 stat 信息
-
生成 kubeadm 配置
- name: 确认 kubeadm 版本 command: "kubeadm version -o short" register: kubeadm_version_output- name: 设置 kubeadm api version 为 v1beta1set_fact:kubeadmConfig_api_version: v1beta1when:- kubeadm_version_output.stdout is version('v1.13.0', '>=')- kubeadm_version_output.stdout is version('v1.15.0', '<')- name: 设置 kubeadm api version 为 v1beta2set_fact:kubeadmConfig_api_version: v1beta2when:- kubeadm_version_output.stdout is version('v1.15.0', '>=')- name: 获取 Docker Cgroup Driver 值shell: docker info --format '{{ '{{' }} json .CgroupDriver {{ '}}' }}' | cut -d'"' -f2register: docker_cgroup_driverwhen: container_manager_detected == 'docker'- name: 设置 docker 为容器运行时 kubelet_cgroup_driver_detected 变量set_fact:kubelet_cgroup_driver_detected: >-{{ docker_cgroup_driver.stdout }}when: container_manager_detected == 'docker'- name: 设置 containerd 为容器运行时 kubelet_cgroup_driver_detected 变量set_fact:kubelet_cgroup_driver_detected: >-{%- if containerd_use_systemd_cgroup -%}systemd{%- else -%}cgroupfs{%- endif -%}when: container_manager_detected == 'containerd'- name: 创建 kubeadm 的配置文件template:src: >-{% if inventory_hostname == groups['kube-master'][0] -%}kubeadm-controlplane-init.{{ kubeadmConfig_api_version }}.yaml.j2{%- elif inventory_hostname in (groups['kube-master'] + groups['new-master']) -%}kubeadm-controlplane-join.{{ kubeadmConfig_api_version }}.yaml.j2{%- else -%}kubeadm-join.{{ kubeadmConfig_api_version }}.yaml.j2{%- endif %}dest: "/etc/kubernetes/kubeadm-config.yaml"owner: rootmode: 0644
-
通过命令kubeadm version -o short查看kubeadm 版本,根据版本确定kubeadmConfig_api_version版本
-
根据不同容器管理器,设置docker_cgroup_driver变量值
-
根据不同的节点分组,使用不同的模版设置/etc/kubernetes/kubeadm-config.yaml文件
-
使用secrets-encryption.yaml.j2模版内容填充/etc/kubernetes/pki/secrets-encryption.yaml文件,EncryptionConfig类型资源用于指定需要加密的资源,详情查看https://kubernetes.io/zh-cn/docs/tasks/administer-cluster/encrypt-data/
kind: EncryptionConfigapiVersion: v1resources:- resources:- secretsproviders:- aescbc:keys:- name: keysecret: GPG4RC0Vyk7+Mz/niQPttxLIeL4HF96oRCcBRyKNpfM=- identity: {}
-
-
配置审计策略配置文件,默认不配置。审计策略配置文件可以控制K8s中记录各种活动的行为,详情请看https://kubernetes.io/zh-cn/docs/tasks/debug/debug-cluster/audit/
-
初始化第一个master节点
-
初始化其它master节点。与初始化第一个master节点类似,不同之处在于
-
加入一个新的节点需要使用kubeadm join,该指令详细讲解请看https://kubernetes.io/zh-cn/docs/reference/setup-tools/kubeadm/kubeadm-join/
-
不需要再 授权 kubelet 自动轮换服务端证书、配置 Pod 安全策略
-
-
如果kubelet 配置有修改则会执行重启kubelet
使用模版设置/etc/kubernetes/kubeadm-config.yaml文件解析
kubeadm-config.yaml配置文件用于kubeadm init初始化控制平面节点或者kubeadm join将工作节点加入集群。根据执行的不同指令(init或join)对于配置文件中的配置类型有不同的需求
当执行kubeadm init命令时,可以使用下面的配置类型。但InitConfiguration和ClusterConfiguration之间只有一个是必须提供
-
InitConfiguration
-
ClusterConfiguration
-
KubeProxyConfiguration
-
kubeletConfiguration
当执行kubeadm join时,需要提供JoinConfiguration类型配置
####### InitConfiguration字段解析
InitConfiguration 包含一组特定于 “kubeadm init” 的运行时元素。 这里的字段仅用于第一次运行 kubeadm init 命令。 之后,此结构中的字段信息不会再被上传到 kubeadm upgrade 所要使用的 kubeadm-config ConfigMap 中
-
localAPIEndpoint:控制平面节点上要部署的API服务器的端点
-
advertiseAddress:设置 API 服务器要公布的 IP 地址
-
bindPort:设置 API 服务器要绑定到的安全端口。默认值为 6443
-
-
nodeRegistration:包含向集群中注册新控制平面节点相关的字段
-
kubeletExtraArgs:用于向kubelet传递额外参数
-
criSocket:用于读取容器运行时信息,此信息会以注解方式添加到Node API对象上,用于后续用途
-
name:Node API对象的.metadata.name字段值
-
-
bootstrapTokens:在kubeadm init执行时会被用到,其中描述了一组要创建的启动引导令牌,这里的信息不会被上传到 kubeadm 在集群中保存的 ConfigMap 中,部分原因是由于信息 本身比较敏感
-
groups:设定此令牌被用于身份认证时对应的附加用户组
-
token:token 用来在节点与控制面之间建立双向的信任关系。 在向集群中添加节点时使用
-
ttl:定义此令牌的声明周期。默认为 24h
-
usages:描述此令牌的可能使用方式。默认情况下,令牌可用于 建立双向的信任关系;不过这里可以改变默认用途
-
####### ClusterConfiguration字段解析
用于定制集群范围的设置
-
networking:包含集群网络拓扑配置,可以定制pod的子网络或者service的子网络
-
dnsDomain:Kubernetes服务所使用的DNS域名,默认值为"cluster.local"
-
podSubnet:pod所使用的子网
-
serviceSubnet:Kubernetes service所使用的子网,默认值为"10.96.0.0/12"
-
-
etcd:etcd数据库配置,可以定制本地etcd或配置API服务器使用一个外部etcd集群
-
local:提供配置本地etcd实例选项,与external互斥
-
ImageMeta:允许用户为etcd定制要使用的容器
-
imageRepository:设置镜像拉取所用的容器仓库。若未设置,则使用ClusterConfiguration中的imageRepository
-
imageTag:允许用户设置镜像的标签,如果设置此字段,则kubeadm不再在集群升级时自动更改组件的版本
-
-
dataDir:etcd用来存放数据的目录,默认为"/var/lib/etcd"
-
extraArgs:为etcd可执行文件提供的额外参数,用于在静态Pod中运行etcd,映射中的每个键对应命令行上的一个标志参数,只是去掉了前面的参数连字符
-
serverCertSANs:为etcd服务器的签名证书设置额外的主体替代名
-
peerCertSANs:为etcd的对等端签名证书设置额外的主体替代名
-
-
external:描述如何连接到外部的etcd集群,与local互斥
-
endpoints:包含一组etcd成员列表
-
caFile:SSL证书机构(CA)文件,用来加密etcd通信,如果使用TLS连接,此字段为必需字段
-
certFile:SSL证书文件,用于加密etcd通信,使用TLS连接,此字段为必需
-
keyFile:SSL密钥文件,用于加密etcd通信,使用TLS连接,此字段必需
-
-
-
apiServer:包含API服务器的一些额外配置
-
extraArgs:要传递给控制平面组件的一组额外参数,映射中的每个键对应命令行上使用的标准名称
-
extraVolumes:一组额外的主机卷,需要挂载到控制平面组件中
-
certSANs:设置API服务器签署证书所用的额外主题替代名
-
timeoutForControlPla:用来控制等待API服务器开始运行的超时时间
-
-
controllerManager:包含控制器管理器的额外配置
-
extraArgs:要传递给控制平面组件的一组额外参数,映射中的每个键对应命令行上使用的标准名称
-
extraVolumes:一组额外的主机卷,需要挂载到控制平面组件中
-
-
scheduler:包含调度器的额外配置
-
extraArgs:要传递给控制平面组件的一组额外参数,映射中的每个键对应命令行上使用的标准名称
-
extraVolumes:一组额外的主机卷,需要挂载到控制平面组件中
-
####### KubeletConfiguration字段解析
用来更改传递给在集群中部署的kubelet实例的配置
-
address:kubelet提供服务所用的IP地址
-
authentication:设置发送给kubelet服务器的请求是如何进行身份认证的
-
anonymous:包含与匿名身份认证相关的配置信息
- enabled:允许匿名用户向kubelet服务器发送请求。未被其他身份认证方法拒绝的请求都会被当做匿名请求。匿名请求对应的用户名为system:anonymous,对应的用户组名为system:unauthenticated
-
webhook:包含与Webhook持有者令牌认证相关的配置
-
enabled:允许使用tokenreviews.authentication.k8s.io API来提供持有者令牌身份认证
-
cacheTTL:启用对身份认证结果的缓存,设置时间
-
x509:包含与x509客户端证书认证相关的配置
-
-
-
authorization:设置发送给kubelet服务器的请求是如何进行鉴权的
-
webhook:包含与Webhook鉴权相关的配置信息
-
cacheAuthorizedTTL:设置来自Webhook鉴权组件的"authorized"响应的缓存时常
-
cacheUnauthorizedTTL:设置来自Webhook鉴权组件的"unauthorized"响应的缓存时常
-
-
-
cgroupDriver:是kubelet用来操控宿主系统上控制组的驱动程序
-
cgroupsPerQOS:启用基于QoS的控制组层次结构:顶层的控制组用于不同QoS类,所有Burstable和BestEffort pod都会被放置到对应的顶级QoS控制组下
-
clusterDNS:集群DNS服务器的IP地址的列表。如果设置了,kubelet将会配置所有容器使用这里的IP地址而不是宿主系统上的DNS服务器来完成DNS解析
-
clusterDomain:集群DNS域名。如果设置此字段,kubelet会配置所有容器,使之在搜索主机的搜索域的同时也搜索这里的DNS域
-
configMapAndSecretChangeDetectionStrategy:ConfigMap和Secret管理器的运行模式。合法值如下:
-
Get:kubelet直接从API服务器取回必要的对象
-
Cache:kubelet使用TTL缓存来管理来自API服务器的对象
-
Watch:kubelet使用watch操作来观察所关心的对象变更
-
-
containerLogMaxFiles:设置每个容器可以存在的日志文件个数上限
-
containerLogMaxSize:定义容器日志文件被轮转之前可以达到的最大尺寸
-
contentType:API服务器发送请求时使用的内容类型
-
cpuCFSQuota:允许为设置了CPU限制的容器实施CPU CFS配额约束
-
cpuCFSQuotaPeriod:设置CPU CFS配额周期值,cpu.cfs_period_us。此值需要介于1毫秒和1秒之间,包含1毫秒和1秒。此功能要求启用 CustomCPUCFSQuotaPeriod
-
cpuManagerPolicy:要使用的策略名称。需要启用CPUManger
-
cpuManagerReconcilePeriod:CPU管理器的协调周期时长。需要启用CPUManager
-
enableControllerAttachDetach:允许Attach/Detach控制器管理调度到节点的卷的挂接和解除挂接,并且禁止kubelet执行任何attach/detach操作
-
evictionHard:维护硬性驱逐域值映射。如果需要禁用,可显式在对应资源上设置其阈值为0%或100%。更多信息请看https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/node-pressure-eviction/
-
kubeReserved:描述为Kubernetes系统组件预留的资源。支持CPU、内存和根文件系统的本地存储,例如cpu: 100m,memory: 256M
-
evictionPressureTransitionPeriod:kubelet离开驱逐压力状况之前必须等待的时长
-
fileCheckFrequency:设置对配置文件中新数据进行检查的时间间隔,设置为0s将不会检查这些文件,将导致容器状态不可预测或延迟
-
hairpinMode:设置kubelet如何为发夹模式数据包配置容器网桥,设置此字段可以让Service中的端点尝试访问自身Service时将服务请求路由到自身。可选值如下
-
promiscuous-bridge:将容器网桥设置为混杂模式
-
hairpin-veth:在容器的veth接口上设置发夹模式
-
none:什么都不做
-
-
maxOpenFiles:kubelet进程可以打开的文件个数,此值不能为负数
-
maxPods:kubelet上可运行pod个数上限,不能为负数
-
nodeLeaseDuration:设置此参数会同步设置Lease资源对象的leaseDurationSeconds属性,Lease资源被创建在kube-node-lease命名空间下。如果在此参数值设置的周期内节点没有续约,则认为此节点不健康
-
port:kubelet用来提供服务所使用的端口,此端口必需介于1~65535,默认值10250
-
resolvConf:指定域名解析配置文件,用作容器DNS解析配置的基础。如果此值设置为空字符串,则相当于禁用了DNS查询,默认值/etc/resolv.conf
-
staticPodPath:指向要运行的本地pod目录,或者指向某个静态pod文件的路径
####### KubeProxyConfiguration字段解析
用于配置Kubernetes代理服务器,主要字段解析如下
-
bindAddress:代理服务器提供服务所用的IP地址,设置为0.0.0.0则在所有网络接口上提供服务
-
clientConnection:设置代理服务器与API服务器通信时要使用的kubeconfig文件和客户端链接设置
- kubeconfig:指向一个KubeConfig文件路径
-
clusterCIDR:集群中pod所使用的CIDR范围,此地址范围用于对来自集群外请求流量进行桥接。如果未设置,则kube-proxy不会对非集群内部的流量做桥接
-
conntrack:
-
maxPerCore:每个CPU核所跟踪的NAT链接的个数上限
-
tcpEstablishedTimeout:空闲TCP连接的保留时间
-
tcpCloseWaitTimeout:设置空闲的CLOSE_WAIT状态的conntrack条目保留在conntrack列表的时间
-
-
mode:设置使用的代理模式,如果未设置则使用最佳代理模式,liunx默认使用iptables
-
liunx平台支持:iptables、ipvs
-
windows平台支持:kernelspace
-
初始化第一个master节点
- name: 确认 kubelet 已停止运行service:name: kubeletstate: stoppedenabled: yes- name: 获取 master 节点需要拉取的镜像列表shell: >kubeadm config images list --config=/etc/kubernetes/kubeadm-config.yaml | grep -v coredns | grep {{ kube_image_repository }}register: kube_image_list- name: 开始拉取 master 节点相关镜像shell: "{{ image_pull_command }} {{ item }}"with_items: "{{ kube_image_list.stdout_lines }}"- name: 初始化第一个 master 节点shell: kubeadm init --config=/etc/kubernetes/kubeadm-config.yaml --ignore-preflight-errors=ImagePull- include_tasks: "{{ (role_path + '/../kube-certificates/tasks/kubeconfig.yml') | realpath }}"- name: 重新加载 daemonsystemd:daemon_reload: yes- name: 重新启动 kubeletservice:name: kubeletstate: restartedenabled: yes- name: 等待 apiserver 运行uri:url: "https://{{ current_host_ip }}:6443/healthz"validate_certs: noregister: apiserver_resultuntil: apiserver_result.status == 200retries: 60delay: 5- name: 等待 kube-scheduler 运行uri:url: "https://127.0.0.1:10259/healthz"validate_certs: noregister: scheduler_resultuntil: scheduler_result.status == 200retries: 60delay: 5- name: 等待 kube-controller-manager 运行uri:url: "https://127.0.0.1:10257/healthz"validate_certs: noregister: controller_manager_resultuntil: controller_manager_result.status == 200retries: 60delay: 5- name: 创建 kubelet 自动轮换服务端证书配置template:src: kubelet-certificates-renewal.yaml.j2dest: /etc/kubernetes/config/kubelet-certificates-renewal.yamlowner: rootgroup: rootmode: 0644- name: 授权 kubelet 自动轮换服务端证书shell: kubectl apply -f /etc/kubernetes/config/kubelet-certificates-renewal.yaml- block:- name: 创建 Pod 安全策略配置文件template:src: pod-security-policy.yaml.j2dest: /etc/kubernetes/config/pod-security-policy.yamlowner: rootgroup: rootmode: 0644- name: 配置 Pod 安全策略shell: kubectl apply -f /etc/kubernetes/config/pod-security-policy.yamlwhen: '"PodSecurityPolicy" in kube_apiserver_enable_admission_plugins'
-
获取需要拉取的镜像列表
-
拉取镜像列表镜像
-
使用init命令初始化第一个master节点,忽略拉取镜像错误。指令详情请看https://kubernetes.io/zh-cn/docs/reference/setup-tools/kubeadm/kubeadm-init/
-
设置pod安全策略。详情请看https://kubernetes.io/zh-cn/docs/concepts/security/pod-security-policy/
初始化worker节点
- name: 判断 Docker 是否早已安装shell: >systemctl status docker | grep running || echo "not running"register: docker_already_running- name: 设置 container_manager_detected 变量set_fact:container_manager_detected: >-{%- if "active" in docker_already_running.stdout -%}docker{%- else -%}containerd{%- endif -%}- name: 创建 kubernetes 相关目录file: name: "{{ item }}"state: directorywith_items:- "{{ kubelet_root_dir }}"- /etc/kubernetes- /usr/share/bash-completion/completions- name: 读取 kubelet.conf 文件 stat 信息stat:path: /etc/kubernetes/kubelet.confregister: kubelet_conf_stat- include_tasks: "{{ (role_path + '/../kube-master/tasks/kubeadm-config.yml') | realpath }}"when: - inventory_hostname in (groups['kube-worker'] + groups['new-worker'])- inventory_hostname not in (groups['kube-master'] + groups['new-master'])- block:- name: 确认 kubelet 已停止运行service:name: kubeletstate: stoppedenabled: yes- name: Worker 节点加入集群shell: >kubeadm join --config /etc/kubernetes/kubeadm-config.yaml --ignore-preflight-errors=DirAvailable--etc-kubernetes-manifests,FileAvailable--etc-kubernetes-pki-ca.crtwhen: - inventory_hostname in (groups['kube-worker'] + groups['new-worker'])- inventory_hostname not in (groups['kube-master'] + groups['new-master'])- not kubelet_conf_stat.stat.exists- block:- name: 确认 kubelet 配置是否有修改template:src: kubelet-config.v1beta1.yaml.j2dest: /var/lib/kubelet/config.yamlowner: rootmode: 0644register: configuration_result- name: 启动/重启 kubeletservice:name: kubeletstate: restartedenabled: yeswhen: configuration_result.changedwhen: - inventory_hostname in (groups['kube-worker'] + groups['new-worker'])- inventory_hostname not in (groups['kube-master'] + groups['new-master'])# - name: 取消在 worker 组的 master 节点 taint,使 master 节点可以调度
# shell: >
# kubectl taint nodes {{inventory_hostname}} node-role.kubernetes.io/control-plane='':NoSchedule --overwrite &&
# kubectl taint nodes {{inventory_hostname}} node-role.kubernetes.io/control-plane-
# delegate_to: "{{ groups['kube-master'][0] }}"
# ignore_errors: yes
# when:
# - inventory_hostname in (groups['kube-worker'] + groups['new-worker'])
# - inventory_hostname in (groups['kube-master'] + groups['new-master'])
-
使用kubeadm join添加工作节点
-
如果/var/lib/kubelet/config.yaml配置有变更则重启kubelet
标记节点角色
- name: 获取当前集群节点信息shell: kubectl get node -o namedelegate_to: "{{ groups['kube-master'][0] }}"ignore_errors: truerun_once: trueregister: kubectl_get_node_output- block:- name: 取消节点原有角色标签shell: >kubectl label node {{ inventory_hostname }} node-role.kubernetes.io/control-plane- &&kubectl label node {{ inventory_hostname }} node-role.kubernetes.io/master- &&kubectl label node {{ inventory_hostname }} node-role.kubernetes.io/worker- &&kubectl label node {{ inventory_hostname }} node-role.kubernetes.io/lb- &&kubectl label node {{ inventory_hostname }} node-role.kubernetes.io/etcd-- name: 设置控制面节点 roleshell: >kubectl label node {{ inventory_hostname }} node-role.kubernetes.io/control-plane='' --overwrite &&kubectl label node {{ inventory_hostname }} node-role.kubernetes.io/master='' --overwrite when: inventory_hostname in (groups['kube-master'] + groups['new-master']) - name: 设置 worker 节点 roleshell: >kubectl label node {{ inventory_hostname }} node-role.kubernetes.io/worker='' --overwritewhen: inventory_hostname in (groups['kube-worker'] + groups['new-worker']) - name: 设置 load balancer 节点 roleshell: >kubectl label node {{ inventory_hostname }} node-role.kubernetes.io/lb='' --overwritewhen: inventory_hostname in groups['lb']- name: 设置 etcd 节点 roleshell: >kubectl label node {{ inventory_hostname }} node-role.kubernetes.io/etcd='' --overwritewhen: inventory_hostname in (groups['etcd'] + groups['new-etcd'])ignore_errors: truedelegate_to: "{{ groups['kube-master'][0] }}"when: inventory_hostname in kubectl_get_node_output.stdout
-
delegate_to委派在master节点组中第一个节点执行查询K8s集群节点名列表,并注册在kubectl_get_node_output变量中,供后面使用
-
如果节点在K8s集群列表中,则设置节点角色,设置节点角色操作也是在master节点组中第一个节点执行
-
通过覆盖角色标签的方式取消节点的标签
-
如果节点在master分组下,则设置控制平面角色和master节点角色
-
如果节点在worker分组下,则设置工作节点角色
-
如果节点是lb节点,则设置lb角色
-
如果节点在etcd组下,则设置etcd角色
-
安装插件
在第一个主节点上执行安装各种插件
-
安装网络插件
-
安装ingress-controller,用于通过ingress类型资源暴露service
-
安装系统指标插件,收集各组件指标
-
安装K8s仪表盘
-
安装证书管理插件
相关文章:

K8S通过Ansible安装集群
K8S通过Ansible安装集群 K8S集群安装可参考https://gitee.com/open-hand/kubeadm-ha.git、https://github.com/easzlab/kubeasz.git 安装高可用集群 git clone https://gitee.com/open-hand/kubeadm-ha.git && cd kubeadm-ha升级内核,非必需,默认不升级&…...

ChatGPT辩证观点:“人才不是一个企业的核心竞争力,对人才的管理能力才是一个企业的核心竞争力”
一、问: “人才不是一个企业的核心竞争力,对人才的管理能力才是一个企业的核心竞争力”这句话的理解和误解,这句话有哪个中心论点转移和变化 二、ChatGPT答: 这句话的理解和误解: 理解:这句话的意思是说…...

windows11 永久关闭windows defender的方法
1、按键盘上的windows按键,再点【设置】选项。 2、点击左侧菜单的【隐私和安全性】,再点击列表的【Windows安全中心】选项。 3、点击界面的【病毒和威胁保护】设置项。 4、病毒保护的全部关闭 5、别人的图(正常是都开着的) 6、终极…...

继承的基本知识
概念 假设基于A类,创建了B类,那么称A为B的父类,B为A的子类 子类会继承父类的成员变量及成员函数,但是不能继承构造、析构、运算符重载 假设又基于B创建了C,那么称B为C的直接基类,A为C的间接基类 继承按…...

【Frida-实战】EA游戏平台的文件监控(PsExec.exe提权)
▒ 目录 ▒ 🛫 问题描述环境 1️⃣ 代码编写开源代码搜索自己撸代码procexp确定句柄对应的文件名并过滤 2️⃣ PsExec.exe提权定位找不到EABackgroundService.exe的问题 PsExec.exe提权PsExec.exe原理 🛬 结论📖 参考资料 🛫 问题…...

可视化和回归分析星巴克咖啡在中国的定价建议
可视化和回归分析星巴克咖啡在中国的定价建议。星巴克的拿铁大杯Tall 在各国的价格。 Claude AI | 代码自动生成的数据可视化代码 选择Claude AI 而非 ChatGPT的理由是前者更懂中文!具体可以参见我前面的两篇文章对比两者的中英文翻译的表现及使用安装等难易程度…...

热门影片怎么买票比较便宜,低价买电影票的方法,纯攻略!
有时候真的有被自己蠢到!看电影看了这么多年,竟然不知道电影票价格才9.9元、19.9元就能买到。之前我看电影动不动就是几十上百块,感觉好亏啊。 其实,我也不敢相信的,通过这些平台,同时在节假日甚至春节档期…...

Python通过SWIG调用C++时出现的ImportError问题解析
摘要 win10系统,编译器为mingw,按照教程封装C的一个类并用python调用,一步步进行直到最后一步运行python代码时,在python代码中import example时报错ImportError: DLL load failed while importing _example: The specified modul…...

3ds Max云渲染有多快,3ds Max云渲染怎么用?
本地渲染效果图和动画3D项目是一个非常耗时的过程,当在场景中使用未优化的几何体或在最终渲染中使用大量多边形模型时,诸如此类的变量最终会增加渲染项目所需的时间和处理器能力。随着提供的渲染服务的云渲染平台出现,越来越多动画师、艺术家…...

Java之线程安全
目录 一.上节回顾 1.Thread类常见的属性 2.Thread类中的方法 二.多线程带来的风险 1.观察线程不安全的现象 三.造成线程不安全现象的原因 1.多个线程修改了同一个共享变量 2.线程是抢占式执行的 3.原子性 4.内存可见性 5.有序性 四.解决线程不安全问题 ---synchroni…...

我有一个方法判断你有没有编程天赋
我有一个方法判断你有没有编程天赋 一 前言 基于知识的诅咒的原理 做一个敲击者很难。问题在于敲击者已拥有的知识(歌曲题目)让 他们想象不到缺乏这种知识会是什么情形。当他们敲击的时候,他 们不能想象听众听到的是那些独立的敲击声而不是…...

python 生成chart 并以附件形式发送邮件
import requests import json import pandas as pd import numpy as np import matplotlib.pyplot as plt data np.random.randn(5, 3)#生成chart def generate_line_chart(data):df pd.DataFrame(np.abs(data),index[Mon, Tue, Wen, Thir, Fri],columns[A, B, C])df.plot()…...

leetcode-035-搜索插入位置
题目及测试 package pid035; /*35. 搜索插入位置 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。请必须使用时间复杂度为 O(log n) 的算法。示例 1:输入: nums …...

读书笔记--数据治理之法
继续延续上一篇文章,对数据治理之法进行学习。数据治理之法是战术层面的方法,是一套涵盖8项举措的数据治理实施方法论,包括梳理现状与确定目标、能力成熟度评估、治理路线图规划、保障体系建设、技术体系建设、治理策略执行与监控、绩效考核与…...

送了老弟一台 Linux 服务器,它又懵了!
大家好,我是鱼皮。 前两天我学编程的老弟小阿巴过生日,我问他想要什么礼物。 本来以为他会要什么游戏机、Q 币卡、鼠标键盘啥的,结果小阿巴说:我想要一台服务器。 鱼皮听了,不禁称赞道:真是个学编程的好苗…...

CentOS 7(2009) 升级 GCC 版本
1. 前言 CentOS 7 默认安装的 gcc 版本为 4.8,但是很多时候都会需要用到更高版本的 gcc 来编译源码,那么本文将会介绍如何在线升级 CentOS 的 gcc 版本。 2. 升级 GCC (1). 安装 centos-release-scl; [imaginemiraclecentos7 ~]$ sudo yum…...

java非静态代码块和静态代码块介绍
代码块 SE.10.0…02.28 非静态普通代码块:定义在方法内部的代码块,不用任何关键字修饰,又名构造代码块、实例代码块 静态代码块:用static修饰的代码块 非静态代码块 public class Test {public static void main(String[] args…...

Golang中接口类型详解与最佳实践(二)
之前的文章《Golang中的interface(接口)详解与最佳实践》详细介绍了接口类型的定义、使用方法和最佳实践。接口类型使得编写可扩展、可维护和可复用的高质量代码变得更加容易。 如何判断是否实现了某个接口? 还是使用之前文章的例子,例如声明了如下一个…...

ChatGPT 探讨内存屏障的意内存
一、与 ChatGPT 探讨内存屏障的意内存 轻松的氛围,跟 ChatGPT 从内存屏障问题一直扯到CAP原理 我: 2023/4/14 17:48:09 那我可以理解为{ shared_var 1; asm volatile ("sfence" ::: "memory"); asm volatile ("lfence" …...

P1039 [NOIP2003 提高组] 侦探推理
此题难度为:提高/省选- 作者为:CCF_NOI 题目描述 明明同学最近迷上了侦探漫画《柯南》并沉醉于推理游戏之中,于是他召集了一群同学玩推理游戏。游戏的内容是这样的,明明的同学们先商量好由其中的一个人充当罪犯(在明…...

模拟电路学习笔记 - 概念与结论
真空二极管,电子管ENIAC发源地,基础方法二极管双极管三极管场向管学习特性,最终运放运方的目的是运用,射频,计算…放大电路大功率元器件和微元器件学习他们的特性分粒 集成设计的角度,不要仅仅分析设计的前…...

Linux驱动开发:I2C子系统
目录 1、I2C简介 1.1 两根线 1.2 信号 1.3 写时序 1.4 读时序 1.5 I2C速率 1.6 I2C驱动框架简介 2、I2C设备驱动 2.1 I2C相关API 2.1.1 i2c_driver 2.1.2 注册:i2c_add_driver 2.1.3 注销:i2c_del_driver 2.1.4 module_i2c_driverÿ…...

[C++] 动态内存与智能指针
众所周知,C五大内存区:全局数据区(静态区)、代码区、栈区、堆区、常量区。 全局数据区(静态区):存放全局变量,静态数据和常量; 代码区:存放所有类成员函数和非成员函数代码,函数体的二进制代码。…...

多态的原理
有了虚函数,会在类的对象增加一个指针,该指针就是虚函数表指针_vfptr;虚表本质就是函数指针数组,虚表里面存放着该对象的虚函数的地址; 派生类继承有虚函数基类的对象模型 子类继承父类的虚表指针时,是对父类的虚表指针进行了拷…...

RK3588平台开发系列讲解(内存篇)Linux 伙伴系统数据结构
平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、 页二、区三、内存节点沉淀、分享、成长,让自己和他人都能有所收获!😄 📢Linux 系统中,用来管理物理内存页面的伙伴系统,以及负责分配比页更小的内存对象的 SLAB 分配器了。 本篇将介绍伙伴系统相关数据结…...

Windows(MFC/C++)上进程间通讯的几种简单又实用的方法
前段时间,做了一个项目,涉及数据传输。项目实现方式有很多种,但不同的实现方式,对数据的传输方法不同,且各有优缺点。 下文就不同情况来如何选择数据传输(通讯)方式。 先说说需求,模块A获取测试数据&#…...

嘉兴桐乡会计考证培训-备考中级职称有必要报班吗?
备考中级会计职称有必要报班吗?其实,备考报班不能说是必需的,但听课学习确实是节省时间的一种方式,根据同学们的反馈,自学所花费的时间远远多于跟着老师学。上元教育就整理了一些学员报班之前走过的弯路 报班之前 在2…...

java元注解和自定义注解的区别
Java的元注解和自定义注解是两个不同的概念。 元注解是Java内置的一组用于修饰其他注解的注解,包括Retention、Target、Inherited和Documented。它们可以控制被修饰的注解的保留策略、目标范围、是否继承等属性,并且可以在编写自定义注解时使用。 Retent…...

技术到底是什么
背景 我发了朋友圈:做了个奇怪的梦,梦见被离职了,理由竟然是:你技术太菜了 我补充评论:我还没想明白怎么回事,就醒了。有点遗憾的是:想再努力反驳两句,结果没机会了… 很多人评论…...

什么CRM客户管理系统最好?
产业互联网背景下,企业数字化转型日渐深化。毋庸置疑,客户是企业的命脉,企业发展的关键便是以客户为中心,为客户创造价值,并不断实现企业的可持续性增长,而这也是每个企业永不落幕的主题。 一套优秀的CRM客…...