docker
1 docker概述
1.1 什么是Docker容器?
Docker容器是一种轻量级的虚拟化技术,它允许开发者将应用及其依赖文件封装在Docker镜像文件中,然后在不同的物理设备上运行这些镜像,从而实现应用的快速部署和资源的高效利用。
1.2 Docker容器和VM的区别
启动速度
- Docker:启动速度非常快,通常在秒级别。这是因为Docker容器直接运行在宿主操作系统上,不需要额外的启动步骤。
- 虚拟机:启动速度相对较慢,通常需要几分钟。虚拟机需要启动一个完整的操作系统,这包括加载内核、初始化各种服务等。
性能损耗
- Docker:由于Docker容器共享宿主操作系统的内核,因此性能损耗较小。Docker的资源开销(CPU、内存等)比虚拟机低。
- 虚拟机:虚拟机需要运行一个完整的操作系统,因此性能损耗较大。虚拟机的资源开销较高,因为每个虚拟机都需要自己的操作系统和内核。
隔离性
- Docker:Docker的隔离性较弱,它属于进程级别的隔离。Docker容器共享宿主操作系统的内核,因此可能存在一定的安全隐患。
- 虚拟机:虚拟机的隔离性较强, 它属于系统级别的隔离。每个虚拟机都有自己的操作系统和内核,因此安全性更高。
架构和实现
- Docker:Docker利用Linux内核的容器化特性(如Namespaces和Cgroups)来实现资源和环境的隔离。Docker引擎可以看作是对这些内核特性的封装。
- 虚拟机:虚拟机通过Hypervisor(虚拟机管理系统)来实现资源和环境的隔离。Hypervisor虚拟化CPU、内存、I/O设备等,为每个虚拟机提供一个独立的操作系统环境。
使用场景
- Docker:适用于微服务架构、DevOps、持续集成/持续部署(CI/CD)、快速开发和测试等场景。特别适合统一开发环境、实现应用的快速打包、分发和部署。
- 虚拟机:适用于需要完全隔离、安全性要求高、需要运行不同操作系统的场景。例如企业服务器虚拟化、遗留系统支持、测试不同操作系统等。
分发和部署
- Docker:Docker通过Dockerfile记录容器的构建过程,可以在集群中实现快速分发和部署。Docker镜像的分发更加体系化。
- 虚拟机:虚拟机通过镜像实现环境交付的一致性,但镜像的分发无法体系化,相对较为繁琐。
1.3 docker优势
更快速的应用交付和部署 :传统的应用开发完成后,需要提供一堆安装程序和配置说明文档,安装部署后需根据配置文档进行繁杂 的配置才能正常运行。Docker化之后只需要交付少量容器镜像文件,在正式生产环境加载镜像并运行即 可,应用安装配置在镜像里已经内置好,大大节省部署配置和测试验证时间。
更便捷的升级和扩缩容 :随着微服务架构和Docker的发展,大量的应用会通过微服务方式架构,应用的开发构建将变成搭乐高积 木一样,每个Docker容器将变成一块“积木”,应用的升级将变得非常容易。当现有的容器不足以支撑业 务处理时,可通过镜像运行新的容器进行快速扩容,使应用系统的扩容从原先的天级变成分钟级甚至秒 级。
更简单的系统运维 应用容器化运行后,生产环境运行的应用可与开发、测试环境的应用高度一致,容器会将应用程序相关 的环境和状态完全封装起来,不会因为底层基础架构和操作系统的不一致性给应用带来影响,产生新的 BUG。当出现程序异常时,也可以通过测试环境的相同容器进行快速定位和修复。
更高效的计算资源利用 Docker是内核级虚拟化,其不像传统的虚拟化技术一样需要额外的Hypervisor [管理程序] 支持,所以在 一台物理机上可以运行很多个容器实例,可大大提升物理服务器的CPU和内存的利用率。
2 docker安装
2.1 docker的基本组成
镜像
是一个用于创建容器的只读模版。可以看做操作系统的一个快照,所以一个镜像可以创建多个容器。
具有层级结构,所以会看到镜像是一层一层的拉取。
容器
Docker 利用容器(Container)独立运行的一个或一组应用。
容器是用镜像创建的运行实例。 它可以被启动、开始、停止、删除。每个容器都是相互隔离的,保证安全的平台。
可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等) 和运行在其中的应用程序。。
容器的定义和镜像几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写 的。
仓库
仓库(Repository)是集中存放镜像文件的场所。
仓库(Repository)和仓库注册服务器(Registry)是有区别的。仓库注册服务器上往往存放着多个仓 库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)。
仓库分为公开仓库(Public)和私有仓库(Private)两种形式。
最大的公开仓库是 Docker Hub(https://hub.docker.com/),存放了数量庞大的镜像供用户下载。 国内的公开仓库包括阿里云 、网易云 等
2.2 安装docker
- 直接参考官网-----地址
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
# 安装
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 测试
sudo docker run hello-world
2.3 阿里云加速
- 直接参考官网------地址
- 1panel加速:
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://docker.1panel.live"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
2.4 底层原理
客户端
docker客户端是和docker交互的主要方式,可以通过命令行来实现交互,比如docker run xxx
守护进程 Docker daemon
Docker 守护进程(通常称为 Docker daemon)是 Docker 架构中的核心组件之一,负责管理和运行 Docker 容器。它是一个后台服务进程,运行在宿主机上,能够监听来自客户端的请求,并执行相应的操作,如创建、启动、停止和删除容器等。
通常适合系统的启动而启动,可以使用如下命令
sudo systemctl start docker
sudo systemctl stop docker
sudo systemctl restart docker
3 docker常用命令
3.1 帮助命令
docker version # 显示 Docker 版本信息。
docker info # 显示 Docker 系统信息,包括镜像和容器数。。
docker --help # 帮助
3.2 镜像命令
3.2.1 docker images
- 列出本地镜像
docker images
可选项 -a: 列出本地所有镜像 -q: 只显示镜像id --digests: 显示镜像的摘要信息
结果:
REPOSITORY 镜像的仓库源 TAG 镜像的标签 IMAGE ID 镜像的ID CREATED 镜像创建时间 SIZE 镜像大小
3.2.2 docker search
- 搜索镜像
docker search ubuntu
可追加的参数:--filter=stars=50 : 列出收藏数不小于指定值的镜像。
3.2.3 docker pull
- 拉取镜像
docker pull ubuntu # 不写标签(tag)默认是拉取最新版本
3.2.4 docker rmi
- 删除镜像
docker rmi -f 镜像id
docker rmi -f 镜像名:id 镜像名:id
docker rmi -f $(docker images -aq) # 删除全部镜像
3.3 容器命令
- 有了镜像才可以创建容器
3.3.1 docker run
- 使用镜像运行容器
docker run -it --name test ubuntu /bin/bash
参数 | 描述 |
---|---|
-d , --detach | 在后台运行容器并打印容器ID |
-i , --interactive | 保持STDIN打开,即使没有附加 |
-t , --tty | 分配一个伪TTY(通常与-i 一起使用) |
-p , --publish | 发布容器端口到主机,格式为 <主机端口>:<容器端口> 或 udp://<主机端口>:<容器端口> |
-e , --env | 设置环境变量,例如 -e VARNAME=value |
-v , --volume | 绑定挂载一个文件或目录,格式为 <主机路径>:<容器路径> 或 `<主机路径>:<容器路径>:[rw |
--name | 为容器指定一个名称 |
--network | 连接容器到指定的网络 |
3.3.2 docker ps
- 参看有哪些运行的容器
docker ps [options]
3.3.3 进入终端后退出容器
exit # 会停止容器
ctrl+p+q #容器不停止退出
3.3.4 启动停止容器
docker start 容器id或容器名称
docker restart 容器id或容器名称
docker stop 容器id或容器名称
docker kill 容器id或容器名称
3.3.5 删除容器
docker rm
docker rm -f $(docker ps -aq) #删除所有容器
docker ps -aq | xargs docker rm #使用管道命令--可以用来启动多个容器
3.3.6 docker commit
- 提交容器为镜像
类似于vm中的快照可以保存容器的状态
docker commit 提交容器副本使之成为一个新的镜像!
docker commit -m="提交的描述信息" -a="作者" 容器id 要创建的目标镜像名:[标签名]
3.4 其他常用命令
3.4.1 docker run -d
- 后台启动容器
docker run -d 容器名:tag
容器在后台运行时,如果没有前台进程(即主进程PID 1退出),容器会自动停止。这是因为Docker容器的生命周期与其主进程绑定。
3.4.2 docker logs
- 查看日志
docker logs -tf --tail 10 容器id
参数
-t 显示时间戳
-f 打印最新的日志
--tail 数字 显示多少条!
3.4.3 docker top
- 查看容器内进程信息
3.4.4 docker inspect
- 查看容器的元数据
3.4.5 docker exec 和 docker attach
- 与运行中的容器进行交互命令
docker exec -it 容器id bashShell # 打开新的终端
docker attach 容器id # 不会启动新的终端
3.4.6 docker cp
- 拷贝文件
docker cp [OPTIONS] SRC_PATH CONTAINER:DEST_PATH
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH
- SRC_PATH:源文件或目录的路径。
- CONTAINER:目标容器的名称或 ID。
- DEST_PATH:目标文件或目录的路径。
- OPTIONS:
--follow-link
,-L
:如果源文件是符号链接,则复制链接的目标文件而不是链接本身。
3.5 可视化
- 百度搜索,有很多
- Portainer
- docker desktop
4 容器的数据卷
4.1 什么是数据卷
容器的数据卷(Docker Volumes)是 Docker 提供的一种持久化数据的方法。容器通常是临时的,意味着它们的文件系统在容器删除时会丢失。为了持久化存储,Docker 提供了数据卷,使容器中的数据能够跨容器和主机系统共享或保留。
数据卷 是 Docker 管理的一种特殊目录,可以让数据独立于容器的生命周期进行持久化存储。
数据卷的特点:
- 持久性存储:
- 数据卷在容器删除后依然存在,因此可以避免容器停止或删除时丢失数据。它们存储的数据可以在容器间共享或保留。
- 容器之间共享数据:
- 多个容器可以挂载同一个数据卷,从而在容器之间共享数据。这使得容器之间的数据同步变得简单和高效。
- 独立于容器生命周期:
- 数据卷的生命周期独立于容器本身。当一个容器删除时,数据卷并不会被删除,除非显式删除数据卷。这确保了容器和数据分离,便于容器的重建和维护。
- 性能优化:
- 数据卷通常比容器内的存储(比如容器文件系统)具有更高的性能,因为它们通常与宿主机的文件系统直接挂载,且可以避免容器文件系统的写入限制。
- 容易备份和恢复:
- 数据卷可以通过 Docker 命令轻松备份、恢复和迁移。你可以将数据卷挂载到另一个容器,或将其内容导出为 tar 文件进行备份。
- 支持宿主机和容器间的数据共享:
- 数据卷可以挂载到宿主机的指定目录,也可以直接在 Docker 中创建。通过挂载宿主机目录到容器内的卷中,可以轻松在宿主机和容器之间共享数据。
- 无需容器管理:
- 容器创建和销毁时,Docker 会自动管理数据卷的挂载和解绑,用户不需要手动处理这些操作。
4.2 三种挂载方式
在 Docker 中,数据卷(Volume)用于持久化和共享容器的数据,通常用于在容器生命周期之外保留数据。Docker 提供了三种主要的挂载方式:匿名挂载卷、命名卷和绑定挂载。以下是这三种挂载方式的实际使用讲解:
1. 匿名挂载卷
匿名挂载卷不指定卷的名称,Docker 会自动创建一个新卷,并将它挂载到容器中。
示例
docker run -it -v /data ubuntu /bin/bash
-v /data
表示将容器中的/data
目录挂载到一个匿名卷。- Docker 会自动为该匿名卷分配一个随机的卷名称,删除容器的时候添加-v参数就会一起删除。(除非被其他容器使用)。
查看卷
docker volume ls
你会看到一个由 Docker 自动创建的匿名卷。
- 查看容器信息来找到数据卷
docker inspect 容器名称
- 查看数据卷信息找到数据卷在宿主机的目录
docker volume inspect 数据卷名称
2. 命名卷
命名卷是显式地给卷命名,可以通过卷名在 Docker 中识别和复用这些卷。
示例
docker volume create test
docker run -it -v test:/data ubuntu /bin/bash
docker volume create test
命令创建了一个名为test
的卷。-v test:/data
表示将命名卷test
挂载到容器的/data
目录。- 命名卷可以在多个容器之间共享。
查看命名卷
docker volume ls
会看到一个名为 test
的卷。
3. 绑定挂载
绑定挂载将宿主机上的一个目录或文件挂载到容器中,这使得容器可以直接访问宿主机上的文件系统。
示例
docker run -it -v /host/data:/container/data ubuntu /bin/bash
/host/data
是宿主机上的目录路径。/container/data
是容器内的目录路径。- 任何在容器内对挂载目录的修改都会直接反映到宿主机上,两者数据实时同步。
查看绑定挂载
绑定挂载没有单独的Docker管理命令,因为它直接映射到宿主机的文件系统。你可以直接查看宿主机目录中的内容,或使用 docker inspect 容器名
查看挂载信息。
4.2.4 总结
- 匿名挂载卷:适用于临时数据存储,容器删除后数据卷依然存在,需要手动删除。
# 删除容器时同时删除关联的匿名数据卷
docker rm -v 容器ID
# 或者单独删除数据卷
docker volume rm 数据卷名称
- 命名卷:适用于持久化存储,可以在不同容器之间共享。
- 绑定挂载:适用于直接与宿主机文件系统交互,通常用于开发环境或需要访问宿主机文件的场景。
4.3 常用命令
4.3.1 继承父容器的数据卷
docker run -it --name docker02 --volumes-from docker01 ubuntu
--volumes-from docker01
:表示容器docker02
会继承容器docker01
中挂载的数据卷。- 这种方式常用于创建多个容器之间共享数据,尤其是当多个容器需要同时使用同一份数据时。
4.3.2 改变文件权限
可以通过在 -v
参数后添加 :ro
或 :rw
来指定容器对挂载目录的读写权限。
:ro
(只读):容器只能读取数据卷中的数据,不能修改。:rw
(读写,默认值):容器可以读取和写入数据卷中的数据。
示例:
# 挂载卷,且设置为只读
docker run -it -P --name nginx02 -v nginxconfig:/etc/nginx:ro nginx /bin/bash
# 挂载卷,且设置为读写(这是默认的行为)
docker run -d -P --name nginx02 -v nginxconfig:/etc/nginx:rw nginx
:ro
和:rw
用来指定容器对挂载目录的权限,默认情况下是读写(rw
)。
4.3.3 其他常用命令
- 查看数据卷:
docker volume ls
- 删除数据卷:
docker volume rm my_volume
- 查看数据卷的详细信息:
docker volume inspect my_volume
- 挂载数据卷到容器:
docker run -v my_volume:/path/in/container my_image
4.4 使用项目示例:nginx
概述
Docker Volumes 是 Docker 中用于持久化存储数据的一种机制。通过使用 Volumes,你可以确保容器中的数据在容器停止或删除后仍然存在。这对于需要持久化存储的应用程序(如数据库、配置文件等)非常有用。
示例程序:使用 Docker Volumes 运行 Nginx
在这个示例中,我们将使用 Docker Volumes 来持久化 Nginx 的配置文件和网站数据。
1. 创建 Docker Volume
首先,我们需要创建两个 Docker Volumes,一个用于存储 Nginx 的配置文件,另一个用于存储网站数据。
docker volume create nginx_config
docker volume create nginx_data
2. 创建 Nginx 配置文件
在本地创建 nginx.conf
和 index.html
文件,并将它们放置在一个目录中。
创建配置文件
worker_processes 1;
events {
worker_connections 1024;
}
http {
default_type application/octet-stream;
server {
listen 8888;
server_name 0.0.0.0;
location / {
root /usr/share/nginx/html/;
index index.html;
}
}
}
3. 创建网站数据
<!DOCTYPE html>
<html>
<head>
<title>Welcome to Nginx with Docker!</title>
</head>
<body>
<h1>Hello, Docker Volumes!</h1>
</body>
</html>
4. 或者替换为Ds生成
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Docker Volume 学习指南</title>
<style>
:root {
--primary: #2496ed;
--secondary: #1e88e5;
--dark: #0d47a1;
--light: #bbdefb;
--background: #f5f7fa;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
background-color: var(--background);
margin: 0;
padding: 0;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
header {
background: linear-gradient(135deg, var(--primary), var(--dark));
color: white;
padding: 2rem 0;
text-align: center;
border-radius: 0 0 10px 10px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
margin-bottom: 2rem;
}
h1 {
margin: 0;
font-size: 2.5rem;
}
.subtitle {
font-size: 1.2rem;
opacity: 0.9;
margin-top: 0.5rem;
}
.card {
background: white;
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 1.5rem;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
}
h2 {
color: var(--dark);
margin-top: 0;
border-bottom: 2px solid var(--light);
padding-bottom: 0.5rem;
}
.code-block {
background: #f0f4f8;
padding: 1rem;
border-radius: 6px;
font-family: 'Courier New', Courier, monospace;
overflow-x: auto;
margin: 1rem 0;
border-left: 4px solid var(--primary);
}
.command {
color: #d6336c;
font-weight: bold;
}
.note {
background: #fff3bf;
padding: 1rem;
border-radius: 6px;
border-left: 4px solid #ffd43b;
margin: 1rem 0;
}
.btn {
display: inline-block;
background: var(--primary);
color: white;
padding: 0.6rem 1.2rem;
border-radius: 4px;
text-decoration: none;
font-weight: bold;
transition: background 0.3s;
}
.btn:hover {
background: var(--dark);
}
footer {
text-align: center;
margin-top: 2rem;
padding: 1rem;
color: #666;
font-size: 0.9rem;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
margin: 2rem 0;
}
</style>
</head>
<body>
<header>
<div class="container">
<h1>Docker Volume 学习指南</h1>
<p class="subtitle">掌握容器数据持久化的关键技术</p>
</div>
</header>
<div class="container">
<div class="card">
<h2>什么是 Docker Volume?</h2>
<p>Docker Volume 是 Docker 提供的用于持久化存储数据的机制。它允许容器与主机或其他容器共享数据,即使容器被删除,Volume 中的数据仍然保留。</p>
<div class="note">
<strong>为什么需要 Volume?</strong> 默认情况下,容器内的文件系统是临时的,当容器停止或删除时,所有更改都会丢失。Volume 解决了这个问题。
</div>
</div>
<div class="grid">
<div class="card">
<h2>创建 Volume</h2>
<p>使用以下命令创建一个命名的 Volume:</p>
<div class="code-block">
<span class="command">docker volume create</span> my_volume
</div>
<p>列出所有 Volume:</p>
<div class="code-block">
<span class="command">docker volume ls</span>
</div>
</div>
<div class="card">
<h2>使用 Volume</h2>
<p>在运行容器时挂载 Volume:</p>
<div class="code-block">
<span class="command">docker run -d --name my_container \</span><br>
<span class="command">-v my_volume:/app/data</span> \<br>
my_image
</div>
<p>这将把 Volume 挂载到容器的 <code>/app/data</code> 目录。</p>
</div>
</div>
<div class="card">
<h2>Volume 类型</h2>
<ul>
<li><strong>命名 Volume</strong> - 由 Docker 管理,是最常用的类型</li>
<li><strong>绑定挂载</strong> - 直接挂载主机文件系统的目录</li>
<li><strong>tmpfs 挂载</strong> - 仅存储在内存中,不持久化</li>
</ul>
<h3>绑定挂载示例</h3>
<div class="code-block">
<span class="command">docker run -d --name my_container \</span><br>
<span class="command">-v /path/on/host:/path/in/container</span> \<br>
my_image
</div>
</div>
<div class="card">
<h2>实际应用示例</h2>
<p>使用 Volume 运行 MySQL 数据库:</p>
<div class="code-block">
<span class="command">docker run -d --name mysql_db \</span><br>
<span class="command">-v mysql_data:/var/lib/mysql \</span><br>
<span class="command">-e MYSQL_ROOT_PASSWORD=my-secret-pw \</span><br>
mysql:latest
</div>
<p>备份 Volume 数据:</p>
<div class="code-block">
<span class="command">docker run --rm \</span><br>
<span class="command">-v mysql_data:/volume \</span><br>
<span class="command">-v $(pwd):/backup \</span><br>
<span class="command">alpine tar cvf /backup/mysql_backup.tar /volume</span>
</div>
</div>
<div class="card">
<h2>进一步学习</h2>
<p>要了解更多关于 Docker Volume 的信息,可以查看以下资源:</p>
<ul>
<li><a href="https://docs.docker.com/storage/volumes/" target="_blank">官方文档 - Docker Volumes</a></li>
<li><a href="https://docs.docker.com/storage/bind-mounts/" target="_blank">官方文档 - Bind Mounts</a></li>
<li><a href="https://docs.docker.com/compose/compose-file/#volumes" target="_blank">Docker Compose 中的 Volume 配置</a></li>
</ul>
<a href="#" class="btn">开始实验</a>
</div>
</div>
<footer>
<div class="container">
<p>© 2023 Docker Volume 学习项目 | 使用 HTML & CSS 构建</p>
</div>
</footer>
</body>
</html>
5. 运行 Nginx 容器
运行 Nginx 容器,并挂载之前创建的两个 Docker Volumes。
docker run -d --name my_nginx -p 8888:8888 \
-v nginx_config:/etc/nginx \
-v nginx_data:/usr/share/nginx/html \
nginx
6. 验证
打开浏览器,访问 http://localhost:8888
,你应该会看到 Hello, Docker Volumes!
的页面。
5. DockerFile
DockerFile是Docker镜像构建的核心工具,掌握它是成为Docker高手的必经之路。
5.1 DockerFile概述
5.1.1 什么是DockerFile
DockerFile
是一个包含构建指令的文本文件,用于自动化创建Docker镜像。它定义了从基础镜像到最终应用镜像的完整构建过程。
DockerFile的特点:
- 文本格式,易于版本控制
- 指令式编程,逐步构建
- 支持缓存机制,提高构建效率
- 可重复构建,确保环境一致性
5.1.2 Docker镜像构建流程
开发应用 → 编写DockerFile → 构建镜像 → 上传仓库 → 下载使用 → 启动运行
具体步骤:
- 编写DockerFile文件 - 定义构建指令
- docker build 构建镜像 - 基于DockerFile创建镜像
- docker run 运行容器 - 基于镜像启动容器
5.2 DockerFile指令详解
5.2.1 基础指令
FROM - 指定基础镜像
FROM ubuntu:20.04
FROM centos:7
FROM scratch # 空白镜像
- 每个DockerFile必须以FROM开始
- 可以使用多个FROM创建多阶段构建
MAINTAINER/LABEL - 维护者信息
# 推荐写法
LABEL maintainer="wds <[email protected]>"
LABEL version="1.0"
LABEL description="My custom image"
5.2.2 环境配置指令
ENV - 设置环境变量
ENV MYPATH /usr/local
ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk
ENV PATH $PATH:$JAVA_HOME/bin
WORKDIR - 设置工作目录
WORKDIR /usr/local
WORKDIR $MYPATH # 可以使用环境变量
- 相当于cd命令,但会创建目录
- 后续的RUN、CMD、COPY等指令都在此目录下执行
USER - 切换用户
USER root
USER nginx
USER 1000 # 也可以使用UID
5.2.3 文件操作指令
COPY - 复制文件
COPY src/ /app/
COPY package.json /app/
COPY --chown=nginx:nginx config.conf /etc/nginx/
- 只能复制构建上下文中的文件
- 推荐使用,功能简单明确
ADD - 增强的复制
ADD app.tar.gz /app/ # 自动解压
ADD https://example.com/file.txt /tmp/ # 支持URL
- 支持自动解压和URL下载
- 一般情况下推荐使用COPY
5.2.4 执行指令
RUN - 构建时执行命令
# Shell格式
RUN yum update -y
RUN apt-get update && apt-get install -y nginx
# Exec格式(推荐)
RUN ["yum", "install", "-y", "vim"]
# 合并命令减少层数(最佳实践)
RUN yum update -y && \
yum install -y vim net-tools && \
yum clean all
CMD - 容器启动命令
# 只能有一个CMD,多个则最后一个生效
CMD ["nginx", "-g", "daemon off;"]
CMD nginx -g "daemon off;" # Shell格式
- 容器启动时的默认命令
- 可以被docker run命令覆盖
ENTRYPOINT - 入口点
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"] # 作为ENTRYPOINT的参数
- 不会被docker run命令覆盖
- 常与CMD配合使用
5.2.5 网络和存储指令
EXPOSE - 声明端口
EXPOSE 80
EXPOSE 443
EXPOSE 8080/tcp # 默认TCP
EXPOSE 53/udp # UDP端口
- 仅是声明,不会自动映射
- 需要在运行时使用-p参数映射
VOLUME - 声明挂载点
VOLUME ["/data"]
VOLUME ["/var/log", "/var/db"]
- 声明匿名卷挂载点
- 运行时数据不会写入容器层
5.2.6 其他常用指令
ARG - 构建参数
ARG VERSION=latest
FROM nginx:$VERSION
ARG USER=1000
USER $USER
- 仅在构建时有效
- 可以通过--build-arg传递
5.3 实战案例
5.3.1 编写DockerFile
创建一个增强版CentOS镜像,安装常用工具:
# 基础镜像
FROM centos:7
# 维护者信息
LABEL maintainer="wds <[email protected]>" \
version="1.0" \
description="Enhanced CentOS with development tools"
# 设置环境变量
ENV MYPATH /usr/local
ENV LANG en_US.UTF-8
# 设置工作目录
WORKDIR $MYPATH
# 安装软件包(合并命令减少层数)
RUN yum update -y && \
yum install -y \
vim \
net-tools \
wget \
curl \
git \
gcc \
make && \
yum clean all && \
rm -rf /var/cache/yum/*
# 创建普通用户
RUN useradd -m -s /bin/bash developer
# 声明端口
EXPOSE 80 443
# 创建数据目录
VOLUME ["/data"]
# 设置默认命令
CMD ["/bin/bash"]
5.3.2 构建镜像
# 基本构建(DockerFile在当前目录)
docker build -t mycentos:1.0 .
# 指定DockerFile路径
docker build -f /path/to/DockerFile -t mycentos:1.0 .
# 传递构建参数
docker build --build-arg VERSION=8 -t mycentos:1.0 .
# 不使用缓存构建
docker build --no-cache -t mycentos:1.0 .
参数说明:
-f
: 指定DockerFile路径-t
: 指定镜像名称和标签.
: 构建上下文路径--build-arg
: 传递构建参数--no-cache
: 不使用构建缓存
5.3.3 测试运行
# 运行容器
docker run -it --name test mycentos:1.0
# 后台运行并映射端口
docker run -d -p 8080:80 --name web mycentos:1.0
# 挂载数据卷
docker run -it -v /host/data:/data --name test mycentos:1.0
5.4 Docker镜像分层机制
5.4.1 分层原理
Docker镜像采用分层存储架构,每个DockerFile指令都会创建一个新的镜像层:
容器层 (读写层)
├── Layer N: CMD ["/bin/bash"]
├── Layer 3: COPY app.jar /app/
├── Layer 2: RUN yum install -y vim
├── Layer 1: FROM centos:7
└── 基础层
分层特点:
- 每层都是只读的
- 层与层之间是增量关系
- 容器运行时添加可写层
5.4.2 分层机制的优势
1. 存储空间优化
# 多个容器共享相同的基础层
Container A: App A + Node.js层 + Ubuntu层
Container B: App B + Node.js层 + Ubuntu层 (共享)
Container C: App C + Node.js层 + Ubuntu层 (共享)
2. 构建缓存加速
FROM node:14 # ← 缓存命中,跳过下载
WORKDIR /app # ← 缓存命中,跳过
COPY package.json . # ← 缓存命中,跳过
RUN npm install # ← 缓存命中,跳过
COPY . . # ← 代码变更,从此处重新构建
RUN npm build # ← 重新执行
3. 镜像共享复用
- 相同的基础镜像层在不同镜像间共享
- 减少网络传输和存储需求
- 提高镜像分发效率
5.4.3 查看镜像分层
docker history
# 查看镜像构建历史
docker history mycentos:1.0
# 输出示例:
# IMAGE CREATED BY SIZE
# a1b2c3d4e5f6 CMD ["/bin/bash"] 0B
# f6e5d4c3b2a1 EXPOSE 80 443 0B
# 1a2b3c4d5e6f RUN yum install -y vim net-tools && yum ... 156MB
# 6f5e4d3c2b1a WORKDIR /usr/local 0B
# b1a2c3d4e5f6 ENV MYPATH=/usr/local 0B
# 查看镜像详细信息
docker inspect mycentos:1.0
# 使用dive工具分析镜像层(需要安装dive)
dive mycentos:1.0
5.4.4 分层优化最佳实践
1. 合并RUN指令
# ❌ 不好的做法 - 创建多个层
RUN yum update -y
RUN yum install -y vim
RUN yum install -y net-tools
RUN yum clean all
# ✅ 推荐做法 - 合并为一层
RUN yum update -y && \
yum install -y vim net-tools && \
yum clean all
2. 合理安排指令顺序
# ✅ 将变化频率低的指令放前面
FROM node:14
WORKDIR /app
COPY package.json . # 依赖文件很少变化
RUN npm install # 利用缓存
COPY . . # 代码经常变化,放最后
3. 使用.dockerignore
不被 COPY
或 ADD
指令包含
# .dockerignore文件
node_modules
*.log
.git
README.md
5.5 DockerFile最佳实践
5.5.1 编写规范
使用官方基础镜像
dockerfileFROM node:14-alpine # 使用官方轻量级镜像
固定版本标签
dockerfileFROM nginx:1.20-alpine # 避免使用latest
最小化镜像大小
dockerfileRUN apk add --no-cache git RUN rm -rf /var/cache/apk/*
合理使用缓存
dockerfile# 将变化少的操作放前面 COPY requirements.txt . RUN pip install -r requirements.txt COPY . . # 代码变化放最后
5.5.2 安全最佳实践
使用非root用户
dockerfileRUN adduser -D appuser USER appuser
最小权限原则
dockerfileCOPY --chown=appuser:appuser . /app
及时清理缓存
dockerfileRUN apt-get update && \ apt-get install -y package && \ rm -rf /var/lib/apt/lists/*
5.6 常见问题与解决方案
5.6.1 构建失败排查
检查构建上下文
bash# 查看发送到Docker daemon的文件 docker build --progress=plain -t myapp .
调试中间层
bash# 基于失败的层启动容器调试 docker run -it <layer_id> /bin/sh
5.6.2 镜像过大问题
- 使用多阶段构建
- 选择轻量级基础镜像
- 及时清理临时文件
- 使用.dockerignore过滤文件
5.7 小结
- DockerFile基础:理解构建流程和基本语法
- 指令详解:掌握FROM、RUN、COPY等核心指令
- 分层机制:理解Docker镜像的存储原理
- 最佳实践:编写高效、安全的DockerFile
6. 仓库管理
6.1 官方仓库
6.1.1 登录退出
docker login
docker logout
6.1.2 基于容器或者file制作镜像
- 用户名一定要正确
docker commit -m "搭建好了hadoop集群" 501ecfd0e6bf wds2dxh/hadoop:v1.0
6.1.3 推送
docker push wds2dxh/hadoop:v1.0
6.2 阿里云镜像仓库
7. Docker网络
Docker网络是容器间通信的重要基础。
7.1 Docker网络基础
7.1.1 docker0网桥原理
什么是docker0?
每个安装了Docker的Linux主机都会自动创建一个名为docker0
的虚拟网桥。这是Docker默认的网桥设备,充当容器间通信的"交换机"。
技术实现:
- 使用Linux的veth-pair(虚拟以太网对)技术
- 一端连接到容器内部,另一端连接到docker0网桥
- 默认IP段:172.17.0.0/16
7.1.2 容器网络创建过程
当启动一个容器时,Docker会:
- 在宿主机上创建一对veth虚拟网卡
- 一端放在容器内部(通常命名为eth0)
- 另一端连接到docker0网桥
- 为容器分配一个IP地址
验证方法:
# 查看宿主机网络接口
ip link show
# 启动容器前后对比,可以看到新增的veth接口
docker run -d --name test nginx
ip link show
7.1.3 容器间通信机制
容器间能够相互ping通的原理:
- docker0充当二层交换机角色
- 所有连接到docker0的容器都在同一网段
- 通过MAC地址学习实现数据包转发
实践演示:
# 启动两个容器
docker run -d --name test1 nginx
docker run -d --name test2 nginx
# 获取容器IP
docker inspect test1 | grep IPAddress
docker inspect test2 | grep IPAddress
# 测试连通性
docker exec test1 ping 容器test2的IP
7.2 容器连接:--link选项
7.2.1 --link的作用
--link
选项允许容器通过名称而非IP地址进行通信,提供了一种简单的服务发现机制。
基本用法:
# 先启动被链接的容器
docker run -d --name database mysql:5.7
# 启动链接到database的容器
docker run -d --name webapp --link database nginx
7.2.2 --link工作原理
--link通过修改容器的/etc/hosts
文件实现名称解析:
# 查看hosts文件内容
docker exec webapp cat /etc/hosts
# 输出类似:
# 127.0.0.1 localhost
# 172.17.0.2 database 容器ID
验证连通性:
# 可以直接使用容器名ping
docker exec webapp ping database
7.2.3 --link的局限性
⚠️ 重要提醒:
- --link已被Docker官方标记为遗留功能(legacy feature)
- 不推荐在生产环境中使用
- 推荐使用自定义网络替代
7.3 自定义网络
7.3.1 查看Docker网络
# 列出所有网络
docker network ls
# 输出示例:
# NETWORK ID NAME DRIVER SCOPE
# xxxxxxxxxxxx bridge bridge local
# xxxxxxxxxxxx host host local
# xxxxxxxxxxxx none null local
7.3.2 Docker网络模式详解
Docker支持多种网络模式,每种模式适用于不同场景:
Bridge模式(默认)
docker run --net=bridge nginx
- 容器连接到docker0网桥
- 容器有独立的网络命名空间
- 最常用的模式
Host模式
docker run --net=host nginx
- 容器直接使用宿主机网络
- 性能最高,但隔离性最差
- 适用于对网络性能要求极高的场景
None模式
docker run --net=none nginx
- 容器无网络配置
- 只有loopback接口
- 需要用户手动配置网络
Container模式
docker run --net=container:other_container nginx
- 新容器共享指定容器的网络命名空间
- Kubernetes的Pod基于此模式实现
自定义网络模式
docker run --net=mynetwork nginx
- 用户创建的自定义网络
- 支持自动DNS解析
- 提供更好的网络隔离
7.3.3 检查网络详细信息
# 查看特定网络的详细配置
docker network inspect bridge
# 或使用网络ID
docker network inspect 网络ID
这个命令会显示网络的子网、网关、连接的容器等详细信息。
7.3.4 创建自定义网络
基本创建:
docker network create mynetwork
高级创建(指定参数):
docker network create \
--driver bridge \
--subnet 192.168.0.0/16 \
--gateway 192.168.0.1 \
mynetwork
参数说明:
--driver
:网络驱动类型--subnet
:子网范围--gateway
:网关地址
7.3.5 使用自定义网络
# 在创建容器时指定网络
docker run -d --name webapp --net mynetwork nginx
# 也可以指定IP地址
docker run -d --name database --net mynetwork --ip 192.168.0.10 mysql:5.7
7.3.6 自定义网络的优势
使用自定义网络的容器具有以下特性:
自动DNS解析:容器可以直接通过名称ping通
bashdocker exec webapp ping database
更好的隔离性:不同自定义网络间默认隔离
灵活的网络配置:可以自定义IP段、网关等
7.3.7 将容器连接到多个网络
一个容器可以同时连接到多个网络,实现复杂的网络拓扑:
# 将现有容器连接到新网络
docker network connect mynetwork existing_container
# 断开网络连接
docker network disconnect mynetwork existing_container
实际效果:
- 容器将拥有多个IP地址
- 可以与多个网络中的容器通信
- 提供了网络间的桥接能力
7.3.8 网络管理最佳实践
- 生产环境建议:
- 为每个应用创建独立的自定义网络
- 避免使用默认bridge网络
- 合理规划IP地址段
- 安全考虑:
- 不同应用间使用不同网络实现隔离
- 必要时使用防火墙规则进一步限制
- 性能优化:
- 对网络性能要求极高的场景考虑host模式
- 使用合适的网络驱动
7.4 小结
本章介绍了Docker网络的核心概念:
- docker0网桥是默认网络的基础
- --link虽然简单但已过时,不推荐使用
- 自定义网络是现代Docker应用的最佳选择
- 不同网络模式适用于不同场景
在下一章中,我们将学习Docker的数据持久化和卷管理。
7.5 tips
7.5.1容器间自动写入host
创建自定义网络,容器加入后即可通过名称互相 ping:
# 1. 创建自定义桥接网络
docker network create mynet
# 2. 启动容器并加入网络(指定名称)
docker run -d --name nginx1 --network mynet nginx
docker run -d --name nginx2 --network mynet nginx
# 3. 在容器内通过名称 ping 通
docker exec nginx1 ping -c 3 nginx2
docker exec nginx2 ping -c 3 nginx1
✅ 容器间可直接用名称通信 —— Docker 自动提供 DNS 解析。
⚠️ 宿主机不能直接 ping 容器名,需手动绑定 IP 到 /etc/hosts
:
# 获取容器 IP 并写入宿主机 hosts(临时方案)
IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' nginx1)
echo "$IP nginx1" | sudo tee -a /etc/hosts
# 现在宿主机可 ping
ping nginx1
📌 推荐:容器间通信用自定义网络 + 容器名,宿主机访问用端口映射或容器 IP。
8. Docker Compose
Docker Compose是用于定义和运行多容器Docker应用程序的编排工具。通过一个YAML配置文件,可以轻松管理复杂的多容器应用架构。
8.1 Docker Compose概述
8.1.1 什么是Docker Compose
Docker Compose解决了单个Docker容器无法满足复杂应用需求的问题。现代应用通常由多个组件组成,比如Web服务器、数据库、缓存服务等,每个组件运行在独立的容器中。
传统方式的问题:
- 需要逐个启动多个容器
- 手动配置容器间的网络连接
- 难以管理容器间的依赖关系
- 部署和维护复杂
Docker Compose的解决方案:
- 通过一个YAML文件定义整个应用栈
- 自动创建和管理容器间的网络
- 处理服务间的依赖关系
- 提供统一的管理命令
8.1.2 核心概念
服务(Services):应用中的一个组件,比如web服务、数据库服务。每个服务可以运行一个或多个容器。
项目(Project):由一组关联的服务组成的完整应用。项目名默认为docker-compose.yml所在目录的名称。
容器(Container):服务的运行实例。一个服务可以有多个容器实例来实现负载均衡。
8.1.3 工作流程
开发应用 → 编写Dockerfile(可选) → 创建docker-compose.yml → 执行docker-compose up
具体步骤:
- 定义环境:使用Dockerfile定义应用环境(如果需要自定义镜像)
- 配置服务:在docker-compose.yml中定义所有服务及其配置
- 启动应用:运行
docker-compose up
启动整个应用栈
8.2 安装Docker Compose
8.2.1 安装方法
# Linux系统安装
sudo curl -L "https://github.com/docker/compose/releases/download/v2.21.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# 添加执行权限
sudo chmod +x /usr/local/bin/docker-compose
# 验证安装
docker-compose --version
8.3 配置文件详解
8.3.1 文件结构
docker-compose.yml是Docker Compose的核心配置文件,采用YAML格式:
version: '3.8' # Compose文件版本
services: # 定义服务
service1:
# 服务1配置
service2:
# 服务2配置
networks: # 自定义网络(可选)
volumes: # 命名数据卷(可选)
8.3.2 版本声明
版本声明决定了可用的配置选项和功能:
version: '3.8' # 推荐使用,支持最新特性
不同版本的主要区别:
3.8
:最新特性,支持所有现代Docker功能3.3
:兼容性好,适合旧版Docker环境
8.3.3 服务配置
镜像选择
指定服务使用的Docker镜像:
services:
web:
image: nginx:alpine # 使用官方镜像
app:
build: . # 使用当前目录的Dockerfile构建
# 或指定详细构建配置
build:
context: ./app # 构建上下文路径
dockerfile: Dockerfile.dev # 指定Dockerfile文件
端口映射
将容器端口映射到宿主机端口,实现外部访问:
services:
web:
ports:
- "8080:80" # 宿主机端口:容器端口
- "443:443" # HTTPS端口映射
工作原理:
- 外部请求访问宿主机的8080端口
- 请求被转发到容器的80端口
- 实现了容器服务的外部访问
环境变量
为容器设置环境变量,传递配置信息:
services:
db:
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: myapp
# 或从文件加载
env_file:
- .env
环境变量的作用:
- 传递敏感配置(数据库密码等)
- 控制应用行为(开发/生产模式)
- 实现不同环境的配置分离
数据卷挂载
实现数据持久化和宿主机与容器间的文件共享:
services:
web:
volumes:
- "./code:/var/www/html" # 相对路径绑定挂载
- "/host/logs:/var/log" # 绝对路径绑定挂载
- "data_volume:/app/data" # 命名卷挂载
挂载类型说明:
- 绑定挂载:直接映射宿主机目录,适合开发环境
- 命名卷:由Docker管理的持久化存储,适合生产环境
服务依赖
定义服务间的启动顺序:
services:
web:
depends_on:
- db # web服务依赖db服务
db:
image: mysql:5.7
注意:depends_on
只控制启动顺序,不等待服务就绪。应用需要自行处理服务连接重试。
网络配置
自定义网络实现服务间通信:
services:
web:
networks:
- frontend
db:
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
网络作用:
- 提供服务间的网络隔离
- 同一网络内的服务可以通过服务名互相访问
- 支持复杂的网络拓扑结构
8.4 常用命令
Docker Compose提供了丰富的命令来管理多容器应用:
8.4.1 启动和停止
# 启动所有服务
docker-compose up # 前台运行,显示日志
docker-compose up -d # 后台运行
docker-compose up --build # 重新构建镜像后启动
# 停止服务
docker-compose stop # 停止容器,不删除
docker-compose down # 停止并删除容器、网络
docker-compose down --volumes # 同时删除数据卷
8.4.2 监控和调试
# 查看服务状态
docker-compose ps # 显示所有服务运行状态
# 查看日志
docker-compose logs # 查看所有服务日志
docker-compose logs -f web # 实时跟踪web服务日志
# 进入容器
docker-compose exec web bash # 在web容器中执行bash
8.4.3 其他管理命令
# 重启服务
docker-compose restart # 重启所有服务
docker-compose restart web # 重启指定服务
# 构建镜像
docker-compose build # 构建所有需要构建的服务
docker-compose build --no-cache # 不使用缓存重新构建
8.5 实战案例:Flask + Redis应用
8.5.1 项目介绍
我们将创建一个访问计数器应用,展示Docker Compose如何管理多服务应用。应用架构:
- Flask Web服务:处理HTTP请求,显示访问次数
- Redis缓存服务:存储访问计数数据
8.5.2 项目结构
flask-redis-app/
├── app.py # Flask应用代码
├── requirements.txt # Python依赖
├── Dockerfile # Flask服务镜像构建
└── docker-compose.yml # Compose配置文件
8.5.3 应用代码
app.py - Flask应用
import time
import redis
from flask import Flask
app = Flask(__name__)
# 连接Redis服务,使用服务名作为主机名
cache = redis.Redis(host='redis', port=6379)
def get_hit_count():
"""获取访问次数,包含重试机制处理Redis连接问题"""
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hello():
count = get_hit_count()
return f'Hello World! I have been seen {count} times.\n'
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
关键点解析:
host='redis'
:使用Redis服务名连接,Docker Compose自动解析- 重试机制:处理Redis服务启动延迟问题
host='0.0.0.0'
:Flask监听所有接口,允许容器外部访问
requirements.txt - Python依赖
Flask==2.3.3
redis==4.6.0
Dockerfile - 镜像构建
FROM python:3.9-alpine
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY app.py .
EXPOSE 5000
CMD ["python", "app.py"]
8.5.4 Docker Compose配置
docker-compose.yml
version: '3.8'
services:
web:
build: . # 使用当前目录Dockerfile构建
ports:
- "5000:5000" # 映射Flask服务端口
depends_on:
- redis # 依赖Redis服务
environment:
- FLASK_ENV=development # 设置Flask开发模式
redis:
image: redis:alpine # 使用官方Redis Alpine镜像
ports:
- "6379:6379" # 可选:暴露Redis端口用于调试
配置解析:
- web服务:构建自定义镜像,映射端口,设置依赖
- redis服务:使用官方镜像,轻量级Alpine版本
- 网络:两个服务自动在同一网络中,可以通过服务名通信
8.5.5 运行应用
启动应用
# 进入项目目录
cd flask-redis-app
# 构建并启动所有服务
docker-compose up --build
启动过程:
- Docker Compose读取配置文件
- 构建web服务的Docker镜像
- 拉取redis:alpine镜像
- 创建网络和容器
- 按依赖顺序启动服务
测试应用
# 访问应用
curl http://localhost:5000
# 输出:Hello World! I have been seen 1 times.
# 再次访问,计数增加
curl http://localhost:5000
# 输出:Hello World! I have been seen 2 times.
查看运行状态
# 查看服务状态
docker-compose ps
# 查看实时日志
docker-compose logs -f
# 停止应用
docker-compose down
8.6 小结
Docker Compose是现代容器化应用部署的重要工具:
- 简化部署:一个命令启动整个应用栈
- 配置管理:通过YAML文件实现基础设施即代码
- 服务编排:自动处理服务依赖和网络通信
- 环境一致性:确保开发、测试、生产环境的一致性