架构

influxdb 排坑记

相同点覆盖

Duplicate points

A point is uniquely identified by the measurement name, tag set, and timestamp. If you submit Line Protocol with the same measurement, tag set, and timestamp, but with a different field set, the field set becomes the union of the old field set and the new field set, where any conflicts favor the new field set.

相同的 measurement,tagset 和 timestamp 的数据会覆盖

解决方法:

  1. 提高时间精度
  2. 增加一个tag来标识不同的点
  3. 预处理数据 ✔️

每个点的 tag 数量

opentsdb 限制为每个点 8 个 tag

influxdb 限制不明确,最好也不要太多

在设计数据库是需要考虑的是,同一类型的字段作为 tag key 还是 tag value?

两种情况分别为:

time        key1 key2 key3
1500000000  true true true
1500000001  true false false
time        key   value
1500000000  _key1 true
1500000000  _key2 true
1500000000  _key3 true
1500000001  _key1 true
1500000001  _key2 false
1500000001  _key3 false

假设某个类型的字段可选值为 k 个,而一个用有 n 个这样的字段。如果作为 tag key,那么可能数据库的 series 复杂度是 O(k^n)。而作为 tag value,那么数据库的复杂度是 O(k * n)。也就是第二种的 series 复杂度大大降低。

但是对于第二种方法,效果就是打的点更多。第一种方法打点 m 个的时候,第二种需要 m * n 个。

另外考虑对于 down sampling 的友好性,虽然第二种造成点更多,但是在进行 down sampling 的时候有更好的压缩效率。

https://docs.influxdata.com/influxdb/v1.2/concepts/schemaanddata_layout/#don-t-have-too-many-series

max-values-per-tag 限制

每个 tag 可选值也有限制,不能超过10万,tag用于标识点,而不是用于存储数据,如果需要存储数据,应该使用 fields。

比如:

  • 域名:可选范围很大
  • 邮箱:可选范围几乎无限大
  • UUID

max-series-per-database 限制

tagset 的定义:每一个点的 tag key 和 tag value 的组合
series 的定义:使用同一个 measurement,retention policy,tagset 组合的点的集合

每一个数据库中的 series 的数量不能超过 100 万

以上连个限制的原因在于,influxdb 为每个 series 建立了索引并且常驻内存,如果过大

tag 只能存储字符串

一般来说,在 tag 中应该存储的是 enum 的值,而不是任意的字符串。

推荐配置

https://docs.influxdata.com/influxdb/v1.6/concepts/schemaanddata_layout/

如果想要使用 group by,推荐使用 tags
如果需要使用函数(mean,sum), 只有 fields 才能
如果需要使用数字,只有 fields 才能

硬件配置

https://docs.influxdata.com/influxdb/v1.6/guides/hardware_sizing/

Python + Influxdb + Grafana 的监控系统

influxdb

为什么我们要把监控数据存到 influxdb 呢? 存到 MySQL 或者 MongoDB 甚至 Elasticsearch 不好么?

数据模型上, 监控数据是和时间相关的, 脱离点产生的时间, 那么数据将毫无意义, 传统数据库中并没又强调这一点.

性能上, influxdb 是时间序列数据库, 这类数据库面临的问题是写入非常多, 而读取比较少. 而普通的数据库一般都是读比较多, 而写入较少, 并且要保证写入的正确性.

监控打点显然是一个面向时间序列的过程, 并且写入非常多, 而一般只有在触发告警, 排查问题的时候可能读取才比较多. 所以从性能和功能考虑上来说, 传统数据库都是不适用的.

influxdb 中常见的存储数据格式:

cpu_usage value=49 1502043216
cpu_usage value=50 1502193042
cpu_usage value=5 1502196258

influxdb 数据模型

我们以一个 measurement(测量) 作为一个表, tag-value, field-value 都是记录数据的键值对, 区别是 tag 是由索引的, 而 field 没有, timestamp 是时间戳. tag set 自然指的是一组 tag 的组合.

measurement,tag=value,tag1=value1 field=value,field1=value1 timestamp

measurement + tag set 被称为一个序列(series). 每一个 series 都可以指定不同的 retention policy.

influxdb 查询

使用类似 SQL 的语言, 执行 influx 进入shell

> CREATE DATABASE mydb
> SHOW DATABASES
> USE mydb

插入数据, 和 SQL 差别还是挺大的, 其中 cpu 是 measurement, 也就是 “表名”, 没指定时间的话, influxdb 会自己加上.

INSERT cpu,host=serverA,region=us_west value=0.64

查询数据, 注意多出来的 timestamp 一栏

> SELECT "host", "region", "value" FROM "cpu"
name: cpu
---------
time                                     host       region   value
2015-10-21T19:28:07.580664347Z  serverA   us_west    0.64

回收策略

默认情况下, influxdb 会永久保留数据, 一般来说这样是没有意义的, 我们可以设置短一点.

CREATE RETENTION POLICY <retention_policy_name> ON <database_name> DURATION <duration> REPLICATION <n> [DEFAULT]

其中 replication 只能设置为 1, 因为开源版只有 1. 可以设置成 30d, 1w

Python 客户端的编写

看到这里有人可能要问了, 不是有 python-influxdb 这个库么, 好好地客户端你为什么要封装一层呢? 答案很简单: 性能.

  1. 调用 influxdb.write_points() 是一个涉及到网络的阻塞操作, 极有可能对于程序造成性能影响.
  2. 如果我们在程序中散落着各种打点的代码, 那么就会造成没打一个点都去调用一些 IO, 不如放在一个队列里面可以每次多打几个, 减少 IO 次数, 这样对程序和 influxdb 的性能都有好处.

UDP vs http

influxdb 支持使用 UDP 和 HTTP 两种协议访问. 显然对于打点这种操纵来说, 我们不关心响应结果, 哪怕一个点没打上, 没打上就没打上吧……所以采用 UDP 就好了. 根据测试 udp 的性能在 http 的几十倍.

按照 influxdb 官网的建议, 我们需要调整 UDP buffer 的 size 到 25MB(26214400B) 为宜.

查看系统的 udp buffer 大小:

$ sysctl net.core.rmem_max
$ sysctl net.core.rmem_default

修改 /etc/sysctl.conf 文件:

net.core.rmem_max=26214400
net.core.rmem_default=26214400

但是这个设置只有到下次重启才能生效, 继续使用 sysctl 设置立即生效:

$ sysctl -w net.core.rmem_max=26214400
$ sysctl -w net.core.rmem_default=26214400

另外注意, UDP 有一个大坑, 只吃吃精度为 s 的打点, 所以在配置和客户端中都必须使用这个时间精度.

P.S. 中文互联网上的好多教程都在使用 http 打点, 误人子弟, 毁人不倦啊……

参考

  1. https://docs.influxdata.com/influxdb/v1.6/supported_protocols/udp/
  2. https://github.com/MikaelGRA/InfluxDB.Client/issues/31
  3. https://blog.codeship.com/a-deep-dive-into-influxdb/
  4. http://docs.grafana.org/features/datasources/influxdb/

kubernetes 初探——使用 helm 部署服务

在 k8s 上部署一个应用还是有些复杂的, 自己的应用当然需要自己一步一步部署, 但是对于一些通用的应用, 比如说 mysql/grafana 这种就没必要自己手工一步一步部署了. 这时候就有了 helm, 通俗的来说他就是 kubernetes 上的 AppStore 或者是 apt-get, 常见的应用都已经在了, 而且你也可以按照他的格式打包自己的应用部署.

Helm 是 Kubernetes 的一个包管理器,你可以把他理解为 apt-get 或者 App Store。Helm 中的一个包称为一个 Chart,每次安装后称为一个 Release, Release 还有 revision,可以升级或者回滚到之前的 revision。 Helm 中还可以添加不同的 repo,repos 中有不同的 Chart。

安装

在 helm 的 release 页面下载, 然后拷贝到本地的 /usr/local/bin/ 目录就好了. helm 运行的时候会使用 ~/.kube/config 文件, 所以本地 kubectl 可以使用就好了.

使用

添加仓库

helm repo add stable https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts  # 国内镜像

安装包(Chart)

helm repo update  # 更新仓库
helm install stable/mysql --generate-name  # 安装一个包

列出值

helm list  # 列出当前的 release
helm status RELEASE # 查看状态

自定义安装

helm show values stable/mysql
helm install -f config.yaml stable/mysql --generate-name

升级和回滚

helm upgrade
helm rollback

卸载

helm uninstall

概念

  • Chart, 大概相当于 package 的意思
  • Repository, Helm 的中心仓库
  • Release, 每次运行一个 Chart 都会生成一个 Release, 每个 release 之间是独立的. Chart/Release 的关系就好比 Docker 的 Image/Container 一样.
  • Revision, 每次更新 Release 都会产生一个新的版本, 可以回滚

基础命令

helm search 查找相关的软件包(chart), 现在 stable 仓库中大概有 200 多个包可以安装.

helm install –name NAME PACKAGE 会安装对应的 chart. 如果不指定 name, 会自动生成一个.

helm status NAME 可以查看对应的包的信息, 一般包括了如何连接使用这个包等信息, 可以当做帮助来用.

在安装包之前更改配置

每个 helm chart 都定义了一些默认的配置, 可以在安装之前查看并修改这些值.

helm inspect values stable/mysql 查看 mysql 的默认值. 或者去 GitHub 上看这个仓库的 readme.

把需要覆盖的变量写到 OVERRIDE.yml 文件中, helm install -f OVERRIDE.yml stable/mysql 就可以使用自己的配置了

更新 release

如果需要更新一个 release, 可以使用 helm upgrade -f OVERRIDE.yml RELEASE_NAME 命令更新相关的配置. 这时就会创建一个新的版本.

使用 helm list 可以查看当前部署的 release, 这时候我们可以看到部署的版本变成了 2 (最初是1).

-> % helm ls
NAME    REVISION        UPDATED                         STATUS          CHART           NAMESPACE
mysql   1               Sat Oct  6 15:44:25 2018        DEPLOYED        mysql-0.3.0     default

如果当前的更新有误, 可以回退到之前的版本, 语法是 helm rollback [RELEASE] [REVISION]

管理 repo

helm repo list 列出当前添加的 repo

$ helm repo list
NAME            URL
stable          https://kubernetes-charts.storage.googleapis.com
local           http://localhost:8879/charts
mumoshu         https://mumoshu.github.io/charts

添加新的 repo

$ helm repo add dev https://example.com/dev-charts

基本使用

参考

  1. https://docs.helm.sh/
  2. https://ezmo.me/2017/09/24/helm-quick-toturial/
  3. https://help.aliyun.com/document_detail/58587.html
  4. https://johng.cn/helm-brief/
  5. https://www.lixf.io/2019/06/13/k8s-docker-images-mirrors/

kubernetes 初探——部署有状态服务

为了部署我们自己的应用, 首先需要把基础设施部署上去, 其中数据库就占了大头. 有人说数据库等应用不是和容器化部署, 但是也有人认为所有的应用都适合容器化部署. 在这里我们不讨论这些观点,仅以部署 MySQL 为例说明一下如何在 K8S 上部署有状态服务。

相关概念

  • PersistentVolume(PV) 是集群之中的一块网络存储。跟 Node 一样,也是集群的资源。PV 跟 Volume 类似,不过会有独立于 Pod 的生命周期。这一 API 对象包含了存储的实现细节,例如 NFS、iSCSI 或者其他的云提供商的存储系统。
  • PersistentVolumeClaim (PVC) 是用户的一个请求。他跟 Pod 类似。Pod 消费 Node 的资源,PVC 消费 PV 的资源。Pod 能够申请特定的资源(CPU 和 内存);Claim 能够请求特定的尺寸和访问模式(例如可以加载一个读写,以及多个只读实例)
  • Stateful Set. Deployment 是无状态的服务,而 StatefulSets 旨在与有状态的应用及分布式系统一起使用。
  • ConfigMap 用来保存非密码的配置. configmap 可以以配置文件或者环境变量等方式挂在到 pod 中
  • Secret 用来保存密码等私密数据
  • Init Container 用于初始化的容器. 有点类似于 docker build 的过程

动态 PV vs 静态 PV

使用 Deployment PVC 还是 Stateful Set

可以看出我们即可以使用普通的 Deployment + PVC 来部署 MySQL, 也可以使用 Stateful Set 来部署, 那么哪种方式更好呢?

个人理解:

  • 对于需要使用挂载一定资源的,使用 PVC 就好了,甚至只需要只读挂载就好。
  • 对于强状态依赖的服务,比如数据库,肯定要使用 PVC

Stack Overflow 上的一个问题[2]也很值得参考.

MySQL 主从集群

本文中我们要部署一个一主多从的 MySQL 集群. 关于一主多从的优点不是本文的重点, 这里就不多说了, 可以参考下面:

  1. 扩容解决方案:在多个slave之间扩展负载以提高性能。在这种模式下,所有的写入和更新操作都必须在主服务器上进行。然而,读取操作通过slave镜像。该模型可以提高写入操作的性能,同时,也能够通过增加slave的节点数量,从而显著地提升读取速度。
  2. 数据安全:数据从master被复制到slave,并且slave可以暂停复制过程。因此,可以在不损坏master的情况下,在slave上运行备份服务。
  3. 分析:现场数据可以在master上创建,而对信息的分析可以在slave进行,而不影响master的性能。
  4. 远程数据分发:可以使用复制为远程站点创建本地数据的副本,而不必一直通过访问master。

参考

  1. 使用 PVC 和 Deployment 部署单实例 MySQL 集群
  2. https://stackoverflow.com/questions/41732819/why-statefulsets-cant-a-stateless-pod-use-persistent-volumes
  3. 使用普通 Deployment 部署单节点 mysql
  4. https://kubernetes.io/cn/docs/tutorials/stateful-application/basic-stateful-set/
  5. 如何使用 ConfigMap
  6. Secret 文档
  7. https://stackoverflow.com/questions/41583672/kubernetes-deployments-vs-statefulsets
  8. 阿里云挂载 OSS Volume
  9. https://jimmysong.io/posts/kubernetes-persistent-volume/
  10. https://kubernetes.io/docs/concepts/storage/volumes/

kubernetes 初探——部署无状态应用

kubernetes 架构图

  • Master. 用于控制整个集群部署的机器,为了高可用,可以使用多台,一般至少三台为宜。
  • Node. 工作节点,用于部署服务。一台机器可以既是 Master 也是 Worker,当然最好 Master 不要做 Worker。
  • Pod. k8s 部署的最小单元,一个 Pod 中可能包含多个 container. Pod 随时可能挂掉,也可能被替换。
  • Label. Pod 的标签,可以通过这些标签(组合)来选择对应的 Pod。
  • Replica Set. 作为一个高可用的系统,每个服务一般来说可能有多个 Pod. Replication Set 用来创建并保证有足够的 Pod 副本。RS 的名字总是 <Deployment 的名字>-<pod template 的 hash 值> 格式的。
  • Deployment. 用户一般来说不会直接创建 Pod, 而是创建一个 Deployment 来部署服务。(在老版本中是创建 RC)
  • Namespace. 命名空间,默认情况下会有 kube-system 和 default 两个命名空间,kube-system 存放的是 k8s 系统的 pod 等资源,而用户部署的资源会在 default 命名空间中。
  • PersistendVolume. 如果把 Node 理解为抽象的 CPU 和内存资源,那么 PV 就可以理解为抽象的硬盘资源。我们通过 Pod 来使用 Node,因此我们也不直接使用 PV,而是通过 PersistentVolumeClaim 来使用 PV。
  • PersistentVolumeClaim. 存储声明,用来声明需要使用的存储资源。
  • Stateful Set. Deployment 对应的部署语义上没有状态的,而 StatefulSet 会充分运用 PV 和 PVC 实现 Pod 重启之间的状态保持。典型应用场景是数据库。
  • Label 和 Selector. K8S 中的资源全部都通过 Label 来标识的选择。

Deployment

deployment 是使用 k8s 部署服务直接操作的概念。其他的概念往往都是通过 deployment 来间接使用的,因此理解 deployment 至关重要。

一个典型的 deployment 配置文件如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80

deployment 的配置可以发生改变,如果只是 replica 的数目发生了改变,那么这只是一个简单的扩容或者缩容操作,k8s 只会简单的打开或者杀死新的 Pod。如果镜像、命令等参数发生了改变,那么 K8S 会把这次操作视为升级,也就是开始一个 RollOut 操作,创建新的 ReplicaSet。在这个过程中,如果 deployment 中的 spec 指定了保留历史 revision 的次数大于零,那么原有的 ReplicaSet 不会被删除,只是会被 Scale 到 0 而已,方便回滚。

文档:

Note: a Deployment’s rollout is triggered if and only if the Deployment’s pod template (i.e. .spec.template) is changed, e.g. updating labels or container images of the template. Other updates, such as scaling the Deployment, will not trigger a rollout.

https://stackoverflow.com/questions/42561791/right-way-to-update-deployments-on-kubernetes

  1. deployment 和 pod 的区别
  2. Kubernetes 基础概念
  3. 客户端和服务端服务发现
  4. kubernetes 命令表
  5. Kubernetes 之 kubectl 常用命令使用指南:1: 创建和删除
  6. Kubernetes 之 kubectl 常用命令
  7. Kubernetes 基本概念以及术语
  8. kubectl create 和 apply 的区别
  9. https://stackoverflow.com/questions/42561791/right-way-to-update-deployments-on-kubernetes
  10. https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
  11. K8S YAML 文件基础
  12. NodePort/LB/Ingress 三种方式的对比
  13. https://kubernetes.io/docs/tasks/access-application-cluster/service-access-application-cluster/

kubernetes 初探——部署集群

随着 docker cloud 的关闭,容器的编排工具之争似乎已经结束了,Docker Swarm 算是完了,Kubernetes 笑到了最后。然而 k8s 的组件众多,因此部署起来也很麻烦。为此,网上有不少的网上有不少的部署教程和脚本,包括但不限于:

  • kubesaz
  • minikube
  • kubespray
  • rke
  • kubernetes: the hard way

本文基于 ubuntu 18.04. CentOS 上好多默认设置都需要修改,因此建议大家基于 Ubuntu 部署 k8s。

使用 kubespray 安装 k8s

(零) 假设我们要在三台机器上安装,另外一台机器作为控制节点。其中每台都作为工作节点,两台作为 master 节点。

IP | 角色
————|———————–
10.4.17.165 | 控制节点,不参与 k8s 集群
10.4.17.167 | master, node
10.4.17.168 | master, node
10.4.17.169 | node

(一) 下载 kubespray 安装包,这里我们使用最新版 (2018.10), 可能需要安装 python3, 如果没有安装的话

VERSION=2.7.0

# download kubespray
wget https://github.com/kubernetes-incubator/kubespray/archive/v${VERSION}.tar.gz
tar xzf v${VERSION}.tar.gz

# install dependencies
pip3 install -r kubespray-${VERSION}/requirements.txt

(二) 生成部署的 hosts.ini

kubespray 中有一个脚本叫做 inventory_builder 用来生成部署的 hosts.ini

cd kubespray-v${VERSION}
cp -r inventory/sample inventory/mycluster
declare -a IPS=(10.4.17.167 10.4.17.168 10.4.17.169)
CONFIG_FILE=inventory/mycluster/hosts.ini python3 contrib/inventory_builder/inventory.py ${IPS[@]}

生成之后,可以查看生成的文件

cat inventory/mycluster/host.ini

[k8s-cluster:children]
kube-master·▸   ·
kube-node·▸ ·

[all]
node1 ▸  ansible_host=10.4.17.167 ip=10.4.17.167
node2 ▸  ansible_host=10.4.17.168 ip=10.4.17.168
node3 ▸  ansible_host=10.4.17.169 ip=10.4.17.169

[kube-master]
node1·▸ ·
node2·▸ ·

[kube-node]
node1·▸ ·
node2·▸ ·
node3·▸ ·

[etcd]
node1·▸ ·
node2·▸ ·
node3·▸ ·

[calico-rr]

[vault]
node1·▸ ·
node2·▸ ·
node3·▸ ·

(三) 修改一些配置

代理:

由于众所周知的原因,k8s 依赖的 gcr.io 在中国大陆范围内无法访问,我们可以使用代理访问,关于如何搭建代理超出了本文的范围。
假设我们的代理是 http://proxy.com:10086, 修改 inventory/mycluster/group_vars/all/all.yml 文件,设置 httpproxy 和 httpsproxy 两个变量。

下载 kubectl 到本机:

设置 kubectllocalhost 和 kubeconfiglocalhost 两个变量为 true. 安装完成后会在本机安装 kubectl, 并且可以使用 inventory/mycluster/artifacts/admin.conf 使用 kubectl.

(四) 部署

这里我们需要使用 tiger 用户,这个用户需要满足如下条件:

  1. 可以无密码远程登录
  2. 在远程主机上具有无密码 sudo 权限
ansible-playbook -i inventory/mycluster/hosts.ini cluster.yml --become --user tiger -v

大概过十几分钟就部署好了

(五) 验证

cd inventory/mycluster/artifacts
./kubectl.sh get nodes

NAME      STATUS    ROLES         AGE       VERSION
node1     Ready     master,node   1d        v1.11.3
node2     Ready     master,node   1d        v1.11.3
node3     Ready     node          1d        v1.11.3

参考

  1. HN 上关于 Docker Cloud 关闭的讨论
  2. 使用 Kubespray 部署生产可用的 Kubernetes 集群

kubernetes 初探——日志收集

在 K8S 中,默认情况下,日志分散在每个不同的 Pod, 可以使用 kubectl logs 命令查看,但是当 Pod 被删除之后,就无法查看该 Pod 的日志了,所以需要一种永久性的保存方式。

Grafana 公司推出的 Loki 看起来不错,完全为 Kubernetes 设计,直接和 grafana 集成。

influxdb+grafana+telegraf 监控系统搭建

本文基于 ubuntu 18.04

要不要用 docker?

这是一个哲学问题,用不用其实都有各自的好处。不过在这里我倾向于不用。因为 influxdb 和 grafana 都有好多的状态,而且不是都可以写到 mysql 中的,所以既然还得 mount 出来,何苦用 docker 呢?telegraf 需要采集系统的相关信息,更不适合放在 docker 里面。

  • InfluxDB,开源的时间序列数据库
  • Grafana,开源的数据可视化工具

搭建过程

influxdb && telegraf

因为这两个都是一家的产品,所以安装步骤都是一样的。按照 官网 给的安装步骤,其实很简单的。

curl -sL https://repos.influxdata.com/influxdb.key | sudo apt-key add -
source /etc/lsb-release
echo "deb https://repos.influxdata.com/${DISTRIB_ID,,} ${DISTRIB_CODENAME} stable" | sudo tee /etc/apt/sources.list.d/influxdb.list

sudo apt-get update && sudo apt-get install influxdb telegraf
sudo systemctl start influxdb telegraf

我们暂时就不配置了,直接使用默认配置。可以通过 systemctl status influxdb 来查看状态

grafana

同样参考 官网 的教程。

基本概念

Grafana 从数据源中读取数据,数据源一般是时间序列数据库。their data can be mixed in a single panel.

Grafana 使用 orginazations and users to manage permissions.

A dashboard consists of rows, rows consist of panels. for each panel, there is a query editor to edit which data the panel should show.

VERSION=5.1.4
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_${VERSION}_amd64.deb
apt-get install -y adduser libfontconfig
dpkg -i grafana_${VERSION}_amd64.deb

sudo systemctl daemon-reload
sudo systemctl enable grafana-server
sudo systemctl start grafana-server

然后就 ok 啦,打开 http://ip:3000 就能访问 grafana 的界面了,默认用户名和密码是 admin。如果是在阿里云等云上面,注意要在安全组里面开一下 3000 端口。

配置

配置 telegraf 的插件

配置 grafana 的 datasource

未完待续

参考资料

  1. https://blog.csdn.net/w958660278/article/details/80484486
  2. https://juejin.im/post/5b4568c851882519790c72f3
  3. https://grafana.com/dashboards/928
  4. http://docs.grafana.org/guides/gettingstarted/
  5. http://docs.grafana.org/guides/basic_concepts/

监控系统基本概念与选型

时序数据库

监控系统的基础是时序数据库

现代的监控系统一般都有如下几部分组成:

时序数据库 + 前端显示 + 报警系统 + 指标收集

一般需要实现的功能:

  • 度量数据收集和可视化
  • 收集尽可能多的性能和状态数据
  • 图形化做有意义的展示
  • 如果发现可疑问题,可以关联其他图表找到原因
  • 错误检测
  • 按需告警,触发条件越宽松则告警应该越少
  • 避免误报

从监控的层次划分的话,一般包含三层监控:

  • 基础层:主机的 CPU, 内存,网络及 IO 等
  • 中间层:应用运行的中间件层,Nginx, Tomcat, MySQL, Redis
  • 应用层:服务端及客户端性能数据,如 API 访问次数和响应时间等

现代的监控越来越关注应用层和其他层数据的整合能力,具有快速找到系统瓶颈,方便扩容或代码优化。

时序数据库的选择

监控数据往往都带有时间戳,其实就是一种典型时间序列数据,而这方面已经有很多专门的存储系统,如 opentsdb,influxdb,prometheus 等。相比 mysql 这样的传统数据库,这类系统在存储、查询上针对时间序列数据都做了特别的优化。

其中 opentsdb 基于 hadoop 生态系统,考虑到搭建的复杂度,暂时不考虑了。influxdb 和 prometheus 都是 Golang 编写的,直接一个二进制文件就可以运行。两者的区别有:

  • prometheus 对于保存长时间的数据有一些问题,influxdb 似乎没有问题
  • 另外 influxdb 可以直接写入,而 prometheus 是基于拉 (pull) 模式的,也就是说程序不能直接写入 prometheus,而是需要由 prometheus 去定期拉监控数据,太反人类了。
  • influxdb 的查询类似 SQL,而 prometheus 的查询语法更加简洁,但是有学习成本,各有千秋吧

所以选用 influxdb 了。

前端显示

唯一的标准自然是越漂亮越好,所以我们选择 grafana。

当然另一需要考虑的是编写查询界面不要过于复杂,这方面 grafana 只需要拖拽空间和勾勾选选就可以了,显然不成问题。

报警系统

grafan 自带了一些报警,但是只能根据阈值报警,显然不能满足我们的需求。我们这里选择了 bosun,是 Stack Overflow 开源的一款监控系统。通过简单的几个语句就可以编写复杂的报警条件。

指标收集

按照前面的分析,对于应用层,也就是我们自己的代码,可以随意地添加代码打点,这里不再赘述。对于系统的 metrics 的收集,可以使用 influxdb 公司钦定的 telegraf。telegraf 也有一些不同的插件,可以很好地支持 mysql、redis 等的监控数据收集。

参考

  1. Logs and Metrics and Graphs, Oh my!
  2. Prometheus 和 influxdb 对比
  3. 360 基于 Prometheus 的在线服务监控实践
  4. 虎牙直播运维负责人张观石:基于时序数据库的直播业务监控实践
  5. 监控系统选型
  6. openTSDB/Bosun 报警语法 介绍 / 学习笔记
  7. https://zhuanlan.zhihu.com/p/28127748

图解一致性哈希

起源

比如你有 N 个 cache 服务器(后面简称 cache ),那么如何将一个对象 object 映射到 N 个 cache 上呢,你很可能会采用类似下面的通用方法计算 object 的 hash 值,然后均匀的映射到到 N 个 cache ;

hash(object) % N

一切都运行正常,再考虑如下的两种情况;

  1. 一个 cache 服务器 m down 掉了(在实际应用中必须要考虑这种情况),这样所有映射到 cache m 的对象都会失效,怎么办,需要把 cache m 从 cache 中移除,这时候 cache 是 N-1 台,映射公式变成了 hash(object) % (N-1)

  2. 由于访问加重,需要添加 cache ,这时候 cache 是 N+1 台,映射公式变成了 hash(object) % (N+1)

1 和 2 意味着什么?这意味着突然之间几乎所有的 cache 都失效了。对于服务器而言,这是一场灾难,洪水般的访问都会直接冲向后台服务器;

再来考虑第三个问题,由于硬件能力越来越强,你可能想让后面添加的节点多做点活,显然上面的 hash 算法也做不到。

有什么方法可以改变这个状况呢,这就是 consistent hashing…

一致性哈希

一致性哈希把哈希值想象成一个环,比如说在 0 ~ 2^32-1 这个范围内,然后将节点(名字、IP等)求哈希之后分不到环上。当有访问请求时,把请求信息求哈希之后,寻找小于该哈希值的下一个节点。

当有节点宕机的时候,请求会依次查找下一个节点,从而不让所有节点的缓存都失效。

当加入新节点的时候,只会影响一个区间内的请求,也不会影响其他区间。

如下图所示:

虚拟节点

以上虽然解决了大部分问题,但是还有三个问题:

  1. 节点有可能在分布不均匀。
  2. 当一个节点因为负载过重宕机以后,所有请求会落到下一台主机,这样就有可能使下一台主机也宕机,这就是雪崩问题。
  3. 不同主机处理能力不同如何配置不同的量。

这时候可以引入虚拟节点。原始的一致性哈希中,每个节点通过哈希之后在环上占有一个位置,可以通过对每个节点多次计算哈希来获得过个虚拟节点。

比如说,本来我们通过节点的 IP 来计算哈希

hash("10.1.1.1")  => n1
hash("10.1.1.2")  => n2
hash("10.1.1.3")  => n3

现在引入两倍的虚拟节点之后

hash("10.1.1.1-1")  => n1-1
hash("10.1.1.1-2")  => n1-2
hash("10.1.1.2-1")  => n2-1
hash("10.1.1.2-2")  => n2-2
hash("10.1.1.3-1")  => n3-1
hash("10.1.1.3-2")  => n3-2

如图所示

引入虚拟节点之后:

  1. 平衡性得到了直接改善
  2. 主机是交替出现的,所以当一个节点宕机后,所有流量会随机分配给剩余节点
  3. 可以给处理能力强的节点配置更多地虚拟节点。

最后,一致性哈希可以用跳表或者平衡二叉树来实现

参考文档

  1. https://blog.csdn.net/MBuger/article/details/76189561
  2. https://www.cnblogs.com/23lalala/p/3588553.html
  3. https://crossoverjie.top/2018/01/08/Consistent-Hash/