本文要點
- 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所示。
在源碼方面,我們會使用Github倉庫。在源碼構建方面,我們會使用AWS CodeBuild項目。在部署方面,我們會使用ECS服務的Fargate啓動形式。
創建和部署CodePipeline應用到ECS Fargate涉及到如下的步驟:
- 爲服務創建ECS Fargate Task Definition
- 配置Task Subnet/s的連接
- 創建或配置S3 Bucket,用於存放CodePipeline Build階段的輸出製件(Output Artifact)。
- 創建CodePipeline,用來在ECS Fargate上部署Docker容器應用(鏡像)。
- 爲Stage製件修改輸入/輸出設置。
- 運行CodePipeline
- 修改代碼並重新運行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
中需要的一個映射。version
是buildspec.yml
需要的另外一個映射。buildspec.yml文件位於GitHub的倉庫中。
添加鏡像定義文件
要部署基於容器的應用,比如部署到ECS上的應用,AWS CodePipeline需要有一個JSON格式的鏡像定義文件。鏡像定義文件默認名爲imagedefinitions.json
,但是可以使用其他的名稱。鏡像定義文件描述了容器應用,它包含兩個屬性:name
和imageURI
。name
指明瞭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所示。
在任務子網中配置連接性
在創建服務之前,我們需要在Subnets中配置到Internet的連接性,在配置服務的時候,我們需要用的這個子網。Route Table列出了路由,我們使用默認的Internet gateway添加一個新的路由。添加路由時,將Destination設置爲0.0.0.0/0,這是一個IP地址。將Target選擇爲internet gateway.。
創建和測試容器服務
接下來,在默認集羣中創建一個ECS容器,如圖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所示的一條信息。
創建和配置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所示。
這樣會啓動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所示。
對於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。
對於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。
如果是第一次創建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。
在Review頁面,我們可以檢查Source和Build環境。向下滑動並點擊Create就可以創建CodeBuild項目了。CodeBuild項目創建成功後會列到Build projects中,如圖10所示。
CodeBuild默認創建的service role並不包含一些所需的權限。我們需要修改service role以便於包含內聯的策略,添加s3:GetObject
和s3:PutObject
權限。要添加的內聯策略如下所示:
{
"Version":"2012-10-17",
"Statement":[
{
"Effect":"Allow",
"Action":["s3:PutObject","s3:GetObject"],
"Resource":"arn:aws:s3:::*"
}
]
}
測試CodeBuild項目
在CodePipeline中創建和配置CodeBuild之前,我們可以測試一下CodeBuild項目,這樣如果有錯誤的話,我們可以立即修正。如圖11所示,點擊Start build將會開始構建。
此時將會啓動Start new build嚮導,點擊Start build。CodeBuild項目將會啓動,代碼會開始構建。當CodeBuild項目完成時,Phase details將會與圖12保持一致。
CodeBuild項目會生成dvohra/node-server Docker鏡像並將其上傳至Docker Hub,如圖13所示。
創建CodePipeline
我們已經爲CodePipeline的每個階段都創建了項目:源碼所需的GitHub代碼倉庫、用於構建的CodeBuild以及用於Staging的ECS Fargate服務,接下來,我們就可以創建CodePipeline了。打開該地址的CodePipeline控制檯,並點擊圖14中所示的Get started。
這樣將會啓動Create pipeline嚮導,如圖15所示。首先,我們指定Pipeline name(node-server-fargate),然後點擊Next step。
接下來,配置Source location。在Source provider中,選擇GitHub,如圖16所示。
接下來,通過Connect to GitHub連接至GitHub,如圖17所示。
選擇GitHub Repository,如圖18所示。
選擇倉庫Branch,如圖19所示,然後點擊Next step。
接下來,我們要配置Build。將Build provider選擇爲AWS CodeBuild,如圖20所示。
後續展現的內容會基於所選擇的Build provider有所差異。對於AWS CodeBuild來說,將會展現一個CodeBuild詳情的區域。在Configure your project區域中,選擇Select an existing project,如圖21所示。在Project name中,選擇之前創建的名爲node-server的項目。
點擊Next step,接下來,我們需要配置CodePipeline的Deploy階段,如圖22所示。將Deployment provider選擇爲Amazon ECS。
後續展現的內容會基於所選擇的Deployment provider有所差異,圖23展示了Amazon ECS的配置區域。在Amazon ECS區域中,選擇Cluster name,這將是ECS服務要部署到的集羣,在這裏選擇default。
Service name中要選擇ECS要部署的服務,也就是node-server-service,如圖24所示。
將Image filename設置爲imagedefinitions.json
,如圖25所示。如果忽略該值的話,默認的Image filename是imagedefinitions.json
,並且這個文件應該位於GitHub倉庫的源碼中。然後點擊Next step。
接下來,選擇Service Role name,如圖26所示。在CodePipeline第一次創建的時候,將會在IAM中自動創建一個service role。在以後的操作中,將會列出Service role供選擇。
點擊Next step,檢查CodePipeline之後點擊Create pipeline,如圖27所示。
如圖28所示,CodePipeline將會創建完成。CodePipeline創建之後將會自動運行,如圖28,它會處於Source階段的In Progress狀態。
Source階段完成之後,它的狀態將會變成Succeeded,如圖29所示。接下來,Build階段將會運行,它會處於In Progress狀態。
Build完成時也會成爲Succeeded狀態,如圖30所示。此時,Staging階段開始運行。
修改輸入/輸出製件
我們已經配置了CodePiepline的所有階段,那麼爲什麼還需要修改輸入/輸出製件的設置呢?這是因爲對於由CodePipeline部署的示例ECS應用程序來說,默認的輸入/輸出製件設置並不是運行CodePipeline所需要的。默認情況下,Staging階段的輸入製件是_Build_階段的輸出製件。CodeBuild只是構建Docker映像並將Docker映像上載到Docker Hub或Amazon ECR中,CodeBuild階段不會生成任何輸出製件。Staging階段的輸入製件需要設置爲Source階段的輸出製件。如果不修改輸入/輸出製件的設置的話,Staging階段將會失敗。創建後自動啓動的CodePipeline將會失敗。爲了修改輸入/輸出製件的設置,點擊Edit,如圖31所示。
在修改完輸入/輸出製件後,點擊Save pipeline changes,如圖32所示。
運行CodePipeline
要在修改之後運行CodePipeline,我們需要點擊Release change,如圖33所示。
在Release change確認對話框中,點擊Release。修改將會應用到CodePipeline上,CodePipeline會從頭開始運行,Source階段會處於In Progress狀態,如圖34所示。 Build和Staging階段所顯示的狀態是之前運行的結果,並不代表這些階段的當前狀態。
CodePipeline的所有階段都會完成,並處於Succeeded狀態,如圖35所示。
前文所述的服務任務會停止運行,基於修改後的任務定義所生成的任務將會開始運行,如圖36所示的RUNNING任務狀態。
要調用新的任務,我們可以像前面那樣在Elastic Network interface找到該任務的公開IP,並在Web瀏覽器中打開URL <Public IP>:8080,這樣就能調用該任務了。Node服務器應用的響應如圖37所示。
修改源碼,重新運行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狀態。
在Build階段成功完成之後,Staging階段會開始運行,其狀態信息如圖39所示。
Staging階段也會成功完成。新的任務將會啓動,使用新任務的公開IP,在Web瀏覽器中打開<Public IP>:8080 URL。新任務將會被調用,Node服務器的響應如圖40所示,Node服務器的響應反映了server.js的變化。
小結
在本文中,我們討論瞭如何將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