kubernetes完整学习笔记
markdown文档笔记下载地址:https://install-data.oss-cn-hongkong.aliyuncs.com/%E7%AC%94%E8%AE%B0/kubernetes%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0.md
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.环境准备
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.sh2)让脚本开机自启:
[root@master ipvs]# lsmod |grep ip_vs
ip_vs_wrr 12697 0
ip_vs_rr 12600 0
ip_vs_sh 12688 0
ip_vs 145497 6 ip_vs_rr,ip_vs_sh,ip_vs_wrr
nf_conntrack 133095 9 ip_vs,nf_nat,nf_nat_ipv4,nf_nat_ipv6,xt_conntrack,nf_nat_masquerade_ipv4,nf_conntrack_netlink,nf_conntrack_ipv4,nf_conntrack_ipv6
libcrc32c 12644 4 xfs,ip_vs,nf_nat,nf_conntrack[root@master ipvs]# echo "bash /opt/ipvs/ipvs.sh" >>/etc/rc.local
[root@master ipvs]# chmod +x /etc/rc.local
1.2.安装docker(每台都执行)
yum -y install yum-utils device-mapper-persistent-data lvm2yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repoyum-config-manager --enable docker-ce-edgeyum install -y docker-ce-19.03.9-3.el7 docker-ce-cli-19.03.9-3.el7 containerd.io docker-composesystemctl enable docker
systemctl start docker#配置镜像加速(阿里云镜像站)
cat > /etc/docker/daemon.json << EOF
{"registry-mirrors": ["https://b9pmyelo.mirror.aliyuncs.com"],"exec-opts": ["native.cgroupdriver=systemd"]
}
EOF
1.3.安装kubeadm和kubelet(每台都执行)
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
EOFyum -y install kubelet-1.19.0-0.x86_64 kubeadm-1.19.0-0.x86_64 kubectl-1.19.0-0.x86_64systemctl enable kubelet
1.4.使用kubeadm部署master
kubeadm init --kubernetes-version=1.19.0 --apiserver-advertise-address=172.16.8.124 --service-cidr=10.64.0.0/24 --pod-network-cidr=10.244.0.0/16 (master上执行,--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加入集群
#当主节点启动完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
#查看组件健康情况
kubectl get cs
- 解决方法
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网络插件
#主节点执行
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/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://docs.projectcalico.org/manifests/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
#安装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# 安装Compose命令补全工具
curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose version --short)/contrib/completion/bash/docker-compose -o /etc/bash_completion.d/docker-compose#重启docker
systemctl daemon-reload
systemctl restart docker#安装harbor
wget https://storage.googleapis.com/harbor-releases/release-1.8.0/harbor-offline-installer-v1.8.0.tgz
tar xvf harbor-offline-installer-v1.8.0.tgz
cd harbor/
vim harbor.yml (修改默认端口)
./prepare
./install.sh#登录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)
1.9.安装coredns
#官方模板: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集群
(master,node都执行)
kubeadm reset
rm -rf /etc/cni/net.d(master上执行 )
kubeadm init --kubernetes-version=1.19.0 --apiserver-advertise-address=192.168.3.124 --service-cidr=10.64.0.0/24 --pod-network-cidr=10.244.0.0/16
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
vim /etc/kubernetes/manifests/kube-controller-manager.yaml (注释掉 - --port=0)
vim /etc/kubernetes/manifests/kube-scheduler.yaml (注释掉 - --port=0)
systemctl start kubelet (node 上执行)
kubeadm reset
kubeadm join 192.168.3.124:6443 --token f62e1g.exfdrr8bg2fxpdmu \--discovery-token-ca-cert-hash sha256:7e4762cccce2e8306b6635cbc2091eae34bbf33577276413521a343c49fc026a
1.11.切换容器引擎为Containerd
#启动模块(默认已经启用)
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镜像配置加速地址设置为阿里云镜像仓库地址
vi /etc/containerd/config.toml[plugins."io.containerd.grpc.v1.cri"]sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.2" ...[plugins."io.containerd.grpc.v1.cri".containerd.default_runtime.options]SystemdCgroup = true...[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]endpoint = ["https://b9pmyelo.mirror.aliyuncs.com"]#重启containerd
systemctl restart containerd
- 配置kubelet使用containerd
vi /etc/sysconfig/kubelet
KUBELET_EXTRA_ARGS=--container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock --cgroup-driver=systemdsystemctl restart kubelet#验证
kubectl get node -o wide
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mLgnwFu3-1654139062211)(C:\Users\linux\AppData\Roaming\Typora\typora-user-images\image-20220531181711832.png)]
- 管理容器工具
containerd提供了ctr命令行工具管理容器,但功能比较简单,所以一般会用crictl工具检查和调试容器。
项目地址:https://github.com/kubernetes-sigs/cri-tools/
设置crictl连接containerd:
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 |
2.kubernetes 资源管理
2.1.kubectl create
kubectl create namespace dev #创建一个namespacekubectl create service clusterip ngx-dep --tcp=80:80 #命令行创建servicekubectl 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模板
kubectl create deployment deploy-nginx --image=nginx:1.17.2 --replicas=3 -n dev --dry-run=client -o yaml > deploy-nginx.yaml
2.2.kubectl api-resources
kubectl api-resources #查看k8s所有的资源以及对应的apiversionkubectl api-versions #查看k8s所有 版本号
2.3.kubectl explain
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(每周)。
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
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
#进入pod容器中
kubectl exec -it nginx-deployment-798444d598-87hlk -n web /bin/sh
2.7.kubectl get
kubectl get cs #获取集群健康状态kubectl get csr #查看申请加入kubernetes集群的token信息kubectl get endpoints #获取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
kubectl edit cm kubeadm-config -n kube-system #cm,configMap;编辑configMap中的配置
2.9.kubectl logs
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 的合并日志
2.10.kubectl delete
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
kubectl autoscale deployment deployment-nginx --cpu-percent=60 --min=2 --max=10 #使用 Deployment “deployment-nginx”设定,使用默认的自动伸缩策略,指定目标CPU使用率,使其Pod数量在2到10之间。
2.12.kubectl patch
#使用(patch)补丁修改、更新资源的字段。
kubectl patch pod rc-nginx-2-kpiqt -p '{"metadata":{"labels":{"app":"nginx-3"}}}' #修改资源配置
2.13.kubectl replace
kubectl replace -f rc-nginx.yaml #根据yaml文件更新修改后配置资源,会停掉原来的资源,重新创建
2.14.kubectl scale
kubectl scale rc redis --replicas=3 -n web #操作pod 的扩容和缩容kubectl scale --replicas=2 -f redis-slave-deployment.yaml
2.15.kubectl rollout
status:显示当前升级状态
history:显示升级历史记录
pause:暂停版本升级过程
resume:继续已经暂停的版本升级过程
restart:重启版本升级过程
undo:回滚到上一级版本 (可以使用--to-revision,指定版本)
kubectl apply -f pc-deployment.yml --record (--record,记录操作记录,便于回滚)#查看历史版本
kubectl rollout history deployment -n app 更新镜像:
(1)通过kubectl set 命令去更改
kubectl set image deploy nginx-deployment nginx=nginx:1.15.2 -n app && 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#版本回滚到v1
kubectl rollout undo deploy nginx-deployment --to-revision=1 -n app
2.16.kubectl taint
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
kubectl run nginx --image=nginx:1.17.1 -n web #命令使用镜像创建容器
2.18.kubectl apply
#如果资源不存在,就创建,相当于kubectl create。
#如果资源存在,就更新,相当于kubectl patch。
kubectl apply -f nginx-pod.yaml -n web --record (--record,记录操作记录,便于回滚)
2.19.kubectl top
kubectl top pod/node -n dev #查看资源使用情况(需要安装metrics-server)
2.20.kubectl expose
#将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
#将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
3.kubernetes 的Pod 详解
pod:k8s 最小控制单元,其中包含一个pause容器(根容器),多个user 容器,提供服务,pod中容器共享网络,存储资源
pause容器作用:可以根据它判断pod的健康状态,同时在pause容器上设置IP,可以让同一个pod中的所有容器共用
3.1.Pod 所有参数配置
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,默认TCPenv: #容器运行前需设置的环境变量列表- name: string #环境变量名称value: string #环境变量的值resources: #资源限制和请求的设置limits: #资源限制的设置cpu: string #Cpu的限制,单位为core数memory: string #内存限制,单位可以为Mib/Gibrequests: #资源请求的设置cpu: string #Cpu请求,容器启动的初始可用数量memory: string #内存请求,容器启动的初始可用数量lifecycle: #生命周期钩子postStart: #容器启动后立即执行此钩子,如果执行失败,会根据重启策略进行重启preStop: #容器终止前执行此钩子,无论结果如何,容器都会终止livenessProbe: #对Pod内各容器健康检查的设置,当探测无响应几次后将自动重启该容器exec: #对Pod容器内检查方式设置为exec方式command: [string] #exec方式需要制定的命令或脚本httpGet: #对Pod内个容器健康检查方法设置为HttpGet,需要制定Path、portpath: stringport: numberhost: stringscheme: stringHttpHeaders:- name: stringvalue: stringtcpSocket: #对Pod内个容器健康检查方式设置为tcpSocket方式port: numberinitialDelaySeconds: 0 #容器启动完成后首次探测的时间,单位为秒timeoutSeconds: 0 #对容器健康检查探测等待响应的超时时间,单位秒,默认1秒periodSeconds: 0 #对容器监控检查的定期探测时间设置,单位秒,默认10秒一次successThreshold: 0failureThreshold: 0securityContext:privileged: falserestartPolicy: [Always | Never | OnFailure] #Pod的重启策略nodeName: <string> #设置NodeName表示将该Pod调度到指定到名称的node节点上nodeSelector: obeject #设置NodeSelector表示将该Pod调度到包含这个label的node上imagePullSecrets: #Pull镜像时使用的secret名称,以key:secretkey格式指定- name: stringhostNetwork: 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: stringpath: stringconfigMap: #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部name: stringitems:- key: stringpath: string
3.2.Pod资源限制
apiVersion: v1
kind: Pod
metadata:name: test-podnamespace: applabels:app: nginx
spec:containers:- name: nginx01 # 容器名称image: nginx:1.17.1 # 容器需要的镜像地址imagePullPolicy: IfNotPresent # 设置镜像拉取策略,Alawys;IfNotPresent;Neverports: # 端口设置- containerPort: 80 # 容器要监听的端口 (0~65536)protocol: TCP # 端口协议resources: # 资源配额limits: # 限制资源的上限cpu: "1" # CPU限制,单位是core数,0.1=100mmemory: "10Gi" # 内存限制requests: # 限制资源的下限cpu: "0.5" # CPU限制,单位是core数, 0.5=500mmemory: "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命令:在容器内执行一次命令。
……lifecycle:postStart: exec:command:- cat- /tmp/healthy
……
② tcpSocket:在当前容器尝试访问指定的socket。
…… lifecycle:postStart:tcpSocket:port: 8080
……
③ httpGet:在当前容器中向某url发起HTTP请求。
…… lifecycle:postStart:httpGet:path: / #URI地址port: 80 #端口号host: 192.168.109.100 #主机地址scheme: HTTP #支持的协议,http或者https
……
以exec方式为例,演示下钩子函数的使用,创建pod-hook-exec.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:name: pod-hook-execnamespace: applabels:app: nginx
spec:containers: # 容器配置- name: nginximage: nginx:1.17.1imagePullPolicy: IfNotPresentports:- name: nginx-portcontainerPort: 80protocol: TCPresources: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不会转发流量。
上面两种探针目前均支持三种探测方式:
- ① exec命令:在容器内执行一次命令,如果命令执行的退出码为0,则认为程序正常,否则不正常。
----------------------------
readinessProbe:exec:command:- cat- /etc/hostsinitialDelaySeconds: 5timeoutSeconds: 2successThreshold: 3failureThreshold: 2periodSeconds: 5
-----------------------------
- ② tcpSocket:将会尝试访问一个用户容器的端口,如果能够建立这条连接,则认为程序正常,否则不正常。
……livenessProbe:tcpSocket:port: 8080failureThreshold: 5 #检测失败5次表示未就绪initialDelaySeconds: 60 #初始化时间periodSeconds: 10 #检测间隔successThreshold: 1 #检查成功为2次表示就绪timeoutSeconds: 5 #检测失败1次表示未就绪
……
- ③ httpGet:调用容器内web应用的URL,如果返回的状态码在200和399之前,则认为程序正常,否则不正常。
------------------------ livenessProbe: #健康检查方式:[readinessProbe,livenessProbe,StartupProbe]httpGet: #请求方式path: /health #请求路径port: 8080 #请求端口scheme: HTTP ##请求协议failureThreshold: 5 #检测失败5次表示未就绪initialDelaySeconds: 60 #初始化时间periodSeconds: 10 #检测间隔successThreshold: 1 #检查成功为2次表示就绪timeoutSeconds: 5 #检测失败1次表示未就绪
----------------------------
- startupProbe : 启动探测,k8s1.16版本后新加的探测方式,用于判断容器内应用程序是否已经启动,如果配置了startuprobe,就会先禁用其他的探测,直到它成功为止。
----------------------------------
startupProbe: #健康检查方式:[readinessProbe,livenessProbe,StartupProbe]httpGet: #请求方式path: / #请求路径port: 8080 #请求端口scheme: HTTP #请求协议failureThreshold: 3 #检测失败3次表示未就绪periodSeconds: 10 #检测间隔successThreshold: 1 #检查成功为2次表示就绪timeoutSeconds: 1 #检测失败1次表示未就绪
----------------------------------
initialDelaySeconds # 容器启动后等待多少秒执行第一次探测。
timeoutSeconds # 探测超时时间。默认1秒,最小1秒。
periodSeconds # 执行探测的频率。默认是10秒,最小1秒。
failureThreshold # 连续探测失败多少次才被认定为失败。默认是3。最小值是1。
successThreshold # 连续探测成功多少次才被认定为成功。默认是1。
重启策略:
- Always:容器失效时,自动重启该容器,默认值。
- OnFailure:容器终止运行且退出码不为0时重启。
- Never:不论状态如何,都不重启该容器。
startupprobe+readinessProbe+ livenessProbe混合案例
apiVersion: v1 # 必选,API的版本号
kind: Pod # 必选,类型Pod
metadata: # 必选,元数据name: read-startup # 必选,符合RFC 1035规范的Pod名称labels: # 可选,标签选择器,一般用于过滤和区分Podapp: nginxrole: frontend # 可以写多个annotations: # 可选,注释列表,可以写多个app: nginx
spec: # 必选,用于定义容器的详细信息containers: # 必选,容器列表- name: read-startup # 必选,符合规范的容器名称image: nginx:latest # 必选,容器所用的镜像imagePullPolicy: Always # 可选,镜像拉取策略command: # 可选,容器启动执行的命令- nginx- -g- "daemon off;"workingDir: /usr/share/nginx/html # 可选,容器的工作目录ports: # 可选,容器需要暴露的端口号列表- name: http # 端口名称containerPort: 80 # 端口号protocol: TCP # 端口协议,默认TCPenv: # 可选,环境变量配置列表- name: TZ # 变量名value: Asia/Shanghai # 变量的值- name: LANGvalue: en_US.utf8readinessProbe:exec:command:- cat- /etc/hostsinitialDelaySeconds: 5timeoutSeconds: 2successThreshold: 3failureThreshold: 2periodSeconds: 5startupProbe:httpGet:path: /port: 80failureThreshold: 30periodSeconds: 10livenessProbe:httpGet:path: /healthzport: 80failureThreshold: 1periodSeconds: 10restartPolicy: Never #重启策略
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文件,内容如下:
apiVersion: v1
kind: Pod
metadata:name: pod-nodenamenamespace: weblabels:app: nginx
spec:containers: # 容器配置- name: nginximage: nginx:1.17.1imagePullPolicy: IfNotPresentports:- name: nginx-portcontainerPort: 80protocol: TCPnodeName: k8s-node1 #指定调度到k8s-node1节点上
nodeSelector:用于将Pod调度到添加了指定标签的Node节点上,它是通过kubernetes的label-selector机制实现的,换言之,在Pod创建之前,会由Scheduler使用MatchNodeSelector调度策略进行label匹配,找出目标node,然后将Pod调度到目标节点,该匹配规则是强制约束。
#给node节点添加标签
kubectl label node k8s-node1 node=web
kubectl label node k8s-node2 node=app
- 创建pod-nodeselector.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:name: pod-nodeselectornamespace: app
spec:containers: # 容器配置- name: nginximage: nginx:1.17.1imagePullPolicy: IfNotPresentports:- name: nginx-portcontainerPort: 80protocol: TCPnodeSelector: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的可选配置项:
pod.spec.affinity.nodeAffinityrequiredDuringSchedulingIgnoredDuringExecution #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, Ltweight #倾向权重,在范围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文件,内容如下:
apiVersion: v1
kind: Pod
metadata:name: pod-nodeaffinity-preferrednamespace: app
spec:containers: # 容器配置- name: nginximage: nginx:1.17.1imagePullPolicy: IfNotPresentports:- name: nginx-portcontainerPort: 80protocol: TCPaffinity: # 亲和性配置nodeAffinity: # node亲和性配置preferredDuringSchedulingIgnoredDuringExecution: # 先匹配node标签中有nodeenv=app或者nodeenv=xyy的node,优先调度到满足指定的规则的Node,相当于软限制 (倾向)- preference: # 一个节点选择器项,与相应的权重相关联matchExpressions:- key: nodeenvoperator: Invalues:- "app"- "xyy"weight: 50
下面演示preferredDuringSchedulingIgnoredDuringExecution(硬限制):
- 创建pod-nodeaffinity-preferred.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:name: pod-nodeaffinity-requirednamespace: dev
spec:containers: # 容器配置- name: nginximage: nginx:1.17.1imagePullPolicy: IfNotPresentports:- name: nginx-portcontainerPort: 80protocol: TCPaffinity: # 亲和性配置nodeAffinity: # node亲和性配置requiredDuringSchedulingIgnoredDuringExecution: #先匹配node标签中有nodeenv=app或者nodeenv=xyy的node,如果没有,则会调度失败,相当于硬规则,类似于定向调度nodeSelectorTerms: # 节点选择列表- matchExpressions:- key: nodeenv # 匹配存在标签的key为nodeenv的节点,并且value是"xxx"或"yyy"的节点operator: Invalues:- "app"- "yyy"
nodeAffinity的注意事项:
如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都满足,Pod才能运行在指定的Node上。
如果nodeAffinity指定了多个nodeSelectorTerms,那么只需要其中一个能够匹配成功即可。
如果一个nodeSelectorTerms中有多个matchExpressions,则一个节点必须满足所有的才能匹配成功。
如果一个Pod所在的Node在Pod运行期间其标签发生了改变,不再符合该Pod的nodeAffinity的要求,则系统将忽略此变化。
4.2.2.Pod亲和性(PodAffinity)
- PodAffinity的可选配置项:
pod.spec.affinity.podAffinityrequiredDuringSchedulingIgnoredDuringExecution #硬限制namespaces #指定参照pod的namespacetopologyKey #指定调度作用域labelSelector # 标签选择器matchExpressions #按节点标签列出的节点选择器要求列表(推荐)key #键values #值operator #关系符 支持In, NotIn, Exists, DoesNotExist.matchLabels #指多个matchExpressions映射的内容preferredDuringSchedulingIgnoredDuringExecution #软限制podAffinityTerm #选项namespaces #指定参照pod的namespacetopologyKey #指定调度作用域labelSelector # 标签选择器matchExpressions key # 键values # 值operatormatchLabels weight # 倾向权重,在范围1-100#topologyKey用于指定调度的作用域,例如:
#如果指定为kubernetes.io/hostname,那就是以Node节点为区分范围。
#如果指定为beta.kubernetes.io/os,则以Node节点的操作系统类型来区分。
创建Pod过程:
- 创建pod-podaffinity-requred.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:name: pod-podaffinity-requrednamespace: app
spec:containers: # 容器配置- name: nginximage: nginx:1.17.1imagePullPolicy: IfNotPresentports:- name: nginx-webcontainerPort: 80protocol: TCPaffinity: # 亲和性配置podAffinity: # Pod亲和性requiredDuringSchedulingIgnoredDuringExecution: # 硬限制- labelSelector:matchExpressions: # 该Pod必须和拥有标签app=nginx或者app=test的Pod在同一个Node上。- key: appoperator: Invalues:- "nginx"- "test"topologyKey: kubernetes.io/hostname
创建Pod过程:
- 创建pod-podaffinity- prefer.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:name: pod-podaffinity-prefernamespace: app
spec:containers: # 容器配置- name: nginx01image: nginx:1.17.1imagePullPolicy: IfNotPresentports:- name: nginx-appcontainerPort: 80protocol: TCPaffinity: # 亲和性配置podAffinity: # Pod亲和性preferredDuringSchedulingIgnoredDuringExecution: #软限制- weight: 50podAffinityTerm:labelSelector:matchExpressions: #先匹配拥有标签app=yyy或者app=test的Pod,如果有,则将pod放在匹配pod同一个Node上,如果没有则让Scheduler根据算法自动调度- key: appoperator: Invalues:- "yyy"- "test"topologyKey: kubernetes.io/hostname
4.2.3.Pod反亲和性(podAntiAffinity)
- 主要实现以运行的Pod为参照,让新创建的Pod和参照的Pod不在一个区域的功能。
apiVersion: v1
kind: Pod
metadata:name: pod-podantiaffinity-requrednamespace: app
spec:containers: # 容器配置- name: nginximage: nginx:1.17.1imagePullPolicy: IfNotPresentports:- name: nginx-portcontainerPort: 80protocol: TCPaffinity: # 亲和性配置podAntiAffinity: # Pod反亲和性requiredDuringSchedulingIgnoredDuringExecution: # 硬限制- labelSelector:matchExpressions:- key: appoperator: Invalues:- "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驱逐。
#设置污点
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 节点名称#给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通过容忍忽略拒绝
- 容忍的详细配置:
kubectl explain pod.spec.tolerations
.........key # 对应着要容忍的污点的键,空意味着匹配所有的键value # 对应着要容忍的污点的值operator # key-value的运算符,支持Equal(默认)和Existseffect # 对应污点的effect,空意味着匹配所有影响tolerationSeconds # 容忍时间, 当effect为NoExecute时生效,表示pod在Node上的停留时间
当operator为Equal的时候,如果Node节点有多个Taint,那么Pod每个Taint都需要容忍才能部署上去。
当operator为Exists的时候,有如下的三种写法:
容忍指定的污点,污点带有指定的effect:
容忍指定的污点,不考虑具体的effect:
容忍一切污点(慎用):
tolerations: # 容忍- key: "tag" # 要容忍的污点的keyoperator: Exists # 操作符effect: NoExecute # 添加容忍的规则,这里必须和标记的污点规则相同
tolerations: # 容忍- key: "tag" # 要容忍的污点的keyoperator: Exists # 操作符
tolerations: # 容忍- operator: Exists # 操作符
- 创建pod-toleration.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:name: pod-tolerationnamespace: dev
spec:containers: # 容器配置- name: nginximage: nginx:1.17.1imagePullPolicy: IfNotPresentports:- name: nginx-portcontainerPort: 80protocol: TCPtolerations: # 容忍- key: "tag" # 要容忍的污点的keyoperator: Equal # 操作符value: "test" # 要容忍的污点的valueeffect: NoExecute # 添加容忍的规则,这里必须和标记的污点规则相同
- 案例:
#给两个节点都打上污点
kubectl taint node 192.168.3.125 tag=test:NoSchedule
kubectl taint node 192.168.3.125 tag=deploy:NoSchedulevim pod-toleration-test.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-tolerationnamespace: dev
spec:containers: # 容器配置- name: nginximage: nginx:1.17.1imagePullPolicy: IfNotPresentports:- name: nginx-portcontainerPort: 80protocol: TCPtolerations: # 容忍- key: "tag" # 要容忍的污点的keyoperator: Equal # 操作符value: "test" # 要容忍的污点的valueeffect: NoSchedule # 添加容忍的规则,这里必须和标记的污点规则相同- key: "tag" # 要容忍的污点的keyoperator: Equal # 操作符value: "deploy" # 要容忍的污点的valueeffect: NoExecute # 添加容忍的规则,这里必须和标记的污点规则相同
5.kubernetes的Pod控制器
5.1.Deployment控制器
Deployment控制器并不直接管理Pod,而是通过管理ReplicaSet来间接管理Pod,即:Deployment管理ReplicaSet,ReplicaSet管理Pod。
Deployment的主要功能如下:
- 支持ReplicaSet的所有功能。
- 支持发布的停止、继续。
- 支持版本滚动更新和版本回退。
创建nginx-deployment.yaml文件,在spec下添加更新策略
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-deploymentnamespace: app
spec:replicas: 3revisionHistoryLimit: 10 #可保留的版本数minReadySeconds: 5 #在等待设置的时间后才进行升级strategy: #策略type: RollingUpdate rollingUpdate: #滚动更新,就是杀死一部分,就启动一部分,在更新过程中,存在两个版本的PodmaxSurge: 30% #最大额外可以存在的副本数,可以为百分比,也可以为整数; 默认25%maxUnavailable: 30% #最大不可用状态的 ;Pod 的最大值,可以为百分比,也可以为整数,默认25%selector:matchLabels:app: nginx#matchExpressions: # Expressions匹配规则# - {key: app, operator: In, values: [nginx-pod]}template:metadata:labels:app: nginxspec:containers:- name: nginximage: nginx:1.17.1imagePullpolicy: ifnotpresentports:- containerPort: 80
- 镜像更新
(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
- 查看rs,发现原来的rs依旧存在,只是Pod的数量变为0,而后又产生了一个rs,Pod的数量变为3
kubectl get rs -n app
- 查看历史版本
kubectl rollout history deployment -n app
- 版本回滚
kubectl rollout undo deploy nginx-deployment --to-revision=1 -n app
- 金丝雀发布(在一批新的Pod资源创建完成后立即暂停更新过程)
(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(用来收集集群中的资源使用情况。)
#wget https://github.com/kubernetes-sigs/metrics-server/archive/v0.3.6.tar.gz
wget https://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
- 安装安装metrics-server(v0.4.1)
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
- 查看metrics-server生成的Pod:
kubectl get pod -n kube-system
5.2.2.错误排查
- 查看资源使用情况报错
- 排查错误
systemctl status kube-apiserver -l #或者 journalctl -u kube-apiserver
metric-server安装要满足2个条件
在master节点要能访问metrics server pod ip(kubeadm部署默认已经满足该条件,二进制部署需注意要在master节点也部署node组件)
apiserver启用聚合层支持(kubeadm默认已经启用,二进制部署需自己启用)
因为我是二进制安装的,所以在master上安装node组件就可以了
kubectl top pod -n app
5.2.3.部署HPA案例
- 使用之前创建的nginx-deployment,使用kubectl edit 将副本数改为 1
- 创建 nginx-service.yaml
apiVersion: v1
kind: Service
metadata: name: nginx-servicenamespace: app
spec:selector: # 标签选择器,用于确定当前Service代理那些Podapp: nginx-deploymenttype: NodePort ports: # - port: 80 # Service端口protocol: TCP targetPort : 80nodePort: 30081
- 创建nginx-hpa.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/v1kind: Deploymentname: nginx-deployment
#使用命令创建HPA控制器
kubectl autoscale deployment nginx-hpa --cpu-percent=3 --min=1 --max=10
- targets为unknown 的原因
需要安装metrics-server
HPA对应的pod,需要设置request资源
#使用命令行设置pod 资源限制
kubectl set resources deployment/nginx-deployment --limits=cpu=800m
- 再次查看:
- 使用ab 命令访问测试
- 查看 HPA状态
5.3.DaemonSet
DaemonSet类型的控制器可以保证集群中的每一台(或指定)节点上都运行一个副本,一般适用于日志收集、节点监控等场景
DaemonSet控制器的特点:
- 每向集群中添加一个节点的时候,指定的Pod副本也将添加到该节点上。
- 当节点从集群中移除的时候,Pod也会被垃圾回收。
DaemonSet的资源清单:
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: # 选择器,通过它指定该控制器管理那些PodmatchLabels: # Labels匹配规则app: nginx-podmatchExpressions: # Expressions匹配规则- key: appoperator: Invalues:- nginx-podtemplate: # 模板,当副本数量不足时,会根据下面的模板创建Pod模板metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80
- 案例
apiVersion: apps/v1
kind: DaemonSet
metadata:name: node-exporternamespace: kube-systemlabels:k8s-app: node-exporter
spec:selector:matchLabels:app: node-exportertemplate:metadata:labels:app: node-exporterspec:containers:- image: prom/node-exportername: node-exporterports:- containerPort: 9100protocol: TCPname: http
5.4.Job
Job主要用于负责批量处理短暂的一次性任务。J可以保证指定数量的Pod执行完成。
Job的特点:
- 当Job创建的Pod执行成功结束时,Job将记录成功结束的Pod数量。
- 当成功结束的Pod达到指定的数量时,Job将完成执行。
Job的资源清单:
apiVersion: batch/v1 # 版本号
kind: Job # 类型
metadata: # 元数据name: # 名称namespace: #命名空间labels: # 标签controller: job
spec: # 详情描述completions: 1 # 指定Job需要成功运行Pod的总次数,默认为1parallelism: 1 # 指定Job在任一时刻应该并发运行Pod的数量,默认为1activeDeadlineSeconds: 30 # 指定Job可以运行的时间期限,超过时间还没结束,系统将会尝试进行终止backoffLimit: 6 # 指定Job失败后进行重试的次数,默认为6manualSelector: true # 是否可以使用selector选择器选择Pod,默认为falseselector: # 选择器,通过它指定该控制器管理那些PodmatchLabels: # Labels匹配规则app: counter-podmatchExpressions: # Expressions匹配规则- key: appoperator: Invalues:- counter-podtemplate: # 模板,当副本数量不足时,会根据下面的模板创建Pod模板metadata:labels:app: counter-podspec:restartPolicy: Never # 重启策略只能设置为Never或OnFailurecontainers:- name: counterimage: busybox:1.30command: ["/bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1;do echo $i;sleep 20;done"]
- Job案例
apiVersion: batch/v1 # 版本号
kind: Job # 类型
metadata: # 元数据name: pc-job # 名称namespace: dev #命名空间
spec: # 详情描述manualSelector: true # 是否可以使用selector选择器选择Pod,默认为falseparallelism: 1 ##指定job需要成功允许pods的次数。默认值:1completions: 1 #指定job 在任一时刻应该并发运行pod 的数量。默认值:1selector: # 选择器,通过它指定该控制器管理那些PodmatchLabels: # Labels匹配规则app: counter-podtemplate: # 模板,当副本数量不足时,会根据下面的模板创建Pod模板metadata:labels:app: counter-podspec:restartPolicy: Never # 重启策略只能设置为Never或OnFailurecontainers:- name: counterimage: busybox:1.30command: [ "/bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1;do echo $i;sleep 3;done" ]
#查看job运行情况
kubectl get job -n dev -w
5.5.CronJob(CJ)
CronJob控制器以Job控制器为其管控对象,并借助它管理Pod资源对象,Job控制器定义的作业任务在其控制器资源创建之后便会立即执行,但CronJob可以以类似Linux操作系统的周期性任务作业计划的方式控制器运行时间点及重复运行的方式,换言之,CronJob可以在特定的时间点反复去执行Job任务。
CronJob的资源清单:
apiVersion: batch/v1beta1 # 版本号
kind: CronJob # 类型
metadata: # 元数据name: # 名称namespace: #命名空间labels:controller: cronjob
spec: # 详情描述schedule: #cron格式的作业调度运行时间点,用于控制任务任务时间执行concurrencyPolicy: #并发执行策略failedJobsHistoryLimit: #为失败的任务执行保留的历史记录数,默认为1successfulJobsHistoryLimit: #为成功的任务执行保留的历史记录数,默认为3jobTemplate: #job控制器模板,用于为cronjob控制器生成job对象,下面其实就是job的定义metadata: {}spec:completions: 1 #指定Job需要成功运行Pod的总次数,默认为1parallelism: 1 #指定Job在任一时刻应该并发运行Pod的数量,默认为1activeDeadlineSeconds: 30 #指定Job可以运行的时间期限,超过时间还没结束,系统将会尝试进行终止backoffLimit: 6 #指定Job失败后进行重试的次数,默认为6template: #模板,当副本数量不足时,会根据下面的模板创建Pod模板spec:restartPolicy: Never #重启策略只能设置为Never或OnFailurecontainers:- name: counterimage: busybox:1.30command: [ "/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:替换,取消当前正在运行的作业并使用新作业替换它。
案例
apiVersion: batch/v1beta1
kind: CronJob
metadata:name: pc-cronjobnamespace: devlabels:controller: cronjob
spec:schedule: "*/1 * * * *"jobTemplate:metadata:spec:template:spec:restartPolicy: OnFailurecontainers:- name: timingimage: busybox:latestcommand: ["echo","hello k8s cronjob!"]
#查看cronjob 任务pod
kubectl get pod -n dev
5.6.StatefulSet(有状态服务)
无状态应用:
- 认为Pod都是一样的。
- 没有顺序要求。
- 不用考虑在哪个Node节点上运行。
- 随意进行伸缩和扩展。
有状态应用:
- 有顺序的要求。
- 认为每个Pod都是不一样的。
- 需要考虑在哪个Node节点上运行。
- 需要按照顺序进行伸缩和扩展。
- 让每个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集群等。
案例模板
apiVersion: v1
kind: Service
metadata:name: service-headlinessnamespace: dev
spec:selector:app: nginx-podclusterIP: None # 将clusterIP设置为None,即可创建headliness Servicetype: ClusterIPports:- port: 80 # Service的端口targetPort: 80 # Pod的端口
---apiVersion: apps/v1
kind: StatefulSet
metadata:name: pc-statefulsetnamespace: dev
spec:replicas: 3serviceName: service-headlinessselector:matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80
Deployment和StatefulSet的区别:Deployment没有唯一标识而StatefulSet有唯一标识。
StatefulSet的唯一标识是根据主机名+一定规则生成的。
StatefulSet的唯一标识是
主机名.headless Service名称.命名空间.svc.cluster.local
。
5.6.1.StatefulSet的金丝雀发布
- StatefulSet支持两种更新策略:OnDelete和RollingUpdate(默认),其中OnDelete表示删除之后才更新,RollingUpdate表示滚动更新
apiVersion: v1
kind: Service
metadata:name: service-headlinessnamespace: dev
spec:selector:app: nginx-podclusterIP: None # 将clusterIP设置为None,即可创建headliness Servicetype: ClusterIPports:- port: 80 # Service的端口targetPort: 80 # Pod的端口
---apiVersion: apps/v1
kind: StatefulSet
metadata:name: pc-statefulsetnamespace: dev
spec:replicas: 3type: RollingUpdateupdateStrategy:rollingUpdate:partition: 2 #表示从第2个分区开始更新,默认是0serviceName: service-headliness #headliness Service名称selector:matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- 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信息转换为对应的访问规则。
# 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 InActConnTCP 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):
kubectl edit cm kube-proxy -n kube-system
#删除原来的kube-proxy pod
kubectl delete pod -l k8s-app=kube-proxy -n kube-system##测试ipvs模块是否开启成功
ipvsadm -Ln
6.1. Service类型
- Service的资源清单:
apiVersion: v1 # 版本
kind: Service # 类型
metadata: # 元数据name: # 资源名称namespace: # 命名空间
spec:selector: # 标签选择器,用于确定当前Service代理那些Podapp: nginxtype: 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实现的。
#查看service 的详细信息
kubectl describe svc nginx-service -n app
spec.type的说明:
ClusterIP:默认值,它是kubernetes系统自动分配的虚拟IP,只能在集群内部访问。
NodePort:将Service通过指定的Node上的端口暴露给外部,通过此方法,就可以在集群外部访问服务。
LoadBalancer:使用外接负载均衡器完成到服务的负载分发,注意此模式需要外部云环境的支持。
ExternalName:把集群外部的服务引入集群内部,直接使用。
6.1.2.ClusterIP类型service
- 只允许集群内部访问
apiVersion: v1
kind: Service
metadata: name: nginx-servicenamespace: app
spec:selector: # 标签选择器,用于确定当前Service代理那些Podapp: nginxclusterIP: 10.0.0.133 # service的IP地址,如果不写,默认会生成一个type: ClusterIPports: #- port: 80 # Service端口protocol: TCP targetPort : 80
6.1.3.HeadLiness类型的Service
这类Service不会分配Cluster IP,如果想要访问Service,只能通过Service的域名进行查询。
创建service-headliness.yaml文件,内容如下:
apiVersion: v1
kind: Service
metadata:name: service-headlinessnamespace: dev
spec:selector:app: nginx-podclusterIP: None # 将clusterIP设置为None,即可创建headliness Servicetype: ClusterIPports:- port: 80 # Service的端口targetPort: 80 # Pod的端口
6.2.4.NodePort类型的Service
NodePort的工作原理就是将Service的端口映射到Node的一个端口上,然后就可以通过
NodeIP:NodePort
来访问Service了。创建service-nodeport.yaml文件,内容如下:
apiVersion: v1
kind: Service
metadata:name: service-nodeportnamespace: dev
spec:selector:app: nginx-podtype: NodePort # Service类型为NodePortports:- port: 80 # Service的端口targetPort: 80 # Pod的端口nodePort: 30002 # 指定绑定的node的端口(默认取值范围是30000~32767),如果不指定,会默认分配
- 查看Service:
kubectl get svc service-nodeport -n dev -o wide
6.1.5.LoadBalancer类型的Service
- LoadBalancer和NodePort很相似,目的都是向外部暴露一个端口,区别在于LoadBalancer需要在集群的外部再配置一个负载均衡设备,外部服务发送到这个设备上的请求,会被设备负载之后转发到集群中。
6.1.6. ExternalName类型的Service
ExternalName类型的Service用于引入集群外部的服务,它通过externalName属性指定一个服务的地址,然后在集群内部访问此Service就可以访问到外部的服务了。
创建service-externalname.yaml文件,内容如下:
apiVersion: v1
kind: Service
metadata:name: test-externalnamespace: app
spec:type: ExternalNameexternalName: 192.168.2.214ports:- name: httpport: 80protocol: TCPtargetPort: 80 #目标端口
6.1.7.k8s 接入外部数据库
集群内部访问外部数据库或者中间件一般采用endpoints与service关联方式映射。
创建endpoints-mysql.yaml文件,内容如下:
apiVersion: v1
kind: Endpoints
metadata:name: mysql-214namespace: app
subsets:- addresses:- ip: 192.168.2.214ports:- port: 16303
- 创建 service-mysql.yaml文件,内容如下:
apiVersion: v1
kind: Service
metadata: name: mysql-214 #要与endpoints 的名称一样namespace: app
spec:type: ClusterIPports: - port: 16303 # Service端口protocol: TCP targetPort : 16303
6.1.8.部署案例
- 创建nginx-deployment.yaml 文件
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-deploymentnamespace: app
spec:replicas: 1strategy: #策略type: RollingUpdate rollingUpdate: #滚动更新maxSurge: 30% #最大额外可以存在的副本数,可以为百分比,也可以为整数; 默认25%maxUnavailable: 30% #最大不可用状态的 ;Pod 的最大值,可以为百分比,也可以为整数,默认25%selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginximage: nginx:1.17.3ports:- containerPort: 80
- 创建 nginx-service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:name: service-nodeportnamespace: app
spec:selector:app: nginxtype: NodePort # Service类型为NodePortports:- port: 80 # Service的端口targetPort: 80 # Pod的端口nodePort: 30082 # 指定绑定的node的端口(默认取值范围是30000~32767),如果不指定,会默认分配
- 测试访问
#查看service
kubectl get svc service-nodeport -n app -o wide
或者
kubectl describe service service-nodeport -n app
6.2.ingress 资源
Service对集群之外暴露服务的主要方式有两种:NodePort和LoadBalancer,但是这两种方式,都有一定的缺点:
- NodePort方式的缺点是会占用很多集群机器的端口,那么当集群服务变多的时候,这个缺点就愈发明显。
- LoadBalancer的缺点是每个Service都需要一个LB,浪费,麻烦,并且需要kubernetes之外的设备的支持。
基于这种现状,kubernetes提供了Ingress资源对象,Ingress只需要一个NodePort或者一个LB就可以满足暴露多个Service的需求。
6.2.1安装ingress需要的环境
#wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml
#wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml
[root@k8s_master ~]# wget --no-check-certificate https://oss.linuxtxc.com/ingress-controller.zip
[root@k8s_master ~]# unzip ingress-controller.zip
[root@k8s_master ~]# cd ingress-controller/
[root@k8s_master ~]# kubectl apply -f ./
6.2.2.Http代理
- 为服务创建 service,yaml文件内容如下:
apiVersion: v1
kind: Service
metadata: name: nginx-servicenamespace: app
spec:selector: # 标签选择器,用于确定当前Service代理那些Podapp: nginxtype: ClusterIPports: #- port: 80 # Service端口protocol: TCP targetPort : 80
---apiVersion: v1
kind: Service
metadata:name: tomcat-servicenamespace: app
spec:selector:app: tomcattype: ClusterIPports:- port: 8080targetPort: 8080
- 创建ingress-http.yaml文件,内容如下:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:name: ingress-httpnamespace: app
spec:rules:- host: nginx.linuxtxc.comhttp:paths:- path: /backend:serviceName: nginx-serviceservicePort: 80- host: tomcat.linuxtxc.comhttp:paths:- path: /backend:serviceName: tomcat-serviceservicePort: 8080
- 查看ingress端口:
kubectl get ing ingress-http -n app
#查看访问ingress端口
kubectl get service -n ingress-nginx
- 访问URL:http://nginx.linuxtxc.com:32443(可以在本地hosts文件中添加解析访问)
6.2.3.Https代理
为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 appreq 产生证书签发申请命令
-newkey 生成新私钥
rsa:4096 生成秘钥位数
-nodes 表示私钥不加密
-sha256 使用SHA-2哈希算法
-keyout 将新创建的私钥写入的文件名
-x509 签发X.509格式证书命令。X.509是最通用的一种签名证书格式。
-out 指定要写入的输出文件名
-subj 指定用户信息
-days 有效期
- 创建ingress-https.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata: name: ingress-httpsnamespace: app
spec:tls:- hosts:- nginx.linuxtxc.com- tomcat.linxtxc.comsecretName: tls-linuxtxc.com ## 指定秘钥rules:- host: nginx.linuxtxc.comhttp:paths:- path: /backend:serviceName: nginx-serviceservicePort: 80- host: tomcat.linuxtxc.comhttp:paths:- path: /backend:serviceName: tomcat-serviceservicePort: 8080
- 查看ingress访问端口
kubectl get service -n ingress-nginx
7.k8s的数据存储
Volume是Pod中能够被多个容器访问的共享目录,它被定义在Pod上,然后被一个Pod里面的多个容器挂载到具体的文件目录下,kubernetes通过Volume实现同一个Pod中不同容器之间的数据共享以及数据的持久化存储。Volume的生命周期不和Pod中的单个容器的生命周期有关,当容器终止或者重启的时候,Volume中的数据也不会丢失。
kubernetes的Volume支持多种类型,比较常见的有下面的几个:
- 简单存储:EmptyDir、HostPath、NFS。
- 高级存储:PV、PVC。
- 配置存储:ConfigMap、Secret。
7.1.EmptyDir
它是最基础的volume类型,一个EmptyDir就是Host 上的一个空目录,当pod被分配到 Node时创建,它的初始内容为空,当pod 被销毁时,EmptyDir中的数据也会永久删除
EmptyDir的用途如下:
- 临时空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保留。
- 一个容器需要从另一个容器中获取数据的目录(多容器共享目录)。
创建volume-emptydir.yaml,内容如下:(一个容器从一个容器中获取数据的目录(多容器共享目录))
apiVersion: v1
kind: Pod
metadata:name: volume-emptydirnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1imagePullPolicy: IfNotPresentports:- containerPort: 80volumeMounts: # 将logs-volume挂载到nginx容器中对应的目录,该目录为/var/log/nginx- name: logs-volumemountPath: /var/log/nginx- name: busyboximage: busybox:1.30imagePullPolicy: IfNotPresentcommand: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,动态读取指定文件volumeMounts: #将logs-volume挂载到busybox容器中的对应目录,该目录为/logs- name: logs-volumemountPath: /logsvolumes: # 声明volume,name为logs-volume,类型为emptyDir- name: logs-volumeemptyDir: {}
- 查看pod
kubectl get pod volume-emptydir -n dev -o wide
- 访问pod中的 nginx
curl 172.16.67.8
- 查看指定容器的标准输出:
kubectl logs -f volume-emptydir -n dev -c busybox
7.2.HostPath
HostPath就是将Node主机中的一个实际目录挂载到Pod中,以供容器使用,这样的设计就可以保证Pod销毁了,但是数据依旧可以保存在Node主机上。
创建volume-hostpath.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:name: volume-hostpathnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1imagePullPolicy: IfNotPresentports:- containerPort: 80volumeMounts: # 将logs-volume挂载到nginx容器中对应的目录,该目录为/var/log/nginx- name: logs-volumemountPath: /var/log/nginx- name: busyboximage: busybox:1.30imagePullPolicy: IfNotPresentcommand: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,动态读取指定文件volumeMounts: # 将logs-volume挂载到busybox容器中的对应目录,该目录为/logs- name: logs-volumemountPath: /logsvolumes: # 声明volume,name为logs-volume,类型为hostPath- name: logs-volumehostPath:path: /opt/logs/nginxtype: DirectoryOrCreate # 目录存在就使用,不存在就先创建再使用
type的值的说明:
DirectoryOrCreate:目录存在就使用,不存在就先创建后使用。
Directory:目录必须存在。
FileOrCreate:文件存在就使用,不存在就先创建后使用。
File:文件必须存在。
Socket:unix套接字必须存在。
CharDevice:字符设备必须存在。
BlockDevice:块设备必须存在。
查看pod
kubectl get pod volume-hostpath -n dev -o wide
- 访问Pod中的Nginx:
curl 172.16.34.7
- 去node节点找到hostPath映射的目录(我是在node01上,如果在此目录中创建文件,到容器中也是可以看到的。)
7.3.NFS
HostPath虽然可以解决数据持久化的问题,但是一旦Node节点故障了,Pod如果转移到别的Node节点上,又会出现问题,此时需要准备单独的网络存储系统,比较常用的是NFS
NFS是一个网络文件存储系统,可以搭建一台NFS服务器,然后将Pod中的存储直接连接到NFS系统上,这样,无论Pod在节点上怎么转移,只要Node和NFS的对接没有问题,数据就可以成功访问。
7.3.1.安装NFS服务
- 下载软件包
yum install -y nfs-utils rpcbind
- 准备一个共享目录:
mkdir -pv /opt/data
- vim /etc/exports,将共享目录以读写权限暴露给
172.16.8.0/24
网段中的所有主机:
/opt/data 172.16.8.0/24(rw,no_root_squash)#加载配置
exportfs -r#启动nfs服务
systemctl start rpcbind
systemctl enable rpcbind
systemctl start nfs-server
systemctl enable nfs-server
- K8s node上安装nfs-utils ,但并不启动服务
yum -y install nfs-utils#查看NFS挂载情况(-d:仅显示已被NFS客户端加载的目录; -e:显示NFS服务器上所有的共享目录。)
showmount -e 172.16.8.217
- 高可用备份方式,可以选择在某一节点上执行:
mount -t nfs 172.16.8.217:/opt/data /mnt
7.3.2.pod挂载案例
- 创建volume-nfs.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:name: volume-nfsnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1imagePullPolicy: IfNotPresentports:- containerPort: 80volumeMounts: # 将logs-volume挂载到nginx容器中对应的目录,该目录为/var/log/nginx- name: logs-volumemountPath: /var/log/nginx- name: busyboximage: busybox:1.30imagePullPolicy: IfNotPresentcommand: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,动态读取指定文件volumeMounts: # 将logs-volume挂载到busybox容器中的对应目录,该目录为/logs- name: logs-volumemountPath: /logsvolumes: # 声明volume- name: logs-volumenfs:server: 172.16.8.217 # NFS服务器地址path: /opt/data/nfs # 共享文件路径
- 在NFS服务器上查看 文件共享情况
7.4.PV与PVC
7.4.1.PV(Persistent Volume)
- 是持久化卷的意思,是对底层的共享存储的一种抽象。它和底层具体的共享存储技术有关,并通过插件完成和共享存储的对接。(它没有namespace区别的划分)
pv的关键配置参数说明:
存储类型:底层实际存储的类型,kubernetes支持多种存储类型,每种存储类型的配置有所不同。
存储能力(capacity):目前只支持存储空间的设置(storage=1Gi),不过未来可能会加入IOPS、吞吐量等指标的配置。
访问模式(accessModes):用来描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载。ReadOnlyMany(ROX):只读权限,可以被多个节点挂载。ReadWriteMany(RWX):读写权限,可以被多个节点挂载。需要注意的是,底层不同的存储类型可能支持的访问模式不同。
回收策略( persistentVolumeReclaimPolicy):当PV不再被使用之后,对其的处理方式,目前支持三种策略:Retain(保留):保留数据,需要管理员手动清理数据。Recycle(回收):清除PV中的数据,效果相当于rm -rf /volume/*。Delete(删除):和PV相连的后端存储完成volume的删除操作,常见于云服务器厂商的存储服务。需要注意的是,底层不同的存储类型可能支持的回收策略不同。
存储类别(storageClassName):PV可以通过storageClassName参数指定一个存储类别。具有特定类型的PV只能和请求了该类别的PVC进行绑定。未设定类别的PV只能和不请求任何类别的PVC进行绑定。
状态(status):一个PV的生命周期,可能会处于4种不同的阶段。Available(可用):表示可用状态,还未被任何PVC绑定。Bound(已绑定):表示PV已经被PVC绑定。Released(已释放):表示PVC被删除,但是资源还没有被集群重新释放。Failed(失败):表示该PV的自动回收失败。
- 创建pv-nfs.yaml,内容如下(使用的之前创建的NFS服务):
apiVersion: v1
kind: PersistentVolume
metadata:name: pv1
spec:nfs: # 存储类型吗,和底层正则的存储对应path: /opt/data/pv1server: 172.16.8.217capacity: # 存储能力,目前只支持存储空间的设置storage: 1GiaccessModes: # 访问模式- ReadWriteManypersistentVolumeReclaimPolicy: Retain # 回收策略---
apiVersion: v1
kind: PersistentVolume
metadata:name: pv2
spec:nfs: # 存储类型吗,和底层正则的存储对应path: /opt/data/pv2 ## 共享文件路径server: 172.16.8.217capacity: # 存储能力,目前只支持存储空间的设置storage: 2GiaccessModes: # 访问模式- ReadWriteManypersistentVolumeReclaimPolicy: Retain # 回收策略---
apiVersion: v1
kind: PersistentVolume
metadata:name: pv3
spec:nfs: # 存储类型吗,和底层正则的存储对应path: /opt/data/pv3server: 172.16.8.217capacity: # 存储能力,目前只支持存储空间的设置storage: 3GiaccessModes: # 访问模式- ReadWriteManypersistentVolumeReclaimPolicy: Retain # 回收策略
- 查看PV
kubectl get pv
7.4.2.PVC(Persistent Volume Claim)
- 是资源的申请,用来声明对存储空间、访问模式、存储类别需求信息,下面是PVC的资源清单文件:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: pvcnamespace: dev
spec:accessModes: #访问模式- ReadWriteMany selector: # 采用标签对PV选择storageClassName: # 存储类别resources: # 请求空间requests:storage: 5Gi
PVC的关键配置参数说明:
访客模式(accessModes):用于描述用户应用对存储资源的访问权限。
用于描述用户应用对存储资源的访问权限:
- 选择条件(selector):通过Label Selector的设置,可使PVC对于系统中已存在的PV进行筛选。
- 存储类别(storageClassName):PVC在定义时可以设定需要的后端存储的类别,只有设置了该class的pv才能被系统选出。
- 资源请求(resources):描述对存储资源的请求。
创建pvc-test.yaml,内容如下:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: pvc1namespace: dev
spec:accessModes: # 访客模式- ReadWriteManyresources: # 请求空间requests:storage: 1Gi---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: pvc2namespace: dev
spec:accessModes: # 访客模式- ReadWriteManyresources: # 请求空间requests:storage: 1Gi---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: pvc3namespace: dev
spec:accessModes: # 访客模式- ReadWriteManyresources: # 请求空间requests:storage: 5Gi
- 查看PVC
kubectl get pvc -n dev -o wide
#pvc3未绑定,是因为没有满足需求的pv
- 查看PV
kubectl get pv
7.4.3.创建pod使用PVC
- 创建pvc-pod.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:name: pod1namespace: dev
spec:containers:- name: busyboximage: busybox:1.30command: ["/bin/sh","-c","while true;do echo pod1 >> /root/out.txt; sleep 10; done;"]volumeMounts:- name: volumemountPath: /root/volumes:- name: volumepersistentVolumeClaim:claimName: pvc1readOnly: false---
apiVersion: v1
kind: Pod
metadata:name: pod2namespace: dev
spec:containers:- name: busyboximage: busybox:1.30command: ["/bin/sh","-c","while true;do echo pod1 >> /root/out.txt; sleep 10; done;"]volumeMounts:- name: volumemountPath: /root/volumes:- name: volumepersistentVolumeClaim:claimName: pvc2readOnly: false
- 查看NFS服务器上共享的的目录
资源释放:
- 用户删除PVC来释放PV。
- 当存储资源使用完毕后,用户可以删除PVC,和该PVC绑定的PV将会标记为“已释放”,但是还不能立刻和其他的PVC进行绑定。通过之前PVC写入的数据可能还留在存储设备上,只有在清除之后该PV才能再次使用。
资源回收:
- kubernetes根据PV设置的回收策略进行资源的回收。
- 对于PV,管理员可以设定回收策略,用于设置与之绑定的PVC释放资源之后如何处理遗留数据的问题。只有PV的存储空间完成回收,才能供新的PVC绑定和使用。
创建PVC后一直绑定不了PV的原因
①PVC的空间申请大小比PV的空间要大。
②PVC的storageClassName和PV的storageClassName不一致。
③PVC的accessModes和PV的accessModes不一致。
7.4.4.使用glusterfs 创建 pv,并挂载使用
#glusterfs分布式存储部署
wget http://oss.linuxtxc.com/%E7%AC%94%E8%AE%B0/%E5%88%86%E5%B8%83%E5%BC%8F%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9Fglusterfs%E5%AE%89%E8%A3%85%E6%96%87%E6%A1%A3.md#kubernetes 所有节点都安装 glusterfs客户端
yum install -y glusterfs glusterfs-fuse
- 创建glusterfs的endpoints
apiVersion: v1
kind: Namespace
metadata:name: app---
apiVersion: v1
kind: Endpoints
metadata:name: glusterfs-clusternamespace: app
subsets:
- addresses:- ip: 172.16.8.212- ip: 172.16.8.213- ip: 172.16.8.214ports:- port: 49152protocol: TCP
- 创建pv使用glusterfs
apiVersion: v1
kind: PersistentVolume
metadata:name: glusterfs-pv01
spec:capacity:storage: 1GiaccessModes:- ReadWriteManyglusterfs:endpoints: glusterfs-clusterpath: app-data #创建的存储卷readOnly: false---
apiVersion: v1
kind: PersistentVolume
metadata:name: glusterfs-pv02
spec:capacity:storage: 2GiaccessModes:- ReadWriteManyglusterfs:endpoints: glusterfs-clusterpath: app-data #创建的存储卷readOnly: false
- 创建PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: glusterfs-pvc01namespace: app
spec:accessModes:- ReadWriteManyresources:requests:storage: 2Gi
- 创建使用Glusterfs卷的应用
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-deploymentnamespace: app
spec:replicas: 2selector:matchLabels:name: nginxtemplate:metadata:labels:name: nginxspec:containers:- name: nginximage: nginxports:- containerPort: 80volumeMounts:- name: nginxglusterfsmountPath: "/usr/share/nginx/html"volumes:- name: nginxglusterfspersistentVolumeClaim:claimName: glusterfs-pvc
- 查看容器是否成功挂载
kubectl exec -it deployment-glu-pv-9fb5fb64-kl48h -n app mount | grep app-data
7.5.configmap
- ConfigMap是一种API对象,用来将非加密数据保存到键值对中。可以用作环境变量、命令行参数或者存储卷中的配置文件。
- ConfigMap可以将环境变量配置信息和容器镜像解耦,便于应用配置的修改。如果需要存储加密信息时可以使用Secret对象。
7.5.1.使用命令行创建
(1)使用目录创建
kubectl create configmap configmap名 --from-file=/path (--from-file,指定在目录下的所有文件都会被用在ConfigMap里面创建一个键值对,键的名字就是文件名,值就是文件的内容)(2)使用文件创建
kubectl create configmap configmap名 --from-file=./file1 --from-file=./file2 (--from-file,可以使用多次,同样键为文件名,值为文件中的内容)(3)通过key-value字符串创建
kubectl create configmap configmap名 --from-literal=key1=123 --from-literal=key2=234(4)通过env文件创建
kubectl create configmap configmap名 --from-env-file=env.txt
其中,env.txt的文件格式为:
key1=***
key2=***
使用ConfigMap有以下几个限制条件:
- ConfigMap必须在pod之前创建
- configmap受namespace的限制,只能相同namespace的pod才可以引用
7.5.2.通过Volume 方式挂载
将ConfigMap中的内容挂载为容器内部的文件或目录
创建 configmap-nginx.yaml,内容如下:
apiVersion: v1
kind: ConfigMap
metadata: name: configmapnamespace: web
data:stream.conf: |stream.conf {server {listen 1550;proxy_pass 192.168.3.125:16303;}}
kubectl get cm -n web
- 将configmap中的配置通过volume挂载
apiVersion: v1
kind: Pod
metadata:name: pod-configmapnamespace: web
spec:containers:- name: nginximage: nginx:1.17.1volumeMounts: - name: configmountPath: /etc/nginx/vhosts #没有则会自动创建,目录下有文件则会覆盖掉volumes:- name: configconfigMap:name: configmap #指的是要从哪个configmap里读取数据
kubectl exec -it pod-configmap -n web /bin/sh
7.5.3.通过SubPath 使原目录文件不被覆盖
- 创建 configmap-php.yaml,内容如下:
apiVersion: v1
kind: ConfigMap
metadata: name: configmap-phpnamespace: web
data:index.php: |<?phpecho phpversion();?>
kubectl get cm -n web
- 创建 volume-configmap-php.yaml,内容如下
apiVersion: v1
kind: Pod
metadata:name: pod-configmap-phpnamespace: web
spec:containers:- name: nginximage: nginx:1.17.1volumeMounts: - name: configmountPath: /usr/share/nginx/html/index.php subPath: index.php #与configmap中的key相同volumes:- name: configconfigMap:name: configmap-php #指的是要从哪个configmap里读取数据
kubectl exec -it pod-configmap-php -n web /bin/sh
- 只覆盖指定的文件
apiVersion: v1
kind: Pod
metadata:name: nginx-configmapnamespace: app
spec:containers:- name: nginximage: nginx:1.17.1command: [ "/bin/sh", "-c", "sleep 3600" ]volumeMounts:- name: configmountPath: /etc/nginx/nginx.confsubPath: nginx.conf #subPath:要覆盖文件的相对路径volumes:- name: configconfigMap:name: configmap-nginx #configMap的名称items: #用于只挂载configmap中特定的key- key: nginx.conf #ConfigMap中key的名称path: nginx.conf # 此处的path相当于 mv nginx.conf nginx.conf
7.6.Secret
跟configMap非常类似的对象,它主要是用于存储敏感信息,例如密码,密钥,证书等等。
Secret有三种类型:
- Opaque
- kubernetes.io/dockerconfigjson
- ServiceAccount
7.6.1.Opaque
- base64 编码格式的 Secret,用来存储密码、密钥等;但数据也可以通过base64 –decode解码得到原始数据,所有加密性很弱。
#使用命令行 创建secret(1)kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt(2)kubectl create secret generic db-user-pass --from-literal=username=admin --from-literal=password=1f2d1e2e67df#手动创建base64加密
echo -n 'admin' | base64
echo -n '1f2d1e2e67df' | base64#解密
echo 'MWYyZDFlMmU2N2Rm' | base64 --decode
- 创建 secret-opaque.yaml,内容如下:
apiVersion: v1
kind: Secret
metadata:name: mysecretnamespace: dev
type: Opaque
data:username: YWRtaW4= #修改为base64加密后的password: MWYyZDFlMmU2N2Rm
kubectl get secret mysecret -n dev -o yaml
- 也可以将一个非 base64 编码的字符串直接放入 Secret 中, 当创建或更新该 Secret 时,此字段将被编码。
apiVersion: v1
kind: Secret
metadata:name: web-secretnamespace: web
type: Opaque
stringData:config.yaml: |apiUrl: "https://my.api.com/api/v1"username: adminpassword: 123456
- 创建pod-secret.yaml,将secert 挂载到 pod中,内容如下:
apiVersion: v1
kind: Pod
metadata:name: pod-secretnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1volumeMounts:- name: configmountPath: /secret/configreadOnly: truevolumes:- name: configsecret:secretName: mysecret
kubectl exec -it pod-secret -n dev /bin/sh
- 只挂载Secret中特定的key
apiVersion: v1
kind: Pod
metadata:name: pod-secret-01namespace: dev
spec:containers:- name: nginximage: nginx:1.17.1volumeMounts:- name: secretmountPath: /optreadOnly: truevolumes:- name: secretsecret:secretName: mysecretitems:- key: usernamepath: cert/username #username 将存储在/opt/cert/username 下
kubectl exec -it pod-secret-01 -n dev /bin/sh
- 将Secret设置为环境变量
apiVersion: v1
kind: Pod
metadata:name: secret-env-pod
spec:containers:- name: redisimage: redisenv:- name: SECRET_USERNAMEvalueFrom:secretKeyRef:name: mysecretkey: username- name: SECRET_PASSWORDvalueFrom:secretKeyRef:name: mysecretkey: password
7.6.2.kubernetes.io/dockerconfigjson
- 用来存储私有docker registry的认证信息。
kubectl create secret docker-registry myregistrykey --docker-server=192.168.3.124 --docker-username=admin --docker-password=admin123 --docker-email=<your-email> --namespace=dev
- 在创建 Pod 的时候,通过 imagePullSecrets 来引用刚创建的 myregistrykey
apiVersion: v1
kind: Pod
metadata:name: dev
spec:containers:- name: fooimage: janedoe/awesomeapp:v1imagePullSecrets:- name: myregistrykey
7.6.3.ServiceAccount
- 用来访问Kubernetes API,由Kubernetes自动创建,并且会自动挂载到Pod的 /run/secrets/kubernetes.io/serviceaccount 目录中。
- 每个namespace下有一个名为default的默认的ServiceAccount对象,这个ServiceAccount里有一个名为Tokens的可以作为Volume一样被Mount到Pod里的Secret,当Pod启动时这个Secret会被自动Mount到Pod的指定目录下,用来协助完成Pod中的进程访问API Server时的身份鉴权过程。
#创建一个sa 名称为admin
kubectl create serviceaccount admin -n dev
- 如果一个Pod在定义时没有指定spec.serviceAccountName属性,则会默认赋值为default,可进行如下指定
apiVersion: v1
kind: Pod
metadata:name: sa-demonamespace: devlabels:app: myapprelease: canary
spec:containers:- name: myappimage: nginx:1.17.2ports:- name: nginx-portcontainerPort: 80serviceAccountName: admin #此处指令为指定sa的名称
7.7.GlusterFS+Heketi实现volume动态扩容
SC:是StorageClass的缩写,表示存储类;这种资源主要用来对pv资源的自动供给提供接口;所谓自动供给是指用户无需手动创建pv,而是在创建pvc时对应pv会由persistentVolume-controller自动创建并完成pv和pvc的绑定;使用sc资源的前提是对应后端存储必须支持restfull类型接口的管理接口,并且pvc必须指定对应存储类名称来引用SC;简单讲SC资源就是用来为后端存储提供自动创建pv并关联对应pvc的接口;
Heketi是一个提供RESTful API管理GlusterFS卷的框架,便于管理员对GlusterFS进行操作:
#yum下载
#wget https://mirrors.aliyun.com/centos/7/storage/x86_64/gluster-9/Packages/h/heketi-9.0.0-1.el7.x86_64.rpm
wget http://oss.linuxtxc.com/deploy/rpm/heketi-9.0.0-1.el7.x86_64.rpm#wget https://mirrors.aliyun.com/centos/7/storage/x86_64/gluster-9/Packages/h/heketi-client-9.0.0-1.el7.x86_64.rpm
wget http://oss.linuxtxc.com/deploy/rpm/heketi-client-9.0.0-1.el7.x86_64.rpm
7.7.1.安装 heketi
#安装rpm包,我只在master上安装
yum http://oss.linuxtxc.com/deploy/rpm/heketi-9.0.0-1.el7.x86_64.rpm
yum http://oss.linuxtxc.com/deploy/rpm/heketi-client-9.0.0-1.el7.x86_64.rpm
- 修改service文件
vim /usr/lib/systemd/system/heketi.service
......................[Unit]
Description=Heketi Server[Service]
Type=simple
WorkingDirectory=/var/lib/heketi
EnvironmentFile=/etc/heketi/heketi.json
User=root
ExecStart=/usr/bin/heketi --config=/etc/heketi/heketi.json
Restart=on-failure
StandardOutput=syslog
StandardError=syslog[Install]
WantedBy=multi-user.target
- 修改heketi配置文件
vim /etc/heketi/heketi.json
.............................
#修改端口,防止端口冲突"port": "18080",
......
#允许认证"use_auth": true,
......
#admin用户的key改为adminkey"key": "adminkey"
#普通用户的key改为userkey"key": "userkey"
......
#修改执行插件为ssh,并配置ssh的所需证书,注意要能对集群中的机器免密ssh登陆,使用ssh-copy-id把pub key拷到每台glusterfs服务器上"executor": "ssh","sshexec": {"keyfile": "/etc/heketi/heketi_key","user": "root","port": "22","fstab": "/etc/fstab"},
......
# 定义heketi数据库文件位置"db": "/var/lib/heketi/heketi.db"
......
#调整日志输出级别"loglevel" : "warning"
- 配置ssh密钥
[root@k8s-master heketi]# ssh-keygen -f /etc/heketi/heketi_key -t rsa -N ''[root@k8s-master heketi]# ssh-copy-id -i /etc/heketi/heketi_key.pub 172.16.8.212
[root@k8s-master heketi]# ssh-copy-id -i /etc/heketi/heketi_key.pub 172.16.8.213
[root@k8s-master heketi]# ssh-copy-id -i /etc/heketi/heketi_key.pub 172.16.8.214
- 验证登录
ssh -i /etc/heketi/heketi_key root@172.16.8.212
- 启动heketi
systemctl start heketi
systemctl enable heketi
- 查看heketi服务状态
systemctl status heketi
7.7.2.heketi添加glusterfs
- Heketi添加cluster
heketi-cli --user admin --server http://172.16.8.124:18080 --secret adminkey --json cluster create
.................................
{"id":"7a263332ed6bc432f7fa81e65e021542","nodes":[],"volumes":[],"block":true,"file":true,"blockvolumes":[]}
- 将所有glusterfs节点作为node添加到cluster
[root@k8s-master heketi]# heketi-cli --server http://172.16.8.124:18080 --user "admin" --secret "adminkey" node add --cluster "7a263332ed6bc432f7fa81e65e021542" --management-host-name 172.16.8.212 --storage-host-name 172.16.8.212 --zone 1[root@k8s-master heketi]# heketi-cli --server http://172.16.8.124:18080 --user "admin" --secret "adminkey" node add --cluster "7a263332ed6bc432f7fa81e65e021542" --management-host-name 172.16.8.213 --storage-host-name 172.16.8.213 --zone 1[root@k8s-master heketi]# heketi-cli --server http://172.16.8.124:18080 --user "admin" --secret "adminkey" node add --cluster "7a263332ed6bc432f7fa81e65e021542" --management-host-name 172.16.8.214 --storage-host-name 172.16.8.214 --zone 1注:要保存每个glusterfs节点ID
- 添加device,volume是基于device创建的,目前heketi仅支持使用裸分区或裸磁盘(未格式化)添加为device,不支持文件系统,所以每个glusterfs节点上要有未格式化的磁盘
[root@k8s-master heketi]# heketi-cli --server http://172.16.8.124:18080 --user "admin" --secret "adminkey" --json device add --name="/dev/sdb" --node "3aee8b6287717c18c72fbcc547e8bff4"[root@k8s-master heketi]# heketi-cli --server http://172.16.8.124:18080 --user "admin" --secret "adminkey" --json device add --name="/dev/sdb" --node "833b827e2d72c73a597c7d82a5ebdbc5"[root@k8s-master heketi]# heketi-cli --server http://172.16.8.124:18080 --user "admin" --secret "adminkey" --json device add --name="/dev/sdb" --node "968afeb52b8924cac7ffac6db3963e03"
- 创建一个大小为2G,副本为2的volume
[root@k8s-master dev]# heketi-cli --server http://172.16.8.124:18080 --user "admin" --secret "adminkey" volume create --size 2 --replica 2#通过下面信息,可以看到Heketi创建了名为vol_15249e32eb410cabe764e4a06e254a7e的数据卷。
7.7.3.创建资源使用volume
- 创建storageclass-glusterfs.yaml,内容如下:
apiVersion: v1
kind: Secret
metadata:name: heketi-secretnamespace: app
data:# echo -n "mypassword" | base64 / base64 encoded "password"key: bXlwYXNzd29yZA==
type: kubernetes.io/glusterfs---
apiVersion: storage.k8s.io/v1beta1
kind: StorageClass
metadata:name: glusterfs
provisioner: kubernetes.io/glusterfs
allowVolumeExpansion: true
parameters:resturl: "http://172.16.8.124:18080"clusterid: "7a263332ed6bc432f7fa81e65e021542"restauthenabled: "true"restuser: "admin"#secretNamespace: "default"#secretName: "heketi-secret"restuserkey: "adminkey"gidMin: "40000"gidMax: "50000"volumetype: "replicate:2"
kubectl get storageclass -n app
- 创建glusterfs-pvc.yaml内容如下:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:name: glusterfs-pvcnamespace: appannotations:volume.beta.kubernetes.io/storage-class: "glusterfs"
spec:accessModes:- ReadWriteManyresources:requests:storage: 2Gi
kubectl get pvc -n app#有绑定的volume才算创建成功,同时PV也会自动创建
- 创建 mysql-deployment.yaml,内容如下:
apiVersion: apps/v1
kind: Deployment
metadata:name: mysqlnamespace: app
spec:selector:matchLabels:name: mysqltemplate:metadata:labels:name: mysqlspec:containers:- name: mysqlimage: mysql:5.7imagePullPolicy: IfNotPresentenv:- name: MYSQL_ROOT_PASSWORDvalue: root123456ports:- containerPort: 3306volumeMounts:- name: glusterfs-mysql-datamountPath: "/var/lib/mysql"volumes:- name: glusterfs-mysql-datapersistentVolumeClaim:claimName: glusterfs-pvcreadOnly: false
- 查看volume挂载情况
kubectl exec -it mysql-5b94895868-n6xld -n app mount | grep 172
- 查看glusterfs集群状态
gluster volume status
7.7.4.动态修改PVC
- 查看当前PVC容量
#修改yaml配置
vim glusterfs-pvc.yamlkubectl apply -f glusterfs-pvc.yaml
- 再次查看 PVC
kubectl get pvc -n app
8.k8s的安全认证
8.1.概念描述
● API Server是访问和管理资源对象的唯一入口。任何一个请求访问API Server,都要经过下面的三个流程:
○ ① Authentication(认证):身份鉴别,只有正确的账号才能通过认证。
○ ② Authorization(授权):判断用户是否有权限对访问的资源执行特定的动作。
○ ③ Admission Control(注入控制):用于补充授权机制以实现更加精细的访问控制功能。
UserAccount:用于限制用户账号对 kubernetes集群资源访问的权限
ServiceAccount:用于为Pod的服务进程在访问kubernetes时提供身份标识。
API Server目前支持的几种授权策略
● AlwaysDeny:表示拒绝所有请求,一般用于测试。
● AlwaysAllow:允许接收所有的请求,相当于集群不需要授权流程(kubernetes默认的策略)。
● ABAC:基于属性的访问控制,表示使用用户配置的授权规则对用户请求进行匹配和控制。
● Webhook:通过调用外部REST服务对用户进行授权。
● Node:是一种专用模式,用于对kubelet发出的请求进行访问控制。
● RBAC:基于角色的访问控制(kubeadm安装方式下的默认选项)。
● RBAC(Role Based Access Control) : 基于角色的访问控制,主要是在描述一件事情:给哪些对象授权了哪些权限。
● RBAC涉及到了下面几个概念:
○ 对象:User、Groups、ServiceAccount。
○ 角色:代表着一组定义在资源上的可操作的动作(权限)的集合。
○ 绑定:将定义好的角色和用户绑定在一起。
● RBAC引入了4个顶级资源对象:
○ Role、ClusterRole:角色,用于指定一组权限。
○ RoleBinding、ClusterRoleBinding:角色绑定,用于将角色(权限的集合)赋予给对象。
8.2.Role、ClusterRole
- Role的资源清单文件:
# Role只能对命名空间的资源进行授权,需要指定namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:name: authorization-rolenamespace: dev
rules:- apiGroups: [""] # 支持的API组列表,""空字符串,表示核心API群resources: ["pods"] # 支持的资源对象列表verbs: ["get","watch","list"]
- ClusterRole的资源清单文件:
# ClusterRole可以对集群范围内的资源、跨namespace的范围资源、非资源类型进行授权
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:name: authorization-clusterrole
rules:- apiGroups: [""] # 支持的API组列表,""空字符串,表示核心API群resources: ["pods"] # 支持的资源对象列表verbs: ["get","watch","list"]
rules中的参数说明:
apiGroups:#支持的API组列表。"","apps","autoscaling","batch"。
resources:#支持的资源对象列表。 "services","endpoints","pods","secrets","configmaps","crontabs","deployments","jobs","nodes","rolebindings","clusterroles","daemonsets","replicasets","statefulsets","horizontalpodautoscalers","replicationcontrollers","cronjobs"。
verbs:#对资源对象的操作方法列表。"get", "list", "watch", "create", "update", "patch", "delete", "exec"。
8.3.RoleBinding、ClusterRoleBinding
角色绑定用来把一个角色绑定到一个目标对象上,绑定目标可以是User、Group或者ServiceAccount。
RoleBinding的资源清单文件:
# RoleBinding可以将同一namespace中的subject对象绑定到某个Role下,则此Subject具有该Role定义的权限
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:name: authorization-role-bindingnamespace: dev
subjects:- kind: Username: testapiGroup: rbac.authorization.k8s.io
roleRef:apiGroup: rbac.authorization.k8s.iokind: Rolename: authorization-role
- ClusterRoleBinding的资源清单文件:
# ClusterRoleBinding在整个集群级别和所有namespaces将特定的subject与ClusterRole绑定,授予权限
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:name: authorization-clusterrole-binding
subjects:- kind: Username: testapiGroup: rbac.authorization.k8s.io
roleRef:apiGroup: rbac.authorization.k8s.iokind: ClusterRolename: authorization-clusterrole
- RoleBinding引用ClusterRole进行授权
○RoleBinding可以引用ClusterRole,对属于同一命名空间内ClusterRole定义的资源主体进行授权。
○ 一种很常用的做法是,集群管理员为集群范围预定义好一组规则(ClusterRole),然后在多个命名空间中重复使用这些ClusterRole。这样可以大幅度提高授权管理工作效率,也使得各个命名空间下的基础性授权规则和使用体验保持一致。
# 虽然authorization-clusterrole是一个集群角色,但是因为使用了RoleBinding
# 所以test用户只能读取dev命名空间中的资源
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:name: authorization-clusterrole-binding
subjects:- kind: Username: testapiGroup: rbac.authorization.k8s.io
roleRef:apiGroup: rbac.authorization.k8s.iokind: ClusterRolename: authorization-clusterrole
8.4.RBAC案例
- 创建一个只能管理dev命名空间下Pods资源的账号。
#创建用户的私钥
cd /etc/kubernetes/pki
(umask 077;openssl genrsa -out devuser.key 2048)#创建证书签署请求(O:组织名 CN:用户名)openssl req -new -key devuser.key -out devuser.csr -subj "/CN=devuser/O=devgroup"#签署证书
openssl x509 -req -in devuser.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out devuser.crt -days 3650#设置集群、用户、上下文信息(不设置--kubeconfig,如--kubeconfig=/root/.devuser.config,则会默认放在家目录下的./kube 中)
kubectl config set-cluster kubernetes --embed-certs=true --certificate-authority=/etc/kubernetes/pki/ca.crt --server=https://172.16.8.124:6443 kubectl config set-credentials devuser --embed-certs=true --client-certificate=devuser.crt --client-key=devuser.key kubectl config set-context devuser@kubernetes --cluster=kubernetes --user=devuser #切换账号到devuser
kubectl config use-context devuser@kubernetes
#查看(当前还没有权限执行当前操作,还需要对用户授权)
kubectl get pods -n dev
- 如果要使用linux的普通用户管理集群
[root@k8s_master ~]# useradd devuser
[root@k8s_master ~]# mkdir /home/devuser/.kube
[root@k8s_master ~]# cp .kube/config /home/devuser/.kube/
[root@k8s_master ~]# chown -R devuser:devuser /home/devuser/
- 查看管理账号,并切换
#输出合并后的kubeconfig的内容,格式为 YAML,密文内容不会显示(--kubeconfig,可以指定只显示某个账户的kubeconfig内容)
kubectl config view#切回admin账户
kubectl config use-context kubernetes-admin@kubernetes
创建Role和RoleBinding,为devuser授权
- 创建dev-role.yaml文件,内容如下:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:name: dev-rolenamespace: dev
rules:- apiGroups: [""] # 支持的API组列表,""空字符串,表示核心API群resources: ["pods"] # 支持的资源对象列表verbs: ["get","watch","list"]---kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:name: authorization-role-binding namespace: dev
subjects:- kind: Username: devuserapiGroup: rbac.authorization.k8s.io
roleRef:kind: Role name: dev-roleapiGroup: rbac.authorization.k8s.io
#切换成 devuser,再次查看
kubectl get pods -n dev
8.5.准入控制
通过了前面的认证和授权之后,还需要经过准入控制通过之后,API Server才会处理这个请求。
准入控制是一个可配置的控制器列表,可以通过在API Server上通过命令行设置选择执行哪些注入控制器。
--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota,DefaultTolerationSeconds
只有当所有的注入控制器都检查通过之后,API Server才会执行该请求,否则返回拒绝。
当前可配置的Admission Control(准入控制)
○ AlwaysAdmit:允许所有请求。○ AlwaysDeny:禁止所有请求,一般用于测试。
○ AlwaysPullImages:在启动容器之前总去下载镜像。
○ DenyExecOnPrivileged:它会拦截所有想在Privileged Container上执行命令的请求。
○ ImagePolicyWebhook:这个插件将允许后端的一个Webhook程序来完成admission controller的功能。
○ Service Account:实现ServiceAccount实现了自动化。
○ SecurityContextDeny:这个插件将使用SecurityContext的Pod中的定义全部失效。
○ ResourceQuota:用于资源配额管理目的,观察所有请求,确保在namespace上的配额不会超标。
○ LimitRanger:用于资源限制管理,作用于namespace上,确保对Pod进行资源限制。
○ InitialResources:为未设置资源请求与限制的Pod,根据其镜像的历史资源的使用情况进行设置。
○ NamespaceLifecycle:如果尝试在一个不存在的namespace中创建资源对象,则该创建请求将被拒 绝。当删除一个namespace时,系统将会删除该namespace中所有对象。
○ DefaultStorageClass:为了实现共享存储的动态供应,为未指定StorageClass或PV的PVC尝试匹配默认StorageClass,尽可能减少用户在申请PVC时所需了解的后端存储细节。
○ DefaultTolerationSeconds:这个插件为那些没有设置forgiveness tolerations并具有notready:NoExecute和unreachable:NoExecute两种taints的Pod设置默认的“容忍”时间,为5min。
○ PodSecurityPolicy:这个插件用于在创建或修改Pod时决定是否根据Pod的security context和可用的 PodSecurityPolicy对Pod的安全策略进行控制
9.k8s+prometheus+grafana实现集群监控
#下载yaml文件
https://oss.linuxtxc.com/k8s-prometheus-grafana.zip
#解压
unzip k8s-prometheus-grafana.zip
#创建资源
cd k8s-prometheus-grafana/
kubectl apply -f node-exporter.yaml
kubectl apply -f grafana/
kubectl apply -f prometheus/
- 访问prometheus(http://192.168.3.124:30003/targets)
- 访问node-exporter(http://192.168.3.124:31672/metrics)
- 访问Grafana(http://192.168.3.124:30106/login),默认用户密码均为admin
- 添加数据源,以下使用的代理模式(推荐),仅限k8s内部访问
- 也可以使用直接访问模式
- 导入面板:Dashboards->Import(导入面板,可以直接输入模板编号315在线导入,或者下载好对应的json模板文件本地导入)
- 查看展示效果
10.k8s集群管理平台部署
10.1.kuboard安装
#下载
#wget http://oss.linuxtxc.com/deploy/yaml/kuboard-v3.yaml
wget https://addons.kuboard.cn/kuboard/kuboard-v3.yamlkubectl apply -f kuboard-v3.yaml
访问 Kuboard
- 在浏览器中打开链接
http://172.16.8.124:30080
- 输入初始用户名和密码,并登录
- 用户名:
admin
- 密码:
Kuboard123
- 用户名:
10.2.kubesphere安装
#wget https://github.com/kubesphere/ks-installer/releases/download/v3.2.1/kubesphere-installer.yaml
#wget https://github.com/kubesphere/ks-installer/releases/download/v3.2.1/cluster-configuration.yamlwget http://oss.linuxtxc.com/deploy/yaml/kubesphere/kubesphere-installer.yamlwget http://oss.linuxtxc.com/deploy/yaml/kubesphere/cluster-configuration.yamlkubectl apply -f kubesphere-installer.yaml
kubectl apply -f cluster-configuration.yaml#查看安装情况
kubectl logs -n kubesphere-system $(kubectl get pod -n kubesphere-system -l app=ks-install -o jsonpath='{.items[0].metadata.name}') -f
- 解决方法:
#yaml文件中的storageClass默认为空,需要在两处添加,我的storageClass为"glusterfs"
vim cluster-configuration.yaml
#再次查看安装情况(安装成功后会有如下提示)
kubectl logs -n kubesphere-system $(kubectl get pod -n kubesphere-system -l app=ks-install -o jsonpath='{.items[0].metadata.name}') -f
- 访问 curl:http://172.16.8.124:30880/
10.3.部署dashboard
wget https://oss.linuxtxc.com/deploy/yaml/kubernetes-dashboard.yaml
kubectl apply -f kubernetes-dashboard.yaml#查看Nodeport访问端口
kubectl get service -n kubernetes-dashboard
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uHlpixpw-1654139062282)(C:\Users\linux\AppData\Roaming\Typora\typora-user-images\image-20220526194221833.png)]
- https://nodeIP:30081
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mPMbyF26-1654139062283)(C:\Users\linux\AppData\Roaming\Typora\typora-user-images\image-20220526194337744.png)]
#创建用户
kubectl create serviceaccount dashboard-admin -n kube-system#用户授权
kubectl create clusterrolebinding dashboard-admin --clusterrole=cluster-admin --serviceaccount=kube-system:dashboard-admin#获取token
kubectl describe secrets -n kube-system $(kubectl -n kube-system get secret | awk '/dashboard-admin/{print $1}')
- 输入token后
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y4RhyYhs-1654139062284)(C:\Users\linux\AppData\Roaming\Typora\typora-user-images\image-20220526194701213.png)]
11.helm
1.1.helm安装
#GitHub下载地址
#https://github.com/helm/helm/releases
wget https://get.helm.sh/helm-v3.5.4-linux-amd64.tar.gz
tar -xf helm-v3.5.4-linux-amd64.tar.gz
mv linux-amd64/helm /usr/local/bin/#查看版本
helm version
1.2.helm安装harbor
#下载harbor Chart
helm repo add harbor https://helm.goharbor.io
helm fetch harbor/harbor --untar#进入目录
cd harbor
- 编辑values.yaml文件
kubernetes完整学习笔记相关推荐
- 【一文弄懂】张正友标定法-完整学习笔记-从原理到实战
张正友标定法-完整学习笔记-从原理到实战 文章目录 张正友标定法-完整学习笔记-从原理到实战 (零)前言: 1 为什么需要标定? 2 相机标定的已知条件和待求解是什么? 标定前的已知条件: 待求信息: ...
- 北斗导航 | RTKLib完整学习笔记(代码解析版)
===================================================== github:https://github.com/MichaelBeechan CSDN: ...
- 《统计学》(贾俊平)考研初试完整学习笔记10~14章
贾俊平<统计学第6版>学习笔记 这是我自己去年考研时整理的笔记,希望能给到432考研以及正在学习统计学的小伙伴们一点帮助吧,我是把这份笔记当作复习时的框架来用的,时不时过一遍,有不熟悉的地 ...
- 《统计学》(贾俊平)考研初试完整学习笔记6~9章
贾俊平<统计学第6版>学习笔记 这是我自己去年考研时整理的笔记,希望能给到432考研以及正在学习统计学的小伙伴们一点帮助吧,我是把这份笔记当作复习时的框架来用的,时不时过一遍,有不熟悉的地 ...
- Kubernetes亲和性学习笔记
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本文是欣宸在学习Kubernetes调度器的 ...
- Kubernetes的学习笔记总结之k8s集群安装部署
kubernets 集群安装部署. 安装 Docker 所有节点都需要安装 Docker. apt-get update && apt-get install docker.io 安装 ...
- VBA完整学习笔记1-39集(共60集)
目录 第1集:宏与VBA 1 什么是VBA,它有什么作用 2 VBA在哪里存放的?怎么运行的 3.什么是宏?宏和VBA有什么关系? 4.录制一个宏 5.编写第一个宏 第2集:VBA中的语句.对象.方法 ...
- 完整学习笔记之Android基础(详版)
Android项目的目录结构(熟悉) Activity:应用被打开时显示的界面 src:项目代码 R.java:项目中所有资源文件的资源id Android.jar:Android的jar包,导入此包 ...
- MySQL完整学习笔记
文章目录 前言 `MySQL`的相关概念 `MySQL`的特点 `MySQL`的安装和配置(使用`ZIP Archive`免安装方式) `MySQL`的安装 `MySQL`基本操作 `MySQL`基本 ...
最新文章
- HDU 排名(简单题)
- NR 5G SA注册流程
- SSH port forwarding: bind: Cannot assign requested address
- mysql_fetch_row,mysql_fetch_array,mysql_fetch_assoc的区别
- java多线程优先级的方法_Java多线程以及线程优先级
- 使用JCrop进行图片裁剪,裁剪js说明,裁剪预览,裁剪上传,裁剪设计的图片处理的工具类和代码
- SpringBoot 自带工具类~StringUtils
- 主键字母自动生成函数
- 百度app不支持popstate_这一次谷歌很强硬,从现在起你的 App 再不支持,将拒绝上架!...
- 小芭比linux怎么装win7_小户型再怎么装也是小?看完我闭嘴了
- form表单重置、清空方法记录
- PS中有哪些快速抠图的方法
- java语言难度最大的地方_学习难度最高的五大编程语言
- 郭德纲 相声下载(续)
- Linux ubuntu 20.04安装unrar
- 程序设计基础实训报告
- 实现一个简陋的MVC框架
- 计算机科学与技术专业有哪些课程,计算机科学与技术专业课程有哪些
- Common Weakness Enumeration (CWE) 2021 Lastest
- 数学建模----图与网络模型