Docker在英雄联盟游戏中的实践探索(四)

  1. 云栖社区>
  2. DockOne.io>
  3. 博客>
  4. 正文

Docker在英雄联盟游戏中的实践探索(四)

轩墨 2017-09-18 11:39:00 浏览1289
展开阅读全文
本文讲的是Docker在英雄联盟游戏中的实践探索(四)【编者的话】这篇博客是Riot的Docker实践系列博客的第四篇,主要讨论了如何添加一个基于Nginx的代理容器,以及如何用Compose来管理多容器应用。

背景

如果你刚加入我们,可以先从这篇介绍的文章开始,了解我们是如何完成英雄联盟的持续发布,以及我们是如何发现这个技术栈可以很好地解决我们的问题。

在我们的第一篇文章中,我们介绍了如何把Jenkins放在Docker容器中。第二篇文章中,我们介绍了如何使用Docker数据卷容器来创建持久化层。我们创建了一个容器,来保存Jenkins相关的文件,以持久化插件、任务和其他的Jenkins核心数据。我们讨论了数据卷容器和宿主机挂载卷之间的区别。最后,为了不持久化Jenkins war文件,我们介绍了如何从Jenkins主目录中移除war文件。

在第二篇文章的末尾,我们已经有了一个功能完备的、可以保存数据的Jenkins镜像。然而,由于若干原因,它还不完美。本篇文章将解决其中一个问题:在Jenkins之前缺少一个web代理。同时,我们将运行3个容器来构建Jenkins环境。本文将分为两个部分:一是如何添加一个代理容器,二是如何使用Compose(一种方便的Docker工具)来管理多容器应用。

读完本文,你将会完成一个全栈(full stack)的Jenkins主服务器。

第一部分: 代理容器

在Riot,我们使用Nginx作为代理,因为它可以容易地重定向至HTTPS,并使Jenkins监听在8080端口,web服务器监听在80端口。这里不会介绍如何配置Nginx的SSL和HTTPS(互联网上可以找到文档和实例);相反,我将介绍如何在容器中运行Nginx代理服务器,并代理Jenkins服务器。

这一部分将涉及以下几点:
  • 创建简单的Nginx容器
  • 学习如何从本地目录中添加文件到镜像中,比如Nginx配置文件
  • 使用Docker容器链接(link),连接Nginx和Jenkins
  • 配置Nginx来代理Jenkins

更换OS
在Riot,我们并不常用Debian;然而,Cloudbees的Jenkins镜像使用Debian作为默认OS,继承自Java 8镜像。但是,Docker的其中一个强大之处在于,我们可以使用任意的OS,因为宿主机并不在乎。这也展示了容器的“混合模式”。这意味着,如果应用在多个容器之间运行,它们并不需要是同一个OS。如果某个特定的进程需要使用某个特定的Linux发行版的库或模块,这种做法就很有价值了。至于应用在Debian/Centos/Ubuntu上的扩展性(spread)是否是个好主意,你们可以自行判断。

你们可以将镜像转换成Ubuntu、Debian,或者任意一个OS。我们将使用CentOS 7。在本文的第四部分,我将会具体地讨论如何更换Jenkins镜像中的默认OS,并移除对于外部镜像的依赖性。需要考虑的是,如果你更换了OS,那么需要更改很多命令和配置,来确保Nginx正常工作。
创建Nginx Dockerfile
在你的项目根目录中,创建一个新目录jenkins-nginx,来保存另一个Dockerfile。现在,你应该有三个目录了(如果你读过之前的文章):

1、设置OS基础镜像:
FROM centos:centos7
MAINTAINER yourname

2、使用yum安装Nginx:
RUN yum -y update; yum clean all
RUN yum -y install http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm; yum -y makecache
RUN yum -y install nginx-1.8.0

注意我们使用的Nginx版本是1.8.0。这是一个最佳实践:总是锁定版本,避免镜像的重新构建使用未经测试的版本。

3、清除不需要的默认Nginx配置:
RUN rm /etc/nginx/conf.d/default.conf
RUN rm /etc/nginx/conf.d/example_ssl.conf

4、添加配置文件:
COPY conf/jenkins.conf /etc/nginx/conf.d/jenkins.conf
COPY conf/nginx.conf /etc/nginx/nginx.conf


这是我们第一次使用COPY命令。ADD命令与COPY命令十分类似。如果想透彻地了解两者的区别,我推荐你阅读以下链接:

针对我们的场景,COPY是最好的选择。就像以上文章中推荐的,我们只是拷贝单个的文件,并不需要ADD命令提供的特性(解压tarball,基于URL的获取等)。我们会更新默认的nginx.conf和Jenkins的配置文件。

5、我们希望Nginx监听80端口:
EXPOSE 80

6、启动Nginx:
CMD ["nginx"]

保存文件,但不要构建它。因为Dockerfile中有两个COPY命令,我们需要首先创建这些文件。否则,如果这些文件不存在,构建将会失败。

01.gif


创建Nginx配置文件
以下的nginx.conf是一个默认配置,然后再修改特定的配置。
daemon off;
user  nginx;
worker_processes  2;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
    use epoll;
    accept_mutex off;
}

http {
    include       /etc/nginx/mime.types;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    client_max_body_size 300m;
    client_body_buffer_size 128k;

    gzip  on;
    gzip_http_version 1.0;
    gzip_comp_level 6;
    gzip_min_length 0;
    gzip_buffers 16 8k;
    gzip_proxied any;
    gzip_types text/plain text/css text/xml text/javascript application/xml application/xml+rss application/javascript application/json;
    gzip_disable "MSIE [1-6]\.";
    gzip_vary on;

    include /etc/nginx/conf.d/*.conf;
}


现在来修改默认配置:
1、使Nginx不以daemon方式运行:
daemon off;

这是因为命令行中调用nginx,Nginx将以daemon方式运行在后台。这会返回exit 0,Docker会认为进程已经退出,然后停止容器。你会发现这种现象经常发生。对于Nginx来说,只要简单地修改下配置就可以解决这个问题。

2、将Nginx的worker数目提升为2:
worker_processes 2;

这是我每次设置Nginx时必定做的事。当然,你可以选择保持该配置为1。Nginx调优可以单独写一篇文章。我不能告诉你什么是对的。粗略地说,该配置指定了多少个单独的Nginx进程。CPU数目是一个不错的参考值,当然,很多NGINX专家会说情况远比这个要复杂。

3、事件调优(Event tuning):
use epoll;
accept_mutex off;

打开epolling可以使用高效的连接模型。为了加速,我们关闭了accept_mutex,因为我们不在乎较低的连接请求数造成的资源浪费。

4、设置代理头:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

除了关闭daemon模式之外,这是第二个必须的Jenkins代理配置。只有这样,Jenkins才能正确地处理请求,否则会出现一些警告。

5、客户端大小:
client_max_body_size 300m;
client_body_buffer_size 128k;

你可能需要这些配置,也可能不需要。不可否认的是,300MB是一个很大的body大小。然而,我们的用户上传文件到Jenkins服务器,其中一些是HPI插件,一些是真实文件。

6、打开GZIP:
gzip on;
gzip_http_version 1.0;
gzip_comp_level 6;
gzip_min_length 0;
gzip_buffers 16 8k;
gzip_proxied any;
gzip_types text/plain text/css text/xml text/javascript application/xml application/xml+rss application/javascript application/json;
gzip_disable "MSIE [1-6]\.";
gzip_vary on;

为了加速,我们打开了gzip压缩。

保存文件为conf/nginx.conf。下一步就是为Jenkins添加特定的配置文件。

02.gif


针对Jenkins的NGINX配置
就像上一章那样,我会先提供一份完整的配置文件,然后再修改特定的配置。你会发现大多数内容可以在Jenkins的官方文档找到。
server {
    listen       80;
    server_name  "";

    access_log off;

    location / {
        proxy_pass         http://jenkins-master:8080;

        proxy_set_header   Host             $host;
        proxy_set_header   X-Real-IP        $remote_addr;
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto http;
        proxy_max_temp_file_size 0;

        proxy_connect_timeout      150;
        proxy_send_timeout         100;
        proxy_read_timeout         100;

        proxy_buffer_size          8k;
        proxy_buffers              4 32k;
        proxy_busy_buffers_size    64k;
        proxy_temp_file_write_size 64k; 

    }

}

只有一个配置,是真正关于代理的:
proxy_pass   http://jenkins-master:8080;

这个配置需要域名jenkins-master存在,这个可以通过容器连接来保证(稍后会讲到)。如果你还没使用容器连接的话,那么需要将映射Jenkins容器的IP/hostname。

然而,你不能把它设置为localhost。这是因为每个Docker容器都有自己的localhost,将代理指向了Nginx容器本身,这里并没有运行在8080端口的Jenkins。为了避免使用容器连接,需要执行Docker Host(应该是你的Desktop或laptop)的IP地址。尽管你是知道这个信息的,但是请想象一下,如果你的Jenkins容器是运行在Dockerhost集群中的任意一台。你需要写一个自动脚本,来获取IP地址,然后编辑配置文件。这是可以做到的,但是非常麻烦。容器连接可以简化这一过程。

03.gif


构建Nginx镜像,并连接到Jenkins镜像
现在,我们已经创建了Nginx和Jenkins的配置文件。请确保你是在顶层目录中。
docker build -t myjenkinsnginx jenkins-nginx/.

构建完成之后,我们可以启动它,并连接到jenkins-master镜像,使代理发生作用。首先,请确保jenkins-data和jenkins-master正在运行。
docker run --name=jenkins-data myjenkinsdata


如果发生了错误,不用紧张,这说明容器已经存在了。这是一个好事,因为这意味着我们没有覆盖原来的数据。
docker stop jenkins-master
docker rm jenkins-master
docker run -p 8080:8080 -p 50000:50000 --name=jenkins-master --volumes-from=jenkins-data -d myjenkins

现在,我们终于可以启动Nginx容器,并连接jenkins-master:
docker run -p 80:80 --name=jenkins-nginx --link jenkins-master:jenkins-master -d myjenkinsnginx

请注意--link参数。你可以在Docker官方网站上找到相关文档。需要确保域名"jenkins-master"在NGINX容器中存在,指向jenkins-master容器的内部Docker网络IP。

请注意Nginx容器必须在jenkins-master容器之后启动。这意味着,如果要停止和重启jenkins-master容器,那么也需要重启Nginx容器。

04.gif


测试一切是否正常很容易。只要在浏览器中输入IP地址,一切应该正常工作了。

如果出了问题,那么一定有什么东西阻塞了80端口(这更可能发生在OSX上)。请确保防火墙已经关闭,或者至少可以接受80端口的流量。如果由于某种原因,你不能清除80端口,请关闭并删除jenkins-nginx容器,以-p 8000:80参数重启它。然后,访问http://yourdockermachineip:8000,看看一切是否正常。
Jenkins镜像清理
我们已经让Nginx监听在80端口了,就不需要Jenkins镜像暴露8080端口了。现在我们需要删除这个端口配置。停止并重启Jenkins容器,同时Nginx容器也需要重启,因为两者是连接在一起的。每次重启时,它们都会连接在一起。
docker stop jenkins-nginx
docker stop jenkins-master
docker rm jenkins-nginx
docker rm jenkins-master
docker run -p 50000:50000 --name=jenkins-master --volumes-from=jenkins-data -d myjenkins
docker run -p 80:80 --name=jenkins-nginx --link jenkins-master:jenkins-master -d myjenkinsnginx

刷新浏览器http://yourdockermachineiphere

已经不能访问8080端口了,相反,可以通过Nginx代理访问它了。

05.gif


与往常一样,代码和示例都可以在GitHub上找到,地址是https://github.com/maxfields20 ... al_04。你会注意到makefile更新了,添加了Nginx容器和Jenkins容器的启动顺序。

Docker Compose和Jenkins

我们现在运行了3个容器,一个是Nginx代理容器,一个是Jenkins应用容器和一个保存Jenkins数据的数据卷容器。我们已经发现,因为数据卷和容器连接,这3个容器之间有启动顺序和依赖。本文将介绍Compose来处理这些。

本小节将涉及:
  • 使用Compose来管理多容器应用

什么是Compose
Compose是从另一个工具Fig发展而来的。Docker将它定义为“运行复杂应用的工具”。你可以从https://docs.docker.com/compose/查看相关文档。当运行应用时,Compose可以帮我们构建镜像,决定停止和启动哪些容器。

例如,如果我希望运行3个容器应用,重新构建Jenkins容器,重新运行应用-可能是升级Jenkins版本。请运行以下命令:
docker stop jenkins-nginx
docker stop jenkins-master
docker rm jenkins-nginx
docker rm jenkins-master
docker build -t myjenkins jenkins-master/.
docker run --name=jenkins-master --volumes-from=jenkins-data -d myjenkins
docker run -p 80:80 --name=jenkins-nginx --link jenkins-master:jenkins-master -d myjenkinsnginx

正确配置之后,我们可以运行Compose:
docker-compose stop
docker-compose build
docker-compose up -d

这和makefile的行为很类似。使用Compose的取舍在于,你必须额外再维护一个配置文件。

本小节单独成章,是因为使用Docker-Compose是一个个人选择。然而,如果你有很强的Windows开发背景,那么Compose可能不是一个很好的选择。

需求
  • 如果你在OSX上使用Docker Toolbox, Compose是默认安装的。
  • 如果你还没安装Docker Toolbox,或者使用Linux,那么请参考https://docs.docker.com/compose/install/安装Compose
  • OSX 或者 Linux

请注意,Compose还不能在Windows上与Windows版本的Docker客户端一起运行。如果你使用的是Windows和Docker Toolbox,情况会有所不同。我的建议是暂时使用makefile。Compose的开发团队正在开发Windows兼容版本,但是1.4版本尚不支持。
第一步:创建Compos配置文件
Compose使用YAML配置文件。我们为每一个需要Compose管理的镜像添加一个条目。
  • 在你的项目根目录中,创建一个文件docker-compose.yml

你完全可以使用另外一个名字,但是默认情况下,Compose将首先查找这个名字。

第二步: Jenkins数据容器
编辑docker-compose.yml,添加以下内容(由于它是yaml,所以需要保持缩进):
jenkinsdata:
 build: jenkins-data

以上是创建了一个容器的条目,叫做“jenkinsdata”。Compose不支持名字中的特殊字符,如“-”。然后,我们添加了一个构建目录,名字是Dockerfile所在的目录名,如“jenkins-data”。

检查一切是否正常:
  1. 保存文件。
  2. 执行docker-compose build

06.gif


Docker-Compose会找到jenkins-data目录,构建Dockerfile,就像执行了docker build jenkins-data/一样。你会注意到,镜像的名字是不同的。Compose使用的命名转换是“projectname_composecontainername”。默认情况下,项目名称是父目录的名字。

这种命名标准是相当重要的。这是产品环境中的容器命名方式。请确保父目录的名字是合理的,或者使用-p来制定镜像名称。你也可以使用-p来区别产品环境和开发环境。

第三步: Jenkins主镜像
继续编辑docker-compose.xml,添加以下内容:
jenkinsmaster:
  build: jenkins-master
  volumes_from:
    - jenkinsdata
  ports:
    - “50000:50000”

就像Jenkins数据镜像一样,我们也有一个条目,来命名容器,并定义构建目录。我们也加了一条volumes_from语句,与命令行中的--volumes-from=的作用相同。但是,请注意Compose使用的容器名并不是真正的名字。这是Compose的一个方便的特性,可以让我们引用这些名字,提高可读性。Compose足够聪明,可以把它们组合在一起,来构建容器。

另一个优势是Compose知道jenkinsmaster依赖于jenkinsdata,因此会以正确的顺序来启动它们。你可以在Compose文件中以任意顺序列举它们。

最后,我们使用ports指令,来处理端口映射。为了JNLP从连接,我们需要确保Jenkins主容器做50000端口映射。

07.gif


第四步:Nginx镜像
jenkinsnginx:
  build: jenkins-nginx
  ports:
     - "80:80"
  links:
     - jenkinsmaster:jenkins-master
 

将像其他两个条目,它也有一个名字(jenkinsnginx)和一个构建目录。但是,我们添加了一条links指令,就像命令行中的--link

08.gif


第五步:将所有这些组合在一些
完整的docker-compose.yml:
jenkinsdata:
 build: jenkins-data
jenkinsmaster:
 build: jenkins-master
 volumes_from:
  - jenkinsdata
 ports:
  - "50000:50000"
jenkinsnginx:
 build: jenkins-nginx
 ports:
  - "80:80"
 links:
  - jenkinsmaster:jenkins-master

我们需要构建所有这些。首先,需要确保没有之前的容器的痕迹。如果你已经清理了,你可以跳过这一步:
docker stop jenkins-nginx
docker rm jenkins-nginx
docker stop jenkins-master
docker rm jenkins-master
docker rm jenkins-data

注意:迁移到新模型,我们只能丢掉数据容器,这很讨厌。在未来的文章中,我将会讨论如何备份数据。但是如果你需要备份这些数据的话,你可以先用第三篇docker up来备份数据。
docker-compose build
docker-compose up -d

注意-d使得Docker-Compose以daemon方式运行容器,就像Docker的参数-d一样。如果你想知道哪些容器正在运行,Docker-Compose也有相类似的特性:
docker-compose ps

09.gif


第六步:使用Compose维护
Compose足够聪明到了解数据卷,并持久化。
  • 在Jenkins实例中,创建一条测试任务
    docker-compose stop
    
  • 简单地编辑一下Jenkins主Dockerfile,例如更改MAINTAINER。
    docker-compose build
    docker-compose up -d
    
  • 回到Jenkins实例,查看测试任务是否已经存在。

Docker Compose会启动数据容器,并重新创建Nginx容器和主容器。

Compose有一个简单的方式来清理所有的东西:
docker-compose rm

这条命令也会删除你的数据容器。如果你不愿意删除数据容器的话,也很简单。
docker-compose rm jenkinsmaster jenkinsgninx


10.gif


总结
你可以在https://github.com/maxfields20 ... al_05上找到代码和实例。

我们了解到Compose可以简化多镜像应用的管理,只需要多加一个配置文件。这个文件用来自描述容器之间的关系。

Compose是一个不错的、可操作的工具。可能的一个缺点是,容器名是基于父目录而定的,总是需要你指定一个项目名称。Compose可以使用PS和RM等工具。

我们也了解到Compose还不能在Windows上运行。你是否使用Compose取决于你是否需要Windows支持,或者你是否喜欢Compose的命名方式。我个人很喜欢docker-compose.yml的自描述方式。你会注意到我仍然提供了makefile,因为我不需要记住所有容器的名字。

至此,基础教程结束了。之后的文章将涉及更高级的内容。

下一步

我们还有三个高级话题:备份,构建从节点和Docker镜像的完全控制。我将会讨论如何完全制作你自己的Jenkins镜像,而不需要依赖公共仓库。主要是因为依赖管理,或者是因为你不喜欢基于Debian的容器,更喜欢Ubuntu或者CentOS。因此,之后我们会从头创建自己的Dockerfiles,来构建从容器。接下来的内容是:
  1. 完全控制所有的镜像
  2. 备份Jenkins镜像
  3. 构建从容器

下次再会!

原文链接:JENKINS, DOCKER, PROXIES, AND COMPOSE(翻译:夏彬 校对:李颖杰)

原文发布时间为:2015-11-15
本文作者:binsummer
本文来自云栖社区合作伙伴DockerOne,了解相关信息可以关注DockerOne。
原文标题:Docker在英雄联盟游戏中的实践探索(四)

网友评论

登录后评论
0/500
评论
轩墨
+ 关注
所属团队号: DockOne.io