Docker 镜像

孤者浪人 提交于 2021-02-11 22:03:34

镜像是部署应用的基石

镜像是什么?

    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就都搞定了。小结:镜像小有很多好处,例如快速部署、快速回滚。减少服务中断时间,同时镜像仓库占用磁盘空间也少了。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!