转载过程中,图片丢失,代码显示错乱。
为了更好的学习内容,请访问原创版本:
https://www.missshi.cn/api/view/blog/5a6328110a745f6335000006
Ps:初次访问由于js文件较大,请耐心等候(5s左右)
在之前的文章中,我们简要介绍的Docker的一些简单实践。
接下来,我们将在本文中进行类似内容的讲解,不过相比而言,使用Docker部署的服务将会更加的复杂。
搭建Jekyll框架的网站
本文中我们将要构建的第一个应用是一个使用Jekyll框架的自定义网站。
该网站会包括以下两个镜像:
- 一个镜像安装了Jekyll及其相关的软件包
- 一个镜像安装了Apache服务用于使网站正常运行。
则以后开发过程可以转化如下:
- 创建Jekyll基础镜像和Apache镜像
- 利用Jekyll镜像创建一个容器并通过卷挂载网站的源代码。
- 使用Apache镜像创建一个容器,这个容器利用包含编译后的网站的卷并为其服务。
- 网站更新时跳转至步骤2重复执行。
Jekyll基础镜像
首先,我们需要创建第一个Jekyll基础镜像。
我们需要创建一个新的目录并创建该镜像所需要的Dockerfile文件。
mkdir jekyllcd jekylltouch Dockerfile
修改Dockerfile文件如下:
FROM ubuntu:16.04MAINTAINER nianshi <[email protected].com>ENV REFRESHED_AT 2018-02-17RUN apt-get -qq updateRUN apt-get -qq install ruby ruby-dev build-essential nodejs libffi-devRUN gem install --no-rdoc --no-ri jekyllVOLUME /dataVOLUME /var/www/htmlWORKDIR /dataENTRYPOINT [ "jekyll", "build", "--destination=/var/www/html" ]
在该Dockerfile文件中,我们首先安装了Ruby和相关的包,此外,我们使用VOLUME指令创建了两个卷:/data/用于存放网站的源代码,/var/www/html用于存放编译后的Jekyll文件。
构建Jekyll基础镜像
创建好Dockerfile文件后,我们可以执行如下命令来构建镜像:
docker build -t nianshi/jekyll .
命令执行完成后,我们则创建完成了一个jekyll的基础镜像。
Apache基础镜像
接下来,我们需要继续创建一个新的文件夹用于构建Apache镜像。
mkdir apachecd apachetouch Dockerfile
修改Dockerfile文件如下:
FROM ubuntu:16.04MAINTAINER nianshi <[email protected].com>RUN apt-get -qq updateRUN apt-get -qq install apache2VOLUME [ "/var/www/html" ]WORKDIR /var/www/htmlENV APACHE_RUN_USER www-dataENV APACHE_RUN_GROUP www-dataENV APACHE_LOG_DIR /var/log/apache2ENV APACHE_PID_FILE /var/run/apache2.pidENV APACHE_RUN_DIR /var/run/apache2ENV APACHE_LOCK_DIR /var/lock/apache2RUN mkdir -p $APACHE_RUN_DIR $APACHE_LOCK_DIR $APACHE_LOG_DIREXPOSE 80ENTRYPOINT [ "/usr/sbin/apache2" ]CMD ["-D", "FOREGROUND"]
构建Apache镜像
创建好Dockerfile文件后,我们可以执行如下命令来构建镜像:
docker build -t nianshi/apache .
命令执行完成后,我们则创建完成了一个apache的基础镜像。
启动Jekyll网站
现在,我们已经完成了两个基础镜像的构建了。
下面,我们需要启动容器来构建我们的网站了:
首先需要下载网站所需要的源代码:http://www.missshi.cn/#/adminBook?_k=95xjmw
访问资源下载页面,搜索Jekyll示例服务依赖代码下载并解压即可,此处我们假设解压目录是/home/nianshi/blog_demo。
此时,我们可以执行如下命令来启动Jekyll容器:
docker run -v /home/nianshi/blog_demo:/data/ --name blog_demo nianshi/jekyll
此时,我们启动了一个blog_demo的容器,并对卷进行了挂载,其中,本地的/home/nianshi/blog_demo目录挂载到的容器中的/data/文件夹下。另外容器对源代码编译后生成的代码会存放在/var/www/html/卷下。
接下来,我们期望启动一个Apache容器,同时希望Apache容器可以连接到Jekyll容器中/var/www/html/卷下获取编译后的文件。
我们可以执行如下命令:
docker run -d -P --name apache --volumes-from blog_demo nianshi/apache
在上述命令中,出现了一个新的参数--volumes-from,该参数用于连接一个卷,从而可以访问到指定容器中的所有的卷。
Ps:当所有使用某个卷的容器全部都被rm命令删除后,该卷会被自动释放。
下面我们来查看一下我们的容器映射到了机器的哪个端口吧:
docker port apache 80# 0.0.0.0:32782
接下来,我们访问32782端口,可以看到如下页面:
更新Jekyll网站
如果需要更新网站的数据,假设要修改Jekyll网站的博客名字,我们只需要通过编辑宿主机上james_blog/_config.yml文件:并将title域改为Nianshi Blog。
然后通过使用docker start命令启动Docker容器即可。
可以看到,Jekyll编译过程第二次被运行,并且往网站已经被更新。这次更新已经写入了对应的卷。现在浏览Jekyll网站,就能看到变化了。
由于共享的卷会自动更新,这一切都不要更新或者重启Apache容器。这个流程非常简单,可以将其扩展到更复杂的部署环境。
备份Jekyll卷
如果担心一不小心删除卷。
由于卷的优点之一就是可以挂载到任意的容器,因此可以轻松备份它们。
现在创建一个新容器,用来备份/var/www/html卷。
docker run --rm --volumes-from blog_demo -v $(pwd):/backup ubuntu tar cvf /backup/blog_demo_backup.tar /var/www/html
上述代码描述了一个最简单的备份方式,基于基础的ubuntu镜像,通过执行tar cvf命令,将卷/var/www/html中的内容压缩为blog_demo_backup.tar文件,并存储到机器当前目录中。
服务扩展方式
- 首先,我们可以通过运行多个Apache容器,这些容器同时共享blog_demo中共享的卷,同时在多个Apache容器前使用一个负载均衡器从而构成一个Web集群。
- 进一步构建一个镜像,用于把拉取源代码(如git clone)过程打包入卷中,在将该卷挂载到jekyll容器中,从而可以生成一个快速迁移的通用解决方案。
使用Docker构建一个Java项目
接下来,我们考虑用Docker构建一个Java项目。
具体来说,主要分为两步:
- 从指定URL中拉取指定的WAR文件并将其保存到卷中。
- 利用一个含有Tomcat服务器的镜像运行拉取的WAR文件。
WAR文件获取器及其构建
首先创建一个新的目录用于准备构建WAR文件获取器镜像:
mkdir fetchercd fetchertouch Dockerfile
接下来,修改Dockerfile文件如下:
FROM ubuntu:16.04MAINTAINER nianshi <[email protected].com>ENV REFRESHED_AT 2018-02-18RUN apt-get -qq updateRUN apt-get -qq install wgetVOLUME [ "/var/lib/tomcat7/webapps/" ]WORKDIR /var/lib/tomcat7/webapps/ENTRYPOINT [ "wget" ]CMD [ "--help" ]
该镜像只完成一件非常简单的事,使用wget从指定的url中拉取文件并保存在/var/lib/tomcat7/webapps/卷中。后续我们会将该卷共享到Tomcat服务器中。
下面,我们可以执行如下命令来构建镜像:
docker build -t nianshi/fetcher .
获取WAR文件
下面,我们以获取示例文件来启动我们刚才构建好的镜像:
docker run -it --name fetcher nianshi/fetcher https://tomcat.apache.org/tomcat-7.0-doc/appdev/sample/sample.war# --2018-02-18 01:55:13-- https://tomcat.apache.org/tomcat-7.0-doc/appdev/sample/sample.war# Resolving tomcat.apache.org (tomcat.apache.org)... 140.211.11.105, 195.154.151.36, 2001:bc8:2142:300::# Connecting to tomcat.apache.org (tomcat.apache.org)|140.211.11.105|:443... connected.# HTTP request sent, awaiting response... 200 OK# Length: 4606 (4.5K)# Saving to: 'sample.war'## sample.war # 100%[===================================================================>] 4.50K --.-KB/s in 0s## 2018-02-18 01:55:15 (217 MB/s) - 'sample.war' saved [4606/4606]
我们可以在Docker容器中切换到/var/lib/tomcat7/webapps/目录下,我们可以找到刚刚下载得到的sample.war文件。
同时,我们也可以在宿主机上找到该文件:
docker inspect -f "{{ .Mounts }}" fetcher# [{642e56f6be0bb520661678cfa61f8c0182d3426a1b44092801bfcc20a154025f /mnt/docker_hub/volumes/642e56f6be0bb520661678cfa61f8c0182d3426a1b44092801bfcc20a154025f/_data /var/lib/tomcat7/webapps local true }]
该命令可以查询fetcher容器的Mounts所在的文件夹。其中,/mnt/docker_hub/volumes/642e56f6be0bb520661678cfa61f8c0182d3426a1b44092801bfcc20a154025f/_data就是该卷在我们实际宿主机的位置。
Tomcat7服务器构建
下面,我们需要继续构建一个Tomcat7服务器镜像。
mkdir tomcat7cd tomcat7touch Dockerfile
修改Dockerfile如下:
FROM ubuntu:16.04MAINTAINER nianshi <[email protected].com>ENV REFRESHED_AT 2018-02-18RUN apt-get -qq updateRUN apt-get -qq install tomcat7 default-jdkENV CATALINA_HOME /usr/share/tomcat7ENV CATALINA_BASE /var/lib/tomcat7ENV CATALINA_PID /var/run/tomcat7.pidENV CATALINA_SH /usr/share/tomcat7/bin/catalina.shENV CATALINA_TMPDIR /tmp/tomcat7-tomcat7-tmpRUN mkdir -p $CATALINA_TMPDIRVOLUME [ "/var/lib/tomcat7/webapps/" ]EXPOSE 8080ENTRYPOINT [ "/usr/share/tomcat7/bin/catalina.sh", "run" ]
下面,我们来构建一下该镜像:
docker build -t nianshi/tomcat7 .
运行WAR文件
下面,我们来启动Tomcat容器来运行一下示例应用吧:
docker run --name tomcat --volumes-from fetcher -d -P nianshi/tomcat7
该命令将会创建一个tomcat容器,并关联fetcher容器中共享的卷。
下面我们来查看一下映射在宿主机的端口
docker port tomcat 8080# 0.0.0.0:32768
接下来,我们在浏览器中打开该页面,页面显示如下:
多容器应用栈
接下来,我们将分享一个使用Express框架的,带有Redis后端的Node.js的应用。
在本应用中,我们需要构建如下一系列镜像:
- 一个Node容器,服务于Node应用
- 一个Redis主容器,用于保存和集群化状态管理
- 两个Redis备份容器,用于集群化应用状态
- 一个日志容器,用于收集日志。
Node.js镜像
首先,同样是需要创建一个新的文件夹用于构建Node.js镜像。
mkdir nodejscd nodejsmkdir -p nodeapptouch Dockerfile
修改Dockerfile文件如下:
FROM ubuntu:16.04MAINTAINER nianshi <[email protected].com>ENV REFRESHED_AT 2018-02-18RUN apt-get -qq updateRUN apt-get -qq install nodejs npmRUN ln -s /usr/bin/nodejs /usr/bin/nodeRUN mkdir -p /var/log/nodeappADD nodeapp /opt/nodeapp/WORKDIR /opt/nodeappRUN npm installVOLUME [ "/var/log/nodeapp" ]EXPOSE 3000ENTRYPOINT [ "nodejs", "server.js" ]
在该Dockerfile文件中,我们使用ADD命令将nodeapp文件夹添加到了容器的/usr/bin/node目录下。其中nodeapp文件夹存储的是配置文件和实际代码。
Ps:代码可以访问http://www.missshi.cn/#/books?_k=xmjul9,搜索Node.js示例服务依赖代码,下载并解压至nodeapp文件夹即可。
其中,server.js文件如下:
var fs = require('fs');var express = require('express'),app = express(),redis = require('redis'),RedisStore = require('connect-redis')(express),server = require('http').createServer(app);var logFile = fs.createWriteStream('/var/log/nodeapp/nodeapp.log', {flags: 'a'});app.configure(function() {app.use(express.logger({stream: logFile}));app.use(express.cookieParser('keyboard-cat'));app.use(express.session({store: new RedisStore({host: process.env.REDIS_HOST || 'redis_primary',port: process.env.REDIS_PORT || 6379,db: process.env.REDIS_DB || 0}),cookie: {expires: false,maxAge: 30 * 24 * 60 * 60 * 1000}}));});app.get('/', function(req, res) {res.json({status: "ok"});});app.get('/hello/:name', function(req, res) {res.json({hello: req.params.name});});var port = process.env.HTTP_PORT || 3000;server.listen(port);console.log('Listening on port ' + port);
下面,我们来构建容器
docker build -t nianshi/nodejs .
Redis基础镜像
下面,我们需要继续构建Redis基础镜像。后续会根据该基础镜像来构建主从镜像。
mkdir redis_basecd redis_basetouch Dockerfile
编写Dockerfile文件如下:
FROM ubuntu:16.04MAINTAINER nianshi <[email protected].com>ENV REFRESHED_AT 2018-02-18RUN apt-get -qq updateRUN apt-get install -qq software-properties-common python-software-propertiesRUN add-apt-repository ppa:chris-lea/redis-serverRUN apt-get -qq updateRUN apt-get -qq install redis-server redis-toolsVOLUME [ "/var/lib/redis", "/var/log/redis" ]EXPOSE 6379CMD []
下面我们来构建Redis基础镜像:
docker build -t nianshi/redis_base .
Redis主镜像
接下来,我们基于Redis基础镜像来构建我们的Redis主镜像。
mkdir redis_primarycd redis_primarytouch Dockerfile
编辑Dockerfile文件如下:
FROM nianshi/redis_baseMAINTAINER nianshi <[email protected].com>ENV REFRESHED_AT 2016-06-01ENTRYPOINT [ "redis-server", "--protected-mode no", "--logfile /var/log/redis/redis-server.log" ]
构建该镜像:
docker build -t nianshi/redis_primary .
Redis从镜像
同样,我们来继续构建构建从镜像:
mkdir redis_slavecd redis_slavetouch Dockerfile
编辑Dockerfile文件如下:
FROM nianshi/redis_baseMAINTAINER nianshi <[email protected].com>ENV REFRESHED_AT 2018-02-18ENTRYPOINT [ "redis-server", "--protected-mode no", "--logfile /var/log/redis/redis-replica.log", "--slaveof redis_primary 6379" ]
构建该镜像:
docker build -t nianshi/redis_slave .
启动Redis后端集群
现在Redis的主从镜像都已经准备好了,我们来启动Redis后端集群。
首先启动主容器:
docker run -d -h redis_primary --name redis_primary nianshi/redis_primary
在该命令中,我们使用了-h命令,-h命令用于设置容器的主机名,从而保证本地DNS可以正确解析。
下面,我们来查看一下容器的日志吧,由于此处我们将容器日志定向到了指定的日志文件夹,而不是标准输出,因此我们不能通过之前的docker logs命令来查看。
而是可以通过如下命令来查看日志:
docker run -it --rm --volumes-from redis_primary ubuntu cat /var/log/redis/redis-server.log
其中,我们启动了另外一个容器,该容器连接至Redis容器的卷用于查询日志,其中--rm命令表示该容器运行结束后自动删除。
日志观察到我们的Redis服务已经正常启动了,下面我们可以继续创建Redis从服务了。
docker run -d -h redis_slave --name redis_slave --link redis_primary:redis_primary nianshi/redis_slave
其中,--link参数用于将redis_primary容器以别名redis_primary连接到Redis从容器。
下面,我们来查看一下新容器的日志:
docker run -it --rm --volumes-from redis_slave ubuntu cat /var/log/redis/redis-replica.log
日志显示我们新启动的容器已经成功连接至Redis主容器。
下面,我们可以再启动第二个Redis从容器来提高服务的可靠性:
docker run -d -h redis_slave2 --name redis_slave2 --link redis_primary:redis_primary nianshi/redis_slave
启动Node容器
目前,我们的后台Redis集群已经可以正常运行了,下面,我们可以启动Node容器了:
docker run -d --name nodeapp -p 4000:3000 --link redis_primary:redis_primary nianshi/nodejs
其中,-p 4000:3000表示将容器的3000端口映射宿主机的4000端口。
此时,使用浏览器打开该页面即可:
输出为
{"status": "ok"}
表示服务目前正常工作,浏览器的会话状态被先记录到Redis的主容器后同步到了两个Redis从服务器上。
捕获应用日志
在生产环境中,我们需要确保可以捕获日志并保存到日志服务器中。
此处,我们以Logstash为例进行讲解,首先需要创建一个Logstash镜像:
mkdir logstashcd logstashtouch Dockerfile
修改Dockerfile如下:
FROM ubuntu:16.04MAINTAINER nianshi <[email protected].com>ENV REFRESHED_AT 2018-02-18RUN apt-get -qq updateRUN apt-get -qq install wgetRUN wget -O - http://packages.elasticsearch.org/GPG-KEY-elasticsearch | apt-key add -RUN echo 'deb http://packages.elasticsearch.org/logstash/1.5/debian stable main' > /etc/apt/sources.list.d/logstash.listRUN apt-get -qq updateRUN apt-get -qq install logstash default-jdkADD logstash.conf /etc/WORKDIR /opt/logstashENTRYPOINT [ "bin/logstash" ]CMD [ "--config=/etc/logstash.conf" ]
此时,创建镜像的过程中用到了一个配置文件logstash.conf,内容如下:
input {file {type => "syslog"path => ["/var/log/nodeapp/nodeapp.log", "/var/log/redis/redis-server.log"]}}output {stdout {codec => rubydebug}}
该配置文件表示logstash会监控两个文件:/var/log/nodeapp/nodeapp.log和/var/log/redis/redis-server.log,将其中新的内容发送给logstash。
下面,我们来构建logstash镜像:
docker build -t nianshi/logstash .
镜像构造完成后,我们可以执行如下命令来启动容器:
docker run -d --name logstash --volumes-from redis_primary --volumes-from nodeapp nianshi/logstash
容器启动后,我们可以执行如下命令来查看logstash容器的输出日志:
docker logs -f logstash
在之前的配置文件中,我们选择将日志进行标准输出,实际在生产环境中,我们更多的选择是会存储到Elasticsearch里。
管理Docker容器的推荐方式
传统上,我们管理机器的方式通常是通过SSH登入运行环境中来管理服务。
但是对于Docker而言,大部分容器内部仅仅只是运行一个进程,因此不推荐这种方式。
如果其中在Docker容器中执行某些命令,更推荐的方式如下:
docker exec [参数] 容器名称 [执行命令]
更多更详细的内容,请访问原创网站:
https://www.missshi.cn/api/view/blog/5a6328110a745f6335000006
Ps:初次访问由于js文件较大,请耐心等候(5s左右)