使用手动 docker 构建在 Heroku 上托管 API

Host an API on Heroku using a manual docker build

我想在 Heroku 免费套餐上托管一个小型 Web 应用程序。我在本地使用 docker compose 到 运行 前端、后端 api 和一个 postgres 数据库。我正在关注 deploying an existing docker image 上的 Heroku 文档。但是,当我尝试访问 API 文档,或尝试卷曲 API(两者都在本地工作)时,我收到错误

2021-01-31T15:19:23.679917+00:00 heroku[router]: at=error code=H14 desc="No web processes running" method=GET path="/docs" host=apitest48398.herokuapp.com request_id=62ffd495-5977-4132-9ec7-d89abf5b1d8f fwd="77.100.21.140" dyno= connect= service= status=503 bytes= protocol=https
2021-01-31T15:19:24.465326+00:00 heroku[router]: at=error code=H14 desc="No web processes running" method=GET path="/favicon.ico" host=apitest48398.herokuapp.com request_id=016de415-dc8e-4b0a-894e-8fad37ff7fee fwd="77.100.21.140" dyno= connect= service= status=503 bytes= protocol=https

请注意,我正在通过容器注册表进行部署,而不是通过将我的 git 存储库连接到 Heroku,因此既不是 Procfile,也不是 heroku.yml 文件。

以下是在最小示例中重现此行为的步骤。请注意,在实际示例中,我有多个 docker 文件,因此即使这个简单示例可以使用 heroku container:push 部署,我更喜欢自己构建图像,因此简单示例更能代表实际用例:

  1. 在名为 main.py 的文件中使用 fastAPI 创建应用程序:
    from fastapi import FastAPI
    
    app = FastAPI()
    
    
    @app.get("/")
    async def root():
        return {"message": "Hello World"}
    
  2. 创建一个 requirements.txt 文件:
    fastapi
    uvicorn
    
  3. 创建 Dockerfile
    FROM python:3.7
    
    ENV PORT=$PORT
    
    COPY ./requirements.txt .
    RUN pip install -r requirements.txt
    
    COPY ./main.py .
    
    EXPOSE $PORT
    
    CMD uvicorn main:app --host 0.0.0.0 --port $PORT
    
    
  4. 构建图像
    docker build . -t herokubackendtest
    
  5. 登录到heroku
    heroku container:login
    
  6. 创建 heroku 应用程序并将图像推送到 heroku(您需要使用不同的、唯一的 APP_NAME)
    export APP_NAME=apitest48398
    heroku create $APP_NAME
    docker tag herokubackendtest registry.heroku.com/$APP_NAME/api
    docker push registry.heroku.com/$APP_NAME/api
    
  7. 发布应用程序,放大 dyno,检查进程是否为 运行ning,并检查日志以验证 uvicorn 是否为 运行ning:
    heroku container:release --app $APP_NAME api
    heroku ps:scale --app $APP_NAME api=1
    heroku ps --app $APP_NAME  # should show === api (Free): /bin/sh -c uvicorn\ main:app\ --host\ 0.0.0.0\ --port\ $PORT (1)
    heroku logs --app $APP_NAME --tail
    

您应该在日志中看到以下内容(端口号会有所不同)

2021-01-31T15:17:39.806481+00:00 heroku[api.1]: Starting process with command `/bin/sh -c uvicorn\ main:app\ --host\ 0.0.0.0\ --port\ 384`
2021-01-31T15:17:40.465132+00:00 heroku[api.1]: State changed from starting to up
2021-01-31T15:17:41.982857+00:00 app[api.1]: INFO:     Started server process [5]
2021-01-31T15:17:41.982918+00:00 app[api.1]: INFO:     Waiting for application startup.
2021-01-31T15:17:41.983162+00:00 app[api.1]: INFO:     Application startup complete.
2021-01-31T15:17:41.983529+00:00 app[api.1]: INFO:     Uvicorn running on http://0.0.0.0:16384 (Press CTRL+C to quit)

现在要测试它,您应该能够在 https://apitest48398.herokuapp.com/docs 看到 openAPI (swagger) 文档,其中 apitest48398 是您的 $APP_NAME。但是我在这个问题开始时发布的日志中得到了错误,我看到一个页面说:

Application error
An error occurred in the application and your page could not be served. If you are the application owner, check your logs for details. You can do this from the Heroku CLI with the command
heroku logs --tail

在我的浏览器中。

我也希望 curl https://apitest48398.herokuapp.com/ 到 return 我的 hello world json,但它 return 一些 HTML 有错误.我也尝试过使用端口 44380,并且端口 uvicorn 在容器内 运行ning(这个一直挂起直到超时)。

我想我错过了一些步骤,也许是 SSL?虽然我知道这是为免费层上的任何 herokuapp.com 域自动启用的,但我看不出有什么方法可以将证书和 pem 文件传递​​给 uvicorn。

任何人都可以指出正确的方向来解决这个问题吗?

使用 web 进程类型标记图像,如 Heroku Docker Registry 所示。你的情况

docker tag herokubackendtest registry.heroku.com/$APP_NAME/web
docker push registry.heroku.com/$APP_NAME/web
heroku container:release web --app $APP_NAME