Bring minecraft-server up to the chart style guidelines (1.0.0+upjava8)
Stage 2 of the Fleet migration for minecraft-server. Major bump: runtime behaviour changes (probes, hardening) and the values interface breaks (resource renames, strict schema). Two real bugs first: - The container declared port 35565 while the Service targeted 25565. It only worked because targetPort was numeric. Both are now server.port (25565) and the Service targets the named port. - The download-serverpack init container was guarded by the map .Values.curseforgeServerpack instead of .enabled, so it rendered for every release and silently no-opped on an empty wget URL. Conventions: - Resources renamed to <release>-<chart>-<kind>, templates to <app>.<kind>.yaml. The PVC deliberately keeps its historical name - renaming it would provision a new, empty volume and orphan the worlds. - Probes (liveness/readiness/startup) via the image's own mc-health, so Ready means the server answers a server-list ping. Startup budget 20 min. - Hardened, values-configurable security contexts: non-root uid 1000, read-only root filesystem with an emptyDir at /tmp, all caps dropped. - Resources from values with the limitFactor helper; an unset memory request is derived from the JVM heap so the two cannot drift apart. - Additive env block rendered through tpl, pinned init image, storage class as a value, RCON wiring only when actually enabled. - curseforge/ftb/curseforgeServerpack are now mutually exclusive. - minecraft.version is pinned to the Java line instead of LATEST. - The mc-router auto-discovery annotation is gone; the service type it implicitly switched is now the explicit value server.service.type. Schema pilot (#68): values.schema.json with additionalProperties false on every chart-owned level, rejecting all three known dead keys.
This commit is contained in:
14 files changed
+789
-142
No files matched your search
@@ -13,7 +13,7 @@ Collection of some self-made helm-charts. Each top-level directory is a standalo
|
|||||||
| `homeassistant` | Home Assistant | StatefulSet, has `NOTES.txt` and Helm tests |
|
| `homeassistant` | Home Assistant | StatefulSet, has `NOTES.txt` and Helm tests |
|
||||||
| `matrix-synapse` | Matrix Synapse | StatefulSet plus an nginx delegation Deployment/Service |
|
| `matrix-synapse` | Matrix Synapse | StatefulSet plus an nginx delegation Deployment/Service |
|
||||||
| `minecraft-router` | itzg/mc-router | Deployment + routing ConfigMap + NodePort Service (TCP only, no ingress); `mappings` is a required value |
|
| `minecraft-router` | itzg/mc-router | Deployment + routing ConfigMap + NodePort Service (TCP only, no ingress); `mappings` is a required value |
|
||||||
| `minecraft-server` | itzg/minecraft-server | StatefulSet + PVC + NodePort Service + init-script ConfigMap (no ingress); pre-existing chart, still on its legacy conventions. Published once per Java line (`appVersion: java8` / `java17` / `java21`) — the image tag comes from `.Chart.AppVersion`, so the Java line is chosen by picking the chart version |
|
| `minecraft-server` | itzg/minecraft-server | StatefulSet + PVC + Service + init-script ConfigMap (no ingress); service type is a value (`ClusterIP` by default, routed via `minecraft-router`). Published once per Java line (`appVersion: java8` / `java17` / `java21`) — the image tag comes from `.Chart.AppVersion`, so the Java line is chosen by picking the chart version, and `minecraft.version` ships a default matching that line. First chart with a strict `values.schema.json` (pilot, #68) |
|
||||||
| `outline` | Outline | StatefulSet, separate DB/Redis secrets |
|
| `outline` | Outline | StatefulSet, separate DB/Redis secrets |
|
||||||
| `paperless-ngx` | Paperless-ngx | Largest chart: consume CronJob, SMB-connector PV/PVC, pre/post-consumption scripts ConfigMap |
|
| `paperless-ngx` | Paperless-ngx | Largest chart: consume CronJob, SMB-connector PV/PVC, pre/post-consumption scripts ConfigMap |
|
||||||
| `patchmon` | PatchMon | Two Deployments (frontend + backend), OIDC secret |
|
| `patchmon` | PatchMon | Two Deployments (frontend + backend), OIDC secret |
|
||||||
|
|||||||
@@ -62,8 +62,10 @@ spec:
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: ci-mc-secret
|
name: ci-minecraft-server-secret
|
||||||
stringData:
|
stringData:
|
||||||
|
# Only consumed when server.rcon.enabled is true (RCON is off by default, and
|
||||||
|
# since chart 1.0.0 the chart no longer references the key while it is off).
|
||||||
rcon-password: ci-dummy-rcon-password
|
rcon-password: ci-dummy-rcon-password
|
||||||
# Only consumed when curseforge.enabled is true; provided so the CI install
|
# Only consumed when curseforge.enabled is true; provided so the CI install
|
||||||
# keeps working if the test values ever enable a CurseForge modpack.
|
# keeps working if the test values ever enable a CurseForge modpack.
|
||||||
|
|||||||
@@ -1,33 +1,21 @@
|
|||||||
# Values for the minecraft-server install-test. Kept as close to the real
|
# Values for the minecraft-server install-test. Kept as close to the real
|
||||||
# deployments as possible; only what a CI runner cannot provide is overridden.
|
# deployments as possible; only what a CI runner cannot provide is overridden.
|
||||||
#
|
#
|
||||||
# 1. minecraft.version: the image tag comes from .Chart.AppVersion, so the JVM
|
# minecraft.version is deliberately NOT set here any more. Since chart 1.0.0 the
|
||||||
# in the image is fixed per published chart version and the Minecraft
|
# chart ships a pinned default that belongs to the Java line it publishes (the
|
||||||
# version has to match that Java line - Java 8 runs Minecraft only up to
|
# image tag comes from .Chart.AppVersion, and Java 8 runs Minecraft only up to
|
||||||
# 1.16.5, 1.18-1.20.4 need Java 17, and 1.20.5+ need Java 21. No single
|
# 1.16.5, 1.18-1.20.4 need Java 17, 1.20.5+ need Java 21). Leaving it unset
|
||||||
# Minecraft version boots on all three lines, so this value is bumped
|
# means the install-test exercises exactly the default a user would get, so a
|
||||||
# together with appVersion in every Java-line PR.
|
# Java line published with a mismatched default version fails CI instead of
|
||||||
# This chart version publishes appVersion "java21", hence 1.21.1 - which is
|
# passing on a CI-only override.
|
||||||
# also what ftb-skies-2, the one server actually running on this line, sets
|
#
|
||||||
# in its real values file, so CI exercises the production version rather
|
# server.service.type is not set either: the default is ClusterIP, which is what
|
||||||
# than an arbitrary Java-21-capable one.
|
# all three real deployments run (routing is done by the static mappings of the
|
||||||
# The production default is "LATEST", which would drift on every restart.
|
# minecraft-router chart). That also keeps CI free of node-port allocation.
|
||||||
# 2. minecraft.resources.storage: the production 32Gi world volume is backed by
|
|
||||||
# a static hostPath PV in CI (see fixtures.yaml); 2Gi is plenty for a server
|
|
||||||
# that only has to boot.
|
|
||||||
# 3. minecraft.resources.memory: sets the JVM heap (-Xmx); 1G instead of 4G
|
|
||||||
# keeps the runner comfortable. The container resource *requests* are
|
|
||||||
# hardcoded in the template (cpu 2 / memory 4Gi) and cannot be overridden -
|
|
||||||
# they fit a 4-vCPU/16GB GitHub runner, and making them configurable is part
|
|
||||||
# of the stage-2 convention alignment.
|
|
||||||
# 4. server.outwardPort stays at the chart default 30565: unlike minecraft-router
|
|
||||||
# that default is already inside the Kubernetes node-port range
|
|
||||||
# (30000-32767), so the k3d API server accepts it unchanged.
|
|
||||||
secrets:
|
secrets:
|
||||||
minecraftServerRef: vaults/CI/items/minecraft-server
|
minecraftServerRef: vaults/CI/items/minecraft-server
|
||||||
|
|
||||||
minecraft:
|
minecraft:
|
||||||
version: "1.21.1"
|
|
||||||
management:
|
management:
|
||||||
# opPlayers deliberately stays at the default (null): the image resolves
|
# opPlayers deliberately stays at the default (null): the image resolves
|
||||||
# every name in OPS against the external Playerdb API and aborts the whole
|
# every name in OPS against the external Playerdb API and aborts the whole
|
||||||
@@ -36,5 +24,19 @@ minecraft:
|
|||||||
# lookup.
|
# lookup.
|
||||||
maxPlayers: 2
|
maxPlayers: 2
|
||||||
resources:
|
resources:
|
||||||
|
# The production 32Gi world volume is backed by a static hostPath PV in CI
|
||||||
|
# (see fixtures.yaml); 2Gi is plenty for a server that only has to boot.
|
||||||
storage: 2Gi
|
storage: 2Gi
|
||||||
|
# JVM heap. 1G instead of 4G keeps the runner comfortable - and since chart
|
||||||
|
# 1.0.0 this also drives the container's memory request (1Gi) and, via
|
||||||
|
# limitFactor 3, its limit (3Gi), so the runner is not asked for the
|
||||||
|
# production-sized 4Gi/12Gi any more.
|
||||||
memory: "1"
|
memory: "1"
|
||||||
|
|
||||||
|
server:
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
# The production default is 2 CPUs, which would not leave much room on a
|
||||||
|
# 4-vCPU GitHub runner alongside the k3d control plane. Since chart 1.0.0
|
||||||
|
# this is configurable instead of hardcoded in the template.
|
||||||
|
cpu: "500m"
|
||||||
@@ -1,24 +1,15 @@
|
|||||||
apiVersion: v2
|
apiVersion: v2
|
||||||
name: minecraft-server
|
name: minecraft-server
|
||||||
description: A Helm chart for deploying a itzg/minecraft-server
|
description: A Helm chart for deploying a itzg/minecraft-server
|
||||||
|
icon: https://avatars.githubusercontent.com/u/988985
|
||||||
|
|
||||||
# A chart can be either an 'application' or a 'library' chart.
|
|
||||||
#
|
|
||||||
# Application charts are a collection of templates that can be packaged into versioned archives
|
|
||||||
# to be deployed.
|
|
||||||
#
|
|
||||||
# Library charts provide useful utilities or functions for the chart developer. They're included as
|
|
||||||
# a dependency of application charts to inject those utilities and functions into the rendering
|
|
||||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
|
||||||
type: application
|
type: application
|
||||||
|
|
||||||
# This is the chart version. This version number should be incremented each time you make changes
|
# The Java line of this chart version is chosen through appVersion, which is
|
||||||
# to the chart and its templates, including the app version.
|
# the image tag: the chart is published once per line (java8 / java17 / java21)
|
||||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
# and minecraft.version in values.yaml must match the line published here.
|
||||||
version: 0.4.0+upjava21
|
version: 1.0.0+upjava8
|
||||||
|
appVersion: "java8"
|
||||||
|
|
||||||
# This is the version number of the application being deployed. This version number should be
|
maintainers:
|
||||||
# incremented each time you make changes to the application. Versions are not expected to
|
- name: jklein
|
||||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
|
||||||
# It is recommended to use it with quotes.
|
|
||||||
appVersion: "java21"
|
|
||||||
@@ -1,3 +1,112 @@
|
|||||||
|
{{/*
|
||||||
|
Render the body of a container "resources:" block from a values dict of the form
|
||||||
|
|
||||||
|
resources:
|
||||||
|
limitFactor: 3 # optional, default 3
|
||||||
|
requests: { cpu, memory, ephemeral-storage / ephemeralStorage, ... }
|
||||||
|
limits: { ... } # optional, every entry optional
|
||||||
|
|
||||||
|
Rules (variant B, see issue #19):
|
||||||
|
- requests are rendered as given (camelCase "ephemeralStorage" is normalized
|
||||||
|
to the Kubernetes key "ephemeral-storage").
|
||||||
|
- an explicitly set limit always wins.
|
||||||
|
- a missing memory / ephemeral-storage limit is computed as
|
||||||
|
limitFactor x the corresponding request.
|
||||||
|
- a missing cpu limit is NOT set at all (the kubelet then treats CPU as
|
||||||
|
unlimited; CPU is compressible, so this cannot kill the workload).
|
||||||
|
|
||||||
|
Usage (the values path to the resources dict varies per chart):
|
||||||
|
resources:
|
||||||
|
{{- include "minecraft-server.resources" .Values.<app>.resources | nindent 12 }}
|
||||||
|
|
||||||
|
NOTE: charts in this repo deliberately have no dependencies, so this helper is
|
||||||
|
duplicated into every chart's templates/_helpers.tpl with the chart's name as
|
||||||
|
define prefix. The reference implementation lives in template-chart.
|
||||||
|
*/}}
|
||||||
|
{{- define "minecraft-server.resources" -}}
|
||||||
|
{{- $resources := . | default dict -}}
|
||||||
|
{{- $factor := 3.0 -}}
|
||||||
|
{{- if not (kindIs "invalid" $resources.limitFactor) -}}
|
||||||
|
{{- $factor = float64 $resources.limitFactor -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- $requests := dict -}}
|
||||||
|
{{- range $key, $value := ($resources.requests | default dict) -}}
|
||||||
|
{{- if not (kindIs "invalid" $value) -}}
|
||||||
|
{{- $_ := set $requests (include "minecraft-server.resourceKey" $key) $value -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- $limits := dict -}}
|
||||||
|
{{- range $key, $value := ($resources.limits | default dict) -}}
|
||||||
|
{{- if not (kindIs "invalid" $value) -}}
|
||||||
|
{{- $_ := set $limits (include "minecraft-server.resourceKey" $key) $value -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- range $key, $request := $requests -}}
|
||||||
|
{{- if and (ne $key "cpu") (not (hasKey $limits $key)) -}}
|
||||||
|
{{- $_ := set $limits $key (include "minecraft-server.multiplyQuantity" (dict "quantity" $request "factor" $factor)) -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- $out := dict -}}
|
||||||
|
{{- with $requests }}{{- $_ := set $out "requests" . }}{{- end -}}
|
||||||
|
{{- with $limits }}{{- $_ := set $out "limits" . }}{{- end -}}
|
||||||
|
{{- toYaml $out -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Normalize a resource name from values to its Kubernetes key.
|
||||||
|
*/}}
|
||||||
|
{{- define "minecraft-server.resourceKey" -}}
|
||||||
|
{{- if eq . "ephemeralStorage" -}}ephemeral-storage{{- else -}}{{ . }}{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Multiply a Kubernetes quantity by a factor, keeping its suffix.
|
||||||
|
Input: dict "quantity" <string|number> "factor" <float64>.
|
||||||
|
Handles plain numbers ("2", "0.5", 1073741824), decimal ("1.5Gi"), binary
|
||||||
|
("512Mi", "1Gi", "10Ki") and milli/SI suffixes ("100m", "500M"). Integer
|
||||||
|
results are printed without a decimal point.
|
||||||
|
*/}}
|
||||||
|
{{- define "minecraft-server.multiplyQuantity" -}}
|
||||||
|
{{- $quantity := printf "%v" .quantity -}}
|
||||||
|
{{- $number := regexFind "^[0-9]+(\\.[0-9]+)?" $quantity -}}
|
||||||
|
{{- if not $number -}}
|
||||||
|
{{- fail (printf "cannot parse resource quantity %q" $quantity) -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- $suffix := trimPrefix $number $quantity -}}
|
||||||
|
{{- if not (regexMatch "^(m|k|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$" $suffix) -}}
|
||||||
|
{{- fail (printf "unsupported resource quantity suffix %q in %q" $suffix $quantity) -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- $product := mulf (float64 $number) .factor -}}
|
||||||
|
{{- if eq $product (floor $product) -}}
|
||||||
|
{{- printf "%d%s" (int64 $product) $suffix -}}
|
||||||
|
{{- else -}}
|
||||||
|
{{- printf "%v%s" $product $suffix -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Resolve the server container's resources.
|
||||||
|
|
||||||
|
The JVM heap (minecraft.resources.memory, in GiB) and the container's memory
|
||||||
|
request used to be configured in two unrelated places - the heap was a value,
|
||||||
|
the request and limit were hardcoded in the template - so raising the heap did
|
||||||
|
not raise the container's memory. Here an unset memory request is derived from
|
||||||
|
the heap instead, which keeps the two from drifting apart; an explicitly set
|
||||||
|
request always wins. The limit follows from the request via the limitFactor
|
||||||
|
rule in "minecraft-server.resources".
|
||||||
|
|
||||||
|
Input: the root context.
|
||||||
|
*/}}
|
||||||
|
{{- define "minecraft-server.serverResources" -}}
|
||||||
|
{{- $resources := deepCopy (.Values.server.resources | default dict) -}}
|
||||||
|
{{- $requests := deepCopy ($resources.requests | default dict) -}}
|
||||||
|
{{- if kindIs "invalid" $requests.memory -}}
|
||||||
|
{{- $_ := set $requests "memory" (printf "%vGi" .Values.minecraft.resources.memory) -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- $_ := set $resources "requests" $requests -}}
|
||||||
|
{{- include "minecraft-server.resources" $resources -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
{{/*
|
{{/*
|
||||||
Render the replica count for a Deployment/StatefulSet.
|
Render the replica count for a Deployment/StatefulSet.
|
||||||
|
|
||||||
@@ -15,14 +124,9 @@ Rules (issue #25):
|
|||||||
Usage (the values path to the workload's replicas varies per chart):
|
Usage (the values path to the workload's replicas varies per chart):
|
||||||
replicas: {{ include "minecraft-server.replicas" (dict "scaleDown" .Values.scaleDown "replicas" .Values.server.replicas) }}
|
replicas: {{ include "minecraft-server.replicas" (dict "scaleDown" .Values.scaleDown "replicas" .Values.server.replicas) }}
|
||||||
|
|
||||||
NOTE: charts in this repo deliberately have no dependencies, so this helper is
|
NOTE: like the resources helper above, this is duplicated into every chart's
|
||||||
duplicated into every chart's templates/_helpers.tpl with the chart's name as
|
templates/_helpers.tpl with the chart's name as define prefix. The reference
|
||||||
define prefix. The reference implementation lives in template-chart.
|
implementation lives in template-chart.
|
||||||
|
|
||||||
This chart is a stage-1 import (issue #67) and does not yet follow the repo
|
|
||||||
conventions; only the replicas helper was taken over, because the scaleDown
|
|
||||||
flag is needed before the Fleet migration. The resources helper follows in
|
|
||||||
stage 2 together with the rest of the convention alignment.
|
|
||||||
*/}}
|
*/}}
|
||||||
{{- define "minecraft-server.replicas" -}}
|
{{- define "minecraft-server.replicas" -}}
|
||||||
{{- if .scaleDown -}}
|
{{- if .scaleDown -}}
|
||||||
@@ -33,3 +137,22 @@ stage 2 together with the rest of the convention alignment.
|
|||||||
1
|
1
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Guard: curseforge, ftb and curseforgeServerpack are mutually exclusive.
|
||||||
|
|
||||||
|
Enabling more than one used to render a duplicate TYPE env entry, where the
|
||||||
|
last one silently won. Fail loudly instead.
|
||||||
|
*/}}
|
||||||
|
{{- define "minecraft-server.validateModpackSources" -}}
|
||||||
|
{{- $enabled := list -}}
|
||||||
|
{{- if .Values.curseforge.enabled -}}{{- $enabled = append $enabled "curseforge" -}}{{- end -}}
|
||||||
|
{{- if .Values.ftb.enabled -}}{{- $enabled = append $enabled "ftb" -}}{{- end -}}
|
||||||
|
{{- if .Values.curseforgeServerpack.enabled -}}{{- $enabled = append $enabled "curseforgeServerpack" -}}{{- end -}}
|
||||||
|
{{- if gt (len $enabled) 1 -}}
|
||||||
|
{{- fail (printf "minecraft-server: curseforge, ftb and curseforgeServerpack are mutually exclusive, but these are enabled at the same time: %s - enable at most one modpack source" (join ", " $enabled)) -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- if and .Values.curseforgeServerpack.enabled (not .Values.curseforgeServerpack.downloadUrl) -}}
|
||||||
|
{{- fail "minecraft-server: curseforgeServerpack.enabled is true but curseforgeServerpack.downloadUrl is not set - the init container would download an empty URL" -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-mc-volume-claim
|
|
||||||
spec:
|
|
||||||
accessModes:
|
|
||||||
- ReadWriteOnce
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
storage: {{ .Values.minecraft.resources.storage }}
|
|
||||||
storageClassName: longhorn
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-service
|
|
||||||
{{- if .Values.mcRouterIntegration.enabled }}
|
|
||||||
annotations:
|
|
||||||
"mc-router.itzg.me/externalServerName": "{{ .Values.mcRouterIntegration.domain }}"
|
|
||||||
{{- end }}
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: {{ .Release.Name }}-server-deployment
|
|
||||||
{{- if .Values.mcRouterIntegration.enabled }}
|
|
||||||
ports:
|
|
||||||
- name: mc-port
|
|
||||||
port: 25565
|
|
||||||
targetPort: 25565
|
|
||||||
type: ClusterIP
|
|
||||||
{{- else }}
|
|
||||||
ports:
|
|
||||||
- nodePort: {{ .Values.server.outwardPort }}
|
|
||||||
port: 25565
|
|
||||||
targetPort: 25565
|
|
||||||
type: NodePort
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
+9
-2
@@ -1,9 +1,10 @@
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ .Release.Name }}-init-config
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-configmap
|
||||||
data:
|
data:
|
||||||
init.sh: |
|
init.sh: |
|
||||||
|
set -e
|
||||||
echo "downloading serverpack"
|
echo "downloading serverpack"
|
||||||
wget -O /modpacks/serverpack.zip {{ .Values.curseforgeServerpack.downloadUrl }}
|
wget -O /modpacks/serverpack.zip {{ .Values.curseforgeServerpack.downloadUrl }}
|
||||||
SERVERPACK_DIR="{{ .Values.curseforgeServerpack.workingDirectory }}"
|
SERVERPACK_DIR="{{ .Values.curseforgeServerpack.workingDirectory }}"
|
||||||
@@ -32,8 +33,14 @@ data:
|
|||||||
cp $BACKUP_DIR/ops.json $SERVERPACK_DIR/ops.json
|
cp $BACKUP_DIR/ops.json $SERVERPACK_DIR/ops.json
|
||||||
cp $BACKUP_DIR/whitelist.json $SERVERPACK_DIR/whitelist.json
|
cp $BACKUP_DIR/whitelist.json $SERVERPACK_DIR/whitelist.json
|
||||||
|
|
||||||
|
# The init container runs as the same uid as the server (see
|
||||||
|
# securityContext.pod) and cp creates the copies owned by that uid, so
|
||||||
|
# this is a no-op in the normal case. It is kept for volumes whose files
|
||||||
|
# still carry a foreign owner from an older chart version, and it must
|
||||||
|
# not be fatal: an unprivileged process cannot chown foreign files, and
|
||||||
|
# with "set -e" a failure here would keep the whole pod from starting.
|
||||||
echo "change owner of files"
|
echo "change owner of files"
|
||||||
chown -R 1000:1000 $SERVERPACK_DIR
|
chown -R 1000:1000 $SERVERPACK_DIR || echo "chown skipped (not permitted); continuing"
|
||||||
|
|
||||||
echo "successfully migrated server"
|
echo "successfully migrated server"
|
||||||
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
# Deliberate exception from the release-chart-kind naming scheme: this chart
|
||||||
|
# is already deployed with 32Gi worlds on these claims, and renaming a PVC
|
||||||
|
# makes Kubernetes provision a new, empty volume and orphan the existing
|
||||||
|
# data (see README, naming exceptions).
|
||||||
|
name: {{ .Release.Name }}-mc-volume-claim
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: {{ .Values.minecraft.resources.storage }}
|
||||||
|
storageClassName: {{ .Values.minecraft.resources.storageClassName }}
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
apiVersion: onepassword.com/v1
|
apiVersion: onepassword.com/v1
|
||||||
kind: OnePasswordItem
|
kind: OnePasswordItem
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ .Release.Name }}-mc-secret
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-secret
|
||||||
spec:
|
spec:
|
||||||
itemPath: {{ .Values.secrets.minecraftServerRef }}
|
itemPath: {{ .Values.secrets.minecraftServerRef }}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-service
|
||||||
|
labels:
|
||||||
|
app: {{ .Release.Name }}-{{ .Chart.Name }}-service
|
||||||
|
spec:
|
||||||
|
type: {{ .Values.server.service.type }}
|
||||||
|
selector:
|
||||||
|
app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
||||||
|
ports:
|
||||||
|
- name: mc-port
|
||||||
|
protocol: TCP
|
||||||
|
port: {{ .Values.server.service.port }}
|
||||||
|
# Named target, so a server.port override cannot break the routing.
|
||||||
|
# Before chart 1.0.0 this was the number 25565 while the container
|
||||||
|
# declared port 35565 - the mismatch went unnoticed precisely because
|
||||||
|
# the target was numeric.
|
||||||
|
targetPort: mc-port
|
||||||
|
{{- if eq .Values.server.service.type "NodePort" }}
|
||||||
|
nodePort: {{ .Values.server.service.outwardPort }}
|
||||||
|
{{- end }}
|
||||||
+95
-46
@@ -1,32 +1,62 @@
|
|||||||
|
{{- include "minecraft-server.validateModpackSources" . }}
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: StatefulSet
|
kind: StatefulSet
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ .Release.Name }}-server-deployment
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
||||||
labels:
|
labels:
|
||||||
app: {{ .Release.Name }}
|
app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
||||||
spec:
|
spec:
|
||||||
replicas: {{ include "minecraft-server.replicas" (dict "scaleDown" .Values.scaleDown "replicas" .Values.server.replicas) }}
|
replicas: {{ include "minecraft-server.replicas" (dict "scaleDown" .Values.scaleDown "replicas" .Values.server.replicas) }}
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app: {{ .Release.Name }}-server-deployment
|
app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
||||||
|
serviceName: {{ .Release.Name }}-{{ .Chart.Name }}-service
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ .Release.Name }}-server-deployment
|
|
||||||
labels:
|
labels:
|
||||||
app: {{ .Release.Name }}-server-deployment
|
app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
||||||
spec:
|
spec:
|
||||||
automountServiceAccountToken: false
|
automountServiceAccountToken: false
|
||||||
|
securityContext:
|
||||||
|
{{- toYaml .Values.securityContext.pod | nindent 8 }}
|
||||||
|
{{- if .Values.curseforgeServerpack.enabled }}
|
||||||
|
initContainers:
|
||||||
|
- name: download-serverpack
|
||||||
|
image: {{ .Values.server.initImage }}
|
||||||
|
command: [ 'sh', '/init/init.sh' ]
|
||||||
|
securityContext:
|
||||||
|
{{- toYaml .Values.securityContext.container | nindent 12 }}
|
||||||
|
volumeMounts:
|
||||||
|
- name: "mc-volume"
|
||||||
|
mountPath: /modpacks
|
||||||
|
subPath: mc-modpacks/
|
||||||
|
- name: "mc-volume"
|
||||||
|
mountPath: /data
|
||||||
|
subPath: mc-data/
|
||||||
|
- name: init-script
|
||||||
|
mountPath: /init/init.sh
|
||||||
|
subPath: init.sh
|
||||||
|
- name: tmp
|
||||||
|
mountPath: /tmp
|
||||||
|
{{- end }}
|
||||||
containers:
|
containers:
|
||||||
- name: {{ .Release.Name }}-mc-server-container
|
- name: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
||||||
image: "itzg/minecraft-server:{{ .Chart.AppVersion }}"
|
image: "itzg/minecraft-server:{{ .Chart.AppVersion }}"
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
|
securityContext:
|
||||||
|
{{- toYaml .Values.securityContext.container | nindent 12 }}
|
||||||
ports:
|
ports:
|
||||||
- name: mc-port
|
- name: mc-port
|
||||||
containerPort: 35565
|
containerPort: {{ .Values.server.port }}
|
||||||
|
protocol: TCP
|
||||||
|
{{- if .Values.server.rcon.enabled }}
|
||||||
- name: minecraft-rcon
|
- name: minecraft-rcon
|
||||||
containerPort: 25575
|
containerPort: {{ .Values.server.rcon.port }}
|
||||||
|
protocol: TCP
|
||||||
|
{{- end }}
|
||||||
- name: metrics
|
- name: metrics
|
||||||
containerPort: 19565
|
containerPort: 19565
|
||||||
|
protocol: TCP
|
||||||
env:
|
env:
|
||||||
- name: EULA
|
- name: EULA
|
||||||
value: "true"
|
value: "true"
|
||||||
@@ -36,21 +66,22 @@ spec:
|
|||||||
value: "{{ .Values.server.motd }}"
|
value: "{{ .Values.server.motd }}"
|
||||||
- name: DIFFICULTY
|
- name: DIFFICULTY
|
||||||
value: {{ .Values.minecraft.difficulty }}
|
value: {{ .Values.minecraft.difficulty }}
|
||||||
|
- name: SERVER_PORT
|
||||||
|
value: "{{ .Values.server.port }}"
|
||||||
{{- if .Values.minecraft.whitelist.enabled }}
|
{{- if .Values.minecraft.whitelist.enabled }}
|
||||||
- name: EXISTING_WHITELIST_FILE
|
- name: EXISTING_WHITELIST_FILE
|
||||||
value: {{ .Values.minecraft.whitelist.overridePolicy }}
|
value: {{ .Values.minecraft.whitelist.overridePolicy }}
|
||||||
|
{{- with .Values.minecraft.whitelist.players }}
|
||||||
- name: WHITELIST
|
- name: WHITELIST
|
||||||
value: {{ .Values.minecraft.whitelist.players }}
|
value: "{{ . }}"
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.minecraft.management.opPlayers }}
|
||||||
- name: OPS
|
- name: OPS
|
||||||
value: {{ .Values.minecraft.management.opPlayers }}
|
value: "{{ . }}"
|
||||||
|
{{- end }}
|
||||||
- name: EXISTING_OPS_FILE
|
- name: EXISTING_OPS_FILE
|
||||||
value: "SYNCHRONIZE"
|
value: "SYNCHRONIZE"
|
||||||
- name: RCON_PASSWORD
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
key: rcon-password
|
|
||||||
name: {{ .Release.Name }}-mc-secret
|
|
||||||
- name: MAX_PLAYERS
|
- name: MAX_PLAYERS
|
||||||
value: "{{ .Values.minecraft.management.maxPlayers }}"
|
value: "{{ .Values.minecraft.management.maxPlayers }}"
|
||||||
- name: FORCE_GAMEMODE
|
- name: FORCE_GAMEMODE
|
||||||
@@ -66,7 +97,16 @@ spec:
|
|||||||
- name: VERSION
|
- name: VERSION
|
||||||
value: "{{ .Values.minecraft.version }}"
|
value: "{{ .Values.minecraft.version }}"
|
||||||
- name: ENABLE_RCON
|
- name: ENABLE_RCON
|
||||||
value: "false"
|
value: "{{ .Values.server.rcon.enabled }}"
|
||||||
|
{{- if .Values.server.rcon.enabled }}
|
||||||
|
- name: RCON_PORT
|
||||||
|
value: "{{ .Values.server.rcon.port }}"
|
||||||
|
- name: RCON_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
key: rcon-password
|
||||||
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-secret
|
||||||
|
{{- end }}
|
||||||
- name: ENABLE_AUTOPAUSE
|
- name: ENABLE_AUTOPAUSE
|
||||||
value: "false"
|
value: "false"
|
||||||
- name: MEMORY
|
- name: MEMORY
|
||||||
@@ -78,7 +118,7 @@ spec:
|
|||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
key: curseforge-api-key
|
key: curseforge-api-key
|
||||||
name: {{ .Release.Name }}-mc-secret
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-secret
|
||||||
- name: CF_SLUG
|
- name: CF_SLUG
|
||||||
value: "{{ .Values.curseforge.modpack.slug }}"
|
value: "{{ .Values.curseforge.modpack.slug }}"
|
||||||
- name: CF_FILE_ID
|
- name: CF_FILE_ID
|
||||||
@@ -108,15 +148,34 @@ spec:
|
|||||||
- name: FTB_MODPACK_VERSION_ID
|
- name: FTB_MODPACK_VERSION_ID
|
||||||
value: "{{ .Values.ftb.versionId }}"
|
value: "{{ .Values.ftb.versionId }}"
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- range $value := .Values.env }}
|
||||||
|
{{- if $value.value }}
|
||||||
|
- name: {{ $value.name }}
|
||||||
|
value: {{ tpl $value.value $ }}
|
||||||
|
{{- else if $value.valueFrom }}
|
||||||
|
{{- if $value.valueFrom.secretKeyRef }}
|
||||||
|
- name: {{ $value.name }}
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ tpl $value.valueFrom.secretKeyRef.name $ }}
|
||||||
|
key: {{ tpl $value.valueFrom.secretKeyRef.key $ }}
|
||||||
|
{{- else if $value.valueFrom.configMapKeyRef }}
|
||||||
|
- name: {{ $value.name }}
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ tpl $value.valueFrom.configMapKeyRef.name $ }}
|
||||||
|
key: {{ tpl $value.valueFrom.configMapKeyRef.key $ }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
livenessProbe:
|
||||||
|
{{- toYaml .Values.livenessProbe | nindent 12 }}
|
||||||
|
readinessProbe:
|
||||||
|
{{- toYaml .Values.readinessProbe | nindent 12 }}
|
||||||
|
startupProbe:
|
||||||
|
{{- toYaml .Values.startupProbe | nindent 12 }}
|
||||||
resources:
|
resources:
|
||||||
requests:
|
{{- include "minecraft-server.serverResources" . | nindent 12 }}
|
||||||
cpu: "2"
|
|
||||||
memory: "4Gi"
|
|
||||||
ephemeral-storage: "100Mi"
|
|
||||||
limits:
|
|
||||||
cpu: "4"
|
|
||||||
memory: "12Gi"
|
|
||||||
ephemeral-storage: "200Mi"
|
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: "mc-volume"
|
- name: "mc-volume"
|
||||||
mountPath: /data
|
mountPath: /data
|
||||||
@@ -124,30 +183,20 @@ spec:
|
|||||||
- name: "mc-volume"
|
- name: "mc-volume"
|
||||||
mountPath: /modpacks
|
mountPath: /modpacks
|
||||||
subPath: mc-modpacks/
|
subPath: mc-modpacks/
|
||||||
{{- if .Values.curseforgeServerpack }}
|
# Writable path required by readOnlyRootFilesystem: the image writes
|
||||||
initContainers:
|
# its server-icon scratch file and mc-server-runner's console named
|
||||||
- name: download-serverpack
|
# pipe to /tmp. Everything else goes to the PVC at /data.
|
||||||
image: alpine
|
- name: tmp
|
||||||
command: [ 'sh', '/init/init.sh' ]
|
mountPath: /tmp
|
||||||
volumeMounts:
|
|
||||||
- name: "mc-volume"
|
|
||||||
mountPath: /modpacks
|
|
||||||
subPath: mc-modpacks/
|
|
||||||
- name: "mc-volume"
|
|
||||||
mountPath: /data
|
|
||||||
subPath: mc-data/
|
|
||||||
- name: init-script
|
|
||||||
mountPath: /init/init.sh
|
|
||||||
subPath: init.sh
|
|
||||||
{{- end }}
|
|
||||||
restartPolicy: Always
|
|
||||||
volumes:
|
volumes:
|
||||||
- name: "mc-volume"
|
- name: "mc-volume"
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
|
# Deliberate exception from the naming scheme: renaming the PVC
|
||||||
|
# would make Kubernetes provision a new, empty volume and orphan
|
||||||
|
# the existing world (see README, naming exceptions).
|
||||||
claimName: {{ .Release.Name }}-mc-volume-claim
|
claimName: {{ .Release.Name }}-mc-volume-claim
|
||||||
- name: init-script
|
- name: init-script
|
||||||
configMap:
|
configMap:
|
||||||
name: {{ .Release.Name }}-init-config
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-configmap
|
||||||
serviceName: {{ .Release.Name }}-service
|
- name: tmp
|
||||||
|
emptyDir: {}
|
||||||
|
|
||||||
@@ -0,0 +1,318 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "minecraft-server values",
|
||||||
|
"description": "Strict values schema (issue #68 pilot). additionalProperties is false on every chart-owned object level, so a key that the chart does not read is rejected at render time instead of being silently ignored. The only objects that stay open are the ones passed verbatim into the Kubernetes pod spec (probes, security contexts, global) - the chart does not interpret their contents, so it cannot judge which keys are meaningful.",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"global": {
|
||||||
|
"description": "Helm's conventional cross-chart values. Unused by this chart, kept open so an umbrella install cannot break on it.",
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"scaleDown": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"secrets": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"minecraftServerRef": {
|
||||||
|
"type": ["string", "null"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"server": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"replicas": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 65535
|
||||||
|
},
|
||||||
|
"service": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["ClusterIP", "NodePort"]
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 65535
|
||||||
|
},
|
||||||
|
"outwardPort": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 65535
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"motd": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"rcon": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"enabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 65535
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"initImage": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"resources": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"limitFactor": {
|
||||||
|
"type": "number",
|
||||||
|
"exclusiveMinimum": 0
|
||||||
|
},
|
||||||
|
"requests": {
|
||||||
|
"$ref": "#/definitions/resourceList"
|
||||||
|
},
|
||||||
|
"limits": {
|
||||||
|
"$ref": "#/definitions/resourceList"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["name"],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"valueFrom": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"secretKeyRef": {
|
||||||
|
"$ref": "#/definitions/keyRef"
|
||||||
|
},
|
||||||
|
"configMapKeyRef": {
|
||||||
|
"$ref": "#/definitions/keyRef"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"livenessProbe": {
|
||||||
|
"$ref": "#/definitions/probe"
|
||||||
|
},
|
||||||
|
"readinessProbe": {
|
||||||
|
"$ref": "#/definitions/probe"
|
||||||
|
},
|
||||||
|
"startupProbe": {
|
||||||
|
"$ref": "#/definitions/probe"
|
||||||
|
},
|
||||||
|
"securityContext": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"pod": {
|
||||||
|
"description": "Rendered verbatim as the pod's securityContext; kept open because the chart does not interpret it.",
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"container": {
|
||||||
|
"description": "Rendered verbatim as the container's securityContext; kept open because the chart does not interpret it.",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"difficulty": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["peaceful", "easy", "normal", "hard"]
|
||||||
|
},
|
||||||
|
"whitelist": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"enabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"overridePolicy": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["SKIP", "MERGE", "SYNCHRONIZE"]
|
||||||
|
},
|
||||||
|
"players": {
|
||||||
|
"type": ["string", "null"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"management": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"opPlayers": {
|
||||||
|
"type": ["string", "null"]
|
||||||
|
},
|
||||||
|
"maxPlayers": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1
|
||||||
|
},
|
||||||
|
"forceGameMode": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"gameMode": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["creative", "survival", "adventure", "spectator"]
|
||||||
|
},
|
||||||
|
"worldSaveName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"serverName": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resources": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"storage": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"storageClassName": {
|
||||||
|
"type": ["string", "null"]
|
||||||
|
},
|
||||||
|
"memory": {
|
||||||
|
"description": "JVM heap in gigabytes, passed to the image as MEMORY with a G suffix appended.",
|
||||||
|
"type": ["string", "number"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": ["string", "number"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"curseforge": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"enabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"forceReload": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"modpack": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"slug": {
|
||||||
|
"type": ["string", "null"]
|
||||||
|
},
|
||||||
|
"versionId": {
|
||||||
|
"type": ["string", "integer", "null"]
|
||||||
|
},
|
||||||
|
"exclude": {
|
||||||
|
"type": ["string", "integer", "null"]
|
||||||
|
},
|
||||||
|
"excludeOverride": {
|
||||||
|
"type": ["string", "integer", "null"]
|
||||||
|
},
|
||||||
|
"include": {
|
||||||
|
"type": ["string", "integer", "null"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ftb": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"enabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"modpackId": {
|
||||||
|
"type": ["string", "integer", "null"]
|
||||||
|
},
|
||||||
|
"versionId": {
|
||||||
|
"type": ["string", "integer", "null"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"curseforgeServerpack": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"enabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"downloadUrl": {
|
||||||
|
"type": ["string", "null"]
|
||||||
|
},
|
||||||
|
"workingDirectory": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"resourceList": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"cpu": {
|
||||||
|
"type": ["string", "number", "null"]
|
||||||
|
},
|
||||||
|
"memory": {
|
||||||
|
"type": ["string", "number", "null"]
|
||||||
|
},
|
||||||
|
"ephemeral-storage": {
|
||||||
|
"type": ["string", "number", "null"]
|
||||||
|
},
|
||||||
|
"ephemeralStorage": {
|
||||||
|
"type": ["string", "number", "null"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"keyRef": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["name", "key"],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"key": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"probe": {
|
||||||
|
"description": "Rendered verbatim as a container probe; kept open because the chart does not interpret it.",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,23 +5,163 @@
|
|||||||
scaleDown: false
|
scaleDown: false
|
||||||
|
|
||||||
secrets:
|
secrets:
|
||||||
|
# op:// item path for the 1Password operator. Only needs to carry a
|
||||||
|
# "curseforge-api-key" key when curseforge.enabled is true and an
|
||||||
|
# "rcon-password" key when server.rcon.enabled is true.
|
||||||
minecraftServerRef: null
|
minecraftServerRef: null
|
||||||
|
|
||||||
server:
|
server:
|
||||||
# Replica count for this workload; 0 is allowed and scales it down.
|
# Replica count for this workload; 0 is allowed and scales it down.
|
||||||
# The chart-global scaleDown flag takes precedence.
|
# The chart-global scaleDown flag takes precedence.
|
||||||
replicas: 1
|
replicas: 1
|
||||||
outwardPort: 30565
|
|
||||||
|
# Port the Minecraft server listens on inside the container. It is passed to
|
||||||
|
# the image as SERVER_PORT, used as the containerPort and as the Service's
|
||||||
|
# target, and the probes go through the image's mc-health script, which reads
|
||||||
|
# SERVER_PORT too - so an override stays consistent everywhere.
|
||||||
|
port: 25565
|
||||||
|
|
||||||
|
service:
|
||||||
|
# ClusterIP is the default because routing to these servers is done by the
|
||||||
|
# static mappings of the minecraft-router chart, which reaches the server
|
||||||
|
# over its in-cluster DNS name. Set NodePort to expose the server directly
|
||||||
|
# on a node port instead (then outwardPort applies).
|
||||||
|
# NOTE: before chart 1.0.0 this was implied by "mcRouterIntegration.enabled",
|
||||||
|
# which also added a misleading mc-router auto-discovery annotation. The
|
||||||
|
# annotation is gone; only the service type remains, now named for what it
|
||||||
|
# actually does.
|
||||||
|
type: ClusterIP
|
||||||
|
# Port the Service exposes in the cluster.
|
||||||
|
port: 25565
|
||||||
|
# Node port used only when type is NodePort. Must be inside the cluster's
|
||||||
|
# node-port range (30000-32767 by default).
|
||||||
|
outwardPort: 30565
|
||||||
|
|
||||||
|
# The \u00A7 sequences are Java-properties escapes for the Minecraft colour
|
||||||
|
# code character and must stay literal backslash sequences - server.properties
|
||||||
|
# is a Java properties file and unescapes them itself.
|
||||||
motd: 'Running \u00A72\u00A7o%MODPACK_NAME% \u00A70version \u00A7r%env:MODPACK_VERSION%'
|
motd: 'Running \u00A72\u00A7o%MODPACK_NAME% \u00A70version \u00A7r%env:MODPACK_VERSION%'
|
||||||
|
|
||||||
|
rcon:
|
||||||
|
# RCON is off by default. Only when this is true does the chart set
|
||||||
|
# ENABLE_RCON, expose the RCON container port and wire RCON_PASSWORD from
|
||||||
|
# the secret - before chart 1.0.0 the password was pulled from the secret
|
||||||
|
# unconditionally although RCON was hardcoded off, which made the secret
|
||||||
|
# key mandatory for a switched-off feature.
|
||||||
|
enabled: false
|
||||||
|
port: 25575
|
||||||
|
|
||||||
|
# Init container that downloads and unpacks a CurseForge server pack; only
|
||||||
|
# used when curseforgeServerpack.enabled is true. Pinned per repo convention
|
||||||
|
# (auxiliary images are never unpinned): before chart 1.0.0 this was a bare
|
||||||
|
# "alpine", i.e. an implicit :latest from Docker Hub.
|
||||||
|
initImage: alpine:3.22
|
||||||
|
|
||||||
|
resources:
|
||||||
|
# Limits are optional: memory and ephemeral-storage limits default to
|
||||||
|
# limitFactor x the request, an explicitly set limit always wins, and a
|
||||||
|
# CPU limit is only set when configured explicitly under "limits"
|
||||||
|
# (CPU is compressible - throttling is preferable to an OOM kill).
|
||||||
|
limitFactor: 3
|
||||||
|
requests:
|
||||||
|
cpu: "2"
|
||||||
|
ephemeral-storage: "100Mi"
|
||||||
|
# Deliberately unset: when no memory request is given, the chart derives
|
||||||
|
# it from minecraft.resources.memory (the JVM heap) so that the heap and
|
||||||
|
# the container's memory request/limit can never drift apart. With the
|
||||||
|
# default heap of 4G this yields a 4Gi request and, via limitFactor 3, a
|
||||||
|
# 12Gi limit - exactly the values that were hardcoded before chart 1.0.0.
|
||||||
|
# Set it explicitly to decouple the two.
|
||||||
|
memory: null
|
||||||
|
|
||||||
|
# Extra environment variables for the server container, appended to the ones
|
||||||
|
# the chart always sets. Values are rendered through tpl and support the value,
|
||||||
|
# secretKeyRef and configMapKeyRef forms:
|
||||||
|
# - name: SOME_ENV
|
||||||
|
# value: "{{ .Release.Name }}"
|
||||||
|
# - name: FROM_SECRET
|
||||||
|
# valueFrom:
|
||||||
|
# secretKeyRef:
|
||||||
|
# name: "some-secret"
|
||||||
|
# key: "someKey"
|
||||||
|
# - name: FROM_CONFIGMAP
|
||||||
|
# valueFrom:
|
||||||
|
# configMapKeyRef:
|
||||||
|
# name: "some-configmap"
|
||||||
|
# key: "someKey"
|
||||||
|
env: []
|
||||||
|
|
||||||
|
# All three probes run the image's own mc-health script, which performs a real
|
||||||
|
# Minecraft server-list ping against SERVER_PORT via mc-monitor - so "Ready"
|
||||||
|
# means the server answers players, not merely that the container process is
|
||||||
|
# alive. (Before chart 1.0.0 this chart had no probes at all, which is why a
|
||||||
|
# pod counted as Ready the moment the container started.)
|
||||||
|
livenessProbe:
|
||||||
|
exec:
|
||||||
|
command: ["mc-health"]
|
||||||
|
periodSeconds: 30
|
||||||
|
timeoutSeconds: 10
|
||||||
|
failureThreshold: 3
|
||||||
|
readinessProbe:
|
||||||
|
exec:
|
||||||
|
command: ["mc-health"]
|
||||||
|
periodSeconds: 15
|
||||||
|
timeoutSeconds: 10
|
||||||
|
failureThreshold: 3
|
||||||
|
# The startup probe gates liveness and readiness, so its budget has to cover
|
||||||
|
# the slowest legitimate start: on a fresh volume the image downloads the
|
||||||
|
# server jar and, for FTB/CurseForge modpacks, the whole modpack before the
|
||||||
|
# world even begins to load. Budget 15s * 80 = 20 minutes; the liveness probe
|
||||||
|
# only starts counting afterwards, so a slow start can no longer be killed.
|
||||||
|
startupProbe:
|
||||||
|
exec:
|
||||||
|
command: ["mc-health"]
|
||||||
|
periodSeconds: 15
|
||||||
|
timeoutSeconds: 10
|
||||||
|
failureThreshold: 80
|
||||||
|
|
||||||
|
securityContext:
|
||||||
|
pod:
|
||||||
|
runAsNonRoot: true
|
||||||
|
# uid/gid 1000 is the image's "minecraft" user. The image's entrypoint
|
||||||
|
# normally starts as root and drops to this user via gosu; starting as the
|
||||||
|
# user directly makes it take the non-root branch, which additionally skips
|
||||||
|
# the entrypoint's recursive chown of /data and its write to
|
||||||
|
# /etc/nsswitch.conf - the latter would fail on a read-only root filesystem.
|
||||||
|
runAsUser: 1000
|
||||||
|
runAsGroup: 1000
|
||||||
|
# fsGroup keeps the world volume writable for the minecraft user.
|
||||||
|
# OnRootMismatch (instead of Always) avoids a recursive chown of the whole
|
||||||
|
# world on every single pod start - on a 32Gi world that is minutes of
|
||||||
|
# startup time. It still runs once if the volume root does not already
|
||||||
|
# carry the group.
|
||||||
|
fsGroup: 1000
|
||||||
|
fsGroupChangePolicy: OnRootMismatch
|
||||||
|
container:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
# Works because the chart mounts an emptyDir at /tmp - the image writes the
|
||||||
|
# server-icon scratch file and mc-server-runner's console named pipe there.
|
||||||
|
# Everything else the server writes goes to the PVC at /data.
|
||||||
|
readOnlyRootFilesystem: true
|
||||||
|
runAsNonRoot: true
|
||||||
|
capabilities:
|
||||||
|
drop:
|
||||||
|
- ALL
|
||||||
|
add: []
|
||||||
|
|
||||||
minecraft:
|
minecraft:
|
||||||
# Valid values are: peaceful, easy, normal, and hard
|
# Valid values are: peaceful, easy, normal, and hard
|
||||||
difficulty: "easy"
|
difficulty: "easy"
|
||||||
whitelist:
|
whitelist:
|
||||||
enabled: "true"
|
# Boolean. Before chart 1.0.0 this was the string "true" and was evaluated
|
||||||
|
# with a plain truthiness test, so even "false" enabled the block.
|
||||||
|
enabled: true
|
||||||
overridePolicy: "MERGE"
|
overridePolicy: "MERGE"
|
||||||
# Comma-separated List of Players
|
# Comma-separated List of Players
|
||||||
players: null
|
players: null
|
||||||
management:
|
management:
|
||||||
# Comma-separated List of Players
|
# Comma-separated List of Players. The image resolves every name against
|
||||||
|
# the external Playerdb API and aborts startup if one does not resolve.
|
||||||
opPlayers: null
|
opPlayers: null
|
||||||
maxPlayers: 10
|
maxPlayers: 10
|
||||||
forceGameMode: true
|
forceGameMode: true
|
||||||
@@ -31,8 +171,25 @@ minecraft:
|
|||||||
serverName: mc-server
|
serverName: mc-server
|
||||||
resources:
|
resources:
|
||||||
storage: "32Gi"
|
storage: "32Gi"
|
||||||
|
# Storage class of the world PVC. Was hardcoded to "longhorn" before chart
|
||||||
|
# 1.0.0. Changing it on an existing release has no effect on the already
|
||||||
|
# bound volume - storageClassName is immutable once the PVC exists.
|
||||||
|
storageClassName: longhorn
|
||||||
|
# JVM heap in gigabytes (passed to the image as MEMORY). Also drives the
|
||||||
|
# container's memory request/limit unless server.resources.requests.memory
|
||||||
|
# is set explicitly.
|
||||||
memory: "4"
|
memory: "4"
|
||||||
version: "LATEST"
|
# Minecraft version to run. Pinned rather than "LATEST", which would silently
|
||||||
|
# drift to a new release on every restart.
|
||||||
|
# This value must match the Java line of the chart version: the image tag
|
||||||
|
# comes from .Chart.AppVersion, so the JVM is fixed per published chart
|
||||||
|
# version. Java 8 runs Minecraft up to 1.16.5, 1.18-1.20.4 need Java 17 and
|
||||||
|
# 1.20.5+ need Java 21. This chart publishes appVersion "java8".
|
||||||
|
version: "1.12.2"
|
||||||
|
|
||||||
|
# curseforge, ftb and curseforgeServerpack are mutually exclusive - enabling
|
||||||
|
# more than one is rejected by the chart (before 1.0.0 it silently rendered a
|
||||||
|
# duplicate TYPE env entry and the last one won).
|
||||||
curseforge:
|
curseforge:
|
||||||
enabled: false
|
enabled: false
|
||||||
forceReload: false
|
forceReload: false
|
||||||
@@ -50,6 +207,3 @@ curseforgeServerpack:
|
|||||||
enabled: false
|
enabled: false
|
||||||
downloadUrl: null
|
downloadUrl: null
|
||||||
workingDirectory: '/data/serverpack'
|
workingDirectory: '/data/serverpack'
|
||||||
mcRouterIntegration:
|
|
||||||
enabled: false
|
|
||||||
domain: null
|
|
||||||
Reference in new issue
Block a user