总体介绍
easy-serverless
是一个应用部署平台,用于满足不少同事对于部署应用迫切的需求,部署小应用再也不需要:申请服务器、搭建环境、申请网络权限、申请域名等一系列操作,打开 easy-serverless
平台,即可通过几步操作将应用部署到公司内部的 Kubernetes
集群上。目前支持部署前端静态网站,Node
服务,Nginx
服务, Mongodb
及 Redis
数据库, MinIO
对象存储等。
平台依赖于公司内部的 Kubernetes
集群,每种类型的容器化应用都由自定义的镜像创建,使用了 Kubernetes
的 Javascript
客户端库提供的 API
来执行容器操作。持久化存储采用分布式存储方案 Longhorn
来对存储卷进行管理。
用户分析
服务种类占比
根据文章发布时统计,总运行服务个数有 114 个
可以看出大部分的平台用户选择把个人博客,平台官网等静态前端网站部署在 easy-serverless
平台上。
整体架构图
其中的 token 为集群中创建的 service account
的 token
,并分配了访问权限,可通过 kubectl get secret
获取到相应的 token
。
图中的自定义 namespace
为以用户名称创建的命名空间,用户创建的服务都运行在自己的命名空间里。
背景知识
想要理解 easy-serverless
的工作原理,我们需要先了解一下 Docker
,Kubernetes
,Longhorn
的知识, 有相关背景知识的同学可以跳过这个章节。
Docker
Docker
的作用和虚拟机相似,但是更加的轻量,不需要模拟硬件也不需要单独运行操作系统,只为每一个应用提供完全隔离的 container
,不同的 container
之间相互不受影响。Docker 中比较重要的三个概念是:
- dockerfile
- image
- container
dockerfile
定义镜像,可指定为 Linux
基础镜像,或者为 Docker Hub 上开源的镜像,然后可在基础镜像上自定义。镜像定义好之后,我们可以通过镜像去创建不同的容器(container),由此创建的容器环境都是一致的(比如操作系统,node 版本等)。创建多个容器化应用,相比起在多个不同的服务器上部署相同的应用,减少了部署上带来的麻烦,也避免了不同服务器环境不一致导致的应用表现不一致的问题。
Kubernetes
Kubernetes
是一个开源的 Docker
容器编排系统,一个 Kubernetes
集群通常包含 2 台及以上的服务器,其中包含一个 master
节点和多个 node
节点, master
节点负责整个集群的管理和控制。它能很好的支撑分布式系统,内部已包含故障发现、服务滚动升级、在线扩容等能力。Kubernetes
中所有的资源对象都可以采用 yaml
文件来定义。
Kubernetes 中比较重要的几个概念是:
- namespace(命名空间)
- pod
- service
- kubectl
namespace
用于实现资源隔离,在逻辑上区分不同资源。在easy-serverless
中使用对应的用户名创建了命名空间, 这规定了 pod,存储卷等资源从属与哪个命名空间。还结合了 configMap 的配置,限定用户的 namespace 能占用的资源配额,比如服务数量,存储空间限制等。pod
里会有一个或者是多个容器,就是我们的服务运行的地方。可以使用 yaml 文件基于特定镜像定义对应的 deployment,也可以直接定义资源类型 pod。service
是独立于 pod 的生命周期的,可以把 pod 里面的服务,暴露到集群外部。Kubernetes
内部的kube-proxy
是一个负载均衡器,负责把 service 的请求转发到后端的某个 pod 实例上, service 也会有自己独特的 Cluster IP。kubectl
为客户端cli
工具, 用户可以通过命令行的方式对集群进行操作, 包括查看pod
运行状态,资源对象的创建,修改,运行等, 我们可以通过网站提供的 交互式教程 来学习相关的知识和Kubernetes
的基本操作。
Longhorn
longhorn
是 Kubernetes
的分布式持久化存储卷(PersistantVolume
)管理系统, 社区目前开源的有 Longhorn
, Ceph
这两个方案。
Kubernetes
有自己的持久卷,longhorn
提供了一个比较完善的管理体系, 可运行 Longhorn UI
对存储卷进行管理操作。使用 longhorn
申请资源时也同样需要在 yaml 中定义持久化存储卷声明。对于有两个节点的集群,每个存储卷会对应一个 engine
和在不同节点上的 2 个副本,这保证了每个 node 都可以访问存储卷,存储卷副本的编排也依赖于 Kubernetes
。
功能点分析
了解完了涉及的基础知识,下面会对 easy-serverless
中的具体功能实现进行说明。
镜像的定制
easy-serverlesss
中支持创建的静态网站,node 服务,mongo,minio 等服务,这些都是基于上传到公司云的自定义的镜像创建的。以创建一个简单的 node 服务镜像为例,说明下如何制作自定义的镜像。
(1)在本地安装 Docker
,定义自己的 dockerfile
FROM node:16-alpine3.11
MAINTAINER username yourEmail@xxx.com
SHELL ["/bin/sh", "-c"]
RUN echo 'start build' && \
sed -i 's/dl-cdn.alpinelinux.org/mirrors.yourmirror.com.cn/g' /etc/apk/repositories && \
apk add git
dockerfile
自定义的镜像基于 DockerHub
中的镜像 node:16-alpine3.11
,alpine 不同版本的 node 镜像,默认已经安装了 node, npm, yarn 等。考虑到直接访问官方镜像源的一些网络限制,在此基础上修改了镜像源地址为内部的镜像源地址,并且执行了一些自定义的操作。
(2)本地基于 dockerfile
构建你的镜像
docker build -f ./dockerfile -t my-node:16-alpine3.11 .
(3) 构建成功之后可以在本地的鲸鱼应用 Docker Desktop 中运行一个容器,测试相关的功能
(4) 公司云平台上是需要把镜像打包之后上传, 我们使用 save 命令保存压缩包到本地,然后上传到公司云平台,就可以使用自己的镜像了
docker save -o ./node-16-alpine.tar.gz my-node
服务和应用的创建
我们使用了 @kubernetes-client-node 客户端库来使用 Kubernetes
提供的能力,它提供了一系列的 API 便于去操作和管理我们的服务。除了 JavaScript 客户端,官方还提供了 Go,Java,Python,dotnet 等客户端库便于使用。
前面提到过 Kubernetes
中所有的资源对象都可以采用 yaml 文件来定义,资源对象的定义包含 Pod, Deployment, Service, Ingress, Replication Controller 等等,一个简单的 yaml 定义如下:
apiVersion: XXX
kind: XXX // 类型
metadata: // 基础信息
name: XXX
labels:
{{key: value}}
spec: // 详细定义可选内容
container:
volumes:
以平台创建的一个 node 服务为例, 我们来分析部署一个服务都需要声明哪些资源, 在 easy-serverless
的实现中一共定义了三种资源类型,ConfigMap(配置集),Deployment(部署,部署中包含容器),以及 Service(服务)。
-
ConfigMap: 一系列配置信息,环境变量的结合,这些信息都能通过 api 或者命令行 kubectl describe configmap app_name 查到。
-
Deployment: deployment 同样在包含了一些基础信息,在 spec 详细信息中创建了一个容器,并且执行拉取仓库代码,安装依赖,运行服务等操作,还定义了内部的端口。
-
Service: 判断了如果用户定义了外部端口,则外部可通过端口访问,没有则内部访问特有的 Cluster IP。
编写好这 3 部分的 yaml,我们就可以使用 API 根据这个 yaml 文件去创建服务了。
限额的配置
为了防止服务器资源的滥用, easy-serverless
中做了一些限额的配置。
每个用户注册时会分配一定额度的资源,比如允许创建的服务数,可使用的存储容量。在新创建资源的时候会去比对已经使用的配额和分配的额度 ,判断用户是否还有额度添加资源。
存储卷的管理
对于 mongodb, redis 这类应用,我们需要用到持久化的存储。
Longhorn 的特点是支持分布式, longhorn-ui 提供了一个可视化的界面可对存储卷进行操作。有对应的重启,备份,快照等功能。
想要访问 longhorn-ui,只需要找到 longhorn 的 service 对应的外部端口号,使用 master节点的IP:port
就可以了
域名及ingress配置
域名我们申请了 XX.dev.XXX.net 这个地址,编辑域名的时候可以在界面中设置流量的转发,这里实际是创建了一个 ingress
类型。
ingress
为 Kubernetes
内部支持的功能,ingress 的作用和 nginx 相似,可以配置对应的规则做资源转发,但是只支持转发到集群内部服务, 根据用户配置的转发规则来添加 ingress 中的规则。
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: {{domainName}}
labels:
create-by: ezsvs
spec:
rules:
- host: {{domainName}}.dev.XXX.net
http:
paths:
{{#each forwards}}
- path: {{path}}
# pathType: Prefix
backend:
serviceName: {{serviceName}}
servicePort: {{servicePort}}
{{/each}}
参考资料
书籍:
《Kubernetes权威指南》