前言

最近刚好有将stable-difussion服务运行在服务器的需求,首先考虑的是amazon,但最便宜的那款一小时都要1美元多,偶然间发现腾讯云有活动高性能应用服务HAI_GPU云服务器_腾讯云,价格如下

这价格快比电费还便宜了,于是买了一张150元的现金券来体验下。

需求

我想使用该服务器跑一个被我魔改后的stable-difussion-webui,而它提供了一键部署Stable Difussion WebUI的选项。

如果能使用这装好的python环境,那我能省事很多。而且我也好奇这是怎么做的。以下是我的过程。

ssh连接

第一步肯定是得连接上去。

但在腾讯云 - 控制台并不能找到连接的按钮,还以为不提供,找文档找到了高性能应用服务 连接 Linux 算力-操作指南-文档中心-腾讯云

看到方式一,很熟悉啊,于是就去做了。

(写这篇文章的时候我又看了遍这个文档,发现还有方式二:通过 Terminal 连接算力),然后我才发现它提供的JupyterLab有项目的基础介绍。所以其实我下面写的这些很大一部分其实人都写得很明白了,╮(╯-╰)╭ )

找到python依赖的安装位置

一进去先ls,发现有个miniconda3,答案就呼之欲出了,请ChatGPT写了个简单的cheatsheet:

# 创建一个新的 Conda 环境
conda create --name myenv python=3.8

# 激活环境
conda activate myenv

# 停用环境
conda deactivate

# 列出所有 Conda 环境
conda env list

node运行装在conda的python

我需要用node去调用python,可以这样做

const workDir = "/root/my-stable-diffusion-webui";
let webUIProcess;

const url = process.env.URL;

const initWebGUI = _ => {
    return new Promise(resolve => {
        console.log('initWebGUI start');
        process.chdir(workDir);
        webUIProcess = spawn('/root/miniconda3/bin/python', ['launch.py', '--xformers', '--api','--nobrowser']);

        webUIProcess.stdout.on('data', (data) => {
            console.log(`stdout: ${data}`);

            return resolve();
        });
        webUIProcess.stderr.on('data', (data) => {
            const errorMessage = data.toString();
            console.error(`stderr: ${errorMessage}`);

            // Check for errors, assuming warnings do not contain 'error'
            if (/error/i.test(errorMessage)) {
                console.log('Kill process...', webUIProcess.pid);
                webUIProcess.kill();  // Kill the current process
                console.log('Process', webUIProcess.pid, ' killed.');
            }
        });
    });
}

值得一提的是,因为该服务器将依赖将依赖全安装在了base这个环境下,所以路径就是/root/miniconda3/bin/python

基础环境配置

  1. nvm安装
  2. redis安装: apt-get install redis

下载所需的底模和lora文件

  1. 因为我本地已经有相应的文件,于是我首先想的是上传。先是用的Cyberduck,但连接失败,检查了下端口,也有开放,不知道啥原因,于是放弃
  2. 尝试直接使用sftp,sftp username@your_server_ip ,确实可行,于是请请ChatGPT写了个简单的cheatsheet:
# 连接到服务器
sftp username@your_server_ip

# 切换到远程目录
cd /home/root/

# 使用 lpwd 查看本地工作目录,lcd 切换本地目录:

# 上传本地文件
put C:\path\to\example.txt

# 使用 get 命令从远程服务器下载文件到本地:
get remotefile.txt


# 退出 sftp 会话
exit

但发现速度不尽如人意,于是放弃

  1. 这时候就可以将思维逆转过来。既然不能上传,那我就下载啊。我需要的底模文件是在【SDXL】 Pixel Art | Base - v1.0 | Stable Diffusion Checkpoint | Civitai,当我直接复制下载链接,使用wget会报错,需要认证。查文档查到了Civitai’s Guide to Downloading via API - Civitai Education。最终能运行的命令类似于: curl -L -H "Content-Type: application/json" -H "Authorization: Bearer your_token" https://civitai.com/api/download/models/311399 -o SDXLPixelArtBase_v10.safetensors

  2. lora文件我则将上传到了s3上。它有个share with presigned URL,于是我想当然地使用,但发现还是报错。最后还是直接用的Object URL

  3. 后来发现文档中推荐的下载是这样写的:

    • 建议将基础模型文件转存至与HAI实例同地域的COS桶中,再在HAI实例中拉取COS文件,速度最快
    • 国内地域HAI实例可能存在网络不稳定情况,推荐启用“学术加速”;国外地域HAI实例下载模型、插件速度更快

    好一个“学术加速”

运行时报错

pm2使用

再再请ChatGPT写了个简单的cheatsheet:

# 安装
npm install pm2 -g

# 指定env文件 
pm2 start "node --env-file=.env app.js"

# 查看 PM2 中正在运行的进程
pm2 list

# 查看应用程序日志
pm2 logs my-app

# 停止应用程序:
pm2 stop my-app

# 重启应用程序:
pm2 restart my-app

# 删除应用程序:
pm2 delete my-app

# 检查日志
pm2 logs app

# 实时监控
pm2 monit

怎么开机自启的

服务跑着跑着爆显存了,感觉问题可能出在这个服务器运行了两个stable-difussion-webui,重启后发现还是有,那么它是怎么做到开机自启的呢?

我先后检查了以下几处(还是请ChatGPT帮的忙):

  1. systemctl
systemctl list-unit-files --type=service --state=enabled
systemctl list-units --type=service --state=running

这个命令将列出所有在系统启动时启用的服务。

  1. 检查 /etc/init.d/ 在一些较旧的系统或特定的服务中,可以通过检查 /etc/init.d/ 目录来查看初始化脚本:
ls /etc/init.d/

这些脚本通常用于传统的 init 系统,在启动时运行。

  1. cron
sudo crontab -l
sudo crontab -u <username> -l

cat /etc/crontab
cat /etc/cron.*/*

5. 检查 rc.local

sudo vim /etc/rc.local

以上检查都无果后,我想起了查看当前进程:

# 显示当前终端中的所有进程:
ps -ef

# 比较可疑的包括
root           1       0  0 12:35 ?        00:00:00 /usr/bin/dumb-init -- /usr/local/bin/application_init.s
root           7       1  0 12:35 pts/0    00:00:00 /bin/bash /usr/local/bin/application_init.sh
root          16       7  0 12:35 pts/0    00:00:00 /usr/bin/python3 /usr/bin/supervisord -c /etc/superviso
root          18      16  0 12:35 pts/0    00:00:00 /bin/bash /usr/local/bin/launch_jupyter.sh
root          19      16  0 12:35 pts/0    00:00:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
root          20      16  0 12:35 pts/0    00:00:00 /bin/bash /usr/local/bin/launch_stable_diffusion_webui.
root          21      18  0 12:35 pts/0    00:00:04 /root/miniconda3/bin/python3 /root/miniconda3/bin/jupyt
root          22      18  0 12:35 pts/0    00:00:00 tee -a /var/log/jupyter_service.log
root          23      20  1 12:35 pts/0    00:01:03 python3 -u launch.py --skip-prepare-environment --xform
root          24      20  0 12:35 pts/0    00:00:00 tee -a /var/log/sd_service.log
root        1225      68  0 12:50 pts/1    00:00:00 vim /etc/systemd/system/supervisord.service
root        1994      21  0 13:29 ?        00:00:00 /root/miniconda3/bin/python3 -m ipykernel_launcher -f /

于是找到了supervisord

supervisord的使用

  1. 请ChatGPT写了个简单的介绍:

supervisord 是一个流行的进程控制系统,用于管理和监控 Unix-like 操作系统上的进程。它提供了一种简单的方式来启动、停止、重启和监控进程。supervisord 是 supervisor 的守护进程,而 supervisor 是用于管理进程的客户端工具。

  1. whereis supervisord找到了配置文件的位置,是在/etc/supervisord.conf,
    [program:stable_diffusion_webui]
    command=/usr/local/bin/launch_stable_diffusion_webui.sh
    autostart=true
    autorestart=false
    numprocs=1
    redirect_stderr=true
    startretries=1
    startsecs=300
    stdout_logfile=/dev/stdout
    stdout_logfile_maxbytes=0
    stdout_events_enabled=true
    stderr_events_enabled=true
    stopasgroup=true
    killasgroup=true
    
  2. 那么刚才的问题得到了解答,开机自启的是supervisor.service,然后supervisor将stable_diffusion_webui给启动了
  3. /usr/local/bin/launch_stable_diffusion_webui.sh这个文件内容是
    #!/bin/bash
    source /mnt/application_env.txt
    cd /root/stable-diffusion-webui/ && python3 -u launch.py --skip-prepare-environment --xformers --listen --enable-insecure-extension-access --port=$STABLE_DIFFUSION_WEBUI_PORT 2>&1 | tee -a /var/log/sd_service.log`
    

开机自启

找到了它开机自启动服务的办法,那么就可以利用它来实现我想要的开机自启。

  1. 先写了个脚本/usr/local/bin/check_and_start_pm2.sh:
#!/bin/bash

# 进入程序目录
cd /path/to/SD-server/stable-difussion-server/

# 检查 pm2 中是否已有名为 "app" 的程序
if ! pm2 list | grep -q 'app'; then
    # 如果没有,启动 pm2 进程
    pm2 start "node --env-file=.env app.js" --name app
else
    echo "PM2 process 'app' is already running."
fi
  1. 给权限chmod +x /usr/local/bin/check_and_start_pm2.sh
  2. /etc/supervisord.conf
    [program:stable_diffusion_webui]
    command=/usr/local/bin/check_and_start_pm2.sh
    autostart=true
    autorestart=true
    numprocs=1
    redirect_stderr=true
    startretries=1
    startsecs=300
    stdout_logfile=/dev/stdout
    stdout_logfile_maxbytes=0
    stdout_events_enabled=true
    stderr_events_enabled=true
    stopasgroup=true
    killasgroup=true
    
  3. 重新加载 supervisord 配置
    supervisorctl reread
    supervisorctl update
    
  4. 这里我还踩到了自己挖的一个坑,/usr/local/bin/check_and_start_pm2.sh文件首行我是写#!/bin/bash,但我nvm是在zsh上装的,于是pm2在bash环境下就识别不到,再加上就行了。 export PATH=$PATH:/root/.nvm/versions/node/v20.16.0/bin

体验

之前用过好几家国内的云服务器,最大的感受是ui很糟糕,然后就是费劲折腾的网络。

但这次体验却着实不赖,因为有了“学术加速”,呵呵。

特别鸣谢

感谢ChatGPT! 它省去了不少我查文档的功夫。