The last step in developing a Web service (or App, as App and Service concepts are equated later) is to start a server to run your App, and in most tutorials, the choices here are usually uwsgi or gunicorn.

You’ll notice that once it’s up and running, it will take up one of your current terminal sessions and go into “long run mode”, like this.

1
2
3
4
[2020-05-23 22:54:57 +0800] [13077] [INFO] Starting gunicorn 20.0.4
[2020-05-23 22:54:57 +0800] [13077] [INFO] Listening at: unix:/tmp/***.socket (13077)
[2020-05-23 22:54:57 +0800] [13077] [INFO] Using worker: sync
[2020-05-23 22:54:57 +0800] [13084] [INFO] Booting worker with pid: 13084

The last line here has no cursor, so you can’t execute other commands. Unless you exit the process with Ctrl+C. If you close the terminal, close the SSH connection client (PuTTy, Xshell, etc.), the Web service process will exit immediately. This is because all the processes you run in the terminal have the current terminal session as their parent and are bound to standard input and output. Many people know that you can add & at the end of a command to turn the process into a background process, but such a background process does not change its parent process, so the process will still be absent after the terminal session ends. So how to solve this problem? I’ve provided three solutions below, and the level of recommendation increases one by one.

If you are lazy, go straight to the third option.

For the subsequent introduction of the three options, assume that the command you run the server is.

1
$ gunicorn -b :8888 -w 4 my_blog.wsgi

Please make changes accordingly, the tutorial is not meant to be copied and pasted 100% . If you are running in a virtual environment, just add the path to the virtual environment to the front.

1
$ /path/to/my/venv/bin/gunicorn -b :8888 -w 4 my_blog.wsgi

nohup

The nohup command makes a process unhung. (It redirects (by default) the standard output and standard error input to the nohup.txt file in the current directory, and changes the parent of the process to 1, i.e. process #1. This process will continue to run after the terminal exits, and is called a daemon. This process is called daemon. It is used as follows.

1
$ nohup gunicorn -b :8888 -w 4 my_blog.wsgi &

Note the addition of nohup and the & at the end

correction

nohup does not change the parent of the process to 1, it only sets SIGHUP to ignore, so that SIGHUP sent by bash is ignored when the session is exited, thus becoming an orphan process taken over by process 1.

supervisor

Using nohup can turn processes to run in the background, but it lacks an important feature: the ability to restart abnormally and boot from the server. You have to remember to start your server when you reboot it. That’s why more powerful and specialized process management tools were created. supervisor is a process manager written in Python that supports process restart exceptions, log storage, and provides a command line program to view and manage current processes. It is used as follows.

  1. Installation

    1
    
    $ pip install supervisor
    
  2. Generate configuration file

    1
    2
    
    $ echo_supervisord_conf > /etc/supervisord.conf
    $ mkdir /etc/supervisor.d
    

    Edit the /etc/supervisord.conf file and uncomment the last two lines of the file.

    1
    2
    
    [include]
    files = supervisor.d/*.ini
    
  3. Write the configuration for the application process /etc/supervisor.d/my_blog.ini, the lines starting with ; in the file are comment lines, uncomment them if you need the configuration to take effect.

     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
    
    [program:myblog]
    command=/path/to/my/venv/bin/gunicorn -b :8888 -w 4 my_blog.wsgi              ; 启动进程的命令
    ;process_name=%(program_name)s ; process_name expr (default %(program_name)s)
    ;numprocs=1                    ; number of processes copies to start (def 1)
    directory=/path/to/my_blog                ; 运行命令时先切换到此目录下
    ;umask=022                     ; umask for process (default None)
    ;priority=999                  ; the relative start priority (default 999)
    ;autostart=true                ; start at supervisord start (default: true)
    ;startsecs=1                   ; # of secs prog must stay up to be running (def. 1)
    ;startretries=3                ; max # of serial start failures when starting (default 3)
    ;autorestart=unexpected        ; when to restart if exited after running (def: unexpected)
    ;exitcodes=0,2                 ; 'expected' exit codes used with autorestart (default 0,2)
    ;stopsignal=QUIT               ; signal used to kill process (default TERM)
    ;stopwaitsecs=10               ; max num secs to wait b4 SIGKILL (default 10)
    ;stopasgroup=false             ; send stop signal to the UNIX process group (default false)
    ;killasgroup=false             ; SIGKILL the UNIX process group (def false)
    ;user=myuser                   ; 启动进程的用户,推荐不要用root用户,否则注释此行
    redirect_stderr=true          ; 重定向错误到输出 (默认false)
    stdout_logfile=/a/path        ; 标准输出的日志地址,会将所有print到终端的输出输出到指定的文件中
    ;stdout_logfile_maxbytes=1MB   ; max # logfile bytes b4 rotation (default 50MB)
    ;stdout_logfile_backups=10     ; # of stdout logfile backups (0 means none, default 10)
    ;stdout_capture_maxbytes=1MB   ; number of bytes in 'capturemode' (default 0)
    ;stdout_events_enabled=false   ; emit events on stdout writes (default false)
    ;stderr_logfile=/a/path        ; stderr log path, NONE for none; default AUTO
    ;stderr_logfile_maxbytes=1MB   ; max # logfile bytes b4 rotation (default 50MB)
    ;stderr_logfile_backups=10     ; # of stderr logfile backups (0 means none, default 10)
    ;stderr_capture_maxbytes=1MB   ; number of bytes in 'capturemode' (default 0)
    ;stderr_events_enabled=false   ; emit events on stderr writes (default false)
    ;environment=A="1",B="2"       ; process environment additions (def no adds)
    ;serverurl=AUTO                ; override serverurl computation (childutils)
    
  4. Start supervisord.

    1
    
    $ supervisord
    
  5. Viewing, terminating and starting processes

    1
    2
    3
    4
    
    $ supervisorctl status    # 查看进程状态
    $ supervisorctl stop my_blog    # 终止my_blog进程
    $ supervisorctl start my_blog    # 启动my_blog进程
    $ supervisorctl restart my_blog    # 重新启动my_blog进程
    

systemd

systemd is a process manager that comes with all newer Linux distributions. If you use it, you don’t have to bother with installing other libraries, it’s clean and fast, so this method is highly recommended. It’s also very simple to use, create the following file.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[Unit]
Description=My blog service

[Service]
Type=forking
ExecStart=gunicorn -b :8888 -w 4 my_blog.wsgi
KillMode=process
Restart=on-failure
RestartSec=3s

[Install]
WantedBy=multi-user.target

Save it to /etc/systemd/system/my_blog.service . Then execute the following command.

1
$ systemctl enable my_blog

This way your processes are automatically added to the bootstrap, and systemd can also view, start, and stop processes.

1
2
3
4
$ systemctl status my_blog    # 查看进程状态
$ systemctl stop my_blog    # 终止my_blog进程
$ systemctl start my_blog    # 启动my_blog进程
$ systemctl restart my_blog    # 重新启动my_blog进程

It’s that easy!