Skip to content

Instantly share code, notes, and snippets.

View jschwinger233's full-sized avatar
😭
On vacation

graymon jschwinger233

😭
On vacation
  • China
View GitHub Profile

最近一周一直在查 calico SDN 挂掉的问题, 过程十分艰辛, 回头想了想觉得可以好好反思一下.

优先尝试进行稳定复现

太沉迷使用各种工具, 倒不是坏事, 只不过性价比太低了.

在开始怀疑 是自己 restart 服务导致的 之后, 就应该立刻开始进行集群的复刻和复现测试:

  1. 复刻: 完全相同的机器上, 使用完全相同的配置(除了端口和落盘目录等), 启动几乎等价的集群, 导入完全相同的数据
  2. 简化: 完全复刻客户端需要启动 calico-felix, 想到一台机器部署两个 felix 是需要技巧的, 不如先简单尝试一下 etcdctl 能否复现, 因为成本低, 就算不能也能快速回到正确的轨道进行完全复刻

KVM 极简入门

一. QEMU

QEMU (Quick Emulator) 提供了硬件和处理器虚拟化, 它跑在用户态, 无需内核.

话不多说, 直接用起来. 我使用的 OS 是 Ubuntu16.

1.1 安装

手动实现 bridge 网络和 calico 网络

本质上要解决的问题是连通 host 和 netns, 来体会一下两者的不同思路.

Bridge

用变量方便大家阅读, 否则区分不出语法和变量

NS=dock-ns3

WHO NEEDS CONTAINERD

标题来自 XCOM2 的成就之一 WHO NEEDS TYGAN, 要求在最后一关使用初级科技通关, 简直恶趣味..

总之成功用 runc 调用 CNI 建立了 calico 网络, 流程是这样的:

  1. 安装 runc, calico, etcd, 创建 calico ippool:
- apiVersion: projectcalico.org/v3
 kind: IPPool

分层和解耦对我的诱惑太大了, 最近写业务有一些想法.

UI -> App

比方说一个服务有两种协议 http 和 grpc, 再加上一个 cli 吧, 算是三种完全不同的 UI 层了, 共享同一个 App 层的函数:

type App interface {
    func CreateInstance(name string, count, memory, cpu int, network, podname string, dns, env []string) *Instance
}

没想到一个 open files limit 都能让我有这么多惊喜.

  1. ulimitrlimit 是一个东西, 有些文章说 ulimit 代表的是 user limit, 限制的是一个 effective user 的总资源, 而 rlimit 限制的是一个进程的资源, 这是错的.
  2. strace -fT ulimit -n 是不行的, 因为 ulimit 是 shell builtins, 所以 strace ulimit 直接报错说找不到命令; 同理 which ulimit 也没有输出(我第一次知道 builtins 是不能 which 的)
  3. man 3 ulimitThis routine is obsolete. Use getrlimit(2), setrlimit(2), and sysconf(3) instead, 有理由相信 ulimit 就是 rlimit
  4. 直接看 bash 源码, 在 builtins/ulimit.def 里可以清楚看到 ulimit 就是调用 rlimit, 第一个问题解决了, 以下讨论 rlimit.
  5. rlimit 是限制用户 / 会话 / 进程组的吗? 只有 RLIMIT_NPROC, RLIMIT_MSGQUEUE, RLIMIT_SIGPENDING 是按照 real user ID 限制的多个进程的, 分别限制的是进程数, POSIX 消息队列字节数, 排队信号数, 其他都是限制单进程.
  6. /proc/$PID/limitulimit 的关系是对应的, 但是如果你在 shell 里 ulimit -a 看到的东西和一个进程的 /proc/$PID/limit 是不一样的, 不要慌, rlimit 是继承的, 所以看看父进程, 看看不同用户的 rlimit 是否不一样, 看看进程自己是否修改了.
  7. docker run --ulimit 是在 docker 层做的! 我意思是, 这不是交给 containerd 和 runc 做的, runc 对此一无所知. 不过具体在哪一步设置的我还不清楚, 我的 docker 解剖计划已经拖延很久了.
  8. 进程出现了 `accept4: too many open files in s

安利浏览器插件 https://github.com/tridactyl/tridactyl/blob/master/readme.md

作为 vimium 的长期用户, 这次是瞬间抛弃 chrome, 只为使用 tridactyl.

highlights:

  1. 纠正了若干 vimium 的错误的 key bindings:
    1. vimium <S-^> 是在 latest tab 之间跳转, vim 里明明是 <C-^>, tridactyl 把这个纠正过来了
    2. vimium u / d 的功能是 vim 里的 <C-u> / <C-d> 翻页, tridactyle u 是恢复最近关闭的 tab, d 是关闭当前 tab
  2. 可以在输入框里 edit in vim!

同事说, curl -v http://10.22.12.87:2376/version 返回 400, 问我怎么用 curl 访问 dockerd.

我的思路是先想办法通过 sdk 访问 version api, 然后 tcpdump 观察一下 url 就可以了, 所以:

  1. pip install docker, python repl 里 docker.from_env().version()
  2. tcpdump -i any port 2376 却什么都看不到, 怀疑 from_env() 走的 unix socket
  3. lsof -p + ss -xp 果然是在的 unix socket, 那我抓个 unix socket 的包吧
  4. socat TCP-LISTEN:2375,fork UNIX-CONNECT:/var/run/docker.sock, 然后 python repl 里 docker.DockerClient(base_url='tcp://localhost:2375').version()
  5. 这次可以抓到包了, 一个简单的 GET HTTP, curl 却发现报错说没有证书
  6. 没证书会返回 400? 我自己手动试了一次 curl -v http://10.22.12.87:2376/version, 发现并没有 400, 而是直接说 Client sent an HTTP request to an HTTPS server.

如何查 unix socket 的 peer

比放说我想确认一个进程是否连接了 unix:///var/run/docker.sock, 对于 TCP 来说 lsof -p$PID | grep $PORT 立刻就能查到, 对于 Unix Socket 来说就不行了.

做法应该是这样的:

  1. lsof 找到这个进程所有的 Unix + SOCK_STREAM 的 sockets, 倒数第二列是 Node Number
  2. ss 找到这个 node number 的 peer
  3. 确认 peer 是 docker sock
@jschwinger233
jschwinger233 / container-performance-investigation.md
Last active March 19, 2020 17:26
app performance problem in container

协助排查了一次容器内进程的性能问题

症状: 平台上容器内的 redis cluster 进程用 redis-benchmark 测试能稳定复现 spike, 但是同宿主机上的 redis 没有问题

  1. 首先怀疑 SDN, 因为没问题的 redis 是 host 网络. 理论上这很难查, 但是在部署了一个 SDN 网络的 redis 后发现依然没有问题, 我比较有把握不是网络问题, 不过也不是特别确定, 因为 redis cluster 的节点跨主机, 说不清楚会发生什么.
  2. 建立最小可复现问题的现场. 我们应该从几乎一模一样的环境(相同的容器里)重建一个拥有相同节点, 相同配置, 相同数据, 只是监听不同端口的集群, 理论上这个集群应该是能复现问题的.
  3. 然后一点点缩小问题的范围, 看到那一步就突然不能复现
    1. 摘掉所有的 slave nodes
    2. 把进程放到宿主机裸跑
  4. 把集群放到同一个宿主机