使用AWS CodePipeline部署Docker容器實現DevOps

本文要點

  • AWS CodePipeline是一項DevOps服務,用來對各種AWS平臺上託管的應用進行持續集成、持續交付和持續部署。
  • Amazon Elastic Container Service(ECS)是AWS託管的一項服務,它能夠使用Docker容器實現應用的容器化。
  • Amazon Fargate是Amazon Elastic Container Service(ECS)的一種無服務器(serverless)啓動形式。
  • 對於部署到ECS的示例應用來說,AWS CodePipeline由一個源碼倉庫(如GitHub repo)、用於進行構建的AWS CodeBuild以及用於Staging環境的AWS ECS(Fargate)服務組成。
  • 將AWS CodePipeline用於AWS ECS服務的好處在於在新Docker鏡像構建和部署的過程中,ECS服務能夠繼續運行。

Docker容器可以使用多種雲平臺進行部署,而Amazon Elastic Container Service(ECS)就是其中之一。ECS提供了Fargate運行類型,這是一個serverless平臺,藉助它容器服務能夠運行在Docker容器中,而不是運行在EC2實例中。

問題

由於Docker鏡像源碼或代碼構建的變更,Docker容器的部署可能需要更新或修改。Docker鏡像中任何的代碼修改都需要重新構建Docker鏡像,Docker服務也需要重新部署。當某個AWS ECS正在運行時,如果沒有集成、構建、交付和部署源碼的機制,那麼就會涉及到停止ECS服務任務,這樣的後果就是ECS服務的停機。ECS服務的高可用性具有很高的優先級,所以停止服務重新進行部署並不是合適的可選方案。

解決方案

AWS CodePipeline是一個用於持續集成、持續交付和持續部署的DevOps服務,它適用於各種AWS平臺上的應用,其中也包括Amazon ECS和Fargate平臺。ECS在更新和修改時,可以不用停止ECS服務任務。AWS CodePipeline在一個動態環境中提供了ECS服務的高可用性,在這種環境中,Docker鏡像的源碼變更是非常常見的。CodePipeline由三個階段組成:源代碼集成、源代碼構建和部署,如圖1所示。

圖1:CodePipeline的不同階段

在源碼方面,我們會使用Github倉庫。在源碼構建方面,我們會使用AWS CodeBuild項目。在部署方面,我們會使用ECS服務的Fargate啓動形式。

創建和部署CodePipeline應用到ECS Fargate涉及到如下的步驟:

  1. 爲服務創建ECS Fargate Task Definition
  2. 配置Task Subnet/s的連接
  3. 創建或配置S3 Bucket,用於存放CodePipeline Build階段的輸出製件(Output Artifact)。
  4. 創建CodePipeline,用來在ECS Fargate上部署Docker容器應用(鏡像)。
  5. 爲Stage製件修改輸入/輸出設置。
  6. 運行CodePipeline
  7. 修改代碼並重新運行CodePipeline。

搭建環境

唯一的先決條件是要有一個AWS帳戶。CodePipeline部署在ECS Fargate上的應用程序是一個Docker應用。任何具有源碼倉庫的Docker鏡像都可以使用,我們所使用的是dvohra/node-server Docker鏡像。Docker鏡像dvohra/node-server的源碼存放在GitHub倉庫中

創建GitHub代碼倉庫

如果要使用新的GitHub源碼倉庫的話,它必須要包含一個Dockerfile文件,據此來構建Docker鏡像。dvohra/node-server鏡像的Dockerfile是基於node:4.6 Docker鏡像的。Dockerfile聲明要複製server.js文件到當前目錄下(這個文件用來創建一個Node服務器)、暴露Node服務器所監聽的8080端口併爲server.js腳本運行一個node命令。server.js文件會創建一個Node服務器並處理HTTP請求/響應。

爲CodeBuild項目添加構建規範

構建規範(build spec)是具有build命令和設置的YAML語法的文件,CodeBuild項目會使用它來運行構建過程。構建規範文件必須叫做“buildspec.yml”並且必須要複製到源碼倉庫的根路徑下。buildspec.yml文件包含了描述各個構建階段的鍵/值對。構建文件中的phases表述了階段序列,這是buildspec.yml中需要的一個映射。versionbuildspec.yml需要的另外一個映射。buildspec.yml文件位於GitHub的倉庫中。

添加鏡像定義文件

要部署基於容器的應用,比如部署到ECS上的應用,AWS CodePipeline需要有一個JSON格式的鏡像定義文件。鏡像定義文件默認名爲imagedefinitions.json,但是可以使用其他的名稱。鏡像定義文件描述了容器應用,它包含兩個屬性:nameimageURIname指明瞭Docker容器的名稱,容器必須要在運行CodePipeline之前運行。imageURI指定了要在Docker容器中運行的Docker鏡像。Docker鏡像一般情況下會與ECS容器中已經運行的鏡像相同。鏡像可以是不同的,變種形式一般會通過鏡像標籤來設置。Node服務器應用所用的imagedefinitions.json文件可以在GitHub上看到,該應用將會部署到ECS Fargate上。

創建任務定義

ECS應用中的任務定義(task definition)描述了ECS部署環境中的容器。在本節中,我們會爲Node服務器容器創建任務定義,以便於將其部署到ECS Fargate中。如果你沒有登錄的話,請訪問該地址並登錄。點擊Get started訪問ECS控制檯。然後,點擊導航區域的Task Definitions。在Task Definitions中點擊Create new Task Definition。在Create new Task Definition中選擇Fargate啓動類型。點擊Next step,我們要配置任務和容器定義。在Add container對話框中,指定Container name(node-server)並指定Image爲_dvohra/node-server_。任務定義如圖2所示。

圖2:任務定義

在任務子網中配置連接性

在創建服務之前,我們需要在Subnets中配置到Internet的連接性,在配置服務的時候,我們需要用的這個子網。Route Table列出了路由,我們使用默認的Internet gateway添加一個新的路由。添加路由時,將Destination設置爲0.0.0.0/0,這是一個IP地址。將Target選擇爲internet gateway.

創建和測試容器服務

接下來,在默認集羣中創建一個ECS容器,如圖3所示。

圖3:具有一個服務的集羣

按照Fargate啓動類型,每個任務都會有一個Elastic Network Interface(ENI)。複製該任務的Public IP,它與Elastic Network Interface的Public IP相同,我們可以從任務Details頁的Network區域,也可以從ENI控制檯找到它。在瀏覽器中打開_<Public IP of Task>:8080_ 這個URL地址並調用Node服務應用。Node服務器會返回如圖4所示的一條信息。

圖4:Node服務器的響應

創建和配置S3 Bucket

CodePipeline在構建Node服務器應用的源碼並將其以Docker鏡像的形式部署爲ECS服務時,需要CodeBuild項目生成“輸出製件(Output Artifacts)”。輸出製件會保存在S3 bucket中,在創建CodePipeline的時候要選擇這個bucket。在S3控制檯中創建一個新的S3 Bucket,或者選擇之前運行CodePipeline時所創建的S3 bucket。

創建CodeBuild項目

接下來,我們要創建一個CodeBuild項目,它用來將源碼構建到Docker鏡像中。關於Docker鏡像dvohra/node-server的源碼以及用於將源碼構建到Docke鏡像中的buildspec.yml文件,我們在前文中已經討論過了。在Docker鏡像構建完成之後,CodeBuild會將其上傳至Docker hub。要創建CodeBuild項目,我們需要在Web瀏覽器中打開這個URL。選擇Build projects並點擊Create project,如圖5所示。

圖5:Build projects>Create project

這樣會啓動Create project嚮導。在Configure project中指定項目名稱(node-server),在Source>Source Provider中選擇GitHub。對於倉庫,使用Use a repository in my account並選擇_dvohra/docker-node-server _倉庫。Git clone depth使用默認的設置,即值爲1。

選中Webhook、Insecure SSL和Build Badge選項。選擇Webhook能夠讓GitHub倉庫在每次代碼變更的時候都會重新構建代碼。Insecure SSL選項能夠在連接項目源時,生成源碼構建的SSL告警。Build Badge能夠讓項目的狀態可見並且可嵌入。在Environment: How to build中的Environment image部分,選擇Use an image managed by AWS CodeBuild。將Operating System選擇爲Ubuntu。對於Runtime,選擇Docker,如圖6所示。

圖6:將運行時選擇爲Docker

對於Runtime version,請選擇aws:codebuild/docker:17.09.0,它代表了Docker的17.9版本。如果有新的可用版本的話,請選擇更新的版本。對於Docker運行時環境,Privileged選項會自動選中,因爲它是構建Docker鏡像所需要的。對於Build specification,請選擇Use the buildspec.yml in the source code root directory,如圖7所示。默認情況下,Buildspec name的值爲buildspec.yml。

圖7 配置環境:如何構建

對於Certificate,選擇Do not install any certificate。CodePipeline並不需要證書,但是如果要實現更安全CodeBuild,我們可以安裝來自S3的自簽名證書。接下來,在Artifacts中配置輸出製件。CodePipeline需要輸出製件。這裏,我們將Type選擇爲Amazon S3,並指明要使用的S3 bucket文件夾名。將Path設置爲“/”,這樣它將會在bucket根路徑下創建文件夾。將Namespace type選擇爲Node。將Bucket name選擇爲我們之前配置的Bucket,如圖8所示,同時,將Cache設置爲No cache

圖8 爲製件配置S3 Bucket

如果是第一次創建CodeBuild項目的話,在Service role中選擇Create a service role in your account選項。如果之前創建過CodeBuild項目,那麼選擇Choose an existing service role from your account。選中Allow AWS CodeBuild to modify this service role,這樣它才能夠和本構建項目一起使用,如圖9所示。對於VPC,請選擇No VPC,然後點擊Continue。

圖9 配置Service Role和Service Role的設置

Review頁面,我們可以檢查SourceBuild環境。向下滑動並點擊Create就可以創建CodeBuild項目了。CodeBuild項目創建成功後會列到Build projects中,如圖10所示。

圖10 CodeBuild項目

CodeBuild默認創建的service role並不包含一些所需的權限。我們需要修改service role以便於包含內聯的策略,添加s3:GetObjects3:PutObject權限。要添加的內聯策略如下所示:

{
   "Version":"2012-10-17",
   "Statement":[
     {
       "Effect":"Allow",
       "Action":["s3:PutObject","s3:GetObject"],
       "Resource":"arn:aws:s3:::*"
     }
   ]
}

測試CodeBuild項目

在CodePipeline中創建和配置CodeBuild之前,我們可以測試一下CodeBuild項目,這樣如果有錯誤的話,我們可以立即修正。如圖11所示,點擊Start build將會開始構建。

圖11 開始構建

此時將會啓動Start new build嚮導,點擊Start build。CodeBuild項目將會啓動,代碼會開始構建。當CodeBuild項目完成時,Phase details將會與圖12保持一致。

圖12 Phase details和Build logs表明CodeBuild已經完成

CodeBuild項目會生成dvohra/node-server Docker鏡像並將其上傳至Docker Hub,如圖13所示。

圖13 CodeBuild生成Docker鏡像並將其上傳至Docker Hub

創建CodePipeline

我們已經爲CodePipeline的每個階段都創建了項目:源碼所需的GitHub代碼倉庫、用於構建的CodeBuild以及用於Staging的ECS Fargate服務,接下來,我們就可以創建CodePipeline了。打開該地址的CodePipeline控制檯,並點擊圖14中所示的Get started。

圖14 CodePipeline>Get started

這樣將會啓動Create pipeline嚮導,如圖15所示。首先,我們指定Pipeline name(node-server-fargate),然後點擊Next step。

圖15 設置Pipeline Name

接下來,配置Source location。在Source provider中,選擇GitHub,如圖16所示。

圖16 選擇Source provider

接下來,通過Connect to GitHub連接至GitHub,如圖17所示。

圖17 Connect to GitHub

選擇GitHub Repository,如圖18所示。

圖18 選擇GitHub倉庫

選擇倉庫Branch,如圖19所示,然後點擊Next step。

圖19 Source location>Next step

接下來,我們要配置Build。將Build provider選擇爲AWS CodeBuild,如圖20所示。

圖20 將Build provider選擇爲AWS CodeBuild

後續展現的內容會基於所選擇的Build provider有所差異。對於AWS CodeBuild來說,將會展現一個CodeBuild詳情的區域。在Configure your project區域中,選擇Select an existing project,如圖21所示。在Project name中,選擇之前創建的名爲node-server的項目。

圖21 選擇CodeBuild項目

點擊Next step,接下來,我們需要配置CodePipeline的Deploy階段,如圖22所示。將Deployment provider選擇爲Amazon ECS

圖22 將Deployment provider選擇爲Amazon ECS

後續展現的內容會基於所選擇的Deployment provider有所差異,圖23展示了Amazon ECS的配置區域。在Amazon ECS區域中,選擇Cluster name,這將是ECS服務要部署到的集羣,在這裏選擇default

圖23 選擇ECS集羣

Service name中要選擇ECS要部署的服務,也就是node-server-service,如圖24所示。

圖24 選擇ECS服務

Image filename設置爲imagedefinitions.json,如圖25所示。如果忽略該值的話,默認的Image filename是imagedefinitions.json,並且這個文件應該位於GitHub倉庫的源碼中。然後點擊Next step。

圖25 指定Image filename

接下來,選擇Service Role name,如圖26所示。在CodePipeline第一次創建的時候,將會在IAM中自動創建一個service role。在以後的操作中,將會列出Service role供選擇。

圖26 選擇Service role

點擊Next step,檢查CodePipeline之後點擊Create pipeline,如圖27所示。

圖27 Create pipeline

如圖28所示,CodePipeline將會創建完成。CodePipeline創建之後將會自動運行,如圖28,它會處於Source階段的In Progress狀態。

圖28 CodePipeline Source階段處於in Progress狀態

Source階段完成之後,它的狀態將會變成Succeeded,如圖29所示。接下來,Build階段將會運行,它會處於In Progress狀態。

圖29 Source階段完成,Build階段處於In Progress狀態

Build完成時也會成爲Succeeded狀態,如圖30所示。此時,Staging階段開始運行。

圖30 Build階段完成,Staging階段處於In Progress狀態

修改輸入/輸出製件

我們已經配置了CodePiepline的所有階段,那麼爲什麼還需要修改輸入/輸出製件的設置呢?這是因爲對於由CodePipeline部署的示例ECS應用程序來說,默認的輸入/輸出製件設置並不是運行CodePipeline所需要的。默認情況下,Staging階段的輸入製件是_Build_階段的輸出製件。CodeBuild只是構建Docker映像並將Docker映像上載到Docker Hub或Amazon ECR中,CodeBuild階段不會生成任何輸出製件。Staging階段的輸入製件需要設置爲Source階段的輸出製件。如果不修改輸入/輸出製件的設置的話,Staging階段將會失敗。創建後自動啓動的CodePipeline將會失敗。爲了修改輸入/輸出製件的設置,點擊Edit,如圖31所示。

圖31 CodePipeline>Edit

在修改完輸入/輸出製件後,點擊Save pipeline changes,如圖32所示。

圖32 Save pipeline changes

運行CodePipeline

要在修改之後運行CodePipeline,我們需要點擊Release change,如圖33所示。

圖33 Release change

Release change確認對話框中,點擊Release。修改將會應用到CodePipeline上,CodePipeline會從頭開始運行,Source階段會處於In Progress狀態,如圖34所示。 BuildStaging階段所顯示的狀態是之前運行的結果,並不代表這些階段的當前狀態。

圖34 Source階段處於In Progress狀態

CodePipeline的所有階段都會完成,並處於Succeeded狀態,如圖35所示。

圖35 CodePipeline的所有階段都成功完成

前文所述的服務任務會停止運行,基於修改後的任務定義所生成的任務將會開始運行,如圖36所示的RUNNING任務狀態。

圖36 新任務正在運行

要調用新的任務,我們可以像前面那樣在Elastic Network interface找到該任務的公開IP,並在Web瀏覽器中打開URL <Public IP>:8080,這樣就能調用該任務了。Node服務器應用的響應如圖37所示。

圖37 Node服務器應用的響應

修改源碼,重新運行CodePipeline

如果ECS服務的響應和沒有CodePipeline時直接調用服務完全一樣,那麼運行CodePipeline的優勢在什麼地方呢?一般來講,部署在生產環境的ECS的源碼會定期更新,這意味着Docker鏡像需要重新構建。在ECS服務任務中部署的Docker鏡像也需要進行更新。在更新Docker鏡像的時候,不能中斷基於ECS的服務。藉助CodePipeline,每次修改源代碼時都會自動重新運行CodePipeline。在沒有任何用戶干預的情況下,每當源代碼發生更改時,ECS部署將會自動更新。

爲了闡述該功能,我們對GitHub倉庫中的代碼稍微修改一下,比如讓server.js中的Hello消息略有差異。在倉庫中點擊Commit changes。CodePipeline將會自動重新運行,如圖38所示,圖中Source階段已經成功完成,而Build階段正處於in progress狀態。

圖38 CodePipeline自動重啓

在Build階段成功完成之後,Staging階段會開始運行,其狀態信息如圖39所示。

圖39 Build階段完成,Staging階段啓動

Staging階段也會成功完成。新的任務將會啓動,使用新任務的公開IP,在Web瀏覽器中打開<Public IP>:8080 URL。新任務將會被調用,Node服務器的響應如圖40所示,Node服務器的響應反映了server.js的變化。

圖40 新任務中已修改的Node服務器響應

小結

在本文中,我們討論瞭如何將Docker容器應用部署到ECS Fargate中。我們演示瞭如何更新Docker鏡像的源代碼,並在不停止容器服務的情況下將新的Docker鏡像部署到正在運行的容器服務中,所有這些都使用AWS CodePipeline完成的。

作者介紹

Deepak Vohra是Sun認證的Java程序員和Web組件開發人員。Vohra在WebLogic Developer Journal、XML Journal、ONJava、Java.net、IBM developerWorks、Java Developer Journal、Oracle雜誌和devx上都發表過Java和Java EE相關的技術文章。Vohra已經出版了五本關於Docker的書籍,他是一位Docker導師。

原文鏈接:

Deploying Docker Containers using an AWS CodePipeline for DevOps

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章