云原生系列3-Kubernetes
k8s缩写是因为k和s之间有八个字符。k8s是基于容器技术的分布式架构方案。官网:https://kubernetes.io/zh-cn/Google在 2014年开源了Kubernetes项目,Kubernetes是一个用于自动化部署、扩展和管理容器化应用程序的开源系统。同样类似的容器编排工具还有docker swarm等,但kubernetes应用最为广泛,社区更为活跃。为什么要使用 Kube
1、Kubernetes概述
k8s缩写是因为k和s之间有八个字符。k8s是基于容器技术的分布式架构方案。官网:https://kubernetes.io/zh-cn/
Google在 2014年开源了Kubernetes项目,Kubernetes是一个用于自动化部署、扩展和管理容器化应用程序的开源系统。同样类似的容器编排工具还有docker swarm等,但kubernetes应用最为广泛,社区更为活跃。
为什么要使用 Kubernetes?
当你的应用只是跑在一台机器,直接一个 docker + docker compose 就够了,方便轻松;
当你的应用需要跑在 3,4 台机器上,你依旧可以每台机器单独配置运行环境 + 负载均衡;
当你应用访问数不断增加,机器逐渐增加到十几台、上百台、上千台时,每次加机器、软件更新、版本回滚,都会变得非常麻烦。单机走向集群已成为必然,这时Kubernetes 就可以一展身手了。
Kubernetes是一个开放的开发平台。没有限定任何编程接口,因此不管是Java、Go、C++仍是用Python编写的服务,均可以毫无困难地映射为Kubernetes的Service,并经过标准的TCP协议进行交互。此外,因为Kubernetes平台对现有的编程语言、编程框架、中间件没有任何侵入性,所以现有的系统很容易改造升级并迁移到Kubernetes平台上。
项目部署的发展历程,经历了物理机、虚拟机、容器化三个阶段的演变。
1、传统部署时代
早期,应用程序直接在物理服务器上运行,无法为物理服务器中的应用程序定义资源边界,这会导致资源分配问题。例如,如果在物理服务器上运行多个应用程序,则可能会出现一个应用程序占用大部分资源的情况,结果可能导致其他应用程序的性能下降。一种解决方案是在不同的物理服务器上运行每个应用程序,但是由于资源利用不足而无法扩展,并且维护许多物理服务器的成本很高。
2、虚拟化部署时代
为了解决物理机存在的弊端,引入了虚拟化技术,支持在单个物理服务器的CPU上运行多个虚拟机(VM),每个VM是一台完整的计算机,在虚拟化硬件之上运行所有组件,包括其自己的操作系统。虚拟化技术允许应用程序在VM之间隔离,提供一定程度的安全,一个应用程序的信息不能被另一应用程序随意访问。虚拟化技术能够更好地利用物理服务器上的资源,由于可轻松地添加或更新应用程序而实现更好的可伸缩性,降低了硬件成本。
3、容器部署时代
容器类似于VM,但是具有被放宽的隔离属性,可以在应用程序之间共享操作系统,因此容器被认为是轻量级的隔离。容器与VM类似,具有自己的文件系统、CPU、内存、进程空间等,由于它们与基础架构分离,因此可以跨云和操作系统发行版本进行移植。
随着微服务、容器化等技术的发展,解决了资源利用率不高的问题,但是随之而来却是如何进行容器管理,过多的容器使得运维工作也成为了一种负担,因此容器编排对于大型依托于容器化部署的分布式系统至关重要。在容器编排方面,Kubernetes所扮演的角色如下图所示:
Kubernetes相当于在容器之上,对服务器资源以及容器调度等过程进行管理。
Kubernetes设计架构,官方架构设计图:
1、用户通过kubectl向api-server提交需要运行的pod描述。
2、api-server接收请求并将相关描述存储到etcd。
3、scheduler 监控api-server拿到相关描述信息,开始扫描node节点,找到满足条件的node节点,将Pod与node的对应关系写入etcd
4、Kubelet定时请求api-server,拿到需要在本机运行的Pod信息,运行起来。
5、用户提交service描述,由kube-proxy负责具体的工作流量转发。
Node是Pod真正运行的主机,可以物理机,也可以是虚拟机。为了管理Pod,每个Node节点上至少要运行container runtime(比如docker或者rkt)、kubelet和kube-proxy服务。
Namespace是对一组资源和对象的抽象集合,比如可以用来将系统内部的对象划分为不同的项目组或用户组。
Container容器通过image启动,并且与同一台机器上的其他容器隔离。
Pod是在K8s集群中运行部署应用或服务的最小单元,Pod支持多个容器在一个Pod中共享网络地址和文件系统。
1、kube-apiserver(master节点):通过 API与k8s集群进行交互,负责接收用户请求并响应给用户。Kubernetes API 是 Kubernetes 控制平面的前端,用于处理内部和外部请求。API 服务器会确定请求是否有效,如果有效,则对其进行处理。可以通过 REST 调用、kubectl 命令行界面或其他命令行工具(例如 kubeadm)来访问 API。
2、kube-scheduler(master节点):集群调度器,负责资源的调度,调度器按照预定的调度策略将Pod调度到相应的节点。调度程序会考虑容器集的资源需求(例如 CPU 或内存)以及集群的运行状况。它会将容器集安排到适当的计算节点。
3、kube-controller-manager(master节点):集群控制器,它运行着所有处理集群日常任务的控制器。包括了节点控制器、副本控制器、端点(endpoint)控制器以及服务账户和令牌控制器。每一个控制器都独立工作以维护其所需的状态。简单讲,就是负责维护集群的状态,比如故障检测、自动扩展、滚动更新等。
4、etcd:配置数据以及有关集群状态的信息位于 etcd(一个键值key-value存储数据库)中。kubernetes集群的数据库,用于保存kubernetes集群的所有数据信息。
5、容器集Pod:容器集是 Kubernetes 对象模型中最小、最简单的单元。它代表了应用的单个实例。每个容器集都由一个容器(或一系列紧密耦合的容器)以及若干控制容器运行方式的选件组成。容器集可以连接至持久存储,以运行有状态应用。
6、kubelet(node节点):每个计算节点中都包含一个 kubelet,这是一个与控制平面通信的微型应用。当控制平面需要在节点中执行某个操作时,kubelet 就会执行该操作。接收控制面的指令,在工作节点上调用容器运行时(containerd)创建容器。
7、kube-proxy(node节点):每个计算节点中还包含 kube-proxy,这是一个用于优化 Kubernetes 网络服务的网络代理。kube-proxy 负责处理集群内部或外部的网络通信。负责为pod提供代理。它会定期从apiserver获取所有的service,并根据service信息创建代理。当某个客户pod要访问其他pod时,访问请求会经过本机的proxy做转发。同时负责为Service提供cluster内部的服务发现和负载均衡。
Minikube安装
minikube基于go语言开发,可以在单机环境下快速搭建可用的k8s集群,快速启动消耗机器资源较少,非常适合测试和本地开发,现有的大部分在线k8s实验环境也是基于minikube的。
Minikube架构,master 节点与其它节点合为一体,而整体则通过宿主机上的 kubectl 进行管理,这样可以更加节省资源。
Minikube 支持 Windows、macOS、Linux 三种 OS,会根据平台不同,下载对应的虚拟机镜像,并在镜像内安装 K8S。
这里直接使用 Windows 安装minikube。
1、docker安装
1、下载地址:https://docs.docker.com/desktop/install/windows-install/
2、双击下载的 Docker for Windows Installer 安装文件,一路 Next,点击 Finish 完成安装。
3、安装好之后会重启电脑
2、安装minikube
1、minikube下载地址 https://minikube.sigs.k8s.io/docs/start/ ,下载 minikube.exe双击运行安装,
https://github.com/kubernetes/minikube/releases/latest/download/minikube-windows-amd64.exe
2、配置系统环境变量path,添加minikube.exe到path中,C:\minikube
3、命令行测试,需要保证docker启动
启动minikube,minikube start,minikube version,minikube status,minikube ip
4、进入minikube,docker环境,minikube ssh,docker ps
5、k8s命令kubectl,kubectl get nodes,kubectl get pods -A
6、停止minikube,minikube stop
2、裸机手动搭建K8S集群
1、三台VM一主二从,每台4颗cpu内存4G,NAT模式,10.1.1.111(master),10.1.1.112(node1),10.1.1.113(node2)
2、三台VM都要先安装docker
卸载旧的docker
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
配置yum源、下载docker-ce.repo
yum install -y yum-utils
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
安装docker
yum install -y docker-ce docker-ce-cli containerd.io
配置阿里云镜像加速器
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://qiyb9988.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
启动Docker
sudo systemctl start docker
设置开机自启动Docker
sudo systemctl enable docker
查看版本
docker version
3、安装k8s前的系统环境准备,官方要求的
每台节点设置不同的hostname
hostnamectl set-hostname masternew
hostnamectl set-hostname node1new
hostnamectl set-hostname node2new
关闭防火墙,允许节点之间通信
systemctl stop firewalld
systemctl disable firewalld
禁用selinux
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config
关闭swap分区
sudo swapoff -a
sudo sed -ri 's/.*swap.*/#&/' /etc/fstab
允许 iptables 检查桥接流量(所有节点)
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
重新加载配置文件
sudo sysctl --system
4、安装k8s集群三大组件 kubeadm、kubelet、kubectl
需要在每台机器上安装以下的软件包:
1、kubeadm:用来初始化集群的指令。
2、kubelet:在集群中的每个节点上用来启动 Pod 和容器等。
3、kubectl:用来与集群通信的命令行工具。
参考阿里巴巴开源镜像站Kubernetes安装步骤:https://developer.aliyun.com/mirror/kubernetes
# 安装 kubeadm、kubelet 和 kubectl,配置yum文件,因为国内无法直接访问google,这里需要将官网中的google的源改为国内源
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
setenforce 0
yum install -y kubelet kubeadm kubectl
systemctl enable kubelet && systemctl start kubelet
# 查看版本信息
kubectl version
5、安装cri环境(要先确保docker是启动的)
在 Kubernetes v1.24 及更早版本中,我们使用docker作为容器引擎在k8s上使用时,依赖一个dockershim的内置k8s组件;k8s v1.24发行版中将dockershim组件给移除了;取而代之的就是cri-dockerd(当然还有其它容器接口);简单讲CRI就是容器运行时接口(Container Runtime Interface,CRI),也就是说cri-dockerd就是以docker作为容器引擎而提供的容器运行时接口;即我们想要用docker作为k8s的容器运行引擎,我们需要先部署好cri-dockerd;用cri-dockerd来与kubelet交互,然后再由cri-dockerd和docker api交互,使我们在k8s能够正常使用docker作为容器引擎;
# 下载cri-docker
wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.3.4/cri-dockerd-0.3.4.amd64.tgz
# 解压cri-docker
tar xvf cri-dockerd-*.amd64.tgz
cp -r cri-dockerd/ /usr/bin/
chmod +x /usr/bin/cri-dockerd/cri-dockerd
# 写入启动cri-docker配置文件
cat > /usr/lib/systemd/system/cri-docker.service <<EOF
[Unit]
Description=CRI Interface for Docker Application Container Engine
Documentation=https://docs.mirantis.com
After=network-online.target firewalld.service docker.service
Wants=network-online.target
Requires=cri-docker.socket
[Service]
Type=notify
ExecStart=/usr/bin/cri-dockerd/cri-dockerd --network-plugin=cni --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.7
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always
StartLimitBurst=3
StartLimitInterval=60s
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
Delegate=yes
KillMode=process
[Install]
WantedBy=multi-user.target
EOF
# 写入cri-docker的socket配置文件
cat > /usr/lib/systemd/system/cri-docker.socket <<EOF
[Unit]
Description=CRI Docker Socket for the API
PartOf=cri-docker.service
[Socket]
ListenStream=%t/cri-dockerd.sock
SocketMode=0660
SocketUser=root
SocketGroup=docker
[Install]
WantedBy=sockets.target
EOF
# 重新加载配置文件(如.service文件、socket文件等)
systemctl daemon-reload
# 启用并立即启动cri-docker.service
systemctl enable --now cri-docker.service
# 查看cri-docker.service运行状态
systemctl status cri-docker.service
6、安装并初始化master节点
# 所有节点添加master节点的域名映射
echo "10.1.1.111 masternew" >> /etc/hosts
# node节点ping测试映射是否成功
ping masternew
# 如果init初始化master失败,可以kubeadm重置,kubeadm:用来初始化集群的指令。
kubeadm reset --cri-socket unix:///var/run/cri-dockerd.sock
# 只有主节点master初始化(只在master执行)
# 注意修改apiserver的地址为master节点的ip
# 注意service-cidr、pod-network-cidr的网络节点不能和master网络ip重叠(service网络、pod网络不要和宿主机一样)
kubeadm init \
--apiserver-advertise-address=10.1.1.111 \
--control-plane-endpoint=masternew \
--image-repository registry.cn-hangzhou.aliyuncs.com/google_containers \
--kubernetes-version v1.28.2 \
--service-cidr=10.96.0.0/16 \
--pod-network-cidr=192.169.0.0/16 \
--cri-socket unix:///var/run/cri-dockerd.sock
等待命令运行完毕即可,执行成功后可以看到会返回一些命令,按照提示执行这些命令即可。
Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
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/
You can now join any number of control-plane nodes by copying certificate authorities
and service account keys on each node and then running the following as root:
kubeadm join masternew:6443 --token 7izf2l.1ssrdhqux2vyradb \
--discovery-token-ca-cert-hash sha256:c6995cbd84c701ef6ba2eff481d43b7ad3c2c1e49387a9c6f7bdc36c97ad590c \
--control-plane # control-plane控制节点,这是加入控制节点命令
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join masternew:6443 --token 7izf2l.1ssrdhqux2vyradb \
--discovery-token-ca-cert-hash sha256:c6995cbd84c701ef6ba2eff481d43b7ad3c2c1e49387a9c6f7bdc36c97ad590c
[root@masternew ~]# mkdir -p $HOME/.kube
[root@masternew ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@masternew ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config
[root@masternew ~]# export KUBECONFIG=/etc/kubernetes/admin.conf
初始化完成后,可以使用命令查看节点信息了,发现是 NotReady 状态
[root@masternew ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
masternew NotReady control-plane 97s v1.28.2
7、工作节点加入k8s集群,加入节点命令是在初始化master完成后给出的,在每个工作节点执行。
[root@node1new ~]# kubeadm join masternew:6443 --token 7izf2l.1ssrdhqux2vyradb \
> --discovery-token-ca-cert-hash sha256:c6995cbd84c701ef6ba2eff481d43b7ad3c2c1e49387a9c6f7bdc36c97ad590c
Found multiple CRI endpoints on the host. Please define which one do you wish to use by setting the 'criSocket' field in the kubeadm configuration file: unix:///var/run/containerd/containerd.sock, unix:///var/run/cri-dockerd.sock
To see the stack trace of this error execute with --v=5 or higher
如果由于当前版本不再默认支持docker,如果服务器使用的docker,
需要在命令后面加入参数--cri-socket unix:///var/run/cri-dockerd.sock
kubeadm join masternew:6443 --token 7izf2l.1ssrdhqux2vyradb \
--discovery-token-ca-cert-hash sha256:c6995cbd84c701ef6ba2eff481d43b7ad3c2c1e49387a9c6f7bdc36c97ad590c \
--cri-socket unix:///var/run/cri-dockerd.sock
[root@node1new ~]# kubeadm join masternew:6443 --token 7izf2l.1ssrdhqux2vyradb \
> --discovery-token-ca-cert-hash sha256:c6995cbd84c701ef6ba2eff481d43b7ad3c2c1e49387a9c6f7bdc36c97ad590c \
> --cri-socket unix:///var/run/cri-dockerd.sock
[preflight] Running pre-flight checks
[WARNING Hostname]: hostname "node1new" could not be reached
[WARNING Hostname]: hostname "node1new": lookup node1new on 8.8.8.8:53: no such host
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
#token默认的有效期为24小时,过期之后就不能用了,需要重新创建token,操作如下
kubeadm token create --print-join-command
# 另外,短时间内生成多个token时,生成新token后建议删除前一个旧的
#查看token命令
kubeadm token list
# 删除token命令
kubeadm token delete tokenid
[root@masternew ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
masternew NotReady control-plane 17m v1.28.2
node1new NotReady <none> 58s v1.28.2
node2new NotReady <none> 48s v1.28.2
8、部署 calico
安装pod网络插件是pod之间进行通信的必要条件,k8s支持众多网络方案,这里选用calico方案。
calico历史版本地址 https://docs.tigera.io/archive#v3.1.7
# 下载calico.yaml
curl https://docs.tigera.io/archive/v3.25/manifests/calico.yaml -O
在初始化master时配置的–pod-network-cidr=192.169.0.0/16,编辑calico.yaml配置文件,这里CALICO_IPV4POOL_CIDR也要改成192.169.0.0/16
calico 默认会找 eth0网卡,如果当前机器网卡不是这个名字会无法启动,需要手动配置下网卡名。
我这里是 ens33,编辑calico.yaml配置文件加入如下内容 ,在 CLUSTER_TYPE 同级目录下
- name: IP_AUTODETECTION_METHOD
value: "interface=ens33"
kubectl apply -f calico.yaml
kubectl apply 应用后需要等待一段时间节点状态才会变为Ready,主机内存cpu越少等待时间越久。
[root@masternew ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
masternew Ready control-plane 61m v1.28.2
node1new Ready <none> 45m v1.28.2
node2new Ready <none> 45m v1.28.2
查看所有的pod,要等一段时间这里状态才会变为Running
默认查询当前命名空间下对象,-A查询所有命名空间下对象,这里查询的对象是pod
[root@masternew ~]# kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system calico-kube-controllers-658d97c59c-6f5r4 0/1 ContainerCreating 0 22m
kube-system calico-node-hhxdx 0/1 PodInitializing 0 22m
kube-system calico-node-mvjtr 0/1 Init:2/3 0 22m
kube-system calico-node-rcgvh 0/1 Init:2/3 0 22m
kube-system coredns-6554b8b87f-4p8zd 0/1 ContainerCreating 0 61m
kube-system coredns-6554b8b87f-xrv4h 0/1 ContainerCreating 0 61m
kube-system etcd-masternew 1/1 Running 0 61m
kube-system kube-apiserver-masternew 1/1 Running 0 61m
kube-system kube-controller-manager-masternew 0/1 Running 6 (4m16s ago) 61m
kube-system kube-proxy-djnzr 1/1 Running 0 45m
kube-system kube-proxy-dz7wh 1/1 Running 0 61m
kube-system kube-proxy-nx8zq 1/1 Running 0 45m
kube-system kube-scheduler-masternew 0/1 Running 6 (2m1s ago) 61m
[root@masternew ~]# kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system calico-kube-controllers-658d97c59c-6f5r4 1/1 Running 0 26m
kube-system calico-node-hhxdx 1/1 Running 1 (60s ago) 26m
kube-system calico-node-mvjtr 1/1 Running 0 26m
kube-system calico-node-rcgvh 1/1 Running 0 26m
kube-system coredns-6554b8b87f-4p8zd 1/1 Running 0 65m
kube-system coredns-6554b8b87f-xrv4h 1/1 Running 0 65m
kube-system etcd-masternew 1/1 Running 0 65m
kube-system kube-apiserver-masternew 1/1 Running 0 65m
kube-system kube-controller-manager-masternew 0/1 Running 7 (3m33s ago) 65m
kube-system kube-proxy-djnzr 1/1 Running 0 49m
kube-system kube-proxy-dz7wh 1/1 Running 0 65m
kube-system kube-proxy-nx8zq 1/1 Running 0 49m
kube-system kube-scheduler-masternew 1/1 Running 7 (3m2s ago) 65m
测试K8S的自愈能力,将一个工作节点关机或重启,查看工作节点状态变为NotReady
节点重启成功后,又会自动加入到集群中,再次查看工作节点状态变为Ready。
[root@masternew ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
masternew Ready control-plane 76m v1.28.2
node1new NotReady <none> 60m v1.28.2
node2new Ready <none> 60m v1.28.2
[root@masternew ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
masternew Ready control-plane 79m v1.28.2
node1new Ready <none> 63m v1.28.2
node2new Ready <none> 63m v1.28.2
3、Dashboard
Dashboard 是Kubernetes官方提供的可视化控制面板
项目地址:https://github.com/kubernetes/dashboard
wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml
下载失败的话,就网页访问,然后将网页内容复制粘贴,文件名可自定义。
kubectl apply -f recommended.yaml
默认查询当前命名空间下对象,-A查询所有命名空间下对象,这里查询的对象是svc服务service
[root@masternew ~]# kubectl get svc -A
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 112m
kube-system kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 112m
kubernetes-dashboard dashboard-metrics-scraper ClusterIP 10.96.149.225 <none> 8000/TCP 6m54s
kubernetes-dashboard kubernetes-dashboard ClusterIP 10.96.159.43 <none> 443/TCP 6m54s
官方下载的要修改才能正常使用,将type:ClusterIP改为type:NodePort
kubectl edit 在线修改,将type:ClusterIP改为type:NodePort,svc指服务service,-n指定命名空间
kubectl edit svc kubernetes-dashboard -n kubernetes-dashboard
[root@masternew ~]# kubectl get svc -A
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 119m
kube-system kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 119m
kubernetes-dashboard dashboard-metrics-scraper ClusterIP 10.96.149.225 <none> 8000/TCP 13m
kubernetes-dashboard kubernetes-dashboard NodePort 10.96.159.43 <none> 443:31260/TCP 13m
集群任意节点都可以访问,注意是https协议,https://集群任意节点ip:31260/ , https://10.1.1.111:31260/
登录需要Token,创建一个Token,名字可自定义,vi dash-token.yaml
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: admin
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: admin
namespace: kubernetes-dashboard
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin
namespace: kubernetes-dashboard
labels:
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
kubectl apply -f dash-token.yaml
dashboard获取登录令牌信息
kubectl create token admin -n kubernetes-dashboard
执行应用这个文件,获取令牌,成功登入。
4、NameSpace
命名空间用来隔离资源,是一种标识机制,不会隔离网络。不同的业务(web、数据库、消息中间件)可以部署在不同的命名空间,实现业务的隔离,并且可以对其进行资源配额,限制cpu、内存等资源的使用。
Namespace 适合用于隔离不同用户创建的资源,每一个添加到Kubernetes集群的工作负载必须放在一个命名空间中,不指定namespace默认都在default下面。
Kubernetes 启动时会创建4个初始命名空间:
1、default:Kubernetes 默认命名空间。
2、kube-system:用于 Kubernetes 系统创建的对象。
3、kube-node-lease:该名字空间包含用于与各个节点关联的 Lease(租约)对象。 节点租约允许 kubelet 发送心跳, 由此控制面能够检测到节点故障。
4、kube-public:所有的客户端(包括未经身份验证的客户端)都可以读取该名字空间。 该名字空间主要预留为集群使用,以便某些资源需要在整个集群中可见可读。
获取所有的ns,kubectl get ns
获取指定命名空间下的pod,kubectl get pod -n kube-system
如果不指定命名空间 指默认命名空间default,kubectl get pod
创建命名空间,kubectl create ns mpp
删除命名空间,假如这个ns里有服务资源也会被删除,kubectl delete ns mpp
Kubernetes资源创建的两种方式:命令行、yaml
[root@masternew ~]# cat mpp-ns.yaml
apiVersion: v1
kind: Namespace
metadata:
name: hello-mpp
[root@masternew ~]# kubectl apply -f mpp-ns.yaml
[root@masternew ~]# kubectl delete -f mpp-ns.yaml
使用create创建资源是一次性的,如果使用apply创建,后期修改yaml文件再次执行apply可以实现更新资源
kubectl create -f xxx.yaml
kubectl apply -f xxx.yaml
直接修改资源对应的yaml文件,并用 kubectl apply -f xxx.yaml 更新应用文件使之生效
注意:当apply不生效时,先使用delete清除资源,再使用apply创建资源
删除yaml文件指定的资源,kubectl delete -f xxx.yaml
查看资源的yaml格式信息,kubectl get 资源类型 资源名称 -o yaml
[root@masternew ~]# kubectl get ns hello-mpp -o yaml
apiVersion: v1
kind: Namespace
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Namespace","metadata":{"annotations":{},"name":"hello-mpp"}}
creationTimestamp: "2023-12-22T07:39:39Z"
labels:
kubernetes.io/metadata.name: hello-mpp
name: hello-mpp
resourceVersion: "44954"
uid: d0422a9b-732b-4022-ace5-6b3144cf40c2
spec:
finalizers:
- kubernetes
status:
phase: Active
[root@masternew ~]# kubectl get ns kubernetes-dashboard -o yaml
apiVersion: v1
kind: Namespace
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Namespace","metadata":{"annotations":{},"name":"kubernetes-dashboard"}}
creationTimestamp: "2023-12-21T10:54:25Z"
labels:
kubernetes.io/metadata.name: kubernetes-dashboard
name: kubernetes-dashboard
resourceVersion: "8620"
uid: f64037a4-3713-40ec-ba7f-17807bbebd25
spec:
finalizers:
- kubernetes
status:
phase: Active
yaml 格式
apiVersion: api版本标签信息
kind:资源类型
metadata:资源元数据信息
spec: 属性
apiVersion: apps/v1 //指定api版本标签
kind: Deployment //定义资源的类型/角色,Deployment为副本控制器,此处资源类型可以是Deployment、Job、Ingress、Service等
metadata: //定义资源的元数据信息,比如资源的名称、namespace、标签等信息
name: nginx-deployment //定义资源的名称,在同一个namespace空间中必须是唯一的
namespace: kube-public //定义资源所在命名空间
labels: //定义资源标签
app: nginx
name: test01
spec: //定义资源需要的参数属性,如是否在容器失败时重新启动容器的属性
replicas: 3 //副本数
selector: //定义标签选择器
matchLabels: //定义匹配标签
app: nginx //需与.spec.template.metadata.labels 定义的标签保持一致
template: //定义业务模板,如果有多个副本,所有副本的属性会按照模板的相关配置进行匹配
metadata:
labels: //定义Pod副本将使用的标签,需与.spec.selector.matchLabels 定义的标签保持一致
app: nginx
spec:
containers: //定义容器属性
- name: nignx //定义一个容器名,一个 - name: 定义一个容器
image: nginx:1.21 //定义容器使用的镜像以及版本
ports:
- name: http
containerPort: 80 定义容器的对外的端口
- name: https
containerPort: 443
5、Pod
Pod:运行中的一组容器,是 Kubernetes 中应用的最小单位,也是在 k8s 上运行容器化应用的资源对象,其他的资源对象都是用来支撑或者扩展 Pod 对象功能的。
k8s 不会直接处理容器,而是 Pod。Pod是多进程设计,运用多个应用程序,也就是一个Pod里面有多个容器,而一个容器里面运行一个应用程序。
创建pod默认在default名称空间下,kubectl run nginx01 --image=nginx
删除pod默认在default名称空间下,kubectl delete pod nginx01
kubectl describe 查询对象详细信息
kubectl describe pod nginx01
kubectl logs 查看对象日志
kubectl logs nginx01
kubectl get 查询对象,-o wide 对象具体信息,-A 所有ns的全部对象,-n 指定命名空间对象
kubectl get pod -o wide 查询默认命名空间的pod对象的具体信息
[root@masternew ~]# cat nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx02
labels:
run: nginx02
spec:
containers:
- image: nginx
name: nginx02
[root@masternew ~]# kubectl apply -f nginx-pod.yaml
[root@masternew ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx01 1/1 Running 0 20m
nginx02 1/1 Running 0 103s
pod是k8s最小资源单位,在k8s中每一个pod都会分配一个ip,一个pod中有多个容器,一个pod可运行多个应用
[root@masternew ~]# kubectl get pod -o wide -o wide 查看详细信息
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx01 1/1 Running 0 22m 192.169.115.67 node2new <none> <none>
nginx02 1/1 Running 0 3m26s 192.169.66.3 node1new <none> <none>
通过ip+端口来访问pod中的容器服务,curl 192.169.66.3
[root@masternew ~]# kubectl exec -it nginx02 -- /bin/bash
root@nginx02:/# cd /usr/share/nginx/html/
root@nginx02:/usr/share/nginx/html# ls
50x.html index.html
root@nginx02:/usr/share/nginx/html# echo hello > index.html
root@nginx02:/usr/share/nginx/html# exit
exit
command terminated with exit code 127
[root@masternew ~]# curl 192.169.66.3
hello
[root@masternew ~]# cat web.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: web
name: web
spec:
containers:
- image: nginx
name: nginx
- image: tomcat:8.5.92
name: tomcat
[root@masternew ~]# kubectl apply -f web.yaml
[root@masternew ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web 2/2 Running 0 3m47s 192.169.66.4 node1new <none> <none>
nginx默认80端口,curl 192.169.66.4
tomcat默认80端口,curl 192.169.66.4:8080
Kubernetes中包含了众多组件,通过watch的机制进行每个组件的协作,每个组件之间的设计实现了解耦,其工作流程如下图所示:
以创建Pod为例:
1、集群管理员或者开发人员通过kubectl或者客户端等构建REST请求,经由apiserver进行鉴权认证(使用kubeconfig文件),验证准入信息后将请求数据(metadata)写入etcd中;
2、ControllerManager(控制器组件)通过watch机制发现创建Pod的信息,并将整合信息通过apiserver写入etcd中,此时Pod处于可调度状态;
3、Scheduler(调度器组件)基于watch机制获取可调度Pod列表信息,通过调度算法(过滤或打分)为待调度Pod选择最适合的节点,并将创建Pod信息写入etcd中,创建请求发送给节点上的kubelet;
4、 kubelet收到Pod创建请求后,调用CNI接口为Pod创建网络环境,调用CRI接口创建Pod内部容器,调用CSI接口对Pod进行存储卷的挂载;
5、等待Pod内部运行环境创建完成,基于探针或者健康检查监测业务运行容器启动状态,启动完成后Pod处于Running状态,Pod进入运行阶段。
6、Deployment
为了更好地解决服务编排的问题,k8s在V1.2版本开始,引入了deployment控制器,这种控制器并不直接管理pod,而是通过管理replicaset来间接管理pod,即:deployment管理replicaset,replicaset管理pod。
k8s的最小单位是pod,通过deployment副本控制器,使 pod 拥有多副本、自愈、扩缩容等能力。
通过 deployment 部署的pod,拥有自愈能力,删除了pod又会重新创建。
[root@masternew ~]# kubectl create deployment tomcat01 --image=tomcat:8.5.92
deployment.apps/tomcat01 created
[root@masternew ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
tomcat01-69bd496b7d-vzcvh 1/1 Running 0 3m45s
[root@masternew ~]# kubectl delete pod tomcat01-69bd496b7d-vzcvh
pod "tomcat01-69bd496b7d-vzcvh" deleted
[root@masternew ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
tomcat01-69bd496b7d-r9nd6 1/1 Running 0 6s
pod是用 deployment 创建的,要删除就要删除 deployment 控制器
[root@masternew ~]# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
tomcat01 1/1 1 1 9m12s
[root@masternew ~]# kubectl delete deploy tomcat01
deployment.apps "tomcat01" deleted
[root@masternew ~]# kubectl get deploy
No resources found in default namespace.
[root@masternew ~]# kubectl get pod
No resources found in default namespace.
deployment 副本数replicas(默认创建一个副本)
--replicas=3 副本数量
[root@masternew ~]# kubectl create deploy nginx --image=nginx --replicas=3
deployment.apps/nginx created
[root@masternew ~]# kubectl get deploy -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
nginx 3/3 3 3 2m45s nginx nginx app=nginx
自动进行分布式部署pod,pod部署在不同节点
[root@masternew ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-7854ff8877-77fvm 1/1 Running 0 4m 192.169.115.72 node2new <none> <none>
nginx-7854ff8877-n824v 1/1 Running 0 4m 192.169.66.13 node1new <none> <none>
nginx-7854ff8877-p6xgs 1/1 Running 0 4m 192.169.115.73 node2new <none> <none>
replicaset标签可以找到这个deployment控制器部署的所有pod,pod里面也有对应的标签,pod和replicaset里面的pod-template-hash是一致的
[root@masternew ~]# kubectl get replicaset --show-labels
NAME DESIRED CURRENT READY AGE LABELS
nginx-7854ff8877 3 3 3 6m45s app=nginx,pod-template-hash=7854ff8877
[root@masternew ~]# kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx-7854ff8877-77fvm 1/1 Running 0 6m58s app=nginx,pod-template-hash=7854ff8877
nginx-7854ff8877-n824v 1/1 Running 0 6m58s app=nginx,pod-template-hash=7854ff8877
nginx-7854ff8877-p6xgs 1/1 Running 0 6m58s app=nginx,pod-template-hash=7854ff8877
使用yaml创建Deployment
apiVersion: apps/v1 # 指定deployment的api版本
kind: Deployment # 指定创建资源的角色/类型
metadata: # 指定Deployment的元数据
name: nginx # 创建名为nginx的Deployment
labels: # 指定Deployment的标签(可自定义多个),这里的标签不需要与任何地方的标签匹配,根据实际场景随意自定义即可
app: demo
spec: # Deployment的资源规格(参数属性)
replicas: 2 # Deployment将创建2个Pod副本(默认为 1)
selector: # 匹配标签选择器,定义Deployment如何查找要管理的Pod,因此这里必须与Pod的template模板中定义的标签保持一致
matchLabels:
app: demo
template: # 指定Pod模板
metadata: # 指定Pod的元数据
labels: # 指定Pod的标签(可自定义多个)
app: demo
spec: # Pod的资源规格(参数属性)
containers: # 指定Pod运行的容器信息
- name: nginx # 指定Pod中运行的容器名
image: nginx:1.20.0 # 指定Pod中运行的容器镜像与版本(不指定镜像版本号则默认为latest)
ports:
- containerPort: 80 # 指定容器的端口(即Nginx默认端口)
k8s 对应的资源api标签信息,如果写的 apiVersion 不存在,k8s无法运行
[root@masternew ~]# kubectl api-versions
admissionregistration.k8s.io/v1
apiextensions.k8s.io/v1
apiregistration.k8s.io/v1
apps/v1
authentication.k8s.io/v1
authorization.k8s.io/v1
autoscaling/v1
autoscaling/v2
batch/v1
certificates.k8s.io/v1
coordination.k8s.io/v1
crd.projectcalico.org/v1
discovery.k8s.io/v1
events.k8s.io/v1
flowcontrol.apiserver.k8s.io/v1beta2
flowcontrol.apiserver.k8s.io/v1beta3
networking.k8s.io/v1
node.k8s.io/v1
policy/v1
rbac.authorization.k8s.io/v1
scheduling.k8s.io/v1
storage.k8s.io/v1
v1
deployment 扩缩容 scale
[root@masternew ~]# kubectl scale deploy nginx --replicas=5
deployment.apps/nginx scaled
[root@masternew ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-7854ff8877-65x56 0/1 ContainerCreating 0 5s
nginx-7854ff8877-77fvm 1/1 Running 0 26m
nginx-7854ff8877-kpzkm 0/1 ContainerCreating 0 5s
nginx-7854ff8877-n824v 1/1 Running 0 26m
nginx-7854ff8877-p6xgs 1/1 Running 0 26m
[root@masternew ~]# kubectl scale deploy nginx --replicas=2
deployment.apps/nginx scaled
[root@masternew ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-7854ff8877-77fvm 1/1 Running 0 28m
nginx-7854ff8877-n824v 1/1 Running 0 28m
Deployment 实现故障转移,如果运行中的一个节点宕机了,k8s会自动在其他节点自动重启拉起pod,保证副本数量可用。等待 5 分钟后,会自动在其他可用 work 节点进行创建并运行pod,之所以要等待5分钟,这是因为 k8s 的 Taint(污点)与 Toleration(容忍)机制所造成。
如果node2活过来了,原来node2的pod还能重新回来吗?不会的,因为2个副本数已经存在了。
滚动升级 set image、回滚 rollout
kubectl get pod -w ,-w查看动态状态 kubectl get pod -o wide, -o wide 查看详细信息
[root@masternew ~]# kubectl get deploy -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
nginx 2/2 2 2 2d15h nginx nginx app=nginx
[root@masternew ~]# kubectl set image deploy nginx nginx=nginx:1.19.2 --record
Flag --record has been deprecated, --record will be removed in the future
deployment.apps/nginx image updated
[root@masternew ~]# kubectl get deploy -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
nginx 2/2 1 2 2d15h nginx nginx:1.19.2 app=nginx
[root@masternew ~]# kubectl set image deploy/nginx nginx=nginx:1.19.10 --record
Flag --record has been deprecated, --record will be removed in the future
deployment.apps/nginx image updated
查看一个deployment的历史部署信息
[root@masternew ~]# kubectl rollout history deploy/nginx
deployment.apps/nginx
REVISION CHANGE-CAUSE
1 <none>
2 kubectl set image deploy nginx nginx=nginx:1.19.2 --record=true
3 kubectl set image deploy/nginx nginx=nginx:1.19.10 --record=true
查看指定deployment部署版本的信息
[root@masternew ~]# kubectl rollout history deployment/nginx --revision=2
deployment.apps/nginx with revision #2
Pod Template:
Labels: app=nginx
pod-template-hash=7d4cc767dd
Annotations: kubernetes.io/change-cause: kubectl set image deploy nginx nginx=nginx:1.19.2 --record=true
Containers:
nginx:
Image: nginx:1.19.2
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
rollout undo 回滚到上一个版本
[root@masternew ~]# kubectl rollout undo deployment/nginx
deployment.apps/nginx rolled back
回滚到指定的历史版本
[root@masternew ~]# kubectl rollout undo deployment/nginx --to-revision=3
deployment.apps/nginx rolled back
7、Service
service:pod的服务发现和负载均衡
部署三个nginx pod,dashboard进入容器,修改三个nginx的index.html
[root@masternew ~]# kubectl create deployment web --image=nginx --replicas=3
cd /usr/share/nginx/html
echo 111 > index.html
echo 222 > index.html
echo 333 > index.html
[root@masternew ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-76fd95c67-qtj9q 1/1 Running 0 5m25s 192.169.115.81 node2new <none> <none>
web-76fd95c67-qxgbp 1/1 Running 0 5m25s 192.169.115.80 node2new <none> <none>
web-76fd95c67-r87dn 1/1 Running 0 5m25s 192.169.66.24 node1new <none> <none>
[root@masternew ~]# curl 192.169.115.81
111
[root@masternew ~]# curl 192.169.115.80
222
[root@masternew ~]# curl 192.169.66.24
333
kubectl expose deploy 暴露服务,默认是ClusterIP类型
[root@masternew ~]# kubectl expose deploy web --port=8000 --target-port=80
service/web exposed
[root@masternew ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 15d
web ClusterIP 10.96.107.73 <none> 8000/TCP 10s
curl访问,查看效果,目前只能在集群内访问
[root@masternew ~]# curl 10.96.107.73:8000
333
[root@masternew ~]# curl 10.96.107.73:8000
111
[root@masternew ~]# curl 10.96.107.73:8000
222
svc域名访问,只能在pod容器内使用,不能在集群内使用,svc的域名格式:svc服务名.namespace名.svc
curl web.default.svc:8000
# 本质是通过标签选择一组服务,统一来做负载均衡
# kubectl get pod --show-labels 查看pod标签
# kubectl get pod -l app=web 查看指定标签的pod
[root@masternew ~]# kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
web-76fd95c67-qtj9q 1/1 Running 0 15m app=web,pod-template-hash=76fd95c67
web-76fd95c67-qxgbp 1/1 Running 0 15m app=web,pod-template-hash=76fd95c67
web-76fd95c67-r87dn 1/1 Running 0 15m app=web,pod-template-hash=76fd95c67
[root@masternew ~]# kubectl get pod -l app=web
NAME READY STATUS RESTARTS AGE
web-76fd95c67-qtj9q 1/1 Running 0 16m
web-76fd95c67-qxgbp 1/1 Running 0 16m
web-76fd95c67-r87dn 1/1 Running 0 16m
[root@masternew ~]# kubectl describe svc web
Name: web
Namespace: default
Labels: app=web
Annotations: <none>
Selector: app=web 标签选择器,选择标签带app=web的pod
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.96.107.73 服务对外访问地址
IPs: 10.96.107.73
Port: <unset> 8000/TCP 服务对外暴露端口
TargetPort: 80/TCP
Endpoints: 192.169.115.80:80,192.169.115.81:80,192.169.66.24:80
Session Affinity: None
Events: <none>
web服务:对外暴露,用户访问。
redis、mysql、mq等只希望在服务内部使用,不对外暴露。
ClusterIP,集群内访问,默认类型
kubectl expose deploy web --port=8000 --target-port=80 --type=ClusterIP
[root@masternew ~]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 15d <none>
web ClusterIP 10.96.107.73 <none> 8000/TCP 20m app=web
NodePort,通过主机ip来访问服务(所有节点主机都行),在ClusterIP基础上为Service在每台机器上绑定一个端口
kubectl expose deploy web --port=8000 --target-port=80 --type=NodePort
[root@masternew ~]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 15d <none>
web NodePort 10.96.152.188 <none> 8000:31734/TCP 4s app=web
LoadBalancer:在NodePort的基础上,借助Cloud Provider创建一个外部负载均衡器,并将请求转发到NodePort
8、Ingress
官方文档:https://kubernetes.io/zh-cn/docs/concepts/services-networking/ingress/
官网地址:https://kubernetes.github.io/ingress-nginx/,ingress就是nginx做的。
安装文档:https://kubernetes.github.io/ingress-nginx/deploy/
Ingress:service的统一网关入口,K8s的Pod和Service需要通过NodePort把服务暴露到外部, 但是随着微服务的增多,端口会变得不好管理。 加一个Ingress来做路由的转发,方便统一管理。
Ingress 主要分为两部分
1. Ingress Controller 是流量的入口,是一个实体软件, 一般是Nginx 和 Haproxy 。
2. Ingress 描述具体的路由规则。
Ingress作用
1. 基于http-header 的路由
2. 基于 path 的路由
3. 单个ingress 的 timeout
4. 请求速率limit
5. rewrite 规则
# 安装ingress
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.6.4/deploy/static/provider/cloud/deploy.yaml
[root@masternew ~]# kubectl get pod -o wide -n ingress-nginx
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
ingress-nginx-admission-create-df9hs 0/1 Completed 0 27s 192.169.115.93 node2new <none> <none>
ingress-nginx-admission-patch-dbgs9 0/1 Completed 0 27s 192.169.115.94 node2new <none> <none>
ingress-nginx-controller-6488b5758c-qvcc7 1/1 Running 0 27s 192.169.115.95 node2new <none> <none>
[root@masternew ~]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.96.121.29 <pending> 80:31554/TCP,443:30928/TCP 115s
ingress-nginx-controller-admission ClusterIP 10.96.241.7 <none> 443/TCP 115s
发现ingress-nginx安装在node2new(10.1.1.113),浏览器访问,http://10.1.1.113:31554/
# ingress-nginx-controller 对外暴露服务的
# ingress-nginx-controller-admission,准入控制器,做限制的,请求不符合ingress对象,拒绝请求
[root@masternew ~]# kubectl create deployment web-nginx --image=nginx --replicas=2
deployment.apps/web-nginx created
[root@masternew ~]# kubectl create deployment web-tomcat --image=tomcat:8.5.92 --replicas=2
deployment.apps/web-tomcat created
[root@masternew ~]# kubectl expose deploy web-nginx --port=8000 --target-port=80
service/web-nginx exposed
[root@masternew ~]# kubectl expose deploy web-tomcat --port=8080 --target-port=8080
service/web-tomcat exposed
[root@masternew ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 16d
web-nginx ClusterIP 10.96.80.5 <none> 8000/TCP 16s
web-tomcat ClusterIP 10.96.188.193 <none> 8080/TCP 9s
[root@masternew ~]# curl 10.96.80.5:8000
[root@masternew ~]# curl 10.96.188.193:8080
集群内部可以访问了,现在给内部服务做负载均衡 ingress了
修改node2new(10.1.1.113)hosts文件
10.1.1.113 nginx.aaa.com
10.1.1.113 tomcat.aaa.com
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-host-bar
spec:
ingressClassName: nginx
rules:
- host: "nginx.aaa.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: web-nginx
port:
number: 8000
- host: "tomcat.aaa.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: web-tomcat
port:
number: 8080
[root@masternew ~]# kubectl apply -f ingress.yaml
ingress.networking.k8s.io/ingress-host-bar created
[root@masternew ~]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress-host-bar nginx nginx.aaa.com,tomcat.aaa.com 80 6m50s
在node2new测试
[root@node2new ~]# curl nginx.aaa.com:31554
[root@node2new ~]# curl tomcat.aaa.com:31554
ingress 实现限流
Annotations配置:https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/
rules:具体请求的规则,https://kubernetes.github.io/ingress-nginx/user-guide/ingress-path-matching/
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-host-bar
# 一些ingress的规则配置,都直接在这里写即可
# 每秒从给定IP接受的请求数。突发限制设置为此限制乘以突发乘数,默认乘数为5。当客户端超过此限制时,报错503
annotations:
nginx.ingress.kubernetes.io/limit-rps: "1"
spec:
ingressClassName: nginx
rules:
- host: "nginx.aaa.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: web-nginx
port:
number: 8000
- host: "tomcat.aaa.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: web-tomcat
port:
number: 8080
[root@masternew ~]# kubectl apply -f ingress.yaml
ingress.networking.k8s.io/ingress-host-bar configured
[root@node2new ~]# curl tomcat.aaa.com:31554
<html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body>
<center><h1>503 Service Temporarily Unavailable</h1></center>
<hr><center>nginx</center>
</body>
</html>
9、存储卷
[root@masternew ~]# cat mysql-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: mysql-pod
spec:
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
ports:
- containerPort: 3306
volumeMounts:
- mountPath: /var/lib/mysql
name: data-volume
volumes:
- name: data-volume
hostPath:
path: /home/mysql/data
type: DirectoryOrCreate
[root@masternew ~]# kubectl apply -f mysql-pod.yaml
[root@masternew ~]# kubectl exec -ti mysql-pod -- bash
root@mysql-pod:/# mysql -uroot -p123456
hostPath 仅用于在单节点集群上进行开发和测试,不适用于多节点集群;例如,当Pod被重新创建时,可能会被调度到与原先不同的节点上,导致新的Pod没有数据。
hostPath 将主机节点上的文件或目录挂载到 Pod 中。hostPath的type值:
本地存储,例如emptyDir、HostPath,对于本地存储,emptyDir的生命周期与Pod资源相同,所以Pod一旦删除,存储的数据同时被删除。HostPath(node1 node2)的生命周期与节点一致,当Pod重新被调度到其他节点时,虽然原节点上的数据没有被删除,但是Pod不再使用此前的数据。
emptyDir 临时卷,与 Pod 一起创建和删除,生命周期与 Pod 相同。emptyDir会创建一个初始内容为空的本地临时目录,通常使用本地临时存储来设置缓存、保存日志等。
网络存储,nfs、cinder、cephfs等,网络存储系统是独立于k8s集群之外的存储资源,数据存储的持久性与集群解耦合。
# 所有节点安装nfs
yum install -y nfs-utils
# 主节点配置
echo "/nfs/data/ *(insecure,rw,sync,no_root_squash)" > /etc/exports
# 创建/nfs/data文件夹(主节点)
mkdir -p /nfs/data
# 启动rpc远程绑定(主节点)
systemctl enable rpcbind --now
# (开机)启动nfs服务(主节点)
systemctl enable nfs-server --now
# 配置生效(主节点)
exportfs -r
# 查看目录
exportfs
# 查看主节点机器有哪些目录可以同步挂载(node节点)
showmount -e 主节点的内网ip
showmount -e 10.1.1.111
# 执行以下命令挂载 nfs 服务器上的共享目录到本机路径(node节点)
mkdir -p /nfs/data
mount -t nfs 10.1.1.111:/nfs/data /nfs/data
# 在master主节点写入一个文件测试看看
cd /nfs/data
echo hello > hello.txt
# 在从节点目录下查看,发现同步可查看
cd /nfs/data
ls
# 在从节点下修改文件内容
echo 111 >> hello.txt
cat hello.txt
# 在主节点查看,也发生了变化
cat hello.txt
无论容器或部署怎么删除,数据都可以持久化保存了
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-nfs
name: nginx-nfs
spec:
replicas: 2
selector:
matchLabels:
app: nginx-nfs
template:
metadata:
labels:
app: nginx-nfs
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
nfs:
server: 10.1.1.111
path: /nfs/data
PV:持久卷(Persistent Volume),删除Pod后,卷不会被删除。将应用需要持久化的数据保存到指定位置
PVC:持久卷申明(Persistent Volume Claim),申明需要使用的持久卷规格
Persistent Volume是持久卷的意思,是对底层共享存储的一种抽象封装。一般情况 PV由管理员创建和配置,它与底层具体的存储技术有关,通过插件完成与存储的对接。PV 是存储资源的抽象,资源清单如下:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
spec:
nfs: # 存储类型,与底层真正存储对应
capacity: # 存储能力,目前只支持存储空间的设置
storage: 2Gi
accessModes: # 访问模式
storageClassName: # 存储类别
persistentVolumeReclaimPolicy: # 回收策略
accessModes用于描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:
ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载
ReadOnlyMany(ROX):只读权限,可以被多个节点挂载
ReadWriteMany(RWX):读写权限,可以被多个节点挂载
回收策略persistentVolumeReclaimPolicy当PV不再被使用了之后,对其的处理方式。目前支持三种策略:
Retain (保留) 保留数据,需要管理员手工清理数据
Recycle(回收) 清除 PV 中的数据,效果相当于执行 rm -rf /thevolume/*
Delete (删除) 与 PV 相连的后端存储完成 volume 的删除操作,当然这常见于云服务商的存储服务
一个 PV 的生命周期中,可能会处于4种不同的阶段:
Available(可用):表示可用状态,还未被任何 PVC 绑定
Bound(已绑定):表示 PV 已经被 PVC 绑定
Released(已释放):表示 PVC 被删除,但是资源还未被集群重新声明
Failed(失败):表示该 PV 的自动回收失败
Persistent Volume Claim 持久卷声明的意思,是用户对存储需求的一种声明。也就是向 kubernetes 系统发出的一种资源需求申请。PVC 作为资源的申请,用来声明对存储空间、访问模式、存储类别需求信息,资源清单如下:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc
namespace: dev
spec:
accessModes: # 访问模式
selector: # 采用标签对PV选择
storageClassName: # 存储类别
resources: # 请求空间
requests:
storage: 5Gi
创建一个pv,创建一个pvc,创建一个pod绑定pvc就可以了。pv连接存储系统,规定一个大小的空间,权限配置。pvc根据自己的使用要求(存储系统、大小、权限),来匹配pv。
# 创建PV池
# nfs主节点
mkdir -p /nfs/data/01
mkdir -p /nfs/data/02
mkdir -p /nfs/data/03
# 创建pv
# vi pv-demo.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv01-10m
spec:
capacity:
storage: 10M
accessModes:
- ReadWriteMany
storageClassName: nfs
nfs:
path: /nfs/data/01
server: 10.1.1.111
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv02-1gi
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
storageClassName: nfs
nfs:
path: /nfs/data/02
server: 10.1.1.111
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv03-3gi
spec:
capacity:
storage: 3Gi
accessModes:
- ReadWriteMany
storageClassName: nfs
nfs:
path: /nfs/data/03
server: 10.1.1.111
# 应用文件
# kubectl apply -f pv-demo.yaml
persistentvolume/pv01-10m created
persistentvolume/pv02-1gi created
persistentvolume/pv03-3gi created
# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv01-10m 10M RWX Retain Available nfs 6s
pv02-1gi 1Gi RWX Retain Available nfs 6s
pv03-3gi 3Gi RWX Retain Available nfs 6s
# 创建PVC
# vi pvc-demo.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: nginx-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 200Mi
storageClassName: nfs
# kubectl apply -f pvc-demo.yaml
persistentvolumeclaim/nginx-pvc created
# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON
pv01-10m 10M RWX Retain Available nfs
pv02-1gi 1Gi RWX Retain Bound default/nginx-pvc nfs
pv03-3gi 3Gi RWX Retain Available nfs
# 这里的绑定后,状态变为 Bound
# 删除pvc,状态变为释放Released
# kubectl delete -f pvc-demo.yaml
persistentvolumeclaim "nginx-pvc" deleted
# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON
pv01-10m 10M RWX Retain Available nfs
pv02-1gi 1Gi RWX Retain Released default/nginx-pvc nfs
pv03-3gi 3Gi RWX Retain Available nfs
# 查看pvc
# kubectl apply -f pvc-demo.yaml
persistentvolumeclaim/nginx-pvc created
# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
nginx-pvc Bound pv03-3gi 3Gi RWX nfs 81s
# 创建POD绑定PVC
# vi pvc-pod.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-deploy-pvc
name: nginx-deploy-pvc
spec:
replicas: 2
selector:
matchLabels:
app: nginx-deploy-pvc
template:
metadata:
labels:
app: nginx-deploy-pvc
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
persistentVolumeClaim:
claimName: nginx-pvc
# 应用
kubectl apply -f pvc-pod.yaml
kubectl get pod
# 查看应用描述,有pvc信息
kubectl describe pod nginx-deploy-pvc-7c466b8668-2xvk2
# 在pv申明的目录下添加文件,然后去pod中查看是否关联上了
[root@masternew ~]# cd /nfs/data/03
[root@masternew 03]# echo hello>index.html
[root@masternew 03]# curl 192.169.66.40
hello
# 删除pod,再次部署
# kubectl delete -f pvc-pod.yaml
deployment.apps "nginx-deploy-pvc" deleted
# kubectl apply -f pvc-pod.yaml
deployment.apps/nginx-deploy-pvc created
# kubectl get pod
[root@masternew ~]# curl 192.169.115.103
hello
# 发现数据依旧存在
PVC 和 PV是一一对应的,管理员手动创建底层存储和PV,用户创建PVC,kubernetes负责根据PVC的声明去寻找PV,并绑定。在用户定义好PVC之后,系统将根据PVC对存储资源的请求在已存在的PV中选择一个满足条件的。找到PV,就将该PV与用户定义的PVC进行绑定,如果找不到PV,PVC则会无限期处于Pending状态,直到等到系统管理员创建了一个符合其要求的PV,PV一旦绑定到某个PVC上,就会被这个PVC独占,不能再与其他PVC进行绑定了,PVC 和 PV是一一绑定。
用户可在pod中像volume一样使用pvc。Pod使用Volume的定义,将PVC挂载到容器内的某个路径进行使用。
用户删除pvc来释放pv。当存储资源使用完毕后,用户可以删除PVC,与该PVC绑定的PV将会被标记为“Released已释放”,但还不能立刻与其他PVC进行绑定。通过之前PVC写入的数据可能还被留在存储设备上,只有在清除之后该PV才能再次使用。
修改k8s配置,动态修改命令 kubectl edit
# 被保留下来的PV在spec.claimRef字段记录着原来PVC的绑定信息:
[root@k8s-master ~]# kubectl get pv pv02-1gi -o yaml
apiVersion: v1
kind: PersistentVolume
...
spec:
accessModes:
- ReadWriteMany
capacity:
storage: 1Gi
claimRef:
apiVersion: v1
kind: PersistentVolumeClaim
name: nginx-pvc
namespace: default
# resourceVersion: "28692"
# uid: 9fb52841-3943-426a-b0fb-b2e4d32e3d6a
nfs:
path: /nfs/data/02
server: 10.1.1.11
persistentVolumeReclaimPolicy: Delete
storageClassName: nfs
volumeMode: Filesystem
status:
phase: Released
# 删除绑定信息中的resourceVersion和uid键,即可重新释放PV使其状态由Released变为Available
kubectl edit pv pv02-1gi
......
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 10Gi
claimRef:
apiVersion: v1
kind: PersistentVolumeClaim
name: task-pv-claim
namespace: default
....
# 再次查看pv状态已经变为Available
# kubectl get pv
资源回收:kubernetes根据pv设置的回收策略进行资源的回收。对于PV,管理员可以设定回收策略,用于设置与之绑定的PVC释放资源之后如何处理遗留数据的问题。只有PV的存储空间完成回收,才能供新的PVC绑定和使用
StorageClass 存储类作用是创建 PV 的模板,https://kubernetes.io/zh-cn/docs/concepts/storage/storage-classes/#local
动态资源供应,通过StorageClass和PVC完成资源动态绑定(系统自动生成PV),根据PVC申请的空间,来实现PV的创建,从而进行绑定。
# vi local-path-storage.yaml
apiVersion: v1
kind: Namespace
metadata:
name: local-path-storage
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: local-path-provisioner-service-account
namespace: local-path-storage
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: local-path-provisioner-role
rules:
- apiGroups: [ "" ]
resources: [ "nodes", "persistentvolumeclaims", "configmaps" ]
verbs: [ "get", "list", "watch" ]
- apiGroups: [ "" ]
resources: [ "endpoints", "persistentvolumes", "pods" ]
verbs: [ "*" ]
- apiGroups: [ "" ]
resources: [ "events" ]
verbs: [ "create", "patch" ]
- apiGroups: [ "storage.k8s.io" ]
resources: [ "storageclasses" ]
verbs: [ "get", "list", "watch" ]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: local-path-provisioner-bind
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: local-path-provisioner-role
subjects:
- kind: ServiceAccount
name: local-path-provisioner-service-account
namespace: local-path-storage
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: local-path-provisioner
namespace: local-path-storage
spec:
replicas: 1
selector:
matchLabels:
app: local-path-provisioner
template:
metadata:
labels:
app: local-path-provisioner
spec:
serviceAccountName: local-path-provisioner-service-account
containers:
- name: local-path-provisioner
image: rancher/local-path-provisioner:master-head
imagePullPolicy: IfNotPresent
command:
- local-path-provisioner
- --debug
- start
- --config
- /etc/config/config.json
volumeMounts:
- name: config-volume
mountPath: /etc/config/
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
volumes:
- name: config-volume
configMap:
name: local-path-config
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-path
provisioner: rancher.io/local-path
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
---
kind: ConfigMap
apiVersion: v1
metadata:
name: local-path-config
namespace: local-path-storage
data:
config.json: |-
{
"nodePathMap":[
{
"node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",
"paths":["/opt/local-path-provisioner"]
}
]
}
setup: |-
#!/bin/sh
set -eu
mkdir -m 0777 -p "$VOL_DIR"
teardown: |-
#!/bin/sh
set -eu
rm -rf "$VOL_DIR"
helperPod.yaml: |-
apiVersion: v1
kind: Pod
metadata:
name: helper-pod
spec:
containers:
- name: helper-pod
image: busybox
imagePullPolicy: IfNotPresent
# kubectl apply -f local-path-storage.yaml
# 设置为默认的存储类型
kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
# 查看sc
[root@k8s-master ~]# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION
local-path (default) rancher.io/local-path Delete WaitForFirstConsumer false
每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件创建持久卷,该字段必须指定。
[root@masternew ~]# cat mysql-pod.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: local-path-pvc
namespace: default
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata:
name: mysql-pod
spec:
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
ports:
- containerPort: 3306
volumeMounts:
- mountPath: /var/lib/mysql
name: local-mysql-data
volumes:
- name: local-mysql-data
persistentVolumeClaim:
claimName: local-path-pvc
[root@masternew ~]# kubectl apply -f mysql-pod.yaml
persistentvolumeclaim/local-path-pvc created
pod/mysql-pod created
[root@masternew ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
mysql-pod 1/1 Running 0 14h
pod去申请pvc,通过动态创建,自动去创建一个pvc,pvc申请多大空间会在pv创建多大空间的(sc存储类创建的)
[root@masternew ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
local-path-pvc Bound pvc-6be8a361-fab0-4827-bb96-7c440cad2698 1Gi RWO local-path 14h
nginx-pvc Bound pv03-3gi 3Gi RWX nfs 15h
[root@masternew ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv01-10m 10M RWX Retain Available nfs 15h
pv02-1gi 1Gi RWX Retain Available default/nginx-pvc nfs 15h
pv03-3gi 3Gi RWX Retain Bound default/nginx-pvc nfs 15h
pvc-6be8a361-fab0-4827-bb96-7c440cad2698 1Gi RWO Delete Bound default/local-path-pvc local-path 3m22s
ConfigMap是 K8S 中的资源对象,用于保存非机密性的配置的,数据可以用 key/value 键值对的形式保存,也可通过文件的形式保存。 一个项目的所有配置文件都是用一个configmap来统一管理。
configmap特点
1、Configmap 是 k8s 中的资源, 相当于配置文件,可以有一个或者多个 Configmap
2、 Configmap 可以做成 Volume,k8s pod 启动之后,通过 volume 形式映射到容器内部指定目录上;
3、容器中应用程序按照原有方式读取容器特定目录上的配置文件;
4、在容器看来,配置文件就像是打包在容器内部特定目录,整个过程对应用没有任何侵入。
[root@masternew ~]# cat redis.conf
appendonly yes
[root@masternew ~]# kubectl create cm redis-conf --from-file=redis.conf
configmap/redis-conf created
[root@masternew ~]# kubectl get cm
NAME DATA AGE
kube-root-ca.crt 1 22d
redis-conf 1 21s
[root@masternew ~]# kubectl get cm redis-conf -o yaml
apiVersion: v1
data:
redis.conf: |
appendonly yes
kind: ConfigMap
metadata:
creationTimestamp: "2024-01-13T00:55:07Z"
name: redis-conf
namespace: default
resourceVersion: "181392"
uid: 7e7aafed-cb51-4bec-9227-cdee6145a62e
在外部修改了redis.conf文件,容器内部是可以感知到,如果容器获取不到更新数据是容器的问题,当前pod没有热更新的能力的。
部署集群Mysql服务
# 创建pv
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv
spec:
capacity:
storage: 3Gi
accessModes:
- ReadWriteMany
storageClassName: nfs
nfs:
path: /nfs/data/mysql
server: 10.1.1.111
---
# 创建pvc
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
storageClassName: nfs
---
# 创建 configmap
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
data:
mysql.cnf: |
[mysqld]
port=3306
character-set-server=utf8mb4
---
# 创建一个mysql服务
apiVersion: v1
kind: Pod
metadata:
name: mysql-pod
spec:
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
ports:
- containerPort: 3306
volumeMounts:
- mountPath: /var/lib/mysql
name: data-volume
- mountPath: /etc/mysql/conf.d
name: conf-volume
volumes:
- name: conf-volume
configMap:
name: mysql-config
- name: data-volume
persistentVolumeClaim:
claimName: mysql-pvc
Secret 是一个主要用来存储密码、token 等一些敏感信息的资源对象。其中,敏感信息是采用 Base64 编码保存起来的。
[root@masternew ~]# echo -n '123456' | base64
MTIzNDU2
[root@masternew ~]# echo 'MTIzNDU2' | base64 --decode
123456
再次修改mysql.yaml文件,引入Secret
# 创建一个mysql服务
apiVersion: v1
kind: Pod
metadata:
name: mysql-pod
spec:
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-password
key: PASSWORD
ports:
- containerPort: 3306
volumeMounts:
- mountPath: /var/lib/mysql
name: data-volume
- mountPath: /etc/mysql/conf.d
name: conf-volume
volumes:
- name: conf-volume
configMap:
name: mysql-config
- name: data-volume
persistentVolumeClaim:
claimName: msyql-pvc
---
# 创建secret对象, 在一些敏感位置使用,一个项目相关的我们都可以放在这里
apiVersion: v1
kind: Secret
metadata:
name: mysql-password
data:
PASSWORD: MTIzNDU2
[root@masternew ~]# kubectl apply -f mysql.yaml
[root@masternew ~]# kubectl exec -ti mysql-pod -- /bin/bash
root@mysql-pod:/# mysql -uroot -p123456
k8s的4种工作负载
Deployment:无状态应用部署,微服务,提供一些副本功能
StatefulSet:有状态应用,redis、mysql、 提供稳定的存储和网络等等
DaemonSet:守护型应用部署,比如日志,每个机器都会运行一份。
Job/CronJob:定时任务部署,垃圾回收清理,日志保存,邮件,数据库备份, 可以在指定时间运行。
有状态应用 statefulset 无状态应用 deployment,与 Deployment 类似, StatefulSet用来管理 Pod 集合的部署和扩缩。Deployment 无序,用来部署无状态应用。StatefulSet有序,用来部署有状态应用,一般用于管理数据库、缓存等,如果需要部署多个MySQL实例,就需要用到StatefulSet。
StatefulSet的核心功能就是通过某种方式记录这些状态,然后在Pod被重新创建时能够为新Pod恢复这些状态。StatefulSet本质上是Deployment的一种变体,在v1.9版本中已成为GA版本,它为了解决有状态服务的问题,它所管理的Pod拥有固定的Pod名称,启停顺序,在StatefulSet中,Pod名字称为网络标识(hostname),还必须要用到共享存储。
在Deployment中,与之对应的服务是service,而在StatefulSet中与之对应的服务是无头服务headless service,与service的区别就是它没有Cluster IP,解析它的名称时将返回该Headless Service对应的全部Pod的Endpoint列表。除此之外,StatefulSet在Headless Service的基础上又为StatefulSet控制的每个Pod副本创建了一个DNS域名,这个域名的格式为:
$(podname).(headless server name) 简称
$(podname).(headless server name).namespace.svc.cluster.local 全称
StatefulSet为每个Pod副本创建了一个DNS域名,这个域名的格式为: $(podname).(headless server name),也就意味着服务间是通过Pod域名来通信而非Pod IP,因为当Pod所在Node发生故障时,Pod会被飘移到其它Node上,Pod IP会发生变化,但Pod域名不会变化。
StatefulSet使用Headless Service无头服务来控制Pod的域名,这个域名的FQDN为:(podname).(headless server name).(namespace).svc.cluster.local,其中,“cluster.local”指的是集群的域名。
# headless service,即无头服务,nginx, ClusterIP: None
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
# StatefulSet,web,3个副本pod nginx,自动创建3个pvc,自动创建3个pv
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx"
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
# 存储卷申请模版,指定了pvc名称,pvc的大小,申请的类型
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "local-path"
resources:
requests:
storage: 1Gi
为什么需要 headless service 无头服务?
在用Deployment时,每一个Pod名称是没有顺序的,是随机字符串,因此是Pod名称是无序的,但是在statefulset中要求必须是有序 ,每一个pod不能被随意取代,pod重建后pod名称还是一样的。而pod IP是变化的,所以是以Pod名称来识别。pod名称是pod唯一性的标识符,必须持久稳定有效。这时候要用到无头服务,它可以给每个Pod一个唯一的名称 。
为什么需要volumeClaimTemplates?
对于有状态的副本集都会用到持久存储,对于分布式系统来讲,它的最大特点是数据是不一样的,所以各个节点不能使用同一存储卷,每个节点有自已的专用存储,但是如果在Deployment中的Pod template里定义的存储卷,是所有副本集共用一个存储卷,数据是相同的,因为是基于模板来的 ,而statefulset中每个Pod都要自已的专有存储卷,所以statefulset的存储卷就不能再用Pod模板来创建了,于是statefulSet使用volumeClaimTemplates,称为卷申请模板,它会为每个Pod生成不同的pvc,并绑定pv, 从而实现各pod有专用存储。
稳定的存储:在 StatefulSet 中使用 VolumeClaimTemplates,为每个 Pod 创建持久卷声明(PVC)。 注意,当 Pod 或者 StatefulSet 被删除时,持久卷声明(pvc)和关联的持久卷(pv)不会被删除。
[root@masternew ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 23d
nginx ClusterIP None <none> 80/TCP 12m
[root@masternew ~]# kubectl get pod 按顺序一个个创建pod,web-0,web-1,web-2
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 12m
web-1 1/1 Running 0 11m
web-2 1/1 Running 0 10m
[root@masternew ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
www-web-0 Bound pvc-100117f2-73c0-4f96-983c-5624be5d05a9 1Gi RWO local-path 16h
www-web-1 Bound pvc-aea9d84b-24a8-4e09-9a08-700aed2dbfa1 1Gi RWO local-path 16h
www-web-2 Bound pvc-1174711c-5221-4af2-ad2a-9410ed1d6fe9 1Gi RWO local-path 16h
[root@masternew ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-100117f2-73c0-4f96-983c-5624be5d05a9 1Gi RWO Delete Bound default/www-web-0 local-path 16h
pvc-1174711c-5221-4af2-ad2a-9410ed1d6fe9 1Gi RWO Delete Bound default/www-web-2 local-path 16h
pvc-aea9d84b-24a8-4e09-9a08-700aed2dbfa1 1Gi RWO Delete Bound default/www-web-1 local-path 16h
结论:
使用StatefulSet 创建pod,按照顺序依次创建pod。 StatefulSet name名 - 0/1/2/3/4 依次类推
删除pod,再次创建pod,pod名称不会发生变化。
StatefulSet可以让pod的名字不变 + PVC,使得pod对应的volume(pv)不会变,也就是存储了存储状态。
当 Pod 或者 StatefulSet 被删除时,持久卷声明(pvc)和关联的持久卷(pv)不会被删除。
Pod 标识:在具有 N 个副本的 StatefulSet中,每个 Pod 会被分配一个从 0 到 N-1 的整数序号,该序号在此 StatefulSet 上是唯一的。部署扩缩容前提保证:
1、对于包含 N 个 副本的 ,当部署 Pod 时,它们是依次创建的,顺序为 0…N-1。
2、当删除 Pod 时,它们是逆序终止的,顺序为 N-1…0。
3、在将扩缩操作应用到 Pod 之前,它前面的所有 Pod 必须是 Running 和 Ready 状态。
在真实的项目中,redis,mysql 持久化存储的pod都是使用 StatefulSet 来部署的,创建有状态应用。
无论pod ip怎么变化,服务名不变,项目中就可以通过服务名来访问,域名只能在容器内部访问。
$(podname).(headless server name) 简称
$(podname).(headless server name).namespace.svc.cluster.local 全称
[root@masternew ~]# kubectl exec -it web-0 -- /bin/bash
root@web-0:/# curl web-1.nginx
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.21.5</center>
</body>
</html>
root@web-0:/# curl web-2.nginx.default.svc.cluster.local
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.21.5</center>
</body>
</html>
Job/CronJob:定时任务部署,垃圾回收清理,日志保存,邮件,数据库备份, 可以在指定时间运行。
一次性定时任务,在给定的时间点调度 Job 运行
周期性定时任务,创建周期性运行的 Job,例如:数据库备份、发送邮件。
CronJob表达式由五个字段组成,分别代表分钟、小时、日、月、周几。每个字段可以是以下任何值:
1、单个数字:例如5表示第5分钟或5月份。
2、逗号分隔的数字列表:例如5,15,25表示第5、15和25分钟。
3、连续的数字范围:例如10-15表示从第10分钟到第15分钟。
4、星号*:表示匹配该字段的所有值。例如在分钟字段上使用星号表示每分钟执行任务。
5、斜杠(/):表示步长值。例如在分钟字段上使用"*/3"表示每隔3分钟执行一次任务。
CronJob表达式示例:
1、每小时执行:0 * * * *
2、每天晚上10点执行:0 22 * * *
3、每周一早上6点执行:0 6 * * 1
4、每2分钟运行一次任务 : */2 * * * *
cronjob类似于Linux 的crontab, cronjob简写为cj,查看cronjob任务,kubectl get cj。
Kind:CronJob
CronJob Spec
.spec.schedule:调度,必需字段,指定任务运行周期,格式同 Cron
.spec.jobTemplate:Job 模板,必需字段,指定需要运行的任务,格式同 Job
.spec.startingDeadlineSeconds :启动 Job 的期限(秒级别),该字段是可选的。如果因为任何原因而错过了被调度的时间,那么错过执行时间的 Job 将被认为是失败的。如果没有指定,则没有期限
.spec.concurrencyPolicy:并发策略,该字段也是可选的。它指定了如何处理被 Cron Job 创建的 Job 的并发执行。只允许指定下面策略中的一种:
Allow(默认):允许并发运行 Job
Forbid:禁止并发运行,如果前一个还没有完成,则直接跳过下一个
Replace:取消当前正在运行的 Job,用一个新的来替换
注意,当前策略只能应用于同一个 Cron Job 创建的 Job。如果存在多个 Cron Job,它们创建的 Job 之间总是允许并发运行。
.spec.suspend :挂起,该字段也是可选的。如果设置为 true,后续所有执行都会被挂起。它对已经开始执行的 Job 不起作用。默认值为 false。
.spec.successfulJobsHistoryLimit 和 .spec.failedJobsHistoryLimit :历史限制,是可选的字段。它们指定了可以保留多少完成和失败的 Job。默认情况下,它们分别设置为 3 和 1。设置限制的值为 0,相关类型的 Job 完成后将不会被保留。
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
# 描述自己的定时任务具体内容
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
args:
- /bin/sh
- -c
- date; echo cronjob logs
restartPolicy: OnFailure
[root@masternew ~]# kubectl apply -f cronjob.yaml
cronjob.batch/hello created
[root@masternew ~]# kubectl get cj
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
hello */1 * * * * False 1 35s 3m14s
[root@masternew ~]# kubectl get job
NAME COMPLETIONS DURATION AGE
hello-28420321 1/1 28s 2m6s
hello-28420322 1/1 34s 66s
hello-28420323 0/1 6s 6s
[root@masternew ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
hello-28420321-fm5v9 0/1 Completed 0 2m11s
hello-28420322-m982f 0/1 Completed 0 71s
hello-28420323-6c76x 0/1 ContainerCreating 0 11s
[root@masternew ~]# kubectl delete cronjob hello
cronjob.batch "hello" deleted
DaemonSet:守护型应用部署,比如日志,每个机器都会运行一份。
DaemonSet是个Pod控制器,会在k8s集群的所有节点都运行一个相同的pod副本,假设这个pod名称为pa,当增加node节点时,这个节点会自动创建一个pa,当删除node节点时,pa副本会自动删除。删除daemonset会删除它们创建的pod。
使用场景
1、需要在每一个node节点运行一个存储服务,例如gluster,ceph
2、需要在每一个node节点运行一个日志收集服务,例如fluentd,logstash
3、需要在每一个node节点运行一个监控服务,例如Prometheus Node Exporter,zabbix agent等
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: daemonset-nginx
namespace: kube-system
labels:
app: daemonset-nginx
spec:
selector:
matchLabels:
app: daemonset-nginx
template:
metadata:
labels:
app: daemonset-nginx
spec:
containers:
- name: nginx
image: nginx
[root@masternew ~]# kubectl get Daemonset -n kube-system
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
calico-node 3 3 3 3 3 kubernetes.io/os=linux 23d
daemonset-nginx 2 2 2 2 2 <none> 10m
kube-proxy 3 3 3 3 3 kubernetes.io/os=linux 23d
在每一个node节点都会创建一个pod,master节点没创建
[root@masternew ~]# kubectl get pod -n kube-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
daemonset-nginx-4zdsk 1/1 Running 0 34s 192.169.66.56 node1new <none> <none>
daemonset-nginx-7ntx6 1/1 Running 0 34s 192.169.115.66 node2new <none> <none>
master节点有污点NoSchedule,不让pod调度到该节点
[root@masternew ~]# kubectl describe node masternew
Taints: node-role.kubernetes.io/control-plane:NoSchedule
如果节点污点Taints为空,会让pod调度到该节点
[root@masternew ~]# kubectl describe node node1new
Taints: <none>
[root@masternew ~]# kubectl describe node node2new
Taints: <none>
DaemonSet有指定节点,那就在指定节点创建pod,如指定了 节点名字,affinity,污点容忍度等,DaemonSet未指定节点,将在所有节点上创建Pod。
10、Pod探究
kubectl get pod -w 动态监听pod的创建过程
pod的几种状态:
running 正常运行状态
Pending 资源分配不对的时候会挂起,出现此状态
Terminating 某个节点突然关机,上面的pod就会是这种状态
ContainerCreating 容器创建的时候
OOMKilled 当要求的内存超过限制的时候,k8s会把这个容器kill后重启(OOM内存溢出)
ErrImagePull 宿主机上不了网,镜像拉去失败
Pod 容器生命周期
每个Pod里运行着一个特殊的被称之为Pause的容器,其他容器则为业务容器,这些业务容器共享Pause容器的网络栈和Volume挂载卷,因此他们之间通信和数据交换更为高效。在设计时可以充分利用这一特性,将一组密切相关的服务进程放入同一个Pod中;同一个Pod里的容器之间仅需通过localhost就能互相通信。
Pause 容器,又叫 Infra 容器。比如说现在有一个 Pod,其中包含了一个容器 A 和一个容器 B ,它们两个就要共享 Network Namespace。在 Kubernetes 里的解法是这样的:它会在每个 Pod 里,额外起一个 Infra container 小容器来共享整个 Pod 的 Network Namespace。Infra container 是一个非常小的镜像,大概 700KB 左右,是一个 C 语言写的、永远处于 “暂停” 状态的容器。由于有了这样一个 Infra container 之后,其他所有容器都会通过 Join Namespace 的方式加入到 Infra container 的 Network Namespace 中。所以说一个 Pod 里面的所有容器,它们看到的网络视图是完全一样的。即:它们看到的网络设备、IP 地址、Mac 地址等等,跟网络相关的信息,其实全是一份,这一份都来自于 Pod 第一次创建的这个 Infra container。这就是 Pod 解决网络共享的一个解法。由于需要有一个相当于说中间的容器存在,所以整个 Pod 里面,必然是 Infra container 第一个启动。并且整个 Pod 的生命周期是等同于 Infra container 的生命周期的,与容器 A 和 B 是无关的。这也是为什么在Kubernetes 里面,它是允许去单独更新 Pod 里的某一个镜像的,即:做这个操作,整个 Pod 不会重建,也不会重启,这是非常重要的一个设计。
Pod hook(钩子函数)是由 Kubernetes 管理的 kubelet 发起的,当容器中的进程启动前或者容器中的进程终止之前运行,这是包含在容器的生命周期之中。可以同时为 Pod 中的所有容器都配置 hook钩子函数。
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
spec:
containers:
- name: lifecycle-demo-container
image: nginx
lifecycle:
postStart: #监听容器启动之前的函数,前置环境准备
exec:
command: ["/bin/sh", "-c", "echo Hello from the postStart handler> /usr/share/message"]
preStop: #监听容器停止之后的函数,释放资源
exec:
command: ["/usr/sbin/nginx","-s","quit"]
postStart 在容器创建之后(但并不能保证钩子会在容器 ENTRYPOINT 之前)执行,这时候 Pod 已经被调度到某台 node 上,被某个 kubelet 管理了,这时候 kubelet 会调用 postStart 操作,该操作跟容器的启动命令是在同步执行的,也就是说在 postStart 操作执行完成之前,kubelet 会锁住容器,不让应用程序的进程启动,只有在 postStart 操作完成之后容器的状态才会被设置成为 RUNNING。
PreStop 在容器终止之前被同步阻塞调用,常用于在容器结束前优雅的释放资源。
如果 postStart 或者 preStop hook 失败,将会终止容器。
init容器,查看官网学习
init容器是一种特殊容器,在 Pod 内的应用容器启动之前运行。init容器可以包括一些应用镜像中不存在的实用工具和安装脚本。每个 Pod 中可以包含多个容器, 应用运行在这些容器里面,同时 Pod 也可以有一个或多个先于应用容器启动的 Init 容器。
init容器官网案例:https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/init-containers/
# cat pod-init.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
[root@masternew ~]# kubectl apply -f pod-init.yaml
pod/myapp-pod createda
[root@masternew ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp-pod 0/1 Init:0/2 0 8s <none> node2new <none> <none>
Init 容器将会等待至发现名称为 mydb 和 myservice 的服务。启动init容器所需要的服务
[root@masternew ~]# vi pod-init.yaml 再次编辑文件追加服务内容
---
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
[root@masternew ~]# kubectl apply -f pod-init.yaml
pod/myapp-pod unchanged
service/myservice created
service/mydb created
[root@masternew ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp-pod 1/1 Running 0 4m32s 192.169.115.68 node2new <none> <none>
等待init容器全部执行完毕后,容器才正式开始执行,Running状态。
Init 容器与普通的容器非常像,区别是每个Init 容器都必须在下一个Init 容器启动之前成功完成,每个Init 容器按照顺序执行。如果 Pod 的 Init 容器失败,kubelet 会不断地重启该 Init 容器直到该容器成功为止。 然而,如果 Pod 对应的restartPolicy 值为 “Never”,Kubernetes 不会重新启动 Pod。容器重启策略默认是Always。
如果为一个 Pod 指定了多个 Init 容器,这些容器会按顺序逐个运行。 每个 Init 容器必须运行成功,下一个才能够运行。当所有的 Init 容器运行完成时, Kubernetes 才会为 Pod 初始化应用容器并像平常一样运行。
为 Pod 设置 Init 容器需要在 Pod 的 spec 中添加 initContainers 字段, 该字段以 Container类型对象数组的形式组织,和应用的 containers 数组同级相邻。Init 容器支持应用容器的全部字段和特性,包括资源限制、数据卷和安全设置。
容器探针,查看官网学习
官网文档:https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
Kubernetes探针(Probe)是用于检查容器运行状况的一种机制。探针可以检查容器是否正在运行,容器是否能够正常响应请求,以及容器内部的应用程序是否正常运行等。在Kubernetes中,探针可以用于确定容器的健康状态,如果容器的健康状态异常,Kubernetes将会采取相应的措施,例如重启容器或将其从服务中删除。容器探针可以确保容器在任何时候都处于可预测的状态。如果没有容器探针,那么容器对于K8S集群而言,就处于一个黑盒状态。下面是没有使用容器探针可能出现的一些案例:
1、容器未启动,负载均衡就把流量转发给容器,导致请求大量异常
2、容器内服务不可用/发生异常,负载均衡把流量转发给容器,导致请求大量异常
3、容器已经不正常工作(如容器死锁导致的应用程序停止响应),K8S集群本身无法感知,不能即时重启容器。
3种探针类型:Liveness Probe(存活探针)、Readiness Probe(就绪探针)、Startup Probe(启动探针)
1、livenessProbe(存活探针),用于检查容器是否正在运行,如果Liveness Probe检查失败,则Kubernetes将重启容器。
2、readinessProbe(就绪探针),用于检查容器是否能够正常响应请求,如果Readiness Probe检查失败,则Kubernetes将停止将流量发送到该容器。
3、startupProbe(启动探针),用于检查容器内部的应用程序是否已经启动并且已经准备好接受流量,如果Startup Probe检查失败,则Kubernetes将重启容器。
探针资源 yaml 中的常用的配置:
spec:
containers:
# 就绪探针
readinessProbe:
# 检测方式
httpGet:
# 超时时间
timeoutSeconds:
# 延迟时间
initialDelaySeconds:
# 失败次数限制
failureThreshold:
# 每多少秒检测一次
periodSeconds:
# 存活探针
livenessProbe:
# 检测方式
httpGet:
# 超时时间
timeoutSeconds:
# 延迟时间
initialDelaySeconds:
# 失败次数限制
failureThreshold:
# 每多少秒检测一次
periodSeconds:
4种探测机制:
1、exec,在容器内执行指定命令,如果命令退出时返回码为 0 则认为诊断成功。
2、httpGet,对容器的 IP 地址上指定端口和路径执行 HTTP GET 请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的。
3、tcpSocket,对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则诊断被认为是成功的。如果远程系统 (容器)在打开连接后立即将其关闭,这算作是健康的。
4、grpc,使用 gRPC 执行一个远程过程调用。目标应该实现 gRPC健康检查。 如果响应的状态是"SERVING",则认为诊断成功。 gRPC 探针是一个 alpha 特性,只有在你启用了"GRPCContainerProbe"特性门控时才能使用。
3种探测结果:
1、Success`(成功)容器通过了诊断。2、Failure(失败)容器未通过诊断。3、Unknown(未知)诊断失败,因此不会采取任何行动。
节点亲和性
Scheduler 是 kubernetes 的调度器,主要的任务是把定义的 pod 分配到集群的节点上。Scheduler 是作为单独的程序运行的,启动之后会一直监听 API Server ,获取Pod.Spec.NodeName为空的 pod,对每个 pod 都会创建一个 binding,表明该 pod 应该放到哪个节点上。
调度分为几个部分:
1、首先是过滤掉不满足条件的节点,这个过程称为predicate(预选);
2、然后对通过的节点按照优先级排序,这个是priority(优选);
3、最后从中选择优先级最高的节点。
如果中间任何一步骤有错误,就直接返回错误(先预选,后优选)
Predicate(预选)有一系列的算法可以使用:
1、PodFitsResources:节点上剩余的资源是否大于 pod 请求的资源
2、PodFitsHost:如果 pod 指定了 NodeName,检查节点名称是否和 NodeName 匹配
3、PodFitsHostPorts:节点上已经使用的 port 是否和 pod 申请的 port 冲突
4、PodSelectorMatches:过滤掉和 pod 指定的 label 不匹配的节点
5、NoDiskConflict:已经 mount 的 volume 和 pod 指定的 volume 不冲突,除非它们都是只读
如果在 predicate(预选) 过程中没有合适的节点,pod 会一直在pending状态(pending:等待),不断重试调度,直到有节点满足条件。经过这个步骤,如果有多个节点满足条件,就继续 priorities (优选)过程:按照优先级大小对节点排序。
优先级由一系列键值对组成,键是该优先级项的名称,值是它的权重(该项的重要性)。通过算法对所有的优先级项目和权重进行计算,得出最终的结果。这些优先级选项包括:
1、LeastRequestedPriority:通过计算 CPU 和 Memory 的使用率来决定权重,使用率越低权重越高。换句话说,这个优先级指标倾向于资源使用比例更低的节点
2、BalancedResourceAllocation:节点上 CPU 和 Memory 使用率越接近,权重越高。这个应该和上面的一起使用,不应该单独使用
3、ImageLocalityPriority:倾向于已经有要使用镜像的节点,镜像总大小值越大,权重越高
pod与node的亲和性,pod.spec.affinity.nodeAffinity
preferredDuringSchedulingIgnoredDuringExecution(优先执行计划):软策略
requiredDuringSchedulingIgnoredDuringExecution(要求执行计划):硬策略
operator 键值运算关系:
In:label 的值在某个列表中
NotIn:label 的值不在某个列表中
Gt:label 的值大于某个值
Lt:label 的值小于某个值
Exists:某个 label 存在
DoesNotExist:某个 label 不存在
#软硬策略(先满足硬策略再满足软策略)
apiVersion: v1
kind: Pod
metadata:
name: affinity2
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: nginx
affinity:
# 可以编写多个亲和性策略
nodeAffinity: #node亲和性
# 硬亲和性限制
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname # 标签键名
operator: NotIn #键值运算关系,NotIn:label的值不在某个列表中。表示不是node02节点就可运行
values:
- k8s-node02 # 标签键值
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1 #权重,权重越大越亲和(多个软策略的情况)
preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8s-node03 # 期望是node03
pod与pod之间的亲和性
pod.spec.affinity.podAffinity 亲和性 pod.spec.affinity.podAntiAffinity 反亲和性
preferredDuringSchedulingIgnoredDuringExecution:软策略
requiredDuringSchedulingIgnoredDuringExecution:硬策略
要有选择中的 Pod, 也就是要有 labelSelector(若没有设置此字段,表示没有选中的 Pod)。topology 是拓扑域的意思,是指一个范围的概念,实际上对应的还是 Node 上的标签。topologyKey 可以理解为 Node 的 label,比如默认所有节点都会有kubernetes.io/hostname这 label,相应的值为节点名称,如 master01 节点的 label 为kubernetes.io/hostname=master01,这种情况每个节点对应的值都不同。
pod亲和性调度需要各个相关的pod对象运行于"同一位置", 而反亲和性调度则要求他们不能运行于"同一位置",这里指定“同一位置” 是通过 topologyKey 来定义的。
在k8s这个集群中,找到期望的pod启动节点:pod亲和性策略:标签: app=node-affinity-pod
反亲和性 podAntiAffinity
Taint污点 和 Toleration 容忍度
所谓污点就是故意给某个节点服务器上设置个污点参数,那么你就能让生成pod的时候使用相应的参数去避开有污点参数的node服务器。而容忍呢,就是当资源不够用的时候,即使这个node服务器上有污点,那么只要pod的yaml配置文件中写了容忍参数,最终pod还是会容忍的生成在该污点服务器上。默认master节点是NoSchedule。
污点(Taint) 是应用在node节点之上的,是为了排斥pod 所存在的。
容忍度(Toleration)是应用于 Pod 上的,允许Pod 调度到带有与之匹配的污点的节点上。
Taint(污点)和 Toleration(容忍)可以作用于 node 和 pod 上,其目的是优化 pod 在集群间的调度,这跟节点亲和性类似,只不过它们作用的方式相反,具有 taint 的 node 和 pod 是互斥关系,而具有节点亲和性关系的 node 和 pod 是相吸的。另外还有可以给 node 节点设置 label,通过给 pod 设置 nodeSelector 将 pod 调度到具有匹配标签的节点上。
Taint 和 toleration 相互配合,可以用来避免 pod 被分配到不合适的节点上。每个节点上都可以应用一个或多个 taint ,这表示对于那些不能容忍这些 taint 的 pod,是不会被该节点接受的。如果将 toleration 应用于 pod 上,则表示这些 pod 可以被调度到具有相应 taint 的节点上。
查看某个节点的Taint污点信息
[root@masternew ~]# kubectl describe node node1new
Taints: <none>
为 node 设置 taint,Node被设置上污点之后就和Pod之间存在了一种相斥的关系,可以让Node拒绝Pod的调度执行,甚至将Node已经存在的Pod驱逐出去。每个污点有一个key和value作为污点的标签,其中value可以为空,effect描述污点的作用。当前taint effect支持如下三个选项:
1、NoSchedule:不会将Pod调度到具有该污点的Node上,Node设置污点前存在的pod就正常运行。
2、 PreferNoSchedule:尽量避免将Pod调度到具有该污点的Node上。
3、NoExecute:不会将Pod调度到具有该污点的Node上,同时会将Node上已经存在的Pod驱逐出去。
给节点node1增加一个污点,它的键名是key1,键值是value1,效果是NoSchedule。 这表示只有拥有和这个污点相匹配的容忍度的 Pod 才能够被分配到node1这个节点。
添加污点,kubectl taint node node1new key1=value1:NoSchedule
[root@masternew ~]# kubectl describe node node1new
Taints: key1=value1:NoSchedule
删除污点,去除污点,最后一个"-"代表删除
kubectl taint node node1new key1:NoSchedule-
为 pod 设置 toleration,只要在 pod 的 spec 中设置 tolerations 字段即可,可以存在多个 key,如下所示:
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
- key: "key2"
operator: "Equal"
value: "value2"
effect: "NoExecute"
- key: "node.alpha.kubernetes.io/unreachable"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 6000
value 的值可以为 NoSchedule、PreferNoSchedule 或 NoExecute。
tolerationSeconds 是当 pod 需要被驱逐时,可以继续在 node 上运行的时间。
多个污点匹配原则,可以给一个节点添加多个污点,也可以给一个 Pod 添加多个容忍度设置。
Kubernetes 处理多个污点和容忍度的过程就像一个过滤器:从一个节点的所有污点开始遍历, 过滤掉那些 Pod 中存在与之相匹配的容忍度的污点。余下未被过滤的污点的 effect 值决定了 Pod 是否会被分配到该节点。
例如给一个节点添加了如下污点:
kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule
假定有一个 Pod,它有两个容忍度:
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
上述 Pod 不会被分配到 node1 节点,因为其没有容忍度和第三个污点相匹配。
但是如果在给节点添加上述污点之前,该 Pod 已经在上述节点运行, 那么它还可以继续运行在该节点上,因为第三个污点是三个污点中唯一不能被这个 Pod 容忍的。
通过污点和容忍度,可以灵活地让 Pod避开某些节点或者将 Pod 从某些节点驱逐。
指定调度节点,指定在某台节点运行pod的命令: nodeName 、nodeSelector(需要再node上指定labels)
Pod.spec.nodeName 将 Pod 直接调度到指定的 Node 节点上,会跳过 Scheduler 的调度策略,该匹配规则是强制匹配。
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: myweb
spec:
replicas: 7
template:
metadata:
labels:
app: nginx
spec:
nodeName: node01 #指定全在node01
containers:
- name: myweb
image: nginx
ports:
- containerPort: 80
Pod.spec.nodeSelector:通过 kubernetes 的 label-selector 机制选择节点,由调度器调度策略匹配 label,而后调度 Pod 到目标节点,该匹配规则属于强制约束。
首先在 master 节点给需要调度的目标 node 打上 label,并在创建 pod 的 yaml 文件中指定 label 来使得 pod 创建的时候调度到指定标签的 node 节点。
# 首先在 master 节点给指定的 node 节点加上标签
kubectl label node node01 disk=ssd
kubectl get node node01 --show-labels |grep disk
# 编写 yaml 文件引入标签
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-busybox
spec:
replicas: 1
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: busybox
image: busybox
imagePullPolicy: IfNotPresent
nodeSelector: # 定义 nodeSelector 参数
disk: ssd # 引入标签
11、认证与鉴权
官网文档:https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/
客户端进行认证和鉴权操作,在Kubernetes集群中,客户端通常有两类:
1、User Account:一般是独立于kubernetes之外的其他服务管理的用户账号
2、Service Account:kubernetes管理的账号,用于为Pod中的服务进程在访问Kubernetes时提供身份标识
ApiServer是访问及管理资源对象的唯一入口,任何一个请求访问ApiServer,都要经过下面三个流程:
1、Authentication (认证): 身份鉴别,只有正确的账号才能够通过认证。
2、Authorization (授权) : 判断用户是否有权限对访问的资源执行特定的动作。
3、Admission Control (准入控制): 用于补充授权机制以实现更加精细的访问控制功能。
认证管理
Kubernetes集群安全的最关键点在于如何识别并认证客户端身份,它提供了3种客户端身份认证方式:。注意: Kubernetes允许同时配置多种认证方式,只要其中任意一个方式认证通过即可。
1、HTTP Base认证:通过用户名+密码的方式认证
这种认证方式是把”用户名:密码”用BASE64算法进行编码后的字符审放在HTTP清求中的Header Authrizaton域里发送给服务端。服务端收到后进行解码,获取用户名及密码,然后进行用户身份认证的过程。
2、HTTP Token认证: 通过一个Token来识别合法用户
这种认证方式是用一个很长的Token来表明客户身份的一种方式,每Token对应一个用户名,当客户端发起APi 调用请求时,在HTTP Header里放入Token,API Server接到Token后会跟服务器中保存的token进行比对,然后进行用户身份认证的过程。
3、HTTPS证书认证: 基于CA根证书签名的双向数字证书认证方式
这种认证方式是安全性最高的一种方式,但是同时也是操作起来最麻烦的一种方式。HTTPS认证大体分为3个过程:
3.1、证书申请和下发, HTTPS通信双方的服务器向CA机构申请证书,CA机构下发根证书、服务端证书及私钥给申请者。
3.2、客户端和服务端的双向认证
客户端向服务器端发起清求,服务端下发自己的证书给客户端, 客户端接收到证书后,通过私钥解密证书,在证书中获得服务端的公钥,客户端利用服务器端的公钥认证证书中的信息,如果一致,则认可这个服务器。
客户端发送自己的证书给服务器端,服务端接收到证书后,通过私钥解密证书,在证书中获得客户端的公钥,并用该公钥认证证书信息,确认客户端是否合法。
3.3、服务端和客户端进行通信 服务端和客户端协商好加密方案后,客户端会产生一个随机的秘钥并加密,然后发送到服务端。服务器端接收这个秘钥后,双方按下来通信的所有内容都通过该随机秘钥加密。
授权管理
授权发生在认证成功之后,通过认证就可以知道请求用户是谁,然后Kubemetes会根据事先定义的授权策略来决定用户是否有权限访问,这个过程就称为授权。
鉴权 每个发送到ApiServer的请求都带上了用户和资源的信息:比如发送请求的用户、请求的路径 资源 、请求的动作get create delete等,授权就是根据这些信息和授权策略进行比较,如果符合策略,则认为授权通过,否则会返回错误。
API Server目前支持以下几种授权策略:
1、AlwaysDeny: 表示拒绝所有请求,一般用于测试
2、AlwaysAllow: 允许接收所有请求,相当于集群不需要授权流程 (Kubernetes默认的策略)
3、ABAC:基于属性的访问控制,表示使用用户配置的授权规则对用户请求进行匹配和控制
4、Webhook: 通过调用外部REST服务对用户进行授权
5、Node: 是一种专用模式,用于对kubelet发出的请求进行访问控制
6、RBAC: 基于角色的访问控制 (kubeadm安装方式下的默认选项)
RBAC(Role-Based Access Control)基于角色的访问控制,给哪些对象授予了哪些权限,其中涉及到了下面几个概念:
1、资源: 对集群中资源和非资源均拥有完整的覆盖
2、对象: User、Groups, ServiceAccount
3、角色:代表着一组定义在资源上的可操作动作作(权限)的集合
4、绑定:将定义好的角色跟用户绑定在一起, RBAC引入了4个顶级资源对象 yaml kind类型.
5、Role 、ClusterRole: 角色,用于指定一组权限
5.1、一个角色就是一组权限的集合。
5.2、apiGroups: 支持的API组列表‘’apps", “autoscaling”, “batch”
5.3、resources:支持的资源对象列表"services", “endpoints”, “pods”,“secrets”,“configmaps”,“crontabs”,“deployments”,“jobs”,“nodes”,“rolebindings”,“clusterroles”,“daemonsets”,“replicasets”,“statefulsets”,“horizontalpodautoscalers”,“replicationcontrollers”,“cronjobs”
5.4、verbs:对资源对象的操作方法列表 “get”, “list”, “watch”, “create”, “update”, “patch”, “delete”, “exec”
6、RoleBinding、ClusterRoleBinding: 角色绑定,用于将角色 (权限) 赋予给对象
6.1、角色绑定用来把一个角色绑定到一个目标对象上,绑定目标可以是User、Group或者ServiceAccount。
6.2、RoleBinding可以引用ClusterRole,对属于同一命名空间内ClusterRole定义的资源主体进行授权。
准入控制
通过了前面的认证和授权之后,还需要经过准入控制处理通过之后,apiserver才会处理这个请求。
准入控制机制是 Kubernetes API Server 中一个非常强大的功能,它允许对进入 Kubernetes 集群的请求做出控制。API Server 会在对象(如 Pod、Node、Service 等)被保存到etcd数据库之前,对该对象进行检查和修改。准入控制可用于实施强制性的数据验证、安全功能、自动化操作等功能。
一句话:就是在创建资源经过身份验证之后,kube-apiserver在数据写入etcd之前做一次拦截,然后对资源进行更改、判断正确性等操作。
API Server 内置了许多准入控制器,常用的包含如下几种:
1、AlwaysAdmit:允许所有请求。
2、AlwaysDeny:拒绝所有请求 ,仅应该用于测试。
3、AlwaysPulllmages :总是下载镜像,即每次创建 Pod 对象之前都要去下载镜像,常用于多租户环境中以确保私有镜像仅能够被拥有权限的用户使用。
4、NamespaceLifecycle :拒绝于不存在的名称空间中创建资源,而删除名称空间将会 级联删除其下的所有其他资源。
5、LimitRanger :可用资源范围界定,用于监控对设置了 LimitRange 的对象所发出的 所有请求,以确保其资源、请求不会超限。
6、ServiceAccount :用于实现 Service Account 管控机制的自动化,实现创建 Pod 对象 时自动为其附加相关的 Service Account 对象。
7、PersistentVolumeLabel :为那些由云计算服务商提供的 PV 自动附加 region 或 zone 标签,以确保这些存储卷能够正确关联且仅能关联到所属的 region 或 zone。
8、DefaultStorageClass :监控所有创建 PVC 对象的请求,以保证那些没有附加任何专 用 Storag巳Class 的请求会自动设定一个默认值。
9、ResourceQuota : 用于对名称空间设置可用资源的上限,并确保在其中创建的任何设 置了资源限额的对象都不会超出名称空间的资源配额。
10、DefaultTolerationSeconds :如果 Pod 对象上不存在污点宽容期限,则为它们设置默 认的宽容期,以宽容“ notready:N oExecute”和“ unreachable:N oExctute ”类的污点 5 分钟 时间。
11、ValidatingAdmission Webhook :并行调用匹配当前请求的所有验证类的 Webhook, 任何一个校验失败,请求即失败。
12、MutatingAdmissionWebhook :串行调用匹配当前请求的所有变异类的 Webhook, 每个调用都可能会更改对象。
例如我们要设置 Deployment 可以拥有的副本数量限制,那么可以定义如下所示的验证策略资源对象:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
name: "demo-policy.example.com" # 策略对象
spec:
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments"]
validations:
- expression: "object.spec.replicas <= 5"
该对象中的 expression 字段的值就是用于验证准入请求的 CEL 表达式,我们这里配置的 object.spec.replicas <= 5,就表示要验证对象的 spec.replicas 属性值是否大于 5,而 matchConstraints 属性则声明了该 ValidatingAdmissionPolicy 对象可以验证哪些类型的请求,我们这里是针对 Deployment 资源对象。
认证方案
1、kubeconfig,kubeconfig 文件包含集群参数(CA证书、API Server地址),客户端参数,集群context信息(集群名称、用户名)。
2、ServiceAccount,Pod 中的容器访问API Server。因为Pod的创建和销毁是动态的,所以要为它手动生成证书是不可行的,k8s 使用 Service Account解决Pod访问API Server的认证问题。
API Server 是集群内部各个组件通讯的中介,也是外部控制的入口。k8s 使用认证(Authentication)、鉴权(Authorization)、准入控制(Admission Control) 三步来确保API Server的安全。
认证和鉴权:认证(authencation): 通过只代表通讯双方是可信的;鉴权(authorization): 确定请求方有哪些资源权限。
UserAccount:给集群外用户访问 API Server,执行 kubectl 命令时用的就是该账号。它是全局性的,跨 namespace, 通常为admin,也可以自定义。
cat /root/.kube/config
users:
- name: kubernetes-admin
user:
ServiceAccount:Pod 容器访问 API Server 的身份认证。它与 namespace 绑定,每个namespace (包含 default)都会自动创建一个默认的 SA。创建 Pod 时,如果未指定 SA, 则使用默认的SA,可通过配置 spec.serviceAccount 指定 SA。
[root@masternew ~]# kubectl get sa
NAME SECRETS AGE
default 0 24d
[root@masternew ~]# kubectl get sa default -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
creationTimestamp: "2023-12-21T09:09:10Z"
name: default
namespace: default
resourceVersion: "326"
uid: 48dacfe8-ff43-49cf-9f63-6644e44ca2d7
SA 是一种特殊的 secret,类型为 kubernetes.io/service-account-token,它由三部分组成:
1、token:由 API Server 私钥签发的 JWT,Pod访问API Server的凭证
2、ca.crt:根证书,用于Client端验证API Server发送的证书,与 /etc/kubernetes/pki/ca.pem 一致
3、namespace:该service-account-token的作用域名空间,Pod 所属 namespace
[root@masternew ~]# kubectl run -it nginx --image=nginx -- /bin/bash
If you don't see a command prompt, try pressing enter.
root@nginx:/# cd /run/secrets/kubernetes.io/serviceaccount/
root@nginx:/run/secrets/kubernetes.io/serviceaccount# ls
ca.crt namespace token
自定义SA
[root@masternew ~]# kubectl create ns my-ns
namespace/my-ns created
[root@masternew ~]# kubectl create sa my-sa -n my-ns
serviceaccount/my-sa created
[root@masternew ~]# kubectl get sa -n my-ns
NAME SECRETS AGE
default 0 22s
my-sa 0 12s
资源属性
apiVersion: v1 # ServiceAccount所属的API群组及版本
kind: ServiceAccount # 资源类型标识
metadata:
name <string> # 资源名称
namespace <string> # ServiceAccount是名称空间级别的资源
automountServiceAccountToken <boolean> # 是否让Pod自动挂载API令牌
secrets <[]Object> # 以该SA运行的Pod所要使用的Secret对象组成的列表
apiVersion <string> # 引用的Secret对象所属的API群组及版本,可省略
kind <string> # 引用的资源的类型,这里是指Secret,可省略
name <string> # 引用的Secret对象的名称,通常仅给出该字段即可
namespace <string> # 引用的Secret对象所属的名称空间
uid <string> # 引用的Secret对象的标识符;
imagePullSecrets <[]Object> # 引用的用于下载Pod中容器镜像的Secret对象列表
name <string> # docker-registry类型的Secret资源的名称
Pod 中 SA 配置:
apiVersion: v1
kind: Pod
metadata:
name: nginx-sa
namespace: my-ns
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
serviceAccountName: my-sa # 指定 SA,不指定默认是default
RBAC基于角色的访问控制,规则配置到角色上,用户就可以绑定角色获得对应的规则权限。
ServiceAccount 是 APIServer 的认证过程,而授权机制通过 RBAC:基于角色的访问控制实现(Role-based access control )
kubernetes 中所有资源对象都是模块化的 API 对象,允许执行 CRUD (Create, Read, Update, Delete) 操作:
资源:pods,configmaps,deployments,nodes,secrets,namespaces,services 等
动作:create,get,delete,list,update,edit,watch,exec,patch
Role:表示一组规则权限,权限只会增加(累加权限),它定义的规则,只适用于单个 namespace
ClusterRole:集群级别的权限控制
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: my-role
namespace: my-ns
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get","watch","list"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get","list","create","update","patch","delete","watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: my-clusterrole
rules:
- apiGroups: [""]
resources: ["services"]
verbs: ["get","create","list"]
系统默认的 ClusterRole: cluster-admin,具有所有资源的管理权限。
[root@masternew ~]# kubectl get clusterrole cluster-admin -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: "2023-12-21T09:08:53Z"
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: cluster-admin
resourceVersion: "70"
uid: 77b7ed9c-ec34-4cf1-91c5-308cdf902670
rules:
- apiGroups:
- '*'
resources:
- '*'
verbs:
- '*'
- nonResourceURLs:
- '*'
verbs:
- '*'
Subject
尝试访问和操作 API Server 的对象:
1、UserAccount:集群外的用户账号
2、Group:用户组,集群中有一些默认创建的组,比如 cluster-admin
3、ServiceAccount:集群内的服务账号,它和 namespace 进行关联,适用于集群内部运行的应用程序,需要通过 API 来完成权限认证,所以在集群内部进行权限操作,我们都需要使用到 ServiceAccount
RoleBinding:将 Role 或 ClusterRole 授权给 Subject,它与 namespace 绑定
ClusterRoleBinding:将 ClusterRole 授权给 Subject,属于集群范围内的授权,与 namespace 无关
# 绑定 Role
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: my-rolebinding-1
namespace: my-ns
subjects:
- kind: User # 权限资源类型
name: mpp # 名称
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: my-role
apiGroup: rbac.authorization.k8s.io
# 绑定 ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: my-rolebinding-2
namespace: my-ns
subjects:
- kind: User
name: mpp
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: my-clusterrole
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: my-clusterrolebinding
subjects:
- kind: Group
name: developer
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: my-clusterrole
apiGroup: rbac.authorization.k8s.io
12、Helm
Helm 是一个 Kubernetes 应用的包管理工具,类似于 Ubuntu 的 APT 和 CentOS 中的 YUM。Helm使用chart 来封装kubernetes应用的 YAML 文件,我们只需要设置自己的参数,就可以实现自动化的快速部署应用。Helm通过打包的方式,支持发布的版本管理和控制,很大程度上简化了Kubernetes应用的部署和管理。Helm有一个跟docker Hub类似的应用中心(https://artifacthub.io/),我们可以在里面找到我们需要部署的应用。
heml中文官方文档:https://helm.sh/zh/docs/intro/install/
wget https://get.helm.sh/helm-v3.10.0-linux-amd64.tar.gz 下载安装包
tar -zxvf helm-v3.10.0-linux-amd64.tar.gz 解压压缩包
[root@masternew ~]# cd linux-amd64/
[root@masternew linux-amd64]# ls
helm LICENSE README.md
[root@masternew linux-amd64]# mv helm /usr/local/bin 在解压目中找到helm程序,移动到需要的目录中
[root@masternew ~]# helm version 查看版本信息代表安装成功
version.BuildInfo{Version:"v3.10.0", GitCommit:"ce66412a723e4d89555dc67217607c6579ffcb21", GitTreeState:"clean", GoVersion:"go1.18.6"}
# 添加仓库,仓库名bitnami 后面是仓库地址
helm repo add bitnami https://charts.bitnami.com/bitnami
添加微软的chart仓库, helm repo add weiruan http://mirror.azure.cn/kubernetes/charts/
helm repo list 查看仓库
helm repo remove weiruan 删除仓库
1、Chart 代表着 Helm 包。它包含运行应用程序需要的所有资源定义和依赖,相当于模版。类似于maven中的pom.xml、Apt中的dpkb或 Yum中的RPM。
2、Repository(仓库) 用来存放和共享 charts。不用的应用放在不同的仓库中。
3、Release 是运行 chart 的实例。一个 chart 通常可以在同一个集群中安装多次。每一次安装都会创建一个新的 release,release name不能重复。
# helm安装项目之后,它会帮我们生成一个详细的使用文档
[root@masternew ~]# helm install my-mysql --set-string auth.rootPassword="123456" bitnami/mysql
NAME: my-mysql
LAST DEPLOYED: Tue Jan 16 08:39:04 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
CHART NAME: mysql
CHART VERSION: 9.16.2
APP VERSION: 8.0.35
** Please be patient while the chart is being deployed **
Tip:
Watch the deployment status using the command: kubectl get pods -w --namespace default
Services:
# 在k8s网络中如何访问该服务
echo Primary: my-mysql.default.svc.cluster.local:3306
Execute the following to get the administrator credentials:
echo Username: root
MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace default my-mysql -o jsonpath="{.data.mysql-root-password}" | base64 -d)
To connect to your database:
# 连接到数据库的方式
1. Run a pod that you can use as a client:
kubectl run my-mysql-client --rm --tty -i --restart='Never' --image docker.io/bitnami/mysql:8.0.35-debian-11-r2 --namespace default --env MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD --command -- bash
2. To connect to primary service (read/write):
mysql -h my-mysql.default.svc.cluster.local -uroot -p"$MYSQL_ROOT_PASSWORD"
[root@masternew ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
my-mysql-0 1/1 Running 0 3m13s
[root@masternew ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 25d
my-mysql ClusterIP 10.96.175.20 <none> 3306/TCP 3m20s
my-mysql-headless ClusterIP None <none> 3306/TCP 3m20s
[root@masternew ~]# helm ls Release 是运行 chart 的实例
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
my-mysql default 1 2024-01-16 08:39:04.957313914 +0800 CST deployed mysql-9.16.2 8.0.35
[root@masternew ~]# MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace default my-mysql -o jsonpath="{.data.mysql-root-password}" | base64 -d)
[root@masternew ~]# kubectl run my-mysql-client --rm --tty -i --restart='Never' --image docker.io/bitnami/mysql:8.0.35-debian-11-r2 --namespace default --env MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD --command -- bash
If you don't see a command prompt, try pressing enter.
I have no name!@my-mysql-client:/$ mysql -h my-mysql.default.svc.cluster.local -uroot -p"$MYSQL_ROOT_PASSWORD"
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 66
Server version: 8.0.35 Source distribution
Copyright (c) 2000, 2023, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
[root@masternew ~]# helm create hello-world
Creating hello-world
[root@masternew ~]# cd hello-world
[root@masternew hello-world]# ll
total 8
drwxr-xr-x 2 root root 6 Jan 15 22:35 charts # 当前我们创建的这个charts是否依赖与其他的charts,如果有就在这里面
-rw-r--r-- 1 root root 1147 Jan 15 22:35 Chart.yaml # charts信息
drwxr-xr-x 3 root root 162 Jan 15 22:35 templates # k8s 资源文件, 模版 {{ 插值 }}
-rw-r--r-- 1 root root 1878 Jan 15 22:35 values.yaml # 具体的值
# 本质里面就是一堆k8s的资源文件,helm启动这个charts,相当于帮我们执行了apply -f
[root@masternew hello-world]# cd templates/
[root@masternew templates]# ls
deployment.yaml _helpers.tpl hpa.yaml ingress.yaml NOTES.txt serviceaccount.yaml service.yaml tests
# 检测 charts 包是否有问题
[root@masternew ~]# helm lint ./hello-world/
==> Linting ./hello-world/
[INFO] Chart.yaml: icon is recommended
1 chart(s) linted, 0 chart(s) failed
[root@masternew ~]# helm install --help 求帮助
helm install [NAME] [CHART] [flags]
[root@masternew ~]# helm install hello hello-world 安装
[root@masternew ~]# helm ls 查看
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
hello default 1 2024-01-16 08:54:45.268349533 +0800 CST deployed hello-world-0.1.0 1.16.0
[root@masternew ~]# helm delete hello 删除
可以直接通过仓库找到charts来安装
也可以自己编写charts包来安装,从git下载 charts
Helm部署MySQL主从复制集群,https://artifacthub.io/packages/helm/bitnami/mysql
安装过程中有两种方式传递配置数据:
1、-f (或–values):使用 YAML 文件覆盖默认配置。可以指定多次,优先使用最右边的文件。
2、–set:通过命令行的方式对指定项进行覆盖。
一主primary两从secondary
[root@masternew ~]# cat mysql-helm.yaml
auth:
rootPassword: "123456"
primary:
persistence:
size: 2Gi
enabled: true
secondary:
replicaCount: 2
persistence:
size: 2Gi
enabled: true
architecture: replication
如果同时使用两种方式,则 --set中的值会被合并到 -f中,但是 --set中的值优先级更高。
[root@masternew ~]# helm install my-db -f mysql-helm.yaml bitnami/mysql
NAME: my-db
LAST DEPLOYED: Tue Jan 16 09:10:06 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
CHART NAME: mysql
CHART VERSION: 9.16.2
APP VERSION: 8.0.35
** Please be patient while the chart is being deployed **
Tip:
Watch the deployment status using the command: kubectl get pods -w --namespace default
Services:
echo Primary: my-db-mysql-primary.default.svc.cluster.local:3306
echo Secondary: my-db-mysql-secondary.default.svc.cluster.local:3306
Execute the following to get the administrator credentials:
echo Username: root
MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace default my-db-mysql -o jsonpath="{.data.mysql-root-password}" | base64 -d)
To connect to your database:
1. Run a pod that you can use as a client:
kubectl run my-db-mysql-client --rm --tty -i --restart='Never' --image docker.io/bitnami/mysql:8.0.35-debian-11-r2 --namespace default --env MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD --command -- bash
2. To connect to primary service (read/write):
mysql -h my-db-mysql-primary.default.svc.cluster.local -uroot -p"$MYSQL_ROOT_PASSWORD"
3. To connect to secondary service (read-only):
mysql -h my-db-mysql-secondary.default.svc.cluster.local -uroot -p"$MYSQL_ROOT_PASSWORD"
主从复制原理:通过部署无头服务(Headless Service)将写操作指向固定的数据库。部署一个Service用来做读操作的负载均衡。数据库之间通过同步程序保持数据一致。
初始化容器(Init Containers)是一种特殊容器,它在Pod内的应用容器启动之前运行。初始化容器未执行完毕或以错误状态退出,Pod内的应用容器不会启动。初始化容器需要在initContainers中定义,与containers同级。
基于上面的特性,初始化容器通常用于:
1、生成配置文件2、执行初始化命令或脚本3、执行健康检查(检查依赖的服务是否处于Ready或健康Health的状态)
这里有两个初始化容器:
1、init-mysql为MySQL实例分配server-id,并将mysql-0的配置文件设置为primary.cnf,其他副本设置为replica.cnf
2、clone-mysql从前一个Pod中获取备份的数据文件放到自己的数据目录下
Pod中运行了2个容器,MySQL 容器和一个充当辅助工具的 xtrabackup 容器,我们称之为边车(sidecar)。Xtrabackup是一个开源的MySQL备份工具,支持在线热备份(备份时不影响数据读写),是目前各个云厂商普遍使用的MySQL备份工具。sidecar容器负责将备份的数据文件发送给下一个Pod,并在副本服务器初次启动时,使用数据文件完成数据的导入。
MySQL使用bin-log同步数据,但是,当数据库运行一段时间后,产生了一些数据,这时候如果我们进行扩容,创建了一个新的副本,有可能追溯不到bin-log的源头(可能被手动清理或者过期自动删除),因此需要将现有的数据导入到副本之后,再开启数据同步,sidecar只负责数据库初次启动时完成历史数据导入,后续的数据MySQL会自动同步。
[root@k8s-master ~]# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/my-db-mysql-primary-0 1/1 Running 0 15m
pod/my-db-mysql-secondary-0 1/1 Running 0 15m
pod/my-db-mysql-secondary-1 1/1 Running 0 13m
pod/nginx 1/1 Running 1 (<invalid> ago) 91m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d17h
service/my-db-mysql-primary ClusterIP 10.96.140.100 <none> 3306/TCP 15m
service/my-db-mysql-primary-headless ClusterIP None <none> 3306/TCP 15m
service/my-db-mysql-secondary ClusterIP 10.96.64.24 <none> 3306/TCP 15m
service/my-db-mysql-secondary-headless ClusterIP None <none> 3306/TCP 15m
NAME READY AGE
statefulset.apps/my-db-mysql-primary 1/1 15m
statefulset.apps/my-db-mysql-secondary 2/2 15m
[root@k8s-master ~]# kubectl port-forward --help
# Listen on port 8888 on localhost and selected IP, forwarding to 5000 in the pod
kubectl port-forward --address localhost,10.19.21.23 pod/mypod 8888:5000
端口转发,临时暴露服务。这个命令是前台命令,退出后,端口转发就失效了,常用来做测试。
可用数据库连接工具连接10.1.1.111 33060,这个前台命令按ctrl+c中断后就不能连接了。
[root@k8s-master ~]# kubectl port-forward pod/my-db-mysql-primary-0 --address=10.1.1.111 33060:3306
Forwarding from 192.168.0.111:33060 -> 3306
Handling connection for 33060
Handling connection for 33060
更多推荐
所有评论(0)