阿里、蚂蚁、晟腾、中科加禾精彩分享 AI 基础设施洞见,现购票可享受 9 折优惠 |AICon 了解详情
写点什么

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:582105

评论

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

如何保证Redis与数据库的双写一致性?,十分钟带你看懂Netty如何实现C-S

Java 程序员 后端

如何在今年难找工作的大环境下成功入职阿里?Java架构师面试高频300题:集合

Java 程序员 后端

如何让自己像打王者荣耀一样发了疯、拼了命、石乐志的学习

Java 程序员 后端

字节跳动Java开放岗面经:14天快速面试,已拿offer,Java全套百度云

Java 程序员 后端

字节跳动一年一更的400多页算法刷题宝典已更新,力扣官网沸腾

Java 程序员 后端

字节首席架构师整合面试痛点,成就399页Java框架核心宝典

Java 程序员 后端

太难了,救救孩子吧,到现在还搞不懂TCP的三次握手四次挥手

Java 程序员 后端

字节跳动,三面我败了!但是我把经验记录了下来,java编程思想第六版百度云

Java 程序员 后端

学生管理系统(SSM简易版)总结,斗鱼Java开发二面被刷

Java 程序员 后端

如何在Spring Boot应用中优雅的使用Date和LocalDateTime

Java 程序员 后端

如何让阿三 Windows 10、11 的恢复分区(Recovery Partition

Java 程序员 后端

太狠了,Spring全家桶笔记,一站式通关全攻略,已入职某厂涨薪18K

Java 程序员 后端

如何使用Spring Cloud Consul的其他配置和发现功能,不会来学

Java 程序员 后端

如何阅读一本书-读书笔记,java二到三年经验面试题

Java 程序员 后端

学会RabbitMQ代理的连接,是一种怎样的体验?,mongodb教程

Java 程序员 后端

头条「2020最新」Spring最易学习教程,百度java面试经验

Java 程序员 后端

模块二作业

doublechun

「架构实战营」

华为云专家向宇:工欲善其事必先利其器,才能做数据的“管家”

华为云数据库小助手

GaussDB GaussDB(for Influx) 华为云数据库 华为云数据库创新Lab

太狠了!阿里技术官熬夜半年肝出来的Spring Boot巅峰之作,爱了

Java 程序员 后端

如何保证高可用?搞定三种集群模式,Redis还不是手到擒来

Java 程序员 后端

如果当时这15道题能答好,现在应该已经被录取了(记一次面试的亲身经历 2020-7-20

Java 程序员 后端

如果当时这16道题能答好,现在应该已经被录取了(记一次面试的亲身经历 2020-9-9

Java 程序员 后端

学IT的人太多了,现在入行还有出路吗?,linux环境高级编程

Java 程序员 后端

学习高并发的前置知识——Java中的线程基础,springcloud实战演练

Java 程序员 后端

学弟学妹们请不要错过自己的“黄金奋斗三年”,java实战项目代码

Java 程序员 后端

太难了,面试官不讲武德!来骗来偷袭,分层架构图案例

Java 程序员 后端

如何快速成长为技术大牛?阿里资深技术专家的总结亮了!

Java 程序员 后端

如何正确使用Spring Cloud Zookeeper,不懂来学,java教程下载网盘

Java 程序员 后端

如何理解互斥锁、条件锁、读写锁以及自旋锁(1),mysql入门到精通电子书

Java 程序员 后端

如何设计一个百万级用户的抽奖系统?,三面蚂蚁核心金融部

Java 程序员 后端

如何在分布式系统中正确的使用缓存?别给你的项目引入定时炸弹!

Java 程序员 后端

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