6.1 使用Jekyll框架和Apache构建应用html
须要构建两个镜像:node
工做流程以下:python
这个例子能够看作是建立一个多主机站点最简单的方法。git
一、Jekyll基础镜像github
创建构建环境:web
mkdir jekyll cd jekyll touch Dockerfile
编写Dockerfile:redis
FROM ubuntu:14.04 MAINTAINER James Turnbull <james@example.com> RUN apt-get -yqq update RUN apt-get -yqq install ruby ruby-dev make nodejs RUN gem install --no-rdoc --no-ri jekyll -v 2.5.3 VOLUME /data VOLUME /var/www/html WORKDIR /data ENTRYPOINT [ "jekyll", "build", "--destination=/var/www/html" ]
注意书上的代码 gem jekyll 后面没有 -v 2.5.3,会报错。docker
在Dockerfile中使用VOLUME指令建立了两个卷:apache
最后,将工做目录指定为/data,并经过ENTRYPOINT指令指定自动构建的命令,这个命令将工做目录/data/中全部的Jekyll网站代码构建到/var/www/html/目录中。npm
二、构建Jekyll基础镜像
docker build -t ivan/jekyll .
三、Apache镜像
创建构建环境:
mkdir apache cd apache touch Dockerfile
编写Dockerfile:
FROM ubuntu:14.04 MAINTAINER James Turnbull <james@example.com> RUN apt-get -yqq update RUN apt-get -yqq install apache2 VOLUME [ "/var/www/html" ] WORKDIR /var/www/html ENV APACHE_RUN_USER www-data ENV APACHE_RUN_GROUP www-data ENV APACHE_LOG_DIR /var/log/apache2 ENV APACHE_PID_FILE /var/run/apache2.pid ENV APACHE_RUN_DIR /var/run/apache2 ENV APACHE_LOCK_DIR /var/lock/apache2 RUN mkdir -p $APACHE_RUN_DIR $APACHE_LOCK_DIR $APACHE_LOG_DIR EXPOSE 80 ENTRYPOINT [ "/usr/sbin/apache2" ] CMD ["-D", "FOREGROUND"]
使用了VOLUME指令建立了一个卷——/var/www/html/,这个目录是用来存放编译后的jekyll网站的;
最后指定了ENTRYPOINT和CMD指令组合来在容器启动时默认运行apache。
四、构建Apache镜像
docker build -t ivan/apache .
五、启动Jekyll网站
首先将Jekyll的一些源代码下载:
cd ~ git clone https://github.com/jamtur01/james_blog.git
而后启动容器:
docker run -v ~/james_blog:/data/ --name james_blog ivan/jekyll
把刚才从github上下载的james_blog目录做为卷挂载到/data(目录里有jekyll网站的源码)。
这里再复习一下卷,卷是在一个或多个容器中特殊指定的目录,卷会绕过联合文件系统,为持久化数据和共享数据提供几个有用的特性:
卷在Docker宿主机的/var/lib/docker/volumes目录中,经过docker inspect命令能够查看某个卷的具体位置:
docker inspect -f "{{.Volumes}}"
若是想在另外一个容器里使用/var/www/html/卷里编译好的网站,能够建立一个新的容器链接到这个卷(即Apache容器):
docker run -d -P --volumes-from james_blog ivan/apache
--volumes-from标志把指定容器里全部的卷加入新建的容器里。
若是删除了最后一个使用卷的容器,卷就被删除了。因此删除有卷的容器时要当心。
这样整个服务就启动好了。
六、备份Jekyll卷
上文说道删除容器可能不当心将卷删除了。因此能够利用一下命令备份卷:
docker run --rm --volumes-from james_blog -v $(pwd):/backup ubuntu tar cvf /backup/james_blog_backup.tar /var/www/html
--rm命令表示该容器只使用一次,运行完后就删除。
将当前目录挂载到/backup,这样在容器中将数据打包到/backup后,该包其实就在当前目录下。
tar cvf命令会建立一个名为james_blog_backup.tar的tar文件(该文件包含了/var/www/html目录里的全部内容)。
七、扩展Jekyll示例
6.2 构建一个Java应用服务(Tomcat)
一、WAR文件获取器
建立构建环境
mkdir fetcher cd fetcher touch Dockerfile
编写Dockerfile
FROM ubuntu:14.04 MAINTAINER Ivan Jiang <ivanjz93@163.com> ENV REFRESHED_AT 2016-07-13 RUN apt-get -yqq update RUN apt-get -yqq install wget VOLUME ["/var/lib/tomcat7/webapps/"] WORKDIR /var/lib/tomcat7/webapps/ ENTRYPOINT ["wget"] CMD ["--help"]
容器执行时,使用wget从指定的URL获取文件,并把它保存在/var/lib/tomcat7/webapps目录。若是运行容器时没有指定URL,ENTRYPOINT和CMD指令组合起来返回wget的帮助。
二、获取WAR文件
docke run -t -i --name sample ivan/fetcher https://tomcat.apache.org/tomcat-7.0-doc/appdev/sample/sample.war
三、构建Tomcat7应用服务器
建立构建环境:
mkdir tomcat7 cd tomcat7 touch Dockerfile
编写Dockerfile:
FROM ubuntu:14.04 MAINTAINER Ivan Jiang <ivanjz93@163.com> ENV REFRESHED_AT 2016-07-13 RUN apt-get -yqq update RUN apt-get -yqq install tomcat7 default-jdk ENV CATALINA_HOME /usr/share/tomcat7 ENV CATALINA_BASE /var/lib/tomcat7 ENV CATALINA_PID /var/run/tomcat7.pid ENV CATALINA_SH /usr/share/tomcat7/bin/catalina.sh ENV CATALINA_TMPDIR /tmp/tomcat7-tomcat7-tmp RUN mkdir -p $CATALINA_TMPDIR VOLUME ["/var/lib/tomcat7/webapps/"] EXPOSE 8080 ENTRYPOINT ["/usr/share/tomcat7/bin/catalina.sh", "run"]
构建镜像:
docker build -t ivan/tomcat7 .
四、运行tomcat7容器
docker run --name sample_app --volumes-from sample -d -P ivan/tomcat7
查看映射到宿主机的端口号:
docker port sample_app 8080
假设端口号是49154,在浏览器访问:IP地址:49154/sample可查看运行的web app。
在例子中做者使用了命令:
docker inspect -f "{{.Volumes}}" sample
查看sample卷的宿主机挂载位置,可是这个命令报错。
OSChina上的TimWang回答个人问题说Docker 1.8将Volumes信息从docker inspect 的输出中删除了,需改用Mounts:
docker inspect -f "{{.Mounts}}" sample
6.3 多容器的应用栈
一、Node.js镜像
建立构建环境:
mkdir nodejs cd nodejs touch Dockerfile
编写Dockerfile:
FROM ubuntu:14.04 MAINTAINER James Turnbull <james@example.com> ENV REFRESHED_AT 2014-06-01 RUN apt-get -yqq update RUN apt-get -yqq install nodejs npm RUN ln -s /usr/bin/nodejs /usr/bin/node RUN mkdir -p /var/log/nodeapp ADD nodeapp /opt/nodeapp/ WORKDIR /opt/nodeapp RUN npm install VOLUME [ "/var/log/nodeapp" ] EXPOSE 3000 ENTRYPOINT [ "nodejs", "server.js" ]
用ln -s创建软链接,把二进制文件modejs链接到node,解决Ubuntu上原有的一些没法向后兼容问题(不懂……)。
构建镜像:
docker build -t ivan/nodejs .
二、Redis基础镜像
mkdir redis_base cd redis_base touch Dockerfile
FROM ubuntu:14.04 MAINTAINER James Turnbull <james@example.com> ENV REFRESHED_AT 2014-06-01 RUN apt-get -yqq update RUN apt-get install -yqq software-properties-common python-software-properties RUN add-apt-repository ppa:chris-lea/redis-server RUN apt-get -yqq update RUN apt-get -yqq install redis-server redis-tools VOLUME [ "/var/lib/redis", "/var/log/redis" ] EXPOSE 6379 CMD []
docker build -t ivan/redis .
三、Redis主镜像
mkdir redis_primary cd redis_primary touch Dockerfile
FROM ivan/redis MAINTAINER James Turnbull <james@example.com> ENV REFRESHED_AT 2014-06-01 ENTRYPOINT [ "redis-server", "--logfile /var/log/redis/redis-server.log" ]
docker build -t ivan/redis_primary
四、Redis从镜像
mkdir redis_replica cd redis_replica touch Dockerfile
FROM ivan/redis MAINTAINER James Turnbull <james@example.com> ENV REFRESHED_AT 2014-06-01 ENTRYPOINT [ "redis-server", "--logfile /var/log/redis/redis-replica.log", "--slaveof redis_primary 6379" ]
docker build -t ivan/redis_replica .
五、建立Redis集群
启动Redis主镜像
docker run -d -h redis-primary --name redis_primary ivan/redis_primary
这里使用-h参数指定容器的主机名,书中给出的是redis_primary,可是执行会报错,应该是新版本的Docker不支持带下划线的主机名,这里改成redis-primary。
启动Redis从服务
docker run -d -h redis-replical --name redis_replical --link redis_primary:redis_primary ivan/redis_replica
这里对-h参数作相同的处理。
六、建立Node容器
docker run -d --name nodeapp -p 3000:3000 --link redis_primary:redis_primary ivan/nodejs
在浏览器中用 宿主机IP:3000测试。
七、捕获应用日志
使用Logstash捕获日志并将日志保存到日志服务器。
mkdir logstash cd logstash touch Dockerfile
FROM ubuntu:14.04 MAINTAINER James Turnbull <james@example.com> ENV REFRESHED_AT 2014-06-01 RUN apt-get -yqq update RUN apt-get -yqq install wget RUN wget -O - http://packages.elasticsearch.org/GPG-KEY-elasticsearch | apt-key add - RUN echo 'deb http://packages.elasticsearch.org/logstash/1.4/debian stable main' > /etc/apt/sources.list.d/logstash.list RUN apt-get -yqq update RUN apt-get -yqq install logstash ADD logstash.conf /etc/ WORKDIR /opt/logstash ENTRYPOINT [ "bin/logstash" ] CMD [ "--config=/etc/logstash.conf" ]
Logstash监控两个文件/var/log/nodeapp/nodeapp.log和/var/log/redis/redis-server.log,将其中的新内容捕获,捕获到的内容输出到标准输出上。现实中通常会将Logstash的内容输出到Elasticsearch集群。
构建以前将做者的logstash.conf下载到构建根目录。
构建镜像
docker build -t ivan/logstash .
启动容器
docker run -d --name logstash --volumes-from redis_primary --volumes-from nodeapp ivan/logstash
查看logstash容器的输出日志
docker logs -f logstash
6.4 不使用SSH管理Docker容器
传统上,使用SSH登入运行环境或者虚拟机管理服务。在Docker里,大部分容器只运行一个进程,因此不能使用这种方法。但是使用卷或者连接完成大部分管理操做。如须要给容器发送信号,可使用docker kill命令:
docker kill -s <signal> <container>
这个操做会发送指定的信号给容器,而不是杀掉容器。
可使用nsenter小工做登入容器,它通常适用于1.2或更早的版本,1.3版本引入的docker exec替换了它大部分的功能。
工具nsenter能够进入Docker用来构成容器的内核命名空间。它能够进入一个已经存在的命名空间,或者在新的一组命名空间里执行一个进程。
能够经过Docker容器安装nsenter:
docker run -v /usr/local/bin:/target jpetazzo/nsenter
这会把nsenter安装到/usr/local/bin目录下。
为了使用nsenter,首先要知道要进入的容器的进程ID,可使用docker inspect命令得到进程PID:
PID=$(docker inspect --format {{.State.Pid}} <container>)
而后运行下面的命令进入容器:
nsenter --target $PID --mount --uts --ipc --net --pid
也能够将容器内执行的命令添加在nsenter命令行的后面:
nsenter --target $PID --mount --uts --ipc --net --pid ls