Istio使用Envoy配置网关


💤本文章是为了记录学习。

流程:

获取请求新增对应headers -->通过 VirtualService 管理流量 --> 使用helm配置

Istio是一个开源的服务网格,它提供了一些功能,比如流量管理、安全性、可观察性等,而Envoy是Istio的默认代理,它提供了流量管理和代理功能。在Istio中,您可以使用Envoy代理来配置网关,以实现对服务流量的管理和控制。

Istio 通过在业务容器之中注入 Sidecar 来实现对微服务集群中东西向流量的接管。而为了能在对外提供服务的同时,不向客户端暴露整个服务网格,Istio 则需要对接 API 网关来管理微服务集群的南北向流量。

镜像构建

在我们使用**istio EnvoyFilter**的 Lua 过滤器时,我们需要提前预装好自己需要的一些 Lua 功能模块,所以我们要针对官网的镜像加以调整新增我们需要的功能

FROM istio/proxyv2:1.15.2
MAINTAINER ycloud
RUN apt-get update && \
        apt-get install -y luarocks libluajit-5.1-dev && \
        luarocks install lua-cjson luasocket

Lua过滤器

通过 EnvoyLua 过滤器,处理 http请求内容,我们这里是通过,获取到body,并识别body中method字段,将method内容生成一个新的headers。来区分请求的服务,针对 method 把流量分发到对应的服务上。下面是EnvoyFilter的详细内容:

这里的 Filter 作用于整个istio-proxy。

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: envoyfilter
spec:
  workloadSelector:
    labels:
      app: istio-ingressgateway
  configPatches:
    # The first patch adds the lua filter to the listener/http connection manager
  - applyTo: HTTP_FILTER
    match:
      context: GATEWAY
      listener:
        filterChain:
          filter:
            name: "envoy.filters.network.http_connection_manager"
            subFilter:
              name: "envoy.filters.http.router"
    patch:
      operation: INSERT_BEFORE
      value: # lua filter specification
       name:  envoy.filters.http.lua
       typed_config:
          "@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua"
          inlineCode: |
              local string_lower = string.lower
              local string_find = string.find
              local url_unescape = require("socket.url").unescape
              local json_decode = require("cjson").decode
              local json_encode = require("cjson").encode
              local string_gmatch = string.gmatch
              
              local my_envoy_on_request
              
              local is_string_not_empty = function(s)
                  return s and type(s) == 'string' and s ~= ''
              end
              
              local base64_table =
                  'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' -- You will need this for encoding/decoding
              
              function base64_enc(data)
                  if not data then return end
                  return ((data:gsub('.', function(x)
                      local r, b = '', x:byte()
                      for i = 8, 1, -1 do
                          r = r .. (b % 2 ^ i - b % 2 ^ (i - 1) > 0 and '1' or '0')
                      end
                      return r;
                  end) .. '0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
                      if (#x < 6) then return '' end
                      local c = 0
                      for i = 1, 6 do c = c + (x:sub(i, i) == '1' and 2 ^ (6 - i) or 0) end
                      return base64_table:sub(c + 1, c + 1)
                  end) .. ({'', '==', '='})[#data % 3 + 1])
              end
              
              function write_error_log(str, ...)
                  if not str then return end
              
                  io.stderr:write(str, ...)
              end
              
              function envoy_on_request(request_handle)
                  local ok, error = pcall(my_envoy_on_request, request_handle)
                  if not ok then
                      local headers = request_handle:headers()
                      local headers_str = json_encode(headers)
                      request_handle:logErr('error happened: ' .. (error or 'nil') ..
                                                ', headers_str: ' .. headers_str)
                  end
              end
              
              my_envoy_on_request = function(request_handle)
                  local content_type
                  local headers = request_handle:headers()
                  if not headers then return end
              
                  local content_type = headers:get('content-type')
                  if not is_string_not_empty(content_type) then return end
                  -- print(content_type)
                  -- print('content-type: ' .. (base64_enc(content_type) or 'nil'))
                  -- write_error_log('content-type: ' .. (base64_enc(content_type) or 'nil'))
                  content_type = string_lower(content_type)
              
                  local is_urlencoded_request = false
                  local is_json_request = false
              
                  if string_find(content_type, 'application/x-www-form-urlencoded', 1, true) then
                      is_urlencoded_request = true
                  elseif string_find(content_type, 'application/json') then
                      is_json_request = true
                  end
              
                  local body = request_handle:body()
                  local request_body_data = body:getBytes(0, body:length())
                  request_handle:streamInfo():dynamicMetadata():set("envoy.lua",
                                                                    "requestBody",
                                                                    request_body_data)
                  local req_json_str = request_body_data
                  if is_urlencoded_request then
                      for key, value in string_gmatch(tostring(req_json_str),
                                                      "([^&=]+)=([^&=]+)") do
                          if key == 'jsonStr' then
                              req_json_str = url_unescape(value)
                              break
                          end
                      end
                  end
              
                  if not req_json_str then return end
              
                  local req_json_t = json_decode(req_json_str)
                  if req_json_t then
                      local req_method = req_json_t.method or req_json_t.reqMethod
                      if not req_method then return end
                      -- 必须重新从 request_handle 中获取 headers 设置
                      local headers = request_handle:headers()
                      headers:add("x-asm-prefer-tag", req_method)
                      request_handle:logWarn('req_method: ' .. (req_method or 'nil') ..
                                                 ', base64: ' ..
                                                 (base64_enc(req_method) or 'nil'))
                  end
              end
              

HttpBin验证

httpbin是一个开源的HTTP请求和响应服务,可以用于测试和调试HTTP客户端。它提供了一个简单的RESTful API,可以模拟各种HTTP请求和响应。

我们可以使用httpbin来模拟参数请求。部署httpbin案例:

---
apiVersion: v1
kind: Service
metadata:
  name: httpbin
  labels:
    app: httpbin
    service: httpbin
spec:
  ports:
  - name: http
    port: 8000
    targetPort: 80
  selector:
    app: httpbin
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
      version: v1
  template:
    metadata:
      labels:
        app: httpbin
        version: v1
    spec:
      containers:
      - image: docker.io/kennethreitz/httpbin
        imagePullPolicy: IfNotPresent
        name: httpbin
        ports:
        - containerPort: 80
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: httpbin
spec:
  gateways:
    - httpbin
  hosts:
    - gsair.gstrain.qa.17usoft.com
  http:
    - match:
        - uri:
           prefix: /post
      route:
        - destination:
            host: httpbin
            port:
              number: 8000
---
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: httpbin
spec:
  selector:
    istio: ingressgateway
  servers:
    - hosts:
        - gsair.gstrain.qa.17usoft.com
      port:
        name: http
        number: 80
        protocol: HTTP

部署好之后,查看服务

[root@ycloud envoy]# kubectl get -f httpbin.yaml 
NAME                     SECRETS   AGE
serviceaccount/httpbin   1         30d

NAME              TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
service/httpbin   NodePort   10.253.212.203   <none>        8000:40534/TCP   30d

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/httpbin   1/1     1            1           30d

在这里插入图片描述

可以看到请求头中新增了我们Lua中定义的headers

GatewayCenter

我们这里是通过helm来为我们构建流量管控规则,使我们更便利。

在我们服务上线的时候记得加上 DestinationRule 资源,用于子流量分发,后后期灰度金丝雀发布做预留准备。

{{- if .Values.gateway.enabled }}
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: {{ include "center-gatewaysvc.fullname" . }}
  labels:
    {{- include "center-gatewaysvc.labels" . | nindent 4 }}
spec:
  host: {{ include "center-gatewaysvc.fullname" . }}
  subsets:
    - labels:
        app.kubernetes.io/instance: {{ include "center-gatewaysvc.fullname" . }}
      name: {{ include "center-gatewaysvc.fullname" . }}
{{- end}}
---
{{- define "http-routes" -}}
{{- range $route := .Values.routes }}
  {{- range $preferTag := $route.preferTag }}
  - match:
      - headers:
          x-asm-prefer-tag:
            exact: {{ $preferTag }}
        uri:
          prefix: /gstrain
        ignoreUriCase: true
    rewrite:
      uri: /{{ $preferTag }}
    route:
      - destination:
          host: {{ $route.destination.host }}
          port:
            number: {{ $route.destination.port }}
          subset: {{ $route.destination.host }}
  {{- end}}
{{- end}}
{{- end}}
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: {{ include "center-gatewaysvc.fullname" . }}
  labels:
    {{- include "center-gatewaysvc.labels" . | nindent 4 }}
spec:
  gateways:
  - {{ include "center-gatewaysvc.fullname" . }}
  hosts:
  {{- toYaml .Values.gateway.hostnames | nindent 4 }}
  http:
  {{- template "http-routes" . }}
---

这里我们处理好之后,大致流程已经走通,后面有需要调整的地方进行处理

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力做一名技术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值