0


Docker的镜像制作

镜像的基本原理

什么是镜像

    首先我们来看看镜像到底是什么?虽然前面有介绍过镜像和容器,但也不是特别的深入。

    镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。

镜像的加载原理

    docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。

    bootfs(boot file system)主要包含bootloader和kernel, bootloader主要是引导加载kernel, Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。

    rootfs (root file system) ,在bootfs之上。包含的就是典型 Linux 系统中的 /dev, /proc, /bin, /etc 等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。

** UnionFS(联合文件系统)**: Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

镜像的结构

    镜像不是一个单一的文件,而是有多层构成。可以通过 docker history 命令查看镜像中各 层内容及大小,每层对应着 Dockerfile 中的一条指令。Docker 镜像默认存储在 /var/lib/docker/目录中。容器其实是在镜像的最上面加了一层读写层, 在运 行容器里做的任何文件改动,都会写到这个读写层。如果删除了容器,也就删除了其最上面的读写层,文件改动也就丢失了。Docker 使用存储驱动管理镜像每层内容及可读写层的容器层。

分层结构的特点

    其实我们也会考虑docker为什么会才用这种分层的结果,它有什么好处呢?最大的一个好处就是共享资源

    比如:有多个镜像都从相同的 base 镜像构建而来,那么宿主机只需在磁盘上保存一份base镜像,同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。

镜像的特点

    大家需要注意,docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部,这一层通常被称为容器层,容器层之下的都叫镜像层

镜像的制作方式

基于容器(现有镜像)创建镜像

基于现有镜像创建主要使用 docker commit 命令,即把一个容器里面运行的程序以及该 程序的运行环境打包起来生成新的镜像

命令格式:

**docker commit [**选项] 容器ID/名称 仓库名称:[标签]

常用选项:

-m说明信息;

-a作者信息;

-p生成过程中停止容器的运行。

首先启动一个镜像,在容器里做修改,然后将修改后的容器提交为新的镜像。需要记住 该容器的 ID 号

[root@bogon ~]# docker ps -a
CONTAINER ID   IMAGE          COMMAND                   CREATED         STATUS         PORTS     NAMES
c4f11e1e291c   nginx:latest   "/docker-entrypoint.…"   4 seconds ago   Up 2 seconds   80/tcp    mynginx
[root@bogon ~]# docker commit -m "new nginx" -a "LiChuang" mynginx mynginx:1.0
sha256:0a817de5a6dbc2fa1775e78110f25ffb9d51e76151afff6912147ed868c73aee
[root@bogon ~]# docker images mynginx
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
mynginx      1.0       0a817de5a6db   7 seconds ago   187MB

基于模板创建镜像

    通过导入操作系统模板文件可以生成镜像,模板可以从 OPENVZ 开源项目下载,下载地址为 http://openvz.org/Download/template/precreated

下面是使用 docker 导入命令将下载的 debian 模板压缩包导入为本地镜像

[root@bogon ~]# wget http://download.openvz.org/template/precreated/debian-7.0-x86-minimal.tar.gz
[root@bogon ~]# cat debian-7.0-x86-minimal.tar.gz | docker import - debian:new
sha256:234c82524631e98feb5e54fbab7dca81bf275617c39b85de49a246c54d86438e
[root@bogon ~]# docker images debian
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
debian       new       234c82524631   2 minutes ago   215MB

基于Dockerfile创建镜像

    除了手动生成 Docker 镜像之外,可以使用 Dockerfile 自动生成镜像。Dockerfile 是由一 组指令组成的文件,其中每条指令对应 Linux 中的一条命令,Docker 程序将读取Dockerfile 中 的指令生成指定镜像。

Dockerfile制作镜像

什么是Dockerfile

    镜像的定制实际上就是定制每一层所添加的配置、文件等信息,但是命令毕竟只是命令,每次定制都得去重复执行这个命令,而且还不够直观,如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么这些问题不就都可以解决了吗?对的,这个脚本就是我们说的
Dockerfile
    Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。

Dockerfile的常用指令

    Dockerfile 结构大致分为四个部分:基础镜像信息、维护者信息、镜像操作指令和容器启 动时执行指令。Dockerfile 每行支持一条指令,每条指令可携带多个参数,支持使用以“#“号开 头的注释。

    在编写 Dockerfile 时,有严格的格式需要遵循:第一行必须使用 FROM 指令指明所基于 的镜像名称;之后使用 MAINTAINER 指令说明维护该镜像的用户信息;然后是镜像操作相关 指令,如 RUN 指令。每运行一条指令,都会给基础镜像添加新的一层。最后使用 CMD 指令 指定启动容器时要运行的命令操作

指令

含义

FROM 镜像

指定新镜像所基于的镜像,第一条指令必须为FROM指令,每创建一个镜像就需要一条FROM指令。

MAINTAINER 名字

说明新镜像的维护人信息

RUN命令

在所基于的镜像上执行命令,并提交到新的镜像中

CMD[“要运行的程序“,”参数1,“参数2“]

指令启动容器时要运行的命令或者脚本,Dockerfile只能有一条CMD命令,如果指定多条则只能最后一条被执行

EXPOSE 端口号

指定新镜像加载到Docker时要开启的端口

ENV 环境变量 变量值

设置一个环境变量的值,会被后面的RUN使用

ADD 源文件/目录 目标文件/目录

将源文件复制到目标文件,源文件要与Dockerfile位于相同目录中,或者是一个URL

COPY 源文件/目录 目标文件/目录

将本地主机上的文件/目录复制到目标地点,源文件/目录要与Dockerfile在相同的目录中

VOLUME [“目录“]

在容器中创建一个挂载点

USER 用户名/UID

指定运行容器时的用户

WORKDIR 路径

为后续的RUN、CMD、ENTRYPOINT指定工作目录

ONBUILD 命令

指定所生成的镜像作为一个基础镜像时所要运行的命令

HEALTHCHECK

健康检查

使用Dockerfile制作镜像

构建SSHD镜像

[root@bogon ~]# mkdir sshd
[root@bogon ~]# cat sshd/Dockerfile
# 第一行必须指明基于的基础镜像
FROM centos:7.6.1810
# 作者信息(可选)
MAINTAINER name
# 镜像的操作指令
RUN yum -y install initscripts openssh-server && \
mkdir -p /sshd && \
echo "root:123456789" | chpasswd && \
echo '#!/bin/bash' > /sshd/run.sh && \
# 生成主机密钥
echo '/usr/sbin/sshd-keygen -A' >> /sshd/run.sh && \
echo '/usr/sbin/sshd -D ' >> /sshd/run.sh && \
chmod -R 755 /sshd/run.sh

# 取消pam限制
RUN sed -ri 's/session required pam_loginuid.so/#session required pam_loginuid.so/g' /etc/pam.d/sshd

# 开放端口
EXPOSE 22

# 设置自启动命令
CMD ["/sshd/run.sh"]
[root@bogon ~]# cd sshd/
# 构建镜像,要注意要在有Dockerfile文件的目录执行
[root@bogon sshd]# docker build -t sshd:v1 .
# 基于刚才制作的镜像启动成容器
[root@bogon sshd]# docker run -id --name sshdkgc -p 10003:22 sshd:v1               
4eb1ccf3330c36acfcf64f5fc585401129881cd6aa2cd2f6ca9802b29482de80

构建systemctl镜像

[root@bogon ~]# mkdir systemctl
[root@bogon ~]# cat systemctl/Dockerfile
# 指定sshd:v1的镜像
FROM sshd:v1
# 指定容器的环境
ENV container docker
# 命令
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == \
systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*; \
rm -f /etc/systemd/system/*.wants/*; \
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*; \
rm -f /lib/systemd/system/anaconda.target.wants/*;
# 挂载宿主机的/sys/fs/cgroup
VOLUME [ "/sys/fs/cgroup" ]
# 启动容器的时候执行的命令
CMD ["/usr/sbin/init"]
[root@bogon ~]# cd systemctl/
[root@bogon systemctl]# docker build -t systemctl:v1 .
# --privileged 特权模式运行一般以特权模式运行后面要传递/sbin/init
# -v 磁盘挂载
[root@bogon systemctl]# docker run --privileged -tid -v /sys/fs/cgroup:/sys/fs/cgroup:ro systemctl:v1 /sbin/init

构建nginx镜像

[root@bogon ~]# mkdir nginx
[root@bogon ~]# wget -P nginx/ http://nginx.org/download/nginx-1.15.2.tar.gz
[root@bogon ~]# cat nginx/Dockerfile
FROM centos:7.6.1810
RUN yum -y install gcc make pcre-devel zlib-devel tar zlib
ADD nginx-1.15.2.tar.gz /usr/src/
RUN cd /usr/src/nginx-1.15.2 \
&& mkdir /usr/local/nginx \
&& ./configure --prefix=/usr/local/nginx && make && make install \
&& ln -s /usr/local/nginx/sbin/nginx /usr/local/sbin/

RUN rm -rf /usr/src/nginx-1.15.2
EXPOSE 80 
EXPOSE 443

CMD ["nginx","-g","daemon off;"]
[root@bogon ~]# cd nginx/
[root@bogon nginx]# docker build -t nginx:kgc .
[root@bogon nginx]# docker run -d -p 80:80 --name kgcnginx nginx:kgc
b6b6a8f0023068db896b434acfebaa6ae94a7c3eb34a671382d9b83c3969da40

构建MySQL镜像

[root@bogon ~]# mkdir mysql
[root@bogon ~]# cat mysql/Dockerfile
# 指定基础镜像
FROM hub.c.163.com/library/mysql:5.7
# 修改编码方式
RUN echo "character-set-server=utf8" >> /etc/mysql/mysql.conf.d/mysqld.cnf
RUN service mysql restart
EXPOSE 3306
[root@bogon ~]# cd mysql/
[root@bogon mysql]# docker build -t mysql:kgc .
# -e 传递环境变量
[root@bogon mysql]# docker run --name mymysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -d mysql:kgc
4195b0e00b85d73117d665c036945a7e89e5a4760240182630eb4d06e71500ef

构建LNMP镜像

[root@bogon ~]# mkdir lnmp
[root@bogon ~]# cat lnmp/Dockerfile
# 基础镜像
FROM centos:7.6.1810

# 配置nginx的yum源
RUN rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
# 初始化RPM数据库并安装nginx
RUN rpm --rebuilddb && yum -y install nginx
# 修改 nginx 配置文件,使之支持 php
RUN sed -i '/^user/s/nginx/nginx\ nginx/g' /etc/nginx/nginx.conf
COPY default.conf /etc/nginx/conf.d/default.conf

# 安装 mariadb 和 php
RUN rpm --rebuilddb && yum -y install mariadb-server mariadb-devel mariadb php-mysql php php-fpm
# 修改php-fpm配置文件
RUN sed -i '/^user/s/apache/nginx/g' /etc/php-fpm.d/www.conf
RUN sed -i '/^group/s/apache/nginx/g' /etc/php-fpm.d/www.conf
# 声明Mariadb的用户以及密码
ENV MARIADB_USER root
ENV MARIADB_PASS 123456
# 支持中文
ENV LC_ALL en_US.UTF-8

# 添加并运行脚本
ADD db_init.sh /root/db_init.sh
RUN chmod 775 /root/db_init.sh
RUN /root/db_init.sh
ADD run.sh /root/run.sh
RUN chmod 775 /root/run.sh
ADD index.php /usr/share/nginx/html/index.php

# 开放的端口
EXPOSE 80
EXPOSE 3306
EXPOSE 443
EXPOSE 9000

# 每次创建容器运行此脚本
CMD ["/root/run.sh"]

[root@bogon ~]# cat lnmp/default.conf
server {
    listen       80;
    server_name  localhost;

    location / {
        root   /usr/share/nginx/html;
        index index.php index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    location ~ \.php$ {
        root           html;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  /usr/share/nginx/html$fastcgi_script_name;
        include        fastcgi_params;
    }

}

[root@bogon ~]# cat lnmp/db_init.sh
#!/bin/bash
mysql_install_db --user=mysql
sleep 3
mysqld_safe &
sleep 3

# 涉及到的变量在Dockerfile中都已经声明
mysqladmin -u "$MARIADB_USER" password "$MARIADB_PASS"

# 授权命令
mysql -u"$MARIADB_USER" -p"$MARIADB_PASS" -e "use mysql; grant all privileges on *.* to '$MARIADB_USER'@'%' identified by '$MARIADB_PASS' with grant option;"
mysql -u"$MARIADB_USER" -p"$MARIADB_PASS" -e "grant all privileges on *.* to '$MARIADB_USER'@'localhost' identified by '$MARIADB_PASS';"
h=$(hostname)
mysql -u"$MARIADB_USER" -p"$MARIADB_PASS" -e "use mysql; update user set password=password('$MARIADB_PASS') where user='$MARIADB_USER' and host='$h';"

[root@bogon ~]# cat lnmp/run.sh
#!/bin/bash

mysqld_safe &
/usr/sbin/nginx &
/usr/sbin/php-fpm

[root@bogon ~]# cat lnmp/index.php
<?php
echo date("Y-m-d H:i:s")."<br />\n";
$link=mysql_connect("localhost","root","123456");
if(!$link) echo "FAILD!";
else echo "MySQL is OK!";
phpinfo();
?>

[root@bogon ~]# cd lnmp/
[root@bogon lnmp]# docker build -t centos:lnmp .
[root@bogon lnmp]# docker run -d --name lnmp -P centos:lnmp /root/run.sh
fd2e0d88c541f1eb3645edd053f2c9708271ceb69ba96f822586268f33f3f123
[root@bogon lnmp]# docker ps -a
CONTAINER ID   IMAGE         COMMAND          CREATED         STATUS         PORTS                                                                                                                                                                          NAMES
fd2e0d88c541   centos:lnmp   "/root/run.sh"   2 seconds ago   Up 2 seconds   0.0.0.0:32775->80/tcp, :::32775->80/tcp, 0.0.0.0:32774->443/tcp, :::32774->443/tcp, 0.0.0.0:32773->3306/tcp, :::32773->3306/tcp, 0.0.0.0:32772->9000/tcp, :::32772->9000/tcp   lnmp

多阶段构建镜像

什么是多阶段构建

通常情况下,构建镜像通常会采用两种方式:

将全部组件及其依赖库的编译、测试、打包等流程封装进一个 Docker 镜像中。采用这种方式往往忽略了以下这些问题:

  • Dockefile 特别长,可维护性降低。
  • 镜像的层次多,体积大,部署时间长。
  • 源代码存在泄漏的风险。

分散到多个 Dockerfile。事先在一个 Dockerfile 将项目及其依赖库编译测试打包好后,再将其拷贝到运行环境中,这种方式需要我们编写两个 Dockerfile 和一些编译脚本才能将其两个阶段自动整合起来,这种方式虽然可以很好地规避第一种方式存在的风险,但明显部署过程较复杂。

为了解决以上这些问题,Docker v17.05 开始支持多镜像阶段构建 (multistage builds)。只需要编写一个 Dockerfile 即可。

多阶段构建镜像的好处

使用Docker多阶段构建有以下几个好处:

减小镜像大小:每个构建阶段只包含必要的依赖项和文件,从而减小了生成的镜像大小。这可以减少镜像的存储空间和传输时间。

提高构建速度:每个构建阶段可以并行执行,因此可以提高构建速度。而且,每个构建阶段只构建所需的内容,从而减少了构建时间。

简化Dockerfile:使用多个构建阶段可以将Dockerfile分解为更小的部分,从而使Dockerfile更加易于管理和维护。每个构建阶段都可以专注于特定的任务,而不必关注整个构建过程。

提高安全性:使用多个构建阶段可以限制敏感信息的泄露。例如,在第一个构建阶段中,可以包含敏感信息,例如私有密钥或密码。而在第二个构建阶段中,可以只包含必要的文件和依赖项。

多阶段构建示例

# 未使用多阶段构建
[root@bogon ~]# mkdir demo
[root@bogon ~]# cd demo/

# 编写C语言测试代码
[root@bogon demo]# cat demo.c
# include<stdio.h>
int main()
{
  printf("%s\n","This is a demo!");
  return 0;
}

# 编写Dockerfile
[root@bogon demo]# cat Dockerfile
FROM centos:7.6.1810

ENV VERSION 1.0

WORKDIR /demo

COPY demo.c .

RUN yum install -y gcc && \
    gcc -v
RUN gcc demo.c -o demo && \
    rm -f demo.c && \
    yum erase -y gcc && \
    cp demo /usr/local/bin/

CMD ["demo"]
[root@bogon demo]# docker build -t demo:v1 .
[root@bogon demo]# docker images demo
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
demo         v1        4575ab954243   21 seconds ago   526MB
# 使用多阶段构建
[root@bogon ~]# mkdir demo
[root@bogon ~]# cd demo/

# 编写C语言测试代码
[root@bogon demo]# cat demo.c
# include<stdio.h>
int main()
{
  printf("%s\n","This is a demo!");
  return 0;
}

# 编写Dockerfile
[root@bogon demo]# cat Dockerfile 
FROM centos:7.6.1810

ENV VERSION 1.0

WORKDIR /demo

COPY demo.c .

RUN yum install -y gcc && \
    gcc -v
RUN gcc demo.c -o demo && \
    rm -f demo.c && \
    yum erase -y gcc && \
    cp demo /usr/local/bin/

FROM centos:7.6.1810
# --from表示从第一个FROM的流程里面复制
COPY --from=0 /usr/local/bin/demo /usr/local/bin/demo

CMD ["demo"] 
[root@bogon demo]# docker build -t demo:v2 .

# 可以看到使用多阶段构建后镜像大小有明显缩减
[root@bogon demo]# docker images demo
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
demo         v2        c05a904e23a7   3 seconds ago   202MB
demo         v1        4575ab954243   2 minutes ago   526MB

注意事项:

在进行多阶段构建的时候第二个FROM里面的COPY可以使用—from=0把第一个阶段的文件复制过来,也可以使用以下方式

FROM centos:7.6.1810 AS build

ENV VERSION 1.0

WORKDIR /demo

COPY demo.c .

RUN yum install -y gcc && \
    gcc -v
RUN gcc demo.c -o demo && \
    rm -f demo.c && \
    yum erase -y gcc && \
    cp demo /usr/local/bin/

FROM centos:7.6.1810
# --from表示从build的流程里面复制
COPY --from=build /usr/local/bin/demo /usr/local/bin/demo

CMD ["demo"] 
标签: docker linux 运维

本文转载自: https://blog.csdn.net/qq_33906471/article/details/138307762
版权归原作者 畅云客 所有, 如有侵权,请联系我们删除。

“Docker的镜像制作”的评论:

还没有评论