Skip to content

Commit d8330b3

Browse files
committed
Switch tutorial to Koyeb.
Fix #1299.
1 parent 2dd33c8 commit d8330b3

File tree

5 files changed

+107
-92
lines changed

5 files changed

+107
-92
lines changed

docs/howto/koyeb.rst

+7-7
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,13 @@ virtualenv as follows:
119119
$ pip install websockets
120120
121121
Look for the URL of your app in the Koyeb control panel. It looks like
122-
``https://<koyeb-app>-<github-user>-<koyeb-app-id>.koyeb.app/``. Connect the
122+
``https://<app>-<user>-<id>.koyeb.app/``. Connect the
123123
interactive client — you must replace ``https`` with ``wss`` in the URL:
124124

125125
.. code-block:: console
126126
127-
$ python -m websockets wss://<koyeb-app>-<github-user>-<koyeb-app-id>.koyeb.app/
128-
Connected to wss://<koyeb-app>-<github-user>-<koyeb-app-id>.koyeb.app/.
127+
$ python -m websockets wss://<app>-<user>-<id>.koyeb.app/
128+
Connected to wss://<app>-<user>-<id>.koyeb.app/.
129129
>
130130
131131
Great! Your app is running!
@@ -146,8 +146,8 @@ Connect an interactive client again:
146146

147147
.. code-block:: console
148148
149-
$ python -m websockets wss://<koyeb-app>-<github-user>-<koyeb-app-id>.koyeb.app/
150-
Connected to wss://<koyeb-app>-<github-user>-<koyeb-app-id>.koyeb.app/.
149+
$ python -m websockets wss://<app>-<user>-<id>.koyeb.app/
150+
Connected to wss://<app>-<user>-<id>.koyeb.app/.
151151
>
152152
153153
In the Koyeb control panel, go to the **Settings** tab, click **Pause**, and
@@ -157,8 +157,8 @@ Eventually, the connection gets closed with code 1001 (going away).
157157

158158
.. code-block:: console
159159
160-
$ python -m websockets wss://<koyeb-app>-<github-user>-<koyeb-app-id>.koyeb.app/
161-
Connected to wss://<koyeb-app>-<github-user>-<koyeb-app-id>.koyeb.app/.
160+
$ python -m websockets wss://<app>-<user>-<id>.koyeb.app/
161+
Connected to wss://<app>-<user>-<id>.koyeb.app/.
162162
Connection closed: 1001 (going away).
163163
164164
If graceful shutdown wasn't working, the server wouldn't perform a closing

docs/intro/tutorial3.rst

+90-82
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,20 @@ and a WebSocket server on ``ws://localhost:8001/`` with:
2828
Now you want to deploy these servers on the Internet. There's a vast range of
2929
hosting providers to choose from. For the sake of simplicity, we'll rely on:
3030

31-
* GitHub Pages for the HTTP server;
32-
* Heroku for the WebSocket server.
31+
* `GitHub Pages`_ for the HTTP server;
32+
* Koyeb_ for the WebSocket server.
33+
34+
.. _GitHub Pages: https://pages.github.com/
35+
.. _Koyeb: https://www.koyeb.com/
36+
37+
Koyeb is a modern Platform as a Service provider whose free tier allows you to
38+
run a web application, including a WebSocket server.
3339

3440
Commit project to git
3541
---------------------
3642

3743
Perhaps you committed your work to git while you were progressing through the
38-
tutorial. If you didn't, now is a good time, because GitHub and Heroku offer
44+
tutorial. If you didn't, now is a good time, because GitHub and Koyeb offer
3945
git-based deployment workflows.
4046

4147
Initialize a git repository:
@@ -45,15 +51,15 @@ Initialize a git repository:
4551
$ git init -b main
4652
Initialized empty Git repository in websockets-tutorial/.git/
4753
$ git commit --allow-empty -m "Initial commit."
48-
[main (root-commit) ...] Initial commit.
54+
[main (root-commit) 8195c1d] Initial commit.
4955
5056
Add all files and commit:
5157

5258
.. code-block:: console
5359
5460
$ git add .
5561
$ git commit -m "Initial implementation of Connect Four game."
56-
[main ...] Initial implementation of Connect Four game.
62+
[main 7f0b2c4] Initial implementation of Connect Four game.
5763
6 files changed, 500 insertions(+)
5864
create mode 100644 app.py
5965
create mode 100644 connect4.css
@@ -62,32 +68,63 @@ Add all files and commit:
6268
create mode 100644 index.html
6369
create mode 100644 main.js
6470
65-
Prepare the WebSocket server
66-
----------------------------
71+
Sign up or log in to GitHub.
6772

68-
Before you deploy the server, you must adapt it to meet requirements of
69-
Heroku's runtime. This involves two small changes:
73+
Create a new repository. Set the repository name to ``websockets-tutorial``,
74+
the visibility to Public, and click **Create repository**.
7075

71-
1. Heroku expects the server to `listen on a specific port`_, provided in the
72-
``$PORT`` environment variable.
76+
Push your code to this repository. You must replace ``python-websockets`` by
77+
your GitHub username in the following command:
7378

74-
2. Heroku sends a ``SIGTERM`` signal when `shutting down a dyno`_, which
75-
should trigger a clean exit.
79+
.. code-block:: console
7680
77-
.. _listen on a specific port: https://devcenter.heroku.com/articles/preparing-a-codebase-for-heroku-deployment#4-listen-on-the-correct-port
81+
$ git remote add origin git@github.com:python-websockets/websockets-tutorial.git
82+
$ git branch -M main
83+
$ git push -u origin main
84+
Enumerating objects: 10, done.
85+
Counting objects: 100% (10/10), done.
86+
Delta compression using up to 10 threads
87+
Compressing objects: 100% (8/8), done.
88+
Writing objects: 100% (10/10), 7.02 KiB | 7.02 MiB/s, done.
89+
Total 10 (delta 0), reused 0 (delta 0), pack-reused 0
90+
To github.com:python-websockets/websockets-tutorial.git
91+
* [new branch] main -> main
92+
Branch 'main' set up to track remote branch 'main' from 'origin'.
93+
94+
Adapt the WebSocket server
95+
--------------------------
7896

79-
.. _shutting down a dyno: https://devcenter.heroku.com/articles/dynos#shutdown
97+
Before you deploy the server, you must adapt it for Koyeb's environment. This
98+
involves three small changes:
99+
100+
1. Koyeb provides the port on which the server should listen in the ``$PORT``
101+
environment variable.
102+
103+
2. Koyeb requires a health check to verify that the server is running. We'll add
104+
a HTTP health check.
105+
106+
3. Koyeb sends a ``SIGTERM`` signal when terminating the server. We'll catch it
107+
and trigger a clean exit.
80108

81109
Adapt the ``main()`` coroutine accordingly:
82110

83111
.. code-block:: python
84112
113+
import http
85114
import os
86115
import signal
87116
117+
.. literalinclude:: ../../example/tutorial/step3/app.py
118+
:pyobject: health_check
119+
88120
.. literalinclude:: ../../example/tutorial/step3/app.py
89121
:pyobject: main
90122

123+
The ``process_request`` parameter of :func:`~asyncio.server.serve` is a callback
124+
that runs for each request. When it returns an HTTP response, websockets sends
125+
that response instead of opening a WebSocket connection. Here, requests to
126+
``/healthz`` return an HTTP 200 status code.
127+
91128
To catch the ``SIGTERM`` signal, ``main()`` creates a :class:`~asyncio.Future`
92129
called ``stop`` and registers a signal handler that sets the result of this
93130
future. The value of the future doesn't matter; it's only for waiting for
@@ -97,8 +134,6 @@ Then, by using :func:`~asyncio.server.serve` as a context manager and exiting
97134
the context when ``stop`` has a result, ``main()`` ensures that the server
98135
closes connections cleanly and exits on ``SIGTERM``.
99136

100-
The app is now fully compatible with Heroku.
101-
102137
Deploy the WebSocket server
103138
---------------------------
104139

@@ -108,79 +143,71 @@ when building the image:
108143
.. literalinclude:: ../../example/tutorial/step3/requirements.txt
109144
:language: text
110145

111-
.. admonition:: Heroku treats ``requirements.txt`` as a signal to `detect a Python app`_.
146+
.. admonition:: Koyeb treats ``requirements.txt`` as a signal to `detect a Python app`__.
112147
:class: tip
113148

114149
That's why you don't need to declare that you need a Python runtime.
115150

116-
.. _detect a Python app: https://devcenter.heroku.com/articles/python-support#recognizing-a-python-app
151+
__ https://www.koyeb.com/docs/build-and-deploy/build-from-git/python#detection
117152

118153
Create a ``Procfile`` file with this content to configure the command for
119154
running the server:
120155

121156
.. literalinclude:: ../../example/tutorial/step3/Procfile
122157
:language: text
123158

124-
Commit your changes:
159+
Commit and push your changes:
125160

126161
.. code-block:: console
127162
128163
$ git add .
129-
$ git commit -m "Deploy to Heroku."
130-
[main ...] Deploy to Heroku.
131-
3 files changed, 12 insertions(+), 2 deletions(-)
164+
$ git commit -m "Deploy to Koyeb."
165+
[main ac96d65] Deploy to Koyeb.
166+
3 files changed, 18 insertions(+), 2 deletions(-)
132167
create mode 100644 Procfile
133168
create mode 100644 requirements.txt
169+
$ git push
170+
Enumerating objects: 7, done.
171+
Counting objects: 100% (7/7), done.
172+
Delta compression using up to 10 threads
173+
Compressing objects: 100% (3/3), done.
174+
Writing objects: 100% (5/5), 906 bytes | 906.00 KiB/s, done.
175+
Total 5 (delta 2), reused 2 (delta 0), pack-reused 0
176+
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
177+
To github.com:python-websockets/websockets-tutorial.git
178+
+ 6bd6032...ac96d65 main -> main
134179
135-
Follow the `set-up instructions`_ to install the Heroku CLI and to log in, if
136-
you haven't done that yet.
137-
138-
.. _set-up instructions: https://devcenter.heroku.com/articles/getting-started-with-python#set-up
139-
140-
Create a Heroku app. You must choose a unique name and replace
141-
``websockets-tutorial`` by this name in the following command:
180+
Sign up or log in to Koyeb.
142181

143-
.. code-block:: console
144-
145-
$ heroku create websockets-tutorial
146-
Creating ⬢ websockets-tutorial... done
147-
https://websockets-tutorial.herokuapp.com/ | https://git.heroku.com/websockets-tutorial.git
148-
149-
If you reuse a name that someone else already uses, you will receive this
150-
error; if this happens, try another name:
182+
In the Koyeb control panel, create a web service with GitHub as the deployment
183+
method. `Install and authorize Koyeb's GitHub app`__ if you haven't done that yet.
151184

152-
.. code-block:: console
153-
154-
$ heroku create websockets-tutorial
155-
Creating ⬢ websockets-tutorial... !
156-
▸ Name websockets-tutorial is already taken
185+
__ https://www.koyeb.com/docs/build-and-deploy/deploy-with-git#connect-your-github-account-to-koyeb
157186

158-
Deploy by pushing the code to Heroku:
187+
Follow the steps to create a new service:
159188

160-
.. code-block:: console
189+
1. Select the ``websockets-tutorial`` repository in the list of your repositories.
190+
2. Confirm that the **Free** instance type is selected. Click **Next**.
191+
3. Configure health checks: change the protocol from TCP to HTTP and set the
192+
path to ``/healthz``. Review other settings; defaults should be correct.
193+
Click **Deploy**.
161194

162-
$ git push heroku
163-
164-
... lots of output...
165-
166-
remote: Released v1
167-
remote: https://websockets-tutorial.herokuapp.com/ deployed to Heroku
168-
remote:
169-
remote: Verifying deploy... done.
170-
To https://git.heroku.com/websockets-tutorial.git
171-
* [new branch] main -> main
195+
Koyeb builds the app, deploys it, verifies that the health checks passes, and
196+
makes the deployment active.
172197

173198
You can test the WebSocket server with the interactive client exactly like you
174-
did in the first part of the tutorial. Replace ``websockets-tutorial`` by the
175-
name of your app in the following command:
199+
did in the first part of the tutorial. The Koyeb control panel provides the URL
200+
of your app in the format: ``https://<app>-<user>-<id>.koyeb.app/``. Replace
201+
``https`` with ``wss`` in the URL and connect the interactive client:
176202

177203
.. code-block:: console
178204
179-
$ python -m websockets wss://websockets-tutorial.herokuapp.com/
180-
Connected to wss://websockets-tutorial.herokuapp.com/.
205+
$ python -m websockets wss://<app>-<user>-<id>.koyeb.app/
206+
Connected to wss://<app>-<user>-<id>.koyeb.app/.
181207
> {"type": "init"}
182208
< {"type": "init", "join": "54ICxFae_Ip7TJE2", "watch": "634w44TblL5Dbd9a"}
183-
Connection closed: 1000 (OK).
209+
210+
Press Ctrl-D to terminate the connection.
184211

185212
It works!
186213

@@ -199,7 +226,7 @@ You can take this strategy one step further by checking the address of the
199226
HTTP server and determining the address of the WebSocket server accordingly.
200227

201228
Add this function to ``main.js``; replace ``python-websockets`` by your GitHub
202-
username and ``websockets-tutorial`` by the name of your app on Heroku:
229+
username and ``websockets-tutorial`` by the name of your app on Koyeb:
203230

204231
.. literalinclude:: ../../example/tutorial/step3/main.js
205232
:language: js
@@ -224,36 +251,17 @@ Commit your changes:
224251
Deploy the web application
225252
--------------------------
226253

227-
Go to GitHub and create a new repository called ``websockets-tutorial``.
228-
229-
Push your code to this repository. You must replace ``python-websockets`` by
230-
your GitHub username in the following command:
231-
232-
.. code-block:: console
233-
234-
$ git remote add origin git@github.com:python-websockets/websockets-tutorial.git
235-
$ git push -u origin main
236-
Enumerating objects: 11, done.
237-
Counting objects: 100% (11/11), done.
238-
Delta compression using up to 8 threads
239-
Compressing objects: 100% (10/10), done.
240-
Writing objects: 100% (11/11), 5.90 KiB | 2.95 MiB/s, done.
241-
Total 11 (delta 0), reused 0 (delta 0), pack-reused 0
242-
To github.com:<username>/websockets-tutorial.git
243-
* [new branch] main -> main
244-
Branch 'main' set up to track remote branch 'main' from 'origin'.
245-
246254
Go back to GitHub, open the Settings tab of the repository and select Pages in
247255
the menu. Select the main branch as source and click Save. GitHub tells you
248256
that your site is published.
249257

250-
Follow the link and start a game!
258+
Open https://<your-username>.github.io/websockets-tutorial/ and start a game!
251259

252260
Summary
253261
-------
254262

255263
In this third part of the tutorial, you learned how to deploy a WebSocket
256-
application with Heroku.
264+
application with Koyeb.
257265

258266
You can start a Connect Four game, send the JOIN link to a friend, and play
259267
over the Internet!

docs/spelling_wordlist.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Dockerfile
2727
dyno
2828
formatter
2929
fractalideas
30+
github
3031
gunicorn
3132
healthz
3233
html
@@ -38,7 +39,7 @@ iterable
3839
js
3940
keepalive
4041
KiB
41-
Koyeb
42+
koyeb
4243
kubernetes
4344
lifecycle
4445
linkerd

example/tutorial/step3/app.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python
22

33
import asyncio
4+
import http
45
import json
56
import os
67
import secrets
@@ -183,14 +184,19 @@ async def handler(websocket):
183184
await start(websocket)
184185

185186

187+
def health_check(connection, request):
188+
if request.path == "/healthz":
189+
return connection.respond(http.HTTPStatus.OK, "OK\n")
190+
191+
186192
async def main():
187193
# Set the stop condition when receiving SIGTERM.
188194
loop = asyncio.get_running_loop()
189195
stop = loop.create_future()
190196
loop.add_signal_handler(signal.SIGTERM, stop.set_result, None)
191197

192198
port = int(os.environ.get("PORT", "8001"))
193-
async with serve(handler, "", port):
199+
async with serve(handler, "", port, process_request=health_check):
194200
await stop
195201

196202

example/tutorial/step3/main.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { createBoard, playMove } from "./connect4.js";
22

33
function getWebSocketServer() {
44
if (window.location.host === "python-websockets.github.io") {
5-
return "wss://websockets-tutorial.herokuapp.com/";
5+
return "wss://websockets-tutorial.koyeb.app/";
66
} else if (window.location.host === "localhost:8000") {
77
return "ws://localhost:8001/";
88
} else {

0 commit comments

Comments
 (0)