Running Python Flask on Bluehost WordPress Hosting Service

Overview

안녕하세요. 오늘은 약간 편법적인 방법으로 Python서버를 Bluehost의 WordPress 호스팅플랜으로 한번 돌려보려고 해요. 성공여부는 모르겠지만 일단 한번 도전해볼게요!

Connect SSH

일단 SSH로 서버에 접속합니다. 블루호스트의 웹호스팅으로 SSH서버에 접속하는 방법은 여기에 있습니다.

ssh 아이디@도메인.com -p 2222 -i ~/.ssh/bluehost/id_rsa

Python 3.12.8 (권한없음)

Python 버젼 확인

파이썬 버젼을 확인해볼까요? 버젼이 너무 오래되었네요.

# python --version
Python 2.7.5
# python3 --version
Python 3.6.8

파이썬 3.12.8 설치시도 (C compiler없음)

오늘 날짜(2025년 1월 13일)로 가장 최근 버전의 파이썬은 3.12.8입니다.

mkdir ~/python

cd ~/python

wget http://www.python.org/ftp/python/3.12.8/Python-3.12.8.tgz

tar zxfv Python-3.12.8.tgz

find ~/python -type d | xargs chmod 0755

cd Python-3.12.8

./configure --prefix=$HOME/python

설치를 위한 C컴파일러가 서버에 없네요.

configure: error: in `/home/__HOMEDIR__/python/Python-3.12.8':
configure: error: no acceptable C compiler found in $PATH
See `config.log' for more details

C Compiler 권한요청

에러수정 안내를 보니까 cPanel사용자가 C compiler group에 속해있지 않다네요.

If the configuration error shown above occurs when you attempt to configure Python with ./configure –prefix=$HOME/python, this means that your cPanel user is not added to the C compiler group on the server. Contact our Support at 888-401-4678 to request you be added to the Compiler group.

888-401-4678에 전화를 해보도록 하겠습니다. 고객센타에서 제 cpanel 계정을 C compiler group에 넣어주었고, 파이썬 버젼도 업그레이드 해주었다네요. 확인해보도록하겠습니다. python은 여전히 2.7이고 대신 python3이 3.6이네요.

$ python --version
Python 2.7.5
$ python3 --version
Python 3.6.8

Python 3.12.8 설치 재시도 (권한없음)

파이썬 설치파일을 실행해볼까요?

./configure --prefix=$HOME/python

권한이 없다고 에러가 납니다.

Python 3.6.8로 서비스 되는지 (되긴 됨)

Run Hello World with Python 3.6.8

일단 버젼 업그레이드는 나중에 하고 2.7은 너무 out dated니까 3.6버젼으로 시도를 계속 해보도록 하겠습니다.

hello_world.py 파이썬 파일을 하나 만들게요.

print("Hello, World!")

파일이 실행은 됩니다.

# python3 hello_world.py
Hello, World!

Run Server with Python 3.6

서버가 띄워지는지 한번 볼까요? Python2는 안되네요.

# python -m http.server
/usr/bin/python: No module named http

Python3로 시도해볼게요.

# python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

8000포트로 서버가 떴어요.

Run Flask with Python 3.6

이번에는 Falsk프레임웤으로 서버가 실행이 되는지 해볼게요

app.py라는 이름으로 파일을 하나 생성하세요.

from flask import Flask

app = Flask(__name__)

@app.route('/')
def home():
    return '<h1>Flask REST API</h1>'

if __name__=="__main__":
    app.run(debug=True)

해당 앱을 실행해 볼까요? Flask가 없다네요

$ flask --app app
-jailshell: flask: command not found

PIP Install Flask 2.0.3 (Pip install 권한 있음)

pip설치 권한이 있는지 모르겠는데 한번 시도해볼게요. 놀랍게도 설치가 잘되네요. 참고로 제 cpanel 계정을 C compiler group에 추가해주기 전에는 pip install 권한이 없었습니다. 일찌감치 권한요청을 하길 잘한거 같아요.

# pip3 install Flask
Successfully installed Flask-2.0.3 Jinja2-3.0.3 MarkupSafe-2.0.1 Werkzeug-2.0.3 click-8.0.4 dataclasses-0.8 itsdangerous-2.0.1

Encoding문제 해결

그러면 다시 Flask 서버를 실행해볼게요. 에러가 나네요. 파이썬이 ASCII엔코딩을 사용하도록 설정이 되어 있대요.

# flask --app app
RuntimeError: Click will abort further execution because Python was configured to use ASCII as encoding for the environment. Consult https://click.palletsprojects.com/unicode-support/ for mitigation steps.

해당 설정을 바꿔볼게요.

# export PYTHONIOENCODING=utf-8
# echo $PYTHONIOENCODING
utf-8

Consult해준대로 Locale과 언어도 바꿔볼까요?

# locale charmap
ANSI_X3.4-1968
# export LC_ALL=en_US.UTF-8
# echo $LC_ALL
en_US.UTF-8
# export LANG=en_US.UTF-8
# echo $LANG
en_US.UTF-8
# locale charmap
UTF-8

그리고 파이썬 파일의 캐릭터셋도 바꿀게요. 일단 현재 파일을 캐릭터셋을 확인합니다.

# file -i app.py
app.py: text/x-python; charset=us-ascii

ASCII를 UTF-8로 바꿔볼게요. 그런데 바로는 안바뀌네요.

# iconv -f us-ascii -t utf-8 app.py > app-utf8.py
# file -i app-utf8.py
text/x-python; charset=us-ascii

그러면 UTF-16으로 변환을 했다가 다시 UTF-8로 변환을 시도해보도록 하겠습니다.

# iconv -f us-ascii -t utf-16 app.py > app-utf16.py
# file -i app16.py
app16.py: text/x-python; charset=utf-16le
# iconv -f utf-16le -t utf-8 app-utf16.py > app-utf8.py
# file -i app8.py
app8.py: text/x-python; charset=utf-8

Run Flask 2.0.3 Server

이제 Flask서버를 다시 실행해보겠습니다. --app옵션이 없다고 나오네요.

# flask --app app-utf8.py
Usage: flask [OPTIONS] COMMAND [ARGS]...
Try 'flask --help' for help.

Error: No such option: --app

파이썬 3.6버젼에서 설치할 수 있는 Flask버젼은 2.0.3이 최선이었나봅니다.

# flask --version
Python 3.6.8
Flask 2.0.3
Werkzeug 2.0.3

그러면 2.0.3버젼에서 실행하던 명령으로 실행해야겠네요

# flask run
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

일단 로컬에서는 실행이 잘되었습니다

# curl http://127.0.0.1:5000/
<h1>Flask REST API</h1>

Make PHP Wrapping Service

Define Flask Routes

PHP Wrapping Service를 만들기 전에 충분한 테스트를 위하여 Flask에 Route을 몇개 더 만들어 볼게요.

from flask import Flask

app = Flask(__name__)

@app.route('/')
def home():
    return '<h1>Flask REST API</h1>'

@app.route('/profile')
def profile():
    return '<h1>Profile</h1>'

@app.route('/business')
def business():
    return '<h1>Business</h1>'

if __name__=="__main__":
    app.run(debug=True)

혹시 SSH를 나갔다 다시 들어왔다면 UTF-8로 설정한 값들이 없어져 있을거에요. 앱을 실행하기 전에 다시 설정해주셔야합니다. 매번 이렇게 해줄수는 없으니 권한 문제가 해결되면 Python과 Flask버젼을 업그레이드 하도록 하겠습니다.

# export PYTHONIOENCODING=utf-8
# export LC_ALL=en_US.UTF-8
# export LANG=en_US.UTF-8
# locale charmap
UTF-8
# file -i app.py
app.py: text/x-python; charset=utf-8

Flask를 실행하고 각 Route를 테스트합니다.

# flask run
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [24/Jan/2025 13:58:26] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [24/Jan/2025 13:58:31] "GET /profile HTTP/1.1" 200 -
127.0.0.1 - - [24/Jan/2025 13:58:35] "GET /business HTTP/1.1" 200 -

# curl http://127.0.0.1:5000/
<h1>Flask REST API</h1>
# curl http://127.0.0.1:5000/profile
<h1>Profile</h1>
# curl http://127.0.0.1:5000/business
<h1>Business</h1>

.htaccess

PHP코드를 하기전에 .htaccess에 Route을 설정할게요.

RewriteEngine On
RewriteRule ^profile index.php?c=profile [L,QSA]
RewriteRule ^business index.php?c=business [L,QSA]

PHP Wrapper App

그러면 이제 PHP로 Corresponding URL을 호출하는 앱을 만들어 볼게요. index.php를 생성합니다.

<?php
$path = $_GET['c'];
$url = "http://127.0.0.1:5000/".$path;
echo file_get_contents($url);
?>

자 이제 최종테스트를 해볼까요? 로컬 터미널에서 해당 서비스를 호출합니다.

$ curl https://__YOUR_DOMAIN__/profile
<h1>Profile</h1>
$ curl https://__YOUR_DOMAIN__/business
<h1>Business</h1>

오래된 버젼으로 어찌어찌 서비스가 되기는 합니다만 이렇게 서비스를 하는 것은 추천드리는 바는 아닙니다. 부득이한 경우에 사용할 수는 있지만 안정적으로 사용할 수는 없을 것 같습니다. 특히 문제가 되는것은 Python버젼이 너무 낮아서 Flask를 최신 버젼으로 설치하지 못하는 것인데 그렇게 되면 OEM등의 기능을 전혀 사용하지 못하게 되어 코드가 너무 낡아져 버립니다. 그래도 이게 되는지 확인하고 싶었고, 불편한 수준에 그쳤지만 어쨌든 Rough하게 가능하다는 거는 확인했으니까 이제 버젼 업그레이드 하는 문제만 해결하면 되겠네요.

Python 3.12.8 재시도 (성공)

Python 3.12.8 재설치 (권한받음)

Bluehost에 연락해서 파이썬 버젼을 업그레이드해달라고 부탁했습니다. 제가 권한이 없어서 직접 설치를 하지 못한다고 했더니 권한을 부여해주더라구요. 대신 서버에 있는 버젼은 업그레이드 할수 없고, 제 Home디렉토리에 설치해서 Alias로 우회하는 방법을 사용해야합니다. 오히려 그게 그들에게도 저에게도 안전한것 같아요. 서버의 파이썬 버젼이 바뀌어도 제 폴더의 파이썬 버젼은 제가 업그레이드할때까지 그대로 일테니까요. 너무 잘된것 같습니다.

그럼 다시 일전에 받아두었던 최신버젼을 설치해보도록하겠습니다. 고객센터에서 참고하라고 보내준 링크입니다. 지난번에 Python-3.12.8를 다운받아서 압축을 해제하는 것 까지는 했으니까 오늘은 configure부터 실행하도록 하겠습니다.

mkdir ~/python

cd ~/python

wget http://www.python.org/ftp/python/3.12.8/Python-3.12.8.tgz

tar zxfv Python-3.12.8.tgz

find ~/python -type d | xargs chmod 0755

cd Python-3.12.8

./configure --prefix=$HOME/python

make

make install

위의 configure명령를 실행하겠습니다.

# ./configure --prefix=$HOME/python
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking for Python interpreter freezing... ./_bootstrap_python
checking for python3.12... no
...
Checked 111 modules (31 built-in, 71 shared, 1 n/a on linux-x86_64, 1 disabled, 7 missing, 0 failed on import)

그럭저럭 잘 실행이 되네요. 그러면 이번에는 make을 실행하도록 하겠습니다.

# make
gcc -std=gnu11 -pthread -c -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O3 -Wall    -std=c11 -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wstrict-prototypes -Werror=implicit-function-declaration -fvisibility=hidden  -I./Include/internal  -I. -I./Include    -DPy_BUILD_CORE -o Programs/python.o ./Programs/python.c
...
Checked 111 modules (31 built-in, 71 shared, 1 n/a on linux-x86_64, 1 disabled, 7 missing, 0 failed on import)

일단 에러는 안났으니까. 그러면 이번엔 진짜 설치를 하도록하겠습니다.

# make install
Creating directory /home/__HOMEDIR__/local/python/bin
Creating directory /home/__HOMEDIR__/local/python/lib
...
WARNING: The scripts pip3 and pip3.12 are installed in '/home/__HOMEDIR__/local/python/bin' which is not on PATH.
Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed pip-24.3.1

성공적으로 Python 3.12.8을 설치했습니다.

Make venv with Python 3.12.8

개발에 앞서 해당 프로젝트 폴더에 들어가서 가상환경을 만듭니다. 그래야 다른 프로젝트랑 패키지버젼이 conflic되지 않을테니까요.

# cd ~/www/__PROJECT_DIR__
# ~/python/bin/python3 -m venv .venv
# source ~/www/__PROJECT_DIR__/.venv/bin/activate
(.venv)#

나중에 편리하게 사용하기 위해서 alias를 만들겠습니다.

alias workon="source ~/www/__PROJECT_DIR__/.venv/bin/activate"
alias workoff="deactivate"

PIP Install Flask (SSL기능 안됨)

이제 Flask를 최신버젼으로 설치해볼까요?

(.venv)# pip install Flask
WARNING: Disabling truststore since ssl support is missing
WARNING: pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available.
...
ERROR: No matching distribution found for Flask

SSL이 왜 안되는지 좀 찾아봐야겠어요.

Install OpenSSL 3.4.0

찾아보니까 새로 설치한 최신버젼의 파이썬이 OpenSSl 1.0.2가 너무 오래되서 더이상 지원하지 않는다고 하네요.

# openssl version
OpenSSL 1.0.2k-fips  26 Jan 2017

현재 서버에 설치되어 있는 OpenSSL 버젼은 1.0.2인데 Python 3.12.8을 사용하려면 최소한 OpenSSL 1.1.1이 설치되어있어야합니다. 일단 제가 이 서버에 루트권한이 없으니까 새로운 버젼의 OpenSSL을 홈디렉토리에 설치하고 해당 OpenSSL을 이용해서 Python을 재설치하면 해결이 될것같아요. 현재시점 OpenSSL의 최신버젼은 3.4.0입니다. OpenSSL 3.4.0을 다운받을게요.

wget https://github.com/openssl/openssl/releases/download/openssl-3.4.0/openssl-3.4.0.tar.gz

다운받은 압축파일을 해제합니다.

tar zxfv openssl-3.4.0.tar.gz

압축이 풀린 폴더를 실행가능하게 권한을 변경할게요

find ~/python -type d | xargs chmod 0755

해당 폴더에 들어가세요

cd openssl-3.4.0

그리고 실행전 필요한 설정을 합니다. 여기서 설정할 것은 prefix라고 최종적으로 설치를 할 폴더를 명시합니다. 그리고 openssldir도 같은 폴더위치를 값으로 할당합니다. prefix와 openssldir은 절대경로로 적어주셔야 설치파일이 실행 됩니다. 공용라이브러리를 사용하지 않을거니까 config할때 shared zlib는 명시하지 않습니다.

./config --prefix=$HOME/local/ssl --openssldir=$HOME/local/ssl

위에서 설정한 대로 설치파일을 컴파일하도록 하겠습니다.

make

컴파일이 완료되면 설치를 진행하기 전에 에러가 없는지 점검을 해본 뒤에 진행할게요

make test

문제가 없으면 실제 설치를 진행합니다.

make install

설치가 완료되면 이제 HOME디렉토리에 설치한 OpenSSL를 실행할건데 이때 OpenSSL을 실행하면 libssl.so.3 모듈을 공용폴더에서 찾으려고 할거에요. 이게 HOME디렉토리에 있다고 미리 알려줘야 OpenSSL이 libssl.so.3를 찾아서 실행할 수가 있어요. 아래와 같이 LD_LIBRARY_PATH경로에 새로 설치한 OpenSSL 라이브러리를 추가합니다.

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HOME/local/ssl/lib64

방금 설치한 OpenSSL의 버젼을 확인해볼까요

# $HOME/local/ssl/bin/openssl version -a
OpenSSL 3.4.0 22 Oct 2024 (Library: OpenSSL 3.4.0 22 Oct 2024)
built on: Mon Jan 27 21:52:41 2025 UTC
platform: linux-x86_64
options:  bn(64,64)
compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -O3 -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_BUILDING_OPENSSL -DZLIB -DNDEBUG
OPENSSLDIR: "$HOME/local/ssl"
ENGINESDIR: "$HOME/local/ssl/lib64/engines-3"
MODULESDIR: "$HOME/local/ssl/lib64/ossl-modules"
Seeding source: os-specific
CPUINFO: OPENSSL_ia32cap=0xfffa32034f8bffff:0x818d19e4fbb

    Python 3.12.8 재설치 with OpenSSL 3.4.0

    OpenSSL경로가 바뀌었기때문에 파이썬을 재설치해야합니다. 아까 다운받아서 풀어놓은 Python-3.12.8폴더에 들어가서 configure를 하는데 이번엔 뒤에 with-openssl플래그를 주어서 새로 설치되는 파이썬이 우리가 방금 홈에 설치한 OpenSSL을 사용하도록 합니다.

    설치폴더에 들어가세요

    cd Python-3.12.8

    설치를 진행하기 전에 OpenSSL의 변경된 경로를 LDFLAGS와 CPPFLAGS를 설정함으로써 파이썬에 전달할거에요.

    export LDFLAGS="-L$HOME/local/ssl/lib64"
    export CPPFLAGS="-I$HOME/local/ssl/include"

    configure를 할때 –with-openssl와 -with-openssl-rpath에 OpenSSL경로를 명시합니다.

    ./configure --prefix=$HOME/local/python --with-openssl=$HOME/local/ssl --with-openssl-rpath=$HOME/local/ssl/lib64

    그리고 make, make install을 실행하여 파이썬을 설치합니다.

    # make
    ...
    # make install
    ...
    Successfully installed pip-24.3.1

    Make new venv with new Python location

    제가 OpenSSL지원하는 버젼을 ~/local에 새롭게 설치했기 때문에 기존에 ~/python에서 Python을 갖다 쓰던 virtual env는 SSL을 사용할 수가 없습니다. venv을 새로 설치한 Python을 가지고 다시 만들어야합니다. 헷갈리니까 ~/python에 있는 파일들은 전부 삭제하도록 하겠습니다.

    일단 workoff를 실행해서 venv에서 나옵니다.

    # workoff

    그리고 프로젝트 폴더에 들어가서 venv폴더를 삭제합니다.

    # cd ~/www/__PROJECT_DIR__
    # rm -rf .venv

    그리고 새로 설치한 SSL이 지원되는 파이썬을 가지고 다시 venv를 만들어 줍니다.

    # ~/local/python/bin/python3 -m venv .venv

    기존에 정의한 alias는 그대로 사용할 수 있습니다.

    # workon
    (.venv) #

    이제 파이썬이 ssl모듈을 지원하는지 확인해 볼게요

    python -m ssl

    PIP install Flask

    드디어 원점으로 돌아온 느낌입니다. Flask패키지를 설치해볼게요.

    # pip install Flask
    ...
    Successfully installed Flask-3.1.0 Jinja2-3.1.5 MarkupSafe-3.0.2 Werkzeug-3.1.3 blinker-1.9.0 click-8.1.8 itsdangerous-2.2.0
    # pip list
    Package      Version
    ------------ -------
    blinker      1.9.0
    click        8.1.8
    Flask        3.1.0
    itsdangerous 2.2.0
    Jinja2       3.1.5
    MarkupSafe   3.0.2
    pip          24.3.1
    Werkzeug     3.1.3

    너무 잘 되네요. 우여곡절끝에 해낸것 같습니다 ㅎㅎ 코딩하면서 또 문제가 생기겠지만 그때마다 하나씩 풀어나가면 해결이 되겠지요. 오늘은 여기까지 하겠습니다. 수고하셨습니다.

    References