Code Planner 쿠버네티스 도입 트러블슈팅

Devkty·2025년 8월 28일

취업도전기

목록 보기
15/32

이번 포스팅에는 저번 시간에 이어서 Code Planner에 쿠버네티스를 도입하며 발생한 트러블슈팅에 대해서 작성해보겠습니다. 추가로 포트포워딩하는 방법도 작성했습니다.
생각보다 쉬울 것 같았던 K8s 도입이 여러 패키지 호환성 문제나 네트워크, EC2 문제가 있어 하루정도 시간이 더 걸렸습니다. 그러나 실습을 하고 실제 IP 주소로 접속도 되니 뿌듯하고 하나를 알아감에 기뻣습니다.

1일차 트러블슈팅

no space left on device 발생

진행도중 과도한 이미지 빌드시, EC2에 용량이 부족해서 발생합니다.

원인 (Cause)

Docker 빌드는 특히 여러 번 실행하거나 대용량 이미지를 빌드할 때 많은 임시 파일, 레이어, 그리고 오래된 이미지를 생성합니다. 이러한 파일들은 시간이 지남에 따라 누적되어 소규모 EC2 인스턴스의 디스크 공간을 빠르게 차지합니다.

해결 방법 (Solution)

불필요한 Docker 데이터를 삭제하여 디스크 공간을 확보해야 합니다. 가장 안전하고 효과적인 방법은 Docker에 내장된 정리 명령을 사용하는 것입니다.

1. Docker 시스템 정리 (가장 중요)

아래 명령어를 터미널에 입력하세요. 이 명령어는 사용되지 않는 모든 Docker 데이터(중단된 컨테이너, 사용되지 않는 네트워크, 오래된 이미지, 빌드 캐시 등)를 한 번에 삭제해 줍니다.

docker system prune -a
  • 명령어를 실행하면 정말로 삭제할 것인지 묻는 메시지(y/N)가 나옵니다. *y를 입력하고 Enter를 누르세요. 이 명령어로 몇 GB의 공간을 확보할 수 있습니다.

2. 디스크 공간 확인

정리 후, 아래 명령어로 디스크 공간이 얼마나 확보되었는지 확인해 보세요.

df -h

3. 다시 빌드 시도

공간이 확보된 것을 확인했다면, 이전에 실패했던 백엔드 Docker 빌드 명령어를 다시 실행하세요.

docker build -t my-backend:test .

백엔드 octokit 문제 발생

문제 원인: require() vs. import

오류 메시지가 Error [ERR_REQUIRE_ESM]: require() of ES Module ... not supported핵심입니다.

간단히 말해 다음과 같습니다. NestJS 프로젝트는 CommonJS (라이브러리를 가져오는 함수를 사용 require())라는 이전 JavaScript 모듈 시스템을 사용하여 빌드되었습니다. 그러나 GitHub 통합을 위해 사용 중인 라이브러리는 ES Modules (명령문을 사용 ) octokit라는 최신 모듈 시스템을 사용하도록 업데이트되었습니다 .import

이전 함수를 사용하여 새로운 ESM 전용 라이브러리를 직접 로드할 수 없습니다 require(). 마치 최신 USB-C 케이블을 기존 USB-A 포트에 연결하는 것과 같습니다. 어댑터 없이는 호환되지 않습니다.

두 가지 주요 옵션이 있습니다. 옵션 1은 현재 상황을 해결하는 가장 빠르고 쉬운 방법입니다 .

해결책 1: octokit 버전 다운그레이드 (가장 쉬운 방법)

가장 간단한 해결책은 octokitCommonJS( ) 시스템을 여전히 지원하는 이전 버전으로 다운그레이드하는 것입니다 require(). 버전 17은 이 설정에 완벽하게 작동하는 안정적이고 널리 사용되는 버전입니다.

  1. 현재 버전을 제거하고 이전 버전을 설치하세요.Codeplanner_Backend 로컬 머신(EC2 인스턴스가 아님)에서 프로젝트
    로 이동하여 다음 명령을 실행하세요.
npm uninstall octokit
npm install @octokit/rest@19.0.13

package.json이렇게 하면 및 파일이 업데이트됩니다 package-lock.json.

  1. 변경 사항을 GitHub 저장소에 커밋하고 푸시합니다 .
  2. EC2 인스턴스의 코드를 업데이트하세요.
cd ~/codeplanner/Codeplanner_Backend
git pull origin main
  1. Docker 이미지를 다시 빌드합니다. 먼저 Minikube Docker 환경에 있는지 확인하세요.
eval $(minikube -p minikube docker-env)
docker build -t my-backend:test .

해결책 2: 동적 import() 사용

위의 방식으로 해결이 안되어 동적으로 import했습니다.

github.service.ts 수정

아래와 같이 여러 줄의 수정이 필요합니다.

import { Injectable, NotFoundException, OnModuleInit } from '@nestjs/common';
// import { Octokit } from '@octokit/rest';
.
.
.
// 2. 타입 에러 방지를 위해 타입만 가져옵니다.
import type { Octokit } from "@octokit/rest";

@Injectable()
export class GithubService implements OnModuleInit {  // 3. OnModuleInit 추가
  private readonly githubApiUrl = 'https://api.github.com';
  private Octokit: new (options: any) => Octokit;  // 4. Octokit 클래스를 저장할 프로퍼티 추가
.
.
.
  // 5. 서비스가 초기화될 때 동적 임포트를 실행하는 메서드 추가
  async onModuleInit() {
    // 'octokit' 패키지에서 Octokit 클래스를 동적으로 가져옵니다.
    this.Octokit = (await import("@octokit/rest")).Octokit;
  }
.
.
.
  // 6. onModuleInit에서 가져온 Octokit 클래스를 사용해 인스턴스 생성
  const octokit = new this.Octokit({
    auth: githubToken.access_token,
  });

github-pull-request.service.ts 수정

필요없는 import 삭제합니다.

// import { Octokit } from '@octokit/rest'; 

위와 같이 조치하고 프로젝트 폴더에서 재빌드하면 됩니다.

docker build -t my-backend:test .

백엔드 execa 문제 발생

이젠 execa 모듈이 문제를 일으킵니다.

이 오류는 Error [ERR_REQUIRE_ESM]: require() of ES Module /app/node_modules/execa/index.js ... not supported다른 종속성 중 하나인 , execa도 ESM 전용 모듈이어서 로드할 수 없다는 것을 의미합니다 require().

해결책은 이전과 동일합니다. 다시 말하지만, 옵션 1이 이 문제를 해결하는 가장 간단하고 빠른 방법입니다.

해결책 : execa 버전 다운그레이드 (가장 쉬운 방법)

execa버전 5는 CommonJS( require()) 시스템을 완벽하게 지원하는 마지막 주요 버전입니다.

  1. 로컬 컴퓨터에서 프로젝트 폴더 로 이동합니다 Codeplanner_Backend.

  2. 다음 명령을 실행하여 호환되는 버전을 설치하세요.

    npm install execa@5
  3. tsconfig.json 에 다음 코드를 마지막에 추가합니다.

    .
    .
    .
    "noFallthroughCasesInSwitch": false,
    "esModuleInterop": true
  }
  1. package.json을 수정합니다.
"dependencies": {
    .
    .
    .
    "@octokit/rest": "^22.0.0",
    "execa": "^5.1.1",
  },
  
  "devDependencies": {
    .
    .
    .
    "@types/nodemailer": "^6.4.14",
    },

위와 같이 조치하고 빌드하면 다음과 같은 오류가 걸린다.

execa오류 메시지가 변경되어 이제 '켜짐' 상태일 때 올바른 가져오기 방법을 알려줍니다 esModuleInterop. 더 이상 사용할 수 없으며 { execa }, "기본 가져오기"를 사용해야 합니다.

4개의 파일(cppcheck.scanner.ts, clang-tidy.scanner.ts, clang-format.scanner.ts, analysis.controller.ts)의 import 형식을 다음과 같이 바꾸면된다.

수정 전

import { execa } from 'execa';

수정 후
Change { execa } to just execa.

import execa from 'execa';

이 변경으로 처음 네 가지 오류가 해결됩니다.


백엔드 nodemailer 유형 오류 ( TS2322)

nodemailer이는 , 해당 유형 정의 파일( @types/nodemailer)과 NestJS 메일러 모듈 간의 알려진 호환성 문제입니다 . 일반적으로 @types/nodemailer를 아직 완전히 호환되지 않는 버전으로 업데이트할 때 발생합니다.

해결 방법

가장 신뢰할 수 있는 해결책은 @types/nodemailer특정하고 알려진 좋은 버전으로 다운그레이드하는 것입니다.

  1. 로컬 컴퓨터에서 프로젝트 폴더 로 이동합니다 Codeplanner_Backend.
  2. 다음 명령을 실행하세요:
npm install @types/nodemailer@6.4.14

6.4.14이렇게 하면 호환되는 것으로 알려진 버전이 설치됩니다 .

  1. email.service.ts 코드수정
.
.
.
// import 추가
import { SentMessageInfo, Transporter } from 'nodemailer'; // 1. Transporter를 여기서 import 합니다.
import { MailOptions } from 'nodemailer/lib/json-transport';
.
.
.
@Injectable()
export class EmailService {
  // 2. transporter의 타입을 Transporter<SentMessageInfo>로 명확하게 지정합니다.
  private transporter: Transporter<SentMessageInfo>;
  .
  .
  .

백엔드 DB RDS 접속용 .pem 파일 인식 불가

문제 원인

오류 메시지가 ENOENT: no such file or directory, open './global-bundle.pem'핵심입니다.

  • ENOENT" E rror NO ENT ry"(또는 파일/디렉토리를 찾을 수 없음) 를 의미하는 표준 오류 코드입니다 .
  • NestJS 애플리케이션은 .이라는 파일을 읽으려고 합니다 global-bundle.pem.
  • DB_SSL_CA_PATH=./global-bundle.pem이 파일은 환경 변수에 정의된 대로 AWS RDS 데이터베이스에 대한 보안 SSL 연결을 설정하는 데 필요합니다 .
  • 이 오류는 Docker 컨테이너 내에 이 .pem파일이 없기 때문에 발생합니다 . 애플리케이션 코드는 복사했지만 이 인증서 파일은 복사하지 않았습니다.

해결 방법

백엔드의 Dockerfile을 업데이트하여 global-bundle.pem파일을 최종 이미지에 복사해야 합니다.

  1. 해당 파일이 프로젝트 global-bundle.pem의 루트 디렉토리에 있는지 확인하세요 .Codeplanner_BackendDockerfile

  2. Codeplanner_Backend/Dockerfile. 을 엽니다 .

  3. 마지막 "Runner" 단계에서는 COPY파일을 포함하는 명령을 추가합니다.

    백엔드 Dockerfile 수정합니다.

    # 빌드 단계에서 생성된 파일들만 가져옵니다.
    COPY --from=builder /app/dist ./dist
    COPY --from=builder /app/node_modules ./node_modules
    COPY --from=builder /app/package.json ./package.json
    
    # --- 이 줄을 추가하세요! ---
    # RDS SSL 연결에 필요한 인증서 파일을 복사합니다.
    COPY global-bundle.pem ./global-bundle.pem
    
    # 5000번 포트를 외부에 노출합니다.
    EXPOSE 5000
  4. 재빌딩합니다.

eval $(minikube -p minikube docker-env)
docker build -t my-backend:test .

데이터베이스 권한 오류

문제 원인 (Cause)

이 오류는 QueryFailedError: must be owner of table project_member연결하려는 데이터베이스 사용자( 파일 codeplanner기준 .env)에게 테이블 구조를 변경할 권한이 없음을 의미합니다 project_member.

이는 TypeORM 의 기능인 . 때문에 발생합니다 . 이 옵션을 활성화 하면 synchronize: trueNestJS 애플리케이션은 시작될 때마다 코드의 엔터티와 일치하도록 데이터베이스 테이블을 자동으로 변경하려고 합니다. 와 같은 명령을 실행하려고 ALTER TABLE ... DROP CONSTRAINT하지만, codeplanner사용자가 테이블의 소유자가 아니므로 PostgreSQL이 요청을 거부합니다.

이는 새로운 애플리케이션을 기존 데이터베이스에 연결할 때 매우 흔히 발생하는 문제입니다.

해결 방법

두 가지 주요 솔루션이 있습니다. 옵션 1은 프로덕션 및 테스트 환경에 가장 안전하고 권장되는 접근 방식입니다.

해결책 1: synchronize: true 비활성화 (권장)

synchronize: true옵션은 초기 개발에는 편리하지만 , 운영 환경이나 기존 데이터베이스에서는 실수로 데이터를 삭제할 수 있어 매우 위험 합니다 . 테스트 시에는 이 옵션을 비활성화하는 것이 좋습니다.

  1. 프로젝트 에서 Codeplanner_BackendTypeORM 데이터베이스 연결 구성을 찾으세요. 아마도 database.module.ts또는 이와 유사한 구성 파일에 있을 것입니다.

  2. 해당 줄을 찾아서 synchronize: true바꾸세요 synchronize: false.

    예 ( database.module.ts):

// ...
TypeOrmModule.forRootAsync({
  // ...
  useFactory: () => ({
    // ... other options
    synchronize: false, // <--- 이 값을 false로 변경하세요.
  }),
}),
// ...
  1. 변경 사항을 저장하고 Docker 이미지를 커밋, 푸시하고 다시 빌드하세요. 이제 애플리케이션이 테이블을 변경하지 않고 데이터베이스에 연결되고 오류가 사라질 것입니다.

해결책 2: 데이터베이스 사용자에게 권한 부여

또는 사용자에게 테이블 소유권을 부여할 수 있습니다 codeplanner. 이 방법은 좀 더 복잡하지만 문제를 해결하는 데 도움이 됩니다. 관리자(예: RDS 인스턴스에서 생성한 마스터 사용자)로 RDS 데이터베이스에 연결하고 SQL 명령을 실행해야 합니다.

-- Connect with your master user first
ALTER TABLE project_member OWNER TO codeplanner;
ALTER TABLE another_table OWNER TO codeplanner;
-- Repeat for all other tables...

이 솔루션을 사용하려면 데이터베이스의 모든 테이블, 뷰, 시퀀스에 대해 이 명령을 실행해야 하므로 지루할 수 있습니다.


2일차 트러블슈팅

AWS EC2와 Minikube를 이용한 Full-Stack 배포 트러블슈팅 회고록

1. 백엔드 배포 오류: ERR_REQUIRE_ESM

문제 원인

NestJS(CommonJS 기반) 프로젝트에서 최신 octokit 라이브러리(ESM 전용)를 import 하면서 모듈 시스템 충돌이 발생했습니다. Docker 빌드 환경에서 동적 import()가 제대로 처리되지 않아 런타임 오류가 계속 재발했습니다.

해결 과정

  1. 1차 시도 (실패): tsconfig.jsonmoduletarget 옵션을 수정하여 동적 import()를 지원하려 했으나, 빌드 환경의 복잡성으로 인해 실패했습니다.
  2. 2차 시도 (성공): 문제의 근본 원인인 라이브러리 버전을 직접 해결했습니다.
    • @octokit/rest 라이브러리를 CommonJS를 지원하는 19 버전으로 다운그레이드했습니다.
    • OnModuleInit과 동적 import 관련 코드를 모두 제거하고, 간단한 import { Octokit } from '@octokit/rest'; 구문으로 되돌렸습니다.
    • 빌드 시 발생하는 타입 추론 오류(TS2742)를 해결하기 위해, 관련 함수에 반환 타입으로 : Promise<any>를 명시해주었습니다.

2. 프론트엔드 외부 접속 불가

문제 원인

EC2의 공인 IP와 포트로 접속 시 Connection Timed Out 오류가 발생했습니다. 단계별 추적 결과, 쿠버네티스 클러스터가 EC2 인스턴스 위에서 실행되는 Minikube 환경이라는 것이 밝혀졌습니다. NodePort 서비스가 EC2의 실제 IP가 아닌, Minikube의 가상 내부 IP(192.168.49.2)에만 포트를 열었기 때문에 외부에서 접속할 수 없었습니다.

해결 과정

  1. 네트워크 진단: Service 생성 → AWS 보안 그룹 확인 → NACL 확인 → 라우팅 테이블 확인 → EC2 ufw 방화벽 확인. 이 모든 것이 정상이었지만 접속은 실패했습니다.
  2. 결정적 단서: EC2 터미널에서 sudo netstat -tulpn | grep <포트번호> 명령어로 확인했을 때, 해당 포트를 사용하는 프로세스가 아무것도 없었습니다. 이를 통해 문제가 쿠버네티스 내부에 있음을 확신했습니다.
  3. 최종 원인 발견: kube-proxy의 로그에서 Minikube의 내부 IP를 확인하며, 클러스터가 EC2 위에서 가상으로 동작하고 있음을 파악했습니다.
  4. 최종 해결: minikube tunnel의 복잡성 대신, kubectl port-forward 명령어를 사용하여 EC2의 네트워크와 쿠버네티스 서비스를 직접 강제로 연결했습니다.
    • -address 0.0.0.0 옵션을 사용하여 localhost뿐만 아니라 모든 외부 IP에서의 접속을 허용했습니다.

3. 프론트엔드-백엔드 통신 오류

문제 원인

  1. ERR_CONNECTION_REFUSED: 프론트엔드(웹 브라우저)가 백엔드 API를 localhost:5000으로 요청했습니다. 이는 사용자 PC를 가리키므로 당연히 연결이 거부되었습니다.
  2. CORS 오류: 백엔드의 외부 주소를 올바르게 설정한 후, 브라우저가 보안 정책(CORS)에 따라 다른 출처(frontend port ≠ backend port)로의 요청을 차단했습니다.

해결 과정

  1. 백엔드 외부 노출: 먼저 백엔드 서비스 타입을 ClusterIP에서 NodePort로 변경하고, kubectl port-forward를 사용해 외부 접속 경로를 확보했습니다.
  2. 프론트엔드에 주소 전달: Next.js는 빌드 시점에 환경 변수를 코드에 "구워버리기" 때문에, 런타임 환경 변수 주입이 실패했습니다.
    • 프론트엔드의 DockerfileARGENV를 추가하여 빌드 시점에 백엔드의 실제 외부 주소를 주입하도록 수정했습니다.
    • docker build --build-arg ... 명령어로 실제 주소를 전달하여 이미지를 빌드했습니다.
  3. CORS 정책 해결: 백엔드(NestJS)의 main.ts 파일에 app.enableCors()를 추가하여, 프론트엔드의 외부 주소(http://<EC2_IP>:<프론트엔드_포트>)를 허용 목록에 등록했습니다.

4. 빌드 환경 리소스 부족

문제 원인

  1. ENOSPC: no space left on device: docker buildnpm install 단계에서 EC2 서버의 디스크 용량이 부족했습니다.
  2. 빌드 멈춤 현상: Creating an optimized production build... 단계에서 빌드가 멈췄습니다. 이는 Next.js 빌드 과정에서 과도한 메모리를 사용하여 EC2의 RAM이 부족해졌기 때문입니다.

해결 과정

  1. 디스크 확보: docker system prune -a -f 명령어로 사용하지 않는 도커 이미지, 캐시, 볼륨을 모두 삭제하여 공간을 확보했습니다.
  2. 메모리 확보: sudo fallocate -l 2G /swapfile 등의 명령어를 통해 2GB의 스왑 파일(가상 메모리)을 생성하고 활성화하여 빌드에 필요한 메모리를 추가로 제공했습니다.

최종 결과 및 완성된 배포 방법

긴 트러블슈팅 끝에, EC2 인스턴스 위의 Minikube 환경에서 Full-Stack 애플리케이션을 안정적으로 배포하는 워크플로우를 완성했습니다.

  • 최종 아키텍처:
    • Frontend (Next.js): NodePort 서비스로 쿠버네티스 내부에 배포.
    • Backend (NestJS): NodePort 서비스로 쿠버네티스 내부에 배포.
    • 외부 연결: EC2 인스턴스에서 두 개의 kubectl port-forward 프로세스가 각각 프론트엔드와 백엔드 서비스를 외부 인터넷과 연결하는 다리 역할을 수행합니다.
  • 핵심 명령어 (EC2에서 항상 실행되어야 하는):
    # 터미널 1: 프론트엔드 연결
    kubectl port-forward service/frontend-service 30986:3000 --address 0.0.0.0
    
    # 터미널 2: 백엔드 연결
    kubectl port-forward service/backend-service 31500:5000 --address 0.0.0.0
  • 배포 프로세스:
    1. 코드 수정 (필요시 백엔드 main.ts에 CORS 주소 추가).

      // ⚠️⚠️⚠️배포환경 관련 설정 주의 필요!!!⚠️⚠️⚠️
      
      import * as crypto from 'crypto';
      // crypto 모듈을 전역으로 설정
      if (!(global as any).crypto) {
        (global as any).crypto = crypto.webcrypto;
      }
      
      import { NestFactory } from '@nestjs/core';
      import { AppModule } from './app.module';
      
      async function bootstrap() {
        const app = await NestFactory.create(AppModule);
      
        const allowedOrigins = [
          'http://localhost:3000', // 로컬 개발용
          'http://43.202.166.254:30986', // 실제 프론트엔드 주소
        ];
      
        // 모든 라우트에 /api 프리픽스 적용
        // app.setGlobalPrefix('api');
      
        // .env에서 CORS_ORIGIN을 불러와서 배열로 변환
        // const corsOrigin = process.env.CORS_ORIGIN?.split(',') || ['http://localhost:3000'];  // 개발 환경시: http://localhost:3000, 배포 환경시: 배포 주소
        app.enableCors({
          origin: allowedOrigins,
          methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
          credentials: true,
        });
      
        const port = process.env.PORT || 5000;  // 백엔드 포트를 5000으로 설정
        await app.listen(port);
        console.log(`🚀 Listening on ${port}`);
      }
      bootstrap();
      
    2. Dockerfile 확인 (프론트엔드는 ARG 설정 확인).

    3. docker build 실행 (프론트엔드는 -build-arg로 백엔드 주소 주입).

    4. docker push로 이미지 레지스트리에 업로드.

    5. kubectl apply -f <deployment>.yaml로 변경사항 적용.

    6. kubectl rollout restart deployment <deployment-name>으로 파드 재시작.

    7. 위의 port-forward 명령어들이 계속 실행 중인지 확인.

→ 이로써 테스팅 완료하였다.

http://43.202.166.254:30986/


포트포워딩 자동화

기존에는 SSH 연결을 유지해둬야하는 문제가 있었습니다. 그래서 그걸 해결하기 위해 EC2에 백그라운드 서비스로 포트포워딩을 등록하면 됩니다.

근본 원인

우리가 이 방법을 써야 하는 이유는 EC2 인스턴스와 그 안에서 실행되는 Minikube 가상 머신이 서로 다른 네트워크 공간에 있기 때문입니다. port-forward는 이 둘 사이에 다리를 놓아주는 역할을 합니다.

해결 방법

port-forward를 백그라운드 서비스로 등록하면 됩니다.

해결책은 간단합니다. 우리가 수동으로 실행하던 port-forward 명령어를 EC2(Ubuntu)가 부팅될 때마다 자동으로 실행하고, 문제가 생기면 알아서 재시작해주는 시스템 서비스(Systemd)로 등록하는 것입니다.

이렇게 하면 터미널 창을 모두 닫아도, EC2 서버가 살아있는 한 포트 연결은 계속 유지됩니다.

설정 방법 (단계별 가이드)

EC2 터미널에서 아래 단계를 순서대로 따라오시면 됩니다.

1단계: 프론트엔드 서비스 파일 생성

먼저 프론트엔드 연결을 위한 서비스 파일을 만들겠습니다.

sudo nano /etc/systemd/system/k8s-frontend-forward.service

nano 편집기가 열리면, 아래 내용을 그대로 복사해서 붙여넣으세요.

[Unit]
Description=Kubernetes Frontend Port Forwarding
After=network.target

[Service]
ExecStart=/usr/local/bin/kubectl --kubeconfig=/home/ubuntu/.kube/config port-forward service/frontend-service 30986:3000 --address 0.0.0.0
Restart=always
User=ubuntu
Group=ubuntu

[Install]
WantedBy=multi-user.target

붙여넣은 후, Ctrl + X를 누르고, Y를 누른 다음, Enter를 눌러서 저장하고 빠져나오세요.

2단계: 백엔드 서비스 파일 생성

이제 백엔드 연결을 위한 서비스 파일을 만들겠습니다.

sudo nano /etc/systemd/system/k8s-backend-forward.service

nano 편집기가 열리면, 아래 내용을 그대로 복사해서 붙여넣으세요.

[Unit]
Description=Kubernetes Backend Port Forwarding
After=network.target

[Service]
ExecStart=/usr/local/bin/kubectl --kubeconfig=/home/ubuntu/.kube/config port-forward service/backend-service 31500:5000 --address 0.0.0.0
Restart=always
User=ubuntu
Group=ubuntu

[Install]
WantedBy=multi-user.target

마찬가지로 Ctrl + X -> Y -> Enter로 저장하고 나옵니다.

3단계: 서비스 등록 및 실행

이제 우리가 만든 두 개의 서비스 파일을 시스템에 등록하고 실행할 차례입니다.

# 1. 시스템에 새로운 서비스 파일이 생겼다고 알립니다.
sudo systemctl daemon-reload

# 2. 두 서비스를 부팅 시 자동으로 시작되도록 활성화합니다.
sudo systemctl enable k8s-frontend-forward.service
sudo systemctl enable k8s-backend-forward.service

# 3. 지금 당장 두 서비스를 시작합니다.
sudo systemctl start k8s-frontend-forward.service
sudo systemctl start k8s-backend-forward.service

4단계: 서비스 상태 확인

두 서비스가 잘 실행되고 있는지 아래 명령어로 확인할 수 있습니다.

sudo systemctl status k8s-frontend-forward.service
sudo systemctl status k8s-backend-forward.service

Active: active (running) 이라는 녹색 글자가 보이면 성공입니다! (q를 눌러서 빠져나올 수 있습니다.)

이제 모든 터미널 창을 닫고 SSH 연결을 끊어도, 프론트엔드와 백엔드는 EC2의 공인 IP와 지정된 포트로 계속해서 접근할 수 있습니다.

포트포워딩 문제 사항

저는 다음과 같은 경로 확인에 실패하는 문제가 있었습니다.

문제의 원인

status=203/EXEC 오류는 Systemd(시스템 서비스 관리자)가 ExecStart에 지정된 명령어(/usr/bin/kubectl)를 실행할 수 없거나 찾을 수 없다는 뜻입니다.

우리가 터미널에서 kubectl을 실행할 때는 셸이 알아서 명령어의 위치를 찾아주지만, Systemd는 매우 제한된 환경에서 실행되기 때문에 명령어의 정확한 전체 경로를 알려주지 않으면 찾지 못하는 경우가 많습니다. 또한, kubectl이 클러스터 접속에 필요한 설정 파일(~/.kube/config)의 위치도 명시해주는 것이 가장 안전합니다.

해결 방법

Systemd에게 kubectl의 정확한 위치와 설정 파일 경로를 알려주겠습니다.

1단계: kubectl의 실제 경로 찾기

먼저, 터미널에서 아래 명령어를 실행하여 kubectl의 실제 전체 경로를 찾아 복사해두세요.

which kubectl

2단계: 서비스 파일 수정

이제 이 경로를 서비스 파일에 적용하겠습니다. 두 개의 서비스 파일을 모두 수정해야 합니다.

전과 같이 두가지의 파일을 만들어서 수정하면 됩니다. 위에 있는 두가지의 내용은 이미 문제 해결이 완료되어 있는 파일입니다.

# 1. 변경된 서비스 파일 다시 불러오기
sudo systemctl daemon-reload

# 2. 두 서비스 다시 시작
sudo systemctl restart k8s-frontend-forward.service
sudo systemctl restart k8s-backend-forward.service

sudo systemctl status k8s-frontend-forward.service
sudo systemctl status k8s-backend-forward.service

해결된 것을 확인할 수 있습니다.

profile
모든걸 기록하며 성장하고 싶은 개발자입니다. 현재 크래프톤 정글 8기를 수료하고 구직활동 중입니다.

0개의 댓글