/
nginx_shell.nix
246 lines (221 loc) · 8.08 KB
/
nginx_shell.nix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# nixos-22.11 channel
# Apr 9, 2023, 9:59 PM EDT
{ nixpkgs_commit ? "ea96b4af6148114421fda90df33cf236ff5ecf1d"
, project_dir ? builtins.toString ./..
, nix_shell_dir ? "${project_dir}/_nix-shell"
, gunicorn_dir ? "${nix_shell_dir}/gunicorn"
, django_dir ? "${nix_shell_dir}/django"
, nginx_dir ? "${nix_shell_dir}/nginx"
, sudo ? false
, port ? "80"
}:
# HOW TO CALL? {{- {{-
# ====================================================
# This Nix shell expression depends on `nix/dev_shell.nix`.
#
# nix-shell --argstr "port" "8001" nix/nginx_shell.nix
#
# or, to replace the calling shell:
#
# exec nix-shell --argstr "port" "8001" nix/nginx_shell.nix
#
# or, when serving from a privileged port (e.g., 80):
#
# # TODO See issue #24
# # NOTE Is it ok to run NGINX as root?
# # Yes: https://unix.stackexchange.com/questions/134301/
#
# nix-shell --arg "sudo" "true" nix/nginx_shell.nix
#
# > NOTE STATIC ASSETS NOT SERVED WHEN USING "sudo"
# >
# > Permissions. The whole project is probably
# > served from a home directory, and NGINX's
# > `nobody` user does not have access to
# > it. For example, files and directories
# > in the "slate-2" repo have 644 and 755
# > permissions, respectively, but the home
# > directory has 750, meanning that `nobody`
# > either has to be the owner of the home
# > directory or it has to be in the group
# > that has read rights on the home dir.
# >
# > The following should help:
# >
# > sudo -a -G <home-dir-allowed-group> nobody
#
# TODO Perhaps moving out the static assets from
# the home directory would be more secure.`
#
# }}- }}-
# HOW TO MONITOR? {{- {{-
# ====================================================
#
# watch -n 1 "{ ls -l _nix-shell/*/*pid ; echo ; sudo ps axf | egrep 'gunicorn|nginx' ; }"
#
# From this thread: https://unix.stackexchange.com/q/64736/85131
#
# }}- }}-
# TODO look into `nginx` package in Nixpkgs
# NOTE ERRORS ON FIRST RUN {{- {{-
# ===================
# There will probably be a lot of errors along the lines of:
#
# 2023/05/05 15:55:03 [emerg] 3106282#3106282: mkdir() "/var/cache/nginx/proxy" failed (13: Permission denied)
#
# This is because (as far as I was able to figure it
# out) the NGINX Nix package has been compiled with
# these hard paths that HAVE TO exist, even though
# they won't be touched (and some of them could be
# over-ridden; e.g., error.log - see below).`
#
# These have done the trick thus far:
#
# sudo mkdir -p /var/log/nginx/
# sudo touch /var/log/nginx/error.log
# sudo mkdir -p /var/cache/nginx/proxy
# sudo mkdir -p /var/cache/nginx/uwsgi
# sudo mkdir -p /var/cache/nginx/scgi
# sudo mkdir -p /var/cache/nginx/fastcgi
# sudo mkdir -p /var/cache/nginx/client_body
#
# # https://serverfault.com/questions/235154
# # (The user should be whoever starts NGINX.)
# sudo chown -R $(whoami):$(whoami) /var/{log,cache}/nginx
#
# As a one-liner:
#
# sudo mkdir -p /var/log/nginx/ && sudo touch /var/log/nginx/error.log && sudo mkdir -p /var/cache/nginx/proxy && sudo mkdir -p /var/cache/nginx/uwsgi && sudo mkdir -p /var/cache/nginx/scgi && sudo mkdir -p /var/cache/nginx/fastcgi && sudo mkdir -p /var/cache/nginx/client_body & sudo chown -R $(whoami):$(whoami) /var/{log,cache}/nginx
# }}- }}-
# OUTDATED NOTE? (2023-07-08) {{- {{-
# NOTE https://discourse.nixos.org/t/how-to-add-local-files-into-nginx-derivation-for-nix-shell/6603
# Opted to keep `nginx.conf` out of the store, because
# 1. it is already in version control
# 2. if changes need to be made, one would have to create another config to override it
# -- Although, are there merits to keep it in the store?
# + with NixOS, this would be a no brainer, but then the config would have to be rebuilt
# + if a default config is kept in the store, it could still be over-ridden with another one using `-c`
# Nonetheless, when this repo is deployed via a `shell.nix`, it may be more convenient to refer to a non-store config.
#
# let
# nginx-with-config = pkgs.writeScriptBin "nginx-alt" ''
# exec ${pkgs.nginx}/bin/nginx -c ${./nginx.conf} "$@"
# '';
#
# in
# }}- }}-
let
# NGINX doc's most valuable pages:
# + [Alphabetical index of directives](http://nginx.org/en/docs/dirindex.html)
# + [Alphabetical index of variables](http://nginx.org/en/docs/varindex.html)
pkgs = # {{-
import
# The downloaded archive will be (temporarily?) housed in the Nix store
# e.g., "/nix/store/gk9x7syd0ic6hjrf0fs6y4bsd16zgscg-source"
# (Try any of the `fetchTarball` commands below in `nix repl`, and it
# will print out the path.)
( builtins.fetchTarball nixpkgs_url) { config = {}; overlays = []; }
;
nixpkgs_url = "https://github.com/nixos/nixpkgs/tarball/${nixpkgs_commit}";
# }}-
timestamp = # {{-
builtins.readFile (
pkgs.runCommand
"timestamp"
{ when = builtins.currentTime; }
"echo -n `date -d @$when +%Y-%m-%d_%H-%M-%S` > $out"
)
;
# https://discourse.nixos.org/t/how-to-create-a-timestamp-in-a-nix-expression/30329
# }}-
nginx_conf = # {{-
import
./nginx/nginx_conf.nix
{ inherit pkgs
nix_shell_dir
django_dir
nginx_dir
timestamp
port
;
}
;
# }}-
# NOTE See https://discourse.nixos.org/t/how-to-add-local-files-into-nginx-derivation-for-nix-shell/6603
# QUESTION Why use the `*Bin` version?
# ANSWER Because the NGINX executable will then be
# added to PATH automatically.
nginx_with_config =
pkgs.writeShellScriptBin
"nginx_lynx"
''
exec ${pkgs.nginx}/bin/nginx -c ${nginx_conf} "$@"
''
;
in
pkgs.mkShell {
buildInputs =
[ pkgs.inotify-tools
nginx_with_config
];
shellHook =
let
gunicorn_pidfile = "${gunicorn_dir}/${timestamp}.pid";
sudo_string = "${ if sudo then "sudo" else "" }";
in
''
set -x
''
# TODO Convert to systemd service instead?
# See note above `inotifywait` below.
+ ''
( cd ${project_dir}/lynx && \
gunicorn \
--bind 127.0.0.1:8000 \
--workers 3 \
--log-level 'debug' \
--preload \
--capture-output \
--pid ${gunicorn_pidfile} \
--access-logfile "${gunicorn_dir}/access_${timestamp}.log" \
--error-logfile "${gunicorn_dir}/error_${timestamp}.log" \
mysite.wsgi:application \
--daemon
)
''
# NOTE Must wait for Gunicorn's pidfile,
# otherwise the clean-up section below won't
# register the proper command to shut down
# Gunicorn (the `cat` is evaluated right
# away, not lazily, so if the pidfile
# doesn't exist, the command will fail with
# `kill -s SIGTERM`).
+ ''
inotifywait --event create,moved_to,attrib --include '${timestamp}.pid$' ${gunicorn_dir}
''
# NOTE `gunicorn_dir` is created in
# `dev_shell.nix` because Gunicorn can be
# tested with simple Just recipe, and NGINX
# is what depends on Gunicorn - not vice
# versa.
+ ''
mkdir -p ${nginx_dir}
${sudo_string} $(which nginx_lynx)
''
# NOTE It may take some time for NGINX to shut
# down (e.g., after leaving the Nix shell);
# the line below in `ps ax` is a good sign:
#
# 2814526 ? S 0:00 nginx: worker process is shutting down
#
+ ''
trap \
"
${sudo_string} $(which nginx_lynx) -s quit
kill -s SIGTERM $( cat ${gunicorn_pidfile} )
" \
EXIT
''
;
}
# vim: set foldmethod=marker foldmarker={{-,}}- foldlevelstart=0 tabstop=2 shiftwidth=2 expandtab: