一、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-alpine, 9.5-alpine (9.5/alpine/Dockerfile)
9.4.18-alpine, 9.4-alpine (9.4/alpine/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)登录数据库