CICD용 .yml 파일을 작성 중 생긴 오류를 정리한 글이다.
지난 글에 이어서 작성된 글이다.
1. 문제 상황
https://lhoju0158.tistory.com/47
[Github actions] 엑세스 키 없을 때 S3, Cloudfront 배포 자동화
좋은 기회로 AWS 자원을 지원받는 일이 있었다. 근데.. S3와 Cloudfront에 대한 엑세스 권한을 주지 않아, 배포 자동화가 힘들었다. 그래서 생각해 낸 편법이 EC2에 접근해서, S3와 Cloudfront에 배포하는
lhoju0158.tistory.com
사전 정보를 주자면, S3 + EC2를 가지고 있는 계정과 Cloudfront 계정은 서로 다르다.
PR 이후 팀원이 다음 코멘트를 남겨줬다.

정리를 하면 내 기존 코드는
EC2에서 S3 업로드, Cloudfront 캐시 초기화 모두를 담당한다.
팀원분의 조언은
EC2에서 S3 업로드, github action이 캐시 초기화를 담당하는 것이 어떤가 이다.
맞는 말이라 코드를 수정했다.
name: PROD용 S3에 업로드 및 CLOUDFRONT 초기화
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: GitHub 코드 체크아웃
uses: actions/checkout@v4
- name: EC2에 SSH 접속 후 배포
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_SSH_KEY }}
port: 22
script: |
cd ~/client
git pull origin main
npm install
npm run build
# S3 업로드
aws s3 sync ./dist ${{ secrets.S3_URL }} --delete
# CloudFront 초기화
mkdir -p ~/.aws
cat > ~/.aws/credentials <<EOL
[default]
aws_access_key_id=${{ secrets.AWS_ACCESS_KEY_ID_PROD }}
aws_secret_access_key=${{ secrets.AWS_SECRET_ACCESS_KEY_PROD }}
region=${{ secrets.AWS_REGION_PROD }}
EOL
aws cloudfront create-invalidation \
--distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID_PROD }} \
--paths "/*"
위 코드를
name: PROD용 S3에 업로드 및 CLOUDFRONT 초기화
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: EC2에 SSH 접속 후 배포
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_SSH_KEY }}
port: 22
script: |
cd ~/client
git pull origin main
npm install
npm run build
# S3 업로드
aws s3 sync ./dist ${{ secrets.S3_URL }} --delete
- name: AWS 로그인
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_PROD }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_PROD }}
aws-region: ${{ secrets.AWS_REGION_PROD }}
- name: CloudFront 캐시 무효화
run: aws cloudfront create-invalidation --distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID_PROD }} --paths "/*"
다음과 같이 정리하면 가독성도 좋고, 접근 권한 분리 측면에서도 나아 보인다. (역시 멋진 팀원분..)
아무튼 해당 .yml을 push했는데..

다음과 같은 오류가 생긴다.. 해당 오류 로그는 다음과 같다.
err: fatal error: An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied
즉, S3 업로드를 하고 싶은데 접근이 거절된다는 오류다.
바꾼건 Cloudfront 주체인데, 왜 S3업로드가 안될까?
2. 해결
원인은 EC2에 저장된 키 때문이었다.
기존 코드 중
mkdir -p ~/.aws
cat > ~/.aws/credentials <<EOL
[default]
aws_access_key_id=${{ secrets.AWS_ACCESS_KEY_ID_PROD }}
aws_secret_access_key=${{ secrets.AWS_SECRET_ACCESS_KEY_PROD }}
region=${{ secrets.AWS_REGION_PROD }}
EOL
을 이용해 EC2에 Cloudfront 계정에 대한 키를 저장했는데 이후 .yml 코드 수정 사이에 EC2에 저장된 키를 초기화 하지 않았다. 이 상황에서 수정된 .yml을 가지고 액션을 하니, EC2는 S3 업로드 시 기존 IAM role이 아닌 Cloudfront 계정 키로 S3 업로드를 진행한다. Cloudfront 계정은 S3 업로드에 대한 접근 권한이 없으므로 접근이 거절되는 것이다.
그래서 EC2에 직접 키를 초기화 한다.
rm -rf ~/.aws
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
unset AWS_SESSION_TOKEN
이후 업로드를 진행하면 정상적으로 된다.

사진을 보면 기존에는 S3 업로드가 거절되지만, 키 초기화 이후, S3 업로드가 가능함을 알 수 있다.

동일한 코드에도 불구하고, Github Actions이 성공함을 볼 수 있다.
그럼 여기서 생긴 의문이 하나 있다.
기존 파일 (EC2가 Cloudfront 초기화도 담당하는 경우)는 왜 S3 업로드 오류가 안생겼는가?

외부적으로는 성공한 듯 보이지만,

자세히 보면 S3업로드 시 오류가 발생함을 알 수 있다. Step 단위 결과에 따라 성공 여부를 결정 지어 S3 업로드는 실패했지만, Cloudfront 초기화는 성공해 중간 실패에 대해 전달받지 못한 것으로 추측된다.
3. 후기
GitHub Actions를 위한 .yml 파일 작성 시 기능에 따라 Step을 나눠 위와 같이 실패를 인식하지 못한 경우를 방지해야겠다. 또한 단순 액션 결과만을 보고 넘기지 말고 로그를 면밀히 파악해야겠다.
'개발 끄적끄적' 카테고리의 다른 글
| [Spring] Security - 웹 보안 이해 (1) | 2025.08.03 |
|---|---|
| [Spring] Security - OAuth 2.0 (3) | 2025.08.01 |
| [GitHub Actions] 엑세스 키 없을 때 S3, Cloudfront 배포 자동화 (0) | 2025.07.12 |
| [Spring] S3 url 한글 인코딩 이슈 해결 (2) | 2025.06.26 |
| [AWS] AWS Certified Cloud Practitioner 자격증 취득 (0) | 2025.04.26 |