kubernetes 1.23.6

# 搭建K8S集群

- 服务器要求
    - 三台服务器
        - k8s-master   192.168.8.128
        - k8s-node1    192.168.8.129
        - k8s-node2    192.168.8.130
    - 最低配置: 2核、2G内存、20G硬盘
    - 最好联网,不能联网话需要提供对应镜像私有仓库

- 软件环境
    - 操作系统: Centos7
    - Docker: 20+
    - k8s: 1.23.6 (在这个版本之上不支持Docker)

## 1. 安装步骤一: 初始化操作
    
```bash
systemctl stop firewalld
systemctl disable firewalld

# 关闭selinux,
sed -i 's/enforcing/disabled/' /etc/selinux/config
setenforce 0

# 关闭 swap, 然后重启虚拟机
swapoff -a
sed -ri 's/.*swap.*/#&/' /etc/fstab

# 根据规划设置主机名
hostnamectl set-hostname <hostname>

# 添加 hosts
cat >> /etc/hosts << EOF
192.168.8.128 k8s-master1
192.168.8.129 k8s-node1
192.168.8.130 k8s-node2
EOF

# 系统调优
cat >> /etc/security/limits.conf << EOF
* soft nofile 655360
* hard nofile 655350
* soft nproc 655350
* hard nproc 655350
* soft memlock unlimited
* hard memlock unlimited
EOF

# 升级内核更新
rpm -import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-2.el7.elrepo.noarch.rpm
yum --disablerepo="*" --enablerepo="elrepo-kernel" list available
yum -y --enablerepo=elrepo-kernel install kernel-ml.x86_64 kernel-ml-devel.x86_64
yum update -y  &&  grub2-set-default 1 && reboot
# 重启后查看内核是否是更新后的内核, uname -a

# 内核模块配置 注意: kernel < 4.19 使用 nf_conntrack_ipv4 并去除ip_vs_fo,  kernel > 4.19 使用 nf_conntrack
yum install ipvsadm ipset sysstat conntrack libseccomp -y
cat >> /etc/modules-load.d/ipvs.conf  << EOF
ip_vs
ip_vs_lc
ip_vs_wlc
ip_vs_rr
ip_vs_wrr
ip_vs_lblc
ip_vs_lblcr
ip_vs_dh
ip_vs_sh
ip_vs_fo
ip_vs_nq
ip_vs_sed
ip_vs_ftp
ip_vs_sh
nf_conntrack
ip_tables
ip_set
xt_set
ipt_set
ipt_rpfilter
ipt_REJECT
ipip
EOF

systemctl enable --now systemd-modules-load.service


# 内核参数调优
cat > /etc/sysctl.d/k8s.conf << EOF 
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
fs.may_detach_mounts = 1
vm.overcommit_memory=1
vm.panic_on_oom=0
fs.inotify.max_user_watches=89100
fs.file-max=52706963
fs.nr_open=52706963
net.netfilter.nf_conntrack_max=2310720

net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_keepalive_intvl =15
net.ipv4.tcp_max_tw_buckets = 36000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_max_orphans = 327680
net.ipv4.tcp_orphan_retries = 3
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.ip_conntrack_max = 65536
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.tcp_timestamps = 0
net.core.somaxconn = 16384
EOF

sysctl --system   # 生效


# 时间同步。 写入计划任务比较好
yum install ntpdate -y
ntpdate time.windows.com

2. 安装步骤二: 安装基础软件(所有节点)

# 安装 Docker
yum remove docker \
                docker-client \
                docker-client-latest \
                docker-common \
                docker-latest \
                docker-latest-logrotate \
                docker-logrotate \
                docker-selinux \
                docker-engine-selinux \
                docker-engine
                    
yum install   wget   -y
wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo
yum list docker-ce --showduplicates|grep "^doc"|sort -r
yum -y install docker-ce-20.10.23-3.el7
cat > /etc/docker/daemon.json << EOF
{
  "registry-mirrors": ["https://c1ncp8uc.mirror.aliyuncs.com"],
  "exec-opts": ["native.cgroupdriver=systemd"]
}
EOF

systemctl daemon-reload
systemctl enable docker
systemctl start docker
docker --version


# 添加阿里云 yum 源
cat > /etc/yum.repos.d/kubernetes.repo << EOF
[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

# 安装 kubeam、kubelet、kubectl
yum install -y kubelet-1.23.6 kubeadm-1.23.6 kubectl-1.23.6
systemctl enable kubelet
systemctl start kubelet

3. 安装步骤三: 部署 Kubernetes Master

# 在 Master 节点下执行
kubeadm init \
--apiserver-advertise-address=192.168.8.128 \
--image-repository registry.aliyuncs.com/google_containers \
--kubernetes-version v1.23.6 \
--service-cidr=10.96.0.0/12 \
--pod-network-cidr=10.244.0.0/16

# 或者把初始化参数导出成配置文件: kubeadm config print init-defaults > kubeadm-calico.conf
# 查看一下对应的镜像版本,确定配置文件是否生效: kubeadm config images list --config kubeadm-calico.conf
# 确认没问题之后我们直接拉取镜像:    kubeadm config images pull --config kubeadm-calico.conf
# 初始化:    kubeadm init --config kubeadm-calico.conf


# 初始化安装成功后,复制如下配置并执行,会生成一个加入集群的命令
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config


# 如果初始化失败后,一般是 kubelet 问题,查错误日志: journalctl -xefu kubelet
# 解决之后,再重置下:  kubeadm reset , 然后再初始化

# 查看节点
kubectl get no

4. 安装步骤四: 加入 Kubernetes Node

# 如果 token 已经过期,就重新申请
kubeam token create --print-join-command

# token 没有过期忘记了。可以通过如下命令获取
kubeadm token list    # 获取 token 名字
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt |openssl rsa -pubin -outform der 2>/dev/null |openssl dgst -sha256 -hex |sed 's/^.* //'   # 获取 hash 值

kubeadm join <address>:<port> --token <token_name>  --discovery-token-ca-cert-hash sha256:<Hash>
# 查看机器状态
[root@k8s-master1 ~]# kubectl get no -o wide
NAME          STATUS     ROLES                  AGE     VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION              CONTAINER-RUNTIME
k8s-master1   NotReady   control-plane,master   13m     v1.23.6   192.168.8.128   <none>        CentOS Linux 7 (Core)   6.4.1-1.el7.elrepo.x86_64   docker://20.10.24
k8s-node1     NotReady   <none>                 3m19s   v1.23.6   192.168.8.129   <none>        CentOS Linux 7 (Core)   6.4.1-1.el7.elrepo.x86_64   docker://20.10.23
k8s-node2     NotReady   <none>                 17s     v1.23.6   192.168.8.130   <none>        CentOS Linux 7 (Core)   6.4.1-1.el7.elrepo.x86_64   docker://20.10.23

[root@k8s-master1 ~]# kubectl get cs
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS    MESSAGE                         ERROR
controller-manager   Healthy   ok                              
etcd-0               Healthy   {"health":"true","reason":""}   
scheduler            Healthy   ok    

[root@k8s-master1 ~]# kubectl get po -n kube-system
NAME                                  READY   STATUS    RESTARTS   AGE
coredns-6d8c4cb4d-7cb48               0/1     Pending   0          18m    # 一般是因为网络的问题,所以一直 Pending
coredns-6d8c4cb4d-vp9hb               0/1     Pending   0          18m
etcd-k8s-master1                      1/1     Running   0          18m
kube-apiserver-k8s-master1            1/1     Running   0          18m
kube-controller-manager-k8s-master1   1/1     Running   0          18m
kube-proxy-f4clb                      1/1     Running   0          18m
kube-proxy-jbgm7                      1/1     Running   0          7m42s
kube-proxy-nqm7d                      1/1     Running   0          4m40s
kube-scheduler-k8s-master1            1/1     Running   0          18m

5. 安装步骤五:部署 CNI 网络插件

https://docs.tigera.io/archive/ 选择与 kubernetes 对应的版本 https://docs.tigera.io/archive/v3.23/getting-started/kubernetes/quickstart 这里使用: Tigera 操作員來安裝 Calico

# 在 Master 节点执行
cd /opt
curl -O https://projectcalico.docs.tigera.io/archive/v3.23/manifests/tigera-operator.yaml
curl -O https://projectcalico.docs.tigera.io/archive/v3.23/manifests/custom-resources.yaml

# 搜索 CALICO_IPV4POOL_CIDR ,修改为 kubeadm 初始化对应的 --pod-network-cidr=10.244.0.0/16 地址
vi custom-resources.yaml
      cidr: 10.244.0.0/16
             

# 部署
kubectl create -f ./tigera-operator.yaml -f ./custom-resources.yaml
# 删除 kubectl delete -f calico.yaml

# 等到每個 pod 都有`STATUS`的`Running`
watch kubectl get pods -n calico-system
  • 效果

[root@k8s-master1 opt]# kubectl get po -A
NAMESPACE          NAME                                       READY   STATUS    RESTARTS   AGE
calico-apiserver   calico-apiserver-7f8fcc8dcb-vtqrh          1/1     Running   0          5m7s
calico-apiserver   calico-apiserver-7f8fcc8dcb-xxbp4          1/1     Running   0          5m7s
calico-system      calico-kube-controllers-7b66454db8-xgsm8   1/1     Running   0          9m11s
calico-system      calico-node-4lt8d                          1/1     Running   0          9m12s
calico-system      calico-node-h6msh                          1/1     Running   0          9m12s
calico-system      calico-node-xcbtf                          1/1     Running   0          9m12s
calico-system      calico-typha-79659df74c-l6nk2              1/1     Running   0          9m12s
calico-system      calico-typha-79659df74c-w68jf              1/1     Running   0          9m12s
kube-system        coredns-6d8c4cb4d-7cb48                    1/1     Running   0          136m
kube-system        coredns-6d8c4cb4d-vp9hb                    1/1     Running   0          136m
kube-system        etcd-k8s-master1                           1/1     Running   0          136m
kube-system        kube-apiserver-k8s-master1                 1/1     Running   0          136m
kube-system        kube-controller-manager-k8s-master1        1/1     Running   0          136m
kube-system        kube-proxy-f4clb                           1/1     Running   0          136m
kube-system        kube-proxy-jbgm7                           1/1     Running   0          125m
kube-system        kube-proxy-nqm7d                           1/1     Running   0          122m
kube-system        kube-scheduler-k8s-master1                 1/1     Running   0          136m
tigera-operator    tigera-operator-7885599d97-ndwr4           1/1     Running   0          9m51s
[root@k8s-master1 opt]# kubectl get no -o wide
NAME          STATUS   ROLES                  AGE    VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION              CONTAINER-RUNTIME
k8s-master1   Ready    control-plane,master   137m   v1.23.6   192.168.8.128   <none>        CentOS Linux 7 (Core)   6.4.1-1.el7.elrepo.x86_64   docker://20.10.24
k8s-node1     Ready    <none>                 126m   v1.23.6   192.168.8.129   <none>        CentOS Linux 7 (Core)   6.4.1-1.el7.elrepo.x86_64   docker://20.10.23
k8s-node2     Ready    <none>                 123m   v1.23.6   192.168.8.130   <none>        CentOS Linux 7 (Core)   6.4.1-1.el7.elrepo.x86_64   docker://20.10.23
  • calicoctl是用来查看管理calico的命令行工具,定位上有点类似于calico版本的kubectl (可不安装)

  • 参考: https://docs.tigera.io/archive/v3.23/maintenance/clis/calicoctl/install

6. 测试 Kubernetes 集群

# 创建部署
kubectl create deploy nginx --image=nginx

# 暴露端口
kubectl expose deploy nginx --port=80 --type=NodePort

# 查看 pod 以及服务信息
kubectl get pod,svc

# 访问节点 http://<IP>:<PORT>

7. 命令行工具 kubectl

  • api-server 只有 master 上可以执行,为了让所有节点都可以与 api-server 通信

# 1. 将 master 节点中 /etc/kubernetes/admin.conf 拷贝到需要运行的服务器的 /etc/kubernetes 目录中
scp /etc/kubernetes/admin.conf root@k8s-node1:/etc/kubernetes


# 2. 在对应的服务器上配置环境变量
echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile
source ~/.bash_profile

# 3. 在 node 节点测试命令
kubectl get no

K8S的资源清单

必须存在的属性

    | 参数名 | 字段类型 | 说明  |
    | --- | --- | --- |
    | version | String | k8s的api版本,目前基本时v1,可以用kubectl api-versions命令查询 |
    | kind | String | 指yaml文件定义的资源类型和角色,比如Pod |
    | metadata | Object | 元数据对象,固定值写metadata |
    | metadata.name | String | 元数据对象的名称,由我们编写,比如Pod的名字 |
    | metadata.namespace | String | 元数据对象的命名空间,由我们自身定义 |
    | spec | Object | 详细定义对象,固定值写spec |
    | spce.containers\[\] | list | spec对象的容器列表 |
    | spce.containers\[\].name | String | 定义容器的名称 |
    | spce.containers\[\].image | String | 定义容器的镜像 |

主要对象

    | 参数名 | 字段类型 | 说明  |
    | --- | --- | --- |
    | spce.containers\[\].name | String | 定义容器的名称 |
    | spce.containers\[\].image | String | 定义容器的镜像 |
    | spce.containers\[\].imagePullPolicy | String | 镜像的拉取策略  <br>Always:每次都拉取新的镜像  <br>Never:仅使用本地镜像,从不拉取  <br>IfNotPresent:如果本地有就使用本地镜像,没有则拉取,不写默认Always  <br>建议使用IfNotPresent,因为docker中latest不是固定值,经常变 |
    | spce.containers\[\].command\[\] | list | 容器的启动命令,因为是数组可以指定多个,不指定则使用镜像打包中的命令 |
    | spce.containers\[\].args\[\] | list | 容器启动命令的参数,因为是数组可以指定多个 |
    | spce.containers\[\].workingDir | String | 容器的工作目录 |
    | spce.containers\[\].volumeMounts\[\] | list | 容器内部存储卷位置 |
    | spce.containers\[\].volumeMounts\[\].name | String | 容器挂载的存储卷名称 |
    | spce.containers\[\].volumeMounts\[\].mountPath | String | 容器挂载的存储卷路径 |
    | spce.containers\[\].volumeMounts\[\].readOnly | String | 容器挂载的存储卷读写模式,true或false,默认true只读 |
    | spce.containers\[\].ports\[\] | list | 容器用到的端口列表 |
    | spce.containers\[\].ports\[\].name | String | 指定端口名称 |
    | spce.containers\[\].ports\[\].containerPort | String | 指定容器需要监听的端口 |
    | spce.containers\[\].ports\[\].hostPort | String | 指定容器所在主机需要监听的端口号,默认和containerPort相同,注意设置了hostPort同一台主机无法启动该容器的形同副本(因为主机的关端口号不能相同,这样会冲突) |
    | spce.containers\[\].ports\[\].protocol | String | 指定端口的协议,支持TCP和UDP,默认TCP |
    | spce.containers\[\].env\[\] | list | 指定容器运行前所需设置的环境变量列表 |
    | spce.containers\[\].env\[\].name | String | 环境变量名称 |
    | spce.containers\[\].env\[\].value | String | 指定环境变量值 |
    | spce.containers\[\].resources | Object | 资源限制和资源请求的值 |
    | spce.containers\[\].resources.limits | Object | 容器运行时资源上限(运行容器所能用的最大资源) |
    | spce.containers\[\].resources.limits.cpu | String | CPU的限制,单位为core数,将用于docker run --CPU-shares参数 |
    | spce.containers\[\].resources.limits.memory | String | 内存限制,单位为MIB、GIB |
    | spce.containers\[\].resources.requests | Object | 容器启动和调度的限制(运行容器所需最小资源) |
    | spce.containers\[\].resources.requests.cpu | String | CPU的限制,单位为core数 |
    | spce.containers\[\].resources.requests.memory | String | 内存限制,单位为MIB、GIB |
    

额外参数

    | 参数名 | 字段类型 | 说明  |
    | --- | --- | --- |
    | spec.restartPolicy | String | Pod的重启策略  <br>Always:Pod一旦终止运行,无论容器时如何终止的,kubelet服务都将它重启,默认  <br>Onfailure:只有Pod以非零退出吗终止时,kubelet才会重启该容器,正常结束退出码为0  <br>Never:Pod终止后,kubelet将退出码报告给master不会重启 |
    | spec.nodeSelector | Object | 定义Node的label过滤标签,以key:value格式指定 |
    | spec.imagePullSecrets | Object | 定义pull镜像时使用的secret名称,以name:secretkey格式指定 |
    | spec.hostNetwork | boolean | 定义是否使用主机网络模式,默认false,设置true表示使用宿主机网络  <br>不使用docker网桥,同时设置了true将无法在同一台宿主机上启动第二个副本 |
    
  • version说明

# 查看当前k8s支持的version 
kubectl api-versions 

# 查看某个资源支持的版本 比如Pod 
kubectl explain pod 或者 kubectl explain pod.apiVersion(可在FIELDS选择字段中选择具体字段,详细查看) 
KIND: Pod 
VERSION: v1

深入Pod

1. 配置文件

# vim 手🦌一个配置 Pod 文件
[root@k8s-master1 ~]# cat nginx-demo.yaml 
apiVersion: v1 # api 文档版本
kind: Pod      # 资源对象类型,也可以配置为 Deployment、Statefulset 这一类的对象
metadata:      # Pod 相关的元数据,类似表述 Pod 的数据
  name: nginx-demo  # Pod 名称
  labels:      # 定义 Pod 标签
    type: app  # 自定义 label 标签, k 为 type, v 为app
    version: 1.0.0      # 自定义 label 标签, Pod 版本号
  namespace: 'default'  # 命名空间的配置
spec:           # 期望Pod按照这里面的描述进行创建containers: #对于Pod中的容器描述
  containers:   # 对于Pod中的容器描述
  - name: nginx #容器的名称
    image: nginx:1.7.9 #指定容器的镜像
    imagePullPolicy: IfNotPresent #镜像拉取策略,指定如果本地有就用本地的,如果没有就拉取远程的
    command: # 指定容器启动时执行的命令
    - nginx
    - -g
    - 'daemon off;'  # nginx -g 'daemon off; '
    workingDir: /usr/share/nginx/html # 定义容器启动后的工作目录
    ports:
    - name: http        # 端口名称
      containerPort: 80 # 描述容器内要暴露什么端口
      protocol: TCP     # 描述该端口是基于哪利协议通信的
    env:                # 坏境变量
    - name: JVM_OPTS    # 环境变量名称
      value: '-Xms128m -Xmx128m' # 环境变量的值
    resources:
      requests:             # 最少需要多少资源
        cpu: 100m           # 限制cpu最少使用0.1个核心
        memory: 128Mi       # 限制内存最少使用128兆
      limits:               # 最多可以用多少资源
        cpu: 200m           # 限制cpu最多使用0.2个核心
        memory: 256Mi       # 限制最多使用256兆
  restartPolicy: OnFailure  # 重启策略,只有失败的情况才会重启
[root@k8s-master1 ~]# kubectl create -f nginx-demo.yaml 
pod/nginx-demo created

[root@k8s-master1 ~]# watch kubectl get po
# IP 地址分配的是 提前定义要的 pod 地址段里
[root@k8s-master1 ~]# kubectl get po nginx-demo -o wide
NAME         READY   STATUS    RESTARTS   AGE   IP               NODE        NOMINATED NODE   READINESS GATES
nginx-demo   1/1     Running   0          27m   10.244.169.131   k8s-node2   <none>           <none>


[root@k8s-master1 ~]# route -n
......
10.244.169.128  192.168.8.130   255.255.255.192 UG    0      0        0 ens33    # 10.244.169.0 段落
......

2. 探针

- 探针类型
    - StartupProbe (启动探针)
        - 当配置了 startupProbe后,会先禁用其他探针, 直到 startupProbe成功后,其他探针才会继续。
    - LivenessProbe (存活探针)
        - 用于探测容器中的应用是否运行,如果探测失败,kubelet 会根据配置的重启策略进行重启,若没有配置,默认就认为容器自动成功,不会执行重启策略。
    - ReadinessProbe (健康探针)
        - 用于探测容器内的程序是否健康,它的返回值如果返回success, 那么就队为该容器已经完全启动,并且该容器是可以接收外部流量的。

- 探针探测方式
    - ExecAction
        - 在容器内部执行一个命令,如果返回值为0,则任务容器时健康的。
    - TCPSocketAction
        - 通过tcp连接监测容器内端口是否开放。如果开放则证明该容器健康
    - HTTPGetAction
        - 生产环境用的较多的方式,发送HTTP请求到容器内的应用程序,如果接口返回的状态码在200~400之间,则认为容器健康。

- 参数配置
    - initialDelaySeconds: 60 # 初始化时间
    - timeoutSecords: 2         # 超时时间
    - periodSeconds: 5          # 监测间隔时间
    - successThreshold: 1    # 检查1次成功就表示成功
    - failureThreshold:2        # 监测失败2次就表示失败

StartupProbe 探针

# 基于上面创建 pod 方式, 测试:  StartupProbe 启动探针、livenessProbe 存活探针、ReadinessProbe 就绪探针
...
    imagePullPolicy: IfNotPresent #镜像拉取策略,指定如果本地有就用本地的,如果没有就拉取远程的
    startupProbe:   # 应用启动探针配置
      #httpGet:     # 探测方式,基于 http 请求探测
      #  path: /index.html
      tcpSocket:   # 探测方式,基于 tcp 请求探测 
        port: 80   # 请求端口
      #exec:       # 探测方式,基于 cmd 请求探测
      #  command:
      #  - sh
      #  - -c 
      #  - "sleep3; echo success > /inited"
      failureThreshold: 3 # 失败多少次才算真正的失败
      periodSeconds: 10   # 间隔时间
      successThreshold: 1 # 多少次监测成功算成功
      timeoutSeconds: 5   # 请求的超时时间
    livenessProbe:   # 应用存活探针配置
      httpGet:       # 探测方式,基于 http 请求探测
        path: /index.html
        port: 80
      failureThreshold: 3 # 失败多少次才算真正的失败
      periodSeconds: 10   # 间隔时间
      successThreshold: 1 # 多少次监测成功算成功
      timeoutSeconds: 5   # 请求的超时时间
    readinessProbe:   # 应用就绪探针配置
      httpGet:        # 探测方式,基于 http 请求探测
        path: /index.html
        port: 80
      failureThreshold: 3 # 失败多少次才算真正的失败
      periodSeconds: 10   # 间隔时间
      successThreshold: 1 # 多少次监测成功算成功
      timeoutSeconds: 5   # 请求的超时时间
...
[root@k8s-master1 ~]# kubectl describe po nginx-demo | grep -E "Liveness|Readiness|Startup"
    Liveness:   http-get http://:80/index.html delay=0s timeout=5s period=10s #success=1 #failure=3     # 其次:存活检测
    Readiness:  http-get http://:80/index.html delay=0s timeout=5s period=10s #success=1 #failure=3     # 其次:就绪检测
    Startup:    tcp-socket :80 delay=0s timeout=5s period=10s #success=1 #failure=3                     # 首先: 启动检测

3. 生命周期

# 基于上面创建 pod 方式, 测试:生命周期
...略
spec:           # 期望Pod按照这里面的描述进行创建containers: #对于Pod中的容器描述
  terminationGracePeriodSeconds: 30 # 当pod被删除时,给这个pod宽限多长时间                                  
  containers:   # 对于Pod中的容器描述
  - name: nginx #容器的名称
    image: nginx:1.7.9 #指定容器的镜像
    imagePullPolicy: IfNotPresent #镜像拉取策略,指定如果本地有就用本地的,如果没有就拉取远程的
    lifecycle:   # 生命周期的配置
      postStart: # 生命周期启动阶段做的事情,不一定在容器的前运行
        exec:
          command:
          - sh
          - -c
          - "echo '<h1>pre stop<h1>' > /usr/share/nginx/html/prestop.httml"
      preStop:
        exec:
          command:
          - sh
          - -c
          - "sleep 50; echo 'sleep finished...' >> /usr/share/nginx/html/prestop.html"
...略

资源调度

Lable 和 Selector

kubectl get po -l type=app     # 搜索标签 type=app
kubectl get po -A -l type=app # 搜索所有命名空间标签 type=app
kubectl get po -A -l type=app --show-labels # 搜索 type=app, 并显示详细标签信息

kubect get po -l "type in (app,app1,app2)"  # 搜索标签 type 包含其中一个值标签
kubect get po -l version!=1.0.0,type=app    # 搜索多标签 version!=1.0.0,type=app

Deployment 无状态

  • 创建

kubectl create deploy nginx --image=nginx

[root@k8s-master1 ~]# kubectl get deploy,rs,po -l app=nginx --show-labels
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE     LABELS
deployment.apps/nginx   1/1     1            1           6h28m   app=nginx

NAME                               DESIRED   CURRENT   READY   AGE     LABELS
replicaset.apps/nginx-85b98978db   1         1         1       6h28m   app=nginx,pod-template-hash=85b98978db

NAME                         READY   STATUS    RESTARTS   AGE    LABELS
pod/nginx-85b98978db-259bm   1/1     Running   0          3h7m   app=nginx,pod-template-hash=85b98978db



[root@k8s-master1 ~]# kubectl get deploy nginx -o yaml > nginx-deploy.yaml

# 修改如下
apiVersion: apps/v1        # deployment api版本
kind: Deployment           # 资源类型为deployment
metadata:                  # 元信息
  labels:                  # 标签
    app: nginx-deploy      # 具体的 key: value配置形式
  name: nginx-deploy       # deployment的名字
  namespace: default       # 所在的命名空间
spec:
  replicas: 1              # 期望副本数
  revisionHistoryLimit: 10 # 进行滚动更新后,保留的历史版本数
  selector:                # 选择器,用于找到匹配的RS
    matchLabels:           # 按照标签匹配
      app: nginx-deploy    # 匹配的标签
  strategy:                # 更新策略
    rollingUpdate:         # 滚动更新配HI
      maxSurge: 25%        # 进行滚动更新时,更新的个数最多可以超过期望副本数的个数/比例
      maxUnavailable: 25%  # 进行滚动更新时,最大不可用比例更新比例,表示在所有副本数中,最多可以有多少个不更新成功
    type: RollingUpdate    # 更新类型,采用滚动更新
  template:                # pod模板
    metadata:              # pod 的元信息
      labels:              # pod的标签
        app: nginx-deploy
    spec:                  # pod期望信息
      containers:          # pod的容器
      - image: nginx:1.7.9 # 镜像
        imagePullPolicy: Always   # 重启策略
        name: nginx        # 容器名称
      restartPolicy: Always # 删除操作最多宽限多长时间■


[root@k8s-master1 ~]# kubectl delete deploy nginx # 删除临时
[root@k8s-master1 ~]# kubectl create -f nginx-deploy.yaml # 使用 yml 文件的方式创建 deploy 
  • 滚动更新

    • 由于当前副本数只有一个,我们扩容一下

    • 只有修改 容器部分 containers ,才会滚动更新

# 扩容不是滚动更新
kubectl edit deploy nginx-deploy
     replicas: 3              # 期望副本数
     
# 修改下镜像,保存执行滚动更新
kubectl edit deploy nginx-deploy         # 或者命令方式 kubectl set image deployment/nginx-deploy nginx=nginx:1.8
      - image: nginx:1.8 # 修改下镜像
      
# 观察自滚动更新状态
[root@k8s-master1 ~]# kubectl rollout status deploy nginx-deploy
Waiting for deployment "nginx-deploy" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deploy" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deploy" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deploy" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deploy" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deploy" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deploy" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "nginx-deploy" rollout to finish: 1 old replicas are pending termination...
deployment "nginx-deploy" successfully rolled out
  • 回归版本

    • 比如操作失误,导致更新的pod无法启动,那么我们想回滚到上一个版本

# 查看更新的历史版本
kubectl rollout history deploy nginx-deploy

#  查看历史版本号的详细信息
kubectl rollout history deployment/nginx-deploy --revision=<REVISION>

# 回滚到历史版本号
kubectl rollout undo deployment/nginx-deploy --to-revision=<REVISION>
  • 扩容缩容

kubectl scale --replicas=6 deploy nginx-deploy    # 扩容到 6个 pod, template 没有改变,不会更改 rs
kubectl scale --replicas=3 deploy nginx-deploy    # 缩容到 3个 pod
  • 滚动更新的暂停和恢复

    • 常用于增加pod或者服务器的时候

kubectl rollout pause deploy nginx-deploy   # 滚动更新暂停

# 修改之后: 不会自动更新,只有恢复的时候才会更新

kubectl rollout resume deploy nginx-deploy  # 滚动更新恢复

StatefulSet 有状态

  • 创建

    • 由于没有 存储卷,这里就不设置了

# statefulset.yaml 
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet # StatefulSet类型的资源
metadata:
  name: web
spec:
  serviceName: "nginx" # 使用哪个service来管理
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports: # 容器内部要暴露的端口
        - containerPort: 80 # 具体暴露的端口号
          name: web         # 该端口配置的名字
 #       volumeMounts:       # 加教数据卷
 #       - name: www         # 指定加载哪个数据卷
 #         mountPath: /usr/share/nginx/html # 加载到容器中的哪个目录
 # volumeClaimTemplates:     # 数据卷模板
 # - metadata:               # 数据卷描述
 #     name: www             # 数据卷的名称
 #     annotations:          # 数据卷的注解
 #       volume.alpha.kubernetes.io/storage-class: anything  
 #   spec: # 数据卷的规约
 #     accessModes: ["ReadWriteOnce"] # 访问模式
 #     resources:
 #       requests:
 #         storage: 1Gi # 需要1G的存储资源
[root@k8s-master1 ~]# kubectl create  -f statefulset.yaml 
service/nginx created
statefulset.apps/web created

[root@k8s-master1 ~]# kubectl get sts,svc,pod
NAME                   READY   AGE
statefulset.apps/web   2/2     9s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   12h
service/nginx        ClusterIP   None         <none>        80/TCP    9s

NAME        READY   STATUS    RESTARTS   AGE
pod/web-0   1/1     Running   0          9s
pod/web-1   1/1     Running   0          7s
  • 扩容和缩容

[root@k8s-master1 ~]# kubectl scale sts web --replicas=4                    # 命令方式
statefulset.apps/web scaled

[root@k8s-master1 ~]# kubectl patch sts web -p '{"spec":{"replicas": 32}}'  # json 方式
statefulset.apps/web patched

[root@k8s-master1 ~]# kubectl get sts
NAME   READY   AGE
web    2/2     9m51s
  • 滚动更新

镜像更新(目前还不支持直接更新image,需要patch来间接实现) 和 deployment 类似

- StatefulSet也可以采用滚动更新策略,同样是修改pod 
- template属性后会触发更新,但是由于pod是有序的,在 StatefulSet中更新时是基于pod的顺序倒序更新的
kubectl patch sts web --type='json' -p='[{"op": "replace","path": "/spec/template/spec/containers/0/image", "value": "nginx:1.8"}]'
  • 灰布发布/金丝雀发布

    • 目标: 将项目上线后产生问题的影响,尽量降到最低

    • 过程: 先更新一部分,如果正常没有问题了,再逐渐更新

利用滚动更新中的 partition 属性,可以实现简易的灰度发布的效 例如我们有5个pqd.如果当前 partition 设置为3,那么此时滚动更新时,只会更新那些序号 >= 3的pod 利用该机制,我们可以通过控制partition的值,来决定只更新其中一部分pod,确认没有问题后再主健增大更新的pod数量,最终实现全部pod更新

[root@k8s-master1 ~]# kubectl patch sts web -p '{"spec":{"replicas": 5}}'
statefulset.apps/web patched

[root@k8s-master1 ~]# kubectl get po
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          25m
web-1   1/1     Running   0          25m
web-2   1/1     Running   0          3m8s
web-3   1/1     Running   0          3m6s
web-4   1/1     Running   0          21s

[root@k8s-master1 ~]# kubectl describe po web-4 |grep "Image:"
    Image:          nginx:1.8
[root@k8s-master1 ~]# kubectl describe po web-0 |grep "Image:"
    Image:          nginx:1.8
    
[root@k8s-master1 ~]# kubectl edit sts web        # 修改,实现金丝雀发布,只更新一部分,这里设置只更新三个
      partition: 3
      
      
[root@k8s-master1 ~]# kubectl describe po web-0 |grep "Image:"    # <3 的未更新
    Image:          nginx:1.8
[root@k8s-master1 ~]# kubectl describe po web-3 |grep "Image:"    # 发现只更新了一小部分 >=3 的pod
    Image:          nginx:1.7.9
    
    
# 最后完全没问题了,再 edit sts ,把 partition 修改为 0
  • 删除更新

只有删除 Pod 的时候才实现更新

[root@k8s-master1 ~]# kubectl edit sts web
  updateStrategy:
    #rollingUpdate:
    #  partition: 3
    #type: RollingUpdate
    type: OnDelete
    
# 修改镜像版本,删除 Pod, 查看是否只更新了删除后的Pod
  • 级联删除和非级联删除

# 删除 statefulset 和 Headless Service
# 级联删除: 删除statefulset 时会同时删除 pods
kubectl delete statefulset web
kubectl delete svc nginx

# 非级联删除: 删除statefulset 时不会删除 pods, 删除sts盾,pod :
kubect1 delete sts web --cascade-false
  • 删除PVC

# statefulset删除層PVC还会保留响,效摇不再使用的增也得要删除
kubectl delete pvc www-web-0 ww-web-1

DaemonSet 守护进程

为每一个匹配的Node都部署-一个守护进程

  • 配置文件

[root@k8s-master1 ~]# kubectl  label no k8s-node1 type=microservices     # 添加一个标签,让守护进程在此标签下运行
node/k8s-node1 labeled
# daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
spec:
  selector:
    matchLabels:
      app: logging
  template:
    metadata:
      labels:
        app: logging
        id: fluentd
      name: fluentd
    spec:
      nodeSelector:
        type: microservices
      containers:
      - name: fluentd-es
        image: agilestacks/fluentd-elasticsearch:v1.3.0
        env: #env:#环境变量配肾
        - name: FLUENTD_ARGS # 环境变量的key
          value: "-qq" # 环境变量的value
        volumeMounts:  # 加载数据卷,避免数据丢失
        - name: containers # 数据卷的名字
          mountPath: /var/lib/docker/containers  # 将数据卷挂载到容器内的哪个日录
        - name: varlog
          mountPath: /varlog
      volumes:      # 定义数据卷
        - hostPath: # 数据卷类型,主机路径的模式,也就是与node共享目录
            path: /var/lib/docker/containers # node 中的共享目录
          name: containers # 定义的数据卷的名称
        - hostPath:
            path: /var/log
          name: varlog
[root@k8s-master1 ~]# kubectl create -f daemonset.yaml 
daemonset.apps/fluentd created

[root@k8s-master1 ~]# kubectl get ds -o wide
NAME      DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR        AGE     CONTAINERS   IMAGES                                     SELECTOR
fluentd   1         1         1       1            1           type=microservices   4m54s   fluentd-es   agilestacks/fluentd-elasticsearch:v1.3.0   app=logging

[root@k8s-master1 ~]# kubectl get po app=logging -o wide      # 只在 node 节点标签 type=microservices 上
NAME            READY   STATUS    RESTARTS   AGE     IP               NODE        NOMINATED NODE   READINESS GATES
fluentd-tpsg5   1/1     Running   0          5m13s   10.244.36.94     k8s-node1   <none>           <none>
  • 滚动更新

只有删除的时候才更新

# 不建议使用RollingUpdate.建议使用OnDelete模式,这样避免频繁更新id
[root@k8s-master1 ~]# kubectl edit ds fluentd
  updateStrategy:
    type: OnDelete

HPA

Pod自动扩容: 可以根据CPU使用率或自定义指标(metrics)

  • ●控制管理器每隔30s (可以通过-horizontal-pod-autoscaler-sync-period惨改)查询metrics的资源使用情况

  • ●支持三种metrics类型

    • 。预定义metrics (比如Pod的CPU) 以利用率的方式计算

    • 。自定义的Pod metrics, 以原始值(raw value) 的方式计算

    • 。自定义的object metrics

  • ●支持两种metrics查询方式: Heapster和白定义的REST API

  • ●支持多metrics

  • HPA 开启指标服务

# 下载metrics-server组件配置文件
wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml -O metrics-server-components.yaml

# #修改镜像地址为国内的地址
sed -i 's#registry.k8s.io/metrics-server#registry.cn-hangzhou.aliyuncs.com/google_containers#g' metrics-server-components.yaml

# 修改容器的tls配置,不验证tls,在containers的args参数中增加 `--kubelet-insecure-tls` 参数
[root@k8s-master1 ~]# kubectl create -f metrics-server-components.yaml 

[root@k8s-master1 ~]# kubectl get po -A |grep metrics
kube-system        metrics-server-5d6946c85b-zhdfj            1/1     Running   0               117s
  • CPU、内存指标监控

    • 实现cpu或内存的监控,首先有个前提条件是该对象必须配置了 resources.requests.cpu 或** resources.requests.memory** 才可以,

    • 可以配置当cpu/memory达到上述配置的百分比后进行扩容或缩容

    • 创建一个HPA:流程:

      • 1.先准备好一个有做资源限制的deployment、service

      • 2.执行命令 kubectl autoscale deploy <deploy. name> --cpu-percent= 20 --min=2 --max=5

      • 3.通过kubectl get hpa可以获取HPA信息

# 1.先准备-个好一个有做资源限制的deployment
[root@k8s-master1 ~]# cat nginx-demo.yaml 
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  labels:
    app: nginx
spec:
  selector:
    app: nginx-deploy
  ports:
  - port: 80
    targetPort: 80
    name: web
  type: NodePort
---
apiVersion: apps/v1        # deployment api版本
kind: Deployment           # 资源类型为deployment
metadata:                  # 元信息
  labels:                  # 标签
    app: nginx-deploy      # 具体的 key: value配置形式
  name: nginx-deploy       # deployment的名字
  namespace: default       # 所在的命名空间
spec:
  replicas: 1              # 期望副本数
  revisionHistoryLimit: 10 # 进行滚动更新后,保留的历史版本数
  selector:                # 选择器,用于找到匹配的RS
    matchLabels:           # 按照标签匹配
      app: nginx-deploy    # 匹配的标签
  strategy:                # 更新策略
    rollingUpdate:         # 滚动更新配HI
      maxSurge: 25%        # 进行滚动更新时,更新的个数最多可以超过期望副本数的个数/比例
      maxUnavailable: 25%  # 进行滚动更新时,最大不可用比例更新比例,表示在所有副本数中,最多可以有多少个不更新成功
    type: RollingUpdate    # 更新类型,采用滚动更新
  template:                # pod模板
    metadata:              # pod 的元信息
      labels:              # pod的标签
        app: nginx-deploy
    spec:                  # pod期望信息
      containers:          # pod的容器
      - image: nginx:1.7.9 # 镜像
        imagePullPolicy: Always   # 重启策略
        name: nginx        # 容器名称
        resources:
          requests:             # 最少需要多少资源
            cpu: 100m           # 限制cpu最少使用0.1个核心
            memory: 128Mi       # 限制内存最少使用128兆
          limits:               # 最多可以用多少资源
            cpu: 200m           # 限制cpu最多使用0.2个核心
            memory: 256Mi       # 限制最多使用256兆
      restartPolicy: Always # 删除操作最多宽限多长时间■
# 2.执行命令 `kubectl autoscale deploy <deploy. name> --cpu-percent= 20 --min=2 --max=5`
[root@k8s-master1 ~]# kubectl autoscale deploy nginx-deploy --cpu-percent=20 --min=2 --max=5
horizontalpodautoscaler.autoscaling/nginx-deploy autoscaled

# 3.通过`kubectl get hpa`可以获取HPA信息
[root@k8s-master1 ~]# kubectl get hpa
NAME           REFERENCE                 TARGETS         MINPODS   MAXPODS   REPLICAS   AGE
nginx-deploy   Deployment/nginx-deploy   <unknown>/20%   2         5         2          111s

[root@k8s-master1 ~]# kubectl top pods
NAME                           CPU(cores)   MEMORY(bytes)           
nginx-deploy-bc96d8cd9-74ms5   0m           1Mi             
nginx-deploy-bc96d8cd9-khc68   0m           1Mi             
# 测试:找到对应服务的service, 编写循环测试脚本提升内存与cpu负载,可以通过多台机器执行上述命令,增加负载,当超过负载后可以查看pods的打容情况  
while true; do wget -q -O- http://<ip:port> > /dev/nul; done

# 查看pods资源使用情况
kubectl get pods
kubectl top pods 
kubectl get hpa -w
kubectl get deploy -w
  • 自定义 metrics

    • ●控制管理器开启-horizontal-pod-autoscaler-use-rest-clients

    • ●控制管理器的--apiserver指向 API Server Aggregator

    • ●在 API Server Aggregator 中注册自定义的metrics API

服务发布-services

配置与基础命令

# 配置详解
apiVersion: v1
kind: Service # 资源类型为Service
metadata:
  name: nginx-svc # Service 名字
  labels:
    app: nginx    # Service 自己本身的标签
spec:
  selector:       # 匹配哪些pod 会被该service 代理
    app: nginx-deploy  # 所有匹配到这些标签的pod 都可以通过该service 进行访问
  ports:               # 端口映射
  - port: 80           # service自己的端口,在使用内网ip访问时使用
    targetPort: 80     #日标pod 的端口
    #nodePort: 32000   # 固定绑定到所有 node 的32000 端口上
    name: web          # 为端口起个名字
  type: NodePort       # 如果不指定 nodePort ,会随机启动一个端口 (30000~32767),映射到 ports 中的端口,该端口处直接绑定在 node 上的。且集群中的每一个node 都会绑定这个端口, 也可以用于将服务暴露给外部访间,但这种方式实际生产环境不推荐,效率低,而且Service 是四层负载
# 创建service
kubectl create -f nginx-sVc.yaml

# 查看service信息,通过service的cluster ip进行访问
kubectl get svc

# 查看pod信息,通过pod的ip进行访问
kubectl get po -owide


# 创建其他 pod 通过 service name进行访问(推荐)
kubectl exec -it busybox -- sh     # busybox 是自己创建的临时 pod
curl htp://nginx-gvc


# 默认在当前 namespace 中访问,如果需要跨namespace访问pod,则在service name后面加上.<namespace>即可
curl http://nginx-svc.default

Service 访问外部服务

  • 推荐方式(不需要经常改动,管理方便)

    • 比如现有业务无感迁移, 无状态相关应用使用 deploy, 数据库相关的使用 service 代理外部服务,后期数据库迁移过来时,也容易修改,只需要更新 service 即可

  • 实现方式:

    • 1.编写 service 配置文件时,不指定 selector 属性

    • 2.自己创建 endpoint

# nginx-svc-endpoint.yaml   使用 service 代理外部 IP 访问
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc-external
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    targetPort: 80
    name: web
  type: ClusterIP
---
apiVersion: v1
kind: Endpoints
metadata:
  labels:
    app: nginx # 与 service 标签一致
  name: nginx-svc-external  # 与 service 名字一致
  namespace: default # 与 service 一致
subsets:
- addresses:
  - ip: <远程地址>  # 代理转发的目标 ip 地址
  ports: # 与 service 一致
  - name: web
    port: 80
    protocol: TCP
# nginx-svc-domain.yaml   使用 service 代理外部 域名 访问
[root@k8s-master1 ~]# cat nginx-domain.yaml 
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc-domain
  labels:
    app: nginx-svc-domain
spec:
  type: ExternalName
  externalName: www.baidu.com
  
[root@k8s-master1 ~]# kubectl get svc -l app=nginx-svc-domain
NAME               TYPE           CLUSTER-IP   EXTERNAL-IP     PORT(S)   AGE
nginx-svc-domain   ExternalName   <none>       www.baidu.com   <none>    41s
kubectl get svc, ep

常用类型

  • ClusterIP 集群内部使用

  • ExternalName 返回定义的 CNAME 别名, 可以配置为域名

  • NodePort

    • 会在所有安装了kube-proxy 的节点都绑定一个端中, 此端口可以代理至对应的Pod,集群外部可以使用任意节点ip +NodePort的端口号访问到集群中对应Pod中的服务,当类型设置为NodePort后,可以在ports配置中增加nodePort配置指定端口,需要在下方的端口范團内,如果不指定会随机指定端口

    • 端口范围: 30000~32767

    • 端口范围配置在/ysteys/systey/kube-episevereservice

  • LoadBalancer 使用云服务商(阿里云、腾讯云等) 提供的负载均衡器服务

服务发布-Ingress

  • 参考: https://kubernetes.io/zh-cn/docs/concepts/services-networking/ingress/

安装 Ingress

  1. 安装 Helm

    • 官网: https://helm.sh/

cd /opt/ && mkdir helm && cd helm/

wget https://get.helm.sh/helm-v3.2.3-linux-amd64.tar.gz
tar fx helm-v3.2.3-linux-amd64.tar.gz 
cp linux-amd64/helm /usr/bin/

[root@k8s-master1 helm]# which helm
/usr/bin/helm

[root@k8s-master1 helm]# helm version
version.BuildInfo{Version:"v3.2.3", GitCommit:"8f832046e258e2cb800894579b1b3b50c2d83492", GitTreeState:"clean", GoVersion:"go1.13.12"}
  1. 添加 Helm 仓库

# 添加仓库
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx

# 查看仓库列表
helm repo list

# 搜索ingress-nginx
[root@k8s-master1 helm]# helm search repo ingress-nginx
NAME                            CHART VERSION   APP VERSION     DESCRIPTION                                       
ingress-nginx/ingress-nginx     4.7.1           1.8.1           Ingress controller for Kubernetes using NGINX a...
  1. 下载安装包

# 下载包
helm pull ingress-nginx/ingress-nginx 

# 解压
tar fx ingress-nginx-4.7.1.tgz
cd ingress-nginx
  1. 配置参数

# 1. 修改 values.yaml 镜像地址:为国内镜像, 国外无需修改镜像
controller:
  name: controller
  image:
    ## Keep false as default for now!
    chroot: false
    registry: registry.cn-hangzhou.aliyuncs.com
    image: google_containers/nginx-ingress-controller
    .... 并注释掉 Hash 校验值,因为修改了镜像地址,所以不需要校验
    # digest: sha256:e5c4824e7375fcf2a393e1c03c293b69759af37a9ca6abdb91b13d78a93da8bd
    # digestChroot: sha256:e0d4121e3c5e39de9122e55e331a32d5ebf8d4d257227cb93ab54a1b912a7627
    
    
    ... 搜索 kube-webhook 修改配置如下
    patch:
      enabled: true
      image:
        registry: registry.cn-hangzhou.aliyuncs.com
        image: google_containers/kube-webhook-certgen
        tag: v1.5.1
        ## for backwards compatibility consider setting the full image url via the repository value below
        ## use *either* current default registry/image or repository format or installing chart by providing the values.yaml will fail
        ## repository:
        #tag: v20230407
        #digest: sha256:543c40fd093964bc9ab509d3e791f9989963021f1e9e4c9c7b6700b02bfb227b
        #pullPolicy: IfNotPresent
        
        

# 2. 修改部署配置的 kind 为 DaemonSet, nodeSelector.ingress 为 true
  # -- Use a `DaemonSet` or `Deployment`
  kind: DaemonSet
  
  nodeSelector:
    kubernetes.io/os: linux
    ingress: "true"
    
    
# 3. 修改其他参数
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
type: ClusterIP          # 如果是云平台使用 LoadBalancer



  admissionWebhooks:
    ...
    enabled: false    # 设置为 false,true 为https证书方式
  1. 创建 Namespace

kubectl create ns ingress-nginx
  1. 安装 ingress

# 为需要部署 ingress 的节点的加标签, 我们这里部署到 node1 商
kubectl label node k8s-node1 ingress=true
 

# 安装 ingress-nginx
helm install ingress-nginx -n ingress-nginx .

# 如果安装失败,先卸载,修改之后重新执行安装:helm uninstall ingress-nginx -n ingress-nginx

# 之后安装到 node1 节点, 
[root@k8s-master1 ingress-nginx]# kubectl get po -n ingress-nginx -o wide
NAME                             READY   STATUS              RESTARTS   AGE   IP              NODE        NOMINATED NODE   READINESS GATES
ingress-nginx-controller-94vcq   0/1     ContainerCreating   0          38s   192.168.8.129   k8s-node1   <none>           <none>

路径匹配和虚拟主机配置

  • 多域名配置: rules 下面多复制几分,修改下 域名即可

[root@k8s-master1 ~]# kubectl get svc |grep nginx-svc     # 目前已经有一个 svc
nginx-svc            NodePort       10.103.189.179   <none>          80:32572/TCP   9h

[root@k8s-master1 ~]# cat ingress.yaml 
apiVersion: networking.k8s.io/v1
kind: Ingress # 资源类型为 Ingress
metadata:
  name: wolfcode-nginx-ingress
  annotations:
    kubernetes.io/ingress.class: "nignx"
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules: # Ingress 规则配置,可以配置多个
  - host: kk8s.wolfcode.cn  #域名配置,可以使用通配符 *
    http:
      paths:     # 相当于 nginx 的location 配置,可以配置多个
      - pathType: Prefix # 路径类型,安装路径类型进行匹配 ImplementationSpecific 需要指定 IngressClass, 具体匹配规则以 IngressClass 中的规则为准。 Exact: 精确匹配, URL  需要与path完全匹配上,且区分大小写。 Prefix: 以 / 作为分隔符来进行前缀匹配
        backend:
          service:
            name: nginx-svc  # 代理到哪个 service
            port: 
              number: 80  # services 的端口
        path: /api         # 等价于 nginx 中的 location 的路径前缀匹配,可正则,和nginx相同
        

[root@k8s-master1 ~]# kubectl create -f ingress.yaml 
ingress.networking.k8s.io/wolfcode-nginx-ingress created

[root@k8s-master1 ~]# kubectl get ingress
NAME                     CLASS    HOSTS              ADDRESS   PORTS   AGE
wolfcode-nginx-ingress   <none>   kk8s.wolfcode.cn             80      2m54s

[root@k8s-master1 ~]# kubectl get po -n ingress-nginx -o wide         # 反向只有 node1 节点上创建了 pod
NAME                             READY   STATUS    RESTARTS   AGE   IP              NODE        NOMINATED NODE   READINESS GATES
ingress-nginx-controller-94vcq   1/1     Running   0          24m   192.168.8.129   k8s-node1   <none>           <none>

[root@k8s-master1 ~]# helm ls -A
NAME            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART                   APP VERSION
ingress-nginx   ingress-nginx   1               2023-07-03 19:44:08.059637001 +0800 CST deployed        ingress-nginx-4.7.1     1.8.1  
# 如果修改了配置文件,使用替换方式重新部署
kubectl replace -f ingress.yaml


curl http://kk8s.wolfcode.cn/api/index.html

配置与存储

ConfigMap

  1. ConfigMap 创建

# 帮助
kubectl create cm -h

Examples:
  # Create a new config map named my-config based on folder bar
  kubectl create configmap my-config --from-file=path/to/bar
  
  # Create a new config map named my-config with specified keys instead of file basenames on disk
  kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt
  
  # Create a new config map named my-config with key1=config1 and key2=config2
  kubectl create configmap my-config --from-literal=key1=config1 --from-literal=key2=config2
  
  # Create a new config map named my-config from the key=value pairs in the file
  kubectl create configmap my-config --from-file=path/to/bar
  
  # Create a new config map named my-config from an env file
  kubectl create configmap my-config --from-env-file=path/to/foo.env --from-env-file=path/to/bar.env


# 文件内容, k: v形式、k=v形式、 yaml 格式
  1. ConfigMap 使用和加载

[root@k8s-master1 ~]# kubectl create configmap test-env-config --from-literal=JAVA_OPTS_TEST=11111 --from-literal=APP_NAME=222222
configmap/test-env-config created


[root@k8s-master1 ~]# more test-cm-pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: test-env-cm
spec:
  restartPolicy: Never
  containers:
    - name: env-test
      image: alpine
      command: ["/bin/sh", "-c", "env; sleep 3600"]
      imagePullPolicy: IfNotPresent
      env:
      - name: JAVA_VM_OPTS
        valueFrom:
          configMapKeyRef:
            name: test-env-config # configMap 的名字
            key: JAVA_OPTS_TEST   # 表示从 name 的 ConfigMap 中获取名字为 key 的 value, 将其赋值给本地环境变量 JAVA_VM_OPTS
      - name: APP
        valueFrom:
          configMapKeyRef:
            name: test-env-config
            key: APP_NAME

## 测试变量是否加载到容器内
[root@k8s-master1 ~]# kubectl logs -f test-env-cm |grep -E "111|222"
JAVA_VM_OPTS=11111
APP=222222
  1. 热更新

[root@k8s-master1 ~]# cat auto-cm-reload.yaml 
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: log-config
  namespace: default
data:
  log_level: INFO
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      run: test-nginx
  template:
    metadata:
      labels:
        run: test-nginx
    spec:
      containers:
      - name: test-nginx
        image: nginx:1.7.9
        volumeMounts:
        - name: config-volume
          mountPath: /etc/config
      volumes:
      - name: config-volume
        configMap:
          name: log-config
# 查看当前挂载内容
kubectl exec test-nginx-7f9db59dcd-8v2zc  -it -- sh -c "cat /etc/config/log_level"
# 修改 INFO 为 DEBUG
[root@k8s-master1 ~]# kubectl edit cm log-config
configmap/log-config edited


# 等待大概 10 秒钟时间,再次查看环境变量的值
[root@k8s-master1 ~]# kubectl exec test-nginx-7f9db59dcd-8v2zc  -it -- sh -c "cat /etc/config/log_level"
DEBUG

ConfigMap 更新后滚动更新 Pod

# 更新 ConfigMap 目前并不会触发相关 Pod 的滚动更新,可以通过修改 pod annotations 的方式强制触发滚动更新
kubectl patch deployment test-nginx --patch '{"spec": {"template": {"metadata": {"annotations": {"version/config": "20190411" }}}}}'

Secret

# 命令帮助
kubectl create secret  -h
  • Service Account

    • 用来访问 Kubernetes API,由 Kubernetes 自动创建,并且会自动挂载到 Pod

[root@k8s-master1 ~]# kubectl exec <pod_name>  -- sh -c "ls /run/secrets/kubernetes.io/serviceaccount"
ca.crt
namespace
token
  • Opaque

    • map 类型,要求 valuebase64 编码格式:

$ echo -n "admin" | base64
YWRtaW4=
$ echo -n "1f2d1e2e67df" | base64
MWYyZDFlMmU2N2Rm

secrets.yml
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
  type: Opaque
data:
  password: MWYyZDFlMmU2N2Rm
  username: YWRtaW4=
# 将 Secret 挂载到 Volume 中
apiVersion: v1
kind: Pod
metadata:
  labels:
    name: seret-test
spec:
  volumes:
  - name: secrets
    secret:
      secretName: mysecret
  containers:
  - image: hub.atguigu.com/library/myapp:v1
    name: db
    volumeMounts:
    - name: secrets
      mountPath: "/etc/myapp"
      readOnly: true
# 将 Secret 导出到环境变量中
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: pod-deployment
  template:
    metadata:
      labels:
        app: pod-deployment
    spec:
      containers:
      - name: pod-1
        image: nginx:1.7.9
        env:
        - name: TEST_USER
          valueFrom:
            secretKeyRef:
              name: mysecret
              key: username
        - name: TEST_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysecret
              key: password
  • kubernetes.io/dockerconfigjson

kubectl create secret docker-registry myregistrykey --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
secret "myregistrykey" created
# 在创建 `Pod` 的时候,通过 `imagePullSecrets` 来引用刚创建的 `myregistrykey`
apiVersion: v1
kind: Pod
metadata:
  name: foo
spec:
  containers:
  - name: foo
    image: nginx:1.7.9
    imagePullSecrets:
    - name: myregistrykey

持久化存储

Volumes-HostPath

将节点上的文件或目录挂载到Pod上,此时该目录会变成持久化存储目录,即使Pod被删除后重启,也可以重新加载到该目录,该目录下的文件不会丢失

  • 空字符串(默认) 默认类型,不做任何检查

  • DirectoryOrCreate 如果在给定的路径上不存在,则创建一个空目录,权限 0755

  • Directory 目录,必须存在。

  • FileOrCreate 如果在给定的路径上不存在,则创建一个空文件,权限设 0644

  • File 必须存在文件。

  • Socket UNIX 套接字,必须存在 。

  • CharDevice 字符设备,必须存在

  • BlockDevice 块设备,必须存在

apiVersion: v1
kind: Pod
metadata:
  name: test-volume-pd
spec:
  containers:
    - image: k8s.gcr.io/test-webserver
      name: test-container
      volumeMounts:
        - mountPath: /test-pd       # 挂载到容器的哪个目录
          name: test-volume         # 挂载哪个volume
  volumes:
    - name: test-volume
      hostPath:
        path: /data                 # 与主机共享目录,加载主机中的指定目录到容器中
        type: Directory             # 检查类型,在挂载前对挂载目录做什么检查操作,有多种谐项,默认为空字符串,不做任何检在

Volumes-EmptyDir

EmptyDir主要用于一个Pod中不同的Container共享数据使用的,由于只是在Pod内部使用,因此与其他volume比较大的区别是,当Pod如果被删除了,那么emptyDir也会被删除。

存储介质可以是任意类型,如SSD、磁盘或网络存储。可以将 emptyDir.medkam 设置为Memory让k8s使用tmpfs (内存支持文件系统),速度比较快,但是重启tmpfs节点时,数据会被清除,且设置的大小会计入到Container的内存限制中。


# 测试一个 Pod 两个容器
[root@k8s-master1 ~]# cat test-volume-emtry.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
    - image: alpine
      name: test-empty1
      command: ["/bin/sh", "-c", "sleep 3600;"]
      volumeMounts:
        - mountPath: /cache
          name: cache-volume
    - image: alpine
      name: test-empty2
      command: ["/bin/sh", "-c", "sleep 3600;"]
      volumeMounts:
        - mountPath: /cache
          name: cache-volume
  volumes:
    - name: cache-volume
      emptyDir: {}
      
[root@k8s-master1 ~]# kubectl create -f test-volume-emtry.yaml 
pod/test-pd created


[root@k8s-master1 ~]# kubectl get po       
NAME                           READY   STATUS      RESTARTS   AGE
test-pd                        2/2     Running     0          53s


# 开两个终端,测试两个容器数据是否共享
[root@k8s-master1 ~]# kubectl exec -it test-pd -c test-empty1 -- sh 
[root@k8s-master1 ~]# kubectl exec -it test-pd -c test-empty2 -- sh 

NFS 挂载

安装 NFS

# 安装 nfs,所有节点安装
yum install nfs-utils -y

# 启动 nfs,所有节点启动
systemctl start nfs-server

# 查看 nfs 版本
cat /proc/fs/nfsd/versions

# NFS节点创建共享目录, ro 只读共享目录, rw 读写共享目录
mkdir -p /home/nfs/{ro, rw}



# NFS节点设置共享目录 export
vim /etc/exports
/home/nfs/ro  xx.xx.xx.0/24(ro,sync,no_subtree_check,no_root_squash) /home/nfs/ro
/home/nfs/rw  xx.xx.xx.0/24(rw,sync,no_subtree_check,no_root_squash) /home/nfs/rw


# NFS节点重新加载
exportfs -f
systemctl reload nfs-server


# 到其他节点安装 nfs-utils 并加载测试
mkdir -p /mnt/nfs/{ro,rw}
mount -t nfs nfs:/home/nfs/rw /mnt/nfs/rw
mount -t nfs nfs:/home/nfs/ro /mnt/nfs/ro

如何使用

  • 创建两个测试的 pod, 测试 nfs 挂载目录文件

[root@k8s-master1 ~]# cat test-nfs-pd1.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: nfs-test-pd1
spec:
  containers:
  - image: nginx
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: test-volume
  volumes:
  - name: test-volume
    nfs:
      server: 192.168.8.131  # 网络存储服务地址
      path: /home/nfs/rw/www/wolfcode # 网络存储路径
      readOnly: false  # 是否只读

动态 StorageClass

参考: https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

# NFS 服务
[root@k8s-master2 nfs]# cat /etc/exports
/home/nfs/dynamic  192.168.8.0/24(rw,sync,no_subtree_check,no_root_squash) /home/nfs/dynamic
[root@k8s-master1 ~]# showmount -e 192.168.8.131
Export list for 192.168.8.131:
/home/nfs/dynamic 192.168.8.0/24


# 部署动态 sc
[root@k8s-master1 ~]# helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
[root@k8s-master1 ~]# helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
    --set nfs.server=192.168.8.131 \
    --set nfs.path=/home/nfs/dynamic \
    --set storageClass.name=managed-nfs-storage


[root@k8s-master1 ~]# kubectl get sc,deploy
NAME         PROVISIONER                                     RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
storageclass.storage.k8s.io/managed-nfs-storage   cluster.local/nfs-subdir-external-provisioner   Delete          Immediate           true                   13m

NAME                                              READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nfs-subdir-external-provisioner   1/1     1            1           13m

PV与PVC

生命周期

  1. 构建

    • 1.1 静态构建

      • 集群管理员创建若干PV卷。这些卷对象带有真实存储的细节信息,并且对集群用户可用(可见)。卷对象存在于Kubernetes API中,可供用户消费(使用)

    • 1.2 动态构建

      • 如果集群中已经有的PV无法满足PVC的需求,那么集群会根据PVC自动构建-个PV,该操作是通过StorageClass实现的。

      • 想要实现这个操作,前提是PVC必须设置StorageClass,否则会无法动态构建该PV,可以通过启用DefaultStorageClass来实现PV的构建。

  2. 绑定

    • 当用户创建一个PVC对象后,主节点会监测新的PVC对象,并且寻找与之匹配的PV卷,找到PV卷后将二者绑定在一起。

    • 如果找不到对应的PV,则需要看PVC是否设置StorageClass来决定是否动态创建PV,若没有配置,PVC 就会致处于未绑定状态,直到有与之匹配的bV后才会申领绑定关系。

  3. 使用

Pod将PVC当作存储卷来使用,集群会通过PVC找到绑定的PV,并为Pod挂载该卷。Pod - -旦使用PVC绑定PV后,为了保护数据,避免数据丢失问题,PV 对象会受到保护,在系统中无法被删除。

  1. 回收策略

当用户不再使用其存储卷时,他们可以从API中将PVC对象删除,从而允许该资源被回收再利用。 PersistentVolume 对象的回收策略告诉集群,当其被从申领中释放时如何处理该数据卷。 目前,數据卷可以被 Retained (保留)、Recycred (回收)或 Deleted (删除)。

  • Retained(保留)

回收策略Retain 使得用户可以手动回收资源。当PersistentVolumeClaim对象被删除时,PersistentVolume 卷仍然存在,对应的数据卷被视为“已释放(released) ”.由于卷上仍然存在这前一申领人的数据,该卷还不能用于其他用领。管理员可以通过下面的步骤来手动回收该卷:

- 1.删除PersistentVolume对象。与之相关的、位于外部基础设施中的存储资产(例如 AWS EBS、GCE PD、Azure Disk或Cinder卷)在PV删除之后仍然存在,
- 2.根据情况,手动清除所关联的存储资产上的数据,
- 3.手动删除所关联的存储资产。
  • Deleted删除

对于支持Delete回收策略的卷插件,删除动作会将PersistentVolume对象以Kubernetes中移除,同时也会从外部基础设施(如 AWS EBS、GCE PD、Azure Disk或Cinder卷)中移除所关联的存储资产。动态制备的卷 会继承其StorageClass中设置的回收策略,该策略默认为 Delete.管理员需要根据用户的期望来配置StorageClass;否则PV卷被创建之后必须要被编辑或者修补。

  • Recycred回收

警告:回收策略Recycle已被废弃。取而代之的建议方案是使用动态制备。 如果下层的卷播件支持,回收策鰭Recycle会在卷上执行-些基本的擦除(rm -rf /thevolume/")操作,之后允许该卷用于新的PVC申领。

PV

  • 状态

    • Available: 空闲,未被绑定

    • Bound: 已经被 PVC 绑定

    • Released: PVC 被删除,资源已回收,但是 PV 未被重新使用

    • Failed: 自动回收失败

  • 配置文件

apiVersion: v1  
kind: PersistentVolume # 描述资源对象为 PV 类型
metadata:
  name: nfs-pv   # PV 的名字
spec:
  capacity:      # 容量配置
    storage: 1Gi # PV 的容量
  accessModes:   # 访间模式: ReadWriteOnce、ReadWriteMany、ReadOnlyMany
    - ReadWriteMany  # 可被单节点独写
  persistentVolumeReclaimPolicy: Recycle # 回收策略
  volumeMode: Filesystem  # 存储学型为文件系统
  storageClassName: slow  # 创建PV的存储类名,需要与 pvc 的相同
  mountOptions: # 加载配置
    - hard
    - nfsvers=4.1
  nfs:  # 链接到 nfs
    path: /home/nfs/ro/test-pv  # 存储路径
    server: 192.168.8.131       # nfs 服务地址

PVC

apiVersion: v1
kind: PersistentVolumeClaim # 资源类型为 PVC
metadata:
  name: nfs-pvc
spec:
  accessModes:
    - ReadWriteMany       # 权限需要与对应的 PV 相同
  volumeMode: Filesystem
  resources:
    requests:
      storage: 1Gi        # 资源小于 pv,不能大于,如果大于无法匹配到 pv
  storageClassName: slow  # 名字需要与对应的 pv 相同
#  selector: # 使用选择器选择对应的 pv
#    matchLabels:
#      release: "stable"
#    matchExpressions:
#      - {key: environment, operator: In, values: [dev]}

测试PV与PVC

apiVersion: v1
kind: Pod
metadata:
  name: test-pvc-pd
spec:
  containers:
  - image: nginx
    name: nginx-volume
    volumeMounts:
    - mountPath: /usr/share/nginx/html # 挂载到容器的哪个目录
      name: test-volume    # 挂载哪个 volume
  volumes:
  - name: test-volume
    persistentVolumeClaim: # 关联 pvc
      claimName: nfs-pvc   # 要关联到哪个 pvc

StorageClass动态

  • 制备器

  • NFS 动态支部案例

    • nfs-provisioner

    • StorageClass配置

    • RBAC配置

    • PVC处于Pending状态

    • PVC测试配置

高级调度

CronJob 计划任务

在k8s中周期性运行计划任价与linux中的crontab相同 注意点: CronJob 执行的时间是controller-manager的时间,所以定要确保contrller-manager时间是准确的,另外cronjob

  • 配置文件

应该是: successfulJobsHistoryLimit

kubectl get cj

初始化容器

initContainers:  # 和 containers 同级,spec.initContainers
  - image: nginx
    imagePullOolicy: IfNotPresent
    command: ["sh", "-c", "sleep 10; echo 'inited'; >> ~/.init"]
    name: init-test
containers:
...
...

污点和容忍

  1. 污点

  • NoSchedule: 节点污点,无法被调度到此,已存在pod不受影响

  • NoExecute: 驱逐Pod,调用到其他节点

  1. 容忍

  • Equal: 容忍和污点对比, key、value 必须相同才可以调度

  • Exists: 容忍和污点只对比 key,key存在则容忍

3。 污点驱逐类型

  • node.kubernetes.io/not-ready:节点未准备好。这相当于节点状况 Ready 的值为 "False"。

  • node.kubernetes.io/unreachable:节点控制器访问不到节点. 这相当于节点状况 Ready 的值为 "Unknown"。

  • node.kubernetes.io/memory-pressure:节点存在内存压力。

  • node.kubernetes.io/disk-pressure:节点存在磁盘压力。

  • node.kubernetes.io/pid-pressure: 节点的 PID 压力。

  • node.kubernetes.io/network-unavailable:节点网络不可用。

  • node.kubernetes.io/unschedulable: 节点不可调度。

  • node.cloudprovider.kubernetes.io/uninitialized:如果 kubelet 启动时指定了一个“外部”云平台驱动, 它将给当前节点添加一个污点将其标志为不可用。在 cloud-controller-manager 的一个控制器初始化这个节点后,kubelet 将删除这个污点。

污点:是标注在节点上的,当我们在一个节点 上打上污点以后,k8s会认为尽量不要将pod调度到该节点上,除非该pod上面表示可以容忍该污点,且-个节点可以打多个污点,此时则需要pod容忍所有污点才会被调度该节点。

# 为节点打上污点
kubectl taint no k8s-master key1=value1:NoSchedule


# 移除污点
kubectl taint no k8s-master key1=value1:NoSchedule-

场景一

# 与 container 同级
tolerations:
- key: "key1"           # key 值
  operator: "Equal"     # 容忍类型,key、value 必须相同才可以调度
  value: "value1"       # value 值
  effect: "NoSchedule"  # 污点类型

场景二

tolerations:
- key: "key1"            # Key 值
  operator: "Exists"     # 容忍类型。key存在则容忍
  effect: "NoSchedule"   # 污点类型

场景三

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
  tolerationSeconds: 3600       # 延迟驱逐

亲和力和反亲和力

详情查看官网: https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/assign-pod-node/

  • Node 亲和性 || Pod 亲和性

    • requiredDuringSchedulingIgnoredDuringExecution 硬亲和性, 规则被满足的时候才能执行调度

    • preferredDuringSchedulingIgnoredDuringExecution 软(反)亲和性,如果没有匹配规则节点,则可以继续调度

例如: 混合云机房

身份认证与权限

  1. 认证

    • User Account 普通账户

    • Service Accounts 服务账户

      • Service Account Adminision Controller

      • Token Controller

      • Service Account Controller

  2. 授权

    • Role (命名空间级别)

      • 代表一个角色,会包含-组权限, 没有拒绝规则,只是附加允许。它是Namespace级别的资源,只能作用与Namespace之内。

    • ClusterRole (集群空间级别)

    • RoleBinding

    • ClusterRoleBinding

kubectl get sa   # 查看认证账户

# 查看已有命名空间角色信息
kubectl get role -A


# 查看已有已有集群空间角色信息
kubectl get clusterrole 


# 查看 rolebinding 信息
kubectl get rolebinding -A


# 查看指定 rolebinding 配置信息
kubect get rolebinding <role_binding_name> -A -o yaml

Helm 包管理器

管理 chart、config、release

安装

参考安装: ingress-nginx 部分

Helm 常用命令

helm version                            # 查看helm版本
helm create xxx                         # 创建一个xxx charts
helm lint ./xxx                         # 检查包的格式或信息是否有问题
helm install xxx1 ./xxx                 # 部署安装xxx,设置名称为xxx1
helm list                               # 列出已经部署的charts
helm history                            # 发布历史
helm upgrade                            # 更新版本
helm rollback                           # 回滚版本
helm package ./xxx                      # 打包charts
helm repo add --username admin --password password myharbor xxx  # 增加repo
helm uninstall xxx1                     # 卸载删除xxx1
helm pull                               # 拉取chart包
helm cm-push                            # 推送chart包
helm repo update                        # 更新仓库资源
helm search hub                         # 从 Artifact Hub 中查找并列出 helm charts。 Artifact Hub中存放了大量不同的仓库
helm search repo                        # 从你添加(使用 helm repo add)到本地 helm 
helm dependency                         # 管理 chart 依赖

Helm 目录结构

mycahrt/
├── Chart.yaml    # chart 描述信息
├── values.yaml   # 定义 chart 模板中自定义配置默认值,可以在外部执行覆盖
├── charts/       # 该目录保存其他依赖的 chart (子chart)
└── templates/    # chart 配置模板,用户渲染最终的 kubernets YAML
    └── NOTES.TEXT          # 用于运行 helm install 时候提示信息
    └── _helpers.tpl        # 用于创建模板的帮助类
    └── deployment.yaml     # Kubernetes deployment 配置
    └── ingress.yaml        # Kubernetes ingress 配置
    └── service.yaml        # Kubernetes service 配置
    └── ingress.yaml        # Kubernetes ingress 配置
    └── serviceaccount.yaml # Kubernetes serviceaccount 配置
    └── tests
        └── test-connection.yaml 

Helm 实践 Redis chart

  • 需要动态storageclasses: (managed-nfs-storage)名字

  • 3主3从

  1. 修改 helm 源

# 查看默认仓库
helm repo list

# 添加仓库
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add azure http://mirror.azure.cn/kubernetes/charts
  1. 搜索 redis chart

helm search repo redis

# 查看指定 chart 文档
helm show readme <serach_name>

  1. 传变量在线安装

已经设置了 nfs 为默认存储, 那这里就不需要 存储参数了

helm install redis-cluster bitnami/redis-cluster \
--set global.redis.password=123456 -n saas
  1. 修改配置安装

[root@k8s-master1 ~]# kubectl get sc
NAME                  PROVISIONER                                     RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
managed-nfs-storage   cluster.local/nfs-subdir-external-provisioner   Delete          Immediate           true                   165m

# 先将 chart 拉到本地
helm pull bitnami/redis-cluster --version 8.6.6

# 解压后,修改 values.yaml 中参数
tar -xvf redis-cluster-8.6.6.tgz

cp redis-cluster/values.yaml ./redis-cluster-values.yaml

# 修改 storageClass 为 managed-nfs-storage
# 设置 redis 密码 password
# 设置 运行在 中间件 节点上
global:
  imageRegistry: ""
  imagePullSecrets: []
  storageClass: "managed-nfs-storage"  # nfs 持久化存储名字
  redis:
    password: "123456"
    
    
password: "123456"

  `nodeSelector`: {"node":"middleware"}       # 设置了服务的 node 亲和性,确保服务运行在指定的节点 (部分 k8s-node 节点运行中间件,部分 k8s-node 节点运行业务)


updateJob:
  nodeSelector: {"node":"middleware"}       # 设置了服务的 node 亲和性,确保服务运行在指定的节点 (部分 k8s-node 节点运行中间件,部分 k8s-node 节点运行业务)
# 安装操作 
kubectl create ns redis

# 安装, helm 名字 redis-cluster , 镜像 bitnami/redis-cluster, 版本 8.6.6, 空间 redis, 变量文件 
helm install redis-cluster bitnami/redis-cluster  -f redis-cluster-values.yaml -n redis
  1. 查看安装情况

helm list -n redis
kubectl get all -n redis
kubectl get sc
  1. 连接 Redis 集群 验证服务

# 获取内部 dns 域名
[root@k8s-master1 ~]# kubectl describe po redis-cluster-4 -n redis |grep REDIS_NODES
      REDIS_NODES:        redis-cluster-0.redis-cluster-headless redis-cluster-1.redis-cluster-headless redis-cluster-2.redis-cluster-headless redis-cluster-3.redis-cluster-headless redis-cluster-4.redis-cluster-headless redis-cluster-5.redis-cluster-headless 
      
# 进入临时终端
kubectl run --namespace redis redis-cluster-client --rm --tty -i --restart='Never'  --env REDIS_PASSWORD=$REDIS_PASSWORD --image bitnami/redis-cluster:7.0.11-debian-11-r27 -- bash

# 登录redis集群测试
redis-cli -c  -h  redis-cluster-1.redis-cluster-headless -a $REDIS_PASSWORD

# 查看集群状态
> cluster info
> cluster nodes
  1. 提供服务给外部

[root@k8s-master1 ~]# kubectl get svc -n redis
NAME                     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)              AGE
redis-cluster            ClusterIP   10.108.84.182   <none>        6379/TCP             101m
redis-cluster-headless   ClusterIP   None            <none>        6379/TCP,16379/TCP   101m

# kubernetes 内部应用只需要链接  <server_name>:<port> 既可通信
redis-cluster:6379
  1. 升级与回滚

# 升级
helm upgrade [release] [chart] [flags]
helm upgrade redis-cluster bitnami/redis-cluster -f redis-cluster-values.yaml -n redis

# 回滚到历史版本
helm ls
helm rollback <release> [revision] [flags]

# 查看历史 
helm history redis-cluster

# 回退到上一个版本
helm rollback redis-cluster

# 回退到指定版本
helm rollback redis-cluster <version>
  1. helm 卸载 redis

helm delete redis -n redis

Helm 实践 kube-prometheus-stack

  • 动态存储: 如何创建, 查看 动态NFS StorageClass 步骤

  • 方法一: Helm 方式安装(推荐)

参考: kube-prometheus-stack安装及使用 - 墨天轮 (modb.pro) 参考: 一文搞懂基于 Helm 部署 Prometheus Stack 全家桶 - Luga Lee - twt企业IT交流平台 (talkwithtrend.com) 参考: prometheus-operator/kube-prometheus: Use Prometheus to monitor Kubernetes and applications running on Kubernetes (github.com)

# 创建命名空间
kubectl create ns kube-monitor

# 添加helm仓库
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

# 更新仓库
helm repo update


# 安装 kube-prometheus-stack, 我 k8s 版本 1.23.6 ,这里我使用默认最新,如果其他版本例如: --version 45.0.0 
helm search repo prometheus-communit  -l |grep prometheus-stack
helm pull prometheus-community/kube-prometheus-stack

修改values.yaml,将storageClassName值替换为创建的managed-nfs-storage

# 备份
[root@ecs-6272 ~]# helm show values prometheus-community/kube-prometheus-stack > /tmp/values.yaml  # 备份一下

# prometheus、alertmanager 存储
storageSpec:
  volumeClaimTemplate:
    spec:
      storageClassName: managed-nfs-storage
      accessModes: ["ReadWriteMany"]
      resources:
        requests:
          storage: 1Gi

存在则更新

# 如果没有,则部署新的
helm install prometheus-community/kube-prometheus-stack -n kube-monitor -f values.yaml 


# 如果已经部署了,修改完配置用 helm upgrade 更新配置。
[root@ecs-6272 ~]# helm upgrade --install prometheus prometheus-community/kube-prometheus-stack -n kube-monitor -f values.yaml   # 更新配置

检查

# 查看是否都是 running
[root@k8s-master1 ~]# kubectl get pod -n kube-monitor

# 查看 pv 和 pvc 是否持久化
[root@k8s-master1 ~]# kubectl get pv,pvc

使用 Ingress 提供服务

# 使用 Ingress 提供服务
[root@k8s-master1 ~]# kubectl get svc -n kube-monitor
NAME                                      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
prometheus-grafana                        ClusterIP   10.103.87.103    <none>        80/TCP                       114m
prometheus-kube-prometheus-alertmanager   ClusterIP   10.97.106.233    <none>        9093/TCP,8080/TCP            114m
prometheus-kube-prometheus-prometheus     ClusterIP   10.107.45.13     <none>        9090/TCP,8080/TCP            114m
...略


[root@k8s-master1 ~]# cat kube-prometheus-ingress.yaml 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: kube-monitor       # Prometheus 自定义命名空间
  name: prometheus-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: grafana.kube.cn
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: prometheus-grafana
            port: 
              number: 80
  - host: prometheus.kube.cn
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: prometheus-kube-prometheus-prometheus
            port: 
              number: 9090
  - host: altermanager.kube.cn
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: prometheus-kube-prometheus-alertmanager
            port:
              number: 9093
[root@k8s-master1 ~]# kubectl create -f ./kube-prometheus-ingress.yaml
# grafana 默认账号密码为 admin | prom-operator
  • 方法二: Yaml 方式安装

git clone https://github.com/prometheus-operator/kube-prometheus.git -b release-0.10


# 修改下 values.yaml 配置

# 1、
kubectl create -f manifests/setup

# 2、
kubectl apply -f manifests/  

# 查看
kubectl get all -n monitoring
kubectl get pods -n monitoring -o wide

# 如果出现错误,定位错误修复
kubecl describe pod [pod_name] -n [namespace]

Helm 实战 RadonDB MySQL Operator 集群

官网: https://radondb.com/ 备份: 查看官网

# 添加 Helm 仓库 radondb 。
helm repo add radondb https://radondb.github.io/radondb-mysql-kubernetes/
helm search repo mysql-operator


# 部署 Operator, 名字为 mysql
kubectl create ns mysql-cluster
helm install mysql radondb/mysql-operator -n mysql-cluster



# 部署 RadonDB MySQL 集群, 使用默认参数为 CRD mysqlclusters.mysql.radondb.com 创建一个实例,即创建 RadonDB MySQL 集群。
[root@k8s-master1 ~]# kubectl get sc
NAME                  PROVISIONER                                     RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
managed-nfs-storage   cluster.local/nfs-subdir-external-provisioner   Delete          Immediate           true                   29h


[root@k8s-master1 ~]# wget https://github.com/radondb/radondb-mysql-kubernetes/releases/latest/download/mysql_v1alpha1_mysqlcluster.yaml 

# 修改 mysql_v1alpha1_mysqlcluster.yaml 持久化存储, storageClass: "managed-nfs-storage"

[root@k8s-master1 ~]# kubectl apply -f  ./mysql_v1alpha1_mysqlcluster.yaml -n mysql-cluster



# 校验 RadonDB MySQL Operator
[root@k8s-master1 ~]# kubectl get deployment,svc -n mysql-cluster
NAME                                   READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/mysql-mysql-operator   1/1     1            1           12m

NAME                             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
service/mysql-operator-metrics   ClusterIP   10.109.127.112   <none>        8443/TCP   12m
service/radondb-mysql-webhook    ClusterIP   10.111.183.95    <none>        443/TCP    12m




# 校验 RadonDB MySQL 集群
[root@k8s-master1 ~]# kubectl get crd -n mysql-cluster| grep mysql.radondb.com
backups.mysql.radondb.com                             2023-07-07T14:36:36Z
mysqlclusters.mysql.radondb.com                       2023-07-07T14:36:36Z
mysqlusers.mysql.radondb.com                          2023-07-07T14:36:36Z


[root@k8s-master1 ~]# kubectl get po -n mysql-cluster
NAME                                    READY   STATUS    RESTARTS      AGE
mysql-mysql-operator-6f7686dc46-kq7p8   2/2     Running   2 (11m ago)   38m
sample-mysql-0                          3/3     Running   0             15m
sample-mysql-1                          3/3     Running   0             5m21s
sample-mysql-2                          3/3     Running   0             2m9s

** 访问集群**

在 Kubernetes 集群内,支持使用 service_name 或者 clusterIP 方式,访问 RadonDB MySQL。 RadonDB MySQL 提供 Leader 和 Follower 两种服务,分别用于客户端访问主从节点。Leader 服务始终指向主节点(可读写),Follower 服务始终指向从节点(只读)。

# ClusterIP 方式
RadonDB MySQL 的高可用读写 IP 指向 Leader 服务的 clusterIP,高可用只读 IP 指向 Follower 服务的 clusterIP。
mysql -h <clusterIP> -P <mysql_Port> -u <user_name> -p

# 以下示例用户名为 radondb_usr, Leader 服务的 clusterIP 为 10.10.128.136 ,连接示例如下:
mysql -h 10.10.128.136 -P 3306 -u radondb_usr -p




# service_name 方式
Kubernetes 集群的 Pod 之间支持通过 service_name 方式访问 RadonDB MySQL。

service_name 方式不适用于从 Kubernetes 集群的物理机访问数据库 Pod。
连接 Leader 服务(RadonDB MySQL 主节点)

mysql -h <leader_service_name>.<namespace> -u <user_name> -p

用户名为 radondb_usr,release 名为 sample,RadonDB MySQL 命名空间为 default ,连接示例如下:
mysql -h sample-leader.default -u radondb_usr -p

连接 Follower 服务(RadonDB MySQL 从节点)
mysql -h <follower_service_name>.<namespace> -u <user_name> -p

用户名为 radondb_usr,release 名为 sample,RadonDB MySQL 命名空间
为 default ,连接示例如下:
mysql -h sample-follower.default -u radondb_usr -p  

卸载

# 卸载 Operator
卸载当前命名空间下 release 名为 demo 的 RadonDB MySQL Operator。
helm delete demo

# 卸载集群
卸载 release 名为 sample RadonDB MySQL 集群。
kubectl delete mysqlclusters.mysql.radondb.com sample

# 卸载自定义资源
kubectl delete customresourcedefinitions.apiextensions.k8s.io mysqlclusters.mysql.radondb.com
kubectl delete customresourcedefinitions.apiextensions.k8s.io mysqlusers.mysql.radondb.com
kubectl delete customresourcedefinitions.apiextensions.k8s.io backups.mysql.radondb.com

k8s 删除 ns、pod 卡住的解决办法

在某些情况下,在k8s中会无法删除某个namespace,它会一直卡在terminating状态下。解决这个问题的步骤为:

#  这个指令找到阻碍这个namespace删除的资源,
kubectl api-resources --verbs=list --namespaced -o name   | xargs -n 1 kubectl get --show-kind --ignore-not-found -n <namespace>


# 然后手动删除这些 Pod 资源。
kubectl delete pod xxx -n xxxx --force


# 或者 `kubectl edit pod -o json` 然后找到里面的"finalizers",把它的值设置成一个空数组。

但是大部分时候,这些资源也杀不掉,解决办法是执行

kubectl edit pod -o json

然后找到里面的"finalizers",把它的值设置成一个空数组。

解决这些资源后,参照以下文档删除那个ns:

Last updated