calico network

在此对calico的网络模式做一个简单的回顾。

  • calico 支持 vxlan和IP in IP两种协议,默认是IP in IP协议的IPIP模式,在不支持IP in IP的情况下,会切换为vxlan,比如azure目前就不支持IP in IP。
  • xlan协议下,报文首部比IP in IP协议的首部要占用多空间。所以此处涉及到集群中网络数据包的MTU Size。
  • 对于calico, 在IP in IP协议下,还能再分为两种模式IPIP 以及BGP。 前者,基于报文首部封装,需要隧道封装。一个基于二层网络,直接而成可达。

ipip为什么称之为隧道呢?

ipip和vxlan的wiki上,都涉及到tunnel。

其实隧道并不能改变网络质量。只是营造出两个节点像是中间像是有一条隧道般,直接连接起来。

比如xlan,就是通过三层网络实现虚拟的二层的端对端连接。 IPIP也是一样。


Nginx HTTP2.0

nginx 开启http2.0

前提:

2020年4月13日 下午12:13:38 Update:

在CLOUDFRONT–> ELB–〉nginx–〉 springcloud 的架构下。 https到了Cloudfront就被卸载了,elb往后的所有都是走http协议。意味着其中只有ELB到客户端才是http2.0协议,ELB后的都是http1.1协议。这一点cloudfront配置的时候,也有明确的写了呢!


HTTP2.0

http2.0 解决了什么问题呢?

首先看看采用http2的两个效果,

https://http2.golang.org/gophertiles

https://http2.akamai.com/demo

  • 对头阻塞

    浏览器,比如chrome,默认建立6个连接。现在打开一个网页,经常见到上百个请求。那么http1.1时,就依赖着这6个连接,获取着上100个资源。然而6个连接,并没有根本上解决http的对头阻塞,因为http基于tcp,tcp的数据传输是由seq的,必须按照顺序接受。只是现在打饭的窗口,增加到6个而已。

    • 为了优化打开速度,于是出现了采用多个域名来分担连接数的方式。比如打开demo.com, 那么静态资源通过域名static.demo.com 与动态资源 api.demo.com,分开处理,这样就有12个连接了。当然有甚至着用了更多的静态资源站点。

    • 还有一种叫雪碧图 /CSS Sprite 的优化方式,简单描述就是小图,拼成大图。减少了请求文件的数量,不过现在这种也越来越少被用到了。

    http2.0,引入了数据帧以及流式的处理方式。在应用层将数据拆封为帧,每个消息都被拆分成若干个帧进行传输,每个帧都分配一个序号。(在http1.1时,应用层的请求并没有被编号哦,而是发送后,等回应,再接着发)每个帧在传输是属于一个数据流,而一个连接上可以存在多个流,各个帧在流和连接上独立传输,到达之后在组装成消息,这样就避免了请求/响应阻塞。当然如果http2.0是基于tcp的话,还是会有对头阻塞的情况,毕竟tcp必须按序号接受消息。

image.png

  • 头部压缩
    • 用数字来代替请求头的值,比如2代表method:get
    • 并且一个连接中, 不再需要重复传输header,比如user-agent的safari…… ,只传输一次,这样也可以大大的节省带宽。
  • 主动推送

    当用户访问index.html时,在http1.1的时代,浏览器解析后,还要再向服务器请求关联的静态资源,然而http2.0,从服务器端,就主动推送过去了

image.png

撸起袖子开干

从运维角度看待,我们也应该主动的推动http2,尤其在大规模并发的情况下,http的每一个小优化,都会被大规模的流量而放大。比如我们常用的前端服务组件nginx也早已支持http2.0,客户端的主流浏览器也早已支持。

我们采用的aws cloudfront ,也支持http2.0,那何不采取主动权呢。另一点,在http2.0主动推送的场景下,websocket,是否会被取代呢?拭目以待。技术的发展真的永无止境呢。

相关链接: https://youtu.be/r5oT_2ndjms


http之缓存

http缓存,可不是简单的流量器与服务器通讯间的缓存机制。

其中还涉及正向代理反向代理CDN的缓存节点的行为。

缓存机制分为两种

  1. 强制缓存

    流程: 浏览器发起请求,服务器返回response,respone header中的expired,表明这个资源的过期时间。后续再又请求时,就拿请求发起的时间与本地缓存文件的expired时间对比,没过期就直接展示给客户端。浏览器的F12。看到的就是(from disk cache)。

    其中涉及两个响应头:

    expired:

    http1.0时代的产物,且客户端与服务器的时间不是一定一致,返回具体的时间戳,这种做法考虑的并不周全。于是后续就有了新的cache-control头。

    这个expired ,曾经还遇到过一次故障,nginx上,开发将~* *.jpg设置为expired +30d。导致cdn缓存没有用上。比如图片A,在linux上的mtime 为Atime ,则返回的expired 头为 Atime +30d。 然而这个文件最后修改都好几个月前的事情了。

cache-control: private、public、no-cacheno-store、max-age,默认认为private。

private: 客户端可以缓存(仅仅浏览器缓存,代理服务器不缓存)
public: 客户端和代理服务器都可缓存

max-age=xxx: 缓存的内容将在 xxx 秒后失效(和private,public 结合一起使用)

no-cache: 需要使用对比缓存来验证缓存数据
no-store: 所有内容都不会缓存,强制缓存,对比缓存都不会触发(很少用,除非明确不想让客户端缓存的资源)

下图中,便是例子,max-age单位为秒,采用默认的private,

image.png

如果expires和cache-control同时存在,cache-control会覆盖expires。建议两个都写,cache-control是http1.1的头字段,expires是http1.0的头字段,都写兼容会好点。

  1. 对比缓存

    采用本地浏览器缓存时,都会更服务器发起一个请求,问一下资源是否被更新了。

    这里引申一点,在cdn的请求下,cdn也会向源站发起请求,用以对比缓存吗?

响应码:

​ * 200 代表服务器资源更新里,重新给你个新的。

​ * 304 代表服务器资源没更新,告诉浏览器你本地可以用,仅仅有响应头,没有数据体,也能减少服务器压力,以及传输带宽。

  • Last-Modified / If-Modified-Since

    服务器第一次响应时,返回Last-Modified: 2020-02-01 23:00:00 ,标示资源的最后修改时间。

    客户端下一次请求时,请求头,带上 If-Modified-Since: 2020-02-01 23:00:00。然后服务器判断后,返回200或是304

  • Etag / If-None-Match(优先级高于Last-Modified / If-Modified-Since)

    服务器第一次响应时,返回Etag: xxxxxx,标示资源的维一标示,如果文件修改了Etag会更新。

    客户端下一次请求时,请求头,带上Etag: xxxxxx。然后服务器判断后,返回200或是304

看到这里,那对比缓存与强制缓存,那个优先级更高呢🧐,毕竟上图中, 你看Etag,Cache-control,Last-Modified都全部返回啊。答案是做个实验呗。 或者参考别人的:

https://www.imooc.com/article/details/id/22841


2020/04/20更新:

如何快速辩证用的是 强制缓存还是对比缓存。从返回码就可以看到端倪。显示来自缓存,并且返回码是200的,这个就是对比缓存了。而304就是对比缓存了。


tls 流程

image.png

如上,三次握手连接后,开始进行建立tls的流程。

整个流程涉及到对称加密,非对称加密,CA证书等等。

通过用ab压测,相同的https和http接口时,https明显的cpu负载。

从上图可以,其中一个步骤是,client 和 server 在协商tls 加密版本。这里就容易产生HTTPS协议降级攻击。攻击发起者的客户端假称自己不支持高版本的加密协议。对此SSL Labs,就可以让你去检测自己的https的加密协议是否足够安全。

🤯🤯🤯我曾一度困惑与所有客户端,访问服务端的时候,服务端都会返回公钥给客户端。凭借相同的公钥,A客户端加密的数据,B客户端,能否窃取后,用公钥解密呢。度娘上搜索,发现别人也有相同的疑问。但结论是不能。RSA是不对称加密算法的设计,便是公钥加密的信息,只有私钥才能解密,从而实现了数据可以的保密的到达拥有私钥的一方。即使被第三方截取,也无法解密。

http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html


mysql index原理

mysql 索引原理之记忆

相关的算法: https://www.cs.usfca.edu/~galles/visualization/xBST.html

二叉树

image-20200403184935952.png

如上图,为二叉树的数据结构,但是二叉树并不是mysql 索引底层使用的数据结构。

原因是二叉树,在某些场景下会出现问题。比如下面这个场景,自增长的主键,数据二叉树只能从头开始遍历。
image-20200403185445753.png

红黑树-二叉平衡树

  • 自动调整高度,减少查找的深度。

red_black-tree.gif

B-Tree

实际上,mysql 采用的上B+ Tree,B树的data也存储在节点上,占用了空间。下图中的特点,与B+Tree比对就很明显了。

image-20200403190342219.png

B+ Tree

Mysql 采用的是B+ Tree,

  • 尤其注意叶子结点,包含所有的索引字段。所以如果查询的字段,仅包含索引字段,那么mysql是不用再次去磁盘读取数据的。
  • 叶子结点之间是有双向指针的。所以查询where id > xx。这种情况是有帮助的。
  • 如下图,查找select data from A where id =30. mysql 载入根节点后,从头开始遍历,发现30介于15与56之间,于是顺着地址再次从磁盘中取出下一个结点,15~49的索引。同理继续操作,直接在叶子结点中,查找到id=30的字段数据。
  • 叶子结点之间的双向链指针,是为了应对where id between 28 to 30,这种场景。这样就不会回到上一个结点中,重新查找相邻结点了。这个也是为什么mysql还有一种索引类型-hash索引。hash 索引通过算法能直接定位到根节点,速度飞快,但是却无法应付范围查找。

image-UvmlX4GAESkVrYF.png

组合索引

下图为组合索引的例子:(id, jobs, date), id从小到大递增,id相同时,按jobs从小到大排列,jobs相同时,再按date从小到大排列。所以mysql调优中的最左前缀原则, 比如下图中,有一个数据(10004,Assistant, 2002-03-01),那么它应该再叶子结点的最右侧。此时如果查找where name = Assistant。可见索引也排不上用场。

image.png


TCP 拥塞控制

为了面试大厂,回顾tcp协议的细节。我用我自己的话来梳理tcp的拥塞控制。当然可能会有一些细节上不是那么到位。

TCP 拥塞的算法:

  1. 慢启动

    网络的带宽上总是有限的,所有的连接都想尽量的抢占带宽。

    形象的场景,比如想要通过tcp连接发送一个1G的文件。假设双方资源都充足,tcp 一开始就以最大的带宽来传输吗?当然不是的。tcp协议会从逐渐增大拥塞窗口的大小,来试探到合适的拥塞窗口大小。

    Chandler: 什么是拥塞窗口呢?可以简单理解为一次连续发送多少个报文。拥塞窗口,会根据网络情况不停的调节,比如出现了丢包重传,那么拥塞窗口就会变小,如果连续没有丢包,就假设网络还有空间,拥塞窗口就会上调。

  2. 拥塞避免

    报文开始传输的时候,会按照指数来增长,比如第一次1个报文,第二次2个报文,第三次4个报文,第四次8个报文,直到数量到了初始门限值,就不再是指数增长,而会改为每次增加一个,来避免拥塞。这个数值会逐个增长,直到出现丢包重传。

  3. 快重传与快回复

    假设发送方传输了24个数据包,却只收到20个回应报文,此时网络并不是拥塞,而是传输中的超时或丢包。那么发送方等到数据包的发送计时器超时,再改用拥塞避免算法补发,这样处理传输效率不高。快重新与快恢复便是为了解决这种场景的。

    • 快重传是指,接受方接受到第1,第2个的数据包,却没有等来后面的第3个数据包,而是来了第四个,第五个,第六个。那么此时接受方会连续发出对第2个数据包的ack。发送方收到后,进而补发。

    • 快恢复是指,在这种场景中,初始门限值不是变回1,而是发送窗口和初始门限值都下调为当前拥塞窗口的一半。


k8s daemon node selector

Running Pods on Only Some Nodes

If you specify a .spec.template.spec.nodeSelector, then the DaemonSet controller will create Pods on nodes which match that node selector. Likewise if you specify a .spec.template.spec.affinity, then DaemonSet controller will create Pods on nodes which match that node affinity. If you do not specify either, then the DaemonSet controller will create Pods on all nodes.

场景:

内网K8S集群,应开发需求,每个应用环境,需要独立的consul,并且应用连接到node上consul-client。但是consul-client并不是所有node都要有,只是存在被选中的node上

从github consul-helm-chart,修改helm value.yaml 文件,(良好的习惯,将helm的value.yaml上传到仓库)

  • 修改image 版本对应版本,以及storage class 等等
  • 另一处,修改client的nodeSelector(只在特定的节点,启动consul client)
    1
    2
    3
    4
    5
    6
    7
    322   # nodeSelector labels for client pod assignment, formatted as a multi-line string.
    323 # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector
    324 # Example:
    325 # nodeSelector: |
    326 # beta.kubernetes.io/arch: amd64
    327 nodeSelector: |
    328 project: dop-test
  • 下一步,我们需要让节点自动注册到自己node上的consul client上。需要pods能够获取到本机consul client 的IP。于是通过valueFrom🤓.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    apiVersion: v1
    kind: Pod
    metadata:
    name: consul-example
    spec:
    containers:
    - name: example
    image: "consul:latest"
    env:
    - name: HOST_IP
    valueFrom:
    fieldRef:
    fieldPath: status.hostIP
    command:
    - "/bin/sh"
    - "-ec"
    - |
    export CONSUL_HTTP_ADDR="${HOST_IP}:8500"
    consul kv put hello world
    restartPolicy: Never
  • 最后便是应用程序,通过consul的headless svc 链接,注册进来啦。


运维之配置变更

Detail Matter, 虽然简单,但却重要!

议题: 如何备份配置文件

  • 在配置中心盛行的情况下,配置每次修改,都会有历史记录(比如etcd/consul等等)。这种系统维护起来当然是非常轻便了。

  • 大量的系统采用本地的配置文件。如何最记录每次的变更呢?

    最简单有效的方式,就是文件名绝对路径+日期,备份到特定的磁盘。

    可以参照: 备份命令脚本,Pros: 逻辑绝对的简单清晰,Cons:服务器放上这个脚本,洁癖症有点难受。

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
#!/bin/bash -e
mybackup() {
src_file=${1?"What files to backup"}

# get default value from environment variable
if [ -n "$BAK_ROOT_DIR" ]; then
bak_root_dir="$BAK_ROOT_DIR"
else
bak_root_dir="/data/backup"
fi

parent_dir=$(dirname "$src_file")
short_fname=$(basename "$src_file")
date="$(date +'%Y%m%d.%H%M%S')"
bak_dir="${bak_root_dir}$parent_dir"
if [ -f "$src_file" ]; then
mkdir -p "$bak_dir"
echo "cp $src_file ${bak_dir}/${short_fname}-${date}"
cp "$src_file" "${bak_dir}/${short_fname}-${date}"
elif [ -d "$src_file" ]; then
mkdir -p "$bak_dir-${short_fname}-${date}"
echo "cp -r $src_file $bak_dir-${short_fname}-${date}"
cp -r "$src_file" "$bak_dir-${short_fname}-${date}"
else
echo "Error: $src_file doesn't exist"
exit 1
fi
}

更特殊的场景

Q:n台nginx,作为负载节点,使用相同的配置文件。如何做到有效的更改,且保证完全的一致呢?

A: 杜绝手工修改产生差异行等情况。通过ansible playbook,统一修改。或修改一处,推送到所有节点,再reload nginx。

Q: 配置文件之备份

A: 首先先思考,这个问题是不是驳论🤔。针对存在文件上的配置文件,如果生产服务器,本来存储都是高可用,那么剩下的最大的风险就是人为失误。于是基于墨菲定律,磁盘定时快照是必不可少的咯。

2021年1月17日 上午10:26:14 补充,在分布式以及容器化的各种场景下,配置文件的维护,更多应该统一从运维平台进行,统一的修改。毕竟运维,不就是可控以及管理嘛。


运维如何保证数据查询安全

生产数据用于测试环境测试,在小型企业应该是比较常见的现象了。测试/开发团队会以无法在内部环境,复现故障为理由,要求将生产备份数据导入到测试环境。即使提供了脱敏脚本,也不见得团队会持续,及时地更新脚本。

此外,另一个问题便是,是否让开发连接生产数据库,即使是使用只读账号。缺少了数据,排错定位,难度会增加不少。目前个人的体会是,在无法拷贝数据的环境下,隐藏或混淆敏感数据,再配合每次查询进行审计/审核记录,这种方式可以兼顾,最大的开发便捷性与安全审计。

在GOOGLE 关键字:production data testing environment,会有更多细节/专业的分析。十分值得琢磨。