Heroku 使用教程

Heroku平台


Heroku平台的灵活性极高且支持多种编程语言。若想把程序部署到Heroku上,开发者要使用Git把程序推送到HerokuGit服务器上。在服务器上,git push命令会自动触发安装、配置和部署程序。

Heroku使用名为Dyno的计算单元衡量用量,并以此为依据收取服务费用。最常用的Dyno类型是Web Dyno,表示一个Web服务器实例。程序可以通过使用更多的Web Dyno以增强其请求处理能力。另一种Dyno类型是Worker Dyno,用来执行后台作业或其他辅助任务。

Heroku提供了大量的插件和扩展,可用于数据库、电子邮件支持和其他很多服务。

准备程序


若想使用 Heroku,程序必须托管在 Git 仓库中。如果你的程序托管在像 GitHub 或 BitBucket 这样的远程 Git 服务器上,那么在本地 clone 程序后会创建一个本地 Git 仓库,可无缝用于 Heroku。如果你的程序没有托管在 Git 仓库中,那么必须在开发电脑上创建一个仓库。

注册Heroku账户


heroku 官网

安装Heroku Toolbelt


Heroku Toolbelt

Heroku Toolbelt是用于创建、管理Herokuapps的命令行工具

heroku 的命令行客户端将被安装到/usr/local/heroku,同时,/usr/local/heroku/bin将被添加到你的PATH环境变量

下载并安装完成后,在 shell 中输入heroku login,用创建heroku账号的email密码登陆

$ heroku login
Enter your Heroku credentials.
Email: adam@example.com
Password (typing will be hidden):
Authentication successful.

把你的 SSH 公钥上传到 Heroku,这一点很重要,上传后才能使用 git push 命令。正常情况下,login 命令会自动创建并上传 SSH 公钥。

创建程序


首先要确保程序在 Git 源码控制系统中,然后在程序的顶级目录下运行创建命令

在 Heroku 创建一个 app, 以便 Heroku 准备好接收你的代码

当你创建一个 app, 将创建并关联一个名为heroku的远端到你的本地仓库

apps:create [NAME] # create a new app 名称必须以字母开始,只能包含小写字母,数字和连字符, 而且名称在 heroku 的所有程序中必须是唯一的

➜  flask_blog git:(rest) ✗ heroku apps:create flask-blog-chaonet
Creating flask-blog-chaonet... done, stack is cedar-14
https://flask-blog-chaonet.herokuapp.com/ | https://git.heroku.com/flask-blog-chaonet.git
Git remote heroku added

查看已创建的 app

➜  flask_blog git:(rest) ✗ heroku apps
=== My Apps
flask-blog-chaonet

默认 Heroku 会为你的 app 生成一个随机的名字, 或者 你可以通过可选参数指定你的 app名。

现在,你可以向 Heroku 部署你的代码了

$ git push heroku master # 必须将 本地 的 master push 到 heroku 的 master

应用现在已经部署了,确保至少一个 app 的实例正在运行:

$ heroku ps:scale web=1

现在,可以用创建的包含 app 名称的 URL 访问 app. 有一个便捷的方法, 你可以像这样打开web站点:

$ heroku open

配置数据库


Heroku 以扩展形式支持 Postgres 数据库。少于 1 万条记录、同时连接数 20 、没有缓存 、不允许fork/follow、没有 Postgres 日志 的小型数据库无需付费即可添加 到程序中。

官网教程: heroku-postgresql

添加 Postgres 到 Heroku 应用

通过命令行,将 Heroku Postgres 添加到一个 Heroku 应用:

➜  flask_blog git:(rest) ✗ heroku addons:create heroku-postgresql:hobby-dev
Creating postgresql-sinuous-8326... done, (free)
Adding postgresql-sinuous-8326 to flask-blog-chaonet... done
Setting DATABASE_URL and restarting flask-blog-chaonet... done, v3
Database has been created and is available
 ! This database is empty. If upgrading, you can transfer
 ! data from another database with pg:copy
Use `heroku addons:docs heroku-postgresql` to view documentation.

选择正确的 Heroku Postgres 方案

heroku-postgresql 插件

最低等级的业余版本有两种方案 hobby-dev 和 hobby-basic

一旦 Heroku Postgres 被添加,一个 DATABASE_URL 设置将在 app 配置中生效,并包含用于访问新提供的 Heroku Postgres 服务的 URL 。

这可以使用 heroku config 命令确认:

➜  flask_blog git:(rest) ✗ heroku config -s | grep DATABASE_URL
DATABASE_URL=postgres://bjtrukgnhvnban:T0_N_OWYVz6NnasUwOf0FxVscO@ec2-54-83-52-71.compute-1.amazonaws.com:5432/d44ph8k76dl0h0

amazonaws ?

  • 建立主数据库

Heroku 中的每个程序都支持多个数据库。Heroku 建议使用 DATABASE_URL 变量保存主用数据库信息。在单个数据库的情况下,新的数据库已经被指定到 DATABASE_URL 。

对于多个数据库的情况,可以设置主用数据库:

$ heroku pg:promote HEROKU_POSTGRESQL_RED
Promoting HEROKU_POSTGRESQL_RED_URL to DATABASE_URL... done

DATABASE_URL 环境变量的格式正是 SQLAlchemy 所需的。回想一下 config.py 脚本的内容,如果 设定了 DATABASE_URL,就使用其中保存的值,所以现在程序可以自动连接到 Postgres 数据库。

  • 查看数据库使用信息
➜  flask_blog git:(rest) ✗ heroku pg:info
=== DATABASE_URL
Plan:        Hobby-dev
Status:      Available
Connections: 0/20
PG Version:  9.4.5
Created:     2016-01-09 02:57 UTC
Data Size:   6.7 MB
Tables:      0
Rows:        0/10000 (In compliance)
Fork/Follow: Unsupported
Rollback:    Unsupported
Add-on:      postgresql-sinuous-8326

heroku 的命令行命令

在 MAC 中安装 Postgres

推荐在本地开发环境与生产环境运行相同的数据库

使用Postgres.app,是在 Mac 下使用PostgreSQL的最简单的方法

postgresapp下载Postgres.app
解压后将Postgres移到应用程序文件夹
双击,然后你的 MAC 中就有一个PostgreSQL服务器在运行了

  • 使用 Postgres.app 的命令行工具

配置$PATH变量

➜  psycopg2-2.6.1  which psql
psql not found
➜  psycopg2-2.6.1  export PATH=$PATH:/Applications/Postgres.app/Contents/Versions/latest/bin
➜  psycopg2-2.6.1  which psql
/Applications/Postgres.app/Contents/Versions/latest/bin/psql

通过命令psql连接你的数据库

➜  psycopg2-2.6.1  psql -h localhost
psql (9.5.0)
Type "help" for help.

chao-# \q
➜  psycopg2-2.6.1

Using Command Line Tools with Postgres.app

在本地安装 Heroku Postgres

与 Python 对接

官网教程

➜  psycopg2-2.6.1  pip install psycopg2
Collecting psycopg2
  Using cached psycopg2-2.6.1.tar.gz
Building wheels for collected packages: psycopg2
  Running setup.py bdist_wheel for psycopg2
  Stored in directory: /Users/chao/Library/Caches/pip/wheels/e2/9a/5e/7b620848bbc7cfb9084aafea077be11618c2b5067bd532f329
Successfully built psycopg2
Installing collected packages: psycopg2
Successfully installed psycopg2-2.6.1

遇到的问题:

没有通过 postgres.app 安装 Postgres,就安装 psycopg2 ,结果报错

  • pip 安装
➜  flask_blog git:(rest) ✗ pip install psycopg2
Collecting psycopg2
  Using cached psycopg2-2.6.1.tar.gz
    Complete output from command python setup.py egg_info:
    running egg_info
    creating pip-egg-info/psycopg2.egg-info
    writing pip-egg-info/psycopg2.egg-info/PKG-INFO
    writing top-level names to pip-egg-info/psycopg2.egg-info/top_level.txt
    writing dependency_links to pip-egg-info/psycopg2.egg-info/dependency_links.txt
    writing manifest file 'pip-egg-info/psycopg2.egg-info/SOURCES.txt'
    warning: manifest_maker: standard file '-c' not found

    Error: pg_config executable not found.

    Please add the directory containing pg_config to the PATH
    or specify the full executable path with the option:

        python setup.py build_ext --pg-config /path/to/pg_config build ...

    or with the pg_config option in 'setup.cfg'.

    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /private/var/folders/36/tdbs35wx1sx4d6nm4hrmwy0h0000gn/T/pip-build-XR5kDF/psycopg2

  • 下载安装包安装
➜  psycopg2-2.6.1  ls
AUTHORS     LICENSE     MANIFEST.in NEWS        README.rst  examples    psycopg     setup.cfg   tests
INSTALL     MANIFEST    Makefile    PKG-INFO    doc         lib         scripts     setup.py

➜  psycopg2-2.6.1  python setup.py build
running build
running build_py
creating build
creating build/lib.macosx-10.6-intel-2.7
creating build/lib.macosx-10.6-intel-2.7/psycopg2
copying lib/__init__.py -> build/lib.macosx-10.6-intel-2.7/psycopg2
copying lib/_json.py -> build/lib.macosx-10.6-intel-2.7/psycopg2
copying lib/_range.py -> build/lib.macosx-10.6-intel-2.7/psycopg2
copying lib/errorcodes.py -> build/lib.macosx-10.6-intel-2.7/psycopg2
copying lib/extensions.py -> build/lib.macosx-10.6-intel-2.7/psycopg2
copying lib/extras.py -> build/lib.macosx-10.6-intel-2.7/psycopg2
copying lib/pool.py -> build/lib.macosx-10.6-intel-2.7/psycopg2
copying lib/psycopg1.py -> build/lib.macosx-10.6-intel-2.7/psycopg2
copying lib/tz.py -> build/lib.macosx-10.6-intel-2.7/psycopg2
creating build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/__init__.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/dbapi20.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/dbapi20_tpc.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/test_async.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/test_bug_gc.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/test_bugX000.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/test_cancel.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/test_connection.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/test_copy.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/test_cursor.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/test_dates.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/test_extras_dictcursor.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/test_green.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/test_lobject.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/test_module.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/test_notify.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/test_psycopg2_dbapi20.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/test_quote.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/test_transaction.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/test_types_basic.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/test_types_extras.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/test_with.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/testconfig.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
copying tests/testutils.py -> build/lib.macosx-10.6-intel-2.7/psycopg2/tests
running build_ext
Error: pg_config executable not found.

Please add the directory containing pg_config to the PATH
or specify the full executable path with the option:

    python setup.py build_ext --pg-config /path/to/pg_config build ...

or with the pg_config option in 'setup.cfg'.
➜  psycopg2-2.6.1

配置 Python 用于 Postgres.app

在 Flask 中使用 Flask-SQLAlchemy 扩展时,可以直接在配置中定义,并由 app 读取。

Postgres.app 的配置文档

配置日志


任何写到 标准输出(stdout) 或标准错误(stderr) 的信息才会被 Heroku 抓取到日志,并在 Heroku 客户端通过heroku logs命令查看。

config.py

        # 输出到 stderr,以便被 Heroku 抓取到 logs
        import logging
        from logging import StreamHandler
        file_handler = StreamHandler()
        file_handler.setLevel(logging.WARNING)
        app.logger.addHandler(file_handler)

定义配置变量


Heroku 可以让你在程序外部配置变量 —— 存储类似加密密钥外部资源地址的数据。

在运行时, 配置的变量被当做环境变量暴露给应用。

变量可以用Heroku的命令行工具配置,也可以在应用的Dashboard页面的settings页签下配置。

官网资料

配置电子邮件


Heroku 没有提供 SMTP 服务器,所以我们要配置一个外部服务器。很多第三方扩展能把 适用于生产环境的邮件发送服务集成到 Heroku 中,但对于测试和评估而言,使用继承自 Config 基类的 mail 配置已经足够了。

由于直接把安全密令写入脚本存在安全隐患,所以我们把访问 mail SMTP服务器的用户 名和密码保存在环境变量中。

运行生产 Web 服务器


Heroku 没有为托管程序提供 Web 服务器,相反,它希望程序启动自己的服务器并监听环 境变量 PORT 中设定的端口。

Flask 自带的开发 Web 服务器表现很差,因为它不是为生产环境设计的服务器。有两个 可以在生产环境中使用、性能良好且支持 Flask 程序的服务器,分别是GunicornuWSGI

若想在本地测试 Heroku 配置,我们最好在虚拟环境中安装 Web 服务器。例如,可通过如下命令安装 Gunicorn:

(venv) $ pip install gunicorn

若要使用 Gunicorn 运行程序,可执行下面的命令:

     (venv) $ gunicorn manage:app
     2013-12-03 09:52:10 [14363] [INFO] Starting gunicorn 18.0
     2013-12-03 09:52:10 [14363] [INFO] Listening at: http://127.0.0.1:8000 (14363)
     2013-12-03 09:52:10 [14363] [INFO] Using worker: sync
     2013-12-03 09:52:10 [14368] [INFO] Booting worker with pid: 14368

manage:app 参数冒号左边的部分表示定义程序的包或者模块,冒号右边的部分表示包中程序实例的名字注意,Gunicor 默认使用端口 8000,而 Flask 默认使用 5000

添加依赖需求文件


Heroku 从程序顶级文件夹下的 requirements.txt 文件中加载包依赖。这个文件中的所有依赖都会在部署过程中导入 Heroku 创建的虚拟环境。

Heroku 的需求文件必须包含程序在生产环境中使用的所有通用依赖,以及支持 Postgres 数据库的 psycopg2 包和 Gunicorn Web 服务器。

可以在需求文件中导入需求文件。

requirements.txt

     -r requirements/prod.txt
     gunicorn==18.0
     psycopg2==2.5.1

添加Procfile文件


Heroku 需要知道使用哪个命令启动程序。命令在一个名为Procfile(没有后缀)的特殊文件中指定。这个文件必须放在程序的顶级文件夹中。

web: gunicorn manage:app

Procfile文件内容的格式很简单:在每一行中指定一个任务名,后跟一个冒号,然后是执行这个任务的命令。名为web的任务比较特殊任务,Heroku 使用这个任务启动 Web 服务器。

Heroku 会为这个任务提供一个PORT环境变量,用于设定程序监听请求的端口。如果设定了PORT变量, Gunicorn默认就会使用其中保存的值,因此无需将其包含在启动命令中。

部署程序后,Heroku 会运行 Procfile 中列出的所有任务。

任务格式模板

程序可在 Procfile 中使用 web 之外的名字声明其他任务,例如程序所需的其他服务。

声明任务类型的方式

使用 Heroku Local 进行本地测试


官方教程

Heroku Local作为Heroku Toolbelt的一部分, 自动被安装。

  • 在本地启动所有在你的Profile中定义的任务

$ heroku local

heroku localheroku local:start的简写,作用相同

➜  flask_blog git:(rest) ✗ heroku local
Downloading forego-0.16.1 to /Users/chao/.heroku... done
forego | starting web.1 on port 5000
web.1  | [2016-01-10 00:11:00 +0800] [17093] [INFO] Starting gunicorn 19.4.5
web.1  | [2016-01-10 00:11:00 +0800] [17093] [INFO] Listening at: http://0.0.0.0:5000 (17093)
web.1  | [2016-01-10 00:11:00 +0800] [17093] [INFO] Using worker: sync
web.1  | [2016-01-10 00:11:00 +0800] [17096] [INFO] Booting worker with pid: 17096

指定运行的端口

➜  flask_blog git:(rest) ✗ heroku local -p 8000
forego | starting web.1 on port 8000
web.1  | [2016-01-10 00:20:22 +0800] [17237] [INFO] Starting gunicorn 19.4.5
web.1  | [2016-01-10 00:20:22 +0800] [17237] [INFO] Listening at: http://0.0.0.0:8000 (17237)
web.1  | [2016-01-10 00:20:22 +0800] [17237] [INFO] Using worker: sync
web.1  | [2016-01-10 00:20:22 +0800] [17240] [INFO] Booting worker with pid: 17240
web.1  | [2016-01-10 00:21:11 +0800] [17237] [CRITICAL] WORKER TIMEOUT (pid:17240)
web.1  | [2016-01-10 00:21:11 +0800] [17240] [INFO] Worker exiting (pid: 17240)
web.1  | [2016-01-10 00:21:11 +0800] [17244] [INFO] Booting worker with pid: 17244

可以在本地指定运行一个特定的进程类型,例如webworker

$ heroku local web

然后,你就可以在本地测试 app。使用Ctrl+C停止

  • 设置本地环境变量

在本地运行 APP,同样需要输入一系列 配置变量 作为 app 的配置。.env文件让你可以收集 app 在本地运行所需要的配置变量。当使用heroku local命令运行 APP,将会读取.env文件,并且,每一个 名称/值 对会被插入环境中,模仿实际的配置变量。

增加一个配置变量到你的.env文件:编辑.env文件,并在新的一行增加一个新name=value对。

由于.env文件中包含密码和其他敏感的账户信息, 所以决不能将其添加到 Git 仓库中,这个文件应该只在存在于本地配置中。使用:echo .env >> .gitignore, 将.env文件列入 git 的忽略文件列表。

需要注意,你部署的生产 app 可能使用到与本地开发 app 不同的服务。例如,部署的生产 app 可能有一个引用Heroku Postgres数据库的DATABASE_URL配置变量,但你的本地 app可能在.env文件中有一个引用本地安装的PostgresDATABASE_URL变量。

有时你可能想要在本地使用和 Heroku 环境相同的配置变量。对于每一个你想要添加到.env文件的配置变量,使用如下命令:

$ heroku config:get CONFIG-VAR-NAME -s >> .env

  • 查看环境变量

使用heroku config查看 app 的所有配置变量。使用cat .env查看.env文件的内容

  • 另一个本地测试工具 Foreman

除了heroku local,原本的foreman也可以使用,但不再提供官方的支持,需要另外安装。

Heroku Local has replaced Foreman in the Heroku Toolbelt

执行 git push 命令部署


部署过程的最后一步是把程序上传到 Heroku 服务器。在此之前,你要确保所有改动都已经提交到本地Git仓库,然后执行git push heroku master把程序上传到远程仓库heroku

上传代码,中间遇到一个坑,一开始从本地的rest分支向herokumaster推送,报错

➜  flask_blog git:(rest) git push heroku master
Counting objects: 315, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (309/309), done.
Writing objects: 100% (315/315), 82.51 KiB | 0 bytes/s, done.
Total 315 (delta 190), reused 3 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote:
remote:  !     Push rejected, no Cedar-supported app detected
remote: HINT: This occurs when Heroku cannot detect the buildpack
remote:       to use for this application automatically.
remote: See https://devcenter.heroku.com/articles/buildpacks
remote:
remote: Verifying deploy....
remote:
remote: !   Push rejected to flask-blog-chaonet.
remote:
To https://git.heroku.com/flask-blog-chaonet.git
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'https://git.heroku.com/flask-blog-chaonet.git'

➜  flask_blog git:(rest) heroku buildpacks:set heroku/python
Buildpack set. Next release on flask-blog-chaonet will use heroku/python.
Run git push heroku master to create a new release using this buildpack.

➜  flask_blog git:(rest) git push heroku master
Counting objects: 315, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (309/309), done.
Writing objects: 100% (315/315), 82.56 KiB | 0 bytes/s, done.
Total 315 (delta 190), reused 3 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Using set buildpack heroku/python
remote:
remote:  !     Push rejected, failed to detect set buildpack heroku/python
remote: More info: https://devcenter.heroku.com/articles/buildpacks#detection-failure
remote:
remote: Verifying deploy....
remote:
remote: !   Push rejected to flask-blog-chaonet.
remote:
To https://git.heroku.com/flask-blog-chaonet.git
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'https://git.heroku.com/flask-blog-chaonet.git'

感觉可能是 分支名称的问题,尝试切换到 master,合并分支后再试

➜  flask_blog git:(rest) git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.

➜  flask_blog git:(master) ✗ git merge rest
Updating 40d4e6e..90872cd
Fast-forward
 .gitignore                                      |   2 +
 Procfile                                        |   1 +
 app.db                                          | Bin 76800 -> 83968 bytes
 app/__init__.py                                 |  67 +++++----
 app/api_1_0/__init__.py                         |   5 +
 app/api_1_0/authentication.py                   |  59 ++++++++
 app/api_1_0/comments.py                         |  79 +++++++++++
 app/api_1_0/decorators.py                       |  21 +++
 app/api_1_0/errors.py                           |  21 +++
 app/api_1_0/posts.py                            |  74 ++++++++++
 app/api_1_0/users.py                            |  75 ++++++++++
 app/auth/__init__.py                            |   5 +
 app/auth/forms.py                               |  65 +++++++++
 app/auth/views.py                               | 196 ++++++++++++++++++++++++++
 app/email.py                                    |  11 ++
 app/exceptions.py                               |   4 +
 app/main/__init__.py                            |  13 ++
 app/main/error.py                               |  12 ++
 app/main/forms.py                               |  69 ++++++++++
 app/main/views.py                               | 401 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 app/models.py                                   |  71 +++++++++-
 app/templates/_comments.html                    |  10 +-
 app/templates/_posts.html                       |  12 +-
 app/templates/{ => auth/email}/confirm.html     |   2 +-
 app/templates/auth/email/confirm.txt            |   6 +
 app/templates/{ => auth/email}/confirmmail.html |   2 +-
 app/templates/{ => auth/email}/confirmmail.txt  |   2 +-
 app/templates/{ => auth}/login.html             |   4 +-
 app/templates/{ => auth}/register.html          |   0
 app/templates/{ => auth}/renew.html             |   0
 app/templates/{ => auth}/unconfirmed.html       |   2 +-
 app/templates/base.html                         |  14 +-
 app/templates/changepassword.html               |   2 +-
 app/templates/changepassword.txt                |   2 +-
 app/templates/confirm.txt                       |   6 -
 app/templates/followers.html                    |   2 +-
 app/templates/{ => main}/index.html             |   6 +-
 app/templates/main/moderate.html                |  16 +++
 app/templates/user.html                         |  14 +-
 app/views.py                                    |   1 -
 app_pro.db                                      |   0
 app_test.db                                     | Bin 0 -> 24576 bytes
 config.py                                       | 130 +++++++++++++----
 requirements.txt                                |  43 ++++++
 run.py                                          | 114 ++++++++++++++-
 test/test_api.py                                |  71 ++++++++++
 test/test_basics.py                             |  24 ++++
 test/test_client.py                             |  86 ++++++++++++
 test/test_selenium.py                           |  90 ++++++++++++
 test/test_user_model.py                         |  45 +++++-
 tmp/coverage/app___init___py.html               | 195 ++++++++++++++++++++++++++
 tmp/coverage/app_api_1_0_authentication_py.html | 209 ++++++++++++++++++++++++++++
 tmp/coverage/app_api_1_0_comments_py.html       | 249 +++++++++++++++++++++++++++++++++
 tmp/coverage/app_api_1_0_decorators_py.html     | 133 ++++++++++++++++++
 tmp/coverage/app_api_1_0_errors_py.html         | 133 ++++++++++++++++++
 tmp/coverage/app_api_1_0_posts_py.html          | 239 ++++++++++++++++++++++++++++++++
 tmp/coverage/app_api_1_0_users_py.html          | 241 ++++++++++++++++++++++++++++++++
 tmp/coverage/app_auth_forms_py.html             | 221 +++++++++++++++++++++++++++++
 tmp/coverage/app_auth_views_py.html             | 483 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tmp/coverage/app_email_py.html                  | 113 +++++++++++++++
 tmp/coverage/app_main___init___py.html          | 117 ++++++++++++++++
 tmp/coverage/app_main_views_py.html             | 823 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tmp/coverage/app_models_py.html                 | 981 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tmp/coverage/coverage_html.js                   | 512 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tmp/coverage/index.html                         | 260 ++++++++++++++++++++++++++++++++++
 tmp/coverage/jquery.debounce.min.js             |   9 ++
 tmp/coverage/jquery.hotkeys.js                  |  99 +++++++++++++
 tmp/coverage/jquery.isonscreen.js               |  53 +++++++
 tmp/coverage/jquery.min.js                      |   4 +
 tmp/coverage/jquery.tablesorter.min.js          |   2 +
 tmp/coverage/keybd_closed.png                   | Bin 0 -> 264 bytes
 tmp/coverage/keybd_open.png                     | Bin 0 -> 267 bytes
 tmp/coverage/status.json                        |   1 +
 tmp/coverage/style.css                          | 326 +++++++++++++++++++++++++++++++++++++++++++
 74 files changed, 7248 insertions(+), 112 deletions(-)
 create mode 100644 Procfile
 create mode 100644 app/api_1_0/__init__.py
 create mode 100644 app/api_1_0/authentication.py
 create mode 100644 app/api_1_0/comments.py
 create mode 100644 app/api_1_0/decorators.py
 create mode 100644 app/api_1_0/errors.py
 create mode 100644 app/api_1_0/posts.py
 create mode 100644 app/api_1_0/users.py
 create mode 100644 app/auth/__init__.py
 create mode 100644 app/auth/forms.py
 create mode 100644 app/auth/views.py
 create mode 100644 app/email.py
 create mode 100644 app/exceptions.py
 create mode 100644 app/main/__init__.py
 create mode 100644 app/main/error.py
 create mode 100644 app/main/forms.py
 create mode 100644 app/main/views.py
 rename app/templates/{ => auth/email}/confirm.html (75%)
 create mode 100644 app/templates/auth/email/confirm.txt
 rename app/templates/{ => auth/email}/confirmmail.html (74%)
 rename app/templates/{ => auth/email}/confirmmail.txt (50%)
 rename app/templates/{ => auth}/login.html (71%)
 rename app/templates/{ => auth}/register.html (100%)
 rename app/templates/{ => auth}/renew.html (100%)
 rename app/templates/{ => auth}/unconfirmed.html (80%)
 delete mode 100644 app/templates/confirm.txt
 rename app/templates/{ => main}/index.html (81%)
 create mode 100644 app/templates/main/moderate.html
 create mode 100644 app_pro.db
 create mode 100644 app_test.db
 create mode 100644 requirements.txt
 create mode 100644 test/test_api.py
 create mode 100644 test/test_basics.py
 create mode 100644 test/test_client.py
 create mode 100644 test/test_selenium.py
 create mode 100644 tmp/coverage/app___init___py.html
 create mode 100644 tmp/coverage/app_api_1_0_authentication_py.html
 create mode 100644 tmp/coverage/app_api_1_0_comments_py.html
 create mode 100644 tmp/coverage/app_api_1_0_decorators_py.html
 create mode 100644 tmp/coverage/app_api_1_0_errors_py.html
 create mode 100644 tmp/coverage/app_api_1_0_posts_py.html
 create mode 100644 tmp/coverage/app_api_1_0_users_py.html
 create mode 100644 tmp/coverage/app_auth_forms_py.html
 create mode 100644 tmp/coverage/app_auth_views_py.html
 create mode 100644 tmp/coverage/app_email_py.html
 create mode 100644 tmp/coverage/app_main___init___py.html
 create mode 100644 tmp/coverage/app_main_views_py.html
 create mode 100644 tmp/coverage/app_models_py.html
 create mode 100644 tmp/coverage/coverage_html.js
 create mode 100644 tmp/coverage/index.html
 create mode 100644 tmp/coverage/jquery.debounce.min.js
 create mode 100644 tmp/coverage/jquery.hotkeys.js
 create mode 100644 tmp/coverage/jquery.isonscreen.js
 create mode 100644 tmp/coverage/jquery.min.js
 create mode 100644 tmp/coverage/jquery.tablesorter.min.js
 create mode 100644 tmp/coverage/keybd_closed.png
 create mode 100644 tmp/coverage/keybd_open.png
 create mode 100644 tmp/coverage/status.json
 create mode 100644 tmp/coverage/style.css

➜  flask_blog git:(master) git push heroku master
Counting objects: 491, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (482/482), done.
Writing objects: 100% (491/491), 232.17 KiB | 0 bytes/s, done.
Total 491 (delta 301), reused 2 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Using set buildpack heroku/python
remote: -----> Python app detected
remote: -----> Installing runtime (python-2.7.11)
remote: -----> Installing dependencies with pip
remote:        Collecting alembic==0.8.4 (from -r requirements.txt (line 1))
remote:        Downloading alembic-0.8.4.tar.gz (950kB)
remote:        Collecting bleach==1.4.2 (from -r requirements.txt (line 2))
remote:        Downloading bleach-1.4.2-py2.py3-none-any.whl
remote:        Collecting blinker==1.4 (from -r requirements.txt (line 3))
remote:        Downloading blinker-1.4.tar.gz (111kB)
remote:        Collecting coverage==4.0.3 (from -r requirements.txt (line 4))
remote:        Downloading coverage-4.0.3.tar.gz (354kB)
remote:        Collecting decorator==4.0.6 (from -r requirements.txt (line 5))
remote:        Downloading decorator-4.0.6-py2.py3-none-any.whl
remote:        Collecting dominate==2.1.16 (from -r requirements.txt (line 6))
remote:        Downloading dominate-2.1.16.zip
remote:        Collecting Flask==0.10.1 (from -r requirements.txt (line 7))
remote:        Downloading Flask-0.10.1.tar.gz (544kB)
remote:        Collecting Flask-Bootstrap==3.3.5.7 (from -r requirements.txt (line 8))
remote:        Downloading Flask-Bootstrap-3.3.5.7.tar.gz (451kB)
remote:        Collecting Flask-HTTPAuth==2.7.0 (from -r requirements.txt (line 9))
remote:        Downloading Flask-HTTPAuth-2.7.0.tar.gz
remote:        Collecting Flask-Login==0.3.2 (from -r requirements.txt (line 10))
remote:        Downloading Flask-Login-0.3.2.tar.gz
remote:        Collecting Flask-Mail==0.9.1 (from -r requirements.txt (line 11))
remote:        Downloading Flask-Mail-0.9.1.tar.gz (45kB)
remote:        Collecting Flask-Markdown==0.3 (from -r requirements.txt (line 12))
remote:        Downloading Flask-Markdown-0.3.tar.gz (165kB)
remote:        Collecting Flask-Migrate==1.6.0 (from -r requirements.txt (line 13))
remote:        Downloading Flask-Migrate-1.6.0.tar.gz
remote:        Collecting Flask-Moment==0.5.1 (from -r requirements.txt (line 14))
remote:        Downloading Flask-Moment-0.5.1.tar.gz
remote:        Collecting Flask-PageDown==0.2.1 (from -r requirements.txt (line 15))
remote:        Downloading Flask-PageDown-0.2.1.tar.gz
remote:        Collecting Flask-Script==2.0.5 (from -r requirements.txt (line 16))
remote:        Downloading Flask-Script-2.0.5.tar.gz (42kB)
remote:        Collecting Flask-SQLAlchemy==2.1 (from -r requirements.txt (line 17))
remote:        Downloading Flask-SQLAlchemy-2.1.tar.gz (95kB)
remote:        Collecting Flask-SSLify==0.1.5 (from -r requirements.txt (line 18))
remote:        Downloading Flask-SSLify-0.1.5.tar.gz
remote:        Collecting Flask-WTF==0.12 (from -r requirements.txt (line 19))
remote:        Downloading Flask_WTF-0.12-py2-none-any.whl
remote:        Collecting ForgeryPy==0.1 (from -r requirements.txt (line 20))
remote:        Downloading ForgeryPy-0.1.tar.gz
remote:        Collecting gunicorn==19.4.5 (from -r requirements.txt (line 21))
remote:        Downloading gunicorn-19.4.5-py2.py3-none-any.whl (112kB)
remote:        Collecting html5lib==0.9999999 (from -r requirements.txt (line 22))
remote:        Downloading html5lib-0.9999999.tar.gz (889kB)
remote:        Collecting httpie==0.9.3 (from -r requirements.txt (line 23))
remote:        Downloading httpie-0.9.3-py2.py3-none-any.whl (66kB)
remote:        Collecting itsdangerous==0.24 (from -r requirements.txt (line 24))
remote:        Downloading itsdangerous-0.24.tar.gz (46kB)
remote:        Collecting Jinja2==2.8 (from -r requirements.txt (line 25))
remote:        Downloading Jinja2-2.8-py2.py3-none-any.whl (263kB)
remote:        Collecting Mako==1.0.3 (from -r requirements.txt (line 26))
remote:        Downloading Mako-1.0.3.tar.gz (565kB)
remote:        Collecting Markdown==2.6.5 (from -r requirements.txt (line 27))
remote:        Downloading Markdown-2.6.5.tar.gz (301kB)
remote:        Collecting MarkupSafe==0.23 (from -r requirements.txt (line 28))
remote:        Downloading MarkupSafe-0.23.tar.gz
remote:        Collecting pbr==1.8.1 (from -r requirements.txt (line 29))
remote:        Downloading pbr-1.8.1-py2.py3-none-any.whl (89kB)
remote:        Collecting psycopg2==2.6.1 (from -r requirements.txt (line 30))
remote:        Downloading psycopg2-2.6.1.tar.gz (371kB)
remote:        Collecting Pygments==2.0.2 (from -r requirements.txt (line 31))
remote:        Downloading Pygments-2.0.2-py2-none-any.whl (672kB)
remote:        Collecting python-editor==0.5 (from -r requirements.txt (line 32))
remote:        Downloading python-editor-0.5.tar.gz
remote:        Collecting requests==2.9.1 (from -r requirements.txt (line 33))
remote:        Downloading requests-2.9.1-py2.py3-none-any.whl (501kB)
remote:        Collecting selenium==2.48.0 (from -r requirements.txt (line 34))
remote:        Downloading selenium-2.48.0-py2-none-any.whl (872kB)
remote:        Collecting six==1.10.0 (from -r requirements.txt (line 35))
remote:        Downloading six-1.10.0-py2.py3-none-any.whl
remote:        Collecting SQLAlchemy==1.0.10 (from -r requirements.txt (line 36))
remote:        Downloading SQLAlchemy-1.0.10.tar.gz (4.7MB)
remote:        Collecting sqlalchemy-migrate==0.10.0 (from -r requirements.txt (line 37))
remote:        Downloading sqlalchemy_migrate-0.10.0-py2-none-any.whl (108kB)
remote:        Collecting sqlparse==0.1.18 (from -r requirements.txt (line 38))
remote:        Downloading sqlparse-0.1.18.tar.gz (58kB)
remote:        Collecting Tempita==0.5.2 (from -r requirements.txt (line 39))
remote:        Downloading Tempita-0.5.2.tar.gz
remote:        Collecting visitor==0.1.2 (from -r requirements.txt (line 40))
remote:        Downloading visitor-0.1.2.tar.gz
remote:        Collecting Werkzeug==0.11.2 (from -r requirements.txt (line 41))
remote:        Downloading Werkzeug-0.11.2-py2.py3-none-any.whl (304kB)
remote:        Collecting wheel==0.24.0 (from -r requirements.txt (line 42))
remote:        Downloading wheel-0.24.0-py2.py3-none-any.whl (63kB)
remote:        Collecting WTForms==2.0.2 (from -r requirements.txt (line 43))
remote:        Downloading WTForms-2.0.2-py27-none-any.whl (128kB)
remote:        Installing collected packages: SQLAlchemy, MarkupSafe, Mako, python-editor, alembic, six, html5lib, bleach, blinker, coverage, decorator, dominate, Werkzeug, Jinja2, itsdangerous, Flask, visitor, Flask-Bootstrap, Flask-HTTPAuth, Flask-Login, Flask-Mail, Markdown, Flask-Markdown, Flask-SQLAlchemy, Flask-Script, Flask-Migrate, Flask-Moment, WTForms, Flask-PageDown, Flask-SSLify, Flask-WTF, ForgeryPy, gunicorn, Pygments, requests, httpie, pbr, psycopg2, selenium, sqlparse, Tempita, sqlalchemy-migrate, wheel
remote:        Running setup.py install for SQLAlchemy
remote:        Running setup.py install for MarkupSafe
remote:        Running setup.py install for Mako
remote:        Running setup.py install for python-editor
remote:        Running setup.py install for alembic
remote:        Running setup.py install for html5lib
remote:        Running setup.py install for blinker
remote:        Running setup.py install for coverage
remote:        Running setup.py install for dominate
remote:        Running setup.py install for itsdangerous
remote:        Running setup.py install for Flask
remote:        Running setup.py install for visitor
remote:        Running setup.py install for Flask-Bootstrap
remote:        Running setup.py install for Flask-HTTPAuth
remote:        Running setup.py install for Flask-Login
remote:        Running setup.py install for Flask-Mail
remote:        Running setup.py install for Markdown
remote:        Running setup.py install for Flask-Markdown
remote:        Running setup.py install for Flask-SQLAlchemy
remote:        Running setup.py install for Flask-Script
remote:        Running setup.py install for Flask-Migrate
remote:        Running setup.py install for Flask-Moment
remote:        Running setup.py install for Flask-PageDown
remote:        Running setup.py install for Flask-SSLify
remote:        Running setup.py install for ForgeryPy
remote:        Running setup.py install for psycopg2
remote:        Running setup.py install for sqlparse
remote:        Running setup.py install for Tempita
remote:        Successfully installed Flask-0.10.1 Flask-Bootstrap-3.3.5.7 Flask-HTTPAuth-2.7.0 Flask-Login-0.3.2 Flask-Mail-0.9.1 Flask-Markdown-0.3 Flask-Migrate-1.6.0 Flask-Moment-0.5.1 Flask-PageDown-0.2.1 Flask-SQLAlchemy-2.1 Flask-SSLify-0.1.5 Flask-Script-2.0.5 Flask-WTF-0.12 ForgeryPy-0.1 Jinja2-2.8 Mako-1.0.3 Markdown-2.6.5 MarkupSafe-0.23 Pygments-2.0.2 SQLAlchemy-1.0.10 Tempita-0.5.2 WTForms-2.0.2 Werkzeug-0.11.2 alembic-0.8.4 bleach-1.4.2 blinker-1.4 coverage-4.0.3 decorator-4.0.6 dominate-2.1.16 gunicorn-19.4.5 html5lib-0.9999999 httpie-0.9.3 itsdangerous-0.24 pbr-1.8.1 psycopg2-2.6.1 python-editor-0.5 requests-2.9.1 selenium-2.48.0 six-1.10.0 sqlalchemy-migrate-0.10.0 sqlparse-0.1.18 visitor-0.1.2 wheel-0.24.0
remote:
remote:
remote: -----> Discovering process types
remote:        Procfile declares types -> web
remote:
remote: -----> Compressing... done, 44.6MB
remote: -----> Launching...
remote:        Released v7
remote:        https://flask-blog-chaonet.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/flask-blog-chaonet.git
 * [new branch]      master -> master

成功,按照requirements.txt安装依赖的库

建立运行环境数据库

➜  flask_blog git:(master) heroku run python run.py deploy
Running python run.py deploy on flask-blog-chaonet... up, run.6723
/app/.heroku/python/lib/python2.7/site-packages/flask_sqlalchemy/__init__.py:800: UserWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future.  Set it to True to suppress this warning.
  warnings.warn('SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future.  Set it to True to suppress this warning.')
INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> c1b387222189, empty message
INFO  [alembic.runtime.migration] Running upgrade c1b387222189 -> c4a4f802bdf4, empty message
INFO  [alembic.runtime.migration] Running upgrade c4a4f802bdf4 -> 83e0975c0db1, add role permission
INFO  [alembic.runtime.migration] Running upgrade 83e0975c0db1 -> ee9c55ba9b22, add user profile
INFO  [alembic.runtime.migration] Running upgrade ee9c55ba9b22 -> 431d275d6a70, add user.about_me
INFO  [alembic.runtime.migration] Running upgrade 431d275d6a70 -> d6f4efe3ffe1, add User.avatar_hash
INFO  [alembic.runtime.migration] Running upgrade d6f4efe3ffe1 -> ac6e9871c9a4, add Post ,relationship with User
INFO  [alembic.runtime.migration] Running upgrade ac6e9871c9a4 -> 6f219f6bca6e, add Post.on_changed_body, body_html, db.event.listen
INFO  [alembic.runtime.migration] Running upgrade 6f219f6bca6e -> 3aca4362a467, add Follow model, user.followers
                                                                                                                ollowed
INFO  [alembic.runtime.migration] Running upgrade 3aca4362a467 -> e3cfb10aeab6, add comment model

创建并配置好数据库表之后就可以重启程序了,直接使用下述命令即可:

     $ heroku restart
     Restarting dynos... done

至此,程序就完全部署好了,可通过 https://<appname>.hero-kuapp.com 访问。

查看日志


不同类型的消息,会通过颜色区分显示

➜  flask_blog git:(master) heroku logs
...
2016-01-10T09:04:16.778460+00:00 heroku[run.6723]: Awaiting client
2016-01-10T09:04:16.871340+00:00 heroku[run.6723]: Starting process with command `python run.py deploy`
2016-01-10T09:04:17.236929+00:00 heroku[run.6723]: State changed from starting to up
2016-01-10T09:04:20.574974+00:00 heroku[run.6723]: State changed from up to complete
2016-01-10T09:04:20.567984+00:00 heroku[run.6723]: Process exited with status 0
2016-01-10T09:14:49.296965+00:00 heroku[web.1]: State changed from up to starting
2016-01-10T09:14:53.791001+00:00 heroku[web.1]: Starting process with command `gunicorn run:app`
2016-01-10T09:14:54.840605+00:00 heroku[web.1]: Stopping all processes with SIGTERM
2016-01-10T09:14:56.154205+00:00 app[web.1]: [2016-01-10 09:14:56 +0000] [3] [INFO] Starting gunicorn 19.4.5
2016-01-10T09:14:56.230606+00:00 app[web.1]: [2016-01-10 09:14:56 +0000] [10] [INFO] Booting worker with pid: 10
2016-01-10T09:14:56.154848+00:00 app[web.1]: [2016-01-10 09:14:56 +0000] [3] [INFO] Listening at: http://0.0.0.0:10268 (3)
2016-01-10T09:14:56.154974+00:00 app[web.1]: [2016-01-10 09:14:56 +0000] [3] [INFO] Using worker: sync
2016-01-10T09:14:56.160120+00:00 app[web.1]: [2016-01-10 09:14:56 +0000] [9] [INFO] Booting worker with pid: 9
2016-01-10T09:14:56.476881+00:00 app[web.1]: [2016-01-10 09:14:56 +0000] [10] [INFO] Worker exiting (pid: 10)
2016-01-10T09:14:56.547413+00:00 app[web.1]: [2016-01-10 09:14:56 +0000] [3] [INFO] Shutting down: Master
2016-01-10T09:14:56.475915+00:00 app[web.1]: [2016-01-10 09:14:56 +0000] [9] [INFO] Worker exiting (pid: 9)
2016-01-10T09:14:56.529060+00:00 app[web.1]: [2016-01-10 09:14:56 +0000] [3] [INFO] Handling signal: term
2016-01-10T09:14:56.954815+00:00 app[web.1]: /app/.heroku/python/lib/python2.7/site-packages/flask_sqlalchemy/__init__.py:800: UserWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future.  Set it to True to suppress this warning.
2016-01-10T09:14:56.954818+00:00 app[web.1]:   warnings.warn('SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future.  Set it to True to suppress this warning.')
2016-01-10T09:14:57.283202+00:00 app[web.1]:   warnings.warn('SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future.  Set it to True to suppress this warning.')
2016-01-10T09:14:57.283198+00:00 app[web.1]: /app/.heroku/python/lib/python2.7/site-packages/flask_sqlalchemy/__init__.py:800: UserWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future.  Set it to True to suppress this warning.
2016-01-10T09:14:57.480937+00:00 heroku[web.1]: State changed from starting to up
2016-01-10T09:14:57.513335+00:00 heroku[web.1]: Process exited with status 0
2016-01-10T09:15:06.569626+00:00 heroku[router]: at=info method=GET path="/" host=flask-blog-chaonet.herokuapp.com request_id=32f4cf3a-d766-441b-bbe1-0e7052a37209 fwd="107.161.27.197" dyno=web.1 connect=0ms service=291ms status=200 bytes=3417
2016-01-10T09:15:09.079151+00:00 heroku[router]: at=info method=GET path="/favicon.ico" host=flask-blog-chaonet.herokuapp.com request_id=4e065440-cb19-420a-834e-8e4d8e4c956c fwd="107.161.27.197" dyno=web.1 connect=1ms service=19ms status=404 bytes=655
2016-01-10T09:15:16.012079+00:00 heroku[router]: at=info method=GET path="/" host=flask-blog-chaonet.herokuapp.com request_id=ac1e63ac-0133-4cad-adac-66563437c70c fwd="107.161.27.197" dyno=web.1 connect=0ms service=506ms status=200 bytes=3504
2016-01-10T09:15:17.703574+00:00 heroku[router]: at=info method=GET path="/followed" host=flask-blog-chaonet.herokuapp.com request_id=0fc98138-ca6f-4873-8327-4a22f892d432 fwd="107.161.27.197" dyno=web.1 connect=1ms service=13ms status=302 bytes=875
2016-01-10T09:15:18.327896+00:00 heroku[router]: at=info method=GET path="/auth/login?next=%2Ffollowed" host=flask-blog-chaonet.herokuapp.com request_id=8d44ff19-c673-4e61-929e-d6ce89678b3a fwd="107.161.27.197" dyno=web.1 connect=0ms service=25ms status=200 bytes=4071
2016-01-10T09:15:19.085786+00:00 heroku[router]: at=info method=GET path="/favicon.ico" host=flask-blog-chaonet.herokuapp.com request_id=d327a6e7-f26b-4514-8813-c2af94dfd639 fwd="107.161.27.197" dyno=web.1 connect=0ms service=3ms status=404 bytes=655
2016-01-10T09:15:20.795897+00:00 heroku[router]: at=info method=GET path="/" host=flask-blog-chaonet.herokuapp.com request_id=de598303-d092-4647-8758-9e050d089f2e fwd="107.161.27.197" dyno=web.1 connect=2ms service=23ms status=200 bytes=3504
2016-01-10T09:15:22.840818+00:00 heroku[router]: at=info method=GET path="/auth/login" host=flask-blog-chaonet.herokuapp.com request_id=db4a579c-d80b-4205-963e-53d4f8aeb4a3 fwd="107.161.27.197" dyno=web.1 connect=2ms service=9ms status=200 bytes=4014
2016-01-10T09:15:25.834388+00:00 heroku[router]: at=info method=GET path="/auth/register" host=flask-blog-chaonet.herokuapp.com request_id=32a0d112-6dfa-4c8b-bd25-2bf0296add0d fwd="107.161.27.197" dyno=web.1 connect=2ms service=31ms status=200 bytes=4084
2016-01-10T09:15:28.548938+00:00 heroku[router]: at=info method=GET path="/" host=flask-blog-chaonet.herokuapp.com request_id=0f731062-0cfe-4e51-8967-13ab87ef03c5 fwd="107.161.27.197" dyno=web.1 connect=2ms service=15ms status=200 bytes=3504

应用程序维护模式


维护时可以设置显示维护信息

➜  flask_blog git:(master) heroku maintenance -h
Usage: heroku maintenance

display the current maintenance status of app

 -a, --app APP       # app to run command against
 -r, --remote REMOTE # git remote of app to run command against

Additional commands, type "heroku help COMMAND" for more details:

  maintenance:off  #  take the app out of maintenance mode
  maintenance:on   #  put the app into maintenance mode

开启显示维护信息

➜  flask_blog git:(master) heroku maintenance:on
Enabling maintenance mode for flask-blog-chaonet... done

页面显示的内容

Application Offline for Maintenance
This application is undergoing maintenance right now. Please check back later.

维护结束后,重新开启应用,正常访问

➜  flask_blog git:(master) heroku maintenance:off
Disabling maintenance mode for flask-blog-chaonet... done
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容