1. 程式人生 > 程式設計 >基於GitLab管理專案配置檔案

基於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. 配置冗餘問題

假如我有一個基礎服務ABC模組依賴服務A的域名,那麼就要在這兩個模組下各寫一個域名配置

export default {
    A_Domain:"https://xxx.readboy.com"
} 複製程式碼

看起來是沒問題,其實這裡我覺得問題很多,假如我的A模組域名更新了,我只知道B模組依賴這個域名,通知了B模組開發人員修改,然後更新了,舊域名移除了,那麼C模組肯定會有問題,如果能由各開發人員維護自己的配置,依賴專案不需要設定,直接配置依賴,直接引用就好了

4. 建立配置和專案依賴

這個問題是基於配置冗餘問題的,我希望我可以知道這個配置(A模組域名)有哪些專案依賴了,如果這個配置更新了,我希望依賴的專案能自動拉取最新的配置並部署

總結

基於上面的問題,網上大概找了下相關的解決方案都不太滿意(自己能力太水^-^),

  1. 我不想要執行時動態拉取配置,總感覺這個可靠性不強(小廠,資源有限)
  2. 獲取配置要足夠簡單,不要再配環境,還要裝什麼客戶端,我希望一個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這個介面要實現功能主要有:

  1. 根據環境來獲取描述檔案的值
  2. 建立相關配置和gitlab專案的依賴關係,當相關配置更新的時候,自己觸發相關專案的gitlab CI,實現自動拉取最新配置,自動部署

獲取配置

自動部署

配置管理後臺的配置值更新的時候,程式會檢測哪個環境的值變了,並獲取依賴該屬性的gitlab專案資訊,利用gitlabAPIAPI連結,自動建立一個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來記錄時間,保證部署成功

github地址

服務端

前端