![](https://i.imgur.com/aAhJ3bs.png) Deploy a minecraft server on AWS EKS (K8S) from an old github respository. ![](https://i.imgur.com/3AhyoEo.png) This is an interesting lab to demostrate how we can fix a legacy project which is not maintained over 3 years and make it work again on the AWS EKS cluster. The goal of this task is to learn how to troubleshoot the possible issues caused by the old yaml settings and fix them if any. ## Prerequisites: 1. k8s cluster - AWS EKS 1.19+ 2. git cli tool 3. kubectl (you can alias k=kubectl) 4. helm cli tool - helm version v.3.2.4+ 5. minecraft java edition client [download link](https://www.minecraft.net/en-us/download) 6. [github link - k8s-minecraft](https://github.com/hoeghh/k8s-minecraft) ![](https://i.imgur.com/7tWq4ms.jpg) ***This post is quite long, and we recommend you to have a deep breath before you read it. Enjoy~*** ## Reference Commands ``` $ kubectl version -> ensure your have your kubectl cmd ready $ alias k=kubectl -> Be lazy to type the whole command $ k get no -> to check your k8s cluster version ``` ## Cloud Architecture ![](https://i.imgur.com/2EMnzeL.png) ## Journey 1. go to your terminal and type below ``` $ git clone https://github.com/hoeghh/k8s-minecraft.git ``` sample output: ``` Cloning into 'k8s-minecraft'... remote: Enumerating objects: 46, done. remote: Counting objects: 100% (5/5), done. remote: Compressing objects: 100% (5/5), done. remote: Total 46 (delta 0), reused 2 (delta 0), pack-reused 41 Receiving objects: 100% (46/46), 4.28 MiB | 1.04 MiB/s, done. Resolving deltas: 100% (10/10), done.``` Directories and files (you can type tree to have below information on Mac) . ├── LICENSE ├── README.md ├── docker-image │   ├── Dockerfile │   └── docker-entrypoint.sh ├── helmchart │   └── k8s-minecraft │   ├── Chart.yaml │   ├── templates │   │   ├── NOTES.txt │   │   ├── _helpers.tpl │   │   ├── deployment.yaml │   │   ├── ingress.yaml │   │   ├── pvc.yaml │   │   └── service.yaml │   └── values.yaml ├── minecraft.pv.yaml ├── minecraft.pvc.yaml └── worlds └── minecraft-server.zip ``` 2. read the manual from the README.md. Since we are more focusing on implementing the application on K8S, we will ignore docker explanation. ``` $ cat README.md # Running in Kubernetes You need a Kubernetes cluster and Helm installed Edit the PV file minecraft.pv.yaml to point to your nfs share. Then apply the PV and PVC, and then helm install minecraft. kubectl apply -f minecraft.pv.yaml kubectl apply -f minecraft.pvc.yaml cd helmchart/k8s-minecraft/ helm install . --name overworld As for now, proxy the pod to your host kubectl port-forward overworld-k8s-minecraft-0 25565:25565 ``` 3. Let's check out the pv and pvc YAML file then we decide what YAML files we will apply to the K8S cluster. minecraft.pv.yaml![pv.yaml](https://i.imgur.com/Dn98s4Q.png) minecraft.pvc.yaml![pvc.yaml](https://i.imgur.com/rMdk8hD.png) AWS EKS already provides us a standard storage class, and the domain name (nfs-server.example.com) is going to break the whole thing. So, I decided to use default storage class instead of this part of settings. ``` $ k get sc -> to list your storge class $ k describe sc gp2 -> storage class gp2 details ``` * Remove the storage class settings out of the minecraft.pvc.yaml as below. ``` apiVersion: v1 kind: PersistentVolumeClaim metadata: name: portal-minecraft spec: accessModes: - ReadWriteOnce resources: requests: storage: 2Gi ``` * Apply the minercraft.pvc.yaml directly. Why can I ignore the pv? Let's see what happened! ``` $ k apply -f minecraft.pvc.yaml persistentvolumeclaim/portal-minecraft created $ k get pv, pvc pending??? why??? what happened? hehehe... you will see the magic soon. ``` 4. Modify the helm charts and values.yaml. Here are several things we need to overcome. * I would like to run minecraft server v1.17.1+, but the currently only supports up to 1.16.5 only. I will use other docker image instead. ![](https://i.imgur.com/qMyMDkM.png) ![](https://i.imgur.com/nLebnZ2.jpg) ![](https://i.imgur.com/Gt2jtHG.png) * service type - use load balancer instead of ClusterIP. I can later use minecraft client to connect minecraft server via the public IP of load balancer. Let's check the difference I made ``` $ cp values.yaml values.yaml.orig $ diff values.yaml values.yaml.orig 8,9c8,9 < repository: "itzg/minecraft-server" < tag: "multiarch-latest" --- > repository: "hoeghh/minecraft" > tag: "1.12.2" 13c13 < type: LoadBalancer --- > type: ClusterIP 26c26 ``` Now, values.yaml is updated as below ``` # Default values for k8s-minecraft. # This is a YAML-formatted file. # Declare variables to be passed into your templates. replicaCount: 1 image: repository: "itzg/minecraft-server" tag: "multiarch-latest" pullPolicy: Always service: type: LoadBalancer port: 25565 name: minecraft minecraft: eula: true ingress: enabled: true annotations: {} path: / port: "25565" hosts: - minecraft.sharedk8s.gaiatechs.info tls: [] # - secretName: chart-example-tls # hosts: # - chart-example.local SecurityContext: 1000 Persistence: Enabled: true MountPath: "/minecraft-data/" VolumeName: "minecraftdata" # define volume claim. This is only considered if Enabled is set to true above # If you have an existing volume claim, add it's name in ExistingClaim # A new PVC will be created if enabled is set to true and ExistingClaim is set to "" ExistingClaim: "portal-minecraft" # VolumeClaimTemplates: # AccessModes: ReadWriteOnce # Storage: 2Gi # StorageClassName: standard # Selector: # Enabled: true # MatchLabels: # app: minecraft # appid: portal Resources: cpuRequest: 1000m cpuLimit: 2000m memoryRequest: 1024Mi memoryLimit: 1512Mi nodeSelector: {} tolerations: [] affinity: {} ``` Let's dry-run the implementation with helm and the customized values.yaml file. The helm command output tells us that the StatefulSet version is incorrectly, so we need to correct the version. ``` $ helm install overworld-demo . --dry-run Error: unable to build kubernetes objects from release manifest: unable to recognize "": no matches for kind "StatefulSet" in version "apps/v1beta1" $ k explain statefulset|grep VER VERSION: apps/v1 ``` Then modify the deployment.yaml file ``` $ cat helmchart/k8s-minecraft/templates/deployment.yaml apiVersion: apps/v1 kind: StatefulSet . . . . .ignore ``` Let's try to run helm again to verify the modifications, and the output is as below. ``` $ helm install overworld-demo . --dry-run NAME: overworld-demo LAST DEPLOYED: Thu Jul 22 21:31:14 2021 NAMESPACE: default STATUS: pending-install REVISION: 1 TEST SUITE: None HOOKS: MANIFEST: --- # Source: k8s-minecraft/templates/service.yaml apiVersion: v1 kind: Service metadata: name: overworld-demo-k8s-minecraft labels: app: k8s-minecraft chart: k8s-minecraft-0.1.0 release: overworld-demo heritage: Helm spec: type: LoadBalancer ports: - port: 25565 targetPort: 25565 protocol: TCP name: minecraft selector: app: k8s-minecraft release: overworld-demo --- # Source: k8s-minecraft/templates/deployment.yaml apiVersion: apps/v1 kind: StatefulSet metadata: name: overworld-demo-k8s-minecraft labels: app: k8s-minecraft chart: k8s-minecraft-0.1.0 release: overworld-demo heritage: Helm spec: serviceName: overworld-demo replicas: 1 selector: matchLabels: app: k8s-minecraft release: overworld-demo template: metadata: labels: app: k8s-minecraft release: overworld-demo spec: containers: - name: k8s-minecraft image: "itzg/minecraft-server:multiarch-latest" env: - name: EULA value: "true" imagePullPolicy: Always ports: - name: minecraft containerPort: 25565 protocol: TCP resources: requests: cpu: "1000m" memory: "1024Mi" limits: cpu: "2000m" memory: "1512Mi" volumeMounts: - name: minecraftdata mountPath: /minecraft-data/ securityContext: fsGroup: 1000 volumes: - name: minecraftdata persistentVolumeClaim: claimName: portal-minecraft --- # Source: k8s-minecraft/templates/ingress.yaml apiVersion: extensions/v1beta1 kind: Ingress metadata: name: overworld-demo-k8s-minecraft labels: app: k8s-minecraft chart: k8s-minecraft-0.1.0 release: overworld-demo heritage: Helm spec: rules: - host: minecraft.sharedk8s.gaiatechs.info http: paths: - path: / backend: serviceName: overworld-demo-k8s-minecraft servicePort: 25565 NOTES: 1. Get the application URL by running these commands: http://minecraft.sharedk8s.gaiatechs.info/ ``` We are ready for the minecraft server deploymnet. Let's excute below commands. ``` $ helm install overworld-demo . $ helm ls -> check the deployment status $ k get po -> check the pod status $ k get pv,pvc -> you will see the statue is bonded $ k get svc -> get the IP/domain to access the minecraft server ``` ![helm](https://i.imgur.com/fAhf2Br.png) ![](https://i.imgur.com/yqgfBIY.png) What happened to the pv,pvc? It is automatically bound because the pod requires to mount the volume. How convenience it is, but I will still recommend you to know more about pv, pvc, storage class details. Here are the description of this pv. If you check the annotation field, you can realize that this pv is created by AWS-ebs-dynamic-provisioner. ``` k describe pv pvc-92976918-627c-493a-86e9-8977c89438ac Name: pvc-92976918-627c-493a-86e9-8977c89438ac Labels: failure-domain.beta.kubernetes.io/region=us-east-1 failure-domain.beta.kubernetes.io/zone=us-east-1b Annotations: kubernetes.io/createdby: aws-ebs-dynamic-provisioner pv.kubernetes.io/bound-by-controller: yes pv.kubernetes.io/provisioned-by: kubernetes.io/aws-ebs Finalizers: [kubernetes.io/pv-protection] StorageClass: gp2 Status: Bound Claim: default/portal-minecraft Reclaim Policy: Delete Access Modes: RWO VolumeMode: Filesystem Capacity: 2Gi Node Affinity: Required Terms: Term 0: failure-domain.beta.kubernetes.io/region in [us-east-1] failure-domain.beta.kubernetes.io/zone in [us-east-1b] Message: Source: Type: AWSElasticBlockStore (a Persistent Disk resource in AWS) VolumeID: aws://us-east-1b/vol-06a338ea5b0cc5f8c FSType: ext4 Partition: 0 ReadOnly: false Events: <none> ``` The log is always the most useful information we can refer to. We also need to determine pod logs and fix errors if any. When I tried the steps from the original github README.md, I have two issues need to be fixed. * pv cannot be equipped * not support minecraft server v1.17.1 ``` $ k get po -> find your pod name $ k logs overworld-demo-k8s-minecraft-0 [init] Running as uid=1000 gid=1000 with /data as 'drwxrwxr-x 2 1000 1000 6 Jul 14 23:45 /data' [init] Resolved version given LATEST into 1.17.1 [init] Resolving type given VANILLA [init] Downloading minecraft_server.1.17.1.jar ... [init] Creating server.properties in /data/server.properties [init] Disabling whitelist [init] Setting whitelist to 'false' in /data/server.properties [init] Setting white-list to 'false' in /data/server.properties [init] Adding server-name with 'Dedicated Server' in /data/server.properties [init] Setting server-port to '25565' in /data/server.properties [init] Setting motd to 'A Vanilla Minecraft Server powered by Docker' in /data/server.properties [init] Setting enable-rcon to 'true' in /data/server.properties [init] Setting rcon.password to 'minecraft' in /data/server.properties [init] Setting rcon.port to '25575' in /data/server.properties [init] Setting level-name to 'world' in /data/server.properties [init] Setting online-mode to 'true' in /data/server.properties [init] Checking for JSON files. [init] Setting initial memory to 1G and max to 1G [init] Starting the Minecraft server... [13:34:06] [main/INFO]: Environment: authHost='https://authserver.mojang.com', accountsHost='https://api.mojang.com', sessionHost='https://sessionserver.mojang.com', servicesHost='https://api.minecraftservices.com', name='PROD' [13:34:07] [main/WARN]: Ambiguity between arguments [teleport, location] and [teleport, destination] with inputs: [0.1 -0.5 .9, 0 0 0] [13:34:07] [main/WARN]: Ambiguity between arguments [teleport, location] and [teleport, targets] with inputs: [0.1 -0.5 .9, 0 0 0] [13:34:07] [main/WARN]: Ambiguity between arguments [teleport, destination] and [teleport, targets] with inputs: [Player, 0123, @e, dd12be42-52a9-4a91-a8a1-11c01849e498] [13:34:07] [main/WARN]: Ambiguity between arguments [teleport, targets] and [teleport, destination] with inputs: [Player, 0123, dd12be42-52a9-4a91-a8a1-11c01849e498] [13:34:07] [main/WARN]: Ambiguity between arguments [teleport, targets, location] and [teleport, targets, destination] with inputs: [0.1 -0.5 .9, 0 0 0] [13:34:07] [main/INFO]: Reloading ResourceManager: Default [13:34:08] [Worker-Main-2/INFO]: Loaded 7 recipes [13:34:09] [Worker-Main-2/INFO]: Loaded 1137 advancements [13:34:11] [Server thread/INFO]: Starting minecraft server version 1.17.1 [13:34:11] [Server thread/INFO]: Loading properties [13:34:11] [Server thread/INFO]: Default game type: SURVIVAL [13:34:11] [Server thread/INFO]: Generating keypair [13:34:11] [Server thread/INFO]: Starting Minecraft server on *:25565 [13:34:12] [Server thread/INFO]: Using epoll channel type [13:34:12] [Server thread/INFO]: Preparing level "world" [13:34:21] [Server thread/INFO]: Preparing start region for dimension minecraft:overworld [13:34:21] [Worker-Main-2/INFO]: Preparing spawn area: 0% *** ignore some preparing logs *** spawnspawn area: 85% [13:35:05] [Worker-Main-2/INFO]: Preparing spawn area: 90% [13:35:05] [Server thread/INFO]: Time elapsed: 44628 ms [13:35:05] [Server thread/INFO]: Done (53.801s)! For help, type "help" [13:35:05] [Server thread/INFO]: Starting remote control listener [13:35:06] [Server thread/INFO]: Thread RCON Listener started [13:35:06] [Server thread/INFO]: RCON running on 0.0.0.0:25575 [13:40:21] [User Authenticator #1/INFO]: UUID of player WebberHLin is 98364cea-2743-4080-b122-0476ce9e9d88 [13:40:21] [Server thread/INFO]: WebberHLin[/10.0.136.41:23506] logged in with entity id 338 at (218.5, 64.0, 107.5) [13:40:21] [Server thread/INFO]: WebberHLin joined the game [13:40:35] [Server thread/INFO]: WebberHLin lost connection: Disconnected [13:40:35] [Server thread/INFO]: WebberHLin left the game [13:41:45] [User Authenticator #2/INFO]: UUID of player WebberHLin is 98364cea-2743-4080-b122-0476ce9e9d88 [13:41:46] [Server thread/INFO]: WebberHLin[/10.0.173.254:45365] logged in with entity id 488 at (218.5, 64.0, 107.5) [13:41:46] [Server thread/INFO]: WebberHLin joined the game [13:42:23] [Server thread/INFO]: WebberHLin lost connection: Disconnected [13:42:23] [Server thread/INFO]: WebberHLin left the game [14:08:35] [User Authenticator #3/INFO]: UUID of player WebberHLin is 98364cea-2743-4080-b122-0476ce9e9d88 [14:08:35] [Server thread/INFO]: WebberHLin[/10.0.159.62:40733] logged in with entity id 621 at (225.8359600534416, 59.0, 125.37970090109104) [14:08:35] [Server thread/INFO]: WebberHLin joined the game [14:08:53] [Server thread/INFO]: WebberHLin drowned [14:31:11] [Server thread/INFO]: WebberHLin was slain by Drowned [15:15:24] [Server thread/INFO]: WebberHLin lost connection: Disconnected [15:15:24] [Server thread/INFO]: WebberHLin left the game [15:15:24] [Server thread/WARN]: handleDisconnection() called twice ``` From `k get svc` you will find the external ip. Mine is `a743a8f2c576546f89e752a2de44d12c-1504217944.us-east-1.elb.amazonaws.com`. Let's launch the minecraft client app and click on the green button to start the game. It might take a while to load the welcome screen. ![](https://i.imgur.com/emGIiNc.jpg) then it is still loading... ![](https://i.imgur.com/SOW3XXt.png) Wow! Hope I can connect to the minecraft server I just configured on the AWS EKS. ![](https://i.imgur.com/a2EiL2N.jpg) Before I connect to the server, I have to change the language back to English(US) then click on the "Done" button. ![](https://i.imgur.com/BH8S1EQ.png) Now we click on the "Multiplayer" button and edit our server information for connection. Then I edit the server IP address and click on the "Done" Button. The server IP address is 'a743a8f2c576546f89e752a2de44d12c-1504217944.us-east-1.elb.amazonaws.com'. ![](https://i.imgur.com/a2EiL2N.jpg) ![](https://i.imgur.com/jqa8W7X.png) Now, you can see the "Minecraft Demo Server on AWS EKS" in the list and double click on it. ![](https://i.imgur.com/2FzHPfc.png) We can play the game with friends via the minecraft server now! ![](https://i.imgur.com/ywXoFR0.png) ## Conclusion The idea of this post is more like a practice for fixing legacy K8S/Helm files and hope you enjoy it. >Do we have other ways to build the minecraft server? Yes, just google "helm minescraft" or type "helm search hub minecraft".