hi,一起來嘗試搭建一個可運(yùn)行代碼node代碼片段serverless平臺

前言

Serverless如此火熱似乎沒啥好介紹的,我就不當(dāng)搬運(yùn)工了...

本文主要介紹筆者如何利用vscode插件+k8s+node來搭建一個可運(yùn)行代碼片段的serveless平臺

主要有以下兩個部分。

  • vscode 插件
  • k8S集群

先看一下最終效果


image

架構(gòu)圖:

image

實(shí)踐過程

vscode插件

vscode 插件是平臺的客戶端,用戶上傳代碼片段,觸發(fā)執(zhí)行,接受返回結(jié)果等作用。也是實(shí)踐過程中較為容易的部分。

只需要注冊右擊按鈕和對于文本的處理即可(相關(guān)代碼省略)


let disposable = vscode.commands.registerTextEditorCommand('disheng-serverless-exec', (textEditor,edit) => {
        const doc=textEditor.document;
        const selection=textEditor.selection;
        // 獲取選取代碼
        const selectedText=doc.getText(selection);
        // ..... post code
        axios({
            //.........
        }).then((response)=>{
            //處理返回并開始取回執(zhí)行結(jié)果
            let getResultTask=setInterval(()=>{
            //.....
                textEditor.edit((editBuilder)=>{
                    editBuilder.insert(selection.end,`\n//執(zhí)行結(jié)果:${resultValue}\n`)
                });
                clearInterval(getResultTask);
            //....
            },1000);
            }
        }); 
        })

環(huán)境準(zhǔn)備(k8s集群)

一臺2C2G 及其以上云服或者物理機(jī)器作為k8s master。(不要問為什么只用一臺,因?yàn)楦F)。

筆者采用是華為云新用戶免費(fèi)15天的2c4g的云服。

k8s的集群的安裝筆者主要采用kubeadm 來安裝.

國內(nèi)安裝主要是官方容器被墻的問題。

kubeadm config print init-defaults > kubeadm-init.yaml

修改repo 為registry.cn-hangzhou.aliyuncs.com/google_containers
再修改相關(guān)配置適合成自己的網(wǎng)絡(luò)環(huán)境
執(zhí)行

kubeadmin init --config kubeadmin-init.yaml

另由于筆者是采用單機(jī)來做k8s 集群,所以 很多非kube-system 的pod也會運(yùn)行在master 節(jié)點(diǎn)上,所以我們需要更改master節(jié)點(diǎn)上的trait 的限制.

kubectl taint node {你的master節(jié)點(diǎn)的名稱} node-role.kubernetes.io/master-

主要組件開發(fā)和部署

serverless-api

此組件主要是提供針對代碼片段和執(zhí)行結(jié)果的相關(guān)操作,筆者采用go&gin&redis 實(shí)現(xiàn)一個簡單的resultful服務(wù)。

主要提供一下幾個接口

  • insertCode // 提交新的運(yùn)行片段
  • insertRunResult // 插入運(yùn)行結(jié)果
  • getNextRunCodeId // 獲取下一個可以執(zhí)行的代碼片段的code的id
  • getCode // 根據(jù)id獲取code
  • getResult // 根據(jù)id獲取result

具體代碼就不貼了,主要是對于redis的一些curd操作。
由于此pod即需要對外提供訪問服務(wù),有需要對內(nèi)提供訪問。所以筆者采用pod + service+
ingress 的方式來組織serverless-api。
以下筆者創(chuàng)建幾類服務(wù)

serverless-api-deploymenet

apiVersion: apps/v1
kind: Deployment
metadata:
  name: serverless-api-deployment
spec:
  selector:
    matchLabels:
      app: serverless-api
  template:
    metadata:
      labels:
        app: serverless-api
    spec:
      containers:
        - name: serverless-api
          image: ********
          ports:
            - containerPort: 8080
        - name: redis
          image: redis:latest
          ports:
            - containerPort: 6379
apiVersion: apps/v1
kind: Service
metadata:
  name: serverless-api-service
spec:
  selector:
    app: serverless-api
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: serverless-ingress
spec:
  rules:
    - host:*****.***.****
      http:
        paths:
          - path: /
            backend:
              serviceName: serverless-api-service
              servicePort: 8080

關(guān)于ingress的暴露 筆者主要采用的ingress controller + hostnework的方式來暴露。

// ........
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-ingress-controller
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: ingress-nginx
      app.kubernetes.io/part-of: ingress-nginx
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
      annotations:
        prometheus.io/port: "10254"
        prometheus.io/scrape: "true"
    spec:
      # wait up to five minutes for the drain of connections
      terminationGracePeriodSeconds: 300
      serviceAccountName: nginx-ingress-serviceaccount
      nodeSelector:
        kubernetes.io/os: linux
      hostNetwork: true // 加入hostnewwork 。部署pod的node上會進(jìn)行對應(yīng)的端口綁定
      containers:
        - name: nginx-ingress-controller
          image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.26.2
// .....

serverless-cronjob

此組件是集群內(nèi)用于創(chuàng)建和調(diào)度作業(yè)容器,筆者這里設(shè)計了一個定時的任務(wù)。定時檢查作業(yè)的容器,并且刪除已經(jīng)完成作業(yè)的任務(wù)的job容器,如果沒有作業(yè)容器還在運(yùn)行則獲取下一段執(zhí)行代碼的id,創(chuàng)建新的作業(yè)容器
此組件 主要依賴 k8s.io/go-client

func main() {
    config, err := rest.InClusterConfig()
    if err != nil {
        panic(err.Error())
    }
    clientset, err := kubernetes.NewForConfig(config)
    if err != nil {
        panic(err.Error())
    }
    for {
        pods, err := clientset.CoreV1().Pods("serverless-job").List(metav1.ListOptions{})
        
        for pods,_ := range pods.Items{
          // ... 刪除已完成的pods,并決定是否獲取并創(chuàng)建下一個job
            
        }
        response, err := http.Get("http://******/getNextRunCodeId")
        // .......
    
        jobClient := clientset.BatchV1().Jobs("serverless-job")
        // 將獲取的codeid利用env傳遞給作業(yè)容器內(nèi) 
        job := batchv1.Job{
            ObjectMeta: metav1.ObjectMeta{
                Name:      "serverless-job-" + RandStr(12),
                Namespace: "serverless-job",
            },
            Spec: batchv1.JobSpec{
                Template: apiv1.PodTemplateSpec{
                    Spec: apiv1.PodSpec{
                        Containers: []apiv1.Container{
                            {
                                Name:  "serverless-job-container",
                                Image:"*********",
                                Env: []apiv1.EnvVar{
                                    {
                                        Name:  "CODEID",
                                        Value: string(body),
                                    },
                                },
                            },
                        },
                        RestartPolicy: apiv1.RestartPolicyNever,
                    },
                },
            },
        }
        // 創(chuàng)建job
        createResult, err := jobClient.Create(&job)
        // **** 
        
        time.Sleep(sleepTime * time.Second)
    }
}

由于cronjob組件需要調(diào)用kubectl 且在集群內(nèi)中運(yùn)行,所以我們不能僅僅使用default token,我們需要使用高級一點(diǎn)的角色權(quán)限,并綁定到pods上。
以下是筆者的相關(guān)實(shí)踐

創(chuàng)建新的命名空間為作業(yè)容器的建立做好準(zhǔn)備

kubectl create namespaces serverless-job
apiVersion: apps/v1
kind: Deployment
metadata:
  name: serverless-cronjob-deployment
spec:
  selector:
    matchLabels:
      app: serverless-cronjob
  template:
    metadata:
      labels:
        app: serverless-cronjob
    spec:
      serviceAccountName: serverless-cronjob // 關(guān)聯(lián)上我們創(chuàng)建的賬戶
      containers:
        - name: serverless-cronjob
          image: ××××××
apiVersion: v1
kind: ServiceAccount
metadata:
  name: serverless-cronjob
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: kubernetes-dashboard
  labels:
    k8s-app: kubernetes-dashboard
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
  - kind: ServiceAccount
    name: serverless-cronjob
    namespace: default

于此同時,我們進(jìn)入到pods內(nèi)就可以看到秘鑰成功的被掛在默認(rèn)的目錄下。

image

serverless-node-job

此組件主要是我們的作業(yè)容器。筆者這里僅僅實(shí)踐了js的代碼片段執(zhí)行實(shí)踐。
此組件主要作用就是獲取代碼 并利用node v8模塊對代碼運(yùn)行并將結(jié)果插入到serverless-api中。

const { CODEID }=process.env
if(CODEID){
//.....
    axios({
        .../
    }).then(response=>{
    try{
        const runResult=runCode(response)
    }catch(err){
        insertResult(err)
    }
     insertResult(runResult)
    // .....
    })
}

至此,筆者關(guān)于serverless平臺搭建的實(shí)踐到此結(jié)束。

總結(jié)

筆者在這里僅僅簡單實(shí)踐了nodejs代碼片段的運(yùn)行,對于serverless更廣泛的應(yīng)用其實(shí)還并未進(jìn)行嘗試。例如函數(shù)的注冊和各類方式的觸發(fā)器,資源調(diào)度/伸縮,日志/監(jiān)控,長伺服應(yīng)用等等. 關(guān)于serverless市面上主要是提供serverless函數(shù)計算和云應(yīng)用兩項服務(wù)。其實(shí)也就是長短伺服業(yè)務(wù)。

但僅僅在筆者簡單的實(shí)踐過程中,也出現(xiàn)了一些頗為頭疼的問題,一是函數(shù)執(zhí)行的實(shí)時性的問題,可以從實(shí)踐成果的gif中看到從提交片段到返回結(jié)果的過程中也有肉眼可查明顯的延時,容器創(chuàng)建和node啟動過程的耗時都是比較大的。因此還是需要進(jìn)行一定的優(yōu)化。比如創(chuàng)建一個同執(zhí)行環(huán)境的容器池,對外開放觸發(fā)接口,把容器創(chuàng)建和node啟動過程省略?

更多問題不在這里贅述,有興趣的朋友可以隨時交流。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容