首页 > 技术文章 > Custom `Namespace` cluster wide resource reports 404 when trying to GET

codenoob 2020-12-23 16:51 原文

提问者定义的CRD配置文件是这样的

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  creationTimestamp: null
  name: namespaces.test.my.domain
spec:
  group: test.my.domain
  names:
    kind: Namespace
    listKind: NamespaceList
    plural: namespaces
    singular: namespace
  scope: Cluster
  versions:
  - name: v1beta1
    schema:
      openAPIV3Schema:
        description: Test is the Schema for the tests API
        properties:
          apiVersion:
            description: 'APIVersion defines the versioned schema of this representation
              of an object. Servers should convert recognized schemas to the latest
              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
            type: string
          kind:
            description: 'Kind is a string value representing the REST resource this
              object represents. Servers may infer this from the endpoint the client
              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
            type: string
          metadata:
            type: object
          spec:
            description: TestSpec defines the desired state of Test
            properties:
              foo:
                description: Foo is an example field of Test. Edit Test_types.go to
                  remove/update
                type: string
            type: object
          status:
            description: TestStatus defines the observed state of Test
            type: object
        type: object
    served: true
    storage: true
    subresources:
      status: {}
status:
  acceptedNames:
    kind: ""
    plural: ""
  conditions: []
  storedVersions: []

他把自己定义的资源 kind 就设置为了 namespace。资源的复数形式就是 namespaces,然后启动了一个名为 namespace-sample 的实例。
比如我们随便启动一个

apiVersion: test.my.domain/v1beta1
kind: Namespace
metadata:
  name: namespace-sample

然后使用以下命令获取资源:
kubectl get namespaces.test.my.domain namespace-sample
会报出错误。调整一下日志等级,在命令后面添加 --v=6,接着去查看 kubectl 的日志输出,有一个资源请求的时候出问题了。

GET https://10.20.9.140:6443/apis/test.my.domain/v1beta1/namespaces/namespace-sample 404 Not Found in 2 milliseconds
"metadata": {},
"status": "Failure",
"message": "the server could not find the requested resource (get namespaces.test.my.domain namespace-sample)",
"reason": "NotFound",
"details": {
  "name": "namespace-sample",
  "group": "test.my.domain",
  "kind": "namespaces",
  "causes": [
    {
      "reason": "UnexpectedServerResponse",
      "message": "404 page not found"
    }
  ]
}
"code": 404

出问题的地方应该是这个文件
kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/request/requestinfo.go 中的 NewRequestInfo 方法(line:117)
在kubernetes中,使用 get 获取 CRD 的 url 规则是这样的:
apis/<group_name>/<version>/<resourceName>/<subresource>
对应到日志里面实际报错的信息 URL 是这样的:
/apis/test.my.domain/v1beta1/namespaces/namespace-sample
但是,kubernetes 中还有其他的 URL 规则,比如获取一些内置资源的 URL 是这样的:

需要注意的是,红框里面的 namespaces 这个是写死的,不可变的。
既然 kubectl 请求这个 URL 出了问题,我们按照 url=/apis/test.my.domain/v1beta1/namespaces/namespace-sample 走一下代码流程:

到这里,URL 前面的几个参数都被分离了出去,还剩下 资源名字 和 资源实例。现在 currentParts=["namespaces", "namespace-sample"]
接着往下走,到了这一步:

如果走了这个 if 分值的话,下面就会接着把 namespace-sample 分配给 requestInfo.Namespace,这明显是错误的。

获取 CRD 不应该走上面的 if 分支,而要走下面的这个判断

所以我觉得最大的问题还是为什么要把 CRD 的名字设置为 namespace,那么可以使得这种情况不 panic 吗?应该是可以的,但是目前来看是有点难。URL 的匹配规则比较复杂,这种冲突要解决的话还得仔细想想怎么不影响现有的规则。
但是,我觉得最简单的办法就是不要使用 namespace 做资源的 kind,本来这种命名就让人十分的困惑。
目前来看应该确实是这样导致的,比如我们什么都不改,只是把上述 crd 的 yaml 文件中 spec.plural 改为 namespacess,在原本的后面多加一个 s,自然我们的 metadata.name 也就变成了namespacess.test.my.domain,然后在执行相同的操作。

这次在获取资源的时候就不会报错了。

推荐阅读