数据可视化的开源方案: Superset vs Redash vs Metabase

人是视觉动物,要用数据把一个故事讲活,图表是必不可少的。如果你经常看到做数据分析同事,在SQL客户端里执行完查询,把结果复制/粘贴到Excel里再做成图表,那说明你的公司缺少一个可靠的数据可视化平台。数据可视化是Business Intelligence(简称BI)中的核心功能,有许多成熟的商用解决方案,如老牌的Tableau, Qilk,新生代的Looker,国内的FineBI等等。不过对于许多小公司来说,这些服务的License费用是一笔不小的开销,且有一种“杀鸡用牛刀”的感觉。那在开源软件如此发达的今天,在数据可视化方面,有什么靠谱的方案可以选择呢?今天给大家介绍三个比较知名的项目,分别是Superset, Redash和Metabase。前两个我都在产生环境中实际使用过,在本文中会重点介绍。Metabase我只是试玩了一下,但我觉得这是一个非常有想法的项目,所以也会和大家聊聊我对它的看法。

选择一个称手的工具,功能上能满足我的需求肯定是首要的。就先从功能需求讲起,我们的数据仓库用的是Amazon Redshift(如果你没听过Redshift,就把它看作是为大数据优化过的PostgreSQL),所以大部分的实际用例都是要将一个SQL查询的结果可视化。我们所需的图表类型也就是常用的那几种,包括折线图,柱形图,饼图等。有了图表之后,接下去就是把相关的图表排版,生成报表页面(Dashboard)。从数据安全性角度,我不希望每个员工都能自由访问所有的Dashboard,所以每个Dashboard需要设置不同的访问级别。另外,我会看重它是否有REST API,能否通过API来创建与管理报表,这部分我们放在以后的文章中再讲。

除了满足功能性需求,易用性与文档在评判一个工具时也是非常重要的。谁不想要一个简单好用,文档清晰的产品呢?

下面我们就从功能性、易用性与文档等方面,来看看这三个开源项目的实际表现吧

Superset

Superset Demo

Superset最初是由Airbnb的数据团队开源的,目前已进入Apache Incubator,算是明星级的开源项目。老实讲,我也是被Airbnb与Apache两块金字招牌吸引才入了坑。目前公司绝大部分报表都在Superset上,大大小小有50个Dashboard,包含了近900个图表。在使用Superset之前我们用的是Looker(很不错的商用BI工具,可惜太贵),一年半前把Looker上所有的Dashboard迁移到Superset上,整个过程也很顺利。用了一年多,虽然在不少小地方有些不满意,但总体来说Superset很好地满足了公司现阶段在数据可视化与业务报表方面的需求。

当你把一个数据库连接到Superset上以后,你定义你要用的每一张表。Superset里表的定义不但包括字段,还需要定义指标(Metric)。指标是对字段的某种统计结果,比如字段上值的求和、平均值、最大值、最小值等。是不是有点糊涂了?但请回想一下,BI工具通常是用来做商业分析的。假想一个电商数据库,虽然在数据表我们存储每笔订单的交易额,但在商业分析时上我们不关心单笔交易,我们关心的可能是一个时间段内的总交额,或是平均交易额。当你画交易月报表时,你不会把每笔交易画在图上,而是把每天的总交易额用一个柱形在图上表示。这就是为什么Superset要引入“指标”这个概念。

对于数据分析人员来说,由于在Superset上他们不是直接写SQL,而是通过选择指标(Metric), 分组条件(Group)和过滤条件(Filter)来画图表,所以在构建复杂查询时可能会有些不适应。另一个难题是Superset里的表不支持join,如果一个图表里的数据要从多个数据表里取,那只能通过建视图来实现。Superset在0.11版本之后加入SQL Lab功能,支持从SQL查询结果直接生成图表。可惜,由于这个功能与Superset的核心设计格格不入,所以实现得比较差,没什么实用价值。

客观地讲,Superset里引入自己的表与指标的概念,在逻辑上是合理的,在统一各种异型的数据源时也是必要的。但实际操作中仍会让人觉得有些麻烦,不够直接了当。

Superset在可视化方面做得很出色,不但是开源领域中的佼佼者,也把很多商用BI工具甩在身后。在0.20版本中支持的图表类型已经达到了36种,而且在选择图表类型时,你可以看到每一种图表的缩略图,下面这张截图大家可以感受一下
Superset Chart Types

Superset的另一个亮点是可以在多个时间维度上观察,因为商业分析中的很多问题都是与时间密切相关的。Superset有4种专门针对时间序列的图表,使用这些图表时,你需要指定一个字段为时间维度,之后就可以对时间维度做丰富的操作

  • 从不同时间粒度去查看你关心的指标(小时/日/周/月/季度/年)
  • 对时间序列做rolling average,比如看一个指标的7日平均线
  • 可以对时间序列做偏移,再做对比,比如把本周的销售业绩与上周同期放在一张图表中对比
  • 不在图表上显示指标的绝对值,而是显示它随着时间变化的增长速度

以上这些都是在数据分析中非常实用的功能。

说完优点,再说说Superset的槽点,最大的槽点是当图表与报表多了以后,管理不方便。这个问题其实很好解决,只要在图表和报表管理时,加上分组或是文件夹的概念就可以了,但至今未见类似的功能。现在公司900多个图表都在一个大列表下,虽然Superset支持Search, Filter或是Favorite,但查找起来还是太麻烦。

Superset的文档也比较糟糕,虽然在安装与快速入门方面提供了很完整的文档,但在具体功能的介绍方面文档严重缺失。就算有些功能有文档,文档的结构也很混乱,所以大部分功能只能自己去尝试,好在这个工具本身并不难用,自己去摸索各个功能也不太困难。

Redash

Redash Demo

如果说Superset是构建一个BI平台,那Redash目标就是更纯粹地做好数据查询结果的可视化。Redash支持很多种数据源,除了最常用的SQL数据库,也支持MongoDB, Elasticsearch, Google Spreadsheet甚至是一个JSON文件。Redash的官方文档里列出了它所支持的所有数据源

它不需要像Superset那样在创建图表前先定义表和指标,而是可以非常直观地将一个SQL查询的结果可视化,这使得它上手很简易。或者说Redash仅仅实现了Superset中SQL Lab的功能,但却把这个功能做到了极致。

Redash有两个非常实用的功能,Query Snippet与Query Parameters。

Query Snippet很好地解决了查询片段的复用问题。做数据报表时经常要用到十分复杂的SQL语句,这些语句是肯定有一些片段是可以在多个Query中复用的。在Redash中我们可以将这些片段定义成Snippet,之后方便地复用。

Query Parameters可以为查询添加可定制参数,让这个图表变得更灵活。比如一个App的日活指标,我可能有时要按iOS/Android切分,有时要按地域切分,或是按新老用户切分。在Superset的Dashboard上我要做三个表图。Redash里我可以把Query的groupby做为一个参数,这样就可以在一张图上搞定。用的时候,运营人员可以图表上方的一个下拉框里选择切分的方式,非常直观好用。

Parameterized Query

Redash的Dashboard可以通过命名来进行分组,Dashboard的名字可以有一个前缀并以冒号结尾,前缀相同的Dashboard就会自动被分为一组。例如“Growth: Daily”,“Growth: Weekly”这两个Dashboard都会被分到“Growth”组下。

相比Superset,Redash在文档方面做得更好,除了快速入门教程以外,每一个功能模块都有文档且条理清晰。

当然Redash也有自己的不足之处,它的可视化种类比Superset逊色不少(不过其实也够用了)。另外,由于它只是纯粹地把数据查询结果可视化,所以也没有Superset里那些对时间维度上的聚合与对比的操作。

Metabase

Metabase Demo

由于我并没有在生产环境下使用过Metabase,只在自己本本上试用过这个工具。所以我只能说一下对它的第一印象。

刚开始用的就觉得这个工具的界面好漂亮,明显是经过UI设计师仔细调校过的。相对的,Superset与Redash一看就是程序员充当设计师的产物。

用了一会儿之后,我觉得Metabase与Superset虽然都想要打造一个完整的BI平台,但在理念上是不同的。Metabase非常注重非技术人员(如产品经理、市场运营人员)在使用这个工具时的体验,让他们能自由地探索数据,回答自己的问题。而在Superset或是Redash里,非技术人员基本上只能看预先建好的Dashboard,不懂SQL或是数据库结构的他们,很难自己去摸索。我非常喜欢Metabase的理念,它更接近一款成熟的商业化产品。当然要把这个理念变为现实是很有挑战的,目前我不知道在面临复杂的真实业务环境中,Metabase是否有想像中那样美好。

另外值得一提的是,Metabase的文档也是三个项目中写得最好最完整的,内容非常丰富。

将来若是有机会,我很愿意更深入地去体验这个产品。

小结

本文简单地介绍了三个开源的数据可视化工具Superset, Redash和Metabase,三者各有所长,我觉得并不存在绝对的最强者。对于刚刚开始搭建BI平台的公司,我相信它们都可以满足大部分报表与业务分析的需求。

虽然Superset是我们公司现在主要使用的可视化工具,但我问过自己“如果现在让我重新选择,我会使用哪个开源项目?”我的答案是Redash,而原因主要不是功能层面,而是技术层面。这里正好可以引出我们下篇要聊的内容,从技术框架与源代码层面来比较一下这三个项目,以及我选择开源项目的一些通用原则,敬请期待!

docker国内镜像源

Docker中国区官方镜像:
https://registry.docker-cn.com
网易:
http://hub-mirror.c.163.com
ustc:
https://docker.mirrors.ustc.edu.cn
中国科技大学:
https://docker.mirrors.ustc.edu.cn
阿里云:
https://cr.console.aliyun.com/

配置docker

为加快拉取镜像速度,建议设置docker国内镜像源
# 创建或修改 /etc/docker/daemon.json 文件,修改为如下形式
{
“registry-mirrors” : [
“https://registry.docker-cn.com”,
“https://docker.mirrors.ustc.edu.cn”,
“http://hub-mirror.c.163.com”,
“https://cr.console.aliyun.com/”
]
}
# 重启docker服务使配置生效
$ systemctl restart docker.service
查看镜像
docker info|grep Mirrors -A 10

JS中ES5和ES6中set和get方法

ES5
情况一:对象已经创建,需要给上面添加setget方法
/*
* 如果使用defineProperty定义setget,默认configurable: false,enumerable: false
* */
var obj={_a:0};
Object.defineProperty(obj,”a”,{
configurable:false,
enumerable:false,
set:function (value) {
this._a=value;
},
get:function () {
return this._a;
}
});
情况二:当创建对象时,使用这种写法
/*
* 当使用对象设置setget时,configurable: true,enumerable: true
* */
var obj1={
_a:0,
set a(value){
this._a=value;
},
get a(){
return this._a;
}
};
console.log(Object.getOwnPropertyDescriptor(obj1,”a”));
ES6
class Box{
constructor(){
this._data=null;
}
set data(value){
this._data=value;
}
get data(){
return this._data;
}
}
getset方法的使用:
get、set方法的使用在JavaScript中并不是像java、C、C++中的XXX.get(),XXX.set(),一样使用,而是通过以下:
/*
* 这就相当于执行了set a(value)这个访问器
* 等号后面值就是访问器中参数value
* */
obj1.a=4;

/*
* 获取obj1.a的值,执行get a()这个访问器
* 如果在等号右边,或者打印的函数中等。
* 因为get a()访问器里有return,就会返回一个值
* */
var sum=obj1.a+5;
console.log(obj1.a);

勒索软件产业化的感想

1.最近,我看到一条新闻。

美国最大旅行社之一的 CWT 公司,内网多达30000台电脑感染了 Ragnar Locker 病毒, 许多文件被加密,无法打开。攻击者要求支付赎金1000万美元。

双方在一个公开的聊天室里面讨价还价,被人看到了,这件事情才暴露。CWT 最终答应支付450万美元(下图)。

2、

我的印象中,美国最近发生了很多这类事件,勒索软件已经成了一个产业。

根据报道,单单是一个叫做 Netwalker 的勒索软件就在过去五个月里面,收入超过2500万美元。它甚至在俄罗斯的论坛里面招募”业务人员”(下图),帮它散布病毒,好处是可以提成60%~70%的赎金。

为了证明自己的”实力”,他们还贴出了比特币账单,都是受害者支付的赎金。最小一笔进账,也有近70万美元。

这个组织还不是最厉害的,另一个勒索软件 GandCrab 声称,赎金收入超过20亿美元。

3、

为什么这种事件现在变多了?

我认为原因就是比特币。这么大金额的支付,通过传统的银行转账,一定会发现谁是收款人。但是,通过比特币,根本查不出来。

由于可以安全地收到赎金,在丰厚利润的刺激下,这类勒索事件以后可能会层出不穷。大公司和大组织将成为攻击的首选目标,因为它们付得起高额赎金。

4、

如果勒索软件成为一个产业,这就意味着,计算机安全会成为一个热门领域,企业将大幅增加软件安全方面的支出,网络安全工程师会变得很抢手,身价越来越高。

所以,安全技术现在值得投资,年轻程序员可以选择主攻这个方向。

老实说,作为一个黑客是一个不错的职业,既可以通过”黑帽子”赚钱(攻击其他系统),也能摇身一变,通过”白帽子”赚钱(提供安全咨询服务)。

5、

最后谈谈,普通企业或者个人使用者,应该怎么防范勒索软件?

(1)我觉得,最简单的措施就是,尽量少用 Windows 系统。Windows 下面真是防不胜防,改用苹果或者 Linux 系统,安全性会提高很多。

(2)数据多备份,关键数据尽量保存在云端。

(3)如果有条件的话,建议使用虚拟化技术。在沙箱环境打开应用软件,这样的话,即使遇到病毒,也不会感染底层系统,因此虚拟化技术也非常看好。

systemd 的前世后生

从 init 系统说起
Linux 操作系统的启动首先从 BIOS 开始,接下来进入 boot loader,由 bootloader 载入内核,进行内核初始化。内核初始化的最后一步就是启动 PID 为 1 的 init 进程。这个进程是系统的第一个进程。它负责产生其他所有的用户进程。init 进程以守护进程(也就是服务)的方式存在,是所有其他进程的祖先。init 进程非常独特,能够完成其他进程无法完成的任务。
init 系统能够定义、管理和控制 init 进程的行为。它负责组织和运行许多独立的或相关的初始化工作(因此被称为 init 系统),从而让计算机系统进入某种用户预定义的运行模式,比如命令行模式或图形界面模式 。
对于一个操作系统而言,仅仅将内核运行起来是毫无实际用途的,必须由 init 系统将操作系统初始化为可操作的状态。比如启动 shell 后,便有了人机交互,这样就可以让计算机执行一些程序完成有实际意义的任务。或者启动 X 图形系统以便提供更佳的人机界面,更加高效的完成任务。这里,字符界面的 shell 或者 X 系统都是一种预设的运行模式。

随着计算机系统软硬件的发展,init 系统也在不断的发展变化之中。大体上的演进路线为 sysvinit -> upstart -> systemd。虽然本文的目的是要介绍 systemd,但是笔者觉得如果能从历史发展的角度观察 init 系统的演进,将会帮助我们更好的理解、使用 systemd。

sysvinit
sysvinit 就是 System V 风格的 init 系统,顾名思义,它源于 System V 系列的 UNIX。最初的 linux 发行版几乎都是采用 sysvinit 作为 init 系统。sysvinit 用术语 runlevel 来定义 “预订的运行模式”。比如 runlevel 3 是命令行模式,runlevel 5 是图形界面模式,runlevel 0 是关机,runlevel 6 是重启。sysvinit 会按照下面的顺序按部就班的初始化系统:

激活 udev 和 selinux
设置定义在 /etc/sysctl.conf 中的内核参数
设置系统时钟
加载 keymaps
启用交换分区
设置主机名(hostname)
根分区检查和 remount
激活 RAID 和 LVM 设备
开启磁盘配额
检查并挂载所有文件系统
清除过期的 locks 和 PID 文件
最后找到指定 runlevel 下的脚本并执行,其实就是启动服务。
除了负责初始化系统,sysvinit 还要负责关闭系统,主要是在系统关闭是为了保证数据的一致性,需要小心地按照顺序进行任务的结束和清理工作。另外,sysvinit 还提供了很多管理和控制系统的命令,比如 halt、init、mesg、shutdown、reboot 等等。
sysvinit 的优点是概念简单。特别是服务(service)的配置,只需要把启动/停止服务的脚本链接接到合适的目录就可以了。
sysvinit 的另一个重要优点是确定的执行顺序,脚本严格按照顺序执行(sysvinit 靠脚本来初始化系统),一个执行完毕再执行下一个,这非常有益于错误排查。

同时,完全顺序执行任务也是 sysvinit 最致命的缺陷。如果 linux 系统只用于服务器系统,那么漫长的启动过程可能并不是什么问题,毕竟我们是不会经常重启服务器的。但是现在 linux 被越来越多的用在了桌面系统中,漫长的启动过程对桌面用户来说是不能接受的。除了启动慢,sysvinit 还有一些其它的缺陷,比如不能很好的处理即插即用的设备,对网络共享磁盘的挂载也存在一定的问题,于是 init 系统开始了它的进化之旅。

upstart
由于 sysvinit 系统的种种弊端,Ubuntu 的开发人员决定重新设计和开发一个全新的 init 系统,即 upstart 。upstart 是第一个被广泛应用的新一代 init 系统。

upstart 基于事件机制,比如 U 盘插入 USB 接口后,udev 得到内核通知,发现该设备,这就是一个新的事件。upstart 在感知到该事件之后触发相应的等待任务,比如处理 /etc/fstab 中存在的挂载点。采用这种事件驱动的模式,upstart 完美地解决了即插即用设备带来的新问题。采用事件驱动机制也带来了一些其它有益的变化,比如加快了系统启动时间。sysvinit 运行时是同步阻塞的。一个脚本运行的时候,后续脚本必须等待。这意味着所有的初始化步骤都是串行执行的,而实际上很多服务彼此并不相关,完全可以并行启动,从而减小系统的启动时间。

upstart 的特点
upstart 解决了之前提到的 sysvinit 的缺点。采用事件驱动模型的 upstart 可以:

更快地启动系统
当新硬件被发现时动态启动服务
硬件被拔除时动态停止服务
这些特点使得 upstart 可以很好地应用在桌面或者便携式系统中,处理这些系统中的动态硬件插拔特性。

主角 systemd 登场
systemd 是 linux 系统中最新的初始化系统(init),它主要的设计目标是克服 sysvinit 固有的缺点,提高系统的启动速度。systemd 和 ubuntu 的 upstart 是竞争对手,但是时至今日 ubuntu 也采用了 systemd,所以 systemd 在竞争中胜出,大有一统天下的趋势。其实,systemd 的很多概念都来源于苹果 Mac OS 操作系统上的 launchd。
systemd 的优点是功能强大,使用方便,缺点是体系庞大,非常复杂,下图展示了 systemd 的架构(此图来自互联网):
systemd 能够在与 upstart 的竞争中胜出自然有很多过人之处,接下来让我们介绍一些 systemd 的主要优点。
兼容性
systemd 提供了和 sysvinit 兼容的特性。系统中已经存在的服务和进程无需修改。这降低了系统向 systemd 迁移的成本,使得 systemd 替换现有初始化系统成为可能。
启动速度
systemd 提供了比 upstart 更激进的并行启动能力,采用了 socket / D-Bus activation 等技术启动服务。一个显而易见的结果就是:更快的启动速度。为了减少系统启动时间,systemd 的目标是:

尽可能启动更少的进程
尽可能将更多进程并行启动
同样地,upstart 也试图实现这两个目标。下图展示了 upstart 相对于 sysvinit 在并发启动这个方面的改进。
upstart 增加了系统启动的并行性,从而提高了系统启动速度。但是在 upstart 中,有依赖关系的服务还是必须先后启动。比如任务 A,B,(C,D)因为存在依赖关系,所以在这个局部,还是串行执行。

systemd 能够更进一步提高并发性,即便对于那些 upstart 认为存在相互依赖而必须串行的服务,比如 Avahi 和 D-Bus 也可以并发启动。从而实现如下图所示的并发启动过程(此图来自互联网):
在 systemd 中,所有的任务都同时并发执行,总的启动时间被进一步降低为 T1。可见 systemd 比 upstart 更进一步提高了并行启动能力,极大地加速了系统启动时间。

systemd 提供按需启动能力

当 sysvinit 系统初始化的时候,它会将所有可能用到的后台服务进程全部启动运行。并且系统必须等待所有的服务都启动就绪之后,才允许用户登录。这种做法有两个缺点:首先是启动时间过长,其次是系统资源浪费。

某些服务很可能在很长一段时间内,甚至整个服务器运行期间都没有被使用过。比如 CUPS,打印服务在多数服务器上很少被真正使用到。您可能没有想到,在很多服务器上 SSHD 也是很少被真正访问到的。花费在启动这些服务上的时间是不必要的;同样,花费在这些服务上的系统资源也是一种浪费。

systemd 可以提供按需启动的能力,只有在某个服务被真正请求的时候才启动它。当该服务结束,systemd 可以关闭它,等待下次需要时再次启动它。
这有点类似于以前系统中的 inetd,并且有很多文章介绍如何把过去 inetd 管理的服务迁移到 systemd。

采用 linux 的 cgroups 跟踪和管理进程的生命周期

systemd 利用了 Linux 内核的特性即 cgroups 来完成跟踪的任务。当停止服务时,通过查询 cgroups ,systemd 可以确保找到所有的相关进程,从而干净地停止服务。
cgroups 已经出现了很久,它主要用来实现系统资源配额管理。cgroups 提供了类似文件系统的接口,使用方便。当进程创建子进程时,子进程会继承父进程的 cgroups 。因此无论服务如何启动新的子进程,所有的这些相关进程都会属于同一个 cgroups ,systemd 只需要简单地遍历指定的 cgroups 即可正确地找到所有的相关进程,将它们一一停止即可。

启动挂载点和自动挂载的管理

传统的 linux 系统中,用户可以用 /etc/fstab 文件来维护固定的文件系统挂载点。这些挂载点在系统启动过程中被自动挂载,一旦启动过程结束,这些挂载点就会确保存在。这些挂载点都是对系统运行至关重要的文件系统,比如 HOME 目录。和 sysvinit 一样,Systemd 管理这些挂载点,以便能够在系统启动时自动挂载它们。systemd 还兼容 /etc/fstab 文件,您可以继续使用该文件管理挂载点。

有时候用户还需要动态挂载点,比如打算访问 DVD 或者 NFS 共享的内容时,才临时执行挂载以便访问其中的内容,而不访问光盘时该挂载点被取消(umount),以便节约资源。传统地,人们依赖 autofs 服务来实现这种功能。
systemd 内建了自动挂载服务,无需另外安装 autofs 服务,可以直接使用 systemd 提供的自动挂载管理能力来实现 autofs 的功能。

实现事务性依赖关系管理

系统启动过程是由很多的独立工作共同组成的,这些工作之间可能存在依赖关系,比如挂载一个 NFS 文件系统必须依赖网络能够正常工作。systemd 虽然能够最大限度地并发执行很多有依赖关系的工作,但是类似”挂载 NFS”和”启动网络”这样的工作还是存在天生的先后依赖关系,无法并发执行。对于这些任务,systemd 维护一个”事务一致性”的概念,保证所有相关的服务都可以正常启动而不会出现互相依赖,以至于死锁的情况。

日志服务

systemd 自带日志服务 journald,该日志服务的设计初衷是克服现有的 syslog 服务的缺点。比如:

syslog 不安全,消息的内容无法验证。每一个本地进程都可以声称自己是 Apache PID 4711,而 syslog 也就相信并保存到磁盘上。
数据没有严格的格式,非常随意。自动化的日志分析器需要分析人类语言字符串来识别消息。一方面此类分析困难低效;此外日志格式的变化会导致分析代码需要更新甚至重写。
systemd journal 用二进制格式保存所有日志信息,用户使用 journalctl 命令来查看日志信息。无需自己编写复杂脆弱的字符串分析处理程序。

systemd journal 的优点如下:
简单性:代码少,依赖少,抽象开销最小。
零维护:日志是除错和监控系统的核心功能,因此它自己不能再产生问题。举例说,自动管理磁盘空间,避免由于日志的不断产生而将磁盘空间耗尽。
移植性:日志文件应该在所有类型的 Linux 系统上可用,无论它使用的何种 CPU 或者字节序。
性能:添加和浏览日志非常快。
最小资源占用:日志数据文件需要较小。
统一化:各种不同的日志存储技术应该统一起来,将所有的可记录事件保存在同一个数据存储中。所以日志内容的全局上下文都会被保存并且可供日后查询。例如一条固件记录后通常会跟随一条内核记录,最终还会有一条用户态记录。重要的是当保存到硬盘上时这三者之间的关系不会丢失。syslog 将不同的信息保存到不同的文件中,分析的时候很难确定哪些条目是相关的。
扩展性:日志的适用范围很广,从嵌入式设备到超级计算机集群都可以满足需求。
安全性:日志文件是可以验证的,让无法检测的修改不再可能。

在了解了 systemd 的种种优势之后让我们开始认识它的一些基本概念。

unit(单元)
系统初始化需要做的事情非常多。需要启动后台服务,比如启动 ssh 服务;需要做配置工作,比如挂载文件系统。这个过程中的每一步都被 systemd 抽象为一个配置单元,即 unit。可以认为一个服务是一个配置单元,一个挂载点是一个配置单元,一个交换分区的配置是一个配置单元等等。systemd 将配置单元归纳为以下一些不同的类型。然而,systemd 正在快速发展,新功能不断增加。所以配置单元类型可能在不久的将来继续增加。下面是一些常见的 unit 类型:

service :代表一个后台服务进程,比如 MySQLd。这是最常用的一类。
socket :此类配置单元封装系统和互联网中的一个套接字 。当下,systemd 支持流式、数据报和连续包的 AF_INET、AF_INET6、AF_UNIX socket 。每一个套接字配置单元都有一个相应的服务配置单元 。相应的服务在第一个”连接”进入套接字时就会启动(例如:nscd.socket 在有新连接后便启动 nscd.service)。
device :此类配置单元封装一个存在于 Linux 设备树中的设备。每一个使用 udev 规则标记的设备都将会在 systemd 中作为一个设备配置单元出现。
mount :此类配置单元封装文件系统结构层次中的一个挂载点。Systemd 将对这个挂载点进行监控和管理。比如可以在启动时自动将其挂载;可以在某些条件下自动卸载。Systemd 会将 /etc/fstab 中的条目都转换为挂载点,并在开机时处理。
automount :此类配置单元封装系统结构层次中的一个自挂载点。每一个自挂载配置单元对应一个挂载配置单元 ,当该自动挂载点被访问时,systemd 执行挂载点中定义的挂载行为。
swap:和挂载配置单元类似,交换配置单元用来管理交换分区。用户可以用交换配置单元来定义系统中的交换分区,可以让这些交换分区在启动时被激活。
target :此类配置单元为其他配置单元进行逻辑分组。它们本身实际上并不做什么,只是引用其他配置单元而已。这样便可以对配置单元做一个统一的控制。这样就可以实现大家都已经非常熟悉的运行级别概念。比如想让系统进入图形化模式,需要运行许多服务和配置命令,这些操作都由一个个的配置单元表示,将所有这些配置单元组合为一个目标(target),就表示需要将这些配置单元全部执行一遍以便进入目标所代表的系统运行状态。 (例如:multi-user.target 相当于在传统使用 SysV 的系统中运行级别 5)
timer:定时器配置单元用来定时触发用户定义的操作,这类配置单元取代了 atd、crond 等传统的定时服务。
snapshot :与 target 配置单元相似,快照是一组配置单元。它保存了系统当前的运行状态。
path:文件系统中的一个文件或目录。
scope:用于 cgroups,表示从 systemd 外部创建的进程。
slice:用于 cgroups,表示一组按层级排列的单位。slice 并不包含进程,但会组建一个层级,并将 scope 和 service 都放置其中。

每个配置单元都有一个对应的配置文件,系统管理员的任务就是编写和维护这些不同的配置文件,比如一个 MySQL 服务对应一个 mysql.service 文件。这种配置文件的语法非常简单,用户不需要再编写和维护复杂的系统脚本了。

依赖关系
虽然 systemd 将大量的启动工作解除了依赖,使得它们可以并发启动。但还是存在有些任务,它们之间存在天生的依赖,不能用”套接字激活”(socket activation)、D-Bus activation 和 autofs 三大方法来解除依赖。比如:挂载必须等待挂载点在文件系统中被创建;挂载也必须等待相应的物理设备就绪。为了解决这类依赖问题,systemd 的配置单元之间可以彼此定义依赖关系。Systemd 用配置单元定义文件中的关键字来描述配置单元之间的依赖关系。比如:unit A 依赖 unit B,可以在 unit B 的定义中用”require A”来表示。这样 systemd 就会保证先启动 A 再启动 B。

systemd 事务
systemd 能保证事务完整性。Systemd 的事务概念和数据库中的有所不同,主要是为了保证多个依赖的配置单元之间没有环形引用。比如存在 unit A、B、C,假如它们的依赖关系如下(此图来自互联网):

存在循环依赖,那么 systemd 将无法启动任意一个服务。此时 systemd 将会尝试解决这个问题,因为配置单元之间的依赖关系有两种:required 是强依赖;want 则是弱依赖,systemd 将去掉 wants 关键字指定的依赖看看是否能打破循环。如果无法修复,systemd 会报错。systemd 能够自动检测和修复这类配置错误,从而极大地减轻了管理员的排错负担。

target 和运行级别
systemd 用目标(target)替代了运行级别的概念,提供了更大的灵活性,如您可以继承一个已有的目标,并添加其它服务,来创建自己的目标。下表列举了 systemd 中的 target 和 sysvinit 中常见的 runlevel 的对应关系:

sysvinit runlevel systemd target 描述
0 poweroff.target 关闭系统。
1,s,single rescue.target 单用户模式。
2,4 multi-user.target 用户定义/域特定运行级别。默认等同于 3。
3 multi-user.target 多用户,非图形化。用户可以通过多个控制台或网络登录。
5 graphical.target 多用户,图形化。通常为所有运行级别 3 的服务外加图形化登录。
6 reboot.target 重启。
emergency emergency.target 紧急 Shell。
总结
本文简要的介绍了 init 系统的发展历史,并概要的介绍了 systemd 的基本概念。由于相比其它的 init 系统优势巨大,所以 systemd 已经被各大 linux 版本接受,在 linux init 系统中一统天下。

ubuntu20.04 启用rc.local

ubuntu20.04 不再使用initd管理系统,改用systemd.

使用systemd设置开机启动
为了像以前一样,在/etc/rc.local中设置开机启动程序,需要以下几步:

1、实现原理
systemd 默认会读取 /etc/systemd/system 下的配置文件,该目录下的文件会链接 /lib/systemd/system/ 下的文件。一般系统安装完 /lib/systemd/system/ 下会有 rc-local.service 文件,即我们需要的配置文件。
target 用于指定 什么时候启动 我们自己自定的软件。
1)将 /lib/systemd/system/rc-local.service 链接到 /etc/systemd/system/ 目录下面来:
ln -fs /lib/systemd/system/rc-local.service /etc/systemd/system/rc-local.service

说明:
[Unit] 区块:启动顺序与依赖关系。
ConditionFileIsExecutable=/etc/rc.local
After=network.target
ConditionFileIsExecutable指定了执行的文件,

After 表示在 network.target 这个target后面进行执行。也就是网络启动完成之后,执行 /etc/rc.local 文件。

[Service] 区块:启动行为,如何启动,启动类型。
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
RemainAfterExit=yes
GuessMainPID=no

3) [Install] 区块,定义如何安装这个配置文件,即怎样做到开机启动。
[Install]
WantedBy=multi-user.target
Alias=rc-local.service

[Install] 块是我们自己编辑新增的

2、创建/etc/rc.local文件
touch /etc/rc.local
3、赋可执行权限
chmod 755 /etc/rc.local
4、编辑rc.local
添加需要开机启动的任务  
#!/bin/bash
echo “test rc ” > /var/test.log
# startup redis-server:
/usr/local/bin/redis-server /usr/redis/redis.conf
5、执行reboot重启系统
查看test.log  
6、总结
通过 /etc/systemd/system/rc-local.service 文件来达到启动时执行 /etc/rc.local 文件的目的。

github免用户名密码管理代码

github免用户名密码管理代码

一、问题描述

git管理代码需要输入用户名密码,为避免重复操作,可设置保存用户名和密码。第一次输入后数据保存,后续管理代码不需再次输入。

用户名和密码明文保存会有安全隐患,对安全要求高的不建议采用。

二、操作步骤

1、git安装完成后,打开git bash,配置git全局保存用户名密码,输入如下命令:git config –global credential.helper store

git config –global credential.helper store

该步骤会在windows用户主目录:C:\Users\用户名下生成一个.gitconfig文件,内容为:

[credential]
helper = store

2、然后在git bash上clone或者pull代码,要求输入用户名密码。输入完成,会在windows用户主目录:C:\Users\用户名下生成一个.git-credentials文件,内容为保存的用户名和密码,明文保存有安全隐患。

3、后续clone、pull或者push等操作都不需要再输入用户名密码。

不能出众,那就出局—从一个人到900人的超长马拉松,Odoo CEO的实战心路

星光不问赶路人,时光不负有心人

Odoo科普:自2004年成立以来,Odoo一直采用开源商业模式为核心运营,除了在企业版提供的30个核心应用程序之外,Odoo在全球拥有2万名活跃成员的社区, 提供了超过1.7万个应用程序,满足了广泛的业务需求,已经成为最大商业应用库。

公司规模从2004年的一个人,到2020年九百人的持久马拉松,一路荆棘,一路成长,这一份企业领军人物对于危机管理的实战心路,相信你会有所启发!Odoo作为中小企业信息化的开源解决方案提供者,希望能为中小企业提供借鉴,在危机中找到活下去的突破口。下文将以Fabien Pinckaers第一人称进行讲述。

不论是08年金融危机,还是2020年的疫情打击,我有一个深刻的感受:企业面对的最大威胁往往不是来自于外部,而来自于内部的商业模式的探索和业务持续优化。基于此,提供以下几点企业经营管理的建议和思考,聊以与各位共度艰难时期。

1.聚焦长期效益。

当情况变量变得混乱的时候,这个时候营收下降是正常的,需要把公司有限的精力转移到具有长期效益的重点领域,比如实施团队的项目实施技巧培训,公司内部更好的运营架构,产品调研上新和开发等。放眼长期效益,而不是在项目获客上纠结停留。

2.危中有机,把握背后的机遇,落子无悔

疫情当下,全球范围内启动在家办公,我们也梳理过,odoo的历史机会是什么?答案是请人请人再请人,企业的根基是人才,在特殊的慢下来的时期,集中子弹广纳贤才。两年之前,招聘方面我们经历过阵痛,很难请到合资格的R&D程序员岗位。危机期间,我们加码了程序员的招聘投入,推出了价值10w的程序员入职奖金(写完这篇,小编就要去学python了…酸了酸了),旨在吸引高标准的人才加入odoo, 实现公司规模扩大,满足市场产品迭代的需求,到明年,Odoo员工会达到1400人。

同时,建议大家不妨关注一下市场推广的机会。这个时候可以提高市场推广的预算,大的推广投资项目可以在这个时候下手。绝大多数人恰恰不会考虑市场推广,如果有好的方案和机会,不妨评估抄底,做好准备。

3.快没子弹的时候怎么办?

关于优先级
专注在核心任务上面,比如可以减少见客户。

关于裁员
到了不得不考虑裁员的地步,我建议频率要低,但是最好一次性裁到位。通知之前,要开诚布公地和员工沟通公司的处境和计划,避免糊弄员工。如果情况到了很糟糕的时候,作为管理者肯定是写在脸上的难看,员工是可以感受到的,透明化去沟通,低频裁员不会动摇军心,以维护多年来建立的信任。

关于减速
营收减速,新单和客户可以减少,长期受益的价值体系,绝对不可以忽视。每次危机,我们都会加大研发投入,短期很难见效,但正是长期坚持产品的打磨迭代,造就了今天Odoo的地位——世界最大的开源管理软件库。我们成长曲线也是波动的,高光时刻和低谷并存,过去13年来,每年的平均增长达到了71.47%。一定要把工作重心放在长期价值的建立上,提供的商业价值会不断强化公司的竞争力,成为强大的内生动力,推动企业步步为营,突出重围。企业经营是一场持久马拉松,眼光和战略足够长远,才能行稳致远。

4.活下去

每个高速成长的公司都经历过一地鸡毛的困难时期,在这一点上,没有例外。从现金流预警到破产,你有两年的时间。供应商的付款死线、员工薪酬都促使企业去找到增收的渠道,Odoo每次绝境都幸运地解决了问题,救公司于水火,回到正确的轨道上来。我解决问题的关键是从根本上解决问题,找到深层次问题所在,让业务进阶,撬动更多的打法和资源。所以企业主必须要深入了解业务和市场,找到疑难杂症的源头,对症下药。

每一次困难期的考验都会指引你走向成功。
成功和失败的公司区别在于,前者可以在情况变坏之前,活下来。

5.CEO之于企业

从模型可以看到,公司处于起步小规模的时候,CEO的角色是从上而下的,管理决策、为公司提供更多的指导、强化组织能力和公司文化的建立。公司规模扩大的时候,需要向自下而上的模式转换,下放更多的权利给员工,自下而上的创新和机制的建立,基层员工提供更多的内容。然而危机中,需要回到自上而下的角色模式,个体的创造力需要放到最低,需要强有力的领导力,集中力量攻坚。在此期间,专注是非常重要的,重要的话,要强调三次,敲黑板!

很多时候,优先级和业务重点经营者一定要有清楚的定位,必须清楚地知道你的产品方向在哪里,集中强将专攻少数项目。这个时候,放弃社交,放弃客户,放弃展会,集中在对公司业务优化和提高上,我很少参加商业展会,这是我一贯的原则。

在投资领域,有一个默契的原则,如果你在各种社交场合经常见到一个公司的创始人,那么这个公司不值得投。时间碎片化在无效的领域,这样的创始人,很难为公司带来核心价值,因为他本身不具价值。公司自然不会做出靓眼的成就,投资的真金白银,很有可能付之东流。

6.市场化产品

关于定价
定价是一个技术活,远比你想象的要复杂的多。产品价位的影响远超想象,一般而言,提价20%,你可能利润会增加40%-60%。在这方面,我是踩过坑的,公司规模很小的时候,总是倾向于低估自己服务的价值,日开发服务费设定为200欧,接下来的几年没日没夜地挣扎在为人民服务的苦线上。(此处CEO的苦笑省略一万字…)

关于商业模式
商业模式可以说是最关键的。我花了十年的时间,探索到正确的商业模式。我们有标准化的企业版和社区版,公司变得可以盈利,能够增加两倍的研发投入,进而持续地提供odoo的商业价值。我们和全球2576个优秀的伙伴合作,伙伴为客户提供技术维护服务。这是一个可持续的商业模式,客户在升级系统或遇到问题的时候,都可以得到迅速的反馈。对于任何规模的服务型的公司都是一样, 在销售或者运营服务方面做一些尝试和改变,实现规模升级。

关于差异化价值
不能出众 那就出局。商业模式的要义在于提供差异化价值,你要做的是提供不可替代的商业价值,而不是优化。Odoo能够迅速扩张的原因不是因为我们比Oracle或者SAP好,大厂比我们市场运营能力强大,在垂直领域和不同行业有很好的解决方案,然而我们品牌定位独到,性价比方面优胜,产品极致追求用户友好,具有很高的灵活性和定制化的可能性。像甲骨文这样的大厂,强大之处也正是弱点所在。Odoo差异性的价值定位,为我们开辟了一条独特的赛道。产品方面,在自己的优势领域,不断投入研发,同时加强对竞品的分析,确保开发的性能是更加优化的。头部巨头不断加强影响力的情况下,中小企业必须要致力跻身于少数的几个可以提供差异化价值的玩家,才能找到长久发展的原生引擎。

Gitolite权限配置

基本含义:

C 代表创建,仅在通配符版本库授权是使用,用于指定谁可以创建与通配符匹配的版本库
R RW RW+ R为只读,RW为读写权限,RW+代表除了拥有读写权限,还可以强制执行推送
RWC RW+C
RWD RW+D D代表允许删除和正则匹配的引用
RWCD RW+CD

传统模式的引用授权
传统模式的引用授权指的是在授权指令中只采用R、RW和RW+的传统授权关键字,而不包括后面介绍的扩展授权指令。传统的授权指令没有把分支的创建和分支删除权限细分,而是和写操作及强制推送操作混杂在一起。

1 @administrators = jiangxin admin
2 @dev = dev1 dev2 badboy
3 @test = test1 test2
4
5 repo test/repo1
6 RW+ = @administrators
7 RW master refs/heads/feature/ = @dev
8 R = @test

关于授权的说明:
– 第6行,对于版本库test/repo1,管理员组用户jiangxin和admin可以读写任意分支、强制推送,以及创建和删除引用。
– 第7行,用户组@dev除了对master和refs/heads/feature/开头的引用具有读写权限外,实际上可以读取所有引用。这是因为读取操作授权阶段无法获知引用。
– 第8行,用户组@test对版本库拥有只读授权。

扩展模式的引用授权
扩展模式的引用授权,指的是该版本库的授权指令出现了下列授权关键字中的一个或多个:RWC、RWD、RWCD、RW+C、RW+D、RW+CD,将分支的创建权限和删除权限从读写权限中分离出来,从而可对分支进行更为精细的权限控制。
– 非快进式推送必须拥有上述关键字中的+方可授权。
– 创建引用必须拥有上述关键字中的C方可授权。
– 删除引用必须拥有上述关键字中的D方可授权。
即引用的创建和删除使用了单独的授权关键字,和写权限和强制推送权限分开。

1 repo test/repo2
2 RW+C = @administrators
3 RW+ = @dev
4 RW = @test
5
6 repo test/repo3
7 RW+CD = @administrators
8 RW+C = @dev
9 RW = @test
通过上面的配置文件,对于版本

库test/repo2.git具有如下的授权:
第2行,用户组@administrators中的用户,具有创建和删除引用的权限,并且能强制推送。
其中创建引用来自授权关键字中的C,删除引用来自授权关键中的+,因为该版本库授权指令中没有出现D,因而删除应用授权沿用传统授权关键字。
第3行,用户组@dev中的用户,不能创建引用,但可以删除引用,并且可以强制推送。
因为第2行授权关键字中字符C的出现,使得创建引用采用扩展授权关键字,因而用户组@dev不具有创建引用的权限。
第4行,用户组@test中的用户,拥有读写权限,但是不能创建引用,不能删除引用,也不能强制推送。
通过上面的配置文件,对于版本库test/repo3.git具有如下的授权:
第7行,用户组@administrators中的用户,具有创建和删除引用的权限,并且能强制推送。
其中创建引用来自授权关键字中的C,删除引用来自授权关键中的D。 –
第8行,用户组@dev中的用户,可以创建引用,并能够强制推送,但不能删除引用。
因为第7行授权关键字中字符C和D的出现,使得创建和删除引用都采用扩展授权关键字,因而用户组@dev不具有删除引用的权限。
第9行,用户组@test中的用户,可以推送到任何引用,但是不能创建引用,不能删除引用,也不能强制推送。
对路径的写授权

在授权文件中,如果一个版本库的授权指令中的正则引用字段出现了以NAME/开头的引用,则表明该授权指令是针对路径进行的写授权,并且该版本库要进行基于路径的写授权判断。

1 repo foo
2 RW = @junior_devs @senior_devs
3
4 RW NAME/ = @senior_devs
5 – NAME/Makefile = @junior_devs
6 RW NAME/ = @junior_devs

关于授权的说明:
– 第2行,初级程序员@junior_devs和高级程序员@senior_devs可以对版本库foo进行读写操作。
– 第4行,设定高级程序员@senior_devs对所有文件(NAME/)进行写操作。
– 第5行和第6行,设定初级程序员@junior_devs对除了根目录的Makefile文件外的其他文件具有写权限。

开发时关于git分支控制的一些心得

开发时关于git分支控制的一些心得(master-hotfix-develop-release)

项目代码正在重构,原来为了个人开发方便,都是从master分支(或一个中间分支)上建立一个自己的分支用于开发,开发完毕后将自己的分支merge到master分支(或一个中间分支),结束自己的分支。

在大量的提交后,会导致一些问题:

并没有版本上的控制,看起来就像一个版本一直在开发一样,不存在一个稳定的版本分支(或者master分支落后很多)。
个人的分支没有得到很好的测试,合并到master分支上可能有一些隐藏bug,导致master分支一直都处于不稳定的状态,出现棘手的问题时无法回退到一个稳定版本。
每个人的开发进度不一样,在merge到master分支时导致很多冲突。
在排查错误时,由于merge参数不统一导致master分支上有超级多的commit,只能在master分支上一点一点的回退版本,执行起来很麻烦。
个人的分支上传到GitHub上,导致远程仓库的分支杂乱无章。
参考了一个成功的git分支模型,对现在的git分支管理进行一些调整,便于规范开发。以本人目前开发的项目为例,项目名是visom-server。

一共有五种分支:master、hotfix、develop、release、self分支。master分支是一个主分支,主要提供一个稳定的版本,时刻都是稳定的保证随时可以上线应用。develop分支是一个和master分支并行的分支,用于主开发的分支。hotfix分支是热修复分支,主要用于修复master分支出现的意料之外的bug,属于临时分支,命名以”hotfix-xxx”为准。release分支是发布版本分支,在develop开发完成后做一些版本信息的或细节调整的分支,也属于临时分支,命名以”release-xxx”为准,xxx表示版本号。self分支是个人开发用的分支,命名无特殊要求。
master、develop分支是核心分支,是并行的一直存在在远程仓库的分支,分别提供稳定版本和主开发的作用。下面说一下完整的开发流程。整个流程相对于上面的参考博客比较,关于版本号的自动创建、规范以及其他版本相关的内容还没有完全吃透,所以对这部分和不适合本人公司开发情况的会有些删减,大家可以多提供意见,谢谢。(本人是使用vscode开发的,有些命令可以用vscode功能代替,比如创建、切换分支等)
master、develop分支
首先基于master分支建立一个develop分支,并将develop分支push到远程仓库里,做为开发的核心分支。
>git checkout -b develop master
>git push

基于develop分支建立自己的分支,做一些开发改动,完成后merge到develop分支而不是master分支。develop分支上有每一次改动提交的记录,所以注意merge时的参数。
>git checkout -b zhengsy develop // 基于develop分支创建并切换到zhengsy分支(自己开发的分支)
>git commit -a -m ‘修复project.index传参bug’ //开发代码后,将所有改动储存到本地并加以描述(分多次提交)
>git checkout develop // 切换到develop分支
>git merge –no-ff zhengsy //将zhengsy分支毫无保留的融合到develop分支
>git branch -d zhengsy // 删除zhengsy分支
// 整个过程中自己的分支并没有推送到远程仓库只存在与自己的本地。
// 如果有问题需要协助请推送到远程仓库,最后完成开发任务时请删除远程分支。

hotfix分支
master分支遇到紧急修复的bug时,请使用hotfix分支。hotfix分支也不会出现在远程仓库,只是一个本地的临时分支,融合时请保留提交记录。具体流程如下:

>git checkout -b hotfix-indexbug master // 基于master分支建立一个hotfix-indexbug分支,用于紧急修复
>git commit -a -m ‘修复index传参bug’ // 修复后,将所有改动储存到本地并加以描述(因为master分支是稳定的出现bug,也改动不大,一次性提交即可)
>
>更新master分支!!!!
>git checkout master // 切换到master分支(版本是v1.0.0)
>git merge –no-ff hotfix-indexbug // 将hotfix-indexbug分支融合到master分支
>git tag -a v1.0.1 -m ‘修复index的bug’ // 在本地有打一个版本的标签。(作用不是很明显)
>
>更新develop分支!!!!
>git check develop // 切换到develop分支
>git merge –no-ff hotfix-indexbug // 将hotfix-indexbug分支融合到develop分支
>
>删除hotfix分支
>git branch -d hotfix-indexbug

release分支(暂时作用不明显)
develop分支完成了版本升级任务,即可以给master分支提供下一个稳定版本时,请使用release分支。可能因为develop分支已经完成了该版本开发任务,可以执行下一个开发版本任务了,但为了使开发人员和版本维护人员可以并行,即开发人员继续使用develop分支开发下一个版本,版本维护人员使用release分支撰写该版本的一些信息和应付一些意料不到的小问题,所以使用release分支作为中转。使用release分支和hotfix分支类似,就不再重复命令了,注意分支命名规范。

有一点需要特殊说明的是,release分支融合到master分支时,请使用–squash参数;release分支融合到develop分支时,请使用–no-ff参数。即:

>// 将release-xxx分支融合到master分支
>git checkout master
>git merge –squash release-xxx
>
>// 将release-xxx分支融合到develop分支
>git checkout develop
>git merge –no-ff release-xxx

以上就是本人的一点心得,仍有不完善的地方比如版本说明方面的说明等,感觉版本方面还是使用GitHub上那种的比较好,即完成一个版本就在GitHub上生成一个版本,自己的本地tag更多的是自己方便一些。欢迎大家多提意见。以后有更完善的方案再来补充。