跳到主要内容

Envoy 配置及策略

· 阅读需 8 分钟

Envoy 的配置使用

https://www.envoyproxy.io/docs

# 启动一个 nginx 环境
docker pull nginx
docker run -dit --name=web nginx
docker exec -it web bash
/# echo 111 > /usr/share/nginx/html/index.html
/# exit
docker inspect web | grep -i ipaddress
# 172.17.0.2

# 启动 envoy
docker pull envoyproxy/envoy:latest

# 先将下面的 envoy 配置文件写了再启动
docker run -d -p 10000:10000 -v /root/envoy.yaml:/etc/envoy/envoy.yaml --name myenvoy envoyproxy/envoy:latest
# 关键是在于写 envoy 的配置文件 envoy.yaml
docker logs myenvoy

#修改hosts
vim /etc/hosts
192.168.26.23<本级IP> bb.yuan.cc bb

# 客户端测试, envoy设置了: domains: [bb.yuan.cc] #只转发此条
curl bb.yuan.cc bb:10000
#没有返回
curl -H "Host: bb.yuan.cc" bb.yuan.cc bb:10000
#返回111

Envoy配置文件结构

listen -- 监听器
1.我监听的地址
2.过滤链
filter1
路由: 转发到哪里
virtual_hosts
只转发什么
转发到哪里 --> 由后面的 cluster 来定义
filter2
filter3
#envoyproxy.io/docs/envoy/v1.19.1/api-v3/config/filter/filter
cluster
转发规则
endpoints
--指定了我的后端地址

vim envoy.yaml 比较老旧的写法了

admin:
access_log_path: /tmp/admin_access.log
address:
socket_address: { address: 127.0.0.1, port_value: 9901 } #定义管理

static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 0.0.0.0, port_value: 10000 } #要监听的地址
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager #指明使用哪个过滤器,这个是做普通的转发
#typed_config:
# "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
# extensions.filters.network.http_connection_manager.v3.HttpConnectionManager 为具体值,type.googleapis.com/envoy为固定前缀
config:
stat_prefix: ingress_http
codec_type: AUTO #都允许
#codec_type: HTTP2 #只允许HTTP2协议访问,(如果是访问nginx容器,要先开启nginx的http2)
#access_log: #添加访问日志
#- name: envoy.file_access_log
# config:
# path: "/tmp/aa.log"
route_config:
name: myroute1
virtual_hosts:
- name: local_service
domains: ["*"] #都允许
#domains: [bb.yuan.cc] #只转发此条
routes:
- match: { prefix: "/" }
#- match: { prefix: "/demo1" } #只匹配 相对目录 的 demo1 目录
route: { cluster: some_service } #可以自定义名字
#- name: local_service2
# domains: ["cc.yuan.cc"]
# routes:
# - match: { prefix: "/" }
# route: { cluster: some_service2 }
http_filters:
#- name: envoy.router #旧的写法,快被废弃了
- name: envoy.filters.http.router
clusters:
- name: some_service #与上面route对应
connect_timeout: 0.25s
type: STATIC
#type: LOGICAL_DNS
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
hosts: [ #指定后端地址
#{ socket_address: { address: 192.168.26.23, port_value: 808 }},
{ socket_address: { address: 172.17.0.2, port_value: 80 }}

]
#- name: some_service2 #与上面route对应
# connect_timeout: 0.25s
# type: STATIC
# dns_lookup_family: V4_ONLY
# lb_policy: ROUND_ROBIN
# hosts: [
# { socket_address: { address: 172.17.0.4, port_value: 80 }}
# ]

Filter的写法语法1

name: 指定使用哪个过滤器

config:

​ 参数1:值1

​ 参数2:值2

​ 。。。

这里选择什么参数,要看name里选择的什么参数要根据所选择的过滤器来判定

和http相关的,一般选择 HTTP connection manager。

https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/filter/filter里找参数

name的位置应该写envoy.filters.network.http_connection_manager

http_connection_manager 常见的参数包括:

codec_type -- 可选值为AUTO(默认)、HTTP1、

HTTP2

stat_prefix

route_config

http_filters

access_log

Filter的写法语法2

name: 指定使用哪个过滤器

typed_config:

​ "@type": type.googleapis.com/envoy.过滤器的具体值

​ 参数1:值1

​ 参数2:值2

​ 。。。

这里选择什么参数,要看name里选择的什么参数要根据所选择的过滤器来判定

和http相关的,一般选择 HTTP connection manager。

https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/filter/filter 里找参数

name的位置应该写envoy.filters.network.http_connection_manager

@type的值到文档里找具体的值

vim envoy.yaml 现在的写法有所改动

admin:
access_log_path: /tmp/admin_access.log
address:
socket_address: { address: 127.0.0.1, port_value: 9901 } #定义管理

static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 0.0.0.0, port_value: 10000 } #要监听的地址
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager #指明使用哪个过滤器,这个是做普通的转发
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
# extensions.filters.network.http_connection_manager.v3.HttpConnectionManager 为具体值,type.googleapis.com/envoy为固定前缀
#config:
stat_prefix: ingress_http
codec_type: AUTO #都允许
route_config:
name: myroute1
virtual_hosts:
- name: local_service
domains: ["*"] #都允许
#domains: [bb.yuan.cc] #只转发此条
routes:
- match: { prefix: "/" }
route: { cluster: some_service }
http_filters:
- name: envoy.filters.http.router
clusters:
- name: some_service #与上面route对应
connect_timeout: 0.25s
type: STATIC
#type: LOGICAL_DNS
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
#hosts: [
# { socket_address: { address: 172.17.0.2, port_value: 80 }}
#]
load_assignment:
cluster_name: some_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 172.17.0.2
port_value: 80
#- endpoint:
# address:
# socket_address:
# address: 172.17.0.4
# port_value: 80

使用 lua

vim envoy.yaml

admin:
access_log_path: /tmp/admin_access.log
address:
socket_address: { address: 127.0.0.1, port_value: 9901 } #定义管理
static_resources:
listeners:
- name: main
address:
socket_address:
address: 0.0.0.0 #要监听的地址
port_value: 10000
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager #指明使用哪个过滤器,这个是做普通的转发
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
# extensions.filters.network.http_connection_manager.v3.HttpConnectionManager 为具体值,type.googleapis.com/envoy为固定前缀
#config:
stat_prefix: ingress_http
codec_type: AUTO #都允许
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains:
- "*"
routes:
- match:
prefix: "/"
route:
cluster: some_service
http_filters:
- name: envoy.filters.http.lua #修改为 lua
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
#inline_code:
inlineCode: |
function envoy_on_response(response_handle)
response_handle:headers():add("X-User-Header","===X====")
end
- name: envoy.filters.http.router
typed_config: {}
clusters:
- name: some_service #与上面route对应
connect_timeout: 0.25s
type: STATIC_DNS #static
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: some_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 172.17.0.2
port_value: 80

EnvoyFilter

EnvoyFilter用来自定义 Istio Pilot 生成的 Envoy 配置。使用 EnvoyFilter 可以修改某些

envoy的字段,添加特定的过滤器,甚至添加全新的监听器、集群等。必须谨慎使用此功能

docker ps | grep pod1
#k8s_istio_proxy_pod1_ns1 #sidecar
#k8s_pod1_pod1_ns1 #业务容器,nginx

docker top 713f7358dbf8 #sidecar 的 conternerId
#运行了两个主要的进程
# /usr/local/bin/pilot-agent proxy sidecar ...
# /usr/local/bin/envoy -c etc/istio/proxy/envpy-rev0.json #envoy 的配置文件有两种,静态|动态

#查看 envoyFilter
kubectl get envoyFilter
#No resources found in ns1 namespace

workloadSelector: 用于指定作用在谁

configPatches:用于配置补丁,下面的属性包括3个大类

- apply_To: 应用到谁

match:指定匹配规则

patch:新的规则

applyTo: 给哪个位置打补丁,可用的值包括

​ INVALID

​ LISTENER #将补丁应用到监听器

​ FILTER_CHAIN #将补丁应用到过滤器链

​ NETWORK_FILTER #将补丁应用到网络过滤器链

​ HTTP_FILTER #将补丁应用到HTTP过滤器链

​ ROUTE_CONFIGURATION

​ VIRTUAL_HOST

​ HTTP_ROUTE

​ CLUSTER

match:

​ context: SIDECAT_OUTBOUNT/SIDECAR_INBOUNT/GATEWAY(弃用)

​ listener:

​ name: 格式为ip:端口

​ portNumber:

​ filterChain:

​ filter:

​ name: 这4行用于定义过滤器

​ 格式为 envoy.filters.network.http_connection_manager

patch:

​ operation: 操作的意思,对应值有

​ MERG

​ ADD

​ REMOVE

​ INSERT_BEFOR

​ INSERT_AFTER

value

举个例子

mkdir chap6 && cd chap6
kubectl get pods -n istio-system -l istio=ingressgateway
#istio-ingressgateway-5h354d356-jdmc4

vim ef1.yaml

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: lua-filter
namespace: istio-system #指定命名空间
spec:
workloadSelector:
labels:
istio: ingressgateway
configPatches:
- 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:
name: envoy.filters.http.lua #插入此过滤器
type_config:
"@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua"
inlineCode: |
function envoy_on_response(response_handle)
response_handle:headers():add("X-User-Header","===X===")
end

# 启动 envoyFilter
kubectl apply -f ef1.yaml
kubectl get envoyFilter -n istio-system
#lua-filter
curl -I aa.yuan.cc
# x-user-header: ===X=== #改变了标题的报文信息

-----------------------------------------------------------

kubectl delete -f ef1.yaml
vim ef2.yaml #阿里云提供的一个demo,用此作些修改

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: addheader-into-ingressgateway
namespace: istio-system #指定命名空间
spec:
workloadSelector:
# select by label in the same namespace
labels:
istio: ingressgateway
configPatches:
# The Envoy config you want to modify
- applyTo: HTTP_FILTER
match:
context: GATEWAY
proxy:
proxyVersion: '^1\.10.*' #改为自己 istio 的版本
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 #插入此过滤器
type_config:
"@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua"
inlineCode: |-
function envoy_on_response(response_handle)
function hasFrameAncestors(rh)
s = rh:headers():get("Content-Security-Policy");
delimiter = ";";
defined = false;
for match in (s..delimiter):gmatch("(.-)"..delimiter) do
match = match:gsub("%s+", "");
if match:sub(1,15)=="frame-ancestors" then
return true;
end
end
return false;
end
if not response_handle:headers():get("Content-Security-Policy") then
csp = "frame-ancestors none;";
response_handle:headers():add("Content-Security-Policy", csp);
elseif response_handle:headers():get("Content-Security-Policy") then
if not hasFrameAncestors(response_handle) then
csp = response_handle:headers():get("Content-Security-Policy");
csp = csp .. ";frame-ancestors none;";
response_handle:headers():replace("Content-Security-Policy", csp);
end
end
if not response_handle:headers():get("X-Frame-Options") then
response_handle:headers():add("X-Frame-Options", "deny");
end
if not response_handle:headers():get("X-XSS-Protection") then
response_handle:headers():add("X-XSS-Protection", "1; mode=block");
end
if not response_handle:headers():get("X-Content-Type-Options") then
response_handle:headers():add("X-Content-Type-Options", "nosniff");
end
if not response_handle:headers():get("Referrer-Policy") then
response_handle:headers():add("Referrer-Policy", "no-referrer");
end
if not response_handle:headers():get("X-Download-Options") then
response_handle:headers():add("X-Download-Options", "noopen");
end
if not response_handle:headers():get("X-DNS-Prefetch-Control") then
response_handle:headers():add("X-DNS-Prefetch-Control", "off");
end
if not response_handle:headers():get("Feature-Policy") then
response_handle:headers():add("Feature-Policy",
"camera 'none';"..
"microphone 'none';"..
"geolocation 'none';"..
"encrypted-media 'none';"..
"payment 'none';"..
"speaker 'none';"..
"usb 'none';")..
end
if response_handle:headers():get("X-Powered-By") then
response_handle:headers():remove("X-Powered-By");
end
end

# 应用 ef2
kubectl apply -f ef2.yaml
# 测试
curl -I aa.yuan.cc
# 加入了对应的信息

Client-go 源码结构及 kubeconfig 配置管理对象

· 阅读需 4 分钟

Client-go 源码结构及 kubeconfig 配置管理对象

Github: https://github.com/kubernetes/client-go

目录结构:

目录结构含义
.githubpull请求的模版
applyconfigurationsapplyconfigurations 提供了⽤于构造服务器端应⽤请求的应⽤配置的类型安全的 go
discovery提供DiscoveryClient发现客户端
dynamic提供DynamicClient动态客户端
examples此⽬录包含涵盖 client-go 的各种⽤例和功能的⽰例。
informers每种kubernetes资源的informer实现
kubernetes提供ClientSet客户端
kubernetes_test提供ClientSet客户端的测试
listers为每⼀个kubernetes资源提供Listers功能,该功能对Get和List请求提供只读的缓存数据
metadata包含关于client-go的元数据信息
pkg客户端认证及版本的包
plugin/pkg/client/auth认证插件包auth
rest提供RestClient客户端,对kubernetes API Server执⾏Rest API操作
restmapper映射器的⼀些⽅法
scale提供scaleclient客户端,⽤于扩容和缩容deployment、replicaset、replication controller等资源对象
testing⼀些测试⽂件
third_party/forked/golang⼀些第三⽅的库
tools提供常⽤⼯具,例如sharedinformer、Reflector、DealtFIFO及Indexers。提供Client查询和缓存机制,以减少向kube-apiserv
transport提供安全的TCP连接,⽀持HTTP STREAM,某些操作需要在客户端和容器之间传输⼆进制流,例如exec、attach等操作
util提供常⽤⽅法,例如workqueue⼯作队列,certificate证书管理等

kubeconfig 配置管理对象

kubeconfig ⽤于管理访问 kube-apiserver 的配置信息,同时也⽀持访问多 kube-apiserver 的配置管理,可以在不同的环境下管理不 同的 kube-apiserver 集群配置,不同的业务线也可以拥有不同的集群。 Kubernetes 的其他组件都使⽤ kubeconfig 配置信息来连接 kube-apiserver 组件,例如当 kubectl 访问 kube-apiserver 时,会默认加载 kubeconfig 配置信息。

kubeconfig 中存储了集群、⽤户、命名空间和⾝份验证等信息,在默认的情况下, kubeconfig 存放在 $HOME/.kube/config 路径下。

Kubeconfig 配置信息如下:

apiVersion: v1 
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM1ekNDQWMrZ0F3... - context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users: - name: kubernetes-admin
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURJVENDQWdtZ0F3SUJBZ...
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBL1VB...

kubeconfig 配置信息通常包含 3 个部分,分别介绍如下。

  • clusters :定义 Kubernetes 集群信息,例如 kube-apiserver 的服务地址及集群的证书信息等。

  • users :定义 Kubernetes 集群⽤户⾝份验证的客户端凭据,例如 client-certificate、client-key、token 及 username/password 等。

  • contexts :定义 Kubernetes 集群⽤户信息和命名空间等,⽤于将请求发送到指定的集群。 client-go 会读取 kubeconfig 配置信息并⽣成 config 对象,⽤于与 kube-apiserver 通信,代码⽰例如下:

package main 
import ( "
k8s.io/client-go/tools/clientcmd"
)
func main() {
config, err := clientcmd.BuildConfigFromFlags("", "/root/.kube/config")
if err != nil {
panic (err)
...

在上述代码中, clientcmd.BuildConfigFromFlags 函数会读取 kubeconfig 配置信息并实例化 rest.Config 对象。其中 kubeconfig 最核⼼ 的功能是管理多个访问 kube-apiserver 集群的配置信息,将多个配置信息合并( merge )成⼀份,在合并的过程中会解决多个配置 ⽂件字段冲突的问题。

该过程由 Load 函数完成,可分为两步:

  • 第1步,加载 kubeconfig 配置信息;

  • 第2步,合并多个 kubeconfig 配置信息。代码⽰例如下。

一、加载 kubeconfig 配置信息

vendor/k8s.io/client-go/tools/clientcmd/loader.go

⼆、合并多个 kubeconfig 配置信息

有两份 kubeconfig 配置信息,集群分别为 cow-clusterpig-cluster ,经过合并后,最终得到⼀份多集群的配置信息

vendor/k8s.io/client-go/tools/clientcmd/loader.go

//由于值会被覆盖,但映射值不会被覆盖,所以我们可以将⾮映射配置合并到映射配置之上,并获得我们所期望的值。 config := clientcmdapi.NewConfig()
mergo.Merge(config, mapConfig, mergo.WithOverride)
mergo.Merge(config, nonMapConfig, mergo.WithOverride)

mergo.MergeWithOverwrite 函数将 src 字段填充到 dst 结构中,私有字段除外,⾮空的 dst 字段将被覆盖。另外, dstsrc 必须拥 有有效的相同类型结构。合并过程举例如下:

src 结构: T {X: "two", Z: Z{A: "three", B: 4}} 
dst 结构: T {X: "One", Y:5, Z{A: "four", B: 6}}

merge后的结构: T {X: "two", Y:5, Z{A: "three", B: 4}}

Istio 架构介绍

· 阅读需 1 分钟

pilot-agent --- 运行在 sidecar 里,主要作用是:

  • 动态的生成 envoy 的设置
  • 启动 envoy 进程
  • 监控 envoy 进程
kubectl get pods -n istio-system -owide

docker top 8fj3n4df356f(istiod conterneId)
#/usr/local/bin/pilot-discovery #控制中心

Discovery Services:pilot-discovery,扮演服务注册中心、istio控制面到envoy之间的桥梁作用:

1)监控服务注册中心 (例如:k8s) 的服务注册情况。k8s下会监控service、endpoint、pod、node等资源信息。

2)监控 istio 控制面信息变化,在k8s下,会监控routerule、virtualservice、Gateway、EgressRule、

ServiceEntry等以k8s CRD形式存在的 istio 控制面配置信息。

3)将1)和2)的两类信息合并组合为envoy可以理解的xds配置信息,以gRPC协议提供给envoy

Agent : pilot-agent,生成envoy配置文件,管理envoy生命周期:

1)生成envoy的配置

2)启动envoy

3)监控和管理envoy的运行情况

Mixer :在 1.5 之后就被废弃了