TGTGInsighttelegram intelligenceLIVE / telegram public index
Back to channels
Welcome to the Black Parade avatar

TGINSIGHT CHAT

Welcome to the Black Parade

@TheB1ackParade

Music

Death has many faces, I look forward to seeing this one.

Subscribers787Current channel subscribers
Tracked posts907Indexed post count
Recent reach1,375Sum of recent post views
Recent posts

Recent posts

Page 74 of 76 · 907 posts

Posted Feb 26

strace -p$PID -fT -etrace=$SYSCAL -eread=$FD 今天查 bug, 查到最后败在了不会熟练使用 strace, 最后重启大法毁尸灭迹, 菜到口交. 总之 -p$PID attach 进程, -f trace 所有线程和子进程, -T 打印 syscal 的调用时间, -etrace=$SYSCAL 只追踪自己感兴趣的 syscall, -eread=$FD 只关注自己感兴趣的 fd 并且把所有 ascii 字符串打印出来, 如果我在三小时前能够熟练使用… 也没卵用, 反正接下去还是肯定查不出来, 太难了这次的 bug. @LiBo 明查秋毫, 当我告诉他 strace etcdctl watch 的输出里连 read 都没有这种可怕的事情之后, 他只花了我吃一根冰淇淋的功夫就反应过来是只 trace 了主线程…. tql, 我就只会问人, 太菜了, API 程序员…

122 views

Posted Feb 25

惊人的事实: sshd 居然没有 idle connection timeout! 几乎所有人都经历过 ssh remote shell 放置一段时间就连接断掉的情况, 记得刚工作的时候配置 terminal 的 ssh 保活是我的必备, 我一直以为 sshd 有 idle timeout 这一功能, 就像 redis 的 TIMEOUT 一样, 今天发现居然并没有. 唯一相关的三个选项是: TCPKeepAlive Specifies whether the system should send TCP keepalive messages to the other side. If they are sent, death of the connection or crash of one of the machines will be properly noticed. However, this means that connections will die if the route is down temporarily, and some people find it annoying. On the other hand, if TCP keepalives are not sent, sessions may hang indefinitely on the server, leaving “ghost” users and consuming server resources. The default is “yes” (to send TCP keepalive messages), and the server will notice if the network goes down or the client host crashes. This avoids infinitely hanging sessions. To disable TCP keepalive messages, the value should be set to “no”. This option was formerly called KeepAlive. ClientAliveCountMax Sets the number of client alive messages (see below) which may be sent without sshd(8) receiving any messages back from the client. If this threshold is reached while client alive messages are being sent, sshd will disconnect the client, terminating the session. It is important to note that the use of client alive messages is very different from TCPKeepAlive (below) (above). The client alive messages are sent through the encrypted channel and therefore will not be spoofable. The TCP keepalive option enabled by TCPKeepAlive is spoofable. The client alive mechanism is valuable when the client or server depend on knowing when a connection has become inactive. The default value is 3. If ClientAliveInterval (see below) is set to 15, and ClientAliveCountMax is left at the default, unresponsive SSH clients will be disconnected after approximately 45 seconds. This option applies to protocol version 2 only. ClientAliveInterval Sets a timeout interval in seconds after which if no data has been received from the client, sshd(8) will send a message through the encrypted channel to request a response from the client. The default is 0, indicating that these messages will not be sent to the client. This option applies to protocol version 2 only. tcpkeepalive 是在 tcp 层做保活, clientalive 是在 ssh 层做保活, 断开连接的情况仅限于心跳检查失败 sshd 才主动断连接, 并不是 idle timeout. 也就是说大家所遭遇的 idle timeout 都是防火墙搞的….

129 views

Posted Feb 24

睡觉的时候想到, 就算是 cnm 其实也是在 oci hook 里执行的, 只不过使用的 hook 是 prestart, prestart 是在 runtime namespace 执行的, 也就是还没创建 net ns, 这就没办法了; 如果使用的 hook 是createContainer 那就好办.

111 views

Posted Feb 23

cmgs 之前提过一个想法是让 cnm 调用 cni, 那么根据我目前的理解, 这件事不会特别好做. 首先 cnm 是更加细粒度的 api, 要用 cni 实现 cnm 是无法准确分步实现的. 其次 cni 的接口是要求传入 container_id 和 netns_path, 那么问题来了, 我没有创建容器哪来的 network namespace, 所以你才会看到 K8s 的 pod 这垃圾玩意儿, 先创建一个 pause 容器, 那不就有 netns 了嘛…. 他妈还发博客说什么 cnm 和 docker 耦合太紧所以采用了 coreos 的 cni, 结果你自己的接口又完全和 pod 模型耦合, 吃屎吧. 当然我本可以.. 先创建一个 netns, 让容器 attach 上去就可以了, 然而 docker run api 没有这种选项, 我去, 就连自定义 cnm 都实现不了, 创建 netns 是硬编码的实现, 是插件之外的实现, 操, 那么唯一的办法就是我在上面的那样了, 先创建一个 sleep 的容器, 然后就可以 docker run --net container:$ID, 这不就是 pod 模型了嘛! 妈的 其实 oci 这种完全中立的标准是一点问题都没有的, 在 runc init 之后, netns 创建好, 但是应用进程没有启动, prestart hook 运行 cni 还是 cnm 都刚刚好, 但是 docker 硬耦合 cnm, containerd 只有 cni, 我面对这一堆开源电子垃圾真是锤胸顿足. 结论, cni 几乎无法实现 cnm.

122 views

Posted Feb 23

徒手操作 CNI 的流水帐记录 1. docker, calicoctl, calico-node without libnetwork, ippool, default profile 2. cat > /etc/cni/net.d/10-calico.conf <<! { "name": "cni-calico", "cniVersion": "0.3.0", "type": "calico", "etcd_endpoints": "http://127.0.0.1:2379", "log_level": "debug", "ipam": { "type": "calico-ipam", "assign_ipv4": "true", "ipv4_pools": ["a15"] } } ! 3. [email protected]:containernetworking/cni.git 4. docker run -d --net=none busybox:latest /bin/sleep 10000000 ln -sf /proc/$PID/ns/net /var/run/netns/$CONTAINER ip netns exec $CONTAINER ip a ./exec-plugin.sh add $CONTAINER /var/run/netns/$CONTAINER 就可以看到 namespace 内的 eth0 分配有指定 ipv4 pool 的 ip 了

113 views

Posted Feb 23

我司的编排系统目前的三大诉求 1. 固定 IP 背景是流行的容器引擎只有 start / stop API, 我们在有些场景需要一个 replace API, 重点是新容器能够继承旧容器的 IP 和 namespace, 难点是固定 IP. 真实的应用呢, 可以考虑创建容器化 etcd 集群, 无论是初始化还是动态扩节点都需要节 etcd 启动时就知道自己的 IP (要在命令行或者配置文件里硬编码写上), 在古典 docker 容器下这是不可能的, 所以我们希望可以先生成一个 init container, 拿到 IP, 然后 replace, 这样就可以构造出配置或命令行参数来运行服务. 从更大的视角看, k8s 不需要解决这个问题, 因为它是 pod 模型, 所以在启动应用的时候可以通过 hook 拿到 pause 容器的 IP; 但是 k8s 的问题是, 它不是通用编排系统, 不是为了支持 vm 等其他 runtime 而设计的, 当然可以支持 vm 而且已经又开源的 operator, 但是居然也要集成到 pod 模型下, 想象一下 vm 还要有一个 pause 容器组成 pod, 用 cmgs 的话说就是脱了裤子放屁. (当然好处不是没有, 这里不说了, vm 本身我的理解也很小白, 再过一段时间有机会恶补一下) 从容器标准 OCI 来看, 所有的这一切都不过是 hooks 而已, 要知道就连容器的网络 link 都是通过 prestart hook 搞进去的, 根本不是什么神奇的操作, 愿意的话只要在 sfterstop 里分别 hook 归还与否 ip. 不过实际来做的话却又不是那么简单, 因为几乎不会有人直接对接 runc, 否则光是 daemon 管理这一套都能让一群 web 程序员捉襟见肘; 那么更高一层的 containerd 眼下就很流行, 但是我看了一下发现 docker -> containerd 迁移成本其实也很大, 不说别的, containerd 压根从设计上就不打算暴露服务给 remote, 所以只提供 unix socket 监听, 所以先套一层服务封装一下, 这特么不就是 dockerd 了吗… 那么继续用 dockerd 的话, 压根连 hook 的 API 都没有暴露, 被 dockerd 吃掉了, 通过 oci 直接来做固定 IP 是行不通了. 不过如果继续用 dockerd 的话, 倒是可以在网络插件上动手, 对接 calico 的 CNM 插件是我们完全自控的组件, 只要能够在归还 ip 时并不实际归还给 calico ipam, 采取一个延迟归还, 或者 using -> released -> free 三级状态, 容器只是 stop 的时候 ip 进入 released 状态而不是 free, 那么可以很容易实现固定 IP. 细节不说了. 然而 cmgs 的态度是要把 dockerd 包一层, 用自己包过的 dockerd 提供服务的话, 我们可以直接为编排系统提供原生实现的 replace 接口, 简直丧心病狂, 但是实际做起来的时候由于 CNM 是内置在 dockerd 里面的, 所以也挺别扭, 我的态度是会花巨大的力气做一个看起很棒但其实限制依然很多的东西, 还不如直接在 CNM 插件上改呢. 如果说 CNM 做固定 IP 很 tricky, 那么直接用 CNI 就非常干净了, 只不过 dockerd 使用 CNI 又会有一种脱了裤子放屁的感觉… 官方做法又是先创建一个 namespace 容器, 然后 CNI 才能创建对应的 link 获取 ip 什么的; 这一点倒是可以通过裸创建 ns 来规避, 问题不大, 但是额外的工作可能是有的. 但我还是觉得比包一层 dockerd 要好啊! 2. diskquota 在我做好了把磁盘作为资源进行调度之后, 缺少的东西就是引擎对 quota 的支持的, 比如 /sda1:/data:rw:10G 这样的 volume 请求, docker 是不认识的. shopee sto 这群一言不合就 fork 然后自己改的家伙已经长期使用魔改后的 docker-ce 了, 其中就加入了对 xfs quota 的支持, 我看了一下代码, 居然是在 graphdriver 里的 quota controller 里改的, 其实挺, 不太好的, graphdriver 是 docker1.1 接口化的成果, 就是为了让人可以有接口协议做插件而不是硬编码改 docker-ce, 我也向来反对侵入式修改. 不过 cmgs 对这件事的看法居然也是把 dockerd 包一层…. 我…. 且不说 graphdriver 了, 我们直接用 prestart hook 调用一下 xfs quota 有何不好, 有现成的接口不用非要重新造轮子… 当然 dockerd 怎么去给 containerd 的 hook 加上 xfs quota 是个问题, 不过是可以想办法做的. 3. vm as runtime 意思是把 vm 作为 container 的代替物, 跑应用的时候先拉个 vm 起来, 里面用 systemd 创建应用进程, 达到完全的资源隔离. 实现这玩意儿我唯一的问题在于选型, 因为已经有符合 oci 的 vm 实现, 甚至连 hypervisor 都是可配置的, 那么是不是直接用这个会更简单呢, 毕竟因为 dockerd 也可以直接更改 oci 实现. 这倒是小问题了, vm 这一套本来我虾的政策就是放到 mesos 去管理而不是我们, 我们只是做得好玩….

115 views

Posted Feb 20

在测试 cgroup 对内存和 cpu 限制的时候想让进程分别消耗内存和 cpu, 查了一下非常简单: 1. cpu consuming: yes > /dev/null &, 要几个核跑满就运行几个 2. memory consuming: tail /dev/zero

134 views

Posted Feb 18

先说问题吧, 一个月前 techops 找过来问我为什么他部署 etcd by systemd 跑不起来, 我一看 service file 是这样的: [Unit] Description=etcd key-value store Documentation=https://github.com/etcd-io/etcd After=network.target [Service] User=etcd Type=notify Environment=ETCD_DATA_DIR=/var/lib/etcd Environment=ETCD_NAME=%m ExecStart=/bin/sh -c '/usr/bin/etcd gateway start --endpoints=http://10.143.203.164:2379 --listen-addr 127.0.0.1:2388' Restart=always RestartSec=10s LimitNOFILE=40000 [Install] WantedBy=multi-user.target 本质上就是把官方仓库里的 service file 改成了运行 etcd gateway 而已, 但是出现的问题是 systemctl start etcd会卡住, 命令不输出, 不返回, 只能打断退出. 那么这里的问题有两点, 第一是 /bin/sh -c, 把它删掉, 或者换成 /bin/bash -c 都能解决问题. 第二个问题是 Type=notify, 先说这个吧. systemd 的 service type 分为几类, 默认是 simple, 然后常用的有 forking, exec, notify, oneshot, 这里说 notify 类型, 简单来说它要求 ExecStart= 的进程通过 sd_notify (https://www.freedesktop.org/software/systemd/man/sd_notify.html#)来通知 systemd "我好了", 然后 systemd 才认为服务是 running, 否则一直是 activating, 而且 systemctl start 也不会返回; 这一点和默认的类型 simple 不一样, simple 可不管死活, 从 systemd 进程 fork 成功就 running 状态了, 甚至不管 exec 是否失败. etcd 本身是实现了 sd_notify 那一套的, 可以看这里: https://github.com/etcd-io/etcd/blob/master/etcdmain/main.go#L50, sd_notify 呢又要求读到环境变量 $NOTIFY_SOCKET 来报告状态, 这些都没问题. 但是问题出在 /bin/sh 也就是 dash(1) 上面, 和 bash 不一样, 对于 -c这个 option, dash 会 fork 子进程而 bash 原地 exec, 这一点不论是通过查看进程树还是 strace(1) 都很容易确认; 而偏偏 systemd 只接收来自自己 fork 出来的子进程的状态, 对于 sh 又 fork 出来的 etcd 的消息是无视了的, 这一点可以通过 systemctl status 确认; 所以就导致了始终无法启动, 但是改成 /bin/bash -c 或者裸跑就可以了. systemd 也算是博大茎深了, 里面对进程的各种处理方式非常值得学习, 如果你高兴的话甚至可以通过 ExecPreStart= 集成 CNM 或者 CNI 来做 SDN, 非常有趣了, 我还在一点点实践当中, 后续应该会有更多的记录.

130 views

Posted Feb 18

自己经常有需求是重新部署一个 docker 容器, 但是又不知道当初创建时的参数是啥, 当然我可以通过 inspect 去抠出一堆参数, 但是太恶心了, 查了一下果然有工具能做这件事, 叫做 runlike: https://github.com/lavie/runlike 不过要注意两点: 1. docker image 版本严重滞后最新版本, 比如我简单试用了一下就发现 --log-driver 再镜像版本里没有了, 但是 pip install 的版本依然有, raise 了 issue: https://github.com/lavie/runlike/issues/60 2. 实现得非常粗糙, 比如在查 --log-driver 的时候看到当时的 PR: https://github.com/lavie/runlike/pull/35/files, 居然是硬编码的 if log_type != 'json-file’, 那如果 docker 配置的默认 log driver 不是 json-file 那这里的逻辑就完全不对. 所以用的时候还是小心一点, 多检查几遍.

123 views

Posted Feb 17

预告一下接下来的几个内容(免得自己太懒跳票了, 所以先立 flags). 1. runc 2. systemd 的小坑 3. calico 的进一步细节: ToR, eBGP, RR 4. 安卓应用道德委员会 (Android Applications Ethics Commission)

46 views

Posted Feb 17

最近在解锁 systemd, 工作内容可以看成把 systemd 包装成 OCI, 之前立下的 flags 总算能兑现其中一个了….

107 views

Posted Feb 17

背到 setjmp / longjmp 的时候我想, 是不是用这两个 syscall 可以实现 coroutine 呢? 查了一下, 发现了一个废弃的了 syscall ucontext.h 系列: https://pubs.opengroup.org/onlinepubs/7908799/xsh/ucontext.h.html, 在 SUSv3 里还是标准, v4 就因为兼容性废弃了, 可惜啦. 简单看了一下, 非常简单好用, 比方说我要实现 greenlet 里最简单的示例: from greenlet import greenlet def test1(): print(12) gr2.switch() print(34) def test2(): print(56) gr1.switch() print(78) gr1 = greenlet(test1) gr2 = greenlet(test2) gr1.switch() 这段代码会打印 12\n56\n34\n, 而 78\n 是不会到达的, 具体的解释可以去看: https://greenlet.readthedocs.io/en/latest/ 那么这段代码用 ucontext.h 提供的 syscall 是很容易实现, 也很容易看懂: #include <ucontext.h> #include <stdio.h> #include <stdlib.h> #define handle_error(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0) static ucontext_t uctx_main, uctx_test1, uctx_test2; static void test1(void) { printf("%d\n", 12); if (swapcontext(&uctx_test1, &uctx_test2) == -1) handle_error("failed to swapcontext"); printf("%d\n", 34); } static void test2(void) { printf("%d\n", 56); if (swapcontext(&uctx_test2, &uctx_test1) == -1) handle_error("failed to swapcontent"); printf("%d\n", 78); } int main(int argc, char *argv[]) { char test1_stack[16384]; char test2_stack[16384]; if (getcontext(&uctx_test1) == -1) handle_error("failed to getcontext"); uctx_test1.uc_stack.ss_sp = test1_stack; uctx_test1.uc_stack.ss_size = sizeof(test1_stack); uctx_test1.uc_link = &uctx_main; // test1 returns and comes back to main makecontext(&uctx_test1, test1, 0); if (getcontext(&uctx_test2) == -1) handle_error("failed to getcontext"); uctx_test2.uc_stack.ss_sp = test2_stack; uctx_test2.uc_stack.ss_size = sizeof(test2_stack); // test2 returns and comes back to main uctx_test1.uc_link = &uctx_main; makecontext(&uctx_test2, test2, 0); if (swapcontext(&uctx_main, &uctx_test1) == -1) handle_error("failed to swapcontext"); return 0; } 就是代价行为, 代码也不别扭, 很正常的 coroutine 书写逻辑. 以此为基础, 要构建上层 API 就不是问题了, 首先是 Greenlet.switch(*args, **kwargs) -> Any 熟悉 Python yield 的同学应该知道这对应的是 coroutine.send(value)操作, 用 ucontext 实现起来没问题, 不赘述. 然后是高阶 API, 比如 Gevent 或者 Go 所提供的 用同步逻辑写 coroutine 的超能力, 只要对可 select 的阻塞调用包装一层就可以了, 属于 1->2 的功能. 这简直就是对我这种没学过汇编的 API 小子的福利啊, 我感觉自己又可以造协程轮子了, 逃!

123 views
12•••5•••10•••15•••20•••25•••30•••35•••40•••45•••50•••55•••60•••65•••70•••7273747576