Dockerfile及其部署镜像步骤
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指令。 |
举例:vim DockerfileFROM nginx:1.26LABEL email=zuojie@88.comRUN apt update && apt install -y vim unzipENV webpath=/data/www/htmlRUN mkdir -p $webpathADD h5-online-mini-game.zip $webpathRUN ls $webpathWORKDIR $webpathRUN unzip h5-online-mini-game.zip && mv h5-online-mini-game h5ADD 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镜像
# 1、拉取nginx镜像docker pull nginx:1.26docker images# 创建项目目录和文件mkdir -p /data/dockerfile/nginxcd /data/dockerfile/nginx(后续都在这个目录里)# 2、创建Dockerfilevim 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 mynginxdocker 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应用镜像
#1. 创建项目目录和文件mkdir -p /data/dockerfile/tomcatcd /data/dockerfile/tomcat#2. 创建Dockerfilevim Dockerfile===========================================内容如下:# 使用官方的 Ubuntu 镜像作为基础镜像FROM ubuntu:22:04# 设置 JAVA_HOME 环境变量ENV JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64ENV PATH=$PATH:$JAVA_HOME/bin# 下载并安装 TomcatARG TOMCAT_VERSION=9.0.87ENV 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/tomcatENV 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 webappscd webapps创建简单的测试页面:# 创建测试WAR应用的目录结构mkdir -p myapp/WEB-INFcd 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-INFvim 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:yamlversion: '3.8'services:tomcat:image: my-tomcat:latestcontainer_name: tomcat-appports:- "8080:8080"volumes:- ./webapps:/opt/tomcat/webapps- ./logs:/opt/tomcat/logsenvironment:- JAVA_OPTS=-Xms512m -Xmx1024mrestart: unless-stopped使用Compose运行:docker-compose up -d
实例3:Docker部署单机小游戏
#1. 创建项目目录mkdir -p /data/games/snakecd /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. 创建Dockerfilevim Dockerfile---------------------------------------------------FROM nginx:alpineCOPY index.html /usr/share/nginx/html/index.htmlCOPY default.conf /etc/nginx/conf.d/default.confEXPOSE 80CMD ["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镜像
# 1. 创建项目目录mkdir -p /data/dockerfile/rediscd /data/dockerfile/redis# 2. 创建Dockerfilevim Dockerfile内容如下:# 使用官方Redis镜像FROM redis:7.2-alpine# 设置时区(可选)ENV TZ=Asia/Shanghai# 复制自定义配置文件COPY redis.conf /usr/local/etc/redis/redis.conf# 暴露端口EXPOSE 6379# 使用自定义配置启动RedisCMD ["redis-server", "/usr/local/etc/redis/redis.conf"]# 3. 创建Redis配置文件vim redis.conf内容如下:# Redis基础配置bind 0.0.0.0port 6379# 安全设置requirepass redis123# 持久化设置save 900 1save 300 10save 60 10000# 内存设置maxmemory 256mbmaxmemory-policy allkeys-lru# 日志设置loglevel noticelogfile ""# 网络设置tcp-keepalive 300# 性能调优timeout 0tcp-backlog 511databases 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-custom6. 验证连接# 进入Redis CLIdocker exec -it redis-server redis-cli -a redis123(没设密码就不用-a)# 或者分步连接docker exec -it redis-server redis-cli127.0.0.1:6379> AUTH redis123127.0.0.1:6379> PING7. Redis基本操作测试# 测试Redis功能# 在Redis CLI中执行:127.0.0.1:6379> ping # 测试连接PONG127.0.0.1:6379> SET mykey "Hello Redis"127.0.0.1:6379> GET mykey127.0.0.1:6379> INFO server # 查看Redis信息127.0.0.1:6379> EXIT
实例5:使用Dockerfile制作MySQL镜像
# 1.创建项目目录和文件mkdir -p /data/dockerfile/mysqlcd /data/dockerfile/mysql# 2.创建Dockerfilevim Dockerfile---------------------------------------------------内容如下:# DockerfileFROM mysql:8.0.20# 设置环境变量ENV MYSQL_ROOT_PASSWORD=root123ENV MYSQL_DATABASE=testENV 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.20environment:MYSQL_ROOT_PASSWORD: root123MYSQL_DATABASE: default_dbports:- "3306:3306"volumes:- mysql_data:/var/lib/mysql- ./my.cnf:/etc/mysql/conf.d/my.cnfrestart: unless-stoppedvolumes:mysql_data:# 安装docker-compose并运行 # version 1.29.2-1apt install docker-composedocker-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.20docker rm mysql-8.0.20docker 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