Table of Contents
Amazon EC2 上安全、可复现的 Ubuntu GPU 工作流
2017 年的清单把全局可写的 Git 部署钩子、过时的 Bazel 软件源、未指定版本的 TensorFlow 源码构建、未锁定的 Python 包和公开 Notebook 教程混在一起。这些做法都不适合作为当前默认方案。
2026 版先确定版本矩阵与成本边界,让 Jupyter 仅监听回环地址并通过带身份验证的隧道访问,同时保存足够证据,以便安全复现或下线机器。这是一套工作流,并不保证任意框架版本都能搭配任意 GPU、CPU 架构、驱动或 CUDA 栈。启动前和安装前都应核对所选版本的官方矩阵。
1. 有意识地选择一条平台路径
| 路径 | 适合场景 | 可复现边界 |
|---|---|---|
| AWS Deep Learning AMI(DLAMI) | 最快开始受支持的交互式工作 | 固定区域与 AMI ID,阅读该镜像的发行说明,激活文档指定的环境,不要随意替换预装 CUDA 或框架包 |
| AWS Deep Learning Container | 团队工作流、CI 和可重复任务 | 用不可变摘要固定镜像,记录宿主机驱动与 GPU 运行时,数据和密钥放在镜像之外 |
| 标准 Ubuntu AMI | 需要完全控制,或 AWS 镜像不满足要求 | 固定 Ubuntu AMI,只按当前 NVIDIA 指南安装驱动/工具包;这条路径的兼容性和补丁维护工作最多 |
AWS DLAMI 已配置常用框架和 NVIDIA 组件。AWS Deep Learning Container 文档会指向当前维护的容器目录。标准 Ubuntu 镜像不会天然更可复现;只有记录 AMI、软件源密钥环、软件包版本、框架锁文件和验证输出之后才算可复现。
安装过程中不要混用路径。尤其不要把无关的 CUDA runfile、发行版软件包和 pip 提供的 CUDA 库叠加在同一环境中,除非所选框架的文档明确支持该组合。
2. 启动前建立兼容性清单
从真正需要的框架版本向外选择,而不是从看起来最新的 GPU 倒推。逐项记录下表:
| 层级 | 必须记录的精确值 | 权威来源/检查方式 |
|---|---|---|
| AWS 位置 | 区域、可用区、实例类型、购买模式 | 当前 EC2 控制台/API 与加速计算实例规格 |
| 机器镜像 | AMI ID、镜像名、所有者、创建日期 | EC2 镜像详情;AMI ID 与区域相关 |
| CPU | x86_64 或 arm64 | 实例规格与 uname -m |
| GPU | 型号、数量、显存、NVIDIA 架构、计算能力 | EC2 规格、nvidia-smi 与 NVIDIA CUDA GPU 表 |
| 驱动 | 精确驱动版本 | nvidia-smi 与 NVIDIA 驱动工具参考 |
| CUDA | 驱动支持的 CUDA 上限;另行记录已安装工具包版本 | nvidia-smi 不能证明已安装 nvcc;适用时运行 nvcc --version |
| 框架 | 名称、精确版本/构建、支持的 Python/CUDA/cuDNN/计算能力 | 所选框架版本的官方安装/构建矩阵 |
| Python 环境 | Python、pip、所有直接与传递依赖、wheel 哈希 | 针对该操作系统、CPU 架构和 Python 版本生成的锁文件 |
只要有一项未知、所需 AMI 并非预期发布者所有、CPU 架构缺少所需 wheel、GPU 显存或计算能力不足,或框架矩阵与驱动/CUDA 组合冲突,就应在启动前停止。
3. 先设置安全与成本控制
- 启动 GPU 实例前创建 AWS Budget 和提醒。提醒只是通知,不会自动关机;若要自动处理,应另行配置并测试预算操作。按所选区域核对当前价格、Spot 中断、配额、EBS、快照、公网 IPv4、流量和闲置资源成本。
- 优先使用无需入站规则的 Systems Manager Session Manager。必须用 SSH 时,只允许固定管理员 CIDR 访问 TCP 22。绝不向
0.0.0.0/0或::/0开放 8888。 - 给实例附加最小权限角色,只允许必需的制品、数据、日志和 Systems Manager 资源。不要把长期 AWS 访问密钥复制到实例。
- 强制 IMDSv2。仅宿主机工作流将响应 hop limit 保持为 1;容器网络可能需要经过明确审查的值。除非确有需要,不通过元数据暴露实例标签。
- 加密 EBS 卷,尽可能把持久数据与一次性工作区分开,并检查每个卷的
DeleteOnTermination。实例存储中的数据应视为临时数据。 - 使用专用 Unix 用户和限制性权限。不要使用
chmod -R 777、无密码部署钩子或多人可写的 checkout。
在获得授权的管理机上,对现有实例强制使用 IMDSv2:
INSTANCE_ID='replace-with-instance-id'
aws ec2 modify-instance-metadata-options
--instance-id "$INSTANCE_ID"
--http-tokens required
--http-endpoint enabled
--http-put-response-hop-limit 1
--instance-metadata-tags disabled
相关第一方资料包括 IMDS 配置、EC2 IAM 角色、安全组规则、EBS 加密和 AWS Budgets。
4. 修改前盘点运行中的宿主机
以普通实例用户运行。IMDSv2 令牌有效期很短,不会写入清单,并在读取两个非秘密元数据字段后清除。令牌头通过标准输入传给 curl,不出现在其参数列表中;但仍应将 shell 和宿主机视为可信环境。run-manifest/ 可能暴露敏感的基础设施细节,应保持私密。
set -euo pipefail
umask 077
mkdir -p run-manifest
cat /etc/os-release | tee run-manifest/os-release.txt
uname -m | tee run-manifest/cpu-architecture.txt
python3 --version 2>&1 | tee run-manifest/python-system.txt
metadata_base='http://169.254.169.254/latest'
metadata_token="$(curl --fail --silent --show-error
--request PUT
--header 'X-aws-ec2-metadata-token-ttl-seconds: 60'
"$metadata_base/api/token")"
for key in ami-id instance-type; do
builtin printf
'header = "X-aws-ec2-metadata-token: %s"n'
"$metadata_token"
| curl --fail --silent --show-error
--config -
"$metadata_base/meta-data/$key"
| tee "run-manifest/$key.txt"
printf 'n'
done
unset metadata_token
command -v nvidia-smi >/dev/null || {
printf '%sn' 'nvidia-smi is missing; stop and repair the driver path.' >&2
exit 1
}
nvidia-smi | tee run-manifest/nvidia-smi.txt
nvidia-smi
--query-gpu=name,pci.bus_id,driver_version,memory.total
--format=csv,noheader
| tee run-manifest/gpu-inventory.csv
if command -v nvcc >/dev/null; then
nvcc --version | tee run-manifest/nvcc.txt
else
printf '%sn' 'nvcc not installed; no local CUDA toolkit recorded.'
| tee run-manifest/nvcc.txt
fi
若选择标准 Ubuntu,应遵循当前 NVIDIA CUDA Linux 安装指南。使用其受支持的软件源/密钥环流程和安装前检查;不要恢复旧的 apt-key、未签名软件源或 HTTP 软件源命令。驱动变更后,如官方说明要求则重启,重新盘点,并在驱动/库不匹配时停止。
5. 创建隔离且锁定的 Python 环境
使用所选框架构建支持的 Python 版本。虚拟环境可以隔离 Python 包,但不能解决驱动或系统库兼容性。
在与生产环境相同的操作系统、CPU 架构、Python 次版本和软件包索引策略下生成 requirements.lock。锁定所有直接和传递依赖并加入验证过的哈希。使用前审查锁文件;不要在长期环境中直接输入没有版本的 pip install tensorflow numpy pandas jupyter。
set -euo pipefail
umask 077
python3 -m venv .venv
.venv/bin/python -m pip install
--require-hashes
--only-binary=:all:
--requirement requirements.lock
.venv/bin/python -m pip check
.venv/bin/python -m pip freeze --all
| tee run-manifest/python-freeze.txt
sha256sum requirements.lock
| tee run-manifest/requirements-lock.sha256
在受支持的标准 Linux GPU 安装中,TensorFlow 当前官方 pip 路径使用 tensorflow[and-cuda] extra,但具体版本及所有解析依赖仍须锁定。DLAMI 则可能要求激活其文档指定的预构建环境。请查阅 TensorFlow pip 安装指南、Python 的 venv 文档和 pip 的可重复安装指南。
6. 证明框架确实在 GPU 上执行
不要只运行 nvidia-smi,还要执行框架级运算。下面的 TensorFlow 冒烟测试会报告软件包构建、可见设备和设备详情,并要求一次矩阵乘法放置在 GPU 0:
import json
import platform
import tensorflow as tf
tf.random.set_seed(20260901)
gpus = tf.config.list_physical_devices("GPU")
report = {
"python": platform.python_version(),
"tensorflow": tf.__version__,
"build": tf.sysconfig.get_build_info(),
"gpus": [
tf.config.experimental.get_device_details(device)
for device in gpus
],
}
print(json.dumps(report, indent=2, default=str))
if not gpus:
raise SystemExit("no GPU visible to TensorFlow")
with tf.device("/GPU:0"):
left = tf.random.uniform((512, 512))
right = tf.random.uniform((512, 512))
product = tf.linalg.matmul(left, right)
print({"device": product.device, "shape": product.shape.as_list()})
if "GPU:0" not in product.device.upper():
raise SystemExit(f"operation was not placed on GPU 0: {product.device}")
用锁定环境的解释器运行,并把输出与任务记录一同保存。GPU 可见并不能证明数值正确、模型收敛、多 GPU 通信、混合精度行为或显存足够;应再为真实负载增加一个小型代表性测试。
7. 让 Jupyter 保持本地且启用身份验证
Jupyter Server 可以执行代码,应把访问权限当作 shell 权限。它默认启用令牌身份验证。保持验证开启,不要把生成的令牌写入工单或日志,并且只监听回环地址:
set -euo pipefail
umask 077
mkdir -p notebooks
cd notebooks
../.venv/bin/jupyter lab
--no-browser
--ServerApp.ip=127.0.0.1
--ServerApp.port=8888
--ServerApp.port_retries=0
--ServerApp.open_browser=False
不要为 8888 添加安全组入站规则。从管理员机器使用下列任一种隧道。SSH 方式:
ssh -N
-L 127.0.0.1:8888:127.0.0.1:8888
ubuntu@replace-with-hostname
或者,完成 Session Manager 前置配置并在本机安装插件后:
INSTANCE_ID='replace-with-managed-instance-id'
aws ssm start-session
--target "$INSTANCE_ID"
--document-name AWS-StartPortForwardingSession
--parameters '{"portNumber":["8888"],"localPortNumber":["8888"]}'
在本机打开 http://127.0.0.1:8888/,输入只显示在实例终端中的令牌。如需长期密码认证,使用 Jupyter 的交互式密码命令,以便只保存哈希。绝不能同时禁用密码与令牌。参见 Jupyter Server 安全指南、其回环默认值与公网服务器警告,以及 AWS Session Manager 端口转发。
8. 分离代码、密钥、数据和日志
- 代码:使用普通用户拥有的 checkout。用 Git 记录精确 commit 和 submodule;不要通过未经审查的
post-receive钩子部署。 - 密钥:使用有限范围的实例角色和获批密钥存储。绝不提交密钥、把密钥放入 Notebook、烘焙进 AMI/容器或打印到盘点日志。
- 输入数据:尽量只读挂载或下载。记录数据集版本和校验和,不记录私有对象 URL 或客户标识符。
- 输出:按明确周期把 checkpoint 写入持久加密 EBS 卷或获批对象存储。停止使用实例存储的机器前,验证远端副本。
- 日志:记录软件包、驱动、框架、命令、退出状态、时间戳和负载指标;集中保存前删除令牌、签名 URL、提示词、个人或受监管数据。
在不修改部署权限的情况下记录源码状态:
set -euo pipefail
git status --porcelain=v1
git rev-parse HEAD | tee run-manifest/source-commit.txt
git submodule status --recursive
| tee run-manifest/source-submodules.txt
提交查询命令见 Git `rev-parse`。工作区不干净是可复现性停止条件:昂贵任务启动前,应提交、丢弃或明确归档差异。
9. 把源码构建视为例外
除非源码补丁、不受支持的计算能力或编译器要求使源码构建不可避免,否则使用已发布 wheel、DLAMI 环境或不可变容器。构建 TensorFlow 前,根据其已测试构建配置和相应 Bazel 安装文档填完矩阵:
| 必须锁定 | 记录值 |
|---|---|
| TensorFlow 标签和完整 Git commit | |
| Python 版本 | |
| Bazel 版本 | |
| 编译器和标准库版本 | |
| NVIDIA 驱动、CUDA 工具包和 cuDNN | |
| GPU 型号、架构和计算能力 | |
| 基础 AMI 或容器摘要 | |
| 构建参数、补丁、wheel 哈希和预期测试 |
不要假设当前 Bazel 可以构建旧 TensorFlow。任何兼容性单元格只有推测而没有文档依据时,都不要继续。
set -euo pipefail
SOURCE_REF='replace-with-reviewed-tag-or-commit'
git clone --filter=blob:none
https://github.com/tensorflow/tensorflow.git
git -C tensorflow fetch --tags --prune
git -C tensorflow checkout --detach "$SOURCE_REF"
git -C tensorflow status --porcelain=v1
git -C tensorflow rev-parse HEAD
在一次性卷或容器中构建,保存完整日志,在源码树之外的全新环境中验证 wheel,并保留其校验和。构建完成本身不能证明 GPU 路径或目标负载可用。
10. 维护私密的可复现清单
把机器可读清单与锁文件、测试输出和校验和保存在一起。不要公开账户 ID、实例 ID、内部主机名、存储桶名、对象路径、令牌或数据集标识符。
schema_version: 1
captured_at_utc: replace-with-iso-8601-time
aws:
region: replace
availability_zone: replace
instance_type: replace
purchase_model: replace
ami_id: replace
ami_name: replace
cpu_architecture: replace
gpu:
model: replace
count: replace
architecture: replace
compute_capability: replace
driver_version: replace
driver_cuda_ceiling: replace
toolkit_version: replace-or-not-installed
environment:
python_version: replace
framework_name: tensorflow
framework_version: replace
framework_build: replace
requirements_lock_sha256: replace
source:
commit: replace
dirty: false
container_digest: replace-or-not-used
verification:
gpu_smoke_test: pass-or-fail
workload_smoke_test: pass-or-fail
storage:
root_delete_on_termination: replace
durable_output_location: private-reference-only
清单包含系统事实,不包含凭据。敏感资源映射应在更严格的访问控制下单独保存。
11. 验证、停止、回滚与清理
开始付费工作前:
- 确认预算、提醒收件人、实例价格模式,以及负责停止闲置容量的指定人员或自动化。
- 确认安全组没有 Notebook 端口、已强制 IMDSv2、实例角色最小化、卷已加密,并按要求记录 SSH/SSM 访问。
- 对照 EC2 型号/架构、
uname -m、GPU 清单、计算能力、驱动、CUDA 工具包、锁定的 Python 包和框架构建元数据。 - 运行
pip check、GPU 冒烟测试、小型负载测试、checkpoint 恢复和仅隧道 Jupyter 访问。 - 确认日志与清单不含秘密或私有数据,且不依赖实例也能恢复持久输出。
工作结束时,刷新并验证 checkpoint,停止 Jupyter,复制清单和必要日志,然后从获得授权的管理机停止实例:
INSTANCE_ID='replace-with-instance-id'
aws ec2 stop-instances --instance-ids "$INSTANCE_ID"
aws ec2 wait instance-stopped --instance-ids "$INSTANCE_ID"
停止实例会结束实例计算费用,但不会消除所有费用:EBS、快照、公网 IPv4/Elastic IP 和其他服务仍可能计费。停止/启动会清除实例存储数据。终止时默认删除根 EBS 卷,其他 EBS 卷则取决于 DeleteOnTermination;执行前阅读 EC2 实例状态变化。
只有在明确的人工关卡验证备份、恢复能力、卷标志、共享依赖和保留义务后才终止实例。回滚时保留不可变 AMI/容器引用、环境锁、源码 commit、清单、数据版本和已知良好测试输出;用它们重新构建,不要原地修补一台没有文档的机器。
12. 官方资料
- AWS Deep Learning AMI
- AWS Deep Learning Container
- AWS 加速计算实例规格
- AWS EC2 Instance Metadata Service
- AWS EC2 IAM 角色
- AWS 安全组规则
- AWS EBS 加密
- AWS Budgets
- AWS EC2 实例状态变化
- AWS Session Manager 端口转发
- NVIDIA CUDA Linux 安装指南
- NVIDIA `nvidia-smi` 参考
- NVIDIA CUDA GPU
- TensorFlow pip 安装
- TensorFlow 源码构建与已测试配置
- Python `venv`
- pip 可重复安装
- Jupyter Server 安全
- Jupyter Server 公网服务器指南
- 在 Ubuntu 上安装 Bazel
- Git `rev-parse`
13. 2017 年原文存档
以下是 source_export 的完整可见正文,用于保留来源。仅规范化行末空白;未增加、删除、修正或激活任何内容。过时的 HTTP 软件源、apt-key、全局可写钩子、未锁定安装列表和公网 Jupyter 链接只作为历史证据,不得执行。
1. Git server building.`
$ ssh-keygen -t rsa -C "user.email"`
Modifying hooks:`
$ vim sample.git/hooks/post-receive`
Sample code for hooks:`
#!/bin/sh
GIT_WORK_TREE=/home/ubuntu/Deployment/sample git checkout -f
chmod -R 777 /home/ubuntu/Deployment/sample`
2. Install bazel.
1). Add Bazel distribution URI as a package source (one time setup)
echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list
curl https://bazel.build/bazel-release.pub.gpg | sudo apt-key add -
If you want to install the testing version of Bazel, replace stable with testing.2). Install and update Bazel
sudo apt-get update && sudo apt-get install bazel
Once installed, you can upgrade to a newer version of Bazel with:
sudo apt-get upgrade bazel
3. Tensorflow compiling.
4. Python module installing.
- tensorflow
- numpy
- pandas
5. Juypter.
https://punchagan.muse-amuse.in/posts/create-a-public-jupyter-server-quickly.html
