社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  docker

Docker学习(五)Dockerfile

我犟不过你 • 4 年前 • 508 次点击  

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
 
508 次点击