写点什么

Rego 不好用?用 Pipy 实现 OPA

作者: Addo Zhang

  • 2021-07-15
  • 本文字数:5099 字

    阅读完需:约 17 分钟

Rego 不好用?用 Pipy 实现 OPA

还不知道 Pipy 是什么的同学可以先看下简介:


Pipy 是一个轻量级、高性能、高稳定、可编程的网络代理。Pipy 核心框架使用 C++ 开发,网络 IO 采用 ASIO 库。 Pipy 的可执行文件仅有 5M 左右,运行期的内存占用 10M 左右,因此 Pipy 非常适合做 Sidecar proxy。


Pipy 内置了自研的 pjs 作为脚本扩展,使得 Pipy 可以用 JS 脚本根据特定需求快速定制逻辑与功能。

Pipy 采用了模块化、链式的处理架构,用顺序执行的模块来对网络数据块进行处理。这种简单的架构使得 Pipy 底层简单可靠,同时具备了动态编排流量的能力,兼顾了简单和灵活。通过使用 REUSE_PORT 的机制(主流 Linux 和 BSD 版本都支持该功能),Pipy 可以以多进程模式运行,使得 Pipy 不仅适用于 Sidecar 模式,也适用于大规模的流量处理场景。 在实践中,Pipy 独立部署的时候用作“软负载”,可以在低延迟的情况下,实现媲美硬件的负载均衡吞吐能力,同时具有灵活的扩展性。


在玩过几次 Pipy 并探究其工作原理后,又有了更多的想法。


初探可编程网关 Pipy可编程网关 Pipy 第二弹:编程实现 Metrics 及源码解读可编程网关 Pipy 第三弹:事件模型设计


在使用 OPA 的时候,一直觉得 Rego 不是那么顺手,使用 pipy js 来写规则的想法油然而生。今天就一起试试这个思路。果然,不试不知道,一试发现太多的惊喜~Pipy 不止于“代理”,更有很多可以适用的场景:


•极小的单一可执行文件(single binary)使得 pipy 可能是最好的 “云原生 sidecar”

•sidecar 不仅仅是代理,还可以做控制器,做运算单元

•proxy 的串路结构适合各种管控类的操作,比如访问控制

•Pipy js 的扩展能力和快速编程能力,很适合做 “规则引擎”,或者用最近流行的说法 “云原生的规则引擎”。对比 OPA 我认为它完全够格做一个 “羽量级规则执行引擎”。


现在我更倾向于定义 pipy 是一个 “云原生的流量编程框架”,代理只是其底层的核心能力,叠加了 pipy js 以后,上层可以做的事情很多,“流量滋养万物”。


在 使用 Open Policy Agent 实现可信镜像仓库检查 之后,就在想 Pipy 是否一样可以做到,将内核替换成 Pipy + 规则。所以今天大部分内容和上面这篇是相似的。


来,一起看看这个“不务正业”的 Pipy 如何实现 Kubernetes 的准入控制器 来做镜像的检查。


环境


继续使用 minikube


minikube start
复制代码


创建部署 Pipy 的命名空间


kubectl create namespace pipykubens pipykubectl label ns pipy pipy/webhook=ignore #后面解释
复制代码


规则


在 OPA 中,通过 kube-mgmt 容器监控 configmap 的改动,将 Policy 推送到同 pod 的 opa 容器中。


对于 Pipy 为了渐变,直接使用挂载的方式将保存了规则的 configmap 挂载到 Pipy 的容器中。


实际的使用中,Pipy 支持轮训的方式检查控制平面中规则的变更,并实时加载;也可以实现与 OPA 的 kube-mgmt 同样的逻辑。


实现了上一讲功能的 pipy 规则如下:


cat > pipy-rule.js <<EOFpipy({  _repoPrefix: '192.168.64.1', //192.168.64.1:5000 是笔者本地容器运行的一个私有仓库。  _tagSuffix: ':latest',}).listen(6443, {  tls: {    cert: os.readFile('/certs/tls.crt').toString(),    key: os.readFile('/certs/tls.key').toString(),  },})  .decodeHttpRequest()  .replaceMessage(    msg => (        ((req, result, invalids, reason) => (            req = JSON.decode(msg.body),            invalids = req.request.object.spec.containers.find(container => (              (!container.image.startsWith(_repoPrefix) ? (                reason = `${container.image} repo not start with ${_repoPrefix}`,                console.log(reason),                true              ) : (false))              ||              (container.image.endsWith(_tagSuffix) ? (                reason = `${container.image} tag end with ${_tagSuffix}`,                console.log(reason),                true              ) : (false)            ))),            invalids != undefined ? (              result = {                "apiVersion": "admission.k8s.io/v1beta1",                "kind": "AdmissionReview",                "response": {                    "allowed": false,                    "uid": req.request.uid,                    "status": {                        "reason": reason,                    },                },              }            ) : (              result = {                "apiVersion": "admission.k8s.io/v1beta1",                "kind": "AdmissionReview",                "response": {                    "allowed": true,                    "uid": req.request.uid                },              }            ),            console.log(JSON.encode(result)),            new Message({              'status' : 200,              'headers': {                'Content-Type': 'application/json'              }              }, JSON.encode(result))        ))()    )  )  .encodeHttpResponse()  EOF
复制代码


将规则保存在 configmap 中:


kubectl create configmap pipy-rule --from-file=pipy-rule.js
复制代码


在 Kubernetes 上部署 Pipy


Kubernetes 与准入控制器(Admission Controller[3])的通信需要使用 TLS。配置 TLS,使用 openssl 创建证书颁发机构(certificate authority CA)和 OPA 的证书/秘钥对。


genrsa -out ca.key 2048req -x509 -new -nodes -key ca.key -days 100000 -out ca.crt -subj "/CN=admission_ca"
复制代码


为 OPA 创建 TLS 秘钥和证书:


cat >server.conf <<EOF[req]req_extensions = v3_reqdistinguished_name = req_distinguished_nameprompt = no[req_distinguished_name]CN = pipy.pipy.svc[ v3_req ]basicConstraints = CA:FALSEkeyUsage = nonRepudiation, digitalSignature, keyEnciphermentextendedKeyUsage = clientAuth, serverAuthsubjectAltName = @alt_names[alt_names]DNS.1 = pipy.pipy.svcEOF
复制代码


注意 CN 和 alt_names 必须与后面创建 Pipy service 的匹配。


openssl genrsa -out server.key 2048openssl req -new -key server.key -out server.csr -config server.confopenssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 100000 -extensions v3_req -extfile server.conf
复制代码


为 OPA 创建保存 TLS 凭证的 Secret:


kubectl create secret tls pipy-server --cert=server.crt --key=server.key
复制代码


将 Pipy 部署为准入控制器(admission controller)。为了方便调试,我们使用启动 Pipy 的时候打开了控制台。


kind: ServiceapiVersion: v1metadata:  name: pipy  namespace: pipyspec:  selector:    app: pipy  ports:  - name: https    protocol: TCP    port: 443    targetPort: 6443  - name: gui # 方便调试    protocol: TCP    port: 6060    targetPort: 6060  - name: http    protocol: TCP    port: 6080    targetPort: 6080---apiVersion: apps/v1kind: Deploymentmetadata:  labels:    app: pipy  namespace: pipy  name: pipyspec:  replicas: 1  selector:    matchLabels:      app: pipy  template:    metadata:      labels:        app: pipy      name: pipy    spec:      containers:         - name: pipy          image: pipy:latest          imagePullPolicy: IfNotPresent          args:            - "pipy"            - "/opt/data/pipy-rule.js"            - "--gui-port=6060" # 方便调试            # - "--log-level=debug"          ports:          - name: gui            containerPort: 6060            protocol: TCP          - name: http            containerPort: 6080            protocol: TCP            - name: https            containerPort: 6443            protocol: TCP          volumeMounts:            - readOnly: true              mountPath: /certs              name: pipy-server            - readOnly: false              mountPath: /opt/data              name: pipy-rule      volumes:        - name: pipy-server          secret:            secretName: pipy-server        - name: pipy-rule          configMap:            name: pipy-rule
复制代码


暴露控制台的访问:

kubectl expose deploy pipy --name pipy-node --type NodePortkubectl get svc pipy-portminikube service --url pipy-node -n pipy# 找到控制台端口
复制代码


接下来,生成将用于将 Pipy 注册为准入控制器的 manifest。


cat > webhook-configuration.yaml <<EOFkind: ValidatingWebhookConfigurationapiVersion: admissionregistration.k8s.io/v1beta1metadata:  name: pipy-validating-webhookwebhooks:  - name: validating-webhook.pipy.flomesh-io.cn    namespaceSelector:      matchExpressions:      - key: pipy/webhook        operator: NotIn        values:        - ignore    rules:      - operations: ["CREATE", "UPDATE"]        apiGroups: ["*"]        apiVersions: ["*"]        resources: ["pods"]    clientConfig:      caBundle: $(cat ca.crt | base64 | tr -d '\n')      service:        namespace: pipy        name: pipyEOF
复制代码


生成的配置文件包含 CA 证书的 base64 编码,以便可以在 Kubernetes API 服务器和 OPA 之间建立 TLS 连接。


kubectl apply -f webhook-configuration.yaml
复制代码


测试


pod-bad-repo.yaml:


apiVersion: v1kind: Podmetadata:  creationTimestamp: null  labels:    run: web-server  name: web-server  namespace: defaultspec:  containers:  - image: nginx:1.21.1    name: web-server    resources: {}  dnsPolicy: ClusterFirst  restartPolicy: Alwaysstatus: {}
复制代码


kubectl apply -f pod-bad-repo.yamlError from server (nginx:1.21.1 repo not start with 192.168.64.1): error when creating "pod-bad-repo.yaml": admission webhook "validating-webhook.pipy.flomesh-io.cn" denied the request: nginx:1.21.1 repo not start with 192.168.64.1
复制代码


pod-bad-tag.yaml:


apiVersion: v1kind: Podmetadata:  creationTimestamp: null  labels:    run: web-server  name: web-server  namespace: defaultspec:  containers:  - image: 192.168.64.1:5000/nginx:latest    name: web-server    resources: {}  dnsPolicy: ClusterFirst  restartPolicy: Alwaysstatus: {}
复制代码


kubectl apply -f pod-bad-tag.yamlError from server (192.168.64.1:5000/nginx:latest tag end with :latest): error when creating "pod-bad-tag.yaml": admission webhook "validating-webhook.pipy.flomesh-io.cn" denied the request: 192.168.64.1:5000/nginx:latest tag end with :latest
复制代码


pod-ok.yaml


apiVersion: v1kind: Podmetadata:  creationTimestamp: null  labels:    run: web-server  name: web-server  namespace: defaultspec:  containers:  - image: 192.168.64.1:5000/nginx:1.21.1    name: web-server    resources: {}  dnsPolicy: ClusterFirst  restartPolicy: Alwaysstatus: {}
复制代码


kubectl apply -f pod-ok.yamlpod/web-server created
复制代码


总结


OPA 哪哪都好,唯一缺点就是其引进的 Rego 语言抬高了使用的门槛。而 Pipy 的规则是通过 JavaScrip 来编写的,前端的同学一样可以完成规则的编写。完全替代可能夸张了一些,但确实在部分场景下可以替代 OPA。


玩到这里,你会发现有了规则,加上功能强大的过滤器(现在我喜欢叫他们 Hook 了),Pipy 的可玩性非常强。


比如OPA: Kubernetes 准入控制策略 Top 5,比如...。大胆的想象吧。

想写一个系列,就叫“如何把 Pipy 玩坏”?


引用链接


[1] GitHub: https://github.com/flomesh-io/pipy

[2] 上一讲功能: https://atbug.com/image-trusted-repository-with-open-policy-agent/

[3] Admission Controller: https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/

2021-07-15 17:582246

评论

发布
暂无评论
发现更多内容

HDFS SHELL详解(6)

罗小龙

hadoop 28天写作 hdfs shell

【PS】给黑白照片上色

德育处主任

PhotoShop ps 28天写作

低代码开发技术

Sam678678

okhttp3 第一次使用

我就感觉到快

智汇华云 | 安超OS为企业数字化转型构建坚实的云基座

华云数据

盘点2020| 开启小马哥的新未来

小马哥

盘点2020

与前端训练营的日子 --Week11

SamGo

学习

微信视频号常见问题 | 视频号 28 天 (06)

赵新龙

28天写作

知乎问答:“既然生命无意义,为什么要活着?”

三只猫

28天写作

夜莺二次开发指南-资产设备管理

ning

滴滴夜莺 夜莺监控

每个人都拥有这项神技能

熊斌

职场成长 28天写作

看图学NumPy:掌握n维数组基础知识点,看这一篇就够了

华为云开发者联盟

Python 机器学习 数组 Numpy

十八般武艺玩转GaussDB(DWS)性能调优:路径干预

华为云开发者联盟

数据库 sql 性能调优 GaussDB 算子

Serverless 在 SaaS 领域的最佳实践

Serverless Devs

Serverless 云原生 SaaS

科技赋能传统产业:工业绿色可视化—核电站工艺流程组态仿真

一只数据鲸鱼

物联网 数据可视化 组态软件 绿色工业 核电站

开发复杂业务系统,有哪些设计思路

邴越

Android开发时的多点触控是如何实现的?

博文视点Broadview

Java 异常处理

大海

Java java异常处理

僵尸进程的成因以及僵尸可以被“杀死”吗?

AI乔治

Java 架构 进程

夜莺二次开发指南-任务执行中心

ning

滴滴夜莺 夜莺监控

Socket粘包问题终极解决方案—Netty版(2W字)!

王磊

Java socket Netty

实时媒体AI,打破内容创作天花板,加速视频创新

华为云开发者联盟

人工智能 云原生 媒体 视频

Redis 学习笔记 02:链表

架构精进之路

redis 七日更 28天写作

APICloud的发展和应用

anyRTC开发者

ios android 跨平台 sdk APICloud

调查bug的手段有哪些?(没有调查,就没有发言权,二)Jan 13, 2021

王泰

28天写作

智能合约上链系统开发|智能合约上链APP软件开发

系统开发

港股配资系统搭建

软件开发大鱼V15988750073

金融科技 港股交易系统开发 在线开户系统 CFD交易系统 港股多账户系统

跨越全场景统一架构三大挑战,MindSpore亮出“四招”

华为云开发者联盟

深度学习 联邦学习 mindspore 算子 ai框架

用Rust写点啥:数据结构篇——单向链表

Kurtis Moxley

数据结构 rust

大型企业引进低代码开发技术是大趋势

Sam678678

进来抄作业:分布式系统中保证高可用性的常用经验

华为云开发者联盟

高可用 运维 设计 分布式系统 系统

Rego 不好用?用 Pipy 实现 OPA_大数据_InfoQ精选文章