k8s学习
Kubernetes(K8S) 入门进阶实战完整教程,黑马程序员K8S全套教程
更多看 Kubernetes详细教程.md
常用命令
#查看pod
kubectl get pod -n <name space>
#查看pod有哪些label
kubectl get pod --show-labels -n <namespace>
#查看service(查看其它资源也类似)
kubectl get svc -n <namespace>
#查看已运行资源的yml
kubectl get pod -o yaml
#应用yaml文件(配置修改,版本修改后执行)
kubectl apply -f xxx.yml (如果要滚动更新,必须使用该命令)
#如果k8s的配置文件没有变动(注意不是业务的配置文件),可以用这个方法快速重启服务
kubectl delete pod ( -n {namespace} ) {pod-name}
#重启多个pod
kubectl delete pod <pod1> <pod2>
#重启一组pod
kubectl delete pod -l app=biz-sense-metro
#停止服务
kubectl delete -f 配置文件or配置文件目录
#启动服务
kubectl apply -f 配置文件or配置文件目录
#重新部署
kubectl delete -f . && kubectl apply -f .
#查看pod详细信息
kubectl describe pod <pod_name>
#查看node详细信息
kubectl describe nodes
#从pod拷贝文件
kubectl cp mysql-478535978-1dnm2:/tmp/message.log message.log
#查看资源类型及简写
kubectl api-resources
#进入容器的交互式shell
kubectl exec -it -n <namespace> <pod> bash (或sh)
#查看pod当前资源使用情况
kubectl top pod_name
#查看某个资源的字段意义
kubectl explain deployment.spec.template.spec.dnsPolicy
#清理docker镜像
docker system prune -af (注意:这个命令会把你暂时关闭的容器,以及暂时没有用到的 Docker 镜像都删掉)
#查看pod中各组件的版本号
kubectl describe pod -n namespace podname | grep "Image:" | sort -u sort -u 表示去重
重点说明
一个pod可以包含多个container,共享网络、ip、端口
ip连通性说明:pod ip,service ip在pod和node层面都可以连通,在集群外部无法连通
端口说明:node port用例暴露服务,以便集群外部可以访问到集群内部的服务;pod port为监听的port;service port为k8s内部service使用
域名说明:k8s支持短域名和完整长域名,短域名如:mysql-default.compnent,长域名如:mysql-default.test.svc.cluster.local,域名只在k8s的pod内可以连通,在集群node节点无法访问
deployments与pod的关系:deployments→ rs→pod,deployments通过k8s控制器,确保pod副本数在期望的状态
ingress说明:通过nodeport端口暴露服务
内部访问链:pod→service name→service ip(可选)→ pod ip
外部访问链:外部服务→ ingress port+path→ pod ip
修改configmap配置后,必须像pod一样
kubectl delete -f configmap
kubectl delete -f configmap
必须重启对应的pod才能生效
kubectl delete -f configmap
kubectl delete -f configmap
对于由deployments、statefulsets、daemonsets派生出来的pod,删除操作等于重启
pod重启说明:对于同一个名字的pod可以自动重启(由于错误导致的重启),没有手动重启,只有手动删除,删除后,日志消失
pod重启后资源状态说明(deployments、statefulsets、daemonsets)
service ip在不重新部署的情况下不会变化
常见故障判断
查看日志:kubectl logs <pod> -n <namespace>
查看后十行日志:kubectl logs <pod> --tail=10
查看上次启动的日志(restarts次数不为0):kubectl logs <pod> -p
查看详细描述:kubectl describe pod <pod> -n <namespace> 关注Reason和Events相关内容
其他命令
开机自启动应用
# 启动chronyd服务
[root@master ~]# systemctl start chronyd
# 设置开机自启动
[root@master ~]# systemctl enable chronyd
重新加载配置
sysctl -p
设置变量,运行脚本
命令行直接输入
images=(
kube-apiserver:v1.17.4
etcd:3.4.3-0
coredns:1.6.5
)
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
日志过滤
kubectl logs -n namespace podname | sed -n '/2020-01-15 12:16*/ ,/2020-01-15 22:34*/p'
kubectl logs -n namespace podname | grep "output" > /tmp/output.log
进入node
查看k8s node
k get node
进入node
ssh node名称
显示运行中的Pod、Service、Deployment以及ReplicaSet的关键信息
kubectl get all
Kubernetes级别的日志
kubectl get events
kubectl命令
kubectl是kubernetes集群的命令行工具,通过它能够对集群本身进行管理,并能够在集群上进行容器化应用的安装部署。kubectl命令的语法如下:
kubectl [command] [type] [name] [flags]
comand:指定要对资源执行的操作,例如create、get、delete
type:指定资源类型,比如deployment、pod、service
name:指定资源的名称,名称大小写敏感
flags:指定额外的可选参数
# 查看所有pod
kubectl get pod
# 查看某个pod
kubectl get pod pod_name
# 查看某个pod,以yaml格式展示结果
kubectl get pod pod_name -o yaml
kubectl get pod pod_name -o json 以json形式显示
命令式对象管理
kubernetes允许对资源进行多种操作,可以通过–help查看详细的操作命令
kubectl --help
以一个namespace / pod的创建和删除简单演示下命令的使用
# 创建一个namespace
[root@master ~]# kubectl create namespace dev
namespace/dev created
# 获取namespace
[root@master ~]# kubectl get ns
NAME STATUS AGE
default Active 21h
dev Active 21s
kube-node-lease Active 21h
kube-public Active 21h
kube-system Active 21h
# 在此namespace下创建并运行一个nginx的Pod
[root@master ~]# kubectl run pod --image=nginx:latest -n dev
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
deployment.apps/pod created
# 查看新创建的pod
[root@master ~]# kubectl get pod -n dev
NAME READY STATUS RESTARTS AGE
pod 1/1 Running 0 21s
# 删除指定的pod
[root@master ~]# kubectl delete pod pod-864f9875b9-pcw7x
pod "pod" deleted
# 删除指定的namespace
[root@master ~]# kubectl delete ns dev
namespace "dev" deleted
命令式对象配置
命令式对象配置就是使用命令配合配置文件一起来操作kubernetes资源。
1) 创建一个nginxpod.yaml,内容如下:
apiVersion: v1
kind: Namespace
metadata:
name: dev
---
apiVersion: v1
kind: Pod
metadata:
name: nginxpod
namespace: dev
spec:
containers:
- name: nginx-containers
image: nginx:latest
2)执行create命令,创建资源:
[root@master ~]# kubectl create -f nginxpod.yaml
namespace/dev created
pod/nginxpod created
此时发现创建了两个资源对象,分别是namespace和pod
3)执行get命令,查看资源:
[root@master ~]# kubectl get -f nginxpod.yaml
NAME STATUS AGE
namespace/dev Active 18s
NAME READY STATUS RESTARTS AGE
pod/nginxpod 1/1 Running 0 17s
这样就显示了两个资源对象的信息
4)执行delete命令,删除资源:
[root@master ~]# kubectl delete -f nginxpod.yaml
namespace "dev" deleted
pod "nginxpod" deleted
此时发现两个资源对象被删除了
总结:
命令式对象配置的方式操作资源,可以简单的认为:命令 + yaml配置文件(里面是命令需要的各种参数)
声明式对象配置
声明式对象配置跟命令式对象配置很相似,但是它只有一个命令apply。
# 首先执行一次kubectl apply -f yaml文件,发现创建了资源
[root@master ~]# kubectl apply -f nginxpod.yaml
namespace/dev created
pod/nginxpod created
# 再次执行一次kubectl apply -f yaml文件,发现说资源没有变动
[root@master ~]# kubectl apply -f nginxpod.yaml
namespace/dev unchanged
pod/nginxpod unchanged
总结:
其实声明式对象配置就是使用apply描述一个资源最终的状态(在yaml中定义状态)
使用apply操作资源:
如果资源不存在,就创建,相当于 kubectl create
如果资源已存在,就更新,相当于 kubectl patch
扩展:kubectl可以在node节点上运行吗 ?
kubectl的运行是需要进行配置的,它的配置文件是$HOME/.kube,如果想要在node节点运行此命令,需要将master上的.kube文件复制到node节点上,即在master节点上执行下面操作:
scp -r HOME/.kube node1: HOME/
使用推荐: 三种方式应该怎么用 ?
创建/更新资源 使用声明式对象配置 kubectl apply -f XXX.yaml
删除资源 使用命令式对象配置 kubectl delete -f XXX.yaml
查询资源 使用命令式对象管理 kubectl get(describe) 资源名称
-f, --filename=[]: Filename, directory, or URL to files to use to create the resource
实战入门
Namespace
Namespace是kubernetes系统中的一种非常重要资源,它的主要作用是用来实现多套环境的资源隔离或者多租户的资源隔离。
默认情况下,kubernetes集群中的所有的Pod都是可以相互访问的。但是在实际中,可能不想让两个Pod之间进行互相的访问,那此时就可以将两个Pod划分到不同的namespace下。kubernetes通过将集群内部的资源分配到不同的Namespace中,可以形成逻辑上的”组”,以方便不同的组的资源进行隔离使用和管理。
可以通过kubernetes的授权机制,将不同的namespace交给不同租户进行管理,这样就实现了多租户的资源隔离。此时还能结合kubernetes的资源配额机制,限定不同租户能占用的资源,例如CPU使用量、内存使用量等等,来实现租户可用资源的管理。
kubernetes在集群启动之后,会默认创建几个namespace
[root@master ~]# kubectl get namespace
NAME STATUS AGE
default Active 45h # 所有未指定Namespace的对象都会被分配在default命名空间
kube-node-lease Active 45h # 集群节点之间的心跳维护,v1.13开始引入
kube-public Active 45h # 此命名空间下的资源可以被所有人访问(包括未认证用户)
kube-system Active 45h # 所有由Kubernetes系统创建的资源都处于这个命名空间
查看
1 查看所有的ns 命令:kubectl get ns
[root@master ~]# kubectl get ns
NAME STATUS AGE
default Active 45h
kube-node-lease Active 45h
kube-public Active 45h
kube-system Active 45h
2 查看指定的ns 命令:kubectl get ns ns名称
[root@master ~]# kubectl get ns default
NAME STATUS AGE
default Active 45h
3 指定输出格式
kubectl get ns ns名称 -o 格式参数
# kubernetes支持的格式有很多,比较常见的是wide、json、yaml
[root@master ~]# kubectl get ns default -o yaml
apiVersion: v1
kind: Namespace
metadata:
creationTimestamp: "2021-05-08T04:44:16Z"
name: default
resourceVersion: "151"
selfLink: /api/v1/namespaces/default
uid: 7405f73a-e486-43d4-9db6-145f1409f090
spec:
finalizers:
- kubernetes
status:
phase: Active
4 查看ns详情 命令:kubectl describe ns ns名称
[root@master ~]# kubectl describe ns default
Name: default
Labels: <none>
Annotations: <none>
Status: Active # Active 命名空间正在使用中 Terminating 正在删除命名空间
# ResourceQuota 针对namespace做的资源限制
# LimitRange针对namespace中的每个组件做的资源限制
No resource quota.
No LimitRange resource.
创建
创建namespace
[root@master ~]# kubectl create ns dev
namespace/dev created
删除
删除namespace
[root@master ~]# kubectl delete ns dev
namespace "dev" deleted
配置方式
首先准备一个yaml文件:ns-dev.yaml
固定写法
apiVersion: v1
kind: Namespace
metadata: 元数据
name: dev
然后就可以执行对应的创建和删除命令了:
创建:kubectl create -f ns-dev.yaml
删除:kubectl delete -f ns-dev.yaml
Pod
Pod是kubernetes集群进行管理的最小单元,程序要运行必须部署在容器中,而容器必须存在于Pod中。
Pod可以认为是容器的封装,一个Pod中可以存在一个或者多个容器。
kubernetes在集群启动之后,集群中的各个组件也都是以Pod方式运行的。可以通过下面命令查看:
[root@master ~]# kubectl get pod -n kube-system
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-6955765f44-68g6v 1/1 Running 0 2d1h
kube-system coredns-6955765f44-cs5r8 1/1 Running 0 2d1h
kube-system etcd-master 1/1 Running 0 2d1h
kube-system kube-apiserver-master 1/1 Running 0 2d1h
kube-system kube-controller-manager-master 1/1 Running 0 2d1h
kube-system kube-flannel-ds-amd64-47r25 1/1 Running 0 2d1h
kube-system kube-flannel-ds-amd64-ls5lh 1/1 Running 0 2d1h
kube-system kube-proxy-685tk 1/1 Running 0 2d1h
kube-system kube-proxy-87spt 1/1 Running 0 2d1h
kube-system kube-scheduler-master 1/1 Running 0 2d1h
注意!pod name是变动的,最好现查现用
创建并运行
kubernetes没有提供单独运行Pod的命令,都是通过Pod控制器来实现的
# 命令格式: kubectl run pod控制器名称 --image=镜像:版本 --port=暴露端口 --namespace 命名空间
# --image 指定Pod的镜像
# --port 指定端口
# --namespace 指定namespace
[root@master ~]# kubectl create ns dev 先创建命名空间才能在命名空间创建pod
[root@master ~]# kubectl run nginx --image=nginx:latest --port=80 --namespace dev
deployment.apps/nginx created
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-6867cdf567-6jrln 1/1 Running 0 16h 10.244.2.2 node2 <none> <none>
NAME pod名字
READY 当前pod中有几个容器在运行/当前pod中有几个容器
RESTARTS 重启次数
AGE 启动了多久
NODE 当前pod调度到了哪个节点运行
查看更详细的信息
[root@master ~]# kubectl describe pod nginx-dd6b5d745-sbfpj -n dev
Name: nginx-dd6b5d745-sbfpj
Namespace: dev
Priority: 0
Node: node1/192.168.182.101 调度到了哪个node
Start Time: Tue, 14 Dec 2021 18:00:17 +0800 启动时间
Labels: pod-template-hash=dd6b5d745
run=nginx
Annotations: <none>
Status: Running
IP: 10.244.1.7 pod的ip
IPs:
IP: 10.244.1.7
Controlled By: ReplicaSet/nginx-dd6b5d745
Containers: pod中的容器们
nginx:
Container ID: docker://513e75ca4bed0b3bc08c68dbffed8b0069b68049dc802107b87d4b99913c9d08
Image: nginx:latest 使用镜像
Image ID: docker-pullable://nginx@sha256:9522864dd661dcadfd9958f9e0de192a1fdda2c162a35668ab6ac42b465f0603
Port: 80/TCP 容器端口
Host Port: 0/TCP 主机端口
State: Running
Started: Tue, 14 Dec 2021 18:00:34 +0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts: 挂载点
/var/run/secrets/kubernetes.io/serviceaccount from default-token-d28j7 (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
default-token-d28j7:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-d28j7
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events: 启动事件,用这里可以排除错误
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 9m23s default-scheduler Successfully assigned dev/nginx-dd6b5d745-sbfpj to node1
Normal Pulling 9m16s kubelet, node1 Pulling image "nginx:latest"
Normal Pulled 9m kubelet, node1 Successfully pulled image "nginx:latest"
Normal Created 9m kubelet, node1 Created container nginx
Normal Started 9m kubelet, node1 Started container nginx
错误run示例
[root@master ~]# kubectl run nginx1 --image=nginx:1111 --port=80 --namespace dev
[root@master ~]# kubectl describe pod nginx1-78f86cf8dd-msszb -n dev
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 63s default-scheduler Successfully assigned dev/nginx1-78f86cf8dd-msszb to node2
Normal BackOff 38s kubelet, node2 Back-off pulling image "nginx:1111"
Warning Failed 38s kubelet, node2 Error: ImagePullBackOff
Normal Pulling 25s (x2 over 58s) kubelet, node2 Pulling image "nginx:1111"
Warning Failed 5s (x2 over 38s) kubelet, node2 Failed to pull image "nginx:1111": rpc error: code = Unknown desc = Error response from daemon: manifest for nginx:1111 not found
Warning Failed 5s (x2 over 38s) kubelet, node2 Error: ErrImagePull
可以看到是pull image错误
访问Pod
用 -o wide
# 获取podIP
[root@master ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ...
nginx 1/1 Running 0 190s 10.244.1.23 node1 ...
#访问POD
[root@master ~]# curl http://10.244.1.23:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
</head>
<body>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
# 动态查看pod
[root@k8s-master01 ~]# kubectl get pods pod-initcontainer -n dev -w
NAME READY STATUS RESTARTS AGE
pod-initcontainer 0/1 Init:0/2 0 15s
pod-initcontainer 0/1 Init:1/2 0 52s
pod-initcontainer 0/1 Init:1/2 0 53s
pod-initcontainer 0/1 PodInitializing 0 89s
pod-initcontainer 1/1 Running 0 90s
删除指定Pod
# 删除指定Pod
[root@master ~]# kubectl delete pod nginx-dqewacdaeq-32fw -n dev
pod "ginx-dqewacdaeq-32fw" deleted
# 此时,显示删除Pod成功,但是再查询,发现又新产生了一个
[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
nginx-feew-32few 1/1 Running 0 21s
# 这是因为当前Pod(nginx-feew-32few)是由Pod控制器(ngnix)创建的,控制器会监控Pod状况,一旦发现Pod死亡,会立即重建
# 此时要想删除Pod(nginx-feew-32few),必须删除Pod控制器(nginx)
# 先来查询一下当前namespace下的Pod控制器
[root@master ~]# kubectl get deploy -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 1/1 1 1 9m7s
# 接下来,删除此PodPod控制器
[root@master ~]# kubectl delete deploy nginx -n dev
deployment.apps "nginx" deleted
# 稍等片刻,再查询Pod,发现Pod被删除了
[root@master ~]# kubectl get pods -n dev
No resources found in dev namespace.
配置操作
基本配置
创建一个pod-nginx.yaml,内容如下:
apiVersion: v1
kind: Pod 是pod不是pod控制器
metadata:
name: nginx 这里是pod的名字,不是pod控制器名字
namespace: dev
spec:
containers:
- image: nginx:latest
name: pod
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
然后就可以执行对应的创建和删除命令了:
创建:kubectl create -f pod-nginx.yaml
删除:kubectl delete -f pod-nginx.yaml
镜像拉取
apiVersion: v1
kind: Pod
metadata:
name: pod-imagepullpolicy
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: Never # 用于设置镜像拉取策略
- name: busybox
image: busybox:1.30
imagePullPolicy,用于设置镜像拉取策略,kubernetes支持配置三种拉取策略:
Always:总是从远程仓库拉取镜像(一直远程下载)
IfNotPresent:本地有则使用本地镜像,本地没有则从远程仓库拉取镜像(本地有就本地 本地没远程下载)
Never:只使用本地镜像,从不去远程仓库拉取,本地没有就报错 (一直使用本地)
默认值说明:
如果镜像tag为具体版本号, 默认策略是:IfNotPresent
如果镜像tag为:latest(最终版本) ,默认策略是always
# 创建Pod
[root@k8s-master01 pod]# kubectl create -f pod-imagepullpolicy.yaml
pod/pod-imagepullpolicy created
# 查看Pod详情
# 此时明显可以看到nginx镜像有一步Pulling image "nginx:1.17.1"的过程
[root@k8s-master01 pod]# kubectl describe pod pod-imagepullpolicy -n dev
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <unknown> default-scheduler Successfully assigned dev/pod-imagePullPolicy to node1
Normal Pulling 32s kubelet, node1 Pulling image "nginx:1.17.1"
Normal Pulled 26s kubelet, node1 Successfully pulled image "nginx:1.17.1"
Normal Created 26s kubelet, node1 Created container nginx
Normal Started 25s kubelet, node1 Started container nginx
Normal Pulled 7s (x3 over 25s) kubelet, node1 Container image "busybox:1.30" already present on machine
Normal Created 7s (x3 over 25s) kubelet, node1 Created container busybox
Normal Started 7s (x3 over 25s) kubelet, node1 Started container busybox
启动命令
在前面的案例中,一直有一个问题没有解决,就是的busybox容器一直没有成功运行,那么到底是什么原因导致这个容器的故障呢?
原来busybox并不是一个程序,而是类似于一个工具类的集合,kubernetes集群启动管理后,它会自动关闭。解决方法就是让其一直在运行,这就用到了command配置。
创建pod-command.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-command
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","touch /tmp/hello.txt;while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done;"]
command,用于在pod中的容器初始化完毕之后运行一个命令。
稍微解释下上面命令的意思:
"/bin/sh","-c", 使用sh执行命令
touch /tmp/hello.txt; 创建一个/tmp/hello.txt 文件
while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done; 每隔3秒向文件中写入当前时间
# 创建Pod
[root@k8s-master01 pod]# kubectl create -f pod-command.yaml
pod/pod-command created
# 查看Pod状态
# 此时发现两个pod都正常运行了
[root@k8s-master01 pod]# kubectl get pods pod-command -n dev
NAME READY STATUS RESTARTS AGE
pod-command 2/2 Runing 0 2s
# 进入pod中的busybox容器,查看文件内容
# 补充一个命令: kubectl exec pod名称 -n 命名空间 -it -c 容器名称 /bin/sh 在容器内部执行命令
# 使用这个命令就可以进入某个容器的内部,然后进行相关操作了
# 比如,可以查看txt文件的内容
[root@k8s-master01 pod]# kubectl exec pod-command -n dev -it -c busybox /bin/sh
/ # tail -f /tmp/hello.txt
14:44:19
14:44:22
14:44:25
特别说明:
通过上面发现command已经可以完成启动命令和传递参数的功能,为什么这里还要提供一个args选项,用于传递参数呢?这其实跟docker有点关系,kubernetes中的command、args两项其实是实现覆盖Dockerfile中ENTRYPOINT的功能。
1 如果command和args均没有写,那么用Dockerfile的配置。
2 如果command写了,但args没有写,那么Dockerfile默认的配置会被忽略,执行输入的command
3 如果command没写,但args写了,那么Dockerfile中配置的ENTRYPOINT的命令会被执行,使用当前args的参数
4 如果command和args都写了,那么Dockerfile的配置被忽略,执行command并追加上args参数
环境变量
创建pod-env.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-env
namespace: dev
spec:
containers:
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","while true;do /bin/echo $(date +%T);sleep 60; done;"]
env: # 设置环境变量列表
- name: "username"
value: "admin"
- name: "password"
value: "123456"
env,环境变量,用于在pod中的容器设置环境变量。
# 进入容器,输出环境变量
[root@k8s-master01 ~]# kubectl exec pod-env -n dev -c busybox -it /bin/sh
/ # echo $username
admin
/ # echo $password
123456
这种方式不是很推荐,推荐将这些配置单独存储在配置文件中,这种方式将在后面介绍。
端口设置
端口设置,也就是containers的ports选项。
访问容器中的程序
需要使用的是
Podip:containerPort
apiVersion: v1
kind: Pod
metadata:
name: pod-ports
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports: # 设置容器暴露的端口列表
- name: nginx-port
containerPort: 80
protocol: TCP
资源配额
容器中的程序要运行,肯定是要占用一定资源的,比如cpu和内存等,如果不对某个容器的资源做限制,那么它就可能吃掉大量资源,导致其它容器无法运行。针对这种情况,kubernetes提供了对内存和cpu的资源进行配额的机制,这种机制主要通过resources选项实现,他有两个子选项:
limits:用于限制运行时容器的最大占用资源,当容器占用资源超过limits时会被终止,并进行重启
requests :用于设置容器需要的最小资源,如果环境资源不够,容器将无法启动
可以通过上面两个选项设置资源的上下限。
apiVersion: v1
kind: Pod
metadata:
name: pod-resources
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
resources: # 资源配额
limits: # 限制资源(上限)
cpu: "2" # CPU限制,单位是core数
memory: "10Gi" # 内存限制
requests: # 请求资源(下限)
cpu: "1" # CPU限制,单位是core数
memory: "10Mi" # 内存限制
cpu:core数,可以为整数或小数
memory: 内存大小,可以使用Gi、Mi、G、M等形式
pod生命周期
pod对象从创建至终的这段时间范围称为pod的生命周期,它主要包含下面的过程:
pod创建过程
运行初始化容器(init container)过程
运行主容器(main container)
容器启动后钩子(post start)、容器终止前钩子(pre stop)
容器的存活性探测(liveness probe)、就绪性探测(readiness probe)
pod终止过程
在整个生命周期中,Pod会出现5种状态(相位),分别如下:
挂起(Pending):apiserver已经创建了pod资源对象,但它尚未被调度完成或者仍处于下载镜像的过程中
运行中(Running):pod已经被调度至某节点,并且所有容器都已经被kubelet创建完成
成功(Succeeded):pod中的所有容器都已经成功终止并且不会被重启
失败(Failed):所有容器都已经终止,但至少有一个容器终止失败,即容器返回了非0值的退出状态
未知(Unknown):apiserver无法正常获取到pod对象的状态信息,通常由网络通信失败所导致
钩子函数
钩子函数能够感知自身生命周期中的事件,并在相应的时刻到来时运行用户指定的程序代码。
kubernetes在主容器的启动之后和停止之前提供了两个钩子函数:
post start:容器创建之后执行,如果失败了会重启容器
pre stop :容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作
钩子处理器支持使用下面三种方式定义动作:
Exec命令:在容器内执行一次命令
……
lifecycle:
postStart:
exec:
command:
- cat
- /tmp/healthy
……
TCPSocket:在当前容器尝试访问指定的socket
……
lifecycle:
postStart:
tcpSocket:
port: 8080
……
HTTPGet:在当前容器中向某url发起http请求
……
lifecycle:
postStart:
httpGet:
path: / #URI地址
port: 80 #端口号
host: 192.168.5.3 #主机地址
scheme: HTTP #支持的协议,http或者https
……
接下来,以exec方式为例,演示下钩子函数的使用,创建pod-hook-exec.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-hook-exec
namespace: dev
spec:
containers:
- name: main-container
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
lifecycle:
postStart:
exec: # 在容器启动的时候执行一个命令,修改掉nginx的默认首页内容
command: ["/bin/sh", "-c", "echo postStart... > /usr/share/nginx/html/index.html"]
preStop:
exec: # 在容器停止之前停止nginx服务
command: ["/usr/sbin/nginx","-s","quit"]
容器探测
容器探测用于检测容器中的应用实例是否正常工作,是保障业务可用性的一种传统机制。如果经过探测,实例的状态不符合预期,那么kubernetes就会把该问题实例” 摘除 “,不承担业务流量。kubernetes提供了两种探针来实现容器探测,分别是:
liveness probes:存活性探针,用于检测应用实例当前是否处于正常运行状态,如果不是,k8s会重启容器
readiness probes:就绪性探针,用于检测应用实例当前是否可以接收请求,如果不能,k8s不会转发
流量
livenessProbe 决定是否重启容器,readinessProbe 决定是否将请求转发给容器。
上面两种探针目前均支持三种探测方式:
Exec命令:在容器内执行一次命令,如果命令执行的退出码为0,则认为程序正常,否则不正常
……
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
……
TCPSocket:将会尝试访问一个用户容器的端口,如果能够建立这条连接,则认为程序正常,否则不正常
……
livenessProbe:
tcpSocket:
port: 8080
……
HTTPGet:调用容器内Web应用的URL,如果返回的状态码在200和399之间,则认为程序正常,否则不正常
……
livenessProbe:
httpGet:
path: / #URI地址
port: 80 #端口号
host: 127.0.0.1 #主机地址
scheme: HTTP #支持的协议,http或者https
……
下面以liveness probes为例,做几个演示:
方式一:Exec
创建pod-liveness-exec.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-exec
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
exec:
command: ["/bin/cat","/tmp/hello.txt"] # 执行一个查看文件的命令
# 创建Pod
[root@k8s-master01 ~]# kubectl create -f pod-liveness-exec.yaml
pod/pod-liveness-exec created
# 查看Pod详情
[root@k8s-master01 ~]# kubectl describe pods pod-liveness-exec -n dev
......
Normal Created 20s (x2 over 50s) kubelet, node1 Created container nginx
Normal Started 20s (x2 over 50s) kubelet, node1 Started container nginx
Normal Killing 20s kubelet, node1 Container nginx failed liveness probe, will be restarted
Warning Unhealthy 0s (x5 over 40s) kubelet, node1 Liveness probe failed: cat: can't open '/tmp/hello11.txt': No such file or directory
# 观察上面的信息就会发现nginx容器启动之后就进行了健康检查
# 检查失败之后,容器被kill掉,然后尝试进行重启(这是重启策略的作用,后面讲解)
# 稍等一会之后,再观察pod信息,就可以看到RESTARTS不再是0,而是一直增长
[root@k8s-master01 ~]# kubectl get pods pod-liveness-exec -n dev
NAME READY STATUS RESTARTS AGE
pod-liveness-exec 0/1 CrashLoopBackOff 2 3m19s
# 当然接下来,可以修改成一个存在的文件,比如/tmp/hello.txt,再试,结果就正常了......
方式二:TCPSocket
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-tcpsocket
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
tcpSocket:
port: 8080 # 尝试访问8080端口
创建pod,观察效果
# 创建Pod
[root@k8s-master01 ~]# kubectl create -f pod-liveness-tcpsocket.yaml
pod/pod-liveness-tcpsocket created
# 查看Pod详情
[root@k8s-master01 ~]# kubectl describe pods pod-liveness-tcpsocket -n dev
......
Normal Scheduled 31s default-scheduler Successfully assigned dev/pod-liveness-tcpsocket to node2
Normal Pulled <invalid> kubelet, node2 Container image "nginx:1.17.1" already present on machine
Normal Created <invalid> kubelet, node2 Created container nginx
Normal Started <invalid> kubelet, node2 Started container nginx
Warning Unhealthy <invalid> (x2 over <invalid>) kubelet, node2 Liveness probe failed: dial tcp 10.244.2.44:8080: connect: connection refused
# 观察上面的信息,发现尝试访问8080端口,但是失败了
# 稍等一会之后,再观察pod信息,就可以看到RESTARTS不再是0,而是一直增长
[root@k8s-master01 ~]# kubectl get pods pod-liveness-tcpsocket -n dev
NAME READY STATUS RESTARTS AGE
pod-liveness-tcpsocket 0/1 CrashLoopBackOff 2 3m19s
# 当然接下来,可以修改成一个可以访问的端口,比如80,再试,结果就正常了......
方式三:HTTPGet
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-httpget
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
httpGet: # 其实就是访问http://127.0.0.1:80/hello
scheme: HTTP #支持的协议,http或者https
port: 80 #端口号
path: /hello #URI地址
# 创建Pod
[root@k8s-master01 ~]# kubectl create -f pod-liveness-httpget.yaml
pod/pod-liveness-httpget created
# 查看Pod详情
[root@k8s-master01 ~]# kubectl describe pod pod-liveness-httpget -n dev
.......
Normal Pulled 6s (x3 over 64s) kubelet, node1 Container image "nginx:1.17.1" already present on machine
Normal Created 6s (x3 over 64s) kubelet, node1 Created container nginx
Normal Started 6s (x3 over 63s) kubelet, node1 Started container nginx
Warning Unhealthy 6s (x6 over 56s) kubelet, node1 Liveness probe failed: HTTP probe failed with statuscode: 404
Normal Killing 6s (x2 over 36s) kubelet, node1 Container nginx failed liveness probe, will be restarted
# 观察上面信息,尝试访问路径,但是未找到,出现404错误
# 稍等一会之后,再观察pod信息,就可以看到RESTARTS不再是0,而是一直增长
[root@k8s-master01 ~]# kubectl get pod pod-liveness-httpget -n dev
NAME READY STATUS RESTARTS AGE
pod-liveness-httpget 1/1 Running 5 3m17s
# 当然接下来,可以修改成一个可以访问的路径path,比如/,再试,结果就正常了......
已经使用liveness Probe演示了三种探测方式,但是查看livenessProbe的子属性,会发现除了这三种方式,还有一些其他的配置,在这里一并解释下:
[root@k8s-master01 ~]# kubectl explain pod.spec.containers.livenessProbe
FIELDS:
exec <Object>
tcpSocket <Object>
httpGet <Object>
initialDelaySeconds <integer> # 容器启动后等待多少秒执行第一次探测
timeoutSeconds <integer> # 探测超时时间。默认1秒,最小1秒
periodSeconds <integer> # 执行探测的频率。默认是10秒,最小1秒
failureThreshold <integer> # 连续探测失败多少次才被认定为失败。默认是3。最小值是1
successThreshold <integer> # 连续探测成功多少次才被认定为成功。默认是1
initialDelaySeconds: 30 # 容器启动后30s开始探测
timeoutSeconds: 5 # 探测超时时间为5s
重启策略
一旦容器探测出现了问题,kubernetes就会对容器所在的Pod进行重启,其实这是由pod的重启策略决定的,pod的重启策略有 3 种,分别如下:
Always :容器失效时,自动重启该容器,这也是默认值。
OnFailure : 容器终止运行且退出码不为0时重启
Never : 不论状态为何,都不重启该容器
重启策略适用于pod对象中的所有容器,首次需要重启的容器,将在其需要时立即进行重启,随后再次需要重启的操作将由kubelet延迟一段时间后进行,且反复的重启操作的延迟时长以此为10s、20s、40s、80s、160s和300s,300s是最大延迟时长。
apiVersion: v1
kind: Pod
metadata:
name: pod-restartpolicy
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
httpGet:
scheme: HTTP
port: 80
path: /hello
restartPolicy: Never # 设置重启策略为Never
Pod调度
一个Pod在哪个Node节点上运行,是由Scheduler组件采用相应的算法计算出来的,这个过程是不受人工控制的。但是在实际使用中,这并不满足的需求,因为很多情况下,我们想控制某些Pod到达某些节点上,那么应该怎么做呢?这就要求了解kubernetes对Pod的调度规则,kubernetes提供了四大类调度方式:
自动调度:运行在哪个节点上完全由Scheduler经过一系列的算法计算得出
定向调度:NodeName、NodeSelector
亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity
污点(容忍)调度:Taints、Toleration
定向调度
指的是利用在pod上声明nodeName或者nodeSelector,以此将Pod调度到期望的node节点上。注意,这里的调度是强制的,这就意味着即使要调度的目标Node不存在,也会向上面进行调度,只不过pod运行失败而已。
NodeName
NodeName用于强制约束将Pod调度到指定的Name的Node节点上。这种方式,其实是直接跳过Scheduler的调度逻辑,直接将Pod调度到指定名称的节点。
apiVersion: v1
kind: Pod
metadata:
name: pod-nodename
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeName: node1 # 指定调度到node1节点上
NodeSelector
NodeSelector用于将pod调度到添加了指定标签的node节点上。它是通过kubernetes的label-selector机制实现的,也就是说,在pod创建之前,会由scheduler使用MatchNodeSelector调度策略进行label匹配,找出目标node,然后将pod调度到目标节点,该匹配规则是强制约束。
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeselector
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeSelector:
nodeenv: pro # 指定调度到具有nodeenv=pro标签的节点上
亲和性调度
上一节,介绍了两种定向调度的方式,使用起来非常方便,但是也有一定的问题,那就是
如果没有满足条件的Node,那么Pod将不会被运行,即使在集群中还有可用Node列表也不行
,这就限制了它的使用场景。
基于上面的问题,
kubernetes还提供了一种亲和性调度
(Affinity)。它在
NodeSelector的基础之上的进行了扩展,可以通过配置的形式
,实现优先选择满足条件的Node进行调度,如果没有,也可以调度到不满足条件的节点上,使调度更加灵活。
Affinity主要分为三类:
nodeAffinity(node亲和性): 以node为目标,解决pod可以调度到哪些node的问题
podAffinity(pod亲和性) : 以pod为目标,解决pod可以和哪些已存在的pod部署在同一个拓扑域中的问题
podAntiAffinity(pod反亲和性) : 以pod为目标,解决pod不能和哪些已存在pod部署在同一个拓扑域中的问题
关于亲和性(反亲和性)使用场景的说明:
亲和性:如果两个应用频繁交互,那就有必要利用亲和性让两个应用的尽可能的靠近,这样可以减少因网络通信而带来的性能损耗。
反亲和性:当应用的采用多副本部署时,有必要采用反亲和性让各个应用实例打散分布在各个node上,这样可以提高服务的高可用性。
NodeAffinity
首先来看一下NodeAffinity的可配置项
pod.spec.affinity.nodeAffinity
requiredDuringSchedulingIgnoredDuringExecution Node节点必须满足指定的所有规则才可以,相当于硬限制
nodeSelectorTerms 节点选择列表
matchFields 按节点字段列出的节点选择器要求列表
matchExpressions 按节点标签列出的节点选择器要求列表(推荐)
key 键
values 值
operat or 关系符 支持Exists, DoesNotExist, In, NotIn, Gt, Lt
preferredDuringSchedulingIgnoredDuringExecution 优先调度到满足指定的规则的Node,相当于软限制 (倾向)
preference 一个节点选择器项,与相应的权重相关联
matchFields 按节点字段列出的节点选择器要求列表
matchExpressions 按节点标签列出的节点选择器要求列表(推荐)
key 键
values 值
operator 关系符 支持In, NotIn, Exists, DoesNotExist, Gt, Lt
weight 倾向权重,在范围1-100。
关系符的使用说明:
- matchExpressions:
- key: nodeenv # 匹配存在标签的key为nodeenv的节点
operator: Exists
- key: nodeenv # 匹配标签的key为nodeenv,且value是"xxx"或"yyy"的节点
operator: In
values: ["xxx","yyy"]
- key: nodeenv # 匹配标签的key为nodeenv,且value大于"xxx"的节点
operator: Gt
values: "xxx"
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeaffinity-required
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #亲和性设置
nodeAffinity: #设置node亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
nodeSelectorTerms:
- matchExpressions: # 匹配env的值在["xxx","yyy"]中的标签
- key: nodeenv
operator: In
values: ["xxx","yyy"]
PodAffinity
PodAffinity主要实现以运行的Pod为参照,实现让新创建的Pod跟参照pod在一个区域的功能。
首先来看一下PodAffinity的可配置项:
pod.spec.affinity.podAffinity
requiredDuringSchedulingIgnoredDuringExecution 硬限制
namespaces 指定参照pod的namespace
topologyKey 指定调度作用域
labelSelector 标签选择器
matchExpressions 按节点标签列出的节点选择器要求列表(推荐)
key 键
values 值
operator 关系符 支持In, NotIn, Exists, DoesNotExist.
matchLabels 指多个matchExpressions映射的内容
preferredDuringSchedulingIgnoredDuringExecution 软限制
podAffinityTerm 选项
namespaces
topologyKey
labelSelector
matchExpressions
key 键
values 值
operator
matchLabels
weight 倾向权重,在范围1-100
apiVersion: v1
kind: Pod
metadata:
name: pod-podaffinity-required
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #亲和性设置
podAffinity: #设置pod亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions: # 匹配env的值在["xxx","yyy"]中的标签
- key: podenv
operator: In
values: ["xxx","yyy"]
topologyKey: kubernetes.io/hostname
上面配置表达的意思是:新Pod必须要与拥有标签nodeenv=xxx或者nodeenv=yyy的pod在同一Node上
PodAntiAffinity
PodAntiAffinity主要实现以运行的Pod为参照,让新创建的Pod跟参照pod不在一个区域中的功能。
它的配置方式和选项跟PodAffinty是一样的,这里不再做详细解释,直接做一个测试案例。
apiVersion: v1
kind: Pod
metadata:
name: pod-podantiaffinity-required
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #亲和性设置
podAntiAffinity: #设置pod亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions: # 匹配podenv的值在["pro"]中的标签
- key: podenv
operator: In
values: ["pro"]
topologyKey: kubernetes.io/hostname
污点和容忍
Label
Label是kubernetes系统中的一个重要概念。它的作用就是在资源上添加标识,用来对它们进行区分和选择。
Label的特点:
一个Label会以key/value键值对的形式附加到各种对象上,如Node、Pod、Service等等
一个资源对象可以定义任意数量的Label ,同一个Label也可以被添加到任意数量的资源对象上去
Label通常在资源对象定义时确定,当然也可以在对象创建后动态添加或者删除
可以通过Label实现资源的多维度分组,以便灵活、方便地进行资源分配、调度、配置、部署等管理工作。
一些常用的Label 示例如下:
版本标签:"version":"release", "version":"stable"......
环境标签:"environment":"dev","environment":"test","environment":"pro"
架构标签:"tier":"frontend","tier":"backend"
标签定义完毕之后,还要考虑到
标签的选择
,这就要使用到Label Selector,即:
Label用于给某个资源对象定义标识
Label Selector用于查询和筛选拥有某些标签的资源对象
当前有两种Label Selector:
- 基于等式的Label Selector
name = slave: 选择所有包含Label中key="name"且value="slave"的对象
env != production: 选择所有包括Label中的key="env"且value不等于"production"的对象
- 基于集合的Label Selector
name in (master, slave): 选择所有包含Label中的key="name"且value="master"或"slave"的对象
name not in (frontend): 选择所有包含Label中的key="name"且value不等于"frontend"的对象
- 标签的选择条件可以使用多个,此时将多个Label Selector进行组合,使用逗号”,”进行分隔即可。例如:
name=slave,env!=production
name not in (frontend),env!=production
命令方式
# 为pod资源打标签
[root@master ~]# kubectl label pod nginx-pod version=1.0 -n dev
pod/nginx-pod labeled
# 为pod资源更新标签
[root@master ~]# kubectl label pod nginx-pod version=2.0 -n dev --overwrite
pod/nginx-pod labeled
# 查看标签
[root@master ~]# kubectl get pod nginx-pod -n dev --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx-pod 1/1 Running 0 10m version=2.0
# 筛选标签 -l 表示 --selector='' 可以通过 kubectl label --help 查看
[root@master ~]# kubectl get pod -n dev -l version=2.0 --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx-pod 1/1 Running 0 17m version=2.0
[root@master ~]# kubectl get pod -n dev -l version!=2.0 --show-labels
No resources found in dev namespace.
#删除标签
[root@master ~]# kubectl label pod nginx-pod version- -n dev
pod/nginx-pod labeled
配置方式
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
labels: #配置文件加标签
version: "3.0"
env: "test"
spec:
containers:
- image: nginx:latest
name: pod
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
然后就可以执行对应的更新命令了:
kubectl apply -f pod-nginx.yaml
Deployment
在kubernetes中,Pod是最小的控制单元,但是kubernetes很少直接控制Pod,一般都是通过Pod控制器来完成的。Pod控制器用于pod的管理,确保pod资源符合预期的状态,当pod的资源出现故障时,会尝试进行重启或重建pod。
在kubernetes中Pod控制器的种类有很多,本章节只介绍一种:Deployment。
流程
用户通过 kubectl 创建 deployment
deployment 创建 replicaset
repliset 创建 pod
命令操作
deploy deployment deployments.apps
是一样的
# 命令格式: kubectl create deployment 名称 [参数]
# --image 指定pod的镜像
# --port 指定端口
# --replicas 指定创建pod数量
# --namespace 指定namespace
# 运行一个deployments,镜像为nginx,副本数为3 port 80 namespace dev
[root@master ~]# kubectl run nginx --image=nginx:latest --port=80 --replicas=3 -n dev
deployment.apps/nginx created
# 查看创建的Pod
[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
nginx-5ff7956ff6-6k8cb 1/1 Running 0 19s
nginx-5ff7956ff6-jxfjt 1/1 Running 0 19s
nginx-5ff7956ff6-v6jqw 1/1 Running 0 19s
# 查看deployment的信息
[root@master ~]# kubectl get deploy -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 3/3 3 3 2m42s
#同时查看deploy和pod
kubectl get deploy,pod -n dev
# UP-TO-DATE:成功升级的副本数量
# AVAILABLE:可用副本的数量
[root@master ~]# kubectl get deploy -n dev -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
nginx 3/3 3 3 2m51s nginx nginx:latest run=nginx
# 查看deployment的详细信息
[root@master ~]# kubectl describe deploy nginx -n dev
Name: nginx pod控制器名称
Namespace: dev 命名空间
CreationTimestamp: Wed, 08 May 2021 11:14:14 +0800
Labels: run=nginx 标签
Annotations: deployment.kubernetes.io/revision: 1
Selector: run=nginx 标签选择器
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: run=nginx
Containers:
nginx:
Image: nginx:latest
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: nginx-5ff7956ff6 (3/3 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 5m43s deployment-controller Scaled up replicaset nginx-5ff7956ff6 to 3
这里告诉我们创建了一个 NewReplicaSet: nginx-5ff7956ff6 (3/3 replicas created),证明deoloyments是通过replicaset管理pod
# 删除 deployment 同时也会删除相应的 pod ,相当于强制删除了 pod
[root@master ~]# kubectl delete deploy nginx -n dev
deployment.apps "nginx" deleted
通过 deployment 更新镜像
set image
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
deploy-hello-server 2/2 2 2 17m
$ kubectl describe deployments deploy-hello-server
Name: deploy-hello-server
Namespace: default
CreationTimestamp: Thu, 03 Jun 2021 03:19:59 -0400
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=deploy-hello-server
...
Pod Template:
Labels: app=deploy-hello-server
Containers:
hello-server:
Image: wssiqi/hello-server:v1 //!!!!可以看到这个deployment的image只有wssiqi/hello-server:v1
Port: <none>
...
我们将这个
image
升级为
wssiqi/hello-server:v2
$ kubectl set image deployments/deploy-hello-server hello-server=wssiqi/hello-server:v2
deployment.apps/deploy-hello-server image updated
deployments/deploy-hello-server:
deployments/ 为固定写法
deploy-hello-server 来自于 Name: deploy-hello-server
hello-server=wssiqi/hello-server:v2:
hello-server 是 container 名字,来自于 Containers: hello-server
wssiqi/hello-server:v2 表示将containerhello-server的image设置为v2版本
检查是否已经更新完成
$ kubectl describe deployments deploy-hello-server | grep Image
Image: wssiqi/hello-server:v2
通过deployment.yaml文件来更新image
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-hello-server
spec:
selector:
matchLabels:
app: deploy-hello-server
replicas: 2
template:
metadata:
labels:
app: deploy-hello-server
spec:
containers:
- image: wssiqi/hello-server:v3 //更新这里
name: hello-server
应用
新的deployment
$ kubectl apply -f deployment.yaml
deployment.apps/deploy-hello-server configured
$ kubectl get deployments -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
deploy-hello-server 2/2 2 2 36m hello-server wssiqi/hello-server:v3 app=deploy-hello-server
配置操作
创建一个deploy-nginx.yaml,内容如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
run: nginx
template:
metadata:
labels:
run: nginx
spec:
containers:
- image: nginx:latest
name: nginx
ports:
- containerPort: 80
protocol: TCP
然后就可以执行对应的创建和删除命令了:
创建:kubectl create -f deploy-nginx.yaml
删除:kubectl delete -f deploy-nginx.yaml
Pod控制器介绍
Pod是
kubernetes的最小管理单元
,在kubernetes中,按照pod的创建方式可以将其分为两类:
自主式pod:kubernetes直接创建出来的Pod,这种pod删除后就没有了,也不会重建
控制器创建的pod:kubernetes通过控制器创建的pod,这种pod删除了之后还会自动重建
Pod控制器是
管理pod的中间层
,使用Pod控制器之后,只需要告诉Pod控制器,想要多少个什么样的Pod就可以了,它会创建出满足条件的Pod并确保每一个Pod资源处于用户期望的目标状态。如果Pod资源在运行中出现故障,它会基于指定策略重新编排Pod。
ReplicaSet(RS)
ReplicaSet的主要作用
是保证一定数量的pod正常运行
,它会持续监听这些Pod的运行状态,
一旦Pod发生故障,就会重启或重建
。同时它还支持对pod数量的扩缩容和镜像版本的升降级。
kubectl get rs
查看replicaset
kubectl describe replicasets.apps nginx-6db489d4b7
Name: nginx-6db489d4b7
Namespace: default
Selector: pod-template-hash=6db489d4b7,run=nginx
Labels: pod-template-hash=6db489d4b7
run=nginx
Annotations: deployment.kubernetes.io/desired-replicas: 2
deployment.kubernetes.io/max-replicas: 3
deployment.kubernetes.io/revision: 1
Controlled By: Deployment/nginx //这里表示是replicasets是由deployment、nginx创建
Replicas: 2 current / 2 desired
Pods Status: 2 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: pod-template-hash=6db489d4b7
run=nginx
Containers:
nginx:
Image: nginx
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 3m56s replicaset-controller Created pod: nginx-6db489d4b7-ghzd9
Normal SuccessfulCreate 3m56s replicaset-controller Created pod: nginx-6db489d4b7-7f6jb
查看pod
kubectl describe pod nginx-6db489d4b7-7f6jb
Name: nginx-6db489d4b7-7f6jb
Namespace: default
...
Controlled By: ReplicaSet/nginx-6db489d4b7 //表示是由ReplicaSet/nginx-6db489d4b7创建pod
...
Deployment(Deploy)
为了更好的解决服务编排的问题,kubernetes在V1.2版本开始,引入了Deployment控制器。值得一提的是,这种控制器并不直接管理pod,而是通过管理ReplicaSet来简介管理Pod,即:Deployment管理ReplicaSet,ReplicaSet管理Pod。所以Deployment比ReplicaSet功能更加强大。
Deployment主要功能有下面几个:
支持ReplicaSet的所有功能
支持发布的停止、继续
支持滚动升级和回滚版本
# 查看当前升级版本的状态
[root@k8s-master01 ~]# kubectl rollout status deploy pc-deployment -n dev
deployment "pc-deployment" successfully rolled out
# 查看升级历史记录
[root@k8s-master01 ~]# kubectl rollout history deploy pc-deployment -n dev
deployment.apps/pc-deployment
REVISION CHANGE-CAUSE
1 kubectl create --filename=pc-deployment.yaml --record=true
2 kubectl create --filename=pc-deployment.yaml --record=true
3 kubectl create --filename=pc-deployment.yaml --record=true
# 可以发现有三次版本记录,说明完成过两次升级
# 版本回滚
# 这里直接使用--to-revision=1回滚到了1版本, 如果省略这个选项,就是回退到上个版本,就是2版本
[root@k8s-master01 ~]# kubectl rollout undo deployment pc-deployment --to-revision=1 -n dev
deployment.apps/pc-deployment rolled back
Horizontal Pod Autoscaler(HPA)
在前面的课程中,我们已经可以实现通过手工执行kubectl scale命令实现Pod扩容或缩容,但是这显然不符合
Kubernetes的定位目标--自动化、智能化
。 Kubernetes期望可以实现通过监测Pod的使用情况,实现pod数量的自动调整,于是就产生了Horizontal Pod Autoscaler(HPA)这种控制器。
HPA可以获取每个Pod利用率,然后和HPA中定义的指标进行对比,同时计算出需要伸缩的具体值,最后实现Pod的数量的调整。其实HPA与之前的Deployment一样,也属于一种Kubernetes资源对象,它通过
追踪分析RC控制的所有目标Pod的负载变化情况,来确定是否需要针对性地调整目标Pod的副本数
,这是HPA的实现原理。
更多看这里
DaemonSet(DS)
DaemonSet类型的控制器可以
保证在集群中的每一台(或指定)节点上都运行一个副本
。一般适用于日志收集、节点监控等场景。也就是说,如果一个Pod提供的功能是节点级别的(每个节点都需要且只需要一个),那么这类Pod就适合使用DaemonSet类型的控制器创建。
DaemonSet控制器的特点:
每当向集群中添加一个节点时,指定的 Pod 副本也将添加到该节点上
当节点从集群中移除时,Pod 也就被垃圾回收了
# 查看daemonset
[root@k8s-master01 ~]# kubectl get ds
job
Job,主要用于负责**批量处理(一次要处理指定数量任务)短暂的一次性(每个任务仅运行一次就结束)**任务。Job特点如下:
当Job创建的pod执行成功结束时,Job将记录成功结束的pod数量
当成功结束的pod达到指定的数量时,Job将完成执行
# 查看job
[root@k8s-master01 ~]# kubectl get job
CronJob(CJ)
CronJob控制器以 Job控制器资源为其管控对象,并借助它管理pod资源对象,Job控制器定义的作业任务在其控制器资源创建之后便会立即执行,但CronJob可以以类似于Linux操作系统的周期性任务作业计划的方式控制其运行时间点及重复运行的方式。也就是说,CronJob可以在特定的时间点(反复的)去运行job任务
# 查看cronjob
[root@k8s-master01 ~]# kubectl get cronjobs
Service
通过上节课的学习,已经能够利用Deployment来创建一组Pod来提供具有高可用性的服务。
虽然每个Pod都会分配一个单独的Pod IP,然而却存在如下两问题:
Pod IP 会随着Pod的重建产生变化
Pod IP 仅仅是集群内可见的虚拟IP,外部无法访问
这样对于访问这个服务带来了难度。因此,kubernetes设计了Service来解决这个问题。
Service可以看作是一组同类Pod对外的访问接口。借助Service,应用可以方便地实现服务发现和负载均衡。
创建集群内部可访问的Service
# 暴露Service
[root@master ~]# kubectl expose deploy nginx --name=svc-nginx1 --type=ClusterIP --port=80 --target-port=80 -n dev
service/svc-nginx1 exposed
ClusterIP 的创建方式只能在集群内部访问
# 查看service
[root@master ~]# kubectl get svc svc-nginx1 -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
svc-nginx1 ClusterIP 10.109.179.231 <none> 80/TCP 3m51s run=nginx
# 这里产生了一个CLUSTER-IP,这就是service的IP,在Service的生命周期中,这个地址是不会变动的
# 可以通过这个IP访问当前service对应的POD
[root@master ~]# curl 10.109.179.231:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
</head>
<body>
<h1>Welcome to nginx!</h1>
.......
</body>
</html>
创建集群外部也可访问的Service
# 上面创建的Service的type类型为ClusterIP,这个ip地址只用集群内部可访问
# 如果需要创建外部也可以访问的Service,需要修改type为NodePort
[root@master ~]# kubectl expose deploy nginx --name=svc-nginx2 --type=NodePort --port=80 --target-port=80 -n dev
service/svc-nginx2 exposed
# 此时查看,会发现出现了NodePort类型的Service,而且有一对Port(80:31928/TC)
[root@master ~]# kubectl get svc svc-nginx2 -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
svc-nginx2 NodePort 10.100.94.0 <none> 80:31928/TCP 9s run=nginx
# 接下来就可以通过集群外的主机访问 节点IP:31928访问服务了
# 例如在的电脑主机上通过浏览器访问下面的地址
http://192.168.90.100:31928/
删除Service
[root@master ~]# kubectl delete svc svc-nginx-1 -n dev
service "svc-nginx-1" deleted
配置方式
创建一个svc-nginx.yaml,内容如下:
apiVersion: v1
kind: Service
metadata:
name: svc-nginx
namespace: dev
spec:
clusterIP: 10.109.179.231 #固定svc的内网ip,不写默认有
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
run: nginx
type: ClusterIP
然后就可以执行对应的创建和删除命令了:
创建:kubectl create -f svc-nginx.yaml
删除:kubectl delete -f svc-nginx.yaml
Service介绍
在kubernetes中,pod是应用程序的载体,我们可以通过pod的ip来访问应用程序,但是
pod的ip地址不是固定的,这也就意味着不方便直接采用pod的ip对服务进行访问
。
为了解决这个问题,kubernetes提供了Service资源,
Service会对提供同一个服务的多个pod进行聚合,并且提供一个统一的入口地址
。通过访问Service的入口地址就能访问到后面的pod服务。
Service在很多情况下只是一个概念,真正起作用的其实是kube-proxy服务进程,每个Node节点上都运行着一个kube-proxy服务进程。当创建Service的时候会通过api-server向etcd写入创建的service的信息,而kube-proxy会基于监听的机制发现这种Service的变动,然后它会将最新的Service信息转换成对应的访问规则。
# 10.97.97.97:80 是service提供的访问入口
# 当访问这个入口的映射时,可以发现后面有三个pod的服务在等待调用,
# kube-proxy会基于rr(轮询)的策略,将请求分发到其中一个pod上去
# 这个规则会同时在集群内的所有节点上都生成,所以在任何一个节点上访问都可以。
[root@node1 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.97.97.97:80 rr
-> 10.244.1.39:80 Masq 1 0 0
-> 10.244.1.40:80 Masq 1 0 0
-> 10.244.2.33:80 Masq 1 0 0
Service类型
Service的资源清单文件:
kind: Service # 资源类型
apiVersion: v1 # 资源版本
metadata: # 元数据
name: service # 资源名称
namespace: dev # 命名空间
spec: # 描述
selector: # 标签选择器,用于确定当前service代理哪些pod
app: nginx
type: # Service类型,指定service的访问方式
clusterIP: # 虚拟服务的ip地址
sessionAffinity: # session亲和性,支持ClientIP、None两个选项
ports: # 端口信息
- protocol: TCP
port: 3017 # service端口
targetPort: 5003 # pod端口
nodePort: 31122 # 主机端口
ClusterIP:默认值,它是Kubernetes系统自动分配的虚拟IP,只能在集群内部访问
NodePort:将Service通过指定的Node上的端口暴露给外部,通过此方法,就可以在集群外部访问服务
LoadBalancer:使用外接负载均衡器完成到服务的负载分发,注意此模式需要外部云环境支持
ExternalName: 把集群外部的服务引入集群内部,直接使用
Endpoint
Endpoint是kubernetes中的一个资源对象,
存储在etcd中,用来记录一个service对应的所有pod的访问地址
,它是根据service配置文件中selector描述产生的。
一个Service由一组Pod组成
,这些
Pod通过Endpoints暴露出来
,Endpoints是实现实际服务的端点集合。换句话说,
service和pod之间的联系是通过endpoints实现的
。
kubectl get endpoints -n namspace -o wide
Ingress介绍
Service对集群之外暴露服务
的主要方式有两种:
NotePort和LoadBalancer
,但是这两种方式,都有一定的缺点:
NodePort方式的缺点是会占用很多集群机器的端口,那么当集群服务变多的时候,这个缺点就愈发明显
LB方式的缺点是每个service需要一个LB,浪费、麻烦,并且需要kubernetes之外设备的支持
基于这种现状,kubernetes提供了Ingress资源对象,Ingress只需要一个NodePort或者一个LB就可以满足暴露多个Service的需求。工作机制大致如下图表示:
更多看这里
数据存储
在前面已经提到,
容器的生命周期可能很短,会被频繁地创建和销毁
。那么容器在销毁时,保存在容器中的数据也会被清除。这种结果对用户来说,在某些情况下是不乐意看到的。为了
持久化保存容器的数据
,kubernetes引入了Volume的概念。
Volume是
Pod中能够被多个容器访问的共享目录
,它
被定义在Pod上
,然
后被一个Pod里的多个容器挂载到具体的文件目录下
,kubernetes通过Volume实现同一个Pod中不同容器之间的数据共享以及数据的持久化存储。Volume的生命容器不与Pod中单个容器的生命周期相关,当容器终止或者重启时,Volume中的数据也不会丢失。
kubernetes的Volume支持多种类型,比较常见的有下面几个:
简单存储:EmptyDir、HostPath、NFS
高级存储:PV、PVC
配置存储:ConfigMap、Secret
基本存储
EmptyDir
EmptyDir是最基础的Volume类型,一个
EmptyDir就是Host上的一个空目录
。
EmptyDir是在Pod被分配到Node时创建的,它的
初始内容为空
,并且
无须指定宿主机上对应的目录文
件,因为kubernetes会自动分配一个目录,当
Pod销毁时, EmptyDir中的数据也会被永久删除
。 EmptyDir用途如下:
临时空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保留
一个容器需要从另一个容器中获取数据的目录(多容器共享目录)
apiVersion: v1
kind: Pod
metadata:
name: volume-emptydir
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
volumeMounts: # 将logs-volume挂在到nginx容器中,对应的目录为 /var/log/nginx
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,动态读取指定文件中内容
volumeMounts: # 将logs-volume 挂在到busybox容器中,对应的目录为 /logs
- name: logs-volume
mountPath: /logs
volumes: # 声明volume, name为logs-volume,类型为emptyDir
- name: logs-volume
emptyDir: {}
HostPath
EmptyDir中数据不会被持久化,它会随着Pod的结束而销毁,如果想简单的将数据持久化到主机中,可以选择HostPath。
HostPath就是将Node主机中一个实际目录挂在到Pod中,以供容器使用,这样的设计就可以保证Pod销毁了,但是数据依据可以存在于Node主机上
。
apiVersion: v1
kind: Pod
metadata:
name: volume-hostpath
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume
mountPath: /var/log/nginx 挂载到的是pod内的 容器中的路径
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","tail -f /logs/access.log"]
volumeMounts:
- name: logs-volume
mountPath: /logs 挂载到的是pod内的 容器中的路径
volumes:
- name: logs-volume
hostPath:
path: /root/logs 挂载到的是pod外的,node内的路径
type: DirectoryOrCreate # 目录存在就使用,不存在就先创建后使用
nfs
HostPath可以解决数据持久化的问题,但是
一旦Node节点故障了,Pod如果转移到了别的节点
,又会出现问题了,此时需要准备
单独的网络存储系统,比较常用的用NFS、CIFS
。
NFS是一个网络文件存储系统
,可以搭建一台NFS服务器,然后将Pod中的存储直接连接到NFS系统上,这样的话,无论Pod在节点上怎么转移,
只要Node跟NFS的对接没问题,数据就可以成功访问
。
apiVersion: v1
kind: Pod
metadata:
name: volume-nfs
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","tail -f /logs/access.log"]
volumeMounts:
- name: logs-volume
mountPath: /logs
volumes:
- name: logs-volume
nfs:
server: 192.168.5.6 #nfs服务器地址
path: /root/data/nfs #共享文件路径
高级存储
为了能够屏蔽底层存储实现的细节,方便用户使用, kubernetes引入PV和PVC两种资源对象
-
PV(Persistent Volume)是
持久化卷
的意思,是对底层的
共享存储的一种抽象
。一般情况下PV由
kubernetes管理员进行创建和配置
,它与底层具体的共享存储技术有关,并通过插件完成与共享存储的对接。 -
PVC(Persistent Volume Claim)是持久卷声明的意思,是用户对于存储需求的一种声明。换句话说,
PVC其实就是用户向kubernetes系统发出的一种资源需求申请
。
使用了PV和PVC之后,工作可以得到进一步的细分:
存储:存储工程师维护
PV: kubernetes管理员维护
PVC:kubernetes用户维护
PV
PV是存储资源的抽象,下面是资源清单文件:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
spec:
nfs: # 存储类型,与底层真正存储对应
capacity: # 存储能力,目前只支持存储空间的设置
storage: 2Gi
accessModes: # 访问模式
storageClassName: # 存储类别
persistentVolumeReclaimPolicy: # 回收策略## 一些操作
PV 的关键配置参数说明:
-
存储类型
底层实际存储的类型
,kubernetes支持多种存储类型,每种存储类型的配置都有所差异 -
存储能力(capacity)
目前只支持
存储空间的设置
( storage=1Gi ),不过未来可能会加入IOPS、吞吐量等指标的配置 -
访问模式(accessModes)
用于描述用户
应用对存储资源的访问权限
,访问权限包括下面几种方式:
ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载
ReadOnlyMany(ROX): 只读权限,可以被多个节点挂载
ReadWriteMany(RWX):读写权限,可以被多个节点挂载
需要注意的是,底层不同的存储类型可能支持的访问模式不同
-
回收策略(persistentVolumeReclaimPolicy)
当
PV不再被使用了之后,对其的处理方式
。目前支持三种策略:
Retain (保留) 保留数据,需要管理员手工清理数据
Recycle(回收) 清除 PV 中的数据,效果相当于执行 rm -rf /thevolume/*
Delete (删除) 与 PV 相连的后端存储完成 volume 的删除操作,当然这常见于云服务商的存储服务
需要注意的是,底层不同的存储类型可能支持的回收策略不同
-
存储类别
PV可以通过
storageClassName参数
指定一个
存储类别
具有
特定类别的PV只能与请求了该类别的PVC进行绑定
未设定类别的PV则只能与不请求任何类别的PVC进行绑定 -
状态(status)
一个 PV 的生命周期中,可能会处于4中不同的阶段
Available(可用): 表示可用状态,还未被任何 PVC 绑定
Bound(已绑定): 表示 PV 已经被 PVC 绑定
Released(已释放): 表示 PVC 被删除,但是资源还未被集群重新声明
Failed(失败): 表示该 PV 的自动回收失败
-
相关命令
# 创建 pv
[root@k8s-master01 ~]# kubectl create -f pv.yaml
# 查看pv
[root@k8s-master01 ~]# kubectl get pv -o wide
PVC
PVC是资源的申请,用来声明对存储空间、访问模式、存储类别需求信息。下面是资源清单文件:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc
namespace: dev
spec:
accessModes: # 访问模式
selector: # 采用标签对PV选择
storageClassName: # 存储类别
resources: # 请求空间
requests:
storage: 5Gi
PVC 的关键配置参数说明:
-
访问模式(accessModes)
用于描述用户应用
对存储资源的访问权限
-
选择条件(selector)
通过
Label Selector
的设置,可使
PVC对于系统中己存在的PV进行筛选
-
存储类别(storageClassName)
PVC在定义时可以设定需要的后端存储的类别
,只有
设置了该class的pv才能被系统选出
-
资源请求(Resources )
描述对存储资源的请求 -
相关命令
# 创建pvc
[root@k8s-master01 ~]# kubectl create -f pvc.yaml
# 查看pvc
[root@k8s-master01 ~]# kubectl get pvc -n dev -o wide
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE VOLUMEMODE
pvc1 Bound pv1 1Gi RWX 15s Filesystem
# 查看pv
[root@k8s-master01 ~]# kubectl get pv -o wide
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM AGE VOLUMEMODE
pv1 1Gi RWx Retain Bound dev/pvc1 3h37m Filesystem
配置存储
ConfigMap
ConfigMap是一种比较特殊的
存储卷
,它的主要作用是用来存储配置信息的。
创建configmap.yaml,内容如下:
apiVersion: v1
kind: ConfigMap
metadata:
name: configmap
namespace: dev
data:
info: |
username:admin
password:123456
接下来,使用此配置文件创建configmap
# 创建configmap
[root@k8s-master01 ~]# kubectl create -f configmap.yaml
configmap/configmap created
# 查看configmap详情
[root@k8s-master01 ~]# kubectl describe cm configmap -n dev
Name: configmap
Namespace: dev
Labels: <none>
Annotations: <none>
Data
====
info:
----
username:admin
password:123456
Events: <none>
接下来创建一个pod-configmap.yaml,将上面创建的configmap挂载进去
apiVersion: v1
kind: Pod
metadata:
name: pod-configmap
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
volumeMounts: # 将configmap挂载到目录
- name: config
mountPath: /configmap/config
volumes: # 引用configmap
- name: config
configMap:
name: configmap
# 创建pod
[root@k8s-master01 ~]# kubectl create -f pod-configmap.yaml
pod/pod-configmap created
# 查看pod
[root@k8s-master01 ~]# kubectl get pod pod-configmap -n dev
NAME READY STATUS RESTARTS AGE
pod-configmap 1/1 Running 0 6s
#进入容器
[root@k8s-master01 ~]# kubectl exec -it pod-configmap -n dev /bin/sh
# cd /configmap/config/
# ls
info
# more info
username:admin
password:123456
# 可以看到映射已经成功,每个configmap都映射成了一个目录
# key--->文件 value---->文件中的内容
# 此时如果更新configmap的内容, 容器中的值也会动态更新
Secret
在kubernetes中,还存在一种和ConfigMap非常类似的对象,称为Secret对象。它
主要用于存储敏感信息,例如密码、秘钥、证书等等。
1 首先使用base64对数据进行编码
[root@k8s-master01 ~]# echo -n 'admin' | base64 #准备username
YWRtaW4=
[root@k8s-master01 ~]# echo -n '123456' | base64 #准备password
MTIzNDU2
2 接下来编写secret.yaml,并创建Secret
apiVersion: v1
kind: Secret
metadata:
name: secret
namespace: dev
type: Opaque
data:
username: YWRtaW4=
password: MTIzNDU2
# 创建secret
[root@k8s-master01 ~]# kubectl create -f secret.yaml
secret/secret created
# 查看secret详情
[root@k8s-master01 ~]# kubectl describe secret secret -n dev
Name: secret
Namespace: dev
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password: 6 bytes
username: 5 bytes
3 创建pod-secret.yaml,将上面创建的secret挂载进去:
apiVersion: v1
kind: Pod
metadata:
name: pod-secret
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
volumeMounts: # 将secret挂载到目录
- name: config
mountPath: /secret/config
volumes:
- name: config
secret:
secretName: secret
# 创建pod
[root@k8s-master01 ~]# kubectl create -f pod-secret.yaml
pod/pod-secret created
# 查看pod
[root@k8s-master01 ~]# kubectl get pod pod-secret -n dev
NAME READY STATUS RESTARTS AGE
pod-secret 1/1 Running 0 2m28s
# 进入容器,查看secret信息,发现已经自动解码了
[root@k8s-master01 ~]# kubectl exec -it pod-secret /bin/sh -n dev
/ # ls /secret/config/
password username
/ # more /secret/config/username
admin
/ # more /secret/config/password
123456
查看当前po中容器中的image的版本
比如想看 namespace 为 front 中的 name 为 front_end 的 pod 中的 image 版本
1 先查看这个pod 的运行状态
k get pod -n front front_end
2 查看详细信息
k describe pod -n front front_end
Containers:
frontend:
Container ID: docker://xxxxx
Image: ip:端口/front_end:2.3.0-502ba535 这里就是image的版本
Image ID: docker-pullable://ip:端口/xxxxx
3 docker 查看镜像
docker images | grep front_end
4 如果发现有多个image。则docker查看容器中运行的是哪一个image
docker ps | grep front
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
容器id 502ba535 命令 26 minutes ago Up 26 minutes 端口 名字
5 可以看到容器中具体运行的 image 的 镜像id 502ba535 ,对比可以知道 运行的镜像版本
6 查看镜像的详细信息
docker inspect 镜像id 502ba535
查看 镜像,容器,po,svc 相关信息
docker images
docker images -a
docker images | grep xxx
docker inspect 镜像id 查看镜像的详细信息
docker ps
docker ps -a
docker ps | grep xxx
docker inspect 容器id 查看容器的详细信息
docker exec -it 容器id /bin/bash 进入容器
docker logs 容器id --tail=100 查看容器日志
k get po
k get po -A
k get po -A | grep xxxx
k describe po -n namespace pod名字 查看pod详细信息
k logs -n namespace pod名字 --tail=10
k logs -n namespace pod名字 -f 实时更新
k exec -it pod名字 /bin/bash 进入pod
k get svc
k get svc -A
k get svc -A | grep xxxx
k describe svc svc名字 查看svc一些信息
k exec -it pod名字 /bin/bash 进入pod
不必 进入和查看日志
配置相关
查看配置
kubectl get cm
查看某个配置
kubectl get cm <name> -o yaml
编辑配置
kubectl edit cm <name>
应用yaml文件
kubectl apply -f xxx.yaml
查看日志
kubectl logs -f <pod> -c <container> -n <namespace>
查看pod(获取pod):kubectl get pod --all-namespaces
查看pod里的container等信息(获取container):kubectl get pod -n industry industry-platform-service-747b74fdc6-6875x -o yaml
查看日志:kubectl logs -f industry-platform-service-7558875577-74qjl -c industry-platform
日志
查看后十行日志
kubectl logs <pod> --tail=10
查看上次启动的日志
kubectl logs <pod> -p
# --since 10m是只打印10分钟内的日志,日志太多的时候可以使用这个参数
kubectl logs -f ( -n {namespace} ) {pod-name} ( --since 10m )
修改image版本
namespace,deployment,container,镜像路径:tag
kubectl --namespace=<namespace> set image <deployment> <container>=imageurl:tag
例:
服务端
kubectl --namespace=default set image deployment/industry-platform-service industry-platform=industry-platform:0.0.1-190-g08c0b3f
前端
kubectl --namespace=default set image deployment/industry-platform-service frontend=spring-test/industry-frontend:2.0.54
进入容器的交互式shell
kubectl exec <pod> -it /bin/bash
例:
kubectl exec industry-platform-service-7558875577-74qjl -it /bin/bash
跨namespace名称空间访问Service服务
Kubernetes的两个Service(ServiceA、ServiceB)和对应的Pod(PodA、PodB)分别属于不同的namespace名称空间,现需要PodA和PodB跨namespace名称空间并通过Service实现互访。应该如何实现?
[root@k8s-master cross_ns]# kubectl get pod -A -o wide | grep -E '(my)|(NAME)'
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myns myapp-deploy1-5b9d78576c-wfw4n 1/1 Running 0 41m 10.244.2.136 k8s-node02 <none> <none>
myns myapp-deploy1-5b9d78576c-zsfjl 1/1 Running 0 41m 10.244.3.193 k8s-node01 <none> <none>
mytest myapp-deploy2-dc8f96497-nnkqn 1/1 Running 0 41m 10.244.3.194 k8s-node01 <none> <none>
mytest myapp-deploy2-dc8f96497-w47dt 1/1 Running 0 41m 10.244.2.137 k8s-node02 <none> <none>
[root@k8s-master cross_ns]#
[root@k8s-master cross_ns]#
[root@k8s-master cross_ns]# kubectl get svc -A -o wide | grep -E '(my)|(NAME)'
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
myns myapp-clusterip1 ClusterIP 10.100.61.11 <none> 80/TCP 41m app=myapp,release=v1
mytest myapp-clusterip2 ClusterIP 10.100.201.103 <none> 80/TCP 41m app=myapp,release=v2
pod跨名称空间namespace与Service通信
说明:是通过Service的NAME进行通信,而不是Service的IP【
因为每次重启Service,NAME不会改变,而IP是会改变的
】。
# 进入ns名称空间下的一个Pod容器
[root@k8s-master cross_ns]# kubectl exec -it -n myns myapp-deploy1-5b9d78576c-wfw4n sh
/ # cd /root/
### 如下说明在同一名称空间下,通信无问题
~ # ping myapp-clusterip1
PING myapp-clusterip1 (10.100.61.11): 56 data bytes
可以ping通
### 如下说明在不同的名称空间下,通过Service的NAME进行通信存在问题
~ # ping myapp-clusterip2
ping: bad address 'myapp-clusterip2'
不通
解决
通过
Service的ExternalName类型
即可实现
跨namespace名称空间与Service通信
。
Service域名格式:
$(service name).$(namespace).svc.cluster.local
,其中 cluster.local 为指定的集群的域名
相关yaml文件
[root@k8s-master cross_ns]# cat svc_ExternalName_visit.yaml
# 实现 myns 名称空间的pod,访问 mytest 名称空间的Service:myapp-clusterip2
apiVersion: v1
kind: Service
metadata:
name: myapp-clusterip1-externalname
namespace: myns
spec:
type: ExternalName
externalName: myapp-clusterip2.mytest.svc.cluster.local !!!!!重点看这里!!!!!
ports:
- name: http
port: 80
targetPort: 80
---
# 实现 mytest 名称空间的Pod,访问 myns 名称空间的Service:myapp-clusterip1
apiVersion: v1
kind: Service
metadata:
name: myapp-clusterip2-externalname
namespace: mytest
spec:
type: ExternalName
externalName: myapp-clusterip1.myns.svc.cluster.local !!!!!重点看这里!!!!!
ports:
- name: http
port: 80
targetPort: 80
运行yaml文件
[root@k8s-master cross_ns]# kubectl apply -f svc_ExternalName_visit.yaml
[root@k8s-master cross_ns]# kubectl get svc -A -o wide | grep -E '(ExternalName)|(NAME)'
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
myns myapp-clusterip1-externalname ExternalName <none> myapp-clusterip2.mytest.svc.cluster.local 80/TCP 28s <none>
mytest myapp-clusterip2-externalname ExternalName <none> myapp-clusterip1.myns.svc.cluster.local 80/TCP 28s <none>
测试
myns 名称空间的pod,访问 mytest 名称空间的Service:myapp-clusterip2
[root@k8s-master cross_ns]# kubectl exec -it -n myns myapp-deploy1-5b9d78576c-wfw4n sh
/ # cd /root/
### 如下说明在同一名称空间下,通信无问题
~ # ping myapp-clusterip1
PING myapp-clusterip1 (10.100.61.11): 56 data bytes
可以ping通
### 如下说明通过Service externalname类型,实现了Pod跨namespace名称空间与Service访问
~ # ping myapp-clusterip1-externalname
PING myapp-clusterip1-externalname (10.100.201.103): 56 data bytes
可以ping通
在外部cmd怎么访问service/pod服务
访问service
ping [service-name].[namespace].svc.cluster.local
访问pod
http://pod_name.所在svc_name.pod_namespace.svc.cluster.local:svc_port/
底层更新镜像步骤
1 下载镜像包
在线下载
docker pull 镜像仓库/镜像
离线导入
1 导入镜像
docker load 镜像包name
#查看镜像,会看到该镜像的名称为空
dokcer images | head
2 镜像打tag
docker tag IMAGEid 本地仓库地址
3、上传镜像至本地仓库
docker push 本地仓库地址
2 修改配置
进入到对应的pod的目录 例如
cd /etc/kubernetes/apps/servicename,找到配置文件(不同服务使用的yml文件名不一致)
修改配置文件里边的image版本
....
image:10.84.194.43:8081/test/test:0.9.1-amd64
....
3 重新启动
更新完镜像版本后重新pod 就可以生效了 (可以重启这个路径下的所有pod ,也可以只用其单独修改的配置文件对应的pod )
kubectl delete -f /etc/kubernetes/apps/sevicename (停用该路径下的所有pod)
kubectl apply -f /etc/kubernetes/apps/servicename (停用后,重启该路径下的所有pod)
注意:如果不想对整个路径下的pod都重启,可以单独重启某个配置文件
总结
操作如下
第一步:cd /etc/kubernetes/apps/servicename
第二步执行: kubectl delete -f pod.yml (停用配置文件)
第三步执行:kubectl apply -f pod.yml (停用后,又启用配置文件) 即可生效
查看pod生命,最近是不是被delete,apply了
使用
history
,看最近的操作记录,
describe pod
查看 pod 的详细信息
Containers: pod中的容器们
nginx:
Container ID: docker://513e75ca4bed0b3bc08c68dbffed8b0069b68049dc802107b87d4b99913c9d08
Image: nginx:latest 使用镜像
Image ID: docker-pullable://nginx@sha256:9522864dd661dcadfd9958f9e0de192a1fdda2c162a35668ab6ac42b465f0603
Port: 80/TCP 容器端口
Host Port: 0/TCP 主机端口
State: Running
Started: Tue, 14 Dec 2021 18:00:34 +0800 !!!!pod的创建时间!!!!
..........
Events: !!!!pod的age!!!!
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 9m23s default-scheduler Successfully assigned dev/nginx-dd6b5d745-sbfpj to node1
Normal Pulling 9m16s kubelet, node1 Pulling image "nginx:latest"
Normal Pulled 9m kubelet, node1 Successfully pulled image "nginx:latest"
Normal Created 9m kubelet, node1 Created container nginx
Normal Started 9m kubelet, node1 Started container nginx
pod的生命周期
参考
Kubernetes Pod的生命周期(Lifecycle)
k8s 健康检查
健康检查(Health Check)
可用于
服务运行的状态监控
,比如腾讯旗下的DNSPOD的D监控,要求
配置一个访问路径
以
判断网站是否可以正常访问
,实际上就是一个健康检查,当
发现健康检查失败时会发送一个邮件通知或者短信来告知网站管理员进行维修
。
而在现代一些
分布式系统
中,用户访问不再是单台主机,而是一个由成百上千台实例组成的集群,
用户请求通过负载均衡器分发到不同的实例,负载均衡帮助解决单台服务器的访问压力
,同时提高了系统的高可用性,而健康检查常常作为当前实例是否“可用”的判断标准。
即:当
系统发现某台实例健康检查不通过
,
负载均衡器将不会把流量导向该实例
。
Kubernetes提供了
两种探针
来检查
容器的状态
,
Liveliness和Readiness
,根据官方文档,
Liveliness探针
是为了
查看容器是否正在运行
,翻译为
存活探针
(livenessProbe),
Readiness探针
是为了
查看容器是否准备好接受HTTP请求
,翻译为
就绪探针
(readinessProbe)。
在
Kubernetes中
,
Pod
是Kubernetes
创建及管理
的
最小的可部署的计算单元
,
一个Pod由一个或者多个容器(Docker,rocket等等)组成
,这些
容器共享内存,网络以及运行容器的方式
。
在Kubernetes上下文中存活探针和就绪探针被称作健康检查。这些容器探针是一些
周期性运行的小进程
,这些
探针返回的结果(成功,失败或者未知)反映了容器在Kubernetes的状态
。基于这些结果,Kubernetes会
判断如何处理每个容器
,以
保证弹性,高可用性和更长的正常运行时间
。
应用
强大的自愈能力
是 Kubernetes 这类
容器编排引擎
的一个重要特性。
自愈的默认实现方式是自动重启发生故障的容器
。除此之外,用户还可以利用
Liveness 和 Readiness
探测机制
设置更精细的健康检查
,进而实现如下需求:
零停机部署
避免部署无效的镜像
更加安全的滚动升级
就绪探针
就绪探针
旨在让Kubernetes
知道你的应用是否准备好为请求提供服务
。Kubernetes
只有在就绪探针通过才会把流量转发到Pod
。如果
就绪探针检测失败,K8s会把Pod从service endpoints中删除
,停止向该容器发送流量,直到它通过`。
存活探针
Liveness探测器
是让Kubernetes
知道你的应用是否活着
。如果你的应用还活着,那么Kubernetes就让它继续存在。
如果你的应用程序已经死了,Kubernetes将移除Pod并重新启动一个来替换它
。
startupProbe
用于
容器启动时
判断
容器是否启动完成
,在
这个探针探测成功前
,即
容器启动成功前
,
livenessProbe
和
readinessProbe
探针都
不会起作用
,
防止某些启动很慢的容器在启动过程中
,
livenessProbe探针探测容器失败触发容器重启导致死锁
。
如果这个探针
探测失败
(容器启动时间超过了阈值),kubelet会
把这个容器kill掉
,再
根据restartPolicy决定是否创建容器
探针类型
探针类型是
指通过何种方式来进行健康检查
,K8S有三种类型的探测:
HTTP,Command和TCP
httpGet: 发送HTTP请求,返回200-400范围状态码表示成功
exec: 执行Shell命令返回状态码为0表示成功
tcpSocket: 发起TCP Socket建立成功
HTTP
HTTP探测可能是
最常见的探针类型
。即使应用
不是HTTP服务
,也
可以创建一个轻量级HTTP服务器来响应探测
。比如让Kubernetes
通过HTTP访问一个URL
,如果
返回码在200到300范围内
,就
将应用程序标记为健康状态
,
否则
它被
标记为不健康
。
command
对于command探测,是指Kubernetes
在容器内运行命令
。如果
command以退出代码0返回
,则
容器将标记为正常
。否则,它被
标记为不健康
。
TCP
Kubernetes
尝试在指定端口上建立TCP连接
。如果它
可以建立连接
,容器被认为是
健康的
;如果它不能被认为是不健康的。这
常用于对gRPC或FTP服务的探测
。
重启策略
Always: 当容器终止后,总是重启,默认策略
OnFailure: 当容器异常退出(退出状态码非0)时,才重启容器
Never: 当容器终止退出,从不重启容器
初始探测延迟 initialDelaySeconds
可以
配置K8S健康检查运行的频率
,
检查成功或失败的条件
,以及
响应的超时时间
。
存活探针探测失败
会导致
pod重新启动
,所以配置初始探测延迟 initialDelaySeconds十分重要,要
确保在应用准备之后探针才启动
。否则,
应用将无限重启
!
示例
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: web
name: web
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
restartPolicy: Always
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: web
ports:
- containerPort: 80
livenessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 5
failureThreshold: 2
periodSeconds: 5
readinessProbe:
httpGet:
path: /index.html
port: 80
initialDelaySeconds: 5
periodSeconds: 5
startupProbe: # startupProbe
httpGet:
path: /healthz
port: liveness-port
failureThreshold: 30
periodSeconds: 10 # 容器启动30*10=300s内liveness和readiness探针不会执行,300s后startup探针还没成功,容器会被kill掉重启
---
apiVersion: v1
kind: Service
metadata:
labels:
app: web
name: web
spec:
ports:
- port: 80
protocol: TCP
selector:
app: web
type: NodePort
initialDelaySeconds: 第一次启动Pod后等待多久时间开始健康检查
failureThreshold: 探针执行失败几次后容器状态变为失败,默认3次
periodSeconds: 健康检查的周期
问题
spark 使用job的方式部署,导致pod被删除(包括delete/evict,常见为大数据节点服务器断电、网络异常)后无法重启,影响相关功能
1 检测是否 hdfs出问题
查看Namenode的pods是否处于Running状态及是否有过重启的记录
查看pods状态
# kubectl -n component get pods -o wide | grep hdfs
查看Namenode是否处于safemode模式
# kubectl -n component exec -it hdfs-namenode-default-0 -- hdfs dfsadmin -safemode get
如果是的话,退出安全模式
kubectl -n component exec -it hdfs-namenode-default-0 -- hdfs dfsadmin -safemode leave
kubernetes启动Pod遇到CrashLoopBackOff的解决思路
问题现状
[root@master ~]# kubectl get pods -n kube-system | grep -v Running
NAME READY STATUS RESTARTS AGE
prometheus-tim-3864503240-rwpq5 0/1 CrashLoopBackOff 2516 8d
查找问题
[root@master ~]# kubectl describe pod prometheus-tim-3864503240-rwpq5 -n kube-system
...
spec.containers{prometheus} Warning BackOff Back-off restarting failed container
8d 4s 59160 kubelet, test-95 Warning FailedSync Error syncing pod
查看异常pod的日志
...
level=info ts=2018-01-29T03:11:57.9034449Z caller=main.go:394 msg="Loading configuration file" filename=/etc/prometheus/prometheus.yaml
level=error ts=2018-01-29T03:11:57.903503914Z caller=main.go:356 msg="Error loading config" err="couldn't load configuration (--config.file=/etc/prometheus/prometheus.yaml): open /etc/prometheus/prometheus.yaml: no such file or directory"
发现是 /etc/prometheus/prometheus.yaml 中的问题
一个pod 对应一个容器,一个镜像
进入
容器
或者
pod
,
pod / 容器中的目录
是
pod.yml 中挂载上去的
docker exec -it contatiner_id bash
kubectl exec -it -n namespace pod_name bash
rancher
看下面的文章 知道是干啥的,会用即可
Rancher 使用介绍(可以通过界面管理 K8s 平台)
Rancher1-简单介绍-认识rancher
Rancher的优点及不足