.gitignore_global
这个事儿,经常用Git的开发者估计都有意识。不过在设定这个文件到底哪些需要被全局忽略的这件事儿上,可能因人而异的多一些。或者说都是通过自己使用的技术栈进行搜索哪些可以放在.gitignore_global
中。
设定全局.gitignore_global
有什么好处?
.gitignore
到每个项目中,造成管理上的不便。.gitignore
的时候误操作提交上去。当然,增加.gitignore_global
可能会发生由于该工程没有.gitignore
导致其他开发者在提交pr时没有设定.gitignore
从而导致一些问题的发生。这里建议第一在项目README.md
中说明之前本地全局设定的.gitignore_global
提醒其他开发者在提交时注意规避相关文件,第二推荐其他用户也设定.gitignore_global
,一劳永逸。
今天也是无意中发现了一个Gist上推荐了一个网站,https://www.toptal.com/developers/gitignore/网站可以根据所输入的技术栈,给你自动生成需要的.gitignore
。
当然,这个toptal我稍微看了下,感觉是一个remote work的hiring,这个小功能估计也是为了吸引开发者来他们网站浏览的一种手段了 :D 不过如果觉得上面有合适的remote岗位的话,说不定可以试一试。
这里我稍微演示一下通过该网站生成并部署到SourceTree的情况:
打开网站后,输入需要生成的技术栈并选中,可一次性输入多个(不过他这个没有数据库和日志相关的选项,如果有这类需求的需要自己去添加):
单击“创建”,就看到为我们生成好的.gitignore
配置文件啦~
在SourceTree(Mac)-偏好设置-Git中,选择红框中「全局忽略列表」的编辑按钮,即可打开本地.gitignore_global
文件,将刚才生成的内容复制粘贴进去保存即可。
这样,当你在SourceTree进行提交的时候,SourceTree会自动读取.gitignore_global
并忽略所有项目下相同忽略的内容,而无需每个项目单独配置.gitignore
啦。
当然,如果想通过命令行设定.gitignore_global
,也是可以的:
touch ~/.gitignore_global
在当前用户家目录下创建全局.gitignore_global
。git config --global core.excludesfile ~/.gitignore_global
,即可实现全局忽略。上海全城封控已经接近一个月了。不过我屋子里因为后来各种买和发的东西够多,以至于现在已经从担心没饭吃的阶段演进到人追着食物变质速度在跑着吃的阶段了……
最近在对监控告警系统的架构设计进行调研。今天中午做饭的时候,突然有个脑洞:为什么告警系统在展示时——不论是实时告警还是历史告警——不能在每条告警后面加一个按钮,叫「一键Google」呢(狗头)。
说实话,这个想法一方面是因为天天考虑监控告警系统设计一些要素,另一方面主要也是源于,这几天有个朋友遇到了几个运维的问题,晚上打电话来问解决方案。其实并不是说问题有多复杂,只不过因为运维人员的经验之中,没有遇到过这个问题,然后在看到一些无厘头的报错的时候,可能一时间不知道该从哪里下手。
这种情况也很正常,因为有的时候我们第一时间看到的报错,一般都是开发人员通过程序返回给前端的报错,而真正的报错是不会直接展示给用户的。这在2C的系统上还是尤为常见的,毕竟一方面非专业用户看不懂,如果直接将异常抛出给用户,用户会一头雾水;另一方面由于网络安全的目的,如果出现像黑帽子通过注入的方式故意引起系统异常从而通过错误返回来做针对性攻击,那么直接抛出异常其实就是白给。
不过,由于监控告警平台本身使用者都是专业用户,且都是内部自己人,因此监控告警平台的信息,理应会将系统资源、中间件、数据库或者容器编排相关的结构化非结构化日志信息上收集中,形成一个很庞大的数据体系,通过DevOps平台开发者的逻辑设计,展示给运维人员,使他们能够在第一时间看到告警及其详细异常信息。
那么,其实如果说在前端看到异常报错之后,根据时间点和系统对应到监控告警平台查看对应的告警信息,然后再做处理,就正常能解决事件了。这个逻辑很简单,也很直接。
然而,假如运维人员再看到告警信息时仍然无法用“运维人员经验”去解决的时候呢?
这时,运维人员一般会干两件事:
第一:打开搜索引擎,按抛出的异常来搜索看是否有人分享解决方案;
第二:询问有经验或者更资深的人员。
不过一般情况下,由于人性的使然,对于这两个方案,一般都是串行去操作的。因为毕竟自己能解决的,一般就不会麻烦人了嘛。
而我这个问题的提出,其实解决的痛点就是这个人性的弱点:可能你在搜索引擎搜索的时候,搜索的并不是真正的错误。
这个时候,如果监控告警平台增加了「一键Google」的功能,那么就是相当于平台告诉运维人员:错误就是这个,按图索骥去吧。
而这个功能的思路呢,最简单的方案就是,在点按按钮的时候,直接去搜索这条异常日志的信息。当然搜索源可以是Google也可以是内部知识库。
再进化一点就是通过NLP可以学习到该日志最主要的关键词。
再进化一点就是可以关联各系统找到根因日志的最主要关键词。
说到这可能有人就觉得好笑了:都拿来用NLP和根因分析了,为啥不直接上AIOps?
咋说呢,我一直有个观点是:AI在运维上的使用,目前还是只能在简单的自愈处理和告警辅助决策上能够做好,真正这个事件是否能够处理好,仍然还是需要人去最终定夺的。
因为运维不像其他领域AI的应用有容错性,像人脸识别有问题你再试一次就好了,特效换脸觉得场景不好你再拍一次就好了。但是运维本身这个工作,只有恢复正常这一种解决方案。也就是要近乎100%的故障恢复成功率。这种活如果给AI,尤其是NLP目前是几个机器学习领域发展较为缓慢的一个了,它的准确率一直也无法达到运维的容错程度。那运维这个活,可能是自己给自己挖坑了。
所以,在这个分析研判的思路上,我觉得倒不如将这种辅助决策,通过一个方便运维人员去查找问题的功能展示,更实在一点。
当然,我也并不完全对AIOps持否定态度,毕竟准确率都是通过算力和样本的积累所逐步达到的。或许真的有朝一日,AIOps的普遍准确度——不是特定问题的准确度——能够达到运维工作的容错程度。到那个时候,或许真的是对运维人员是一个特别幸福的日子。
也有可能,是一个要找其他新工作的日子。
]]>人嘛,有的时候摆脱一些道不同的事儿,也会开心得很。
今天清早一起来给朋友搜技术资料,结果突然发现在跟妹子吃饭的时候有个消息推送:Typora收费了。
目前公布的政策是$14.99一个授权,每个授权可以激活三台设备。同时在FAQ里,作者明确写出了支持支付宝的付款途径,以及对于该费用是订阅制还是买断制的说明。
下面这段挺有意思的其实,作者说明正常的版本升级无需付费。但有可能在主版本号升级时可能会需要重新付费,但仍要等待当时到来的政策声明。
这其实就给人一遐想空间了:版本买断付费制?但万一年内大版本就升级了呢?
当然,年内大版本不太现实,Typora这个编辑器我印象里已经用了至少四五年了,从我大学毕业前后就开始接触了它。五年期间的更新都是Beta版本,这个软件开发周期互联网公司直呼外行啊(狗头)。(腾讯游戏:你们都不懂,这才是内行)(狗头)
这款以“A truly minimal markdown editor”为宣传点的Markdown编辑器,以其跨平台、简洁易用、不失功能等特点,在我上手后就喜欢了,要知道当年其实还是MarkdownPad还在Windows流行的年代,但我一直觉得MarkdownPad过于笨重以及不知道为啥总感觉莫名其妙就卡顿一下,所以不是特别感冒。
当然Typora更让我看重一点的是他的跨平台,因为我手头基本上所有类型操作系统设备都会有(以Windows、macOS、Ubuntu为主)。虽然Markdown编辑器数不胜数,但其实选择一个能够全平台且操作都比较统一而且适合写作的编辑器,还是有点困难的。虽然VS Code、Sublime Text、Atom等全平台通用型编辑器对于Markdown支持性良好,但毕竟这几款是通用型编辑器,并没有对Markdown特性上做多少功夫。而Typora的出现,其实对于我来说填补了我的对于专用跨平台Markdown编辑器需求。
毕竟作者一直有说关于正式发版之后会采用付费制,而且事实如此发生,用户的反应也没有那么大——当然我个人觉得也有可能是用户群也比较少吧,毕竟Markdown编辑器实在是太多了——不过,我非常乐见优秀软件的开发者能够通过合理的收费补足一些开发的成本,其实开源共享是一个主流方向,但是小而美的软件的小规模付费我觉得也是可以接受的,比如我就买了MWeb、Notability、GoodNotes等软件(但GoodNotes是真滴贵)。
不过我个人目前还暂时不会购买Typora的正式版本授权,主要是:
最后,放一下Typora最后一个Beta版本的官方下载链接吧,转需:
Typora Beta 0.11.18:
不要问我Windows的下载链接为啥是1117,你问作者去(狗头)
希望Typora能够越来越好,不过也别搞出前段时间Notability买断改订阅制那种事件……说实话,Notability要不是用户一片骂声道歉改掉了方案,估计会对付费生态有很大影响……
]]>2009年,班里的英语老师为了与同学们能够在线学习英语,放置一些英语学习资料,以及有简单的论坛互动功能,我第一次使用了Google协作平台(即现在的Google Site),这个网站就叫做English Heaven(下图)。当时对于Google的服务就有所吸引了。再后来到了2010年中考刚毕业,建站经历有两年多的我第一次使用 Google Webmaster Tools (即现在的Google Search Console)以及Google Adsense是我与谷歌密切接触的事件。众所周知,前者是Google提供给站长为他们提供SEO服务的工具,后者则是Google为站长提供的广告营收服务。虽然因为种种,最后并没有薅到在网站上挂着的Google Adsense广告的佣金——好像有二十多刀吧——不过还是因为此对于Google有着非同一般的感情。
现在还存在的Google 协作平台网站“English Heaven”
而与GDG的故事,却姗姗来迟。时光流逝,转眼从高中走到了大学毕业,我也学习了自己一直感兴趣的计算机专业,也从事着相关的工作。期间呢,其实很多谷歌的服务我还是用着的。不过也是由于众所周知的原因,有些服务也逐步淡出了我们的生活,这虽有不舍,但仍然令人惋惜。不过在青岛读大学的一次机缘巧合,让我无意中了解到了GDG,才知道原来有GDG(Google Developer Group)这样的组织,将世界各地的开发者爱好者聚在一起,交流新技术,共享科技盛宴。
时为GDG Shanghai活动中心一隅
时为GDG Shanghai活动中心活动合影
毕业后,我来到了上海,也就顺其自然的参与进了一直心心念的GDG Shanghai了。说实话,在这里,有很多对技术感兴趣的大牛一起交流学习,所有人真的都是为了技术而走到了一起,这样的氛围真的很让人愉悦。这种氛围其实比起学习到了一种新的技术、新的编程思路和新的算法,更能让人欢欣鼓舞。
GDG Shanghai 与个推联合技术分享活动上,摄影师捕捉到我的提问瞬间
当然,GDG带给我的技能上的改变也是很多的,因为GDG的原因,我更多的了解了数据科学与CV相关的前沿发展,也更因为GDG得以有机会参加了第一次的Kaggle Days China的活动,这在我的职业生涯之中也是带给我了十足的帮助。
2019 Kaggle Days China (Beijing) 现场,最右侧的嘉宾为来自Google的Yufeng Guo,在YouTube的“AI Adventures”频道,专门讲解机器学习等相关知识
虽然因为疫情,GDG活动现在绝大部分都只能在线上举办,但我仍希望GDG能够越办越好~感谢GDG,感谢GDG Shanghai,感谢马妹妹等GDG Shanghai的组织者和志愿者们的辛勤付出~希望GDG能够越发的发展壮大,也希望GDG的每一位成员在未来的发展之中都能受到GDG的良好氛围的感染,让自己的价值得以放大。
]]>TensorFlow实训系列活动后合影,中间为主讲人董莉萍老师
今下午搜资料的时候,发现了一个知乎专栏的文章,文章标题为《谈谈 Golang, 以及我走的一些弯路》作者是Karminski-牙医,看上面的title是前掘金的技术总监。至于文章本身关于Golang的相关踩坑没有什么需要说的,毕竟个人经验之谈。不过这位大佬提到了一个关于Erlang和Golang的发展历史和Erlang现在的处境,倒是让我有了些许联想。
帖子是这么说的:
... 这是我第一次, 也是最后一次用 Erlang 给企业写应用.
是的, Erlang 输在了这里. Erlang 的发明者 Joe Armstrong 有一篇文章 solving-the-wrong-problem 开头第一句就说了这么一句话:We're right and the rest of the world is wrong. We (that is Erlang folks) are solving the right problem, the rest of the world (non Erlang people) are solving the wrong problem.
现在来看, 这句话简直太中二了, 大意就是, 错的不是我, 是世界.
Erlang 为什么没有在 CPU 主频无法继续提升, 而核心数猛增的这么好的生态下火起来. 这个问题其实大佬早就说过了. Erlang 也不是唯一一个倒下去的例子. Richard P. Gabriel ( Common Lisp的发明者之一 ) 在这篇文章中 The Rise of Worse is Better 很好地阐述了为什么 Lisp 会没人用, 这个道理同样适用于 Erlang 身上.
简单来讲就是, Erlang 太好了, 为了完美的解决问题导致设计的很难学很难使用. 曲高和寡. 而那些简单好用的垃圾, 才能流行起来.
很合理, 这个道理再简单不过了. 这也是为什么大家不去看书, 而是喜欢去听喜马拉雅听, 喜欢去看知乎, 喜欢去看掘金, 喜欢这些被咀嚼一遍的东西, 觉得学到了知识. 因为对大家来说, 看书太难了, 太痛苦了.
帖子是2019年发的,时隔2年被我翻出来仔细阅读了一下。Erlang的发展到现在,其实很像昆曲,阳春白雪固然雅,然而曲高和寡。然后我又转念一想,好像这个时代的发展,让我们逐渐失去了对于阳春白雪的耐性了。人们困在了算法里追求着速度与效率,与短视频绑定很少输入长期的学习思考,问题使用现用现查也不再追求系统学习与思考了。反正有的是大佬和神犇在带着我们前进。
当然,并不是说这种发展是错误的。当今世界与历史上的世界必然是不同的,信息爆炸已然成为时代的主旋律。各种xGC产生的内容都源源不断的在各个平台各个渠道产生与传播,个中真真假假也是层出不穷。人们去处理这些事物就已经身心俱疲,可能没有耐心去沉淀自己的一些东西也是能够理解的——毕竟精力都耗光了。也因此,能够真正的静下心来,耐心去研究一个好东西,这种人本身就在这个社会是弥足珍贵的珍稀动物了。
所以这种Erlang的现象,在各处都随处可见:刚才提到的昆曲、没有耐心阅读的名著、躺在B站收藏夹里的公开课、堆在CAJ和PDF阅读器里的精选论文、能解决万事但极少有人翻阅的开发文档……
是这些内容没有用吗?不是,只是因为,我们的精力,都被吸收的过于干净了……
另外也让我想起了朋友最近一直在鄙视的vtuber/vup,TA的意思是要表演就要好好的拿出职业素质,“套个皮算什么”。不过我个人觉得这个反而方便了主播或者从业者节约时间去开播了,我也观察了一段时间目前这个领域的情况,虽然我是觉得目前这个领域从业者大多数都是聊聊天,真通过这个设计好自己的演艺发展路线的也是极少的。
但直到今天突然看到了这篇文章,我觉得现在vtuber/vup的出现,也是有种Erlang的意思:原来的都太复杂了,虽然美,但是咱们都简单点,我套个你愿意看的外壳,展现自己的才艺或者陪你唠嗑。你也愿意看这个外壳一起应援,就够了。哪还有啥现场不现场的问题啊,拿出我自己能干的,不要再耗费更多的精力了,开心就好,开心就好。
]]>application.properties
或者application.xml
并可通过一些优先级设置,将远程配置中心的配置项,用我们本地的文件进行覆盖,以方便本地的开发和调试,并可以通过.gitignore
控制防止提交到远程仓库。在Django内,其实也有这样的方法能够做这个事儿,那就是使用local_settings.py
。在Django的.gitignore
模板文件里面,对于这个文件是默认存在的,也就会在Git提交时无须担心其提交到远程仓库。
其实这玩意儿不是那么难理解,但是我今天在网上搜索相关资料的时候,发现有现有的教程都是同一篇互相抄,抄的源头也被我找到了是Stack Overflow,而且代码是有问题的。这篇文章其实主要是修正下网上的错误,以正视听。
我的开发环境:
MacBook Pro (13-inch, 2020) / PyCharm 2021.1 / Python 3.9 / Django 3.2.5
首先,在你工程的settings.py
的结尾处添加如下代码:
try:
from .local_settings import *
except ImportError:
pass
就是这个代码,网上互抄的全部都不行无法执行,错在了
local_settings
前面的.
没了……
这个代码的意义就在于尝试读取是否有local_settings.py
这个文件,如果有的话,且在settings.py
读入有问题时则读入覆盖配置。
然后,在你的settings.py
的文件夹下新建local_settings.py
,然后放置需要覆盖的参数即可,例如我的工程放置的如下:
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
DEBUG = True
ALLOWED_HOSTS = ['*']
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'db',
'USER': 'user',
'PASSWORD': 'password',
'HOST': '127.0.0.1',
'PORT': '3306',
},
'lite': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
好了,就这么简单。
]]>难道是因为今天没啥概念可以拱火了?(狗头)
当然对于这个概念,其实好早就提出来了。摘要一下Wiki关于元宇宙(metaverse)的解释:
The Metaverse is a collective virtual shared space, created by the convergence of virtually enhanced physical reality and physically persistent virtual space, including the sum of all virtual worlds, augmented reality, and the Internet. The word "metaverse" is made up of the prefix "meta" (meaning beyond) and the stem "verse" (a back-formation from "universe"); the term is typically used to describe the concept of a future iteration of the internet, made up of persistent, shared, 3D virtual spaces linked into a perceived virtual universe.
翻译:Metaverse是一个集体虚拟共享空间,由虚拟增强的物理现实和物理持久性虚拟空间(包括所有虚拟世界,增强现实和Internet的总和)的融合创建。“ metaverse”一词由前缀“ meta”和词干“ verse”组成;该术语通常用于描述由链接到感知虚拟世界的持久,共享的3D虚拟空间组成的互联网未来迭代的概念。
这个概念来源于尼尔·斯蒂芬森1992 年的科幻小说《雪崩》中,近期由于这个概念在老扎(Mark Zuckerberg)的Facebook战略中处于突出的地位,而他也在各个公开场合对该概念进行了阐明。具体可以搜一搜他的发言新闻稿。我就不赘述了。
目前来看,元宇宙对于AR/VR的发展及其扩展应用都是有互相促进的作用的。其实正巧上个月的时候跟朋友有聊过这个话题,我和朋友当时的观点直接放图吧:
其实元宇宙这个,仍然是一个炒概念的活,当然有人炒概念就一定会有解释概念的人把这个概念做实。但是对于技术的人来说,这东西其实还是一些现有技术的融合罢了。只不过这个技术的融合,随着目前互联网基础设施(云计算、带宽、5G等)逐步普及而能够成为现实。
当然,对于上面两个图,我有两点还是想强调下的(狗头):
最后附上两个元宇宙的两个图:
总之,静待花开,这可能是开启了人类能够进入一个新的社交和娱乐的时代的大门了。
]]>其实翻书本来是想看看有没有写关于Python类的装饰符相关的东西,结果翻了下目录好像并没有。不过我发现了一个在Python世界一直没有关注过的东西:GUI。
其实对于我这个B/S死忠粉来说,对于C/S的应用几乎没有怎么编程经验,也就没怎么接触过GUI这块,无论是大学期间Java程序设计这门课讲GUI还是C# .net课上的GUI,相关的课程作业和实验设计我都是有点头疼的,emmmm 可能也跟我一直从事B/S相关事情有关系吧。
今天吸引来,主要是因为想了解下Python GUI比起Java和C#,有没有什么特点。
先说结论:Python自带GUI框架,能用;几行代码开个窗口,方便。果然人生苦短,我用Python。(狗头)
Python GUI框架主要有tkinter、wxPython、PyQt、PythonCard、Dabo等,而Python自带的是thinkter。简单的窗口启动代码如下:
from tkinter import *
Label(text="啥玩意").pack()
mainloop()
运行后如图:
咋说呢,三行代码起一个GUI,这是我第一个接触到的能干这个事儿的语言(当然时过境迁,现在估计有很多现代化语言都可以干这个事儿了,知道的大佬可以在评论区留言)。
再来点元素:
from tkinter import *
from tkinter.messagebox import showinfo
def reply():
showinfo(title="恋爱循环", message="按钮按下去啦!")
window = Tk()
button = Button(window, text="快来点击", command=reply)
button.pack()
Label(text="啥玩意").pack()
window.mainloop()
运行后如图:
好!很好!很有精神!不算空行才9行代码,把Java需要写十几行实现的功能给搞定了。
Python GUI的面向对象写法以及窗口嵌套:
类定义文件TestGUI.py
:
from tkinter import *
from tkinter.messagebox import showinfo
class TestGUI(Frame):
def __init__(self, parent=None):
Frame.__init__(self, parent)
button = Button(self, text='按下去', command=self.reply)
button.pack()
def reply(self):
showinfo(title="恋爱循环2", message="でも そんなんじゃ だーめ")
调用文件bindgui.py
:
from tkinter import *
from TestGUI import TestGUI
main = Tk()
Label(main, text=__name__).pack()
popup = Toplevel()
Label(popup, text="绑定").pack(side=LEFT)
TestGUI(popup).pack(side=RIGHT)
main.mainloop()
运行后如图:
好了,Hello World就写到这了,我也不做代码讲解了。想多说下自己对于C/S端当下发展的理解。
其实很长一段时间我一直觉得在移动互联网的蓬勃发展,B/S端将逐步取代C/S端,一方面是因为越来越多的人用手机而不是PC,另一方面由于Web技术的进步,Web在全平台适配上已经不是什么难事,因此当初我认为C/S终将会走向没落,而B/S终究会成为最为重要的程序开发方式。
当然直到现在,我觉得我的判断还是对了一半的:B/S端确实成为了最为重要的程序开发方式,即使是App开发,使用Native开发的也要少于用WebView的。但是我失算的一点是,C/S端并没有走向没落,相反人家还活得好好的。究其原因我认为当时的判断忽略了两点:一是物联网IoT相关领域的发展,二是传统行业对于GUI软件的需求。
众所周知,近几年IoT其实发展的还是可以的,尤其是工业互联网和智能家居领域的相关技术发展,而这些数字化系统必不可少的需要配置相关的软件去操控这些物理设备,需要在系统启动后就能直接打开软件并进入相关界面,这个时候使用B/S其实并不是特别合适,因为毕竟浏览器这种方式还需要访问网址之类的东西。当然随着目前的发展,有些操控版已经换成了基于Android开发的App了,这种的倒是可以解决这个问题。
另外就是传统行业一些需求,其实并不是说他们的业务不适合B/S开发,而是技术债积累过多,对于系统的重构成本可能不太合算,这样的话反而使用C/S端更容易一些。而且据我所知C#的.net窗口开发在画界面方面是可以直接拖拽的,所以C/S编程相关的职位其实要求不是那么高,而换了B/S之后所有架构都要重新评估,引入了技术复杂度,对于一个非互联网的传统企业来说,IT本身就属于成本部门或稳定部门而不是利润来源部门,必然唯恐避之不及。这种客观存在的情况也是C/S一直存在的必要之一,当然相比第一个的增量,这一个仅仅是一个存量,并不能阻挡大势。
这就让我想起当年互联网兴起的时候,有人宣称书籍将会消亡,而再往前电视出现之后,有人也宣称过广播会消亡。虽然我们都知道事物都是螺旋上升的,旧事物会消亡,但是这个真正的“旧”的道理,是站在对人是否有用、有利、能可持续发展的角度来看的。书籍和广播固然是传统的,但他仍然是人们获取知识、交流思想和传输知识的有益渠道,只要这个作用没有消失,他们也不会消失,即使他们从历史的角度来说,确实是“旧”的。
拿过这样的思想,再来看待当下科技互联网的发展,其实所有的主流语言、开发模式、软件工程方式方法,都是这样的道理,只不过是你方唱罢我登场。The wheel turns, nothing is ever new.
]]>不过,整体而言,作为云时代的应用来说,FaaS、Serverless、云函数等基于云本身演化出来的SaaS产品,更代表了下一代云应用以及CNCF所贯彻的云原生的理念。毕竟作为云计算从2009年开始由亚马逊衍生发展,一直以来包括我在内,很多人对云上应用都持有一种保守的目光看待:云的服务与传统IDC提供的包括虚拟化服务在内的各种服务有什么区别吗?
其实在很长一段时间,这个问题无论云计算厂商如何吹的天花乱坠,仍然都摆脱不了云服务器本身与IDC相关服务的同质化情况。直到近两年,由各大厂商加盟组织的CNCF,开始对这个问题有了明确的思考与回答:真正的云的应用,应该是从云而生的(即云原生Cloud Native)。
当然,云原生的得以实现,离不开IaaS和PaaS自云计算兴起以来各路大神和开源社区的卓越贡献,毕竟作为云原生的土壤,云基础设施的稳健与巩固是云原生能够推广的关键。这也是我国在“十四五”期间将新一代大数据中心的建设纳入“新基建”七大领域之一的原因之一。
作为一名技术从业人员的角度来看,云原生将逐步降低应用的开发周期,提高应用迭代效率,尤其是对于中小企业和大企业中作为辅助相关的业务开发来说,云原生的应用场景和费用支出都是比传统应用架构开发模式的性价比更高。而云原生相关的开发岗位,预计也将会逐步进入各非互联网行业招聘信息的视野主流,而不仅仅只在互联网行业发展。
而从非技术从业角度来看,云原生不仅降低了应用开发周期,更可以降低非技术从业人员参与业务开发的门槛,在去年参加的腾讯云云开发CTO交流日北京站的技术沙龙活动上,可以看到厂商都在这方面有做很多努力:云开发的应用可预留更多可配置项目,让非技术人员可以根据业务需求及时调整,从而增加业务的快速启动。
去年参加腾讯云云开发CTO交流日·北京站,对于Serverless和云开发的现状有了一定了解
不过这样的发展,对于一些只能CRUD相关业务系统的从业者来说不是什么好事,因为这些需求企业有了更具性价比的业务启动思路,可能开发只需要一个人,配合多个业务人员来做就可以,不需要太多开发相关岗位了。这在去年的CTO交流日活动上与其他与会者的交流中,是一个比较认同的点。
关于云原生时代的职业影响这个话题,可以回头有空再独立探讨。接下来解决一下实际问题:腾讯云Serverless Koa服务部署后日志打印Undefined的解决。
前两天有个需求,因为懒得搭建全套Web,因此瞄准了Serverless。不过我一直没怎么实际接触过Serverless部署,只能摸着石头过河。后来经过搜索和高人指点,最终选择了腾讯云Serverless的Node.js Koa的示例工程进行开发实现。当然Koa这个下一代Web Framework,我也是第一次使用,两眼一抹黑全部都是新的,都是要慢慢搞。
这其中就踩了一个坑:在传入post请求时,Demo工程打印的是undefined
。如下图:
究其原因,其实还是挺简单的:因为腾讯云云函数服务提供的koa并没有引入koa-bosyparser
这个组件,所以我们需要安装一下。
在这里也有个小坑:koa工程是在代码的src
目录下,所以需要先进该目录操作。另外在操作时需要先将文件夹node_module
、yarn.lock
、package-lock.json
全部删除。
后在头部加入该组件引用代码:
const bodyParser = require('koa-bodyparser')
之后修改app.use(router.allowedMethods()).use(router.routes())
这行代码为:
app.use(router.allowedMethods()).use(bodyParser()).use(router.routes())
最后打开终端(这玩意是Coding的Cloud Studio,也就是VS Code的Web开源版本,跟VS Code用法完全一致)执行命令:
yarn add koa-bodyparser
修改如图:
保存后,点击部署,部署完成后我们再测试一下:
大功告成。
最后给两张图,关于Serverless云函数创建Koa框架模板相关步骤:
创建第一步:
创建第二步:
创建后初始化代码(post方法nice是我添加的):
于是,需求比较明确了:
我第一时间想起了Github Actions这个工具,众所周知,这个工具自2019年内测到向公众开放后,众多从业者将自己的博客的编译工作从本地线下扔到了Github Actions上;一些从业者所写的前端小玩意儿,都可以很轻松的通过Github Actions将工程迅速部署,提高了效率以及节约了成本。甚至一些有后端的工程,只需要寻(bai)找(piao)一个后端存储数据的地儿,就可以低成本甚至零成本搭建个人主页,这简直是一个十分有效提高生产率的东西啊!
不过说起来,其实Github Actions与Jenkins的功能是一致的,都是一个CI/CD的DevOps生产力工具,只不过Github Actions以其简单易用一经推出就受到了十分的欢迎。
虽然一直有知道这个功能,但是苦于没什么场景可以使用,今天这个需求正好让我能够使用Github Actions,零成本的解决一下需求。
首先,准备一下更新RSS的主程序convertRSS.py
和requirements.txt
:
import requests
import os, sys
import argparse
# the main get content codes
def get_url(url):
# some server requests need headers, otherwise return empty
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36',
}
page = requests.get(url, headers = headers).content
# convert to str within utf-8 encoding
page = page.decode('utf-8')
return page
# the main convert codes
def convert_content(content):
content = content.replace("ZNing源创库","张宁网·源创库")
return content
if __name__ == '__main__':
# need system args input
arg_prsr = argparse.ArgumentParser()
arg_prsr.add_argument('--url', required=True, type=str, default="", help = 'Required. Which one RSS Xml do you want to convert.')
args = arg_prsr.parse_args(sys.argv[1:])
url = args.url
# place a flag to judge should write file to disk or not
flag = False
url_content = convert_content(get_url(url))
url_list = url.split("/")
if os.path.exists(url_list[-1]):
with open(url_list[-1], 'r', encoding='utf-8') as file:
if(file.read()!=url_content):
flag = True
else:
# if the file dose not exist, the flag should placed True to convert first time
flag = True
if flag:
# writing to disk codes
with open(url_list[-1], 'w', encoding='utf-8') as file:
file.write(url_content)
print("The RSS Xml had update, convert done.")
sys.exit(0)
else:
print("The RSS Xml have no change, convert abort.")
sys.exit(1)
这里解释下我的程序的逻辑:通过系统执行传参--url
将需要处理的RSS XML URL传值进入,后通过函数get_url()
完成源文件的获取并转换成字符串,紧接着通过convert_content()
函数处理替换源文件字符串。完成后紧接着判断本地是否有该文件,如果没有则直接创建,如果有则判断本地文件是否与处理替换后文件内容是否相同,如果不相同则覆盖修改本地文件完成更新,并以0
返回值正常退出,如果相同则无需更新,以1
返回值返回退出程序。
至于为什么设计这两个返回值,文末会讲到。
下面是该程序的requirements.txt
,显然得,不再赘述:
requests==2.25.1
之后Github Actions创建一个仓库,把这俩文件放进去。
另外是需要去Settings-Developer settings-Personal access tokens(链接:https://github.com/settings/tokens)里面生成一个密钥,将这个密钥添加到仓库设置的Actions secrets(链接:https://github.com/``{username}/``{reponame}/settings/secrets/actions),在Actions secrets的名字即是配置文件引入的变量名,这里我的仓库叫CONVERT_TOKEN
。
文件和密钥均备妥后,点击菜单标签Actions,后点击set up a workflow yourself
,进入创建自己的工作流。
工作流创建文件使用的是YAML语法,具体使用文档可以查阅:https://docs.github.com/cn/actions 这里不再赘述更多用法,直接贴出本示例项目创建的工作流文件convert-rss.yml
:
name: 'Convert RSS Actions'
on:
schedule:
- cron: '0,15,30,45 * * * *'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout from repo
uses: actions/checkout@main
with:
ref: main
- name: Install Python latest
uses: actions/setup-python@main
with:
python-version: '3.x'
architecture: 'x64'
- name: Install dependencies
run: |
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Doing fetch
run: python convertRSS.py --url https://zning.me/atom.xml
- name: Commit files
run: |
git config --local user.email "zhn038@gmail.com"
git config --local user.name "ZNing"
git add .
git commit -m "提交RSS XML更新 $(date "+%Y-%m-%d %H:%M:%S")"
- name: Push changes
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.CONVERT_TOKEN }}
branch: main
Github会在该分支仓库里创建.github/workflow
文件夹,并在里面创建工作流文件convert-rss.yml
,文件名没有任何要求,Github Actions会对该文件夹下所有.yml
进行解析与执行。
这里稍微解释一下我自己这个工作流文件所干的事情:
Convert RSS Actions
;requirements.txt
安装Python依赖;创建好后,就等着Github Actions自己按计划调起Actions工作即可,不过根据实际测试,由于这玩意儿全球从业者估计使用的都挺多的,所以实际执行时间并不会完全按照设定的时间走,一般会晚个8-12分钟。
仓库所有内容其实都可以本地创建,其中Actions的定义文件在本地创建
.github/workflow
文件夹后放置提交推送给Github也是可以的,只不过我这边为了演示方便,就不再在本地做操作了,直接在Github网页上直接将所有步骤都解决了。
这里解释一下刚才Python返回值的问题,因为我需要区分两种状态情况,而Github Actions是可以根据返回值判断该节点运行正常与否(详见:https://docs.github.com/cn/actions/creating-actions/setting-exit-codes-for-actions)只要是0就认为是正常,非0则是异常,且异常不会继续执行接下来的节点。
而我的需求其实就正好是,如果有更新则提交并推送仓库,如果没有则终止即可。因此上面的Python程序才会设置如此的返回值。
不过可能是我Python程序有点小问题,发现如果内容相同的时候,后台程序也会提交更新,但是这个情况出现的很随机,有的时候又比对相同1值退出,正常返回错误。目前不太清楚这是为什么,欢迎知道的大佬在评论区留言。
最后,本文示例程序正常开源,协议MIT,欢迎自取使用:
]]>本文就简单的介绍一下如何使用Gitee中转GitHub/GiLab仓库吧。
注意: 本操作仅限于从GitHub Clone仓库操作,不能Push。如果是Push受阻,您可能需要提高一定的上网技术才能解决该问题了。
首先,登陆gitee.com
,没有账号可以先注册,如果有开源中国账号的可以直接关联注册。
登陆后,在左上角+
处找到从GitHub/GitLab导入仓库
,单击进入。
在如下显示的页面,在Git仓库地址粘贴你需要clone的仓库地址,Gitee会自动去GitHub拉取信息,如果你需要拉取的仓库在Gitee已有同学拉取过且设置为开源,他还会提示你该仓库已被拉取的地址。图上我以拉取最近微服务消息队列新宠Apache Pulsar为例演示。
注意: 他人拉取的仓库不一定是最新的,有可能人家在半年前拉取过就没再更新过,你需要自己点进去看是否是你的需要。
如果没有需要调整或者输入的,点击导入
按钮即可。等待Gitee给你拉取完仓库即可。
拉取完毕,在你的主页里就有这个仓库啦~
Gitee仓库(左)和GitHub仓库(右)对比如上图。这样你在本地再clone你Gitee的仓库即可,例如我的这个仓库:
git clone git@gitee.com:zning/pulsar.git
仓库路径右侧有个刷新图标,如果你过段时间仓库更新,需要再次clone或者需要pull的话,可以点击该按钮先更新自己的Gitee仓库,后在本地pull即可。
注意: Gitee仓库对于更新操作是,删除仓库内容重新全量克隆,而不是执行
git pull
命令,所以谨记不要在这个仓库上提交任何分支和任何修改,以免丢失信息。
Gitee除了可以clone任意的GitHub仓库以外,也可以关联自己的GitHub账号,快速将自己GitHub仓库clone到Gitee。相当方便。
**PS:**最后声明,这篇文章早在一年前的7月就打算写了,奈何一直拖着没交付。[Doge] 当初是答应同事要写的,现在把这个事儿补上。[Doge]
]]>今晚文章交付了,看大佬考完试的交付物了。[Doge]
我报名了6月的这次考试,本来到点登录打印准考证是个再平常不过的事情,结果……
沃日?什么鬼?网站挂了?不过我发现一个很奇怪的地方,就是访问报错。ERR_UNSAFE_PORT
,从来没见过的报错。
于是我网上一查,发现Google Chrome其实早在2014年就维护了一个“默认非安全端口列表”以拦截通过浏览器发起的不安全请求。这个列表有点长,其实是一些高危服务所在端口,例如22和23。但是……查了下,一堆文章提供的端口列表并没有10080
端口啊。查了下Chrome源代码
https://src.chromium.org/viewvc/chrome/trunk/src/net/base/net_util.cc?view=markup#l68
上面好像也没有10080。是哪里出问题了吗?
后来我搜了下资料,原来Google Chrome刚刚在91版本更新了端口的Block List,将10080端口加入了。
究其原因,是Google Chrome社区开发者早在年初就讨论的关于防止10080端口在NAT Slipstreaming 2.0攻击中被滥用的问题。四月确认更新源码封禁,并在近期91版本实施。
比较尴尬的是,中国证券投资基金业协会基金从业资格考试报名网站就是用的10080,这就导致了文章一开头出现的错误。
临时的解决方案其实很简单,就是更换浏览器。虽然网上有可以直接强制打开不安全端口的方法,但是这增加了安全风险,如果不是专业人士十分不建议去跟风操作。目前低于91版本的Chrome,以及Safari和Edge还是可以使用该端口访问网站,所以影响还是可以解决的。
(看社区所反馈,Firefox已经跟Chrome同步封禁,因为没有具体再搜版本号因此没有写Firefox)
但是目前从开源社区讨论的情况来看,10080端口集体封禁是大势所趋,希望中国证券投资基金业协会早点更新下基金从业资格考试报名网站,修改下访问端口,以保证未来的考试工作正常进行。
]]>Could not resolve URL for hyperlinked relationship using view name "user-detail"
经过查询,由于我在app里的urls.py
里设定了namespace名称,因此在Django设置序列化的文件serializers.py
中,需要在serializers.HyperlinkedIdentityField
中指定view_name
的名称,例如:
class UserSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name="myapp:user-detail")
class Meta:
model = User
fields = ('url', 'username')
至此,该问题解决。
其实还是挺简单的,由于我的需求只需要在Django后台页面显示,所以只需要在Models那里处理一下就好了。由于我需要的字段比较多,因此在app目录下新建了一个文件cons.py
专门存储字段。
以下是模型文件models.py
:
# -*- coding: UTF-8 -*-
from django.db import models
from .cons import SALCATEGORIES
from .cons import YEAR
from .cons import MONTH
from .cons import ORG
# Create your models here.
class Salary(models.Model):
month = models.CharField('月',max_length=50, choices=MONTH)
year = models.CharField('年',max_length=10, choices=YEAR)
org = models.CharField('组织名',max_length=100, choices=ORG)
create_timestamp = models.DateTimeField('创建时间',auto_now_add=True)
change_timestamp = models.DateTimeField('修改时间',auto_now=True)
def __str__(self):
return dict(YEAR)[self.year]+dict(MONTH)[self.month]+'_'+dict(ORG)[self.org]
class Meta:
db_table = 'salary'
cons.py
文件引入的内容如下:
# -*- coding: UTF-8 -*-
SALCATEGORIES = (
('shubas','应发 - 基本工资'),
('shupos','应发 - 岗位工资'),
('shupls','应发 - 绩效工资'),
('shuotr','应发 - 其他'),
('wihuem','扣缴 - 失业保险'),
('wihmed','扣缴 - 医疗保险'),
('wihold','扣缴 - 养老保险'),
('wihgjj','扣缴 - 公积金'),
('wihbgj','扣缴 - 补充住房公积金'),
('wihotr','扣缴 - 其他'),
('relsal','实发 - 实发工资'),
('relotr','实发 - 其他'),
)
YEAR = (
('2020','2020年'),
('2021','2021年'),
('2022','2022年'),
('2023','2023年'),
('2024','2024年'),
('2025','2025年'),
('2026','2026年'),
('2027','2027年'),
('2028','2028年'),
)
MONTH = (
('01','01月'),
('02','02月'),
('03','03月'),
('04','04月'),
('05','05月'),
('06','06月'),
('07','07月'),
('08','08月'),
('09','09月'),
('10','10月'),
('11','11月'),
('12','12月'),
)
ORG = (
('abc','中国abc公司'),
('cde','上海cde公司'),
)
这个键值对,存储的是前面的键,显示的是后面的值
上面的这个形式,对于该变量的类型也是一个挺有趣的东西。众所周知( )
是个元组的初始化标志,正常来说元组是不能直接转换为字典的,但是上面这样的变量是可以正常转换的,请见如下测试代码和输出:
print(type(ORG))
for i in ORG:
print(i[0]+"_"+i[1])
print(type(i))
print(type(dict(ORG)))
print(dict(ORG))
for i in dict(ORG):
print(i)
print(type(i))
输出:
<class 'tuple'>
abc_中国abc公司
<class 'tuple'>
cde_上海cde公司
<class 'tuple'>
<class 'dict'>
{'abc': '中国abc公司', 'cde': '上海cde公司'}
abc
<class 'str'>
cde
<class 'str'>
回到实现上来。最终我需要的后台显示情况如下:
数据库表存储情况如下:
这里我们介绍一下Django里模型Field
的choices
字段:
一个 sequence 本身由正好两个项目的迭代项组成(例如
[(A,B),(A,B)...]
),作为该字段的选择。如果给定了选择,它们会被 模型验证 强制执行,默认的表单部件将是一个带有这些选择的选择框,而不是标准的文本字段。
每个元组中的第一个元素是要在模型上设置的实际值,第二个元素是人可读的名称。例如:
YEAR_IN_SCHOOL_CHOICES = [
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
('GR', 'Graduate'),
]
> 一般来说,最好在模型类内部定义选择,并为每个值定义一个合适的名称的常量:
> ```python
> from django.db import models
>
> class Student(models.Model):
FRESHMAN = 'FR'
SOPHOMORE = 'SO'
JUNIOR = 'JR'
SENIOR = 'SR'
GRADUATE = 'GR'
YEAR_IN_SCHOOL_CHOICES = [
(FRESHMAN, 'Freshman'),
(SOPHOMORE, 'Sophomore'),
(JUNIOR, 'Junior'),
(SENIOR, 'Senior'),
(GRADUATE, 'Graduate'),
]
year_in_school = models.CharField(
max_length=2,
choices=YEAR_IN_SCHOOL_CHOICES,
default=FRESHMAN,
)
>
def is_upperclass(self):
return self.year_in_school in {self.JUNIOR, self.SENIOR}
> ```
> 虽然你可以在模型类之外定义一个选择列表,然后引用它,但在模型类内定义选择和每个选择的名称,可以将所有这些信息保留在使用它的类中,并帮助引用这些选择(例如,`Student.SOPHOMORE` 将在导入 `Student` 模型的任何地方工作)。
> 你还可以将你的可用选择收集到可用于组织目的的命名组中:
> ```
MEDIA_CHOICES = [
('Audio', (
('vinyl', 'Vinyl'),
('cd', 'CD'),
)
),
('Video', (
('vhs', 'VHS Tape'),
('dvd', 'DVD'),
)
),
('unknown', 'Unknown'),
]
每个元组中的第一个元素是应用于该组的名称。第二个元素是一个二元元组的迭代,每个二元元组包含一个值和一个可读的选项名称。分组后的选项可与未分组的选项结合在一个单一的列表中(如本例中的
'unknown'
选项)。
对于每一个设置了
choice
的模型字段,Django 会添加一个方法来检索字段当前值的可读名称。参见数据库 API 文档中的get_FOO_display()
。
请注意,选择可以是任何序列对象——不一定是列表或元组。这让你可以动态地构造选择。但是如果你发现自己把
chips
魔改成动态的,你可能最好使用一个合适的的带有ForeignKey
的数据库表。chips
是用于静态数据的,如果有的话,不应该有太大的变化。
每当
choices
的顺序变动时将会创建新的迁移。
除非
blank=False
与default
一起设置在字段上,否则包含"---------"
的标签将与选择框一起呈现。要覆盖这种行为,可以在choices
中添加一个包含None
的元组,例如(None, 'Your String For Display')
。另外,你也可以在有意义的地方使用一个空字符串来代替None
——比如在CharField
。
最后,新设备镇楼压轴。这可是近几年手头设备最贵的一年了。
]]>关于离职,这是一个思前想后的结果,这是一个艰难的决定。
衷心感谢农行近三年来的培养和鼓励。感谢数据中心各位领导、同事的支持,共同建设运维平台提高生产运维工作效率与质量。感谢2020年在人行数研所借调期间各行同事、各厂同学的支持、协助,在疫情这个特殊时段齐心保障首次数字人民币消费券试点工作顺利完成。更感谢一起码代码、查BUG、做变更的各位大佬们,此生有幸、共愿坦途。
最近半年多一直没有更新过网站了,一方面是近期各种资格考试和培训把业余时间占满了,没来得及总结近期技术积累;另一方面也在考虑行业发展规划方面的事情。
今天这篇,除了告辞,也想简单说一下个人对于目前IT运维,主要以应用与系统层的领域的愚见。
当下IT运维发展,在容器、微服务的大规模使用和传统裸机、虚拟化存量维护的现状下,逐步在技术形态上两极分化:一个方向上,以科技公司、互联网公司和开源团队为主要推动力的前沿技术,将容器与微服务进行规模化、网格化,继而提出了新的运维需求——服务自治、故障自愈、智能监控,因此逐步形成了云原生、Serverless、FaaS、IoC、无代码化等新业务开发和运维方式方法与模式;另一个方向上,传统运维——指纯手工操作占比60%以上、裸机偏多、虚化资源半自动或完全手工等特征——看似没有了领域市场,然而事实上该场景在中小企业、传统行业信息科技事业部等相关职能部门仍大量存在。
一个事物在同一个时刻出现两极分化时,必然会在某个节点选择方向。就应用系统层运维来说,目前节点就是前沿技术发展替代传统方式的爆发起点之时。只不过这个爆发点对于科技互联网企业,早在五年前就启动了。其他领域的传导没有那么快,也是十分正常的。毕竟对于科技互联网企业,技术的革新和快速迭代是他们的主要营收业务,但对于其他企业来说这只是业务支持,按照“互联网黑话”的说法就叫“IT赋能”。
当下前沿技术发展的路径,其实总体来看,是“小而美”的发展策略。要说“小而不美”那是前五年的话,近几年提出的自治、自愈,以及Serverless、无代码化技术的正式投产,是从“不美”到“美”的一种“转型宣言”。一个微服务集群就像是一个城池里的各种角色的人,即亚当·斯密在《国富论》总结提到的工业革命后的社会化分工,每个人在整个社会体系里面专业化的干一件事,将生产力以指数级提升,国民生产总值不断累积加,共同建设发展同一座城池。一个个容器或pods只干一两件事,他们之间相互通讯交流,共同推动了整个业务的正常运转。
在第一次、第二次软件工程危机前的面向过程编程方法兴盛之时,其实这也是“小而美”的时代,但由于业务的发展,单个文件的“小而美”被人为变”大“了。危机后,面向对象编程方法将“小而美”细化到代码内部实现,而对外一个单体程序则是庞然大物。而现阶段的”小而美“则是把代码内部实现和功能最小化,但是放眼到整个系统来看则是把复杂性上升到了系统层面。这么来看,历代软件工程的革新都想从简从美改善,但最终只不过将无序熵从一个领域转为另外一个领域罢了。从这个角度来看,前沿技术或许也不是什么前沿技术,更像是高智商人群设计的游戏规则,各种矛盾转移的”甩锅内卷大赛“。当然,这种看法有点戏谑了。
而这种发展路径,必然将无序熵从开发人员转移到了运维人员。原来的软件工程的开发模式——面向过程到面向对象——是一种开发人员的内部优化,现在的技术革新解放开发人员的同时改变运维人员的工作模式——运维人员也需要相当的代码功底才能做好运维工作,因为一个容器或pod所关联的,仅仅是一个功能,甚至是一个方法或函数的代码,这是一种操作系统与代码强绑定的关系。也由此,SRE发展出了DevOps,DevOps发展出了多个业态方向。
所以,现在的IT运维,尤其是应用与系统层领域,成了一个两面包夹芝士:开发人员由于前沿技术革新,不断提出运维新要求;用户、业务方或领导由于对前沿技术革新并不完全理解,对于运维人员工作内容上的忍耐度越来越低。
解决这样的窘境,可能只有开发运维干一件事才能解决,那就是共同声明:我们联合!
说是简单说点,结果写到这发现扯了这么多。以后公众号将持续更新,去年开的几个栏目:“源产控”系列、“慧响悦书”系列,以及正常的技术总结、日常评谈,都会持续跟进。至于更新频率嘛我尽量完成周更。
所以,没有完全告辞之意就在此了。欢迎关注慧响公众号,一起交流~不见不散~
2021年5月28日 于上海陆家嘴
]]>bin/elasticsearch-sql-cli uri=http://elastic:ESabc+2333@10.66.66.2:9200/
但执行后,发现使用bin/elasticsearch-sql-cli
登录时,出错提示./x-pack-env: No such file or directory
,莫名其妙,遂即查看下该脚本,内容如下:
#!/bin/bash
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
source "`dirname "$0"`"/elasticsearch-env
source "`dirname "$0"`"/x-pack-env
CLI_JAR=$(ls $ES_HOME/bin/elasticsearch-sql-cli-*.jar)
exec \
"$JAVA" \
-jar "$CLI_JAR" \
"$@"
其中有效行第二行的source "`dirname "$0"`"/x-pack-env
看来执行起来有问题,估计是在依赖导入生效时找不到路径。经过一番折腾,解决了该问题,即将该行替换为如下命令:
# source "`dirname "$0"`"/x-pack-env
source /usr/share/elasticsearch/bin/x-pack-env
即将引入依赖的路径写死即可,使用时请根据实际路径修改x-pack-env
的指向路径。
修改完后,再用刚才的命令启动Elasticsearch SQL,正常进入,完美结局。
后来又仔细查找了下资料,发现该问题是Elasticsearch 7.4版本的一个小bug,在Elasticsearch 7.8的包内,bin/elasticsearch-sql-cli
这个脚本内容已经修改为:
#!/bin/bash
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
source "`dirname "$0"`"/elasticsearch-env
source "$ES_HOME"/bin/x-pack-env
CLI_JAR=$(ls "$ES_HOME"/bin/elasticsearch-sql-cli-*.jar)
exec \
"$JAVA" \
"$XSHARE" \
-jar "$CLI_JAR" \
"$@"
也就是说,如果Elasticsearch 7.8安装时,操作系统环境变量内$ES_HOME
能够正确设置的话,就可以正常启动Elasticsearch SQL了。这样比直接修改产品本身脚本来说要规范的多了。
本篇为慧响技术角“源产控”专题系列第3篇文章。
慧响技术角“源产控”专题,将聚焦开源、国产化、自主可控三个方向的技术,以操作系统、中间件、数据库、程序应用等为粗分类,更新相关技术的发展趋势、探究技术核心的深度使用、系统总结技术整体架构,为对相关技术的学习者提供可观的资料,亦为个人同步学习总结的笔记,以飨读者。
本篇对在CentOS 8上使用Elastic Stack套件中的Elasticsearch、Kibana进行简要总结,对Elasticsearch 7.8.0的部署、认证设置与Kibana 7.8.0的配套部署进行了详细总结。未来对在CentOS 8上使用Elastic Stack相关套件,将陆续更新其使用总结、性能调优等方面的系列文章,敬请期待。
提起Elastic Stack,就不得不提到ELK。ELK是三个开源软件的缩写,分别为:Elasticsearch 、 Logstash以及Kibana , 它们都是开源软件。不过现在还新增了一个Beats,它是一个轻量级的日志收集处理工具(Agent),Beats占用资源少,适合于在各个服务器上搜集日志后传输给Logstash,官方也推荐此工具,目前由于原本的ELK Stack成员中加入了Beats工具所以已改名为Elastic Stack。
Elastic Stack包含:
Elasticsearch是个开源分布式搜索引擎,提供搜集、分析、存储数据三大功能。它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制,RESTful风格接口,多数据源,自动搜索负载等。详细可参考Elasticsearch权威指南。
Logstash主要是用来日志的搜集、分析、过滤日志的工具,支持大量的数据获取方式。一般工作方式为C/S架构,client端安装在需要收集日志的主机上,Server端负责将收到的各节点日志进行过滤、修改等操作在一并发往Elasticsearch上去。
Kibana也是一个开源和免费的工具,Kibana可以为 Logstash 和 Elasticsearch 提供的日志分析友好的 Web 界面,可以帮助汇总、分析和搜索重要数据日志。
Beats在这里是一个轻量级日志采集器,其实Beats家族有6个成员,早期的ELK架构中使用Logstash收集、解析日志,但是Logstash对内存、CPU、I/O等资源消耗比较高。相比 Logstash,Beats所占系统的CPU和内存几乎可以忽略不计。
YUM
方式安装输入如下命令,下载安装公共签名证书:
rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch
在目录/etc/yum.repos.d/
下新建文件elasticsearch.repo
,文件内容填写如下:
[elasticsearch]
name=Elasticsearch repository for 7.x packages
baseurl=https://artifacts.elastic.co/packages/7.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=0
autorefresh=1
type=rpm-md
添加好后,直接执行yum -y install --enablerepo=elasticsearch elasticsearch
安装即可。
RPM
包手工安装执行如下命令,进行安装:
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.8.0-x86_64.rpm;
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.8.0-x86_64.rpm.sha512;
shasum -a 512 -c elasticsearch-7.8.0-x86_64.rpm.sha512;
rpm --install elasticsearch-7.8.0-x86_64.rpm;
**注意:**若
shasum
提示找不到命令,请输入yum -y install perl-Digest-SHA
进行依赖安装。
**提示:**鉴于Elastic网站为国外,下载速度极慢,可选择国内镜像地址,例如华为云的镜像,下载地址:https://mirrors.huaweicloud.com/elasticsearch/7.8.0/
安装完成,系统提示如下:
### NOT starting on installation, please execute the following statements to configure elasticsearch service to start automatically using systemd
sudo systemctl daemon-reload
sudo systemctl enable elasticsearch.service
### You can start elasticsearch service by executing
sudo systemctl start elasticsearch.service
Created elasticsearch keystore in /etc/elasticsearch/elasticsearch.keystore
[/usr/lib/tmpfiles.d/elasticsearch.conf:1] Line references path below legacy directory /var/run/, updating /var/run/elasticsearch → /run/elasticsearch; please update the tmpfiles.d/ drop-in file accordingly.
如有更为灵活的需求,可通过官网下载Elasticsearch源码包进行部署。首先需建立用户属组:
groupadd -g 888 elasticsearch;
useradd -g elasticsearch -m -u 888 elasticsearch;
后通过官网或镜像下载地址下载,解压到需要部署的文件夹:
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.8.0-linux-x86_64.tar.gz;
tar -zxvf elasticsearch-7.8.0-linux-x86_64.tar.gz -C /usr/share/;
mv /usr/share/elasticsearch-7.8.0/ /usr/share/elasticsearch;
chown -R elasticsearch:elasticsearch /usr/share/elasticsearch;
部署完成后就进入配置环节了,在配置之前我们需要知道的是进程文件和配置文件所在地。通过上述方式一、二安装,进程文件路径在/usr/share/elasticsearch
,配置文件路径在/etc/elasticsearch
。通过上述方式三安装,进程文件路径在解压文件路径,例如示例给出的/usr/share/elasticsearch
,配置文件在进程文件路径下的config
文件夹内。
在配置文件路径下有一个文件叫jvm.options
,修改其中的-Xms
、-Xmx
两行参数来调整jvm的初始化堆大小以及最大堆大小。该值建议设置为操作系统内存的40%~50%。注:-Xms
与-Xmx
相同。例:若虚拟机内存为8G,可设置为如下参数:
-Xms4g
-Xmx4g
或者
-Xms4096m
-Xmx4096m
在配置文件路径下有一个文件叫elasticsearch.yml
文件,对该文件进行配置以使集群启动:
# 集群名称,集群中所有节点统一
cluster.name: Elasticsearch-Cluster
# 当前节点名,自定义但集群内不允许重复
node.name: node-1
node.attr.rack: r1
# 下方数据和日志存放路径请按照实际情况修改路径
# 通过方法一、二安装,默认路径已存在,通过方法三安装,请指定存在的路径
path.data: /var/lib/elasticsearch
path.logs: /var/logs/elasticsearch
bootstrap.memory_lock: true
# 当前节点内网IP地址,虽也可以设置为0.0.0.0但建议还是按此设置
network.host: 10.66.66.1
http.port: 9200
# 集群中所有节点地址
discovery.seed_hosts: ["10.66.66.1", "10.66.66.2","10.66.66.3"]
# 集群中所有节点名,应与集群所有节点的node.name一致
cluster.initial_master_nodes: ["node-1", "node-2","node-3"]
另外,在启动Elasticsearch 7.8之前需先做些准备工作。首先编辑文件/etc/security/limits.conf
,在尾部增加如下配置:
elasticsearch soft nofile 100000
elasticsearch hard nofile 100000
elasticsearch soft memlock unlimited
elasticsearch hard memlock unlimited
elasticsearch soft nproc 4096
elasticsearch hard nproc 4096
编辑文件/etc/sysctl.conf
,根据文件内已有内容,调整或新增如下配置:
vm.swappiness=10
vm.max_map_count=262144
添加完成后,执行sysctl -p
。
最后修改/etc/hosts
文件,增加Elasticsearch集群主机名,例如:
10.66.66.1 node-1
10.66.66.2 node-2
10.66.66.3 node-3
Elasticsearch内置Java,无需再在系统层再次部署Java。
完成配置后即可启动,方法一、二可通过systemctl
命令进行启停:
systemctl start elasticsearch;
systemctl stop elasticsearch;
systemctl restart elasticsearch;
如需开启开机自启,可通过如下命令开启:
systemctl daemon-reload;
systemctl enable elasticsearch;
方法三启动可通过如下命令进行启停:
# 启动
/usr/share/elasticsearch/bin/elasticsearch -d -p /usr/share/elasticsearch/pid;
# 停止
esid=$(cat /usr/share/elasticsearch/pid && echo);
kill -SIGTERM $esid;
启动后可以执行如下命令,查看启动日志、集群节点发现、主节点选举是否正常:
# 路径请注意:1. 按照配置文件设置的日志存放路径寻找;2. 按照实际集群名输入日志文件名
tail -f /var/logs/elasticsearch/Elasticsearch-Cluster.log;
# 按照方法一、二安装后也可以通过如下命令查看Elasticsearch进程情况
systemctl status elasticsearch;
也可以通过如下网址浏览器或curl
命令访问直接获取Elasticsearch返回的集群健康检查信息:
curl -XGET http://node-1:9200/_cluster/health?pretty
也可以在浏览器直接访问机器的9200
端口,可展示如下页面:
Elasticsearch默认启动后,9200
端口通过设定的Host IP是可以随意访问的,这十分不安全。为了保证一定的安全性,我们可以修改elasticsearch.yml
文件里http.port
字段,修改默认端口,以及增加访问时必须帐密认证访问。
自Elasticsearch 6.8开始,Elastic将部分X-Pack付费功能免费开放使用,其中就有基础认证功能,因此在Elasticsearch 6.8以后,可直接使用Elasticsearch自带的认证功能。而之前的版本,则需要一个开源插件叫做elasticsearch-http-basic,作者仓库地址:https://github.com/Asquera/elasticsearch-http-basic 。然而目前此仓库已封版不再更新,因此建议如需使用Elasticsearch,不要使用Elasticsearch 6.8以前的版本。
接下来我们配置Elasticsearch 7.8的认证功能,首先在某一台集群节点中,进入进程文件路径,执行如下命令,创建一个证书颁发机构:
bin/elasticsearch-certutil ca;
一路回车即可,中间有设置CA的密码,无需设置。完成后将在进程文件路径目录生成文件elastic-stack-ca.p12
。后继续在该台已生成证书颁发机构的集群节点继续执行如下命令,创建一个证书与私钥:
bin/elasticsearch-certutil cert --ca elastic-stack-ca.p12;
一路回车即可,中间有设置证书的密码,无需设置。完成后将在进程文件路径目录生成文件elastic-certificates.p12
。完成生成后,将该文件拷贝到其他机器的相同路径,集群所有机器修改两个文件的属组:
chown -R elasticsearch:elasticsearch *.p12;
集群所有机器建立软连接到配置文件路径下,示例如下,具体路径请修改为实际路径:
ln -s /usr/share/elasticsearch/elastic-certificates.p12 /etc/elasticsearch/elastic-certificates.p12;
ln -s /usr/share/elasticsearch/elastic-stack-ca.p12 /etc/elasticsearch/elastic-stack-ca.p12;
配置elasticsearch.yml
:
# 设置集群互信通信端口9300
transport.port: 9300
http.cors.enabled: true
http.cors.allow-origin: "*"
http.cors.allow-headers: Authorization
# 开启X-Pack的安全认证
xpack.security.enabled: true
# 开启X-Pack的集群内互信安全认证,与上面安全认证开关同步必开
xpack.security.transport.ssl.enabled: true
# 验证模式为证书模式
xpack.security.transport.ssl.verification_mode: certificate
# 配置证书路径
xpack.security.transport.ssl.keystore.path: elastic-certificates.p12
xpack.security.transport.ssl.truststore.path: elastic-certificates.p12
# 如果需要启用SSL/TLS通过HTTPS访问ES集群,再添加如下配置
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.keystore.path: elastic-certificates.p12
xpack.security.http.ssl.truststore.path: elastic-certificates.p12
xpack.security.http.ssl.client_authentication: none
修改discovery.seed_hosts
字段配置,增加集群互信通信端口9300
,例如:
discovery.seed_hosts: ["10.66.66.1:9300", "10.66.66.2:9300","10.66.66.3:9300"]
完成配置后,重启Elasticsearch进程即可。后进行内置用户认证密码的设置,在某一台集群节点中,进入进程文件路径,执行如下命令:
bin/elasticsearch-setup-passwords interactive;
这里给Elasticsearch中内置用户创建密码,其内置用户有:
完成此步设置,再使用HTTP/HTTPS协议通过9200
端口访问时,就需要帐密了。输入帐密即可访问。curl
也可,命令测试示例如下,例如用户名为elastic
,密码为ESabc+2333
:
curl -uelastic:ESabc+2333 -XGET http://node-1:9200/_cluster/health?pretty
Kibana的部署配置比较简单,安装方式类似Elasticsearch,具体可自行选择,本节不再赘述,使用方法二,执行如下命令,进行安装:
wget https://artifacts.elastic.co/downloads/kibana/kibana-7.8.0-x86_64.rpm;
shasum -a 512 kibana-7.8.0-x86_64.rpm;
rpm --install kibana-7.8.0-x86_64.rpm;
**注意:**若
shasum
提示找不到命令,请输入yum -y install perl-Digest-SHA
进行依赖安装。
**提示:**鉴于Elastic网站为国外,下载速度极慢,可选择国内镜像地址,例如华为云的镜像,下载地址:https://mirrors.huaweicloud.com/kibana/7.8.0/
按上述方式安装后其进程文件路径在/usr/share/kibana
,配置文件路径在/etc/kibana
。
修改/etc/kibana/kibana.yml
文件为如下配置:
server.port: 5601
server.host: "0.0.0.0"
# 设置Elasticsearch集群地址,方便Kibana做容灾管理
elasticsearch.hosts: ["https://node-1:9200","https://node-2:9200","https://node-3:9200"]
kibana.index: ".kibana"
# 国际化设置,设置为中文
i18n.locale: "zh-CN"
# 开启X-Pack的安全认证
xpack.security.enabled: true
# Elasticsearch内置账户密码
elasticsearch.username: "kibana"
elasticsearch.password: "ESabc+2333" # 设置内置账户密码时kibana账户的密码
# Kibana SSL/TLS访问开启,若无需配置SSL/TLS,可忽略
server.ssl.enabled: true
server.ssl.key: /etc/kibana/kibana-certificates.key
server.ssl.certificate: /etc/kibana/kibana-certificates.cer
server.ssl.certificateAuthorities: /etc/kibana/kibana-certificates-ca.cer
server.ssl.clientAuthentication: none
# Elasticsearch如开启SSL/TLS访问,则需要配置如下两条规则
elasticsearch.ssl.verificationMode: certificate
elasticsearch.ssl.certificateAuthorities: /etc/kibana/kibana-certificates-ca.cer
上述Kibana配置中SSL/TLS的认证有点小插曲需要说明,由于Kibana现在不支持.p12
文件的加密认证方式,若目前没有SSL/TLS安全机构认可的签发证书,但仍需要启用SSL/TLS,此时需要我们将.p12
文件转换后使用配置。具体操作如下:
openssl pkcs12 -in elastic-certificates.p12 -nocerts -nodes > kibana-certificates.key
openssl pkcs12 -in elastic-certificates.p12 -clcerts -nokeys > kibana-certificates.cer
openssl pkcs12 -in elastic-certificates.p12 -cacerts -nokeys -chain > kibana-certificates-ca.cer
将生成的几个文件放置在合适的路径,例如上述配置将这几个文件放置在了/etc/kibana/
路径下,后调用即可。
**注意:**仍然建议使用域名向安全机构申请认可的签发证书后配置Kibana的SSL/TLS选项,因为自签发证书浏览器不认为是安全的,仍有安全风险,请注意。
完成配置后即可启动,可通过systemctl
命令进行启停:
systemctl start kibana;
systemctl stop kibana;
systemctl restart kibana;
如需开启开机自启,可通过如下命令开启:
systemctl daemon-reload;
systemctl enable kibana;
启动后通过浏览器访问即可,可通过输入内置用户访问Kibana,例如用户名为elastic
,密码为ESabc+2333
:
yum -y install xxx
命令突然卡死不动,Ctrl+C
取消执行也无解,遂关闭SSH重新建立连接,然而再次重新执行yum -y install xxx
命令时却报如下错误:
...
root@localhost:~>yum -y install xxx
Loaded plugins: fastestmirror, langpacks
Existing lock /var/run/yum.pid: another copy is running as pid 27970.
Another app is currently holding the yum lock; waiting for it to exit...
The other application is: yum
Memory : 43 M RSS (362 MB VSZ)
Started: Mon Jul 13 18:27:38 2020 - 03:46 ago
State : Uninterruptible, pid: 27970
Another app is currently holding the yum lock; waiting for it to exit...
The other application is: yum
Memory : 43 M RSS (362 MB VSZ)
Started: Mon Jul 13 18:27:38 2020 - 03:48 ago
State : Uninterruptible, pid: 27970
...
初步判断应该是进程号为27970
的那个假死了,即使刚才SSH关闭了也并没有杀掉他,遂干脆利落的执行了kill -9 27970
直接杀死,后再执行yum -y install xxx
命令,结果又有报错:
...
root@localhost:~>yum -y install xxx
error: rpmdb: BDB0113 Thread/process 27970/140274709284672 failed: BDB1507 Thread died in Berkeley DB library
error: db5 error(-30973) from dbenv->failchk: BDB0087 DB_RUNRECOVERY: Fatal error, run database recovery
error: cannot open Packages index using db5 - (-30973)
error: cannot open Packages database in /var/lib/rpm
CRITICAL:yum.main:
Error: rpmdb open failed
...
哦豁,凉了,rpmdb本地数据存储文件炸了……不过好在有解决方法。
执行如下命令,清理YUM仓库本地数据存储文件:
mv /var/lib/rpm/__db* /tmp;
执行如下命令,清理yum缓存:
rpm --rebuilddb;
yum clean all
大功告成。
现在细想一下,yum当时有可能是因为网络问题假死,可能再等等就好了,不过毕竟当时有点着急,以后得注意下。
]]>本文是该书的第一部分的下半部分,是书中的第五章,主要介绍了管理的相关内容,涉及了项目任务管理、团队建设、度量与目标树等部分。
管理是有关执行和实现组织目标、愿景和使命必需的所有活动。它是“以明智和合乎道德的手段来完成任务”。要在管理上成功,。为一个奋发向上的领导,需要专注和致力于学习和成长。也需要安排好任务、人和度量以实现预期的目标。并有如下的特点:
经验告诉我们,在每个成功的故事里,最为核心的是在关键的时刻,领导、经理和团队能否在挑战和机遇的面前崛起并使公司获益。“我们确实需要改变目前的方式,来适应新的世界秩序,软件和硬件是开发服务的原材料。我们需要重新思考机构、流程、激励和项目管理。”
《韦氏词典》中对管理是这样定义的,“执行或者监督某些事情”或“以执法手段完成某件事情”。对这一定义,我们将略加修改,融入道德因素,更新后的定义是“以明智和合乎道德的手段来完成任务”。
一般来说,雇员有权知道他的表现如何,其不知道真实表现是对股东和员工的不道德行为。使命完成的方式与使命的实际完成一样至关重要。
管理和领导有许多区别,但两者都很重要。如果领导是承诺,那么管理就是行动。如果领导是目的地,那么管理就是方向。如果领导是激励,那么管理就是动机。如果领导是拉力,那么管理就是推力。对于成功的最大化股东财富,需要两者兼备。
管理包括度量活动、目标评估、指标制订。也包括人员配备中的人事责任、人事评估和团队建设(包括技能和其他的特性)。最后,管理包括项目管理中的所有活动,例如驱动团队完成任务,设定有挑战性的项目进度等等。
好的经理需要具备什么素质
从领导的角度看,管理是一个有几个参数的函数,其中有些参数也适用于领导函数。
最好的经理在看到细节的同时,又能令人难以置信地面向目标和任务。一旦分配到一个任务或目标,他们就能把任务或目标分解。这个分解的活动不仅涉及行动,也包括沟通、组织结构、合理的薪酬后勤和资金等。
好的经理练就了一身管理人的技能,这有助于他们把组织内每个人的最大潜力都发掘出来。
最后,即使最好的经理也承认自己需要不断地提高,而能够意识到提高的唯一办法是度量。
好的经理在预算内按时完成项目,并且符合为股东创造价值的预期。优秀的经理,甚至能在面对逆境的时候亦能如此。但是,理解如何成功地完成那些项目所需要采取的行动也非常重要。
普鲁士名将赫尔穆特·冯·毛奇有句非常出名的话,“没有万全的作战计划”。这句话对管理复杂产品的研发尤为正确。
经验告诉我们,虽然项目计划很重要,但是创造价值的部分却不是最初的计划,而是在过程中对项目及其可能路径的深入思考。不幸的是,太多人把原来制订的计划看成是通往成功的唯一路径,而不是众多可能路径中的一个。毛奇的这句话警示我们要把关注点从最初计划的执行,转移到具有重要价值的应急上。与其像激光一样聚焦在计划的精密性上,我们更应该考虑选择哪条路径以取得项目的成功。因此,在AKF我们实践了5-95规则:即用5%的时间制订一个充足、保守和详细的计划,同时承认这个计划不是完备的,把其余95%的时间投入到应急演练,以应付突发事件。
项目计划的价值是在思考的过程中理解可能的执行选项,从而更好地为股东创造价值。
在成本可控的前提下,为了确保最大的产出和最高的质量,我们必须不断地寻找负担得起的最好的人才。大多数人并不积极地管理技能、人员和团队的组成,其结果相当于欺骗公司和股东。
组织的产出取决于个人的产出和团队的规模。效率是高性价比可扩展性的一个组成部分,用来度量以同样的投入取得更多或更好的产出,或者以较少的投入取得更多的产出。人的扩展性是与个人、规模和组织相关联的。
记住,不做某事的决定意味着你决定了做某事。此外,忽略了一些应该做的事情其实等同于决定不去做。如果你没有花几周的时间与团队的成员们相处,那相当于你已经决定不花时间与他们相处。这是绝对不可原谅的,而且当与董事会讨论这些问题的时候,你的感觉可能也不会太好。
组织的扩展在很大程度上依赖于人才,而这又取决于人均产出及在企业文化影响下行为的一致性。花园应该是经过精心设计的,团队也同样如此。设计团队意味着要找到符合组织愿景和使命所需要的合适人才。
在快速增长的公司里,管理者通常会花很多时间来面试和挑选候选人,但通常花在每个候选人身上的时间却很少。更糟的是,这些管理者通常不会花时间去分析和总结以往招聘决策的好坏。要为某个特定的岗位找到合适的人,需要重视和纠正过去招聘中的失败,复制以往招聘中的成功。然而,在面试中常常只关注技能,而忽略其他更重要的方面,如企业文化的适应性及团队的配合度。自问:为什么你要舍弃某人?为什么有人决定要离开?
要从生产力和质量的角度来考虑组织的人员需求。同时鼓励尽可能花更多的时间与候选人互动,力争第一次就找到合适的人。
照顾花园与优化团队异曲同工。所有与管理团队相关的事情,常常因为时间不足而被忽略。我们可能会花工夫来摘新花,但是却时常忘记,花园里那些已经盛开的花朵也同样需要滋养。
培养的目的是让团队成员成长,以满足股东的期望。这包括指导、赞扬、正确地掌握技术或方法、调整薪酬和股权以及任何使员工更强大和更优秀的办法。
好的团队喜欢积极的、可以实现的挑战。
类比管理花园,营养就是你用在指导表现不佳的个人使其达到可以接受的表现水平的时间,是团队花在补偿表现差的个人所造成的不良结果的时间。对大多数执行人员和管理者来说,给花园除草是最痛苦的活动,因此,这往往是我们最后的手段。
虽然你必须遵守公司淘汰员工的有关要求,但是想办法尽快淘汰妨碍大家实现目标的人极为重要。越早淘汰这些表现差的人,就能越快找到合适的替代者,让团队向前发展。
当一个员工制造出充满敌意的工作环境时,这种情况会更加明显。长期的实践让我们领悟到,对表现差的人要尽早淘汰。
我们确信要营造一种企业文化,支持度量任何与创造股东价值相关联的活动。通常我们建议度量成本、可用性、响应时间、生产率以及质量。
成本直接影响平台的扩展性。无疑,公司的工程计划预算,如果不是有人发给你,就是你自己制订。理想的情况是,一个成长型公司有专门的部分预算用在平台或服务的扩展上。这个百分比就是一个有趣的、需要跟踪度量的指标。
我们建议以工程总支出的百分比和每笔交易的花费,作为度量扩展的成本。而对采用相对成本还是绝对成本来度量和报告可扩展性的成本存在着争议。因此,与其只报告扩展的绝对成本,在位股东创造价值的基础上,将这种价值规范化。
当要寻找度量的目标时,可用性是一个必不可少的选择。报告宕机时间中有多少与平台或系统的可扩展性问题相关联。目的是彻底消除因为用户无法完成交易而丧失业务机会的现象。
与可用性密切相关的是响应时间。即使没有服务水平协议(SLA),响应时间的度量应该和服务水平协议互相对照比较。在理想的情况下,响应时间所涉及的用户交易,应该是与实际的终端,而不是代理之间的互动。对关键的交易,除了绝对度量内部或外部服务水平以外,还应该包括跟踪月度环比的相对度量。假如收入和放弃率与某个关键交易的响应时间变长紧密相关,这些数据可以解释可扩展性项目的作用。
生产效率是另外一个重要的度量指标。在考虑如何度量生产效率的时候,真正的诀窍是把它分成至少两个部分。第一部分讨论工程团队是否用尽可能多的时间来解决工程相关的任务。第二部分的重点是度量每个工作日的产出。
质量不在可扩展性管理的度量范围之内。显然需要了解像错误数量这样的典型指标,生产系统和版本发布中的KLOC量,整个产品中错误的数量和产品质量工作的成本,我们建议围绕着影响可扩展性,把这些因素进一步分解。
目标树的根是一个或者多个公司或组织的大目标,将其分解成次级的目标,通过达成这些次级目标进而实现大目标。
质量和可用性都会影响产品变现的机会。
到目前为止,我们描绘了一张经理的画像,他同时是工头、战术家、园丁和测量师,还不止这些。除了负责确保团队完成工作外,还要决定选择哪条路径,达成什么目标以及如何度量工作的进展,经理必须要确保已经用推土机把通往目标的路径推平铺好。
我们的意思只是管理者负责为组织扫清通往目标成功道路上的障碍。人们很容易把这种想法和“任何挡在路上的都是阻碍成功的障碍,必须移除”混淆起来。有时,路上的障碍起着确保你表现正常的作用。真正的障碍是出了问题,但没有处理。
当你遇到这些障碍时,记住这个团队不是为你工作,而是与你一起工作你可能是团队的负责人,但也是团队成功的关键部分。好的管理者实际上会亲自上手,帮助团队完成目标。
]]>本篇为慧响技术角“源产控”专题系列第2篇文章。
慧响技术角“源产控”专题,将聚焦开源、国产化、自主可控三个方向的技术,以操作系统、中间件、数据库、程序应用等为粗分类,更新相关技术的发展趋势、探究技术核心的深度使用、系统总结技术整体架构,为对相关技术的学习者提供可观的资料,亦为个人同步学习总结的笔记,以飨读者。
本篇对在CentOS 8上使用Nginx 1.18的基本安装与基本使用进行介绍与总结,未来对在CentOS 8上使用Nginx 1.18的相关,将陆续更新其使用总结、性能调优等方面的系列文章,敬请期待。
Nginx(发音同“engine X”)是异步框架的网页服务器,也可以用作反向代理、负载平衡器和HTTP缓存。该软件由伊戈尔·赛索耶夫创建并于2004年首次公开发布。2011年成立同名公司以提供支持。2019年3月11日,Nginx公司被F5 Networks以6.7亿美元收购。Nginx是免费的开源软件,根据类BSD许可证的条款发布。一大部分Web服务器使用Nginx,通常作为负载均衡器。
其开源版本官网是nginx.org。商业版本官网是nginx.com。
不过说起来Nginx,就不得不提在Nginx被F5收购后Nginx作者被捕的事儿了。据外媒报道,2019年12月12日,俄罗斯警方搜查了商业服务器公司Nginx,并当场带走了两位联合创始人。。关于这个事儿,我在文章《战疫之下,哪些事可能会改变我们》也拿其为例对个人品牌与口碑塑造的一些风险进行了阐述。有兴趣的可以点进去看一看。
对于Nginx,由于其发展迅速、性能卓越、开源开放,收割了很多Web服务提供与负载均衡、反向代理场景的市场,也衍生了很优秀的二次开发版本例如国人开发的OpenResty、阿里开源的Tengine等。在“在CentOS 8上使用Nginx 1.18”系列后面将择机对两个优秀的二次开发进行介绍。
虽然我们可以通过yum -y install nginx
进行安装,但是在“源产库”系列第一篇文章《CentOS 8之初相识》中有过介绍,其镜像源预编译的版本为1.14,而截至文章发表之日Nginx的稳定版本已更新到了1.18,主线版本更新到了1.19,因此我们使用官网的稳定源码版本进行下载编译。
Nginx版本下载页,截至发文(2020-07-07)稳定版本已更新到了1.18,主线版本更新到了1.19
小知识:关于稳定版本和主线版本。前者是Nginx更新接收针对高严重性错误的修复,但不会使用最新的功能,其版本号的第二位用偶数表示。而后者是Nginx是更新活跃的开发分支,其添加了最新功能和错误修复,其版本号的第二位用奇数表示。不过在Nginx中,“稳定”指的是功能和更新频率,它与软件质量无关。稳定分支在其生命周期中从不接收新功能,并且通常仅接收一个或两个更新,用于修复严重的错误。 稳定分支的生命周期一般是一年,每年四月官方就会停止对当前稳定分支的维护,不再提供错误修复补丁。
下载并解压nginx-1.18.0.tar.gz
:
wget http://nginx.org/download/nginx-1.18.0.tar.gz;
tar -zxvf nginx-1.18.0.tar.gz;
进入目录cd nginx-1.18.0
,目录结构如下:
总用量 1204
drwxr-xr-x 6 1001 1001 326 7月 7 18:50 auto
-rw-r--r-- 1 1001 1001 302863 4月 21 22:09 CHANGES
-rw-r--r-- 1 1001 1001 462213 4月 21 22:09 CHANGES.ru
drwxr-xr-x 2 1001 1001 168 7月 7 18:50 conf
-rwxr-xr-x 1 1001 1001 2502 4月 21 22:09 configure
drwxr-xr-x 4 1001 1001 72 7月 7 18:50 contrib
drwxr-xr-x 2 1001 1001 40 7月 7 18:50 html
-rw-r--r-- 1 1001 1001 1397 4月 21 22:09 LICENSE
drwxr-xr-x 2 1001 1001 21 7月 7 18:50 man
-rw-r--r-- 1 1001 1001 49 4月 21 22:09 README
drwxr-xr-x 9 1001 1001 91 7月 7 18:50 src
conf
文件夹内为Nginx的初始配置文件,contrib
文件夹放置了三位开源贡献者贡献的三个插件内容,html
文件夹为默认站点文件内容,src
文件夹为Nginx源码。configure
文件为编译的配置文件。
拷贝Nginx安装包内如下文件到路径,可以让vim对nginx.conf
的语言语法进行高亮解析:
cp -r contrib/vim/* ~/.vim/;
./configure --help
可以查看Nginx编译支持参数列表,configure
文件进行设置后,会在objs
文件内生成Nginx的安装所需文件,其中ngx_modules.c
,该文件决定接下来编译有哪些模块需要编译进Nginx。
本文不涉及模块的编译使用,我们使用最简单的编译设置进行编译安装:
./configure --prefix=/usr/local/nginx;
如果报如下错误,请执行命令yum -y install pcre-devel openssl openssl-devel
进行安装:
...
checking for PCRE library ... not found
checking for PCRE library in /usr/local/ ... not found
checking for PCRE library in /usr/include/pcre/ ... not found
checking for PCRE library in /usr/pkg/ ... not found
checking for PCRE library in /opt/local/ ... not found
./configure: error: the HTTP rewrite module requires the PCRE library.
You can either disable the module by using --without-http_rewrite_module
option, or install the PCRE library into the system, or build the PCRE library
statically from the source with nginx by using --with-pcre=<path> option.
出现以下内容,即为configure
文件设置完成:
Configuration summary
+ using system PCRE library
+ OpenSSL library is not used
+ using system zlib library
nginx path prefix: "/usr/local/nginx/"
nginx binary file: "/usr/local/nginx/sbin/nginx"
nginx modules path: "/usr/local/nginx/modules"
nginx configuration prefix: "/usr/local/nginx/conf"
nginx configuration file: "/usr/local/nginx/conf/nginx.conf"
nginx pid file: "/usr/local/nginx/logs/nginx.pid"
nginx error log file: "/usr/local/nginx/logs/error.log"
nginx http access log file: "/usr/local/nginx/logs/access.log"
nginx http client request body temporary files: "client_body_temp"
nginx http proxy temporary files: "proxy_temp"
nginx http fastcgi temporary files: "fastcgi_temp"
nginx http uwsgi temporary files: "uwsgi_temp"
nginx http scgi temporary files: "scgi_temp"
紧接着执行make
命令,执行后,在objs
文件夹下可以看到编译后的nginx
二进制文件和目标文件。无报错说明正常。如若首次安装Nginx,紧接着执行make install
安装即可。如果是进行Nginx升级,make install
是不应该用的,需要做的是将该目录下生成的nginx
二进制文件拷贝到已有Nginx的二进制文件相应目录中。
安装完成,输入如下命令进行启动Nginx:
/usr/local/nginx/sbin/nginx;
Nginx基本指令格式为nginx -s reload
,具体指令解释如下:
nginx -?/-h
为显示帮助信息;nginx -c
为使用指定的配置文件,后跟配置文件路径;nginx -g
为指定配置指令,后跟需要指定的配置指令;nginx-p
为指定运行目录,后跟运行目录路径;nginx -s
发送信号,其中后跟参数stop
为立即停止服务、quit
为有序停止服务;reload
为重载配置文件、reopen
为重新开始记录日志文件;nginx -t/-T
测试配置文件是否有语法错误;nginx -v、nginx -V
打印Nginx的版本信息或编译信息。在浏览器访问IP,如图,可以访问即正常。
ps -ef | grep nginx
查看进程情况:
root 8556 1 0 19:24 ? 00:00:00 nginx: master process /usr/local/nginx/sbin/nginx
nobody 8557 8556 0 19:24 ? 00:00:00 nginx: worker process
root 8561 8702 0 19:25 pts/0 00:00:00 grep --color=auto nginx
现在安装好的基本Nginx,完整的nginx.conf
文件如下:
#user nobody;
worker_processes auto;
error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log logs/access.log main;
server {
listen 80;
server_name localhost;
access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
Nginx配置语法有如下的规则:
;
结尾,指令与参数间以空格符号分隔;{}
将多条指令组织在一起;include
语句允许组合多个配置文件以提升可维护性;#
添加注释,提高可读性;$
可调用变量;Nginx有多种指令块,其中http
块表示此为HTTP协议处理块、upstream
表示有上游服务提供的配置信息、location
对应URL的表达式、server
对应一个或一组域/域名的访问。
以下是配置文件部分的参数解释:
worker_processes
为设定工作进程数,若设置为auto
则Nginx将根据机器的内核数进行设定分配,如无特殊需求一般建议设置为auto
。error_log
设定错误日志的路径与等级,其中等级有debug
, info
, notice
, warn
, error
, crit
, alert
, emerg
几种,以严重性从高到低的顺序列出。根据官方文档的解释,设定某个级别的意义是该级别及以上的日志会被打印在日志文件中,例如设置info
则包括其本身在内以及notice
, warn
, error
, crit
, alert
, emerg
都会被列出。若第二个字段为空,默认为error
。log_format main 'XXX'
设定日志格式,main
标示为命名,在遇到对不同域名进行不同格式的命名记录时需要用到,server
区块下access_log logs/access.log main;
可以以main格式进行记录。关于其他参数例如pid
、worker_connections
等,以及server
、upstream
、location
指令块,将在后续的“在CentOS 8上使用Nginx 1.18”系列文章中陆续介绍与展示使用方法。
本文是该书的第一部分的中间部分,是书中的第四章,主要介绍了领导力相关的概念、模型、理念、成功因果路线图等部分。
本书把领导力定义为“影响一个组织或者个人达成某个特定目标的行为的力量”,即对组织或个人完成具体目标的影响力。
领导不仅仅指个人或组织的直接报告人。你可以领导你的同伴、来自于其他机构的人,甚至管理层。一个组织内的榜样或楷模也是领导。你会看到,领导是关于你做什么和如何影响你周围其他人或好或坏的行为。
而领导的失败是无法扩展的最常见原因。经常有公司只聚焦在产品功能的研发上,没有意识到在客户的不断要求下,要达到合适的系统可用时间和响应速度需要做什么事情。
【领导:天生的还是人造的】一个人的领导能力是直接取决于其影响个人或组织行为的能力。影响行为的能力来自于几个方面,有些是天生的品质,有些是环境熏陶的结果,还有些是日久形成的容易修改的工具和方法。但是经常发生的是,好的外表被当成是最重要的,但是铁的事实是大多数人都愿意围着长相好看的人转。
我们相信,人的性格是天生的,这些因素可能不仅是由基因决定的,还由环境养成的。事实上,长相、感召力、仪表、魅力和人格只是许多领导因素中的一部分,尽管这些因素有所帮助,其他的因素在形成领导的影响力方面同样重要。
我们相信领导的能力,或者说影响一个人或组织的相关目标的行为,是关于几个特性的函数。我们把这些特性叫做函数的参数。输入参数到领导力函数后,会返回一个结果,这个结果就可以反映出个人有效地改变和影响行为的能力。
领导力函数的有些参数,与你能否通过创新或者凭毅力做出什么事情有关系。在这里,创新指的是随口说出愿景的能力,而毅力是不断地花时间进行尝试,但产生的效果是一样的。
领导力函数的其他参数与别人如何看待你有关系。这里有两个重点:感觉和感觉的效果对你所领导团队的影响。如果你是位领导,任何时候都会有人在看着你,他们将在你最弱的时候看着你,并据此形成印象。
用函数来描述领导力的且的是要告诉你,尽管可能无法改变一些事情,但是你仍然可以努力来当一个好的领导。
在缺乏经验或者存在高度无知的情况下,对自己能力 的高估最严重。而要从好的领导力中受益,必须清楚地掌握现状。在《和谐领导》(Resonant Leadership)一书中,理查德·伯亚兹和安妮·麦基发现专注、希望和同情是改变个人的三个要素。专注是自知,包违的和能力,希望和回懂有助于产生愿景,从而驱动改变。
最好的选择就是有一个来自于老板、同级和下级的工作表现评议,常用的是360度评议(360-degree review process)。只有那些可以确切地告诉你如何帮助他们改善表现和绩效评议结果的人,才是你要影响的人。而这个过程必须是匿名的。
当然,如果绩效自评结果不能形成改进计划,那就是浪费你和组织的管理时间。我们认为计划应该既包括依赖和发扬优点,也包括克服自己的缺点。没有人因为优点而无法达到目标,也没有人因为缺点而取得成功。关于领导力,我们必须要通过最小化缺点来减少亏损面,通过最大化优点来增加积累面。
最好的领导人所共同拥有的几个特性,包括以身作则、不刚自用、努力完成使命,同时留意和同情组织的需要、及时决策、给团队授权、和股东的利益保持一致。
大多数的人都会赞同,在较好的工作环境或者文化环境里工作的员工,比在相对较差的工作环境或者文化环境里工作的员工的产出要高。
评估你对团队期望的文化标谁,并再次确定自己的行为举止是否与企业文化标准的要求一致。但这并不是说你的团队不能做你没做过的任何事情。从行为的角度来说,你应该表现给你的团队看,已所不欲,勿施于人。
他人对你滥用职权的看法会毁掉你的信用而且影响到领导力函数的结果。破坏领导力的函数会造成员工浪费时间来讨论滥用职收甚至让他们误以为类似的滥用职权是可以接受的行为,这些讨论浪费时间和金钱,减小组织的规模。
每个人都可以通过“以身作则”来提升领导力。严于律己,不徇私枉法,行为的方式应和你期望自己的组织的表现一样。
任何一个公司的任何一个职位都不允许自我意识膨胀。过度自夸的言行与打造最好的团队背道而驰,随着时间的推移将会侵蚀股东的价值。最好的领导在为股东争取利益方面大公无私。你对他最忠心,愿意为他做任何事情,那个老板一定会把股东的利益放在第一位,并且总是强调团队。首先想着如何为股东创造更多的价值,而不是如何为个人带来价值。
能干的领导和经理完成使命,伟大的领导和经理则是通过营造文化氛国,使员工感受到资识和尊重,诚实而且及时地处理绩效相关的反馈。
好的领导会确保那些产生最多价值的人得到最好的奖赏,确保那些表现远在一般人之上的人能够得到应有的休假。
关爱并不意味着要在组织内部实行福利或者终身雇用制度。关爱也并不意味着要去设置容易达成的目标。
检验“以人为本”是较为简单的,对于成功组织内经验丰富的领导,看看他手下有多少人曾经不断地跟随这位经理升迁就能知道。
“以已为本,使命为先”的领导把员工当成向上爬的梯子,踩着他们往上走。“以人为本,使命为先”的领导则为所有的一流员工建立好向上爬的梯子。
一般而言,你要依靠合适的信息,不浪费时间,迅速行动并做出最佳决策。勇敢和决策是一个领导必然要关注的事情。
我们推崇一个信条,“你想让大家做什么,那么就教导什么,你教导什么,你的标准就是什么。”这里面的允许既指其他人也包括你自己。不论大事小情,这一条特别适用于那些违反道义的情况。
什么都不能摧毁你内在的可信性和你对组织的影响能力(甚至包括对不正当行为的理解)。绝不能把说谎、欺骗或偷盗与为股东创造财富相提并论。
赋予团队权力,比任何领导力活动或行为对组织扩展能力的影响都大。授权是分配行为、责任和所有权,可能包括把部分或全部领导权和管理权交给个人或组织。以领导力而言,授权是提升个人、团队、领导和经理对其所负责事项的骄傲感。总体看来,相信自己被赋予权力的个人,比那些相信自己仅仅执行命令的个人,在做决策和管理过程中的效率更高。简而言之,真正的授权管理相当于加倍组织的产出,因为其本身不再成为所有活动的瓶颈。
然而,领导可能明确地把权力赋予其他人,但事实上却通过设定一个瓶颈限制着组织的产出。其结果是士气、产出和信任均遭到破坏。另外,赋予个人和团队权力并不是说领导对结果不再负责任。尽管领导可以授权,但是对达成的结果,他却要对股东负责。
简单地说,在一个以营和为目的的公司里,你的工作就是为股东创造财富。更重要的是,你的工作就是要使股东的财富最大化。而如果是在非营利机构,这些是由慈善团体捐助或者别人付钱请你做好事的话,那么更常见的是创造一种情感财富。
最有成效的领导往往通过理念来影响其组织,把团队的利益置于个人利益之上,为团队及其成员提供智能激发,为团队成员的福利和职业发展展现出诚实和个性化的关怀。
这些高尚的领导方式是通过理念的影响来实现,常被称为“变革型领导方式。”变革型领导方式的扩展性明显更好。不像交易型关注个人,变革型可以关注团队。从与个人讨论,变成与团队研究如何激励大家来完成任务。
总的来说,领导在愿景和使命上面往往没有投入足够的精力。一般情况下,可以在年度计划会议中,安排一到两个小时有关愿景和使命的讨论。这个讨论团队可以选择,但是领导必须参与。
愿景应该是可以度量和验证的。但是在制定愿景的进程中,基于未来状态不是什么去定义最终状态耗时太多。因此,我们建议愿景要简洁地定义理想的终极状态。
展现愿景最简单的方法或许是从如何给一个人发出指令的角度整看。当指令发出时,你会同时说明如何确定目标是否达成。一个愿景应该符合下面这些标准:
如果愿景是对理想未来或者旅途终点的生动描绘,那么使命就是我们到达目的地的总路线或者行动计划。对使命的描述更亲于公司的现状,因为现状对到达理想状态或公司的愿景极为重要。使命应当包括一些目的,一些今天要做的事情和如何达成愿景的方向同愿景描述一样,使命的描述也应该是可以验证的。使命的描述应该符合下述条件:
目标就是旅行中确保我们走在正确道路上的路标或者里程碑。我们认为最好的目标是通过SMART来制订的。SMART是:
目标不会告诉人们如何做什么事情,但是会指明是否在沿着正确的道路前进。
作为一个领导,你可以做得最好和最容易的事情之一,是确保组织的成功,让大家理解他们所做的日常贡献怎么对实现组织的愿景起作用,进而为股东创造价值。我们把对这种关系的理解叫做成功的因果路线图。通常比较容易确定这种关系,否则我们就要质疑这一岗位存在的必要性。
]]>