Mad*_*ist 12 lisp common-lisp amazon-ec2 hunchentoot amazon-web-services
我想知道如何部署一个用Hunchentoot,Wookie,Woo甚至Clack编写的Common Lisp Web应用程序.
也就是说,假设我编写了一个包含一些文件,软件包等的应用程序.通常,当我在本地工作时,我只需在REPL中运行一个启动服务器的命令,然后使用localhost:8000或类似的方式访问它.
但是,对于将应用程序部署到AWS EC2等生产服务器的过程,我感到有些疑惑.我应该以什么形式部署Lisp代码?有不同的选择吗?如果需要重新启动服务器或遇到问题,会发生什么?
Ehv*_*nce 17
我最近通过为Web应用程序构建自包含的可执行文件找到了一些东西,我在lisp-journey/web-dev(运输和部署部分)以及Common Lisp Cookbook/scripting的构建部分上写了这篇文章. #for-web-apps.
我在这里复制有趣的部分,每个资源上都有更多.欢迎编辑,主要是关于这些资源谢谢!
另请参阅https://github.com/CodyReichert/awesome-cl#interfaces-to-other-package-managers以获取与Homebrew和Debian软件包的绑定.
如何构建(自包含)可执行文件是特定于实现的(请参阅下面的Buildapp和Rowsell).对于SBCL,正如 其文档所述,它是一个问题:
sb-ext
:executable t是一个运行外部进程的SBCL扩展.请参阅其他
SBCL扩展
(其中许多扩展是在其他库中实现可移植的).
load 告诉你构建一个可执行文件而不是图像.我们可以构建一个图像来保存当前Lisp图像的状态,以后再回来使用它.如果我们做了大量计算密集型工作,那么特别有用.
如果您尝试在Slime中运行它,您将收到有关运行的线程的错误:
无法在运行多个线程的情况下保存核心.
从简单的SBCL repl运行该命令.
我想你的项目有Quicklisp依赖项.你必须:
--load 项目的.asd这给了:
--eval
从命令行或Makefile中,使用make和asdf:make :my-system:
ros build
现在我们已经看到了基础知识,我们需要一种便携式方法.自版本3.1以来,ASDF允许这样做.它引入了从.asd读取参数的ros install
my-app命令.将此添加到.asd声明中:
apt install buildapp
并打电话main.
所以,在Makefile中:
(sb-ext:save-lisp-and-die #P"path/name-of-executable" :toplevel #'my-app:main-function :executable t)
Run Code Online (Sandbox Code Playgroud)
Roswell,一个实现管理器以及更多,也有这个bordeaux-threads命令,应该适用于许多实现.
我们还可以使用Roswell使我们的应用程序可以安装(ql:quickload
"bordeaux-threads").查看其文档.
我们将在Buildapp上完成一个词 ,这是一个久经考验且仍然流行的"SBCL或CCL应用程序,用于配置和保存可执行的Common Lisp镜像".
许多应用程序使用它(例如,
pgloader),它可以在Debian上使用:bt但是现在你不应该使用asdf:make或Roswell.
我们可以类似地为我们的web应用程序构建一个自包含的可执行文件.因此它将包含一个Web服务器,并且能够在命令行上运行:
(load "my-app.asd")
(ql:quickload :my-app)
(sb-ext:save-lisp-and-die #p"my-app-binary" :toplevel #'my-app:main :executable t)
Run Code Online (Sandbox Code Playgroud)
请注意,这会运行生产Web服务器,而不是开发Web服务器,因此我们可以立即在VPS上运行二进制文件,并从外部访问应用程序.
我们有一件事需要处理,它是找到并将正在运行的Web服务器的线程放在前台.在我们的uiop函数中,我们可以这样做:
build:
sbcl --non-interactive \
--load my-app.asd \
--eval '(ql:quickload :my-app)' \
--eval "(sb-ext:save-lisp-and-die #p\"my-app\" :toplevel #my-app:main :executable t)"
Run Code Online (Sandbox Code Playgroud)
我们使用了uiop:quit库(sb-ext:quit,别名M-x slime-connect)M-x slime-eval-region,它是已经加载的ASDF的一部分,以便以可移植的方式退出(fab update带有可选的返回代码,而不是git pull).
看到食谱.
直接使用可执行文件.从外面可以看到Web应用程序.
看到这个buildpack.
了解如何在您的系统上执行此操作.
大多数GNU/Linux发行版现在都带有Systemd.
示例搜索结果:
它就像编写配置文件一样简单:
:build-operation "program-op" ;; leave as is
:build-pathname "<binary-name>"
:entry-point "<my-system:main-function>"
Run Code Online (Sandbox Code Playgroud)
运行命令启动它:
LISP ?= sbcl
build:
$(LISP) --non-interactive \
--load my-app.asd \
--eval '(ql:quickload :my-app)' \
--eval '(asdf:make :my-system)'
Run Code Online (Sandbox Code Playgroud)
检查其状态的命令:
$ ./my-web-app
Hunchentoot server is started.
Listening on localhost:9003.
Run Code Online (Sandbox Code Playgroud)
和Systemd可以处理日志记录(我们写入stdout或stderr,它写日志):
(defun main ()
(start-app :port 9003) ;; our start-app, for example clack:clack-up
;; let the webserver run.
;; warning: hardcoded "hunchentoot".
(handler-case (bt:join-thread (find-if (lambda (th)
(search "hunchentoot" (bt:thread-name th)))
(bt:all-threads)))
;; Catch a user's C-c
(#+sbcl sb-sys:interactive-interrupt
#+ccl ccl:interrupt-signal-condition
#+clisp system::simple-interrupt-condition
#+ecl ext:interactive-interrupt
#+allegro excl:interrupt-signal
() (progn
(format *error-output* "Aborting.~&")
(clack:stop *server*)
(uiop:quit)))
(error (c) (format t "Woops, an unknown error occured:~&~a~&" c))))
Run Code Online (Sandbox Code Playgroud)
它处理崩溃并重新启动应用程序:
# /etc/systemd/system/my-app.service
[Unit]
Description=stupid simple example
[Service]
WorkingDirectory=/path/to/your/app
ExecStart=/usr/local/bin/sthg sthg
Type=simple
Restart=always
RestartSec=10
Run Code Online (Sandbox Code Playgroud)
它可以在重启后启动应用程序:
sudo systemctl start my-app.service
Run Code Online (Sandbox Code Playgroud)
启用它:
systemctl status my-app.service
Run Code Online (Sandbox Code Playgroud)
如果您的服务器上出现SBCL错误:
journalctl -f -u my-app.service
Run Code Online (Sandbox Code Playgroud)
然后禁用ASLR:
Restart=always
Run Code Online (Sandbox Code Playgroud)
这里的小例子:http://cvberry.com/tech_writings/howtos/remotely_modifying_a_running_program_using_swank.html.
它定义了一个永远打印的简单函数:
[Install]
WantedBy=basic.target
Run Code Online (Sandbox Code Playgroud)
在我们的服务器上,我们运行它
sudo systemctl enable my-app.service
Run Code Online (Sandbox Code Playgroud)
我们在开发机器上进行端口转发:
mmap: wanted 1040384 bytes at 0x20000000, actually mapped at 0x715fa2145000
ensure_space: failed to allocate 1040384 bytes at 0x20000000
(hint: Try "ulimit -a"; maybe you should increase memory limits.)
Run Code Online (Sandbox Code Playgroud)
这将安全地将example.com上的服务器上的端口4006转发到我们本地计算机的端口4006(swanks接受来自localhost的连接).
我们连接到正在运行的swank sb-ext,输入端口4006.
我们可以编写新代码:
sudo bash -c "echo 0 > /proc/sys/kernel/randomize_va_space"
Run Code Online (Sandbox Code Playgroud)
:executable t例如,像往常一样评估它.输出应该改变.
CV Berry的页面上有更多指针.
Quickutil的示例.请参阅有关lisp-journey的说明.
它必须在服务器上运行(一个简单的fabfile命令可以通过ssh调用它).事先,a load已--load在服务器上运行,因此存在新代码但未运行.它连接到本地swank服务器,加载新代码,连续停止和启动应用程序.
请参阅https://lispcookbook.github.io/cl-cookbook/testing.html#continuous-integration
要在生产环境中运行Lisp图像,可以使用以下命令从Lisp代码生成fasl文件:
(compile-file "app.lisp")
Run Code Online (Sandbox Code Playgroud)
通过调用sbcl运行生成的.fas文件。
sbcl --noinform \
--load app.fas \
--eval "(defun main (argv) (declare (ignore argv)) (hunchentoot:start (make-instance 'hunchentoot:easy-acceptor :port 4242)))"
Run Code Online (Sandbox Code Playgroud)
我找到了一个博客,其中包含一个解决方案,我已经根据我对 Linux 机器上的生产系统的需求进行了调整。不幸的是,我找不到该博客的参考资料,所以我只能向您展示我的解决方案,即针对 CCL 的解决方案(而原始解决方案是针对 SBCL 的),我对此更为熟悉。这是启动系统的程序:
(require 'swank)
(require 'hunchentoot)
(defparameter *httpd-port* 9090) ; The port Hunchentoot will be listening on
(defparameter *shutdown-port* 6700) ; The port CCL will be listening for shutdown
; this port is the same used in /etc/init.d/hunchentoot
(defparameter *swank-port* 5016) ; The port used for remote interaction with slime
;; Start the Swank server
(defparameter *swank-server*
(swank:create-server :port *swank-port* :dont-close t))
(require 'YOUR-PACKAGE)
(YOUR-PACKAGE:YOUR-STARTING-FUNCTION)
(princ "Hunchentoot started on port ")
(princ *httpd-port*)
(terpri)
(let* ((socket (make-socket :connect :passive :local-host "127.0.0.1" :local-port *shutdown-port* :reuse-address t))
(stream (accept-connection socket)))
(close stream)
(close socket))
(print "Stopping Hunchentoot...")
(YOUR-PACKAGE:YOUR-STOPPING-FUNCTION)
(dolist (proc (all-processes))
(unless (equal proc *current-process*)
(process-kill proc)))
(sleep 1)
(quit)
Run Code Online (Sandbox Code Playgroud)
这个想法是,您可以通过指定 swank 使用的端口来使用 slime 连接到正在运行的系统。我使用过它几次,例如动态更改数据库链接,这种可能性的强大功能给我留下了深刻的印象。
正在运行的系统可以通过以下方式终止:
telnet 127.0.0.1 6700
Run Code Online (Sandbox Code Playgroud)
并由类似以下内容发起:
nohup ccl -l initcclserver.lisp >& server.out &
Run Code Online (Sandbox Code Playgroud)
在该脚本的早期版本中,我找到了 SBCL 特定的部分,因此如果您使用它,您可以修改该脚本。
为了接受终止连接:
(sb-bsd-sockets:socket-bind socket #(127 0 0 1) *shutdown-port*)
(sb-bsd-sockets:socket-listen socket 1)
(multiple-value-bind (client-socket addr port)
(sb-bsd-sockets:socket-accept socket)
(sb-bsd-sockets:socket-close client-socket)
(sb-bsd-sockets:socket-close socket)))
Run Code Online (Sandbox Code Playgroud)
关闭系统:
(dolist (thread (sb-thread:list-all-threads))
(unless (equal sb-thread:*current-thread* thread)
(sb-thread:terminate-thread thread)))
(sleep 1)
(sb-ext:quit)
Run Code Online (Sandbox Code Playgroud)
希望这能有所帮助。
| 归档时间: |
|
| 查看次数: |
1067 次 |
| 最近记录: |