---
# System prepended metadata

title: '[kubernetes]kubernetes學習筆記 (一)'
tags: [kubernetes]

---

# [kubernetes]kubernetes學習筆記 (一)
###### tags `kubernetes`

----

#### 1.搭建kubernetes集群

##### Kubernetes 组件介绍

- 一个 kubernetes 集群主要由控制节点（master）、工作节点（node）构成，每个节点上都会安装不同的组件。

- 控制节点（master）：集群的控制平面，负责集群的决策。
  - API Server：集群操作的唯一入口，接收用户输入的命令，提供认证、授权、API注册和发现等机制，同时协调各组件的工作。

  - Scheduler：负责集群资源调度，按照预定的调度策略将 Pod 调度到相应的 node 节点上。

  - ControllerManager：负责维护集群的状态，比如程序部署安排、故障检测、自动扩展和滚动更新等。

  - Etcd：负责存储集群中各种资源对象的信息。

- 工作节点（node）：集群的数据平面，负责为容器提供运行环境。
  - Kubelet：负责维护容器的生命周期，即通过控制 Docker ，来创建、更新、销毁容器。

  - KubeProxy：负责提供集群内部的服务发现和负载均衡。

  - Docker：负责节点上容器的各种操作。

- 部署网络组件的作用？
  实现容器跨主机网络通信
- 容器接口

CNI 容器网络接口
CRI 容器运行时接口 （对接容器接口）
CSI 容器存储接口

- k8s弃用docker前，与弃用后

k8s -> docker-shim/cri -> docker  （有dockershim，默认能兼容docker）

k8s -> cri -> docker(支持cri)            （后期需要docker去兼容对接 CRI）

##### 1.1.环境准备 

```shell
master 192.168.3.124
node1  192.168.3.125
node2  192.168.3.126

##### 关闭防火墙
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    # 永久，sed中&，经常用来在原文本下增加字符串，其中&就相当于要被替换".*swap.*
swapoff -a && swapon -a  #刷新swp
（在初始化得时候如果常出现swp报错请重启服务器，然后再执行一遍关闭swp得命令）

#修改主机名，修改hosts
vim  /etc/hosts
192.168.3.124  master
192.168.3.125  node01
192.168.3.126  node02

#将桥接ipv4流量传递到iptables的链上
cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_nonlocal_bind = 1
net.ipv4.ip_forward = 1
EOF
sysctl --system # 生效

#时间同步
yum install -y ntpdate
timedatectl set-timezone Asia/Shanghai

在kubernetes中service有两种代理模型，一种是基于iptables，另一种是基于ipvs的。ipvs的性能要高于iptables的，但是如果要使用它，需要手动载入ipvs模块。
修改内核参数:
cat <<EOF >  /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_nonlocal_bind = 1
net.ipv4.ip_forward = 1
EOF
sysctl -p /etc/sysctl.d/k8s.conf 

1）加载ipvs：
[root@k8s-master ~] mkdir /opt/ipvs && cd /opt/ipvs
[root@master ipvs] yum -y install ipset ipvsadm sysstat conntrack libseccomp
[root@master ipvs] vim ipvs.sh
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_sh
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- nf_conntrack_ipv4

#执行脚本
[root@master ipvs] chmod +x ipvs.sh
[root@master ipvs] bash ipvs.sh

2)让脚本开机自启：
[root@master ipvs]# lsmod |grep ip_vs

[root@master ipvs]# echo "bash /opt/ipvs/ipvs.sh" >> /etc/rc.local
[root@master ipvs]# chmod +x /etc/rc.local
```

##### 1.2.安装docker（每台都执行）

```shell
yum -y install yum-utils device-mapper-persistent-data lvm2

yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

yum-config-manager --enable docker-ce-edge

yum install -y docker-ce-19.03.9-3.el7 docker-ce-cli-19.03.9-3.el7 containerd.io  docker-compose

systemctl enable docker
systemctl start docker

#配置镜像加速（阿里云镜像站）
cat > /etc/docker/daemon.json << EOF
{
  "registry-mirrors": ["https://b9pmyelo.mirror.aliyuncs.com"],
  "exec-opts": ["native.cgroupdriver=systemd"]
}
EOF

#配置重载docker守护进程而不重启容器
（1）vi /etc/docker/daemon.json
{
  "live-restore": true
}

（2）手动启用该过程
dockerd --live-restore systemd
```

##### 1.3.安装kubeadm和kubelet（每台都执行）

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

yum -y install  kubelet-1.23.0-0.x86_64  kubeadm-1.23.0-0.x86_64 kubectl-1.23.0-0.x86_64

systemctl enable kubelet
```

##### 1.4.使用kubeadm部署master

```shell
kubeadm init --kubernetes-version=1.23.0 --apiserver-advertise-address=192.168.1.211  --service-cidr=10.64.0.0/24 --pod-network-cidr=10.244.0.0/16   --image-repository registry.aliyuncs.com/google_containers

#注：主机名中不能带有_（下划线），不然初始化会报错

mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf  $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config

查看node信息
kubectl get nodes
```

##### 1.5.让node加入集群

```shell
#当主节点启动完k8s之后会生成加入节点的命令，视自己服务器而定
kubeadm join 172.16.8.124:6443 --token xxxxx \
    --discovery-token-ca-cert-hash xxxxxx

#默认token有效期为24小时，当过期之后，该token就不可用了。这时就需要重新创建token，操作如下：
kubeadm token create --print-join-command

#查看节点是否已加入
kubectl get nodes
```

##### 1.6.集群组件报错

###### 1.6.1.所有节点显示 NotReady  （是因为还没安装网络组件）

![image-20220407170459496](http://oss.linuxtxc.com/images/image-20220407170459496.png)

```shell
#查看组件健康情况
kubectl get  cs
```

![image-20220407170643950](http://oss.linuxtxc.com/images/image-20220407170643950.png)

- 解决方法

```shell
vim  /etc/kubernetes/manifests/kube-controller-manager.yaml （注释掉 - --port=0）
vim  /etc/kubernetes/manifests/kube-scheduler.yaml          （注释掉 - --port=0）
systemctl start kubelet 

```



##### 1.7.安装pod网络插件 

```shell
#主节点执行
#kubectl apply -f https://github.com/flannel-io/flannel/blob/master/Documentation/kube-flannel.yml

#安装calico
# wget https://docs.projectcalico.org/v3.8/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml

wget https://install-data.oss-cn-hongkong.aliyuncs.com/deploy/yaml/calico.yaml
kubectl apply -f  calico.yaml

#修改部分 （#取消注释）

- name: CALICO_IPV4POOL_CIDR   
  value: "10.244.0.0/16"    #地址为初始化集群时的“--pod-network-cidr”的设置IP


查看pods信息
kubectl get pods -n kube-system
```

##### 1.8.安装私有镜像仓库harbor

```shell
#安装docker-compose
#可以通过修改URL中的版本，可以自定义您的需要的版本
curl -L https://get.daocloud.io/docker/compose/releases/download/1.21.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose

为安装脚本添加执行权限
chmod +x /usr/local/bin/docker-compose


#重启docker
systemctl daemon-reload
systemctl restart docker

#安装harbor
#官方下载地址：https://github.com/goharbor/harbor/releases

wget https://github.com/goharbor/harbor/releases/download/v2.5.1/harbor-offline-installer-v2.5.1.tgz

tar xvf harbor-offline-installer-v2.5.1.tgz
cd harbor/
vim harbor.yml (修改默认端口)
./prepare
# --with-chartmuseum ,启用chart仓库
./install.sh  --with-chartmuseum  

#登录harbor (没有证书，需要添加信任)
(1)docker login http://192.168.3.124:8080 (docker登录harbor)
(docker无法连接harbor时，请在/etc/docker/daemon.json 中添加{
  "registry-mirrors": ["https://b9pmyelo.mirror.aliyuncs.com"],
  "exec-opts": ["native.cgroupdriver=systemd"],
   "insecure-registries": ["harbor的主机ip:端口"]
}
（如果还不行请./install.sh）

#创建连接镜像仓库的secret
kubectl create secret docker-registry harbor-key-secret --docker-server=harbor.linuxtxc.com:9090 --docker-username=admin --docker-password=Harbor123 
```

##### 1.9.安装coredns

```shell
#官方模板：https://github.com/coredns/deployment/blob/master/kubernetes/coredns.yaml.sed
#复制内容模板内容
[root@k8s_master yaml]# vim coredns.yaml

#要修改的内容
CLUSTER_DNS_IP: 10.0.0.2   #对应kubelet的DNS
CLUSTER_DOMAIN: cluster.local
REVERSE_CIDRS: in-addr.arpa ip6.arpa
STUBDOMAINS: 无
UPSTREAMNAMESERVER: /etc/resolv.conf
# 修改镜像为1.7.1
coredns/coredns:1.7.1

#修改后的coredns.yaml
wget http://oss.linuxtxc.com/deploy/yaml/coredns.yaml
kubectl apply -f coredns.yaml
```



##### 1.10.重置kubernetes集群

```shell
(master，node都执行) 
rm -rf  ./kube
rm -rf /etc/cni/net.d/*


(master上执行 )
kubeadm  reset

kubeadm init --kubernetes-version=1.23.0 --apiserver-advertise-address=192.168.1.211  --service-cidr=10.64.0.0/24 --pod-network-cidr=10.244.0.0/16   --image-repository registry.aliyuncs.com/google_containers

mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config

#出现pod无法起来时
#vim  /etc/kubernetes/manifests/kube-controller-manager.yaml （注释掉 - --port=0）
#vim  /etc/kubernetes/manifests/kube-scheduler.yaml          （注释掉 - --port=0）
#systemctl restart kubelet 

(node 上执行)
kubeadm reset
kubeadm join 192.168.3.124:6443 --token f62e1g.exfdrr8bg2fxpdmu \
    --discovery-token-ca-cert-hash sha256:7e4762cccce2e8306b6635cbc2091eae34bbf33577276413521a343c49fc026a
    
    
#安装1.24之后的版本时
rm -rf /etc/containerd/config.toml 

containerd config default |   tee /etc/containerd/config.toml

sed -i 's#SystemdCgroup = false#SystemdCgroup = true#g' /etc/containerd/config.toml

sed -i 's#sandbox_image = "k8s.gcr.io/pause:3.6"#sandbox_image = "registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.6"#g' /etc/containerd/config.toml
 
systemctl restart containerd
```



##### 1.11.切换容器引擎为Containerd

```shell
#启动模块（默认已经启用）
modprobe overlay
modprobe br_netfilter

#设置必需的 sysctl 参数
cat > /etc/sysctl.d/cri-containerd.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
sysctl --system

#安装containerd （已经安装docker后，默认会有containerd）
#yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
#yum install -y containerd.io
#mkdir -p /etc/containerd

#生成默认的配置文件
containerd config default > /etc/containerd/config.toml
```

- 修改配置文件

​      • pause镜像设置过阿里云镜像仓库地址

​      • cgroups驱动设置为systemd

​      • 拉取Docker Hub镜像配置加速地址设置为阿里云镜像仓库地址 

```shell

sed -i 's#SystemdCgroup = false#SystemdCgroup = true#g' /etc/containerd/config.toml

sed -i 's#sandbox_image = "k8s.gcr.io/pause:3.6"#sandbox_image = "registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.6"#g' /etc/containerd/config.toml

vim /etc/containerd/config.toml        [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
          endpoint = ["https://b9pmyelo.mirror.aliyuncs.com"]  #修改

#重启containerd
systemctl restart containerd
```

- 配置kubelet使用containerd

```shell
vi /etc/sysconfig/kubelet 
KUBELET_EXTRA_ARGS=--container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock --cgroup-driver=systemd

systemctl restart kubelet

#验证
kubectl get node -o wide
```

![image-20220531181711832](http://oss.linuxtxc.com/images/image-20220531181711832.png)

- 管理容器工具

containerd提供了ctr命令行工具管理容器，但功能比较简单，所以一般会用crictl工具检查和调试容器。

项目地址：https://github.com/kubernetes-sigs/cri-tools/

设置crictl连接containerd：

```shell
vi /etc/crictl.yaml
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
debug: false
```

- docker与crictl命令对照表：

| ***\*镜像相关功能\**** | ***\*Docker\****        | ***\*Containerd\****     |
| ---------------------- | ----------------------- | ------------------------ |
| 显示本地镜像列表       | docker images           | crictl images            |
| 下载镜像               | docker pull             | crictl pull              |
| 上传镜像               | docker push             | 无，例如buildk           |
| 删除本地镜像           | docker rmi              | crictl rmi               |
| 查看镜像详情           | docker inspect IMAGE-ID | crictl inspecti IMAGE-ID |

| ***\*容器相关功能\**** | ***\*Docker\**** | ***\*Containerd\**** |
| ---------------------- | ---------------- | -------------------- |
| 显示容器列表           | docker ps        | crictl ps            |
| 创建容器               | docker create    | crictl create        |
| 启动容器               | docker start     | crictl start         |
| 停止容器               | docker stop      | crictl stop          |
| 删除容器               | docker rm        | crictl rm            |
| 查看容器详情           | docker inspect   | crictl inspect       |
| 附加容器               | docker attach    | crictl attach        |
| 执行命令               | docker exec      | crictl exec          |
| 查看日志               | docker logs      | crictl logs          |
| 查看容器资源           | docker status    | crictl status        |

| ***\*POD\**** ***\*相关功能\**** | ***\*Docker\**** | ***\*Containerd\**** |
| -------------------------------- | ---------------- | -------------------- |
| 显示 POD 列表                    | 无               | crictl pods          |
| 查看 POD 详情                    | 无               | crictl inspectp      |
| 运行 POD                         | 无               | crictl run           |
| 停止 POD                         | 无               | crictl stop          |



##### 1.12.编写dockerfile

- 使用alpine 构建 nginx镜像

```shell
FROM alpine:latest
RUN  sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories    #更换下载地址
RUN  apk update && apk --no-cache add  gcc libc-dev make openssl-dev pcre-dev zlib-dev linux-headers curl gnupg libxslt-dev gd-dev geoip-dev pcre wget zlib 
RUN  mkdir /opt/apps
ADD  nginx-1.19.6.tar.gz  /usr/local/src/ 

RUN cd /usr/local/src/nginx-1.19.6 && ./configure  --prefix=/opt/apps/nginx --with-http_stub_status_module --with-http_geoip_module --with-http_ssl_module --with-http_gzip_static_module  --with-http_v2_module --with-stream --with-stream_ssl_module --user=nginx --group=nginx && make && make install 
ENV PATH $PATH:/opt/apps/nginx/sbin
COPY nginx.conf /opt/apps/nginx/conf/nginx.conf 
ADD index.html /opt/apps/nginx/html/index.html
RUN echo 'if (\$request_filename ~* .*\.(jspx|jsp|z|gz|bz2|7z|zip|rar|tar|tar.gz|tar.bz2|tgz)$) { \
    return 403; \
    break;  \
}' > /opt/apps/nginx/conf/filter.forbid
RUN addgroup  -g 2019 -S nginx && adduser  -s /sbin/nologin -S -D  -u 2019 -G nginx nginx
RUN  mkdir /opt/apps/nginx/conf/vhosts && mkdir -p /opt/logs/nginx && chown -R nginx:nginx /opt/apps/nginx
WORKDIR  /opt/apps/nginx
EXPOSE 80 443 
CMD ["nginx", "-g", "daemon off;"]
```

##### 13.给pod内的etcd集群做数据备份

- 在pod内生成快照，写入周期性计划任务

```shell
* 2 * * * /usr/bin/kubectl exec -it etcd-0 -n apisix -- etcdctl snapshot save /bitnami/etcd/data/member/snap/etcd-`/usr/bin/date +\%Y\%m\%d\%H\%M`.db &> /apps/kubernetes/backup/etcd-0-backup.log

* 2 * * * /usr/bin/kubectl exec -it etcd-1 -n apisix -- etcdctl snapshot save /bitnami/etcd/data/member/snap/etcd-`/usr/bin/date +\%Y\%m\%d\%H\%M`.db &> /apps/kubernetes/backup/etcd-1-backup.log

* 2 * * * /usr/bin/kubectl exec -it etcd-2 -n apisix -- etcdctl snapshot save /bitnami/etcd/data/member/snap/etcd-`/usr/bin/date +\%Y\%m\%d\%H\%M`.db &> /apps/kubernetes/backup/etcd-2-backup.log
```

- 在node节点上从挂载的pvc中复制到备份目录（写入周期性计划任务中）

```shell
30 2 * * *  /usr/bin/find  /var/lib/kubelet/pods/d8a02579-3574-4f97-a4e8-ce3fc8c8ab3d/volumes/kubernetes.io~nfs/pvc-47a02205-0a35-48bf-80af-09130d37697/data/memeber/snap  -ctime -1 -type f | /usr/bin/xargs -i /usr/bin/cp {} /apps/kubernetes/backup

* * * * 7  /usr/bin/find  /apps/kubernetes/backup  -ctime +15 -type f | /usr/bin/xargs -i /usr/bin/rm -rf {}
```



-----

#### 2.kubernetes 资源管理

##### 2.1.kubectl create

```shell
kubectl create  namespace  dev    #创建一个namespace    

kubectl create service clusterip ngx-dep --tcp=80:80  #命令行创建service

kubectl create -f  ns-dev.yaml    #根据yaml文件创建资源

kubectl create deployment  deploy-nginx  --image=nginx:1.17.2 --replicas=3  -n dev
#暴露端口
kubectl expose deployment deploy-nginx   --port=80 --target-port=80 --type=NodePort --name=nginx-service -n dev

#命令行创建yaml模板（–dry-run：不执行命令，打印资源文件，-o yaml，将资源输出为YAML格式）
kubectl create deployment  deploy-nginx  --image=nginx:1.17.2 --replicas=3  -n dev --dry-run=client -o yaml > deploy-nginx.yaml

#命令行创建service
kubectl create service clusterip ngx-dep --clusterip="none" --tcp=80:80 --dry-run=client -o yaml

kubectl create service nodeport nginx-service --tcp=80:80  --dry-run=client -o yaml
```

##### 2.2.kubectl api-resources 

```shell
kubectl api-resources   #查看k8s所有的资源以及对应的apiversion

kubectl api-versions    #查看k8s所有 版本号
```

##### 2.3.kubectl explain

```shell
kubectl explain 资源类型（pod,deployment）   # 查看某种资源可以配置的一级配置

kubectl explain 资源类型.属性       #查看资源的子属性
```

##### 2.4.kubectl label

版本类标签（release）：stable（稳定版）、canary（金丝雀版本，可以将其称之为测试版中的测试版）、beta（测试版）；
环境类标签（environment）：dev（开发）、qa（测试）、production（生产）、op（运维）；
应用类（app）：ui、as、pc、sc；
架构类（tier）：frontend（前端）、backend（后端）、cache（缓存）；
分区标签（partition）：customerA（客户A）、customerB（客户B）；
品控级别（Track）：daily（每天）、weekly（每周）。

```shell
kucectl label pod  nginx-pod version=1.0 -n web    #为pod打上标签

kubectl label pod  --show-labels -n web            #显示pod的标签

kucectl label pod  nginx-pod version=1.0  --overwrite -n web     #覆盖原来的标签
```

##### 2.5.kubectl describe

```shell
kubectl describe  pod nginx-pod  -n web   #查看pod 的详细信息

kubectl describe service nginx-svc -n web  #查看service的 详细信息

kubectl describe node 192.168.3.125     #查看node的详细信息  
```

##### 2.6.kubectl exec 

```shell
#进入pod容器中
kubectl exec -it  nginx-deployment-798444d598-87hlk  -n web /bin/sh
```

##### 2.7.kubectl   get

```shell
kubectl get cs   #获取集群健康状态

kubectl get csr  #查看申请加入kubernetes集群的token信息

kubectl get endpoints -n web #获取service对应的所有pod的访问地址

kubectl get pod -n web -o wide -w    #查看namespace web下的pod，--all-namespaces：查看所有namespace下的pod；-w：动态查看；-o wide:查看更多信息

kubectl get pod  --show-labels  （显示资源标签）

kubectl get pod  -l app=nginx  （-l,根据标签过滤资源）
```

##### 2.8.kubectl edit

```shell
kubectl edit cm kubeadm-config -n kube-system   #cm,configMap;编辑configMap中的配置
```

##### 2.9.kubectl logs

```shell
kubectl logs nginx-deployment-798444d598-87hlk  -n web   #查看pod日志

kubectl logs -f  nginx-deployment-798444d598-87hlk -n web  #实时查看日志

kubectl log  nginx-deployment-798444d598-87hlk -c <container_name> -n web  #查看pod中单个容器的日志
kubectl logs -l app=frontend  -n web    #返回全部标记为 app=frontend 的 pod 的合并日志
kubectl logs --tail=20 -f  nginx-deployment-798444d598-87hlk -n web

#过滤时间段的日志
kubectl logs -f -n namespace pod-name --since-time="2021-12-22T02:00:00+00:00"

```

##### 2.10.kubectl delete

```shell
kubectl delete -f  deployment-nginx.yaml   #删除该yaml中创建的资源

kubectl delete pod nginx-deployment-798444d598-87hlk -n web  #删除pod资源

#有部分 Terminating状态的pod无法删除，可以使用 --grace-period=0 --force强制删除
kubectl delete  pod calico-kube-controllers-6b77fff45-m6w4n -n kube-system --grace-period=0 --force
```

##### 2.11.kubectl    autoscale

```shell
kubectl autoscale deployment deployment-nginx --cpu-percent=60 --min=2 --max=10     #使用 Deployment “deployment-nginx”设定，使用默认的自动伸缩策略，指定目标CPU使用率，使其Pod数量在2到10之间。
```

##### 2.12.kubectl patch

```shell
#使用（patch）补丁修改、更新资源的字段。
kubectl patch pod rc-nginx-2-kpiqt -p '{"metadata":{"labels":{"app":"nginx-3"}}}'  #修改资源配置
```

##### 2.13.kubectl replace

```shell
kubectl replace -f rc-nginx.yaml   #根据yaml文件更新修改后配置资源，会停掉原来的资源，重新创建
```

##### 2.14.kubectl scale

``` shell
kubectl scale rc redis --replicas=3 -n web    #操作pod 的扩容和缩容

kubectl scale --replicas=2 -f redis-slave-deployment.yaml   
```

##### 2.15.kubectl  rollout

```shell
status：显示当前升级状态
history：显示升级历史记录
pause：暂停版本升级过程
resume：继续已经暂停的版本升级过程
restart：重启版本升级过程
undo：回滚到上一级版本 （可以使用--to-revision，指定版本）
kubectl apply -f pc-deployment.yml --record  (--record，记录操作记录,便于回滚)
   

更新镜像：
（1）通过kubectl set 命令去更改，nginx为容器名
kubectl set image  deploy  nginx-deployment nginx=nginx:1.15.2 -n app --record  && kubectl  rollout  pause deployment  nginx-deployment -n app   #相当于灰度发布

（2）通过修改deployment文件去更改
kubectl apply -f nginx-deployment.yml --record  && kubectl  rollout  pause deployment    nginx-deployment -n app 

#继续更新过程
kubectl rollout resume deployment nginx-deployment -n app

#查看历史版本
kubectl  rollout history deployment nginx-deployment -n app 

#版本回滚到v1
kubectl rollout undo deploy nginx-deployment --to-revision=1 -n app
```

##### 2.16.kubectl  taint

```shell
 kubectl taint node k8s-node1 tag=webapps:PreferNoSchedule  #设置污点
 
 kubectl taint node k8s-node1 tag:PreferNoSchedule-         #去除污点
 
 kubectl taint node k8s-node1 tag-                          #去除所有污点
 
 kubectl describe nodes k8s-node1 | grep Taints            #查看节点上的污点
 
#将node标记为不可调度的状态，这样就不会让新创建的pod在此node上运行。
kubectl cordon 192.168.1.48
 
#恢复node为可调度的状态。
kubectl uncordon 192.168.1.48

#可以让node在维护期间排除节点。drain本意排水，意思是将出问题的node下的pod转移到其它node下运行,并且不接收新的pod。
kubectl drain 192.168.1.48 --ignore-daemonsets --delete-local-data

#drain的参数
--force
当一些pod不是经 ReplicationController, ReplicaSet, Job, DaemonSet 或者 StatefulSet 管理的时候
就需要用--force来强制执行 (例如:kube-proxy)

--ignore-daemonsets
无视DaemonSet管理下的Pod
 
--delete-local-data
如果有mount local volumn的pod，会强制杀掉该pod并把料清除掉
```

##### 2.17.kubectl  run

```shell
kubectl run nginx --image=nginx:1.17.1 -n web   #命令使用镜像创建容器
```

##### 2.18.kubectl  apply

```shell
#如果资源不存在，就创建，相当于kubectl create。
#如果资源存在，就更新，相当于kubectl patch。
kubectl apply -f nginx-pod.yaml -n web --record  （--record，记录操作记录,便于回滚）
```

##### 2.19.kubectl  top

```shell
kubectl top  pod/node  -n dev   #查看资源使用情况（需要安装metrics-server）

#-l,标签选择，--sort-by,根据参数排序
kubectl top pod  -l name=cpu-utillzer --sort-by="cpu” 	-A
```

##### 2.20.kubectl  expose

```shell
#将tomcat-deployment 服务的8080 端口 暴露给 tomcat-service 的service （也相当于创建service）
kubectl expose deployment tomcat-deployment   --port=8080 --target-port=8080 --type=ClusterIP --name=tomcat-service -n app
```

##### 2.21.kubectl  cp

```shell
#将pod里的文件拷贝到主机
#kubectl cp -c 容器名 pod名:文件绝对路径 文件目标位置 -n namespace名
kubectl cp     nginx-deployment-84b859f76c-crdrl:tmp/helm-v3.5.4-linux-amd64.tar.gz  /tmp/helm-v3.5.4-linux-amd64.tar.gz -n app

#将主机文件拷贝到pod
#kubectl cp 主机文件路径  -c 容器 pod名:容器内绝对路径 -n namespace名
kubectl cp   tomcat-java-demo-master.zip  nginx-deployment-84b859f76c-crdrl:tmp  -n app   （如果pod中有多个容器，需要指定进入的容器名）

注：pod后的目录绝对路径不用写"/"
```

##### 2.22.kubectl  certificate

```
#kubectl certificate用来修改证书资源,可选approve/deny同意与拒绝审批。



```

##### 2.23.kubectl config

```
#config命令,生成集群信息,集群用户和用户权限并把这些内容写入kubectl读取的配置文件
# 设置集群参数
[root@k8s-master admin]# kubectl config set-cluster kubernetes --certificate-authority=/etc/kubernetes/cert/ca.pem --embed-certs=true --server=${KUBE_APISERVER} --kubeconfig=kubectl.kubeconfig
#设置客户端认证参数
[root@k8s-master admin]# kubectl config set-credentials admin --client-certificate=admin.pem --client-key=admin-key.pem --embed-certs=true --kubeconfig=kubectl.kubeconfig
#设置上下文参数,包含集群名称和访问集群的用户名字
[root@k8s-master admin]# kubectl config set-context kubernetes --cluster=kubernetes --user=admin --kubeconfig=kubectl.kubeconfig
#使用默认上下文
[root@k8s-master admin]# kubectl config use-context kubernetes --kubeconfig=kubectl.kubeconfig

```

##### 2.24.docker  命令

```shell
#查看容器运行的进程
docker top 容器ID/容器名

#删除所有未被容器使用的镜像
docker image prune -a

#删除所有停止运行的容器
docker container prune

#删除所有未被挂载的卷
docker volume prune


#删除所有未使用的网络
docker network prune

#用于清理磁盘，删除关闭的容器、无用的数据卷和网络，以及dangling镜像(即无tag的镜像),-a,清理的更加彻底
docker system prune -a



#查看Docker的磁盘使用情况,-v,显示空间使用的详细信息
docker system df -v
```



---

#### 3.kubernetes 的Pod 详解

pod：k8s 最小控制单元，其中包含一个pause容器（又叫infra容器，基础容器），多个业务 容器，提供服务，pod中容器共享网络，存储资源
pause容器作用：可以根据它判断pod的健康状态，同时在pause容器上设置IP，可以让同一个pod中的所有容器共用

- 创建一个pod工作流程：

  - kubectl向apiserver发送部署请求

  - apiserver将工作负载控制器（deployment等）持久化到etcd，etcd和apiserver进行通信

  - controller-manager通过watch api监听apiserver，得知新创建的工作负载控制器对象后，将从队列中拉出，根据工作负载控制器的描述挨个创建（rs，rc）等对象，并持久化回到etcd中

  - scheduler 调度pod到可用的node节点上，加载namespace。并将pod对下个返回apiserver写入到etcd中
  
  - kubelet收到scheduler调度信息后，将通过容器创建pod
  

##### 3.1.Pod  所有参数配置

```yaml
apiVersion: v1
kind: Namespace
metadata:
   name: dev
---
#所有常用配置参数
apiVersion: v1     #必选，版本号，例如v1
kind: Pod       　 #必选，资源类型，例如 Pod
metadata:       　 #必选，元数据
  name: string     #必选，Pod名称
  namespace: string  #Pod所属的命名空间,默认为"default"
  labels:       　　  #自定义标签列表
    - name: string      　          
spec:  #必选，Pod中容器的详细定义
  containers:  #必选，Pod中容器列表
  - name: string   #必选，容器名称
    image: string  #必选，容器的镜像名称
    imagePullPolicy: [ Always|Never|IfNotPresent ]  #获取镜像的策略 
    command: [string]   #容器的启动命令列表，如不指定，使用打包时使用的启动命令
    args: [string]      #容器的启动命令参数列表
    workingDir: string  #容器的工作目录
    volumeMounts:       #挂载到容器内部的存储卷配置
    - name: string      #引用pod定义的共享存储卷的名称，需用volumes[]部分定义的的卷名
      mountPath: string #存储卷在容器内mount的绝对路径，应少于512字符
      readOnly: boolean #是否为只读模式
    ports: #需要暴露的端口库号列表
    - name: string        #端口的名称
      containerPort: int  #容器需要监听的端口号
      hostPort: int       #容器所在主机需要监听的端口号，默认与Container相同
      protocol: string    #端口协议，支持TCP和UDP，默认TCP
    env:   #容器运行前需设置的环境变量列表
    - name: string  #环境变量名称
      value: string #环境变量的值
    resources: #资源限制和请求的设置
      limits:  #资源限制的设置
        cpu: string     #Cpu的限制，单位为core数
        memory: string  #内存限制，单位可以为Mib/Gib
      requests: #资源请求的设置
        cpu: string    #Cpu请求，容器启动的初始可用数量
        memory: string #内存请求,容器启动的初始可用数量
    lifecycle: #生命周期钩子
		postStart: #容器启动后立即执行此钩子,如果执行失败,会根据重启策略进行重启
		preStop: #容器终止前执行此钩子,无论结果如何,容器都会终止
    livenessProbe:  #对Pod内各容器健康检查的设置，当探测无响应几次后将自动重启该容器
      exec:       　 #对Pod容器内检查方式设置为exec方式
        command: [string]  #exec方式需要制定的命令或脚本
      httpGet:       #对Pod内个容器健康检查方法设置为HttpGet，需要制定Path、port
        path: string
        port: number
        host: string
        scheme: string
        HttpHeaders:
        - name: string
          value: string
      tcpSocket:     #对Pod内个容器健康检查方式设置为tcpSocket方式
         port: number
       initialDelaySeconds: 0       #容器启动完成后首次探测的时间，单位为秒
       timeoutSeconds: 0    　　    #对容器健康检查探测等待响应的超时时间，单位秒，默认1秒
       periodSeconds: 0     　　    #对容器监控检查的定期探测时间设置，单位秒，默认10秒一次
       successThreshold: 0
       failureThreshold: 0
       securityContext:
         privileged: false
  restartPolicy: [Always | Never | OnFailure]  #Pod的重启策略
  nodeName: <string> #设置NodeName表示将该Pod调度到指定到名称的node节点上
  nodeSelector: obeject #设置NodeSelector表示将该Pod调度到包含这个label的node上
  imagePullSecrets: #Pull镜像时使用的secret名称，以key：secretkey格式指定
  - name: string
  hostNetwork: false   #是否使用主机网络模式，默认为false，如果设置为true，表示使用宿主机网络
  volumes:   #在该pod上定义共享存储卷列表
  - name: string    #共享存储卷名称 （volumes类型有很多种）
    emptyDir: {}       #类型为emtyDir的存储卷，与Pod同生命周期的一个临时目录。为空值
    hostPath: string   #类型为hostPath的存储卷，表示挂载Pod所在宿主机的目录
      path: string      　　        #Pod所在宿主机的目录，将被用于同期中mount的目录
    secret:       　　　#类型为secret的存储卷，挂载集群与定义的secret对象到容器内部
      scretname: string  
      items:     
      - key: string
        path: string
    configMap:         #类型为configMap的存储卷，挂载预定义的configMap对象到容器内部
      name: string
      items:
      - key: string
        path: string
        
```

###### 3.1.1.环境变量ENV

- 创建容器时，为容器设置环境变量

- - **应用场景：**

    • 容器内应用程序希望获取Pod信息

    • 容器内应用程序希望通过用户定义的变量改变默认行为

    **变量值几种定义方式：**

    • 自定义变量值

    • 变量值从Pod属性获取

    • 变量值从Secret、ConfigMap获取

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-envars
spec:
  containers:
    - name: test-container
      image: busybox
      command: [ "sh", "-c","sleep 3600h"]
      env:
        #变量值从Pod属性获取
        - name: MY_NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: MY_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: MY_POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: MY_POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        #自定义变量值
        - name: PASSWORD
          value: qwer123
```



##### 3.2.Pod资源限制

- limits 限制容器最高使用多少资源，requests 容器使用的最小资源申请（对于k8s来说是预留，主要用作k8s对node资源一个规划和分配）。
- requests一般小于limits的20%-30%。
- limits建议不能超出宿主机配置，否则没意义了，至少要低于宿主机配置的20%
- requests必须小于或等于limits。
- k8s会根据requests的值去查找能满足该值的node进行调度，如果不满足，pod处于未分配创建。
- requests决定了一个节点分配的pod数量，所以不要设置太大，否则会造成node资源浪费，即跑的pod少，实际负载很低。
- kubectl describe node k8s-node2 查看节点资源分配情况。

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-pod
  namespace: app
  labels:
    app: nginx
spec:
  containers:
    - name: nginx01 # 容器名称
      image: nginx:1.17.1 # 容器需要的镜像地址
      imagePullPolicy: IfNotPresent # 设置镜像拉取策略，Alawys;IfNotPresent;Never
      ports: # 端口设置
        - containerPort: 80 # 容器要监听的端口 （0~65536）
          protocol: TCP # 端口协议
      resources: # 资源配额
        limits: # 限制资源的上限
          cpu: "1" # CPU限制，单位是core数,0.1=100m
          memory: "10Gi" # 内存限制
        requests: # 限制资源的下限
          cpu: "0.5" # CPU限制，单位是core数, 0.5=500m
          memory: "1Gi" # 内存限制
```



- imagePullPolicy：用于设置镜像拉取的策略，kubernetes支持配置三种拉取策略：
  - Always：总是从远程仓库拉取镜像（一直远程下载）。

  - IfNotPresent：本地有则使用本地镜像，本地没有则从远程仓库拉取镜像（本地有就用本地，本地没有就使用远程下载）。

  - Never：只使用本地镜像，从不去远程仓库拉取，本地没有就报错（一直使用本地，没有就报错）。


##### 3.3.Pod 生命周期

我们一般将Pod对象从创建到终止的这段时间范围称为Pod的生命周期，它主要包含下面的过程：

- Pod创建过程。
  - 运行初始化容器（init container）过程，执行一些主容器的前置工作，执行完就结束。 
  - **应用场景：**
    - 环境检查：例如确保应用容器依赖的服务启动后再启动应用容器
    - 初始化配置：例如给应用容器准备配置文件

  - 运行主容器（main container）：
    - 容器启动后钩子（post start）。

    - 容器的存活性探测（liveness probe）、就绪性探测（readiness probe）。

    - 容器终止前钩子（pre stop）。

  - Pod终止过程。


###### 3.3.1.钩子函数

- 钩子函数能够感知自身生命周期中的事件，并在相应的时刻到来时运行用户指定的程序代码。

- kubernetes在主容器启动之后和停止之前提供了两个钩子函数：
  - post start：容器创建之后执行，如果失败会重启容器。

  - pre stop：容器终止之前执行，执行完成之后容器将成功终止，在其完成之前会阻塞删除容器的操作。

- 钩子处理器支持使用下面的三种方式定义动作：
  - ① exec命令：在容器内执行一次命令。


```yaml
……
  lifecycle:
     postStart: 
        exec:
           command:
             - cat
             - /tmp/healthy
……
```

​         ② tcpSocket：在当前容器尝试访问指定的socket。

```yaml
…… 
   lifecycle:
      postStart:
         tcpSocket:
            port: 8080
……
```

​        ③ httpGet：在当前容器中向某url发起HTTP请求。

```yaml
…… 
   lifecycle:
      postStart:
         httpGet:
            path: / #URI地址
            port: 80 #端口号
            host: 192.168.109.100 #主机地址  
            scheme: HTTP #支持的协议，http或者https
……
```

以exec方式为例，演示下钩子函数的使用，创建pod-hook-exec.yaml文件，内容如下：

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-hook-exec
  namespace: app
  labels:
    app: nginx 
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
      resources:
        limits:
          cpu: "2"
          memory: "10Gi"
        requests:
          cpu: "1"
          memory: "10Mi"
      lifecycle: # 生命周期配置
        postStart: # 容器创建之后执行，如果失败会重启容器
          exec: # 在容器启动的时候，执行一条命令，修改掉Nginx的首页内容
            command: ["/bin/sh","-c","echo postStart ... > /usr/share/nginx/html/index.html"]
        preStop: # 容器终止之前执行，执行完成之后容器将成功终止，在其完成之前会阻塞删除容器的操作
          exec: # 在容器停止之前停止Nginx的服务
            command: ["/usr/sbin/nginx","-s","quit"]
```



###### 3.3.2.容器探针

- 容器探测用于检测容器中的应用实例是否正常工作，是保障业务可用性的一种传统机制。

  - liveness probes：存活性探测，用于检测应用实例当前是否处于正常运行状态，如果不是，k8s会重启容器。

  - readiness probes：就绪性探测，用于检测应用实例是否可以接受请求，如果不能，k8s不会转发流量，并将容器从service中剔除。


- 上面两种探针目前均支持三种探测方式：
  - ① exec命令：在容器内执行一次命令，如果命令执行的退出码为0，则认为程序正常，否则不正常。


```yaml
----------------------------
readinessProbe:
      exec:
        command:
        - cat
        - /etc/hosts
      initialDelaySeconds: 5
      timeoutSeconds: 2
      successThreshold: 3
      failureThreshold: 2
      periodSeconds: 5
-----------------------------
```

② tcpSocket：将会尝试访问一个用户容器的端口，如果能够建立这条连接，则认为程序正常，否则不正常。

```yaml
……
   livenessProbe:
     tcpSocket:
       port: 8080
     failureThreshold: 5           #检测失败5次表示未就绪
     initialDelaySeconds: 60       #初始化时间
     periodSeconds: 10             #检测间隔
     successThreshold: 1           #检查成功为1次表示就绪，存活和启动探测的这个值必须是 1
     timeoutSeconds: 5             #检测超时5秒表示未就绪 
……
```

- ③ httpGet：调用容器内web应用的URL，如果返回的状态码在200和399之前，则认为程序正常，否则不正常。

```yaml
------------------------ 
 livenessProbe:         #健康检查方式：[readinessProbe,livenessProbe,StartupProbe]
   httpGet:                      #请求方式
     path: /health               #请求路径
     port: 8080                  #请求端口
     scheme: HTTP                ##请求协议
   failureThreshold: 5           #检测失败5次表示未就绪
   initialDelaySeconds: 60       #初始化时间
   periodSeconds: 10             #检测间隔
   successThreshold: 1           #检查成功为1次表示就绪，存活和启动探测的这个值必须是 1
   timeoutSeconds: 5             #检测超时5秒表示未就绪
----------------------------
```

- startupProbe ： 启动探测，k8s1.16版本后新加的探测方式，用于判断容器内应用程序是否已经启动，如果配置了startuprobe，就会先禁用其他的探测，直到它成功为止。

```yaml
----------------------------------
startupProbe:             #健康检查方式：[readinessProbe,livenessProbe,StartupProbe]
  httpGet:                        #请求方式
    path: /                       #请求路径
    port: 8080                    #请求端口
    scheme: HTTP                  #请求协议
  failureThreshold: 3             #检测失败3次表示未就绪
  periodSeconds: 10               #检测间隔
  successThreshold: 1             #检查成功为1次表示就绪，存活和启动探测的这个值必须是 1
  timeoutSeconds: 1               #检测超时1秒表示未就绪
----------------------------------
```

initialDelaySeconds   # 容器启动后等待多少秒执行第一次探测。

timeoutSeconds    # 探测超时时间。默认1秒，最小1秒。

periodSeconds    # 执行探测的频率。默认是10秒，最小1秒。

failureThreshold   # 连续探测失败多少次才被认定为失败。默认是3。最小值是1。

successThreshold   # 连续探测成功多少次才被认定为成功。默认是1，存活和启动探测的这个值必须是 1。

- 重启策略：

  - Always：容器失效时，自动重启该容器，默认值。
  
  
    - OnFailure：容器终止运行且退出码不为0时重启。
  
  
    - Never：不论状态如何，都不重启该容器。
  
  
  - readinessProbe+ livenessProbe混合案例
  

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: probe-deployment
  namespace: app
spec:
  replicas: 2
  revisionHistoryLimit: 10  #可保留的版本数
  minReadySeconds: 5        #在等待设置的时间后才进行升级
  strategy:  #策略 
    type: RollingUpdate 
    rollingUpdate:  #滚动更新 
      maxSurge: 30%  #最大额外可以存在的副本数，可以为百分比，也可以为整数； 默认25%
      maxUnavailable: 30% #最大不可用状态的 ；Pod 的最大值，可以为百分比，也可以为整数，默认25% 
  selector:
    matchLabels:
      project: dev
      app: nginx
  template:
    metadata:
      labels:
        project: dev
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.17.3
          resources: # 资源配额
            limits: # 限制资源的上限
              cpu: "1" # CPU限制，单位是core数,0.1=100m
              memory: "100Mi" # 内存限制
            requests: # 限制资源的下限
              cpu: "0.5" # CPU限制，单位是core数, 0.5=500m
              memory: "50Mi" # 内存限制
          livenessProbe: #存活性检查，重启容器
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 5   ##初始化时间,多少秒后开始第一次检测
            timeoutSeconds: 1        #检测超时1秒表示未就绪，默认1秒，最小1秒
            periodSeconds: 10        #执行探测的频率，默认是10秒，最小1秒
            failureThreshold: 5      #检测失败5次表示未就绪，默认是3
            successThreshold: 1      #检查成功为1次表示就绪，默认是1,存活和启动探测的这个值必须是 1
          readinessProbe:   #就绪性探测,从service中剔除容器
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 5   ##初始化时间,多少秒后开始第一次检测
            timeoutSeconds: 1        #检测超时1秒表示未就绪，默认1秒，最小1秒
            periodSeconds: 10        #执行探测的频率，默认是10秒，最小1秒
            failureThreshold: 5      #检测失败5次表示未就绪，默认是3
            successThreshold: 3      #检查成功为2次表示就绪，默认是1
```



----

#### 4.kubernetes 中Pod调度

- kubernetes提供了四大类调度方式。
  - 自动调度：运行在哪个Node节点上完全由Scheduler经过一系列的算法计算得出。

  - 定向调度：NodeName、NodeSelector。

  - 亲和性调度：NodeAffinity、PodAffinity、PodAntiAffinity。

  - 污点（容忍）调度：Taints、Toleration。


##### 4.1.定向调度

- 定向调度，指的是利用在Pod上声明的nodeName或nodeSelector，以此将Pod调度到期望的Node节点上。注意，这里的调度是强制的，这就意味着即使要调度的目标Node不存在，也会向上面进行调度，只不过Pod运行失败而已。

nodeName：用于强制约束将Pod调度到指定的name的Node节点上。这种方式，其实是直接跳过Scheduler的调度逻辑，直接将Pod调度到指定名称的节点。

- 创建一个pod-nodename.yaml文件，内容如下：

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-nodename
  namespace: web
  labels:
    app: nginx
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  nodeName: k8s-node1                #指定调度到k8s-node1节点上
```

nodeSelector：用于将Pod调度到添加了指定标签的Node节点上，它是通过kubernetes的label-selector机制实现的，换言之，在Pod创建之前，会由Scheduler使用MatchNodeSelector调度策略进行label匹配，找出目标node，然后将Pod调度到目标节点，该匹配规则是强制约束。

```shell
#给node节点添加标签
kubectl label node k8s-node1 node=web
kubectl label node k8s-node2 node=app
```

- 创建pod-nodeselector.yaml文件，内容如下：

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeselector
  namespace: app
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  nodeSelector:
    node: app        #指定调度到具有node=app的Node节点上
```

##### 4.2.亲和性调度

- 虽然定向调度的两种方式，使用起来非常方便，但是也有一定的问题，那就是如果没有满足条件的Node，那么Pod将不会被运行，即使在集群中还有可用的Node列表也不行，这就限制了它的使用场景。

- 基于上面的问题，kubernetes还提供了一种亲和性调度（Affinity）。它在nodeSelector的基础之上进行了扩展，可以通过配置的形式，实现优先选择满足条件的Node进行调度，如果没有，也可以调度到不满足条件的节点上，使得调度更加灵活。

- Affinity主要分为三类：

  - nodeAffinity（node亲和性）：以Node为目标，解决Pod可以调度到哪些Node的问题。

  - podAffinity（pod亲和性）：以Pod为目标，解决Pod可以和那些已存在的Pod部署在同一个拓扑域中的问题。

  - podAntiAffinity（pod反亲和性）：以Pod为目标，解决Pod不能和那些已经存在的Pod部署在同一拓扑域中的问题。



###### 4.2.1.node亲和性（nodeAffinity）

- 查看nodeAffinity的可选配置项：

```yaml
pod.spec.affinity.nodeAffinity
  requiredDuringSchedulingIgnoredDuringExecution  #Node节点必须满足指定的所有规则才可以，相当于硬限制
    nodeSelectorTerms         #节点选择列表
      matchFields             #按节点字段列出的节点选择器要求列表  
      matchExpressions   #按节点标签列出的节点选择器要求列表(推荐)
        key             #键
        values          #值
        operator #      关系符 支持Exists, DoesNotExist, In, NotIn, Gt, Lt
---
  preferredDuringSchedulingIgnoredDuringExecution    #优先调度到满足指定的规则的Node，相当于软限制 (倾向)     
    preference    #一个节点选择器项，与相应的权重相关联
      matchFields  #按节点字段列出的节点选择器要求列表
      matchExpressions   #按节点标签列出的节点选择器要求列表(推荐)
        key              #键
        values           #值
        operator     #关系符 支持In, NotIn, Exists, DoesNotExist, Gt, Lt  
    weight           #倾向权重，在范围1-100。
---
- matchExpressions:
	- key: nodeenv # 匹配存在标签的key为nodeenv的节点
	  operator: Exists   
	- key: nodeenv # 匹配标签的key为nodeenv,且value是"xxx"或"yyy"的节点
	  operator: In    
      values: ["xxx","yyy"]
    - key: nodeenv # 匹配标签的key为nodeenv,且value大于"xxx"的节点
      operator: Gt   
      values: "xxx"
```

- 下面演示preferredDuringSchedulingIgnoredDuringExecution（软限制）：
  - 创建pod-nodeaffinity-preferred.yaml文件，内容如下：


```yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeaffinity-preferred
  namespace: app
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  affinity: # 亲和性配置
    nodeAffinity: # node亲和性配置
      preferredDuringSchedulingIgnoredDuringExecution: # 先匹配node标签中有nodeenv=app或者nodeenv=xyy的node，优先调度到满足指定的规则的Node，相当于软限制 (倾向)
        - preference: # 一个节点选择器项，与相应的权重相关联
            matchExpressions:
              - key: nodeenv
                operator: In
                values:
                  - "app"
                  - "xyy"
          weight: 50
```

- 下面演示preferredDuringSchedulingIgnoredDuringExecution（硬限制）：
  - 创建pod-nodeaffinity-preferred.yaml文件，内容如下：


```yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeaffinity-required
  namespace: dev
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  affinity: # 亲和性配置
    nodeAffinity: # node亲和性配置
      requiredDuringSchedulingIgnoredDuringExecution: #先匹配node标签中有nodeenv=app或者nodeenv=xyy的node，如果没有，则会调度失败，相当于硬规则，类似于定向调度
        nodeSelectorTerms: # 节点选择列表
          - matchExpressions:
              - key: nodeenv # 匹配存在标签的key为nodeenv的节点，并且value是"xxx"或"yyy"的节点
                operator: In
                values:
                  - "app"
                  - "yyy"
```

nodeAffinity的注意事项：

- 如果同时定义了nodeSelector和nodeAffinity，那么必须两个条件都满足，Pod才能运行在指定的Node上。

- 如果nodeAffinity指定了多个nodeSelectorTerms，那么只需要其中一个能够匹配成功即可。

- 如果一个nodeSelectorTerms中有多个matchExpressions，则一个节点必须满足所有的才能匹配成功。

- 如果一个Pod所在的Node在Pod运行期间其标签发生了改变，不再符合该Pod的nodeAffinity的要求，则系统将忽略此变化。

  

###### 4.2.2.Pod亲和性（PodAffinity）

- PodAffinity的可选配置项：

```yaml
pod.spec.affinity.podAffinity
  requiredDuringSchedulingIgnoredDuringExecution   #硬限制
    namespaces                                    #指定参照pod的namespace
    topologyKey                                   #指定调度作用域
    labelSelector                              # 标签选择器
      matchExpressions                #按节点标签列出的节点选择器要求列表(推荐)
        key                           #键
        values                         #值
        operator                     #关系符 支持In, NotIn, Exists, DoesNotExist.
      matchLabels                 #指多个matchExpressions映射的内容  
  preferredDuringSchedulingIgnoredDuringExecution  #软限制    
    podAffinityTerm                              #选项
      namespaces                                 #指定参照pod的namespace
      topologyKey                                 #指定调度作用域
      labelSelector                                # 标签选择器
         matchExpressions 
            key                                     # 键  
            values                                   # 值  
            operator
         matchLabels 
    weight                           # 倾向权重，在范围1-100

#topologyKey用于指定调度的作用域，例如:
#如果指定为kubernetes.io/hostname，那就是以Node节点为区分范围。
#如果指定为beta.kubernetes.io/os，则以Node节点的操作系统类型来区分。
```

- 创建Pod过程：
  - 创建pod-podaffinity-requred.yaml文件，内容如下：


```yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-podaffinity-requred
  namespace: app
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-web
          containerPort: 80
          protocol: TCP
  affinity: # 亲和性配置
    podAffinity: # Pod亲和性
      requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
        - labelSelector:
            matchExpressions: # 该Pod必须和拥有标签app=nginx或者app=test的Pod在同一个Node上。
              - key: app
                operator: In
                values:
                  - "nginx"
                  - "test"
          topologyKey: kubernetes.io/hostname
```

- 创建Pod过程：
  - 创建pod-podaffinity- prefer.yaml文件，内容如下：


```yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-podaffinity-prefer
  namespace: app
spec:
  containers: # 容器配置
    - name: nginx01
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-app
          containerPort: 80
          protocol: TCP
  affinity: # 亲和性配置
    podAffinity: # Pod亲和性
      preferredDuringSchedulingIgnoredDuringExecution:  #软限制
        -  weight: 50
           podAffinityTerm:
             labelSelector:
               matchExpressions:  #先匹配拥有标签app=yyy或者app=test的Pod，如果有，则将pod放在匹配pod同一个Node上，如果没有则让Scheduler根据算法自动调度
                 - key: app
                   operator: In
                   values:
                     - "yyy"
                     - "test"
             topologyKey: kubernetes.io/hostname
```

###### 4.2.3.Pod反亲和性（podAntiAffinity）

- 主要实现以运行的Pod为参照，让新创建的Pod和参照的Pod不在一个区域的功能。

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-podantiaffinity-requred
  namespace: app
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  affinity: # 亲和性配置
    podAntiAffinity: # Pod反亲和性
      requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
        - labelSelector:
            matchExpressions:
              - key: app
                operator: In
                values:
                  - "nginx"
          topologyKey: kubernetes.io/hostname
```

##### 4.3.污点和容忍

###### 4.3.1.污点（Taints）

- Node被设置了污点之后就和Pod之间存在了一种相斥的关系，进而拒绝Pod调度进来，甚至可以将已经存在的Pod驱逐出去。

- 污点的格式为：`key=value:effect`，key和value是污点的标签，effect描述污点的作用，支持如下三个选项：
  - PreferNoSchedule：kubernetes将尽量避免把Pod调度到具有该污点的Node上，除非没有其他节点可以调度。

  - NoSchedule：kubernetes将不会把Pod调度到具有该污点的Node上，但是不会影响当前Node上已经存在的Pod。

  - NoExecute：kubernetes将不会把Pod调度到具有该污点的Node上，同时也会将Node上已经存在的Pod驱逐。


```shell
#设置污点
kubectl taint node xxx key=value:effect

#去除污点
kubectl taint node xxx key:effect-

#去除所有污点
kubectl taint node xxx key-

#查询所有节点的污点
wget -O jq https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64
chmod +x ./jq
cp jq /usr/bin
kubectl get nodes -o json | jq '.items[].spec'

#查看所有节点上的污点
kubectl describe node  | grep Taint

#给k8s-node1节点设置污点（PreferNoSchedule）
kubectl taint node k8s-node1 tag=test:PreferNoSchedule

#为k8s-node1取消污点（PreferNoSchedule），并设置污点（NoSchedule）
kubectl taint node k8s-node1 tag:PreferNoSchedule-
kubectl taint node k8s-node1 tag=test:NoSchedule
```

###### 4.3.2.容忍

污点是拒绝，容忍就是忽略，Node通过污点拒绝Pod调度上去，Pod通过容忍忽略拒绝

- 容忍的详细配置：

```shell
kubectl explain  pod.spec.tolerations  
.........
tolerations
- key       # 对应着要容忍的污点的键，空意味着匹配所有的键
  value     # 对应着要容忍的污点的值
  operator  # key-value的运算符，支持Equal（默认）和Exists
  effect    # 对应污点的effect，空意味着匹配所有影响
  tolerationSeconds   # 容忍时间, 当effect为NoExecute时生效，表示pod在Node上的停留时间
```

当operator为Equal的时候，如果Node节点有多个Taint，那么Pod每个Taint都需要容忍才能部署上去。

当operator为Exists的时候，有如下的三种写法：

- 容忍指定的污点，污点带有指定的effect：

- 容忍指定的污点，不考虑具体的effect：

- 容忍一切污点（慎用）：

```yaml
tolerations: # 容忍
  - key: "tag" # 要容忍的污点的key
    operator: Exists # 操作符
    effect: NoExecute # 添加容忍的规则，这里必须和标记的污点规则相同  
```

```yaml
tolerations: # 容忍
  - key: "tag" # 要容忍的污点的key
    operator: Exists # 操作符
```

```yaml
tolerations: # 容忍
  - operator: Exists # 操作符
```

- 创建pod-toleration.yaml文件，内容如下：

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-toleration
  namespace: dev
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  tolerations: # 容忍
    - key: "tag" # 要容忍的污点的key
      operator: Equal # 操作符
      value: "test" # 要容忍的污点的value
      effect: NoExecute # 添加容忍的规则，这里必须和标记的污点规则相同
```

- 案例：

```shell
#给两个节点都打上污点
kubectl taint node 192.168.3.125 tag=test:NoSchedule
kubectl taint node 192.168.3.125 tag=deploy:NoSchedule

vim  pod-toleration-test.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-toleration
  namespace: dev
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  tolerations: # 容忍
    - key: "tag" # 要容忍的污点的key
      operator: Equal # 操作符
      value: "test" # 要容忍的污点的value
      effect: NoSchedule # 添加容忍的规则，这里必须和标记的污点规则相同
    - key: "tag" # 要容忍的污点的key
      operator: Equal # 操作符
      value: "deploy" # 要容忍的污点的value
      effect: NoExecute # 添加容忍的规则，这里必须和标记的污点规则相同 
```



----

#### 5.kubernetes的Pod控制器

##### 5.1.Deployment控制器

- Deployment控制器并不直接管理Pod，而是通过管理ReplicaSet来间接管理Pod，即：Deployment管理ReplicaSet，ReplicaSet管理Pod。

- Deployment的主要功能如下：
  - 支持ReplicaSet的所有功能。

  - 支持发布的停止、继续。

  - 支持版本滚动更新和版本回退。

- 创建nginx-deployment.yaml文件，在spec下添加更新策略

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: app
spec:
  replicas: 3
  revisionHistoryLimit: 10  #可保留的版本数
  minReadySeconds: 5        #在等待设置的时间后才进行升级
  strategy:  #策略 
    type: RollingUpdate 
    rollingUpdate:  #滚动更新，就是杀死一部分，就启动一部分，在更新过程中，存在两个版本的Pod 
      maxSurge: 30%  #最大额外可以存在的副本数，可以为百分比，也可以为整数； 默认25%
      maxUnavailable: 30% #最大不可用状态的 ；Pod 的最大值，可以为百分比，也可以为整数，默认25% 
  selector:
    matchLabels:
      app: nginx
    #matchExpressions: # Expressions匹配规则 
    #  - {key: app, operator: In, values: [nginx-pod]}
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.17.1
          imagePullpolicy: ifnotpresent
          ports:
            - containerPort: 80
```

- 案例

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: java-demo
  name: java-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: java-demo
  template:
    metadata:
      labels:
        app: java-demo
    spec:
      imagePullSecrets:
      - name: harbor-key-secret 
      containers:
      - image: harbor.linuxtxc.com:9090/txcrepo/tomcat-demo:v1
        name: java-demo
        resources:
          limits:
            cpu: 1
            memory: 1Gi
          requests:
            cpu: 0.7
            memory: 700Mi
        livenessProbe:  #存活检查：检查时间，将杀死容器，根据restartPolicy操作
          httpGet:
            path: /
            port: 8080
          initialDelaySeconds: 60   #容器启动后60秒启动健康检查
          periodSeconds: 30    #隔30秒检查一次    
        readinessProbe:  #就绪检查：检查失败，k8s会将pod从service，endpoints剔除
          httpGet:
            path: /
            port: 8080
          initialDelaySeconds: 60   #容器启动后60秒启动健康检查
          periodSeconds: 30    #隔30秒检查一次    
        volumeMounts:
        - name: config  #要和下面的volumes名字一样
          mountPath: "/usr/local/tomcat/webapps/ROOT/WEB-INF/classes/application.yml"     #容器内路径
          subPath: application.yml  #防止被configmap覆盖掉容器内目录下的其他文件
      volumes:
      - name: config   #要和上面的volumeMounts名字一样
        configMap:
          name: java-demo-config
          items:
          - key: "application.yml"
            path: "application.yml" 
```

- 镜像更新

```shell
（1）kubectl set image deployment nginx-deployment nginx=nginx:1.17.2 -n app
（2）或者修改 nginx-deployment.yaml 中的image，再使用kubectl apply -f nginx-deployment.yaml --recard 更新资源
（3）kubectl edit deployment nginx-deployment -n app ，直接使用命令修改deployment
#查看升级过程
kubectl get pod -n app -w
```

![image-20220312150808108](http://oss.linuxtxc.com/images/image-20220312150808108.png)

- 查看rs，发现原来的rs依旧存在，只是Pod的数量变为0，而后又产生了一个rs，Pod的数量变为3

```shell
kubectl get rs -n app
```

![image-20220312151030643](http://oss.linuxtxc.com/images/image-20220312151030643.png)

- 查看历史版本

```shell
kubectl  rollout history deployment -n app 
```

![image-20220312152424542](http://oss.linuxtxc.com/images/image-20220312152424542.png)

- 版本回滚

```
kubectl rollout undo deploy nginx-deployment --to-revision=1 -n app
```

- 金丝雀发布（在一批新的Pod资源创建完成后立即暂停更新过程）

```shell
（1）kubectl set image  deploy  nginx-deployment nginx=nginx:1.17.3 -n app   && kubectl  rollout  pause deployment  nginx-deployment -n app

（2）kubectl apply -f nginx-deployment.yml --record  && kubectl  rollout  pause deployment    nginx-deployment -n app
```

##### 5.2.Horizontal Pod Autoscaler（HPA）

HPA可以获取每个Pod的利用率，然后和HPA中定义的指标进行对比，同时计算出需要伸缩的具体值，最后实现Pod的数量的调整。

###### 5.2.1.安装metrics-server（用来收集集群中的资源使用情况。）

```shell
#wget https://github.com/kubernetes-sigs/metrics-server/archive/v0.3.6.tar.gz
wget  http://oss.linuxtxc.com/metrics-server.zip
#解压
unzip metrics-server.zip

#进入metrics-server/deploy/1.8+/目录
cd  metrics-server/deploy/1.8+

#修改metrics-server-deployment.yaml文件
vim metrics-server-deployment.yaml

按图中添加下面选项
hostNetwork: true
image: registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server-amd64:v0.3.6 
args:
  - --kubelet-insecure-tls 
  - --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP
```

![image-20220312164723469](http://oss.linuxtxc.com/images/image-20220312164723469.png)

- 安装安装metrics-server（v0.4.1）

```shell
wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.4.1/components.yaml

修改components.yaml
修改部分：
- --kubelet-insecure-tls
image: registry.cn-shanghai.aliyuncs.com/xuweiwei-kubernetes/metrics-server:v0.4.1
```

![image-20220312183606595](http://oss.linuxtxc.com/images/image-20220312183606595.png)

- 查看metrics-server生成的Pod：

```shell
kubectl get pod -n kube-system
```

![image-20220312165123704](http://oss.linuxtxc.com/images/image-20220312165123704.png)

###### 5.2.2.错误排查

- 查看资源使用情况报错

![image-20220312165514330](http://oss.linuxtxc.com/images/image-20220312165514330.png)

- 排查错误

```shell
systemctl status kube-apiserver -l  #或者 journalctl -u kube-apiserver
```

![image-20220312173924651](http://oss.linuxtxc.com/images/image-20220312173924651.png)

- metric-server安装要满足2个条件

  - 在master节点要能访问metrics server pod ip（kubeadm部署默认已经满足该条件，二进制部署需注意要在master节点也部署node组件）

  - apiserver启用聚合层支持（kubeadm默认已经启用，二进制部署需自己启用）

因为我是二进制安装的，所以在master上安装node组件就可以了

```
kubectl top pod -n app
```

![image-20220312182326385](http://oss.linuxtxc.com/images/image-20220312182326385.png)

###### 5.2.3.部署HPA案例

- 使用之前创建的nginx-deployment，使用kubectl  edit 将副本数改为 1

![image-20220312184814408](http://oss.linuxtxc.com/images/image-20220312184814408.png)

  - 创建 nginx-service.yaml

```
apiVersion: v1 
kind: Service 
metadata: 
  name: nginx-service
  namespace: app
spec:
  selector: # 标签选择器，用于确定当前Service代理那些Pod
    app: nginx-deployment
  type: NodePort 
  ports: # 
    - port: 80 # Service端口
      protocol: TCP 
      targetPort : 80
      nodePort: 30081
```

- 创建nginx-hpa.yaml

```yaml
apiVersion: autoscaling/v1 # 版本号
kind: HorizontalPodAutoscaler # 类型
metadata: # 元数据
  name: nginx-hpa # deployment的名称
  namespace: app  #命名类型
spec:
  minReplicas: 1 # 最小Pod数量
  maxReplicas: 10 # 最大Pod数量
  targetCPUUtilizationPercentage: 3 # CPU使用率指标
  scaleTargetRef:  # 指定要控制的Nginx的信息
    apiVersion: apps/v1
    kind: Deployment
    name: nginx-deployment
```

```shell
#使用命令创建HPA控制器
kubectl autoscale deployment nginx-hpa --cpu-percent=3 --min=1 --max=10
```

- targets为unknown 的原因

![image-20220314130425072](http://oss.linuxtxc.com/images/image-20220314130425072.png)

  - 需要安装metrics-server

  - HPA对应的pod，需要设置request资源

```yaml
#使用命令行设置pod 资源限制
kubectl set resources deployment/nginx-deployment --limits=cpu=800m
```

- 再次查看：

![image-20220314135225796](http://oss.linuxtxc.com/images/image-20220314135225796.png)

- 使用ab 命令访问测试

![image-20220315110716103](http://oss.linuxtxc.com/images/image-20220315110716103.png)

- 查看 HPA状态

![image-20220315110747229](http://oss.linuxtxc.com/images/image-20220315110747229.png)



##### 5.3.DaemonSet

- DaemonSet类型的控制器可以保证集群中的每一台（或指定）节点上都运行一个副本，一般适用于日志收集、节点监控等场景

- DaemonSet控制器的特点：
  - 每向集群中添加一个节点的时候，指定的Pod副本也将添加到该节点上。

  - 当节点从集群中移除的时候，Pod也会被垃圾回收。

- DaemonSet的资源清单：

```yaml
apiVersion: apps/v1 # 版本号
kind: DaemonSet # 类型
metadata: # 元数据
  name: # 名称
  namespace: #命名空间
  labels: #标签
    controller: daemonset
spec: # 详情描述
  revisionHistoryLimit: 3 # 保留历史版本
  updateStrategy: # 更新策略
    type: RollingUpdate # 滚动更新策略
    rollingUpdate: # 滚动更新
       maxSurge: 30%  #最大额外可以存在的副本数，可以为百分比，也可以为整数； 默认25%
      maxUnavailable: 1 # 最大不可用状态的Pod的最大值，可用为百分比，也可以为整数，默认25%
  selector: # 选择器，通过它指定该控制器管理那些Pod
    matchLabels: # Labels匹配规则
      app: nginx-pod
    matchExpressions: # Expressions匹配规则
      - key: app
        operator: In
        values:
          - nginx-pod
  template: # 模板，当副本数量不足时，会根据下面的模板创建Pod模板
     metadata:
       labels:
         app: nginx-pod
     spec:
       containers:
         - name: nginx
           image: nginx:1.17.1
           ports:
             - containerPort: 80
```

- 案例

```yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
  namespace: kube-system
  labels:
    k8s-app: node-exporter
spec:
  selector:
    matchLabels:
      app: node-exporter
  template:
    metadata:
      labels:
        app: node-exporter
    spec:
      containers:
      - image: prom/node-exporter
        name: node-exporter
        ports:
        - containerPort: 9100
          protocol: TCP
          name: http
```



##### 5.4.Job

- Job主要用于负责批量处理短暂的一次性任务。J可以保证指定数量的Pod执行完成。

- Job的特点：
  - 当Job创建的Pod执行成功结束时，Job将记录成功结束的Pod数量。

  - 当成功结束的Pod达到指定的数量时，Job将完成执行。

- Job的资源清单：

```yaml
apiVersion: batch/v1 # 版本号
kind: Job # 类型
metadata: # 元数据
  name:  # 名称
  namespace:  #命名空间
  labels: # 标签
    controller: job
spec: # 详情描述
  completions: 1 # 指定Job需要成功运行Pod的总次数，默认为1
  parallelism: 1 # 指定Job在任一时刻应该并发运行Pod的数量，默认为1
  activeDeadlineSeconds: 30 # 指定Job可以运行的时间期限，超过时间还没结束，系统将会尝试进行终止
  backoffLimit: 6 # 指定Job失败后进行重试的次数，默认为6
  manualSelector: true # 是否可以使用selector选择器选择Pod，默认为false
  selector: # 选择器，通过它指定该控制器管理那些Pod
    matchLabels: # Labels匹配规则
      app: counter-pod
    matchExpressions: # Expressions匹配规则
      - key: app
        operator: In
        values:
          - counter-pod
  template: # 模板，当副本数量不足时，会根据下面的模板创建Pod模板
     metadata:
       labels:
         app: counter-pod
     spec:
       restartPolicy: Never # 重启策略只能设置为Never或OnFailure
       containers:
         - name: counter
           image: busybox:1.30
           command: ["/bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1;do echo $i;sleep 20;done"]
```

- Job案例

```yaml
apiVersion: batch/v1 # 版本号
kind: Job # 类型
metadata: # 元数据
  name: pc-job # 名称
  namespace: dev #命名空间
spec: # 详情描述
  manualSelector: true # 是否可以使用selector选择器选择Pod，默认为false
  parallelism: 1   ##指定job需要成功允许pods的次数。默认值：1
  completions: 1   #指定job 在任一时刻应该并发运行pod 的数量。默认值：1
  selector: # 选择器，通过它指定该控制器管理那些Pod
    matchLabels: # Labels匹配规则
      app: counter-pod
  template: # 模板，当副本数量不足时，会根据下面的模板创建Pod模板
    metadata:
      labels:
        app: counter-pod
    spec:
      restartPolicy: Never # 重启策略只能设置为Never或OnFailure
      containers:
        - name: counter
          image: busybox:1.30
          command: [ "/bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1;do echo $i;sleep 3;done" ]
```

```shell
#查看job运行情况
kubectl get job -n dev -w
```



##### 5.5.CronJob（CJ）

- CronJob控制器以Job控制器为其管控对象，并借助它管理Pod资源对象，Job控制器定义的作业任务在其控制器资源创建之后便会立即执行，但CronJob可以以类似Linux操作系统的周期性任务作业计划的方式控制器运行时间点及重复运行的方式，换言之，CronJob可以在特定的时间点反复去执行Job任务。

- 应用场景
  - 数据备份

  - 清理日志

- CronJob的资源清单：

```yaml
apiVersion: batch/v1beta1 # 版本号
kind: CronJob # 类型
metadata: # 元数据
  name:  # 名称
  namespace:  #命名空间
  labels:
    controller: cronjob
spec: # 详情描述
  schedule:                  #cron格式的作业调度运行时间点，用于控制任务任务时间执行
  concurrencyPolicy:            #并发执行策略
  failedJobsHistoryLimit:       #为失败的任务执行保留的历史记录数，默认为1
  successfulJobsHistoryLimit:   #为成功的任务执行保留的历史记录数，默认为3
  jobTemplate:        #job控制器模板，用于为cronjob控制器生成job对象，下面其实就是job的定义
    metadata: {}
    spec:
      completions: 1     #指定Job需要成功运行Pod的总次数，默认为1
      parallelism: 1     #指定Job在任一时刻应该并发运行Pod的数量，默认为1
      activeDeadlineSeconds: 30   #指定Job可以运行的时间期限，超过时间还没结束，系统将会尝试进行终止
      backoffLimit: 6             #指定Job失败后进行重试的次数，默认为6
      template:               #模板，当副本数量不足时，会根据下面的模板创建Pod模板
        spec:
          restartPolicy: Never     #重启策略只能设置为Never或OnFailure
          containers:
            - name: counter
              image: busybox:1.30
              command: [ "/bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1;do echo $i;sleep 20;done" ]
```

- schedule：cron表达式，用于指定任务的执行时间。
  - */1  *  *  *  *：表示分  时  日  月  周。
  - 分钟的值从0到59。

  - 小时的值从0到23。

  - 日的值从1到31。

  - 月的值从1到12。

  - 星期的值从0到6，0表示星期日。

  - 多个时间可以用逗号隔开，范围可以用连字符给出：* 可以作为通配符，/表示每...

- concurrencyPolicy：并发执行策略
  - Allow：运行Job并发运行（默认）。

  - Forbid：禁止并发运行，如果上一次运行尚未完成，则跳过下一次运行。

  - Replace：替换，取消当前正在运行的作业并使用新作业替换它。

- 案例

```yaml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: pc-cronjob
  namespace: dev
  labels:
    controller: cronjob
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    metadata:
    spec:
      template:
         spec:
           restartPolicy: OnFailure
           containers:
             - name: timing
               image: busybox:latest
               command: ["echo","hello k8s cronjob!"]
```

```shell
#查看cronjob 任务pod
kubectl get pod -n dev
```

![image-20220316104124649](http://oss.linuxtxc.com/images/image-20220316104124649.png)

##### 5.6.StatefulSet（有状态服务）

- 无状态应用：
  - 认为Pod都是一样的。
  - 没有顺序要求。
  - 不用考虑在哪个Node节点上运行。
  - 随意进行伸缩和扩展。

- 有状态应用：
  - 有顺序的要求。
  - 认为每个Pod都是不一样的。
  - pod有序的部署，扩容，删除和停止
  - pod分配有个稳定的且唯一的网络标识
  - pod分配有个独享的存储

- StatefulSet是Kubernetes提供的管理有状态应用的负载管理控制器。
- StatefulSet部署需要HeadLinessService（无头服务）。

为什么需要HeadLinessService（无头服务）？

- 在用Deployment时，每一个Pod名称是没有顺序的，是随机字符串，因此是Pod名称是无序的，但是在StatefulSet中要求必须是有序 ，每一个Pod不能被随意取代，Pod重建后pod名称还是一样的。
- 而Pod IP是变化的，所以是以Pod名称来识别。Pod名称是Pod唯一性的标识符，必须持久稳定有效。这时候要用到无头服务，它可以给每个Pod一个唯一的名称 。

-  StatefulSet常用来部署RabbitMQ集群、Zookeeper集群、MySQL集群、Eureka集群等。

- 案例模板

```yaml
apiVersion: v1
kind: Service
metadata:
  name: service-headliness
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: None # 将clusterIP设置为None，即可创建headliness Service
  type: ClusterIP
  ports:
    - port: 80 # Service的端口
      targetPort: 80 # Pod的端口
---

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: pc-statefulset
  namespace: dev
spec:
  replicas: 3
  serviceName: service-headliness
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
        - name: nginx
          image: nginx:1.17.1
          ports:
            - containerPort: 80
```

- Deployment和StatefulSet的区别：Deployment没有唯一标识而StatefulSet有唯一标识。

- StatefulSet的唯一标识是根据主机名+一定规则生成的。
- StatefulSet的唯一标识是`statefulset名称-编号（主机名）.headless Service名称.命名空间.svc.cluster.local`。
- pod独享存储：statefulset的存储卷使用volumeClaimTemplate 创建，称为卷申请模板，当VolumeClaimTemplate 创建 一个PersistentVolume时，同时也会为每个pod分配并创建一个编号的PVC，每个PVC绑定

###### 5.6.1.StatefulSet的金丝雀发布

- StatefulSet支持两种更新策略：OnDelete和RollingUpdate（默认），其中OnDelete表示删除之后才更新，RollingUpdate表示滚动更新

```yaml
apiVersion: v1
kind: Service
metadata:
  name: service-headliness
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: None # 将clusterIP设置为None，即可创建headliness Service
  type: ClusterIP
  ports:
    - port: 80 # Service的端口
      targetPort: 80 # Pod的端口
---

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: pc-statefulset
  namespace: dev
spec:
  replicas: 3
  type: RollingUpdate
  updateStrategy:
    rollingUpdate:
    partition: 2   #表示从第2个分区开始更新，默认是0
  serviceName: service-headliness  #headliness Service名称
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
        - name: nginx
          image: nginx:1.17.1
          ports:
            - containerPort: 80			  			
```



----

#### 6.k8s 的service服务

- 在kubernetes中，Pod是应用程序的载体，我们可以通过Pod的IP来访问应用程序，但是Pod的IP地址不是固定的，这就意味着不方便直接采用Pod的IP对服务进行访问。

- 为了解决这个问题，kubernetes提供了为一组pod提供负载均衡，并且提供一个统一的入口地址，通过访问Service的入口地址就能访问到后面的Pod服务。

- Service在很多情况下只是一个概念，真正起作用的其实是kube-proxy服务进程，每个Node节点上都运行了一个kube-proxy的服务进程。当创建Service的时候会通过API Server向etcd写入创建的Service的信息，而kube-proxy会基于监听的机制发现这种Service的变化，然后它会将最新的Service信息转换为对应的访问规则。

```shell
# 10.97.97.97:80 是service提供的访问入口
# 当访问这个入口的时候，可以发现后面有三个pod的服务在等待调用，
# kube-proxy会基于rr（轮询）的策略，将请求分发到其中一个pod上去
# 这个规则会同时在集群内的所有节点上都生成，所以在任何一个节点上访问都可以。
[root@k8s-node1 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
 -> RemoteAddress:Port  Forward Weight ActiveConn InActConn
 TCP 10.97.97.97:80 rr
  -> 10.244.1.39:80   Masq  1  0  0
  -> 10.244.1.40:80   Masq  1  0  0
  -> 10.244.2.33:80   Masq  1  0  0
```

- kube-proxy支持的两种工作模式：
  - iptables模式：
    - iptables模式下，kube-proxy为Service后端的每个Pod创建对应的iptables规则，直接将发向Cluster IP的请求重定向到一个Pod的IP上。

    - 该模式下kube-proxy不承担四层负载均衡器的角色，只负责创建iptables规则。该模式的优点在于较userspace模式效率更高，但是不能提供灵活的LB策略，当后端Pod不可用的时候无法进行重试。

  - ipvs模式：
    - ipvs模式和iptables类似，kube-proxy监控Pod的变化并创建相应的ipvs规则。ipvs相对iptables转发效率更高，除此之外，ipvs支持更多的LB算法。

- 开启ipvs（必须安装ipvs内核模块，否则会降级为iptables）：

```shell
kubectl edit cm kube-proxy -n kube-system
```

![编辑kube-proxy.gif](http://cdn.nlark.com/yuque/0/2021/gif/513185/1609904265505-1136f319-e955-4b3a-a5a0-fa830b8f3630.gif)

```shell
#重新构建所有节点的kube-proxy pod
kubectl delete pod -l k8s-app=kube-proxy -n kube-system

##查看ipvs模块是否开启成功
ipvsadm -Ln

##二进制安装修改ipvs模式（配置文件路径根据实际安装目录为准）
# vi kube-proxy-config.yml
mode: ipvs
ipvs:
  scheduler: "rr"
  
#重启kube-proxy服务
systemctl restart kube-proxy
```

##### 6.1. Service类型

- Service的资源清单：

```yaml
apiVersion: v1 # 版本
kind: Service # 类型
metadata: # 元数据
  name: # 资源名称
  namespace: # 命名空间
spec:
  selector: # 标签选择器，用于确定当前Service代理那些Pod
    app: nginx
  type: NodePort # Service的类型，指定Service的访问方式
  clusterIP:  #虚拟服务的IP地址，不写会默认生成
  sessionAffinity: # session亲和性，支持ClientIP、None两个选项，默认值为None，ClientIP，即来自同一个客户端发起的所有请求都会转发到固定的一个Pod上
  ports: # 端口信息
    - port: 8080 # Service端口
      protocol: TCP # 协议
      targetPort : # Pod端口
      nodePort:  # 主机端口
```



###### 6.1.1.Endpoint

- service关联pod一种资源

- Endpoint是kubernetes中的一个资源对象，存储在etcd中，用来记录一个service对应的所有Pod的访问地址，它是根据service配置文件中的selector描述产生的。

- 一个service由一组Pod组成，这些Pod通过Endpoints暴露出来，Endpoints是实现实际服务的端点集合。换言之，service和Pod之间的联系是通过Endpoints实现的。

```shell
#查看service 的详细信息
kubectl describe svc nginx-service -n app
```

![image-20220320182429946](http://oss.linuxtxc.com/images/image-20220320182429946.png)

- 查看endpoint

```
 kubectl get  endpoints -n web
```

![image-20220608151612784](http://oss.linuxtxc.com/images/image-20220608151612784.png)

spec.type的说明：

- ClusterIP：默认值，它是kubernetes系统自动分配的虚拟IP，只能在集群内部访问。

- NodePort：将Service通过指定的Node上的端口暴露给外部，通过此方法，就可以在集群外部访问服务。

- LoadBalancer：使用外接负载均衡器完成到服务的负载分发，注意此模式需要外部云环境的支持。

- ExternalName：把集群外部的服务引入集群内部，直接使用。

###### 6.1.2.ClusterIP

- 只允许集群内部访问

```yaml
apiVersion: v1 
kind: Service 
metadata: 
  name: nginx-service
  namespace: app
spec:
  selector: # 标签选择器，用于确定当前Service代理那些Pod
    app: nginx
  clusterIP: 10.0.0.133 # service的IP地址，如果不写，默认会生成一个
  type: ClusterIP
  ports: # 
    - port: 80 # Service端口
      protocol: TCP 
      targetPort : 80
```



###### 6.1.3.HeadLiness

- 这类Service不会分配Cluster IP，如果想要访问Service，只能通过Service的域名进行查询。

- 创建service-headliness.yaml文件，内容如下：

```yaml
apiVersion: v1
kind: Service
metadata:
  name: service-headliness
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: None # 将clusterIP设置为None，即可创建headliness Service
  type: ClusterIP
  ports:
  - name: http
    port: 80 # Service的端口
    targetPort: 80 # Pod的端口
  ports:
  - name: https
    port: 443 # Service的端口
    targetPort: 443 # Pod的端口
```

###### 6.2.4.NodePort

- NodePort的工作原理就是将Service的端口映射到Node的一个端口上，然后就可以通过`NodeIP:NodePort`来访问Service了。

- 创建service-nodeport.yaml文件，内容如下：

```yaml
apiVersion: v1
kind: Service
metadata:
  name: service-nodeport
  namespace: dev
spec:
  selector:
    app: nginx-pod
  type: NodePort # Service类型为NodePort
  ports:
    - port: 80 # Service的端口
      targetPort: 80 # Pod的端口
      nodePort: 30002 # 指定绑定的node的端口（默认取值范围是30000~32767），如果不指定，会默认分配
```

- 查看Service：

```yaml
kubectl get svc service-nodeport -n dev -o wide
```



###### 6.1.5.LoadBalancer

- LoadBalancer和NodePort很相似，目的都是向外部暴露一个端口，区别在于LoadBalancer需要在集群的外部再配置一个负载均衡设备，外部服务发送到这个设备上的请求，会被设备负载之后转发到集群中。
- 创建service-LoadBalancer.yaml,内容如下

```yaml
apiVersion: v1
kind: Service
metadata:
  name: service-nodeport
  namespace: web
spec:
  selector:
    app: web-deployment
  type: LoadBalancer # Service类型为NodeBalancer
  ports:
    - port: 80 # Service的端口
      targetPort: 80 # Pod的端口
```



###### 6.1.6. ExternalName

- ExternalName类型的Service用于引入集群外部的服务，它通过externalName属性指定一个服务的地址，然后在集群内部访问此Service就可以访问到外部的服务了。

- 创建service-externalname.yaml文件，内容如下：

```yaml
apiVersion: v1
kind: Service
metadata:
  name: test-external
  namespace: app
spec:
  type: ExternalName
  externalName: 192.168.2.214
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 80 #目标端口
```

###### 6.1.7.k8s 接入外部数据库

- 集群内部访问外部数据库或者中间件一般采用endpoints与service关联方式映射。

- 创建endpoints-mysql.yaml文件，内容如下：

```yaml
apiVersion: v1
kind: Endpoints
metadata:
  name: mysql-214
  namespace: app
subsets:
  - addresses:
    - ip: 192.168.2.214
    ports:
    - port: 16303
```

- 创建 service-mysql.yaml文件，内容如下：

```yaml
apiVersion: v1 
kind: Service 
metadata: 
  name: mysql-214   #要与endpoints 的名称一样
  namespace: app
spec:
  type: ClusterIP
  ports: 
    - port: 16303 # Service端口
      protocol: TCP 
      targetPort : 16303
```



###### 6.1.8.部署案例

- 创建nginx-deployment.yaml 文件

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: app
spec:
  replicas: 1
  strategy:  #策略 
    type: RollingUpdate 
    rollingUpdate:  #滚动更新 
      maxSurge: 30%  #最大额外可以存在的副本数，可以为百分比，也可以为整数； 默认25%
      maxUnavailable: 30% #最大不可用状态的 ；Pod 的最大值，可以为百分比，也可以为整数，默认25% 
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.17.3
          ports:
            - containerPort: 80
```

- - 创建 nginx-service-nodeport.yaml

```yaml
apiVersion: v1
kind: Service
metadata:
  name: service-nodeport
  namespace: app
spec:
  selector:
    app: nginx
  type: NodePort # Service类型为NodePort
  ports:
    - port: 80 # Service的端口
      targetPort: 80 # Pod的端口
      nodePort: 30082 # 指定绑定的node的端口（默认取值范围是30000~32767），如果不指定，会默认分配
```

- 测试访问

```yaml
#查看service
kubectl get svc service-nodeport -n app -o wide
或者
kubectl describe service service-nodeport -n app
```

![image-20220324160508048](http://oss.linuxtxc.com/images/image-20220324160508048.png)

###### 6.1.9.pod使用主机网络

- 使用hostNetwork: true 参数

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      # 使用主机网络
      hostNetwork: true
      # 该设置是使POD使用k8s的dns，dns配置在/etc/resolv.conf文件中
      # 如果不加，pod默认使用所在宿主主机使用的DNS，这样会导致容器内不能通过service name访问k8s集群中其他POD
      dnsPolicy: ClusterFirstWithHostNet
      containers:
      - name: nginx
        image: nginx:1.17.9
        ports:
        - name: metrics
          # 如果hostNetwork: true，hostPort必须跟containerPort一样，所以hostPort一般不写，端口也是占用宿主机上的端口。
          hostPort: 80
          containerPort: 80
```



##### 6.2.ingress 资源

- Service对集群之外暴露服务的主要方式有两种：NodePort和LoadBalancer，但是这两种方式，都有一定的缺点：
  - NodePort方式的缺点是会占用很多集群机器的端口，那么当集群服务变多的时候，这个缺点就愈发明显。

  - LoadBalancer的缺点是每个Service都需要一个LB，浪费，麻烦，并且需要kubernetes之外的设备的支持。

- 基于这种现状，kubernetes提供了Ingress资源对象，Ingress只需要一个NodePort或者一个LB就可以满足暴露多个Service的需求。

###### 6.2.1安装ingress需要的环境

```shell
#wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.1/deploy/static/provider/cloud/deploy.yaml

#项目地址
#https://kubernetes.github.io/ingress-nginx/deploy

#修改镜像地址
image: k8s.gcr.io/ingress-nginx/controller:v1.1.1@sha256:d8196e3bc1e72547c5dec66d6556c0ff92a23f6d0919b206be170bc90d5f9185
修改成：
image: registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller:v1.1.1

image: k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1@sha256:64d8c73dca984af206adf9d6d7e46aa550362b1d7a01f3a0a91b20cc67868660
修改成：
image: registry.cn-hangzhou.aliyuncs.com/google_containers/kube-webhook-certgen:v1.1.1


[root@k8s_master ~]# wget http://oss.linuxtxc.com/ingress-nginx.zip
[root@k8s_master ~]# unzip ingress-nginx.zip
[root@k8s_master ~]# kubectl apply -f  deploy.yaml
```

![image-20220611171810086](http://oss.linuxtxc.com/images/image-20220611171810086.png)



###### 6.2.2.Http代理

- 为服务创建 service，yaml文件内容如下：

```yaml
apiVersion: v1 
kind: Service 
metadata: 
  name: nginx-service
  namespace: app
spec:
  selector: # 标签选择器，用于确定当前Service代理那些Pod
    app: nginx
  type: ClusterIP
  ports: # 
  - name: http 
    port: 80 # Service端口
    protocol: TCP 
    targetPort : 80
  - name: https
    port: 443 # Service端口
    protocol: TCP 
    targetPort : 443
---

apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
  namespace: app
spec:
  selector:
    app: tomcat
  type: ClusterIP
  ports:
  - port: 8080
    targetPort: 8080
```

- 创建ingress-http.yaml文件，内容如下：

```yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-http
  namespace: app
spec:
  rules:
  - host: nginx.linuxtxc.com
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx-service
          servicePort: 80  #service端口
  - host: tomcat.linuxtxc.com
    http:
      paths:
      - path: /
        backend:
          serviceName: tomcat-service
          servicePort: 8080
```

- kubernetes1.19版本之后 ingress配置yaml

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
  namespace: app
spec:
  ingressClassName: nginx
  rules:
  - host: "nginx.linuxtxc.com"
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80
```



- 查看ingress端口：

```shell
kubectl get ing ingress-http -n app
```

![image-20220411203539747](http://oss.linuxtxc.com/images/image-20220411203539747.png)

```shell
#查看访问ingress端口
kubectl get service -n ingress-nginx
```

![image-20220411203347463](http://oss.linuxtxc.com/images/image-20220411203347463.png)

- 访问URL：http://tomcat.linuxtxc.com:32443（可以在本地hosts文件中添加解析访问）

![image-20220411203156937](http://oss.linuxtxc.com/images/image-20220411203156937.png)



###### 6.2.3.修改ingress访问端口

```shell
#查看ingress-controller 的pod部署在哪个节点上
kubectl get pod -n ingress-nginx -o wide
```

![image-20220611164948163](http://oss.linuxtxc.com/images/image-20220611164948163.png)

- 修改ingress配置，并将上面nginx.linuxtxc.com 域名指向node01 公网IP

```shell
kubectl edit deployment.apps/ingress-nginx-controller -n ingress-nginx

#添加 --http-port=83
```

![image-20220611171040476](http://oss.linuxtxc.com/images/image-20220611171040476.png)

```shell
#删除旧的ingress-nginx-controller pod ，让其自动重新创建
kubectl delete pod ingress-nginx-controller-6bc4ff465c-pmjdr -n ingress-nginx
```

- 访问url：http://nginx.linuxtxc.com:83

![image-20220611173000581](http://oss.linuxtxc.com/images/image-20220611173000581.png)



###### 6.2.4.Https代理

```shell
为ingress 配置https:
 (1)openssl req -newkey rsa:4096 -nodes -sha256 -keyout /opt/k8s/https/linuxtxc.com.key -x509 -out /opt/k8s/https/linuxtxc.com.crt -subj /C=CN/ST=BJ/L=BJ/O=DEVOPS/CN=linuxtxc.com  -days 3650
 
 (2)kubectl create secret tls tls-linuxtxc.com --key=/opt/k8s/https/linuxtxc.com.key --cert=/opt/k8s/https/linuxtxc.com.crt -n app
 
 req 　　  产生证书签发申请命令
-newkey  生成新私钥
rsa:4096  生成秘钥位数
-nodes   表示私钥不加密
-sha256  使用SHA-2哈希算法
-keyout  将新创建的私钥写入的文件名
-x509 　　签发X.509格式证书命令。X.509是最通用的一种签名证书格式。
-out 指定要写入的输出文件名
-subj    指定用户信息
-days    有效期
```

- 创建ingress-https.yaml

```yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata: 
  name: ingress-https
  namespace: app
spec:
  tls:
  - hosts:
    - nginx.linuxtxc.com
    - tomcat.linxtxc.com
    secretName: tls-linuxtxc.com    ## 指定秘钥
  rules:
  - host: nginx.linuxtxc.com
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx-service
          servicePort: 80
  - host: tomcat.linuxtxc.com
    http:
      paths:
      - path: /
        backend:
          serviceName: tomcat-service
          servicePort: 8080
```

- kubernetes1.19版本之后 ingress的yaml配置

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
spec:
  tls:
  - hosts:
    - nginx.linuxtxc.com
    secretName: tls-linuxtxc.com    ## 指定秘钥
  ingressClassName: nginx
  rules:
  - host: "nginx.linuxtxc.com"
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80
```

- 查看ingress  https访问端口

```shell
kubectl get service -n ingress-nginx
```

![image-20220613195734911](http://oss.linuxtxc.com/images/image-20220613195734911.png)

- 修改https 访问端口

```shell
kubectl edit deployment.apps/ingress-nginx-controller -n ingress-nginx
```

![image-20220613200814699](http://oss.linuxtxc.com/images/image-20220613200814699.png)

- 删除旧的ingress-controller  的pod，让其重新生成

```shell
 kubectl delete pod ingress-nginx-controller-764766d96f-mtpmd -n ingress-nginx
```

- 重新使用修改的端口访问

![image-20220613201241842](http://oss.linuxtxc.com/images/image-20220613201241842.png)

###### 6.2.5.ingress个性化配置

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
  annotations:
    nginx.ingress.kubernetes.io/proxy-connect-timeout: 600s
    nginx.ingress.kubernetes.io/proxy-send-timeout: 600s
    nginx.ingress.kubernetes.io/proxy-read-timeout: 600s
    nginx.ingress.kubernetes.io/proxy-body-size: 8m           #客户端请求正文的最大允许大小
    nginx.ingress.kubernetes.io/proxy-buffering: "on"         #打开代理缓冲
    nginx.ingress.kubernetes.io/proxy-buffers-number: "4"     #代理缓冲区数
    nginx.ingress.kubernetes.io/proxy-buffer-size: "8k"       #代理缓冲区大小
    nginx.ingress.kubernetes.io/proxy-max-temp-file-size: "1024m"    #代理最大临时文件大小
    #nginx.ingress.kubernetes.io/proxy-http-version: "1.0"     #代理http版本
    #nginx.ingress.kubernetes.io/connection-proxy-header: "keep-alive"  #连接代理的标头
    #nginx.ingress.kubernetes.io/enable-access-log: "false"   #启用访问日志
    #nginx.ingress.kubernetes.io/enable-rewrite-log: "true"    #启用日志重写
    #nginx.ingress.kubernetes.io/x-forwarded-prefix: "/"   #X-Forwarded-Prefix标头，匹配/之后对接下来的进行操作
    #nginx.ingress.kubernetes.io/rewrite-target: https://www.baidu.com  #重定向
    #nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"   #后端连接协议
    #nginx.ingress.kubernetes.io/permanent-redirect: https://www.baidu.com #永久重定向（301）
    #nginx.ingress.kubernetes.io/whitelist-source-range: 220.167.31.8i #允许什么ip访问
    nginx.ingress.kubernetes.io/configuration-snippet: |   #自定义配置
      if ($http_user_agent ~* '(iphone)') {
          rewrite  ^/(.*) https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/annotations.md break;
      }
spec:
  tls:
  - hosts:
    - nginx.linuxtxc.com
    secretName: tls-linuxtxc.com    ## 指定秘钥
  ingressClassName: nginx
  rules:
  - host: "nginx.linuxtxc.com"
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80
```

###### 6.2.6.ingress-nginx 高可用方案

- 通过DaemonSet+NodeSelector实现将副本在特定节点上运行，同时使用宿主机网络，可以在访问ingress时不需要加NodePort端口

```shell
#将ingress-controller yaml文件中的Deployment改为 DaemonSet,replicas注释掉，使用nodeSelector调度pod到指定的node上，还可以使用keepalived配置VIP，在负载均衡器上配置VIP地址
vim  deploy.yaml

#给节点打上标签
kubectl label  node  k8s-node01  ingress-nginx=true
kubectl label  node  k8s-node02  ingress-nginx=true
```

- 测试直接使用域名访问

##### 6.3.NetworkPolicy（网络策略）

- 是一种关于 Pod 间及与其他网络端点间所允许的通信规则的规范。

```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:           #networkpolicy规约中包含了在一个命名空间中定义特定网络策略所需的所有信息
  podSelector:   #每个networkpolicy都包含一个podselector，对有其标签的pod（role:db），没有的话就对应所有命名空间下所有的pod
    matchLabels:
      role: db    #标签
  policyTypes:   #每个networkpolicy都包含一个policytype列表，包含ingress和egress
 - Ingress    #定义所选择的pod入站规则，如果此字段为空则不允许任何入站流量   
 - Egress      #定义所选择的pod出战规则，如果此字段为空则不允许任何出站流量   
  ingress:  
 - from:
    - ipBlock:  #针对特定的ip cidr范围设置策略，如果设置此字段，则不可以设置其他字段
        cidr: 172.17.0.0/16   #cidr是ip范围，允许这个范围内的ip访问networkpolicy的podselector匹配的pod集合
        except:                #except是ip范围，不允许这个范围内的ip访问networkpolicy的podselector匹配的pod集合
        - 172.17.1.0/24
    - namespaceSelector:  #这将选择特定的命名空间，应将所有的pod用做输入源或者输出目的地
        matchLabels:
          project: myproject
    - podSelector:    #这将与在networkpolicy相同的命名空间中选择带有特定标签的pod（role:frontend），应将其允许作为入口源或者出口目的地
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
 - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978
```



