Skip to content

Docker 全篇

Docer 简介

为什么要使用Docker

目前整个软件行业存在着以下几个痛点:

  • 软件更新发布及部署低效,过程烦琐且需要人工介入。

  • 环境一致性难以保证。

  • 不同环境之间迁移成本太高。

Docker 在很大程度上解决了上述问题。首先,Docker 的使用十分简单,从开发的角度来看就是“三步走”:构建、运输、运行。其中,关键步骤是构建环节,即打包镜像文件。但是从测试和运维的角度来看,那就只有两步:复制、运行。有了这个镜像文件,想复制到哪里运行都可以,完全和平台无关。Docker 这种容器技术隔离出了独立的运行空间,不会和其他应用争用系统资源,不需要考虑应用之间的相互影响。其次,因为在构建镜像时就处理完了服务程序对于系统的所有依赖,所以在使用时,可以忽略原本程序的依赖以及开发语言。对测试和运维人员而言,可以更专注于自己的业务内容。最后,Docker 为开发者提供了一种开发环境的管理办法,帮助测试人员保证环境的同步,为运维人员提供了可移植的标准化部署流程。

Docker理念

Docker是基于Go语言实现的云开源项目。Docker的主要目标是“Build,Ship and Run Any App,Anywhere”,也就是通过对应用组件的封装、分发、部署、运行等生命周期的管理,使用户的APP(可以是一个WEB应用或数据库应用等等)及其运行环境能够做到“一次镜像,处处运行”。 Docker解决了运行环境和配置问题的软件容器,方便做持续集成并有助于整体发布的容器虚拟化技术。

alt text

容器与虚拟机比较

  • 容器的发展史

alt text

alt text

  • 传统的虚拟机技术

虚拟机(virtual machine)就是带环境安装的一种解决方案。 它可以在一种操作系统里面运行另一种操作系统,比如在Windows10系统里面运行Linux系统CentOS7。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。这类虚拟机完美的运行了另一套系统,能够使应用程序,操作系统和硬件三者之间的逻辑不变。

alt text

  • 虚拟机的缺点

① 资源占用多
② 冗余步骤多
③ 启动慢

  • 容器虚拟化技术

由于前面虚拟机存在某些缺点,Linux发展出了另一种虚拟化技术:Linux容器(Linux Containers,缩写为 LXC)

Linux容器是与系统其他部分隔离开的一系列进程,从另一个镜像运行,并由该镜像提供支持进程所需的全部文件。容器提供的镜像包含了应用的所有依赖项,因而在从开发到测试再到生产的整个过程中,它都具有可移植性和一致性。

Linux 容器不是模拟一个完整的操作系统而是对进程进行隔离。有了容器,就可以将软件运行所需的所有资源打包到一个隔离的容器中。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需的库资源和设置。系统因此而变得高效轻量并保证部署在任何环境中的软件都能始终如一地运行。

alt text

  • VM 对比 Docker

alt text

比较了 Docker 和传统虚拟化方式的不同之处:

  • 传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;
  • 容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。
  • 每个容器之间互相隔离,每个容器有自己的文件系统 ,容器之间进程不会相互影响,能区分计算资源。

Docker 作用

更快速的应用交付和部署

传统的应用开发完成后,需要提供一堆安装程序和配置说明文档,安装部署后需根据配置文档进行繁杂的配置才能正常运行。Docker化之后只需要交付少量容器镜像文件,在正式生产环境加载镜像并运行即可,应用安装配置在镜像里已经内置好,大大节省部署配置和测试验证时间。

更便捷的升级和扩缩容

随着微服务架构和Docker的发展,大量的应用会通过微服务方式架构,应用的开发构建将变成搭乐高积木一样,每个Docker容器将变成一块“积木”,应用的升级将变得非常容易。当现有的容器不足以支撑业务处理时,可通过镜像运行新的容器进行快速扩容,使应用系统的扩容从原先的天级变成分钟级甚至秒级。

更简单的系统运维

应用容器化运行后,生产环境运行的应用可与开发、测试环境的应用高度一致,容器会将应用程序相关的环境和状态完全封装起来,不会因为底层基础架构和操作系统的不一致性给应用带来影响,产生新的BUG。当出现程序异常时,也可以通过测试环境的相同容器进行快速定位和修复。

更高效的计算资源利用

Docker是内核级虚拟化,其不像传统的虚拟化技术一样需要额外的Hypervisor支持,所以在一台物理机上可以运行很多个容器实例,可大大提升物理服务器的CPU和内存的利用率。

Docker 的下载和安装

官网和仓库

docker官网:http://www.docker.com

Docker Hub官网: https://hub.docker.com/

Docker 安装

alt text

  • 先卸载旧的Dokcer版本、
sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine
  • 下载yum安装工具
yum install -y yum-utils
  • 下载docker-ce的仓库文件,方便后面的安装
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
  • 安装Docker
yum install docker-ce docker-ce-cli containerd.io
  • 启动Docker,并检验是否安装成功
systemctl start docker
docker version

Docker 的卸载 (没有测试过)

  • 查看是否安装旧版的Docker
# 适用于使用 yum 安装 docker 的系统
yum list installed | grep docker
 
# 或者查看 docker 版本
docker --version

没有出现任何东西就表示没有安装过 docker,或者提示没有 docker 这个指令,就说明没有安装 docker。

  • 停止运行docker
systemctl stop docker
  • 查看docker状态
systemctl status docker
  • 删除安装过docker的安装包
sudo apt-get purge docker-ce docker-ce-cli containerd.io
  • 删除Docker相关文件
sudo rm -rf /var/lib/docker
sudo rm -rf /etc/docker
sudo rm -rf /var/run/docker.sock

Docker 的基本组成

镜像(image)

Docker 镜像(Image)就是一个只读的模板。镜像可以用来创建 Docker 容器,一个镜像可以创建很多容器。 它也相当于是一个root文件系统。比如官方镜像 centos:7 就包含了完整的一套 centos:7 最小系统的 root 文件系统。 相当于容器的“源代码”,docker镜像文件类似于Java的类模板,而docker容器实例类似于java中new出来的实例对象。

容器(container)

alt text

Docker是一个Client-Server结构的系统,Docker守护进程运行在主机上, 然后通过Socket连接从客户端访问,守护进程从客户端接受命令并管理运行在主机上的容器。 容器,是一个运行时环境,就是我们前面说到的集装箱。可以对比mysql演示对比讲解.

alt text

从面向对象角度

Docker 利用容器(Container)独立运行的一个或一组应用,应用程序或服务运行在容器里面,容器就类似于一个虚拟化的运行环境,容器是用镜像创建的运行实例。就像是Java中的类和实例对象一样,镜像是静态的定义,容器是镜像运行时的实体。容器为镜像提供了一个标准的和隔离的运行环境,它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台

从镜像容器角度

可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。

alt text

仓库(repository)

仓库(Repository)是集中存放镜像文件的场所。 类似于 Maven仓库,存放各种jar包的地方; github仓库,存放各种git项目的地方; Docker公司提供的官方registry被称为Docker Hub,存放各种镜像模板的地方。 仓库分为公开仓库(Public)和私有仓库(Private)两种形式。 最大的公开仓库是 Docker Hub(https://hub.docker.com/), 存放了数量庞大的镜像供用户下载。国内的公开仓库包括阿里云 、网易云等。

Docker 架构图

alt text

alt text

小结

需要正确的理解仓库/镜像/容器这几个概念: Docker 本身是一个容器运行载体或称之为管理引擎。我们把应用程序和配置依赖打包好形成一个可交付的运行环境,这个打包好的运行环境就是image镜像文件。只有通过这个镜像文件才能生成Docker容器实例(类似Java中new出来一个对象)。 image文件可以看作是容器的模板。Docker 根据 image 文件生成容器的实例。同一个 image 文件,可以生成多个同时运行的容器实例。 镜像文件

  • image 文件生成的容器实例,本身也是一个文件,称为镜像文件。 容器实例
  • 一个容器运行一种服务,当我们需要的时候,就可以通过docker客户端创建一个对应的运行实例,也就是我们的容器 仓库
  • 就是放一堆镜像的地方,我们可以把镜像发布到仓库中,需要的时候再从仓库中拉下来就可以了。

底层原理

为什么Docker会比VM虚拟机快

  • docker有着比虚拟机更少的抽象层

由于docker不需要Hypervisor(虚拟机)实现硬件资源虚拟化,运行在docker容器上的程序直接使用的都是实际物理机的硬件资源。因此在CPU、内存利用率上docker将会在效率上有明显优势。

  • docker利用的是宿主机的内核,而不需要加载操作系统OS内核

当新建一个容器时,docker不需要和虚拟机一样重新加载一个操作系统内核。进而避免引寻、加载操作系统内核返回等比较费时费资源的过程,当新建一个虚拟机时,虚拟机软件需要加载OS,返回新建过程是分钟级别的。而docker由于直接利用宿主机的操作系统,则省略了返回过程,因此新建一个docker容器只需要几秒钟。

alt text

alt text

Docker 常用命令

帮助启动类命令

  • 启动docker
shell
systemctl start docker
  • 停止docker
shell
systemctl stop docker
  • 重启docker
shell
systemctl restart docker
  • 查看docker状态
shell
systemctl status docker
  • 开机启动
shell
systemctl enable docker
  • 查看docker概要信息
shell
docker info
  • 查看docker总体帮助文档
shell
docker --help
  • 查看docker命令帮助文档
shell
docker 具体命令 --help
  • 实时监控正在运行的 Docker 容器的资源使用情况
绝密文件,请勿展开
shell
dockers stats 
CONTAINER ID   NAME        CPU %     MEM USAGE / LIMIT     MEM %     NET I/O           BLOCK I/O         PIDS
# 容器ID        容器名      CPU占用    内存使用 / 限制         内存占比   网络收/发流量       磁盘读/写数据      进程数量

5f16003c4ff7   catering    0.06%     318.9MiB / 1.674GiB   18.60%    37.9MB / 69.4MB   3.61TB / 0B       32
# catering 应用容器,CPU 占用低,内存使用了约 318MB,占用限制的 18.6%,
# 网络接收 37.9MB,发送 69.4MB,磁盘读取高达 3.61TB,写入为 0B,共运行 32 个进程

150cb16bfaac   mysql       0.11%     345.6MiB / 1.674GiB   20.16%    1.31MB / 1.26MB   1.44GB / 22.1MB   48
# MySQL 容器,CPU 使用略高,内存使用 345MB,约占 20%,
# 网络收发都在 1.3MB 左右,磁盘读取 1.44GB,写入 22MB,共 48 个进程

20bfe2cebc7c   vde-redis   0.10%     10.34MiB / 1.674GiB   0.60%     451MB / 36.3MB    907GB / 495MB     6
# Redis 容器,内存非常低(10MB),CPU 仅 0.1%,
# 网络接收达 451MB,发送 36.3MB,磁盘读写非常高:读取 907GB,写入 495MB,仅运行 6 个进程

镜像命令

  • 列出本地主机上的镜像
shell
docker images

alt text

shell
# REPOSITORY:表示镜像的仓库源
# TAG:镜像的标签版本号
# IMAGE ID:镜像ID
# CREATED:镜像创建时间
# SIZE:镜像大小
# 同一仓库源可以有多个 TAG版本,代表这个仓库源的不同个版本,我们使用 REPOSITORY:TAG 来定义不同的镜像。
# 如果你不指定一个镜像的版本标签,例如你只使用 ubuntu,docker 将默认使用 ubuntu:latest 镜像
# OPTIONS说明:
# -a :列出本地所有的镜像(含历史映像层)
# -q :只显示镜像ID。
  • 查询镜像信息
shell
docker search [OPTIONS] 镜像名字

# OPTIONS说明:
# --limit : 只列出N个镜像,默认25个
# docker search --limit 5 redis

alt text

提示:docker search这个命令可能没有效果,会出现 Error response from daemon: Get "https://index.docker.io/v1/search?q=nginx&n=25": dial tcp 69.63.176.59:443: connect: connection refused 的错误显示,这是因为这个地址在中国大陆 几乎必定被墙或者干扰,即使你设置了阿里云、网易、USTC 的镜像加速器,它们也只是用于拉镜像,不负责搜索功能。 执行搜索命令 docker search docker.1ms.run/tomcat 就可以成功,因为这个是你直接访问的国内私有/镜像仓库地址(docker.1ms.run),搜索请求没有走 Docker Hub,自然没被墙。

  • 下载镜像
shell
docker pull 镜像名字[:TAG]

docker pull 镜像名字

# 没有TAG就是最新版等价于 docker pull 镜像名字:latest  docker pull ubuntu

提示:docker pull命名拉取镜像会出现 docker: Error response from daemon: Get “https://registry-1.docker.io/v2/”: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers).的错误;解决方式使用腾讯云 Docker 镜像加速器

shell
# 1. 创建或修改 Docker 配置文件
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://mirror.ccs.tencentyun.com"]
}
EOF

# 2. 重启 Docker
sudo systemctl daemon-reload
sudo systemctl restart docker

# 3. 重新尝试搜索或拉取 Redis
docker search redis
  • 修改镜像名称
shell
# 使用docker tag命令重命名
docker tag 旧镜像名称:标签 新镜像名称:新标签
# 举例
docker tag myapp:1.0 myregistry.com/team/myapp:1.0

# 删除旧的镜像
docker rmi 旧镜像名称:标签
  • 查看镜像/容器/数据卷所占的空间
docker system df
  • 删除镜像
shell
docker rmi <镜像名或镜像ID>
# rmi:是 remove image 的缩写,用于删除一个或多个 Docker 镜像
# 如果你确定要强制删除,可以加 -f 参数 例如:sudo docker rmi -f mycentos:0.1
  • 复制镜像(从一个服务器上复制镜像到另一个服务器[不上传到镜像仓库的方式])
shell
# 1、在源服务器上镜像保存为tar文件(镜像大可以考虑gzip进行压缩)
docker save -o image.tar image-name:tag

# 2、将tar文件传输到目标服务器

#3、在目标服务器上加载镜像(加载之后,docker images就可以看到该镜像了)
docker load -i image.tar

容器命令

  • 基于一个镜像启动一个新的容器
shell
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
# docker run	告诉 Docker “我要运行一个容器”
# [OPTIONS]	可选参数,用来控制容器的运行方式(比如是否后台运行、挂载卷、映射端口等) 
# 常用的OPTIONS 的参数:
# --name="容器新名字"       为容器指定一个名称;
# -d: 后台运行容器并返回容器ID,也即启动守护式容器(后台运行);
# -i:以交互模式运行容器,通常与 -t 同时使用;
# -t:为容器重新分配一个伪输入终端,通常与 -i 同时使用;
# 也即启动交互式容器(前台有伪终端,等待交互);
# -P: 随机端口映射,大写P
# -p: 指定端口映射,小写p
# IMAGE	使用哪个镜像来创建容器,比如 ubuntu、mycentos:0.1
# [COMMAND]	覆盖镜像默认的启动命令(可选)
# [ARG...]	传递给 [COMMAND] 的参数(可选)

举例①:运行一个镜像并进入交互模式

docker run -it ubuntu bash # -it: 启动交互终端 ubuntu: 镜像名 bash: 要执行的命令

举例②:后台运行容器

docker run -d nginx # -d: 表示后台运行容器(detached)nginx: 运行 nginx 镜像

举例③:影响端口

docker run -d -p 8080:80 nginx # -p 8080:80: 把本机的 8080 端口映射到容器的 80 端口

举例④:挂载本地目录到容器

docker run -v /host/path:/container/path myimage

  • 列出当前所有正在运行的容器
shell
docker ps [OPTIONS]

# OPTIONS说明(常用):
# -a :列出当前所有正在运行的容器+历史上运行过的
# -l :显示最近创建的容器。
# -n:显示最近n个创建的容器。
# -q :静默模式,只显示容器编号。

alt text

  • 退出容器

方式一:

shell
exit
# run进去容器,exit退出,容器停止

方式二:

shell
ctrl+p+q
# run进去容器,ctrl+p+q退出,容器不停止
  • 启动已停止运行的容器

docker start 容器ID或者容器名

  • 重启容器
shell
docker restart 容器ID或者容器名
  • 停止容器
shell
docker stop 容器ID或者容器名
  • 强制停止容器
shell
docker kill 容器ID或容器名
  • 删除已停止的容器
shell
docker rm 容器ID

重要且常用的命令

  • 启动守护式容器(后台服务器)

在大部分的场景下,我们希望 docker 的服务是在后台运行的, 我们可以过 -d 指定容器的后台运行模式。

shell
docker run -d 容器名

特别注意: 问题:然后docker ps -a 进行查看, 会发现容器已经退出很重要的要说明的一点: Docker容器后台运行,就必须有一个前台进程。容器运行的命令如果不是那些一直挂起的命令(比如运行top,tail),就是会自动退出的(如下图:运行Centos镜像会出现这个问题)。这个是docker的机制问题,比如你的web容器,我们以nginx为例,正常情况下,我们配置启动服务只需要启动响应的service即可。例如service nginx start。但是,这样做,nginx为后台进程模式运行,就导致docker前台没有运行的应用,这样的容器后台启动后,会立即自杀因为他觉得他没事可做了。所以,最佳的解决方案是将你要运行的程序以前台进程的形式运行,常见就是命令行模式,表示我还有交互操作,别中断。

alt text

CentOS 前后台启动演示case

前台交互式启动:docker run -it mycentos:0.1

后台守护式启动:docker run -d mycentos:0.1

  • 查看容器日志
绝密文件,请勿展开
shell
docker logs 容器ID # 查看日志信息

docker logs --help
Options:
      --details        Show extra details provided to logs 
*  -f, --follow         Follow log output
      --since string   Show logs since timestamp (e.g. 2013-01-02T13:23:37) or relative (e.g. 42m for 42 minutes)
*      --tail string    Number of lines to show from the end of the logs (default "all")
*  -t, --timestamps     Show timestamps
      --until string   Show logs before a timestamp (e.g. 2013-01-02T13:23:37) or relative (e.g. 42m for 42 minutes)
  ~ docker run -d centos /bin/sh -c "while true;do echo 6666;sleep 1;done" #模拟日志      
#显示日志
-tf		#显示日志信息(一直更新)
--tail number #需要显示number条日志
docker logs -t --tail n 容器id #查看n行日志
docker logs -ft 容器id #跟着日志
  • 查看容器内运行的进程
docker top 容器ID
  • 查看容器内部细节,输出为JSON格式
docker inspect 容器ID
  • 进入正在运行的容器并以命令行交互
shell
# 方式一
docker exec -it 容器ID(后者容器名称) bin/bash
# 方式二
docker attach 容器id
# docker exec #进入当前容器后开启一个新的终端,可以在里面操作。(常用)
# docker attach # 进入容器正在执行的终端
  • 从容器内拷贝文件到主机
绝密文件,请勿展开
shell
docker cp 容器id:容器内路径 目的主机路径
# 查看容器
[root@VM-4-17-centos home]# docker ps
CONTAINER ID   IMAGE     COMMAND       CREATED          STATUS          PORTS     NAMES
2b19b6c989ee   centos    "/bin/bash"   47 seconds ago   Up 46 seconds             nice_sanderson
# 进入容器
[root@VM-4-17-centos home]# docker attach 2b19b6c989ee
[root@2b19b6c989ee /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
[root@2b19b6c989ee /]# cd home
[root@2b19b6c989ee home]# ls
[root@2b19b6c989ee home]# touch zwj.java
[root@2b19b6c989ee home]# ls
zwj.java
# 拷贝
[root@2b19b6c989ee home]# touch zwj.javaread escape sequence
[root@VM-4-17-centos home]# docker cp 2b19b6c989ee:/home/zwj.java /home/www
# 查看验证
[root@VM-4-17-centos home]# cd www
[root@VM-4-17-centos www]# ls
zwj.java

命令汇总

绝密文件,请勿展开
attach      Attach local standard input, output, and error streams to a running container  # 当前终端 attach 连接正在运行的容器
build       Build an image from a Dockerfile                                               # 通过 Dockerfile 构建镜像
commit      Create a new image from a container's changes                                  # 将容器提交为新的镜像
cp          Copy files/folders between a container and the local filesystem                # 容器与本地之间复制文件
create      Create a new container                                                         # 创建一个新的容器但不启动
diff        Inspect changes to files or directories on a container's filesystem            # 查看容器文件系统改动
events      Get real time events from the server                                           # 获取 Docker 服务实时事件流
exec        Run a command in a running container                                           # 在运行中的容器中执行命令
export      Export a container's filesystem as a tar archive                               # 导出容器文件系统为 tar 包(不含历史层)
history     Show the history of an image                                                   # 显示镜像的构建历史
images      List images                                                                    # 列出所有本地镜像
import      Import the contents from a tarball to create a filesystem image                # 从 tar 包导入生成镜像(对应 export)
info        Display system-wide information                                                # 显示 Docker 系统级别信息
inspect     Return low-level information on Docker objects                                 # 查看容器、镜像等对象的详细信息
kill        Kill one or more running containers                                            # 强制停止一个或多个容器
load        Load an image from a tar archive or STDIN                                      # 从 tar 包中加载镜像(对应 save)
login       Log in to a Docker registry                                                    # 登录到镜像仓库(如 Docker Hub)
logout      Log out from a Docker registry                                                 # 登出当前镜像仓库
logs        Fetch the logs of a container                                                  # 获取容器的日志输出
pause       Pause all processes within one or more containers                              # 暂停容器中的所有进程
port        List port mappings or a specific mapping for the container                     # 显示容器端口映射
ps          List containers                                                                # 查看当前所有容器列表(默认仅显示运行中)
pull        Pull an image or a repository from a registry                                  # 从镜像仓库拉取镜像
push        Push an image or a repository to a registry                                    # 将本地镜像推送到镜像仓库
rename      Rename a container                                                             # 重命名容器
restart     Restart one or more containers                                                 # 重启容器
rm          Remove one or more containers                                                  # 删除一个或多个容器
rmi         Remove one or more images                                                      # 删除一个或多个镜像
run         Run a command in a new container                                               # 启动一个新容器并运行命令
save        Save one or more images to a tar archive (streamed to STDOUT by default)       # 将镜像保存为 tar 包(对应 load)
search      Search the Docker Hub for images                                               # 搜索 Docker Hub 镜像
start       Start one or more stopped containers                                           # 启动一个或多个已停止的容器
stats       Display a live stream of container(s) resource usage statistics                # 实时查看容器资源使用情况
stop        Stop one or more running containers                                            # 停止容器运行
tag         Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE                          # 给镜像打标签
top         Display the running processes of a container                                   # 查看容器中运行的进程
unpause     Unpause all processes within one or more containers                            # 恢复容器中已暂停的进程
update      Update configuration of one or more containers                                 # 更新容器配置(如资源限制)
version     Show the Docker version information                                            # 显示 Docker 的版本信息
wait        Block until one or more containers stop, then print their exit codes           # 等待容器停止并打印退出码

Docker镜像详解

镜像是什么

镜像是一种轻量级、可执行的独立软件保,用来打包软件运行环境和基于运行环境开发的软件,他包含运行某个软件所需的所有内容,包括代码、运行时库、环境变量和配置文件。 所有应用,直接打包docker镜像,就可以直接跑起来!

如何得到镜像:

  • 从远程仓库下载
  • 拷贝(将镜像制作成tar包的方式)
  • 自己制作 DockerFile

Docker镜像加载原理

UnionFs (联合文件系统)

UnionFs(联合文件系统):Union文件系统(UnionFs)是一种分层、轻量级并且高性能的文件系统,他支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下( unite several directories into a single virtual filesystem )。Union文件系统是 Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像 特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。

Docker镜像加载

docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。 boots(boot file system)主要包含 bootloader和 Kernel, bootloader主要是引导加 kernel, Linux刚启动时会加bootfs文件系统,在 Docker镜像的最底层是 boots。这一层与我们典型的Linux/Unix系统是一样的,包含boot加載器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由 bootfs转交给内核,此时系统也会卸载bootfs。 rootfs(root file system),在 bootfs之上。包含的就是典型 Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。 rootfs就是各种不同的操作系统发行版,比如 Ubuntu, Centos等等。

alt text

平时我们安装进虚拟机的CentOS都是好几个G,为什么Docker这里才200M?

alt text

对于个精简的OS,rootfs可以很小,只需要包合最基本的命令,工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就可以了。由此可见对于不同的Linux发行版, boots基本是一致的, rootfs会有差別,因此不同的发行版可以公用bootfs.

镜像的分层原理

分层存储的工作原理

Docker镜像是由一系列只读的层(layer)组成的。这些层按照从下到上的顺序堆叠,每一层都是基于下面一层的变化。当我们创建或更新一个镜像时,只有被改变的部分会被添加为一个新层,其他部分保持不变。 这种分层存储的机制使得Docker镜像可以被高效地构建、传输和存储。

举例说明:docker 拉取Redis 镜像,下载Redis镜像,实际是下载6个层级去实现的,当存在相同的层级时,就是实现层级的共享,例如下图中的第一个层级,存在则不需要下载,直接复用!

alt text

查看镜像分层的方式可以通过docker image inspect 命令

docker image inspect

绝密文件,请勿展开
shell
[root@VM-12-15-centos ~]# docker image inspect redis:6.2.6
[
    {
        "Id": "sha256:3c3da61c4be0fb9e93fc84fb702b6a1e01d8c43ffae4d2ea88192803c2b44191",
        "RepoTags": [
            "redis:6.2.6"
        ],
        "RepoDigests": [
            "redis@sha256:b7fd1a2c89d09a836f659d72c52d27b9f71202c97014a47639f87c992e8c0f1b"
        ],
        "Parent": "",
        "Comment": "",
        "Created": "2022-04-20T13:29:53.41875044Z",
        "DockerVersion": "20.10.12",
        "Author": "",
        "Config": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "6379/tcp": {}
            },
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "GOSU_VERSION=1.14",
                "REDIS_VERSION=6.2.6",
                "REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-6.2.6.tar.gz",
                "REDIS_DOWNLOAD_SHA=5b2b8b7a50111ef395bf1c1d5be11e6e167ac018125055daa8b5c2317ae131ab"
            ],
            "Cmd": [
                "redis-server"
            ],
            "Image": "sha256:fa6fb6c397c7a4acd229a5c8252793a25dc853df9a2e8af04ac9507bfb9d9d24",
            "Volumes": {
                "/data": {}
            },
            "WorkingDir": "/data",
            "Entrypoint": [
                "docker-entrypoint.sh"
            ],
            "OnBuild": null,
            "Labels": null
        },
        "Architecture": "amd64",
        "Os": "linux",
        "Size": 112618313,
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/a347b17b19d17964a77a1ab604fab844b9654f09c7ba00d4df8a7cd85ee46cd8/diff:/var/lib/docker/overlay2/e520dc0fca2b5995bafd50cbd2bf70f5858a8b31c80eda9405879438a45ecbec/diff:/var/lib/docker/overlay2/b4019e80c76f8c08829dabd97d44c589a1548a27ce4b7849e82711aaa3968caf/diff:/var/lib/docker/overlay2/4573f713d338c38efca765fdd19d53934713551027a861c6cd9991db29054e8e/diff:/var/lib/docker/overlay2/cf5f0e524b7b1dcf91606a5bb34dc5353d8f977fc16378fe3198872024e3c6f5/diff",
                "MergedDir": "/var/lib/docker/overlay2/a2f60309700c13dd92243d76320b523c0d468c7b667e953bdc1b5452b505be71/merged",
                "UpperDir": "/var/lib/docker/overlay2/a2f60309700c13dd92243d76320b523c0d468c7b667e953bdc1b5452b505be71/diff",
                "WorkDir": "/var/lib/docker/overlay2/a2f60309700c13dd92243d76320b523c0d468c7b667e953bdc1b5452b505be71/work"
            },
            "Name": "overlay2"
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:9c1b6dd6c1e6be9fdd2b1987783824670d3b0dd7ae8ad6f57dc3cea5739ac71e",
                "sha256:d65e371da0453ddb98c6be22dcc90e7a6eb875e2132210657dde8322cb257dc6",
                "sha256:f0228b605b4af7181c89c268d333a0d41d815fe30dda1e7f481f02dee3642d27",
                "sha256:b959234ba79a363e2ebbdb652b33de94df87c24ba63d78c34b8c0823930cd94c",
                "sha256:cfcecd8b0c43b02f466e77a62b0e507df795d752f32fafb37c9a88e00e9d1934",
                "sha256:65d8db3af33580e3274c7dbea66651b51e2d74dce04c3a7ad8292b039275eed3"
            ]
        },
        "Metadata": {
            "LastTagTime": "2025-05-07T08:13:00.014145248+08:00"
        }
    }
]

:在上面的docker image inspect redis:6.2.6查询的信息显示,在RootFS 中的Layers 中显示6层,当下载的时候也可以看到是分为这6个层级下载

层的创建过程

通常,我们使用Dockerfile来定义和创建Docker镜像。Dockerfile中的每一条指令(如FROM,RUN,COPY等)都会创建一个新的层。 例如,考虑以下Dockerfile:

shell
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y python3
COPY app.py /app/
CMD ["python3", "/app/app.py"]

这个Dockerfile会创建四个层:

  • 基础层:从ubuntu:22.04镜像开始。
  • 第二层:运行apt-get update && apt-get install -y python3,安装Python3。
  • 第三层:将app.py文件复制到镜像的/app/目录。
  • 第四层:指定容器启动时运行的命令python3 /app/app.py。

层的存储与复用

这些层都是只读的,并且每一层只存储与前一层的差异部分。当我们构建一个新镜像时,Docker会检查每一层的内容是否已经存在。如果一个层与现有的层完全相同,Docker会直接复用这个层,而不是重新创建。 这种机制大大提高了构建和存储效率。例如,如果你有多个Dockerfile都基于python:3.11,那么这个基础层只需要存储一次,所有的镜像都可以共享它。

Dockerfile1:

shell
FROM python:3.11
RUN pip install django
COPY app1/ /app/
WORKDIR /app
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

Dockerfile2:

shell
FROM python:3.11
RUN pip install django pillow
COPY app2/ /app/
WORKDIR /app
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

当我们构建这两个镜像时,Docker会创建以下层:

  1. 基础层(python:3.11)
  2. Django层(RUN pip install django)
  3. Pillow层(RUN pip install django pillow)
  4. 应用1层(COPY app1/ /app/)
  5. 应用2层(COPY app2/ /app/) 其中,基础层和Django层是共享的,因为它们在两个Dockerfile中是相同的。Pillow层、应用1层和应用2层是独立的,因为它们在每个Dockerfile中是不同的。 当这两个镜像都构建完成后,Docker只需在文件系统中存储一次基础层和Django层,而不是两次。这样就节省了存储空间,并且加速了镜像的构建和分发过程。

创建自定义镜像

使用 docker commit 命令可以通过对现有容器进行修改来创建新的镜像。-a 选项用于指定作者信息,-m 选项用于添加提交信息。

  • 启动并修改容器,例如,启动一个 Ubuntu 容器并安装一些软件包
shell
# 启动容器
docker run -it ubuntu /bin/bash
# 在容器内执行一些操作,例如安装 vim:
apt-get update
apt-get install -y vim
  • 提交容器为镜像:在另一个终端中,使用 docker ps 命令查看正在运行的容器,获取容器的 ID 或名称。然后使用 docker commit 命令提交容器为新的镜像
shell
docker commit -a "Your Name" -m "Added vim" <container_id_or_name> new_image_name:tag
#  -a "Your Name" 指定作者信息。
#  -m "Added vim" 添加提交信息。
#  <container_id_or_name> 是容器的 ID 或名称。
#  new_image_name:tag 是新镜像的名称和标签。
  • 验证新镜像:使用 docker images 命令查看新创建的镜像,可以看到新创建的镜像 new_image_name:tag
shell
docker images
  • 运行新镜像,可以使用新创建的镜像启动容器:
shell
docker run -it new_image_name:tag /bin/bash

Docker 挂载外部文件

Docker挂载外部文件是指将宿主机上的文件或目录挂载到Docker容器内部,以便容器内的应用程序可以访问这些文件。这种操作通常用于数据持久化、配置文件共享、日志收集等场景

数据卷介绍

基础概念

挂载(Mount):在计算机科学中,挂载是指将一个文件系统连接到另一个文件系统的过程。在Docker中,挂载允许容器访问宿主机的文件或目录

卷(Volume):Docker卷是一种特殊的目录,它可以被容器使用,也可以被宿主机或其他容器使用。卷的设计目的是为了持久化数据,并且与容器的生命周期独立

优势

数据卷是主机上的一个目录,被映射到容器内某个目录,可以实现:

  • 持久化数据(即使容器删除,数据仍在)
  • 多个容器共享数据
  • 容器和宿主机之间传输数据

类型

  • 绑定挂载(Bind Mounts):将宿主机的文件或目录直接挂载到容器内
  • 卷(Volumes):由Docker管理的特殊目录,可以跨容器共享
  • tmpfs挂载:将内存用作临时存储空间

应用场景

  • 配置文件:将应用程序的配置文件放在宿主机上,通过挂载传递给容器。
  • 日志文件:将容器的日志输出到宿主机的特定目录,便于集中管理和监控。
  • 数据库文件:确保数据库的数据文件在容器重启后仍然存在。

实战:Mysql 数据卷挂载

使用数据卷技术实现Mysql 数据持久化

shell
docker run --name mysql \
-d -p 3306:3306 --restart unless-stopped \
-v /root/vde/mysql/data:/var/lib/mysql \
-v /root/vde/mysql/conf/my.cnf:/etc/my.cnf \
-e MYSQL_ROOT_HOST="%"  \
-e MYSQL_ROOT_PASSWORD="123456" \
mysql:8.0.30 \
--default-authentication-plugin=mysql_native_password

在运行Docker容器命令中加上 -v实现挂载,将容器内部文件夹内容和服务器中的文件内容实现同步,从而实现Mysql 数据持久化

alt text

具名和匿名挂载

匿名挂载和具名挂载的区别

  • 匿名挂载:不写宿主机目录
  • 具名挂载:会给宿主机起一个名字
shell
# 匿名挂载  -v 容器内路径!
docker run -d -P --name nginx01 -v /ect/nginx nginx  
# 查看所有的 volume 的情况
[root@@e0b133aa7755 home]# docker volume ls
local 9f38292179faa178afcce54d80be99d4ddd68c91d2a68870bcece72d2b7ed061 
# 这里发现,这种就是匿名挂载,我们在—v 只写了容器内的路径,没有写容器外的路径!

# 具名挂载
[root@@e0b133aa7755 home]#docker run-d -P --name nginx02 -v juming-nginx:/etc/nginx nginx 
95b809564484c8ac87d65c69643e7e67447f1c77ff9a91b93edec7003692e3a9
[root@@e0b133aa7755 home]# docker volume ls 
DRIVER VOLUME NAME 
local  juming-nginx # 通过—v 卷名:容器内路径
# 查看一下这个卷
# 所有的docker容器内的卷,没有指定的目录的情况下都是在/var/lib/docker/volumes/xxx/_data

区分挂载方式

shell
-v 容器內路径 # 匿名挂载
-v 卷名:容器内路径 # 具名挂载
-v 宿主机路径:容器内路径 # 指定路径挂载!

拓展:ro、rw

shell
# 通过 —v 容器内路径:ro(readonly) rw(readwrite) 改变读写权限 
# 一旦这个了设置了容器权限,容器对我们挂载出来的内容就有限定了!
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:ro nginx 
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:rw nginx 
# ro 只要看到ro就说明这个路径只能通过宿主机来操作,容器内部是无法操作!

Docker File

初始Dockerfile

DockerFile使用来构建docker镜像的文件!命令脚本!通过这个脚本可以生成镜像,镜像时一层一层的,脚本是一个一个的命令,每个命令都是一层!镜像是一层一层的,脚本是一行一行的

  • Dockerfile 内容
shell
# 指令都是大写的、
# 创建一个dockerfile文件,命名为Dockerfile
#文件内容:·
FROM centos   # 一层
VOLUME ["volume01","volume02"]  # 一层 再创建镜像的时候就挂载出来
CMD echo "---end---"    # 一层
CMD /bin/bash    # 一层
  • 构建Dockerfile
shell
# 使用 Dockerfile 构建 Docker 镜像
docker build \                     # 构建镜像命令
  -f Dockerfile \                 # 指定 Dockerfile 文件路径(此处为当前目录下的 Dockerfile)
  -t zy/cnetos1.0 \               # 给构建好的镜像命名为 zy/cnetos1.0(zy 是命名空间,cnetos1.0 是镜像名和标签)
  .                               # 指定构建上下文路径为当前目录(.)

# 举例
docker build -f Dockerfile -t zy/cnetos1.0 .

alt text

  • 查看宿主机中挂载的文件
shell
# 显示 所有 Docker 卷的列表,包括:卷的名称、驱动类型(默认是 local)
docker volume ls

# 查看卷的详细信息
docker volume inspect VLOUME NAME

alt text

类型命令中挂载方式docker volume ls 是否可见持久化路径
✅ 具名挂载-v myvolume:/data✅ 是/var/lib/docker/volumes/myvolume/_data
✅ 匿名挂载-v /data(省略左侧)✅ 是(名称为随机字符串)/var/lib/docker/volumes/xxx/_data
❌ 指定路径挂载-v /host/path:/data❌ 否使用宿主机原路径

Dockerfile 详解

dockerFile是用来构建docker镜像的文件!命令参数脚本!

shell
构建步骤
1. 编写一个dockerFile文件 
2.docker build 构建成为一个镜像
3. docker run 运行镜像
4. docker push 发布镜像(DockerHub、阿里云镜像)

Dockerfile文件说明

Dockerfile 一般分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令,’#’ 为 Dockerfile 中的注释;Docker以从上到下的顺序运行Dockerfile的指令。为了指定基本映像,第一条指令必须是FROM。一个声明以#字符开头则被视为注释。可以在Docker文件中使用RUN,CMD,FROM,EXPOSE,ENV等指令。

  • FROM:指定基础镜像,必须为第一个命令
shell
# tag或digest是可选的,如果不使用这两个值时,会使用latest版本的基础镜像
FROM <image>
FROM <image>:<tag>
FROM <image>@<digest>

# 示例
FROM mysql:5.6
  • MAINTAINER: 维护者信息
shell
MAINTAINER <name>

#示例
MAINTAINER Jasper Xu
MAINTAINER sorex@163.com
MAINTAINER Jasper Xu <sorex@163.com>
  • RUN:构建镜像时执行的命令
shell
# RUN用于在镜像容器中执行命令,其有以下两种命令执行方式:

# 格式一:shell执行
RUN <command>

# 格式二:exec执行
RUN ["executable", "param1", "param2"]

# 示例
RUN ["executable", "param1", "param2"]
RUN apk update
RUN ["/etc/execfile", "arg1", "arg1"]

# 注:RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定--no-cache参数,如:docker build --no-cache
  • ADD:将本地文件添加到容器中,tar类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似wget
shell
ADD <src>... <dest>
ADD ["<src>",... "<dest>"] 用于支持包含空格的路径

# 示例
ADD hom* /mydir/          # 添加所有以"hom"开头的文件
ADD hom?.txt /mydir/      # ? 替代一个单字符,例如:"home.txt"
ADD test relativeDir/     # 添加 "test" 到 `WORKDIR`/relativeDir/
ADD test /absoluteDir/    # 添加 "test" 到 /absoluteDir/
COPY:功能类似ADD,但是是不会自动解压文件,也不能访问网络资源
  • CMD:构建容器后调用,也就是在容器启动时才进行调用
shell
CMD ["executable","param1","param2"] (执行可执行文件,优先)
CMD ["param1","param2"] (设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数)
CMD command param1 param2 (执行shell内部命令)

# 示例
CMD echo "This is a test." | wc -
CMD ["/usr/bin/wc","--help"]

# 注:CMD不同于RUN,CMD用于指定在容器启动时所要执行的命令,而RUN用于指定镜像构建时所要执行的命令。
  • ENTRYPOINT:配置容器,使其可执行化。配合CMD可省去"application",只使用参数。
shell
ENTRYPOINT ["executable", "param1", "param2"] (可执行文件, 优先)
ENTRYPOINT command param1 param2 (shell内部命令)

# 示例
ENTRYPOINT["top","-b"]
CMD["-c"]
  • ARG:用于指定传递给构建运行时的变量
shell
ARG <name>[=<default value>]

# 示例
ARG site
ARG build_user=www
  • ENV:设置环境变量
shell
ENV <key> <value>  #<key>之后的所有内容均会被视为其<value>的组成部分,因此,一次只能设置一个变量
ENV <key>=<value> ...  #可以设置多个变量,每个变量为一个"<key>=<value>"的键值对,如果<key>中包含空格,可以使用\来进行转义,也可以通过""来进行标示;另外,反斜线也可以用于续行

# 示例
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat=fluffy
  • EXPOSE:指定于外界交互的端口
shell
EXPOSE <port> [<port>...]

# 示例
EXPOSE 80 443
EXPOSE 8080
EXPOSE 11211/tcp 11211/udp
# 注:EXPOSE并不会让容器的端口访问到主机。要使其可访问,需要在docker run运行容器时通过-p来发布这些端口,或通过-P参数来发布EXPOSE导出的所有端口
  • VOLUME:用于指定持久化目录
shell
VOLUME ["/path/to/dir"]

# 示例
VOLUME ["/data"]
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
  • WORKDIR:工作目录,类似于cd命令
shell
WORKDIR /path/to/workdir

# 示例
WORKDIR /a  (这时工作目录为/a)
WORKDIR b  (这时工作目录为/a/b)
WORKDIR c  (这时工作目录为/a/b/c)

指令区分

ARG和ENV区别

特性ARG(构建参数)ENV(环境变量)
定义时机Docker 构建镜像时 使用Docker 构建和运行容器时 都可以用
是否持久❌ 只在 docker build 阶段有效,不进入镜像中✅ 镜像中保留,容器运行时仍然可用
是否可覆盖✅ 可通过 --build-arg 传参覆盖✅ 可通过 -e 运行容器时覆盖
用途控制镜像构建过程中的逻辑(如基础镜像、版本等)设置容器运行时所需的配置,如端口、路径、用户名等
示例命令docker build --build-arg VERSION=1.0 .docker run -e JAVA_HOME=/usr/java container_name

1️⃣ ARG 示例

shell
# Dockerfile
ARG VERSION=1.0
FROM openjdk:${VERSION}
# 构建镜像
docker build --build-arg VERSION=17 -t my-java-app .

# 注:当构建镜像时传入了VERSION=17的参数,就会覆盖掉Dockerfile中VERSION 的默认参数

2️⃣ ENV 示例

shell
FROM ubuntu
ENV JAVA_HOME=/usr/java
ENV PATH=$JAVA_HOME/bin:$PATH
# 注:容器运行后依然能通过 echo $JAVA_HOME 获取变量

CMD和ENTRYPOINT区别

CMD 和 ENTRYPOINT 都是 Dockerfile 中用于定义容器启动时执行的命令,但它们有明显区别,尤其是在传参方式和覆盖行为上

特性CMDENTRYPOINT
定义目的提供容器启动的默认命令或参数强制容器启动时执行的命令
可被覆盖✅ 可以被 docker run 的命令行参数覆盖❌ 不会被覆盖,但可被 CMD 提供参数
常见写法CMD ["echo", "hello"]CMD echo helloENTRYPOINT ["ping"]
搭配使用方式常与 ENTRYPOINT 搭配作为其默认参数CMD 组合使用形成完整命令

示例说明:

1️⃣ CMD 会被覆盖

shell
FROM ubuntu
CMD ["echo", "Hello from CMD"]

docker build -t cmd-demo .
docker run cmd-demo                  # 输出:Hello from CMD
docker run cmd-demo echo "Hi"       # 输出:Hi(CMD 被覆盖了)

2️⃣ ENTRYPOINT 不会被覆盖

shell
FROM ubuntu
ENTRYPOINT ["echo", "Hi from ENTRYPOINT"]

docker build -t entry-demo .
docker run entry-demo               # 输出:Hi from ENTRYPOINT
docker run entry-demo hello world   # 输出:Hi from ENTRYPOINT hello world(追加参数)

3️⃣ ENTRYPOINT + CMD 组合使用(推荐方式)

shell
FROM ubuntu
ENTRYPOINT ["ping"]
CMD ["127.0.0.1"]

docker build -t combo-demo .
docker run combo-demo               # 相当于:ping 127.0.0.1
docker run combo-demo google.com    # 相当于:ping google.com(CMD 被覆盖)

指令总结

指令含义说明
FROM基础镜像,一切构建从这里开始(如:FROM centos
MAINTAINER维护者信息,标注镜像创建者姓名和邮箱(已被弃用,建议用 LABEL 替代)
ARG设置构建参数,仅在 docker build 阶段有效,不会保留在镜像中
ENV设置环境变量,用于构建时或运行时读取配置
WORKDIR设置工作目录,后续的 RUNCMD 等命令将在该目录下执行
RUN构建镜像时执行的命令,如安装软件包、配置环境
ADD复制文件并自动解压,也可处理 URL 下载和解压压缩包
COPY复制文件到镜像中(比 ADD 简单,不会解压或处理 URL)
VOLUME挂载卷,用于数据持久化或与宿主机/其他容器共享数据
EXPOSE声明端口,告诉 Docker 容器内部将使用哪些端口
CMD容器启动时执行的默认命令,可以被 docker run 指令的参数覆盖
ENTRYPOINT容器启动时执行的主命令,不会被覆盖,可追加参数
ONBUILD触发指令,当该镜像被 FROM 继承时,会触发该指令

指令应用

shell
# 设置构建参数,默认使用 JDK 17 版本
ARG VERSION=17

# 使用构建参数,作为基础镜像
FROM openjdk:${VERSION}

# 设置维护者信息(推荐使用 LABEL 替代 MAINTAINER)
LABEL maintainer="your_name <your_email@example.com>"

# 设置构建时环境变量
ARG JAR_FILE=target/app.jar

# 设置运行时环境变量(在容器运行时也可用)
ENV TZ=Asia/Shanghai \
    LANG=en_US.UTF-8

# 设置容器内的工作目录
WORKDIR /app

# 拷贝 jar 包到容器中(也可用 ADD,如果你想自动解压)
COPY ${JAR_FILE} app.jar

# 声明数据卷(可用于日志或配置等持久化)
VOLUME ["/app/logs"]

# 声明容器将使用的端口(仅声明,不会真正映射)
EXPOSE 8080

# 构建镜像时执行的命令(这里示例为更新和安装 curl,仅作为构建阶段的操作)
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

# ONBUILD 指令,仅当此镜像被用作其他 Dockerfile 的基础镜像时才执行
ONBUILD COPY config.yml /app/config.yml

# 容器启动时执行的主命令,可通过 docker run 追加参数
ENTRYPOINT ["java", "-jar", "app.jar"]

# 可选:默认参数,如果不传参数会使用这些(ENTRYPOINT 不会被覆盖,CMD 会被覆盖)
CMD ["--spring.profiles.active=prod"]

Docker 网络

Docker实际应用

通过Jar包制作镜像

  • 本地镜像需要jdk的基础镜像(最好先在本地安装好该镜像)

alt text

  • 创建Dockerfile文件并填充一下信息(该文件和Jar放在同一级目录下)
shell
# 使用官方 Java 基础镜像(推荐选择与开发环境一致的 JDK 版本,由于服务器内网环境无法拉取线上基础镜像故自行构建Java8镜像)
FROM openjdk:8 

# 设置容器内工作目录(可选)
# WORKDIR /app

# 复制 JAR 文件到容器中(注意名称与实际打包的 JAR 文件一致)(这里提前将app.jar通过oss复制进来放在了Dockerfile文件同级目录中,故无需指定target路径)
COPY catering.jar catering.jar 

# 暴露应用端口(与 application.yml/properties 中 server.port 一致)
EXPOSE 8087 

# 设置 JVM 启动参数(可选,如内存限制、时区)
ENV JAVA_OPTS="-Xmx512m -Duser.timezone=Asia/Shanghai"

# 启动命令(使用 exec 形式避免信号丢失)
ENTRYPOINT exec java ${JAVA_OPTS} -jar catering.jar
  • 通过下面命令构建镜像,执行命令之后就可以在镜像文件中查看到该文件
docker build -t catering .

常用工具镜像启动脚本

  • Ngixn 启动
shell
#!/bin/sh

echo "start nginx images============"
docker run \
-p 8888:8888 \
-p 3000:3000 \
--name nginx \
--restart=always \
-v ./conf/nginx.conf:/etc/nginx/nginx.conf \
-v ./conf/conf.d:/etc/nginx/conf.d \
-v ./log:/var/log/nginx \
-v ./html/dsit:/usr/share/nginx/html/dist \
-d nginx

echo "run nginx containers end============"
  • MySql 启动
shell
#!/bin/sh
echo "start mysql images============"

docker run --name mysql \
-d -p 3306:3306 --restart unless-stopped \
-v /root/vde/mysql/data:/var/lib/mysql \
-v /root/vde/mysql/conf/my.cnf:/etc/my.cnf \
-e MYSQL_ROOT_HOST="%"  \
-e MYSQL_ROOT_PASSWORD="123456" \
mysql:8.0.30 \
--default-authentication-plugin=mysql_native_password

echo "run mysql containers end============"
  • Redis 启动
shell
#!/bin/sh

echo "start redis images============"
docker run --restart=always \
-p 6379:6379 \
--name redis \
-v /root/vde/redis/conf/redis.conf:/etc/redis.conf \
-v /root/vde/redis/data:/data \
-d redis:6.2.6 redis-server /etc/redis.conf

echo "run redis containers end============"

Last updated: