8 Commits
v2.0 ... main

Author SHA1 Message Date
f9d3a9d4ee fix: 时区异常问题
All checks were successful
Build Docker Images / build (map[app_version:v2.0 buggy:false tag:v2.0]) (push) Successful in 1m11s
Build Docker Images / build (map[app_version:v1.0 buggy:false tag:v1.0]) (push) Successful in 1m40s
Build Docker Images / build (map[app_version:v2.1 buggy:false tag:v2.1]) (push) Successful in 1m7s
Build Docker Images / build (map[app_version:v3.0-buggy buggy:true tag:v3.0-buggy]) (push) Successful in 1m47s
2025-12-17 12:58:00 +08:00
aea85d62e4 build: 添加
All checks were successful
Build Docker Images / build (map[app_version:v2.1 buggy:false tag:v2.1]) (push) Successful in 1m3s
Build Docker Images / build (map[app_version:v3.0-buggy buggy:true tag:v3.0-buggy]) (push) Successful in 1m28s
Build Docker Images / build (map[app_version:v1.0 buggy:false tag:v1.0]) (push) Successful in 1m22s
Build Docker Images / build (map[app_version:v2.0 buggy:false tag:v2.0]) (push) Successful in 1m26s
2025-12-17 12:49:56 +08:00
7e2ccfcba0 debug: 测试 Gitea Actions Secrets 是否生效
Some checks failed
Build Docker Images / build (map[app_version:v2.1 buggy:false tag:v2.1]) (push) Waiting to run
Build Docker Images / build (map[app_version:v1.0 buggy:false tag:v1.0]) (push) Failing after 11s
Build Docker Images / build (map[app_version:v2.0 buggy:false tag:v2.0]) (push) Failing after 11s
Build Docker Images / build (map[app_version:v3.0-buggy buggy:true tag:v3.0-buggy]) (push) Failing after 14s
2025-12-17 12:46:37 +08:00
c75e679a92 feat: 精简镜像构架支持
Some checks failed
Build Docker Images / build (map[app_version:v1.0 buggy:false tag:v1.0]) (push) Failing after 10s
Build Docker Images / build (map[app_version:v2.0 buggy:false tag:v2.0]) (push) Failing after 10s
Build Docker Images / build (map[app_version:v2.1 buggy:false tag:v2.1]) (push) Failing after 9s
Build Docker Images / build (map[app_version:v3.0-buggy buggy:true tag:v3.0-buggy]) (push) Failing after 10s
2025-12-17 12:41:50 +08:00
1e4c268a43 feat: 增加 Gitea Actions 支持
Some checks failed
Build Multi-Platform Docker Images / build (map[app_version:v2.0 buggy:false tag:v2.0]) (push) Failing after 2m59s
Build Multi-Platform Docker Images / build (map[app_version:v2.1 buggy:false tag:v2.1]) (push) Failing after 17s
Build Multi-Platform Docker Images / build (map[app_version:v3.0-buggy buggy:true tag:v3.0-buggy]) (push) Failing after 16s
Build Multi-Platform Docker Images / build (map[app_version:v1.0 buggy:false tag:v1.0]) (push) Has been cancelled
2025-12-17 12:37:55 +08:00
27e9a74e4e feat: 美化错误页面 2025-12-17 12:31:16 +08:00
5fdd986a63 doc: 更新自述文档 2025-12-17 12:28:44 +08:00
a1552008de feat: 删除.drone.yml && 采用build.sh手动构建镜像 2025-12-17 12:27:20 +08:00
7 changed files with 224 additions and 139 deletions

View File

@@ -1,73 +0,0 @@
kind: pipeline
type: docker
name: build-by-tag
node:
name: pve
trigger:
event:
- tag
ref:
- refs/tags/v*
steps:
- name: build-and-push-normal
image: plugins/docker
settings:
registry: harbor.seahi.me
repo: harbor.seahi.me/stu/versions-for-swarm
username:
from_secret: harbor_username
password:
from_secret: harbor_password
insecure: true
tags:
- ${DRONE_TAG}
build_args:
- APP_VERSION=${DRONE_TAG}
auto_tag: false
when:
ref:
exclude:
- refs/tags/*buggy*
- name: build-and-push-buggy
image: plugins/docker
settings:
registry: harbor.seahi.me
repo: harbor.seahi.me/stu/versions-for-swarm
username:
from_secret: harbor_username
password:
from_secret: harbor_password
insecure: true
tags:
- ${DRONE_TAG}
build_args:
- APP_VERSION=${DRONE_TAG}
- BUGGY=true
auto_tag: false
when:
ref:
include:
- refs/tags/*buggy*
- name: tag-latest-if-v2.1
image: plugins/docker
settings:
registry: harbor.seahi.me
repo: harbor.seahi.me/stu/versions-for-swarm
username:
from_secret: harbor_username
password:
from_secret: harbor_password
insecure: true
tags:
- latest
build_args:
- APP_VERSION=${DRONE_TAG}
auto_tag: false
when:
ref:
- refs/tags/v2.1

View File

@@ -0,0 +1,53 @@
name: Build Docker Images
on:
push:
branches:
- main
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
version:
- tag: v1.0
app_version: v1.0
buggy: false
- tag: v2.0
app_version: v2.0
buggy: false
- tag: v2.1
app_version: v2.1
buggy: false
- tag: v3.0-buggy
app_version: v3.0-buggy
buggy: true
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Harbor
uses: docker/login-action@v3
with:
registry: harbor.seahi.me
username: ${{ secrets.HARBOR_USERNAME }}
password: ${{ secrets.HARBOR_PASSWORD }}
- name: Build and push ${{ matrix.version.tag }}
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64
push: true
tags: harbor.seahi.me/stu/versions-for-swarm:${{ matrix.version.tag }}
build-args: |
APP_VERSION=${{ matrix.version.app_version }}
BUGGY=${{ matrix.version.buggy }}
cache-from: type=registry,ref=harbor.seahi.me/stu/versions-for-swarm:buildcache
cache-to: type=registry,ref=harbor.seahi.me/stu/versions-for-swarm:buildcache,mode=max

View File

@@ -7,8 +7,7 @@ ARG BUGGY=false
# 设置时区 # 设置时区
RUN apk add --no-cache tzdata && \ RUN apk add --no-cache tzdata && \
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
echo "Asia/Shanghai" > /etc/timezone && \ echo "Asia/Shanghai" > /etc/timezone
apk del tzdata
ENV TZ=Asia/Shanghai \ ENV TZ=Asia/Shanghai \
APP_VERSION=${APP_VERSION} \ APP_VERSION=${APP_VERSION} \
@@ -16,17 +15,15 @@ ENV TZ=Asia/Shanghai \
WORKDIR /app WORKDIR /app
RUN pip install flask COPY requirements.txt .
# COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt
# RUN pip install --no-cache-dir -r requirements.txt
COPY app.py . COPY app.py .
EXPOSE 80 EXPOSE 80
# 添加健康检查v2.0+ 特性) # 添加健康检查v2.0+ 特性)
# 注意v1.0 版本没有 /health 端点,不应添加健康检查 HEALTHCHECK --interval=10s --timeout=3s --start-period=30s --retries=3 \
# HEALTHCHECK --interval=10s --timeout=3s --start-period=5s --retries=3 \ CMD wget -q --spider http://127.0.0.1:80/health 2>&1 || exit 1
# CMD wget --no-verbose --tries=1 --spider http://localhost/health || exit 1
CMD ["python", "app.py"] CMD ["python", "app.py"]

View File

@@ -2,8 +2,6 @@
> 🎓 专为 Docker Swarm 滚动更新与回滚教学设计的多版本应用 > 🎓 专为 Docker Swarm 滚动更新与回滚教学设计的多版本应用
[![Build Status](https://drone.seahi.me/api/badges/Teaching/versions-for-swarm/status.svg)](https://drone.seahi.me/Teaching/versions-for-swarm)
## 📋 项目简介 ## 📋 项目简介
这是一个用于演示 Docker Swarm 滚动更新、回滚和版本管理的教学项目。通过**视觉化的版本差异**(不同颜色主题),学生可以直观地观察到: 这是一个用于演示 Docker Swarm 滚动更新、回滚和版本管理的教学项目。通过**视觉化的版本差异**(不同颜色主题),学生可以直观地观察到:
@@ -20,7 +18,7 @@
| **v1.0** | 🔵 蓝色 | 基础功能 | 初始部署演示 | | **v1.0** | 🔵 蓝色 | 基础功能 | 初始部署演示 |
| **v2.0** | 🟢 绿色 | 新增健康检查、API接口 | 正常升级演示 | | **v2.0** | 🟢 绿色 | 新增健康检查、API接口 | 正常升级演示 |
| **v2.1** | 🟢 绿色 | 性能优化、Bug修复 | 稳定版本 | | **v2.1** | 🟢 绿色 | 性能优化、Bug修复 | 稳定版本 |
| **v3.0-buggy** | 🔴 红色 | ⚠️ 50%概率返回500错误 | 回滚场景演示 | | **v3.0-buggy** | 🔴 红色 | ⚠️ 70%概率返回500错误 | 回滚场景演示 |
## 🚀 快速开始 ## 🚀 快速开始
@@ -29,9 +27,32 @@
```bash ```bash
docker service create \ docker service create \
--replicas 3 \ --replicas 3 \
--name demo-app \ --name s00 \
--publish 9000:80 \ --publish 9000:80 \
--env STUDENT_ID=00 \ --env STUDENT_ID=00 \
--update-delay 10s \
--update-parallelism 1 \
harbor.seahi.me/stu/versions-for-swarm:v1.0 harbor.seahi.me/stu/versions-for-swarm:v1.0
```
### 2⃣ 部署后续版本 (v2.0)
```bash
docker service update \
--image harbor.seahi.me/stu/versions-for-swarm:v2.0 \
s00
```
### 3⃣ 部署稳定版本 (v2.1)
```bash
docker service update \
--image harbor.seahi.me/stu/versions-for-swarm:v2.1 \
s00
```
### 4⃣ 部署故障版本 (v3.0-buggy)
```bash
docker service update \
--image harbor.seahi.me/stu/versions-for-swarm:v3.0-buggy \
s00
```

141
app.py
View File

@@ -1,30 +1,98 @@
from flask import Flask, jsonify
import socket
import os import os
from datetime import datetime
import random import random
import socket
from datetime import datetime
from flask import Flask, jsonify
app = Flask(__name__) app = Flask(__name__)
# 从环境变量读取版本号 # 从环境变量读取版本号
VERSION = os.getenv('APP_VERSION', 'v1.0') VERSION = os.getenv("APP_VERSION", "v1.0")
BUGGY = os.getenv('BUGGY', 'false').lower() == 'true' BUGGY = os.getenv("BUGGY", "false").lower() == "true"
# 版本主题配置 # 版本主题配置
THEMES = { THEMES = {
'v1.0': {'color': '#4A90E2', 'name': '蓝色经典版', 'features': '基础功能'}, "v1.0": {"color": "#4A90E2", "name": "蓝色经典版", "features": "基础功能"},
'v2.0': {'color': '#50C878', 'name': '绿色升级版', 'features': '新增健康检查、API接口'}, "v2.0": {
'v2.1': {'color': '#50C878', 'name': '绿色稳定版', 'features': '性能优化、Bug修复'}, "color": "#50C878",
'v3.0-buggy': {'color': '#E74C3C', 'name': '红色测试版', 'features': '⚠️ 此版本存在已知问题'} "name": "绿色升级版",
"features": "新增健康检查、API接口",
},
"v2.1": {"color": "#50C878", "name": "绿色稳定版", "features": "性能优化、Bug修复"},
"v3.0-buggy": {
"color": "#E74C3C",
"name": "红色测试版",
"features": "⚠️ 此版本存在已知问题",
},
} }
@app.route('/')
@app.route("/")
def hello(): def hello():
# 模拟 v3.0-buggy 的问题 # 模拟 v3.0-buggy 的问题
if BUGGY and random.random() < 0.5: if BUGGY and random.random() < 0.5:
return "💥 服务异常:数据库连接失败", 500 return (
f"""
<!DOCTYPE html>
<html>
<head>
<title>服务异常</title>
<meta charset="utf-8">
<meta http-equiv="refresh" content="3">
<style>
body {{
font-family: Arial;
max-width: 700px;
margin: 50px auto;
padding: 30px;
background: linear-gradient(135deg, #E74C3C22 0%, #E74C3C44 100%);
}}
.error-box {{
background: white;
padding: 40px;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
text-align: center;
border-left: 5px solid #E74C3C;
}}
.error-icon {{
font-size: 4em;
margin-bottom: 20px;
}}
.error-message {{
color: #E74C3C;
font-size: 1.5em;
font-weight: bold;
margin-bottom: 20px;
}}
.info {{
color: #666;
margin: 10px 0;
}}
.auto-refresh {{
color: #999;
font-size: 0.85em;
margin-top: 20px;
}}
</style>
</head>
<body>
<div class="error-box">
<div class="error-icon">💥</div>
<div class="error-message">服务异常:数据库连接失败</div>
<div class="info">版本: {VERSION}</div>
<div class="info">主机: {socket.gethostname()}</div>
<div class="info">学号: s{os.getenv("STUDENT_ID", "00")}</div>
<div class="auto-refresh">🔄 页面每3秒自动刷新</div>
</div>
</body>
</html>
""",
500,
)
theme = THEMES.get(VERSION, THEMES['v1.0']) theme = THEMES.get(VERSION, THEMES["v1.0"])
return f""" return f"""
<!DOCTYPE html> <!DOCTYPE html>
@@ -39,10 +107,10 @@ def hello():
max-width: 700px; max-width: 700px;
margin: 50px auto; margin: 50px auto;
padding: 30px; padding: 30px;
background: linear-gradient(135deg, {theme['color']}22 0%, {theme['color']}44 100%); background: linear-gradient(135deg, {theme["color"]}22 0%, {theme["color"]}44 100%);
}} }}
.header {{ .header {{
background: {theme['color']}; background: {theme["color"]};
color: white; color: white;
padding: 20px; padding: 20px;
border-radius: 10px 10px 0 0; border-radius: 10px 10px 0 0;
@@ -55,13 +123,13 @@ def hello():
box-shadow: 0 4px 6px rgba(0,0,0,0.1); box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}} }}
.highlight {{ .highlight {{
color: {theme['color']}; color: {theme["color"]};
font-weight: bold; font-weight: bold;
font-size: 1.2em; font-size: 1.2em;
}} }}
.version-badge {{ .version-badge {{
display: inline-block; display: inline-block;
background: {theme['color']}; background: {theme["color"]};
color: white; color: white;
padding: 5px 15px; padding: 5px 15px;
border-radius: 20px; border-radius: 20px;
@@ -91,7 +159,7 @@ def hello():
</head> </head>
<body> <body>
<div class="header"> <div class="header">
<h1>🚀 {theme['name']}</h1> <h1>🚀 {theme["name"]}</h1>
<div class="version-badge">版本 {VERSION}</div> <div class="version-badge">版本 {VERSION}</div>
</div> </div>
<div class="info"> <div class="info">
@@ -102,15 +170,15 @@ def hello():
</tr> </tr>
<tr> <tr>
<td>🎓 学号服务</td> <td>🎓 学号服务</td>
<td class="highlight">s{os.getenv('STUDENT_ID', '00')}</td> <td class="highlight">s{os.getenv("STUDENT_ID", "00")}</td>
</tr> </tr>
<tr> <tr>
<td>⏰ 当前时间</td> <td>⏰ 当前时间</td>
<td class="highlight">{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</td> <td class="highlight">{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}</td>
</tr> </tr>
<tr> <tr>
<td>✨ 版本特性</td> <td>✨ 版本特性</td>
<td class="highlight">{theme['features']}</td> <td class="highlight">{theme["features"]}</td>
</tr> </tr>
</table> </table>
<div class="auto-refresh">🔄 页面每3秒自动刷新观察容器变化</div> <div class="auto-refresh">🔄 页面每3秒自动刷新观察容器变化</div>
@@ -119,21 +187,26 @@ def hello():
</html> </html>
""" """
@app.route('/api/version')
@app.route("/api/version")
def version(): def version():
return jsonify({ return jsonify(
'version': VERSION, {
'hostname': socket.gethostname(), "version": VERSION,
'student_id': os.getenv('STUDENT_ID', '00'), "hostname": socket.gethostname(),
'timestamp': datetime.now().isoformat(), "student_id": os.getenv("STUDENT_ID", "00"),
'buggy': BUGGY "timestamp": datetime.now().isoformat(),
}) "buggy": BUGGY,
}
)
@app.route('/health')
@app.route("/health")
def health(): def health():
if BUGGY and random.random() < 0.3: if BUGGY and random.random() < 0.7:
return {'status': 'unhealthy'}, 503 return {"status": "unhealthy"}, 503
return {'status': 'healthy', 'version': VERSION}, 200 return {"status": "healthy", "version": VERSION}, 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80) if __name__ == "__main__":
app.run(host="0.0.0.0", port=80)

View File

@@ -1,25 +1,38 @@
#!/bin/bash #!/bin/bash
REGISTRY="harbor.seahi.me/stu" REGISTRY="harbor.seahi.me/stu"
IMAGE_NAME="whoami-for-swarm" IMAGE_NAME="versions-for-swarm"
PLATFORMS="linux/amd64,linux/386,linux/arm64"
# 构建 v1.0 # 创建并使用支持多平台的 builder如果不存在
docker build --build-arg APP_VERSION=v1.0 \ if ! docker buildx inspect multiplatform-builder >/dev/null 2>&1; then
-t ${REGISTRY}/${IMAGE_NAME}:v1.0 . docker buildx create --name multiplatform-builder --driver docker-container --bootstrap
docker push ${REGISTRY}/${IMAGE_NAME}:v1.0 fi
docker buildx use multiplatform-builder
# 构建 v2.0 echo "🚀 开始构建所有版本..."
docker build --build-arg APP_VERSION=v2.0 \
-t ${REGISTRY}/${IMAGE_NAME}:v2.0 .
docker push ${REGISTRY}/${IMAGE_NAME}:v2.0
# 构建 v2.1 # 并行构建所有版本
docker build --build-arg APP_VERSION=v2.1 \ docker buildx build --platform ${PLATFORMS} --build-arg APP_VERSION=v1.0 \
-t ${REGISTRY}/${IMAGE_NAME}:v2.1 . -t ${REGISTRY}/${IMAGE_NAME}:v1.0 --push \
docker push ${REGISTRY}/${IMAGE_NAME}:v2.1 --cache-from type=registry,ref=${REGISTRY}/${IMAGE_NAME}:buildcache \
--cache-to type=registry,ref=${REGISTRY}/${IMAGE_NAME}:buildcache,mode=max . &
# 构建 v3.0-buggy有问题的版本 docker buildx build --platform ${PLATFORMS} --build-arg APP_VERSION=v2.0 \
docker build --build-arg APP_VERSION=v3.0-buggy --build-arg BUGGY=true \ -t ${REGISTRY}/${IMAGE_NAME}:v2.0 --push \
-t ${REGISTRY}/${IMAGE_NAME}:v3.0-buggy . --cache-from type=registry,ref=${REGISTRY}/${IMAGE_NAME}:buildcache \
docker push ${REGISTRY}/${IMAGE_NAME}:v3.0-buggy --cache-to type=registry,ref=${REGISTRY}/${IMAGE_NAME}:buildcache,mode=max . &
echo "✅ 所有版本构建完成!" docker buildx build --platform ${PLATFORMS} --build-arg APP_VERSION=v2.1 \
-t ${REGISTRY}/${IMAGE_NAME}:v2.1 --push \
--cache-from type=registry,ref=${REGISTRY}/${IMAGE_NAME}:buildcache \
--cache-to type=registry,ref=${REGISTRY}/${IMAGE_NAME}:buildcache,mode=max . &
docker buildx build --platform ${PLATFORMS} --build-arg APP_VERSION=v3.0-buggy --build-arg BUGGY=true \
-t ${REGISTRY}/${IMAGE_NAME}:v3.0-buggy --push \
--cache-from type=registry,ref=${REGISTRY}/${IMAGE_NAME}:buildcache \
--cache-to type=registry,ref=${REGISTRY}/${IMAGE_NAME}:buildcache,mode=max . &
# 等待所有后台任务完成
wait
echo "✅ 所有版本构建完成(支持平台: ${PLATFORMS}"

1
requirements.txt Normal file
View File

@@ -0,0 +1 @@
flask