1
- Deployment guides
2
- =================
1
+ Deployment
2
+ ==========
3
3
4
- Discover how to deploy your application on various platforms.
4
+ .. currentmodule :: websockets
5
5
6
- Platforms-as-a-Service
6
+ Architecture decisions
7
7
----------------------
8
8
9
+ When you deploy your websockets server to production, at a high level, your
10
+ architecture will almost certainly look like the following diagram:
11
+
12
+ .. image :: architecture.svg
13
+
14
+ The basic unit for scaling a websockets server is "one server process". Each
15
+ blue box in the diagram represents one server process.
16
+
17
+ There's more variation in routing. While the routing layer is shown as one big
18
+ box, it is likely to involve several subsystems.
19
+
20
+ As a consequence, when you design a deployment, you must answer two questions:
21
+
22
+ 1. How will I run the appropriate number of server processes?
23
+ 2. How will I route incoming connections to these processes?
24
+
25
+ These questions are interrelated. There's a wide range of valid answers,
26
+ depending on your goals and your constraints.
27
+
28
+ Platforms-as-a-Service
29
+ ......................
30
+
31
+ Platforms-as-a-Service are the easiest option. They provide end-to-end,
32
+ integrated solutions and they require little configuration.
33
+
34
+ Here's how to deploy on some popular PaaS providers. Since all PaaS use
35
+ similar patterns, the concepts translate to other providers.
36
+
9
37
.. toctree ::
10
38
:titlesonly:
11
39
@@ -14,8 +42,13 @@ Platforms-as-a-Service
14
42
fly
15
43
heroku
16
44
17
- Self-hosted
18
- -----------
45
+ Self-hosted infrastructure
46
+ ..........................
47
+
48
+ If you need more control over your infrastructure, you can deploy on your own
49
+ infrastructure. This requires more configuration.
50
+
51
+ Here's how to configure some components mentioned in this guide.
19
52
20
53
.. toctree ::
21
54
:titlesonly:
@@ -24,3 +57,160 @@ Self-hosted
24
57
supervisor
25
58
nginx
26
59
haproxy
60
+
61
+ Running server processes
62
+ ------------------------
63
+
64
+ How many processes do I need?
65
+ .............................
66
+
67
+ Typically, one server process will manage a few hundreds or thousands
68
+ connections, depending on the frequency of messages and the amount of work
69
+ they require.
70
+
71
+ CPU and memory usage increase with the number of connections to the server.
72
+
73
+ Often CPU is the limiting factor. If a server process goes to 100% CPU, then
74
+ you reached the limit. How much headroom you want to keep is up to you.
75
+
76
+ Once you know how many connections a server process can manage and how many
77
+ connections you need to handle, you can calculate how many processes to run.
78
+
79
+ You can also automate this calculation by configuring an autoscaler to keep
80
+ CPU usage or connection count within acceptable limits.
81
+
82
+ .. admonition :: Don't scale with threads. Scale only with processes.
83
+ :class: tip
84
+
85
+ Threads don't make sense for a server built with :mod: `asyncio `.
86
+
87
+ How do I run processes?
88
+ .......................
89
+
90
+ Most solutions for running multiple instances of a server process fall into
91
+ one of these three buckets:
92
+
93
+ 1. Running N processes on a platform:
94
+
95
+ * a Kubernetes Deployment
96
+
97
+ * its equivalent on a Platform as a Service provider
98
+
99
+ 2. Running N servers:
100
+
101
+ * an AWS Auto Scaling group, a GCP Managed instance group, etc.
102
+
103
+ * a fixed set of long-lived servers
104
+
105
+ 3. Running N processes on a server:
106
+
107
+ * preferably via a process manager or supervisor
108
+
109
+ Option 1 is easiest if you have access to such a platform. Option 2 usually
110
+ combines with option 3.
111
+
112
+ How do I start a process?
113
+ .........................
114
+
115
+ Run a Python program that invokes :func: `~asyncio.server.serve ` or
116
+ :func: `~asyncio.router.route `. That's it!
117
+
118
+ Don't run an ASGI server such as Uvicorn, Hypercorn, or Daphne. They're
119
+ alternatives to websockets, not complements.
120
+
121
+ Don't run a WSGI server such as Gunicorn, Waitress, or mod_wsgi. They aren't
122
+ designed to run WebSocket applications.
123
+
124
+ Applications servers handle network connections and expose a Python API. You
125
+ don't need one because websockets handles network connections directly.
126
+
127
+ How do I stop a process?
128
+ ........................
129
+
130
+ Process managers send the SIGTERM signal to terminate processes. Catch this
131
+ signal and exit the server to ensure a graceful shutdown.
132
+
133
+ Here's an example:
134
+
135
+ .. literalinclude :: ../../example/faq/shutdown_server.py
136
+ :emphasize-lines: 14-16
137
+
138
+ When exiting the context manager, :func: `~asyncio.server.serve ` closes all
139
+ connections with code 1001 (going away). As a consequence:
140
+
141
+ * If the connection handler is awaiting
142
+ :meth: `~asyncio.server.ServerConnection.recv `, it receives a
143
+ :exc: `~exceptions.ConnectionClosedOK ` exception. It can catch the exception
144
+ and clean up before exiting.
145
+
146
+ * Otherwise, it should be waiting on
147
+ :meth: `~asyncio.server.ServerConnection.wait_closed `, so it can receive the
148
+ :exc: `~exceptions.ConnectionClosedOK ` exception and exit.
149
+
150
+ This example is easily adapted to handle other signals.
151
+
152
+ If you override the default signal handler for SIGINT, which raises
153
+ :exc: `KeyboardInterrupt `, be aware that you won't be able to interrupt a
154
+ program with Ctrl-C anymore when it's stuck in a loop.
155
+
156
+ Routing connections
157
+ -------------------
158
+
159
+ What does routing involve?
160
+ ..........................
161
+
162
+ Since the routing layer is directly exposed to the Internet, it should provide
163
+ appropriate protection against threats ranging from Internet background noise
164
+ to targeted attacks.
165
+
166
+ You should always secure WebSocket connections with TLS. Since the routing
167
+ layer carries the public domain name, it should terminate TLS connections.
168
+
169
+ Finally, it must route connections to the server processes, balancing new
170
+ connections across them.
171
+
172
+ How do I route connections?
173
+ ...........................
174
+
175
+ Here are typical solutions for load balancing, matched to ways of running
176
+ processes:
177
+
178
+ 1. If you're running on a platform, it comes with a routing layer:
179
+
180
+ * a Kubernetes Ingress and Service
181
+
182
+ * a service mesh: Istio, Consul, Linkerd, etc.
183
+
184
+ * the routing mesh of a Platform as a Service
185
+
186
+ 2. If you're running N servers, you may load balance with:
187
+
188
+ * a cloud load balancer: AWS Elastic Load Balancing, GCP Cloud Load
189
+ Balancing, etc.
190
+
191
+ * A software load balancer: HAProxy, NGINX, etc.
192
+
193
+ 3. If you're running N processes on a server, you may load balance with:
194
+
195
+ * A software load balancer: HAProxy, NGINX, etc.
196
+
197
+ * The operating system — all processes listen on the same port
198
+
199
+ You may trust the load balancer to handle encryption and to provide security.
200
+ You may add another layer in front of the load balancer for these purposes.
201
+
202
+ There are many possibilities. Don't add layers that you don't need, though.
203
+
204
+ How do I implement a health check?
205
+ ..................................
206
+
207
+ Load balancers need a way to check whether server processes are up and running
208
+ to avoid routing connections to a non-functional backend.
209
+
210
+ websockets provide minimal support for responding to HTTP requests with the
211
+ ``process_request `` hook.
212
+
213
+ Here's an example:
214
+
215
+ .. literalinclude :: ../../example/faq/health_check_server.py
216
+ :emphasize-lines: 7-9,16
0 commit comments