Particular / ServiceControl

Backend for ServiceInsight and ServicePulse
https://docs.particular.net/servicecontrol/
Other
51 stars 47 forks source link

Allow option to not automatically create a ServiceControl Instance Windows Service #1623

Closed fourpastmidnight closed 2 years ago

fourpastmidnight commented 5 years ago

Provide an option to "opt-out" of installing a ServiceControl instance as a Windows Service to make it more "container-friendly".

WojcikMike commented 5 years ago

Thank you very much for creating this issue @fourpastmidnight , I've passed it to our team taking care of enhancements and they will add it to theirs backlog. Given the nature of our work, we don't know when we will start working on that issue.

But when we do we will comment on that issue.

WilliamBZA commented 5 years ago

You can already use a dockerfile to deploy ServiceControl.

A dockerfile that looks something like so:

FROM microsoft/mssql-server-windows-developer

SHELL ["powershell"]

# Create ServiceControl SQL DB
RUN mkdir 'c:/sqldb'; \
    SqlCMD -E -q 'ALTER LOGIN [sa] WITH PASSWORD = ''asdf1234!'';'; \
    SqlCMD -E -q 'ALTER LOGIN [sa] ENABLE;'

# Install ServiceControl
RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; \
    Invoke-WebRequest "https://github.com/Particular/ServiceControl/releases/download/3.6.9/Particular.ServiceControl-3.6.9.exe" -OutFile "$env:TEMP\Particular.ServiceControl-3.6.9.exe" -UseBasicParsing; \
    & "$env:TEMP\Particular.ServiceControl-3.6.9.exe" /quiet /LV* c:/install.log

COPY startup.ps1 /

COPY ProgressTracker c:/

CMD .\startup -Verbose

And the corresponding startup.ps1 file which looks something like:

Import-Module 'C:/Program Files (x86)/Particular Software/ServiceControl Management/ServiceControlMgmt.psd1';
New-ServiceControlInstance -Name ServiceControl -InstallPath c:/servicecontrol -DBPath c:/temp/db -LogPath c:/temp/logs -DatabaseMaintenancePort 33334 -Port 33333 -ErrorQueue error -AuditQueue audit -ErrorLogQueue errorlog -AuditLogQueue auditlog -Transport SQLServer -ForwardAuditMessages:$false -ForwardErrorMessages:$false -ConnectionString '' -AuditRetentionPeriod 01:00:00 -HostName * -ErrorRetentionPeriod 10:00:00:00;

Obviously you will have to tweak the connection settings and locations yourself, but it is 100% doable.

WilliamBZA commented 5 years ago

You can also disable the windows service from the docker script or the PS file and then trigger the SC instance manually.

It should go without saying, but it is vitally important that the disk where the ravendb is configured is stable. Also, ServiceControl is not able to run multiple times against the same store, there is specific scaleout configuration that is required for multiple instances of ServiceControl.

fourpastmidnight commented 5 years ago

@WilliamBZA FTR, I was never questioning whether or not a ServiceControl instance could be run in a container because a Windows Service is created for it and runs as a Windows Service. I know it can run in a container. In fact, I used your Dockerfile at your repository which I found yesterday before posting this question to get me started, so thank you very much for your contribution. I'm iterating on what you have to provide a much more "generic" container which can configure the instance via passing environment variables when using docker run.

However, with exceptions such as Windows services which, well, have been services forever--such as IIS, MSMQ, etc., generally, you run console applications in Docker containers. Now, I know ServiceControl can be run as a console application. In fact, when running as a console application, it logs output to STDOUT, which is great for containerization!

The ask here, however, is that Particular Software does not assume that a ServiceControl instance should always have a Windows Service created for it. ServiceControl could default to having a Windows Service created for it; I think that makes perfect sense for backward compatibility. However, New-ServiceControlInstance should support a switch, -NoService, or -Standalone perhaps, which will instruct the SCMU to not create the Windows Service. With the way creation of an instance occurs, to best containerize ServiceControl you now force container image authors to do one of the following:

  1. Run a tracker exe
  2. Disable the service control service via sc config Particular.ServiceControl.1 start= disabled and/or
  3. Remove the service via sc delete Particular.ServiceControl.1
  4. Manually unzip the instance files to a directory of the author's choosing using Expand-Archive and set the configuration appropriately.

Number 1 above is completely unnecessary since ServiceControl is already a console application that outputs logging to STDOUT. So, why make container authors go through the hurdle. Numbers 2 and 3, while not difficult, could be completely avoided by not installing it as a service in the first place, which is easily decidable at instance creation time. And, it's one or two less docker RUN statements in the Dockerfile which also means less layers in the image. And actually, number 4 might be the easiest in lieu of any switch parameter to New-ServiceControlInstance which would do this for me.

And yes, it does go without saying that the disk must be highly-available and stable. And I'm very aware that an instance expects exclusive access to the database. I have a lot of experience with ServiceControl.

fourpastmidnight commented 5 years ago

Well, it turns out this is much more difficult than it needs to be. The options are only 1 and 4 from my previous comment. If you install an instance of service control via New-ServiceControlInstance, and then proceed to either remove or disable the Windows Service, you are unable to run ServiceControl.exe by invoking it at the command line. I have a feeling that the entry in the Windows Service Control Manager gets "tombstoned" (i.e. queued for removal), but isn't completely removed (probably there's still something holding a handle to some part of ServiceControl somewhere). Normally, you'd reboot the server and everything would be good. But, that's not how it works in Docker-land.

I don' think option 1 is a great option, even though "it works". It's just not necessary. So I'm left with manually unzipping the service control files and having to set up ServiceControl.exe.config myself (which, I have to do anyway since not all Service Control options are exposed via PowerShell cmdlets). It's not all that bad, but it's not really ideal either. Too bad, since ServiceControl, at its core, is nothing more than a glorified console application which should be so easy to configure to run inside a Docker container but is anything but given the available instance creation options.

[UPDATE} OK, I think I'm wrong about this. I'm running v1.48 locally on my machine, which I can run interactively by just executing ServiceControl.exe. Apparently, in later versions, this has changed (and I'm attempting to run 3.7.0 in my container). I found a suitable command line switch that will allow options 2 and 3.

fourpastmidnight commented 5 years ago

OK, I can run 3.7.0 stand-alone, but have to use an undocumented command line switch (at least, it's not documented on the page that tells you how to run SC from the command line as an unprivileged user). Also, does ServiceControl no longer log to STDOUT?? (My comment above where I said that it did was for the 1.48 version installed on my local machine). When I run v1.48 standalone, I get plenty of output. But when running 3.7.0, I see no output to STDOUT (save for Press CTRL+C to exit), but I can see that data is written to the log files.

Why did this change? Is there a way to tell SC to log to STDOUT (in addition to logging to a file)? Because now, for getting logs from the container console it's really difficult because you need to look at such-and-such a date and grab the log with the highest sequence number to get the most recent log. AND, if the log was just switched to, then you may not see much of anything in the log. This seems like a HUGE step backward for supporting containerization.

WilliamBZA commented 5 years ago

Can I ask you to send a mail through to support at particular.net? We're actively working on the containerization stuff for ServiceControl and ServicePulse and I'd love to get on a call with you to discuss some of the requirements before we head too far down a particular path.

fourpastmidnight commented 5 years ago

Just by way of update, last night (ok, early this morning, 2am) I finally managed to be able to docker run (lots of volume mappings) servicecontrol:3.7.0 and it's been running now for almost 9 hours. I also managed to use MSMQ as my transport. Just a caveat that I have nothing else "on the bus" except ServiceControl, so I don't know if inter-container communication between NServiceBus endpoints and a ServiceControl instance in a container would actually work. But that's my next step.

So far, the experience is leaving me with mixed emotions. I'm extremely happy that running SC in a container appears to be working. However, the most recent versions of Service Control have made running it in a container a lot harder than it needs to be, as I've already mentioned. I also have another issue open with respect to console logging (#1622). In SC 1.48, SC logs at the INFO level (I think) when run standalone. Now, it seems only ERROR level and above are logged to the console, which is far from container-friendly, and this cannot be configured. And the use of rolling log files and how SC does rolling log files makes "tailing" them via PowerShell non-trivial, rendering useless the docker container logs <container_name> command.

@WilliamBZA I would love to be able to provide any feedback I can to help you guys make this more container-friendly. I'll send an email to support referencing this issue. Thanks for reaching out! (It may be a few days before I send that email--I'm reaching to some people at my organization to see if they have any additional input about the work I've done and how that may affect how we would create container images of and deploy ServiceControl via containers.)

fourpastmidnight commented 5 years ago

Also, it appears that ServiceControl may not be picking up the license file by way of the app.config setting NSerivceBus/LicensePath? I specified the option, and mapped a volume to the container which contains the license file (with the correct filename), but hitting the SC API, the information returned says I'm running a "trial" instance instead of showing the license file information.

BTW, I expect to reach out to support early next week. Thanks again for your interest.

WilliamBZA commented 5 years ago

BTW, I expect to reach out to support early next week. Thanks again for your interest.

Please do, the license location thing is also of interest to me so I will have plenty of information to learn from you.

fourpastmidnight commented 5 years ago

See #1624 for more information on the licensing issue. I think I'm good, but the API shows incorrect information.

fourpastmidnight commented 5 years ago

@WilliamBZA I didn't know where to post this, so I more or less chose this particular issue at random.

But today, I successfully started up an NSB Endpoint, a ServiceControl instance, and a SQL Server Database, each in their own container, with the Endpoint and ServiceControl using MSMQ as their transport. I then opened ServiceInsight and put in the URL for the containerized ServiceControl instance and I saw my endpoint in the Endpoint Explorer pane!

So this means that it's totally possible to use NService Bus with MSMQ in Windows Docker Containers--which is great for brown-field projects that aren't necessarily looking to switch transports in the very near future. This was all done using Windows Server 2019 LTSC Windows Container images. (I don't believe it's possible with Windows Server 2016 since MS didn't quite have things working wrt MSMQ in Windows Server 2016.)

Anyway, success. Just wanted to let you know.