08-Docker创建镜像

一、Dockerfile创建自定义Docker镜像

1、Dockerfile简介

1.1 Dockerfile概念

    Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。

    镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。

    Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。有了 Dockerfile,当我们需要定制自己额外的需求时,只需在 Dockerfile 上添加或者修改指令,重新生成 image 即可,省去了敲命令的麻烦。

1.2 关系图

    通过上图可以看出使用 Dockerfile 定义镜像,运行镜像启动容器。

1.3 Dockerfile作用

    1、对于开发人员:可以为开发团队提供一个完全一致的开发环境;

    2、对于测试人员:可以直接拿开发时所构建的镜像或者通过Dockerfile文件构建一个新的镜像开始工作了;

    3、对于运维人员:在部署时,可以实现应用的无缝移植。

1.4 Dockerfile组成

    DockerFile分为四部分组成:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。一开始必须要指明所基于的镜像名称,接下来一般会说明维护者信息;后面则是镜像操作指令,例如 RUN 指令。每执行一条RUN 指令,镜像添加新的一层,并提交;最后是 CMD 指令,来指明运行容器时的操作命令。

1.5 Dockerfile常用命令

1.6 构建镜像

    docker build 命令会根据Dockerfile 文件及上下文构建新 Docker 镜像。构建上下文是指 Dockerfile 所在的本地路径或一个URL(Git仓库地址)。构建上下文环境会被递归处理,所以构建所指定的路径还包括了子目录,而URL还包括了其中指定的子模块。

    将当前目录做为构建上下文时,可以像下面这样使用docker build命令构建镜像:

    说明:构建会在 Docker 后台守护进程(daemon)中执行,而不是CLI中。构建前,构建进程会将全部内容(递归)发送到守护进程。大多情况下,应该将一个空目录作为构建上下文环境,并将 Dockerfile 文件放在该目录下。

    在构建上下文中使用的 Dockerfile 文件,是一个构建指令文件。为了提高构建性能,可以通过.dockerignore文件排除上下文目录下不需要的文件和目录。

    在 Docker 构建镜像的第一步,docker CLI 会先在上下文目录中寻找.dockerignore文件,根据.dockerignore 文件排除上下文目录中的部分文件和目录,然后把剩下的文件和目录传递给 Docker 服务。

    Dockerfile一般位于构建上下文的根目录下,也可以通过-f指定该文件的位置:

    构建时,还可以通过-t参数指定构建成镜像的仓库、标签。

    例如:仓库:标签 

1.7 镜像标签

    如果存在多个仓库下,或使用多个镜像标签,就可以使用多个-t参数:

    在 Docker 守护进程执行 Dockerfile 中的指令前,首先会对Dockerfile 进行语法检查,有语法错误时会返回:

1.8 缓存

    Docker 守护进程会一条一条的执行Dockerfile 中的指令,而且会在每一步提交并生成一个新镜像,最后会输出最终镜像的ID。生成完成后,Docker 守护进程会自动清理你发送的上下文。

    Dockerfile文件中的每条指令会被独立执行,并会创建一个新镜像,RUN cd /tmp等命令不会对下条指令产生影响。

    Docker 会重用已生成的中间镜像,以加速docker build的构建速度。以下是一个使用了缓存镜像的执行过程:

    构建缓存仅会使用本地父生成链上的镜像,如果不想使用本地缓存的镜像,也可以通过--cache-from指定缓存。指定后将不再使用本地生成的镜像链,而是从镜像仓库中下载。

1.9 寻找缓存的逻辑

    Docker寻找缓存的逻辑其实就是树型结构根据 Dockerfile 指令遍历子节点的过程。下图可以说明这个逻辑。

    大部分指令可以根据上述逻辑去寻找缓存,除了 ADD 和 COPY 。这两个指令会复制文件内容到镜像内,除了指令相同以外,Docker 还会检查每个文件内容校验(不包括最后修改时间和最后访问时间),如果校验不一致,则不会使用缓存。

    除了这两个命令,Docker并不会去检查容器内的文件内容,比如 RUN apt-get -y update,每次执行时文件可能都不一样,但是Docker认为命令一致,会继续使用缓存。这样一来,以后构建时都不会再重新运行apt-get -y update。

    如果Docker没有找到当前指令的缓存,则会构建一个新的镜像,并且之后的所有指令都不会再去寻找缓存。

2、 Dockerfile官方实践

2.1 Dockerfile官方资料

Docker与PostgreSQL

调研发现,目前Docker 官方Hub已经提供PostgreSQL各个版本的镜像。

网址:https://hub.docker.com/_/postgres/

获取方式:docker pull postgres

支持的PostgreSQL版本有:

10.4, 10, latest (10/Dockerfile)

10.4-alpine, 10-alpine, alpine (10/alpine/Dockerfile)

9.6.9, 9.6, 9 (9.6/Dockerfile)

9.6.9-alpine, 9.6-alpine, 9-alpine (9.6/alpine/Dockerfile)

9.5.13, 9.5 (9.5/Dockerfile)

9.5.13-alpine, 9.5-alpine (9.5/alpine/Dockerfile)

9.4.18, 9.4 (9.4/Dockerfile)

9.4.18-alpine, 9.4-alpine (9.4/alpine/Dockerfile)

9.3.23, 9.3 (9.3/Dockerfile)

9.3.23-alpine, 9.3-alpine (9.3/alpine/Dockerfile)

问题提交地址:

https://github.com/docker-library/postgres/issues

目前由PostgreSQL

Docker社区维护,地址https://github.com/docker-library/postgres

支持的架构有:

IBM z Systems (s390x):https://hub.docker.com/u/s390x/

ARMv7 32-bit (arm32v7):https://hub.docker.com/u/arm32v7/

Windows x86-64 (windows-amd64):https://hub.docker.com/u/winamd64/

Linux x86-64 (amd64):https://hub.docker.com/u/amd64/

Other architectures built by official images:(butnotofficially supported by Docker, Inc.)

IBM POWER8 (ppc64le):https://hub.docker.com/u/ppc64le/

x86/i686 (i386):https://hub.docker.com/u/i386/

ARMv8 64-bit (arm64v8):https://hub.docker.com/u/arm64v8/

ARMv6 32-bit (arm32v6):https://hub.docker.com/u/arm32v6/(RaspberryPi 1, Raspberry Pi Zero)

ARMv5 32-bit (arm32v5):https://hub.docker.com/u/arm32v5/

2.2对官网10.4, 10, latest (10/Dockerfile)的理解

     对官网10.4, 10, latest (10/Dockerfile)的理解这部分内容如下所示:

FROM debian:stretch-slim

# set -ex 其中 e 代表 若指令传回值不等于0(指令成功返回0,不成功返回值不是0),立刻退出shell  ; x 代表 执行指令后,会先显示该指令及所下的参数

RUN set -ex; \

# command -v gpg 是用来判断是否存在gpg这个命令(这个命令是用来对密钥进行操作的,生成密钥、列出密钥等),

# ! 代表取反,也就是说,这个判断条件在当  gpg这个命令不存在的时候成立,会执行then后面的内容

if ! command -v gpg > /dev/null; then \

# apt-get 是 Ubuntu中安装包的命令,等同于  centos中的  yum, 这里  就相当于先更新所有本机器上的包

apt-get update; \

# no-install-recommends参数用来避免安装非必须的文件,从而最小化安装所需要的包,下面安装的包是和密钥gpg命令相关的包

apt-get install -y --no-install-recommends \

gnupg \

dirmngr \

; \

# 删除下载的安装包

rm -rf /var/lib/apt/lists/*; \

fi

# explicitly set user/group IDs

# 创建postgres用户组,组id设置为999,在用户组postgres中创建用户postgres,用户id设置为999

RUN groupadd -r postgres --gid=999 && useradd -r -g postgres --uid=999 postgres

# grab gosu for easy step-down from root

# 设置全局变量 相当于shell中的  GOSU_VERSION=1.10

ENV GOSU_VERSION 1.10

# 等同于第四行, x 代表 执行指令后,会先显示该指令及所下的参数

RUN set -x \

# 安装 ca-certificates 证书相关的包 和  wget 从url下载文件的包,安装完删除安装包

&& apt-get update && apt-get install -y --no-install-recommends ca-certificates wget && rm -rf /var/lib/apt/lists/* \

# 从github 下载gosu , https://github.com/tianon/gosu/ 这个是gosu这个项目的网站,其功能是为了替代su和sudo,因为su和sudo在执行的时候需要TTY和信号转发的动作。

# 具体详情可以查看 https://github.com/tianon/gosu/ 上的功能介绍

# wget -O 保存下载文件的路径  下载地址

&& wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture)" \

&& wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture).asc" \

# 设置环境变量 GUNPGHOME , 这个是临时设置的,如果系统重启后,这个环境变量就会失效 (如果想要设置永久的环境变量需要写入到文件里)

&& export GNUPGHOME="$(mktemp -d)" \

# 这是配置了公钥,原文说是为了验证下载

&& gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \

# 验证下载的版本

&& gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu \

# 删除临时目录和密钥文件

&& rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc \

# 给命令  /usr/local/bin/gosu 设置可执行权限

&& chmod +x /usr/local/bin/gosu \

# 切换到nobody用户

&& gosu nobody true \

# 删除刚刚安装的包 ca-certificates 和 wget  ,  purge 是卸载并清除软件包的配置,auto-remove是卸载所有自动安装且不再使用的软件包

&& apt-get purge -y --auto-remove ca-certificates wget

# make the "en_US.UTF-8" locale so postgres will be utf-8 enabled by default

# set命令  e 代表指令返回值不为0则退出shell;u当执行时使用到未定义过的变量,显示错误信息;x执行指令后,会先显示该指令及所下的参数

RUN set -eux; \

# 判断是否有  /etc/dpkg/dpkg.cfg.d/docker 这个文件,如果有则执行后面的语句

if [ -f /etc/dpkg/dpkg.cfg.d/docker ]; then \

# if this file exists, we're likely in "debian:xxx-slim", and locales are thus being excluded so we need to remove that exclusion (since we need locales)

# grep是抓取命令, -q代表不输出结果,这个意思其实是判断  /etc/dpkg/dpkg.cfg.d/docker 文件中是否存在  '/usr/share/locale' 这个内容,

# 可以用  echo $? 来输出返回值,如果存在则返回值是0,如果不存在返回是1,又由于最开头设置了  set -eux,所以如果不存在,则后续指令直接退出

grep -q '/usr/share/locale' /etc/dpkg/dpkg.cfg.d/docker; \

# 删除 /etc/dpkg/dpkg.cfg.d/docker 文件中存在  /usr/share/locale 这个内容的一行

sed -ri '/\/usr\/share\/locale/d' /etc/dpkg/dpkg.cfg.d/docker; \

# !代表取反,这个就是验证是否删除完成

! grep -q '/usr/share/locale' /etc/dpkg/dpkg.cfg.d/docker; \

fi; \

# 更新,升级。安装locales包,删除下载的安装包

apt-get update; apt-get install -y locales; rm -rf /var/lib/apt/lists/*; \

# -i 设置语言环境, -c 代表即使报错,仍强制执行, -f 设置字符符号的语言 

localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8

# 设置环境变量,设置语言为英语,字符集为utf-8

ENV LANG en_US.utf8

# 创建目录 /docker-entrypoint-initdb.d

RUN mkdir /docker-entrypoint-initdb.d

# set命令  e 代表指令返回值不为0则退出shell;x执行指令后,会先显示该指令及所下的参数

RUN set -ex; \

# pub  4096R/ACCC4CF8 2011-10-13 [expires: 2019-07-02]

#      Key fingerprint = B97B 0AFC AA1A 47F0 44F2  44A0 7FCC 7D46 ACCC 4CF8

# uid                  PostgreSQL Debian Repository

key='B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8'; \

export GNUPGHOME="$(mktemp -d)"; \

gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \

gpg --export "$key" > /etc/apt/trusted.gpg.d/postgres.gpg; \

rm -rf "$GNUPGHOME"; \

apt-key list

ENV PG_MAJOR 10

ENV PG_VERSION 10.4-1.pgdg90+1

RUN set -ex; \

\

# 获取系统内核位数

dpkgArch="$(dpkg --print-architecture)"; \

case "$dpkgArch" in \

amd64|i386|ppc64el) \

# arches officialy built by upstream

# 如果输出结果在  amd64, i386, ppc64el的话,执行后面的命令

echo "deb http://apt.postgresql.org/pub/repos/apt/ stretch-pgdg main $PG_MAJOR" > /etc/apt/sources.list.d/pgdg.list; \

apt-get update; \

;; \

*) \

# we're on an architecture upstream doesn't officially build for

# let's build binaries from their published source packages

# 如果版本不在 amd64, i386, ppc64el 中的话,执行下面命令

echo "deb-src http://apt.postgresql.org/pub/repos/apt/ stretch-pgdg main $PG_MAJOR" > /etc/apt/sources.list.d/pgdg.list; \

\

tempDir="$(mktemp -d)"; \

cd "$tempDir"; \

\

savedAptMark="$(apt-mark showmanual)"; \

\

# build .deb files from upstream's source packages (which are verified by apt-get)

apt-get update; \

apt-get build-dep -y \

postgresql-common pgdg-keyring \

"postgresql-$PG_MAJOR=$PG_VERSION" \

; \

DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \

apt-get source --compile \

postgresql-common pgdg-keyring \

"postgresql-$PG_MAJOR=$PG_VERSION" \

; \

# we don't remove APT lists here because they get re-downloaded and removed later

\

# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies

# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies)

apt-mark showmanual | xargs apt-mark auto > /dev/null; \

apt-mark manual $savedAptMark; \

\

# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be)

ls -lAFh; \

dpkg-scanpackages . > Packages; \

grep '^Package: ' Packages; \

echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list; \

# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes")

#  Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)

#  ...

#  E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages  Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)

apt-get -o Acquire::GzipIndexes=false update; \

;; \

esac; \

\

# 安装postgresql-common包

apt-get install -y postgresql-common; \

# 修改 /etc/postgresql-common/createcluster.conf 文件的参数

sed -ri 's/#(create_main_cluster) .*$/\1 = false/' /etc/postgresql-common/createcluster.conf; \

# 安装 postgresql

apt-get install -y \

"postgresql-$PG_MAJOR=$PG_VERSION" \

; \

\

rm -rf /var/lib/apt/lists/*; \

\

if [ -n "$tempDir" ]; then \

# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)

apt-get purge -y --auto-remove; \

rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \

fi

# make the sample config easier to munge (and "correct by default")

# 配置默认的配置文件  postgresql.conf

# mv -v 这里表示移动postgresql.conf.sample 到/usr/share/postgresql/文件夹下, -v 表示显示命令执行的信息

RUN mv -v "/usr/share/postgresql/$PG_MAJOR/postgresql.conf.sample" /usr/share/postgresql/ \

# ln -sv , -s 代表设置软连接,-v 代表显示详细的处理过程

&& ln -sv ../postgresql.conf.sample "/usr/share/postgresql/$PG_MAJOR/" \

# 修改postgresql.conf.sample中的 listen_addresses参数

&& sed -ri "s!^#?(listen_addresses)\s*=\s*\S+.*!\1 = '*'!" /usr/share/postgresql/postgresql.conf.sample

# 创建 /var/run/postgresql目录,将目录的所属用户和用户组设置为postgres,将目录赋予可执行权限

RUN mkdir -p /var/run/postgresql && chown -R postgres:postgres /var/run/postgresql && chmod 2777 /var/run/postgresql

# 设置将postgresql目录设置到环境变量中

ENV PATH $PATH:/usr/lib/postgresql/$PG_MAJOR/bin

ENV PGDATA /var/lib/postgresql/data

# 创建目录环境变量中使用到的目录,并赋予777权限

RUN mkdir -p "$PGDATA" && chown -R postgres:postgres "$PGDATA" && chmod 777 "$PGDATA" # this 777 will be replaced by 700 at runtime (allows semi-arbitrary "--user" values)

# 挂载目录为  /var/lib/postgresql/data

VOLUME /var/lib/postgresql/data

# 将docker-entrypoint.sh 脚本考入到容器内的/usr/local/bin 目录下

COPY docker-entrypoint.sh /usr/local/bin/

# 将 /usr/local/bin/docker-entrypoint.sh 这个脚本软连到 / 目录下

RUN ln -s usr/local/bin/docker-entrypoint.sh / # backwards compat

# ENTRYPOINT这个命令需要在  docker run启动运行的时候执行,类似于开机启动脚本

ENTRYPOINT ["docker-entrypoint.sh"]

# 容器内部端口地址为5432

EXPOSE 5432

# 开机启动的执行命令

CMD ["postgres"]

3 Dockerfile实践

3.1 以Postgressql10.3为例

当前安装的流程是源码安装

如果要用yum安装的话。区别如下:

    1.没办法将安装文件指定目录

    2.yum会自己将依赖包进行安装,不再需要手动安装

    3.yum安装完成后会自动配置好环境变量,不再需要手动配置

    4.yum安装完成后会自动创建出默认的初始化的库

二、手工集成自定义Docker镜像

1、Docker集成HGDB

1.1部署流程图

1.2具体操作

1)启动Docker服务

2)下载centos镜像

3)用centos镜像创建容器

4)进入容器

5)安装HGDB

6)将容器打包成镜像

7)初始化宿主机数据库

8)启动镜像映射至宿主机5458端口,并挂载本地磁盘节点到容器上

9)进入到容器后,启动应用

10)登录数据库

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容

  • docker基本概念 1. Image Definition 镜像 Image 就是一堆只读层 read-only...
    慢清尘阅读 8,720评论 1 21
  • 实例来源:tofar 摘录来源:Docker — 从入门到实践 欢迎大家添加自己的实例 (email: yun_...
    molscar阅读 417评论 1 1
  • 五、Docker 端口映射 无论如何,这些 ip 是基于本地系统的并且容器的端口非本地主机是访问不到的。此外,除了...
    R_X阅读 1,734评论 0 7
  • 在前面两节我们学习了如何安装以及简单的运行管理docker容器,在本节我们将会更多的探讨关于docker镜像的知识...
    井底蛙蛙呱呱呱阅读 4,080评论 0 5
  • Docker docker常用命令: sudo docker images // 查看本机已有的镜像 docker...
    neo_ng阅读 598评论 0 1