11_apollo_docker_setup_host子模块软件架构分析

11_apollo_docker_setup_host子模块软件架构分析

1. 概述

apollo_docker_setup_host子模块是Apollo自动驾驶平台的主机环境配置工具,负责在宿主机上安装和配置Docker环境、设置系统参数、配置设备规则以及管理Docker资源。该模块通过一系列Shell脚本实现自动化部署,包括Docker安装、内核模块加载、Udev设备规则配置、时间同步设置和核心转储配置等功能,为Apollo系统在容器化环境中运行提供必要的基础设施支持。

2. 软件架构图

graph TB subgraph "用户层" U1[系统管理员] U2[开发人员] U3[运维人员] end subgraph "脚本层" S1[setup_host.sh] S2[install_docker.sh] S3[cleanup_resources.sh] end subgraph "配置层" C1[Udev规则配置] C2[内核参数配置] C3[时间同步配置] C4[Docker配置] end subgraph "系统层" SYS1[Linux内核] SYS2[Udev子系统] SYS3[Docker守护进程] SYS4[系统服务] end subgraph "设备层" D1[USB串口设备] D2[FPD-Link相机] D3[其他外设] end U1 --> S1 U2 --> S2 U3 --> S3 S1 --> C1 S1 --> C2 S1 --> C3 S2 --> C4 S3 --> C4 C1 --> SYS2 C2 --> SYS1 C3 --> SYS4 C4 --> SYS3 SYS2 --> D1 SYS2 --> D2 SYS2 --> D3 style S1 fill:#e1f5fe style S2 fill:#e1f5fe style S3 fill:#e1f5fe style C1 fill:#f3e5f5 style C2 fill:#f3e5f5 style C3 fill:#f3e5f5 style C4 fill:#f3e5f5

3. 调用流程图

sequenceDiagram participant User as 用户 participant Setup as setup_host.sh participant Install as install_docker.sh participant Cleanup as cleanup_resources.sh participant System as 系统服务 participant Docker as Docker守护进程 participant Udev as Udev子系统 User->>Setup: 执行setup_host.sh Setup->>Setup: 配置核心转储格式 Setup->>Setup: 设置NTP时间同步 Setup->>Setup: 复制Udev规则文件 Setup->>Setup: 配置UVC视频模块 Setup->>Udev: 加载设备规则 Udev-->>Setup: 规则加载完成 Setup-->>User: 主机配置完成 User->>Install: 执行install_docker.sh Install->>Install: 检查内核版本 Install->>Install: 安装文件系统支持 Install->>Install: 安装前置依赖包 Install->>Install: 配置Docker仓库 Install->>System: 安装Docker软件包 System-->>Install: 安装完成 Install->>Install: 配置用户权限 Install->>Docker: 重启Docker服务 Docker-->>Install: 服务启动完成 Install-->>User: Docker安装完成 User->>Cleanup: 执行cleanup_resources.sh Cleanup->>Cleanup: 检查容器环境 Cleanup->>Docker: 查询已退出容器 Docker-->>Cleanup: 返回容器列表 alt 存在已退出容器 Cleanup->>Docker: 删除已退出容器 Docker-->>Cleanup: 删除完成 end Cleanup->>Docker: 查询悬空镜像 Docker-->>Cleanup: 返回镜像列表 alt 存在悬空镜像 Cleanup->>Docker: 删除悬空镜像 Docker-->>Cleanup: 删除完成 end Cleanup->>Docker: 查询悬空卷 Docker-->>Cleanup: 返回卷列表 alt 存在悬空卷 Cleanup->>Docker: 删除悬空卷 Docker-->>Cleanup: 删除完成 end Cleanup-->>User: 资源清理完成

4. UML类图

4.1 核心脚本类图

classDiagram class SetupHostScript { +execute() +configureCoreDump() +setupNtpSync() +installUdevRules() +configureUvcModule() -apolloRootDir: string } class InstallDockerScript { +execute(action: string) +installDocker() +uninstallDocker() +installFilesystemSupport() +installPrereqPackages() +setupDockerRepoAndInstall() +postInstallSettings() -arch: string -archAlias: string } class CleanupResourcesScript { +execute() +cleanupExitedContainers() +cleanupDanglingImages() +cleanupDanglingVolumes() -isInContainer: boolean } class UdevRuleManager { +copyRules(source: string, dest: string) +loadRules() +reloadRules() -ruleFiles: list~string~ } class KernelConfigurator { +setCorePattern(pattern: string) +loadModule(moduleName: string) +checkKernelVersion(): string -kernelVersion: string } class TimeSyncManager { +setupNtpSync() +addCronJob(cronEntry: string) +checkCronEntry(pattern: string): boolean -ntpServer: string } class DockerInstaller { +installPackages() +setupRepository() +configureUserPermissions() +restartService() -packages: list~string~ -repositoryUrl: string } class DockerCleaner { +getExitedContainers(): list~string~ +getDanglingImages(): list~string~ +getDanglingVolumes(): list~string~ +removeContainers(ids: list~string~) +removeImages(ids: list~string~) +removeVolumes(ids: list~string~) } SetupHostScript --> UdevRuleManager SetupHostScript --> KernelConfigurator SetupHostScript --> TimeSyncManager InstallDockerScript --> DockerInstaller InstallDockerScript --> KernelConfigurator CleanupResourcesScript --> DockerCleaner

4.2 配置管理类图

classDiagram class ConfigManager { +loadConfig(path: string) +saveConfig(path: string) +getValue(key: string): string +setValue(key: string, value: string) -configData: map~string,string~ } class UdevConfig { +getCameraRules(): list~UdevRule~ +getGpsRules(): list~UdevRule~ +addRule(rule: UdevRule) +removeRule(ruleId: string) -rules: list~UdevRule~ } class UdevRule { +getSubsystem(): string +getDriver(): string +getAttributes(): map~string,string~ +getSymlink(): string +getMode(): string +getOwner(): string +getGroup(): string -subsystem: string -driver: string -attributes: map~string,string~ -symlink: string -mode: string -owner: string -group: string } class CameraRule { +getCameraType(): CameraType +getPosition(): CameraPosition +getFocalLength(): string -cameraType: CameraType -position: CameraPosition -focalLength: string } class GpsRule { +getPortNumber(): int +getDeviceName(): string -portNumber: int -deviceName: string } class SystemConfig { +getCorePattern(): string +setCorePattern(pattern: string) +getNtpServer(): string +setNtpServer(server: string) -corePattern: string -ntpServer: string -cronEntries: list~string~ } ConfigManager --> UdevConfig ConfigManager --> SystemConfig UdevConfig --> UdevRule UdevRule <|-- CameraRule UdevRule <|-- GpsRule

4.3 设备管理类图

classDiagram class DeviceManager { +detectDevices(): list~Device~ +mapDevice(device: Device, alias: string) +getDeviceByAlias(alias: string): Device -devices: map~string,Device~ } class Device { <> +getId(): string +getType(): DeviceType +getPath(): string +getAttributes(): map~string,string~ -id: string -type: DeviceType -path: string -attributes: map~string,string~ } class CameraDevice { +getResolution(): Resolution +getFrameRate(): float +getFocalLength(): string +getPosition(): CameraPosition -resolution: Resolution -frameRate: float -focalLength: string -position: CameraPosition } class GpsDevice { +getPortNumber(): int +getBaudRate(): int +getDriver(): string -portNumber: int -baudRate: int -driver: string } class FpdLinkCamera { +getModel(): string +getInterface(): string -model: string -interface: string } class UsbSerialDevice { +getVendorId(): string +getProductId(): string -vendorId: string -productId: string } DeviceManager --> Device Device <|-- CameraDevice Device <|-- GpsDevice CameraDevice <|-- FpdLinkCamera GpsDevice <|-- UsbSerialDevice

5. 状态机分析

5.1 主机配置状态机

stateDiagram-v2 [*] --> IDLE: 脚本启动 IDLE --> CHECKING_ENVIRONMENT: 开始配置 CHECKING_ENVIRONMENT --> CONFIGURING_COREDUMP: 环境检查通过 CHECKING_ENVIRONMENT --> ERROR: 环境检查失败 CONFIGURING_COREDUMP --> CONFIGURING_NTP: 核心转储配置完成 CONFIGURING_NTP --> INSTALLING_UDEV_RULES: NTP配置完成 INSTALLING_UDEV_RULES --> CONFIGURING_UVC: Udev规则安装完成 CONFIGURING_UVC --> COMPLETED: UVC模块配置完成 COMPLETED --> [*]: 配置成功 ERROR --> [*]: 配置失败 note right of CHECKING_ENVIRONMENT : 检查Apollo根目录、系统权限 note right of CONFIGURING_COREDUMP : 设置/proc/sys/kernel/core_pattern note right of CONFIGURING_NTP : 添加ntpdate到crontab note right of INSTALLING_UDEV_RULES : 复制udev规则到/etc/ note right of CONFIGURING_UVC : 添加uvcvideo到/etc/modules

5.2 Docker安装状态机

stateDiagram-v2 [*] --> IDLE: 脚本启动 IDLE --> CHECKING_KERNEL: 开始安装 CHECKING_KERNEL --> INSTALLING_FS_SUPPORT: 内核版本检查通过 CHECKING_KERNEL --> ERROR: 内核版本不兼容 INSTALLING_FS_SUPPORT --> INSTALLING_PREREQS: 文件系统支持安装完成 INSTALLING_PREREQS --> SETTING_UP_REPO: 前置依赖安装完成 SETTING_UP_REPO --> INSTALLING_DOCKER: 仓库配置完成 INSTALLING_DOCKER --> CONFIGURING_PERMISSIONS: Docker安装完成 CONFIGURING_PERMISSIONS --> RESTARTING_SERVICE: 用户权限配置完成 RESTARTING_SERVICE --> COMPLETED: 服务重启成功 COMPLETED --> [*]: 安装成功 ERROR --> [*]: 安装失败 note right of CHECKING_KERNEL : 检查内核版本>=4.0,验证overlay模块 note right of INSTALLING_FS_SUPPORT : 加载overlay内核模块 note right of INSTALLING_PREREQS : 安装apt-transport-https、curl等 note right of SETTING_UP_REPO : 添加Docker官方APT仓库 note right of INSTALLING_DOCKER : 安装docker-ce、docker-ce-cli、containerd.io note right of CONFIGURING_PERMISSIONS : 将用户添加到docker组 note right of RESTARTING_SERVICE : 重启Docker守护进程

5.3 资源清理状态机

stateDiagram-v2 [*] --> IDLE: 脚本启动 IDLE --> CHECKING_CONTAINER: 开始清理 CHECKING_CONTAINER --> CLEANING_CONTAINERS: 检测到已退出容器 CHECKING_CONTAINER --> CHECKING_IMAGES: 无已退出容器 CLEANING_CONTAINERS --> CHECKING_IMAGES: 容器清理完成 CHECKING_IMAGES --> CLEANING_IMAGES: 检测到悬空镜像 CHECKING_IMAGES --> CHECKING_VOLUMES: 无悬空镜像 CLEANING_IMAGES --> CHECKING_VOLUMES: 镜像清理完成 CHECKING_VOLUMES --> CLEANING_VOLUMES: 检测到悬空卷 CHECKING_VOLUMES --> COMPLETED: 无悬空卷 CLEANING_VOLUMES --> COMPLETED: 卷清理完成 COMPLETED --> [*]: 清理完成 note right of CHECKING_CONTAINER : 检查是否在容器内运行 note right of CLEANING_CONTAINERS : 删除status=exited的容器 note right of CHECKING_IMAGES : 查询dangling=true的镜像 note right of CLEANING_IMAGES : 删除悬空镜像 note right of CHECKING_VOLUMES : 查询dangling=true的卷 note right of CLEANING_VOLUMES : 删除悬空卷

5.4 设备映射状态机

stateDiagram-v2 [*] --> IDLE: 系统启动 IDLE --> DETECTING_DEVICE: 设备插入 DETECTING_DEVICE --> MATCHING_RULE: 设备检测完成 MATCHING_RULE --> CREATING_SYMLINK: 规则匹配成功 MATCHING_RULE --> IDLE: 规则匹配失败 CREATING_SYMLINK --> SETTING_PERMISSIONS: 符号链接创建完成 SETTING_PERMISSIONS --> SETTING_OWNERSHIP: 权限设置完成 SETTING_OWNERSHIP --> READY: 所有权设置完成 READY --> IDLE: 设备移除 note right of DETECTING_DEVICE : Udev检测设备属性 note right of MATCHING_RULE : 匹配udev规则文件 note right of CREATING_SYMLINK : 创建设备符号链接 note right of SETTING_PERMISSIONS : 设置设备访问权限 note right of SETTING_OWNERSHIP : 设置设备所有者和组

6. 源码分析

6.1 setup_host.sh脚本分析

6.1.1 脚本初始化部分

setup_host.sh脚本是主机配置的核心脚本,负责设置Apollo运行所需的基础环境。脚本首先通过版权声明和许可证信息,确保代码的合法性和规范性。脚本使用Apache 2.0许可证,这是开源项目中常用的许可证类型。

bash 复制代码
#!/usr/bin/env bash

###############################################################################
# Copyright 2018 The Apollo Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
###############################################################################
6.1.2 Apollo根目录定位

脚本通过动态计算Apollo根目录,确保脚本可以从任何位置执行。这种设计提高了脚本的灵活性和可移植性。

bash 复制代码
APOLLO_ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd )"

这段代码使用了Bash的内置命令来获取脚本所在目录的绝对路径。${BASH_SOURCE[0]}获取当前脚本的路径,dirname命令提取目录部分,cd命令切换到该目录,pwd命令获取当前工作目录的绝对路径。最后通过../..导航到Apollo项目的根目录。

6.1.3 核心转储配置

核心转储配置是系统调试和故障分析的重要功能。脚本通过修改/proc/sys/kernel/core_pattern文件来设置核心转储文件的存储位置和命名格式。

bash 复制代码
# Setup core dump format.
if [ -e /proc/sys/kernel ]; then
  echo "${APOLLO_ROOT_DIR}/data/core/core_%e.%p" | \
      sudo tee /proc/sys/kernel/core_pattern
fi

这段代码首先检查/proc/sys/kernel目录是否存在,这是Linux内核参数的虚拟文件系统。如果存在,则使用echo命令将核心转储格式写入core_pattern文件。格式字符串core_%e.%p中,%e表示可执行文件名,%p表示进程ID。sudo tee命令确保以root权限写入文件。

6.1.4 NTP时间同步配置

时间同步对于分布式系统至关重要,特别是对于需要精确时间戳的自动驾驶系统。脚本通过配置cron任务来实现每分钟一次的时间同步。

bash 复制代码
# Setup ntpdate to run once per minute. Log at /var/log/syslog.
grep -q ntpdate /etc/crontab
if [ $? -ne 0 ]; then
  echo "*/1 * * * * root ntpdate -v -u us.pool.ntp.org" | \
      sudo tee -a /etc/crontab
fi

这段代码首先使用grep -q命令在/etc/crontab文件中搜索ntpdate关键字。-q选项表示静默模式,只返回退出状态码。如果未找到(退出状态码不为0),则添加新的cron任务。cron任务格式*/1 * * * *表示每分钟执行一次,root指定执行用户,ntpdate -v -u us.pool.ntp.org是实际执行的命令,其中-v表示详细输出,-u表示使用非特权端口。

6.1.5 Udev规则安装

Udev规则用于管理Linux系统中的设备,包括设备命名、权限设置和符号链接创建。脚本将Apollo项目中的Udev规则文件复制到系统目录。

bash 复制代码
# Add udev rules.
sudo cp -r ${APOLLO_ROOT_DIR}/docker/setup_host/etc/* /etc/

这段代码使用cp -r命令递归复制docker/setup_host/etc/目录下的所有内容到系统的/etc/目录。sudo确保以root权限执行复制操作。这种设计允许Apollo项目自带设备规则,简化了部署过程。

6.1.6 UVC视频模块配置

UVC(USB Video Class)是USB视频设备的标准驱动。脚本通过配置UVC模块的时钟参数来优化视频设备的性能。

bash 复制代码
# Add uvcvideo clock config.
grep -q uvcvideo /etc/modules
if [ $? -ne 0 ]; then
  echo "uvcvideo clock=realtime" | sudo tee -a /etc/modules
fi

这段代码首先检查/etc/modules文件中是否已包含uvcvideo模块。如果未包含,则添加配置行uvcvideo clock=realtimeclock=realtime参数指定使用实时时钟,这对于视频采集的同步非常重要。

6.2 install_docker.sh脚本分析

6.2.1 脚本架构和辅助函数

install_docker.sh脚本负责Docker的安装和卸载,采用模块化设计,将不同功能封装到独立的函数中。脚本首先定义了一些颜色和格式的辅助变量,用于美化输出信息。

bash 复制代码
ARCH="$(uname -m)"

##==============================================================##
## Note(storypku): DRY broken for this self-contained script.
##==============================================================##
BOLD='\033[1m'
RED='\033[0;31m'
WHITE='\033[34m'
NO_COLOR='\033[0m'

function info() {
  (echo >&2 -e "[${WHITE}${BOLD}INFO${NO_COLOR}] $*")
}

function error() {
  (echo >&2 -e "[${RED}ERROR${NO_COLOR}] $*")
}

脚本使用uname -m命令获取系统架构,支持x86_64和aarch64两种架构。颜色变量使用ANSI转义序列定义,infoerror函数提供统一的日志输出接口。

6.2.2 文件系统支持安装

Docker使用overlay2存储驱动,需要内核支持overlay文件系统。install_filesystem_support函数负责检查和加载overlay模块。

bash 复制代码
function install_filesystem_support() {
  local kernel_version="$(uname -r)"
  if [ "$kernel_version" == "4.4.32-apollo-2-RT" ]; then
    info "Apollo realtime kernel ${kernel_version} found."
    sudo modprobe overlay
  else
    local kernel_version_major=${kernel_version:0:1}
    local overlay_ko_path="/lib/modules/$kernel_version/kernel/fs/overlayfs/overlay.ko"
    if [ "${kernel_version_major}" -ge 4 ] && [ -f "${overlay_ko_path}" ] ; then
      info "Linux kernel ${kernel_version} has builtin overlay2 support."
      sudo modprobe overlay
    elif [ ${kernel_version_major} -ge 4 ]; then
      error "Overlay kernel module not found at ${overlay_ko_path}." \
            "Are you running on a customized Linux kernel? "
      exit 1
    else
      error "Linux kernel version >= 4 expected. Got ${kernel_version}"
      exit 1
    fi
  fi
}

该函数首先获取内核版本,然后进行多种情况的处理。对于Apollo专用的实时内核,直接加载overlay模块。对于其他内核,检查主版本号是否大于等于4,并验证overlay模块文件是否存在。如果条件满足,加载overlay模块;否则输出错误信息并退出。

6.2.3 前置依赖包安装

Docker安装需要一些前置依赖包,install_prereq_packages函数负责安装这些依赖。

bash 复制代码
function install_prereq_packages() {
  sudo apt-get -y update
  sudo apt-get -y install \
    apt-transport-https \
    ca-certificates \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
}

该函数首先更新APT包索引,然后安装一系列必要的软件包。apt-transport-https支持通过HTTPS访问APT仓库,ca-certificates提供SSL证书,curl用于下载文件,gnupg-agent用于GPG密钥管理,software-properties-common用于管理软件源。

6.2.4 Docker仓库配置和安装

setup_docker_repo_and_install函数负责配置Docker官方仓库并安装Docker软件包。

bash 复制代码
function setup_docker_repo_and_install() {
  local issues_link="https://github.com/ApolloAuto/apollo/issues"
  local arch_alias=
  if [ "${ARCH}" == "x86_64" ]; then
    arch_alias="amd64"
  elif [ "${ARCH}" == "aarch64" ]; then
    arch_alias="arm64"
  else
    error "Currently, ${ARCH} support has not been implemented yet." \
          "You can create an issue at ${issues_link}."
    exit 1
  fi

  curl -fsSL "https://download.docker.com/linux/ubuntu/gpg" | sudo apt-key add -
  sudo add-apt-repository \
    "deb [arch=${arch_alias}] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) \
    stable"
  sudo apt-get update
  sudo apt-get install -y docker-ce \
    docker-ce-cli \
    containerd.io
}

该函数首先根据系统架构设置架构别名,x86_64对应amd64,aarch64对应arm64。然后使用curl下载Docker的GPG密钥并添加到APT密钥环。接着添加Docker官方APT仓库,仓库URL包含架构别名和Ubuntu发行版代号。最后更新包索引并安装Docker相关软件包。

6.2.5 安装后配置

post_install_settings函数负责Docker安装后的配置,包括用户权限设置和服务重启。

bash 复制代码
function post_install_settings() {
  sudo usermod -aG docker $USER
  sudo systemctl restart docker
  # sudo groupadd docker
  # sudo gpasswd -a $USER docker
  # newgrp docker
}

该函数使用usermod命令将当前用户添加到docker组,这样用户就可以在不使用sudo的情况下执行Docker命令。然后使用systemctl重启Docker服务,使配置生效。注释掉的代码提供了另一种用户组配置方法。

6.2.6 Docker卸载功能

uninstall_docker函数提供了Docker的卸载功能,用于清理Docker相关软件包和配置。

bash 复制代码
function uninstall_docker() {
  sudo apt-get -y remove docker docker-engine docker.io
  sudo apt-get purge docker-ce

  sudo sed -i '/download.docker.com/d' /etc/apt/sources.list
  sudo apt-key del 0EBFCD88
}

该函数首先使用apt-get remove删除旧版本的Docker软件包,然后使用apt-get purge彻底删除Docker CE及其配置文件。接着使用sed命令从APT源列表中删除Docker仓库配置,最后删除Docker的GPG密钥。

6.2.7 主函数和命令行参数处理

main函数和install_docker函数提供了命令行接口,支持install和uninstall两种操作。

bash 复制代码
function install_docker() {
  # Architecture support, currently: x86_64, aarch64
  install_filesystem_support
  install_prereq_packages
  setup_docker_repo_and_install
  post_install_settings
}

function main() {
  case $1 in
    install)
      install_docker
      ;;
    uninstall)
      uninstall_docker
      ;;
    *)
      install_docker
      ;;
  esac
}

main "$@"

install_docker函数按顺序调用各个安装步骤函数。main函数使用case语句处理命令行参数,支持install、uninstall和默认操作。最后调用main函数并传递所有命令行参数。

6.3 cleanup_resources.sh脚本分析

6.3.1 容器环境检测

cleanup_resources.sh脚本用于清理Docker资源,首先检测是否在容器环境中运行。

bash 复制代码
if [ -f /.dockerenv ]; then
  echo "Oops, this script is expected to run on host rather than from within container."
  exit 1
fi

这段代码检查/.dockerenv文件是否存在,该文件是Docker容器创建的标识文件。如果存在,说明脚本在容器内运行,输出错误信息并退出。这种设计防止了在容器内执行主机清理操作。

6.3.2 已退出容器清理

脚本使用Docker命令查询和清理已退出的容器。

bash 复制代码
exited_containers="$(docker ps -qa --no-trunc --filter "status=exited")"
if [ -z "${exited_containers}" ]; then
    echo "Congrats, no exited docker containers found."
else
    echo "Exited containers found, cleanup ..."
    docker rm ${exited_containers}
fi

这段代码使用docker ps -qa命令查询所有容器(包括停止的容器),--no-trunc选项显示完整的容器ID,--filter "status=exited"过滤出已退出的容器。如果查询结果为空,输出提示信息;否则使用docker rm命令删除这些容器。

6.3.3 悬空镜像清理

悬空镜像是没有标签且未被任何容器使用的镜像,占用磁盘空间。

bash 复制代码
dangling_images="$(docker images --filter "dangling=true" -q --no-trunc)"
if [ -z "${dangling_images}" ]; then
    echo "Congrats, no dangling docker images found."
else
    echo "Dangling images found, cleanup ..."
    docker rmi ${dangling_images}
fi

这段代码使用docker images命令查询镜像,--filter "dangling=true"过滤出悬空镜像,-q选项只显示镜像ID,--no-trunc显示完整的镜像ID。如果查询结果为空,输出提示信息;否则使用docker rmi命令删除这些镜像。

6.3.4 悬空卷清理

悬空卷是不被任何容器使用的Docker卷。

bash 复制代码
dangling_volumes="$(docker volume ls -qf dangling=true)"
if [ -z "${dangling_volumes}" ]; then
    echo "Congrats, no dangling docker volumes found."
else
    echo "Dangling volumes found, cleanup ..."
    docker volume rm ${dangling_volumes}
fi

这段代码使用docker volume ls命令查询卷,-qf dangling=true过滤出悬空卷并只显示卷名。如果查询结果为空,输出提示信息;否则使用docker volume rm命令删除这些卷。

6.4 Udev规则文件分析

6.4.1 FPD-Link相机规则

99-asucam.rules文件定义了FPD-Link相机的Udev规则,用于将相机设备映射到Apollo系统识别的标准设备名。

bash 复制代码
SUBSYSTEM=="video4linux", SUBSYSTEMS=="pci", ATTR{name}=="FPD5-0106", MODE="0666", SYMLINK+="camera/front_6mm", OWNER="apollo", GROUP="apollo"
SUBSYSTEM=="video4linux", SUBSYSTEMS=="pci", ATTR{name}=="FPD5-0106", MODE="0666", SYMLINK+="camera/obstacle", OWNER="apollo", GROUP="apollo"
SUBSYSTEM=="video4linux", SUBSYSTEMS=="pci", ATTR{name}=="FPD5-0106", MODE="0666", SYMLINK+="camera/lanemark", OWNER="apollo", GROUP="apollo"
SUBSYSTEM=="video4linux", SUBSYSTEMS=="pci", ATTR{name}=="FPD4-0101", MODE="0666", SYMLINK+="camera/front_12mm", OWNER="apollo", GROUP="apollo"
SUBSYSTEM=="video4linux", SUBSYSTEMS=="pci", ATTR{name}=="FPD4-0101", MODE="0666", SYMLINK+="camera/trafficlights", OWNER="apollo", GROUP="apollo"
SUBSYSTEM=="video4linux", SUBSYSTEMS=="pci", ATTR{name}=="FPD1-0002", MODE="0666", SYMLINK+="camera/left_front", OWNER="apollo", GROUP="apollo"
SUBSYSTEM=="video4linux", SUBSYSTEMS=="pci", ATTR{name}=="FPD2-0103", MODE="0666", SYMLINK+="camera/right_front", OWNER="apollo", GROUP="apollo"
SUBSYSTEM=="video4linux", SUBSYSTEMS=="pci", ATTR{name}=="FPD3-0002", MODE="0666", SYMLINK+="camera/rear_6mm", OWNER="apollo", GROUP="apollo"

每条Udev规则包含多个匹配条件和动作。SUBSYSTEM=="video4linux"指定设备子系统为视频设备,SUBSYSTEMS=="pci"指定父设备子系统为PCI,ATTR{name}=="FPD5-0106"指定设备名称属性。动作部分包括MODE="0666"设置设备权限为读写,SYMLINK+="camera/front_6mm"创建符号链接,OWNER="apollo"GROUP="apollo"设置设备所有者和组。

6.4.2 USB串口GPS规则

99-usbtty.rules文件定义了USB串口GPS设备的Udev规则。

bash 复制代码
SUBSYSTEM=="tty", SUBSYSTEMS=="usb-serial", DRIVERS=="novatel_gps", ATTRS{port_number}=="0", MODE="0666", SYMLINK+="novatel0", OWNER="apollo", GROUP="apollo"
SUBSYSTEM=="tty", SUBSYSTEMS=="usb-serial", DRIVERS=="novatel_gps", ATTRS{port_number}=="1", MODE="0666", SYMLINK+="novatel1", OWNER="apollo", GROUP="apollo"
SUBSYSTEM=="tty", SUBSYSTEMS=="usb-serial", DRIVERS=="novatel_gps", ATTRS{port_number}=="2", MODE="0666", SYMLINK+="novatel2", OWNER="apollo", GROUP="apollo"

这些规则用于将Novatel GPS设备的USB串口映射到标准设备名。SUBSYSTEM=="tty"指定设备子系统为终端设备,SUBSYSTEMS=="usb-serial"指定父设备子系统为USB串口,DRIVERS=="novatel_gps"指定驱动程序为Novatel GPS驱动,ATTRS{port_number}=="0"指定端口号。每条规则创建一个符号链接novatel0novatel1novatel2,对应不同的GPS端口。

7. 设计模式

7.1 模板方法模式(Template Method Pattern)

setup_host.sh脚本体现了模板方法模式的思想。脚本定义了主机配置的固定流程,包括核心转储配置、NTP时间同步、Udev规则安装和UVC模块配置等步骤。这些步骤按照固定的顺序执行,但每个步骤的具体实现可以独立变化。

bash 复制代码
# 模板方法:主机配置流程
# 1. 配置核心转储格式
if [ -e /proc/sys/kernel ]; then
  echo "${APOLLO_ROOT_DIR}/data/core/core_%e.%p" | \
      sudo tee /proc/sys/kernel/core_pattern
fi

# 2. 设置NTP时间同步
grep -q ntpdate /etc/crontab
if [ $? -ne 0 ]; then
  echo "*/1 * * * * root ntpdate -v -u us.pool.ntp.org" | \
      sudo tee -a /etc/crontab
fi

# 3. 安装Udev规则
sudo cp -r ${APOLLO_ROOT_DIR}/docker/setup_host/etc/* /etc/

# 4. 配置UVC视频模块
grep -q uvcvideo /etc/modules
if [ $? -ne 0 ]; then
  echo "uvcvideo clock=realtime" | sudo tee -a /etc/modules
fi

这种设计模式的优势在于:

  1. 提供了统一的配置流程,确保所有必要的配置步骤都被执行
  2. 每个步骤可以独立修改,不影响其他步骤
  3. 便于添加新的配置步骤,扩展系统功能

7.2 策略模式(Strategy Pattern)

install_docker.sh脚本体现了策略模式的思想。脚本支持install和uninstall两种操作策略,通过命令行参数选择执行哪种策略。

bash 复制代码
function main() {
  case $1 in
    install)
      install_docker
      ;;
    uninstall)
      uninstall_docker
      ;;
    *)
      install_docker
      ;;
  esac
}

这种设计模式的优势在于:

  1. 将不同的操作策略封装到独立的函数中
  2. 通过统一的接口(main函数)选择执行策略
  3. 便于添加新的操作策略,如upgrade、reinstall等

7.3 责任链模式(Chain of Responsibility Pattern)

install_docker.sh脚本的install_docker函数体现了责任链模式的思想。函数按顺序调用多个子函数,每个子函数负责一个特定的安装步骤。如果某个步骤失败,整个安装过程终止。

bash 复制代码
function install_docker() {
  # Architecture support, currently: x86_64, aarch64
  install_filesystem_support
  install_prereq_packages
  setup_docker_repo_and_install
  post_install_settings
}

这种设计模式的优势在于:

  1. 将复杂的安装过程分解为多个简单的步骤
  2. 每个步骤可以独立测试和调试
  3. 便于修改或替换某个步骤的实现

7.4 工厂模式(Factory Pattern)

Udev规则文件体现了工厂模式的思想。规则文件定义了设备映射的"工厂",根据设备属性创建相应的符号链接。

bash 复制代码
SUBSYSTEM=="video4linux", SUBSYSTEMS=="pci", ATTR{name}=="FPD5-0106", MODE="0666", SYMLINK+="camera/front_6mm", OWNER="apollo", GROUP="apollo"

这种设计模式的优势在于:

  1. 根据设备属性自动创建设备映射
  2. 统一管理设备的命名和权限
  3. 便于添加新的设备类型和映射规则

7.5 单例模式(Singleton Pattern)

setup_host.sh脚本中的Apollo根目录变量体现了单例模式的思想。脚本通过计算获取唯一的Apollo根目录,并在整个脚本中使用这个变量。

bash 复制代码
APOLLO_ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd )"

这种设计模式的优势在于:

  1. 确保整个脚本使用相同的根目录路径
  2. 避免重复计算,提高效率
  3. 便于维护和修改根目录路径

7.6 观察者模式(Observer Pattern)

cleanup_resources.sh脚本体现了观察者模式的思想。脚本"观察"Docker资源的状态,并根据状态执行相应的清理操作。

bash 复制代码
exited_containers="$(docker ps -qa --no-trunc --filter "status=exited")"
if [ -z "${exited_containers}" ]; then
    echo "Congrats, no exited docker containers found."
else
    echo "Exited containers found, cleanup ..."
    docker rm ${exited_containers}
fi

这种设计模式的优势在于:

  1. 根据资源状态动态执行清理操作
  2. 避免不必要的清理操作,提高效率
  3. 提供清晰的反馈信息,增强用户体验

7.7 命令模式(Command Pattern)

install_docker.sh脚本体现了命令模式的思想。脚本将不同的操作封装到独立的函数中,通过main函数统一调度执行。

bash 复制代码
function install_docker() {
  install_filesystem_support
  install_prereq_packages
  setup_docker_repo_and_install
  post_install_settings
}

function uninstall_docker() {
  sudo apt-get -y remove docker docker-engine docker.io
  sudo apt-get purge docker-ce
  sudo sed -i '/download.docker.com/d' /etc/apt/sources.list
  sudo apt-key del 0EBFCD88
}

这种设计模式的优势在于:

  1. 将操作封装到独立的函数中,提高代码的可读性
  2. 便于添加新的操作命令
  3. 支持操作的撤销和重做(如uninstall操作)

7.8 装饰器模式(Decorator Pattern)

install_docker.sh脚本中的info和error函数体现了装饰器模式的思想。这些函数装饰了标准的echo命令,添加了颜色和格式化输出。

bash 复制代码
function info() {
  (echo >&2 -e "[${WHITE}${BOLD}INFO${NO_COLOR}] $*")
}

function error() {
  (echo >&2 -e "[${RED}ERROR${NO_COLOR}] $*")
}

这种设计模式的优势在于:

  1. 统一了日志输出的格式
  2. 提高了日志信息的可读性
  3. 便于修改日志输出的样式和行为

8. 总结

apollo_docker_setup_host子模块通过精心设计的Shell脚本实现了Apollo主机环境的自动化配置。该模块采用模块化设计,将Docker安装、系统配置、设备管理和资源清理等功能封装到独立的脚本中,提高了代码的可维护性和可重用性。通过模板方法、策略模式、责任链等设计模式的应用,实现了灵活的配置流程和错误处理机制。Udev规则文件提供了统一的设备映射方案,简化了设备管理。该模块为Apollo系统在容器化环境中运行提供了坚实的基础设施支持,是Apollo部署流程中不可或缺的重要组成部分。

相关推荐
wal13145202 小时前
OpenClaw 2026.4.2 版本更新:默认 YOLO 模式,告别批准提示
人工智能·yolo·openclaw
yanwumuxi2 小时前
Windows本地部署Dify(Docker)
人工智能·docker·语言模型
鼎上西瓜刀2 小时前
labelimg在windows上的使用
人工智能·深度学习
2301_764441332 小时前
大模型的“做梦”机制与Harness Engineering(驾驭工程)
人工智能·语言模型·自然语言处理
夏沫の梦2 小时前
生成式推荐系统:技术演进、核心架构与工业实践
人工智能
AnchorYYC2 小时前
TEI Inference Toolkit - 工业级Embedding/NLI /Reranking服务调用最佳实践
人工智能·python·持续部署·#大模型应用
QYR_112 小时前
多道分析器市场深度分析:核技术应用升级驱动下的数字化转型机遇
人工智能·市场调研
Ulyanov2 小时前
Pymunk 2D物理游戏开发教程系列 第二篇:约束与关节篇 -《摇摆特技车》
python·架构·系统仿真·雷达电子战·仿真引擎
跨境卫士-小汪2 小时前
平台验证升级以后社媒团队如何避免账号批量异常
大数据·人工智能·产品运营·跨境电商·营销策略
璞华Purvar2 小时前
香精香料PLM优选:璞华易研以AI配方能力,赋能行业研发升级(2026年)
大数据·人工智能