caddy &grpc(3)caddy에 역방향 프록시 플러그인 추가

10511 단어
caddy-grpc는 caddy에 역방향 프록시 플러그인을 추가합니다
프로젝트 주소:https://github.com/yhyddr/caddy-grpc
전언
지난번에 우리는 Caddy에서 원하는 플러그인을 확장하는 방법을 배웠다.블로그에서 대략적인 틀만 제공하였다.이번에는 구체적인 플러그인caddy-grpc에 따라 배우겠습니다.
그것을 선택한 이유는 그 자체가 하나의 독립된 응용 프로그램이기 때문에, 여기에 그것을 Caddy 플러그인으로 만들었기 때문이다.아마도 당신은 Caddy의 좋은 디자인을 한층 더 이해했을 것이다.
플러그인 역할
이 플러그인의 목적은 Improbable-eng/grpc-web/go/grpcwebproxy의 목적과 같지만, 독립된 Go 프로그램이 아닌Caddy 중간부품 플러그인입니다.
이 프로젝트의 역할은 무엇일까?
이것은 gRPC-웹 프로토콜로 기존의 gRPC 서버를 지원하고 그 기능을 공개하여 브라우저에서 gRPC 서비스를 사용할 수 있는 소형 리버스 에이전트입니다.특징:
  • 구조화 기록(바로log) 에이전트가 stdout(표준 출력)에 요청
  • 디버그 가능한 HTTP 포트(기본 포트8080)
  • Prometheus 모니터링 에이전트 요청/metrics 디버그 포트에서)
  • Request/debug/requests 및 연결 추적 엔드포인트/debug/events
  • TLS 1.2 서비스(기본 포트8443:
  • 클라이언트 인증서 인증 활성화 옵션
  • 보안(일반 텍스트) 및 TLS gRPC 백엔드 연결:
  • 사용자 지정 CA 인증서를 사용하여 연결

  • 사실 이 역방향 에이전트를 캐디 서버의 중간부품으로 만들었다는 뜻이다.
    사용
    네가 필요할 때 통과할 수 있다
    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
    }
  • 우리는 여전히 c.Next()에서 시작해서 프로필을 읽는 데 사용되고 있음을 알아차렸다. 실제로 여기서grpc라는 token을 읽고 다음 단계
  • 를 진행했다
  • 그리고 우리는 grpc가 뒤따라 읽는 것이 감청 주소라는 것을 보았다.
  • if !c.Args(&s.backendAddr) { //loads next argument into backendAddr and fail if none specified
                return c.ArgErr()
            }

    여기는caddyfile에 대응하는 설정grpc localhost:9090
  • 주의c.Next(), c.Args(), c.NextBlock()는 모두caddyfile의 설정을 읽는 함수입니다.caddy에서token
  • 이라고 부릅니다.
  • 또한 tls의 설정을 주의했다. 앞에서 언급한 바와 같이 이 서비스는 tls1.2의 서비스를 오픈한 것이다
  •         tlsConfig := &tls.Config{}
            tlsConfig.MinVersion = tls.VersionTLS12
    
            s.backendTLS = tlsConfig
            s.backendIsInsecure = false
  • 그리고 위에서 말한 caddyfile 문법의 설정 읽기
  • //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
    }
  • backendAddr는grpc서비스의 감청 주소
  • 입니다.
  • next는 다음 플러그인의Handler 처리
  • backendIsInsecure와backendTLS는 백엔드 서비스가 서로 다른 보안 정책을 사용했는지 여부입니다.
  • wrappedGrpc는 이 플러그인의 관건이다. 이것은grpcwebprotocol을 실현하여grpc서비스가 브라우저에 접근할 수 있도록 하는 것이다.

  • 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
    }
    
  • 우선grpc의 설정 부분입니다. 만약grpc를 알고 있다면 이것은grpc 클라이언트를 설정하는 옵션이라는 것을 알 수 있습니다.여기에는 Codec 인코딩과 다양한 보안 정책 옵션이 클라이언트에 추가되었습니다.
  •     //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)
        }
  • 그리고grpc 서버를 설치한 옵션
  • 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
        )
  • 마지막으로grpcweb을 사용합니다.WrapServer를 통한 웹 서비스 호출
  • // 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

    좋은 웹페이지 즐겨찾기