Skip to content

Commit 9bb3c44

Browse files
committed
Add a basic deployment guide page.
1 parent 4d72125 commit 9bb3c44

File tree

1 file changed

+334
-0
lines changed

1 file changed

+334
-0
lines changed
Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
import hyperdiv as hd
2+
from ...router import router
3+
from ...page import page
4+
from ...code_examples import code_example, docs_markdown
5+
6+
7+
@router.route("/guide/deploying")
8+
def deploying():
9+
with page() as p:
10+
11+
p.title("# Deploying Hyperdiv")
12+
13+
p.heading("## Deploying as a local tool")
14+
15+
hd.markdown(
16+
"""
17+
18+
A Hyperdiv app can be deployed as a local tool, that runs
19+
locally on the user's computer and automatically opens in
20+
a browser tab. This approach can be used to for example
21+
ship a Hyperdiv UI with a Python tool that is
22+
`pip`-installable.
23+
24+
In this approach we want to set the `HD_PRODUCTION_LOCAL`
25+
to `"1"`, so when the app runs, it automatically chooses
26+
an open port and opens a browser tab when invoked. (See
27+
[Environment Variables](/reference/env-variables)).
28+
29+
Suppose you deploy a tool on PyPI whose code bundle
30+
structure looks like:
31+
32+
```sh
33+
my_tool/
34+
my_hyperdiv_app/
35+
start.py
36+
...other app code...
37+
launch.py
38+
...
39+
```
40+
41+
Then `launch.py` could look like this:
42+
43+
```py
44+
import os
45+
import shutil
46+
47+
def launch_app():
48+
app_path = os.path.join(
49+
os.path.dirname(__file__),
50+
"my_hyperdiv_app",
51+
"start.py"
52+
)
53+
python = shutil.which("python3")
54+
os.environ["HD_PRODUCTION_LOCAL"] = "1"
55+
os.execl(python, "python", app_path)
56+
```
57+
58+
Then, you can point your packaging system's config to
59+
automatically convert this function into a command-line
60+
tool. For example, using
61+
[`console_scripts`](https://python-packaging.readthedocs.io/en/latest/command-line-scripts.html#the-console-scripts-entry-point)
62+
in `setuptools` or Poetry
63+
[`scripts`](https://python-poetry.org/docs/pyproject/#scripts).
64+
65+
For reference, Hyperdiv itself provides a command-line
66+
tool called `hyperdiv` based on
67+
[Click](https://click.palletsprojects.com/). Its
68+
implementation is
69+
[here](https://github.com/hyperdiv/hyperdiv/blob/main/hyperdiv/cli.py)
70+
and its Poetry `scripts` config is
71+
[here](https://github.com/hyperdiv/hyperdiv/blob/main/pyproject.toml). See
72+
the `[tool.poetry.scripts]` section.
73+
74+
"""
75+
)
76+
77+
p.heading("## Deploying on the web or local network")
78+
79+
hd.markdown(
80+
"""
81+
82+
A Hyperdiv app can be deployed as a web app on the world
83+
wide web or a host on a local network.
84+
85+
Suppose you have a host `foo.com` and want to deploy a
86+
Hyperdiv app there.
87+
88+
"""
89+
)
90+
91+
p.heading("### Simplest Case")
92+
93+
hd.markdown(
94+
"""
95+
96+
In the simplest case, you can just install Hyperdiv on the
97+
host and run an app there, on a port of your choice:
98+
99+
```
100+
$ HD_PORT=9000 python my-app.py
101+
```
102+
103+
Then, provided that port `9000` is open externally, you
104+
can navigate to `http://foo.com:9000` to use the
105+
app, and share this URL with users.
106+
107+
"""
108+
)
109+
110+
p.heading("### Using Nginx as a Reverse Proxy")
111+
112+
hd.markdown(
113+
"""
114+
115+
In the case above, The Hyperdiv app does not support TLS,
116+
so traffic is unencrypted. This may be ok when deploying
117+
Hyperdiv on a local, private network.
118+
119+
A more robust setup uses a reverse-proxy like Nginx to
120+
terminate TLS and forward traffic to the actual Hyperdiv
121+
app. In this setup, you can also run the Hyperdiv app in
122+
multiple processes, on multiple ports, and let Nginx play
123+
the role of a load balancer, forwarding user connections
124+
to one of the available Hyperdiv processes.
125+
126+
"""
127+
)
128+
129+
p.heading("#### Nginx Config")
130+
131+
hd.markdown(
132+
"""
133+
134+
Here's a basic Nginx config that sets up the Hyperdiv app
135+
on `my-app.foo.com`:
136+
137+
```sh
138+
server {
139+
server_name my-app.foo.com;
140+
listen 80;
141+
142+
location / {
143+
proxy_pass http://my-hyperdiv-app;
144+
proxy_http_version 1.1;
145+
proxy_set_header Host $http_host;
146+
proxy_set_header X-Real-IP $remote_addr;
147+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
148+
proxy_set_header X-Forwarded-Proto $scheme;
149+
150+
proxy_connect_timeout 3600;
151+
proxy_send_timeout 3600;
152+
proxy_read_timeout 3600;
153+
}
154+
155+
# Websocket config:
156+
location /ws {
157+
proxy_pass http://my-hyperdiv-app;
158+
proxy_http_version 1.1;
159+
proxy_set_header Host $http_host;
160+
proxy_set_header X-Real-IP $remote_addr;
161+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
162+
proxy_set_header X-Forwarded-Proto $scheme;
163+
164+
proxy_connect_timeout 3600;
165+
proxy_send_timeout 3600;
166+
proxy_read_timeout 3600;
167+
168+
proxy_set_header Upgrade $http_upgrade;
169+
proxy_set_header Connection "Upgrade";
170+
}
171+
}
172+
173+
upstream my-hyperdiv-app {
174+
server 127.0.0.1:9000 max_fails=0;
175+
}
176+
```
177+
178+
In this setup, the Hyperdiv app runs on port `9000`, Nginx
179+
runs on port `80`, and Nginx forwards connections to the
180+
app.
181+
182+
You can run the Hyperdiv app in multiple processes on
183+
multiple ports and let Nginx play the role of a load
184+
balancer, forwarding a new user connection to one of the
185+
processes:
186+
187+
```sh
188+
upstream my-hyperdiv-app {
189+
server 127.0.0.1:9000 max_fails=0;
190+
server 127.0.0.1:9001 max_fails=0;
191+
server 127.0.0.1:9002 max_fails=0;
192+
server 127.0.0.1:9003 max_fails=0;
193+
}
194+
```
195+
196+
"""
197+
)
198+
199+
p.heading("#### TLS Setup")
200+
201+
hd.markdown(
202+
"""
203+
204+
For a basic TLS setup, using
205+
[Certbot](https://certbot.eff.org) is recommended. After
206+
setting up the Nginx config, Certbot will automatically
207+
modify your config to listen on port 443 with valid TLS
208+
certificates.
209+
210+
"""
211+
)
212+
213+
p.heading("### Using Supervisor")
214+
215+
hd.markdown(
216+
"""
217+
218+
To manage the Hyperdiv processes of the app running behind
219+
Nginx, using [Supervisor](http://supervisord.org) is
220+
recommended. Supervisor monitors the running processes and
221+
restarts them if they crash, captures their logs, and
222+
offers a command-line interface for bulk-starting/stopping
223+
the processes.
224+
225+
Suppose user `bob` is deploying an app called `my-app` on
226+
Linux. The app's code will be stored in
227+
`/home/bob/apps/my-app`. This directory will have the
228+
following structure:
229+
230+
```
231+
my-app/
232+
start.py
233+
start.sh
234+
... other app code ...
235+
logs/
236+
```
237+
238+
`start.py` is the app's entrypoint, that you'd normally
239+
run with `python start.py` to run your app.
240+
241+
`start.sh` is a script that enters the virtualenv in which
242+
Hyperdiv is you installed, and then executes the app's
243+
script:
244+
245+
```sh
246+
# start.sh
247+
source {path to virtualenv}/bin/activate
248+
exec python start.py
249+
```
250+
251+
If you're using Pyenv and installed Hyperdiv in a Pyenv
252+
virtualenv called `"hyperdiv-env"`, the script would be:
253+
254+
```sh
255+
# start.sh
256+
pyenv activate hyperdiv-env
257+
exec python start.py
258+
```
259+
260+
Then, here's a Supervisor config for this setup:
261+
262+
```ini
263+
[program:my-app]
264+
process_name=my-app
265+
numprocs=1
266+
command=/bin/bash /home/bob/apps/my-app/start.sh
267+
directory=/home/bob/apps/my-app
268+
environment=HD_PRODUCTION=1,HD_PORT=9000
269+
startretries=3
270+
stopasgroup=true
271+
killasgroup=true
272+
stderr_logfile=/home/bob/apps/my-app/logs/err.log
273+
stdout_logfile=/home/bob/apps/my-app/logs/out.log
274+
user=bob
275+
autostart=true
276+
autorestart=true
277+
```
278+
279+
This config runs the app in a single process on port
280+
`9000` and captures its output in `logs/err.log` and
281+
`logs/out.log` inside the app directory.
282+
283+
284+
"""
285+
)
286+
287+
p.heading("#### Multiple Processes")
288+
289+
hd.markdown(
290+
"""
291+
292+
We can make minor modifications to the config to make
293+
Supervisor spawn multiple processes on different ports:
294+
295+
```ini
296+
[program:my-app]
297+
process_name=my-app-%(process_num)s
298+
numprocs=4
299+
command=/bin/bash /home/bob/apps/my-app/start.sh
300+
directory=/home/bob/apps/my-app
301+
environment=HD_PRODUCTION=1,HD_PORT=900%(process_num)s
302+
startretries=3
303+
stopasgroup=true
304+
killasgroup=true
305+
stderr_logfile=/home/bob/apps/my-app/logs/err.log
306+
stdout_logfile=/home/bob/apps/my-app/logs/out.log
307+
user=bob
308+
autostart=true
309+
autorestart=true
310+
```
311+
312+
These are the modified lines:
313+
314+
```ini
315+
process_name=my-app-%(process_num)s
316+
numprocs=4
317+
environment=HD_PRODUCTION=1,HD_PORT=900%(process_num)s
318+
```
319+
320+
In this config, we tell supervisor to start and maintain 4
321+
processes running the app, and use the `%(process_num)s`
322+
Supervisor variable to give unique names and ports to each
323+
process. The `%(process_num)s` variable starts at `0` and
324+
increments by `1` with each added process.
325+
326+
In this case, the app will run in 4 processes on ports
327+
`9000`, `9001`, `9002`, `9003`. Using the Nginx config
328+
above, with Certbot set up, we can access our app at
329+
`https://my-app.foo.com`, and Nginx will round-robin
330+
incoming connections into one of the four underlying
331+
processes.
332+
333+
"""
334+
)

0 commit comments

Comments
 (0)