docker / 群晖 · 2023年6月28日 1

群晖 Web Station 网络门户 Container Manager(Docker) 重启后无法使用问题

群晖的docker套件从7x以后改名为 Container Manager 实际上还是原来的docker套件

群晖把 Web Station(下文使用web代替)和Container Manager (下文使用docker代替)做了联动,可以通过在docker中配置 【通过 Web Station 设置网页门户】 功能 把docker中的服务直接对公开给外网使用

配置见下图

选择对应的docker容器的端口

门户类型推荐使用 【基于名称】这样就可以直己定义一个三级域名(群晖自带的ddns定义的是二级域名,这个二级域名的证书包含了任意的三级证书)区分不同的服务了

配置好以后 通过配置的主机名就可以直接访问了。

接下来问题就来了,再重启docker 或者 重启系统的时候,你会发现上面配置的映射全都404了,

打开docker套件查看,发现docker中的服务是正常的,

再打开web套件查看,发现配置的门户全都显示服务已禁用

如下图所示

机器每次重启或者docker套件重启,都会导致这个【网络门户】显示【服务已禁用】,我联系过售后技术,也提供了远程链接支持,然并卵,售后技术并没有解决问题

我也尝试翻群晖文档,但是并没有找到相关的api,所以尝试自己在机器上尝试碰运气 看看能不能找到,

最终我在群晖的命令行工具包中找到了一个cli叫 synowebapi 经过不断地尝试,该api是一个万能工具,可以操作群晖的所有功能,基于此工具我写了个自动恢复的脚本(脚本在最后面)

使用该脚本配置了一个定时任务 每分钟触发一次 自动恢复由于docker重启或者机器重启导致的【网络门户】404的问题,配置如下

该脚本使用了如下功能

  1. 列出系统套件运行状态
  2. 获取web套件service、portal 列表
  3. 获取docker套件容器列表
  4. 停止、运行 docker容器

该脚本会自动检测套件运行状态,并且自动检查对应状态,发现无法访问的【网络门户】则自动重启对应的docker容器

#!/bin/bash
DISABLE_COLOR=true

BLACK="\e[30m"
RED="\e[31m"
GREEN="\e[32m"
YELLOW="\e[33m"
BLUE="\e[34m"
MAGENTA="\e[35m"
CYAN="\e[36m"
LIGHT_GRAY="\e[37m"
DARK_GRAY="\e[90m"
LIGHT_RED="\e[91m"
LIGHT_GREEN="\e[92m"
LIGHT_YELLOW="\e[93m"
LIGHT_BLUE="\e[94m"
LIGHT_MAGENTA="\e[95m"
LIGHT_CYAN="\e[96m"
WHITE="\e[97m"
END="\e[0m"

if [ $DISABLE_COLOR == true ]; then
  BLACK=""
  RED=""
  GREEN=""
  YELLOW=""
  BLUE=""
  MAGENTA=""
  CYAN=""
  LIGHT_GRAY=""
  DARK_GRAY=""
  LIGHT_RED=""
  LIGHT_GREEN=""
  LIGHT_YELLOW=""
  LIGHT_BLUE=""
  LIGHT_MAGENTA=""
  LIGHT_CYAN=""
  WHITE=""
  END=""
fi

restartDockerContainer() {
  stop=$(synowebapi --exec api=SYNO.Docker.Container method=stop version=1 name=$1 2>/dev/null)
  status=$(jq '.success' <<<$stop)
  if [ $status == false ]; then
    echo -e $RED"stop container: $1 error: $stop"$END
  fi

  start=$(synowebapi --exec api=SYNO.Docker.Container method=start version=1 name=$1 2>/dev/null)
  status=$(jq '.success' <<<$start)
  if [ $status == false ]; then
    echo -e $RED"start container: $1 error: $stop"$END
  fi

  echo "restart container finished: $1"
}

checkServiceIsOffline() {
  echo -e "checking container: $GREEN$1$END url: $GREEN$2$END"
  result=$(curl -I $2 2>/dev/null | grep 200)
  if [ ${#result} -eq 0 ]; then
    echo -e "conatiner need restarted: $RED$1$END"
    return 1
  else
    echo -e "container is$BLUE online$END"
    return 0
  fi
}

pakcageList=$(synowebapi --exec api=SYNO.Core.Package method=list version=2 additional='["status"]' 2>/dev/null | jq -c '.data.packages | map(select(.id == "WebStation" or .id == "ContainerManager"))')

isRunning=$(jq -r 'map(select(.id == "WebStation") | .additional.status) | .[]' <<<$pakcageList)

if [ $isRunning != "running" ]; then
  echo -e "Web Station$RED not running$END exit"
  exit 0
fi
echo "Web Station is running go next step"

isRunning=$(jq -r 'map(select(.id == "ContainerManager") | .additional.status) | .[]' <<<$pakcageList)

if [ $isRunning != "running" ]; then
  echo -e "Container Manager$RED not running$END exit"
  exit 0
fi
echo "Container Manager is running go next step"

webStationPortal=$(synowebapi --exec api=SYNO.WebStation.WebService.Portal method=list version=1 2>/dev/null | jq -c '.data.portals | map({host: .fqdn, http: .http_port[0], https: .https_port[0], service: .service, display_name: .shortcut.display_name})')
webStationPortalCount=$(jq 'length' <<<$webStationPortal)

if [ $webStationPortalCount -eq 0 ]; then
  echo -e $RED"not found Web Station portal"$END
  exit 0
fi

echo -e "found Web Station portal count: $RED$webStationPortalCount"$END

webStationService=$(synowebapi --exec api=SYNO.WebStation.WebService.Service method=list version=1 2>/dev/null | jq -c '.data.services | map(select(.category == "Docker"))')
webStationServiceCount=$(jq 'length' <<<$webStationService)

if [ $webStationServiceCount -eq 0 ]; then
  echo -e $RED"not found Web Station services exit"$END
  exit 0
fi

echo -e "found Web Station services count: $RED$webStationServiceCount"$END

dockerContainer=$(synowebapi --exec api=SYNO.Docker.Container method=list version=1 limit=-1 offset=0 type=all 2>/dev/null | jq -c '.data.containers | map(. | select(.status=="running" and .services[0] != null) | {name: .name, status: .status, id: .id, service: .services[0]})')
dockerContainerCount=$(jq 'length' <<<$dockerContainer)
echo -e "found docker container count: $RED$dockerContainerCount"$END

for idx in $(seq 0 $(($webStationServiceCount - 1))); do
  echo "-------------------[$((idx + 1))]---------------------"
  service=$(jq -c ".[$idx]" <<<$webStationService)
  serviceName=$(jq -r '.display_name' <<<$service)
  serviceId=$(jq '.service' <<<$service)
  serviceEnable=$(jq '.enable' <<<$service)

  portal=$(jq -c "map(select(.service == $serviceId)) | .[]" <<<$webStationPortal)
  portalHost=$(jq -r '.host' <<<$portal)

  echo -e "service: $GREEN$serviceName$END enable: $BLUE$serviceEnable$END"

  if [ $serviceEnable == true ]; then
    echo -e "reverse proxy: $GREEN$portalHost$END ➡️  $GREEN$serviceName$END is$BLUE running$END skip"
    continue
  fi

  if [ ${#portal} -ne 0 ]; then
    container=$(jq -c "map(select(.service.service == $serviceId)) | .[]" <<<$dockerContainer)

    if [ ${#container} -eq 0 ]; then
      echo "service: $GREEN$serviceName$END container$RED is not found skip$END"
      continue
    fi

    containerName=$(jq -r '.name' <<<$container)
    containerStatus=$(jq -r '.status' <<<$container)

    echo -e "service container: $GREEN$containerName$END status: $GREEN$containerStatus$END"

    if [ $containerStatus != 'running' ]; then
      echo -e "contianer$GREEN $containerName$END is$RED not running$END skip"
      continue
    fi

    httpPort=$(jq '.http' <<<$portal)
    httpsPort=$(jq '.https' <<<$portal)
    proxyTarget=$(jq -r '.proxy_target' <<<$service)
    httpOffline=0
    httpsOffline=0

    if [ ${#httpPort} -ne 0 ]; then
      echo -e "reverse proxy found$GREEN http://$portalHost:$httpsPort $END➡️ $GREEN $proxyTarget $END"
      checkServiceIsOffline $containerName "http://$portalHost:$httpPort"
      httpOffline=$?
    fi

    if [ ${#httpsPort} -ne 0 ]; then
      echo -e "reverse proxy found$GREEN https://$portalHost:$httpsPort $END➡️ $GREEN $proxyTarget $END"
      checkServiceIsOffline $containerName "https://$portalHost:$httpsPort"
      httpsOffline=$?
    fi

    if [[ $httpOffline -eq 1 || $httpsOffline -eq 1 ]]; then
      restartDockerContainer $containerName
    else
      echo "service: $RED$serviceName is$BLUE online$END proccess next"
    fi

  else
    echo -e "service:$RED $serviceName is not found $END"
  fi

done

echo "script finished"