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. 參考文檔

help.aliyun.com/documen


推薦閱讀:

Docker Remote API 如何使用?
基於OSS搭建私有(跨區域)Docker鏡像倉庫
把docker鏡像當作桌面系統來用
Docker 重要更新: 原生支持多階段構建(multi-stage build)
Docker Remote API 開發(一)

TAG:eggjs | 阿里云 | Docker |