Python社区  »  docker

Docker学习(五)Dockerfile

我犟不过你 • 2 月前 • 47 次点击  

Dockerfile用于构建docker镜像,本章节从一个Dockerfile的例子讲起,先整体看下具体做了什么,在详细介绍每条指定的作用与用法。

1、找到mysql的Dockerfile,并分析

访问 https://registry.hub.docker.com/ ,搜索mysql

mysql容器

点击图中红框进到mysql的github源码。

github mysql

选择5.7,打开Dockerfile

#构建镜像的依赖
FROM debian:buster-slim

# 执行添加组和添加用户的命令,不熟悉的同学看下mysql添加组和用户命令
RUN groupadd -r mysql && useradd -r -g mysql mysql

#更新依赖版本库、--no-install-recommends不安装非必须依赖、删除指定目录的文件
RUN apt-get update && apt-get install -y --no-install-recommends gnupg dirmngr && rm -rf /var/lib/apt/lists/*

# 环境变量 指定gosu版本,以便后面使用
ENV GOSU_VERSION 1.12
#执行很多的脚本命令,以反斜杆结尾代表一个命令,此处建议这么写,不能写多个run,以减少镜像的层数
RUN set -eux; \
    savedAptMark="$(apt-mark showmanual)"; \
    apt-get update; \
    apt-get install -y --no-install-recommends ca-certificates wget; \
    rm -rf /var/lib/apt/lists/*; \
    dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
    wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
    wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \
    export GNUPGHOME="$(mktemp -d)"; \
    gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \
    gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \
    gpgconf --kill all; \
    rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \
    apt-mark auto '.*' > /dev/null; \
    [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \
    apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
    chmod +x /usr/local/bin/gosu; \
    gosu --version; \
    gosu nobody true

#创建docker-entrypoint-initdb.d文件
RUN mkdir /docker-entrypoint-initdb.d

#更新依赖源以及安装必要依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
# for MYSQL_RANDOM_ROOT_PASSWORD
        pwgen \
# for mysql_ssl_rsa_setup
        openssl \
# FATAL ERROR: please install the following Perl modules before executing /usr/local/mysql/scripts/mysql_install_db:
# File::Basename
# File::Copy
# Sys::Hostname
# Data::Dumper
        perl \
# install "xz-utils" for .sql.xz docker-entrypoint-initdb.d files
        xz-utils \
    && rm -rf /var/lib/apt/lists/*

RUN set -ex; \
# gpg: key 5072E1F5: public key "MySQL Release Engineering <mysql-build@oss.oracle.com>" imported
    key='A4A9406876FCBD3C456770C88C718D3B5072E1F5'; \
    export GNUPGHOME="$(mktemp -d)"; \
    gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \
    gpg --batch --export "$key" > /etc/apt/trusted.gpg.d/mysql.gpg; \
    gpgconf --kill all; \
    rm -rf "$GNUPGHOME"; \
    apt-key list > /dev/null

#两个环境变量
ENV MYSQL_MAJOR 5.7
ENV MYSQL_VERSION 5.7.31-1debian10
#输出信息到文件/etc/apt/sources.list.d/mysql.list
RUN echo "deb http://repo.mysql.com/apt/debian/ buster mysql-${MYSQL_MAJOR}" > /etc/apt/sources.list.d/mysql.list

# the "/var/lib/mysql" stuff here is because the mysql-server postinst doesn't have an explicit way to disable the mysql_install_db codepath besides having a database already "configured" (ie, stuff in /var/lib/mysql/mysql)
# also, we set debconf keys to make APT a little quieter
RUN { \
        echo mysql-community-server mysql-community-server/data-dir select ''; \
        echo mysql-community-server mysql-community-server/root-pass password ''; \
        echo mysql-community-server mysql-community-server/re-root-pass password ''; \
        echo mysql-community-server mysql-community-server/remove-test-db select false; \
    } | debconf-set-selections \
    && apt-get update && apt-get install -y mysql-server="${MYSQL_VERSION}" && rm -rf /var/lib/apt/lists/* \
    && rm -rf /var/lib/mysql && mkdir -p /var/lib/mysql /var/run/mysqld \
    && chown -R mysql:mysql /var/lib/mysql /var/run/mysqld \
# ensure that /var/run/mysqld (used for socket and lock files) is writable regardless of the UID our mysqld instance ends up having at runtime
    && chmod 1777 /var/run/mysqld /var/lib/mysql \
# comment out a few problematic configuration values
    && find /etc/mysql/ -name '*.cnf' -print0 \
        | xargs -0 grep -lZE '^(bind-address|log)' \
        | xargs -rt -0 sed -Ei 's/^(bind-address|log)/#&/' \
# don't reverse lookup hostnames, they are usually another container
    && echo '[mysqld]\nskip-host-cache\nskip-name-resolve' > /etc/mysql/conf.d/docker.cnf

#挂载数据卷到宿主机,而不是在容器内部,防止重启容器数据丢失或容器主键增大
VOLUME /var/lib/mysql

#将docker-entrypoint.sh复制到容器的/usr/local/bin/下
COPY docker-entrypoint.sh /usr/local/bin/

#为usr/local/bin/docker-entrypoint.sh建立一个名为/entrypoint.sh的软连接
RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat

#指令用于设定容器启动时第一个运行的命令及其参数、具体命令在docker-entrypoint.sh中
ENTRYPOINT ["docker-entrypoint.sh"]

#声明端口3306(经典MySQL协议),33060(X协议)
EXPOSE 3306 33060

#运行
CMD ["mysqld"]

2、逐步分析每个涉及到的命令

2.1、FROM

基础镜像,构建的依赖基于该镜像。

2.2、RUN

执行命令行命令。

shell格式:

RUN [shell 命令]
例: yum install nginx 

exec格式:

RUN ["执行文件", "参数1", "参数2"]
例:RUN ["./startup.sh" "dev" "standalone" ]

注意: RUN方法每执行一次,会创建一层镜像,会导致镜像膨胀。可以使用“&&”分隔,如下所示:

RUN yum install wget \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
&& tar -xvf redis.tar.gz

2.3、ENV

设置环境变量,在后续步骤中可以使用。

ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...

例:
ENV NODE_VERSION 7.2.0

RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
&& curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"

2.4、VOLUME

匿名数据券,如果启动时忘记或未指定数据券,会自动挂载到匿名数据券。
优点:
a)防止因容器重启而导致的数据丢失
b)防止容器空间不断膨胀

VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>

例:
VOLUME /var/lib/mysql

启动时可以进行覆盖挂载数据券:

#使用了 mydata 这个命名卷挂载到了 /data 这个位置,替代了 Dockerfile 中定义的匿名卷的挂载配置
docker run -d -v mydata:/data xxxx

2.5、COPY

从宿主机的路径中复制文件到容器中。目标路径是容器中的路径,会自动创建。

COPY [--chown=<user>:<group>] <源路径1>...  <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",...  "<目标路径>"]
例:
COPY docker-entrypoint.sh /usr/local/bin/
#镜像内的jdk属于jenkins:jenkins,否则默认是root:root
COPY --chown=jenkins:jenkins  JDK/jdk /home/jenkins/jdk

ADD命令
ADD基本能做COPY所做的所有事情(建议使用COPY),除了不能用在 multistage 的场景下。

优点 :在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。
缺点 :在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。

2.6、ENTRYPOINT

类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。

但是, 如果运行 docker run 时使用了 --entrypoint 选项,此选项的参数可当作要运行的程序覆盖 ENTRYPOINT 指令指定的程序。

优点 :在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。

注意 :如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。

格式

ENTRYPOINT ["<executeable>","<param1>","<param2>",...]

可以搭配 CMD 命令使用:一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参,以下示例会提到。

示例

假设已通过 Dockerfile 构建了 nginx:test 镜像:

FROM nginx

ENTRYPOINT ["nginx", "-c"] # 定参
CMD ["/etc/nginx/nginx.conf"] # 变参

1、不传参运行

$ docker run  nginx:test

容器内会默认运行以下命令,启动主进程。

nginx -c /etc/nginx/nginx.conf

2、传参运行

$ docker run  nginx:test -c /etc/nginx/new.conf

容器内会默认运行以下命令,启动主进程(/etc/nginx/new.conf:假设容器内已有此文件)

nginx -c /etc/nginx/new.conf

2.7、EXPOSE

仅仅只是声明端口。

作用
帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。
在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。

格式

EXPOSE <端口1> [<端口2>...]
例:
#声明端口3306(经典MySQL协议),33060(X协议)
EXPOSE 3306 33060

2.8、CMD

类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:

CMD 在docker run 时运行。
RUN 是在 docker build。

作用 :为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。

注意 :如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。

用法

CMD <shell 命令> 
CMD ["<可执行文件或命令>","<param1>","<param2>",...] 
CMD ["<param1>","<param2>",...]  # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数

3、Build 上下文的概念

docker架构

如上图所示:Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 DockerRemote API,而如 docker 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 docker 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。

当我们进行镜像构建的时候,并非所有定制都会通过 RUN 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 COPY 指令、 ADD 指令等。而 docker build 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?

这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径, docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。

4、Dockerfile镜像构建

以下面的构建nginx举例:

  docker build -t nginx:v4 .

上述命令中的这个 “.” ,实际上是在指定上下文的目录, docker build 命令会将该目录下的内容打包交给 Docker 引擎以帮助构建镜像。

写一个最简单的Dockerfile:
进入opt文件夹,创建一个Dockerfile

cd /opt  

vi Dockerfile

Dockerfile

FROM centos:7

RUN yum -y install epel-release \
&&yum -y install nginx

EXPOSE 80

构建成功后主要执行信息

Step 1/3 : FROM centos:7
 ---> 7e6257c9f8d8
Step 2/3 : RUN yum -y install epel-release &&yum -y install nginx
...
Complete!
Removing intermediate container 180f2e55c821
 ---> 5c0e3cb8a914
Step 3/3 : EXPOSE 80
 ---> Running in 6996b4d9150f
Removing intermediate container 6996b4d9150f
 ---> 116f7ab71d60
Successfully built 116f7ab71d60
Successfully tagged jiang/nginx:v4

查看镜像:

images

参考文章:
https://www.runoob.com/docker/docker-dockerfile.html
https://www.cnblogs.com/lfxiao/p/9594185.html

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/74032
 
47 次点击  
分享到微博