本章主要介绍应用程序在服务器上部署方式的演变以及Kubernetes的概念、组件和工作原理。
在部署应用的方式上,主要经历了三个时代:
有点:简单,不需要其它技术参与
缺点:不能为应用程序定义资源使用边界,很难合理分配计算资源,而程序直接容易产生影响。
优点:程序环境不会相互影响,提供一定程度的安全性
缺点:增加了操作系统,浪费了资源
优点:
可以保证每个容器拥有自己的文件系统、CPU、内存、进程空间等 运行应用程序所需的资源都被容器包装,并和地层基础框架解耦 容器化的应用程序可以跨云服务商、跨Linux操作系统发行版本部署
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hE9Y58Qa-1680596600514)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\2023-03-03-16-19-52-image.png?msec=1680573118594)]
为了管理容器,就产生了容器编排软件:
Swarm Docker自己的容器编排软件
Mesos Apache的一个资源统一管理软件,需要和Marathon结合使用
Kubernetes Google开源的容器编排软件
Kubernetes是一个全新的基于容器技术的分布式架构领先方案,是谷歌严格保密十几年的密码武器**Borg**系统的一个开源版本,与2014年9月发布第一个版本,2015年7月发布第一个正式版本。
Kubernetes本质是一组服务器集群,它可以在集群的每个节点上运行特定的程序,来对节点中的容器进行管理。它的目的就是实现资源管理的自动化,主要提供以下功能:
自我修复:一旦某一个容器崩溃,能够在1秒左右迅速启动新的容器;
弹性伸缩:可以根据需要,自动对集群中正在运行的容器数量进行调整;
服务发现:服务可以通过自动发现的形式找到它所依赖的服务;
负载均衡:如果一个服务起了多个容器,能够自动实现请求的负载均衡;
版本回款:如果发现发布的程序版本有问题,可以立即回退到原来的版本;
存储编排:可以根据容器自身的需求自动创建存储卷;
一个Kubernetes集群主要是由控制节点(Master)和工作节点(Node)组成,每个节点都会安装不同的组件。
Master 集群的控制平面,负责集群的决策
ApiServer: 资源操作的统一入口,接收用户输入的命令,提供认证、授权、API注册和发现等机制。
Scheduler: 负责集群资源调度,按照预定的资源调度策略将Pod调度到对应的Node节点上。
ControllerManager:负责维护集群的状态,比如程序部署安排、故障检测、自动扩展、滚动更新等。
Etcd:负责存储集群中各种资源对象信息。
Node 集群的数据平面,负责为容器提供运行环境
Kubelet: 负责维护集群的生命周期,即通过控制Docker来创建、更新、销毁容器。
KubeProxy: 负责提供集群内部的服务发现和负载均衡。
Docker: 负责节点上容器的各种操作。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x6UrfVfh-1680596600515)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\2023-03-03-16-52-08-image.png?msec=1680573118589)]
下面以部署一个Nginx服务来说明Kubernetes系统各个组件调用的关系:
1)首先要明确,一旦kubernetes启动之后,master和node都会将自身的信息存储在etcd数据库中;
2)一个nginx服务起的安全请求会首先被发送到master节点的apiServer组件;
3)apiServer组件会调用Scheduler组件来决定到底应该把服务安装到哪个node节点上;
4)apiServer调用controller-manager去调度node节点安装nginx服务;
5)kuberlet接收到指令后,会通知docker,然后由docker来启动一个nginx的pod,pod是kubernetes操作的最小单元,容器必须跑在pod中;
6)一个nginx服务就行了,如果需要访问nginx,就需要通过kube-proxy来对pod产生访问的代理,这样外界就可以访问集群中的nginx服务了。
Master:集群控制节点,每个集群中需要至少有一个master节点负载集群的管控;
Node:工作负载节点,由master分配容器到这些node节点上,然后node节点上的docker负责容器的运行;
Controller:控制器,通过它来实现对pod的管理,比如启动pod,停止pod,伸缩pod等;
Pod:Kubernetes的最小控制单元,容器都是运行在Pod中,一个Pod中可以运行一个或者多个容器。
Service:Pod对外服务的统一入口
Label:标签,用来对Pod进行分类,同一类Pod会拥有相同的标签
NameSpace:命名空间,用来隔离Pod的运行环境
本章主要介绍如何搭建kubernetes集群
Kubernetes集群大体上分为两类:一主多从和多主多从。
一主多从:一台Master节点和多台Node节点,搭建简单,单机故障风险,适合于测试环境
多主多从:多台Master节点和多台Node节点,搭建满分,安全性高,适合于生成环境
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1wwjMait-1680596600515)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\2023-03-03-17-23-49-image.png?msec=1680573118486)]
Kubernetes目前有多种集群安装方式,目前主流的方式有minikube、kubeadm、二进制包。
minikube:一个用于快速搭建单节点Kubernetes的工具
kubeadm:一个快速搭建kubernetes集群的工具
二进制包:从官网下载每个组件的二进制包,依此安装(对于理解Kubernetes组件有帮助),推荐用于生成环境。
一主二从
主机规划
作用 | IP地址 | 操作系统 | 配置 |
---|---|---|---|
Master | 192.168.1.111 | CentOS 7 | CPU: 2核,内存: 2G,硬盘:50G |
Node1 | 192.168.1.114 | CentOS 7 | CPU: 2核,内存: 2G,硬盘:50G |
Node2 | 192.168.1.115 | CentOS 7 | CPU: 2核,内存: 2G,硬盘:50G |
配置信息
配置项 | 配置值 | 说明 |
---|---|---|
Service网段 | 10.96.0.0/12 | 不要和宿主机在同一个网段 |
Pod网段 | 10.244.0.0/16 | 不要和宿主机在同一个网段 |
一主二从,需要三台CentOS服务器,在每台服务器中分别安装docker-19., kubeadm-1.23.0, kubectl-1.23.0, kubelet-1.23.0。
多主多从
TODO~~
要求CentOS版本在7.5或以上
cat /etc/redhat-relase
CentOS Linux Release 7.5.1804(Core)
推荐Linux内核在4.0+
uname -r
在/etc/hosts中配置每台服务器的域名
192.168.1.111 node1
192.168.1.114 node4
192.168.1.114 node5
yum install ntpdate -y
ntpdate time.windows.com
关闭防火墙
systemctl stop firewalld
禁止开机启动
systemctl disable firewalld
关闭iptables
systemctl stop iptables
禁止开机启动
systemctl disable iptables
查看selinux状态
getenforce # Disabled表示停止,Enabled表示启用
临时关闭
setenforce 0
永久停止
vim /etc/selinux/config
# 修改
SELINUX=disabled
永久停止方式二
sed -i 's/enforcing/disabled/' /etc/selinux/config # 永久
vim /etc/fstab
# 注释掉,修改完需要重启Linux
#/dev/mapper/centos-swap swap swap defaults 0 0
禁用swap分区方式二
swapoff -a # 临时
sed -ri 's/.*swap.*/#&/' /etc/fstab # 永久
将桥接的IPv4流量传递到iptables的链
vim /etc/sysctl.d/k8s.conf
# 添加内容如下
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
重新加载配置
sysctl --system
加载网桥过滤模块
modprobe br_netfilter
查看网桥过滤模块是否加载成功
lsmod | grep br_netfilter
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iR8vWyte-1680596600516)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\2023-03-03-20-01-24-image.png?msec=1680573118399)]
在kubernetes中的service既可以做反向代理,又可以做负载均衡。
代理有两种模式,一种是基于iptables的,另一种基于ipvs。ipvs的性能要高一些,使用ipvs需要安装ipvs模块
1)安装ipset和ipvs
yum install -y ipset ipvsadm
2)添加需要加载的模块写入脚本
cat <<EOF > /etc/sysconfig/modules/ipvs.modules
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
#modprobe -- nf_conntrack_ipv4
modprobe -- nf_conntrack
EOF
3)为脚本添加执行权限
chmod +x /etc/sysconfig/modules/ipvs.modules
4)执行脚本
/bin/bash /etc/sysconfig/modules/ipvs.modules
5)查看模块是否加载成功
lsmod | grep -e ip_vs -e nf_conntrack # 注意:低内核版本这里是nf_conntrack_ipv4
nf_conntrack_netlink 40960 0
nfnetlink 16384 4 nf_conntrack_netlink,nf_tables,ip_set
ip_vs_ftp 16384 0
nf_nat 32768 4 nf_nat_ipv6,nf_nat_ipv4,xt_nat,ip_vs_ftp
ip_vs_sed 16384 0
ip_vs_nq 16384 0
ip_vs_fo 16384 0
ip_vs_sh 16384 0
ip_vs_dh 16384 0
ip_vs_lblcr 16384 0
ip_vs_lblc 16384 0
ip_vs_wrr 16384 0
ip_vs_rr 16384 0
ip_vs_wlc 16384 0
ip_vs_lc 16384 0
ip_vs 151552 24 ip_vs_wlc,ip_vs_rr,ip_vs_dh,ip_vs_lblcr,ip_vs_sh,ip_vs_fo,ip_vs_nq,ip_vs_lblc,ip_vs_wrr,ip_vs_lc,ip_vs_sed,ip_vs_ftp
nf_conntrack 143360 9 xt_conntrack,nf_nat,ip6t_MASQUERADE,nf_nat_ipv6,ipt_MASQUERADE,nf_nat_ipv4,xt_nat,nf_conntrack_netlink,ip_vs
nf_defrag_ipv6 20480 1 nf_conntrack
nf_defrag_ipv4 16384 1 nf_conntrack
libcrc32c 16384 4 nf_conntrack,nf_nat,xfs,ip_vs
6)修改master节点模式为ipvs
# 在master节点执行如下命令
kubectl edit configmap kube-proxy -n kube-system
修改如下mode值为"ipvs"
ipvs:
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: “”
strictARP: false
syncPeriod: 0s
tcpFinTimeout: 0s
tcpTimeout: 0s
udpTimeout: 0s
kind: KubeProxyConfiguration
metricsBindAddress: “”
mode: “ipvs” # 这里修改为 ipvs
7)删除所有kube-proxy的pod
kubectl get pod -n kube-system | grep kube-proxy | awk '{system(" kubectl delete pod "$1" -n kube-system")}'
8)校验kube-proxy是否ipvs模式
kubectl logs kube-proxy-xxx -n kube-system # 检查是否日志出现Using ipvs Proxier
reboot
1)切换镜像源
wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -0/etc/yum . repos .d/docker-ce .repo
2)查看当前镜像源中支持的docker版本
yum list docker-ce --showduplicates
3)安装特定版本的docker-ce
# 必须指定--setopt=obsoletes=0,否则yum会自动安装更高版本
yum install --setopt=obsoletes=0 docker-ce-18.06.3.ce-3.el7 -y
4)添加一个配置文件
# Docker在默认情况下使用的Cgroup Driver为cgroupfs,而kubernetes推荐使用systemd来代替cgroupfs
mkdir /etc/docker
cat <<EOF > /etc/docker/daemon.json
exec-opts": ["native .cgroupdriver=systemd"] ."registry-mirrors": ["https ://kn8t2bca.mirror .aliyuncs.com"]
EOF
由于kubenetes的镜像在国外,速度比较慢,所以切换成国内的镜像源
1)编辑/etc/yum.repos.d/kubernetes.repo,添加内容如下:
vim /etc/yum.repos.d/kubernetes.repo
# 添加内容如下
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
2)安装kubeadm, kubectl, kubelet
yum install kubeadm-1.23.0 kubectl-1.23.0 kubelet-1.23.0 -y
3)配置kubelet
vim /etc/sysconfig/kubelet
添加内容如下
KUBELET_CGROUP_ARGS=“–cgroup-driver=systemd”
#配置Service的代理为ipvs,如果未配置会自动降级
KUBE_PROXY_MODE=“ipvs”
配置cgroup
vim /etc/sysconfig/kubelet
添加内容如下
KUBELET_EXTRA_ARGS=“–cgroup-driver=systemd --pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:3.2”
4)设置开机自启动
systemctl enable kubelet
下面操作只需要在master节点执行
1)初始化集群
kubeadm init \
--image-repository registry.aliyuncs.com/google_containers \
--service-cidr=10.96.0.0/12 \
--pod-network-cidr=10.244.0.0/16 \
--apiserver-advertise-address=192.168.1.111
如果不能初始化,需要检查是否因为从k8s.gcr.io拉不下来镜像,如果镜像无法拉取下来,才采用如下方法解决:
准备集群镜像
使用kubeadm安装kubernetes集群需要从官网镜像仓库k8s.gcr.io拉取组件的镜像,k8s.gcr.io在国内无法访问,但可以从阿里云的镜像仓库拉取,只不过名字不一样而已。因此可以从阿里云的镜像仓库把镜像拉下来,然后使用打标签的形式,把它换成k8s.gcr.io/$imageName,最后在把阿里云的镜像删除即可。
1)查看镜像
kubeadm config images list
2)拉取镜像
images=(
kube-apiserver:v1.23.0
kube-controller-manager:v1.23.0
kube-scheduler:v1.23.0
kube-proxy:v1.23.0
pause:3.2
etcd:3.4.13-0
coredns:1.7.0
)
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
初始化成功之后会在控制台看到如下信息
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 ( i d − u ) : (id -u): (id−u):(id -g) $HOME/.kube/configAlternatively, if you are the root user, you can run:
export KUBECONFIG=/etc/kubernetes/admin.conf
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.1.111:6443 --token d5o7uf.yb2c7uqm90x9ubur
–discovery-token-ca-cert-hash sha256:e60c8c8d039166b80a054df762431c6218e2efd9b435db4ccc905ff4c911f144
2)创建必要文件
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
echo 'export KUBECONFIG=$HOME/.kube/config' >> $HOME/.bashrc
source $HOME/.bashrc
3)在master节点部署CNI网络插件
Kubenetes支持多种网络插件,比如flannel, calico, canal等,任选一种即可。
安装flannel
下面操作依旧在master节点上执行,插件使用Daemon控制器,会在每个节点执行
wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
安装flannel插件
kubectl apply -f kube-flannel.yml
注意:kube-flannel.yml中的有一个网段地址 10.244.0.0/16对应的是初始化集群时的Pod网段地址,需要一致,否则会无法访问;
4)将node节点加入集群
下面操作只需要在node节点执行
在node4, node5分别执行一下脚本将其加入集群
kubeadm join 192.168.1.111:6443 --token d5o7uf.yb2c7uqm90x9ubur \
--discovery-token-ca-cert-hash sha256:e60c8c8d039166b80a054df762431c6218e2efd9b435db4ccc905ff4c911f144
过一会查看集群状态
kubectl get nodes
NAME STATUS ROLES AGE VERSION
node1 Ready control-plane,master 37m v1.23.0
node4 Ready 35m v1.23.0
node5 Ready 35m v1.23.0
如果安装过程中发生错误,需要卸载,执行以下脚本卸载
#!/bin/bash
kubeadm reset -f
modprobe -r ipip
lsmod
rm -rf $HOME/.kube
rm -rf ~/.kube/
rm -rf /etc/kubernetes/
rm -rf /etc/systemd/system/kubelet.service.d
rm -rf /etc/systemd/system/kubelet.service
rm -rf /usr/bin/kube*
rm -rf /etc/cni
rm -rf /opt/cni
rm -rf /var/lib/etcd
rm -rf /var/etcd
yum remove -y kubeadm kubectl kubelet kubernetes-cni cri-tools socat
reboot
1)部署nginx
kubectl create deployment deploy-nginx --image=nginx:1.18.0
2)暴露端口
kubectl expose deployment deploy-nginx --port=80 --type=NodePort
3)查看pod
kubectl get pod
# 结果如下
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nginx-volume-hostpath 1/1 Running 0 99m 172.16.0.43 node4 <none> <none>
pod-nginx-volume-nfs 1/1 Running 0 56m 172.16.0.44 node4 <none> <none>
4)查看Service
kubectl get svc
# 结果如下
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
deploy-nginx NodePort 10.110.151.20 <none> 80:30574/TCP 32s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 33m
5)在浏览器中访问
service暴漏的端口是30574,在master节点访问页面如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IK86u2dJ-1680596600516)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\explorer-visit.png?msec=1680573118400)]
创建失败,通过kubectl describe deployment nginx查看发现报以下错误: loadFlannelSubnetEnv failed: open /run/flannel/subnet.env: no such file or directory。解决办法:分别在master, 所有node节点上创建文件如下:
vim /run/flannel/subnet.env
# service network FLANNEL_NETWORK=10.96.0.0/12 # pod network FLANNEL_SUBNET=10.244.0.0/16 FLANNEL_MTU=1450 FLANNEL_IPMASQ=true
注意:通过以上方式不能根据解决问题,当服务器重启后subnet.env文件又会丢失,根据解决办法是重新初始化时加上–pod-network-cidr=10.244.0.0/16参数
kubeadm init --image-repository registry.aliyuncs.com/google_containers --pod-network-cidr=172.16.0.1/12
在Kubernetes中,所有的内容都抽象为资源,用户需要操作资源来管理Kubernetes。
Kubernetes的最小管理单元是pod而不是容器,容器放在pod中,Kubernetes一般也不会直接管理pod,而是通过pod控制器管理pod。
Pod提供的服务通过Service来访问;Pod中的数据持久化,通过存储系统来实现。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RLUtafcQ-1680596600517)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\3.1-1.png?msec=1680573118676)]
命令式对象管理:直接使用命令操作kubernetes资源
kubectl run nginx-pod --image=nginx:1.17.1 --port=80
命令式对象配置:通过命令配置和配置文件操作kubernetes资源
kubectl create/patch -f nginx-pod.yaml
声明式对象配置:通过apply命令和配置文件操作kubernetes资源
kubectl apply -f nginx-pod.yaml
类型 | 操作对象 | 适用环境 | 优点 | 缺点 |
---|---|---|---|---|
命令式对象管理 | 对象 | 测试 | 简单 | 只能操作活动对象,无法审计、跟踪 |
命令式对象配置 | 文件 | 开发 | 可以审计、跟踪 | 项目大时、配置文件多,操作麻烦 |
声明式对象配置 | 目录 | 开发 | 支持目录操作 | 意外情况下难以调试 |
kubectl命令
kubectl是kubernetes集群命令行工具,通过它能够对集群本身进行管理,并能够在集群上进行容器化应用的安装部署。
语法:kubectl [command] [type] [name] [flags]
command:指定要对资源进行的操作,如:create, get, delete
type:指定资源类型,如:pod,deployment,service
name:指定资源名称(资源名称区分大小写)
flages:指定额外的可选参数
示例:
1)查看所有pod
kubectl get pod
2)查看某个pod
kubectl get pod pod_name
3)查看某个pod,以yaml格式展示
kubectl get pod pod_name -o yaml
命令
通过kubectl --help可以查看都有哪些命令
命令分类 | 命令 | 翻译 | 命令作用 |
---|---|---|---|
基本命令 | create | 创建 | 创建一个资源 |
edit | |||
get | |||
patch | 更新 | 更新一个资源 | |
delete | 删除 | ||
explain | 解释 | 展示资源文档 | |
运行和调试 | run | 运行 | 在集群中运行一个指定的镜像 |
expose | 暴露 | 暴漏资源为service | |
describe | 描述 | 显示资源内部信息 | |
logs | 日志 | 输出容器在pod中的日志 | |
attach | 缠绕 | 进入运行中的容器 | |
exec | 执行 | 执行容器中的一个命令 | |
cp | 复制 | 在pod外复制文件 | |
rollout | 首次展示 | 管理资源发布 | |
scale | 规模 | 扩(缩)pod的数量 | |
autoscale | 自动调整 | 自动调整pod的数量 | |
高级命令 | apply | rc | |
label | 标签 | ||
其它命令 | cluster-info | 集群信息 | 显示集群信息 |
version | 版本 | 显示当前Server和Client的版本 |
资源类型
查看所有资源类型
kubectl api-resources
资源分类 | 资源名称 | 缩写 | 资源作用 |
---|---|---|---|
集群级别资源 | nodes | no | 集群组成部分 |
namespace | ns | 隔离pod | |
pods | po | 装在容器 | |
pod资源控制器 | replicationcontrollers | rc | 控制pod资源 |
replicasets | rc | 控制pod资源 | |
jobs | 控制pod资源 | ||
cronjobs | cj | 控制pod资源 | |
statefulsets | |||
服务发现资源 | services | ||
ingress | |||
存储资源 | volumattachments | ||
persistentvolumes | pv | 存储 | |
persistentvolumeclaims | pvc | 存储 | |
配置资源 | configmaps | cm | 配置 |
secrets |
表格不完整,待补充
命令式对象配置就是使用命令配合配置文件一起操作kubenetes资源。
示例:kubectl create/patch -f nginx-pod.yaml
1)创建一个nginx-pod.yaml
apiVersion: v1
kind: Namespace
metadata:
name: dev
---
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
namespace: dev
spec:
containers:
- name: nginx-containers
image: nginx-1.18.0
2)执行create命令,创建资源
kubectl create -f nginx-pod.yaml
[root@node1 k8s-test]# kubectl create -f nginx-pod.yaml
namespace/dev created
pod/nginx-pod created
此时会发现创建了命名空间 dev, 在该命名空间下创建一个pod nginx-pod。
3)查看资源
kubectl get -f nginx-pod.yaml
[root@node1 k8s-test]# kubectl get -f nginx-pod.yaml
NAME STATUS AGE
namespace/dev Active 3m1sNAME READY STATUS RESTARTS AGE
pod/nginx-pod 1/1 Running 0 3m
4)删除资源
kubectl delete -f nginx-pod.yaml
总结:命令式对象配置的方式操作资源,可以简单认为:命令 + yaml配置文件(里面是命令需要的各种参数)
声明式对象配置和命令式对象配置很相似,只有一个命令apply,只用于创建和更新资源。
kubectl apply -f nginx-pod.yaml
[root@node1 k8s-test]# kubectl apply -f nginx-pod.yaml
namespace/dev created
pod/nginx-pod created
如果再次运行,提示如下:
[root@node1 k8s-test]# kubectl apply -f nginx-pod.yaml
namespace/dev unchanged
pod/nginx-pod unchanged
如果修改了nginx-pod.yaml,运行提示如下:
[root@node1 k8s-test]# kubectl apply -f nginx-pod.yaml
namespace/dev unchanged
pod/nginx-pod configured
总结:声明式对象配置就是使用apply描述一个资源的最终状态(在yaml中定义的状态)
使用apply操作资源:
如果资源不存在,就创建,相当于kubectl create
如果资源已存在,就更新,相当于kubectl patch
kubectl的运行是需要进行配置的,它的配置文件是$HOME/.kube,如果想要在node节点上运行此命令,需要将master节点上的.kube文件复制到node节点上,即在master节点上执行:
scp -r $HOME/.kube node4:$HOME/
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F1yXtvcH-1680596600518)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\pod.png?msec=1680573118400)]
每个pod中包含一个或多个容器,这些容器分为两类:
用户容器
Pause容器,每个Pod都会有的一个根容器
评估Pod的健康状态
可以在根容器设置IP地址,其它容器共享该IP实现Pod内的网络通信
这是pod的内部通讯,pod之间的通讯采用虚拟二层网络技术来实现,当前用的Flannel
Pod的资源清单
apiVersion: v1 # 必须,版本号
kind: Pod # 必须,资源类型
metadata: # 必须,元数据
name: string # 必须,Pod名称
namespace: string # Pod所属命名空间,默认为"default"
labels: # 自定义标签列表
- name: string
spec: # 必须,pod中的详细定义
containers: # 必选,pod中容器列表
- name: string # 必须,容器名称
image: string # 必选,容器镜像名称
imagePullPolicy: [ Always|Never|IfNotPresent ]
command: [string] # 容器的启动列表命令,如不指定,使用打包时使用的启动命令
args: [string] # 容器的启动命令参数列表
workingDir: string # 容器的工作目录
volumnMounts: # 挂载到容器内部的存储卷配置
- name: string # 引用pod定义的共享存储卷的名称,需要volumes[]部分定义的卷名
mountPath: string # 存储卷在容器内mount的绝对路径,应少于512字符
readOnly: boolean # 是否为只读模式
ports: # 需要暴露的端口库号列表
- name: string # 端口的名称
containerPort: int # 容器需要监听的端口号
hostPort: int # 容器所在主机需要监听的端口,默认与container相同
protocol: string # 端口协议,支持TCP和UDP,默认TCP
env: # 容器运行前需要设置的环境变量列表
- name: string # 环境变量名称
value: string # 环境变量的值
resources: # 资源限制和请求设置
limits: # 资源限制的设置
cpu: string # cpu的限制,单位为core数,将用于docker run --cpu-shares参数
memory: string # 内存请求,容器启动的初始可用数量
lifecycle: # 生命周期函数
postStart: # 容器启动时立即执行此钩子,如果执行失败,会根据重启策略进行重启
preStop: # 容器终止前执行此钩子,无论结构如何,容器都会终止
livenessProbe: # 对pod内各容器健康检测的设置,当探测无响应几次后自动重启该容器
exec: # 都容器内检查方式设置为exec方式
command: [string] # exec方式需要指定的命令或脚本
httpGet: # 对pod内容器健康检查方式设置为HttpGet,需要制定path, port
path: # string
port: # string
host: string
scheme: string
HttpHeaders: string
- name: string
value: string
tcpSocket: #对pod内各容器健康检查方式设置为tcpSocket方式
port: number
initialDelaySeconds: 0 # 容器启动后首测探测的时间
timeoutSeconds: 0 # 对容器健康检查探测等待响应的超时时间,单位秒,默认1秒
periodSeconds: 0 # 对容器监控检查的定期探测时间设置,默认秒,默认10一次
successThreshold: 0
failureThreshold: 0
securityContext:
privileged: false
restartPolicy: [Always|Never|OnFailure] # pod重启策略
nodeName: > # 设置NodeName表示将pod调度到指定的名称的node节点上
nodeSelector: object # 设置NodeSelector表示将该pod调度到包含这个label的node上
imagePullSecrets: # Pull镜像时使用secret名称,以key: secretkey格式指定
- name: string
hostNetwork: false # 是否使用主机网络模式,默认false, 如果设置为true,表示使用宿主机网络
volumes: # 在该pod上定义共享存储卷列表
- name: string # 共享卷名称(volumes类型有很多种)
emptyDir: {} # 类型为emptyDir的存储卷,与pod同生命周期的一个临时目录。为空值
hostPath: string # 类型为hostPath的存储卷,表示挂载pod所在宿主机的目录
path: string # pod所在宿主机的目录,将被用于同期中mount目录
secret: # 类型为secret的存储卷,挂载集群与定义的secret对象到容器内部
scretname: string
items:
- key: string
path: string
configMap: # 类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
name: string
items:
- key: string
path: string
可以使用命令查看每种资源的可配置项
kubectl explain 资源类型 # 查看某种资料可配置的一级属性
kubectl explain 资源类型.属性 # 查看属性的子属性
在kubernetes中,基本所有资源的一级属性都是一样的,主要包含5部分:
apiVersion 版本,由kubernetes内部定义,版本号必须可以用kubectl api-versions查询到
kind 类型,由kubernetes内部定义,可以使用kubectl api-resources查询到
metadata 元数据,主要是资源标识和说明,常用的有name, namespace, labels等
spec 描述,对各种资源的详细描述(这是配置最重要的一部分)
status 状态,不需要定义,由kubernetes自动生成;
spec的子属性
containers <[]Object> 容器列表,用于定义容器的详细信息;
nodeName 根据nodeName的值,将Pod调度到指定的Node节点上;
nodeSelecor
hostNetwork 是否使用主机网络模式,默认false;true表示使用宿主机网络;
volumes <[] Object> 存储卷,用于定义Pod上面挂的存储信息;
restartPolicy 重启策略,表示Pod在遇到故障的时候的处理策略;
Kubenetes在集群启动之后,集群中的各个组件都是以pod方式运行的。可以通过命令查看:
kubectl get pods -n kubesystem
创建并运行
kubernetes没有提供单独的pod命令,都是通过pod控制器来实现的。
命令格式: kube run pod控制器名称 [参数] (试验有问题,没有创建pod控制器)
参数:
–image 指定的pod镜像
–port 指定的端口
–namespace 指定的命名空间
示例:
创建pod:使用命令kubectl run nginx --image=nginx:1.18.0 --port=80 -n dev创建pod后,教程创建的pod名称自动添加了随机串,试验没有
删除pod:使用命令 kubectl delete pods nginx -n dev 删除pod,试验可以删除成功,教程删除后又自动创建了一个
nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
namespace: dev
spec:
containers:
- name: pod # 容器名称
image: nginx:1.18.0 # 容器镜像
imagePullPolicy: IfNotPresent # 镜像拉取策略
command: <[]string> # 容器的启动命令列表,如不指定,使用打包时使用的命令
args: <[]string> # 容器的启动命令需要的参数列表
evn: <[]string> # 容器环境变量的配置
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
resources:
运行
[root@node1 k8s-pod]# vim pod-nginx.yaml
会直接创建pod, 而不是创建pod控制器
[root@node1 k8s-pod]# vim pod-nginx.yaml
imagePullPolicy,用于设置镜像拉取策略,有三种策略
Always 总是从远程仓库拉取
IfNotPresent 本地有则使用本地镜像,否则从远程仓库拉取
Never 只是用本地镜像,从不去远程仓库拉取
默认值说明
如果镜像tag为具体版本号,默认策略是IfNotPresent
如果镜像tag为:latest(最新版本),默认策略是Always
command <[]string> # 容器启动的命令列表,如不指定,使用打包时使用的启动命令
args <[]string> #
apiVersion: v1
kind: Pod
metadata:
name: busybox-pod
namespace: dev
spec:
containers:
- name: busybox
image: busybox:1.30
command: ["/bin/bash","-c","touch /tmp/hello.txt;while true;do /bin/echo $(data + %T) >> /tmp/hello.txt; sleep 3; done;"]
“/bin/bash” 使用sh执行命令, “-c” 以管理员方式运行
进入容器:
kubectl exec pod名称 -n 命名空间 -it -c 容器名称 执行命令
kubectl exec busybox -n dev -it -c busybox /bin/bash
特别说明:
command可以完成启动命令和传递参数,为什么还需要一个args选项用于传递参数?其实这跟docker有关系,kubernetes中的command, args两项其实是实现覆盖Dockerfile中EntryPoint的功能。
如果command和args均没有写,那么使用Dockerfile的配置;
如果有command,无args,Dockerfile的配置会忽略,执行command;
如果无command,有args,Dockerfile中的EntryPoint执行,使用args参数;
如果有command,有args,Dockerfile的配置会被忽略,执行command追加args;
env <[]Object> 容器环境变量配置(不太推荐给容器配置环境变量,推荐将配置单独放在配置文件中)
apiVersion: v1
kind: Pod
metadata:
name: busybox-pod
namespace: dev
spec:
containers:
- name: busybox
image: busybox:1.30
command: ["/bin/bash","-c","touch /tmp/hello.txt;while true;do /bin/echo $(data + %T) >> /tmp/hello.txt; sleep 3; done;"]
env: # 设置环境变量
- name: "username"
value: "admin"
- name: "password"
- value: "123456"
ports <[]Object> 表示容器要暴露的端口号列表
prots:
name: # 端口名称,如果指定,必须保证name在pod中唯一
containerPort: # 容器需要监听的端口
hostPort: # 容器要在主机上公开的端口,如果设置,主机上只能运行容器的一个副本(一般省略)
hostIP: # 要将外部端口绑定主机的IP(一般省略)
protocol: # 端口协议,必须是TCP(默认)/STCP、UDP。
示例:
apiVersion: v1
kind: Pod
metadata:
name: busybox-pod
namespace: dev
spec:
containers:
- name: busybox
image: busybox:1.30
ports: # 容器暴露的端口
- name: port-80 # 端口名称
containerPort: 80 # 容器监听的端口
protocol: TCP # 协议
说明:要访问容器中的程序,需要使用:<容器端口> (和pod网络不通如何处理?)
Kubernetes对CPU和内存进行配额的机制,主要通过resource选项实现。
limits: 用于限制运行时容器的最大占用资源,当容器占用资源超过limits时会被终止,并进行重启;
requests: 用于设置容器的最小资源,如果环境资源不够,容器将无法重启;
示例:
apiVersion: v1
kind: Pod
metadata:
name: busybox-pod
namespace: dev
spec:
containers:
- name: busybox
image: busybox:1.30
resources: # 资源配额
limits: # 资源上线
cpu: "2" # CPU最大核数
memory: "10Gi" # 最大内存容量
requests: # 请求最小资源
cpu: "1" # CPU最小核数
memory: "10Mi" # 最小内存容量
cpu: core数,可以为整数或小数
memory: 内存大小,可以使用Gi,Mi,G,M
将Pod对象从创建到终止这段时间叫做pod的生命周期;
pod创建过程
运行初始化容器(init container)过程
运行主容器(main container,即:用户容器)过程
容器启动后钩子(post start)
容器终止前钩子(pre stop)
容器存活性探测(liveness probe)
容器就绪性探测(readiness probe)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VGBg6L9P-1680596600519)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\pod-lifecycle.png?msec=1680573118587)]
在整个生命周期中,Pod会出现5中状态:
挂起(Pending): apiserver已经创建了pod资源对象,但尚未被调度完成或仍处于镜像下载过程中;
运行中(Running):pod已经被调度到某节点,并且pod中的所有容器已经被kubectl创建完成;
成功(Succeeded):pod中的所有容器已经成功终止并且不会被重启(即容器启动成功之后运行完成任务后正常终止,之后不会被再次启动的容器);
失败(Failed):所有容器都已经终止,但至少有一个容器终止失败,即容器返回了非0值的退出状态(该状态对应Succeeded状态);
未知(Unknow):apiserver无法正常获取到pod对象的状态信息,通常由网络通讯失败所导致;
Pod的创建过程
用户通过kubectl或api客户端提交需要创建Pod的信息给apiServer;
apiServer开始生成pod对象信息,并将信息存入etcd,然后返回确认信息至客户端;
apiServer开始反应etcd中pod对象的变化,其它组件使用watch机制来跟踪检查apiServer的变化;
scheduler发现有新的pod对象要创建,开始为pod分配主机并将结果信息更新至apiServer;
node节点上的kubelet发现有pod调度过来,尝试调用docker启动容器,并将结果返回至apiServer;
apiServer将收到的pod状态存入etcd;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BQdg28Dl-1680596600519)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\pod-create.png?msec=1680573118488)]
初始化容器是在pod的主容器启动之前要运行的容器,主要做一些容器的前置工作。
两大特征
初始化容器必须运行完成直结束,若某初始化容器运行失败,那么kubernetes需要重启它直至成功完成;
初始化容器必须按照定义的顺序执行,当且仅当一个成功之后,后面的一个才能运行;
应用场景
提供主容器镜像中不具备的工具程序或自定义代码;
初始化容器要先于应用容器串行启动并完成,因此可用于延后应用程序的启动直至其依赖的条件得到满足;
示例
假设要以容器来运行nginx,在运行nginx之前,要先要能够连上mysql和redis服务器。
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
namespace: dev
spec:
containers:
- name: main-container
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
initContainers:
- name: test-mysql
image: busybox:1.30
command: ['sh', '-c', 'util ping 192.168.1.118 -c 1; do echo waiting for mysql...; sleep 2; done;']
- name: test-redis
image: busybox:1.30
command: ['sh', '-c', 'util ping 192.168.1.117 -c 1; do echo waiting for redis...; sleep 2; done;']
钩子函数
Kubernetes在主容器创建之后和容器停止之前提供了两个钩子函数:
postStart: 容器创建之后执行,如果失败会重启容器;
preStop: 容器终止之前执行,执行完成之后容器会成功终止,在其完成之前会阻塞删除容器的操作;
钩子函数三种处理方式
---
lifecycle:
postStart:
exec:
command:
- cat
- /tmp/healthy
说明:容器启动后执行命令 cat /tmp/healthy
示例1
apiVersion: v1
kind: Pod
metadata:
name: pod-nginx-hook-exec
namespace: dev
labels:
user: wangke
spec:
containers:
- name: nginx
image: nginx:1.18.0
imagePullPolicy: Never
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
lifecycle:
postStart:
exec:
command: ["/bin/bash", "-c", "echo post start"]
preStop:
exec:
command: ["/bin/bash", "-c", "echo pre stop"]
lifecycle:
postStart:
tcpSocket:
port: 8080
说明:在容器启动之后连接8080端口
lifecycle:
postStart:
httpGet:
scheme: HTTP # http or https
host: 192.168.1.114
port: 80
path: /
说明:容器启动后向 http://192.168.1.114/ 发起http请求
容器探测
容器探测用于检测容器中的应用是否可以正常工作,是保障业务可用性的一种传统机制。如果经过探测,实例的状态不符合预期,那么kubenetes会把问题实例“摘除”,不承担业务流量。kubernetes提供两种探针实现容器探测,分别是:
liveness probes: 存活性探针,用于检测应用实例当前是否处于正常状态,如果不是,kubernetes会重启容器;存活性探针的作用在于决定是否重启容器;
readiness probes: 就绪性探针,用于检测应用实例当前是否可以接收请求,如果不能,kubernetes不会转发流量;就绪性探针的作用在于决定是否接收请求;
上面两种探针支撑三种探测模式:
Exec命令: 在容器内执行一次命令,如果命令执行的退出码为0,则表示应用正常
livenessProbe:
lifecycle:
exec:
command: ["/bin/bash", "-c", "echo Are you OK?"]
initialDelaySeconds: > # 容器启动后多少秒执行第一次探测
timeoutSeconds: > # 探测超时时间。默认1s, 最小1s
periodSeconds: > # 执行探测的频率,默认10s, 最小1s
failureThreshold: > # 连续探测多少次认为失败,默认3次,最少1次
successThreshold: > # 连续探测多少次认为成功,默认1次
TcpSocket: 将会尝试访问容器的一个端口,如果能够建立连接,则认为应用正常
livenessProbe:
tcpSocket:
port: 8080
HttpGet: 调用容器内Web应用的URL,返回状态码在200~399之间,则认为程序正常
liveness:
httpGet:
schema: http
host: 127.0.0.1
port: 80
path: /
就绪探针
TODO
重启策略
存活性探针探测出应用出现问题,就会对容器所在的pod进行重启,这其实是由重启策略决定的,pod有三种重启策略:
三种重启策略
Always: 容器失效时自动重启,默认值;
OnFailure: 容器终止运行且退出码不为0时重启
Never: 无论状态如何,从不重启
重启策略适用于pod对象中的所有容器,首次需要启动的容器,将在其需要重启时立即重启,随后需要重启的操作将由kubernetes延时一段时间后执行,且反复重启的延长时间以次10s,20s,40s,80s,160s和300s递增。
containers:
- name: nginx
image: nginx:1.18.0
restartPolicy: Never
Pod的终止过程
用户向apiServer发送删除pod的命令;
apiServer中的pod对象信息会随着时间的推移而更新,过了宽限期内(默认30s),pod被视为dead;
将pod标记为terminating状态;
kubelet在监听到pod对象转化为terminating状态的同时启动pod关闭过程;
端点控制器监控到pod对象的关闭行为时将其从所有匹配到此端点的service资源的端点列表中移除;
如果当前pod对象定义了preStop钩子处理器,则在其标记为terminating后即会以同步的方式启动执行;
pod对象中的容器进程收到停止信号;
宽限期结束后,若pod中还存在仍在运行的进程,那么pod对象会收到立即终止的信号;
Kubelet请求apiServer将此pod的资源宽限期设置为0从而完成删除操作,此时pod对于用户已经不可见;
批量删除dev下Evicted的Pod
kubectl get pods -n dev | grep Evicted | awk '{print$1}' | xargs kubectl delete pods -n dev
在默认情况下,Pod在哪个节点上运行是由Scheduler组件采用相关算法计算出来的,。但在实际情况下,我们想控制Pod运行在某个节点上,这就需要pod的调度机制。
四大类调度方法
自动调度:由Scheduler计算得出(默认)
定向调度:NodeName, NodeSelecor决定
亲和性调度:NodeAffinity, PodAffinity, PodAntiAffinity
污点(容忍)调度:Taints, Toleration
利用Pod上声明的nodeName或者nodeSelector,依此将Pod调度到期望的Node节点上。如果该Node不存在也会强制调度,将会失败。
nodeName用于强制将Pod调度到指定Name的Node节点上,跳过Scheduler直接写入PodList。nodeName是pod的属性。
apiVersion: v1
kind: Pod
metadata:
name: pod-nginx
namespace: dev
labels:
user: wangke
spec:
containers:
- name: nginx
image: nginx:1.18.0
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
nodeName: node4
nodeSelector用于将pod调度到指定标签的node节点上。它是通过label-selector机制实现的。在pod创建之前,会由scheduler使用MatchNodeSelector调度策略进行lael匹配,找出目标node,然后将pod调度到目标节点,该匹配规则是强制约束。
kubectl label nodes node4 "name=node4"
kubectl label nodes node5 "name=node5"
apiVersion: v1
kind: Pod
metadata:
name: pod-nginx
namespace: dev
labels:
user: wangke
spec:
containers:
- name: nginx
image: nginx:1.18.0
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
nodeSelector:
name: node4
亲和性调度优先选择满足条件的node进行调度,如果没有,也可以调度到不满足的节点上,是调度更加灵活。
亲和度Affinity主要分为三类:
nodeAffinity(node亲和性): 以node为目标,解决pod可以调度到哪些node;
podAffinity(pod亲和性): 以pod为目标,解决pod可以和哪些已知pod部署在同一个拓扑域中的问题;
podAntiAffinity(pod反亲和性): 以pod为目标,解决pod不能和哪些已知pod部署在同一个拓扑域中的问题;
关于亲和性和反亲和性的应用场景:
亲和性:如果两个应用频繁交互,那就有必要让两个应用尽可能的靠近,减少网络通讯问题;
反亲和性:当采用多副本部署时,有必要采用反亲和性让各应用实例打散在各node上,以提供服务的高可用;
NodeAffinity
pod.spec.affinity.nodeAffinity
requiredDuringSchedulingIgnoredDuringExecution Node节点必须满足指定的规则才可以,相当于硬限制
nodeSelectorItems 节点选择列表
matchFields 按节点字段列出的节点选择器要求列表matchExceptions 按节点标签列出的节点选择器列表(推荐)
key values operator 关系符,支持Exists, DoesNotExist, In, NotIn, Gt, Lt
preferredDuringSchedulingIgnoredDuringExecution 优先调度到指定的规制node,相当于软限制(倾向)
preference 一个节点选择器,与相应的权重相关联 matchFileds 按节点字段列出的节点选择器要求列表 matchExpressions 按节点标签列出的节点选择器要求列表(推荐) key values operator 关系符 支持In, NotIn, Exists, DoesNotExist, Gt, Lt weight 倾向权重,在范围1-100
关系符的使用说明
- matchExpressions:
- key: nodeenv # 匹配标签的key为nodeenv的节点
operator: Exists
- key: nodeenv # 匹配标签的key为nodeenv,且value是"xxx"或"yyy"的节点
operator: In
values: ["xxx", "yyy"]
- key: nodeenv # 匹配标签的key为nodeenv,且values大于"xxx"的节点
operator: Gt
values: "xxx"
node亲和性调度-硬限制-1
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod-20230319-1
namespace: dev
labels:
user: wangke
spec:
containers:
- name: nginx
image: nginx:1.18.0
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
nodeSelectorTerms:
- matchExpressions:
- key: name
operator: In
values: ["node5"]
node亲和性调度-软限制
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod-20230319-2
namespace: dev
labels:
user: wangke
spec:
containers:
- name: nginx
image: nginx:1.18.0
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution: # 软限制
- weight: 1
preference:
matchExpressions:
- key: name
operator: In
values: ["node6"]
NodeAffinity规则设置的注意事项
如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都得到满足,pod才能够运行在指定的node上;
如果nodeAffinity指定了多个nodeSelector,那么只需要其中一个能够匹配成功即可;
如果一个nodeSelectorTerms中有多个matchExpressions,则一个节点必须满足所有的才能匹配成功;
如果一个pod所在node在pod运行期间标签发生了变化,不符合该pod的节点亲和性需求,则系统将忽略此变化;
PodAffinity
podAffinity以pod做为参考,实现让新创建的pod跟参照pod在一个区域。
podAffinity配置项
kubectl explain pod.spec.affinity.podAffinity
requiredDuringSchedulingIgnoredDuringExecution <[]Object> 硬限制
namespaces 指定参照pod的namespace topologKey 指定调度作用域 labelSelector 标签选择器 matchExpressions 按节点标签列出的节点选择器要求列表(推荐) key values operator 关系符 支持 In, NotIn, Exists, DoesNotExist matchLabels 指多个matchExpressions映射内容
preferredDuringSchedulingIgnoredDuringExecution <[]Object> 软限制
podAffinityTerm namespaces topologKey labelSeletor matchExpressions key values operator matchLabels weight 倾向权重,在范围1-100
topologyKey用于指定调度的作用域:
如果指定为kubernetes.io/hostname,那就以node节点为区分范围
如果指定为beta.kubernetes.io/os,则以node的操作系统类型来区分
pod亲和性-硬限制
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod-20230319-4
namespace: dev
labels:
user: wangke
spec:
containers:
- name: nginx
image: nginx:1.18.0
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions:
- key: app
operator: In
values: ["cust"]
topologyKey: kubernetes.io/hostname
PodAntiAffinity
PodAntiAffinity主要实现以运行pod为参照,让新创建的pod跟参照pod不在一个区域的功能。配置跟podAffinity一样。
pod反亲和性-硬约束
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod-20230319-5
namespace: dev
labels:
user: wangke
spec:
containers:
- name: nginx
image: nginx:1.18.0
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions:
- key: app
operator: In
values: ["cust"]
topologyKey: kubernetes.io/hostname
污点(Taints)
前面的调度方式都是站在pod角度上,通过在pod上添加属性,来确定pod是否要调度指定的node上。其实也可以站在node角度上,通过在node上添加污点属性,来决定是否运行pod调度过来。
node上添加污点之后就和pod存在一种互斥关系,进而拒绝pod调度进来,甚至可以将已经存在的pod驱逐出去。
污点的格式:key=value:effect,key和value是污点标签,effect描述污点的作用,支持三种选项:
PreferNoSchedule: 尽量避免把pod调度到具有该污点的node上,除非没有其它节点可调度;
NoSchedule: 不会把pod调度到该污点的node上,但不会影响当前node上已存在的pod;
NoExecute: 不会把pod调度到该污点的node上,同时也会将node上已存在的pod驱离;
使用kubectl设置和去除污点的命令如下:
设置污点
kubectl taint nodes node4 key=value:effect
示例
kubectl taint nodes node4 tag=pns:PreferNoSchedule
去除污点
kubectl taint nodes node4 key:effect-
示例
kubectl taint nodes node4 tag:NoSchedule-
去除所有污点
kube ctl taint nodes node4 key-
提示:
使用kubeadm搭建集群,默认会给master节点添加一个污点,所以pod不会调度到master节点,Taints: node-role.kubernetes.io/master:NoSchedule
容忍(Toleration)
在node上添加污点可以用于拒绝pod调度上来,但是如果就是想将一个pod调度到一个有污点的node上,可以使用容忍。
污点是拒绝,容忍是忽略
apiVersion: v1
kind: Pod
metadata:
name: pod-nginx-toleration
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.18.0
tolerations: # 添加容忍
- key: "tag" # 要容忍的污点key
operation: "Equal" # 操作符;支持Equal和Exists(默认,即存在这个tag即可)
value: "to" # 容忍污点的value
effect: "NoExecute" # 添加容忍的规则,这里必须和标记污点规则相同,即匹配tag=to:NoExecute
tolerationSeconds: 60000s # 容忍时间,当effect为NoSchedual是生效,表示pod在node上停留的时间
Label的作用是在资源上添加标识,用来区分和选择资源。
Label的特点:
一个Label会以key/value键值的形式附件到各种对象上,如Node, Pod, Service。
一个资源对象可以定义任意数量的Label,同一个Label也可以被添加到任意数量的资源上。
Label通常在资源对象定义时确定,也可以在对象创建后动态的添加或删除。
通过标签可以多维度分组,以便灵活,方便的进行资源分配、调度、配置、部署等工作。
版本标签:“version”: “release”, “version”: “stable”
环境标签:“environment”: “dev”
架构标签:“tier”: “frontend”
标签定义完毕之后,还要考虑标签的选择,使用Label Selector,即:
Label给某个资源对象定义标签
Label Selector用于查询和筛选有某些标签的资源对象
有两种Selector
基本等式的Label Selector
name = slave: 选择所有包含Label种key="name"且value="slave"的对象
基于集合的Lable Selector
name in (master, slave): 选择所有包含Label中的key="name"且value="master"或"slave"的对象
命令方式
1)给pod打标签
kubectl label pod nginx-pod version=1.0 -n dev
2)更新pod标签
kubectl label pod nginx-pod version=2.0 -n dev --overwrite
3)查看标签
kubectl get pod nginx-pod -n dev --show-labels
4)筛选标签
kubectl get pod -n dev -l version=2.0 --show-labels
5)根据标签查询pod
kubectl get pods -l “version=2.0” -n dev --show-labels
6)删除标签
kubectl label pod nginx-pod version- -n dev
在Kubernetes中,Pod是最小的控制单元,但Kubernetes很少直接控制Pod,一般通过Pod控制器完成。Pod控制器用于管理Pod,确保Pod资源符合预期的状态,当Pod的资源出现故障的时候,会尝试进行重启或重建Pod。Deployment是Kubernetes众多控制器中的一种。
在Kubernetes中,按照pod的创建方式可以将其分为两类:
自助式pod:直接创建出来的pod,这种pod删除之后就没有了,不会重建;
控制器创建pod:通过控制器创建pod,这种pod删除之后还会自动重建;
控制器的类型:
ReplicationController: 比较原始的pod控制器,已经废弃,被ReplicaSet代替;
ReplicaSet: 保证指定数据量的pod运行,并支持pod数量变更,镜像版本变更;
Deployment: 通过控制ReplicaSet来控制pod,并支持滚动升级,版本回退;
Horizontal Pod Autoscaler: 可以根据集群负载自动调整pod数量,实现削峰填谷;
DaemonSet: 在集群中的指定Node上都运行一个副本,一般用于守护进程类的任务;
Job: 它创建出来的pod只要完成任务就立即退出,用于执行一次性任务;
Cronjob: 它创建出来的pod会周期性执行,用于执行周期性的任务;
StatefulSet: 关联有状态应用;
保证指定数据量的pod运行,它会持续监听这些pod的运行状态,一旦pod发生故障,就会重启或重建。同时它还支持对pod数据的扩缩容和版本镜像升级。
ReplicaSet的资源清单
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: pod-nginx
namespace: dev
spec:
replicas: 3 # 副本数量
selector: # 选择器,指定该控制器管理哪些pod
matchLabels: # 标签匹配规则
app: pod-nginx
matchExpressions:
- {key: app, operator: In, values: [pod-nginx]}
template: # 模板,当副本数量不足时,会根据下面的模板来创建pod
metadata:
labels:
app: pod-nginx
spec:
containers:
- name: nginx
image: nginx:1.18.0
ports:
- containerPort: 80
查看控制器
kubectl get rs -n dev -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
rs-nginx 3 3 3 68m nginx nginx:1.18.0 app=pod-nginx,app in (pod-nginx)
DESIRED 期望的
CURRENT 当前的
READY 准备好提供服务的
扩缩容
kubectl edit rs rs-nginx -n dev
使用以上命令编辑保存即可生效
或者使用下面命令方式
kubectl scale rs rs-nginx --replicas=6 -n dev
镜像升级
依然使用编辑命令
使用命令修改
kubectl set image rs rs-nginx nginx=nginx:1.20.2 -n dev
删除ReplicaSet
kubectl delete rs rs-nginx -n dev
在删除rs前,会将replicasclear调整为0,等待所有pod被删除后,再执行rs对象删除。
为了更好的解决服务编排问题,在Kubernetes v1.2版本,引入了Deployment,他通过ReplicaSet来间接管理pod。
支持ReplicaSet所有功能
支持发布停止、继续
支持版本滚动升级、回退
命令格式 kubectl create deployment 名称 [参数]
参数
--image 指定pod镜像
--port 指定端口
--replicas 指定创建pod的数量
--namespace 指定命名空间
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vGRNWuKO-1680596600520)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\deployment.png?msec=1680573118481)]
Deployment通过标签关联到Pod,如上图的标签 env=dev
1)创建nginx的deployment
kubectl create deployment nginx-dep --image=nginx:1.18.0 --port=80 --replicas=2 -n dev
2)查看deployment
kubectl get deploy -n dev -o wide
3)通过配置文件创建deployment
nginx-deploy.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: pod-nginx-acc namespace: dev spec: replicas: 3 # 副本数量 revisionHistoryLimit: 3 # 保留历史版本,默认10 paused: false # 暂停部署(是否创建出deployment后暂停创建pod),默认是false progressDeadlineSeconds: 600 # 部署超时时长()s,默认600 strategy: # 策略 type: RollingUpdate # 滚动更新 rollingUpdate: # 滚动更新 maxSurge: 30% # 最大额外可以存在的副本数,可以为百分比,也可以为整数 maxUnavailable: 30% # 最大不可用状态的pod的最大值,可以为百分比,也可以为整数 selector: # 选择器,通过它指定该控制器管理哪些pod matchLabels: app: nginx-acc matchExpressions: - {key: app, operator: In, values: [nginx-acc]} template: metadata: labels: app: nginx-acc spec: containers: - name: nginx image: nginx:1.18.0 ports: - containerPort: 80 protocol: TCP
kubectl apply -f nginx-deploy.yaml
扩缩容
与replicaset的扩缩容相同
镜像策略
Deployment支持两种镜像更新策略:滚动更新(默认)和重建更新,可以通过strategy进行配置。
strategy指新的pod替换旧的pod的策略,支持两个属性:
type: 指定策略类型,可以取值RollingUpdate或Recreate RollingUpdate: 滚动更新,就是杀死一部分,旧启动一部分,在更新过程中存在两个版本的pod; Recreate: 在创建pod之前会杀掉所有已存在的pod; maxUnavailabel: 用来指定升级过程中不可用pod的最大数量,默认25%; maxSurge: 用来指定在升级过程中超过期望的pod的最大数量,默认25%;
重建更新
spec:
strategy: # 策略
type: Recreate
使用命令行升级镜像
kubectl set image deploy pod-nginx-acc nginx=nginx:1.22.1 -n dev
滚动更新
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
观察镜像升级过程
kubectl get pods -n dev -w
查看rs,发现原来的rs依然存在,只是pod的数量变为0,而后又产生了一个新的rs。新的rs产生后原有rs依然保留是为了进行版本回退。
版本回退
Deployment支持版本升级过程的暂停、继续以及版本回退等诸多功能。
kubectl rollout: 支持以下选项:
status 显示当前升级状态
history 显示升级历史记录
pause 暂停版本升级过程
resume 继续版本升级过程
restart 重启版本升级过程
undo 回滚到上一个版本(可以使用–to-revision回滚到指定版本)
查看当前升级版本的状态
kubectl rollout status deploy deploy-nginx-acc -n dev
kubectl rollout status deploy deploy-nginx-acc -n dev
deployment “deploy-nginx-acc” successfully rolled out
查看升级历史记录
kubectl rollout history deploy deploy-nginx-acc -n dev
kubectl rollout history deploy deploy-nginx-acc -n dev
deployment.apps/deploy-nginx-acc
REVISION CHANGE-CAUSE
1
2说明
CHANGE-CAUSE为空是因为在启动的时候使用如下命令
kubectl create -f deploy-nginx-acc.yaml --record=true,时其中的–record=true没有加
版本回退
kubectl rollout undo deploy deploy-ngxin-acc --to-revision=1 -n dev
金丝雀发布
Deployment支持更新过程中的控制,如暂停(pause),继续(resume)。
比如有一批新的pod资源创建完成之后立即暂停更新过程,此时仅存在一部分新版本应用,主题部分还是旧的版本。然后,再筛选一小部分的用户请求路由到新版本的pod应用,继续观察是否稳定的按期望的方式运行。确定没有问题之后再继续完成余下的pod资源滚动更新,否则立即回退更新操作。这就是所谓的金丝雀发布。
更新deployment版本,并配置暂停deployment
kubectl set image deploy deploy-nginx-acc nginx=nginx:1.22.1 -n dev && kubectl rollout pause deploy deploy-nginx-acc -n dev
观察更新状态
kubectl rollout status deploy deploy-nginx-acc -n dev
继续更新操作
kubectl rollout resume deploy deploy-nginx-acc -n dev
HPA控制器通过监控pod的使用情况,实现pod数量的自动调整。
HPA控制器可以获取每个pod的利用率,然后和HPA中的指标做对比,同时计算出需要伸缩的具体值,周后实现pod数量的调整。其实HPA和Deployment都属于Kubenetes的资源对象,它通过分析目标pod的负载情况,来确定是否需要针对性调整目标pod的副本数。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Vv4te4G-1680596600521)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\horizontal_pod_autoscale-1.png?msec=1680573118483)]
1)安装metrics-server
metrics-server可以用来收集集群中的资源使用情况;
下载并解压metrics-server-0.3.6
git clone -b v0.3.6 https://github.com/kubernetes-incubator/metrics-server
编辑metrics-server-deployment.yaml
cd /opt/kubernetes/metrics-server-0.3.6/deploy/1.8+
vim metrics-server-deployment.yaml
template:
metadata:
name: metrics-server
labels:
k8s-app: metrics-server
spec:
hostNetwork: true # 添加 hostNetwork
serviceAccountName: metrics-server
volumes:
# mount in tmp so we can safely use from-scratch images and/or read-only containers
- name: tmp-dir
emptyDir: {}
containers:
- name: metrics-server
# 修改镜像拉取地址为阿里云
image: registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server-amd64:v0.3.6
imagePullPolicy: Always
# 添加args参数
args:
- --kubectl-insecure-tls
- --kubectl-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP
volumeMounts:
- name: tmp-dir
mountPath: /tmp
执行安装
kubectl apply -f ./ # 注意,是在metrics-server-0.3.6/deploy/1.8+目录下
查看pod运行情况(实验失败 metrics-server-6555956f7b-xtxrz 没有起来,未找到原因)
kubectl get pods -n kube-system
查看node资源使用情况
kubectl top node
查看pod资源使用情况
kubectl top pod -n kube-system
2)准备deployment, service
kubectl run nginx --image=nginx:1.18.0 --requests=cpu=100m -n dev
kubectl expose deployment nginx --type=NodePort --port=80 -n dev
3)部署HPA
hpa-nginx.yaml
apiVersion: autoscaling/v1
kind: HorizonalPodAutoscaler
metadata:
name: hpa-nginx
namespace: dev
spec:
minReplicas: 1 # hpa控制的最小pod数量
maxReplicas: 10 # hpa控制的最大pod数量
targetCPUUtilizationPercentage: 3 # CPU使用率指标,即3%
scaleTargetRef: # 指定要控制的ngin信息
apiVersion: apps/v1
kind: Deployment
name: deploy-nginx # hpa要控制的deployment名称
4)创建hpa
kubectl apply -f hpa-nginx.yaml
5)查看
6)测试
DaemonSet类型的控制器可以保证集群中每一台(或指定)节点上都运行一个副本,一般用于日志收集,节点监控。
TODO
TODO
Namespace是kubernetes系统中一种非常重要的资源,主要用来实现多套环境的资源隔离或者多租户的资源隔离
kubenetes的默认命名空间
如果删除命令空间卡主了,可以使用下面方法解决
NAMESPACE=dev
kubectl get namespace $NAMESPACE -o json > $NAMESPACE.json
sed -i -e 's/"kubernetes"//' $NAMESPACE.json
kubectl replace --raw "/api/v1/namespaces/$NAMESPACE/finalize" -f ./$NAMESPACE.json
通过Deployment可以创建一组Pod,但每个Pod中的每一个容器都会分配一个IP,会存在以下问题:
IP会随着Pod的重建而产生变化
IP仅仅是集群内可见的虚拟IP,外部无法访问
因此,Kubernetes设计了Service来解决这个问题。
Service可以看作一组同类Pod对外访问的入口,借助Service,应用可以方便的实现服务的发现和负载均衡。Service相当于四层负载。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ssUitKxZ-1680596600521)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\service.png?msec=1680573118678)]
Service在很多情况下只是一个概念,真正起作用的是kube-proxy服务进程,每个node节点上都运行着一个kube-proxy服务进程。当创建service时会通过api-server向etcd写入创建service的信息,而kube-proxy会基于监听机制发现这种service变动,然后将最新的service信息转换成对应的访问规则。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SnHKsjFM-1680596600522)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\kube-proxy.png?msec=1680573118489)]
10.97.97.97:80是service提供的访问入口
当访问这个入口的时候,发现后面有三个pod的服务在等待调用
kube-proxy会基于rr(轮询)的策略,将请求分发到其中一个pod上
这个规则会在集群内的所有节点上都生效,所以任何一个节点上访问都可以
kube-proxy目前支持三种工作模式
userspace
在userspace模式下,kubernets会为每个service创建一个监听端口,发向Cluster IP(即:Service IP)的请求会被iptables规则重定向到kube-proxy监听的端口上,kube-proxy根据LB算法选择一个pod建立连接提供服务。
在该模式下,kube-proxy充当四层负载角色。由于kube-proxy运行在userspace中,在进行转发处理时会增加内核和用户空间直接的数据拷贝,虽然比较稳定,但效率较低;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5n89RwKW-1680596600522)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\service-userspace.png?msec=1680573118588)]
iptables
在iptables模式下,kube-proxy为service后端的每个pod创建对应的iptables规则,直接将发向Cluster IP(即:Service IP)的请求重定向到Pod IP。
该模式下kube-proxy不承担四层负载的角色,只负责创建iptables规则。该模式的优点是较userpace模型效率更高,但不能提供LB策略,当后端pod不可用时也无法进行重试。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cI6qL6Qk-1680596600522)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\service-iptables.png?msec=1680573118595)]
ipvs(当前流行的模式)
ipvs和iptables模式类似,kube-proxy监听pod的变化并创建相应的ipvs规则。ipvs相对iptables转发效率更高,支持更多LB算法。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oTnM8Q62-1680596600523)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\service-ipvs.png?msec=1680573118591)]
创建过程
1)当apiserver获取到创建service的请求发送给kube-proxy;
2)kube-proxy会根据请求生成ipvs策略;
3)当请求从Client尽量,会根据策略负载到后端的pod;
此模式必须安装ipvs内核模块,否则会降级为iptables。
安装后开启ipvs
kubectl edit cm kube-proxy -n kube-system
把里面的mode修改为ipvs
ipvs:
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
strictARP: false
syncPeriod: 0s
tcpFinTimeout: 0s
tcpTimeout: 0s
udpTimeout: 0s
kind: KubeProxyConfiguration
metricsBindAddress: ""
mode: "ipvs"
删除已存在的pod(其实是kube-proxy)并重新创建
kubectl delete pod -l k8s-app=kube-proxy -n kube-system
查看ipvsadm
ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConnTCP 192.168.1.111:32285 rr
TCP 10.96.0.1:443 rr
-> 192.168.1.111:6443 Masq 1 0 0
TCP 10.96.0.10: 9153 rr
1)创建集群内部可以访问的Service
type为ClusterIP的Service只能在集群内部访问
暴露Service
kubectl expose deploy nginx-dep --name nginx-svc --type=ClusterIP --port=80 --target-port=80 -n dev
查看Service
kubectl get svc nginx-svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
nginx-svc ClusterIP 10.96.187.99 80/TCP 79s run=nginx-label
2)创建集群外部可以访问的Service
type为NodePort的Service可以在集群外部访问
kubectl expose deploy nginx-dep --name nginx-svc --type=NodePort --port=80 --target-port=80 -n dev
查看Service
kubectl get svc nginx-svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
nginx-svc-2 NodePort 10.101.239.1 80:30250/TCP 49s run=nginx-label
删除Service
kubectl delete svc nginx-svc -n dev
使用配置方式
nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
namespace: dev
spec:
clusterIP: 192.168.1.11
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
run: nginx-label
type: NodePort
Service资源清单
apiVersion: v1
kind: Service
metadata:
name: svc-nginx
namespace: dev
spec:
clusterIP: 10.96.0.11 # 虚拟服务的IP地址
sessionAffinity: None # session亲和性(类似iphash负载),支持ClientIP, None两个选项
type: NodePort # Service的类型:ClusterIP, NodePort, LoadBalancer, ExternalName
ports: # 端口信息
- port: 80 # service端口
targetPort: 80 # pod端口
nodePort: 31122 # 主机端口
protocol: TCP
selector: # 标签选择器,用于确定当前service代理哪些pod
run: nginx-acc
service的类型(type)
ClusterIP: 默认值,Kubernetes自动分配的虚拟IP,只能在集群内访问;
NodePort: 将Service通过指定Node上的端口暴露给外部,通过此方法,就可以在集群外部访问服务;
LoadBalancer: 使用外接负载均衡器完成到服务的负载均衡,注意此模式需要外部云环境支持;
ExternalName: 把集群外部的服务引入集群内部,直接使用;
apiVersion: v1
kind: Service
metadata:
name: svc-nginx-acc
namespace: dev
spec:
selector:
app: nginx-acc
clusterIP: 10.96.0.11 # 如果不写会自动生成一个
type: ClusterIP
ports:
- port: 80 # service端口
targetPort: 80 # pod端口
查看
[root@node1 service]# kubectl get svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
svc-nginx-acc ClusterIP 10.96.0.11 80/TCP 37s app=nginx-acc
查看service的描述
Name: svc-nginx-acc
Namespace: dev
Labels:
Annotations:
Selector: app=nginx-acc
Type: ClusterIP
IP Families:
IP: 10.96.0.11
IPs: 10.96.0.11
Port: 80/TCP
TargetPort: 80/TCP
Endpoints: 172.16.0.33:80,172.16.0.34:80,172.16.0.36:80
Session Affinity: None
Events:
Endpoint:
Endpoint是kubernetes中的一个资源对象,存储在etcd中,用来记录一个service对应所有pod的访问地址,它是根据service配置文件中selector描述产生的。
一个Service对应一组Pod,这些Pod通过Endpoints暴露出来,Endpoints是实现实际服务的端点集合。换句话说service和pod之间的联系是通过endpoints实现的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cpGmxasg-1680596600523)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\service-endpoint.png?msec=1680573118599)]
负载分发策略
对Service的访问被分发到后端Pod上去,目前kubernetes提供两种负载策略:
如果不定义,默认使用kube-proxy策略,如随机,轮询;
如果需要客户端地址保持,可以使用sessionAffinity: ClientIP选项;
在某些场景,开发人员不想使用Service提供的负载均衡功能,希望自己来控制负载均衡策略,针对这种情况,kubernetes提供了HeadLiness Service。这类Service不会分配ClusterIP,如果想要访问service,只能通过service域名进行查询。
apiVersion: v1
kind: Service
metadata:
name: svc-nginx-acc-2
namespace: dev
spec:
selector:
app: nginx-acc
clusterIP: None # 将clusterIP设置None,即可创建headliness类型的service
type: ClusterIP
ports:
- port: 80 # service端口
targetPort: 80 # pod端口
查看
[root@node1 service]# kubectl get svc -n dev
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc-nginx-acc ClusterIP None 80/TCP 8s
查看域名解析情况
先进入到一个pod
kubectl exec -it deploy-nginx-acc-5c7c9ccff-5ksg4 -n dev /bin/bash
再查看域名解析
cat /etc/resolv.conf
nameserver 10.96.0.10
search dev.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
可以看到域名解析服务器是 10.96.0.10
访问(实验无法访问)
dig @10.96.0.10 svc-nginx-acc-2.dev.svc.cluster.local
如果希望将Service暴露给集群外部访问,就使用NodPort类型的Service。工作原理是将Servie的端口映射到Node上的一个端口,然后可以通过NodeIP:NodePort来访访问。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0qsXgEav-1680596600524)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\service-nodetype.png?msec=1680573118487)]
service nodetype 示例
apiVersion: v1
kind: Service
metadata:
name: svc-nginx-acc-3
namespace: dev
spec:
clusterIP: 10.96.0.13
ports:
- port: 80 # service 端口
targetPort: 80 # pod端口
nodePort: 30000 # 指定绑定到node上的端口(默认的取值范围:30000~32767),如果不指定,会默认分配
selector:
run: nginx-acc
type: NodePort
LoadBalancer类型和NodePort类型相似,目的都是向外部暴露一个端口区别于LoadBalancer会在集群外部再来做一个负载均衡,而这个需要外部支持,外部服务发送到这个外部负载的请求,会被转发到集群中去。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T9CMGkeB-1680596600524)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\service-loadbalancer.png?msec=1680573118602)]
ExternalName类型的Service用于引入集群外部的服务,它通过externalName属性指定外部一个服务的地址,然后在集群内访问此Service就可以访问到外部服务了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UBf5uJuI-1680596600524)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\service-externalname.png?msec=1680573118483)]
apiVersion: v1
kind: Service
metadata:
name: svc-externalname
namespace: dev
spec:
type: ExternalName # service类型
externalName: www.baidu.com # 外部地址
域名解析
dig @10.96.0.10 svc-externalname.dev.svc.cluster.local
Service对集群之外暴露服务的主要方式有两种:NodePort和LoadBalancer,但这两种有一定的缺陷:
NodePort方式会占用很多集群机器的端口,当集群服务很多时,这种缺点愈发明显;
LoadBalancer的缺点是每个Service需要一个LB,浪费、麻烦,并且需要Kubernetes之外的设备支持。
基于这种现状,kubernetes提供Ingress资源对象,Ingress只需要一个NodePort或者一个LB就可以满足暴露多个Service的需求。工作机制大概如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NCNFQW2i-1680596600525)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\ingress.png?msec=1680573118758)]
实际上,Ingress相当于一个7层负载均衡器,是kubernetes对反向代理的一个抽象,它的工作原理类似Nginx,可以理解成在Ingress里建立诸多映射,Ingress Controller通过监听这些配置规则并转换成Nginx的配置,然后对外提供服务。这里有两个概念:
1)ingress:kubernetes中的一个资源对象,作用是定义请求如何转发到service的规则;
2)ingress controller:具体实现反向代理以及负载均衡的程序,对ingress定义的规则进行解析,根据配置的规则来实现请求转发,实现方式有很多,如Nginx, Haproxy, Contour等;
Ingress(以Nginx为例)的工作原理:
1)用户编写Ingress规则,说明哪个域名对应kubernetes集群中的哪个Service;
2)Ingress控制器动态感知Ingress服务规则变化,然后生成一段对应的的Nginx配置;
3)Ingress控制器会将生成的Nginx配置写入一个运行着Nginx的服务中,并动态更新;
4)到此为止,其实真正工作的就是一个Nginx,内部配置了用户定义的请求转发规则;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lmurQqeB-1680596600525)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\ingress-process.png?msec=1680573118813)]
搭建ingress环境(未完成)
有三种方式可以安装Ingress + Nginx
通过Helm安装
通过kubectl apply安装
通过插件安装
1)通过kubectl
登录Github,搜索ingress-nginx,可以搜索到kubernetes/ingress-nginx,查看其支持的kubernetes版本。
下载deploy.yaml
https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.1/deploy/static/provider/cloud/deploy.yaml
如果使用kubectl apply -f deploy.yaml直接安装会碰到拉取下面两个镜像
registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.1.1
registry.k8s.io/ingress-nginx/controller:v1.2.0
失败问题,需要自己单独下载
docker pull liangjw/kube-webhook-certgen:v1.1.1
docker pull bitnami/nginx-ingress-controller:1.2.0
修改deploy.yaml
注释掉 externalTrafficPolicy: Local
修改controller/deploy/的image
image: bitnami/nginx-ingress-controller:1.2.0
修改ingress-nginx-admission-create的image
image: liangjw/kube-webhook-certgen:v1.1.1
修改ingress-nginx-admission-patch的image
image: liangjw/kube-webhook-certgen:v1.1.1
修改deploy.yaml种Service的类型LoadBalancer为NodePort
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.1.1
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
#externalTrafficPolicy: Local
ports:
- appProtocol: http
name: http
port: 80
protocol: TCP
targetPort: http
nodePort: 30080 # nodePort端口修改为30080
- appProtocol: https
name: https
port: 443
protocol: TCP
targetPort: https
nodePort: 30443 # nodePort端口修改为300443
selector:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
type: NodePort # 类型修改为NodePort
执行
kubectl apply -f deploy.yaml
查看pod
kubectl get pods -n ingress-nginx
查看Service
kubectl get svc -n ingress-nginx -o wide
# 结果如下
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.106.143.19 <pending> 80:31177/TCP,443:32501/TCP 47h
ingress-nginx-controller-admission ClusterIP 10.110.36.165 <none> 443/TCP 47h
1)准备3个nginx pod和3个tomcat pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: pod-nginx
template:
metadata:
labels:
app: pod-nginx
spec:
containers:
- name: nginx
image: nginx:1.18.0
ports:
- containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-tomcat
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: pod-tomcat
template:
metadata:
labels:
app: pod-tomcat
spec:
containers:
- name: tomcat
image: tomcat:8.5-jre10-slim
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: svc-nginx
namespace: dev
spec:
selector:
app: pod-nginx
clusterIP: None
type: ClusterIP
ports:
- port: 80
targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: svc-tomcat
namespace: dev
spec:
selector:
app: pod-tomcat
clusterIP: None
type: ClusterIP
ports:
- port: 8080
targetPort: 8080
2)创建ing-nginx-tomcat.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ing-nginx
namespace: dev
spec:
rules:
- host: mynginx.alisls.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc-nginx
port:
number: 80
- host: mytomcat.alisls.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc-tomcat
port:
number: 8080
如果创建ingress出现如下错误:
Internal error occurred: failed calling webhook “validate.nginx.ingress.kubernetes.io”: Post “https://ingress-nginx-controller-admission.ingress-nginx.svc:443/networking/v1/ingresses?timeout=10s”: dial tcp 10.102.20.133:443: connect: connection refused
先查询 ValidatingWebhookConfiguration
kubectl get ValidatingWebhookConfiguration # 结果如下 NAME WEBHOOKS AGE ingress-nginx-admission 1 5m
再将其删除
kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission
查看ingress
kubectl get ing ingress-http -n dev
# 结果
NAME HOSTS ADRESS PORTS AGE
ingress-http mynginx.alisls.com,mytomcat.alisls.com 80 15s
查看详细ingress
kubectl describe ing ingress-http -n dev
访问(在hosts中配置域名)
192.168.1.111 mynginx.alisls.com
192.168.1.111 mytomcat.alisls.com
在浏览器里输入
http://mynginx.alisls.com:30080 # 注意:端口是ingress-nginx的端口
为了持久化容器数据,kubernetes引入了volume的概念。
Volume是Pod中能够被多个容器共享访问的目录,它定义在Pod之上,然后被一个Pod里的多个容器挂载到具体的目录下,Kubernetes通过Volume实现同一个Pod中不同容器之间共享数据以及数据持久化存储。
Volume的生命周期不与Pod中单个容器的生命周期相关,当容器终止或启动时,Volume中的数据也不会丢失。
Kubernetes的Volume支持多种类型:
基本存储:EmptyDir, HostPath, NFS
高级存储:PV, PVC
配置存储:ConfigMap, Secret
EmptyDir是最基础的Volume类型,一个EmptyDir就是Host上的一个空目录。
EmptyDir是Pod分配到Node时创建,它的初始内容为空,并且无需指定宿主机上对应的目录,因为Kubernetes会自动分配一个目录,当Pod销毁时,EmptyDir的数据也会被永久删除。
EmptyDir的用途:
临时空间,某些应用程序运行时所需要的临时目录,且无需永久保存;
一个容器需要从另外一个容器中获取数据目录
示例1
在一个pod中准备两个容器nginx和busibox,生命一个volume分别挂载到两个容器的目录中,nginx容器负责向volume中写数据,busibox负责从volume中读数据到控制台;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ykvxSS20-1680596600526)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\volume-emptydir.png?msec=1680573118603)]
apiVersion: v1
kind: Pod
metadata:
name: pod-nginx-busibox
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.18.0
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume # 所挂载volume的名称
mountPath: /var/log/nginx # 容器路径
- name: busibox
image: busibox:1.30
ports:
- containerPort: 81
volumeMounts:
- name: logs-volume # 所挂载volume的名称
mountPath: /logs # 容器路径
command: ["/bin/bash", "-c", "tail -f /logs/access.log"] # 初始命令,动态读取指定文件内容
volumes: # 声明volume
- name: logs-volume
emptyDir: {} # 存储卷的类型
查看指定容器标准输出
kubectl logs -f volume-nginx-busibox -n dev -c busibox
HostPath是将node中一个实际的目录挂载到pod中,以供pod使用,这样当pod销毁时数据依然保存在node目录中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bsGaf1vA-1680596600526)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\volume-hostpath.png?msec=1680573118605)]
apiVersion: v1
kind: Pod
metadata:
name: pod-nginx-volume-hostpath
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.18.0
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume # 所挂载volume的名称
mountPath: /var/log/nginx # 容器路径
- name: html-volume
mountPath: /usr/share/nginx/html
volumes: # 声明volume
- name: logs-volume
hostPath:
path: /opt/kubernetes/volume/nginx/log
type: DirectoryOrCreate # 目录存在就使用,不存在就创建
- name: html-volume
hostPath:
path: /opt/kubernetes/volume/nginx/html
type取值:
DirectoryOrCreate 目录存在就使用,不存在就先创建后使用
Directory 目标必须先存在
FileOrCreate 文件存在就使用,不存在就先创建后使用
File 文件必须实现存在
Socket unix套接字必须存在
CharDevice 字符设备必须存在
BlockDevice 块设备必须存在
创建pod后查看其所调度的的node
[root@node1 volume]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nginx-volume-hostpath 1/1 Running 0 3s 172.16.0.43 node4 <none> <none>
该pod调度到node4上,查看node4对应目录如下:
[root@node4 log]# pwd
/opt/kubernetes/volume/nginx/log
[root@node4 log]# ll
total 4
-rw-r–r-- 1 root root 0 Mar 22 16:07 access.log
-rw-r–r-- 1 root root 2040 Mar 22 16:03 error.log
HostPath可以解决数据持久化问题,但当一个Node节点故障,Pod转移到其它节点,其它不能共享之前Node中HostPath目录中的数据,又会出现问题,此时需要准备网络存储系统,比如常见的NFS, CIFS。
NFS是一个网络存储系统,可以搭建一个NFS服务器,然后将Pod中的存储直接连接到NFS系统,这样的化,无论Pod在节点上如何转移,只要Node跟NFS没有问题,数据据可以成功访问。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z9pMSzeg-1680596600527)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\volume-nfs.png?msec=1680573118598)]
安装NFS服务器
1)为了简单,只在master节点创建nfs服务器
yum install nfs-utils -y
2)准备一个共享目录
mkdir -p /opt/kubernetes/volume/data/nfs -pv
3)将共享目录以读写权限暴露给192.168.1.0/24网段中的所有主机
vim /etc/exports
/opt/kubernetes/volume/data/nfs 192.168.1.0/24(rw,no_root_squash)
4)启动nfs服务
systemctl start nfs
5)在两个node节点(node4, node5)上也安装nfs
yum install nfs-utils -y
安装完之后注意不需要启动,这样的目的是为了node节点可以驱动nfs设备
示例
apiVersion: v1
kind: Pod
metadata:
name: pod-nginx-volume-nfs
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.18.0
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume # 所挂载volume的名称
mountPath: /var/log/nginx # 容器路径
volumes: # 声明volume
- name: logs-volume
nfs:
server: 192.168.1.111 # nfs服务器地址
path: /opt/kubernetes/volume/data/nfs # 共享文件路径
由于kubernetes支持的存储系统很多,为了屏蔽不同存储系统的底层实现,引入了pv, pvc两种资源对象。
PV(Persistence Volume) 是持久化卷的意思,是对底层的共享存储的一种抽象。一般情况下PV由kubernetes管理进行创建和配置,它与底层具体的共享存储技术有关,并通过插件完成与共享存储的对接。
PVC(Persistence Volume Claim)是存储卷声明的意思,是用户对存储需求的一种声明。换句话说,PVC其实就是用户向Kubernetes系统发出的一种资源需求申请。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QnG9oBwv-1680596600527)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\pvc&pv.png?msec=1680573118686)]
使用了PV和PVC之后,工作可以进一步细分:
存储:存储工程师维护
PV:kubernetes管理员维护
PVC:kubernetes用户维护
PV是存储资源的抽象,资源清单如下:
apiVersion: v1
kind: PersistenceVolume
metadata:
name: pv
spec:
nfs: # 存储类型,与底层真正存储对应
capacity: # 存储能力,目前只支持存储空间设置
storage: 2Gi
accessModes: # 访问模式
storageClassName: # 存储类别
persistenceVolumeReclaimPolicy: # 回收策略
PV的关键配置参数说明:
存储类型
底层实际存储的类型,kubernetes支持多种存储类型,每种存储类型的配置都有所差异;
存储能力(capacity)
目前只支持存储空间的设置(storage=1Gi),不过未来可能加入IOPS,吞吐量等指标;
访问模式(accesModes)
用于描述用户对存储资源的访问权限,访问权限包括
ReadWriteOnce(RWO):读写权限,但只能被单个节点挂载;
ReadOnlyMany(ROX):只读权限,可以被多个节点挂载;
ReadWriteMany(RWX):读写权限,可以被多个节点挂载;
`需要注意的是,底层不同的存储类型可能支持的访问模式不同`
回收策略(persistentVolumeReclaimPolicy)
当PV不再被使用后,对其处理的方式:
Retain(保留)保留数据,需要管理员手工清理数据;
Recycle(回收)清除PV中的数据,相当于执行rm -rf /thevolume/*;
Delete(删除)与PV相连的后端存储完成Volume的删除操作,这是常见于云服务商的存储服务;
存储类别
PV可以通过storageClassName参数指定一个存储类别
具有特定类别的PV只能与请求了该类别的PV进行绑定;
未设定类别的PV则只能与不请求任何类别的PV进行绑定;
状态(status)
一个PV的生命周期中,可能会处于4中不同的阶段:
Availabel(可用):表示可用状态,还未被任何PVC绑定;
Bound(已绑定):表示PV已经被PVC绑定;
Released(已释放):表示PV被删除,但是资源还未被
Failed(失败):表示该PV的自动回收失败;
示例
使用NFS做为存储,创建3个PV,对应NFS中的3个暴露路径
1)NFS环境准备
# 创建目录
mkdir -p /opt/kubernetes/volume/data/{pv1,pv2,pv3} -pv
# 暴露服务
vim /ect/exports
/opt/kubernetes/volume/data/pv1 192.168.1.0/24(rw,no_root_squash)
/opt/kubernetes/volume/data/pv2 192.168.1.0/24(rw,no_root_squash)
/opt/kubernetes/volume/data/pv3 192.168.1.0/24(rw,no_root_squash)
# 重启服务
systemctl restart nfs
2)创建volume-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv1
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /opt/kubernetes/volume/data/pv1
server: 192.168.1.111
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /opt/kubernetes/volume/data/pv2
server: 192.168.1.111
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv3
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /opt/kubernetes/volume/data/pv3
server: 192.168.1.111
创建PV
kubectl create -f volume-pv.yaml
查看PV
kubectl get pv -o wide
# 结果如下
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE VOLUMEMODE
pv1 1Gi RWX Retain Available 74s Filesystem
pv2 1Gi RWX Retain Available 74s Filesystem
pv3 1Gi RWX Retain Available 74s Filesystem
PVC就是对资源的申请,用来声明对存储空间,访问模式,存储类别需求信息。
资源清单
apiVersion: v1
kind: PersistentVolumnClaim
metadata:
name: pvc1
namespace: dev
spec:
accessModes: # 访问模式
selector: # 采用标签对PV选择
storageClassName: # 存储类别
resources: # 请求空间
requests:
storage: 5Gi
PVC的关键配置参数说明:
访问模式(accessModes)
用于描述用户应用对存储资源的访问权限;
选择条件(selector)
通过Label Selector的设置,可使PVC对系统中已存在的PV进行筛选;
存储类别(storageClassName)
PVC在定义时可用设定后端存储的类别,只有设置了该class的pv才能被系统选出;
资源请求(Resources)
描述对存储资源的请求
示例
1)创建volume-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc1
namespace: dev
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc2
namespace: dev
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc3
namespace: dev
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
kubectl create -f volume-pvc.yaml
# 结果如下
persistentvolumeclaim/pvc1 created
persistentvolumeclaim/pvc2 created
persistentvolumeclaim/pvc3 created
查看pvc
kubectl get pvc -n dev -o wide
# 结果如下
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE VOLUMEMODE
pvc1 Bound pv1 1Gi RWX 2m1s Filesystem
pvc2 Bound pv2 1Gi RWX 2m1s Filesystem
pvc3 Bound pv3 1Gi RWX 2m1s Filesystem
2)创建pods使用pvc
apiVersion: v1
kind: Pod
metadata:
name: pod-nginx-pvc1
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.18.0
volumeMounts:
- name: volume
mountPath: /var/log/nginx
volumes:
- name: volume
persistentVolumeClaim:
claimName: pvc1
readOnly: false # 对空间可读可写
---
apiVersion: v1
kind: Pod
metadata:
name: pod-nginx-pvc2
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.18.0
volumeMounts:
- name: volume
mountPath: /var/log/nginx
volumes:
- name: volume
persistentVolumeClaim:
claimName: pvc2
readOnly: false # 对空间可读可写
kubectl create -f pod-nginx-pvc.yaml
TODO
Secret主要用于存储敏感信息,例如密码,密钥,证书等;
1)首先使用base64对数据进行编码
echo -n 'admin' | base64 # 用户名
echo -n '123456' | base64 # 密码
2)编写secret.yaml,并创建Secret
apiVersion: v1
kind: Secret
metadata:
name: secret
namespace: dev
type: Opaque
data:
username: YWRtaW4=
password: MTIzNDU2
创建secret
kubectl create -f secret.yaml
3)创建pod,挂载secret
apiVersion: v1
kind: Pod
metadata:
name: pod-secret
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.18.0
volumeMounts:
- name: config
mountPath: /secret/config
volumes:
- name: config
secret:
secretName: secret
进入容器,查看secret信息,其已经解码
kubectl exec -it pod-secret /bin/sh -n dev
ls /secret/config
# 结果显示
password username
# 再查看 password和username
more /secret/config/username
# 结果显示
admin
对Kubernetes的各种客户端进行认证和鉴权操作。
客户端
在kubernetes集群种,有两种类型:
User Account: 一般是独立于kubernetes之外其他服务管理的用户账号;
Service Account: kubernetes管理的账号,用于为Pod中的服务进程在访问Kubernetes时提供身份标识;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hoRuuq9c-1680596600528)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\account-type.png?msec=1680573118690)]
认证、授权、准入控制
ApiServer是访问以及管理资源对象的唯一入口,任何一个请求访问ApiServer,都需要经过下面三个流程:
1)Authentication(认证):身份鉴别,只有正确的账号才能通过认证;
2)Authorization(授权):判断用户是否有权限对访问的资源执行特定的操作;
3)Admission Control(准入控制):用于补充授权机制以实现更加精细的的访问控制;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OGcDrXng-1680596600528)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\authentication-flow.png?msec=1680573118604)]
Kubenetes集群安全的最关键点在于如何识别客户端身份,它提供了3种客户端身份认证方式:
1)HTTP Base认证:通过用户名 + 密码的方式认证
这种认证方式是把“用户名:密码”用BASE64算法进行编码后的字符串放在HTTP请求的Header Authorization域发给服务端。服务端收到后进行解密,获取用户名和密码,然后进行身份认证。
2)HTTP Token认证:通过一个Token来识别合法用户
这种方式是一个一个很长的字符串(Token)来表明客户身份的一种方式。每个Token对应一个用户名,当客户端发起API调用请求时,要在HTTP的请求Header里放入Token,API Server接收到Token后会跟服务器中保留的token进行比对,然后进行用户身份认证的过程。
3)HTTPS证书认证:基于CA根证书签名的双向数字证书认证方式
`这种方式是安全性最高的一种方式,但同时也是操作起来最麻烦的一种方式`
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oA5UfmYa-1680596600529)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\authentication-https.png?msec=1680573118688)]
HTTPS认证大体分为3个过程:
1)证书申请和下发
HTTPS通信双方的服务器向CA机构申请证书,CA机构下发根证书、服务端证书以及私钥给申请者;
2)客户端和服务端双向认证
a.客户端向服务器端发起请求,服务器端下发自己的证书给客户端;
客户端接收到证书后,通过私钥解密证书,在证书中获得服务端的公钥
客户端利用服务器端的公钥认证证书中的信息,如果一致,则认可这个服务器
b.客户端发起自己的证书给服务端,服务端接收到证书后,通过私钥解密证书,
在证书中获得客户端的公钥,并用公钥认证证书信息,确认客户端是否合法
3)服务端和客户端进行通信
服务器端和客户端协商好加密方案后,客户端会产生一个随机的密钥加密,然后发送到服务端,服务端接收到这个密钥后,双方接下来通信的所有内容都通过该随机密钥加密;
Kubernetes运行同时配置多种认证方式,只要其中任意一个方式认证通过即可
授权发生在认证之后,通过认证就可以知道访问得用户是谁,然后Kubernetes会根据事先定义的授权策略来决定用户是否有权限访问,这个过程称为授权。
每个发送到ApiServer的请求都带上了用户和资源信息:比如发送请求的用户、请求的路径、请求的动作等,授权就是根据这些信息和授权策略进行比较,如果符合策略,则认为授权通过,否则返回失败;
ApiServer目前支持以下授权策略:
AlwaysDeny:表示拒绝所有请求,一般用于测试;
AlwaysAllow:允许接收所有请求,相当于集群不需要授权流程(Kubernetes默认的策略)
ABAC:基于属性的访问控制,表示使用用户配置的授权规则对用户请求进行匹配和控制;
Webhook:通过调用外部REST服务对用户进行授权;
Node:是一种专用模式,用于对kubelet发出请求进行访问控制;
RBAC:基于角色的访问控制(kubeadm安装方式下默认的选项)
RBAC(Role-Based Access Control)基于角色的访问控制,主要在描述一件事:给哪些角色授予了哪些权限,涉及到以下几个概念:
用户:User, Groups, ServiceAccount;
角色:代表着一组定义在资源上可操作的权限集合;
绑定:将定义好的角色和用户绑定在一起;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cYEgoyNA-1680596600529)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\authorization-rbac.png?msec=1680573118688)]
Kubernetes的RBAC授权引入了4个顶级资源对象:
Role,ClusterRole:角色,用于指定一组权限;
RoleBinding,ClusterRoleBinding:角色绑定,用于将角色赋予用户;
Role, RoleCluster
一个角色就是一组权限的集合,这里的权限都是许可形式的(白名单)。
# Role只能对命名空间内的资源进行授权,需要指定namespace
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
name: role-dev-pods
namespace: dev
rules:
- apiGroups: [""] # 支持的API组列表,""表示空字符串,表示核心API组
resources: ["pods"] # 支持的资源对象列表
verbs: ["get", "watch", "list"] # 允许对资源对象的操作方法列表
上面表示在dev命名空间定义了一个角色 role-dev-pods,对核心组的pods资源可以进行get, watch, list操作。
# ClusterRole可以对集群范围内资源、跨namespace的范围资源,非资源类型进行授权
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: cluster-role-pod
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
需要详细说明的是:
apiGroups: 支持的API组列表
“”, “apps”, “autoscaling”, “batch”
resources: 支持的资源对象列表
“services”, “endpoints”, “pods”, “secrets”, “configmaps”, “crontabs”, “deployments”, “jobs”, “nodes”, “rolebindings”, “clusterroles”, “daemonsets”, “replicasets”, “statefuls”, “horizontalpodautoscalers”, “replicationcontrollers”, “cronjobs”
verbs: 对资源对象的操作方法列表
“get”, “list”, “watch”, “create”, “update”, “patch”, “delete”, “exec”
RoleBinding ClusterRoleBinding
角色绑定用来把一个角色绑定到一个目标对象上,绑定目标可以是User, Group或者ServiceAcount
# RoleBinding可以将同一namespace中subject绑定到某个Role下,则此subject即具有该Role定义的权限
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: authorization-role-binding
namespace: dev
subjects:
- kind: User
name: mux
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: authorization-role
apiGroup: rbac.authorization.k8s.io
上述配置表明:将用户mux绑定到角色authorization-role
# ClusterRoleBinding在整个集群级别和所有namespace将特定的subject与ClusterRole绑定,授予权限
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: authorization-clusterrole-binding
subjects:
- kind: User
name: mux
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: authorization-clusterrole
apiGroup: rbac.authorization.k8s.io
RoleBinding引用ClusterRole进行授权
RoleBinding可以引用ClusterRole,对属于同一命名空间内(ClusterRole),然后再多个命名空间中重复使用这些ClusterRole。这样可以大幅提高授权管理工作效率,也使得各个命名空间下的基础授权规则和使用体验保持一致。
# 虽然authorization-clusterrolehi一个集群角色,但是因为使用了RoleBinding,
# 所以mux只能读取dev命令空间中的资源
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: authorization-role-binding-ns
namespace: dev
subjects:
- kind: User
name: mux
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: authorization-clusterrole
apiGroup: rbac.authorization.k8s.io
示例:创建一个只能管理dev空间下pods资源的账号
1)创建账号
# 1)创建证书
cd /etc/kubernetes/pki/
(umask 077;openssl genrsa -out devman.key 2048)
# 2)用apiserver的证书去签署
# 2-1)签名申请,申请的用户是devman,组是devgroup
openssl req -new -key devman.key -out devman.csr -subj "/CN=devman/O=devgroup"
# 2-2)签署证书
openssl x509 -req -in devman.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out devman.crt -days 3650
# 3)设置集群、用户、上下文信息
kubectl config set-cluster kubernetes --embed-certs=true --certificate-authority=/etc/kubernetes/pki/ca.crt --server=https://192.168.1.111:6443
kubectl config set-credentials devman --embed-certs=true --client-certificate=/etc/kubernetes/pki/devman.crt --client-key=/etc/kubernetes/pki/devman.key
kubectl config set-context devman@kubernetes --cluster=kubernetes --user=devman
# 切换账号
kubectl config use-context devman@kubernetes
测试,切换到用户devman@kubernetes后,查询dev命名空间下的pod提示没有权限
kubectl get pods -n dev
# 结果如下
Error from server (Forbidden): pods is forbidden: User "devman" cannot list resource "pods" in API group "" in the namespace "dev"
切回管理员账号
kubectl config use-context kubernetes-admin@kubernetes
2)创建Role,创建RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: role-dev
namespace: dev
rules:
- apiGroups: [""] # 支持的API组列表,""表示空字符串,表示核心API组
resources: ["pods"] # 支持的资源对象列表
verbs: ["get", "watch", "list"] # 允许对资源对象的操作方法列表
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: authorizaiton-role-binding
namespace: dev
subjects:
- kind: User
name: devman
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: role-dev
apiGroup: rbac.authorization.k8s.io
3)为devman授权
kubectl create -f role-rolebinding.yaml
再切换到devman账号,查询dev下的pod
[root@node1 role]# kubectl config use-context devman@kubernetes
Switched to context "devman@kubernetes".
[root@node1 role]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
deploy-myapp-754f5c8b88-4hjqp 1/1 Running 1 19h
deploy-myweb-67ffff5c49-f6qgg 1/1 Running 1 19h
通过前面的认证和授权之后,还需要经过准入控制通过之后,apiserver才会处理这个请求。
准入控制一个可配置的控制器列表,可以通过在Api-Server上通过命令行设置选择进入执行哪些准入控制器;
--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota,DefaultTolerationSeconds
只有当所有准入控制器都检查通过后,apiserver才执行该请求,否则返回拒绝。
当前可配置的Admission Control准入控制如下:
AlwaysAdmit: 允许所有请求
AlwaysDeny: 禁止所有请求,一般用于测试
AlwaysPullImages: 在启动容器之前总是去下载镜像
DenyExecOnPrivileged: 它会拦截所有想在Privileged Container上执行命令的请求
ImagePolicyWebHook: 这个插件将允许后端的一个Webhook程序来完成admission controller的功能。
Service Account:实现ServiceAccount实现了自动化;
SecurityContextDeny: 这个插件将使用SecurityContext的Pod中定义全部失效;
ResourceQuota:用于资源配额管理,观察所有请求,确保在namespace上配额不会超标;
LimitRanger: 用于资源限制管理,作用于namespace上,确保对Pod进行资源限制;
InitalResources:为设置资源请求与限制Pod,根据其镜像的历史资源的使用情况进行设置;
NamespaceLifecycle:如果尝试在一个不存在的namespace中创建资源对象,则该创建请求将被拒绝,
DefaultStorageClass: 为了实现共享存储的动态供应,为未指定StorageClass或PV的PVC尝试匹配默认的StorageClass,尽可能减少用户在申请PVC时所需了解的后端存储细节;
DefaultTolerationSeconds: 这个插件为那些没有设置forgiveness tolerations并具有notready:NoExecute和unreachable:NoExecute两种taints的Pod设置默认的“容忍”时间,为5min
PodSecurityPolicy: 这个插件用于在创建或修改Pod时决定是否根据Pod的security context和可用的
Helm是kubernetes一个包管理工具
# 官网
https://helm.sh/
# 下载
https://github.com/helm/helm/releases/tag/v3.1.2
解压 helm
tar -zxvf helm-v3.1.2-linux-amd64.tar.gz
mv linux-amd64 helm-3.1.2
helm version
helm repolist 查看helm仓库
helm help
Dashboard是kubernetes基于Web界面部署容器化应用,监控应用状态,执行故障排查以及管理Kubernetes的各种资源。
下载yaml(实验无法下载)
wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0/aio/deploy/recommended.yaml
修改kubernetes-dashboard的service类型
apiVersion: v1
kind: Service
metadata:
name: kubernetes-dashboard
namespace: kubernetes-dashboard
labels:
k8s-app: kubernetes-dashboard
spec:
type: NodeType
ports:
- port: 443
targetPort: 443
nodePort: 30009
selector:
k8s-app: kubernetes-dashboard
部署
kubectl create -f recommended.yaml
查看kubernetes-dashboard的资源
kubectl get svc,pod -n kubernetes-dashboard
创建访问账号
kubectl create serviceaccount dashboard-admin -n kubernetes-dashboard
授权
kubectl create clusterrolebinding dashboard-admin-rb --cluster-role=cluster-admin --servcieaccount=kubernetes-dashboard:dashboard-admin
获取token
kubectl get secrets -n kubernetes-dashboard | grep dashboard-admin
kubectl describe secrets dashboard-admin-token-xbqhh -n kubernetes-dashboard
访问
https://192.168.1.111:30009
# MyWeb Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ing-myweb
namespace: dev
spec:
rules:
- host: myweb.alisls.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc-myapp
port:
number: 80
---
# MyWeb Service
apiVersion: v1
kind: Service
metadata:
name: svc-myweb
namespace: dev
spec:
selector:
app: myweb-1.0.0
clusterIP: None
type: ClusterIP
ports:
- port: 80 # service 端口
targetPort: 80 # pod端口
---
# MyWeb Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-myweb
namespace: dev
spec:
replicas: 1
selector:
matchLabels:
app: myweb-1.0.0
template:
metadata:
labels:
app: myweb-1.0.0
spec:
containers:
- name: myweb
image: myweb:1.0.0
ports:
- containerPort: 80
protocol: TCP
volumeMounts:
- name: vol-myweb
mountPath: /var/log/nginx
volumes:
- name: vol-myweb
persistentVolumeClaim:
claimName: pvc-myweb
readOnly: false
nodeName: node4
---
# MyWeb PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-myweb
namespace: dev
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
---
# MyWeb PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-myweb
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Recycle
nfs:
path: /home/wangke/web-apps/myweb/logs
server: 192.168.1.111
# MyApp Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ing-myapp
namespace: dev
spec:
rules:
- host: myapp.alisls.com
http:
paths:
- path: /user
pathType: Prefix
backend:
service:
name: svc-myapp
port:
number: 9001
---
# MyApp Service
apiVersion: v1
kind: Service
metadata:
name: svc-myapp
namespace: dev
spec:
selector:
app: myapp-1.0.0
clusterIP: None
type: ClusterIP
ports:
- port: 9001 # service 端口
targetPort: 9001 # pod端口
---
# MyApp Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-myapp
namespace: dev
spec:
replicas: 1
selector:
matchLabels:
app: myapp-1.0.0
template:
metadata:
labels:
app: myapp-1.0.0
spec:
containers:
- name: myapp
image: myapp:1.0.0
ports:
- containerPort: 9001
protocol: TCP
volumeMounts:
- name: vol-myapp
mountPath: /var/log/app
volumes:
- name: vol-myapp
persistentVolumeClaim:
claimName: pvc-myapp
readOnly: false
nodeName: node4
---
# MyApp PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-myapp
namespace: dev
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
---
# MyApp PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-myapp
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Recycle
nfs:
path: /home/wangke/svc-apps/myapp/log
server: 192.168.1.111