# k8s基础入门

# 背景

Kubernetes是一个轻便的和可扩展的开源平台,用于管理容器化应用和服务。通过Kubernetes能够进行应用的自动化部署和扩缩容。在Kubernetes中,会将组成应用的容器组合成一个逻辑单元以更易管理和发现。它可以帮助:

  • 从IT基础设施主机化向容器化转换
  • 从人肉式运维工作模式向自动化运维模式转换
  • 从自动化运维体系向全体系智能化运维模式转换

这里涉及到主机->虚拟机->容器过渡,为了降低虚拟机造成的物理主机资源浪费,提高物理主机的资源利用率,并能够提供像虚拟机一样良好的应用程序隔离运行环境,人们把这种轻量级的虚拟机,称为"容器"。

container

# 容器管理工具

容器管理工具类似于虚拟机管理工具,主要用于容器的创建、启动、关闭、删除等。常用的管理工具有:

  • docker公司的 docker
  • 国内阿里公司的 Pouch
  • LXC、LXD、RKT等等

# 容器编排部署工具

容器管理工具可以完成容器的基础管理,但是容器的应用并不是只能进行简单应用部署的,可以使用容器完成企业中更加复杂的应用部署,当需要对多应用的系统进行部署时,就需要更加复杂的工具来完成对容器运行应用的编排,这就是我们所说的容器编排部署工具。常见的容器编排部署工具有:

  • docker三剑客:
    • docker machine:在远程电脑上安装Docker引擎。
    • docker compose:定义和运行多容器应用。
    • docker swarm:原生集群工具。
  • mesos + marathon:
    • mesos的主要作用是在分布式计算过程中,对计算机资源进行管理和分配。
    • Marathon实现了服务发现和负载平衡、为部署提供REST API服务、授权和SSL、配置约束等功能。Marathon支持通过Shell命令和Docker部署应用。提供Web界面、支持cpu/mem、实例数等参数设置,支持单应用的Scale,但不支持复杂的集群定义。
    • 如果将Mesos类比为操作系统的内核,负责资源调度。则Marathon可以类比为服务管理系统,比如init,systemd或upstart等系统,用来管理应用的状态信息。Marathon将应用程序部署为长时间运行的Mesos任务。
  • kubernetes:主要用于管理云平台中多个主机上的容器化的应用,Kubernetes的目标是让部署容器化的应用简单并且高效,提供了应用部署,规划,更新,维护的一种机制。

# 起源

k8s起源于谷歌的Borg系统开源,归属于CNCF (opens new window),它是一个开源软件基金会,致力于云计算的普遍性和持续性。OpenShift和Rancher的平台核心是k8s,它的主要贡献者为Google,Redhat,Microsoft,IBM,Intel。它的版本历史:

  • 2014年9月:第一个正式版本。
  • 2015年7月:1.0版本。
  • 2017年10月:docker支持kubernetes。

早期Kubernetes的Node数也就100台,pod管理支持就1000个,到2021年OpenAI将Node数扩展到了7500个,Pod数超过150000个。

# K8s功能

  • 自动装箱:基于容器对应用运行环境的资源配置要求自动部署应用容器
  • 自我修复(自愈能力):
    • 当容器失败时,会对容器进行重启。
    • 当所部署的Node节点有问题时,会对容器进行重新部署和重新调度。
    • 当容器未通过监控检查时,会关闭此容器。
    • 直到容器正常运行时,才会对外提供服务。
  • 水平扩展:通过简单的命令、用户UI界面或基于CPU等资源使用情况,对应用容器进行规模扩大或规模剪裁。
  • 服务发现:用户不需要使用额外的服务发现机制,就能够基于Kubernetes自身能力实现服务发现和负载均衡。
  • 滚动更新:可以根据应用的变化,对应用容器运行的应用,进行一次性或批量式更新。
  • 版本回退:可以根据应用部署情况,对应用容器运行的应用,进行历史版本即时回退。
  • 密钥和配置管理:在不需要重新构建镜像的情况下,可以部署和更新密钥和应用配置,类似热部署。
  • 存储编排:
    • 自动实现存储系统挂载及应用,特别对有状态应用实现数据持久化非常重要。
    • 存储系统可以来自于本地目录、网络存储(NFS、Gluster、Ceph、Cinder等)、公共云存储服务等。

# K8s架构

目前主要有两类集群架构:

  • 无中心节点架构:
    • GlusterFS
  • 有中心节点架构:
    • HDFS
    • K8S

那么K8S的集群架构节点角色功能:

  • MasterNode
    • k8s集群控制节点,对集群进行调度管理,接受集群外用户去集群操作请求;
    • Master Node由API Server、Scheduler、Cluster State Store(ETCD数据库)和Controller Manger Server所组成;
  • WorkerNode
    • 集群工作节点,运行用户业务应用容器;
    • Worker Node包含kubelet、kube proxy和Container Runtime;

他们架构图如下:

jiagou1

jiagou2

下面介绍下他们的组件:

  • kubectl:管理K8s的命令行工具,可以操作K8s中的资源对象。
  • etcd:是一个高可用的键值数据库,存储K8s的资源状态信息和网络信息的,etcd中的数据变更是通过api server进行的。
  • apiserver: 提供K8s api,是整个系统的对外接口,提供资源操作的唯一入口,供客户端和其它组件调用,提供了K8s各类资源对象(pod,deployment,Service等)的增删改查,是整个系统的数据总线和数据中心,并提供认证、授权、访问控制、API注册和发现等机制,并将操作对象持久化到etcd中。相当于“营业厅”。
  • scheduler:负责K8s集群中pod的调度的,scheduler通过与apiserver交互监听到创建Pod副本的信息后,它会检索所有符合该Pod要求的工作节点列表,开始执行Pod调度逻辑。调度成功后将Pod绑定到目标节点上,相当于“调度室”。
  • controller-manager:与apiserver交互,实时监控和维护K8s集群的控制器的健康情况,对有故障的进行处理和恢复,相当于“大总管”。
  • kubelet:每个Node节点上的kubelet定期就会调用API Server的REST接口报告自身状态,API Server接收这些信息后,将节点状态信息更新到etcd中。kubelet也通过API Server监听Pod信息,从而对Node机器上的POD进行管理,如创建、删除、更新Pod。
  • kube-proxy:提供网络代理和负载均衡,是实现service的通信与负载均衡机制的重要组件,kube-proxy负责为Pod创建代理服务,从apiserver获取所有service信息,并根据service信息创建代理服务,实现service到Pod的请求路由和转发,从而实现K8s层级的虚拟转发网络,将到service的请求转发到后端的pod上。

# 部署安装

主要有两种方式部署。

# 二进制源码包部署

  • 获取源码包
  • 部署在各节点中
  • 启动服务
    • Master
      • api-server
      • etcd
      • scheduler
      • controller manager
    • Worker
      • kubelet
      • kube-proxy
      • docker
  • 生成证书
    • http
    • https

# 使用kubeadmin部署

参考资料:

主要安装部署如下:

  • 安装软件 kubelet kubeadm kubectl
  • 初始化集群
  • 添加node到集群中
  • 证书自动生成
  • 集群管理系统是以容器方式存在,容器运行在master
  • 容器镜像是谷歌提供
    • 阿里云下载容器镜像,需要重新打标记
    • 谷歌下载

前置条件:

  • 需要三台至少2CPU 2G内存的linux服务器。他们之间网络互通。
  • 每台服务器要有独立的主机名,MAC地址和product_uuid。
  • 每台服务器要禁用Swap,firewalld,以及SELINUX。
  • 三台主机之间要时间同步。
  1. 修改主机名为唯一的:

    • 查看主机名:

      hostname

    • 设置主机名:

      hostnamectl set-hostname master1
      hostnamectl set-hostname worker1
      hostnamectl set-hostname worker2

    • 如果可以的话设置ip地址。

    • 设置主机机名称解析。修改/etc/hosts文件:

      192.168.20.20 master1
      192.168.20.21 worker1
      192.168.20.22 worker2
      
      1
      2
      3
  2. 查看MAC是否是唯一的:

    ip link
    ifconfig -a

  3. 查看product_uuid是否唯一,正常情况下这个均为唯一:

    cat /sys/class/dmi/id/product_uuid

    如果不唯一,则需要修改,下面是Hyper-V虚拟平台的修改方案:

    $VMName = '<NameOfVirtualMachine>'
    $MSVM =  Get-WmiObject -Namespace root\virtualization\v2 -ClassName Msvm_ComputerSystem -Filter "ElementName = '$VMName'"
    
    # get current settings object
    $MSVMSystemSettings = $null
    foreach($SettingsObject in $MSVM.GetRelated('Msvm_VirtualSystemSettingData'))
    {
        $MSVMSystemSettings = [System.Management.ManagementObject]$SettingsObject
    }
    
    # assign a new id
    $MSVMSystemSettings['BIOSGUID'] = "{$(([System.Guid]::NewGuid()).Guid.ToUpper())}"
    
    $VMMS = gwmi -Namespace root\virtualization\v2 -Class msvm_virtualsystemmanagementservice
    # prepare and assign parameters
    $ModifySystemSettingsParameters = $VMMS.GetMethodParameters('ModifySystemSettings')
    $ModifySystemSettingsParameters['SystemSettings'] = $MSVMSystemSettings.GetText([System.Management.TextFormat]::CimDtd20)
    # invoke modification
    $VMMS.InvokeMethod('ModifySystemSettings', $ModifySystemSettingsParameters, $null)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  4. 关闭firewalld。

    systemctl stop firewalld
    systemctl disable firewalld
    firewall-cmd --state
    
    1
    2
    3

    ubuntu下是ufw:

    ufw disable
    
    1
  5. 关闭SELinux

    sed -ri 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config

  6. 关闭Swap分区

    systemctl --type swap systemctl mask "dev-sda3.swap"

    或者是在/etc/fstab中关掉。

    临时关闭:

    swapoff -a

    验证是否已经关掉:

    free -m
    lsblk -o +PARTTYPE

  7. 时间同步

    apt install ntpdate
    crontab -e
    0 */1 * * * ntpdate time1.aliyun.com
    crontab -l

  8. 配置网桥过滤,以及转发。将 Linux 系统作为路由或者 VPN 服务就必须要开启 IP 转发功能。当 linux 主机有多个网卡时一个网卡收到的信息是否能够传递给其他的网卡。如果设置成 1 的话可以进行数据包转发,可以实现 VxLAN 等功能。不开启会导致 docker 部署应用无法访问。

    vi /etc/sysctl.d/k8s.conf

    net.bridge.bridge-nf-call-ip6tables = 1
    net.bridge.bridge-nf-call-iptables = 1
    # 开启主机向容器的包转发
    net.ipv4.ip_forward = 1
    vm.swappiness = 0
    
    1
    2
    3
    4
    5

    modprobe br_netfilter
    modprobe overlay lsmod | grep br_netfilter
    sysctl -p /etc/sysctl.d/k8s.conf

    或者使用下面方式配置永久生效:

    cat <<EOF | tee /etc/modules-load.d/k8s.conf
    br_netfilter
    EOF
    
    1
    2
    3
  9. 开启ipvs:

    apt install ipset ipvsadm
    mkdir -p /etc/sysconfig/modules/
    
    1
    2

    添加需要加载的模块

    cat > /etc/sysconfig/modules/ipvs.modules <<EOF
    #!/bin/bash
    modprobe -- ip_vs
    modprobe -- ip_vs_rr
    modprobe -- ip_vs_wrr
    modprobe -- ip_vs_sh
    modprobe -- nf_conntrack
    EOF
    
    1
    2
    3
    4
    5
    6
    7
    8

    授权运行:

    chmod 755 /etc/sysconfig/modules/ipvs.modules && bash /etc/sysconfig/modules/ipvs.modules
    
    1

    检查是否开启:

    lsmod | grep -e ip_vs -e nf_conntrack_ipv4
    
    1
  10. 安装docker,并配置cgroupdriver(/etc/docker/daemon.json)。

    {
      "exec-opts":["native.cgroupdriver=systemd"]
    }
    
    1
    2
    3

    使用如下命令查看:

    # 查看所有可安装docker的版本:
    apt-cache madison docker.io
    # 查看docker相关信息
    docker info
    
    1
    2
    3
    4
  11. 修改k8s的cgroup配置。在1.22以后如果用户不设置,kubeadmin默认设置为systemd。这一步应该不用设置。

    #k8s默认配置文件为
    /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
    
    vi /etc/sysconfig/kubelet  
    KUBELET_EXTRA_ARGS="--cgroup-driver=systemd"  
    
    # 或者debian如下:
    cat > /etc/default/kubelet <<EOF
    KUBELET_EXTRA_ARGS=--cgroup-driver=systemd --fail-swap-on=false
    EOF
    
    # 可以用下面的命令查看是否有上面设置的参数。 
    
    systemctl status -l kubelet
    systemctl daemon-reload
    systemctl enable kubelet
    systemctl restart kubelet
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

# kubeadm安装

首先要知道k8s的三大组件:

  1. kubeadm:初始化集群、管理集群。
  2. kubelet:用于接收api server指令,对pod生命周期进行管理。
  3. kubectl:集群命令行管理工具。

在这我们主要使用kubeadm。它在k8s生态中的位置如下:

kubeadmin

常用命令如下:

# 启动一个 Kubernetes 主节点
kubeadm init 
# 启动一个 Kubernetes 工作节点并且将其加入到集群
kubeadm join 
# 更新一个 Kubernetes 集群到新版本
kubeadm upgrade 
# 如果你使用 kubeadm v1.7.x 或者更低版本,你需要对你的集群做一些配置以便使用 kubeadm upgrade 命令
kubeadm config 
# 查看管理令牌
kubeadm token
# 还原之前使用 kubeadm init 或者 kubeadm join 对节点产生的改变,删除之前的操作。
kubeadm reset 
# 打印出 kubeadm 版本
kubeadm version 
# 预览一组可用的新功能以便从社区搜集反馈
kubeadm alpha 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

注意如果是国内的话需要使用国内的镜像源

apt-get update && apt-get install -y apt-transport-https

curl -fsSL https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | sudo apt-key add -

cat >/etc/apt/sources.list.d/kubernetes.list <<EOF 
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
EOF

apt-get update

# 删除旧的版本
apt remove kubeadm kubectl kubelet -y
# 安装最新版本
apt -y install kubectl kubelet kubeadm
# 升级系统时不升级这些组件
apt-mark hold kubelet kubeadm kubectl
# 安装指定版本
apt install -y kubelet=1.23.2-00 kubeadm=1.23.2-00 kubectl=1.23.2-00
# 查看版本
kubectl version
# 开机启动
systemctl enable kubelet
# 查看出错信息
journalctl -xefu kubelet
# -xefu 说明
# -x --catalog Add message explanations where available
# -e --pager-end Immediately jump to the end in the pager
# -f --follow Follow the journal
# -u --unit=UNIT Show logs from the specified unit
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

如果是其他节点下载缓慢可以同步Master节点的:

如果集群下载缓慢的话,需要同步apt缓存,位置在/var/cache/apt/archives/

rsync -av  --progress /var/cache/apt/archives/ root@master1:/var/cache/apt/archives/
1
2
3

# K8S和Docker

# CRI接口时间演变

参考文档:

K8S和Docker的关系有些复杂,基本他们之间的演进关系如下:

  1. 2014年Docker是鼎盛时期,K8S刚诞生,当时它支持Docker。
  2. 2015年7月,K8S发布1.0版本。
  3. 2015年12月11日,CNCF成立。
  4. 2016年12月9日,K8S的1.5版本发布,引入了CRI(Container Runtime Interface)容器运行时接口。在此之前Kubelet通过内嵌的dockershim操作Docker API。
  5. 2016年12月,docker在容器生态中竞争失败,宣布将其containerd捐赠给CNCF。2017年3月containerd正式加入CNCF。
  6. 2017年10月,CRI-O的1.0发布,支持将runc和Kata容器作为容器运行时。CRI-O 能够让 Kubernetes 使用任意兼容 OCI 的运行时作为运行 Pod 的容器运行时,CRI-O 本身也是 CRI-runtime。操控 CRI-O 的常用命令行工具包括:
    1. crictl:用于排除故障和直接与 CRI-O 容器引擎一起工作(需安装 cri-tools,较常用)。
    2. runc:用于运行容器镜像(CRI-O 自带,几乎不用)。
    3. podman:用于在容器引擎之外管理 pod 和容器镜像(run, stop, start, ps, attach, exec 等,需安装 podman,经常用)。一些 Docker 功能被包含在其他工具中,而不是 CRI-O 中。例如,podman提供了与许多 docker 命令功能完全兼容的命令行,并将这些功能也扩展到管理 pod 上。一些 Docker 功能被包含在其他工具中,而不是 CRI-O 中。例如,podman提供了与许多 docker 命令功能完全兼容的命令行,并将这些功能也扩展到管理 pod 上。
  7. 2018年7月发布Containerd1.1.2正式将CRI-containerd以插件形式cri-plugion集成到了Containerd中。理论上来说,从此刻开始Containerd就可以独立支持Kubelete CRI了。

# CRI接口架构演变

看下containerd和dockershim之间的:

k8scontainerd

  1. 使用docker-shim的时候:

    dockershim

    dockershim2

    dockershim3

  2. 到了Containerd时候:

    dockercontainerd

    dockercontainerd2

在k8s的1.24版本以后,Dockershim不在kubelet里面了,这个时候的选择有:

  1. 安装docker-ce,containerd,以及dockershim(cri-dockerd)。
  2. 安装containerd。
  3. 安装cri-o。

# Cri-Dockerd

  1. 官网 (opens new window)下载包。

  2. dpkg -i download.deb。

  3. systemctl start cri-docker.service cri-docker.socket

  4. 配置cri-dockerd

    vim /usr/lib/systemd/system/cri-docker.service
    
    ExecStart=/usr/bin/cri-dockerd --container-runtime-endpoint=unix:///var/run/cri-dockerd.sock --network-plugin=cni --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause
    # 注意 sock 是/var/lib/cri-dockerd.sock
    # 启动服务
    systemctl daemon-reload
    systemctl restart cri-docker.service cri-docker.socket
    #设置开机启动
    systemctl enable cri-docker.service cri-docker.socket
    # 查看启动状态
    systemctl status cri-docker.service cri-docker.socket
    # 卸载
    dpkg -P cri-dockerd
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  5. 配置kubelet:

    # 配置kubelet
    vi /var/lib/kubelet/kubeadm-flags.env
    
    KUBELET_KUBEADM_ARGS="--container-runtime=remote --container-runtime-endpoint=unix:///var/run/cri-dockerd.sock --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause"
    
    # 重启kubelet
    systemctl daemon-reload
    systemctl restart kubelet
    
    1
    2
    3
    4
    5
    6
    7
    8
  6. 启动Master节点:

    # 下载相关镜像
    kubeadm config images pull --image-repository registry.aliyuncs.com/google_containers --cri-socket unix:///var/run/cri-dockerd.sock
    
    # kubeadm初始化
    # --kubernetes-version=v1.26.0 
    # --apiserver-advertise-address 为本机ip。
    kubeadm init --image-repository registry.aliyuncs.com/google_containers --pod-network-cidr=10.10.0.0/16 --service-cidr=10.20.0.0/16 --apiserver-advertise-address=192.168.20.20 --cri-socket unix:///var/run/cri-dockerd.sock
    
    1
    2
    3
    4
    5
    6
    7

# 使用containerd方式

下面是直接把worker1节点从cri-dockerd变为containerd方式。如果是一开始就使用containerd方式,只用第5,6步就行了。

  1. 先查看下有多少pod在该节点上:

    kubectl get pod -owide -A | grep worker1  
    kubectl get pod -owide -A -w  
    kubectl describe node worker1  
    
    1
    2
    3
  2. 将该节点设置为不可调度,并驱离

    kubectl drain worker1 --ignore-daemonsets

  3. 停掉cri-dockerd

    systemctl stop docker docker.socket 
    systemctl disable docker docker.socket
    systemctl stop cri-docker.service cri-docker.socket
    systemctl disable cri-docker.service cri-docker.socket
    
    1
    2
    3
    4
  4. 启动containerd:

    systemctl status containerd.service
    
    1
  5. 修改containerd的配置文件:

    containerd config default > /etc/containerd/config.toml
    
    vi /etc/containerd/config.toml
    # 做如下操作:
    [plugins."io.containerd.grpc.v1.cri"]
      sandbox_image = "registry.aliyuncs.com/google_containers/pause"
    
    [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
      SystemdCgroup = true
    
    [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
      endpoint = ["https://b9pmyelo.mirror.aliyuncs.com"]
    
    # 重启containerd
    systemctl daemon-reload
    systemctl restart containerd.service
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
  6. 配置kubelet使用containerd:

    vi /var/lib/kubelet/kubeadm-flags.env
    
    KUBELET_KUBEADM_ARGS="--container-runtime-endpoint=unix:///var/run/containerd/containerd.sock --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause"
    
    systemctl daemon-reload
    systemctl restart kubelet
    
    kubectl get nodes -o wide
    
    1
    2
    3
    4
    5
    6
    7
    8
  7. 把当前节点设置为可调度的:

    # cordon是设置为不可调度,不驱离。
    kubectl uncordon worker1
    # 删掉master上的一节点发现会到worker1上
    kubectl delete pod podName
    crictl ps  
    crictl pods  
    
    1
    2
    3
    4
    5
    6

# CRI-O方式安装

  1. 启用内核模块:

    modprobe overlay
    modprobe br_netfilter
    
    1
    2
  2. 安装cri-o,设置好下面环境变量然后按照文档 (opens new window)安装:

    export VERSION=1.26
    export OS=Debian_11
    
    1
    2
  3. 配置镜像地址,启动cri-o

    vi /etc/crio/crio.conf
    pause_image = "registry.aliyuncs.com/google_containers/pause"
    
    systemctl daemon-reload
    systemctl restart crio
    systemctl enable crio
    
    1
    2
    3
    4
    5
    6
  4. 配置kubelet

    vi /var/lib/kubelet/kubeadm-flags.env
    KUBELET_KUBEADM_ARGS="--container-runtime=remote --container-runtime-endpoint=unix:///var/run/crio/crio.sock --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause"
    
    systemctl daemon-reload
    systemctl restart kubelet
    systemctl enable kubelet
    
    1
    2
    3
    4
    5
    6
  5. 加入Master节点

    kubeadm token create --print-join-command
    
    kubeadm join MasterIp:6443 --token ywalg8.biu9f8whl3n59q7l --discovery-token-ca-cert-hash sha256:aff733afd431754a2a0f978be5a4261b3d17c0af5abbf2cf0fd2dfe655e3996e --cri-socket unix:///var/run/crio/crio.sock
    
    scp root@master1:/root/.kube/config /root/.kube/config
    
    kubectl label nodes worker2 node-role.kubernetes.io/node=
    
    # 也可不用做如下操作
    crictl config --set runtime-endpoint=unix:///var/run/crio/crio.sock
    
    # 做个测试
    kubectl scale deployment demo1 --replicas 8  
    
    # 查看不同的实例所在节点
    kubectl get pod -owide  
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

# Master节点配置

使用kubeadm启动:

# 下载相关镜像
kubeadm config images pull --image-repository registry.aliyuncs.com/google_containers --cri-socket unix:///var/run/cri-dockerd.sock

# kubeadm初始化
# --kubernetes-version=v1.26.0 
# --apiserver-advertise-address 为本机ip。
kubeadm init --image-repository registry.aliyuncs.com/google_containers --pod-network-cidr=10.10.0.0/16 --service-cidr=10.20.0.0/16 --apiserver-advertise-address=192.168.20.20 --cri-socket unix:///var/run/cri-dockerd.sock
1
2
3
4
5
6
7

注意--cri-socket的可能性一共有以下四种:

  1. unix:///var/run/dockershim.sock #1.24.0 以后版本已经没有了
  2. unix:///run/containerd/containerd.sock #没有docker,有 containerd
  3. unix:///run/crio/crio.sock #没有containerd和docker
  4. unix:///var/run/cri-dockerd.sock #有cri-docker + docker + containerd

# 安装flannel网络

  1. 上面安装完后,配置以使kubectl访问集群

    mkdir -p $HOME/.kube
    sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
    sudo chown $(id -u):$(id -g) $HOME/.kube/config
    # 使用如下命令查看是否有coredns,如果没有需要进行第2步卸载重新装,如果有的话直接第3步。
    kubectl get pod -A
    
    1
    2
    3
    4
    5
  2. 卸载kubelet

    kubeadm reset
    kubeadm reset --cri-socket unix:///var/run/cri-dockerd.sock
    # 清空定义的所有内容
    # 使用 flannel 插件
    ip link delete cni0
    ip link delete flannel.1
    rm -rf /var/lib/cni/
    rm -rf /etc/kubernetes
    rm -rf /root/.kube/config
    rm -rf /var/lib/etcd
    ipvsadm -C
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  3. 安装pod Flannel网络,需要下载文件kube-flannel.yml (opens new window)

    1. 将其中PodCIDR默认地址从"10.244.0.0/16"改为"10.10.0.0/16"

    2. 如果有多个网卡,使用如下参数指定

      - --ip-masq
      - --kube-subnet-mgr
      - --iface=enp0s8
      
      1
      2
      3
    3. kubectl apply -f kube-flannel.yaml

    4. kubectl get ds -l app=flannel -n kube-system

    5. kubectl get pod -A # 正常情况下应该会有flannel的相关容器

# 去掉调度污点

上面安装完后,可以测试部署应用了:

kubectl create deployment demo1 --image=nginx:latest
kubectl delete pods podsName
kubectl delete deployment demo1
kubectl get pod
kubectl describe pod podName

上面操作或显示状态为pending,这是因为当前master节点角色为控制平面,是不允许运行容器的。它有一个不能调度的污点标志,如果我们想让master节点运行容器,需要去掉这个污点。

kubectl describe node | grep -i taint
kubectl taint nodes master1 node-role.kubernetes.io/master- #1.24.0以前
kubectl taint node master1 node-role.kubernetes.io/control-plane- # 1.24.0以后

# 使用ipvs转发流量

让k8s的负载均衡使用ipvs来转发流量:

kubectl describe configmap kube-proxy -n kube-system
kubectl edit configmap kube-proxy -n kube-system

mode: "ipvs"

kubectl get pod -A
# 删掉后会发现它会重启一个
kubectl delete pod -n kube-system kube-proxy-*
1
2
3
4
5
6
7
8

# 从节点配置

如果当前环境是从Master节点镜像过来的,需要:

  1. 修改ip
  2. 删除k8s
  3. 修改主机名

在Master节点上:

kubeadm token create --print-join-command

在从节点上:

kubeadm join MasterIp:6443 --token ywalg8.biu9f8whl3n59q7l --discovery-token-ca-cert-hash sha256:aff733afd431754a2a0f978be5a4261b3d17c0af5abbf2cf0fd2dfe655e3996e --cri-socket unix:///var/run/cri-dockerd.sock

然后准备kubectl

mkdir .kube
scp root@master1:/root/.kube/config /root/.kube/config
1
2

检查角色:

kubectl get node
kubectl label nodes worker1 node-role.kubernetes.io/node=
1
2

扩充负载:

kubectl scale deployment demo1 --replicas 5  

# 查看不同的实例所在节点
kubectl get pod -owide  
1
2
3
4

# 其他

# 设置别名

vi /etc/profile
alias k=kubectl
complete -F __start_kubectl k
1
2
3

# 常见错误诊断

当出现错误时,可以依次查看如下是否跟我们配置的一样:

systemctl status -l kubelet
systemctl status -l cri-docker
systemctl status cri-docker.socket
journalctl -xefu kubelet
# k8s查看组件状态
kubectl get cs
# 查看pod,留意下coredns是否安装
kubectl get pod -A
# 查看节点
kubectl get node -owide  
kubectl get all -A
1
2
3
4
5
6
7
8
9
10
11

# 常见错误

  1. 如果出现如下错误:

    unknown service runtime.v1alpha2.RuntimeService
    
    1

    解决办法:将 /etc/containerd/config.toml中的disabled_plugins = ["cri"]注释掉。

  2. 如果kubelet安装失败,可能是cri-docker配置未生效。

  3. 出现错误:

    WARN[0000] image connect using default endpoints: [unix:///var/run/dockershim.sock unix:///run/containerd/containerd.sock unix:///run/crio/crio.sock unix:///var/run/cri-dockerd.sock]. As the default settings are now deprecated, you should set the endpoint instead.
    
    1

    这时可以需要设置crictl使用哪个socket

    crictl config runtime-endpoint unix:///var/run/containerd/containerd.sock
    
    1

    也可以修改默认配置文件:

    vi /etc/crictl.yaml
    # 加入如下编辑内容
    runtime-endpoint: unix:///var/run/containerd/containerd.sock
    image-endpoint: unix:///var/run/containerd/containerd.sock
    debug: false
    pull-image-on-create: false
    disable-pull-on-run: false
    
    1
    2
    3
    4
    5
    6
    7

# cgroup介绍

cgroups,其名称源自控制组群(control groups)的简写,是Linux内核的一个功能,用来限制、控制与分离一个进程组的资源(如CPU、内存、磁盘输入输出等)。它的一个设计目标是为不同的应用情况提供统一的接口,从控制单一进程(像nice)到操作系统层虚拟化(像OpenVZ,Linux-VServer,LXC)。cgroups提供:

  • 资源限制:组可以被设置不超过设定的内存限制;这也包括虚拟内存。
  • 优先级:一些组可能会得到大量的CPU或磁盘IO吞吐量。
  • 结算:用来衡量系统确实把多少资源用到适合的目的上。
  • 控制:冻结组或检查点和重启动。

具体功能如下:

  1. 限制进程组可以使用的资源数量(Resource limiting )。比如:memory子系统可以为进程组设定一个memory使用上限,一旦进程组使用的内存达到限额再申请内存,就会触发OOM(out of memory)。
  2. 进程组的优先级控制(Prioritization )。比如:可以使用cpu子系统为某个进程组分配特定cpu share。
  3. 记录进程组使用的资源数量(Accounting )。比如:可以使用cpuacct子系统记录某个进程组使用的cpu时间。
  4. 进程组隔离(Isolation)。比如:使用ns子系统可以使不同的进程组使用不同的namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间。
  5. 进程组控制(Control)。比如:使用freezer子系统可以将进程组挂起和恢复。

cgroup 是通过一系列的文件来管控所有的资源分配的,包括创建了一个 cgroup,同时将一个 cgroup 和这个进程进行关联,也就是将进程号 echo 到那个 procs 文件里面,同时修改cpu 的 quota 来限制其使用的资源,这一整套都是 cgroup 的文件系统,cgroup 本身可以有不同的 driver。

docker 默认用 cgroupfs 作为 cgroup 驱动。而操作系统默认使用systemd驱动(systemd cgroup driver),如果其他进程是使用 systemd 拉起来的,systemd 拉起来的那些进程就由 systemd 这套系统去管控的,docker 拉起的进程是由 cgroupfs 这套系统去管控的,这就会出现同一套资源被两套系统网外分,当系统压力大的时候就会引发不必要的问题,所以kubernetes 在启动的时候会做一些检查,在启动 kubelet 的时候,去判断 docker 使用了哪一个 cgroup driver,如果 docker 本身不是使用 systemd,那么它就会默认报错不启动。所以当使用 kubernetes 的时候,要将两者统一。

当操作系统使用 systemd 作为 init system 时,初始化进程生成一个根 cgroup 目录结构并作为 cgroup 管理器。systemd 与 cgroup 紧密结合,并且为每个 systemd unit 分配 cgroup,因此,在 systemd 作为 init system 的系统中,默认并存着两套 groupdriver。docker 和 kubelet 管理的进程被 cgroupfs 驱动管理,而 systemd 拉起的服务由 systemd 驱动管理,让 cgroup 管理混乱且容易在资源紧张时引发问题。因此 kubelet 会默认--cgroup-driver=systemd,若运行时 cgroup 不一致时,kubelet 会报错。

# 下载镜像

保存相关镜像:

kubeadm config images list >> image.list
1

下载相关镜像:

#!/bin/bash
img_list='
k8s.gcr.io/kube-apiserver:v1.17.2
k8s.gcr.io/kube-controller-manager:v1.17.2
k8s.gcr.io/kube-scheduler:v1.17.2
k8s.gcr.io/kube-proxy:v1.17.2
k8s.gcr.io/pause:3.1
k8s.gcr.io/etcd:3.4.3-0
k8s.gcr.io/coredns:1.6.5
'

for img in $img_list
do
    docker pull $img
done
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

worker节点需要的镜像为:

k8s.gcr.io/kube-proxy
k8s.gcr.io/pause