Kubernetes 学习笔记

Kubernetes 学习笔记

1. Kubernetes介绍

本章主要介绍应用程序在服务器上部署方式的演变以及Kubernetes的概念、组件和工作原理。

1.1 应用部署方式的演变

在部署应用的方式上,主要经历了三个时代:

  • 传统部署: 互联网早期,会直接将应用程序部署在物理机上

有点:简单,不需要其它技术参与

缺点:不能为应用程序定义资源使用边界,很难合理分配计算资源,而程序直接容易产生影响。

  • 虚拟化部署:可以在一台物理机上运行多台虚拟机,每个虚拟机都是一个独立的环境

优点:程序环境不会相互影响,提供一定程度的安全性

缺点:增加了操作系统,浪费了资源

  • 容器化部署

优点:

可以保证每个容器拥有自己的文件系统、CPU、内存、进程空间等

运行应用程序所需的资源都被容器包装,并和地层基础框架解耦

容器化的应用程序可以跨云服务商、跨Linux操作系统发行版本部署

部署方式演进
为了管理容器,就产生了容器编排软件:

  • Swarm Docker自己的容器编排软件

  • Mesos Apache的一个资源统一管理软件,需要和Marathon结合使用

  • Kubernetes Google开源的容器编排软件

1.2 Kubernetes简介

Kubernetes是一个全新的基于容器技术的分布式架构领先方案,是谷歌严格保密十几年的密码武器**Borg**系统的一个开源版本,与2014年9月发布第一个版本,2015年7月发布第一个正式版本。

Kubernetes本质是一组服务器集群,它可以在集群的每个节点上运行特定的程序,来对节点中的容器进行管理。它的目的就是实现资源管理的自动化,主要提供以下功能:
  • 自我修复:一旦某一个容器崩溃,能够在1秒左右迅速启动新的容器;

  • 弹性伸缩:可以根据需要,自动对集群中正在运行的容器数量进行调整;

  • 服务发现:服务可以通过自动发现的形式找到它所依赖的服务;

  • 负载均衡:如果一个服务起了多个容器,能够自动实现请求的负载均衡;

  • 版本回款:如果发现发布的程序版本有问题,可以立即回退到原来的版本;

  • 存储编排:可以根据容器自身的需求自动创建存储卷;

1.3 Kubernetes组件

一个Kubernetes集群主要是由控制节点(Master)和工作节点(Node)组成,每个节点都会安装不同的组件。

Master 集群的控制平面,负责集群的决策

ApiServer: 资源操作的统一入口,接收用户输入的命令,提供认证、授权、API注册和发现等机制。

Scheduler: 负责集群资源调度,按照预定的资源调度策略将Pod调度到对应的Node节点上。

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

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

Node 集群的数据平面,负责为容器提供运行环境

Kubelet: 负责维护集群的生命周期,即通过控制Docker来创建、更新、销毁容器。

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

Docker: 负责节点上容器的各种操作。
Kubernetes 架构
下面以部署一个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服务了。

1.4 Kubernetes概念

Master:集群控制节点,每个集群中需要至少有一个master节点负载集群的管控;

Node:工作负载节点,由master分配容器到这些node节点上,然后node节点上的docker负责容器的运行;

Controller:控制器,通过它来实现对pod的管理,比如启动pod,停止pod,伸缩pod等;

Pod:Kubernetes的最小控制单元,容器都是运行在Pod中,一个Pod中可以运行一个或者多个容器。

Service:Pod对外服务的统一入口

Label:标签,用来对Pod进行分类,同一类Pod会拥有相同的标签

NameSpace:命名空间,用来隔离Pod的运行环境

2. 集群环境搭建

本章主要介绍如何搭建kubernetes集群

2.1 集群规划

2.1.1 集群类型

Kubernetes集群大体上分为两类:一主多从和多主多从。

  • 一主多从:一台Master节点和多台Node节点,搭建简单,单机故障风险,适合于测试环境

  • 多主多从:多台Master节点和多台Node节点,搭建满分,安全性高,适合于生成环境
    集群模式

2.1.2 安装方式

Kubernetes目前有多种集群安装方式,目前主流的方式有minikube、kubeadm、二进制包。

  • minikube:一个用于快速搭建单节点Kubernetes的工具

  • kubeadm:一个快速搭建kubernetes集群的工具

  • 二进制包:从官网下载每个组件的二进制包,依此安装(对于理解Kubernetes组件有帮助),推荐用于生成环境。

2.2 环境搭建

2.2.1 环境准备

一主二从

主机规划

作用IP地址操作系统配置
Master192.168.1.111CentOS 7CPU: 2核,内存: 2G,硬盘:50G
Node1192.168.1.114CentOS 7CPU: 2核,内存: 2G,硬盘:50G
Node2192.168.1.115CentOS 7CPU: 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~~

2.2.2 环境配置

  1. 操作系统要求

要求CentOS版本在7.5或以上

cat /etc/redhat-relase

CentOS Linux Release 7.5.1804(Core)

推荐Linux内核在4.0+

uname -r

  1. 主机名解析

在/etc/hosts中配置每台服务器的域名

192.168.1.111 node1

192.168.1.114 node4

192.168.1.114 node5

  1. 时间同步
yum install ntpdate -y
ntpdate time.windows.com
  1. 关闭firewalld服务

关闭防火墙

systemctl stop firewalld

禁止开机启动

systemctl disable firewalld
  1. 关闭iptables服务

关闭iptables

systemctl stop iptables

禁止开机启动

systemctl disable iptables
  1. 禁用selinux

查看selinux状态

getenforce # Disabled表示停止,Enabled表示启用

临时关闭

setenforce 0

永久停止

vim /etc/selinux/config

# 修改
SELINUX=disabled

永久停止方式二

sed -i 's/enforcing/disabled/' /etc/selinux/config # 永久
  1. 禁用swap分区
vim /etc/fstab

# 注释掉,修改完需要重启Linux
#/dev/mapper/centos-swap swap swap defaults 0 0

禁用swap分区方式二

swapoff -a # 临时
sed -ri 's/.*swap.*/#&/' /etc/fstab # 永久
  1. 修改Linux内核参数

将桥接的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

lsmod |grep br_netfilter
br_netfilter 22256 0
bridge 155432 1 br_netfilter

  1. 配置ipvs功能
    在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
  1. 重启
reboot

2.2.3 安装Docker

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

2.2.4 安装K8s组件

由于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

2.2.5 集群初始化

下面操作只需要在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): (idu):(id -g) $HOME/.kube/config

Alternatively, 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

2.2.6 卸载Kubernetes

如果安装过程中发生错误,需要卸载,执行以下脚本卸载

#!/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

2.3 集群测试

2.3.1 部署Nginx

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节点访问页面如下:
预览页面

创建失败,通过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

3 资源管理

3.1 资源管理介绍


在Kubernetes中,所有的内容都抽象为资源,用户需要操作资源来管理Kubernetes。

Kubernetes的最小管理单元是pod而不是容器,容器放在pod中,Kubernetes一般也不会直接管理pod,而是通过pod控制器管理pod。

Pod提供的服务通过Service来访问;Pod中的数据持久化,通过存储系统来实现。
资源访问方式

3.2 资源管理方式

  • 命令式对象管理:直接使用命令操作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

类型操作对象适用环境优点缺点
命令式对象管理对象测试简单只能操作活动对象,无法审计、跟踪
命令式对象配置文件开发可以审计、跟踪项目大时、配置文件多,操作麻烦
声明式对象配置目录开发支持目录操作意外情况下难以调试

3.2.1 命令式对象管理

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的数量
高级命令applyrc
label标签
其它命令cluster-info集群信息显示集群信息
version版本显示当前Server和Client的版本

资源类型

查看所有资源类型

kubectl api-resources
资源分类资源名称缩写资源作用
集群级别资源nodesno集群组成部分
namespacens隔离pod
podspo装在容器
pod资源控制器replicationcontrollersrc控制pod资源
replicasetsrc控制pod资源
jobs控制pod资源
cronjobscj控制pod资源
statefulsets
服务发现资源services
ingress
存储资源volumattachments
persistentvolumespv存储
persistentvolumeclaimspvc存储
配置资源configmapscm配置
secrets

表格不完整,待补充

3.2.2 命令式对象配置

命令式对象配置就是使用命令配合配置文件一起操作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 3m1s

NAME READY STATUS RESTARTS AGE
pod/nginx-pod 1/1 Running 0 3m

4)删除资源

kubectl delete -f nginx-pod.yaml

总结:命令式对象配置的方式操作资源,可以简单认为:命令 + yaml配置文件(里面是命令需要的各种参数)

3.2.3 声明式对象配置

声明式对象配置和命令式对象配置很相似,只有一个命令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

3.2.4 node节点kubectl

kubectl的运行是需要进行配置的,它的配置文件是$HOME/.kube,如果想要在node节点上运行此命令,需要将master节点上的.kube文件复制到node节点上,即在master节点上执行:

scp -r $HOME/.kube node4:$HOME/

4 Pod

4.1 Pod详解

4.2.1 Pod结构

pod结构
每个pod中包含一个或多个容器,这些容器分为两类:

  • 用户容器

  • Pause容器,每个Pod都会有的一个根容器

    • 评估Pod的健康状态

    • 可以在根容器设置IP地址,其它容器共享该IP实现Pod内的网络通信

    这是pod的内部通讯,pod之间的通讯采用虚拟二层网络技术来实现,当前用的Flannel

4.2.2 Pod定义

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: <string> # 设置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 <map[]> 根据nodeSelector中定义的信息选择将该Pod调度到包含这些label的Node上

  • hostNetwork 是否使用主机网络模式,默认false;true表示使用宿主机网络;

  • volumes <[] Object> 存储卷,用于定义Pod上面挂的存储信息;

  • restartPolicy 重启策略,表示Pod在遇到故障的时候的处理策略;

Kubenetes在集群启动之后,集群中的各个组件都是以pod方式运行的。可以通过命令查看:

kubectl get pods -n kubesystem

4.2.3 Pod配置

创建并运行

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: <Object> # 资源限制和资源请求设置

运行

[root@node1 k8s-pod]# vim pod-nginx.yaml 
会直接创建pod, 而不是创建pod控制器

[root@node1 k8s-pod]# vim pod-nginx.yaml

4.2.4 镜像策略

imagePullPolicy,用于设置镜像拉取策略,有三种策略

  • Always 总是从远程仓库拉取

  • IfNotPresent 本地有则使用本地镜像,否则从远程仓库拉取

  • Never 只是用本地镜像,从不去远程仓库拉取

默认值说明

如果镜像tag为具体版本号,默认策略是IfNotPresent

如果镜像tag为:latest(最新版本),默认策略是Always

4.2.5 启动命令

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的功能。

  1. 如果command和args均没有写,那么使用Dockerfile的配置;

  2. 如果有command,无args,Dockerfile的配置会忽略,执行command;

  3. 如果无command,有args,Dockerfile中的EntryPoint执行,使用args参数;

  4. 如果有command,有args,Dockerfile的配置会被忽略,执行command追加args;

4.2.6 环境变量

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"

4.2.7 端口配置

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网络不通如何处理?)

4.2.8 资源配额

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

4.3 Pod生命周期

将Pod对象从创建到终止这段时间叫做pod的生命周期;

  1. pod创建过程

  2. 运行初始化容器(init container)过程

  3. 运行主容器(main container,即:用户容器)过程

  • 容器启动后钩子(post start)

  • 容器终止前钩子(pre stop)

  • 容器存活性探测(liveness probe)

  • 容器就绪性探测(readiness probe)

  1. pod终止过程
    pod声明周期
    在整个生命周期中,Pod会出现5中状态:
  • 挂起(Pending): apiserver已经创建了pod资源对象,但尚未被调度完成或仍处于镜像下载过程中;

  • 运行中(Running):pod已经被调度到某节点,并且pod中的所有容器已经被kubectl创建完成;

  • 成功(Succeeded):pod中的所有容器已经成功终止并且不会被重启(即容器启动成功之后运行完成任务后正常终止,之后不会被再次启动的容器);

  • 失败(Failed):所有容器都已经终止,但至少有一个容器终止失败,即容器返回了非0值的退出状态(该状态对应Succeeded状态);

  • 未知(Unknow):apiserver无法正常获取到pod对象的状态信息,通常由网络通讯失败所导致;

4.3.1 Pod创建

Pod的创建过程

  1. 用户通过kubectl或api客户端提交需要创建Pod的信息给apiServer;

  2. apiServer开始生成pod对象信息,并将信息存入etcd,然后返回确认信息至客户端;

  3. apiServer开始反应etcd中pod对象的变化,其它组件使用watch机制来跟踪检查apiServer的变化;

  4. scheduler发现有新的pod对象要创建,开始为pod分配主机并将结果信息更新至apiServer;

  5. node节点上的kubelet发现有pod调度过来,尝试调用docker启动容器,并将结果返回至apiServer;

  6. apiServer将收到的pod状态存入etcd;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BQdg28Dl-1680596600519)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\pod-create.png?msec=1680573118488)]

4.3.2 初始化容器

初始化容器是在pod的主容器启动之前要运行的容器,主要做一些容器的前置工作。

两大特征

  1. 初始化容器必须运行完成直结束,若某初始化容器运行失败,那么kubernetes需要重启它直至成功完成;

  2. 初始化容器必须按照定义的顺序执行,当且仅当一个成功之后,后面的一个才能运行;

应用场景

  • 提供主容器镜像中不具备的工具程序或自定义代码;

  • 初始化容器要先于应用容器串行启动并完成,因此可用于延后应用程序的启动直至其依赖的条件得到满足;

示例

假设要以容器来运行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;']

4.3.3 运行主容器

钩子函数

Kubernetes在主容器创建之后和容器停止之前提供了两个钩子函数:

  1. postStart: 容器创建之后执行,如果失败会重启容器;

  2. preStop: 容器终止之前执行,执行完成之后容器会成功终止,在其完成之前会阻塞删除容器的操作;

钩子函数三种处理方式

  • exec命令: 在容器内执行一次命令
---
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"]
  • tcpSoket: 在当前容器尝试访问指定的socket
lifecycle:
  postStart:
    tcpSocket:
      port: 8080

说明:在容器启动之后连接8080端口

  • httpGet: 在容器启动之后,向url发起http请求
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: <integer> # 容器启动后多少秒执行第一次探测
          timeoutSeconds: <integer> # 探测超时时间。默认1s, 最小1s
          periodSeconds: <integer> # 执行探测的频率,默认10s, 最小1s
          failureThreshold: <integer> # 连续探测多少次认为失败,默认3次,最少1次
          successThreshold: <integer> # 连续探测多少次认为成功,默认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有三种重启策略:

    三种重启策略

    1. Always: 容器失效时自动重启,默认值;

    2. OnFailure: 容器终止运行且退出码不为0时重启

    3. Never: 无论状态如何,从不重启

    重启策略适用于pod对象中的所有容器,首次需要启动的容器,将在其需要重启时立即重启,随后需要重启的操作将由kubernetes延时一段时间后执行,且反复重启的延长时间以次10s,20s,40s,80s,160s和300s递增。

    containers:
      - name: nginx
        image: nginx:1.18.0
    restartPolicy: Never
    

4.3.4 Pod终止

Pod的终止过程

  1. 用户向apiServer发送删除pod的命令;

  2. apiServer中的pod对象信息会随着时间的推移而更新,过了宽限期内(默认30s),pod被视为dead;

  3. 将pod标记为terminating状态;

  4. kubelet在监听到pod对象转化为terminating状态的同时启动pod关闭过程;

  5. 端点控制器监控到pod对象的关闭行为时将其从所有匹配到此端点的service资源的端点列表中移除;

  6. 如果当前pod对象定义了preStop钩子处理器,则在其标记为terminating后即会以同步的方式启动执行;

  7. pod对象中的容器进程收到停止信号;

  8. 宽限期结束后,若pod中还存在仍在运行的进程,那么pod对象会收到立即终止的信号;

  9. Kubelet请求apiServer将此pod的资源宽限期设置为0从而完成删除操作,此时pod对于用户已经不可见;

4.3.5 批量删除

批量删除dev下Evicted的Pod

kubectl get pods -n dev | grep Evicted | awk '{print$1}' | xargs kubectl delete pods -n dev

4.4 Pod的调度

在默认情况下,Pod在哪个节点上运行是由Scheduler组件采用相关算法计算出来的,。但在实际情况下,我们想控制Pod运行在某个节点上,这就需要pod的调度机制。

四大类调度方法

  1. 自动调度:由Scheduler计算得出(默认)

  2. 定向调度:NodeName, NodeSelecor决定

  3. 亲和性调度:NodeAffinity, PodAffinity, PodAntiAffinity

  4. 污点(容忍)调度:Taints, Toleration

4.4.1 定向调度

利用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调度到目标节点,该匹配规则是强制约束。

  1. 给node添加标签
kubectl label nodes node4 "name=node4"
kubectl label nodes node5 "name=node5"
  1. 指定nodeSelector创建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
  nodeSelector:
    name: node4

4.4.2 亲和性调度

亲和性调度优先选择满足条件的node进行调度,如果没有,也可以调度到不满足的节点上,是调度更加灵活。

亲和度Affinity主要分为三类:

  1. nodeAffinity(node亲和性): 以node为目标,解决pod可以调度到哪些node;

  2. podAffinity(pod亲和性): 以pod为目标,解决pod可以和哪些已知pod部署在同一个拓扑域中的问题;

  3. 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规则设置的注意事项

  1. 如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都得到满足,pod才能够运行在指定的node上;

  2. 如果nodeAffinity指定了多个nodeSelector,那么只需要其中一个能够匹配成功即可;

  3. 如果一个nodeSelectorTerms中有多个matchExpressions,则一个节点必须满足所有的才能匹配成功;

  4. 如果一个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

4.4.3 污点和容忍

污点(Taints)

前面的调度方式都是站在pod角度上,通过在pod上添加属性,来确定pod是否要调度指定的node上。其实也可以站在node角度上,通过在node上添加污点属性,来决定是否运行pod调度过来。

node上添加污点之后就和pod存在一种互斥关系,进而拒绝pod调度进来,甚至可以将已经存在的pod驱逐出去。

污点的格式:key=value:effect,key和value是污点标签,effect描述污点的作用,支持三种选项:

  1. PreferNoSchedule: 尽量避免把pod调度到具有该污点的node上,除非没有其它节点可调度;

  2. NoSchedule: 不会把pod调度到该污点的node上,但不会影响当前node上已存在的pod;

  3. 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上停留的时间

4.5 Label

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

4.6 Pod控制器

在Kubernetes中,Pod是最小的控制单元,但Kubernetes很少直接控制Pod,一般通过Pod控制器完成。Pod控制器用于管理Pod,确保Pod资源符合预期的状态,当Pod的资源出现故障的时候,会尝试进行重启或重建Pod。Deployment是Kubernetes众多控制器中的一种。

在Kubernetes中,按照pod的创建方式可以将其分为两类:

  1. 自助式pod:直接创建出来的pod,这种pod删除之后就没有了,不会重建;

  2. 控制器创建pod:通过控制器创建pod,这种pod删除之后还会自动重建;

控制器的类型:

  • ReplicationController: 比较原始的pod控制器,已经废弃,被ReplicaSet代替;

  • ReplicaSet: 保证指定数据量的pod运行,并支持pod数量变更,镜像版本变更;

  • Deployment: 通过控制ReplicaSet来控制pod,并支持滚动升级,版本回退;

  • Horizontal Pod Autoscaler: 可以根据集群负载自动调整pod数量,实现削峰填谷;

  • DaemonSet: 在集群中的指定Node上都运行一个副本,一般用于守护进程类的任务;

  • Job: 它创建出来的pod只要完成任务就立即退出,用于执行一次性任务;

  • Cronjob: 它创建出来的pod会周期性执行,用于执行周期性的任务;

  • StatefulSet: 关联有状态应用;

4.6.1 ReplicaSet(RS)

保证指定数据量的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

镜像升级

  1. 依然使用编辑命令

  2. 使用命令修改

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对象删除。

4.6.2 Deployment(deploy)

为了更好的解决服务编排问题,在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

4.6.3 Horizonal Pod Autoscalar(HPA)

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)测试

4.6.4 DaemonSet

DaemonSet类型的控制器可以保证集群中每一台(或指定)节点上都运行一个副本,一般用于日志收集,节点监控。

4.6.5 Job

TODO

4.6.6 CronJob

TODO

5 Namespace

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

7 Service

7.1 Service介绍

通过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 InActConn

TCP 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

7.2 Service类型

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: 把集群外部的服务引入集群内部,直接使用;

7.2.1 ClusterIP类型

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选项;

7.2.2 HeadLiness类型

在某些场景,开发人员不想使用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

7.2.3 NodePort类型

如果希望将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

7.2.4 LoadBalancer类型

LoadBalancer类型和NodePort类型相似,目的都是向外部暴露一个端口区别于LoadBalancer会在集群外部再来做一个负载均衡,而这个需要外部支持,外部服务发送到这个外部负载的请求,会被转发到集群中去。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T9CMGkeB-1680596600524)(file://E:\OneDrive\ebook\kubernetes\Kubernetes-20230301\service-loadbalancer.png?msec=1680573118602)]

7.2.5 ExternalName类型

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

8 Ingress介绍

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)]

8.1 Ingress使用

8.1.1 环境准备

搭建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

8.1.2 创建ingress

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的端口

9. 数据存储

为了持久化容器数据,kubernetes引入了volume的概念。

Volume是Pod中能够被多个容器共享访问的目录,它定义在Pod之上,然后被一个Pod里的多个容器挂载到具体的目录下,Kubernetes通过Volume实现同一个Pod中不同容器之间共享数据以及数据持久化存储。

Volume的生命周期不与Pod中单个容器的生命周期相关,当容器终止或启动时,Volume中的数据也不会丢失。

Kubernetes的Volume支持多种类型:
  • 基本存储:EmptyDir, HostPath, NFS

  • 高级存储:PV, PVC

  • 配置存储:ConfigMap, Secret

9.1 基本存储

9.1.1 EmptyDir

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

9.1.2 HostPath

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

9.1.3 NFS

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 # 共享文件路径

9.2 高级存储

由于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用户维护

9.2.1 pv

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

9.2.2 pvc

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

9.3 配置存储

9.3.1 ConfigMap

TODO

9.3.2 Secret

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

10. 安全认证

对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)]

10.1 认证管理

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运行同时配置多种认证方式,只要其中任意一个方式认证通过即可

10.2 授权管理

授权发生在认证之后,通过认证就可以知道访问得用户是谁,然后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

10.3 准入控制

通过前面的认证和授权之后,还需要经过准入控制通过之后,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和可用的

11. Helm

Helm是kubernetes一个包管理工具
# 官网
https://helm.sh/
# 下载
https://github.com/helm/helm/releases/tag/v3.1.2

11.1 安装helm

解压 helm

tar -zxvf helm-v3.1.2-linux-amd64.tar.gz
mv linux-amd64 helm-3.1.2

11.2 Helm命令

  • helm version

  • helm repolist 查看helm仓库

  • helm help

11. Dashboard

Dashboard是kubernetes基于Web界面部署容器化应用,监控应用状态,执行故障排查以及管理Kubernetes的各种资源。

11.1 部署Dashboard

下载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

12. 其它

12.1 MyWeb完整示例

# 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

12.2 MyApp完整示例

# 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
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值