[07] Docker基础(中)
1. Docker 镜像#
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。
1.1 UnionFS 联合文件系统#
Union 文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(Unite Several Directories into A Single Virtual FileSystem)。Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
1.2 Docker 镜像加载原理#
Docker 的镜像实际上由一层一层的文件系统组成,这种层级的文件系统就是 UnionFS。
bootfs(boot file system)主要包含 bootLoader 和 Kernel,bootLoader 主要是引导加载 Kernel。Linux 刚启动时会加载 bootfs 文件系统,在 Docker 镜像的最底层是 bootfs。这一层与我们典型的 Linux/Unix 系统是一样的,包含 boot 加载器和内核。当 boot 加载完成之后整个内核就都在内存中了,此时内存的使用权已由 bootfs 转交给内核,此时系统也会卸载 bootfs。
rootfs(root file system)在 bootfs 之上,包含的就是典型 Linux 系统中的 /dev、/proc、/bin、/etc 等标准目录和文件。rootfs 就是各种不同的操作系统发行版,比如 Ubuntu,CentOS 等等。
Q1:平时我们安装进虚拟机的 CentOS 都是好几个 G,为什么 Docker 这里才 215M?
对于一个精简的 OS,rootfs 可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用宿主机的 Kernel,自己只需要提供 rootfs 就行了。由此可见对于不同的 Linux 发行版,bootfs 基本是一致的,只是 rootfs 会有差别,因此不同的发行版可以共用 bootfs。
Q2:为什么 tomcat 这么大?→ “分层”的镜像
在下载的过程中我们可以看到 Docker 的镜像好像是在一层一层的下载 ...
Q3:为什么 Docker 镜像要采用这种分层结构呢?
最大的一个好处就是共享资源。比如:有多个镜像都从相同的 base 镜像构建而来,那么宿主机只需在磁盘上保存一份 base 镜像,同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了,而且镜像的每一层都可以被共享!
1.3 commit 操作#
Docker 方式启动 tomcat,访问首页出现 404 错误。
docker run -it -p 8888:8080 tomcat # 分别代表 <docker容器端口>:<tomcat端口>
docker run -it -P tomcat # 随机分配 docker 容器端口,但是 tomcat 会是默认的 8080
通过 netstat -anp | more
查看端口占用情况,docker 的确在监听 8888 端口。
进入正在运行的容器,一探究竟:
这时候再访问,成功了。
但这么做,只在当前有效。容器停止后,下一次再使用镜像生成新的容器时,这个错误还是存在,即实际上问题的根源是在生成容器的镜像上,只有将镜像修改了,再生成的容器才不会再出现这个问题。
【容器 → 镜像】由镜像可以生成容器实例,也可以根据当前正在运行的容器实例的实际状况又生成新的镜像(有点像反射)。
通过 docker commit
提交容器副本使之成为一个新的镜像,命令格式如下:
docker commit -m="提交的描述信息" -a="作者" 容器ID 要创建的目标镜像名:[标签名]
上一小节修改过 webapps 的 tomcat 运行实例算是一个标准的 tomcat 容器了,故以它为模板 commit 一个新的 tomcat 镜像 nuist/mytomcat。这样一来,以后再创建 tomcat 容器时,使用我们自己生成的镜像即可(它跟阿里云拉下来的没什么差别,只是保存了我们之前对容器做的修改)。
docker commit -a="ljq" -m="tomcat update the folder-webapps" c9e9af3087aa nuist/mytomcat:1.1
测试:
镜像构建时,会一层层构建,前一层是后一层的基础,每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层(比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除,在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉)。
顺便 review 下“守护方式”启动容器:
1.4 Re: Docker#
Docker 镜像都是只读的。当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间,因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。前面讲过镜像使用的是分层存储,容器也是如此,每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为「容器存储层」。
容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡,因此,任何保存于容器存储层的信息都会随容器删除而丢失。// => 引入数据卷
a. Image Definition#
镜像(Image)就是一堆只读层(Read-Only Layer)的统一视角。
从左边我们看到了多个只读层,它们重叠在一起。除了最下面一层,其它层都会有一个指针指向下一层。这些层是 Docker 内部的实现细节,并且能够在主机(运行 Docker 的机器)的文件系统上访问到。统一文件系统(union file system)技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角,这样就隐藏了多层的存在,在用户的角度看来,只存在一个文件系统。我们可以在上图的右边看到这个视角的形式。
root@liujiaqi /]# tree -L 1 /var/lib/docker/
/var/lib/docker/
├── buildkit
├── containers
├── image
├── network
├── overlay2
├── plugins
├── runtimes
├── swarm
├── tmp
├── trust
└── volumes
11 directories, 0 files
b. Container Definition#
容器(Container)的定义和镜像(Image)几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。
细心的读者可能会发现,容器的定义并没有提及容器是否在运行,没错,这是故意的。正是这个发现帮助我理解了很多困惑。
要点:容器 = 镜像 + 读写层,并且容器的定义并没有提及是否要运行容器。
c. Running Container Definition#
一个运行态容器(Running Container)被定义为一个可读写的统一文件系统加上隔离的进程空间和包含其中的进程。下面这张图片展示了一个运行中的容器。
正是“文件系统隔离技术”使得 Docker 成为了一个前途无量的技术。一个容器中的进程可能会对文件进行修改、删除、创建,这些改变都将作用于可读写层(Read-Write Layer)。下面这张图展示了这个行为:
我们可以通过运行 docker run centos touch liujiaqi.txt
来验证我们上面所说的;即便这个 centos 容器不再运行,我们依旧能够在宿主机的文件系统上找到这个新文件。
d. Image Layer Definition#
为了将零星的数据整合起来,我们提出了镜像层(Image Layer)这个概念。下面的这张图描述了一个镜像层,通过图片我们能够发现一个层并不仅仅包含文件系统的改变,它还能包含了其他重要信息。
元数据(metadata)就是关于这个层的额外信息,它不仅能够让 Docker 获取运行和构建时的信息,还包括父层的层次信息。需要注意,只读层(Read-Only Layer)和读写层(Read-Write Layer)都包含元数据。
除此之外,每一层都包括了一个指向父层的指针。如果一个层没有这个指针,说明它处于最底层。
「Metadata Location」我发现在我自己的主机上,镜像层(Image Layer)的元数据被保存在名为“json”的文件中,比如说:
一个容器的元数据好像是被分成了很多文件,但或多或少能够在 /var/lib/docker/containers/<id>
目录下找到,<id>
就是一个可读层的 id。这个目录下的文件大多是运行时的数据,比如说网络、日志等等。
e. Tying It All Together!#
docker create <Image-ID>
为指定的镜像(Image)添加了一个可读写层(Read-Write Layer),构成了一个新的容器。注意,这个容器并没有运行。
docker start <Container-ID>
为容器文件系统创建了一个进程隔离空间。注意,每一个容器只能够有一个进程隔离空间。
docker run <Image-ID>
先是利用镜像创建了一个容器,然后运行这个容器。这个命令非常地方便,并且隐藏了两个命令的细节(类似于 git pull
命令,它就是 git fetch
和 git merge
两个命令的组合;同样地,docker run
就是 docker create
和 docker start
两个命令的组合)。
docker ps
会列出所有运行中的容器。这隐藏了非运行态容器的存在,如果想要找出这些容器,我们需要使用下面这个命令。
docker ps -a
会列出所有的容器,不管是运行的,还是停止的。
docker images
会列出了所有顶层(Top-Level)镜像。实际上,在这里我们没有办法区分一个镜像(Image)和一个只读层(Read-Only Layer),所以我们提出了 Top-Level 镜像。只有创建容器时使用的镜像或者是直接 pull 下来的镜像能被称为顶层(Top-Level)镜像,并且每一个顶层镜像下面都隐藏了多个镜像层。
docker images -a
列出了所有的镜像,也可以说是列出了所有的只读层。如果你想要查看某一个 Image-ID 下的所有层,可以使用 docker history
来查看。
docker stop <Container-ID>
会向运行中的容器发送一个 SIGTERM 的信号,然后停止所有的进程。
docker kill <Container-ID>
向所有运行在容器中的进程发送了一个不友好的 SIGKILL 信号。
stop 和 kill 命令会发送 UNIX 的信号给运行中的进程,docker pause <Container-ID>
则不一样,它利用了 cgroups 的特性将运行中的进程空间暂停。
docker rm <Container-ID>
会移除构成容器的可读写层(Read-Write Layer)。注意,这个命令只能对非运行态容器执行。
docker rmi <Image-ID>
会移除构成镜像的一个只读层。你只能够使用 docker rmi
来移除最顶层(Top Level Layer,也可以说是镜像),你也可以使用 -f
参数来强制删除中间的只读层(Read-Only Layer)。
docker commit <Container-ID>
将容器的可读写层转换为一个只读层,这样就把一个容器转换成了不可变的镜像。
docker build
命令非常有趣,它会反复的执行多个命令。从下图可以看到,build 命令根据 Dockerfile 文件中的 FROM 指令获取到镜像,然后重复地 ① run(create & start)、② 修改、③ commit。在循环中的每一步都会生成一个新的层,因此许多新的层会被创建。
docker exec <Running-Container-ID>
会在运行中的容器执行一个新进程。
docker inspect <Container-ID> | <Image-ID>
会提取出容器或者镜像最顶层的元数据。
docker save <Image-ID>
会创建一个镜像的压缩文件,这个文件能够在另外一个主机的 Docker 上使用。和 export 命令不同,这个命令为每一个层都保存了它们的元数据。这个命令只能对镜像生效。
docker export <Container-ID>
会创建一个 tar 文件,并且移除了元数据和不必要的层,将多个层整合成了一个层,只保存了当前统一视角看到的内容(expoxt 后的容器再 import 到 Docker 中,通过 docker images -tree
命令只能看到一个镜像;而 save 后的镜像则不同,它能够看到这个镜像的历史镜像)。
docker history <Image-ID>
会递归地输出指定镜像的历史镜像。
2. Docker 数据卷#
2.1 什么是数据卷#
Docker 容器产生的数据,如果不通过 docker commit
生成新的镜像,使得数据做为镜像的一部分保存下来,那么当容器删除后,数据自然也就没有了。
当我们在使用 Docker 容器的时候,会产生一系列的数据文件,这些数据文件在我们删除 Docker 容器时是会消失的,但是其中产生的部分内容我们是希望能够把它给保存起来另作用途的,Docker 将应用与运行环境打包成容器发布,我们希望在运行过程钟产生的部分数据是可以持久化的的,而且容器之间我们希望能够实现数据共享。
作用:
- 容器的持久化;
- 容器间继承+共享数据;
- 容器和宿主机共享数据
“卷”就是目录或文件,存在于一个或多个容器中,由 Docker 挂载到容器,但不属于联合文件系统,因此能够绕过 Union File System 提供一些用于持续存储或共享数据的特性:
- 数据卷可以在容器之间共享或重用数据;
- 数据卷中的更改可以立即生效;
- 数据卷中的更改不会包含在镜像的更新中;
- 数据卷默认会一直存在,即使容器被删除;
- 数据卷的生命周期一直持续到没有容器使用它为止。
“卷”的设计目的就是数据的持久化,完全独立于容器的生存周期,因此 Docker 不会在容器删除时删除其挂载的数据卷。
Docker 镜像由多个只读层叠加而成,启动容器时,Docker 会加载只读镜像层并在镜像栈顶部加一个读写层:
容器中的管理数据主要有两种方式:
- 数据卷(Data Volumes):容器内数据直接映射到本地主机环境;
- 数据卷容器(Data Volume Containers):使用特定容器维护数据卷。
有关命令:
# 查看所有数据卷
docker volume ls
# 查看数据卷细节
docker volume inspect <volume-name>
# 清理无用数据卷
docker volume prune
# 清理指定数据卷
docker volume rm <volume-name>
# 创建数据卷
docker volume create <volume-name>
2.2 原始拷贝方式#
最原始的 copy 方式,这个也是管理数据的方式,但是基本不会用到。docker cp
用于容器与主机之间的数据拷贝。
# 宿主机文件复制到容器内
docker cp [OPTIONS] SRC_PATH CONTAINER:DEST_PATH
# 容器内文件复制到宿主机
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH
2.3 数据卷方式#
数据卷(Data Volumes)是一个可供一个或多个容器使用的特殊目录,它将主机操作系统目录直接映射进容器。
注意事项:
- 挂载数据卷,最好是通过 run 而非 create/start 创建启动容器,create/start 命令创建启动容器后,再挂载数据卷相当麻烦,要修改很多配置文件,但并非不可以;
- Docker 官网推荐尽量进行目录挂载,不要进行文件挂载。
有 3 种数据卷类型:
- bind mounts:直接在宿主机的文件系统中,但是容器可以访问;
- 命名的数据卷:磁盘上 Docker 管理的数据卷,起了卷名;
- 匿名的数据卷:磁盘上 Docker 管理的数据卷,没给卷名。
数据卷其实都在(如果没有网络文件系统等情况下)宿主机文件系统里面的,只是第一种是在宿主机内的特定目录下,而后两种则在 Docker 管理的目录下,这个目录一般是 /var/lib/docker/volumes/
推荐大家使用“宿主机数据卷”方式持久化数据。
a. bind mounts#
容器内的数据被存放到宿主机文件系统的任意位置,甚至存放到一些重要的系统目录或文件中。除了 Docker 之外的进程也可以任意对他们进行修改。
当使用 bind mounts 时,宿主机的目录或文件被挂载到容器中。容器将按照挂载目录或文件的绝对路径来使用或修改宿主机的数据。宿主机中的目录或文件不需要预先存在,在需要的使用会自动创建。
使用 bind mounts 在性能上是非常好的,但这依赖于宿主机有一个目录妥善结构化的文件系统。
使用 bind mounts 的容器可以在通过容器内部的进程对主机文件系统进行修改,包括创建,修改和删除重要的系统文件和目录,这个功能虽然很强大,但显然也会造成安全方面的影响,包括影响到宿主机上 Docker 以外的进程。
数据覆盖问题:
- 如果挂载一个空的数据卷到容器中的一个非空目录中,那么这个目录下的文件会被复制到数据卷中;
- 如果挂载一个非空的数据卷到容器中的一个目录中,那么容器中的目录会显示数据卷中的数据。如果原来容器中的目录有数据,那么原始数据会被隐藏掉。
命令格式(-v 即 volume):
docker run -it -v </宿主机绝对路径目录:/容器内目录>[:权限] <镜像名>
# 通过 -v 容器内路径:ro rw 改变读写权限
# ro:readonly 只读
# rw:readwrite 可读可写
# docker run -it -v /宿主机绝对路径目录:/容器内目录:ro 镜像名
# docker run -it -v /宿主机绝对路径目录:/容器内目录:rw 镜像名
# 例如:
# docker run -d -P --name nginx05 -v lagouedu1:/etc/nginx:ro nginx
# docker run -d -P --name nginx05 -v lagouedu2:/etc/nginx:rw nginx
# ro即readonly,一旦添加这个选项,容器只能读宿主机往目录里添的内容,自己不能做增删改操作。
Dockers 自动创建对应的文件夹:
查看数据卷是否挂载成功:
容器和宿主机之间共享数据:
容器停止退出后,主机修改后数据是否同步?同步!
开发环境中推荐各位小伙伴为挂载目录授最高权限 777;生产环境需要查看官网文档,结合实际生产环境进行授权。
DockerFile 添加
[Java] Hello.java → Hello.class
[Docker] Images → DockerFile
- 根目录下新建 mydocker 文件夹并进入
- 可在 DockerFile 中使用
VOLUME
指令来给镜像添加一个或多个数据卷(出于可移植和分享的考虑,用-v 主机目录:容器目录
这种方法不能够直接在 DockerFile 中实现。由于宿主机目录是依赖于特定宿主机的,并不能够保证在所有的宿主机上都存在这样的特定目录)。 - 构建 DockerFile
# volume test FROM centos VOLUME ["/dataVolumeContainer1","/dataVolumeContainer2"] CMD echo "finished, success!" CMD /bin/bash
- build 后生成镜像,通过该镜像 run 容器
docker build -f /mydocker/dockerFile -t mycentos . docker run -it mycentos
通过上述步骤,容器内的卷目录地址已经知道,那么对应的主机目录地址在哪呢?→ 通过 docker inspect <Container-ID>
查看:
若 Docker 挂载主机目录,访问时出现 cannot open directory .: Permission denied
,解决办法是在挂载目录后多加一个 --privileged=true
参数。
b. Volume#
# 指定数据卷名为 nginx-data (如果该数据卷不存在,会自动创建)
docker run -itd --name nginx -p 80:80 -v nginx-data:/etc/nginx nginx:1.19.3-alpine
# 查看挂载的数据卷名称
docker volume ls
# 查看nginx-data所在的宿主机目录
docker volume inspect nginx-data
# 删除容器
docker rm $(docker stop $(docker ps -aq))
# 查看挂载数据是否还存在,通过查看数据,发现删除容器后,宿主机中的数据还存在
ls
####################################################################################
# 匿名,仅指定容器内目录
docker run -itd --name nginx2 -p 80:80 -v /etc/nginx nginx:1.19.3-alpine
# 查看容器信息中的挂载信息
docker inspect nginx2
# 查看数据卷所在的宿主机目录
docker volume inspect dbd07daa4e40148b11....
2.4 数据卷容器#
如果用户需要在多个容器之间共享一些持续更新的数据,最简单的方式是使用数据卷容器。数据卷容器也是一个容器,但是它的目的是专门用来提供数据卷供其他容器挂载。
某个命名的容器挂载数据卷,其它容器通过挂载这个“父”容器实现数据共享,则这个挂载数据卷的容器,就称之为“数据卷容器”。
测试容器间传递共享 --volumes-from <Container-Name>
。发现创建好的数据卷容器是处于停止运行的状态,因为使用该参数所挂载数据卷的容器自己并不需要保持在运行状态。
3. 常用服务安装#
3.1 MySQL#
# 1. 拉取mysql镜像到本地
docker pull mysql:tag (tag不加默认最新版本)
# 2. 运行mysql服务
# -- 没有暴露外部端口,外部不能连接
docker run --name mysqltest -e MYSQL_ROOT_PASSWORD=root -d mysql:tag
# -- 暴露外部端口,外部可以能连接
docker run --name mysqltest -e MYSQL_ROOT_PASSWORD=root -p 3306:3306 -d mysql:tag
# 3. 进入mysql容器
docker exec -it 容器名称|容器id bash
# 4. 外部查看mysql日志
docker logs 容器名称|容器id
# 5. 将容器数据位置与宿主机位置挂载保证数据安全
docker run -d --name mysqltest -p 3306:3306
-v /root/mysql/data:/var/lib/mysql
-v /root/mysql/conf.d:/etc/mysql/conf.d
-e MYSQL_ROOT_PASSWORD=<pwd>
mysql:tag
# 6. 将mysql数据库备份为sql文件
# -- 导出全部数据
docker exec mysql|容器id sh -c 'exec mysqldump --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD"' > /root/all-databases.sql
# -- 导出指定库数据
docker exec mysql|容器id sh -c 'exec mysqldump --databases 库表 -uroot -p"$MYSQL_ROOT_PASSWORD"' > /root/all-databases.sql
# --导出指定库数据不要数据
docker exec mysql|容器id sh -c 'exec mysqldump --no-data --databases 库表 -uroot -p"$MYSQL_ROOT_PASSWORD"' > /root/all-databases.sql
# 7. 执行sql文件到mysql中
docker exec -i mysql|容器id sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD"' < /root/xxx.sql
【注意】不能在一台 Docker 服务器上同时启动多个挂载到同一个数据卷的容器(参考锁表)。
【补充】mysql 配置文件的加载
执行“查找配置文件”的命令,查看 mysql 加载配置文件的顺序:
$ mysql --verbose --help|grep -A 1 'Default options'
Default options are read from the following files in the given order:
/etc/my.cnf /etc/mysql/my.cnf /usr/etc/my.cnf ~/.my.cnf
所以 mysql 是按优先级顺序, 依次使用如上配置文件。但如果配置文件中包含类似如下内容,说明配置文件使用的是 includedir 下以 .cnf
为后缀的文件。
$ cat /etc/my.cnf
---------------------------
...
!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/
3.2 Tomcat#
# 1. 下载tomcat镜像
docker pull tomcat
# 2. 运行tomcat实例
docker run -p 8080:8080 -d --name mytomcat1 tomcat
# 3. 进入tomcat容器
docker exec -it mytomcat1 /bin/bash
# 4. 将webapps目录挂载在外部
docker run -d -p 8080:8080 -v /root/webapps:/usr/local/tomcat/webapps --name mytomcat2 tomcat
# 5. 将配置目录挂载到外部
docker run -d -p 8080:8080 -v tomcatConf:/usr/local/tomcat/conf --name mytomcat3 tomcat
有些配置不是重启容器就能生效的,如下这种情况就得重新创建容器:
3.3 ELK#
4. Docker Registry#
4.1 DockerHub#
4.2 Registry#
4.3 Harbor#
5. 微服务 Docker 打包#
5.1 手动上传方式#
a. Docker Plugin#
(1)打开 Docker 远程连接
vi /usr/lib/systemd/system/docker.service
# 允许来自任意IP的客户端连接
[DEL] ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
[ADD] ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock
(2)重载并重启 Docker:systemctl daemon-reload && systemctl restart docker
(3)IDEA → Setting#Plugins → 搜索 "Docker" 插件,安装后重启 IDEA;
(4)创建 learn-docker 微服务,resource 目录下新建目录 docker 以及文件 Dockerfile;
(5)pom.xml 中引入插件 docker-maven-plugin
// TODO
b. Deployment#
- 关联部署服务器;
- 新建目录(上下文环境),在其中创建 Dockerfile 文件,借助 IDEA 提示功能完成编写;
- 将打包好的微服务的 jar 也拖拽到新建目录中;
- 手动 docker build,然后 run。
5.2 Jib 构建#
a. Jib 说明#
Jib 是 Google 开发的可以直接构建 Java 应用的 Docker 和 OCI 镜像的类库,以 Maven 和 Gradle 插件形式提供。
Jib 带来的是,它允许您通过简单地将插件添加到您选择的构建工具(Maven 或 Gradle)来创建容器,没有额外的文件,只需几行配置,它处理将应用程序打包到容器映像的所有步骤。
Jib 是来自 Google 的开源 Java 容器,它允许 Java 开发人员使用他们所知道的 Java 工具构建容器,它不需要您编写 Dockerfile 或安装了 Docker,它直接集成到 Maven 和 Gradle 中。
和传统的插件区别:
(1)Docker 构建流程
在“传统”Java到Docker映像构建流程中,我们需要创建 Dockerfile 文件以及手动编译 Jar。
(2)Jib 构建流程
通过 Jib,Java 开发者可以使用他们熟悉的 Java 工具来构建容器,Jib 是一个快速而简单的容器镜像构建工具,它负责处理将应用程序打包到容器镜像中所需的所有步骤,它不需要你编写 Dockerfile 或安装 Docker,而且可以直接集成到 Maven 和 Gradle 中 —— 只需要将插件添加到构建中,就可以立即将 Java 应用程序容器化。
b. Jib 示例#
(0)设置 Horbor 用户权限
将 idea 的微服务推送到 Harbor,并且用 itcast 的用户,所有我们要设置我们的 itcast 用户是开发者。
(1)pom.xml 配置 Jib
在 properties 中配置 harbor 的共有配置
<properties>
<!--harbor 仓库地址-->
<docker.registry.url>itcastharbor.com</docker.registry.url>
<!--harbor 的项目名称-->
<docker.registry.name>library</docker.registry.name>
<!--harbor 账号-->
<docker.registry.username>itcast</docker.registry.username>
<!--harbor 密码-->
<docker.registry.password>Qwert123</docker.registry.password>
</properties>
(2)编译配置、插件配置 jib-maven-plugin
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>2.8.0</version>
<configuration>
<allowInsecureRegistries>true</allowInsecureRegistries>
<!--from节点用来设置镜像的基础镜像,相当于Docerkfile中的FROM关键字-->
<from>
<!--使用openjdk官方镜像,tag是:8-jdk-alpine,表示镜像的操作系统是alpine,装好了jdk8-->
<image>openjdk:8-jdk-alpine</image>
</from>
<to>
<!--镜像名称和tag,使用了mvn内置变量${project.version},表示当前工程的version-->
<image>${docker.registry.url}/${docker.registry.name}/${project.artifactId}:${project.version}
</image>
<tags>
<!--版本号-->
<tag>${project.version}</tag>
</tags>
<!--harbor的认证信息-->
<auth>
<username>${docker.registry.username}</username>
<password>${docker.registry.password}</password>
</auth>
</to>
<!--容器相关的属性-->
<container>
<jvmFlags>
<!--一些启动参数-->
<jvmFlag>-Djava.security.edg=file:/dev/./urandom</jvmFlag>
</jvmFlags>
<!--挂载volume的配置-->
<volumes>
<volume>/tmp</volume>
<volume>/logs</volume>
</volumes>
<ports>
<!--暴漏端口号-->
<port>8080</port>
</ports>
<!--微服务的启动类-->
<mainClass>com.heima.test.Application</mainClass>
<format>OCI</format>
<!--使用该参数将镜像的创建时间与系统时间对其-->
<creationTime>USE_CURRENT_TIMESTAMP</creationTime>
</container>
</configuration>
<executions>
<!--执行打包配置-->
<execution>
<id>jib-maven-plugin</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Docker maven plugin -->
</plugins>
</build>
(3)执行构建
在项目根目录执行 mvn clean compile jib:build
就可以了
我们看到已经推送成功了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?