由于 Apollo 概念比較多,剛開始使用比較複雜,最好先過一遍概念再動手實踐嘗試使用。
1、背景
随着程式功能的日益複雜,程式的配置日益增多,各種功能的開關、參數的配置、伺服器的位址……對程式配置的期望值也越來越高,配置修改後實時生效,灰階釋出,分環境、分叢集管理配置,完善的權限、稽核機制……
在這樣的大環境下,傳統的通過配置檔案、資料庫等方式已經越來越無法滿足開發人員對配置管理的需求。是以 Apollo 配置中心應運而生!
2、簡介
Apollo(阿波羅)是攜程架構部門研發的開源配置管理中心,能夠集中化管理應用不同環境、不同叢集的配置,配置修改後能夠實時推送到應用端,并且具備規範的權限、流程治理等特性。
3、特點
部署簡單
灰階釋出
版本釋出管理
提供開放平台API
用戶端配置資訊監控
提供Java和.Net原生用戶端
配置修改實時生效(熱釋出)
權限管理、釋出稽核、操作審計
統一管理不同環境、不同叢集的配置
4、基礎模型
如下即是 Apollo 的基礎模型:
(1)、使用者在配置中心對配置進行修改并釋出
(2)、配置中心通知Apollo用戶端有配置更新
(3)、Apollo用戶端從配置中心拉取最新的配置、更新本地配置并通知到應用

5、Apollo 的四個次元
Apollo支援4個次元管理Key-Value格式的配置:
application (應用)
environment (環境)
cluster (叢集)
namespace (命名空間)
(1)、application
Apollo 用戶端在運作時需要知道目前應用是誰,進而可以根據不同的應用來擷取對應應用的配置。
每個應用都需要有唯一的身份辨別,可以在代碼中配置 app.id 參數來辨別目前應用,Apollo 會根據此指來辨識目前應用。
(2)、environment
在實際開發中,我們的應用經常要部署在不同的環境中,一般情況下分為開發、測試、生産等等不同環境,不同環境中的配置也是不同的,在 Apollo 中預設提供了四種環境:
FAT(Feature Acceptance Test):功能測試環境
UAT(User Acceptance Test):內建測試環境
DEV(Develop):開發環境
PRO(Produce):生産環境
在程式中如果想指定使用哪個環境,可以配置變量 env 的值為對應環境名稱即可。
(3)、cluster
一個應用下不同執行個體的分組,比如典型的可以按照資料中心分,把上海機房的應用執行個體分為一個叢集,把北京機房的應用執行個體分為另一個叢集。
對不同的叢集,同一個配置可以有不一樣的值,比如說上面所指的兩個北京、上海兩個機房設定兩個叢集,兩個叢集中都有 mysql 配置參數,其中參數中配置的位址是不一樣的。
(4)、namespace
一個應用中不同配置的分組,可以簡單地把 namespace 類比為不同的配置檔案,不同類型的配置存放在不同的檔案中,如資料庫配置檔案,RPC 配置檔案,應用自身的配置檔案等。
熟悉 SpringBoot 的都知道,SpringBoot 項目都有一個預設配置檔案 application.yml,如果還想用多個配置,可以建立多個配置檔案來存放不同的配置資訊,通過指定 spring.profiles.active 參數指定應用不同的配置檔案。這裡的 namespace 概念與其類似,将不同的配置放到不同的配置 namespace 中。
Namespace 分為兩種權限,分别為:
public(公共的): public權限的 Namespace,能被任何應用擷取。
private(私有的): 隻能被所屬的應用擷取到。一個應用嘗試擷取其它應用 private 的 Namespace,Apollo 會報 “404” 異常。
Namespace 分為三種類型,分别為:
私有類型: 私有類型的 Namespace 具有 private 權限。例如 application Namespace 為私有類型。
公共類型: 公共類型的 Namespace 具有 public 權限。公共類型的N amespace 相當于遊離于應用之外的配置,且通過 Namespace 的名稱去辨別公共 Namespace,是以公共的 Namespace 的名稱必須全局唯一。
關聯類型(繼承類型): 關聯類型又可稱為繼承類型,關聯類型具有 private 權限。關聯類型的 Namespace 繼承于公共類型的 Namespace,将裡面的配置全部繼承,并且可以用于覆寫公共 Namespace 的某些配置。
6、本地緩存
Apollo用戶端會把從服務端擷取到的配置在本地檔案系統緩存一份,用于在遇到服務不可用,或網絡不通的時候,依然能從本地恢複配置,不影響應用正常運作。
本地緩存路徑預設位于以下路徑,是以請確定/opt/data或C:\opt\data\目錄存在,且應用有讀寫權限。
Mac/Linux: /opt/data/{appId}/config-cache
Windows: C:\opt\data{appId}\config-cache
本地配置檔案會以下面的檔案名格式放置于本地緩存路徑下:
上圖簡要描述了Apollo用戶端的實作原理
用戶端和服務端保持了一個長連接配接,進而能第一時間獲得配置更新的推送。
用戶端還會定時從 Apollo 配置中心服務端拉取應用的最新配置。
這是一個 fallback 機制,為了防止推送機制失效導緻配置不更新
用戶端定時拉取會上報本地版本,是以一般情況下,對于定時拉取的操作,服務端都會傳回 304 - Not Modified
定時頻率預設為每 5 分鐘拉取一次,用戶端也可以通過在運作時指定 apollo.refreshInterval 來覆寫,機關為分鐘。
用戶端從 Apollo 配置中心服務端擷取到應用的最新配置後,會儲存在記憶體中。
用戶端會把從服務端擷取到的配置在本地檔案系統緩存一份 在遇到服務不可用,或網絡不通的時候,依然能從本地恢複配置。
應用程式從 Apollo 用戶端擷取最新的配置、訂閱配置更新通知。
配置更新推送實作
前面提到了 Apollo 用戶端和服務端保持了一個長連接配接,進而能第一時間獲得配置更新的推送。長連接配接實際上我們是通過 Http Long Polling 實作的,具體而言:
用戶端發起一個 Http 請求到服務端
服務端會保持住這個連接配接 60 秒
如果在 60 秒内有用戶端關心的配置變化,被保持住的用戶端請求會立即傳回,并告知用戶端有配置變化的 namespace 資訊,用戶端會據此拉取對應 namespace 的最新配置
如果在 60 秒内沒有用戶端關心的配置變化,那麼會傳回 Http 狀态碼 304 給用戶端
用戶端在收到服務端請求後會立即重新發起連接配接,回到第一步
考慮到會有數萬用戶端向服務端發起長連,在服務端我們使用了 async servlet(Spring DeferredResult) 來服務 Http Long Polling 請求。
8、總體設計
上圖簡要描述了Apollo的總體設計,我們可以從下往上看:
Config Service 提供配置的讀取、推送等功能,服務對象是 Apollo 用戶端
Admin Service 提供配置的修改、釋出等功能,服務對象是 Apollo Portal(管理界面)
Config Service 和 Admin Service 都是多執行個體、無狀态部署,是以需要将自己注冊到 Eureka 中并保持心跳
在 Eureka 之上我們架了一層 Meta Server 用于封裝Eureka的服務發現接口
Client 通過域名通路 Meta Server 擷取Config Service服務清單(IP+Port),而後直接通過 IP+Port 通路服務,同時在 Client 側會做 load balance 錯誤重試
Portal 通過域名通路 Meta Server 擷取 Admin Service 服務清單(IP+Port),而後直接通過 IP+Port 通路服務,同時在 Portal 側會做 load balance、錯誤重試
為了簡化部署,我們實際上會把 Config Service、Eureka 和 Meta Server 三個邏輯角色部署在同一個 JVM 程序中
9、可用性考慮
配置中心作為基礎服務,可用性要求非常高,下面的表格描述了不同場景下Apollo的可用性:
Apollo 配置中心建立項目與配置
接下來我們将建立一個 Apollo 的用戶端項目,引用 Apollo 來實作配置動态更新,不過在此之前我們需要提前進入 Apollo Portal 界面,在裡面提前建立一個項目并在其配置一個參數,友善後續用戶端引入該配置參數,測試是否能動态變化。
1、登入 Apollo
我這裡是部署到 Kubernetes 中,通過 NodePort 方式暴露出一個端口,打開這個位址登入 Apollo:
使用者名:apollo
密 碼:admin
2、修改與增加部門資料
在登入後建立項目時,選擇部門預設隻能選擇 Apollo 自帶的 測試部門1與測試部門2兩個選項。
開始這真讓人迷糊,原來 Apoloo 沒有修改或新增部門資訊的管理節目,隻能通過修改資料庫,來新增或者修改資料,這裡打開 Portal 對月的資料庫中的表 ApolloPortalDB 修改 key 為 organizations 的 value 的 json 資料,改成自己對于的部門資訊。
3、建立一個項目
修改完資料庫部門資訊後,重新登入 Apollo Portal,然後建立項目,這時候選擇部門可以看到已經變成我們自己修改後的部門資訊了,選擇我們自定義部門,然後設定應用 ID 為
apollo-test
,應用名為
apollo-demo
。
建立完成後進入配置管理界面:
4、建立一個配置參數
建立一個配置參數,友善後續 Apollo 用戶端項目引入該參數,進行動态配置測試。
1
2<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4 <modelVersion>4.0.0modelVersion>
5
6 <parent>
7 <groupId>org.springframework.bootgroupId>
8 <artifactId>spring-boot-starter-parentartifactId>
9 <version>2.1.8.RELEASEversion>
10 parent>
11
12 <groupId>club.mydlqgroupId>
13 <artifactId>apollo-demoartifactId>
14 <version>0.0.1version>
15 <name>apollo-demoname>
16 <description>Apollo Demodescription>
17
18 <properties>
19 <java.version>1.8java.version>
20 properties>
21
22 <dependencies>
23 <dependency>
24 <groupId>org.springframework.bootgroupId>
25 <artifactId>spring-boot-starter-webartifactId>
26 dependency>
27 <dependency>
28 <groupId>com.ctrip.framework.apollogroupId>
29 <artifactId>apollo-clientartifactId>
30 <version>1.4.0version>
31 dependency>
32 dependencies>
33
34 <build>
35 <plugins>
36 <plugin>
37 <groupId>org.springframework.bootgroupId>
38 <artifactId>spring-boot-maven-pluginartifactId>
39 plugin>
40 plugins>
41 build>
42
43project>
2、配置檔案添加參數
在 application.yml 配置檔案中添加下面參數,這裡簡單介紹下 Apollo 參數作用:
apollo.meta: Apollo 配置中心位址。
apollo.cluster: 指定使用某個叢集下的配置。
apollo.bootstrap.enabled: 是否開啟 Apollo。
apollo.bootstrap.namespaces : 指定使用哪個 Namespace 的配置,預設 application。
apollo.cacheDir=/opt/data/some-cache-dir: 為了防止配置中心無法連接配接等問題,Apollo 會自動将配置本地緩存一份。
apollo.autoUpdateInjectedSpringProperties: Spring應用通常會使用 Placeholder 來注入配置,如${someKey:someDefaultValue},冒号前面的是 key,冒号後面的是預設值。如果想關閉 placeholder 在運作時自動更新功能,可以設定為 false。
apollo.bootstrap.eagerLoad.enabled : 将 Apollo 加載提到初始化日志系統之前,如果設定為 false,那麼将列印出 Apollo 的日志資訊,但是由于列印 Apollo 日志資訊需要日志先啟動,啟動後無法對日志配置進行修改,是以 Apollo 不能管理應用的日志配置,如果設定為 true,那麼 Apollo 可以管理日志的配置,但是不能列印出 Apollo 的日志資訊。
3、建立測試 Controller 類
寫一個 Controller 類來輸出 test 變量的值,使用了 Spring 的 @Value 注解,用于讀取配置檔案中的變量的值,這裡來測試該值,項目啟動後讀取到的變量的值是設定在 application 配置檔案中的預設值,還是遠端 Apollo 中的值,如果是 Apollo 中配置的值,那麼再測試在 Apollo 配置中心中改變該變量的值後,這裡是否會産生變化。
5、JVM 啟動參數添加啟動參數
由于本人的 Apollo 是部署在 Kubernetes 環境中的,JVM 參數中必須添加兩個變量:
env: 應用使用 Apollo 哪個環境,例如設定為 DEV 就是指定使用開發環境,如果設定為 PRO 就是制定使用生産環境。
apollo.configService: 指定配置中心的位址,跳過 meta 的配置,在測試時指定 meta 位址無效果。如果 Apollo 是部署在 Kubernetes 中,則必須設定該參數為配置中心位址,如果 Apollo 不是在 Kubernetes 環境中,可以不設定此參數,隻設定 meta 參數即可。一般情況下,configService 和 meta 值一緻。
如果是在 Idea 中啟動,可以配置啟動參數,加上:
1-Dapollo.configService=http://192.168.2.11:30002 -Denv=DEV
(2)、兩個叢集都配置同樣的參數不同的值
在兩個叢集
beijing
與
shanghai
中,都統一配置參數
test
,并且設定不同的值。
(3)、示例項目 application.yml 修改叢集配置參數,并啟動項目觀察結果
指定叢集為 beijing:
可以看到用的是 shanghai 叢集的配置
3、不同命名空間下的配置
(1)、建立兩個命名空間
命名空間有兩種,一種是 public(公開),一種是 private 私有,公開命名空間所有項目都能讀取配置資訊,而私有的隻能 app.id 值屬于該應用的才能讀取配置。
這裡建立 dev-1 與 dev-2 兩個私有的命名空間,用于測試。
在兩個命名空間中,都統一配置參數
test
,并且設定不同的值,設定完後釋出。
(3)、示例項目 application.yml 修改命名空間配置參數,并啟動項目觀察結果
指定命名空間為 dev-1:
1......
1......
2
3apollo:
4 bootstrap:
5 namespaces: dev-1 #設定 dev-1 命名空間
6
7......
springboot-apollo.yaml
1apiVersion: v1
2kind: Service
3metadata:
4 name: springboot-apollo
5spec:
6 type: NodePort
7 ports:
8 - name: server
9 nodePort: 31080
10 port: 8080
11 targetPort: 8080
12 - name: management
13 nodePort: 31081
14 port: 8081
15 targetPort: 8081
16 selector:
17 app: springboot-apollo
18---
19apiVersion: apps/v1
20kind: Deployment
21metadata:
22 name: springboot-apollo
23 labels:
24 app: springboot-apollo
25spec:
26 replicas: 1
27 selector:
28 matchLabels:
29 app: springboot-apollo
30 template:
31 metadata:
32 name: springboot-apollo
33 labels:
34 app: springboot-apollo
35 spec:
36 restartPolicy: Always
37 containers:
38 - name: springboot-apollo
39 image: mydlqclub/springboot-apollo:0.0.1
40 imagePullPolicy: Always
41 ports:
42 - containerPort: 8080
43 name: server
44 env:
45 - name: JAVA_OPTS
46 value: "-Denv=DEV"
47 ##注意修改此處的 mydlqcloud 為你自己的 Namespace 名稱
48 - name: APP_OPTS
49 value: "
50 --app.id=apollo-demo
51 --apollo.bootstrap.enabled=true
52 --apollo.bootstrap.eagerLoad.enabled=false
53 --apollo.cacheDir=/opt/data/
54 --apollo.cluster=default
55 --apollo.bootstrap.namespaces=application
56 --apollo.autoUpdateInjectedSpringProperties=true
57 --apollo.meta=http://service-apollo-config-server-dev.mydlqcloud:8080
58 "
59 resources:
60 limits:
61 memory: 1000Mi
62 cpu: 1000m
63 requests:
64 memory: 500Mi
65 cpu: 500m
(2)、部署 SpringBoot 應用到 Kubernetes
-n:建立應用到指定的 Namespace 中。
1$ kubectl apply -f springboot-apollo.yaml -n mydlqcloud
3、測試部署的應用接口
上面的應用配置了 NodePort 端口,可以通過此端口通路 Kubernetes 叢集内的應用接口,本人 Kubernetes 叢集位址為 192.168.2.11 且 NodePort 端口為 31081,是以浏覽器通路位址
http://192.168.2.11:31081/test來測試接口,顯示:
1test的值為:123456
可以看到能通過 Apollo 擷取參數值,此文章到此結束。