乐趣区

关于运维:运维工作新时代自主编码实现运维自动化的转型之旅

引言

随着业务零碎和底层中间件服务的复杂度一直减少,传统手工运维形式面临着诸多挑战和限度。人工编写运维脚本显得十分低效,同时手动执行运维操作存在着微小危险。在此状况下,推动运维自动化成为运维人员必须落地施行的工作。运维同学如果能够有中央自主通过编码的形式,实现各种自动化工作和运维性能。不仅能够提高效率,升高危险,还能为运维工作带来新的冲破。

然而,要迈向这条运维自动化之路并不容易。咱们须要克服传统运维的局限性,同时要把握编码技能和提供适应的平台。

本文将介绍如何掂量运维自动化率的概念,并提供一个反对运维同学通过编码实现自动化的平台。通过编码实现运维自动化的转型之旅,让运维工作迈入新的时代。心愿也能给大家提供一个全新的视角。

运维工作面临的挑战和限度

简单的脚本治理和手动操作

过于依赖手动操作和编写简单的手工脚本,容易引发故障,减少了运维工作量和危险:

脚本保护和版本控制:随着工夫的推移,保护的脚本可能会变得越来越简单、并且难以保护。同时,在团队多人合作的状况下,对脚本进行版本控制和治理也存在挑战,特地容易产生用错脚本的状况。

•手工操作谬误:手动操作容易引入人为的操作谬误,尤其是在解决线上工作时。一个小谬误可能导致系统故障或数据失落,从而减少了零碎的不稳定性和危险。

人为失误和依赖集体技能

运维过程中的人为失误会导致系统故障和数据失落,适度依赖集体技能的状况也会使得团队单干和常识传承艰难:

•依赖个别人员:如果某个运维同学负责的工作过于依赖于集体技能和教训,那么当该同学到职或休假时,可能会影响运维工作的失常发展。

不足流程规范:人为操作不足标准化流程和标准,在解决工作时没有固化的流程提供领导和参考,容易造成人为失误危险。

个人成长和倒退的局限

日常工作中,大家更容易关注到业务研发,而对于业务运维的工作容易漠视。这种状况给运维同学的个人成长和倒退带来了一些局限性:

紧急情况和工作压力:运维同学通常须要在 7*24 小时待命用来解决问题和故障,以确保零碎的稳定性和可用性。导致集体常常处于低压工作状态,集体的倒退和学习可能受到限制。

倒退和回升空间:随着云计算倒退,局部运维工作正逐渐被云厂商和 DevOps 自动化代替,特地是混合云时代,传统运维必须要转变思维深刻到业务或者产品底层,从而晋升集体竞争力。

运维自动化的重要性

老本

运维人员管着公司的服务器资源,每年公司须要为 IT 资源领取数十亿的老本,随着资源规模的一直增长,老本管制和策略变的至关重要。在这种状况下,欠缺的资源老本管理工具和自动化摊派机制变得尤为重要,否则老本治理将面临微小的累赘和压力。

效率

在运维工作当中,例如资源分配和治理、扩容缩容、日常巡检、版本更新、服务重启、集群治理等,这些都是运维最根底的日常工作,目前这些工作上大多都是偏日常和反复的,手工操作将节约掉大部分的工夫,如果通过自动化解决掉这些问题,将解放运维的生产力,晋升运维效率,让运维的同学能够有更多的精力去做更有价值的事件。

稳定性

通过自动化晋升运维效率的同时,也能够大幅升高人为失误,最大水平保障系统的稳定性运行,即便呈现问题,也可能通过自动化疾速发现响应和主动复原。

编码实现的运维自动化

下面提到了运维自动化的重要性。自从往年 4 月份退出技术保障部门以来,我始终在思考如何晋升运维的自动化程度,并心愿能找到一种掂量该晋升的办法。因而,在 4 月份就提出了一个运维自动化率这样的一个掂量指标。

运维自动化率的定义

运维自动化率的定义范畴是技术保障部门的所有运维人员。该指标能够通过以下公式计算:

运维自动化率 = 自动化操作次数(通过泰山麒麟)/ 手工操作次数(通过堡垒机登录)+ 自动化操作次数

其中,分子示意通过泰山麒麟进行的自动化操作次数。这些操作能够是自动化运维命令、运维性能或自动化编排工作。分母示意通过堡垒机登录之后进行的手工操作的次数,再加上分子的数量。

通过这个指标,能够掂量在给定工夫内运维人员应用自动化工具绝对于手工操作的比例,从而评估运维的自动化程度。较高的自动化率意味着更多的工作能够通过自动化实现,缩小了手工操作的工作量,进步了效率和稳定性,从 4 月份掂量开始,技术保障部的运维同学运维自动化率从 Q2 的 3% 晋升到 目前为 63%。

为什么要运维自主编码实现

最后的起因是发现各个运维小团队都应用本人独立的运维工具。通过剖析,这是因为不同的运维团队有不同的需要,为了满足各自的需要,每个团队都会开发本人的运维工具。随着工夫的推移,就呈现了许多不同的运维工具平台。因而开始思考是否能够提供一个平台来满足所有运维人员的需要。

然而,问题又来了,这些需要应该由谁来开发呢?最正当的解决方案是由运维人员本人来开发。因为只有运维人员最理解本人的需要。

升高沟通老本:运维同学最理解本人的需要,运维团队能够依据业务需要和环境特点开发定制化的运维工具和脚本,确保性能与业务需要完满符合。这样能够升高与平台方的沟通老本,缩小需要解释和了解的工夫和精力。

疾速响应需要:运维团队可能疾速开发或批改运维性能,及时响应业务变动和运维需要。不用期待平台方的排期反对或更新,能够迅速满足需要变动,进步运维的灵活性和响应速度。

节约保护老本:绝对于各个团队自建运维工具,通过自行编码能够节俭许多公共局部的保护老本。运维同学只须要关注本人的业务逻辑,而不必放心整个运维工具的保护。这样能够升高保护老本,并进步工作效率。

助力业余成长:通过编码实现运维性能,能够促成运维人员的技术成长。他们能够晋升本人的编程能力、零碎理解能力和问题解决能力。

通过让所有运维同学都参加其中,能够为运维同学提供更广大的学习和成长机会,能够施展出更大的价值。这样做不仅能够晋升运维团队的整体能力,还能助力个体运维人员的个人成长和职业倒退。

案例剖析:ChubaoFS 的运维自动化

接入步骤和示例

1、申请运维零碎菜单

分割泰山麒麟平台管理员创立运维零碎菜单。在这个过程中,平台管理员将创立对应运维零碎的菜单名称,并依据菜单调配用户公有的鉴权文件。这个鉴权文件将在后续的 Controller 开发中被应用。

apiVersion: v1
clusters:
- cluster:
    certificate-authority: ca.pem
    server: https://xxx.jd.com:80
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kubecfg
  name: default 
current-context: default
kind: Config
preferences: {}
users:
- name: kubecfg
  user:
    client-certificate-data: xxxxx(领有菜单对应的 namespace 所有权限)
    client-key-data: xxxxx(领有菜单对应的 namespace 所有权限)

2、创立运维性能

在泰山麒麟平台中创立运维性能时,反对两种实现形式。一种是基于运维同学提供的 HTTP 接口服务,另一种是基于运维本人编码实现的自定义(基于 Kubernetes CRD)模式的 Controller。本文重点将介绍基于自定义模式的 Controller 实现形式。

创立运维性能步骤:

① 进入开发者模式,点击 ” 新建 ” 按钮,在窗口中能够抉择原子类型为自定义,后续须要运维同学开发相应的 Controller 代码

②下面步骤 ” 确认 ” 后,会在列表页呈现对应名称的运维原子记录,点击操作栏里的“字段保护”按钮开始配置形容该运维性能的具体数据结构。

通过以上两步,失去了一个形容运维性能的参数

{
  "apiVersion": "test.sops.com/v1",
  "kind": "Binlog",
  "spec": {
    "sqlTypes": "delete",
    "dbs": "test",
    "tbs": "recycle_test",
    "ip": "127.0.0.1",
    "workType": "rollback",
    "startTime": "2023-12-25 08:00:00",
    "stopTime": "2023-12-25 11:00:00",
    "type": "recoverer"
  },
  "status": {"custom": {},
    "state": "succeed",
    "message": ""
  }
}

依据以上参数,接下来是编写执行该运维性能的代码逻辑了。

3、编写运维性能代码

泰山麒麟平台提供了代码模版,倡议下载提供的模版进行编写具体的 Controller 局部,并退出平台调配的鉴权文件,这样能够确保 Controller 运行时能够 Watch 到麒麟平台的操作音讯。

代码模版蕴含两个例子,一个是 Controller 实现繁多性能样例,另一个是 Controller 实现多功能样例,次要蕴含以下 2 个外围文件:

package main
import (
    "controllers/example/api/web/service"
    "flag"
    "os"
    examplev1 "controllers/example/api/v1"
    "controllers/example/controllers"
    "k8s.io/apimachinery/pkg/runtime"
    clientgoscheme "k8s.io/client-go/kubernetes/scheme"
    _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
    ctrl "sigs.k8s.io/controller-runtime"
    "sigs.k8s.io/controller-runtime/pkg/log/zap"
    // +kubebuilder:scaffold:imports
)

var (scheme   = runtime.NewScheme()
    setupLog = ctrl.Log.WithName("setup")
)

func init() {_ = clientgoscheme.AddToScheme(scheme)
    _ = examplev1.AddToScheme(scheme)
    // +kubebuilder:scaffold:scheme
}

func main() {
    var metricsAddr string
    var enableLeaderElection bool
    // 设置启动参数
    flag.StringVar(&metricsAddr, "metrics-addr", ":8090", "The address the metric endpoint binds to.")
    flag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
        "Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.")
    flag.Parse()

    // 配置日志打印参数
    ctrl.SetLogger(zap.New(func(o *zap.Options) {o.Development = true}))
    // 退出到 controller manager 治理
    mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
        Scheme:             scheme,
        MetricsBindAddress: metricsAddr,
        LeaderElection:     enableLeaderElection,
        Port:               9443,
    })
    if err != nil {setupLog.Error(err, "unable to start manager")
        os.Exit(1)
    }
    // 不须要 web 能力能够删除此行
    go service.RunServer(mgr)
        
    // 外围代码,注册 CRD,与麒麟平台自定的资源建设 watch 机制
    if err = (&controllers.ExampleKindReconciler{Client: mgr.GetClient(),
        Log:    ctrl.Log.WithName("controllers").WithName("ExampleKind"),
        Scheme: mgr.GetScheme(),}).SetupWithManager(mgr); err != nil {setupLog.Error(err, "unable to create controller", "controller", "ExampleKind")
        os.Exit(1)
    }
    // +kubebuilder:scaffold:builder
    setupLog.Info("starting manager")
    if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {setupLog.Error(err, "problem running manager")
        os.Exit(1)
    }
}
package controllers
import (
    "context"
    "strconv"
    "github.com/go-logr/logr"
    "k8s.io/apimachinery/pkg/runtime"
    ctrl "sigs.k8s.io/controller-runtime"
    "sigs.k8s.io/controller-runtime/pkg/client"
    examplev1 "controllers/example/api/v1"
)

// ExampleKindReconciler reconciles a ExampleKind object
type ExampleKindReconciler struct {
    client.Client
    Log    logr.Logger
    Scheme *runtime.Scheme
}

var num = 0
// +kubebuilder:rbac:groups=example.sreplat.com,resources=examplekinds,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=example.sreplat.com,resources=examplekinds/status,verbs=get;update;patch
// 当麒麟平台上执行一个运维过能时,controller 就会 watch 参数,并携带参数信息进入到这个函数。func (r *ExampleKindReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
    num += 1
    ctx := context.Background()
    _ = r.Log.WithValues("examplekind", req.NamespacedName)
    example := &examplev1.ExampleKind{}
        
    // your logic here
    // 上面都是样例代码,用户间接实现本人的业务逻辑即可
    if err := r.Get(ctx, req.NamespacedName, example); err != nil {r.Log.V(1).Info("couldn't find module:" + req.String())
    } else {r.Log.V(1).Info("接管 Moduler 资源的变更", "Resource.spec", example.Spec)
        r.Log.V(1).Info("接管 Moduler 资源的变更", "Status", example.Status)
    }
    if example.Status.Event == "created" {
        example.Status.Event = "created_done"
        example.Spec.Ba += strconv.Itoa(num)
        r.Log.V(1).Info("创立业务完结了,资源的状态更新为 done", "num:", num, "example.Status.Event", example.Status.Event)
        r.Update(ctx, example)
    }
    if example.Status.Event == "updated" {
        example.Status.Event = "updated_done"
        example.Spec.Ba += strconv.Itoa(num)
        r.Log.V(1).Info("更新业务完结了,资源的状态更新为 done", "num:", num, "example.Status.Event", example.Status.Event)
        r.Update(ctx, example)
    }
    if example.Status.Event == "list" {
        example.Status.Event = "list_done"
        example.Spec.Ba += strconv.Itoa(num)
        r.Log.V(1).Info("查问业务完结了,资源的状态更新为 done", "num:", num, "example.Status.Event", example.Status.Event)
        r.Update(ctx, example)
    }
    if example.Status.Event == "deleted" {
        example.Status.Event = "deleted_done"
        example.Spec.Ba += strconv.Itoa(num)
        r.Log.V(1).Info("删除业务完结了,资源的状态更新为 done", "num:", num, "example.Status.Event", example.Status.Event)
        r.Update(ctx, example)
    }
    return ctrl.Result{}, nil}

func (r *ExampleKindReconciler) SetupWithManager(mgr ctrl.Manager) error {return ctrl.NewControllerManagedBy(mgr).
        For(&examplev1.ExampleKind{}).
        Complete(r)
}

4、部署运维性能代码

当实现代码后,能够将其部署在行云部署的容器中。为了不便对立治理,倡议在“泰山麒麟(SOPS)”零碎下自行申请利用。如果 Controller 对于运行环境有特殊要求,也能够抉择自行部署。

5、运维性能公布

当实现运维性能开发和部署后,能够在泰山麒麟平台点击“公布”按钮,公布之后,能够通过“受权”性能,将这个性能提供给其余运维人员应用。

6、执行运维性能

反对两种执行模式,能够在运维性能上面间接针对运维性能进行执行,也能够通过运维编排性能,将运维性能编排成自动化的运维场景执行。

单个运维性能执行

运维编排性能执行

7、查看执行记录

在泰山麒麟平台中执行后,反对查看运维性能执行中的参数、过程、后果以及运行日志。

相干问题和计划

不晓得哪些性能能够接入:运维团队先整顿了所有线上批量操作的需要和动作,而后依照优先级逐个进行接入。

不足能源接入,导致接入迟缓:晚期平台仅提供了繁多的性能,例如执行一条指令或执行一个操作,不足针对运维场景的工作,这导致运维团队不足接入的能源。起初,平台引入了“运维编排”性能,通过编排的形式反对简单的运维场景,同时还能对高危危险操作进行人工审批,以确保安全。运维编排的引入满足了运维团队的需要,也进步了运维人员的接入能源。

成绩和播种

截至目前,ChubaoFS 已通过上述形式实现了 43 个运维性能原子,并设计了 18 个运维编排的自动化工作。每周均匀执行自动化工作的次数约为 500 次。

泰山 - 麒麟平台

平台简介

麒麟平台通过扩大 Kubernetes 的自定义资源定义(Custom Resource Definitions, CRD)性能,为运维工程师提供可编程的对立运维平台。用户专一于运维性能开发,平台解决通用属性和集成工作。实现基础设施即代码(IaC)理念,并反对申明式 API。

通过 CRD 扩大 Kubernetes API,引入自定义资源类型,满足特定运维需要。工程师定义资源类型,编码实现运维性能,平台解决创立、更新、删除等通用操作和资源集成。采纳申明式形式形容所需运维状态,无需关注底层实现细节。编写资源定义文件,提交给麒麟平台解决,主动实现操作,确保零碎达到冀望状态。

麒麟平台简化了运维工程师的开发工作,进步了可维护性和可扩展性,实现了基础设施即代码的理念,平台反对以下性能:

•运维性能:执行单个运维性能,配置审批,受权执行等属性

•命令执行:批量执行运维命令及执行过程管制,包含超时,并发,止损 kill 等通用属性

•定时工作:对运维性能或命令执行定时工作,如巡检,数据备份,日志清理等

•资源展现:展现资源信息,如 IT 资产、数据库、中间件,应用程序等

•资源操作:对展现进去的资源增加一个具体的运维性能,须要时能够疾速在平台上对该资源执行运维操作

•运维编排:通过图形界面提供流程编辑工具,能够将多个运维原子 (运维性能和命令) 和审批流串联起来,实现简单运维作业自动化缩小人工干预,并反对人工复核审批,大大晋升了运维操作的安全性。

平台现状

截至目前,技术保障部内的运维团队曾经将相干产品,包含 JMQ、JIMDB、LogBook、ChubaoFS、数据库、对象存储、图片服务和利用运维接入了麒麟平台。

作者:京东批发 井亮亮

起源:京东云开发者社区 转载请注明起源

退出移动版