Azure

StreamlitをAzure App Serviceで動かす(1)

前回はDockerでStreamlit+Nginx+OAuth2 Proxyを連携させるシステムを構築しました。
次はこのシステムをAzure App Serviceで動かしてみようと思います。
2021年12月5日現在では、Azure App Seriveの複数コンテナアプリはまだプレビュー段階で、docker-composeのnetworksが使えないなどの制限が多いため、ここではこれまで3つに分かれていたコンテナを1つにまとめた上でDeployするという方針で行きます。
今回は、まずコンテナを1つにまとめるところまでをやりたいと思います。

環境

Windows 10 Pro
Docker Desktop 4.1.1
Visual Studio Code 1.62.2 + Docker extension

Dockerfile作成

3つのコンテナを1つにまとめる方法ですが、ベースとしてNginxの公式イメージを使い、そこにPython(Streamlit)とOAuth2 Proxyをインストールするというのがシンプルそうなので、この方針で行きます。
Dockerfileは以下のようになります。

FROM nginx

RUN apt-get update && \
    apt-get install -y python3.9 && \
    apt-get install -y python3-pip && \
    apt-get install -y wget

ARG VERSION=v7.2.0
ARG FILENAME=oauth2-proxy-${VERSION}.linux-amd64

RUN wget -q https://github.com/oauth2-proxy/oauth2-proxy/releases/download/${VERSION}/${FILENAME}.tar.gz \
 && tar xzf ${FILENAME}.tar.gz \
 && mv ${FILENAME}/oauth2-proxy /bin/ \
 && rm -rf ${FILENAME}*
 
RUN pip install streamlit

1行目でベースイメージをnginxとしています。
続けて、pythonやwgetなど必要なライブラリをインストールします。
その次に、OAuth2 Proxyのバイナリを取得し、/bin以下に配置します。
最後に、streamlitをインストールしています。

インストールするものは以上なのですが、このまま動かしてもNginxが起動するだけで、OAuth2 ProxyやStreamlitは動きません。
それは、entrypointというコンテナ起動時に動くプログラムがNginxを起動するだけだからです。

そこで、Nginx、OAuth2 Proxy、Streamlitの3つを動かすためのラッパースクリプトを用意し、それをentrypintに指定していきます。
新たにmy_wrapper_script.shというファイルを追加し、その中身を以下のようにして保存します。

#!/bin/bash

/docker-entrypoint.sh nginx
  
streamlit hello &

/bin/oauth2-proxy --cookie-secure=false &

wait -n

exit $?

最初にnginxを起動しています。もともとのnginxの公式イメージのentrypointをそのまま利用しています。
次にstreamlitのデモプログラムを起動します。コマンドの最後に&をつけることでバックグラウンド起動になりますので、スクリプトはここで止まらず次に続きます。
その後にOAuth2 Proxyを起動します。同様に&を最後につけます。

最後にwaitコマンドでコンテナが終了しないようにします。
waitコマンドは-nを付けることで、バックグラウンドプロセスのいずれかの終了まで待つようになります。

Dockefileに以下の2行を追加して、entrypointを書き換えます。

COPY my_wrapper_script.sh my_wrapper_script.sh

ENTRYPOINT ./my_wrapper_script.sh

最後にNginxの設定ファイルを修正します。
3つのプログラムが同一コンテナ上で動くことになるので、すべてlocalhostでアクセスするようにします。

events {
    worker_connections  16;
}
http {
    server {
        listen 80;
        server_name localhost;

        location /oauth2/ {
            proxy_pass       http://localhost:4180;
            proxy_set_header Host                    $host;
            proxy_set_header X-Real-IP               $remote_addr;
            proxy_set_header X-Scheme                $scheme;
            proxy_set_header X-Auth-Request-Redirect $request_uri;
        }

        location = /oauth2/auth {
            proxy_pass       http://localhost:4180;
            proxy_set_header Host             $host;
            proxy_set_header X-Real-IP        $remote_addr;
            proxy_set_header X-Scheme         $scheme;
            proxy_set_header Content-Length   "";
            proxy_pass_request_body           off;
        }

        location / {
            auth_request /oauth2/auth;
            error_page 401 = /oauth2/sign_in;
            proxy_pass http://localhost:8501/;
        }
        location ^~ /static {
            proxy_pass http://localhost:8501/static/;
        }
        location ^~ /healthz {
            proxy_pass http://localhost:8501/healthz;
        }
        location ^~ /vendor {
            proxy_pass http://localhost:8501/vendor;
        }
        location /stream {
            proxy_pass http://localhost:8501/stream;
            proxy_http_version 1.1;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $host;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_read_timeout 86400;
        }        
    }
}

Dockerfileに以下の行を追加して、Nginxの設定ファイルもコピーします。

COPY ./nginx/etc/nginx.conf /etc/nginx/nginx.conf

Dockerfileの完成形は以下のようになります。

FROM nginx

RUN apt-get update && \
    apt-get install -y python3.9 && \
    apt-get install -y python3-pip && \
    apt-get install -y wget

ARG VERSION=v7.2.0
ARG FILENAME=oauth2-proxy-${VERSION}.linux-amd64

RUN wget -q https://github.com/oauth2-proxy/oauth2-proxy/releases/download/${VERSION}/${FILENAME}.tar.gz \
 && tar xzf ${FILENAME}.tar.gz \
 && mv ${FILENAME}/oauth2-proxy /bin/ \
 && rm -rf ${FILENAME}*
 
RUN pip install streamlit

COPY my_wrapper_script.sh my_wrapper_script.sh
COPY ./nginx/etc/nginx.conf /etc/nginx/nginx.conf

ENTRYPOINT ./my_wrapper_script.sh

ファイル構成はこのようになります。

ローカル環境での動作確認

ではローカル環境で動かして動作確認したいと思います。
Dockerfileを選択し、右クリックからBuild Imageを選択して、イメージを作成します。

イメージが作成出来たら、コンテナを起動しますが、これまでdocker-compose.ymlで指定していたパラメータをコンテナ起動時に指定する必要があります。
VSCodeでTerminalを開き、以下のコマンドをたたきます。

docker run --rm -it  -p 80:80 -e OAUTH2_PROXY_COOKIE_SECRET=クッキーシークレット -e OAUTH2_PROXY_CLIENT_ID=クライアントID -e OAUTH2_PROXY_CLIENT_SECRET=クライアントシークレット -e OAUTH2_PROXY_COOKIE_EXPIRE=30m -e OAUTH2_PROXY_REDIRECT_URL=http://localhost/oauth2/callback -e OAUTH2_PROXY_EMAIL_DOMAINS=* streamlitnginx:latest

コンテナが起動したら、ブラウザでhttp://localhostにアクセスします。
Googleの認証後にStreamlitのデモ画面が見れればローカル環境での検証は完了です。


次回は、Azure App ServiceにDeployしてみます。