amazon-web-services - 部署 Common Lisp Web 应用程序

标签 amazon-web-services amazon-ec2 lisp common-lisp hunchentoot

我想知道如何部署一个用 Hunchentoot、Wookie、Woo 甚至 Clack 编写的 Common Lisp Web 应用程序。

也就是说,假设我编写了一个包含一些文件、包等的应用程序。通常,当我在本地工作时,我只需在 REPL 中运行一个命令来启动服务器,然后使用 localhost:8000 访问它。或类似的东西。

但是,我对将应用程序部署到 AWS EC2 等生产服务器的过程感到有些困惑。我应该以什么形式部署 Lisp 代码?有不同的选择吗?如果服务器需要重新启动或遇到问题,会发生什么情况?

最佳答案

我最近通过为网络应用程序构建独立的可执行文件找到了一些东西,我在 lisp-journey/web-dev 上写了这篇文章。 (运输和部署部分),以及 Common Lisp Cookbook/scripting#for-web-apps 上的构建部分.
我在这里复制了有趣的部分,每个资源都有更多内容。欢迎编辑,主要针对这些资源,谢谢!
编辑 2019 年 7 月:我在食谱上贡献了一个页面:https://lispcookbook.github.io/cl-cookbook/web.html
编辑 :另请参阅提供专业 CL 支持的工具和平台列表:https://github.com/CodyReichert/awesome-cl#deployment
(已编辑)如何将 Web 应用程序作为脚本运行
我在下面解释了如何构建和运行可执行文件,但我们当然可以将应用程序作为脚本运行。在 lisp 文件中,说 run.lisp , 确保:

  • 加载项目的 asd 文件:(load "my-project.asd")
  • 加载其依赖项:(ql:quickload :my-project)
  • 调用它的主函数:(my-project:start) (给定 start 是一个导出符号,否则 ::start )。

  • 这样做时,应用程序启动并返回一个 Lisp REPL。您可以与正在运行的应用程序进行交互。您可以更新它,甚至可以在它运行时安装新的 Quicklisp 库。
    如何构建一个独立的可执行文件
    另见 https://github.com/CodyReichert/awesome-cl#interfaces-to-other-package-managers用于绑定(bind) Homebrew 和 Debian 软件包。
    与 SBCL
    如何构建(自包含)可执行文件是特定于实现的(请参阅
    在 Buildapp 和 Rowsell 下方)。使用 SBCL,正如所说
    its documentation ,
    这是一个问题:
    (sb-ext:save-lisp-and-die #P"path/name-of-executable" :toplevel #'my-app:main-function :executable t)
    
    sb-ext是运行外部进程的 SBCL 扩展。查看其他
    SBCL extensions
    (其中许多都在其他库中实现了可移植性)。:executable t告诉构建一个可执行文件而不是一个
    图片。我们可以构建一个图像来保存我们当前的状态
    Lisp 图像,稍后再回来使用它。特别有用,如果
    我们做了很多计算密集型的工作。
    如果你尝试在 Slime 中运行它,你会得到一个关于线程运行的错误:

    Cannot save core with multiple threads running.


    从简单的 SBCL repl 运行命令。
    我想你的项目有 Quicklisp 依赖项。那么你必须:
  • 确保在 Lisp 启动时安装并加载了 Quicklisp(您
    完成 Quicklisp 安装)
  • load项目的 .asd
  • 安装依赖项
  • 构建可执行文件。

  • 这给出了:
    (load "my-app.asd")
    (ql:quickload :my-app)
    (sb-ext:save-lisp-and-die #p"my-app-binary" :toplevel #'my-app:main :executable t)
    
    在命令行或 Makefile 中,使用 --load--eval :
    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)"
    
    与美国自卫队
    现在我们已经了解了基础知识,我们需要一个可移植的方法。由于其
    版本 3.1,ASDF 允许这样做。它介绍了 make command ,
    从 .asd 读取参数。将此添加到您的 .asd 声明中:
    :build-operation "program-op" ;; leave as is
    :build-pathname "<binary-name>"
    :entry-point "<my-system:main-function>"
    
    并调用 asdf:make :my-system .
    因此,在 Makefile 中:
    LISP ?= sbcl
    
    build:
        $(LISP) --non-interactive \
            --load my-app.asd \
            --eval '(ql:quickload :my-app)' \
            --eval '(asdf:make :my-system)' 
    
    使用 Roswell 或 Buildapp
    Roswell ,实现经理和许多
    更多,还有ros build命令,这应该适用于许多
    实现。
    我们还可以通过 ros install my-app 使我们的应用程序可与 Roswell 一起安装.请参阅其文档。
    我们会以一句话结束
    Buildapp ,经过实战考验的
    仍然流行的“用于配置和保存 SBCL 或 CCL 的应用程序”
    一个可执行的 Common Lisp 镜像”。
    许多应用程序都使用它(例如,
    pgloader ),可在
    Debian:apt install buildapp ,但你现在不应该用 asdf:make 或 Roswell 需要它。
    对于网络应用程序
    我们可以类似地为我们的网络应用程序构建一个独立的可执行文件。它
    因此将包含一个 Web 服务器并且能够在
    命令行:
    $ ./my-web-app
    Hunchentoot server is started.
    Listening on localhost:9003.
    
    请注意,这运行的是生产网络服务器,而不是开发网络服务器,
    所以我们可以立即在我们的 VPS 上运行二进制文件并从
    外部。
    我们有一件事情要处理,那就是找到并放置
    在前台运行的 Web 服务器。在我们的 main函数,我们
    可以做这样的事情:
    (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))))
    
    我们使用了 bordeaux-threads库( (ql:quickload "bordeaux-threads") ,别名 bt )和 uiop ,这是 ASDF 的一部分,所以
    已经加载,以便以可移植的方式退出( uiop:quit ,带有
    一个可选的返回码,而不是 sb-ext:quit )。
    解析命令行参数
    见食谱here . TLDR;使用 uiop:command-line-arguments获取参数列表。为了真正解析它们,有一些库。
    部署
    直接与可执行文件。 Web 应用程序从外面立即可见。
    在 Heroku 上
    this buildpack .
    守护进程,崩溃时重启,处理日志
    了解如何在您的系统上执行此操作。
    大多数 GNU/Linux 发行版现在都带有 Systemd。
    示例 search结果:
  • https://seanmcgary.com/posts/deploying-nodejs-applications-with-systemd/

  • 就像写一个配置文件一样简单:
    # /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
    
    运行命令来启动它:
    sudo systemctl start my-app.service
    
    检查其状态的命令:
    systemctl status my-app.service
    
    Systemd 可以处理 日志 (我们写入 stdout 或 stderr,它会写入日志):
    journalctl -f -u my-app.service
    
    它处理崩溃和 重新启动应用程序 :
    Restart=always
    
    它可以重启后启动应用 :
    [Install]
    WantedBy=basic.target
    
    启用它:
    sudo systemctl enable my-app.service
    
    调试 SBCL 错误:ensure_space: failed to allocation n bytes
    如果您的服务器上的 SBCL 出现此错误:
    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.)
    
    然后禁用 ASLR :
    sudo bash -c "echo 0 > /proc/sys/kernel/randomize_va_space"
    
    连接到远程 Swank 服务器
    这里的小例子:http://cvberry.com/tech_writings/howtos/remotely_modifying_a_running_program_using_swank.html .
    演示项目在这里:https://lisp-journey.gitlab.io/blog/i-realized-that-to-live-reload-my-web-app-is-easy-and-convenient/
    它定义了一个永远打印的简单函数:
    ;; a little common lisp swank demo
    ;; while this program is running, you can connect to it from another terminal or machine
    ;; and change the definition of doprint to print something else out!
    ;; (ql:quickload :swank)
    ;; (ql:quickload :bordeaux-threads)
    
    (require :swank)
    (require :bordeaux-threads)
    
    (defparameter *counter* 0)
    
    (defun dostuff ()
      (format t "hello world ~a!~%" *counter*))
    
    (defun runner ()
      (bt:make-thread (lambda ()
                        (swank:create-server :port 4006)))
      (format t "we are past go!~%")
      (loop while t do
           (sleep 5)
           (dostuff)
           (incf *counter*)
           ))
    
    (runner)
    
    在我们的服务器上,我们运行它
    sbcl --load demo.lisp
    
    我们在开发机器上进行端口转发:
    ssh -L4006:127.0.0.1:4006 username@example.com
    
    这将安全地将 example.com 服务器上的端口 4006 转发到
    我们本地计算机的端口 4006(swanks 接受来自
    本地主机)。
    我们通过 M-x slime-connect 连接到跑酷, 输入
    端口 4006。
    我们可以编写新代码:
    (defun dostuff ()
      (format t "goodbye world ~a!~%" *counter*))
    (setf *counter* 0)
    
    并像往常一样使用 M-x slime-eval-region 对其进行评估例如。输出应该改变。
    CV Berry 的页面上有更多提示。
    热重载
    示例与 Quickutil .请参阅关于 lisp-journey 的注释。
    它必须在服务器上运行(一个简单的 fabfile 命令可以调用它
    通过 ssh)。事先,一个fab update已运行 git pull
    服务器,因此新代码存在但未运行。它连接到
    本地 swank 服务器,加载新代码,停止和启动应用程序
    排。
    持续集成、可执行文件的持续交付、Docker
    https://lispcookbook.github.io/cl-cookbook/testing.html#continuous-integration

    关于amazon-web-services - 部署 Common Lisp Web 应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48103501/

    相关文章:

    amazon-web-services - 使用 API Gateway POST 到 SQS 时无法确定要授权的服务/操作名称

    amazon-web-services - 如何在 `If` 的 `Resources` 中使用 `serverless.yml` 条件?

    java - Amazon EC2 上的 Neo4j - 无法从远程机器访问

    emacs - 通过 macports 设置 SLIME

    amazon-web-services - 我可以对 AWS IAM 用户隐藏一些托管区域吗?

    node.js - Elastic Beanstalk 环境变量在 SSH 中丢失

    ssh - EC2 : how to bundle an ssh private key into a private AMI?

    amazon linux 上的 mysql - MySQL 守护进程无法启动

    lisp - Common Lisp - 名称冲突 我认为包系统应该保护我免受

    lisp - 如何从 ads_name 类型获取 VLAX-OBJECT