2019独角兽企业重金招聘Python工程师标准>>>
6.1 使用Jekyll框架和Apache构建应用
需要构建两个镜像:
- 一个镜像安装了Jekyll以及其他用于构建Jekyll网站的必要的软件包;
- 一个镜像通过Apache来让Jekyll网站工作起来。
工作流程如下:
- 创建Jekyll基础镜像和Apache镜像;
- 从Jekyll镜像创建一个容器,这个容器存放通过卷挂载的网站源代码;
- 从Apache镜像创建一个容器,这个容器利用包含编译后的网站的卷为其创建服务;
- 在网站需要更新时,清理并重复上面的步骤。
这个例子可以看做是创建一个多主机站点最简单的方法。
1、Jekyll基础镜像
建立构建环境:
mkdir jekyll
cd jekyll
touch Dockerfile
编写Dockerfile:
FROM ubuntu:14.04
MAINTAINER James Turnbull
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,会报错。
在Dockerfile中使用VOLUME指令创建了两个卷:
- /data/,用于存放网站源代码;
- /var/www/html/,用于存放编译后的Jekyll网站码。
最后,将工作目录指定为/data,并通过ENTRYPOINT指令指定自动构建的命令,这个命令将工作目录/data/中所有的Jekyll网站代码构建到/var/www/html/目录中。
2、构建Jekyll基础镜像
docker build -t ivan/jekyll .
3、Apache镜像
建立构建环境:
mkdir apache
cd apache
touch Dockerfile
编写Dockerfile:
FROM ubuntu:14.04
MAINTAINER James Turnbull
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。
4、构建Apache镜像
docker build -t ivan/apache .
5、启动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标志把指定容器里所有的卷加入新建的容器里。
如果删除了最后一个使用卷的容器,卷就被删除了。所以删除有卷的容器时要小心。
这样整个服务就启动好了。
6、备份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目录里的所有内容)。
7、扩展Jekyll示例
- 运行多个Apache容器,这些容器都使用james_blog容器的卷,在这些Apache容器前面加一个负载均衡器,就拥有了Web集群;
- 进一步构建一个镜像,这个镜像把用户提供的源数据复制到卷里,再把这个卷挂载到jekyll容器里。这就是一个可迁移的通用方案,而且宿主机本地包含任何源代码;
- 在上一个扩展基础上为我们的服务构建一个Web前段,这个服务用语从指定的源自动构建和部署网站。
6.2 构建一个Java应用服务(Tomcat)
1、WAR文件获取器
创建构建环境
mkdir fetcher
cd fetcher
touch Dockerfile
编写Dockerfile
FROM ubuntu:14.04
MAINTAINER Ivan Jiang
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的帮助。
2、获取WAR文件
docke run -t -i --name sample ivan/fetcher https://tomcat.apache.org/tomcat-7.0-doc/appdev/sample/sample.war
3、构建Tomcat7应用服务器
创建构建环境:
mkdir tomcat7
cd tomcat7
touch Dockerfile
编写Dockerfile:
FROM ubuntu:14.04
MAINTAINER Ivan Jiang
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 .
4、运行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 多容器的应用栈
1、Node.js镜像
创建构建环境:
mkdir nodejs
cd nodejs
touch Dockerfile
编写Dockerfile:
FROM ubuntu:14.04
MAINTAINER James Turnbull
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 .
2、Redis基础镜像
mkdir redis_base
cd redis_base
touch Dockerfile
FROM ubuntu:14.04
MAINTAINER James Turnbull
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 .
3、Redis主镜像
mkdir redis_primary
cd redis_primary
touch Dockerfile
FROM ivan/redis
MAINTAINER James Turnbull
ENV REFRESHED_AT 2014-06-01
ENTRYPOINT [ "redis-server", "--logfile /var/log/redis/redis-server.log" ]
docker build -t ivan/redis_primary
4、Redis从镜像
mkdir redis_replica
cd redis_replica
touch Dockerfile
FROM ivan/redis
MAINTAINER James Turnbull
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 .
5、创建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参数做相同的处理。
6、创建Node容器
docker run -d --name nodeapp -p 3000:3000 --link redis_primary:redis_primary ivan/nodejs
在浏览器中用 宿主机IP:3000测试。
7、捕获应用日志
使用Logstash捕获日志并将日志保存到日志服务器。
mkdir logstash
cd logstash
touch Dockerfile
FROM ubuntu:14.04
MAINTAINER James Turnbull
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
这个操作会发送指定的信号给容器,而不是杀掉容器。
可以使用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}} )
然后运行下面的命令进入容器:
nsenter --target $PID --mount --uts --ipc --net --pid
也可以将容器内执行的命令添加在nsenter命令行的后面:
nsenter --target $PID --mount --uts --ipc --net --pid ls