在Segment早期,我們的基礎設施是拼裝的,我們通過AWS的用戶界面提供實例,有一個由從未使用的AMI組成的組件,以及通過三種不同方式實現的配置。
隨著公司業務開始騰飛,我們的工程師團隊開始擴充,架構也開始日益復雜,但是生產環境的相關工作仍然局限于我們少數這些知道神秘陷阱的人。我們一直在逐步改善過程,但我們需要給我們的基礎設施更深層次的改革以保持快速發展。
因此,幾個月前,我們坐下來問自己:“如果我們今天設計,那么基礎設施設置會是什么樣子?”
在10周的過程后,我們完全重新設計了基礎設施,我們撤下了幾乎每一個實例和舊的配置,將我們的服務移到Docker容器中運行,并且切換為使用全新的AWS賬號。
我們花了很多時間考慮如何使生產環境設置變得可審計、簡單并且易用,同時仍然不失可擴展的彈性。
下面就是我們的解決方案。
獨立的AWS賬戶
我們切換到了完全獨立的AWS賬戶,而不是使用Region或者Tag來分離不同的預生產環境和生產環境實例,我們需要確保提供的腳本不會影響當前運行的服務,并且使用全新的賬戶意味著我們將從一張白紙開始。
運維賬戶提供了跳轉和集中登錄服務,團隊中的每個人都有一個AWS IAM賬戶。
其它環境擁有一系列IAM角色來進行切換,這意味著只能有一個登錄點來管理賬戶,也只有一個地方限制訪問。
舉個例子來說,Alice也許可以訪問上圖中的所有三個環境,Bob只能訪問開發環境(哪怕他刪除了生產環境中的負載均衡器),但是他們都是通過同一個運維賬戶登錄的。
作為對于復雜的IAM設置限制訪問的替代,我們只是簡單地通過環境來鎖定用戶并且通過角色來分組,從界面上使用每一個賬戶就像切換當前的活躍角色一樣方便。
我們可以免費獲得真正意義上的隔離,無需額外配置,而不是擔心預生產環境沙盒會不安全或者會改動生產環境數據庫。
分享配置代碼帶來的額外好處就是我們的預生產環境事實上將會成為生產環境的一個鏡像,配置中僅有的區別只有實例大小和日期數目。
最后,我們也可以跨賬戶合并賬單。我們使用相同的發票來支付月付賬單,可以看到一個按照環境來分割的詳細分解后的費用。
Docker和ECS
一旦我們設置好了賬戶,接下來就是輪到如何設計服務真正運行了,為此,我們轉向了Docker和EC2容器服務(ECS)。
截止今天為止,我們大部分的服務都運行在Docker容器里,包括我們的API和數據流管道。容器每秒鐘都會接收數千次請求,每月處理500億條事件。
Docker的最大好處就是可以一定程度上授權團隊從無到有構建服務,我們不再有一組復雜的配置腳本或AMIs——我們只需交給生產集群一個鏡像然后運行即可。不會再有有狀態的實例了,我們可以保證預生產環境和生產環境運行的是一模一樣的代碼。
配置完我們的服務如何運行在容器里后,我們選擇了ECS作為調度器。
在一個較高的水平,ECS負責在生產環境中實際運行我們的容器。ECS關注服務的調度,即將服務放置在單獨的主機上運行,并且在當連接到ELB時確保零宕機重載服務。ECS甚至可以通過AZs調度提供更好的可用性,如果某個容器掛了,ECS會確保在集群中重新調度一個新的實例。
切換到ECS極大地簡化了運行服務的工作,從而不需要擔心Upstart工作或者配置實例。添加Dockerfile、設置任務定義以及將其和集群關聯都是非常容易的。
在我們的設置中,Docker鏡像通過CI(持續集成)來構建,然后再推送到Docker Hub。當一個服務啟動時,會從Dokcer Hub上拉取鏡像,接著ECS就可以跨機器調度了。
我們將服務集群按照它們的關注領域和負載profile(比如針對API、CDN、APP等的不同的集群)分組,擁有分離的集群意味著更好的可見性以及針對每一個集群都可以決定如何使用不同的實例類型(因為ECS沒有實例關聯的概念)。
每一個服務都有一個特定的任務定義,指出了運行在哪一個版本的容器里、運行多少實例以及選擇哪一個集群。
在運行過程中,服務通過ELB注冊它自身,使用健康檢查來確認容器是否準備好運行。我們在ELB中指向一個本地的Route53條目,這樣服務借助于DNS可以互相通信和簡單地引用。
設置非常的棒,因為我們不需要任何服務發現,本地的DNS就完成了所有的記賬工作。
ECS可以運行所有的服務,我們從ELB獲得了免費的CloudWatch監控指標,這比起在啟動階段就不得不通過一個中央認證授權中心注冊服務要簡單多了,并且最大的好處還是在于我們不需要親自處理服務狀態沖突了。