GCP에서 OCI로 웹서버 이전하기

SongWoo Yu·2025년 10월 7일
post-thumbnail

OCI로 이전하는 이유

기본적으로 GCP나 AWS가 제일 편하고 좋다. 다만 OCI는 평생 무료 서버를 제공해준다. 그것도 4Core VCPU, VLAM 24GB, BootVoulme 200GB내로 총 4개의 VM Instance를 제공한다. 가장 좋은 무료 자원을 제공하는 것이다. 그렇기에 이번에 오사카 리전에 계정을 하나 만들었다. 다만 문제는 가장 좋은 구성으로 무료 서버 하나를 구성하려고 했더니, 총 할당량이 부족하여 생성할 수 없단다... 역시 무료는 총량이 정해져있고, 그 중에 배분해주나 보다.. 운 좋게 만든 사람도 갖가지 이유로 멈추고, 계정이 정지당하기도 한다고 한다. 계속해서 만들다보면 언젠가 할당량이 잠시 비었을 때 들어갈 수 있다는 글도 본 것 같다. 가장 좋은 방식은 유료 버전으로 바꾸고, 거기서 평생 무료 인스턴스를 만드는 방법이다. 그러나 과금 발생의 우려가 있다고는 한다. 나의 경우 현재 400달러/1month 크레딧을 받았기 때문에 좋은 서버로 만들어서 이것저것 해보고, 그 동안 평생 무료 서버를 구성해보다가 안 되면 1달 뒤에 유료로 변경하고 무료 서버를 생성해보려고 한다.

생성

구성이전 작업

마이그레이션 방법이 따로 있다고는 하나, 나의 경우 도커와 페이지 생성 연습을 위해서 그냥 해보려고 한다.

도커 설치

### 필수 패키지 업데이트 및 설치
ubuntu@instance-gcp-backup:~/gcpbackup$ sudo apt-get update
ubuntu@instance-gcp-backup:~/gcpbackup$ sudo apt-get install -y ca-certificates curl gnupg lsb-release
### 2. Docker 공식 GPG 키 등록 및 도커 저장소 추가
ubuntu@instance-gcp-backup:~/gcpbackup$ sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
  | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

### 패키지 인덱스 업데이트
ubuntu@instance-gcp-backup:~/gcpbackup$ sudo apt-get update

### 도커 설치
ubuntu@instance-gcp-backup:~/gcpbackup$ sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
### 서비스 시작 및 자동 실행 등록
ubuntu@instance-gcp-backup:~/gcpbackup$ sudo systemctl enable docker
sudo systemctl start docker

### 사용자를 도커에 등록하여 sudo 사용 없이 도커 명령어 사용
ubuntu@instance-gcp-backup:~/gcpbackup$ sudo usermod -aG docker $USER
ubuntu@instance-gcp-backup:~/gcpbackup$ exit
### 다시 접속

### 도커 명령어 사용하여 확인하기
ubuntu@instance-gcp-backup:~$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

Nginx Proxy Manger 생성

### 사용자 디렉토리 내부에 새로운 디렉토리 생성
ubuntu@instance-gcp-backup:~$ mkdir gcpbackup
ubuntu@instance-gcp-backup:~/gcpbackup$ mkdir -p npm/{data,letsencrypt,mysql}
ubuntu@instance-gcp-backup:~/gcpbackup/npm$ vim .env
# MariaDB 설정
MYSQL_ROOT_PASSWORD=StrongRootPass123!
MYSQL_DATABASE=npm
MYSQL_USER=npm
MYSQL_PASSWORD=StrongUserPass123!

ubuntu@instance-gcp-backup:~/gcpbackup/npm$ vim docker-compose.yml
version: '3.8'

services:
  db:
    image: mariadb:10.11
    container_name: npm-db
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - ./mysql:/var/lib/mysql
    command: >
      mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

  app:
    image: jc21/nginx-proxy-manager:latest
    container_name: nginx-proxy-manager
    restart: unless-stopped
    depends_on:
      - db
    ports:
      - '80:80'     # HTTP
      - '443:443'   # HTTPS
      - '81:81'     # 관리자 UI
    environment:
      DB_MYSQL_HOST: db
      DB_MYSQL_PORT: 3306
      DB_MYSQL_USER: ${MYSQL_USER}
      DB_MYSQL_PASSWORD: ${MYSQL_PASSWORD}
      DB_MYSQL_NAME: ${MYSQL_DATABASE}
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
    env_file:
      - .env


ubuntu@instance-gcp-backup:~/gcpbackup/npm$ docker compose up -d
ubuntu@instance-gcp-backup:~/gcpbackup/npm$ docker ps
CONTAINER ID   IMAGE                             COMMAND                  CREATED              STATUS              PORTS                                                                                      NAMES
8c2a80bf5f90   jc21/nginx-proxy-manager:latest   "/init"                  About a minute ago   Up About a minute   0.0.0.0:80-81->80-81/tcp, [::]:80-81->80-81/tcp, 0.0.0.0:443->443/tcp, [::]:443->443/tcp   nginx-proxy-manager
11fe5800a428   mariadb:10.11                     "docker-entrypoint.s…"   About a minute ago   Up About a minute   3306/tcp 

두 개의 도커 컨테이너가 구동 중인 것을 볼 수 있다. DB와 NPM이다.이제 서버의 포트를 열어주고, ip 주소의 81번 포트로 접속해주면 내가 만든 nginx proxy manager을 볼 수 있다. 초기 아이디와 비밀번호를 확인한 후 이를 넣고 변경해줘야 한다.

각 SPA가 들어갈 docker-compose.yml, defult.conf 파일을 만들어준다.

대충 다음과 같이 만든다.

songwoo@instance-dypile:/opt/pqm.kr$ cat docker-compose.yml 
version: '3.8'

services:
  web:
    image: nginx:latest
    restart: unless-stopped
    ports:
      - "8083:80"
    volumes:
      - ./pqm.conf:/etc/nginx/conf.d/default.conf
      - ./html:/usr/share/nginx/html
songwoo@instance-dypile:/opt/pqm.kr$ cat pqm.conf 
server {
    listen 80;
    server_name pqm.kr;

    location / {
        root /usr/share/nginx/html;
        index index.html;

        try_files $uri $uri/ /index.html;

        add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
        expires off;
    }
}

SPA는 다음과 같이 conf에 try_files $uri $uri/ /index.html이 들어가줘야 한다. 특히 history mode일 때 그렇다. history mode는 MPA일 때처럼 /구조로 url이 설정되는 모드를 말한다. 해쉬모드일 때는 또 다르다. #이게 url에 붙게 된다.

docker-compose.yml이 존재하는 디렉토리에서 docker compose up -d 명령어를 작성하여 서비스를 올리고, 해당 포트로 접속하여 제대로 동작하는지 확인한다.

GCP에서 한 방법

songwoo@instance-dypile:/opt/npm$ ls
data  docker-compose.yml  letsencrypt
songwoo@instance-dypile:/opt/npm$ pwd
/opt/npm
songwoo@instance-dypile:/opt/npm$ cat docker-compose.yml 
version: '3.8'
services:
  app:
    image: 'jc21/nginx-proxy-manager:latest'
    restart: unless-stopped
    ports:
      # These ports are in format <host-port>:<container-port>
      - '80:80'    # Public HTTP Port
      - '443:443'  # Public HTTPS Port
      - '81:81'    # Admin Web UI
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
songwoo@instance-dypile:/opt/npm$ cd ..
songwoo@instance-dypile:/opt$ ls
containerd  dy.pile.kr  google-cloud-ops-agent  npm  posco.pile.kr  pqm.kr
songwoo@instance-dypile:/opt$ cd dy.pile.kr/
songwoo@instance-dypile:/opt/dy.pile.kr$ ls
docker-compose.yml  dy.conf  html
songwoo@instance-dypile:/opt/dy.pile.kr$ cat docker-compose.yml dy.conf 
version: '3.8'

services:
  web:
    image: nginx:latest
    ports:
      - "8082:80"
    volumes:
      - ./dy.conf:/etc/nginx/conf.d/default.conf
      - ./html:/usr/share/nginx/html
server {
    listen 80;
    server_name dy.pile.kr;

    location / {
        root /usr/share/nginx/html;
        index index.html;

        try_files $uri $uri/ /index.html;

        add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
        expires off;
    }
}
songwoo@instance-dypile:/opt/dy.pile.kr$ cd ..
songwoo@instance-dypile:/opt$ ls
containerd  dy.pile.kr  google-cloud-ops-agent  npm  posco.pile.kr  pqm.kr
songwoo@instance-dypile:/opt$ cd posco.pile.kr/
songwoo@instance-dypile:/opt/posco.pile.kr$ ls
docker-compose.yml  html  posco.conf
songwoo@instance-dypile:/opt/posco.pile.kr$ cat docker-compose.yml posco.conf 
version: '3.8'

services:
  web:
    image: nginx:latest
    ports:
      - "8081:80"
    volumes:
      - ./posco.conf:/etc/nginx/nginx.conf
      - ./html:/usr/share/nginx/html
events {}

http {
        include /etc/nginx/mime.types;

        server {
                listen 80;

                location / {
                        root /usr/share/nginx/html;
                        index index.html;

                        try_files $uri $uri/ /index.html;

                        add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
                        expires off;
                }
        }
}
songwoo@instance-dypile:/opt/posco.pile.kr$ cd ../pqm.kr/
songwoo@instance-dypile:/opt/pqm.kr$ ls
docker-compose.yml  html  pqm.conf
songwoo@instance-dypile:/opt/pqm.kr$ cat docker-compose.yml pqm.conf 
version: '3.8'

services:
  web:
    image: nginx:latest
    restart: unless-stopped
    ports:
      - "8083:80"
    volumes:
      - ./pqm.conf:/etc/nginx/conf.d/default.conf
      - ./html:/usr/share/nginx/html
server {
    listen 80;
    server_name pqm.kr;

    location / {
        root /usr/share/nginx/html;
        index index.html;

        try_files $uri $uri/ /index.html;

        add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
        expires off;
    }
}

0개의 댓글