Istio Ingress 针对指定 Host 设定 HTTP Basic 认证

针对一些没有认证又需要开放到公网的服务,可以在 Istio Ingress 的请求过程中增加一个 Lua 脚本来进行简单的认证流程。

下面的 Basic xxxx 替换成实际的用户名密码(base64(username:password)),host:find(“xxxx.xxx.com”) 替换成实际需要增加认证的 Host 域名:

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: istio-basic-auth-lua
  namespace: istio-system
spec:
  workloadSelector:
    labels:
      istio: ingressgateway
  configPatches:
  - applyTo: HTTP_FILTER
    match:
      context: ANY
    patch:
      operation: INSERT_BEFORE
      value:
        name: envoy.filters.http.lua
        typed_config:
          "@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua"
          inline_code: |
            function envoy_on_request(request_handle)
              local host = request_handle:headers():get("host")
              if host and host:find("xxxx.xxx.com") then
                local auth = request_handle:headers():get("authorization")
                local expected_auth = "Basic xxxx"
                if auth == nil or auth ~= expected_auth then
                  request_handle:respond(
                    {
                      [":status"] = "401",
                      ["www-authenticate"] = 'Basic realm="Restricted"'
                    },
                    "Unauthorized"
                  )
                end
              end
            end

VPS 全端口转发

在 /etc/sysctl.conf 中添加:

net.ipv4.ip_forward = 1

之后执行:

sysctl -p

假设 VPS 公网 IP 为 A,目标 VPS 公网 IP 为 B:

iptables -t nat -A PREROUTING -p tcp -d A -m tcp ! --dport 22 -j DNAT --to-destination B
iptables -t nat -A POSTROUTING -p tcp -d B -j MASQUERADE
iptables -t nat -A PREROUTING -p udp -d A -j DNAT --to-destination B
iptables -t nat -A POSTROUTING -p udp -d B -j MASQUERADE

这样可以把除 TCP 22 端口的请求权端口转发到 B。

Helm repo 使用 Github 仓库

使用 Github Private Token 即可,配合 raw 读取路径:

helm repo add --username xxx --password xxx private-repo 'https://raw.githubusercontent.com/xxx/xxx-charts/master'

Flux 创建 github ssh Secret 方式

flux create secret git aurora-admin-charts-auth --url=ssh://git@github.com/doraemonext/aurora-admin-charts --private-key-file=/home/doraemonext/.ssh/id_rsa

可以看到:

git secret ‘aurora-admin-charts-auth’ created in ‘flux-system’ namespace

接下来创建 GitRepository:

apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: aurora-admin-charts
  namespace: flux-system
spec:
  interval: 1m
  url: ssh://git@github.com/doraemonext/aurora-admin-charts
  ref:
    branch: master
  secretRef:
    name: aurora-admin-charts-auth

Kubernetes 支持 NFS 作为 StorageClass

helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
    --set nfs.server=x.x.x.x \
    --set nfs.path=/exported/path

Ubuntu 24.04 安装 NFS Server

sudo apt update && sudo apt upgrade -y
sudo apt install nfs-kernel-server -y
sudo mkdir -p /var/nfs_share  sudo chmod 777 /var/nfs_share
sudo emacs /etc/exports

编辑文件:

/var/nfs_share 192.168.0.0/24(rw,sync,no_subtree_check,no_root_squash)

然后执行:

sudo exportfs -a 
sudo exportfs -v 

其他客户端可以使用以下命令来挂载:

sudo mount 192.168.0.150:/var/nfs_share /mnt

或者添加到 /etc/fstab 中:

192.168.0.150:/var/nfs_share /mnt/ nfs defaults 0 0

ArgoCD 安装 & Istio 集成

按官方手册安装:

kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

然后下载 argocd CLI:

wget https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
chmod +x argocd-linux-amd64
sudo mv argocd-linux-amd64 /usr/local/bin/argocd

修改 argocd server 启动为 –insecure 模式:

curl -kLs -o install.yaml https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

新建 kustomization.yml 文件:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ./install.yaml

patches:
- path: ./patch.yml

新建 patch.yml 文件:

# Use --insecure so Ingress can send traffic with HTTP
# --bashref /argocd is the subpath like https://IP/argocd
# env was added because of https://github.com/argoproj/argo-cd/issues/3572 error
---
apiVersion: apps/v1
kind: Deployment
metadata:
 name: argocd-server
spec:
 template:
   spec:
     containers:
     - args:
       - /usr/local/bin/argocd-server
       - --staticassets
       - /shared/app
       - --redis
       - argocd-redis:6379
       - --insecure
       name: argocd-server
       env:
       - name: ARGOCD_MAX_CONCURRENT_LOGIN_REQUESTS_COUNT
         value: "0"

然后在当前目录执行:

kubectl apply -k ./ -n argocd --wait=true

接下来和 istio 进行集成并申请对应的证书:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: argocd-doraemonext-net-cert-prod
  namespace: istio-system
spec:
  dnsNames:
  - argocd.doraemonext.net
  duration: 2160h0m0s
  issuerRef:
    group: cert-manager.io
    kind: ClusterIssuer
    name: letsencrypt
  privateKey:
    algorithm: RSA
    encoding: PKCS1
    size: 2048
  renewBefore: 360h0m0s
  secretName: argocd-doraemonext-net-cert-prod
  usages:
  - server auth
  - client auth
---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: argocd-doraemonext-net-gateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "argocd.doraemonext.net"
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: argocd-doraemonext-net-cert-prod
    hosts:
    - "argocd.doraemonext.net"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: argocd-doraemonext-net-vs
  namespace: istio-system
spec:
  hosts:
  - "argocd.doraemonext.net"
  gateways:
  - argocd-doraemonext-net-gateway
  http:
  - match:
    - port: 80
    redirect:
      authority: "argocd.doraemonext.net:443"
      scheme: https
  - match:
    - port: 443
    route:
    - destination:
        host: argocd-server.argocd.svc.cluster.local
        port:
          number: 80

最后通过 argocd CLI 来初始化密码:

argocd admin initial-password -n argocd

用初始化的密码登录即可。

QPS/RT/并发的关系

取自:https://wx.zsxq.com/group/15552551584552/topic/814811112852482

一个请求如果需要消耗 10ms,但是其中 9 毫秒是 CPU 调用查 DB,实际这个请求需要消耗本机 CPU 1ms,也就是单核 QPS = 1000ms/1ms=1000,8 核机器理论支撑最大 QPS 为 8000。

如果用单线程去压这个服务,RT(Reaction Time)是 10ms(1ms+9ms DB),那么 QPS 是 100,但是其中 CPU 有 90% 的时间在等待 DB 返回,相当于 90% 的时间在休息,那么需要用 10 个并发一起来压,刚好填满休息的 9ms,这样让 CPU 跑到 100%,RT 还是 10ms,但是 QPS 会提升到 1000。

如果 10 个并发,QPS 提升到 1000 后,继续加并发到 20,因为 CPU 负载已经 100% 了,你可以理解这个时候前 10 个并发跑着的时候,后面 10 个并发都要等 10ms,然后再轮到自己,所以 RT=10ms(等待CPU调度时间)+1ms(CPU 实际处理时间)+9ms(DB 处理时间)=20ms,QPS 仍然是 1000。

几个扩展场景:

场景:

还是上面的例子,如果 9ms 访问 DB 改成需要访问两次 DB,分别为 4ms、5ms,那么我们可以将代码重构,两次 DB 访问不用串行改成并行(业务允许这么做),那么总 RT=1ms(本机 CPU 耗时)+max(4,5)ms = 6ms。RT 从 10ms 优化到了 6ms,减少了 40%,是个很了不起的改进,如果还是用 10 并发来压那么总 QPS 变了吗?

没有。CPU 依然只能够执行 1000 个请求每秒,因为每个请求需要 1ms 的 CPU 时间,这是硬瓶颈。并且如果未来进一步提高并发,QPS 不会变化,RT 会继续增大。

最简单计算 CPU time 和 CPU wait time 的方法

一个并发跑的时候,如果 RT=1000ms,一次业务请求调用了 MySQL 10 次,每次 10ms 总共 100ms;

调用了服务 B 2 次分别为 100/400ms,2 次总共 500ms,那么对于这个业务 CPU time=1000(总RT)-100(MySQL)-500(服务B)=400ms,wait time=1000-400=600ms

Kubernetes 1.28 升级到最新版本

之前自己搭的集群是 1.28 版本,有点太老了,逐步升级到最新版本。本文章以升级到 1.29.13 为目标版本为例。

操作流程如下。

更新 sources 并升级 kubeadm

所有节点都需要执行下面的命令:

sudo rm -rf /etc/apt/sources.list.d/kubernetes.list
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt update
sudo apt-mark unhold kubeadm
sudo apt install -y kubeadm='1.29.13-*'
sudo apt-mark hold kubeadm
sudo reboot

controlplane 节点执行 kubeadm 升级

sudo kubeadm upgrade plan

确认输出无误,我这里的输出是:

[upgrade/config] Making sure the configuration is correct:
[upgrade/config] Reading configuration from the cluster...
[upgrade/config] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[preflight] Running pre-flight checks.
[upgrade] Running cluster health checks
[upgrade] Fetching available versions to upgrade to
[upgrade/versions] Cluster version: v1.28.11
[upgrade/versions] kubeadm version: v1.29.13
I0129 06:03:58.136307    2963 version.go:256] remote version is much newer: v1.32.1; falling back to: stable-1.29
[upgrade/versions] Target version: v1.29.13
[upgrade/versions] Latest version in the v1.28 series: v1.28.15

Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':
COMPONENT   CURRENT       TARGET
kubelet     4 x v1.28.2   v1.28.15

Upgrade to the latest version in the v1.28 series:

COMPONENT                 CURRENT    TARGET
kube-apiserver            v1.28.11   v1.28.15
kube-controller-manager   v1.28.11   v1.28.15
kube-scheduler            v1.28.11   v1.28.15
kube-proxy                v1.28.11   v1.28.15
CoreDNS                   v1.10.1    v1.11.1
etcd                      3.5.9-0    3.5.16-0

You can now apply the upgrade by executing the following command:

	kubeadm upgrade apply v1.28.15

_____________________________________________________________________

Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':
COMPONENT   CURRENT       TARGET
kubelet     4 x v1.28.2   v1.29.13

Upgrade to the latest stable version:

COMPONENT                 CURRENT    TARGET
kube-apiserver            v1.28.11   v1.29.13
kube-controller-manager   v1.28.11   v1.29.13
kube-scheduler            v1.28.11   v1.29.13
kube-proxy                v1.28.11   v1.29.13
CoreDNS                   v1.10.1    v1.11.1
etcd                      3.5.9-0    3.5.16-0

You can now apply the upgrade by executing the following command:

	kubeadm upgrade apply v1.29.13

_____________________________________________________________________


The table below shows the current state of component configs as understood by this version of kubeadm.
Configs that have a "yes" mark in the "MANUAL UPGRADE REQUIRED" column require manual config upgrade or
resetting to kubeadm defaults before a successful upgrade can be performed. The version to manually
upgrade to is denoted in the "PREFERRED VERSION" column.

API GROUP                 CURRENT VERSION   PREFERRED VERSION   MANUAL UPGRADE REQUIRED
kubeproxy.config.k8s.io   v1alpha1          v1alpha1            no
kubelet.config.k8s.io     v1beta1           v1beta1             no
_____________________________________________________________________

确认无误后执行:

sudo kubeadm upgrade apply v1.29.13

如果存在其他的节点,那么在其他节点上执行:

sudo kubeadm upgrade node

升级 kubelet 和 kubectl

sudo apt-mark unhold kubelet kubectl && \
sudo apt-get update && sudo apt-get install -y kubelet='1.29.13-*' kubectl='1.29.13-*' && \
sudo apt-mark hold kubelet kubectl
sudo systemctl daemon-reload
sudo systemctl restart kubelet

LangGraph 构建 Workflow & Agent

LangGraph 的优势

  • 持久化层提供了中断和 Action 动作支持(比如在 Loop 中允许人工操控);
  • 持久化层提供了对话级别的记忆和长时间记忆的能力;
  • 提供了不同的方式去流式输出 workflow/agent 的输出以及当前的中间状态;
  • 提供了丰富的部署、可观测、评价体系支持;

Workflow & Agent 的区别

Workflow & Agent 区别:

  • Workflow 是通过预定义的代码路径来协调 LLM & Tools 的系统;
  • Agent 是通过 LLM 动态的指导自身的运行过程和 Tool 的使用,最终达成完成任务的目标;

Prompt chaining

通过将任务分解为一系列的步骤,每个 LLM 调用处理前一个 LLM 的输出。中间也可以通过添加 Gate(langgraph 中的 conditional_edge)来进行分支处理。

适用场景:任务可以清晰的分解的情况。

Parallelization

在 Prompt chaining 的基础上,可以同时执行多个 LLM 调用或者 Tool 调用来提高效率。

适用场景:需要同时处理大量数据或任务的情况。

Routing

可以根据用户 input 的特征,将任务分配给最适合的 LLM 或者 Tool。

适用场景:需要根据不同的输入类型采取不同的处理方式。

Orchestrator-Worker

在 Orchestrator-Worker 类型中,一个 Orchestrator LLM 负责分配任务,多个 Worker LLM 或者 Tool 执行具体任务。

适用场景:需要集中控制和分布式执行复杂任务。

Evaluator-optimizer

在 Evaluator-optimizer 架构中,一个 LLM 生成输出,另外一个 LLM 或者 Tool 评估并优化该输出(or 拒绝)。

适用场景:对于需要高质量输出且需要反复改进的任务场景。

Agent

在智能体的场景下,LLM 动态的指导其自身的过程和 Tool 的使用,控制任务的完成方式。

适用场景:需要高度灵活性和自主性的任务。

原始文章:https://langchain-ai.github.io/langgraph/tutorials/workflows/