镜像是部署应用的基石
镜像是什么?
1.一个分层存储的文件:优点 - 易于扩展、优化存储空间
2.一个软件的环境
3.一个镜像可以创建N个容器
4.一种标准化的交付
镜像不是一个单一的文件,而是有多层构成。可以通过docker history <ID/NAME>查看镜像中各层内容及大小,每层对应着Dockerfile中的一条指令。Docker镜像默认存储在/var/lib/docker/<stoage-driver>
[root@node1 ~]# docker history nginx
IMAGE CREATED CREATED BY SIZE COMMENT
881bd08c0b08 47 hours ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
<missing> 47 hours ago /bin/sh -c #(nop) STOPSIGNAL SIGTERM 0B
<missing> 47 hours ago /bin/sh -c #(nop) EXPOSE 80 0B
<missing> 47 hours ago /bin/sh -c ln -sf /dev/stdout /var/log/nginx… 22B
<missing> 47 hours ago /bin/sh -c set -x && apt-get update && apt… 54MB
<missing> 47 hours ago /bin/sh -c #(nop) ENV NJS_VERSION=1.15.9.0.… 0B
<missing> 47 hours ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.15.9-… 0B
<missing> 47 hours ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do… 0B
<missing> 2 days ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 2 days ago /bin/sh -c #(nop) ADD file:5ea7dfe8c8bc87ebe… 55.3MB
容器其实是在镜像的最上面加了一层读写层,在运行容器里文件改动时,会先从镜像里要写的文件复制到容器自己的文件系统中,都会写到这个读写层。如果容器删除了,最上面的读写层也就删除了,改动也就丢失了。所以无论多少个容器共享一个镜像,所做的写操作都是从镜像的文件系统中复制过来操作的,并不会修改镜像的源文件,这种方式提高磁盘利用率。若想持久化这些改动,可以通过docker commit 将容器保存成一个新镜像。
镜像管理常用命令
这个是我在生产环境中的
root@ *node* @ntp_server:/root# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
harbor:5000/skyform/admin v1 c93560620a93 2 weeks ago 535 MB
harbor:5000/skyform/zabbixdataenhancer v1 2c6739c0cf27 2 weeks ago 184 MB
harbor:5000/skyform/zabbixcmdb v1 48a3488927ec 2 weeks ago 172 MB
harbor:5000/skyform/sys v1 766323bdcfc3 3 months ago 171 MB
harbor:5000/skyform/opssync v1 03bee45d59dc 3 months ago 149 MB
harbor:5000/skyform/project v1 0f421139d3cf 3 months ago 172 MB
harbor:5000/skyform/monitorconfig v1 52b857337569 3 months ago 146 MB
harbor:5000/skyform/zabbixcollector v1 abb70d74539d 3 months ago 184 MB
harbor:5000/skyform/alarm v1 6afdd9ab41ed 3 months ago 168 MB
harbor:5000/skyform/patrolmanagement v1 4b1f14799f6b 3 months ago 155 MB
harbor:5000/skyform/rms v1 59facfc8aefa 3 months ago 176 MB
docker.io/grafana/grafana latest 7a7874a2caba 4 months ago 223 MB
docker.io/redis latest 0a153379a539 5 months ago 83.4 MB
musingtec/tomcat8/jdk8 v7.0 521dc0ccb488 8 months ago 983 MB
harbor:5000/skyform/kafkaplugin test 7fda08863238 10 months ago 162 MB
harbor:5000/skyform/sys test bf1de5e30847 10 months ago 171 MB
harbor:5000/skyform/grafana test 45bef2df9ee6 11 months ago 301 MB
harbor:5000/skyform/cas test c8c76fa8ec6f 11 months ago 193 MB
harbor:5000/skyform/kafka v1 c74a25350302 13 months ago 270 MB
harbor:5000/skyform/elasticsearch master 7c52ec727039 13 months ago 580 MB
harbor:5000/skyform/zabbix-server-mysql test fe5e21361029 15 months ago 107 MB
harbor:5000/skyform/zabbix-web-nginx-mysql test 45e29c5dcfb8 17 months ago 176 MB
harbor:5000/skyform/mysql test b4e78b89bcf3 17 months ago 412 MB
harbor:5000/skyform/zookeeper test f2249a75c5d0 17 months ago 143 MB
harbor:5000/skyform/elasticsearch test d1ac13423d3c 18 months ago 580 MB
docker.io/kibana 5.5.2 70e73a80c440 18 months ago 382 MB
pull - 从镜像仓库拉取镜像
[root@node1 ~]# docker image pull
"docker image pull" requires exactly 1 argument.
See 'docker image pull --help'.
Usage: docker image pull [OPTIONS] NAME[:TAG|@DIGEST]
Pull an image or a repository from a registry
[root@node1 ~]# docker image pull busybox
Using default tag: latest
latest: Pulling from library/busybox
697743189b6d: Pull complete
Digest: sha256:061ca9704a714ee3e8b80523ec720c64f6209ad3f97c0ff7cb9ec7d19f15149f
Status: Downloaded newer image for busybox:latest
[root@node1 ~]# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 881bd08c0b08 47 hours ago 109MB
busybox latest d8233ab899d4 2 weeks ago 1.2MB
inspect - 显示一个或多个镜像详细信息
Usage: docker image inspect [OPTIONS] IMAGE [IMAGE...] [flags]
[root@node1 ~]# docker inspect busybox
[
{
"Id": "sha256:d8233ab899d419c58cf3634c0df54ff5d8acc28f8173f09c21df4a07229e1205",
"RepoTags": [
"busybox:latest"
],
...
import - 导入tag归档的容器文件系统创建镜像
Usage: docker image import [OPTIONS] file|URL|- [REPOSITORY[:TAG]] [flags]
docker import test.tar name:tag
[root@localhost ~]# cat name.tar | docker image import – name:tag
save - 保存一个或多个镜像到一个tar归档文件
Usage: docker image save [OPTIONS] IMAGE [IMAGE...] [flags]
[root@localhost ~]# docker image save name/id > name.tar
load - 从tar归档或标准输入导入镜像
Usage: docker image load [OPTIONS]
docker load -i name.tar
docker load < name.tar
save,load命令用于将一个镜像从一台Docker主机到另一台Docker主机。
rm - 移除一个或多个镜像
Usage: docker image rm [OPTIONS] IMAGE [IMAGE...] [flags]
[root@node1 ~]# docker image rm busybox
Untagged: busybox:latest
Untagged: busybox@sha256:061ca9704a714ee3e8b80523ec720c64f6209ad3f97c0ff7cb9ec7d19f15149f
Deleted: sha256:d8233ab899d419c58cf3634c0df54ff5d8acc28f8173f09c21df4a07229e1205
Deleted: sha256:adab5d09ba79ecf30d3a5af58394b23a447eda7ffffe16c500ddc5ccb4c0222f
build - 从Dockerfile构建镜像
tag - 创建一个引用源镜像标记目标镜像
push - 推送一个镜像到镜像仓库
一个Dockerfile遵循特定的格式和指令集。常用指令如下表:
ENTRYPOINT与CMD区别在于ENTRYPOINT可以使用CMD作为参数,通常都是用作启动后台服务。
一个简单的Dockerfile是这样的:
FROM ubuntu:15.04
COPY . /app
RUN make /app
CMD python /app/app.py
FROM 从ubuntu:15.04镜像创建一个图层。
COPY 从Docker客户端的当前目录添加文件。
RUN make构建你的应用用你的应用程序。
CMD 在容器启动时运行的命令。
Docker镜像由只读层组成,每个层都代表一个Dockerfile指令。这些层是堆叠的,每一层都是前一层变化的增量。
Docker Build命令
docker build命令是根据上下文自动构建镜像。构建上下文是指定位置PATH或文件集URL,PATH是本地文件系统上的目录,URL是一个Git仓库地址。
示例:
Usage: docker build [OPTIONS] PATH | URL | - [flags]
# docker build .
# docker build -t shykes/myapp .
# docker build -t shykes/myapp -f /path/Dockerfile /path
# docker build -t shykes/myapp http://www.example.com/Dockerfile
构建由Docker守护程序运行,而不是CLI。构建过程第一件事是将整个上下文(递归)发送到守护进程。建议空目录作为上下文,并将Dockerfile保存在该目录中,目录中仅包含构建Dockerfile所需的文件。
构建PHP网站镜像并部署
建议大家不要直接就上手写Dockerfile,正确方式是先创建一个容器在里面将要做的操作先梳理下,再按照顺序复制到Dockerfile文件。根据我们之前所说的,先准备一个基础镜像,再构建项目镜像。部署过PHP网站的朋友知道,PHP是一个动态程序,负责解析的是一个叫PHP-FPM的服务,而这个服务不支持静态页面处理。一般结合Nginx解决这个问题。Nginx本身是一个静态Web服务器,并不支持解析PHP程序,但它支持了FastCGI接口来调用动态服务来解析PHP程序。
当客户端请求PHP页面时,Nginx通过fastcgi接口转发给本地9000端口的PHP-FPM子进程处理,处理完成后返回Nginx。所以,秉承着一个容器一个服务的原则,咱们要创建两个基础镜像:
Nginx和PHP基础镜像目录结构:
[root@node1 lamp]# tree .
.
├── Dockerfile-nginx
└── Dockerfile-php
0 directories, 2 files
php
[root@node1 lamp]# cat Dockerfile-php
FROM centos:7
MAINTAINER mail.test.com
RUN yum install epel-release -y && \
yum install -y gcc gcc-c++ make gd-devel libxml2-devel \
libcurl-devel libjpeg-devel libpng-devel openssl-devel \
libmcrypt-devel libxslt-devel libtidy-devel autoconf \
iproute net-tools telnet wget curl && \
yum clean all && \
rm -rf /var/cache/yum/*
RUN wget http://docs.php.net/distributions/php-5.6.36.tar.gz && \
tar zxf php-5.6.36.tar.gz && \
cd php-5.6.36 && \
./configure --prefix=/usr/local/php \
--with-config-file-path=/usr/local/php/etc \
--with-config-file-scan-dir=/usr/local/php/etc/php.d \
--enable-fpm --enable-opcache --enable-static=no \
--with-mysql --with-mysqli --with-pdo-mysql \
--enable-phar --with-pear --enable-session \
--enable-sysvshm --with-tidy --with-openssl \
--with-zlib --with-curl --with-gd --enable-bcmath \
--with-jpeg-dir --with-png-dir --with-freetype-dir \
--with-iconv --enable-posix --enable-zip \
--enable-mbstring --with-mhash --with-mcrypt --enable-hash \
--enable-xml --enable-libxml --enable-debug=no && \
make -j 4 && make install && \
cp php.ini-production /usr/local/php/etc/php.ini && \
cp sapi/fpm/php-fpm.conf /usr/local/php/etc/php-fpm.conf && \
sed -i "90a \daemonize = no" /usr/local/php/etc/php-fpm.conf && \
mkdir /usr/local/php/log && \
cd / && rm -rf php*
ENV PATH $PATH:/usr/local/php/sbin
WORKDIR /usr/local/php
EXPOSE 9000
CMD ["php-fpm"]
nginx
[root@node1 lamp]# cat Dockerfile-nginx
FROM centos:7
MAINTAINER mail.test.com
RUN yum install -y gcc gcc-c++ make \
openssl-devel pcre-devel gd-devel libxslt-devel \
iproute net-tools telnet wget curl && \
yum clean all && \
rm -rf /var/cache/yum/*
RUN wget http://nginx.org/download/nginx-1.12.2.tar.gz && \
tar zxf nginx-1.12.2.tar.gz && \
cd nginx-1.12.2 && \
./configure --prefix=/usr/local/nginx \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_realip_module \
--with-http_image_filter_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_secure_link_module \
--with-http_stub_status_module \
--with-stream \
--with-stream_ssl_module && \
make -j 4 && make install && \
mkdir -p /usr/local/nginx/conf/vhost && \
rm -rf /usr/local/nginx/html/* && \
echo "ok" >> /usr/local/nginx/html/status.html && \
cd / && rm -rf nginx-1.12.2*
ENV PATH $PATH:/usr/local/nginx/sbin
WORKDIR /usr/local/nginx
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
构建
[root@node1 lamp]# docker build -t php56 -f Dockerfile-php .
[root@node1 lamp]# docker build -t nginx112 -f Dockerfile-nginx .
有了基础镜像,就可以基于这个镜像封装项目到镜像了。
项目镜像
cd project
# vi Dockerfile-php
FROM php56
COPY wwwroot /wwwroot
CMD ["php-fpm"]
# cat wwwroot/phpinfo.php
<?php phpinfo();?>
docker build -t php56:v1 -f Dockerfile-php01 .
[root@node1 lamp]# cat Dockerfile-nginx01
FROM nginx112
COPY nginx.conf /usr/local/nginx/conf/
[root@node1 lamp]# cat nginx.conf
user nobody;
worker_processes 1;
error_log logs/error.log info;
events {
worker_connections 1024;
}
http {
include mime.types;
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"';
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
}
[root@node1 lamp]# docker build -t nginx112:v1 -f Dockerfile-nginx01 .
以上nginx配置文件,只是在默认配置文件基础上增加了处理PHP的location:
[root@node1 lamp]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx112 v1 bc0cb0604cd6 59 seconds ago 364MB
php v1 b78b979d24cb 3 minutes ago 520MB
nginx112 latest c390f3e1a040 9 minutes ago 364MB
php56 latest 49d4d870a2cd 17 minutes ago 520MB
nginx latest 881bd08c0b08 2 days ago 109MB
centos 7 1e1148e4cc2c 3 months ago 202MB
部署LNMP网
[root@node1 lamp]# docker network create lnmp
e735b1d4b2142f8c7328a899482e2bfbd4f39a023f07b791402ac84422167fa6
[root@node1 lamp]# docker volume create wwwroot
wwwroot
[root@node1 lamp]# docker container run -d --name lnmp_nginx -p 88:80 --net lnmp --mount src=wwwroot,dst=/usr/local/nginx/html nginx112:v1
06b1c36d91f5406d3dabb8ef97b781eea8c1847e2161cfc5abc0bf9ce94c5b25
[root@node1 lamp]# docker container run -d --name lnmp_php --net container:lnmp_nginx --mount src=wwwroot,dst=/usr/local/nginx/html php:v1
db76cd1f5c079fa35da44b774ad187fca77d748fb92338b7c327ba53a7341274
创建PHP容器时指定与Nginx容器同一个网络,这样Nginx就可以代理127.0.0.1:9000到PHP-FPM了。
访问测试:http://192.168.43.199:88
构建JAVA网站镜像并部署
JAVA程序必须有JDK环境,咱们直接把JDK放到宿主机上,容器以挂载形式使用,减少镜像大小及提高性能。
[root@node1 ~]# tar xf jdk-8u151-linux-x64.tar.gz
[root@node1 ~]# mv jdk1.8.0_151 /usr/local/jdk1.8
[root@node1 tomcat]# cat Dockerfile
FROM centos:7
MAINTAINER mail.test.com
ENV VERSION=8.5.37
ENV JAVA_HOME /usr/local/jdk
RUN yum install wget curl unzip iproute net-tools -y && \
yum clean all && \
rm -rf /var/cache/yum/*
RUN wget http://mirrors.shu.edu.cn/apache/tomcat/tomcat-8/v${VERSION}/bin/apache-tomcat-${VERSION}.tar.gz && \
tar zxf apache-tomcat-${VERSION}.tar.gz && \
mv apache-tomcat-${VERSION} /usr/local/tomcat && \
rm -rf apache-tomcat-${VERSION}.tar.gz /usr/local/tomcat/webapps/* && \
mkdir /usr/local/tomcat/webapps/ROOT && \
echo "ok" > /usr/local/tomcat/webapps/ROOT/status.html && \
sed -i '1a JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom"' /usr/local/tomcat/bin/catalina.sh && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
ENV PATH $PATH:/usr/local/tomcat/bin
WORKDIR /usr/local/tomcat
EXPOSE 8080
CMD ["catalina.sh", "run"]
[root@node1 tomcat]# docker build -t tomcat-85 -f Dockerfile .
[root@node1 tomcat]# docker container run -d --name tomcat85test -v /usr/local/jdk1.8/:/usr/local/jdk -p 89:8080 tomcat-85
注意:VERSION=8.5.37 Tomcat版本号会更新,构建前需确保这个版本号可用,否则构建失败。
访问测试:http://192.168.43.199:89/status.html
减少镜像层 一次RUN指令形成新的一层,尽量Shell命令都写在一行,减少镜像层。
例如:
FROM centos:7
MAINTAINER mail.test.com
RUN yum install epel-release -y
RUN yum install -y gcc gcc-c++ make -y
RUN wget http://docs.php.net/distributions/php-5.6.36.tar.gz
RUN tar zxf php-5.6.36.tar.gz
RUN cd php-5.6.36
RUN ./configure --prefix=/usr/local/php
RUN make -j 4
RUN make install
EXPOSE 9000
CMD ["php-fpm"]
应该写成:
FROM centos:7
MAINTAINER mail.163.com
RUN yum install epel-release -y && \
yum install -y gcc gcc-c++ make
RUN wget http://docs.php.net/distributions/php-5.6.36.tar.gz && \
tar zxf php-5.6.36.tar.gz && \
cd php-5.6.36 && \
./configure --prefix=/usr/local/php \
make -j 4 && make install
EXPOSE 9000
CMD ["php-fpm"]
优化镜像大小:清理无用数据
一次RUN形成新的一层,如果没有在同一层删除,无论文件是否最后删除,都会带到下一层,所以要在每一层清理对应的残留数据,减小镜像大小。
FROM centos:7
MAINTAINER mail.test.com
RUN yum install epel-release -y && \
yum install -y gcc gcc-c++ make gd-devel libxml2-devel \
libcurl-devel libjpeg-devel libpng-devel openssl-devel \
libmcrypt-devel libxslt-devel libtidy-devel autoconf \
iproute net-tools telnet wget curl && \
yum clean all && \
rm -rf /var/cache/yum/*
RUN wget http://docs.php.net/distributions/php-5.6.36.tar.gz && \
tar zxf php-5.6.36.tar.gz && \
cd php-5.6.36 && \
./configure --prefix=/usr/local/php \
make -j 4 && make install && \
cd / && rm -rf php*
至少能节省几十M,甚至几百M。
减少网络传输时间
最好在内部有一个存放软件包的地方,类似于上述的PHP官方下载地址:http://docs.php.net/distributions/php-5.6.36.tar.gz 如果用到maven构建这样的操作,同时也更改为私有maven仓库,减少网络传输时间,提高镜像构建速度。
多阶段镜像构建
如果运行一个项目,根据咱们上面的做法,是直接把代码拷贝到基础镜像里,如果是一个需要预先代码编译的项目呢?例如JAVA语言,如何代码编译、部署在一起完成呢!
上面做法需要事先在一个Dockerfile构建一个基础镜像,包括项目运行时环境及依赖库,再写一个Dockerfile将项目拷贝到运行环境中,有点略显复杂了。
像JAVA这类语言如果代码编译是在Dockerfile里操作,还需要把源代码构建进去,但实际运行时只需要构建出的包,这种把源代码放进去有一定安全风险,并且也增加了镜像体积。
为了解决上述问题,Docker 17.05开始支持多阶段构建(multi-stage builds),可以简化Dockerfile,减少镜像大小。
例如,构建JAVA项目镜像:在github上找了一个JAVA博客项目作为演示:
[root@node1 ~]# git clone https://github.com/b3log/solo.git
正克隆到 'solo'...
remote: Enumerating objects: 35, done.
remote: Counting objects: 100% (35/35), done.
remote: Compressing objects: 100% (27/27), done.
remote: Total 40664 (delta 9), reused 20 (delta 7), pack-reused 40629
接收对象中: 100% (40664/40664), 90.90 MiB | 226.00 KiB/s, done.
处理 delta 中: 100% (22324/22324), done.
# cd solo
# vi Dockerfile01
FROM maven AS build
ADD ./pom.xml pom.xml
ADD ./src src/
RUN mvn clean package
FROM tomcat-85
RUN rm -rf /usr/local/tomcat/webapps/ROOT
COPY --from=build target/*.war /usr/local/tomcat/webapps/ROOT.war
CMD ["catalina.sh", "run"]
# docker build -t tomcat-85 -f Dockerfile01 .
# docker container run -d --name solotest -v /usr/local/jdk1.8:/usr/local/jdk solo:v1
首先,第一个FROM 后边多了个 AS 关键字,可以给这个阶段起个名字。然后,第二部分FROM用的我们上面构建的Tomcat镜像,COPY关键字增加了--from参数,用于拷贝某个阶段的文件到当前阶段。这样一个Dockerfile就都搞定了。小结:镜像小有很多好处,例如快速部署、快速回滚。减少服务中断时间,同时镜像仓库占用磁盘空间也少了。
来源:oschina
链接:https://my.oschina.net/u/1769595/blog/3019291