[Gradio] Gradio FastAPI로 띄우기

2024. 11. 7. 22:17Developers 공간 [Shorts]/Software Basic

728x90
반응형
<분류>
A. 수단
- OS/Platform/Tool : Linux, Kubernetes(k8s), Docker, AWS
- Package Manager : node.js, yarn, brew, 
- Compiler/Transpillar : React, Nvcc, gcc/g++, Babel, Flutter

- Module Bundler  : React, Webpack, Parcel

B. 언어
- C/C++, python, Javacsript, Typescript, Go-Lang, CUDA, Dart, HTML/CSS

C. 라이브러리 및 프레임워크 및 SDK
- OpenCV, OpenCL, FastAPI, PyTorch, Tensorflow, Nsight

 


1. What? (현상)

Gradio를 활용해 아래와 같은 간단한 UI를 만들었다고 가정하겠습니다.

import gradio as gr

def create_ui():
    with gr.Blocks() as ui:
        with gr.Tab("A"):
            with gr.Row():
            	prompt = gr.Textbox(show_label=False, placeholder="Prompt")  
    return ui

 

일반적으로 Gradio를 활용하면 gradio에서 제공하는 launch() 함수로 실행합니다.

def main(args):

    interface = create_ui()
    interface.queue()

    interface.launch(
        server_port=args.port,
        server_name="0.0.0.0",
        ssl_verify=False,
        share=False, 
        auth=(args.username, args.password) if args.username is not None else None
    )
더보기

 -----------------------------------------------

<위 main 함수를 실행할 때 argument 선언>

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description='Run gradio interface')

    parser.add_argument('--port', type=int, default=4050, help='port number except http, https', required=False)
    parser.add_argument('--http', action='store_true', help='HTTP mode', required=False)
    parser.add_argument('--https', action='store_true', help='HTTPS mode', required=False)
    parser.add_argument('--username', type=str, help='Gradio username', required=False)
    parser.add_argument('--password', type=str, help='Gradio password', required=False)

    args = parser.parse_args()
    main(args)

 -----------------------------------------------

 

Gradio웹 Interface 라이브러리 혹은 웹 Application 툴킷이므로, launch()함수를 활용하는 경우 내부적으로 실제 Back-end 웹서버는 FastAPI Web Framework를 통해 구현되어있어 Uvicorn 웹서버를 활용합니다.

더보기

----------------------------------------------------

<Gradio와 FastAPI의 Web Server>

 

Gradio와 FastAPI에서 사용하는 web server가 무엇인지에 대해 이해하시려면 먼저 웹서버와 그 외 것들의 개념에 대해 정리할 필요가 있습니다.

  • Web Server : HTTP요청을 처리하고, 정적 콘텐츠(HTML, 이미지, 파일 등)를 제공하거나 동적 요청을 웹 애플리케이션에 전달하는 역할
    • ex) Apache, Tomcat, IIS(Internet Information Services), NginX : 전통적인 웹 서버로, 주로 WSGI를 통해 정적 콘텐츠 & 동적 웹 앱의 실행을 담당하며 PHP, Hava, ASP.NET과 같은 웹 기술과 함께 사용됨
    • ex) Uvicorn, Gunicorn, Daphne, Hypercorn : 비교적 최근에 등장한 ASGI 서버로, 고성능 비동기 작업 및 실시간 데이터 처리에 사용되며 주로 Python과 함께 발전 중
      ** ASGI : https://tkayyoo.tistory.com/112
  • Web Framework : 웹 애플리케이션을 개발하기 쉽게 해주는 라이브러리나 도구 세트로,  웹 애플리케이션에서의 라우팅, 데이터베이스 처리, 템플릿 렌더링 등을 제공
    • Back-end framework library:  
      • ex) Java : Spring,
      • ex) Javascript : Node.js, Express.js
      • ex) Python : Django, Flask, Fast API
      • ex) Ruby : Ruby on Rails
    • Front-end framework library
      • ex) Javacsript : Angular.js, React, Vue.js, Svelte
  • Web Interface Library or Web Application Toolkit : 웹 애플리케이션을 쉽게 만들고(frontend) 배포(backend)할 수 있도록 특정 목적에 맞게 특화된 도구로, 주로 딥러닝 프로젝트를 위한 Web Interface를 생성하는데 사용 됨
    • ex) Gradio, Streamlit, Dash

그럼 FastAPIGradio는 각각 내부적으로 웹 서버를 실행하기 위해 어떤 웹 서버 라이브러리를 사용할까요?

----------------------------------------------------

 

이 때 Gradio는 NginX로 띄우거나 FastAPI로 직접 띄울 수 있도록 가이드를 제공하는데, 이번 글에서는 Gradio를 FastAPI로 직접 띄워 보겠습니다.

** FastAPI : https://www.gradio.app/guides/fastapi-app-with-the-gradio-client

** NginX : https://www.gradio.app/guides/running-gradio-on-your-web-server-with-nginx


2. Why? (원인)

  • X

3. How? (해결책)

 

1. FastAPI를 활용해 HTTP로 띄우기

 

간단하게 FastAPI를 원래 활용한 것과 같이 app을 선언해준 뒤에, gradio에서 제공하는 mount_gradio_app을 활용해 해당 path에 gradio를 routing을 해주고 실행합니다.

from fastapi import FastAPI
import uvicorn
from gradio import mount_gradio_app

def main(args):
    interface = create_ui()
    interface.queue()

    app = FastAPI()
    app = mount_gradio_app(app, interface, path="/")

    if args.http: 
        args.port = 80

    uvicorn.run(
            app, 
            host="0.0.0.0", 
            port=args.port
    )

 


 

2. Response 추가하기

 

FastAPI에서와 같이 route별로 response를 추가해 개발자가 손쉽게 RESTful API를 작성할 수 있도록 돕습니다.​

 

아래는 다음을 구현해본 것입니다.

  • Case1. 내가 제공한 웹페이지의 DNS가 abc.com이라고 할때, 엔드포인트가 "/hello"인 abc.com/hello을 접속하면  message로 "Hello, World!" 메시지를 받습니다.
    ** 참고로 보통 async 함수의 input arugment는 필수로 필요한 매개변수는 변수 타입으로 선언하고, 필수가 아닌 매개변수의 경우에는 Union[type] = None 꼴로 선언합니다.
  • Case2. HTTP 요청을 처리하는 log_ip라는 미들웨어 함수를 정의해 접속한 사용자의 IP를 프린트한 후 원래 요청을 처리합니다.
    ** FastAPI Middleware는 미들웨어는 요청이 들어오고 응답이 나가기 전에 실행되는 함수나 클래스로, 모든 HTTP요청에 대해 공통적으로 실행되는 코드를 작성할 때 사용합니다.
from fastapi import FastAPI, Request
import uvicorn
from gradio import mount_gradio_app

def main(args):
    interface = create_ui()
    interface.queue()

    app = FastAPI()

	# Case1
    @app.get("/hello")
    async def read_root():
        return {"message": "Hello, World!"}
   
    # Case2
    @app.middleware("http")
    async def log_ip(request: Request, call_next):
        client_ip = request.client.host
        print(f"Connected User IP: {client_ip}")
        response = await call_next(request)
        return response

    app = mount_gradio_app(app, interface, path="/")
    if args.http: 
        args.port = 80

    uvicorn.run(
            app, 
            host="0.0.0.0", 
            port=args.port
    )

 


3. HTTPS로 띄우기

 

먼저 HTTPS에서 SSL에서 일어나는 과정에 대해 간단히 이해할 필요가 있습니다. 궁금하시다면 아래 더보기를 참조하세요.

더보기

----------------------------------------------------

<HTTPS의 SSL(Secure Sockets Layer)이란>

 

클라이언트와 서버가 주고받는 실제 정보는 대칭키 방식으로 암호화합니다. 
** 대칭키 방식 : 동일한 키(public key)로 암호화와 복호화를 같이 할 수 있는 방식으로, 빠르지만 위험합니다.

이런 방식으로 진행할 대칭키를 공개키 방식으로 암호화합니다.
** 공개키(비대칭키) 방식 : A키(public key)로 암호화를 하면 B키(private key)로 복호화(ex. RSA)하거나 , B키를 가지고 암호화를 하면 A키로 복호화하는 방식(ex. 전자서명)입니다. 상대적으로 안전하지만 느립니다.

 

[SSL handshake와 session communication]

 

(1) client hello : (브라우저는) 랜덤데이터와 "클라이언트가 지원가능한 암호화 방식"을 보냅니다.
(2) server hello : (서버는) 랜덤데이터와 "후보중 서버가 선택한 암호화 방식", 그리고 인증서를 보냅니다.
** 인증서(SSL Certificate) :  CA의 정보, 서비스의 도메인, 서버가 발행한 공개키 등의 정보가 들어있으며, 이 인증서는 CA에서 이미 private 키로 암호화되어있습니다.
(3) (브라우저는) CA의 public key를 가지고 있으므로 CA인증서를 확인해보고 맞으면 신뢰하기로 합니다. 
(4)(5) (브라우저는) 위에서 주고 받은 두 랜덤데이터를 조합해 대칭키를 생성합니다. 그리고 이 대칭키를 서버가 발행한 공개키로 암호화해 보냅니다.
(6) (서버는) 받은 대칭키를 서버가 발행한 private key로 복호화해서 확인합니다. 이 대칭키는 임시키라고 하며, 이후에 Session Key라는 대칭키를 만들어내는데 활용합니다. 

 

 

<TLS란 (Transport Layer Security)란>

 

인터넷 상의 커뮤니케이션을 위한 개인정보와 데이터보안을 용이하게하기 위해 설계되어 널리 채택된 보안 프로토콜입니다.

 

Netscape가 개발한 SSL 암호화 프로토콜에서 발전한 것으로 SSL 3.1이 TLS1.0이며, 용어는 혼용되기도 합니다.

 

HTTPS는 HTTP프로토콜 상위에서 TLS암호화를 구현한 것으로, HTTPS를 사용한 웹사이트는 TLS 암호화를 이용합니다.

----------------------------------------------------

 

위 내용을 이해하고 나면 HTTPS를 구현하기 위해 필요한 인증서 파일은 아래와 같다는 것을 알 수 있습니다.

  • certfile : SSL certificate 인증서
  • keyfile : private key 키파일
더보기

----------------------------------------------------

<테스트용 자체 인증서>

 

일반 사용자는 certfilekeyfile을 얻기 어려울 수도 있습니다.

 

이때 임시로 자체서명을 통해 localhost를 https환경으로 만들어 테스트할 수 있는 방법도 있습니다.

apt-get install mkcert
mkcert -key-file configs/certs/key.pem -cert-file configs/certs/cert.pem  localhost 127.0.0.1 ::1 0.0.0.0

----------------------------------------------------

더보기

----------------------------------------------------

<인증서 확장자 종류>

 

보통 아래와 같은 확장자를 가집니다.

  • .der (Distinguished Encoding Representation) : 바이너리 포맷으로, 사설 또는 금융 등 특수분야 시스템에서 활용하는 포맷입니다.
  • .pem (Privacy Enhanced Mail) : Base64 인코딩 된 ASCII text 파일로, 단순한 editor로 수정이가능하고 대부분의 산업에 사용되는 표준 포맷입니다. 아래와 같은 형태로 되어있습니다.
    ----------------BEGIN CERTIFICATE--------------
    ----------------END CERTIFICATE----------------

----------------------------------------------------

 

코드는 아래와 같습니다.

from fastapi import FastAPI
import uvicorn
from gradio import mount_gradio_app

def main(args):
    interface = create_ui()
    interface.queue()

    app = FastAPI()
    app = mount_gradio_app(app, interface, path="/")

    if args.https:
        args.port = 443
    elif args.http: 
        args.port = 80

    if args.https:
        import ssl
        ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)

        certfile = './config/cert.pem'
        keyfile = './config/key.pem'
        ssl_context.load_cert_chain(certfile, keyfile)
        
        uvicorn.run(
                app, 
                host="0.0.0.0", 
                port=args.port,
                ssl_keyfile=keyfile,
                ssl_certfile=certfile
        )
    else : 
        uvicorn.run(
                app, 
                host="0.0.0.0", 
                port=args.port
        )

https://www.gradio.app/guides/fastapi-app-with-the-gradio-client

https://weareyoung8-8.tistory.com/4

https://rudaks.tistory.com/entry/fastapi-%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4Middleware-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0

https://m.post.naver.com/viewer/postView.naver?volumeNo=36212235&memberNo=2521903&searchKeyword=CSR&searchRank=145

 

 

728x90
반응형