Build your own Envoy Control Plane
https://youtu.be/qAuq4cKEG_E?si=6oAnvvc2i3qgpfhQ
Steve Sloka. VMware
- contour의 maintainer
What is Envoy?
Envoy: cloud-native application을 위한 edge / service proxy 오픈소스 프로덕트.
Envoy 용어 정리
- upstream: any request that's in Envoy, and it Routes somewhere to certain endpoint.
- downstream: any request that comes to Envoy
Listener: Named Network Location (port, unix domain socket, etc...)
- downstream client가 통신을 위해 연결되는 지점.
- 일반적으로 TCP / UDP
- Filter Chain 형태로 chain 적용이 가능함. L7이나 HTTP network proxy가 가능한 이유.
Router: Listener가 Call하는 지점. (listener는 different routes를 Call할 수 있다.)
- L7 route에서는 virtual host를 사용할 수 있음.
- 특정 요청을 여러 개의 destination으로 나눠서 보낼 수 있다
- Route Entries per Host
- HTTP header 변경 가능.
Cluster: Route가 바라보는 destination.
- group of logically similar upstream hosts.
- 별도의 이름으로 정의할 수 있고, points to different set of endpoints 라는 점에서 k8s의 service와 개념적으로 유사하다고 본다
- cluster 내부에는 member가 있고, member 정보는 service Discovery에 의해 확인된다.
Cluster Service Discovery: cluster는 자신의 member( = endpoint) 정보를 어떻게 확인하는가?
- Static: cluster에서 직접 정의함.
- Strict / Logical DNS: DNS Lookup dynamically or async
- strict는 DNS query로 나오는 모든 endpoint 정보를 확인. Load balancer로 endpoint 호출 조절
- logical은 DNS query로 나오는 첫 번째 endpoint만 호출 (load balancing 없음)
- Original Destination
- Endpoint Discovery Service (EDS)
- xDS protocol 중 하나
Configuring Envoy
- Static File: listener, routers 등 필요한 정보를 전부 static file로 정의해서 envoy에 밀어넣는다.
- File System: envoy가 특정 file system에서 configuration 확인
- REST: REST 방식의 경우 Polling해야만 함. slow + overhead.
- Management Servers
- gRPC-SotW
- gRPC-Delta
gRPC의 경우 bi-directional Stream 방식으로 변경사항을 쉽게 전달할 수 있다.
- SotW: state of the world. 변경사항이 생길 때마다, 변경사항을 포함한 모든 정보를 다시 전달
- 9개 cluster에서 1개가 삭제된 경우, 8개의 cluster 정보를 전달
- Delta: 변경사항만 전달한다.
- 9개 cluster에서 1개가 삭제된 경우, '1개가 삭제됐다' 는 정보를 전달
xDS
리소스 (listener, route, cluster, endpoint) 타입별로 Discovery Service를 정의할 수도 있고, ADS 하나만 정의하고 여기서 모든 리소스를 처리하도록 만들 수도 있다.
- default: 리소스 타입별로 gRPC stream 생성. (4 streams)
- ADS의 경우 1 stream으로 해결할 수 있음
여기서는 각 타입별로 xDS 정의 + SotW 방식을 구현할 예정. 가장 간단하다.
Implementation
최초에 Envoy를 만들고, bootstrap config 정보를 전달한다.
- bootstrap: static file 형태, preload에 필요한 정보들. (clusters, listeners...) xDS server를 호출할 수 있는 endpoint도 여기서 정의한다.
- dynamic 설정이 필요한 부분은 xDS Server를 통해 전달받는다.
xDS Server: source information을 받아서 envoy에 전달하는 역할.
- envoy가 세팅해야 하는 listener, cluster, route 관련 정보
- 구현체마다 다름. contour의 경우 k8s의 service, endpoints, secrets, ingress object 정보가 source of truth이다.
- 필요한 정보 취합해서 envoy configuration을 만들고, xDS Cache에 저장하고, pass down to Envoy.
나머지는 go control plane의 sample code 보면서 진행.
예컨대 아래와 같은 config.yaml 정보가 있다고 하자. (contour의 example, static file)
name: testconfig
spec:
listeners:
- name: listener_0
address: 0.0.0.0
port: 9000
routes:
- name: echoroute
prefix: /
clusters:
- echo
clusters:
- name: echo
endpoints:
- address: 10.52.131.244
port: 9091
- address: 10.52.131.244
port: 9092
// internal/example/main/main.go
package main
import (
"context"
"flag"
"os"
"github.com/envoyproxy/go-control-plane/internal/example"
"github.com/envoyproxy/go-control-plane/pkg/cache/v3"
"github.com/envoyproxy/go-control-plane/pkg/server/v3"
"github.com/envoyproxy/go-control-plane/pkg/test/v3"
)
var (
l example.Logger
port uint
nodeID string
)
func init() {
l = example.Logger{}
flag.BoolVar(&l.Debug, "debug", false, "Enable xDS server debug logging")
// The port that this xDS server listens on
flag.UintVar(&port, "port", 18000, "xDS management server port")
// Tell Envoy to use this Node ID
flag.StringVar(&nodeID, "nodeID", "test-id", "Node ID")
}
func main() {
flag.Parse()
// Create a cache.
// snapshot cache는 go control plane (xDS server) 의 핵심.
// 각 envoy proxy에 전달해줘야 하는 정보를 들고 있다가, 필요한 곳에 load in하는 구조
// 변경사항이 생기면 New Snapshot Cache를 생성 -> envoy에 전달.
cache := cache.NewSnapshotCache(false, cache.IDHash{}, l)
// example 1. file system 변경되면 전파
proc.ProcessFile(watcher.NotifyMessage{
Operation: watcher.Create,
FilePath: watchDirectoryFileName,
})
notifyCh := make(chan watcher.NotifyMessage)
go func() {
watcher.Watch(watchDirectoryFileName, notifyCh)
}()
go func() {
// run xDS Server
// Run the xDS server
ctx := context.Background()
cb := &test.Callbacks{Debug: l.Debug}
srv := server.NewServer(ctx, cache, cb)
example.RunServer(srv, port)
}
for {
select {
case msg <- notifyCh:
proc.ProcessFile(msg)
}
}
// example 2. Create the snapshot that we'll serve to Envoy
snapshot := example.GenerateSnapshot()
if err := snapshot.Consistent(); err != nil {
l.Errorf("snapshot inconsistency: %+v\n%+v", snapshot, err)
os.Exit(1)
}
l.Debugf("will serve snapshot %+v", snapshot)
// Add the snapshot to the cache
if err := cache.SetSnapshot(context.Background(), nodeID, snapshot); err != nil {
l.Errorf("snapshot error %q for %+v", err, snapshot)
os.Exit(1)
}
// Run the xDS server
ctx := context.Background()
cb := &test.Callbacks{Debug: l.Debug}
srv := server.NewServer(ctx, cache, cb)
example.RunServer(srv, port)
}
file system의 변경사항을 확인하는 로직은
- fileWatcher로 file 변경사항을 파악한다
- proc.ProcessFile() 메소드로 yaml을 파싱해서 envoy cluster, router, listener 정보를 확인하고, cache.NewSnapShot() 메소드로 snapshot을 생성한다
- Consistent() 메소드로 생성된 snapshot의 validation 체크
- SetSnapShot()으로 전파.
bootstrap.yaml 필드 설명.
static_resources:
clusters:
- connect_timeout: 1s
type: LOGICAL_DNS
load_assignment:
cluster_name: xds_cluster ## xDS server 정보.
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.1
port_value: 9092
dynamic_resources:
cds_config: # xDS server의 endpoint 정보들.
resource_api_version: V3
api_config_source:
api_type: GRPC
transport_api_version: V3
grpc_services:
- envoy_grpc:
cluster_name: xds_cluster # dynamic cluster that comes from this endpoint.
set_node_on_first_message_only: true
lds_config:
resource_api_version: V3
api_config_source:
api_type: GRPC
transport_api_version: V3
grpc_services:
- envoy_grpc:
cluster_name: xds_cluster
set_node_on_first_message_only: true
layered_runtime:
layers:
- name: runtime-0
rtds_layer:
rtds_config:
resource_api_version: V3
api_config_source:
transport_api_version: V3
api_type: GRPC
grpc_services:
envoy_grpc:
cluster_name: xds_cluster
name: runtime-0
# 해당 envoy의 identifier - node정보, Cluster 정보
node:
cluster: test-cluster-1
id: test-id-1
'학습일지 > Service Mesh' 카테고리의 다른 글
KubeCon 2019 - Istio Multi-Cluster Service Mesh Patterns Explained (0) | 2022.11.02 |
---|---|
istio 개념 정리 (3) - mTLS Security (0) | 2022.10.29 |
Deview 2021 - istio/Envoy로 Multi-IDC L7 로드밸런서 만들기 (0) | 2022.10.22 |
istio 개념 정리 (2) - VirtualService / DestinationRule / Gateway (0) | 2022.10.10 |
istio 개념 정리 (1) - Service Mesh와 istio (1) | 2022.10.06 |