目标

实现当 Pull Request 合并到仓库主分支之后(或者手动创建 Release 之后),某机器能够自动获取到仓库中某目录下的文件,并同步到机器的特定目录。

要求

  • 可靠性:每次 Release 成功,都要确保 VM 中同步了最新文件
  • 一致性:VM 中的文件应该和仓库主分支一致
  • 安全性:VM 尽量不要向公网暴露端口,尤其不能暴露 SSH 等关键端口

本方案的思路

image-20220113142058302

当用户触发 Pipeline 后,Azure Pipeline Agent 将仓库中某目录的文件同步到云端 Storage。由于云端 Storage 挂载到虚拟机且自动同步,因此最新的文件得以同步到虚拟机。

优点:

  1. 实现简单,不需要维护专门程序
  2. VM 不暴露 22 等关键端口
  3. 利用 AzCopy 同步,保证了一致性

其它对比方案

如果不感兴趣可以跳过本节。

将 VM 作为 Pipeline Agent / Environment

image-20220113112357070

把 VM 变成 agent,从而直接接管 pipeline 流程。

  1. 优点:
    1. 实现简单
    2. VM 不暴露 22 等关键端口
  2. 缺点:
    1. 违反 Pipeline 设计原则(Agent 不应该是有状态的)。可能会有未预料的后果
    2. 据说微软将会停止对自管理 Agent 池的支持。此方法彼时将失效

方法一:通过 Pipeline Environment。结果报错:微软封锁自托管的代理池。

方法二:通过 Agent Pool。

通过 SSH (SCP) 同步文件

image-20220113113009239

  1. 开放 SSH 端口的 IP 限制,或将 Azure Devops 机器的 IP 段加入 SSH 白名单。此后,利用 SSH 登录后上传文件。
    1. 优点:实现简单。
    2. 缺点:
      1. 会开放 22 端口
      2. 如果要实现一致同步,则脚本逻辑将会比较复杂

通过 Storage 暂存文件,然后 SSH 控制 VM 下载

image-20220113113135639

该方案和前一种基本一样,只不过增加 Storage 作为暂存。

通过自定义的程序进行部署

自己编写一个服务程序,跑在 VM 上,让 Azure Pipeline Agent 与之通信,二者协调进行部署。

  1. 优点:可控性强
  2. 缺点:
    1. 仍然需要暴露一个 TCP 端口做监听
    2. 需要人工开发、维护该程序

image-20220113113219435

使用边界代理避免 SSH 直接交互

image-20220113113336953

使用专门的边界代理机器,Agent 与之它通信,它与实际被部署的机器通信,进行部署。

  1. 优点:不对外开放 22 端口
  2. 缺点:
    1. 实现和维护都比较复杂
    2. 更高的硬件成本
    3. 如果要实现一致同步,则脚本逻辑将会比较复杂

轮询拉取仓库

image-20220113113453983

  1. 优点:不对外开放 22 端口
  2. 缺点:
    1. 实现一个可靠的轮询服务并不容易
    2. 轮询产生大量无用请求

本方案的实施

创建 File share

在 Azure 创建一个 Storage Account,然后创建一个 File share:

image-20220113143305506

记下 share 的名称。并记下 Access Key.

image-20220113143542321

VM 挂载 Fileshare

在 VM 上,根据上面的信息填写下列脚本中的变量,保存为 sh 文件,增加执行权限,执行后完成挂载。

mountPath=/path/to/mount
storageAccount=accountname
accessKey=Your Key
fileShareName=demo-share

sudo mkdir $mountPath -p
if [ ! -d "/etc/smbcredentials" ]; then
sudo mkdir /etc/smbcredentials
fi
if [ ! -f "/etc/smbcredentials/$storageAccount.cred" ]; then
    sudo bash -c 'echo "username='$storageAccount'" >> /etc/smbcredentials/'$storageAccount'.cred'
    sudo bash -c 'echo "password='$accessKey'" >> /etc/smbcredentials/'$storageAccount'.cred'
fi
sudo chmod 600 /etc/smbcredentials/$storageAccount.cred

sudo bash -c 'echo "//'$storageAccount'.file.core.windows.net/'$fileShareName' '$mountPath' cifs nofail,vers=3.0,credentials=/etc/smbcredentials/'$storageAccount'.cred,dir_mode=0777,file_mode=0777,serverino" >> /etc/fstab'
sudo mount -t cifs //$storageAccount.file.core.windows.net/$fileShareName $mountPath -o vers=3.0,credentials=/etc/smbcredentials/$storageAccount.cred,dir_mode=0777,file_mode=0777,serverino

可以通过 Azure 网页向 Fileshare 上传一些文件,看是否同步到了 VM 中。

创建 SAS Token

SAS Token 提供给 Pipeline Agent 使用。

image-20220113135917122

将得到的 Token(以 ? 开头) 记下来。

创建 Devops Pipeline

我们以 Release Pipeline 为例。首先设置仓库源。添加 Artifact,设置 Source、分支等信息:

image-20220113145145671

注意:此处的别名,本例为 _BINGVIZ 后续会用到。

创建一个 Stage:

image-20220113145338762

往其中添加 Agent job(必须是 Windows 系统 Agent),添加 PowerShell Task:

image-20220113145441933

写入脚本:

azcopy sync "$(System.ArtifactsDirectory)\\$(SourceDir)" "$(StorageUrl)$(SASToken)" --recursive=true --delete-destination=true

image-20220113145537641

保存之后,设置变量:

image-20220113145817446

  • SASToken:之前在 Azure 生成的 Token.

  • SourceDir:分为两部分。路径分隔符必须为 \,否则会报错。

    • 一部分是 Clone 下来的仓库的别名,可以从 Artifacts 看到。
    • 另一部分是仓库内目录的相对路径。取决于你想同步哪个目录。
  • StorageUrl:Fileshare 的 Url,可以从 Fireshare 属性看到。

    image-20220113150650757

保存之后,即可创建 Release:

image-20220113150044935

参考

Mount SMB Azure file share on Linux | Microsoft Docs

Transfer data to or from Azure Files by using AzCopy v10 | Microsoft Docs

附注

如何取消挂载?

sudo umount $mntPath