三、DockerFile语法详解
DockerFile是用来定义Docker镜像构建过程的文本文件;
它包含一系列的指令和参数,去告诉("命令")Docker如何去制作这个镜像。
3.1 DockerFile的文件结构
一个基本的DockerFile通常包含以下几个部分:
1.基础镜像指令(FROM)
- 指定基于哪个已有的奖项构建新镜像。
- 每个DockerFile必须以FROM指令开始
- ps:
FORM ubuntu:20.4
- ps:
2.工作目录设置(WORKDIR,可选)
- 设置容器内部的工作目录
- ps:
WORKDIR /app
- ps:
3.复制文件(COPY或ADD,可选)
COPY将文件或目录从构建上下文复制到镜像中的指定路径ADD可以把压缩包解压,把解压后的目录拷贝到镜像
4.运行命令(RUN,可选)
- 在镜像构建过程中执行的命令,可以安装软件包,更新系统等。'
- ps:
RUN apt-get update && apt-get install -y nginx
- ps:
5.容器启动时执行命令(CMD或ENTRYPOINT,可选)
- 容器启动后执行的默认命令,可悲DockerFile中的CMD或ENTRYPOINT指令指定。
- ps:
["nginx","-g","daemon off;"]
- ps:
6.暴露端口(EXPOSE,可选)
- 声明容器运行时的服务将监听的端口
- ps:
EXPOSE 80
- ps:

bash
实战演练:
基于Dockerfile构建镜像,可以使用docker build命令,docker build 命令中使用 -f 可以指定具体的dockerfile文件。
[root@docker nginx]# vim dockerfile
FROM rockylinux:8.9
RUN yum install -y wget nginx
EXPOSE 80
ENTRYPOINT ["/usr/sbin/nginx","-g","daemon off;"]
解释:
1.FROM XXX --> 指定以哪个镜像为地基;
2.RUN XXX --> 运行什么命令
3.EXPOSE XX --> 声明容器运行时监听的端口为xxx
注意:只是一个声明,并不会自动打开或映射一个端口,还是需要在运行容器时加上参数 -p 去映射到主机的端口上去。
4.ENTRYPOINT XXX --> 容器启动时执行的命令,以下为示例讲解:
/usr/sbin/nginx --> Nginx可执行文件的路径
-g daemon off --> 用于服务以前台(非守护进程)模式运行
# 写完dockerfile之后进行build,如下图所示:
[root@docker nginx]# docker build -t mynginx:v1 -f dockerfile .
命令解释:
1. `docker build` --> 构建新的镜像
2. `-t` 是'tag'的缩写,指定一个名称+标签 --> mynginx:v1 --> 名称:标签
3. `-f` dockerfile 或者使用 --file --> 使用当前目录下的dockerfile(此处是小写,而不是默认的Dockerfile大写)
注意:如果遵循默认,则无需加 -f
4. `.` 代表Docker build 构建时所依赖的本地文件目录(可以看我下面放的示例图)
nginx]#


bash
Tips:查看容器的日志
]# docker logs xxxx
3.2 测试示例
3.2.1 示例-Tomacat服务基于Dockerfile文件做成镜像
bash
# 上传所需要的资源
[root@docker tomcat]# ls
apache-tomcat-8.0.26.tar.gz
jdk-8u45-linux-x64.rpm
[root@docker tomcat]# vim Dockerfile
FROM rockylinux:8.9
RUN yum install -y wget
COPY jdk-8u45-linux-x64.rpm /usr/local
ADD apache-tomcat-8.0.26.tar.gz /usr/local
RUN cd /usr/local && yum install -y jdk-8u45-linux-x64.rpm
RUN mv /usr/local/apache-tomcat-8.0.26 /usr/local/tomcat8
ENTRYPOINT /usr/local/tomcat8/bin/startup.sh && tail -F /usr/local/tomcat8/logs/catalina.o
ut
EXPOSE 8080
[root@docker tomcat]# docker build -t tomcat:v1 .
# 这里注意Dockerfile,如果Dockerfile就是首字母D大写,则不用 -f xxx
# 加入写的是dockerfile,则应该写成 -f dockerfile
[root@docker tomcat]# docker images
tomcat:v1
[root@docker tomcat]# docker run --name tomcat8 -d -p 8080 tomcat:v1
[root@docker tomcat]# docker ps | grep tomcat
... 32770->8080/tcp tomcat8
# 测试成功
[root@docker tomcat]# curl 172.25.254.10:32770
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Apache Tomcat/8.0.26</title>
<link href="favicon.ico" rel="icon" type="image/x-icon" />
<link href="favicon.ico" rel="shortcut icon" type="image/x-icon" />
<link href="tomcat.css" rel="stylesheet" type="text/css" />
</head>
3.2.2 示例-Go代码基于Dockerfile打包成镜像
生产环境中,代码是开发人员写的,运维大概率接触不到。
bash
[root@docker ~]# yum install -y go
[root@docker ~]# mkdir test-go
[root@docker ~]# cd test-go/
[root@docker test-go]# vim main.go
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func statusOKHandler(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "success~welcome to study"})
}
func versionHandler(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"version": "v1.1版本"})
}
func main() {
router := gin.New()
router.Use(gin.Recovery())
router.GET("/", statusOKHandler)
router.GET("/version", versionHandler)
router.Run(":8080")
}
[root@docker test-go]# go mod init test
[root@docker test-go]# go env -w GOPROXY=https://goproxy.cn,direct
[root@docker test-go]# go mod tidy
[root@docker test-go]# CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o k8s-demo main.go
[root@docker test-go]# ls
go.mod go.sum k8s-demo main.go
[root@docker test-go]# ./k8s-demo
bash
# 打开另外一个终端或者浏览器去测试
[root@docker ~]# hostname -i
172.25.254.10
[root@docker ~]# curl 172.25.254.10:8080
{"status":"success~welcome to study"}
[root@docker ~]# curl 172.25.254.10:8080/version
{"version":"v1.1版本"}
bash
# 上传资源包中的alpine的tar包,并上传至镜像
]# docker load -i alpine.tar
]# mkdir test-go
]# cd test-go
[root@docker test-go]# vim Dockerfile
FROM alpine:latest
WORKDIR /data/app
COPY k8s-demo /data/app/
CMD ["/bin/sh","-c","./k8s-demo"]
[root@docker test-go]# docker build -t go-k8s:v1 .
[root@docker test-go]# docker images |grep go
go-k8s:v1 110a5befdfbc 43.2MB 16.6MB
[root@docker test-go]# docker run -d --name go -p 30180:8080 go-k8s:v1
[root@docker test-go]# docker ps | grep go
0.0.0.0:30180->8080/tcp
# 进行测试
[root@docker test-go]# curl 172.25.254.10:30180
{"status":"success~welcome to study"}
[root@docker test-go]# curl 172.25.254.10:30180/version
{"version":"v1.1版本"}
四、Docker数据持久化及网络模式介绍
在Docker中,容器的文件系统是临时的。如果容器被删除或者重新创建,所有未保存的数据都会丢失。为了解决这个问题,我们可以使用以下两种方式来持久化数据。
- Bind Mounts:
- 将宿主机上的特定目录挂载到容器内。
- Volumes:
- 由Docker管理的特殊目录,用于存储数据。
4.1 Bind Mounts实现数据持久化-案例介绍
Bind Mounts 允许你将宿主机文件系统中的目录或文件挂载到容器内的某个目录。这种方式非常直接,适合在开发和测试环境中使用。
假设我们要运行一个简单的 Nginx 容器,并将宿主机上的 html 目录挂载到容器内的 /usr/share/nginx/html 目录。
bash
[root@docker ~]# mkdir -p /root/mynginx/html
[root@docker ~]# echo "Hello,My name is Kaser." > /root/mynginx/html/index.html
# 上传资源包
[root@docker ~]# docker load -i nginx1.26.tar.gz
Loaded image: nginx:1.26
[root@docker ~]# docker images | grep nginx:1.26
[root@docker ~]# docker run -d --name mynginx1 -v /root/mynginx/html:/usr/share/nginx/html -p 80 nginx:1.26
[root@docker ~]# docker ps
0.0.0.0:32771->80/tcp
[root@docker ~]# docker exec -it mynginx1 /bin/bash
root@69ca8ad3e28b:/# cd /usr/share/nginx/html/
root@69ca8ad3e28b:/usr/share/nginx/html# more index.html
Hello,My name is Kaser.
exit
[root@docker ~]# docker ps | grep mynginx
32771->80/tcp
# 测试
[root@docker ~]# curl 172.25.254.10:32771
Hello,My name is Kase
那么,什么是数据持久化呢?
如果不小心将这个mynginx1容器删去,如下:
bash
# 将mynginx1容器删去
[root@docker ~]# docker rm -f mynginx1
# 访问不了
[root@docker ~]# curl 172.25.254.10:32771
curl: (7) Failed to connect to 172.25.254.10 port 32771: Connection refused
# 但是如果重新运行一个容器
[root@docker ~]# docker run -d -p 80 --name mynginx1 -v /root/mynginx/html:/usr/share/nginx/html nginx:1.26
# 重新测试,发现一切都回来了,这就是 -v 的力量!
[root@docker ~]# docker ps | grep mynginx1
32792->80/tcp mynginx1
[root@docker ~]# curl 172.25.254.10:32792
Hello,My name is Kaser.
[root@docker ~]# docker exec -it mynginx1 /bin/bash
root@1142f60a9933:/# cat /usr/share/nginx/html/index.html
Hello,My name is Kaser.
root@1142f60a9933:/#
exit
4.2 volume实现数据持久化-案例介绍
Volumes 是 Docker 推荐的持久化数据方式,因为它们独立于宿主机的文件系统,并由 Docker 管理,适合生产环境使用。
bash
[root@docker ~]# docker volume create myvolume
myvolume
[root@docker ~]# docker run -d -p 81:80 --name mynginx2 -v myvolume:/usr/share/nginx/html nginx:1.26
[root@docker ~]# docker volume inspect myvolume
[
{
"CreatedAt": "2025-12-03T13:48:40+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/myvolume/_data",
"Name": "myvolume",
"Options": null,
"Scope": "local"
}
]
[root@docker ~]# cd /var/lib/docker/volumes/myvolume/_data
[root@docker _data]# echo "xxxxxxxxxxxxxxxxxxxxxx" > index.html
[root@docker _data]# cd
[root@docker ~]# docker exec -it mynginx2 /bin/bash
root@0cdd6dccbcf0:/# cd /usr/share/nginx/html/
root@0cdd6dccbcf0:/usr/share/nginx/html# cat index.html
xxxxxxxxxxxxxxxxxxxxxx
exit
[root@docker _data]# docker ps | grep mynginx2
81->80/tcp mynginx2
[root@docker _data]# curl 172.25.254.10:81
xxxxxxxxxxxxxxxxxxxxxx
删除之后,数据持久化如何体现的呢?
bash
[root@docker _data]# docker rm -f mynginx2
mynginx2
[root@docker _data]# cat index.html
xxxxxxxxxxxxxxxxxxxxxx
[root@docker _data]# docker run -d -p 81:80 --name mynginx2 -v myvolume:/usr/share/nginx/html nginx:1.26
[root@docker _data]# docker exec -it mynginx2 /bin/bash
root@2de43e250c40:/# cat /usr/share/nginx/html/index.html
xxxxxxxxxxxxxxxxxxxxxx
root@2de43e250c40:/#
exit
[root@docker _data]# curl 172.25.254.10:81
xxxxxxxxxxxxxxxxxxxxxx
4.3 Docker容器的网络模式

4.3.1 none模式
创建的容器没有网络地址,只有lo网卡;
bash
[root@docker ~]# docker run -itd --name net-none --net=none --privileged=true centos
[root@docker ~]# docker ps | grep centos
net-none
[root@docker ~]# docker exec -it net-none /bin/bash
[root@e9669e99fb14 /]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
[root@e9669e99fb14 /]# exit
4.3.2 container模式
Docker 网络的 container 模式是指在创建新容器时,**通过 --net=container 参数,指定新容器与已经存在的某个容器共享同一个网络。**如图所示,右侧黄色的新创建容器通过这个设置与左侧已经存在的容器共享网络空间。因此,新创建的容器不会拥有自己独立的 IP 地址,而是与左侧容器共享同一个 IP 地址(如 172.17.0.2)、端口范围等网络资源。两个容器的进程可以通过 lo 网卡设备进行通信。
这种共享网络的机制意味着新容器在网络层面上与指定的已有容器完全一致,两者可以互相访问彼此的服务和端口,就像它们在同一个本地网络中一样。

bash
# 上面的容器
[root@docker ~]# docker exec -it net-none /bin/bash
[root@e9669e99fb14 /]# ip a
inet 127.0.0.1/8 scope host lo
# 下面的容器
[root@docker ~]# docker run -itd --name net-container --net=container:net-none --privileged=true centos
# 进入容器内查看网络
[root@docker ~]# docker exec -it net-container /bin/bash
[root@e9669e99fb14 /]# ip a
inet 127.0.0.1/8 scope host lo
# 哇哦,两个容器的网络竟然是一样的
[root@docker ~]# docker ps
net-container
net-none

4.3.3 bridge桥接模式
bridge是docker默认的网络模式。
每个容器都连接到一个名为docker0的虚拟桥接网络,通过NAT(网络地址转换)与宿主机的网络通信。
bash
[root@docker ~]# docker run --name net-bridge -it --privileged=true centos
[root@df6c2fd83c56 /]# ip a
1: lo
inet 127.0.0.1/8
2: eth0
inet 172.17.0.2/16
4.3.4 host模式
- 容器与宿主机共享网络命名空间
- 容器将不会有自己的IP地址,而是使用宿主机的IP地址
- 对于高性能网络通信且无需网络隔离的应用,这种模式非常有用
bash
[root@docker ~]# docker run -itd --name net-host --net=host --privileged=true centos
[root@docker ~]# docker ps
net-host
net-container
net-none
[root@docker ~]# docker exec -it net-host /bin/bash
[root@docker /]# ip a
1: lo
inet 127.0.0.1/8
2: eth0
inet 172.25.254.10/24
3: docker0
inet 172.17.0.1/16
五、Docker资源配额
docker run 限制CPU、内存;
5.1 docker运行容器控制CPU使用
5.1.1 Docker运行容器CPU份额配置指南
在Docker中,可以通过 --cpu-shares 参数为容器设置 CPU 份额值。
使用以下命令可以查看相关帮助信息:
bash
~]# docker run --help | grep cpu-shares
-c, --cpu-shares int CPU shares (relative weight)
# 设置容器的CPU份额值(相对权重)
重要说明
1.cpu-shares 参数用于在创建容器时指定容器所使用的 CPU 份额值。
2.该参数值仅为一个弹性的加权值,并不能保证容器获得具体的 CPU 核心
3.默认情况下,每个 Docker 容器的 CPU 份额值为 1024。
4.在同一个 CPU 核心上同时运行多个容器时,CPU 加权值的效果才会显现出来。
5.1.2 案例分析
假设两个容器A和B,CPU份额分别为2048和1024,可能的情况如下:
-
情况 1:A 和 B 正常运行
- 如果容器 A 和 B 占用同一个 CPU,当 CPU 进行时间片分配时,容器 A 比容器 B 多一倍的机会获得 CPU 时间片。
-
情况 2:依赖其他容器的运行状态
- 分配结果还取决于当时其他容器的运行状态,例如:
- 1、如果容器 A 的进程一直处于空闲状态,那么容器 B 可以获取比容器 A 更多的 CPU 时间片。
- 2、如果主机上只运行了一个容器,即使它的 CPU 份额值只有 50,它也可以独占整个主机的 CPU 资源。
5.1.3 资源分配原理
cgroups 仅在多个容器同时争抢同一个 CPU 资源时,CPU 配额才会生效。因此,无法单纯根据某个容器的 CPU 份额值确定它获得的 CPU 资源数量。资源分配结果取决于同时运行的其他容器的 CPU 分配和容器中进程的运行情况。
5.1.4 Docker容器跑在指定的cpu上
Docker允许通过--cpuset参数将容器绑定到特定的CPU和内存节点上。
对于多核CPU服务器,这些参数有用,可以控制容器运行时限定使用哪些CPU和内存节点
--cpuset-cpus- 指定容器可以使用的具体CPU是哪个
--cpuset-memes- 指定容器可以使用的内存节点
5.1.5 扩充-服务器架构介绍
从系统架构来看,当前的商用服务器大体可以分为三类:
对称多处理器结构 (SMP: Symmetric Multi-Processor)
- 简介:在 SMP 架构中,所有处理器共享同一个内存空间,并且所有处理器之间的访问速度是相同的。所有处理器都对称地访问主内存和 I/O 子系统。该架构适用于中小规模的并行处理。
- 例子:x86 服务器,双路服务器。
- 特点:主板上通常有两个或多个物理 CPU,所有 CPU 共享同一内存。
非一致存储访问结构 (NUMA: Non-Uniform Memory Access)
-
简介:在 NUMA 架构中,系统被划分为多个节点,每个节点包含一个或多个 CPU 和本地内存。各节点之间通过高速互联连接。访问本地内存比访问其他节点的内存更快,因此内存访问时间取决于内存和 CPU 的相对位置。
-
例子:IBM 小型机 pSeries 690。
-
特点:具有多个 CPU 和内存节点,内存访问速度因节点不同而异,适合需要大量内存和高吞吐量的应用。
海量并行处理结构 (MPP: Massive Parallel Processing)
- 简介:MPP 架构由大量相对独立的计算节点组成,每个节点有自己的 CPU、内存和 I/O 子系统。各节点通过高速网络连接,协同完成并行计算任务。MPP 适用于处理大规模数据和高性能计算任务。例子:大型机 Z14。
- 特点:支持大规模的并行处理,每个节点独立运行,但通过网络协同工作,适用于高性能计算和大数据处理。
总结:
- SMP:多处理器共享同一内存,适用于中小规模并行处理。
- NUMA:多个节点,每个节点有独立的内存和 CPU,适用于需要高吞吐量的应用。
- MPP:大量独立节点并行处理,适用于高性能计算和大数据处理。
5.2 CPU 配额控制参数的混合使用
5.2.1 案例1-使用stress压测工具测试软件
测试cpu-shares和cpuset-cpus混合使用运行效果,就需要一个压缩力测试工具stress来让容器实例把cpu跑满。
bash
# 下载Epel库
[root@docker ~]# rpm -ivh https://mirrors.aliyun.com/epel/epel-release-latest-8.noarch.rpm
[root@docker ~]# /usr/bin/crb enable
[root@docker ~]# yum install epel-release
[root@docker ~]# yum install -y stress
# 产生2个CPU进程,2个I/O进程,20s后停止运行
]# stress -c 2 -i 2 --verbose --timeout 30s
# 另一个终端执行top进行查看,是否将CPU资源跑满

5.2.2 案例2-在Docker容器中运行stress压测工具
创建两个容器实例: docker10 和docker20。 让docker10和docker20只运行在cpu0和cpu1上,最终测试一下docker10和docker20使用cpu的百分比。实验拓扑图如下:
可惜我的物理机核心数是2核,但是不影响实验效果。
bash
# 运行两个容器docker10,docker20,并配置CPU份额
[root@docker ~]# docker run -itd --name docker10 --cpuset-cpus 0,1 --cpu-shares 512 rockylinux:8.9 /bin/bash
[root@docker ~]# docker run -itd --name docker20 --cpuset-cpus 0,1 --cpu-shares 1024 rockylinux:8.9 /bin/bash
# 并指定这两个容器压测cpu0,cpu1,且份额为 512:1024 --> 1:2
# 上传资源包里面的stress文件并赋予执行权限
[root@docker ~]# chmod +x stress
[root@docker ~]# docker cp stress docker10:/bin
[root@docker ~]# docker exec -it docker10 /bin/bash
[root@f383c452c917 /]# stress -c 2 -v -t 5m
[root@docker ~]# docker cp stress docker20:/bin
[root@docker ~]# docker exec -it docker20 /bin/bash
[root@d76e3983631b /]# stress -c 2 -v -t 5
# 打开另外一个终端去查看
]# top --> # 去观察docker20是否是docker10的2倍。
# 说明--cpu-shares限制资源成功

5.3 Docker容器控制内存
Docker提供参数-m,--memory=" "
限制容器的内存使用量。
5.3.1 示例
1.允许容器使用的内存上限为128M
bash
[root@docker ~]# docker run -it -m 128m rockylinux:8.9
[root@52dc78aa87e4 /]# cat /sys/fs/cgroup/memory/memory.limit_in_bytes
134217728
2.创建一个docker,只使用2个cpu核心,只能使用128M内存
bash
[root@docker ~]# docker run -it --cpuset-cpus 0,1 -m 128m rockylinux:8.9
5.4 Docker实现容器开机自启
--restart=always 参数
bash
[root@docker ~]# docker run -itd --name ziqidong --restart=always rockylinux:8.9 /bin/bash
[root@docker ~]# docker ps
[root@docker ~]# reboot
...
# 重启之后进行
[root@docker ~]# docker ps
[root@docker ~]# docker exec -it ziqidong /bin/bash
[root@79c9fc0c1c70 /]# hostname -i
172.17.0.2
[root@79c9fc0c1c70 /]# exit
[root@docker ~]#
5.5 Docker容器实现自动释放资源
--rm 参数
bash
[root@docker ~]# docker run --help | grep rm
--rm Automatically remove the container
# 当容器命令运行结束后,自动删除容器,自动释放资源
bash
[root@docker ~]# docker run -it --rm --name rm-ceshi rockylinux:8.9 sleep 4
