一、K8S概念 1. 什么是Kubernetes ?
k8s是一组服务器集群,K8s所管理的集群节点上的容器。官方网站
Kubernetes是一个可移植的,可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化。它拥有一个庞大且快速增长的生态系统。Kubernetes的服务,支持和工具广泛可用。
Kubernetes这个名字起源于希腊语,意思是舵手或飞行员。Google在2014年开源了Kubernetes项目。Kubernetes将超过15年的Google在大规模生产工作负载方面的经验与社区中最好的想法和实践相结合。
2. 为什么需要它?他的功能? 容器是捆绑和运行应用程序的好方法。在生产环境中,您需要管理运行应用程序的容器,并确保没有停机时间。例如,如果一个容器发生故障,则需要启动另一个容器。如果由系统处理此行为,会不会更容易?
这就是Kubernetes的救援方法!Kubernetes为您提供了一个可弹性运行分布式系统的框架。它负责应用程序的扩展和故障转移,提供部署模式等。例如,Kubernetes可以轻松管理系统的Canary部署。
自我修复
在节点故障时重新启动失败的容器,替换和重新部署容器,保证预期的副本数量;杀死健康检查失败的容器,并且在未准备好之前不会处理客户端请求,确保线上服务不中断。
弹性伸缩
使用命令、UI或者基于CPU使用情况自动快速扩容和缩容应用程序实例,保证应用业务高峰并发时的高可用性;业务低峰时回收资源,以最小成本运行服务。
自动部署和回滚
Kubernetes采用滚动更新策略更新应用,一次更新一个Pod,而不是同时删除所有Pod,如果更新过程中出现问题,Kubernetes将回滚更改,升级保证业务不受影响。
存储编排
挂载外部存储系统,无论是来自本地存储,公有云(如AWS),还是网络存储(如NFS、iSCSI、GlusterFS、Ceph)都作为集群资源的一部分使用,极大提高存储使用灵活性。
服务发现和负载均衡
Kubernetes为多个容器提供一个统一访问入口(内部IP地址和一个DNS名称),并且负载均衡关联的所有容器,使得用户无需考虑容器IP问题。集群内应用可以通过DNS名称访问另一个应用,方便微服务之间通信。
机密和配置管理
管理机密数据和应用程序配置,而不需要把敏感数据暴露在镜像里,提高敏感数据安全性。
资源监控
Node节点组件集成cAdvisor资源收集工具,可通过Heapster汇总整个集群节点资源数据,然后存储到InfluxDB时序数据库,再由Grafana展示,可以快速实现对集群资源监控,满足基本监控需求。
提供认证和授权
支持属性访问控制 (ABAC)、角色访问控制(RBAC)认证授权策略,控制用户是否有权限使用Kubernetes API做某些事情,精细化权限分配。
3. Kubernetes架构
kubernetes分别有两种角色:1、master 管理节点 2、worker 工作节点
生产k8s集群图构
4. Kubernetes组件
kubernetes分为 Master节点和 Node节点,前者是管理节点,后者是容器运行的节点。其中Master节点主要有3个重要组件,分别是APIServer,sheduler 和 controller manager。
Node节点 有两个组件 kubelet 和 kubelet (有时候master节点也可以既是管理节点也是工作节点)
4.1 Master 组件 API Server
APIServer组件负责响应用户的管理请求、进行指挥协调工作,所有服务访问统一入口。
Scheduler
scheduler组件是将待调度的pod按照一定的调度算法绑定到合适的工作节点上,负责介绍任务,选择合适的点进行分配任务。
Controller manager
是一组控制器的合集,负责控制控制管理对应的资源,如副本(replication)和工作节点(node)等。维持副本期望数目
ETCD
etcd 负责以K/V的形式保存 Kubernetes Cluster 的配置信息和各种资源的状态信息。当数据发生变化时,etcd 会快速地通知 Kubernetes 相关组件。键值对数据库储存K8S集群所有重要信息(持久化)
4.2 Node组件
管理维护pod运行的agent,直接跟容器引擎交互实现容器的生命周期管理
Kube-proxy
将service的流量转发到对应endpoint,负责写入规则至 IPTABLES、IPvs实现服务映射访间的
Flannel网络
维持各个节点上pod之间的通信。
4.3 重要组件 Coredns
可以为集群中的Svc创建一个域名IP的对应关系解析
Dashboard
给K8S集群提供一个B/S结构访问体系
Ingress Controller
官方只能实现四层代理, INGRESS可以实现七层代理
Federation
提供一个可以跨集群中心多K8S统一管理功能
Prometheus
提供K8sS集群的监控能力
ELK
提供K8s集群旦志统一分析介入平台
5. k8s网络架构概念
CNI(容器网络接口)
CNI是Container Network Interface的是一个标准的,通用的接口。现在容器平台:docker,kubernetes,mesos,容器网络解决方案:flannel,calico,weave。只要提供一个标准的接口,就能为同样满足该协议的所有容器平台提供网络功能,而CNI正是这样的一个标准接口协议。
pod的通信分类
同一个Pod内的多个容器之间:lo Network
各Pod之间 或 不同主机各Pod 的通讯:Over lay Network
Pod 与 Service之间的通讯:各节点的 Iptables规则 或者 IPVS
K8S整体网络模型图
K8S中的三种网络
node网络:负责不同主机之间的pod通信的入口和出口(物理网络)
service网络:负责对外提供服务(暴露网络)
pod网络:负责pod内部容器之间的通信 和 pod 与 pod之间的通信(内部网络)
K8S的网络通讯原理
同一个Pod内部通讯:
同一个Pod共享同一个网络命名空间,共享同一个 Linux协议栈。
Pod1 至 Pod2:
Pod1至Pod2不在同一台主机:Pod的地址是与 docker0在同一个网段的,但 docker0网段与宿主机网卡是两个完全不同的IP网段,并且不同Node之间的通信只能通过宿主机的物理网卡进行。将PoIP和所在 Node’MJIP关联起来,通过这个关联让Pod可以互相访问
Podl与Pod2在同一台机器:由 Docker0网桥直接转发请求至Pod2,不需要经过 Flannel
Pod 至 Service的网络:
Pod到外网:
Pod向外网发送请求,查找路由表,转发数据包到宿主机的网卡,宿主网卡完成路由选择后, iptables执行 Masquerade,把源IP更改为宿主网卡的IP,然后向外网服务器发送请求
外网访问Pod:
6. Kubernetes核心概念 6.1 container
容器可以运行服务和程序,容器是独立运行的一个或一组应用。容器可以进行启动、开始、停止、删除等操作,每个容器都是相互隔离的。可以把容器看作是一个简易版的linux环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行其中的应用程序。
6.2 namespace
可以将一个物理的cluster逻辑上划分成多个虚拟cluster,每个cluster就是一个namespace。不同的namespace里的资源是完全隔离的。
6.3 labels
标签用于区分对象(比如pod和services), kubernetes中的任意对象都是通过label进行标识,label的实质是一系列的key/value键值对,其中key与value由用户自己指定。label可以附加到各种资源对象上,如node、pod、service、RC等,一个资源对象可以定义任意数量的label,可以通过label selector(标签选择器)查询和筛选资源对象。label是rc和service运行的基础,两者通过label来进行关联node上运行的pod
6.4 pod
在kubernetes系统中, pod是最小部署单元, 一个pod包含一个或多个容器(一组容器的集合), pod 是一个可以被创建、销毁、调度、管理的最小部署单元。kubernates为每个pod都分配了唯一的ip地址, 称之为PodIP, 一个pod里的多个容器共享podip地址,它负责外部跟容器之间进行通信。
Pod主要分为两种类型:1、自主式pod,2、控制器管理pod
pod是kubernetes最重要的基本概念,也是k8s中的最小运行单元,一个pod中可以运行一个或多个的container及一个管理 container——pause
6.5 Controller Manager
Controller Manager 由 kube-controller-manager 和 cloud-controller-manager 组成, 是Kubernetes 的大脑, 它通过 apiserver 监控整个集群的状态, 并确保集群处于预期的工作状态。
6.6 services
services 是kubernetes最外围的单元,通过虚拟一个访问ip及服务端口,可以访问我们定义好的pod资源,是通过iptables的nat转发来实现,转发的目标端口为kube_proxy生成的随机端口。services代理pod集合对外表现是一个访问入口,分配一个集群ip地址,来自这个ip的请求将负载均衡转发后端pod中的容器,service通过lable selector选择一组pod提供服务。
deployment可以部署多个副本,每个pod 都有自己的IP,外界如何访问这些副本那?
答案是:service
k8s的 service定义了外界访问一组特定pod的方式。service有自己的IP和端口,service为pod提供了负载均衡。
k8s运行容器pod与访问容器这两项任务分别由controller和service执行。
K8S-Service类型:
ClusterIp:默认类型,自动分配一个仅Cluster内部可以访问的虚拟IP
NodePort:在ClusterIP基础上为Service在每台机器上绑定一个端口,这样可以通过NodeIP:NodePort来访问服务
LoadBalancer:在NodePort基础上,借助cloud provider创建一个外部负载均衡器,并将请求转发到NodeIP:NodePort
二、部署K8S集群(kubeadm)
准备三台Linux虚拟机(K8S集群三台起步),系统用CentOS7.4,虚拟机配置是2颗CPU和2G内存(K8S最低要求的配置),网络使用桥接网卡方式并使用静态IP
主机名
IP地址
角色描述
Master
ens32:192.168.2.1
K8S Master节点/ETCD节点
Node1
ens32:192.168.2.2
K8S Node节点
Node2
ens32:192.168.2.3
K8S Node节点
harbor
ens32:192.168.2.4
docker 镜像仓库节点
1. 系统环境初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 hostnamectl set-hostname master1 hostnamectl set-hostname node1 hostnamectl set-hostname node2 hostnamectl set-hostname harbor logout timedatectl set-timezone Asia/Shanghai timedatectl set-local-rtc 0 systemctl restart rsyslog systemctl restart crond wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo yum install -y epel-release conntrack ntpdate ntp ipvsadm ipset iptables-services iptables curl sysstat libseccomp wget unzip net-tools git yum-utils jq device-mapper-persistent-data lvm2 ntpdate ntp1.aliyun.com cat <<EOF>> /etc/hosts 192.168.2.1 master1 192.168.2.2 node1 192.168.2.3 node2 192.168.2.4 hub.lemon.com 199.232.68.133 raw.githubusercontent.com EOF systemctl stop firewalld systemctl disable firewalld systemctl start iptables systemctl enable iptables iptables -F && service iptables save swapoff -a sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab setenforce 0 sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config systemctl stop postfix && systemctl disable postfix mkdir /var/log /journal mkdir /etc/systemd/journald.conf.d cat > /etc/systemd/journald.conf.d/99-prophet.conf <<EOF [Journal] # 持久化保存到磁盘 Storage=persistent # 压缩历史日志 Compress=yes SyncIntervalSec=5m RateLimitInterval=30s RateLimitBurst=1000 # 最大占用空间 10G SystemMaxUse=10G # 单日志文件最大 200 MSystemMaxFileSize=200M # 日志保存时间 2 周 MaxRetentionSec=2week # 不将日志转发到 syslog ForwardToSyslog=no EOF systemctl restart systemd-journald
2. 升级系统内核并优化
CentOS 7.x 系统自带的 3.10.x 内核存在一些 Bugs,导致运行的 Docker、Kubernetes 不稳定,例如:
高版本的 docker(1.13 以后) 启用了 3.10 kernel 实验支持的 kernel memory account 功能(无法关闭),当节点压力大如频繁启动和停止容器时会导致 cgroup memory leak;网络设备引用计数泄漏, 会导致类似于报错:”kernel:unregister_netdevice: waiting for eth0 to become free. Usage count = 1”;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm yum --enablerepo=elrepo-kernel install -y kernel-lt cat /boot/grub2/grub.cfg |grep menuentry grub2-editenv list yum update -y grub2-set-default "CentOS Linux (4.4.236-1.el7.elrepo.x86_64) 7 (Core)" && reboot uname -r modprobe br_netfilter cat > /etc/sysconfig/modules/ipvs.modules <<EOF #!/bin/bash modprobe -- ip_vs modprobe -- ip_vs_rr modprobe -- ip_vs_wrr modprobe -- ip_vs_sh modprobe -- nf_conntrack_ipv4 EOF chmod 755 /etc/sysconfig/modules/ipvs.modules bash /etc/sysconfig/modules/ipvs.modules lsmod | grep -e ip_vs -e nf_conntrack_ipv4 chmod a+x /etc/rc.d/rc.local echo 'bash /etc/sysconfig/modules/ipvs.modules' >> /etc/rc.localcat > /etc/sysctl.d/kubernetes.conf <<EOF # 关闭IPV6协议 net.ipv6.conf.all.disable_ipv6=1 # 开启网桥模式 net.bridge.bridge-nf-call-iptables=1 net.bridge.bridge-nf-call-ip6tables=1 # 开启路由转发 net.ipv4.ip_forward=1 net.ipv4.tcp_tw_recycle=0 # 禁止使用 swap 空间,只有当系统 OOM 时才允许使用它 vm.swappiness=0 # 不检查物理内存是否够用 vm.overcommit_memory=1 # 开启 OOM vm.panic_on_oom=0 fs.inotify.max_user_watches=1048576 fs.inotify.max_user_instances=8192 # 开启的文件句柄数目 fs.file-max=52706963 # 开启对大的文件数目 fs.nr_open=52706963 # ~~~ net.netfilter.nf_conntrack_max=2310720 vm.dirty_bytes=15728640 EOF sysctl -p /etc/sysctl.d/kubernetes.conf
3. 部署Docker、kubeadm、kubectl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 cd /etc/yum.repos.d/wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo cat <<EOF>> kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/ enabled=1 gpgcheck=0 EOF yum clean all && yum makecache && cd yum -y install docker-ce-18.09.6 mkdir -p /etc/docker cat > /etc/docker/daemon.json <<EOF { "registry-mirrors": ["https://p8hkkij9.mirror.aliyuncs.com"], "exec-opts": ["native.cgroupdriver=systemd"] } EOF systemctl start docker systemctl enable docker yum -y install kubeadm-1.15.1 kubectl-1.15.1 kubelet-1.15.1 systemctl enable kubelet
4. 初始化主节点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 cat image-k8s-v1_15_1.sh images=( kube-apiserver:v1.15.1 kube-controller-manager:v1.15.1 kube-scheduler:v1.15.1 kube-proxy:v1.15.1 pause:3.1 etcd:3.3.10 coredns:1.3.1 ) for imageName in ${images[@]} ; do docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName k8s.gcr.io/$imageName docker rmi registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName done bash image-k8s-v1_15_1.sh docker images REPOSITORY TAG IMAGE ID CREATED SIZE k8s.gcr.io/kube-apiserver v1.15.1 68c3eb07bfc3 14 months ago 207MB k8s.gcr.io/kube-controller-manager v1.15.1 d75082f1d121 14 months ago 159MB k8s.gcr.io/kube-scheduler v1.15.1 b0b3c4c404da 14 months ago 81.1MB k8s.gcr.io/kube-proxy v1.15.1 89a062da739d 14 months ago 82.4MB k8s.gcr.io/coredns 1.3.1 eb516548c180 20 months ago 40.3MB k8s.gcr.io/etcd 3.3.10 2c4adeb21b4f 21 months ago 258MB k8s.gcr.io/pause 3.1 da86e6ba6ca1 2 years ago 742kB [root@master1 ~] [root@master1 ~] apiVersion: kubeadm.k8s.io/v1beta2 bootstrapTokens: - groups: - system:bootstrappers:kubeadm:default-node-token token: abcdef.0123456789abcdef ttl: 24h0m0s usages: - signing - authentication kind: InitConfiguration localAPIEndpoint: advertiseAddress: 192.168.2.1 bindPort: 6443 nodeRegistration: criSocket: /var/run/dockershim.sock name: master1 taints: - effect: NoSchedule key: node-role.kubernetes.io/master --- apiServer: timeoutForControlPlane: 4m0s apiVersion: kubeadm.k8s.io/v1beta2 certificatesDir: /etc/kubernetes/pki clusterName: kubernetes controllerManager: {} dns: type : CoreDNS etcd: local : dataDir: /var/lib/etcd imageRepository: k8s.gcr.io kind: ClusterConfiguration kubernetesVersion: v1.15.1 networking: dnsDomain: cluster.local podSubnet: "10.244.0.0/16" serviceSubnet: 10.96.0.0/12 scheduler: {} --- apiVersion: kubeproxy.config.k8s.io/v1alpha1 kind: KubeProxyConfiguration featureGates: SupportIPVSProxyMode: true mode: ipvs [root@master1 ~]
5. 加入主节点以及其余工作节点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 [root@master1 ~] Your Kubernetes control-plane has initialized successfully! To start using your cluster, you need to run the following as a regular user: mkdir -p $HOME /.kube sudo cp -i /etc/kubernetes/admin.conf $HOME /.kube/config sudo chown $(id -u):$(id -g) $HOME /.kube/config You should now deploy a pod network to the cluster. Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at: https://kubernetes.io/docs/concepts/cluster-administration/addons/ Then you can join any number of worker nodes by running the following on each as root: kubeadm join 192.168.2.1:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:4dc6790b3232bc4fd7f80e614d38e21b15cfd3d318f09b596b68336375b381e4 systemctl status kubelet | grep running Active: active (running) since 日 2020-09-13 01:53:32 CST; 6min ago [root@master1 ~] NAME STATUS ROLES AGE VERSION master1 NotReady master 11m v1.15.1 node1 NotReady <none> 2m13s v1.15.1 node2 NotReady <none> 2m10s v1.15.1
这就需要下面来部署flannel来解决扁平化网络问题
6. 部署 CNI - flannel网络 1 2 3 4 5 6 7 8 9 kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml [root@master1 ~] NAME STATUS ROLES AGE VERSION master1 Ready master 18m v1.15.1 node1 Ready <none> 8m38s v1.15.1 node2 Ready <none> 8m35s v1.15.1+
7. 查看集群状态 和相关的容器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [root@master1 ~] NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME master1 Ready master 20m v1.15.1 192.168.2.1 <none> CentOS Linux 7 (Core) 4.4.236-1.el7.elrepo.x86_64 docker://18.9.6 node1 Ready <none> 10m v1.15.1 192.168.2.2 <none> CentOS Linux 7 (Core) 4.4.236-1.el7.elrepo.x86_64 docker://18.9.6 node2 Ready <none> 10m v1.15.1 192.168.2.3 <none> CentOS Linux 7 (Core) 4.4.236-1.el7.elrepo.x86_64 docker://18.9.6 [root@master1 ~] NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES kube-system coredns-5c98db65d4-57czz 1/1 Running 4 136d 10.244.0.9 master1 <none> <none> kube-system coredns-5c98db65d4-s9fsm 1/1 Running 4 136d 10.244.0.8 master1 <none> <none> kube-system etcd-master1 1/1 Running 3 136d 192.168.2.1 master1 <none> <none> kube-system kube-apiserver-master1 1/1 Running 3 136d 192.168.2.1 master1 <none> <none> kube-system kube-controller-manager-master1 1/1 Running 3 136d 192.168.2.1 master1 <none> <none> kube-system kube-flannel-ds-amd64-6qw52 1/1 Running 20 136d 192.168.2.3 node2 <none> <none> kube-system kube-flannel-ds-amd64-hj7wr 1/1 Running 1 136d 192.168.2.1 master1 <none> <none> kube-system kube-flannel-ds-amd64-wc96r 1/1 Running 20 136d 192.168.2.2 node1 <none> <none> kube-system kube-proxy-5xcpl 1/1 Running 5 136d 192.168.2.3 node2 <none> <none> kube-system kube-proxy-lwjps 1/1 Running 4 136d 192.168.2.1 master1 <none> <none> kube-system kube-proxy-r77pb 1/1 Running 4 136d 192.168.2.2 node1 <none> <none> kube-system kube-scheduler-master1 1/1 Running 3 136d 192.168.2.1 master1 <none> <none>
8. 搭建配置harbor私有仓库
安装Harbor需要先安装docker和docker-compose,上面的系统初始化、系统升级和优化、安装Docker的步骤这里不再陈述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 cat /etc/docker/daemon.json { "registry-mirrors" : ["https://p8hkkij9.mirror.aliyuncs.com" ], "exec-opts" : ["native.cgroupdriver=systemd" ], "insecure-registries" : ["https://hub.lemon.com" ] } [root@harbor ~] [root@harbor ~] [root@harbor ~] [root@harbor ~] docker-compose version 1.23.2, build 1110ad01 [root@harbor ~] /usr/bin/openssl [root@harbor ~] [root@harbor ssl]
1 2 3 4 5 [root@harbor ssl] Signature ok subject=/C=CN/ST=Beijing/L=Beijing/O=lemon/OU=hub/CN=hub.lemon.com Getting CA Private Key
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [root@harbor ssl] [root@harbor ssl] 总用量 4.0K -rw-r--r-- 1 root root 1.9K 9月 13 02:23 hub.lemon.com.crt [root@harbor ssl] [root@harbor ssl] [root@harbor ssl] [root@harbor ssl] [root@harbor ssl]
1 2 3 [root@harbor ~] [root@harbor ~] [root@harbor ~]
1 2 3 [root@harbor harbor] [root@harbor harbor]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [root@harbor harbor] [root@harbor harbor] REPOSITORY TAG IMAGE ID CREATED SIZE goharbor/prepare v1.9.0 aa594772c1e8 12 months ago 147MB [root@harbor harbor] 1、停止Harbor [root@harbor ~] 2、启动Harbor [root@harbor ~]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@harbor ~] docker-compose -f /usr/local /harbor/docker-compose.yml up -d END [root@harbor ~] [root@harbor harbor] Username: admin Password: Harbor12345 WARNING! Your password will be stored unencrypted in /root/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/ Login Succeeded https://hub.lemon.com/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [root@master1 ~] Username: admin Password: WARNING! Your password will be stored unencrypted in /root/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/ Login Succeeded [root@master1 ~] [root@master1 ~] [root@master1 ~] Untagged: httpd:latest Untagged: httpd@sha256:0fce91cc167634ede639701b7dd1d8093f4ad2f2d9d0d5a8f4be2eaef8a570fb
1 2 3 4 5 6 7 8 9 [root@master1 ~] The push refers to repository [hub.lemon.com/library/httpd] f1fee4547086: Pushed a6a46c0268b1: Pushed 951b1be5cf2d: Pushed d37da03a9458: Pushed 07cab4339852: Pushed v1: digest: sha256:8c9bc11ca46ffd0b6b8a00e30aa670abef6c0d5d308e318a5cb8cf9e23931649 size: 1367
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 [root@master1 ~] Untagged: hub.lemon.com/library/httpd:v1 Untagged: hub.lemon.com/library/httpd@sha256:8c9bc11ca46ffd0b6b8a00e30aa670abef6c0d5d308e318a5cb8cf9e23931649 Deleted: sha256:6d82971d37d087be9917ab2015a4dc807569c736d3f2017c0821ddc4ed126617 Deleted: sha256:59a897aaa844713f078ea9234bd61b0f4885598a9ffb1267b4c59983813abb52 Deleted: sha256:6942605f2c5a8ba622491e369f2585daafe749a645835f5abb4fb9d11803664d Deleted: sha256:0f44970c8ecb7e1107f45ff7d5a7f7f3799a9821dce5cd30c51f2f7641339665 Deleted: sha256:97635989e45ed57deef09cd09be52d008a073f2e1e045a1ba91956fbc2db2961 Deleted: sha256:07cab433985205f29909739f511777a810f4a9aff486355b71308bb654cdc868 [root@master1 ~] kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead. deployment.apps/httpd-01 created [root@master1 ~] NAME READY UP-TO-DATE AVAILABLE AGE httpd-01 1/1 1 1 21s [root@master1 ~] NAME DESIRED CURRENT READY AGE httpd-01-6c9fbcfb65 1 1 1 47s [root@master1 ~] NAME READY STATUS RESTARTS AGE httpd-01-6c9fbcfb65-jvmc8 1/1 Running 0 58s [root@master1 ~] NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES httpd-01-6c9fbcfb65-jvmc8 1/1 Running 0 69s 10.244.1.2 node1 <none> <none> [root@master1 ~] bin build cgi-bin conf error htdocs icons include logs modules [root@master1 ~] <html><body><h1>It works!</h1></body></html> docker rm -v $(docker ps -qa -f status=exited)
9. 基本的使用下K8S 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 [root@master1 ~] NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES httpd-01-6c9fbcfb65-jvmc8 1/1 Running 1 12h 10.244.1.3 node1 <none> <none> kubectl -n default delete pod httpd-01-6c9fbcfb65-jvmc8 [root@master1 ~] NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES httpd-01-6c9fbcfb65-9hlhv 1/1 Running 1 12h 10.244.1.3 node1 <none> <none> [root@master1 ~] deployment.extensions/httpd-01 scaled [root@master1 ~] NAME READY UP-TO-DATE AVAILABLE AGE httpd-01 3/3 3 3 12h [root@master1 ~] NAME READY STATUS RESTARTS AGE httpd-01-6c9fbcfb65-9hlhv 1/1 Running 1 12h httpd-01-6c9fbcfb65-hjhjn 1/1 Running 0 51s httpd-01-6c9fbcfb65-x52hh 1/1 Running 0 51s [root@master1 ~] NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES httpd-01-6c9fbcfb65-9hlhv 1/1 Running 1 12h 10.244.1.3 node1 <none> <none> httpd-01-6c9fbcfb65-hjhjn 1/1 Running 0 2m8s 10.244.2.3 node2 <none> <none> httpd-01-6c9fbcfb65-x52hh 1/1 Running 0 2m8s 10.244.2.2 node2 <none> <none> [root@master1 ~] kubectl expose deployment nginx --port=80 --target-port=8000 [root@master1 ~] NAME READY UP-TO-DATE AVAILABLE AGE httpd-01 3/3 3 3 13h [root@master1 ~] service/httpd-01 exposed [root@master1 ~] NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR httpd-01 ClusterIP 10.99.191.162 <none> 88/TCP 30s run=httpd-01 kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 13h <none> [root@master1 ~] NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES httpd-01-6c9fbcfb65-9hlhv 1/1 Running 1 13h 10.244.1.3 node1 <none> <none> httpd-01-6c9fbcfb65-hjhjn 1/1 Running 0 23m 10.244.2.3 node2 <none> <none> httpd-01-6c9fbcfb65-x52hh 1/1 Running 0 23m 10.244.2.2 node2 <none> <none> [root@master1 ~] root@httpd-01-6c9fbcfb65-9hlhv:/usr/local /apache2 [root@master1 ~] root@httpd-01-6c9fbcfb65-hjhjn:/usr/local /apache2 [root@master1 ~] root@httpd-01-6c9fbcfb65-x52hh:/usr/local /apache2 [root@master1 ~] node2-10.244.2.3 [root@master1 ~] node2-10.244.2.2 [root@master1 ~] node1-10.244.1.3 [root@master1 ~] node2-10.244.2.3 [root@master1 ~] IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 10.96.0.1:443 rr -> 192.168.2.1:6443 Masq 1 3 0 TCP 10.96.0.10:53 rr -> 10.244.0.6:53 Masq 1 0 0 -> 10.244.0.7:53 Masq 1 0 0 TCP 10.96.0.10:9153 rr -> 10.244.0.6:9153 Masq 1 0 0 -> 10.244.0.7:9153 Masq 1 0 0 TCP 10.99.191.162:88 rr -> 10.244.1.3:80 Masq 1 0 1 -> 10.244.2.2:80 Masq 1 0 1 -> 10.244.2.3:80 Masq 1 0 2 UDP 10.96.0.10:53 rr -> 10.244.0.6:53 Masq 1 0 0 -> 10.244.0.7:53 Masq 1 0 0 [root@master1 ~] apiVersion: v1 kind: Service metadata: creationTimestamp: "2020-09-13T07:50:56Z" labels: run: httpd-01 name: httpd-01 namespace: default resourceVersion: "21567" selfLink: /api/v1/namespaces/default/services/httpd-01 uid: 624c99c1-b27f-4990-aa5b-d381e0a37755 spec: clusterIP: 10.99.191.162 ports: - port: 88 protocol: TCP targetPort: 80 selector: run: httpd-01 sessionAffinity: None type : NodePort status: loadBalancer: {} [root@master1 ~] NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR httpd-01 NodePort 10.99.191.162 <none> 88:32552/TCP 28m run=httpd-01 kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 14h <none> [root@master1 ~] tcp6 0 0 :::32552 :::* LISTEN 1945/kube-proxy [root@node1 ~] tcp6 0 0 :::32552 :::* LISTEN 1568/kube-proxy [root@node2 ~] tcp6 0 0 :::32552 :::* LISTEN 1601/kube-proxy
至此整体kubernetes集群架构搭建完成
三、Kubectl常用命令
kubectl官方详细命令
1. 查看集群状态 1 2 3 4 5 kubectl version --short=true kubectl cluster-info
2. 创建资源对象 1 2 3 4 5 6 7 8 kubectl run name --image=(镜像名) --replicas=(副本数) --port=(容器要暴露的端口) --labels=(设定自定义标签) kubectl create -f **.yaml kubectl apply -f **.yaml
3、查看资源对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 kubectl get namespace kubectl get pods,services -o wide kubectl get pod -l "key=value,key=value" -n kube-system kubectl get pod -l "key1 in (val1,val2),!key2" -L key kubectl get pod -w
4、打印容器中日志信息 1 2 kubectl logs name -f -c container_name -n kube-system
5、在容器中执行命令 1 2 3 4 5 kubectl exec name -c container_name -n kube-system -- 具体命令 kubectl exec -it pod_name /bin/sh 进入容器的交互式shell
6、删除资源对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 kubectl delete [pods/services/deployments/...] name kubectl delete [pods/services/deployments/...] -l key=value -n kube-system kubectl delete [pods/services/deployments/...] --all -n kube-system kubectl delete [pods/services/deployments/...] source_name --force --grace-period=0 -n kube-system kubectl delete -f xx.yaml kubectl apply -f xx.yaml --prune -l <labels> kubectl delete rs rs_name --cascade=false
7、更新资源对象 1 2 kubectl replace -f xx.yaml --force
8、将服务暴露出去(创建Service) 1 kubectl expose deployments/deployment_name --type ="NodePort" --port=(要暴露的容器端口) --name=(Service对象名字)
9、扩容和缩容 1 2 3 4 kubectl scale deployment/deployment_name --replicas=N kubectl scale deployment/deployment_name --replicas=N --current-replicas=M
10、查看API版本
11、在本地主机上为API Server启动一个代理网关 1 2 3 4 5 kubectl proxy --port=8080 curl localhost:8080/api/v1/namespaces/ | jq .items[].metadata.name
12、当定义资源配置文件时,不知道怎么定义的时候,可以查看某类型资源的配置字段解释 1 2 kubectl explain pods/deployments/...
13、查看某资源对象的配置文件 1 2 kubectl get source_type source_name -o yaml --export
14、标签管理相关命令 1 2 3 4 5 kubectl label pods/pod_name key=value kubectl label nodes node_name key=value
15、注解管理相关命令 1 kubectl annotate pods pod_name key=value
16、patch修改Deployment控制器进行控制器升级 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 kubectl patch deployment deployment-demo -p '{"spec": {"minReadySeconds": 5}}' kubectl set image deployments deployment-demo myapp=ikubernetes/myapp:v2 kubectl rollout status deployment deployment-demo kubectl get deployments deployment-demo --watch kubectl rollout pause deployments deployment-demo kubectl rollout resume deployments deployment-demo kubectl rollout history deployments deployment-demo kubectl rollout undo deployments deployment-demo --to-revision=2
四、k8s的资源清单(重点) 1. 资源类型
k8s中所有的资源都会抽象为资源,资源实例化之后,叫做对象。
分为以下三种:
1.1 名称空间级别
服务发现及负载均衡型资源( ServiceDiscovery LoadBalance): Service(SVC)、Ingress、……
配置与存储型资源: Volume(存储卷)、CSI(容器存储接口,可以扩展各种各样的第三方存储卷)
特殊类型的存储卷: ConfigMap(当配置中心来使用的资源类型)、Secret(保存敏感数据)、DownwardAPI(把外部环境中的信息输出给容器)
工作负载型资源(workload): Pod(pause)、Replicase、 Deployment、StatefulSet、DaemonSet、Job、Cron Job(Replication Controller在v1.11版本被废弃)
1.2 集群级资源级别
Namespace
Node
Role
Clusterrole
RoleBinding
ClusterRoleBinding
1.3 元数据型资源
HPA
PodTemplate
Limi tRange
2. 资源清单
在k8s中,一般使用yaml格式的文件来创建符合我们预期期望的pod,这样的yaml文件我们一般称为资源清单
2.1 yaml语法格式
是一个可读性高,用来表达数据序列的格式。YAML 的意思其实是:仍是一种标记语言,但为了强调这种语言以数据做为中心,而不是以标记语言为重点。
基本语法
缩进时不允许使用Tab键,只允许使用空格
缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
# 标识注释,从这个字符一直到行尾,都会被解释器忽略
YAML 支持的数据结构
对象:键值对的集合,又称为映射(mapping)、哈希(hashes)、字典(dictionary)
数组:一组按次序排列的值,又称为序列(sequence)、列表(list)
纯量(scalars):单个的、不可再分的值
对象类型:对象的一组键值对,使用冒号结构表示
Yaml 也允许另一种写法,将所有键值对写成一个行内对象
1 hash: { name: Steve , age: 18 }
数组类型:一组连词线开头的行,构成一个数组
数组也可以采用行内表示法
复合结构:对象和数组可以结合使用,形成复合结构
1 2 3 4 5 6 7 8 9 languages: - Ruby - Perl - Python websites: YAML: yaml.org Ruby: ruby-lang.org Python: python.org Perl: use.perl.org
纯量:纯量是最基本的、不可再分的值。以下数据类型都属于纯量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 1 字符串布尔值整数浮点数 Null 2 时间 日期 数值直接以字面量的形式表示 number: 12.30 布尔值用true和false表示 isSet: true null 用 ~ 表示 parent: ~ 时间采用 ISO8601 格式 iso8601: 2001-12-14t21:59:43.10-05:00 日期采用复合 iso8601 格式的年、月、日表示 date: 1976-07-31 YAML允许使用两个感叹号,强制转换数据类型 e: !!str 123 f: !!str true
字符串
字符串默认不使用引号表示
如果字符串之中包含空格或特殊字符,需要放在引号之中
单引号和双引号都可以使用,双引号不会对特殊字符转义
1 2 s1: '内容\n字符串' s2: "内容\n字符串"
单引号之中如果还有单引号,必须连续使用两个单引号转义
字符串可以写成多行,从第二行开始,必须有一个单空格缩进。换行符会被转为空格
多行字符串可以使用|保留换行符,也可以使用>折叠换行
1 2 3 4 5 6 7 this: | Foo Bar that: > Foo Bar
+ 表示保留文字块末尾的换行,- 表示删除字符串末尾的换行
1 2 3 4 5 6 7 8 s1: | Foo s2: |+ Foo s3: |- Foo
2.2 k8s剧本的常用字段
必须存在的属性
参数名
字段类型
说明
apiVersion
String
K8S API 的版本,目前基本是v1,可以用 kubectl api-version 命令查询
kind
String
这里指的是 yaml 文件定义的资源类型和角色, 比如: Pod
metadata
Object
元数据对象,固定值写 metadata
metadata.name
String
元数据对象的名字,这里由我们编写,比如命名Pod的名字
metadata.namespace
String
元数据对象的命名空间,由我们自身定义
Spec
Object
详细定义对象,固定值写Spec
spec.containers[]
list
这里是Spec对象的容器列表定义,是个列表
spec.containers[].name
String
这里定义容器的名字
spec.containers[].image
String
这里定义要用到的镜像名称
主要属性【这些属性比较重要,如果不指定的话系统会自动补充默认值】
参数名称
字段类型
说明
spec.containers[].name
String
这里定义容器的名字
spec.containers[].image
String
这里定义要用到的镜像名称
spec.containers[].imagePullPolicy
String
定义镜像拉取策略,有Always、Never、IfNotPresent三个值可选(1)Always:意思是每次都尝试重新拉取镜像(2)Never:表示仅使用本地镜像(3)lfNotPresent:如果本地有镜像就使用本地镜像,没有就拉取在线镜像。上面三个值都没设置的话,默认是Always。
spec.containers[].command[]
List
指定容器启动命令,因为是数组可以指定多个,不指定则使用镜像打包时使用的启动命令。
spec.containers[].args[]
List
指定容器启动命令参数,因为是数组可以指定多个。
spec.containers[].workingDir
String
指定容器的工作目录,进入容器时默认所在的目录
spec.containers[].volumeMounts[]
List
指定容器内部的存储卷配置
spec.containers[].volumeMounts[].name
String
指定可以被容器挂载的存储卷的名称
spec.containers[].volumeMounts[].mountPath
String
指定可以被容器挂载的存储卷的路径
spec.containers[].volumeMounts[].readOnly
String
设置存储卷路经的读写模式,true或者false,默认为读写模式
spec.containers[].ports[]
List
指定容器需要用到的端口列表
spec.containers[].ports[].name
String
指定端口名称
spec.containers[].ports[].containerPort
String
指定容器需要监听的端口号
spec.containers[].ports[].hostPort
String
指定容器所在主机需要监听的端口号,默认跟上面containerPort相同,注意设置了hostPort同一台主机无法启动该容器的相同副本(因为主机的端口号不能相同,这样会冲突)
spec.containers[].ports[].protocol
String
指定端口协议,支持TCP和UDP,默认值为 TCP
spec.containers[].env[]
List
指定容器运行前需设置的环境变量列表
spec.containers[].env[].name
String
指定环境变量名称
spec.containers[].env[].value
String
指定环境变量值
spec.containers[].resources
Object
指定资源限制和资源请求的值(这里开始就是设置容器的资源上限)
spec.containers[].resources.limits
Object
指定设置容器运行时资源的运行上限
spec.containers[].resources.limits.cpu
String
指定CPU的限制,单位为core数,将用于docker run –cpu-shares参数这里前面文章 Pod资源限制有讲过)
spec.containers[].resources.limits.memory
String
指定MEM内存的限制,单位为MlB、GiB
spec.containers[].resources.requests
Object
指定容器启动和调度时的限制设置
spec.containers[].resources.requests.cpu
String
CPU请求,单位为core数,容器启动时初始化可用数量
spec.containers[].resources.requests.memory
String
内存请求,单位为MIB、GiB,容器启动的初始化可用数量
额外的参数项
参数名称
字段类型
说明
spec.restartPolicy
String
定义Pod的重启策略,可选值为Always、OnFailure,默认值为Always。1.Always:Pod一旦终止运行,则无论容器是如何终止的,kubelet服务都将重启它。2.OnFailure:只有Pod以非零退出码终止时,kubelet才会重启该容器。如果容器正常结束(退出码为0),则kubelet将不会重启它。3.Never:Pod终止后,kubelet将退出码报告给Master,不会重启该Pod。
spec.nodeSelector
Object
定义Node的Label过滤标签,以key:value格式指定,选择node节点去运行
spec.imagePullSecrets
Object
定义pull镜像时使用secret名称,以name:secretkey格式指定
spec.hostNetwork
Boolean
定义是否使用主机网络模式,默认值为false。设置true表示使用宿主机网络,不使用docker网桥,同时设置了true将无法在同一台宿主机上启动第二个副本。
查看资源有那些资源清单属性,使用以下命令
查看属性说明,使用以下命令
1 kubectl explain pod.apiVersion
资源清单格式
1 2 3 4 5 6 7 8 9 apiVersion: group/apiversion kind: metadata: name: namespace: lables: annotations: spec: status:
资源清单的常用命令
1.获取apiVersion版本信息
2.获取资源的apiVersion的版本信息(以pod为例),该命令同时输出属性设置帮助文档
字段配置格式
1 2 3 4 5 6 7 8 apiVersion <string> metadata <Object> labels <map[string]string> finalizers <[]string> ownerReferences <[]Object> hostPID <boolean> priority <integer> name <string> -required-
示例:通过定义清单文件创建Pod
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 apiVersion: v1 kind: Pod metadata: name: k8s-apache-pod namespace: default labels: app: httpd spec: containers: - name: web01 image: hub.lemon.com/library/httpd:v1
通过yaml文件创建pod
1 kubectl apply -f xxx.yaml
获取资源的资源配置文件
1 2 kubectl get pod {podName} -o yaml
3、pod的生命周期
pod对象自从创建开始至终止退出的时间范围称为生命周期,在这段时间中,pod会处于多种不同的状态,并执行一些操作;其中,创建主容器为必须的操作,其他可选的操作还包括运行初始化容器(init container)、容器启动后钩子(start hook)、容器的存活性探测(liveness probe)、就绪性探测(readiness probe)以及容器终止前狗子(pre stop hook)等,这些操作是否执行则取决于pod的定义
init C & start-stop & readiness & liveness 整体流程:
1、kubectl 调用 apiserver –> etcd –> kubelet –> CRI 进行容器初始化
2、首先会先启动一个pause容器(任何pod启动时都会先pause容器)
3、init C 容器不会伴随整个pod的生命周期;如果是正常退出(0),进入main C,否则(非0),一直重启 失败。
4、进入main C后有两个参数,start:在刚启动时可以允许他执行一个命令,stop:退出时也可以允许他执行一个命令。
5、readiness模板:可以在main C运行的多少秒之后进行就绪检测;检测通过后,pod状态就为running,否则为failed。
6、liveness模板:伴随着整个main C的生命周期,当main C里面的进程 与 liveness检测结果不一致时,就可以执行对应的命令。
3.1 Init C
Pod能够具有多个容器,应用运行在容器里面,但是它也可能有一个或多个先于应用容器启动的Init容器
Init容器与普通的容器非常像,除了如下两点:
Init容器总是运行到成功完成为止
每个Init容器都必须在下一个Init容器启动之前成功完成
如果Pod的Init容器失败,Kubernetes会不断地重启该Pod,直到Init容器成功为止。然而,如果Pod对应的restartPolicy为Never,它不会重新启动, 因为Init容器具有与应用程序容器分离的单独镜像,所以它们的启动相关代码具有如下优势:
它们可以包含并运行实用工具,但是出于安全考虑,是不建议在应用程序容器镜像中包含这些实用工具的。
它们可以包含使用工具和定制化代码来安装,但是不能出现在应用程序镜像中。例如,创建镜像没必要FROM另一个镜像,只需要在安装过程中使用类似sed、awk、python或dig这样的工具。
应用程序镜像可以分离出创建和部署的角色,而没有必要联合它们构建一个单独的镜像。
Init容器使用LinuxNamespace,所以相对应用程序容器来说具有不同的文件系统视图。因此,它们能够具有访问Secret的权限,而应用程序容器则不能。
它们必须在应用程序容器启动之前运行完成,而应用程序容器是并行运行的,所以Init容器能够提供了一种简单的阻塞或延迟应用容器的启动的方法,直到满足了一组先决条件。
Init C 特殊说明:
在Pod启动过程中,Init容器会按顺序在网络和数据卷初始化之后启动。每个容器必须在下一个容器启动之前成功退出。
如果由于运行时或失败退出,将导致容器启动失败,它会根据Pod的restartPolicy指定的策略进行重试。然而,如果Pod的restartPolicy设置为Always,Init容器失败时会使用RestartPolicy策略。
在所有的Init容器没有成功之前,Pod将不会变成Ready状态。Init容器的端口将不会在Service中进行聚集。正在初始化中的Pod处于Pending状态,但应该会将Initializing状态设置为true。
如果Pod重启,所有Init容器必须重新执行。
对Init容器spec的修改被限制在容器image字段,修改其他字段都不会生效。更改Init容器的image字段,等价于重启该Pod。
Init容器具有应用容器的所有字段。除了readinessProbe,因为Init容器无法定义不同于完成(completion)的就绪(readiness)之外的其他状态。这会在验证过程中强制执行。
在Pod中的每个app和Init容器的名称必须唯一;与任何其它容器共享同一个名称,会在验证时抛出错误
Init 容器示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 apiVersion: v1 kind: Pod metadata: name: lemon-pod labels: app: lemon01 spec: containers: - name: main-container image: busybox:latest command: ['sh' , '-c' , 'echo The main-container is running! && sleep 3600' ] initContainers: - name: init-container01 image: busybox:latest command: ['sh' , '-c' , 'until test -e /live01; do echo waiting for live01; sleep 10; done;' ] - name: init-container02 image: busybox:latest command: ['sh' , '-c' , 'until test -e /live02; do echo waiting for live02; sleep 10; done;' ]
1 2 $ kubectl exec -it lemon-pod -c init-container01 -- touch /live01 $ kubectl exec -it lemon-pod -c init-container02 -- touch /live02
3.2 容器探针
探针是由kubelet对容器执行的定期诊断。要执行诊断,kubelet调用由容器实现的Handler。
探测方式:
readinessProbe:指示容器是否准备好服务请求。如果就绪探测失败,端点控制器将从与Pod匹配的所有Service的端点中删除该Pod的IP地址。初始延迟之前的就绪状态默认为Failure。如果容器不提供就绪探针,则默认状态为Success。
livenessProbe:指示容器是否正在运行。如果存活探测失败,则kubelet会杀死容器,并且容器将受到其重启策略的影响。如果容器不提供存活探针,则默认状态为Success。
有三种类型的处理程序:
ExecAction:在容器内执行指定命令。如果命令退出时返回码为0则认为诊断成功。
TCPSocketAction:对指定端口上的容器的IP地址进行TCP检查。如果端口打开,则诊断被认为是成功的。
HTTPGetAction:对指定的端口和路径上的容器的IP地址执行HTTPGet请求。如果响应的状态码大于等于200且小于400,则诊断被认为是成功的。
每次探测都将获得以下三种结果之一:
成功:容器通过了诊断。
失败:容器未通过诊断。
未知:诊断失败,因此不会采取任何行动
Init 容器init 模板检测探针 - 就绪检测
readinessProbe-httpget
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 apiVersion: v1 kind: Pod metadata: name: readiness-httpget-pod namespace: default labels: app: lemon02 spec: containers: - name: readiness-httpget-container image: httpd:latest imagePullPolicy: IfNotPresent readinessProbe: httpGet: port: 80 path: /index01.html initialDelaySeconds: 1 periodSeconds: 3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ kubectl get pod NAME READY STATUS RESTARTS AGE readiness-httpget-pod 0/1 Running 0 11m $ kubectl log -f readiness-httpget-pod 10.244.1.1 - - [25/Feb/2021:08:19:11 +0000] "GET /index01.html HTTP/1.1" 404 196 10.244.1.1 - - [25/Feb/2021:08:19:14 +0000] "GET /index01.html HTTP/1.1" 404 196 $ kubectl exec -it readiness-httpget-pod -- /bin/sh $ kubectl log -f readiness-httpget-pod 10.244.1.1 - - [25/Feb/2021:08:28:53 +0000] "GET /index01.html HTTP/1.1" 200 6 $ kubectl get pod NAME READY STATUS RESTARTS AGE readiness-httpget-pod 1/1 Running 0 22m
liveness 检测探针 - 存活检测
livenessProbe-exec
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 apiVersion: v1 kind: Pod metadata: name: liveness-exec-pod namespace: default labels: app: lemon03 spec: containers: - name: liveness-exec-container image: busybox:latest imagePullPolicy: IfNotPresent command: ["/bin/sh" , "-c" , "touch /tmp/live ; sleep 60; rm -rf /tmp/live; sleep 3600" ] livenessProbe: exec: command: ["test" , "-e" , "/tmp/live" ] initialDelaySeconds: 1 periodSeconds: 3
1 2 3 4 5 6 7 $ kubectl get pod -w NAME READY STATUS RESTARTS AGE liveness-exec-pod 1/1 Running 0 9s $ kubectl get pod NAME READY STATUS RESTARTS AGE liveness-exec-pod 1/1 Running 1 100s
livenessProbe-httpget
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 apiVersion: v1 kind: Pod metadata: name: liveness-httpget-pod namespace: default spec: containers: - name: liveness-httpget-container image: httpd:latest imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 livenessProbe: httpGet: port: http path: /index.html initialDelaySeconds: 1 periodSeconds: 3 timeoutSeconds: 10
1 2 3 4 5 6 7 8 9 $ kubectl get pod NAME READY STATUS RESTARTS AGE liveness-httpget-pod 1/1 Running 0 102s $ kubectl exec -it liveness-httpget-pod -- rm /usr/local /apache2/htdocs/index.html $ kubectl get pod NAME READY STATUS RESTARTS AGE liveness-httpget-pod 1/1 Running 1 2m53s
livenessProbe-tcp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 apiVersion: v1 kind: Pod metadata: name: probe-tcp spec: containers: - name: nginx image: nginx:latest livenessProbe: initialDelaySeconds: 5 timeoutSeconds: 1 tcpSocket: port: 8080
3.3 Pod hook
Podhook(钩子)是由Kubernetes管理的kubelet发起的,当容器中的进程启动前或者容器中的进程终止之前运行,这是包含在容器的生命周期之中。可以同时为Pod中的所有容器都配置hook。
Hook的类型包括两种:
exec:执行一段命令
HTTP:发送HTTP请求
start、stop 动作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 apiVersion: v1 kind: Pod metadata: name: lifecycle-demo spec: containers: - name: lifecycle-demo-container image: nginx:latest lifecycle: postStart: exec: command: ["/bin/sh" , "-c" , "echo Hello from the postStart handler > /var/message" ] preStop: exec: command: ["/bin/sh" , "-c" , "echo Hello from the poststop handler > /var/message" ]
3.4 Init C & 探针 整合使用
init-readiness-liveness-hook.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 apiVersion: v1 kind: Pod metadata: name: lemon-pod-irlp labels: app: lemon01 spec: initContainers: - name: init-container01 image: busybox:latest command: ['sh' , '-c' , 'until test -e /live01; do echo waiting for live01; sleep 10; done;' ] - name: init-container02 image: busybox:latest command: ['sh' , '-c' , 'until test -e /live02; do echo waiting for live02; sleep 10; done;' ] containers: - name: main-container image: busybox:latest command: ['sh' , '-c' , 'echo The main-container is running! && sleep 3600' ] imagePullPolicy: IfNotPresent readinessProbe: httpGet: port: 80 path: /index01.html initialDelaySeconds: 1 periodSeconds: 3 livenessProbe: exec: command: ["test" , "-e" , "/tmp" ] initialDelaySeconds: 1 periodSeconds: 3 lifecycle: postStart: exec: command: ["/bin/sh" , "-c" , "echo Hello from the postStart handler > /var/message" ] preStop: exec: command: ["/bin/sh" , "-c" , "echo Hello from the poststop handler > /var/message" ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 $ kubectl exec -it lemon-pod-irlp -c init-container01 -- touch /live01 $ kubectl exec -it lemon-pod-irlp -c init-container02 -- touch /live02 $ kubectl get pod NAME READY STATUS RESTARTS AGE lemon-pod-irlp 0/1 Running 0 51m $ kubectl describe pod lemon-pod-irlp Readiness probe failed: Get http://10.244.2.12:80/index01.html $ kubectl exec -it lemon-pod-irlp -c main-container -- /bin/sh / tcp 0 0 :::80 :::* LISTEN 287/httpd $ kubectl get pod NAME READY STATUS RESTARTS AGE lemon-pod-irlp 1/1 Running 0 4m42s $ kubectl exec -it lemon-pod-irlp -c main-container -- rm -rf /tmp/ $ kubectl get pod NAME READY STATUS RESTARTS AGE lemon-pod-irlp 1/1 Running 0 5m35s lemon-pod-irlp 0/1 Running 1 6m1s $ kubectl describe pod lemon-pod-irlp Liveness probe failed: $ kubectl exec -it lemon-pod-irlp -- cat /var/message Hello from the postStart handler
3.5 Pod的相位
Pod的status字段是一个PodStatus对象,PodStatus中有一个phase字段。
无论是手动创建还是通过控制器创建pod,pod对象总是应该处于其生命进程中以下几个相位之一:
挂起(Pending):Pod已被Kubernetes系统接受,但有一个或者多个容器镜像尚未创建。等待时间包括调度Pod的时间和通过网络下载镜像的时间,这可能需要花点时间。
运行中(Running):该Pod已经绑定到了一个节点上,Pod中所有的容器都已被创建。至少有一个容器正在运行,或者正处于启动或重启状态。
成功(Succeeded):Pod中的所有容器都被成功终止,并且不会再重启。
失败(Failed):Pod中的所有容器都已终止了,并且至少有一个容器是因为失败终止。也就是说,容器以非0状态退出或者被系统终止。
未知(Unknown):因为某些原因无法取得Pod的状态,通常是因为与Pod所在主机通信失败。
4、controllers 控制器
kubernetes不会直接创建pod,而是通过controller来管理pod的,所以controller负责维护集群的状态,比如故障检测、自动扩展、滚动更新。
Kubernetes中会有很多的controller(控制器),这些就相当于一个状态机(快照),用来控制Pod的具体状态和行为。
1. ReplicationController ReplicationController(RC)用来确保容器应用的副本数始终保持在用户定义的副本数,即如果有容器异常退出,会自动创建新的 Pod 来替代;而如果异常多出来的容器也会自动回收;
2. ReplicaSet 在新版本的 Kubernetes 中建议使用 ReplicaSet来取代 ReplicationController 。ReplicaSet 跟ReplicationController 没有本质的不同,只是名字不一样,并且 ReplicaSet 支持集合式的 selector;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 apiVersion: extensions/v1beta1 kind: ReplicaSet metadata: name: frontend spec: replicas: 3 selector: matchLabels: tier: web template: metadata: labels: tier: web spec: containers: - name: nginx image: nginx:latest env: - name: GET_HOSTS_FROM value: dns ports: - containerPort: 80
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 $ kubectl get pod --show-labels NAME READY STATUS RESTARTS AGE LABELS frontend-d8rs5 1/1 Running 0 29s tier=web frontend-gczvb 1/1 Running 0 29s tier=web frontend-twj7z 1/1 Running 0 29s tier=web $ kubectl label pod frontend-d8rs5 tier=lemon --overwrite=true pod/frontend-d8rs5 labeled $ kubectl get pod --show-labels NAME READY STATUS RESTARTS AGE LABELS frontend-d8rs5 1/1 Running 0 3m3s tier=lemon frontend-gczvb 1/1 Running 0 3m3s tier=web frontend-mrm8n 1/1 Running 0 10s tier=web frontend-twj7z 1/1 Running 0 3m3s tier=web $ kubectl delete pod --all pod "frontend-d8rs5" deleted pod "frontend-gczvb" deleted pod "frontend-mrm8n" deleted pod "frontend-twj7z" deleted $ kubectl get pod --show-labels NAME READY STATUS RESTARTS AGE LABELS frontend-26xlj 1/1 Running 0 11s tier=web frontend-m4vhl 1/1 Running 0 11s tier=web frontend-zs9tk 1/1 Running 0 11s tier=web $ kubectl delete rs frontend -n default
3. Deployment Deployments(无状态,守护进程类,只关注群体不关注个体)
一个 Deployment 控制器为 Pods和 ReplicaSets 提供声明式的更新能力。
虽然 ReplicaSet可以独立使用,但一般还是建议使用 Deployment来自动管理ReplicaSe,这样就无需担心跟其他机制的不兼容问题(比如 ReplicaSe不支持rolling-update 但 Deployment支持)。
Deployment 为 Pod 和 ReplicaSet 提供了一个声明式定义 (declarative) 方法,用来替代以前的ReplicationController 来方便的管理应用。典型的应用场景包括;
定义 Deployment 来创建 Pod 和 ReplicaSet
应用扩容和缩容
滚动升级和回滚
暂停和继续 Deployment
3.1 创建一个 deployment 对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 apiVersion: extensions/v1beta1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 template: metadata: labels: app: web spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80
1 2 3 4 5 6 7 8 9 10 11 12 13 $ kubectl get deploy NAME READY UP-TO-DATE AVAILABLE AGE nginx-deployment 3/3 3 3 62s $ kubectl get rs NAME DESIRED CURRENT READY AGE nginx-deployment-687dc75df7 3 3 3 65s $ kubectl get pod NAME READY STATUS RESTARTS AGE nginx-deployment-687dc75df7-88tkk 1/1 Running 0 68s nginx-deployment-687dc75df7-b6whp 1/1 Running 0 68s nginx-deployment-687dc75df7-ljh46 1/1 Running 0 68s
3.2 Deployment 扩容缩 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $ kubectl scale deployment nginx-deployment --replicas=5 deployment.extensions/nginx-deployment scaled $ kubectl get pod NAME READY STATUS RESTARTS AGE nginx-deployment-687dc75df7-88tkk 1/1 Running 0 7m22s nginx-deployment-687dc75df7-b6whp 1/1 Running 0 7m22s nginx-deployment-687dc75df7-ljh46 1/1 Running 0 7m22s nginx-deployment-687dc75df7-rnjzt 1/1 Running 0 8s nginx-deployment-687dc75df7-wfx74 1/1 Running 0 8s $ kubectl scale deployment nginx-deployment --replicas=2 deployment.extensions/nginx-deployment scaled $ kubectl get pod NAME READY STATUS RESTARTS AGE nginx-deployment-687dc75df7-88tkk 1/1 Running 0 8m18s nginx-deployment-687dc75df7-ljh46 1/1 Running 0 8m18s
如果集群支持 horizontal pod autoscaling 的话,还可以为Deployment设置自动扩展
1 kubectl autoscale deployment nginx-deployment --min=10--max=15--cpu-percent=80
3.3 滚动升级和回滚
Deployment 更新策略
Deployment 可以保证在升级时只有一定数量的 Pod 是 down 的。默认的,它会确保至少有比期望的Pod数量少一个是up状态(最多一个不可用)。
Deployment 同时也可以确保只创建出超过期望数量的一定数量的 Pod。默认的,它会确保最多比期望的Pod数量多一个的 Pod 是 up 的(最多1个 surge )。
未来的 Kuberentes 版本中,将从1-1变成25%-25% 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 $ kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1 $ kubectl describe deploy nginx-deployment | grep -i 'image' Image: nginx:1.9.1 $ kubectl rollout undo deployment/nginx-deployment $ kubectl describe deploy nginx-deployment | grep -i 'image' Image: nginx:1.7.9 $ kubectl edit deployment nginx-deployment 40 - image: nginx:latest deployment.extensions/nginx-deployment edited $ kubectl describe deploy nginx-deployment | grep -i 'image' Image: nginx:latest $ kubectl get rs NAME DESIRED CURRENT READY AGE nginx-deployment-5458686b99 2 2 2 23s nginx-deployment-687dc75df7 0 0 0 18m nginx-deployment-6df444bb79 0 0 0 5m55s
Rollover(多个rollout并行)
假如您创建了一个有5个niginx:1.7.9 replica的 Deployment,但是当还只有3个nginx:1.7.9的 replica 创建出来的时候您就开始更新含有5个nginx:1.9.1 replica 的 Deployment。在这种情况下,Deployment 会立即杀掉已创建的3个nginx:1.7.9的 Pod,并开始创建nginx:1.9.1的 Pod。它不会等到所有的5个nginx:1.7.9的Pod 都创建完成后才开始改变航道。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 $ kubectl rollout history deployment nginx-deployment deployment.extensions/nginx-deployment REVISION CHANGE-CAUSE 2 <none> 3 <none> 4 <none> $ kubectl rollout undo deployment nginx-deployment --to-revision=2 deployment.extensions/nginx-deployment rolled back $ kubectl describe deploy nginx-deployment | grep -i 'image' Image: nginx:1.9.1 $ ps: kubectl rollout pause deployment nginx-deployment $ kubectl delete deployment nginx-deployment -n default
清理 Policy
可以通过设置.spec.revisonHistoryLimit项来指定 deployment 最多保留多少 revision 历史记录。默认的会保留所有的 revision;如果将该项设置为0,Deployment 就不允许回退了
4. DaemonSet DaemonSet 确保全部(或者一些)Node 上运行一个 Pod 的副本。当有 Node 加入集群时,也会为他们新增一个Pod 。当有 Node 从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod
使用 DaemonSet 的一些典型用法:
运行集群存储 daemon,例如在每个 Node 上运行glusterd、ceph
在每个 Node 上运行日志收集 daemon,例如fluentd、logstash
在每个 Node 上运行监控 daemon,例如Prometheus Node Exporter、collectd、Datadog 代理、New Relic 代理,或 Ganglia gmond
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 apiVersion: apps/v1 kind: DaemonSet metadata: name: deamonset-example labels: app: daemonset spec: selector: matchLabels: name: deamonset-example template: metadata: labels: name: deamonset-example spec: containers: - name: daemonset-example image: nginx:latest
1 2 3 4 5 6 7 $ kubectl get pod NAME READY STATUS RESTARTS AGE deamonset-example-tqmfx 1/1 Running 0 94s deamonset-example-wqvsm 1/1 Running 0 94s $ kubectl delete daemonset deamonset-example -n default
5. Jobs :一次性任务 一次性任务,运行完成后pod销毁,不再重新启动新容器。还可以任务定时运行。
Job 负责批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个 Pod 成功结束
特殊说明:
spec.template 格式同 Pod
RestartPolicy 仅支持 Never 或 OnFailure
单个Pod时,默认 Pod 成功运行后 Job 即结束
spec.completions 标志 Job 结束需要成功运行的 Pod 个数,默认为1
spec.parallelism 标志并行运行的 Pod 的个数,默认为1
spec.activeDeadlineSeconds 标志失败Pod的重试最大时间,超过这个时间不会继续重试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 apiVersion: batch/v1 kind: Job metadata: name: pi spec: template: metadata: name: pi spec: containers: - name: pi image: perl command: ["perl" ,"-Mbignum=bpi" ,"-wle" ,"print bpi(2000)" ] restartPolicy: Never
1 2 3 4 5 6 7 8 9 10 11 12 13 14 $ kubectl get job NAME COMPLETIONS DURATION AGE pi 1/1 3m26s 8m54s $ kubectl get pod NAME READY STATUS RESTARTS AGE pi-bfmq8 0/1 Completed 0 9m1s $ kubectl log -f pi-bfmq8 log is DEPRECATED and will be removed in a future version. Use logs instead.3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275905 $ kubectl delete job pi -n default
6. CronJob :定时任务 cronjob 其实就是在job的基础上加上了时间调度,可以:在给定的时间点运行一个任务,也可以周期性的在给定时间点运行。(与linux中的crontab类似)
Cron Job管理基于时间的 Job,即:
使用前提条件:当前使用的Kubernetes集群,版本 >= 1.8(对 CronJob)。对于先前版本的集群,版本 <1.8,启动 API Server时,通过传递选项–runtime-config=batch/v2alpha1=true可以开启 batch/v2alpha1API。
典型的用法如下所示:
在给定的时间点调度 Job 运行
创建周期性运行的 Job,例如:数据库备份、发送邮件
特殊说明:
spec.template 格式同 Pod
RestartPolicy 仅支持 Never 或 OnFailure
单个 Pod 时,默认 Pod 成功运行后 Job 即结束
spec.completions 标志 Job 结束需要成功运行的 Pod 个数,默认为1
spec.parallelism 标志并行运行的 Pod 的个数,默认为1
spec.activeDeadlineSeconds 标志失败 Pod 的重试最大时间,超过这个时间不会继续重试
spec.schedule:调度,必需字段,指定任务运行周期,格式同 Cron
spec.jobTemplate:Job 模板,必需字段,指定需要运行的任务,格式同 Job
spec.startingDeadlineSeconds:启动 Job 的期限(秒级别),该字段是可选的。如果因为任何原因而错过了被调度的时间,那么错过执行时间的 Job 将被认为是失败的。如果没有指定,则没有期限
spec.concurrencyPolicy:并发策略,该字段也是可选的。它指定了如何处理被 Cron Job 创建的 Job 的并发执行。只允许指定下面策略中的一种:
Allow(默认):允许并发运行 Job
Forbid:禁止并发运行,如果前一个还没有完成,则直接跳过下一个
Replace:取消当前正在运行的 Job,用一个新的来替换
注意, 当前策略只能应用于同一个Cron Job创建的Job。如果存在多个Cron Job, 它们创建的 Job 之间总是允许并发运行。
spec.suspend:挂起,该字段也是可选的。如果设置为true,后续所有执行都会被挂起。它对已经开始执行的 Job 不起作用。默认值为false。
spec.successfulJobsHistoryLimit和.spec.failedJobsHistoryLimit:历史限制,是可选的字段。它们指定了可以保留多少完成和失败的 Job。默认情况下,它们分别设置为3和1。设置限制的值为0,相关类型的 Job 完成后将不会被保留。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 apiVersion: batch/v1beta1 kind: CronJob metadata: name: hello spec: schedule: "*/1 * * * *" jobTemplate: spec: template: spec: containers: - name: hello image: busybox args: - /bin/sh - -c - date; echo Hello from the Kubernetes cluster restartPolicy: OnFailure
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $ kubectl get cronjob NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE hello */1 * * * * False 1 10s 22s $ kubectl get job NAME COMPLETIONS DURATION AGE hello-1614322500 1/1 3s 62s hello-1614322560 0/1 2s 2s $ kubectl get pod NAME READY STATUS RESTARTS AGE hello-1614322500-p7wmh 0/1 Completed 0 6s hello-1614322560-vsz58 0/1 Completed 0 27s $ kubectl logs -f hello-1614322500-p7wmh Sat Mar 20 13:03:17 UTC 2021 Hello from the Kubernetes cluster $ kubectl delete cronjob hello -n default
7. StateFulSet StatefulSets(管理有状态应用) 作为 Controller 为 Pod 提供唯一的标识。它可以保证部署和 scale 的顺序StatefulSet是为了解决有状态服务的问题(对应Deployments和ReplicaSets是为无状态服务而设计),其应用场景包括:
稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现。
稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现。
有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现。
有序收缩,有序删除(即从N-1到0)。
8. Horizontal Pod Autoscaler Horizontal Pod Autoscaler 应用的资源使用率通常都有高峰和低谷的时候,如何削峰填谷,提高集群的整体资源利用率,让service中的Pod个数自动调整呢?这就有赖于Horizontal Pod Autoscaler了,顾名思义,使Pod水平自动缩放。
5、Service
Kubernetes Service定义了这样一种抽象:一个Pod的逻辑分组,一种可以访问它们的策略 —— 通常称为微服务。这一组Pod能够被Service访问到,通常是通过Label Selector 。
Service能够提供负载均衡的能力,但是在使用上有个限制:Service 只提供 4 层负载均衡能力,而没有 7 层功能, 但有时可能需要更多的匹配规则来转发请求,这点上 4 层负载均衡是不支持的。
5.1 Service 的类型 Service 在 K8s 中有以下四种类型:
ClusterIp:默认类型,自动分配一个仅 Cluster 内部可以访问的虚拟 IP
NodePort:在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过: NodePort 来访问该服务
LoadBalancer:在 NodePort 的基础上,借助 cloud provider 创建一个外部负载均衡器,并将请求转发到: NodePort
ExternalName:把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建,这只有 kubernetes 1.7 或更高版本的 kube-dns 才支持
5.2 VIP 和 Service 代理 在 Kubernetes 集群中,每个 Node 运行一个kube-proxy进程。kube-proxy负责为Service实现了一种VIP(虚拟 IP)的形式,而不是ExternalName的形式。在 Kubernetes v1.0 版本,代理完全在 userspace。在Kubernetes v1.1 版本,新增了 iptables 代理,但并不是默认的运行模式。从 Kubernetes v1.2 起,默认就是iptables 代理。在 Kubernetes v1.8.0-beta.0 中,添加了 ipvs 代理。
在 Kubernetes 1.14 版本开始默认使用ipvs 代理。
在 Kubernetes v1.0 版本,Service是 “4层”(TCP/UDP over IP)概念。在 Kubernetes v1.1 版本,新增了Ingress API(beta 版),用来表示 “7层”(HTTP)服务。
代理模式的分类如下:
1、userspace 代理模式
2、iptables 代理模式
3、ipvs 代理模式
这种模式,kube-proxy 会监视 Kubernetes Service对象和Endpoints,调用netlink接口以相应地创建ipvs 规则并定期与 Kubernetes Service对象和Endpoints对象同步 ipvs 规则,以确保 ipvs 状态与期望一致。访问服务时,流量将被重定向到其中一个后端 Pod。
与 iptables 类似,ipvs 于 netfilter 的 hook 功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着 ipvs 可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外,ipvs 为负载均衡算法提供了更多选项,例如:
rr:轮询调度
lc:最小连接数
dh:目标哈希
sh:源哈希
sed:最短期望延迟
nq:不排队调度
5.3 ClusterIP clusterIP 主要在每个 node 节点使用 IPVS,将发向 clusterIP 对应端口的数据,转发到 kube-proxy 中。然后 kube-proxy 自己内部实现有负载均衡的方法,并可以查询到这个 service 下对应 pod 的地址和端口,进而把数据转发给对应的 pod 的地址和端口。
为了实现图上的功能,主要需要以下几个组件的协同工作:
apiserver 用户通过kubectl命令向apiserver发送创建service的命令,apiserver接收到请求后将数据存储到etcd中
kube-proxy kubernetes的每个节点中都有一个叫做kube-porxy的进程,这个进程负责感知service,pod的变化,并将变化的信息写入本地的 IPVS 规则中
IPVS 使用NAT等技术将virtualIP的流量转至endpoint中
创建 myapp-deploy-svc.yaml 文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 apiVersion: apps/v1 kind: Deployment metadata: name: myapp-deploy namespace: default spec: replicas: 3 selector: matchLabels: app: myapp release: stabel template: metadata: labels: app: myapp release: stabel env: test spec: containers: - name: myapp image: nginx:latest imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: myapp namespace: default spec: type: ClusterIP selector: app: myapp release: stabel ports: - name: http port: 80 targetPort: 80
查看 IPVSADM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 $ kubectl get svc -owide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 172d <none> myapp ClusterIP 10.104.132.57 <none> 80/TCP 3m10s app=myapp,release=stabel $ ipvsadm -Ln IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 10.96.0.1:443 rr -> 192.168.2.1:6443 Masq 1 3 0 TCP 10.96.0.10:53 rr -> 10.244.0.14:53 Masq 1 0 0 -> 10.244.0.15:53 Masq 1 0 0 TCP 10.96.0.10:9153 rr -> 10.244.0.14:9153 Masq 1 0 0 -> 10.244.0.15:9153 Masq 1 0 0 TCP 10.104.132.57:80 rr -> 10.244.1.40:80 Masq 1 0 3 -> 10.244.1.41:80 Masq 1 0 3 -> 10.244.2.40:80 Masq 1 0 3 UDP 10.96.0.10:53 rr -> 10.244.0.14:53 Masq 1 0 0 -> 10.244.0.15:53 Masq 1 0 0
5.4 Headless Service 有时不需要或不想要负载均衡,以及单独的 Service IP 。遇到这种情况,可以通过指定 ClusterIP(spec.clusterIP) 的值为 “None” 来创建 Headless Service 。这类 Service 并不会分配 Cluster IP, kube-proxy 不会处理它们,而且平台也不会为它们进行负载均衡和路由。
1 2 3 4 5 6 7 8 9 10 11 12 apiVersion: v1 kind: Service metadata: name: myapp-headless namespace: default spec: selector: app: myapp clusterIP: "None" ports: - port: 80 targetPort: 80
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 $ kubectl get svc -owide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 172d <none> myapp ClusterIP 10.104.132.57 <none> 80/TCP 5m30s app=myapp,release=stabel myapp-headless ClusterIP None <none> 80/TCP 19s app=myapp $ dig -t A myapp-headless.default.svc.cluster.local. @10.96.0.10 ; <<>> DiG 9.11.4-P2-RedHat-9.11.4-16.P2.el7_8.6 <<>> -t A myapp-headless.default.svc.cluster.local. @10.96.0.10 ;; global options: +cmd ;; Got answer: ;; WARNING: .local is reserved for Multicast DNS ;; You are currently testing what happens when an mDNS query is leaked to DNS ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5421 ;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;myapp-headless.default.svc.cluster.local. IN A ;; ANSWER SECTION: myapp-headless.default.svc.cluster.local. 30 IN A 10.244.1.41 myapp-headless.default.svc.cluster.local. 30 IN A 10.244.1.40 myapp-headless.default.svc.cluster.local. 30 IN A 10.244.2.40 ;; Query time: 0 msec ;; SERVER: 10.96.0.10#53(10.96.0.10) ;; WHEN: 四 3月 04 10:36:53 CST 2021 ;; MSG SIZE rcvd: 237
5.5 NodePort nodePort 的原理在于在 node 上开了一个端口,将向该端口的流量导入到 kube-proxy,然后由 kube-proxy 进一步到给对应的 pod
1 2 3 4 5 6 7 8 9 10 11 12 13 14 apiVersion: v1 kind: Service metadata: name: myapps namespace: default spec: type: NodePort selector: app: web release: stabel ports: - name: http port: 80 targetPort: 80
1 2 3 4 5 6 7 8 9 $ kubectl get svc -owide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 172d <none> myapp ClusterIP 10.104.132.57 <none> 80/TCP 11m app=myapp,release=stabel myapp-headless ClusterIP None <none> 80/TCP 6m42s app=myapp myapps NodePort 10.100.41.94 <none> 80:32195/TCP 42s app=web,release=stabel ipvsadm -Ln
5.6 LoadBalancer loadBalancer 和 nodePort 其实是同一种方式。区别在于 loadBalancer 比 nodePort 多了一步,就是可以调用cloud provider 去创建 LB 来向节点导流。
5.7 ExternalName 这种类型的 Service 通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容( 例如:hub.atguigu.com )。
ExternalName Service 是 Service 的特例,它没有 selector,也没有定义任何的端口和Endpoint。相反的,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务。
1 2 3 4 5 6 7 8 apiVersion: v1 kind: Service metadata: name: my-service-1 namespace: default spec: type: ExternalName externalName: hub.armin.com
1 2 3 4 5 6 7 8 9 $ kubectl get svc -owide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 172d <none> my-service-1 ExternalName <none> hub.armin.com <none> 29s <none> myapp ClusterIP 10.104.132.57 <none> 80/TCP 21m app=myapp,release=stabel myapp-headless ClusterIP None <none> 80/TCP 15m app=myapp myapps NodePort 10.100.41.94 <none> 80:32195/TCP 9m51s app=web,release=stabel $ dig -t A hub.armin.com.svc.cluster.local. @10.96.0.10
当查询主机 my-service.defalut.svc.cluster.local ( SVC_NAME.NAMESPACE.svc.cluster.local )时,集群的DNS 服务将返回一个值 my.database.example.com 的 CNAME 记录。访问这个服务的工作方式和其他的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发。
6、Ingress 必须具有 Ingress 控制器 才能满足 Ingress 的要求。 仅创建 Ingress 资源本身没有任何效果。
需要部署 Ingress 控制器,例如 ingress-nginx 。 你可以从许多 Ingress 控制器 中进行选择。
理想情况下,所有 Ingress 控制器都应符合参考规范。但实际上,不同的 Ingress 控制器操作略有不同。
mandatory.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 apiVersion: v1 kind: Namespace metadata: name: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx --- kind: ConfigMap apiVersion: v1 metadata: name: nginx-configuration namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx --- kind: ConfigMap apiVersion: v1 metadata: name: tcp-services namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx --- kind: ConfigMap apiVersion: v1 metadata: name: udp-services namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx --- apiVersion: v1 kind: ServiceAccount metadata: name: nginx-ingress-serviceaccount namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRole metadata: name: nginx-ingress-clusterrole labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx rules: - apiGroups: - "" resources: - configmaps - endpoints - nodes - pods - secrets verbs: - list - watch - apiGroups: - "" resources: - nodes verbs: - get - apiGroups: - "" resources: - services verbs: - get - list - watch - apiGroups: - "" resources: - events verbs: - create - patch - apiGroups: - "extensions" - "networking.k8s.io" resources: - ingresses verbs: - get - list - watch - apiGroups: - "extensions" - "networking.k8s.io" resources: - ingresses/status verbs: - update --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: Role metadata: name: nginx-ingress-role namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx rules: - apiGroups: - "" resources: - configmaps - pods - secrets - namespaces verbs: - get - apiGroups: - "" resources: - configmaps resourceNames: - "ingress-controller-leader-nginx" verbs: - get - update - apiGroups: - "" resources: - configmaps verbs: - create - apiGroups: - "" resources: - endpoints verbs: - get --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: RoleBinding metadata: name: nginx-ingress-role-nisa-binding namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: nginx-ingress-role subjects: - kind: ServiceAccount name: nginx-ingress-serviceaccount namespace: ingress-nginx --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: nginx-ingress-clusterrole-nisa-binding labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: nginx-ingress-clusterrole subjects: - kind: ServiceAccount name: nginx-ingress-serviceaccount namespace: ingress-nginx --- apiVersion: apps/v1 kind: Deployment metadata: name: nginx-ingress-controller namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx spec: replicas: 2 selector: matchLabels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx template: metadata: labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx annotations: prometheus.io/port: "10254" prometheus.io/scrape: "true" spec: terminationGracePeriodSeconds: 300 serviceAccountName: nginx-ingress-serviceaccount nodeSelector: kubernetes.io/os: linux containers: - name: nginx-ingress-controller image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0 args: - /nginx-ingress-controller - --configmap=$(POD_NAMESPACE)/nginx-configuration - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services - --udp-services-configmap=$(POD_NAMESPACE)/udp-services - --publish-service=$(POD_NAMESPACE)/ingress-nginx - --annotations-prefix=nginx.ingress.kubernetes.io securityContext: allowPrivilegeEscalation: true capabilities: drop: - ALL add: - NET_BIND_SERVICE runAsUser: 101 env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace ports: - name: http containerPort: 80 protocol: TCP - name: https containerPort: 443 protocol: TCP livenessProbe: failureThreshold: 3 httpGet: path: /healthz port: 10254 scheme: HTTP initialDelaySeconds: 10 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 10 readinessProbe: failureThreshold: 3 httpGet: path: /healthz port: 10254 scheme: HTTP periodSeconds: 10 successThreshold: 1 timeoutSeconds: 10 lifecycle: preStop: exec: command: - /wait-shutdown --- apiVersion: v1 kind: LimitRange metadata: name: ingress-nginx namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx spec: limits: - min: memory: 90Mi cpu: 100m type: Container
service-nodeport.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 apiVersion: v1 kind: Service metadata: name: ingress-nginx namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx spec: type: NodePort ports: - name: http port: 80 targetPort: 80 protocol: TCP - name: https port: 443 targetPort: 443 protocol: TCP selector: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx ---
创建 ingress-nginx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 [root@k8s-master-192 ingress-nginx] namespace/ingress-nginx created configmap/nginx-configuration created configmap/tcp-services created configmap/udp-services created serviceaccount/nginx-ingress-serviceaccount created clusterrole.rbac.authorization.k8s.io/nginx-ingress-clusterrole configured role.rbac.authorization.k8s.io/nginx-ingress-role created rolebinding.rbac.authorization.k8s.io/nginx-ingress-role-nisa-binding created clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress-clusterrole-nisa-binding configured deployment.apps/nginx-ingress-controller created limitrange/ingress-nginx created service/ingress-nginx created [root@k8s-master-192 ingress-nginx] NAME READY STATUS RESTARTS AGE nginx-ingress-controller-7fcf8df75d-7jj4z 1/1 Running 0 85s nginx-ingress-controller-7fcf8df75d-l9bwc 1/1 Running 0 85s [root@k8s-master-192 ingress-nginx] NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx NodePort 10.100.56.91 <none> 80:31352/TCP,443:30499/TCP 80s
6.2 准备service 和 pod
为了方便后续的实验,创建下图所示的模型
创建 tomcat-nginx.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment namespace: ingress-nginx spec: replicas: 3 selector: matchLabels: app: nginx-pod template: metadata: labels: app: nginx-pod spec: containers: - name: nginx image: nginx:1.17.1 ports: - containerPort: 80 --- apiVersion: apps/v1 kind: Deployment metadata: name: tomcat-deployment namespace: ingress-nginx spec: replicas: 3 selector: matchLabels: app: tomcat-pod template: metadata: labels: app: tomcat-pod spec: containers: - name: tomcat image: tomcat:8.5-jre10-slim ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: nginx-service namespace: ingress-nginx spec: selector: app: nginx-pod clusterIP: None type: ClusterIP ports: - port: 80 targetPort: 80 --- apiVersion: v1 kind: Service metadata: name: tomcat-service namespace: ingress-nginx spec: selector: app: tomcat-pod clusterIP: None type: ClusterIP ports: - port: 8080 targetPort: 8080
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@k8s-master-192 ingress-nginx] deployment.apps/nginx-deployment created deployment.apps/tomcat-deployment created service/nginx-service created service/tomcat-service created [root@k8s-master-192 ingress-nginx] NAME READY UP-TO-DATE AVAILABLE AGE nginx-deployment 3/3 3 3 83s nginx-ingress-controller 2/2 2 2 7m16s tomcat-deployment 3/3 3 3 83s [root@k8s-master-192 ingress-nginx] NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx NodePort 10.100.56.91 <none> 80:31352/TCP,443:30499/TCP 6m42s nginx-service ClusterIP None <none> 80/TCP 48s tomcat-service ClusterIP None <none> 8080/TCP 48s
6.3 Http代理 创建ingress-http.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 apiVersion: extensions/v1beta1 kind: Ingress metadata: name: ingress-http namespace: dev spec: rules: - host: nginx.test.com http: paths: - path: / backend: serviceName: nginx-service servicePort: 80 - host: tomcat.test.com http: paths: - path: / backend: serviceName: tomcat-service servicePort: 8080
使用配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 [root@k8s-master-192 ingress-nginx] ingress.extensions/ingress-http created [root@k8s-master-192 ingress-nginx] NAME HOSTS ADDRESS PORTS AGE ingress-http nginx.test.com,tomcat.test.com 10.100.56.91 80 45s [root@k8s-master-192 ingress-nginx] Name: ingress-http Namespace: ingress-nginx Address: 10.100.56.91 Default backend: default-http-backend:80 (<none>) Rules: Host Path Backends ---- ---- -------- nginx.test.com / nginx-service:80 (10.244.3.72:80,10.244.3.74:80,10.244.4.63:80) tomcat.test.com / tomcat-service:8080 (10.244.3.73:8080,10.244.4.64:8080,10.244.4.65:8080) Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion" :"extensions/v1beta1" ,"kind" :"Ingress" ,"metadata" :{"annotations" :{},"name" :"ingress-http" ,"namespace" :"ingress-nginx" },"spec" :{"rules" :[{"host" :"nginx.test.com" ,"http" :{"paths" :[{"backend" :{"serviceName" :"nginx-service" ,"servicePort" :80},"path" :"/" }]}},{"host" :"tomcat.test.com" ,"http" :{"paths" :[{"backend" :{"serviceName" :"tomcat-service" ,"servicePort" :8080},"path" :"/" }]}}]}} Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal CREATE 109s nginx-ingress-controller Ingress ingress-nginx/ingress-http Normal CREATE 109s nginx-ingress-controller Ingress ingress-nginx/ingress-http Normal UPDATE 99s nginx-ingress-controller Ingress ingress-nginx/ingress-http Normal UPDATE 99s nginx-ingress-controller Ingress ingress-nginx/ingress-http
修改本机的hosts文件,添加如下内容
1 2 master虚拟机的IP地址 nginx.test.com masete虚拟机的IP地址 tomcat.test.com
查看ingress为service提供的端口号
1 2 [root@k8s-master-192 ingress-nginx] ingress-nginx NodePort 10.100.56.91 <none> 80:31352/TCP,443:30499/TCP 16m
在浏览器中测试访问, 是能够访问通
6.4 Https代理 创建证书
1 2 3 4 5 6 [root@k8s-master-192 ingress-nginx] Generating a 2048 bit RSA private key ...........................................................................+++ ....................................+++ writing new private key to 'tls.key' -----
创建密钥
1 2 [root@k8s-master-192 ingress-nginx] secret/tls-secret created
创建 ingress-https.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 apiVersion: extensions/v1beta1 kind: Ingress metadata: name: ingress-https namespace: ingress-nginx spec: tls: - hosts: - nginx.test.com - tomcat.test.com secretName: tls-secret rules: - host: nginx.test.com http: paths: - path: / backend: serviceName: nginx-service servicePort: 80 - host: tomcat.test.com http: paths: - path: / backend: serviceName: tomcat-service servicePort: 8080
使用配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 [root@k8s-master-192 ingress-nginx] ingress.extensions/ingress-https created [root@k8s-master-192 ingress-nginx] Name: ingress-https Namespace: ingress-nginx Address: 10.100.56.91 Default backend: default-http-backend:80 (<none>) TLS: tls-secret terminates nginx.test.com,tomcat.test.com Rules: Host Path Backends ---- ---- -------- nginx.test.com / nginx-service:80 (10.244.3.72:80,10.244.3.74:80,10.244.4.63:80) tomcat.test.com / tomcat-service:8080 (10.244.3.73:8080,10.244.4.64:8080,10.244.4.65:8080) Annotations: Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal CREATE 40s nginx-ingress-controller Ingress ingress-nginx/ingress-https Normal CREATE 40s nginx-ingress-controller Ingress ingress-nginx/ingress-https Normal UPDATE 34s nginx-ingress-controller Ingress ingress-nginx/ingress-https Normal UPDATE 34s nginx-ingress-controller Ingress ingress-nginx/ingress-https
获取端口,左边的是http使用的端口,右边是https使用的端口,因此要使用的端口是30499
1 2 [root@k8s-master-192 ingress-nginx] ingress-nginx NodePort 10.100.56.91 <none> 80:31352/TCP,443:30499/TCP 32m
使用浏览器访问
7、K8S - 存储
ConfigMap
Secret
volume
Persistent Volume(PV)
7.1 ConfigMap
ConfigMap 功能在 Kubernetes1.2 版本中引入,许多应用程序会从配置文件、命令行参数或环境变量中读取配置信息。ConfigMap API 给我们提供了向容器中注入配置信息的机制,ConfigMap 可以被用来保存单个属性,也可以用来保存整个配置文件或者 JSON 二进制大对象。
7.1.1 ConfigMap 的创建
使用目录创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 $ ll /etc/kubernetes/configmap-dir/ game.properties ui.properties $ cat /etc/kubernetes/configmap-dir/game.properties enemies=aliens lives=3 enemies.cheat=true enemies.cheat.level=noGoodRotten secret.code.passphrase=UUDDLRLRBABAS secret.code.allowed=true secret.code.lives=30 $ cat /etc/kubernetes/configmap-dir/ui.properties color.good=purple color.bad=yellow allow.textmode=true how.nice.to.look=fairlyNice $ kubectl create configmap game-config-1 --from-file=/etc/kubernetes/configmap-dir $ kubectl get configmap -n default -owide NAME DATA AGE game-config-1 2 <invalid> $ kubectl get configmap -n default -o yaml apiVersion: v1 items: - apiVersion: v1 data: game.properties: | enemies=aliens lives=3 enemies.cheat=true enemies.cheat.level=noGoodRotten secret.code.passphrase=UUDDLRLRBABAS secret.code.allowed=true secret.code.lives=30 ui.properties: | color.good=purple color.bad=yellow allow.textmode=true how.nice.to.look=fairlyNice kind: ConfigMap metadata: creationTimestamp: "2021-03-08T11:54:57Z" name: game-config-1 namespace: default resourceVersion: "15839" selfLink: /api/v1/namespaces/default/configmaps/game-config-1 uid: cb84c4f5-e509-4ecb-8513-68184b8b57af kind: List metadata: resourceVersion: "" selfLink: "" $ kubectl describe configmap game-config-1 -n default Name: game-config-1 Namespace: default Labels: <none> Annotations: <none> Data ==== ui.properties: ---- color.good=purple color.bad=yellow allow.textmode=true how.nice.to.look=fairlyNice game.properties: ---- enemies=aliens lives=3 enemies.cheat=true enemies.cheat.level=noGoodRotten secret.code.passphrase=UUDDLRLRBABAS secret.code.allowed=true secret.code.lives=30 Events: <none>
使用文件创建
只要指定为一个文件就可以从单个文件中创建 ConfigMap
1 2 3 $ kubectl create configmap game-config-2 --from-file=/etc/kubernetes/configmap-dir/game.properties $ kubectl get configmaps game-config-2 -o yaml
使用字面值创建
使用文字值创建,利用–from-literal参数传递配置信息,该参数可以使用多次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 $ kubectl create configmap game-config-3 --from-literal=k1.how=v1 --from-literal=k2.how=v2 $ kubectl get configmaps game-config-3 -o yaml apiVersion: v1 data: k1.how: v1 k2.how: v2 kind: ConfigMap metadata: creationTimestamp: "2021-03-08T12:01:19Z" name: game-config-3 namespace: default resourceVersion: "16388" selfLink: /api/v1/namespaces/default/configmaps/game-config-3 uid: b9e048f3-a17d-4517-bfe5-311c51377aaa
7.1.2 Pod 中使用 ConfigMap 使用 ConfigMap 来替代环境变量
env: 指定导入k/v
envFrom: 全部导入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 apiVersion: v1 kind: ConfigMap metadata: name: special-config namespace: default data: special.how: very special.sam: charm special.lep: value --- apiVersion: v1 kind: ConfigMap metadata: name: env-config namespace: default data: log_level: INFO log_level: DEBUG --- apiVersion: v1 kind: Pod metadata: name: cm-test-pod-01 spec: containers: - name: test-container image: arminto/my_nginx:v1 command: [ "/bin/sh" , "-c" , "env" ] env: - name: SPECIAL_HOW_KEY valueFrom: configMapKeyRef: name: special-config key: special.how - name: SPECIAL_SAM_KEY valueFrom: configMapKeyRef: name: special-config key: special.sam envFrom: - configMapRef: name: env-config restartPolicy: Never
1 2 3 4 $ kubectl apply -f cm-test-01.yaml $ kubectl logs cm-test-pod-01 | grep SPECIAL SPECIAL_HOW_KEY=very SPECIAL_SAM_KEY=charm
用 ConfigMap 设置命令行参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 apiVersion: v1 kind: ConfigMap metadata: name: config-01 namespace: default data: a: b c: d --- apiVersion: v1 kind: Pod metadata: name: cm-test-pod-02 spec: containers: - name: test-container image: arminto/my_nginx:v1 command: ["/bin/sh" ,"-c" ,"echo $(KEY-01) $(KEY-02)" ] env: - name: KEY-01 valueFrom: configMapKeyRef: name: config-01 key: a - name: KEY-02 valueFrom: configMapKeyRef: name: config-01 key: c restartPolicy: Never
1 2 $ kubectl logs cm-test-pod-02 b d
通过数据卷插件使用ConfigMap
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 apiVersion: v1 kind: ConfigMap metadata: name: config-02 namespace: default data: how: HOW sam: SAM --- apiVersion: v1 kind: Pod metadata: name: test-pod-02 spec: containers: - name: test-container image: arminto/my_nginx:v1 command: ["/bin/sh" ,"-c" ,"sleep 36000" ] volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: config-02 restartPolicy: Never
1 2 3 4 5 6 7 8 9 10 $ kubectl exec -it test-pod-02 -- ls -lh /etc/config total 0 lrwxrwxrwx 1 root root 10 Mar 9 08:44 how -> ..data/how lrwxrwxrwx 1 root root 10 Mar 9 08:44 sam -> ..data/sam $ kubectl exec -it test-pod-02 -- cat /etc/config/how HOW $ kubectl exec -it test-pod-02 -- cat /etc/config/sam SAM
7.1.3 ConfigMap 的热更新 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 apiVersion: v1 kind: ConfigMap metadata: name: log-config namespace: default data: log_level: INFO --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: my-nginx spec: replicas: 1 template: metadata: labels: run: my-nginx spec: containers: - name: my-nginx image: arminto/my_nginx:v1 ports: - containerPort: 80 volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: log-config
1 2 $ kubectl exec -it my-nginx-7b6584d96d-hpbml -- cat /etc/config/log_level INFO
修改 ConfigMap,将其INFO改为DEBUG
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $ kubectl edit configmap log-config apiVersion: v1 data: log_level: DEBUG kind: ConfigMap metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion" :"v1" ,"data" :{"log_level" :"DEBUG" },"kind" :"ConfigMap" ,"metadata" :{"annotations" :{},"name" :"log-config" ,"namespace" :"default" }} creationTimestamp: "2021-03-09T09:04:08Z" name: log-config namespace: default resourceVersion: "36210" selfLink: /api/v1/namespaces/default/configmaps/log-config uid: 8e2f15fc-cc05-414b-ad2f-9597cacf7c9a
修改log_level的值为DEBUG等待大概 10 秒钟时间,再次查看环境变量的值
1 2 $ kubectl exec -it my-nginx-7b6584d96d-hpbml -- cat /etc/config/log_level DEBUG
7.2 Secret
Secret 解决了密码、token、密钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者 Pod Spec中。
并且,Secret 可以以 Volume 或者 环境变量的方式使用。
Secret 有三种类型
Service Account
Opaque
kubernetes.io/dockerconfigjson
7.2.1 Service Account
大致了解一下即可,Service Account 用来访问 Kubernetes API,由 Kubernetes 自动创建,并且会自动挂载到 Pod的/run/secrets/kubernetes.io/serviceaccount目录中。
PS : 只有与 apiserver 组件进行交互的 pod 才会有如下的 证书、命名空间、tekon密钥。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 $ kubectl exec -it kube-proxy-2x7r8 -n kube-system -- ls -lh /run/secrets/kubernetes.io/serviceaccount total 0 lrwxrwxrwx 1 root root 13 Mar 10 02:50 ca.crt -> ..data/ca.crt lrwxrwxrwx 1 root root 16 Mar 10 02:50 namespace -> ..data/namespace lrwxrwxrwx 1 root root 12 Mar 10 02:50 token -> ..data/token $ kubectl get secret NAME TYPE DATA AGE default-token-c6vqw kubernetes.io/service-account-token 3 46d $ kubectl describe secret default-token-c6vqw ………………………………………………………………………… Data ==== ca.crt: 1025 bytes namespace: 7 bytes token: eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tYzZ2cXciLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjJjNjlmMjkzLTcwNTQtNDY4My05ODNmLTljYmQyNTBlZTQ1ZiIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.WdQ32P2ndcKUNbyrsKouTgBEVQf8Smq1mSd9zRpBou1KN2DUJ6UPWHvMdDlqwvN2CACOWZcri0eN8NABvNEW6cQMv7O7-GoVsuv5HLDxmO1IC1tTVUr8d-uBxULiSiQZ_Mj2LK1QKYNAHzVYRlEozzI3wZAOWBDqA7ZFVpXRQMES0lzlRc9ETZqQmxIHgF_hyTzpsRaLkR480vppOTORejZGrm_wiSvYVKKvNe3H35TRtHARCZDlGv7DAickld2rnYuKSyLdZz29CmDiE6L0ZyzTCMgPalNkmjcuij28XG19I4GLio7IiS5ZMDrQsmr_4t3G449PPSiH_0C78LICuA
7.2.2 Opaque Secret
base64编码格式的Secret,用来存储密码、密钥等。
Opaque 类型的数据是一个 map 类型,要求 value 是 base64 编码格式。
1、创建 Opaque-Secret
1 2 3 4 5 6 $ echo -n "admin" | base64 YWRtaW4= $ echo -n "1f2d1e2e67df" | base64 MWYyZDFlMmU2N2Rm
1 2 3 4 5 6 7 8 9 apiVersion: v1 kind: Secret metadata: name: op-secret type: Opaque data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm
1 2 3 4 5 6 7 8 9 10 11 $ kubectl get secret NAME TYPE DATA AGE default-token-c6vqw kubernetes.io/service-account-token 3 46d op-secret Opaque 2 11s $ kubectl describe secret op-secret ………………………………………………………………………… Data ==== password: 12 bytes username: 5 bytes
2、使用方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 apiVersion: v1 kind: Pod metadata: labels: name: seret-test name: seret-test spec: volumes: - name: secrets secret: secretName: op-secret containers: - image: arminto/my_nginx:v1 name: db volumeMounts: - name: secrets mountPath: '/etc/secrets' readOnly: true
1 2 3 4 5 6 7 8 9 10 11 $ kubectl exec -it seret-test -- ls -l /etc/secrets total 0 lrwxrwxrwx 1 root root 15 Mar 10 03:25 password -> ..data/password lrwxrwxrwx 1 root root 15 Mar 10 03:25 username -> ..data/username $ kubectl exec -it seret-test -- cat /etc/secrets/password 1f2d1e2e67df $ kubectl exec -it seret-test -- cat /etc/secrets/username admin
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 apiVersion: extensions/v1beta1 kind: Deployment metadata: name: pod-deployment spec: replicas: 2 template: metadata: labels: app: pod-deployment spec: containers: - name: pod-1 image: arminto/my_nginx:v1 ports: - containerPort: 80 env: - name: TEST_USER valueFrom: secretKeyRef: name: op-secret key: username - name: TEST_PASSWORD valueFrom: secretKeyRef: name: op-secret key: password
1 2 3 4 5 $ kubectl exec -it pod-deployment-7cfd69dcf7-222q2 -- /bin/sh / admin / 1f2d1e2e67df
7.2.3 dockerconfigjson
用来存储私有 docker registry 的认证信息。
1、使用 Kuberctl 创建 docker registry 认证的 secret(注:针对私有仓库)
1 2 3 4 5 $ kubectl create secret docker-registry myregistrykey --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD
2、在创建 Pod 的时候,通过 imagePullSecrets 来引用刚创建的 myregistrykey
1 2 3 4 5 6 7 8 9 10 11 apiVersion: v1 kind: Pod metadata: name: foo spec: containers: - name: foo image: hub.lemon.com/library/my_nginx:v1 imagePullSecrets: - name: myregistrykey
7.3 Volume
容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题。首先,当容器崩溃时,kubelet 会重启它,但是容器中的文件将丢失——容器以干净的状态(镜像最初的状态)重新启动。其次,在Pod中同时运行多个容器时,这些容器之间通常需要共享文件。Kubernetes 中的Volume抽象就很好的解决了这些问题。
Kubernetes 中的卷有明确的寿命 —— 与封装它的 Pod 相同。所f以,卷的生命比 Pod 中的所有容器都长,当这个容器重启时数据仍然得以保存。当然,当 Pod 不再存在时,卷也将不复存在。也许更重要的是,Kubernetes支持多种类型的卷,Pod 可以同时使用任意数量的卷。
Kubernetes 支持以下类型的卷:
awsElasticBlockStore、azureDisk、azureFile、cephfs、csi、downwardAPI、emptyDir
fc、flocker、gcePersistentDisk、gitRepo、glusterfs、hostPath、iscsi、local、nfs
persistentVolumeClaim、projected、portworxVolume、quobyte、rbd、scaleIO、secret
storageos、vsphereVolume
比较常用的两类 emptyDir、hostPath
7.3.1 emptyDir
当 Pod 被分配给节点时,首先创建emptyDir卷,并且只要该 Pod 在该节点上运行,该卷就会存在。正如卷的名字所述,它最初是空的。Pod 中的容器可以读取和写入emptyDir卷中的相同文件,尽管该卷可以挂载到每个容器中的相同或不同路径上。当出于任何原因从节点中删除 Pod 时,emptyDir中的数据将被永久删除。
emptyDir的用法有:
暂存空间,例如用于基于磁盘的合并排序
用作长时间计算崩溃恢复时的检查点
Web服务器容器提供数据时,保存内容管理器容器提取的文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 apiVersion: v1 kind: Pod metadata: name: test-pd spec: containers: - name: test-container01 image: arminto/my_nginx:v1 volumeMounts: - mountPath: /cache01 name: cache-volume - name: test-container02 image: tomcat:latest volumeMounts: - mountPath: /cache02 name: cache-volume volumes: - name: cache-volume emptyDir: {}
1 2 3 4 5 6 7 8 9 10 11 12 $ kubectl exec -it test-pd -c test-container01 -- /bin/sh / /cache01 /cache01 test-pd $ kubectl exec -it test-pd -c test-container02 -- /bin/sh test.log test-pd
7.3.2 hostPath
hostPath卷将主机节点的文件系统中的文件或目录挂载到集群中。
hostPath 的用途如下:
运行需要访问 Docker 内部的容器;使用 /var/lib/docker 的 hostPath
在容器中运行 cAdvisor;使用 /dev/cgroups 的 hostPath
允许 pod 指定给定的 hostPath 是否应该在 pod 运行之前存在,是否应该创建,以及它应该以什么形式存在
除了所需的path属性之外,用户还可以为hostPath卷指定type
值
行为
空字符串(默认)用于向后兼容,这意味着在挂载 hostPath 卷之前不会执行任何检查。
DirectoryOrCreate
如果在给定的路径上没有任何东西存在,那么将根据需要在那里创建一个空目录,权限设置为 0755,与 Kubelet 具有相同的组和所有权。
Directory
给定的路径下必须存在目录。
FileOrCreate
如果在给定的路径上没有任何东西存在,那么会根据需要创建一个空文件,权限设置为 0644,与 Kubelet 具有相同的组和所有权。
File
给定的路径下必须存在文件。
Socket
给定的路径下必须存在 UNIX 套接字。
CharDevice
给定的路径下必须存在字符设备。
BlockDevice
给定的路径下必须存在块设备。
使用这种卷类型是请注意,因为:
由于每个节点上的文件都不同,具有相同配置(例如从 podTemplate 创建的)的 pod 在不同节点上的行为可能会有所不同。
当 Kubernetes 按照计划添加资源感知调度时,将无法考虑hostPath使用的资源。
在底层主机上创建的文件或目录只能由 root 写入。您需要在特权容器中以 root 身份运行进程,或修改主机上的文件权限以便写入hostPath卷。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 apiVersion: v1 kind: Pod metadata: name: h-pod spec: containers: - image: arminto/my_nginx:v1 name: test-container volumeMounts: - mountPath: /hv name: test-volume volumes: - name: test-volume hostPath: path: /data type: Directory
1 2 3 4 5 6 7 8 9 10 $ kubectl get pod -owide NAME READY STATUS RESTARTS AGE IP NODE h-pod 0/1 ContainerCreating 0 7s <none> k8s-node-192.168.2.22 $ hostname k8s-node-192.168.2.22 $ echo 'hv test' > /data/h-test.log $ kubectl exec -it h-pod -- cat /hv/h-test.log hv test
7.4 Persistent Volume(PV)
PersistentVolume(简称PV) 是 Volume 之类的卷插件,也是集群中的资源,但独立于Pod的生命周期(即不会因Pod删除而被删除),不归属于某个Namespace。
PersistentVolumeClaim(简称PVC)是用户存储的请求,PVC消耗PV的资源,可以请求特定的大小和访问模式,需要指定归属于某个Namespace,在同一个Namespace 的 Pod才可以指定对应的PVC。
PV (持久化卷),是对底层的共享存储的一种抽象,PV 由管理员进行创建和配置, 它和具体的底层的共享存储技术的实现方式有关,比如Ceph、GlusterFS、NFS等,都是通过插件机制完成与共享存储的对接。
PVC (持久化卷声明),PVC 是用户存储的一种声明,PVC 和 Pod 比较类型,Pod 是消耗节点,PVC 消耗的是 PV 资源,Pod 可以请求 CPU 的内存,而 PVC 可以请求特定的存储空间和访问模式。对于真正存储的用户不需要关心底层的存储实现细节,只需要直接使用PVC即可。
但是通过PVC请求一定的存储空间也很有可能不足以满足对于存储设备的各种需求,而且不同的应用程序对于存储性能的要求也能也不尽相同,比如读写速度、并发性能等,为了解决这一问题,Kubernetes又为我们引入了一个新的资源对象: StorageClass,通过StorageClass的定义,管理员可以将存储资源定义为某种类型的资源,比如快速存储、慢速存储等,用户根据StorageClass的描述就可以非常直观的知道各种存储资源特性了,这样就可以根据应用的特性去申请合适的存储资源了。
7.4.1 PV 和 PVC的生命周期
PV 可以看作可用的存储资源,PVC则是对存储资源的需求,PV 和 PVC的互相关系遵循如下图
7.4.2 资源供应 (Provisioning)
Kubernetes支持两种资源的供应模式:静态模式(Staic)和动态模式(Dynamic)。资源供应的结果就是创建好的PV。
静态模式:集群管理员手工创建许多 PV,在定义 PV 时需要将后端存储的特性进行设置。
动态模式:集群管理员无须手工创建 PV,而是通过 StorageClass 的设置对后端存储进行描述,标记为某种 “类型(Class)”。此时要求 PVC 对存储的类型进行声明,系统将自动完成 PV 的创建及 PVC 的绑定。
PVC 可以声明 Class 为””,说明该 PVC 禁止使用动态模式。
1、静态资源下,通过PV和PVC完成绑定,并供Pod使用的存储管理机制
2.动态资源下,通过StorageClass和PVC完成资源动态绑定 (系统自动生成PV,并供Pod使用的存储管理机制)
7.4.3 资源绑定 (Binding) 在用户定义好PVC后,系统将根据PVC对存储资源的请求 (存储空间和访问模式)在已存在的PV中选择一个满足PVC要求的PV,一旦找到,就将该PV与用户定义的PVC进行绑定,然后用户的应用就可以使用这个PVC了。如果系统中没有满足PVC要求的PV,PVC则会无限期处于Pending状态,直到等到系统管理员创建了一个符合要求的PV。PV一旦绑定在某个PVC上,就被这个PVC独占,不能再与其他PVC进行绑定了。在这种情况下,当PVC申请的存储空间比PV的少时,整个PV的空间都能够为PVC所用,可能会造成资源的浪费。如果资源供应使用的是动态模式,则系统在PVC找到合适的StorageClass后,将会自动创建PV并完成PVC的绑定。
7.4.4 资源使用 (Using) Pod 使用volume的定义, 将 PVC 挂载到容器内的某个路径进行使用。volume 的类型为 persistentVoulumeClaim , 在容器应用挂载了一个 PVC 后, 就能被持续独占使用。
不过, 多个Pod可以挂载同一个PVC, 应用程序需要考虑多个实例共同访问一块存储空间的问题。
7.4.5 资源释放 (Releasing) 当用户对存储资源使用哪个完毕后,用户可以删除PVC,与该PVC绑定的PV将会被标记为已释放,但还不能立刻与其他PVC进行绑定。通过之前PVC写入的数据可能还留在存储设备上,只有在清除之后该PV才能继续使用。
7.4.6 资源回收 (Reclaiming) 对于PV,管理员可以设定回收策略(Reclaim Policy)用于设置与之绑定的PVC释放资源之后,对于遗留数据如何处理。只有PV的存储空间完成回收,才能供新的PVC绑定和使用。
7.4.7 持久化卷声明的保护 PVC 保护的目的是确保由 pod 正在使用的 PVC 不会从系统中移除,因为如果被移除的话可能会导致数据丢失,当启用 PVC 保护 alpha 功能时,如果用户删除了一个 pod 正在使用的 PVC,则该 PVC 不会被立即删除。PVC 的删除将被推迟,直到 PVC 不再被任何 pod 使用。
7.4.8 PV 的类型 及 访问模式
PersistentVolume 类型以插件形式实现。以下仅列部分常用类型:
GCEPersistentDisk
AWSElasticBlockStore
NFS
RBD (Ceph Block Device)
CephFS、Glusterfs
PV 的访问模式 有三种 ReadWriteOnce 、 ReadOnlyMany 、 ReadWriteMany
PersistentVolume 可以以资源提供者支持的任何方式挂载到主机上。如下表所示,供应商具有不同的功能,每个PV 的访问模式都将被设置为该卷支持的特定模式。例如,NFS 可以支持多个读/写客户端,但特定的 NFS PV 可能以只读方式导出到服务器上。每个 PV 都有一套自己的用来描述特定功能的访问模式。
ReadWriteOnce——该卷可以被单个节点以读/写模式挂载(命令行缩写:RWO)
ReadOnlyMany——该卷可以被多个节点以只读模式挂载(命令行缩写:ROX)
ReadWriteMany——该卷可以被多个节点以读/写模式挂载(命令行缩写:RWX)
一个卷一次只能使用一种访问模式挂载,即使它支持很多访问模式。以下只列举部分常用插件
Volume 插件
ReadWriteOnce
ReadOnlyMany
ReadWriteMany
AWSElasticBlockStore
✓
-
-
CephFS
✓
✓
✓
GCEPersistentDisk
✓
✓
-
Glusterfs
✓
✓
✓
HostPath
✓
-
-
NFS
✓
✓
✓
RBD
✓
✓
-
7.4.9 PV 的回收策略 及 阶段状态
回收策略包括
Retain(保留)——手动回收
Recycle(回收)——基本擦除( rm -rf /thevolume/* )【ps 在新版本中该策略已被弃用】
Delete(删除)——关联的存储资产(例如 AWS EBS、GCE PD、Azure Disk 和 OpenStack Cinder 卷)将被删除。
当前,只有 NFS 和 HostPath 支持回收策略。AWS EBS、GCE PD、Azure Disk 和 Cinder 卷支持删除策略。
PV 可以处于以下的某种状态
Available(可用)——一块空闲资源还没有被任何声明绑定
Bound(已绑定)——卷已经被声明绑定
Released(已释放)——声明被删除,但是资源还未被集群重新声明
Failed(失败)——该卷的自动回收失败
命令行会显示绑定到 PV 的 PVC 的名称。
7.4.10 PV 的实验演练 - NFS 1、安装 NFS 服务器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 $ yum install -y nfs-common nfs-utils rpcbind $ mkdir -p /nfs01 /nfs02 /nfs03 /nfs04 $ chmod 777 /nfs01 /nfs02 /nfs03 /nfs04 $ chown nfsnobody /nfs01 /nfs02 /nfs03 /nfs04 $ cat /etc/exports /nfs01 *(rw,no_root_squash,no_all_squash,sync) /nfs02 *(rw,no_root_squash,no_all_squash,sync) /nfs03 *(rw,no_root_squash,no_all_squash,sync) /nfs04 *(rw,no_root_squash,no_all_squash,sync) $ systemctl start rpcbind nfs $ showmount -e 192.168.245.44 Export list for 192.168.245.44: /nfs01 * /nfs02 * /nfs03 * /nfs04 * $ mkdir /test $ mount -t nfs 192.168.245.44:/nfs01 /test / $ cd /test / $ touch tset.txt $ echo lemon > test.txt $ cat test.txt lemon $ umount /test $ rm -rf /test
2、部署 PV
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 apiVersion: v1 kind: PersistentVolume metadata: name: nfspv1 spec: capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: nfs nfs: path: /nfs01 server: 192.168 .245 .44 --- apiVersion: v1 kind: PersistentVolume metadata: name: nfspv2 spec: capacity: storage: 10Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: nfs nfs: path: /nfs02 server: 192.168 .245 .44 --- apiVersion: v1 kind: PersistentVolume metadata: name: nfspv3 spec: capacity: storage: 20Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: nfs nfs: path: /nfs03 server: 192.168 .245 .44 --- apiVersion: v1 kind: PersistentVolume metadata: name: nfspv4 spec: capacity: storage: 30Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain storageClassName: static nfs: path: /nfs04 server: 192.168 .245 .44
3、创建服务并使用 PVC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 apiVersion: v1 kind: Service metadata: name: nginx labels: app: nginx spec: ports: - port: 80 name: web clusterIP: None selector: app: nginx --- apiVersion: apps/v1 kind: StatefulSet metadata: name: web spec: selector: matchLabels: app: nginx serviceName: "nginx" replicas: 3 template: metadata: labels: app: nginx spec: containers: - name: nginx image: arminto/my_nginx:v1 ports: - containerPort: 80 name: web volumeMounts: - name: www mountPath: /usr/share/nginx/html volumeClaimTemplates: - metadata: name: www spec: accessModes: ["ReadWriteOnce" ] storageClassName: "nfs" resources: requests: storage: 1Gi
1 2 3 4 5 6 7 8 9 10 $ echo 'web-0' > /nfs01/index.html $ echo 'web-1' > /nfs02/index.html $ echo 'web-2' > /nfs03/index.html $ curl 10.244.2.24 web-0 $ curl 10.244.1.26 web-1 $ curl 10.244.1.27 web-2
7.4.11 关于 StatefulSet 的说明
匹配 Pod name ( 网络标识 ) 的模式为:$(statefulset名称)-$(序号),比如上面的示例:web-0,web-1,web-2 。
1 2 3 4 5 6 $ kubectl get pod NAME READY STATUS RESTARTS AGE h-pod 1/1 Running 0 23h web-0 1/1 Running 0 48m web-1 1/1 Running 0 48m web-2 1/1 Running 0 48m
StatefulSet 为每个 Pod 副本创建了一个 DNS 域名,这个域名的格式为: $(podname).(headless servername),也就意味着服务间是通过Pod域名来通信而非 Pod IP,因为当Pod所在Node发生故障时, Pod 会被飘移到其它 Node 上,Pod IP 会发生变化,但是 Pod 域名不会有变化。
1 2 3 4 5 6 7 $ kubectl exec -it h-pod -- /bin/sh / PING web-0.nginx (10.244.2.24): 56 data bytes 64 bytes from 10.244.2.24: seq=0 ttl=64 time=0.137 ms 64 bytes from 10.244.2.24: seq=1 ttl=64 time=0.056 ms 64 bytes from 10.244.2.24: seq=2 ttl=64 time=0.076 ms
StatefulSet 使用 Headless 服务来控制 Pod 的域名,这个域名的 FQDN 为:$(servicename).$(namespace).svc.cluster.local,其中,“cluster.local” 指的是集群的域名。
1 2 3 4 5 6 7 8 9 $ kubectl get pod -owide -n kube-system | grep coredns coredns-5c98db65d4-4v4kd 1/1 Running 13 47d 10.244.1.17 coredns-5c98db65d4-9k5xw 1/1 Running 7 47d 10.244.0.9 $ dig -t A nginx.default.svc.cluster.local. @10.244.0.9 ;; ANSWER SECTION: nginx.default.svc.cluster.local. 11 IN A 10.244.2.24 nginx.default.svc.cluster.local. 11 IN A 10.244.1.26 nginx.default.svc.cluster.local. 11 IN A 10.244.1.27
根据 volumeClaimTemplates,为每个 Pod 创建一个 pvc,pvc 的命名规则匹配模式:(volumeClaimTemplates.name)-(pod_name),比如上面的 volumeMounts.name=www, Podname=web-[0-2],因此创建出来的 PVC 是 www-web-0、www-web-1、www-web-2 。
1 2 3 4 5 $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE www-web-0 Bound nfspv1 1Gi RWO nfs 173m www-web-1 Bound nfspv2 10Gi RWO nfs 172m www-web-2 Bound nfspv3 20Gi RWO nfs 172m
删除 Pod 不会删除其 pvc,手动删除 pvc 将自动释放 pv
Statefulset的启停顺序
有序部署:部署StatefulSet时,如果有多个Pod副本,它们会被顺序地创建(从0到N-1)并且,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS nfspv1 1Gi RWO Retain Available nfs nfspv2 10Gi RWO Retain Available static nfspv3 20Gi RWO Retain Available slow $ kubectl apply -f pvc.yaml service/nginx created statefulset.apps/web created $ kubectl get pod NAME READY STATUS RESTARTS AGE web-0 1/1 Running 0 19s web-1 0/1 Pending 0 16s
有序删除:当Pod被删除时,它们被终止的顺序是从N-1到0。
1 2 $ kubectl get pod -owide -n default -w $ kubectl delete statefulset web -n default
有序扩展:当对 Pod 执行扩展操作时,与部署一样,它前面的Pod必须都处于Running和Ready状态。
1 $ kubectl scale statefulset/web --replicas=4
StatefulSet使用场景
稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于 PVC 来实现。
稳定的网络标识符,即 Pod 重新调度后其 PodName 和 HostName 不变。
有序部署,有序扩展,基于 init containers 来实现。
有序收缩。
7.4.12 PV卷 的资源释放流程
生产环境下要再三确认后才可以进行如下操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 $ kubectl delete -f pvc.yaml $ kubectl delete pvc www-web-0 www-web-1 www-web-2 $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS nfspv1 1Gi RWO Retain Released default/www-web-0 nfs nfspv2 10Gi RWO Retain Released default/www-web-1 nfs nfspv3 20Gi RWO Retain Released default/www-web-2 nfs nfspv4 30Gi RWX Retain Available static $ kubectl get pv nfspv1 -o yaml claimRef: apiVersion: v1 kind: PersistentVolumeClaim name: www-web-0 namespace: default resourceVersion: "69260" uid: e175a86b-aa2c-4900-a7c4-faa1865315ab $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS nfspv1 1Gi RWO Retain Available nfs nfspv2 10Gi RWO Retain Available nfs nfspv3 20Gi RWO Retain Available nfs nfspv4 30Gi RWX Retain Available static
五、K8S集群调度
Scheduler 是 kubernetes 的调度器,主要任务是把定义的 pod 分配到集群的节点上。有四点需要考虑:
公平:如何保证每个节点都能被分配资源
资源高效利用:集群所有资源最大化被使用
灵活:允许用户根据自己的需求控制调度的逻辑
效率:调度的性能要好,能够尽快地对大批量的 pod 完成调度工作
Sheduler 是作为单独的程序运行的,启动之后会一直监听 API Server,获取PodSpec.NodeName为空的 pod,对每个 pod 都会创建一个 binding,表明该 pod 应该放到哪个节点上。
1、调度说明 调度过程 调度分为几个部分:首先是过滤掉不满足条件的节点,这个过程称为predicate;然后对通过的节点按照优先级排序,这个是priority;最后从中选择优先级最高的节点。如果中间任何一步骤有错误,就直接返回错误。
Predicate 有一系列的算法可以使用:
PodFitsResources:节点上剩余的资源是否大于 pod 请求的资源
PodFitsHost:如果 pod 指定了 NodeName,检查节点名称是否和 NodeName 匹配
PodFitsHostPorts:节点上已经使用的 port 是否和 pod 申请的 port 冲突
PodSelectorMatches:过滤掉和 pod 指定的 label 不匹配的节点
NoDiskConflict:已经 mount 的 volume 和 pod 指定的 volume 不冲突,除非它们都是只读
如果在 predicate 过程中没有合适的节点,pod 会一直在pending状态,不断重试调度,直到有节点满足条件。经过这个步骤,如果有多个节点满足条件,就继续 priorities 过程:按照优先级大小对节点排序。
优先级由一系列键值对组成,键是该优先级项的名称,值是它的权重(该项的重要性)。这些优先级选项包括:
LeastRequestedPriority:通过计算 CPU 和 Memory 的使用率来决定权重,使用率越低权重越高。换句话说,这个优先级指标倾向于资源使用比例更低的节点
BalancedResourceAllocation:节点上 CPU 和 Memory 使用率越接近, 权重越高。这个要和上面的一起使用, 不应该单独使用
ImageLocalityPriority:倾向于已经有要使用镜像的节点,镜像总大小值越大,权重越高
通过算法对所有的优先级项目和权重进行计算,得出最终的结果。
自定义调度器 除了 kubernetes 自带的调度器,你也可以编写自己的调度器。通过spec:schedulername参数指定调度器的名字,可以为 pod 选择某个调度器进行调度。比如下面的 pod 选择my-scheduler进行调度,而不是默认的default-scheduler:
1 2 3 4 5 6 7 8 9 10 11 apiVersion: v1 kind: Pod metadata: name: annotation-second-scheduler labels: name: multischeduler-example spec: schedulername: my-scheduler containers: - name: pod-with-second-annotation-container image: arminto/my_nginx:v1
2、调度亲和性
一般情况下 POD 是通过集群自动调度选择某个节点的,默认情况下调度器考虑的是资源足够,并且负载尽量平均,但是有的时候需要能够更加细粒度的去控制 POD 的调度,比如内部的一些服务 gitlab 之类的也是跑在Kubernetes集群上的,就不希望对外的一些服务和内部的服务跑在同一个节点上了,害怕内部服务对外部的服务产生影响;有的时候两个服务直接交流比较频繁,又希望能够将这两个服务的 POD 调度到同样的节点上。
这就需要用到 Kubernetes 里面的一个概念:亲和性,亲和性主要分为两类:nodeAffinity和podAffinity。
节点亲和性(nodeAffinity) 键值运算关系
In:label 的值在某个列表中
NotIn:label 的值不在某个列表中
Gt:label 的值大于某个值
Lt:label 的值小于某个值
Exists:某个 label 存在
DoesNotExist:某个 label 不存在
pod.spec.nodeAffinity
requiredDuringSchedulingIgnoredDuringExecution:硬策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 apiVersion: v1 kind: Pod metadata: name: nodeaffinity01 labels: app: node-affinity-pod spec: containers: - name: with-node-affinity image: arminto/my_nginx:v1 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - k8s-node-192.168.2.55
1 2 3 4 5 $ kubectl get nodes --show-labels $ kubectl get pod -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nodeaffinity01 0/1 Pending 0 5s <none> <none> <none> <none>
preferredDuringSchedulingIgnoredDuringExecution:软策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 apiVersion: v1 kind: Pod metadata: name: nodeaffinity02 labels: app: node-affinity-pod spec: containers: - name: with-node-affinity image: arminto/my_nginx:v1 affinity: nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 preference: matchExpressions: - key: kubernetes.io/hostname operator: In values: - k8s-node-192.168.2.55
1 2 3 4 $ kubectl get pod -owide NAME READY STATUS RESTARTS AGE IP NODE nodeaffinity01 0/1 Pending 0 3m46s <none> <none> nodeaffinity02 1/1 Running 0 5s 10.244.4.8 k8s-node-192.168.2.33
Tips:硬策略和软策略是可以合并一起使用的,但是必须要先满足硬策略,才能在满足软策略。
Pod 亲和性(podAffinity) pod.spec.affinity.podAffinity/podAntiAffinity
preferredDuringSchedulingIgnoredDuringExecution:软策略
requiredDuringSchedulingIgnoredDuringExecution:硬策
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 apiVersion: v1 kind: Pod metadata: name: test labels: app: lemon spec: containers: - name: lemon image: arminto/my_nginx:v1 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - k8s-node-192.168.2.33 --- apiVersion: v1 kind: Pod metadata: name: lemon labels: app: lemon spec: containers: - name: lemon image: arminto/my_nginx:v1 affinity: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - lemon topologyKey: kubernetes.io/hostname podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 podAffinityTerm: labelSelector: matchExpressions: - key: tools operator: In values: - armin topologyKey: kubernetes.io/hostname
1 2 3 4 $ kubectl get pod -owide NAME READY STATUS RESTARTS AGE IP NODE lemon 1/1 Running 0 4s 10.244.4.12 k8s-node-192.168.2.33 test 1/1 Running 0 4s 10.244.4.11 k8s-node-192.168.2.33
亲和性/反亲和性调度策略比较如下:
调度策略
匹配标签
操作符
拓扑域支持
调度目标
nodeAffinity
主机
In, NotIn, Exists,DoesNotExist, Gt, Lt
否
指定主机
podAffinity
POD
In, NotIn, Exists,DoesNotExist
是
POD与指定POD同一拓扑域
podAnitAffinity
POD
In, NotIn, Exists,DoesNotExist
是
POD与指定POD不在同一拓扑域
Tips:拓扑域 就是节点的意思
3、调度污点
节点亲和性,是pod的一种属性(偏好或硬性要求),它使pod被吸引到一类特定的节点。Taint 则相反,它使节点能够排斥一类特定的 pod。
Taint 和 Toleration
Taint 和 toleration 相互配合,可以用来避免 pod 被分配到不合适的节点上。每个节点上都可以应用一个或多个taint ,这表示对于那些不能容忍这些 taint 的 pod,是不会被该节点接受的。如果将 toleration 应用于 pod上,则表示这些 pod 可以(但不要求)被调度到具有匹配 taint 的节点上
污点(Taint) 1、污点 ( Taint ) 的组成
使用kubectl taint命令可以给某个 Node 节点设置污点,Node 被设置上污点之后就和 Pod 之间存在了一种相斥的关系,可以让 Node 拒绝 Pod 的调度执行,甚至将 Node 已经存在的 Pod 驱逐出去。
每个污点的组成如下:
每个污点有一个 key 和 value 作为污点的标签, 其中 value 可以为空, effect 描述污点的作用。当前 tainteffect 支持如下三个选项:
NoSchedule:表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上
PreferNoSchedule:表示 k8s 将尽量避免将 Pod 调度到具有该污点的 Node 上
NoExecute:表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上,同时会将 Node 上已经存在的 Pod 驱逐出去
2、污点的设置、查看和去除
1 2 3 4 5 6 7 8 $ kubectl taint nodes node1 key1=value1:NoExecute $ kubectl describe node node-name $ kubectl taint nodes node1 key1:NoExecute-
容忍(Tolerations) 设置了污点的 Node 将根据 taint 的 effect:NoSchedule、PreferNoSchedule、NoExecute 和 Pod 之间产生互斥的关系,Pod 将在一定程度上不会被调度到 Node 上。但我们可以在 Pod 上设置容忍 ( Toleration ) ,意思是设置了容忍的 Pod 将可以容忍污点的存在,可以被调度到存在污点的 Node 上。
pod.spec.tolerations
1 2 3 4 5 6 7 8 9 10 11 12 13 tolerations: - key: "key1" operator: "Equal" value: "value1" effect: "NoExecute" tolerationSeconds: 3600 - key: "key1" operator: "Equal" value: "value1" effect: "NoSchedule" - key: "key2" operator: "Exists" effect: "PreferNoSchedule"
其中 key, vaule, effect 要与 Node 上设置的 taint 保持一致
operator 的值为 Exists 将会忽略 value 值
tolerationSeconds 用于描述当 Pod 需要被驱逐时可以在 Node 上继续保留运行的时间,会一直循环
1、不指定 key 值时,表示容忍所有的污点 key
1 2 tolerations: - operator: "Exists"
2、当不指定 effect 值时,表示容忍所有的污点作用
1 2 3 tolerations: - key: "key" operator: "Exists"
3、有多个 Master 存在时,防止资源浪费,可以如下设置
1 kubectl taint nodes Node-Name node-role.kubernetes.io/master=:PreferNoSchedule
4、固定节点 指定调度节点 1、Pod.spec.nodeName 将 Pod 直接调度到指定的 Node 节点上,会跳过 Scheduler 的调度策略,该匹配规则是强制匹配。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 apiVersion: extensions/v1beta1 kind: Deployment metadata: name: web01 spec: replicas: 5 template: metadata: labels: app: web01 spec: nodeName: k8s-node-192.168.2.22 containers: - name: web01 image: arminto/my_nginx:v1 ports: - containerPort: 80
2、Pod.spec.nodeSelector:通过 kubernetes 的 label-selector 机制选择节点,由调度器调度策略匹配 label,而后调度 Pod 到目标节点,该匹配规则属于强制约束。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 apiVersion: extensions/v1beta1 kind: Deployment metadata: name: web02 spec: replicas: 3 template: metadata: labels: app: web02 spec: nodeSelector: k1: v1 containers: - name: web02 image: arminto/my_nginx:v1 ports: - containerPort: 80
1 $ kubectl label node k8s-node-192.168.2.33 k1=v1
六、K8S 集群安全
机制说明:Kubernetes 作为一个分布式集群的管理工具,保证集群的安全性是其一个重要的任务。API Server 是集群内部各个组件通信的中介,也是外部控制的入口。所以 Kubernetes 的安全机制基本就是围绕保护 API Server 来设计的。Kubernetes 使用了认证(Authentication)、鉴权(Authorization)、准入控制(AdmissionControl)三步来保证API Server的安全。
1、集群认证 Authentication
HTTP Token 认证:通过一个 Token 来识别合法用户
HTTP Token 的认证是用一个很长的特殊编码方式的并且难以被模仿的字符串 - Token 来表达客户的一种方式。Token 是一个很长的很复杂的字符串,每一个 Token 对应一个用户名存储在 API Server 能访问的文件中。当客户端发起 API 调用请求时,需要在 HTTP Header 里放入 Token。
HTTP Base 认证:通过用户名+密码的方式认证
用户名+:+密码用 BASE64 算法进行编码后的字符串放在 HTTP Request 中的 HeatherAuthorization 域里发送给服务端,服务端收到后进行编码,获取用户名及密码
最严格的 HTTPS 证书认证:基于 CA 根证书签名的客户端身份认证方式
HTTPS 证书认证
需要认证的节点
两种类型
两种类型Kubenetes 组件对 API Server 的访问:kubectl、Controller Manager、Scheduler、kubelet、kube-proxy
Kubernetes 管理的 Pod 对容器的访问:Pod(dashborad 也是以 Pod 形式运行)
安全性说明
Controller Manager、Scheduler 与 API Server 在同一台机器,所以直接使用 API Server 的非安全端口访问,--insecure-bind-address=127.0.0.1
kubectl、kubelet、kube-proxy 访问 API Server 就都需要证书进行 HTTPS 双向认证
证书颁发
手动签发:通过 k8s 集群的跟 ca 进行签发 HTTPS 证书
自动签发:kubelet 首次访问 API Server 时,使用 token 做认证,通过后,Controller Manager 会为kubelet 生成一个证书,以后的访问都是用证书做认证了
kubeconfig kubeconfig 文件包含集群参数(CA证书、API Server地址),客户端参数(上面生成的证书和私钥),集群context 信息(集群名称、用户名)。Kubenetes 组件通过启动时指定不同的 kubeconfig 文件可以切换到不同的集群。
ServiceAccount Pod中的容器访问API Server。因为Pod的创建、销毁是动态的,所以要为它手动生成证书就不可行了。Kubenetes使用了Service Account解决Pod 访问API Server的认证问题。
Secret 与 SA 的关系 Kubernetes 设计了一种资源对象叫做 Secret,分为两类,一种是用于 ServiceAccount 的 service-account-token,另一种是用于保存用户自定义保密信息的 Opaque。ServiceAccount 中用到包含三个部分:Token、ca.crt、namespace。
oken是使用 API Server 私钥签名的 JWT。用于访问API Server时,Server端认证
ca.crt,根证书。用于Client端验证API Server发送的证书
namespace, 标识这个service-account-token的作用域名空间
1 2 kubectl get secret --all-namespaces kubectl describe secret default-token-5gm9r --namespace=kube-system
默认情况下,每个 namespace 都会有一个 ServiceAccount,如果 Pod 在创建时没有指定 ServiceAccount,就会使用 Pod 所属的 namespace 的 ServiceAccount。
总结
2、集群鉴权 3、准入控制 准入控制是API Server的插件集合,通过添加不同的插件,实现额外的准入控制规则。甚至于API Server的一些主要的功能都需要通过 Admission Controllers 实现,比如 ServiceAccount。
官方文档上有一份针对不同版本的准入控制器推荐列表,其中最新的 1.14 的推荐列表是:
1 NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota
列举几个插件的功能:
NamespaceLifecycle:防止在不存在的 namespace 上创建对象,防止删除系统预置 namespace,删除namespace 时,连带删除它的所有资源对象。
LimitRanger:确保请求的资源不会超过资源所在 Namespace 的 LimitRange 的限制。
ServiceAccount:实现了自动化添加 ServiceAccount。
ResourceQuota:确保请求的资源不会超过资源的 ResourceQuota 限制。