1.前言
资源编排服务ROS(Resource Orchestration Service)是阿里云提供的一项简化云计算资源管理的服务。开发者和管理员可以编写模板,在模板中定义所需的阿里云资源(例如:ECS实例、RDS数据库实例)、资源间的依赖关系等。ROS的编排引擎将根据模板自动完成所有资源的创建和配置,实现自动化部署及运维。
为什么选择ROS资源编排
企业云上大量的资源管理需求带来了效率、合规和成本方面的挑战,ROS可以帮助您轻松管理多个资源:
- 采用基础设施即代码(Infrastructure as Code, IaC)的设计理念,模板的管理融入CI/CD流程,确保合规性。
- 无需手动创建多个资源,使用模板一键部署多个资源。
- 提供大量模板示例和解决方案示例,帮助企业客户部署复杂解决方案。
- 利用云上弹性,按需批量部署和释放资源,节省成本。
本文将以一个小白视角,探索如何根据阿里云的文档从0开始写一篇ROS资源编排的代码。
2.工具准备
注:本次教程所有的操作均基于macos,其他平台的操作请自行稍作修改
2.1 ROS CDK安装
阿里云ROS CDK(Cloud Development Toolkit)是资源编排(ROS)提供的一种命令行工具,帮助您使用多种编程语言定义云资源。您无需使用繁琐的JSON或YAML模板语法,即可使用ROS CDK完成资源的创建和配置,实现自动化部署及运维。写一篇ROS自动化测试脚本的第一步是安装ROS CDK工具。如何安装ROS在帮助文档中已经写得非常详细。
2.2 python环境安装
2.2.1使用Anaconda(推荐)
ⅰ. 开源、免费,强大的python环境管理软件,社区版包含很多常用软件
ⅱ. 简单、方便的环境隔离与虚拟环境创建
ⅲ. Anaconda 还附带了一个叫做 Navigator(导航)的桌面 GUI 工具,可以直观地创建和管理环境,安装、删除扩展包
ⅳ. 下载链接
2.2.2 直接安装python
ⅰ. 下载链接
2.3 IDE、代码编辑器推荐
2.3.1 VScode(推荐)
ⅰ. 开源免费、轻量的代码编辑器
ⅱ. 社区生态好、更新快
ⅲ. 插件生态众多,推荐部分插件
- Python
- Atom Material Icons
- GitLens — Git supercharged
- IntelliCode
ⅳ. 下载链接
2.3.2 pycharm
ⅰ. 开箱即用、功能强大的IDE
ⅱ. 补全、代码检查等功能做得较好
ⅲ. 下载链接
2.4 AI编码插件
使用AI编码插件可以大幅提升代码编写效率,最经典的插件是Github Copilot,功能包括行/函数级自动续写、自然语言生成代码、生成单元测试、生成代码注释等等,然而这是一个收费插件(10美元/月),有很多免费版的插件可以平替,这里推荐几个
2.4.1 通义灵码
ⅰ. 通义千问系列产品,目前已经开放公测,阿里云账号可以直接使用
ⅱ. 功能简介与下载链接
ⅲ. 代码解释,询问一段代码是什么意思,帮忙理解代码逻辑,这里给出一段阿里云ROS的代码,但是插件给出的解释是AWS的CDK,但是除了这一点错误,其余的代码解释都是正确的
ⅳ. AI代码生成(根据注释生成代码),这里我只写了注释「#创建安全组」,插件自动帮我生成了一段代码来放开80端口
2.4.2 Amazon CodeWhisperer
ⅰ. AWS出品的AI编码工具,免费
ⅱ. 功能简介与下载链接
2.4.3 CodeGeeX
ⅰ. 模型开源
ⅱ. 功能简介与下载链接
3. 环境准备
3.1 创建工程目录并初始化工程
进入终端,执行以下命令,创建工程目录并初始化工程。每个ROS CDK应用都要求创建在一个独立的工程目录下,且该应用需要使用独立工程目录中模块的依赖项。所以在创建应用之前,需要先创建一个工程目录并进行初始化。
#创建iac-ros-cdk文件夹 mkdir iac-ros-cdk #进入iac-ros-cdk文件夹 cd iac-ros-cdk #使用ros cdk初始化项目 ros-cdk init --language=python --generate-only=true
3.2 创建虚拟环境
Python工程的运行依赖于虚拟环境(virtualenv),所以在初始化Python工程之后需要创建一个属于当前工程的虚拟环境。
python3 -m venv .venv
3.3 进入虚拟环境
source .venv/bin/activate
3.4 测试环境是否创建成功
在终端执行以下命令,测试环境是否创建成功
python
若出现此界面则说明你的虚拟环境已经创建成功,此时的项目应该是这样的
3.5 在编译器中配置环境
打开项目中的一个示例python文件「iac_ros_cdk_stack.py」,在VScode中选择python解释器,开始编写python代码
3.6 安装需要的包
此时无法解析编译器无法解析「ros_cdk_core」,表明当前python环境「.venv」里没有这个包
在VScode中新建终端,终端会自动执行命令切换到当前目录并激活「.venv」环境
将pip源换成清华大学的源,加速pip安装包的速度(可选),然后执行pip install xx,在「.venv」环境中安装包
# 将pip的源换为国内的清华源,加速下载,这一步为可选项 pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple # 安装ros_cdk_core pip install ros_cdk_core
示例,换源并安装「ros_cdk_core」这个包
以上安装方式对应的是单次安装一个包的情况,如果你需要安装很多包,可以在「requirements.txt」中添加你需要的包
然后在终端中执行如下命令来安装「requirements.txt」中写的所有包
#安装requirements.txt中写的包 pip install -r requirements.txt
3.7 配置阿里云凭证信息
登录阿里云官网,获取当前账号的AK信息
执行以下命令,配置阿里云凭证信息。
ros-cdk config
根据界面提示输入配置信息。
endpoint(optional, default:https://ros.aliyuncs.com): defaultRegionId(optional, default:cn-hangzhou):cn-hangzhou [1] AK [2] StsToken [3] RamRoleArn [4] EcsRamRole [0] CANCEL Authenticate mode [1...4 / 0]: 1 accessKeyId:************************ accessKeySecret:****************************** ✅ Your cdk configuration has been saved successfully!
配置内容说明:
- endpoint:ROS服务地址。
- defaultRegionId:ROS资源栈部署的地域。默认值为cn-hangzhou。
- Authenticate mode:鉴权方式。本示例的鉴权方式为AccessKey,您需要输入AccessKey ID和AccessKey Secret。关于如何获取AccessKey,请参见交互式配置(快速配置)。
4.代码编写演示
4.1 代码文件建立与命名规范
找到你领取的文档,这里以我本次领取的一篇文档为例
- 新建文件,要注意以下三点
- 文件正确归类—计算的ROS代码应该放在ECS目录下
- 文件正确命名—deploy_FTP_on_ECS_460772.py
- 「deploy_FTP_on_ECS」描述文档测试内容,在ECS上部署FTP服务
- 「460772」该文档的id
- 类的正确命名
- 类名应遵循 CapWords 约定
- 类名应该能体现当前类的功能,比如「DeployFTP」
4.2 找寻文档描述的前提条件
一般文档会描述实验前需要部署的资源,比如本篇文档规定了以下内容
- 实例规格:ecs.g6.large
- 操作系统:Alibaba Cloud Linux 3.2104 LTS 64位
现在已经知道需要首先建立一个ECS,建立ECS需要以下依赖资源
- VPC
- Vswitch
- 安全组
那么已经可以明确你的ROS代码需要编排的资源清单
4.3 分步骤编写代码
如果是没用过的资源怎么找必填参数、怎么填写参数(通过文档)
a. 建立VPC
# 创建VPC vpc = ecs.Vpc( self, "VPC", ecs.VPCProps( cidr_block="10.0.0.0/8", vpc_name="ftp-ros-cdk-vpc", description="This is ros python cdk from jinzhan", ), )
b. 建立VSwitch
# 创建交换机 vswitch = ecs.VSwitch( self, "VSwitch", ecs.VSwitchProps( zone_id=zone_id_param, vpc_id=vpc.resource.ref, cidr_block="10.0.0.0/20", ), )
c. 创建ECS
ⅰ. 首先定义ECS的密码
# 设置ECS实例密码 ecs_password_param = core.RosParameter( self, "ECS_password", type=core.RosParameterType.STRING, association_property="ALIYUN::ECS::Instance::Password", default_value="Jinz_Aliyun1", )
ⅱ. 定义ECS使用的镜像
# 设置ECS实例镜像 image_id_param = core.RosParameter( self, "ImageId", default_value="aliyun_3_x64_20G_alibase_20230629.vhd", type=core.RosParameterType.STRING, description={"zh-cn": "镜像ID", "en": "Image ID"}, label="公共镜像Alibaba Cloud Linux 3.2104 LTS 64位", )
ⅲ. 设置ECS的实例类型
# 设置ECS实例类型 instance_type_param = core.RosParameter( self, "InstanceType", association_property="ALIYUN::ECS::Instance::InstanceType", association_property_metadata={"ZoneId": "ZoneId"}, default_value="ecs.g6.large", label="Instance Type", type=core.RosParameterType.STRING, description={}, )
ⅳ. 设置ECS的区域
# 地区id类型 zone_id_param = core.RosParameter( self, "ZoneId", type=core.RosParameterType.STRING, association_property="ALIYUN::ECS::Instance::ZoneId", default_value="cn-hangzhou-k", )
ⅴ. 创建ECS实例,按照文档的要求在ECS上执行命令
# 创建ECS实例 ecs_instance = ecs.Instance( self, "ECS-demo", ecs.InstanceProps( instance_name="ecs-jinzhan-agent-ros-python-cdk", vpc_id=vpc.resource.ref, v_switch_id=vswitch.resource.ref, security_group_id=sg.resource.ref, image_id=image_id_param, instance_type=instance_type_param, instance_charge_type="PayAsYouGo", password=ecs_password_param, allocate_public_ip=True, user_data=core.FnReplace( value=[ {"ros-notify": wait_condition_handle_param.attr_curl_cli}, { "Fn::Join": [ "", [ "#!/bin/bash\n", "dnf install -y vsftpd\n", "systemctl enable vsftpd.service\n", "systemctl start vsftpd.service\n", "netstat -antup | grep ftp\n", "adduser ftptest\n", # 将ftp用户密码设置为abc123 "sudo chpasswd <<< \"ftptest:abc123\"\n", "mkdir /var/ftp/test\n", "touch /var/ftp/test/testfile.txt\n", "chown -R ftptest:ftptest /var/ftp/test\n", "sudo echo '#!/bin/bash' | sudo tee update_vsftpd_conf.sh\n", '''sudo echo 'if [ -f "/etc/vsftpd/vsftpd.conf" ]; then \n sed -i "s/^anonymous_enable=.*/anonymous_enable=NO/" /etc/vsftpd/vsftpd.conf \n sed -i "s/^local_enable=.*/local_enable=YES/" /etc/vsftpd/vsftpd.conf\n sed -i "s/^listen=.*/listen=YES/" /etc/vsftpd/vsftpd.conf\n echo "修改成功!"\nelse\n echo "vsftpd.conf文件不存在。"\nfi' | sudo tee -a update_vsftpd_conf.sh''' "\n", "sudo chmod +x update_vsftpd_conf.sh\n", "sudo ./update_vsftpd_conf.sh\n", # 在行首添加#注释掉listen_ipv6=YES,关闭监听IPv6 sockets "sudo sed -i 's/^listen_ipv6=YES/#&/' /etc/vsftpd/vsftpd.conf\n", "sudo tee -a /etc/vsftpd/vsftpd.conf << EOF\n", "#设置本地用户登录后所在目录。\n" "local_root=/var/ftp/test\n", "#全部用户被限制在主目录。\n", "chroot_local_user=YES\n", "#启用例外用户名单。\n", "chroot_list_enable=YES\n", "#指定例外用户列表文件,列表中用户不被锁定在主目录。\n", "chroot_list_file=/etc/vsftpd/chroot_list\n", "#开启被动模式。\n", "pasv_enable=YES\n", "allow_writeable_chroot=YES\n", "#本教程中为Linux实例的公网IP。\n", "EOF\n", # 创建update_vsftpd_conf_ip.sh脚本,将获取本机ip并替换pasv_address=<本机ftp地址> "echo '#!/bin/bash' > update_vsftpd_conf_ip.sh\n", "echo '' >> update_vsftpd_conf_ip.sh\n", "echo 'public_ip=$(curl -s ifconfig.me)' >> update_vsftpd_conf_ip.sh\n" "echo '' >> update_vsftpd_conf_ip.sh\n", '''echo 'sudo sh -c "echo \"pasv_address=$public_ip\" >> /etc/vsftpd/vsftpd.conf"' >> update_vsftpd_conf_ip.sh\n''', "echo '' >> update_vsftpd_conf_ip.sh\n", "chmod +x update_vsftpd_conf_ip.sh\n", "./update_vsftpd_conf_ip.sh\n", "sudo tee -a /etc/vsftpd/vsftpd.conf << EOF\n", "#设置被动模式下,建立数据传输可使用的端口范围的最小值。\n", "#建议您把端口范围设置在一段比较高的范围内,例如50000~50010,有助于提高访问FTP服务器的安全性。\n", "pasv_min_port=50000\n", "#设置被动模式下,建立数据传输可使用的端口范围的最大值。\n", "pasv_max_port=50010\n", "EOF\n", "touch /etc/vsftpd/chroot_list\n", "systemctl restart vsftpd.service\n" ], ] }, ] ), ), )
d. 创建安全组
# 创建安全组 sg = ecs.SecurityGroup( self, "SecurityGroup", ecs.SecurityGroupProps( vpc_id=vpc.resource.ref, ), )
e. 添加安全组入方向端口
# 添加安全组入方向端口 sg_ingress_50000_50010 = ecs.SecurityGroupIngress( self, "SecurityGroupIngress_80", ecs.SecurityGroupIngressProps( security_group_id=sg.resource.ref, source_cidr_ip="0.0.0.0/0", ip_protocol="tcp", nic_type="intranet", port_range="50000/50010", ), ) sg_ingress_21 = ecs.SecurityGroupIngress( self, "SecurityGroupIngress_8080", ecs.SecurityGroupIngressProps( security_group_id=sg.resource.ref, source_cidr_ip="0.0.0.0/0", ip_protocol="tcp", nic_type="intranet", port_range="21/21", ), ) sg_ingress_22 = ecs.SecurityGroupIngress( self, "SecurityGroupIngress_22", ecs.SecurityGroupIngressProps( security_group_id=sg.resource.ref, source_cidr_ip="0.0.0.0/0", ip_protocol="tcp", nic_type="intranet", port_range="22/22", ), )
f. 使用云助手运行命令
# 安全组:入方向添加安全组规则并放行50000/50010、22、21端口 install_ftp = ecs.RunCommand( self, "InstallFTP", ecs.RunCommandProps( instance_ids=[ecs_instance.attr_instance_id], type="RunShellScript", sync=True, timeout=600, command_content="" ), ) # 如果开了安全组,才运行installftp install_ftp.add_dependency(sg_ingress_50000_50010) install_ftp.add_dependency(sg_ingress_22) install_ftp.add_dependency(sg_ingress_21)
g. 添加输出
# Output core.RosOutput(self, "FTP URL", value=core.FnJoin("", ["ftp://", ecs_instance.attr_public_ip, ":21"]), description="FTP User:ftptest, FTP Password:abc123, port:21, 50000/50010")
h. 更改app.py文件
修改app.py文件,导入你刚刚写好的文件,起个名字,这里起名叫「FTP-stack」
#!/usr/bin/env python3 *** import ros_cdk_core as core from python_cdk.ECS.deploy_FTP_on_ECS_460772 import DeployFTP app = core.App() DeployFTP(app, "FTP-stack") app.synth()
4.4 如何创建shell脚本来执行复杂命令
此文档中有一些复杂操作,无法使用简单的一两句命令来实现,这时候需要使用shell脚本来完成操作,例如这里需要使用vim来更改文件,在文件末尾添加参数并且注释某些行,显然这里已经无法使用简单的命令行来完成了。
这时候比较考验你的代码能力,如果你实在不清楚shell脚本怎么编写,这时候应该拿出我们的最强AI编码工具Chatgpt。使用Chatgpt来辅助我们编写代码。
此时GPT给出了一个实现此功能的脚本,但是此脚本需要你手动创建文件,在ROS中我们需要纯命令完成,那要如何解决?
现在gpt给出了纯命令行版本,你只需要将这段命令添加上引号,按照python中的代码规范填入user_data中,这段任务就完成了。其他比较复杂的操作类似
5.使用代码部署资源
5.1 使用ROS CDK部署资源
打开终端,执行如下命令,其中「FTP-stack」是你在app.py中起的名字
ros-cdk synth FTP-stack -j ros-cdk deploy FTP-stack --sync=true
看到如下输出说明第一行执行成功
看到如下输出说明第二行执行成功
5.2 检查资源部署情况
从控制台打开资源编排,查看事件。如果你的资源状态没有任何回滚,说明至少此次的资源部署是成功的
6. 验证任务是否完成
6.1进入ECS查询日志文件
连接你创建的ECS,执行如下命令,查看每一步命令是否都执行成功
less /var/log/cloud-init-output.log
比方说我此时看到的结果如下,认真查看发现没有报错,那可以暂时确定ECS上所有的命令都成功执行
6.2手动验证任务是否完成
此文档的最终目的是建立一个FTP服务器,那么需要我们手动验证服务器是否建立成功,文档原文如下
现在已经完成资源部署,ECS上的命令也已经按照文档全部执行完成。最后还需要手动验证一下FTP服务器是否建立成功,可以在自己的电脑上连接创立的FTP服务器,检查FTP服务是否正常运行
可以看到成功连接服务器,并且服务器上有文件,那么此次ROS文档测试完成。
6.3 当你确定你此次任务完成,请及时销毁资源
ros-cdk destroy FTP-stack --sync=true
6.4 将完成的代码更新推送到Github上
# 初始化git git init # 将本地仓库与远程仓库关联 git remote add origin https://github.com/XiaoTianJianJun/ROS_JINZ_DEV/tree/master # 创建名为jinz_dev的分支 git checkout -b jinz_dev # 添加本次修改的内容 git add . # 简要介绍一下本次修改的内容 git commit -m "Initial commit" # 将更改推送到代码仓库 git push origin jinz_dev
7.弹药库
- ROS CDK官方文档是一个很好的学习途径
- OpenAPI CDK:cdk的代码示例可以查看openapi对应产品的cdk页面,可以动态根据输入参数输出运行示例
- 模版示例
- Chatgpt
8.总结
核心内容:未接触过ROS资源部署的新人如何快速上手
- 全流程演示ROS资源编排:本文详细描述了从零开始完成一次ROS资源编排的代码编写的过程。它包含了可能遇到的大部分问题,并致力于让初学者也能够快速上手。
- ROS的优势:ROS资源编排能够提高云资源的管理效率和稳定性,减少人工操作和错误,同时提供了可视化的监控和安全可靠的保障。
- 写代码遇到困难如何解决:
- 善用搜索引擎,自行解决问题
- 使用AI工具,提升编码效率
- 遇到不懂的问题时,积极向他人请教