yuzd/AntDeployAgent

【demo演示】一键发布WindowsService项目(netcore普通项目)

yuzd opened this issue · 0 comments

yuzd commented

前提:由于是演示WindowsService项目所以得部署好agent

参考:#1
注意:Windows 服务器上 需要安装一个agent 就是 安装一个windows服务,用来处理iis的发布和windows服务的发布,可以点击上面的参考链接进行安装。
注意: 得用管理员权限部署agent 防止会出现部署到Windows 服务上权限不足问题

打开vs 选择创建一个新的netcore 控制台项目(群文件有demo工程可以下载)

image

Nuget添加如下引用

  1. Microsoft.Extensions.Hosting
  2. System.ServiceProcess.ServiceController

新建一个ServiceBaseLifetime.cs 并将下面的内容复制进去

using Microsoft.Extensions.Hosting;
using System;
using System.ServiceProcess;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp4
{

    public class ServiceBaseLifetime : ServiceBase, IHostLifetime
    {
        private readonly TaskCompletionSource<object> _delayStart = new TaskCompletionSource<object>();

        public ServiceBaseLifetime(IApplicationLifetime applicationLifetime)
        {
            ApplicationLifetime = applicationLifetime ?? throw new ArgumentNullException(nameof(applicationLifetime));
        }

        private IApplicationLifetime ApplicationLifetime { get; }

        public Task WaitForStartAsync(CancellationToken cancellationToken)
        {
            cancellationToken.Register(() => _delayStart.TrySetCanceled());
            ApplicationLifetime.ApplicationStopping.Register(Stop);

            new Thread(Run).Start(); // Otherwise this would block and prevent IHost.StartAsync from finishing.
            return _delayStart.Task;
        }

        private void Run()
        {
            try
            {
                Run(this); // This blocks until the service is stopped.
                _delayStart.TrySetException(new InvalidOperationException("Stopped without starting"));
            }
            catch (Exception ex)
            {
                _delayStart.TrySetException(ex);
            }
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            Stop();
            return Task.CompletedTask;
        }

        // Called by base.Run when the service is ready to start.
        protected override void OnStart(string[] args)
        {
            _delayStart.TrySetResult(null);
            base.OnStart(args);
        }

        // Called by base.Stop. This may be called multiple times by service Stop, ApplicationStopping, and StopAsync.
        // That's OK because StopApplication uses a CancellationTokenSource and prevents any recursion.
        protected override void OnStop()
        {
            ApplicationLifetime.StopApplication();
            base.OnStop();
        }
    }
}

新建一个ServiceBaseLifetimeHostExtensions.cs 并将下面的内容复制进去

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp4
{
    public static class ServiceBaseLifetimeHostExtensions
    {
        public static IHostBuilder UseServiceBaseLifetime(this IHostBuilder hostBuilder)
        {
            return hostBuilder.ConfigureServices((hostContext, services) => services.AddSingleton<IHostLifetime, ServiceBaseLifetime>());
        }

        public static void RunAsService(this IHostBuilder hostBuilder)
        {
            hostBuilder.UseServiceBaseLifetime().Build().Run();
        }

        public static Task RunAsServiceAsync(this IHostBuilder hostBuilder)
        {
            return hostBuilder.UseServiceBaseLifetime().Build().RunAsync(CancellationToken.None);
        }
    }

}

新建一个服务类 TestService.cs 并写入以下内容(该服务就是每1秒往d:\log.txt写入当前时间)

using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;

namespace ConsoleApp4
{
   
    public class TestService: IHostedService,IDisposable
    {
         readonly System.Timers.Timer tmBak = new System.Timers.Timer();

        public TestService()
        {
            tmBak.Interval = 1000;//1秒执行1次
            tmBak.AutoReset = true;//执行1次false,一直执行true
            tmBak.Enabled = true;
            tmBak.Elapsed += (sender, eventArgs) =>
            {
                using (StreamWriter sw = new StreamWriter("D:\\log.txt",true))
                {
                    sw.WriteLine($"AntDeploy Windows服务:{DateTime.Now:yyyy-MM-dd HH:mm:ss}");
                }
            };
        }


        public Task StartAsync(CancellationToken cancellationToken)
        {
            tmBak.Start();
            return Task.CompletedTask;
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            tmBak.Stop();
            return Task.CompletedTask;
        }

        public void Dispose()
        {
            this.tmBak.Dispose();
        }
    }
}

编辑 Program.cs 写入如下内容:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace ConsoleApp4
{
    class Program
    {
        // P/Invoke declarations for Windows.
        [DllImport("kernel32.dll")] static extern IntPtr GetConsoleWindow();
        [DllImport("user32.dll")] static extern bool IsWindowVisible(IntPtr hWnd);
        public static bool HaveVisibleConsole()
        {
            return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
                          IsWindowVisible(GetConsoleWindow())
                          :
                          Console.WindowHeight > 0;
        }

        private static async Task Main(string[] args)
        {
            var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
            var pathToContentRoot = Path.GetDirectoryName(pathToExe);
            Directory.SetCurrentDirectory(pathToContentRoot);

            var isService = !(Debugger.IsAttached || args.Contains("--console"));

            if (HaveVisibleConsole()) isService = false;
            var builder = new HostBuilder()
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddHostedService<TestService>();
                });

            if (isService)
            {
                await builder.RunAsServiceAsync();
            }
            else
            {
                await builder.RunConsoleAsync();
            }
        }
    }
}

在工程上点击右键 然后选择 AntDeploy

image

配置AntDeploy

添加一个环境 名字叫 测试 然后 在 测试环境里面添加 windows服务器 这里我做测试就添加就是我本机,注意Host里面是填写格式为:ip:端口号

image

注意:Token不是windows服务器的密码!!!是安装agent后,agent的配置文件里面配置的Token(你自己自定义配置的)
注意:Port不是你要发布的项目的端口号!!!是安装agent后,agent的配置文件里面配置的端口号(你自己自定义配置的)
点击【Connect Test】按钮进行确认agent可以成功链接,否则会发布失败
如果【Connect Fail】失败 请查看 #10

进入 WindowsService Tab界面

Sdk类型选择 netcore
ServiceName 填写上面我们设置的名称:[TestService]
image

点击 【Deploy】按钮进行发布
image

确认服务器无误 点击 【是】开始执行一键部署
如果发布出现错误会出现下图所示:
image

可以在Log里面查看失败原因是因为我部署agent没有用管路员权限 报权限不足失败
image

部署成功 如下图:

image

查看D盘下是否log.txt是否正常每隔1秒写入了当前时间
image

这里演示的是windows服务上没有这个service
所以自动创建了。
如果service已存在的情况 Deploy 就会全量覆盖 不会重新创建site的。
如果想要覆盖时排除指定文件 可以在 Setting Tab界面的IgnoreList里面增加(支持正则)