使用Docker部署Terraria服务器的详情
之所以写这篇文章,是因为我搞了这玩意,所以不写出来就浪费了。
Terraria服务器
搭建Terraria服务器是一件很简单的事情,steam版的安装目录下有一个TerrariaServer的可执行程序,直接执行就行了,或者去网上找TerrariaServer的三种系统的整合。如果有多个存档怎么办,创建多个服务器进程就可以了,只要端口号不一样就OK,但是有一个问题是我在寒假的时候遇到的,那就是我的主力机是放在学校的,家里只有一台旧台式(Ubuntu18,3代U)和我带回家的旧笔记本戴尔n4110(win10 1903,2代U),可能是机器比较老的原因,两台机器创建多个服务器进程的时候会错误,而我回到学校后写下这篇文章的时候用我的主力机(win10 1903 9代U)测试是不会的。
Docker容器
使用Docker容器也是很巧合,因为当时我正好在学,就直接用Docker构建了一个Terraria服务器,apt update后装了sshd,然后把程序和存档传进去然后启动,没什么技术含量,泰拉存档或者玩家名为中文时,由于我的Linux下没有中文语言环境,在启动的时候存档是中文名称或者玩家加入时我的终端正好开着显示可能就会卡住不动然后我只能疯狂按ctrlc停止它,直到假期快结束的时候,我才意识到要从头构建一个镜像,而且要摒弃一些功能,加入一些必要的功能,比如说,中文语言环境,为此我写了三个dockerfile(轻量版,仅ssh版,完全版)。
首先看dockerfile下的所有文件
三个版本中只有轻量版是没有authorized_keys公钥和sshd_config配置文件,因为轻量版没有sshd服务,只有TerrariaServer。dockerfile是构建镜像的文件,fishing.sh和purple.sh是对应我的两个存档的启动脚本。Terraria文件夹中包含了服务器程序和存档文件,内容如下:
Terraria/worlds中存放的是存档文件,有需要直接更换或添加就行
authorized_keys公钥文件:学Linux的懂得都懂,自己生成一对密钥就可以
sshd_config配置文件:因为默认不允许root登录,所以比较麻烦,修改为允许root登录,关闭密码登录,允许密钥登录,有需要也是可以自己改,在RUN执行安装ssh后COPY进去可以直接覆盖
purple.sh启动脚本内容如下:
/root/Terraria/TerrariaServer -world "worlds/purple.wld" -logpath "logs"
#这里的"worlds/purple.wld"是相对路径,默认是7777端口不改了,指定一个世界就可以自动开启不用手动操作
轻量级
FROM ubuntu:18.04
MAINTAINER thankvinci "thankvinci@163.com"
COPY fishing.sh /fishing.sh #将启动脚本复制到镜像的根目录
COPY purple.sh /purple.sh
COPY Terraria /root/Terraria #将Terraria服务器文件夹复制到root用户目录下
ENV LANG=C.UTF-8 #设置语言环境
仅ssh版
FROM ubuntu:18.04
MAINTAINER thankvinci "thankvinci@163.com"
RUN apt-get update \ #执行更新指令并安装sshd
&& apt-get install -y openssh* \
&& mkdir -p /var/run/sshd \
&& mkdir -p /root/.ssh
COPY authorized_keys /root/.ssh/authorized_keys #将公钥文件复制到root用户目录下的.ssh文件夹中
COPY sshd_config /etc/ssh/sshd_config #将这个写好的配置文件复制到/etc/ssh下
COPY fishing.sh /fishing.sh #同轻量版的
COPY purple.sh /purple.sh
COPY Terraria /root/Terraria
ENV LANG=C.UTF-8
完全版
FROM ubuntu:18.04
MAINTAINER thankvinci "thankvinci@163.com"
RUN apt-get update \ #apt更新安装ssh
&& apt-get install -y openssh* \
&& mkdir -p /var/run/sshd \
&& mkdir -p /root/.ssh \
&& apt-get install -y language-pack-zh-hans language-pack-zh-hans \ #安装中文语言环境
&& apt-get install -y vim \ #安装vim
&& apt-get install -y lsof \ #安装lsof,这个方便查绑定端口
&& apt-get install -y screen \ #安装screen,方便在容器内部启动多个后台执行的终端
&& apt-get install -y iproute2 \
&& apt-get install -y net-tools \
&& apt-get install -y kde-l10n-zhcn \
&& localedef -c -f UTF-8 -i zh_CN zh_CN.utf8 \
&& echo "export LC_ALL=zh_CN.utf8" >> /etc/profile
COPY authorized_keys /root/.ssh/authorized_keys #后面和“仅ssh版”一样
COPY sshd_config /etc/ssh/
COPY fishing.sh /fishing.sh
COPY purple.sh /purple.sh
COPY Terraria /root/Terraria
ENV LANG=C.UTF-8
我制作的先后顺序是这样:完全版,轻量版,仅ssh版,以目前来看完全版是完全没有必要的,功能太多,特别是apt安装的中文语言环境占的空间太大了,最后ENV定义的LANG其实已经能正常显示中文了,所以我就搞了一个轻量版,但是轻量版没有ssh,如果要备份服务器存档的话就比较麻烦。
这是当时构建后截的图,light版只有142MB,里面包括了Ubuntu1804镜像的约70M和Terraria文件夹75M;sshonly版主要是因为update多了150MB,complete版主要是因为还装了中文环境,比sshonly多了480MB,所以于情于理我都觉得完全版完全没有必要,就当不存在了,最合适的就是sshonly,方便维护,备份,light版就适合于那种一次开坑的那种,存档无所谓,玩到通关要么删容器,要么装个ssh把存档提取出来。
镜像的构建
docker build -t ts:v5 /mnt/study/docker/sshonly #其实写到这里,我给sshonly更新了5次,所以tag就改为v5了,也就是说complete和light完全没有改的必要
顺便再提一个我构建时遇到的问题,就是我是把构建的所有文件放到smb的共享目录下,由于没有权限操作我就把执行了chmod 777 -R ,把所有文件都改成了777权限,结果构建的后的ssh密钥不能用,原因就是这个密钥应该是除了密钥的用户以外,其他用户不能有读以外的权限,所以,smb的共享目录没有权限的话777就够了,-R真的大可不必。
插播一下网络的构建
容器启动的时候,默认是bridge,172.17.0.1的网段,但是ip是不一定的,特别是我是多个容器,有时候是2有时候是3或者4,所以要指定ip,才方便知道哪个存档的ip是哪个,要么构建一个新的网络要么就指定network和ip
docker network create --driver bridge --subnet=172.18.12.0/16 --gateway=172.18.1.1 terrarianet
容器的首次启动
docker run -it --name tspurple --network=terrarianet --ip 172.18.12.1 ts:v5 /bin/bash -c 'service ssh start && sh purple.sh'
启动时执行多个命令,service ssh start就是sshonly版和完全版启动时附带执行的指令,我以前是自学Ubuntu,经常用service xxx start,后来上课要用centos就改成systemctl start xxx,在家用Ubuntu也是service,回学校写这篇文章时才发现Ubuntu18-21都是可以用systemctl的,这就挺方便的可以方便地在构建时就设置ssh自启动,那ssh版和轻量版的首次启动指令就可以统一了。
docker run -it --name tspurplelight --network=terrarianet --ip 172.18.12.2 ts:light /bin/bash purple.sh
这是轻量版的启动指令。
关于连接上的问题
这部分主要是外部系统的配置,无关容器,需要的软件如下:
apt-get install lsof
apt-get install socat
我搭建的是1405服务器,原本是为了电脑手机都能进才开的,然后拿的是家里的主机在跑,家里宽带是有ipv6公网的,也就是说,我在学校也可以访问,一开始没有使用容器的时候,我是直接在Ubuntu上跑的,但是我始终无法通过数据网络访问到,使用tcping工具也ping不到7777端口,局域网内就可以,于是我用lsof -i:7777查了一下端口,发现只有tcp,而没有tcp6,这就让人很头大,因为我刚学docker,里面有一个示例是端口映射的,我通过数据网络能访问到那个端口,于是我就想能不能把服务器搭在docker里面,结果是docker容器确实有应该映射端口那个端口,但是我始终访问不到,这里有点偏技术,lsof 查出来tcp和tcp6的7777都在运行,但是还是只有tcp7777的有效,于是进容器查一下,一拍脑袋,容器内部的7777也是只有tcp,那么就是说,使用docker进行端口映射的话,ipv4和ipv6是分开的,因为容器内部就没有tcp6,所以容器外部就映射了个寂寞。
最后是在网上找如何映射ipv4的端口到ipv6,用socat,指令也很简单:
socat TCP6-LISTEN:8888,reuseaddr,fork TCP4:172.18.12.1:7777 #把容器的7777端口映射到本机的8888端口上
不过回到学校后有一个问题,宿舍的网络是纯ipv4,访问不了我纯ipv6的网络端口,只能开手机数据来进行连接。
关于后台运行服务
Linux的进程如果遇到终端关闭是会结束的,因为进程相当于是shell的子进程,我一般是用screen
apt-get install screen
使用screen创建一个新的终端,开启服务后按ctrla d把这个screen挂到后台。
screen -S ts # ts是自定义的screen名称,可以重复,但是为了方便找到最好不要重复
screen -ls # 查找对应screen的id
screen -r id # 用查到的id恢复screen
实际操作
screen -S tspurple docker run -it --name tspurple --network=terrarianet --ip 172.18.12.1 ts:v5 /bin/bash -c 'service ssh start && sh purple.sh'
这段命令拆成两部分来看,screen -S tspurple是创建窗口,后面则是在窗口中执行的命令,在服务启动后就可以ctrla+d把窗口挂到后台
同样的,端口映射也一样
screen -S tsfsocat socat TCP6-LISTEN:8888,reuseaddr,fork TCP4:172.18.12.1:7777
就这样一步到位。
写到最后
实际体验下还是存在一些问题的,比如说,我是手机端游玩,当遇到打电话进来时,数据网络会被切断,然后我就断开了,但是此时我在泰拉中的角色并不会立马退出来,要等若干秒的时间才会掉出来,如果此时疯狂进入服务器的话,会导致服务器的7777端口,即游戏的端口不见了,具体情况就是,服务器进程是在运行的,但是绑定的端口掉了,这个我查了好久也查不到不关闭进程如何切换端口或重新绑定端口,只能说重新启动这个服务器进程。