Heroku部署Flask项目经历

记录heroku部署flask项目的踩坑经历,比较详细。

花了好多天,折腾了很久终于成功,还好没有半路放弃。

Flask博客项目地址,部分功能改善中,欢迎star/fork/issue

3天时间,不下15次了,各种坑都踩一下,啊啊啊终于部署成功了。。大体分4个部分:

  • heroku部署过程
  • 部署踩坑及解决
  • 资料及博客参考
  • 思考与总结



一、heroku部署具体过程

0.heroku注册

  • 首先到heroku进行注册,需要点击 最下面的图片 验证你不是机器人,正常情况下图片不显示。所以需要 fanqiang,代理或者vpn都可以,这里不赘述。
  • 邮箱最好用 gmail,或者国外的,国内的qq/163等貌似是不支持的
  • 注册时如果总提交不了,很大可能 密码强度不够,你需要把密码改得“刁钻”些


1.下载heroku CLI

其实就是Heroku Toolbelt,方便我们用 命令行来管理 在heroku上的程序,具体安装见 这里


2.创建git项目

进入myblog项目,激活 虚拟环境

lucy@ubuntu:~$cd project myblog
lucy@ubuntu:~$source venv/bin/activate
(venv)lucy@ubuntu:~$

初始化 git项目 ,并commit到git仓库

(venv)lucy@ubuntu:~$ git init
(venv)lucy@ubuntu:~$ git add .
(venv)lucy@ubuntu:~$ git commit -m"v1.0"


3.flask_app相关文件

3.1 首先是 requirements.txt

这里包含了所有依赖包。记得最后pip install psycopg2,并把 psycopg2 加入文件中。

/* requirements.txt */
alembic==0.9.9
bleach==2.1.3
blinker==1.4
click==6.7
dominate==2.3.1
Flask==0.12.2
Flask-Admin==1.5.1
Flask-Bootstrap==3.3.7.1
Flask-Login==0.4.1
Flask-Mail==0.9.1
Flask-Migrate==2.1.1
Flask-Moment==0.6.0
Flask-Script==2.0.6
Flask-SimpleMDE==0.3.0
Flask-SQLAlchemy==2.3.2
Flask-SSLify==0.1.5
Flask-WTF==0.14.2
gunicorn==19.7.1
html5lib==1.0.1
itsdangerous==0.24
Jinja2==2.10
Mako==1.0.7
Markdown==2.6.11
MarkupSafe==1.0
psycopg2==2.7.4
psycopg2-binary==2.7.4
python-dateutil==2.7.2
python-editor==1.0.3
six==1.11.0
SQLAlchemy==1.2.6
visitor==0.1.3
webencodings==0.5.1
Werkzeug==0.14.1
WTForms==2.1

3.2 其次是 Procfile 文件

里面保存了通过gunicorn这个web服务器运行的程序。:号后面的是具体程序app,前面是程序所在文件manage.py

/* Procfile */
web: gunicorn manage:app

3.3 紧接着 runtime.txt 文件

这个文件指明你在heroku的app上,所用的Python版本。
其实heroku默认只支持 Python-3.6.4Python-2.7.14 两个版本,你也可以添加 runtime.txt,在里面指定具体的Python版本。当然heroku还是会“任性地”就近原则,给你安装上面两个版本之一233…具体可见文档 Heroku Python Support 和文档 Specifying a Python Runtime

/* runtime.txt */
/* 比如我这里写的2.7.13版本,最后heroku 还是装的2.7.14 */
python-2.7.13

3.4 最后就是 .gitignore 文件

把不想要被git追踪的文件放入,例如日志文件、生产环境文件等。特别要注意的,是装有环境变量的.env文件

/* .gitignore */
venv/
log/
*.sqlite
*.log
*.swp
*.pyc
*.cpf
.env


4、heroku创建app

4.1 首先 登陆 heroku,输入电子邮件、密码

(venv) lucy@ubuntu:~/projects/myblog$ heroku login

Enter your Heroku credentials:
Email: xxxxxx@gmail.com
Password: **********
Logged in as xxxxxx@gmail.com

4.2 然后创建项目,这里取名 wakingup

(venv) lucy@ubuntu:~/projects/myblog$ heroku create wakingup

Creating ⬢ wakingup... done
https://wakingup.herokuapp.com/ | https://git.heroku.com/wakingup.git

4.3 接着配置postgresql数据库

这里操作和书上已经不一样了,需要注意。

(venv) lucy@ubuntu:~/projects/myblog$ heroku addons:create heroku-postgresql:hobby-dev
Creating heroku-postgresql:hobby-dev on ⬢ wakingup... free
Database has been created and is available
! This database is empty. If upgrading, you can transfer
! data from another database with pg:copy
Created postgresql-silhouetted-58473 as DATABASE_URL
(venv) lucy@ubuntu:~/projects/myblog$ heroku pg:promote postgresql-silhouetted-58473
Ensuring an alternate alias for existing DATABASE_URL... !
▸   postgresql-silhouetted-58473 is already promoted on ⬢ wakingup

4.4 配置相关的 环境变量

通过heroku config,可查看heroku_app上的所有环境变量。由下面我们可以知道,DATABASE_URL,即为Heroku平台中postgresql数据库的路径地址,因此要检查config.py,确保数据库设置为DATABASE_URL

(venv) lucy@ubuntu:~/projects/myblog$ heroku config

=== wakingup Config Vars
DATABASE_URL: xxxxx...(这里一长串隐去)

设置相应环境变量,特别注意,不要忘了SSL_DISABLE设为1

(venv) lucy@ubuntu:~/projects/myblog$ heroku config:set FLASK_CONFIG=heroku
(venv) lucy@ubuntu:~/projects/myblog$ heroku config:set SSL_DISABLE=1
(venv) lucy@ubuntu:~/projects/myblog$ heroku config:set FLASK_ADMIN_NAME='weiyanying'
(venv) lucy@ubuntu:~/projects/myblog$ heroku config:set FLASK_ADMIN_EMAIL='xxxxxx'(这一串隐去)
(venv) lucy@ubuntu:~/projects/myblog$ heroku config:set FLASK_ADMIN_PASSWORD='xxxxxx'(嘿嘿不给你看)

最后还特别注意的,不要忘记,将环境变量 导入或者写入.env 文件中!!!

    /* .env */
FLASK_CONFIG=heroku
DATABASE_URL=xxxxxx...
FLASK_ADMIN_NAME=weiyanying
FLASK_ADMIN_EMAIL=xxxxxx...
FLASK_ADMIN_PASSWORD=xxxxxx...
SSL_DISABLE=1


5、heroku部署

5.1 修改 manage.py 文件,进行本地数据初始化测试

我的博客和狗书作者不太一样,需要初始化一个user_admin。在经历了无数次deploy失败后,终于摸索出了具体的“路径”

/* manage.py */
#!/usr/bin/env python
import os
from app import create_app, db
from app.models import User,Say,Post,Link,Tag,Comment
from flask_script import Manager, Shell
from flask_migrate import Migrate, MigrateCommand,init,migrate,upgrade
app=create_app(os.getenv('FLASK_CONFIG') or 'default')
migrate=Migrate(app,db)
manager=Manager(app)

@app.shell_context_processor
def make_shell_context():
    return dict(app=app,db=db,User=User,Say=Say,Post=Post,Link=Link,Tag=Tag,Comment=Comment)

@manager.command
def profile(length, profile_dir):
    """Start the application under the code profiler."""
    from werkzeug.contrib.profiler import ProfilerMiddleware
    app.wsgi_app = ProfilerMiddleware(app.wsgi_app,restrictions[length],profile_dir=profile_dir)
    app.run()

@manager.command
def init_data():
    """Run deployment tasks."""
    db.create_all()
    admin_name=os.environ.get('FLASK_ADMIN_NAME')
    admin_email=os.environ.get('FLASK_ADMIN_EMAIL')
    admin_password=os.environ.get('FLASK_ADMIN_PASSWORD')
    user_admin=User(name=admin_name,email=admin_email,
        password=admin_password)
    db.session.add(user_admin)
    db.session.commit()

manager.add_command('shell', Shell(make_context=make_shell_context))
manager.add_command('db',MigrateCommand)
if __name__=='__main__':
manager.run()

将之前所有的sqlite删除,所有的migrations删除。然后进行 初始化,以及创建迁移仓库,测试无误进入下一步

(venv) lucy@ubuntu:~/projects/myblog$ python manage.py init_data
(venv) lucy@ubuntu:~/projects/myblog$ python manage.py db init
(venv) lucy@ubuntu:~/projects/myblog$ python manage.py db migrate

5.2 部署前将改动提交到git

哎呀千头万绪、千辛万苦、千山万水终于到了最后一关,你想哎呀终于好多了。。但素!,这时候一定要警惕的!!部署前一定要记得 把改动提交到git!!! 否则233..你懂的

(venv) lucy@ubuntu:~/projects/myblog$ git status
.....(这里就是改动的内容了)

(venv) lucy@ubuntu:~/projects/myblog$ git add -A
(venv) lucy@ubuntu:~/projects/myblog$ git commit -m"v2.0"

5.3 提交到 远程仓库,后面就全部是heroku上的操作了

(venv) lucy@ubuntu:~/projects/myblog$ git push heroku master
Counting objects: 7413, done.
Compressing objects: 100% (5645/5645), done.
Writing objects: 100% (7413/7413), 45.31 MiB | 162.00 KiB/s, done.
Total 7413 (delta 1256), reused 7406 (delta 1251)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Python app detected
remote: !     The latest version of Python 2 is python-2.7.14 (you are using python-2.7.13, which is unsupported).
remote: !     We recommend upgrading by specifying the latest version (python-2.7.14).
remote:       Learn More: https://devcenter.heroku.com/articles/python-runtimes
remote: -----> Installing python-2.7.13
remote: -----> Installing pip
remote: -----> Installing requirements with pip
remote:       Collecting alembic==0.9.9 (from -r     /tmp/build_30a685958675476f56e4e6b42894b53e/requirements.txt (line 1))
remote:         Downloading alembic-0.9.9.tar.gz (1.0MB)
remote:       Collecting bleach==2.1.3 (from -r /tmp/build_30a685958675476f56e4e6b42894b53e/requirements.txt (line 2))
......(这里还有一堆)
remote:       Successfully installed Flask-0.12.2 Flask-Admin-1.5.1 Flask-Bootstrap-3.3.7.1 Flask-Login-0.4.1 Flask-Mail-0.9.1 Flask-Migrate-2.1.1 Flask-Moment-0.6.0 Flask-SQLAlchemy-2.3.2 Flask-SSLify-0.1.5 Flask-Script-2.0.6 Flask-SimpleMDE-0.3.0 Flask-WTF-0.14.2 Jinja2-2.10 Mako-1.0.7 Markdown-2.6.11 MarkupSafe-1.0 SQLAlchemy-1.2.6 WTForms-2.1 Werkzeug-0.14.1 alembic-0.9.9 bleach-2.1.3 blinker-1.4 click-6.7 dominate-2.3.1 gunicorn-19.7.1 html5lib-1.0.1 itsdangerous-0.24 psycopg2-2.7.4 psycopg2-binary-2.7.4 python-dateutil-2.7.2 python-editor-1.0.3 six-1.11.0 visitor-0.1.3 webencodings-0.5.1
remote:
remote: -----> Discovering process types
remote:       Procfile declares types -> web
remote:
remote: -----> Compressing...
remote:       Done: 39.3M
remote: -----> Launching...
remote:       Released v9
remote:       https://wakingup.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To git@heroku.com:wakingup.git
* [new branch]     master -> master

到了远程仓库以后,执行下面的操作。查看logs没有出差错,打开 https://wakingup.herokuapp.com/ 或者heroku open命令打开浏览器,看到与本地一模一样的画面,Done! 成功!然后重启就行了。这时候就开心的不行了。。

(venv) lucy@ubuntu:~/projects/myblog$ heroku run python manage.py init_data
(venv) lucy@ubuntu:~/projects/myblog$ heroku logs --tail
(venv) lucy@ubuntu:~/projects/myblog$ heroku restart

5.4 还可git push到github仓库

heroku 还可以实现与 github的同步,这里就不多说了可阅读heroku的官方文档。在github上也建一个myblog仓库,然后git push到上面

(venv) lucy@ubuntu:~/projects/myblog$ git push origin master


6.记录heroku常用其他命令

6.1 销毁 heroku项目

(venv) lucy@ubuntu:~/projects/myblog$ heroku destroy <项目名称>

6.2 查看heroku数据库情况

(venv) lucy@ubuntu:~/projects/myblog$ heroku pg:info

6.3 hero 命令行,比如下面的
当然首先要确保你的manage.py里面配置好shell这些参数

(venv) lucy@ubuntu:~/projects/myblog$ heroku run python manage.py shell

等等,还有很多,具体可见 官方文档





二、部署踩坑及解决


1、解决python2.7编码问题

这个项目是几个月之前的了,当时用的python2.7,本来想部署到阿里云,但是一直部署不上去,然后搁置。最近拿出来 重构,前前后后改了很多,就python版本沿用之前的2.7。

flask_admin 做的后台,偶尔会打不开,时好时坏,后面才想到可能是 编码 问题。
网上搜索(真的是良莠不齐233..),最后找到一个方案,好歹解决了。

venv路径下找到python2.7,然后找到 site.py 文件,加上 以下几句,顺利解决

(venv) lucy@ubuntu:~/projects/myblog$ cd venv/lib/python2.7
(venv) lucy@ubuntu:~/projects/myblog/venv/lib/python2.7$ ls site.py* -al

- rw-rw-r-- 1 lucy lucy 27587 4月   4 18:09 site.py
-rw-rw-r-- 1 lucy lucy 24918 4月   2 09:54 site.pyc

(venv) lucy@ubuntu:~/projects/myblog/venv/lib/python2.7$ vim site.py
/* 在前面加上 */
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

上面说的头头是道,很有道理。但是如果将app部署到了heroku上,还如何修改site.py??(emmm 是不是感觉被骗了一样…)

前前后后周折很久,能否通过命令行或者别的方式,加一个site.py上去(当然事实证明是,天方夜谭 233…)只好胡乱试了,后面想通了一丢丢。在每一个 含中文的py文件 头部,加上下面这几句

#-*- coding:utf-8 -*-

嘿嘿非常神奇的,老毛病再没犯过,撒花~


2、数据库迁移问题

heroku部署数据库这块很多坑,不多说都是泪,上面已经讲得很详细了。参考 博客,总结得挺好。no such table问题,也可看看这一篇。然后每一次向数据库提交数据,都要记得db.commit()


3、git push部署与requirements.txt

有时候git push heroku master 提交不了,出现git push heroku master: Heroku push rejected 这类型的情况,有一种可能是:你的 requirements.txt混进了奇怪的包,或者不能被远程的heroku_app pip下载的包,需要排除,所以每次git push 前最好查看一下requriements.txt

/* 有几回莫名其妙混进这个包 */
pkg resources=0.0.0


4、git push部署与http.postBuffer

之前不明所以,把migrations文件归入gitignore文件中,后面了解了放出来重新git push部署,结果如下。这下我明白了原来 git 上传是有大小限制的,具体参考 博客

/* 文件太大会出现http.postBuffer相关error */
error: unable to rewind rpc post data - try increasing http.postBuffer
error: RPC failed; curl 56 Recv failure: Connection reset by peer
fatal: The remote end hung up unexpectedly
fatal: The remote end hung up unexpectedly
Everything up-to-date
Completed with errors, see above

/* 解决方法,就是增加http.postBuffer */
(venv) lucy@ubuntu:~/projects/myblog$ git config --global http.postBuffer 524288000

/* 上面这个太大了233,超出了范围,换成小一点的通过 */
(venv) lucy@ubuntu:~/projects/myblog$ git config --global http.postBuffer 48000000


5、github和heroku git无法登陆

这估计是最吓人的bug了。。昨天下午正忙着,好不容易有一点头绪,结果没法提交到远端。诡异的是,heroku 的git和github都是如此。一脸懵逼,后面heroku login也连不上了

/* heroku 显示443错误 */
▸ EAI_AGAIN: getaddrinfo EAI_AGAIN api.heroku.com:443
/* github 显示can not connect to server */

因为之前有做过一点爬虫,所以第一反应可能之前操作太频繁??被heroku以为是robort然后封ip了,怕怕。但用浏览器又可正常访问heroku和github,排除这个可能

网上有一种说法是,如果 挂代理可能会占用443端口,导致无法进行访问。这回开始了解了一些443端口的知识,此时正挂着shadow,赶紧卸掉,但是仍是一点用也没有

继续穿回小马甲搜索,google提示用 ssh替换https方式 操作git。蒙头去试,果真如此


1)首先说 github这边

添加ssh简单略过,https模式换成ssh模式,可直接在.git目录下config中修改

(venv) lucy@ubuntu:~/projects/myblog/.git$ vim config
[remote "origin"]
    url = git@github.com:LUCY78765580/myblog.git
    fetch = +refs/heads/*:refs/remotes/origin/*

然后又遇到问题了,借鉴 博客,解决掉一部分

(venv) lucy@ubuntu:~/projects/myblog$ ssh git@github.com
ssh: connect to host github.com port 22: Connection refused

/* 进入.ssh目录,添加config文件 */
/* 注意要用sudo vim <文件名>,否则可能退不出来 */
/* User这里是电子邮件 */
(venv) lucy@ubuntu:~/projects/myblog$ cd ~/.ssh

(venv) lucy@ubuntu:~/.ssh$ touch config
(venv) lucy@ubuntu:~/.ssh$ sudo vim config
/* config */
Host github.com
User xxxxxx@qq.com
Hostname ssh.github.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/id_rsa
Port 443

然后你以为这就万事大吉了嘛?不可能的,heroku的bug从不认输 (镇定脸..)

(venv) lucy@ubuntu:~/projects/myblog$ ssh git@github.com
ssh: Could not resolve hostname github.com: Temporary failure in name resolution
fatal: Could not read from remote repository.

结果还是stackoverflow 帮了大忙,参考 这个答案

  • 第一种 用ping得到ip,将ip地址填入/etc/hosts文件中
  • 第二种 sudo vim /etc/resolv.conf,填入nameserver 8.8.8.8nameserver 8.8.4.4
  • 最后出现 “Hi LUCY78765580! You’ve successfully authenticated, but GitHub does not provide shell access”代表 连接成功,可以进行git push操作了
  • 可能会出现类似的,Warning: Permanently added ‘github.com,192.30.252.130’ (RSA) to the list of known hosts.Permission denied (publickey),这里建议不管成功与否,保险起见,还是将上述的ip填入/etc/hosts


2)然后 heroku这边

同理的,通过ssh git@heroku.com 得到相应的 hosts,填入/etc/下的hosts文件.当然还是遇到问题:Permission denied (publickey)。原来是忘记配 ssh-key ,参考stackoverflow上的 discuss,也可查看 heroku官方文档

/* 因为上面github已经生成一个了,只需一步 */
(venv) lucy@ubuntu:~/projects/myblog$ heroku keys:add


6、heroku 部署后Bootstrap样式失效

这个问题在网上也有人遇到过,但是没有很明确的答案。大概2种解法:

  • stackoverflow上说的,可以用//maxcdn,详见 这里
  • 不要用cdn,本地引用的方式就可以了,详见这里

就着第2种思路去试了。首先去Bootstrap中文网jQuery网站获取下载地址,用wget下载到static目录下面,然后解压(可参考 博客

(venv) lucy@ubuntu:~/projects/myblog/app/static$ wget <Bootstrap-3.3.7地址>
(venv) lucy@ubuntu:~/projects/myblog/app/static$ wget <jQuery-3.3.1地址>
(venv) lucy@ubuntu:~/projects/myblog/app/static$ tar –xvf <Bootrap-3.3.7 的tar文件>
(venv) lucy@ubuntu:~/projects/myblog/app/static$ unzip–xvf <jQuery-3.3.1 的zip文件>

在 base.html 中修改,link标签可以采取flask中的url_for 方式访问。解决。

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <script src="../static/jquery-3.3.1.min.js"></script>
    <link rel="stylesheet" href="{{ url_for('static',filename='bootstrap-3.3.7-dist/css/bootstrap.min.css') }}">
    <script src="../static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
    <link rel="shortcut icon" href="{{ url_for('static',filename='favicon.ico') }}" type="image/x-icon">
    <link rel="stylesheet" href="{{ url_for('static',filename='styles.css') }}">
</head>


7、部署后git push更新的CSS样式失效

这里意思是,已经部署成功的app重新修改样式,成功又一次git push heroku master到远端,但是 css样式没有发生改变,没有更新。

今天早上也遇到这个问题,成功把俺吓住,以为哪哪又出问题了。后面灵光一闪,清空浏览器缓存刷新,成功嘿嘿。。

不要问我怎么发现的,因为俺聪明啊(233试了三个浏览器这个事实我会告诉你?(逃~

/* 顺带记一下,用火狐浏览器查看heroku博客,不用清空缓存也可正常显示;使用360和chrome,则需要在更新css后,清空浏览器,然后刷新才能正常显示。*/


8、解决TextArea自适应宽度

这个问题也很烦人,之前已经设计好的样式,比如发表说说的文本框 rows=8 ,cols=41 对齐得挺好,部署后不能看了。。强迫症患者简直不能忍(具体图就不放了)
网上找到 这个答案,还是挺靠谱的。如下所示,添上style=”…一系列”,最终解决。

<form class="NewForm" method="POST" action="">
    {{ form.hidden_tag() }}
    <div class="col-md-12">{{ form.content(rows=8,cols=41,placeholder=form.content.label.text,style="width:100%;overflow:auto;word-break:break-all;") }}</div>
    <div style="height:20px;clear:both;"></div>
    <div class="col-md-12">{{ form.submit(class="btn btn-default") }}</div>
</form>





三、资料及博客参考


1、heroku官方文档

2、Heroku 使用教程

3、部署Flask App到Heroku完整过程

4、heroku 部署flask应用

5、【flasky/heroku/部署】(欢迎大家挑刺和补充)可能是目前为止最详细的了





四、总结与思考

总的内容大概就是上面这些了,真的是又长又chou的一篇文,嗯是挺细的。

现在怀疑自己是“招bug”体质 一类,部署那么点东西,莫名其妙要踩无数坑??。

好几回都想要放弃了,但是总归坚持了下来,最终部署成功大大增强了信心嘿嘿。
这次的部署,也见识了很多以前忽略的东西,比如linux命令、git操作、使用搜索。
总归言之以下几点:

  • 看document/log/code 一定要非常 仔细,否则很多时候会忽略很重要的信息
  • 英文搜索(google/stackoverflow 一类) 效率要高很多
  • 日常 工具 (linux/git/github/vim一类)熟练,可以大大提高生产效率,要加油的
  • 有些困难真的是 无法绕过去的,这时候要做的,就是坚持住、战胜它

这篇文算比较“细致”or “唠叨”的一篇,主要记录踩坑的过程,以后不会犯相同的错误。
当然如果帮助到了需要的人,本宝宝也hin开心的。

最后附上博客项目github地址,一些功能完善中,欢迎star/fork/提issue。(捂脸逃~


-------------本文结束感谢您的阅读-------------