目录
一.镜像说明
1.Docker 镜像中有没有内核
从镜像大小上面来说,一个比较小的镜像只有1MB多点或几MB,而内核文件需要几十MB, 因此镜像里面是没有内核的,镜像在被启动为容器后将直接使用宿主机的内核,而镜像本身则只提供相应的rootfs,即系统正常运行所必须的用户空间的文件系统,比如: /dev/,/proc,/bin,/etc等目录,容器当中/boot目录是空的,而/boot当中保存的就是与内核相关的文件和目录。
2.为什么没有内核
由于容器启动和运行过程中是直接使用了宿主机的内核,不会直接调用物理硬件,所以也不会涉及到硬件驱动,因此也无需容器内拥有自已的内核和驱动。而如果使用虚拟机技术,对应每个虚拟机都有自已独立的内核
3.容器中的程序后台运行会导致此容器启动后立即退出
Docker容器如果希望启动后能持续运行,就必须有一个能前台持续运行的进程,如果在容器中启动传统的服务,如:httpd,php-fpm等均为后台进程模式运行,就导致 docker 在前台没有运行的应用,这样的容器启动后会立即退出。所以一般会将服务程序以前台方式运行,对于有一些可能不知道怎么实现前台运行的程序,只需要在你启动的该程序之后添加类似于 tail ,top 这种可以前台运行的程序即可. 比较常用的方法,如 tail -f /etc/hosts
#httpd
ENTRYPOINT [ "/usr/sbin/apache2" ]
CMD ["-D", "FOREGROUND"]
#nginx
ENTRYPOINT [ "/usr/sbin/nginx", "-g", "daemon off;" ]
#用脚本运行容器
cat run_haproxy.sh
#!/bin/bash
haproxy -f /etc/haproxy/haproxy.cfg
tail -f /etc/hosts
tail -n1 Dockerfile
CMD ["run_haproxy.sh"]
4.docker 镜像的生命周期
5.镜像的制作方式
Docker 镜像制作类似于虚拟机的镜像(模版)制作,即按照公司的实际业务需求将需要安装的软件、相关配置等基础环境配置完成,然后将其做成镜像,最后再批量从镜像批量生成容器实例,这样可以极大的简化相同环境的部署工作.
Docker的镜像制作分为手动制作(基于容器)和自动制作(基于DockerFile),企业通常都是基于Dockerfile制作镜像
总结:
-
下载官方或者别人制作好的 镜像进行修改
-
利用dockerfile 生成自己的镜像
docker commit #通过修改现有容器,将之手动构建为镜像
docker build #通过Dockerfile文件,批量构建为镜像
二.手动构建镜像
1.基于容器手动制作镜像步骤
docker commit 格式
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
# REPOSITORY 软件名
# TAG 版本号
#选项 OPTIONS
-a, --author string: 指定提交镜像时的作者信息。格式通常是 "作者姓名 <电子邮件地址>",例如 "John Hannibal Smith hannibal@ateam.com"。
-c, --change list: 在创建的镜像上应用 Dockerfile 指令。这个选项允许你在提交镜像时执行额外的 Dockerfile 操作,类似于在 Dockerfile 中使用的指令。
-m, --message string: 提交时的提交消息,用于描述这次提交的目的或变更内容。
-p, --pause: 在提交镜像时是否暂停容器。默认情况下为 true,即在提交之前暂停容器。
#说明:
制作镜像和CONTAINER状态无关,停止状态也可以制作镜像
如果没有指定[REPOSITORY[:TAG]],REPOSITORY和TAG都为<none>
提交的时候标记TAG号: 生产当中常用,后期可以根据TAG标记创建不同版本的镜像以及创建不同版本的容器
基于容器手动制作镜像步骤具体如下:
下载一个系统的官方基础镜像,如: CentOS 或 Ubuntu
基于基础镜像启动一个容器,并进入到容器
在容器里面做配置操作
< 安装基础命令
< 配置运行环境
< 安装服务和配置服务
< 放业务程序代码提交为一个新镜像 docker commit
基于自己的的镜像创建容器并测试访问
2.实战案例
2.1 基于busybox 制作 httpd 镜像
docker run -it --name b1 busybox
#启动 busybox 容器
/ # hostname -i
/ # httpd --help 进入容器查看 httpd 的帮助
/ # mkdir /data/html -p 新建主站点目录
/ # echo testpage > /data/html/index.html 新建站点目录
/ # httpd -f -h /data/html/ -f 前台启动 -h 指定主站点
/ # httpd --help
BusyBox v1.36.1 (2023-05-18 22:34:17 UTC) multi-call binary.
Usage: httpd [-ifv[v]] [-c CONFFILE] [-p [IP:]PORT] [-u USER[:GRP]] [-r REALM] [-h HOME]
or httpd -d/-e/-m STRING
Listen for incoming HTTP requests
-i Inetd mode
-f Run in foreground #前台运行
-v[v] Verbose
-p [IP:]PORT Bind to IP:PORT (default *:80)
-u USER[:GRP] Set uid/gid after binding to port
-r REALM Authentication Realm for Basic Authentication
-h HOME Home directory (default .) #指定主站点, 否则是当前目录
-c FILE Configuration file (default {/etc,HOME}/httpd.conf)
-m STRING MD5 crypt STRING
-e STRING HTML encode STRING
-d STRING URL decode STRING
docker commit -a "zhou" -c 'CMD /bin/httpd -fv -h /data/html' -c "EXPOSE 80" b1 httpd-busybox:v1.0
docker images
启动制作好的容器
docker run -d --name web1 -P httpd-busybox:v1.0
# 将容器启动然后 -
docker port web1
#测试
curl 192.168.240.11:32770
2.2 基于官方镜像生成的容器制作tomcat镜像
下载启动 tomcat 容器
docker run -d -p 8080:8080 tomcat
#下载启动 tomcat 容器
修改容器
docker ps -a
docker exec -it 0b3c bash
# 进入容器
ls
cp -a webapps.dist/* webapps/
打包成镜像
docker commit -m "add webapps app" -a "zhou" 0b3c4d7e9e2e tomcat
docker images
启动镜像
docker run -d --name t1 -p 8081:8080 tomcat
2.3 基于CentOS的基础镜像利用yum安装手动制作nginx的镜像
启动容器
docker run -it --name c1 centos:latest bash
cat /etc/redhat-release
wget http://mirrors.aliyun.com/repo/Centos-8.repo
#在宿主机中下载 yum 文件
docker ps -a
#查看相关容器 id
docker cp Centos-8.repo e12b218ed4b3:/
#使用 docker cp 命令将文件拷贝进 容器
Successfully copied 4.61kB to e12b218ed4b3:/
yum install wget -y
#进入容器测试
安装nginx ,修改配置文件
yum install nginx net-tools vim -y
vim /etc/nginx/nginx.conf
daemon off;
#前台执行
#生成页面
echo "nginx page in docker" > /usr/share/nginx/html/index.html
打包成镜像
docker commit -a 'zhou' -m 'yum nginx' -c "EXPOSE 80 443" c1 centos:nginx-v1
启动容器
docker run -d -p 80:80 --name n1 centos:nginx-v1 nginx
alias rmc="docker ps -aq | xargs -n 1 docker rm -f"
#批量删除容器
三.自动构建镜像
1.Dockerfile使用详解
DockerFile 是一种被Docker程序解释执行的脚本,由一条条的命令组成的,每条命令对应linux下面的一条命令,Docker程序将这些DockerFile指令再翻译成真正的linux命令,其有自己的书写方式和支持的命令,Docker程序读取DockerFile并根据指令生成Docker镜像,相比手动制作镜像的方式,DockerFile更能直观的展示镜像是怎么产生的,有了DockerFile,当后期有额外的需求时,只要在之前的DockerFile添加或者修改响应的命令即可重新生成新的Docker镜像,避免了重复手动制作镜像的麻烦,类似与shell脚本一样,可以方便高效的制作镜像Docker守护程序 Dockerfile 逐一运行指令,如有必要,将每个指令的结果提交到新镜像,然后最终输出新镜像的ID。Docker守护程序将自动清理之前发送的上下文
请注意,每条指令都是独立运行的,并会导致创建新镜像,比如 RUN cd /tmp 对下一条指令不会有任何影响。
Docker将尽可能重用中间镜像层(缓存),以显著加速 docker build 命令的执行过程,这由 Using cache 控制台输出中的消息指示
1.1 Dockerfile镜像制作和使用流程
Dockerfile 构建过程
-
从基础镜像运行一个容器
-
执行一条指令,对容器做出修改
-
执行类似docker commit的操作,提交一个新的中间镜像层(可以利用中间层镜像创建容器进行调试和排错)
-
再基于刚提交的镜像运行一个新容器
-
执行Dockerfile中的下一条指令,直至所有指令执行完毕
1.2 Dockerfile文件的制作镜像的分层结构
1.3 Dockerfile文件格式
Dockerfile 是一个有特定语法格式的文本文件
dockerfile 官方说明: https://docs.docker.com/engine/reference/builder/
帮助: man 5 dockerfile
1.4 Docker 文件说明
-
每一行以Dockerfile的指令开头,指令不区分大小写,但是惯例使用大写
-
使用 # 开始作为注释
-
每一行只支持一条指令,每条指令可以携带多个参数
-
指令按文件的顺序从上至下进行执行
-
每个指令的执行会生成一个新的镜像层,为了减少分层和镜像大小,尽可能将多条指令合并成一条指令
-
制作镜像一般可能需要反复多次,每次执行dockfile都按顺序执行,从头开始,已经执行过的指令已经缓存,不需要再执行,如果后续有一行新的指令没执行过,其往后的指令将会重新执行,所以为加速镜像制作,将最常变化的内容放下dockerfile的文件的后面
1.5 Dockerfile 相关指令
dockerfile 文件中的常见指令:
常用指令 | 含义 |
---|---|
FROM | 指定基础镜像(是Dockerfile的第一行指令) |
MAINTAINER | 指定镜像维护人信息(可选) |
RUN | 指定Linux命令,如果要执行连续多条命令时建议用 ; 或 && 或 <<EOF 串起来使用 |
EXPOSE | 指定容器应用的端口 |
ENV | 设置镜像的环境变量 |
ADD/COPY | 复制文件/目录到镜像里 |
VOLUME | 指定容器的匿名数据卷 |
USER | 指定容器的运行用户 |
WORKDIR | 指定后续镜像层的工作目录 |
ARG | 指定构建镜像时传入的参数变量,docker build --build-arg 变量=值 |
CMD/ENTRYPOINT | 指定容器启动时执行的命令 |
1.5.1 FROM:指定基础镜像
定制镜像,需要先有一个基础镜像,在这个基础镜像上进行定制。
FROM 就是指定基础镜像,此指令通常必需放在Dockerfile文件第一个非注释行。后续的指令都是运行于此基准镜像所提供的运行环境
基础镜像可以是任何可用镜像文件,默认情况下,docker build会在docker主机上查找指定的镜像文件,在其不存在时,则会从Docker Hub Registry上拉取所需的镜像文件.如果找不到指定的镜像文件,docker build会返回一个错误信息
如何选择合适的镜像呢?
对于不同的软件官方都提供了相关的docker镜像,比如: nginx、redis、mysql、httpd、tomcat等服务类的镜像,也有操作系统类,如: centos、ubuntu、debian等。建议使用官方镜像,比较安全。
格式:
FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
#说明:
--platform 指定镜像的平台,比如: linux/amd64, linux/arm64, or windows/amd64
tag 和 digest是可选项,如果不指定,默认为latest
https://hub.docker.com/_/centos/tags
示例:
mkdir /data/dockerfile/system/centos/centos7 -p
cd /data/dockerfile/system/centos/centos7/
vim Dockerfile
FROM centos:7
LABEL author="zhou cloud" \
version="1.0" \
description="test"
RUN mkdir /app
docker build -t c7:v3 .
docker run -it c7:v3 bash
1.5.2 LABEL:指定镜像元数据
LABEL: 说明信息
可以指定镜像元数据,如: 镜像作者等
LABEL <key>=<value> <key>=<value> <key>=<value> ...
示例:
LABEL "com.example.vendor"="ACME Incorporated"
#这行指令定义了一个标签,其键为 "com.example.vendor",对应的值为 "ACME Incorporated"。这个标签通常用来指明镜像的供应商或者制造商。
LABEL com.example.label-with-value="foo"
#这行指令定义了另一个标签,其键为 com.example.label-with-value,对应的值为 "foo"。这个标签是一个示例,它展示了如何设置一个简单的键值对标签。
LABEL version="1.0"
这行指令定义了一个标签,其键为 version,对应的值为 "1.0"。这个标签通常用来指明镜像的版本号。
LABEL description="This text illustrates \
that label-values can span multiple lines."
这行指令定义了一个名为 description 的标签,对应的值是一个多行文本 "This text illustrates that label-values can span multiple lines."。在 Dockerfile 中,可以使用 \ 符号来跨行定义值。
一个镜像可以有多个label ,还可以写在一行中,即多标签写法,可以减少镜像的的大小
1.5.3 RUN:执行shell命令
RUN 指令用来在构建镜像阶段需要执行 FROM 指定镜像所支持的Shell命令。
通常各种基础镜像一般都支持丰富的shell命令
注意: RUN 可以写多个,每一个RUN指令都会建立一个镜像层,所以尽可能合并成一条指令,比如将多个shell命令通过 && 连接一起成为在一条指令
每个RUN都是独立运行的,和前一个RUN无关
#shell 格式: 相当于 /bin/sh -c <命令> 此种形式支持环境变量
RUN <命令>
#exec 格式: 此种形式不支持环境变量,注意:是双引号,不能是单引号
RUN ["可执行文件", "参数1", "参数2"]
#exec格式可以指定其它shell
RUN ["/bin/bash","-c","echo hello wang"]
1.5.4 COPY :复制文本
复制本地宿主机的 到容器中的
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"] #路径中有空白字符时,建议使用此格式
说明:
- 可以是多个,可以使用通配符,通配符规则满足Go的filepath.Match 规则filepath.Match 参考链接: https://golang.org/pkg/path/filepath/#Match
- 必须是build上下文中的路径(为 Dockerfile 所在目录的相对路径),**不能是其父目录中的文件**
- 如果是目录,则其内部文件或子目录会被递归复制,**但目录自身不会被复制**
- 如果指定了多个, 或在中使用了通配符,则必须是一个目 录,**且必须以** **/** **结尾**
- 可以是绝对路径或者是 WORKDIR 指定的相对路径
- 使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等
- 如果事先不存在,它将会被自动创建,这包括其父目录路径,即递归创建目录
1.5.5 ADD:制和解包文件
该命令可认为是增强版的COPY,不仅支持COPY,还支持自动解缩。可以将复制指定的 到容器中的
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
说明:
- 可以是Dockerfile所在目录的一个相对路径;也可是一个 URL;还可是一个 tar 文件(自动解压)
- 可以是绝对路径或者是 WORKDIR 指定的相对路径
- 如果是目录,只复制目录中的内容,而非目录本身
- 如果是一个 URL ,下载后的文件权限自动设置为 600- 如果为URL且不以/结尾,则指定的文件将被下载并直接被创建为,如果以 / 结尾,则文件名URL指定的文件将被直接下载并保存为/< filename>
- 如果是一个本地文件系统上的打包文件,如: gz, bz2 ,xz ,它将被解包 ,其行为类似于"tar -x"命令,但是通过URL获取到的tar文件将不会自动展开
- 如果有多个,或其间接或直接使用了通配符,则必须是一个以/结尾的目录路径;如果不以/结尾,则其被视作一个普通文件,的内容将被直接写入到
1.5.6 CMD:容器启动命令
一个容器中需要持续运行的进程一般只有一个,CMD 用来指定启动容器时默认执行的一个命令,且其运行结束后,容器也会停止,所以一般CMD 指定的命令为持续运行且为前台命令.
-
如果docker run没有指定任何的执行命令或者dockerfile里面也没有ENTRYPOINT,那么开启容器时就会使用执行CMD指定的默认的命令
-
前面介绍过的 RUN 命令是在构建镜像进执行的命令,注意二者的不同之处
-
每个 Dockerfile 只能有一条 CMD 命令。如指定了多条,只有最后一条被执行
-
如果用户启动容器时用 docker run xxx 指定运行的命令,则会覆盖 CMD 指定的命令
# 使用 exec 执行,推荐方式,第一个参数必须是命令的全路径,此种形式不支持环境变量
CMD ["executable","param1","param2"]
# 在 /bin/sh 中执行,提供给需要交互的应用;此种形式支持环境变量
CMD command param1 param2
# 提供给 ENTRYPOINT 命令的默认参数
CMD ["param1","param2"]
1.5.7 ENTRYPOINT:入口点
功能类似于CMD,配置容器启动后执行的命令及参数
# 使用 exec 执行
ENTRYPOINT ["executable", "param1", "param2"]
# shell中执行
ENTRYPOINT command param1 param2
ENTRYPOINT 不能被 docker run 提供的参数覆盖,而是追加,即如果docker run 命令有参数,那么参数全部都会作为ENTRYPOINT的参数
如果docker run 后面没有额外参数,但是dockerfile中的CMD里有(即上面CMD的第三种用法),即Dockerfile中即有CMD也有ENTRYPOINT,那么CMD的全部内容会作为ENTRYPOINT的参数
如果docker run 后面有额外参数,同时Dockerfile中即有CMD也有ENTRYPOINT,那么docker run后面的参数覆盖掉CMD参数内容,最终作为ENTRYPOINT的参数
可以通过docker run --entrypoint string 参数在运行时替换,注意string不要加空格
使用CMD要在运行时重新写命令本身,然后在后面才能追加运行参数,ENTRYPOINT则可以运行时无需重写命令就可以直接接受新参数
每个 Dockerfile 中只能有一个 ENTRYPOINT,当指定多个时,只有最后一个生效
1.5.8 ARG:构建参数
ARG指令在build 阶段指定变量,和ENV不同的是,容器运行时不会存在这些环境变量
ARG <name>[=<default value>]
如果和ENV同名,ENV覆盖ARG变量
可以用 docker build --build-arg <参数名>=<值> 来覆盖
说明: ARG 和 FROM
#FROM指令支持由第一个FROM之前的任何ARG指令声明的变量
#示例:
ARG CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD /code/run-app
FROM extras:${CODE_VERSION}
CMD /code/run-extras
#在FROM之前声明的ARG在构建阶段之外,所以它不能在FROM之后的任何指令中使用。 要使用在第一个FROM之前声明的ARG的默认值,请在构建阶段内使用没有值的ARG指令
#示例:
ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version
1.5.9 VOLUME:匿名卷
在容器中创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等,一般会将宿主机上的目录挂载至VOLUME 指令指定的容器目录。即使容器后期被删除,此宿主机的目录仍会保留,从而实现容器数据的持久保存。
宿主机目录为
/var/lib/docker/volumes/<volume_id>/_data
语法:
vOLUME <容器内路径>
VOLUME ["<容器内路径1>", "<容器内路径2>"...]
注意:
-
Dockerfile中的VOLUME实现的是匿名数据卷,无法指定宿主机路径和容器目录的挂载关系
-
通过docker rm -fv <容器ID> 可以删除容器的同时删除VOLUME指定的卷
在容器创建两个/data/ ,/data2的挂载点
VOLUME [ "/data1","/data2" ]
1.5.10 EXPOSE: 暴露端口
指定服务端的容器需要对外暴露(监听)的端口号,以实现容器与外部通信。EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会真正暴露端口,即不会自动在宿主进行端口映射因此,在启动容器时需要通过 -P 或-p ,Docker 主机才会真正分配一个端口转发到指定暴露的端口才可使用
注意:即使Dockerfile没有EXPOSE 端口指令,也可以通过docker run -p临时暴露容器内程序真正监听的端口,所以EXPOSE 相当于指定默认的暴露端口,可以通过docker run -P 进行真正暴露
EXPOSE <port>[/ <protocol>] [<port>[/ <protocol>] ..]
#说明
<protocol>用于指定传输层协议,可为tcp或udp二者之一,默认为TCP协议
1.5.11 WORKDIR:指定工作目录
为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录,当容器运行后,进入容器内WORKDIR指定的默认目录
WORKDIR 指定工作目录(或称当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会自行创建
1.5.12 ONBUILD :子镜像引用父镜像的指令
可以用来配置当构建当前镜像的子镜像时,会自动触发执行的指令,但在当前镜像构建时,并不会执行,即延迟到子镜像构建时才执行
ONBUILD [INSTRUCTION]
说明:
-
尽管任何指令都可注册成为触发器指令,但ONBUILD不能自我能套,且不会触发FROM和MAINTAINER指令
-
使用 ONBUILD 指令的镜像,推荐在标签中注明,例如 ruby:1.9-onbuild
1.5.12 HEALTHCHECK:健康检查
检查容器的健康性
HEALTHCHECK [选项] CMD <命令> #设置检查容器健康状况的命令
HEALTHCHECK NONE #如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
HEALTHCHECK 支持下列选项:
--interval=<间隔> #两次健康检查的间隔,默认为 30 秒
--timeout=<时长> #健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默
认 30 秒
--retries=<次数> #当连续失败指定次数后,则将容器状态视为 unhealthy,默认3次
--start-period=<FDURATION> #default: 0s
#检查结果返回值:
0 #success the container is healthy and ready for use
1 #unhealth the container is not working correctly
2 #reserved do not use this exit code
1.5.13 USER: 指定当前用户
指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户当服务不需要管理员权限时,可以通过该命令指定运行用户这个用户必须是事先建立好的,否则无法切换如果没有指定 USER,默认是 root 身份执行
USER <user>[:<group>]
USER <UID>[:<GID>]
1.6 构建 docker build 命令
docker build命令使用Dockerfile文件创建镜像
docker build [OPTIONS] PATH | URL | -
说明:
PATH | URL | - #可以使是本地路径,也可以是URL路径。若设置为 - ,则从标准输入获取Dockerfile的内容
-f, --file string #Dockerfile文件名,默认为 PATH/Dockerfile
--force-rm #总是删除中间层容器,创建镜像失败时,删除临时容器
--no-cache #不使用之前构建中创建的缓存
-q --quiet=false #不显示Dockerfile的RUN运行的输出结果
--rm=true #创建镜像成功时,删除临时容器
-t --tag list #设置注册名称、镜像名称、标签。格式为 <注册名称>/<镜像名称>:<标签>(标签默认为latest)
2.基于Dockerfile创建镜像实战案例
2.1 建立工作目录
mkdir /opt/apache
cd /opt/apache
2.2 创建并编写Dockerfile文件
vim Dockerfile
#基于的基础镜像
FROM centos:7
RUN rm -rf /etc/yum.repos.d
ADD qh.repo /etc/yum.repos.d/
#镜像操作指令安装apache软件
RUN yum -y update
RUN yum -y install httpd
#开启 80 端口
EXPOSE 80
#复制网站首页文件
ADD index.html /var/www/html/index.html
##方法一
ENTRYPOINT [ "/usr/sbin/apachectl" ]
CMD ["-D", "FOREGROUND"]
##方法二
#将执行脚本复制到镜像中
ADD run.sh /run.sh
RUN chmod 755 /run.sh
CMD ["/run.sh"]
2.3 编写执行脚本
vim run.sh
rm -rf /run/httpd/* #清理httpd的缓存
/usr/sbin/apachectl -D FOREGROUND #指定为前台运行
#因为Docker容器仅在它的1号进程(PID为1)运行时,会保持运行。如果1号进程退出了,Docker容器也就退出了。
2.4 本地新建清华源
[base]
name=CentOS-$releasever - Base - mirrors.aliyun.com
failovermethod=priority
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-vault/7.9.2009/os/x86_64/
gpgcheck=0
2.5 创建测试页面
echo "this is test web from byyb " > index.html
2.6 使用Dockerfile生成镜像
docker build -t hc1 .
2.7 使用新的镜像运行并测试
docker run -d -p 8800:80 hc1
docker ps -a
docker port dbc8ab38d4d7
192.168.240.11:8800
真机访问
四.如何去缩小构建镜像的大小
1)尽可能的采用小体积的基础镜像;
2)尽可能的减少Dockerfile指令的数量;
3)在RUN指令里添加安装软件后清空yum/apt缓存、软件包的命令;
4)使用多级(多阶段)构建的方法 。
FROM 第一阶段的基础镜像 AS 别名
......
FROM 第二阶段的基础镜像
COPY --from=别名 第一阶段构建的文件/目录 当前阶段的路径
.....
CMD/ENTRYPOINT 指定容器启动时执行的命令
五.Docker 数据管理
Docker镜像由多个只读层叠加而成,启动容器时,Docker会加载只读镜像层并在镜像栈顶部添加一个读写层
如果运行中的容器修改了现有的一个已经存在的文件,那该文件将会从读写层下面的只读层复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏,此即“写时复制(COW copy on write)"机制
如果将正在运行中的容器修改生成了新的数据,那么新产生的数据将会被复制到读写层,进行持久化保存,这个读写层也就是容器的工作目录,也为写时复制(COW) 机制。
COW机制节约空间,但会导致性低下,虽然关闭重启容器,数据不受影响,但会随着容器的删除,其对应的可写层也会随之而删除,即数据也会丢失.如果容器需要持久保存数据,并不影响性能可以用数据卷技术实现
如下图是将对根的数据写入到了容器的可写层,但是把/data 中的数据写入到了一个另外的volume 中用于数据持久化
1.容器的数据管理介绍
Docker镜像是分层设计的,镜像层是只读的,通过镜像启动的容器添加了一层可读写的文件系统,用户写入的数据都保存在这一层中。
1.1 Docker容器的分层
容器的数据分层目录
-
LowerDir: image 镜像层,即镜像本身,只读
-
UpperDir: 容器的上层,可读写 ,容器变化的数据存放在此处
-
MergedDir: 容器的文件系统,使用Union FS(联合文件系统)将lowerdir 和 upperdir 合并完成后给容器使用,最终呈现给用户的统一视图
-
WorkDir: 容器在宿主机的工作目录,挂载后内容会被清空,且在使用过程中其内容用户不可见
范例: 查看指定容器数据分层
docker inspect
1.2 哪些数据需要持久化
有状态的协议
有状态协议就是就通信双方要记住双方,并且共享一些信息。而无状态协议的通信每次都是独立的,与上一次的通信没什么关系。
"状态”可以理解为“记忆”,有状态对应有记忆,无状态对应无记忆
-
左侧是无状态的http请求服务,右侧为有状态
-
下层为不需要存储的服务,上层为需要存储的部分服务
1.3 容器数据持久保存方式
如果要将写入到容器的数据永久保存,则需要将容器中的数据保存到宿主机的指定目录
Docker的数据类型分为两种:
-
数据卷(Data Volume): 直接将宿主机目录挂载至容器的指定的目录 ,推荐使用此种方式,此方式较常用
-
数据卷容器(Data Volume Container): 间接使用宿主机空间,数据卷容器是将宿主机的目录挂载至一个专门的数据卷容器,然后让其他容器通过数据卷容器读写宿主机的数据 ,此方式不常用
2.数据卷(data volume)
2.1 数据卷使用场景
-
数据库
-
日志输出
-
静态web页面
-
应用配置文件
-
多容器间目录或文件共享
2.2 数据卷的特点
-
数据卷是目录或者文件,并且可以在多个容器之间共同使用,实现容器之间共享和重用对数据卷更改数据在所有容器里面会立即更新。
-
数据卷的数据可以持久保存,即使删除使用使用该容器卷的容器也不影响。
-
在容器里面的写入数据不会影响到镜像本身,即数据卷的变化不会影响镜像的更新
-
依赖于宿主机目录,宿主机出问题,上面容器会受影响,当宿主机较多时,不方便统一管理
-
匿名和命名数据卷在容器启动时初始化,如果容器使用的镜像在挂载点包含了数据,会拷贝到新初始化的数据卷中
2.3 数据卷使用的方法
启动容器时,可以指定使用数据卷实现容器数据的持久化,数据卷有三种
-
指定宿主机目录或文件: 指定宿主机的具体路径和容器路径的挂载关系
-
容器和真机共用空间, 把容器的文件挂载到 真机的文件夹
-
-
匿名卷: 不指定数据名称,只指定容器内目录路径充当挂载点,docker自动指定宿主机的路径进行挂载
-
命名卷: 指定数据卷的名称和容器路径的挂载关系 和匿名卷在一个目录 , 只是取了一个自定义的名字
docker run 命令的以下格式可以实现数据卷
-v, --volume=[host-src:]container-dest[:<options>]
<options>
ro 从容器内对此数据卷是只读,不写此项默认为可读可写
rw 从容器内对此数据卷可读可写,此为默认值
方法一:
#匿名卷,只指定容器内路径,没有指定宿主机路径信息,宿主机自动生成/var/lib/docker/volumes/<卷ID>/_data目录,并挂载至容器指定路径
-v <容器内路径>
#示例:
docker run --name nginx -v /etc/nginx nginx
docker ps -a
docker inspect nginx
宿主机
cd /var/lib/docker/volumes/dd4df4d69487f67b2ba1189c6900ab8fab3c99900c959c5c1e30b8d71f92174d/_data
ls
conf.d fastcgi_params mime.types modules nginx.conf scgi_params uwsgi_params
#进入容器
docker exec -it nginx bash
cd /etc/nginx
ls
conf.d fastcgi_params mime.types modules nginx.conf scgi_params uwsgi_params
方法二:
#指定宿主机目录或文件格式:
-v <宿主机绝对路径的目录或文件>:<容器目录或文件>[:ro] #将宿主机目录挂载容器目录,两个目录
都可自动创建
示例:
docker run -it --name c1 -v /data:/opt centos:7
cd /opt
ls
alpine.tar dockerfile hosts mysql nginx.tar
宿主机目录
cd /data
ls
alpine.tar dockerfile hosts mysql nginx.tar
方法三:
#命名卷将固定的存放在/var/lib/docker/volumes/<卷名>/_data
-v <卷名>:<容器目录路径>
#可以通过以下命令事先创建,如可没有事先创建卷名,docker run时也会自动创建卷
docker volume create <卷名>
#示例:
docker run -d -p 80:80 --name nginx01 -v vol1:/usr/share/nginx/html/ nginx
六.网络管理
docker容器创建后,必不可少的要和其它主机或容器进行网络通信
官方文档:
https://docs.docker.com/network/
1.Docker的默认的网络通信
1.1 Docker 安装后默认的网络设置
Docker服务安装完成之后,默认在每个宿主机会生成一个名称为docker0的网卡其IP地址都是172.17.0.1/16
yun -y install bridge-utils
brctl show
另外会额外创建三个默认网络,用于不同的使用场景:
docker network list
NETWORK ID NAME DRIVER SCOPE
ac7b741d98a0 bridge bridge local #桥接网络,默认使用的模式,容器基于SNAT进行地址转换访问宿主机以外的环境
0c00ea56153d host host local #host网络,直接使用宿主机的网络( 不创建net namespace),性能最好,但是容器端口不能冲突
57379c2b5bd0 none null local #空网络,容器不会分配有效的IP地址(只有一个回环网卡用于内部通信),用于离线数据处理等场景。
1.2 创建容器后的网络配置
每次新建容器后
-
宿主机多了一个虚拟网卡,和容器的网卡组合成一个网卡,比如: 137: veth8ca6d43@if136,而在容器内的网卡名为136,可以看出和宿主机的网卡之间的关联
-
容器会自动获取一个172.17.0.0/16网段的随机地址,默认从172.17.0.2开始,第二次容器为172.17.0.3,以此类推
-
容器获取的地址并不固定,每次容器重启,可能会发生地址变化
1.3 容器间的通信
同一个宿主机的不同容器可以相互通信
默认情况下
同一个宿主机的不同容器之间可以相互通信
1.4 修改默认网络设置
新建容器默认使用docker0的网络配置,可以修改默认指向自定义的网桥网络
范例: 用自定义的网桥代替默认的docker0
新建网桥
brctl addbr docker1
#新建网桥 docker1
brctl show
ip a a 192.168.1.1/24 dev docker1
#给网桥设置地址
#第一个a是address 第二个a是add
ip a
brctl
Usage: brctl [commands]
commands:
addbr <bridge> add bridge #添加一个新的桥接设备(bridge)。
delbr <bridge> delete bridge #删除指定的桥接设备。
addif <bridge> <device> add interface to bridge#将一个网络接口(device)添加到指定的桥接设备中。
delif <bridge> <device> delete interface from bridge #从指定的桥接设备中删除一个网络接口。
hairpin <bridge> <port> {on|off} turn hairpin on/off#打开或关闭指定桥接设备上某个端口的 hairpin 功能。Hairpin 模式允许从同一桥接设备的不同端口上收到的数据包被重新发送到同一桥接设备的其他端口。
setageing <bridge> <time> set ageing time #设置桥接设备中 MAC 地址的老化时间。
setbridgeprio <bridge> <prio> set bridge priority #设置桥接设备的优先级。
setfd <bridge> <time> set bridge forward delay #设置桥接设备的前向延迟时间。
sethello <bridge> <time> set hello time #设置桥接设备的 hello 时间。
setmaxage <bridge> <time> set max message age#设置桥接设备的最大消息老化时间。
setpathcost <bridge> <port> <cost> set path cost#设置桥接设备上某个端口的路径成本。
setportprio <bridge> <port> <prio> set port priority#设置桥接设备上某个端口的优先级。
show [ <bridge> ] show a list of bridges #显示当前系统中所有桥接设备的列表,或者显示特定桥接设备的详细信息。
showmacs <bridge> show a list of mac addrs #显示特定桥接设备中的 MAC 地址列表。
showstp <bridge> show bridge stp info #显示特定桥接设备的 Spanning Tree Protocol(STP)信息。
stp <bridge> {on|off} turn stp on/off #打开或关闭特定桥接设备上的 Spanning Tree Protocol(STP)。
让容器使用docker1网桥
# 修改service 文件
[root@localhost ~]#vim /usr/lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -b docker1
# 在启动命令行加入 -b docker1
[root@localhost ~]#dockerd --help
#可以使用帮助查看
重启服务
[root@localhost ~]#systemctl daemon-reload
[root@localhost ~]#systemctl restart docker
[root@localhost ~]#docker run -it centos:7
#进入容器
[root@dccc332eb77d /]# ifconfig
bash: ifconfig: command not found
[root@dccc332eb77d /]# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
192.168.1.2 dccc332eb77d
[root@localhost ~]#docker run -d nginx
caaa25ea6d563cef9a46a7a0103c08c7994bf701e1e65db2118c8a526c246f86
[root@localhost ~]#brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02425d39db85 no
docker1 8000.da8c6c3a3f21 no veth0a164df
virbr0 8000.525400ea3ab5 yes virbr0-nic
2.容器名称互联
新建容器时,docker会自动分配容器名称,容器ID和IP地址,导致容器名称,容器ID和IP都不固定,那么如何区分不同的容器,实现和确定目标容器的通信呢?解决方案是给容器起个固定的名称,容器之间通过固定名称实现确定目标的通信
有两种固定名称:
-
容器名称
-
容器名称的别名
2.1 容器名称介绍
即在同一个宿主机上的容器之间可以通过自定义的容器名称相互访问,比如: 一个业务前端静态页面是使用nginx,动态页面使用的是tomcat,另外还需要负载均衡调度器,如: haproxy 对请求调度至nginx和tomcat的容器,由于容器在启动的时候其内部IP地址是DHCP 随机分配的,而给容器起个固定的名称,则是相对比较固定的,因此比较适用于此场景
2.2 容器名称实现
docker run 创建容器,可使用--link选项实现容器名称的引用
--link list #Add link to another container
格式:
docker run --name <容器名称> #先创建指定名称的容器
docker run --link <目标通信的容器ID或容器名称> #再创建容器时引用上面容器的名称
示例1:
先建立一个带名字的容器
[root@localhost ~]# docker run -it --name c1 centos:7 bash
[root@8da362532cfe /]#
再建立第二个容器和第一个 连接
[root@localhost ~]# docker run -it --name c2 --link c1 centos:7 bash
[root@df292bea3409 /]# ping c1
[root@df292bea3409 /]# cat /etc/hosts
需要注意的是如果c1 容器发生改变 将无法改变
示例2:实现LAMP
[root@localhost data]#vim env_mysql.list
MYSQL_ROOT_PASSWORD=123
MYSQL_DATABASE=wordpress
MYSQL_USER=wpuser
MYSQL_PASSWORD=wppass
[root@localhost data]#vim env_wordpress.list
WORDPRESS_DB_HOST=mysql:3306
WORDPRESS_DB_NAME=wordpress
WORDPRESS_DB_USER=wpuser
WORDPRESS_DB_PASSWORD=wppass
WORDPRESS_TABLE_PREFIX=wp_
docker run -d --name mysql -v /data/mysql:/var/lib/mysql -p 3306:3306 --env-file=env_mysql.list mysql:5.7.32
docker run -d --name wordpress --link mysql --env-file=/data/env_wordpress.list -p 80:80 wordpress
真机浏览器访问192.168.240.11:80
3.网络连接模式
3.1 网络模式介绍
Docker的网络支持五种网络模式
none(无网络)模式:
在这种模式下,容器没有网络接口,只有
lo
(回环)接口。适合于不需要网络连接的容器。bridge(桥接)模式:
这是默认的网络模式。在桥接模式下,容器连接到 Docker 守护进程创建的默认桥接网络上。它使用 NAT(网络地址转换)来为容器提供外部通信能力。
host(主机)模式:
使用主机的网络栈,容器和主机共享同一个网络命名空间。这意味着容器绑定到主机的网络接口,不会进行 NAT 转换,容器的网络性能与主机相同。
container(容器)模式:
在这种模式下,可以指定容器连接到另一个容器的网络栈。这样,两个容器可以共享同一个网络命名空间,能够直接相互通信,而不需要通过网络。
自定义网络模式(Custom network mode):
除了以上四种模式,还可以创建自定义的用户定义网络(User-defined networks)。这些网络允许用户创建一个具有特定配置的独立网络,以满足特定应用或服务的需求。自定义网络可以通过 Docker 的网络驱动程序进行管理和配置。
查看默认的网络模式
docker network ls
3.2 网络模式指定
默认新建的容器使用Bridge模式,创建容器时,docker run 命令使用以下选项指定网络模式
格式
docker run --network <mode>
docker run --net=<mode>
<mode>: 可是以下值
none
bridge
host
container:<容器名或容器ID>
<自定义网络名称>
3.2.1 bridge 网络模式
可以和外部网络之间进行通信,通过SNAT访问外网,使用DNAT可以让容器被外部主机访问,所以此模式也称为NAT模式
此模式宿主机需要启动ip_forward功能
bridge网络模式特点
-
网络资源隔离: 不同宿主机的容器无法直接通信,各自使用独立网络
-
无需手动配置: 容器默认自动获取172.17.0.0/16的IP地址,此地址可以修改
-
可访问外网: 利用宿主机的物理网卡,SNAT连接外网
-
外部主机无法直接访问容器: 可以通过配置DNAT接受外网的访问
-
低性能较低: 因为通过NAT,网络转换带来更多的损耗
-
端口管理繁琐: 每个容器必须手动指定唯一的端口,容器产生端口冲容
修改默认的bridge模式网络配置
vim /etc/docker/daemon.json
{
"registry-mirrors": ["https://ifxbyp3x.mirror.aliyuncs.com"],
"bip": "192.168.100.100/24",
"fixed-cidr": "192.168.100.128/26",
"default-gateway": "192.168.100.200"
}
systemctl restart docker
ip a
3.2.2 Host模式
如果指定host模式启动的容器,那么新创建的容器不会创建自己的虚拟网卡,而是直接使用宿主机的网卡和IP地址,因此在容器里面查看到的IP信息就是宿主机的信息,访问容器的时候直接使用宿主机IP+容器端口即可,不过容器内除网络以外的其它资源,而且某一个端口一旦被其中一个容器占用那么其它容器就不能再监听此端口,不过容器的其它资源比如容器的文件系统、容器系统进程等还是基于namespace相互隔离。 如: 文件系统、系统进程等仍然和宿主机保持隔离
此模式由于直接使用宿主机的网络无需转换,网络性能最高,但是各容器内使用的端口不能相同,适用于运行容器端口比较固定的业务
Host 网络模式特点:
-
使用参数 --network host 指定
-
共享宿主机网络
-
网络性能无损耗
-
网络故障排除相对简单
-
各容器网络无隔离
-
网络资源无法分别统计
-
端口管理困难: 容易产生端口冲突
-
不支持端口映射
示例:
docker run -d --network host --name web1 nginx
docker inspect web1
docker run -it --rm --net=host busybox sh
/ # ip a
3.2.3 none模式
在使用none 模式后,Docker 容器不会进行任何网络配置,没有网卡、没有IP也没有路由,因此默认无法与外界通信,需要手动添加网卡配置IP等,所以极少使用
none模式特点
-
使用参数 --network none 指定
-
默认无网络功能,无法和外部通信
示例:
docker run -it --network none centos:7
docker run -it -d --name nginx1 -p 80:80 --net=none busybox sleep 1000000
2a6438531d1200d5acf5eac48307f7c711fcd8ab4a43fd5eb882ea9e30cc66bd
docker exec -it 2a sh
/ # ifconfig
3.2.4 Container 模式
使用此模式创建的容器需指定和一个已经存在的容器共享一个网络,而不是和宿主机共享网,新创建的容器不会创建自己的网卡也不会配置自己的IP,而是和一个被指定的已经存在的容器共享IP和端口范围,因此这个容器的端口不能和被指定容器的端口冲突,除了网络之外的文件系统、进程信息等仍然保持相互隔离,两个容器的进程可以通过lo网卡进行通信
Container 模式特点
-
使用参数 –-network container:名称或ID 指定
-
与宿主机网络空间隔离
-
空器间共享网络空间
-
适合频繁的容器间的网络通信
-
直接使用对方的网络,较少使用
示例:
docker run -d --name web1 nginx
docker run -d --name web2 --network container:web1 nginx
#端口冲突
docker run -it --network container:web1 centos:7 bash
新建的 容器,会有自己的 网络命名空间
查看网络命名空间
ls /var/run/docker/netns/
ln -s /var/run/docker/netns/* /var/run/netns/
#需要做软连接
建立容器时选择相应的网络模式
使用docker run创建Docker容器时,可以用 --net 或 --network 选项指定容器的网络模式
-
host模式:使用 --net=host 指定。
-
none模式:使用 --net=none 指定
-
container模式:使用 --net=container:NAME_or_ID 指定
-
bridge模式:使用 --net=bridge 指定,默认设置,可省略