1. Docker镜像生产构建
我们已经了解了Docker的基本概念和管理,下面就开始着手创建自己的第一个Docker镜像,这里我们选用最早pull下来的centos作为基础镜像,然后在上面部署Nginx来做案例讲解。
1.1. 手动构建镜像
手动构建镜像,简单的说就是我们基于一个基础镜像启动一个容器,然后对这个容器进行更改,更改完毕后,进行提交。
1.1.1. 启动容器
运行一个CentOS容器,命名为mynginx
[root@linux-node1 ~]# docker pull centos
[root@linux-node1 ~]# docker run --name mynginx -it centos
在容器里面安装Nginx
[root@2e110e00eef4 /]# rpm -ivh http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm
[root@2e110e00eef4 /]# yum install -y nginx
想要Docker容器保持活跃的状态,需要其中运行的进程不能中断,默认情况下,Nginx会以守护进程的方式启动,这会导致容器只是短暂运行,在守护进程被fork启动后,发起守护进程的原始进程就会退出,这时容器就会停止运行了。所以我们需要将Nginx放在前端运行。
[root@2e110e00eef4 /]# vi /etc/nginx/nginx.conf
#在配置文件最上面增加下面配置
daemon off;
[root@2e110e00eef4 /]# exit
1.1.2. 提交镜像
获取容器ID
[root@linux-node1 ~]# docker ps -a | grep mynginx
2e110e00eef4 centos "/bin/bash" 8 minutes ago Exited (0) 2 minutes ago mynginx
提交修改后的容器为镜像
[root@linux-node1 ~]# docker commit -m "My Nginx" 2e110e00eef4 test/mynginx:v1
sha256:cfd25da2c9c5dd2bcce9e5d2ef4e316b46b5f03617176b97b60a34f2958a6d70
- -m:指定提交的说明信息,类似SVN和Git。
- 之后是用来创建镜像的容器的 ID;
- 最后指定目标镜像的仓库名和标签信息。
查看镜像
[root@linux-node1 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test/mynginx v1 cfd25da2c9c5 49 seconds ago 373MB
nginx latest f09fe80eb0e7 2 weeks ago 109MB
centos latest 1e1148e4cc2c 2 months ago 202MB
从我们创建的镜像运行一个容器
[root@linux-node1 ~]# docker run -d -p 91:80 test/mynginx:v1 nginx
现在你的手动构建的第一个镜像就完成了。你可以用同样的方式安装任意你需要的软件到镜像里面,然后使用镜像来启动一个容器,只要记住容器启动必须要有一个不会退出的进程在执行即可。
1.2. Dockerfile构建
Dockerfile是为快速构建docker image而设计的,当你使用docker build命令的时候,docker 会读取当前目录下的命名为Dockerfile(首字母大写)的纯文本文件并执行里面的指令构建出一个docker image。这比SaltStack的配置管理要简单的多,不过还是要掌握一些简单的指令。
Dockerfile 由一行行命令语句组成,并且支持以#开头的注释行。指令是不区分大小写的,但是通常我们都大写。
下面我们通过构建一个Nginx的镜像来学习Dockerfile的编写。
1.2.1. Nginx Dockerfile实战
注意:第一个指令必须是FROM。
[root@linux-node1 ~]# mkdir -p /opt/dockerfile/mynginx
[root@linux-node1 ~]# cd /opt/dockerfile/mynginx/
[root@test-node1 nginx]# vim Dockerfile
# This docker file uses the centos image
# VERSION 1
# Author: Jason Zhao
# Base image
FROM centos
# Maintainer
MAINTAINER shundong.zhao zhaoshundong@gmail.com
#Commands to update the image
RUN rpm -ivh http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm
RUN yum install -y nginx --enablerepo=epel
ADD index.html /usr/share/nginx/html/index.html
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx"]
为了大家更容易学习,我这里总结一个命令的介绍:
一般的,我们将Dockerfile 分为四部分:
- 基础镜像信息
- 维护者信息
- 镜像操作指令
- 容器启动时执行指令
1.2.2. 常用指令的介绍
构建Dockerfile
注意:ADD index.html就是放一个文件进去,这个文件需要大家自己准备一下。例如:
[root@linux-node1 mynginx]# echo "nginx in docker test" > index.html
使用dokcer build命令构建镜像,最后的.表示当前路径
[root@linux-node1 mynginx]# docker build -t mynginx:v2 .
构建完毕之后,我们就可以Run起来。
[root@linux-node1 ~]# docker run -d -p 92:80 mynginx:v2 nginx
测试访问
[root@linux-node1 mynginx]# curl http://192.168.56.11:92
nginx in docker test
现在你应该发现编写Dockerfile有多么的简单了吧,不过我们还是要系统的再来学习一遍。
1.2.3. Dockerfile命令详解
下面我们来分别介绍下上面使用到的命令:
FROM
格式:FROM
MAINTAINER
格式:MAINTAINER
RUN
格式:RUN
CMD
格式:
CMD ["executable","param1","param2"] 使用 exec 执行,推荐方式; CMD command param1 param2 在 /bin/sh 中执行,提供给需要交互的应用; CMD ["param1","param2"] 提供给ENTRYPOINT的默认参数;
解释:
CMD指定容器启动是执行的命令,每个Dockerfile只能有一条CMD命令,如果指定了多条,只有最后一条会被执行。如果你在启动容器的时候也指定的命令,那么会覆盖Dockerfile构建的镜像里面的CMD命令。
ENTRYPOINT
格式:
ENTRYPOINT ["executable", "param1", "param2"] ENTRYPOINT command param1 param2(shell中执行)。 解释:和CMD类似都是配置容器启动后执行的命令,并且不可被 docker run提供的参数覆盖。
每个 Dockerfile 中只能有一个ENTRYPOINT,当指定多个时,只有最后一个起效。ENTRYPOINT没有CMD的可替换特性,也就是你启动容器的时候增加运行的命令不会覆盖ENTRYPOINT指定的命令。
所以生产实践中我们可以同时使用ENTRYPOINT和CMD,例如:
ENTRYPOINT ["/usr/bin/rethinkdb"]
CMD ["--help"]
USER
格式:USER daemon 解释:指定运行容器时的用户名和UID,后续的RUN指令也会使用这里指定的用户。
EXPOSE
格式:EXPOSE
ENV
格式:ENV
ADD
格式:
ADD
所有拷贝到container中的文件和文件夹权限为0755,uid和gid为0
如果文件是可识别的压缩格式,则docker会帮忙解压缩
VOLUME
格式:VOLUME ["/data"] 解释:可以将本地文件夹或者其他container的文件夹挂载到container中。
WORKDIR
格式:WORKDIR /path/to/workdir 解释:切换目录,为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录。可以多次切换(相当于cd命令),也可以使用多个 WORKDIR指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。 例如:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
则最终路径为 /a/b/c。
ONBUILD
ONBUILD 指定的命令在构建镜像时并不执行,而是在它的子镜像中执行
ARG
格式:ARG
1.3. Docker镜像生产规划实践
现在我们学会了如何使用Dockerfile来构建镜像,没错,真正生产环境我们也是大规模的使用Dockerfile。那么我们应该如何入手呢?
首先我们需要参考一些别人编写的Dockerfile,学习一些规范和技巧,可以来这里找找答案:https://github.com/dockerfile。
可以参考网友编写的Dockerfile的技巧和方法,那么真正的生产环境,肯定要根据自己公司或者团队的技术栈来构建不同的Docker镜像,根据Docker镜像的分层观念,我们可以在这个基础上对我们的镜像进行分层。
- 系统层
- 运行环境层
- 应用服务层
案例如下:
[root@linux-node1 ~]# mkdir /opt/dockerfile
[root@linux-node1 ~]# cd /opt/dockerfile/
[root@linux-node1 dockerfile]# mkdir system runtime app
[root@linux-node1 dockerfile]# tree
.
├── app
├── runtime
└── system
1.4. 基础系统镜像
1.4.1. CentOS系统镜像
默认的官方CentOS镜像有很多常用的命令并不提供,可以根据企业需求进行定制。需要注意的是使用yum安装完毕后,记得执行yum clean all。
因为yum会把下载的软件包和header存储在cache中,而不会自动删除。如果我们觉得它们占用了磁盘空间,可以使用yum clean all指令进行清除,可以减少镜像的大小。
[root@linux-node1 ~]# cd /opt/dockerfile/system/
[root@linux-node1 system]# mkdir centos
1.编写Dockerfile
[root@linux-node1 system]# vim centos/Dockerfile
# Docker for CentOS
#Base image
FROM centos
#Who
MAINTAINER Jason.Zhao xxx@gmail.com
#Base pkg
RUN rpm -ivh http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm
RUN yum install -y wget mysql-devel supervisor git redis tree net-tools sudo psmisc && yum clean all
2.构建镜像
[root@linux-node1 system]# docker build -t unixhot/centos ./centos/
3.查看镜像
[root@linux-node1 system]# docker images | grep unixhot
unixhot/centos latest 4edcb790dacf 24 seconds ago 303 MB
4.使用镜像创建容器
[root@linux-node1 system]# docker run --name mycentos -it unixhot/centos
/bin/bash
[root@b137b1cdd3ac /]# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.3 0.0 11776 1872 ? Ss 03:56 0:00 /bin/bash
root 15 0.0 0.0 47424 1668 ? R+ 03:56 0:00 ps aux
1.4.2. CentOS系统镜像包含SSH
在很多时候如果你需要在CentOS里面启动sshd服务,那么就需要安装openssh-server并且重新生成SSH的主机密钥。同时如果,你需要给镜像设置一个密码,可以使用chpasswd非交互的方式来进行。
[root@linux-node1 ~]# cd /opt/dockerfile/system/
[root@linux-node1 system]# mkdir centos-ssh
1.编写Dockerfile
[root@linux-node1 system]# vim centos-ssh/Dockerfile
# Docker for CentOS
#Base image
FROM centos
#Who
MAINTAINER Jason.Zhao xxx@gmail.com
#EPEL
RUN rpm -ivh http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm
#Base pkg
RUN yum install -y openssh-clients openssl-devel openssh-server wget mysql-devel
supervisor git redis tree net-tools sudo psmisc && yum clean all
# For SSHD
RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
RUN ssh-keygen -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key
RUN ssh-keygen -A -t dsa -f /etc/ssh/ssh_host_dsa_key
# Set root password
RUN echo "root:unixhot.com" | chpasswd
2.构建镜像
[root@linux-node1 system]# docker build -t unixhot/centos-ssh:v1 ./centos-ssh/
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM centos
---> 980e0e4c79ec
Step 2 : MAINTAINER Jason.Zhao xxx@gmail.com
---> **Using cache**
---> d08da8648d91
Step 3 : RUN rpm -ivh
http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm
---> **Using cache**
---> ad9a05bcfb78
…(省略后面输出)
这里有一个小技巧,为了加快构建的速度,注意到上面前三步的输出Using cache,因为Docker镜像的分层原理,已经构建过的layer不会重复构建。
3.查看镜像
[root@linux-node1 system]# docker images | grep unixhot
unixhot/centos-ssh v1 ff1ab7d7e7f4 19 seconds ago 304 MB
unixhot/centos latest 4edcb790dacf 2 hours ago 303 MB
4.使用镜像创建容器
[root@linux-node1 system]# docker run -d --name centos-ssh-demo -p 8022:22
unixhot/centos-ssh:v1 /usr/sbin/sshd -D
5f5bbe98a17c620f91a7d3e68a605a4bcbd48621f32aaa095245e59a8691e229
[root@linux-node1 system]# docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5f5bbe98a17c unixhot/centos-ssh:v1 "/usr/sbin/sshd -D" 5 seconds ago Up 2
seconds 0.0.0.0:8022->22/tcp centos-ssh-demo
5.使用ssh连接容器
[root@linux-node1 ~]# ssh -p 8022 root@192.168.56.11
root@192.168.56.11's password:
[root@5f5bbe98a17c ~]# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 82560 3600 ? Ss 05:46 0:00 /usr/sbin/sshd -D
root 7 0.0 0.0 11636 1116 ? Ss 05:47 0:00 /bin/bash
root 13 0.0 0.0 11636 1116 ? Ss 05:48 0:00 /bin/bash
root 63 0.7 0.0 139272 5424 ? Ss 05:51 0:00 sshd: root@pts/0
root 65 0.5 0.0 13376 1988 pts/0 Ss 05:51 0:00 -bash
root 78 0.0 0.0 49024 1808 pts/0 R+ 05:51 0:00 ps aux
Good Job,你现在可以通过ssh连接到自己创建的Docker容器上了。但是有没有发现一个问题,Docker只允许在前台运行一个进程,那已经运行了sshd。那么如何运行别的进程呢。显然我们做一个能ssh的镜像,目的就是为了在上面运行各种服务。怎么办呢?有很多解决方案。例如你编写一个脚本start.sh。可以在里面写多个服务的启动命令,只要start.sh不执行完毕,即可。当然真正生产推荐的是使用Supervisor。
1.4.3. 使用Supervisor管理进程
Supervisor (http://supervisord.org) 是一个用 Python 写的进程管理工具,可以很方便的用来启动、重启、关闭进程(不仅仅是 Python 进程)。除了对单个进程的控制,还可以同时启动、关闭多个进程。
Supervisor 可以运行在 Linux、Mac OS X 上。如前所述,supervisor 是 Python 编写的,所以安装起来也很方便,可以直接用 pip :
pip install supervisor
现在让我们重新创建一个新的不启动ssh的容器,来实验下supervisor的安装和相关配置。以便于,我们编写Dockerfile。
1.启动测试supervisor的docker容器
[root@linux-node1 system]# docker run --rm -it unixhot/centos-ssh:v1 /bin/bash
[root@98aced104917 /]# ls -l /etc/supervisord*
-rw-r--r-- 1 root root 7953 Aug 20 2015 /etc/supervisord.conf
/etc/supervisord.d:
total 0
注意,在上一小节的镜像构建中,我们已经使用yum安装了supervisord。supervisor的配置文件为/etc/supervisord.conf。
2.Supervisor配置
Supervisor 相当强大,提供了很丰富的功能,不过我们可能只需要用到其中一小部分。安装完成之后,可以编写配置文件,来满足自己的需求。为了方便,我们把配置分成两部分:supervisord(supervisor 是一个 C/S 模型的程序,这是 server 端,对应的有 client 端:supervisorctl)和应用程序(即我们要管理的程序)。
[root@98aced104917 /]# vi /etc/supervisord.conf
#下面并没有列出完整的supervisord.conf的配置,默认注释掉的内容,请自行查看。
**[unix_http_server]**
# file=/var/run/supervisor/supervisor.sock #UNIX socket 文件,supervisorctl
会使用
**[supervisord]**
logfile=/var/log/supervisor/supervisord.log #主要日志文件,默认位置是
$CWD/supervisord.log
logfile_maxbytes=50MB #日志文件大小,超出会 rotate,默认 50MB
logfile_backups=10 #日志文件保留备份数量默认 10
loglevel=info #日志级别,默认 info,其它: debug,warn,trace
pidfile=/var/run/supervisord.pid #pid文件位置
**nodaemon=true #注意需要修改的为此处,将supervisor放在前台运行。**
minfds=1024 #可以打开的文件描述符的最小值,默认 1024
minprocs=200 #可以打开的进程数的最小值,默认 200
**[supervisorctl]**
#通过 UNIX socket 连接 supervisord,路径与 unix_http_server 部分的 file 一致
serverurl=unix:///var/run/supervisor/supervisor.sock ; use a unix:// URL for a
unix socket
**[include]**
#包含supervisord.d目录下*.ini文件,也就是说,我们可以将需要启动应用程序的配置放在这个目录下,这就是运维标准化。
files = supervisord.d/*.ini
3. program 配置
supervisor主配置文件的变更只需要修改nodaemon=true,可以看到关于程序的配置在主配置文件里面都是注释掉的:
;[program:theprogramname]
;command=/bin/cat ; the program (relative uses PATH, can take args)
;process_name=%(program_name)s ; process_name expr (default %(program_name)s)
;numprocs=1 ; number of processes copies to start (def 1)
;directory=/tmp ; directory to cwd to before exec (def no cwd)
;umask=022 ; umask for process (default None)
;priority=999 ; the relative start priority (default 999)
;autostart=true ; start at supervisord start (default: true)
;autorestart=true ; retstart at unexpected quit (default: true)
(省略部分输出)
我们现在要做的事情,就是编写一个sshd的程序管理配置,放置到/etc/supervisord.d目录下,注意文件后缀是.ini,你可以复制上面注释掉的内容进行修改:
[root@98aced104917 /]# vi /etc/supervisord.d/sshd.ini
[program:sshd]
command=/usr/sbin/sshd -D
process_name=%(program_name)s
autostart=true
stdout_logfile_maxbytes=100MB
stdout_logfile_backups=10
4.启动supervisord
[root@98aced104917 ~]# /usr/bin/supervisord -c /etc/supervisord.conf
[root@98aced104917 ~]# netstat -ntlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 31/sshd
tcp6 0 0 :::22 :::* LISTEN 31/sshd
可以看到sshd已经启动了。是由supervisor进行启动的。
5. 使用 supervisorctl
Supervisorctl 是 supervisord 的一个命令行客户端工具,可以用来管理supervisord启动的进程。
[root@98aced104917 ~]# supervisorctl status
sshd RUNNING pid 31, uptime 0:02:35
停止与启动sshd
[root@98aced104917 ~]# supervisorctl stop sshd
sshd: stopped
[root@98aced104917 ~]# supervisorctl status
sshd STOPPED Oct 14 06:45 AM
[root@98aced104917 ~]# supervisorctl start sshd
sshd: started
[root@98aced104917 ~]# supervisorctl status
sshd RUNNING pid 42, uptime 0:00:03
6..将supervisord.conf配置文件scp到宿主机目录下,和Dockerfile同目录
[root@98aced104917 /]# scp /etc/supervisord.conf
192.168.56.11:/opt/dockerfile/system/centos-ssh/
[root@98aced104917 ~]# scp /etc/supervisord.d/sshd.ini
192.168.56.11:/opt/dockerfile/system/centos-ssh/
好的,可以退出容器,这个容器的生命周期结束。
1.4.4. 标准化CentOS系统镜像
现在我们来构建一个全新的包含ssh的centos系统镜像。同时如果你不需要ssh。你依然可以使用supervisor连进行进程管理。
[root@linux-node1 ~]# cd /opt/dockerfile/system/centos-ssh/
[root@linux-node1 centos-ssh]# ls -l
total 16
-rw-r--r-- 1 root root 554 Oct 14 01:40 Dockerfile
-rw-r--r-- 1 root root 142 Oct 14 02:53 sshd.ini
-rw-r--r-- 1 root root 7953 Oct 14 02:30 supervisord.conf
1.重新编写Dockerfile
[root@linux-node1 centos-ssh]# vim Dockerfile
# Docker for CentOS
#Base image
FROM centos
#Who
MAINTAINER Jason.Zhao xxx@gmail.com
#EPEL
RUN rpm -ivh http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm
#Base pkg
RUN yum install -y openssh-clients openssl-devel openssh-server wget mysql-devel
supervisor git redis tree net-tools sudo psmisc && yum clean all
# For SSHD
RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
RUN ssh-keygen -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key
RUN ssh-keygen -A -t dsa -f /etc/ssh/ssh_host_dsa_key
# Set root password
RUN echo "root:unixhot.com" | chpasswd
#Supervisord config
ADD supervisord.conf /etc/supervisord.conf
ADD sshd.ini /etc/supervisord.d/sshd.ini
# Outside Port
EXPOSE 22
#supervisord start
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
2.构建镜像
[root@linux-node1 centos-ssh]# docker build -t unixhot/centos-ssh:latest .
3.查看镜像
[root@linux-node1 centos-ssh]# docker images | grep unixhot
unixhot/centos-ssh latest 0d4b39f9100e 31 seconds ago 304 MB
unixhot/centos-ssh v1 ff1ab7d7e7f4 About an hour ago 304 MB
unixhot/centos latest 4edcb790dacf 3 hours ago 303 MB
4.构建容器
[root@linux-node1 ~]# docker run -d --name centos-ssh-supervisor -p 2222:22
unixhot/centos-ssh
9ebf53b3cacd093c11a9b5773c5fc62875626061f3f3fe0b5380013c78f2f15b
[root@linux-node1 ~]# docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9ebf53b3cacd unixhot/centos-ssh "/usr/bin/supervisord" 6 seconds ago Up 3
seconds 0.0.0.0:2222->22/tcp centos-ssh-supervisor
5.使用ssh连接容器
[root@linux-node1 ~]# ssh root@192.168.56.11 -p 2222
root@192.168.56.11's password:
[root@9ebf53b3cacd ~]# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.3 0.0 117248 14668 ? Ss 07:25 0:00 /usr/bin/python /usr/bin/supervisord
-c /etc/supervisord.conf
root 9 0.0 0.0 82560 3608 ? S 07:25 0:00 /usr/sbin/sshd -D
root 25 1.2 0.0 139272 5428 ? Ss 07:26 0:00 sshd: root@pts/0
root 27 0.0 0.0 13376 1984 pts/0 Ss 07:27 0:00 -bash
root 40 0.0 0.0 49024 1808 pts/0 R+ 07:27 0:00 ps aux
操作系统标准化约定:
统一使用supervisor进行进程的管理
所有Docker容器,使用centos-ssh镜像时提供supervisor的ini配置文件。
1.5. 运行环境镜像
现在有了系统镜像,当然文中的知识案例,具体的情况可以根据你所在企业的具体情况进行相关的配置,现在我们可以在系统镜像的基础上来构建运行环境。
1.5.1. Java 运行环境
Java环境在生产中非常常见,例如会启动一个单一的Java小进程处理队列里面的内容,只需要有JDK即可。那首先我们先构建一个只包含JDK的运行环境:
[root@linux-node1 ~]# cd /opt/dockerfile/runtime/
[root@linux-node1 runtime]# mkdir java
Java运行环境要基于centos-ssh的镜像来操作,所以先运行一个临时容器来构建Java运行环境,然后把操作步骤转换为Dockerfile。
[root@linux-node1 ~]# docker run --rm -it unixhot/centos-ssh /bin/bash
[root@ec2696c0f9f4 /]# yum install -y java-1.8.0-openjdk
java-1.8.0-openjdk-devel
使用yum安装的openjdk,默认可以不设置JAVA_HOME,即可执行相关的java程序。
1.编写Dockerfile
[root@linux-node1 runtime]# vim java/Dockerfile
# Docker for CentOS
#Base image
FROM unixhot/centos-ssh
#Who
MAINTAINER Jason.Zhao xxx@gmail.com
#Base pkg
RUN yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel && yum clean all
# JAVA_HOME
ENV JAVA_HOME /usr/lib/jvm/java-1.8.0-openjdk
# Outside Port
EXPOSE 22
#supervisord start
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
2.构建镜像
[root@linux-node1 runtime]# docker build -t unixhot/runtime-java ./java/
3.查看镜像
[root@linux-node1 ~]# docker images | grep unixhot
unixhot/runtime-java latest ff3a2bb0b2a9 2 minutes ago 505.1 MB
unixhot/centos-ssh latest 02d375a33cd1 38 minutes ago 304 MB
unixhot/centos-ssh v1 ff1ab7d7e7f4 2 hours ago 304 MB
unixhot/centos latest 4edcb790dacf 4 hours ago 303 MB
当然十分可以使用,需要大家去运行一个小程序进行测试,这里就不演示了。
1.5.2. Tomcat运行环境
相对于单一使用Java命令启动Java服务,Tomcat
1.启动centos-ssh镜像的实例
[root@linux-node1 ~]# docker run --rm -it unixhot/centos-ssh /bin/bash
[root@80f529b965ef /]# yum install -y java-1.8.0-openjdk
java-1.8.0-openjdk-devel
为什么不直接使用Java的运行环境构建Tomcat?
首先,确实可以直接使用Java的运行环境来进行Tomcat的构建,但是会破坏我们系统层、运行环境层、业务层的架构,也就是将依赖关系复杂话了,这要根据企业自身情况来做。我先说说我的理由:
如果Tomcat使用的Java版本要升级到JDK 1.8版本,而Java运行环境的版本是1.7,不能升级,因为有很多服务就是在JDK 1.7的环境下开发的,未进行迁移。这个时候,如果Tomcat是基于Java 1.7的运行环境构建的,就出现依赖关系了。所以,仅仅是个人的建议:让问题简单化!
2.安装部署Tomcat 8
[root@80f529b965ef /]# cd /usr/local/src
[root@80f529b965ef src]# wget http://mirrors.cnnic.cn/apache/tomcat/tomcat-8/v8.5.6/bin/apache-tomcat-8.5.6.tar.gz
[root@80f529b965ef src]# tar zxf apache-tomcat-8.5.6.tar
[root@80f529b965ef src]# mv apache-tomcat-8.5.6 /usr/local/
[root@80f529b965ef src]# ln -s /usr/local/apache-tomcat-8.5.6/
/usr/local/tomcat
3.使用supervisor启动tomcat
[root@897cc10b8adf ~]# cd /etc/supervisord.d/
[root@897cc10b8adf supervisord.d]# vi tomcat.ini
[program:tomcat]
command=/usr/local/tomcat/bin/catalina.sh run
process_name=%(program_name)s
autostart=true
stdout_logfile_maxbytes=100MB
stdout_logfile_backups=10
更新supervisor并查看tomcat状态
[root@897cc10b8adf supervisord.d]# supervisorctl update
tomcat: stopped
tomcat: updated process group
[root@897cc10b8adf supervisord.d]# supervisorctl status
sshd RUNNING pid 408, uptime 0:06:00
tomcat RUNNING pid 704, uptime 0:00:03
4.准备Dockerfile的构建环境
现在我们开始编写Dockerfile,在编写之前,我们先决定一件事情,apache-tomcat的包,我们应该是像实验中使用wget进行下载并解压呢,还是应该提前将包放在宿主机,构建的时候使用Dockerfile ADD进行呢。
我建议选择后者,因为实际的工作中,我们通常要对Tomcat进行很多定制化的配置,例如进行安全规范的调整等。
这里我们能演示的就是直接下载,可以想象一下,这个已经是你修改过的软件包。
[root@linux-node1 ~]# cd /opt/dockerfile/runtime/
[root@linux-node1 runtime]# mkdir tomcat
[root@linux-node1 runtime]# cd tomcat/
[root@linux-node1 tomcat]# wget http://mirrors.cnnic.cn/apache/tomcat/tomcat-8/v8.5.6/bin/apache-tomcat-8.5.6.tar.gz
5.编写Dockerfile
# Docker for CentOS
#Base image
FROM unixhot/centos-ssh
#Who
MAINTAINER Jason.Zhao xxx@gmail.com
#Base pkg
RUN yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel && yum clean all
# JAVA_HOME
ENV JAVA_HOME /usr/lib/jvm/java-1.8.0-openjdk
# Tomcat
ADD apache-tomcat-8.5.6.tar.gz /usr/local/
RUN ln -s /usr/local/apache-tomcat-8.5.6 /usr/local/tomcat
ADD tomcat.ini /etc/supervisord.d/tomcat.ini
ENV TOMCAT_HOME /usr/local/tomcat
# Outside Port
EXPOSE 22 8080
#supervisord start
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
6.构建tomcat镜像
[root@linux-node1 tomcat]# ls -l
total 9096
-rw-r--r-- 1 root root 9304958 Oct 6 16:39 apache-tomcat-8.5.6.tar.gz
-rw-r--r-- 1 root root 546 Oct 14 05:51 Dockerfile
-rw-r--r-- 1 root root 165 Oct 14 06:33 tomcat.ini
[root@linux-node1 tomcat]# docker build -t unixhot/runtime-tomcat .
7.查看镜像
[root@linux-node1 tomcat]# docker images | grep unixhot
unixhot/runtime-tomcat latest ef8372a88ad4 6 minutes ago 518.2 MB
unixhot/runtime-java latest ff3a2bb0b2a9 2 hours ago 505.1 MB
unixhot/centos-ssh latest 02d375a33cd1 3 hours ago 304 MB
unixhot/centos-ssh v1 ff1ab7d7e7f4 4 hours ago 304 MB
unixhot/centos latest 4edcb790dacf 6 hours ago 303 MB
8.构建tomcat-demo容器
[root@linux-node1 tomcat]# docker run -d --name tomcat-demo -p 88:8080 -p
89:22 unixhot/runtime-tomcat
8cea3ef85634210eb0cfab0f65a63b0ebd6961b5a67fc765b762785c01cd2c18
[root@linux-node1 tomcat]# docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8cea3ef85634 unixhot/runtime-tomcat "/usr/bin/supervisord" 5 seconds ago Up 2
seconds 0.0.0.0:89->22/tcp, 0.0.0.0:88->8080/tcp tomcat-demo
9.访问tomcat
http://192.168.56.11:88/
1.6. 应用镜像
最上层的业务镜像就是在运行环境的基础上,直接使用或者根据业务情况再次进行二次定制。例如对于PHP运行环境,默认只安装基础通用的模块,对于业务需要的时候再次进行二次构建。这里我们拿一个比较简单的Jenkins来模拟。
1.6.1. Jenkins镜像构建
JenkinsJenkins是基于Java开发的一种持续集成工具,它有自己的yum安装方式,但是最直接的还是直接下载war包存放到tomcat目录下。
[root@linux-node1 ~]# cd /opt/dockerfile/app/
[root@linux-node1 app]# mkdir jenkins
[root@linux-node1 app]# cd jenkins/
[root@linux-node1 jenkins]# wget http://mirrors.jenkins-ci.org/war/latest/jenkins.war
2.编写Dockerfile
# Docker for CentOS
#Base image
FROM unixhot/runtime-tomcat
#Who
MAINTAINER Jason.Zhao xxx@gmail.com
# Jenkins
ADD jenkins.war /usr/local/tomcat/webapps/
# Outside Port
EXPOSE 22 8080
#supervisord start
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
3.构建jenkins镜像
[root@linux-node1 jenkins]# docker build -t unixhot/jenkins .
4.查看镜像
[root@linux-node1 jenkins]# docker images | grep unixhot
unixhot/jenkins latest a7b16bc093ed 40 seconds ago 588.7 MB
unixhot/runtime-tomcat latest 3b596c9a7696 2 hours ago 518.8 MB
unixhot/runtime-java latest 476a6a2fc074 2 hours ago 505.8 MB
unixhot/centos-ssh latest d85aa8a9dfd4 3 hours ago 304.6 MB
unixhot/centos-ssh v1 3309208ed679 4 hours ago 300.9 MB
unixhot/centos latest 83f0491a30e1 4 hours ago 282.3 MB
5.创建jenkins-demo容器
[root@linux-node1 jenkins]# docker run -d --name jenkins-demo -p 91:22 -p
92:8080 unixhot/jenkins
6d88ba756befc84ef1820818994be028c12ac13825c2b439d680358d101e922e
[root@linux-node1 jenkins]# docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6d88ba756bef unixhot/jenkins "/usr/bin/supervisord" 7 seconds ago Up 2 seconds
0.0.0.0:91->22/tcp, 0.0.0.0:92->8080/tcp jenkins-demo
6.访问jenkins并进行初始化安装
当然如果你想构建一个启动后直接就可以使用的Jenkins,可以在安装完毕后,把刚才的容器重新提交成为镜像。
7.安装Jenkins并提交成为jenkins-login镜像。
进入镜像或者安装的密码,填入密码框并继续。后面步骤省略。全部安装默认即可。注意安装插件时时间会比较长。
[root@linux-node1 ~]# ./docker_in.sh jenkins-demo
[root@b3bbada66693 /]# cat /root/.jenkins/secrets/initialAdminPassword
b5257b8891464ebf890b38c00d24de71
获取Container ID:
[root@linux-node1 ~]# docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b3bbada66693 unixhot/jenkins "/usr/bin/supervisord" 9 minutes ago Up 9 minutes
0.0.0.0:91->22/tcp, 0.0.0.0:92->8080/tcp jenkins-demo
提交新的镜像
[root@linux-node1 ~]# docker commit -m "jenkins login" b3bbada66693
unixhot/jenkins-login
[root@linux-node1 ~]# docker images | grep jenkins
unixhot/jenkins-login latest c453363bb9f7 22 seconds ago 776.6 MB
unixhot/jenkins latest a7b16bc093ed 13 minutes ago 588.7 MB
8.使用jenkins-login镜像构建容器
现在可以使用的Jenkins容器诞生了,赶紧创建一个容器来进行使用吧。
[root@linux-node1 ~]# docker run -d --name jenkins-login -p 93:22 -p 94:8080
unixhot/jenkins-login
af421b4d5b076c756ab58ed8cc95c615a321f9517d30b755667d5d80d1336a63
[root@linux-node1 ~]# docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
af421b4d5b07 unixhot/jenkins-login "/usr/bin/supervisord" 4 seconds ago Up 2
seconds 0.0.0.0:93->22/tcp, 0.0.0.0:94->8080/tcp jenkins-login
9.访问Jenkins容器,开始持续集成之路