【黑科技回忆童年】用Docker玩转神奇宝贝!
【黑科技回忆童年】用Docker玩转神奇宝贝!
这个画面怀念吗?现在就教你如何用docker玩转神奇宝贝!
项目实战
原项目简介
weplay是一个socket.io库的展示项目,你可以在socket.io官网的展示页面看到它。它是由socket.io库的开创者rauchg,将一个已有的用Javascript实现的GBC模拟器,包装成一个Node.js模块后,再运用socket.io和HTML5的画布API,用Redis作为数据中转以及持续化存储的一个中型项目。
准备工作
1.项目总览:
-
weplay作为IO服务器,提供评论数据的持续化储存和实时GBC界面数据的输出服务。
-
weplay-web提供网页服务。
-
weplay-emulator运行js模拟器,将数据渲染出的每个画面输出到Redis中。
-
weplay-presence用socket.io接口统计实时在线总人数。
项目已经被很明确的分割成了几个模块,由后台服务器链接起来。
2.数据库的加密加固工作:
由于此项目之前的架构大概是一体化,单一服务器的部署思路。Redis数据库端没有做加密访问。但是我们都知道,Redis是很容易被破解的,它的快速是把双刃剑。如果我们在此添加访问受密码保护的Redis服务器的代码,那么我们的服务就算是在某些危机四伏的弹性云平台上也能很好的运作。 所以对于所有模块进行打补丁。
3.与容器化无关的功能增加与版本更新
-
如添加弹幕模式
-
优化模拟器运行效率和内存占用
-
可由环境变量方便的选择加载的游戏ROM
容器化核心:Dockerfile编写
1.首先给个具体的实例,是weplay-web,项目网页服务器的容器化Dockerfile。
FROM node:0.10
MAINTAINER Jiahao Dai <dyejarhoo@gmail.com>
RUN git clone https://github.com/imdjh/weplay-web /srv/weplay-web && \
cd /srv/weplay-web && \ npm install && \ npm install forever -g
ADD docker-entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
EXPOSE 3000
CMD ["/entrypoint.sh"]
实际上编写Dockerfile很简单,不外乎几个步骤:
-
FROM宏,用来指定基础镜像,docker的应用镜像都是有层级的,可以复用造一个轮子不用从砍大叔开始;
-
MAINTAINER,用来告诉使用者这个镜像有问题该找谁;
-
RUN,你希望镜像在被构建(build)时,执行什么命令,在这里我们用git工具下载最新的源代码并用npm安装依赖;
-
ADD,你希望将什么文件复制到容器中;
-
EXPOSE,你希望容器的哪些端口对外开放;
-
CMD,有哪些命令(如果是指令,一定主意路径要在PATH环境变量中)是需要在容器初始运行时作为进程PID1存在的(当ENTRYPOINT为空时)。
和几个注意点:
-
每个Dockerfile中的宏指令会生成一个镜像层(image layer),所以最好在执行命令的时候,最后要回收垃圾,这样执行完这条宏,生成的镜像层会比较轻巧。例如这条语句:RUN apt-get update && apt-get install -y libcairo2-dev && rm -rf /var/lib/apt/lists/*,就做到了最小化安装一个软件源里的二进制包。
-
将守护进程写在单一的文件中,例如本项目中的docker-entrypoint.sh。方便拓展和日后修改。
docker-entrypoint.sh的一般编写思路
-
设置默认环境变量的值
-
当某些环境变量未被设置时,进入出错模式,自动退出执行。
-
执行守护进程
例如letweplay的入口脚本:
#!/bin/bash
# 设置环境变量export
WEPLAY_WEB_PORT=3000
export WEPLAY_IO_URL="${IO_URL_PORT:-BAD}"
export NODE_ENV='production'
if [[ -n "${REDIS_PORT}" ]];then export WEPLAY_REDIS_URI=${REDIS_PORT_6379_TCP_ADDR}:${REDIS_PORT_6379_TCP_PORT} # 当某些环境变量未被设置时,进入出错模式,自动退出执行 export WEPLAY_REDIS_AUTH=${REDIS_PASSWORD}else echo "Redis setting not found, can't start server." >&2 && exit 1
fi
if ( $(echo ${WEPLAY_IO_URL} | grep -q BAD ) );then echo "IO_URL_PORT is missing, can't start entry server." >&2 && exit 1
fi
# 执行守护进程
forever /srv/weplay-web/index.js
容器化实现
1.Docker Compose方案
感谢compose工具,否则部署一个多模块的项目会是让人头疼的一件事。 这里选用灵雀云平台http://www.alauda.cn/,采用灵雀云的开源命令行工具alauda,支持compose部署,可以做到多模块的项目一键部署在灵雀云平台。对于本项目来说我们可以按照下面的步骤迅速布置起来一个平台环境。
-
首先使用python2的pip工具,下载安装alauda工具。注意python3环境下,此工具会报错不能正常使用。
-
然后,登陆进入自己的账号,进行日后API调用的Oauth审核。
-
将如下yml格式的内容保存成my.yml
core: image: 'index.alauda.cn/imdjh/letweplay-core:fallback' environment: - GAME=pokemon/yellow - SAVEDELAY=120000 - WEPLAY_REDIS_AUTH=rosebud links: - redis size: 'XS' entry: image: 'index.alauda.cn/imdjh/letweplay:latest' environment: - 'IO_URL_PORT=http://io-imdjh.myalauda.cn' - 'WEPLAY_REDIS_AUTH=rosebud' - 'THIS_URL_PORT=http://entry-imdjh.myalauda.cn' - WEPLAY_WEB_PORT=80 links: - redis ports: - 80/http restart: on-failure size: 'XXS' io: image: 'index.alauda.cn/imdjh/letweplay-io:latest' environment: - 'THIS_URL_PORT=http://io-imdjh.myalauda.cn' - 'WEPLAY_REDIS_AUTH=rosebud' links: - redis ports: - 3001/http size: 'XXS' redis: image: 'index.alauda.cn/library/redis:latest' size: 'XXS' ports: - 6379/tcp volumes: - /data:10
-
再运行alauda compose up -f my.yml,等待出现诸如:
$ alauda compose up -f my.yml [alauda] Creating and starting service "redis"
[alauda] Creating and starting service "core"
[alauda] Creating and starting service "io"
[alauda] Creating and starting service "entry"
[alauda] OK
-
因为redis在启动后需要一定时间才能开始监听端口,而链接到的各容器会不能立即接连到数据库而出错。所以我们需要使用如下指令手动重启容器,由于它们并不存有数据,所以重启是无害的。
$ alauda service stop io && alauda service start io $ alauda service stop entry && alauda service start entry $ alauda service stop core && alauda service start core
-
既然用了有状态的容器,那么就对数据库做一个存档吧,灵雀云可以很方便地进行存储卷备份:
$ alauda backup create redis-$(date +%F) redis '/data'
[alauda] Creating backup "redis-2015-11-30"
[alauda] OK
2.单个击破-单个部署方案:如果出于种种原因没办法安装compose工具,那么我们也能一条条的docker run起来:
$ #跑一个Redis容器,命令为some-redis
$ sudo docker run --name some-redis -d redis $ #跑个GBC模拟器,链接到some-redis
$ sudo docker run -d -e 'GAME=pokemon/yellow' --link some-redis:redis imdjh/letweplay-core $ #Socket.io服务器开起来,链接到some-redis
$ sudo docker run -d --link some-redis:redis -e 'THIS_URL_PORT=http://localhost:3001' -p 3001:3001 imdjh/letweplay-io $ #开启万维网服务器,链接到some-redis,服务已经全部开启!
$ sudo docker run -d -e 'THIS_URL_PORT=1.2.3.4:3000' -e 'IO_URL_PORT=1.2.3.4:3001' --link some-redis:redis -p 0.0.0.0:3000:3000 imdjh/letweplay
3.图形化操作方案:如果你更习惯图形化界面的操作,可以在灵雀云控制台中,由redis, letweplay-core, letweplay-io, letweplay的顺序进行服务的创建。
本实例一共使用了4个容器。配置如下:
-
3台XXS(256 MB)的容器分别运行:letweplay, letweplay-io, redis
-
1台XS(512MB)的容器运行:letweplay-core
部署的配置参数可以用如下截图作为参考:
-
数据库redis部分
-
IO服务器letweplay-io部分
-
模拟器服务器letweplay-core部分
演示地址:lwp-entry-imdjh.myalauda.cn
怎么样?很酷吧!
最近发现docker在游戏开发运维中还是有不少好处滴,希望和游戏圈的诸位同好共同交流,玩转docker这个大火的技术,下面是一个技术交流群,感兴趣的朋友可以微信扫一扫,共同探讨