em-script/scripts/packages/install_docker.sh

575 lines
15 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# Debian 12 Docker 安装脚本 (使用国内源)
# 从国内镜像源安装 Docker CE
# 支持多源切换和自动选择最佳源
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 日志函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 定义多个 Docker 源
declare -A DOCKER_SOURCES=(
["aliyun"]="https://mirrors.aliyun.com/docker-ce"
["tsinghua"]="https://mirrors.tuna.tsinghua.edu.cn/docker-ce"
["ustc"]="https://mirrors.ustc.edu.cn/docker-ce"
["huawei"]="https://mirrors.huaweicloud.com/docker-ce"
["163"]="https://mirrors.163.com/docker-ce"
["azure"]="https://mirror.azure.cn/docker-ce"
["official"]="https://download.docker.com"
)
# 源优先级顺序
DOCKER_SOURCE_ORDER=("aliyun" "ustc" "huawei" "163" "tsinghua" "azure" "official")
# 当前使用的源
CURRENT_SOURCE=""
CURRENT_SOURCE_URL=""
# 测试单个源的连接性
test_source() {
local source_name="$1"
local source_url="${DOCKER_SOURCES[$source_name]}"
local codename=$(lsb_release -cs)
local arch=$(dpkg --print-architecture)
local test_url="${source_url}/linux/debian/dists/${codename}/stable/binary-${arch}/Packages"
# 测试连接
local start_time=$(date +%s%N)
if curl -sfL --connect-timeout 5 --max-time 15 "$test_url" -o /dev/null 2>/dev/null; then
local end_time=$(date +%s%N)
local time_diff=$(( (end_time - start_time) / 1000000 ))
echo "$time_diff"
return 0
fi
return 1
}
# 测试所有源并选择最佳
select_best_source() {
log_info "测试 Docker 源连接速度..."
local best_source=""
local best_time=999999
for source_name in "${DOCKER_SOURCE_ORDER[@]}"; do
local source_url="${DOCKER_SOURCES[$source_name]}"
printf " %-12s %-50s " "$source_name" "$source_url"
if time_ms=$(test_source "$source_name"); then
echo -e "${GREEN}${NC} ${time_ms}ms"
if (( time_ms < best_time )); then
best_time=$time_ms
best_source=$source_name
fi
else
echo -e "${RED}${NC} 连接失败"
fi
done
if [[ -n "$best_source" ]]; then
log_success "选择最佳源: $best_source (${best_time}ms)"
CURRENT_SOURCE="$best_source"
CURRENT_SOURCE_URL="${DOCKER_SOURCES[$best_source]}"
return 0
else
log_error "所有源均不可用"
return 1
fi
}
# 使用指定源
use_source() {
local source_name="$1"
if [[ -z "${DOCKER_SOURCES[$source_name]}" ]]; then
log_error "未知的源: $source_name"
log_info "可用源: ${!DOCKER_SOURCES[*]}"
return 1
fi
log_info "测试指定源: $source_name"
if test_source "$source_name" >/dev/null; then
CURRENT_SOURCE="$source_name"
CURRENT_SOURCE_URL="${DOCKER_SOURCES[$source_name]}"
log_success "使用源: $source_name ($CURRENT_SOURCE_URL)"
return 0
else
log_error "$source_name 不可用"
return 1
fi
}
# 尝试下一个源
try_next_source() {
local current="$CURRENT_SOURCE"
local found_current=false
for source_name in "${DOCKER_SOURCE_ORDER[@]}"; do
if [[ "$found_current" == "true" ]]; then
if test_source "$source_name" >/dev/null; then
CURRENT_SOURCE="$source_name"
CURRENT_SOURCE_URL="${DOCKER_SOURCES[$source_name]}"
log_info "切换到备用源: $source_name"
return 0
fi
fi
if [[ "$source_name" == "$current" ]]; then
found_current=true
fi
done
# 如果没有找到,从头开始尝试
for source_name in "${DOCKER_SOURCE_ORDER[@]}"; do
if [[ "$source_name" != "$current" ]]; then
if test_source "$source_name" >/dev/null; then
CURRENT_SOURCE="$source_name"
CURRENT_SOURCE_URL="${DOCKER_SOURCES[$source_name]}"
log_info "切换到备用源: $source_name"
return 0
fi
fi
done
return 1
}
# 检查系统要求
check_system() {
log_info "检查系统要求..."
# 检查是否为 Debian 系统
if ! command -v lsb_release &> /dev/null; then
apt update && apt install -y lsb-release
fi
local distro=$(lsb_release -si)
local version=$(lsb_release -sr)
if [[ "$distro" != "Debian" ]]; then
log_error "此脚本仅支持 Debian 系统"
exit 1
fi
if [[ "${version%%.*}" -lt 10 ]]; then
log_error "此脚本需要 Debian 10 或更高版本"
exit 1
fi
# 检查架构
local arch=$(dpkg --print-architecture)
if [[ "$arch" != "amd64" && "$arch" != "arm64" ]]; then
log_error "不支持的架构: $arch"
exit 1
fi
log_success "系统检查通过: $distro $version ($arch)"
}
# 卸载旧版本
uninstall_old_versions() {
log_info "卸载可能存在的旧版本 Docker..."
# 停止所有运行中的容器
if command -v docker &> /dev/null; then
docker stop $(docker ps -aq) 2>/dev/null || true
fi
# 卸载旧版本
apt remove -y docker docker-engine docker.io containerd runc docker-compose 2>/dev/null || true
# 清理相关文件
rm -rf /var/lib/docker /etc/docker
rm -f /etc/apt/sources.list.d/docker.list
rm -f /etc/apt/keyrings/docker.gpg
log_success "旧版本清理完成"
}
# 安装依赖包
install_dependencies() {
log_info "安装依赖包..."
apt update
apt install -y \
ca-certificates \
curl \
gnupg \
lsb-release \
apt-transport-https \
software-properties-common
log_success "依赖包安装完成"
}
# 添加 Docker 仓库
add_docker_repository() {
if [[ -z "$CURRENT_SOURCE_URL" ]]; then
log_error "未选择 Docker 源"
return 1
fi
log_info "添加 Docker 仓库 (使用 $CURRENT_SOURCE 源)..."
log_info "源地址: $CURRENT_SOURCE_URL"
# 创建密钥目录
mkdir -p /etc/apt/keyrings
# 移除旧的密钥和仓库配置
rm -f /etc/apt/keyrings/docker.gpg
rm -f /etc/apt/sources.list.d/docker.list
# 下载并添加 GPG 密钥
log_info "下载 GPG 密钥..."
if ! curl -fsSL "${CURRENT_SOURCE_URL}/linux/debian/gpg" | gpg --dearmor -o /etc/apt/keyrings/docker.gpg 2>/dev/null; then
log_warning "$CURRENT_SOURCE 下载 GPG 密钥失败,尝试官方源..."
curl -fsSL "https://download.docker.com/linux/debian/gpg" | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
fi
chmod a+r /etc/apt/keyrings/docker.gpg
# 添加仓库
local arch=$(dpkg --print-architecture)
local codename=$(lsb_release -cs)
echo "deb [arch=${arch} signed-by=/etc/apt/keyrings/docker.gpg] ${CURRENT_SOURCE_URL}/linux/debian ${codename} stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
log_success "Docker 仓库添加完成 (源: $CURRENT_SOURCE)"
}
# 安装 Docker
install_docker() {
log_info "安装 Docker CE..."
local max_retries=3
local retry_count=0
while (( retry_count < max_retries )); do
# 更新包索引
log_info "更新包索引..."
if ! apt update 2>&1; then
log_warning "apt update 失败"
fi
# 尝试安装 Docker CE
log_info "尝试从 $CURRENT_SOURCE 安装 Docker..."
# 使用 set +e 暂时禁用错误退出
set +e
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 2>&1
local install_result=$?
set -e
if [[ $install_result -eq 0 ]]; then
log_success "Docker CE 安装完成 (源: $CURRENT_SOURCE)"
return 0
fi
retry_count=$((retry_count + 1))
log_warning "安装失败 (尝试 $retry_count/$max_retries)"
if (( retry_count < max_retries )); then
log_info "尝试切换到其他源..."
if try_next_source; then
# 重新配置仓库
add_docker_repository
else
log_error "没有更多可用的源"
break
fi
fi
done
log_error "Docker 安装失败,已尝试所有可用源"
log_info "建议手动检查网络连接或稍后重试"
exit 1
}
# 配置 Docker
configure_docker() {
log_info "配置 Docker..."
# 创建配置目录
mkdir -p /etc/docker
# 配置 daemon.json
cat > /etc/docker/daemon.json << 'EOF'
{
"registry-mirrors": [
"https://docker-proxy.syy.1msoft.cn",
"https://registry.docker-cn.com",
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com",
"https://mirror.baidubce.com"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
},
"storage-driver": "overlay2",
"exec-opts": ["native.cgroupdriver=systemd"],
"iptables": false
}
EOF
# 配置 containerd
mkdir -p /etc/containerd
containerd config default | tee /etc/containerd/config.toml > /dev/null
# 重新加载配置
systemctl daemon-reload
log_success "Docker 配置完成"
}
# 启动服务
start_services() {
log_info "启动 Docker 服务..."
# 启动服务
systemctl enable docker
systemctl enable containerd
systemctl start docker
systemctl start containerd
# 等待服务启动
sleep 2
# 检查服务状态
if systemctl is-active --quiet docker; then
log_success "Docker 服务启动成功"
else
log_error "Docker 服务启动失败"
journalctl -u docker --no-pager -n 20
exit 1
fi
}
# 配置用户权限
configure_user_permissions() {
log_info "配置用户权限..."
# 获取当前用户
local current_user=""
if [[ -n "$SUDO_USER" ]]; then
current_user="$SUDO_USER"
elif [[ -n "$USER" && "$USER" != "root" ]]; then
current_user="$USER"
fi
if [[ -n "$current_user" ]]; then
# 检查用户是否存在
if id "$current_user" &>/dev/null; then
usermod -aG docker "$current_user"
log_success "已将用户 $current_user 添加到 docker 组"
log_warning "请重新登录或运行 'newgrp docker' 以使权限生效"
fi
fi
}
# 测试安装
test_installation() {
log_info "测试 Docker 安装..."
# 检查版本
local docker_version=$(docker --version)
local docker_compose_version=$(docker compose version 2>/dev/null || echo "docker-compose plugin")
log_success "Docker 版本: $docker_version"
log_info "Docker Compose: $docker_compose_version"
# 运行测试容器
log_info "运行测试容器..."
if docker run --rm hello-world >/dev/null 2>&1; then
log_success "Docker 测试通过!"
else
log_warning "Docker 运行测试失败,但安装可能成功。请检查网络连接。"
fi
# 显示使用信息
cat << 'EOF'
Docker 安装完成!
常用命令:
docker --version # 查看版本
docker run hello-world # 运行测试容器
docker ps -a # 查看所有容器
docker images # 查看镜像
docker pull <image> # 拉取镜像
docker build -t <name> . # 构建镜像
服务管理:
sudo systemctl start docker # 启动服务
sudo systemctl stop docker # 停止服务
sudo systemctl restart docker # 重启服务
配置说明:
- 已配置国内镜像加速器
- 日志轮转已配置 (最大100M保留3个文件)
- 使用 overlay2 存储驱动
- 已启用 systemd cgroup 驱动
EOF
}
# 列出可用源
list_sources() {
echo "可用的 Docker 源:"
echo "=========================================="
for source_name in "${DOCKER_SOURCE_ORDER[@]}"; do
printf " %-12s %s\n" "$source_name" "${DOCKER_SOURCES[$source_name]}"
done
echo ""
echo "默认优先级: ${DOCKER_SOURCE_ORDER[*]}"
}
# 显示帮助信息
show_help() {
cat << EOF
Debian 12 Docker 安装脚本
此脚本支持多个国内镜像源,自动选择最佳源或手动指定源。
用法: $0 [选项]
选项:
-h, --help 显示此帮助信息
-f, --force 强制重新安装(不检查现有安装)
-s, --source NAME 指定使用的源 (aliyun, tsinghua, ustc, huawei, 163, azure, official)
-t, --test 仅测试所有源的连接速度
-l, --list 列出所有可用源
可用源:
aliyun 阿里云镜像 (推荐)
tsinghua 清华大学镜像
ustc 中科大镜像
huawei 华为云镜像
163 网易镜像
azure 微软 Azure 镜像
official Docker 官方源
示例:
$0 # 自动选择最佳源安装
$0 -s aliyun # 使用阿里云源安装
$0 -s ustc --force # 使用中科大源强制重装
$0 --test # 测试所有源速度
$0 --list # 列出所有可用源
EOF
}
# 主函数
main() {
local force_install=false
local specified_source=""
local test_only=false
# 解析参数
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
show_help
exit 0
;;
-f|--force)
force_install=true
shift
;;
-s|--source)
specified_source="$2"
shift 2
;;
-t|--test)
test_only=true
shift
;;
-l|--list)
list_sources
exit 0
;;
*)
log_error "未知选项: $1"
show_help
exit 1
;;
esac
done
# 检查是否为 root 用户
if [[ $EUID -ne 0 ]]; then
log_error "请使用 root 用户或 sudo 运行此脚本"
exit 1
fi
# 仅测试模式
if [[ "$test_only" == true ]]; then
select_best_source
exit 0
fi
# 检查是否已安装
if command -v docker &> /dev/null && [[ "$force_install" != true ]]; then
log_warning "Docker 已经安装。如需重新安装,请使用 --force 选项。"
docker --version
exit 0
fi
log_info "开始安装 Docker CE..."
echo
check_system
# 选择源
if [[ -n "$specified_source" ]]; then
if ! use_source "$specified_source"; then
log_warning "指定源不可用,自动选择最佳源..."
select_best_source || exit 1
fi
else
select_best_source || exit 1
fi
echo
uninstall_old_versions
install_dependencies
add_docker_repository
install_docker
configure_docker
start_services
configure_user_permissions
test_installation
echo
log_success "Docker 安装完成!(使用源: $CURRENT_SOURCE)"
}
# 执行主函数
main "$@"