init: 增加基础代码
This commit is contained in:
commit
9c6fc2e1db
46
.gitea/workflows/build.yaml
Normal file
46
.gitea/workflows/build.yaml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
name: Build and Push Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- v**
|
||||||
|
- V**
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY_HOST: gitea.proxy.liyanyan.work
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout env
|
||||||
|
run: |
|
||||||
|
echo "目标名称:${{ gitea.ref_name }}"
|
||||||
|
|
||||||
|
TAG_NAME=${{ gitea.ref_name }}
|
||||||
|
IMAGE_TAG="${REGISTRY_HOST}/${{ gitea.repository }}:${TAG_NAME}"
|
||||||
|
echo "IMAGE_TAG=${IMAGE_TAG}"
|
||||||
|
IMAGE_LATEST_TAG="${REGISTRY_HOST}/${{ gitea.repository }}:latest"
|
||||||
|
echo "IMAGE_LATEST_TAG=${IMAGE_LATEST_TAG}"
|
||||||
|
|
||||||
|
if [ -z "${{ secrets.REGISTRY_USER }}" ]; then echo "REGISTRY_USER is empty"; else echo "REGISTRY_USER is set"; fi
|
||||||
|
if [ -z "${{ secrets.REGISTRY_PASSWORD }}" ]; then echo "REGISTRY_PASSWORD is empty"; else echo "REGISTRY_PASSWORD is set"; fi
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: https://gitea.proxy.liyanyan.work/github/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ gitea.ref_name }}
|
||||||
|
|
||||||
|
- name: Build Docker image
|
||||||
|
run: |
|
||||||
|
docker build -t "${IMAGE_TAG}" -t "${IMAGE_LATEST_TAG}" .
|
||||||
|
|
||||||
|
- name: Docker login
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login "${REGISTRY_HOST}" -u "${{ secrets.REGISTRY_USER }}" --password-stdin
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
run: |
|
||||||
|
docker push ${IMAGE_TAG}
|
||||||
|
docker push ${IMAGE_LATEST_TAG}
|
||||||
64
.gitea/workflows/dispatch.yaml
Normal file
64
.gitea/workflows/dispatch.yaml
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
myChoice:
|
||||||
|
description: '请选择分支'
|
||||||
|
type: choice
|
||||||
|
required: true
|
||||||
|
options:
|
||||||
|
- branch
|
||||||
|
- tag
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Explore-Gitea-Actions:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
env:
|
||||||
|
# 手动设置
|
||||||
|
MY_CHOICE: ${{ inputs.myChoice }}
|
||||||
|
REGISTRY_HOST: gitea.proxy.liyanyan.work
|
||||||
|
|
||||||
|
# 内置
|
||||||
|
GITEA_REPO: ${{ gitea.repository }}
|
||||||
|
GITEA_REF_NAME: ${{ gitea.ref_name }}
|
||||||
|
GITEA_SERVER_URL: ${{ gitea.server_url }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- run: |
|
||||||
|
echo MY_CHOICE: ${MY_CHOICE};
|
||||||
|
echo REGISTRY_HOST: ${REGISTRY_HOST};
|
||||||
|
echo GITEA_REPO: ${GITEA_REPO};
|
||||||
|
echo GITEA_REF_NAME: ${GITEA_REF_NAME};
|
||||||
|
echo GITEA_SERVER_URL: ${GITEA_SERVER_URL};
|
||||||
|
|
||||||
|
- name: Checkout env
|
||||||
|
run: |
|
||||||
|
echo "目标名称:${GITEA_REF_NAME}"
|
||||||
|
|
||||||
|
if [ "${MY_CHOICE}" = "tag" ]; then
|
||||||
|
TAG_NAME="${GITEA_REF_NAME}"
|
||||||
|
else
|
||||||
|
TAG_NAME="latest"
|
||||||
|
fi
|
||||||
|
|
||||||
|
IMAGE_TAG="${REGISTRY_HOST}/${GITEA_REPO}:${TAG_NAME}"
|
||||||
|
echo "IMAGE_TAG=${IMAGE_TAG}"
|
||||||
|
|
||||||
|
if [ -z "${{ secrets.REGISTRY_USER }}" ]; then echo "REGISTRY_USER is empty"; else echo "REGISTRY_USER is set"; fi
|
||||||
|
if [ -z "${{ secrets.REGISTRY_PASSWORD }}" ]; then echo "REGISTRY_PASSWORD is empty"; else echo "REGISTRY_PASSWORD is set"; fi
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: https://gitea.proxy.liyanyan.work/github/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ GITEA_REF_NAME }}
|
||||||
|
|
||||||
|
- name: Build Docker image
|
||||||
|
run: |
|
||||||
|
docker build -t "${IMAGE_TAG}" .
|
||||||
|
|
||||||
|
- name: Docker login
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login "${REGISTRY_HOST}" -u "${{ secrets.REGISTRY_USER }}" --password-stdin
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
run: |
|
||||||
|
docker push ${IMAGE_TAG}
|
||||||
8
Dockerfile
Normal file
8
Dockerfile
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
FROM nginx:alpine
|
||||||
|
|
||||||
|
# Copy our default server config; site content is not baked into the image.
|
||||||
|
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
109
README.md
Normal file
109
README.md
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
### 项目说明
|
||||||
|
|
||||||
|
本项目用于构建通用的静态web服务docker模版,纯静态web服务可以使用本项目产出的docker镜像快速搭建稳定的线上服务。
|
||||||
|
|
||||||
|
|
||||||
|
### 原理说明
|
||||||
|
|
||||||
|
每次创建新 tag 时触发工作流,同时发布 tag名和latest两个镜像版本到gitea自动的docker管理平台。
|
||||||
|
使用Dockerfile构建基于nginx的镜像,具体使用中,可以宿主机 default.conf / nginx 工作目录到宿主机,宿主机只需要首次部署时构建 docker-compose,后续部署只需替换nginx目录即可。
|
||||||
|
|
||||||
|
|
||||||
|
### 用法说明
|
||||||
|
|
||||||
|
#### 设计约束
|
||||||
|
|
||||||
|
- 基础镜像:nginx:alpine(体积小)
|
||||||
|
- 不考虑 TLS(HTTPS 请由外部反向代理/网关处理)
|
||||||
|
- 站点内容不随镜像打包,运行时挂载 `./site` 到容器 `/usr/share/nginx/html`
|
||||||
|
|
||||||
|
#### 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├─ Dockerfile
|
||||||
|
├─ nginx/
|
||||||
|
│ └─ default.conf # Nginx 静态站点配置(可按项目调整)
|
||||||
|
├─ example/
|
||||||
|
│ ├─ docker-compose.yml # 本地/线上均可复用的示例
|
||||||
|
│ └─ site/ # 示例静态资源(仅本地验证用)
|
||||||
|
└─ .gitea/workflows/
|
||||||
|
└─ build.yml # 基于 tag 的构建与推送
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 本地构建镜像
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t static-web:latest .
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 使用 docker 命令启动服务
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 在仓库根目录下准备你的站点目录
|
||||||
|
mkdir -p site && echo '<h1>Hello</h1>' > site/index.html
|
||||||
|
|
||||||
|
docker run -d \
|
||||||
|
--name static-web \
|
||||||
|
-p 8080:80 \
|
||||||
|
-v "$(pwd)/nginx":/etc/nginx/conf.d:ro \
|
||||||
|
-v "$(pwd)/site":/usr/share/nginx/html:ro \
|
||||||
|
static-web:latest
|
||||||
|
|
||||||
|
# 访问: http://localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### 使用 docker-compose.yml 命令启动服务
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd example
|
||||||
|
# 可直接使用示例站点:example/site/index.html
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# 访问: http://localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
### 版本与发布
|
||||||
|
|
||||||
|
- 推荐使用语义化标签发布:例如 `v1.0.0`、`v1.0.1`。
|
||||||
|
- 推送 tag 后,CI 会构建并推送两个镜像标签:`<tag>` 与 `latest`。
|
||||||
|
|
||||||
|
### CI/CD(Gitea Actions)
|
||||||
|
|
||||||
|
工作流文件:`.gitea/workflows/build.yml`
|
||||||
|
|
||||||
|
- 触发条件:push tag(匹配 `v*`)。
|
||||||
|
- 必需 secrets(在仓库设置中配置):
|
||||||
|
- `REGISTRY_HOST`:Docker Registry 主机(例如 `registry.example.com`)。
|
||||||
|
- `REGISTRY_USERNAME` / `REGISTRY_PASSWORD`:用于登录 Registry 的凭据。
|
||||||
|
- `REGISTRY_IMAGE`:完整镜像名(例如 `registry.example.com/org/static-web`)。
|
||||||
|
- Runner 需具备 Docker 执行环境;`runs-on` 使用的标签可按你的 Runner 调整。
|
||||||
|
|
||||||
|
本地验证镜像成功推送后,线上 `docker-compose.yml` 可使用远程镜像:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: registry.example.com/org/static-web:latest
|
||||||
|
ports:
|
||||||
|
- "8080:80"
|
||||||
|
volumes:
|
||||||
|
- ./nginx:/etc/nginx/conf.d:ro
|
||||||
|
- ./site:/usr/share/nginx/html:ro
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nginx 配置要点
|
||||||
|
|
||||||
|
- 根目录:`/usr/share/nginx/html`(通过卷挂载提供内容)。
|
||||||
|
- Gzip:对 CSS/JS/JSON/SVG 等启用压缩。
|
||||||
|
- 缓存:对静态资源(js/css/img/fonts)设置长缓存;HTML 禁止缓存。
|
||||||
|
- 安全:默认开启 `X-Content-Type-Options`、`X-Frame-Options`、`Referrer-Policy`。`CSP` 默认注释,如需请按项目定制。
|
||||||
|
- SPA 场景:若需前端路由降级到 `index.html`,可将 `location /` 中的 `try_files` 调整为 `try_files $uri /index.html;`。
|
||||||
|
|
||||||
|
### 常见问题
|
||||||
|
|
||||||
|
- 端口冲突:修改 `-p 8080:80` 映射或释放宿主机端口。
|
||||||
|
- 权限问题:确保宿主机 `./nginx` 与 `./site` 对 Runner/部署主机可读;挂载只读可避免误改。
|
||||||
|
- 缓存过期:生产环境建议为静态资源加内容哈希;或调整 `Cache-Control` 策略。
|
||||||
|
- CI 无法登录 Registry:核对 `REGISTRY_HOST`、用户名/密码与网络可达性,Runner 需具备 `docker login` 能力。
|
||||||
12
example/docker-compose.yml
Normal file
12
example/docker-compose.yml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
version: "3"
|
||||||
|
|
||||||
|
services:
|
||||||
|
static-web:
|
||||||
|
image: gitea.proxy.liyanyan.work/liyy/static-web:latest
|
||||||
|
container_name: static-web
|
||||||
|
ports:
|
||||||
|
- "8080:80"
|
||||||
|
volumes:
|
||||||
|
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:rw
|
||||||
|
- ./site:/usr/share/nginx/html:ro
|
||||||
|
restart: unless-stopped
|
||||||
17
example/site/index.html
Normal file
17
example/site/index.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>静态站点示例</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif; margin: 2rem; }
|
||||||
|
.tag { background: #eef2ff; color: #3730a3; padding: 2px 6px; border-radius: 4px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>静态站点运行正常 ✅</h1>
|
||||||
|
<p>这是用于本地验证的示例页面。你可以把自己的静态资源挂载到 <code>./site</code>。</p>
|
||||||
|
<p>容器内访问路径为 <span class="tag">/usr/share/nginx/html</span>。</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
47
nginx/default.conf
Normal file
47
nginx/default.conf
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# Minimal static site config (no TLS). Site is mounted at runtime.
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
# Static root (mounted from host: ./site -> /usr/share/nginx/html)
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# Gzip for common text types
|
||||||
|
gzip on;
|
||||||
|
gzip_types text/plain text/css application/javascript application/json application/xml image/svg+xml;
|
||||||
|
gzip_min_length 1024;
|
||||||
|
gzip_comp_level 5;
|
||||||
|
|
||||||
|
# Security headers (adjust per project needs)
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||||
|
# add_header Content-Security-Policy "default-src 'self'" always; # Consider tailoring CSP; may break 3rd-party assets
|
||||||
|
|
||||||
|
# Default: serve files directly; 404 if missing
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Long cache for static assets by extension
|
||||||
|
location ~* \.(?:ico|css|js|mjs|gif|jpe?g|png|svg|webp|woff2?|ttf)$ {
|
||||||
|
expires 30d;
|
||||||
|
access_log off;
|
||||||
|
add_header Cache-Control "public, max-age=2592000, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Avoid caching HTML to ensure fresh content
|
||||||
|
location ~* \.(?:html)$ {
|
||||||
|
expires -1;
|
||||||
|
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Optional: enable basic status (restrict in production)
|
||||||
|
# location /__status {
|
||||||
|
# stub_status;
|
||||||
|
# allow 127.0.0.1;
|
||||||
|
# deny all;
|
||||||
|
# }
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user