Recentemente tive a necessidade de manter um código de serviço Windows que também pudesse ser executado como um serviço no Linux.
Estudando algumas possibilidades, a maneira mais elegante que encontrei foi aproveitar o mesmo projeto dotnetcore para gera o serviço do Windows ou um CLI que permite a configuração como serviço no Linux.
Vou explicar em seguida, todos os passos que fiz para que isso funcionasse utilizando a biblioteca TopShelf e gerando uma compilação para cada plataforma.
Todo código apresentado abaixo está disponível no GitHub:
O que é a biblioteca TopShelf
TopShelf é uma biblioteca que visa facilitar e organizar o processo para gerar serviço Windows.
A partir da versão 4.1, está suportando o padrão netstandard2.0 que nos permite compilar o projeto para FullFramework e dotnetcore.
Atualmente ela trabalha da seguinte forma, você cria uma aplicação console utilizando a biblioteca TopShelf, ela irá entender automaticamente alguns comandos como “install” por exemplo, que efetua a instalação do serviço do Windows. Se você não informar nenhum parâmetro esperado pelo TopShelf, a aplicação irá executar como um console, e isso nos permite fazer o que esperamos.
Criando o código comum
O código comum será um projeto console em dotnetcore (estou usando a versão 2.1 no caso), onde vou adicionar a dependência para a biblioteca TopShelf.
A sequência inicial de comandos para criar o projeto será:
c:\code\simple-service-counter> dotnet new console
Em seguida adicionamos a referência ao TopShelf:
c:\code\simple-service-counter > dotnet add package Topshelf --version 4.1.0.177-develop
Em seguida vamos a um detalhe importante, vamos ajustar o projeto para dar suporte a mais de um Framework, possibilitando que nosso build compile para ambas as plataformas.
Vamos criar uma classe (ImplementacaoServico.cs) que será nosso código do serviço. Nosso serviço será um simples contador, mas você poderá implementar o que precisar.
Vamos agora configurar a classe inicial do projeto console para utilizar o TopShelf e chamar nossa lógica.
Perfeito, com esses poucos passos você pode executar seu projeto simplesmente para verificar se está funcionando como um console normal.
Execute o comando abaixo para ver seu serviço executado como um console:
c:\code\simple-service-counter > dotnet run --framework net461 (ou netcoreapp2.1)
Se tudo estiver correto, você deverá ter um resultado como este:
Tudo OK até aqui? Então vamos seguir para gerar e instalar o serviço no Windows e em seguida no Linux.
Publicando e instalando no Windows
Primeiramente vamos publicar nosso serviço apontando para o framework 4.6.1 que nos permite instalar o serviço no windows.
Execute o seguinte comando para gerar a publicação:
c:\code\simple-service-counter > dotnet publish simple-service-counter.csproj -c Release -f net461 -o dist\windows --self-contained
Agora vamos instala-lo no windows, para isso entre na pasta “dist\windows” dentro da pasta do projeto e digite:
c:\code\simple-service-counter\dist\windows > simple-service-counter.exe install
E você deverá ter a seguinte saída:
Se você abrir o gerenciador de serviços do Windows, poderá iniciar e parar o serviço.
Para remover o serviço, somente digite:
c:\code\simple-service-counter\dist\windows > simple-service-counter.exe uninstall
Fácil né.
Publicando e instalando no Linux
Para o Linux vamos fazer uma publicação utilizando dotnetcore 2.1 e publicar para Linux utilizando a opção self-contained (Que leva com a publicação todas as dependências, incluindo o framework evitando a necessidade de instalá-lo no Linux).
Para gerar a publicação, execute o comando:
c:\code\simple-service-counter > dotnet publish simple-service-counter.csproj -c Release -f netcoreapp2.1 -o dist\linux -r linux-x64 --self-contained
Agora vamos compactar a pasta “dist\linux” onde foi publicado o projeto e vamos levar esta pasta para um servidor (ou VM) com Linux (meu caso Lubuntu 18.10).
Note que o arquivo compactado fica grande ~30MB. E isso é porque está levando junto todas as dependências, incluindo o framework.
Copie o arquivo compactado para sua máquina Linux. ( Use o programa scp ou outro recurso qualquer para copiar).
Já no Linux, descompacte o arquivo com o comando
$ unzip <arquivo>.zip -d ~/meuservico
Em seguida precisamos dar permissão de execução para o programa executável, então entre na pasta onde estão os arquivos e altere a permissão para:
$ chmod 755 simple-service-counter
Podemos agora testar se nosso console funciona simplesmente executando ele no Shell:
$ ./simple-service-counter
Veja que a saída de console do programa funciona conforme esperado.
O que precisamos agora para torná-lo um serviço, que possa ser iniciado e parado nos padrões systemd é seguir os passos:
- Criar um arquivo de configuração em “/etc/systemd/system/servico-counter.service”, com o conteúdo:
[Unit]
Description=Servico contador
After=network.target
[Service]
User=root
Restart=on-failure
Type=exec
ExecStart=/<LOCAL>/meuservico/linux/simple-service-counter
[Install]
WantedBy=multi-user.target
- Iniciar o serviço executando:
$ sudo systemctl start servico-counter
- Verificar o status do serviço para verificar se está em execução, com o comando:
$ sudo systemctl status servico-counter
Esta checagem se estiver ok irá apresentar a seguinte saida:
- Podemos definir para que ele seja iniciado com o boot se desejar, utilizando o comando:
$ sudo systemctl enable servico-counter
- Podemos também pará-lo, com o comando:
$ sudo systemctl stop servico-counter
- Para remover o serviço, basta apagar o arquivo “/etc/systemd/system/servico-counter.service” e em seguida excluir a pasta da aplicação.
Simples também né!!!
Conclusão
Como vimos é muito simples criar uma estrutura que nos permita portar um serviço entre windows e Linux mantendo um único código fonte para ambos.
Espero que este artigo ajude e que eu possa gerar muitos outros em seguida.
Tem alguma dica, sugestão, crítica ou dúvida, pode me enviar.
Muito obrigado.
NOTAS IMPORTANTES
Um problema que identifiquei em todas as versões posteriores que utilizei do TopShelf é que algo nele não está mais permitindo a execução como console no Linux :-(.
Abri uma issue no projeto do TopShelf para verificarem este ponto, ou entendermos o porque de ter parado:
Topshelf#513
Top comments (0)