diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..02afd9d --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,5 @@ +.git +.run +bin +**/*.log + diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..35f71b0 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,38 @@ +# +# Multi-stage build for backend API and worker. +# +# Build: +# docker build -f backend/Dockerfile -t feedsystem-backend:api --target api . +# docker build -f backend/Dockerfile -t feedsystem-backend:worker --target worker . +# + +ARG GO_VERSION=1.24.5 + +FROM golang:${GO_VERSION} AS build +WORKDIR /src/backend + +COPY backend/go.mod backend/go.sum ./ +RUN go mod download + +COPY backend/ ./ + +ENV CGO_ENABLED=0 +RUN go build -trimpath -ldflags="-s -w" -o /out/api ./cmd +RUN go build -trimpath -ldflags="-s -w" -o /out/worker ./cmd/worker + +FROM alpine:3.21 AS base +RUN apk add --no-cache ca-certificates tzdata && adduser -D -H -s /sbin/nologin app +WORKDIR /app +COPY --from=build /src/backend/configs ./configs +RUN mkdir -p ./.run/uploads && chown -R app:app /app +USER app + +FROM base AS api +COPY --from=build /out/api /app/api +EXPOSE 8080 +ENTRYPOINT ["/app/api"] + +FROM base AS worker +COPY --from=build /out/worker /app/worker +ENTRYPOINT ["/app/worker"] + diff --git a/backend/configs/config.docker.yaml b/backend/configs/config.docker.yaml new file mode 100644 index 0000000..2ee2181 --- /dev/null +++ b/backend/configs/config.docker.yaml @@ -0,0 +1,22 @@ +server: + port: 8080 + +database: + host: mysql + port: 3306 + user: root + password: 123456 + dbname: feedsystem + +redis: + host: redis + port: 6379 + password: 123456 + db: 0 + +rabbitmq: + host: rabbitmq + port: 5672 + username: admin + password: password123 + diff --git a/docker-compose.yml b/docker-compose.yml index a1dfc3a..5b994a1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,15 +1,106 @@ version: '3.8' services: + mysql: + image: mysql:8.0 + restart: always + environment: + MYSQL_ROOT_PASSWORD: "123456" + MYSQL_DATABASE: "feedsystem" + TZ: "Asia/Shanghai" + ports: + - "3306:3306" + volumes: + - mysql_data:/var/lib/mysql + command: + - --default-authentication-plugin=mysql_native_password + - --character-set-server=utf8mb4 + - --collation-server=utf8mb4_0900_ai_ci + healthcheck: + test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -uroot -p123456 --silent"] + interval: 5s + timeout: 5s + retries: 20 + + redis: + image: redis:7-alpine + restart: always + command: ["redis-server", "--appendonly", "yes", "--requirepass", "123456"] + ports: + - "6379:6379" + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "-a", "123456", "ping"] + interval: 5s + timeout: 3s + retries: 20 + rabbitmq: - image: rabbitmq:3-management # 使用带有管理界面的版本 - container_name: my-rabbitmq + image: rabbitmq:3-management restart: always ports: - - "5672:5672" # 消息队列端口 - - "15672:15672" # 管理后台端口 + - "5672:5672" + - "15672:15672" environment: - - RABBITMQ_DEFAULT_USER=admin - - RABBITMQ_DEFAULT_PASS=password123 + RABBITMQ_DEFAULT_USER: admin + RABBITMQ_DEFAULT_PASS: password123 volumes: - - ./data:/var/lib/rabbitmq # 数据持久化,保存到当前目录下的 data 文件夹 \ No newline at end of file + - rabbitmq_data:/var/lib/rabbitmq + healthcheck: + test: ["CMD-SHELL", "rabbitmq-diagnostics -q ping"] + interval: 5s + timeout: 5s + retries: 20 + + backend: + build: + context: . + dockerfile: backend/Dockerfile + target: api + restart: always + ports: + - "8080:8080" + volumes: + - ./backend/configs/config.docker.yaml:/app/configs/config.yaml:ro + - backend_uploads:/app/.run/uploads + depends_on: + mysql: + condition: service_healthy + redis: + condition: service_healthy + rabbitmq: + condition: service_healthy + + worker: + build: + context: . + dockerfile: backend/Dockerfile + target: worker + restart: always + volumes: + - ./backend/configs/config.docker.yaml:/app/configs/config.yaml:ro + depends_on: + mysql: + condition: service_healthy + redis: + condition: service_healthy + rabbitmq: + condition: service_healthy + + frontend: + build: + context: . + dockerfile: frontend/Dockerfile + restart: always + ports: + - "5173:80" + depends_on: + - backend + +volumes: + mysql_data: + redis_data: + rabbitmq_data: + backend_uploads: + diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000..788c89d --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,6 @@ +.git +node_modules +dist +**/*.log +npm-debug.log* + diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..a9b1526 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,22 @@ +# +# Multi-stage build for Vue (Vite) frontend, served by Nginx. +# +# Build: +# docker build -f frontend/Dockerfile -t feedsystem-frontend . +# + +FROM node:24-alpine AS build +WORKDIR /src + +COPY frontend/package.json frontend/package-lock.json ./ +RUN npm ci + +COPY frontend/ ./ +RUN npm run build + +FROM nginx:1.27-alpine +COPY frontend/nginx.conf /etc/nginx/conf.d/default.conf +COPY --from=build /src/dist /usr/share/nginx/html +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] + diff --git a/frontend/nginx.conf b/frontend/nginx.conf new file mode 100644 index 0000000..5b68c06 --- /dev/null +++ b/frontend/nginx.conf @@ -0,0 +1,23 @@ +server { + listen 80; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + # SPA routing (Vue Router history mode) + location / { + try_files $uri $uri/ /index.html; + } + + # Reverse proxy to backend (strip /api prefix) + location /api/ { + proxy_pass http://backend:8080/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} +