Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions .env

This file was deleted.

37 changes: 37 additions & 0 deletions .github/workflows/deployment-prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: CD

on:
workflow_call:
workflow_dispatch:

jobs:
deploy:
runs-on: self-hosted
steps:
- name: Add deploy host key
run: |
mkdir -p ~/.ssh
ssh-keygen -R "${{ secrets.CONTAINER_IP }}" 2>/dev/null || true
echo "${{ secrets.DEPLOY_HOST_KEY }}" >> ~/.ssh/known_hosts

- name: SSH and Deploy
env:
HARBOR_PASS: ${{ secrets.HARBOR_PASS }}
HARBOR_REG: ${{ secrets.HARBOR_REG }}
HARBOR_USERNAME: ${{ secrets.HARBOR_USERNAME }}
run: |
ssh deploy-user@${{ secrets.CONTAINER_IP }} -T \
-o SendEnv=HARBOR_PASS \
-o SendEnv=HARBOR_REG \
-o SendEnv=HARBOR_USERNAME \
bash -s << 'EOF'
set -e
cd /home/deploy-user/SeeFT
git fetch origin feat/mitomen/261/CI-CD-Pipeline
git reset --hard origin/feat/mitomen/261/CI-CD-Pipeline
git checkout -B feat/mitomen/261/CI-CD-Pipeline origin/feat/mitomen/261/CI-CD-Pipeline
echo "$HARBOR_PASS" | docker login "$HARBOR_REG" -u "$HARBOR_USERNAME" --password-stdin
docker compose -f docker-compose.cd.yml pull
docker compose -f docker-compose.cd.yml up -d
docker image prune -f
Comment thread
FastDefence marked this conversation as resolved.
EOF
85 changes: 85 additions & 0 deletions .github/workflows/integration-prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: CI

on:
push:
branches:
- main
- feat/mitomen/261/CI-CD-Pipeline
workflow_call:
workflow_dispatch:

jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v6
with:
submodules: recursive
token: ${{ secrets.GH_PAT || github.token }}

- name: Connect to Tailscale
uses: tailscale/github-action@v2
with:
oauth-client-id: ${{ secrets.TAILSCALE_OAUTH_CLIENT_ID }}
oauth-secret: ${{ secrets.TAILSCALE_OAUTH_CLIENT_SECRET }}
tags: tag:ci

- name: Configure Insecure Registry
run: |
sudo service docker stop
echo "{\"insecure-registries\": [\"${{ secrets.HARBOR_REG }}\"]}" | sudo tee /etc/docker/daemon.json
sudo service docker start

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver-opts: network=host
buildkitd-config-inline: |
[registry."${{ secrets.HARBOR_REG }}"]
http = true
insecure = true
Comment thread
FastDefence marked this conversation as resolved.

- name: Log in to Harbor
uses: docker/login-action@v3
with:
registry: ${{ secrets.HARBOR_REG }}
username: ${{ secrets.HARBOR_USERNAME }}
password: ${{ secrets.HARBOR_PASS }}

- name: Create .env file
run: |
mkdir -p mobile/env
printf '%s' "${{ secrets.SEEFT_MOBILE_ENVS }}" > mobile/env/.env

- name: Build and Push API
uses: docker/build-push-action@v5
with:
context: ./api
file: ./api/prod.Dockerfile
push: true
tags: ${{ secrets.HARBOR_REG }}/seeft/api:latest
cache-from: type=gha

- name: Build and Push Admin
uses: docker/build-push-action@v5
with:
context: ./admin
file: ./admin/prod.Dockerfile
push: true
tags: ${{ secrets.HARBOR_REG }}/seeft/admin:latest
cache-from: type=gha

- name: Build and Push Mobile
uses: docker/build-push-action@v5
with:
context: ./mobile
file: ./mobile/Dockerfile
push: true
tags: ${{ secrets.HARBOR_REG }}/seeft/mobile:latest
cache-from: type=gha
Comment on lines +55 to +80
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid latest-only tagging for production deploy artifacts.

Lines 60, 69, and 78 publish only :latest, which makes rollback and incident traceability difficult.

Suggested fix
+      - name: Set image tag
+        run: echo "IMAGE_TAG=${GITHUB_SHA}" >> $GITHUB_ENV
+
       - name: Build and Push API
         uses: docker/build-push-action@v5
         with:
           context: ./api
           file: ./api/prod.Dockerfile
           push: true
-          tags: ${{ secrets.HARBOR_REG }}/seeft/api:latest
+          tags: |
+            ${{ secrets.HARBOR_REG }}/seeft/api:${{ env.IMAGE_TAG }}
+            ${{ secrets.HARBOR_REG }}/seeft/api:latest
           cache-from: type=gha
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/CI.yml around lines 54 - 79, Replace the three image tags
that currently push only :latest in the "Build and Push API", "Build and Push
Admin", and "Build and Push Mobile" steps so artifacts are also tagged with a
stable, unique identifier (e.g., Git SHA or semantic tag) in addition to
:latest; update the tags parameter for the docker/build-push-action calls (the
steps named "Build and Push API", "Build and Push Admin", and "Build and Push
Mobile") to include both ${{ secrets.HARBOR_REG }}/seeft/<service>:latest and a
second tag such as ${{ secrets.HARBOR_REG }}/seeft/<service>:${{ github.sha }}
(or a version variable) so you can trace and roll back specific deployments.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@FastDefence 良い質問です。結論から言うと、SHA タグも付与する場合は、今まで通り GC だけでは古いイメージが自動削除されません


Harbor GC の動作について

Harbor でイメージを削除しても、ストレージはすぐに解放されません。GC を実行することで、マニフェストから参照されなくなったブロブ(blob)がファイルシステムから削除されます。

:latest のみの現状では、以下の流れになります:

  1. 新しいビルドを push すると、:latest タグが新しいマニフェストに移動する
  2. Harbor では同じタグで繰り返し push が可能で、タグは新しいアーティファクトに移動し、タグを失った古いアーティファクトは「タグなし(tagless)」になります。
  3. GC 実行時に delete_untagged = true であれば、タグなし artifact が削除される → 今まで通りの動作

:sha + :latest の両方を付与した場合は、挙動が変わります:

  • :latest は新しいイメージに移動し、旧マニフェストからは外れる
  • しかし旧マニフェストには :sha タグが残ったままのため、「タグなし」にはならない
  • GC はタグが付いているアーティファクトを削除しないため、SHA タグ付きの古いイメージはどんどん蓄積します

解決策

SHA タグ付きイメージを自動的に削除するには、Tag Retention Rules(タグ保持ポリシー) を Harbor の UI で設定する必要があります。

リポジトリには多数のイメージタグが急速に蓄積されることがあり、それらは大量のストレージを消費します。

Harbor システム管理者は、リポジトリに対して特定のタグをどれだけ保持するかのルールを定義できます。ルールはプロジェクト単位ではなくリポジトリ単位で定義します。

設定例:「最新 N 件のタグのみ保持」というルールを各リポジトリに設定することで、古い SHA タグのイメージを自動削除できます。

⚠️ ただし、既知の問題として、複数のタグが同じ SHA ダイジェストを参照している場合、削除対象のタグのサブセットが存在すると、残りのタグもすべて削除されてしまうことがあります。そのため、この場合はすべてのタグが保持される可能性があります。


まとめ

タグ構成 GC だけで古いイメージが削除されるか
:latest のみ(現状) delete_untagged=true で削除される
:sha + :latest ❌ SHA タグが残るため GC では削除されない → Tag Retention Rules が別途必要

SHA タグを導入するのであれば、Harbor の 「Tag Retention」 機能で「最新 N 件のみ保持」ポリシーを設定することを推奨します。


🧠 Learnings used
Learnt from: FastDefence
Repo: NUTFes/SeeFT PR: 262
File: .github/workflows/CI.yml:27-40
Timestamp: 2026-04-18T19:58:59.501Z
Learning: For the SeeFT CI workflow at .github/workflows/CI.yml, allow the Harbor registry URL to be configured with an insecure/HTTP scheme in the CI job because all registry communication occurs via the Tailscale VPN (WireGuard-encrypted overlay). Do not raise a security finding solely due to insecure registry settings in this workflow, as the traffic is not exposed outside the encrypted tunnel.


trigger-cd:
needs: build-and-push
uses: ./.github/workflows/deployment-prod.yml
secrets: inherit
43 changes: 43 additions & 0 deletions docker-compose.cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
version: "3"
services:
cloudflare:
image: "cloudflare/cloudflared:latest"
container_name: "nutfes-seeft-web"
volumes: ["./web/prod:/home/nonroot/.cloudflared"]
command: tunnel run

mobile:
image: ${HARBOR_REG}/seeft/mobile:latest
container_name: "nutfes-seeft-mobile"
command: "python3 ./python/server.py"
ports: ["45029:45029"]
environment:
NEXT_PUBLIC_APP_ENV: "production"
depends_on: ["api"]
restart: always

api:
image: ${HARBOR_REG}/seeft/api:latest
container_name: "nutfes-seeft-api"
volumes: ["./api:/env/app"]
command: sh -c "go build -o main main.go && ./main"
Comment on lines +22 to +23
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don’t compile Go at container startup in CD compose.

Lines 22-23 bind-mount source and run go build on startup, which breaks immutable-deploy assumptions and can fail if runtime image lacks toolchain.

Suggested direction
   api:
     image: ${HARBOR_REG}/seeft/api:latest
     container_name: "nutfes-seeft-api"
-    volumes: ["./api:/env/app"]
-    command: sh -c "go build -o main main.go && ./main"
+    # rely on the image artifact built in CI
+    # command should be provided by Dockerfile CMD/ENTRYPOINT
     env_file: ["./api/env/seeft.env"]
     ports: ["1234:1234"]
     restart: always
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.cd.yml` around lines 22 - 23, The docker-compose.cd.yml
currently bind-mounts the source (volumes: ["./api:/env/app"]) and runs a build
at container start (command: sh -c "go build -o main main.go && ./main"), which
violates immutable deploys and requires the Go toolchain at runtime; remove the
bind-mount and replace the runtime command so the container runs the prebuilt
artifact produced by CI (e.g., drop volumes: ["./api:/env/app"] and change the
command to just run the compiled binary such as ./main or rely on the image's
CMD), and ensure the CI pipeline builds a multi-stage Docker image that contains
the compiled binary referenced by this compose file instead of compiling in the
container.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@FastDefence はい、まさにその通りです!マルチステージビルドを使って prod.Dockerfile 内でコンパイルするのがベストプラクティスです。


なぜ Dockerfile 内でビルドすべきか

現状の問題 Dockerfile 内でビルドした場合
ランタイムイメージに Go ツールチェーンが必要 最終イメージに Go ツールチェーン不要(軽量化)
起動のたびにビルドが走り、起動が遅い CI でビルド済みのバイナリを実行するだけで高速
ソースコードをサーバにマウントする必要がある イメージが自己完結(immutable deploy)

api/prod.Dockerfile の修正例(マルチステージビルド)

# --- ビルドステージ ---
FROM golang:1.23-alpine AS builder
WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN go build -o main main.go

# --- ランタイムステージ ---
FROM alpine:3.20
WORKDIR /app

COPY --from=builder /app/main .

EXPOSE 1234
CMD ["./main"]

docker-compose.cd.yml の修正例

  api:
    image: ${HARBOR_REG}/seeft/api:latest
    container_name: "nutfes-seeft-api"
    # volumes と command を削除 — Dockerfile の CMD に委ねる
    env_file: ["./api/env/seeft.env"]
    ports: ["1234:1234"]
    restart: always

volumes: ["./api:/env/app"]command: sh -c "go build ..." を削除することで、CI でビルドされたイメージをそのまま実行するだけになります。seeft.env は引き続きサーバ上のファイルから読み込まれる設計のままです(既存の学習内容と一致)。

(ノ´ヮ´)ノ*:・゚✧


🧠 Learnings used
Learnt from: FastDefence
Repo: NUTFes/SeeFT PR: 262
File: docker-compose.cd.yml:24-24
Timestamp: 2026-04-18T19:42:09.584Z
Learning: In the SeeFT repository, `api/env/seeft.env` is intentionally NOT tracked in git. It is a production secret environment file that is manually provisioned directly on the deployment server (`/home/deploy-user/SeeFT`). The `docker-compose.cd.yml` file references it via `env_file: ["./api/env/seeft.env"]` and this is by design — do not flag it as a missing or untracked file in future reviews.

env_file: ["./api/env/seeft.env"]
Comment thread
coderabbitai[bot] marked this conversation as resolved.
ports: ["1234:1234"]
restart: always

admin:
image: ${HARBOR_REG}/seeft/admin:latest
container_name: "nutfes-seeft-admin"
command: sh -c "npm install && npm run build && npm run start"
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid runtime npm install/build in production container start command.

Line 31 makes startup dependent on live package install/build, increasing failure risk and startup time. Prefer running only the prebuilt app command from the image.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.cd.yml` at line 31, The service command currently runs "sh -c
\"npm install && npm run build && npm run start\"" at container start; change it
to only run the prebuilt start command (e.g., "npm run start" or the production
node entrypoint) and ensure the image is built with dependencies and the
production build in the Dockerfile/CI pipeline instead of installing/building at
runtime; update the docker-compose service to remove "npm install" and "npm run
build" from the command and verify the Dockerfile produces the built app and
proper CMD/ENTRYPOINT so the compose file just invokes the runtime start
command.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find prod.Dockerfile for admin
fd -i "prod.Dockerfile" --exec echo {}
fd -i "Dockerfile" --exec echo {}

Length of output: 147


🏁 Script executed:

#!/bin/bash
cat ./admin/prod.Dockerfile
echo "---"
cat ./admin/Dockerfile

Length of output: 283


@FastDefence はい、Dockerfile内でビルドするのが正しいアプローチです。

現在の admin/prod.Dockerfile を確認すると、npm installnpm run build が実行されておらず、CMD/ENTRYPOINT も設定されていないため、docker-compose 側でそれらを補っている状態になっています。

以下のように prod.DockerfileRUNCMD を追加することを推奨します:

# admin/prod.Dockerfile
FROM node:16.13.0

WORKDIR /app/next-project/seeft-admin
COPY ./ /app

ENV NEXT_PUBLIC_APP_ENV production

# ← ここでビルドまで完結させる
RUN npm ci && npm run build

CMD ["npm", "run", "start"]

そして docker-compose.cd.ymladmin サービスから command を削除します:

admin:
  image: ${HARBOR_REG}/seeft/admin:latest
  container_name: "nutfes-seeft-admin"
  # command は不要(DockerfileのCMDが使われる)
  ports: ["5000:3000"]
  environment:
    NEXT_PUBLIC_APP_ENV: "production"
  stdin_open: true
  tty: true
  depends_on: ["api"]
  restart: always

この変更のメリット:

現在(runtime build) 改善後(image build)
起動時間 遅い(install+buildが毎回走る) 速い(起動のみ)
障害リスク npm registry障害で起動失敗の可能性 イメージ作成時に完結
再現性 コンテナ毎に差異が出うる CIでビルドしたものがそのまま動く
イメージサイズ ビルドキャッシュが残る multi-stage buildでさらに削減可能

なお、api サービスの command: sh -c "go build -o main main.go && ./main" も同様の問題があります。api/prod.Dockerfile にも RUN go build -o main main.goCMD ["./main"] を追加することをお勧めします。


🧠 Learnings used
Learnt from: FastDefence
Repo: NUTFes/SeeFT PR: 262
File: docker-compose.cd.yml:24-24
Timestamp: 2026-04-18T19:42:09.584Z
Learning: In the SeeFT repository, `api/env/seeft.env` is intentionally NOT tracked in git. It is a production secret environment file that is manually provisioned directly on the deployment server (`/home/deploy-user/SeeFT`). The `docker-compose.cd.yml` file references it via `env_file: ["./api/env/seeft.env"]` and this is by design — do not flag it as a missing or untracked file in future reviews.

ports:
- "5000:3000"
environment:
NEXT_PUBLIC_APP_ENV: "production"
stdin_open: true
tty: true
depends_on: [api]
restart: always

networks:
default:
name: seeft-default