# 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="https://2134947750-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvILwbD2PrkBCkSitM3m4%2Fuploads%2F9zWRRq3IudiKQBirDwOc%2Fd47df11a63fd808706560d431c910afb.png?alt=media&#x26;token=f0c08f58-2d27-47e9-89d5-b39bbd551ed7" 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="https://2134947750-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvILwbD2PrkBCkSitM3m4%2Fuploads%2FGIB4oys8A9hzJ7olh70B%2F4270e838dcb1319007cf22d672532dba.png?alt=media&#x26;token=ed5a50fd-a71a-481a-9602-6ffaf5ff1b7c" 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="https://2134947750-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvILwbD2PrkBCkSitM3m4%2Fuploads%2FEchilHVaOKwXZhG2cuCb%2F4d4b4cec2f046f78420cac6448c2a454.png?alt=media&#x26;token=4e8fc5cf-1f1e-4a72-8b1f-6578b841a0cc" alt=""><figcaption></figcaption></figure>

<figure><img src="https://2134947750-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvILwbD2PrkBCkSitM3m4%2Fuploads%2FjBz9kGs1vLQ9knTfHCrm%2F4d4b4cec2f046f78420cac6448c2a454.png?alt=media&#x26;token=69ff913f-cfad-4fd1-b865-22b3570c336b" 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:

```
```
