2022-01-20 调用k8s的api删除sts的pod分析
摘要:记录调用k8s的api删除pod的协议, 目的在于为了更精确的控制删除pod.k8s的api定义:"io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions": {"description": "DeleteOptions may be provided when deleting an API object.","properties": {"a
·
目录
二. 使其中一个pod所在的node处于not ready状态, 此时该pod处于Terming, 且无法自动重新调度到其他node上.
三. 执行client-go代码设置GracePeriodSeconds为0, 执行删除pod. 则pod被重新调度, 恢复running
摘要:
记录调用k8s的api删除pod的协议, 目的在于为了更精确的控制删除pod.
k8s的api定义:
"io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions": {
"description": "DeleteOptions may be provided when deleting an API object.",
"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"
},
"dryRun": {
"description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"items": {
"default": "",
"type": "string"
},
"type": "array"
},
"gracePeriodSeconds": {
"description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.",
"format": "int64",
"type": "integer"
},
"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"
},
"orphanDependents": {
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"type": "boolean"
},
"preconditions": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions",
"description": "Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned."
},
"propagationPolicy": {
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.",
"type": "string"
}
},
"type": "object",
"x-kubernetes-group-version-kind": [
{
"group": "",
"kind": "DeleteOptions",
"version": "v1"
},
]
},
以上协议没有强制删除的参数. 继续追踪
从k8s的kubectl的cmd命令着手
func (o *DeleteOptions) Complete(f cmdutil.Factory, args []string, cmd *cobra.Command) error {
cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
o.WarnClusterScope = enforceNamespace && !o.DeleteAllNamespaces
if o.DeleteAll || len(o.LabelSelector) > 0 || len(o.FieldSelector) > 0 {
if f := cmd.Flags().Lookup("ignore-not-found"); f != nil && !f.Changed {
// If the user didn't explicitly set the option, default to ignoring NotFound errors when used with --all, -l, or --field-selector
o.IgnoreNotFound = true
}
}
if o.DeleteNow {
if o.GracePeriod != -1 {
return fmt.Errorf("--now and --grace-period cannot be specified together")
}
o.GracePeriod = 1
}
if o.GracePeriod == 0 && !o.ForceDeletion {
// To preserve backwards compatibility, but prevent accidental data loss, we convert --grace-period=0
// into --grace-period=1. Users may provide --force to bypass this conversion.
o.GracePeriod = 1
}
if o.ForceDeletion && o.GracePeriod < 0 {
o.GracePeriod = 0
}
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
if err != nil {
return err
}
dynamicClient, err := f.DynamicClient()
if err != nil {
return err
}
o.DryRunVerifier = resource.NewDryRunVerifier(dynamicClient, f.OpenAPIGetter())
if len(o.Raw) == 0 {
r := f.NewBuilder().
Unstructured().
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions).
LabelSelectorParam(o.LabelSelector).
FieldSelectorParam(o.FieldSelector).
SelectAllParam(o.DeleteAll).
AllNamespaces(o.DeleteAllNamespaces).
ResourceTypeOrNameArgs(false, args...).RequireObject(false).
Flatten().
Do()
err = r.Err()
if err != nil {
return err
}
o.Result = r
o.Mapper, err = f.ToRESTMapper()
if err != nil {
return err
}
o.DynamicClient, err = f.DynamicClient()
if err != nil {
return err
}
}
return nil
}
type DeleteOptions struct {
resource.FilenameOptions
LabelSelector string
FieldSelector string
DeleteAll bool
DeleteAllNamespaces bool
CascadingStrategy metav1.DeletionPropagation
IgnoreNotFound bool
DeleteNow bool
ForceDeletion bool
WaitForDeletion bool
Quiet bool
WarnClusterScope bool
Raw string
GracePeriod int
Timeout time.Duration
DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resource.DryRunVerifier
Output string
DynamicClient dynamic.Interface
Mapper meta.RESTMapper
Result *resource.Result
genericclioptions.IOStreams
}
func (f *DeleteFlags) AddFlags(cmd *cobra.Command) {
f.FileNameFlags.AddFlags(cmd.Flags())
if f.LabelSelector != nil {
cmd.Flags().StringVarP(f.LabelSelector, "selector", "l", *f.LabelSelector, "Selector (label query) to filter on.")
}
if f.FieldSelector != nil {
cmd.Flags().StringVarP(f.FieldSelector, "field-selector", "", *f.FieldSelector, "Selector (field query) to filter on, supports '=', '==', and '!='.(e.g. --field-selector key1=value1,key2=value2). The server only supports a limited number of field queries per type.")
}
if f.All != nil {
cmd.Flags().BoolVar(f.All, "all", *f.All, "Delete all resources, in the namespace of the specified resource types.")
}
if f.AllNamespaces != nil {
cmd.Flags().BoolVarP(f.AllNamespaces, "all-namespaces", "A", *f.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.")
}
if f.Force != nil {
cmd.Flags().BoolVar(f.Force, "force", *f.Force, "If true, immediately remove resources from API and bypass graceful deletion. Note that immediate deletion of some resources may result in inconsistency or data loss and requires confirmation.")
}
if f.CascadingStrategy != nil {
cmd.Flags().StringVar(
f.CascadingStrategy,
"cascade",
*f.CascadingStrategy,
`Must be "background", "orphan", or "foreground". Selects the deletion cascading strategy for the dependents (e.g. Pods created by a ReplicationController). Defaults to background.`)
cmd.Flags().Lookup("cascade").NoOptDefVal = "background"
}
if f.Now != nil {
cmd.Flags().BoolVar(f.Now, "now", *f.Now, "If true, resources are signaled for immediate shutdown (same as --grace-period=1).")
}
if f.GracePeriod != nil {
cmd.Flags().IntVar(f.GracePeriod, "grace-period", *f.GracePeriod, "Period of time in seconds given to the resource to terminate gracefully. Ignored if negative. Set to 1 for immediate shutdown. Can only be set to 0 when --force is true (force deletion).")
}
if f.Timeout != nil {
cmd.Flags().DurationVar(f.Timeout, "timeout", *f.Timeout, "The length of time to wait before giving up on a delete, zero means determine a timeout from the size of the object")
}
if f.IgnoreNotFound != nil {
cmd.Flags().BoolVar(f.IgnoreNotFound, "ignore-not-found", *f.IgnoreNotFound, "Treat \"resource not found\" as a successful delete. Defaults to \"true\" when --all is specified.")
}
if f.Wait != nil {
cmd.Flags().BoolVar(f.Wait, "wait", *f.Wait, "If true, wait for resources to be gone before returning. This waits for finalizers.")
}
if f.Output != nil {
cmd.Flags().StringVarP(f.Output, "output", "o", *f.Output, "Output mode. Use \"-o name\" for shorter output (resource/name).")
}
if f.Raw != nil {
cmd.Flags().StringVar(f.Raw, "raw", *f.Raw, "Raw URI to DELETE to the server. Uses the transport specified by the kubeconfig file.")
}
}
// DeleteFlags composes common printer flag structs
// used for commands requiring deletion logic.
type DeleteFlags struct {
FileNameFlags *genericclioptions.FileNameFlags
LabelSelector *string
FieldSelector *string
All *bool
AllNamespaces *bool
CascadingStrategy *string
Force *bool
GracePeriod *int
IgnoreNotFound *bool
Now *bool
Timeout *time.Duration
Wait *bool
Output *string
Raw *string
}
分析发现:
所谓的--force强制删除, 是将 GracePeriod 设置为0.
那么在协议中, 将GracePeriodSeconds 设置为0, 将达到同样的效果.
client-go调用k8s接口删除测试:
https://github.com/kubernetes/client-go
编译脚本:
#!/bin/bash
rm ./app -rf
go env -w GOPROXY=https://goproxy.cn,direct
go mod download
go mod vendor
go build -o app .
测试删除pod的代码:
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Note: the example only works with the code within the same release/branch.
package main
import (
"context"
"flag"
"fmt"
"path/filepath"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
//
// Uncomment to load all auth plugins
// _ "k8s.io/client-go/plugin/pkg/client/auth"
//
// Or uncomment to load specific auth plugins
// _ "k8s.io/client-go/plugin/pkg/client/auth/azure"
// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
// _ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
// _ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
)
func main() {
var kubeconfig *string
if home := homedir.HomeDir(); home != "" {
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
} else {
kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
}
flag.Parse()
// use the current context in kubeconfig
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
if err != nil {
panic(err.Error())
}
// create the clientset
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err.Error())
}
pods, err := clientset.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{})
if err != nil {
panic(err.Error())
}
fmt.Printf("There are %d pods in the cluster\n", len(pods.Items))
// for _, pod := range pods.Items {
// // dbClusterStr, _ := json.Marshal(pod.Spec)
// // fmt.Println(pod.Spec.Hostname, pod.Spec.Subdomain)
// // fmt.Println("")
// }
// Examples for error handling:
// - Use helper functions like e.g. errors.IsNotFound()
// - And/or cast to StatusError and use its properties like e.g. ErrStatus.Message
namespace := "default"
{
pod := "mypod-0"
fPod, err := clientset.CoreV1().Pods(namespace).Get(context.TODO(), pod, metav1.GetOptions{})
if errors.IsNotFound(err) {
fmt.Printf("Pod %s in namespace %s not found\n", pod, namespace)
} else if statusError, isStatus := err.(*errors.StatusError); isStatus {
fmt.Printf("Error getting pod %s in namespace %s: %v\n",
pod, namespace, statusError.ErrStatus.Message)
} else if err != nil {
panic(err.Error())
} else {
fmt.Printf("Found pod %s in namespace %s\n", pod, namespace)
fmt.Println(fPod.Spec.NodeName)
// fmt.Println(fPod.Spec.ServiceAccountName)
fmt.Println(fPod.Spec.Hostname)
fmt.Println(fPod.Status.HostIP)
fmt.Println(fPod.Status.PodIP)
// fmt.Println(fPod.Status.NominatedNodeName)
// fmt.Println(fPod.Status.Message)
// fmt.Println(fPod.Status.Reason)
fmt.Println(fPod.Status.Phase)
}
}
{
pod := "mypod-1"
ops := metav1.DeleteOptions{}
var gs int64 = 0
ops.GracePeriodSeconds = &gs
// ops.PropagationPolicy = clientset.CoreV1().DeletePropagationForeground
err := clientset.CoreV1().Pods(namespace).Delete(context.TODO(), pod, ops)
if nil != err {
fmt.Println("Delete pod fail", err)
return
} else {
fmt.Println("Delete pod ok")
}
}
}
测试流程:
一. 先启动sts的两个pod, 使处于running状态
NAME STATUS ROLES AGE VERSION
node-201 Ready master 21h v1.18.20
node-202 Ready <none> 21h v1.18.20
node-203 Ready <none> 21h v1.18.20
mypod-0 1/1 Running 0 90s 10.168.2.34 node-203 <none> <none>
mypod-1 1/1 Running 0 89s 10.168.1.31 node-202 <none> <none>
二. 使其中一个pod所在的node处于not ready状态, 此时该pod处于Terming, 且无法自动重新调度到其他node上.
NAME STATUS ROLES AGE VERSION
node-201 Ready master 21h v1.18.20
node-202 NotReady <none> 21h v1.18.20
node-203 Ready <none> 21h v1.18.20
mypod-0 1/1 Running 0 7m3s 10.168.2.34 node-203 <none> <none>
mypod-1 1/1 Terminating 0 7m2s 10.168.1.31 node-202 <none> <none>
三. 执行client-go代码设置GracePeriodSeconds为0, 执行删除pod. 则pod被重新调度, 恢复running
[root@node-201 out-of-cluster-client-configuration]# ./app
There are 15 pods in the cluster
Found pod mypod-0 in namespace default
node-203
mypod-0
192.168.58.203
10.168.2.34
Running
Delete pod ok
NAME STATUS ROLES AGE VERSION
node-201 Ready master 21h v1.18.20
node-202 NotReady <none> 21h v1.18.20
node-203 Ready <none> 21h v1.18.20
mypod-0 1/1 Running 0 8m7s 10.168.2.34 node-203 <none> <none>
mypod-1 1/1 Running 0 3s 10.168.2.36 node-203 <none> <none>
更多推荐
已为社区贡献9条内容
所有评论(0)