一种基于日志服务CLI工具实现的多区域发布方案

简介: 一种基于日志服务CLI工具实现的多区域发布方案

项目背景

SLS 的 命令行工具 aliyunlog 功能非常丰富,能够实现对 logstore、project、shard、index 等资源的管理操作,支持了数十条操作命令。使用也非常简单,通过 python 安装命令行工具之后,配置 AK 和region  信息即可通过简单的命令行来进行操作。

在实际项目使用中却遇到了一些问题。

发布问题

项目中进行埋点后,需要在 SLS 控制台配置报表并进行查看。正常的发布顺序为:

日常logstore(开发调试)->预发logstore(测试验证)->生产logstore(用户使用)

所以每当报表或者索引有修改时,会先在日常环境进行配置和调试,通过后再同步到预发环境让测试同学验证,验证通过后再同步到线上生产环境。

如果每次同步都是通过手动配置的话会有2个问题:

1、效率非常低,预发同步1次,生产环境因为有n个区域,所以要同步 n 次,n+1次重复劳动。

2、容易出错,手动同步一个复制粘贴搞错了很可能会导致报表不可用。

脚本封装

因为需要同步的索引和报表都属于项目,所以一开始考虑使用 aliyunlog 的 copy_logstore 命令,但实际在执行过程中发现命令行既不报错,索引和报表也没有同步成功。所以干脆自己写个脚本来实现 SLS 相关资源的发布功能。

考虑到本人对 JavaScript 比较熟悉,再加上其优秀的并发能力,所以考虑用 Node.js 编写一个脚本来实现。

以终为始,首先我们要考虑最终希望实现的具体效果,那就是把一个环境的配置信息发布到另一个环境。所以只用支持两个参数,一个是待发布的环境,一个是源环境,像下面这样:

node xxx.js [from_logstore] [to_logstore]

实际的执行过程应该是从源环境中读取报表和索引,然后更新到目标环境。这些信息就需要通过 aliyunlog 来获取了,aliyunlog 所依赖的参数我们写到对应的配置文件中,格式类似下面这样:

{
"daily": {
"host": "hsot1",
"project": "project1",
"logstore": "store1",
"AccessKeyID": "LT******",
"AccessKeySecret": "zl******"  },
"pre": {
"host": "host2",
"project": "project2",
"logstore": "store2",
"AccessKeyID": "LT***",
"AccessKeySecret": "ny***"  },
"product": [
    {
"host": "host3",
"project": "project3",
"logstore": "store3",
"AccessKeyID": "LT***",
"AccessKeySecret": "vgU***"    },
    {
"host": "host4",
"project": "project4",
"logstore": "store4",
"AccessKeyID": "LT***",
"AccessKeySecret": "vg***"    },
......  ]
}

这里由于同步是单向的,不可能从生产环境同步到预发环境,同时生产环境有多个区域,所以 project 写成数组形式。


接下来考虑如何同步。

1 同步索引。获取源 logstore 的索引,然后同步到目标 logstore。

image.png

2 同步报表。报表相对索引略微复杂一些,因为一个 logstore 下会有多个报表,为了高效需要并发执行,同步之前也需要注意修改项目信息。

image.png

3 删除配置文件。为了保证对下次操作不造成影响,需要删除临时生成的配置文件


完整的代码如下:

constenv=require('./.env.sls.json');
const { execSync, exec } =require('child_process');
const { join } =require('path');
const { writeFileSync } =require('fs');
// eslint-disable-next-line no-unused-varsconst [_nodePath, _filePath, from, to] =process.argv;
constsource=env[from];
constdistList=Array.isArray(env[to]) ?env[to] : [env[to]];
(async () => {
// 创建临时目录execSync('mkdir -p ./conf');
awaitPromise.allSettled(updateIndex(source, distList));
console.log('step1 updatedIndex')
constids=list();
constdashboards=awaitdownload(ids);
console.log('step2 download dashboard')
try {
awaitPromise.allSettled(upload(dashboards));
  } catch(e) {
console.error(e);
  }
console.log('step3 update dashboard')
execSync('rm ./conf/*')
})();
functionupdateIndex(){
letcmd=`aliyunlog log get_index_config --project_name=${source.project}--logstore_name=${source.logstore}--access-id=${source.AccessKeyID}--access-key=${source.AccessKeySecret}--region-endpoint=${source.host}--format-output=json,no_escape`;
constindex=JSON.parse(execSync(cmd).toString());
writeFileSync(`./conf/index.json`, JSON.stringify(index, null, 2)); 
constfile=`file://${join(__dirname, `./conf/index.json`)}`;
returndistList.map(dist=>newPromise((resolve, reject) => {
cmd=`aliyunlog log create_index --project_name=${dist.project}--logstore_name=${dist.logstore}--access-id=${dist.AccessKeyID}--access-key=${dist.AccessKeySecret}--region-endpoint=${dist.host}--format-output=json,no_escape --index_detail=${file}`;
exec(cmd, error=> {
if(error) {
cmd=`aliyunlog log update_index --project_name=${dist.project}--logstore_name=${dist.logstore}--access-id=${dist.AccessKeyID}--access-key=${dist.AccessKeySecret}--region-endpoint=${dist.host}--format-output=json,no_escape --index_detail=${file}`;
returnexec(cmd, err=> {
if(err) {
reject(err);
console.log(err);
          }
console.log(`Updated index for host ${dist.host}`)
resolve();
        })
      }
console.log(`Created index for host ${dist.host}`)
resolve()
    })
  }))
}
functionlist() {
constcmd=`aliyunlog log list_dashboard --project=${source.project}--access-id=${source.AccessKeyID}--access-key=${source.AccessKeySecret}--region-endpoint=${source.host}--format-output=json,no_escape`;
const { dashboardItems } =JSON.parse(execSync(cmd).toString());
returndashboardItems    .filter((it) =>/^dashboard/.test(it.dashboardName))
    .map((it) =>it.dashboardName);
}
functiondownload(dashboards) {
returnPromise.all(
dashboards.map((it) => {
returnnewPromise((resolve, reject) => {
constcmd=`aliyunlog log get_dashboard --project=${source.project}\--entity=${it}--access-id=${source.AccessKeyID}--access-key=${source.AccessKeySecret}--region-endpoint=${source.host}--format-output=json,no_escape`;
exec(cmd, (error, stdout) => {
if (error) returnreject(error);
resolve(JSON.parse(stdout.toString()));
        });
      });
    }),
  );
}
functionupload(dashboards) {
returndashboards.map((it) => {
returnnewPromise((resolve, reject) => {
distList.forEach((dist) => {
constfilePath=`./conf/dashboard-${newDate().getTime()}.${Math.random().toString(36)}.json`try {
constvalue=replaceProjectName(it, dist.project);
writeFileSync(filePath, JSON.stringify(value, null, 2));
        } catch (e) {
console.error('writeFileSync:', e);
        }
constfile=`file://${join(__dirname, filePath)}`;
constcmd=`aliyunlog log create_dashboard --project=${dist.project}\--detail=${file}--access-id=${dist.AccessKeyID}--access-key=${dist.AccessKeySecret}--region-endpoint=${dist.host}`;
exec(cmd, (error, stdout) => {
if (error) {
constcmd2=`aliyunlog log update_dashboard --project=${dist.project}\--detail=${file}--access-id=${dist.AccessKeyID}--access-key=${dist.AccessKeySecret}--region-endpoint=${dist.host}`;
returnexec(cmd2, (err, out) => {
if (err) returnreject(err);
elseconsole.info(`Updated dashboards for host ${dist.host}.`);
resolve(out.toString());
            });
          } else {
console.info(`Created dashboards for host ${dist.host}.`);
          }
resolve(stdout.toString());
        });
      });
    });
  });
}
functionreplaceProjectName(obj, value) {
constKEY='project';
for (constpropinobj) {
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
if (prop===KEY) {
obj[prop] =value;
      } elseif (typeofobj[prop] ==='object') {
replaceProjectName(obj[prop], value);
      }
    }
  }
returnobj;
}


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
7月前
|
运维 安全 API
统一接入API赋能开发者:自动高效、灵活编排的云产品日志采集方案
随着企业对网络安全和数据安全防护水平要求的逐步提升,企业管理对企业生产运维过程中所产生的日志数据,在留存处理、权限隔离、跨境合规、数据汇总等方面提出了更高阶的需求。为了满足大客户及一些国际化客户安全合规、简单快速地接入日志、使用日志、操作日志,我们提出了一种新的解决方案——“云产品统一接入API”。统一接入API主要针对阿里云云产品日志类型,以API的方式提供企业或组织用户快速上手,编排灵活的日志采集方案。
|
2月前
|
Web App开发 监控 应用服务中间件
全新架构!日志服务 SLS 自研免登录方案发布
全新架构!日志服务 SLS 自研免登录方案发布
87447 7
|
6月前
|
存储 缓存 前端开发
两种异步日志方案的介绍
两种异步日志方案的介绍
87 0
|
3月前
|
消息中间件 设计模式 Java
spdlog中的异步日志方案
spdlog中的异步日志方案
241 2
|
8月前
|
存储 SQL 监控
一文解读如何落地企业级云上日志审计方案
近年来,随着云计算的广泛应用和企业上云的深度普及,许多企业或个人用户将各种日志托管在云上进行留存,从而进行查询、审计等操作。什么是日志审计?为什么要做日志审计?企业如何落地云上日志审计方案?带着这些问题出发,本文将以阿里云日志服务(SLS)旗下的日志审计服务为视角切入,带领读者从当今网络安全形势和法律法规要求出发,解读云计算背景下,如何构建、运行、实践一个标准的企业级云上审计方案,从而更好地保障企业或组织的云上资产、云上数据的安全,确保企业业务持续安全稳定地运行。
|
4月前
|
Java Linux
异步日志方案log4cpp
异步日志方案log4cpp
132 0
|
5月前
|
Linux
Linux日志自动清理方案
Linux日志自动清理方案
129 0
|
11月前
|
Java Spring
优雅处理 Spring Boot 日志文件:高效、可维护的日志管理方案详解
优雅处理 Spring Boot 日志文件:高效、可维护的日志管理方案详解
352 0
|
8月前
|
消息中间件 Kubernetes Kafka
一份接地气的Kubernetes日志方案
本文主要聊聊Kubernetes场景下收集微服务应用日志方案,相对来说更接地气,非常好落地。
|
9月前
|
Java 应用服务中间件 数据库连接
Springboot日志记录方案—官方原版
Springboot日志记录方案—官方原版
82 0

相关产品

  • 日志服务