こしあん
2024-01-15

GradioとStreamlitのアプリをnginxでサブディレクトリにルーティングさせる


1k{icon} {views}


1つのドメイン直下にサブディレクトリを作り、複数のGradio、Streamlitのアプリを配置するパターンをコンテナベースで実装してみます。nginxによるリバースプロキシを使います。

やりたいこと

1つのドメイン直下にサブディレクトリを作り、複数のGradio、Streamlitのアプリを配置したい。

  • ドメイン
    • /app1/ : Gradioのアプリ1
    • /app2/ : Gradioのアプリ2
    • /app3/ : Streamlitのアプリ3

として、全部を1個のdocker-composeで起動するというもの

nginxのコンテナにトップのindex.htmlの配信と、各アプリのリバースプロキシの両方を持たせる。

ディレクトリ構造

+ gradio1
  + src
    - app.py
  - Dockerfile
  - requirements.txt
+ gradio2
  + src
    - app.py
  - Dockerfile
  - requirements.txt
+ streamlit1
  + src
    - app.py
  - Dockerfile
  - requirements.txt
+ nginx
  - default.conf
  - Dockerfile
  - index.html
- docker-compose.yaml

動かしてみる

docker-compose.yamlのあるディレクトリで起動

docker-compose build

docker-compose up

上からトップページ、「/app1/」、「/app2/」、「/app3/」のサブディレクトリ

コード

docker-compose

services:
  reverse_proxy:
    build: nginx
    image: multiple_image_nginx
    stdin_open: true
    tty: true
    ports:
      - "5001:80"
    extra_hosts:
      - "host.docker.internal:host-gateway"
  gradio1:
    build: gradio1
    image: multiple_image_gradio1
    stdin_open: true
    tty: true
    ports:
      - "30001:8080"
    environment:
      - APP_ENV=docker
      - GRADIO_ROOT_PATH=/app1
  gradio2:
    build: gradio2
    image: multiple_image_gradio2
    stdin_open: true
    tty: true
    ports:
      - "30002:8080"
    environment:
      - APP_ENV=docker
      - GRADIO_ROOT_PATH=/app2
  streamlit1:
    build: streamlit1
    image: multiple_image_streamlit1
    stdin_open: true
    tty: true
    ports:
      - "30003:8080"

gradio1

Dockerfile

FROM ubuntu:22.04

RUN apt-get update
ENV TZ=Asia/Tokyo
ENV LANG=en_US.UTF-8
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apt-get install -yq --no-install-recommends python3-pip \
        python3-dev \
        build-essential \
        openssh-server \
        git \
        wget \
        tzdata && apt-get upgrade -y && apt-get clean

RUN ln -s /usr/bin/python3 /usr/bin/python
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
EXPOSE 8080

COPY src /src
WORKDIR /src

ENTRYPOINT ["python", "app.py"]

マウントとか使えばもっとスマートにできるけど、とりあえずこれで動くからOKということで

requirements.txt

gradio==4.14.0

src/app.py

import gradio as gr
import os

with gr.Blocks() as demo:
    gr.Markdown("# This is App 1")
    gr.Button("Click me", variant="primary")

if __name__ == "__main__":
    if "APP_ENV" in os.environ.keys():
        demo.launch(server_port=8080, server_name="0.0.0.0")
    else:
        demo.launch()

gradio2

Dockerfile

gradio1と同じなので省略

requirements.txt

gradio1と同じなので省略

src/app.py

import gradio as gr
import os

with gr.Blocks() as demo:
    gr.Markdown("# This is App 2")
    gr.Button("Click me", variant="primary")

if __name__ == "__main__":
    if "APP_ENV" in os.environ.keys():
        demo.launch(server_port=8080, server_name="0.0.0.0")
    else:
        demo.launch()

streamlit1

Dockerfile

FROM ubuntu:22.04

RUN apt-get update
ENV TZ=Asia/Tokyo
ENV LANG=en_US.UTF-8
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apt-get install -yq --no-install-recommends python3-pip \
        python3-dev \
        build-essential \
        openssh-server \
        git \
        wget \
        tzdata && apt-get upgrade -y && apt-get clean

RUN ln -s /usr/bin/python3 /usr/bin/python
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
EXPOSE 8080

COPY src /src
WORKDIR /src

ENTRYPOINT ["streamlit", "run", "app.py", "--server.port=8080", "--server.address=0.0.0.0"]

requirements.txt

streamlit==1.30.0

src/app.py

import streamlit as st

def main():
    st.markdown("# This is App 3")
    st.button("Click me")

if __name__ == "__main__":
    main()

nginx

Dockerfile

FROM nginx:latest
COPY default.conf /etc/nginx/conf.d/default.conf
COPY index.html /var/www/html/index.html

default.conf

server {
  listen       80;
  server_name  localhost;

  location / {
    root /var/www/html;
    index index.html;
  }

  location /app1/ {
    proxy_pass http://host.docker.internal:30001/;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
  }

  location /app2/ {
    proxy_pass http://host.docker.internal:30002/;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
  }

  location /app3/ {
    proxy_pass http://host.docker.internal:30003/;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
  }
}

docker-compose.yamlでextra_hosts: - "host.docker.internal:host-gateway"を指定し、ホスト側のポートを読めるようにしておいて、default.confでhttp://host.docker.internal:30001/のようにするのがポイント。

index.html

<html>
    <body>
        <h1>App 1</h1>
        <a href="./app1/">link</a>
        <h1>App 2</h1>
        <a href="./app2/">link</a>
        <h1>App 3</h1>
        <a href="./app3/">link</a>
    </body>
</html>

参考情報



Shikoan's ML Blogの中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内

技術書コーナー

北海道の駅巡りコーナー


Add a Comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です