使用ECS进行部署是计算巢服务常用的部署场景,在服务完成部署以后,怎么进行升级是经常被忽略的问题,本实践将针对常见的两种部署方式如何升级进行说明,ISV在使用时可以具体场景具体分析,决定使用哪种部署方式,进而选择不同的升级方式。
部署方式介绍
ECS镜像部署方式:ECS镜像部署方式为将应用部署所需的软件包、容器镜像等依赖内容提前打包到ECS镜像中,适用于比较复杂的部署场景。优势是部署稳定快速,缺点是数据和软件应用同在系统盘中,在升级时直接替换系统盘会有数据覆盖问题,需要挂载数据盘来解决。
RunCommand命令部署方式:RunCommand命令部署方式不依赖ECS镜像,主要应用部署通过Shell命令完成,适用于相对简单的部署场景。优势是进行部署升级时都是通过RunCommand中的命令执行来完成,不需要进行系统盘更换,升级时没有数据问题,整体操作更简单,缺点是命令执行在某些情况下会存在不兼容的情况,比如ECS实例未开公网,拉取不下来文件等问题。
ECS镜像部署方式升级
镜像部署具有很多优势,适用于比较复杂或有卡点的部署场景,如docker部署场景不存在docker镜像无法拉取,所有的文件都已经事先准备好,不会出现命令式部署由于网络、环境等原因出现部署失败的情况,整体来说稳定而又快速,目前是我们ECS场景部署服务的最佳实践,在服务部署方面应用比较广泛。
升级存在问题
但在升级方面,ECS镜像部署存在数据和应用耦合的问题,ECS镜像进行升级操作会直接更换系统盘,用户的历史数据会被覆盖掉,解决方式是让用户自己备份数据,等升级完再进行倒入恢复,操作较麻烦,体验不好。
推荐解决方案
目前推荐的方式是ECS挂载数据盘,应用相关数据保存到数据盘中,实现软件应用和数据的分离。在服务进行升级时,进行系统盘更换也不会覆盖数据盘中的数据,用户数据不会丢失,系统盘更换后数据盘重新进行挂载,原有的应用数据还在。
具体过程如下图所示:
服务部署需要做以下改造:
创建ECS实例时进行数据盘挂载。
将软件应用数据存放路径改为数据盘挂载目录,有初始数据需要提前复制到数据盘中。
以创建服务时通过精选模板创建服务中的ECS镜像部署为例,以下为ECS挂载数据盘和修改应用数据存放路径的代码示例。
InstanceGroup:
Type: ALIYUN::ECS::InstanceGroup
Properties:
# 这里省去部分无关属性
SystemDiskCategory: cloud_essd
SystemDiskSize: 200
InternetMaxBandwidthOut: 1
IoOptimized: optimized
MaxAmount: 1
DiskMappings:
- Category: cloud_essd
Size: 200
# 数据盘初始化和挂载
UserData: |
#cloud-config
bootcmd:
- |
#!/bin/bash
function init_and_mount_data_disk() {
disk_device="/dev/vdb"
parted_disk_device="/dev/vdb1"
mount_point="/data"
# 已初始化的磁盘,跳过初始化,直接挂载
if blkid -o value -s TYPE $disk_device &>/dev/null; then
echo "Disk is already initialized."
else
echo "Disk is not initialized, initializing..."
cat >> /root/InitDataDisk.sh << "EOF"
#!/bin/bash
echo "p
n
p
w
" | fdisk -u /dev/vdb
EOF
/bin/bash /root/InitDataDisk.sh
rm -f /root/InitDataDisk.sh
mkfs -t ext4 $parted_disk_device
fi
cp /etc/fstab /etc/fstab.bak
mkdir $mount_point
echo `blkid $parted_disk_device | awk '{print $2}' | sed 's/\\\"//g'` $mount_point ext4 defaults 0 0 >> /etc/fstab
mount -a
}
init_and_mount_data_disk
RunInstallCommand:
Type: ALIYUN::ECS::RunCommand
Properties:
InstanceIds:
Fn::GetAtt:
- InstanceGroup
- InstanceIds
Type: RunShellScript
Sync: true
Timeout: 3600
CommandContent:
Fn::Sub:
- |
#!/bin/bash
function save_log_to_data_disk() {
mkdir -p /data/logs
chmod -R 666 /data/logs
# 使用数据盘目录存放日志,避免升级Ecs镜像时数据丢失
sed -i 's/LOG_DIR/\/data/g' /home/admin/application/deploy.sh
}
# 用于触发版本升级
echo "当前时间:${CurTime}"
cd /home/admin/application
/bin/bash deploy.sh stop
save_log_to_data_disk
/bin/bash deploy.sh start
- AccountId:
Ref: ALIYUN::TenantId
CurTime:
Ref: CurTime
完整服务可以参考创建服务时,通过精选模板创建服务中的ECS镜像部署。
RunCommand部署方式升级
RunCommand部署方式适用于较简单的部署场景,主要通过执行Shell命令的方式去进行软件部署。
升级存在问题
RunCommand方式存在部分限制,如命令执行不能有卡点;部署在中国内地的ECS实例,不能拉取中国境外的容器镜像;不能拉取github上的文件等。
推荐解决方案
这种部署方式进行服务升级时比较简单,不涉及到系统盘的变更,不存在数据丢失的问题,新老服务版本的不同主要是RunCommand命令的内容不同,升级时通过执行新服务版本的RunCommand命令来完成升级。这种方式的RunCommand命令一般要执行以下三个步骤:
关闭原有应用。
更新应用部署内容(拉取软件包、容器镜像等)。
启动新版应用。
以创建服务时通过精选模板创建服务中的ECS软件包部署为例,以下为RunCommand命令实现的代码示例。
RunInstallCommand:
Type: ALIYUN::ECS::RunCommand
Properties:
InstanceIds:
Fn::GetAtt:
- InstanceGroup
- InstanceIds
Type: RunShellScript
Sync: true
Timeout: 3600
CommandContent:
Fn::Sub:
- |
#!/bin/bash
function stop_application() {
if [ -f "/home/admin/application/deploy.sh" ]; then
/bin/bash /home/admin/application/deploy.sh stop
fi
}
function start_application() {
yum install -y java
mkdir -p /home/admin/application
cd /home/admin/application
# 2. 拉取新版的软件包
wget '{{ computenest::file::springboot }}' -O package.tgz
tar xvf package.tgz
# 3. 启动新版应用
/bin/bash deploy.sh start
}
# 1. 关闭原有应用
stop_application
start_application
- AccountId:
Ref: ALIYUN::TenantId
完整服务可以参考创建服务时,通过精选模板创建服务中的ECS软件包部署和 ECS容器部署。