当前位置: 资讯 > 美国G口大带宽服务器 >TestDriven.io 博客中文翻译(二十五)
相关服务:美国G口大带宽服务器租用
烧瓶条纹教程
本教程展示了如何将 Stripe 添加到 Flask 应用程序中,以接受一次性付款。
需要处理订阅付款?查看烧瓶条纹订阅。
条纹支付策略
Stripe 目前有三种接受一次性付款的策略:
你应该使用哪种策略?
- 如果您想快速启动并运行,请使用条带检出。如果您已经使用了 Checkout 的旧的模态版本,并且正在寻找一种类似的方法,那么这是一条可行之路。它提供了许多开箱即用的强大功能,支持多种语言,甚至可以用于定期支付。最重要的是,Checkout 为您管理整个付款过程,因此您甚至无需添加任何表单就可以开始接受付款!
- 如果您希望为最终用户定制付款体验,请使用付款意向 API(以及元素)。
收费 API 呢?
- 虽然您仍然可以使用 Charges API,但是如果您是 Stripe 的新手,请不要使用它,因为它不支持最新的银行法规(如 SCA )。如果使用的话,你会看到很高的下降率。如需了解更多信息,请查看官方 Stripe 文档中的费用与付款意向 API页面。
- 还在用收费 API?如果你的大多数客户都在美国或加拿大,你还不需要迁移。查看结帐迁移指南指南了解更多信息。
初始设置
第一步是设置一个基本的 Python 环境并安装 Flask。
创建一个新的项目文件夹,创建并激活一个虚拟环境,并安装 Flask:
`$ mkdir flask-stripe-checkout && cd flask-stripe-checkout
$ python3.10 -m venv env
$ source env/bin/activate
(env)$ pip install flask`
你可以随意把 virtualenv 和 Pip 换成诗歌或 Pipenv 。更多信息,请查看现代 Python 环境。
接下来,创建一个名为 app.py 的文件,并添加一个基本的“Hello World”应用程序的代码:
`# app.py
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/hello")
def hello_world():
return jsonify("hello, world!")
if __name__ == "__main__":
app.run()`
启动服务器:
`(env)$ FLASK_ENV=development python app.py`
你应该在你的浏览器中看到"hello, world!"在http://127 . 0 . 0 . 1:5000/hello。
添加条纹
条纹时间到了。从安装开始:
`(env)$ pip install stripe`
接下来,注册一个新的 Stripe 账户(如果你还没有的话),然后导航到仪表盘。点击“开发者”:

然后在左侧栏中点击“API keys”:

每个 Stripe 帐户有四个 API 密钥:两个用于测试,两个用于生产。每一对都有一个“秘密密钥”和一个“可公开密钥”。不要向任何人透露密钥;可发布的密钥将被嵌入到任何人都可以看到的页面上的 JavaScript 中。
目前右上角的“查看测试数据”开关表示我们正在使用测试键。这就是我们想要的。
将您的测试 API 键存储为环境变量,如下所示:
`(env)$ export STRIPE_PUBLISHABLE_KEY=<YOUR_STRIPE_PUBLISHABLE_KEY>
(env)$ export STRIPE_SECRET_KEY=<YOUR_STRIPE_SECRET_KEY>`
接下来,将条带密钥添加到您的应用程序中:
`# app.py
import os
import stripe
from flask import Flask, jsonify
app = Flask(__name__)
stripe_keys = {
"secret_key": os.environ["STRIPE_SECRET_KEY"],
"publishable_key": os.environ["STRIPE_PUBLISHABLE_KEY"],
}
stripe.api_key = stripe_keys["secret_key"]
@app.route("/hello")
def hello_world():
return jsonify("hello, world!")
if __name__ == "__main__":
app.run()`
最后,您需要在https://dashboard.stripe.com/settings/account的“帐户设置”中指定一个“帐户名称”:

创造产品
接下来,我们需要创造一个产品来销售。
单击顶部导航栏中的“产品”,然后单击“添加产品”:

添加产品名称,输入价格,然后选择“一次性”:

点击“保存产品”。
有了 API 密钥和产品设置,我们现在可以开始添加 Stripe Checkout 来处理支付。
工作流程
在用户点击购买按钮后,我们需要做以下事情:
-
获取可发布密钥
- 从客户端向服务器发送 AJAX 请求,请求可发布的密钥
- 用键回应
- 使用键创建 Stripe.js 的新实例
-
创建签出会话
- 向服务器发送另一个 AJAX 请求,请求一个新的结帐会话 ID
- 生成新的签出会话并发回 ID
- 重定向到用户完成购买的结帐页面
-
适当地重定向用户
- 成功付款后重定向到成功页面
- 取消付款后重定向到取消页面
-
用条纹网钩确认付款
- 设置 webhook 端点
- 使用条带 CLI 测试端点
- 用条带注册端点
获取可发布密钥
JavaScript 静态文件
让我们首先创建一个新的静态文件来保存我们所有的 JavaScript。
添加一个名为“static”的新文件夹,然后向该文件夹添加一个名为 main.js 的新文件:
`// static/main.js console.log("Sanity check!");`
接下来,向 app.py 添加一条新路线,该路线提供了一个【index.html】模板*😗
`# app.py
@app.route("/")
def index():
return render_template("index.html")`
确保也导入render_template:
`from flask import Flask, jsonify, render_template`
对于模板,首先添加一个名为“templates”的新文件夹,然后添加一个名为base.html的基础模板,其中包含了提供 main.js 静态文件的脚本标签:
`<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Flask + Stripe Checkout</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[[email protected]](/cdn-cgi/l/email-protection)/css/bulma.min.css">
<script src="{{ url_for('static', filename='main.js') }}"></script>
<script defer src="https://use.fontawesome.com/releases/v5.14.0/js/all.js"></script>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>`
接下来,向名为index.html的新模板添加一个支付按钮:
`<!-- templates/index.html -->
{% extends "base.html" %}
{% block content %}
<section class="section">
<div class="container">
<button class="button is-primary" id="submitBtn">Purchase!</button>
</div>
</section>
{% endblock %}`
再次运行开发服务器:
`(env)$ FLASK_ENV=development python app.py`
导航到 http://127.0.0.1:5000 ,打开 JavaScript 控制台。您应该看到健全性检查:

途径
接下来,向 app.py 添加一个新的路由来处理 AJAX 请求:
`# app.py
@app.route("/config")
def get_publishable_key():
stripe_config = {"publicKey": stripe_keys["publishable_key"]}
return jsonify(stripe_config)`
AJAX 请求
接下来,使用获取 API 向 static/main.js 中的新/config端点发出 AJAX 请求:
`// static/main.js console.log("Sanity check!"); // new // Get Stripe publishable key fetch("/config") .then((result) => { return result.json(); }) .then((data) => { // Initialize Stripe.js const stripe = Stripe(data.publicKey); });`
来自fetch请求的响应是一个可读流。result.json()返回一个承诺,我们将它解析为一个 JavaScript 对象——即data。然后我们使用点符号来访问publicKey以获得可发布的密钥。
将 Stripe.js 包含在 templates/base.html 中,就像这样:
`<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Flask + Stripe Checkout</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[[email protected]](/cdn-cgi/l/email-protection)/css/bulma.min.css">
<script src="https://js.stripe.com/v3/"></script> <!-- new -->
<script src="{{ url_for('static', filename='main.js') }}"></script>
<script defer src="https://use.fontawesome.com/releases/v5.14.0/js/all.js"></script>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>`
现在,在页面加载之后,将调用/config,它将使用 Stripe publish key 进行响应。然后,我们将使用这个键创建 Stripe.js 的新实例。
工作流程:
-
获取可发布密钥从客户端向服务器发送 AJAX 请求,请求可发布密钥用键响应使用键创建 Stripe.js 的新实例
-
创建签出会话
- 向服务器发送另一个 AJAX 请求,请求一个新的结帐会话 ID
- 生成新的签出会话并发回 ID
- 重定向到用户完成购买的结帐页面
-
适当地重定向用户
- 成功付款后重定向到成功页面
- 取消付款后重定向到取消页面
-
用条纹网钩确认付款
- 设置 webhook 端点
- 使用条带 CLI 测试端点
- 用条带注册端点
创建签出会话
接下来,我们需要将一个事件处理程序附加到按钮的 click 事件,该事件将向服务器发送另一个 AJAX 请求,以生成一个新的结帐会话 ID。
途径
首先,添加新路由:
`# app.py
@app.route("/create-checkout-session")
def create_checkout_session():
domain_url = "http://127.0.0.1:5000/"
stripe.api_key = stripe_keys["secret_key"]
try:
# Create new Checkout Session for the order
# Other optional params include:
# [billing_address_collection] - to display billing address details on the page
# [customer] - if you have an existing Stripe Customer ID
# [payment_intent_data] - capture the payment later
# [customer_email] - prefill the email input in the form
# For full details see https://stripe.com/docs/api/checkout/sessions/create
# ?session_id={CHECKOUT_SESSION_ID} means the redirect will have the session ID set as a query param
checkout_session = stripe.checkout.Session.create(
success_url=domain_url + "success?session_id={CHECKOUT_SESSION_ID}",
cancel_url=domain_url + "cancelled",
payment_method_types=["card"],
mode="payment",
line_items=[
{
"name": "T-shirt",
"quantity": 1,
"currency": "usd",
"amount": "2000",
}
]
)
return jsonify({"sessionId": checkout_session["id"]})
except Exception as e:
return jsonify(error=str(e)), 403`
在这里,我们-
- 定义了一个
domain_url(用于重定向) - 将条带密钥分配给
stripe.api_key(因此当我们请求创建新的结帐会话时,它将被自动发送) - 创建了签出会话
- 在响应中发回了 ID
注意使用了domain_url的success_url和cancel_url。在成功支付或取消的情况下,用户将分别被重定向回这些 URL。我们将很快设立/success和/cancelled路线。
AJAX 请求
将事件处理程序和后续 AJAX 请求添加到 static/main.js :
`// static/main.js console.log("Sanity check!"); // Get Stripe publishable key fetch("/config") .then((result) => { return result.json(); }) .then((data) => { // Initialize Stripe.js const stripe = Stripe(data.publicKey); // new // Event handler document.querySelector("#submitBtn").addEventListener("click", () => { // Get Checkout Session ID fetch("/create-checkout-session") .then((result) => { return result.json(); }) .then((data) => { console.log(data); // Redirect to Stripe Checkout return stripe.redirectToCheckout({sessionId: data.sessionId}) }) .then((res) => { console.log(res); }); }); });`
这里,在解析了result.json()承诺之后,我们调用了 redirectToCheckout 方法,该方法带有来自已解析承诺的结帐会话 ID。
导航到 http://127.0.0.1:5000 。点击按钮后,您将被重定向到 Stripe Checkout 实例(一个 Stripe 托管页面,用于安全收集支付信息),其中包含 t 恤产品信息:

我们可以使用 Stripe 提供的几个测试卡号中的一个来测试表单。还是用4242 4242 4242 4242吧。
- 电子邮件:有效的电子邮件
- 卡号:
4242 4242 4242 4242 - 到期日:未来的任何日期
- CVC:任何三个数字
- 名称:任何东西
- 邮政编码:任意五个数字
如果一切顺利,付款应该被处理,但重定向将失败,因为我们还没有设置/success URL。
工作流程:
-
获取可发布密钥从客户端向服务器发送 AJAX 请求,请求可发布密钥用键响应使用键创建 Stripe.js 的新实例
-
创建结账会话向服务器发送另一个 AJAX 请求,请求一个新的结帐会话 ID生成新的结账会话并发回 ID重定向到用户完成购买的结账页面
-
适当地重定向用户
- 成功付款后重定向到成功页面
- 取消付款后重定向到取消页面
-
用条纹网钩确认付款
- 设置 webhook 端点
- 使用条带 CLI 测试端点
- 用条带注册端点
适当地重定向用户
最后,让我们连接用于处理成功和取消重定向的模板和路由。
成功模板:
`<!-- templates/success.html -->
{% extends "base.html" %}
{% block content %}
<section class="section">
<div class="container">
<p>Your payment succeeded.</p>
</div>
</section>
{% endblock %}`
已取消的模板:
`<!-- templates/cancelled.html -->
{% extends "base.html" %}
{% block content %}
<section class="section">
<div class="container">
<p>Your payment was cancelled.</p>
</div>
</section>
{% endblock %}`
路线:
`# app.py
@app.route("/success")
def success():
return render_template("success.html")
@app.route("/cancelled")
def cancelled():
return render_template("cancelled.html")`
回到 http://127.0.0.1:5000 ,点击支付按钮,再次使用信用卡号4242 4242 4242 4242以及其余的虚拟信用卡信息。提交付款。你应该被重定向回http://127 . 0 . 0 . 1:5000/success。
要确认实际收费,请点击 Stripe 仪表盘上的“支付”返回:

回顾一下,我们使用密钥在服务器上创建一个惟一的签出会话 ID。这个 ID 随后被用来创建一个结帐实例,最终用户在点击支付按钮后被重定向到这个实例。收费发生后,他们会被重定向回成功页面。
一定要测试出一个被取消的付款。
工作流程:
-
获取可发布密钥从客户端向服务器发送 AJAX 请求,请求可发布密钥用键响应使用键创建 Stripe.js 的新实例
-
创建结账会话向服务器发送另一个 AJAX 请求,请求一个新的结帐会话 ID生成新的结账会话并发回 ID重定向到用户完成购买的结账页面
-
适当地重定向用户支付成功后重定向至成功页面取消付款后重定向至取消页面
-
用条纹网钩确认付款
- 设置 webhook 端点
- 使用条带 CLI 测试端点
- 用条带注册端点
用条纹网钩确认付款
我们的应用程序在这一点上工作得很好,但我们仍然不能以编程方式确认支付,或者在支付成功时运行一些代码。我们已经在用户结帐后将他们重定向到成功页面,但是我们不能只依赖那个页面,因为付款确认是异步发生的。
Stripe 和一般编程中有两种类型的事件:同步事件,具有即时效果和结果(例如,创建一个客户),异步事件,没有即时结果(例如,确认付款)。因为支付确认是异步完成的,用户可能会在他们的支付被确认之前和我们收到他们的资金之前被重定向到成功页面。
当支付完成时,获得通知的最简单的方法之一是使用回调或所谓的 Stripe webhook 。我们需要在应用程序中创建一个简单的端点,每当事件发生时(例如,当用户购买 T 恤衫时),Stripe 就会调用这个端点。通过使用 webhooks,我们可以绝对肯定支付成功。
为了使用 webhooks,我们需要:
- 设置 webhook 端点
- 使用条带 CLI 测试端点
- 用条带注册端点
这一部分由尼克·托马兹奇撰写。
端点
创建一个名为stripe_webhook的新路径,它会在每次支付成功时打印一条消息:
`# app.py
@app.route("/webhook", methods=["POST"])
def stripe_webhook():
payload = request.get_data(as_text=True)
sig_header = request.headers.get("Stripe-Signature")
try:
event = stripe.Webhook.construct_event(
payload, sig_header, stripe_keys["endpoint_secret"]
)
except ValueError as e:
# Invalid payload
return "Invalid payload", 400
except stripe.error.SignatureVerificationError as e:
# Invalid signature
return "Invalid signature", 400
# Handle the checkout.session.completed event
if event["type"] == "checkout.session.completed":
print("Payment was successful.")
# TODO: run some custom code here
return "Success", 200`
stripe_webhook现在作为我们的 webhook 端点。这里,我们只寻找每当结帐成功时调用的checkout.session.completed事件,但是您可以对其他条带事件使用相同的模式。
确保将request导入添加到顶部:
`from flask import Flask, jsonify, render_template, request`
测试 webhook
我们将使用 Stripe CLI 来测试 webhook。
一旦下载并安装了,在新的终端窗口中运行以下命令,登录到您的 Stripe 帐户:
此命令应生成一个配对代码:
`Your pairing code is: peach-loves-classy-cozy
This pairing code verifies your authentication with Stripe.
Press Enter to open the browser (^C to quit)`
通过按 Enter,CLI 将打开您的默认 web 浏览器,并请求访问您的帐户信息的权限。请继续并允许访问。回到您的终端,您应该看到类似于以下内容的内容:
`> Done! The Stripe CLI is configured for Flask Stripe Test with account id acct_<ACCOUNT_ID>
Please note: this key will expire after 90 days, at which point you'll need to re-authenticate.`
接下来,我们可以开始侦听条带事件,并使用以下命令将它们转发到我们的端点:
`$ stripe listen --forward-to 127.0.0.1:5000/webhook`
这也将生成一个 webhook 签名密码:
`> Ready! Your webhook signing secret is whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (^C to quit)`
为了初始化端点,将密码保存为另一个环境变量,如下所示:
`(env)$ export STRIPE_ENDPOINT_SECRET=<YOUR_STRIPE_ENDPOINT_SECRET>`
接下来,像这样将它添加到stripe_keys字典中:
`# app.py
stripe_keys = {
"secret_key": os.environ["STRIPE_SECRET_KEY"],
"publishable_key": os.environ["STRIPE_PUBLISHABLE_KEY"],
"endpoint_secret": os.environ["STRIPE_ENDPOINT_SECRET"], # new
}`
Stripe 现在会将事件转发到我们的端点。要测试,通过4242 4242 4242 4242运行另一个测试支付。在您的终端中,您应该会看到Payment was successful.消息。
一旦完成,停止stripe listen --forward-to 127.0.0.1:5000/webhook过程。
如果您想识别进行购买的用户,可以使用 client_reference_id 将某种用户标识符附加到条带会话。
例如:
@app.route("/create-checkout-session") def create_checkout_session(): domain_url = "http://127.0.0.1:5000/" stripe.api_key = stripe_keys["secret_key"] try: checkout_session = stripe.checkout.Session.create( # new client_reference_id=current_user.id if current_user.is_authenticated else None, success_url=domain_url + "success?session_id={CHECKOUT_SESSION_ID}", cancel_url=domain_url + "cancelled", payment_method_types=["card"], mode="payment", line_items=[ { "name": "T-shirt", "quantity": 1, "currency": "usd", "amount": "2000", } ] ) return jsonify({"sessionId": checkout_session["id"]}) except Exception as e: return jsonify(error=str(e)), 403
注册端点
最后,在部署你的应用程序后,你可以在 Stripe 仪表板中注册端点,在开发者> Webhooks 下。这将生成一个 webhook 签名密码,用于您的生产应用程序。
例如:

工作流程:
-
获取可发布密钥从客户端向服务器发送 AJAX 请求,请求可发布密钥用键响应使用键创建 Stripe.js 的新实例
-
创建结账会话向服务器发送另一个 AJAX 请求,请求一个新的结帐会话 ID生成新的结账会话并发回 ID重定向到用户完成购买的结账页面
-
适当地重定向用户支付成功后重定向至成功页面取消付款后重定向至取消页面
-
用条纹网钩确认付款设置 webhook 端点使用条带 CLI 测试端点用条带注册端点
后续步骤
在生产中,你需要有 HTTPS,这样你的连接是安全的。您可能还想将domain_url存储为一个环境变量。最后,在创建结帐会话之前,最好确认在/create-checkout-session路线中使用了正确的产品和价格。为此,您可以:
- 将您的每个产品添加到数据库中。
- 然后,当您动态创建产品页面时,将产品数据库 ID 和价格存储在 purchase 按钮的数据属性中。
- 更新
/create-checkout-session路由以仅允许 POST 请求。 - 更新 JavaScript 事件监听器,从数据属性中获取产品信息,并将它们与 AJAX POST 请求一起发送到
/create-checkout-session路由。 - 在创建结帐会话之前,解析路由处理程序中的 JSON 有效负载,并确认产品存在且价格正确。
干杯!
–
从 GitHub 上的flask-strip-check outrepo 中抓取代码。
烧瓶教程
描述
Flask 是一个 Python web 框架,用于构建 web 应用程序。它是轻量级的,所以它不会为你做很多决定。换句话说,你可以决定你想要如何实现事情。本质上,Flask 简单而可扩展,非常适合开发 RESTful APIs 和微服务。
TestDriven.io 上的教程和文章更接近高级,涵盖了 Docker 容器化、部署、支付处理以及将 Flask 与 React 和 Vue 等前端框架相结合等主题。
通过创建一个静态站点并将其部署到 Netlify,利用 Python 和 Flask 的 JAMstack。
本教程展示了如何使用 Flask、WhiteNoise 和 Amazon CloudFront 管理静态文件。
- 发帖者 发帖者
阿玛尔沙姬 - 最后更新于2023 年 2 月 10 日
常用的 web 身份验证方法。
在本文中,我们将从教育和开发的角度来看 Django 和 Flask 的最佳用例,以及它们的独特之处。
在 Python 应用程序中启用多区域支持。
- 发帖者 发帖者
阿玛尔沙姬 - 最后更新于2022 年 11 月 14 日
向 Flask 应用程序添加社交认证。
部署一个带有 PostgreSQL 的 Flask 应用程序进行渲染。
- 发帖者 发帖者
阿玛尔沙姬 - 最后更新于2022 年 10 月 14 日
这篇文章着眼于什么是 CSRF 以及如何在 Flask 中防止 CSRF 攻击。
将 Flask 配置为与 Postgres、Nginx 和 Gunicorn 一起在 Docker 上运行。
- 发帖者 发帖者
阿玛尔沙姬 - 最后更新于2022 年 9 月 23 日
将基于会话的身份验证添加到由 Flask 和 Svelte 支持的单页面应用程序(SPA)中。
本教程着眼于如何用 Flask 和 Stripe 处理订阅支付。
使用 APIFairy 构建一个 Flask API。
- 发帖者 发帖者
阿玛尔沙姬 - 最后更新于2022 年 4 月 18 日
将 htmx 和 Tailwind CSS 添加到 Flask 中,以提高开发人员的工作效率。
- 发帖者 发帖者
阿玛尔沙姬 - 最后更新于2022 年 3 月 28 日
有兴趣从 Flask 转到 FastAPI 吗?本文比较和对比了 Flask 和 FastAPI 中的常见模式。
将一个 Flask 应用程序部署到 AWS Elastic Beanstalk。
高级查看应用程序和请求上下文如何在 Flask 中工作。
对 TDD 如何工作感兴趣?本指南将引导您从头到尾使用现代工具和技术完成整个过程。
查看如何配置 Celery 来处理 Flask 应用程序中的长时间运行的任务。
查看如何配置 Redis 队列(RQ)来处理 Flask 应用程序中的长时间运行的任务。
快速将 Stripe 添加到 Flask 应用程序,以便接受付款。
Flask 中应用程序和请求上下文如何工作的概述。
pytest 测试烧瓶应用指南。
使用 Hashicorp 的 Vault 和 Consul 为 Flask web 应用程序创建动态 Postgres 凭据的真实示例。
向 Flask、Redis Queue 和 Amazon SES 的新注册用户发送确认电子邮件。
- 发帖者
郄佳朝 Medlin - 最后更新于2021 年 11 月 3 日
结合烧瓶和 Vue 的三种不同方法。
- 发帖者 发帖者
阿玛尔沙姬 - 最后更新于2021 年 6 月 18 日
本教程详细介绍了如何配置 Flask 与 Postgres、Gunicorn、Traefik 和 Let’s Encrypt 一起在 Docker 上运行。
本文着眼于 Flask 2.0 的新异步功能,以及如何在 Flask 项目中利用它。
如何将基于 Flask 的微服务(以及 Postgres 和 Vue.js)部署到 Kubernetes 集群的分步演练。
了解什么是 Werkzeug,以及 Flask 如何将其用于核心 HTTP 功能。
通过 Flask-Session 和 Redis 了解如何在 Flask 中使用服务器端会话。
这篇文章介绍了如何在 Docker Swarm 上运行 Flask 应用程序。
了解会话如何在 Flask 中工作。
使用 Stripe、Vue.js 和 Flask 开发一个销售产品的 web 应用程序。
使用 Gitlab CI 将 Flask 和 Vue 支持的全栈 web 应用打包并部署到 Heroku。
如何用 Vue 和 Flask 设置一个基本的 CRUD 应用程序的分步演练。
生产中的流水作业
在本教程中,我们将看看如何用 Docker 配置 Flower 在 Nginx 后面运行。我们还将设置基本身份验证。
码头工人
假设您使用 Redis 作为您的消息代理,您的 Docker 编写配置将类似于:
`version: '3.7' services: redis: image: redis expose: - 6379 worker: build: context: . dockerfile: Dockerfile command: ['celery', '-A', 'app.app', 'worker', '-l', 'info'] environment: - BROKER_URL=redis://redis:6379 - RESULT_BACKEND=redis://redis:6379 depends_on: - redis flower: image: mher/flower:0.9.7 command: ['flower', '--broker=redis://redis:6379', '--port=5555'] ports: - 5557:5555 depends_on: - redis`
截至发稿时,官方 Flower Docker 图像还没有版本> 0.9.7 的标签,这就是为什么使用了
0.9.7标签。更多信息见1 . 0 . 0 版本的 Docker 标签。想检查正在使用的版本吗?运行
docker-compose exec flower pip freeze。
您可以在 GitHub 上的芹菜花码头 repo 中查看这个样本代码。
app.py :
`import os
from celery import Celery
os.environ.setdefault('CELERY_CONFIG_MODULE', 'celery_config')
app = Celery('app')
app.config_from_envvar('CELERY_CONFIG_MODULE')
@app.task
def add(x, y):
return x + y`
芹菜 _ 配置. py :
`from os import environ
broker_url = environ['BROKER_URL']
result_backend = environ['RESULT_BACKEND']`
Dockerfile :
`FROM python:3.10
WORKDIR /usr/src/app
RUN pip install celery[redis]==5.2.7
COPY app.py .
COPY celery_config.py .`
快速测试:
`$ docker-compose build
$ docker-compose up -d --build
$ docker-compose exec worker python
>> from app import add
>> task = add.delay(5, 5)
>>>
>>> task.status
'SUCCESS'
>>> task.result
10`
您应该能够在 http://localhost:5557/ 查看仪表板。
Nginx
要在 Nginx 之后运行 flower,首先将 Nginx 添加到 Docker Compose 配置中:
`version: '3.7' services: redis: image: redis expose: - 6379 worker: build: context: . dockerfile: Dockerfile command: ['celery', '-A', 'app.app', 'worker', '-l', 'info'] environment: - BROKER_URL=redis://redis:6379 - RESULT_BACKEND=redis://redis:6379 depends_on: - redis flower: image: mher/flower:0.9.7 command: ['flower', '--broker=redis://redis:6379', '--port=5555'] expose: # new - 5555 depends_on: - redis # new nginx: image: nginx:latest volumes: - ./nginx.conf:/etc/nginx/nginx.conf ports: - 80:80 depends_on: - flower`
engine . conf:
`events {} http { server { listen 80; # server_name your.server.url; location / { proxy_pass http://flower:5555; proxy_set_header Host $host; proxy_redirect off; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } } }`
快速测试:
`$ docker-compose down
$ docker-compose build
$ docker-compose up -d --build
$ docker-compose exec worker python
>> from app import add
>> task = add.delay(7, 7)
>>>
>>> task.status
'SUCCESS'
>>> task.result
14`
这一次,仪表板应该可以在 http://localhost/ 上看到。
证明
要添加基本认证,首先创建一个 htpasswd 文件。例如:
`$ htpasswd -c htpasswd michael`
接下来,向nginx服务添加另一个卷,以将 htpasswd 从主机挂载到 /etc/nginx/。容器中的 htpasswd :
`nginx: image: nginx:latest volumes: - ./nginx.conf:/etc/nginx/nginx.conf - ./htpasswd:/etc/nginx/.htpasswd # new ports: - 80:80 depends_on: - flower`
最后,为了保护“/”路由,将 auth_basic 和 auth_basic_user_file 指令添加到位置块:
`events {} http { server { listen 80; # server_name your.server.url; location / { proxy_pass http://flower:5555; proxy_set_header Host $host; proxy_redirect off; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; auth_basic "Restricted"; auth_basic_user_file /etc/nginx/.htpasswd; } } }`
有关使用 Nginx 设置基本身份验证的更多信息,请查看使用 HTTP 基本身份验证限制访问指南。
最终测试:
`$ docker-compose down
$ docker-compose build
$ docker-compose up -d --build
$ docker-compose exec worker python
>> from app import add
>> task = add.delay(9, 9)
>>>
>>> task.status
'SUCCESS'
>>> task.result
18`
再次导航到 http://localhost/ 。这一次应该会提示您输入用户名和密码。
前端教程
描述
前端 web 开发(也称为客户端开发)是使用 HTML、CSS 和 JavaScript(以及 JavaScript 库和框架)为 web 和移动应用程序构建用户可以查看和交互的图形用户界面的过程。换句话说:这是一种软件工程,为网络和移动设备上的视觉和交互提供动力。
由于 React、Vue 和 Angular 等现代前端框架,web 开发已经显著转向前端。越来越多的情况是,后端只是为前端提供数据来管理、准备和显示给最终用户。
TestDriven.io 上的教程和文章讲述了如何用 React、Vue、Angular、Svelte、htmx 和 Tailwind CSS 构建和测试现代用户界面。
通过创建一个静态站点并将其部署到 Netlify,利用 Python 和 Flask 的 JAMstack。
如何用 Vue 和 FastAPI 设置一个基本的 CRUD 应用程序的分步演练。
测试 Vue 应用程序中的 Pinia 数据存储。
- 发帖者 发帖者
阿玛尔沙姬 - 最后更新于2022 年 9 月 23 日
将基于会话的身份验证添加到由 Flask 和 Svelte 支持的单页面应用程序(SPA)中。
本文作为单元测试 Vue 组件的指南。
提高 Pyodide 代码的可维护性,用 PouchDB 增加一个持久数据层。
将 web workers 配置为使用 Pyodide,并在浏览器中使用 Pandas 分析数据。
用 Python 和 Pyodide 构建单页面应用。
用 FastAPI 构建 CRUD app,React。
- 发帖者 发帖者
阿玛尔沙姬 - 最后更新于2022 年 4 月 18 日
将 htmx 和 Tailwind CSS 添加到 Flask 中,以提高开发人员的工作效率。
使用 WebAssembly (WASM)、via Pyodide 和 CodeMirror 在浏览器中构建 Python 代码编辑器。
- 发帖者 发帖者
阿玛尔沙姬 - 最后更新于2022 年 4 月 3 日
在 Django 中加入 htmx 和 Tailwind CSS,提高开发者生产力。
- 发帖者
郄佳朝 Medlin - 最后更新于2021 年 11 月 3 日
结合烧瓶和 Vue 的三种不同方法。
在第 1 部分中,我们将建立整个项目,然后用测试驱动开发来开发 UI。
在第 2 部分中,我们将在开始添加基本的计算器功能之前完成 UI。
本文着眼于实现单页面应用程序(SPA)的新方法——基于 WebSockets 的 HTML。
使用 Stripe、Vue.js 和 Flask 开发一个销售产品的 web 应用程序。
如何用 Vue 和 Flask 设置一个基本的 CRUD 应用程序的分步演练。
- 由 发布
尼克·托马齐奇 - 最后更新于2020 年 12 月 14 日
将基于会话的身份验证添加到由 Django 和 React 支持的单页面应用程序(SPA)中。
本文是对 CSS Grid 的介绍,您将看到如何用简单的 CSS 创建复杂的基于网格的布局。
本文着眼于如何使用 useContext 和 useReducer 挂钩来使 React 应用程序及其状态管理变得干净而高效。
React 钩子的介绍。
在下面的教程中,我们将带您了解如何配置 Cypress 来与 CircleCI 并行运行测试。
这篇文章着眼于如何将 Cypress 引入到您的测试驱动开发工作流中。
这篇文章详细介绍了如何使用 Cypress 和 Docker 为 Angular 应用程序添加端到端测试。
这篇文章整理了网上一些从 Heroku 迁移到 AWS 的最佳教程。
从 Travis CI 获取电报通知
原文:https://testdriven.io/blog/getting-telegram-notifications-from-travis-ci/
我最近在 TestDriven.io 上完成了关于 Docker、Flask 和 React 的微服务课程,在课程进行到一半时,我开始有点不耐烦 Travis CI 花了多长时间来完成我的构建,以便我可以将新的 Docker 映像推送到 AWS 并继续下一步。当构建完成时,我需要一个通知,这样我就可以离开去做别的事情,而不是盯着构建日志等待它完成。Travis 自带了几个通知选项,但我想通过电报获得通知,这是不支持的,所以我决定推出自己的通知。
设置电报机器人
Telegram 提供了一个创建看起来相对容易使用的机器人的 API,所以我开始研究官方文档。第一步是创建一个专用的机器人,这实际上是通过 Telegram 应用程序本身,通过与“机器人父亲”进行交互来完成的。如果你一直跟着,我会假设你已经有一个电报帐户,所以点击 BotFather 链接应该会在你的聊天客户端打开一个新的对话。在那里,您可以键入/start来获得可能的命令列表。
键入/newbot将开始这个过程,首先要求显示名称(当从您的机器人获得消息时,您将看到这个名称),然后是用户名。对于这个例子,我选择“TestDriven TestBot”作为显示名称,选择“testdriven_test_bot”作为用户名。请注意,您的用户名必须以“bot”的某种变体结尾,正如文档和 BotFather 所概述的那样。一旦完成,BotFather 会给你一个到你的新机器人的链接,以及一个 API 令牌(在这篇文章的其余部分我将把它称为<TOKEN>),你会想把它保存在某个地方。

首先,这个机器人根本不做什么。在我点击对话链接之前,我从终端使用 HTTPie 检查了状态(你可以使用 curl,或者浏览器,或者你喜欢的任何东西…我只是更喜欢 HTTPie 的格式化输出):
`# curl https://api.telegram.org/bot<TOKEN>/getUpdates
$ http https://api.telegram.org/bot<TOKEN>/getUpdates
HTTP/1.1 200 OK
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Content-Length,Content-Type,Date,Server,Connection
Connection: keep-alive
Content-Length: 23
Content-Type: application/json
Date: Thu, 28 Feb 2019 17:06:45 GMT
Server: nginx/1.12.2
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
{
"ok": true,
"result": []
}`
如果你跟着做,一定要把
<TOKEN>替换成你自己机器人的令牌,注意是bot<TOKEN>,而不仅仅是<TOKEN>。
上面的状态显示机器人还活着,但是没有任何当前对话。我点击了僵尸父亲消息中的链接(t.me/testdriven_test_bot)并开始了对话,然后再次检查了状态:
`# curl https://api.telegram.org/bot<TOKEN>/getUpdates
$ http https://api.telegram.org/bot<TOKEN>/getUpdates
HTTP/1.1 200 OK
...
{
"ok": true,
"result": [
{
"message": {
"chat": {
"first_name": "Anson",
"id": 488404184,
"last_name": "VanDoren",
},
...
}
]
}`
我刚刚开始的对话现在出现了,我需要对话 id(在本例中为488404184),下面我称之为<CHAT_ID>。现在我有了令牌和聊天 id,我将测试发送一条消息:
`# curl -s -X POST https://api.telegram.org/bot<TOKEN>/sendMessage \
# -d chat_id=<CHAT_ID> \
# -d text="The bot speaks"
$ http POST https://api.telegram.org/bot<TOKEN>/sendMessage \
chat_id=<CHAT_ID> \
text="The bot speaks"
HTTP/1.1 200 OK
...
{
"ok": true,
"result": {
"chat": {
"first_name": "Anson",
"id": 488404184,
"last_name": "VanDoren",
},
"date": 1551374658,
"from": {
"first_name": "TestDriven TestBot",
"id": 746763956,
"is_bot": true,
"username": "testdriven_test_bot"
},
"message_id": 2,
"text": "The bot speaks"
}
}`
看起来 API 很高兴,我也可以在我的 Telegram 客户端中看到发送的消息:

到目前为止,一切看起来都很好,所以现在是时候将它集成到 Travis 中了。
设置 Travis 环境变量
为了让 Travis 在构建完成时给我发送消息,我需要让它知道我的 Telegram API 令牌和聊天 ID。我不想在我的 .travis.yml 文件或我提交给公共回购的任何其他文件中包含任何一个,所以有两个选择,要么在构建脚本中加密它们,要么将它们设置为 travis 环境变量。关于这两个选项的更多信息可以在 Travis 文档中找到,但是因为我可以对所有的构建/阶段/版本使用相同的脚本,所以我选择了环境变量。
为了创建环境变量,我导航到我的 repo,点击“更多选项>设置”,然后向下滚动到“环境变量”部分。我添加了两个变量,TELEGRAM_CHAT_ID和TELEGRAM_TOKEN。这是我在上面使用的两个相同的变量,但是为了避免 Travis 设置中的歧义,我对它们进行了重命名。

修改构建脚本
接下来,我在我的 .travis.yml 文件中添加了一个after_script步骤。我可以很容易地直接在构建脚本文件本身中完成一个简单的通知,但是我想添加一些额外的细节。因此,我选择编写一个单独的 shell 脚本来封装它,然后在我的构建配置文件中引用该脚本:
`after_script: - bash ./telegram_notification.sh`
编写通知脚本
在我的项目根目录中,我创建了一个名为telegram_notification.sh的 bash 脚本:
`#!/bin/sh
# Get the token from Travis environment vars and build the bot URL:
BOT_URL="https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendMessage"
# Set formatting for the message. Can be either "Markdown" or "HTML"
PARSE_MODE="Markdown"
# Use built-in Travis variables to check if all previous steps passed:
if [ $TRAVIS_TEST_RESULT -ne 0 ]; then
build_status="failed"
else
build_status="succeeded"
fi
# Define send message function. parse_mode can be changed to
# HTML, depending on how you want to format your message:
send_msg () {
curl -s -X POST ${BOT_URL} -d chat_id=$TELEGRAM_CHAT_ID \
-d text="$1" -d parse_mode=${PARSE_MODE}
}
# Send message to the bot with some pertinent details about the job
# Note that for Markdown, you need to escape any backtick (inline-code)
# characters, since they're reserved in bash
send_msg "
-------------------------------------
Travis build *${build_status}!*
\`Repository: ${TRAVIS_REPO_SLUG}\`
\`Branch: ${TRAVIS_BRANCH}\`
*Commit Msg:*
${TRAVIS_COMMIT_MESSAGE} [Job Log here](${TRAVIS_JOB_WEB_URL})
--------------------------------------
"`
测试它
将您的更改提交到 .travis.yml 和新的 telegram_notification.sh 文件,推送到 Github,等待 travis 完成构建。

这就够了!您可以修改消息以适应您的需要和口味,Travis 有相当多的内置环境变量,如果您愿意,您可以将它们放入您的通知脚本中。为了方便起见,上面的脚本也可以作为 GitHub gist 使用。
一旦你建立并运行了你的机器人,将它混合到其他项目中也是非常容易的。我在跨几种不同的语言、框架和平台的几个不同的部署脚本中使用同一个 bot。在任何可以发出 HTTP 请求的地方,都可以通过 bot 触发通知!
使用 Docker 部署自托管 GitHub 动作运行器
在本教程中,我们将详细介绍如何使用 Docker 将自托管的GitHub Actionsrunner 部署到 DigitalOcean 。我们还将看到如何在垂直方向(通过 Docker Compose)和水平方向(通过 Docker Swarm)扩展跑步者。
依赖关系:
- 文档编号 v19.03.8
- 坞站-复合 v1.29.2
- 对接机 v0.16.2
GitHub 操作
GitHub Actions 是一个持续集成和交付(CI/CD)解决方案,与 GitHub 完全集成。GitHub Actions 工作流中的作业在名为 runners 的应用程序上运行。你既可以使用 GitHub 托管的运行程序,也可以在自己的基础设施上运行自己的自托管运行程序。
跑步者既可以添加到个人存储库中,也可以添加到组织中。我们将采用后一种方法,以便运行人员可以处理来自同一个 GitHub 组织中多个存储库的作业。
开始之前,你需要创建一个个人访问令牌。在您的开发者设置中,点击“个人访问令牌”。然后,单击“生成新令牌”。提供描述性注释并选择repo、workflow和admin:org范围。
数字海洋设置
首先,注册一个数字海洋账户,如果你还没有的话,然后生成一个访问令牌,这样你就可以访问数字海洋 API。
将令牌添加到您的环境中:
`$ export DIGITAL_OCEAN_ACCESS_TOKEN=[your_digital_ocean_token]`
安装 Docker Machine 如果你的本地机器上还没有的话。
旋转出一个叫做runner-node的小液滴:
`$ docker-machine create \
--driver digitalocean \
--digitalocean-access-token $DIGITAL_OCEAN_ACCESS_TOKEN \
--digitalocean-region "nyc1" \
--digitalocean-image "debian-10-x64" \
--digitalocean-size "s-4vcpu-8gb" \
--engine-install-url "https://releases.rancher.com/install-docker/19.03.9.sh" \
runner-node;`
Docker 部署
SSH 进入 droplet:
`$ docker-machine ssh runner-node`
添加以下 docker 文件,注意注释:
`# base
FROM ubuntu:18.04
# set the github runner version
ARG RUNNER_VERSION="2.283.3"
# update the base packages and add a non-sudo user
RUN apt-get update -y && apt-get upgrade -y && useradd -m docker
# install python and the packages the your code depends on along with jq so we can parse JSON
# add additional packages as necessary
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
curl jq build-essential libssl-dev libffi-dev python3 python3-venv python3-dev python3-pip
# cd into the user directory, download and unzip the github actions runner
RUN cd /home/docker && mkdir actions-runner && cd actions-runner \
&& curl -O -L https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz \
&& tar xzf ./actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz
# install some additional dependencies
RUN chown -R docker ~docker && /home/docker/actions-runner/bin/installdependencies.sh
# copy over the start.sh script
COPY start.sh start.sh
# make the script executable
RUN chmod +x start.sh
# since the config and run script for actions are not allowed to be run by root,
# set the user to "docker" so all subsequent commands are run as the docker user
USER docker
# set the entrypoint to the start.sh script
ENTRYPOINT ["./start.sh"]`
用最新版本的 runner 更新
RUNNER_VERSION变量,可以在这里找到。
同样添加 start.sh 文件:
`#!/bin/bash
ORGANIZATION=$ORGANIZATION
ACCESS_TOKEN=$ACCESS_TOKEN
REG_TOKEN=$(curl -sX POST -H "Authorization: token ${ACCESS_TOKEN}" https://api.github.com/orgs/${ORGANIZATION}/actions/runners/registration-token | jq .token --raw-output)
cd /home/docker/actions-runner
./config.sh --url https://github.com/${ORGANIZATION} --token ${REG_TOKEN}
cleanup() {
echo "Removing runner..."
./config.sh remove --unattended --token ${REG_TOKEN}
}
trap 'cleanup; exit 130' INT
trap 'cleanup; exit 143' TERM
./run.sh & wait $!`
ORGANIZATION和ACCESS_TOKEN (GitHub 个人访问令牌)环境变量用于请求跑步者注册令牌。
有关更多信息,请查看文档中的为组织创建注册令牌一节。
注意到:
`cleanup() {
echo "Removing runner..."
./config.sh remove --unattended --token ${REG_TOKEN}
}
trap 'cleanup; exit 130' INT
trap 'cleanup; exit 143' TERM
./run.sh & wait $!`
本质上,当容器停止时,用于在容器停止时移除流道的清理逻辑将会执行。
关于 shell 信号处理和
wait的更多信息,请查看这个堆栈交换答案。
构建映像并在分离模式下旋转容器:
`$ docker build --tag runner-image .
$ docker run \
--detach \
--env ORGANIZATION=<YOUR-GITHUB-ORGANIZATION> \
--env ACCESS_TOKEN=<YOUR-GITHUB-ACCESS-TOKEN> \
--name runner \
runner-image`
确保分别用您的组织和个人访问令牌替换
<YOUR-GITHUB-ORGANIZATION>和<YOUR-GITHUB-ACCESS-TOKEN>。
快速查看一下容器日志:
您应该会看到类似如下的内容:
`--------------------------------------------------------------------------------
| ____ _ _ _ _ _ _ _ _ |
| / ___(_) |_| | | |_ _| |__ / \ ___| |_(_) ___ _ __ ___ |
| | | _| | __| |_| | | | | '_ \ / _ \ / __| __| |/ _ \| '_ \/ __| |
| | |_| | | |_| _ | |_| | |_) | / ___ \ (__| |_| | (_) | | | \__ \ |
| \____|_|\__|_| |_|\__,_|_.__/ /_/ \_\___|\__|_|\___/|_| |_|___/ |
| |
| Self-hosted runner registration |
| |
--------------------------------------------------------------------------------
# Authentication
√ Connected to GitHub
# Runner Registration
Enter the name of the runner group to add this runner to: [press Enter for Default]
Enter the name of runner: [press Enter for 332d0614b5e9]
This runner will have the following labels: 'self-hosted', 'Linux', 'X64'
Enter any additional labels (ex. label-1,label-2): [press Enter to skip]
√ Runner successfully added
√ Runner connection is good
# Runner settings
Enter name of work folder: [press Enter for _work]
√ Settings Saved.
√ Connected to GitHub
2021-10-23 22:36:01Z: Listening for Jobs`
然后,在您的 GitHub 组织名称下,点击“设置”。在左侧栏中,点击“动作”,然后点击“跑步者”。您应该会看到一个注册的跑步者:

为了进行测试,将run-on:[自托管] 添加到存储库的工作流 YAML 文件中。
例如:
`name: Sample Python on: [push] jobs: build: runs-on: [self-hosted] steps: - uses: actions/[[email protected]](/cdn-cgi/l/email-protection) - name: Install dependencies run: | python3 -m pip install --upgrade pip pip3 install pytest - name: Test with pytest run: | python3 -m pytest`
然后,运行新的构建。回到您的终端,在 Docker 日志中,您应该看到作业的状态:
`2021-10-24 00:46:26Z: Running job: build
2021-10-24 00:46:34Z: Job build completed with result: Succeeded`
如果您尝试运行作业的存储库是公共的,那么您必须更新默认的 runner 组以允许公共存储库。更多信息,请查看公共存储库的自托管跑步者安全性。
完成后取下容器:
再次查看日志。您应该看到:
`Removing runner...
# Runner removal
√ Runner removed successfully
√ Removed .credentials
√ Removed .runner`
该跑步者在您的 GitHub 组织的操作设置中应该不再可用:

移除容器:
使用 Docker 合成进行垂直缩放
想在一个液滴上旋转多个转轮吗?
首先将下面的 docker-compose.yml 文件添加到框中:
`version: '3' services: runner: build: . environment: - ORGANIZATION=<YOUR-GITHUB-ORGANIZATION> - ACCESS_TOKEN=<YOUR-GITHUB-ACCESS-TOKEN>`
确保分别用您的组织和个人访问令牌替换<YOUR-GITHUB-ORGANIZATION>和<YOUR-GITHUB-ACCESS-TOKEN>。
按照官方安装指南在 droplet 上下载安装 Docker Compose,然后构建镜像:
启动两个容器实例:
`$ docker-compose up --scale runner=2 -d`
你应该在 GitHub 上看到两个跑步者:

开始两次构建。打开撰写日志:
您应该会看到类似这样的内容:
`runner_2 | 2021-10-24 00:52:56Z: Running job: build
runner_1 | 2021-10-24 00:52:58Z: Running job: build
runner_2 | 2021-10-24 00:53:04Z: Job build completed with result: Succeeded
runner_1 | 2021-10-24 00:53:11Z: Job build completed with result: Succeeded`
你可以像这样缩小:
`$ docker-compose up --scale runner=1 -d`
退出 SSH 会话并销毁机器/液滴:
`$ docker-machine rm runner-node -y
$ eval $(docker-machine env -u)`
使用 Docker Swarm 进行水平缩放
想要在多个 DigitalOcean 微滴之间进行水平缩放?
配置水滴
将数字海洋访问令牌添加到您的环境中:
`$ export DIGITAL_OCEAN_ACCESS_TOKEN=[your_digital_ocean_token]`
旋转三个新的数字海洋液滴:
`$ for i in 1 2 3; do
docker-machine create \
--driver digitalocean \
--digitalocean-access-token $DIGITAL_OCEAN_ACCESS_TOKEN \
--digitalocean-region "nyc1" \
--digitalocean-image "debian-10-x64" \
--digitalocean-size "s-4vcpu-8gb" \
--engine-install-url "https://releases.rancher.com/install-docker/19.03.9.sh" \
runner-node-$i;
done`
在第一个节点runner-node-1上初始化群模式:
`$ docker-machine ssh runner-node-1 -- docker swarm init --advertise-addr $(docker-machine ip runner-node-1)`
使用上一个命令输出中的 join 令牌将剩余的两个节点作为 workers 添加到群中:
`$ for i in 2 3; do
docker-machine ssh runner-node-$i -- docker swarm join --token YOUR_JOIN_TOKEN HOST:PORT;
done`
例如:
`$ for i in 2 3; do
docker-machine ssh runner-node-$i -- docker swarm join --token SWMTKN-1-4a341wv2n8c2c0cn3f9d0nwxndpohwuyr58vtal63wx90spfoo-09vdgcfarp6oqxnncgfjyrh0i 161.35.12.185:2377;
done`
您应该看到:
`This node joined a swarm as a worker.
This node joined a swarm as a worker.`
构建 Docker 映像
这一次,让我们在本地构建映像,并将其推送到 Docker Hub 映像注册中心。
Dockerfile:
`# base
FROM ubuntu:18.04
# set the github runner version
ARG RUNNER_VERSION="2.283.3"
# update the base packages and add a non-sudo user
RUN apt-get update -y && apt-get upgrade -y && useradd -m docker
# install python and the packages the your code depends on along with jq so we can parse JSON
# add additional packages as necessary
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
curl jq build-essential libssl-dev libffi-dev python3 python3-venv python3-dev python3-pip
# cd into the user directory, download and unzip the github actions runner
RUN cd /home/docker && mkdir actions-runner && cd actions-runner \
&& curl -O -L https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz \
&& tar xzf ./actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz
# install some additional dependencies
RUN chown -R docker ~docker && /home/docker/actions-runner/bin/installdependencies.sh
# copy over the start.sh script
COPY start.sh start.sh
# make the script executable
RUN chmod +x start.sh
# since the config and run script for actions are not allowed to be run by root,
# set the user to "docker" so all subsequent commands are run as the docker user
USER docker
# set the entrypoint to the start.sh script
ENTRYPOINT ["./start.sh"]`
start.sh :
`#!/bin/bash
ORGANIZATION=$ORGANIZATION
ACCESS_TOKEN=$ACCESS_TOKEN
REG_TOKEN=$(curl -sX POST -H "Authorization: token ${ACCESS_TOKEN}" https://api.github.com/orgs/${ORGANIZATION}/actions/runners/registration-token | jq .token --raw-output)
cd /home/docker/actions-runner
./config.sh --url https://github.com/${ORGANIZATION} --token ${REG_TOKEN}
cleanup() {
echo "Removing runner..."
./config.sh remove --unattended --token ${REG_TOKEN}
}
trap 'cleanup; exit 130' INT
trap 'cleanup; exit 143' TERM
./run.sh & wait $!`
建立形象:
`$ docker build --tag <your-docker-hub-username>/actions-image:latest .`
确保用您的 Docker Hub 用户名替换<your-docker-hub-username>。然后,将映像推送到注册表:
`$ docker push <your-docker-hub-username>/actions-image:latest`
部署
要部署堆栈,首先添加一个 Docker 合成文件:
`version: '3' services: runner: image: <your-docker-hub-username>/actions-image:latest deploy: mode: replicated replicas: 1 placement: constraints: - node.role == worker environment: - ORGANIZATION=<YOUR-GITHUB-ORGANIZATION> - ACCESS_TOKEN=<YOUR-GITHUB-ACCESS-TOKEN>`
再次更新<your-docker-hub-username>以及环境变量。
将 Docker 守护进程指向runner-node-1并部署堆栈:
`$ eval $(docker-machine env runner-node-1)
$ docker stack deploy --compose-file=docker-compose.yml actions`
列出堆栈中的服务:
`$ docker stack ps -f "desired-state=running" actions`
您应该会看到类似如下的内容:
`ID NAME IMAGE NODE DESIRED STATE
xhh3r8rfhh46 actions_runner.1 mjhea0/actions-image:latest runner-node-2 Running`
确保跑步者在 GitHub 上:

让我们再添加两个节点:
`$ docker service scale actions_runner=3
actions_runner scaled to 3
overall progress: 3 out of 3 tasks
1/3: running
2/3: running
3/3: running
verify: Service converged`
验证:

开始几项工作,并确保它们成功完成。
缩小到单个流道:
`$ docker service scale actions_runner=1
actions_runner scaled to 1
overall progress: 1 out of 1 tasks
1/1: running
verify: Service converged`
验证:

完成后,将机器/液滴取下:
`$ docker-machine rm runner-node-1 runner-node-2 runner-node-3 -y`
使用 Docker 部署自托管 GitLab CI 运行程序
在本教程中,我们将详细介绍如何使用 Docker 将自托管的 GitLab CI/CD 运行程序部署到数字海洋。
GitLab CI/CD
GitLab CI/CD 是一个持续集成和交付(CI/CD)解决方案,与 GitLab 完全集成。GitLab CI/CD 管道中的作业在称为 runners 的进程上运行。你既可以使用 GitLab 托管的共享运行器,也可以在自己的基础设施上运行自己的自托管运行器。
范围
Runners 可以对 GitLab 实例中的所有项目和组可用,也可以对特定的组或特定的项目(存储库)可用。我们将使用第一种方法,这样我们可以用同一个运行器处理来自多个存储库的作业。
您还可以使用标签来控制运行程序可以运行哪些作业。
关于跑步者范围的更多信息,请看官方文件中的跑步者范围。
码头工人
因为您可能想在工作中运行docker命令——构建和测试 Docker 容器中运行的应用程序——所以您需要选择以下三种方法之一:
我们将使用套接字绑定方法(Docker-out-of-Docker?)将对接插座捆绑安装到具有容积的容器上。运行 GitLab runner 的容器将能够与 Docker 守护进程通信,从而产生兄弟容器。
虽然我对这些方法没有什么强烈的意见,但我还是推荐阅读为您的 CI 或测试环境使用 Docker-in-Docker?三思而后行。作者 jérme Petazzoni,Docker-in-Docker 的创造者。
如果你对 Docker-in-Docker 方法感兴趣,可以看看定制的 Gitlab CI/CD Runner,用 Docker-in-Docker 进行高速缓存。
数字海洋设置
首先,注册一个数字海洋账户,如果你还没有的话,然后生成一个访问令牌,这样你就可以访问数字海洋 API。
将令牌添加到您的环境中:
`$ export DIGITAL_OCEAN_ACCESS_TOKEN=[your_digital_ocean_token]`
安装 Docker Machine 如果你的本地机器上还没有的话。
旋转出一个叫做runner-node的小液滴:
`$ docker-machine create \
--driver digitalocean \
--digitalocean-access-token $DIGITAL_OCEAN_ACCESS_TOKEN \
--digitalocean-region "nyc1" \
--digitalocean-image "debian-10-x64" \
--digitalocean-size "s-4vcpu-8gb" \
--engine-install-url "https://releases.rancher.com/install-docker/19.03.9.sh" \
runner-node;`
Docker 部署
SSH 进入 droplet:
`$ docker-machine ssh runner-node`
创建以下文件和文件夹:
`├── config
│ └── config.toml
└── docker-compose.yml`
将以下内容添加到 docker-compose.yml 文件中:
`version: '3' services: gitlab-runner-container: image: gitlab/gitlab-runner:v14.3.2 container_name: gitlab-runner-container restart: always volumes: - ./config/:/etc/gitlab-runner/ - /var/run/docker.sock:/var/run/docker.sock`
在此,我们:
- 使用了官方 GitLab Runner Docker 图片。
- 增加了 Docker 套接字和“配置”文件夹的容量。
- 将端口 9252 暴露给 Docker 主机。稍后会有更多内容。
按照官方安装指南在 droplet 上下载并安装 Docker Compose,然后旋转容器:
如果您遇到 Docker 编写挂起的问题,请查看这个堆栈溢出问题。
接下来,您需要获得注册令牌和 URL。在您小组的“CI/CD 设置”中,展开“跑步者”部分。一定要禁用共享跑步者。

运行以下命令来注册一个新的跑步者,确保用您的组的注册令牌和 URL 替换<YOUR-GITLAB-REGISTRATION-TOKEN>和<YOUR-GITLAB-URL>:
`$ docker-compose exec gitlab-runner-container \
gitlab-runner register \
--non-interactive \
--url <YOUR-GITLAB-URL> \
--registration-token <YOUR-GITLAB-REGISTRATION-TOKEN> \
--executor docker \
--description "Sample Runner 1" \
--docker-image "docker:stable" \
--docker-volumes /var/run/docker.sock:/var/run/docker.sock`
您应该会看到类似如下的内容:
`Runtime platform
arch=amd64 os=linux pid=18 revision=e0218c92 version=14.3.2
Running in system-mode.
Registering runner... succeeded
runner=hvdSfcc1
Runner registered successfully. Feel free to start it, but if it's running already
the config should be automatically reloaded!`
同样,我们使用了 docker 套接字绑定方法,以便docker命令可以在运行于 runner 上的作业内部运行。
查看 GitLab Runner 命令以了解更多关于
register命令以及注册和管理跑步者的其他命令。
回到 GitLab,您应该会在您小组的“CI/CD 设置”中看到已注册的跑步者:

通过为您的一个存储库运行 CI/CD 管道来测试它。
回到您的终端,查看一下集装箱日志:
`$ docker logs gitlab-runner-container -f`
您应该会看到作业的状态:
`Checking for jobs... received
job=1721313345 repo_url=https://gitlab.com/testdriven/testing-gitlab-ci.git runner=yK2DqWMQ
Job succeeded
duration_s=32.174537956 job=1721313345 project=30721568 runner=yK2DqWMQ`
配置
记下配置文件 config/config.toml :
`$ cat config/config.toml
concurrent = 1
check_interval = 0
[session_server]
session_timeout = 1800
[[runners]]
name = "Sample Runner 1"
url = "https://gitlab.com/"
token = "yK2DqWMQB1CqPsRx6gwn"
executor = "docker"
[runners.custom_build_dir]
[runners.cache]
[runners.cache.s3]
[runners.cache.gcs]
[runners.cache.azure]
[runners.docker]
tls_verify = false
image = "docker:stable"
privileged = false
disable_entrypoint_overwrite = false
oom_kill_disable = false
disable_cache = false
volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
shm_size = 0`
查看高级配置,了解更多可用选项。您可以配置许多东西,例如日志和缓存选项、内存限制和 CPU 数量等等。
因为我们没有利用外部缓存,比如亚马逊 S3 或谷歌云存储,所以删除[runners.cache]部分。然后,重新启动转轮:
`$ docker-compose exec gitlab-runner-container gitlab-runner restart`
尝试同时运行两个作业。由于并发性设置为 1 - concurrent = 1 -在运行器上一次只能运行一个作业。因此,其中一个作业将保持“挂起”状态,直到第一个作业完成运行。如果您只是为一个小团队设置跑步者,那么您也许可以一次只运行一个任务。随着团队的成长,您会想要尝试并发配置选项:
concurrent-限制在所有运行程序中全局同时运行的作业数量。limit-适用于单个跑步者,限制可同时处理的工作数量。默认为0,表示不应用限制。request_concurrency-适用于单个跑步者,限制新工作的并发请求数量。默认为1。
在我们更新并发选项之前,添加一个新的 runner:
`$ docker-compose exec gitlab-runner-container \
gitlab-runner register \
--non-interactive \
--url <YOUR-GITLAB-URL> \
--registration-token <YOUR-GITLAB-REGISTRATION-TOKEN> \
--executor docker \
--description "Sample Runner 2" \
--docker-image "docker:stable" \
--docker-volumes /var/run/docker.sock:/var/run/docker.sock`
然后,像这样更新 config/config.toml :
`concurrent = 4 # NEW check_interval = 0 [session_server] session_timeout = 1800 [[runners]] name = "Sample Runner 1" url = "https://gitlab.com/" token = "yK2DqWMQB1CqPsRx6gwn" executor = "docker" limit = 2 # NEW request_concurrency = 2 # NEW [runners.custom_build_dir] [runners.docker] tls_verify = false image = "docker:stable" privileged = false disable_entrypoint_overwrite = false oom_kill_disable = false disable_cache = false volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"] shm_size = 0 [[runners]] name = "Sample Runner 2" url = "https://gitlab.com/" token = "qi-b3gFzVaX3jRRskJbz" limit = 2 # NEW request_concurrency = 2 # NEW executor = "docker" [runners.custom_build_dir] [runners.cache] [runners.cache.s3] [runners.cache.gcs] [runners.cache.azure] [runners.docker] tls_verify = false image = "docker:stable" privileged = false disable_entrypoint_overwrite = false oom_kill_disable = false disable_cache = false volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"] shm_size = 0`
现在,我们可以跨两个运行程序同时运行四个作业,每个运行程序有两个子流程:

重新启动:
`$ docker-compose exec gitlab-runner-container gitlab-runner restart`

通过运行四个作业来测试它。
请记住,如果您正在构建和测试 Docker 映像,您最终会耗尽磁盘空间。因此,定期删除 Docker 主机上所有未使用的图像和容器是一个好主意。
Example crontab:
`@weekly /usr/bin/docker system prune -f`
–
就是这样!
不要忘记注销跑步者:
`$ docker-compose exec gitlab-runner-container gitlab-runner unregister --all-runners`
然后,回到您的本地机器,关闭机器/droplet:
`$ docker-machine rm runner-node`
基于 Python 的应用程序的 Heroku 替代方案
Heroku 改变了开发人员构建和部署软件的方式,使得构建、部署和扩展应用程序变得更加容易和快速。他们为如何管理云服务制定了各种标准和方法——即十二因素应用——这些标准和方法在今天仍然与基于微服务和云原生应用高度相关。不幸的是,从 2022 年 11 月 28 日开始,Heroku 将停止其免费层。这意味着您将不再能够利用免费的 dynos、Postgres 数据库和 Redis 实例。
关于 Heroku 停止其免费产品层的更多信息,请查看 Heroku 的下一章和对 Heroku 免费资源的反对。
在本文中,您将了解最佳的 Heroku 替代方案(及其优缺点)。
什么是 Heroku?
Heroku 成立于 2007 年,是一个为网络应用提供托管服务的云计算平台。它们提供了抽象的环境,您无需管理底层基础设施,从而轻松管理、部署和扩展 web 应用程序。只需几次点击,您就可以启动并运行您的应用程序,准备接收流量。
在 Heroku 出现之前,运行一个 web 应用程序的过程是相当具有挑战性的,主要是留给经验丰富的系统操作专业人员而不是开发人员。Heroku 提供了一个自以为是的层,抽象出了 web 服务器所需的大部分配置。大多数 web 应用程序可以(并且仍然可以)利用这样的环境,因此较小的公司和团队可以专注于应用程序开发,而不是配置 web 服务器、安装 Linux 包、设置负载平衡器以及在传统服务器上进行基础设施管理的所有其他事情。
Heroku 的利与弊
尽管 Heroku 很受欢迎,但多年来它还是受到了很多批评。
如果你已经熟悉 Heroku,可以跳过这一部分。
赞成的意见
易用性
Heroku 可以说是最用户友好的 PaaS 平台。您只需定义运行 web 应用程序所需的命令,Heroku 就会为您完成剩下的工作,而无需花费数天时间来设置和配置 web 服务器和底层基础设施。您可以在几分钟内启动并运行您的应用程序!
此外,Heroku 利用 git 来版本化和部署应用程序,这使得部署和回滚变得容易。
最后,与大多数 PaaS 平台不同,Heroku 提供了优秀的错误日志,使得调试相对容易。
流行
在成立的头五年,Heroku 几乎没有竞争对手。他们的用户/开发者体验远远领先于其他人,公司需要一段时间来适应。这一点,再加上它们巨大的免费层,意味着大多数面向开发人员的教程都使用 Heroku 作为他们的部署平台。即使到今天,绝大多数 web 开发教程、书籍和课程仍然利用 Heroku 进行部署。
Heroku 还对一些最流行的语言和运行时(通过build pack)提供一流的支持,如 Python、Ruby、Node.js、PHP、Go、Java、Scala 和 Clojure。虽然这些都是官方支持的语言,但您仍然可以将自己的语言或自定义运行时带到 Heroku 平台上。
集成和附加组件
经常被忽视的是,Heroku 提供了数百个附加工具和服务——从数据存储和缓存到监控和分析,再到数据和视频处理。只需点击一个按钮,您就可以通过提供第三方云服务来扩展您的应用,而无需手动安装或配置。
缩放比例
Heroku 允许开发者轻松地纵向和横向扩展他们的应用。可以通过 Heroku 的仪表盘或 CLI 实现缩放。此外,如果你运行更高性能的 dyno,你可以利用免费的自动缩放功能,这将根据当前的流量增加 web dynos 的数量。
骗局
费用
与市场上的其他 PaaS 相比,Heroku 相当昂贵。虽然他们的起始计划是每个 dyno 每月 7 美元,但随着你的应用程序的扩展,你很快就必须升级到更好的 dyno,这需要花费相当多的钱。由于更高性能的 dynos 的价格,Heroku 可能不适合大型高流量应用程序。
Heroku 与 AWS EC2 相比大约贵五倍。
但是请记住,Heroku 是一个 PaaS,它为您做了很多繁重的工作,而 EC2 只是一个 Linux 实例,您必须自己管理它。
缺乏控制和灵活性
Heroku 没有提供足够的控制,也缺乏透明度。通过使用他们的服务,你将高度依赖他们的技术和设计决策。它们的一些限制阻碍了可伸缩性——例如,应用程序只能监听单个端口,函数的最大源代码大小为 500 MB,并且没有办法微调数据库。Heroku 也高度依赖 AWS,这意味着如果一个 AWS 区域宕机,您的服务(托管在该区域)也会宕机。
类似地,Heroku 实际上是为普通的 RESTful APIs 设计的。如果你的应用包括繁重的计算,或者你需要调整基础设施来满足你的特定需求,Heroku 可能不是一个很好的选择。
缺少地区
Heroku 提供两种类型的运行时:
公共运行时只支持两个地区,美国和欧盟,而私有空间运行时支持 6 个地区。
这意味着,如果你不是企业用户,你只能在美国(弗吉尼亚州)或欧盟地区(爱尔兰都柏林)托管你的应用程序。
`$ heroku regions
ID Location Runtime
───────── ─────────────────────── ──────────────
eu Europe Common Runtime
us United States Common Runtime
dublin Dublin, Ireland Private Spaces
frankfurt Frankfurt, Germany Private Spaces
oregon Oregon, United States Private Spaces
sydney Sydney, Australia Private Spaces
tokyo Tokyo, Japan Private Spaces
virginia Virginia, United States Private Spaces`
缺乏新功能
当今世界,发展趋势变化比以往任何时候都快。这迫使托管服务追随潮流,吸引寻找尖端技术的团队。Heroku 的许多竞争对手,我们很快就会谈到,正在推进和增加新功能,如无服务器、边缘计算等。另一方面,Heroku 认为稳定性比特性开发更重要。这并不意味着他们没有增加新功能;他们只是添加新功能的速度比一些竞争对手慢得多。
如果你想知道 Heroku 接下来会发生什么,看看他们的路线图吧。
锁住
一旦你在 Heroku 上运行生产代码,就很难迁移到不同的主机提供商。
请记住,如果您不再使用 PaaS,您将不得不处理 Heroku 自己处理的所有事情,所以请准备好雇用一两个 SysAdmin 或 DevOps。
Heroku 的核心特征
在这一节中,我们将看看 Heroku 的核心特性,这样你就可以了解在寻找替代方案时应该寻找什么。
同样,如果你已经熟悉 Heroku 的特性,可以跳过这一部分。
| 特征 | 描述 |
|---|---|
| Heroku 运行时 | Heroku 运行时负责提供和编排 dyno,管理和监控 dyno 的生命周期,提供适当的网络配置、HTTP 路由、日志聚合等等。 |
| CI/CD 系统 | 易于使用的 CI/CD,负责构建、测试、部署、增量应用程序更新等。 |
| 基于 git 的部署 | 使用 git 管理应用部署。 |
| 数据持久性 | 完全托管的数据服务,如 Postgres 、 Redis 和 Apache Kafka 。 |
| 缩放功能 | 易于使用的工具,使开发人员能够按需进行水平和垂直扩展。 |
| 日志记录和应用指标 | 日志记录、监控和应用程序指标。 |
| 协作功能 | 与他人轻松协作。协作者可以对您的应用程序进行部署、缩放和访问数据等操作。 |
| 附加组件 | 数百种附加工具和服务–从数据存储和缓存到监控和分析,再到数据和视频处理,无所不包。 |
在寻找替代方案时,您应该优先考虑这些功能。你不可能找到 Heroku 的 1:1 替代品,所以一定要确定哪些功能是“必须拥有”而不是“最好拥有”。
例如:
必备物品
- 实心用户界面
- 构建包
- 基于 git 的部署
- 经过战斗考验的
- 简单缩放
拥有美好事物
- 应用和基础设施监控
- 使用 AWS
- 自由层
- Add-ons
- CI/CD 系统
Heroku 替代方案
最后,在这一节中,我们将看看最好的 Heroku 替代方案以及它们的优缺点。
数字海洋应用平台
App Platform 是 DigitalOcean 用于将应用部署到云的完全托管解决方案。它集成了 CI/CD,可以很好地与 GitHub 和 GitLab 兼容。它原生支持流行的语言和框架,如 Python、Node.js、Django、go 和 PHP。或者,它允许你通过 Docker 部署应用程序。
其他重要特性:
- 水平和垂直缩放
- 内置警报、监控和洞察力
- 零停机部署和回滚
该平台的用户界面/UX 简单明了,给人一种类似 Heroku 的感觉。
DigitalOcean 应用程序平台起价为 5 美元/月,1 个 CPU 和 512 MB 内存。要了解更多关于他们定价的信息,请查看官方定价页面。
赞成的意见
- 使用方便
- 最便宜的 PaaS 之一
- 免费计划,允许您托管多达 3 个静态网站
- 体面的区域支持(8 个区域)
- 托管应用上的 SSL 保护
- DDoS 缓解
骗局
- 相对较新的 PaaS(成立于 2020 年)
- 构建通常需要 15 分钟
- 不支持重复作业(如 cron)
- 缺少文件
想学习如何将 Django 应用程序部署到 DigitalOcean 的应用程序平台?在 DigitalOcean 的 App 平台上查看运行 Django。
提供;给予
2019 年推出的 Render 是 Heroku 的绝佳替代品。它允许你完全免费地托管静态站点、web 服务、PostgreSQL 数据库和 Redis 实例。它极其简单的用户界面/UX 和强大的 git 集成让你可以在几分钟内运行一个应用程序。它具有对 Python、Node.js、Ruby、Elixir、Go 和 Rust 的原生支持。如果这些都不适合您,Render 还可以通过 docker 文件进行部署。
Render 的免费自动缩放功能将确保您的应用程序总是以合适的成本获得必要的资源。此外,Render 上托管的所有内容也可以获得免费的 TLS 证书。
参考他们的官方文件以获得更多关于他们免费计划的信息。
赞成的意见
- 非常适合初学者
- 轻松设置和部署应用
- 自由层
- 与 Heroku 相比,预算适中(便宜约 50%)
- 基于实时 CPU 和内存使用情况的自动扩展
- 出色的客户支持
骗局
- 相对较新的 PaaS(成立于 2019 年)
- 有限的地区支持(仅限俄勒冈州、法兰克福、俄亥俄州和新加坡)
- 免费层应用程序需要很长时间才能启动和运行
- 没有构建包(看看这个问题)
- 缺乏附加产品生态系统
想了解如何部署应用程序进行渲染?查看我们的教程:
Fly.io
Fly.io 是一个流行的、灵活的 PaaS。他们不是转售 AWS 或 GCP 服务,而是将你的应用程序托管在运行于世界各地的物理专用服务器之上。正因为如此,他们能够提供比其他 PaaS 更便宜的主机服务,比如 Heroku。他们的主要关注点是尽可能靠近他们的客户部署应用程序(你可以在 22 个地区中选择)。Fly.io 支持三种构建器:Dockerfile、 buildpacks ,或者预构建的 Docker 映像。
他们还提供缩放和自动缩放功能。
与其他 PaaS 相比,Fly.io 采用不同的方法来管理您的资源。它没有花哨的管理仪表板;相反,所有的工作都是通过他们名为 flyctl 的 CLI 来完成的。
他们的免费计划包括:
- 多达 3 个共享 cpu-1x 256 MB 虚拟机
- 3GB 永久卷存储(总计)
- 160GB 出站数据传输
这应该足够运行一些小应用程序来测试他们的平台了。
赞成的意见
- 小型项目的免费计划
- 巨大的区域支持(在编写本报告时有 22 个区域)
- 出色的文档和完整的 API 文档
- 轻松实现水平和垂直缩放
骗局
- 只能通过 CLI 管理(可能不适合初学者)
- 没有现成的 GitHub 或 GitLab 集成
- 每个地区的定价不同
想学习如何在 Fly.io 上部署 Django 应用程序吗?查看部署 Django 应用程序来飞行。
谷歌应用引擎
Google App Engine (GAE)是一个完全托管的无服务器平台,用于大规模开发和托管网络应用。它具有强大的内置自动扩展功能,可以根据需求自动分配更多/更少的资源。GAE 原生支持用 Python、Node.js、Java、Ruby、C#、Go 和 PHP 编写的应用程序。或者,它通过定制运行时或 Dockerfiles 提供对其他语言的支持。
它具有强大的应用程序诊断功能,您可以结合云监控和日志来监控您的应用程序的健康和性能。
谷歌为新客户提供 300 美元的免费积分,可以为小应用服务几年。
赞成的意见
- 300 美元免费信贷
- 稳定且经过测试,成立于 2008 年
- 强大的应用程序诊断功能(可与其他 GCP/谷歌服务结合使用)
- 它可以扩展到零,这意味着如果没有人使用你的服务,你不用支付任何费用
- 强大的客户支持
骗局
- 相当昂贵
- 如果你不熟悉 GCP,学习曲线会很陡
- 由于谷歌的专有软件,供应商被锁定
- 他们的定价可能更直截了当
想学习如何在 Google App Engine 上部署 Django 应用程序吗?查看将 Django 应用部署到 Google 应用引擎。
Platform.sh
Platform.sh 是一个专门为持续部署而构建的平台即服务。它允许您在云上托管 web 应用程序,同时使您的开发和测试工作流更加高效。它与 GitHub 直接集成,允许开发人员立即从 GitHub 库进行部署。它支持现代开发语言,如 Python、Java、PHP 和 Go,以及许多不同的框架。
Platform.sh 不提供免费计划。他们的开发者计划(不适合生产)起价 10 美元/月。他们的生产就绪计划起价为每月 50 美元。
赞成的意见
- 出色的 CI/CD 以及与 GitHub 的集成
- 您的 GitHub 分支(开发/阶段/生产)反映在 Platform.sh 上
- 通过自动缩放轻松扩展
- 良好的文档
- 出色的客户支持
骗局
- 没有空闲层
- 随着网站的发展,费用会越来越高
- 可能不适合小型企业
AWS 弹性豆茎
AWS Elastic Beanstalk (EB)是一个易于使用的服务,用于部署和扩展 web 应用程序。它连接多个 AWS 服务,例如计算实例( EC2 )、数据库( RDS )、负载平衡器(应用负载平衡器)和文件存储系统( S3 ),等等。EB 可以让你快速部署用 Python、Go、Java、.Net、Node.js、PHP 和 Ruby。它还支持 Docker。
Elastic Beanstalk 通过抽象出底层架构,使应用部署变得更加容易,同时仍然允许实例和数据库的低级配置。它与 git 集成得很好,并允许您进行增量部署。它还支持负载平衡和自动伸缩。
弹性豆茎的伟大之处在于它不需要额外的费用。您只需为应用程序消耗的资源(EC2 实例、RDS 等)付费。).
赞成的意见
- 预算友好
- 如果你已经熟悉 AWS,那就太好了
- 高度可定制,提供高级别的控制
- 自动扩展和多个可用性区域,最大限度地提高应用的可靠性
- 出色的支持
骗局
- 不适合小型项目
- 与其他 PaaS 提供商相比,设置和运营相对困难
- 没有部署失败通知
- 复杂的文档
想了解如何将应用程序部署到 Elastic Beanstalk 吗?查看我们的教程:
微软 Azure 应用服务
Azure App Service 允许您快速轻松地为任何平台或设备创建企业级 web 和移动应用,并将其部署在可扩展且可靠的云基础设施上。它本身支持 Python。网,。NET Core,Node.js,Java,PHP,容器。他们有内置的 CI/CD 和零停机部署。
其他重要特性:
- 用于跟踪和故障排除的日志收集和失败请求跟踪
- 使用 Azure 活动目录进行身份验证
- 监控和警报
如果你是新客户,你可以获得 200 美元的免费积分来测试 Azure。
赞成的意见
- 与 Visual Studio 很好地集成
- Azure Autoscale 可以帮助您优化成本
- 内置 SSL/TLS 证书
- 通过 Azure Monitor 轻松调试和分析
- 稳定,99.95%的正常运行时间
骗局
- 昂贵的
- 不是最直观的 PaaS
- 如果你不熟悉 Azure,学习曲线会很陡
- 复杂的文档
想学习如何在 Azure App Service 上部署 Django 应用?查看将 Django 应用部署到 Azure 应用服务。
数字海洋水滴上的 Dokku
Dokku 声称是你见过的最小的 PaaS 实现。它允许您构建和管理应用程序从构建到扩展的生命周期。它基本上是一个迷你的 Heroku,你可以在你的 Linux 机器上自己运行。Dokku 由 Docker 提供支持,并与 git 很好地集成。
Dokku 提供了一个名为 Dokku PRO 的高级计划,该计划具有用户友好的界面和其他功能。你可以在他们的官网了解更多。
Dokku 的最低系统要求是 1 GB 内存。这意味着你可以以每月 6 美元的价格在 DigitalOcean Droplet 上运行它。
赞成的意见
- 完全免费和开源
- 易于部署的应用
- 丰富的命令行界面
- 各种插件
骗局
- 必须是自托管的
- 需要初始配置
- 文件可以改进
- 缩放并不容易
想学习如何在 Dokku 上部署 Django 应用程序吗?查看在数字海洋水滴上将 Django 应用程序部署到 Dokku。
皮顿 Anywhere
PythonAnywhere 是基于 Python 编程语言的在线集成开发环境(IDE)和 web 托管服务(PaaS)。它为 Django 、 web2py 、烧瓶和瓶子提供了开箱即用的部署选项。与列表中的其他 PaaS 相比,PythonAnywhere 的行为更像一个传统的 web 服务器。您可以访问它的文件系统,并可以通过 SSH 进入控制台来查看日志等等。
它提供了一个免费计划,这对初学者或者只是想测试不同 Python 框架的人来说是很棒的。免费计划允许您在your_username.pythonanywhere.com托管一个 web 应用。您还可以使用免费计划来启动 MySQL 实例。
其他相对便宜的付费计划可以在他们的定价页面上看到。
赞成的意见
- 一个小项目的免费托管
- 好用,基本没有学习曲线
- 为 Django、web2py、烧瓶和瓶子预先配置
- 一键免费 SSL
- 强大的客户支持
骗局
- 不支持 CI/CD
- 仅支持 Python 应用程序
- 没有 ASGI 支持
- 无自动缩放
发动机场
Engine Yard 是一个 PaaS 解决方案,允许开发人员在云中规划、构建、部署和管理应用。Engine Yard 还为部署、管理 AWS、支持数据库和微服务容器开发提供服务。它主要关注 Ruby on Rails,但也支持其他语言,如 Python、PHP 和 Node.js。
Engine Yard 通过自动对托管环境进行堆栈更新和安全修补,简化了云上的应用管理。还可以通过应用指标来扩展应用的资源。
赞成的意见
- 可以部署到任何 AWS 区域
- 快速简单的部署
- 自动化数据库管理
- 旨在扩展
- 良好的客户支持
骗局
- 昂贵的
- 免费试用仅 14 天
- Python 不是主要焦点,因为他们关注的是 Ruby on Rails 应用程序
维克塞尔
Vercel 是一个静态站点和无服务器功能的云平台。它主要用于前端项目,但也支持 Python、Node.js、Ruby、Go 和 Docker。Vercel 使开发人员能够托管即时部署、自动扩展、几乎不需要监管的网站和 web 服务——所有这些都不需要配置。它也有一个漂亮而直观的用户界面。
Vercel 提供免费计划,包括:
- 100 GB 带宽
- 内置 CI/CD
- 自动 HTTPS/SSL
- 每次 git 推送的预览
赞成的意见
骗局
- 不支持许多框架
- Python、Go、Ruby 只能作为无服务器函数使用
- 不提供太多的控制
Netlify
Netlify 是一个面向网络开发者和企业的基于云的开发平台。它允许开发者托管静态站点和无服务器功能。它支持 Python、Node.js、Go、PHP、Ruby、Rust 和 Swift。它无疑是前端项目使用最多的托管平台之一。
Netlify 有一个直观的用户界面,非常容易使用,因为它不需要任何配置。
其免费计划包括:
- 100 GB 带宽
- 每月 300 分钟构建时间
- 现场预览
- 即时回滚到任何版本
- 静态资产和动态无服务器功能的部署
赞成的意见
骗局
- 不支持许多框架
- 它的大多数本地支持的语言只能用作无服务器功能
- 有限控制
铁路. app
Railway.app 是一个鲜为人知的基础设施平台,允许您提供基础设施,在本地使用该基础设施进行开发,然后将其部署到云中。无论项目规模大小,它都适用于每种语言。
其特点包括:
- 自动缩放
- 使用指标
- 自动构建
- 协作功能
赞成的意见
- 适合开发和原型制作
- 与 GitHub 的良好集成
- 模板适用于几乎所有的框架
骗局
- 相对较新的平台
- 不如本文中的其他 PaaS 解决方案流行
- 由于公司名称的原因,很难找到任何与 Railway.app 相关的内容
红帽 OpenShift
OpenShift 是红帽的云计算 PaaS 产品。这是一个构建在云中 Kubernetes 之上的应用程序平台,应用程序开发人员和团队可以在其中构建、测试、部署和运行他们的应用程序。
OpenShift 拥有无缝的 DevOps 工作流,可以水平和垂直扩展,并且可以自动扩展。
赞成的意见
- 稳定,2011 年发布
- 与 GitHub 和 Docker 的强大集成
- 直观的 UI
骗局
- 可能会很贵
- 可以改进监测和故障排除
- 缓慢的客户支持
应用程序
Appliku 是一个 PaaS 平台,它使用您的云服务器来部署您的应用。您可以通过 Appliku 的仪表板链接您的 DigitalOcean 或 AWS 帐户并配置服务器。虽然他们的主要焦点是基于 Python 的应用程序,但是您可以通过利用 Docker 来部署用任何语言构建的应用程序。Appliku 的定价基于托管服务器的数量,因此您可以根据需要部署任意多的应用。他们确实提供免费层。
赞成的意见
- 专为 Python/Django 构建
- 经济高效地运行多个应用
- 使用任何云提供商
- CI/CD、GitHub 和 GitLab 集成
- 让我们加密集成
- 轻松访问服务器日志
骗局
- 更新的平台(2019 年)
- 不如其他一些平台受欢迎
结论
Heroku 是一个成熟、久经考验且稳定的平台。它为您做了很多繁重的工作,并且将为您节省大量的时间和金钱,尤其是对于小型团队。Heroku 让您可以专注于您的产品,而不是摆弄您的服务器的配置选项和雇用 DevOps 工程师或系统管理员。
它可能不是最便宜的选择,但它仍然是市场上最好的 PaaS 之一。因此,如果你已经在使用 Heroku,你应该有一个很强的理由放弃它。
虽然市场上有许多替代方案,但没有一个能与 Heroku 的开发人员体验相匹配。目前,Heroku 最有希望的替代品是数字海洋应用平台和 T2 渲染。这两个平台的问题是它们相对较新,还没有经过实战考验。如果你只是想找一个地方来免费托管你的应用程序,那就用 Render 吧。
Heroku 教程
描述
Heroku 是一个平台即服务(PaaS ),为 web 应用程序提供托管服务。它提供了抽象的环境,您不必管理底层基础设施,从而使管理、部署和扩展 web 应用程序变得容易。Heroku 信奉“包含电池”的哲学。这是一个固执己见的环境,但也是一个您不必管理的环境——因此您可以专注于应用程序开发,而不是支持它的环境。
TestDriven.io 上的教程和文章教授高级 Heroku 部署策略和工作流,通常使用 Docker 进行持续集成和部署。
- 由 发布
尼克·托马齐奇 - 最后更新于2022 年 11 月 8 日
了解什么是最好的 Heroku 替代方案(及其利弊)。
用 FastAPI 和 MongoDB 开发一个异步 API。
如何通过 Heroku 容器运行时使用 Docker 将 Django 应用程序部署到 Heroku。
开发一个生产就绪的 RESTful API,用 FastAPI 提供一个机器学习模型。
使用 Gitlab CI 将 Flask 和 Vue 支持的全栈 web 应用打包并部署到 Heroku。
简化在 Heroku 上部署、维护和扩展生产级 Django 应用的流程。
这篇文章整理了网上一些从 Heroku 迁移到 AWS 的最佳教程。
网络套接字上的超文本标记语言
本文着眼于实现单页面应用程序(SPA)的新方法——基于 WebSockets 的 HTML。
本文由原文西班牙语版本翻译改编而成。
这是什么?
实现单页面应用程序(SPA)的传统方法是在后端和前端之间划分职责:
- 后端通过 JSON RESTful API 提供信息
- 前端通过 API 调用从后端异步获取数据
不幸的是,这种模式成本很高,需要两个专门的开发人员配置文件,并且由于您必须开发和维护两个不同的应用程序(通常使用两种不同的语言),因此会减慢发布周期。这也使得快速原型制作和 SEO 变得困难。自 21 世纪初以来,这种模式一直占据主导地位。为了交付具有桌面应用外观和感觉的 web 应用,这是我们必须付出的代价。然而,这种情况正在开始改变,因为许多团队认识到收益不会超过成本,并且正在尝试交付 SPA 的新模式。一种开始流行的模式是通过 WebSocket 提供 HTML 而不是 JSON。
它是如何工作的?
Chris MC cord,Phoenix(Elixir生态系统中最受欢迎的框架)的创建者,在 ElixirConf 2019 上展示了一项名为 LiveView 的技术,该技术通过 WebSockets 提供 HTML 服务。不久之后,他演示了如何在不使用 React、Angular 或 Vue 等客户端 JavaScript 框架的情况下,用 LiveView 在 15 分钟内构建一个实时 Twitter 克隆。他展示了从后端框架交付一个流畅的实时 UI 是可能的。这启发了其他开发人员使用其他语言和 web 框架创建自己的实现。
McCord 的解决方案是通过 WebSockets 将 HTML 而不是 JSON 发送到前端。这种方法不仅简化了开发,而且性能也得到了很好的提升,因为呈现逻辑由后端处理,前端不需要发出任何新的 HTTP 请求来获取新数据。
传统方法
同样,使用这种方法,浏览器首先从前端发出一个 HTTP 请求,获得一个带有原始的、预处理过的信息的 JSON 响应。然后前端负责处理信息并创建相应的 HTML。

基于 WebSockets 方法的 HTML
使用这种方法,浏览器或服务器可以开始工作,因为 WebSockets 提供双向通信。

例子
让我们看一个显示博客文章的快速例子。
-
连接:我们从一个连接开始。WebSockets 支持客户端和服务器之间的双向通信,您只需建立一次。

-
组件请求:客户端请求与
/article/2路线相关联的特定文章的内容。
-
后端逻辑:服务器为模板生成相关的 HTML、CSS、JavaScript,使用模板系统(比如 Jinja),通过 WebSocket 通道返回模板片段。

-
更新 DOM :最后前端用模板片段更新 DOM,显示博客文章。
Django 演示
我用 Django 创建了一个 HTML over WebSockets 方法的原型,它使用 Django 通道来支持 WebSocket。这个应用程序本身相当简单。它只是一个有文章和评论的博客。也就是说,这是一个完整的 SPA,所以页面更改不需要页面刷新。

你可以在这里看到 WebSockets 的威力:

你可以在 GitHub 上找到代码。
优点和缺点
好处:
- HTML 呈现/处理只发生在后端
- 实时
- WebSockets 协议比 HTTP 快
- 使用慢速连接工作
- 几乎不用任何 JavaScript 就能创建一个 SPA
- 简单的搜索引擎优化
缺点:
- 服务器需要更多的资源,因为您必须为每个客户端打开一个 WebSocket
- 新生的生态系统——很少的框架、教程和文档
资源
您可以从以下资源开始:
- Web 软件的未来是 HTML-over-WebSockets
- 凤凰城现场直播
- Hotwire -通过网络发送 HTML 来构建现代网络应用
- 反应器-Django 的实时视图库
- sock puppet——使用您已经了解并喜爱的 Django 工具构建反应式应用程序
- Action Cable——将 WebSockets 与你的 Ruby 和 Rails 应用程序的其余部分无缝集成
- 用 Ruby 和 Rails 构建快速、可靠的实时应用
结论
虽然这种方法仍然很新,但值得一看。采用在增长,每个月都有越来越多的工具和例子出现。就我个人而言,我对它如此鲜为人知感到惊讶。我想这与 JavaScript 生态系统的强大有很大关系。