Skip to content

Docker 是基于 Google 公司推出的 GoLang 语言开发的开源项目,基于 Linux 内核的 Cgroups 和 Namespace 以及 UnionFS 等技术,对进程进行封装隔离,属于操作系统层面的虚拟化技术

利用Docker技术,可以保证开发、测试、生产环境的一致性,避免环境差异导致环境的问题

图例

Docker 引擎

Docker引擎

Docker 组成

Docker组成

核心组件

  1. image 镜像 - 应用程序所需的环境打包为镜像
  2. container 容器 - 镜像运行后的实例
  3. 镜像仓库 - 存放镜像的仓库,作用好比Github
  4. Dockerfile - 将你部署项目的操作写成一个部署脚本,且该脚本还能构建出镜像文件

容器镜像

可以把 Docker 当成是 Maven 仓库,镜像可以理解成是 Maven 的依赖包,当需要使用某些镜像的时候如果本地没有,那么就会前往中央仓库下载,然后安装到本地,下次使用直接使用本地的镜像

拉取远程镜像命令,官方示例

sh
# 拉取
docker pull hello-world

# 运行
docker run hello-world

hello-world

一个镜像的名称是由两部分组成的,一个是repository,还有一个是tag,一般情况下约定repository就是镜像名称,tag作为版本,默认为latest,表示最新版本。所以指定版本运行的话使用下面命令,tag可以省略(默认最新版)

sh
docker pull 名称:版本

把镜像拉取到本地运行之后会自动创建容器,可以使用 ps 命令查看所有的 Docker 容器

sh
# 查看所有的容器
docker ps -a

# 查看运行中的容器
docker ps

如果是仅仅创建容器不进行启动的话,可以使用以下命令来进行创建容器

sh
docker create hello-world

一个镜像可以创建多个容器,每个容器都有一个随机的容器ID,后面是容器的创建时间以及当前的运行状态,最后一列是容器的名称,在创建容器时,名称如果没有指定会自动生成,可以在使用 run 命令时使用 --name 参数进行手动指定名称启动

sh
docker run --name=hello hello-world

开启/关闭容器

使用上面的 ps 命令可以查看到全部的容器,可以使用 docker start 和 docker stop 命令来开启和关闭容器

开启容器:

sh
docker start [容器名称/容器ID]

关闭容器:

sh
docker stop [容器名称/容器ID]

重启容器:

sh
 docker restart [容器名称/容器ID]

不可以直接使用镜像名称来进行开启或者关闭操作,需要使用容器名称或者容器ID

删除容器和镜像

删除容器:

sh
docker rm [容器名称/容器ID]

不可使删除正在运行中的容器,需要先停止容器,然后再删除容器,如果希望在容器停止运行后自动删除可以使用以下命令

sh
docker run --rm 镜像名称

所有的数据都是保存在容器中,所以删除容器的时候,容器中的数据也会被删除。

删除镜像:

sh
docker rmi 镜像名称

可以使用以下命令来查看全部的镜像

sh
docker images

容器的网络管理

Docker 在安装的时候会在主机创建三个网络,使用以下命令查看

sh
docker network ls

默认情况下有bridgehostnone这三种网络类型

  • none:相当于内部容器进行断网,这让容器是无法连接到互联网的
  • bridge(默认):容器默认使用类型,这是桥接网络
  • host:当容器使用host网络时,会共享宿主主机的网络,并且跟宿主主机的网络配置一样(使用这种模式也就不需要做什么端口映射等操作)

创建容器的时候使用 network 进行指定使用的网络

sh
docker run --network=[网络名] [镜像]

自定义网络

除了默认的三种网络之外,还可以自定义网络,让容器连接到这个网络

Docker默认提供三种网络驱动:bridgeoverlaymacvlan,不同的驱动对应着不同的网络设备驱动,实现的功能也不一样

使用 network 创建自定义网络(创建容器时添加 --network=[网络名] 使用新规则)

sh
docker network create --driver bridge test

使用network inspect命令来查看网络配置信息

sh
docker network inspect test

不同的网络是相互隔离的,无法进行通信

可以在创建的时候就指定相同的自定义网络,如果容器已经创建完成可以使用 network connect 进行连接到指定的网络

sh
docker network connect [网络名] [容器名称/容器ID]

容器之间的通信

我们可以直接通过容器的IP地址在容器间进行通信,只要保证两个容器处于同一个网络下即可,非常方便

缺点:在大部分情况下,容器部署之后的IP地址是自动分配的(也可以使用--ip来手动指定)

Docker DNS服务器

Docker 提供了类似于DNS服务器的套路,域名解析IP,这里的域名就是指容器的名称

sh
docker run -it --name=netTest-3 ubuntu

image-20240527183352179

使用这种方式只有在自定义网络的情况下才能使用

还有一种方式是让两个容器共享同一个网络,两个容器共享同一个IP地址,这样也可以达到通信的结果

sh
docker run -it --name=netTest-2 --network=container:[共享的容器] ubuntu-net

容器持久化存储

在容器中所作的操作都是保存到最顶层的,这样不好处就是一旦删除容器在进行重装的时候会非常难受,在这种情况下可以使用数据卷(Data Volume)

在宿主机中创建一个文件夹,其中创建一个文件写入些许内容,然后保存,接着就可以将宿主主机上的目录或文件挂载到容器的某个目录上

sh
docker run -it -v ~/docker/resource:/root/docker/resource ubuntu-vim

在使用-v参数时不指定宿主主机上的目录进行挂载的话,那么就由Docker来自动创建一个目录,并且会将容器中对应路径下的内容拷贝到这个自动创建的目录中,最后挂在到容器中,同时这个数据卷就被 Docker 进行管理了,即使是删除对应的容器数据卷也不会消失

sh
# 查看被Docker管理的数据卷
docker volume ls

# 删除对应的数据卷
docker volume rm [VOLUME NAME]

# 查看数据卷信息
docker volume inspect [VOLUME NAME]

容器数据之间的数据共享

通过上面持久化的形式进行数据共享,还有一种方式是通过创建一个容器,这个容器上挂载了主机上的文件,然后其他容器创建的时候通过 --volumes-from 指定即可

sh
# 创建一个挂载容器
docker run -it -v ~/docker/resource:/root/docker/resource --name=test ubuntu-vim
# 创建一个容器指定 test 容器目录
docker run -it --volumes-from test ubuntu-vim

也可以通过 volume 创建一个数据卷然后再创建的时候指定一下挂载目录,两个容器的数据卷相同的情况下,也可以达到数据共享的实现

sh
docker volume create [源名称]

image-20240527213753706

唯一不好的一点就是被挂载的目录是固定的,并不能自定义。然后进行挂载

sh
docker run -it -v test:/root/test --name=t1 ubuntu-vim
docker run -it -v test:/root/test --name=t2 ubuntu-vim

以上的两个容器的 /root/test 中数据会被相互共享

常用命令

日志

打印容器中的日志信息(如果想要持续打印加入 -f 属性)

sh
docker logs [容器名称/容器ID]

进入容器

进入正在运行中的容器内,如果使用 exit 或者 Ctrl + C 退出,容器会停止运行,如果想退出容器但不想容器停止,则按住 Ctrl + P + Q 退出

sh
docker attach [容器名称/容器ID]

也可以使用 exec 命令在容器中启动一个新的终端或是在容器中执行命令

sh
docker exec -it [容器名称/容器ID] bash

也可以仅仅在容器中执行一条命令

sh
docker exec [容器名称/容器ID] [指令]

终止容器

之前使用 stop 进行终止容器,这种方式是等待善后工作完成在进行终止,下面是强制停止容器

sh
docker kill [容器名称/容器ID]

暂停运行

容器暂时停止运行单不是真的停止了运行,在暂停运行后无法处理工作

sh
# 暂停运行
docker pause [容器名称/容器ID]

# 继续运行
docker unpause [容器名称/容器ID]

监控

sh
docker status

使用以上命令可以查看所有容器所占用的资源,可以使用以下命令查看某个容器中的进程

sh
docker top [容器名称/容器ID]

资源管理

内存

对于容器所使用的硬件资源是可以手动指定的

sh
docker run -m [内存限制] --memory-swap=[内存和交换分区总共的内存限制] [镜像名称]

其中-m参数是对容器的物理内存的使用限制,而--memory-swap是对内存和交换分区总和的限制,它们默认都是-1,也就是说没有任何的限制(如果在一开始仅指定-m参数,那么交换内存的限制与其保持一致,内存+交换等于-m的两倍大小)默认情况下跟宿主主机一样

CPU

默认情况下所有容器使用的CPU资源数是一样的,可以通过调整不同的容器的CPU权重(默认为1024)来按需分配资源,这里需要使用到-c选项,也可以输入全名--cpu-share

sh
docker run -c [分配数] [镜像]

限制在指定的CPU上运行 --cpuset-cpus ,使用多个以逗号分开

sh
docker run -it --cpuset-cpus=[CPU数] [镜像]

还有其余一些 --cpus--cpu-period--cpu-quota 可以百度详细信息

系统镜像

所谓的镜像其实就是一个封装的操作系统,这个系统中有很多依赖如:JDK、MYSQL、Nginx等等内容,没有封装过的基本的系统镜像被称为 Base 镜像,项目就是基于 Base 镜像进行打包的(也可以不要,如之前的 hello-world 就是基于当前的系统执行)

相较于正常的系统,这些镜像去掉了系统内核,使用的内核是宿主机的内核,也就是说,你当前的系统内核版本是多少,这些系统镜像的内核版本就是多少(可以使用 uname -r 查看内核版本)

这些 Base 镜像基本都是由 Linux 的发行版本进行封装,如:Ubuntu、CentOS、Kali等

下载 Base 镜像:

sh
docker pull ubuntu

启动镜像

Docker 会对没有操作的容器进行关闭,如果直接启动系统镜像会瞬间关闭(因为它没有事情做,开着也是浪费资源)对于这种情况可以加上 -it 属性

sh
docker run -it ubuntu

-i 表示在容器上打开一个标准的输入接口,-t 表示分配一个伪tty设备,可以支持终端登录,一般这两个是一起使用,否则base容器启动后就自动停止了,退出时输入 exit

当想重新启动的时候可以使用 start 进行重新启动,一定要加上 -i 否则无法进入镜像终端

sh
docker start -i [容器名称/容器ID]

软件结构

在 Base 镜像中安装的软件是分层的,Base 镜像为底安装的软件会一层一层的叠加上去,封顶为容器层。如果不同层中存在相同位置的文件,那么上层的会覆盖掉下层的文件,最终看到的是一个叠加之后的文件系统。当需要修改容器中的文件时,不会直接对镜像中的文件镜像进行修改,修改的是容器层,不会影响到下面的镜像

结构示例
容器层
......
软件二(MySql)
软件一(Nginx)
Base 地基

分层的好处:拿 Base 镜像举例,现在有两个镜像,底层的 Base 镜像是同一个,那么不采用分层结构的话这个 Base 镜像会被安装两遍造成无意义的资源浪费。但是采用分层的话 Base 镜像只需要安装一遍即可,这个 Base 镜像会被共享使用

既然是被共享使用,那这个镜像的文件就不能够被改变,否则就失去了通用性,想要进行改变而不失去通用性就需要使用封顶的容器层

可以使用 history 命令查看镜像层等

sh
docker history 镜像名称

构建镜像

首先拉取一下 Base 镜像

shell
docker pull ubuntu

commit(不推荐)

运行镜像

sh
docker run -it ubuntu

更新一下 apt

sh
apt update

安装一下JDK

sh
apt install openjdk-17-jdk

先退出 Base 系统镜像,然后使用 commit 构建

sh
docker commit [容器名称/容器ID] 新的镜像名称

按照上面的格式进行一个构建操作

image-20240525201044028

优点:自定义程度很高

缺点:构建效率太低,作为使用者来说,构建情况不透明有安全风险

dockerfile

跟上面不同的是这种构建方式是采用命令的形式进行构建,类似于 win 的批处理

先创建一个名为 Dockerfile 文件(名称不可随意更改)

sh
vim Dockerfile

输入格式:

dockerfile
FROM <基础镜像>
RUN <执行的命令>

# 案例
FROM ubuntu
RUN apt update
# 因为在构建过程中是无法干预的所以要加入-y 确认安装
RUN apt install -y openjdk-17-jdk

Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大

输入完命令之后,保存退出,然后输入以下命令构建

sh
docker build -t <镜像名> <构建文件的目>

image-20240525211205956

-t:设置镜像名称

不对其进行指定的话默认查找的是 Dockerfile 这个文件,可以使用 -f 或者 --file 参数来指定 Dockerfile 的路径和文件名,如:

sh
docker build -f mycustom-Dockerfile .

在这个命令中,mycustom-Dockerfile 是自定义的 Dockerfile 文件名,. 指示 Docker 在当前目录下寻找该文件

常用关键字

dockerfile
FROM mysql:5.6						#FROM指令定义基础镜像,FROM指令必须是第一个指令
MAINTAINER Benjamin <qq.com>		#MAINTAINER指令指定维护者信息,此指令为可选
ENV JAVA_HOMR="/usr/local/java"		#ENV定义环境变量,后续指令中可以通过$JAVA_HOME来引用环境变量,ENV定义的变量可以在容器中使用
ARG name="xiaolming"				#ARG也是定义环境变量,与ENV不同的是,ARG定义的环境变量只能在Dockerfile中使用,在容器中不存在
RUN yum install -y httpd			#RUN指令用于运行一个命令,如使用yum安装httpd服务
COPY /file*.txt /file/				#COPY用于复制宿主机文件到容器指定目录,COPY支持通配符
ADD	hello-world /app/				#ADD也是用于复制宿主机文件到容器指定目录,ADD也支持通配符,与COPY不同的是,ADD会自动解压文件
WORKDIR /app/						#WORKDIR指令用于用于设置当前工作目录
USER root							#USER指令用于切换用户
VOLUME	/data						#VOLUME指令用于定义匿名卷,表示将容器内的/data目录挂载为匿名卷到宿主机上
EXPOSE	80 8089/udp					#EXPOSE用于声明容器端口,如果未指定协议,则默认为TCP,仅仅是声明,并没有绑定宿主机端口映射
CMD ["nginx", "-g", "daemon off;"]	#CMD指令的作用是指定容器主进程的默认启动指令
ENTRYPOINT ["/usr/sbin/nginx","-g","daemon off;"]	#ENTRYPOINT指令也是指定容器主进程的默认启动指令,但与CMD有区别。

Docker Hub

Docker Hub 是云端存储镜像可以把创建的镜像推送到远程仓库

网址:https://hub.docker.com

上传

上传之前在 Hub 进行创建仓库(不做赘述),在系统中使用 docker login 进行登录

进行镜像重命名(这回复制一个新的镜像但是名称和版本不同),如果命名不一致的话它会新建一个新的仓库

sh
docker tag test_java:latest ximuliunian/test_java:1.0

image-20240527091230898

上传到远程仓库(名称要和远程仓库名称一样)

sh
docker push [名称]:[版本]

image-20240527091817520

完事之后可以使用 search 命令进行搜搜镜像

sh
docker search ximuliunian

查看 ximuliunian 账户里面的所有公共仓库

SpringBoot应用程序镜像

首先得有一个可以运行的 SpringBoot 项目,在项目根目录创建 Dockerfile 文件,并配置(IDEA)一下Docke,采用TCP套接字,然后在Docke服务器更改一下 service 文件

sh
vim /etc/systemd/system/multi-user.target.wants/docker.service

添加标红内容 -H tcp://0.0.0.0

image-20240527102647867

重启Docker服务,如果是云服务器,记得开启2375 TCP连接端口

sh
systemctl daemon-reload && systemctl restart docker.service

image-20240527103215911

对项目进行打包,然后编写 Dockerfile 文件

dockerfile
# Base 镜像
FROM ubuntu
# 作者
LABEL authors="曦暮流年"
# 安装JDK环境
RUN apt update && apt install -y openjdk-17-jdk
# 复制jar包
COPY target/weiliao-0.0.1.jar app.jar
# 启动命令,在容器启动的时候执行的命令
CMD java -jar app.jar

打包好的镜像会存放在服务器中,可以使用以下命令运行

sh
docker run -d -p 8080:8080 [镜像]

-d 代表在后台运行

-p 表示绑定端口,前面为 宿主机端口:容器端口