1. 程式人生 > >利用docker開啟持續交付之路

利用docker開啟持續交付之路

持續交付即Continuous Delivery,簡稱CD,隨著DevOps的流行正越來越被傳統企業所重視。持續交付講求以短週期、小細粒度,自動化的方式頻繁的交付軟體,在這個過程中要求開發、測試、使用者體驗等角色緊密合作,快速收集反饋,從而不斷改善軟體質量並減少浪費。然而,在我所接觸的傳統企業中,對於持續交付實踐的實施都還非常初級,坦白說,大部分還停留的手工生成釋出包,手工替換檔案進行部署的階段,這樣做無疑缺乏管理且容易出錯。如果究其原因,我想主要是因為構建一個可實際執行且適合企業自身環境的持續釋出流程並不簡單。然而,Docker作為輕量級的基於容器的解決方案,它對系統侵入性低,容易移植,天生就適合做自動化部署,這些特性非常有助於降低構建持續交付流程的複雜度。本文將通過一個實際案例分享我們在一個真實專案中就如何使用Docker構建持續釋出流程的經驗總結,這些實踐也許不是最先進的,但確是非常實際和符合當時環境的。

專案背景

我們的客戶來自物流行業,由於近幾年業務的飛速發展,其老的入口網站對於日常訪問和訂單查詢還勉強可以支撐,但每當遇到像雙十一這樣訪問量成倍增長的情況就很難招架了。因此,客戶希望我們幫助他們開發一個全新的入口網站。

新網站採用了動靜分離的策略,使用Java語言,基於REST架構,並結合CMS系統。簡單來說,可以把它看成是時下非常典型的一個基於Java的Web應用,它具體包含如下幾個部分:

  • 基於Jersey的動態服務(處理客戶端的動態請求)
  • 二次開發的OpenCMS系統,用於靜態匯出站點
  • 基於js的前端應用並可以打包成為一個OpenCMS支援的站點
  • 後臺任務處理服務(用於處理實時性要求不高的任務,如:郵件傳送等)

以下是系統的邏輯軟體架構圖:

面臨的挑戰以及為什麼選擇Docker

在設計持續交付流程的過程中,客戶有一個非常合理的需求:是否可以在測試環境中儘量模擬真實軟體架構(例如:模擬靜態伺服器的水平擴充套件),以便儘早發現潛在問題?基於這個需求,可以嘗試將多臺機器劃分不同的職責並將相應服務按照職責進行部署。然而,我們遇到的第一個挑戰是:硬體資源嚴重不足  儘管客戶非常積極的配合,但無奈於企業內部層層的審批制度。經過兩個星期的努力,我們很艱難的申請到了兩臺四核CPU加8G記憶體的物理機(如果申請虛擬機器可能還要等一段時間),同時還獲得了一個Oracle資料庫例項。因此,最終我們的任務就變為把所有服務外加持續整合伺服器(Jenkins)全部部署在這兩臺機器上,並且,還要模擬出這些服務真的像是分別執行在不同職責的機器上並進行互動。如果採用傳統的部署方式,要在兩臺機器上完成這麼多服務的部署是非常困難的,需要小心的調整和修改各個服務以及中介軟體的配置,而且還面臨著一旦出錯就有可能耗費大量時間排錯甚至需要重灌系統的風險。第二個挑戰是:企業內部對UAT(與產品環境配置一致,只是資料不同)和產品環境管控嚴格,我們無法訪問,也就無法自動化。這就意味著,整個持續釋出流程不僅要支援自動化部署,同時也要允許下載獨立釋出包進行手工部署。

最終,我們選擇了Docker解決上述兩個挑戰,主要原因如下:

  • Docker是容器,容器和容器之間相互隔離互不影響,利用這個特性就可以非常容易在一臺機器上模擬出多臺機器的效果
  • Docker對作業系統的侵入性很低,因其使用LXC虛擬化技術(Linux核心從2.6.24開始支援),所以在大部分Linux發行版下不需要安裝額外的軟體就可執行。那麼,安裝一臺機器也就變為安裝Linux作業系統並安裝Docker,接著它就可以服役了
  • Docker容器可重複運,且Docker本身提供了多種途徑分享容器,例如:通過export/import或者save/load命令以檔案的形式分享,也可以通過將容器提交至私有Registry進行分享,另外,別忘了還有Docker Hub

下圖是我們利用Docker設計的持續釋出流程:

圖中,我們專門設計了一個環節用於生成唯一發布包,它打包所有War/Jar、資料庫遷移指令碼以及配置資訊。因此,無論是手工部署還是利用Docker容器自動化部署,我們都使用相同的釋出包,這樣做也滿足持續交付的單一製品原則(Single Source Of Truth,Single Artifact)。

Docker與持續整合

持續整合(以下簡稱CI)可以說是當前軟體開發的標準配置,重複使用率極高。而將CI與Docker結合後,會為CI的靈活性帶來顯著的提升。由於我們專案中使用Jenkins,下面會以Jenkins與Dcoker結合為例進行說明。

1.建立Jenkins容器

相比於直接把Jenkins安裝到主機上,我們選擇把它做為Docker容器單獨使用,這樣就省去了每次安裝Jenkins本身及其依賴的過程,真正做到了拿來就可以使用。

Jenkins容器使建立一個全新的CI變的非常簡單,只需一行命令就可完成:

docker run -d -p 9090:8080 ——name jenkins jenkins:1.576

該命令啟動Jenkins容器並將容器內部8080埠重定向到主機9090埠,此時訪問:主機IP:9090,就可以得到一個正在執行的Jenkins服務了。

為了降低升級和維護的成本,可將構建Jenkins容器的所有操作寫入Dockerfile並用版本工具管理,如若需要升級Jenkins,只要重新build一次Dockerfile:

FROM ubuntu

ADD sources.list /etc/apt/sources.list

RUN apt-get update && apt-get install -y -q wget

RUN wget -q -O – http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | apt-key add –

ADD jenkins.list /etc/apt/sources.list.d/

RUN apt-get update

RUN apt-get install -y -q jenkins

ENV JENKINS_HOME /var/lib/jenkins/

EXPOSE 8080

CMD [“java”, “-jar”, “/usr/share/jenkins/jenkins.war”]

每次build時標註一個新的tag:

docker build -t jenkins:1.578 —rm .

另外,建議使用Docker volume功能將外部目錄掛載到JENKINS_HOME目錄(Jenkins會將安裝的外掛等檔案存放在這個目錄),這樣保證了升級Jenkins容器後已安裝的外掛都還存在。例如:將主機/usr/local/jenkins/home目錄掛載到容器內部/var/lib/jenkins:

docker run -d -p 9090:8080 -v /usr/local/jenkins/home:/var/lib/jenkins ——name jenkins jenkins:1.578

  1. 使用Docker容器作為Jenkins容器的Slave

在使用Jenkins容器時,我們有一個原則: 不要在容器內部存放任何和專案相關的資料 。因為執行中的容器不一定是穩定的,而Docker本身也可能有Bug,如果把專案資料存放在容器中,一旦出了問題,就有丟掉所有資料的風險。因此,我們建議Jenkins容器僅負責提供Jenkins服務而不負責構建,而是把構建工作代理給其他Docker容器做。

例如,為了構建Java專案,需要建立一個包含JDK及其構建工具的容器。依然使用Dockerfile構建該容器,以下是示例程式碼(可根據專案實際需要安裝其他工具,比如:Gradle等):

FROM ubuntu

RUN apt-get update && apt-get install -y -q openssh-server openjdk-7-jdk

RUN mkdir -p /var/run/sshd

RUN echo ‘root:change’ |chpasswd

EXPOSE 22

CMD [“/usr/sbin/sshd”, “-D”]

在這裡安裝openssh-server的原因是Jenkins需要使用ssh的方式訪問和操作Slave,因此,ssh應作為每一個Slave必須安裝的服務。執行該容器:

docker run -d -P —name java java:1.7

其中,-P是讓Docker為容器內部的22埠自動分配重定向到主機的埠,這時如果執行命令:

docker ps

804b1d9e4202       java:1.7           /usr/sbin/sshd -D     6 minutes ago       Up 6 minutes       0.0.0.0:49153->22/tcp   java

埠22被重定向到了49153埠。這樣,Jenkins就可以通過ssh直接操作該容器了(在Jenkins的Manage Nodes中配置該Slave)。

有了包含構建Java專案的Slave容器後,我們依然要遵循容器中不能存放專案相關資料的原則。此時,又需要藉助volume:

docker run -d -v /usr/local/jenkins/workspace:/usr/local/jenkins -P —name java java:1.7

這樣,我們在Jenkins Slave中配置的Job、Workspace以及下載的原始碼都會被放置到主機目錄/usr/local/jenkins/workspace下,最終達成了不在容器中放置任何專案資料的目標。

通過上面的實踐,我們成功的將一個Docker容器配置成了Jenkins的Slave。相比直接將Jenkins安裝到主機上的方式,Jenkins容器的解決方案帶來了明顯的好處:

  • 重用更加簡單,只需一行命令就可獲得CI的服務;
  • 升級和維護也變的容易,只需要重新構建Jenkins容器即可;
  • 靈活配置Slave的能力,並可根據企業內部需要預先定製具有不同能力的Slave,比如:可以創建出具有構建Ruby On Rails能力的Slave,可以創建出具有構建NodeJS能力的Slave。當Jenkisn需要具備某種能力的Slave時,只需要docker run將該容器啟動,並配置為Slave,Jenkins就立刻擁有了構建該應用的能力。

如果一個組織內部專案繁多且技術棧複雜,那麼採用Jenkins結合Docker的方案會簡化很多配置工作,同時也帶來了相率的提升。

Docker與自動化部署

說到自動化部署,通常不僅僅代表以自動化的方式把某個應用放置在它應該在的位置,這只是基本功能,除此之外它還有更為重要的意義:

  • 以快速且低成本的部署方式驗證應用是否在目標環境中可執行(通常有TEST/UAT/PROD等環境);
  • 以不同的自動化部署策略滿足業務需求(例如:藍綠部署);
  • 降低了運維的成本並促使開發和運維人員以端到端的方式思考軟體開發(DevOps)。

在我們的案例中,由於上述挑戰二的存在,導致無法將UAT乃至產品環境的部署全部自動化。回想客戶希望驗證軟體架構的需求,我們的策略是:儘量使測試環境靠近產品環境。

  1. 標準化Docker映象

很多企業內部都存在一套叫做標準化的規範,在這套規範中定義了開發中所使用的語言、工具的版本資訊等等,這樣做可以統一開發環境並降低運維團隊負擔。在我們的專案上,依據客戶提供的標

準化規範,我們建立了一系列容器並把它們按照不同的職能進行了分組,如下圖:

圖中,我們把Docker映象分為三層:基礎映象層、服務映象層以及應用映象層,下層映象的構建依賴上層映象,越靠上層的映象越穩定越不容易變。

基礎映象層

  • 負責配置最基本的、所有映象都需要的軟體及服務,例如上文提到的openssh-server

服務映象層

  • 負責構建符合企業標準化規範的映象,這一層很像SaaS

應用映象層

  • 和應用程式直接相關,CI的產出物

分層後, 由於上層映象已經提供了應用所需要的全部軟體和服務,因此可以顯著加快應用層映象構建的速度。曾經有人擔心如果在CI中構建映象會不會太慢?經過這樣的分層就可以解決這個問題。

在Dockerfile中使用FROM命令可以幫助構建分層映象。例如:依據標準化規範,客戶的產品環境執行RHEL6.3,因此在測試環境中,我們選擇了centos6.3來作為所有映象的基礎作業系統。這裡給出從構建base映象到Java映象的方法。首先是定義base映象的Dockerfile:

FROM centos

# 可以在這裡定義使用企業內部自己的源

RUN yum install -y -q unzip openssh-server

RUN ssh-keygen -q -N “” -t dsa -f /etc/ssh/ssh_host_dsa_key && ssh-keygen -q -N “” -t rsa -f /etc/ssh/ssh_host_rsa_key

RUN echo ‘root:changeme’ | chpasswd

RUN sed -i “s/#UsePrivilegeSeparation.*/UsePrivilegeSeparation no/g” /etc/ssh/sshd_config \

&& sed -i “s/UsePAM.*/UsePAM no/g” /etc/ssh/sshd_config

EXPOSE 22

CMD [“/usr/sbin/sshd”, “-D”]

接著,構建服務層基礎映象Java,依據客戶的標準化規範,Java的版本為:jdk-6u38-linux-x64:

FROM base

ADD jdk-6u38-linux-x64-rpm.bin /var/local/

RUN chmod +x /var/local/jdk-6u38-linux-x64-rpm.bin

RUN yes | /var/local/jdk-6u38-linux-x64-rpm.bin &>/dev/null

ENV JAVA_HOME /usr/java/jdk1.6.0_38

RUN rm -rf var/local/*.bin

CMD [“/usr/sbin/sshd”, “-D”]

如果再需要構建JBoss映象,就只需要將JBoss安裝到Java映象即可:

FROM java

ADD jboss-4.3-201307.zip /app/

RUN unzip /app/jboss-4.3-201307.zip -d /app/ &>/dev/null && rm -rf /app/jboss-4.3-201307.zip

ENV JBOSS_HOME /app/jboss/jboss-as

EXPOSE 8080

CMD [“/app/jboss/jboss-as/bin/run.sh”, “-b”, “0.0.0.0”]

這樣,所有使用JBoss的應用程式都保證了使用與標準化規範定義一致的Java版本以及JBoss版本,從而使測試環境靠近了產品環境。

  1. 更好的組織自動化釋出指令碼

為了更好的組織自動化釋出指令碼,版本化控制是必須的。我們在專案中單獨建立了一個目錄:deploy,在這個目錄下存放所有與釋出相關的檔案,包括:用於自動化釋出的指令碼(shell),用於構建映象的Dockerfile,與環境相關的配置檔案等等,其目錄結構是:

├── README.md

├── artifacts   # war/jar,資料庫遷移指令碼等

├── bin         # shell指令碼,用於自動化構建映象和部署

├── images       # 所有映象的Dockerfile

├── regions     # 環境相關的配置資訊,我們只包含本地環境及測試環境

└── roles       # 角色化部署指令碼,會本bin中指令碼呼叫

這樣,當需要向某一臺機器上安裝java和jboss映象時,只需要這樣一條命令:

bin/install.sh images -p 10.1.2.15 java jboss

而在部署的過程中,我們採用了角色化部署的方式,在roles目錄下,它是這樣的:

├── nginx

│   └── deploy.sh

├── opencms

│   └── deploy.sh

├── service-backend

│   └── deploy.sh

├── service-web

│   └── deploy.sh

└── utils.sh

這裡我們定義了四種角色:nginx,opencms,service-backend以及service-web。每個角色下都有自己的釋出指令碼。例如:當需要釋出service-web時,可以執行命令:

bin/deploy.sh -e test -p 10.1.2.15 service-web

該指令碼會載入由-e指定的test環境的配置資訊,並將service-web部署至IP地址為10.1.2.15的機器上,而最終,bin/deploy.sh會呼叫每個角色下的deploy.sh指令碼。

角色化後,使部署變的更為清晰明瞭,而每個角色單獨的deploy指令碼更有利於劃分責任避免和其他角色的干擾。

  1. 構建本地虛擬化環境

通常在聊到自動化部署指令碼時,大家都樂於說這些指令碼如何簡化工作增加效率,但是,其編寫過程通常都是痛苦和耗時,需要把指令碼放在相應的環境中反覆執行來驗證是否工作正常。這就是我為什麼建議最好首先構建一個本地虛擬化環境,有了它,就可以在自己的機器上反覆測試而不受網路和環境的影響。

Vagrant(http://www.vagrantup.com/)是很好的本地虛擬化工具,和Docker結合可以很容易的在本地搭建起與測試環境幾乎相同的環境。以我們的專案為例,可以使用Vagrant模擬兩臺機器,以下是Vagrantfile示例:

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

config.vm.define “server1″, primary: true do |server1|

server1.vm.box = “raring-docker”

server1.vm.network :private_network, ip: “10.1.2.15”

end

config.vm.define “server2″ do |server2|

server2.vm.box = “raring-docker”

server2.vm.network :private_network, ip: “10.1.2.16”

end

end

由於部署指令碼通常採用SSH當方式連線,所以,完全可以把這兩臺虛擬機器看做是網路中兩臺機器,呼叫部署指令碼驗證是否正確。限於篇幅,這裡就不多說了。

4 構建企業內部的Docker Registry

上文提到了諸多分層映象,如何管理這些映象?如何更好的分享?答案就是使用Docker Registry。Docker Registry是一個映象倉庫,它允許你向Registry中提交(push)映象同時又可以從中下載(pull)。

構建本地的Registry非常簡單,執行下面的命令:

docker run -p 5000:5000 registry

更多關於如何使用Registry可以參考地址:https://github.com/docker/docker-registry

當搭建好Registry後,就可以向它push你的映象了,例如:需要將base映象提交至Registry:

docker push your_registry_ip:5000/base:centos

而提交Java和JBoss也相似:

docker push your_registry_ip:5000/java:1.6

docker push your_registry_ip:5000/jboss:4.3

使用下面的方式下載映象:

docker pull your_registry_ip:5000/jboss:4.3

總結

本文總結我們在實際案例中使用Docker一些實踐,它給我們的印象就是非常靈活,幾乎是一個多面手,給整個流程帶來了極大的靈活性和擴充套件性,並且也展現了極好的效能,符合它天生就為部署而生的特質。

相關推薦

利用docker開啟持續交付

持續交付即Continuous Delivery,簡稱CD,隨著DevOps的流行正越來越被傳統企業所重視。持續交付講求以短週期、小細粒度,自動化的方式頻繁的交付軟體,在這個過程中要求開發、測試、使用者體驗等角色緊密合作,快速收集反饋,從而不斷改善軟體質量並減少浪費。然而

Docker學習總結(8)——利用Docker開啟持續交付

持續交付即Continuous Delivery,簡稱CD,隨著DevOps的流行正越來越被傳統企業所重視。持續交付講求以短週期、小細粒度,自動化的方式頻繁的交付軟體,在這個過 程中要求開發、測試、

docker入門持續交付

安裝Docker | 服務相關 wget -qO- https://get.docker.com/ | sh # 以非root使用者可以直接執行docker sudo usermod -aG d

持續交付

什麼是持續交付 持續交付(Continuous delivery,縮寫為 CD),是一種軟體工程方法,讓軟體產品的產出過程在一個短週期內完成,以保證軟體可以穩定、持續的保持在隨時可以釋出的狀況。它的目標在於讓軟體的編譯、測試與釋出變得更快更頻繁。這種方式可以減少軟體開發的成本與時間,減少風險。

利用jenkins和docker實現持續交付

利用jenkins和docker實現持續交付     一.什麼是持續交付   讓軟體產品的產出過程在一個短週期內完成,以保證軟體可以穩定、持續的保持在隨時可以釋出的狀況。它的目標在於讓軟體的構建、測試與釋出變得更快以及更頻繁。這種方式可以減少軟體開發的成本與時間,減少風險。 &n

開啟Python取經-CLASS-6(Part 1)

int code 中標 cnblogs 環境 執行 變量 spa -c 第一個python程序 HELLO WORLD 1 print("hello world") 單行註釋:# 多行註釋:‘‘‘....‘‘‘或者"""....""" 在linux編程中,要在程序中

開啟python學習

under 配置 版本 入門 驗證 文件 客戶 而在 code   新生入學這一周來,基本都在看python從入門到精通,邊看書邊敲代碼,簡單的幾行代碼,鞏固學到的知識,像當初學習各類編程語言一樣,從最初開發環境的搭建,數據類型等,學習中做好筆記,然後學會運用。   學習目

Python Appium 開啟Android測試

ppp encoding dev aps 發出 cli 進行 utf8 androi 1、獲取 Android app的Activity 打開終端cmd,先cd進入到剛才下載的“新浪.apk”目錄下,然後使用aapt dump badging xx

TCP/IP(一)開啟計算機網絡

廣域網 概述 pic .cn 慢慢 internet 通信 hub album 前言 在一段時間裏,都很想知道一臺電腦怎麽跟另一臺電腦通信的,我發送一個qq給女朋友,怎麽準確的發送過去的,又是怎麽接受消息的。 接下來一段時間給大家慢慢分享關於計算機網絡的相關知識。 一、局域

【好課推薦】再不懂區塊鏈,你就out了!快快開啟進階,挑戰高薪職位吧

理念 培訓 政府 改變 全國 要素 sha 精品 下一個 區塊鏈技術被認為是繼蒸汽機、電力、互聯網之後,下一代顛覆性的核心技術。 如果說蒸汽機釋放了人們的生產力,電力解決了人們基本的生活需求,互聯網徹底改變了信息傳遞的方式,那麽區塊鏈作為構造信任的機器,將可能徹底改變整個人

開啟程式猿

開啟程式猿之路 自我介紹 在大學之前就常常聽說,高中好好努力,上了大學就可以隨便玩了,沒有人管;還聽說不掛科,不翹課的大學不完整。在高考之後我就問我姐姐:大學生是不是空餘時間很多,很閒。她回答我說:你可以很閒,時間到了去上課,下課就回宿舍或者出去玩;也可以選擇讓自己忙起來,去參加學生會,

持續交付應用標準化模型與實踐

有標準化的自動化是平臺,無標準化的自動化是工具! 標準化在多個場合的交流中,始終是大家關注的焦點,無非就是What/Why/How之類的問題。當然脫離標準化,自動化是否可以執行?答案不能否定,但這樣的自動化成本和代價必須要更高。因為這樣,意味著每一次應用的接入都需要重新Review之前的自動化實現。

持續整合——Maven的Missing artifact問題解決

      今天在建立一個新的Maven專案時,在其中添加了很多依賴。剛開始為了避免錯誤就每新增一次,儲存一下,Eclipse就會下載相應的包。最後為了加快速度就把剩下的包全部添加了,再次儲存就出現了Missing artifact錯誤,就連以前正常的包也出現了這個問題。  

程式設計師如何開啟機器學習

我曾是一名想進入AI行業的軟體開發者。為了更快熟悉這裡邊的門道,我閱讀了機器學習的書籍,瀏覽了不少帖子,還學習了Coursera上關於機器學習的課程。 但是,但是,依然不知道如何開始…… 你是否也有這樣的經歷呢? 很多開發者都問我:我該如何開始學習機器學習? 記不

開啟自學python

今年3月份初辭職,從深圳回到廣州,決心轉行。之前在學校時搞過網站設計,對IT還是有一點點的理解。畢業後的三年在深圳的一家藥企工作,在其中的最大感受是:技術更新的速度非常慢,生產的裝置和工具還是十幾年前的,需要工人來一步步操作,一點自動化都沒有。身邊很多同學投身到IT行業中,時

從零學習人工智慧,開啟職業規劃

####作者介紹 零壹,資深演算法工程師,目前擔任 AI 醫療專案技術負責人,CSDN 專家。前供求世界網路科技運營總監、數學碩士,數十次獲得建模獎項,全國研究生數學建模競賽一等獎得主,熱衷分享。個人微訊號:huangtaonide、微信公眾號:R-data、還

Openstack+Kubernetes+Docker微服務實踐--RPC

重點來了,本文全面闡述一下我們的RPC是怎麼實現並如何使用的,跟Kubernetes和Openstack怎麼結合。  在選型一文中說到我們選定的RPC框架是Apache Thrift,它的用法是在Main方法中重啟服務,在Client端連線服務去呼叫, 而我的想法是要跟Du

持續整合——資料訪問層單元測試遇到的問題

在編寫資料訪問層的單元測試時,遇到不少問題,有些問題可以很容易Google到解決方法,而有些只能自己研究解決。這裡分享幾個典型的問題以及解決方法。先交代一下用到的測試框架 Spring Test + SpringTestDbUnit + DbUnit。一、先說一個低階的問題。

Docker學習總結(14)——從程式碼到上線, 雲端Docker持續交付實踐

2016雲棲大會·北京峰會於8月9號在國家會議中心拉開帷幕,在雲棲社群開發者技術專場中,來自阿里雲技術專家羅晶(瑤靖)為在場的聽眾帶來《從程式碼到上線,雲端Docker化持續交付實踐》精彩分享。

十三:對微服務與持續交付整體的理解

微服務專欄地址 目錄 1. 簡介   本文的核心是理解概念與流程,沒有涉及多少具體是實際操作層面的內容,後續有計劃會整理相關內容,持續交付流水線也是一塊很大的內容,需要實際探索、實踐、總結出最適合的方案。文章的內容大多數整理於《微服務架構