这是默认的:
目前是有以下几点想要修改:
1、在顶部增加一个公告栏,方便给学生通知各种信息。
2、在目录处增加共享目录的介绍(新挂载了/datasets
等目录,但是默认欢迎界面中不显示这些,所以就想显式地介绍下)
目前情况是,实例开机之后,会自动运行/etc/autodl-motd
文件,然后Autodl
私有云管理后台提供了两个钩子,一个是“容器创建完成后执行脚本
”,一个是“容器每次开机后执行脚本
”。目前的思路是,创建一个新的欢迎脚本,在容器第一次创建之后,替换原有的autodl-motd
文件,并增加公告栏,通过远程文件进行更新,每次连接SSH
的时候,都会自动获取远端公告信息,如果有更新就显示公告栏,如果没有公告就不显示公告栏。
下面是效果,首先是无公告,增加数据集目录介绍:
然后是远端更新公告之后:
操作步骤
由于代码较多,所以建议将脚本存放在远端存储,比如又拍云。我们新建一个文件vpms.sh
,链接地址是:https://xxx.xxxx.com/xxxx/vpms.sh
,填入:
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
#!/usr/bin/env bash set -euo pipefail # 备份原厂 motd cp -a /etc/autodl-motd "/etc/autodl-motd.bak.$(date +%s)" 2>/dev/null || true # 覆盖为定制 motd cat >/etc/autodl-motd <<'MOTD' #!/bin/bash if [[ $- == *i* ]]; then printf "+-----------------------------------------------AutoDL-----------------------------------------------------+\n" # ===== CJK-safe ASCII 公告:边框黄,正文红,meta 黄,支持 \n 换行 ===== draw_notice_box() { local body_raw="$1" mtime="$2" exp="$3" # 展开 \n local body="$(printf '%b' "$body_raw")" local cols boxw cols=${COLUMNS:-$(tput cols 2>/dev/null || echo 100)} [ "$cols" -lt 60 ] && cols=60 boxw=$(( cols - 4 )) [ "$boxw" -gt 100 ] && boxw=100 # 颜色 local Y="\033[1;33m" R="\033[1;31m" N="\033[0m" # 顶部横线 + 居中标题 local title=" 公告 " local line; line=$(printf '%*s' "$boxw" '' | tr ' ' '-') local padL=$(( (boxw - ${#title}) / 2 )); [ $padL -lt 1 ] && padL=1 local padR=$(( boxw - padL - ${#title} )) printf "${Y}+%s+${N}\n" "$line" tput sc printf "${Y}|%*s%s%*s${N}" "$padL" "" "$title" "$padR" "" tput rc; tput cuf $((boxw+1)); printf "${Y}|${N}\n" # 正文(红色) local L while IFS= read -r L; do tput sc printf "${Y}| ${R}%s${N}" "$L" tput rc; tput cuf $((boxw+1)); printf "${Y}|${N}\n" done <<< "$body" # 空一行(正文和时间之间) if [ -n "$mtime" ] || [ -n "$exp" ]; then tput sc; printf "${Y}|${N}"; tput rc; tput cuf $((boxw+1)); printf "${Y}|${N}\n" fi # meta 信息(黄色) if [ -n "$mtime" ] || [ -n "$exp" ]; then local meta="" [ -n "$mtime" ] && meta="最后更新:$mtime" [ -n "$exp" ] && meta="${meta:+$meta | }有效期:$exp" tput sc printf "${Y}| ${Y}%s${N}" "$meta" tput rc; tput cuf $((boxw+1)); printf "${Y}|${N}\n" fi # 底部横线 printf "${Y}+%s+${N}\n\n" "$line" } # ===== 公告读取(/etc/autodl-notice;首行可为 EXPIRES=...;正文空则不显示)===== NOTICE="/etc/autodl-notice" if [ -s "$NOTICE" ]; then # 从文件中读取 meta 行 mt="" first=$(head -n1 "$NOTICE" 2>/dev/null || true) exp=$(echo "$first" | sed -n 's/^EXPIRES=\(.*\)$/\1/p') mt_epoch=$(grep -m1 '^MTIME_EPOCH=' "$NOTICE" | cut -d= -f2 || true) body="$(grep -vE '^(EXPIRES=|MTIME_EPOCH=)' "$NOTICE")" body_trimmed="$(echo "$body" | sed '/^[[:space:]]*$/d')" show=1 if [ -n "$exp" ]; then now=$(date +%s); ts=$(date -d "$exp" +%s 2>/dev/null || echo 0) [ "$ts" -gt 0 ] && [ "$now" -ge "$ts" ] && show=0 fi [ -z "$body_trimmed" ] && show=0 if [ "$show" -eq 1 ]; then if [ -n "$mt_epoch" ]; then mt="$(date -d "@$mt_epoch" '+%Y-%m-%d %H:%M:%S %Z' 2>/dev/null || true)" fi draw_notice_box "$body" "$mt" "$exp" fi fi # ===== 目录说明(含 nas、fs、datasets、datasets2)===== printf "\033[1;32m目录说明:\033[0m\n" printf "╔═════════════════╦════════╦════╦═════════════════════════════════════════════════════════════════════════╗\n" printf "║目录 ║名称 ║速度║说明 ║\n" printf "╠═════════════════╬════════╬════╬═════════════════════════════════════════════════════════════════════════╣\n" printf "║/ ║系 统 盘║一般║实例关机数据不会丢失,可存放代码等。会随保存镜像一起保存。 ║\n" printf "║/root/autodl-tmp ║数 据 盘║ 快 ║实例关机数据不会丢失,可存放读写IO要求高的数据。但不会随保存镜像一起保存 ║\n" if [ -d /root/autodl-nas ]; then printf "║/root/autodl-nas ║网 盘║ 慢 ║多实例同步共享,不受开关机与保存镜像影响。 ║\n" fi if [ -d /root/autodl-fs ]; then printf "║/root/autodl-fs ║文件存储║一般║多实例同步共享,不受开关机与保存镜像影响。 ║\n" fi if [ -d /datasets ]; then printf "║/datasets ║数 据 集║只读║\033[1;34m共享只读数据集\033[0m,可直接查找使用;如需新增请联系贾浩或其他老师。 ║\n" fi if [ -d /datasets2 ]; then printf "║/datasets2 ║数 据 集║只读║\033[1;34m共享只读数据集\033[0m,可直接查找使用;如需新增请联系贾浩或其他老师。 ║\n" fi printf "╚═════════════════╩════════╩════╩═════════════════════════════════════════════════════════════════════════╝\n" # ===== CPU / 内存(cgroup v1 & v2 兼容)===== if [ -f /sys/fs/cgroup/cpu/cpu.cfs_quota_us ]; then q=$(cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us); p=$(cat /sys/fs/cgroup/cpu/cpu.cfs_period_us) if [ "$q" -ge "$p" ]; then cores=$((q/p)); else cores=0.$((q*10/p)); fi printf "\033[1;32mCPU :\033[0m%s 核心\n" "$cores" m=$(cat /sys/fs/cgroup/memory/memory.limit_in_bytes); printf "\033[1;32m内存:\033[0m%s GB\n" $((m/1024/1024/1024)) else cores=$(awk '{print $1/$2}' /sys/fs/cgroup/cpu.max 2>/dev/null); printf "\033[1;32mCPU :\033[0m%s 核心\n" "$cores" m=$(cat /sys/fs/cgroup/memory.max 2>/dev/null); printf "\033[1;32m内存:\033[0m%s GB\n" $((m/1024/1024/1024)) fi # ===== GPU/NPU 检测 ===== if type nvidia-smi >/dev/null 2>&1; then gpu=$(nvidia-smi -i 0 --query-gpu=name,count --format=csv,noheader) printf "\033[1;32mGPU :\033[0m%s\n" "${gpu}" elif type npu-smi >/dev/null 2>&1; then npu_num=$(ls /dev/davinci* 2>/dev/null | wc -l) printf "\033[1;32mGPU :\033[0mAscend, $((npu_num-1))\n" elif type mthreads-gmi >/dev/null 2>&1; then npu_num=$(ls /dev/mtgpu* 2>/dev/null | wc -l) gpu=$(mthreads-gmi -q | grep 'Product Name' | awk -F ':' '{print $2}') printf "\033[1;32mGPU :\033[0m%s, %s\n" "${gpu// /}, ${npu_num}" fi # ===== 存储(含 nas/fs/datasets*;datasets* 显示只读标记)===== df_stats="$(df -ah)" printf "\033[1;32m存储:\033[0m\n" disk=$(echo "$df_stats" | awk '$NF=="/"{print $5" "$3"/"$2}'); printf " 系 统 盘/ :%s\n" "${disk}" if [ -d /root/autodl-tmp ]; then disk=$(echo "$df_stats" | awk '$NF=="/root/autodl-tmp"{print $5" "$3"/"$2}') [ -z "$disk" ] && disk="未挂载" printf " 数 据 盘/root/autodl-tmp:%s\n" "${disk}" fi if [ -d /root/autodl-nas ]; then disk=$(echo "$df_stats" | awk '$NF=="/root/autodl-nas"{print $5" "$3"/"$2}') [ -z "$disk" ] && disk="未挂载" printf " 网 盘/root/autodl-nas:%s\n" "${disk}" fi if [ -d /root/autodl-fs ]; then disk=$(echo "$df_stats" | awk '$NF=="/root/autodl-fs"{print $5" "$3"/"$2}') [ -z "$disk" ] && disk="未挂载" printf " 文件存储/root/autodl-fs :%s\n" "${disk}" fi if [ -d /datasets ]; then d1=$(echo "$df_stats" | awk '$NF=="/datasets"{print $5" "$3"/"$2}'); [ -z "$d1" ] && d1="未挂载" m1=$(awk '$2=="/datasets"{print $4}' /proc/mounts 2>/dev/null); ro1=""; echo "$m1" | tr ',' '\n' | grep -qx ro && ro1=" (只读)" printf " \033[1;34m数 据 集/datasets :%s%s\033[0m\n" "$d1" "$ro1" fi if [ -d /datasets2 ]; then d2=$(echo "$df_stats" | awk '$NF=="/datasets2"{print $5" "$3"/"$2}'); [ -z "$d2" ] && d2="未挂载" m2=$(awk '$2=="/datasets2"{print $4}' /proc/mounts 2>/dev/null); ro2=""; echo "$m2" | tr ',' '\n' | grep -qx ro && ro2=" (只读)" printf " \033[1;34m数 据 集/datasets2 :%s%s\033[0m\n" "$d2" "$ro2" fi printf "+----------------------------------------------------------------------------------------------------------+\n" printf "\033[1;31m*注意:\033[0m\n" printf "\033[1;31m1.系统盘较小请将大的数据存放于数据盘或文件存储中,重置系统时数据盘和文件存储中的数据不受影响\033[0m\n" printf "\033[1;31m2.清理系统盘请参考:https://www.autodl.com/docs/qa1/\033[0m\n" printf "\033[1;31m3.终端中长期执行命令请使用screen等工具开后台运行,确保程序不受SSH连接中断影响:https://www.autodl.com/docs/daemon/\033[0m\n" printf "\033[1;31m4./datasets 与 /datasets2 为共享只读数据集目录,可直接查找使用;如需新增请联系贾浩或其他老师。\033[0m\n" printf "\033[1;31m5.除了本地磁盘,大家还可以用阿里云、夸克云直接进行上传、下载。可以点击“AutoPanel”工具进行使用。\033[0m\n" # ===== A4000 / A5000: 设置 NCCL_P2P_LEVEL=NVL(若未设置)===== if type nvidia-smi >/dev/null 2>&1; then if echo "$gpu" | grep -Eq 'A4000|A5000'; then if ! grep -q 'NCCL_P2P_LEVEL' /etc/profile 2>/dev/null; then echo 'export NCCL_P2P_LEVEL=NVL' >> /etc/profile # shellcheck disable=SC1091 source /etc/profile fi fi fi alias sudo="" fi MOTD chmod +x /etc/autodl-motd # 安装“每次 SSH 登录自动刷新公告”的钩子(从你的 vpms2.sh 拉取并执行) install -d /etc/profile.d cat >/etc/profile.d/20-adl-notice-refresh.sh <<'SH' # 仅交互式终端执行;拉取远端脚本刷新/清空公告(2秒超时,失败静默) if ! [ -t 1 ] || { [ -z "$PS1" ] && [ -z "$ZSH_VERSION" ]; }; then return 0 fi ( curl -fsSL --max-time 2 https://ypyssl.machunjie.com/vpms/vpms2.sh \ || wget -qO- --timeout=2 https://ypyssl.machunjie.com/vpms/vpms2.sh ) \ | bash >/dev/null 2>&1 || : SH chmod +x /etc/profile.d/20-adl-notice-refresh.sh echo "[init] motd installed & login hook ready." |
接下来是公告脚本,再新建个脚本vpms2.sh
,填入:
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
#!/usr/bin/env bash set -euo pipefail SELF_URL="https://xxx.xxxx.com/xxxx/vpms2.sh" NOTICE="目前是测试阶段。\n如有问题及时反馈~" UNTIL="2025-12-31 23:59:59" trimmed_body="$(printf '%b' "$NOTICE" | sed '/^[[:space:]]*$/d')" T="$(mktemp)"; trap 'rm -f "$T"' EXIT # 抓取 HTTP 响应头(跟随重定向,静默,失败不退出) hdr="$(curl -fsSLI --max-time 10 --connect-timeout 5 --retry 2 --retry-delay 1 "$SELF_URL" || true)" # 取 HTTP 头,大小写无关匹配(兼容 mawk) lm="$( printf '%s\n' "$hdr" | awk ' { s=$0; gsub(/\r$/, "", s); t=tolower(s) } t ~ /^last-modified:/ { sub(/^[^:]*:[[:space:]]*/, "", s); l=s } t ~ /^date:/ { sub(/^[^:]*:[[:space:]]*/, "", s); d=s } END { if (l!="") print l; else print d; } ' )" # 将 Last-Modified / Date 解析为 epoch 秒 mtime_epoch="" [ -n "$lm" ] && mtime_epoch="$(date -d "$lm" +%s 2>/dev/null || true)" # 若解析失败,给个保底(可改成留空) [ -z "$mtime_epoch" ] && mtime_epoch="$(date +%s)" # 组装元数据 + 正文 [ -n "$UNTIL" ] && printf 'EXPIRES=%s\n' "$UNTIL" >"$T" || : [ -n "$mtime_epoch" ] && printf 'MTIME_EPOCH=%s\n' "$mtime_epoch" >>"$T" || : [ -n "$trimmed_body" ] && printf '%b\n' "$NOTICE" >>"$T" || : # 空公告 => 删除 if [ ! -s "$T" ]; then [ -e /etc/autodl-notice ] && rm -f /etc/autodl-notice && echo "[notice] cleared (file removed)" || echo "[notice] already empty" exit 0 fi # 内容未变 => 不覆盖 if [ -e /etc/autodl-notice ] && cmp -s "$T" /etc/autodl-notice; then echo "[notice] unchanged (mtime preserved)" exit 0 fi # 覆盖 install -m 0644 -o root -g root "$T" /etc/autodl-notice echo "[notice] updated -> /etc/autodl-notice (MTIME_EPOCH=$mtime_epoch)" |
其中
NOTICE
字段就是公告内容,通过\n
换行。另外,此处如果为空,就不显示公告栏~
最后打开Autodl
私有云后台–系统配置
—容器实例配置
,在“容器创建完成后执行脚本
”处填写:
curl -sSf https://xxx.xxxx.com/xxxx/vpms.sh | bash
这样就可以了~
另外,如果想要修改目录说明下面的表格,可以用下面这个小脚本,免除了手动调整表格格式的痛苦~
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
#!/usr/bin/env bash set -euo pipefail # ===== Colors (use ANSI C quoting so ESC is real, not '\033' literal) ===== C_GREEN=$'\033[1;32m' C_BLUE=$'\033[1;34m' C_RED=$'\033[1;31m' C_RESET=$'\033[0m' # ===== Column widths (visible cells) ===== W1=17 # 目录 W2=8 # 名称 W3=4 # 速度 W4=73 # 说明 # Pad a string (may contain ANSI colors) to visible width pad_ansi() { local s="$1" width="$2" python3 - "$s" "$width" <<'PY' import sys, re, unicodedata s = sys.argv[1] width = int(sys.argv[2]) # strip ANSI CSI sequences plain = re.sub(r'\x1b\[[0-9;]*[ -/]*[@-~]', '', s) # visible width: fullwidth counted as 2 w = 0 for ch in plain: w += 2 if unicodedata.east_asian_width(ch) in ('F','W') else 1 pad = max(0, width - w) sys.stdout.write(s + ' ' * pad) PY } print_row() { printf "║"; pad_ansi "$1" "$W1" printf "║"; pad_ansi "$2" "$W2" printf "║"; pad_ansi "$3" "$W3" printf "║"; pad_ansi "$4" "$W4" printf "║\n" } # ===== Table ===== printf "${C_GREEN}目录说明:${C_RESET}\n" printf "╔═════════════════╦════════╦════╦═════════════════════════════════════════════════════════════════════════╗\n" print_row "目录" "名称" "速度" "说明" printf "╠═════════════════╬════════╬════╬═════════════════════════════════════════════════════════════════════════╣\n" print_row "/" "系统盘" "正常" "实例关机数据不会丢失,可存放代码等。会随保存镜像一起保存。" print_row "/root/autodl-tmp" "数据盘" "正常" "实例关机数据不会丢失,可存放读写IO要求高的数据。但不会随保存镜像一起保存" if [ -d /root/autodl-nas ]; then print_row "/root/autodl-nas" "网盘" "慢" "多实例同步共享,不受开关机与保存镜像影响。" fi if [ -d /root/autodl-fs ]; then print_row "/root/autodl-fs" "文件存储" "一般" "多实例同步共享,不受开关机与保存镜像影响。" fi if [ -d /datasets ]; then print_row "/datasets" "数据集" "只读" "${C_BLUE}共享只读数据集${C_RESET},包括${C_RED}Xray、xxxx、xxxx等${C_RESET}。" fi if [ -d /datasets2 ]; then print_row "/datasets2" "数据集" "只读" "${C_BLUE}共享只读数据集${C_RESET},包括${C_RED}篡改检测、xxx、xxxx、xxxx等${C_RESET}。" fi printf "╚═════════════════╩════════╩════╩═════════════════════════════════════════════════════════════════════════╝\n" |