0
0

More than 1 year has passed since last update.

FastAPIにリクエストしてRで作ったグラフ画像を返すサーバーをDockerコンテナ(プロトタイプ第1号)

Last updated at Posted at 2021-12-25

初めに

題名の通り。もう少し改良は加えていきたいが、基本的なことはこの仕組みで実行できる。
Rのイメージにpythonをaptでインストールしている。他にもっと良いやり方はあるかも、、、

環境

Windows10 + Docker DeskTop(WSL2 - Ubuntu 20.04.3 LTS)

docker -v
Docker version 20.10.11, build dea9396

フォルダ構成

fastapi
 ├ Dockerfile
 └ app
   ├ main.py
   └ rexec
     └ graph.py

ファイル

Dockerfile
# R-4.1.2
FROM r-base:4.1.2

# ダウンロード先のミラーサイト設定(53のJapanを選択)
RUN R -e 'chooseCRANmirror(ind=53)'

# ggplot2のパッケージをインストール
RUN R -e 'install.packages("ggplot2")'
# shinyのパッケージをインストール
RUN R -e 'install.packages("shiny", dependencies = TRUE)'

# python-3.9
ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y python3.9 python3-pip python3-setuptools python3-dev

# pipのupgrade
RUN pip3 install -U pip

# fastapiとuvicornのインストール
RUN pip3 install fastapi uvicorn pyper

# ポートはデフォルトの8000で利用
EXPOSE 8000

# デフォルトのディレクトリ設定
WORKDIR /app

# サーバー起動
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import json
import uvicorn
import os
from rexec import graph

app = FastAPI()

# 許可するドメインを設定
origins = [
    # "http://domainname.com",
    # "https://domainname.com",
    "http://localhost",
    "http://localhost:3000",
]

# クロス設定
app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


@app.get("/api/graph/{type}")
def read_item(type: int):
    base64image = graph.getBase64Graph(type)
    return base64image

graph.py
import pyper
import os


def getBase64Graph(type: int = 1):

    r = pyper.R()
    r(
        """
        # ライブラリインポート
        library(ggplot2)
        library(shiny)
        """
    )
    # typeで出力するグラフを切り替え
    if type == 1:
        r(
            """
            p <- ggplot(data = iris,
                mapping = aes(x=Sepal.Width)) +
                geom_density(fill = "gold")
            """
        )
    if type == 2:
        r(
            """
            p <- ggplot(data = iris,
                        mapping = aes(x=Species, y=Petal.Length, fill=Species)) +
                        geom_boxplot()
            """
        )
    if type == 3:
        r(
            """
            p <- ggplot(data = iris,
                        mapping = aes(x=Sepal.Length, y=Petal.Length)) +
                        geom_point(aes(colour=Species)) +
                        geom_smooth(method = "lm", aes(colour=Species))
            """
        )
    # 画像出力&base64変換(sample.pngはアクセスorユーザー毎等でユニークな名前にすることを推奨)
    filename = "sample.png"
    r(
        """
        # 画像出力(サイズは dpi*width × dpi*height のピクセルサイズとなる)
        ggsave(filename = "$filename",
            plot = p,
            device = "png",
            width = 6.4, height = 4.8,
            dpi = 100)

        # base64変換
        b64image <- base64enc::dataURI(file = "$filename", mime = "image/png")
        """.replace(
            "$filename", filename
        )
    )
    # base64で取得
    res = r.get("b64image")
    # ファイル削除
    os.remove(filename)

    return res

コンテナ構築シェル

# dockerファイルのイメージビルド
docker build -t pyrimage .
# コンテナ化(-v appのフォルダ共有、-p 8000ポート使用)
docker run -d --name pyrgraph -v $PWD/app:/app -p 8000:8000 pyrimage

確認方法

以下のようなReactのページを用意
※axiosはnpmでインポートしてください

App.jp
import { React, useState, useEffect } from "react";
import axios from "axios";

const App = () => {
  const [base64image, setBase64image] = useState(null);
  const baseUrl = "http://localhost:8000/api/graph/";

  const getBase64Image = async (getNo) => {
    const responce = await axios.get(`${baseUrl}${getNo}`);
    setBase64image(responce.data);
  };

  return (
    <>
      <div>
        <button onClick={(e) => getBase64Image(1)}>Graph1</button>
        <button onClick={(e) => getBase64Image(2)}>Graph2</button>
        <button onClick={(e) => getBase64Image(3)}>Graph3</button>
      </div>
      {base64image !== null ? <img src={base64image} /> : null}
    </>
  );
};
export default App;

こんな感じ

graph.gif

最後に

image作成に時間がかかるのがイマイチ、、shinyのインストールは課題。base64にしたいだけだからpythonでやった方が良いかも、、
データをポストして、内容に応じたグラフを描画とかしたい。
個人でグラフ画像とか引っ張りたいときは、Jupyterとか使うと簡単にできるんだろうけどね、、

参考

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0