Docker容器技术

TOC

认识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的指令是否与之前的一致决定是否使用缓存
  • 除了ADDCOPY指令,因为这两个指令会复制文件内容到镜像内,除指令外,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;"]