Deploying Common Lisp Web Applications

I am wondering how to start deploying a Common Lisp web application written, for example, by Hunchentoot, Wookie, Woo or even Clack.

That is, suppose I'm writing an application containing some files, packages, etc. Usually, when I work locally, I just run a command in REPL, which starts the server, and then dials it using localhost:8000or something like that.

However, I'm a little puzzled by what happens to deploy the application to a production server such as AWS EC2. In what form should I deploy the Lisp code? Are there any different options? What happens if the server needs to be restarted or experiencing problems?

+11
source share
3 answers

Recently, I found out something by creating stand-alone executable files for web applications, and wrote about it in lisp-trip / web-dev (sections on delivery and deployment), as well as on the assembly part in Common Lisp Cookbook / scripting # for web applications .

I copy the interesting parts here, there is a little more on each resource. Editing is welcome, especially on these resources, thanks!

change July 2019 I added the page to the cookbook: https://lispcookbook.imtqy.com/cl-cookbook/web.html

: . , CL: https://github.com/CodyReichert/awesome-cl#deployment

https://github.com/CodyReichert/awesome-cl#interfaces-to-other-package-managers Homebrew Debian.

SBCL

() (. Buildapp Rowsell). SBCL, , :

(sb-ext:save-lisp-and-die #P"path/name-of-executable" :toplevel #'my-app:main-function :executable t)

sb-ext SBCL . SBCL ( ).

:executable t , . , Lisp, . , , .

Slime, :

.

SBCL.

, Quicklisp. :

  • , Quicklisp Lisp ( 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)"

ASDF

, , . 3.1, ASDF . make, .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

, , ros build, .

Roswell ros install my-app. .

Buildapp, - SBCL CCL, Common Lisp ".

(, pgloader), Debian: apt install buildapp, asdf: make Roswell.

-

-. , - :

$ ./my-web-app
Hunchentoot server is started.
Listening on localhost:9003.

, -, , VPS .

, , - . 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 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).

. .

. - .

Heroku

.

, ,

, .

GNU/Linux 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: sure_space: n

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.

, :

;; 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

4006 example.com 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. .

( fabfile ). fab update git pull , , . swank, , .

, , Docker

https://lispcookbook.imtqy.com/cl-cookbook/testing.html#continuous-integration

+16

lisp, fasl lisp :

(compile-file "app.lisp")

.fas, sbcl.

sbcl --noinform \
     --load app.fas \
     --eval "(defun main (argv) (declare (ignore argv)) (hunchentoot:start (make-instance 'hunchentoot:easy-acceptor :port 4242)))"
+4

, 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)

, , , swank. , , " ", .

The running system may be interrupted:

telnet 127.0.0.1 6700

and is triggered by something like:

nohup ccl -l initcclserver.lisp >& server.out &

In a previous version of the script, I found SBCL-specific details, so if you use it, you can change the script.

To accept terminating connections:

(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)))

To close the system:

(dolist (thread (sb-thread:list-all-threads))
  (unless (equal sb-thread:*current-thread* thread)
    (sb-thread:terminate-thread thread)))
(sleep 1)
(sb-ext:quit)

Hope this helps.

+3
source

All Articles