由於個人對於作業系統某種程度上的潔癖,所以對於這樣的東西把他放在 Docker 裡面也是挺合理的。雖然說,這兩個都有人把他做到 Docker 裡面去,但是實際上在跑起來的時候,問題還是挺多的。
請留意,以下列舉的都是我自己遇到的狀況,每個人的環境不同,或許會略有差異。本篇文章的操作可能會有時校性,且可能也跟硬體有關,請自行斟酌使用。
主要環境
Linux 22.04LTS Server
Intel 13th i5 with 64GB DDR5
NVIDIA A4500 20GB GDDR6 ECC
Stable Diffusion WebUI Docker
目前在 Github 上最為通用的應該是這一組,
以本篇文章的這個版本 5.0.1 來說,應該已經算是相對穩定的版本了。而,由於我是把他放在伺服器端,所以我並沒有 X 可以使用,雖然有一點不方便,但也還算可以運作。
然後,我們把他抓下來之後,你可以直接跑應該不會有太大的問題,基本上這個版本已經比之前好很多了 我大概撞牆撞了兩個月。我這邊僅以 AUTOMATIC1111 來說明(應該還是這個介面的使用者居多吧)。
你可以使用原作者的方式來安裝,
docker compose --profile download up --build
# wait until its done, then:
docker compose --profile auto up --build
應該會經過一個漫長的等待,當你看到畫面出現,
Running on local URL: http://localhost:7860
這個時候用瀏覽器打開網址就能看到 Stable Diffusion WebUI 的操作界面了。
Stable Diffusion WebUI Docker Compose
當然,依照原作者的方式安裝不會有太多問題,但,由於所有的資料都會放在 ./data
底下,然後所有的輸出都會放在 ./output
底下,這個時候就會有點麻煩。我剛剛說過了,我是放在遠端的伺服器上,所以我並沒有很方便的介面可以隨時去更動或是察看這兩個資料夾。
如果你在本地端跑,或是你就是單純跑在你自己在用的機器上,就沒差!
所以,我需要設定一個 docker-compose.yaml
來設定這台容器,讓我比較方便可以存取我所需要的東西。
但是!
由於原本 AUTOMATIC1111 的 entrypoint.sh
利用了一些軟連結來掛載資料夾。所以你看原作者所提供的 docker-compose.yaml
的掛載方式,
x-base_service: &base_service
ports:
- "7860:7860"
volumes:
- &v1 ./data:/data
- &v2 ./output:/output
也就是說,我們在 Container 裡面有 /data
與 /output
相對應的資料夾,來放運作的時候會產生的資料。而掛載這些資料夾的人,就是 entrypoint.sh
這個檔案的這個部分,
# main
MOUNTS["${ROOT}/models/Stable-diffusion"]="/data/StableDiffusion"
MOUNTS["${ROOT}/models/VAE"]="/data/VAE"
MOUNTS["${ROOT}/models/Codeformer"]="/data/Codeformer"
MOUNTS["${ROOT}/models/GFPGAN"]="/data/GFPGAN"
MOUNTS["${ROOT}/models/ESRGAN"]="/data/ESRGAN"
MOUNTS["${ROOT}/models/BSRGAN"]="/data/BSRGAN"
MOUNTS["${ROOT}/models/RealESRGAN"]="/data/RealESRGAN"
MOUNTS["${ROOT}/models/SwinIR"]="/data/SwinIR"
MOUNTS["${ROOT}/models/ScuNET"]="/data/ScuNET"
MOUNTS["${ROOT}/models/LDSR"]="/data/LDSR"
MOUNTS["${ROOT}/models/hypernetworks"]="/data/Hypernetworks"
MOUNTS["${ROOT}/models/torch_deepdanbooru"]="/data/Deepdanbooru"
MOUNTS["${ROOT}/models/BLIP"]="/data/BLIP"
MOUNTS["${ROOT}/models/midas"]="/data/MiDaS"
MOUNTS["${ROOT}/models/Lora"]="/data/Lora"
MOUNTS["${ROOT}/embeddings"]="/data/embeddings"
MOUNTS["${ROOT}/config.json"]="/data/config/auto/config.json"
MOUNTS["${ROOT}/ui-config.json"]="/data/config/auto/ui-config.json"
MOUNTS["${ROOT}/extensions"]="/data/config/auto/extensions"
# extra hacks
MOUNTS["${ROOT}/repositories/CodeFormer/weights/facelib"]="/data/.cache"
其實這對我來說有點困擾,所以,我在建立這個 Docker 映象檔時,把這個部分改掉了。
MOUNTS["${ROOT}/models"]="/data/models"
MOUNTS["${ROOT}/embeddings"]="/data/embeddings"
MOUNTS["${ROOT}/config.json"]="/data/config/auto/config.json"
MOUNTS["${ROOT}/ui-config.json"]="/data/config/auto/ui-config.json"
MOUNTS["${ROOT}/extensions"]="/data/config/auto/extensions"
然後如果你之前建立過映象檔,可以去 AUTOMATIC1111 資料夾中重新建立一份新的。接著,這個時候我們就來準備自己的 docker-compose.yaml
來啟動。
stable-diffusion-webui:
image: sd-auto:49
environment:
- CLI_ARGS=--allow-code --medvram --xformers --enable-insecure-extension-access --api
ports:
- "7860:7860"
volumes:
- /mnt/ext/sd-data/.cache:/data/.cache
- /mnt/ext/sd-data/config:/data/config
- /mnt/ext/sd-models:/data/models
- /mnt/ext/sd-embeddings:/data/embeddings
- /mnt/ext/sd-repositories:/stable-diffusion-webui/repositories
- /mnt/ext/sd-textual_inversion_templates:/stable-diffusion-webui/textual_inversion_templates
- /mnt/ext/sd-textual_inversion:/stable-diffusion-webui/textual_inversion
- /mnt/ext/sd-output:/output
stop_signal: SIGINT
deploy:
resources:
reservations:
devices:
- driver: nvidia
device_ids: ['0']
capabilities: [gpu]
restart_policy:
delay: 5s
max_attempts: 10
window: 120s
你會發現我把很多東西用 Volumes 拿到外面來放,我大概解釋一下,
/data/.cache
在容器裡被指向/root/.cache
,如果你想每次啟動機器都重抓一次本來使用的時候,曾經快取的一些資料的話,那就不用設定沒差。/data/config
,/data/embeddings
,/data/models
,/output
都另外放。/stable-diffusion-webui/repositories
有些額外的資料會被拉下來放在這裡,如果你的容器重起,可能會要重抓。/stable-diffusion-webui/textual_inversion_templates
,/stable-diffusion-webui/textual_inversion
放外面比較好更新。
請注意!在啟動他之前,請你把上一次啟動所產生的檔案放到你的新的位置。
啟動之後就可以不管他了。由於我的 /mnt/ext/sd-*
這些資料夾,全部都是遠端連回 NAS,所以我就可以在遠端透過連回 NAS 的方式,方便察看到我所需要的檔案了。
Stable Diffusion WebUI patch
我要怎麼在不更動映象檔的情況下,打一點 patch 給容器去修理一些問題?我們剛剛有看到 entrypoint.sh
檔案,其實他最後面有這一段,
if [ -f "/data/config/auto/startup.sh" ]; then
pushd ${ROOT}
. /data/config/auto/startup.sh
popd
fi
也就是說,你的 ./data/config/auto/startup.sh
應該會有這個檔案存在。那麼,如果想要對容器做什麼動作(在 WebUI 還沒被啟動之前),就可以透過修改這個檔案來做。
例如,如果你有裝 Additional Networks 這個外掛,他有一個 models/lora
是要你放 Lora 模型的地方,我這邊直接把他連到 /data/models/Lora
去比較方便,這樣我們只要把 Lora 放在同一個地方就好了。由於 extensions
被我們掛到其他地方,那這個外掛的模組讀取就會需要重新指定路徑,不然他會讀不到,
# 放在 startup.sh 最後面
# link extensions to lora
if [ -d "${ROOT}/extensions/sd-webui-additional-networks/models/lora" ] || [ -L "${ROOT}/extensions/sd-webui-additional-networks/models/lora" ] ; then
rm -rf "${ROOT}/extensions/sd-webui-additional-networks/models/lora"
fi
if [ -d "/data/models/Lora" ]; then
ln -sT /data/models/Lora "${ROOT}/extensions/sd-webui-additional-networks/models/lora"
fi
其中的 /data/models/Lora
就是我們剛剛在 docker-compose.yaml
當中所指定的路徑。而 Lora 則是在建立映象檔時本來就會被建立的一個資料夾。
額外的 Patch
由於現在可以支援 --nowebui
,對,他雖然叫做 WebUI 但是因為可以開啟 --api
模式,但是又不想使用他的 UI 的時候,就可以打開 --nowebui
來關閉。
但是,這個關閉的地方有一個 Bug Issue#7984,就是在開啟 --nowebui
的時候會讀不到 Lora 的模型。目前有 workaround 的解法,我把他弄成 patch,請自行取用。
--- webui.py 2023-03-28 06:43:35.437298130 +0000
+++ webui-patch.py 2023-03-28 06:44:11.345979601 +0000
@@ -220,6 +220,7 @@
api = create_api(app)
modules.script_callbacks.app_started_callback(None, app)
+ modules.script_callbacks.before_ui_callback()
print(f"Startup time: {startup_timer.summary()}.")
api.launch(server_name="0.0.0.0" if cmd_opts.listen else "127.0.0.1", port=cmd_opts.port if cmd_opts.port else 7861)
就把這檔案找地方放(例如我放在 /mnt/ext/sd-data/.cache/api_lora.patch
),然後在你的 ./data/config/auto/startup.sh
裡面加入,
#Patch Lora modules for api_only
patch -f -i /data/.cache/api_lora.patch /stable-diffusion-webui/webui.py
這樣他在啟動容器之前,就會先把 patch 打好,這樣若是我們啟動 --nowebui
的時候 Lora 的模型讀取才不會讀不到。
結語
裝在 Docker 裡面真的好很多!因為那個 Pytorch 跟那一堆外掛要裝的東西實在太多,如果裝在主開發環境下超可怕的!