关于 On-Call

On-Call

要说具体的 On-Call,早在 2016年还在负责运维工作的时候,就是 On-Call 的状态,那时候虽然没有明确的规定,但是做得事情就是 On-Call。在年会之后,公司正式宣布执行 On-Call,并且我在上周又重新体会了下 On-Call,感觉涉及到的东西可太多了。

售后服务

在一家做 2B 市场的公司,销售所售卖的,或者说客户所购买的,远远不仅是你的软件。如果单论软件,那么有无数的公司产品比你牛,销售比你多,研发比你强,又怎么争得过呢。我理解 2B 公司最大的卖点是服务:软件安装了,产品运行了,业务上线了,之后呢?无穷尽的服务,小到一项报警项,大到节点故障,都是靠着服务堆起来的。

怎么才能服务好呢? 有一个良好的售后团队,或者说一个良好的售后体系支撑。因为我没有经历过太多的公司,只能就自己接触到的做 2B 公司的目前情况来说,没有见到那种让人眼前一亮的售后服务。

接触到的小公司售后团队(甚至一些国产2B 大厂)大多都是这么做的:

  1. 安装实施
  2. 定期巡检
  3. 故障处理

能做到上述 3 点尤其是第 2 点的并不多。我理解的 3 个阶段:
安装实施:了解客户环境,及时记录并沟通确定环境中不稳定的点,做好 PlanB,最终实施完成也要形成实施报告,无论是交付客户,还是公司内部之后的持续跟踪,都是只有好处没有坏处。

定期巡检:周期性与客户沟通进行巡检,很多客户在使用你的产品,其实他们是没有了解过多的使用上的内容的,毕竟产品说明书(使用文档)几百上千页,应该没有哪些真实用户会一点点的研究了解,大多只是出于使用上。那么这时就需要售后同学定期去进行巡检,帮助客户去发现问题,同时也是在教育用户去了解更多的产品细节。

故障处理:这里就涉及到今天聊得主题,当线上环境出现问题,怎么做?如何做?我理解的故障处理,无论 Bug 多难复现,无论操作多苛刻,都要第一时间恢复线上业务,如果业务不能恢复,其他一切免谈。

为什么 On-Call?

在我上述提到的 3 点中,1、2通常是由售后同学来完成,最重要的第 3 点通常是由研发同学修复。

在售后同学,遇到了线上故障:

  • 如何去了解这个故障的影响范围?
  • 如何准确全面的去提供这个故障相关信息?
  • 如何第一时间找到功能模块负责的相应研发同事?

上述问题在工作中经常遇到,在产品没有完全成熟前,需要售后同学对产品了解不仅限于使用,而需要更多的去了解产品内容,产品通过何种方式提供这个功能。

当然,在一个小公司中,我们无法要求更多,我们需要做的是,第一时间修复问题,恢复业务,于是有了 On-Call。

如何 On-Call?

轮值表:On-Call 的要求其实是一个售后岗位的基本职责,只是对于研发同学来说可能略微难过,7 24 小时 365 响应。具体落实下来就可能是以 7 * 24 为周期的轮值。这期间可能有很多要求,比如多久要接通电话,多久要接入远程等等。

故障时间线:可能一次故障设计到了多个功能模块的同学,那么我们要根据这个事故,编写文档,记录详细的时间线(当然我们现在做的并不好),至少可以让我们再进行任务交接时,了解到这个任务目前的状态,不至于一脸懵逼。永远保持这个事情处于跟踪状态。永远。哪怕是定了闹钟去提醒自己。

事故报告:之前没觉得事故报告的编写需要花费很多的时间,这周明显当头一棒,要想写好一份报告,大部分情况下需要花费的时间精力远远要比这次事故的解决方式多得多。这份报告不仅交付给客户,还能让我们针对后续的产品开发设计上避免踩坑。

后续工作:在恢复线上业务之后,我们针对该问题进行修复,无论是代码修复,还是文档修复,都需要多次验证,保证自己的代码是健全的(当然我往往考虑不周。。。)。

个人感受

在上周 On-Call 过程中,占用的时间远远超出预期,整整 2人天的时间。整个人精神紧绷,因为 On-Call 的事情永远是优先级最高的事情,你随时都面临着工作内容被打断的状态,也可能因为太久没有经历这种状态,导致我在 On-Call 期间本职工作的工作进度,工作效率都不能让自己满意,很多时候需要远离工位,进行彻底的放松,才能继续下去。
同时也让自己更多的注意代码内容,用更多的时间保证自己代码的稳定性是值得的,无论是对自己,还是对于其他同事都是一种负责的体现。

人,才是最大的 Bug。

参考链接

Python 调用 systemd watchdog 方法

systemd

在之前的博客中介绍过 systemd 的基本使用及通过 timer 来替换 crontab 的方法,今天来说一下如何调用 watchdog。

在 systemd 中,提供 watchdog 来检测服务状态状态,官方文档中描述这个功能为 “keep-alive ping”,我们可以在服务的启动配置中,添加 WatchdogSec 来指定 timeout 时间,在服务程序中通过发送 WATCHDOG=1 来不断的通知 systemd,服务处于正常状态,当超过 timeout 时间未收到 WATCHDOG=1 信号后,systemd 会根据 Restart 配置,决定是否自动重启服务。

示例

服务程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
root@yiran-30-250:/usr/lib/systemd/system
$ cat /root/project/watchdog/test.py
#!/usr/bin/python
# coding:utf-8
import os
import time
import socket
import logging

print("Test starting up...")
time.sleep(1) # 模拟执行真实业务
print("Test startup finished")

try:
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
addr = os.getenv("NOTIFY_SOCKET") # systemd default addr = "/run/systemd/notify"
if addr and addr[0] == "@":
addr = "\0" + addr[1:]
except Exception:
logging.error("Failed to get notify socket addr")

if sock and addr:
sock.connect(addr)
sock.sendall("READY=1") # 通知 systemd 服务启动完成

count = 0

while True:
print("Running...")
time.sleep(2)
if count <= 10:
sock.sendall("WATCHDOG=1") # 通知 systemd 服务正常运行
logging.info("Notify socket addr is:%s", addr)
logging.info("test.service watchdog timestamp update succeeded")
count += 1

服务 systemd 配置:

1
2
3
4
5
6
7
8
9
10
11
root@yiran-30-250:/usr/lib/systemd/system
$ cat test.service
[Unit]
Description=A test service written in Python

[Service]
Environment=PYTHONUNBUFFERED=true
ExecStart=/usr/bin/python /root/project/watchdog/test.py
Type=notify
Restart=always
WatchdogSec=5

我们启动服务观察下运行状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
root@yiran-30-250:/usr/lib/systemd/system
$ systemctl daemon-reload
root@yiran-30-250:/usr/lib/systemd/system
$ systemctl start test.service
root@yiran-30-250:/usr/lib/systemd/system
$ systemctl status test
● test.service - A test service written in Python
Loaded: loaded (/usr/lib/systemd/system/test.service; static; vendor preset: disabled)
Active: active (running) since 日 2019-03-10 10:16:39 CST; 21s ago
Main PID: 12202 (python)
Memory: 4.0M
CGroup: /system.slice/test.service
└─12202 /usr/bin/python /root/project/watchdog/test.py
# 在 Python 程序中,如果没有指定输出位置,会默认打到系统日志中
3月 10 10:16:41 yiran-30-250 python[12202]: Running...
3月 10 10:16:43 yiran-30-250 python[12202]: Running...
3月 10 10:16:45 yiran-30-250 python[12202]: Running...
3月 10 10:16:47 yiran-30-250 python[12202]: Running...
3月 10 10:16:49 yiran-30-250 python[12202]: Running...
3月 10 10:16:51 yiran-30-250 python[12202]: Running...
3月 10 10:16:53 yiran-30-250 python[12202]: Running...
3月 10 10:16:55 yiran-30-250 python[12202]: Running...
3月 10 10:16:57 yiran-30-250 python[12202]: Running...
3月 10 10:16:59 yiran-30-250 python[12202]: Running...

timeout 导致服务重启:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
root@yiran-30-250:/usr/lib/systemd/system
$ systemctl status test
● test.service - A test service written in Python
Loaded: loaded (/usr/lib/systemd/system/test.service; static; vendor preset: disabled)
Active: deactivating (stop-sigabrt) (Result: watchdog) since 日 2019-03-10 10:17:06 CST; 2ms ago
Main PID: 12202 (python)
Memory: 3.9M
CGroup: /system.slice/test.service
└─12202 /usr/bin/python /root/project/watchdog/test.py

3月 10 10:16:49 yiran-30-250 python[12202]: Running...
3月 10 10:16:51 yiran-30-250 python[12202]: Running...
3月 10 10:16:53 yiran-30-250 python[12202]: Running...
3月 10 10:16:55 yiran-30-250 python[12202]: Running...
3月 10 10:16:57 yiran-30-250 python[12202]: Running...
3月 10 10:16:59 yiran-30-250 python[12202]: Running...
3月 10 10:17:01 yiran-30-250 python[12202]: Running...
3月 10 10:17:03 yiran-30-250 python[12202]: Running...
3月 10 10:17:05 yiran-30-250 python[12202]: Running...
3月 10 10:17:06 yiran-30-250 systemd[1]: test.service watchdog timeout (limit 5s)!

journalctl 查看具体重启原因:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
3月 10 10:19:20 yiran-30-250 python[12303]: Running...
3月 10 10:19:22 yiran-30-250 python[12303]: Running...
3月 10 10:19:24 yiran-30-250 python[12303]: Running...
3月 10 10:19:26 yiran-30-250 python[12303]: Running...
3月 10 10:19:28 yiran-30-250 python[12303]: Running...
3月 10 10:19:29 yiran-30-250 systemd[1]: test.service watchdog timeout (limit 5s)!
3月 10 10:19:29 yiran-30-250 systemd[1]: test.service: main process exited, code=dumped, status=6/ABRT
3月 10 10:19:29 yiran-30-250 systemd[1]: Unit test.service entered failed state.
3月 10 10:19:29 yiran-30-250 systemd[1]: test.service failed.
3月 10 10:19:29 yiran-30-250 systemd[1]: test.service holdoff time over, scheduling restart.
3月 10 10:19:29 yiran-30-250 systemd[1]: Starting A test service written in Python...
3月 10 10:19:29 yiran-30-250 python[12324]: Test starting up...
3月 10 10:19:30 yiran-30-250 python[12324]: Test startup finished
3月 10 10:19:30 yiran-30-250 systemd[1]: Started A test service written in Python.

参考链接

Python 生成器使用

背景

在清理 Pocket 列表的时候,发现自己很早之前收藏过 dabeaz 在 2008 年 PyCon 关于生成器的 PPT 讲解,今天读完,有所收获。

在 PPT 中, dabeaz 通过一个具体的文件处理的例子,一步一步的讲解了程序的演进,具体代码可以在 Github 查看。

生成器

使用 yield 关键字的函数就是生成器。生成器在运行时生成值,所以只能迭代一次。生成器可以通过 next 关键字执行,通常我们通过 for 循环来迭代生成器,可以自动处理 StopIteration 情况。

一个简单的生成器例子:

1
2
3
4
5
6
7
8
def countdown(n):
while n > 0:
yield n
n -= 1
>>> for i in countdown(5):
... print(i, end=' ')
...
5 4 3 2 1

当我们调用生成器时,仅返回一个生成器对象,不会执行函数内容,只有当执行 __next__() 时函数才会真正执行。yield 会返回给调用者当前值,同时暂停执行,等待下一次调用 __next__() 继续执行。

协程

在 python 中通过生成器的方式来实现协程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
In [7]: def recv_count():
...: try:
...: while True:
...: n = yield
...: print("T-minus", n)
...: except GeneratorExit:
...: print("boom")
...:
In [8]: r = recv_count()
In [9]: r.send(None) # init
In [10]: r.next() # yield 未接收到调用者发送具体值
('T-minus', None)
In [13]: r.send(1)
('T-minus', 1)
In [14]: r.send(2)
('T-minus', 2)
In [15]: r.send(3)
('T-minus', 3)

关于为什么要执行 r.send(None) ,可以看 PEP-342 中具体解释:

Because generator-iterators begin execution at the top of the generator’s function body, there is no yield expression to receive a value when the generator has just been created. Therefore, calling send() with a non-None argument is prohibited when the generator iterator has just started, and a TypeError is raised if this occurs (presumably due to a logic error of some kind). Thus, before you can communicate with a coroutine you must first call next() or send(None) to advance its execution to the first yield expression.

在此基础上进行扩展,我们写一个生产消费者模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
root@yiran-30-250:/tmp
$ cat a.py
#!/usr/bin/python3

def consumer():
r = ''
while True:
n = yield r
print('Consuming %s ...' % n)
r = '200 OK'
def producer(c):
c.send(None)
n = 0
while n < 5:
n += 1
print("Producing %s ..." % n)
r = c.send(n)
print("Consumer return: %s" % r)
c.close()
c = consumer()
producer(c)

root@yiran-30-250:/tmp
$ python3 a.py
Producing 1 ...
Consuming 1 ...
Consumer return: 200 OK
Producing 2 ...
Consuming 2 ...
Consumer return: 200 OK
Producing 3 ...
Consuming 3 ...
Consumer return: 200 OK
Producing 4 ...
Consuming 4 ...
Consumer return: 200 OK
Producing 5 ...
Consuming 5 ...
Consumer return: 200 OK

在 Python3.3 之后,添加了 yield from, asyncio 等关键字,在之后的博客中单独记录。

参考链接

Libvirt CPU 配置

Libvirt CPU 配置参数

我们先来看下 Libvirt 关于虚拟机 CPU 配置项:

  • match
  • check
  • mode
  • model

具体配置解释可以去 Libvirt 官方文档中查看,这里主要说一下 mode 参数,看一下 mode 具体含义及可选配置:

host-passthrough

Libvirt 通知 KVM 对 CPU 不做任何配置项修改,直通给虚拟机。因为虚拟机可以使用与物理主机相同 CPU 指令集,性能最好,相反,在虚拟机热迁移过程中,对目标主机 CPU 要求同型号同代。

host-model

本质上是根据物理主机 CPU 从 cpu_map.xml 文件中选择最匹配的 CPU 型号。由于CPU定义是在启动虚拟机之前复制的,因此可以在不同主机上使用完全相同的XML,同时仍然提供每个主机支持的最佳虚拟机 CPU。属于在功能与性能之间的平衡。

custom

不指定 mode 属性时的默认值。此模式使得无论虚拟机启动哪个主机,虚拟机都将看到相同的硬件,兼容性最好。

最佳选择

在不考虑虚拟机兼容性(热迁移)情况下,优先选择 host-passthrough ,综合考虑选择 host-model

OpenStack 中如果检测到 hypervisor 是 kvm-qemu ,则默认值为 host-model ;在 hypervisor 是其他类型时,默认值为 none,即由 hypervisor 自己选择。

我查看了自己两台 VPS 的 CPU 信息,如下:

Vultr

1
2
3
4
5
6
7
8
9
[root@vultr ~]# lscpu
Architecture: x86_64
厂商 ID: GenuineIntel
CPU 系列: 6
型号: 60
型号名称: Virtual CPU 71438xxxxxxx
超管理器厂商: KVM
虚拟化类型: 完全
Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx rdtscp lm constant_tsc rep_good nopl xtopology cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm invpcid_single pti fsgsbase bmi1 avx2 smep bmi2 erms invpcid xsaveopt arat

BWH

1
2
3
4
5
6
7
8
9
[root@bwh ~]# lscpu
Architecture: x86_64
厂商 ID: GenuineIntel
CPU 系列: 6
型号: 13
型号名称: QEMU Virtual CPU version (cpu64-rhel6)
超管理器厂商: KVM
虚拟化类型: 完全
Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm rep_good nopl pni pclmulqdq ssse3 cx16 pcid sse4_1 sse4_2 x2apic popcnt aes xsave avx f16c rdrand hypervisor lahf_lm fsgsbase smep xsaveopt

可以看到 BWH 的 CPU 比 Vultr CPU 指令集少了很多,而且 CPU 型号是 QEMU Virtual CPU version (cpu64-rhel6) 推断物理机 libvirt 配置是 custom ,果然便宜无好货。

参考链接

学会拒绝

趁着春节 7 天假期,把 18年所有的年假都请了下来,计划在家里待 20多天,这次假期最大的感受就是父母不懂得拒绝,或者说他们不想拒绝。

我小时候也是这样,可能是因为父母的关系,我一直处于一个周围同学中的“好人”状态,有事情要找人帮忙,一般都会找到我这来,因为我不拒绝。这也给我自身造成了一种假象:“我跟周围的关系不错”。那么我不拒绝的原因是什么呢?

最早可能是我不会拒绝,觉得拒绝会让对方难堪;之后可能是我觉得这段关系是我所看重的,所以我为了维持关系不拒绝,但是根本原因还是第一条;最终可能是我懒。(?

不拒绝,不想拒绝或者说不懂得拒绝,给我造成了很多的困扰,很被动。我这个人是一个喜欢计划的人,无论是生活还是工作,我都希望能够按照我的计划进行(当然如果出现计划外的事情往往我会比较懊恼。但是又因为我不拒绝,导致我的计划经常被中断,甚至是有些时效性的事情,就被彻底的终止了。可能这件事情对我比较重要,但是因为不拒绝,导致事情没完成。

那么我是从什么开始学会拒绝的呢?准确的说是同事教我的(虽然现在这位同事经常被我拒绝 - - 。在 2017年,我负责的工作准确的说是一半在公司内部,另一半在与公司外部的合作上。公司外部的工作优先级比较高,当我在做公司内部工作的时候,经常性的被打断,去处理优先级比较高的工作,造成我的整体工作效率不高,甚至说很低,哪怕我已经尽可能的考虑到部分原因,但是计划永远赶不上变化,我们要针对不同的事情去拒绝,比如当我正在处理公司外部的工作 A,我的老板让我立即做公司内部工作 B,自我评估优先级之后,我们决定是否接受 B,而不是因为那是我的老板,我就毫不犹豫的接受,因为当时接受的结果很有可能会造成 A & B 都做不好。

亲戚、朋友甚至是恋人关系也是一样,这种与工作关系不同,在让你帮忙时,他们往往会带上“情感”上的束缚,会利用“情感”关系去控制你,仿佛你不做这段关系就到此为止一样,这种是最可怕的,要学会拒绝,避免深陷。

当然我现在的拒绝可能比较生硬,导致不愉快,但是终究对我来说是个好事,期望后续能够通过阅读《情感勒索》这部书来改善。

聊一聊 ISO 9660

ISO 9660

ISO 9660,也被一些硬件和软件供应商称作CDFS(光盘文件系统),是一个由国际标准化组织(ISO)为光盘介质发布的文件系统。其目标是能够在不同的操作系统如Windows、Mac OS以及类Unix系统上交换数据。

我们平时接触到的 ISO 格式文件均为 ISO 9660,我们可以通过 file 命令进行查看,以 RedHat 发行版为例:

1
2
[root@node redhat]# file rhel-server-7.6-x86_64-dvd.iso 
rhel-server-7.6-x86_64-dvd.iso: ISO 9660 CD-ROM filesystem data 'RHEL-7.6 Server.x86_64 ' (bootable)

用途

  1. 安装操作系统,我们可以通过将 ISO 挂载到物理服务器/PC 上,从 ISO 启动进行操作系统的安装;
  2. 作为软件安装源,比如红帽系列的版本,在 ISO 中,通常会有 Packages 路径,下面包含了当前版本完整的软件源,我们可以通过本地挂载的方式使用;
  3. 作为软件发行方式,常见的有 VMware vmtools, KVM Virtio 等统一打包方式;

制作方式

  1. genisoimage
  2. mkisofs

第一种方式不常用,主要使用第二种方式进行使用,之前的文章中提到过 LiveCD 制作方式也是通过 mkisofs 命令继续构建的。

这里我们可以参考 VMware 官方文档了解如何定制自己的 ISO。

说一下何时需要定制我们自己的 ISO。当我们获取到各个发行版本的 ISO 后,我们可以正常使用进行安装,但是都是交互式安装,需要我们通过图形化界面/命令行交互的方式进行配置确认及参数设置。如果我们想要 ISO 支持静默安装,那么就需要我们自己定制属于自己的 KickStart 脚本,然后放置到 ISO 中并设置为默认执行项。
此场景对于 *nix 系统均适用。

当前路径结构

1
2
3
4
5
6
7
8
9
10
11
12
13
test/
├── a.b00
├── ata_liba.v00
|...
├── char_ran.v00
├── efi
│   └── boot
│   ├── boot.cfg
│   ├── bootia32.efi
│   └── bootx64.efi
├── efiboot.img
├── hid_hid.v00
|...

执行 mkisofs 命令,指定 ISO 源文件路径,指定目标 ISO 名称,即可构建。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
[root@installer mnt]# mkisofs -relaxed-filenames -J -R -o custom_esxi.iso -b isolinux.bin -c boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table  test/
Warning: creating filesystem that does not conform to ISO-9660.
I: -input-charset not specified, using utf-8 (detected in locale settings)
Size of boot image is 4 sectors -> No emulation
2.89% done, estimate finish Sat Jan 12 21:56:27 2019
5.78% done, estimate finish Sat Jan 12 21:56:27 2019
8.66% done, estimate finish Sat Jan 12 21:56:27 2019
11.55% done, estimate finish Sat Jan 12 21:56:27 2019
14.44% done, estimate finish Sat Jan 12 21:56:27 2019
17.33% done, estimate finish Sat Jan 12 21:56:27 2019
20.21% done, estimate finish Sat Jan 12 21:56:27 2019
23.10% done, estimate finish Sat Jan 12 21:56:27 2019
25.98% done, estimate finish Sat Jan 12 21:56:27 2019
28.88% done, estimate finish Sat Jan 12 21:56:27 2019
31.76% done, estimate finish Sat Jan 12 21:56:27 2019
34.65% done, estimate finish Sat Jan 12 21:56:27 2019
37.53% done, estimate finish Sat Jan 12 21:56:27 2019
40.42% done, estimate finish Sat Jan 12 21:56:27 2019
43.31% done, estimate finish Sat Jan 12 21:56:27 2019
46.20% done, estimate finish Sat Jan 12 21:56:27 2019
49.08% done, estimate finish Sat Jan 12 21:56:27 2019
51.97% done, estimate finish Sat Jan 12 21:56:27 2019
54.85% done, estimate finish Sat Jan 12 21:56:27 2019
57.74% done, estimate finish Sat Jan 12 21:56:27 2019
60.63% done, estimate finish Sat Jan 12 21:56:27 2019
63.52% done, estimate finish Sat Jan 12 21:56:27 2019
66.40% done, estimate finish Sat Jan 12 21:56:27 2019
69.29% done, estimate finish Sat Jan 12 21:56:27 2019
72.17% done, estimate finish Sat Jan 12 21:56:27 2019
75.06% done, estimate finish Sat Jan 12 21:56:27 2019
77.95% done, estimate finish Sat Jan 12 21:56:27 2019
80.84% done, estimate finish Sat Jan 12 21:56:27 2019
83.72% done, estimate finish Sat Jan 12 21:56:27 2019
86.61% done, estimate finish Sat Jan 12 21:56:27 2019
89.49% done, estimate finish Sat Jan 12 21:56:27 2019
92.39% done, estimate finish Sat Jan 12 21:56:27 2019
95.27% done, estimate finish Sat Jan 12 21:56:27 2019
98.16% done, estimate finish Sat Jan 12 21:56:27 2019
Total translation table size: 2048
Total rockridge attributes bytes: 14593
Total directory bytes: 6144
Path table size(bytes): 50
Max brk space used 25000
173203 extents written (338 MB)

使用 file 命令查看构建结果:

1
2
[root@installer mnt]# file custom_esxi.iso 
custom_esxi.iso: # ISO 9660 CD-ROM filesystem data 'CDROM' (bootable)

定制 ISO 信息

通过上面的命令,我们构建了属于我们自己的 ISO,但是这时候出现一个需求,如果我们要对我们定制的 ISO 进行版本管理,要求可以通过 ISO 自动升级,那么我们在需要有渠道/方式获取 ISO 属性信息,比如:release date,size,dependency… 我们如何管理这些属性信息呢?

最简单的方式肯定是直接再编写一个文件,与 ISO 一一匹配,在文件中记录 ISO 相应信息,比如:

1
2
3
4
{"md5": "04db3dc9d872c382b1f42c9741815138",
"size": 354719744,
"dependency": ["yiran1", "yiran2", "yiran3"]
}

这样虽然简单实现了,但是很不方便管理,通过查看 ISO 9660 维基百科中的描述,可以看到有一个特性:

最开始的32768字节没有被ISO 9660数据结构使用,因此可以有其他用处,例如:CD-ROM可以在此区包含一个替代文件系统描述信息块,通常被混合光盘(Hybrid CD)用于提供Mac OS特定的内容。此未用块后面跟着一连串卷描述符,详细记录了该磁盘上的内容和类型信息(类似被FAT和NTFS格式磁盘使用的BIOS参数块)。

可以看到 ISO 前 32KiB 空间未被使用,Mac OS 早就利用这个空间来进行信息记录了,那么我们可以通过 dd 的方式来将二进制的信息写入 ISO 前 32KiB 空间中。
这样就避免了同时维护 ISO 及 ISO 对应 metadata 文件,管理起来更加方便。

参考链接

Shell cat 保存文件方式

背景

在 Shell 中保存文件可以通过 echo 保存一个字符串, cat 保存一个字符片段,最近在用 cat 编写 Nginx 配置文件的时候,想要写入 $test 类似字段,但是 Shell 会自动将其识别为变量而忽略,记录下该方式。

cat 编写文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
root@yiran-30-250:/tmp
$ cat cat.sh
#!/usr/bin/env bash

cat << EOF > /tmp/yiran
aaa
bbb
ccc
ddd
$eee
$fff

EOF
root@yiran-30-250:/tmp
$ bash cat.sh
root@yiran-30-250:/tmp
$ cat yiran
aaa
bbb
ccc
ddd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
root@yiran-30-250:/tmp
$ cat cat2.sh
#!/usr/bin/env bash

cat << 'EOF' > /tmp/yiran
aaa
bbb
ccc
ddd
$eee
$fff

EOF
root@yiran-30-250:/tmp
$ bash cat2.sh
root@yiran-30-250:/tmp
$ cat yiran
aaa
bbb
ccc
ddd
$eee
$fff

Wireshark 远程抓包方法

背景

最近在看 《Wireshark网络分析就这么简单》,目前读了 1/3,觉得作为一本工具介绍书籍还是不错的,至少可以让我更关注网络相关知识。
书中有大量的实例来讲解 Wireshark 的使用,及网络基础,在阅读的过程中,需要实际动手操作下,由于搜到的教程中没有正确操作的步骤,特此记录。

环境信息

  • 个人 PC
    • Windows 10
  • 服务器
    • CentOS 7

操作流程

(1)个人 PC

  1. 登陆 Wireshark 官网,下载最新版本软件包并安装
  2. 打开 Wireshark ,选择本机的网卡,并进行抓取,验证软件是否正常工作

(2)服务器

验证个人 PC 上软件正常后,我们需要在服务器安装 rpcapd,以便让 Wireshark 连接该服务器。

  1. 访问 rpcapd github
  2. 克隆相应代码 cd /tmp; git clone https://github.com/rpcapd-linux/rpcapd-linux
  3. 安装相应依赖 yum install glibc-static flex binutils
  4. 编译安装 libpcap cd /tmp/rpcapd-linux/libpcap ; ./configure && make
  5. 编译安装 rpcapd ,注意 README 中的 libcrypt.a in glibc-static is broken in ,remove -static in Makefile ,需要先将 Makefile 中的 -static 删掉,执行 make 即可
  6. 执行 ./rpcapd -n 可以看到程序正在运行:
1
2
3
4
root@yiran-30-250:/tmp/rpcapd-linux 
master ✗ $ ./rpcapd -n
Press CTRL + C to stop the server...
bind(): Address already in use (code 98)

(3)个人 PC

  1. 打开 Wireshark,点击 Capture –> Options –> Manage Interfaces –> Remote interfaces –> + –> 在 Host 输入对应服务器 IP 即可
  2. 在 Wireshark 首页上我们可以看到服务器上的相应网卡列表,选择我们想要抓取的网卡,即可抓取。

2018年读书记录

年终总结

  • 计划 30 本,实际25本。

根据 2017 和 2018 两年的状态来看,平均 20本左右是正常状态了,平时很少有长时间的阅读时间,今天这种元旦假期无人打扰的阅读时间更是少的可怜,还是要学会利用片段阅读。
调整下阅读策略,把想读的书调整为 1 本,防止过度堆积导致无法进行,希望 19 年可以有所改变。

“总要有点进步嘛”。

已读

  1. 《区块链》
  2. 《人工智能》
  3. 《哲学家们都干了些什么》
  4. 《大数据时代 : 生活、工作与思维的大变革》
  5. 《鞋狗 : 耐克创始人菲尔·奈特亲笔自传》
  6. 《大话存储(终极版) : 存储系统底层架构原理极限剖析》
  7. 《魔法时刻》
  8. 《大规模分布式存储系统 : 原理解析与架构实战》
  9. 《Python 自动化运维 : 技术与最佳实践》
  10. 《支付战争 : 互联网金融创世纪》
  11. 《遗落的南境1:湮灭》
  12. 《Effective Python : 编写高质量Python代码的59个有效方法》
  13. 《精通VMware vSphere 5》
  14. 《写给大家看的设计书(第3版)》
  15. 《算法图解》
  16. 《Fluent Python》
  17. 《C 程序设计语言》
  18. 《The Hitchhiker’s Guide to Python》
  19. 《Python Cookbook》
  20. 《高效程序员的45个习惯 : 敏捷开发修炼之道》
  21. 《Docker 进阶与实战》
  22. 《动物农场》
  23. 《爆款文案》
  24. 《Selenium 2自动化测试实战 : 基于Python语言》
  25. 《重新定义公司 : 谷歌是如何运营的》

在读

  • 《HTTP权威指南》
  • 《Go 语言圣经》

想读

  • 《大话数据结构》

cgroup 资源预留

cgroup 资源预留

背景

在之前的博客中,提到了 cgroup 中如何为自己的服务配置资源限制,比如 CPU,内存等,当时以为在 cgroup.conf 中配置的服务,那么相应绑定的 CPU 就归属于该服务,也就是资源预留,今天发现并不是这样,记录下如何通过 cgroup 做资源预留。

资源预留

在之前提到的博客中关于资源限制是这么配置的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[root@node 20:56:49 ~]$cat /etc/cgconfig.conf
# yiran cgroups configuration

group . {
cpuset {
cpuset.memory_pressure_enabled = "1";
}
}

group yiran {
cpuset {
cpuset.cpus = "0,1,2,3,4,5";
cpuset.mems = "0-1";
cpuset.cpu_exclusive = "1";
cpuset.mem_hardwall = "1";
}
}

group yiran/test {
cpuset {
cpuset.cpus = "0";
cpuset.mems = "0-1";
cpuset.cpu_exclusive = "1";
cpuset.mem_hardwall = "1";
}
}

我为 yiran 这个服务做了限制,其中分配给 yiran/test 的 CPU 是 0,并且配置了 cpuset.cpu_exclusive 选项。注意,这里的 exclusive 的意思并不是说分配到 yiran/test 中的服务独自占用该线程,而是说分配到 yiran/test 中的服务只会占用该线程,不会侵入到其他线程中,也就是通常我们说的 Limit。
可以通过 stress 命令来进行验证:

1
2
3
# 对一台配置好 cgroup 的物理服务器 CPU 加压
[root@node 21:05:37 ~]$stress -c 24
stress: info: [4033] dispatching hogs: 24 cpu, 0 io, 0 vm, 0 hdd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 新开终端,执行 top 命令查看 CPU 情况
[root@node 21:05:37 ~]$top
top - 21:06:35 up 5 days, 9:03, 3 users, load average: 32.58, 19.04, 15.43
Tasks: 449 total, 36 running, 412 sleeping, 0 stopped, 1 zombie
%Cpu0 : 89.0 us, 8.6 sy, 0.0 ni, 2.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 99.7 us, 0.3 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu2 : 90.4 us, 8.9 sy, 0.0 ni, 0.3 id, 0.3 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu3 : 95.4 us, 4.6 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu4 : 84.4 us, 13.6 sy, 0.3 ni, 1.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu5 : 46.5 us, 36.0 sy, 1.7 ni, 15.8 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu6 : 93.4 us, 6.6 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu7 : 94.4 us, 5.6 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu8 : 88.0 us, 12.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu9 : 88.7 us, 11.3 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu10 : 88.4 us, 10.9 sy, 0.0 ni, 0.3 id, 0.0 wa, 0.0 hi, 0.3 si, 0.0 st
%Cpu11 : 85.4 us, 13.9 sy, 0.7 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu12 : 98.7 us, 1.3 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu13 : 39.0 us, 12.0 sy, 0.0 ni, 49.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu14 : 92.7 us, 6.9 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.3 si, 0.0 st
%Cpu15 : 98.7 us, 1.3 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu16 : 97.0 us, 3.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu17 : 97.0 us, 2.3 sy, 0.0 ni, 0.3 id, 0.0 wa, 0.0 hi, 0.3 si, 0.0 st
%Cpu18 :100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu19 : 93.7 us, 6.3 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu20 : 88.4 us, 11.6 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu21 : 89.4 us, 10.6 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu22 : 88.1 us, 11.9 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu23 : 90.4 us, 9.6 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st

那么我们如何确保自己的服务有足够的资源运行呢? 比如像 vSphere ESXi 上面可以针对某些虚拟机进行具体的资源预留(CPU,内存,磁盘等)。
其实只要将其他资源也限制在一个 cgroup 中就可以了。

  1. /etc/cgconfig.conf 中创建一个新的 cgroup,将系统上其余的资源分配给该 group,如创建 yiran/others 并分配 3个 线程;
  2. 修改 /etc/cgrules.conf ,将系统其它所有服务分配到步骤1创建的 group 中,如:
    1
    *       cpuset       yiran/others

修改配置完成后,分别重启 cgredcgconfig 使配置生效:

1
2
systemctl restart cgred
systemctl restart cgconfig

此时我们再通过 stress 进行验证

1
2
[root@node 21:09:29 ~]$stress -c 24
stress: info: [12475] dispatching hogs: 24 cpu, 0 io, 0 vm, 0 hdd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
top - 21:11:58 up 5 days,  9:09,  3 users,  load average: 38.11, 30.37, 21.48
Tasks: 445 total, 38 running, 406 sleeping, 0 stopped, 1 zombie
%Cpu0 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 :100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu2 : 0.0 us, 0.0 sy, 0.3 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu3 : 0.0 us, 0.3 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu4 : 88.1 us, 11.9 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu5 : 81.9 us, 16.1 sy, 1.6 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.3 si, 0.0 st
%Cpu6 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu7 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu8 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu9 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu10 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu11 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu12 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu13 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu14 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu15 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu16 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu17 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu18 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu19 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu20 : 0.0 us, 0.0 sy, 0.3 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu21 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu22 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu23 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 13174792+total, 11522948+free, 6439344 used, 10079088 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 12406676+avail Mem

我在 yiran/others 中配置了 3 个线程,那么此时哪怕我执行 stress worker 为 24,也只会在这3个线程中,不会对其他 cgroup 服务产生干扰,从而达到了资源预留的效果。