常见服务网格开发简介

📅 发布时间:2026/7/15 10:56:11 👁️ 浏览次数:
常见服务网格开发简介
第一阶段环境准备 (安装 K8s 和 Istio)假设你有一个可用的 K8s 集群 (v1.24)。1. 安装 Istio使用istioctl进行安装并启用 sidecar 自动注入。# 1. 下载 istioctl (请根据实际版本调整这里以 1.20 为例) curl -L https://istio.io/downloadIstio | ISTIO_VERSION1.20.0 sh - cd istio-1.20.0 export PATH$PWD/bin:$PATH # 2. 安装 Istio (使用 demo profile生产环境请用 default 或 custom) istioctl install --set profiledemo -y # 3. 验证安装 kubectl get pods -n istio-system # 确保 istiod, istio-ingressgateway 等状态为 Running # 4. 启用命名空间的自动注入 kubectl label namespace default istio-injectionenabled第二阶段开发 Java 边车服务 (基于 Spring Boot)这个服务的角色是Envoy 的外部授权服务 (External Authorization Service)。当流量进入 Pod 时Envoy 会先暂停请求发送一个检查请求 (CheckRequest) 给你的 Java 服务。你的服务解析出 URL、Header、Body (需注意 gRPC 限制HTTP body 通常需要特殊配置或仅记录 Header/Path若必须记录 Body 需开启 Envoy 的include_body_in_check选项)记录日志后返回“允许 (OK)”。1. 创建 Spring Boot 项目依赖Spring Web, Lombok, Slf4j。pom.xml关键依赖:xmldependencies !-- Web 支持用于提供 gRPC 或 HTTP 接口供 Envoy 调用 -- !-- 注意Istio ext_authz 默认使用 gRPC (Envoy Authz Service Protocol v3) -- !-- 为了简化演示我们使用 HTTP 模式 (Envoy 支持 HTTP 模式的 ext_authz) -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency !-- JSON 处理 -- dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId /dependency /dependencies2. 核心代码实现 (AuthzController.java)Istio Envoy 的 HTTP 模式外部授权会将请求属性封装成 JSON 发送给指定的 HTTP 服务。javapackage com.example.sidecar; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.Map; RestController RequestMapping(/) Slf4j public class AuthzController { private final ObjectMapper objectMapper new ObjectMapper(); /** * Istio Envoy HTTP External Authorization 接口 * Envoy 会 POST 一个 JSON 对象到这里 */ PostMapping(/check) public ResponseEntityMapString, Object check(RequestBody JsonNode requestJson) { try { // 1. 解析请求上下文 // 结构参考: https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto#service-auth-v3-httpauthorizationrequest JsonNode attributes requestJson.get(attributes); JsonNode request attributes.get(request); String path request.get(path).asText(); String method request.get(method).asText(); JsonNode headers request.get(headers); // 2. 尝试获取 Body (需要在 Envoy 配置中开启 include_body_in_check) String body ; if (request.has(body)) { body request.get(body).asText(); } // 3. 构建日志信息 MapString, String logData new HashMap(); logData.put(method, method); logData.put(path, path); logData.put(timestamp, String.valueOf(System.currentTimeMillis())); logData.put(body_preview, body.length() 200 ? body.substring(0, 200) : body); // 提取特定 Header (如 User-ID) if (headers.has(x-user-id)) { logData.put(user_id, headers.get(x-user-id).asText()); } // 4. 打印日志 (生产环境可发送到 Kafka/ES) log.info( SIDECAR INTERCEPTED REQUEST ); log.info(Path: {}, path); log.info(Method: {}, method); log.info(Body: {}, body); // 这里拿到了完整的入参 Body log.info(Headers: {}, headers.toString()); log.info(); // 5. 返回允许 (Status 200 OK) // Envoy 期望的响应格式 MapString, Object response new HashMap(); response.put(status, OK); // 如果需要添加 Header 传给后端可以放在 headers 字段此处略 return ResponseEntity.ok(response); } catch (Exception e) { log.error(Error processing authz request, e); // 出错时默认拒绝还是允许通常为了高可用选择允许但记录错误 MapString, Object errorResp new HashMap(); errorResp.put(status, OK); // Fail open return ResponseEntity.ok(errorResp); } } // 健康检查 GetMapping(/health) public String health() { return OK; } }重要配置 (application.yml):yamlserver: port: 8081 # 边车服务端口不要和主应用冲突 logging: level: com.example.sidecar: INFO关键点说明如何拿到 Body默认的 Envoyext_authz不发送 Body。你必须在 Istio 配置中显式开启。这会在后续 YAML 中体现 (includeBodyInCheck: true)。第三阶段构建 Docker 镜像创建Dockerfile:dockerfile编辑FROM eclipse-temurin:17-jre-alpine WORKDIR /app COPY target/sidecar-logger-0.0.1-SNAPSHOT.jar app.jar EXPOSE 8081 ENTRYPOINT [java, -jar, app.jar]构建并推送到镜像仓库bash编辑mvn clean package -DskipTests docker build -t your-registry/sidecar-logger:1.0 . docker push your-registry/sidecar-logger:1.0第四阶段K8s 部署与 Istio 配置 (核心步骤)我们需要部署一个包含主业务应用和Java 边车服务的 Pod并配置 Istio 让流量先经过边车。1. 编写deployment.yaml这个文件定义了一个 Pod里面有两个容器my-app: 你的主业务 (例如一个简单的 Hello World)。logger-sidecar: 刚才开发的 Java 日志服务。yamlapiVersion: apps/v1 kind: Deployment metadata: name: my-app-with-logger-sidecar # 部署名称 labels: app: my-app # 标签后面 Istio 要靠这个标签找到它 spec: replicas: 1 # 只启动 1 个副本 selector: matchLabels: app: my-app template: metadata: labels: app: my-app annotations: # 【关键注解】告诉 Istio请自动给这个 Pod 注入 istio-proxy (Envoy) 容器 sidecar.istio.io/inject: true spec: containers: # 容器 1主业务应用 - name: main-business-app image: nginx:alpine # 这里用 Nginx 模拟你的真实业务代码 ports: - containerPort: 80 # 主业务监听 80 端口 # 容器 2Java 边车服务 - name: java-logger-sidecar image: your-docker-repo/sidecar-logger:1.0 # 【替换】换成你构建好的 Java 镜像地址 ports: - containerPort: 8081 # 对应 Java 代码里的 server.port # 资源限制 (防止边车占用太多内存) resources: requests: memory: 128Mi cpu: 100m limits: memory: 256Mi cpu: 200m # 健康检查 (K8s 定期检查边车是否活着) livenessProbe: httpGet: path: /health port: 8081 initialDelaySeconds: 10 periodSeconds: 102. 编写AuthorizationPolicy和PeerAuthentication(配置 Envoy 拦截)这是最关键的一步。我们要告诉 Istio 的 Envoy“在所有请求进入my-app之前先调用localhost:8081/check进行授权检查”。创建文件ext-authz-policy.yaml:yamlapiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: enable-java-logging namespace: default # 和你的 Deployment 在同一个命名空间 spec: # 【选择器】只对带有 appmy-app 标签的 Pod 生效 selector: matchLabels: app: my-app # 【动作】CUSTOM 表示使用自定义的外部提供商 (而不是简单的允许/拒绝) action: CUSTOM rules: - to: - operation: # 拦截这些 HTTP 方法 (也可以写成 [*] 拦截所有) methods: [GET, POST, PUT, DELETE] providers: # 【关联】引用上面 operator 里定义的 java-logger-provider - name: java-logger-provider3. 配置MeshConfig扩展提供商 (ExtensionProvider)你需要将java-logger-provider注册到 Istio 的全局配置中。这通常通过修改IstioOperator或在istio-configmap中配置。方法 A使用 IstioOperator (推荐重启 istiod 生效)创建一个istio-operator.yaml:yaml编辑apiVersion: install.istio.io/v1alpha1 kind: IstioOperator metadata: namespace: istio-system spec: profile: demo meshConfig: # 【关键】定义扩展提供商 # 意思是我有一个叫 java-logger-provider 的外部服务专门做授权检查 extensionProviders: - name: java-logger-provider envoyExtAuthzHttp: # 服务地址格式是 服务名.命名空间.svc.cluster.local # 这里指向我们上面定义的 Service 名字 (见后面的 service.yaml) service: my-app-with-logger-sidecar.default.svc.cluster.local port: 8081 # Java 边车的端口 pathPrefix: /check # 调用的接口路径对应 Java 里的 PostMapping(/check) # 【关键功能】包含哪些 Header 到检查请求中 includeHeadersInCheck: [x-user-id, content-type, authorization] # 【最关键功能】允许发送 Body (请求参数) 给 Java 边车 # 默认是 false (不送 Body)必须设为 true 才能拿到入参 includeBodyInCheck: true # 最大发送多少字节的 Body (默认 8KB)如果参数很大可以调大 maxRequestBytes: 8192 # 如果 Java 边车挂了或超时该怎么办 # Code_OK 放行 (Fail Open推荐保证业务可用) # Code_Forbidden 拒绝 (Fail Closed更安全但风险大) statusOnError: Code_OK应用配置bashistioctl install -f istio-operator.yaml -y注意这会重启 istiod导致短暂的控制平面不可用请在测试环境操作。方法 B如果不方便重启 istiod (使用 Telemetry API 替代方案)如果无法修改 MeshConfig可以使用TelemetryAPI 将 Access Log 发送到 HTTP 服务但这通常只能拿到访问日志格式的数据不如ext_authz灵活且能拿到完整 Body 结构。鉴于你要“拿到请求入参”必须坚持使用ext_authzincludeBodyInCheck。4. 暴露服务 (Service Gateway)为了让外部流量进来触发拦截yamlapiVersion: v1 kind: Service metadata: name: my-app-with-sidecar spec: selector: app: my-app ports: - name: http port: 80 targetPort: 80 --- apiVersion: networking.istio.io/v1beta1 kind: Gateway metadata: name: my-app-gateway spec: selector: istio: ingressgateway servers: - port: number: 80 name: http protocol: HTTP hosts: - * --- apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: my-app-vs spec: hosts: - * gateways: - my-app-gateway http: - match: - uri: prefix: / route: - destination: host: my-app-with-sidecar port: number: 80第五阶段部署与验证1. 部署所有资源bashkubectl apply -f deployment.yaml kubectl apply -f ext-authz-policy.yaml kubectl apply -f service-gateway.yaml # 确保 IstioOperator 已应用并重启了 istiod部署完成会看见一个pod下存在3个容器my-app-with-logger-sidecar-7b9f8c6d-x2z 3/3 Running 0 2m2. 验证流程检查 Pod 状态bashkubectl get pods # 应该看到 my-app-with-sidecar-xxx 中有 3 个容器 # 1. my-app (nginx) # 2. logger-sidecar (java) # 3. istio-proxy (envoy)发送测试请求bash# 获取 Ingress IP export INGRESS_HOST$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath{.status.loadBalancer.ingress[0].ip}) # 发送带 Body 的 POST 请求 curl -X POST http://$INGRESS_HOST/test-api \ -H Content-Type: application/json \ -H x-user-id: user-123 \ -d {username: zhangsan, password: secret123, action: login}查看 Java 边车日志bash# 找到 Pod 名称 POD_NAME$(kubectl get pod -l appmy-app -o jsonpath{.items[0].metadata.name}) # 查看 logger-sidecar 容器的日志 kubectl logs $POD_NAME -c logger-sidecar预期输出你应该能在日志中看到类似以下内容证明成功拦截并获取了入参 SIDECAR INTERCEPTED REQUEST Path: /test-api Method: POST Body: {username: zhangsan, password: secret123, action: login} Headers: {x-user-id:[user-123], ...} 同时curl 命令应该能正常收到 Nginx 的响应因为 Java 服务返回了 OK。常见问题与注意事项Body 大小限制includeBodyInCheck: true默认只传输前 8KB。如果请求体很大Java 服务拿到的会被截断。如需更大需在IstioOperator中调整maxRequestBytes。性能影响这种模式是同步阻塞的。每个请求都要Client - Envoy - Java Sidecar - Envoy - App。Java 服务必须非常快毫秒级。如果 Java 服务挂了或慢整个业务接口都会慢或不可用除非配置statusOnError: Code_OK进行熔断。优化建议在 Java 代码中接收请求后立即异步记录日志放入内存队列或直接返回 OK不要让日志写入磁盘/网络阻塞主线程返回。HTTPS 问题如果在 Mesh 内部启用了 mTLSEnvoy 到 Java Sidecar (localhost) 通常是明文 HTTP因为它们在同一个 Pod 网络命名空间内不受 mTLS 影响。替代方案对比如果发现性能损耗太大或者不需要严格的“独立进程”强烈建议将这段 Java 代码直接做成Jar 包依赖放入主应用中使用 Spring Boot 的OncePerRequestFilter。那样就没有网络开销且一定能拿到完整的 InputStream通过ContentCachingRequestWrapper。主应用内 Filter 获取 Body 的核心代码片段供参考java// 在主应用中 public class LogFilter extends OncePerRequestFilter { Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { ContentCachingRequestWrapper wrappedRequest new ContentCachingRequestWrapper(request); chain.doFilter(wrappedRequest, response); // 请求结束后再读取不影响主流程 byte[] content wrappedRequest.getContentAsByteArray(); String body new String(content, StandardCharsets.UTF_8); log.info(Intercepted Body: {}, body); } }总结本方案严格按照你的要求实现了一个独立的 Java 边车服务利用Istio External Authorization (HTTP 模式)机制在流量进入主应用前进行拦截成功获取了HTTP Method, Path, Headers 以及 Request Body并进行了日志记录。下一步操作清单编写 Java 代码并打包镜像。配置IstioOperator开启includeBodyInCheck。部署Deployment(含双容器) 和AuthorizationPolicy。通过curl测试并观察kubectl logs。