caddy &grpc(3)caddy에 역방향 프록시 플러그인 추가
프로젝트 주소:https://github.com/yhyddr/caddy-grpc
전언
지난번에 우리는 Caddy에서 원하는 플러그인을 확장하는 방법을 배웠다.블로그에서 대략적인 틀만 제공하였다.이번에는 구체적인 플러그인
caddy-grpc
에 따라 배우겠습니다.그것을 선택한 이유는 그 자체가 하나의 독립된 응용 프로그램이기 때문에, 여기에 그것을 Caddy 플러그인으로 만들었기 때문이다.아마도 당신은 Caddy의 좋은 디자인을 한층 더 이해했을 것이다.
플러그인 역할
이 플러그인의 목적은 Improbable-eng/grpc-web/go/grpcwebproxy의 목적과 같지만, 독립된 Go 프로그램이 아닌Caddy 중간부품 플러그인입니다.
이 프로젝트의 역할은 무엇일까?
이것은 gRPC-웹 프로토콜로 기존의 gRPC 서버를 지원하고 그 기능을 공개하여 브라우저에서 gRPC 서비스를 사용할 수 있는 소형 리버스 에이전트입니다.특징:
8080
)/metrics
디버그 포트에서)/debug/requests
및 연결 추적 엔드포인트/debug/events
8443
:사실 이 역방향 에이전트를 캐디 서버의 중간부품으로 만들었다는 뜻이다.
사용
네가 필요할 때 통과할 수 있다
example.com
grpc localhost:9090
첫 번째 행 example.com은 서비스할 사이트의 호스트 이름/주소입니다.두 번째 줄은grpc라는 명령으로 백엔드 gRPC 서비스 노드 주소를 지정할 수 있다(즉 예시의localhost:9090).(참고: 위의 구성은 기본적으로 TLS 1.2에서 백엔드 gRPC 서비스로 설정됨)
Caddyfile 구문
grpc backend_addr {
backend_is_insecure
backend_tls_noverify
backend_tls_ca_files path_to_ca_file1 path_to_ca_file2
}
backend_is_insecure
기본적으로 에이전트는 TLS를 사용하여 백엔드에 연결하지만, 백엔드가 명문으로 서비스를 제공하는 경우 이 옵션을 추가해야 합니다
backend_tls_noverify
기본적으로 백엔드의 TLS를 확인합니다.검증하지 않으려면 이 옵션을 추가해야 합니다
backend_tls_ca_files
백엔드 인증서 확인을 위한 PEM 인증서 체인 경로(쉼표로 구분됨).비어 있으면 host 호스트 CA 체인이 사용됩니다.
원본 코드
디렉토리 구조
caddy-grpc
├── LICENSE
├── README.md
├── proxy // grpc proxy
│ ├── DOC.md
│ ├── LICENSE.txt
│ ├── README.md
│ ├── codec.go
│ ├── director.go
│ ├── doc.go
│ └── handler.go
├── server.go // Handle
└── setup.go //
Setup.go
지난번에 진행된 플러그인 작성 순서에 따라 기억이 안 나면, 캐디에 플러그인 확장을 추가하는 방법을 보십시오.
먼저 설치된 setup을 보십시오.go 파일
init func
func init() {
caddy.RegisterPlugin("grpc", caddy.Plugin{
ServerType: "http",
Action: setup,
})
}
이 플러그인은 http 서버에 등록되어 있으며 이름은grpc입니다
setup func
그리고 가장 중요한 setup 함수를 보았습니다. 방금 언급한 사용 방법에서caddyfile의 옵션을 분석하는 것이 바로 그것입니다.그것 또한 분석된directive를 Caddy의 controller에 넘겨서 이 플러그인을 설정합니다
// setup configures a new server middleware instance.
func setup(c *caddy.Controller) error {
for c.Next() {
var s server
if !c.Args(&s.backendAddr) { //loads next argument into backendAddr and fail if none specified
return c.ArgErr()
}
tlsConfig := &tls.Config{}
tlsConfig.MinVersion = tls.VersionTLS12
s.backendTLS = tlsConfig
s.backendIsInsecure = false
//check for more settings in Caddyfile
for c.NextBlock() {
switch c.Val() {
case "backend_is_insecure":
s.backendIsInsecure = true
case "backend_tls_noverify":
s.backendTLS = buildBackendTLSNoVerify()
case "backend_tls_ca_files":
t, err := buildBackendTLSFromCAFiles(c.RemainingArgs())
if err != nil {
return err
}
s.backendTLS = t
default:
return c.Errf("unknown property '%s'", c.Val())
}
}
httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
s.next = next
return s
})
}
return nil
}
if !c.Args(&s.backendAddr) { //loads next argument into backendAddr and fail if none specified
return c.ArgErr()
}
여기는caddyfile에 대응하는 설정
grpc localhost:9090
tlsConfig := &tls.Config{}
tlsConfig.MinVersion = tls.VersionTLS12
s.backendTLS = tlsConfig
s.backendIsInsecure = false
//check for more settings in Caddyfile
for c.NextBlock() {
switch c.Val() {
case "backend_is_insecure":
s.backendIsInsecure = true
case "backend_tls_noverify":
s.backendTLS = buildBackendTLSNoVerify()
case "backend_tls_ca_files":
t, err := buildBackendTLSFromCAFiles(c.RemainingArgs())
if err != nil {
return err
}
s.backendTLS = t
default:
return c.Errf("unknown property '%s'", c.Val())
}
}
보시다시피
c.NextBlock()
를 통해 모든 새로운token의 분석을 진행하고 c.Val()을 사용하여 읽은 후에 서로 다른 설정을 합니다.httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
s.next = next
return s
})
server.go
다음은 두 번째 단계로 넘어가겠습니다.
struct
우선 이 플러그인의 가장 핵심적인 구조를 살펴보자.저장된 데이터
type server struct {
backendAddr string
next httpserver.Handler
backendIsInsecure bool
backendTLS *tls.Config
wrappedGrpc *grpcweb.WrappedGrpcServer
}
serveHTTP
지난번 글에서 두 번째 중요한 부분인데 서버 HTTP의 실현은 구체적인 기능을 대표한다.저번에 저희 내용은 다음 핸들한테 전달하는 논리밖에 없었어요.
func (g gizmoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
return g.next.ServeHTTP(w, r)
}
이제 이grpc에 어떤 논리가 첨가되었는지 봅시다.
// ServeHTTP satisfies the httpserver.Handler interface.
func (s server) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
//dial Backend
opt := []grpc.DialOption{}
opt = append(opt, grpc.WithCodec(proxy.Codec()))
if s.backendIsInsecure {
opt = append(opt, grpc.WithInsecure())
} else {
opt = append(opt, grpc.WithTransportCredentials(credentials.NewTLS(s.backendTLS)))
}
backendConn, err := grpc.Dial(s.backendAddr, opt...)
if err != nil {
return s.next.ServeHTTP(w, r)
}
director := func(ctx context.Context, fullMethodName string) (context.Context, *grpc.ClientConn, error) {
md, _ := metadata.FromIncomingContext(ctx)
return metadata.NewOutgoingContext(ctx, md.Copy()), backendConn, nil
}
grpcServer := grpc.NewServer(
grpc.CustomCodec(proxy.Codec()), // needed for proxy to function.
grpc.UnknownServiceHandler(proxy.TransparentHandler(director)),
/*grpc_middleware.WithUnaryServerChain(
grpc_logrus.UnaryServerInterceptor(logger),
grpc_prometheus.UnaryServerInterceptor,
),
grpc_middleware.WithStreamServerChain(
grpc_logrus.StreamServerInterceptor(logger),
grpc_prometheus.StreamServerInterceptor,
),*/ //middleware should be a config setting or 3rd party middleware plugins like for caddyhttp
)
// gRPC-Web compatibility layer with CORS configured to accept on every
wrappedGrpc := grpcweb.WrapServer(grpcServer, grpcweb.WithCorsForRegisteredEndpointsOnly(false))
wrappedGrpc.ServeHTTP(w, r)
return 0, nil
}
//dial Backend
opt := []grpc.DialOption{}
opt = append(opt, grpc.WithCodec(proxy.Codec()))
if s.backendIsInsecure {
opt = append(opt, grpc.WithInsecure())
} else {
opt = append(opt, grpc.WithTransportCredentials(credentials.NewTLS(s.backendTLS)))
}
backendConn, err := grpc.Dial(s.backendAddr, opt...)
if err != nil {
return s.next.ServeHTTP(w, r)
}
director := func(ctx context.Context, fullMethodName string) (context.Context, *grpc.ClientConn, error) {
md, _ := metadata.FromIncomingContext(ctx)
return metadata.NewOutgoingContext(ctx, md.Copy()), backendConn, nil
}
grpcServer := grpc.NewServer(
grpc.CustomCodec(proxy.Codec()), // needed for proxy to function.
grpc.UnknownServiceHandler(proxy.TransparentHandler(director)),
/*grpc_middleware.WithUnaryServerChain(
grpc_logrus.UnaryServerInterceptor(logger),
grpc_prometheus.UnaryServerInterceptor,
),
grpc_middleware.WithStreamServerChain(
grpc_logrus.StreamServerInterceptor(logger),
grpc_prometheus.StreamServerInterceptor,
),*/ //middleware should be a config setting or 3rd party middleware plugins like for caddyhttp
)
// gRPC-Web compatibility layer with CORS configured to accept on every
wrappedGrpc := grpcweb.WrapServer(grpcServer, grpcweb.WithCorsForRegisteredEndpointsOnly(false))
wrappedGrpc.ServeHTTP(w, r)
Proxy
위의 글에서proxy를 사용했음을 주의하십시오.TransparentHandler. proxy에 있는handler입니다.go에서 정의한 함수입니다.gRPC 서비스를 실현하는 데 사용되는 에이전트입니다.여기에는 gRPC의 상호작용의 실현과 관련이 있는데 중심은 클라이언트와 서버의stream 전송이다. 본고와 관계가 크지 않기 때문에 관심이 있으면 내려와서 이해할 수 있다.
결어
이걸 캔디의 플러그인으로 뭘 가져왔는지 생각해볼까?
한순간에 확장할 수 있는 설정을 많이 얻었죠?Caddy에서 원하는 플러그인의 기능을 처음에 말한 독립된 응용 프로그램에 적용하는 것이 아니라
만약 당신도 HTTP 서비스를 하고 있다면, 아직도 Caddy의 일부 기능과 생태를 탐내고 있다면, 이렇게 접속하세요.
그것 은grpc-web 까지 관련되어 있어 흥미 가 있으면 학습 을 확장할 수 있다
grpc-web client implementations/examples:
Vue.jsGopherJS
참고 자료
caddy:https://github.com/caddyserver/caddy중간부품을 쓰는 방법:https://github.com/caddyserver/caddy/wiki/Writing-a-Plugin:- HTTP-Middlewarecaddy-grpc 플러그인:https://github.com/pieterlouw/caddy-grpc
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.