日常小问题收集

网络协议

TPC

TPC 三次握手过程

A -> SYN -> B 
A <- SYN,ACK <- B
A -> ACK -> B

A 发 SYN 包给B:A(LISTEN -> SYN_SENT)
B 收到 SYN 包: B (LISTEN -> SYN_REVD)
B 发 SYN,ACK 包给A,A收到包: A (SYN_SENT -> ESTABLISHED)
A 发 ACK 包给B,B收到包:B(SYN_SENT -> ESTABLISHED)

TPC 四次分手过程

A -> FIN -> B
B -> ACK -> A
B -> FIN,ACK -> A
A -> ACK -> B

A 发 FIN 包给B:A(ESTABLISHED -> FIN_WAIT_1)
B 收到 ACK 包: B (ESTABLISHED -> CLOSE_WAIT)
B 发 ACK 包给A,A收到包: A (FIN_WAIT_1 -> FIN_WAIT_1)
B 发 FIN,ACK 包给A: B (CLOSE_WAIT -> CLOSED)
A 收到FIN,ACK 包: A (FIN_WAIT_1 -> TIME_WAIT)
A 发 ACK 包给B,B收到包:B(LAST_ACK -> CLOSED)

TCP Close是因为服务端有个关闭的过程,所以在收到包后会先应答一个ACK,等应用程序处理结束后再发一个 FIN,ACK 包。

iptables问题

对于长连接为何修改ep不能生效

我们知道k8s的service本质是通过和endpoint构成iptables转发规则来做到为应用做负载均衡,原理如下:

                   
获取 clusterIP 和 ep的目标地址ip
┌────────────────────┐
│ │
│ │
│ │
│ │
│ │
┌──────▼────────┐ ┌─────┴──────┐
│ kube-proxy │ │ service
│ │ │ │
└──────┬────────┘ │ endpoints │
写入iptabltes │ └────────────┘
┌──────▼───────┐
│ node │
│ │
│ iptables │
│ │
└──────────────┘

create by asciiflow.com

kube-proxy 会从 api-server watch 到 service 的 clusterIP 和 endpoint 的目标IP ,将其转换为 iptables 规则写入节点。

基于这个原理,今天有个同事希望通过修改直接 endpoint 目标端的地址来切换目标,但他发现一直没成功,在切换了 endpoint 地址后,应用还是访问旧的地址。

后面查看是因为应用和目标地址是长连接,在应用pod里面通过netstat可以看到,应用有一个和 clusterIP 的 ESTABLISHED 表明已建立的连接,到目标地址上通过 netstat 可以看到有一条和 pod IP 的 ESTABLISHED。

那么为什么长连接就不会被iptables 的 nat 转到新的IP呢,我们看看kube-proxy 向 iptables 写入的规则:

# 先看filter表
iptables -S -t filter | grep ctstate
-A INPUT -m conntrack --ctstate NEW -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A INPUT -m conntrack --ctstate NEW -m comment --comment "kubernetes externally-visible service portals" -j KUBE-EXTERNAL-SERVICES
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -m conntrack --ctstate NEW -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A OUTPUT -m conntrack --ctstate NEW -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A KUBE-FIREWALL ! -s 127.0.0.0/8 -d 127.0.0.0/8 -m comment --comment "block incoming localnet connections" -m conntrack ! --ctstate RELATED,ESTABLISHED,DNAT -j DROP
-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack pod source rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack pod destination rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

可以结合 netfilter 结构图来看:

首先我们看从应用发出的流量:
从应用端发出的流量会经过 output 然后到路由检查,再通过 postrouting 直接出去。
如果是已经建立的连接,在 filter 表的 output 链上并不会进入到KUBE-SERVICES,而是直接被ACCEPT了:

$ iptables -S -t filter | grep OUTPUT
-P OUTPUT ACCEPT # OUTPUT链默认规则
-A OUTPUT -j CLOUDWALKER-HOOK-3
-A OUTPUT -m conntrack --ctstate NEW -m comment --comment "kubernetes service portals" -j KUBE-SERVICES # ctatate 为新建连接才会进入KUBE-SERVICES链条
-A OUTPUT -j KUBE-FIREWALL

数据库

MYSQL

MySQL 索引优化:

  • 尽量利用B+树的最左前缀优化
  • 利用覆盖索引

语言

golang

golang debug 工具 dlv 可以非常方便 go 程序做单步调试。

工具安装:

$ git clone https://github.com/go-delve/delve
$ cd delve
$ go install github.com/go-delve/delve/cmd/dlv

clusterClient.submitJob(jobGraph) -> 异步将 jobGraph 序列化成一个bin二进制文件 -> POST 到 v1/jobs 路径

运维

k8s

kubectl的last-applied-configuration引起的问题

kubectl 如果直接 apply 导出带resourceVersion等字段的yaml,会导致下次 apply 不带resourceVersion报错Invalid value错误。
最好的方法是导出的时候使用:

kubectl get <资源> -ojson -A | jq 'del(.items[] | .metadata.resourceVersion,.metadata.uid,.metadata.managedFields,.metadata.annotations["kubectl.kubernetes.io/last-applied-configuration"],.status,.metadata.ge
neration,.metadata.creationTimestamp)'
```
如果apply已经报错了,恢复方法:
```bash
kubectl get <资源> -ojson -A | jq 'del(.items[] | .metadata.resourceVersion,.metadata.uid,.metadata.managedFields,.metadata.annotations["kubectl.kubernetes.io/last-applied-configuration"],.status,.metadata.generation,.metadata.creationTimestamp)' | kubectl replace -f -

shikanon wechat
欢迎您扫一扫,订阅我滴↑↑↑的微信公众号!