由于要做 K8S 的 sidecar 扩展,而其实际是由 Envoy 来实现的。要将 Envoy 部署为 sidecar,需要先安装 Istio。
$ wget https://raw.githubusercontent.com/istio/istio/master/release/downloadIstioCandidate.sh
$ sh downloadIstioCandidate.sh
完成下载后,将得到一个 istio-1.8.1
目录,将目录内的 bin
目录加入到环境变量 PATH
中。注意,此处应当是全局的环境变量,因此可以考虑将 istio-1.8.1
目录整体移往 /opt
,并且修改 /etc/profile
以加入环境变量。
然后执行命令安装 Istio,并开启自动注入:
$ istioctl install --set profile=demo -y
$ kubectl label namespace default istio-injection=enabled
部署完毕后,可以用以下命令来查看自动注入的启用情况:
$ kubectl get namespace -L istio-injection
按上一篇所说的,我们原本在 K8S 里有一个名为 sample-service
的 Pod,在注入开启后,我们需要重新部署之:
$ kubectl delete -f sample-service.yaml
$ kubectl create -f sample-service.yaml
完成后会发现,Pod 的 Ready 状态有所改变,在没有注入时,状态为 1/1
,而开启注入后,状态为 2/2
,此时即代表注入成功。
接着要打一个 Envoy 的镜象,在打镜象前,需要对 Envoy 作出配置,我们在这里简单的进行一个代理,将请求转发至 sample-service
。先通过 kubectl describe pod sample-service
可以得知 Pod 的 IP,然后作出如下配置:
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.http_connection_manager
config:
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match:
prefix: "/"
route:
cluster: sample-envoy-service
http_filters:
- name: envoy.router
clusters:
- name: sample-envoy-service
connect_timeout: 30s
type: STATIC
lb_policy: ROUND_ROBIN
hosts:
- socket_address:
address: 192.168.235.233
port_value: 9001
需要注意的是,这里的 admin 相关的端口必须配置,因为内带了一个管理员工具,我们会需要访问到它。
然后写一个 Dockerfile 就可以打包镜象了:
FROM envoyproxy/envoy-dev:e98e41a8e168af7acae8079fc0cd68155f699aa3
COPY envoy.yaml /etc/envoy/envoy.yaml
$ sudo docker build -t envoy:v1 .
打完镜象后,将它运行起来即可:
$ sudo docker run -it -d --name envoy -p 9901:9901 -p 10000:10000 envoy:v1
完成后即可对 10000 端口发出请求:
$ curl 'http://0.0.0.0:10000'
即可以看到我们原本部署于 9001 端口上的内容了,即告代理成功。
对于外部的访问,也可以在浏览器下使用以下 URL:
http://10.211.55.16:10000
这样我们就不再需要开启 32001 端口来供外部访问了。
踩坑
第一次部署好 sample-service
服务后,跑起 Envoy 并在外部浏览器请求时,会出现一个异常,upstream connect error or disconnect/reset before headers. reset reason: connection termination
,初看之下,就是请求在 header 被处理之前就挂了。那就要去查一下是否 header 里面有 Envoy 不支持的东西了。
经过排查,发现是我在 Ktor 内开启了 Compress 插件,并且未把 Accept-Encoding: gzip
加入响应头,而 Envoy 在转发时,默认将原始的响应头送出,因此在浏览器侧无法接受到 Accept-Encoding
的值,所以就没有进行解压而产生了异常。解决方法也很简单,就是在 Ktor 项目启动的时候,禁用 Compress:
fun Application.module() {
installPlugin(
useCompress = false, // 此处禁用压缩
sessionIdentifier = "SampleSession",
headers = mapOf("X-Engine" to "Ktor")) { }
routing {
... ...
}
}
另外,上面说到了 admin
相关的端口配置,其实 Envoy 对 admin 提供了操作接口,需要进入容器来操作,如下:
$ sudo docker exec -it envoy sh
# curl 'http://0.0.0.0:9901/help'
# curl 'http://0.0.0.0:9901/stats'
# curl 'http://0.0.0.0:9901/server_info'
容器中可能没有 curl,在此情况下需要手动安装一下,apt update && apt install curl
。
最后,经张瑾大神的指点,才知道 Istio 是 Envoy 的上层实现,其实很多情况下,我们并没有必要直接使用 Envoy,而是在 Istio 上发挥就好了,下次再研究吧。