【问题标题】:TFS2010 Build Definition to Deploy to multiple servers?TFS2010 构建定义部署到多台服务器?
【发布时间】:2010-08-19 07:04:15
【问题描述】:

我一直在研究 TFS2010 使用 MSDeploy 的新构建和部署功能。到目前为止,一切进展顺利(尽管很难找到有关特定场景的信息)。

我可以修改我的构建定义以指定要部署到的 2 个或更多服务器吗?我需要做的是部署到多台服务器(因为我的测试环境中有两台使用 NLB)。

我现在拥有的是一个构建定义,它构建、运行我的测试,然后部署到我的一个测试服务器(其中运行 MsDeployAgentService)。它工作正常,并且每个 Web 项目都按照其项目文件中的配置进行部署。我使用的 MSBuild 参数是:

* /p:DeployOnBuild=True
* /p:DeployTarget=MsDeployPublish
* /p:MSDeployServiceURL=http://oawww.testserver1.com.au/MsDeployAgentService
* /p:CreatePackageOnPublish=True
* /p:MsDeployPublishMethod=RemoteAgent
* /p:AllowUntrustedCertificated=True
* /p:UserName=myusername
* /p:Password=mypassword 

注意:我不使用 /p:DeployIISAppPath="xyz" 因为它不会部署我的所有项目并覆盖我的项目配置。

我可以添加另一个构建参数来让它调用多个 MSDeployServiceURL 吗?类似于指定另一个服务器的第二个 /p:MSDeployServiceURL 参数?

或者我必须寻找其他解决方案,例如编辑 WF?

我在 2 个月前在这里看到了一个几乎完全相同的问题:TFS 2010 - Deploy to Multiple Servers After Build,所以看起来我不是唯一一个试图解决这个问题的人。

我还在讨论 MSDeploy 的 IIS.NET 论坛上发帖:http://forums.iis.net/t/1170741.aspx。它有相当多的观点,但同样没有答案。

【问题讨论】:

    标签: deployment msbuild msdeploy build-definition


    【解决方案1】:

    您不必构建项目两次即可部署到两台服务器。构建过程将构建一组部署文件。然后,您可以使用 InvokeProcess 部署到多个服务器。

    首先创建一个名为 ProjectName 的变量。然后将分配活动添加到“编译项目”序列。它位于“尝试编译项目”序列中。以下是 Assign 的属性:

    To: ProjectName
    Value: System.IO.Path.GetFileNameWithoutExtension(localProject)
    

    以下是部署到测试服务器的 InvokeProcess 活动的属性:

    Arguments: "/y /M:<server> /u:<domain>\<user> /p:<password>"
    FileName: String.Format("{0}\{1}.deploy.cmd", BuildDetail.DropLocation, ProjectName)
    
    You will need to change <server>, <domain>, <user>, and <password> to the values that reflect your environment.
    

    如果您需要手动部署到服务器,可以从您的构建文件夹运行以下命令:

    deploy.cmd /y /M:<server> /u:<domain>\<user> /p:<password>
    

    【讨论】:

    • 如果我有机会重做我的实现,这看起来是正确的做法。遗憾的是,它并没有像这样开箱即用。谢谢!
    • 我已经更新了我们的流程,所以这些现在已经参数化了。对于 MSBuild 参数,您还可以指定 IIS 应用程序路径。如果您需要将多个实例部署到同一台服务器,这也可以参数化。
    【解决方案2】:

    我找不到我正在寻找的解决方案,但这是我最终想出的解决方案。

    我希望在 TFS 参数中保持解决方案的简单性和可配置性,同时保持与已经提供的 MSBuildArguments 方法保持一致,该方法已经得到了很多推广。所以我创建了一个新的构建模板,并在 WorkFlow 的 Arguments 选项卡中添加了一个名为 MSBuildArguments2 的新 TFS WorkFlow Argument。

    我在 BuildTemplate WorkFlow 中搜索了所有出现的 MSBuildArguments(出现了两次)。

    使用MSBuildArguments 的两个任务称为Run MSBuild for Project。在此任务的正下方,我添加了一个带有条件的新“If”块:

    Not String.IsNullOrEmpty(MSBuildArguments2)
    

    然后我复制了“Run MSBuild for Project”任务并将其粘贴到新 If 的“Then”块中,相应地更新了其标题。您还需要更新新任务的 ConmmandLineArguments 属性以使用您的新参数。

    CommandLineArguments = String.Format("/p:SkipInvalidConfigurations=true {0}", MSBuildArguments2)

    经过这些修改后,工作流程如下所示:

    保存并签入新的工作流程。更新您的构建定义以使用这个新的工作流,然后在构建定义的 Process 选项卡中,您将找到一个名为 Misc 的新部分,其中包含可供使用的新参数。因为我只是使用这个新参数进行部署,所以我复制了与 MSBuild Arguments 完全相同的参数,并将 MSDeployServiceURL 更新到我的第二个部署服务器。

    就是这样。我想一个更优雅的方法是将 MSBuildArguments 转换为字符串数组,然后在 WorkFlow 过程中循环遍历它们。但这符合我们的 2 个服务器要求。

    希望这会有所帮助!

    【讨论】:

      【解决方案3】:

      我对此的解决方案是在 Package 之后运行一个新的 Target。每个需要生成包的项目都包含这个目标文件,我选择让 Include 以外部设置的“DoDeployment”属性为条件。此外,每个项目都定义了 DeploymentServerGroup 属性,以便根据项目类型正确过滤目标服务器。

      正如您在底部看到的,我只是在执行带有服务器列表的命令文件,非常简单。

      <!-- 
      This targets file allows a project to deploy its package  
      
      As it is used by all project typesconditionally included from the project file 
      

      -->

      <UsingTask TaskName="Microsoft.TeamFoundation.Build.Tasks.BuildStep" AssemblyFile="$(TeamBuildRefPath)\Microsoft.TeamFoundation.Build.ProcessComponents.dll" />
      
      <!-- Each Server needs the Group metadatum, either Webservers, Appservers, or Batch. -->
      <Choose>
          <When Condition="'$(Configuration)' == 'DEV'">
              <ItemGroup>
                  <Servers Include="DevWebServer">
                      <Group>Webservers</Group>
                  </Servers>
                  <Servers Include="DevAppServer">
                      <Group>Appservers</Group>
                  </Servers>
              </ItemGroup>
          </When>
          <When Condition="'$(Configuration)' == 'QA'">
              <ItemGroup>
                  <Servers Include="QAWebServer1">
                      <Group>Webservers</Group>
                  </Servers>
                  <Servers Include="QAWebServer2">
                      <Group>Webservers</Group>
                  </Servers>
                  <Servers Include="QAAppServer1">
                      <Group>Appservers</Group>
                  </Servers>
                  <Servers Include="QAAppServer2">
                      <Group>Appservers</Group>
                  </Servers>
              </ItemGroup>
          </When>
      </Choose>
      
      <!-- DoDeploy can be set in the build defintion -->
      <Target Name="StartDeployment" AfterTargets="Package">
      
          <PropertyGroup>
              <!-- The _PublishedWebsites area -->
              <PackageLocation>$(WebProjectOutputDir)_Package</PackageLocation>
      
              <!-- Override for local testing -->
              <PackageLocation Condition="$(WebProjectOutputDirInsideProject)">$(IntermediateOutputPath)Package\</PackageLocation>
      
          </PropertyGroup>
      
          <Message Text="Tier servers are @(Servers)" />
      
          <!-- A filtered list of the servers.  DeploymentServerGroup is defined in each project that does deployment -->
          <ItemGroup>
              <DestinationServers Include="@(Servers)" Condition="'%(Servers.Group)' == '$(DeploymentServerGroup)'" />
          </ItemGroup>
      
          <Message Text="Dest servers are @(DestinationServers)" />
      
      </Target>
      
      <!-- Only perform the deployment if any servers fit the filters -->
      <Target Name="PerformDeployment" AfterTargets="StartDeployment" Condition="'@(DestinationServers)' != ''">
      
          <Message Text="Deploying $(AssemblyName) to @(DestinationServers)" />
      
          <!-- Fancy build steps so that they better appear in the build explorer -->
          <BuildStep
                          TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
                          BuildUri="$(BuildUri)"
                          Message="Deploying $(AssemblyName) to @(DestinationServers)...">
              <Output TaskParameter="Id" PropertyName="StepId" />
          </BuildStep>
      
          <!-- The deployment command will be run for each item in the DestinationServers collection.  -->
          <Exec Command="$(AssemblyName).deploy.cmd /Y /M:%(DestinationServers.Identity)" WorkingDirectory="$(PackageLocation)" />
      
          <BuildStep
                          TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
                          BuildUri="$(BuildUri)"
                          Id="$(StepId)"
                          Status="Succeeded"
                          Message="Deployed $(AssemblyName) to @(DestinationServers)"/>
          <OnError ExecuteTargets="MarkDeployStepAsFailed" />
      </Target>
      
      <Target Name="MarkDeployStepAsFailed">
          <BuildStep
                  TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
                  BuildUri="$(BuildUri)"
                  Id="$(StepId)"
                  Status="Failed" />
      </Target>
      

      【讨论】:

      • 有趣...所以您将工作从 TFS 工作流中取出并将其放回 MSBUILD 脚本中。您的解决方案中的每个项目是否在构建后都调用此目标?在这里,我认为 MSBUILD 已经死了,被埋葬了 :)
      • 当我在编写 MSBuild 文件方面变得更好后,我开始更喜欢使用它们而不是使用笨重且容易崩溃的 WorkFlow 设计器。每个需要部署其包的项目都包含其 csproj 中的此目标文件,并且目标调用是自动的,因为它在 Package 目标之后完成。
      【解决方案4】:

      我是另一篇类似帖子的作者。我还没有找到解决方案。我相信它将修改工作流程以添加后处理 MSBUILD -sync 任务。这似乎是最优雅的,但仍然希望找到一些不那么打扰的东西。

      【讨论】:

      【解决方案5】:

      我不确定这是否可以帮助您使用 TFS 2010,但我有一篇有关 TFS 2012 的博客文章:Multiple web projects deployment from TFS 2012 to NLB enabled environment

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多