Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 15907 articles
Browse latest View live

Docker是如何实现隔离的

$
0
0

概述

容器化技术在当前云计算、微服务等体系下大行其道,而 Docker 便是容器化技术的典型,对于容器化典型的技术,我们有必要弄懂它,所以这篇文章,我会来分析下 Docker 是如何实现隔离技术的,Docker 与虚拟机又有哪些区别呢?接下来,我们开始逐渐揭开它的面纱。

从运行一个容器开始

我们开始运行一个简单的容器,这里以 busybox镜像为例,它是一个常用的Linux工具箱,可以用来执行很多Linux命令,我们以它为镜像启动容器方便来查看容器内部环境。 执行命令:

docker run -it --name demo_docker busybox /bin/sh
复制代码

这条命令的意思是:启动一个 busybox镜像的 Docker 容器, -it参数表示给容器提供一个输出/输出的交互环境,也就是TTY。 /bin/sh表示容器交互运行的命令或者程序。

进程的隔离

执行成功后我们就会进入到了 Docker 容器内部,我们执行 ps -ef查看进程

/ # ps -ef
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/sh
    8 root      0:00 ps -ef
复制代码

使用 top命令查看进程资源

Mem: 1757172K used, 106080K free, 190676K shrd, 129872K buff, 998704K cached
CPU:  0.0% usr  0.2% sys  0.0% nic 99.6% idle  0.0% io  0.0% irq  0.0% sirq
Load average: 0.00 0.01 0.05 2/497 9
  PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND
    1     0 root     S     1300  0.0   1  0.0 /bin/sh
    9     1 root     R     1292  0.0   3  0.0 top
复制代码

而我们在宿主机查看下当前执行容器的进程 ps -ef|grep busybox

root       5866   5642  0 01:19 pts/4    00:00:00 /usr/bin/docker-current run -it --name demo_docker busybox /bin/sh
root       5952   5759  0 01:20 pts/11   00:00:00 grep --color=auto busybox
复制代码

这里我们可以知道,对于宿主机 docker run执行命令启动的只是一个进程,它的 pid是5866。而对于容器程序本身来说,它被隔离了,在容器内部都只能看到自己内部的进程,那 Docker 是如何做到的呢?它其实是借助了Linux内核的 Namespace技术来实现的,这里我结合一段C程序来模拟一下进程的隔离。

#define _GNU_SOURCE
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
/* 定义一个给 clone 用的栈,栈大小1M */
#define STACK_SIZE (1024 * 1024)
static char container_stack[STACK_SIZE];

char* const container_args[] = {
    "/bin/bash",
    NULL
};


int container_main(void* arg)
{
    printf("容器进程[%5d] ----进入容器!\n",getpid());
    mount("proc", "/proc", "proc", 0, NULL);
    /**执行/bin/bash */
    execv(container_args[0], container_args);
    printf("出错啦!\n");
    return 1;
}

int main()
{
    printf("宿主机进程[%5d] - 开始一个容器!\n",getpid());
    /* 调用clone函数 */
    int container_pid = clone(container_main, container_stack+STACK_SIZE,  CLONE_NEWPID | CLONE_NEWNS | SIGCHLD, NULL);
    /* 等待子进程结束 */
    waitpid(container_pid, NULL, 0);
    printf("宿主机 - 容器结束!\n");
    return 0;
}
复制代码

考虑到很多同学对C语言不是很熟悉,我这里简单解释下这段程序,这段程序主要就是执行 clone()函数,去克隆一个进程,而克隆执行的程序就是我们的 container_main函数,接着下一个参数就是栈空间,然后 CLONE_NEWPIDCLONE_NEWNS表示Linux NameSpace的调用类别,分别表示创建新的进程命名空间和 挂载命名空间。

  • CLONE_NEWPID会让执行的程序内部重新编号 PID,也就是从 1号进程开始

  • CLONE_NEWNS会克隆新的挂载环境出来,通过在子进程内部重新挂载 proc文件夹,可以屏蔽父进程的进程信息。

我们执行一下这段程序来看看效果。

编译

gcc container.c -o container
复制代码

执行

[root@host1 luozhou]# ./container 
宿主机进程[ 6061] - 开始一个容器!
容器进程[    1] ----进入容器!
复制代码

这里我们看到输出在宿主机看来,这个程序的 PID6061,在克隆的子进程来看,它的 PID1,我们执行 ps -ef查看一下进程列表

[root@host1 luozhou]# ps -ef
UID         PID   PPID  C STIME TTY          TIME CMD
root          1      0  0 01:46 pts/2    00:00:00 /bin/bash
root         10      1  0 01:48 pts/2    00:00:00 ps -ef
复制代码

我们发现确实只有容器内部的进程在运行了,再执行 top命令


   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                  
     1 root      20   0  115576   2112   1628 S   0.0  0.1   0:00.00 bash                                                                                                     
    11 root      20   0  161904   2124   1544 R   0.0  0.1   0:00.00 top  
复制代码

结果也只有2个进程的信息。

这就是容器隔离进程的基本原理了,Docker主要就是借助 Linux 内核技术Namespace来做到隔离的,其实包括我后面要说到文件的隔离,资源的隔离都是在新的命名空间下通过 mount挂载的方式来隔离的。

文件的隔离

了解完进程的隔离,相信你们已经对 Docker 容器的隔离玩法就大概的印象了,我们接下来看看,Docker 内部的文件系统如何隔离,也就是你在 Docker 内部执行 ls显示的文件夹和文件如何来的。

我们还是以前面的 Docker 命令为例,执行 ls

bin   dev   etc   home  proc  root  run   sys   tmp   usr   var
复制代码

我们发现容器内部已经包含了这些文件夹了,那么这些文件夹哪里来的呢?我们先执行 docker info来看看我们的 Docker 用到的文件系统是什么?

Server Version: 1.13.1
Storage Driver: overlay2
复制代码

我的版本是1.13.1,存储驱动是 overlay2,不同的存储驱动在 Docker 中表现不一样,但是原理类似,我们来看看 Docker 如何借助 overlay2来变出这么多文件夹的。我们前面提到过,Docker都是通过mount 去挂载的,我们先找到我们的容器实例id.

执行 docker ps -a |grep demo_docker

c0afd574aea7        busybox                         "/bin/sh"                42 minutes ago      Up 42 minutes 
复制代码

我们再根据我们的容器ID 去查找挂载信息,执行 cat /proc/mounts | grep c0afd574aea7

shm /var/lib/docker/containers/c0afd574aea716593ceb4466943bbd13e3a081bf84da0779ee43600de0df384b/shm tmpfs rw,context="system_u:object_r:container_file_t:s0:c740,c923",nosuid,nodev,noexec,relatime,size=65536k 0 0
复制代码

这里出现了一个挂载信息,但是这个记录不是我们的重点,我们需要找到 overlay2的挂载信息,所以这里我们还需要执行一个命令: cat /proc/mounts | grep system_u:object_r:container_file_t:s0:c740,c923

overlay /var/lib/docker/overlay2/9c9318031bc53dfca45b6872b73dab82afcd69f55066440425c073fe681109d3/merged overlay rw,context="system_u:object_r:container_file_t:s0:c740,c923",relatime,lowerdir=/var/lib/docker/overlay2/l/FWESUOVO6DYTXBBJIQBPUWLN6K:/var/lib/docker/overlay2/l/XPKQU6AMUX3AKLAX2BR6V4JQ3R,upperdir=/var/lib/docker/overlay2/9c9318031bc53dfca45b6872b73dab82afcd69f55066440425c073fe681109d3/diff,workdir=/var/lib/docker/overlay2/9c9318031bc53dfca45b6872b73dab82afcd69f55066440425c073fe681109d3/work 0 0
shm /var/lib/docker/containers/c0afd574aea716593ceb4466943bbd13e3a081bf84da0779ee43600de0df384b/shm tmpfs rw,context="system_u:object_r:container_file_t:s0:c740,c923",nosuid,nodev,noexec,relatime,size=65536k 0 0
复制代码

这里 overlay挂载并没有和容器id关联起来,所以我们直接根据容器id是找不到 overlay挂载信息的,这里借助了 context去关联的,所以我们通过 context的内容就找到了我们挂载的地址啦。我们进入目录看看结果

[root@host1 l]# ls /var/lib/docker/overlay2/9c9318031bc53dfca45b6872b73dab82afcd69f55066440425c073fe681109d3/merged
bin  dev  etc  home  proc  root  run  sys  tmp  usr  var
复制代码

我们发现这个和我们容器的目录是一致的,我们在这个目录下创建一个新的目录,然后看看容器内部是不是会出现新的目录。

文件系统隔离文件系统隔离

上面的图片验证了容器内部的文件内容和挂载的 /var/lib/docker/overlay2/ID/merged下是一致的,这就是Docker文件系统隔离的基本原理。

资源的限制

玩过 Docker 的同学肯定知道,Docker 还是可以限制资源使用的,比如 CPU 和内存等,那这部分是如何实现的呢? 这里就涉及到Linux的另外一个概念 Cgroups技术,它是为进程设置资源限制的重要手段,在Linux 中,一切皆文件,所以 Cgroups技术也会体现在文件中,我们执行 mount -t cgroup就可以看到 Cgroups的挂载情况

cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,devices)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,net_prio,net_cls)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,hugetlb)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,perf_event)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,freezer)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,blkio)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,cpuacct,cpu)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,pids)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,memory)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,cpuset)

复制代码

我们看到上面挂载的目录有包括 cpumemory那我们猜测大概就是在这个文件夹下面配置限制信息的了。我们跑一个容器来验证下,执行命令:

docker run -d --name='cpu_set_demo' --cpu-period=100000 --cpu-quota=20000 busybox md5sum /dev/urandom 
复制代码

这个命令表示我们需要启动一个容器,这个容器一直产生随机数进行md5计算来消耗CPU, --cpu-period=100000 --cpu-quota=20000表示限制 CPU 使用率在20%,关于这两个参数的详细说明可以点击 这里

进程top进程top

我们查看进程消耗情况发现 刚刚启动的容器资源确实被限制在20%,说明 Docker 的CPU限制参数起作用了,那对应在我们的 cgroup文件夹下面是怎么设置的呢? 同样,这里的配置肯定是和容器实例id挂钩的,我的文件路径是在 /sys/fs/cgroup/cpu/system.slice/docker-5bbf589ae223b347c0d10b7e97cd1461ef82149a6d7fb144e8b01fcafecad036.scope下, 5bbf589ae223b347c0d10b7e97cd1461ef82149a6d7fb144e8b01fcafecad036就是我们启动的容器id了。

切换到上面的文件夹下,查看我们设置的参数:

[root@host1]# cat cpu.cfs_period_us
100000
[root@host1]# cat cpu.cfs_quota_us 
20000
复制代码

发现这里我们的容器启动设置参数一样,也就是说通过这里的文件值来限制容器的cpu使用情况。这里需要注意的是,不同的Linux版本 Docker Cgroup 文件位置可能不一样,有些是在 /sys/fs/cgroup/cpu/docker/ID/下。

与传统虚拟机技术的区别

经过前面的进程、文件系统、资源限制分析,详细各位已经对 Docker 的隔离原理有了基本的认识,那么它和传统的虚拟机技术有和区别呢?这里贴一个网上的Docker和虚拟机区别的图

图片来源极客时间图片来源极客时间

这张图应该可以清晰的展示了虚拟机技术和 Docker 技术的区别了,虚拟机技术是完全虚拟出一个单独的系统,有这个系统去处理应用的各种运行请求,所以它实际上对于性能来说是有影响的。而 Docker 技术 完全是依赖 Linux 内核特性 Namespace 和Cgroup 技术来实现的,本质来说:你运行在容器的应用在宿主机来说还是一个普通的进程,还是直接由宿主机来调度的,相对来说,性能的损耗就很少,这也是 Docker 技术的重要优势。

Docker 技术由于 还是一个普通的进程,所以隔离不是很彻底,还是共用宿主机的内核,在隔离级别和安全性上没有虚拟机高,这也是它的一个劣势。

总结

这篇文章我通过实践来验证了 Docker 容器技术在进程、文件系统、资源限制的隔离原理,最后也比较了虚拟机和 Docker 技术的区别,总的来说 Docker技术由于是一个普通的宿主机进程,所以具有性能优势,而虚拟机由于完全虚拟系统,所以具备了高隔离性和安全性的优势,两者互有优缺点。不过容器化是当下的趋势,相信随着技术的成熟,目前的隔离不彻底的问题也能解决,容器化走天下不是梦。

参考

  1. people.redhat.com/vgoyal/pape…
  2. docs.docker.com/v17.09/engi…
  3. lwn.net/Articles/25…

以图形化的方式简单介绍Kubernetes Ingress

$
0
0

【编者的话】原文地址: here。本文主要介绍了 Kubernetes Ingress 的内部原理,以及一些 Ingress 的用法示例。

索引

本文有两部分:


内容摘要

Kubernetes Ingress 不是一个 Kubernetes Service,简单地说,它只是一个将请求重定向到集群内部服务的 Nginx Pod,只不过这个 Pod 是通过 Kubernetes Service 进行访问,通常使用的是 LoadBalancer 类型的 Service。

本文有必要读吗?

首先,本文将向你提供一个清晰且简单的概述,用以说明神秘的 Kubernetes Ingress 背后到底是什么,这样能够让你更容易理解你正在实施的内容。

然后,本文将向你展示一些使用样例的配置示例。

为什么使用 Ingress?

使用 Ingress 可以将你集群的内部服务对外暴露,它能够帮你节省内部宝贵的静态 IP,因为你不需要声明多个 LoadBalancer 类型的 service。而且,如我们所见,它还能够进行多样化的配置以及更加简易的设置方式。

本文将要讲述什么?

  • 首先,我们将简短阐述 HTTP 服务器(尤其是 Nginx)是如何工作以及它们可以做什么。
  • 然后,我们将展示在不使用 Kubernetes Ingress 资源的情况下,如何手动搭建 Ingress。
  • 最后,我们将看到 Kubernetes Ingress 只是一个预配置的 Nginx 服务器。


听起来有点迷惑?那就继续往下看吧。

简要了解一个简单的 HTTP 服务器的世界

让我们回到容器、Kubernetes以及现代云计算世界之前的那个时代。跟我一起,简单回顾一下。

一个 HTTP 服务器(如 Nginx)能做什么?
如下图所示,它能够接收一个基于 HTTP 协议访问某个特定文件路径的请求,检查文件系统中该文件路径是否存在,如果存在就将其返回。

请输入图片名称

本例中 Nginx 的配置可能如下:
location /folder {  
root /var/www/;
index index.html;
}


一个 HTTP 服务器(如 Nginx)还能做什么?
如下图所示,它能够接收一个访问特定文件路径的请求,然后将这个请求重定向到另一个服务器上(这里它扮演着代理角色)并且将来自于这个服务器的请求响应内容返回给客户端。客户端不需要做什么改变,如果请求的文件存在,它就能收到想要的结果。

请输入图片名称

我们不会深入解释这个过程,本例中 Nginx 作为一个代理服务器使用的配置可能如下:
location /folder {  
proxy_pass http://second-nginx-server:8000;
}


这个配置表示 Nginx 能够扮演一个代理角色,对外提供文件系统服务,重定向请求到其他服务器上并返回对应的响应内容。

一个简单的 Kubernetes 示例

使用 ClusterIP 类型的 service
我们假定现在你已经对 Kubernetes Service 有所了解(可以参考另一篇文章 Kubernetes Services)。如下图所示,我们有两个 worker 节点,这里先忽略 master 节点。我们有两个服务: service-nginxservice-python,它们指向了不同的 pod。Service 不会调度到任何节点上,让我们先简单地认为它们“存在于集群的任何地方”。

请输入图片名称

目前你可以看到在我们集群内部,我们能够通过 service 访问 Nginx pod 和 Python pod。如果我们想在集群外部也能够访问它们,我们就需要将service 转换为 LoadBalancer 类型。

使用 LoadBalancer 类型的 service
如下图,你可以看到我们将 ClusterIP 类型的 service 转成了 LoadBalancer 类型。我们的 Kubernetes 集群部署在能够支持 LoadBalancer 类型 Service 的云服务提供商上(例如 Gcloud、AWS、DigitalOcean 等),可以看到提供商创建了 2 个外部的负载均衡器,负载均衡器将请求转发到了集群节点的外部 IP 地址,进而转发到了内部服务上。

请输入图片名称

从上图可以看出,两个负载均衡器都有自己的 IP 地址。如果我们将请求发往地址为 22.33.44.55 的负载均衡器上,请求被转发到集群内的 service-nginx。如果我们将请求发往地址为 77.66.55.44 的负载均衡器上,请求被转发到集群内的 service-python

这种运行方式很棒!但 IP 地址有限且负载均衡器的价格取决于云服务提供商。设想下我们集群内部不止 2 个服务,而是拥有很多内部服务。如果我们都想构建 LoadBalancer 方式,那成本将迅速上涨。

有更好的解决方案能够让我们只使用一个负载均衡器(只使用一个 IP 地址),仍然可以访问我们内部的服务吗?在继续探索之前,我们先手动实现一种方式(非 Kubernetes 方式)。

手动配置一个 Nginx 代理服务
如之前所述,Nginx 可以作为一个代理服务。如下图所示,集群内有一个新的 service 叫做 service-nginx-proxy,它作为我们集群内唯一的 LoadBalancer 类型 service。在集群内 service-nginx-proxy也会指向一个或者多个 Nginx pod 终端,不过在下图中我们没有展示出来。其他两个 service 由之前的类型转为了简单的 ClusterIP 类型。

请输入图片名称

从图中我们能够看到,通过一个 LoadBalancer(11.22.33.44) 访问不同的 http url 返回的请求内容是不同的,因为如图中黄线所示,使用不同 url 访问相同的地址,请求重定向的服务是不同的。

service-nginx-proxy这个服务使用 Nginx 代理配置了传入路径与重定向目标的关系,依据请求 url 决定了请求应该重定向到哪个服务。

在这个示例中(如上图所示)我们有两个选项:红色线条和蓝色线条。红色线条重定向到了 service-nginx,而蓝色线条重定向到了 service-python。Nginx 配置如下所示:

very simplified Nginx config example

location /folder {
proxy_pass http://service-nginx:3001;
}
location /other {
proxy_pass http://service-python:3002;
}


现在我们需要手动配置 service-nginx-proxy服务,构建合适的 Nginx 配置文件指向我们 ClusterIP 类型的 service。这是一种能做到的、可行的且通用的解决方案。

基于这种通用的解决方案,Kubernetes Ingress 就是用来让构建配置文件的过程变得更简单且更加可控。现在你应该能理解上述示例的好处了,接下来我们继续讨论 Kubernetes Ingress。

使用 Kubernetes Ingress

对比下图和上述图例,其实没什么区别。我们只是使用了一个事先配置好的 Nginx(Kubernetes Ingress) 服务,不过所有代理配置已经完成,减少了很多手动配置的工作。

请输入图片名称

以上就是理解 Kubernetes Ingress 的所有内容,接下来我们介绍一些使用示例。

安装 Kubernetes Ingress Controller

Kubernetes Ingress 是 Kubernetes 附加的资源,使用以下命令进行安装:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.24.1/deploy/mandatory.yaml  
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.24.1/deploy/provider/cloud-generic.yaml


使用下面的命令,可以看到 ingress-nginx 命名空间下安装的所有 k8s 资源:
kubectl get svc,pod --namespace=ingress-nginx  


请输入图片名称

如上图所示,可以看到一个具备外部 IP 地址的普通 LoadBalancer service 以及一个附属的 pod。你可以使用 kubectl exec命令进入 pod 内部,看到它包含了一个事先配置好的 Nginx 服务。

请输入图片名称

nginx.conf文件中你可以看到不同代理配置以及其他一些相关配置项。

Kubernetes Ingress 配置示例

上述示例使用的 Ingress yaml 文件如下所示:

just example, not tested

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
namespace: default
name: test-ingress
spec:
rules:
- http:
paths:
- path: /folder
  backend:
    serviceName: service-nginx
    servicePort: 3001
- http:
paths:
- path: /other
  backend:
    serviceName: service-python
    servicePort: 3002


我们可以通过命令 kubectl create -f ingress.yaml来构建这个 Ingress。执行完命令之后,yaml 文件就会转变为之前安装的 Ingress Controller 服务中的 Nginx 配置。

不同命令空间下 Kubernetes Ingress 示例

现在如果你的集群有一个内部服务,它和 Ingress 在不同的命令空间,该如何进行重定向呢?因为一个 Ingress 资源是命令空间化的, 在 Ingress 配置中你只能重定向到相同命令空间下的服务

如果你定义了多个 Ingress yaml 配置,那么所有的配置会集中写入 Ingress Controller 使用的 Nginx 配置文件中。意味着: 所有配置都使用相同的 LoadBalancer IP地址。

因此,为不同命名空间下的内部服务创建不同命令空间下的 Ingress 资源即可。例如 service-nginx在 default 命名空间下:

just example, not tested

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
**namespace: default**
name: ingress1
spec:
rules:
- http:
paths:
- path: /folder
  backend:
    serviceName: service-nginx
    servicePort: 3001


service-python在 namespace2 命令空间下:

just example, not tested

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
namespace: namespace2
name: ingress2
spec:
rules:
- http:
paths:
- path: /other
  backend:
    serviceName: service-python
    servicePort: 3002


如何微调 Ingress Nginx 的配置?

你可以通过 Kubernetes Ingress 资源定义中的 annotations 属性来实现。如下所示,你可以配置不同内容就像直接在配置 Nginx 一样。
kind: Ingress  
metadata:
name: ingress
annotations:
  kubernetes.io/ingress.class: nginx
  nginx.ingress.kubernetes.io/proxy-connect-timeout: '30'
  nginx.ingress.kubernetes.io/proxy-send-timeout: '500'
  nginx.ingress.kubernetes.io/proxy-read-timeout: '500'
  nginx.ingress.kubernetes.io/send-timeout: "500"
  nginx.ingress.kubernetes.io/enable-cors: "true"
  nginx.ingress.kubernetes.io/cors-allow-methods: "*"
  nginx.ingress.kubernetes.io/cors-allow-origin: "*"
...


你甚至可以做一个特殊规则配置:
nginx.ingress.kubernetes.io/configuration-snippet: |  
if ($host = 'www.wuestkamp.com' ) {
rewrite ^ https://wuestkamp.com$request_uri permanent;
}


这些 annotations 设置会被转换为 Nginx 配置。你可以通过手动连接(kubectl exec)到 ingress Nginx pod 内检查下这些配置项。

还有一些不同的配置示例可以参考:

https://github.com/kubernetes/ ... ation

https://github.com/kubernetes/ ... y-waf

检查 Ingress 或者 Nginx 的日志

找出问题或者错误可以通过查看 Ingress 的日志,如下所示:
kubectl logs -n ingress-nginx ingress-nginx-controller-6cfd5b6544-k2r4n  


请输入图片名称

使用 curl 命令测试你的配置

如果你想测试 Ingress 或者 Nginx 的配置规则是否生效,使用 curl -v yourhost.com是一个不错的选择,用它来取代浏览器访问,避免缓存问题。

重定向或者 Ingress Rules的不同设置方式

在本文中介绍的示例都是通过路径来重定向到不同的服务,例如 /folder或者 /other/directory。这是所谓的“路径列表”。

另一种方式是可以根据请求的来源地址(例如 api.myurl.comwebsite.myurl.com) 区分不同请求,进而重定向到不同的内部 ClusterIP service 上。配置如下所示:
apiVersion: networking.k8s.io/v1beta1  
kind: Ingress
metadata:
name: simple-fanout-example
spec:
rules:
- host: api.myurl.com
http:
  paths:
  - path: /foo
    backend:
      serviceName: service1
      servicePort: 4200
  - path: /bar
    backend:
      serviceName: service2
      servicePort: 8080
- host: website.myurl.com
http:
  paths:
  - path: /
    backend:
      serviceName: service3
      servicePort: 3333


从上可以看出,针对特定的地址(host)以及不同的 http 路径可以重定向到不同的内部服务。

SSL 或者 HTTPS 的配置方式

SSL,你应该听说过吧?你可能想让你的 web 服务通过 https 安全访问。Kubernetes Ingress 提供了更加简单的“TLS Termination”机制,也就是说它能够处理所有 SSL 通信,解密或者终止 SSL 请求并将解密后的内容发往集群内部服务。

如果你集群内多个内部服务使用的是相同的 SSL 证书,这种机制是很适用的,因为你只需要在你的 Ingress 上配置一次即可,而不需要修改所有内部服务。Ingress 能通过一个配置好的 TLS Kubernetes Secret 来使用 SSL 证书,如下所示:
apiVersion: networking.k8s.io/v1beta1  
kind: Ingress
metadata:
name: tls-example-ingress
spec:
tls:
- hosts:
- sslexample.foo.com
  secretName: testsecret-tls
  rules:
- host: sslexample.foo.com
  http:
    paths:
    - path: /
      backend:
        serviceName: service1
        servicePort: 80


需要注意的是,如果在不同的命令空间中有多个不同的 Ingress 资源,你要确保你的 TLS secret 同样能够在这些命名空间中被使用。

总结

本文主要是想为你提供有关神秘的 Kubernetes Ingress 背后实现原理的概述。简单来说,它无非就是一种轻松配置 Nginx 服务器的方法,该服务器会将请求重定向到集群内的其他内部服务。

通过 Ingress,你可以节省宝贵的静态 IP 地址以及 LoadBalancer 资源。但是,你不应该将 Kubernetes Ingress 视为 Kubernetes 的一种 Service 类型。Ingress 本身不是 Kubernetes 的一种 Service 类型,它只是使用了一个 Service,主要是 LoadBalancer。

需要注意的是,还有其他 Kubernetes Ingress 类型并非基于 Nginx 服务实现,不过它们只是使用了不同的代理技术。

影响K8S Pod分配和调度策略的两大关键特性

$
0
0

在Kubernetes中有一个最复杂的调度器可以处理pod的分配策略。基于在pod规范中所提及的资源需求,Kubernetes调度器会自动选择最合适的节点来运行pod。

但在许多实际场景下,我们必须干预调度过程才能在pod和一个节点或两个特定pod之间进行匹配。因此,Kubernetes中有一种十分强大的机制来管理及控制pod的分配逻辑。

那么,本文将探索影响Kubernetes中默认调度决定的关键特性。

节点亲和性/反亲和性

Kubernetes一向以来都是依赖label和selector来对资源进行分组。例如,某服务使用selector来过滤具有特定label的pod,这些label可以选择性地接收流量。Label和selector可以使用简单的基于等式的条件(=and!=)来评估规则。通过nodeSelector的特性(即强制将pod调度到特定节点上),可以将这一技术扩展到节点中。



此外,label和selector开始支持基于集合的query,它带来了基于in、notin和exist运算符的高级过滤技术。与基于等式的需求相结合,基于集合的需求提供了复杂的技术来过滤Kubernetes中的资源。

节点亲和性/反亲和性使用label和annotation的基于表达集的过滤技术来定义特定节点上的pod的分配逻辑。Annotation可以提供不会暴露到selector的其他元数据,这意味着用于annotation的键不会包含在query和过滤资源中。但是节点亲和性可以在表达式中使用annotation。反亲和性可以确保pod不会被强制调度到与规则匹配的节点上。

除了能够在query中使用复杂的逻辑之外,节点亲和性/反亲和性能够为分配逻辑强制施加硬性和软性规则。硬性规则将会执行严格的策略,可能会阻止将pod分配到不符合条件的节点上。而软性规则则会首先确认节点是否与特定的条件相匹配,如果它们不匹配,它将使用默认的调度模式来分配Pod。表达式 requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution将会分别执行硬性规则和软性规则。

以下是在硬性和软性规则下使用节点亲和性/反亲和性的示例:
affinity:  
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
  nodeSelectorTerms:
    - matchExpressions:
      - key: "failure-domain.beta.kubernetes.io/zone"
        operator: In
        values: ["asia-south1-a"]


以上规则将指示Kubernetes调度器尝试将Pod分配到在GKE集群的asia-south1-a区域中运行的节点上。如果没有可用的节点,则调度器将会直接应用标准的分配逻辑。
affinity:  
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
  nodeSelectorTerms:
    - matchExpressions:
      - key: "failure-domain.beta.kubernetes.io/zone"
        operator: NotIn


以上规则通过使用NotIn运算符来强制执行反亲和性。这是一个硬性规则,它能够确保没有pod被分配到运行在asia-south1-a空间中的GKE节点。

Pod亲和性/反亲和性

尽管节点亲和性/反亲和性能够处理pod和节点之间的匹配,但是有些场景下我们需要确保pod在一起运行或在相同的节点上不运行2个pod。Pod亲和性/反亲和性将帮助我们应用强制实施粒度分配逻辑。

与节点亲和性/反亲和性中的表达式类似,pod亲和性/反亲和性也能够通过 requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution强制实施硬性以及软性规则。还可以将节点亲和性与pod亲和性进行混合和匹配,以定义复杂的分配逻辑。

为了能够更好地理解概念,想象一下我们有一个web和缓存deployment,其中三个副本在一个3节点的集群中运行。为了确保在web和缓存pod之间低延迟,我们想要在用一个节点上运行它们。与此同时,我们不想在相同的节点上运行超过1个缓存pod。基于此情况,我们需要实施以下策略:每个节点仅运行1个且只有1个缓存Pod的web pod。

首先,我们将使用反亲和性规则来部署缓存,它将阻止超过1个pod运行在1个节点上:
affinity:  
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - redis
        topologyKey: "kubernetes.io/hostname"


topoloyKey使用附加到节点的默认label动态过滤节点的名称。请注意,我们使用podAntiAffinity表达式和in运算符来应用规则的方式。

假设在集群的某个节点上安排了3个pod缓存,那么现在我们想要在与缓存Pod相同的节点上部署web pod。我们将使用podAffinity来实施这一逻辑:
podAffinity:  
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - redis
        topologyKey: "kubernetes.io/hostname"


以上代码表明Kubernetes调度器要寻找有缓存Pod的节点并部署web pod。

除了节点和pod的亲和性/反亲和性之外,我们还能使用taints和tolerations来定义自定义分配逻辑。此外,我们还能写自定义调度程序,它可以从默认的调度程序中接管调度逻辑。

[分享创造] 开源一个类微博 twitter 的网站

$
0
0

网址: https://sserr.net

代码: https://github.com/coyove/iis

之前的发布: https://v2ex.com/t/628871

时隔一个多月,当初的 todo 基本上都清掉了,代码也重构到了开源出去也不会太丢人的程度 XD。

总的来说这是一个类微博的系统,提供了大部分你所熟知的微博功能。时间线合并( timeline merging )采用读扩散,所以可以轻松的支持热点用户推文分发至海量 follower 的情况。回复 /@/收藏等则是写扩散。

后端采用纯 KV 形式存储,目前有两种实现( dynamodb 和基于本地文件系统)。KV 不要求事务支持,所以理论上任何支持 set(k, v)和 get(k)的 IO 都可以用作存储驱动。( dynamodb 会有 stale read,要解决这个问题需要本地缓存,如 redis )

由于该架构设计特点,(以 dynamodb 为例)当一个账户 follow 的其他账户数目超过 10000 时合并延迟和性能会受比较大影响。但在另一方面,时间线合并的负担绝大部分在客户端,所以对于后端来说没有任何压力。

【译】软件架构师之路

$
0
0

今天给大家带来一篇自己翻译的干货《软件架构师之路》。本周Github上升很快的项目。其内容对致力于成为软件架构师(不论前后端)的同学应该都会有极大的帮助。

项目地址

中文地址github.com/gamedilong/…

原文地址github.com/justinamill…

如果有看完英文原文,发现本文翻译内容中存在问题或者错误的欢迎到中文Git地址PR,如能够对大家起到一定的帮助也欢迎star

内容

什么是软件架构?

  • 软件架构师是一名软件开发专家,他可以进行高层设计选择并决定技术标准,包括软件编码标准,工具和平台。 (出处: 维基百科:软件架构师)
  • 软件架构(architecture)是一个系统的基本组织,由其组件、它们之间的相互关系和环境以及决定系统设计和演化的原则来表示。 (出处: 软件架构手册)

软件架构的层次

软件架构可以被抽象的分为几个层次,不同的层次对技能的要求不同。对层次有很多不同的划分,我最喜欢如下这三种划分:

  • 应用级: 最低层次的架构。聚焦单个具体的应用。 非常注重细节, 底层设计。 沟通仅限入单个开发团队。
  • 解决方案级: 中级别的架构. 聚焦解决业务需求(业务解决方案)的一个或多个应用。进行一些高层次但是主要以低层次的设计为主,需要在多个开发团队之间的沟通。
  • 企业层级: 最高级别的架构。专注于多种解决方案。高层次的抽象设计,需要将解决方案对应用架构师进行详细说明。 需要在整个组织沟通。查看 链接获得更多相关信息。

有时架构师也被看作是不同利益相关者之间的“粘合剂”。 三个例子:

  • 水平方向: 架起业务与开发人员或不同开发团队之间的沟通桥梁。
  • 垂直方向: 架起开发人员和管理人员之间的沟通桥梁。
  • 技术方向: 不同的技术栈或应用程序的集成和融合。

软件架构师的典型工作内容

要了解架构师所需的必要技能,我们首先需要了解架构师平时主要干什么。以下是我认为最重要的一些工作内容:

  • 定义和确定开发技术和平台。

  • 定义开发标准,如编码标准、工具、评审过程、测试方法等。

  • 支持认识和理解业务需求。

  • 根据需求设计系统并做出决策。

  • 记录并传达架构定义、设计和决策。

  • 检查和评审架构与代码等,检查是否符合约定的设计模式和编码标准。

  • 与其他架构师和相关方协作。

  • 负责开发的指导和咨询。

  • 细化、细化上级设计为下级设计。

    注意: 架构是一项持续的工作,尤其当项目采取敏捷开发的模式,上述工作应该也是反复迭代进行的。

软件架构师的重要技能

为了支撑上述工作需要很多重要的能力。就我个人的经验,每个软件架构师应该具备如下十项技能:

  • 设计能力
  • 决策能力
  • 化繁为简能力
  • 编码能力
  • 文档架构能力
  • 沟通能力
  • 评估能力
  • 平衡能力
  • 指导、答疑能力
  • 营销能力

(1) 设计能力

什么是好的设计?这可能是最重要和最具挑战性的问题。要把理论和实践区别开来。根据我的经验,两者兼而有之是最有价值的。让我们从理论开始:

  • 了解基本的设计模式: 设计模式是架构师设计开发可维护、可扩展系统的一项最重要工具。通过设计模式你可以设计解决通用问题的可重用方案。 由John Vlissides,Ralph Johnson,Richard Helm,Erich Gamma撰写的《设计模式:可重用面向对象软件的要素》一书每个从事软件开发的人都有必要阅读一番。尽管上述模式发布于20多年前,其仍然是现代软件架构的重要基础。例如,本书描述了模型-视图-控制器(MVC)模式,该模式应用于许多领域,也是一些新模式(如MVVM)的基础。

  • 深耕模式和反模式: 如果你已经知道了所有的基本GoF模式,那么可以用更多的软件设计模式扩展你的知识. 或者深入你感兴趣的领域。我最喜欢的应用程序集成相关的内容之一是Gregor Hohpe编写的“企业集成模式”一书。本书适用于两个不同环境的应用程序需要交换数据时,无论是一些传统系统的旧式文件交换还是现代微服务体系结构。

  • 了解质量测量: 定义架构远不是终点。指引手册和编码标准的定义、应用和管理是有原因的,这么做是因为质量和非功能性需求。你想拥有一个可维护、可靠、可适应、安全、可测试、可扩展、可用等的系统,而实现所有这些质量属性的一个方法就是应用好的架构工作。你可以在维基百科上了解更多关于质量衡量的信息。理论很重要。如果你不想成为一名站在空中楼阁上的架构师,那么实践同样重要,甚至更重要。

  • 尝试了解不同的技术栈: 这是成为一个更好的架构师的一项重要工作。尝试新的技术栈并至上而下的学习他们。不同的技术可以给你带来不同的设计理念和模式。对新技术的学习最好不要浮于表面,应该去多实践深入感受解决的痛点和其存在的问题。架构师不仅需要涉猎广泛,在某些领域也要有深厚的知识。 当然并不需要掌握所有的技术,你需要对你所在领域最核心的技术有扎实的了解。 你也可以尝试其他领域的技术,例如, 如果你深入SAP R/3,你就应该也去尝试一下JavaScript,反正亦然。时刻保持好奇心并付诸实践。还可以去试一些你讨厌了很多年的技术。

  • 分析和理解应用模式: 看一下当前的任一比较流行的框架,例如Angular。 可以在实践中研究很多模式,例如“观察者模式”。 尝试了解它如何在框架中应用,为什么要这样做。 如果真的很有时间且认真,可以更深入地了解代码并了解其实现方式.

  • 加入一些用户组. Meetup

(2) 决策能力

架构师需要能够做出决策并将项目或整个组织引导到正确的方向。

  • 知道重点: 不要把时间浪费在不重要的决定或者行为上。学会抓住重点。据我所知,目前还没有一本书讲这方面的内容。个人评估某件事是否重要,通常考虑如下两点:
    1. 概念完整性:即使您决定以一种方式做到这一点,坚持下去,即使有时以其他方式更好地做到这一点也是如此。 通常,这会让概念整体上更简单,简化可理解性并简化维护性。
    2. 一致性:例如,如果您定义并应用命名约定,它就无关于大小写,而是以相同的方式在任何地方应用它。
  • 优先级: 有些决定是非常关键的。如果不尽早决策,会产生很多冗余到后期不太能删除的方案,导致维护困难,甚至于导致开发人员无法继续开发,直到给出决策。这种时候,往往给出坏的决定好于没有决定。当然,遇到这种情况时优先选择当前方案中的最优解。这里我建议看看在敏捷软件开发中广泛使用的加权最短作业优先(WSJF)模型。尤其是时间关键性和风险降低是评估体系结构决策优先级的关键。
  • 明确自己的职责: 不要决策在你能力或者职责范围之外的事情。这是至关重要的,如果不考虑的话,它可能会严重破坏你架构师的地位。为了避免这种情况,一定要于你的伙伴们明确你承担的责任及角色。如果架构师不止一个,那么你应该尊重当前的组织架构。作为级别低的一方,最好是给出建议不是决策。此外,我建议始终和同伴一起评审关键决策。
  • 评估多个选项: 在决策时,一定要有一个以上的选择。在我参与的大多数案例中,有不止一个(好的)选择。只有一个选择是不好的,两个方面:首先,似乎你没有做好你的工作,其次,它阻碍了做出正确的决定。通过定义度量标准,可以基于事实而不是直觉(例如许可证成本或成熟度)比较选项。这通常会导致更好、更可持续的决策。此外,向不同的利益相关者推销决策也更容易。此外,如果没有正确评估选项,则在讨论时可能会遗漏一些因素。

(3) 化繁为简能力

请记住Occam剃刀的解决问题的原则,它表示更喜欢简单。我把这个原则解释为:如果你对这个问题有太多的假设要解决,那么你的解决方案可能是错误的,或者导致不必要的复杂解决方案。为了得到一个好的解决方案,应该减少(简化)假设。

  • 方案规整: 为了简化解决方案,从不同的位置角度评估它们通常有助于清理、规整解决方案。尝试通过自上而下和自下而上的思考来形成解决方案。如果您有一个数据流或进程,那么首先考虑从左到右,再从右到左。可以提出一些问题,比如:“在一个完美的世界里,你的解决方案会怎么样?或者:“X公司/个人会怎么做?“(其中X可能不是你的竞争对手,而是BAT(百度、阿里、腾讯)之一。)这两个问题都迫使你减少Occam的Razor建议的假设。
  • 退一步: 经过激烈和长时间的讨论,得出的结果往往是高度复杂的草案。你永远不应该把这些看作是最终的结果。退一步:再看一眼大局(抽象层面)。还是有意义的吗?然后在抽象的层次上再进行一次重构。有时,停止讨论并在第二天继续讨论会有帮助。至少我的大脑需要一些时间来处理和想出更好、更优雅和更简单的解决方案
  • 分而治之: 把问题分成小块来简化。然后独立解决。然后验证这些小块是否匹配在一起。退一步看一下这个的整体情况。
  • 重构不是坏事: 如果找不到更好的主意,从更复杂的解决方案开始完全可以。如果解决方案遇到麻烦,您可以稍后重新考虑解决方案并应用您的学习。重构不是邪恶的。 但是在开始重构之前,请记住要进行以下工作:(1)进行足够的自动化测试,以确保系统的正确功能;以及(2)从利益相关者的支持。 要了解有关重构的更多信息,建议阅读<<重构。 改进现有代码的设计>>,作者是Martin Fowler。

(4) 编码能力

即使作为一个企业级架构师,最抽象的架构层次,你仍然应该知道开发人员每天都在做什么。如果你不明白这是怎么做到的,你可能会面临两大问题:

  1. 开发者不会接受你的嘴炮。
  2. 不了解开发人员的实践需求和面临的困难.
  • 有一个辅助项目: 这样做的目的是尝试新技术和工具,以了解当今和未来的开发方式。经验是观察,情感和假设的结合(Kurt Schneider的“软件工程中的经验和知识管理”)。阅读教程或一些利弊是好的。但这仅仅是“书籍知识”。仅当你自己尝试事物时,才能体验到情绪并建立关于事物好坏的假设。而且,使用某项技术的时间越长,你的评估就会越准确。这将帮助您在日常工作中做出更好的决定。当我开始编程时,我没有代码完成,只有一些实用程序库可以加快开发速度。显然,在这种背景下,我今天会做出错误的决定。今天,我们拥有大量的编程语言,框架,工具,过程和实践。只有您对主要趋势有一定的经验和粗略的了解,才能参与对话并引导开发朝正确的方向发展。

  • 找到合适的东西去尝试: 您无法尝试所有内容。 这根本是不可能的。 您需要一种更有条理的方法。 我最近发现的一种来源是ThoughtWorks的Technology Radar。 他们将技术,工具,平台,语言和框架分为四类:采用,试用,评估和保留。 通过这种分类,更容易获得新事物的概述及其准备情况,以更好地评估下一步要探索的趋势。

    • 采用: “已经准备好提供企业级服务”
    • 试用: “能够在一个承担一定风险的项目中尝试”
    • 评估: “还需进一步评估其对业务的影响”
    • 保留: “谨慎处理“

(5) 文档架构能力

架构文档有时更重要,有时则不重要。重要的文档例如体系结构决策或代码指南。在开始编码之前通常需要初始文档,并且需要不断改进。其他文档可以自动生成,因为代码也可以是文档,例如UML类图。

  • 代码整洁: 如果做对的话,代码是最好的文档。 一个好的架构师应该能够区分好的代码和坏的代码。 罗伯特·C·马丁(Robert C. Martin)所著的<<代码整洁之道>>一书是了解更多关于好坏代码的宝贵资源。.
  • 尽可能生成文档: 系统变化很快,很难更新文档。无论是关于api还是以CMDBs(配置管理数据库)形式出现的系统环境:底层信息通常变化太快,无法手动更新相应的文档。例如:对于api,如果您是模型驱动的,则可以基于定义文件自动生成文档,或者直接从源代码生成文档。有很多工具存在,比如Swagger和RAML是一一些不错的初始选择。
  • 必要而精简:无论您需要记录什么文件(例如决策文件),都应尝试一次只关注一件事,并且仅包含关于这件事的必要信息。 大量的文档很难阅读和理解。 附加信息应存储在附录中。 特别是对于决策文件,讲一个有说服力的故事而不是仅仅发表大量论据,更为重要。 此外,这为您和您的同事节省了很多时间,而后者需要阅读。 看看您过去做过的一些文档(源代码,模型,决策文件等),然后问自己以下问题:“是否包含所有必要的信息才能理解它?”,“确实需要哪些信息,并且 可以省略吗?”和“文档中是否有红线?”。
  • 了解有关架构框架的更多信息: 该点也可以应用于所有其他“技术”点。 我把它放在这里,是因为TOGAF或Zachmann之类的框架正在提供“工具”,这些工具在文档站点上感觉很沉重,尽管它们的附加值并不限于文档。 在这样的框架中获得认证可以教会您更系统地解决体系结构。

(6) 沟通能力

根据我的观察,这是最被低估的技能之一。如果你在设计上很聪明,但不能传达你的想法,你的想法可能会影响较小,甚至无法成功。

  • 学习如何传达你的想法: 在会议上进行协作时,知道如何正确的沟通传达你的想法,将其同步到你的同行是至关重要的。 我发现《 UZMO-用笔思考》是提高我在这一领域技能的好资源。 作为架构师,你通常不仅会参加会议,而且通常需要主持并主导会议。

  • 大型的演讲: 将你的想法呈现给小型或大型团体应该对您来说可行。 如果对此感到不舒服,请开始向你最好的朋友介绍。慢慢扩大小组。 这是你只能通过离开自己的舒适区来学习的东西。 请耐心等待,此过程可能需要一些时间。

  • 找到合适的沟通方式: 不同的利益相关者有不同的利益和观点。它们需要在各自的层面上用不同的方式单独解决。在你交流之前,退后一步,检查你想分享的信息是否有正确的层次,关于抽象性、内容、目标、动机等。例如:开发人员通常对解决方案的非常小的细节感兴趣,而经理则更喜欢知道哪个选项能节省最多的钱。

  • 经常沟通: 如果没有人知道,再香的架构也是毫无意义的。定期在每个组织级别上分发目标体系结构及其背后的思想。安排与开发人员、架构师和管理人员的会议,向他们展示所需或定义的方式。

  • 透明: 定期交流只能部分缓解缺少的透明度。 您需要使决策背后的原因透明化。 特别是,如果人们不参与决策过程,则很难理解和遵循其背后的决策和理由。

  • 随时准备发表演讲: 总有人有疑问,你想马上给出正确的答案。尽量把最重要的幻灯片放在一个统一的集合里,你可以展示和解释。它为你节省了很多时间,也给你自己带来了安全感。

(7) 评估能力

  • 了解基本项目管理原则: 作为架构师或首席开发人员,您经常被要求估算实现您的想法:多长时间、多少人、多少人、哪些技能等。?当然,如果你计划引入新的工具或框架,你需要对这些“管理”问题有一个答案。最初,你应该能够给出一个粗略的估计,如天,月或年。别忘了,它不仅仅是关于实现,还有更多的因素要考虑,比如需求管理、测试和Bugfix。因此,您应该了解所使用的软件开发过程中的工作。通过过去的使用数据,你可以得到更好的评估,并从中得出你的预测。如果你没有过去的数据,你也可以尝试一些方法,如巴里W鲍姆的COCOMO。如果你被分配在一个敏捷项目中,学习如何正确地进行评估和计划:Mike Cohn的《敏捷评估和计划》一书提供了这个领域的一个坚实的概述。

  • 评估“未知”架构: 作为架构师,您还应该能够评估体系结构对于当前或未来上下文的适用性。这不是一项简单的任务,但是您可以通过手头的一组问题来准备,这些问题对于每个架构都是常见的。它不仅关乎体系结构,还关乎系统的管理方式,因为这也让您了解了系统的质量。我建议总是准备好一些问题并准备好使用。一般问题的一些想法:

    1. 设计实践: 架构遵循哪些模式?它们是否得到正确使用?设计是否遵循红线或是否存在不受控制的增长?是否有明确的结构和关注点的分离?
    2. 开发实践: 制定并遵守规范指南?代码的版本是怎样的?部署实践?
    3. 质量保证: 测试自动化覆盖范围?静态代码分析到位且效果良好?同行评议到位?
    4. 安全性: 有哪些安全概念?内置安全?渗透测试或自动安全分析工具到位并定期使用?

(8) 平衡能力

  • 质量是有代价的: 早些时候我谈到了质量和非功能性需求。如果过度使用架构,将会增加成本,并可能降低开发速度。你需要平衡架构和功能需求。应避免过度设计。
  • 解决矛盾目标: 矛盾目标的典型例子是短期和长期目标。项目往往倾向于构建最简单的解决方案,而架构师考虑的是长期的远景。通常,简单的解决方案不适合长期的解决方案,并且有可能在以后被丢弃(沉没成本)。为了避免实施方向错误,需要考虑两件事:
    1. 开发人员和业务部门需要了解长期愿景及其好处,以便调整其解决方案。
    2. 负责预算的经理需要参与进来,以了解财务影响。不一定要把100%的长远眼光直接放在适当的位置,但开发出来的成本大体在预算范围内。
  • 冲突管理: 架构师往往是不同背景的多个群体之间的粘合剂。这可能会导致不同层次的沟通冲突。为了找到一个平衡的解决方案,同时也反映长期的战略目标,架构师的角色往往是帮助克服冲突。 关于传播理论的起点是舒尔茨·冯·图恩的“四耳模型”。 基于此模型,可以显示并推论很多。 但是,该理论需要一些实践,在交流研讨会上应该有经验。

(9) 指导、答疑能力

在咨询和辅导方面,积极主动可能是最好的选择。如果有人问你,那就太晚了。架构重构是尽量要避免的。你需要以某种方式预见未来几周、几个月甚至几年,并为下一步做好准备。

  • 有远见: 如果你参与在一个项目中,无论是传统的瀑布式方法还是敏捷方法,你始终需要对要实现的中长期目标有一个愿景。 这不是一个详细的概念,而是针对每个人都可以落地的路线图。 由于无法一次完成所有工作(这是一段旅程),因此我更喜欢使用成熟度模型。 它们给出了易于使用的清晰结构,并且每次都给出了当前的进度状态。 对于不同的方面,我使用不同的模型,例如 开发实践或持续交付。 成熟度模型中的每个级别都有明确的要求,这些要求遵循SMART准则,以便轻松衡量是否已达到要求。 我发现一个很好的例子是持续交付。
  • 建立实践社区(CoP): 在一个共同兴趣小组之间交流经验和知识有助于分发思想和标准化方法。 例如,你可以每三个月左右将所有JavaScript开发人员和架构师聚集在一个房间中,讨论过去和当前的挑战以及如何解决它们或采用新的方法论和方法。 架构师可以共享,讨论和调整其愿景,开发人员可以共享经验并向同行学习。 这样的交流不仅可以为企业带来极大的好处,而且对个人本身也非常有利,因为它有助于建立更强大的网络并传播思想。 还可以查看SAFe框架中的文章实践社区,该文章在敏捷环境中解释了CoP概念。
  • 进行公开会议: 误解或模棱两可的一个原因是缺乏沟通。在固定的时间段内,例如每周30分钟,与同事交流热门话题。这次会议没有议程,一切都可以讨论。尽量当场解决小事。安排对更复杂主题的跟进。

(10) 营销推广

你的想法很好,你已经很好地沟通了,但是仍然没有人愿意追随?那么你可能缺乏营销技巧。

  • 激励和说服: 公司如何说服你购买产品? 他们证明了它的价值和好处。 但不止如此。 他们包装得很好,并使其尽可能容易消化。
    1. 原型: 展示你想法的原型。有很多创建原型的工具。在喜欢SAP的企业背景下,可以查看build.me,在其中您可以快速轻松地创建漂亮且可点击的UI5应用程序。

    2. 视频: 与“无聊的PPT”不同的是,你还可以播放一段视频,展示你的想法或至少方向。

但请不要过度营销:从长远来看,内容是王道。如果你的话没有实现,从长远来看,这将损害你的声誉。

  • 坚持自己的想法: 有些时候人们不喜欢你的想法,或者他们太懒了,不喜欢你的想法。如果你真的被自己的想法所说服,你就应该不断地去追求它们,并“战斗”。有时这是必要的。具有长期目标的架构决策通常不是最简单的:开发人员不喜欢它们,因为它们更复杂。经理们不喜欢他们,因为他们在短期内更贵。这是你的工作,你要坚持和谈判。
  • 寻找盟友: 建立或执行你自己的想法可能是困难的,甚至是不可能的。努力寻找能够支持和帮助说服他人的盟友。使用你的网络。如果你还没有,现在就开始建造它。你可以先和你的(思想开放的)同龄人谈谈你的想法。如果他们喜欢,或者至少部分喜欢,如果别人问他们,他们很可能会支持你的想法(“X的想法很有趣。”)。如果他们不喜欢,问为什么:也许你错过了什么?或者你的故事不够有说服力?下一步是找到有决策权的盟友。要求开诚布公的讨论。如果你害怕讨论,记住有时候你需要离开你的舒适区。
  • 重复一遍,相信它: “[…] 研究表明,反复接触某个观点会使人们相信该观点更为普遍,即使该观点仅来自一个人也是如此。”(来源:《金融品牌》)如果您经常发布很少的信息,则可以帮助您 说服人们更容易。 但请注意:从我的角度来看,应该明智地使用这种策略,因为它可能适得其反,成为糟糕的营销技巧。

架构师的技术路线图

Architect roadmap

相关书籍

  • Refactoring. Improving the Design of Existing Code by Martin Fowler
  • Enterprise Integration Patterns written by Gregor Hohpe
  • Design Patterns: Elements of Reusable Object-Oriented Software by John Vlissides, Ralph Johnson, Richard Helm, Erich Gamma
  • Experience and Knowledge Management in Software Engineering by Kurt Schneider
  • Clean Code by Robert C. Martin
  • UZMO — Thinking With Your Pen
  • Agile Estimating and Planning by Mike Cohn

为什么 K8s 在阿里能成功?| 问底中国 IT 技术演进 - 阿里巴巴云原生 - 博客园

$
0
0

作者:
曾凡松 阿里云云原生应用平台高级技术专家
张振 阿里云云原生应用平台高级技术专家

导读:本文描述了阿里巴巴在容器管理领域的技术演进历程,解读了为什么 K8s 最终能够大获成功的原因,以及到今年 双11 阿里巴巴内部的 K8s 应用情况。内容着重描述了阿里巴巴基于 K8s 的云原生改造实践过程的三大能力升级,在对应能力升级过程中沉淀的技术解决方案,以及通过这些能力升级所取得的业务价值。

从 2015 年 Google 牵头成立 CNCF 以来,云原生技术开始进入公众的视线并取得快速的发展,到 2018 年包括 Google、AWS、Azure、Alibaba Cloud 等大型云计算供应商都加入了 CNCF,云原生技术也从原来的应用容器化发展出包括容器、Service Mesh、微服务、不可变基础设施、Serverless、FaaS 等众多技术方向,CFCF 旗下也囊括了越来多的开源项目。

Kubernetes 作为 CNCF 的第一个项目从诞生之初就就令人瞩目,Kubernetes 由 Google 工程师基于 Google 内部多年集群管理系统 Borg 的设计经验,结合云计算时代的基础设施特点重新设计而得,旨在帮助企业解决大规模 IT 基础设施的应用容器编排难题。

Google 在 2014 年 6 月开源 Kubernetes 以后,在 Redhat、Microsoft、Alibaba 等厂商和众多开源爱好者共同的努力下,成长为如今容器编排领域的事实标准,极大的推动了云原生领域的发展。

今天为大家分享来自阿里云的 Kubernetes 大规模实践经验,展现阿里云如何基于 Kubernetes 推动阿里巴巴应用运维技术栈走向云原生,如何推动 Kubernetes自身的技术进步,充分挖掘云原生时代的红利助力阿里巴巴大幅降低 双11 的 IT 成本。

容器在阿里巴巴的发展历程

1.png

在 2011 年之前,阿里巴巴使用 VM 虚拟化技术将一个物理机切分为 3 个虚拟机,用于部署淘宝服务,而随着淘宝业务的飞速发展,基于 VM 的技术方案在灵活性上跟不上业务的步伐。

因此,阿里巴巴在 2011 年就开始探索基于 Linux lxc 的容器技术,用于替代传统基于 VM 的应用部署方案,到 2013 年,研发了基于 Linux lxc 的 T4 容器和 AI 容器编排系统。这在当时已是非常领先的技术方案,但自己研发的容器技术与基于 VM 时代的运维系统始终存在一些兼容性问题。

在 2013 年随着 Docker 容器镜像方案的出现,阿里巴巴技术人员立即看到了基于容器 + Docker 镜像技术的未来,开始大力投入到这一领域的研究当中,到 2015 年 Aliswarm、Zeus、Hippo 等容器编排系统蓬勃发展,各自开疆扩土服务了阿里巴巴经济体的一部分业务。诸多的系统在解决了业务运维成本的同时,也带来了一定的重复建设成本,同时也导致了阿里巴巴内部的资源分布比较分散,无法统一调度多样的业务类型发挥出不同业务错峰使用资源的优势。

正是在这样的背景下,Sigma 系统应运而出并在 2017 年统一了阿里巴巴的资源池,统一调度阿里巴巴所有的核心业务,并第一次支持将在线服务与离线作业运行在同一个物理机上,大幅提高数据中心的资源利用效率并降低了阿里巴巴的 IT 成本。

随着云原生技术的高速发展,阿里巴巴也看到了云原生技术的潜力,以及未来企业 IT 全面上云的必然趋势,从 2018 年开始转型到 Kubernetes 技术,通过 Kubernetes 扩展能力将 Sigma 积累多年的调度能力通过 Kubernetes 的方式提供出来。

在 2019 年阿里巴巴宣布全面上云,阿里巴巴开始全面拥抱 Kubernetes,并将 Sigma 调度系统全面的迁移到基于 Kubernetes 的调度系统,该系统也正是支持了今年最大规模 双11 电商交易系统的底层基础设施,稳定的支持了大促前后数百次的应用变更并提供极速的应用发布与扩容体验,为 双11 的顺畅的购物体验立下悍马功劳。

为什么 K8s 在阿里能成功

Kubernetes 在众多的技术中脱颖而出,概括起来可以归纳为以下三个方面。

2.png

  • 首先是其在诞生之初就为云时代而生,拥有超前的眼光和先进的设计理念,加之最初由天才的 Google 工程师基于其内部 Borg 多年的经验设计而来,诞生之后就飞速发展;

后来随着 RedHat、IBM、微软、Vmware、阿里云等来自全球的优秀工程师大力投入,打造了繁荣的社区和生态系统,成为企业容器编排系统的首选。

阿里巴巴经济体拥有众多的子公司,这些子公司在加入阿里巴巴大家庭时或多或少都会有一套自有的容器编排系统,在融入阿里巴巴的基础设施过程中,Kubernetes 是最标准也最容易被经济体内外的客户所接受的一个方案。

  • 其次,Kubernetes 倡导的申明式 API 的设计理念,也贴合了阿里巴巴在应用运维领域的经验与教训;

传统的运维系统通常是基于过程式的设计,而过程式的运维系统在较长的系统调用链路下,通常会出现因异常处理复杂而导致的系统效率低下。

在大规模应用运维系统中复杂又繁多的状态处理也是一个大难题,基于过程式的系统设计很难确保系统的一致性,针对这些边界异常的处理通常又导致运维系统变得非常复杂,最终为异常兜底的只能依赖运维人员的人工操作。基本上可以认为基于过程式的运维系统难以应对超大规模的应用管理,而 Kubernetes 提供的申明式 API 却是解决应用运维状态轮转的一剂良药,是提高运维技术栈整体链路效率的最佳实践原则。

  • 第三,Kubernetes 模块化、可扩展的架构设计,满足阿里巴巴的定制化改造以支持众多业务运维场景的需求。

在阿里巴巴内部,即有大量的无状态核心电商系统,也有大量的缓存、消息队列等中间件有状态系统,也包括大量带有倒排索引数据的检索系统,还有大量的 AI 计算任务,不用的应用类型对底层容器管理平台的要求也有所不同。

因此,一个模块化方便迁入自定义应用管理策略、易于扩展调度模型的设计显得至关重要,是能够服务阿里内部众多应用形态、提供统一容器管理基础设施的关键,Kubernetes 基本上提供了这些关键基础能力,虽然在实际应用过程中仍然会遇到非常多的实际问题。

阿里巴巴的 K8s 应用情况

3.png

在 2019 年 双11,阿里巴巴内部核心业务主要运行在神龙、ECS、ECI 三种资源类型的基础设施之上,而这些不同类型的基础设施资源均通过 Kubernetes 统一管理,以容器的形态提供给上层应用使用,完成了核心业务的支撑。

有别于以往的 双11,今年核心电商业务应用大规模部署在神龙裸金属服务器上。如果有关注过阿里云技术的发展,应该不会对神龙服务器感到陌生,它是阿里云自主研发的新一代云服务器,通过“软硬一体”的技术开创性的将云计算的虚拟化开销分摊到低价硬件板卡上,彻底的释放 CPU 的计算能力,第一次真正的做到了云计算虚拟化的“零”开销。

容器也是一种轻量级的虚拟化方案,神龙+容器+Kubernetes 的结合正是云原生时代的最佳拍档,支撑了今年最大规模的 双11,也将是未来的主流技术形态。

阿里巴巴也在继续使用 ECS 作为 Kubernetes 的底层资源供给,ECS 作为传统的云计算虚拟化方式支撑了部门集团内部业务,同时结合灵活性更好的弹性容器实例 ECI 用于应对业务突发的流量峰值,为业务带来了云计算的弹性价值,真正实现了按需申请、释放资源的极致弹性能力,降低了业务需要提前规划资源所带来的成本。

这些分布在海内外的数十万个节点的资源,被数十个 Kubernetes 集群托管,运行着阿里巴巴上万个应用,共计超过百万的容器,其规模之大前所未有。在今年的 双11 中,阿里巴巴内部最大的 Kubernetes 集群规模达到万级;当然这并不是Kubernetes 的技术极限,而是我们考虑数据中心资源效率与基础设施容灾能力之间所取的平衡,在将来如果有需要这个数字也可能变得更大。

基于 K8s 的云原生改造实践

Kubernetes 作为云原生技术的代表,已经成为了容器编排领域的事实标准,阿里巴巴自 2017 年开始探索,到 2018 年确认技术转型到使用 Kubernetes 来管理生产的容器。

在落地 K8s 的过程中,我们主要面临着两大难题:

4.png

  • 其一,上层多样的业务运维平台;

为了支撑阿里巴巴内部多样的业务形态,在内部发展出来了多个典型的业务运维平台,每一个运维平台的基础设施、流程控制、应用发布策或多或少都会存在一些差别,缺少一个统一的应用运维标准。在调度与集群管理的技术演进过程中,如何牵引整个运维体系升级的同时并保持多个业务的平台及其上业务的稳定性,这是一个巨大的工程。

  • 其二,随着阿里巴巴经济体全面上云战略的实施,整个底层基础设施包括存储、网络、基础运维软件的技术演进也非常迅速。调度与集群管理需要在支持好基础设施快速演进的同时,迭代自身的技术架构,并同时保证业务的稳定性。

基于 K8s 的云原生技术改造正是在这样的背景下诞生,发展到 2019 年 Kubernetes 在内部已大规模部署,所有的核心业务也都已经运行在 K8s 集群管理中。但在这几年的实践过程中,有一个问题始终萦绕在工程师头脑中,在阿里巴巴这么大体量、这么复杂的业务下,遗留了大量传统的运维习惯以及支撑这些习惯的运维体系,在这样的背景下落地Kubernetes (内部一个形象的比喻叫做给高速飞行的飞机更换发动机)到底是在坚持什么,哪些地方可以妥协,哪些地方必须改变?

这一章节, 将为大家分享我们这几年对这个问题的一些思考,特别是经过了今年的 双11 考验后,这个问题的答案基本上得到了工程师群里的集体认可。

负责顶层设计的架构师终于可以喘一口气:拥抱 Kubernetes 本身并不是目的,而通过拥抱 Kubernetes 翘动业务的云原生改造,通过 Kubernetes 的能力治理传统运维体系下的沉疴顽疾,真正释放云的弹性能力,为业务的应用交付解绑提速,才是这次技术变革的最大价值所在。

5.png

面向终态升级

在传统的运维体系下,应用的变更都是运维通过创建操作工单发起工作流,继而对容器平台发起一个个的变更来完成的。比如升级一个服务下的 3000 个实例,工单会被提前计算并生成出多个批次的子任务,并逐个的调用容器平台的接口完成变更应用的变更。

为了确保应用发布工单的顺利执行,在每一个子工单内部,每一个容器的发布也是一个工作流,包括监控开管、镜像拉取、容器启停、服务注册、配置推送等等,如果一切正常该流程会按预期有序的进行。

6.png

在大规模应用发布的场景中,诸如宿主机宕机、磁盘异常、IO 异常、网络异常、内核异常等几乎是必然存在的,如果发布流程中的某一个步骤出现了错误,通常情况下需要运维平台按照一定的策略来重试,直到超过该批次的超时阈值,这将会带来三个问题,下面逐一展开。

  • 其一是重试带来的效率问题;

每一个子任务的执行时间将被任务内的长尾发布所拖累,假设将 3000 个容器分为 30 批次每批 100 个(仅为示意并非最佳实践),每一批次内出现一个容器发布异常时,该批次的发布时间将被重试拉长。

  • 其二是失败带来的一致性问题;

对于发布异常的容器,在工单结束之后通常只能通过外围链路巡检的方式来治理,而事实上通常的巡检是依赖运维人员手工操作的,带来了极大的人工成本和不确定性。

  • 第三是应用并发变更冲突问题。

如果在应用发布的过程中,同时提交了应用扩容的请求,由 3000 扩容到 3200 个实例,扩容的 200 个实例应该采用旧版本还是新版本,采用旧版本扩容将面临的问题是谁最终负责这 200 个旧版本实例的升级,采用新版本扩容将面临的是稳定性问题,如果新版本存在问题新扩容的实例将产生较大的影响。

正是因为这些复杂的问题导致多数运维系统拒绝了并发的应用变更,导致并发操作效率非常底下。

K8s 为应用管理所提供的申明式 API 的设计理念同时解决了解决了这三个问题,用户只需要描述期望的最终状态以及达成期望状态的过程中需要遵守的限制条件,达成终态所需要执行的复杂操作全部交由 K8s 的来完成。

在应用发布过程中,通常情况下 K8s 通过控制并发度及最大不可用实例数来约束应用发布对服务的影响,对于发布过程中失败的实例通过最终一致的方式在系统内部解决。正是基于这一设计,用户发起服务变更时只是更新了应用的预期状态,并不需要等待任何任务的结束,一并解决了应用发布效率、线上配置的一致性和并发变更冲突效率的问题。

7.png

基于面向终态的理念管理应用,我们开发 Advanced StatefulSet 的应用管理工作模型,顾名思义它基于 Kubernetes 官方的 StatefulSet 扩展而来。

在官方的工作模型中,应用通过滚动的方式完成版本升级,也就是创建新的 Pod 同时删除旧版本的 Pod,直到整个应用切换为新的版本。

这种方式简单直接,但存在效率的问题,比如所有应用的 Pod 需要重新的调度,这在大规模应用发布场景将给调度器带来很大的压力;同时,因为新版本 Pod 为全新创建,需要重新分配 IP 并挂载远程卷,这对云计算网络、存储基础设施也将是很大的挑战;再者,因为容器是被全新调度出来的,在机器上需要重新下载新的应用镜像,这将大幅降低应用发布的效率。

为了提高应用发布的效率和资源的确定性,开发了这一工作负载模型,它支持原地发布应用,应用发布前后应用所在的位置保持不变,同时支持了并发更新、容错暂停等丰富的发布策略,高效的满足了阿里巴巴内部电商应用的发布需求。因为应用发布前后位置不变,因此我们可以在灰度发布的过程中预先下载并解压即将要发布的容器镜像,从而大幅提高应用发布的效率。

8.png

在面向终态的应用管理中,复杂的运维过程被 K8s 内部所实现,K8s根据用户的期望及现状计算出需要执行的动作,并逐步的变更直到终态。面向终态带来了卓越的运维效率提升,但同时也为系统工程架构提出了更高的要求。

我们知道在 K8s 内部是一个模块化、分布式的系统,通往终态的运维决策分散在内部的多个模块中,这些模块都有可能对容器发起一些运维动作,比如控制器、运维 Operator、重调度器甚至是 kubelet。在高度自动化的系统中,一旦出现预期外的异常,其杀伤力可能会对其上运行的业务造成灾难性的后果,加之 K8s 中决策分散在众多的模块中,所带来的问题是系统风险的控制变得更加困难,对这个系统设计的质量有很高的要求。

为了控制整个系统的风险,如上图所示,我们在 K8s 系统的关键位置对关键行为行为进行了埋点,针对性的制定了限流及熔断的策略,使得整个系统即使在出现极端错误的场景下,也能够最大化的保护其上运行的业务。

自愈能力升级

9.png

在阿里巴巴传统的运维体系下,容器平台仅生产资源,应用的启动以及服务发现是在容器启动后由运维平台系统来完成的,这种分层的方法给了运维系统最大的自由度,也在容器化后促进了阿里巴巴的容器生态繁荣。

但是这种方式有一个严重的问题,因为容器调度平台无法自主地去触发容器的扩缩容,而需要和一个个运维平台来做复杂的联动,上层运维系统也因为需要感知到底层基础设施的信息,从而导致进行了很多重复建设的工作。

在工程实践上,这些复杂性使得即使经过了细心的设计与大量的投入其工作效率也不高,严重妨碍宿主机发生故障、重启,容器中进程发生崩溃、卡住等异常时的自愈修复效率,同时也让应用弹性伸缩的实现变得非常的复杂和低效。

10.png

我们解决这一问题的思路是通过 K8s 中提供了容器命令以及生命周期钩子,将启动应用以及检查应用启动状态这一正个流程内置到 pod 中,包括与监控、VIP、服务中心、配置中心等基础设施的交互,通过 Pod 实现容器与应用实例的生命周期统一。

容器平台不再是仅生产资源,而是交付可以直接为业务使用的服务,从而使得可以在 K8s 系统内部完成故障自愈闭环,极大地简化了应用故障自愈以及自动弹性扩缩容能力的建设。提高系统自愈的效率,实际上也是帮助业务获得更好的运行时稳定性和应用运维效率。

11.png

在完成了容器与应用实例的生命周期统一之后,我们正在打造一个统一控制器编程框架:Operator Platform。

Operator Platform 由中心的控制组件与一个 sidecar 框架容器以及客户端代码组成,通过对通用的控制器能力的抽象,包括:事件通知、灰度管理、版本控制、缓存、命令管道等能力的封装集成,支持多语言编写operator,使得开发者无需要理解 K8s 的众多的接口细节及错误处理,从而降低了基于 operator 的自动化运维能力的开发难度,使得越来越多的运维能力通过operator 的方式沉淀到 K8s 生态体系中来,让更多的有状态应用能够自动化地部署,提高整个运维体系的运转效率。

通过这种方式,构建了整个机器故障自愈的体系,高效的串联起包括机器锁定、应用驱逐、机器线下、异常修复等流程,确保了集群宿主机的在线率以及业务的可用性。未来,我们期望通过将 operator 编写标准化的方式去推动多个运维平台的基础运维能力能够被最大化的复用,减少重复建设的成本。

不可变基础设施

12.png

第三个重要的能力升级是对不可变基础设施的升级。

我知道 Docker 提供了一种统一的应用交付的形式,通过把应用的二进制、配置、依赖文件在构建过程中打到一个镜像中,从而确保了应用被一次构建出来之后在多个环境中交付的确定性,避免了环境不一致带来的诸多问题。

而 K8s 更进一步,通过将不同用途的 Docker 容器组装在一起成为一个 pod,通常情况下在升级 pod 时需要整个的销毁重建,从而确保应用镜像、卷、资源规格的一致性。在落地 K8s 的过程中,坚持了不可变基础设施的设计理念,通过 K8s pod 将原本运行在一个富容器中的应用与运维基础组件分离到不同的容器中,并通过升级容器镜像的方式完成应用的升级。

这里有一个概念需要澄清,并不是使用 K8s 就等于践行了不可变基础设施的理念,而是必须要确保应用运维通过镜像升级而不是动态发布文件的方式完成,而事实上因为一些历史原因,这一用法在行业中普遍存在。

13.png

当然,与 K8s 有所不同的是,我们并未强制坚持 pod 的不可变而是取了一个折中的方式,即坚持容器不可变。

原因是我们将应用容器与运维基础设施容器分离之后,运维容器作为应用容器的 sidecar 容器,其拥有着不同的版本迭代策略。应用容器由应用运维人员负责发布,其策略因应用的不同而不同,比如电商应用使用 StatefulSet 而本地生活使用 Deployment 来管理应用,而基础设施容器则由基础设施运维负责,其发布策略与应用本身也存在一些差别。

为了解决这个问题,我们开发了一个叫做 SidecarSet 的基础设施容器管理模型,它使用同一个集合统一管理多个应用的运维容器,将基础设施的变更与应用容器的变更进行分离,从而支持基础设施的快速演进。将基础设施容器定义从应用 pod 中抽离出来后,应用管理员不再关心基础容器的启动参数,而是交由基础设施运维人员通过配置 SidecarSet 自动为应用注入运维容器,简化了应用运维的复杂性。

可以看到,这种关注点分离的设计,同时简化了应用运维与基础设施运维的负担。

总结与展望

阿里云通过落地 K8s 推动阿里巴巴运维体系走向云原生,在应用容器发布管理效率、服务稳定性以及企业 IT 成本方面取得了很大的突破。

我们一直在思考,通过什么样的方式能够将阿里巴巴的应用管理经验输出到更多的场景,解决更多客户面临的应用管理难题,在企业全面云化这样的趋势下,如何解决企业在公有云、私有云、混合云以及多云场景的应用管理复杂性。

14.png

正是在这样的背景下,阿里云与微软在 2019 年 11 月联合推出了一款用于构建和交付云原生应用的标准规范,即  Open Application Model(简称 OAM)

OAM 提出了一种通用的模型,让各平台可以在统一的高层抽象上透出应用部署和运维能力,解决跨平台的应用交付问题。同时, OAM以标准化的方式沟通和连接应用开发者、运维人员、应用基础设施,让云原生应用交付和管理流程更加连贯、一致。

15.png

通过应用交付标准化的方法,我们期望未来在云上部署一个应用,就像手机在应用商店中安装一个淘宝一样便捷与高效。

最后,本文提到的阿里巴巴在云原生改造上完成的相关能力升级,我们都已经或者即将开源到 OpenKruise 项目中,欢迎大家关注与交流!


Spark Streaming 数据限流简述

$
0
0

  Spark Streaming对实时数据流进行分析处理,源源不断的从数据源接收数据切割成一个个时间间隔进行处理;
  流处理与批处理有明显区别,批处理中的数据有明显的边界、数据规模已知;而流处理数据流并没有边界,也未知数据规模;
  由于流处理的数据流特征,使之数据流具有不可预测性,而且数据处理的速率还与硬件、网络等资源有关,在这种情况下如不对源源不断进来的数据流速率进行限制,那当Spark节点故障、网络故障或数据处理吞吐量下来时还有数据不断流进来,那将有可能将出现OOM进而导致Spark Streaming程序崩溃;
  在Spark Streaming中不同的数据源采用不同的限速策略,但无论是Socket数据源的限流策略还是Kafka数据源的限流策略其速率(rate)的计算都是使用PIDController算法进行计算而得来;
  下面从源码的角度分别介绍Socket数据源与Kafka数据源的限流处理。

速率限制的计算与更新

  Spark Streaming的流处理其实是基于微批处理(MicroBatch)的,也就是说将数据流按某比较小的时间间隔将数据切割成为一段段微批数据进行处理;

                                      添加监听器


  StreamingContext调用Start()启动的时候会将速率控制器(rateController)添加到StreamingListener监听器中;
  当每批次处理完成时将触发监听器(RateController),使用该批处理的处理结束时间、处理延迟时间、调度延迟时间、记录行数调用PIDRateEstimator传入PID算法中(PID Controller)计算出该批次的速率(rate)并更新速率限制(rateLimit)与发布该限制速率;

override def onBatchCompleted(batchCompleted: StreamingListenerBatchCompleted) {    
val elements = batchCompleted.batchInfo.streamIdToInputInfo

for {
processingEnd <- batchCompleted.batchInfo.processingEndTime
workDelay <- batchCompleted.batchInfo.processingDelay
waitDelay <- batchCompleted.batchInfo.schedulingDelay
elems <- elements.get(streamUID).map(_.numRecords)
} computeAndPublish(processingEnd, elems, workDelay, waitDelay)
}

private def computeAndPublish(time: Long, elems: Long, workDelay: Long, waitDelay: Long): Unit =
Future[Unit] {
val newRate = rateEstimator.compute(time, elems, workDelay, waitDelay)
newRate.foreach { s =>
rateLimit.set(s.toLong)
publish(getLatestRate())
}
}

Socket数据源限流

  批次的限制速率上面已经算出,这里说的是接收Socket过来的数据时的数据限流;
  SocketInputStream类receive方法接收到数据后将数据存入 BlockGenerator的Buffer中,在写入Buffer前调用限流器 (RateLimiter)对写入数据进行限流;
  RateLimiter限流器使用了Google开源的 Guava中内置的RateLimiter限流器,该类只是对Guava限流器的简单封装;
  在Spark Streaming中可通过使用两个参数配置初始速率与最大速率spark.streaming.receiver.maxRate、spark.streaming.backpressure.initialRate;亦可配置PIDController算法相关的四个参数值;
  RateLimiter限流器是基于令牌桶的算法基本原理比较简单,以一个恒定的速率生成令牌放入令牌桶中,桶满则停止,处理请求时需要从令牌桶中取出令牌,当桶中无令牌可取时阻塞等待,此算法用于确保系统不被洪峰击垮。

private lazy val rateLimiter = GuavaRateLimiter.create(getInitialRateLimit().toDouble)    
/**
* Push a single data item into the buffer.
*/
def addData(data: Any): Unit = {
if (state == Active) {
//调用限流器等待
waitToPush()
synchronized {
if (state == Active) {
currentBuffer += data
} else {
throw new SparkException(
"Cannot add data as BlockGenerator has not been started or has been stopped")
}
}
} else {
throw new SparkException(
"Cannot add data as BlockGenerator has not been started or has been stopped")
}
}

def waitToPush() {
//限流器申请令牌
rateLimiter.acquire()
}

Guava库中RateLimiter限流器基本使用:

//创建限流器,每秒产生令牌数1    
RateLimiter rateLimiter=RateLimiter.create(1);
for (int i = 0; i < 10; i++) {
//获得一个令牌,未申请到令牌则阻塞等待
double waitTime = rateLimiter.acquire();
System.out.println(String.format("id:%d time:%d waitTime:%f",i,System.currentTimeMillis(),waitTime));
}

Kafka数据源限流的实现

  在Spark Streaming Kafka包拉取Kafka数据会进行如下动作:
  1、取Kafka中最新偏移量、分区
  2、通过rateController限制每个分区可拉取的最大消息数
  3、在DirectKafkaInputDStream中创建KafkaRDD,在其中调用相关对象拉取数据

  通过如上步骤也可用看出,只要限制了Kafka某个分区的偏移量(offset)范围也就可限制从Kafka拉取的消息数量,从而达到限流的目的,Spark streaming kafka也是通过此实现的;

计算每个分区速率限制,有如下步骤:
  1、通过seekToEnd获取最新可用偏移量与当前偏移量对比获得当前所有分区延迟偏移量
  单个分区偏移量延迟=最新偏移量记录-当前偏移量记录
  2、获取配置项中每个分区最大速率 (spark.streaming.kafka.maxRatePerPartition),背压率计算,计算每个分区背压率计算公式为:
  单个分区背压率=单个分区偏移量延迟/所有分区总延迟*速率限制
  速率限制(rateLimit):为通过PIDController动态计算得来

  如有配置每个分区最大速率则取配置项最大速率与背压率两者中的最小值,未配置则取背压率作为每个分区速率限制;

  3、将批次间隔(batchDuration)*每个分区速率限制=每个分区最大消息数
  4、取当前分区偏移量+分区最大消息数 与 最新偏移量两者当中最小的,由此来控制拉取消息速率;

  如当前偏移量+分区最大消息数 大于 最新偏移量则取 最新偏移量否则取 当前偏移量+分区最大消息数作为拉取Kafka数据的Offset范围;

// 限制每个分区最大消息数    
protected def clamp(
offsets: Map[TopicPartition, Long]): Map[TopicPartition, Long] = {

maxMessagesPerPartition(offsets).map { mmp =>
mmp.map { case (tp, messages) =>
val uo = offsets(tp)
tp -> Math.min(currentOffsets(tp) + messages, uo)
}
}.getOrElse(offsets)
}

  不管是Kafka数据源还是Socket数据源Spark Streaming中都使用了PIDController算法用于计算其速率限制值,两者的差别也只是因为两种数据源的获取方式数据特征而决定的。Socket数据源使用了Guava RateLimiter、而Kafka数据源自己实现了基于Offsets的限流;
  以上所介绍的框架版本为:Spark Streaming 版本为2.3.2与spark-streaming-kafka-0-10_2.11;

参考资料: 

http://kafka.apache.org
http://spark.apache.org

聊聊区块链中的几个技术点

$
0
0

0×00 前言 

随着技术浪潮的涌动,国家政策的推动,区块链又慢慢的进入了我们的视野中。在 2020 年初这个时刻,不妨我们再回头看看区块链的发展,聊聊区块链中的几个技术点,为新的一年打打基础。

2017 年是数据货币大爆发的一年,其标志性事件是 2017 年 12 月比特币价格达到历史最高,并将区块链引入公众的视野中;也因此,2018 年被称之为区块链元年,各类数字货币和与区块链沾边的技术如雨后春笋般出现在互联网上;后来随着区块链的监管力度增大,2019 年则是区块链冷寂的一年,最后经过考验的都是具有价值的区块链项目。

那么本文,我们就抛开数字货币不谈,仅从区块链的方面来聊聊;文中使用比特币 v0.19.x(commit: 0655c7a94cc9bf54d43eceed805e83f1b59e2409) 的源码来帮助理解。

0×01 区块链的简介

区块链随着比特币的诞生而诞生,首次出现于比特币的白皮书中( https://bitcoin.org/bitcoin.pdf),用于存储比特币的交易记录;在比特币中,根据时序将多条交易记录整理集中存储以形成区块,块与块之间采用哈希值的方式连接形成链式结构,我们将这种结构称为区块链。

比特币中,多个节点通过P2P网络共同维护一条区块链,使得这种链式结构具有去中心化、不可篡改、可追溯等特性。后续的以太坊、超级账本等项目也都基于这种链式结构。

这里我们抛开数字货币,把区块链作为主角来看,我们可以更加容易的来理解区块链:区块链就是一个基于P2P的分布式数据库,以多个节点共同维护一份数据;那么从这个角度来看,比特币的区块链存储的「交易记录」也是数据,只是数据比较特别而已。

0×02 区块链vs分布式数据库

我们可以认为区块链就是基于P2P的分布式数据库,是因为区块链和分布式数据库有着相似的目标:使用多个节点来共同维护一份数据。

但我们仅仅以「存储」这一个操作来理解,并且忽略掉了它们本身的应用场景、默认所有节点都是可信、可靠、无延时通信的、等等。实际环境下,我们需要去考虑如上诸多的因素,因此区块链不能等同于分布式存储数据库。

我们使用表格来对比区块链和分布式数据库:

区块链 分布式数据库
架构 分布式 分布式
价值主张 数字化信任系统 高性能存储和访问
网络通信 peer-to-peer client-server
管理方式 集中管理 分散管理
数据结构 链式 索引、等等
节点关系 怀疑 & 制约 信任 & 协作
一致性 共识算法 主从复制
数据持久 数据不可变 可修改 & 非持久
性能

在了解区块链和分布式存储数据库的异同点后,我们可以知道无论是分布式存储数据库还是区块链,都需要去解决分布式中的问题;并且区块链还需要去解决它所特有的问题。所以我们以分布式存储数据库为基础,来帮助我们理解区块链中所涉及到的技术点。

0×03 分布式中的挑战

1.FLP不可能原理 

对于分布式系统中的不确定性,Fischer、Lynch和Patterson三位科学家在1985年发表论文并提出FLP不可能原理:在网络可靠,但允许节点失效(即便只有一个)的最小化异步模型系统中,不存在一个可以解决一致性问题的确定性算法。

所以理论上不存在一个可以完美解决一致性问题的方法,但在工程应用中,我们可以选择牺牲部分特性,以换取一个可行的方案。

2.CAP原理 

那么我们应该如何选择代价,来换取这个可行的方案呢?在 2000 年由 Eric Brewer 教授在 ACM 组织的一个研讨会上提出了 CAP 原理:分布式系统不可能同时确保以下三个特性:一致性(Consistency)、可用性(Availability)和分区容忍性(Partition),设计中往往需要弱化对某个特性的保证。

根据 CAP 原理,我们就可以根据不同的需求,对三种特性进行抉择。如访问分布式的网站的静态内容,可以接受数据延迟更新,这是弱化了一致性;而在区块链中,即便牺牲性能也要保证只有一份公认的数据,这是弱化了可用性。

3.拜占庭容错 

在分布式数据库中,节点之间是相互信任的、是忠诚的,它们可能会离线、宕机,但它们绝不会发送错误的消息;所以我们可以信任任意一个节点,分布式数据库常用「主从复制」实现一致性,也就是:从中选择一个节点作为主节点,其他节点从该节点复制数据,如果该节点出现故障,则重新选择新的主节点。

而在区块链中节点是自由的加入和退出的,可能会出现恶意节点:该节点可能会离线、宕机,并且会发送错误的消息来扰乱数据的一致性;这就是常说的拜占庭将军问题。

这是 20 世纪 80 年代提出的一个假想问题,描述的是:「一组拜占庭将军分别各率领一支军队共同围困一座城市,由于各支军队处于城市不同方向,所以他们只能通过信使进行联系;军队的行动策略限定为进攻或撤离两种,部分军队进攻部分军队撤离都可能会造成灾难性后果,因此各位将军必须通过投票来达成一致策略;每位将军都将自己投票信息通过信使通知其他将军,所以每位将军根据自己的投票和其他所有将军送来的信息就可以知道投票结果而决定行动策略」。

在上图中,由叛军发送错误的投票信息引起不一致的问题,称之为「拜占庭错误」,能够处理拜占庭错误的方法称为「拜占庭容错」(Byzantine Fault Tolerance)。那么区块链中是如何解决的呢?

0×04 共识算法

PBFT算法 

PBFT(Practical Byzantine Fault Tolerance) 算法的提出主要就是为了解决拜占庭错误。其算法的核心为三大阶段:pre-prepare阶段(预准备阶段),prepare阶段(准备阶段),commit阶段(提交阶段),我们以下图来理解该算法。

其中 C 表示发起请求客户端,0123 表示服务节点,3 节点出现了故障,用 f 表示故障节点的个数。

1.C 向 0 节点发起请求

2.0 节点广播该请求到其他服务节点

3.节点在收到 pre-prepare 消息后,可以选择接受和拒绝该消息,接收该消息则广播 prepare 消息到其他服务节点

4.当一个节点在 prepare 阶段并收到 2f 个 prepare 消息后,进入到 commit 阶段,广播 commit 消息到其他服务节点

5.当一个节点在 commit 阶段并收到 2f+1 个 commit 消息后(包括它自己),发送消息给 C 客户端

6.当 C 客户端收到 f+1 个 reply 消息后,表示共识已经完成

(详细可以参考 http://pmg.csail.mit.edu/papers/osdi99.pdf)

PBFT 中节点数必须满足 N >= 3f+1 这个关系,只要节点中的故障节点不超过 1/3 时,就可以完成共识确定一致性。由于 PBFT 算法的特性以及性能问题,所以其常用于小规模联盟链中。

PoW算法 

比特币中使用 PoW(Proof of Work) 算法,即为工作量证明算法。其算法的核心为利用复杂的数学计算竞争一次添加区块的机会,结合「不利原则」,并仅认可最长的链为合法的链 这一规则,完成节点共识。

在比特币中,PoW 的工作方式如下:

1.用户发起交易,由节点广播交易至所有节点

2.节点收到交易打包并将其放入块中

3.某一节点计算出了哈希结果,获得添加区块的机会,将 2 中的块添加到区块链尾部,并广播区块至所有节点

4.节点收到新的区块信息后,验证区块合法性,合法后将其添加到区块链尾部,并进入下一轮的竞争

通过 PoW 算法,比特币可以允许全网有 50% 的节点错误的情况下,依然能够完成共识。

PoW 算法的实现

PoW 算法位于区块生成的模块中(挖矿)。我们先看看比特币的启动流程,比特币程序入口位于 bitcoind.cpp 下,通过这样的调用链启动比特币中的各项服务:

main()->AppInit()->AppInitMain()

其中包括 RPC 服务,在比特币中我们需要使用 bitcoin-cli 通过 RPC 服务启动挖坑,最终到 rpc/mining.cpp/generateBlocks() 这个区块生成主逻辑:

其中 pow.cpp/CheckProofOfWork() 函数进行了 PoW 算法的验证,主要是判断在当前 nonce 值的情况下,区块哈希值是否小于难度值:

其他 

但由于 PoW 算法性能低下、而且会造成大量的算力浪费,大家纷纷提出新的共识算法,如 PoS(股权权益证明)、DPoS(委托权益人证明机制),等等;但以比特币占据区块链项目的半壁江山来看,PoW 仍是目前用得最多的共识算法。

0×05 存储结构

在了解共识算法后,我们可以保证数据的一致性了,那么这些数据是如何在区块链中存储的呢?

Merkle树 

在比特币中,使用 Merkle 树组织和存储一个块内的交易信息,它是一种基于哈希的二叉树(或多叉树),其结构如下:

1.叶子节点存储数据

2.非叶子节点存储其子节点的内容的哈希值

使用 Merkle 树的优势所在:

1.快速比较大量数据,比较根节点的哈希值即可知道两组数据是否相同

2.快速定位修改,任何子节点的变动都会传递至根节点,从根节点向下检索即可找到修改的节点。

3.Merkle 树实现 在比特币中,Merkle 树的生成是挖矿步骤中的子步骤,跟入上文中的区块生成流程中的 miner.cpp/IncrementExtraNonce() 函数中,在该函数中调用 consensus/merkle.cpp/BlockMerkleRoot() 函数以构建 Merkle 树:

哈希链 

在一个区块中,除了打包成Merkle树的交易信息,还包括块高度、随机数、时间等等信息,其中父块哈希值将各个区块联系起来,形成链式结构,如下:

哈希链实现

在比特币中,区块由区块头和 Merkle 交易树组成,区块头数据结构定义在 primitives/block.h,如下:

0×06 网络通信

那么在区块链中,各个节点之间是如何传递数据的呢?相对于分布式数据库 server-client 网络结构,采用主从复制的方式同步数据,区块链则是 peer-to-peer 的网络结构,节点在获取数据的同时,还需要提供数据给其他节点。

我们直接来看看比特币中 p2p 协议的实现方式。

p2p 协议的实现方式

在比特币中,默认在 8333 端口建立 tcp 监听,启动 p2p 服务。在 bitcoind 启动流程的 init.cpp/AppInitMain() 中,对网络进行了初始化启动:

[init.cpp/AppInitMain()]1.node.connman->Start()启动节点入口,网络初始化和建立[net.cpp/Start()]2.InitBinds()
  建立网络监听3.AddOneShot()
  添加种子节点4.&TraceThread<>......启动五个网络处理线程

p2p网络处理流程就由这五个线程进行负责:

其中负责 p2p 协议处理的线程就是 ThreadMessageHandler() 线程,我们主要来看看这一部分的流程;在该线程中尝试对每个节点接收数据,接收到数据就如下的调用流程:

[net_processing.cpp]ProcessMessages()->ProcessMessage()

在 ProcessMessages() 对协议格式进行判断,比特币中 p2p 协议格式如下:

随后进入到 ProcessMessage() 进行实际的消息处理流程,在该函数中主要逻辑是多个 if-else 语句根据 commmand 进入不同的消息的处理流程,支持的消息有:

每个命令下都有不同报文格式和处理逻辑,比特币通过这样的方式,打通了节点间的通道。

0×07 总结

通过本文我们在脱离数字货币的情况下,从分布式数据库的角度,聊了些区块链中的几个技术点,了解到区块链与传统分布式的异同之处,也了解到区块链中的基本概念和原理。

区块链的去中心化、不可篡改性的特性,给我们提供了无限的遐想,但目前还没有公开的、完善的区块链项目出现,我们希望这一天能早点到来。

References: 

1. 《区块链技术指南》 

2. 《白话区块链》 

3. 《区块链原理、设计与应用》 

4.  https://bitcoin.org/bitcoin.pdf 

5.  https://101blockchains.com/blockchain-vs-database-the-difference/ 

6.  https://groups.csail.mit.edu/tds/papers/Lynch/jacm85.pdf 

7.  https://medium.com/ultrain-chain/the-difference-between-blockchain-and-a-distributed-database-556f8361e6b3 

8.  http://pmg.csail.mit.edu/papers/osdi99.pdf 

9.  https://github.com/bitcoin/bitcoin 

10.  https://zh.*********.org/wiki/%E6%8B%9C%E5%8D%A0%E5%BA%AD%E5%B0%86%E5%86%9B%E9%97%AE%E9%A2%98 

11.  https://www.zhihu.com/question/264717547

转载请注明来源

原文: https://paper.seebug.org/1110/

*本文作者:0x7F@知道创宇404实验室,转载请注明来自FreeBuf.COM


Kubernetes 基础概念知多少

$
0
0

kubernetes(简称k8s)是一种用于在一组主机上运行和协同容器化应用程序的管理平台,皆在提供高可用、高扩展性和可预测性的方式来管理容器应用的生命周期。通过k8s,用户可以定义程序运行方式、部署升级策略、动态伸缩容,使得用户以一种更灵活可靠的方式来管理应用程序。

关于k8s,是一种对应用服务的打包、部署、监控等一整套生命周期的自动化管理平台,目前各大公司已在生产环境部署使用,同时k8s社区比较活跃,在未来一段时间内会越来越流行,可以说是以后服务部署的事实标准,对于Java开发者来说,你可以不直接使用它,但是不能不了解它。

总结来看,k8s特点如下:

  • 自动装箱:基于容器,结合调度策略将多种应用部署到同一节点上,提高资源利用率;

  • 自我修复:支持故障转移/重启,具有健康检查机制;

  • 水平扩展:通过命令手动执行扩容,可基于CPU等资源负载率来动态实现伸缩容;

  • 服务发现/负载均衡:通过KubeDNS(或CoreDNS)为系统内置了服务发现功能,为每个service配置DNS名称,service通过iptables或ipvs内建了负载均衡机制;

  • 自动部署:自动发布和回滚,支持灰度、增量发布等;

  • 配置管理:ConfigMap实现了配置数据与Docker镜像解耦,为开发部署提供了良好的灵活性;

  • 批处理:除了管理服务型应用之外,Kubernetes还支持批处理作业及CI(持续集成)。

从k8s的角度看,它把各个待管理的资源进行了抽象,比如针对服务器(物理机或者虚拟机)抽象出Node;对于容器不直接管理而是抽象出Pod来管理容器;对于集群内的服务调用,抽象出service来表示同类型的多个Pod,同时提供负载均衡策略等。

对于初学者来说,k8s的一些抽象资源和基本概念可能会造成一头雾水,本文就k8s的基础概念和组件进行简单分析,让初学者更快了解k8s概念,话不多说,let‘s go~

k8s基础概念

k8s使用共享网络将多个物理机(或者虚拟机)汇集到一个集群中,该集群是配置k8s所有组件、功能和工作负载的物理平台。集群中一台服务器会作为master负责管理整个集群(为了master高可用一般会将master部署成多节点)。

Master是集群的网关和中枢,负责诸如为用户和客户端暴露API、跟踪其他服务器的健康状态、以最优方式调度工作负载,以及编排其他组件之间的通信等任务,它是用户/客户端与集群之间的核心联络点,并负责Kubernetes系统的大多数集中式管控逻辑。单个Master节点即可完成其所有的功能,但出于冗余及负载均衡等目的,生产环境中通常需要协同部署多个此类主机。Node是Kubernetes集群的工作节点,负责接收来自Master的工作指令并根据指令相应地创建或销毁Pod对象,以及调整网络规则以合理地路由和转发流量等。理论上讲,Node可以是任何形式的计算设备,不过Master会统一将其抽象为Node对象进行管理。

几种资源抽象

  • Pod:k8s管理的最小调度单元,k8s不直接来管理容器,使用一个抽象的资源对象来封装一个或者多个容器,这个抽象即为Pod。同一Pod中的容器共享网络名称空间和存储资源,这些容器可经由本地回环接口lo直接通信,同时对于Mount、User及PID等资源也进行了隔离;

  • 标签资源和标签选择器:标签(Label)是将资源进行分类的标识符,k8s的Pod资源选择大都是基于标签来的,一个对象可以拥有多个标签,一个标签也可以附加于多个对象(通常是同一类对象)之上。标签选择器(Selector)全称为“Label Selector”,它是一种根据Label来过滤符合条件的资源对象的机制;

  • Controller:Pod控制器,尽管Pod是k8s的最小调度单元,但用户通常并不会直接部署及管理Pod对象,而是要借助于另一类抽象——控制器(Controller)对其进行管理,k8s的控制器包括ReplicationController、ReplicaSet、Deployment、StatefulSet、Job等,每种controller都有对应的功能(比如Deployment是最常见的无状态应用的控制器,它支持应用的扩缩容、滚动更新等操作,为容器化应用赋予了极具弹性的功能);

  • Service:Service是建立在一组Pod对象之上的资源抽象,它通过标签选择器选定一组Pod对象,并为这组Pod对象定义一个统一的固定访问入口(通常是一个IP地址);

  • 存储卷:一般是独立于容器文件系统之外的存储空间,常用于扩展容器的存储空间并为它提供持久存储能力,大体上可分为临时卷、本地卷和网络卷;

  • Name和Namespace:名称是网络资源的唯一标识符,通常在一个命名空间内,名称标识是唯一的,名称空间通常用于实现租户或项目的资源隔离,从而形成逻辑分组;

  • Ingress:k8s中将Pod进行了网络隔离,如果需要开放一些Pod供外部使用,则需要一个配置一个流量进入k8s集群内的通道,除了Service外,Ingress也是实现策略之一。

k8s组件

一个典型的k8s集群由master节点、多个工作节点和ETCD组成,其中ETCD作为集群状态存储。master节点负责整个集群的管理工作,为集群提供管理API,并负责编排和监控各工作节点,各工作节点已Pod形式管理运行容器。master主要由apiserver、controller-manager和scheduler三个组件组成,同时负责与ETCD教育存储集群状态数据,而每个工作节点主要包含kubelet、kube-proxy及容器引擎(最常见的是Docker)等组件。各个组件整体如下图:

master节点

master包含组件如下:

  • API server:对外提供restful api,k8s集群的网关;

  • Controller:Pod控制器,k8s通过控制器来管理Pod资源,控制器包括ReplicationController、ReplicaSet、Deployment、StatefulSet、Job等,每种controller都有对应的功能(比如Deployment是最常见的无状态应用的控制器,它支持应用的扩缩容、滚动更新等操作,为容器化应用赋予了极具弹性的功能);

  • Scheduler:K8s调度器,K8s管理成千上万容器资源,api server接收到请求之后就由Scheduler按照对应调度策略进行在不同Node间的请求调度操作;

  • ETCD:k8s集群状态都是存储在etcd中(通过API Server共享给集群的各组件及客户端),通过etcd的watch机制来进行k8s各组件的协同操作,一般etcd通过集群部署方式保证高可用。

node节点

node节点接受master的管理,负责管理各个Pod资源:

  • kubelet:kubelet是node的守护进程,node接受master的管控,Kubelet会向api server注册当前node,定期向master会报node资源占用情况;

  • 容器运行环境:node会提供一个容器运行环境,负责下载并运行容器,k8s目前支持的容器运行环境包括Docker、RKT、cri-o和Fraki等;

  • kube-proxy:每个node都需要一个kube-proxy进程,比如对service按需生成iptables或ipvs规则,控制流量访问。

核心组件

k8s除了etcd、master、node这几个组件之外,还有一些核心组件,如下:

  • DNS服务:目前k8s使用的是coreDNS,之前使用的是KubeDNS;

  • Kubernetes Dashboard:k8s的管理web UI;

  • Heapster:容器和节点的性能监控与分析系统,它收集并解析多种指标数据,如资源利用率、生命周期事件等。新版本的Kubernetes中,其功能会逐渐由Prometheus结合其他组件所取代。

  • Ingress Controller:Service是一种工作于传统层的负载均衡器,而Ingress是在应用层实现的HTTP(s)负载均衡机制。不过,Ingress资源自身并不能进行“流量穿透”,它仅是一组路由规则的集合,这些规则需要通过Ingress控制器(Ingress Controller)发挥作用。目前,此类的可用项目有Nginx、Traefik、Envoy及HAProxy等。


PingCAP 的 5 年远程办公实践

$
0
0


前言


2020 年的春节注定是一个不平凡的春节,全国都在抗击新型冠状病毒肺炎。除了不出门,勤洗手,戴口罩之类的常规操作,我们就在想,在这个大背景下,我们还能够做哪些事情?考虑到春节假期临近结束,返程的旅途中可能会加大传染的概率,延长隔离时间、远程在家办公也许是普通群众能给国家在这场战役中做的最大贡献。然而在我们国家,暂且不论别的行业, 至少我们所在的高科技行业还没有普及远程办公的文化,所以我们在此将 PingCAP 实践了近五年的工程师远程办公经验介绍给大家。本文将尽量少描述理念,而更多的从实践方面讲述我们的落地经验,以期在这样的一个特殊的时刻帮助更多的朋友和公司尽快行动起来,为国家为社会贡献一份我们微薄的力量。我们已经通过实践证明,在这个时代,至少对于类似软件工程这样的主要以脑力和创意为主的工作,已经有足够的方法论和基础设施,让远程工作的效率不比传统模式差,有时候甚至能有更好的产出(相信已经有同学想起了早上拥挤的交通对心情和思维的副作用)。下面我们聊聊一些具体落地的经验。

01

远程办公的管理哲学

远程办公在国外并不是一件新鲜的事情。在硅谷,尤其是新一代的科技公司几乎都有远程工作的基因,这背后有很多原因在这篇文章中就不展开了,如果感兴趣的朋友可以看看来自 37 Signals 的 David Heinemeier Hansson 的《Remote》一书。对于我们来说,PingCAP 从公司建立之初就开始践行这个文化,主要出于这几点原因:一方面包括我在内的几位联合创始人都是工程师出身,本身对于效率比较有追求,自由的工作形式能够提高我们的工作效率,同时我们痛恨低效形式主义;另一方面,对于一个初创的公司来说,我们不希望人才因为地域的限制而不能加入我们。

一个很好的例子是我们的首席架构师 siddontang,也是我们招聘的第一位员工,因为家庭原因不希望来北京,过去的几年一直都在珠海的家里远程工作(这篇 blog 详细描述了他的亲身经历:https://github.com/siddontang/blog/blob/master/2016/my-remote-work.md)。另外一个非常重要的原因是我们的员工是全球分布,基于开源的开发模式,所以一开始就注入了远程工作的基因。

软件工程是一项以脑力为主要资源开展的工作,在如今高度发达的互联网技术支撑下,其实是天然适合远程工作的,但是我们为什么大多数时候觉得远程工作不如集中工作效率高? 除了远程带来的沟通协作障碍外,我们认为其实最根本的差异还是在管理哲学上,是倾向于传统监管的管理思维还是自驱的管理思维,在 PingCAP,我们在企业文化上一直倡导的是后者。为此,我们需要建立一个强大的愿景驱动力,并落实在我们的各种细节中,同时尽可能为同事们营造自由、开放、分享的工作氛围。

幸运的是,这也和我们从事的开源领域的工作方向完美契合。举个例子,在 PingCAP 我们从来不进行任何形式的打卡,每周五我们都有例行但是议题不限的员工 TGIF 分享 ,任何一位同事都有机会站在台上分享自己的工作成果和心得,甚至我们发给大家的周边产品都是在设计、选材上一遍一遍的精挑细选,且限量供应,以期让每一个小伙伴感受到温暖和尊重。这一切的工作看似和我们的远程办公没有直接关系,但是实际上让我们一点一点地变成了一个脱离形式、高于形式而存在的强大的远程组织。


02

目标和计划管理

如果问一个问题,对于工程师团队来说,什么时候需要沟通最多?我想是制定计划和目标的时候。

软件工程远程办公我们首先要解决的是我们要建立远程可操作的更加清晰、高效的目标和计划管理。从宏观层面说,在 PingCAP 我们依赖的是 OKR 这个工具进行公司以及团队的目标管理,OKR 是硅谷以及国内的很多互联网公司越来越流行的目标管理工具。经过探索,我们认为 OKR 是一个比较适合远程工作团队的目标管理工具,因为 OKR 相比 KPI 来说,首先更加强调由团队成员共同制定团队目标,这样带来的好处是易于让整个团队就目标和关键结果达成共识,始终保持团队的目标导向一致。其次能够让团队成员更加明白做手头上的事情对于团队以及对于公司的意义,这一点对于远程团队尤为重要,极大的有利于促进部门与部门、人与人的协作,让团队更加具有整体性,最后,OKR 还有一个很重要的特点:透明,在我们的实践中,每个团队都可以看到别的团队的 OKRs,大家在制定完各自团队的 OKR 后,还需要在公司级别宣布,确保大家都能了解。

从微观层面说,例如一个具体的项目计划制定和执行跟踪,也需要一样的透明。我们的实践是项目的负责人为每一个大的项目建立一个全局的项目「地图」,力求做到即使是半路加入的同学,看到这个地图后,就能够清楚的知道现在是什么情况,需要的资源的链接在哪,负责人是谁,风险点在哪。这个对于远程工程团队的管理者来说更是至关重要。下面是一个例子:

某个项目事项追踪表 当我们把我们的目标和计划能够清晰、高效、透明的在整个公司内部制定、发布和管理起来,远程办公已经具备了初步的可操作性。

03

工欲善其事,必先利其器

既然我们这里更多的是讨论实操,我们接下来重点讲一讲软件工程远程办公环境我们所使用的工具。企业文化、目标管理我们需要相对长时间的工作去逐渐建立,工具的引入则相对快速见效,也就是俗话说的,工欲善其事,必先利其器,使用良好的工具会让事情事半功倍。PingCAP 的主要产品 TiDB 是一个开源的数据库,我们研发的主要工作流都是构建在 Github 上面,完全对社区公开。所以我们的工具链也是以 Github 为中心,串联其它的工具,下面是完整的工具列表(这些工具很多都有国内的替代工具,如果公司不像 PingCAP 这种员工全球分布的,可以根据实际需求选择):
  • GitHub:代码托管,公开的 RFC,社区 Issue 反馈,产品发布,Code Review 等。
  • Zoom:在线会议。
  • Slack:即时通讯,机器人消息中枢。
  • 微信、企业微信:即时通讯(没错,我们两个都用,但以企业微信为主)。
  • 在线文档:文档协作,幻灯片,表格。
  • 邮件,日历。
  • Confluence:内部的文档,包括已成型的设计文档(如内部的 RFC 文档),Wiki 等。
  • Jira:Bug 和 Milestone 跟踪。
  • Trello:看板,记录一些重要客户和事件的备忘。
  • Jenkins:持续集成,daily build。
这里通过一个小例子介绍一下我们研发的工作流:1. 假设我们需要做一个新功能,从构思开始,可能第一个会使用的工具是在线文档,负责的同事会草拟一个文档,将大致的想法在其中描述,然后通过 Share 的功能,分享给相关的同事,大多数时候这些设计文档都会 share 到所有的工程师都会在的邮件组里,任何人都可以上去评论或者编辑。2. 文档经过沟通讨论定稿之后(沟通环节我会在下面一节重点介绍),会同步到 Confluence 和 GitHub 中(如果可以公开的话,英文版会发到 GitHub 上)。3. 接着该项目将被拆分成多个子项目,通过 JIRA 分配到具体的人,完成后直接提交到 GitHub 上,项目的该模块的 Reviewer(也包括 Maintainer)会参与 Code Review,收集到两个 LGTM(Looks good to me) 并通过各种持续集成工具的测试后,最终合并到主干。修 Bug 的流程也类似,值得一提的是我们开发了一个 bot,用于同步 GitHub 中来自社区的 Issue 到内部的 JIRA 中。 优秀工程师的创造力是无穷的,尤其在远程工作的背景下,我们非常鼓励工程师通过自制工具来提升工作的效率。除了上面提到的 Issue 机器人,我们的 Chaos 测试(最近已经开源, https://github.com/pingcap/chaos-mesh),CI/CD,甚至包括社交网络上简单的动态舆情监测,都有自动化的工具在做。还有小伙伴们通过自动化的手段优化自己工作中的一些流程,举几个好玩的例子:disksing 同学使用 App Script 自动生成自己的周报(http://disksing.com/review-recorder/);siddontang 同学自己写了个小工具 github-cli(https://github.com/siddontang/github-cli)来高效的追踪关注的 Github 项目的动态;我用 Python 写了一个小脚本,每日收集 Trello 上指定 Board 内的卡片的更新,并给我汇总发邮件……这样的例子数不胜数,有时候真是很佩服大家想象力和动手能力,我们非常坚定地鼓励大家做这些事情。 我们的 IFTTT 机器人在收集提及 TiDB、PingCAP 的推文 由我们的 bot 同步的 Github Issue 每天下班之前自动生成的当天动态报告 每周周会之前自动生成的 Weekly Report
提前根据模版生成出来的个人周报 提醒大家准备周报的企业微信机器人 SRE 机器人自动 Merge PR 并 Cherry-pick 到 Release 分支 介绍了这么多好玩的东西,其实我想表达的是:对于远程工作来说,能交给机器做的,尽量不要人来做,自动化是至关重要的。尤其对于线上的协作来说,多一个人的参与就意味着多一份沟通成本。我对于工程师团队选择开发相关的效率工具,有几个建议:1. 选择有开放 API 的工具,方便写 bot,形成协同效应。2. 切忌讳过多过杂,选择几个好用的,将其用透。3. 使用 Slack 之类的 IM 作为各种工具的 Message Hub,尽可能做到在一个地方就能一目了然事情的状态。另外就像上面提到的,由于我们也有一些海外的同事、客户以及海外社区沟通的需求,所以主要选用的工具基本都是国际上比较通用的,如果你们公司的业务都在国内的话,这些工具基本都可以找到国内的或者私有部署的替代方案,例如 ONES,Tower,Gitlab 等。

04

对远程友好的沟通和协作机制

如果说上面聊的工具只是基础的话,那么远程工作最大的挑战就是沟通了。对于一个成熟的团队来说良好的沟通一定是必不可少的,甚至沟通的品质决定了做事情的品质。 并不是说因为远程工作因为条件约束,就少沟通甚至不沟通了,相反的,在这种环境下我们的沟通可能会更多更细致,只是形式并不仅仅限于面对面的会议这种形式而已。

在聊我们的沟通实践之前,我想先聊聊沟通的意义,首先我认为沟通最重要的意义在于: 信息拉平。对于一个远程的团队来说,用大白话来说也就是:大家需要互相都知道自己该干嘛,团队正在干嘛以及该干嘛。这件事情在很多公司是通过大大小小的会议,或者甚至吼一嗓子完成的。 但是在一个远程的团队中,沟通这件事情需要做得更加的透明。即使是远程,大部分时候会议仍然是最高效的信息拉平方式,类似 Zoom 这样的视频会议工具已经提供了很好的平台,而且智能手机和移动互联网的普及也让会议的参与变得更加的便捷。这里多提一句,PingCAP 是 Zoom 的重度用户(也是企业客户),Zoom 的用户体验真的非常棒,我们即使是全公司级别的会议,也都是通过 Zoom 完成的(昨天刚得知一个令人振奋的消息,也给 Zoom 做个友情广告,目前国内用户访问 zoom.com.cn 可以免费不限时长使用,直到疫情得到有效控制之日)。在 PingCAP 从形式上来说,因为会议基本都会有远程的同学参与,所以默认都是线上会议。从内容上来说,大概有两种会议,一种是例会,一种是具体业务的沟通会。相信和别的公司也没什么不一样,我这里聊聊我们觉得比较好的一些实践:在 PingCAP 各个团队(包括虚拟团队)大大小小一定都会有例会,通常以周为单位,有些比较重要且紧急的项目会以天为单位,会议的时间和长度也不一定。周会是一个很好的机会能让团队成员互相了解大家在干嘛,对于 Manager 也能很好的知道方向有没有歪,进度是否正常。另外一点,分享一些关于会议的实践: 1. 对于类似例会这种偏信息拉平的会议,Manager 最好不要直接在这类会议上做决策。因为很多信息可能是刚接收到马上做决策不一定是经过深刻的思考,另一方面信息可能不全面,还需要进一步的跨团队沟通。 2. 善用 Calendar。我建议研发团队内部将 Calendar 可以别人可见(这点上财务,商务,高管团队需要酌情考虑),通过订阅和你相关的同事的 Calendar 是一个也是一个很好的信息拉平渠道。 3. 会议前发 Agenda,会议后形成 Meeting notes 发给参会的人,并记录在 Wiki 中。4. 尽量少开大会长会。Amazon 的「两个 Pizza 原则」也同样适应于开会(这点说起来简单,其实做起来很困难,尤其在跨团队协作上,我们也在努力)。这里说几个我们亲身经历的坑。因为远程的关系,在 PingCAP 我们一直以来要求尽可能的使用文档进行沟通,我们确实在早期很严格的践行了,但是那个时候我们重度依赖在线文档,于是陷入了一个问题:协同功能很棒, 但是索引功能很弱。于是很多时候就出现了,可能记录某件事情的文档找不到了,或者有多个文档(很多甚至只是讨论稿)在描述同一个事情。为了解决这个问题,我们后来引入了 Confluence,用做团队 Wiki,主要起到信息索引和搜索的功能,我们非常依赖 Confluence,并且玩出了很多花样,这里我只举几个最佳实践的例子:1. 给每个团队创建团队的 Page(类似前面提到的「地图」的概念)索引一切和这个团队相关的内容,让新人能够一目了然。例如下面是来自 TiKV 团队的例子: 2. 团队周报和个人周报,每个团队的周报会一层层的汇总和归纳,在每周的高管例会前发出。所有的周报都在 Confluence 里被索引。3. Logbook,这个是我们比较有意思的东西,对于一些带有探索性质的工作,例如一些小实验,性能测试,一些特殊场景的优化等等。我们也会详细的记录下来,形成一个个实验 logbook,这些 logbooks 可以通过关键字被 Confluence 的检索到,一是作为未来实现或者输出成文章的素材,二是防止将来做重复的工作。 在内部协作全面引入 Confluence 后,我们的文档信息碎片的问题得到了比较大的缓解,唯一美中不足的是 Confluence 的移动端做得实在不尽如人意,希望 Atlassian 团队未来能改进。 另一个坑来自于 IM 工具的选择。这个可能也不是坑,更多的是由于微信平台本身不是为了办公场景设计的带来的问题。由于我们多数的国内客户都倾向于使用微信作为沟通的渠道,作为一个 toB 企业,我们必须去适应这个现实(比如我手机上有几千个微信群),这个问题导致了我们 IM 沟通的碎片化,而远程工作的环境会进一步放大这个问题。可能同一件事情,有些同学看着微信,有些同学看着 Slack,这就导致了消息不对称。再者微信是一个封闭系统,没有 Open API,很难通过技术的手段同步到一个平台。另一个问题是,微信这种用法,工作和生活很难分开,有时候很令人苦恼。这个问题通过引入企业微信得到了一定的缓解,但是因为企业微信又是一个新的 IM,也是一个封闭系统,信息碎片化的问题和海外同事使用习惯上的问题仍然存在。在这个方面,我们仍然在探索。

05

远程办公环境下的自我管理

远程办公还有一个很重要的方面是个人的心理建设和自我管理。这点因人而异,其实很多人不太适合长期 work from home,例如我远程工作的时候一定要从家走出来,去个咖啡厅或者 WeWork 之类的地方才能进入工作状态,但是我们的首席架构师就可以五年如一日将他家的书房当成办公室。人无疑是最重要的一环,不过在这篇文章中,我并不想展开太多,有机会再详细聊聊,这篇文章我希望尽量关注比较普适的方法。 在远程环境下,需要工作者能够克服孤独感,并且由于没有同事在身边,需要比较强大的自律精神克服倦怠感。在这点上,我推荐使用一些个人时间管理工具,例如番茄钟,日历等工具。但是和公司选用工具一样,切忌贪多,选择一个用透最好。另外在生活中也保持一个规律的作息习惯也会很有帮助,这点在上面引用的 siddontang 那篇博客(https://github.com/siddontang/blog/blob/master/2016/my-remote-work.md)中有很好的阐述。 另外一点比较重要的是,很多工程师可能是一个比较内向的性格,遇到困难的时候,尤其是在远程的环境下,容易钻牛角尖。这种情况下,一定切记要主动的求助和沟通,甚至可能需要比面对面的环境下更加频繁的沟通。 对于管理者来说,要做到这点,需要将任务拆解得足够细,在前期沟通需要反复确认是否和远程工作的同学达成一致,这个环节需要非常的重视。

06

Online and Offline(线上与线下)

PingCAP 并不是一个彻底的每个人都远程办公的公司,我们仍然在各个大城市有我们的办公室(北京、上海、广州、深圳、成都、杭州、硅谷)。就像上一节说的,远程工作看起来很美,但是可能也并不适合每一个人。人是社会化动物,很多时候我们仍然需要从线上走到线下,和同事一起吃顿饭,聊个天。因为这点,我们的解法是: PingCAP 使用远程的工作方式和文化,但是仍然保留着各地的办公室,所以我们有一个不成文的惯例,当每个城市的员工超过 4 个人的时候,就可以找个办公室了。在各地 Office 的运营上,也是比较有 PingCAP 的特色的。很多传统公司的各地子公司通常是定位特殊的办事处,例如销售,测试,研发等。但是由于我们的远程办公文化, 我们各地的 Office 其实更像是一个虚拟的组织,也就是说可能同一个团队的同学会隶属于不同的 Office,或者反过来,每一个分公司都可能会有不同职能、不同团队的同学。这样是有好处的:1. 作为一个 toB 公司,我们国内的客户也主要分布在几个主要城市,在客户当地有分公司能更方便的开展客户支持和市场活动。2. 在同一个城市的办公室内有不同部门的同事,有助与构建更多样化且健康的文化,也能更顺畅的进行跨团队合作。办公室的 Manager 更偏向于当地办公室具体事务和活动的管理和组织,另外每个 Office 都会有一个行政来处理日常的事务。所以,通常我们的 Team building 会有几种:1. 当地 Office 自己会有 TB 的经费,可以自己组织活动。2. 每当团队出差到同一个地方的时候,组织团队的 TB(当然,我们大多数是程序员,基本就是吃吃吃)。这里提到了出差,顺便介绍一下,我们建议远程研发团队的 Managers 大概一个月需要尽量和团队的大多数成员 Face to Face 的见一次面,这些行程通常可以和客户拜访安排在一起。 线下的沟通可以让线上的交流更加顺畅。



总得来说,远程办公并非十全十美,像我们这样的科技公司具备天然的文化和规制土壤,但仍然有很多地方有继续改进的空间,欢迎大家给我们多提建议。很高兴在国内远程办公文化尚未普及之时,能够用这么长的篇幅为大家分享一点落地经验。在这个特殊时期,我们在家不动的同时劳动创造价值,也算是为社会做了点微小的贡献 





「在家办公」无聊又寂寞?你该试试这 16 个远程协作神器

$
0
0

昨天,国务院正式宣布将春节假期延长至 2 月 2 日,但出于安全考虑,部分公司也许会选择短期内安排员工在家办公。

爱范儿编辑部在广州和北京均设有办公室,线上协作一直都是日常工作的一部分。今天,我们将分享编辑部日常远程协作的工作流以及相关工具,希望可帮助大家找到一个高效的远程协作方式。

懒人目录

  • 团队沟通
  • 任务管理
  • 在线协作
  • 文件传输
  • 素材管理

团队沟通

企业微信

企业微信是微信旗下的企业通讯协作应用,是我们工作的主力沟通工具,应用场景主要有三个:日常沟通、电话会议和 OA。

在日常沟通场景下,企业微信优势主要有两个:一方面是可让发出方知道目标接收方是否收到信息,另一方面则是可减少上班用微信的干扰。

  • 利用「已读」确定已知会目标受众

在用企业微信之前,HR 或主编在微信工作群发个通知,总难免迎来刷屏的「已读」回复。如果在企业微信发通知,每条信息都会显示其阅读情况,发出者可清晰看到群中的哪些人已读/未读该信息,进而确定消息是否传达到位。

另一个场景是,我们的文章在推送前会先在群中进行预览确认,「已读」功能可帮助我们确定群里此刻的冷清,到底是因为主编没看到信息呢,还是他们看到了在思考是否要调整。如果在紧急的情况下遇上前者,那消息发出者就可以直接给主编打电话联系(在企业微信上可查看所有同事的电话号码)。

  • 在企业微信聊工事,减少干扰

大家基本有个共识,在企业微信就是聊工事,所以沟通起来更直接和言简意赅。

虽然无法在工作时间内完全不看微信,但在赶重要工作时,我们还是可短时间关闭微信,只留着企业微信,在尽可能减少外界干预的情况下不错失重要工作消息。

  • 内置电话会议功能

爱范儿编辑部的一天是从晨会开始的。

每个工作日早上,主编都会直接用企业微信群里的「语音通话」功能,连线广州和北京办公室的同事们,完成一天选题的讨论工作。我们一般会先在群里发出自己当天的选题安排以及相关资料,并在电话会议中对选题逐一展开讨论。

  • 繁复的审批工作走 OA

企业能在企业微信的「审批」功能中按需设计不同审批模块,设置审批人、知会人、所需提交的信息和资料等。这样一来,异地办公和临时出差都不会影响申请/批准各类请假和物资申请等任务。

除了以上功能,企业微信还提供企业云盘、文档协作和日程提醒等功能。

其它类似应用:钉钉

阿里巴巴旗下的钉钉同样可满足日常工作沟通需求,也搭载了丰富的协作模块。相比之下,企业微信以个人微信作为登陆账号,可免除另外注册,进入门槛更低,而钉钉支持的第三方扩展应用则更多,大家可按需选择。

针对会议的产品:腾讯会议

对于只需要在这个特殊时期使用在线会议功能的读者,我们建议可使用腾讯会议。

新用户只需绑定手机号就能开始使用,操作界面简洁明了。在首页,一共就只有三个按钮:加入会议、快速会议、预定会议。

「快速会议」就是马上发起会议,可直接通过企业微信、微信、QQ 等方式发出邀请,接收方一点击即可加入会议。「预定会议」就如其名,是预约会议,分享和加入方式不变。

「加入会议」则是与会者快速加入的入口,只需输入会议号即可直接加入会议。

鉴于今年春节的特殊情况,腾讯会议前天宣布将对所有用户免费开放最多同时 300 人在线会议的服务,一直延续到疫情结束。

任务管理

Google Docs

虽然大家每天看的推送文章都是编辑写的内容,但那些确保每天内容充足,推送准时的却是运营的同事。他们在编辑部里承担日常任务管理的责任。

Google Docs 是 Google 旗下的免费在线文档。简单来说,它有点像将 Office 三件套搬到了线上,提供多人协作实时编辑功能,还提供了文件内「聊天室」功能。只需简单一个链接,就分享给同事,并设置不同的编辑权限。

▲ 每周选题会前,运营都会发出选题表链接提醒大家填写选题

我们选择 Google Docs 除了因为其编辑功能强大,还在于它和我们的企业邮箱(Gmail)相关联。即便获得分享链接,也只有公司邮箱登陆用户才能打开查看。

我们主要用 Google Docs 中的表格(就类似 Excel 那样的文件)来管理长期的选题和排期。在「大内容排期表」下包含了多个不同的工作表,像「选题会登记表」和各个公众号的排期表,需要编辑和运营共同更新维护,记录内容时间跨度长,整个部门都可见。

其它类似应用:WPS

WPS 提供在线协作文档编辑功能,同样合适沉淀时间跨度长的精细内容,更重要是不用搭梯子。软件的部分付费企业版提供了企业组织架构功能,即可限制只有企业员工才能查看/编辑协作文档。

Trello

除了编辑部的作者,爱范儿还有一批长期合作,遍布各地的的外部「天才作家」,而 Trello 是我们用来和他们协作管理项目的工具。

Trello 看起来就像是一块「白板」,你可以将项目按流程划分为多个阶段(以列表形式呈现),而且还能像移动白板上的便签纸一样方便地将任务从一个阶段拖到另一个阶段。

以我们为例,「天才作家」的管理板里就包括了「提交新选题」「已通过选题」「正在写选题」等列表,追踪工作中的每个节点。

想提交一个新选题,作家可在「提交选题」里添加一张新卡片,撰写思路,附上相关超链接、图片、文件,添加协作成员,设置项目截止日期等。与此同时,管理编辑也能在卡片中的留言功能和作者讨论内容。

和年度排期内容文档相比,Trello 这种管理工具更像是一个功能丰富的 To-do 应用,更适合用来督促零散任务的达成。

其它类似应用:Teambition

Teambition 是一款类似于 Trello 的国内产品,同样采用直观的「看板」任务管理界面。相比之下,Teambition 有个优势是接入了钉钉的服务(被阿里收购后去掉了和企业微信连接)。用户可将钉钉里的企业结构和 Teambition 衔接起来,在企业通讯软件中可一并管理任务。

除此以外,Teambition 还内嵌日历功能,用户可选择在日历中查看自己每日工作安排分布,同时也可关注同事,掌握合作团队中整体时间安排。

在线协作

石墨文档

爱范儿的员工众所周知,公司后台编辑器的稳定性可以说是薛定谔亲传,虽然通常都安分守己能够好好完成编辑和发布文章,可一旦碰上崩溃再打开看到一片空白时就欲哭无泪了,某刘姓主编就曾数次中招,所以一个趁手的编辑器就像士兵有枪在手一样重要。

在不同的文本编辑器中也分为本地和在线两种,其中本地编辑器大多用于独自写稿,而在线编辑器由于一般具备多人协作功能更适合用于多人合写。

石墨文档是目前我们在工作中使用频率最高的多人在线协作工具。比如前段时间爱范儿关于 2010 年代几篇稿子中的主要内容就是分工协作在石墨上完成的。

石墨作为一款多人协作软件,出生时就带有不少小众软件小而美的特点,这一点在爱范儿是一个比较讨好的加分项,当然审美这个加分项也得首先在满足功能需求的基础之上。

石墨的注册路径非常灵活,除了支持邮箱登录之外,还支持微信和钉钉等多种账号注册,基本上涵盖了中小型企业最常用的注册路径。

从实际协同写作的角度来说,石墨无疑是好用的,如果你是爱范儿老读者的话或许可以发现其实许多大型发布会,像 Google I/O 或者苹果秋季发布会等虽然前台的作者账号只有一个人,但实际上大多都是合写完成。

多人共享编辑和实时保存这些基本功能就不再细讲了,石墨的优势在我看来是无门槛的公开分享编辑和非常鲜明的多人修订痕迹,另外较为完善的移动客户端也是加分项。

前者会让多人协作更加轻松,因为你可以看到每一段每一句都来自于谁,在最后统稿之前每一次小修改都显得井然有序,同时只要一个简单的公开链接就能让非注册用户也能无门槛加入其中。此外石墨排版的精致明晰,整体使用起来有种赏心悦目的感觉。

当然发展了数年的石墨在今天也离尽善尽美有相当的距离。首先服务器和页面的稳定性就不如大公司的产品,偶尔的崩溃碰见了就要认倒霉。此外对于流行的 Markdown 语法支持也比较有限。

其他类似应用:腾讯文档、有道云笔记

腾讯文档是一款腾讯开发,支持多人在线协作办公软件,在定位上和石墨文档差不多,比 WPS 和 Office 套件相对更轻量。

比起石墨文档来说,腾讯文档最大的优势就是「不用注册」,因为用 QQ 号或者微信号就可以直接登录,而这两个账号已经是国民级的体量,要知道注册过程其实是软件使用的第一道大门槛。

腾讯文档在使用体验方面大体和石墨近似,但是背靠腾讯的体量,还支持小程序形式使用,非常适合 QQ 和微信用户在移动端进行分享协作。

在石墨文档和腾讯文档外,有道云笔记也同样具备云协作的功能,除了支持网易邮箱登陆之外,也支持 QQ、微信、微博和钉钉等账号登录。

文件传输

百度网盘

在日常工作中,出差跑会是我们工作的一部分,在参加发布会或是其它大型活动的时候,经常碰到需要从前方回传视频素材等内容到后方进行剪辑的情况,用什么工具回传一直是一个比较头疼的问题。尽管目前有很多支持文件回传的小工具,但从稳定性和容易上手程度来说,还是选择了给百度网盘「氪金」。

百度网盘最大的特点就是开了 VIP 之后使用起来畅通无阻,但是免费情况下过狠的限速问题导致寸步难行。我们的方法是为团队公共账号开通 VIP。但这样还有一个问题就是每当更换设备的时候,比如在前方更换另一台手机录制视频时都要重新登陆 app,这时就需要及时向后方获取验证码。

因为很多人都在用这个账号,加上大多数素材回传时间比较吃紧,所以后方一般也不是从应用内直接下载,而是让前方通过分享链接的方式获取素材。

从速度、稳定性、功能性、上手度上来说,氪金后的百度网盘在大多数方面都做的不错,至少对于一个规模不大的企业来说,一个百度网盘 VIP 账号就足够了。

其它类似应用:Google Drive、文叔叔、奶牛快传

Google Drive

因为公司提供了稳定的网络环境,所以部分需要出国出差的活动比如 CES、MWC 或是 IFA 这种大型活动时也会使用 Google Drive 进行素材回传。

Google Drive 的优点是在国外使用速度很快,充钱只是获取更大的空间容量,但是在速度上没什么限制。当然这种情况一般就是使用个人 Google 账号上传分享了,毕竟在海外使用 Google 还是相当便利的。

文叔叔

「文叔叔」是 AppSo 之前介绍过的一款云存储产品,具备 20GB 的免费空间并且不限速的特点相当有诱惑力。「文叔叔」还能够在没有登录的状态下分享文件,这一点对于所有人来说都非常友好。

「文叔叔」另外一个有趣的功能点是支持将文件以链接的形势直接发送到邮箱或者通过短信发送到手机,省去了再次复制粘贴的麻烦,最大同时支持发送 10 个邮箱或者手机号码。点击链接就能下载文件,同样也不用登录。速度上看能够达到 30M/S,20GB 的空间对于非视频为主来说也足够使用了。

奶牛快传

「奶牛快传」属于在手机上非常好用的一个临时大文件传输服务,特点是用完即走,不需要下载 app,直接搜索小程序就能够使用。

在非登录状态下「奶牛快传」支持的传输文件不能大于 2GB,文件可以保存 72 小时。选择微信或支付宝登录后文件传输就没有大小限制了,文件可以保存 168 小时(7 天)。

速度方面「奶牛快传」不算非常快,但是对于手机使用来说也足够,实测显示上传 100MB 的文件大概在 1 分钟左右,下载速度基本在 5M/s 以上。掐指一算,下载个 100M 的视频也就是 10 来秒的事情。

素材管理

群晖 NAS 云盘

除了出差跑会的需求之外,内容团队还有大量的素材是可以复用的,比如大量的产品照除了会在评测体验中用到外,在之后的行业文章中也会复用,这个时候使用自有素材无疑是更好更安全的选择。

为了更方便的取用自有素材,搭建一个私有云 NAS 几乎是一个最佳的选择。但是使用 NAS 一个比较烦人的地方就是图片检索,毕竟作为搜索引擎,Google 仍然是这个星球的第一名,而存储在 NAS 中的图片就需要手动来添加一些标签以方便后期检索了。

爱范儿的 NAS 使用的是 Synology 群晖科技的产品,除了使用电脑之外也支持手机下载 Synology Moments 使用。并且图库支持智能分类,可以区分不同的人物、主题和位置等,虽说有的不太准。

其它类似应用:Eagle 

在搭建 NAS 之前内容部门使用过一段时间 Eagle,这是一个图库应用,支持连接到 Google Drive、Dropbox、One Drive 和坚果云等云端存储服务。只要把云盘的资源库挂到这个图库上,Eagle 就会根据云盘的同步状况进行实时读取,这样的话,你在 A 电脑修完的图导入到资源库后,连接了 Eagle 的 B 电脑就可以直接使用。

大众小众不重要,多尝试才能找到趁手的工具

以上就是爱范儿内容团队日常工作使用频率最高的几款软件,当然这些软件也并非一成不变,比如之前进行电话会议的时候也使用过 Zoom,但随着一些变动最后被企业微信所替代,而像 NAS 图库等则是去年在日常工作过程中感到不便才新搭建的。

其实可以看到,我们使用的工具有大众也有小众的,有国产也有非国产的,重点是在个人使用与团队配合中逐渐找到最适合最趁手的工具,这样才是提高效率的关键。而我们也希望在今天介绍的这些工具中,在经过尝试之后能确实对你有所帮助。

这是假期远程协作专题策划的第一篇,接下来爱范儿的技术团队和产品团队也会进行持续分享。

本文由李晨和方嘉文远程协作撰写

#欢迎关注爱范儿官方微信公众号:爱范儿(微信号:ifanr),更多精彩内容第一时间为您奉上。

爱范儿 |原文链接· 查看评论· 新浪微博


[译]你不知道的NodeJS

$
0
0

你不知道的NodeJS

更新:这篇文章现在是我的书《Node.js进阶》的一部分。 在 jscomplete.com/node-beyond…中阅读此内容的更新版本以及有关Node.js的更多信息。

在今年的Forward.js会议(关于JavaScript的会议)上,我分享了题为“你不知道的NodeJS”的演讲。 在那次演讲中,我向观众提出了一系列有关Nodejs运行时的问题,大多数有技术背景的观众无法回答其中大多数问题。

我没有真正去统计这个数据但确实能在会议室里感觉到。演讲后一些有勇气的人走近我并且承认了这个事实。

这就是让我发表演讲的原因。 我认为我们没有以正确的方式教授Node.js! 关于Node.js的大多数学习内容都聚焦于Node包上,而不是它的运行时上。 大多数Node包将模块封装在自身的Node运行时中(例如http或stream)。 当你遇到问题时,这些问题可能是在自身运行时发生,并且如果你不了解Node运行时,就会遇到麻烦。

关于Node.js的大多数学习内容都聚焦于Node包上,而不是它的运行时上。

我为这篇文章精选一些问题和回答。列如下的标题中,可以尝试先闹中回答它们。(如果你在这里发现了错误或者有歧义的回答,请让我知道)

问题 #1: 什么是调用堆栈?它是V8的一部分吗?

调用肯定是V8的一部分。它是V8用于保存函数调用轨迹的一种数据结构。每次我们运行一个函数,V8都会将该函数的引用放入调用堆栈,并对该函数中嵌套的其他函数进行相同的操作。这也包括递归调用的函数。

当嵌套的函数运行结束,V8将一次弹出一个函数并用它的返回值替换它的位置。

为什么这对于Node很重要? 因为每个 Node进程仅获得一个调用堆栈。 如果使该调用堆栈处于繁忙状态,则整个 Node进程都处于繁忙状态。记住这一点。

问题 #2: 什么是事件轮询? 它是V8的一部分吗?

你认为下图中的事件轮询在哪里?

事件轮询由 libuv模块提供,它不是V8的一部分。

事件轮询是处理外部事件并将它们转换回调函数运行的一种机制。这种轮询会循环的从事件队列中选择事件执行,并将它们的回调函数推入调用堆栈中。

如果这是你第一次听到事件循环,则这些定义不会有太大帮助。 事件循环只是更大架构下中的一部分:

你需要理解事件轮询背后更大的架构、V8所扮演的角色、 Node.js的APIs以及知道这些事情是如何推入队列并被V8执行的。

Node.jsAPIs是像 setTimeoutfs.readFile这样的函数。这些并不是 JavaScript的一部分,而由 Node.js提供的函数。

事件循环位于这张照片的中间(实际上是它的一个更复杂的版本),并且像一个组织者。 当V8调用堆栈为空时,事件循环可以决定下一步执行什么。

问题 #3: 当调用堆栈和事件轮询队列全部为空时, Node.js会做什么?

它简单的退出.

当你启动一个 Node.js进程时,Node将自动启动事件轮询。当事件轮询处于空闲状态并且无其他事件去处理时,程序将退出。

To keep a Node process running, you need to place something somewhere in event queues. For example, when you start a timer or an HTTP server you are basically telling the event loop to keep running and checking on these events. 为了保持Node进程运行,你需要向事件队列中放入一些内容。比如,当你可以启动一个定时器或者一个 HTTP服务时,你就相当于告诉事件轮询保持运行,同时去监听一些事件。

问题 #4: 除了V8和Libuv,Node还具有其他哪些外部依赖项?

以下是一个Node进程所有可以使用的独立库:

  • http-parser
  • c-ares
  • OpenSSL
  • zlib 它们所有都独立于Node,它们都有拥有自己独立的源码以及证书。Node仅仅是使用它们。所以你需要记住这些,如果你想知道你的程序运行在什么地方。如果你正在处理数据压缩相关的事情,你可能会在 zlib库底层堆栈遇到遇到麻烦,那么你将面对一个 zilb相关的错误,而不是归责于Node。

问题 #5: Node能否不依赖于V8运行?

这可能是一个棘手的问题。 你确实需要一个VM来运行Node进程,但是V8并不是唯一可以使用的VM。 您可以使用 Chakra

问题 #6: module.exportsexports有什么不同 ?

你可以一直使用 module.exports去导出模块的API。除一种情况外,你也可以使用 export:

module.exports.g = ...  // Ok

exports.g = ...         // Ok

module.exports = ...    // Ok

exports = ...           // Not Ok
复制代码

为什么?

export仅仅是 module.export的一个别名或引用。 更改导出时,你将更改该引用,而不再更改官方API(即module.exports)。 你只需要在模块作用域中获取局部变量即可。

问题 #7: 为什么顶级变量不是全局变量?

如果你在模块 module1中定义了一个顶级变量 g:

// module1.js

var g = 42;
复制代码

同时你有一个模块 module2引用了模块 module1,并且尝试访问变量 g,你将得到 g is not defined.的错误。

为什么?

如果你在浏览器端做相同的事情,你可以在该定义该顶级变量脚本之后的所有脚本里访问该顶级变量。 每个Node文件在后台都有其自己的IIFE(函数调用表达式)。 在Node文件中声明的所有变量都作用于该IIFE。

相关问题: 下面这个仅仅含有一行代码的Node文件将会输出什么?

// script.js

console.log(arguments);
复制代码

你将会看一些参数!

为什么?

因为Node执行的是一个函数。Node将你的代码包装到一个函数中。该函数中明确定义了上图中你所见的5个参数。

问题 #8: 这些对象: exportrequiremodule都是全局可用的,然而在它们在每个文件又有所不同,为什么?

当你需要使用 require对象,你就是像一个全局变量那样直接使用它。然而,如果你在两个不同的文件中检查 require,你将看到两个不同的对象。为什么? 因为 由于具有相同的IIFE魔法:

如你所见, “这IIFE魔法”向你的代码中传递以下五个参数: exports, require, module, __filename, 和 __dirname。当你在Node中使用这5个参数时,它们看起来像全局变量,但实际上它们仅仅是函数参数。

问题 #9: 什么是Node中的循环依赖?

如果你定义一个模块 module1引用了模块 module2,同时模块 module2内部又引用了模块 module1。将发生什么?报错?

// module1
require('./module2');

// module2
require('./module1');
复制代码

你不会得到报错。因为Node允许那种情况。 所以模块 module1引用模块 module2,但是因为模块 module2依赖模块 module1且模块 module1没有加载完成,模块 module1将仅仅获取到模块 module2的一个部分版本。程序将提示警告。

问题 #10: 什么时候适合使用文件系统的同步方法(如 readFileSync)?

Node模块 fs中的每一个方法都有一个同步版本。为什么你会使用一个同步方法代替一个异步方法?

有时候,使用同步方法会更好。比如服务器仍在加载时,可以在任何初始化步骤中使用它。 通常情况下,初始化步骤之后执行的所有操作都取决于在那里获取的数据。 只要你使用同步方法是一次性的,就可以使用同步方法来避免引入回调狱。

但是,如果您在处理程序(例如HTTP服务器请求回调)中使用同步方法,那简直就是100%错误。 不要那样做。

我希望你能够回答以上部分或者全部的问题。

感谢阅读。

关于数据库连接池大小 · brettwooldridge/HikariCP Wiki · GitHub

$
0
0

关于连接池大小

Brett Wooldridge编辑了此页面 on 8 Jan 2017 ·  29个修订

开发人员经常会错误地配置连接池。在配置池时,需要理解一些原则,对于某些原则可能是违反直觉的。

10,000个同时前端用户

想象一下,您有一个网站,尽管它可能不是Facebook规模,但仍然经常有10,000个用户同时发出数据库请求-每秒约有20,000个事务。您的连接池应该有多大?您可能会惊讶,问题不是 多大,而是 多小!

观看Oracle真实世界性能小组的这段简短视频,以进行令人大开眼界的演示(约10分钟):

{Spoiler Alert},如果您没有观看视频。哦,加油!观看,然后回到这里。

您可以从视频中看到,在不进行任何其他更改的情况下,仅减小连接池大小就可以将应用程序的响应时间从约100ms减少到约2ms-改进了50倍以上。

但为什么?

我们似乎最近在计算的其他部分已经了解到,少即是多。为什么只有4个线程的 Nginx Web服务器可以大大 胜过具有100个进程的 Apache Web服务器?如果回想一下计算机科学101,这不是很明显吗?

即使是只有一个CPU内核的计算机也可以“同时”支持数十个或数百个线程。但是我们所有人[应该]都知道,这只是 时间片的神奇之处,而不仅仅是操作系统的一个把戏。实际上,那个单核一次只能执行 一个线程。然后OS切换上下文,该内核执行另一个线程的代码,依此类推。计算的基本定律是,给定单个CPU资源,按时间顺序执行A和B  总是比“同时” 执行A和B更快。一旦线程数超过CPU内核数,添加更多线程就会变慢,而不是更快。

几乎是真的...

有限的资源

它不像上面说的那么简单,但是很接近。还有其他一些因素在起作用。当我们查看数据库的主要瓶颈时,它们可以概括为三个基本类别: CPUDiskNetwork。我们可以在其中添加 内存,但是与 磁盘网络相比,带宽存在几个数量级的差异。

如果我们忽略“  磁盘网络”,那将很简单。在具有8个计算核心的服务器上,将连接数设置为8将提供最佳性能,并且由于上下文切换的开销,超出此限制的任何内容都将开始变慢。但是我们不能忽略 磁盘网络。数据库通常将数据存储在 磁盘上传统上由旋转的金属板组成,金属板带有安装在步进电机驱动臂上的读/写头。读/写头一次只能位于一个位置(一次查询的读/写数据),并且必须“搜寻”到新的位置才能为另一次查询读/写数据。因此,存在寻道时间成本以及旋转成本,由此磁盘必须等待数据在读/写盘上“再次出现”。缓存在这里当然有帮助,但是原理仍然适用。

在这段时间内(“ I / O等待”),连接/查询/线程仅被“阻塞”以等待磁盘。正是在这段时间内,OS可以通过为另一个线程执行更多代码来更好地利用CPU资源。因此,由于线程在I / O上被阻塞,因此通过使连接/线程的数量大于物理计算内核的数量,我们实际上可以完成更多的工作。

还有多少?我们将会看到。还有多少个问题也取决于 磁盘子系统,因为较新的SSD驱动器没有“寻找时间”的成本或需要处理的旋转因素。不要被欺骗,“ SSD  更快,因此我可以拥有 更多线程”。那就是向后180度。更快,没有寻道,没有旋转延迟意味着 更少的阻塞,因此 更少的线程(更接近内核数)将比更多的线程有更好的性能。  只有在阻塞为执行创造机会时,更多的线程才能更好地执行。

网络类似于 磁盘。当发送/接收缓冲区填满并停顿时,通过以太网接口通过有线方式写数据也会引入阻塞。10千兆位接口的停滞速度将小于千兆以太网,而千兆以太网将停滞于100兆位以下。但是就资源阻塞而言,网络排名第三。有些人经常从计算中忽略它。

这是另一个打破文本墙的图表。

您可以在上述PostgreSQL基准测试中看到,TPS速率在大约50个连接处开始趋于平稳。在上面的Oracle视频中,他们显示连接从2048个下降到96个。我们可以说,即使96个也可能太高了,除非您使用的是16或32核处理器。

公式

PostgreSQL项目提供了以下公式作为起点,但是我们认为该公式将在很大程度上适用于数据库。您应该测试您的应用程序,即模拟预期的负载,并 此起点 附近尝试不同的池设置:

连接数=((core_count * 2)+ Effective_spindle_count)
A formula which has held up pretty well across a lot of benchmarks for years is
that for optimal throughput the number of active connections should be somewhere
near ((core_count * 2) + effective_spindle_count). Core count should not include
HT threads, even if hyperthreading is enabled. Effective spindle count is zero if
the active data set is fully cached, and approaches the actual number of spindles
as the cache hit rate falls. ... There hasn't been any analysis so far regarding
how well the formula works with SSDs.

猜猜那是什么意思?您的带有一个硬盘的4核i7小型服务器应该正在运行以下连接池:  9 = ((4 * 2) + 1)。将其 10称为一个不错的整数。看起来低吗?试试看,我们打赌您可以轻松地处理3000个前端用户,在这种设置下以6000 TPS的速度运行简单查询。如果运行负载测试,则可能会发现,随着您将连接池推到更远的位置 10(在给定的硬件上),TPS速率开始下降,前端响应时间开始攀升。

公理:您需要一个小的池,池中充满等待连接的线程。

如果您有10,000个前端用户,那么拥有10,000个连接池将使您精神错乱。1000仍然很恐怖。甚至有100个连接,过度杀伤力。您需要一个最多只有几十个连接的小型池,并且希望池中等待连接的其余应用程序线程被阻塞。如果对池进行了适当的调整,则将其设置为数据库能够同时处理的查询数量的极限,这几乎不超过如上所述的(CPU内核* 2)。

我们永远不会停止对我们遇到的内部Web应用程序的惊奇,几十个前端用户执行定期活动,并且连接池包含100个连接。不要过度配置数据库。


“锁池”

对于获得许多联系的单个演员,“池锁定”的前景有所提高。这在很大程度上是应用程序级的问题。是的,增加池的大小可以减轻这些情况下的锁定,但是我们敦促您在扩大池之前先检查一下在应用程序级别可以执行的操作。

为了避免死锁,计算池大小是一个相当简单的资源分配公式:

    池大小= T n x(C m -1)+ 1

其中 T n是最大线程数,而 C m是单个线程 同时保持的最大 连接数。

例如,设想三个线程( T n = 3),每个线程都需要四个连接来执行某些任务( C m = 4)。确保永远不会发生死锁所需的池大小为:

    池大小= 3 x(4-1)+ 1 = 10

另一个示例,您最多有八个线程( T n = 8),每个线程需要三个连接才能执行某些任务( C m = 3)。确保永远不会发生死锁所需的池大小为:

    池大小= 8 x(3-1)+ 1 = 17

👉这不一定是 最佳的池大小,但 最低要求,以避免死锁。

some在某些环境中,使用JTA(Java事务管理器)可以通过将相同的Connection从返回 getConnection()到当前事务中已经拥有Connection的线程中来,大大减少所需的连接数。


注意事项

池大小调整最终非常特定于部署。

例如,混合了长时间运行的事务和非常短的事务的系统通常是最难于使用任何连接池进行调整的系统。在那种情况下,创建两个池实例可以很好地工作(例如,一个用于长时间运行的作业,另一个用于“实时”查询)。

在主要运行长时间事务的系统中,通常对所需连接数存在“外部”限制-例如作业执行队列仅允许一定数量的作业一次运行。在这些情况下,作业队列大小应为“正确大小”以匹配池(而不是相反)。

你做对了么?国家卫健委发布新型肺炎预防指南

$
0
0

钛媒体快讯 | 1月29日消息,国家卫健委就新型冠状病毒感染的肺炎疫情预防公众指导建议有关情况召开发布会。会议公布了家庭、公共场所、公共交通等多个版本的新型冠状病毒感染肺炎的预防指南。

公众预防指南之一:

新型冠状病毒感染的肺炎通用预防指南

新型冠状病毒感染的肺炎是一种新发疾病,根据目前对该疾病的认识制定本指南, 适用于疾病流行期间公众个人预防指导

一、 尽量减少外出活动

(一)避免去疾病正在流行的地区。

(二)建议春节期间减少走亲访友和聚餐,尽量在家休息。

(三)减少到人员密集的公共场所活动,尤其是空气流动性差的地方,例如公共浴池、温泉、影院、网吧、KTV、商场、车站、机场、码头、展览馆等。

二、个人防护和手卫生

(一)外出佩戴口罩。外出前往公共场所、就医和乘坐公共交通工具时,佩戴医用外科口罩或N95口罩。

(二)随时保持手卫生。减少接触公共场所的公共物品和部位;从公共场所返回、咳嗽手捂之后、饭前便后,用洗手液或香皂流水洗手,或者使用含酒精成分的免洗洗手液;不确定手是否清洁时,避免用手接触口鼻眼;打喷嚏或咳嗽时,用手肘衣服遮住口鼻。

三、健康监测与就医

(一)主动做好个人与家庭成员的健康监测,自觉发热时要主动测量体温。家中有小孩的,要早晚摸小孩的额头,如有发热要为其测量体温。

(二)若出现可疑症状,应主动戴上口罩及时就近就医。若出现新型冠状病毒感染可疑症状(包括发热、咳嗽、咽痛、胸闷、呼吸困难、轻度纳差、乏力、精神稍差、恶心呕吐、腹泻、头痛、心慌、结膜炎、轻度四肢或腰背部肌肉酸痛等),应根据病情,及时到医疗机构就诊。并尽量避免乘坐地铁、公共汽车等交通工具,避免前往人群密集的场所。就诊时应主动告诉医生自己的相关疾病流行地区的旅行居住史,以及发病后接触过什么人,配合医生开展相关调查。

四、保持良好卫生和健康习惯。

(一)居室勤开窗,经常通风。

(二)家庭成员不共用毛巾,保持家居、餐具清洁,勤晒衣被。

(三)不随地吐痰,口鼻分泌物用纸巾包好,弃置于有盖垃圾箱内。

(四)注意营养,适度运动。

(五)不要接触、购买和食用野生动物(即野味);尽量避免前往售卖活体动物(禽类、海产品、野生动物等)的市场。(六)家庭备置体温计、医用外科口罩或N95口罩、家用消毒用品等物资。

公众预防指南之二:

有疾病流行地区居住旅行史人员

新型冠状病毒感染的肺炎预防指南

新型冠状病毒感染的肺炎是一种新发疾病,根据目前对该疾病的认识制定本指南,适用于在两周内有武汉等疾病流行地区居住、旅行史的人员。

(一)尽快到所在村支部或社区进行登记,减少外出活动,尤其是避免到人员密集的公共场所活动。

(二)从离开疾病流行地区的时间开始,连续14天进行自我健康状况监测,每天两次。条件允许时,尽量单独居住或居住在通风良好的单人房间,并尽量减少与家人的密切接触。

(三)若出现可疑症状(包括发热、咳嗽、咽痛、胸闷、呼吸困难、轻度纳差、乏力、精神稍差、恶心呕吐、腹泻、头痛、心慌、结膜炎、轻度四肢或腰背部肌肉酸痛等),应根据病情及时就诊。 就医途中具体指导建议如下:

1.前往医院的路上,病人应该佩戴医用外科口罩或N95口罩。

2.如果可以,应避免乘坐公共交通工具前往医院,路上打开车窗。

3.时刻佩戴口罩和随时保持手卫生。在路上和医院时,尽可能远离其他人(至少1米)。

4.若路途中污染了交通工具,建议使用含氯消毒剂或过氧乙酸消毒剂,对所有被呼吸道分泌物或体液污染的表面进行消毒。

公众预防指南之三:

家庭新型冠状病毒感染的肺炎预防指南

新型冠状病毒感染的肺炎是一种新发疾病,根据目前对该疾病的认识制定本指南, 适用于家庭场所

一、日常预防

(一)避免去疾病正在流行的地区。

(二)减少到人员密集的公共场所活动,尤其是空气流动性差的地方,例如公共浴池、温泉、影院、网吧、KTV、商场、车站、机场、码头、展览馆等。

(三)不要接触、购买和食用野生动物(即野味),避免前往售卖活体动物(禽类、海产品、野生动物等)的市场,禽肉蛋要充分煮熟后食用。

(四)居室保持清洁,勤开窗,经常通风。

(五)随时保持手卫生。减少接触公共场所的公共物品和部位;从公共场所返回、咳嗽用手捂之后、饭前便后,用洗手液或香皂流水洗手,或者使用含酒精成分的免洗洗手液;不确定手是否清洁时,避免用手接触口鼻眼;打喷嚏或咳嗽时用手肘衣服遮住口鼻。

(六)外出佩戴口罩。外出前往公共场所、就医和乘坐公共交通工具时,应佩戴医用外科口罩或N95口罩。

(七)保持良好卫生和健康习惯。家庭成员不共用毛巾,保持家居、餐具清洁,勤晒衣被。不随地吐痰,口鼻分泌物用纸巾包好,弃置于有盖垃圾箱内。注意营养,勤运动。

(八)主动做好个人及家庭成员的健康监测。自觉发热时要主动测量体温。家中有小孩的,要早晚摸小孩的额头,如有发热要为其测量体温。

(九)准备常用物资。家庭备置体温计、一次性口罩、家庭用的消毒用品等物资。

二、家庭成员出现可疑症状时的建议

(一)若出现新型冠状病毒感染的肺炎可疑症状(如发热、咳嗽、咽痛、胸闷、呼吸困难、轻度纳差、乏力、精神稍差、恶心呕吐、腹泻、头痛、心慌、结膜炎、轻度四肢或腰背部肌肉酸痛等症状),应根据病情及时就医。

(二)避免乘坐地铁、公共汽车等公共交通工具,避免前往人群密集的场所。

(三)就诊时应主动告诉医生自己的相关疾病流行地区的旅行居住史,以及发病后接触过什么人,配合医生开展相关调查。

(四)患者的家庭成员应佩戴口罩,与无症状的其他家庭成员保持距离,避免近距离接触。

(五)若家庭中有人被诊断为新型冠状病毒感染的肺炎,其他家庭成员如果经判定为密切接触者,应接受14天医学观察。

(六)对有症状的家庭成员经常接触的地方和物品进行消毒。

公众预防指南之四:

公共场所新型冠状病毒感染的肺炎预防指南

新型冠状病毒感染的肺炎是一种新发疾病,根据目前对该疾病的认识制定本指南,适用于商场、餐馆、影院、KTV、网吧、公共浴池、体育馆、展览馆、火车站、地铁站、飞机场、公交汽车站等公共场所。

一、公共场所工作人员要自行健康监测,若出现新型冠状病毒感染的可疑症状(如发热、咳嗽、咽痛、胸闷、呼吸困难、轻度纳差、乏力、精神稍差、恶心呕吐、腹泻、头痛、心慌、结膜炎、轻度四肢或腰背部肌肉酸痛等),不要带病上班。

二、若发现新型冠状病毒感染的可疑症状者,工作人员应要求其离开。

三、公用物品及公共接触物品或部位要定期清洗和消毒。

四、 保持公共场所内空气流通。保证空调系统或排气扇运转正常,定期清洗空调滤网,加强开窗通风换气。

五、洗手间要配备足够的洗手液,保证水龙头等供水设施正常工作。

六、保持环境卫生清洁,及时清理垃圾。

七、疾病流行地区,公众应尽量减少前往公共场所,尤其避免前往人流密集和空气流通较差的地方。

公众预防指南之五:

公共交通工具新型冠状病毒感染的肺炎预防指南

新型冠状病毒感染的肺炎是一种新发疾病,根据目前对该疾病的认识制定本指南,适用于飞机、火车、地铁、公共汽车和轮船等公共交通工具。

一、发生疾病流行地区的公共交通工具在岗工作人员应佩戴医用外科口罩或N95口罩,并每日做好健康监测。

二、公共交通工具建议备置体温计、口罩等物品。

三、增加公共交通工具清洁与消毒频次,做好清洁消毒工作记录和标识。

四、保持公共交通工具良好的通风状态。

五、保持车站、车厢内的卫生整洁,及时清理垃圾。

六、做好人员工作与轮休安排,确保司乘人员得到足够休息。

公众预防指南之六:

病例密切接触者的居家医学观察指南

新型冠状病毒感染的肺炎确诊病例的密切接触者应从和病人接触的最后一天起采取医学观察14天。在家中观察期间需与医学观察人员保持联系,并需要了解病情观察和护理要点,掌握家庭预防的洗手、通风、防护和消毒措施。

在居家医学观察期间的具体建议如下:

(一)将密切接触者安置在通风良好的单人房间,拒绝一切探访。

(二)限制密切接触者活动,最小化密切接触者和家庭成员活动共享区域。确保共享区域(厨房、浴室等)通风良好(保持窗户开启)。

(三)家庭成员应住在不同房间,如条件不允许,和密切接触者至少保持1米距离。哺乳期母亲可继续母乳喂养婴儿。

(四)其他家庭成员进入密切接触者居住空间时应佩戴口罩,口罩需紧贴面部,在居住空间中不要触碰和调整口罩。口罩因分泌物变湿、变脏,必须立即更换。摘下并丢弃口罩之后,进行双手清洗。

(五)与密切接触者有任何直接接触,或离开密接接触者居住空间后,需清洁双手。准备食物、饭前便后也均应清洁双手。如果双手不是很脏,可用酒精免洗液清洁。如双手比较脏,则使用肥皂和清水清洗。(注意酒精使用安全,如意外吞食用或引发火灾)。

(六)使用肥皂和清水洗手时,最好使用一次性擦手纸。如果没有,用洁净的毛巾擦拭,毛巾变湿时需要更换。

(七)偶然咳嗽或打喷嚏时用来捂住口鼻的材料可直接丢弃,或者使用之后正确清洗(如用普通的肥皂/洗涤剂和清水清洗手帕)。

(八)家属应尽量减少与密切接触者及其用品接触。如避免共用牙刷、香烟、餐具、饭菜、饮料、毛巾、浴巾、床单等。餐具使用后应使用洗涤剂和清水清洗。

(九)推荐使用含氯消毒剂和过氧乙酸消毒剂,每天频繁清洁、消毒家庭成员经常触碰的物品,如床头柜、床架及其他卧室家具。至少每天清洁、消毒浴室和厕所表面一次。

(十)使用普通洗衣皂和清水清洗密切接触者衣物、床单、浴巾、毛巾等,或者用洗衣机以60-90摄氏度和普通家用洗衣液清洗,然后完全干燥上述物品。将密切接触者使用的床品放入洗衣袋。不要甩动衣物,避免直接接触皮肤和自己的衣服。

(十一)戴好一次性手套和保护性衣物(如塑料围裙)再去清洁和触碰被密切接触者的人体分泌物污染的物体表面、衣物或床品。戴手套前、脱手套后要进行双手清洁及消毒。

(十二)若确诊病例的密切接触者出现可疑症状,包括发热、咳嗽、咽痛、胸闷、呼吸困难、轻度纳差、乏力、精神稍差、恶心呕吐、腹泻、头痛、心慌、结膜炎、轻度四肢或腰背部肌肉酸痛等,应立即就医。 具体指导建议如下:

1.前往医院的路上,病人应该佩戴医用外科口罩或N95口罩。

2.如果可以,避免乘坐公共交通工具前往医院,路上打开车窗。

3.时刻佩戴口罩和随时保持手卫生。在路上和医院时,尽可能远离其他人(至少1米)。

4.若路途中污染了交通工具,建议使用含氯消毒剂或过氧乙酸消毒剂,对所有被呼吸道分泌物或体液污染的表面进行消毒。

(来源:国家卫建委)

更多精彩内容,关注钛媒体微信号(ID:taimeiti),或者下载钛媒体App

SpringCloud基础教程(五)-配置中心热生效和高可用

$
0
0

 我的博客: 兰陵笑笑生,欢迎浏览博客!

 上一章 SpringCloud基础教程(四)-配置中心入门当中,我们在对Eureka的有了基本的基础认识之上,深入的了解Eureka高可用集群和其他的生产环境中用到的一些配置。本章将开始了解分布式环境下的配置中心。

前言

 在实际的项目运行中,我们会根据实际需求修改配置内容,那么有没有一种方式,能够在不启动服务组件的情况向让配置文件动态的生效呢,Spring Cloud Conifg中提供了一种方式了。当然我们还需要考虑一旦配置服务宕机的话,那么配置客户端是无法获取到配置信息的,所以针对配置服务,我们也希望能提供高可用的服务。

一、配置热生效

 首先我们在客户端(每个开发的微服务)的控制器上添加 @RefreshScope注解,并在客户端(项目的pom.xml中引入actuator依赖(actuator中包含了/actuator/refresh的api):

@RefreshScope
@RestController
public class ValueController {

    @Value("${k1}")
     String value;

    @GetMapping("/get")
    public String getValue(){
        return value;
    }
}复制代码

亲自测试,如果将@RefreshScope注解添加在启动类上,value是不管用的,只有添加在需要热加载的Bean上才生效。


            org.springframework.boot
            spring-boot-starter-actuator复制代码

 修改bootstrap.xml 添加如下配置,表示可以通过手动的刷新改变获取配置信息

management:
  endpoints:
    enabled-by-default: true
    web:
      exposure:
        include: refresh复制代码

 在配置完成之后,我们通过以下的步骤测试热生效:

步骤一:启动Config Server和Config Client项目访问。

步骤二:访问http://localhost:9003/get 返回的结果是 “master-test-v1 ”;

步骤三:修改配置文件ConfigServer-test.properties 内容修改成”k1=master-test-v2“;提交到git仓库

步骤四:访问http://localhost:9003/get 返回的结果依旧是 “master-test-v1 ”;

步骤五:通过POST访问http://localhost:g003/actuator/refresh 访问得到响应【“k1”】, 注意这里的版本是2.x系列的springCloud,和1.X系列的接口访问不一样):

file

第六步:再次去访问http://localhost:9003/get 返回的结果变成了是 “master-test-v2 ”;

 通过以上的配置,我们成功的测试了手动的请求可以刷新并动态的加载最新的配置,当然这样还是存在一个缺点,就是需要手动刷新。当然SpringCloud中介绍了使用Bus来通知Spring Cloud Config,后期的文章还需继续生如介绍。

二、高可用

 在上文中,我们的配置的客户端都是通过制定配置Config Server的实例地址方式获取配置信息的,一旦Config Server遇到故障,获取配置信息就会出现故障,SpringCloud同样支持通过高可用的方式解决这样的问题。接下来我们结合Eureka组件,来搭建高可用的配置中心。

2.1 Config Server配置

 在Config Server的项目中添加Eureka的依赖:


            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client复制代码

 并在ConfigServerApplicaition.Java启动类添加注解@EnableEurekaClient 开启Eureka注册:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableConfigServer
@EnableEurekaClient
public class ConfigServerApplicaition {
    
    public static void main(String[] args) {
        SpringApplication.run(ConfigServerApplicaition.class, args);
    }
}
复制代码

 修改Config Server的applicaiton.xml配置:修改端口号和实例Id,启动2个Config Server实例:

server:
  port: 6001
#服务提供方
spring:
  application:
  #服务的名称
    name: server-config-cluster
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/lnxxs/springCloudConfig.git
          password:
          username:
          #git非植
      label: master
      uri: http://localhost:6001/
      enabled: true
      #http安全
  security:
    user:
      name: user
      password: pwd
      #注册的实实例
eureka:
  instance:
    instance-id: config-server1
  client:
    service-url:
    #连接eureka的url
      defaultZone:  http://eureka7001.com:7001/eureka/复制代码

 在Eureka的监控中心,我们看到了名称为server-config-cluster的实例有2个,分别是config-server1和

config-server2:

file

2.2、客户端配置

 客户端需要从Eureka获取Config Server的地址,首先在客户端的项目pom.xml中添加Eureka的依赖:


            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client复制代码

 启动类添加@EnableDiscoveryClient注解,启动开发服务发现的功能,发现Config Server配置服务信息:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableAutoConfiguration
@EnableDiscoveryClient
public class ConfigClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigClientApplication.class, args);
    }
}复制代码

 同时在bootstrap.xml洗澡呢Eureka注册中心的地址配置,同时将spring.cloud.config.uri去掉,打开spring.cloud.config.discover.enabled=ture ,并添加spring.cloud.config.service-id为配置服务的spring.application.name= server-config-cluster,具体配置如下:

spring:
  application:
    name: server-client
  cloud:
    config:
        label: master
        profile: test
        #修改uri为 discovery.service-id
        #uri: http://localhost:6001/
        name: ConfigServer
        fail-fast: true
        retry:
          initial-interval: 1000
          max-attempts: 6
          max-interval: 2000
          multiplier: 1.1
        username: user
        password: pwd
        discovery:
          service-id: server-config-cluster
          enabled: true
management:
  endpoints:
    enabled-by-default: true
    web:
      exposure:
        include: refresh
eureka:
  instance:
    instance-id: config-client
  client:
    service-url:
    #连接eureka的url
      defaultZone:  http://eureka7001.com:7001/eureka/复制代码

 通过以上的配置,就可以实现配置中心的高可用。

三、总结

 本章是对配置中心做了深入的介绍,包括配置的热加载和配置服务的高可用,这些都是微服务架构所需要的。Spring Cloud Config功能特别的丰富,不止于此,同时还支持更换git仓库为SVN或者其他的仓库等,有感兴趣的同学可以自行研究。

file

.以就是本期的分享,你还可以关注公众号: 程序员笑笑生,关注更多精彩内容!

file

file

SpringCloud基础教程(一)-微服务与SpringCloud

SpringCloud基础教程(二)-服务发现 Eureka

SpringCloud基础教程(三)-Eureka进阶

SpringCloud 基础教程(四)-配置中心入门

SpringCloud基础教程(五)-配置中心热生效和高可用

SpringCloud 基础教程(六)-负载均衡Ribbon

更多精彩内容,请期待...

本文由博客一文多发平台 OpenWrite发布!


半数员工都在家上班的少数派,推荐这些远程协作好工具

$
0
0

Matrix 精选

Matrix 是少数派的写作社区,我们主张分享真实的产品体验,有实用价值的经验与思考。我们会不定期挑选 Matrix 最优质的文章,展示来自用户的最真实的体验和观点。

文章代表作者个人观点,少数派仅对标题和排版略作修改。


远程办公本来是基于网络和工具的不断进化,带来的一种更先进的团队协作模式,这种模式可以突破地域限制,帮助组织面向全球招揽合适的人才,同时也有助于降低办公成本。万万没想到在今天这个特殊时期被大家反复提起,在目前的情况下,启动远程办公,减少聚集接触,确实有助于疾病传播的控制。但远程办公并不是适合所有人,工具选择也各有偏重,我们基于过往的经验,盘点一些协作工具的相关内容,希望能给大家一些指引和帮助。

为了方便大家快速定位,我们将远程工作中的几个关键需求分拆为即时沟通、文档协作、任务看板、文件传输、会议讨论五个部分,先从上手即用的单一工具开始盘点,大家可以根据自己的需求进行定位选择。最后再针对需要系统配置的企业微信、钉钉、飞书三个综合性团队协作工具进行简单对比解析。

注:有部分比较好用的国外工具,因为监管原因,在国内不能使用,这里就不再赘述了。

单一功能的工具

即时沟通

即时沟通主要是 IM 文字类的通讯工具,用于必要的线上沟通。当然,最简单直接的就是微信拉群,但因为微信会与私人信息混淆,且文件容易失效,所以只适合临时应急,不推荐团队长期使用。

Slack

放眼全球,目前最优秀的团队即时沟通工具依然当属 Salck,也是全球最热门、增长最快的 SaaS 创业公司之一。它的特点是优秀的分组(频道)管理功能,就好像把线上沟通分成了不同的小会议室,可以选择开放或私密,灵活管理。在聊天功能和界面上也有大量的创新,表情回复可以避免无效刷屏,嵌套回复也是将信息归类,可以让其他人更完整地看到某一个话题的讨论。

Slack 最强大的还是开放的机器人系统,可以通过其他工具的开放接口,自己定制不同的 bot,自动在对话框里推送任务、数据等通知,配合自定义的消息推送机制,实现高效的团队沟通。我派付费使用 Slack 接近一年的时间,功能体验是非常优秀的。问题主要在于中文化和网络互通稳定性不够好,如果团队没有这方面的障碍,可以好好研究利用,Power+ 小组也专门出了一些使用指南和对比体验,供大家参考。

深度阅读推荐:

QQ / TIM

为啥推荐 QQ 呢?作为最早从 PC 客户端演进过来的通讯工具,QQ 保留了大量适合协作的功能,比如大文件传输、稳定的多人通话、屏幕共享演示、聊天记录漫游等,这些都是团队沟通中常用的功能,QQ 不仅在功能上比微信全面、完善,每个功能点的完成度也很高。可能有不少人都已经放弃 QQ,但其实它的功能一直在优化,对于以沟通需求为主的团队来说,可以重新去下载试试。

当然,基于 QQ 为企业打造的 TIM 也是不错选择,有在线文档、日程管理的支持,但不知道维护情况怎样,毕竟算是比较边缘的项目,投入资源应该相对有限。

我们在远程协作主题播客里曾谈及 QQ 的优势,具体请见:

文档协作

文档协作主要是针对规划解析、会议纪要、合同条款等内容的在线预览和讨论需求,由于内容量比较大,不适合在即时聊天工具里面沟通,需要单独的协作工具来解决。传统模式就是 Word 里面的批注功能,需要保存不同版本的文件,传来传去。

Notion

近两年异军突起的文档协作工具,Notion 跟 Slack 有些类似,都是以某一个功能为中心,进行其他功能的聚合。除了基本的文档笔记功能,官方提供的使用模板就更多了:团队 Wiki、待办事项、笔记和草稿、设计规范、阅读清单、每周议程、团队日历……甚至是路线图,你都可以在 Notion 里完成。正如官方给它的定义:一款「将笔记、知识库和任务管理无缝整合的协作平台」。

正是因为 Notion 模版的开放性,以至于我派的开发团队,完全使用它进行产品开发的项目管理了。当然,作为功能丰富的海外工具,Notion 的使用门槛也偏高,除了中文本地化,也需要对 Wiki 知识库、任务管理等工具有一定的了解,才能更好的驾驭它。

深度阅读推荐:

石墨文档

石墨作为老牌的在线文档工具,也是我派一直在用的产品,主要是设计比较简洁,功能又基本够用。除了编辑器的稳定性,石墨文档主要是权限设置功能比较丰富,可以向陌生人分享阅读和编辑权限,也可以设置密码访问,适合不同的需求和场景。同时支持 Word、PDF 格式的导出,可以在在聊天和邮件中便捷发送。

文档工具其实并不好做,光多人同时编辑的稳定性就非常考验技术支持能力,而一旦出现 Bug,数据丢失,后果也非常严重。石墨在各个浏览器打开,协作编辑能力都比较稳定,值得信赖。

WPS Office

WPS 目前应该是国内兼容微软 Office 做得最好的产品,近两年界面设计也更加简洁,还推出了团队协作功能。云文档默认为每位用户提供了免费的 1GB 云存储空间,不仅支持实时同步、查看编辑记录,还支持全文检索、历史版本等功能,可以在所有支持标准 HTML5 浏览器上登录 WPS 账号后,在任何场景下实现全平台创作。

对于标准文档比较多的团队,比如合同、表格等文件,可以尝试用 WPS 来协作试试,相对 Office 协作来说,免费使用门槛比较低。

深度阅读推荐:

iWork 套件

如果团队成员都是 Mac 电脑,其实 iWork 套件也提供了不错的在线协作功能。而 iWork 其实是首要保证文档的一致性,在不同的设备上都能保持一致,而且不需要使用者自己去调整,所以限制了一部分编辑功能(例如自动生成目录),也放弃了离线功能。再辅以之前提到的在「批准与修改」中的「接受或拒绝」修改的功能,尽可能避免了文档冲突这种烦心的情况出现。

深度阅读推荐:

任务看板

看板是团队协作中,对个人任务的另一种呈现形式,可以按照不同的项目或者流程来定制看板,查看进度,是在即时沟通和文档协作之后的进阶能力。

Trello

应该算是看板工具的鼻祖,以看板为主的项目和任务管理工具,同样简洁易用,且大部分功能是免费的,我派也在很早就启用 Trello 进行选题管理和稿件排期。它的特点是可以根据行业的需求进行工作流的灵活定制,包括市场营销人员、项目规划师、软件开发者、高校老师、人力资源经理、婚礼策划师、全职父母,还有旅游规划师等等,都在根据自身需求来个性化地使用 Trello 这一可视化工具。

深度阅读推荐:

Teambition

Teambition 是本土工具中,接近 Trello 功能又比较多的工具产品,发展了很多年之后,已经被阿里收购。本土工具的优点就是中文化和响应速度都很好,我派主要用来做目标管理和商务流程管理。它的特点是创建项目时提供了 40 多种模板,涵盖了产品研发、市场营销、行政人事等多个方面,每个模板都有已经划分好的任务流程,只要找到合适的项目就能快速创建项目并设置好任务列表。

深度阅读推荐:

文件传输

主要针对视频、批量图片、程序等大体积文件的存储和传输,也是团队协作常见的需求。

坚果云

坚果云是国内较早的文件同步服务,定位更偏向于办公和团队协作。这意味着它需要在连接的速度、稳定性,以及整个系统的设计上做出足够的努力。

坚果云的核心优势在于同步盘功能和文件共享,无论将其用于重要文件夹的存档和云同步,还是和同事们之间分享大文件,坚果云的表现都很亮眼。

深度阅读推荐:

奶牛快传

奶牛快传 是近两年起步的网盘工具,非常适合进行大文件线上分享。它不需注册就能直接上传和下载文件,最关键的是,上传下载速度都非常快,完全不限速,也不限制文件体积大小。它的移动网页版和 iOS 客户端还支持直接预览多媒体文件,免除反复下载的痛苦。如果你经常需要和他人快速共享文件,特别是大文件,奶牛快传一定可以满足你的需求。

会议讨论

团队协作中,总有一些需要讨论的问题,比如关于目标规划的确认,关于项目执行的细节,服务于会议的工具就应运而生了。会议的通话部分大家可以选择 QQ、Skype 、 ZOOM小鱼视频 等专业产品进行解决,小鱼也是国内较早提供音视频会议功能的企业服务,对于普通网络的多人视频会议有更好的优化支持。我们这里主要介绍一些会议过程中的相关工具。

Explain Everything

远程白板工具,我们可以将会议转化成一块共享的白板。然后再将这块白板分享给参与者,他们在 App 内输入分享代码,就可以加入参与编辑。

深度阅读推荐:

会议桌

一个专门根据会议需求定制的本土协作产品,特点是运用 WebRTC 的最新技术,无需下载安装任何插件,在网页中即可享受便捷的语音、视频服务。同时提供共享白板,共享文档展示的功能。

深度阅读推荐:

综合团队协作工具

前面盘点了一些独立工具,接下来把几个大综合团队协作工具解析一下。综合协作工具,就是集成了多个功能,并且将功能打通,形成完整工作流的工具。这类产品的开发和维护要求较高,基本只有头部互联网公司才能持续投入,目前比较成型的也就腾讯、阿里、字节跳动三家。

企业微信

企业微信是基于微信的通信能力,整合了企业内部通讯录和工作台,工作台当中包含日历、会议、文档、微盘等基本功能,在第三方应用平台,还可以找到考勤、财务、CRM、云 OA 等大量应用,还有腾讯自己开发的 TAPD 看板应用。

企业微信整体来说,更像一个中间平台,没有针对核心功能做深度的调整,比如即时聊天部分,只比微信增加了已读状态,日程和文档也相对独立,没有进行深度的打通。因此企业微信比较适合对工具依赖不强的企业,可以接入自己的 OA 系统到企业微信工作台,主要用它的聊天和通知功能。目前企业微信开启了微信用户的互通能力,就是企业微信成员,可以跟微信用户拉群沟通,比较适合大量外联沟通需求或需要社群管理的组织来使用。

钉钉

钉钉是阿里投入巨大的企业服务产品,据说是源于当年【来往】产品的失败,原班人马针对中小企业需求打造了钉钉。钉钉的定位则更偏向于管理,围绕即时聊天,把日程和任务放在首位,要求员工做好自己的工作安排。比较适合传统的管理中心制企业。

应用中心也提供了基本的网盘、邮件功能,网盘中的文档能力由 WPS 提供支持,视频和电话会议功能比较完善,也是传统企业的常用需求。在应用商店里,提供更多来自第三方的行业解决方案,比如门店账务、企业采购、固定资产、报销管理等等,比企业微信更细分,也比较符合阿里服务的企业对象。如果是工作流程比较完善和固定,注重管理和审批的企业,更适合选择钉钉。

飞书

飞书是源于字节跳动内部使用的协作工具,因此产品本身带有字节的文化属性,强调个体创造能力,尽量减少管理层级。飞书的在即时聊天、日常管理的基础上,把协作文档提到了更高的等级,统一称为云空间。

在聊天模块更接近 Slack 的体验,更强调讨论。日历模块也做了较大修改,可以关联成员、文档、地点等更多信息。工作台也已经有不少独立的第三方应用,包括日常审批、工资单等,同时也可以利用 API 自己开发相关的机器人,比如我派就开发了少数派网站库存机器人和客服机器人的对接,会自动向指定群组发送通知。

飞书相对于前两者,更适合互联网、文创类公司,也是我派目前的主要的团队协作工具。目前正提供限时免费到 5 月,可以去  官网注册 试试。

最后强调两句

远程工作需要好工具,但核心不是工具,还要看行业属性、公司结构、个体能力等等。我派属于互联网领域的文创类组织,团队不大,管理扁平,本身比较适合远程模式,但也不算是做得特别好,还在不断学习和完善当中。但非常时期,以上工具依然可以作为应急的工作解决方案,最后,团队能不能形成远程协作的模式,还要具体分析,切勿盲目推崇,折腾团队。

同时,少数派这里能够推荐的工作类型始终有限,在同领域或其他领域中如果你有好的工具推荐,也欢迎在本文评论区或 Matrix 社区进行分享。


【远程办公】5分钟实现内网穿透

$
0
0

来讲讲为啥要做内网映射

从公网中访问自己的内网设备一直是个麻烦事情,尤其是做微信开发等。设备可能处于路由器后,或者运营商因为IP地址短缺不给你分配公网IP地址。如果我们想直接访问到这些设备,一般非常麻烦。

  • 求网管大佬在路由器上给自己内网加个 端口映射
  • 购买 花生壳等动态域名解析软件
  • 使用 natapp 等免费(也有付费的)的提供的内网映射服务
  • 基于 ngrok/frp自建内网映射服务

为什么放弃 ngrok,使用 frp

我们在2016年提供了一个 ngrok的免费服务,并且分享了搭建的步骤可以参考 《Angrok 一个内网穿透服务》,搭建步骤对于一般的用户非常不友好,后边也就停止了相关的服务转向了 frp

Github 的关注度对比

穿透协议支持

frp 支持 http ssh tcp udp ftp 等协议

开始动手

准备工作

搭建一个完整的frp服务,我们需要

  • 公网IP 的 ECS 一台
  • 域名 (若不需要解析则不需要)

安装 frp (frps)服务端

  • 下载 frp 安装包

github.com/fatedier/fr…

  • 解压压缩包,修改 frps.ini
[common]
bind_port = 7000     # frps 服务启动,占用的端口
vhost_http_port = 80  # frps 服务监听转发的端口
复制代码
  • 启动 frps服务
./frps -c ./frps.ini
复制代码

安装 frp(frpc)客户端

自定义域名访问内网服务

  • 修改 frpc.ini
[common]
server_addr = ECS的公网IP
server_port = 7000

[随意但必须唯一]
type = http
local_port = 本地目标服务的端口
custom_domains = 自定义的域名
复制代码
  • 启动客户端
./frpc -c ./frpc.ini

复制代码
  • 访问 自定义域名即可访问内网的服务

使用ssh访问公司内网机器

  • 修改 frpc.ini
[common]
server_port = 7000

[随意但必须唯一]
type = tcp
local_ip = 127.0.0.1  
local_port = 22
remote_port = 10022
复制代码
  • 启动客户端
./frpc -c ./frpc.ini

复制代码
  • 通过 ssh 访问内网机器
ssh -p 10022 root@x.x.x.x
复制代码

image

使用filebeat收集kubernetes中的应用日志 - 宋净超的博客|Cloud Native|云原生布道师

$
0
0

前言

本文已同步更新到Github仓库 kubernetes-handbook中。

昨天写了篇文章 使用Logstash收集Kubernetes的应用日志,发现logstash十分消耗内存(大约500M),经人提醒改用filebeat(大约消耗10几M内存),因此重写一篇使用filebeat收集kubernetes中的应用日志。

在进行日志收集的过程中,我们首先想到的是使用Logstash,因为它是ELK stack中的重要成员,但是在测试过程中发现,Logstash是基于JDK的,在没有产生日志的情况单纯启动Logstash就大概要消耗 500M内存,在每个Pod中都启动一个日志收集组件的情况下,使用logstash有点浪费系统资源,经人推荐我们选择使用 Filebeat替代,经测试单独启动Filebeat容器大约会消耗 12M内存,比起logstash相当轻量级。

方案选择

Kubernetes官方提供了EFK的日志收集解决方案,但是这种方案并不适合所有的业务场景,它本身就有一些局限性,例如:

  • 所有日志都必须是out前台输出,真实业务场景中无法保证所有日志都在前台输出
  • 只能有一个日志输出文件,而真实业务场景中往往有多个日志输出文件
  • Fluentd并不是常用的日志收集工具,我们更习惯用logstash,现使用filebeat替代
  • 我们已经有自己的ELK集群且有专人维护,没有必要再在kubernetes上做一个日志收集服务

基于以上几个原因,我们决定使用自己的ELK集群。

Kubernetes集群中的日志收集解决方案

编号方案优点缺点
1每个app的镜像中都集成日志收集组件部署方便,kubernetes的yaml文件无须特别配置,可以为每个app自定义日志收集配置强耦合,不方便应用和日志收集组件升级和维护且会导致镜像过大
2单独创建一个日志收集组件跟app的容器一起运行在同一个pod中低耦合,扩展性强,方便维护和升级需要对kubernetes的yaml文件进行单独配置,略显繁琐
3将所有的Pod的日志都挂载到宿主机上,每台主机上单独起一个日志收集Pod完全解耦,性能最高,管理起来最方便需要统一日志收集规则,目录和输出方式

综合以上优缺点,我们选择使用方案二。

该方案在扩展性、个性化、部署和后期维护方面都能做到均衡,因此选择该方案。

logstash日志收集架构图

我们创建了自己的logstash镜像。创建过程和使用方式见 https://github.com/rootsongjc/docker-images

镜像地址: index.tenxcloud.com/jimmy/filebeat:5.4.0

测试

我们部署一个应用对logstash的日志收集功能进行测试。

创建应用yaml文件 fielbeat-test.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: filebeat-test
  namespace: default
spec:
  replicas: 3
  template:
    metadata:
      labels:
        k8s-app: filebeat-test
    spec:
      containers:
      - image: sz-pg-oam-docker-hub-001.tendcloud.com/library/filebeat:5.4.0
        name: filebeat
        volumeMounts:
        - name: app-logs
          mountPath: /log
        - name: filebeat-config
          mountPath: /etc/filebeat/
      - image: sz-pg-oam-docker-hub-001.tendcloud.com/library/analytics-docker-test:Build_8
        name : app
        ports:
        - containerPort: 80
        volumeMounts:
        - name: app-logs
          mountPath: /usr/local/TalkingData/logs
      volumes:
      - name: app-logs
        emptyDir: {}
      - name: filebeat-config
        configMap:
          name: filebeat-config
---
apiVersion: v1
kind: Service
metadata:
  name: filebeat-test
  labels:
    app: filebeat-test
spec:
  ports:
  - port: 80
    protocol: TCP
    name: http
  selector:
    run: filebeat-test
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: filebeat-config
data:
  filebeat.yml: |
    filebeat.prospectors:
    - input_type: log
      paths:
        - "/log/*"
        - "/log/usermange/common/*"
    output.elasticsearch:
      hosts: ["172.23.5.255:9200"]
    username: "elastic"
    password: "changeme"
    index: "filebeat-docker-test"

注意事项

  • 将app的 /usr/local/TalkingData/logs目录挂载到logstash的 /log目录下。
  • Filebeat容器大概需要10M左右内存。
  • 该文件可以在 manifests/test/filebeat-test.yaml找到。
  • 我使用了自己的私有镜像仓库,测试时请换成自己的应用镜像。
  • filebeat镜像制作参考

创建应用

部署Deployment

kubectl create -f filebeat-test.yaml

查看 http://172.23.5.255:9200/_cat/indices将可以看到列表有这样的indices:

green open filebeat-docker-test            7xPEwEbUQRirk8oDX36gAA 5 1   2151     0   1.6mb 841.8kb

访问Kibana的web页面,查看 filebeat-docker-test的索引,可以看到filebeat收集到了app日志。

Kibana页面

「真诚赞赏,手留余香」


为何把日志打印到控制台很慢? - 颇忒脱 - SegmentFault 思否

$
0
0

原文

容器打印日志到控制台阻塞的排障的时候看到一个观点:

把日志打印到控制台要比打印到文件慢,而且是非常慢。

log4j2和logback的两个issue官方也提到了这一点(见 LOG4J2-2239LOGBACK-1422)。

那么为何输出到控制台慢?有何办法加速呢?问题要从三个角度来分别回答:

  1. linux的 stdout角度
  2. Java程序角度
  3. docker容器角度

stdout角度

写到控制台其实就是写到 stdout,更严格的说应该是 fd/1。Linux操作系统将 fd/0fd/1fd/2分别对应 stdinstdoutstdout

那么问题就变成为何写到 stdout慢,有何优化办法?

造成 stdout慢的原因有两个:

  • 你使用的终端会拖累 stdout的输出效率
  • stdout的缓冲机制

在SO的这个问题中: Why is printing to stdout so slow? Can it be sped up?,这回答提到 打印到stdout慢是因为终端的关系,换一个快速的终端就能提升。这解释了第一个原因。

stdout本身的缓冲机制是怎样的? Stdout Buffering介绍了glibc对于stdout缓冲的做法:

  • stdout指向的是终端的时候,那么它的缓冲行为是 line-buffered,意思是如果缓冲满了或者遇到了newline字符,那么就flush。
  • stdout没有指向终端的时候,那么它的缓冲行为是 fully-buffered,意思是只有当缓冲满了的时候,才会flush。

其中缓冲区大小是4k。下面是一个总结的表格“
GNU libc (glibc) uses the following rules for buffering”:

StreamTypeBehavior
stdininputline-buffered
stdout (TTY)outputline-buffered
stdout (not a TTY)outputfully-buffered
stderroutputunbuffered

那也就是说当 stdout指向一个终端的时候,它采用的是 line-buffered策略,而终端的处理速度直接影响到了性能。

同时也给了我们另一个思路,不将 stdout指向终端,那么就能够用到 fully-buffered,比起 line-buffered能够带来更大提速效果(想想极端情况下每行只有一个字符)。

我写了一段小代码来做测试( gist)。先试一下 stdout指向终端的情况:

$ javac ConsolePrint.java
$ java ConsolePrint 100000
...
lines: 100,000
System.out.println: 1,270 ms
file: 72 ms
/dev/stdout: 1,153 ms

代码测试了三种用法:

  • System.out.println指的是使用 System.out.println所花费的时间
  • file指的是用4k BufferedOutputStream 写到一个文件所花费的时间
  • /dev/stdout则是同样适用4k BufferedOutputStream 直接写到 /dev/stdout所花费的时间

发现写到文件花费速度最快,用 System.out.println和写到 /dev/stdout所花时间在一个数量级上。

如果我们将输出重定向到文件:

$ java ConsolePrint 100000 > a
$ tail -n 5 a
...
System.out.println: 920 ms
file: 76 ms
/dev/stdout: 31 ms

则会发现 /dev/stdout速度提升到 file一个档次,而 System.out.println并没有提升多少。之前不是说 stdout不指向终端能够带来性能提升吗,为何 System.out.println没有变化呢?这就要Java对于 System.out的实现说起了。

Java程序角度

下面是 System的源码:

public final static PrintStream out = null;
...
private static void initializeSystemClass() {
  FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
  setOut0(newPrintStream(fdOut, props.getProperty("sun.stdout.encoding")));
}
...
private static native void setOut0(PrintStream out);
...
private static PrintStream newPrintStream(FileOutputStream fos, String enc) {
  ...
  return new PrintStream(new BufferedOutputStream(fos, 128), true);
}

可以看到 System.outPrintStream类型,下面是 PrintStream的源码:

private void write(String s) {
  try {
    synchronized (this) {
      ensureOpen();
      textOut.write(s);
      textOut.flushBuffer();
      charOut.flushBuffer();
      if (autoFlush && (s.indexOf('\n') >= 0))
        out.flush();
    }
  } catch (InterruptedIOException x) {
    Thread.currentThread().interrupt();
  } catch (IOException x) {
    trouble = true;
  }
}

可以看到:

  1. System.out使用的缓冲大小仅为128字节。大部分情况下够用。
  2. System.out开启了autoFlush,即每次write都会立即flush。这保证了输出的及时性。
  3. PrintStream的所有方法加了同步块。这避免了多线程打印内容重叠的问题。
  4. PrintStream如果遇到了newline符,也会立即flush(相当于 line-buffered)。同样保证了输出的及时性。

这解释了为何 System.out慢的原因,同时也告诉了我们就算把 System.out包到BufferedOutputStream里也不会有性能提升。

Docker容器角度

那么把测试代码放到Docker容器内运行会怎样呢?把gist里的Dockerfile和ConsolePrint.java放到同一个目录里然后这样运行:

$ docker build -t console-print .
$ docker run -d --name console-print console-print 100000
$ docker logs --tail 5 console-print
...
lines: 100,000
System.out.println: 2,563 ms
file: 27 ms
/dev/stdout: 2,685 ms

可以发现 System.out.println/dev/stdout的速度又变回一样慢了。因此可以怀疑 stdout使用的是 line-buffered模式。

为何容器内的 stdout不使用 fully-buffered模式呢?下面是我的两个猜测:

  • 不论你是 docker run -t分配 tty启动,还是 docker run -d不非配tty启动,docker都会给容器内的 stdout分配一个 tty
  • 因为docker的logging driver都是以“行”为单位收集日志的,那么这个 tty必须是 line-buffered

虽然 System.out.println很慢,但是其吞吐量也能够达到~40,000 lines/sec,对于大多数程序来说这不会造成瓶颈。

参考文档

神秘的Eureka自我保护

$
0
0

根据CAP定理,Eureka是一个AP系统,这就导致了在网络分区期间多个注册表中的信息不一致。自我保护功能则是为了尽可能降低这种不一致。

自我保护的定义

自我保护(self preservation)是Eureka的一项功能,Eureka注册表在未收到实例的心跳情况超过一定阈值时停止驱逐过期的实例。

从一个健康的系统开始

把下面看成一个健康的系统。
1.jpg

假设所有的微服务都处于健康的状态并成功注册到Eureka注册表中。

多个注册表间会同步注册表记录,所有的微服务实例都处于UP状态。假设实例2从注册中心发现里实例4,并调用实例4上的服务。

突发网络分区

假设出现了网络分区,系统变成下面的状态。
2.jpg

由于网络分区,实例4和5丢失了注册中心的连接,但是实例2仍然可以连接到实例4。Eureka服务端因为没有收到实例4和5的心跳(超过一定时间后),将他们驱逐。然后Eureka服务端意识到突然丢失了超过15%(2/5)的心跳,因此其进入自我保护模式。

从此时开始,Eureka服务端不在驱逐任何实例,即使实例真正的下线了。
3.jpg

实例3下线,但其始终存在注册表中。但此时注册表还会接受新实例的注册。

自我保护的基本原理

自我保护功能在下面两种情况下是合理的:
  • Eureka服务端因为弱网分区问题没有收到心跳(这并不意味着客户端下线),但是这种问题可能会很快被修复。
  • 即使Eureka服务端和客户端的连接断开,客户端间还可以继续保持连接。(比如上面实例2仍然可以连接到实例4)


配置(默认)

下面的配置会直接或间接影响到自我保护的行为。
eureka.instance.lease-renewal-interval-in-seconds = 30  

客户端发送心跳的频率。服务端会以此在计算期望收到心跳数,默认30秒。
eureka.instance.lease-expiration-duration-in-seconds = 90  

多长时间未收到心跳后,实例才可以被驱逐,默认90秒。
eureka.server.eviction-interval-timer-in-ms = 60 * 1000  

Eureka服务端驱逐操作的执行频率,默认60秒。
eureka.server.renewal-percent-threshold = 0.85  

期望心跳数达到该阈值后,就会进入自我保护模式,默认0.85。
eureka.server.renewal-threshold-update-interval-ms = 15 * 60 * 1000  

期望心跳数的计算间隔,默认15分钟。
eureka.server.enable-self-preservation = true  

是否允许Eureka服务端进入自我保护模式,默认开启。

理解配置

Eureka服务端在“上一分钟实际收到的心跳数"小于“每分钟期望的心跳数"时就会进入自我保护模式。

期望的每分钟心跳数

假设renewal-percent-threshold设置为0.85。

计算方式:
  • 单个实例每分钟期望的心跳数是:21
  • N个实例的每分钟期望的心跳数:2 * N
  • 期望的上一分钟最小心跳数:2 * N * 0.85


实际的每分钟心跳数

正如上面所述,两个定时调度器独立地运行计算实际和期望的心跳数。此外还有另一个调度任务EvictionTask进行结果比较,并识别当前系统是否在自我保护状态。

这个调度任务每个eviction-interval-timer-in-ms时间执行一次,并决定是否驱逐实例。

结论

  • 基于使用的经验,大多数情况下自我保护模式都是错误的,它错误地认为一些下线的微服务实例是不良的网络分区
  • 自我保护永远不会过期,除非下线的实例重新上线
  • 如果启用了自我保留,则无法对实例的心跳间隔进行微调,因为自我保护在计算期望心跳数是按照30s间隔来计算的
  • 除非环境中经常出现类似的网络分区故障,否则建议关闭


原文链接: https://atbug.com/translation- ... tion/
    Viewing all 15907 articles
    Browse latest View live