diff --git a/.gitignore b/.gitignore index af43d257..5b938871 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ **/passwords **/minectl **/server.properties +tmp/ diff --git a/README.md b/README.md index 9792de6e..6fcf73e5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,12 @@ -# minectl 🗺 +# minectl 🗺 + +![Minecraft](https://img.shields.io/badge/Minecraft-62B47A?style=for-the-badge&logo=Minecraft&logoColor=white) +![Go](https://img.shields.io/badge/go-00ADD8?style=for-the-badge&logo=go&logoColor=white) +![Scaleway](https://img.shields.io/badge/scaleway-4F0599?style=for-the-badge&logo=scaleway&logoColor=white) +![DigitalOcean](https://img.shields.io/badge/DigitalOcean-0080FF?style=for-the-badge&logo=DigitalOcean&logoColor=white) +![Civo](https://img.shields.io/badge/Civo-239DFF?style=for-the-badge&logo=Civo&logoColor=white) +![Prometheus](https://img.shields.io/badge/Prometheus-E6522C?style=for-the-badge&logo=Prometheus&logoColor=white) +[![Build Binary](https://github.com/dirien/minectl/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/dirien/minectl/actions/workflows/ci.yaml) `minectl`️️ is a cli for creating Minecraft (java or bedrock) server on different cloud provider. @@ -60,6 +68,11 @@ spec: java: xmx: 2G xms: 2G + rcon: + password: test + port: 25575 + enabled: true + broadcast: true edition: "java|bedrock" properties: | level-seed=stackitminecraftrocks @@ -110,11 +123,29 @@ Flags: --id string contains the server id ``` +#### Monitoring 📊 + +Every instance of minectl 🗺, has following monitoring components included: + +- Prometheus (https://github.com/prometheus/prometheus) +- Node exporter (https://github.com/prometheus/node_exporter) + +The `edition:java` has on top following exporter included: + +- Minecraft exporter (https://github.com/dirien/minecraft-prometheus-exporter) + +You can acces the `prometheus` via + +```bash +http://:9090/graph +``` + #### Getting Started - [Civo Java Edition](docs/getting-started-civo.md) - [Civo Bedrock Edition](docs/getting-started-civo-bedrock.md) - [Scaleway Java Edition](docs/getting-started-scaleway.md) +- [How to monitor your multi-cloud minectl 🗺 server?](docs/multi-server-monitoring-civo.md) ### Supported cloud provider ☁ @@ -140,6 +171,7 @@ Apache License, Version 2.0 ### Roadmap 🛣️ - [x] Support Bedrock edition [#10](https://github.com/dirien/minectl/issues/10) +- [x] Add monitoring capabilities to minectl server [#21](https://github.com/dirien/minectl/issues/21) - [ ] List Minecraft Server - [ ] Update Minecraft Server - [ ] Support Mods and Plugins diff --git a/config/java/server-civo.yaml b/config/java/server-civo.yaml index 4ae6fc27..dcb42d9e 100644 --- a/config/java/server-civo.yaml +++ b/config/java/server-civo.yaml @@ -13,20 +13,22 @@ spec: java: xmx: 2G xms: 2G + rcon: + password: test + port: 25575 + enabled: true + broadcast: true edition: java properties: | level-seed=stackitminecraftrocks - broadcast-rcon-to-ops=true view-distance=10 enable-jmx-monitoring=false server-ip= resource-pack-prompt= - rcon.port=25575 gamemode=survival server-port=25565 allow-nether=true enable-command-block=false - enable-rcon=false sync-chunk-writes=true enable-query=false op-permission-level=4 @@ -34,7 +36,6 @@ spec: resource-pack= entity-broadcast-range-percentage=100 level-name=world - rcon.password= player-idle-timeout=0 motd=Civo Minecraft query.port=25565 diff --git a/config/java/server-do.yaml b/config/java/server-do.yaml index 1681c135..33898639 100644 --- a/config/java/server-do.yaml +++ b/config/java/server-do.yaml @@ -13,20 +13,22 @@ spec: java: xmx: 2G xms: 2G + rcon: + password: test + port: 25575 + enabled: true + broadcast: true edition: java properties: | level-seed=stackitminecraftrocks - broadcast-rcon-to-ops=true view-distance=10 enable-jmx-monitoring=false server-ip= resource-pack-prompt= - rcon.port=25575 gamemode=survival server-port=25565 allow-nether=true enable-command-block=false - enable-rcon=false sync-chunk-writes=true enable-query=false op-permission-level=4 @@ -34,7 +36,6 @@ spec: resource-pack= entity-broadcast-range-percentage=100 level-name=world - rcon.password= player-idle-timeout=0 motd=DigitalOcean Minecraft query.port=25565 diff --git a/config/java/server-scaleway.yaml b/config/java/server-scaleway.yaml index 2a0136a3..f124612c 100644 --- a/config/java/server-scaleway.yaml +++ b/config/java/server-scaleway.yaml @@ -13,20 +13,22 @@ spec: java: xmx: 2G xms: 2G + rcon: + password: test + port: 25575 + enabled: true + broadcast: true edition: java properties: | level-seed=stackitminecraftrocks - broadcast-rcon-to-ops=true view-distance=10 enable-jmx-monitoring=false server-ip= resource-pack-prompt= - rcon.port=25575 gamemode=survival server-port=25565 allow-nether=true enable-command-block=false - enable-rcon=false sync-chunk-writes=true enable-query=false op-permission-level=4 @@ -34,7 +36,6 @@ spec: resource-pack= entity-broadcast-range-percentage=100 level-name=world - rcon.password= player-idle-timeout=0 motd=Scaleway Minecraft query.port=25565 diff --git a/docs/dashboard/tutorial.json b/docs/dashboard/tutorial.json new file mode 100644 index 00000000..7909ff89 --- /dev/null +++ b/docs/dashboard/tutorial.json @@ -0,0 +1,374 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 26, + "iteration": 1626068074665, + "links": [], + "panels": [ + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "string" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.0.1", + "targets": [ + { + "exemplar": false, + "expr": "minecraft_prometheus_exporter_blocks_mined_total{job=\"$server\", player=\"$player\"}", + "format": "time_series", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "blocks mined", + "transformations": [ + { + "id": "labelsToFields", + "options": { + "valueLabel": "block" + } + } + ], + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "string" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 6, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.0.1", + "targets": [ + { + "exemplar": false, + "expr": "minecraft_prometheus_exporter_walk_one_cm_total{job=\"$server\", player=\"$player\"}", + "format": "time_series", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Walk On CM", + "transformations": [ + { + "id": "labelsToFields", + "options": { + "valueLabel": "block" + } + } + ], + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "displayMode": "color-text", + "filterable": false + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "string" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 2, + "options": { + "showHeader": false + }, + "pluginVersion": "8.0.1", + "targets": [ + { + "exemplar": true, + "expr": "minecraft_prometheus_exporter_player_online_total{job=\"$server\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Online", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "player" + ] + } + } + } + ], + "type": "table" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 5, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.0.1", + "targets": [ + { + "exemplar": false, + "expr": "minecraft_prometheus_exporter_deaths_total{job=\"$server\", player=\"$player\"}", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "💀", + "transformations": [ + { + "id": "labelsToFields", + "options": { + "valueLabel": "block" + } + } + ], + "type": "stat" + } + ], + "refresh": "", + "schemaVersion": 30, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": { + "selected": true, + "text": "minecraft-server-do", + "value": "minecraft-server-do" + }, + "datasource": null, + "definition": "label_values(job)", + "description": null, + "error": null, + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "server", + "options": [], + "query": { + "query": "label_values(job)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "minecraft-.*", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "allValue": null, + "current": { + "selected": true, + "text": "ediri", + "value": "ediri" + }, + "datasource": null, + "definition": "label_values(player)", + "description": null, + "error": null, + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "player", + "options": [], + "query": { + "query": "label_values(player)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "minectl", + "uid": "JU4wDoink", + "version": 2 +} \ No newline at end of file diff --git a/docs/getting-started-civo-bedrock.md b/docs/getting-started-civo-bedrock.md index 3cb3ce40..61dd4839 100644 --- a/docs/getting-started-civo-bedrock.md +++ b/docs/getting-started-civo-bedrock.md @@ -1,5 +1,4 @@ -![Civo](img/civo.png) - +![Civo](https://img.shields.io/badge/Civo-239DFF?style=for-the-badge&logo=Civo&logoColor=white) # Getting Started on Civo with the Bedrock edition Bedrock Edition (also known as the Bedrock editions, Bedrock versions, or just Bedrock) refers to the multi-platform diff --git a/docs/getting-started-civo.md b/docs/getting-started-civo.md index ebca8c23..5a7d553b 100644 --- a/docs/getting-started-civo.md +++ b/docs/getting-started-civo.md @@ -1,5 +1,4 @@ -![Civo](img/civo.png) - +![Civo](https://img.shields.io/badge/Civo-239DFF?style=for-the-badge&logo=Civo&logoColor=white) # Getting Started - Civo edition ## API Key @@ -38,20 +37,22 @@ spec: java: xmx: 2G xms: 2G + rcon: + password: test + port: 25575 + enabled: true + broadcast: true edition: java properties: | level-seed=randomseed - broadcast-rcon-to-ops=true view-distance=10 enable-jmx-monitoring=false server-ip= resource-pack-prompt= - rcon.port=25575 gamemode=survival server-port=25565 allow-nether=true enable-command-block=false - enable-rcon=false sync-chunk-writes=true enable-query=false op-permission-level=4 @@ -59,7 +60,6 @@ spec: resource-pack= entity-broadcast-range-percentage=100 level-name=world - rcon.password= player-idle-timeout=0 motd=Civo Minecraft query.port=25565 diff --git a/docs/getting-started-scaleway.md b/docs/getting-started-scaleway.md index daf1de78..e27e9d97 100644 --- a/docs/getting-started-scaleway.md +++ b/docs/getting-started-scaleway.md @@ -1,5 +1,4 @@ -![Civo](img/scaleway.png) - +![Scaleway](https://img.shields.io/badge/scaleway-4F0599?style=for-the-badge&logo=scaleway&logoColor=white) # Getting Started - Scaleway edition ## Access and Secret Key @@ -46,20 +45,22 @@ spec: java: xmx: 2G xms: 2G + rcon: + password: test + port: 25575 + enabled: true + broadcast: true edition: java properties: | level-seed=stackitminecraftrocks - broadcast-rcon-to-ops=true view-distance=10 enable-jmx-monitoring=false server-ip= resource-pack-prompt= - rcon.port=25575 gamemode=survival server-port=25565 allow-nether=true enable-command-block=false - enable-rcon=false sync-chunk-writes=true enable-query=false op-permission-level=4 @@ -67,7 +68,6 @@ spec: resource-pack= entity-broadcast-range-percentage=100 level-name=world - rcon.password= player-idle-timeout=0 motd=Scaleway Minecraft query.port=25565 diff --git a/docs/img/civo.png b/docs/img/civo.png deleted file mode 100644 index 19d434a2..00000000 Binary files a/docs/img/civo.png and /dev/null differ diff --git a/docs/img/dashboard.png b/docs/img/dashboard.png new file mode 100644 index 00000000..57758983 Binary files /dev/null and b/docs/img/dashboard.png differ diff --git a/docs/img/dns.png b/docs/img/dns.png new file mode 100644 index 00000000..43fb91a7 Binary files /dev/null and b/docs/img/dns.png differ diff --git a/docs/img/grafana.png b/docs/img/grafana.png new file mode 100644 index 00000000..425be1b4 Binary files /dev/null and b/docs/img/grafana.png differ diff --git a/docs/img/prometheus.png b/docs/img/prometheus.png new file mode 100644 index 00000000..9896bca6 Binary files /dev/null and b/docs/img/prometheus.png differ diff --git a/docs/img/prometheus_sd_do.png b/docs/img/prometheus_sd_do.png new file mode 100644 index 00000000..115ded89 Binary files /dev/null and b/docs/img/prometheus_sd_do.png differ diff --git a/docs/img/prometheus_sd_scaleway.png b/docs/img/prometheus_sd_scaleway.png new file mode 100644 index 00000000..96417a3c Binary files /dev/null and b/docs/img/prometheus_sd_scaleway.png differ diff --git a/docs/img/prometheus_targets.png b/docs/img/prometheus_targets.png new file mode 100644 index 00000000..4632e66a Binary files /dev/null and b/docs/img/prometheus_targets.png differ diff --git a/docs/img/scaleway.png b/docs/img/scaleway.png deleted file mode 100755 index d880326b..00000000 Binary files a/docs/img/scaleway.png and /dev/null differ diff --git a/docs/multi-server-monitoring-civo.md b/docs/multi-server-monitoring-civo.md new file mode 100644 index 00000000..a25937f6 --- /dev/null +++ b/docs/multi-server-monitoring-civo.md @@ -0,0 +1,380 @@ +![Civo](https://img.shields.io/badge/Civo-239DFF?style=for-the-badge&logo=Civo&logoColor=white) +![Kubernetes](https://img.shields.io/badge/Kubernetes-326CE5?style=for-the-badge&logo=Kubernetes&logoColor=white) +![Prometheus](https://img.shields.io/badge/Prometheus-E6522C?style=for-the-badge&logo=Prometheus&logoColor=white) +![Grafana](https://img.shields.io/badge/Grafana-F46800?style=for-the-badge&logo=Grafana&logoColor=white) + +# How to monitor your multi-cloud minectl 🗺 server? - Civo edition + +minectl 🗺 supports will be installed with following monitoring stack: + +- Prometheus (https://github.com/prometheus/prometheus) +- Node exporter (https://github.com/prometheus/node_exporter) +- Minecraft exporter (https://github.com/dirien/minecraft-prometheus-exporter) + +So you don't need to do anything. You can directly access your Prometheus instance via `http://:9090/graph` in the +browser of your choice. + +![img.png](img/prometheus.png) + +Every target is already configured, and you are ready to go. + +![img.png](img/prometheus_targets.png) + +So how, we can use this setting to monitor multi-cloud minectl 🗺 server? + +Enter the stage for `Civo` + +# Civo, Kubernetes and the marketplace + +Civo (https://www.civo.com), a UK-based startup, offers cloud native infrastructure services powered only by Kubernetes. +With launch times < +90s it is incredible fast and offers several state of the art ways of managing your clusters, using your favourite tools +like Terraform and Pulumi, or our simple to use CLI or API. + +On top, Civo offers a curated marketplace of Kubernetes applications. With these we can quick and easy install them in +to our Civo Kubernetes clusters + +# Create your Civo monitoring cluster + +In these guide we will cover the CLI way to create a Civo monitoring cluster: + +## CLI + +Follow the instructions to install the [Civo CLI](https://github.com/civo/cli#set-up) + +In order to use the command-line tool, you will need to authenticate yourself to the Civo API using a special key. You +can find an automatically-generated API key or regenerate a new key at https://www.civo.com/api. + +So let us create a Civo Kubernetes Cluster and install the +[kube-prometheus-stack](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack) as +the base for our monitoring stack. + +We will install the cert-manager directly via the marketplace. + +````bash +civo k3s create minectl-monitoring --nodes=1 --size=g3.k3s.large --applications=cert-manager,Nginx --remove-applications=Traefik +```` + +`The cluster black-dust (b7d9d2da-8947-4dbb-b349-3ed40363c498) has been created` + +Wait until the cluster is ready to use. + +```bash +civo k3s ls ++--------------------------------------+--------------------+--------+-------+-------+----------+ +| ID | Name | Region | Nodes | Pools | Status | ++--------------------------------------+--------------------+--------+-------+-------+----------+ +| 10142bd2-d34d-4804-941f-1e98e4a577da | minectl-monitoring | LON1 | 0 | 0 | BUILDING | ++--------------------------------------+--------------------+--------+-------+-------+----------+ +``` + +Get the kubeconfig or merge it your context. What ever you prefer. + +```bash +civo k3s config minectl-monitoring --save -p civo.yaml +export KUBECONFIG=civo.yaml + +kubectl get node + +NAME STATUS ROLES AGE VERSION +k3s-minectl-monitoring-09b924fa-node-pool-2336 Ready 39s v1.20.2+k3s1 +``` + +The good part is, that we already installed the nginx ingress controller and the cert-manger. So we can directly dive +into the installation of +the [kube-prometheus-stack](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack) + +### Kubernetes + +#### cert-manger + +Perpare a DNS Alias (I use `grafana-minectl.ediri.online`) and point to the public IP of your ingress controller: + +![img.png](img/dns.png) + +```bash +kubectl get svc -n ingress-nginx +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +ingress-nginx-controller-admission ClusterIP 10.43.141.34 443/TCP 30m +ingress-nginx-controller LoadBalancer 10.43.58.0 74.220.21.72 80:30780/TCP,443:31620/TCP 30m +``` + +Let us now install the ns, certificate, and the (cluster)-issuer for our Grafana UI. As namespace, we use `monitoring`, +that will also the namespace we deploy the monitoring stack too. + +```yaml +cat < values.yaml +grafana: + persistence: + enabled: true + size: 50Gi + storageClassName: civo-volume + + + serviceMonitor: + enabled: true + interval: 1m + selfMonitor: false + + ingress: + annotations: + kubernetes.io/ingress.class: nginx + enabled: true + tls: + - secretName: ediri-online-tls + hosts: + - grafana-minectl.ediri.online + hosts: + - grafana-minectl.ediri.online + + alertmanagerSpec: + storage: + volumeClaimTemplate: + spec: + storageClassName: civo-volume + accessModes: [ "ReadWriteOnce" ] + resources: + requests: + storage: 5Gi + +prometheus: + ingress: + enabled: false + + prometheusSpec: + replicas: 1 + + storageSpec: + volumeClaimTemplate: + spec: + storageClassName: civo-volume + accessModes: [ "ReadWriteOnce" ] + resources: + requests: + storage: 50Gi +EOF +``` + +```bash +helm upgrade -i monitoring prometheus-community/kube-prometheus-stack -n monitoring -f values.yaml +``` + +If everything works fine, you should be greeted with the Grafana screen. With a valid SSL certificate. + +![img.png](img/grafana.png) + +Login with the defaults `admin:prom-operator` + +To get the minecraft metrics to appear in the prometheus, we need to use the Kubernetes service of type `ExternalName` +with `Endpoint` and the `ServiceMonitor`. + +```yaml +cat < /minecraft/server.jar -{{else if eq .Edition "bedrock"}} +{{ else if eq .Spec.Minecraft.Edition "bedrock" -}} curl -sLSf https://minecraft.azureedge.net/bin-linux/bedrock-server-1.17.2.01.zip > /tmp/bedrock-server.zip unzip -o /tmp/bedrock-server.zip -d /minecraft chmod +x /minecraft/bedrock_server -{{end}} +{{- end }} echo "eula=true" > /minecraft/eula.txt mv /tmp/server.properties /minecraft/server.properties systemctl restart minecraft.service diff --git a/pgk/template/cloud-config.yaml.tmpl b/pgk/template/cloud-config.yaml.tmpl index 83700b00..303d1601 100644 --- a/pgk/template/cloud-config.yaml.tmpl +++ b/pgk/template/cloud-config.yaml.tmpl @@ -5,6 +5,10 @@ users: shell: /bin/false - name: node_exporter shell: /bin/false + {{ if eq .Spec.Minecraft.Edition "java" -}} + - name: minecraft_exporter + shell: /bin/false + {{- end }} package_update: true @@ -12,7 +16,7 @@ packages: - apt-transport-https - ca-certificates - curl - - {{if eq .Edition "java"}} openjdk-16-jre-headless {{else if eq .Edition "bedrock"}} unzip {{end}} + - {{if eq .Spec.Minecraft.Edition "java"}}openjdk-16-jre-headless{{else if eq .Spec.Minecraft.Edition "bedrock"}}unzip{{end}} - fail2ban fs_setup: @@ -31,7 +35,15 @@ write_files: net.ipv4.conf.all.forwarding=1 - path: /tmp/server.properties content: | - {{ .Properties }} + {{- range $element := .Properties }} + {{ $element -}} + {{- end -}} + {{- if .Spec.Minecraft.Java.Rcon.Enabled -}} + broadcast-rcon-to-ops={{.Spec.Minecraft.Java.Rcon.Broadcast}} + rcon.port={{.Spec.Minecraft.Java.Rcon.Port}} + enable-rcon={{.Spec.Minecraft.Java.Rcon.Enabled}} + rcon.password={{.Spec.Minecraft.Java.Rcon.Password}} + {{- end }} - path: /tmp/prometheus.yml content: | global: @@ -46,13 +58,18 @@ write_files: scrape_interval: 5s static_configs: - targets: ['localhost:9100'] + {{- if eq .Spec.Minecraft.Edition "java" }} + - job_name: 'minecraft_exporter' + scrape_interval: 1m + static_configs: + - targets: ['localhost:9150'] + {{- end }} - path: /etc/systemd/system/prometheus.service content: | [Unit] Description=Prometheus Wants=network-online.target After=network-online.target - [Service] User=prometheus Group=prometheus @@ -62,7 +79,6 @@ write_files: --storage.tsdb.path /var/lib/prometheus/ \ --web.console.templates=/etc/prometheus/consoles \ --web.console.libraries=/etc/prometheus/console_libraries - [Install] WantedBy=multi-user.target - path: /etc/systemd/system/node_exporter.service @@ -71,45 +87,58 @@ write_files: Description=Node Exporter Wants=network-online.target After=network-online.target - [Service] User=node_exporter Group=node_exporter Type=simple ExecStart=/usr/local/bin/node_exporter - [Install] WantedBy=multi-user.target + {{ if eq .Spec.Minecraft.Edition "java" -}} + - path: /etc/systemd/system/minecraft-prometheus-exporter.service + content: | + [Unit] + Description=Minecraft Exporter + Wants=network-online.target + After=network-online.target + [Service] + User=minecraft_exporter + Group=minecraft_exporter + Type=simple + ExecStart=/usr/local/bin/minecraft-prometheus-exporter \ + --mc.rcon-password={{.Spec.Minecraft.Java.Rcon.Password}} + [Install] + WantedBy=multi-user.target + {{ end -}} - path: /etc/systemd/system/minecraft.service content: | [Unit] Description=Minecraft Server Documentation=https://www.minecraft.net/en-us/download/server - [Service] WorkingDirectory=/minecraft Type=simple - {{if eq .Edition "java"}} - ExecStart=/usr/bin/java -Xmx2G -Xms2G -jar server.jar nogui - {{else if eq .Edition "bedrock"}} + {{- if eq .Spec.Minecraft.Edition "java" }} + ExecStart=/usr/bin/java -Xmx{{.Spec.Minecraft.Java.Xmx}} -Xms{{.Spec.Minecraft.Java.Xms}} -jar server.jar nogui + {{ else if eq .Spec.Minecraft.Edition "bedrock" }} ExecStart=/bin/sh -c "LD_LIBRARY_PATH=. ./bedrock_server" - {{end}} + {{- end }} Restart=on-failure RestartSec=5 - [Install] WantedBy=multi-user.target runcmd: + - export PROM_VERSION=2.28.1 - mkdir /etc/prometheus - mkdir /var/lib/prometheus - - curl -sSL https://github.com/prometheus/prometheus/releases/download/v2.27.1/prometheus-2.27.1.linux-amd64.tar.gz | tar -xz - - cp prometheus-2.27.1.linux-amd64/prometheus /usr/local/bin/ - - cp prometheus-2.27.1.linux-amd64/promtool /usr/local/bin/ + - curl -sSL https://github.com/prometheus/prometheus/releases/download/v$PROM_VERSION/prometheus-$PROM_VERSION.linux-amd64.tar.gz | tar -xz + - cp prometheus-$PROM_VERSION.linux-amd64/prometheus /usr/local/bin/ + - cp prometheus-$PROM_VERSION.linux-amd64/promtool /usr/local/bin/ - chown prometheus:prometheus /usr/local/bin/prometheus - chown prometheus:prometheus /usr/local/bin/promtool - - cp -r prometheus-2.27.1.linux-amd64/consoles /etc/prometheus - - cp -r prometheus-2.27.1.linux-amd64/console_libraries /etc/prometheus + - cp -r prometheus-$PROM_VERSION.linux-amd64/consoles /etc/prometheus + - cp -r prometheus-$PROM_VERSION.linux-amd64/console_libraries /etc/prometheus - chown -R prometheus:prometheus /var/lib/prometheus - chown -R prometheus:prometheus /etc/prometheus/consoles - chown -R prometheus:prometheus /etc/prometheus/console_libraries @@ -119,32 +148,42 @@ runcmd: - systemctl start prometheus - systemctl enable prometheus - - curl -sSL https://github.com/prometheus/node_exporter/releases/download/v1.1.2/node_exporter-1.1.2.linux-amd64.tar.gz | tar -xz - - cp node_exporter-1.1.2.linux-amd64/node_exporter /usr/local/bin + - export NODE_EXPORTER_VERSION=1.1.2 + - curl -sSL https://github.com/prometheus/node_exporter/releases/download/v$NODE_EXPORTER_VERSION/node_exporter-$NODE_EXPORTER_VERSION.linux-amd64.tar.gz | tar -xz + - cp node_exporter-$NODE_EXPORTER_VERSION.linux-amd64/node_exporter /usr/local/bin - chown node_exporter:node_exporter /usr/local/bin/node_exporter - systemctl daemon-reload - systemctl start node_exporter - systemctl enable node_exporter + {{ if eq .Spec.Minecraft.Edition "java" -}} + - export MINECRAFT_EXPORTER_VERSION=0.3.1 + - curl -sSL https://github.com/dirien/minecraft-prometheus-exporter/releases/download/v$MINECRAFT_EXPORTER_VERSION/minecraft-prometheus-exporter_$MINECRAFT_EXPORTER_VERSION.linux-amd64.tar.gz | tar -xz + - cp minecraft-prometheus-exporter /usr/local/bin + - chown minecraft_exporter:minecraft_exporter /usr/local/bin/minecraft-prometheus-exporter + - systemctl start minecraft-prometheus-exporter.service + - systemctl enable minecraft-prometheus-exporter.service + {{ end -}} + - ufw allow ssh - ufw allow 5201 - {{if eq .Edition "java"}} + {{ if eq .Spec.Minecraft.Edition "java" -}} - ufw allow proto udp to 0.0.0.0/0 port 25565 - {{else if eq .Edition "bedrock"}} + {{ else if eq .Spec.Minecraft.Edition "bedrock" -}} - ufw allow proto udp to 0.0.0.0/0 port 19132 - {{end}} + {{ end -}} - echo [DEFAULT] | sudo tee -a /etc/fail2ban/jail.local - echo banaction = ufw | sudo tee -a /etc/fail2ban/jail.local - echo [sshd] | sudo tee -a /etc/fail2ban/jail.local - echo enabled = true | sudo tee -a /etc/fail2ban/jail.local - sudo systemctl restart fail2ban - {{if eq .Edition "java"}} + {{ if eq .Spec.Minecraft.Edition "java" -}} - curl -sLSf https://launcher.mojang.com/v1/objects/a16d67e5807f57fc4e550299cf20226194497dc2/server.jar > /minecraft/server.jar - {{else if eq .Edition "bedrock"}} + {{ else if eq .Spec.Minecraft.Edition "bedrock" -}} - curl -sLSf https://minecraft.azureedge.net/bin-linux/bedrock-server-1.17.2.01.zip > /tmp/bedrock-server.zip - unzip -o /tmp/bedrock-server.zip -d /minecraft - chmod +x /minecraft/bedrock_server - {{end}} + {{- end }} - echo "eula=true" > /minecraft/eula.txt - mv /tmp/server.properties /minecraft/server.properties - systemctl restart minecraft.service diff --git a/pgk/template/template.go b/pgk/template/template.go index 57f3e1ef..d09a55a8 100644 --- a/pgk/template/template.go +++ b/pgk/template/template.go @@ -3,6 +3,7 @@ package template import ( "bytes" _ "embed" + "github.com/minectl/pgk/model" "strings" "text/template" ) @@ -15,13 +16,13 @@ var cloudConfig string type Template struct { Template *template.Template - Values templateValues + Values *templateValues } type templateValues struct { - Properties string - Edition string + *model.MinecraftServer Mount string + Properties []string } type Templater interface { @@ -37,31 +38,30 @@ func (t Template) GetTemplate() (string, error) { return buff.String(), nil } -func NewTemplateCivo(properties, edition string) (*Template, error) { +func NewTemplateCivo(model *model.MinecraftServer) (*Template, error) { civo, err := template.New("civo").Parse(bash) if err != nil { return nil, err } return &Template{ Template: civo, - Values: templateValues{ - Properties: strings.Replace(properties, "\t", "", -1), - Edition: edition, + Values: &templateValues{ + MinecraftServer: model, }, }, nil } -func NewTemplateCloudConfig(properties, edition, mount string) (*Template, error) { +func NewTemplateCloudConfig(model *model.MinecraftServer, mount string) (*Template, error) { cloudInit, err := template.New("cloud-init").Parse(cloudConfig) if err != nil { return nil, err } return &Template{ Template: cloudInit, - Values: templateValues{ - Properties: strings.Replace(properties, "\t", " ", -1), - Edition: edition, - Mount: mount, + Values: &templateValues{ + MinecraftServer: model, + Properties: strings.Split(model.GetProperties(), "\n"), + Mount: mount, }, }, nil } diff --git a/pgk/template/template_test.go b/pgk/template/template_test.go new file mode 100644 index 00000000..a57e3f7b --- /dev/null +++ b/pgk/template/template_test.go @@ -0,0 +1,678 @@ +package template + +import ( + "github.com/minectl/pgk/model" + "github.com/stretchr/testify/assert" + "testing" +) + +var ( + bedrock = model.MinecraftServer{ + Spec: model.Spec{ + Minecraft: model.Minecraft{ + Edition: "bedrock", + Properties: "level-seed=stackitminecraftrocks\nview-distance=10\nenable-jmx-monitoring=false\n", + }, + }, + } + java = model.MinecraftServer{ + Spec: model.Spec{ + Minecraft: model.Minecraft{ + Java: model.Java{ + Xms: "2G", + Xmx: "2G", + Rcon: model.Rcon{ + Port: 2, + Password: "test", + Enabled: true, + Broadcast: true, + }, + }, + Edition: "java", + Properties: "level-seed=stackitminecraftrocks\nview-distance=10\nenable-jmx-monitoring=false\n", + }, + }, + } + bedrockCivoWant = `#!/bin/bash + +tee /tmp/server.properties < /tmp/bedrock-server.zip +unzip -o /tmp/bedrock-server.zip -d /minecraft +chmod +x /minecraft/bedrock_server +echo "eula=true" > /minecraft/eula.txt +mv /tmp/server.properties /minecraft/server.properties +systemctl restart minecraft.service +systemctl enable minecraft.service` + + javaCivoWant = `#!/bin/bash + +tee /tmp/server.properties < /minecraft/server.jar + +echo "eula=true" > /minecraft/eula.txt +mv /tmp/server.properties /minecraft/server.properties +systemctl restart minecraft.service +systemctl enable minecraft.service` + + bedrockCloudInitWant = `#cloud-config +users: + - default + - name: prometheus + shell: /bin/false + - name: node_exporter + shell: /bin/false + + +package_update: true + +packages: + - apt-transport-https + - ca-certificates + - curl + - unzip + - fail2ban + +fs_setup: + - label: minecraft + device: /dev/sda + filesystem: xfs + overwrite: false + +mounts: + - [/dev/sda, /minecraft] + +# Enable ipv4 forwarding, required on CIS hardened machines +write_files: + - path: /etc/sysctl.d/enabled_ipv4_forwarding.conf + content: | + net.ipv4.conf.all.forwarding=1 + - path: /tmp/server.properties + content: | + level-seed=stackitminecraftrocks + view-distance=10 + enable-jmx-monitoring=false + + - path: /tmp/prometheus.yml + content: | + global: + scrape_interval: 15s + + scrape_configs: + - job_name: 'prometheus' + scrape_interval: 5s + static_configs: + - targets: ['localhost:9090'] + - job_name: 'node_exporter' + scrape_interval: 5s + static_configs: + - targets: ['localhost:9100'] + - path: /etc/systemd/system/prometheus.service + content: | + [Unit] + Description=Prometheus + Wants=network-online.target + After=network-online.target + [Service] + User=prometheus + Group=prometheus + Type=simple + ExecStart=/usr/local/bin/prometheus \ + --config.file /etc/prometheus/prometheus.yml \ + --storage.tsdb.path /var/lib/prometheus/ \ + --web.console.templates=/etc/prometheus/consoles \ + --web.console.libraries=/etc/prometheus/console_libraries + [Install] + WantedBy=multi-user.target + - path: /etc/systemd/system/node_exporter.service + content: | + [Unit] + Description=Node Exporter + Wants=network-online.target + After=network-online.target + [Service] + User=node_exporter + Group=node_exporter + Type=simple + ExecStart=/usr/local/bin/node_exporter + [Install] + WantedBy=multi-user.target + - path: /etc/systemd/system/minecraft.service + content: | + [Unit] + Description=Minecraft Server + Documentation=https://www.minecraft.net/en-us/download/server + [Service] + WorkingDirectory=/minecraft + Type=simple + ExecStart=/bin/sh -c "LD_LIBRARY_PATH=. ./bedrock_server" + Restart=on-failure + RestartSec=5 + [Install] + WantedBy=multi-user.target + +runcmd: + - export PROM_VERSION=2.28.1 + - mkdir /etc/prometheus + - mkdir /var/lib/prometheus + - curl -sSL https://github.com/prometheus/prometheus/releases/download/v$PROM_VERSION/prometheus-$PROM_VERSION.linux-amd64.tar.gz | tar -xz + - cp prometheus-$PROM_VERSION.linux-amd64/prometheus /usr/local/bin/ + - cp prometheus-$PROM_VERSION.linux-amd64/promtool /usr/local/bin/ + - chown prometheus:prometheus /usr/local/bin/prometheus + - chown prometheus:prometheus /usr/local/bin/promtool + - cp -r prometheus-$PROM_VERSION.linux-amd64/consoles /etc/prometheus + - cp -r prometheus-$PROM_VERSION.linux-amd64/console_libraries /etc/prometheus + - chown -R prometheus:prometheus /var/lib/prometheus + - chown -R prometheus:prometheus /etc/prometheus/consoles + - chown -R prometheus:prometheus /etc/prometheus/console_libraries + - mv /tmp/prometheus.yml /etc/prometheus/prometheus.yml + - chown prometheus:prometheus /etc/prometheus/prometheus.yml + - systemctl daemon-reload + - systemctl start prometheus + - systemctl enable prometheus + + - export NODE_EXPORTER_VERSION=1.1.2 + - curl -sSL https://github.com/prometheus/node_exporter/releases/download/v$NODE_EXPORTER_VERSION/node_exporter-$NODE_EXPORTER_VERSION.linux-amd64.tar.gz | tar -xz + - cp node_exporter-$NODE_EXPORTER_VERSION.linux-amd64/node_exporter /usr/local/bin + - chown node_exporter:node_exporter /usr/local/bin/node_exporter + - systemctl daemon-reload + - systemctl start node_exporter + - systemctl enable node_exporter + + - ufw allow ssh + - ufw allow 5201 + - ufw allow proto udp to 0.0.0.0/0 port 19132 + - echo [DEFAULT] | sudo tee -a /etc/fail2ban/jail.local + - echo banaction = ufw | sudo tee -a /etc/fail2ban/jail.local + - echo [sshd] | sudo tee -a /etc/fail2ban/jail.local + - echo enabled = true | sudo tee -a /etc/fail2ban/jail.local + - sudo systemctl restart fail2ban + - curl -sLSf https://minecraft.azureedge.net/bin-linux/bedrock-server-1.17.2.01.zip > /tmp/bedrock-server.zip + - unzip -o /tmp/bedrock-server.zip -d /minecraft + - chmod +x /minecraft/bedrock_server + - echo "eula=true" > /minecraft/eula.txt + - mv /tmp/server.properties /minecraft/server.properties + - systemctl restart minecraft.service + - systemctl enable minecraft.service` + + javaCloudInitWant = `#cloud-config +users: + - default + - name: prometheus + shell: /bin/false + - name: node_exporter + shell: /bin/false + - name: minecraft_exporter + shell: /bin/false + +package_update: true + +packages: + - apt-transport-https + - ca-certificates + - curl + - openjdk-16-jre-headless + - fail2ban + +fs_setup: + - label: minecraft + device: /dev/sda + filesystem: xfs + overwrite: false + +mounts: + - [/dev/sda, /minecraft] + +# Enable ipv4 forwarding, required on CIS hardened machines +write_files: + - path: /etc/sysctl.d/enabled_ipv4_forwarding.conf + content: | + net.ipv4.conf.all.forwarding=1 + - path: /tmp/server.properties + content: | + level-seed=stackitminecraftrocks + view-distance=10 + enable-jmx-monitoring=false + broadcast-rcon-to-ops=true + rcon.port=2 + enable-rcon=true + rcon.password=test + - path: /tmp/prometheus.yml + content: | + global: + scrape_interval: 15s + + scrape_configs: + - job_name: 'prometheus' + scrape_interval: 5s + static_configs: + - targets: ['localhost:9090'] + - job_name: 'node_exporter' + scrape_interval: 5s + static_configs: + - targets: ['localhost:9100'] + - job_name: 'minecraft_exporter' + scrape_interval: 1m + static_configs: + - targets: ['localhost:9150'] + - path: /etc/systemd/system/prometheus.service + content: | + [Unit] + Description=Prometheus + Wants=network-online.target + After=network-online.target + [Service] + User=prometheus + Group=prometheus + Type=simple + ExecStart=/usr/local/bin/prometheus \ + --config.file /etc/prometheus/prometheus.yml \ + --storage.tsdb.path /var/lib/prometheus/ \ + --web.console.templates=/etc/prometheus/consoles \ + --web.console.libraries=/etc/prometheus/console_libraries + [Install] + WantedBy=multi-user.target + - path: /etc/systemd/system/node_exporter.service + content: | + [Unit] + Description=Node Exporter + Wants=network-online.target + After=network-online.target + [Service] + User=node_exporter + Group=node_exporter + Type=simple + ExecStart=/usr/local/bin/node_exporter + [Install] + WantedBy=multi-user.target + - path: /etc/systemd/system/minecraft-prometheus-exporter.service + content: | + [Unit] + Description=Minecraft Exporter + Wants=network-online.target + After=network-online.target + [Service] + User=minecraft_exporter + Group=minecraft_exporter + Type=simple + ExecStart=/usr/local/bin/minecraft-prometheus-exporter \ + --mc.rcon-password=test + [Install] + WantedBy=multi-user.target + - path: /etc/systemd/system/minecraft.service + content: | + [Unit] + Description=Minecraft Server + Documentation=https://www.minecraft.net/en-us/download/server + [Service] + WorkingDirectory=/minecraft + Type=simple + ExecStart=/usr/bin/java -Xmx2G -Xms2G -jar server.jar nogui + + Restart=on-failure + RestartSec=5 + [Install] + WantedBy=multi-user.target + +runcmd: + - export PROM_VERSION=2.28.1 + - mkdir /etc/prometheus + - mkdir /var/lib/prometheus + - curl -sSL https://github.com/prometheus/prometheus/releases/download/v$PROM_VERSION/prometheus-$PROM_VERSION.linux-amd64.tar.gz | tar -xz + - cp prometheus-$PROM_VERSION.linux-amd64/prometheus /usr/local/bin/ + - cp prometheus-$PROM_VERSION.linux-amd64/promtool /usr/local/bin/ + - chown prometheus:prometheus /usr/local/bin/prometheus + - chown prometheus:prometheus /usr/local/bin/promtool + - cp -r prometheus-$PROM_VERSION.linux-amd64/consoles /etc/prometheus + - cp -r prometheus-$PROM_VERSION.linux-amd64/console_libraries /etc/prometheus + - chown -R prometheus:prometheus /var/lib/prometheus + - chown -R prometheus:prometheus /etc/prometheus/consoles + - chown -R prometheus:prometheus /etc/prometheus/console_libraries + - mv /tmp/prometheus.yml /etc/prometheus/prometheus.yml + - chown prometheus:prometheus /etc/prometheus/prometheus.yml + - systemctl daemon-reload + - systemctl start prometheus + - systemctl enable prometheus + + - export NODE_EXPORTER_VERSION=1.1.2 + - curl -sSL https://github.com/prometheus/node_exporter/releases/download/v$NODE_EXPORTER_VERSION/node_exporter-$NODE_EXPORTER_VERSION.linux-amd64.tar.gz | tar -xz + - cp node_exporter-$NODE_EXPORTER_VERSION.linux-amd64/node_exporter /usr/local/bin + - chown node_exporter:node_exporter /usr/local/bin/node_exporter + - systemctl daemon-reload + - systemctl start node_exporter + - systemctl enable node_exporter + + - export MINECRAFT_EXPORTER_VERSION=0.3.1 + - curl -sSL https://github.com/dirien/minecraft-prometheus-exporter/releases/download/v$MINECRAFT_EXPORTER_VERSION/minecraft-prometheus-exporter_$MINECRAFT_EXPORTER_VERSION.linux-amd64.tar.gz | tar -xz + - cp minecraft-prometheus-exporter /usr/local/bin + - chown minecraft_exporter:minecraft_exporter /usr/local/bin/minecraft-prometheus-exporter + - systemctl start minecraft-prometheus-exporter.service + - systemctl enable minecraft-prometheus-exporter.service + - ufw allow ssh + - ufw allow 5201 + - ufw allow proto udp to 0.0.0.0/0 port 25565 + - echo [DEFAULT] | sudo tee -a /etc/fail2ban/jail.local + - echo banaction = ufw | sudo tee -a /etc/fail2ban/jail.local + - echo [sshd] | sudo tee -a /etc/fail2ban/jail.local + - echo enabled = true | sudo tee -a /etc/fail2ban/jail.local + - sudo systemctl restart fail2ban + - curl -sLSf https://launcher.mojang.com/v1/objects/a16d67e5807f57fc4e550299cf20226194497dc2/server.jar > /minecraft/server.jar + + - echo "eula=true" > /minecraft/eula.txt + - mv /tmp/server.properties /minecraft/server.properties + - systemctl restart minecraft.service + - systemctl enable minecraft.service` +) + +func TestCivoBedrockTemplate(t *testing.T) { + t.Run("Test Template Bedrock for Civo bash", func(t *testing.T) { + civo, err := NewTemplateCivo(&bedrock) + if err != nil { + t.Fatal(err) + } + got, err := civo.GetTemplate() + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, bedrockCivoWant, got) + }) +} + +func TestCivoJavaTemplate(t *testing.T) { + t.Run("Test Template Java for Civo bash", func(t *testing.T) { + civo, err := NewTemplateCivo(&java) + if err != nil { + t.Fatal(err) + } + got, err := civo.GetTemplate() + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, javaCivoWant, got) + }) +} + +func TestCloudInitBedrockTemplate(t *testing.T) { + t.Run("Test Template Bedrock for Cloud-Init", func(t *testing.T) { + civo, err := NewTemplateCloudConfig(&bedrock, "sda") + if err != nil { + t.Fatal(err) + } + got, err := civo.GetTemplate() + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, bedrockCloudInitWant, got) + }) +} + +func TestCloudInitJavaTemplate(t *testing.T) { + t.Run("Test Template Java for Cloud-Init", func(t *testing.T) { + civo, err := NewTemplateCloudConfig(&java, "sda") + if err != nil { + t.Fatal(err) + } + got, err := civo.GetTemplate() + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, javaCloudInitWant, got) + }) +}