1. K8S 概览
1.1 K8S 是什么
K8S官网文档:https://kubernetes.io/zh/docs/home/
1.2 K8S核心特性
服务发现与负载均衡
:无需修改你的应用程序即可使用陌生的服务发现机制。存储编排
:自动挂载所选存储系统,包括本地存储。Secret和配置管理
:部署更新Secrets和应用程序的配置时不必重新构建容器镜像,且不必将软件堆栈配置中的秘密信息暴露出来。批量执行
:除了服务之外,Kubernetes还可以管理你的批处理和CI工作负载,在期望时替换掉失效的容器。水平扩缩
:使用一个简单的命令、一个UI或基于CPU使用情况自动对应用程序进行扩缩。自动化上线和回滚
:Kubernetes会分步骤地将针对应用或其配置的更改上线,同时监视应用程序运行状况以确保你不会同时终止所有实例。自动装箱
:根据资源需求和其他约束自动放置容器,同时避免影响可用性。自我修复
:重新启动失败的容器,在节点死亡时替换并重新调度容器,杀死不响应用户定义的健康检查的容器。
1.3 K8S 核心架构
我们已经知道了 K8S 的核心功能:自动化运维管理多个容器化程序。那么 K8S 怎么做到的呢?这里,我们从宏观架构上来学习 K8S 的设计思想。首先看下图:
K8S 是属于Master-Worker架构
,即有 Master 节点负责核心的调度、管理和运维,Worker 节点则
执行用户的程序。但是在 K8S 中,主节点一般被称为Master Node
,而从节点则被称为Worker Node
或者 Node。
注意
:Master Node 和 Worker Node 是分别安装了 K8S 的 Master 和 Woker 组件的实体服务器,
每个 Node 都对应了一台实体服务器(虽然 Master Node 可以和其中一个 Worker Node 安装在同
一台服务器,但是建议 Master Node 单独部署),所有 Master Node 和 Worker Node 组成了
K8S 集群,同一个集群可能存在多个 Master Node 和 Worker Node
。
首先来看Master Node都有哪些组件:
kube-apiserver
。K8S 的请求入口服务。API Server 负责接收 K8S 所有请求(来自 UI 界面或者CLI命令行工具),然后,API Server 根据用户的具体请求,去通知其他组件干活。Schedule
r。K8S 所有 Worker Node 的调度器。当用户要部署服务时,Scheduler 会选择最合适的 Worker Node(服务器)来部署。Controller Manager
。K8S 所有 Worker Node 的监控器。Controller Manager 有很多具体的
Controller,Node Controller、Service Controller、Volume Controller等。Controller 负责监控和调整在 Worker Node上部署的服务的状态,比如用户要求 A 服务部署 2个副本,那么当其中一个服务挂了的时候,Controller 会马上调整,让 Scheduler 再选择一个 Worker Node 重新部署服务。etcd。K8S 的存储服务
。etcd 存储了K8S 的关键配置和用户配置
,K8S 中仅 API Server
才具备读写权限,其他组件必须通过 API Server 的接口才能读写数据。
接着来看Worker Node的组件:
Kubelet。Worker Node 的监视器,以及与 Master Node 的通讯器
。Kubelet 是 Master Node
安插在 Worker Node 上的"眼线",它会定期向 Master Node 汇报自己 Node上运行的服务的状态,并接受来自Master Node 的指示采取调整措施。负责控制所有容器的启动停止,保证节点工作正常。Kube-Proxy
。K8S 的网络代理。Kube-Proxy 负责 Node 在 K8S 的网络通讯、以及对外部网络流量的负载均衡。Container Runtime。Worker Node 的运行环境
。即安装了容器化所需的软件环境确保容器化程序能够跑起来,比如 Docker Engine运行环境
2、K8S安装教程
视频地址:图灵K8S
2.1. 安装要求
- 3台以上机器,操作系统 CentOS7.7以上64位系统
- 硬件配置:2GB或更多RAM,2个CPU或更多CPU,硬盘30GB或更多
- 集群中所有机器之间网络互通
- 可以访问外网,需要拉取镜像
- 禁止swap分区
2.2. centos7兼容版本(7.7~7.9)
2.3. 三台主机
搭建k8s集群,三台主机,每台至少2G以上
主机 说明
主机 | 说明 |
---|---|
192.168.23.201 | k8s-master |
192.168.23.202 | k8s-node1 |
192.168.23.203 | k8s-node2 |
备注:本次装机选用k8s 1.19.16版本,dokcer1.18.09版本
主机安装参考链接,这里只参考第六集
2.4. 安装docker(三台主机都需要安装)
powershell
#卸载旧版本
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
sudo yum update -y
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
#配置docker的yum地址
sudo yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
#安装指定docker版本
sudo yum -y install docker-ce-18.09.9 docker-ce-cli-18.09.9 containerd.io
#启动dokcer并开机自启
sudo systemctl start docker
sudo systemctl enable docker
#Docker配置修改
vim /etc/docker/daemon.json
{
"graph": "/data/docker",
"registry-mirrors": [
"https://xv8xjvpp.mirror.aliyuncs.com",
"https://docker.m.daocloud.io/",
"https://dockerproxy.com/",
"https://mirror.baidubce.com/",
"https://docker.nju.edu.cn/",
"https://ccr.ccs.tencentyun.com/"
],
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
#加载配置文件并重启docker
sudo systemctl daemon-reload
sudo systemctl restart docker
常见错误
如果yum update失败,需要进行更换镜像,下面以清华镜像为例, 三台机器都要操作
- 打开配置yum所在文件,CentOS-Base.repo
powershell
vi /etc/yum.repos.d/CentOS-Base.repo
- 将CentOS-Base.repo里的内容全部替换为以下内容
xml
# CentOS-Base.repo
[base]
name=CentOS-$releasever - Base
baseurl=http://mirrors.aliyun.com/centos/$releasever/os/$basearch/
http://mirrors.tuna.tsinghua.edu.cn/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
#released updates
[updates]
name=CentOS-$releasever - Updates
baseurl=http://mirrors.aliyun.com/centos/$releasever/updates/$basearch/
http://mirrors.tuna.tsinghua.edu.cn/centos/$releasever/updates/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
#additional packages that may be useful
[extras]
name=CentOS-$releasever - Extras
baseurl=http://mirrors.aliyun.com/centos/$releasever/extras/$basearch/
http://mirrors.tuna.tsinghua.edu.cn/centos/$releasever/extras/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-$releasever - Plus
baseurl=http://mirrors.aliyun.com/centos/$releasever/centosplus/$basearch/
http://mirrors.tuna.tsinghua.edu.cn/centos/$releasever/centosplus/$basearch/
gpgcheck=1
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
#contrib - packages by Centos Users
[contrib]
name=CentOS-$releasever - Contrib
baseurl=http://mirrors.aliyun.com/centos/$releasever/contrib/$basearch/
http://mirrors.tuna.tsinghua.edu.cn/centos/$releasever/contrib/$basearch/
gpgcheck=1
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
# 使用failovermethod指定优先级,这里设置为随机(roundrobin),也可以改为priority并指定每个URL的优先级
failovermethod=roundrobin
- 清理YUM缓存并更新,更新完之后再安装Docker
xml
yum clean all
yum makecache
yum update
2.5. 主机环境配置(三台都需要)
powershell
#1.关闭防火墙
sudo systemctl stop firewalld
sudo systemctl disable firewalld
#2.关闭selinux
#selinux禁用
setenforce 0
sed -i 's/enforcing/disabled/' /etc/selinux/config
#2.关闭swap分区
swapoff -a
vim /etc/fstab # 注释到swap那一行 永久关闭
#3.设置主机名
hostnamectl set-hostname k8s-master
hostnamectl set-hostname k8s-node1
hostnamectl set-hostname k8s-node2
#4.添加host,IP需要改成你自己机器的IP
cat >> /etc/hosts << EOF
192.168.23.201 k8s-master
192.168.23.202 k8s-node1
192.168.23.203 k8s-node2
EOF
#5.允许iptables 检查桥接流量
cat > /etc/sysctl.d/k8s.conf << EOF
net.ipv4.ip_forward = 1
net.ipv4.tcp_tw_recycle = 0
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system
#6.设置时间同步
yum install -y ntpdate
ntpdate time.windows.com
2.6. 安装kubelet,kubeadm,kubectl(三台机器都需要执行)
powershell
#1.配置k8s的yum源
cat <<EOF | sudo tee /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
#2.如果之前安装了k8s,先卸载旧版本
yum -y remove kubelet kubeadm kubectl
#3.查看可以安装的版本
#可以将 kubelet 替换为 kubeadm 或 kubectl,以查看相应组件的可用版本
yum list --showduplicates kubelet | sort -r
#4,安装指定的kubelet,kubeadm,kubectl版本
sudo yum install -y kubelet-1.19.16 kubeadm-1.19.16 kubectl-1.19.16
# 启动kubelet并卡机自启
sudo systemctl start kubelet
sudo systemctl enable kubelet
#检查k8s是否启动
sudo systemctl status kubelet
#排查错误
journalctl -xefu kubelet
#master主机上执行
# 下载镜像。
# 此镜像 kubernetes 的仓库中,由于网络原因,无法连接,下面提供了一种替换方案。
images=(
kube-apiserver:v1.19.16
kube-controller-manager:v1.19.16
kube-scheduler:v1.19.16
kube-proxy:v1.19.16
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
kubeadm init \
--apiserver-advertise-address=192.168.23.201 \
--image-repository=registry.aliyuncs.com/google_containers \
--kubernetes-version=v1.19.16 \
--pod-network-cidr=192.168.0.0/16 \
--service-cidr=10.96.0.0/12
#master主机上执行
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
export KUBECONFIG=/etc/kubernetes/admin.conf
#手动拉取(三台机器都要)
docker pull docker.io/calico/cni:v3.20.6
docker pull docker.io/calico/kube-controllers:v3.20.6
docker pull docker.io/calico/node:v3.20.6
#安装网络插件 Calico(仅master主机需要)
#下载calico.yaml
mkdir /root/k8s
cd /root/k8s
wget https://docs.projectcalico.org/v3.20/manifests/calico.yaml
kubectl apply -f calico.yaml
#申请一个新令牌
kubeadm token create --print-join-command
#加入node节
#复制新生成的令牌
kubeadm join 192.168.23.201:6443 --token xxxx --discovery-token-ca-cert-hash sha256:xxxxx
三个节点都是Ready
表示搭建成功
3. K8S基础命令
3.1 kubectl命令使用
使用kubectl 查看
xml
Cluster Management Commands:
certificate 修改 certificate 资源.
cluster-info 显示集群信息
top Display Resource (CPU/Memory/Storage) usage.
cordon 标记 node 为 unschedulable
uncordon 标记 node 为 schedulable
drain Drain node in preparation for maintenance
taint 更新一个或者多个 node 上的 taints
Troubleshooting and Debugging Commands:
describe 显示一个指定 resource 或者 group 的 resources 详情
logs 输出容器在 pod 中的日志
attach Attach 到一个运行中的 container
exec 在一个 container 中执行一个命令
port-forward Forward one or more local ports to a pod
proxy 运行一个 proxy 到 Kubernetes API server
cp 复制 files 和 directories 到 containers 和从容器中复制 files 和 directories.
auth Inspect authorization
Advanced Commands:
diff Diff live version against would-be applied version
apply 通过文件名或标准输入流(stdin)对资源进行配置
patch 使用 strategic merge patch 更新一个资源的 field(s)
replace 通过 filename 或者 stdin替换一个资源
wait Experimental: Wait for a specific condition on one or many resources.
convert 在不同的 API versions 转换配置文件
kustomize Build a kustomization target from a directory or a remote url.
Settings Commands:
label 更新在这个资源上的 labels
annotate 更新一个资源的注解
completion Output shell completion code for the specified shell (bash or zsh)
Other Commands:
alpha Commands for features in alpha
api-resources Print the supported API resources on the server
api-versions Print the supported API versions on the server, in the form of "group/version"
config 修改 kubeconfig 文件
plugin Provides utilities for interacting with plugins.
version 输出 client 和 server 的版本信息
3.2 Namespace
k8s中,命名空间(Namespace) 提供一种机制同一集群中的资源划分为相互隔离的组,同一命名空间内的资源名称要唯一,命名空间是用来隔离资源的,不隔离网路。
kubernetes启动时会创建四个初始命名空间:
-
default
kubernetes包含这个命名空间,以便你无需创建新的命名空间即可开始使用新集群
-
kube-node-lease
该命名空间包含用于各个节点关联的Lease(租约)对象。节点租约允许kubelet发送心跳,由此控制能够检测到节点故障。
-
kube-public
所有的客户端(包括未经身份验证的客户端)都可以读取该命名空间。该命名空间主要预留未集群使用,以便某些资源需要在整个集群中可见可读。该命名空间的公属性是一种约定而非要求。
-
kube-system
该命名空间使用kubernetes系统创建的对象。
xml
#查看namespace
kubectl get namespace
#查看kube-system下的pod
kubectl get pods -n kube-system
#查看所有namespace下的pod
kubectl get pods -A
创建Namespace
- 命令行方式
xml
kubectl create namespace XXXXX
- yaml方式
新建一个名为
my-namespace.yaml 的 YAML 文件,并写入下列内容:
xml
#XXXXX.yaml
apiVersion: v1
kind: Namespace
metadata:
name: my-namespace
然后运行:
xml
kubectl create -f XXXXX.yaml
删除namespace
xml
kubectl delete namespace XXXXX
kubectl delete -f XXXXX.yaml
3.3 Pod
创建Pod示例:运行一个NGINX容器
命令方式
yaml
#创建pod
kubectl run mynginx --image=nginx:1.14
yaml
#获取pod的信息,-owide 表示更详细的显示信息 -n 命名空间 查询对应namespace下的pod
kubectl get pod
kubectl get pod -owide
kubectl get pod -n <namespace-name>
yaml
#查看pod的详情
kubctl describe pod <pod-name>
yaml
#查看pod的运行日志
kubectl logs <pod-name>
#删除pod
kubectl delete pod <pod-name>
yaml方式
yaml
#nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: mynginx
name: mynginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
部署成功访问nginx
3.4 Deployment
Deployment负责创建和更新应用程序的实例,使Pod拥有多副本,自愈,扩容等能力
。创建Deployment后,kubernetes master将应用程序实列调整到集群中的各个节点上。如果托管实列的节点关闭或被删除,Deployment控制器会将该实例替换为集群中另一个节点上的实例,这提供了一种自我修复机制来解决机器故障维护问腿。
创建一个Tomcat应用程序
使用 kubectl create deployment 命令可以创建一个应用部署deployment与pod
yaml
#my-tomcat表示pod的名称 --image表示镜像的地址
kubectl create deployment my-tomcat --image=tomcat:9.0.55
#查看一下deployment的信息
kubectl get deployment
#删除deployment
kubectl delete deployment my-tomcat
#查看Pod打印的日志
kubectl logs my-tomcat-6d6b57c8c8-n5gm4
#使用 exec 可以在Pod的容器中执行命令
kubectl exec my-tomcat-6d6b57c8c8-n5gm4 -- env #使用 env 命令查看环境变量
kubectl exec my-tomcat-6d6b57c8c8-n5gm4 -- ls / # 查看容器的根目录下面内容
kubectl exec my-tomcat-6d6b57c8c8-n5gm4 -- sh #进入Pod容器内部并执行bash命令,如果想退出
容器可以使用exit命令
思考:下面两个命令有什么区别?
yaml
kubectl create my-tomcat --image=tomcat:9.0.55
kubectl create deployment my-tomcat --image=tomcat:9.0.55
自愈
现在我们来删除刚刚添加的pod,看看会发生什么
yaml
#查看pod信息,-w意思是一直等待观察pod信息的变动
kubectl get pod -w
开另外一个命令窗口执行如下命令,同时观察之前命令窗口的变化情况
yaml
kubectl delete pod my-tomcat-6d6b57c8c8-n5gm4
我们可以看到之前那个tomcat的pod被销毁,但是又重新启动了一个新的tomcat pod,
这是k8s的服务自愈功能
,不需要运维人员干预
多副本
命令行的方式
yaml
# 创建3个副本
kubectl create deployment my-tomcat --image=tomcat:9.0.55 --replicas=3
yaml方式
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-deployment
labels:
app: tomcat-deployment
spec:
replicas: 3 # 定义了 3 个副本
selector:
matchLabels:
app: tomcat-deployment
template:
metadata:
labels:
app: tomcat-deployment
spec:
containers:
- name: tomcat
image: tomcat:9.0.55
扩缩容
yaml
# 扩容到5个pod
kubectl scale --replicas=5 deployment my-tomcat
# 缩到3个pod
kubectl scale --replicas=3 deployment my-tomcat
#修改 replicas
kubectl edit deployment my-tomcat
滚动升级与回滚
对my-tomcat这个deployment进行滚动升级和回滚,将tomcat版本由tomcat:9.0.55升级到
tomcat:10.1.11,再回滚到tomcat:9.0.55
滚动升级:
yaml
kubectl set image deployment my-tomcat tomcat=tomcat:10.1.11 --record
可以执行 kubectl get pod -w 观察pod的变动情况,可以看到有的pod在销毁,有的pod在创建
查看pod信息
yaml
kubectl get pod
查看某个pod的详细信息,发现pod里的镜像版本已经升级了
yaml
kubectl describe pod my-tomcat-85c5c8f685-lnkfm
版本回滚:
查看历史版本
yaml
kubectl rollout history deploy my-tomcat
回滚到上一个版本
yaml
kubectl rollout undo deployment my-tomcat #--to-revision 参数可以指定回退的版本
#回滚(回到指定版本)
kubectl rollout undo deployment/my-dep --to-revision=2
查看pod详情,发现版本已经回退了
访问tomcat pod
集群内访问(在集群里任一worker节点都可以访问)
yaml
curl 192.168.36.74:8080
集群外部访问
当我们在集群之外访问是发现无法访问,那么集群之外的客户端如何才能访问呢?这就需要我们的
service服务了,下面我们就创建一个service,使外部客户端可以访问我们的pod
3.5 Service
创建service示例
命令行形式
yaml
kubectl expose deployment my-tomcat --port=8080 --type=NodePort
yaml
#查看service信息,port信息冒号后面的端口号就是对集群外暴露的访问接口
#NodePort范围在 30000-32767之间
kubectl get svc -owide
集群外部访问
使用集群节点的ip加上暴露的端口就可以访问
tomcat版本太高返回404的解决办法:进入tomcat容器,把 webapps 目录删除,再
把 webapps.dist 重命名为 webapps 即可。
yaml形式
yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: my-tomcat
name: my-tomcat # Service 的名称
spec:
ports:
- port: 8080 #service的虚拟ip对应的端口,在集群内网机器可以访问用service的虚拟ip加该端口号访问
nodePort: 30001 #Service在宿主主机上映射的外网访问端口,端口范围必须在30000~32767
protocol: TCP
port: 80 # Service 暴露的端口
targetPort: 8080 # 被暴露的 Pod 的容器端口,一般与pod内部容器暴露的端口一致
selector:
app: my-tomcat
type: NodePort # Service 类型,可以是 ClusterIP、NodePort、LoadBalancer 等
3.6 存储
Volume
Volume指的是存储卷,包含可被Pod中容器访问的数据目录。容器中的文件在磁盘上是临时存放的,当容器崩溃时文件会丢失,同事无法在多个Pod中共享文件,通过使用存储卷可以解决这问题。
kubernetes 支持很多类型的卷。Pod可以同时使用任意数目的卷类型。临时卷类型的生命周期与Pod相同,但持久卷可以比Pod的存活期长。当Pod不再存在时,kubernetes 也会销毁临时卷;不过kubernetes 不会销毁永久卷。对于给定的Pod中任何类型的卷,在容器重启期间数据都不会丢失。
卷的核心就是一个目录,其中可能存有数据,Pod中的容器可以访问该目录中的数据。所采用的不同卷类型将决定该目录如何形成的、使用何种介质保存数据以及目录中存放的东西。常见有confingMap、emptyDir、local、nfs、secret
等。
- ConfingMap:可以将配置文件以键值对的形式保存到ConfigMap中,并且可以在Pod中以文件或环境变量的形式使用。ConfigMap可以用来存储不敏感的配置信息,如应用程序的配置文件。
- EmptyDir:是一个空目录,可以在Pod用来存储临时数据,当Pod删除时,该目录也会删除。
- Local:将本地文件系统的目录或文件映射到Pod中的一个Volume中,可以用来在Pod中共享文件或数据。
- NFS:将网络上的一个或多个NFS共享目录挂载到Pod中的Volume中,可以用来在多个Pod之间共享数据。
- Secret:将敏感信息以密文的形式保存到Secret中,并且可以在Pod中以文件或环境变量的形式使用。Secret可以用来存储敏感信息,如用户的密码、证书等。
使用方式
使用卷时, 在 .spec.volumes 字段中设置为 Pod 提供的卷,并在 .spec.containers[*].volumeMounts
字段中声明卷在容器中的挂载位置。 容器中的进程看到的文件系统视图是由它们的容器镜像的初始内
容以及挂载在容器中的卷(如果定义了的话)所组成的。 其中根文件系统同容器镜像的内容相吻合。
任何在该文件系统下的写入操作,如果被允许的话,都会影响接下来容器中进程访问文件系统时所看
到的内容。
搭建nfs文件系统
nfs(network filesystem ): 网络文件存储系统
安装nfs-server
yaml
# 在每个机器。
yum install -y nfs-utils
yaml
# 在master 执行以下命令
echo "/nfs/data/ *(insecure,rw,sync,no_root_squash)" > /etc/exports
# 执行以下命令,启动 nfs 服务;创建共享目录
mkdir -p /nfs/data
# 在master执行10
systemctl enable rpcbind
systemctl enable nfs-server
systemctl start rpcbind
systemctl start nfs-server
# 使配置生效
exportfs -r
#检查配置是否生效
exportfs
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-pv-demo
name: nginx-pv-demo
spec:
replicas: 2
selector:
matchLabels:
app: nginx-pv-demo
template:
metadata:
labels:
app: nginx-pv-demo
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
nfs:
server: 192.168.23.201
path: /nfs/data/nginx-pv
测试通过,实现挂载
PV & PVC
基本使用
静态供应示例
创建PV池
yaml
#nfs主节点1
mkdir -p /nfs/data/01
mkdir -p /nfs/data/02
mkdir -p /nfs/data/03
创建PV
yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv01-10m
spec:
capacity:
storage: 10M
accessModes:
- ReadWriteMany
storageClassName: nfs
nfs:
path: /nfs/data/01
server: 192.168.23.201
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv02-1gi
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
storageClassName: nfs
nfs:
path: /nfs/data/02
server: 192.168.23.201
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv03-3gi
spec:
capacity:
storage: 3Gi
accessModes:
- ReadWriteMany
storageClassName: nfs
nfs:
path: /nfs/data/03
server: 192.168.23.201
创建PVC
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nginx-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 200Mi
storageClassName: nfs
自动分配1G的
创建Pod绑定PVC
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
动态供应
3.7 配置
ConfigMap
在 Kubernetes 中,ConfigMap 是一种用于存储非敏感信息的 Kubernetes 对象
。它用于存储配置
数据,如键值对、整个配置文件或 JSON 数据等。ConfigMap 通常用于容器镜像中的配置文件、命令
行参数和环境变量等。
ConfigMap 可以通过三种方式进行配置数据的注入:
环境变量注入
:将配置数据注入到 Pod 中的容器环境变量中。配置文件注入
:将配置数据注入到 Pod 中的容器文件系统中,容器可以读取这些文件。命令行参数注入
:将配置数据注入到容器的命令行参数中。
优点
- 避免了硬编码,将配置数据与应用代码分离。
- 便于维护和更新,可以单独修改 ConfigMap 而不需要重新构建镜像。
- 可以通过多种方式注入配置数据,更加灵活。
- 可以通过 Kubernetes 的自动化机制对 ConfigMap 进行版本控制和回滚。
- ConfigMap 可以被多个 Pod 共享,减少了配置数据的重复存储。
定义 ConfigMap
基本操作
yaml
# 查看 configmap
$ kubectl get configmap/cm
# 查看详细
$ kubectl describe configmap/cm my-config
# 删除 cm
$ kubectl delete cm my-config
- 命令行创建:
可以使用kubectl create configmap命令来创建configmap,具体命令如下:
yaml
kubectl create configmap my-config --from-literal=key1=value1 --from
literal=key2=value2
通过配置文件创建:推荐
可以通过创建YAML文件的方式来定义configmap的内容。例如,创建一个名为
内容如下:
- 然后使用kubectl apply -f命令来创建configmap。
通过文件创建:
yaml
echo-n admin >./username
echo -n 123456 > ./password
kubectl create configmap myconfigmap --from-file=./username --from-file=./password
通过文件夹创建:
可以将多个配置文件放在同一个文件夹下,然后使用如:kubectl create configmap命令来创建configmap
yaml
kubectl create configmap my-config --from-file=config-files/
这将创建一个名为my-config的configmap,其中包含config-files/文件夹下所有的文件内容作为键值对。
通过环境变量创建:
可以将环境变量的值转换为configmap。例如,使用以下命令将当前环境变量的值转换为configmap:
yaml
kubectl create configmap my-config --from-env-file=<env>
使用示例
yaml
# docker安装redis
docker run -v /data/redis/redis.conf:/etc/redis/redis.conf \
-v /data/redis/data:/data \
-d --name myredis \
-p 6379:6379 \
redis:latest redis-server /etc/redis/redis.conf
创建ConfigMap
通过文件的方式创建
yaml
#创建redis.conf
daemonize yes
requirepass root
# 创建配置,redis保存到k8s的etcd
kubectl create cm redis-conf --from-file=redis.conf
#查看资源清单
kubectl get cm redis-conf -oyaml
通过yaml的方式创建
yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-conf
data:
redis.conf: |
maxmemory-policy allkeys-lru
requirepass root
创建Pod
yaml
apiVersion: v1
kind: Pod
metadata:
name: redis
spec:
containers:
- name: redis
image: redis
command:
- redis-server
- "/redis-master/redis.conf" #指的是redis容器内部的位置11
# command: ["/bin/bash", "-ce", "tail -f /dev/null"]
ports:
- containerPort: 6379
volumeMounts:
- mountPath: /data
name: data
- mountPath: /redis-master
name: config
volumes:
- name: data
emptyDir: {}
- name: config
configMap:
name: redis-conf
items:
- key: redis.conf
path: redis.conf
测试
yaml
kubectl exec -it redis -- redis-cli
127.0.0.1:6379> config get maxmemory-policy
Secret
定义 Secret
- 使用命令行创建:
可以使用 kubectl create secret 命令来创建 secret,例如
yaml
kubectl create secret generic my-secret --from-literal=username=admin --from-literal=password=admin123
- 使用 YAML 文件定义:
可以创建一个 YAML 文件来定义 Secret 对象,例如
yaml
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
data:
username: YWRtaW4= # base64 编码后的用户名 admin
password: MWYyZDFlMmU2N2Rm # base64 编码后的密码 1f2d1e2e67df
注意: 这个 YAML 文件定义了一个名为 my-secret 的 Secret 对象,其中包含了两个 base64 编码后的
key-value 对:username 和 password。
使用文件创建:
yaml
echo -n admin >./username
echo -n 123456 > ./password
kubectl create secret generic mysecret --from-file=./username --from-file=./password
- 通过环境变量创建:
可以将环境变量的值转换为secret。例如,使用以下命令将当前环境变量的值转换为secret:
yaml
kubectl create secret generic my-config --from-env-file=<env>
使用示例:从私有docker仓库拉取镜像
yaml
docker pull registry.cn-hangzhou.aliyuncs.com/fox666/tulingmall-product:0.0.5
无法从私有镜像仓库拉取镜像,抛出如下错误:
解决方案:使用 docker 的用户信息来生成 secret:
yaml
##命令格式
kubectl create secret docker-registry myregistrykey \
--docker-server=<你的镜像仓库服务器> \
--docker-username=<你的用户名> \
--docker-password=<你的密码> \
--docker-email=<你的邮箱地址>
kubectl create secret docker-registry myregistrykey --docker-server=registry.cn-hangzhou.aliyuncs.com --docker-username=fox666 --docker-password=xxx
在创建 Pod 的时候,通过imagePullSecrets来引用刚创建的myregistrykey
yaml
apiVersion: v1
kind: Pod
metadata:
name: tulingmall-product
spec:
containers:
- name: tulingmall-product
image: registry.cn-hangzhou.aliyuncs.com/fox666/tulingmall-product:0.0.5
imagePullSecrets:
- name: myregistrykey
3.8 Ingress
Ingress 是一种kubernetes资源类型,它允许kubernetes 集群中暴露HTTP和HTTPS服务。通过Ingress,你可以将流量路由到不同的服务和端口点,而无需使用不同的负载均衡器。Ingress通常使用Ingress Contrller实现,它是一个运行在kubernetes 集群中的负载均衡器,它根据Ingress规则下面是一个将所有流量都发送到同一Service的简单Ingress示例:
Ingress和Service区别
Ingress和Service都是kubernetes中用于将流量路由到应用的机制,但他们是在路由层面上有所不同:
- Service是kubernetes中抽象的应用程序服务,它公开了一个单一的IP地址和端口,可以用于在kubernetes集群内部的Pod之间进行流量路由。
- Ingress是一个kubernetes资源对象,它提供了对集群外部路由的规则。Ingress通过一个公共IP地址和端口将流量路由到一个或多个Service。
安装Ingress
yaml
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.47.0/deploy/static/provider/baremetal/deploy.yaml
yaml
#修改镜像
vi deploy.yaml
#1、将image k8s.gcr.io/ingress-nginx/controller:v0.46.0@sha256:52f0058bed0a17ab0fb35628ba97e8d52b5d32299fbc03cc0f6c7b9ff036b61a的值改为如下值:
registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/ingress-nginx-controller:v0.46.0
2)安装ingress,执行如下命令
yaml
kubectl apply -f ingress-controller.yaml
- 查看是否安装成功
yaml
kubectl get pod,svc -n ingress-nginx -owide
使用Ingress
官网地址:
yaml
https://kubernetes.github.io/ingress-nginx/
配置ingress访问规则(就是类似配置nginx的代理转发配置),让ingress将域名tomcat.tuling.com
转发给后端的my-tomcat服务,新建一个文件ingress-tomcat.yaml,内容如下:
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
spec:
rules:
- host: tomcat.tuling.com #转发域名
http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: my-tomcat
port:
number: 8080 #service的端口
执行如下命令生效规则:
yaml
kubectl apply -f ingress-tomcat.yaml
查看生效的ingress规则:
yaml
kubectl get ing
在访问机器配置host,win10客户机在目录:C:\Windows\System32\drivers\etc,在host里增加如
下host(ingress部署的机器ip对应访问的域名)
yaml
192.168.65.82 tomcat.tuling.com
配置完后直接在客户机浏览器访问http://tomcat.tuling.com:30940能正常访问tomcat。
Service&Ingress总结
4. K8S核心原理
4.1 K8S的网络模型
K8S的网络中主要存在4种类型的通信:
- 同一Pod内的容器间通信
- 各个Pod彼此间的通信
- Pod和Service间的通信
集群外部流量和Service之间的通信
K8S为Pod和Service资源对象分别使用了各自的专有网络,Pod网络由K8S的网络插件配置实现,而
Service网络则由K8S集群进行指定。如下图:
K8S使用的网络插件需要为每个Pod配置至少一个特定的地址,即Pod IP。Pod IP地址实际存在于某个
网卡(可以是虚拟机设备)上。
而Service的地址却是一个虚拟IP地址,没有任何网络接口配置在此地址上,它由Kube-proxy借助
iptables规则或ipvs规则重定向到本地端口,再将其调度到后端的Pod对象。Service的IP地址是集群提
供服务的接口,也称为Cluster IP。
Pod网络和IP由K8S的网络插件负责配置和管理,具体使用的网络地址可以在管理配置网络插件时进行
指定,如10.244.0.0/16网络。而Cluster网络和IP是由K8S集群负责配置和管理,如10.96.0.0/12网
络。
从上图进行总结起来,一个K8S集群包含是三个网络。
节点网络
:各主机(Master、Node、ETCD等)自身所属的网络,地址配置在主机的网络接口,用于各主机之间的通信,又称为节点网络。Pod网络
:专用于Pod资源对象的网络,它是一个虚拟网络,用于为各Pod对象设定IP地址等网络参数,其地址配置在Pod中容器的网络接口上。Pod网络需要借助kubenet插件或CNI插件实现。Service网络
:专用于Service资源对象的网络,它也是一个虚拟网络,用于为K8S集群之中的Service配置IP地址,但是该地址不会配置在任何主机或容器的网络接口上,而是通过Node上的kube-proxy配置为iptables或ipvs规则,从而将发往该地址的所有流量调度到后端的各Pod对象之上。
4.2 K8S的工作流程
用K8S部署Nginx的过程中,K8S内部各组件是如何协同工作的:
我们在master节点执行一条命令要master部署一个nginx应用(kubectl create deployment nginx --image=nginx)
yaml
1. 这条命令首先发到master节点的网关api server,这是matser的唯一入口
2. api server将命令请求交给controller mannager进行控制
3. controller mannager 进行应用部署解析
4. controller mannager 会生成一次部署信息,并通过api server将信息存入etcd存储中
5. scheduler调度器通过api server从etcd存储中,拿到要部署的应用,开始调度看哪个节点有资源适合部署
6. scheduler把计算出来的调度信息通过api server再放到etcd中
7. 每一个node节点的监控组件kubelet,随时和master保持联系(给api-server发送请求不断获取最新数据),
拿到master节点存储在etcd中的部署信息
8. 假设node2的kubelet拿到部署信息,显示他自己节点要部署某某应用
9. kubelet就自己run一个应用在当前机器上,并随时给master汇报当前应用的状态信息
10. node和master也是通过master的api-server组件联系的
11. 每一个机器上的kube-proxy能知道集群的所有网络,只要node访问别人或者别人访问node,
12. node上的kube
proxy网络代理自动计算进行流量转发
4.3 k8s架构原理六连问
K8S 是一个基于容器技术的分布式集群管理系统。既然是个分布式系统,那势必有多个 Node 节点(物理主机或虚拟机),它们共同组成一个分布式集群,并且这些节点中会有一个 Master 节点,由它来统一管理Node 节点。
如图所示:
问题一:主节点和工作节点是如何通信的呢?
首先,Master 节点启动时,会运行一个 kube-apiserver
进程,它提供了集群管理的 API 接口,是集群内各个功能模块之间数据交互和通信的中心枢纽,并且它也提供了完备的集群安全机制。
在 Node 节点上,使用 K8S 中的 kubelet
组件,在每个 Node 节点上都会运行一个 kubelet 进程,它负责向 Master 汇报自身节点的运行情况
,如 Node 节点的注册、终止、定时上报健康状况等,以及接收 Master发出的命令,创建相应 Pod。
在 K8S 中,Pod 是最基本的操作单元
,它与 docker 的容器有略微的不同,因为 Pod 可能包含一个或多个容器(可以是 docker 容器),这些内部的容器是共享网络资源
的,即可以通过 localhost 进行相互访问。关于 Pod 内是如何做到网络共享的,每个 Pod 启动,内部都会启动一个 pause 容器(google的一个镜像),它使用默认的网络模式,而其他容器的网络都设置给它,以此来完成网络的共享问题。
如图所示:
问题二:Master 是如何将 Pod 调度到指定的 Node 上的?
该工作由 kube-scheduler
来完成,整个调度过程通过执行一些列复杂的算法最终为每个 Pod 计算出一个最佳的目标 Node,该过程由 kube-scheduler 进程自动完成。常见的有轮询调度
(RR)。当然也有可能,我们需要将 Pod 调度到一个指定的 Node 上,我们可以通过节点的标签(Label)和 Pod 的 nodeSelector 属性的相互匹配,来达到指定的效果。
如图所示:
问题三:各节点、Pod 的信息都是统一维护在哪里的,由谁来维护?
从上面的 Pod 调度的角度看,我们得有一个存储中心
,用来存储各节点资源使用情况、健康状态、以及各Pod 的基本信息
等,这样 Pod 的调度来能正常进行。
在 K8S 中,采用 etcd 组件
作为一个高可用强一致性的存储仓库,该组件可以内置在 K8S 中,也可以外部搭建供 K8S 使用。
集群上的所有配置信息都存储在了 etcd
,为了考虑各个组件的相对独立,以及整体的维护性,对于这些存储数据的增、删、改、查,统一由 kube-apiserver 来进行调用,apiserver 也提供了 REST 的支持,不仅对各个内部组件提供服务外,还对集群外部用户暴露服务。
外部用户可以通过 REST 接口,或者 kubectl 命令行工具进行集群管理,其内在都是与 apiserver 进行通信。
如图所示:
问题四:外部用户如何访问集群内运行的 Pod ?
前面讲了外部用户如何管理 K8S,而我们更关心的是内部运行的 Pod 如何对外访问。使用过Docker 的同学应该知道,如果使用 bridge 模式,在容器创建时,都会分配一个虚拟 IP,该 IP 外部是没法访问到的,我们需要做一层端口映射
,将容器内端口与宿主机端口进行映射绑定
,这样外部通过访问宿主机的指定端口,就可以访问到内部容器端口了。
那么,K8S 的外部访问是否也是这样实现的?答案是否定的,K8S 中情况要复杂一些。因为上面讲
的 Docker 是单机模式下的,而且一个容器对外就暴露一个服务。在分布式集群下,一个服务往往由多个Application 提供,用来分担访问压力,而且这些 Application 可能会分布在多个节点上,这样又涉及到了跨主机的通信。
这里,K8S 引入了 Service 的概念,将多个相同的 Pod
包装成一个完整的 service
对外提供服务,至于获取到这些相同的 Pod,每个 Pod 启动时都会设置 labels 属性
,在 Service 中我们通过选择器 Selector,选择具有相同 Name 标签属性的 Pod
,作为整体服
务,并将服务信息通过 Apiserver 存入 etcd 中,该工作由Service Controller 来完成。同时,每个节点上会启动一个 kube-proxy 进程
,由它来负责服务地址到 Pod地址的代理
以及负载均衡
等工作。
如图所示:
问题五:Pod 如何动态扩容和缩放?
既然知道了服务是由 Pod 组成的,那么服务的扩容也就意味着 Pod 的扩容
。通俗点讲,就是在需要时将Pod 复制多份
,在不需要后,将 Pod 缩减至指定份数
。K8S 中通过 Replication Controller
来进行管理,为每个 Pod 设置一个期望的副本数,当实际副本数与期望不符时,就动态的进行数量调整
,以达到期望值。期望数值可以由我们手动更新,或自动扩容代理
来完成。
如图所示:
问题六:各个组件之间是如何相互协作的?
最后,讲一下 kube-controller-manager 这个进程的作用。我们知道了 ectd 是作为集群数据的存储中心
,apiserver 是管理数据中心
,作为其他进程与数据中心通信的桥梁。而 Service Controller、Replication Controller 这些统一交由 kube-controller-manager
来管理,kube-controller-manager 作为一个守护进程,每个 Controller 都是一个控制循环,通过 apiserver
监视集群的共享状态,并尝试将实际状态与期望不符的进行改变。关于 Controller,manager 中还包含了 Node 节点控制器(Node Controller)、资源配额管控制器(ResourceQuota Controller)、命名空间控制器(Namespace Controller)等。
如图所示: