百种弊病,皆从懒生

在 redshift 中计算 p95 latency

p95 latency 的定义: 把一段时间的 latency 按照从小到大排序, 砍掉最高的 %5, 剩下最大的值就是 p95 latency. p99, p90 同理.

p95 latency 表示该时间段内 95% 的 reqeust 都比这个值快.

一般我直接看 CloudWatch, 和 datadog 算好的 p95 值. 这次看看怎么从 access log 里直接计算 p95 latency.

假设在 redshift 中有一张表存储了应用的 access log, 结构如下:

CREATE TABLE access_log (
    url         string,
    time        string,
    resp_time   real
);
url time resp_time
/test1 2018-10-11T00:10:00.418480Z 0.123
/test2 2018-10-11T00:12:00.512340Z 0.321

要算 p95 很简单, 把 log 按分钟数分组, 用 percentile_cont 在组内按 resp_time 排序计算 就能得到:

select date_trunc('minute', time::timestamp) as ts,
      percentile_cont(0.95) within group(order by resp_time) as p95
from access_log 
group by 1
order by 1;

得到:

     ts          |        p95
---------------------+-------------------
 2018-10-11 00:00:00 |  0.71904999999995
 2018-10-11 00:01:00 | 0.555550000000034
 2018-10-11 00:02:00 | 0.478999999999939
 2018-10-11 00:03:00 | 0.507250000000081
 2018-10-11 00:04:00 | 0.456000000000025
 2018-10-11 00:05:00 | 0.458999999999949
 2018-10-11 00:06:00 | 0.581000000000054
 2018-10-11 00:07:00 | 0.585099999999937
 2018-10-11 00:08:00 | 0.527999999999908
 2018-10-11 00:09:00 | 0.570999999999936
 2018-10-11 00:10:00 | 0.587950000000069
 2018-10-11 00:11:00 | 0.648900000000077
 2018-10-11 00:12:00 | 0.570000000000024
 2018-10-11 00:13:00 | 0.592649999999954
 2018-10-11 00:14:00 | 0.584149999999998
 2018-10-11 00:15:00 |  3.00854999999952
 2018-10-11 00:16:00 | 0.832999999999871
 2018-10-11 00:17:00 |  1.07154999999991
 2018-10-11 00:18:00 | 0.553600000000092
 2018-10-11 00:19:00 | 0.605799999999997
 2018-10-11 00:20:00 | 0.832000000000137
 ...

PERCENTILE_CONT 是逆分布函数, 给定一个百分比, 在一个连续分布模型上计算该百分比处的数值, 如果在该点处没有数据, 会根据最接近的前后值进行插值计算出实际值.

......

EkS 评测 part-3

这篇记录对 ingress 的测试.

ingress 用来将外部流量导入 k8s 内的 service. 将 service 的类型设置为 LoadBalancer / NodePort 也可以将单个 service 暴露到公网, 但用 ingress 可以只使用一个公网入口,根据 host name 或 url path 来将请求分发到不同的 service.

一般 k8s 内的资源都会由一个 controller 来负责它的状态管理, 都由 kube-controller-manager 负责, 但 ingress controller 不是它的一部分,需要是视情况自己选择合适的 ingress controller.

在 eks 上我主要需要 ingress-nginxaws-alb-ingress-controller. 注意, nginx inc 还维护一个 kubernetes-ingress, 和官方那个不是一个东西, 没测试过.

这里主要只测试了 ingress-nginx, 看了下内部实现, 数据的转发真扭曲….

......

eks 评测 part-2

上文测试了一下 EKS 和 cluster autoscaler, 本文记录对 persisten volume 的测试.

PersistentVolume

创建 gp2 类型的 storageclass, 并用 annotations 设置为默认 sc, dynamic volume provision 会用到:

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
    name: gp2
    annotations:
        storageclass.kubernetes.io/is-default-class: "true"
provisioner: kubernetes.io/aws-ebs
reclaimPolicy: Retain
parameters:
    type: gp2
    fsType: ext4
    encrypted: "true"

因为 eks 是基于 1.10.3 的, volume expansion 还是 alpha 状态, 没法自动开启(没法改 api server 配置), 所以 storageclass 的 allowVolumeExpansion, 设置了也没用. 这里 encrypted 的值必须是字符串, 否则会创建失败, 而且报错莫名其妙.

创建 pod 的时候指定一个已存在的 ebs volume

apiVersion: v1
kind: Pod
metadata:
    name: test
spec:
    volumes:
        - name: test
          awsElasticBlockStore:
              fsType: ext4
              volumeID: vol-03670d6294ccf29fd
    containers:
        - image: nginx
          name: nginx
          volumeMounts:
              - name: test
                mountPath: /mnt

kubectl -it test -- /bin/bash 进去看一下:

root@test:/# df -h
Filesystem      Size  Used Avail Use% Mounted on
overlay          20G  2.2G   18G  11% /
tmpfs           3.9G     0  3.9G   0% /dev
tmpfs           3.9G     0  3.9G   0% /sys/fs/cgroup
/dev/xvdcz      976M  2.6M  907M   1% /mnt
/dev/xvda1       20G  2.2G   18G  11% /etc/hosts
shm              64M     0   64M   0% /dev/shm
tmpfs           3.9G   12K  3.9G   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs           3.9G     0  3.9G   0% /sys/firmware

那块 volume 的确绑定在 /mnt

......

EKS 评测

EKS 正式 launch 还没有正经用过, 最近总算试了一把, 记录一点.

Setup

AWS 官方的 Guide 只提供了一个 cloudformation template 来设置 worker node, 我喜欢用 terraform, 可以跟着这个文档尝试:https://www.terraform.io/docs/providers/aws/guides/eks-getting-started.html 来设置完整的 eks cluster 和管理 worker node 的 autoscaling group.

设置完 EKS 后需要添加一条 ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
data:
  mapRoles: |
    - rolearn: arn:aws:iam::<account-id>:role/eksNodeRole
      username: system:node:{{EC2PrivateDNSName}}
      groups:
        - system:bootstrappers
        - system:nodes

这样 worker node 节点才能加入集群.

网络

之前一直没有在 AWS 上尝试构建 k8s 的一个原因, 就是不喜欢 overlay 网络, 给系统带来了额外的复杂度和管理开销, VPC flowlog 看不到 pod 之间流量, 封包后 tcpdump 不好 debug 应用层流量.

......

Kubernetes in Action Notes

Miscellaneous notes when reading <Kubernetes in Action>.

api group and api version

core api group need’t specified in apiVersion field.

For example, ReplicationController is on core api group, so only:

apiVersion: v1
kind: ReplicationController
...

ReplicationSet is added later in app group, v1beta2 version (k8s v1.8):

apiVersion: apps/v1beta2            1
kind: ReplicaSet           

https://kubernetes.io/docs/concepts/overview/kubernetes-api/

ReplicationController VS ReplicationSet

ReplicationController is replaced by ReplicationSet, which has more expressive pod selectors.

ReplicationController’s label selector only allows matching pods that include a certain label, ReplicationSet can meet multi labels at same time.

rs also support operator on key value: In, NotIn, Exists, DoesNotExist

If migrate from rc to rs, can delete rc with --cascade=false option, it will delete rc only, but left pods running, then we can create a rs with same selector to make pods under management.

DaemonSet

DaemonSet ensures pod run exact one copy on one node, useful for processes like monitor agent and log collector. Use node-selector to make ds only run on specific nodes.

If node is made unschedulable, normal pods won’t be scheduled to deploy on them, but ds will still be deployed to it, since ds will bypass scheduler.

Job

Job is used to run a single completable task.

......

杂谈

忙了好一阵, 两个月没写了, 工作上的事告一段落, 也该补上这笔帐了, 老规矩, 随便写写 :)

最近在做什么?

把公司的代码环境从 python 2.7 升级到 python3.6, 前后忙了 3 个月, 50w 行代码, 也是够呛, 好歹算是顺利完成了, 具体的过程6月零散写过 几篇文章, 大差不差, 后续又碰了不少坑, 但也都能解决. 下一步打算基于 python3 的一些特性对代码做些重构, 从基础库开始吧.

最近看了什么书?

<特别的猫>, 特别喜欢的一本书, 不是那种猫奴一昧赞美猫咪多么多么可爱的文字, 作者和猫咪过了一辈子, 把它们当成自己的朋友, 家人一样对待, 迫于无奈对猫做过残酷的事情, 也尽力照顾过病痛中的猫,把它从死亡边缘拉回来过, 猫给她带来的快乐和烦恼都在这本书里. 读着读着, 会产生一种错觉, 那一个个精灵古怪的小东西就自己脚边转悠. 作者是一个懂生活的人, 太羡慕了.

......

升级celery 到 4.2.0 碰到的坑

在把代码往 python3 迁移的过程中需要升级一些第三方库, 升级了 gevent 后发现 celery 有问题, 于是尝试把 celery 从3.1.25 升级到 4.2.0, 中间碰到了很多问题, 记录一点.

配置的变化

CELERY_ACCEPT_CONENT 之前默认是都允许的, 4.0 开始默认值只允许 json, 因为我用的是msgpack, 所以需要修改这个配置让它接受 msgpack.

CELERY_RESULT_SERIALIZER 之前默认是pickle, 现在默认也变成了json, 如果task 的返回结果是 binary 的话, json 无法处理,要么把结果 base64 编码, 要么把CELERY_RESULT_SERIALIZER 配置成 msgpack, pickle 明显 py2 / 3 不兼容, 没用.

......

编写 python 2/3 兼容代码

上一篇 里简单得提了一点开始做 python 2 到 python3 迁移时候碰到的问题, 和工具的选择(推荐用 six).这篇讲下编写 python 2 / 3 兼容代码要注意的事情.

_future_

python2 里自带的向后兼容模块,将 python3 的一些语法行为 backport 到 python2 里, 使用的时候需要在文件头部声明, 作用域只在当前文件.

首先是几个在 python 2.7 里不用特意写,已经默认开启的特性:

  • from __future__ import nested_scopes 2.2 开始就默认开启了,用于修改嵌套函数内的变量搜索作用域, 在此之前, 全局模块的优先级比被嵌套函数的父函数要高, 现在都没这个问题了.
  • from __future__ import generators, yield 关键词, 2.3 默认支持.
  • from __future__ import with_statement, with 关键词, 2.6 默认支持.

我显示开启的两个特性:

......

杂谈松本清张

去年在 kindle 上买了套松本清张的合集, 总共有10本, 断断续续看到现在终于看完了第 9 本<隐花平原>, 随便扯一点(喂,为什么不看完最后一本!

松本清张 (1909 ~ 1992), 社会派推理开创人, 这套书里的作品各个年代都有, 最有名的<砂之器>(貌似仲间姐姐拍过剧?) 和 <点与线> 却没收录.

本格派讲究逻辑的精巧, 整个故事就像在玩密室逃脱一样, 一环扣一环, 最后谜底揭开的时候让人惊呼"卧槽", 写的就很棒了, 但如果被提前剧透了, 就丧失了大半乐趣.

......

From python2 to python3

This article won’t provide perfect guide for porting py2 code to py3, just list the solutions I tried, the problems I come to, and my choices. I haven’t finished this project, also I haven’t gave up so far :).

Won’t explain too much about the differences between py2 and py3, will write down some corner cases which are easy to miss.

The codebase I’m working on:

  • Only support python2.7, don’t consider python2.6
  • 1X repos, about half a million lines of code in total (calculated by cloc).
  • These repos will import each other, bad design from early days, not easy to resolve, which means I can’t switch to py3 one by one, I need write py2/3 compatiblility code for them, and switch together(I’m also considering solve the import problem first).
  • Test coverage is not good, best is around 80%, lowest is 30%.

Tools

2to3, a command line tools packaged with py2, it’s a oneway porting to convert your code to py3, new code won’t work under py2, since I need be compatible with py2 and py3 for long time, didn’t try it.

future, it tries to make you write single clean python3.x code without ugly hack with six. I used it it first, but come to many problems, will explain later.

......