Deploy to Fly#
This guide describes how to deploy a websockets server to Fly.
The free tier of Fly is sufficient for trying this guide.
The free tier include up to three small VMs. This guide uses only one.
We’re going to deploy a very simple app. The process would be identical for a more realistic app.
Create application#
Here’s the implementation of the app, an echo server. Save it in a file called
app.py
:
#!/usr/bin/env python
import asyncio
import http
import signal
import websockets
async def echo(websocket):
async for message in websocket:
await websocket.send(message)
async def health_check(path, request_headers):
if path == "/healthz":
return http.HTTPStatus.OK, [], b"OK\n"
async def main():
# Set the stop condition when receiving SIGTERM.
loop = asyncio.get_running_loop()
stop = loop.create_future()
loop.add_signal_handler(signal.SIGTERM, stop.set_result, None)
async with websockets.serve(
echo,
host="",
port=8080,
process_request=health_check,
):
await stop
if __name__ == "__main__":
asyncio.run(main())
This app implements typical requirements for running on a Platform as a Service:
it provides a health check at
/healthz
;it closes connections and exits cleanly when it receives a
SIGTERM
signal.
Create a requirements.txt
file containing this line to declare a dependency
on websockets:
websockets
The app is ready. Let’s deploy it!
Deploy application#
Follow the instructions to install the Fly CLI, if you haven’t done that yet.
Sign up or log in to Fly.
Launch the app — you’ll have to pick a different name because I’m already using
websockets-echo
:
$ fly launch
Creating app in ...
Scanning source code
Detected a Python app
Using the following build configuration:
Builder: paketobuildpacks/builder:base
? App Name (leave blank to use an auto-generated name): websockets-echo
? Select organization: ...
? Select region: ...
Created app websockets-echo in organization ...
Wrote config file fly.toml
? Would you like to set up a Postgresql database now? No
We have generated a simple Procfile for you. Modify it to fit your needs and run "fly deploy" to deploy your application.
This will build the image with a generic buildpack.
Fly can build images with a Dockerfile or a buildpack. Here, fly
launch
configures a generic Paketo buildpack.
If you’d rather package the app with a Dockerfile, check out the guide to containerize an application.
Replace the auto-generated fly.toml
with:
app = "websockets-echo"
kill_signal = "SIGTERM"
[build]
builder = "paketobuildpacks/builder:base"
[[services]]
internal_port = 8080
protocol = "tcp"
[[services.http_checks]]
path = "/healthz"
[[services.ports]]
handlers = ["tls", "http"]
port = 443
This configuration:
listens on port 443, terminates TLS, and forwards to the app on port 8080;
declares a health check at
/healthz
;requests a
SIGTERM
for terminating the app.
Replace the auto-generated Procfile
with:
web: python app.py
This tells Fly how to run the app.
Now you can deploy it:
$ fly deploy
... lots of output...
==> Monitoring deployment
1 desired, 1 placed, 1 healthy, 0 unhealthy [health checks: 1 total, 1 passing]
--> v0 deployed successfully
Validate deployment#
Let’s confirm that your application is running as expected.
Since it’s a WebSocket server, you need a WebSocket client, such as the interactive client that comes with websockets.
If you’re currently building a websockets server, perhaps you’re already in a virtualenv where websockets is installed. If not, you can install it in a new virtualenv as follows:
$ python -m venv websockets-client
$ . websockets-client/bin/activate
$ pip install websockets
Connect the interactive client — you must replace websockets-echo
with the
name of your Fly app in this command:
$ python -m websockets wss://websockets-echo.fly.dev/
Connected to wss://websockets-echo.fly.dev/.
>
Great! Your app is running!
Once you’re connected, you can send any message and the server will echo it, or press Ctrl-D to terminate the connection:
> Hello!
< Hello!
Connection closed: 1000 (OK).
You can also confirm that your application shuts down gracefully.
Connect an interactive client again — remember to replace websockets-echo
with your app:
$ python -m websockets wss://websockets-echo.fly.dev/
Connected to wss://websockets-echo.fly.dev/.
>
In another shell, restart the app — again, replace websockets-echo
with your
app:
$ fly restart websockets-echo
websockets-echo is being restarted
Go back to the first shell. The connection is closed with code 1001 (going away).
$ python -m websockets wss://websockets-echo.fly.dev/
Connected to wss://websockets-echo.fly.dev/.
Connection closed: 1001 (going away).
If graceful shutdown wasn’t working, the server wouldn’t perform a closing handshake and the connection would be closed with code 1006 (abnormal closure).