> For the complete documentation index, see [llms.txt](https://close.gitbook.io/yun-wei-bi-ji/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://close.gitbook.io/yun-wei-bi-ji/kubernetes/k8s/kubernetes-1.23.6.md).

# 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. 安装步骤二: 安装基础软件(所有节点)

```bash
# 安装 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

```bash
# 在 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

```bash
# 如果 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值>
```

```bash
# 查看机器状态
[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

```bash
# 在 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
```

* 效果

```bash
[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 集群

```bash
# 创建部署
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 通信

```bash
# 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说明

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

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

## 深入Pod

### 1. 配置文件

```bash
# 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  # 重启策略，只有失败的情况才会重启
```

```bash
[root@k8s-master1 ~]# kubectl create -f nginx-demo.yaml 
pod/nginx-demo created

[root@k8s-master1 ~]# watch kubectl get po
```

```bash
# 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 探针**

```yaml
# 基于上面创建 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   # 请求的超时时间
...略
```

```bash
[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. 生命周期

<figure><img src="/files/MeRtTvAomMylKmhTIGpq" alt=""><figcaption></figcaption></figure>

```bash
# 基于上面创建 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

```bash
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 无状态

* **创建**

```bash
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 ，才会滚动更新

```bash
# 扩容不是滚动更新
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无法启动，那么我们想回滚到上一个版本

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

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

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

* **扩容缩容**

```bash
kubectl scale --replicas=6 deploy nginx-deploy    # 扩容到 6个 pod, template 没有改变，不会更改 rs
kubectl scale --replicas=3 deploy nginx-deploy    # 缩容到 3个 pod
```

* 滚动更新的暂停和恢复
  * 常用于增加pod或者服务器的时候

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

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

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

### StatefulSet 有状态

* **创建**
  * 由于没有 存储卷，这里就不设置了

```yaml
# 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的存储资源
```

```bash
[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
```

* **扩容和缩容**

```bash
[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的顺序倒序更新的
```

```bash
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更新

```bash
[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 的时候才实现更新

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

* **级联删除和非级联删除**

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

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

* **删除PVC**

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

### DaemonSet 守护进程

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

<figure><img src="/files/EVzp15i6h3M1SJNMvDu6" alt=""><figcaption></figcaption></figure>

* **配置文件**

```bash
[root@k8s-master1 ~]# kubectl  label no k8s-node1 type=microservices     # 添加一个标签，让守护进程在此标签下运行
node/k8s-node1 labeled
```

```yaml
# 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
```

```bash
[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>
```

* 滚动更新

> 只有删除的时候才更新

```bash
# 不建议使用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 开启指标服务

```bash
# 下载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` 参数
```

```bash
[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信息

```yaml
# 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 # 删除操作最多宽限多长时间■
```

```bash
# 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             
```

```bash
# 测试:找到对应服务的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

<figure><img src="/files/3drsw0CqbNvcTOzAP2am" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/qunge37w5YAyjL1VyCC2" alt=""><figcaption></figcaption></figure>

### 配置与基础命令

```yaml
# 配置详解
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 是四层负载
```

```bash
# 创建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

```yaml
# 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
```

```yaml
# 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
```

```bash
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/&#x20>;

### 安装 Ingress

1. 安装 Helm
   * 官网: <https://helm.sh/>

```bash
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"}
```

2. 添加 Helm 仓库

```bash
# 添加仓库
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...
```

3. 下载安装包

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

# 解压
tar fx ingress-nginx-4.7.1.tgz
cd ingress-nginx
```

4. 配置参数

```yaml
# 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证书方式
```

5. 创建 Namespace

```bash
kubectl create ns ingress-nginx
```

6. 安装 ingress

```bash
# 为需要部署 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 下面多复制几分，修改下 域名即可

```bash
[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  
```

```bash
# 如果修改了配置文件，使用替换方式重新部署
kubectl replace -f ingress.yaml


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

## 配置与存储

### ConfigMap

1. ConfigMap 创建

```bash
# 帮助
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 格式

```

2. ConfigMap 使用和加载

```bash
[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
```

3. 热更新

```yaml
[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

```

```bash
# 查看当前挂载内容
kubectl exec test-nginx-7f9db59dcd-8v2zc  -it -- sh -c "cat /etc/config/log_level"
```

```bash
# 修改 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**

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

### Secret

```bash
# 命令帮助
kubectl create secret  -h
```

* Service Account
  * 用来访问 Kubernetes API，由 Kubernetes 自动创建，并且会自动挂载到 `Pod` 的

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

* Opaque
  * `map` 类型，要求 `value` 是 `base64` 编码格式：

```yaml
$ 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=
```

```bash
# 将 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
```

```bash
# 将 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

```bash
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
```

```bash
# 在创建 `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 块设备,必须存在

```bash
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的内存限制中。

```bash

# 测试一个 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

```bash
# 安装 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 挂载目录文件

```bash
[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>

```bash
# 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
```

```bash
[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 对象会受到保护，在系统中无法被删除。

4. 回收策略

> 当用户不再使用其存储卷时，他们可以从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: 自动回收失败
* 配置文件

```yaml
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

```yaml
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

```yaml
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

```bash
kubectl get cj
```

### 初始化容器

```yaml
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,调用到其他节点

2. 容忍

* 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容忍所有污点才会被调度该节点。

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


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

场景一

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

场景二

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

场景三

```yaml
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

```bash
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 常用命令

```bash
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 目录结构

```bash
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 源

```bash
# 查看默认仓库
helm repo list

# 添加仓库
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add azure http://mirror.azure.cn/kubernetes/charts
```

2. 搜索 redis chart

```bash
helm search repo redis

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

3. 传变量在线安装<br>

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

<pre class="language-bash"><code class="lang-bash"><strong>helm install redis-cluster bitnami/redis-cluster \
</strong>--set global.redis.password=123456 -n saas
</code></pre>

3. 修改配置安装

```bash
[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 节点运行业务）
```

```bash
# 安装操作 
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
```

4. 查看安装情况

```bash
helm list -n redis
kubectl get all -n redis
kubectl get sc
```

5. 连接 `Redis 集群` 验证服务

```bash
# 获取内部 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
```

6. 提供服务给外部

```bash
[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
```

7. 升级与回滚

```bash
# 升级
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>
```

8. helm 卸载 redis

```bash
helm delete redis -n redis
```

### Helm 实践 kube-prometheus-stack

* 动态存储： 如何创建， 查看 动态NFS StorageClass 步骤
* 方法一: Helm 方式安装(推荐)

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

```bash
# 创建命名空间
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**

```bash
# 备份
[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
```

**存在则更新**

```bash
# 如果没有，则部署新的
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   # 更新配置
```

**检查**

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

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

```

**使用 Ingress 提供服务**

```yaml
# 使用 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
```

```bash
[root@k8s-master1 ~]# kubectl create -f ./kube-prometheus-ingress.yaml
# grafana 默认账号密码为 admin | prom-operator
```

* 方法二: Yaml 方式安装

```bash
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/> 备份： 查看官网

```bash
# 添加 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 服务始终指向从节点（只读）。

```bash
# 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  

```

**卸载**

```bash
# 卸载 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状态下。解决这个问题的步骤为：

```bash
#  这个指令找到阻碍这个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"，把它的值设置成一个空数组。
```

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

```bash
kubectl edit pod -o json
```

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

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

```
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://close.gitbook.io/yun-wei-bi-ji/kubernetes/k8s/kubernetes-1.23.6.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
