Skip to content
Merged
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
123 changes: 13 additions & 110 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,6 @@

이 디렉토리는 CM-ANT 프로젝트의 GitHub Actions 워크플로우를 포함합니다.

## 배경 설명

### Docker Hub 다이제스트 관리 전략

Docker Hub에서 **하나의 태그에 여러 digest가 존재하는 것은 의도된 기능**입니다. 이는 멀티 플랫폼 지원, 이력 관리, 롤백 기능을 위한 정상적인 설계입니다.

**현재 상황**:
- Docker Hub의 `v0.4.0` 태그에 2개 이상의 digest 존재 (정상)
- `pull_policy: always` 설정에도 불구하고 이전 digest의 이미지가 pull될 수 있음
- Docker 클라이언트가 첫 번째 digest를 우선 선택하지만, 순서가 최신순이 아닐 수 있음

**해결 전략**:

#### 1. 기본 전략 (권장)
- **mayfly의 `--force` 옵션**: Docker Hub API로 digest 정보 확인 후 최신 digest로 명시적 pull
- **digest 기반 설치**: 태그 대신 digest로 설치하여 정확한 버전 보장
- **기존 호환성 유지**: 기본 동작은 기존과 동일하게 유지

**사용법**:
```bash
# 전체 서비스 업데이트 (digest 기반)
mayfly infra update --force

# 특정 서비스만 업데이트 (digest 기반)
mayfly infra update --force -s cm-ant

# 기존 방식 (기본 동작)
mayfly infra update -s cm-ant
```

**동작 방식**:
1. **docker-compose.yaml 파싱**: 실제 이미지 정보 추출
2. **Docker Hub API 호출**: 각 태그의 digest 목록 조회
3. **최신 digest 선택**: `last_pushed` 시간 기준으로 정렬
4. **digest 기반 pull**: `image@digest` 형태로 명시적 pull
5. **태그 재할당**: 원본 태그로 재태깅하여 호환성 보장

#### 2. 보조 전략 (현재 구현)
- **하나의 태그 = 하나의 digest** 원칙 적용
- 기존 Docker 이미지 삭제 후 새 이미지 생성
- 깔끔한 다이제스트 관리로 예측 가능한 동작 보장

**이러한 배경으로 Docker 이미지 삭제 및 재생성 기능이 보조 수단으로 제공됩니다.**

## 기존 워크플로우

Expand All @@ -62,31 +19,8 @@ mayfly infra update -s cm-ant

## 새로운 워크플로우

### 3. Delete Docker Image
- **파일**: `delete-docker-image.yaml`
- **트리거**: 수동 실행 (`workflow_dispatch`)
- **기능**: Docker Hub에서 특정 태그의 이미지를 삭제

#### 사용법:
1. GitHub 리포지토리의 **Actions** 탭으로 이동
2. **Delete Docker Image** 워크플로우 선택
3. **Run workflow** 버튼 클릭
4. 입력값 설정:
- `tag_name`: 삭제할 태그명 (예: `v0.4.0`, `0.4.0`)
- `confirm_delete`: `DELETE` 입력 (확인용)

#### 특징:
- ✅ **안전한 삭제**: 태그 존재 여부 확인 후 삭제
- ✅ **삭제 검증**: 삭제 후 검증 단계 포함
- ✅ **상세한 로그**: 삭제 과정의 모든 단계 표시
- ✅ **권한 처리**: Personal Access Token 우선 사용

#### 사용 시기:
- Docker Hub에서 잘못된 이미지가 업로드되었을 때
- 태그 충돌을 해결하고 싶을 때
- 수동으로 특정 태그를 정리하고 싶을 때

### 4. Rebuild Docker Image
### 3. Rebuild Docker Image
- **파일**: `retag-release.yaml`
- **트리거**: 수동 실행 (`workflow_dispatch`)
- **기능**: 기존 Git 태그 위치는 그대로 유지하고 Docker 이미지만 재빌드
Expand All @@ -103,14 +37,12 @@ mayfly infra update -s cm-ant
- ✅ **Git 태그 위치 유지**: 기존 커밋 위치 그대로 유지
- ✅ **Docker 이미지만 재빌드**: 같은 코드로 새로운 이미지 생성
- ✅ **안전한 작업**: Git 히스토리에 영향 없음
- ✅ **Digest 충돌 해결**: 기존 Docker 이미지만 삭제

#### 사용 시기:
- Docker Hub에서 같은 태그에 여러 digest가 존재할 때
- Docker 이미지 빌드 과정에서 문제가 있었을 때
- Git 태그 위치는 유지하되 Docker 이미지만 새로 만들고 싶을 때

### 5. Move Tag to Latest Commit ⚠️
### 4. Move Tag to Latest Commit ⚠️
- **파일**: `force-rebuild.yaml`
- **트리거**: 수동 실행 (`workflow_dispatch`)
- **기능**: Git 태그를 현재 HEAD 커밋으로 이동하고 Docker 이미지 재빌드
Expand All @@ -130,7 +62,7 @@ mayfly infra update -s cm-ant
- **복구 불가**: 한번 실행하면 이전 태그 위치로 복구하기 어려움

#### 특징:
- **완전한 재태깅**: Git 태그와 Docker Hub 태그를 모두 삭제 후 재생성
- **완전한 재태깅**: Git 태그를 현재 HEAD로 이동 후 Docker 이미지 재생성
- **최신 코드 반영**: 현재 HEAD 커밋의 모든 변경사항이 반영됨
- **소스-이미지 동기화**: Git 태그 위치와 Docker 이미지 내용이 완벽히 일치

Expand All @@ -141,28 +73,21 @@ mayfly infra update -s cm-ant

## 문제 해결 시나리오

### 시나리오 1: Docker Hub에서 같은 태그에 여러 digest 존재
```bash
# 문제: v0.4.0 태그가 두 개의 다른 digest를 가리킴
# 해결: Rebuild Docker Image 워크플로우 사용
# 결과: Git 태그 위치는 유지, Docker 이미지만 새로 생성
```

### 시나리오 2: 최신 코드를 특정 태그에 반영하고 싶음
### 시나리오 1: 최신 코드를 특정 태그에 반영하고 싶음
```bash
# 문제: 최신 커밋들을 v0.4.0 태그에 반영하고 싶음
# 해결: Move Tag to Latest Commit 워크플로우 사용
# 주의: Git 태그 위치가 변경됨 (기존 참조 깨질 수 있음)
```

### 시나리오 3: 일반적인 새 릴리즈
### 시나리오 2: 일반적인 새 릴리즈
```bash
# 방법: 기존 CD 워크플로우 사용
git tag v0.4.1
git push origin v0.4.1
```

### 시나리오 4: Docker 이미지 빌드 문제 해결
### 시나리오 3: Docker 이미지 빌드 문제 해결
```bash
# 문제: Docker 이미지 빌드 과정에서 문제 발생
# 해결: Rebuild Docker Image 워크플로우 사용
Expand All @@ -177,21 +102,6 @@ git push origin v0.4.1
- `DOCKER_USERNAME`: Docker Hub 사용자명
- `DOCKER_PASSWORD`: Docker Hub 비밀번호 (이미지 생성용)

### Personal Access Tokens (PAT)
- `DOCKER_PAT`: Docker Hub Personal Access Token (이미지 삭제용)
- `CR_PAT`: GitHub Container Registry Personal Access Token
- `UPDATE_SWAGGER_DOC_PAT`: Swagger 문서 업데이트용 PAT
- `CB_GITHUB_ROBOT_PAT`: GitHub Robot용 PAT

### Docker Hub PAT 생성 방법
1. **Docker Hub 로그인** → **Account Settings** → **Security**
2. **"New Access Token"** 클릭
3. **권한 설정**: `Read, Write, Delete` 권한 부여
4. **토큰 생성** 후 GitHub Organization Secrets에 `DOCKER_PAT`로 추가

### 권한 차이
- **`DOCKER_PASSWORD`**: 이미지 생성/업데이트만 가능 (삭제 불가)
- **`DOCKER_PAT`**: 이미지 생성/업데이트/삭제 모두 가능

## 워크플로우 실행 권한

Expand All @@ -200,14 +110,12 @@ git push origin v0.4.1

## 워크플로우 선택 가이드

### 🚀 mayfly --force vs 🗑️ Delete Docker Image vs 🔄 Rebuild Docker Image vs 🏷️ Move Tag to Latest Commit
### 🔄 Rebuild Docker Image vs 🏷️ Move Tag to Latest Commit

| 상황 | 권장 방법 | 이유 |
|------|-----------|------|
| **일반적인 최신 이미지 업데이트** | **`mayfly infra update --force`** | digest 기반으로 정확한 최신 버전 보장 |
| 잘못된 이미지 삭제 | **Delete Docker Image** | 특정 태그만 삭제, 안전함 |
| Docker Hub digest 충돌 | **Rebuild Docker Image** | Git 태그 위치 유지, 안전함 |
| Docker 이미지 빌드 문제 | **Rebuild Docker Image** | 같은 코드로 새 이미지 생성 |
| **일반적인 최신 이미지 업데이트** | **`mayfly infra update`** | 버전 체크 후 안전한 업데이트 |
| Docker 이미지 빌드 문제 | **Rebuild Docker Image** | Git 태그 위치 유지, 안전함 |
| 최신 커밋을 태그에 반영 | **Move Tag to Latest Commit** | Git 태그 위치 변경 필요 |
| 일반적인 새 릴리즈 | **기존 CD 워크플로우** | 새 태그 생성 (v0.4.1) |

Expand All @@ -227,22 +135,17 @@ git push origin v0.4.1
4. 확인 코드 정확히 입력 (`DELETE`, `REBUILD` 또는 `MOVE_TAG`)

### Docker Hub API 오류
1. **HTTP 401 오류**: `DOCKER_PAT` 설정 확인 (삭제 권한 필요)
1. **HTTP 401 오류**: Docker Hub 인증 정보 확인
2. **HTTP 403 오류**: Docker Hub 계정 권한 확인
3. **API 레이트 리미트**: Docker Hub API 사용량 확인
4. **네트워크 연결**: 연결 상태 및 방화벽 설정 확인

### Docker 이미지 삭제 관련 오류
1. **삭제 권한 부족**: `DOCKER_PAT` 토큰에 `Delete` 권한이 있는지 확인
2. **태그 존재 여부**: 삭제하려는 태그가 실제로 존재하는지 확인
3. **Organization 권한**: Docker Hub Organization의 관리자 권한 확인

### Git 태그 관련 오류
1. 기존 태그가 존재하는지 확인
2. 태그 삭제 권한 확인
3. 원격 저장소 접근 권한 확인

### 다이제스트 문제 해결
1. **여러 digest 확인**: `curl -s "https://hub.docker.com/v2/repositories/cloudbaristaorg/cm-ant/tags/0.4.0/" | jq '.images'`
2. **최신 digest 확인**: `jq '.images[0].digest'`로 첫 번째 digest 확인
### 버전 체크 문제 해결
1. **현재 실행 중인 버전 확인**: `docker inspect --format='{{.Config.Image}}' [container_name]`
2. **Docker Hub 최신 버전 확인**: `curl -s "https://hub.docker.com/v2/repositories/cloudbaristaorg/cm-ant/tags/" | jq '.results[0].name'`
3. **캐시 문제**: Docker Hub API 캐시로 인한 지연 (보통 10-30분)
47 changes: 0 additions & 47 deletions .github/workflows/force-rebuild.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,6 @@ on:
description: 'Tag name to move to latest commit (e.g., v0.4.0)'
required: true
type: string
delete_existing_docker_image:
description: 'Delete existing Docker Hub image before rebuilding?'
required: true
type: choice
default: 'yes'
options:
- 'yes'
- 'no'
confirm_move:
description: '⚠️ WARNING: This will move the Git tag to current HEAD and rebuild Docker image. Type "MOVE_TAG" to confirm'
required: true
Expand Down Expand Up @@ -64,41 +56,6 @@ jobs:

echo "Git tag $TAG_NAME deleted"

- name: Delete existing Docker Hub tag (conditional)
if: github.event.inputs.delete_existing_docker_image == 'yes'
run: |
TAG_NAME="${{ github.event.inputs.tag_name }}"
echo "Deleting existing Docker Hub tag: $TAG_NAME"

# Delete the tag from Docker Hub
# Try with Personal Access Token first, fallback to password
if [ -n "${{ secrets.DOCKER_PAT }}" ]; then
echo "Using Personal Access Token for deletion"
curl -X DELETE \
-H "Authorization: Bearer ${{ secrets.DOCKER_PAT }}" \
"https://hub.docker.com/v2/repositories/${{ env.DOCKER_REGISTRY_NAME }}/${{ env.IMAGE_NAME }}/tags/$TAG_NAME/" \
-H "Accept: application/json" 2>/dev/null || echo "Docker Hub tag $TAG_NAME not found"
else
echo "Using username/password for deletion"
curl -X DELETE \
-u ${{ secrets.DOCKER_USERNAME }}:${{ secrets.DOCKER_PASSWORD }} \
"https://hub.docker.com/v2/repositories/${{ env.DOCKER_REGISTRY_NAME }}/${{ env.IMAGE_NAME }}/tags/$TAG_NAME/" \
-H "Accept: application/json" 2>/dev/null || echo "Docker Hub tag $TAG_NAME not found"
fi

echo "Docker Hub tag $TAG_NAME deleted"

- name: Skip Docker Hub deletion
if: github.event.inputs.delete_existing_docker_image == 'no'
run: |
echo "Skipping Docker Hub image deletion as requested by user"
echo "⚠️ Note: This may result in multiple digests for the same tag"

- name: Wait for cleanup (conditional)
if: github.event.inputs.delete_existing_docker_image == 'yes'
run: |
echo "Waiting 30 seconds for cleanup to complete..."
sleep 30

- name: Create new Git tag at current HEAD
run: |
Expand Down Expand Up @@ -178,9 +135,5 @@ jobs:
echo "- **Tag**: ${{ github.event.inputs.tag_name }}" >> $GITHUB_STEP_SUMMARY
echo "- **New Digest**: ${{ steps.docker_build.outputs.digest }}" >> $GITHUB_STEP_SUMMARY
echo "- **New Commit**: ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
echo "- **Docker Image Deletion**: ${{ github.event.inputs.delete_existing_docker_image }}" >> $GITHUB_STEP_SUMMARY
echo "- **Status**: ✅ Successfully moved tag to latest commit" >> $GITHUB_STEP_SUMMARY
echo "- **⚠️ Warning**: Git tag position has been changed!" >> $GITHUB_STEP_SUMMARY
if [ "${{ github.event.inputs.delete_existing_docker_image }}" = "no" ]; then
echo "- **⚠️ Note**: Multiple digests may exist for the same tag" >> $GITHUB_STEP_SUMMARY
fi
47 changes: 0 additions & 47 deletions .github/workflows/retag-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,6 @@ on:
description: 'Tag name to rebuild Docker image (e.g., v0.4.0)'
required: true
type: string
delete_existing_docker_image:
description: 'Delete existing Docker Hub image before rebuilding?'
required: true
type: choice
default: 'yes'
options:
- 'yes'
- 'no'
confirm_rebuild:
description: 'Confirm Docker image rebuild (type "REBUILD" to confirm)'
required: true
Expand Down Expand Up @@ -51,41 +43,6 @@ jobs:
fi
echo "Validating tag: $TAG_NAME"

- name: Delete existing Docker image from Docker Hub (conditional)
if: github.event.inputs.delete_existing_docker_image == 'yes'
run: |
TAG_NAME="${{ github.event.inputs.tag_name }}"
echo "Deleting existing Docker image for tag: $TAG_NAME"

# Delete the Docker image from Docker Hub (Git tag position remains unchanged)
# Try with Personal Access Token first, fallback to password
if [ -n "${{ secrets.DOCKER_PAT }}" ]; then
echo "Using Personal Access Token for deletion"
curl -X DELETE \
-H "Authorization: Bearer ${{ secrets.DOCKER_PAT }}" \
"https://hub.docker.com/v2/repositories/${{ env.DOCKER_REGISTRY_NAME }}/${{ env.IMAGE_NAME }}/tags/$TAG_NAME/" \
-H "Accept: application/json"
else
echo "Using username/password for deletion"
curl -X DELETE \
-u ${{ secrets.DOCKER_USERNAME }}:${{ secrets.DOCKER_PASSWORD }} \
"https://hub.docker.com/v2/repositories/${{ env.DOCKER_REGISTRY_NAME }}/${{ env.IMAGE_NAME }}/tags/$TAG_NAME/" \
-H "Accept: application/json"
fi

echo "Docker image for tag $TAG_NAME deleted from Docker Hub"

- name: Skip Docker Hub deletion
if: github.event.inputs.delete_existing_docker_image == 'no'
run: |
echo "Skipping Docker Hub image deletion as requested by user"
echo "⚠️ Note: This may result in multiple digests for the same tag"

- name: Wait for tag deletion (conditional)
if: github.event.inputs.delete_existing_docker_image == 'yes'
run: |
echo "Waiting 30 seconds for Docker Hub to process deletion..."
sleep 30

- name: Generate Docker tags
id: meta
Expand Down Expand Up @@ -154,8 +111,4 @@ jobs:
echo "- **Tag**: ${{ github.event.inputs.tag_name }}" >> $GITHUB_STEP_SUMMARY
echo "- **New Digest**: ${{ steps.docker_build.outputs.digest }}" >> $GITHUB_STEP_SUMMARY
echo "- **Git Tag Position**: Unchanged (same commit)" >> $GITHUB_STEP_SUMMARY
echo "- **Docker Image Deletion**: ${{ github.event.inputs.delete_existing_docker_image }}" >> $GITHUB_STEP_SUMMARY
echo "- **Status**: ✅ Successfully rebuilt Docker image" >> $GITHUB_STEP_SUMMARY
if [ "${{ github.event.inputs.delete_existing_docker_image }}" = "no" ]; then
echo "- **⚠️ Note**: Multiple digests may exist for the same tag" >> $GITHUB_STEP_SUMMARY
fi