跳到主要内容

k8s常见问题总结[持续更新...]

·2329 字·5 分钟·

1. 怎么手动停止pod #

在k8s集群中, 你可能会遇到pod无法删除的问题。 比如pod的terminationGracePeriodSeconds设置的很长, 可以通过下面方式来停止:

  1. 进入pod 容器中,查看阻塞的进程pid,kill掉改进程。
  2. 如果阻塞进程是1号进程, 是无法通过kill的手段操作的,可以登录所在的机器查看容器的进程
直接终止容器内的进程可能会导致应用状态不一致或数据丢失。在执行这些操作之前,最好确保了解Pod中运行的应用以及相应的风险

如果使用containerd作为容器runtime, 使用ctr命令查找到对应的容器

ctr -n k8s.io container ls | grep $imageName
#获取到taskID
ctr -n k8s.io task kill taskID

或者使用crictl命令

crictl ps -a  | grep $podName 
#获取到containerID
crictl stop $containerID
  1. 为什么ctr c ls无法查看pod的容器。 因为在k8s中,所有k8s的containerd的容器、镜像等魔人均位于的k8s.io的namespace下,这个和k8s pod的namespace是不一样的。 而containerd默认在default namespace下
  2. containerd 的container概念和task 与docker的container概念有差异。 即contaienrd创建container后,并不会启动容器。 ctr start 容器后,才会拉起进程, 称为task。

2. 如何更新configmap #

有时候我们会将一组文件创建为一个configmap资源,并通过以下命令

#path为一组配置文件路径
kubectl create cm configmap-demo --from-file=$path/

但是更新的时候,没办法使用kubectl apply的命令,会提示以下错误。kubectl apply不支持--from-file 的参数。

error: unknown flag: --from-file
See 'kubectl apply --help' for usage.

但是我们可以通过管道的方式来完成, 通过--dry-run=client 参数将会打印出要创建到apiserver上的内容, 然后该内容将作为单个文件通过管道被apply成功

kubectl create cm configmap-demo --from-file=$path/ --dry-run=client -o yaml | k apply -f -

可以通过kubectl help apply 查看支持的参数,如果configmap是是单个文件的话,则可以通过-f参数来更新

kubectl apply cm configmap-demo -f $filename

3. 如何筛选符合条件的Pod/Node等 #

LabelSelector #

labelSeletor包含两种语义的表达, Equality-basedSet-Based, 实际上Equality-basedSet-Based的一种特化场景。

Equality-based #

基于等式的条件表达式, 即key, value的条件为 = == != ,其中前两个是等价的。 这种情况下,就要求value必须是唯一的,且key, value是在筛选时必须给定。

通过kubectl工具可以实现这些筛选,用法如下 kubectl get pod -l key1=value1,key2=value2

Set-Based #

基于集合的条件表达式,这种方式将筛选条件表达为 key Operator value 三元组。 其中Operator包含了in notinexists。比如,

selector:
  matchLabels:
    component: redis
  matchExpressions:
    - { key: tier, operator: In, values: [cache] }
    - { key: environment, operator: NotIn, values: [dev] }

而对于以下需求,情况就稍显复杂, 就需要依赖Set-Based语义来表达。

  • 仅筛选包含该key的资源 kubect get pod -l $key
  • 筛选不包含该key的 kebectl get pod -l $key exists
  • 符合某个key,但是value可以有多个选择的 kubectl get pods -l $key in ($value1, $value2)
  • 符合某个key,但是value不符合某些选项的 kubectl get pods -l $key notin ($value1, $value2)

4. LabelSelector在ListOptions怎么写 #

除了在kubectl命令或者yaml中使用labelSelector, 我们在代码中使用以下的方式来实现List过程的筛选, 这里仅以Pod为例,实际上LabelSelector同样支持Job,Deployment, ReplicasSet, DeamonSet等多种资源。

func FilterPods() {
        selector := metav1.LabelSelector{
		MatchLabels:      make(map[string]string),
		MatchExpressions: make([]metav1.LabelSelectorRequirement, 0),
	}
	lsr := metav1.LabelSelectorRequirement{
		Key:      "apps.kruise.io/cloneset-instance-id",
		Operator: metav1.LabelSelectorOpExists,
		Values:   nil,
	}
	selector.MatchExpressions = append(selector.MatchExpressions, lsr)

	listOptions := metav1.ListOptions{LabelSelector: selector.String()} //这里是错误的用法
	podList, err := c.clientSet.CoreV1().Pods(so.Namespace).List(context.TODO(), listOptions)
	if err != nil {
		klog.Errorf("%+v get pod list error %v", so.NamespacedName, err)
		return nil, err
	}
}	

会报以下错误

get pod list error unable to parse requirement: <nil>: Invalid value: "&LabelSelector{MatchLabels:map[string]string{}": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName',  or 'my.name',  or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')

selector.String()实际上是返回了一个pb格式的string串,并不是LabelSelector string要求的格式"x equal y, x in (y1, y2)"。 正确写法如下

listOptions := metav1.ListOptions{LabelSelector: metav1.FormatLabelSelector(&selector)}

//或者如果是确定规则,也可以直接hardcode ~ 
listOptions := metav1.ListOptions{LabelSelector: "key1 in (value1, value2)"}

LabelSelector api给了我们两个字段来表示筛选条件

  • MatchLabels, 一个map类型,这个对应的就是equal-based的语义,即Key Operator Value是三元组中Opeartor是Equal
  • MatchExpression, 则是Set-Based语义标准的Key Operator Value模式 这两类在最终语义的表达上,都会被转换为内部的Requirement 结构,即
type Requirement struct {
	key      string
	operator selection.Operator
	// In huge majority of cases we have at most one value here.
	// It is generally faster to operate on a single-element slice
	// than on a single-element map, so we have a slice here.
	strValues []string
}

所以我们看下 LabelSelectorapi是怎么被FormatLabelSelector处理为string的。FormatLabelSelector 实现如下:

func FormatLabelSelector(labelSelector *LabelSelector) string {
        //这里先将labelSelector api格式转换为内部的Selector格式,具体就是一组[]Requirements表示。
	selector, err := LabelSelectorAsSelector(labelSelector)
	if err != nil {
		return "<error>"
	}

	l := selector.String()
	if len(l) == 0 {
		l = "<none>"
	}
	return l
}

//其中,LabelSelectorAsSelector使用了默认的internalSelector, 这是一个[]Requirement类型。
type internalSelector []Requirement
func (s internalSelector) String() string {
   var reqs []string
   for ix := range s {
      reqs = append(reqs, s[ix].String())
   }
   return strings.Join(reqs, ",")
}

如果我们的label条件仅包含Equal语义,或者In Values中仅有一个值。那么我们还可以通过 LabelSelectorAsMap 方法,将LabelSelector api转换为一个map[string]string格式,然后对map进行序列化。

labelMap, err := metav1.FormatLabelSelector(&selector)
listOptions := metav1.ListOptions{LabelSelector: metav1.FormatLabelSelector(&selector)}
LabelSelectorAsMap 这种方式不适合筛选条件包含 InNotInExistsNotExist。 除非In的条件仅有一个Value