1 # 명령서버 1대 로그인 cat <<'EOF' > eks-cluster.yaml apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: my-eks-cluster region: ap-northeast-2 managedNodeGroups: - name: standard-nodes instanceType: c7i-flex.large minSize: 1 maxSize: 3 EOF eksctl create cluster -f eks-cluster.yaml 2 k get no 3 # 터미널 2 wa 4 이 코드는 Python의 Flask 프레임워크를 사용하여 아주 간단한 웹 서버를 만들고, 이를 실행하기 위한 설정 파일(requirements.txt)을 생성하는 과정을 담고 있습니다. 주로 Docker 컨테이너를 만들거나 AWS EKS(Kubernetes) 같은 클라우드 환경에 배포하기 전, 가장 기초가 되는 "Hello World" 수준의 애플리케이션을 준비하는 단계라고 보시면 됩니다. ---------- mkdir 3 cd 3 cat <<'EOF' > app.py from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return "Hello from EKS (c7i-flex.large) and GitHub Actions!" if __name__ == '__main__': app.run(host='0.0.0.0', port=8080) EOF cat <<'EOF' > requirements.txt Flask==3.0.0 EOF cat <<'EOF' > Dockerfile FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY app.py . EXPOSE 8080 CMD ["python", "app.py"] EOF 1. app.py (웹 애플리케이션 본체) 이 파일은 실제 웹 서버의 로직을 담당합니다. from flask import Flask: 웹 서버를 만들기 위한 Flask 라이브러리를 불러옵니다. app = Flask(__name__): Flask 애플리케이션 객체를 생성합니다. @app.route('/'): 사용자가 웹사이트의 메인 주소(루트 경로)에 접속했을 때 아래 함수를 실행하라는 뜻입니다. return "Hello from EKS...": 브라우저 화면에 해당 문구를 출력합니다. 여기서 c7i-flex.large는 AWS의 특정 인스턴스 타입을 언급하고 있어, 이 서버가 어떤 환경에서 돌아가는지 명시적으로 보여주려는 의도가 보입니다. app.run(host='0.0.0.0', port=8080): 서버를 실행합니다. 0.0.0.0은 외부(인터넷) 어디서든 접속을 허용하겠다는 의미입니다. 8080번 포트에서 서비스를 제공합니다. ----------- 2. requirements.txt (설치 명세서) 이 파일은 이 프로그램을 실행하기 위해 어떤 도구가 필요한지 적어둔 리스트입니다. Flask==3.0.0: "이 프로젝트를 돌리려면 Flask 라이브러리의 3.0.0 버전이 꼭 필요해!"라고 선언하는 것입니다. 나중에 서버 환경에서 pip install -r requirements.txt 명령어를 실행하면, 일일이 설치할 필요 없이 필요한 라이브러리를 한 번에 설치할 수 있습니다. ------------ 3. cat <<'EOF' > ... (쉘 명령어) 코드의 가장 바깥을 감싸고 있는 이 문법은 리눅스/유닉스 터미널 명령어로, **"파일을 직접 생성해라"**라는 의미입니다. cat <<'EOF': "지금부터 내가 입력하는 내용을 파일에 써라. EOF라는 글자가 나올 때까지!"라는 뜻입니다. > app.py: 그 내용을 app.py라는 이름의 파일로 저장하라는 경로 지정입니다. --------------- 요약하자면? 이 스크립트는 **"AWS EKS와 GitHub Actions를 테스트하기 위한 아주 가벼운 Flask 웹 서버 파일 2개를 자동으로 만드는 과정"**입니다. 이후 과정으로는 보통 이 파일들을 가지고 Dockerfile을 만들어 이미지를 빌드하고, GitHub Actions를 통해 AWS EKS 클러스터에 배포하는 작업 ------------- 5 mkdir -p k8s cat <<'EOF' > k8s/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: flask-app spec: replicas: 2 selector: matchLabels: app: flask-app template: metadata: labels: app: flask-app spec: containers: - name: flask-app image: REPLACE_ME_IMAGE_URI ports: - containerPort: 8080 EOF 앞서 작성하신 app.py가 웹 애플리케이션의 **'내용'**이라면, 이 deployment.yaml 파일은 그 애플리케이션을 쿠버네티스(Kubernetes, K8s)라는 오케스트레이션 시스템 위에서 **'어떻게 실행하고 유지할지'**를 정의하는 설계도입니다. 주요 설정값들의 의미를 쉽게 풀어 설명해 드릴게요. 1. 주요 설정 항목 설명 kind: Deployment: 쿠버네티스에게 "이 프로그램의 상태를 정의된 대로 계속 유지해줘"라고 명령하는 리소스 타입입니다. (예: 서버가 죽으면 자동으로 다시 살려라 등) replicas: 2: 똑같은 웹 서버 컨테이너를 2개 띄우라는 뜻입니다. 하나가 고장 나도 다른 하나가 서비스할 수 있게 하여 안정성을 높입니다. selector & labels (app: flask-app): 쿠버네티스가 수많은 컨테이너 중에서 어떤 것이 flask-app에 속하는지 식별하기 위한 이름표입니다. image: REPLACE_ME_IMAGE_URI: 가장 중요한 부분입니다. 이전에 만든 app.py를 Docker 이미지로 빌드해서 저장소(ECR 등)에 올린 후, 그 이미지의 주소를 여기에 적어야 합니다. 보통 CI/CD 과정(GitHub Actions)에서 이 부분을 실제 주소로 자동 치환합니다. containerPort: 8080: app.py에서 설정한 포트 번호(8080)와 일치하도록 컨테이너 내부 통로를 열어주는 설정입니다. ------ 2. 전체적인 흐름 이해 이 파일은 보통 다음과 같은 워크플로우에서 사용됩니다. 빌드: GitHub Actions가 코드를 가져와 Docker 이미지를 만듭니다. 푸시: 생성된 이미지를 Amazon ECR 같은 저장소에 업로드합니다. 치환: deployment.yaml 파일의 REPLACE_ME_IMAGE_URI 부분을 방금 업로드한 이미지 주소로 바꿉니다. 배포: kubectl apply -f k8s/deployment.yaml 명령어를 통해 EKS 클러스터에 배포합니다. -------- 현재 작성하신 파일에는 Service 설정이 빠져 있습니다. Deployment는 서버를 띄우기만 할 뿐, 외부에서 접속할 수 있는 '문(Gateway)'은 만들지 않습니다. 실제로 브라우저에서 접속하려면 LoadBalancer 타입의 Service 설정 파일이 추가로 필요할 것입니다. cat <<'EOF' > k8s/service.yaml apiVersion: v1 kind: Service metadata: name: flask-service spec: type: LoadBalancer selector: app: flask-app ports: - port: 80 targetPort: 8080 EOF service.yaml 파일은 쿠버네티스 클러스터 내부에서 돌고 있는 컨테이너들을 세상 밖(인터넷)과 연결해 주는 역할을 합니다. 1. service.yaml 상세 분석 kind: Service: 파드(Pod, 컨테이너 집합)들에게 고정된 주소를 부여하고, 네트워크 트래픽을 전달하는 역할을 정의합니다. type: LoadBalancer: 클라우드 환경(AWS EKS 등)에서 이 설정을 사용하면, AWS가 자동으로 Classic Load Balancer 또는 Network Load Balancer를 생성해 줍니다. 이를 통해 사용자들은 퍼블릭 IP나 DNS 주소로 접속할 수 있게 됩니다. selector (app: flask-app): 아주 중요한 연결 고리입니다. 이전에 만든 deployment.yaml에 적힌 labels: app: flask-app을 찾아 "이리로 손님들을 보내줘!"라고 지정하는 것입니다. port: 80: 외부 사용자가 접속할 때 사용하는 포트입니다. 보통 웹사이트 접속 기본 포트인 80을 사용합니다. targetPort: 8080: 서비스가 받은 트래픽을 컨테이너 내부의 몇 번 포트로 보낼지 결정합니다. 우리 app.py가 8080에서 돌고 있으니 여기로 연결해야 합니다. 2. 전체 구조도 (Flow) 이제 지금까지 만든 파일들이 어떻게 유기적으로 작동하는지 한눈에 파악해 보세요. 사용자: 브라우저에 LoadBalancer 주소(Port 80)를 입력합니다. Service (flask-service): 요청을 받아 app: flask-app 이름표를 가진 파드들을 찾습니다. Deployment (flask-app): 현재 실행 중인 2개의 파드(Replicas: 2) 중 하나로 트래픽을 보냅니다. Pod (Container): 내부의 8080 포트로 트래픽을 전달받아 app.py가 실행되고 결과를 응답합니다. -------------- 6 AWS 계정 ID (12자리 숫자)로 직접 변경해 주셔야 GitHub Actions가 정상적으로 AWS 권한을 얻을 수 있습니다. AWS 계정 ID를 확인하고 파일에 적용하는 방법은 다음과 같습니다. 1. AWS 계정 ID 확인하기 터미널에서 아래 명령어를 실행하시면 본인의 12자리 계정 ID가 바로 출력됩니다 aws sts get-caller-identity --query "Account" --output text 697016550159 -------- # 수정 mkdir -p .github/workflows cat <<'EOF' > .github/workflows/deploy.yml name: Deploy to EKS on: push: branches: [ "main" ] env: AWS_REGION: ap-northeast-2 ECR_REPOSITORY: my-flask-app EKS_CLUSTER: my-eks-cluster # OIDC 인증을 위한 권한 설정 permissions: id-token: write contents: read jobs: build-and-deploy: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Configure AWS credentials with OIDC uses: aws-actions/configure-aws-credentials@v4 with: # 입력해주신 AWS 계정 ID가 적용되었습니다. role-to-assume: arn:aws:iam::697016550159:role/GitHubActions_EKS_Role aws-region: ${{ env.AWS_REGION }} - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v2 - name: Build, tag, and push image to Amazon ECR env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} IMAGE_TAG: ${{ github.sha }} run: | docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG echo "IMAGE=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_ENV - name: Update kubeconfig run: aws eks update-kubeconfig --name ${{ env.EKS_CLUSTER }} --region ${{ env.AWS_REGION }} - name: Deploy to EKS run: | sed -i.bak "s|REPLACE_ME_IMAGE_URI|$IMAGE|" k8s/deployment.yaml kubectl apply -f k8s/deployment.yaml kubectl apply -f k8s/service.yaml EOF ------- 드디어 자동 배포의 핵심인 GitHub Actions 워크플로우(CI/CD) 파일까지 준비되었군요! 제공해주신 계정 ID(588752323301)가 적용된 이 설정은 GitHub에 코드를 push하는 순간, 빌드부터 EKS 배포까지 전 과정을 자동으로 수행하게 됩니다. 이 파일의 주요 단계들 설명입니다. 1. 주요 자동화 단계 설명 OIDC 기반 인증 (Security): role-to-assume 부분을 보시면 IAM Role을 사용합니다. 이는 보안상 위험한 Access Key를 저장하지 않고, GitHub과 AWS가 서로 신뢰 관계(OIDC)를 맺어 일시적인 권한을 받는 가장 권장되는 방식입니다. Docker 이미지 빌드 & 푸시: app.py와 requirements.txt를 가지고 Docker 이미지를 만듭니다. 태그로 ${{ github.sha }}(커밋 해시값)를 사용하여 이미지 버전이 겹치지 않게 관리합니다. sed를 이용한 이미지 주소 치환: deployment.yaml에 적어두었던 REPLACE_ME_IMAGE_URI를 방금 빌드한 ECR 이미지 주소로 실시간 교체합니다. 이 단계가 있어야만 쿠버네티스가 최신 코드를 인식합니다. kubectl apply: 수정된 설정 파일을 EKS에 전송하여 실제 서버를 업데이트합니다. ---------- 2. 전체 배포 파이프라인 구조 지금까지 만든 모든 파일이 합쳐져서 아래와 같은 흐름으로 동작하게 됩니다. Code Push: 개발자가 GitHub으로 코드를 보냅니다. Build: GitHub Actions가 컨테이너 이미지를 굽습니다. Store: 생성된 이미지를 Amazon ECR 저장소에 보관합니다. Deploy: EKS 클러스터에 접속하여 "새 이미지를 사용해라"라고 명령합니다. Service: AWS LoadBalancer를 통해 전 세계 사용자가 8080 포트로 실행 중인 Flask 앱에 접속합니다. -------- 3. 성공적인 실행을 위한 체크리스트 이 파일을 실행하기 전에 AWS에서 다음 사항들이 미리 준비되어 있어야 합니다: IAM Role 생성: GitHubActions_EKS_Role이라는 이름의 역할이 생성되어 있고, GitHub 리포지토리와의 OIDC 신뢰 관계가 설정되어 있어야 합니다. ECR 리포지토리: my-flask-app이라는 이름의 리포지토리가 ap-northeast-2 지역에 미리 만들어져 있어야 합니다. EKS 클러스터: my-eks-cluster라는 이름의 클러스터가 가동 중이어야 합니다. Dockerfile: 현재 폴더에 Docker 이미지를 어떻게 만들지 정의한 Dockerfile이 필요합니다. --------------- # 1. 현재 폴더를 Git 저장소로 초기화 (가장 중요!) git init # 2. 원격 저장소(GitHub 주소) 연결 # (주의: 아래 주소의 '아이디'와 '리포지토리명'을 본인의 것으로 바꿔주세요) # git remote add origin https://github.com/topasvga1/YOUR_REPOSITORY_NAME.git git remote add origin https://github.com/topasvga1/s1.git # 3. 파일들 다시 담기 git add . # 4. 커밋 기록 생성 git commit -m "feat: initial commit with k8s and actions" # 5. 브라우저 이름을 main으로 변경 git branch -M main # 6. 드디어 원격 저장소로 푸시! git push -u origin main -------------- 제 모든 파일이 준비되었고, 마지막으로 GitHub 원격 저장소에 코드를 밀어넣는(Push) 단계만 남았네요. 작성하신 명령어들은 로컬에서 작업한 app.py, k8s/*.yaml, .github/workflows/deploy.yml 등 모든 파일을 GitHub 서버로 전송하여 **자동 배포(Actions)**를 트리거하는 완벽한 흐름입니다. 1. 명령어 흐름의 의미 git config: 이 컴퓨터에서 수행하는 작업이 누구의 것인지(topasvga1) 명시합니다. git add .: 현재 폴더의 모든 파일을 '업로드 대기 바구니'에 담습니다. git commit: 현재 상태를 하나의 '버전'으로 기록합니다. 이때 비로소 로컬 저장소에 데이터가 쌓입니다. git push -u origin main: 내 컴퓨터에 저장된 기록을 GitHub 저장소(origin)로 보냅니다. -u 옵션 덕분에 앞으로는 git push만 입력해도 main 브랜치로 자동 전송됩니다. --------- 2. 푸시 직후 벌어지는 일 (GitHub Actions 실행) 명령어가 성공적으로 실행되면, GitHub 리포지토리의 'Actions' 탭에서 다음과 같은 일이 벌어집니다. Workflow Trigger: main 브랜치에 코드가 들어온 것을 감지하고 deploy.yml이 실행됩니다. ECR Push: 빌드된 my-flask-app 이미지가 AWS 저장소로 올라갑니다. EKS Update: kubectl 명령어가 실행되면서 EKS 클러스터의 파드들이 새로운 이미지로 교체됩니다. --------- 3. 최종 점검 (중요) 아직 **Dockerfile**을 만들지 않으셨다면, 위 명령어를 실행하기 전에 프로젝트 최상위 루트에 아래 내용으로 파일을 하나 만들어주세요. (이 파일이 없으면 GitHub Actions의 docker build 단계에서 에러가 발생합니다.) cat <<'EOF' > Dockerfile # 1. 파이썬 실행 환경 설정 FROM python:3.9-slim # 2. 컨테이너 내부 작업 디렉토리 설정 WORKDIR /app # 3. 필요한 라이브러리 설치 파일 복사 및 설치 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 4. 소스 코드 전체 복사 COPY . . # 5. 앱이 사용할 포트 명시 EXPOSE 8080 # 6. 앱 실행 명령어 CMD ["python", "app.py"] EOF ------- # 트러블 슈팅 # 원격 저장소의 변경 사항을 로컬로 가져와서 병합합니다. (rebase 방식을 사용하여 커밋 히스토리를 깔끔하게 유지합니다) git pull origin main --rebase # 정상적으로 가져왔다면 다시 푸시합니다. git push -u origin main # 로컬 코드로 원격 저장소를 강제로 덮어씁니다. git push -u origin main --force -------- 이 에러(fetch first)는 현재 작업 중인 폴더(로컬)에는 없는 파일이나 과거 기록이 GitHub(원격 저장소)에 이미 존재하기 때문에 발생합니다. 이전에 다른 폴더에서 푸시했던 실습 파일이나, 리포지토리를 처음 만들 때 생성된 README 파일 등이 남아있기 때문입니다. 새로운 폴더(3)에서 Git을 다시 초기화하고 커밋하셨기 때문에, 로컬과 GitHub의 과거 기록이 달라서 일반적인 푸시가 거절된 상태입니다. 현재 작성하신 최신 EKS 실습 코드가 가장 중요하므로, 이 코드로 GitHub 저장소를 완전히 덮어쓰는 강제 푸시(Force Push)를 진행하는 것이 가장 깔끔한 해결책입니다. 터미널에 아래 명령어를 복사하여 실행해 주세요. git push -u origin main --force topasvga1 ghp_o6KluGjdK 8 export GITHUB_ORG="topasvga1" export GITHUB_REPO="s1" export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) export ROLE_NAME="GitHubActions_EKS_Role" export CLUSTER_NAME="my-eks-cluster" export REGION="ap-northeast-2" ---------- 이제 배포에 필요한 모든 **환경 변수(Environment Variables)**를 완벽하게 정의하셨네요! 이 변수들은 앞으로 진행할 AWS 인프라 설정(IAM Role 생성, OIDC 연결 등)이나 스크립트 실행 시 일관성을 유지해 주는 아주 중요한 역할을 합니다. 현재 설정하신 값을 기준으로 다음 단계에서 수행해야 할 핵심 작업들을 정리해 드립니다. -------- 2. 다음 단계: IAM Role & OIDC 생성 (필수) GitHub Actions가 AWS에 접근하려면, 단순히 변수만 설정하는 게 아니라 AWS 상에 해당 역할(Role)이 실제로 존재해야 합니다. 아직 만들지 않으셨다면 아래 흐름으로 진행될 것입니다. OIDC Provider 등록: AWS가 GitHub(https://www.google.com/search?q=token.actions.githubusercontent.com)을 믿을 수 있는 인증 기관으로 등록합니다. IAM Role 생성: $ROLE_NAME 이름으로 역할을 만들고, topasvga1/s1 저장소에서 오는 요청만 허용하도록 **신뢰 정책(Trust Policy)**을 설정합니다. 권한 부여: 이 역할에 ECR 푸시 권한(AmazonEC2ContainerRegistryPowerUser)과 EKS 접근 권한을 부여합니다. ----------- 3. 바로 실행할 수 있는 팁 만약 AWS CLI를 통해 이 역할을 바로 생성하고 싶으시다면, 방금 선언한 변수들을 그대로 사용하여 명령어를 구성할 수 있습니다. 예를 들어, EKS 클러스터 접속 설정을 업데이트할 때도 아래처럼 간단히 실행 가능합니다: aws eks update-kubeconfig --name $CLUSTER_NAME --region $REGION 9 aws iam create-open-id-connect-provider \ --url "https://token.actions.githubusercontent.com" \ --client-id-list "sts.amazonaws.com" \ --thumbprint-list "1b511abead59c6ce207077c0bf0e0043b1382612" "1c58a3a8518e8759bf075b76b750d4f2df264fcd" 본격적으로 GitHub Actions와 AWS를 안전하게 연결하는 OIDC(OpenID Connect) 공급자를 생성하는 단계에 진입하셨군요! 이 명령어는 보안상 위험한 Access Key/Secret Key 대신, GitHub이 발행한 신뢰할 수 있는 토큰을 통해 AWS 권한을 얻을 수 있게 해주는 기초 공사입니다. 1. 명령어의 의미 분석 --url "https://token.actions.githubusercontent.com": GitHub Actions가 인증 정보를 발행하는 공식 주소입니다. AWS가 이 주소를 통해 들어오는 인증 요청을 검증하게 됩니다. --client-id-list "sts.amazonaws.com": GitHub 토큰이 사용될 대상(Audience)을 지정합니다. AWS의 보안 토큰 서비스(STS)를 사용하겠다는 선언입니다. --thumbprint-list: GitHub 서버의 인증서 고유 식별값(지문)입니다. 2026년 기준, 최신 지문들을 잘 포함하셨습니다. (AWS가 해당 URL의 인증서가 위조되지 않았는지 확인하는 용도입니다.) 2. OIDC 기반 인증의 장점 비밀번호 없는 인증: GitHub Secrets에 복잡한 Access Key를 저장할 필요가 없어 유출 위험이 사라집니다. 최소 권한 원칙: 특정 리포지토리(topasvga1/s1)에서 온 요청만 특정 역할(GitHubActions_EKS_Role)을 빌려 쓸 수 있도록 정교하게 제어할 수 있습니다. 자동 만료: 발급된 토큰은 단기용이므로 보안성이 매우 높습니다. 3. 다음으로 해야 할 일 (IAM Role 생성) OIDC 공급자를 생성한 후에는, 이 공급자를 신뢰하는 IAM Role을 실제로 만들어야 합니다. 아래 명령어를 통해 GitHub 저장소(topasvga1/s1) 전용 신뢰 정책을 생성할 수 있습니다. 먼저 신뢰 정책 파일(trust-policy.json)을 만듭니다: 그 다음, 역할을 생성하고 정책을 연결합니다: 10 export GITHUB_ORG="topasvga1" export GITHUB_REPO="s1" export AWS_ACCOUNT_ID="697016550159" export ROLE_NAME="GitHubActions_EKS_Role" cat < trust-policy.json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::${AWS_ACCOUNT_ID}:oidc-provider/token.actions.githubusercontent.com" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringLike": { "token.actions.githubusercontent.com:sub": "repo:${GITHUB_ORG}/${GITHUB_REPO}:*" }, "StringEquals": { "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" } } } ] } EOF 11 aws iam create-role \ --role-name ${ROLE_NAME} \ --assume-role-policy-document file://trust-policy.json GitHub Actions가 AWS에 접근할 수 있도록 하는 핵심 보안 설정인 IAM Role 생성을 완료하셨습니다! 작성하신 trust-policy.json은 매우 정교하게 설정되었습니다. 특히 Condition 절의 repo:${GITHUB_ORG}/${GITHUB_REPO}:* 부분 덕분에, 오직 **본인의 저장소(topasvga1/s1)**에서 실행되는 워크플로우만 이 역할을 빌려 쓸 수 있습니다. 이는 다른 사람의 GitHub Actions가 본인의 AWS 계정에 접근하는 것을 방지하는 아주 중요한 장치입니다. 1. 현재까지 진행 상황 OIDC Provider: 생성 완료 (AWS와 GitHub 사이의 신뢰 구축) IAM Role: 생성 완료 (${ROLE_NAME}) Trust Policy: 적용 완료 (특정 GitHub 저장소만 허용) 2. 다음 단계: 역할에 권한 부여 (Policy Attach) 역할은 만들었지만, 아직 이 역할이 ECR에 이미지를 올리거나 EKS에 배포할 권한은 없는 상태입니다. 아래 명령어를 실행하여 필요한 권한들을 연결해 주세요. 11 # ECR 접근 권한 (AWS 관리형 정책) 연결 aws iam attach-role-policy \ --role-name ${ROLE_NAME} \ --policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser # EKS 클러스터 조회 권한 (인라인 정책) 생성 및 연결 cat < eks-policy.json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "eks:DescribeCluster", "Resource": "arn:aws:eks:${REGION}:${AWS_ACCOUNT_ID}:cluster/${CLUSTER_NAME}" } ] } EOF aws iam put-role-policy \ --role-name ${ROLE_NAME} \ --policy-name EKSAccessPolicy \ --policy-document file://eks-policy.json 3. 마지막 관문: EKS Access Entry 설정 (매우 중요) EKS는 IAM 역할만 만든다고 해서 자동으로 접근을 허용하지 않습니다. **EKS 클러스터 내부의 RBAC(권한 관리 시스템)**에 이 역할을 등록해 주어야 kubectl apply 명령어가 성공합니다. 아래 명령어를 통해 생성한 역할을 EKS의 관리자 권한군에 추가하세요. export AWS_ACCOUNT_ID="697016550159" export CLUSTER_NAME="my-eks-cluster" export ROLE_NAME="GitHubActions_EKS_Role" export REGION="ap-northeast-2" # Access Entry 생성 aws eks create-access-entry \ --cluster-name ${CLUSTER_NAME} \ --principal-arn arn:aws:iam::${AWS_ACCOUNT_ID}:role/${ROLE_NAME} \ --type STANDARD # Cluster Admin 정책 연결 aws eks associate-access-policy \ --cluster-name ${CLUSTER_NAME} \ --principal-arn arn:aws:iam::${AWS_ACCOUNT_ID}:role/${ROLE_NAME} \ --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy \ --access-scope type=cluster 위의 권한 설정까지 모두 마쳤다면, 이제 모든 준비가 끝났습니다! 수정된 .github/workflows/deploy.yml 파일이 있는지 확인합니다. git add ., git commit -m "setup: complete ci/cd pipeline", git push origin main을 실행합니다. GitHub 웹사이트의 Actions 탭으로 가서 빌드와 배포가 성공적으로 진행되는지 즐겁게 감상하세요! 13 2. ECR 리포지토리 생성 (이미지 저장소) GitHub Actions가 이미지를 푸시하려면 AWS에 저장소가 미리 준비되어 있어야 합니다. aws ecr create-repository \ --repository-name my-flask-app \ --region ${REGION} ---------- 14 3. 최종 배포 확인 (GitHub Push) 이제 모든 인프라 설정이 끝났습니다! 로컬 프로젝트 폴더로 돌아가서 코드를 푸시하고 배포가 시작되는지 확인하세요. # 모든 변경 사항 추가 (Dockerfile, k8s 설정 등) git add . # 커밋 git commit -m "deploy: complete infrastructure setup and ready for eks deployment" # GitHub로 푸시! git push origin main topasvga1 ----------- ! [rejected] main -> main (fetch first) error: failed to push some refs to 'https://github.com/topasvga1/s1.git' hint: Updates were rejected because the remote contains work that you do not 드디어 마지막 관문인 Git 충돌 상황이네요! ! [rejected] main -> main (fetch first) 에러가 발생하는 이유는 GitHub 원격 저장소에 이미 파일(예: 리포지토리 생성 시 생긴 README.md 등)이 있는데, 내 로컬 컴퓨터에는 그 파일들이 없어서 순서가 맞지 않기 때문입니다. 이럴 때는 원격의 내용을 먼저 가져와서 합친(Merge) 후에 다시 밀어넣으면 됩니다. 아래 순서대로 입력해 보세요. # 1. 원격의 내용을 강제로 가져와서 합칩니다. git pull origin main --allow-unrelated-histories # 2. 만약 화면이 멈추고 글자를 입력하는 창(vi 편집기)이 뜬다면: # Esc 누르고 -> :wq 입력 -> Enter (저장 후 종료) # 3. 다시 푸시합니다. # 로컬의 이력으로 원격 저장소를 강제로 교체합니다. git push origin main --force topasvga1 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 GitHub에서 리포지토리를 처음 만들 때 **'Add a README.md'**나 '.gitignore' 같은 옵션을 체크하셨다면, 원격 저장소에는 이미 commit 기록이 한 개 존재하게 됩니다. 반면, 내 컴퓨터는 git init으로 완전히 깨끗하게 시작했죠. Git은 이 두 역사가 서로 다르다고 판단하여 안전을 위해 푸시를 막는 것입니다. ------------- 푸시 성공 후 꼭 확인하세요! 푸시가 성공하면 이제 터미널 작업은 끝납니다. 바로 GitHub.com의 [Actions] 탭으로 이동하세요. 노란색 원: 배포가 진행 중입니다. (Docker 빌드 및 EKS 전송 중) 초록색 체크: 배포 완료! kubectl get svc로 접속 주소를 확인하세요. 빨간색 X: 설정 오류입니다. (IAM 권한이나 변수 오타일 확률이 높습니다.) 어떤 방법으로 진행하시겠어요? 강제 푸시(--force)를 하면 이 에러는 즉시 사라집니다! --------------- 확인하기 GitHub: 본인의 리포지토리(s1)의 Actions 탭으로 가서 워크플로우가 돌아가는지 확인합니다. AWS: ECR에 새로운 이미지가 올라왔는지 확인합니다. EKS: 배포가 완료되면 아래 명령어로 접속 주소를 확인합니다. kubectl get svc flask-service ------------------------ 깃허브 동작 확인 --------------- 3s Run aws eks update-kubeconfig --name my-eks-cluster --region ap-northeast-2 Error: aws: [ERROR]: An error occurred (AccessDeniedException) when calling the DescribeCluster operation: User: arn:aws:sts::697016550159:assumed-role/GitHubActions_EKS_Role/GitHubActions is not authorized to perform: eks:DescribeCluster on resource: arn:aws:eks:ap-northeast-2:697016550159:cluster/my-eks-cluster because no identity-based policy allows the eks:DescribeCluster action Error: Process completed with exit code 254. # 변수 재설정 export ROLE_NAME="GitHubActions_EKS_Role" # JSON 정책 파일 생성 cat < eks-describe-policy.json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "eks:DescribeCluster", "eks:ListClusters" ], "Resource": "*" } ] } EOF # 역할에 인라인 정책 연결 aws iam put-role-policy \ --role-name ${ROLE_NAME} \ --policy-name GitHubActions-EKS-Describe-Policy \ --policy-document file://eks-describe-policy.json --------- # 변수 확인 export AWS_ACCOUNT_ID="697016550159" export CLUSTER_NAME="my-eks-cluster" export ROLE_NAME="GitHubActions_EKS_Role" # 관리자 정책 연결 (정확한 ARN 포맷) aws eks associate-access-policy \ --cluster-name ${CLUSTER_NAME} \ --principal-arn arn:aws:iam::${AWS_ACCOUNT_ID}:role/${ROLE_NAME} \ --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy \ --access-scope type=cluster ------ 트러블 슈팅 이제 GitHub Actions로 돌아가세요! 인프라 측면에서 할 수 있는 모든 조치(IAM Role 권한 부여 + EKS Access Entry 등록)가 끝났습니다. GitHub Actions 페이지의 실패한 워크플로우에서 **[Re-run all jobs]**를 클릭합니다. Update kubeconfig 단계에서 DescribeCluster 에러가 사라지는지 확인합니다. Deploy to EKS 단계에서 kubectl apply가 성공적으로 수행되는지 확인합니다. 만약 이번에도 안 된다면, 그것은 AWS 권한 문제가 아니라 deployment.yaml 파일 내의 오타나 이미지 주소 치환 로직의 문제일 확률이 높습니다. 하지만 지금 설정으로는 권한 때문에 막힐 일은 없을 거예요. 성공을 빕니다! 1 # ecr 권한 aws iam attach-role-policy \ --role-name GitHubActions_EKS_Role \ --policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser 2 ecr 레포 생성 aws ecr create-repository \ --repository-name my-flask-app \ --region ap-northeast-2 \ --image-scanning-configuration scanOnPush=true 3 cat <<'EOF' >> main.tf # 1. EKS 워커 노드를 위한 IAM Role 생성 resource "aws_iam_role" "node_group_role" { name = "eks-node-group-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "ec2.amazonaws.com" } }] }) } # 2. 노드 그룹에 필요한 3가지 필수 정책 연결 resource "aws_iam_role_policy_attachment" "node_group_worker_policy" { policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy" role = aws_iam_role.node_group_role.name } resource "aws_iam_role_policy_attachment" "node_group_cni_policy" { policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy" role = aws_iam_role.node_group_role.name } resource "aws_iam_role_policy_attachment" "node_group_ecr_policy" { policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly" role = aws_iam_role.node_group_role.name } EOF ---------------- 모두 삭제법 terraform destroy -auto-approve eksctl delete cluster --name my-eks-cluster (삭제후 3분후 동일 이름으로 재설치가 가능하다.2 11:59:35 [ℹ] building cluster stack "eksctl-my-eks-cluster-cluster" 2026-02-22 11:59:35 [!] 1 error(s) occurred and cluster hasn't been created properly, you may wish to check CloudFormation console 2026-02-22 11:59:35 [ℹ] to cleanup resources, run 'eksctl delete cluster --region=ap-northeast-2 --name=my-eks-cluster' 2026-02-22 11:59:35 [✖] creating CloudFormation stack "eksctl-my-eks-cluster-cluster": operation error CloudFormation: CreateStack, https response error StatusCode: 400, RequestID: bc887135-9187-4833-8533-0b6c79697db6, AlreadyExistsException: Stack [eksctl-my-eks-cluster-cluster] already exists Error: failed to create cluster "my-eks-cluster" (N/A:N/A) [root@kops-ec2 ~]#