Windows Service 対応の実行ファイルを Golang で作る
Windows のログをリモート転送するために Grafana loki の promtail を導入しようとしたが Windows 向けの実行ファイルが Windows Service としての起動に対応していなかったのでやり方を調べた。
Golang では golang.org/x/sys/windows/svc を使うことでWindowsService対応の実行ファイルがわりと簡単につくれることがわかった。
以下のようにすればよい
package main
import (
"context"
"log"
"os"
"os/signal"
"golang.org/x/sys/windows/svc"
)
var serviceName string = "hogehoge"
var StopCh = make(chan bool)
type service struct {
stopCh chan<- bool
}
func (s *service) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
changes <- svc.Status{State: svc.StartPending}
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
loop:
for {
select {
case c := <-r:
switch c.Cmd {
case svc.Interrogate:
changes <- c.CurrentStatus
case svc.Stop, svc.Shutdown:
s.stopCh <- true
break loop
}
}
}
changes <- svc.Status{State: svc.StopPending}
return
}
func dosomething(ctx context.Context) {
// Do something
}
func main() {
isService, err := svc.IsWindowsService()
if err != nil {
log.Fatalf("Failed to detect process environment: %v",err.Error())
}
if isService {
go func() {
err = svc.Run(serviceName, &service{stopCh: StopCh})
if err != nil {
log.Fatalf("Failed to start %s service: %v", serviceName, err.Error())
}
}()
}
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
go func() {
dosomething(ctx)
}()
for {
if <-StopCh {
log.Printf("Shutting down %s", serviceName)
break
}
}
}
同じようにして promtail のコードを修正して Windows Service に対応させることも考えたが、ビルド対象OSによって動作を切り替えてLinux等での動作に影響ない形でこれを実装するのがちょっと面倒だったので、外部コマンドを実行するだけの簡単な Windows Service を作った。
https://github.com/mamiya312/command2windowsservice
Windows Service にこれを登録し、これ経由でPromtailを起動することにしてやりたいことは実現できた。 以下のように使う。
C:\> sc create promtail binPath= "C:\promtail\command2windowsservice.exe -name promtail C:\promtail\v2.7.3\promtail-windows-amd64.exe --config.file C:\promtail\config\config.yaml
C:\> sc qc promtail
[SC] QueryServiceConfig SUCCESS
SERVICE_NAME: promtail
TYPE : 10 WIN32_OWN_PROCESS
START_TYPE : 2 AUTO_START (DELAYED)
ERROR_CONTROL : 1 NORMAL
BINARY_PATH_NAME : C:\promtail\command2windowsservice.exe -name promtail C:\promtail\v2.7.3\promtail-windows-amd64.exe --config.file C:\promtail\config\config.yaml
LOAD_ORDER_GROUP :
TAG : 0
DISPLAY_NAME : promtail
DEPENDENCIES :
SERVICE_START_NAME : LocalSystem
command2windowsservice.exe の引数にサービス名と promtail の実行ファイル、引数を指定している。
promtail の設定は以下のような感じ。どこにどんなログが出るのかわかっていないので、絞ったりはしていない
clients:
# - url: https://127.0.0.1/loki/api/v1/push
# basic_auth:
# username: DUMMY
# password: DUMMY
scrape_configs:
- job_name: windows.application
windows_events:
use_incoming_timestamp: false
bookmark_path: "C:\\promtail\\config\\Application.xml"
eventlog_name: "Application"
xpath_query: '*'
exclude_event_data: true
exclude_user_data: true
labels:
job: windows
relabel_configs:
- source_labels: ['computer']
target_label: 'host'
- job_name: windows.security
windows_events:
use_incoming_timestamp: false
bookmark_path: "C:\\promtail\\config\\Security.xml"
eventlog_name: "Security"
xpath_query: '*'
exclude_event_data: true
exclude_user_data: true
labels:
job: windows
relabel_configs:
- source_labels: ['computer']
target_label: 'host'
- job_name: windows.setup
windows_events:
use_incoming_timestamp: false
bookmark_path: "C:\\promtail\\config\\Setup.xml"
eventlog_name: "Setup"
xpath_query: '*'
exclude_event_data: true
exclude_user_data: true
labels:
job: windows
relabel_configs:
- source_labels: ['computer']
target_label: 'host'
- job_name: windows.system
windows_events:
use_incoming_timestamp: false
bookmark_path: "C:\\promtail\\config\\System.xml"
eventlog_name: "System"
xpath_query: '*'
exclude_event_data: true
exclude_user_data: true
labels:
job: windows
relabel_configs:
- source_labels: ['computer']
target_label: 'host'
windows_exporter がとても参考になった