K8S發佈解釋型語言應用的最佳實踐

說明

咱們知道,k8s在發佈編譯型語言的應用時,幾乎不用多考慮,就會選擇將編譯好jar/war包(java語言)或者二進制文件(golang/c++)直接打到鏡像當中,生成新的應用鏡像,而後將鏡像推到鏡像倉庫,再觸發k8s完成應用發佈或者版本更新。但是相反,解釋型語言(php/lua/python)咱們通常不會這麼作。爲何呢?php

由於咱們知道,打鏡像和推送鏡像的一個過程是相對比較耗時的。對於編譯型語言來說,編譯過程自己就比較耗時,在編譯完成以後,再進行鏡像打包和推送鏡像的過程所消耗的時間跟編譯過程自己相比,根本微不足道。對於開發人員來說,他們有一個這樣心理預期,多了這麼一點點的時間,對於他們來說,幾乎是無感知的。java

而對於解釋型語言的應用開發者來說則否則,咱們以php爲例來做說明。對於一個php開發者來說,有的時候,一次發佈,可能就只修改了一個文件,經過傳統的發佈方式,完成一次發佈可能只須要10秒鐘。而若是隻是修改了一個文件卻須要將整個代碼完整打包,生成一個新的鏡像,打鏡像推鏡像的時間可能就須要1分鐘,再加上鏡像替換,流動更新的時間,整個更新操做的時間就會拉的更長。雖然這在生產環境中的表現可能還並不明顯,但在須要持續集成,頻繁迭代的測試環境中,這種表如今發佈時間上的巨大心理落差幾乎沒法讓開發人員接受。事實上,這也是大多數解釋型語言的開發者抵制docker的一個緣由之一。node

那麼到底有沒有更好的解決辦法呢?python

單獨發佈代碼

事實上,有不少公司在實際使用中針對解釋型語言的應用都選擇了使用容器標準化運行時環境,代碼單獨發佈的方式。nginx

具體的作法是,使用鏡像打包標準的php運行環境,而後將代碼直接發佈到k8s的全部node節點上,而後在任意節點上啓動php標準鏡像,並掛載所在節點的指定代碼目錄便可。由於全部節點上都有代碼,因此php容器能夠在任意節點之間漂移。若是不想把代碼發佈到全部節點,只想發佈到某幾個節點,也很好解決,只須要給這幾個節點打上label,而後限定php容器只運行在帶有該label的節點上便可。c++

這麼作的優勢顯而易見:git

  1. 經過容器的方式標準化了php環境,並且簡化了php環境的部署操做。
  2. php代碼發佈仍然採用傳統的發佈方式獨立發佈,不受打鏡像推鏡像的時間影響,作到了快速迭代。

缺點一樣顯而易見:golang

  1. 首先鏡像和代碼拆開發布的方式自己就不是容器化標準所推薦的方法
  2. 咱們須要經過label的方式嚴格對node和pod的對應關係作限定,一旦出錯,就會出現pod正常運行,訪問報錯的現象,由於pod沒法正常掛載代碼。
  3. 由於一臺宿主機上只有一份代碼,若是該臺宿主機上同時運行了同一個應用的多個pod,那麼這些pod將會掛載同一份代碼,當多個進程同時讀寫同一份代碼時,須要代碼自行解決衝突,雖然這種狀況其實不多出現(絕大多數時候,應用程序的開發者都會避免在代碼目錄執行寫操做,而對於讀操做,多進程是能夠共存的)。固然另外一個可行的解決辦法是經過pod的反親和性調度避免將同一個應用的多個pod運行在同一個node節點上,毫無疑問,這也增長了配置的複雜性。

應用容器主動拉取代碼

事實上,這是合乎容器化標準的一種實現。docker

而要實現這種方式,須要解決兩個問題:json

  1. 應用容器第一次啓動時,如何獲取代碼
  2. 應用代碼更新時,容器如何更新代碼

第一個問題,咱們使用kubernetes的init container技術來解決(在某些應用場中,使用pod啓動後的鉤子事件也能夠解決,但在相似lua的應用場中,在應用容器啓動以前就須要代碼就位的狀況下,這種方式並不合適,因此init container纔是最好的選擇)。其原理是在應用容器啓動以前,先啓動一個初始化容器,而後在初始化容器中執行從git倉庫,或者從發佈服務器拉取代碼的操做,將代碼直接拉取到應用容器的存儲位置,而後啓動應用容器,這樣應用容器一啓動,就有了代碼。

第二個問題,能夠直接調用k8s api,獲取到當前應用的全部pod信息,而後觸發循環調用pod上的rsync,執行主動拉取操做。

事實上,在咱們當前的測試環境的lua代碼的發佈場景中,尤爲適用。由於咱們在測試環境中,持續集成的過程是自動完成的,每分鐘執行一次持續集成操做。會先從git倉庫將代碼拉取到發佈節點,發佈節點啓動rsync server端。而後各node節點上配置定時任務,每分鐘從發佈節點同步代碼。對於lua應用來說,每次代碼更新就須要執行一次Nginx重載。經過這種發佈方式,咱們能夠實現只有在有代碼更新時,才執行nginx重載,而不是每分鐘一次。

缺點:

  1. 增長了發佈服務器的壓力,全部pod再也不共享宿主機代碼,而是每一個pod一套獨立代碼,全部pod都會從發佈服務器拉取代碼,在pod數量較大時,對發佈服務器的io是個考驗。另外若是某節點宕機時,全部pod漂移到新節點上從新啓動,會同一時間向發佈服務器拉取代碼,對發佈服務器的io壓力也會較大

  2. 其配置相對於獨立發佈代碼的方式實際上是一樣複雜的,可是其不用限定pod與node的對應關係,增長了調度的靈活性。

下面是一個應用容器主動拉取代碼的k8s deployment配置文件示例:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: dyland-lua-api-pre
  namespace: php
spec:
  replicas: 1
  selector: 
    matchLabels:
      name: dyland-lua-api-pre
      envrion: php
  template:
    metadata:
      annotations: 
        prometheus.io/scrape: "true"
        prometheus.io/port: "9913"
        prometheus.io/scheme: "http"
        prometheus.io/path: "/metrics"
      labels:
        name: dyland-lua-api-pre
        envrion: php
        version: ""
    spec:
      initContainers:
        - name: rsync-code
          image: hub.dz11.com/op-base/rsync:v3.1.3
          command:
            - "sh"
            - "-c"
            - >
              /bin/echo '123456' > /home/www/server/rsync.pwd;
              chmod 400 /home/www/server/rsync.pwd;
              /usr/bin/rsync -avzLu --password-file=/home/www/server/rsync.pwd www@$(RSYNC_SERVER)::pre-lua/dyland-lua-api.pre.wh03 /home/www/server/ > /dev/stdout 2>&1;
          env:
            - name: RSYNC_SERVER
              value: 192.168.1.10
          volumeMounts:
            - name: www-root
              mountPath: "/home/www/server"
      containers:
        - name: dyland-lua-api-pre
          image: hub.dz11.com/op-base/openresty:1.11.2.4
          imagePullPolicy: Always
          lifecycle:
            postStart:
              exec:
                command:
                  - "sh"
                  - "-c"
                  - >
                    /bin/echo 'options single-request-reopen' >> /etc/resolv.conf;
          ports:
            - containerPort: 80
          env:
            - name: APP_NAME
              value: dyland-lua-api.pre.wh03
          volumeMounts:
            - name: nginx-conf 
              mountPath: /usr/local/ngx_openresty/nginx/conf/vhost
            - name: applogdir
              mountPath: /home/www/logs/applogs
            - name: srvlogdir
              mountPath: /home/www/logs/srvlogs
            - name: www-root
              mountPath: /home/www/server
        #      mountPath: /home/www/server/dyland-lua-api.pre.wh03
        - name: nginx-vts-exporter
          image: hub.dz11.com/library/nginx-vts-exporter:v0.10.3
          ports:
            - containerPort: 9913
          env:
            - name: NGINX_HOST
              value: "http://localhost/status/format/json"
      volumes:
        - name: nginx-conf
          configMap:
            name: dyland-lua-api-pre-configmap
        - name: applogdir
          hostPath:
            path: /home/www/logs/applogs
        - name: srvlogdir
          hostPath:
            path: /home/www/logs/srvlogs
        - name: www-root
          emptyDir: {}
        #  hostPath:
        #    path: /home/www/server/dyland-lua-api.pre.wh03
      imagePullSecrets:
        - name: dk-reg
      nodeSelector:
        testenv: pre
相關文章
相關標籤/搜索