关于kubernetes:国内最具影响力科技创投媒体36Kr的容器化之路

4次阅读

共计 8216 个字符,预计需要花费 21 分钟才能阅读完成。

本文由 1 月 19 日晚 36Kr 运维开发工程师田翰明在 Rancher 技术交换群的技术分享整顿而成。微信搜寻 rancher2,增加 Rancher 小助手为好友,退出技术群,实时加入下一次分享~

田翰明,36Kr 运维开发工程师,在 36Kr 次要负责运维自动化,CI/CD 的建设,以及利用容器化的推动。

背景

36Kr 是一家创建于 2010 年,专一于科技创投畛域的媒体公司,业务场景并不简单,前端次要应用 NodeJS 进行 Render,挪动端有 Android 也有 iOS,后端服务简直全都由 PHP 来反对。应用 PHP 的次要起因是在最后进行技术选型的时候发现,PHP 进行 Web 开发效率比拟高,起初就始终这样连续下来了。

然而在前期,随着业务的突飞猛涨,在程序设计中又没能进行解耦,就导致了许多服务耦合成了一个很臃肿的单体利用,逻辑耦合重大,进而导致了很多的性能问题,随着问题越来越难改,开发工作又越来越紧,就不得不往后拖,越往后拖留下的问题就更难改,造成了一个恶性循环,留下了很多的技术债,很不利于后续的开发工作,并且一旦呈现了问题,也很难追溯具体起因,所以在那时候常常听到一句话“这是历史遗留问题”。

B/S、C/S、单体利用,这是一种很传统 也很简略的架构,然而毛病也暴露无遗,所以常常因为一个业务逻辑的性能问题,进而影响到所有的业务。在运维侧,运维只可能通过堆机器,升配置等策略来应答,投入了很多的机器老本和人力老本,然而收效甚微,很是被动。

这种状况曾经是火烧眉毛了,终于技术团队决定应用 Java 语言进行重构,将单体利用进行微服务化拆解,彻底改变这种因为单体利用故障而导致生产环境呈现大范畴的故障。

需要剖析 + 选型

在重构打算开始一段时间后,为了节俭虚机资源,咱们一台虚机上运行了多个 Java 程序,然而因为没有资源隔离和灵便的调度零碎,其实也会导致一些资源的节约。并且在高并发场景下,偶然会有资源抢占导致一个利用影响另一个利用的状况。为此,咱们运维专门开发了一套自动化部署零碎,零碎内包含部署、监控检测、部署失败回滚、重启等根底性能。

随着过后 K8s 的风靡,还有 Rancher 2.x 的公布,咱们逐步发现,咱们所面临的这些问题,它们根本都能解决,比方资源隔离、deployment 的控制器模型、灵便的调度零碎,这些都有,这就是最好的自动化部署零碎啊,于是咱们运维侧,也开始决定向容器化进军。

在选型上,因为咱们的服务根本都在阿里云下面,所以第一个想到的是阿里云。时因为咱们和华为有一些业务的往来,所以华为的 CCE 也作为了备选,然而思考到咱们的服务资源全副在阿里云上,这个迁徙老本切实太大了,所以就没再思考华为云。

咱们一开始应用过 Rancher 1.6,然而只是用来治理主机上部署的原生 Docker。也因而对 Rancher 的产品产生了很大的好感。

需要方面,因为要升高咱们研发人员的学习老本,容器治理平台的易用性非常重要。此外,K8s 的根底性能是必须的,因为 K8s 还在高速倒退阶段,所以能须要够随时跟上更新,有安全漏洞后也须要第一工夫进行更新打补丁,同时还要有根本的权限管制。而且咱们公司外部没有专门的 K8S 团队,运维人员也只有 2 位,所以如果可能有业余人员进行技术上的交换,产生了问题能够有业余的服务团队来帮助也非常重要。

综上,基本上就是 Rancher 完胜,UI 做得十分敌对,开发人员可能很快上手,更新迭代速度也十分快,发现破绽后也会有具体的补丁计划,认证策略也完满反对咱们的 OpenLDAP 协定,可能对开发、测试、运维人员进行不同权限管制,并且也是第一家做到反对多云环境的,不便当前咱们做跨云的计划。

咱们这次容器化的过程,次要经验了以下几个因素的思考,明天我就来和大家分享咱们在 Rancher 上的一些实际,心愿能给大家带来帮忙:

  • 利用的容器化革新
  • Rancher 的高可用性
  • 容器的运维
  • 多租户隔离

利用的容器化革新

因为咱们的开发人员,有相当一部分是没有接触过容器的,为了能对开发人员更敌对一些,咱们的镜像分成了两层,次要的 Dockerfile 编写是由咱们运维人员来编写的,而开发人员代码仓库里的 Dockerfile 是最简略的,基本上只有代码拷贝的过程和一些必传的变量,具体能够参考以下示例:

## 这是运维人员保护的 Dockerfile 示例
## 本示例仅做参考
FROM alpine:3.8
MAINTAINER yunwei <yunwei@36kr.com>
WORKDIR /www
RUN mv /etc/apk/repositories /etc/apk/repositories.bak \
    && echo "http://mirrors.aliyun.com/alpine/v3.8/main/" >> /etc/apk/repositories \
  && apk update && apk upgrade
RUN apk --no-cache add ca-certificates wget && \
    wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub && \
    wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.29-r0/glibc-2.29-r0.apk && \
    apk add glibc-2.29-r0.apk && rm -f glibc-2.29-r0.apk
RUN apk add -U --no-cache \
  bash \
  sudo \
  tzdata \
  drill  \
  iputils \
    curl \
  busybox-extras \
  && rm -rf /var/cache/apk/* \
  && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY java-jar/jdk1.8.0_131 /usr/local/jdk1.8.0_131
ENV TZ="Asia/Shanghai"
ENV JAVA_HOME=/usr/local/jdk1.8.0_131
ENV CLASSPATH=$JAVA_HOME/bin
ENV PATH=.:$JAVA_HOME/bin:$PATH
ENV JAVA_OPTS="-server -Xms1024m -Xmx1024m"
CMD java -jar $JAVA_OPTS -Dserver.port=8080 server.jar

=======================================

## 这是开发人员保护的 Dockerfile 的示例
FROM harbor.36kr.com/java:v1.1.1
MAINTAINER developer <developer@36kr.com>
ADD web.jar ./server.jar

能够看到,开发人员所保护的 Dockerfile 能够说相当简略了,这大大的升高了开发人员保护的难度。

另外,因为构建产物的大小,很大水平上决定了部署工夫的长短,所以咱们应用了号称最小的镜像——alpine,alpine 有很多的长处:

  • 体积小
  • 有包管理器、有丰盛的依赖
  • 大厂的反对,蕴含 Docker 公司在内的多家大厂官网应用

然而他有一个毛病,alpine 上并没有 glibc 库,他所应用的是一个 musl libc 的小体积代替版,然而 Java 是必须依赖的 glibc 的,不过早就有大神理解了这点,在 GitHub 上曾经提供了预编译的 glibc 库,名字为alpine-pkg-glibc,装上这个库就能够完满反对 Java,同时还可能放弃体积很小。

Rancher 的高可用性

装置 Rancher 的形式有两种:单节点装置和高可用集群装置。个别单节点装置仅实用于测试或者 demo 环境,所以要正式投入使用的话,还是举荐高可用集群的装置形式。

咱们一开始测试环境就应用了单节点装置的形式,起初因为 Rancher Server 那台机器呈现过一次重启,就导致了测试环境故障,尽管备份了,然而还是失落了大量数据,最初咱们测试环境也采纳了 HA 高可用部署,整个架构如下图所示。

Rancher Server 我是采纳的 RKE 装置,并且为了避免阿里云呈现区域性的故障,咱们将 Rancher Server 的三台机器,部署在了两个可用区,Rancher Server-001、003 在北京的 H 区、Rancher Server-002 在北京的 G 区。

负载平衡,咱们采纳的是阿里云的 SLB,也是洽购的主备型实例,避免单点故障,因为 Rancher 必须应用 SSL 证书,咱们也有本人的域名证书,为了不便在 SLB 上进行 SSL 证书的保护,咱们应用的是 7 层协定,在 SLB 上做的 SSL 终止,Rancher Server 的架构图能够参考下图:

上游集群,也就是用来承载业务的 K8s 集群,咱们也是一半一半,在阿里云的两个可用区进行部署的,须要留神的是,为了保障两个区的网络时延 <= 15 ms,这就实现了一个高可用的灾备架构。

备份方面,咱们也应用了阿里云 ECS 快照 + ETCD S3 协定备份到了阿里云的 OSS 对象存储两种计划,确保呈现故障后,可能及时复原服务。

部署的具体教程能够参考 Rancher 官网文档。

容器的运维

容器的运维,这里次要指容器的日志收集和容器监控,容器监控方面呢,Rancher 自带了 Prometheus 和 Grafana,而且和 Rancher 的 UI 有一些整合,就十分的不便,所以监控方面我就不开展讲了,我次要说一说日志收集。

在 K8s 里,日志的收集相比传统的物理机、虚机等形式要简单一些,因为 K8s 所提供的是动静的环境,像绑定 hostpath 这种形式是不实用的,咱们能够通过以下这个表格直观的比照一下:

能够看到,K8s 须要采集的日志品种比拟多,而容器化的部署形式,在单机器内的利用数是很高的,而且都是动静的,所以传统的采集形式是不适用于 K8s 的。

目前 K8s 的采集形式大体能够分为两种,被动采集 被动推送

被动推送个别有 DockerEngine 和 业务直写两种形式:DockerEngine 是 Docker 的 LogDriver 原生自带的,个别只能收集 STDOUT、个别不倡议应用;而业务直写,则须要在利用里集成日志收集的 SDK,通过 SDK 间接发送到收集端,日志不须要落盘,也不须要部署 Agent,然而业务会和 SDK 强绑定,灵活性偏低,倡议对于日志量较大,或者对日志有定制化要求的场景应用。

被动推送是采纳部署日志收集 Agent 进行采集的,有两种形式,一种是 Daemonset 每个机器节点上部署一个 Agent,还有一种 Sidecar,每个 Pod 以 Sidecar 的模式部署一个 Agent。

Sidecar 部署形式比拟耗费资源,相当于每个 Pod 都有一个 agent,然而这种形式 灵活性以及隔离性较强,适宜大型的 K8s 集群或者作为 PaaS 平台为业务方提供服务的群应用,Daemonset 部署形式,资源耗费较小,适宜性能繁多、业务不多的集群。

联合咱们本身的场景,属于小规模集群,并且业务也不算多,咱们抉择了 Daemonset 的部署形式,在测试环境,咱们通过调研抉择了阿里开源的一个日志收集组件 log-pilot GitHub 地址是:github.com/AliyunContainerService/log-pilot,通过联合 Elasticsearch、Kibana 等算是一个不错的 K8s 日志解决方案。

因为咱们的服务器都在阿里云上,咱们运维人员比拟少只有 2 位,没有精力再去保护一个大型的分布式存储集群,所以咱们的业务日志抉择存储在了阿里云的日志服务,所以在生产环境,咱们的 K8s 也应用了阿里云日志服务,目前单日日志 6 亿 + 没有任何问题。

应用阿里云收集日志呢,你须要开明阿里云的日志服务,而后装置 Logtail 日志组件 alibaba-log-controller Helm,这个在官网文档里有装置脚本,我把文档链接贴在上面,在装置组件的过程中会主动创立 aliyunlogconfigs CRD,部署alibaba-log-controller 的 Deployment,最初以 DaemonSet 模式装置 Logtail。而后你就能够在控制台,接入你想要收集的日志了。装置完当前是这样的:

Logtail 反对采集容器内产生的文本日志,并附加容器的相干元数据信息一起上传到日志服务。Kubernetes 文件采集具备以下性能特点:

  • 只需配置容器内的日志门路,无需关怀该门路到宿主机的映射
  • 反对通过 Label 指定采集的容器
  • 反对通过 Label 排除特定容器
  • 反对通过环境变量指定采集的容器
  • 反对通过环境变量指定排除的容器
  • 反对多行日志(例如 java stack 日志)
  • 反对 Docker 容器数据主动打标签
  • 反对 Kubernetes 容器数据主动打标签

如果你想理解更多,能够查看阿里云日志服务的官网文档:

https://help.aliyun.com/docum…

容器的多租户隔离

我这里所讲的,次要指的是企业外部用户的多租户隔离,而不是指的 SaaS、KaaS 服务模型的多租户隔离。

在权限方面,因为我司对于权限的管控较严格,而 Rancher 恰好提供了十分不便的基于 集群、我的项目、命名空间等多个粒度的权限管制,并且反对我司基于 OpenLDAP 的认证协定,十分便于管理,我能够给不同项目组的开发、测试人员开明绝对应的 集群 / 我的项目 / 命名空间的权限。

比方下图,我能够给集群增加用户、也能够给某个 Project 增加用户,并且能够指定几个不同的角色,甚至能够自定义角色。

比方场景 1:我能够给 我的项目组长,调配开发环境集群 -> 我的项目 1 所有者 (Owner) 权限,而后我的项目组长能够自在管制给本我的项目增加他的成员,并调配相应权限。

场景 2:我能够给 测试经理,调配测试集群的所有者 (Owner) 权限,由测试经理来调配,谁来负责哪个我的项目的测试部署,以及开发人员只能查看日志等。

在资源方面,肯定要进行容器的资源配额设置,如果不设置资源限额,一旦某一个利用呈现了性能问题,将会影响整个 node 节点上的所有利用,K8s 会将呈现问题的利用调度到其余 node 上,如果你的资源不够,将会呈现整个零碎的瘫痪,导致雪崩。

Java 利用的资源配额限度也有一个坑,因为默认 Java 是通过 /proc/meminfo 来获取内存信息的,默认 JVM 会应用零碎内存的 25% 作为 Max Heap Size,然而容器内的 /proc/meminfo 是宿主机只读模式挂载到容器里的,所以采取默认值是行不通的,会导致利用超过容器限度的内存配额后被 OOM,而健康检查又将服务重启,造成利用一直的重启。

那是不是通过手动参数设置 JVM 内存 = 容器内存限额 呢?不行!因为 JVM 耗费的内存不仅仅是 Heap,因为 JVM 也是一个利用,它须要额定的空间去实现它的工作,你须要配置的限额应该是 Metaspace + Threads + heap + JVM 过程运行所需内存 + 其余数据 对于这块,因为波及到的内容较多,就不进行开展,感兴趣的同学能够本人去搜寻 一下。

总 结

因为咱们的业务场景并不简单,所以咱们的容器化之路,其实走的也绝对来讲蛮顺畅的,咱们的运维人员很少,只有 2 位,所以咱们也没有太多的工夫精力去保护太多的自建零碎,咱们应用了很多的阿里云产品,包含 Rancher,他很不便的部署形式,敌对的 UI,包含集成好的监控等等,在容器化之路上给了咱们很大的信念。

咱们应用构建两层镜像的形式,升高了开发人员的学习复杂度。应用了小体积镜像 alpine + 预编译 glibc 减小了镜像体积。进步了部署的工夫,在架构上,咱们采纳了阿里云双区机房的灾备的架构,以及齐备的备份计划。应用 Daemonset 部署的日志收集组件,收集到阿里云日志服务,撑持咱们 6 亿 / 日的日志零碎。Rancher 还提供给了咱们深度集成的监控零碎、多租户隔离等。还有咱们本人踩坑 踩进去的资源配额设置。

其实容器化并不简单,如果没有 K8s,咱们须要本人构建衰弱监测零碎、发版零碎、保护不同的主机环境,不能细粒度的进行资源划分,不能更无效的利用计算资源,运维的工作次要是什么?在我看来其实就是 节约老本、提高效率。虚拟化、自动化、智能化、高性能、高可用、高并发 等等,这些无一不是围绕着老本和效率这两个词,而 K8s 其实曾经帮咱们都做好了,而像 Rancher 这种编排平台又帮咱们升高了 K8s 的学习复杂度,所以你要做的就是退出 K8s,好了,到这里这次的分享就完结了。感激~

社区 QA

Q1:K8S 在生产环境的高可用存储计划有举荐吗?

A1:存储计划没有标准答案,咱们次要应用阿里云,所以用的是阿里云的块存储,比拟常见的计划还有 Ceph、GlusterFS、Portworx、OpenEBS 等,他们各有优劣,需联合本人的业务需要进行抉择

Q2:灰度公布,Kubernetes 网络流量能够通过服务网格分流实现网络层面的散发,然而波及到利用大版本的更新时候,波及到数据库构造的变更的时候,如何实现灰度公布?

A2:没有遇到过这个场景,不过提供一个思路,能够筹备两套数据库,网络分流也能够分流到不通数据库,具体须要你本人验证一下是否可行

要分分明这是两层,一层是逻辑层,一层是数据层,不能一概而论

Q3:Pipeline 是用什么做的?Pipeline 下,如何解决同一个分支,须要并行测试多个版本的场景?我用 Rancher 的 Pipeline,局限性比拟大,就是同一个分支无奈并行多套进行测试。命名空间在应用,然而同一个分支下,命名空间是写在.rancher.yml 下的,所以无奈辨别,Rancher 的 Pipeline 不能在里面注入变量进行辨别。

A3:Rancher 的 Pipline 目前还是有一些不够灵便,咱们应用的是自建 Jenkins 做 Pipeline 的,并行测试,能够用命名空间等隔离策略进行隔离,或者筹备多套测试环境

Q4: 你们运维的 Dockerfile 和开发的 Dockerfile 是怎么合并的?

A4:开发的 Dockerfile 是 From 运维的 Dockerfile

Q5:你们 k8s 的破绽扫描用的什么工具?个别什么级别的镜像破绽须要进行修复?

A5:临时没有应用漏扫工具,咱们次要依据 Rancher 企业服务告诉的修复倡议进行修复

Q6: 就是比如说从外网,通过 service ip 可能登陆并且治理容器。想实现这一步必须通过将 service ip 裸露进去,而后这个 service ip 怎么裸露进去?麻烦解答一下。

A6:如果需要是治理容器,其实能够应用 Rancher 的用户权限管制,让某一用户领有某一容器的权限,裸露 service ip 到公网,让用户治理容器是无奈实现的

Q6 : 好的,谢谢,我还有一点不明确,这个 service ip 有什么方法能让他裸露进去呢?你意思是说让不同的用户通过 rancher 平台去治理不同的容器吗?麻烦再给解答一下,谢谢。

A6:能够应用 NodePort 裸露,通过 Node ip 和 端口进行拜访,或者应用 私有云的负载平衡产品

Q6 : 我不是这个意思,我是想把 service ip 裸露进去,不只单单想通过集群外部拜访。

A6:service ip 原本就是 K8s 外部的,裸露不了,只能转发

Q7: 为何没有放在 3 个可用区,如果可用区 H 挂掉,是否会导致集群不可拜访?

A7:3 个可用区当然也是能够的,Rancher HA 架构,只有有一个 Server 可用就没有关系

Q8:求教下你们多套开发测试环境的 pipeline 是怎么样的流程呢 (差异化)?有应用 helm template 吗,不便解说下更多细节么?

A8:目前是通过 Jenkins 部署参数,部署的时候能够抉择 命名空间、环境标识、分支等,通过 sed 批改 template

Q9:请问你们的 devops 流是怎么的呢?一个环境对应一个 docker 镜像,还是说 test pre prd 共用一个 docker 镜像呢?如果是一个 docker 镜像共用 test pre prd 的话是怎么做的呢(比方不同环境的配置以及开发的协‘同开发流)?

A9:咱们是用的同一个镜像,部署时通过抉择不同的环境标识参数,程序会主动注入不同环境的配置,须要开发进行一些相应的配置批改

Q10: 不大懂容器的资源限度该如何配置,本人配置了感觉不起作用

A10:Rancher 能够在我的项目、命名空间、Pod 三个粒度进行设置,优先级相同

正文完
 0