图解Kubernetes的Pod核心资源-来白嫖啊
推荐手机阅读原文:https://mp.weixin.qq.com/s/nR6P6eidE1r5A2viLCFHWA
推荐手机阅读阅读:https://mp.weixin.qq.com/s/nR6P6eidE1r5A2viLCFHWA
推荐手机阅读阅读:https://mp.weixin.qq.com/s/nR6P6eidE1r5A2viLCFHWA
一、Pod定义
如下图,在K8S中资源调度的基本单位是Pod
Pod其实是一个抽象的概念,Pod里是我们的业务容器(docker/containerd)。像大家听过的Deployment、StatefulSet、CronJob等资源调度对象所调度的资源都是Pod。
为了更好的理解Pod的概念,大家可以将Pod理解成VM 虚拟机,将Pod中的容器理解成VM中的进程。既然这样理解,就意味着Pod中的容器进程可以直接通过localhost+端口号实现网络互通,也意味着Pod中的容器可以实现类似直接读取彼此产出到磁盘上的文件的效果。
如上图:容器1访问:127.0.0.1:8082
可以访问到容器2。
在实际用应用中,比如我们有两个服务:服务A和服务B,并且他俩之间只能通过本地回环网卡通信,那我们在就应该将它们分配进同一个pod中。
那什么是资源调度? 简单来说就是将:为Pod挑选一个合适的物理节点,然后将Pod中的容器启动好对外提供服务。
二、Pod入门yaml描述文件
简单的,只要将我们想启动的Docker容器填入Pod资源对象的containers字段中,再通过kubectl命令创建Pod,K8S会为Pod选择一个合适的Node,并在该Node上启动用户指定的容器。
如下是一个Pod的Yaml描述文件,Pod中定义了两个容器分别是:nginx、shell
apiVersion: v1 # 必选,API的版本号
kind: Pod # 必选,类型Pod
metadata: # 必选,元数据
name: daemon-pod # 必选,符合RFC 1035规范的Pod名称
spec:
containers:
- name: nginx
image: nginx:latest # 必选,容器所用的镜像的地址
- name: shell
image: busybox
stdin: true
tty: true
使用kubectl命名创建Pod,可以看到Ready数为2、Node字段说明该Pod运行在叫node02的宿主机上
验证,找到node02
登录node02,查看node02上是否有相应的docker容器
可以看到K8S不光启动了nginx、busybox容器,还多启动一个叫pause的容器,大家也叫它infra容器。
Infra容器的作用:Pod中的所有容器会共享一个NetworkNamespace,因为在创建pod中的业务容器前,会先创建pause容器占用NetworkNamespace,后续创建的业务容器都加入到pause的网络中,相当于在Docker run命令中添加参数:--net=container:pause
,这也是为什么Pod中的所有容器的ip其实都是pod的ip。
如下图,进入shell容器中,看到它的ip其实就是上图中的pod ip。
在nginx容器中,也能看到容器的ip就是pod的ip
三、共享NetworkNamespace
如下图是K8S创建Pod时,Pod的网络协议栈的初始化过程
简单解读,理解pause容器是K8S网络模型中的精髓~
- kubelet通过CRI协议向底层的容器运行时(docker/containerd)下发命令,让它创建一个叫pause 的初始化容器,这个pause容器里运行着一个极简的C程序,具体的逻辑就是将自己阻塞中,目的是让pause容器快速占用并一直持有一个networkname
- 创建pause容器时,会携带参数
--net=node
意为不初始化网络协议栈,说白了就是除了自带的lo回环网卡外,不添加其他的网卡。 - kubelet通过CNI协议为pause容器初始化网络协议,也就是给它添加网络并分配IP
- Pod中定义的业务容器都加入pause容器的network namespace,它们都使用统一分配给pause的IP
疑问:为什么pause容器的网络协议栈不由容器运行时创建它时立即分配好呢?
答:这是个好问题,这么做也是呼应了K8S网络的核心目标思想:
IP分配,换句话说K8S要保证在整个集群中每个POD要独立不重复的IP地址
IP路由,换句话说K8S要保证在整个集群中各个POD的IP是要互通的
这也是它为什么设计这个流程的原因
四、共享PID
默认情况下Pod中的各容器是不会共享同一个统一个PID Namespace的,需要手动添加参数shareProcessNamespace: true
apiVersion: v1
kind: Pod
metadata:
name: daemon-pod
spec:
# 共享pid namespace
shareProcessNamespace: true
containers:
- name: nginx
image: nginx:latest
- name: shell
image: busybox
stdin: true
tty: true
验证:如下图,在shell容器中可以看到nginx进程(通过这更好的将pod理解成虚拟机),同理登陆pod重点任意容器也能看到pid=1的进程是pause进程。
此时pause容器为Pod提供1号进程,在Uninx中,进程为1的进程被称作init进程。
这个init进程很特殊,因为它会维护一张进程表并不断的检测其他进程的状态,当出现:子进程因父进程的异常退出而变成“孤儿进程”或者是叫“僵尸进程”时,init进程(pause)会收养这个游离的进程,然后在它退出时,释放它占用的资源,否则会可能会出现大量的僵尸进程占用操作系统的进程表项。
在k8s1.8之前,默认是启用共享pid namespace
在k8s1.8之后,则需要像本小节一样,通过参数shareProcessNamespace
显示的开启
问:既然共享了pause的pid有这么多好处,为啥后续版本的k8s不再默认开启了呢?
答:一方面:k8s推荐的做法是,单个pod里面放尽量少的容器,如只放你的业务容器,这样僵尸进程带来的影响几乎可以忽略不计,共享与不共享,意义不大。
另一方面:像一些特殊的如systemd镜像,启动需要获取pid1,否则无法启动
五、容器生命周期
可以通过lifecycle
字段在容器创建完成后以及关闭前执行指定的动作,如创建用户/创建目录,启动脚本等
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:latest
lifecycle:
postStart: # 容器创建完成后执行的指令, 可以是exec httpGet TCPSocket
exec:
command:
- sh
- -c
- 'mkdir /data/ '
preStop: # 关闭前的操作
httpGet:
path: /
port: 80
# exec:
# command:
# - sh
# - -c
# - sleep 9
restartPolicy: Always
spec.containers.lifecycle.postStart
参数可以指定容器在创建完成后执行一段指令
回忆一下,容器还有个command参数即:spec.containers.command
也可以指定一段指令。注意点如下:
- 这俩command执行的先后并不能100%保证。
spec.containers.lifecycle.postStart
的执行依赖于容器创建后的环境
而spec.initContainers.command
的不会依赖业务容器的环境,执行时间也会先于如上两个command。
六、初始化容器
6.1、简介
业务容器的启动依赖很多环境配置,如:
- wget等可以从网络上下载文件的命令
- 或者是有些命令需要以root的权限运行初始化,如修改文件的权限、修改内核参数
如果有攻击性的程序获得了使用这些命令的权限,就会有很大的安全隐患,为了安全,我们是不希望业务容器中包含这些危险的命令。
这时可以使用initcontainer完成这种工作,因为initcontainer运行结束后会退出,没有后续的安全隐患。
6.2、与普通容器的区别
- 初始化容器会依次执行,上一个运行结束,下一个才会执行。
- 初始化容器不成功结束,不会启动业务容器,K8S会不断的重启该Pod。但是如果Pod的restartPolicy设置为Never,K8S就不再重启该Pod了。
- init容器不支持:lifecycle、livenessProbe、readinessProbe、startupProbe探针
6.3、实验
准备yaml,initContainer和业务容器共享挂载volume的方式,让业务容器共享initContainer的初始化文件
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: test-init-container
name: test-init-container
spec:
replicas: 1
selector:
matchLabels:
app: test-init-container
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: test-init-container
spec:
volumes:
- name: data
emptyDir: {}
initContainers:
- name: init01
image: busybox
volumeMounts:
- name: data
mountPath: /tmp
command:
- sh
- -c
- touch /tmp/test-init-container.txt
containers:
- image: nginx
name: nginx
volumeMounts:
- name: data
mountPath: /tmp
查看Pod执行的Event可以看到先执行了初始化容器的相关操作
[root@master01 initContainer]# kubectl describe pod test-init-container-79d689d7d8-fgz2s
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 6m47s default-scheduler Successfully assigned default/test-init-container-79d689d7d8-fgz2s to master01
Normal Pulling 6m47s kubelet Pulling image "busybox"
Normal Pulled 6m43s kubelet Successfully pulled image "busybox" in 3.781619189s
Normal Created 6m43s kubelet Created container init01
Normal Started 6m43s kubelet Started container init01
Normal Pulling 6m42s kubelet Pulling image "nginx"
Normal Pulled 6m11s kubelet Successfully pulled image "nginx" in 31.093619016s
Normal Created 6m11s kubelet Created container nginx
Normal Started 6m11s kubelet Started container nginx
验收
[root@master01 initContainer]# kubectl exec -ti test-init-container-79d689d7d8-fgz2s -- sh
Defaulted container "nginx" out of: nginx, init01 (init)
# ls /tmp
test-init-container.txt
七、Pod探针
名称 | 作用 | 引入版本 |
---|---|---|
startupProbe | 1. 用于判断容器内的应用进程是否成功启动。 2. 若配置了startupProbe。直到它检测通过前,会禁用其他探针 3. startupProbe检测未通过,会使用restartPolicy重启策略重启 4. startupProbe探测成功后将不再探测。 |
1.16 |
readinessProbe | 1. 用户探测容器内的程序是否健康,是否可以接收流量 2. 探测成功表示该容器已经完全启动,可接收流量。 3. 若未配置,默认返回success |
|
livenessProbe | 1. 用于判断容器是否运行 2. 若探测失败kubelet根据重启策略重启容器 3. 若未配置,默认返回success |
检测方式一:ExecAction
原理如下:执行一个脚本,返回0表示成功,返回非0表示异常
[root@master01 yamls]# touch 1
[root@master01 yamls]# cat 1
[root@master01 yamls]# echo $?
0
[root@master01 yamls]# cat 123123.txt
cat: 123123.txt: 没有那个文件或目录
[root@master01 yamls]# echo $?
1
#!/bin/bash
#K8S 的存活探针
function liveness()
{
result=`nmap 127.0.0.1 -p $Target_PORT | grep $Target_PORT/tcp | awk '{print $2}'`
if [ "$result" != "open" ];then
echo 'port not open'
return 1
fi
}
liveness
检测方式二:TcpSocketAction
通过Tcp连接检查容器内的端口是否是连通的,如果连通,认为容器健康。原理如下:
# 连通的情况
[root@master01 yamls]# telnet 10.10.10.101 2380
Trying 10.10.10.101...
Connected to 10.10.10.101.
Escape character is '^]'.
^CConnection closed by foreign host.
# 非连通的情况
[root@master01 yamls]# telnet 10.10.10.101 2381
Trying 10.10.10.101...
telnet: connect to address 10.10.10.101: Connection refused
检测方式三:HttpGetAction
通过应用程序暴露的Http接口,来检查应用程序是否健康,若返回的状态码在[200,400)之间,认为程序健康。
7.1、livenessProbe
目的:判断容器是否启动了,检测失败后会重启容器
参考
livenessProbe:
failureThreshold: 5
httpGet:
path: /health
port: 8080
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
7.2、readinessProbe
目的:探测容器中的应用是否是正常的
检测通过:表示应用可以接收流量,READY状态变成1/1
[root@master01 yamls]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 54s
检测失败,READY状态变成0/1,且RESTARTS且0,表示不会重启容器
[root@master01 yamls]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx 0/1 Running 0 54s
参考
readinessProbe:
failureThreshold: 3
httpGet:
path: /ready
port: 8181
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
7.3、startupProbe
在有了livenessProbe和readinessProbe之后,为啥还整一个startupProbe出来呢?
可用来应对极端情况:应用启动时各种加载配置,导致启动的特别慢。 最终导致livenessProbe检查失败,当livenessProbe检查失败时,k8s会重启容器。 重启之后应用启动还是慢,livenessProbe还是失败,就进入了死循环
startupProbe其实就是将等待探测应用正常启动的步骤从livenessProbe中提取出来,放在livenessProbe步骤前。若配置了startupProbe,livenessProbe和readinessProbe会先被禁用,等startupProbe通过后,livenessProbe和readinessProbe才会生效。
实验:
apiVersion: v1 # 必选,API的版本号
kind: Pod # 必选,类型Pod
metadata: # 必选,元数据
name: nginx # 必选,符合RFC 1035规范的Pod名称
spec: # 必选,用于定义容器的详细信息
containers: # 必选,容器列表
- name: nginx # 必选,符合RFC 1035规范的容器名称
image: nginx:latest # 必选,容器所用的镜像的地址
ports: # 可选,容器需要暴露的端口号列表
- name: http # 端口名称,如果需要暴露多个端口,需要保证每个port的name不能重复
containerPort: 80 # 端口号
protocol: TCP # 端口协议,默认TCP
startupProbe:
httpGet: # httpGet检测方式,生产环境建议使用httpGet实现接口级健康检查,健康检查由应用程序提供。
path: /api/successStart # 检查路径
port: 80
restartPolicy: Always
因为没有/api/successStart
接口,所以startupProbe检测不通过
如下:pod的status为running,但是Ready为0
[root@master01 yamls]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx 0/1 Running 0 19s
查看详情:
startupProbe会按照继续按策略探测,当失败的次数达到预期后,会重启,如下重启了4次了
[root@master01 yamls]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx 0/1 CrashLoopBackOff 4 (27s ago) 2m57s
可以将HttpGet检测方式换成TcpSocket去检测80端口,startupProbe校验即可通过
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:latest
startupProbe:
tcpSocket:
port: 80
restartPolicy: Always
[root@master01 yamls]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 14s
八、Pod退出流程
当我们关闭或者删除pod时,Pod的状态会变成:Terninating
[root@master01 yamls]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx 0/1 Terninating 0 2m16s
另外你会发现,执行delete命令时,会等待一段时间
[root@master01 yamls]# kubectl delete pod nginx
pod "nginx" deleted
这个等待的时间是k8s给pod留出来的一段宽限期,可以通过kubectl edit pod xxx
查看pod的配置,默认情况下有一个叫terminationGracePeriodSeconds: 30
的参数,值为30秒,表示在pod被delete之后,有30秒的宽限期。
在这个宽限期中会做收尾的工作如:
- 将pod所属的service的endpoint对应记录删除
- 执行lifecycle 的 preStop 定义的命令
上文说过lifecycle,重新贴出来,如下:
lifecycle:
postStart: # 容器创建完成后执行的指令, 可以是exec httpGet TCPSocket
exec:
command:
- sh
- -c
- 'mkdir /data/ '
preStop: # 容器关闭前的操作
httpGet:
path: /
port: 80
exec:
command:
- sh
- -c
- sleep 9
九、HPA
9.1、简介
全称:Horizontal Pod Autoscaler
可以根据CPU、内存使用率或者是自定义的指标完成对Pod的自动扩缩容
查看K8S集群的HPA相关API
- v1版本是稳定版,只支持CPU指标
- v2beta1支持CPU、内存、自定义指标
- v2beta2支持CPU、内存、自定义指标、额外指标ExternelMetrics(公有云厂商提供)
9.2、使用
使用限制:
- 不能对如DaemonSet类型的资源进行扩所容。
- 必须安装了metrics-server
- 必须配置requests参数
准备测试环境
# 创建模版yaml
kubectl create deployment nginx-dp --image=nginx --dry-run=client -oyaml > nginx-dp.yaml
# 更新resources
containers:
- image: nginx
name: nginx
resources:
requests:
cpu: 10m
# 创建deployment
kubectl apply -f nginx-dp.yaml
# 查看pod的CPU指标
[root@master01 yamls]# kubectl top pod
NAME CPU(cores) MEMORY(bytes)
busybox 0m 0Mi
nginx-dp-84c4fd8fc6-s7mnx 0m 6Mi
# 为dp暴露一个service
kubectl expose deployment nginx-dp --port=80
可以通过如下命令使用HPA
# 当CPU使用率超过10%就扩容,扩容最大数Pod数为10,最小数为1
kubectl autoscale deployment nginx-dp --cpu-percent=10 --min=1 --max=3
压测,可以观察到pod会被自动扩容
while true; do wget -q -O- http://192.168.217.66 > /dev/null;done
注意点:如果CPU或者是Memory的飙升的源头是数据库压力,那么我们对pod进行扩容不仅没有好转,返回适得其反。
十、静态Pod
10.1、简介
静态Pod是由kubelet直接管理的,且只能在该kubelet所在的Node上运行。
静态Pod不受ApiServer管理,无法与ReplicationController、Deployment、DaemonSet进行关联。
kubelet也无法对静态Pod进行健康检查。
有两种方式创建静态Pod,分别是使用:静态文件/Http,若使用静态配置文件创建pod,需要在kubelet的启动参数statucPodPath
中指定静态Pod的yaml描述文件的位置。
10.2、实验
只要将pod的yaml文件放入指定的目录中,过一会便能通过kubectl查看到pod。
尝试通过kubectl删除Pod,会一直处于pending状态,这是因为kubectl会通过apiserver下发删除的命令,而apiserver无法管理静态pod。故,若想删除静态Pod,需要将对应的pod的yaml文件移出statucPodPath
。
十一、更多Pod属性
十二、对比DockerCompose、DockerSwarm
像Docker公司推出的集群调度工具:Docker Compose或是Docker Swarm它们调度的基本单位都是Docker容器。
点击查看白日梦的笔记:玩转Docker容器调度-DockerCompose、DockerSwarm
而在K8S中集群调度的基本单位是上文中长篇介绍的Pod,他们两者维度是不同的。Pod显然是站在更高的维度上。
因为在容器编排领域中,难点不是为容器选择一个合适的节点然后将容器启动好。难点是解决应用间复杂的相互依赖关系。
比如下:
- 不同应用之间通过本地文件相互通信
- 不同应用之间通过Http协议/RPC协议相互通信
- 不同应用之间通过localhost+端口号互通
- 在所有宿主机上均启动一个Pod副本
前文说了,大家可以把Pod理解成传统意义上的虚拟机,Pod中的容器相当于虚拟机中的不同应用,Pod中有哪些容器由开发人员说了算。
这样其实就是变相的将容器编排最为复杂环节的皮球重新踢给了开发人员,由他们自己描述好,也就实现了天然的解决了应用的复杂依赖关系编排这一难点。K8S调度时也要以Pod为基本单位去选择一个相对合适宿主机,批量的启动好Pod中的容器就行。
十三、参考
Kubernetes官网
《Kubernetes权威指南》
《Kubernetes网络原理与实践》
图解Kubernetes的Pod核心资源-来白嫖啊的更多相关文章
- kubernetes之常用核心资源对象
部门产品线本身是做DEVOPS平台,最近部署架构也在往K8S上靠了,不得不学一下K8S.自己搭建了K8S集群与harbor仓库来学习. 1.kubernetes之常用核心资源对象 1.1.K8s服务部 ...
- 图解kubernetes控制器StatefulSet核心实现原理
StatefulSet是k8s中有状态应用管理的标准实现,今天就一起来了解下其背后设计的场景与原理,从而了解其适用范围与场景 1. 基础概念 首先介绍有状态应用里面的需要考虑的一些基础的事情,然后在下 ...
- 白嫖党看番神器 AnimeSeacher
- Anime Searcher - 简介 通过整合第三方网站的视频和弹幕资源, 给白嫖党提供最舒适的看番体验~ 目前有 4 个资源搜索引擎和 2 个弹幕搜索引擎, 资源丰富, 更新超快, 不用下载, ...
- k8s核心资源之Pod概念&入门使用讲解(三)
目录 1. k8s核心资源之Pod 1.1 什么是Pod? 1.2 Pod如何管理多个容器? 1.3 Pod网络 1.4 Pod存储 1.5 Pod工作方式 1.5.1 自主式Pod 1.5.2 控制 ...
- 图解kubernetes调度器SchedulingQueue核心源码实现
SchedulingQueue是kubernetes scheduler中负责进行等待调度pod存储的对,Scheduler通过SchedulingQueue来获取当前系统中等待调度的Pod,本文主要 ...
- Kubernetes之Pod使用
一.什么是Podkubernetes中的一切都可以理解为是一种资源对象,pod,rc,service,都可以理解是 一种资源对象.pod的组成示意图如下,由一个叫”pause“的根容器,加上一个或多个 ...
- Kubernetes 学习10 Service资源
一.Service对应组件关系 1.在kubernetes平台之上,pod是有生命周期的,所以为了能够给对应的客户端提供一个固定的访问端点,因此我们在客户端和服务Pod之间添加一个固定的中间层,这个中 ...
- 图解kubernetes调度器SchedulerExtender扩展
在kubernetes的scheduler调度器的设计中为用户预留了两种扩展机制SchdulerExtender与Framework,本文主要浅谈一下SchdulerExtender的实现, 因为还有 ...
- Kubernetes概念及核心对象
想学习更多相关知识请看博主的个人博客地址:https://blog.huli.com/ https://blog.huli.com/ 1.kubernetes快速入门 Kubernetes 集群将所有 ...
随机推荐
- 从0到1搭建一款页面自适应组件(Vue.js)
组件将根据屏幕比例及当前浏览器窗口大小,自动进行缩放处理. 建议在组件内使用百分比搭配flex进行布局,以便于在不同的分辨率下得到较为一致的展示效果.使用前请注意将body的margin设为0,否则会 ...
- WPF开发随笔收录-本地日志LogUtil类
一.前言 生活中的日志是记录你生活的点点滴滴,让它把你内心的世界表露出来,更好的诠释自己的内心世界.而在开发者眼中的日志是我们排除问题的第一手资料,项目中的程序上线之后,一旦发生异常,第一件事就是先去 ...
- 基于.NetCore开发博客项目 StarBlog - (13) 加入友情链接功能
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...
- 跨模态语义关联对齐检索-图像文本匹配(Image-Text Matching)
论文介绍:Negative-Aware Attention Framework for Image-Text Matching (基于负感知注意力的图文匹配,CVPR2022) 代码主页:https: ...
- 渗透测试(PenTest)基础指南
什么是渗透测试? 渗透测试(Penetration Test,简称为 PenTest),是指通过尝试利用漏洞攻击来评估IT基础设施的安全性.这些漏洞可能存在于操作系统.服务和应用程序的缺陷.不当配置或 ...
- VGA设计(原理说明。Verilog代码实现,仿真结果)
各类显示屏的显示原理大部分是利用人眼的视觉暂留效应.比如之前的数码管显示就是设计每个周期内各个小段按顺序显示,来达到显示一个数字的效果. VGA同理,显示屏在显示时是一个像素一个像素地显示,在人眼看来 ...
- 简答一波 HashMap 常见八股面试题 —— 算法系列(2)
请点赞,你的点赞对我意义重大,满足下我的虚荣心. Hi,我是小彭.本文已收录到 GitHub · Android-NoteBook 中.这里有 Android 进阶成长知识体系,有志同道合的朋友,关注 ...
- NOI / 1.4编程基础之逻辑表达式与条件分支讲解-01:判断数正负
总时间限制: 1000ms 内存限制: 65536kB 题目: 描述 给定一个整数N,判断其正负. 输入 一个整数N(-109 <= N <= 109) 输出 如果N > 0, 输出 ...
- Git上传仓库
上传代码到gitee 方法1 1. 将远程仓库克隆到本地 git clone https://gitee.com/abc/aaa.git 2. 添加或修改本地文件 3. 将本地代码push到远程仓库 ...
- wdos centos64位通过yum来升级PHP
通过yum list installed | grep php可以查看所有已安装的php软件 使用yum remove php -- 将所有的包删除 通过yum list php*查看是否有自己需要安 ...