【问题标题】:Deploy Flask on Apache using mod_wsgi使用 mod_wsgi 在 Apache 上部署 Flask
【发布时间】:2017-01-30 04:16:26
【问题描述】:

我正在尝试部署一个我为部署在 Apache 服务器上而构建的 Flask Web 应用程序。我在 Raspberry Pi 3 上使用 Raspbian (Jessie) 操作系统。该应用程序在烧瓶内置开发 Web 服务器上运行完美,但我无法在 Apache 服务器上部署它。

这就是我所做的:

sudo apt-get update
sudo apt-get -y install python3 ipython3 python3-flask
sudo apt-get -y install apache2
sudo apt-get -y install libapache2-mod-wsgi-py3

conf文件为:/etc/apach2/sites-available/arduinoweb.conf:

<VirtualHost *>
 ServerName 10.0.0.20

 WSGIDaemonProcess arduinoweb user=pi group=pi threads=5
 WSGIScriptAlias / /var/www/ArduinoWeb/arduinoweb.wsgi

<Directory /var/www/ArduinoWeb/ArduinoWeb>
 WSGIProcessGroup arduinoweb
 WSGIApplicationGroup %{GLOBAL}
 WSGIScriptReloading On
 Require all granted
</Directory>

Alias /static /var/www/ArduinoWeb/ArduinoWeb/static
<Directory /var/www/ArduinoWeb/ArduinoWeb/static/>
Require all granted
</Directory>

Alias /temp /var/www/ArduinoWeb/ArduinoWeb/temp
<Directory /var/www/ArduinoWeb/ArduinoWeb/temp/>
Require all granted
</Directory>

Alias /templates /var/www/ArduinoWeb/ArduinoWeb/templates
<Directory /var/www/ArduinoWeb/ArduinoWeb/templates/>
Require all granted
</Directory>

ErrorLog ${APACHE_LOG_DIR}/error.log
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/access.log combined


</VirtualHost>
~

/var/www/Arduinoweb/arduinoweb.wsgi中的WSGI脚本文件:

import sys

if sys.version_info[0]<3:       # require python3
    raise Exception("Python3 required! Current (wrong) version: '%s'" % sys.version_info)

sys.path.insert(0, '/var/www/Arduinoweb/Arduinoweb')

from app import app as application

Apache的错误日志:

[Wed Sep 21 21:46:22.669633 2016] [wsgi:error] [pid 17681:tid 1972667440] [client 10.0.0.3:64819] mod_wsgi (pid=17681): Target WSGI script '/var/www/ArduinoWeb/arduinoweb.wsgi' cannot be loaded as Python module.
[Wed Sep 21 21:46:22.669971 2016] [wsgi:error] [pid 17681:tid 1972667440] [client 10.0.0.3:64819] mod_wsgi (pid=17681): Exception occurred processing WSGI script '/var/www/ArduinoWeb/arduinoweb.wsgi'.
[Wed Sep 21 21:46:22.670196 2016] [wsgi:error] [pid 17681:tid 1972667440] [client 10.0.0.3:64819] Traceback (most recent call last):
[Wed Sep 21 21:46:22.671185 2016] [wsgi:error] [pid 17681:tid 1972667440] [client 10.0.0.3:64819]   File "/var/www/ArduinoWeb/arduinoweb.wsgi", line 8, in <module>
[Wed Sep 21 21:46:22.671238 2016] [wsgi:error] [pid 17681:tid 1972667440] [client 10.0.0.3:64819]     from app import app as application
[Wed Sep 21 21:46:22.671406 2016] [wsgi:error] [pid 17681:tid 1972667440] [client 10.0.0.3:64819] ImportError: No module named 'app'

我不明白为什么它找不到应用程序。

这是python文件/var/www/Arduinoweb/Arduinoweb/app.py:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask import request, redirect, url_for, render_template, jsonify
from socket import *
from time import time
from threading import Timer
from datetime import datetime
import fileinput

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:Qazwsx@localhost/arduinoweb'
app.debug = True
db = SQLAlchemy(app)

class User(db.Model):

    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)
    email = db.Column(db.String(120), unique=True)

    def __init__(self, username, email):
        self.username = username
        self.email = email
    def __repr__(self):
        return '<user %r>' % self.username


class Temp(db.Model):
        __tablename__ = "Temp"
        id = db.Column("id", db.Integer, primary_key=True)
        Temp = db.Column("Temp", db.Integer)
        Date = db.Column("Date", db.Date)
        Time = db.Column("Time", db.Time)
        DateTime = db.Column("DateTime", db.String)

        def __init__(self, Temp, Date=None, Time=None, DateTime=None):
                self.Temp = Temp
                if Date is None:
                        Date = str(datetime.now()).split('.')[0]
                self.Date = Date
                if Time is None:
                        Time = str(datetime.now()).split('.')[0]
                self.Time = Time
                if DateTime is None:
                        DateTime = repr(datetime.now().replace(second=0, microsecond=0)).split('datetime.datetime',1)[1]
                self.DateTime = DateTime

class EC(db.Model):
        __tablename__ = "EC"
        id = db.Column("id", db.Integer, primary_key=True)
        EC = db.Column("EC", db.Float)
        Date = db.Column("Date", db.Date)
        Time = db.Column("Time", db.Time)
        DateTime = db.Column("DateTime", db.String)

        def __init__(self, EC, Date=None, Time=None, DateTime=None):
                self.EC = EC
                if Date is None:
                        Date = str(datetime.now()).split('.')[0]
                self.Date = Date
                if Time is None:
                        Time = str(datetime.now()).split('.')[0]
                self.Time = Time
                if DateTime is None:
                        DateTime = repr(datetime.now().replace(second=0, microsecond=0)).split('datetime.datetime',1)[1]
                self.DateTime = DateTime

class PH(db.Model):
        __tablename__ = "PH"
        id = db.Column("id", db.Integer, primary_key=True)
        PH = db.Column("PH", db.Float)
        Date = db.Column("Date", db.Date)
        Time = db.Column("Time", db.Time)
        DateTime = db.Column("DateTime", db.String)

        def __init__(self, PH, Date=None, Time=None, DateTime=None):
                self.PH = PH
                if Date is None:
                        Date = str(datetime.now()).split('.')[0]
                self.Date = Date
                if Time is None:
                        Time = str(datetime.now()).split('.')[0]
                self.Time = Time
                if DateTime is None:
                        DateTime = repr(datetime.now().replace(second=0, microsecond=0)).split('datetime.datetime',1)[1]
                self.DateTime = DateTime

class Humidity(db.Model):
        __tablename__ = "Humidity"
        id = db.Column("id", db.Integer, primary_key=True)
        Humidity = db.Column("Humidity", db.Integer)
        Date = db.Column("Date", db.Date)
        Time = db.Column("Time", db.Time)
        DateTime = db.Column("DateTime", db.String)

        def __init__(self, Humidity, Date=None, Time=None, DateTime=None):
                self.Humidity = Humidity
                if Date is None:
                        Date = str(datetime.now()).split('.')[0]
                self.Date = Date
                if Time is None:
                        Time = str(datetime.now()).split('.')[0]
                self.Time = Time
                if DateTime is None:
                        DateTime = repr(datetime.now().replace(second=0, microsecond=0)).split('datetime.datetime',1)[1]
                self.DateTime = DateTime

class HumidityRoots(db.Model):
        __tablename__ = "HumidityRoots"
        id = db.Column("id", db.Integer, primary_key=True)
        HumidityRoots = db.Column("HumidityRoots", db.Integer)
        Date = db.Column("Date", db.Date)
        Time = db.Column("Time", db.Time)
        DateTime = db.Column("DateTime", db.String)

        def __init__(self, HumidityRoots, Date=None, Time=None, DateTime=None):
                self.HumidityRoots = HumidityRoots
                if Date is None:
                        Date = str(datetime.now()).split('.')[0]
                self.Date = Date
                if Time is None:
                        Time = str(datetime.now()).split('.')[0]
                self.Time = Time
                if DateTime is None:
                        DateTime = repr(datetime.now().replace(second=0, microsecond=0)).split('datetime.datetime',1)[1]
                self.DateTime = DateTime


@app.route('/Sensors')
def sensors_function():
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto("GETSENSORS".encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return rec_data

@app.route('/OutputsState')
def outputs_state_function():
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto("GETOUTPUTSSTATE".encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return rec_data

@app.route('/WebModeState')
def web_mode_state_function():
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto("GETWEBMODE".encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return rec_data

@app.route('/PLCState')
def plcstatefunction():
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto("GETPLCSTATE".encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return rec_data

@app.route('/IrrigateOnOff')
def irrigate_on_off_function():
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto("IRRIGATEOnOff".encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return rec_data

@app.route('/SprinklersOnOff')
def sprinklers_on_off_function():
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto("SprinklersOnOff".encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return rec_data

@app.route('/SetDateTime' , methods=['POST'])
def set_date_time_function():
        completeAnswer =  "%s:%s:%s:%s:%s:%s:%s:%s" % ("SETDATETIME", request.form.get('dOw'), request.form.get('SetDate'), request.form.get('SetMonth'), request.form.get('SetYear'), request.form.get('SetHour'), request.form.get('SetMinute'), request.form.get('SetSeconds'))
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto(completeAnswer.encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return "ok"

@app.route('/SetIrrigation' , methods=['POST'])
def set_irrigation_function():
        completeAnswer =  "%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s" % ("SETIRRIGATION",request.form.get('SetIrrigationMode'), request.form.get('SetHumidityRangeMin'), request.form.get('SetHumidityRangeMax'), request.form.get('SetHour1'), request.form.get('SetHour1OnTime'), request.form.get('SetHour1OffTime'), request.form.get('SetHour2'), request.form.get('SetHour2OnTime'), request.form.get('SetHour2OffTime'), request.form.get('SetHour3'), request.form.get('SetHour3OnTime'), request.form.get('SetHour3OffTime'), request.form.get('SetHour4'), request.form.get('SetHour4OnTime'), request.form.get('SetHour4OffTime'))
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto(completeAnswer.encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return "ok"

@app.route('/SetEC' , methods=['POST'])
def set_EC_function():
        completeAnswer =  "%s:%s:%s:%s:%s" % ("SETEC", request.form.get('SetECRangeMin'), request.form.get('SetECRangeMax'), request.form.get('SetDoseEC'), request.form.get('SetECDelay'))
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto(completeAnswer.encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return "ok"

@app.route('/SetPH' , methods=['POST'])
def set_PH_function():
        completeAnswer =  "%s:%s:%s:%s:%s:%s" % ("SETPH", request.form.get('SetPHRangeMin'), request.form.get('SetPHRangeMax'), request.form.get('SetDosePHUp'), request.form.get('SetDosePHDown'), request.form.get('SetPHDelay'))
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto(completeAnswer.encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return "ok"

@app.route('/SetWaterTemp' , methods=['POST'])
def set_water_temp_function():
        completeAnswer =  "%s:%s:%s" % ("SETWATERTEMP", request.form.get('SetWaterTempRangeMin'), request.form.get('SetWaterTempRangeMax'))
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto(completeAnswer.encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return "ok"

@app.route('/SetSprinklers' , methods=['POST'])
def set_sprinklers_function():
        completeAnswer =  "%s:%s:%s:%s:%s" % ("SETSPRINKLERS", request.form.get('SetSprinklersBeginEndHoursBegin'), request.form.get('SetSprinklersBeginEndHoursEnd'), request.form.get('SetSprinklersOnTime'), request.form.get('SetSprinklersOffTime'))
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto(completeAnswer.encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return "ok"

@app.route('/SetAlerts' , methods=['POST'])
def set_alerts_function():
        completeAnswer =  "%s:%s:%s:%s:%s:%s" % ("SETALERTS", request.form.get('SetIrrigationThresholdAlert'), request.form.get('ECAlertOffset'), request.form.get('PHAlertOffset'), request.form.get('ResetCounterState'), request.form.get('AlertsState'))
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto(completeAnswer.encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return "ok"

@app.route('/')
def index():
        return render_template('index.html')

@app.route('/Charts')
def charts():
        return render_template('charts.html')

@app.route('/livechart')
def live_chart():
        return render_template('livechart.html')

@app.route('/TempQuery' , methods=['POST'])
def temp_query():
        answerDate = request.form.get('date')
        answerSensor = request.form.get('sensor')
        datafile = 'temp/TempByDateDbFile.txt'
        if answerSensor == 'Temp':
                DbTemp = Temp.query.filter_by(Date = answerDate).all()
                ## create the file from db
                file = open(datafile, 'w')
                for item in DbTemp:
                        file.write('{x: new Date' + str(item.DateTime) + ' , y: ' + str(item.Temp) + '},' + '\n')
                file.close()
        elif  answerSensor == 'EC':
                DbTemp = EC.query.filter_by(Date = answerDate).all()
                ## create the file from db
                file = open(datafile, 'w')
                for item in DbTemp:
                        file.write('{x: new Date' + str(item.DateTime) + ' , y: ' + str(item.EC) + '},' + '\n')
                file.close()
        elif  answerSensor == 'PH':
                DbTemp = PH.query.filter_by(Date = answerDate).all()
                ## create the file from db
                file = open(datafile, 'w')
                for item in DbTemp:
                        file.write('{x: new Date' + str(item.DateTime) + ' , y: ' + str(item.PH) + '},' + '\n')
                file.close()
        elif  answerSensor == 'Humidity':
                DbTemp = Humidity.query.filter_by(Date = answerDate).all()
                ## create the file from db
                file = open(datafile, 'w')
                for item in DbTemp:
                        file.write('{x: new Date' + str(item.DateTime) + ' , y: ' + str(item.Humidity) + '},' + '\n')
                file.close()
        elif  answerSensor == 'HumidityRoots':
                DbTemp = HumidityRoots.query.filter_by(Date = answerDate).all()
                ## create the file from db
                file = open(datafile, 'w')
                for item in DbTemp:
                        file.write('{x: new Date' + str(item.DateTime) + ' , y: ' + str(item.HumidityRoots) + '},' + '\n')
                file.close()
        ##replace "-" in ","
        f = open(datafile,'r')
        filedata = f.read()
        f.close()
        newdata = filedata.replace("-",", ")
        f = open(datafile,'w')
        f.write(newdata)
        f.close()
        return 'OK'

@app.route('/RenderTempChart' , methods=['POST' , 'GET'])
def render_temp_chart():
        datafile = 'temp/TempByDateDbFile.txt'
        with open(datafile, 'r') as myfile:
                file = myfile.read()
        return render_template('DbTemp.html', file = file)

@app.route('/Control' , methods=['POST' , 'GET'])
def control():
        return render_template('control.html')
"""
def update_data(interval):  # store in DB all sensors real time data
        Timer(interval, update_data, [interval]).start()
        SensorsAnswer = sensors_function().split()
        addTemp = Temp(int(SensorsAnswer[2]))
        addEC = EC(float(SensorsAnswer[0]))
        addPH = PH(float(SensorsAnswer[1]))
        addHumidity = Humidity(int(SensorsAnswer[3]))
        addHumidityRoots = HumidityRoots(int(SensorsAnswer[5]))
        db.session.add(addTemp)
        db.session.add(addEC)
        db.session.add(addPH)
        db.session.add(addHumidity)
        db.session.add(addHumidityRoots)
        db.session.commit()

update_data(300) # Store data in DB every x seconds
"""
if __name__ == "__main__":
        app.run()

即使使用这样的简单代码也无法正常工作(同样的错误):

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello world!"

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

我正在使用 python 3.4.2。我没有在虚拟环境中使用它。

文件夹结构:

app.py 位于/var/www/Arduinoweb/Arduinoweb/app.py arduinoweb.wsgi 位于/var/www/Arduinoweb/arduinoweb.wsgi

我确实启用了 VirtualHost arduinoweb.conf 并重新启动了 apach2 服务。

【问题讨论】:

  • 你有 SElinux 运行吗?
  • 你需要的目录中有__init__.py吗?
  • 如果应用程序在简单的烧瓶开发服务器中运行可能是它缺少 init.py 的一种方式?
  • SELinux 怎么样?
  • 没有启用 selinux。无论如何,问题出在路上..谢谢

标签: apache flask mod-wsgi


【解决方案1】:

首先,这是:

<Directory /var/www/ArduinoWeb/ArduinoWeb>
 WSGIProcessGroup arduinoweb
 WSGIApplicationGroup %{GLOBAL}
 WSGIScriptReloading On
 Require all granted
</Directory>

应该是:

<Directory /var/www/ArduinoWeb>
 WSGIProcessGroup arduinoweb
 WSGIApplicationGroup %{GLOBAL}
 Require all granted
</Directory>

从技术上讲,这个错误会导致两个问题。

首先,Apache 甚至不应该让你使用那个 WSGI 脚本,因为它没有权限。看起来您的 Apache 配置中的其他地方正在广泛访问您的文件系统,而实际上并不应该这样做。

我还删除了重新加载选项,因为无论如何都默认为打开并且不需要。

第二个是 WSGI 脚本不会在守护进程组的上下文中运行。这意味着代码将以嵌入式模式加载,并以 Apache 用户身份运行。如果您的文件的权限是只有pi 用户可以读取它们,那么它将无法访问app 模块。

将SeverName 设置为 IP 地址通常也是错误的。如果它正在工作,那么它只是这样做,因为这是 Apache 配置中的第一个 VirtualHost,因此当 Apache 无法正确进行基于名称的虚拟主机匹配时,它默认使用它。

不管怎样,看看有没有帮助,否则提供运行的输出:

ls -las /var/www/ArduinoWeb /var/www/ArduinoWeb/ArduinoWeb

因此可以检查目录/文件的所有权/权限。

【讨论】:

  • 试试你写的,同样的错误,我检查了所有权/权限,一切都好
  • 所以您假设您了解哪些所有权/权限是正确的? :-) 希望看到他们进行验证。您是否曾经检查过是否按照其他人的要求启用了 SELinux。通常如果在/var/www 下,即使启用了 SELinux,Apache 访问它也不应该有问题。
  • 4 drwxrwxrwx 6 pi pi 4096 Sep 24 11:36 。 4 drwxrwxrwx 3 pi pi 4096 9 月 24 日 11:04 .. 4 -rwxrwxrwx 1 pi 148 9 月 21 日 20:43 app2.py 16 -rwxrwxrwx 1 pi 15405 9 月 24 日 11:36 app.py 4 drwxr-xr-x 2 www-data www-data 4096 9 月 24 日 11:04 pycache 4 drwxrwxrwx 4 pi pi 4096 9 月 10 日 19:18 静态 4 drwxrwxrwx 2 pi pi 4096 9 月 10 日 19:18 temp 4 drwxrwxrwx 2 pi pi 4096 9 月 10 日 19:18 模板
  • 我将所有 dir 递归更改为 777 与 pi 所有者以避免权限问题,但只是想知道在实际生产 Apache 服务器中处理权限的正确方法是什么?
  • 您确定在默认主机配置文件中没有其他一些与 mod_wsgi 相关的配置吗? sites-enabled 目录中还有什么?它们都是符号链接吗?您没有不小心在sites-enabled 中创建了您的配置文件的单独副本并且内容不同?
【解决方案2】:

感谢您的帮助.. 最终这似乎只是 wsgi 脚本路径中的一个输入错误:“/var/www/ArduinoWeb/ArduinoWeb”而不是“/var/www/Arduinoweb/Arduinoweb”。

现在它似乎工作正常。谢谢!!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-09
    • 1970-01-01
    • 1970-01-01
    • 2012-12-20
    • 1970-01-01
    • 2012-12-19
    • 2012-06-17
    相关资源
    最近更新 更多