关于云计算:Rego不好用用Pipy实现OPA

4次阅读

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

还不晓得 Pipy 是什么的同学能够看下 GitHub。

Pipy 是一个轻量级、高性能、高稳固、可编程的网络代理。Pipy 外围框架应用 C++ 开发,网络 IO 采纳 ASIO 库。Pipy 的可执行文件仅有 5M 左右,运行期的内存占用 10M 左右,因而 Pipy 非常适合做 Sidecar proxy。
Rego 不好用?用 Pipy 实现 OPA
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 pipy 
kubens pipy
kubectl 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 <<EOF
pipy({
  _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)的通信须要应用 TLS。配置 TLS,应用 openssl 创立证书颁发机构(certificate authority CA)和 OPA 的证书 / 秘钥对。

openssl genrsa -out ca.key 2048
openssl req -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_req
distinguished_name = req_distinguished_name
prompt = no
[req_distinguished_name]
CN = pipy.pipy.svc
[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = pipy.pipy.svc
EOF

留神 CNalt_names 必须与前面创立 Pipy service 的匹配。

openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -config server.conf
openssl 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: Service
apiVersion: v1
metadata:
  name: pipy
  namespace: pipy
spec:
  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/v1
kind: Deployment
metadata:
  labels:
    app: pipy
  namespace: pipy
  name: pipy
spec:
  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 NodePort
kubectl get svc pipy-port
minikube service --url pipy-node -n pipy
# 找到控制台端口

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

cat > webhook-configuration.yaml <<EOF
kind: ValidatingWebhookConfiguration
apiVersion: admissionregistration.k8s.io/v1beta1
metadata:
  name: pipy-validating-webhook
webhooks:
  - 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: pipy
EOF

生成的配置文件蕴含 CA 证书的 base64 编码,以便能够在 Kubernetes API 服务器和 OPA 之间建设 TLS 连贯。

kubectl apply -f webhook-configuration.yaml

测试

pod-bad-repo.yaml:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: web-server
  name: web-server
  namespace: default
spec:
  containers:
  - image: nginx:1.21.1
    name: web-server
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}
kubectl apply -f pod-bad-repo.yaml
Error 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: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: web-server
  name: web-server
  namespace: default
spec:
  containers:
  - image: 192.168.64.1:5000/nginx:latest
    name: web-server
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}
kubectl apply -f pod-bad-tag.yaml
Error 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: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: web-server
  name: web-server
  namespace: default
spec:
  containers:
  - image: 192.168.64.1:5000/nginx:1.21.1
    name: web-server
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}
kubectl apply -f pod-ok.yaml
pod/web-server created

总结

OPA 哪哪都好,惟一毛病就是其引进的 Rego 语言贬低了应用的门槛。而 Pipy 的规定是通过 JavaScrip 来编写的,前端的同学一样能够实现规定的编写。齐全代替可能夸大了一些,但的确在局部场景下能够代替 OPA。

玩到这里,你会发现有了规定,加上功能强大的过滤器(当初我喜爱叫他们 Hook 了),Pipy 的可玩性十分强。

比方 OPA: Kubernetes 准入控制策略 Top 5,比方 …。大胆的设想吧。

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

文章对立公布在公众号 云原生指北

正文完
 0