使用 Kubeadm 部署 Kubernetes 集群

    前言

    Kubernetes 相关简介

    首先,它是一个全新的基于容器技术的分布式架构领先方案。这个方案虽然还很新,但它是谷歌十几年以来大规模应用容器技术的经验积累和升华的一个重要成果。确切地说,Kubernetes是谷歌严格保密十几年的秘密武器----Borg的一个开源版本。Borg是谷歌的一个久负盛名的内部使用的大规模集群管理系统,它基于容器技术,目的是实现资源管理的自动化,以及跨多个数据中心的资源利用率的最大化。

    在Kubernetes中,Service(服务)是分布式集群架构的核心,一个Service对象拥有如下关键特征:

    1. 拥有一个唯一指定的名字(比如mysql-server)
    2. 拥有一个虚拟IP(Cluster IP、Service IP或VIP)和端口号
    3. 能够提供某种远程服务能力
    4. 被映射到了提供这种服务能力的一组容器应用上

    Service 的服务进程目前都基于Socket通信方式对外提供服务,比如 Redis、Memcache、MySQL、Web Service,或者是实现某个具体业务的一个特定的TCP Server进程。容器提供了强大的隔离功能,所以有必要把为Service提供服务的这组进程放入容器中进行隔离。为此,Kubernetes设计了Pod对象,将每个服务进程封装到相应的Pod中,使其成为Pod中运行的一个容器(Container)。为了建立Service和Pod间的关联关系,Kubernetes首先给每个Pod贴上一个标签(Label),如给运行MySQL的Pod贴上name=mysql标签,给运行PHP的Pod贴上name=php标签,然后给相应的Service定义标签选择器(Label Selector),比如MySQL Service的标签选择器的选择条件为name=mysql,意为该Service要作用于所有包含name=mysql Label的Pod上。

    Kubernetes优点

    1. 自动装箱、自我修复、水平扩展、服务发现和负载均衡、自动发布和回滚
    2. 密钥和配置管理、存储编排、批量处理执行

    Kubernetes介绍

    在集群管理方面,Kubernetes将集群中的机器划分为一个Master节点和一群工作节点(Node)。

    Master节点上运行着集群管理相关的一组进程kube-apiserver、kube-controller-manager和kube-scheduler,这些进程实现了整个集群的资源管理、Pod调度、弹性伸缩、安全控制、系统监控和纠错等管理功能,并且都是全自动完成的。

    Node作为集群中的工作节点,运行着真正的应用程序,在Node上Kubernetes管理的最小运行单元是Pod。Node上运行着Kubernetes的kubelet、kube-proxy服务进程,这些服务进程负责Pod的创建、启动、监控、重启、销毁,以及实现软件模式的负载均衡器。

    master节点

    Master节点上运行着以下一组关键进程

    1. Kubernetes API Server(kube-apiserver),提供了HTTP Rest接口的关键服务进程,是Kubernetes里所有资源的增、删、改、查等操作的唯一入口,也是集群控制的入口进程。
    2. Kubernetes Controller Manager(kube-controller-manager),Kubernetes里所有资源对象的自动化控制中心,可以理解为资源对象的“大总管”。
    3. Kubernetes Scheduler(kube-scheduler),负责资源调度(Pod调度)的进程,相当于公交公司的“调度室”。

    其实Master节点上往往还启动了一个etcd Server进程,因为Kubernetes里的所有资源对象的数据全部是保存在etcd中的。

    node节点

    除了Master,Kubernetes集群中的其他机器被称为Node节点,在较早的版本中也被称为Minion。与Master一样,Node节点也可以是一台物理主机,也可以是一台虚拟机。Node节点才是Kubernetes集群中的工作负载节点,每个Node都会被Master分配一些工作负载(Docker容器),当某个Node宕机时,其上的工作负载会被Master自动转移到其他节点上去。
    每个Node节点上都运行着以下一组关键进程。

    1. kubelet:负责Pod对应的容器的创建、启停等任务,同时与Master节点密切协作,实现集群管理的基本功能。
    2. kube-proxy:实现Kubernetes Service的通信与负载均衡机制的重要组件。
    3. Docker Engine(docker):Docker引擎,负责本机的容器创建和管理工作。

    Pod

    Pod是Kubernetes的最重要也最基本的概念,每个Pod都有一个特殊的被称为“根容器”的Pause容器。Pause容器对应的景象属于Kubernetes平台的一部分,除了Pause容器,每个Pod还包含一个活多个紧密相关的用户业务容器。假如pod里面有php-fpm和nginx两个容器,他们首先不能端口冲突,其次,##他们都是共享pause容器的。

    Pod其实有两种类型:普通的Pod及静态Pod(static Pod),后者比较特殊,它并不存放在Kubernetes的etcd存储里,而是存放在某个具体的Node上的一个具体文件中,并且只在此Node上启动运行。而普通的Pod一旦被创建,就会被放入到etcd中存储,随后会被Kubernetes Master调度到某个具体的Node上并进行绑定(Binding),随后该Pod被对应的Node上的kubelet进程实例化成一组相关的Docker容器并启动起来。在默认情况下,当Pod里的某个容器停止时,Kubernetes会自动检测到这个问题并且重新启动这个Pod(重启Pod里的所有容器),如果Pod所在的Node宕机,则会将这个Node上的所有Pod重新调度到其它节点上。
    Pod、容器与Node的关系图如下:

    K8S结构图

    etcd内部架构图

    各组件的作用

    master节点组件作用:
    • CrontrollerManager:维护副本期望数目的作用。
    • scheduler:资源调度器,选择合适的节点把任务交给api server分配任务。
    • api server:一切服务访问的统一入口,任何东西都要跟它进行交互。把接收到的各类信息,写入到etcd中进行存储。
    • etcd:是go语言编写的键值对的分布式的存储数据库。存储k8s的关键数据,协助集群正常运转。
    node节点组件作用:
    • kubelet:C(容器) R(运行环境) I(接口) 直接跟容器引擎进行交互实现维护pod的生命周期。
    • kube-proxy:负责规则写入iptables、ipvs,实现访问、负载均衡等作用
    其他组件:
    • CoreDNS:可以为集群中的svc创建一个域名ip的对应关系解析
    • Dashboard:给k8s集群提供一个B/S结构访问体系
    • INGRESS controller:官方只能实现4层代理,Ingress可以实现7层代理
    • Federation:提供一个可以跨集群中心多K8S统一管理功能
    • prometheus:提供一个k8s的集群的监控能力
    • ELK:提供K8S集群日志统一分析接入平台。

    安装环境

    组件明细

    组件 版本
    CentOS 7.6
    Kubernetes v1.19.4
    Etcd v3.3.10
    Flannel v0.13.0

    部署节点说明

    主机名 IP地址 角色 配置k8
    k8s-master 192.168.100.101 master 2 CPU, 4G MEM, 100G DISK
    K8s-node01 192.168.100.102 node01 2 CPU, 4G MEM, 100G DISK
    K8s-node02 192.168.100.103 node02 2 CPU, 4G MEM, 100G DISK

    部署过程

    系统初始化环境准备

    1、分别设置每台服务器的主机名和本地hosts

    • 设置主机名
    hostnamectl set-hostname k8s-master
    hostnamectl set-hostname k8s-node01
    hostnamectl set-hostname k8s-node02
    
    • 写本地hosts
    cat <<EOF >>/etc/hosts
    
    192.168.100.101 k8s-master
    192.168.100.102 k8s-node01
    192.168.100.103 k8s-node02
    
    EOF
    

    2、禁用防火墙、selinux和swap

    systemctl stop firewalld
    systemctl disable firewalld
    setenforce 0
    sed -i "s/^SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config
    swapoff -a
    sed -i 's/.*swap.*/#&/' /etc/fstab
    

    3、配置相关内核参数

    cat > /etc/sysctl.d/k8s.conf <<EOF
    
    net.bridge.bridge-nf-call-ip6tables = 1
    net.bridge.bridge-nf-call-iptables = 1
    net.ipv4.ip_forward = 1
    EOF
    
    modprobe br_netfilter
    sysctl -p /etc/sysctl.d/k8s.conf
    

    4、配置 ipvs相关模块

    yum install ipset ipvsadm -y
    
    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_ipv4
    EOF
    chmod 755 /etc/sysconfig/modules/ipvs.modules && bash /etc/sysconfig/modules/ipvs.modules && lsmod | grep -e ip_vs -e nf_conntrack_ipv4
    

    5、同步时间

    # 安装 ntpdate
    yum install ntpdate -y
    
    # 同步本地时间
    ntpdate time.windows.com
    
    # 跟网络源做同步
    ntpdate cn.pool.ntp.org
    
    # 时间同步定时任务
    crontab -e
    * */1 * * * /usr/sbin/ntpdate cn.pool.ntp.org
    
    # 重启 crond 服务
    service crond restart
    

    集群组件安装

    1、安装docker环境

    • 配置国内yum源
    yum install -y yum-utils device-mapper-persistent-data lvm2
    yum-config-manager \
        --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    
    • 查看当前docker版本列表
    yum list docker-ce.x86_64  --showduplicates |sort -r
    docker-ce.x86_64            3:20.10.0-3.el7                    docker-ce-stable 
    docker-ce.x86_64            3:19.03.9-3.el7                    docker-ce-stable 
    docker-ce.x86_64            3:19.03.8-3.el7                    docker-ce-stable 
    docker-ce.x86_64            3:19.03.7-3.el7                    docker-ce-stable 
    docker-ce.x86_64            3:19.03.6-3.el7                    docker-ce-stable 
    docker-ce.x86_64            3:19.03.5-3.el7                    docker-ce-stable 
    docker-ce.x86_64            3:19.03.4-3.el7                    docker-ce-stable 
    docker-ce.x86_64            3:19.03.3-3.el7                    docker-ce-stable 
    docker-ce.x86_64            3:19.03.2-3.el7                    docker-ce-stable 
    docker-ce.x86_64            3:19.03.14-3.el7                   docker-ce-stable 
    docker-ce.x86_64            3:19.03.1-3.el7                    docker-ce-stable 
    docker-ce.x86_64            3:19.03.13-3.el7                   docker-ce-stable 
    docker-ce.x86_64            3:19.03.13-3.el7                   @docker-ce-stable
    docker-ce.x86_64            3:19.03.12-3.el7                   docker-ce-stable 
    docker-ce.x86_64            3:19.03.11-3.el7                   docker-ce-stable 
    docker-ce.x86_64            3:19.03.10-3.el7                   docker-ce-stable 
    docker-ce.x86_64            3:19.03.0-3.el7                    docker-ce-stable 
    docker-ce.x86_64            3:18.09.9-3.el7                    docker-ce-stable 
    docker-ce.x86_64            3:18.09.8-3.el7                    docker-ce-stable 
    docker-ce.x86_64            3:18.09.7-3.el7                    docker-ce-stable 
    docker-ce.x86_64            3:18.09.6-3.el7                    docker-ce-stable 
    docker-ce.x86_64            3:18.09.5-3.el7                    docker-ce-stable 
    docker-ce.x86_64            3:18.09.4-3.el7                    docker-ce-stable 
    docker-ce.x86_64            3:18.09.3-3.el7                    docker-ce-stable 
    docker-ce.x86_64            3:18.09.2-3.el7                    docker-ce-stable 
    docker-ce.x86_64            3:18.09.1-3.el7                    docker-ce-stable 
    docker-ce.x86_64            3:18.09.0-3.el7                    docker-ce-stable 
    
    • 安装指定版本(需在kubeadm当前支持的版本列表内)
    yum install -y --setopt=obsoletes=0 docker-ce-19.03.9-3.el7
    
    • 设置普通用户免sudo运行,启动服务
    sudo groupadd docker
    sudo gpasswd -a ${USER} docker
    newgrp docker 
    systemctl start docker
    systemctl enable docker
    
    • 修改docker默认的cgroup driver为systemd
    • 查看最大的磁盘路径,修改data-root的路径,/var/lib/docker为默认路径,如果需要独立数据盘,请重新格式化数据盘并修改data-root路径。
    $ cat>/etc/docker/daemon.json<<'EOF'
    {
        "exec-opts": ["native.cgroupdriver=systemd"],
        "log-driver": "json-file",
        "data-root": "/var/lib/docker",
        "log-opts": {
            "max-size": "100m",
            "max-file": "10"
        },
        "oom-score-adjust": -1000,
        "registry-mirrors": ["https://registry.docker-cn.com"],
        "storage-driver": "overlay2",
        "storage-opts":["overlay2.override_kernel_check=true"],
        "live-restore": true
    }
    EOF
    
    $ systemctl restart docker
    
    $ docker info | grep Cgroup
    Cgroup Driver: systemd
    

    2、安装Kubeadm相关组件

    • 配置国内kubernetes源
    cat <<EOF > /etc/yum.repos.d/kubernetes.repo
    [kubernetes]
    name=Kubernetes
    baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
    enabled=1
    gpgcheck=0
    repo_gpgcheck=0
    gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
    
    EOF
    
    • 安装指定版本的组件(需要跟目标集群版本一致,此处为1.19.4)
    yum install -y kubelet-1.19.4 kubeadm-1.19.4 kubectl-1.19.4
    systemctl enable kubelet
    

    Master节点部署

    1、使用kubeadm初始化集群

    kubeadm init --kubernetes-version=1.19.4 \
      --apiserver-advertise-address=10.200.14.112 \
      --image-repository=registry.aliyuncs.com/google_containers \
      --service-cidr=10.10.0.0/16 \
      --pod-network-cidr=10.1.0.0/16
    

    此处定义api-server地址为master内网网卡IP地址,POD网段为10.1.0.0/16

    2、集群初始化成功后会返回如下信息,此命令需要后续阶段在其他node节点执行

    kubeadm join 10.200.14.112:6443 --token fm08rl.5xpr14nhs83p729w \
        --discovery-token-ca-cert-hash sha256:44850f41f4567417491f4ef5591f189765b279f5bc2d876fc5e8ce3edc0c87e5 
    

    3、配置kubectl工具

    mkdir -p $HOME/.kube
    sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
    sudo chown $(id -u):$(id -g) $HOME/.kube/config
    
    #自动补全
    yum install -y bash-completion
    echo "source /usr/share/bash-completion/bash_completion" >> ~/.bashrc
    echo "source <(kubectl completion bash)" >> ~/.bashrc
    

    4、检查集群健康状态

    $ kubectl get cs
    NAME                 STATUS    MESSAGE             ERROR
    controller-manager   Healthy   ok                  
    scheduler            Healthy   ok                  
    etcd-0               Healthy   {"health":"true"}
    etcd-1               Healthy   {"health":"true"}
    etcd-2               Healthy   {"health":"true"}
    
    • 如果controller-manager和scheduler 状态为Unhealthy
    # sed -i '/--port\=0/ s/^\(.*\)$/#\1/g' /etc/kubernetes/manifests/kube-controller-manager.yaml
    # sed -i '/--port\=0/ s/^\(.*\)$/#\1/g' /etc/kubernetes/manifests/kube-scheduler.yaml
    # systemctl restart kubelet
    
    

    5、如果在集群初始化过程中遇到问题,需要执行以下命令清理环境

    kubeadm reset
    ifconfig cni0 down
    ip link delete cni0
    ifconfig flannel.1 down
    ip link delete flannel.1
    rm -rf /var/lib/cni/
    rm -rf /var/lib/etcd/*
    

    Flannel网络组件部署

    1、部署Flannel网络组件

    这里注意需要修改文件配置里pod网段,与初始化时--pod-network-cidr时指定的网段一致。如果报错直接去浏览器复制粘贴。

    #wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
    
    #sed -i 's/10.244.0.0\/16/10.1.0.0\/16/g' kube-flannel.yml 
    #kubectl apply -f kube-flannel.yml
    
    

    flannel默认会选择服务器的eth0网卡,如果服务器存在多网卡,则需手动指定内网网卡名称。

    ......
    containers:
          - name: kube-flannel
            image: quay.io/coreos/flannel:v0.11.0-amd64
            command:
            - /opt/bin/flanneld
            args:
            - --ip-masq
            - --kube-subnet-mgr
            - --iface=eth1
    ......
    

    2、检查集群系统相关组件状态是否正常

    $ kubectl get pod -n kube-system
    NAME                              READY   STATUS    RESTARTS   AGE
    coredns-7b7df549dd-m2jtg          1/1     Running   0          13h
    coredns-7b7df549dd-p7hwl          1/1     Running   0          13h
    etcd-kmaster                      1/1     Running   0          13h
    kube-apiserver-kmaster            1/1     Running   0          13h
    kube-controller-manager-kmaster   1/1     Running   0          13h
    kube-flannel-ds-amd64-5vtp6       1/1     Running   0          13h
    kube-flannel-ds-amd64-hsdh7       1/1     Running   0          13h
    kube-proxy-7tn8w                  1/1     Running   0          13h
    kube-proxy-nl8bd                  1/1     Running   0          13h
    kube-scheduler-kmaster            1/1     Running   0          13h
    

    3、测试集群DNS是否正常工作

    • 运行curl工具容器测试解析
    $ kubectl run curl --image=radial/busyboxplus:curl -it
    [ root@curl-65f88748fd-5p4cm:/ ]$ nslookup kubernetes.default
    
    Server:    10.1.0.10
    Address 1: 10.1.0.10 kube-dns.kube-system.svc.cluster.local
    
    Name:      kubernetes.default
    Address 1: 10.1.0.1 kubernetes.default.svc.cluster.loca
    

    Node节点部署

    1、在其余两台node节点上执行Master初始化完成显示的命令:

    kubeadm join 192.168.100.101:6443 --token osru8w.rd9l1jz4iipyxq3t \
        --discovery-token-ca-cert-hash sha256:16fbd998a83d22b65089a0c519ec819a5f8ecebe918b551e52fe6b1717ec10c0 
    

    2、在master上执行集群状态检测

    $ kubectl get nodes
    NAME         STATUS   ROLES    AGE   VERSION
    k8s-master   Ready    master   13h   v1.19.4
    k8s-node01   Ready    <none>   13h   v1.19.4
    k8s-node02   Ready    <none>   13h   v1.19.4
    

    STATUS均为Ready,说明集群状态正常。

    3、部署完成后,master节点上默认存在不接受普通Pod调度的taints,可以执行以下命令取消:

    kubectl taint nodes k8s-master node-role.kubernetes.io/master:NoSchedule-
    

    这样,普通的Pod也可以调度到master节点上了。

    部署应用测试

    1、创建Nginx容器

    $ kubectl create deployment nginx --image=nginx
    $ kubectl expose deployment nginx --port=80 --type=NodePort
    $ kubectl get pod,svc
    NAME                         READY   STATUS    RESTARTS   AGE
    pod/nginx-6799fc88d8-ghcpv   1/1     Running   0          3m1s
    pod/nginx-6799fc88d8-njj6p   1/1     Running   0          3m
    pod/nginx-6799fc88d8-zvztr   1/1     Running   0          3m24s
    
    NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
    service/kubernetes   ClusterIP   10.10.0.1       <none>        443/TCP        9m6s
    service/nginx        NodePort    10.10.194.166   <none>        80:31293/TCP   3m19s
    

    2、本机访问nginx的svc,返回如下结果则正常

    $ curl 10.10.194.166 -I
    HTTP/1.1 200 OK
    Server: nginx/1.19.4
    Date: Fri, 13 Nov 2020 05:51:06 GMT
    Content-Type: text/html
    Content-Length: 612
    Last-Modified: Tue, 27 Oct 2020 15:09:20 GMT
    Connection: keep-alive
    ETag: "5f983820-264"
    Accept-Ranges: bytes
    

    集群中加入master节点

    1、得到加入集群的KEY

    kubeadm init phase upload-certs --experimental-upload-certs
    

    执行结果如下:

    [root@k8s-master ~]# kubeadm init phase upload-certs --upload-certs
    I0122 08:01:15.517893   60086 version.go:252] remote version is much newer: v1.23.2; falling back to: stable-1.18
    W0122 08:01:17.049273   60086 configset.go:202] WARNING: kubeadm cannot validate component configs for API groups [kubelet.config.k8s.io kubeproxy.config.k8s.io]
    [upload-certs] Storing the certificates in Secret "kubeadm-certs" in the "kube-system" Namespace
    [upload-certs] Using certificate key:
    5d817a5480c54bb079eab4f7b75b4dfe21bd36e059dfb46bf39f724adb3349aa
    

    2、得到加入集群的token

    kubeadm token create --print-join-command
    

    执行结果如下:

    [root@k8s-master ~]# kubeadm token create --print-join-command
    W0122 08:01:19.445355   60121 configset.go:202] WARNING: kubeadm cannot validate component configs for API groups [kubelet.config.k8s.io kubeproxy.config.k8s.io]
    kubeadm join 172.16.64.2:6443 --token xhsmiv.ggj00ojs6dvv8b23     --discovery-token-ca-cert-hash sha256:5211bd42a2e81b933b52ec83686f93ae6212542d22d00c621fad20f0dc9592b4
    

    3、将得到的token和key进行拼接,得到如下命令:

    kubeadm join 192.168.92.129:6443 --token xhsmiv.ggj00ojs6dvv8b23     
    		--discovery-token-ca-cert-hash sha256:5211bd42a2e81b933b52ec83686f93ae6212542d22d00c621fad20f0dc9592b4 
        --control-plane --certificate-key  5d817a5480c54bb079eab4f7b75b4dfe21bd36e059dfb46bf39f724adb3349aa
    

    注意事项:

    1、不要使用 --experimental-control-plane,会报错
    
    2、要加上--control-plane --certificate-key,不然就会添加为node节点而不是master
    
    3join的时候节点上不要部署,如果部署了kubeadm reset后再join
    

    4、 第一次加入集群的时候会有以下报错:

    [preflight] Running pre-flight checks
    [preflight] Reading configuration from the cluster...
    [preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
    error execution phase preflight:
    One or more conditions for hosting a new control plane instance is not satisfied.
    
    unable to add a new control plane instance a cluster that doesn't have a stable controlPlaneEndpoint address
    
    Please ensure that:
    * The cluster has a stable controlPlaneEndpoint address.
    * The certificates that must be shared among control plane instances are provided.
    
    
    To see the stack trace of this error execute with --v=5 or higher
    

    5、主节点上在kubeadm-config中添加controlPlaneEndpoint
    大概在这么个位置:

    kind: ClusterConfiguration
    kubernetesVersion: v1.18.0
    controlPlaneEndpoint: 172.16.64.2:6443
    

    然后再在准备添加为master的节点上执行kubeadm join(第三步)的命令

    6、成功

    This node has joined the cluster and a new control plane instance was created:
    
    * Certificate signing request was sent to apiserver and approval was received.
    * The Kubelet was informed of the new secure connection details.
    * Control plane (master) label and taint were applied to the new node.
    * The Kubernetes control plane instances scaled up.
    * A new etcd member was added to the local/stacked etcd cluster.
    
    To start administering your cluster from this node, you need to run the following as a regular user:
    
    	mkdir -p $HOME/.kube
    	sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
    	sudo chown $(id -u):$(id -g) $HOME/.kube/config
    
    Run 'kubectl get nodes' to see this node join the cluster.
    

    联系我们

    加入社区

    微信扫码
    加入官方交流群

    立即体验

    在线开通,按量计费,真正的云服务!

    立即开始

    选择观测云版本

    代码托管平台