问题描述

因为我的树莓派 3B+ 是 arm 架构,因此无法直接安装 shiny server,只好求助于 docker,直接拉取一般的 shiny-server 镜像是不行的,找来找去,最后发现 hvalev/shiny-server-arm 镜像是可以的,部署后自带的那个 shinyApp 示例是可以运行的。

我最近整了个项目,汇总 n 个人的 .xlsx 文件后自动生成 .docx 文档,代码放在 code1.R 里;但是同事不喜欢看代码,还是喜欢有 UI 界面那种点点点模式,所以我又利用 shinydashboard 做了一个 shinyApp,代码放在 code2.R 里。code1.R 里的代码我无论是直接在树莓派里运行还是在 root 权限下的容器里,命令行模式下都是可以运行的,code2.R 我在本地的 Ubuntu 20.04 上是正常运行的,可是放在 shiny-server 下,运行 docker 容器后,浏览器里总是出问题,点击编织文档按钮就提示 “Disconnected from the server. Reload”。检查了下 shinyApp 运行日志,内容如下:

Listening on http://127.0.0.1:33933
sh: 1: /usr/local/shiny-server/ext/pandoc/pandoc: Exec format error
Warning in system(paste(shQuote(path), "--version"), intern = TRUE) :
  运行命令''/usr/local/shiny-server/ext/pandoc/pandoc' --version'的状态是2
Warning: Error in [[: 下标出界
  [No stack trace available]

我的代码

library(shiny)
library(shinydashboard)

ui = dashboardPage(
  dashboardHeader(
    title = "文档自动化", 
    titleWidth = 260
  ),
  
  dashboardSidebar(
    width = 260,
    collapsed = FALSE,
    sidebarMenu(
      menuItem(
        "开始使用", 
        tabName = "start", 
        icon = icon("rocket")
      )
    )
  ),
  dashboardBody(
    tabItems(
      tabItem(
        tabName = "start",
        fluidPage(
          sidebarLayout(
            sidebarPanel(
              dateRangeInput("period", "定义工作期间:", weekstart = 1)
            ),
            mainPanel(
              fluidPage(
                actionButton(
                  "button2", "编织文档", width = "300px",  
                  icon = icon("feather"),
                  class = "btn-primary btn-md"
                ),
                tableOutput("gendocx")
              )
            )
          )
        )
      )
    )
  )
)

server = function(input, output, session) {
  observeEvent(input$button2,{
    rmarkdown::render(input = "report.Rmd")
    output$gendocx <- renderTable(
      {
        if (file.exists(report.docx)) {
          print("文档已编织完成,可以下载了。")
        } else {
          print("请检查参数设置,正确导入数据。")
        }
      },
      colnames = FALSE
    )
  })
}

shinyApp(ui = ui, server = server)

其中 report.Rmd 就是 R Markdown 自动的生成 docx 文档的示例文档。

我的树莓派系统环境

R version 4.1.2 (2021-11-01)
Platform: aarch64-unknown-linux-gnu (64-bit)
Running under: Ubuntu 22.04.1 LTS

Locale:
  LC_CTYPE=zh_CN.UTF-8       LC_NUMERIC=C              
  LC_TIME=zh_CN.UTF-8        LC_COLLATE=zh_CN.UTF-8    
  LC_MONETARY=zh_CN.UTF-8    LC_MESSAGES=zh_CN.UTF-8   
  LC_PAPER=zh_CN.UTF-8       LC_NAME=C                 
  LC_ADDRESS=C               LC_TELEPHONE=C            
  LC_MEASUREMENT=zh_CN.UTF-8 LC_IDENTIFICATION=C       

Package version:
  base64enc_0.1.3 bslib_0.4.0     cachem_1.0.6    digest_0.6.29  
  evaluate_0.16   fastmap_1.1.0   fs_1.5.2        glue_1.6.2     
  graphics_4.1.2  grDevices_4.1.2 highr_0.9       htmltools_0.5.3
  jquerylib_0.1.4 jsonlite_1.8.0  knitr_1.40      magrittr_2.0.3 
  memoise_2.0.1   methods_4.1.2   R6_2.5.1        rappdirs_0.3.3 
  rlang_1.0.4     rmarkdown_2.16  sass_0.4.2      stats_4.1.2    
  stringi_1.7.8   stringr_1.4.1   tinytex_0.41    tools_4.1.2    
  utils_4.1.2     xfun_0.32       yaml_2.3.5     

Pandoc version: 2.9.2.1

docker 拉取的 shiny-server-arm 系统环境

sudo docker exec -it 3f0aef9cdda0 bash
R version 4.2.1 (2022-06-23)
Platform: aarch64-unknown-linux-gnu (64-bit)
Running under: Debian GNU/Linux 10 (buster)

Locale:
  LC_CTYPE=zh_CN.UTF-8    LC_NUMERIC=C            LC_TIME=zh_CN.UTF-8    
  LC_COLLATE=zh_CN.UTF-8  LC_MONETARY=zh_CN.UTF-8 LC_MESSAGES=C          
  LC_PAPER=C              LC_NAME=C               LC_ADDRESS=C           
  LC_TELEPHONE=C          LC_MEASUREMENT=C        LC_IDENTIFICATION=C    

Package version:
  base64enc_0.1.3 bslib_0.3.1     digest_0.6.29   evaluate_0.16  
  fastmap_1.1.0   fs_1.5.2        glue_1.6.2      graphics_4.2.1 
  grDevices_4.2.1 highr_0.9       htmltools_0.5.2 jquerylib_0.1.4
  jsonlite_1.8.0  knitr_1.40      magrittr_2.0.3  methods_4.2.1  
  R6_2.5.1        rappdirs_0.3.3  rlang_1.0.2     rmarkdown_2.16 
  sass_0.4.1      stats_4.2.1     stringi_1.7.8   stringr_1.4.1  
  tinytex_0.41    tools_4.2.1     utils_4.2.1     xfun_0.32      
  yaml_2.3.5     

Pandoc version: 2.2.1

问题已解决。

切换到 root 用户的容器里安装 pandoc

我之前已经安装,没有安装的先安装,举个例子:

sudo docker exec -it 3f0aef9cdda0 bash
apt install pandoc

注:这里的 3f0aef9cdda0 是容器 ID,需要换成你自己的。

设置环境变量

shinyApp 里加入如下代码:

Sys.setlocale(category = "LC_ALL", locale = "zh_CN.UTF-8")
Sys.setenv(RSTUDIO_PANDOC = "/usr/bin")
options(bitmapType = 'cairo')

其中第二行代码是解决本问题的关键,告诉 R 你的计算机的 pandoc 在哪里,本地没有报错是因为我用的是 RStudio,它自带了 pandoc。

报错问题解决了,但是 pandoc 2.2.1 版本有点低,生成的 docx 文档格式有点问题,解决方法是把 pandoc 升级到 2.9.2.1,具体操作如下:

先切换到 root 用户的容器里

sudo docker exec -it 3f0aef9cdda0 bash

修改软件仓库源

nano /etc/apt/sources.list

buster 改成 bullseye

仅仅升级 pandoc

apt update
apt-get --only-upgrade install pandoc

把更改提交到 docker 容器

先用 exit 退出 root 用户,在树莓派里

sudo docker commit -m "added some dependencies" 3f0aef9cdda0 shiny_with_deps