基于阿里云云效Flow的Python Web服务构建与部署
基本要求
- 理解并掌握Python虚拟环境的应用价值与管理操作
- 掌握Docker命令与Dockerfile构建逻辑,能够基于Dockerfile创建容器镜像
- 熟练掌握阿里云云服务器ECS、阿里云云效代码管理Codeup与流水线Flow,以及阿里云容器镜像服务ACR的相关配置与管理操作
- 理解并掌握Python项目的多种部署方式,初步体验流水线的应用模式
实验过程
范例代码结构
环境搭建
本地开发环境
- 在本地环境安装Git与Python3(略)
- 在本地环境安装Python第三方库
准备云服务器ECS(略)
在ECS安装Docker引擎(略)
在ECS安装Python3
(ecs)$ sudo apt install python3
(ecs)$ sudo apt install python3-venv


基于阿里云容器镜像服务ACR配置镜像仓库
访问容器镜像服务-阿里云,点击“管理控制台”进入实例列表,创建使用个人实例。具体使用的镜像仓库将在后续配置Flow流水线时按需创建。
基于阿里云云效代码管理Codeup创建代码库
- 在Codeup新建远程仓库
新建代码库exp_pyms_demo
,注意勾选“创建.gitignore”,并选择Python模板。
新建后,Codeup默认创建Master分支并提供推荐.gitignore
文件。

- 配置SSH访问Codeup远程仓库
根据如何配置SSH密钥及自定义SSH认证密钥的路径_云效(Apsara Devops)-阿里云帮助中心,分别在本地环境与ECS服务器创建SSH公私钥,并在Codeup对应页面添加公钥,保证本地环境与ECS服务器均可基于SSH访问Codeup远程仓库。
本地环境已有公钥可以直接使用。下面为ECS服务器添加公钥:
- 在ECS安装Git
apt-get install git
git config --global user.name "<用户名>"
git config --global user.email "<邮箱>"

获取系统Git版本(需大于1.9):
git --version

- 对于OpenSSH,ECS已经内置
- 生成SSH密钥
ssh-keygen -t ed25519 -C "<注释内容>"
[!NOTE]
如果没有添加注释的需求,
-C
及以后的内容可以不写。
选择生成路径,一般我们选择默认即可。默认路径如下:
# 以 ED25519 算法为例
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/user/.ssh/id_ed25519):
这里直接敲回车就可以。密钥默认生成路径:/home/user/.ssh/id_ed25519
,公钥与之对应为:/home/user/.ssh/id_ed25519.pub
。
设置密钥口令,口令默认为空。可以选择使用口令保护私钥文件。如果不想在每次使用 SSH 协议访问仓库时,都要输入用于保护私钥文件的口令,可以在创建密钥时,输入空口令。
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
保持空白,连续敲击两次回车就可以了。
[!WARNING]
密钥用于鉴权,请谨慎保管。公钥文件以
.pub
扩展名结尾,可以公开给其他人,而没有.pub
扩展名的私钥文件不要泄露给任何人!
- 拷贝公钥
xclip -sel clip < ~/.ssh/id_ed25519.pub
[!NOTE]
错误信息 “Error: Can’t open display: (null)” 表明
xclip
命令无法找到可用的 X11 显示环境。这通常发生在你通过 SSH 连接到服务器并且没有启用 X11 转发的情况下。由于你是以root
用户身份连接的,而且xclip
需要访问图形界面,所以它无法在没有 X11 转发的情况下工作。可以直接查看
/root/.ssh/id_ed25519.pub
文件的内容。cat /root/.ssh/id_ed25519.pub
执行这个命令后,文件的内容将被显示在终端中,可手动复制这些内容。
- 在Codeup上设置公钥
登录云效Codeup,在页面右上角选择个人设置->SSH公钥,添加生成的SSH公钥信息。
基于阿里云云服务器ECS直接部署范例服务
获取范例服务包并解压
- 将范例服务包上传ECS服务器
参考通过WinSCP上传本地文件到Linux系统的ECS或下载文件到本地-阿里云帮助中心,把范例文件上传到ECS。
下载并打开WinSCP,用root
用户名登录,即可拖拽传输文件了:
- 解压后获得
exp_pyws_demo
目录,以此作为工作目录进入。
(ecs)$ tar -xzvf exp_pyms_demo.tar.gz
(ecs)$ cd exp_pyws_demo
(ecs)(exp_pyws_demo)$ ls -F
(ecs)(exp_pyws_demo)$
在虚拟环境部署范例服务
创建Python虚拟环境venv
并安装requirements.txt
指定的相关依赖:
(ecs)(exp_pyws_demo)$ cat requirements.txt #查看所需依赖
(ecs)(exp_pyws_demo)$ python3 -m venv exp_venv #完成后生成目录exp_venv
(ecs)(exp_pyws_demo)$ source exp_venv/bin/activate #激活Python虚拟环境
(ecs)(exp_pyws_demo)(exp_venv)$ pip3 install -r requirements.txt #安装所需依赖
启动Python Web服务:
(ecs)(exp_pyws_demo)(exp_venv)$ python3 server.py
使用浏览器访问<ECS IP>:8000/
,查看服务页面输出。
查看无误后,键入Ctrl + C
停止服务:
随后退出虚拟环境venv
,删除虚拟目录。
(ecs)(exp_pyws_demo)(exp_venv)$ deactivate
(ecs)(exp_pyws_demo)$ rm -r exp_venv
以容器形式部署范例服务
在exp_pyws_demo
目录下创建Dockerfile
文件,将Python Web服务范例代码打包进容器镜像。
# 基于Python最新基础镜像
FROM python:3.10-slim-buster
LABEL maintainer="<邮箱>"
# 工作目录
WORKDIR /app
# 安装Python库
COPY requirements.txt requirements.txt
RUN apt-get update && apt-get install -y \
&& pip3 install -r requirements.txt
# 清理apt软件包缓存
RUN rm -rf /var/lib/apt/lists/*
# 复制当前文件夹全部文件到镜像
COPY . .
# 暴露服务端口8000
EXPOSE 8000
# 容器启动时执行命令
CMD [ "python3", "server.py"]
使用docker build
命令构建镜像,完成后通过docker images
可查询。
(ecs)(exp_pyws_demo)$ docker build -t exp_pyms_demo .
(ecs)(exp_pyws_demo)$ docker images
基于镜像exp_pyms_demo
启动容器:
(ecs)$ docker run -d --restart=always --name exp_pyms_demo -p 8000:8000 exp_pyms_demo
浏览器访问http://<ECS IP>:8000/
,查看服务页面输出。此外也可在ECS中使用curl
命令访问ECS本地服务URL,观察输出已确认服务部署是否生效。
(ecs)$ curl http://127.0.0.1:8000
测试确认部署无误,关闭容器,删除容器与镜像。
(ecs)$ docker stop exp_pyms_demo #停止容器
(ecs)$ docker rm exp_pyms_demo #删除容器
(ecs)$ docker rmi exp_pyms_demo #删除镜像
基于阿里云云效Flow在虚拟环境部署范例服务
配置Git仓库,创建分支venv
将范例服务包下载到本地环境后,解压进入目录exp_pyws_demo
,将该目录作为Git工作目录。此时查看目录,内容如下:
(local)$ ls -a -p
完成本地仓库初始化之后关联远程仓库。
(local)$ git init
(local)$ git remote add origin git@codeup.aliyun.com:6705174d49e9309ce56b9e53/exp_pyms_demo.git
查看当前所在分支与工作目录文件状态。每次开工前,首先从远程仓库git pull
。
(local)$ git pull origin master
(local)$ ls -a -F
# Codeup远程仓库创建时生成了.gitignore,此时通过git pull已经下载到本地
通过git status
观察工作目录文件状态,随后全部纳入跟踪且暂存,再提交。
(local)$ git status
(local)$ git add .
# 将本地目录全部文件纳入跟踪
(local)$ git commit -m "init"
# 进行本地初始化提交
(local)$ git push -u origin master
# 推送至远程仓库master分支
(local)$ git checkout -b venv
# 创建venv分支并切换
(local)$ git push --set-upstream origin venv
# 创建远程仓库,与本地仓库关联
配置云效Flow以支持ECS虚拟环境部署
- 根据模板创建流水线
在云效流水线Flow系统新建流水线,选择云效预置模板“Python·测试、构建、部署到阿里云ECS/自有主机”,将测试、构建、部署三个阶段命名分别改为代码测试、制品构建、服务部署。
- 配置阶段“流水线源”
点击“添加流水线源”,在“代码源”页面中:
- “选择代码源”选择
Codeup
- “代码仓库”选择
exp_pyms_demo
- “默认分支”选择
venv
- “触发事件”选择
代码提交
- “工作目录”设为
exp_pyms_demo
- 配置阶段“代码测试”
本阶段模板设置有“Python单元测试”与“Python代码扫描”两个任务。我们暂不考虑单元测试,删除“Python单元测试”任务。而“Python代码扫描”任务保持模板默认配置即可。
- 配置阶段“制品构建”
本阶段模板设置有一个任务“Python构建上传到仓库”,包含“Python构建”与“构建物上传”两步骤。我们无需对Python脚本进行特殊构建,只需使用默认设置将代码制品打包上传至阿里云提供的“云效公共存储空间”即可。
- 配置阶段“服务部署”
本阶段模板有一个任务“主机部署”,其中:
“制品”选择上一步“构建物上传”步骤设定的制品名称
Artifacts_${PIPELINE_ID}
“主机组”
首次使用时,需要点击“新建主机组”,弹出界面中选择“新建主机组”下方的“新建主机组-阿里云ECS标签”。
“服务连接”点击“添加服务连接”创建新的服务连接,进行部署授权。
如果没有服务连接,需要新建,并提供服务授权:
“地域”选择自己名下的ECS服务器所在地域。
“ECS标签键”,“ECS标签值”依次选择即可。
[!NOTE]
需要先设置ECS服务器的标签:
- “部署配置”
“下载路径”保持默认。
“执行用户”保持默认。
“部署脚本”定义如下:
# 清理上一次部署时的运行环境
pkill python3
rm -rf ~/exp_pyms_demo
# 解压代码到指定运行目录
mkdir ~/exp_pyms_demo
tar -xvzf /home/admin/app/package.tgz -C ~/exp_pyms_demo
# 此处路径必须与“部署配置->下载路径”一致
cd ~/exp_pyms_demo
# 建立Python虚拟环境并安装依赖
python3 -m venv exp_venv
source exp_venv/bin/activate
pip3 install -r requirements.txt
# 运行Web服务
nohup python3 server.py > /dev/null 2>&1 &
# nohup:no hang up,程序输出重定向到nohup.out,终端退出不会影响运行
# > /dev/null:将server.py的日志输出重定向到/dev/null而非标注输出stdout
# 2>&1:2为stderr,>&为重定向,将标准错误出重定向到标准输出
# &:让程序在后台执行,不会被CTRL+C触发的SIGINT中断,终端退出不会影响运行
运行流水线
点击“保存并运行”按钮,弹出对话框中点击“运行”按钮,流水线即被触发,逐一执行阶段任务直至最终完成服务部署。
此时阿里云ECS已经基于容器启动了Python Web服务,通过curl
命令可测试服务接口是否可访问。
(ecs)$ curl http://127.0.0.1:8000
也可选择某台能访问ECS IP的终端设备,使用浏览器访问<ECS IP>:8000/
,可查看服务页面输出。
修改设备文件,提交代码并触发流水线
在本地环境修改devices.csv
,添加一条设备信息,保存退出。
(local)$ vim devices.csv
7097988787878778888,device4,client,PC,00003,10,2023-08-01 00:00:00,WiFi,00:00:00:00:00:15,156.123.2.13,alive
直接提交并推送远程仓库并触发流水线执行。
(local)$ git commit -a -m "docs:Add a device info in devices.csv"
(local)$ git push origin venv
可见流水线已经自动执行一次:
流水线自动执行完毕后,使用浏览器重新访问该服务,可发现设备信息表多出一行,更新成功。
完成测试后关闭范例服务
(ecs)$ ps aux|grep server.py
(ecs)$ kill 4981 #<PID>
(ecs)$ ps aux|grep server.py
基于阿里云云效Flow以容器形式部署范例服务
配置Git仓库,创建分支docker
在本地环境从当前venv
分支中创建检出docker
分支。
(local)$ git checkout -b docker
# 基于分支venv检出分支docker
(local)$ git branch
准备Dockerfile文件,用于在流水线中制作承载范例服务的Docker镜像。
# 基于Python最新基础镜像
FROM python:3.10-slim-buster
LABEL maintainer="<邮箱>"
# 工作目录
WORKDIR /app
# 安装Python库
COPY requirements.txt requirements.txt
RUN apt-get update && apt-get install -y \
&& pip3 install -r requirements.txt
# 清理apt软件包缓存
RUN rm -rf /var/lib/apt/lists/*
# 复制当前文件夹全部文件到镜像
COPY . .
# 暴露服务端口8000
EXPOSE 8000
# 容器启动时执行命令
CMD [ "python3", "server.py"]
添加
(local)$ git add Dockerfile
(local)$ git commit -m "docs:Add Dockerfile"
(local)$ git push --set-upstream origin docker
# 推送远程仓库分支origin/docker,没有则新建
(local)$ la -F
配置云效Flow以支持ECS容器镜像部署
基于之前配置的云效Flow流水线进行修改,以支持ECS容器镜像模式部署范例服务。
在云效控制台页面选中之前配置的流水线,点击“编辑”按钮,进入流水线配置页面。其中“代码测试”阶段保持不变,其他三个阶段均需修改。
- 配置阶段“流水线源”
“默认分支”选择docker
- 配置阶段“镜像构建”
将原“制品构建”阶段更名为“镜像构建”,首先新建“并行任务”用于构建镜像,随后再删除原有任务“Python构建上传到仓库”(否则若该阶段无任务,自身也会被自动删除)。
新建“并行任务”时,在弹出页面中选择“镜像构建”,再选择“Python镜像构建”。
随后配置任务“Python镜像构建”,在步骤“镜像构建并推送至ACR(个人版)”中:
- 点击“添加服务连接”,创建新的服务连接,“使用范围”设为私密。
- 选择“地域”。
- 在ACR控制台新建仓库
exp_pyms_demo
,并绑定到此处。 - “标签”:
${DATETIME}
- “Dockerfile路径”:默认为
Dockerfile
,即从仓库目录直接查找。
- 配置阶段“服务部署”
首先新建“并行任务”用于容器部署,再删除任务“主机部署”。
新建“并行任务”时,在弹出页面中选择“部署”,再选择“Docker部署”。
在具体配置“Docker部署”任务之前,先点击页面顶部的“变量和缓存”,新建字符变量ACR_LOGIN_PWD
,默认值为登录阿里云容器镜像服务ACR时所需密码,必须采用“私密模式”保存。该变量将用于后续任务配置中的“部署脚本”。
回到“流程配置”页面,在“服务部署”阶段配置“Docker部署”任务:
“主机组”选择之前所建主机组。
“执行用户”保持默认。
“部署脚本”定义如下,使用时请更换阿里云账号username
以及地域region
。
docker login --username=<username> --password=${ACR_LOGIN_PWD} crpi-exr3j7gox2plovb8.cn-heyuan.personal.cr.aliyuncs.com
docker rm -f exp_pyms_demo
docker pull ${CONTAINER_TAG}
docker run -d --restart=always --name exp_pyms_demo -p 8000:8000 ${CONTAINER_TAG}
点击“添加”新增变量,选择“上游任务 制品/镜像 下载地址”,新增变量“CONTAINER_TAG
”,变量值选择镜像公网地址。
运行流水线
点击“保存并运行”按钮,弹出对话框点击“运行”按钮,流水线即被触发,逐一执行阶段任务直至最终完成服务部署。
由于始终报错,决定先将基础镜像拉取至ACR镜像仓库“:
(ecs)$ docker login --username=沙里院的雪 crpi-exr3j7gox2plovb8.cn-heyuan.personal.cr.aliyuncs.com
(ecs)$ docker pull python:3.10-slim-buster
(ecs)$ docker tag python:3.10-slim-buster crpi-exr3j7gox2plovb8.cn-heyuan.personal.cr.aliyuncs.com/whim111/exp_pyms_demo:1.0
(ecs)$ docker push crpi-exr3j7gox2plovb8.cn-heyuan.personal.cr.aliyuncs.com/whim111/exp_pyms_demo:1.0
同时Dockerfile的开头也要修改:
# 基于ACR中的Python基础镜像
FROM crpi-exr3j7gox2plovb8.cn-heyuan.personal.cr.aliyuncs.com/whim111/exp_pyms_demo:1.0
这次Python镜像构建成功。但是Docker部署有报错:
端口未释放的问题,重启ECS就解决了。
运行完成后,在阿里云ECS上执行docker images
与docker ps
命令查看到生成的镜像和容器。此时用浏览器重新访问该服务,可见之前一样的页面输出。
修改代码文件,提交代码并触发流水线
在本地环境根据“代码测试”阶段“Python代码扫描”任务上次执行所提示的信息,修订server.py存在的格式问题后提交、推送。
(local)$ git commit -a -m "style: Adjust format on server.py"
(local)$ git push origin docker
本次推送将再次自动触发流水线,可发现修订后的代码文件正常通过了代码扫描。