阿里开源的Z-Image-Turbo,教你本地docker部署,无限生成无水印图片,3090就可以部署
本文介绍了在Ubuntu24系统下使用RTX3090显卡部署Z-Image-Turbo图像生成模型的方法。首先需要安装docker和配置nvidia-container-toolkit,然后从ModelScope社区下载模型文件。文章提供了两个核心文件:zimage_server.py(FastAPI服务端代码,包含模型加载和推理逻辑)和index.html(前端交互界面)。服务端代码特别强调了关
一、环境
1.操作系统:ubuntu24
2.显卡:RTX3090 24G
3.已经安装了docker和安装并配置 nvidia-container-toolkit
二、模型下载
1.到modelscope社区找到Tongyi-MAI/Z-Image-Turbo,点击“模型文件”,再点击“下载模型”,如下图
官方链接:
https://modelscope.cn/models/Tongyi-MAI/Z-Image-Turbo/files?version=master
2.然后选择一个下载方式就行,如图
3.下载成功如下图
三、新建2个运行所需文件
1.新建python文件,名字为:zimage_server.py,代码如下:
注: 可以根据自己的实际情况修改端口,分辨率等。
import os
import torch
import base64
import logging
import uvicorn
from io import BytesIO
from contextlib import asynccontextmanager
from fastapi import FastAPI, Form, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from diffusers import DiffusionPipeline
from fastapi.responses import FileResponse
# --- 配置 ---
MODEL_PATH = "/data/models/Z-Image-Turbo"
HOST = "0.0.0.0"
PORT = 8000
DEVICE = "cuda"
# 日志配置
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
logger = logging.getLogger(__name__)
ml_models = {}
@asynccontextmanager
async def lifespan(app: FastAPI):
logger.info(f"正在加载模型: {MODEL_PATH} ...")
try:
# --- 官方代码逻辑移植 1: 精度设置 ---
# 官方建议使用 bfloat16 以获得最佳性能并防止黑图
# 如果你的显卡比较旧(不支持 bfloat16),请改回 torch.float16
dtype = torch.bfloat16 if torch.cuda.is_bf16_supported() else torch.float16
pipe = DiffusionPipeline.from_pretrained(
MODEL_PATH,
torch_dtype=dtype,
use_safetensors=True
)
# 移动到 GPU
pipe.to(DEVICE)
# --- 官方代码逻辑移植 2: 内存优化 ---
# 如果显存不够,取消下面这行的注释
# pipe.enable_model_cpu_offload()
ml_models["pipe"] = pipe
logger.info(f">>> 模型加载成功 (Precision: {dtype})")
except Exception as e:
logger.error(f"模型加载失败: {e}")
raise e
yield
ml_models.clear()
if torch.cuda.is_available():
torch.cuda.empty_cache()
app = FastAPI(lifespan=lifespan)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"]
)
@app.get("/")
async def index():
return FileResponse("/workspace/index.html")
@app.post("/api/generate")
async def generate(prompt: str = Form(...)):
if "pipe" not in ml_models:
raise HTTPException(status_code=503, detail="Model not loaded")
logger.info(f"收到任务: {prompt[:50]}...")
try:
pipe = ml_models["pipe"]
# --- 官方代码逻辑移植 3: 关键推理参数 ---
image = pipe(
prompt=prompt,
height=1024, # 官方默认分辨率
width=1024,
num_inference_steps=9, # 官方设置: Turbo 模型只需要很少的步数
guidance_scale=0.0, # 【核心修复】Turbo 模型必须设为 0,否则会黑图或乱码
output_type="pil"
).images[0]
buffer = BytesIO()
image.save(buffer, format="PNG")
base64_img = base64.b64encode(buffer.getvalue()).decode()
return {"image": base64_img}
except Exception as e:
logger.error(f"生成出错: {e}")
raise HTTPException(status_code=500, detail=str(e))
if __name__ == "__main__":
uvicorn.run(app, host=HOST, port=PORT)
2.新建html文件,名字为:index.html,代码如下:
注: 要修改为主机的IP,如下图。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Z-Image-Turbo WebUI</title>
<style>
:root {
--primary-color: #4a90e2;
--primary-hover: #357abd;
--bg-color: #f5f7fa;
--card-bg: #ffffff;
--text-color: #333333;
--border-radius: 12px;
--shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Helvetica Neue", Arial, sans-serif;
background-color: var(--bg-color);
color: var(--text-color);
margin: 0;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.container {
background-color: var(--card-bg);
padding: 40px;
border-radius: var(--border-radius);
box-shadow: var(--shadow);
width: 100%;
max-width: 550px;
text-align: center;
transition: transform 0.3s ease;
}
h2 {
margin-top: 0;
margin-bottom: 30px;
color: #2c3e50;
font-weight: 700;
letter-spacing: -0.5px;
}
.input-group {
margin-bottom: 25px;
text-align: left;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #4a5568;
font-size: 14px;
}
textarea {
width: 100%;
padding: 14px 16px;
font-size: 16px;
border: 2px solid #e2e8f0;
border-radius: 8px;
box-sizing: border-box;
transition: all 0.3s ease;
outline: none;
background-color: #f8fafc;
font-family: inherit;
resize: none;
min-height: 52px;
overflow-y: hidden;
line-height: 1.5;
}
textarea:focus {
border-color: var(--primary-color);
background-color: #fff;
box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.1);
}
button {
background-color: var(--primary-color);
color: white;
border: none;
padding: 14px 30px;
font-size: 16px;
font-weight: 600;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s ease;
width: 100%;
box-shadow: 0 4px 6px rgba(74, 144, 226, 0.2);
}
button:hover {
background-color: var(--primary-hover);
transform: translateY(-1px);
box-shadow: 0 6px 8px rgba(74, 144, 226, 0.3);
}
button:active {
transform: translateY(0);
box-shadow: 0 2px 4px rgba(74, 144, 226, 0.2);
}
button:disabled {
background-color: #cbd5e0;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
.download-btn {
background-color: #48bb78;
margin-top: 15px;
display: none; /* Hidden by default */
box-shadow: 0 4px 6px rgba(72, 187, 120, 0.2);
}
.download-btn:hover {
background-color: #38a169;
box-shadow: 0 6px 8px rgba(72, 187, 120, 0.3);
}
.download-btn:active {
box-shadow: 0 2px 4px rgba(72, 187, 120, 0.2);
}
#result {
margin-top: 30px;
min-height: 60px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
img {
max-width: 100%;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
animation: fadeIn 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px) scale(0.98);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.loading {
display: inline-block;
width: 30px;
height: 30px;
border: 3px solid #f3f3f3;
border-top: 3px solid var(--primary-color);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.error-message {
color: #e53e3e;
background: #fff5f5;
padding: 12px 16px;
border-radius: 8px;
font-size: 14px;
border: 1px solid #fed7d7;
width: 100%;
box-sizing: border-box;
}
.status-text {
color: #718096;
font-size: 14px;
margin-top: 10px;
}
</style>
</head>
<body>
<div class="container">
<h2>Z-Image-Turbo WebUI</h2>
<div class="input-group">
<label for="prompt">Prompt 提示词</label>
<textarea
id="prompt"
placeholder="输入提示词,例如:a cat in the snow"
rows="1"
></textarea>
</div>
<button id="generateBtn" onclick="generate()">生成图片</button>
<button id="downloadBtn" class="download-btn" onclick="downloadImage()">
下载图片
</button>
<div id="result"></div>
</div>
<script>
// Auto-resize textarea
const promptInput = document.getElementById("prompt");
promptInput.addEventListener("input", function () {
this.style.height = "auto";
this.style.height = this.scrollHeight + "px";
});
async function generate() {
const resultDiv = document.getElementById("result");
const btn = document.getElementById("generateBtn");
const downloadBtn = document.getElementById("downloadBtn");
const prompt = promptInput.value.trim();
if (!prompt) {
// Shake animation for empty input
promptInput.style.borderColor = "#e53e3e";
promptInput.focus();
setTimeout(() => {
promptInput.style.borderColor = "";
}, 2000);
return;
}
// UI Loading State
btn.disabled = true;
downloadBtn.style.display = "none"; // Hide download button during generation
btn.innerHTML =
'<span class="loading" style="width: 16px; height: 16px; border-width: 2px; vertical-align: middle; margin-right: 8px;"></span> 生成中...';
resultDiv.innerHTML = ""; // Clear previous result
const formData = new FormData();
formData.append("prompt", prompt);
try {
let resp = await fetch("http://IP:8000/api/generate", {
method: "POST",
body: formData,
});
if (!resp.ok) {
throw new Error(`HTTP error! status: ${resp.status}`);
}
let data = await resp.json();
if (data.image) {
resultDiv.innerHTML = `<img src="data:image/png;base64,${data.image}" alt="Generated Image" />`;
downloadBtn.style.display = "block"; // Show download button
} else {
throw new Error("No image data received");
}
} catch (error) {
console.error(error);
resultDiv.innerHTML = `<div class="error-message">生成失败: ${error.message}</div>`;
} finally {
btn.disabled = false;
btn.textContent = "生成图片";
}
}
function downloadImage() {
const img = document.querySelector("#result img");
if (img) {
const a = document.createElement("a");
a.href = img.src;
a.download = `z-image-${Date.now()}.png`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
}
// Allow Enter key to submit (Shift+Enter for new line)
promptInput.addEventListener("keydown", function (event) {
if (event.key === "Enter" && !event.shiftKey) {
event.preventDefault();
generate();
}
});
</script>
</body>
</html>
四、拉取镜像并运行
1.我已经构建一个镜像了,直接使用下面的命令拉取就行
docker pull registry.cn-hangzhou.aliyuncs.com/rihebty/zimage-server:v1
2.使用以下命令运行,如果你的端口和路径跟我的不一样记得修改一下
docker run --gpus all -d \
-p 8000:8000 \
-v /home/z-image/Z-Image-Turbo:/data/models/Z-Image-Turbo \
-v /home/z-image/index.html:/workspace/index.html:ro \
-v /home/z-image/zimage_server.py:/workspace/zimage_server.py:ro \
--name zimage-container \
registry.cn-hangzhou.aliyuncs.com/rihebty/zimage-server:v1
3.查看是否运行成功
输入查看日志文件
docker logs -f zimage-container
运行成功会显示如下图:
五、访问web页面进行生图
1.在浏览器输入:http://服务器IP:8000,下面是我生成的图片效果
提示词如下:
一张融合“写实质感 + 二次元画风 + 高级 CG 渲染”的《鬼灭之刃》主题海报。主体是一位站在画面中央的灶门炭治郎(可替换为任意角色),保持原有动漫面部特征与配色,但皮肤、眼睛、发丝与纹理采用写实级 CG 材质表现。角色手持日轮刀,刀刃释放淡蓝色“水之呼吸” CG 能量特效,能量呈流体状发光纹路,带有轻微粒子散射效果。他脚下是一块从雾气弥漫的浅水水面中浮出的深色湿润板岩,倒影清晰、略带二次元柔光边缘。
光照与氛围(写实光影 + 动漫色调):
金黄清晨体积光穿透森林树叶倾泻而下,形成真实物理感十足的体积雾与丁达尔效应,同时保留二次元式干净、高对比的光影边缘。斑驳的光斑落在炭治郎的脸部、刀刃与衣褶上,使写实材质与动漫色块互相融合。刀刃能量在水面上形成微光焦散 Caustics,与周围冷色雾气形成强烈视觉对比,制造史诗感。
细节与材质(写实纹理 × 二次元线条):
写实但带柔化处理的角色皮肤、精致的眼部渲染、半写实发丝质感。额头伤痕、耳饰与日轮刀纹路清晰,既保留原动画设计,又加入微微的金属反射与磨损细节。服装布料带有真实褶皱纹理,但边缘保留柔和的二次元描线。板岩湿润且带有微苔纹理,水面拥有真实反射与轻微折射,同时保持略微卡通化的干净色彩。背景灰烬微粒与落花片呈 CG 粒子效果,略带景深虚化。
技术规格(CG 渲染 + 动漫视觉):
采用虚幻引擎 5 超写实 CG 渲染风格混合高饱和、干净色块的二次元调色。
8K 画质,面部与刀刃 Logo 区域超高清对焦。
电影级光线追踪、漫反射 + 高光双层材质处理。
动漫式色彩分级(高对比、清晰色层)与写实 CG 高动态范围光照融合。
整体呈现电影海报级质感、CG 角色精度和动漫角色辨识度。


六、docker构建镜像文件,你可以自己构建
1.把上面新建的两个文件放在和 Dockerfile 同一目录下,内容在下面
2.构建命令
docker build -t zimage-server:v1 .
3.Dockerfile文件内容
# 1. 基础镜像:改用 CUDA 12.4 (目前 PyTorch 支持最好的高版本)
# 即使物理机驱动是 CUDA 13,容器内用 CUDA 12.4 也是完全兼容的
FROM nvidia/cuda:13.0.1-runtime-ubuntu22.04
# 2. 环境变量设置
ENV DEBIAN_FRONTEND=noninteractive \
LANG=C.UTF-8 \
LC_ALL=C.UTF-8 \
PYTHONUNBUFFERED=1 \
# 显存优化参数
PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True \
# 设置默认模型路径,方便 Python 代码读取
MODEL_PATH=/data/models/Z-Image-Turbo
# 3. 安装系统依赖
# 增加了 git (pip 安装 git+... 需要) 和 curl
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 python3-pip python3-venv \
git build-essential wget curl ca-certificates \
libgl1 libglib2.0-0 libsm6 libxrender1 libxext6 \
&& rm -rf /var/lib/apt/lists/*
# 4. 升级 pip
RUN python3 -m pip install --upgrade pip
WORKDIR /workspace
# 5. 安装 PyTorch (CUDA 13 版本)
# 这里使用了 cu130 的源,确保下载到支持 GPU 的版本
RUN pip install --no-cache-dir torch torchvision --index-url https://download.pytorch.org/whl/cu130
# 6. 安装 Diffusers 和相关 AI 依赖
# 补充了 sentencepiece 和 protobuf (Transformers 必须)
RUN pip install --no-cache-dir git+https://wget.la/https://github.com/huggingface/diffusers.git
RUN pip install --no-cache-dir transformers accelerate safetensors modelscope sentencepiece protobuf
# 7. 安装 Web 服务依赖
RUN pip install --no-cache-dir fastapi "uvicorn[standard]" pillow aiofiles python-multipart
# 8. 复制代码文件
COPY zimage_server.py /workspace/zimage_server.py
# 如果你有 index.html 也复制进去,没有则注释掉
COPY index.html /workspace/index.html
# 9. 暴露端口
EXPOSE 8000
# 10. 启动命令
# 确保 host 是 0.0.0.0 才能被外部访问
CMD ["uvicorn", "zimage_server:app", "--host", "0.0.0.0", "--port", "8000"]
更多推荐


所有评论(0)