Docker容器技术
认识Docker
什么是Docker?
它是Linux里运行的应用的开源软件,它跟KVM的区别更像是Docker是运行在Linux系统中的一个软件,以应用的形式来模拟成虚拟机使用(想象一下再Windows上运行VMware和QQ),既可以是一个应用,也可以是一台服务,甚至是一个完整的操作系统。
为什么选择Docker?
Docker与传统虚拟化比较
启动速度更快、计算能力损耗几乎无、性能接近原生超过虚拟机、系统支持量也远远超过虚拟机、隔离性资源限制,而虚拟机完全隔离。总结就是达到了虚拟机的效果还更方便快捷。
Docker的优势
- 1.解决环境不一致问题:因为不同环境下的软件版本、依赖库、配置都可能不一致。docker可以把应用和依赖一起打包进镜像,运行docker可以保证环境一致。
- 2.轻量化与高效利用资源:安装虚拟机需要完整的操作系统,每个虚拟机实例都会很占用资源,docker基于宿主机的内核运行,更轻量,启动速度跟快。
- 3.保障环境隔离与安全性:每个容器都是独立运行的,互不干扰。即使某个应用崩溃,也不会影响宿主机和其他容器里的应用。
- 4.方便服务迁移和管理:应用和依赖被打包到一个镜像里,不管是本地、测试服务器、还是云平台,只要有 Docker 环境,就能直接运行。如需迁移只需要打包镜像就可以迁移服务所需要的整个环境。在服务器上管理多个应用也很方便,只需要管理每个应用的容器即可。
Docker的核心概念
Docker的三大核心概念:
- 镜像:Docker的镜像是创建容器的基础,可以理解是一个安装系统环境的镜像,也可以理解为是安装一个应用程序的镜像
- 容器:Docker的容器是从镜像创建来的运行实例,可以创建、启动、停止和删除,所创建的每个容器都是相互隔离、互不可见的,可以保证平台安全
- 仓库:Docker的仓库是用来存储镜像的地方可以把创建的镜像使用push命令上传上去,其他容器使用可以直接pull下来
熟悉了上面三个概念的作用和使用则是学会了Docker的使用。
一、Docker的安装
yum安装
1.配置docker的yum源
yum install -y yum-utils
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
2.搜索docker版本并安装、启动
#搜索版本号
yum list docker-ce --showduplicates | sort -r
yum -y install docker-ce-20.10.9
#启动docker服务
systemctl start docker
apt安装
1.更新apt的docker源
apt update
apt -y install ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list
2.安装docker并启动
apt update
apt -y install docker-ce docker-ce-cli containerd.io
#启动docker
systemctl start docker && systemctl enable docker
安装包安装
1.下载解压安装包并初始化命令
# 根据版本下载安装
export docker_version='28.0.2'
wget https://download.docker.com/linux/static/stable/x86_64/docker-${docker_version}.tgz
tar zxf docker-${docker_version}.tgz -C /usr/local/
ln -s /usr/local/docker/* /usr/local/bin
2.编辑docker的systemctl配置文件
cat > /lib/systemd/system/docker.service << EOF
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target firewalld.service
Wants=network-online.target
[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/local/bin/dockerd --data-root /var/lib/docker
ExecReload=/bin/kill -s HUP $MAINPID
# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
# Uncomment TasksMax if your systemd version supports it.
# Only systemd 226 and above support this version.
#TasksMax=infinity
TimeoutStartSec=0
# set delegate yes so that systemd does not reset the cgroups of docker containers
Delegate=yes
# kill only the docker process, not all processes in the cgroup
KillMode=process
# restart the docker process if it exits prematurely
Restart=on-failure
StartLimitBurst=3
StartLimitInterval=60s
[Install]
WantedBy=multi-user.target
EOF
3.重新加载systemctl配置并启动docker服务
systemctl daemon-reload && systemctl enable docker --now
二、初步使用Docker
运行第一个Docker应用
先通过一条命令来运行一个Nginx应用,该命令会在后台运行启动nginx:latest镜像并取名为firstapp,再映射容器的80端口到物理机上的80端口。
docker run --name firstapp -d -p 80:80 nginx:latest
我们可以通过docker ps命令来查看容器运行的状态,如下所示:
[root@helloworld ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
56f1f80803f2 nginx:latest "/docker-entrypoint.…" 9 seconds ago Up 9 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp firstapp
docker运行的大致步骤:
1.docker客户端通过API与docker daemon通信。
2.检查是否存在nginx:latst这个镜像,如果没有则会去拉取该镜像。
3.基于镜像开始创建容器,设置挂载点和端口映射,注册容器的名字。
4.配置容器的运行环境,比如工作目录、环境变量等工作。
5.最后启动容器,就是调用Linux内核的namespace + cgroup功能创建一个新进程。
Docker服务基本信息查询
查看版本:docker version
可以查看docker应用相关版本信息,如下所示:
[root@helloworld ~]# docker version
Client: Docker Engine - Community
Version: 27.4.1
API version: 1.47
Go version: go1.22.10
Git commit: b9d17ea
Built: Tue Dec 17 15:45:46 2024
OS/Arch: linux/amd64
Context: default
Server: Docker Engine - Community
Engine:
Version: 27.4.1
API version: 1.47 (minimum version 1.24)
Go version: go1.22.10
Git commit: c710b88
Built: Tue Dec 17 15:45:46 2024
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.7.24
GitCommit: 88bf19b2105c8b17560993bee28a01ddc2f97182
runc:
Version: 1.2.2
GitCommit: v1.2.2-0-g7cb3632
docker-init:
Version: 0.19.0
GitCommit: de40ad0
查看详情:docker info
可以查看docker服务的总体信息,包括配置信息(比如:资源限制、工作数据目录等),如下所示:
[root@helloworld ~]# docker info
Client: Docker Engine - Community
Version: 27.4.1
Context: default
Debug Mode: false
Plugins:
buildx: Docker Buildx (Docker Inc.)
Version: v0.19.3
Path: /usr/libexec/docker/cli-plugins/docker-buildx
compose: Docker Compose (Docker Inc.)
Version: v2.32.1
Path: /usr/libexec/docker/cli-plugins/docker-compose
Server:
Containers: 2
Running: 2
Paused: 0
Stopped: 0
Images: 19
Server Version: 27.4.1
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Using metacopy: false
Native Overlay Diff: true
userxattr: false
Logging Driver: json-file
Cgroup Driver: systemd
Cgroup Version: 2
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
Swarm: inactive
Runtimes: io.containerd.runc.v2 runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 88bf19b2105c8b17560993bee28a01ddc2f97182
runc version: v1.2.2-0-g7cb3632
init version: de40ad0
Security Options:
apparmor
seccomp
Profile: builtin
cgroupns
Kernel Version: 6.8.0-51-generic
Operating System: Ubuntu 24.04.1 LTS
OSType: linux
Architecture: x86_64
CPUs: 8
Total Memory: 14.73GiB
Name: islide-jenkins
ID: 6696df7b-20c2-44cd-add6-9b20634620a8
Docker Root Dir: /var/lib/docker
Debug Mode: false
Experimental: false
Insecure Registries:
127.0.0.0/8
Registry Mirrors:
https://docker.mybacc.com/
https://docker.xiaogenban1993.com/
https://dockerhub.websoft9.com/
https://lwr998gq.mirror.aliyuncs.com/
Live Restore Enabled: false
Docker常用的次级命令
- attach:介入到一个正在运行的容器
- build:根据 Dockerfile 构建一个镜像
- commit:根据容器的更改创建一个新的镜像
- cp:在本地文件系统与容器中复制 文件/文件夹
- create:创建一个新容器
- exec:在容器中执行一条命令
- images:列出镜像
- kill:杀死一个或多个正在运行的容器
- logs:取得容器的日志
- paus:暂停一个或多个容器的所有进程
- ps:列出启动的容器详情
- pull:拉取一个镜像或仓库到 registry
- push:推送一个镜像或仓库到 registry
- rename:重命名一个容器
- restart:重新启动一个或多个容器
- rm:删除一个或多个容器
- rmi:删除一个或多个镜像
- run:在一个新的容器中执行一条命令
- search:在 Docker Hub 中搜索镜像
- start:启动一个或多个已经停止运行的容器
- stats:显示一个容器的实时资源占用
- stop:停止一个或多个正在运行的容器
- tag:为镜像创建一个新的标签
- top:显示一个容器内的所有进程
- unpause:恢复一个或多个容器内所有被暂停的进程
三、Docker镜像管理
搜索镜像
命令语法:docker search【镜像名】
【示例】搜索ubuntu:22.04镜像
docker search ubuntu:22.04
拉取镜像
命令语法:docker pull【镜像名】
【示例】拉取ubuntu:22.04镜像
docker pull ubuntu:22.04
查看镜像
命令语法:docker images【选项(可选)】【镜像名(可选)】
选项和参数:
- -a:显示所有的镜像
- -p:只显示镜像id
【示例】查看本地镜像
# 查看所有镜像
docker images
# 查询指定镜像
docker images nginx:v1
# 查询指定镜像只显示id
docker images nginx:v1 -q
REPOSITORY:镜像属于的仓库 TAG:镜像的标签信息
IMAGE ID:镜像的唯一ID号 CREATED:镜像的创建时间
VIRTUAL SIZE:镜像的大小
镜像标签
命令语法:docker tag【镜像名 | 镜像id】【镜像名】:【镜像TAG】
【示例】修改nginx:latest镜像名为nginx:v1
docker tag nginx:latest nginx:v1
推送镜像
命令语法:docker push【镜像名】
推送镜像需要把镜像改名成镜像仓库/仓库组/镜像名:镜像标签这样的标准格式,然后登录仓库,再进行上传。
【示例】将ubuntu:22.04上传到镜像仓库
# 修改tag
docker tag ubuntu:22.04 docker.io/helloworld/ubuntu:22.04
# 登录仓库
docker login docker.io
# 推送镜像
docker push docker.io/helloworld/ubuntu:22.04
删除镜像
命令语法:docker rmi【选项(可选)】【镜像名 | 镜像ID】
选项和参数:
- -f:强制删除镜像,无论镜像是否在容器中被使用运行
【示例】删除ubuntu:22.04镜像
# 普通删除
docker rmi ubuntu:22.04
# 强制删除
docker rmi -f ubuntu:22.04
四、Docker容器管理
启动容器
第一种方式启动:create+start
命令语法:
1.创建容器: docker create【选项】【选项参数】【镜像名 | 镜像ID】
2.启动容器(通过上图红线的ID号启动):docker start 【容器名字 | 容器ID】
【示例】使用nginx:latest镜像创建一个名为test的容器,并启动
docker create --name test nginx:latest
docker start test
第二种方式启动(推荐):run
命令语法:docker run【选项】【选项参数】【镜像名 | 镜像ID】
选项和参数:
- -i:表示在交互模式下运行容器
- -t:分配一个伪终端(pseudo-TTY),使得容器与终端之间可以进行交互
- -d:让容器在后台运行
- –name:指定容器的名称
- -p:将容器内部使用的网络端口映射到指定的主机端口上,可重复使用
- -P:把容器里所有暴露的端口(EXPOSE)自动随机映射到宿主机的端口。
- -v,–volume:将主机的目录或文件挂载到容器中使用,可重复使用
- -e,–env:设置容器的环境变量,可重复使用
- -l,–label:选项用于为容器或其他 Docker 对象添加标签(label)
- -w,–workdir:选项用于设置容器内的当前工作目录
- –restart:指定容器在退出时的重启策略。可选值包括 no(不重启)、always(总是重启)和=on-failure(发生错误时重启)
- –rm:在容器停止后自动删除容器。适用于一次性任务
- –network:指定容器使用的网络。可选值包括 bridge(默认网络)、host(与主机共享网络)和用户定义的网络
- –link:连接到另一个容器。已弃用,不推荐使用
【示例】使用nginx:latest镜像运行一个容器,名字为test
docker run --name test -d nginx:latest
查看容器
命令语法:docker ps【选项(可选)】
选项和参数:
- -a:查看所有状态的容器
- -q:只显示容器的id
【示例】查看所有状态的容器
docker ps -a
CONTAINER ID: 容器 ID IMAGE: 使用的镜像
COMMAND: 启动容器时运行的命令 CREATED: 容器的创建时间
STATUS: 容器状态
状态有7种:
- created(已创建)
- restarting(重启中)
- running 或 Up(运行中)
- removing(迁移中)
- paused(暂停)
- exited(停止)
- dead(死亡)
PORTS: 容器的端口信息和使用的连接类型(tcp\udp) NAMES: 容器名称
重启容器
命令语法:docker restart【容器名 | 容器ID】
【示例】
docker restart test
停止容器
命令语法:docker stop【容器名 | 容器ID】
【示例】
docker stop test
删除容器
命令语法:docker rm【选项(可选)】【容器名 | 容器ID】
选项和参数:
- -f:强制删除容器,不管是不是正在运行的容器
【示例】
# 删除容器
docker rm test
# 强制删除容器
docker rm -f test
容器执行命令
命令语法:docker exec【选项(可选)】【容器名 | 容器ID】【执行命令】
如果只加-t,不加-i,你能看到输出,但没法持续输入交互。
选项和参数:
- -i:保持标准输入流(STDIN)打开
- -t:分配一个伪终端(TTY),输出会带格式,像你平常用shell一样
【示例】
docker exec -it test ls /etc
查看容器日志
命令语法:docker logs【选项(可选)】【容器名 | 容器ID】
选项和参数:
- -f,–follow:实时监控日志输出
- -n,–tail:查看屏幕输出倒数多少行
- -t,–timestamps:显示时间
【示例】
# 查看test容器日志
docker logs test
# 实时查看test容器日志
docker logs -f test
# 查看test容器后10行日志
docker logs -n 10 test
五、Docker高级操作
端口映射
可以使用-P或者-p选项将容器端口暴露到宿主机的端口上,提供服务访问入口。
选项与参数:
- -P:把容器里所有暴露的端口(EXPOSE)自动随机映射到宿主机的端口。
- -p:将容器内部使用的网络端口映射到指定的主机端口上,可重复使用
-p 主机端口:容器端口:选项后的第一个参数为指定需要暴露在主机的哪个端口上,第二个参数为指定需要暴露容器的哪个端口
【示例】
# 暴露容器端口到主机的随机端口
docker run --name demo1 -P 80 -d nginx:latest
# 暴露容器端口到主机的指定端口
docker run --name demo2 -p 80:80 -d nginx:latest
# 暴露容器的多个端口
docker run --name demo3 -p 81:80 -p 82:82 -d nginx:latest
查看容器的运行情况,如下所示:
[root@helloworld ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bdf1fd027b58 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:82->82/tcp, :::82->82/tcp, 0.0.0.0:81->80/tcp, [::]:81->80/tcp demo3
de9ad88ae5ab nginx:latest "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp demo2
c08366e9118b nginx:latest "/docker-entrypoint.…" 3 minutes ago Up 3 minutes 0.0.0.0:32768->80/tcp, [::]:32768->80/tcp demo1
进入容器内部
使用docker exec命令运行一个shell环境即可进入容器内部
docker exec -it test /bin/bash
容器执行一次性任务
可以使用--rm选项来让容器执行一次性的任务,运行完之后不留痕迹。
选项和参数:
- –rm:容器执行结束后自动删除容器(文件系统和元数据)。
【示例】
docker run --rm --name taskcontainer ubuntu:latest echo "I am running a task"
设置环境变量
可以使用-e来定义容器里面的环境变量,可以传递多个,每多一个则使用一个-e来进行配置。
选项和参数:
- -e:定义容器环境变量,键值对形式,可重复使用
【示例】
docker run --name nginx -e NGINX_VERSION=1.21.3 -e LOG_PATH=/var/log/nginx -d nginx:latest
容器重启策略
使用--restart=选项可以配置容器的重启策略。
现象和参数:
- –restart:用来设置容器退出后是否自动重启等重启策略的选择
- 参数列表有:
- no(默认):不自动重启
- always:无论退出状态码是什么,总是会自动重启容器
- unless-stopped:和always类似,但如果容器被手动
docker stop,那么Docker服务重启后不会再启动它 - on-failure:仅当容器因错误退出(退出码非 0)时才会重启,可选的
max-retries表示最多重启几次,超过次数就不再重启
【示例】
docker run --name test --restart=always -d nginx:latest
容器的导入导出
第一种方法(推荐):load和save
使用该方式可以完整保留镜像状态,基本都是使用该方法。
导出镜像:docker save -o【存储文件名】【要存储的镜像名】
【示例】导出容器镜像
docker save -o nginx.img nginx:latest
载入镜像:docker load -i【存储文件名】
【示例】导入容器镜像
docker load -i nginx.img
# 或者另外一种方式
docker load < nginx.img
第二种方法(不推荐):import和export
使用该方法会重置镜像的状态,也就是不能保存容器的元数据,不推荐使用。
导出容器:docker export 【容器名 | 容器ID】 > 【储存的文件名】
【示例】导出容器镜像
docker export ba7a5dc88d29 > nginx.bak
导入容器:cat 【文件名】| docker import - 【生成的镜像名:标签】
【示例】导入容器镜像
cat nginx.bak | docker import - nginx:latest
六、容器网络管理
网络管理
1.创建网络
语法格式:docker network create 【选项和选项参数(可选)】【网络名称】
选项和参数:
- -d:参数指定Docker网络类型,有 bridge、overlay
【示例】下面先创建一个新的Docker网络
docker network create -d bridge test-net
可以使用docker network ls来查看已有网络,如下所示:
[root@helloworld ~]# docker nework ls
NETWORK ID NAME DRIVER SCOPE
9f780c41134b bridge bridge local
00b733e14e13 host host local
8df264e3b314 none null local
7598b5420ac3 test-net bridge local
2.使用网络
运行容器时可以使用--network选项来指定网络,在同一网络下的容器之间可以实现网络互联,比如:同网络容器之间可以ping通对方容器名。
选项和参数:
- –network:运行一个容器时连接到指定的网络
【示例】
docker run -itd -p 80:80 --name nginx --network test-net nginx:latest
3.删除网络
语法格式:docker network create 【选项和选项参数(可选)】【网络名称】
选项和参数:
- -f:强制删除网络,不管是否有容器正在使用该网络
【示例】
docker network rm test-net
DNS配置
修改Docker配置设置DNS
修改docker配置文件:/etc/docker/daemon.json
{
"dns" : [
"114.114.114.114",
"8.8.8.8"
]
}
配置完,需要重启 docker 才能生效
设置后,启动容器的 DNS 会自动配置为 114.114.114.114 和 8.8.8.8
运行参数设置DNS
选项和参数:
- -h,–hostname:设定容器的主机名,它会被写到容器内的/etc/hostname和/etc/hosts
- –dns:添加 DNS 服务器到容器的 /etc/resolv.conf 中,让容器用这个服务器来解析所有不在 /etc/hosts 中的主机名
- –dns-search:设定容器的搜索域,当设定搜索域为.example.com时,在搜索一个名为host的主机时,DNS不仅搜索host,还会搜索host.example.com
【示例】
docker run -it --rm -h host_ubuntu --dns=114.114.114.114 --dns-search=test.com ubuntu:latest
温馨提示:如果在容器启动时没有指定–dns和–dns-search,Docker 会默认用宿主主机上的 /etc/resolv.conf来配置容器的DNS解析
七、容器数据持久化管理
目录挂载(Bind Mounts)
可以使用-v或者--volume来挂载宿主机的目录到容器中使用,从而实现数据持久化,并且可以挂载多个。
选项和参数:
- -v,–volume:将主机的目录或文件挂载到容器中使用,可重复使用
-v 主机目录路径:容器目录路径:选项后面参数前面是主机的目录路径,后面才是容器的目录路径,这个不能反着来
【示例】
docker run --name nginx -p 80:80 -v ./logs:/var/log/nginx -v ./data:/var/www/html -d nginx:latest
数据卷(Volumes)
1.自动创建数据卷(方便)
跟目录挂载方式一样使用-v或者--volume选项来创建使用持久化数据卷,系统自动在/var/lib/docker/volumes/目录下面创建一个数据卷名称或者ID相同名字的目录,将docker数据存放在其中。
特别注意:在没有修改docker的root-data配置的情况下默认才会在/var/lib/docker/volumes/目录下自动创建。
选项和参数:
-v volumes数据卷名称:容器目录路径:跟目录挂载不同选项后面的参数前面是volumes数据卷的名称-v 容器目录路径:不需要加前面volumes数据卷名称参数,直接跟容器目录路径参数即可
【示例】
# 创建指定数据卷名字
docker run --name demo1 -v nginx_data:/var/www/html -d nginx:latest
# 创建一个匿名数据卷
docker run --name demo2 -v /var/www/html -d nginx:latest
我们可以通过docker volume ls命令来查看数据卷信息,如下所示:
[root@helloworld ~]# docker volume ls
DRIVER VOLUME NAME
local 9d19ffc29bc22bdc61a860b81632dae27f22c389fff207328607a4e09dcb68a9
local 03525e3a1d3dc69921c76a4fcce894573dcfc5d9fac4e9cc3cbe522c7f067d8a
local nginx_data
总结:
1.当使用-v volumes数据卷名称:容器目录路径选项参数时,系统没有该数据卷的时候会自动创建一个命名的数据卷。
2.使用-v 容器目录路径选项参数时,则会自动创建一个匿名的数据卷。
2.手动创建数据卷(麻烦)
语法格式:docker volume create【数据卷名】
【示例】
docker volume create test-volumes
共享数据卷
当多个容器需要共同目录数据的时候则可以使用--volumes-from选项可以跟某一个已经存在的容器挂载相同的卷。
选项和参数:
- –volumes-from:让一个容器挂载另一个容器的所有数据卷(挂载目录和数据卷)
【示例】
# 先创建一个容器做好目录挂载和数据卷创建
docker run --name demo1 -v nginx_data:/var/www/html -v ./logs:/var/log/nginx -d nginx:latest
# 再创建一个容器共享之前容器的所有数据卷
docker run --name demo2 --volumes-from demo1 -d nginx:latest
八、Dockerfile制作镜像
Dockerfile关键字
- FROM:拉取或属于镜像名
- MAINTAINER:创建者信息
- RUN:运行命令
- COPY:拷贝本地文件到容器中去
- ADD:功能相当于COPY,当识别文件为压缩文件会自动解压(解压出来不包括本文件夹)
- USER:以什么用户运行容器
- EXPOSE:容器内部服务运行的端口,方便映射
- ARG:定义构建阶段变量
- ENV:定义容器环境变量
- VOLUME:映射本地目录到容器中
- WORKDIR:定义工作路径,相当于cd命令,对RUN,CMD,ENTRYPOINT生效
- CMD:在docker run时也就是运行容器启动时执行的脚本或者命令
- ENTRYPOINT:类似于CMD指令,但其不会被docker run的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数追加给ENTRYPOINT指令指定的程序。但是, 如果运行
docker run时使用了--entrypoint选项,将覆盖ENTRYPOINT指令指定的程序。
注意:如果Dockerfile中如果存在多个CMD或者ENTRYPOINT指令,都是仅最后一个生效
Dokerfile制作镜像步骤
1.创建工作目录
mkdir /docker && cd /docker
2.编辑好Dockerfile文件
vim Dockerfile
3.命令创建镜像
命令语法:docker build 【选项】【工作路径】【镜像名:标签】
选项和参数:
- -t,–tag:指定镜像的名字标签信息
- -f:指定Dockerfile文件位置
【示例】
# 指定相对路径
docker build -t httpd:latest .
# 指定绝对路径
docker build -t httpd:latest /docker
注意:如果没有-f选项一定要加后面的.符号,表示使用当前路径的Dockerfile文件
也可以直接后面跟路径,表示指定Dockerfile的路径,如下所示:
docker build -t httpd:latest /test
Dockerfile多段构建
为什么需要多段构建?
在传统 Dockerfile 里:
- 构建软件时需要安装编译器、依赖库等(体积大)。
- 但运行软件时只需要可执行文件(体积小)。
- 如果不做优化,构建依赖和运行环境会一起进最终镜像,导致镜像很臃肿。
多段构建使用FROM...AS关键字来定义阶段名,如下所示:
# 第一阶段:构建
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp .
# 第二阶段:运行
FROM golang:1.21
WORKDIR /app
COPY --from=builder /app/myapp .
CMD ["./myapp"]
还可以使用多段构建来区分步骤,这样看上去更美观,构建步骤也更加清晰,如下所示:
# 安装nginx环境阶段
FROM ubuntu:22.04 AS nginx
# 其他工具安装和环境配置
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 安装nginx
RUN apt update; \
apt install -y --no-install-recommends --no-install-suggests \
nginx
EXPOSE 80
WORKDIR /var/www/html
ENTRYPOINT [ "nginx" ]
CMD [ "-g", "daemon off;" ]
# 添加代码阶段
FROM nginx AS add-code
# 添加配置文件和html页面
COPY conf/nginx.conf /etc/nginx/nginx.conf
COPY . /var/www/html/
Dokerfile缓存机制
Dockerfile构建过程
docker build -t name:tag .
当执行该命令时,docker进行构建,流程如下:
1.把当前目录及子目录当做上下文传递给docker服务
2.从当前目录中找到Dockerfile
3.检查Dockerfile的语法
4.依次执行Dockerfile中的指令,根据指令生成中间过度镜像(存储在本地,为之后的指令或构建作缓存)
寻找缓存的逻辑
- docker会以树型结构根据Dockerfile指令遍历子节点的过程
- 就是在构建的时候docker会根据顺序查看Dockerfile的指令是否与之前的一致决定是否使用缓存
- 除了ADD和COPY指令,因为这两个指令会复制文件内容到镜像内,除指令外,docker还会检查添加文件的内容校验是否一致(不包括最后修改时间和最后访问时间),决定是否使用缓存
注意:除这两个命令还有apt update之类的指令,有可能更新的东西不一样,但是指令一样,所以会使用缓存 - 如果docker没有找到当前指令的缓存,则会构建一个新的镜像,并且之后的所有指令都不会再去寻找缓存
比如以下例子:
vim Dockerfile
--------------------
FROM ubuntu:20.04
RUN mkdir /test
COPY test.txt /test/
第一次构建信息
[root@felix docker]# docker build -t test:1 .
Sending build context to Docker daemon 261.6MB
Step 1/3 : FROM ubuntu:20.04
---> 20fffa419e3a
Step 2/3 : RUN mkdir /test
---> Running in 6e5a46f601b6
Removing intermediate container 6e5a46f601b6
---> cb63ab30bf7c
Step 3/3 : COPY test.txt /test/
---> e9eafd1c4f1f
Successfully built e9eafd1c4f1f
Successfully tagged test:1
第二次构建信息
[root@felix docker]# docker build -t test:2 .
Sending build context to Docker daemon 261.6MB
Step 1/3 : FROM ubuntu:20.04
---> 20fffa419e3a
Step 2/3 : RUN mkdir /test
---> Using cache
---> cb63ab30bf7c
Step 3/3 : COPY test.txt /test/
---> Using cache
---> e9eafd1c4f1f
Successfully built e9eafd1c4f1f
Successfully tagged test:2
就会发现同样的指令,第二次直接在Step 2/3中使用了缓存并且image id不变
我们再修改指令
vim Dockerfile
--------------------
FROM ubuntu:20.04
RUN mkdir /test
COPY test.txt /test/
RUN echo test > /test/test.txt
再次构建发现从Step 4/4开始不使用缓存并生成不同的image id
[root@felix docker]# docker build -t test:3 .
Sending build context to Docker daemon 261.6MB
Step 1/4 : FROM ubuntu:20.04
---> 20fffa419e3a
Step 2/4 : RUN mkdir /test
---> Using cache
---> cb63ab30bf7c
Step 3/4 : COPY test.txt /test/
---> Using cache
---> e9eafd1c4f1f
Step 4/4 : RUN echo test > /test/test.txt
---> Running in f78bf7c873a6
Removing intermediate container f78bf7c873a6
---> 24128c55be3e
Successfully built 24128c55be3e
Successfully tagged test:3
不使用缓存机制
强制构建的时候不使用缓存。
选项和参数:
- –no-cache:不使用缓存,每条指令都重新生成镜像
【示例】
docker build --no-cache .
Dockerfile示例
java类镜像
FROM ubuntu:20.04
MAINTAINER www.ihavecats.cn
RUN mkdir -p /usr/share/zoneinfo/Asia
COPY Shanghai /usr/share/zoneinfo/Asia/
RUN ln -sb /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
WORKDIR /app
ADD jdk-11.0.13_linux-x64_bin.tar.gz /usr/local
RUN mv /usr/local/jdk-11.0.13 /usr/local/jdk11
COPY *.jar /app
EXPOSE 8080
CMD /usr/local/jdk11/bin/java -jar /java/*.jar --server.port=8080
nginx类镜像
FROM ubuntu:20.04
MAINTAINER www.ihavecats.cn
RUN mkdir -p /usr/share/zoneinfo/Asia
COPY Shanghai /usr/share/zoneinfo/Asia/
RUN ln -sb /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN useradd nginx
RUN apt-get update && apt-get install libpcre3 libpcre3-dev zlib1g-dev libssl-dev build-essential -y
ADD nginx-1.23.1.tar.gz /nginx
RUN cd /nginx/nginx-1.23.1 && ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_stub_status_module && make && make install
COPY nginx.conf /usr/local/nginx/conf/nginx.conf
WORKDIR /var/www/html
EXPOSE 80
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]