咱們知道,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
缺點一樣顯而易見:golang
事實上,這是合乎容器化標準的一種實現。docker
而要實現這種方式,須要解決兩個問題:json
第一個問題,咱們使用kubernetes的init container技術來解決(在某些應用場中,使用pod啓動後的鉤子事件也能夠解決,但在相似lua的應用場中,在應用容器啓動以前就須要代碼就位的狀況下,這種方式並不合適,因此init container纔是最好的選擇)。其原理是在應用容器啓動以前,先啓動一個初始化容器,而後在初始化容器中執行從git倉庫,或者從發佈服務器拉取代碼的操做,將代碼直接拉取到應用容器的存儲位置,而後啓動應用容器,這樣應用容器一啓動,就有了代碼。
第二個問題,能夠直接調用k8s api,獲取到當前應用的全部pod信息,而後觸發循環調用pod上的rsync,執行主動拉取操做。
事實上,在咱們當前的測試環境的lua代碼的發佈場景中,尤爲適用。由於咱們在測試環境中,持續集成的過程是自動完成的,每分鐘執行一次持續集成操做。會先從git倉庫將代碼拉取到發佈節點,發佈節點啓動rsync server端。而後各node節點上配置定時任務,每分鐘從發佈節點同步代碼。對於lua應用來說,每次代碼更新就須要執行一次Nginx重載。經過這種發佈方式,咱們能夠實現只有在有代碼更新時,才執行nginx重載,而不是每分鐘一次。
缺點:
增長了發佈服務器的壓力,全部pod再也不共享宿主機代碼,而是每一個pod一套獨立代碼,全部pod都會從發佈服務器拉取代碼,在pod數量較大時,對發佈服務器的io是個考驗。另外若是某節點宕機時,全部pod漂移到新節點上從新啓動,會同一時間向發佈服務器拉取代碼,對發佈服務器的io壓力也會較大
其配置相對於獨立發佈代碼的方式實際上是一樣複雜的,可是其不用限定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