OTA是一种无线分发和安装软件更新的技术,可以让用户在不连接电脑的情况下直接通过设备的无线网络(如Wi-Fi或蜂窝数据网络)进行系统升级。
Android的升级(包含系统和应用程序)
操作系统升级
系统升级的实现过程大致如下:
-
通知和检查更新:谷歌发布新版本的Android操作系统后,设备制造商会基于这个新版本进行定制和优化,然后将更新推送给用户。用户的设备会定期自动检查更新,或者用户可以手动在设备的设置中检查更新。如果有可用的更新,设备会收到更新通知。
-
下载更新:用户同意安装更新后,设备会在后台下载更新包。为了节省流量,用户可以选择仅在连接到Wi-Fi时下载。
-
安装更新:下载完成后,设备会提示用户安装更新。安装过程中,设备会重启并进入恢复模式。在恢复模式下,设备会用新的系统镜像替换旧的系统镜像。Android系统采用A/B(Seamless)更新机制,这种机制允许设备在后台安装更新,而不会影响用户正在进行的操作。当安装完成后,设备会重启并运行新的系统版本。
应用程序升级
Android应用程序的升级主要通过Google Play商店进行。应用开发者发布新版本后,Google Play商店会自动通知用户进行更新。用户可以选择自动或手动更新应用。更新过程如下:
-
通知和检查更新:Google Play商店会定期检查应用更新,并通过通知栏提示用户有可用的更新。
-
下载更新:用户同意安装更新后,Google Play商店会下载应用程序的更新包。为了节省流量,用户可以选择仅在连接到Wi-Fi时下载。
-
安装更新:下载完成后,Google Play商店会自动安装更新包,替换旧版本的应用程序。
A/B(Seamless)更新机制
A/B(Seamless)更新机制是Android 7.0(Nougat)及以后版本引入的一种更新策略,旨在让系统更新过程更加平滑、快速且对用户几乎无感知。A/B更新机制的实现依赖于设备上的双分区系统,每个分区都有一个完整的系统镜像。这两个分区通常被称为A分区和B分区。
当有新的系统更新可用时,A/B更新机制按照以下步骤进行:
-
后台下载:当用户同意安装系统更新后,设备会在后台下载新的系统镜像。在此过程中,用户可以继续正常使用设备,不会受到影响。
-
非活动分区安装:下载完成后,新的系统镜像会被安装到当前非活动的分区。例如,如果当前活动分区是A分区,那么新的系统镜像将会被安装到B分区。此时,A分区仍然运行着旧版本的系统,用户仍然可以正常使用设备。
-
分区切换:当新系统镜像安装完成后,设备会在下次启动时切换到新的活动分区。例如,如果新的系统镜像安装在B分区,那么设备在重启后会从B分区启动。这时,用户会看到系统已经升级到新版本。
这种A/B更新机制的优势在于:
-
减少设备停机时间:由于系统更新主要在后台进行,用户在更新过程中仍然可以正常使用设备,减少了设备因更新而导致的停机时间。
-
降低升级风险:如果新系统镜像在安装过程中出现问题,设备仍然可以通过切换回原来的分区来恢复正常使用。这降低了因更新失败导致设备无法使用的风险。
-
无缝升级:由于分区切换通常在设备启动时完成,用户几乎感受不到系统更新的过程,实现了无缝升级。
需要注意的是,并非所有Android设备都支持A/B(Seamless)更新机制。设备制造商需要在硬件和软件层面支持该机制,才能为用户提供无缝升级体验。
Ubuntu系统的升级
Ubuntu设备本身并未采用与Android完全相同的A/B(Seamless)更新机制。但Ubuntu Core系统,一种针对物联网(IoT)设备和嵌入式系统设计的Ubuntu版本,采用了类似的更新策略。Ubuntu Core使用名为"snap"的软件包格式来分发和管理应用程序。Snap软件包通过类似于Android A/B更新机制的策略来实现系统和应用程序更新,具体包括以下特点:
-
事务性更新:Snap软件包提供原子性更新,确保在更新过程中系统和应用程序始终保持一致的状态。即使在更新过程中出现故障,系统也能恢复到更新前的状态,降低因更新失败导致的设备故障风险。
-
同时保留新旧版本:Snap软件包允许在更新应用程序时保留新旧版本。当新版本出现问题时,可以轻松回退到旧版本。这类似于Android A/B更新机制中在两个分区上保留新旧系统镜像的做法。
-
自动更新:Ubuntu Core系统会自动检查并安装Snap软件包的更新。与Android A/B更新机制类似,这种自动更新策略可以确保设备始终保持最新状态,提高安全性。
总之,虽然Ubuntu设备并未采用与Android完全相同的A/B(Seamless)更新机制,但Ubuntu Core系统中的Snap软件包采用了类似的策略来实现系统和应用程序更新。这些策略同样可以提供无缝升级体验和降低更新风险。
Snap软件包升级机制
Snap是一个由Canonical开发的通用软件包管理系统,用于在各种Linux发行版中分发和管理应用程序。Snap提供了一个沙盒环境,使应用程序能够在不同的Linux发行版上以一致的方式运行。Snap软件包包含了应用程序及其所有依赖项,这使得升级过程变得相对简单。
Snap软件包升级遵循以下步骤:
-
检查更新:首先,Snap客户端会定期检查Snap应用程序的更新。这可以通过与Snap商店通信来实现,以查询是否有任何可用的更新。
-
下载更新:如果发现新版本,Snap客户端会自动下载更新的Snap包。这个下载过程发生在后台,用户通常不会注意到正在进行的更新操作。
-
安装更新:一旦更新的Snap包被下载,它将被安装在一个新的、隔离的目录中。这意味着新版本的应用程序将与旧版本并行安装,不会影响旧版本的文件。
-
更新符号链接:安装完成后,Snap客户端会更新指向应用程序的符号链接,将其指向新版本。这会确保下次启动应用程序时,将使用更新后的版本。
-
回滚(如有必要):Snap具有自动回滚功能,这意味着如果新版本的应用程序无法正常启动或运行,Snap客户端会自动将符号链接切换回旧版本。这有助于确保在升级过程中不会出现长时间的应用程序中断。
Snap通过将新版本与旧版本并行安装并使用符号链接切换的方式,实现了简单、稳定且安全的升级过程。这种方法还提供了一定程度的容错能力,因为如果新版本出现问题,系统可以自动回滚到旧版本。
Ubuntu应用程序的升级
Snap软件包:
如果你的软件服务是作为一个Snap软件包分发的,那么OTA更新将自动通过Snap Store进行。Snap软件包是一种自包含的应用程序格式,包含了应用程序所需的所有依赖项。Snap软件包的更新通过以下步骤进行:
-
发布更新:在你的软件服务有新版本时,将新版本发布到Snap Store。
-
自动检查更新:用户的Ubuntu设备会定期自动检查Snap Store中的软件包更新。
-
下载和安装更新:当发现新版本时,设备会自动下载并安装更新。这个过程是无缝的,用户不需要进行任何手动操作。
使用APT软件包管理器:
如果你的软件服务是作为一个Debian软件包(.deb)分发的,你可以将其托管在一个APT软件源中。要实现OTA更新,你需要执行以下操作:
-
创建和维护APT软件源:为你的软件服务创建一个APT软件源,并在新版本发布时更新软件源。
-
将软件源添加到用户设备:用户需要将你的APT软件源添加到他们的设备,以便通过apt-get或其他APT工具检查和安装更新。
-
用户检查和安装更新:用户的设备会定期自动检查更新,或者用户可以手动运行sudo apt-get update和sudo apt-get upgrade来检查并安装更新。
使用第三方软件更新服务:
除了Snap和APT之外,还有一些第三方服务可以帮助你实现Ubuntu上的软件服务的OTA更新。例如,AppImage、Flatpak等分发格式,或者GitHub、GitLab等代码托管服务。你需要遵循这些服务的文档和指南来发布和管理你的软件服务的更新。
ROS OTA
ROS软件包更新的流程
前期准备
-
搭建自己的APT软件源服务端:在一台可访问的服务器上搭建APT软件源。这需要配置Web服务器(例如Apache或Nginx)并设置相应的目录结构。
-
使用catkin打包ROS软件:将您的ROS软件包使用catkin和bloom打包成.deb格式。这需要按照特定的格式和步骤来操作,以便生成可用于APT软件源的软件包。
-
上传.deb文件并更新Packages.gz:将生成的.deb文件上传到APT软件源服务器上的相应目录。然后,更新Packages.gz文件以包含新上传的软件包信息。这可以通过运行dpkg-scanpackages命令实现。
初始准备
-
编写更新软件包的业务逻辑代码:编写一个Python脚本或其他编程语言的程序,实现软件包更新的功能。此代码应包含调用apt-get命令来检查并安装更新。
-
手动安装第一版带有外部接口的软件包:在目标设备上,手动安装第一版的软件包。这可以通过使用dpkg或apt-get命令完成。
-
配置systemd服务实现自启动:编写一个systemd服务单元文件,以便将您的程序设置为开机自启动。服务单元文件应指定程序的可执行文件路径以及必要的配置。将服务单元文件放置在/etc/systemd/system/目录下,并运行sudo systemctl enable your-service命令来启用服务。
更新过程
-
触发更新接口:当有新版本可用时,通过云端或Android端下发指令触发更新。这可以通过使用HTTP请求、MQTT消息或其他通信方式实现。
-
调用更新命令:在您的业务逻辑代码中,当接收到更新指令时,执行apt-get update和apt-get install your-package命令来更新软件包。这将确保从APT软件源下载并安装最新版本的软件包。
-
安装软件到指定位置:在打包过程中,确保.deb文件中指定了正确的安装路径。当使用apt-get install命令安装软件包时,文件将被安装到指定的位置。
重启设备或服务
回滚
回滚的大致流程如下:
-
在APT软件源中保留旧版本:在更新软件包时,不要删除APT软件源中的旧版本。这将使得在需要回滚时,可以从软件源重新安装旧版本。
-
检测更新后的软件包是否正常运行:在安装新版本软件包后,检测软件是否正常运行。这可以通过检查程序的返回值或者观察其运行日志等方式实现。根据具体情况,可以自定义检测方法。
-
回滚到旧版本:如果在检测过程中发现新版本软件包存在问题,可以使用apt-get install命令重新安装旧版本。例如:
sudo apt-get install your-package=old.version.number
请将old.version.number替换为旧版本的实际版本号。
- 记录回滚事件:为了确保在回滚过程中有足够的信息来调查和解决问题,可以记录回滚事件。这可以包括在回滚前后收集程序的运行日志、记录回滚原因等。
需要注意的是,回滚过程可能需要根据实际情况进行调整。例如,在某些情况下,可能需要恢复数据库或配置文件的旧版本,或者执行其他特定操作以确保回滚后的系统能够正常运行。
重点事项
APT软件源如何搭建
- 准备服务器和域名:
你需要一个可供外部访问的服务器来托管APT软件源。服务器可以是云服务器、虚拟私有服务器(VPS)或自有服务器。此外,为了方便用户访问,你可能还需要一个域名来指向你的服务器。
- 安装并配置Web服务器:
在服务器上安装一个Web服务器,例如Apache、Nginx或Lighttpd。Web服务器将用于托管APT软件源的文件。安装完成后,为你的APT软件源创建一个新的虚拟主机或网站,并为其配置适当的目录权限。
- 准备软件包:
将你的Debian软件包(.deb文件)上传到服务器的指定目录。你可以根据需要组织目录结构,但通常会按照架构和发行版划分子目录。例如:
/your-repo/
├── dists/
│ ├── stable/
│ │ ├── main/
│ │ │ ├── binary-amd64/
│ │ │ └── binary-i386/
│ │ └── contrib/
│ │ ├── binary-amd64/
│ │ └── binary-i386/
│ └── unstable/
│ └── main/
│ ├── binary-amd64/
│ └── binary-i386/
└── pool/
├── main/
│ ├── p/
│ │ └── your-package/
│ │ └── your-package_1.0.0_amd64.deb
│ └── q/
└── contrib/
├── r/
└── s/
- 创建并签署Release文件:
在dists目录下,为每个发行版创建一个名为Release的文件。Release文件包含有关发行版的元数据,例如发行版名称、架构和组件。这是一个Release文件的示例:
Origin: Your Repository
Label: Your Repository
Suite: stable
Version: 1.0
Codename: your-repo
Architectures: amd64 i386
Components: main contrib
Description: Your Repository
使用GPG密钥对Release文件进行签名,以确保软件源的安全性和完整性。首先创建一个GPG密钥对(如果尚未创建),然后使用gpg命令对Release文件进行签名,生成Release.gpg文件。将Release和Release.gpg文件上传到服务器的相应dists子目录。
- 创建软件包索引:
使用dpkg-scanpackages工具为每个子目录创建软件包索引。dpkg-scanpackages是dpkg-dev软件包的一部分,你可能需要在本地计算机上安装它。运行dpkg-scanpackages命令为每个子目录生成Packages和Packages.gz文件。例如:
dpkg-scanpackages binary-amd64 /dev/null | gzip -9c > binary-amd64/Packages.gz
dpkg-scanpackages binary-i386 /dev/null | gzip -9c > binary-i386/Packages.gz
将生成的Packages和Packages.gz文件上传到服务器的相应目录。
- 用户添加软件源:
用户需要在他们的Ubuntu系统中将你的APT软件源添加到/etc/apt/sources.list文件或在/etc/apt/sources.list.d/目录下创建一个新的.list文件。例如:
deb http://your-repo-domain.com/your-repo/ stable main contrib
deb-src http://your-repo-domain.com/your-repo/ stable main contrib
如果你对Release文件进行了签名,用户还需要导入你的GPG公钥,以便APT能够验证软件源的签名。你可以将GPG公钥以文件的形式提供给用户,或者将其上传到公共密钥服务器。用户可以使用以下命令导入GPG公钥:
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys YOUR_KEY_ID
或者,如果你将公钥保存为文件,用户可以使用以下命令导入:
sudo apt-key add your_public_key_file.gpg
- 用户更新软件包列表并安装软件:
用户需要运行以下命令以更新软件包列表并从你的APT软件源安装软件:
sudo apt-get update
sudo apt-get install your-package
catkin工具生成.deb格式的文件
bloom工具可以更轻松地将ROS软件包发布到APT存储库,从而为用户提供方便的.deb软件包安装。
以下是使用bloom发布ROS软件包的基本步骤:
- 安装bloom:首先,确保您已经安装了bloom。如果没有,可以使用以下命令安装:
sudo apt-get install python-bloom
- 为你的软件包创建一个新的版本:在项目的版本控制系统中,创建一个新的版本(例如,git tag)并将其推送到远程仓库。这是一个示例命令:
git tag 0.1.0
git push origin 0.1.0
请将0.1.0替换为您的软件包的实际版本号。
3. 运行bloom-release:运行bloom-release命令,为您的软件包生成.deb文件。您需要指定软件包的发行版(例如,Ubuntu 20.04)和ROS版本(例如,ROS Noetic)。例如:
bloom-release --rosdistro noetic --track focal your_package_name --version 0.1.0 --edit
请将your_package_name替换为您的实际软件包名称,将0.1.0替换为实际版本号。
在打包过程中,可以通过CMakeLists.txt文件指定软件包的安装路径。以下是一个示例CMakeLists.txt文件,其中演示了如何指定安装路径:
cmake_minimum_required(VERSION 3.0.2)
project(your_package_name)
find_package(catkin REQUIRED)
catkin_package()
include_directories(${catkin_INCLUDE_DIRS})
add_executable(your_executable src/your_source_file.cpp)
target_link_libraries(your_executable ${catkin_LIBRARIES})
# 指定二进制文件的安装路径
install(TARGETS your_executable
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
# 指定配置文件的安装路径
install(FILES
config/your_config_file
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/config
)
# 指定启动文件的安装路径
install(DIRECTORY launch/
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch
)
CI/CD
创建GitLab CI/CD配置文件:在项目的根目录下,创建一个名为.gitlab-ci.yml的文件。这个文件将包含用于构建和部署软件包的指令。以下是一个基本的示例:
image: ros:noetic
stages:
- build
- deploy
build_package:
stage: build
script:
- sudo apt-get update
- sudo apt-get install -y python3-bloom reprepro
- git tag $CI_COMMIT_REF_NAME
- bloom-release --rosdistro noetic --track focal your_package_name --version $CI_COMMIT_REF_NAME --edit
artifacts:
paths:
- ../your_package_name-release
deploy_package:
stage: deploy
script:
- sudo apt-get update
- sudo apt-get install -y sshpass
- cd ../your_package_name-release
- export SSHPASS=$DEPLOY_PASSWORD
- find . -type f -name "*.deb" -exec sshpass -e scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null {} $DEPLOY_USER@$DEPLOY_SERVER:/path/to/your/repo/ \;
- sshpass -e ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $DEPLOY_USER@$DEPLOY_SERVER 'cd /path/to/your/repo && dpkg-scanpackages . | gzip -9c > Packages.gz'
only:
- /^v\d+\.\d+\.\d+(-\S*)?$/
在这个示例中,我们定义了两个阶段:build和deploy。build_package任务负责使用bloom构建软件包,并将结果保存为构建工件。deploy_package任务负责将构建好的.deb文件上传到APT软件源服务器,并更新Packages.gz文件。
请注意,此示例需要以下环境变量:
DEPLOY_PASSWORD:用于在部署过程中进行身份验证的密码。
DEPLOY_USER:用于在部署过程中进行身份验证的用户名。
DEPLOY_SERVER:APT软件源服务器的地址。
您需要将这些环境变量添加到GitLab项目的CI/CD设置中。此外,将your_package_name替换为您的实际软件包名称,并将/path/to/your/repo替换为您的APT软件源服务器上的实际路径。
设置GitLab CI/CD触发条件:在示例.gitlab-ci.yml文件中,我们使用only关键字来指定仅在推送满足特定条件的标签时才执行部署任务。在这个例子中,部署仅在推送符合语义版本命名规范的标签时进行。您可以根据实际情况修改触发条件。例如,您可以选择在推送到特定分支时进行部署,或者在满足其他条件时触发部署。
提交并推送代码:将.gitlab-ci.yml文件添加到项目中,并将其推送到GitLab仓库。这将触发GitLab CI/CD流程。当满足触发条件时,CI/CD流程将自动执行build_package和deploy_package任务,从而自动构建并将新版本的软件包上传到APT软件源。