이번 회사에서는 멀티뷰어처럼 제한적인 환경에서 5명정도 트래픽을 감당할 수 있는 저사용 리눅스 서버를 요청했다.

(클라우드 하고 싶다..ㅋㅋㅋ) 개념을 잡는겸 대용량 트래픽처리는 없으나 도커 컨테이너를 실행 시켜서 웹 백엔드 , 웹 프론트엔드

개발 서버처럼 구현 하고자 했다. 구동 방식에 대해서 정리 하고자 블로그에 글을 남기면서 정리하고자 내용을 남기겠다.

 

sudo apt update 

sudo apt install firewalld

 
sudo apt-get update 
sudo apt-get install ufw

 

1) apt 업데이트

sudo apt-get update

 

2) mysql 설치

sudo apt-get install mysql-server

3) mysql 버전 확인

mysql --version

# 결과 (버전 확인 되면 설치 성공)
mysql  Ver 8.0.31-0ubuntu0.20.04.1 for Linux on x86_64 ((Ubuntu))

 

3. 접속

1) root 유저로 접속

sudo service mysql start

sudo mysql -u root -p

[Enter password : 가 나오면 password 입력하면 된다.

 

상태 확인 명령어

sudo service mysql status

 

mysql 필요 폴더 설치

sudo mkdir -p /var/run/mysqld sudo chown mysql:mysql /var/run/mysqld

 

서버 재시작

sudo systemctl start mysql

 

 

mysql 워크벤치 설치 방법

sudo apt install snapd

 

sudo snap install mysql-workbench-community

 

방화벽 설정 확인

sudo ufw status

방화벽 설정 명령어

sudo ufw allow 3306

 

mysql 설정 파일 수정

sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

bind-address 설정이 127.0.0.1 또는 0.0.0.0 으로 변경

sudo systemctl restart mysql

 

mysql 접속 후 권한 체크

sudo mysql -u root -p

SELECT host, user FROM mysql.user;

없으면 root 권한 전체 주기

GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION;

적용 저장

FLUSH PRIVILEGES;

 

비번 설정하기

sudo mysql

 

계정 확인

SELECT User, Host, plugin FROM mysql.user WHERE User = 'root';

 

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'uimd5191!';

FLUSH PRIVILEGES; 

EXIT;

 

다시 접속 확인

mysql -u root -p

 

위에 과정 다 진행 후 서비스로 시작하기

sudo systemctl stop mysql

sudo systemctl start mysql

sudo systemctl enable mysql

레디스 서버 설치

sudo apt install redis-server

 

nginx 설치

sudo apt install nginx

 

nginx 권한 설정

sudo chown -R www-data:www-data /etc/nginx

 

레디스 서버 외부 접속 허용

sudo nano /etc/redis/redis.conf

bind 0.0.0.0 으로 변경 후 저장

protected-mode no 로 변경

 

설치를 다 하고 나서

https://www.docker.com/products/docker-desktop/ 

 

 

Docker Desktop: The #1 Containerization Tool for Developers | Docker

Docker Desktop is collaborative containerization software for developers. Get started and download Docker Desktop today on Mac, Windows, or Linux.

www.docker.com

도커데스크탑 윈도우 다운로드

 
 

생성된 계정에 프론트, 백엔드 프로젝트

프론트
이미지 빌드 – (프론트 디렉토리에서 진행 해야함)
docker build -t vue3-app .
도커 이미지 태그 추가 – (현재 버전명으로 통일해서 올려야 함)
docker tag vue3-app coin255/vue3-app:0.1v
docker login - > 이미지 업로드 시 로그인 필요
docker push coin255/vue3-app:0.1v

백엔드
이미지 빌드 – (백엔드 경로로 이동 후 빌드 명령어 입력)
docker build -t nestjs-backend .
docker tag nestjs-backend coin255/nestjs-backend:0.1v
docker login - > 이미지 업로드 시 로그인 필요
docker push coin255/nestjs-backend:0.1v

 

각 프로젝트에서 위 명령어 실행 해서 이미지들 생성 후


리눅스 서버에 Docker와 Docker Compose를 설치

sudo apt update
sudo apt install -y docker.io
sudo curl -L "https://github.com/docker/compose/releases/download/2.22.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

 

초기 설치 진행 후 

리눅스 도커이미지 받기
docker login
id -> coin255
pw => 132645df!@

Docker Hub에서 이미지 가져오기

프론트 이미지 가져오기
sudo docker pull coin255/vue3-app:0.1v

프론트 백엔드 이미지 가져오기
sudo docker pull coin255/nestjs-backend:0.1

compose 실행 리눅스 서버에서 실행
sudo docker-compose up -d

nginx 로그 폴더 생성해줘야함
sudo mkdir -p /var/log/nginx
sudo chmod 755 /var/log/nginx
nginx 재시작
sudo docker-compose restart nginx

로그 확인 방법 -> 컨테이명 치면 나옴
sudo docker logs mysql
sudo docker logs redis

 

위 작업을 진행 후

docker-compose.yml 을 수정

version: '3.8'
services:
  backend:
    container_name: backend_service
    image: coin255/nestjs-backend:0.3v
    ports:
      - "3002:3002"
    restart: always
    environment:
      - DB_HOST=host.docker.internal
      - DB_PORT=3306
      - DB_USER=root
      - DB_PASSWORD=uimd5191!
      - DB_NAME=pb_db_web
    extra_hosts:
      - "host.docker.internal:192.168.0.xx"  # 리눅스에서 실제 IP 주소 사용
    volumes:
      - /home/coin/Desktop/backend
    networks:
      - mynetwork

  frontend:
    container_name: frontend_service
    image: coin255/vue3-app:0.3v
    ports:
      - "8080:8080"
    restart: always
    volumes:
      - /home/coin/Desktop/f
    networks:
      - mynetwork

  nginx:
    image: nginx:alpine
    container_name: nginx_service
    volumes:
      - ./logs:/etc/nginx/logs
      - /etc/nginx/nginx.conf:/etc/nginx/nginx.conf
    ports:
      - "80:80"
    restart: always
    networks:
      - mynetwork


networks:
  mynetwork:
    driver: bridge

 

sudo docker-compose up -d

 

정상 작동 TCP 등 API IP 우회를 위해서

백엔드 프론트 코드를 변경해준다.

 

웹소켓 + tcp 설정 TS 파일에 아래 구문을 추가해서 Ai 측 TCP 수락을 하는 부분에 코드를 추가하기 힘들다고 웹쪽으로 업무가 넘어와서  끊고 다시 시작 할 수 있도록 변경하는 구문으로 AI 측 오류를 해결하는 방안으로 코드 수정

restartTcpConnection() {
    if (this.connectedClient) {
      this.logger.warn('⚠️ 기존 TCP 연결을 종료하고 다시 연결을 시도');
      this.connectedClient.end(); // 안전하게 종료 요청
      this.connectedClient.destroy(); // 강제 종료
      this.connectedClient = null;
    }

    setTimeout(() => {
      this.logger.warn('🔄 TCP 서버 재연결을 시도합니다.');
      this.setupTcpServer('192.168.0.131', 11235);
    }, 500);
  }

 

async sendDataToEmbeddedServer(data: any): Promise<void> {
    // 데이터 중복 체크
    if (
      this.tcpQueue.some(
        (item) => JSON.stringify(item) === JSON.stringify(data),
      )
    ) {
      this.logger.warn('⚠️ 중복 데이터로 인해 전송이 무시되었습니다.');
      return;
    }

    // 데이터 큐에 추가
    this.tcpQueue.push(data);

    await this.processQueue(); // 큐 처리 시작
  }

  private async processQueue(): Promise<void> {
    if (this.isProcessing || !this.tcpQueue.length) {
      return;
    }

    this.isProcessing = true; // 처리 중 상태로 설정
    const data = this.tcpQueue.shift(); // 큐에서 데이터 가져오기

    try {
      if (this.connectedClient && !this.connectedClient.destroyed) {
        const serializedData = JSON.stringify(data.payload);

        if (serializedData && this.isNotDownloadOrUploading) {
          this.connectedClient.write(serializedData);
          this.logger.log(`웹백엔드 -> 코어로 전송: ${serializedData}`);
          this.notRes = true;

          // 데이터 전송 후 일정 시간 대기 (예: 100ms)
          await new Promise((resolve) => setTimeout(resolve, 100));
        }
      } else {
        this.logger.warn('⚠️ 활성화된 코어 TCP 없음. 데이터 전송 안됨.');
        this.notRes = false;
      }
    } catch (error) {
      this.logger.error(`🚨 TCP 데이터 전송 오류: ${error.message}`);
    } finally {
      this.isProcessing = false; // 처리 상태 해제
      await this.processQueue(); // 다음 큐 처리
    }
  }

  stopTcpServer(): void {
    if (this.connectedClient) {
      this.connectedClient.destroy();
    }
  }

  setupTcpServer(newAddress: string, newPort: number): void {
    const connectClient = () => {
      if (!this.connectedClient || this.connectedClient.destroyed) {
        const newClient = new net.Socket();

        newClient.setTimeout(10000); // 10초 동안 클라이언트 소켓이 데이터를 송수신하지 않으면 timeout 이벤트가 발생하도록 설정

        newClient.connect(newPort, newAddress, () => {
          this.logger.warn('코어 TCP 웹 백엔드 연결 성공');
          this.connectedClient = newClient;
          this.wss.emit('isTcpConnected', true);
          this.reconnectAttempts = 0; // 재연결 시도 횟수 초기화
          this.notRes = false;
        });

        newClient.on('timeout', () => {
          this.logger.error('🚨 코어 TCP 웹 백엔드 연결 타임아웃');
          this.handleReconnectFailure(newClient);
        });

        newClient.on('data', (chunk) => {
          this.logger.warn(`코어 TCP 서버로부터 데이터 수신 성공`); // 추가된 로깅
          if (this.wss) {
            this.sendDataToWebSocketClients(chunk);
            this.notRes = false;
          } else {
            this.logger.error('🚨 WebSocketService가 초기화되지 않았습니다.');
          }
        });

        newClient.on('end', () => {
          this.logger.warn('코어 TCP 클라이언트 연결 종료');
          this.sendDataToWebSocketClients({ err: true });
          this.handleReconnectFailure(newClient);
        });

        newClient.on('error', (err: any) => {
          this.logger.error(
            `🚨[${err.code} - 코어 서버 연결 거부] 코어 TCP 연결 오류 - ${err}`,
          );
          this.sendDataToWebSocketClients({ err: true });
          this.handleReconnectFailure(newClient);
        });
      } else {
        this.logger.warn(
          '⚠️ 이미 클라이언트 연결이 활성화되어 있습니다. 연결 재활성화 시 문제 없음 정상 코드',
        );
      }
    };

    connectClient();
  }

  private handleReconnectFailure(client: net.Socket) {
    if (!this.mainPc) {
      return;
    }
    this.reconnectAttempts++;
    client.destroy(); // 기존 소켓 종료
    this.connectedClient = null;

    this.logger.warn(
      `⚠️ TCP 연결 실패, 재연결 시도 중 (${this.reconnectAttempts}/${this.maxReconnectAttempts})... 재 연결 텀 1초`,
    );

    if (this.reconnectAttempts < this.maxReconnectAttempts) {
      setTimeout(
        () => this.setupTcpServer('192.168.0.131', 11235),
        this.reconnectDelay,
      );
      // 연결 실패 후 즉시 재시도를 방지 - 끊기고 나서 바로 재연결 시도하면 여러가지 문제발생 할 수 있어서 바로 재시작 안함
      // 재연결 지연 시간을 두어, 자원 낭비를 줄이고 시스템을 안정화하려는 목적
    } else {
      this.logger.error('🚨 최대 재연결 시도 횟수 초과.');
    }
  }

 

허트비트 도입으로 불안정한 TCP 끊길경우 재연결 시도 코드 추가를 하고

프론트 쪽에서는 이부분도 사실 윈도우 변수를 사용하고 싶지 않았지만..

AI 측에서 계속 응용프로그램처럼.. 쉽게 설치가 되었으면 좋겠다는.. 요구사항이 있어서 어쩔 수 없이.. exe 로 강제로 설치가 가능하게 IP도 수시로 바꿀 수 있게 윈도우 변수를 사용하여 전역으로 추가하였다.

config 에 윈도우 변수를 추가.. 한 후 공통으로 사용되는 http 정의 ts 파일을 수정해준다.

export function useHttpClient() {
    let apiBaseUrl: any = window.APP_API_BASE_URL || 'http://192.168.0.XX:3002';
    // type 용도 -> ? 쿼리 스트링으로 보낼지 여부
    const httpGet = async <T>(url: Endpoint, parameters?: string, type?: boolean, linuxServeSet = false): Promise<ApiResponse<T>> => {
        return httpGetAct(url.endpoint, parameters, type, linuxServeSet);
    };

    const httpGetAct = async <T>(url: string, parameters?: string, type?: boolean, linuxServeSet = false): Promise<ApiResponse<T>> => {
        const options: AxiosRequestConfig = {
            headers: {
                'Content-Type': 'application/json; charset=UTF-8',
                'Cache-Control': 'public, max-age=3600' // 응답을 1시간 동안 캐싱하도록 지정
            },
        };

        axios.defaults.withCredentials = true;
        const slush = parameters && parameters !== '' ? (type ? '?' : '/') : '';
        parameters = parameters || '';
        if (linuxServeSet) {
            apiBaseUrl = window.LINUXSERVERIP;
        } else {
            apiBaseUrl = window.APP_API_BASE_URL;
        }
        try {
            const response: HttpResponse<T> = await axios.get(`${apiBaseUrl}/${url}${slush}${parameters}`, options);
            return Promise.resolve(response.data || {code: 500, data: undefined, success: false});
        } catch (e) {
            return Promise.reject(e);
        }
    };

 

export const createH17 = async (request): Promise<ApiResponse<void>> => {
    return httpClient.httpPost(apiConstants.Hl7Create.post, request, '', false, window.LINUX_SERVER_SET );
};

 

config.js

window.APP_API_BASE_URL='http://192.168.0.xx:80/api'; // MultiViewer - 'http://192.168.0.xx:80/api',   Main PC Only - 'http://127.0.0.1:3002'
window.MAIN_API_IP = 'http://192.168.0.xx:80/api'; // MultiViewer - 'http://192.168.0.Xx:80/api',   Main PC Only - 'http://127.0.0.1:3002'
window.MAIN_API = '192.168.0.43'; // MultiViewer - 'http://192.168.0.xx:80/api',   Main PC Only - 'http://127.0.0.1:3002'
window.MAIN_WEBSOCKET_IP = 'http://192.168.0.xx:3002';  // MultiViewer - 'http://192.168.0.xx:3002', Main PC Only - 'http://127.0.0.1:3002'
window.PROJECT_TYPE='pb';  // pb or bm
window.PROJECT_VERSION='02.02.009';
window.WEB_BACKEND_VERSION='0.0.94v';
window.WEB_FRONTEND_VERSION='0.3.03v';
window.MACHINE_VERSION='100a';  // 12a or 100a
window.FORCE_VIEWER = 'main'; // main or viewer or exhibition
window.PORT = '8080';
window.LINUXSERVERIP = 'http://192.168.0.xx:3020';
window.LINUX_SERVER_SET = true;

 

위에처럼 로직을 전체 변경 추가 해준 후 내부망 IP 로 접근하면 아래와 같이 접속이 가능하다.

 

 

'DevOps > docker' 카테고리의 다른 글

프론트 코드 도코 이미지로 도커 허브에 올리기  (0) 2023.11.21

회사 특성상 장비에서 TCP 통신을 통하여 AI 데이터베이스 코어 쪽과 통신을 해서 웹 백 그리고 부분을 무겁지 않은 NestJs 채택하여 구축하였다. 첫 도전이었기에 프런트 개발자로서.. 익숙함과 러닝커브가 많지 않다는 과정하에 nests를 활용하여 프런트와도 양방향 통신이 가능한 웹 소켓 연결이 수월한 NestJs 와 mysql을 사용하였다. 

현 회사에 특징 중 많은 이미지 파일 이동 + 1대의 Main PC에 내부망에 있는 PC 5대의 동시 접속까지는 고려해야 하는 상황이었다..

Nginx 에 프락시를 사용하여 로드밸런스를 해주고 특정 IP를 막는 용도로 사용하고 있는 중 전체 리스트 항목을 가지고 올 때 너무 느리다는 단점이 부각 됐다.(멀티 부어 PC에서 이미지를 옮기는 와중 너무 느리다는 단점이 부각) 이점을 고치기 위하여 첫 웹백엔드  개발 쪽에 심중 있게 살펴보던 와중 Redis 서버가 눈에 들어왔다.


레디스에 장점 중에는 in-memory cache이라는 큰 장점이 있었다. 기존 nests 서버 옆은 SQL 에 하드웨어로 읽고 쓰기를 진행 했다면 Redis는 랜덤 액세스 메모리(RAM)를 사용하여 엄청난 속도를 보여줬다. 레디스 서버에서 미리 가지고 있다가 프론트가 요청을 하면 바로 보여주는 방식이라서 정말 엄청 빨라진 것을 볼 수 있었다.



윈도우 기준 설치 방법은 정말 간단하다.

사이트 접속 -&TG; MIS 설치

Releases · microsoftarchive/redid (github.com)

 

윈도우 기준 설치 방법은 정말 간단하다.

사이트 접속 -> msi 설치

Releases · microsoftarchive/redis (github.com)

설치 후 자동으로 서비스에 등록이 되어 컴퓨터를 재시작해도 자동으로 서버가 실행된다.

Redis의 기본포트는 6379 이다.

위에처럼 떠 있으면 제대로 실행 된거다.

 

너무 느렸던 list 페이지에 도입을 했더니 스냅샷에 엄청난 단축을 보여줬다.

 

기존은 2230ms 유휴상태를 제거해도 그 정도 걸렸다.

엄청난 단축이였다..

 

http 통신으로 다시 재 호출 하지 않고 호출하면서 배열을 조작하는 부분들이 다시 로직을 안 쓰다보니깐 많은 단축을 보여줬다! ㅎㅎ

 

백엔드 코드에서 작업은

인터셉터로 먼저 가로채서 작업을 진행 하기로 했다. 우선 인터셉터로 ts 파일을 하나 작성 해준다.

 

백엔드 코드에서 엔드포인트 쪽에다가 인터셉터를 주입해준다~!

업데이트 등 될경우에는 

아래와 같이 레디스 명령어를 사용해서 지정해둔 키값을 삭제 해주고 업데이트 쳐준다.

업데이트 후 데이터가 남아있는 오류는 면할 수 있다.

 

다음에는 추가로 레디스를 활용해서 이미지 캐시 처리도 진행 해보겠다.

 

사이트 접속 -> Stable version 설치

nginx: download

NGINX 압축 해제

C:\Program Files\nginx-1.26.1 -> 경로에 압축 해제

nginx를 설치를 완료 후에는 nssm을 설치 해야합니다.

서비스 등록을 해야 컴퓨터 재부팅 후 자동 서버 재실행을 위한 설치 입니다.

사이트 접속 -> Latest release 설치

NSSM - the Non-Sucking Service Manager

C:\Program Files\nssm-2.24 위치에 설치

CD C:\Program Files\nssm-2.24\win64 이동 후

 

nssm install nginx 입력

전체 설치가 끝 난 후에는 nginx conf 파일 수정을 합니다.

 

직접 수정 시

윈도우 검색 창 -> 워드패드 관리자 권한 실행 후
C:\Program Files\nginx-1.26.1\conf
열기

 

user  nobody;
worker_processes  auto;

error_log  logs/error.log;
pid        logs/nginx.pid;

events {
    worker_connections  4096;  # 동시 연결 수 증가
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    access_log  logs/access.log;

    gzip  on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
    gzip_comp_level 5;

    # 첫 번째 백엔드 서버 정의
    upstream backend_server_1 {
        server 192.168.0.131:3002;
    }

    # 두 번째 백엔드 서버 정의
    upstream backend_server_2 {
        server 192.168.0.131:3003;
    }

    # 포트 80에서 수신되는 요청을 처리하는 서버
    server {
        listen       80;
        server_name  localhost;

        root C:/workspace/testdist/frontend_80;
 
        location /api/ {
            proxy_pass         http://backend_server_1;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Proto $scheme;
            
            proxy_http_version 1.1;
            # WebSocket 지원 설정
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_read_timeout 300s;
            proxy_connect_timeout 300s;
        }

        location /socket.io/ {
            proxy_pass         http://backend_server_1;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Proto $scheme;
            
            proxy_http_version 1.1;
            # WebSocket 지원 설정
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_read_timeout 300s;
            proxy_connect_timeout 300s;
        }

        location / {
            try_files $uri $uri/ /index.html;
            proxy_pass         http://backend_server_1;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Proto $scheme;
            
            proxy_http_version 1.1;
            # WebSocket 지원 설정
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_read_timeout 300s;
            proxy_connect_timeout 300s;
        }
    }

    # 포트 81에서 수신되는 요청을 처리하는 서버
    server {
        listen       81;
        server_name  localhost;

        location /api/ {
            proxy_pass         http://backend_server_2;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Proto $scheme;
            
            proxy_http_version 1.1;
            # WebSocket 지원 설정
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_read_timeout 300s;
            proxy_connect_timeout 300s;
        
}

        location /socket.io/ {
            proxy_pass         http://backend_server_2;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Proto $scheme;
            
            proxy_http_version 1.1;
            # WebSocket 지원 설정
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_read_timeout 300s;
            proxy_connect_timeout 300s;
        }

        location / {
            proxy_pass         http://backend_server_2;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Proto $scheme;
            
            proxy_http_version 1.1;
            # WebSocket 지원 설정
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_read_timeout 300s;
            proxy_connect_timeout 300s;
        }
    }
}

 

위와 같이 작성 웹소켓 사용으로 등 여러가지 옵션 추가 후 저장

Nginx 서버 실행 서비스로 실행 하기

C:\Program Files\nssm-2.24\win64 nssm이 위치하는 폴더를 위치로 관리자모드 cmd 창에 nssm start nginx 명령어를 칩니다.

위 와 같이 작업 완료를 확인 후 

port 방화벽 인바운드 규칙에 추가

# Windows 방화벽 명령어를 칩니다. 인 바운드 규칙 들어간 후 제대로 추가가 됐는지 확인 안 되어있으면 새 규칙으로 추가.

netsh advfirewall firewall add rule name="Open Port 3002" dir=in action=allow protocol=TCP localport=3002

netsh advfirewall firewall add rule name="Open Port 8080" dir=in action=allow protocol=TCP localport=8080

netsh advfirewall firewall add rule name="Open Port 80" dir=in action=allow protocol=TCP localport=80

 

 

# 정상 작동 확인

curl -I http://192.168.0.131:3002/

 

 

최종확인

# 요청url /api 호출 시 프록시 서버에서 3002로 정상 전달되는지 확인

 

'DevOps > nginx' 카테고리의 다른 글

nginx 셋팅 (개발서버)  (0) 2023.01.13

1. 도커파일 작성

multi-stage 빌드를 사용하여 프론트엔드 애플리케이션을 빌드하고 배포하는 과정 작

ARG APP_NAME=""

FROM node:18-slim AS builder
WORKDIR /tmp/workspace
ARG APP_NAME
ENV BASE_PATH="${APP_NAME}"

ADD component component
ADD console-app console-app
COPY package.json ./package.json
COPY yarn.lock ./yarn.lock
RUN yarn install
RUN yarn workspace console-app build
----  빌더스테이지('builder') ---- 스테이지에서는 프로젝트의 소스 코드를 추가하고 필요한 의존성 설치한 뒤 빌드를 실


FROM node:18-slim as deploy
WORKDIR /tmp/workspace
COPY --from=builder /tmp/workspace/component/src ./component/src
COPY --from=builder /tmp/workspace/component/public ./component/public
COPY --from=builder /tmp/workspace/component/package.json ./component/

COPY --from=builder /tmp/workspace/console-app/public ./console-app/public
COPY --from=builder /tmp/workspace/console-app/.next ./console-app/.next
COPY --from=builder \
/tmp/workspace/console-app/package.json \
/tmp/workspace/console-app/next.config.js \
/tmp/workspace/console-app/.env.* \
./console-app/

COPY --from=builder /tmp/workspace/package.json ./package.json
COPY --from=builder /tmp/workspace/yarn.lock ./yarn.lock
RUN yarn install --production
배포 스테이지 ('deploy')  서브모듈 컴포넌트왕 콘솔앱의 필요 파일 복사 필요한 의존성만 설치


FROM node:18-alpine AS runner
WORKDIR /var/run/sugarbricks
ARG APP_NAME
ENV NODE_ENV production
ENV BASE_PATH="${APP_NAME}"

#ENV NEXT_TELEMETRY_DISABLED 1

COPY --from=deploy /tmp/workspace/ ./

EXPOSE 3000

CMD ["yarn", "workspace", "console-app", "start"]
실행 스테이지 최종 실행 이미지 정의 애플리케이션 실행

 

.dockerignore 파일 생성 후 불필요한 디렉터리나 파일 제외 하기 위해 추가 한다.

도커 허브 가입 후 

 

도커 repository 생성

 

생성 후

 

도커파일이 있는 디렉터리에서 명령어 실행

docker build -t your-dockerhub-username/your-image-name:latest .

 

도커 cli 를 통해 허브 로그인

 

도커 허브에 푸시

docker push your-dockerhub-username/your-image-name:tag

 

 

docker run -d -p 8080:80 --name my-container -e "NODE_ENV=production" my-image:tag 

위에는 참고로 실행하는명령어 도커 로컬에서 실행 하면 된다.

'DevOps > docker' 카테고리의 다른 글

리눅스 서버 도커 컨테이너 실행  (0) 2025.02.13

오늘 젠킨스에서 개발서버에 배포를 했더니 아래와같은 문제가 발생하였다 무한으로 루프를 도는것처럼... npm install 에서 넘어가질않았다.

 

로그를 확인해본결고 전에는 없던 경고가 생겨있엇다.

 

대충 이런 경고문들이 뜬후 진전이 없는 모습을 보게되었다.

버전이 맞지 않거나 변경되었다는 말이었다.

 

무한루프에 빠진것은 어쩌면 아래에 npm install 부분이 문제로 보였다.

npm dependencies 의존성에서 협업을 진행중에 서로 다른 npm 을 설치하다보면 node_modules 만 설치하고 

package.josn 에서는 빠진 부분들이 있어서 생기는 문제로 보여

npm install 로만 적혀있던부분을 -> npm install --save 로 변경하였다.

협업을 진행하는 중이라면 npm install --save 로 진행을 하는게 좋다.

npm 5 버전 이상은 자동으로 된다고 하던데... 혹시몰라서 진행을 했더니

배포도 말끔하게 잘되었다.

 

오늘은 nginx 웹 서버를 셋팅하는 작업을 진행 할거다!

nginx 에대한 기본 설명을 좀 하고 셋팅 과정을 천천히 진행해보자!

 

nginx 는 아파치와 같은 웹서버중 하나이며, 가볍고 여러요청을 한번에 처리가 가능한 장점을 가진 웹서버이다.

동작이 단순하고 전달자 역할만 해서 동시접속에 특화가 되어있다! 구동방식은 event driven (비동기처리방식)인데

요청이 들어올시 어떤 동작을 해야하는 지만 알려주고 다른 요청을 처리하는 방식이다.

cpu와 관계없이 모든 입출력들을 전부 이벤트 리스너로 전달하기 때문에 흐름이 끊기지 않고 응답이 빠르게 진행되어 1개의 프로세스로 보다 더빠른 작업을 가능하게 한다. 여기서 웹서버는 무엇이냐? 

웹서버의 역할은 html, css 자바스크립트, 이미지 와 같은 정적인 정보를 사용자 에게 전송해주는 역할을 한다.

또한 리버스 프록시의 역할도 한다! 리버스 프록시는 기존의 포워드 프록시가 클라이언트 앞단에서 요청을 처리한다면, 내부망의 서버 앞단에서 요청을 처리하는데 이 리버스 프록시의 장점은 보안에 강점이 있기 때문이다. was 웹어플리케이션 서버는 대부분 db서버와 연결되어있다. was 가 최전방에 있으면 보안에 취약해진다. 

위 그림처럼 바로 디비랑 연결이 되어있어서 보안이 매우 취약 한데 여기서 웹서버 인 nginx 를 두면 웹서버가 웹어플리케이션 서버랑 통신해서 결과를 클라이언트에게 제공하면 보안에 취약점을 잡을 수있다.! 서론이 너무 길었다 이제 셋팅을 해보도록 하겟다.

 

터미널을 킨 후 ssh로 개발 서버로 진입

비번을입력 후

NGINX.CONF 파일 찾아내는 명령어 -> sudo find / -name nginx.conf

터미널창에 입력해준다.( nginx 를 이미 개발서버에 설치 한 과정에서 설명)

찾은 후에 

  • 보통 /etc/nginx/* 아래에 설정파일이 위치해있고, 로그파일은 /var/log/nginx/* 에 위치해있다.

ls 로 항목들을 보면 

Nginx.conf(접속자 수, 동작 프로세스 수 등 퍼포먼스에 관한 설정들)

Sites-enabled( 활성화된 사이트들의 설정 파일들이 위치한다. 존재 하지 않은 경우 직접 디렉토리 생성 가능)

등등 이 있다 우선 우리는 

Sites-available ( 설정을 저장하는 곳이다. 여기에 설정을 만든 것은 실제로 nginx에 반영되지는 않습니다. 반영하기 위해서는 sites-enabled에 설정파일을 복사 또는 심볼릭링크를 걸어준다.)

 

nginx의 폴더 안을 들여다 보면 sites-available과 sites-enabled라는 폴더가 존재한다. 이 폴더들은 한 웹서버에서 여러가지 웹 서비스를 다른 도메인으로 운영 할 때를 대비해서 있는 폴더라고 이해하면 된다.

sites-available에는 각 도메인의 고유 설정 파일을 저장 해 두고, sites-enabled에 심볼릭 링크를 작성 혹은 삭제 함으로서 손쉽게 웹 서비스를 실행 혹은 중단 시키는 것이 가능하다.

 

등등으로 이해하고 vim 을 사용하여 

Nginx 가상서버 파일 작성

nginx.conf vim으로 수정하기(메인 설정 파일)

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}

http {
  include    mime.types;

  server {
        listen  8000;

        server_name tmng.tablenjoy.com;

        location @rewrites {
                rewrite ^(.+)$ /index.html last;
        }

        index index.html;

      #charset koi8-r;
      #access_log  logs/tadmin_html/host.access.log  main;
      #root /home/dev/hitable2020-frontend-manager/dist;

      location / {
        root    /home/dev/hitable2020-frontend-manager/dist;
        index   index.html;
        try_files $uri $uri/ @rewrites;
       }
    }

    server {
                listen 8003;
                listen [::]:8003;
                server_name tapi.tablenjoy.com;

                ssl on;
                ssl_certificate /home/dev/인증서/star_tablenjoy_com/star_tablenjoy_com.crt;
                ssl_certificate_key /home/dev/인증서/star_tablenjoy_com/star_tablenjoy_com.key;
                ssl_prefer_server_ciphers on;
                error_page 497 https://$host:8003$request_uri;
                location / {
                        add_header 'Access-Control-Allow-Origin' '*';
                        add_header 'Access-Control-Allow-Credentials' 'true';
                        add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
                        add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';

                        if ($request_method = 'OPTIONS') {
                                add_header 'Content-Type' 'text/plain charset=UTF-8';
                                add_header 'Content-Length' 0;
                                return 204;
                        }
                        proxy_redirect off;
                        proxy_set_header Host $http_host;
                        proxy_set_header Connction "";
                        proxy_set_header X-forward-for $proxy_add_x_forwarded_for;
                        proxy_pass http://127.0.0.1:8080;
                }

        }

    server {
        listen  8002;

        server_name tmng.tablenjoy.com;
        ssl on;
        ssl_certificate /home/dev/인증서/star_tablenjoy_com/star_tablenjoy_com.crt;
        ssl_certificate_key /home/dev/인증서/star_tablenjoy_com/star_tablenjoy_com.key;
        ssl_prefer_server_ciphers on;

        location @rewrites {
                rewrite ^(.+)$ /index.html last;
        }

        index index.html;

      #charset koi8-r;
      #access_log  logs/tadmin_html/host.access.log  main;

      location / {
        root    /home/dev/hitable2020-frontend-brand-admin/dist;
        index   index.html;
        try_files $uri $uri/ @rewrites;
       }
    }

    server {
        listen  8005;

        server_name tmobile.hi-table.com;

        location @rewrites {
                rewrite ^(.+)$ /index.html last;
        }

        index index.html;

      #charset koi8-r;
      #access_log  logs/tmobile_html/host.access.log  main;

      location / {
        root    /home/dev/hitable2020-frontend-store-manager/dist;
        index   index.html;
        try_files $uri $uri/ @rewrites;
       }
    }

        server {
                listen 7400;
                listen [::]:7400;
                server_name tapi.tablenjoy.com;

                ssl on;
                ssl_certificate /home/dev/인증서/star_tablenjoy_com/star_tablenjoy_com.crt;
                ssl_certificate_key /home/dev/인증서/star_tablenjoy_com/star_tablenjoy_com.key;
                ssl_prefer_server_ciphers on;
                error_page 497 https://$host:7400$request_uri;
                location / {
                        add_header 'Access-Control-Allow-Origin' '*';
                        add_header 'Access-Control-Allow-Credentials' 'true';
                        add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
                        add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';

                        if ($request_method = 'OPTIONS') {
                                add_header 'Content-Type' 'text/plain charset=UTF-8';
                                add_header 'Content-Length' 0;
                                return 204;
                        }
                        proxy_redirect off;
                        proxy_set_header Host $http_host;
                        proxy_set_header Connction "";
                        proxy_set_header X-forward-for $proxy_add_x_forwarded_for;
                        proxy_pass http://127.0.0.1:3000;
                }

        }

        server {
                listen 8001 default_server;
                listen [::]:8001 default_server;
                server_name _;



                ssl on;
                ssl_certificate /home/dev/인증서/star_tablenjoy_com/star_tablenjoy_com.crt;
                ssl_certificate_key /home/dev/인증서/star_tablenjoy_com/star_tablenjoy_com.key;
                ssl_prefer_server_ciphers on;
                location / {
                        add_header 'Access-Control-Allow-Origin' '*';
                        add_header 'Access-Control-Allow-Credentials' 'true';
                        add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
                        add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';

                        if ($request_method = 'OPTIONS') {
                                add_header 'Content-Type' 'text/plain charset=UTF-8';
                                add_header 'Content-Length' 0;
                                return 204;
                        }

                        proxy_redirect off;
                        proxy_set_header Host $http_host;
                        proxy_set_header X-real-ip $remote_addr;
                        proxy_set_header Connction "";
                        proxy_set_header X-forward-for $proxy_add_x_forwarded_for;
                        proxy_pass http://127.0.0.1:3000;
                }
        }

}

                                                                                                                                                                                                                                                                                                                                                          113,0-1       23%

listen 은 port 설정 하는 부분이다. 기본 포트는 80

root는 document root 설정 하는 부분 디폴드 는 html 디렉토리.

proxy_pass 설정은 nginx 뒷 단에 was 가 존재하는 경우이며, 확장자 기반으로 요청 처리를 분리하는 것 location /  이렇게 되어있으면 127.0.0.1:3000 으로 넘기는것이다.

server_name 도메인이름 처럼 바꿔주게 되는데 방문자가 어떤 주소로 들어오냐에따라 해당 도메인 이름을 가진 server{...} 블록이 처리한다.

 

- http 블록

http 블록은 server, location 블록을 포함한다. http 블록을 여러개 생성하여 관리할 수 있지만, 권장사항은 아니다.(권장사항은 http 블록을 하나만 생성하는 것) (oop 상위 클래스 개념으로 생각하면 쉽다.)

 

-서버 블록

- 하나의 웹사이트를 선언하는 데 사용된다. 가상 호스팅(Virtual Host)의 개념이다.

 

- location 블록

서버의 하위단 블록으로 특정 경로, 즉 특정 url을 처리하는 단위이다.

예를 들어  개발 소개 블로그 (www.dev.com) 에서

www.dev.com/nginx   (nginx를 소개하는 카테고리) 

www.dev_moster.com/apache (apache를 소개하는 카테고리)

로 나누고 싶을때 사용할 수 있다.

 

- events 블록

http, server, location 블록과 엮이지 않고 독립적은 블록이다.

주로, 네트워크 동작 방법을 설정한다. 

예를 들어 

events {

worker_connections 100;

}

이렇게 worker_connections 을 100으로 설정하면,

이 웹서버에 한번에 최대 100명이 동시에 접근을 할 수 있다는 것을 의미한다. 

 

ssl 부분은 도메인 인증서, 체인 인증서, 루트 인증서를 발급 완료받은걸 서버에 업로드한다. 그 경로를 적어주면 연결된다.!

ssl_certificate /root/ssl/nginx-ssl.crt;
ssl_certificate_key /root/ssl/nginx-ssl.key;

위에처럼 위치를 지정해주면 된다.

추가적으로 http 로 접속 시 https 로 리다이렉트 하는 부분은 아래와 같이 코드를 추가하면된다.

    return 301 https://$host$request_uri;

아래 리스타트 명령어로 재실행 시킨다.

sudo systemctl restart nginx

site available 에서 설정파일 추가

server {
        listen 80 default_server;
        listen [::]:80 default_server;

        # SSL configuration
        #
        # listen 443 ssl default_server;
        # listen [::]:443 ssl default_server;
        #
        # Note: You should disable gzip for SSL traffic.
        # See: https://bugs.debian.org/773332
        #
        # Read up on ssl_ciphers to ensure a secure configuration.
        # See: https://bugs.debian.org/765782
        #
        # Self signed certs generated by the ssl-cert package
        # Don't use them in a production server!
        #
        # include snippets/snakeoil.conf;

        root /var/www/html;

        # Add index.php to the list if you are using PHP
        index index.html index.htm index.nginx-debian.html;

        server_name _;

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ =404;
        }

        # pass PHP scripts to FastCGI server
        #
        #location ~ \.php$ {
        #       include snippets/fastcgi-php.conf;
        #
        #       # With php-fpm (or other unix sockets):
        #       fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
        #       # With php-cgi (or other tcp sockets):
        #       fastcgi_pass 127.0.0.1:9000;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #       deny all;
        #}
}

server {
    listen 8005;
    listen [::]:8005;

    root /home/dev/hitable2020-frontend-manager/dist;
    index index.html index.htm;
    location / {
       try_files $uri $uri/ /index.html;
    }
}

server {
        listen 8001 ssl;

        root /var/www/html;
        index index.html index.htm;
        server_name _;

        location / {
                try_files $uri $uri/ =404;
        }

        location ~/.well-known {
                allow all;
        }
}

여기서 심볼릭 링크를 걸어줘야한다 site-adailable/ 에서 만든 사이트를 site-enabled/에 추가해야 활성화가 된다.

site-adailable/ 에 추가한 사이트를 site-enabled/에 심볼릭 링크하여 사이트를 활성화 할 수 있다.

심볼릭 링크랑 윈도위의 바로가기이다.

 

site-enabled 추가

#
# In most cases, administrators will remove this file from sites-enabled/ and
# leave it as reference inside of sites-available where it will continue to be
# updated by the nginx packaging team.
#
# This file will automatically load configuration files provided by other
# applications, such as Drupal or Wordpress. These applications will be made
# available underneath a path with that package name, such as /drupal8.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##

# Default server configuration
#
server {
        listen 80 default_server;
        listen [::]:80 default_server;

        # SSL configuration
        #
        # listen 443 ssl default_server;
        # listen [::]:443 ssl default_server;
        #
        # Note: You should disable gzip for SSL traffic.
        # See: https://bugs.debian.org/773332
        #
        # Read up on ssl_ciphers to ensure a secure configuration.
        # See: https://bugs.debian.org/765782
        #
        # Self signed certs generated by the ssl-cert package
        # Don't use them in a production server!
        #
        # include snippets/snakeoil.conf;

        root /var/www/html;

        # Add index.php to the list if you are using PHP
        index index.html index.htm index.nginx-debian.html;

        server_name _;

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ =404;
        }

        # pass PHP scripts to FastCGI server
        #
        #location ~ \.php$ {
        #       include snippets/fastcgi-php.conf;
        #
        #       # With php-fpm (or other unix sockets):
        #       fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
        #       # With php-cgi (or other tcp sockets):
        #       fastcgi_pass 127.0.0.1:9000;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #       deny all;
        #}
}

server {
    listen 8005;
    listen [::]:8005;

    root /home/dev/hitable2020-frontend-manager/dist;
    index index.html index.htm;
    location / {
       try_files $uri $uri/ /index.html;
    }
}

server {
        listen 8001 ssl;

        root /var/www/html;
        index index.html index.htm;
        server_name _;

        location / {
                try_files $uri $uri/ =404;
        }

        location ~/.well-known {
                allow all;
        }
}
# Virtual Host configuration for example.com
#
# You can move that to a different file under sites-available/ and symlink that
# to sites-enabled/ to enable it.
#

conf.d 폴더

  • nginx.conf에서 include로 불러올 수 있는 conf 파일 저장 폴더.

 

server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;

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

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}

좀더 디테일하게 하고 싶으면 검색을 하면서 추가적으로 셋팅을 더넣으면 될것같다 내부아이피로 포트를 타면 페이지가 잘뜨는것을 확인할수있다~ 내부아이피타임으로 설정은 추가적으로 들어가면 내부에서 와이파이가 연결되어있을경우에만 회사에서 사용가능한 개발서버가 열린다~

 

101 번서버에 추가적으로 nginx 가 잘연결됐다.

'DevOps > nginx' 카테고리의 다른 글

두개의 서버 설정 - Local 윈도우 기준  (0) 2024.08.27

+ Recent posts