Dockerfile
一键创建Docker镜像
通过编写Dockerfile,可以将环境配置、应用程序代码、依赖关系等打包成一个镜像,便于快速创建容器。

Dockerfile常用指令
| Dockerfile 指令 | 说明(都是大写字母) |
|---|---|
| FROM | 指定基础镜像(父镜像) |
| LABEL | 添加新建镜像的元数据(镜像的基本信息,作者、邮箱、简介),使用键值对的形式 |
| RUN | 在容器中执行Linux命令,命令之间用&&连接 |
| ENV | 在容器中设置环境变量 |
| ADD | 复制本地文件自动解压缩,支持URL下载 |
| COPY | 将文件或目录复制到镜像中,只有复制功能 |
| VOLUME | 指定持久化目录 |
| CMD | 容器启动时执行的命令(会被运行的命令覆盖) |
| ENTRYPOINT | 容器启动时执行的命令(不会被运行的命令覆盖) |
| WORKDIR | 工作目录,等价执行cd命令 |
| EXPOSE | 指定对外暴露端口 |
| USER | 指定后续指令的用户上下文。USER <用户名>[:<用户组>] |
| ARG | 定义在构建过程中传递给构建器的变量,可使用 "docker build" 命令设置。ARG <参数名>[=<默认值>] |
| ONBUILD | 当该镜像被用作另一个构建过程的基础时,添加触发器。ONBUILD <其它指令> |
| STOPSIGNAL | 设置发送给容器以退出的系统调用信号。 |
| HEALTHCHECK | 定义周期性检查容器健康状态的命令。用于指定某个程序或者指令来监控 docker 容器服务的运行状态。 HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令 HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令 HEALTHCHECK [选项] CMD <命令> : 这边 CMD 后面跟随的命令使用,可以参考 CMD 的用法。 |
| SHELL | 覆盖Docker中默认的shell,用于RUN、CMD和ENTRYPOINT指令。 |
javascript
举例:
vim Dockerfile
FROM nginx:1.26
LABEL email=zuojie@88.com
RUN apt update && apt install -y vim unzip
ENV webpath=/data/www/html
RUN mkdir -p $webpath
ADD h5-online-mini-game.zip $webpath
RUN ls $webpath
WORKDIR $webpath
RUN unzip h5-online-mini-game.zip && mv h5-online-mini-game h5
ADD default.conf /etc/nginx/conf.d/
VOLUME ["/etc/nginx","$webpath"] IEXPOSE 80/tcp 443/tcp
#ENTRYPOINT /docker-entrypoint.sh
#CMD nginx g daemon off
#通过Dockerfile创建镜像
docker build -t nginx-test .
实例1:构建nginx镜像
javascript
# 1、拉取nginx镜像
docker pull nginx:1.26
docker images
# 创建项目目录和文件
mkdir -p /data/dockerfile/nginx
cd /data/dockerfile/nginx(后续都在这个目录里)
# 2、创建Dockerfile
vim Dockerfile
---------------------------------------
# 使用官方Nginx镜像作为基础镜像
FROM nginx:latest
# 维护者信息(可选)
LABEL maintainer="your-email@example.com"
# 复制自定义Nginx配置文件
COPY default.conf /etc/nginx/conf.d/default.conf
# 复制网站文件到Nginx默认目录
COPY html /usr/share/nginx/html
# 设置工作目录
WORKDIR /usr/share/nginx/html
# 暴露Nginx默认端口
EXPOSE 80
# 使用Nginx默认启动命令(可以省略,因为会继承基础镜像的CMD)
CMD ["nginx", "-g", "daemon off;"]
---------------------------------------
# 3、创建Nginx配置文件
vim default.conf
---------------------------------------
server {
listen 80;
listen [::]:80;
server_name localhost;
# 访问日志设置
access_log /var/log/nginx/host.access.log main;
# 网站根目录
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
# 错误页面配置
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# 健康检查端点
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
---------------------------------------
# 4、创建网站内容
# 创建html目录
mkdir html
# 创建首页(随便写一句话,这个仅供参考)
vim html/index.html
---------------------------------------
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Welcome to My Nginx</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
border-bottom: 2px solid #4CAF50;
padding-bottom: 10px;
}
.info {
background: #e7f3ff;
padding: 15px;
border-left: 4px solid #2196F3;
margin: 20px 0;
}
</style>
</head>
<body>
<div class="container">
<h1>🚀 Welcome to My Custom Nginx</h1>
<p>这是一个基于Docker自定义构建的Nginx服务器</p>
<div class="info">
<h3>📋 服务器信息</h3>
<p><strong>服务器:</strong> Nginx in Docker Container</p>
<p><strong>构建时间:</strong> 2024</p>
<p><strong>状态:</strong> ✅ 运行正常</p>
</div>
<h3>🔗 可用链接</h3>
<ul>
<li><a href="/">首页</a></li>
<li><a href="/health">健康检查</a></li>
</ul>
</div>
</body>
</html>
---------------------------------------
# 5、创建错误页面(可跳过)
vim html/50x.html
---------------------------------------
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
padding: 50px;
}
.error {
color: #d9534f;
font-size: 48px;
}
</style>
</head>
<body>
<div class="error">500</div>
<h2>服务器内部错误</h2>
<p>抱歉,服务器遇到了问题,请稍后重试。</p>
</body>
</html>
---------------------------------------
# 6、构建Docker镜像并验证
docker build -t mynginx:latest .
docker images | grep mynginx
docker history mynginx:latest
# 7、运行容器
# 运行容器(后台模式)
docker run -d \
--name mynginx-container \
-p 80:80 \
mynginx:latest
# 8、测试访问
# 检查容器状态
docker ps
# 查看容器日志
docker logs mynginx-container
# 测试访问(使用浏览器访问IP)
# 9、推送镜像到镜像仓库(补充内容,可以省略)
# 登录到Docker Hub或其他镜像仓库
docker login
# 重新标记镜像(如果需要推送到Docker Hub)
docker tag mynginx:latest yourusername/mynginx:latest
# 推送镜像
docker push yourusername/mynginx:latest
实例2:构建Tomcat应用镜像
html
#1. 创建项目目录和文件
mkdir -p /data/dockerfile/tomcat
cd /data/dockerfile/tomcat
#2. 创建Dockerfile
vim Dockerfile
===========================================
内容如下:
# 使用官方的 Ubuntu 镜像作为基础镜像
FROM ubuntu:22:04
# 设置 JAVA_HOME 环境变量
ENV JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
ENV PATH=$PATH:$JAVA_HOME/bin
# 下载并安装 Tomcat
ARG TOMCAT_VERSION=9.0.87
ENV TOMCAT_VERSION=$TOMCAT_VERSION
# 使用局域网安装包(根据你的实际情况修改IP地址)
RUN wget http://192.168.57.200/Software/apache-tomcat-${TOMCAT_VERSION}.tar.gz -O /tmp/tomcat.tar.gz && \
tar -xvf /tmp/tomcat.tar.gz -C /opt/ && \
ln -s /opt/apache-tomcat-${TOMCAT_VERSION} /opt/tomcat && \
rm /tmp/tomcat.tar.gz
# 设置 CATALINA_HOME 环境变量
ENV CATALINA_HOME=/opt/tomcat
ENV PATH=$PATH:$CATALINA_HOME/bin
# 创建Tomcat用户和组
RUN groupadd -r tomcat && \
useradd -r -g tomcat -d /opt/tomcat -s /bin/false tomcat && \
chown -R tomcat:tomcat /opt/apache-tomcat-${TOMCAT_VERSION} && \
chmod +x /opt/tomcat/bin/*.sh
# 创建webapps目录(确保存在)
RUN mkdir -p /opt/tomcat/webapps
# 复制自定义应用(可选)
# COPY myapp.war /opt/tomcat/webapps/
# 暴露 Tomcat 默认的 HTTP 端口
EXPOSE 8080
# 设置工作目录
WORKDIR /opt/tomcat
# 切换用户(安全考虑)
USER tomcat
# 启动 Tomcat 服务
CMD ["catalina.sh", "run"]
============================================
----------------------------------------------------------------
3. 创建测试应用(中间这几步可以不做,默认丑猫页面)
mkdir webapps
cd webapps
创建简单的测试页面:
# 创建测试WAR应用的目录结构
mkdir -p myapp/WEB-INF
cd myapp
# 创建 index.jsp:
vim index.jsp
<!DOCTYPE html>
<html>
<head>
<title>Tomcat Test Application</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
.container { max-width: 800px; margin: 0 auto; }
.info { background: #e7f3ff; padding: 15px; border-left: 4px solid #2196F3; }
</style>
</head>
<body>
<div class="container">
<h1>🚀 Tomcat Docker 测试应用</h1>
<div class="info">
<h3>服务器信息</h3>
<p><strong>Java 版本:</strong> <%= System.getProperty("java.version") %></p>
<p><strong>Tomcat 版本:</strong> <%= application.getServerInfo() %></p>
<p><strong>当前时间:</strong> <%= new java.util.Date() %></p>
</div>
<h3>测试链接</h3>
<ul>
<li><a href="/">首页</a></li>
<li><a href="/manager">管理界面</a></li>
</ul>
</div>
</body>
</html>
# 创建 WEB-INF/web.xml:
mkdir -p WEB-INF
vim WEB-INF/web.xml
内容:
xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name>Tomcat Test Application</display-name>
<description>
A simple test application for Tomcat Docker image
</description>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
# 打包WAR文件:
# 返回项目根目录
cd /data/dockerfile/tomcat
# 打包WAR文件
jar -cvf myapp.war -C webapps/myapp/ .
# 将WAR文件复制到Docker构建上下文
cp myapp.war ./
4. 修改Dockerfile以包含应用
如果需要包含测试应用,取消注释并修改Dockerfile:
dockerfile
# 在COPY部分取消注释:
COPY myapp.war /opt/tomcat/webapps/
--------------------------------------------------------------
#3. 构建镜像
docker build -t my-tomcat:9.0.87 -t my-tomcat:latest .
# 查看构建的镜像
docker images my-tomcat
#4. 运行容器
# 运行容器(基础版本)
docker run -d --name tomcat-container -p 8080:8080 my-tomcat:latest
#5. 测试访问
# 检查容器状态
docker ps
# 查看容器日志
docker logs tomcat-container
# 测试访问
curl http://localhost:8080
# 如果部署了测试应用,访问:
curl http://localhost:8080/myapp/
#扩展. 使用Docker Compose()
创建 docker-compose.yml:
yaml
version: '3.8'
services:
tomcat:
image: my-tomcat:latest
container_name: tomcat-app
ports:
- "8080:8080"
volumes:
- ./webapps:/opt/tomcat/webapps
- ./logs:/opt/tomcat/logs
environment:
- JAVA_OPTS=-Xms512m -Xmx1024m
restart: unless-stopped
使用Compose运行:
docker-compose up -d
实例3:Docker部署单机小游戏
html
#1. 创建项目目录
mkdir -p /data/games/snake
cd /data/games/snake
#2. 创建游戏文件
vim index.html
----------------------------------------------------------------
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>贪吃蛇游戏</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
font-family: Arial, sans-serif;
}
.game-container {
text-align: center;
background: white;
padding: 30px;
border-radius: 15px;
box-shadow: 0 15px 35px rgba(0,0,0,0.3);
}
h1 {
color: #333;
margin-bottom: 20px;
font-size: 2.5em;
}
canvas {
border: 3px solid #333;
background: #000;
display: block;
margin: 0 auto;
}
.score {
font-size: 28px;
margin: 15px 0;
color: #333;
font-weight: bold;
}
.controls {
margin-top: 20px;
color: #666;
font-size: 16px;
}
.game-over {
color: #e74c3c;
font-size: 24px;
font-weight: bold;
margin: 10px 0;
}
.restart-btn {
padding: 12px 25px;
margin: 15px 0;
border: none;
background: #2ecc71;
color: white;
border-radius: 8px;
cursor: pointer;
font-size: 16px;
font-weight: bold;
transition: background 0.3s;
}
.restart-btn:hover {
background: #27ae60;
}
</style>
</head>
<body>
<div class="game-container">
<h1>🐍 贪吃蛇游戏</h1>
<div class="score">得分: <span id="score">0</span></div>
<div id="gameOver" class="game-over" style="display: none;">游戏结束! 按空格键重新开始</div>
<canvas id="gameCanvas" width="400" height="400"></canvas>
<button class="restart-btn" onclick="resetGame()">重新开始游戏</button>
<div class="controls">
使用 ↑ ↓ ← → 方向键控制蛇的移动<br>
按空格键可以快速重新开始
</div>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreElement = document.getElementById('score');
const gameOverElement = document.getElementById('gameOver');
// 游戏配置
const gridSize = 20;
const tileCount = canvas.width / gridSize;
// 游戏状态
let snake = [];
let food = {};
let dx = 0;
let dy = 0;
let score = 0;
let gameRunning = true;
let gameSpeed = 120; // 毫秒
// 初始化游戏
function initGame() {
snake = [
{x: 10, y: 10},
{x: 9, y: 10},
{x: 8, y: 10}
];
dx = 1; // 初始向右移动
dy = 0;
score = 0;
scoreElement.textContent = score;
gameRunning = true;
gameOverElement.style.display = 'none';
randomFood();
}
// 随机生成食物
function randomFood() {
let newFood;
let onSnake;
do {
newFood = {
x: Math.floor(Math.random() * tileCount),
y: Math.floor(Math.random() * tileCount)
};
onSnake = snake.some(segment =>
segment.x === newFood.x && segment.y === newFood.y
);
} while (onSnake);
food = newFood;
}
// 绘制游戏
function drawGame() {
// 清空画布
ctx.fillStyle = '#1a1a1a';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制网格(可选)
ctx.strokeStyle = '#333';
for (let i = 0; i < tileCount; i++) {
for (let j = 0; j < tileCount; j++) {
ctx.strokeRect(i * gridSize, j * gridSize, gridSize, gridSize);
}
}
// 绘制蛇
snake.forEach((segment, index) => {
if (index === 0) {
// 蛇头
ctx.fillStyle = '#2ecc71';
} else {
// 蛇身
ctx.fillStyle = '#27ae60';
}
ctx.fillRect(segment.x * gridSize, segment.y * gridSize, gridSize - 1, gridSize - 1);
// 蛇身边框
ctx.strokeStyle = '#fff';
ctx.strokeRect(segment.x * gridSize, segment.y * gridSize, gridSize - 1, gridSize - 1);
});
// 绘制食物
ctx.fillStyle = '#e74c3c';
ctx.beginPath();
const centerX = food.x * gridSize + gridSize / 2;
const centerY = food.y * gridSize + gridSize / 2;
const radius = gridSize / 2 - 2;
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
ctx.fill();
}
// 更新游戏状态
function updateGame() {
if (!gameRunning) return;
// 移动蛇
const head = {x: snake[0].x + dx, y: snake[0].y + dy};
// 检查墙壁碰撞
if (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount) {
endGame();
return;
}
// 检查自身碰撞
if (snake.some(segment => segment.x === head.x && segment.y === head.y)) {
endGame();
return;
}
// 添加新的头部
snake.unshift(head);
// 检查是否吃到食物
if (head.x === food.x && head.y === food.y) {
score += 10;
scoreElement.textContent = score;
// 每得50分加速一次
if (score % 50 === 0 && gameSpeed > 50) {
gameSpeed -= 10;
}
randomFood();
} else {
// 没吃到食物,移除尾部
snake.pop();
}
drawGame();
}
// 结束游戏
function endGame() {
gameRunning = false;
gameOverElement.style.display = 'block';
gameOverElement.textContent = `游戏结束! 最终得分: ${score} 按空格键重新开始`;
}
// 重置游戏
function resetGame() {
initGame();
gameSpeed = 120;
drawGame();
}
// 键盘控制
function changeDirection(event) {
// 防止页面滚动
if([32, 37, 38, 39, 40].indexOf(event.keyCode) > -1) {
event.preventDefault();
}
// 空格键重新开始
if (event.keyCode === 32 && !gameRunning) {
resetGame();
return;
}
if (!gameRunning) return;
const keyPressed = event.keyCode;
const goingUp = dy === -1;
const goingDown = dy === 1;
const goingRight = dx === 1;
const goingLeft = dx === -1;
switch(keyPressed) {
case 37: // 左箭头
if (!goingRight) {
dx = -1;
dy = 0;
}
break;
case 38: // 上箭头
if (!goingDown) {
dx = 0;
dy = -1;
}
break;
case 39: // 右箭头
if (!goingLeft) {
dx = 1;
dy = 0;
}
break;
case 40: // 下箭头
if (!goingUp) {
dx = 0;
dy = 1;
}
break;
}
}
// 触摸控制(移动设备)
let touchStartX = 0;
let touchStartY = 0;
function handleTouchStart(event) {
touchStartX = event.touches[0].clientX;
touchStartY = event.touches[0].clientY;
}
function handleTouchMove(event) {
if (!touchStartX || !touchStartY || !gameRunning) return;
const touchEndX = event.touches[0].clientX;
const touchEndY = event.touches[0].clientY;
const dxTouch = touchEndX - touchStartX;
const dyTouch = touchEndY - touchStartY;
// 确定主要滑动方向
if (Math.abs(dxTouch) > Math.abs(dyTouch)) {
// 水平滑动
if (dxTouch > 0 && dx !== -1) {
// 向右
dx = 1;
dy = 0;
} else if (dxTouch < 0 && dx !== 1) {
// 向左
dx = -1;
dy = 0;
}
} else {
// 垂直滑动
if (dyTouch > 0 && dy !== -1) {
// 向下
dx = 0;
dy = 1;
} else if (dyTouch < 0 && dy !== 1) {
// 向上
dx = 0;
dy = -1;
}
}
touchStartX = null;
touchStartY = null;
}
// 事件监听
document.addEventListener('keydown', changeDirection);
canvas.addEventListener('touchstart', handleTouchStart, false);
canvas.addEventListener('touchmove', handleTouchMove, false);
// 防止移动端页面滚动
document.body.addEventListener('touchmove', function(event) {
if (event.target === canvas) {
event.preventDefault();
}
}, false);
// 启动游戏
initGame();
drawGame();
// 游戏主循环
setInterval(updateGame, gameSpeed);
</script>
</body>
</html>
------------------------------------------------------------------
#3. 创建Dockerfile
vim Dockerfile
---------------------------------------------------
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
COPY default.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
---------------------------------------------------
#4. 创建Nginx配置
vim default.conf
---------------------------------------------------
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
---------------------------------------------------
#5. 构建和运行
docker build -t snake-game .
docker run -d --name snake -p 8081:80 snake-game
实例4:使用Dockerfile制作Redis镜像
html
# 1. 创建项目目录
mkdir -p /data/dockerfile/redis
cd /data/dockerfile/redis
# 2. 创建Dockerfile
vim Dockerfile
内容如下:
# 使用官方Redis镜像
FROM redis:7.2-alpine
# 设置时区(可选)
ENV TZ=Asia/Shanghai
# 复制自定义配置文件
COPY redis.conf /usr/local/etc/redis/redis.conf
# 暴露端口
EXPOSE 6379
# 使用自定义配置启动Redis
CMD ["redis-server", "/usr/local/etc/redis/redis.conf"]
# 3. 创建Redis配置文件
vim redis.conf
内容如下:
# Redis基础配置
bind 0.0.0.0
port 6379
# 安全设置
requirepass redis123
# 持久化设置
save 900 1
save 300 10
save 60 10000
# 内存设置
maxmemory 256mb
maxmemory-policy allkeys-lru
# 日志设置
loglevel notice
logfile ""
# 网络设置
tcp-keepalive 300
# 性能调优
timeout 0
tcp-backlog 511
databases 16
# 4. 构建镜像
# 构建Redis镜像
docker build -t redis-custom .
# 查看构建的镜像
docker images
# 5. 运行容器
# 运行Redis容器
docker run -d \
--name redis-server \
-p 6379:6379 \
-v redis_data:/data \
redis-custom
6. 验证连接
# 进入Redis CLI
docker exec -it redis-server redis-cli -a redis123(没设密码就不用-a)
# 或者分步连接
docker exec -it redis-server redis-cli
127.0.0.1:6379> AUTH redis123
127.0.0.1:6379> PING
7. Redis基本操作测试
# 测试Redis功能
# 在Redis CLI中执行:
127.0.0.1:6379> ping # 测试连接
PONG
127.0.0.1:6379> SET mykey "Hello Redis"
127.0.0.1:6379> GET mykey
127.0.0.1:6379> INFO server # 查看Redis信息
127.0.0.1:6379> EXIT
实例5:使用Dockerfile制作MySQL镜像
html
# 1.创建项目目录和文件
mkdir -p /data/dockerfile/mysql
cd /data/dockerfile/mysql
# 2.创建Dockerfile
vim Dockerfile
---------------------------------------------------
内容如下:
# Dockerfile
FROM mysql:8.0.20
# 设置环境变量
ENV MYSQL_ROOT_PASSWORD=root123
ENV MYSQL_DATABASE=test
ENV MYSQL_USER=root
# 暴露端口
EXPOSE 3306
# 使用官方镜像的入口点
CMD ["mysqld"]
---------------------------------------------------
# 3.构建自定义镜像并查看镜像
docker build -t mysql-8.0.20-custom:8.0.20 .
docker images
# 4.运行容器
docker run -d --name mysql-8.0.20 -p 3306:3306 -v mysql_data:/var/lib/mysql mysql-8.0.20-custom:8.0.20
# 5.进入MySQL验证连接
docker exec -it mysql-8.0.20 mysql -uroot -proot123
========================================================================
补充内容:
使用docker-compose(推荐)
# 创建 docker-compose.yml 文件:
version: '3.8'
services:
mysql:
build: .
container_name: mysql-8.0.20
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: default_db
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
- ./my.cnf:/etc/mysql/conf.d/my.cnf
restart: unless-stopped
volumes:
mysql_data:
# 安装docker-compose并运行 # version 1.29.2-1
apt install docker-compose
docker-compose up -d
--------------------------------------
# 如果中间出错可以删了重新构建
# ①删除现有镜像
docker rmi mysql-8.0.20-custom:latest
# 重新构建并指定版本标签
docker build -t mysql-8.0.20-custom:8.0.20 .
# 查看镜像
docker images
##或者
# ②重置数据卷(如果之前删除过旧的数据)
# 删除旧数据卷
docker stop mysql-8.0.20
docker rm mysql-8.0.20
docker volume rm mysql_data
# 重新运行
docker run -d --name mysql-8.0.20 -p 3306:3306 -v mysql_data:/var/lib/mysql mysql-8.0.20-custom:8.0.20