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

简介: 本文讲的是Docker在英雄联盟游戏中的实践探索(四),【编者的话】这篇博客是Riot的Docker实践系列博客的第四篇,主要讨论了如何添加一个基于Nginx的代理容器,以及如何用Compose来管理多容器应用。
本文讲的是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在英雄联盟游戏中的实践探索(四)
目录
相关文章
|
13天前
|
测试技术 Linux Docker
【好玩的经典游戏】Docker部署FC-web游戏模拟器
【好玩的经典游戏】Docker部署FC-web游戏模拟器
51 1
|
18天前
|
存储 Kubernetes Docker
构建高效稳定的Docker容器集群:从原理到实践
【4月更文挑战第19天】 在当今微服务架构盛行的时代,容器化技术已经成为了软件开发和部署的标准实践。本文深入探讨了如何利用Docker容器技术,结合Kubernetes集群管理工具,构建一个高效、稳定且可扩展的容器化环境。文章首先简述了Docker的核心原理及其优势,接着详细阐述了Kubernetes的基本概念与组件,最后通过一个实际案例来指导读者如何从零开始搭建并优化一个基于Docker和Kubernetes的容器集群系统。
22 1
|
1月前
|
前端开发 Ubuntu 开发者
【Docker系列】Docker-核心概念/常用命令与项目部署实践
【4月更文挑战第1天】 Docker是容器化技术,打包应用及依赖,实现快速部署。核心概念包括镜像、容器和仓库。镜像是只读模板,容器是镜像运行实例,仓库用于存储和分发镜像。常用命令如`docker search`、`docker pull`、`docker images`、`docker ps`等。安装Docker在Ubuntu上涉及`apt-get update`、`install docker-ce`等步骤。了解这些基础,开发者能更高效地部署和管理应用。Docker简化了环境配置,增强了软件的可移植性和扩展性,是现代开发的必备技能。
445 3
|
2月前
|
SQL 数据库 Docker
OBCP实践 - OceanBase Docker 体验
开发者和技术爱好者可以在无需复杂安装配置的情况下迅速搭建OceanBase数据库环境,从而方便地进行功能测试、性能评估或者开发调试等实践活动。同时,由于Docker的便捷性,此方法也支持跨平台部署,使得OceanBase数据库能够在不同的操作系统(如Linux、Windows、macOS)上轻松体验。
86 2
|
2月前
|
监控 持续交付 Docker
深入浅出:基于Docker的微服务部署实践
【2月更文挑战第26天】在当前软件开发领域,微服务架构与容器化技术成为提升应用可伸缩性、可靠性和开发效率的关键手段。本文将深入探讨如何利用Docker容器技术实现微服务的快速部署与管理,涵盖环境搭建、服务打包、网络配置及持续集成等核心话题。通过实例演示,旨在为开发者提供一套行之有效的微服务部署解决方案。
|
2月前
|
Kubernetes Go 开发者
Go语言与Docker容器结合的实践应用与案例分析
【2月更文挑战第23天】本文通过分析实际案例,探讨了Go语言与Docker容器技术结合的实践应用。通过详细阐述Go语言在容器化环境中的开发优势,以及Docker容器技术在Go应用部署中的重要作用,本文旨在为读者提供Go语言与Docker容器结合的具体实现方法和实际应用场景。
|
2月前
|
Kubernetes 云计算 开发者
云计算中的容器化技术:Docker与Kubernetes的实践
云计算中的容器化技术:Docker与Kubernetes的实践
147 0
|
3月前
|
运维 云计算 Docker
深入理解与实践:基于Docker的微服务架构优化策略
本文旨在为软件开发和运维人员提供一个全面的指南,探讨如何通过Docker容器技术优化微服务架构。我们不仅深入分析了Docker在微服务环境中的关键作用,还提出了一系列实践策略,以提高部署效率、增强系统稳定性,并确保服务的可伸缩性和安全性。通过具体案例分析和比较传统部署方式的局限性,本文展示了Docker如何成为微服务架构优化不可或缺的工具,旨在帮助读者构建一个更加灵活、高效和可靠的服务环境。
153 1
|
4月前
|
Java Docker Windows
Windows下部署SpringBoot的实践方案(Docker & Docker Desktop)
Windows下部署SpringBoot的实践方案(Docker & Docker Desktop)
80 0
|
5月前
|
运维 Cloud Native Linux
【云原生|Docker系列第4篇】Docker的容器的入门实践
【云原生|Docker系列第4篇】Docker的容器的入门实践