# Envoy 作為前端代理
**注意:本書中的 Service Mesh 章節已不再維護,請轉到 [istio-handbook](https://jimmysong.io/istio-handbook) 中瀏覽。**
本文是使用 Envoy 作為前端代理的介紹,僅使用 docker 容器和 docker-compose 做編排在單機中運行,幫助我們從更底層了解 Envoy,當我們將 Envoy 作為 Istio Service Mesh 的 data panel 的時候將更加游刃有余。
## 快速開始
Envoy 中的所有規則配置跟 Kubernetes 一樣都是通過 YAML 文件來完成的。在繼續下面的步驟之前,首先克隆 Envoy 的 GitHub repo。
```bash
git clone https://github.com/envoyproxy/envoy.git
```
## 運行 sandbox 測試
Envoy 官方提供了以下打包用例:
- [Front Proxy](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/front_proxy)
- [Zipkin Tracing](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/zipkin_tracing)
- [Jaeger Tracing](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/jaeger_tracing)
- [gRPC Bridge](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/grpc_bridge)
全部可以使用 `docker-compose` 運行,代碼可以在 https://github.com/envoyproxy/envoy/tree/master/examples 找到。
## Front proxy
Envoy 在 envoymesh 的邊緣做反向代理,詳細使用方式見 <https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/front_proxy>,在此我將解說下以下問題:
- Envoy 是如何作為進程外架構運行的?
- 為何說 Envoy 是無侵入式架構?
- Envoy 作為邊緣反向代理能做什么?
本示例的架構圖如下所示,此時 Envoy 將作為一個反向代理,類似于 Nginx,但與 Nginx 不同的是它還會作為一個進程,伴隨每個服務一起運行在同一個容器中(在 Kubernetes 中可以作為 Sidecar 與應用容器一起運行在同一個 Pod 中)。

在此示例中一共有 3 個服務,我們需要為其創建容器編排的 `docker-compose.yml` 文件。
```yaml
version: '2'
services:
front-envoy:
build:
context: .
dockerfile: Dockerfile-frontenvoy
volumes:
- ./front-envoy.yaml:/etc/front-envoy.yaml
networks:
- envoymesh
expose:
- "80"
- "8001"
ports:
- "8000:80"
- "8001:8001"
service1:
build:
context: .
dockerfile: Dockerfile-service
volumes:
- ./service-envoy.yaml:/etc/service-envoy.yaml
networks:
envoymesh:
aliases:
- service1
environment:
- SERVICE_NAME=1
expose:
- "80"
service2:
build:
context: .
dockerfile: Dockerfile-service
volumes:
- ./service-envoy.yaml:/etc/service-envoy.yaml
networks:
envoymesh:
aliases:
- service2
environment:
- SERVICE_NAME=2
expose:
- "80"
networks:
envoymesh: {}
```
使用 docker-compose 啟動可以保證三個服務都在同一個網絡內,即 `frontproxy_envoymesh` 網絡中。
其中 `front-envoy` 是前端(邊緣)Envoy 服務,用來做反向代理,它使用的是 `Dockerfile-frontenvoy` 文件來構建鏡像的,我們來看下該 `Dockerfile` 的內容。
```docker
FROM envoyproxy/envoy:latest
RUN apt-get update && apt-get -q install -y \
curl
CMD /usr/local/bin/envoy -c /etc/front-envoy.yaml --service-cluster front-proxy
```
其中 `/etc/front-envoy.yaml` 是本地的 `front-envoy.yaml` 掛載進去的。我們看下該文件的內容。
```yaml
static_resources:
listeners:
- address:
socket_address:
address: 0.0.0.0
port_value: 80
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
codec_type: auto
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: backend
domains:
- "*"
routes:
- match:
prefix: "/service/1"
route:
cluster: service1
- match:
prefix: "/service/2"
route:
cluster: service2
http_filters:
- name: envoy.router
config: {}
clusters:
- name: service1
connect_timeout: 0.25s
type: strict_dns
lb_policy: round_robin
http2_protocol_options: {}
hosts:
- socket_address:
address: service1
port_value: 80
- name: service2
connect_timeout: 0.25s
type: strict_dns
lb_policy: round_robin
http2_protocol_options: {}
hosts:
- socket_address:
address: service2
port_value: 80
admin:
access_log_path: "/dev/null"
address:
socket_address:
address: 0.0.0.0
port_value: 8001
```
我們看到其中包括了三大配置項:
- **static_resources**:路由配置信息
- **cluster**:envoymesh 的服務注冊信息
- **admin**:管理接口,可以通過訪問 8001 端口的,訪問 `/stats` 獲取當前 envoymesh 的一些統計信息,訪問 `/server_info` 獲取 Envoy 的版本信息
使用 `docker-compose` 啟動三個容器。
```bash
$ pwd
envoy/examples/front-proxy
$ docker-compose up --build -d
$ docker-compose ps
Name Command State Ports
-------------------------------------------------------------------------------------------------------------
example_service1_1 /bin/sh -c /usr/local/bin/ ... Up 80/tcp
example_service2_1 /bin/sh -c /usr/local/bin/ ... Up 80/tcp
example_front-envoy_1 /bin/sh -c /usr/local/bin/ ... Up 0.0.0.0:8000->80/tcp, 0.0.0.0:8001->8001/tcp
```
我們下面將過一遍 Envoy 作為前端代理的所有功能,這些功能是通用功能。
### 路由
訪問 service1 <http://localhost:8000/service/1> 將看到如下輸出。
```bash
$ curl -v localhost:8000/service/1
*
Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8000 (#0)
> GET /service/1 HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< content-type: text/html; charset=utf-8
< content-length: 89
< server: envoy
< date: Fri, 20 Apr 2018 08:26:33 GMT
< x-envoy-upstream-service-time: 14
<
Hello from behind Envoy (service 1)! hostname: a3e4185a9a49 resolvedhostname: 172.18.0.4
* Connection #0 to host localhost left intact
```
訪問 service2 <http://localhost:8000/service/2> 將看到如下輸出。
```bash
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8000 (#0)
> GET /service/2 HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< content-type: text/html; charset=utf-8
< content-length: 89
< server: envoy
< date: Fri, 20 Apr 2018 08:27:27 GMT
< x-envoy-upstream-service-time: 10
<
Hello from behind Envoy (service 2)! hostname: f6650e1911a0 resolvedhostname: 172.18.0.3
* Connection #0 to host localhost left intact
```
我們看到訪問請求被路由到了正確的服務后端。
### 負載均衡
增加 service1 的示例數。
```bash
$ docker-compose scale service1=3
WARNING: The scale command is deprecated. Use the up command with the --scale flag instead.
Starting frontproxy_service1_1 ... done
Creating frontproxy_service1_2 ... done
Creating frontproxy_service1_3 ... done
$ docker-compose ps
Name Command State Ports
---------------------------------------------------------------------------------------------------------------------------
frontproxy_front-envoy_1 /usr/bin/dumb-init -- /bin ... Up 10000/tcp, 0.0.0.0:8000->80/tcp, 0.0.0.0:8001->8001/tcp
frontproxy_service1_1 /bin/sh -c /usr/local/bin/ ... Up 10000/tcp, 80/tcp
frontproxy_service1_2 /bin/sh -c /usr/local/bin/ ... Up 10000/tcp, 80/tcp
frontproxy_service1_3 /bin/sh -c /usr/local/bin/ ... Up 10000/tcp, 80/tcp
frontproxy_service2_1 /bin/sh -c /usr/local/bin/ ... Up 10000/tcp, 80/tcp
```
我們看到現在 service1 已經有了 3 個實例,現在再訪問 service1 <http://localhost:8000/service/1>。
```bash
$ while true;do curl localhost:8000/service/1;sleep 1;done
Hello from behind Envoy (service 1)! hostname: a3e4185a9a49 resolvedhostname: 172.18.0.4
Hello from behind Envoy (service 1)! hostname: fe44dba64122 resolvedhostname: 172.18.0.5
Hello from behind Envoy (service 1)! hostname: c5b9f1289e0f resolvedhostname: 172.18.0.6
Hello from behind Envoy (service 1)! hostname: a3e4185a9a49 resolvedhostname: 172.18.0.4
Hello from behind Envoy (service 1)! hostname: fe44dba64122 resolvedhostname: 172.18.0.5
Hello from behind Envoy (service 1)! hostname: c5b9f1289e0f resolvedhostname: 172.18.0.6
```
我們看到對 service1 的已經有負載均衡了,使用的策略是 `round_robin`,這些都是在 `front-envoy.yaml` 文件中的 `cluster` 項下配置的。
### admin 端點
訪問 <http://localhost:8001> 可以看到 Envoy admin 提供以下管理 API 端點。
| 命令 | 描述 |
| -------------------- | ---------------------------------------- |
| / | Admin 主頁 |
| /certs | 打印機器上的 certs |
| /clusters | upstream cluster 狀態 |
| /config_dump | 輸出當前的 Envoy 配置 |
| /cpuprofiler | 開啟/關閉 CPU profiler |
| /healthcheck/fail | 導致服務失敗健康檢查 |
| /healthcheck/ok | 導致服務通過健康檢查 |
| /help | 打印管理命令的幫助信息 |
| /hot_restart_version | 打印熱重啟兼容版本 |
| /listeners | 打印 listener 地址 |
| /logging | 查詢/更改日志級別 |
| /quitquitquit | 退出服務 |
| /reset_counters | 將計數器重置為 1 |
| /runtime | 打印運行時值 |
| /runtime_modify | 修改運行時值 |
| /server_info | 打印服務器版本/狀態信息 |
| /stats | 打印服務器狀態統計信息 |
| /stats/prometheus | 打印 prometheus 格式的服務器狀態統計信息 |
Envoy 提供了 API 管理端點,可以對 Envoy 進行動態配置,參考 [v2 API reference](https://www.envoyproxy.io/docs/envoy/latest/api-v2/api)。
## 參考
- [Front proxy](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/front_proxy)
- 序言
- 云原生
- 云原生(Cloud Native)的定義
- CNCF - 云原生計算基金會簡介
- CNCF章程
- 云原生的設計哲學
- Play with Kubernetes
- 快速部署一個云原生本地實驗環境
- Kubernetes與云原生應用概覽
- 云原生應用之路——從Kubernetes到Cloud Native
- 云原生編程語言
- 云原生編程語言Ballerina
- 云原生編程語言Pulumi
- 云原生的未來
- Kubernetes架構
- 設計理念
- Etcd解析
- 開放接口
- CRI - Container Runtime Interface(容器運行時接口)
- CNI - Container Network Interface(容器網絡接口)
- CSI - Container Storage Interface(容器存儲接口)
- Kubernetes中的網絡
- Kubernetes中的網絡解析——以flannel為例
- Kubernetes中的網絡解析——以calico為例
- 具備API感知的網絡和安全性管理開源軟件Cilium
- Cilium架構設計與概念解析
- 資源對象與基本概念解析
- Pod狀態與生命周期管理
- Pod概覽
- Pod解析
- Init容器
- Pause容器
- Pod安全策略
- Pod的生命周期
- Pod Hook
- Pod Preset
- Pod中斷與PDB(Pod中斷預算)
- 集群資源管理
- Node
- Namespace
- Label
- Annotation
- Taint和Toleration(污點和容忍)
- 垃圾收集
- 控制器
- Deployment
- StatefulSet
- DaemonSet
- ReplicationController和ReplicaSet
- Job
- CronJob
- Horizontal Pod Autoscaling
- 自定義指標HPA
- 準入控制器(Admission Controller)
- 服務發現
- Service
- Ingress
- Traefik Ingress Controller
- 身份與權限控制
- ServiceAccount
- RBAC——基于角色的訪問控制
- NetworkPolicy
- 存儲
- Secret
- ConfigMap
- ConfigMap的熱更新
- Volume
- Persistent Volume(持久化卷)
- Storage Class
- 本地持久化存儲
- 集群擴展
- 使用自定義資源擴展API
- 使用CRD擴展Kubernetes API
- Aggregated API Server
- APIService
- Service Catalog
- 資源調度
- QoS(服務質量等級)
- 用戶指南
- 資源對象配置
- 配置Pod的liveness和readiness探針
- 配置Pod的Service Account
- Secret配置
- 管理namespace中的資源配額
- 命令使用
- Docker用戶過度到kubectl命令行指南
- kubectl命令概覽
- kubectl命令技巧大全
- 使用etcdctl訪問kubernetes數據
- 集群安全性管理
- 管理集群中的TLS
- kubelet的認證授權
- TLS bootstrap
- 創建用戶認證授權的kubeconfig文件
- IP偽裝代理
- 使用kubeconfig或token進行用戶身份認證
- Kubernetes中的用戶與身份認證授權
- Kubernetes集群安全性配置最佳實踐
- 訪問Kubernetes集群
- 訪問集群
- 使用kubeconfig文件配置跨集群認證
- 通過端口轉發訪問集群中的應用程序
- 使用service訪問群集中的應用程序
- 從外部訪問Kubernetes中的Pod
- Cabin - Kubernetes手機客戶端
- Kubernetic - Kubernetes桌面客戶端
- Kubernator - 更底層的Kubernetes UI
- 在Kubernetes中開發部署應用
- 適用于kubernetes的應用開發部署流程
- 遷移傳統應用到Kubernetes中——以Hadoop YARN為例
- 最佳實踐概覽
- 在CentOS上部署Kubernetes集群
- 創建TLS證書和秘鑰
- 創建kubeconfig文件
- 創建高可用etcd集群
- 安裝kubectl命令行工具
- 部署master節點
- 安裝flannel網絡插件
- 部署node節點
- 安裝kubedns插件
- 安裝dashboard插件
- 安裝heapster插件
- 安裝EFK插件
- 生產級的Kubernetes簡化管理工具kubeadm
- 使用kubeadm在Ubuntu Server 16.04上快速構建測試集群
- 服務發現與負載均衡
- 安裝Traefik ingress
- 分布式負載測試
- 網絡和集群性能測試
- 邊緣節點配置
- 安裝Nginx ingress
- 安裝配置DNS
- 安裝配置Kube-dns
- 安裝配置CoreDNS
- 運維管理
- Master節點高可用
- 服務滾動升級
- 應用日志收集
- 配置最佳實踐
- 集群及應用監控
- 數據持久化問題
- 管理容器的計算資源
- 集群聯邦
- 存儲管理
- GlusterFS
- 使用GlusterFS做持久化存儲
- 使用Heketi作為Kubernetes的持久存儲GlusterFS的external provisioner
- 在OpenShift中使用GlusterFS做持久化存儲
- GlusterD-2.0
- Ceph
- 用Helm托管安裝Ceph集群并提供后端存儲
- 使用Ceph做持久化存儲
- 使用rbd-provisioner提供rbd持久化存儲
- OpenEBS
- 使用OpenEBS做持久化存儲
- Rook
- NFS
- 利用NFS動態提供Kubernetes后端存儲卷
- 集群與應用監控
- Heapster
- 使用Heapster獲取集群和對象的metric數據
- Prometheus
- 使用Prometheus監控kubernetes集群
- Prometheus查詢語言PromQL使用說明
- 使用Vistio監控Istio服務網格中的流量
- 分布式跟蹤
- OpenTracing
- 服務編排管理
- 使用Helm管理Kubernetes應用
- 構建私有Chart倉庫
- 持續集成與發布
- 使用Jenkins進行持續集成與發布
- 使用Drone進行持續集成與發布
- 更新與升級
- 手動升級Kubernetes集群
- 升級dashboard
- 領域應用概覽
- 微服務架構
- 微服務中的服務發現
- 使用Java構建微服務并發布到Kubernetes平臺
- Spring Boot快速開始指南
- Service Mesh 服務網格
- 企業級服務網格架構
- Service Mesh基礎
- Service Mesh技術對比
- 采納和演進
- 定制和集成
- 總結
- Istio
- 安裝并試用Istio service mesh
- 配置請求的路由規則
- 安裝和拓展Istio service mesh
- 集成虛擬機
- Istio中sidecar的注入規范及示例
- 如何參與Istio社區及注意事項
- Istio教程
- Istio免費學習資源匯總
- 深入理解Istio Service Mesh中的Envoy Sidecar注入與流量劫持
- 深入理解Istio Service Mesh中的Envoy Sidecar代理的路由轉發
- Linkerd
- Linkerd 使用指南
- Conduit
- Condiut概覽
- 安裝Conduit
- Envoy
- Envoy的架構與基本術語
- Envoy作為前端代理
- Envoy mesh教程
- SOFAMesh
- SOFAMesh中的Dubbo on x-protocol
- SOFAMosn
- 使用 SOFAMosn 構建 SOFAMesh
- 大數據
- Spark standalone on Kubernetes
- 運行支持Kubernetes原生調度的Spark程序
- Serverless架構
- 理解Serverless
- FaaS-函數即服務
- OpenFaaS快速入門指南
- 邊緣計算
- 人工智能