256-在Ubuntu服务器上配置Shiny应用
刘小泽写于2022.6.28 附上一个实用的英文链接:https://www.charlesbordet.com/en/guide-shiny-aws/#4-deploy-the-app-on-the-server
[TOC]
0 前言
之前看到腾讯云在搞618活动,推出了轻量服务器,价格比较合适,300多元就能使用10M带宽,这应该对网页服务器比较有帮助
优惠活动在:https://cloud.tencent.com/act/pro/computeinit618
它和云服务器有一些区别,不过对于我们非专业领域人士,应该也是性价比较高了:
除此以外,还有学生优惠:
- 阿里学生:https://developer.aliyun.com/plan/grow-up
- 腾讯学生:https://cloud.tencent.com/act/campus
1 开箱
推荐配置ubuntu,后面比较好操作,centos要麻烦一点
1.1 首先配置登陆用户名和密码
首次登陆修改只能使用默认的用户名ubuntu
,设置好以后会强制关机重启【因为我选择了ubuntu系统,所以这个用户名默认是这个,其他系统会有所不同】
1.2 然后登陆
ssh ubuntu@你的公网ip
查看系统配置
lsb_release -a #然后得到详细系统版本
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 18.04.4 LTS
Release: 18.04
Codename: bionic
1.3 新建用户
sudo -i adduser reed
# 然后输入用户名密码
# 其余选择一律enter跳过
1.4 提高新用户权限
# 在初始账号(ubuntu)下
sudo -i
# 然后
vi /etc/sudoers
# 在最后一行添加
reed ALL=(ALL:ALL) NOPASSWD: ALL
# 最后:x!强制保存退出
# 之后reed账户可以使用sudo -i直接切换成root了
如果你不想每次ssh登录都输入密码,可以参考: 174-小服务器,快记住我的密码吧
2 安装&配置
2.1 安装依赖库
sudo apt install --fix-missing libcurl4-openssl-dev libxml2-dev libgdal-dev libssl-dev libglu1-mesa-dev libmagick++-dev libudunits2-dev
2.2 安装R
参考:https://cloud.r-project.org/bin/linux/ubuntu/
sudo vim /etc/apt/sources.list
# 然后将下面👇这样加到最后
deb http://mirrors.tuna.tsinghua.edu.cn/CRAN/bin/linux/ubuntu xenial/
# 添加公匙
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9
sudo add-apt-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu bionic-cran40/'
sudo apt update
sudo apt install r-base
# 目前安装的是:R version 4.2.0 (2022-04-22)
2.3 安装Shiny server
参考:https://www.rstudio.com/products/shiny/download-server/ubuntu/
sudo su - \
-c "R -e \"install.packages('shiny', repos='https://mirrors.tuna.tsinghua.edu.cn/CRAN/')\""
sudo apt-get install gdebi-core
wget https://download3.rstudio.org/ubuntu-18.04/x86_64/shiny-server-1.5.18.987-amd64.deb
sudo gdebi shiny-server-1.5.18.987-amd64.deb
安装好以后,会自动新建一个shiny的用户
需要对shiny用户做一些简单的设置
# 先切换到root模式
sudo -i
# 然后设定密码
sudo passwd shiny # 同样可以参考之前的1.4部分,提高它的权限
# 添加组
sudo groupadd shiny-apps
sudo usermod -aG shiny-apps shiny
sudo usermod -aG shiny-apps reed # 也允许主用户可以访问
# change owner:将指定文件的拥有者改为指定的用户或组
sudo chown -R shiny:shiny-apps /srv/shiny-server
sudo chmod g+w /srv/shiny-server
sudo chmod g+s /srv/shiny-server
# g - the permissions that other users in the file's group have for it
# w - set user or group ID have right to write
# s - set user or group ID have right to execute
# 设置完成,查看【/srv/shiny-server这个目录就是存放各种shiny app的地方啦】
ls -l /srv/shiny-server
# total 0
# lrwxrwxrwx 1 shiny shiny-apps 38 Jun 28 13:17 index.html -> /opt/shiny-server/samples/welcome.html
# lrwxrwxrwx 1 shiny shiny-apps 37 Jun 28 13:17 sample-apps -> /opt/shiny-server/samples/sample-apps
# 之后我们都用shiny这个用户安装R包
su - shiny
2.3 安装Rstudio server
https://www.rstudio.com/products/rstudio/download-server/debian-ubuntu/
wget https://download2.rstudio.org/server/bionic/amd64/rstudio-server-2022.02.3-492-amd64.deb
sudo gdebi rstudio-server-2022.02.3-492-amd64.deb
2.4 安装R包
首先切换到shiny用户,然后再操作!
配置.Rprofile
# 命令
vi .Rprofile
# 然后输入下面👇
Sys.setenv(LANG="en_US.UTF-8")
options=(repo = c(CRAN = "https://mirrors.tuna.tsinghua.edu.cn/CRAN/"))
options(BioC_mirror="https://mirrors.tuna.tsinghua.edu.cn/bioconductor")
# 最后保存退出
配置.Renviron
提示:如果你写下面的这个路径,前提你得有这个目录,因此需要先新建目录
mkdir /home/shiny/R_Library
# 命令
vi .Renviron
# 然后输入下面👇
R_LIBS=/home/shiny/R_Library
# 最后保存退出
因为我们是要配置shiny网页,所以接下来我们需要以shiny
用户登陆
# 之后我们都用shiny这个用户安装R包
su - shiny
####################
# 下面👇均在R中进行 #
####################
R
# 配置镜像 (前面配置过.Rprofile的话,可以跳过)
local({
r <- getOption( "repos" );
r[ "CRAN" ] <- "https://mirrors.tuna.tsinghua.edu.cn/CRAN/";
options( repos = r )
BioC <- getOption( "BioC_mirror" );
BioC[ "BioC_mirror" ] <- "https://mirrors.ustc.edu.cn/bioc/";
options( BioC_mirror = BioC )
})
# 需要安装的R包 (我分别安装shiny需要的包和另外需要依赖的包)
shiny_pkgs <- c("colourpicker", "rvg", "tikzDevice", "openxlsx", "futile.logger", "ggplot2", "igraph", "ggraph", "shiny", "shinyBS", "shinyAce", "shinyWidgets", "shinydashboard", "stringr", "DT", "R.utils", "knitr", "rmarkdown", "shinyjs", "cowplot", "RColorBrewer", "waiter", "VennDiagram", "extrafont", "ggupset")
other_pkgs <- c("clusterProfiler", "dplyr", "DOSE", "europepmc", "fst", "ggplot2", "ggraph", "igraph", "magrittr", "openxlsx", "stringr", "stringi", "tidyr", "VennDiagram", "rlang", "AnnotationDbi", "BiocManager", "cowplot", "ComplexUpset", "data.table", "forcats", "fgsea", "futile.logger", "ggplotify", "ggsci", "ggupset", "ggrepel", "ggridges", "ggnewscale", "GOplot", "GOSemSim", "labeling", "msigdbr", "pheatmap", "tm", "treemap", "RColorBrewer", "rappdirs", "RCurl", "reshape2", "rio", "rrvgo", "scales", "stats", "testthat", "tibble", "wordcloud", "knitr", "rmarkdown","markdown","pacman")
orgdb_pkgs <- c("org.Hs.eg.db","org.Mm.eg.db","org.Rn.eg.db","org.Dm.eg.db",
"org.At.tair.db","org.Sc.sgd.db","org.Dr.eg.db","org.Ce.eg.db","org.Bt.eg.db",
"org.Ss.eg.db","org.Gg.eg.db","org.Mmu.eg.db","org.Cf.eg.db","org.EcK12.eg.db",
"org.Xl.eg.db","org.Ag.eg.db","org.Pt.eg.db","org.EcSakai.eg.db","org.Mxanthus.db")
probe_pkgs <- c("hcg110.db", "hgfocus.db", "hgu133a.db", "hgu133a2.db",
"hgu133b.db", "hgu133plus2.db", "hgu95a.db", "hgu95av2.db", "hgu95b.db",
"hgu95c.db", "hgu95d.db", "hgu95e.db", "hta20probeset.db", "hthgu133pluspm.db",
"hugene10sttranscriptcluster.db", "hugene20sttranscriptcluster.db",
"hugene21sttranscriptcluster.db", "u133x3p.db", "illuminaHumanv3.db",
"illuminaHumanv4.db", "illuminaHumanv1.db", "illuminaHumanv2.db"
)
all_pkgs <- c(shiny_pkgs,other_pkgs,orgdb_pkgs,probe_pkgs)
# 判断式安装
if (!requireNamespace("BiocManager", quietly = TRUE)){
install.packages("BiocManager")
}
CRANpackages <- available.packages() # 全部的CRAN包
lapply( all_pkgs,
function( p ){
if( !require( p, character.only = T ) ){
if( p %in% rownames( CRANpackages) ){
install.packages( p )
}else{
BiocManager::install( p, suppressUpdates = FALSE, ask = FALSE)
}
}
}
)
3 启动
3.1 添加端口,实现Shiny和Rstudio自由
虽然我们服务器已经安装成功并且激活了这两个功能,但是要从网页去访问,还得设置好服务器端口
在防火墙
中进行设置:shiny默认设置3838
端口,rstudio默认是8787
端口(这个默认端口后续可以修改,防止别人拿到你的IP就去8787…)
3.2 让shiny项目成真
首先把项目放在自己的家目录
# 方案1:项目在github上,可以直接git clone
cd /home/shiny
git clone 你的项目地址
# 方案2:本地上传
scp -r /本地目录/app shiny@你的IP地址:/home/shiny
# 然后在服务器上设置这个目录的可读权限
chmod 400 /home/shiny/app
然后在/srv/shiny-server
中加入项目的快捷方式
sudo ln -s /home/shiny/app /srv/shiny-server
# 同时更改项目属主是shiny
sudo chown -R shiny /srv/shiny-server/app
然后重启
sudo systemctl restart shiny-server.service
之后输入:http://你的IP:3838/你的shiny项目名称
如果放置了多个应用,那么就需要:
http://你的IP:3838/应用1
http://你的IP:3838/应用2
可能的报错
如果无法运行shiny app,去/var/log/shiny-server/
目录下看看log文件中的报错信息,一般是由于缺少包
解决后,立刻就能恢复运行
4 Nginx反向代理
它的用处就是:帮我们自动去掉那个烦人的
3838
,省得每次都输入
反向代理,用比较现实点的例子解释:
- 外卖小哥想给3838这个房间送物资
- 由于疫情防控,不让进去,只能交给门口的居委会工作人员
- 居委会人员帮外卖小哥把物资送到3838房间
服务器也是如此:
- 用户给3838端口发个指令,想获取其中的内容
- 服务器中的Nginx现在实行统一管理,它负责调度
- Nginx帮用户把指令送去3838
Nginx这个过程实现,利用的就是服务器中默认的HTTP网站端口:80
4.1 安装Nginx
sudo apt-get update
sudo apt-get install nginx
sudo systemctl start nginx
4.2 配置Nginx
cd /etc/nginx/sites-available
sudo vi shiny.conf
# 添加以下👇信息,然后保存退出
server {
# listen 80 means the Nginx server listens on the 80 port.
listen 80;
listen [::]:80;
# Replace it with your (sub)domain name.
server_name 域名1 域名2(如果你只有一个域名,就只写一个);
# The reverse proxy, keep this unchanged:
location / {
proxy_pass http://localhost:3838;
proxy_redirect http://localhost:3838/ $scheme://$host/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 20d;
proxy_buffering off;
}
}
# 然后设置软连接
cd ../sites-enabled
sudo ln -s ../sites-available/shiny.conf .
# 然后切换root用户
sudo -i
# 修改/etc/nginx/nginx.conf
vi /etc/nginx/nginx.conf
# 在其中的http区域中添加下面👇信息
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
4.3 最后激活
sudo nginx -t
# 成功会提示:
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
sudo systemctl restart nginx
5 优化
5.1 删掉原来的shiny app
默认情况下,直接输入IP地址会显示自带的shiny app模板:
只要测试shiny可以用了,就可以删掉它了:
sudo rm -rf /srv/shiny-server/sample-apps
5.2 如果只有一个shiny app
然后如果目前只有一个shiny app,不希望用www.main.com/myapp
这样的方式,而是直接一点:www.myapp.com
可以修改shiny-server的配置文件
sudo vi /etc/shiny-server/shiny-server.conf
# 下面👇是全部内容
# Instruct Shiny Server to run applications as the user "shiny"
run_as shiny;
preserve_logs true; # 保存报错日志(shiny默认犯了错秒删。。。)
sanitize_errors false;
# Define a server that listens on port 3838
server {
listen 3838;
# Define a location at the base URL
location / {
# 默认存放shiny app的目录
site_dir /srv/shiny-server;
# 默认存放shiny 日志文件的目录
log_dir /var/log/shiny-server;
# 我们之前将index.html删掉了,我们又不希望别人看到我们shiny app的布局,可以隐藏首页,直接进入shiny
directory_index off;
}
## 我们每个单独的应用可以设置自己的存放目录(这里设置主页就显示app)
location / {
app_dir /home/shiny/app; #location to the app
log_dir /var/log/shiny-server/app; # log
}
## 如果还有其他app
location / {
app_dir /home/shiny/app2; #location to the app
log_dir /var/log/shiny-server/app2; # log
}
}
# 最后保存退出
# 然后将/srv/shiny-server 属主切换回shiny
sudo chown -R shiny:shiny-apps /srv/shiny-server
重启:sudo systemctl restart shiny-server.service
到此为止,你应该可以直接输入IP地址来访问到你的应用 下面👇,我们继续优化这个网址
5.3 给IP起个简单的名字吧 =》 解析域名
为了方便宣传,我们一般不会直接给别人一串数字的IP地址,而且给它起个名字,这就是设置域名
有一家比较知名的域名公司:https://www.godaddy.com/zh-sg
他们还可以提供域名经纪服务、域名价值评估
选好心仪的域名后,一般等待1-2天实名认证,通过了就可以设置DNS record
首页DNS
=》 管理区域
=》DNS记录
然后修改类型A的数据为你的IP地址
最后,重新进入**【4.2 配置Nginx】** ,将server_name
修改为你的域名(而不是IP),再进行**【4.3 最后激活】**
5.4 【可选】备案
如果域名解析到的服务器在中国香港、中国台湾、中国澳门、或非中国的国家和地区,则不需要备案
每个国内的云平台都有自己的备案方式,都是相当麻烦,这里以腾讯云为例:
需要实名上传身份证+视频核验,填写网站用途+中文描述,需要承诺书请手写正楷签字(接受签名章,请勿使用连笔签)=》然后当地网管打电话审核=》国家网管局审核 =》 审核通过,必须在主页下方放上备案号
5.5 给域名加个锁
”锁“就是HTTPS
HTTPS就是防止在HTTP的传输过程中,因信息未加密而被别有用心的人拦截篡改
HTTPS和HTTP的区别有:
https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443
首先利用Let’s Encrypt获取免费证书
sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository universe
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install certbot python-certbot-nginx
# 获取证书
sudo certbot certonly --nginx -d 自己的域名
# 需要注意:这个域名必须已经被解析
然后这个证书就被放在:/etc/letsencrypt/live/你的域名/
这个目录中
然后安装证书
HTTP默认监听80端口,HTTPS则是443端口,这里我们将Nginx增加监听443
curl https://ssl-config.mozilla.org/ffdhe2048.txt > /home/shiny/dhparam.pem
sudo vi /etc/nginx/sites-available/shiny.conf
# 最终版本(注意将”你的域名“这里替换掉)
server {
listen 80;
listen [::]:80;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name 这里放自己的域名;
# The reverse proxy, keep this unchanged:
location / {
proxy_pass http://localhost:3838;
proxy_redirect http://localhost:3838/ $scheme://$host/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 20d;
proxy_buffering off;
}
# certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
ssl_certificate /etc/letsencrypt/live/你的域名/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/你的域名/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
ssl_session_tickets off;
# curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam.pem
ssl_dhparam /home/shiny/dhparam.pem;
# intermediate configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# HSTS (ngx_http_headers_module is required) (63072000 seconds)
add_header Strict-Transport-Security "max-age=63072000" always;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
# verify chain of trust of OCSP response using Root CA and Intermediate certs
ssl_trusted_certificate /etc/letsencrypt/live/你的域名/chain.pem;
}
接着检查
sudo nginx -t
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
最后重启
sudo systemctl restart nginx
sudo systemctl restart shiny-server.service
添加自动更新
Certbot会自动检测证书是否过期,并且提前更新新的证书
sudo certbot renew --dry-run
感想
期间换了几个服务器折腾了几次,包括:腾讯云(广州节点)、AWS(美国节点)、阿里云(主要使用它的香港节点),以及不知名的一些香港云服务器,感觉还是得大品牌的靠谱,虽然价格高一点,但用着省心;然后就是要考虑你的需求,如果是想兼顾海外和国内用户,最好是香港的服务器,还不用备案,当然它的价格要比内地IP贵一点。另外经过这一次使用DoDaddy服务,感觉还不错,它自己的DNS配置很快并且比较稳定。
最后,还是那句~生信在于折腾!
zzz 补充
z1: 卸载R
sudo apt-get autoremove r-base-core
z2: 服务器总是自动断线
sudo vim /etc/ssh/sshd_config
# 找到下面这两行
#ClientAliveInterval 0
#ClientAliveCountMax 3
# 然后去掉注释,并且修改
ClientAliveInterval 30 #意思是:服务端每隔多少秒向客户端发送一个信号
ClientAliveCountMax 86400 #意思是:客户端多少次没有相应,服务器自动断掉连接
# 最后重启ssh
service sshd restart
z3: 如果安装出现依赖项缺失,则在命令行安装缺失项
sudo apt-get -y install libcurl4-gnutls-dev libxml2-dev libssl-dev subversion scons libfuse-dev gcc
sudo apt-get -y install libmariadb-client-lgpl-dev libcurl4-openssl-dev libudunits2-dev
z4: 设置了端口,Rstudio server还是登录不上
可能是防火墙拦截了
# 首先安装
sudo apt-get update
sudo apt-get install iptables
sudo apt-get install iptables-persistent
# 然后运行
# 开放端口
iptables -I INPUT -p tcp --dport 8787 -j ACCEPT
iptables -I OUTPUT -p udp --dport 8787 -j ACCEPT
# 保存防火墙规则, /etc/iptables/rules.v4
sudo netfilter-persistent save
z5: 关于Rstudio-server 配置文件
主要有两个:
/etc/rstudio/rserver.conf
/etc/rstudio/rsession.conf
其中:
###############################
# 关于 rserver.conf 常用的配置
###############################
# 监听端口,默认为 8787
www-port=8787
# 地址,默认为0.0.0.0,会接收任何IP连接
www-address=0.0.0.0
# 指定库文件路径,一般不需要改动,可以用于添加系统标准库,第三方外部库文件依赖
rsession-ld-library-path=/opt/local/lib:/opt/local/someapp/lib
# 指定 R 路径
rsession-which-r=/usr/local/bin/R
# 设置用户组(方便增加多用户)
auth-required-user-group=rstudio-group
###############################
# 关于 rsession.conf 常用的配置
###############################
# 会话超时,默认 2 小时 Rstudio 没有操作就会断开服务器连接,如果有任务运行不会断开
session-timeout-minutes=120
# 设置R包路径,默认在~/R/packages
r-libs-user=~/R/packages
# 镜像设置
r-cran-repos=https://mirrors.tuna.tsinghua.edu.cn/CRAN/
z6: Rstudio server常用操作
- 启动:
sudo rstudio-server start
- 关闭:
sudo rstudio-server stop
- 重启:
sudo rstudio-server restart
- 查看状态:
sudo rstudio-server status
z7: Shiny server 常用操作
- 启动:
sudo systemctl start shiny-server
sudo systemctl enable shiny-server
- 重启:
sudo systemctl restart shiny-server.service
- 查看日志:
cat /var/log/shiny-server.log
z8: 如果发现新建的用户使用tab补全或者上下建失效
ubuntu对于新用户,默认使用shell为/bin/sh
,这是不支持tab的
# 首先在当前用户下查看shell
echo $SHELL
# /bin/sh
# 然后用root用户更改
usermod -s /bin/bash 用户名
# 退出然后重新进入
echo $SHELL
/bin/bash
z9: 不同服务器之间快速拷贝数据
rsync -av 用户名1@第一台服务器IP:/home/data 用户名1@第二台服务器IP:/home
z10: Rstudio server添加新用户
之前我们在z5中设置了Rstudio-server 配置文件,其中指定了auth-required-user-group=rstudio-group
这里只需要将新用户放进这个组中即可
sudo groupadd rstudio-group
sudo usermod -a -G rstudio-group shiny
# 查看一下
id shiny
# uid=999(shiny) gid=999(shiny) groups=999(shiny),1002(shiny-apps),1003(rstudio-group)
z11: 安装某个R包报错提示:ERROR: no packages specified
可能当前你的用户不是shiny用户,切换回去就好啦
z12: 如果你发现配置文件写的都是对的,但是网页不显示或者显示的不是shiny app
查看配置文件的属主和属组是不是shiny,因为有时可能切换到root直接去运行命令,导致配置文件的归属直接改成了root,默认的shiny用户无法访问到这些配置文件
如果是这种情况,直接切换回来就好
# 比如不小心使用了root操作,改变了它的所属
ls -lh ../sites-available/shiny.conf
# -rw-r--r-- 1 root root 1.7K Jun 30 21:23 ../sites-available/shiny.conf
# 更改回来即可(如果是更改整个目录,就加 -R )
sudo chown shiny:shiny-apps ../sites-available/shiny.conf
ls -lh ../sites-available/shiny.conf
# -rw-r--r-- 1 shiny shiny-apps 1.7K Jun 30 21:23 ../sites-available/shiny.conf
z13 :如果shiny网页上传文件报错
参考:https://stackoverflow.com/questions/49666900/413-request-error-while-using-shiny https://stackoverflow.com/questions/28476643/default-nginx-client-max-body-size
默认只能上传不超过1M文件,我们可以修改这个大小限制。
首先在R中设置限制是30M:
options(shiny.maxRequestSize=30*1024^2)
然后再Nginx中修改:
sudo vi /etc/nginx/nginx.conf
# 然后在http那里写入
client_max_body_size 30M;
# 保存退出