创建macvlan
配置块
docker network create -d macvlan \
--subnet=10.0.0.0/22 \
--gateway=10.0.0.10 \
--ipv6 \
--subnet=fd03:6df6:ddfa::/64 --gateway=fd03:6df6:ddfa::1 \
-o parent=enp6s18 \
macvlan_net_v6Docker 安装、镜像、网络与 Docker Compose 服务模板。
docker network create -d macvlan \
--subnet=10.0.0.0/22 \
--gateway=10.0.0.10 \
--ipv6 \
--subnet=fd03:6df6:ddfa::/64 --gateway=fd03:6df6:ddfa::1 \
-o parent=enp6s18 \
macvlan_net_v6nano docker_image_backup.pyimport os
import subprocess
import datetime
import re
# === 配置部分 ===
BACKUP_DIR = "./docker_image_backups" # 备份存放目录
DATE_FORMAT = "%Y%m%d" # 备份文件的日期格式
# 确保备份目录存在
if not os.path.exists(BACKUP_DIR):
os.makedirs(BACKUP_DIR)
# === 获取所有 Docker 镜像,按创建时间排序 ===
def get_docker_images():
result = subprocess.run(
["docker", "images", "--format", "{{.Repository}}:{{.Tag}}"],
stdout=subprocess.PIPE, text=True
)
images = result.stdout.strip().split("\n")
image_list = [image.strip() for image in images if image.strip()]
return image_list
# === 解析用户输入的序号范围 ===
def parse_selection(selection, max_index):
selected_indexes = set()
for part in selection.split(","):
if "-" in part:
start, end = part.split("-")
if start.isdigit() and end.isdigit():
selected_indexes.update(range(int(start), int(end) + 1))
else:
return None # 非法输入
elif part.isdigit():
selected_indexes.add(int(part))
else:
return None # 非法输入
return {i for i in selected_indexes if 1 <= i <= max_index} # 过滤超出范围的数字
# === 备份 Docker 镜像 ===
def backup_images(image_list):
print("\n=== 可备份的 Docker 镜像 ===")
for i, name in enumerate(image_list, 1):
print(f"{i}. {name}")
selection = input("\n请输入要备份的镜像序号(支持 1,3-5,7):")
selected_indexes = parse_selection(selection, len(image_list))
if not selected_indexes:
print("\n❌ 输入错误,请输入有效的序号范围!")
return
for index in sorted(selected_indexes):
image_name = image_list[index - 1]
# 处理镜像名,去掉 ":tag" 部分,只保留仓库名
base_image_name = image_name.split(":")[0] # 只取仓库名
safe_image_name = base_image_name.replace("/", "_").replace(":", "_") # 适配文件名
backup_file = os.path.join(BACKUP_DIR, f"{safe_image_name}_{datetime.datetime.now().strftime(DATE_FORMAT)}.tar")
print(f"\n📦 备份镜像 {image_name} 到 {backup_file}...")
subprocess.run(["docker", "save", "-o", backup_file, image_name])
print(f"✅ 备份完成: {backup_file}")
# === 查找备份文件并导入 ===
def import_backup():
print("\n=== 备份文件列表 ===")
backup_files = sorted([f for f in os.listdir(BACKUP_DIR) if re.match(r".+_\d{8}\.tar$", f)], reverse=True)
if not backup_files:
print("❌ 没有找到符合格式的备份文件!")
return
for i, file in enumerate(backup_files, 1):
print(f"{i}. {file}")
selection = input("\n请输入要导入的备份文件序号:")
if not selection.isdigit() or not (1 <= int(selection) <= len(backup_files)):
print("❌ 输入错误,请输入有效的序号!")
return
selected_file = backup_files[int(selection) - 1]
selected_file_path = os.path.join(BACKUP_DIR, selected_file)
# 解析原始镜像名
match = re.match(r"(.+?)_\d{8}\.tar$", selected_file)
if not match:
print(f"❌ 解析镜像名称失败:{selected_file}")
return
base_image_name = match.group(1).replace("_", "/") # 还原成镜像格式
new_tag = f"{base_image_name}:{datetime.datetime.now().strftime(DATE_FORMAT)}"
print(f"\n📂 正在导入 {selected_file_path}...")
# `docker load` 并解析返回的镜像 ID
load_output = subprocess.run(["docker", "load", "-i", selected_file_path], stdout=subprocess.PIPE, text=True)
print(load_output.stdout) # 显示 `docker load` 输出,检查导入的镜像
# 提取 `docker load` 输出的镜像名称
loaded_image_name = None
match = re.search(r"Loaded image: (\S+)", load_output.stdout)
if match:
loaded_image_name = match.group(1)
print(f"✅ 发现已导入的镜像: {loaded_image_name}")
else:
print("❌ 未能解析导入的镜像名称,可能已导入但未命名")
return
print(f"\n🔖 重新打标签: {new_tag}")
subprocess.run(["docker", "tag", loaded_image_name, new_tag])
print(f"✅ 导入完成,镜像已重新命名为 {new_tag}")
# 确保新镜像可见
subprocess.run(["docker", "images"])
# === 主菜单 ===
def main():
while True:
print("\n=== Docker 镜像备份工具 ===")
print("1. 备份 Docker 镜像")
print("2. 导入 Docker 镜像备份")
print("3. 退出")
choice = input("请选择操作 (1/2/3):")
if choice == "1":
image_list = get_docker_images()
if image_list:
backup_images(image_list)
else:
print("❌ 没有可用的 Docker 镜像!")
elif choice == "2":
import_backup()
elif choice == "3":
print("🔚 退出程序")
break
else:
print("❌ 请输入有效的选项!")
if __name__ == "__main__":
main()sudo nano docker_install.sh#!/bin/bash
# 确保脚本以 root 权限运行
if [[ $EUID -ne 0 ]]; then
echo "请使用 root 用户或 sudo 运行此脚本"
exit 1
fi
# 安装所需的基本软件包
echo "正在安装必需的软件包..."
apt update
DEBIAN_FRONTEND=noninteractive apt install -y \
ca-certificates curl gnupg software-properties-common sudo cifs-utils
# 检查系统发行版和版本信息
. /etc/os-release
# 定义 Docker 源的 URL 和密钥
DOCKER_GPG_KEY_URL="https://download.docker.com/linux"
DOCKER_GPG_DIR="/etc/apt/keyrings"
DOCKER_GPG_FILE="${DOCKER_GPG_DIR}/docker.gpg"
# 检查系统发行版并设置相应的 Docker 源
echo "检测系统发行版..."
case "$ID" in
debian)
DOCKER_REPO_URL="${DOCKER_GPG_KEY_URL}/debian"
;;
ubuntu)
DOCKER_REPO_URL="${DOCKER_GPG_KEY_URL}/ubuntu"
;;
*)
echo "不支持的系统发行版: $ID"
exit 1
;;
esac
# 创建目录并配置 Docker GPG 密钥
echo "正在配置 Docker GPG 密钥..."
mkdir -p "$DOCKER_GPG_DIR"
curl -fsSL "${DOCKER_REPO_URL}/gpg" | gpg --dearmor -o "$DOCKER_GPG_FILE"
chmod a+r "$DOCKER_GPG_FILE"
# 添加 Docker 源到 sources.list
echo "正在添加 Docker 源..."
echo "deb [arch=amd64 signed-by=${DOCKER_GPG_FILE}] ${DOCKER_REPO_URL} ${VERSION_CODENAME} stable" | tee /etc/apt/sources.list.d/docker.list
# 更新软件包列表
echo "正在更新软件包列表..."
apt update
# 安装 Docker 及相关插件
echo "正在安装 Docker 组件..."
DEBIAN_FRONTEND=noninteractive apt install -y \
docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 启用并启动 Docker 服务
echo "正在重新加载 systemctl 并启动 Docker..."
systemctl daemon-reexec
systemctl enable --now docker
# 验证 Docker 是否安装成功
echo "验证 Docker 安装..."
if ! docker --version &>/dev/null; then
echo "Docker 安装失败,请检查错误日志!"
exit 1
fi
echo "Docker 安装完成,当前版本:$(docker --version)"sudo bash docker_insatll.shdocker compose up -ddocker compose downmkdir -p /app/alist && cd /app/alistnano docker-compose.ymlservices:
alist:
container_name: alist
restart: always
volumes:
- ./data:/opt/alist/data
ports:
- '55244:5244'
environment:
- 'PUID=0'
- 'PGID=0'
- 'UMASK=022'
image: xhofe/alist:v3.24.0
network_mode: "bridge"docker compose up -dmkdir -p /app/ddns && cd /app/ddnsnano docker-compose.ymlservices:
ddns:
image: jeessy/ddns-go:v5.5.1
container_name: ddns
network_mode: "host"
volumes:
- /app/ddns:/root
restart: always
command: -l :9877 -f 600docker compose up -dmkdir -p /app/gitlib && cd /app/gitlibnano docker-compose.ymlservices:
git:
image: gitlab/gitlab-ce:16.2.0-ce.0
container_name: gitlab
network_mode: "bridge"
ports:
- 30022:22
- 30880:80
volumes:
- /app/gitlab/etc:/etc/gitlab
- /app/gitlab/log:/var/log/gitlab
- /app/gitlab/opt:/var/opt/gitlab
restart: alwaysdocker compose up -dmkdir -p /app/gogs && cd /app/gogsnano docker-compose.ymlservices:
gogs:
image: gogs/gogs:0.13
container_name: Gogs
network_mode: "bridge"
ports:
- 10022:22
- 10880:3000 #optional
volumes:
- /app/gogs:/data
restart: alwaysdocker compose up -dmkdir -p /app/halo && cd /app/halonano docker-compose.ymlservices:
halo:
image: halohub/halo:2.5.2
container_name: halo
ports:
- 8890:8090
volumes:
- ./halo:/root/.halo2
command:
- "--halo.security.initializer.superadminusername=admin"
- "--halo.security.initializer.superadminpassword=P@88w0rd"
restart: always
network_mode: "bridge"docker compose up -dmkdir -p /app/mysql && cd /app/mysqlnano docker-compose.ymlservices:
gogs:
image: mysql:8.0.33-oracle
container_name: mysql
network_mode: "bridge"
environment:
- 'TZ=Asia/Shanghai'
- 'MYSQL_ROOT_PASSWORD=123456'
ports:
- 3306:3306
volumes:
- /app/mysql/data:/var/lib/mysql
- /app/mysql/mysqld.cnf:/etc/mysql/mysql.conf.d/mysqld.cnf
restart: alwaysdocker compose up -dnano /app/mysql/mysqld.cnf[mysqld]
skip-host-cache
skip-name-resolve
max_connections=2000
max_user_connections=100
character_set_server = utf8mb4mkdir -p /app/netdata && cd /app/netdatanano docker-compose.ymlservices:
netdata:
image: netdata/netdata:v1.39.1
container_name: netdata
ports:
- 19999:19999
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- PGID=0
cap_add:
- SYS_PTRACE
security_opt:
- apparmor=unconfined
restart: always
network_mode: "bridge"docker compose up -dmkdir -p /app/nginxwebui && cd /app/nginxwebuinano docker-compose.ymlservices:
nginxWebUi-server:
image: cym1102/nginxwebui:3.5.8
volumes:
- ./data:/home/nginxWebUI
environment:
BOOT_OPTIONS: "--server.port=8080"
privileged: true
network_mode: "host"docker compose up -dmkdir -p /app/pigallery && cd /app/pigallerynano docker-compose.ymlservices:
pigallery2:
image: bpatrik/pigallery2:1.9.5-debian-buster
container_name: pigallery2
environment:
- NODE_ENV=production
volumes:
- ./config:/app/data/config
- ./data:/app/data/db
- /data/picture:/app/data/images:ro
- ./tmp:/app/data/tmp
restart: always
networks:
macvlan_net_v6:
ipv4_address: 10.0.0.52
networks:
macvlan_net_v6:
external: truedocker compose up -dmkdir -p /app/reader && cd /app/readernano docker-compose.ymlservices:
reader:
image: hectorqin/reader:openj9-3.0.4
container_name: reader
volumes:
- ./log:/log
- ./storage:/storage
ports:
- 38081:8080
restart: always
network_mode: "bridge"docker compose up -dmkdir -p /app/heimdall && cd /app/heimdallnano docker-compose.ymlservices:
heimdall:
image: linuxserver/heimdall:2.5.4
container_name: heimdall
network_mode: "bridge"
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Shanghai
volumes:
- /app/heimdall:/config
ports:
- 33380:80
restart: alwaysnano /app/heimdall/php/php-local.ini; Edit this file to override php.ini directives
date.timezone = Asia/Shanghai
upload_max_filesize = 100M
post_max_size = 150Mdocker compose up -dmkdir -p /app/jellyfin && cd /app/jellyfinnano docker-compose.ymlservices:
jellyfin:
image: nyanmisaka/jellyfin:230801-amd64
container_name: jellyfin
networks:
macvlan_net_v6:
ipv4_address: 10.0.0.71
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Shanghai
volumes:
- ./data:/config
- /data:/media
- ./cache:/cache
restart: always
privileged: true
networks:
macvlan_net_v6:
external: trueservices:
jellyfin:
image: nyanmisaka/jellyfin:240401-amd64
container_name: jellyfin
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Shanghai
volumes:
- ./data:/config
- /data:/media
# - /sda:/nas
- ./cache:/cache
restart: always
privileged: true
network_mode: "bridge"
ports:
- '8096:8096'docker compose up -dmkdir -p /app/nginx && cd /app/nginxnano docker-compose.ymlservices:
app:
image: 'jc21/nginx-proxy-manager:2.10.4'
restart: unless-stopped
ports:
# These ports are in format <host-port>:<container-port>
- '22280:80' # Public HTTP Port
- '22443:443' # Public HTTPS Port
- '22281:81' # Admin Web Port
# Add any other Stream port you want to expose
# - '21:21' # FTP
# Uncomment the next line if you uncomment anything in the section
# environment:
# Uncomment this if you want to change the location of
# the SQLite DB file within the container
# DB_SQLITE_FILE: "/data/database.sqlite"
# Uncomment this if IPv6 is not enabled on your host
# DISABLE_IPV6: 'true'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencryptdocker compose up -dmkdir -p /app/portainer && cd /app/portainernano docker-compose.ymlservices:
portainer:
container_name: portainer
restart: always
volumes:
- /app/portainer:/data
- /var/run/docker.sock:/var/run/docker.sock
ports:
- '9000:9000'
image: portainer/portainer-ce:linux-amd64-2.16.2
network_mode: "bridge"docker compose up -dmkdir -p /app/cloudreve && cd /app/cloudrevenano docker-compose.ymlservices:
cloudreve:
image: xavierniu/cloudreve:3.5.1
container_name: cloudreve
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Shanghai
ports:
- 55212:5212
restart: always
network_mode: "bridge"
volumes:
- ./uploads:/cloudreve/uploads
- ./config:/cloudreve/config
- ./avatar:/cloudreve/avatar
- ./db:/cloudreve/db
- ./downloads:/cloudreve/uploads
- ./downloads:/downloadsmkdir -p /app/gitea && cd /app/giteanano docker-compose.ymlservices:
gogs:
image: bitnami/gitea:1.19.4-debian-11-r18
network_mode: "bridge"
environment:
- 'TZ=Asia/Shanghai'
- 'GITEA_ADMIN_PASSWORD=password123'
ports:
- 30880:3000
volumes:
- /app/gitea/override.ini:/bitnami/gitea/custom/conf/app.ini:ro
restart: alwaysdocker compose up -dmkdir -p /app/share && cd /app/sharenano docker-compose.ymlservices:
microbin:
image: danielszabo99/microbin:1
container_name: share-microbin
ports:
- 9635:8080
volumes:
- ./microbin:/app/pasta_data
restart: always
network_mode: "bridge"docker compose up -dmkdir -p /app/talebook && cd /app/talebooknano docker-compose.ymlservices:
talebook:
image: talebook/talebook:v3.6.4
container_name: talebook
ports:
- 38082:80
volumes:
- ./talebook:/data
restart: always
network_mode: "bridge"docker compose up -d