基於GitLab管理專案配置檔案
要什麼樣的配置服務
我們的專案以前都是由各開發人員自己寫一個config.js
或者app.conf
來管理專案的配置資訊,經常出現下面的問題,我需要一個配置服務來解決它
1. 杜絕犯低階錯誤
如果沒犯低階錯誤的話,一般也不會出現什麼問題,什麼是低階錯誤呢,就拿前端來說,以前引入配置一般這樣
if( env === 'dev' ){
configs = import('./dev.config')
}else if( env === 'test' ){
configs = import('./test.config')
}else{
configs = import('./prod.config' )
}
複製程式碼
本地為瞭解決測試環境的一個bug,本來env
應該為dev
的,結果為了用測試環境的配置,強制把env
設定為test
了,然後問題解決了,測試環境測試也沒問題了,釋出的時候就直接更新到正式環境了,結果就gg了,一查原來是env
忘記改了。
2. 不希望當前環境看到其它環境的配置值
接著杜絕犯低階錯誤
,上面那樣寫前端配置還會有個問題,就是前端按F12
可以看到我們測試伺服器的一些資訊,比如某個網頁的測試前端域名,這些資訊我其時是不想外面看到的
3. 配置冗餘問題
假如我有一個基礎服務A
,B
和C
模組依賴服務A
的域名,那麼就要在這兩個模組下各寫一個域名配置
export default {
A_Domain:"https://xxx.readboy.com"
}
複製程式碼
看起來是沒問題,其實這裡我覺得問題很多,假如我的A
模組域名更新了,我只知道B
模組依賴這個域名,通知了B
模組開發人員修改,然後更新了,舊域名移除了,那麼C
模組肯定會有問題,如果能由各開發人員維護自己的配置,依賴專案不需要設定,直接配置依賴,直接引用就好了
4. 建立配置和專案依賴
這個問題是基於配置冗餘問題
的,我希望我可以知道這個配置(A
模組域名)有哪些專案依賴了,如果這個配置更新了,我希望依賴的專案能自動拉取最新的配置並部署
總結
基於上面的問題,網上大概找了下相關的解決方案都不太滿意(自己能力太水^-^),
- 我不想要執行時動態拉取配置,總感覺這個可靠性不強(小廠,資源有限)
- 獲取配置要足夠簡單,不要再配環境,還要裝什麼客戶端,我希望一個
http
就能解決,來吧,搞起來
配置服務實現的基本功能
專案管理
新建一個專案,指定這個專案有哪些環境,一般固定prod
,env
,dev
配置管理
專案指定幾個環境,相應專案下的配置就有相應的環境,開發人員填入相應的值就行
這基本就完成了專案的配置儲存功能,怎麼用呢?這個時候就需要定義一個配置描述檔案(.config.yml
)了,基本內容就是你要哪些專案的哪個配置,由配置服務介面根據描述檔案自動返回配置值
執行效果(gif圖,有點大)
配置更新自動部署所有依賴的專案
format: json
itemFormat: 配置key處理規則,看下面
project:
name: readboyconfig
description: 專案描述
id: gitlab專案ID
configs:
projecta:
- configa
- configb
- configc
projectb:
- configb
- configc
- configd
branch: test
複製程式碼
format
表示輸出檔案格式,值如下:
js
(es6)
json
ini
yml
project
表示工程資訊,主要是相關配置更新後用於重啟服務用的
id
專案在gitlab中的ID
name
專案名稱
description
專案描述資訊
branch
表示要獲取的該分支下的配置
itemFormat
表示生成配置項的方式,取值如下
ignore
忽略專案名
prefix
專案名+配置名
dot
專案名+ .
+連線配置
tree
保持層級結構
project_without_prefix
當前專案不加字首,其它專案同 prefix
project_without_dot
當前專案不用.
連線,其它專案同 dot
project_without_tree
當前專案不保持層級結構,其它專案同 tree
configs
專案配置資訊,具體參考後臺資料
CI配置
所有配置資訊更新後,會觸發依賴專案的CI
並攜帶引數 config_trigger=gitlab_config_server
,如果某些階段在自動觸發的CI中不要執行,可以在CI
檔案中加上條件
only:
variables
- $config_trigger == 'gitlab_config_server'
複製程式碼
或
except:
variables
- $config_trigger == 'gitlab_config_server'
複製程式碼
yml格式說明結束
獲取配置
curl "${GITLAB_CONFIG_SERVER}" -fd "`cat .config.yml`" > src/config/index.js
複製程式碼
GITLAB_CONFIG_SERVER
這個介面要實現功能主要有:
- 根據環境來獲取描述檔案的值
- 建立相關配置和
gitlab
專案的依賴關係,當相關配置更新的時候,自己觸發相關專案的gitlab CI
,實現自動拉取最新配置,自動部署
配置管理後臺的配置值更新的時候,程式會檢測哪個環境的值變了,並獲取依賴該屬性的gitlab
專案資訊,利用gitlab
的API
,API連結,自動建立一個pipeline
來拉取配置,部署程式。至此,一個簡單的基於gitlab
的配置服務就完成了。
當然要完全把這個配置服務利用起來,還需要跟開發人員做要求,配置使用,依賴專案的配置絕對不要再寫一遍,不然配置變了,更新就會出問題。
一些CI配置
前端CI檔案
image: node:8.9.3
stages:
- build
- buildImage
- deploy
variables:
ALI_REGISTRY_HOST: ""
ALI_REGISTRY_IMAGE: ""
ALI_SERVICE_NAME: ""
PROD_NAMESPACE: ebag-prod
BETA_IMAGE: beta-$CI_COMMIT_SHA
BETA_LATEST: beta-latest
masterbuild:
stage: build
script:
- printf "\nenv:" >> .config.yml
- printf " prod" >> .config.yml
- curl "${CONFIG_SERVER}" -fd "`cat .config.yml`" > src/config/index.js
- npm install --no-optional
- npm run build
- node ./tools/generate.config.js
- qshell='./tools/qshell-linux-x64'
- chmod a+x "${qshell}"
- ${qshell} account "${QINIU_AK}" "${QINIU_SK}"
- ${qshell} qupload 8 ./qiniuconfig
only:
- master
artifacts:
expire_in: 1 week
paths:
- dist
imagebuild:
stage: buildImage
image: docker:latest
script:
- docker login -u $ALI_REGISTRY_USER -p $ALI_REGISTRY_PASSWORD $ALI_REGISTRY_HOST
- docker build -t $ALI_REGISTRY_IMAGE:$BETA_IMAGE -t $ALI_REGISTRY_IMAGE:$BETA_LATEST -f docker/Dockerfile .
- docker push $ALI_REGISTRY_IMAGE:$BETA_IMAGE
- docker push $ALI_REGISTRY_IMAGE:$BETA_LATEST
only:
- master
複製程式碼
前端Dockerfile
FROM nginx:latest
COPY dist /usr/share/nginx/html
COPY docker/default.conf /etc/nginx/conf.d
複製程式碼
服務端CI檔案
image: docker:git
stages:
- build
- deploy
variables:
ALI_REGISTRY_IMAGE: "..."
ALI_SERVICE_NAME: "readboyconfig"
build_test:
stage: build
script:
- echo `date "+%Y%m%d%H%M%S"` > ./datetime
- docker build --build-arg APP_ROOT=/go/src/$CI_PROJECT_NAME --build-arg EXPOSE_PORT=6381 --build-arg DREAM_ENV=test --build-arg TAG_NAME=${CI_COMMIT_SHA} --build-arg CONFIG_SERVER=${CONFIG_SERVER} -t ${ALI_REGISTRY_IMAGE}:latest -t ${ALI_REGISTRY_IMAGE}:${CI_COMMIT_SHA}_`cat ./datetime` -f docker/Dockerfile .
- docker login -u $ALI_REGISTRY_USER -p $ALI_REGISTRY_PASSWORD $ALI_REGISTRY_HOST
- docker push ${ALI_REGISTRY_IMAGE}:${CI_COMMIT_SHA}_`cat ./datetime`
- docker push ${ALI_REGISTRY_IMAGE}:latest
artifacts:
expire_in: 2 days
paths:
- datetime
only:
- test
deploy_test:
stage: deploy
variables:
IMAGE_NAME: ${ALI_REGISTRY_IMAGE}:${CI_COMMIT_SHA}
image: ...
before_script:
- mkdir -p ~/.kube
- echo "$TEST_KUBERNETES_CONFIG" > ~/.kube/config
- echo "$TEST_KUBERNETES_CA" > ~/.kube/ca.crt
script:
- kubectl -n ebag-test set image deployment/${ALI_SERVICE_NAME} ${ALI_SERVICE_NAME}=${IMAGE_NAME}_`cat ./datetime`
only:
- test
複製程式碼
服務端Dockerfile檔案(去掉一些非必要資訊)
FROM golang:1.9.2 AS gobuild
WORKDIR ${APP_ROOT}
RUN curl "${CONFIG_SERVER}" -fd "`cat .comfig.yml`" > ./conf/dev/app.conf
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main ./main.go
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o migrate ./migrate.go
FROM alpine:latest
ARG APP_ROOT
ARG DREAM_ENV
ARG EXPOSE_PORT
WORKDIR /app
EXPOSE ${EXPOSE_PORT}
USER root
RUN mkdir -p ./conf/dev && touch ./conf/dev/app.conf
RUN mkdir -p ./log
VOLUME ["/app/log"]
COPY --from=gobuild ${APP_ROOT}/migrate ./migrate
COPY --from=gobuild ${APP_ROOT}/main ./main
COPY --from=gobuild ${APP_ROOT}/conf/ ./conf/
COPY --from=gobuild ${APP_ROOT}/migration/ ./migration/
COPY --from=gobuild ${APP_ROOT}/docker/start.sh ./start.sh
COPY --from=gobuild ${APP_ROOT}/bin ./bin
ENTRYPOINT ["/app/start.sh"]
複製程式碼
注意
curl
一定要把-f
加上,不然你的CONFIG_SERVER
有bug的話,會部署一個空的配置檔案到伺服器上,出現服務不可用的問題,加上-f
,如果你的CONFIG_SERVER
有bug,返回500
了,CI
會中斷執行,不會把有問題的程式部署到伺服器
為什麼有要datetime
如果內容沒變,編譯出來的映象名稱是一樣的,重新部署pod
會失敗,所以為了保證每次編譯出來的映象名稱不一樣,用datetime
來記錄時間,保證部署成功