☰
如何用 /etc/machine/create-user.sh 自定义 container machine 的首次启动用户初始化?
2026/10/6 19:48:30 网站建设 项目流程

如何用 /etc/machine/create-user.sh 自定义 container machine 的首次启动用户初始化?

【免费下载链接】containerA tool for creating and running Linux containers using lightweight virtual machines on a Mac. It is written in Swift, and optimized for Apple silicon.项目地址: https://gitcode.com/GitHub_Trending/container30/container

用 container 在 Mac 上创建 container machine 时,首次启动会自动创建一个与你 macOS 主机账号对应的 Linux 用户:主机用户名被映射进去,Mac 的$HOME挂载到/Users/<username>,仓库和 dotfiles 在两边可见。这套默认行为由内置的用户初始化脚本完成。如果你需要在首次启动时做不同的用户配置——例如修改 shell、追加初始文件、调整权限规则——可以把自定义脚本以可执行文件的形式放进镜像的/etc/machine/create-user.sh,它会取代内置脚本执行。

本文的任务就是这条路径:写出自定义脚本 → 打进自建镜像 → 用该镜像创建 machine → 验证首次启动用户初始化结果。依据是 docs/container-machine.md 的 “Bring your own container machine image” 一节,以及仓库中 init 与内置脚本的实现(init、create-user.sh)。

先弄清首次启动初始化是怎么触发的

container machine 镜像启动时,init 脚本先于系统/sbin/init运行。它处理-u(首次用户设置)时的逻辑是:

  1. 用id "${CONTAINER_USER}"检查用户是否已存在;
  2. 若不存在:镜像内存在/etc/machine/create-user.sh时执行它,否则执行内置的/sbin.machine/create-user.sh;
  3. 无论是否执行了用户创建,都写入/etc/.machine.initialized标记初始化完成。

也就是说:自定义脚本在首次启动、以 root 身份、只运行一次(docs/container-machine.md 的表述),且优先级高于内置脚本。内置脚本本身做的事可以参考 create-user.sh:直接写/etc/group、/etc/passwd、/etc/shadow创建用户(不依赖 useradd/adduser 等镜像特定工具),创建CONTAINER_HOME并从/etc/skel填充初始文件,最后写入一条免密 sudo 规则到/etc/sudoers.d/。

脚本里可用的环境变量

自定义脚本运行时,以下变量由启动环境提供(docs/container-machine.md 列出了前五项):

变量内容
CONTAINER_USER你的主机用户名
CONTAINER_UID对应用户的 UID
CONTAINER_GID对应用户的 GID
CONTAINER_HOME用户家目录,形如/home/<username>
CONTAINER_MACHINE_IDmachine 的 ID
CONTAINER_SHELLinit 脚本导出:从镜像解析出的系统默认 shell。Debian/Ubuntu 读取/etc/adduser.conf的DSHELL,其他镜像读取/etc/default/useradd的SHELL,找不到时分别回退到/bin/bash或/bin/sh

这些变量在 MachineConfiguration.swift 的processEnvironment中注入,CONTAINER_SHELL由 init 在调用脚本前export。

编写镜像:把自定义脚本打进 image

镜像的前提是包含/sbin/init(docs/container-machine.md:“Any Linux image that includes/sbin/initworks as a container machine”)。下面以文档中的 Ubuntu 24.04 machine 镜像为例,仅追加自定义脚本部分。脚本内容取自仓库内置脚本的写法,用单引号 heredoc 防止构建期展开变量:

FROM ubuntu:24.04 ENV container container RUN apt-get update && \ apt-get install -y \ dbus systemd openssh-server net-tools iproute2 iputils-ping curl wget vim-tiny man sudo && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ yes | unminimize RUN >/etc/machine-id RUN >/var/lib/dbus/machine-id RUN systemctl set-default multi-user.target RUN systemctl mask \ dev-hugepages.mount \ sys-fs-fuse-connections.mount \ systemd-update-utmp.service \ systemd-tmpfiles-setup.service \ console-getty.service RUN systemctl disable \ networkd-dispatcher.service RUN sed -i -e 's/^AcceptEnv LANG LC_\*$/#AcceptEnv LANG LC_*/' /etc/ssh/sshd_config # 自定义首次启动用户初始化脚本(必须可执行) RUN mkdir -p /etc/machine && \ cat > /etc/machine/create-user.sh <<'EOF' #!/bin/sh set -e if ! getent group "${CONTAINER_GID}" >/dev/null 2>&1; then echo "${CONTAINER_USER}:x:${CONTAINER_GID}:" >> /etc/group fi if ! getent passwd "${CONTAINER_UID}" >/dev/null 2>&1; then echo "${CONTAINER_USER}:x:${CONTAINER_UID}:${CONTAINER_GID}::${CONTAINER_HOME}:${CONTAINER_SHELL}" >> /etc/passwd echo "${CONTAINER_USER}:!:19000:0:99999:7:::" >> /etc/shadow fi mkdir -p "${CONTAINER_HOME}" if [ -d /etc/skel ]; then cp -a /etc/skel/. "${CONTAINER_HOME}" fi # 在这里追加你自己的初始化步骤(写初始文件、配置组等) # 放在 chown 之前,确保新文件的所有权随后被正确设置 chown -R "${CONTAINER_UID}:${CONTAINER_GID}" "${CONTAINER_HOME}" mkdir -p /etc/sudoers.d sudoers_file=$(echo "${CONTAINER_USER}" | tr '.' '_') echo "${CONTAINER_USER} ALL=(ALL) NOPASSWD:ALL" > "/etc/sudoers.d/${sudoers_file}" chmod 440 "/etc/sudoers.d/${sudoers_file}" EOF chmod +x /etc/machine/create-user.sh

上面脚本主体与仓库内置脚本行为一致(直接改/etc/group、/etc/passwd、/etc/shadow,填充/etc/skel,配置免密 sudo),保证 machine 的基本约定不被破坏;你的自定义逻辑加在注释标记的位置即可。文档只要求该文件是可执行脚本,不限制脚本内容。

构建镜像并创建 machine

container build -t local/ubuntu-machine:latest . container machine create local/ubuntu-machine:latest --name ubuntu

container machine create之后,machine 尚未启动过,用户初始化还没发生。接下来第一次run会先启动 machine:

container machine run -n ubuntu whoami container machine run -n ubuntu pwd

验证首次启动初始化结果

按 docs/container-machine.md Quickstart 的判断标准,上面两条命令的输出应为:whoami输出你的主机用户名(而不是 root),pwd输出/home/<you>(你的 Mac 家目录挂载点,文档示例表述)。

再核对用户记录与初始化标记:

# 确认自定义脚本写入的 /etc/passwd 条目(shell 应为 CONTAINER_SHELL 解析结果) container machine run -n ubuntu -- cat /etc/passwd # 免密 sudo 规则文件(文件名中用户名里的点号会替换为下划线) container machine run -n ubuntu -- ls /etc/sudoers.d # init 在用户设置完成后写入的标记文件 container machine run -n ubuntu -- ls -l /etc/.machine.initialized

/etc/.machine.initialized由 init 写入,存在即表示首次初始化流程已经走过一遍。

限制与重新测试的注意事项

  • 只运行一次:init 用id "${CONTAINER_USER}"判断用户是否已存在,已存在则跳过用户创建脚本。如果你改完脚本想重新观察首次启动行为,需要删除 machine 再重建——container machine rm ubuntu会连同其持久化存储一起删除(docs/container-machine.md “List, inspect, stop, delete” 一节),操作前确认数据无需保留。
  • 脚本必须在镜像里且可执行:/etc/machine/create-user.sh是随镜像文件系统分发的;只对创建时使用的镜像生效,之后换脚本必须重建 machine。
  • 脚本以 root 运行:脚本内不需要再提权,但要保证set -e下的任何一步失败不会留下半初始化的用户状态——内置脚本用getent做了幂等检查,自定义步骤建议同样考虑重复执行安全。
  • 验证命令如cat /etc/passwd输出内容随你的主机用户名不同而不同,文档没有固定预期值,按上述判断标准核对即可。

完成验证后,这台 machine 就可以按 docs/container-machine.md 的常规用法使用了:container machine run -n ubuntu进入交互 shell,container machine set -n ubuntu cpus=4 memory=8G等修改需 stop 后重新 run 才生效。

【免费下载链接】containerA tool for creating and running Linux containers using lightweight virtual machines on a Mac. It is written in Swift, and optimized for Apple silicon.项目地址: https://gitcode.com/GitHub_Trending/container30/container

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询