目录
1. 镜像制作的原因和方式
(1)镜像制作是因为某种需求,官方的镜像无法满足需求,需要我们通过一定手段来自定义镜像来满足要求。制作镜像往往因为以下原因:
- 编写的代码如何打包到镜像中直接跟随镜像发布。
- 第三方制作的内容安全性未知,如含有安全漏洞。
- 特定的需求或者功能无法满足,如需要给数据库添加审计功能。
- 公司内部要求基于公司内部的系统制作镜像,如公司内部要求使用自己的操作系统作为基础镜像。
(2)制作容器镜像,主要有两种方法:
- 制作快照方式获得镜像(偶尔制作的镜像):在基础镜像上(比如 Ubuntu),先登录容器中,然后安装镜像需要的所有软件,最后整体制作快照。
- Dockerfile 方式构建镜像(经常更新的镜像):将软件安装的流程写成 Dockerfile,使用 docker build 构建成容器镜像。
2. 快照方式制作镜像
2.1 docker commit命令来制作镜像
(1)功能:
- 从容器创建一个新的镜像。
(2)语法:
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
(3)参数:
- -a:提交的镜像作者;
- -c:使用 Dockerfile 指令来创建镜像;可以修改启动指令;
- -m:提交时的说明文字;
- -p:在 commit 时,将容器暂停。
(4)样例:
docker commit c3f279d17e0a maxhou/mynginx:v01
2.2 实战C++ HelloWorld 镜像制作
(1)创建临时工作目录:
mkdir -p /data/maxhou/commitimage
cd /data/maxhou/commitimage
(2)编写 c++源代码文件,vi demo.c:
#include <stdio.h>
int main()
{
printf("hello docker!\n");
return 0;
}
(3)启动一个 centos7 的容器:
[xiaomaker@xiaomaker-virtual-machine:commitimage]$ docker run -it --name mycppcommit centos:7 bash
[root@40de1bf45017 /]#
(4)替换为国内软件源:
[root@40de1bf45017 /]# sed -e 's|^mirrorlist=|#mirrorlist=|g' \
> -e 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirro
rs.ustc.edu.cn/centos|g' \
> -i.bak \
> /etc/yum.repos.d/CentOS-Base.repo
[root@40de1bf45017 /]# yum makecache
(5)安装编译软件,并创建源代码目录:
[root@40de1bf45017 /]# yum install -y gcc
[root@40de1bf45017 /]# mkdir /src/
(6)打开另外一个 shell,拷贝源代码到容器中:
[xiaomaker@xiaomaker-virtual-machine:commitimage]$ docker cp ./demo.c mycppcommit:/src
Successfully copied 2.048kB to mycppcommit:/src
#查看容器中
[root@40de1bf45017 /]# ls -l /src
total 4
-rw-r--r-- 1 root root 80 May 16 05:30 demo.c
(7)编译运行:
[root@40de1bf45017 /]# cd /src
[root@40de1bf45017 src]# gcc demo.c -o demo
[root@40de1bf45017 src]# ./demo
hello docker!
(8)提交为一个镜像:
[xiaomaker@xiaomaker-virtual-machine:commitimage]$ docker commit mycppcommit mycppimg:v1.0
sha256:97d178ba9e5da794dd8276fe0ee23dc73510abea7f03f7f3c3a59a978dc8fa2c
[xiaomaker@xiaomaker-virtual-machine:commitimage]$ docker images mycppimg
REPOSITORY TAG IMAGE ID CREATED SIZE
mycppimg v1.0 97d178ba9e5d 13 seconds ago 714MB
(9)测试镜像能否正常运行:
[xiaomaker@xiaomaker-virtual-machine:commitimage]$ docker run -it mycppimg:v1.0 ./src/demo
hello docker!
3. Dockerfile 制作镜像
3.1 Dockerfile 是什么
(1)概念如下:
- 镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,这个脚本就是 Dockerfile。
- Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction), 每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
(2)Dockerfile 格式:
# Comment
INSTRUCTION arguments
- 该指令不区分大小写。然而,约定是它们是大写的,以便更容易地将它们与参数区分开来。
- Docker 按顺序运行指令 Dockerfile。
- Docker 将以开头的行视为#注释,行中其他任何地方的标记#都被视为参数。这允许像这样的语句:
# Comment
RUN echo 'we are running some # of cool things'
(3)生活案例来理解什么是Dockerfile:
- Dockerfile 就像我们盖房子的时候的施工图纸,地基多深有多少层,每层是什么,以及多高。
3.2 为什么需要 Dockerfile
(1)可以按照需求自定义镜像:
- 和 docker commit 一样能够自定义镜像,官方的镜像可以说很少能直接满足我们应用的,都需要我们自己打包自己的代码进去然后做成对应的应用镜像对外使用。
(2)很方便的自动化构建,重复执行:
- 通过 dockerfile 可以自动化的完成镜像构建,而不是像 docker commit 一样,手动一个命令一个命令执行,而且可以重复执行, docker commit 的话很容易忘记执行了哪个命令,哪个命令没有执行。
(3)维护修改方便,不再是黑箱操作:
- 使用 docker commit 意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为黑箱镜像,dockerfile 很容易二次开发。
(4)更加标准化,体积可以做的更小:
- docker 容器启动后,系统运行会生成很多运行时的文件,如果使用 commit 会导致这些文件也存储到镜像里面,而且 commit 的时候安装了很多的依赖文件,没有有效的清理机制的话会导致镜像非常的臃肿。
- 使用 Dockerfile 则会更加标准化,而且提供多级构建,将编译和构建分开,不会有运行时的多余文件,更加的标准化。
3.3 Dockerfile 指令
3.3.1 指令清单
(1)指令如下以及官方文档链接:https://docs.docker.com/reference/dockerfile/。
指令 | 功能 | 备注 |
---|---|---|
FROM | 构建镜像基于哪个镜像,也就是基础镜像 | 必须掌握 |
MAINTAINER | 镜像维护者姓名或邮箱地址 | 已经废弃,被label 替代了 |
LABEL | 为镜像添加元数据 | |
COPY | 拷贝文件或目录到镜像中,跟 ADD 类似,但不具备自动下载或解压的功能 | 必须掌握 |
ADD | 拷贝文件或目录到镜像中,如果是 URL 或压缩包便会自动下载或自动解压 | 必须掌握 |
WORKDIR | 指定工作目录 | 必须掌握 |
RUN | 指定 docker build 过程中运行的程序 | 必须掌握 |
VOLUME | 指定容器挂载点 | |
EXPOSE | 声明容器的服务端口(仅仅是声明) | |
ENV | 设置环境变量 | 必须掌握 |
CMD | 运行容器时执行的命令 | 必须掌握 |
ENTRYPOINT | 运行容器时程序入口 | 必须掌握 |
ARG | 指定构建时的参数 | |
SHELL | 指定采用哪个 shell | 使用较少 |
USER | 指定当前用户 | |
HEALTHCHECK | 健康检测指令 | |
ONBUILD | 在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。 | 使用较少 |
STOPSIGNAL | 允许您覆盖发送到容器的默认信号。 | 使用较少 |
下面我们详细介绍 Dockerfile 当中重要的一些指令,一些不经常使用的参考官方文档。
3.3.2 FROM指令
(1)功能:
- FROM 指令用于为镜像文件构建过程指定基础镜像,后续的指令运行于此基础镜像所提供的运行环境;
(2)注意事项:
- FROM 指令必须是 Dockerfile 中非注释行或者 ARG 之后的第一个指令;
- 实践中,基准镜像可以是任何可用镜像文件,默认情况下, docker build 会在docker 主机上查找指定的镜像文件,在其不存在时,则会自动从 Docker 的公共库 pull 镜像下来。如果找不到指定的镜像文件, docker build 会返回一个错误信息;
- FROM 可以在一个 Dockerfile 中出现多次,如果有需求在一个 Dockerfile 中创建多个镜像,或将一个构建阶段作为另一个的依赖。
- 如果 FROM 语句没有指定镜像标签,则默认使用 latest 标签。
(3)语法:
FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
(4)参数:
- :构建的 cpu 架构,如 linux/amd64,linux/arm64,windows/amd64
- <image>:指定作为 base image 的名称;
- <tag>: base image 的标签,省略时默认 latest;
- <digest>:是镜像的哈希码;
- AS <name>: 指定构建步骤的名称,配合 COPY --from=<name>可以完成多级构建。
(5)样例:
FROM busybox:latest
(6)实战:
- 创建 Docker 目录,确保目录中没有内容:
mkdir -p /data/myworkdir/dockerfile/web1
cd /data/myworkdir/dockerfile/web1
- 编辑 Dockerfile,测试 FROM 指令和注释,在 web1 目录中 vi Dockerfile,输入以下内容:
#我的 web 站点 by maxhou
FROM ubuntu:22.04 as buildbase
- 执行构建,打造镜像 v0.1 版本:
[xiaomaker@xiaomaker-virtual-machine:web1]$ docker build -t web1:v0.1 .
- 运行制作的镜像,可以看到操作系统版本:
[xiaomaker@xiaomaker-virtual-machine:web1]$ docker run --name web1 --rm -it web1:v0.1 cat /etc/*release*
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=22.04
DISTRIB_CODENAME=jammy
DISTRIB_DESCRIPTION="Ubuntu 22.04.2 LTS"
PRETTY_NAME="Ubuntu 22.04.2 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.2 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-andpolicies/privacy-policy"
UBUNTU_CODENAME=jammy
3.3.3 MAINTAINER指令
(1)功能:
- 用于让 dockerfile 制作者提供本人的详细信息
- 该功能已经废弃,由 label 替代
(2)语法:
MAINTAINER <authtor's detail>
(3)参数:
- <authtor’s detail>:作者信息。
(4)样例:
MAINTAINER "maxhou <maxhou@xiaomaker.com>"
(5)实战:
- 接 FROM 添加制作者信息,使用 MAINTAINER:
#我的 web 站点 by maxhou
FROM ubuntu:22.04 as buildbase
MAINTAINER "maxhou maxhou@xiaomaker.com"
- 再次编译 0.2 版本:
[xiaomaker@xiaomaker-virtual-machine:web1]$ docker build -t web1:v0.2 .
- 查看镜像信息,可以看到作者信息已经添加完成:
[xiaomaker@xiaomaker-virtual-machine:web1]$ docker image inspect web1:v0.2
[
{
....
{
"DockerVersion": "",
"Author": "\"maxhou maxhou@xiaomaker.com\"",
}
....
3.3.4 LABEL指令
(1)功能:
- 为镜像添加元数据,元数据是 kv 对形式
(2)语法:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
(3)样例:
LABEL com.example.label-with-value="foo"
LABEL multi.label1="value1" multi.label2="value2" other="value3"
(4)实战:
- 我们使用 LABEL 添加额外的元数据信息,继续 vi Dockerfile:
#我的 web 站点 by maxhou
FROM ubuntu:22.04 as buildbase
MAINTAINER "maxhou maxhou@xiaomaker.com"
LABEL company="com.bit" app="nginx"
- 我们继续构建 v0.3 版本:
[xiaomaker@xiaomaker-virtual-machine:web1]$ docker build -t web1:v0.3 .
- 查看镜像元数据:
[xiaomaker@xiaomaker-virtual-machine:web1]$ docker inspect web1:v0.3
[
....
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
"],
"Cmd": [
"/bin/bash"
],
"Image": "",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {
"app": "nginx",
"company": "com.bit",
"org.opencontainers.image.ref.name": "ubuntu",
"org.opencontainers.image.version": "22.04"
}
},
3.3.5 COPY指令
(1)功能:
- 用于从 docker 主机复制新文件或者目录至创建的新镜像指定路径中 。
(2)语法:
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
(3)参数:
- <src>:要复制的源文件或目录, 支持使用通配符;
- <dest>:目标路径,即正在创建的 image 的文件系统路径; 建议使用绝对路径,否则, COPY 指定以 WORKDIR 为当前路径在路径中有空白字符时,通常使用第 2 种格式;
- - -chown:修改用户和组。
- - -from <name>可选项:可以从之前构建的步骤中拷贝内容,结合 FROM … AS <name>往往用作多级构建,后续我们有实战课专门完成多级构建
(4)注意事项:
- <src>必须是 build 上下文中的路径, 不能是其父目录中的文件;
- 如果<src>是目录,则其内部文件或子目录会被递归复制,但目录自身不会被复制;
- 如果指定了多个<src>,或在<src>中使用了通配符,则<dest>必须是一个目录,且必须以 / 结尾;
- 如果<dest>事先不存在,它将会被自动创建,这包括父目录路径。
(5)样例:
COPY index.html /data/web/html/ #要确保 dockerfile 同级路径下有index.html 文件
(6)实战:
- 创建一个 index.html,作为我们站点的首页 ,vi index.html 输入下面内容:
<html>
<h1>Hello ,My Home Page! by bit</h1>
</html>
- 我们通过 COPY 命令添加到我们的镜像中,并且我们指定我们的根目录为/data/web/www:
[xiaomaker@xiaomaker-virtual-machine:web1]$ cat Dockerfile
#我的 web 站点 by maxhou
FROM ubuntu:22.04 as buildbase
MAINTAINER "maxhou maxhou@xiaomaker.com"
LABEL company="com.bit" app="nginx"
COPY index.html /data/web/www/
- 我们再次编译 v0.4 版本镜像:
[xiaomaker@xiaomaker-virtual-machine:web1]$ docker build -t web1:v0.4 .
- 我们运行镜像,可以看到 index.html 已经进去了:
[xiaomaker@xiaomaker-virtual-machine:web1]$ docker run --name web1 --rm -it web1:v0.4 ls /data/web/www
index.html
3.3.6 ENV指令
(1)功能:
- 用于为镜像定义所需的环境变量,并可被 Dockerfile 文件中位于其后的其它指
令(如 ENV、 ADD、 COPY 等)所调用。 - 调用格式为$variable_name 或 ${variable_name}。
(2)语法:
ENV <key>=<value> ...
(3)样例:
ENV MY_NAME="John Doe"
(4)实战:
- 目录后面可能复用,我们将目录的位置提取为变量,通过 ENV 来设置,我们再次编辑 Dockerfile:
#我的 web 站点 by maxhou
FROM ubuntu:22.04 as buildbase
MAINTAINER "maxhou maxhou@xiaomaker.com"
LABEL company="com.bit" app="nginx"
ENV WEB_ROOT=/data/web/www/
COPY index.html ${
WEB_ROOT}
- 我们再次编译 v0.5 版本镜像:
[xiaomaker@xiaomaker-virtual-machine:web1]$ docker build -t web1:v0.5 .
- 我们运行 v0.5 版本镜像,然后看下 index.html 是否在${WEB_ROOT}的目录下面
[xiaomaker@xiaomaker-virtual-machine:web1]$ docker run --name web1 --rm -it web1:v0.5 ls /data/web/www
index.html
- 我们也可以通过 inspect 查看镜像,可以看到环境变量已经内置到镜像里面了
[xiaomaker@xiaomaker-virtual-machine:web1]$ docker image inspect web1:v0.5
[
....
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
",
"WEB_ROOT=/data/web/www/"
],
3.3.7 WORKDIR指令
(1)功能:
- 为 Dockerfile 中所有的 RUN、 CMD、 ENTRYPOINT、 COPY 和 ADD 指定设定工作目录
(2)语法:
WORKDIR /path/to/workdir
(3)注意事项:
- 默认的工作目录是/。
- 如果提供了相对路径,它将相对于前一条 WORKDIR 指令的路径。
- WORKDIR 指令可以解析先前使用设置的环境变量 ENV。
(4)样例:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
#此处最终命令的输出 Dockerfile 为/a/b/c.
(5)实战:
- 后面我们要下载 nginx,我们指定一个工作目录,通过 WORKDIR 来指定,后续我们就可以使用相对路径来执行 nginx 的编译了。我们再次编辑 Dockerfile:
#我的 web 站点 by maxhou
FROM ubuntu:22.04 as buildbase
MAINTAINER "maxhou maxhou@xiaomaker.com"
LABEL company="com.bit" app="nginx"
ENV WEB_ROOT=/data/web/www/
COPY index.html ${
WEB_ROOT}
WORKDIR /usr/local
- 我们再次编译镜像 v0.6:
[xiaomaker@xiaomaker-virtual-machine:web1]$ docker build -t web1:v0.6 .
- 执行 pwd 命令查看当前目录:
[xiaomaker@xiaomaker-virtual-machine:web1]$ docker run --name web1 --rm -it web1:v0.6 pwd
/usr/local
3.3.8 ADD指令
(1)功能:
- ADD 指令类似于 COPY 指令, ADD 支持使用 TAR 文件和 URL 路径,会自动
完成解压和下载
(2)语法:
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
(3)参数:
- <src>:要复制的源文件或目录, 支持使用通配符;
- <dest>:目标路径,即正在创建的 image 的文件系统路径; 建议使用
绝对路径,否则, ADD 指定以 WORKDIR 为其实路径;在路径中有空白字符时,通常使用第 2 种格式; - - -chown:修改用户和组。
(4)实战:
- 我们登录 nginx 官网 https://nginx.org/,找到最新稳定版本的 nginx 的下载地址https://nginx.org/en/download.html,可以看到最新版本为 1.22.1,我们复制该链接https://nginx.org/download/nginx-1.22.1.tar.gz:
- 有了链接地址后,我们发现这是一个 URL 地址,我们通过 ADD 命令下载,因为nginx 的未来版本还会变,所以我们可以提取 ningx 的版本为环境变量,我们再次编辑我们的 Dockerfile:
#我的 web 站点 by maxhou
FROM ubuntu:22.04 as buildbase
MAINTAINER "maxhou maxhou@xiaomaker.com"
LABEL company="com.bit" app="nginx"
ENV WEB_ROOT=/data/web/www/
ENV NGINX_VERSION="nginx-1.22.1"
COPY index.html ${
WEB_ROOT}
WORKDIR /usr/local
ADD https://nginx.org/download/${
NGINX_VERSION}.tar.gz ./src
- 我们执行编译 v0.7:
[xiaomaker@xiaomaker-virtual-machine:web1]$ docker build -t web1:v0.7 .
- 我们运行 v0.7 镜像查看,看是否已经完成了下载:
[xiaomaker@xiaomaker-virtual-machine:web1]$ docker run --name web1 --rm -it web1:v0.7 ls -l /usr/local/src
total 1052
-rw------- 1 root root 1073948 Oct 19 09:23 nginx-1.22.1.tar.gz
-
可以看到此时并没有被解压。
-
我们手动下载下来这个压缩包,放到我们的服务器目录,此时目录的结构如下:
[xiaomaker@xiaomaker-virtual-machine:web1]$ wget https://nginx.org/download/nginx-1.22.1.tar.gz
[xiaomaker@xiaomaker-virtual-machine:web1]$ ll -h
total 1.1M
drwxr-xr-x 2 root root 4.0K Mar 14 15:32 ./
drwxr-xr-x 4 root root 4.0K Mar 14 14:37 ../
-rw-r--r-- 1 root root 298 Mar 14 15:26 Dockerfile
-rw-r--r-- 1 root root 53 Mar 14 15:01 index.html
-rw-r--r-- 1 root root 1.1M Oct 19 17:23 nginx-1.22.1.tar.gz
- 我们再次编辑 Dockerfile,添加将 nginx 放到 src2 目录:
#我的 web 站点 by maxhou
FROM ubuntu:22.04 as buildbase
MAINTAINER "maxhou maxhou@xiaomaker.com"
LABEL company="com.bit" app="nginx"
ENV WEB_ROOT=/data/web/www/
ENV NGINX_VERSION="nginx-1.22.1"
COPY index.html ${
WEB_ROOT}
WORKDIR /usr/local
ADD https://nginx.org/download/${
NGINX_VERSION}.tar.gz ./src
ADD ${
NGINX_VERSION}.tar.gz ./src2
- 执行命令编译 v0.8:
docker build -t web1:v0.8 .
- 我们运行 v0.8 版本的镜像,然后查看该镜像的/usr/local/src2 目录,可以看到nginx 已经被解压:
[xiaomaker@xiaomaker-virtual-machine:web1]$ docker run --name web1 --rm -it web1:v0.8 ls -l /usr/local/src2
total 4
drwxr-xr-x 8 1001 1001 4096 Oct 19 08:02 nginx-1.22.1
3.3.9 RUN指令
(1)功能:
- 用于指定 docker build 过程中运行的程序,其可以是任何命令
(2)语法&#