一、安装 docker

1.在线安装

# step 1: 安装必要的一些系统工具
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg

# step 2: 信任 Docker 的 GPG 公钥
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Step 3: 写入软件源信息
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu \
  "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
 
# Step 4: 安装Docker
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 安装指定版本的Docker-CE:
# Step 1: 查找Docker-CE的版本:
# apt-cache madison docker-ce
#   docker-ce | 17.03.1~ce-0~ubuntu-xenial | https://mirrors.aliyun.com/docker-ce/linux/ubuntu xenial/stable amd64 Packages
#   docker-ce | 17.03.0~ce-0~ubuntu-xenial | https://mirrors.aliyun.com/docker-ce/linux/ubuntu xenial/stable amd64 Packages
# Step 2: 安装指定版本的Docker-CE: (VERSION例如上面的17.03.1~ce-0~ubuntu-xenial)
# sudo apt-get -y install docker-ce=[VERSION]

systemd Docker 服务优化配置

完整配置目录结构

/etc/systemd/system/docker.service.d/
├── 10-proxy.conf          # 网络代理配置
├── 20-limits.conf         # 资源限制保护
├── 30-security.conf       # 安全加固
├── 40-logging.conf        # 日志优化
├── 50-performance.conf    # 性能调优  
├── 60-reliability.conf    # 可靠性增强
└── 70-containerd.conf     # containerd 专项优化
1. 10-proxy.conf (网络代理配置)
# /etc/systemd/system/docker.service.d/10-proxy.conf
# Purpose: 配置 Docker 守护进程的网络代理,用于受限网络环境
# SRE Note: 企业环境中必须配置,避免因网络问题导致服务中断
# Safety: 使用 NO_PROXY 保护内部网络,防止代理环路

[Service]
# 代理服务器配置 (替换为实际值)
Environment="HTTP_PROXY=http://proxy.corp.example.com:8080/"
Environment="HTTPS_PROXY=http://proxy.corp.example.com:8080/"

# 免代理列表 - 关键内部网络和域名
Environment="NO_PROXY=\
127.0.0.1,\
localhost,\
172.17.0.0/16,\          # Docker 默认网桥
10.0.0.0/8,\             # 企业内网
192.168.0.0/16,\         # 本地网络
.docker.internal,\       # Docker 内部通信
.zjx521,\              # 企业域名
cluster.local,\          # K8s 集群域名 (兼容性考虑)
metadata.google.internal # GCP 元数据服务
"

# 验证命令: systemctl show docker --property=Environment
2. 20-limits.conf (资源限制保护)
# /etc/systemd/system/docker.service.d/20-limits.conf
# Purpose: 保护 dockerd 进程本身不被资源耗尽
# SRE Note: 基于 64GB RAM 服务器计算,避免 infinity 导致系统级故障
# Formula: 
#   LimitNOFILE = 容器数 × 1024 × 1.5 (安全系数)
#   LimitNPROC  = 容器数 × 256 × 1.2 (安全系数)

[Service]
# 文件描述符限制 (支持 1000 容器 × 1024 连接)
LimitNOFILE=1572864    # 1.5M = 1000 × 1024 × 1.5

# 进程数限制 (支持 1000 容器 × 256 进程)
LimitNPROC=307200      # 300K = 1000 × 256 × 1.2

# 任务总数限制 (cgroup v2 任务数)
TasksMax=16384         # 16K 任务,足够支撑大规模部署

# 内存保护 (防止 OOM kill)
MemoryAccounting=yes
MemoryLimit=32G        # 32GB 内存限制,保留宿主机资源

# CPU 保护 (避免 dockerd 占用过多 CPU)
CPUAccounting=yes
CPUQuota=800%          # 8 核 CPU 配额,保留 20% 给系统

# 验证命令: cat /proc/$(pgrep dockerd)/limits
3. 30-security.conf (安全加固)
# /etc/systemd/system/docker.service.d/30-security.conf
# Purpose: 最小权限原则,减少攻击面
# SRE Note: 安全基线要求,防止容器逃逸
# Reference: CIS Docker Benchmark v1.6.0

[Service]
# 丢弃不必要的 capabilities
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_SYS_CHROOT CAP_SETFCAP

# 禁止权限提升
NoNewPrivileges=yes

# 文件系统保护
ProtectSystem=strict       # /usr, /boot, /etc 只读
ProtectHome=yes           # /home, /root, /run/user 不可见
PrivateTmp=yes            # 私有 /tmp 目录
ProtectControlGroups=yes  # cgroup 文件系统只读
ProtectKernelTunables=yes # /proc/sys, /sys 只读
ProtectKernelModules=yes  # 内核模块操作禁止

# 网络隔离
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
RestrictRealtime=yes      # 禁止实时调度

# IPC 隔离
PrivateIPC=yes

# 验证命令: systemctl show docker --property=CapabilityBoundingSet --property=NoNewPrivileges
4. 40-logging.conf (日志优化)
# /etc/systemd/system/docker.service.d/40-logging.conf
# Purpose: 防止日志爆炸,确保系统稳定性
# SRE Note: 生产环境常见故障点,日志文件过大导致磁盘耗尽
# Strategy: 限制日志大小 + 轮转 + 优先级控制

[Service]
# 日志级别控制 (生产环境推荐 info)
Environment="DOCKER_LOG_LEVEL=info"

# systemd 日志限制
LogsDirectoryMaxFiles=10
LogsDirectoryMode=0755

# journald 集成优化
LogRateLimitIntervalSec=30s
LogRateLimitBurst=1000

# 防止日志洪水攻击
StandardOutput=journal
StandardError=journal
SyslogLevel=info

# 验证命令: journalctl -u docker --since "1 hour ago" --no-pager | wc -l
5. 50-performance.conf (性能调优)
# /etc/systemd/system/docker.service.d/50-performance.conf
# Purpose: 优化 dockerd 启动和服务性能
# SRE Note: 基于大规模集群经验,减少服务中断时间
# Metrics: 目标启动时间 < 5s,恢复时间 < 30s

[Service]
# 启动超时优化 (避免卡在启动阶段)
TimeoutStartSec=300      # 5 分钟启动超时
TimeoutStopSec=120       # 2 分钟停止超时

# 优雅关闭配置
KillMode=mixed           # 先 SIGTERM,再 SIGKILL
KillSignal=SIGTERM
SendSIGKILL=yes
SendSIGHUP=yes          # 通知子进程重载

# 重启策略 (自动恢复)
Restart=on-failure
RestartSec=5s           # 5 秒后重启
StartLimitInterval=60s  # 60 秒内
StartLimitBurst=5       # 最多重启 5 次

# 调度优化
Nice=-5                 # 提高进程优先级
IOSchedulingClass=realtime
IOSchedulingPriority=2  # IO 优先级

# 验证命令: systemctl show docker --property=TimeoutStartSec --property=Restart
6. 60-reliability.conf (可靠性增强)
# /etc/systemd/system/docker.service.d/60-reliability.conf
# Purpose: 确保 dockerd 高可用,减少服务中断
# SRE Note:  要求 99.99% 可用性,必须配置
# Principle: 预防性配置 + 快速恢复

[Service]
# 心跳检测 (systemd 层面)
WatchdogSec=30s         # 30 秒心跳超时

# 环境变量控制 (便于调试)
Environment="DOCKER_OPTS=--live-restore --iptables=false"

# 依赖关系优化
Requires=containerd.service
After=containerd.service network-online.target

# 资源分配保障
CPUShares=1024          # 保证 CPU 份额
BlockIOWeight=1000      # IO 权重

# 故障隔离
Slice=system.slice      # 放入系统 slice
Delegate=yes            # 委托 cgroup 管理给 dockerd

# 验证命令: systemctl show docker --property=WatchdogSec --property=Requires
7. 70-containerd.conf (containerd 专项优化)
# /etc/systemd/system/docker.service.d/70-containerd.conf
# Purpose: 优化 containerd 作为 Docker 默认运行时
# SRE Note: Docker 23.0+ 默认使用 containerd,必须专门优化
# Compatibility: 确保与 containerd 1.7+ 兼容

[Service]
# containerd 通信优化
Environment="CONTAINERD_NAMESPACE=moby"
Environment="CONTAINERD_SNAPSHOTTER=overlayfs"

# gRPC 超时配置
Environment="CONTAINERD_GRPC_MAX_RECV_MSG_SIZE=16777216"  # 16MB
Environment="CONTAINERD_GRPC_MAX_SEND_MSG_SIZE=16777216"  # 16MB

# 连接池优化
Environment="CONTAINERD_GRPC_CONNECTION_TIMEOUT=30s"
Environment="CONTAINERD_GRPC_KEEPALIVE_TIME=60s"

# 存储优化 (与 dockerd 协同)
Environment="DOCKER_STORAGE_OPTS=--storage-opt dm.basesize=50G"

# 验证命令: ctr -n moby version && ctr -n moby containers list
部署脚本 (一键应用所有优化)
#!/bin/bash
# deploy-docker-systemd-optimizations.sh
#  2025 标准 Docker systemd 优化部署脚本

set -euo pipefail
IFS=$'\n\t'

echo " 开始部署 Docker systemd 优化配置..."

# 1. 创建配置目录
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo chown root:root /etc/systemd/system/docker.service.d
sudo chmod 755 /etc/systemd/system/docker.service.d

# 2. 部署所有配置文件
cat <<'EOF' | sudo tee /etc/systemd/system/docker.service.d/10-proxy.conf
# ... (上面的 10-proxy.conf 内容) ...
EOF

cat <<'EOF' | sudo tee /etc/systemd/system/docker.service.d/20-limits.conf
# ... (上面的 20-limits.conf 内容) ...
EOF

cat <<'EOF' | sudo tee /etc/systemd/system/docker.service.d/30-security.conf
# ... (上面的 30-security.conf 内容) ...
EOF

cat <<'EOF' | sudo tee /etc/systemd/system/docker.service.d/40-logging.conf
# ... (上面的 40-logging.conf 内容) ...
EOF

cat <<'EOF' | sudo tee /etc/systemd/system/docker.service.d/50-performance.conf
# ... (上面的 50-performance.conf 内容) ...
EOF

cat <<'EOF' | sudo tee /etc/systemd/system/docker.service.d/60-reliability.conf
# ... (上面的 60-reliability.conf 内容) ...
EOF

cat <<'EOF' | sudo tee /etc/systemd/system/docker.service.d/70-containerd.conf
# ... (上面的 70-containerd.conf 内容) ...
EOF

# 3. 设置正确权限
sudo chmod 644 /etc/systemd/system/docker.service.d/*.conf
sudo chown root:root /etc/systemd/system/docker.service.d/*.conf

# 4. 重载 systemd 配置
echo " 重载 systemd 配置..."
sudo systemctl daemon-reload

# 5. 重启 Docker 服务
echo " 重启 Docker 服务..."
sudo systemctl restart docker

# 6. 验证配置
echo " 验证配置是否生效..."
sleep 5

# 7. 健康检查
echo " 执行健康检查..."
if sudo docker info >/dev/null 2>&1; then
    echo " Docker 服务健康"
else
    echo " Docker 服务异常,请检查日志"
    sudo journalctl -u docker --since "5 minutes ago" --no-pager
    exit 1
fi

# 8. 验证关键限制
echo " 验证关键限制设置..."
docker_pid=$(pgrep dockerd || echo "0")
if [ "$docker_pid" -ne 0 ]; then
    echo "PID: $docker_pid"
    echo "文件描述符限制: $(cat /proc/$docker_pid/limits | grep 'Max open files' | awk '{print $3}')"
    echo "进程数限制: $(cat /proc/$docker_pid/limits | grep 'Max processes' | awk '{print $3}')"
    echo "Capabilities: $(sudo systemctl show docker --property=CapabilityBoundingSet --value)"
fi

echo " Docker systemd 优化配置部署完成!"
echo " 配置文件位置: /etc/systemd/system/docker.service.d/"
echo " 验证命令: systemctl cat docker | grep -A 10 '\[Service\]'"
验证清单 (检查表)
# 1. 配置文件权限检查
ls -la /etc/systemd/system/docker.service.d/
# 应该显示: -rw-r--r-- 1 root root

# 2. systemd 配置合并验证
systemctl cat docker | grep -A 20 '\[Service\]'

# 3. 进程限制验证
docker_pid=$(pgrep dockerd)
cat /proc/$docker_pid/limits | grep -E "(open files|processes|tasks)"

# 4. 安全特性验证
systemctl show docker --property=CapabilityBoundingSet --property=NoNewPrivileges --property=ProtectSystem

# 5. 服务状态验证
systemctl status docker --no-pager

# 6. 容器创建测试
docker run --rm alpine echo " 配置生效,容器可正常创建"

# 7. 日志验证
journalctl -u docker -f --since "1 minute ago"  # 观察实时日志

关键注意事项:

1.变量替换
# 10-proxy.conf 中必须替换实际代理地址
Environment="HTTP_PROXY=http://YOUR_PROXY_IP:PORT/"
2.硬件适配
# 20-limits.conf 需要根据服务器规格调整
# 公式: LimitNOFILE = (预期容器数 × 1024 × 1.5)
# 例如: 200 容器 → 200 × 1024 × 1.5 = 307,200

3.安全审计

# 部署后运行安全审计
docker bench security --version 1.6.0
4.监控集成
# 添加到 Prometheus node exporter
echo "docker_fd_usage $(ls -l /proc/$(pgrep dockerd)/fd | wc -l)" > /var/lib/prometheus/node_exporter/docker.prom
5.变更管理
# 所有配置变更必须通过 GitOps
git commit -m "feat(docker): apply SRE 2025 systemd optimizations"
git push origin main

daemon.json 优化

cat /etc/docker/daemon.json
{
    "live-restore": true,
    "registry-mirrors": [
        "https://docker.m.daocloud.io",
        "https://docker.xuanyuan.me",
        "https://docker.1ms.run",
        "https://docker-0.unsee.tech",
        "https://docker.hlmirror.com"
    ],
    "insecure-registries": [],
    "log-driver": "json-file",
    "log-opts": {
        "max-size": "100m",
        "max-file": "3"
    },
    "default-ulimits": {
        "nofile": {
            "Name": "nofile",
            "Soft": 65536,
            "Hard": 65536
        }
    },
    "features": {
        "buildkit": true
    },
    "proxies": {
        "http-proxy": "http://YOUR_PROXY_IP:YOUR_PROXY_PORT",
        "https-proxy": "http://YOUR_PROXY_IP:YOUR_PROXY_PORT",
        "no-proxy": "localhost,127.0.0.0/8,172.16.0.0/12,192.168.0.0/16,10.0.0.0/8"
    }
}

这套配置是 2025 标准的实现,它:

  •  分层清晰:每个文件专注单一职责
  •  防御性设计:避免 infinity,设置合理限制
  •  可观测性:内置验证和监控点
  •  自动化友好:一键部署脚本
  •  安全优先:遵循最小权限原则
  •  containerd 优化:适配 Docker 新架构

2.离线安装(二进制安装)

cat > package_docker_offline.sh  <<'EOF1'
#!/bin/bash
# package_docker_offline.sh - 用于为离线环境打包 Docker 安装文件

set -euo pipefail # 启用错误处理

# --- 配置区 ---
DOCKER_VERSION="28.5.2"
# URL="https://download.docker.com/linux/static/stable/" # 官方源通常更稳定用于打包
URL="https://mirrors.aliyun.com/docker-ce/linux/static/stable/" # 国内源,打包时使用

# 输出目录和包名
OUTPUT_DIR="docker-offline-package"
ARCH=$(uname -m)
case $ARCH in
    x86_64) DOCKER_ARCH="x86_64" ;;
    aarch64) DOCKER_ARCH="aarch64" ;;
    *) echo "Unsupported architecture: $ARCH"; exit 1 ;;
esac
PACKAGE_NAME="docker-offline-${DOCKER_VERSION}-${ARCH}"

# --- 函数区 ---

# 简单的彩色输出函数 (打包阶段用)
color() {
    local RES_COL=60
    local MOVE_TO_COL="\\033[${RES_COL}G"
    local SETCOLOR_SUCCESS="\\033[1;32m"
    local SETCOLOR_FAILURE="\\033[1;31m"
    local SETCOLOR_WARNING="\\033[1;33m"
    local SETCOLOR_NORMAL="\\E[0m"

    echo -ne "$1"
    printf "%s" "$MOVE_TO_COL"
    echo -n "["
    if [[ "$2" == "success" || "$2" == "0" ]]; then
        echo -ne "$SETCOLOR_SUCCESS"
        echo -n "  OK  "
    elif [[ "$2" == "failure" || "$2" == "1" ]]; then
        echo -ne "$SETCOLOR_FAILURE"
        echo -n "FAILED"
    else
        echo -ne "$SETCOLOR_WARNING"
        echo -n "WARNING"
    fi
    echo -e "$SETCOLOR_NORMAL]"
}


echo "[*] Preparing packaging environment..."
mkdir -p "${OUTPUT_DIR}/${PACKAGE_NAME}"
cd "${OUTPUT_DIR}"

# 1. 下载 Docker 二进制
echo "[*] Downloading Docker ${DOCKER_VERSION} (${DOCKER_ARCH})..."
if wget --progress=bar:force:noscroll "${URL}${DOCKER_ARCH}/docker-${DOCKER_VERSION}.tgz" -O "${PACKAGE_NAME}/docker-${DOCKER_VERSION}.tgz"; then
    color "Docker binary downloaded" "success"
else
    color "Failed to download Docker binary" "failure"
    exit 1
fi

# 2. 下载补全脚本
echo "[*] Downloading Docker bash completion..."
if wget -q -O "${PACKAGE_NAME}/docker_completion" "https://raw.githubusercontent.com/docker/cli/master/contrib/completion/bash/docker"; then
    color "Docker completion downloaded" "success"
else
    color "Failed to download Docker completion" "warning" # 不致命
fi

# 3. 创建 systemd 服务文件 (优化版)
echo "[*] Creating systemd service file..."
cat > "${PACKAGE_NAME}/docker.service" <<'EOF2'
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target firewalld.service
Wants=network-online.target

[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/local/bin/dockerd -H unix:///var/run/docker.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always

# Note that StartLimit* options were moved from "Service" to "Unit" in systemd 230.
StartLimitBurst=3
StartLimitInterval=60s

# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity

# Comment TasksMax if your systemd version does not supports it.
# Only systemd 226 and above support this version.
TasksMax=infinity

# set delegate yes so that systemd does not reset the cgroups of docker containers
Delegate=yes

# kill only the docker process, not all processes in the cgroup
KillMode=process

[Install]
WantedBy=multi-user.target
EOF2
color "Systemd service file created" "success"


# 4. 创建 daemon.json 配置模板 (包含优化项)
echo "[*] Creating daemon.json template with optimizations..."
# 注意:在模板中使用占位符,安装时替换
cat > "${PACKAGE_NAME}/daemon.json" <<'EOF3'
{
    "live-restore": true,
    "registry-mirrors": [
        "https://docker.m.daocloud.io",
        "https://docker.xuanyuan.me",
        "https://docker.1ms.run",
        "https://docker-0.unsee.tech",
        "https://docker.hlmirror.com"
    ],
    "insecure-registries": [],
    "log-driver": "json-file",
    "log-opts": {
        "max-size": "100m",
        "max-file": "3"
    },
    "default-ulimits": {
        "nofile": {
            "Name": "nofile",
            "Soft": 65536,
            "Hard": 65536
        }
    },
    "features": {
        "buildkit": true
    },
    "proxies": {
        "http-proxy": "http://YOUR_PROXY_IP:YOUR_PROXY_PORT",
        "https-proxy": "http://YOUR_PROXY_IP:YOUR_PROXY_PORT",
        "no-proxy": "localhost,127.0.0.0/8,172.16.0.0/12,192.168.0.0/16,10.0.0.0/8"
    }
}
EOF3
color "daemon.json template created" "success"

# 5. 创建安装脚本 (离线安装时运行)
echo "[*] Creating offline installation script..."
cat > "${PACKAGE_NAME}/install_docker_offline.sh" <<'INSTALL_SCRIPT_EOF'
#!/bin/bash
# install_docker_offline.sh - 离线安装 Docker 的脚本

set -euo pipefail

if [[ $EUID -ne 0 ]]; then
    echo "Error: This script must be run as root." >&2
    exit 1
fi

# --- 检测操作系统 ---
OS_ID=""
if [ -f /etc/os-release ]; then
    . /etc/os-release
    OS_ID=$ID
elif command -v lsb_release >/dev/null 2>&1; then
    OS_ID=$(lsb_release -si | tr '[:upper:]' '[:lower:]')
elif [ -f /etc/redhat-release ]; then
    OS_ID="rhel"
elif [ -f /etc/debian_version ]; then
    OS_ID="debian"
fi

# --- 安装依赖 ---
install_dependencies() {
    echo "[*] Installing dependencies (wget, curl, bash-completion)..."
    case "$OS_ID" in
        ubuntu|debian)
            apt-get update
            apt-get install -y wget curl bash-completion
            ;;
        centos|rhel|rocky|almalinux|anolis|fedora)
            if command -v dnf &> /dev/null; then
                dnf install -y wget curl bash-completion
            else
                yum install -y wget curl bash-completion
            fi
            ;;
        *)
            echo "Warning: Unsupported OS '$OS_ID'. Please install wget, curl, and bash-completion manually."
            ;;
    esac
}

# --- 安装 Docker ---
install_docker() {
    echo "[*] Installing Docker..."
    tar xf "docker-${DOCKER_VERSION}.tgz"
    cp docker/* /usr/local/bin/

    # Install systemd service
    cp docker.service /lib/systemd/system/docker.service
    systemctl daemon-reload

    # Install completion
    mkdir -p /etc/bash_completion.d
    cp docker_completion /etc/bash_completion.d/docker

    echo "[+] Docker binary and service installed."
}

# --- 配置 Docker ---
config_docker() {
    echo "[*] Configuring Docker..."
    mkdir -p /etc/docker

    # 直接使用打包好的 daemon.json 模板
    cp daemon.json /etc/docker/daemon.json

    # --- 可选:尝试探测并替换为最优镜像源 ---
    # (在离线环境中可能不适用,但保留逻辑供参考)
    # BEST_MIRROR=""
    # for mirror in "https://docker.m.daocloud.io" "https://docker.hlmirror.com"; do
    #     if curl -s -m 3 -f "$mirror" > /dev/null 2>&1; then
    #         BEST_MIRROR="$mirror"
    #         break
    #     fi
    # done
    # if [ -n "$BEST_MIRROR" ]; then
    #     sed -i "s|https://docker.m.daocloud.io|${BEST_MIRROR}|g" /etc/docker/daemon.json
    #     echo "[+] Docker configured with mirror: $BEST_MIRROR"
    # else
    #     echo "[-] Could not probe a better mirror, using default list."
    # fi

    echo "[+] Docker configured with default settings (including mirrors and proxy placeholder)."
    echo "    Please edit /etc/docker/daemon.json to adjust mirrors or configure proxy."
}

# --- 启动 Docker ---
start_docker() {
    echo "[*] Starting Docker service..."
    systemctl enable --now docker > /dev/null 2>&1
    if docker version > /dev/null 2>&1; then
        echo "[+] Docker service started successfully."
    else
        echo "[-] Failed to start Docker service."
        exit 1
    fi
}

# --- Main Execution ---
# Get Docker version from the package filename or the tgz file
DOCKER_VERSION=$(basename docker-*.tgz | sed 's/docker-\(.*\)\.tgz/\1/')

install_dependencies
install_docker
config_docker
start_docker

echo "[*] Docker ${DOCKER_VERSION} installation complete!"
echo "    Configuration file: /etc/docker/daemon.json"
echo "    Please review and adjust the configuration as needed."
INSTALL_SCRIPT_EOF

chmod +x "${PACKAGE_NAME}/install_docker_offline.sh"
color "Installation script created" "success"

# 6. 打包
echo "[*] Creating offline package ${PACKAGE_NAME}.tar.gz..."
tar -czf "${PACKAGE_NAME}.tar.gz" "${PACKAGE_NAME}"

echo "[+] Offline package created: ${OUTPUT_DIR}/${PACKAGE_NAME}.tar.gz"
echo "    To install offline, transfer this file to the target machine and run:"
echo "    tar -xzf ${PACKAGE_NAME}.tar.gz && cd ${PACKAGE_NAME} && sudo ./install_docker_offline.sh"

cd ..
EOF1

说明

  1. daemon.json 模板:现在包含了你要求的多个镜像源 (registry-mirrors) 和一个占位符形式的代理配置 (proxies)。用户解压后安装,会得到这个默认配置,然后可以根据自己的网络环境进行修改。
  2. install_docker_offline.sh (离线安装脚本):这个脚本被打包进离线包中。它在目标机器上运行时,会:
    • 安装依赖 (wgetcurlbash-completion)。
    • 解压并安装 Docker 二进制和服务。
    • 直接复制打包好的 daemon.json 到 /etc/docker/
    • 启动 Docker 服务。

使用流程

  1. 在有网络的机器上:运行 bash package_docker_offline.sh。它会生成 docker-offline-package/docker-offline-${VERSION}-${ARCH}.tar.gz
  2. 在离线机器上
    tar -xzf docker-offline-${VERSION}-${ARCH}.tar.gz
    cd docker-offline-${VERSION}-${ARCH}
    sudo ./install_docker_offline.sh
  3. (可选) 安装后:用户编辑 /etc/docker/daemon.json 来启用代理或调整镜像源。

二、容器化思维(Docker 原生视角)

   1.容器的本质:隔离的进程,不是轻量 VM

     技术深度解析:

  • 进程视角 vs VM 视角:容器本质是带隔离属性的 Linux 进程,共享宿主机内核,而非完整的操作系统虚拟化

Linux Namespace 机制深度剖析

隔离类型 功能描述 系统调用参数 内核版本 典型用途 安全性考量 SRE 关注点
MNT Namespace (Mount) 提供文件系统挂载点的隔离能力,每个命名空间拥有独立的挂载视图 CLONE_NEWNS 2.4.19 容器中独立的文件系统视图(如 /proc/sys),避免影响宿主机 需注意 bind mount 可能穿透边界,需限制 控制容器访问敏感路径,防止误操作破坏宿主机文件系统
PID Namespace (Process ID) 实现进程 ID 的隔离,使容器内的进程 PID 从 1 开始,不与宿主机冲突 CLONE_NEWPID 2.6.24 避免进程号冲突,简化调试与监控 子命名空间中的进程无法直接被宿主机 kill 或 ps 查看,需通过 nsenter 进程生命周期管理需明确,避免僵尸进程堆积
IPC Namespace (Inter-Process Communication) 隔离进程间通信资源,如信号量、消息队列、共享内存等 CLONE_NEWIPC 2.6.19 多个容器间通信互不干扰,提升安全性和稳定性 共享内存若未清理可能造成内存泄漏 监控 IPC 资源使用情况,确保无资源泄露
Net Namespace (Network) 提供网络栈隔离,包括网络设备、IP 地址、端口、路由表等 CLONE_NEWNET 2.6.29 每个容器拥有独立网络栈,支持多网卡、防火墙规则 网络策略需严格控制,防止横向渗透 网络策略(如 CNI、iptables)必须自动化且可审计;关注网络性能与延迟
UTS Namespace (UNIX Timesharing System) 隔离主机名(hostname)和域名(domain name) CLONE_NEWUTS 2.6.19 容器可自定义主机名,便于服务发现与日志识别 主机名变更不影响系统稳定性,但需统一规范 日志系统应支持基于 hostname 的过滤,便于排查
User Namespace (User) 实现用户和组 ID 的映射隔离,提升权限控制粒度 CLONE_NEWUSER 3.8 容器内 root 用户映射为宿主机非特权用户,增强安全性 需配置正确的 UID/GID 映射,否则可能引发权限问题 推荐启用 user namespace 以降低攻击面;配合 seccomp 和 AppArmor 使用

 Linux Control Groups (cgroups) 


1. 本质定位:分布式系统的"资源熔断器"

核心观点
"Cgroups 不是资源限制工具,而是系统韧性的基础设施。它通过硬性隔离防止单点故障扩散至全局,是保障 SLO/SLI 的最后一道防线。"

  • 历史意义
    Google 2006 年发起(原名 process containers),2007 年因命名冲突重命名为 cgroups 并合入 Linux 2.6.24 内核。
    →洞察这是 Google 将 Borg 经验反哺开源的关键一步——用内核级隔离解决多租户资源共享的熵增问题。
2. 为什么 必须掌控 cgroups?

关键
"永不信任进程—— 所有工作负载必须运行在资源约束的沙箱中。这是 '零信任架构'起点。"

风险场景 无 cgroups 后果 cgroups v2 应对方案
内存泄漏 单容器耗尽宿主机内存 → 全节点 OOM Kill --memory="512m"
• 底层映射到 cgroups v2 的 memory.max
• 必须配合 --oom-score-adj=-500 (Docker 默认开启 OOM 保护)
CPU 饥饿 低优先级任务阻塞关键服务线程 --cpus=1.5
• 底层映射到 cpu.max = "150000 100000" (150% CPU)
• 禁用 cpu.weight (Docker 未暴露此参数)
I/O 风暴 磁盘写满导致 etcd 延迟 → 集群脑裂 Docker 原生不支持
• 必须通过 --device-write-bps (cgroups v1)
• cgroups v2 需用 systemd 或 crun
systemd-run --scope -p IOAccounting=yes -p IOWeight=100 docker run...
网络拥塞 恶意容器占满带宽 → API 服务超时 Docker 原生不支持 cgroups 限速
• 通过 --network 驱动参数
docker network create --driver bridge --opt com.docker.network.driver.mtu=1400 limited-net
• 生产环境必须用 CNI 插件 (如 bandwidth)
  • 与命名空间协同

核心概念:Linux 的"孤儿院"机制

普通 Linux 系统:
systemd (PID 1) → 负责收养所有孤儿进程
                → 自动清理僵尸进程

Docker 容器(无 --init):
your-app (PID 1) → 不会收养孤儿进程
                  → 僵尸进程堆积 → PID 耗尽

tini 的三个关键作用

  1. 当好 PID 1

    • 接收系统信号(SIGTERM, SIGKILL)
    • 代替你的应用处理信号
  2. 清理僵尸进程

    • 当子进程退出时,自动调用 wait() 清理
    • 防止僵尸进程堆积
  3. 优雅关闭

    • 收到 SIGTERM 时,转发给你的应用
    • 等待应用完成清理工作
    • 最后才退出容器

"Linux 要求 PID 1 进程负责回收僵尸进程。容器默认将应用作为 PID 1,但大部分应用(如 Java/Node.js)不会实现这个功能。当子进程退出时,会变成僵尸进程,长期累积后耗尽系统 PID 资源,导致整个宿主机无法创建新进程。

--init 启用 tini 作为真正的 init 系统:

  • 作为 PID 1 接收所有信号
  • 自动回收僵尸进程
  • 将 SIGTERM 转发给应用,实现优雅关闭

这是防止系统级故障的基础防护,Google SRE 要求所有生产容器必须启用。"

预防僵尸进程 --init

  1. 先说结论
    "这是防止僵尸进程导致系统级故障的基础防护。"

  2. 解释机制
    "Linux 要求 PID 1 进程回收僵尸进程。容器默认将应用作为 PID 1,但应用通常不处理这个责任。僵尸进程累积会耗尽 PID 资源,导致宿主机无法创建新进程。"

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐