# 部署

  • 可使用官方的部署工具 kubeadm ,部署标准的 k8s 。

  • 也可部署其它 k8s 发行版。它们在 k8s 的基础上封装了其它组件,比如 Web UI、网络插件。

    • minikube :用于部署测试用途的 k8s 。
    • Rancher :由 Rancher Labs 公司开源。
    • OpenShift :由 Red Hat 公司开源。
    • kubesphere :由青云公司开源。
    • KubeOperator :由飞致云公司开源。
  • 也可使用云平台托管的 k8s 发行版,不需要用户部署维护。

    • Elastic Kubernetes Service(EKS):由 AWS 云提供。
    • Azure Kubernetes Service(AKS):由 Azure 云提供。
    • Google Kubernetes Engine(GKE):由 Google 云提供。
    • Tencent Kubernetes Engine(TKE):由腾讯云提供。

# kubeadm

:一个命令行工具,用于部署标准的 k8s 集群。

  • 本身不会安装 kubelet、kubectl、网络插件。

# 安装

  • 用 yum 安装:
    # 采用阿里的镜像源,因为谷歌的镜像源需要翻墙访问
    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=1
    repo_gpgcheck=1
    gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
    EOF
    
    # 安装
    yum install -y kubeadm
    
    • 这会同时安装依赖的 kubelet、kubectl 。

# 命令

kubeadm
        version
        config        # 管理配置。这些配置会保存为一个名为 kubeadm-config 的 ConfigMap ,位于 kube-system 命名空间
          print       # 打印配置
            init-defaults
            join-defaults

        init          # 将本机初始化为主节点
        join          # 使本机连接到主节点,加入 k8s 集群
        reset         # 撤销 init、join 命令对本机的影响

        token         # 管理 token ,用于一个节点 join 主节点时的认证
          create
            --ttl     # 有效时长,过期则自动删除。默认为 24h
          delete <token>...
          list

# 部署

  1. 部署 k8s 时,主机需要满足以下条件:

    • 至少 2v CPU、2G RAM 。
      • 空载时,主节点的全部进程大概占用 500M 内存,而工作节点的 kubelet 占用 100M 内存。
    • 禁用 Swap 分区。
    • 每个主机的 hostname、MAC 地址不同。
    • 安装 docker 引擎。
      • dockerd 采用的 Cgroup 驱动默认为 cgroupfs ,而 kubelet 默认为 systemd 。因此需要修改 dockerd 的配置,并重启 dockerd :
        {
          "exec-opts": ["native.cgroupdriver=systemd"]
        }
        
    • 主机的防火墙开通了 k8s 所需的端口 6443、10250 。
    • 在集群的至少一个主机上安装 kubeadm、kubectl 。
    • 在集群的每个主机上安装 kubelet ,并启动:
      systemctl start  kubelet
      systemctl enable kubelet
      
  2. 下载 Docker 镜像:

    kubeadm config images pull --image-repository registry.aliyuncs.com/google_containers
    
  3. 在一个主机上执行以下命令,初始化为主节点:

    kubeadm init
                --config                            # 指定配置文件
                --kubernetes-version <string>       # k8s 版本。默认为最新的 stable 版本
                --image-repository registry.aliyuncs.com/google_containers  # 镜像仓库,默认为 k8s.gcr.io ,但它需要翻墙访问
                --node-name <name>                  # 指定当前节点名
                --pod-network-cidr 10.244.0.0/16    # Pod IP 的子网范围,也会被用于设置 cluster-cidr 。默认为空,不会分配 CIDR
                --service-cidr <string>             # Service IP 的子网范围,默认为 10.96.0.0/12
                --service-dns-domain <string>       # Service 的域名起点,默认为 cluster.local
    
    • 这会生成管理员的 kubeconfig 配置文件,将它拷贝到默认路径:
      mkdir -p ~/.kube
      cp /etc/kubernetes/admin.conf ~/.kube/config
      
  4. 在其它主机上执行以下命令,加入 k8s 集群:

    kubeadm join 10.0.0.1:6443
          --token ****** --discovery-token-ca-cert-hash sha256:******   # 加入集群时需要使用 token 认证
          --control-plane     # 加入之后成为控制平面节点,默认是工作节点
    
    • 至少需要部署一个主节点和一个工作节点。
    • 默认给主节点设置了污点,不允许用作工作节点,降低主节点故障的风险。可以移除污点:
      kubectl taint nodes --all node-role.kubernetes.io/master-
      
    • 部署多个主节点时,可以实现 k8s 的高可用。
      • 此时需要给多个 kube-apiserver 实例创建一个负载均衡 IP 或 DNS ,用于访问。
  5. 启用一种 CNI 网络插件:

    curl https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml | kubectl apply -f -
    

# kubectl

:一个命令行工具,用于管理已部署的 k8s 集群。

# 安装

  • 下载二进制文件:
    wget https://dl.k8s.io/release/v1.23.1/bin/linux/amd64/kubectl -O /usr/bin/kubectl
    chmod +x /usr/bin/kubectl
    kubectl completion bash > /etc/bash_completion.d/kubectl  # 启用 kubectl 命令补全,保存为 bash_completion 脚本
    
  • kubectl 命令通过访问 kube-apiserver 来控制 k8s 集群,此时需要读取一种配置文件,称为 kubeconfig 。
    • kubeconfig 配置了要连接的 kube-apiserver 地址,以及使用的集群名、命名空间、账号。
    • 默认读取 ~/.kube/config 文件作为 kubeconfig ,也可以在执行命令时加上 --kubeconfig <path> 参数,或声明环境变量 export KUBECONFIG=<path>

# 命令

kubectl
        # 查看集群
        cluster-info      # 显示集群信息
        version           # 显示 client 和 server 的版本
        config            # 访问 kubeconfig
            view          # 显示 kubeconfig 的内容
              --raw       # 是否显示完整内容,默认省略 token
        proxy             # 运行一个 HTTP 服务器,反向代理 apiserver
            --port=8001
            --address=127.0.0.1     # 监听的地址
            --accept-hosts='^localhost$,^127\.0\.0\.1$' # 允许 HTTP 请求采用的目标地址,采用正则匹配

        # 修改资源
        create                      # 创建资源
            -f <file>               # 指定一个配置文件。不支持指定多个文件,支持指定配置文件的 URL
            -f <dir>                # 指定一个目录,读取其下所有文件
              -R                    # 递归处理目录
        edit <resource> <name>      # 修改资源的 YAML 配置文件
        replace -f <file>           # 替换资源的配置文件。如果该资源不存在则报错。如果只是更改无序字段的顺序,则不会更新配置文件
            --force                 # 先删除旧资源,再创建
        apply -f <file>             # 更新资源的配置文件。如果该资源不存在则自动创建,如果该资源没有变化则显示 unchanged

        patch -p '{"spec":{"unschedulable":true}}'  # 更新资源的配置文件中的指定字段,而不是修改整个配置文件
        delete                      # 删除资源
            pod <name>...           # 删除指定名称的 pod
            deployment <name>...    # 删除指定名称的 deployment
            -f <file>               # 删除配置文件中指定的资源
            --grace-period=30       # 覆盖 Pod 已配置的宽限期
            --force                 # 立即从 etcd 删除资源,同时命令 kubelet 终止相应的 Pod ,依然会等待一小段宽限期
            --ignore-not-found      # 如果要删除的资源不存在,则不报错。默认会报错

        # 查看资源
        describe <resource>         # 查看资源的信息,包括配置、状态、event
                node [name]         # 查看 node 上部署了哪些 Pod ,以及每个 Pod 的 CPU 、内存使用率
        get <resource> [name]...    # 查看资源的信息。不指定 name 则显示这种资源的所有实例
            --kubeconfig ~/.kube/config # 指定 kubeconfig 的文件路径
            -n default              # --namespace ,指定命名空间,只查看该命名空间中的资源
            -A                      # --all-namespaces ,查看所有命名空间
            -l 'k1=v1, k2 notin (v1,v2)'            # --selector ,标签选择器
            --field-selector status.phase!=Running  # 字段选择器

            -o wide                 # --output ,输出格式。默认显示列表格式的简介,wide 是显示更多列
            -o name                 # 只显示名称
            -o json                 # 显示 JSON 格式的详细配置
            -o yaml                 # 显示 YAML 格式的详细配置
            -o template --template={{.spec.replicas}} # 按自定义的 Golang 模板显示

        # 管理 Node
        cordon      <node>          # 暂停调度节点。这会将该节点标记为 unschedulable 状态,不能用于部署新 Pod ,但不影响已部署的 Pod
        uncordon    <node>          # 恢复调度 Node
        drain       <node>          # 清空节点上部署的 Pod 。这会将该节点标记为 unschedulable 状态,将其上所有 Pod 标记为 evicted 状态
        taint node  <node>          # 设置节点的污点
                        dedicated=db:NoSchedule # 设置一个键值对 dedicated=special 作为污点,效果为 NoSchedule
                        dedicated-              # 删除指定 key 的所有污点

        # 管理 pod
        expose                      # 创建 service ,映射端口
        exec <pod> -- <command>     # 在 pod 中执行命令,采用容器内的默认用户(不支持主动选用 root 用户)。注意在 command 之前要加上分隔符 --
            -c <name>               # --container ,选择 pod 中的某个容器。默认选择第一个容器
            -it                     # 进入容器内终端
        rollout [command] <resource> [name]...  # 更新部署应用,仅限 daemonset、deployment、statefulset 类型,如果省略 name 则选择所有应用
            history                 # 显示更新部署的历史版本,默认只记录最近 10 个
            pause                   # 暂停更新
            resume                  # 继续更新
            restart                 # 根据 spec.strategy 重启应用(在后台异步执行)
            status                  # 显示状态
            undo <resource> <name>  # 回滚部署一个历史版本
                --to-revision=0     # 回滚到指定编号的版本。默认为 0 ,即上一个版本(非当前版本)
        scale <deployment|statefulset> <name>... --replicas=0   # 调整某个应用的 Pod 实例数量
        logs <pod>                  # 查看日志
            -c <name>               # --container ,选择 pod 中的某个容器
            --all-containers        # 选择 pod 中的所有容器
            -p                      # --previous ,选择之前的容器实例。比如一个 Pod 在重启前后存在不同的容器实例
            -f                      # --follow ,保持显示
            --tail 10               # 只显示最后几行。默认从头开始显示
            --timestamps            # 增加显示时间戳
            --since 1h              # 只显示最近一段时间的日志
            --since-time 2021-01-01T08:00:00Z # 只显示指定时刻开始的日志
        cp <pod>:<src_path> <dst_path>    # 将 Pod 中的文件拷贝到本机
  • resource 类型可以是 nodes、pods、services 等。
    • 不区分单复数,比如 node 等价于 nodes 。
    • 支持通过逗号分隔符指定多个,比如 nodes,pods 。
  • 例:
    kubectl create deployment nginx --image=nginx:1.20          # 部署一个应用
    kubectl exec nginx-6d777db949-5b77c -c nginx -it -- bash    # 进入容器内终端
    kubectl expose deployment nginx --port=80 --target-port=80  # 为应用 Nginx 创建 service ,默认为 ClusterIP 类型,并映射端口
    
  • 关于配置文件的版本:
    • 用户修改配置文件时可以指定 resourceVersion 字段,如果与 k8s 存储的不同,则说明用户不是在最新版本上编辑,会被 k8s 拒绝。
      • 如果省略 resourceVersion ,或者采用 kubectl patch 方式,则不会检查版本。
    • kubectl apply 可能会在 annotations 中增加一个 kubectl.kubernetes.io/last-applied-configuration 字段,记录资源的当前配置,用于比较差异,找出更新了哪些字段。
    • Deployment 资源还会增加一个 generation 字段,用于记录版本序号。

# minikube

:一个命令行工具,用于部署单节点的 k8s 集群,常用于测试。

  • 可以在本机部署多个 k8s 集群。每个 k8s 集群位于一个容器内,包含多个进程。

# 部署

  1. 安装 docker

  2. 下载 minikube :

    wget https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
    sudo install minikube-linux-amd64 /usr/local/bin/
    
  3. 以非 root 用户部署 k8s :

    useradd leo
    usermod leo -G docker
    su - leo
    minikube start
    
  4. 在本机安装 kubectl ,或使用 minikube 内置的 kubectl :

    minikube kubectl -- get pods -A
    

# 命令

minikube
        start                             # 部署一个 k8s 集群
            -p <name>                     # --profile ,指定 k8s 集群的名称,默认为 minikube
            --driver docker               # 驱动,默认会自动选择
            --kubernetes-version v1.2.3   # k8s 版本。默认为最新的 stable 版本
        stop
        delete          # 删除当前 k8s 集群
            -p <name>   # 指定 k8s 集群的名称
            --all       # 删除所有 k8s 集群
        pause           # 暂停运行
        unpase

        profile         # 显示当前 k8s 集群的名称
            list        # 显示所有 k8s 集群
        status

# Rancher

:一个 Web 网站,可以创建、托管多个 k8s 集群。

# 原理

  • 架构:

    • 先部署 Rancher server 。
    • 然后在一些主机上运行 Rancher agent ,部署 k8s 集群,或者控制已有的 k8s 集群。
      • 需要 agent 能通过网络单向连接到 server ,被 server 控制。不需要 server 能访问到 agent 的 IP 。
      • Rancher 可以托管多个 k8s 集群,称为下游集群。
        • 当 Rancher server 故障时,下游集群依然会正常运行。
  • Rancher 提供了多种 k8s 发行版:

    • RKE(Rancher Kubernetes Engine)
      • :一个 k8s 发行版。
      • 提供了一个命令行工具 rke ,用于部署 RKE 。
    • k3s
      • :一个轻量级的 k8s 发行版。
      • 主节点只需运行 k3s server 进程,工作节点只需运行 k3s agent 进程。
      • 默认采用 sqlite3 作为数据库。
  • Rancher 会在下游集群中创建一些专用应用:

    Namespace Type Name Comment
    default service kubernetes 用于反向代理 apiserver ,默认地址为 https://10.43.0.1
    cattle-system deployment cattle-cluster-agent 用于供 Rancher 访问下游集群的 apiserver 。如果它不可用,则使用 cattle-node-agent
    cattle-system daemonset cattle-node-agent 用于管理 k8s Node ,比如升级 kubelet
    cattle-system daemonset kube-api-auth 用于对 RKE 的用户进行身份认证
    cattle-fleet-system deployment fleet-agent 用于编排下游集群,包含一些 k8s controller
    • Cattle 是 Rancher 的容器编排引擎,基于 docker-compose.yml 或 k8s YAML 文件。
    • Fleet 是一个独立于 Rancher 的项目,用于以 GitOps 方式编排大量 k8s 集群。

# 部署

  • 用 docker 部署一个单节点的 Rancher :

    docker run -d \
        -p 80:80 -p 443:443 \
        --name rancher \
        --privileged \
        rancher/rancher:v2.6.3-linux-amd64
    
    • 此时 Rancher 会在容器内运行一个 k3s 集群。
  • 或者在 k8s 中用 helm 部署单节点的 Rancher 。也可以部署成多节点,实现高可用。

# 用法

  • 在 Rancher 的 Cluster Management 页面,可以添加 k8s 集群让 Rancher 托管。

    • 从托管 k8s 中移除一个节点时,需要手动清空节点上的数据,否则重新添加该节点时可能出错。参考 官方文档 (opens new window)
  • 可通过 Rancher 的 Web 表单配置 k8s ,也可以通过一个 cluster.yml 文件进行配置,如下:

    name: test-k8s                                # k8s 集群名
    rancher_kubernetes_engine_config:
      private_registries:                         # 让 Rancher 部署 k8s 组件时采用自定义的镜像仓库
        - is_default: true
          url: harbor.test.com
          # user: ***
          # password: ***
      services:                                   # 配置各个 k8s 组件
    
        kube-api:
          # event-ttl: 1h                         # event 的保留时长
          service_cluster_ip_range: 10.43.0.0/16
          service_node_port_range: 30000-32767
    
        kube-controller:
          cluster_cidr: 10.42.0.0/16              # Pod IP 的子网范围
          service_cluster_ip_range: 10.43.0.0/16  # Service IP 的子网范围
          # extra_args:                           # 添加命令行参数
          #   node-cidr-mask-size: '24'           # 每个节点的子网掩码长度
          #   terminated-pod-gc-threshold: 12500  # 如果整个 k8s 集群中 terminated pod 数量达到该阈值,则触发一次垃圾收集,删除它们
          extra_binds:
            - /etc/localtime:/etc/localtime:ro
          # node-monitor-period: 5s               # node controller 每隔多久从 apiserver 获取一次各节点 kubelet 的状态
          # node-monitor-grace-period: 40s        # 如果持续一段时间,不能获取节点的状态,则标记为 Ready=Unknown
          # pod-eviction-timeout: 5m              # 如果继续持续一段时间节点的 Ready 条件为 False 或 Unknown ,则驱逐该节点上的 Pod
    
        kubelet:
          cluster_domain: cluster.local
          cluster_dns_server: 10.43.0.10
          # fail_swap_on: true                    # 如果节点启用了 swap 内存,则拒绝启动 kubelet
          extra_args:
            max-pods: 250                         # 每个节点上最多允许部署的 Pod 数,默认为 110
            serialize-image-pulls: 'false'        # 是否同时只能拉取一个镜像,默认为 true
    
            # 节点压力驱逐
            eviction-hard: memory.available<5%,nodefs.available<5%,imagefs.available<5%,nodefs.inodesFree<5%  # 硬驱逐阈值,默认不存在
            # eviction-soft: memory.available<10%,nodefs.available<10%  # 软驱逐阈值,默认不存在
            # eviction-soft-grace-period: memory.available=1m,nodefs.available=1m,imagefs.available=1m,nodefs.inodesFree=1m   # 每个阈值持续满足一定时长之后,才触发软驱逐
            # eviction-max-pod-grace-period: 30       # 软驱逐终止 Pod 时的宽限期
            eviction-pressure-transition-period: 30s  # 每次解除节点压力的 condition 之前需要等待一定时长,避免在驱逐阈值上下波动 。默认为 5m
            # eviction-minimum-reclaim:               # 每次驱逐至少回收多少资源,避免频繁驱逐。默认为 0
            #   memory.available: 1Gi
            #   nodefs.available: 1Gi
    
            # 每隔 ContainerGCPeriod=1m 清理一次节点上的容器,删除 stopped 状态的容器
            # 每隔 ImageGCPeriod=5m 清理一次节点上的镜像
            # image-gc-high-threshold: 85             # 一个百分数。当节点的磁盘使用率达到高水位时,开始清理未被使用的 Docker 镜像,从最旧的镜像开始删除,直到磁盘使用率降至低水位
            # image-gc-low-threshold: 80
    
            # 设置每个节点保留的资源,剩下的资源才可分配给 pod
            kube-reserved: cpu=0.5,memory=1Gi                                   # 保留给 kubelet、容器运行时等 k8s 系统进程
            system-reserved: cpu=0.5,memory=1Gi,ephemeral-storage=10Gi,pid=1000 # 保留给 sshd 等系统进程
            # enforce-node-allocatable: pods          # 强行限制哪些 Cgroup 的可分配资源,默认为 pods 。可增加限制 pods,kube-reserved,system-reserved ,但可能导致系统服务被 OOM 杀死
            # kube-reserved-cgroup: /kubelet.slice    # 增加了 enforce-node-allocatable 限制时,需要声明 reserved-cgroup 。如果该 Cgroup 不存在,则需要手动创建,否则 kubelet 会启动失败
            # system-reserved-cgroup: /system.slice
    
            # 关于状态
            # node-status-update-frequency: 10s       # 每隔多久 kubelet 上报一次节点状态到 apiserver
            # global-housekeeping-interval: 1m        # 每隔多久 cAdvisor 采集一次全局监控指标,可以发现新容器
            # housekeeping-interval: 10s              # 每隔多久 cAdvisor 采集一次已有容器的监控指标
            # runtime-request-timeout: 2m             # API 请求的超时时间
            # volume-stats-agg-period: 1m             # 每隔多久计算所有 volume 的已用体积
    
  • Rancer 在 k8s 中增加了项目(project)的概念,用于对命名空间分组。

  • 可以在 Rancher 上创建多个用户,然后允许用户访问哪些下游集群、项目。

    • 这会自动在下游集群里创建用户、ClusterRoleBindings 。
    • 可以在 Rancher 网页上导出用户的 kubeconfig ,它访问 apiserver 时有两种方式:
      • 访问 Rancher 的 HTTP API ,通过 Rancher 用户认证之后,被反向代理到下游集群的 apiserver ,以 ServiceAccount 的身份进行操作。默认采用该方式,但 Rancher 可能代理耗时较久、故障。
      • 直接访问下游集群的 apiserver ,使用 k8s user 的身份,由 kube-api-auth 进行身份认证。建议采用该方式。
  • k8s 各组件之间使用自签名的 ssl 证书进行认证,每隔几个月 ssl 证书会到期,需要重启续订。

    • 在 Rancher 的 Cluster Management 网页,可选择一个集群,轮换证书(Rotate Certificates)。这会自动生成新的证书,然后重启所有 k8s 组件。
    • Rancher v2.6.3 开始,会在 ssl 证书只剩 30 天有效期时自动更新证书。
  • Rancher 创建 k8s 集群时,有多种 CNI 可选:

    • Flannel
    • Calico
    • Canal
    • Weave