8. apiserver(五、版本转换)
k8s对于Deployment这些资源有外版本、内版本、存储版本的概念,处理逻辑都是针对内版本进行的,存储使用的是存储版本,面向用户的是外版本。
ExternalVersion(input) -> InternalVersion(handle) -> StorageVersion(store)
8.1 版本初识
内版本示例:
type Deployment struct {
metav1.TypeMeta
// +optional
metav1.ObjectMeta
// Specification of the desired behavior of the Deployment.
// +optional
Spec DeploymentSpec
// Most recently observed status of the Deployment.
// +optional
Status DeploymentStatus
}
外版本示例:
type Deployment struct {
metav1.TypeMeta `json:",inline"`
// Standard object's metadata.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
// +optional
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Specification of the desired behavior of the Deployment.
// +optional
Spec DeploymentSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
// Most recently observed status of the Deployment.
// +optional
Status DeploymentStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
内外版本在定义上很明显的区别就在于外版本在字段定义之外还有json:",inline"这种序列化标记,因为外版本对应的是用于输入或者输出时的文件。
存储版本则是外版本其中的一个,一般是最新的stable外版本。
8.2 版本转换函数
下面为hpa的两个转换函数:
func Convert_autoscaling_HorizontalPodAutoscaler_To_v1_HorizontalPodAutoscaler(in *autoscaling.HorizontalPodAutoscaler, out *autoscalingv1.HorizontalPodAutoscaler, s conversion.Scope) error {
...
}
func Convert_v1_HorizontalPodAutoscaler_To_autoscaling_HorizontalPodAutoscaler(in *autoscalingv1.HorizontalPodAutoscaler, out *autoscaling.HorizontalPodAutoscaler, s conversion.Scope) error {
...
}
注册入口为:
// Install registers the API group and adds types to a scheme
func Install(scheme *runtime.Scheme) {
// 1. 注册内部版本
utilruntime.Must(autoscaling.AddToScheme(scheme))
// 2. 注册外部版本
utilruntime.Must(v2beta2.AddToScheme(scheme))
utilruntime.Must(v2.AddToScheme(scheme))
utilruntime.Must(v2beta1.AddToScheme(scheme))
utilruntime.Must(v1.AddToScheme(scheme))
// 3. 设置版本顺序,注意这里的第一个也是存储版本
utilruntime.Must(scheme.SetVersionPriority(v2.SchemeGroupVersion, v1.SchemeGroupVersion, v2beta1.SchemeGroupVersion, v2beta2.SchemeGroupVersion))
}
8.3 版本转换流程
下面以hpa创建为例,探究一下版本转换流程:
func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Interface, includeName bool) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
...
// 1. 转成内部版本
decoder := scope.Serializer.DecoderToVersion(decodeSerializer, scope.HubGroupVersion)
obj, gvk, err := decoder.Decode(body, &defaultGVK, original)
...
requestFunc := func() (runtime.Object, error) {
// 2. 在存储进etcd的时候转成storage版本
return r.Create(
ctx,
name,
obj,
rest.AdmissionToValidateObjectFunc(admit, admissionAttributes, scope),
options,
)
}
...
result, err := finisher.FinishRequest(ctx, func() (runtime.Object, error) {
...
result, err := requestFunc()
...
})
...
// 3. 返回给用户之前再转换成用户指定的版本
transformResponseObject(ctx, scope, req, w, code, outputMediaType, result)
}
}
8.3.1 decode
// Decode attempts a decode of the object, then tries to convert it to the internal version. If into is provided and the decoding is
// successful, the returned runtime.Object will be the value passed as into. Note that this may bypass conversion if you pass an
// into that matches the serialized version.
func (c *codec) Decode(data []byte, defaultGVK *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
...
// 1. 序列化成用户指定版本
obj, gvk, err := c.decoder.Decode(data, defaultGVK, decodeInto)
...
if into != nil {
...
// 2. 转成内部版本
if err := c.convertor.Convert(obj, into, c.decodeVersion); err != nil {
return nil, gvk, err
}
return into, gvk, strictDecodingErr
}
...
}
8.3.2 encode
向etcd存储的流程如下:
func (s *store) Create(ctx context.Context, key string, obj, out runtime.Object, ttl uint64) error {
...
// 1. 转为存储版本,并进行序列化
data, err := runtime.Encode(s.codec, obj)
...
if out != nil {
// 2. 再将out转成内部版本
err = decode(s.codec, s.versioner, data, out, putResp.Header.Revision)
...
}
return nil
}
其中第1步转为存储版本的步骤为:
func (c *codec) doEncode(obj runtime.Object, w io.Writer, memAlloc runtime.MemoryAllocator) error {
encodeFn := c.encoder.Encode
...
// 1. 将内版本转换成存储版本
out, err := c.convertor.ConvertToVersion(obj, c.encodeVersion)
...
// 2. 序列化到w
return encodeFn(out, w)
}