发布一个k8s部署视频:https://edu.csdn.net/course/detail/26967
课程内容:各种k8s部署方式。包括minikube部署,kubeadm部署,kubeasz部署,rancher部署,k3s部署。包括开发测试环境部署k8s,和生产环境部署k8s。
腾讯课堂连接地址https://ke.qq.com/course/478827?taid=4373109931462251&tuin=ba64518
第二个视频发布 https://edu.csdn.net/course/detail/27109
腾讯课堂连接地址https://ke.qq.com/course/484107?tuin=ba64518
介绍主要的k8s资源的使用配置和命令。包括configmap,pod,service,replicaset,namespace,deployment,daemonset,ingress,pv,pvc,sc,role,rolebinding,clusterrole,clusterrolebinding,secret,serviceaccount,statefulset,job,cronjob,podDisruptionbudget,podSecurityPolicy,networkPolicy,resourceQuota,limitrange,endpoint,event,conponentstatus,node,apiservice,controllerRevision等。
第三个视频发布:https://edu.csdn.net/course/detail/27574
详细介绍helm命令,学习helm chart语法,编写helm chart。深入分析各项目源码,学习编写helm插件
第四个课程发布:https://edu.csdn.net/course/detail/28488
本课程将详细介绍k8s所有命令,以及命令的go源码分析,学习知其然,知其所以然
加qq群,请联系:
————————————————
//创建edit命令
func NewCmdEdit(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := editor.NewEditOptions(editor.NormalEditMode, ioStreams)//初始化结构体
o.ValidateOptions = cmdutil.ValidateOptions{EnableValidation: true}
cmd := &cobra.Command{//创建cobra命令
Use: "edit (RESOURCE/NAME | -f FILENAME)",
DisableFlagsInUseLine: true,
Short: i18n.T("Edit a resource on the server"),
Long: editLong,
Example: fmt.Sprintf(editExample),
Run: func(cmd *cobra.Command, args []string) {
if err := o.Complete(f, args, cmd); err != nil {//准备运行
cmdutil.CheckErr(err)
}
if err := o.Run(); err != nil {//运行
cmdutil.CheckErr(err)
}
},
}
// bind flag structs
o.RecordFlags.AddFlags(cmd)//record选项
o.PrintFlags.AddFlags(cmd)//print选项
usage := "to use to edit the resource"
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)//文件选项
cmdutil.AddValidateOptionFlags(cmd, &o.ValidateOptions)//validate选项
cmd.Flags().BoolVarP(&o.OutputPatch, "output-patch", "", o.OutputPatch, "Output the patch if the resource is edited.")//output-patch选项
cmd.Flags().BoolVar(&o.WindowsLineEndings, "windows-line-endings", o.WindowsLineEndings,
"Defaults to the line ending native to your platform.")//windows-line-endings选项
cmdutil.AddApplyAnnotationVarFlags(cmd, &o.ApplyAnnotation)//save-config选项
cmdutil.AddIncludeUninitializedFlag(cmd)
return cmd
}
type EditOptions struct {//edit结构体
resource.FilenameOptions
RecordFlags *genericclioptions.RecordFlags
PrintFlags *genericclioptions.PrintFlags
ToPrinter func(string) (printers.ResourcePrinter, error)
OutputPatch bool
WindowsLineEndings bool
cmdutil.ValidateOptions
OriginalResult *resource.Result
EditMode EditMode
CmdNamespace string
ApplyAnnotation bool
ChangeCause string
genericclioptions.IOStreams
Recorder genericclioptions.Recorder
f cmdutil.Factory
editPrinterOptions *editPrinterOptions
updatedResultGetter func(data []byte) *resource.Result
}
func NewEditOptions(editMode EditMode, ioStreams genericclioptions.IOStreams) *EditOptions {
return &EditOptions{//初始化edit结构体
RecordFlags: genericclioptions.NewRecordFlags(),
EditMode: editMode,
PrintFlags: genericclioptions.NewPrintFlags("edited").WithTypeSetter(scheme.Scheme),
editPrinterOptions: &editPrinterOptions{
// create new editor-specific PrintFlags, with all
// output flags disabled, except json / yaml
printFlags: (&genericclioptions.PrintFlags{
JSONYamlPrintFlags: genericclioptions.NewJSONYamlPrintFlags(),
}).WithDefaultOutput("yaml"),
ext: ".yaml",
addHeader: true,
},
WindowsLineEndings: goruntime.GOOS == "windows",
Recorder: genericclioptions.NoopRecorder{},
IOStreams: ioStreams,
}
}
//准备
func (o *EditOptions) Complete(f cmdutil.Factory, args []string, cmd *cobra.Command) error {
var err error
o.RecordFlags.Complete(cmd)//recordflag complete
o.Recorder, err = o.RecordFlags.ToRecorder()//record flag转recorder
if err != nil {
return err
}
if o.EditMode != NormalEditMode && o.EditMode != EditBeforeCreateMode && o.EditMode != ApplyEditMode {//判断editmode是否有效
return fmt.Errorf("unsupported edit mode %q", o.EditMode)
}
o.editPrinterOptions.Complete(o.PrintFlags)//print complete
if o.OutputPatch && o.EditMode != NormalEditMode {//如果指定了output-patch则editmode必须是NormalEditMode
return fmt.Errorf("the edit mode doesn't support output the patch")
}
cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace()//获取namsapce和enforcenamespace
if err != nil {
return err
}
b := f.NewBuilder().
Unstructured()
if o.EditMode == NormalEditMode || o.EditMode == ApplyEditMode {
// when do normal edit or apply edit we need to always retrieve the latest resource from server
b = b.ResourceTypeOrNameArgs(true, args...).Latest()
}
r := b.NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions).
ContinueOnError().
Flatten().
Do()//创建原始对象Result
err = r.Err()
if err != nil {
return err
}
o.OriginalResult = r//设置原始result
o.updatedResultGetter = func(data []byte) *resource.Result {//创建更新后result
// resource builder to read objects from edited data
return f.NewBuilder().
Unstructured().
Stream(bytes.NewReader(data), "edited-file").
ContinueOnError().
Flatten().
Do()
}
o.ToPrinter = func(operation string) (printers.ResourcePrinter, error) {//转printer函数
o.PrintFlags.NamePrintFlags.Operation = operation
return o.PrintFlags.ToPrinter()
}
o.CmdNamespace = cmdNamespace
o.f = f
return nil
}
//运行
func (o *EditOptions) Run() error {
edit := NewDefaultEditor(editorEnvs())//获取edit命令
// editFn is invoked for each edit session (once with a list for normal edit, once for each individual resource in a edit-on-create invocation)
editFn := func(infos []*resource.Info) error {//edit函数
var (
results = editResults{}
original = []byte{}
edited = []byte{}
file string
err error
)
containsError := false
// loop until we succeed or cancel editing
for {
// get the object we're going to serialize as input to the editor
var originalObj runtime.Object//原始obj
switch len(infos) {
case 1:
originalObj = infos[0].Object//设置原始obj
default:
l := &unstructured.UnstructuredList{//如果有多个obj则设置List对象
Object: map[string]interface{}{
"kind": "List",
"apiVersion": "v1",
"metadata": map[string]interface{}{},
},
}
for _, info := range infos {
l.Items = append(l.Items, *info.Object.(*unstructured.Unstructured))
}
originalObj = l
}
// generate the file to edit
buf := &bytes.Buffer{}//buffer对象
var w io.Writer = buf//设置w为buffer
if o.WindowsLineEndings {//如果是windows则用windows的换行
w = crlf.NewCRLFWriter(w)
}
if o.editPrinterOptions.addHeader {//给edit buffer添加头
results.header.writeTo(w, o.EditMode)
}
if !containsError {
if err := o.editPrinterOptions.PrintObj(originalObj, w); err != nil {//打印原始对象到edit buffer
return preservedFile(err, results.file, o.ErrOut)
}
original = buf.Bytes()//获取原始bytes
} else {
// In case of an error, preserve the edited file.
// Remove the comments (header) from it since we already
// have included the latest header in the buffer above.
buf.Write(cmdutil.ManualStrip(edited))
}
// launch the editor
editedDiff := edited
edited, file, err = edit.LaunchTempFile(fmt.Sprintf("%s-edit-", filepath.Base(os.Args[0])), o.editPrinterOptions.ext, buf)//开启编辑工具
if err != nil {
return preservedFile(err, results.file, o.ErrOut)
}
// If we're retrying the loop because of an error, and no change was made in the file, short-circuit
if containsError && bytes.Equal(cmdutil.StripComments(editedDiff), cmdutil.StripComments(edited)) {包含错误并且editeddiff和edited相等,则返回错误
return preservedFile(fmt.Errorf("%s", "Edit cancelled, no valid changes were saved."), file, o.ErrOut)
}
// cleanup any file from the previous pass
if len(results.file) > 0 {
os.Remove(results.file)
}
klog.V(4).Infof("User edited:\n%s", string(edited))
// Apply validation
schema, err := o.f.Validator(o.EnableValidation)//生成校验schema
if err != nil {
return preservedFile(err, file, o.ErrOut)
}
err = schema.ValidateBytes(cmdutil.StripComments(edited))//校验编辑后结果
if err != nil {//如果校验错误则打印警告,并继续
results = editResults{
file: file,
}
containsError = true
fmt.Fprintln(o.ErrOut, results.addError(apierrors.NewInvalid(corev1.SchemeGroupVersion.WithKind("").GroupKind(),
"", field.ErrorList{field.Invalid(nil, "The edited file failed validation", fmt.Sprintf("%v", err))}), infos[0]))
continue
}
// Compare content without comments
if bytes.Equal(cmdutil.StripComments(original), cmdutil.StripComments(edited)) {//如果原始和编辑后相同则打印nochange,并返回
os.Remove(file)
fmt.Fprintln(o.ErrOut, "Edit cancelled, no changes made.")
return nil
}
lines, err := hasLines(bytes.NewBuffer(edited))//判断编辑后是否有行
if err != nil {
return preservedFile(err, file, o.ErrOut)
}
if !lines {//如果没有行,则打印文件空,并返回
os.Remove(file)
fmt.Fprintln(o.ErrOut, "Edit cancelled, saved file was empty.")
return nil
}
results = editResults{
file: file,
}
// parse the edited file
updatedInfos, err := o.updatedResultGetter(edited).Infos()//获取编辑后的info
if err != nil {
// syntax error
containsError = true
results.header.reasons = append(results.header.reasons, editReason{head: fmt.Sprintf("The edited file had a syntax error: %v", err)})
continue
}
// not a syntax error as it turns out...
containsError = false
updatedVisitor := resource.InfoListVisitor(updatedInfos)//设置visitor
// need to make sure the original namespace wasn't changed while editing
if err := updatedVisitor.Visit(resource.RequireNamespace(o.CmdNamespace)); err != nil {//包装visitor
return preservedFile(err, file, o.ErrOut)
}
// iterate through all items to apply annotations
if err := o.visitAnnotation(updatedVisitor); err != nil {//判断是否创建annotation
return preservedFile(err, file, o.ErrOut)
}
switch o.EditMode {//判断编辑模式
case NormalEditMode://normalEditMode
err = o.visitToPatch(infos, updatedVisitor, &results)
case ApplyEditMode://applyEditMode
err = o.visitToApplyEditPatch(infos, updatedVisitor)
case EditBeforeCreateMode://editBeforeCreateMode
err = o.visitToCreate(updatedVisitor)
default:
err = fmt.Errorf("unsupported edit mode %q", o.EditMode)
}
if err != nil {
return preservedFile(err, results.file, o.ErrOut)
}
// Handle all possible errors
//
// 1. retryable: propose kubectl replace -f
// 2. notfound: indicate the location of the saved configuration of the deleted resource
// 3. invalid: retry those on the spot by looping ie. reloading the editor
if results.retryable > 0 {//如果retryable大于0,则输出警告,返回
fmt.Fprintf(o.ErrOut, "You can run `%s replace -f %s` to try this update again.\n", filepath.Base(os.Args[0]), file)
return cmdutil.ErrExit
}
if results.notfound > 0 {//如果notFound大于0,则输出警告,返回
fmt.Fprintf(o.ErrOut, "The edits you made on deleted resources have been saved to %q\n", file)
return cmdutil.ErrExit
}
if len(results.edit) == 0 {
if results.notfound == 0 {
os.Remove(file)
} else {
fmt.Fprintf(o.Out, "The edits you made on deleted resources have been saved to %q\n", file)
}
return nil
}
if len(results.header.reasons) > 0 {
containsError = true
}
}
}
switch o.EditMode {//判断editMode
// If doing normal edit we cannot use Visit because we need to edit a list for convenience. Ref: #20519
case NormalEditMode://normalEditMode
infos, err := o.OriginalResult.Infos()//获取原始info
if err != nil {
return err
}
if len(infos) == 0 {
return errors.New("edit cancelled, no objects found")
}
return editFn(infos)//运行edit函数
case ApplyEditMode://apply Edit mode
infos, err := o.OriginalResult.Infos()
if err != nil {
return err
}
var annotationInfos []*resource.Info
for i := range infos {
data, err := util.GetOriginalConfiguration(infos[i].Object)
if err != nil {
return err
}
if data == nil {
continue
}
tempInfos, err := o.updatedResultGetter(data).Infos()
if err != nil {
return err
}
annotationInfos = append(annotationInfos, tempInfos[0])
}
if len(annotationInfos) == 0 {
return errors.New("no last-applied-configuration annotation found on resources, to create the annotation, use command `kubectl apply set-last-applied --create-annotation`")
}
return editFn(annotationInfos)
// If doing an edit before created, we don't want a list and instead want the normal behavior as kubectl create.
case EditBeforeCreateMode:// editBeforeCreateMode
return o.OriginalResult.Visit(func(info *resource.Info, err error) error {
return editFn([]*resource.Info{info})
})
default:
return fmt.Errorf("unsupported edit mode %q", o.EditMode)
}
}
//normalEditMode运行函数
func (o *EditOptions) visitToPatch(originalInfos []*resource.Info, patchVisitor resource.Visitor, results *editResults) error {
err := patchVisitor.Visit(func(info *resource.Info, incomingErr error) error {//访问Info
editObjUID, err := meta.NewAccessor().UID(info.Object)//获取编辑后info uid
if err != nil {
return err
}
var originalInfo *resource.Info
for _, i := range originalInfos {//遍历原始info
originalObjUID, err := meta.NewAccessor().UID(i.Object)//获取原始info uid
if err != nil {
return err
}
if editObjUID == originalObjUID {//如果编辑后info uid和原始info uid相等
originalInfo = i//设置原始info
break
}
}
if originalInfo == nil {
return fmt.Errorf("no original object found for %#v", info.Object)
}
originalJS, err := encodeToJSON(originalInfo.Object.(runtime.Unstructured))//把原始info转json
if err != nil {
return err
}
editedJS, err := encodeToJSON(info.Object.(runtime.Unstructured))//编辑后info转json
if err != nil {
return err
}
if reflect.DeepEqual(originalJS, editedJS) {//如果编辑后json和编辑前json相等
// no edit, so just skip it.
printer, err := o.ToPrinter("skipped")
if err != nil {
return err
}
return printer.PrintObj(info.Object, o.Out)//打印对象返回
}
preconditions := []mergepatch.PreconditionFunc{
mergepatch.RequireKeyUnchanged("apiVersion"),
mergepatch.RequireKeyUnchanged("kind"),
mergepatch.RequireMetadataKeyUnchanged("name"),
}
// Create the versioned struct from the type defined in the mapping
// (which is the API version we'll be submitting the patch to)
versionedObject, err := scheme.Scheme.New(info.Mapping.GroupVersionKind)
var patchType types.PatchType
var patch []byte
switch {
case runtime.IsNotRegisteredError(err)://如果是IsNotRegisteredError
// fall back to generic JSON merge patch
patchType = types.MergePatchType
patch, err = jsonpatch.CreateMergePatch(originalJS, editedJS)
if err != nil {
klog.V(4).Infof("Unable to calculate diff, no merge is possible: %v", err)
return err
}
for _, precondition := range preconditions {
if !precondition(patch) {
klog.V(4).Infof("Unable to calculate diff, no merge is possible: %v", err)
return fmt.Errorf("%s", "At least one of apiVersion, kind and name was changed")
}
}
case err != nil:
return err
default:
patchType = types.StrategicMergePatchType
patch, err = strategicpatch.CreateTwoWayMergePatch(originalJS, editedJS, versionedObject, preconditions...)//比较原始json和编辑后json创建patch
if err != nil {
klog.V(4).Infof("Unable to calculate diff, no merge is possible: %v", err)
if mergepatch.IsPreconditionFailed(err) {
return fmt.Errorf("%s", "At least one of apiVersion, kind and name was changed")
}
return err
}
}
if o.OutputPatch {//如果指定output-patch则打印patch
fmt.Fprintf(o.Out, "Patch: %s\n", string(patch))
}
patched, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, patchType, patch, nil)//应用patch到服务端
if err != nil {
fmt.Fprintln(o.ErrOut, results.addError(err, info))
return nil
}
info.Refresh(patched, true)//刷新info对象
printer, err := o.ToPrinter("edited")
if err != nil {
return err
}
return printer.PrintObj(info.Object, o.Out)//打印结果
})
return err
}