Egg.js+Antd 擼個簡易版阿里雲CS控制台?
CI/CD 流程的自動部署部分
更新:2017.09.26 完善自動部署部分
1. 預覽
鏡像列表:
集群列表:
應用列表:
2. 數據表
2.1 clusters 集群
// {root}/app/model/cluster.jsnuse strict;nnmodule.exports = app => {n const { STRING, TEXT } = app.Sequelize;n const Cluster = app.model.define(cluster, {n name: STRING,n region: STRING,n masterUrl: STRING(1024),n ca: TEXT,n key: TEXT,n cert: TEXT,n });n return Cluster;n};n
2.2 images/image_tags 鏡像/鏡像版本
// {root}/app/model/image.jsnuse strict;nnmodule.exports = app => {n const { STRING } = app.Sequelize;n const Image = app.model.define(image, {n namespace: STRING(512),n region: STRING,n name: STRING,n repo_full_name: STRING(1024),n });n Image.associate = function() {n app.model.Image.hasMany(app.model.ImageTag, { as: tags, foreignKey: image_id });n };n return Image;n};nn// {root}/app/model/image_tags.jsnuse strict;nnmodule.exports = app => {n const { STRING, DATE } = app.Sequelize;n const Tag = app.model.define(image_tag, {n digest: STRING,n tag: STRING,n pushed_at: DATE,n }, {n createdAt: false,n updatedAt: false,n });n Tag.associate = function() {n app.model.ImageTag.belongsTo(app.model.Image, { as: tags, foreignKey: image_id });n };n return Tag;n};n
2.3 hooks webhook
use strict;nnmodule.exports = app => {n const { STRING } = app.Sequelize;n const Hook = app.model.define(hook, {n name: STRING,n callbackUrl: STRING(1024),n accessToken: STRING(1024),n });n return Hook;n};n
2.4 deploy/deploy_image/deploy_env 部署配置
// {root}/app/model/deploy.jsnuse strict;nnmodule.exports = app => {n const { TEXT, STRING } = app.Sequelize;n const Deploy = app.model.define(deploy, {n template: TEXT,n app: STRING,n });n Deploy.associate = function() {n app.model.Deploy.belongsTo(app.model.Cluster, { as: cluster, foreignKey: cluster_id });n app.model.Deploy.hasMany(app.model.DeployEnv, { as: env, foreignKey: deploy_id });n app.model.Deploy.hasMany(app.model.DeployImage, { as: image, foreignKey: deploy_id });n };n return Deploy;n};nn// {root}/app/model/deploy_env.jsnuse strict;nnmodule.exports = app => {n const { STRING } = app.Sequelize;n const Env = app.model.define(deploy_env, {n key: STRING,n value: STRING,n }, {n createdAt: false,n updatedAt: false,n });n Env.associate = function() {n app.model.DeployEnv.belongsTo(app.model.Deploy, { as: env, foreignKey: deploy_id });n };n return Env;n};nn// {root}/app/model/deploy_image.jsnuse strict;nnmodule.exports = app => {n const { STRING } = app.Sequelize;n const Image = app.model.define(deploy_image, {n key: STRING,n }, {n createdAt: false,n updatedAt: false,n });n Image.associate = function() {n app.model.DeployImage.belongsTo(app.model.Deploy, { as: image, foreignKey: deploy_id });n app.model.DeployImage.belongsTo(app.model.Image, { as: repo, foreignKey: image_id });n };n return Image;n};n
3. Controls/Services
3.1 CURD
- eggjs/egg-sequelize
3.2 Hooks
需要驗證 accessToken,callbackUrl 用於向後傳遞信息(比如發送釘釘通知)
// https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.karFPe&treeId=257&articleId=105735&docType=1nconst { data } = yield axios.post(callbackUrl,n {n msgtype: markdown,n markdown: {n title: #Image Pushed,n text: `#### 鏡像:${newImage.get(name)} nn> 版本:${newImageTag.get(tag)}n `,n },n });n
截圖:
3.3 Clusters 集群信息
// https://help.aliyun.com/document_detail/26065.html?spm=5176.doc26063.6.883.YpbfjInnconst axios = require(axios);nconst https = require(https);nntry {n const { ca, key, cert, masterUrl } = cluster;n const agent = new https.Agent({n ca,n key,n cert,n });n const { data } = yield axios.get(`${masterUrl}/projects/`, {n httpsAgent: agent,n });n this.ctx.body = data;n} catch (error) {n this.ctx.throw(500, #cluster: request info failed); n}n
3.4 Clusters 集群更新
這裡要單獨拿出來講的原因是,阿里雲CS的基礎單元:
- cluster 集群
- app 應用
- service 服務
- container 容器
但是更新集群的時候是什麼樣呢?Template:
version: 2nlabels:n aliyun.addon: truenservices:n logtail:n image: registry-internal.cn-hangzhou.aliyuncs.com/acs/ilogtail:0.11.1n volumes:n - /var/log/:/var/log/n - /acs/log/:/acs/log/n - /tmp/ilogtail_log/:/usr/local/ilogtail/log/n - /etc/localtime:/etc/localtimen labels:n aliyun.latest_image: truen aliyun.addon: truen aliyun.global: truen devices:n - /dev/mem:/dev/memn environment:n - log_region=cn_hangzhoun net: hostn restart: alwaysn cap_add:n - SYS_RAWIOn oom_kill_disable: truen logspout:n image: registry-internal.cn-hangzhou.aliyuncs.com/acs/logspout:0.1-1158379n labels:n aliyun.addon: truen aliyun.global: truen restart: alwaysn volumes:n - /acs/log/:/acs/log/n - /var/run/docker.sock:/tmp/docker.sockn environment:n - ROUTE_URIS=file:///acs/logn net: hostn oom_kill_disable: truen
參考文檔可知:
更新的必要參數是是 template (version 也是,文檔有誤),要自動更新,最簡單的方式是添加 environment 參數。但是一個 template 里可能有多個 service (docker compose service)。
為了區分,最好是分兩個表:
- deploy_env,用來寫一些特定參數
- deploy_image ,用來查詢最新的鏡像以及 tag。
一個真實環境的例子:
fe:n environment:n - NGINXLOCATION=$REGIONn ports:n - 9280:80n - 9243:443n image: registry-internal.us-west-1.aliyuncs.com/$WWW_FE_FULLNAMEn restart: alwaysn labels:n aliyun.scale: 3n aliyun.lb.port_80: http://www_fe:80n aliyun.lb.port_443: https://www_fe:443n aliyun.rolling_updates: truenbe:n environment:n - NGINXLOCATION=$REGIONn ports:n - 9380:80n - 9343:443n image: registry-internal.us-west-1.aliyuncs.com/$WWW_BE_FULLNAMEn restart: alwaysn labels:n aliyun.scale: 3n aliyun.lb.port_80: http://www_be:80n aliyun.lb.port_443: https://www_be:443n aliyun.rolling_updates: truen
- $REGION 是全局的環境變數
- $WWW_BE_FULLNAME / $WWW_FE_FULLNAME 用來查詢對應的鏡像以及版本
3.4 觸發部署
又是一個很頭痛的問題,不能隨便哪個鏡像更新了就去更新集群吧,所以和工頭討論了一下,最終選擇在hook上添加 deployId,在能觸發部署的鏡像倉庫 hook 上添加query參數:
http://{your_hook_url}?accessToken={accessToken}&deployId={deployId}n
4. UI (React + Mobx + Antd)
4.1 packages
- react
- react-helmet
- react-router-v4
- mobx
- mobx-persist
- mobx-decorators
- root-import
4.2 demo/source
數據是公司私有數據,就不放源碼了,和下面項目基本一樣。
- ImplementsIO/implements.io
5. 參考文檔
https://help.aliyun.com/document_detail/26072.html?spm=5176.product25972.6.890.70qDnV
推薦閱讀:
※Docker Remote API 如何使用?
※基於OSS搭建私有(跨區域)Docker鏡像倉庫
※把docker鏡像當作桌面系統來用
※Docker 重要更新: 原生支持多階段構建(multi-stage build)
※Docker Remote API 開發(一)