运维实战 容器部分 Docker镜像

Posted 洛冰音

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了运维实战 容器部分 Docker镜像相关的知识,希望对你有一定的参考价值。

镜像的分层结构

  • 共享宿主机的kernel

  • base镜像提供的时最小的linux系统

  • 同一docker主机支持运行多种linux发行版

  • 采用分层结构的最大好处是: 共享资源

Copy-in-Write 可写容器层

  • 容器层以下所有镜像层都是只读的

  • docker从上往下依次查找文件

  • 容器层保存镜像变化的部分,并不会对镜像本身进行任何修改

  • 一个镜像最多只能有127层

镜像的构建

采用Dockerfile方式

##下载原始镜像busybox
docker pull busybox
##以交互式方式创建容器并简单测试
[root@Server1 ~]# docker run -it --name Busybox busybox
/ # ls
bin   dev   etc   home  proc  root  sys   tmp   usr   var
/ # mkdir Test
/ # ls
Test  bin   dev   etc   home  proc  root  sys   tmp   usr   var
/ # 

##退出交互式的方式
ctrl+ d 			退出并关闭容器
ctrl+ pq 			退出并将容器打入后台

##创建一个空目录用于存放Dockerfile
[root@Server1 ~]# cd /mnt/
[root@Server1 mnt]# mkdir Docker
[root@Server1 Docker]# vim Dockerfile

##文件内容
FROM busybox
RUN touch Test
RUN cd Test
RUN touch file{1..10}

##使用当前目录的Dockerfile构建镜像
[root@Server1 Docker]# docker build -t testbox .
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM busybox
 ---> 388056c9a683
Step 2/3 : RUN mkdir Test
 ---> Running in 0bbfb83c4a2f
Removing intermediate container 0bbfb83c4a2f
 ---> 2dd01c73369c
Step 3/3 : RUN touch file{1..10}
 ---> Running in 9c15b75bfdfe
Removing intermediate container 9c15b75bfdfe
 ---> aa4cd0b3c97a
Successfully built aa4cd0b3c97a
Successfully tagged testbox:latest

##查看本机镜像可以看到新构建的testbox
[root@Server1 Docker]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
testbox             latest              aa4cd0b3c97a        17 seconds ago      1.23MB
busybox             latest              388056c9a683        2 weeks ago         1.23MB

##查看构建历史可以看到每一步操作都成为了新的一层
[root@Server1 Docker]# docker history testbox:latest 
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
aa4cd0b3c97a        43 seconds ago      /bin/sh -c touch file{1..10}                    0B                  
2dd01c73369c        44 seconds ago      /bin/sh -c mkdir Test                           0B                  
388056c9a683        2 weeks ago         /bin/sh -c #(nop)  CMD ["sh"]                   0B                  
<missing>           2 weeks ago         /bin/sh -c #(nop) ADD file:8d57331dc331805f0…   1.23MB  

Dockerfile的方式可以看到基于busybox后构建的新层的记录和操作内容.

而如果使用commit方式则会有略微不同.

采用Commit方式

##删除刚才采用Dockerfile方式创建的容器
[root@Server1 Docker]# docker rmi testbox:latest 
Untagged: testbox:latest
Deleted: sha256:aa4cd0b3c97a7fcf23834549a7a5a169c93b84e4f1b2cc4e77fd6deba2996a8a
Deleted: sha256:3961fc3b6ac0630016bbcab16b49c69294b7b12727995db5ec416b7d961c936e
Deleted: sha256:2dd01c73369c25ce7d6605e4f144a5660e30eef40368a1aece8b08ba5a6c5288
Deleted: sha256:80b97add01edb4a71fc8c4e3a11823cef75fb312d6ed0a74ac0f4dd5b8ff18a4
[root@Server1 Docker]# docker run -it --name Busybox busybox
/ # ls
bin   dev   etc   home  proc  root  sys   tmp   usr   var
/ # mkdir Test
/ # cd Test/
/Test # touch file{1..10}

ctrl+p q退出交互模式并将容器打入后台

[root@Server1 Docker]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
7b38fd8cf7b9        busybox             "sh"                31 seconds ago      Up 31 seconds                           Busybox
##提交更新并生成新的镜像testbox
[root@Server1 Docker]# docker commit -m "V1" Busybox testbox:V1
sha256:1f68ffbebec57daf89e32b9e444679406651bb24817a84eb1e4c0cefb2bdc49e
[root@Server1 Docker]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
testbox             V1                  1f68ffbebec5        5 seconds ago       1.23MB
busybox             latest              388056c9a683        2 weeks ago         1.23MB
##查看镜像构建的历史,可以看到新层的具体内容并没有显示出来
[root@Server1 Docker]# docker history testbox:V1 
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
1f68ffbebec5        12 seconds ago      sh                                              41B                 V1
388056c9a683        2 weeks ago         /bin/sh -c #(nop)  CMD ["sh"]                   0B                  
<missing>           2 weeks ago         /bin/sh -c #(nop) ADD file:8d57331dc331805f0…   1.23MB         

因此还是推荐使用Dockerfile的方式构建镜像

Dockerfile详解

  • FROM

    指定base镜像,如果本地不存在则从网络拉取

  • MAINTAINER

    设置镜像的作者,比如用户邮箱等等

  • Copy

    • 从本机将文件从build context复制到镜像
    • 支持两种形式: COPY src destCOPY ["src", "dest"]
    • 需要注意的是本机文件必须位于build context, 也就是Dockerfile所在目录中
  • ADD

    • 用法与COPY类似,不同的是src可以是归档压缩文件,文件会被解压后放入容器内
    • 本机文件并不需要位于build context
    • 也可以从网络下载文件并拷贝到镜像
  • ENV

    设置环境变量,变量可以被后续的指令使用

    • 举例 ENV HOSTNAME Server1.example.com
  • EXPOSE

    如果在容器中运行应用服务,需要把服务端口暴露出去的时候使用

    • EXPOSE 80, 即对外暴露80端口
  • VOLUME

    • 申明数据卷, 通常指定的是应用的数据挂载点
    • 挂载目录的目的是为了持久化
    • 举例 VOLUME ["/var/www/html"]
  • WORKDIR

    • RUN, CMD, ENTERPOINT, ADD, COPY指令设置镜像中的当前工作目录
    • 如果目录不存在则会自动创建
  • RUN

    • 在容器中运行命令并创建新的镜像层, 常用于安装软件包
    • 举例 RUN yum install -y vim
  • CMD和ENTRYPOINT

    • 这两个指定都是用于设置容器启动后执行的命令
    • 区别在于CMD会被RUN后面的命令行覆盖, 而ENTRYPOINT一定不会被忽略/一定会执行
    • docker run后面的参数可以传递给ENTRYPOINT指令作为参数
    • Dockerfile中只能指定一个ENTRYPOINT, 如果指定了很多个则只有最后一个有效

SHELL和EXEC格式的区别

FROM busybox
ENV name world
ENTRYPOINT echo "hello, $name"

Shell格式底层会调用/bin/sh -c来执行指令,可以解析变量,而下面的Exec格式不会.

FROM busybox
ENV name world
ENTRYPOINT ["/bin/echo" "hello, $name"]

必须改写为如下形式才能正常解析变量.

FROM busybox
ENV name world
ENTRYPOINT ["/bin/sh" "-c" "echo hello, $name"]

Exec格式时,ENTRYPOINT可以通过CMD提供额外参数, CMD的额外参数可以在容器启动时动态替换.

Shell格式时,ENTRYPOINT会忽略任何CMDdocker run提供的参数

操作过程

[root@Server1 Docker]# vim Dockerfile 
[root@Server1 Docker]# touch index.html
[root@Server1 Docker]# echo Server1 > index.html 
[root@Server1 Docker]# ls
Dockerfile  index.html  nginx-1.18.0.tar.gz

##Dockerfile内容
FROM busybox
RUN mkdir Test
RUN touch /Test/Testfile
COPY index.html /
ADD nginx-1.18.0.tar.gz /
RUN mv nginx-1.18.0 Nginx
ENV HOSTNAME Server1
EXPOSE 80
VOLUME ["/data"]
WORKDIR /Test
ENTRYPOINT ["/bin/echo", "hello"]
CMD ["world"]

[root@Server1 Docker]# docker build -t demo .
Sending build context to Docker daemon  1.043MB
Step 1/12 : FROM busybox
 ---> 388056c9a683
Step 2/12 : RUN mkdir Test
 ---> Running in 939e77e989f6
Removing intermediate container 939e77e989f6
 ---> 9537fc624d09
Step 3/12 : RUN touch /Test/Testfile
 ---> Running in 2ce46791eec2
Removing intermediate container 2ce46791eec2
 ---> 169b8b8c81f7
Step 4/12 : COPY index.html /
 ---> 6abd12f14c62
Step 5/12 : ADD nginx-1.18.0.tar.gz /
 ---> 7fd946b03383
Step 6/12 : RUN mv nginx-1.18.0 Nginx
 ---> Running in 9ac35e723827
Removing intermediate container 9ac35e723827
 ---> a817a5a3b1eb
Step 7/12 : ENV HOSTNAME Server1
 ---> Running in 5a494de50eb1
Removing intermediate container 5a494de50eb1
 ---> eb900f03448c
Step 8/12 : EXPOSE 80
 ---> Running in 77affb4662d8
Removing intermediate container 77affb4662d8
 ---> e17a7d084fcb
Step 9/12 : VOLUME ["/data"]
 ---> Running in 4208622c722a
Removing intermediate container 4208622c722a
 ---> 281920256f8d
Step 10/12 : WORKDIR /Test
 ---> Running in 31f5db039dd2
Removing intermediate container 31f5db039dd2
 ---> 73b41cdf5a3c
Step 11/12 : ENTRYPOINT ["/bin/echo", "hello"]
 ---> Running in bb798ebc3335
Removing intermediate container bb798ebc3335
 ---> bb598d50bedf
Step 12/12 : CMD ["world"]
 ---> Running in 3ac90fc7965b
Removing intermediate container 3ac90fc7965b
 ---> bae4405b5d28
Successfully built bae4405b5d28
Successfully tagged demo:latest
[root@Server1 Docker]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
demo                latest              bae4405b5d28        6 seconds ago       13.7MB
busybox             latest              388056c9a683        2 weeks ago         1.23MB
[root@Server1 Docker]# docker run --rm demo:latest 
hello world
[root@Server1 Docker]# docker run --rm demo:latest linux
hello linux

--rm参数常用于临时测试内容,当命令执行结束后临时生成的容器就会关闭并删除.

在其后跟着的参数会传递给容器使用.

因此不传递参数时根据Dockerfile的内容,输出的内容是hello world

而当传递了linux后,CMD的参数被覆盖,输出的内容是hello linux

关于VOLUME的示例

[root@Server1 Docker]# docker run -it test
/Test # mount | grep data
/dev/mapper/rhel-root on /data type xfs (rw,relatime,attr2,inode64,noquota)

回到虚拟机,检测容器及其持久化路径

[root@Server1 volumes]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS               NAMES
df2e62b200ba        test                "sh"                About a minute ago   Up About a minute   80/tcp              agitated_pasteur
[root@Server1 volumes]# docker inspect df2e62b200ba
[
    {
        "Id": "df2e62b200bafe221739f6535fc89264b9791e60aa698537619ded42b3686e9b",
        "Created": "2021-04-27T03:01:58.831986169Z",
        "Path": "sh",
        "Args": [],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 17440,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2021-04-27T03:01:59.097958403Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:73b41cdf5a3c2bc7251b046f768859cd633e02f19c6a61ce5dd6931ba026d3c4",
        "ResolvConfPath": "/var/lib/docker/containers/df2e62b200bafe221739f6535fc89264b9791e60aa698537619ded42b3686e9b/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/df2e62b200bafe221739f6535fc89264b9791e60aa698537619ded42b3686e9b/hostname",
        "HostsPath": "/var/lib/docker/containers/df2e62b200bafe221739f6535fc89264b9791e60aa698537619ded42b3686e9b/hosts",
        "LogPath": "/var/lib/docker/containers/df2e62b200bafe221739f6535fc89264b9791e60aa698537619ded42b3686e9b/df2e62b200bafe221739f6535fc89264b9791e60aa698537619ded42b3686e9b-json.log",
        "Name": "/agitated_pasteur",
        "RestartCount": 0,
        "Driver": "overlay2",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": null,
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "default",
            "PortBindings": {},
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "Capabilities": null,
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "private",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "ConsoleSize": [
                0,
                0
            ],
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 0,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": [],
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DeviceCgroupRules": null,
            "DeviceRequests": null,
            "KernelMemory": 0,
            "KernelMemoryTCP": 0,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,
            "OomKillDisable": false,
            "PidsLimit": null,
            "Ulimits": null,
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "MaskedPaths": [
                "/proc/asound",
                "/proc/acpi",
                "/proc/kcore",
                "/proc/keys",
                "/proc/latency_stats",
                "/proc/timer_list",
                "/proc/timer_stats",
                "/proc/sched_debug",
                "/proc/scsi",
                "/sys/firmware"
            ],
            "ReadonlyPaths": [
                "/proc/bus",
                "/proc/fs",
                "/proc/irq",
                "/proc/sys",
                "/proc/sysrq-trigger"
            ]
        },
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/59faba205ad3939e7a17f0127ae3c8cde7ff1360088c52df1e0793d66f39bf45-init/diff:/var/lib/docker/overlay2/6a304244cf9f7712409a6fa2c95c4c51d14a5085873e0d58457713dc772831d0/diff:/var/lib/docker/overlay2/ceaa36629aa9b33f4e898f8bb10797379487b746251e6da9773f36ec3addb809/diff:/var/lib/docker/overlay2/96cc89b932c5d43df2052ee68c93ddafd5aa3966018abc87f6111015b2b8e4a5/diff:/var/lib/docker/overlay2/e272c5d92a4906480fc601b600f6310f2c065f0c85a649d9d322db837aace931/diff:/var/lib/docker/overlay2/03d7b22fc476b81aac770b2be9f3d55db1a405a7e5e341ef7038e8c97c378edd/diff:/var/lib/docker/overlay2/04834c81f78ab1e77f9d2f32ab79e19b11ff34af47af0dca505824dd8b5805ad/diff",
                "MergedDir": "/var/lib/docker/overlay2/59faba205ad3939e7a17f0127ae3c8cde7ff1360088c52df1e0793d66f39bf45/merged",
                "UpperDir": "/var/lib/docker/overlay2/59faba205ad3939e7a17f0127ae3c8cde7ff1360088c52df1e0793d66f39bf45/diff",
                "WorkDir": "/var/lib/docker/overlay2/59faba205ad3939e7a17f0127ae3c8cde7ff1360088c52df1e0793d66f39bf45/work"
            },
            "Name": "overlay2"
        },
        "Mounts": [
            {
                "Type": "volume",
                "Name": "30b75ff3583bd344cecbc68ba19a023a61c4b9be8e98332adba879f9ac78136c",
                "Source": "/var/lib/docker/volumes/30b75ff3583bd344cecbc68ba19a023a61c4b9be8e98332adba879f9ac78136c/_data",
                "Destination": "/data",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
        "Config": {
            "Hostname": "df2e62b200ba",
            "Domainname": "",
            "User": "",
            "AttachStdin": true,
            "AttachStdout": true,
            "AttachStderr": true,
            "ExposedPorts": {
                "80/tcp": {}
            },
            "Tty": true,
            "OpenStdin": true,
            "StdinOnce": true,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "HOSTNAME=Server1"
            ],
            "Cmd": [
                "sh"
            ],
            "Image": "test",
            "Volumes": {
                "/data": {}
            },
            "WorkingDir": "/Test",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {}
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "c6391e8cdc95aaf864677d0d82f982093bca840a86de81f8565f9d4f29573cfa",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {
                "80/tcp": null
            },
            "SandboxKey": "/var/run/docker/netns/c6391e8cdc95",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "d2a94cbedd95fdca62af4755c865f968077b498418df14f033ab238b08d2adc7",
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.2",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
            "MacAddress": "02:42:ac:11:00:02",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "65b6bbdf4f1fc36862cd679fcc5fc6fc2c6b5326f8c9c7f76d1e4508975ae6c4",
                    "EndpointID": "d2a94cbedd95fdca62af4755c865f968077b498418df14f033ab238b08d2adc7",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
            }
        }
    }
]

[root@Server1 volumes]# cd /var/lib/docker/volumes/30b75ff3583bd344cecbc68ba19a023a61c4b9be8e98332adba879f9ac78136c/_data
[root@Server1 _data]# ls
[root@Server1 _data]# touch Testfile
  • 可以看到原本该路径下什么文件也没有
  • 而当我们创建了测试文件后,该文件在测试容器中也可以看到了
/ # cd /data/
/data # ls
Testfile
  • 同理,在任意一侧对该文件做修改/删除, 都会反映在另一侧

镜像的优化

优化逻辑

  • 选择最精简的基础镜像
  • 减少镜像的层数
  • 清理镜像构建的中间产物
  • 注意优化网络请求
  • 尽量去用构建缓存
  • 使用多阶段构建镜像

举例

  • 首先尝试创建一个Nginx的镜像,这里使用精简版的RHEL7作为base镜像
FROM rhel7
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
COPY NeuWings.repo /etc/yum.repos.d/NeuWings.repo
RUN rm -rf /etc/yum.repos.d/rhel7.repo
RUN rpmdb --rebuilddb
RUN yum install -y gcc pcre-devel zlib-devel > /dev/null
ADD nginx-1.18.0.tar.gz /mnt
WORKDIR /mnt/nginx-1.18.0
RUN ./configure --prefix=/usr/local/nginx > /dev/null
RUN yum install -y make > /dev/null
RUN make > /dev/null
RUN make install > /dev/null
CMD ["/usr/local/nginx/sbin/nginx", "-g" "daemon off;"]
  • 查看构建完成的大小: 346M
[root@Server1 Docker]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
demo                latest              181d43b6a7dc        40 seconds ago      346MB
rhel7               latest              0a3eb3fde7fd        6 years ago         140MB

尝试精简

  • 压缩镜像的层数,将可以放在同一条命令执行的都整合到同一条
FROM rhel7
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
COPY NeuWings.repo /etc/yum.repos.d/NeuWings.repo
ADD nginx-1.18.0.tar.gz /mnt
WORKDIR /mnt/nginx-1.18.0
RUN rm -rf /etc/yum.repos.d/rhel7.repo && rpmdb --rebuilddb && yum install -y gcc pcre-devel zlib-devel make > /dev/null && /mnt/nginx-1.18.0/configure --prefix=/usr/local/nginx > /dev/null && yum install -y make > /dev/null && make > /dev/null && make install > /dev/null && yum clean all > /dev/null && rm -rf /mnt/nginx-1.18.0
CMD ["/usr/local/nginx/sbin/nginx", "-g" "daemon off;"]
  • 查看构建完成的大小: 258M
[root@Server1 Docker]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
demo2               latest              7f186836d1d9        14 seconds ago      258MB
demo                latest              181d43b6a7dc        8 minutes ago       346MB
rhel7               latest              0a3eb3fde7fd        6 years ago         140MB

已知其实我们需要的其实只是Nginx的二进制文件, 中途为了编译而安装的依赖以及编译中产生的内容其实在服务中都没有用到

  • 采用多阶段构建的方法,创建一个临时镜像用于编译,将编译好的二进制文件拷贝到最终要创建的镜像
FROM rhel7 as build
COPY NeuWings.repo /etc/yum.repos.d/NeuWings.repo
ADD nginx-1.18.0.tar.gz /mnt
RUN rm -rf /etc/yum.repos.d/rhel7.repo && rpmdb --rebuilddb && yum install -y gcc pcre-devel zlib-devel make > /dev/null && cd /mnt/nginx-1.18.0 && sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && ./configure --prefix=/usr/local/nginx > /dev/null && yum install -y make > /dev/null && make > /dev/null && make install > /dev/null && yum clean all > /dev/null && rm -rf /mnt/nginx-1.18.0 && rm -rf /var/cache/yum

FROM rhel7
COPY --from=build /usr/local/nginx /usr/local/nginx
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g" "daemon off;"]
  • 首先构建一个镜像,镜像内编译安装Nginx
  • 再阶段性构建第二个镜像,将第一个镜像中的Nginx二进制文件复制过来
  • 这种方式省去了中途安装的依赖和编译内容,镜像大小大大降低
[root@Server1 Docker]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
demo2               latest              94f2bf6f8c66        7 seconds ago       141MB
<none>              <none>              56fe29ab9d19        8 seconds ago       258MB
demo                latest              181d43b6a7dc        24 minutes ago      346MB
rhel7               latest              0a3eb3fde7fd        6 years ago         140MB

  • 查看构建完成的大小: 141M, 仅比base镜像大了1m而已.

  • 如果还要继续压缩,就只能寻找更精简的base镜像了

FROM nginx as base

# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
ARG TIME_ZONE

RUN mkdir -p /opt/var/cache/nginx && \\
    cp -a --parents /usr/lib/nginx /opt && \\
    cp -a --parents /usr/share/nginx /opt && \\
    cp -a --parents /var/log/nginx /opt && \\
    cp -aL --parents /var/run /opt && \\
    cp -a --parents /etc/nginx /opt && \\
    cp -a --parents /etc/passwd /opt && \\
    cp -a --parents /etc/group /opt && \\
    cp -a --parents /usr/sbin/nginx /opt && \\
    cp -a --parents /usr/sbin/nginx-debug /opt && \\
    cp -a --parents /lib/x86_64-linux-gnu/ld-* /opt && \\
    cp -a --parents /lib/x86_64-linux-gnu/libpcre.so.* /opt && \\
    cp -a --parents /lib/x86_64-linux-gnu/libz.so.* /opt && \\
    cp -a --parents /lib/x86_64-linux-gnu/libc* /opt && \\
    cp -a --parents /lib/x86_64-linux-gnu/libdl* /opt && \\
    cp -a --parents /lib/x86_64-linux-gnu/libpthread* /opt && \\
    cp -a --parents /lib/x86_64-linux-gnu/libcrypt* /opt && \\
    cp -a --parents /usr/lib/x86_64-linux-gnu/libssl.so.* /opt && \\
    cp -a --parents /usr/lib/x86_64-linux-gnu/libcrypto.so.* /opt && \\
    cp /usr/share/zoneinfo/${TIME_ZONE:-ROC} /opt/etc/localtime

FROM gcr.io/distroless/base-debian10

COPY --from=base /opt /

EXPOSE 80 443

ENTRYPOINT ["nginx", "-g", "daemon off;"]
  • 操作过程
##获取最精简的基础镜像
[root@Server1 Docker]# docker load < base-debian10.tar 
de1602ca36c9: Loading layer  3.041MB/3.041MB
1d3b68b6972f: Loading layer  17.77MB/17.77MB
Loaded image: gcr.io/distroless/base-debian10:latest
##构建需要的nginx镜像
[root@Server1 Docker]# docker build -t webserer:V1 -f Dockerfile2 .
##查看构建结果
[root@Server1 Docker]# docker images
REPOSITORY                        TAG                 IMAGE ID            CREATED             SIZE
webserer                          V1                  059742ab983d        6 seconds ago       31.9MB
<none>                            <none>              2cb570a37c5b        8 seconds ago       146MB
nginx                             latest              62d49f9bab67        13 days ago         133MB
gcr.io/distroless/base-debian10   latest              d48fcdd54946        51 years ago        19.2MB
  • 查看构建完成的大小: 31.9M

以上是关于运维实战 容器部分 Docker镜像的主要内容,如果未能解决你的问题,请参考以下文章

运维实战 容器部分 Docker安全

运维实战 容器部分 Docker网络

运维实战 容器部分 Docker入门简介

运维实战 容器部分 Docker数据卷

运维实战 容器部分 Docker Swarm

运维实战 容器部分 Docker Machine