no image
[Infra] Docker, Docker-compose 설치하는 법 (Ubuntu)
시스템 업데이트 sudo apt updatesudo : 관리자 권한으로 실행apt update : 최신 패키지 업데이트 사전 필수 패키지 설치 sudo apt install -y apt-transport-https ca-certificates curl software-properties-common 패키지ca-certificates : 서버가 HTTPS 통신할 때 필요한 공인 인증서들을 모아둔 패키지. 인증된 사이트(예: Docker 저장소)에 안전하게 연결하기 위해 필요.curl: 명령어로 인터넷 서버에서 파일을 다운로드하거나 요청을 보낼 수 있는 툴. Docker GPG 키나 설치 스크립트를 다운로드할 때 사용.gnupg: 소프트웨어 설치 시 신뢰할 수 있는지 검증하는 "전자 서명" 기능을..
2025.10.04
no image
[ubuntu] ufw (방화벽) 세팅하기
ufw 활성화 여부 체크 기본적으로 EC2 인스턴스 생성하면 방화벽 설정 꺼져있음. sudo ufw status를 통해 방화벽 설정 체크할 수 있는데, inactive라는 것은 방화벽 자체가 꺼져있다는 것. ufw 활성화 전 22번 포트 열기포트 열기 전 ufw 활성화해버리면, MobaXterm같은 툴로 해당 EC2에 ssh 접근이 불가능 함.따라서 ssh접근 가능하도록 최소한 22번 포트는 열어둔 후 → 방화벽 설정 켜야함!여기선 열어야 할 포트 모두 열고 ufw 활성화할 예정. sudo ufw allow 1. ssh로 접근하기 위한 22번 포트 열기 2. 웹서비스를 위한 http(80), https(443) 포트 열기 나머지 포트는??내가 만들 서비스는 80, 443, 22포트만..
2025.10.02
no image
MobaXterm 설치 & .pem 키로 접속하기
MobaXterm 설치하기아래 링크로 들어가 둘 중 아무거나 클릭하여 설치하기.https://mobaxterm.mobatek.net/download-home-edition.html MobaXterm free Xserver and tabbed SSH client for WindowsThe ultimate toolbox for remote computing - includes X server, enhanced SSH client and much more!mobaxterm.mobatek.net pem키로 ssh 접속하기Session 클릭 SSH 클릭 host에 EC2 host pc url 입력Use private key 체크 후 .pem 파일 첨부OK 선택
2025.09.06
no image
[AWS] NACL 설정하기
NACL이란NACL ⇒ Network Access Control ListSubnet의 접근 제어를 관리한다! 문지기의 역할임은 보안 그룹과 비슷 보안 그룹과의 차이보안그룹이 인스턴스에 대한 접근을 제어하는 용도라면,NACL은 Subnet에 대한 접근을 제어하는 용도이다! VPC, Subnet, 인스턴스 무슨 차이?VPC ⇒ Virtual Private Cloud 즉, 개인 당 주어지는 네트워크 공간이다. 아파트 단지 같은 개념Subnet ⇒ 네트워크 공간을 작은 네트워크 단위로 쪼갠 것. 아파트 한 동(101동, 102동..) 개념인스턴스 ⇒ 네트워크 공간을 사용하는 하나의 컴퓨터. 집 한 채(203호, 204호…) 개념 특징하나의 Subnet에 1개의 NACL만 할당 가능허용 규칙 & 차..
2025.09.05
[AWS] 보안그룹 설정하기
보안그룹이란?인스턴스로 외부에서 내부로 들어오는 트래픽 (인바운드) 내부에서 외부로 나가는 트래픽(아웃바운드)을 관리하는 문지기 같은 역할.프로토콜 유형, 포트, IP 등 지정 가능대표 포트번호: http - 80 ssh - 22 https - 443보안그룹 특징허용 규칙만 가능. 특정 IP만 비허용 불가.아웃바운드 규칙은 기본적으로 모든 트래픽 (0.0.0.0) 허용 상태.하나의 인스턴스에 여러 보안 그룹 설정 가능각 인스턴스에 여러 보안 그룹 할당 가능 (각 보안 그룹의 모든 룰 적용)한 번 인바운드/아웃바운드 통과한 트래픽은 아웃바운드/인바운드 규칙 적용 X ⇒ stateful 하다! ⇒ 즉, 아웃바운드를 전부 막아놔도, 인바운드에 443 포트 신호가 들어..
2025.09.04
no image
EC2 인스턴스 생성
새로운 서비스를 위한 서버 환경을 구축하기 위해 EC2 인스턴스를 생성했다. 보안 관련 설정은 추후 하기로 하고, 우선 EC2 인스턴스만 생성해봤다. 우선 AWS Console에 들어가 로그인 후 인스턴스 시작 버튼을 클릭한다. AMI 선택AMI: EC2 인스턴스를 구동시키기 위한 정보들을 모은 단위. OS를 어떤 것을 쓸건지, 소프트웨어는 어떤 게 설치되는지 등의 정보를 모아 EC2를 실행할 때 사용한다. 나는 Ubuntu 22.04를 선택했다. 인스턴스 유형 선택t3.small선택예상 요금 : 시간당 0.0209$ ⇒ 월 15$https://aws.amazon.com/ko/ec2/instance-types/ 클라우드 컴퓨팅 인스턴스 - Amazon EC2 인스턴스 유형 - AWS aws.amaz..
2025.08.27
no image
Oracle Cloud 인스턴스 생성
Oracle Cloud 사용하는 이유?1. 무료 서버의 기간 제한이 없다. AWS는 프리티어 기간이 1년.GCP(Google Cloud Platform)의 경우, 300달러로 넉넉하지만 90일의 제한 기간이 있음.반면, 오라클 클라우드는 무료로 제공되는 VM의 제한기간이 없어 계속해서 서버를 띄워둘 수 있다!! 2. VM을 2대나 지원한다. AWS와 유사한 스펙의 무료 서버를 2대나 지원한다.여러 개의 프로젝트를 띄우려는 나에게는 2대를 이용해 띄우는게 훨씬 안정적이고 부담이 덜하다.또한, 실 서비스용 서버를 만든다고 해도, 하나는 서버용 하나는 DB용으로 나눠 더 안정적 운영이 가능하다. 회원가입우선 회원가입 먼저 한다.나는 리전을 싱가포르로 선택함 (하지만 이 글을 보는 사람들은 다른 곳을 선택..
2025.08.03
no image
[트러블슈팅 - Nginx] cannot load certificate "/etc/letsencrypt/live/www.film-moa.com/fullchain.pem": BIO_new_file() failed...
에러 메시지 / 문제 상황Docker Compose로 Nginx 컨테이너 실행 시 다음과 같은 오류 발생함.cannot load certificate "/etc/letsencrypt/live/www.film-moa.com/fullchain.pem": BIO_new_file() failed...초기에는 페이지가 제대로 렌더링되지 않고 Nginx 기본 화면만 표시되거나 컨테이너가 반복적으로 종료되는 현상 발생함. 원인최종 원인은 Nginx 컨테이너가 SSL 인증서 파일에 접근할 권한이 없어서 인증서를 불러오는 데 실패했던 것이었음실제로 MobaXterm을 통해 파일 접근했을 때, /etc/letsencrypt까지는 접근이 되나, 이후 /live, /archive 디렉토리에는 접근이 불가했음 (Permis..
2025.06.05
no image
[트러블슈팅 - Docker] E: List directory /var/lib/apt/lists/partial is missing. - Acquire (13: Permission denied)
에러메세지 / 문제상황컨테이너 내에서 명령어 실행 시 apt-get update 실행 시 다음과 같은 권한 에러 발생: 원인 컨테이너가 jenkins 사용자 권한으로 실행되고 있었으며, 해당 사용자는 apt 명령 실행에 필요한 시스템 디렉토리에 대한 쓰기 권한이 없음. jenkins 이미지는 기본적으로 루트가 아닌 사용자로 동작함. 시도 1services: jenkins: image: jenkins/jenkins:lts ports: - "8080:8080" volumes: - jenkins_home:/var/jenkins_home - /var/run/docker.sock:/var/run/docker.sock restart: unless-stopped→..
2025.06.04
[트러블슈팅 - Docker / Jenkins / SpringBoot] Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured. Reason: Failed to determine suitable jdbc url
에러메세지 / 문제상황Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.Reason: Failed to determine suitable jdbc url 백엔드 개발자들은 많이 보는 에러일 것이다.springboot 서버를 실행할 때, 설정파일 (application.yaml)을 제대로 못읽었을 때 발생하는 에러이다.. 원인Spring Boot 프로젝트에서 데이터베이스 연결 정보를 application.yml과 application-secret.yml 두 개 파일로 나눠 관리 중이었음. Jenkins를 이용해 application-secret.yml을 ..
2025.06.03

 

 

 

 

 

시스템 업데이트 

 

sudo apt update
  • sudo : 관리자 권한으로 실행
  • apt update : 최신 패키지 업데이트

 

 

 

사전 필수 패키지 설치

 

sudo apt install -y apt-transport-https ca-certificates curl software-properties-common

 

 패키지

  • ca-certificates : 서버가 HTTPS 통신할 때 필요한 공인 인증서들을 모아둔 패키지. 인증된 사이트(예: Docker 저장소)에 안전하게 연결하기 위해 필요.
  • curl: 명령어로 인터넷 서버에서 파일을 다운로드하거나 요청을 보낼 수 있는 툴. Docker GPG 키나 설치 스크립트를 다운로드할 때 사용.
  • gnupg: 소프트웨어 설치 시 신뢰할 수 있는지 검증하는 "전자 서명" 기능을 제공하는 툴. Docker GPG 키를 확인하고 인증하는 데 필요해.
  • lsb-release: 서버가 어떤 리눅스 배포판 버전인지 (Ubuntu 22.04, 등)을 알려주는 유틸리티. Docker 저장소를 설정할 때 리눅스 버전을 자동으로 가져올 때 필요
  • software-properties-common: APT 저장소를 쉽게 추가하거나 관리하는 명령어(add-apt-repository)를 제공해. Docker 저장소 등록이나 PPA 추가할 때 사용

 

패키지가 설치되어있는지 확인하는 명령어

dpkg -l | grep [패키지명]

 

 

 

Docker 공식 GPG 키 다운로드

sudo mkdir -p /etc/apt/keys
  • -p : 디렉터리가 이미 존재해도 에러 없이 넘어감
curl -fsSL <https://download.docker.com/linux/ubuntu/gpg> | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

: curl -fsSL

  • Docker GPG 키를 다운로드한다.
  • f (실패 시 메시지 표시)
  • s (출력 메시지 숨김)
  • L (리다이렉트를 따라가서 다운로드)

: sudo gpg --dearmor

  • 다운로드한 GPG키를 다운로드 한 GPG 키를 바이너리 형식으로 변환

: -o /usr/share/keystrings/docker-archive-keyring.gpg

  • GPG 키를 저장할 경로를 지정함

 

GPG키란?

  • "파일이 신뢰할 수 있는 출처인지" 확인하는 전자 서명
  • 소프트웨어를 다운로드할 때, 해커가 조작한 파일이 아닌지 검증할 때 사용한다.
  • Docker 설치 시 GPG 키로 패키지의 무결성과 진짜 여부를 확인한다.

 

Docker 저장소 추가

echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] <https://download.docker.com/linux/ubuntu> $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
  • dpkg --print-architecture: 시스템 아키텍처(예: amd64)를 가져온다
  • signed-by : 위에서 다운 받은 GPG 키 지정
  • lsb_release -cs : 배포판 코드네임(예: jammy)을 가져온다.

 

다시한번 패키지 목록 업데이트

sudo apt update

 

 

 

Docker, Docker compose 설치

sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
  • docker-ce : Docker 엔진 (Community Edition). 컨테이너를 생성, 실행, 관리하는 핵심 서버 컴포넌트
  • docker-ce-cli: Docker CLI (Command Line Interface). docker run, docker ps 같은 명령어를 제공하는 커맨드 라인 도구
  • containerd.io: Docker 내부에서 컨테이너를 실제로 실행시키는 저수준 런타임. 독립 실행 가능하며 Kubernetes에서도 사용된다.
  • docker-buildx-plugin: 다양한 플랫폼(arm64, amd64 등)과 고급 빌드를 지원하는 Docker 빌드 플러그인. docker buildx build 명령어를 사용 가능하게 한다.
  • docker-compose-plugin: 여러 컨테이너를 정의하고 함께 실행할 수 있게 해주는 Compose 기능을 Docker CLI에 통합한 플러그인. docker compose 명령어 제공

 

 

 

[ubuntu] ufw (방화벽) 세팅하기

친환경 개발자
|2025. 10. 2. 22:24

 

 

ufw 활성화 여부 체크

 

기본적으로 EC2 인스턴스 생성하면 방화벽 설정 꺼져있음.

 

sudo ufw status

를 통해 방화벽  설정 체크할 수 있는데, inactive라는 것은 방화벽 자체가 꺼져있다는 것.

 

 

ufw 활성화 전 22번 포트 열기

포트 열기 전 ufw 활성화해버리면, MobaXterm같은 툴로 해당 EC2에 ssh 접근이 불가능 함.

따라서 ssh접근 가능하도록 최소한 22번 포트는 열어둔 후 → 방화벽 설정 켜야함!

여기선 열어야 할 포트 모두 열고 ufw 활성화할 예정.

 

sudo ufw allow <포트번호>

 

    1. ssh로 접근하기 위한 22번 포트 열기

 

    2. 웹서비스를 위한 http(80), https(443) 포트 열기

 

 

나머지 포트는??

내가 만들 서비스는 80, 443, 22포트만 외부에 노출시키고,

80 혹은 443포트로 들어오는 요청만 받은 후

Nginx 리버스프록시를 통해 내부로 요청을 분기시킬 것이다.

따라서, 외부 포트는 80, 443, 22만 열어두고 나머지 8080포트 등 다른 포트는 열지 않는다.

 

ufw 활성화

 

sudo ufw enable

 

최종 ufw 목록 확인

 

 

sudo ufw status numbered

MobaXterm 설치하기

아래 링크로 들어가 둘 중 아무거나 클릭하여 설치하기.

https://mobaxterm.mobatek.net/download-home-edition.html

 

 

MobaXterm free Xserver and tabbed SSH client for Windows

The ultimate toolbox for remote computing - includes X server, enhanced SSH client and much more!

mobaxterm.mobatek.net

 

 

pem키로 ssh 접속하기

Session 클릭

 

 

 

SSH 클릭

 

 

 

 

  • host에 EC2 host pc url 입력
  • Use private key 체크 후 .pem 파일 첨부
  • OK 선택

[AWS] NACL 설정하기

친환경 개발자
|2025. 9. 5. 17:32

NACL이란

NACL ⇒ Network Access Control List

Subnet의 접근 제어를 관리한다! 문지기의 역할임은 보안 그룹과 비슷

 

 

 

 

보안 그룹과의 차이

보안그룹이 인스턴스에 대한 접근을 제어하는 용도라면,

NACL은 Subnet에 대한 접근을 제어하는 용도이다!

 

 

 

 

VPC, Subnet, 인스턴스 무슨 차이?

  • VPC ⇒ Virtual Private Cloud 즉, 개인 당 주어지는 네트워크 공간이다. 아파트 단지 같은 개념
  • Subnet ⇒ 네트워크 공간을 작은 네트워크 단위로 쪼갠 것. 아파트 한 동(101동, 102동..) 개념
  • 인스턴스 ⇒ 네트워크 공간을 사용하는 하나의 컴퓨터. 집 한 채(203호, 204호…) 개념

 

 

 

특징

  1. 하나의 Subnet에 1개의 NACL만 할당 가능
  2. 허용 규칙 & 차단 규칙 모두 설정 가능 (↔ 보안그룹은 허용 규칙만 가능)
  3. 규칙 별 우선 순위 적용이 가능 (낮은 수일수록 높은 우선순위)
  4. 한 번 인바운드/아웃바운드를 통과한 트래픽이라도, 아웃바운드/인바운드 규칙 적용됨 (↔ 보안그룹은 적용 안됨) ⇒ statless

 

 

 

규칙 우선순위

낮은 수일수록 우선순위가 적용되어, 상충되는 규칙이 있을 경우 우선 순위에 따라 규칙을 수행한다.

101번 규칙에 80포트를 허용하더라도, 100번 규칙으로 80포트를 막으면 80포트는 막힌다!

 

 

 

 

세팅하기

인바운드

  • 외부 인터넷용은 여러 클라이언트에서 모두 접근 가능하도록 해야하므로, 기본 적으로는 allow
  • 추후 차단해야 할 IP 있을 때 차단하기.
  • 보안 그룹에서 설정 철저히 해야 함.

아웃바운드

NACL은 stateless 이므로, 아웃바운드까지 함께 적용해줘야 한다!

  • 기본적으로 아웃바운드는 다 열어두는 편이라고 함.
  • 단 보통 아웃바운드의 포트가 1024~65535이므로 그렇게 설정함.
  • 그러나 초기에는 포트를 모두 열어두는게 좋음. ssh 접근해서 sudo apt update 등 세팅할 때는 포트가 열려있어야 통신 가능.

서브넷과 연결하기

[AWS] 보안그룹 설정하기

친환경 개발자
|2025. 9. 4. 23:17

보안그룹이란?

  • 인스턴스로 외부에서 내부로 들어오는 트래픽 (인바운드) 내부에서 외부로 나가는 트래픽(아웃바운드)을 관리하는 문지기 같은 역할.
  • 프로토콜 유형, 포트, IP 등 지정 가능
  • 대표 포트번호:
          http - 80
          ssh - 22
          https - 443

보안그룹 특징

  1. 허용 규칙만 가능. 특정 IP만 비허용 불가.
  2. 아웃바운드 규칙은 기본적으로 모든 트래픽 (0.0.0.0) 허용 상태.
  3. 하나의 인스턴스에 여러 보안 그룹 설정 가능
  4. 각 인스턴스에 여러 보안 그룹 할당 가능 (각 보안 그룹의 모든 룰 적용)
  5. 한 번 인바운드/아웃바운드 통과한 트래픽은 아웃바운드/인바운드 규칙 적용 X
         ⇒ stateful 하다!
         ⇒ 즉, 아웃바운드를 전부 막아놔도, 인바운드에 443 포트 신호가 들어오면 해당 포트로 응답을 함.

 

 

설정하기

인바운드 규칙

  • 웹서비스이므로 HTTP, HTTPS로 들어오는 모든 요청을 받도록 우선 설정.
  • SSH를 통한 접근은 관리자(나)만 접근할 수 있도록 특정 IP만 열어두기.⇒ SSM (session manager)로 관리할 수 있음.
  • ⇒ 만약 도서관, 카페, 회사 등 여러 곳에서 접근해야 하는 경우라면 특정 IP만 열어두는게 불가능함

아웃바운드 규칙

  • 기본적으로 보안그룹은 stateful 하므로, 모든 위치로 허용한다.
  • 추후 필요할 경우에만 설정.

 

 

설정이 완료됐다면?

해당 보안 그룹을 적용하고 싶은 인스턴스에 적용하면 된다.

용도에 따라 보안 그룹을 미리 설정해두고, 인스턴스를 생성해서 바로 적용해놓자!

EC2 인스턴스 생성

친환경 개발자
|2025. 8. 27. 19:34

새로운 서비스를 위한 서버 환경을 구축하기 위해 EC2 인스턴스를 생성했다.

 

보안 관련 설정은 추후 하기로 하고, 우선 EC2 인스턴스만 생성해봤다.

 

 


우선 AWS Console에 들어가 로그인 후 인스턴스 시작 버튼을 클릭한다.

 

 

AMI 선택

AMI: EC2 인스턴스를 구동시키기 위한 정보들을 모은 단위.

 

OS를 어떤 것을 쓸건지, 소프트웨어는 어떤 게 설치되는지 등의 정보를 모아 EC2를 실행할 때 사용한다.

 

나는 Ubuntu 22.04를 선택했다.

 

 

인스턴스 유형 선택

  • t3.small선택
  • 예상 요금 : 시간당 0.0209$ ⇒ 월 15$

https://aws.amazon.com/ko/ec2/instance-types/

 

클라우드 컴퓨팅 인스턴스 - Amazon EC2 인스턴스 유형 - AWS

 

aws.amazon.com

여기 EC2 성능 별 요금이 적혀있다.

 

우선 개발 단계이므로 낮은 성능의 EC2 로 선정.

 

 

키페어 생성

  • 추후 외부에서 이 EC2 인스턴스 내부로 접근할 수 있도록 하는 출입키 개념.
  • 절대 잃어버리면 안되니 잘 보관해야 함 (잃어버리면 EC2를 새로 구축해야 함)

이름을 적당히 입력하고, RSA선택 & .pem 선택 

이후 키 페어 생성 클릭

 

보안 그룹 생성

  • 특정 IP에서만 서버에 접근하게 하는 등의 보안 설정을 할 수 있음
  • 우선은 기본으로 두고 생성. 추후 설정할 것.

 

 

스토리지(볼륨)

  • 스토리지는 컴퓨터의 하드디스크와 같은 역할.

요금표!

 

gp2보다 gp3가 GB당 요금이 저렴하고, 성능은 더 좋다. ⇒ 무료인 3000IOPS, 125MB/s처리량으로 gp3 선택했다 !

 

  • IOPS는 초당 I/O작업수를 말하고, 처리량은 한번에 옮길 수 있는 데이터양을 말한다.
  • 예상 요금 ⇒ 0.0912$/GB * 30GB ⇒ 월 2.736$

 

인스턴스 생성 클릭

클릭하면 인스턴스 생성 완료 메세지가 뜰 것이다.

 

 

 

탄력적 IP 할당하기

EC2에서는 기본적으로 인스턴스를 다시 시작할 때마다 새로운 IP가 할당된다.

 

매번 접속 IP가 바뀐다면, 유지보수에도 번거롭고,

 

탄력적 IP를 할당하지 않거나 할당해놓고 인스턴스 중지시켜놓으면 요금이 발생하므로 반드시 할당하고 관리 주의할 것.

탄력적 IP 주소 할당 클릭

 

  • 기본적으로 선택된 값으로 두고 할당 클릭하면 바로 IP가 할당됨
  • 할당된 IP를 인스턴스와 연결하는 작업이 필요함

 

최종 연결 확인!!

Oracle Cloud 인스턴스 생성

친환경 개발자
|2025. 8. 3. 08:15

Oracle Cloud 사용하는 이유?

1. 무료 서버의 기간 제한이 없다.

 

AWS는 프리티어 기간이 1년.

GCP(Google Cloud Platform)의 경우, 300달러로 넉넉하지만 90일의 제한 기간이 있음.

반면, 오라클 클라우드는 무료로 제공되는 VM의 제한기간이 없어 계속해서 서버를 띄워둘 수 있다!!

 

2. VM을 2대나 지원한다.

 

AWS와 유사한 스펙의 무료 서버를 2대나 지원한다.

여러 개의 프로젝트를 띄우려는 나에게는 2대를 이용해 띄우는게 훨씬 안정적이고 부담이 덜하다.

또한, 실 서비스용 서버를 만든다고 해도, 하나는 서버용 하나는 DB용으로 나눠 더 안정적 운영이 가능하다.

 

 

 

회원가입

우선 회원가입 먼저 한다.

  • 나는 리전을 싱가포르로 선택함  (하지만 이 글을 보는 사람들은 다른 곳을 선택하기를.. 가용 자원이 없을 확률이 있습니다.)
    • 서울이나 일본은 인스턴스를 1개밖에 생성 못할 수도 있다고 하여 다른 나라를 선택해야 했음.
    • https://www.cloudping.info/
    • 리전별 속도를 확인할 수 있는 사이트인데, 다른 지역 보다 싱가포르가 잘 나오는 편이여서 선택.

 

 

VCN 생성

 

인스턴스 생성 전, vcn 먼저 생성해줘야 한다. 

인스턴스 생성 단계에서 VCN을 선택하는 란이 있는데, 그 때가서 만들면 꼬이기 때문.

Oracle Cloud Console 접속 → 좌상단 메뉴 → Networking → Virtual cloud network → Create VCN

 

 

이름 설정 및 구획 선택.

컴파트먼트는 따로 건들지 않았다면 root가 기본으로 설정됨.

 

 

이후 Create 클릭하면

생성 완료!

 

ssh 키 생성

인스턴스 만들기 전, ssh키도 미리 생성해줘야 한다.

인스턴스 만들 때 ssh키 등록하는 부분이 있기 때문이다.

MobaXterm 으로 생성할 것이다.

tools → MobaKeyGen 클릭

 

 

 

 

Generate 클릭

 

  1. 메모장에 ssh-rsa ~~~ 부분 모두 드래그해서 복사한 뒤, .pub 확장자로 저장

  2. save private key 버튼 눌러 .ppk 저장

잃어버리지 않게 저장 완료 후 폴더 위치를 잘 기억해둘 것.

 

 

인스턴스 만들기

VM 이름을 입력하고, 컴파트먼트도 선택한다.

 

  • 이미지
    • ubuntu 선택 ⇒ Canonical ubuntu 22.04 선택 (Arm용)
  • 셰입
    • A1.Flex 선택

 

 

그리고, 

2번의 보안 설정은 우선 기본값으로 두고 넘어갔다. 추후 생성 후에 조정할 수 있다.

 

 

3번 네트워킹 설정

vnc 이름 설정 후

아까 미리 만들어둔 vcn과 서브넷을 선택한다.

ipv4도 할당되도록 선택할 것.

 

 

 

 

4번 Storage

부트볼륨은 OS인 우분투가 설치되는 디스크.

기본 설정에서 건드리지 않고 넘어감.

 

 

이후 Create 버튼을 눌러 생성하면 완료!!

 

에러 메시지 / 문제 상황

Docker Compose로 Nginx 컨테이너 실행 시 다음과 같은 오류 발생함.

cannot load certificate "/etc/letsencrypt/live/www.film-moa.com/fullchain.pem": BIO_new_file() failed...

초기에는 페이지가 제대로 렌더링되지 않고 Nginx 기본 화면만 표시되거나 컨테이너가 반복적으로 종료되는 현상 발생함.

 

 


 

원인

  • 최종 원인은 Nginx 컨테이너가 SSL 인증서 파일에 접근할 권한이 없어서 인증서를 불러오는 데 실패했던 것이었음
  • 실제로 MobaXterm을 통해 파일 접근했을 때, /etc/letsencrypt까지는 접근이 되나, 이후 /live, /archive 디렉토리에는 접근이 불가했음 (Permission denied 에러)
  • 호스트에서 /etc/letsencrypt/live/... 경로의 인증서 파일들은 심볼릭 링크로 구성되어 있는데,
  • 실제 인증서 파일이 있는 /etc/letsencrypt/archive/... 경로에 대한 권한이 컨테이너 내부에서 부족했기 때문임.

 


 

시도

시도 1

인증서 파일명에 '1'이 붙은 문제를 의심하여 nginx.conf 수정

  • 중간에 certbot문제라고 생각하여 인증서를 재설치했었음 
  • sudo certbot --nginx -d www.film-moa.com -d film-moa.com

  • /etc/letsencrypt/archive 폴더를 ls -l 명령어로 확인 시 파일명 뒤에 1이 붙어 있는 것을 확인하여 nginx.conf파일에 파일명을 수정함

변경전

  # 2. HTTPS(443포트) 요청을 처리하는 서버 블록
  server {
    listen 443 ssl;
    server_name www.film-moa.com film-moa.com;

    ssl_certificate /etc/letsencrypt/live/www.film-moa.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/www.film-moa.com/privkey.pem;

    ... 중략 ...
  }

 

변경후

  # 2. HTTPS(443포트) 요청을 처리하는 서버 블록
  server {
    listen 443 ssl;
    server_name www.film-moa.com film-moa.com;

    ssl_certificate /etc/letsencrypt/live/www.film-moa.com/fullchain1.pem;
    ssl_certificate_key /etc/letsencrypt/live/www.film-moa.com/privkey1.pem;

    ... 중략 ...
  }

⇒ 그러나 여전히 같은 에러가 발생 BIO_new_file() failed

시도 2

시스템 nginx 프로세스 점유 의심 및 중지

  • docker compose down 후 다시 컨테이너를 띄우고 log를 확인하니, 80포트가 이미 사용 중으로 나옴
  • 포트 충돌 문제라고 판단해 프로세스를 강제 종료함

⇒ 그러나 여전히 같은 에러가 발생 BIO_new_file() failed

시도 3

심볼릭 링크 구조 및 실제 파일 존재하는지 접근하여 확인 & 권한 수정

  • ls -l /etc/letsencrypt/live/www.film-moa.com/ 명령어로 심볼릭 링크 확인함.
  • ls -l /etc/letsencrypt/archive/www.film-moa.com/ 명령어로 실제 타겟 파일 존재 여부 확인함.
  • 그러나 일반 사용자(ubuntu)로 접근 시 권한 에러(Permission denied) 발생하는 것을 확인함.
  • 컨테이너 내부에서 root가 아닌 별도의 사용자 계정으로 인증서를 읽으려 했기 때문에 문제가 발생했음을 최종적으로 확인함.
  • 해당 경로에 대해 권한을 수정함
sudo chmod 755 /etc/letsencrypt
sudo chmod 755 /etc/letsencrypt/live
sudo chmod 755 /etc/letsencrypt/archive
sudo chmod 755 /etc/letsencrypt/live/www.film-moa.com
sudo chmod 755 /etc/letsencrypt/archive/www.film-moa.com
  • 755
  • 소유자(owner)는 읽기(r), 쓰기(w), 실행(x) 권한을 모두 갖고, 다른 사용자(group, others)는 읽기(r), 실행(x) 권한만 가지는 설정임.

해결책

컨테이너가 호스트의 /etc/letsencrypt 경로를 마운트하여 내부에서 접근할 때 권한이 충분한지 반드시 확인해야 함.

  • 해결책으로 호스트에서 인증서가 있는 디렉터리(/etc/letsencrypt) 권한을 컨테이너에서 접근 가능한 형태로 변경하거나,
  • 호스트에서 실제 인증서 파일을 복사하여 별도의 디렉토리에 배치하고, 이 디렉토리를 Docker 볼륨으로 마운트하는 방법으로 권한 문제 해결이 가능함.
  • 본 프로젝트에서는 /etc/letsencrypt 경로의 접근 권한을 컨테이너가 읽을 수 있도록 수정하여 문제 해결함.

회고 및 정리

  • 문제 발생 초기에 Nginx 설정 파일이나 포트 충돌 문제 등 명확한 문제를 찾는 데 많은 시간을 소모하였음.
  • 여러 시도를 통해 문제를 좁혀갔으나, SSL 인증서 파일의 접근 권한 문제라는 근본적인 문제를 인지하는 데 시간이 오래 걸림.
  • 앞으로 SSL과 같이 권한 설정이 중요한 부분을 다룰 때는, 처음부터 권한 문제를 염두에 두고 접근 권한 및 파일 소유자, 그룹 등의 설정을 우선적으로 확인할 필요가 있음을 깨달았음.
  • 이를 통해 Docker 컨테이너와 호스트 간 볼륨 마운트 시 발생할 수 있는 권한 문제에 대한 이해도를 높였으며, 향후 유사 문제 발생 시 더 빠른 원인 진단이 가능할 것으로 기대함.

에러메세지 / 문제상황

컨테이너 내에서 명령어 실행 시 apt-get update 실행 시 다음과 같은 권한 에러 발생:

 


 

원인

 

컨테이너가 jenkins 사용자 권한으로 실행되고 있었으며, 해당 사용자는 apt 명령 실행에 필요한 시스템 디렉토리에 대한 쓰기 권한이 없음. jenkins 이미지는 기본적으로 루트가 아닌 사용자로 동작함.

 

 

시도 1

services:
  jenkins:
    image: jenkins/jenkins:lts
    ports:
      - "8080:8080"
    volumes:
      - jenkins_home:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
    restart: unless-stopped

→ Docker 소켓 공유는 있었으나, root 권한 설정이 없어 apt 실행 불가.

시도 2

컨테이너 내부(exec)에서 강제 수행을 위해 sudo apt-get update 실행 시도.

결과: sudo: command not found.

기본 이미지에 sudo가 포함되지 않음.

 

시도 3

services:
  jenkins:
    image: jenkins/jenkins:lts
    user: jenkins
    group_add:
      - "${DOCKER_GID}"
    ports:
      - "8080:8080"
    volumes:
      - jenkins_home:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
    restart: unless-stopped

→ 보안 목적상 jenkins 유저로 제한하고 group_add 설정을 통해 Docker 소켓 접근 권한을 주었으나, 여전히 apt, groupadd, usermod 실행 불가.

 

시도 4

services:
  jenkins:
    image: jenkins/jenkins:lts
    user: root
    group_add:
      - "${DOCKER_GID}"
    ports:
      - "8080:8080"
    volumes:
      - jenkins_home:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
    restart: unless-stopped

→ 컨테이너를 root 사용자로 실행하여 apt 관련 시스템 명령이 정상 작동함.

 

 


 

해결책

docker-compose.yml에 user: root 옵션 추가.

컨테이너를 루트 사용자 권한으로 실행함으로써 apt, groupadd, usermod 등 시스템 명령 실행 가능해짐.

Jenkins는 여전히 컨테이너 내에서 자체적으로 jenkins 사용자로 실행되므로 보안상 큰 문제 없음

 

 


 

회고 및 정리

Jenkins 컨테이너에서 시스템 명령어(apt, groupadd 등)를 실행하기 위해 user: root 설정을 적용하였다.

일반적으로 Docker 컨테이너는 사용자 공간이 호스트와 격리되어 있어, 컨테이너 내 root 권한은 호스트 시스템의 보안에 직접적인 위협이 되지 않는다.

 

따라서 컨테이너 내부에서 root로 실행해도 시스템 자원을 침해하지 않으며, 제한된 범위 내에서 필요한 작업만 수행할 수 있다.

또한 Jenkins 공식 Docker 이미지는 내부적으로 Jenkins 프로세스를 별도의 일반 사용자(jenkins)로 실행하는 구조이기 때문에

컨테이너가 root로 시작되더라도 Jenkins 서비스는 최소 권한 환경에서 운영된다.

 

이로 인해 시스템 도구 설치나 Docker CLI 접근과 같은 자동화 작업은 루트 권한으로 처리하면서도,

Jenkins 자체는 보안 측면에서 안전하게 동작할 수 있다.

 

결론적으로, 이 구성은 Docker 환경 내에서 실용성과 보안을 모두 고려한 현실적인 운영 방식이며,

user: root 설정은 Jenkins 컨테이너 운영에 있어 유효하고 안정적인 선택이었다.

에러메세지 / 문제상황

Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine suitable jdbc url

 

백엔드 개발자들은 많이 보는 에러일 것이다.

springboot 서버를 실행할 때, 설정파일 (application.yaml)을 제대로 못읽었을 때 발생하는 에러이다..

 

원인

Spring Boot 프로젝트에서 데이터베이스 연결 정보를 application.yml과 application-secret.yml 두 개 파일로 나눠 관리 중이었음. Jenkins를 이용해 application-secret.yml을 credentials에서 복사해 프로젝트 경로에 넣어줬으나, Spring Boot가 실제 실행 시 ${}로 선언된 환경변수를 JVM 또는 컨테이너 환경에서 찾지 못해 데이터베이스 연결 정보가 누락되어 발생한 문제.

application.yml (기존)

server:
  servlet:
    context-path: /api/v1
  port: ${SPRING_CONTAINER_PORT}

spring:
  application:
    name: fourcut
  config:
    import: "optional:classpath:application-secret.yml"
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: ${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
  jpa:
    database-platform: org.hibernate.dialect.MySQLDialect
    hibernate:
      ddl-auto: update  # none, update, create, create-drop
    properties:
      hibernate:
        show_sql: true
        format_sql: true
        use_sql_comments: true
        dialect: org.hibernate.dialect.MySQLDialect

  security:
    oauth2:
      client:
        registration:
          kakao:
            client-id: ${KAKAO_CLIENT_ID}
            client-secret: ${KAKAO_CLIENT_SECRET}
            redirect-uri: ""
            authorization-grant-type: authorization_code
            client-authentication-method: client_secret_post
            client-name: kakao
            scope:
              - account_email
        provider:
          kakao:
            authorization-uri: <https://kauth.kakao.com/oauth/authorize>
            token-uri: <https://kauth.kakao.com/oauth/token>
            user-info-uri: <https://kapi.kakao.com/v2/user/me>
            user-name-attribute: id

kakao:
  logout-redirect-uri: 

jwt:
  secret: ${JWT_SECRET}
  access:
    expiration: ${JWT_ACCESS_EXPIRATION}
  refresh:
    expiration: ${JWT_REFRESH_EXPIRATION}

cloud:
  aws:
    credentials:
      access-key: ${AWS_ACCESS_KEY}
      secret-key: ${AWS_SECRET_KEY}
    region:
      static: ap-northeast-2
    s3:
      bucket: ${AWS_S3_BUCKET}

cloudfront:
  domain: ${AWS_CLOUDFRONT_DOMAIN}
  keyPairId: ${CLOUDFRONT_KEY_PAIR_ID}
  privateKeyPath: ${CLOUDFRONT_PRIVATE_KEY_PATH}

application-secret.yml(기존)

SPRING_IMAGE_NAME: spring-project
SPRING_CONTAINER_NAME: backend
SPRING_CONTAINER_PORT: 8081
KAKAO_CLIENT_ID: kakao_client_id
KAKAO_CLIENT_SECRET: kakao_client_secret
DB_URL: jdbc:mysql://<db_url>:3306/<db_name>?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
DB_USERNAME: db_username
DB_PASSWORD: db_password
JWT_SECRET: jwt_secret
JWT_ACCESS_EXPIRATION: 3600000
JWT_REFRESH_EXPIRATION: 604800000
AWS_ACCESS_KEY: aws_access_key
AWS_SECRET_KEY: aws_secret_key
AWS_S3_BUCKET: aws_s3_bucket
AWS_CLOUDFRONT_DOMAIN: aws_cloudfront_domain
CLOUDFRONT_PRIVATE_KEY_PATH: private_key.pem
CLOUDFRONT_KEY_PAIR_ID: cloudfront_key_pair_id

시도 1

  • Jenkins credentials를 통해 application-secret.yml 파일을 서버에 복사한 후 바로 Spring Boot가 읽을 것이라 생각하고 환경변수 참조(${})로 작성함.
  • 파일은 정상적으로 복사되었으나, Spring Boot가 해당 값을 환경변수에서 찾으려 하면서 실패함.

 

시도 2

  • Jenkins 빌드 단계의 Execute Shell에서 export 명령어를 이용해 YAML 파일의 변수를 쉘 환경변수로 등록 시도.
  • export 명령어로는 Jenkins 쉘 내에서만 변수가 유지되고, 실제 Docker 컨테이너 환경에는 전달되지 않아 Spring Boot는 여전히 환경변수를 찾지 못함.

 

시도 3

  • docker-compose.yml 파일의 environment: 블록에 환경변수를 전달하여 컨테이너 내부에 변수를 전달하려 시도.
  • Jenkins 쉘에서 export된 환경변수가 docker-compose 명령어까지 전달되지 않아 실패.
  • 추가로 docker-compose.yml에 환경변수를 다 명시하게 되면, Jenkins Credentials로 파일(application-secret.yml)을 관리할 이유가 없어져 "credentials로 파일 복사"라는 설계 의도 자체가 무너진다는 문제 인식 → 이 방법을 포기함

 

해결책

  • 환경변수(${}) 참조 방식을 사용하지 않고, application-secret.yml 파일을 "완성형" 형태로 변경하여 Spring Boot가 직접 읽고 바로 설정값을 사용할 수 있도록 변경.
  • 즉, application-secret.yml에 데이터베이스 URL과 계정 정보 등 모든 민감한 정보를 하드코딩하여 직접 기입하고, 이를 spring.config.import로 application.yml에서 불러오는 구조로 변경.

application.yml

server:
  servlet:
    context-path: /api/v1
  port: ${SPRING_CONTAINER_PORT}

spring:
  application:
    name: fourcut
  config:
    import: "optional:classpath:application-secret.yml"

application-secret.yml

BUCKET_NAME: bucket_name

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://{mysql_container_name}:3306/{db_name}?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
    username: mysql_username
    password: mysql_password

  jpa:
    database-platform: org.hibernate.dialect.MySQLDialect
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        show_sql: true
        format_sql: true
        use_sql_comments: true
        dialect: org.hibernate.dialect.MySQLDialect

  security:
    oauth2:
      client:
        registration:
          kakao:
            client-id: kakao_client_id
            client-secret: kakao_client_secret
            redirect-uri: kakao_redirect_uri
            authorization-grant-type: grant_type
            client-authentication-method: kakao_client_authentication_method
            client-name: kakao_client_name
            scope:
              - account_email
        provider:
          kakao:
            authorization-uri: kakao_authorization_uri
            token-uri: kakao_token_uri
            user-info-uri: kakao_user_info_uri
            user-name-attribute: id

kakao:
    logout-redirect-uri: kakao_logout_redirect_uri

jwt:
  secret: jwt_secret
  access:
    expiration: jwt_access_expiration
  refresh:
    expiration: jwt_refresh_expiration

cloud:
  aws:
    credentials:
      access-key: aws_access_key
      secret-key: aws_secret_key
    region:
      static: cloudfront_region_static
    s3:
      bucket: s3_bucket_name

cloudfront:
  domain: aws_cloudfront_domain
  keyPairId: aws_cloudfront_key_pair_id
  privateKeyPath: aws_cloudfront_private_key_path

cookie:
  domain: .film-moa.com
  path: /
  same-site: Strict
  http-only: true
  secure: ${COOKIE_SECURE:true}

 

회고 및 정리

  • Spring Boot는 ${} 표현을 만나면 JVM 환경변수 또는 Docker 컨테이너 환경변수에서 값을 찾기 때문에, ${}를 사용하려면 docker-compose.yml의 environment 설정이나 JVM 시스템 프로퍼티로 반드시 값을 주입해야 한다는 것을 정확히 이해함.
  • application-secret.yml 파일을 Jenkins Credentials로 복사만 했을 경우에는, ${} 변수 치환이 자동으로 일어나지 않는다는 점을 놓쳤고, 이는 JVM이나 Docker 컨테이너 실행 시점에 환경변수로 전달되지 않으면 해결할 수 없다는 것을 알게 됨.
  • docker-compose.yml 파일에 모든 환경변수를 주입하는 방식(environment:)을 쓰려면, 결국 Jenkins Credentials에 파일(application-secret.yml)로 관리하는 의미가 없어지고, 오히려 관리 포인트가 분산되어 복잡해진다는 것을 인식함.
  • 이 때문에 application.yml에서는 최소 설정(server.port, context-path 등)만 남기고, application-secret.yml은 완성된 값(하드코딩 형태)으로 작성하여 Spring Boot가 파일만 읽으면 동작하도록 구조를 바꿈.
  • 결국 민감 정보 관리 방식을 파일 기반으로 통일하고, Jenkins Credentials를 통해 민감 파일을 복사하는 방식으로 간소화하면서, docker-compose.yml은 포트(SPRING_CONTAINER_PORT) 등 필수 최소값만 넘기는 구조로 정리함.
  • 이번 경험을 통해, 배포 파이프라인을 설계할 때 "환경변수 기반 관리"와 "파일 기반 관리"는 서로 전략이 다르며, 둘을 섞으면 복잡성과 오류 가능성이 급격히 올라간다는 것을 체감함. 따라서 초기에 민감정보 관리 방식을 명확히 정의하는 것이 중요함을 배움.
  • Jenkins credentials를 활용할 때, Spring Boot가 참조하는 방식(yml 직접 읽기 vs 환경변수 참조)을 명확히 맞춰야 하며, credentials 파일을 완성형으로 구성하면 설정과 관리가 매우 단순해지고, 트러블슈팅 시간도 대폭 줄일 수 있다는 것을 깨달음.