From 63b273feb57824f106a6d577cceb5e6964ae4a95 Mon Sep 17 00:00:00 2001 From: Chris Fordham Date: Sat, 16 Jan 2021 07:54:17 +1100 Subject: [PATCH] Support for arm64, k8s, helm (#1552) * Initial helm chart, docker for arm64. * Add missing slash to local helm values file in gitignore. * Add in support to provide loadBalancerIP by helm value. * Ports should not be set on the service if using default ClusterIP svc type. * Revert svc condition. * Remove condition on service ports. * Add in nodePort key as a value configurable. * Provide a default nodePort value. * Make nodePort conditional. --- .gitignore | 3 + Dockerfile.arm64 | 33 +++++++ Makefile | 85 +++++++++++++++++++ README.md | 47 +++++++++- charts/nukkit/.helmignore | 23 +++++ charts/nukkit/Chart.yaml | 6 ++ charts/nukkit/templates/NOTES.txt | 15 ++++ charts/nukkit/templates/_helpers.tpl | 63 ++++++++++++++ charts/nukkit/templates/configmap.yaml | 14 +++ charts/nukkit/templates/deployment.yaml | 70 +++++++++++++++ charts/nukkit/templates/hpa.yaml | 28 ++++++ charts/nukkit/templates/service.yaml | 21 +++++ charts/nukkit/templates/serviceaccount.yaml | 12 +++ .../templates/tests/test-connection.yaml | 15 ++++ charts/nukkit/values.yaml | 74 ++++++++++++++++ nukkit.yml.default | 4 + 16 files changed, 512 insertions(+), 1 deletion(-) create mode 100644 Dockerfile.arm64 create mode 100644 Makefile create mode 100644 charts/nukkit/.helmignore create mode 100644 charts/nukkit/Chart.yaml create mode 100644 charts/nukkit/templates/NOTES.txt create mode 100644 charts/nukkit/templates/_helpers.tpl create mode 100644 charts/nukkit/templates/configmap.yaml create mode 100644 charts/nukkit/templates/deployment.yaml create mode 100644 charts/nukkit/templates/hpa.yaml create mode 100644 charts/nukkit/templates/service.yaml create mode 100644 charts/nukkit/templates/serviceaccount.yaml create mode 100644 charts/nukkit/templates/tests/test-connection.yaml create mode 100644 charts/nukkit/values.yaml create mode 100644 nukkit.yml.default diff --git a/.gitignore b/.gitignore index 6b5cd202ba8..543cd13b686 100644 --- a/.gitignore +++ b/.gitignore @@ -232,3 +232,6 @@ creativeitems.json recipes.json data/ data/* + +# a file that can be used for your helm chart values +/helm-values.local.yaml diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 new file mode 100644 index 00000000000..045f840c8e0 --- /dev/null +++ b/Dockerfile.arm64 @@ -0,0 +1,33 @@ +FROM arm64v8/openjdk:8-jdk-slim AS build +WORKDIR /usr/local/src/nukkit +COPY src /usr/local/src/nukkit/src +COPY mvn* pom.xml /usr/local/src/nukkit/ +COPY .git /usr/local/src/nukkit/.git +COPY .mvn /usr/local/src/nukkit/.mvn +COPY .gitmodules /usr/local/src/nukkit/.gitmodules +RUN apt-get -y update && \ + apt-get install -y build-essential git maven && \ + git submodule update --init && \ + mvn clean package + +FROM arm64v8/openjdk:8-jre-slim AS run +LABEL maintainer="Chris Fordham " +COPY --from=build /usr/local/src/nukkit/target/nukkit-1.0-SNAPSHOT.jar /opt/nukkit/nukkit.jar +COPY nukkit.yml.default /etc/opt/nukkit/nukkit.yml +RUN useradd --user-group \ + --no-create-home \ + --home-dir /var/opt/nukkit \ + --shell /usr/sbin/nologin \ + minecraft && \ + mkdir -p /var/opt/nukkit && \ + chown -R minecraft /opt/nukkit /var/opt/nukkit /etc/opt/nukkit/nukkit.yml && \ + ln -sfv /etc/opt/nukkit/nukkit.yml /var/opt/nukkit/nukkit.yml && \ + apt-get -y update && \ + apt-get -y install lsof && \ + rm -rf /var/lib/apt/lists/* +USER minecraft +VOLUME /etc/opt/nukkit /var/opt/nukkit /opt/nukkit +EXPOSE 19132 +WORKDIR /var/opt/nukkit +ENTRYPOINT ["java"] +CMD [ "-jar", "/opt/nukkit/nukkit.jar" ] diff --git a/Makefile b/Makefile new file mode 100644 index 00000000000..2bf78eea82f --- /dev/null +++ b/Makefile @@ -0,0 +1,85 @@ +DOCKER_REGISTRY = index.docker.io +IMAGE_NAME = nukkit +IMAGE_VERSION = latest +IMAGE_ORG = flaccid +IMAGE_TAG = $(DOCKER_REGISTRY)/$(IMAGE_ORG)/$(IMAGE_NAME):$(IMAGE_VERSION) +export DOCKER_BUILDKIT = 1 + +WORKING_DIR := $(shell pwd) + +.DEFAULT_GOAL := docker-build + +.PHONY: build run + +# TODO: finish +#build:: ## builds nukkit + +# TODO: finish +#run:: ## runs the binary + +docker-release:: docker-build docker-push ## builds and pushes the docker image to the registry +docker-release-arm64:: docker-build-arm64 docker-push-arm64 ## builds and pushes the arm64 docker image to the registry + +docker-push:: ## pushes the docker image to the registry + @docker push $(IMAGE_TAG) + +docker-push-arm64:: ## pushes the arm64 docker image to the registry + @docker push $(DOCKER_REGISTRY)/$(IMAGE_ORG)/$(IMAGE_NAME):arm64 + +docker-build:: ## builds the docker image locally + @echo http_proxy=$(HTTP_PROXY) http_proxy=$(HTTPS_PROXY) + @echo building $(IMAGE_TAG) + @docker build --pull \ + --build-arg=http_proxy=$(HTTP_PROXY) \ + --build-arg=https_proxy=$(HTTPS_PROXY) \ + -t $(IMAGE_TAG) $(WORKING_DIR) + +docker-build-arm64:: ## builds the arm64 docker image locally + @echo http_proxy=$(HTTP_PROXY) http_proxy=$(HTTPS_PROXY) + @echo building $(IMAGE_TAG) + @docker build --pull \ + --build-arg=http_proxy=$(HTTP_PROXY) \ + --build-arg=https_proxy=$(HTTPS_PROXY) \ + -f Dockerfile.arm64 \ + -t $(DOCKER_REGISTRY)/$(IMAGE_ORG)/$(IMAGE_NAME):arm64 $(WORKING_DIR) + +docker-run:: ## runs the docker image locally + @docker run \ + -it \ + -p 19132:19132 \ + $(DOCKER_REGISTRY)/$(IMAGE_ORG)/$(IMAGE_NAME):$(IMAGE_VERSION) + +helm-install:: ## installs using helm from chart in repo + @helm install --name nukkit ./charts/nukkit + +helm-upgrade:: ## upgrades deployed helm release + @helm upgrade nukkit ./charts/nukkit + +helm-purge:: ## deletes and purges deployed helm release + @helm delete --purge nukkit + +helm-render:: ## prints out the rendered chart + @helm install --dry-run --debug charts/nukkit + +helm-validate:: ## runs a lint on the helm chart + @helm lint charts/nukkit + +install-ghr:: ## installs ghr + @cd /tmp + @wget https://github.com/tcnksm/ghr/releases/download/v0.12.2/ghr_v0.12.2_linux_amd64.tar.gz + @tar zxvf ghr_v0.12.2_linux_amd64.tar.gz + @sudo mv ghr_v0.12.2_linux_amd64/ghr /usr/local/bin/ + +# a help target including self-documenting targets (see the awk statement) +define HELP_TEXT +Usage: make [TARGET]... [MAKEVAR1=SOMETHING]... + +Available targets: +endef +export HELP_TEXT +help: ## this help target + @cat .banner + @echo + @echo "$$HELP_TEXT" + @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / \ + {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) diff --git a/README.md b/README.md index 6080b4c5e47..15784d085df 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ It has a few key advantages over other server software: * Written in Java, Nukkit is faster and more stable. * Having a friendly structure, it's easy to contribute to Nukkit's development and rewrite plugins from other platforms into Nukkit plugins. -Nukkit is **under improvement** yet, we welcome contributions. +Nukkit is **under improvement** yet, we welcome contributions. Links -------------------- @@ -67,6 +67,51 @@ Use [docker-compose](https://docs.docker.com/compose/overview/) to start server docker-compose up -d ``` +Kubernetes & Helm +------------- + +Validate the chart: + +`helm lint charts/nukkit` + +Dry run and print out rendered YAML: + +`helm install --dry-run --debug nukkit charts/nukkit` + +Install the chart: + +`helm install nukkit charts/nukkit` + +Or, with some different values: + +``` +helm install nukkit \ + --set image.tag="arm64" \ + --set service.type="LoadBalancer" \ + charts/nukkit +``` + +Or, the same but with a custom values from a file: + +``` +helm install nukkit \ + -f helm-values.local.yaml \ + charts/nukkit +``` + +Upgrade the chart: + +`helm upgrade nukkit charts/nukkit` + +Testing after deployment: + +`helm test nukkit` + +Completely remove the chart: + +`helm uninstall nukkit` + + Contributing ------------ Please read the [CONTRIBUTING](.github/CONTRIBUTING.md) guide before submitting any issue. Issues with insufficient information or in the wrong format will be closed and will not be reviewed. diff --git a/charts/nukkit/.helmignore b/charts/nukkit/.helmignore new file mode 100644 index 00000000000..0e8a0eb36f4 --- /dev/null +++ b/charts/nukkit/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/nukkit/Chart.yaml b/charts/nukkit/Chart.yaml new file mode 100644 index 00000000000..18fb6920fba --- /dev/null +++ b/charts/nukkit/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: nukkit +description: A Helm chart for Nukkit +type: application +version: 0.1.0 +appVersion: 0.1.0 diff --git a/charts/nukkit/templates/NOTES.txt b/charts/nukkit/templates/NOTES.txt new file mode 100644 index 00000000000..996aa14e3e1 --- /dev/null +++ b/charts/nukkit/templates/NOTES.txt @@ -0,0 +1,15 @@ +1. Get the application URL by running these commands: +{{- if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "nukkit.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "nukkit.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "nukkit.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "nukkit.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80 +{{- end }} diff --git a/charts/nukkit/templates/_helpers.tpl b/charts/nukkit/templates/_helpers.tpl new file mode 100644 index 00000000000..09bfbe78fd2 --- /dev/null +++ b/charts/nukkit/templates/_helpers.tpl @@ -0,0 +1,63 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "nukkit.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "nukkit.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "nukkit.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "nukkit.labels" -}} +helm.sh/chart: {{ include "nukkit.chart" . }} +{{ include "nukkit.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "nukkit.selectorLabels" -}} +app.kubernetes.io/name: {{ include "nukkit.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "nukkit.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "nukkit.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/charts/nukkit/templates/configmap.yaml b/charts/nukkit/templates/configmap.yaml new file mode 100644 index 00000000000..00cbcee8771 --- /dev/null +++ b/charts/nukkit/templates/configmap.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "nukkit.name" . }}-conf + labels: + app: {{ template "nukkit.name" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +data: + nukkit.yml: |- +{{- if .Values.config }} +{{ .Values.config | indent 4 }} +{{- end -}} diff --git a/charts/nukkit/templates/deployment.yaml b/charts/nukkit/templates/deployment.yaml new file mode 100644 index 00000000000..5937c9d08fe --- /dev/null +++ b/charts/nukkit/templates/deployment.yaml @@ -0,0 +1,70 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "nukkit.fullname" . }} + labels: + {{- include "nukkit.labels" . | nindent 4 }} +spec: +{{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} +{{- end }} + selector: + matchLabels: + {{- include "nukkit.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "nukkit.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "nukkit.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + volumeMounts: + - name: {{ template "nukkit.name" . }}-conf + subPath: nukkit.yml + mountPath: /var/opt/nukkit/nukkit.yml + ports: + - name: udp + containerPort: 19132 + protocol: UDP + # TODO: fix/re-test + # readinessProbe: + # exec: + # command: + # - lsof + # - -i + # - :19132 + # initialDelaySeconds: 3 + # periodSeconds: 5 + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumes: + - name: {{ template "nukkit.name" . }}-conf + configMap: + name: {{ template "nukkit.name" . }}-conf + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/charts/nukkit/templates/hpa.yaml b/charts/nukkit/templates/hpa.yaml new file mode 100644 index 00000000000..205cc5415fa --- /dev/null +++ b/charts/nukkit/templates/hpa.yaml @@ -0,0 +1,28 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "nukkit.fullname" . }} + labels: + {{- include "nukkit.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "nukkit.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/charts/nukkit/templates/service.yaml b/charts/nukkit/templates/service.yaml new file mode 100644 index 00000000000..58dfb2f8303 --- /dev/null +++ b/charts/nukkit/templates/service.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "nukkit.fullname" . }} + labels: + {{- include "nukkit.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: udp + protocol: UDP + name: udp +{{- if eq .Values.service.port "NodePort" }} + nodePort: {{ .Values.service.nodePort }} +{{- end }} + selector: + {{- include "nukkit.selectorLabels" . | nindent 4 }} +{{- if .Values.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} +{{- end }} diff --git a/charts/nukkit/templates/serviceaccount.yaml b/charts/nukkit/templates/serviceaccount.yaml new file mode 100644 index 00000000000..dc56a543579 --- /dev/null +++ b/charts/nukkit/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "nukkit.serviceAccountName" . }} + labels: + {{- include "nukkit.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/nukkit/templates/tests/test-connection.yaml b/charts/nukkit/templates/tests/test-connection.yaml new file mode 100644 index 00000000000..3c0330acc4b --- /dev/null +++ b/charts/nukkit/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "nukkit.fullname" . }}-test-connection" + labels: + {{- include "nukkit.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test-success +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "nukkit.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/charts/nukkit/values.yaml b/charts/nukkit/values.yaml new file mode 100644 index 00000000000..8f6721ece63 --- /dev/null +++ b/charts/nukkit/values.yaml @@ -0,0 +1,74 @@ +# Default values for nukkit. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: flaccid/nukkit + pullPolicy: Always + # Overrides the image tag whose default is the chart appVersion. + tag: "latest" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 19132 + loadBalancerIP: "" + nodePort: 39132 + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +config: | + settings: + # Multi-language setting + # Available: eng, chs, cht, jpn, rus, spa, pol, bra, kor, ukr, deu + language: "eng" diff --git a/nukkit.yml.default b/nukkit.yml.default new file mode 100644 index 00000000000..3d4d2aae20f --- /dev/null +++ b/nukkit.yml.default @@ -0,0 +1,4 @@ +settings: + # Multi-language setting + # Available: eng, chs, cht, jpn, rus, spa, pol, bra, kor, ukr, deu + language: "eng"