这是默认的:
目前是有以下几点想要修改:
1、在顶部增加一个公告栏,方便给学生通知各种信息。
2、在目录处增加共享目录的介绍(新挂载了/datasets
等目录,但是默认欢迎界面中不显示这些,所以就想显式地介绍下)
目前情况是,实例开机之后,会自动运行/etc/autodl-motd
文件,然后Autodl
私有云管理后台提供了两个钩子,一个是“容器创建完成后执行脚本
”,一个是“容器每次开机后执行脚本
”。目前的思路是,创建一个新的欢迎脚本,在容器第一次创建之后,替换原有的autodl-motd
文件,并增加公告栏,通过远程文件进行更新,每次连接SSH
的时候,都会自动获取远端公告信息,如果有更新就显示公告栏,如果没有公告就不显示公告栏。
下面是效果,首先是无公告,增加数据集目录介绍:
然后是远端更新公告之后:
操作步骤
由于代码较多,所以建议将脚本存放在远端存储,比如又拍云。我们新建一个文件vpms.sh
,链接地址是:https://xxx.xxxx.com/xxxx/vpms.sh
,填入:
#!/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
,填入:
#!/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
这样就可以了~
另外,如果想要修改目录说明下面的表格,可以用下面这个小脚本,免除了手动调整表格格式的痛苦~
#!/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"