diff --git a/.github/workflows/README.md b/.github/workflows/README.md index f5791f7..264060d 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -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 이미지 삭제 및 재생성 기능이 보조 수단으로 제공됩니다.** ## 기존 워크플로우 @@ -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 이미지만 재빌드 @@ -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 이미지 재빌드 @@ -130,7 +62,7 @@ mayfly infra update -s cm-ant - **복구 불가**: 한번 실행하면 이전 태그 위치로 복구하기 어려움 #### 특징: -- **완전한 재태깅**: Git 태그와 Docker Hub 태그를 모두 삭제 후 재생성 +- **완전한 재태깅**: Git 태그를 현재 HEAD로 이동 후 Docker 이미지 재생성 - **최신 코드 반영**: 현재 HEAD 커밋의 모든 변경사항이 반영됨 - **소스-이미지 동기화**: Git 태그 위치와 Docker 이미지 내용이 완벽히 일치 @@ -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 워크플로우 사용 @@ -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`**: 이미지 생성/업데이트/삭제 모두 가능 ## 워크플로우 실행 권한 @@ -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) | @@ -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분) diff --git a/.github/workflows/force-rebuild.yaml b/.github/workflows/force-rebuild.yaml index eb658fd..f94b166 100644 --- a/.github/workflows/force-rebuild.yaml +++ b/.github/workflows/force-rebuild.yaml @@ -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 @@ -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: | @@ -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 diff --git a/.github/workflows/retag-release.yaml b/.github/workflows/retag-release.yaml index ea41abc..eca074c 100644 --- a/.github/workflows/retag-release.yaml +++ b/.github/workflows/retag-release.yaml @@ -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 @@ -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 @@ -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