2024년 12월 28일 토요일

자바스크립트 동기 비동기 처리 정리

 

자바스크립트는 기본적으로 동기 방식으로 작동한다. 즉, 코드가 위에서 아래로 순차적으로 실행된다.

하지만, 자바스크립트는 비동기 처리가 필요한 상황(예: 네트워크 요청, 타이머, 파일 읽기 등)에서 비동기 처리를 할 수 있도록 콜백, 프라미스(Promises), async/await와 같은 기능을 제공하여 비동기 방식으로 작업할 수 있게 한다.

자바스크립트의 비동기 처리는 이벤트 루프(Event Loop)를 통해 이루어지며, 이를 통해 메인 스레드를 차단하지 않고 비동기 작업이 완료될 때까지 기다린 후 실행을 이어갈 수 있다.

자바스크립트에서 여러 함수를 순차적으로 호출할 때, 기본적으로는 각 함수가 이전 함수의 실행이 완료될 때까지 기다리지 않는다. 즉, 자바스크립트는 한 함수가 끝나기 전에 다음 함수를 호출할 수 있다. 하지만 이 동작 방식은 함수 내부의 작업이 동기식인지 비동기식인지에 따라 다르다.

  1. 동기 함수: 함수가 동기적으로 작성된 경우, 자바스크립트는 해당 함수가 완전히 실행될 때까지 기다렸다가 다음 함수를 실행한다. 즉, 이전 함수가 완료되지 않으면 다음 함수로 넘어가지 않는다.
  2. 비동기 함수: 함수 내부에 비동기 코드가 포함된 경우(예: setTimeout, fetch, Promise 등), 해당 함수는 비동기 작업이 완료되기를 기다리지 않고 바로 다음 함수를 호출한다. 비동기 작업이 완료되면 나중에 콜백 또는 Promisethen 블록에서 결과를 처리한다.

예를 들어, 다음 코드에서는 fetchData()가 비동기 함수라면 processData()가 바로 실행된다.

function fetchData() {
  setTimeout(() => console.log("Data fetched"), 1000); // 1초 후 실행
}

function processData() {
  console.log("Processing data");
}

fetchData(); // 비동기 함수
processData(); // 즉시 실행됨

이 코드에서는 fetchData가 완료되기를 기다리지 않고 processData가 바로 실행된다. 만약 순차적으로 실행되기를 원한다면, Promiseasync/await를 사용하여 비동기 함수를 동기적인 방식으로 제어할 수 있다.

자바스크립트에서 함수의 동기/비동기 동작 여부는 함수가 비동기 작업을 포함하는지에 따라 결정된다. 즉, setTimeout, Promise, async/await 등의 비동기 처리가 포함되어 있는지가 동기/비동기 동작을 구분하는 기준이다.

결과를 반환하지 않는 동기 함수는 여전히 호출된 순서대로 실행을 마치고 다음 코드로 넘어가기 때문에 동기적으로 동작한다.

예시

아래의 calculateSum 함수는 결과를 반환하지 않지만, for 루프를 통해 합계를 계산한 후 console.log로 결과를 출력한다. 이 함수는 순차적으로 실행되며, 완료될 때까지 다음 코드로 넘어가지 않는다.

function calculateSum(arr) {
  let sum = 0;
  for (let num of arr) {
    sum += num;
  }
  console.log("Sum:", sum); // 결과를 출력
}

calculateSum([1, 2, 3, 4, 5]);
console.log("이 문장은 합계가 출력된 후 실행된다.");

위 코드를 실행하면, calculateSum 함수가 완료된 후 "이 문장은 합계가 출력된 후 실행된다."가 출력된다. calculateSum이 결과를 반환하지 않더라도, 순차적으로 실행되므로 동기적으로 동작한다고 볼 수 있다.

따라서 동기/비동기 여부는 반환 여부와 관계없이 함수 내부에 비동기 코드가 있는지에 따라 결정된다.

자바스크립트에서 제공하는 비동기 함수 또는 비동기 처리를 지원하는 메서드는 아래와 같은 것들이 있다. 이를 이용해 시간이 오래 걸릴 수 있는 작업을 비동기적으로 처리할 수 있다.


타이머 관련 비동기 함수

  1. setTimeout 지정된 시간이 지난 후 콜백 함수를 실행한다.
  2. setInterval 지정된 간격마다 콜백 함수를 반복 실행한다.
  3. clearTimeoutsetTimeout으로 설정된 작업을 취소한다.
  4. clearIntervalsetInterval로 설정된 작업을 취소한다.

네트워크 요청 관련 함수

  1. fetch

    HTTP 요청을 보내고 응답을 비동기적으로 처리한다.

    fetch("<https://api.example.com/data>").then(response => response.json());
    
    
  2. XMLHttpRequest

    옛날 방식으로 HTTP 요청을 처리하는 객체로, 비동기 호출 지원.

    const xhr = new XMLHttpRequest();
    xhr.open("GET", "<https://api.example.com/data>", true);
    xhr.send();
    
    
  3. WebSocket

    서버와 실시간 통신을 위한 비동기 API.

    const ws = new WebSocket("wss://example.com/socket");
    ws.onmessage = (event) => console.log(event.data);
    
    
  4. Axios

    외부 라이브러리지만, 널리 사용되는 Promise 기반 HTTP 요청 라이브러리.

    axios.get("<https://api.example.com/data>").then(response => console.log(response));
    
    

이벤트 관련 비동기 처리

  1. DOM Event Listeners

    DOM 이벤트를 비동기로 처리.

    document.addEventListener("click", () => console.log("Clicked!"));
    
    
  2. EventEmitter (Node.js)

    이벤트 기반 비동기 처리를 위한 객체.

    const EventEmitter = require("events");
    const emitter = new EventEmitter();
    emitter.on("event", () => console.log("Event triggered"));
    emitter.emit("event");
    
    

Promise 기반 비동기 함수

  1. Promise

    비동기 작업의 성공 또는 실패를 처리하는 객체.

    const promise = new Promise((resolve, reject) => {
      setTimeout(() => resolve("Done"), 1000);
    });
    
    
  2. Promise.all

    여러 Promise를 병렬로 처리하고, 모두 완료되면 결과를 반환한다.

  3. Promise.race

    여러 Promise 중 가장 먼저 완료된 하나의 결과를 반환한다.

  4. Promise.allSettled

    모든 Promise의 상태(성공/실패)를 반환한다.

  5. Promise.any

    여러 Promise 중 가장 먼저 성공한 결과를 반환한다.


Async/Await

  1. async/awaitPromise를 쉽게 다룰 수 있도록 지원하는 ES8 구문.

    async function fetchData() {
      const response = await fetch("<https://api.example.com/data>");
      return await response.json();
    }
    
    

타이머 및 스케줄링 관련 (Node.js)

  1. process.nextTick 이벤트 루프의 다음 틱에서 실행.

  2. setImmediate 이벤트 루프의 다음 반복 주기에서 실행.

  3. queueMicrotask 마이크로태스크 큐에 작업을 추가.

    queueMicrotask(() => console.log("Microtask executed"));
    
    

File I/O 및 데이터 처리 (Node.js)

  1. fs.readFile

    파일을 비동기적으로 읽음.

    const fs = require("fs");
    fs.readFile("file.txt", "utf-8", (err, data) => console.log(data));
    
    
  2. fs.writeFile

    파일을 비동기적으로 씀.


실시간 작업 및 워커 관련

  1. setTimeout/setInterval (실시간 작업)

  2. Web Workers

    스레드에서 실행되는 비동기 작업.

    const worker = new Worker("worker.js");
    worker.onmessage = (event) => console.log(event.data);
    
    
  3. SharedWorker

    여러 스크립트 간 공유되는 워커.

  4. Service Workers

    웹 애플리케이션의 백그라운드 작업 처리.


애니메이션 및 UI 관련

  1. requestAnimationFrame

    브라우저에서 애니메이션 작업을 효율적으로 처리하기 위해 비동기로 실행.

    requestAnimationFrame(() => console.log("Animation frame"));
    
    
  2. cancelAnimationFrame

    예약된 애니메이션 프레임을 취소.


기타 비동기 처리

  1. setTimeout/setInterval

  2. queueMicrotask

  3. process.nextTick (Node.js 전용)

  4. IntersectionObserver

    요소가 뷰포트에 진입하거나 나갈 때 비동기적으로 알림.

  5. MutationObserver

    DOM 변경 사항을 비동기로 감지.

    const observer = new MutationObserver((mutations) => console.log(mutations));
    observer.observe(document.body, { childList: true });
    
    
  6. FileReader

    파일을 비동기적으로 읽는 Web API.

    const reader = new FileReader();
    reader.onload = (event) => console.log(event.target.result);
    reader.readAsText(someFile);
    
    
  7. Geolocation API

    위치 정보를 비동기로 가져옴.

    navigator.geolocation.getCurrentPosition((position) => console.log(position));
    
    
  8. Notification

    사용자에게 비동기로 알림을 표시.

2024년 11월 8일 금요일

웹 애플리케이션의 시스템 성능 개선과 트러블슈팅

웹 애플리케이션의 시스템 성능 개선과 트러블슈팅을 위한 일반적인 방법은 다음과 같다.

1. 성능 개선 방안

a) 캐싱(Caching)

  • 데이터베이스 캐싱: 자주 조회되는 데이터를 캐시(예: Redis, Memcached)에 저장해 DB 부하를 줄인다.
  • 컨텐츠 캐싱: Cloudflare 같은 CDN 서비스를 통해 이미지, JS, CSS 파일 등 정적 파일을 캐싱하여 빠르게 제공한다.
  • 페이지 캐싱: 사용자 세션에 따라 달라지지 않는 페이지는 캐시하여 렌더링 시간을 줄인다.

b) 데이터베이스 최적화

  • 인덱스 최적화: 자주 사용하는 쿼리의 칼럼에 적절히 인덱스를 설정하여 조회 성능을 높인다.
  • 쿼리 최적화: 쿼리를 단순화하고 복잡한 연산을 줄여 쿼리 실행 시간을 줄인다.
  • 데이터 정규화 및 비정규화: 정규화로 데이터 중복을 최소화하고, 필요한 경우 비정규화로 빠른 읽기 성능을 지원한다.

c) 비동기 처리 및 분산 처리

  • 비동기 요청 처리: 오래 걸리는 작업(예: 파일 처리, 데이터 분석)은 비동기로 처리하여 사용자 경험을 개선한다.
  • 분산 작업 처리: Redis, Kafka, RabbitMQ 등의 메시지 큐를 활용해 비동기 분산 처리를 적용한다.

d) 네트워크 최적화

  • CDN 사용: 전 세계에 분산된 CDN 서버로부터 데이터를 제공하여 사용자와의 물리적 거리를 줄인다.
  • HTTP/2 및 Keep-Alive 사용: 다중 요청을 병렬로 처리하고, 연결을 유지해 다수의 리소스 로딩 시간을 줄인다.
  • Gzip 압축: 텍스트 기반 파일을 Gzip으로 압축하여 전송 시간을 절감한다.

e) 웹 서버 최적화

  • 서버 자원 모니터링: CPU, 메모리, 네트워크 I/O 등의 리소스 사용량을 주기적으로 확인해 병목 현상을 파악하고 개선한다.
  • 로드 밸런싱: 웹 서버 여러 대로 트래픽을 분산하여 서버 과부하를 방지한다.
  • 컨테이너 오케스트레이션: Docker, Kubernetes를 사용해 애플리케이션을 효율적으로 배포하고 스케일링한다.

2. 트러블슈팅 방안

a) 성능 모니터링 및 로깅

  • 애플리케이션 성능 모니터링(APM): Datadog, New Relic, Prometheus 등을 사용해 트랜잭션 및 요청 시간을 추적하여 성능 저하 원인을 파악한다.
  • 로그 분석: Elastic Stack, Splunk 등의 로그 분석 도구로 예외 발생 원인 및 에러 로그를 조사해 이슈를 해결한다.

b) 데이터베이스 트러블슈팅

  • 쿼리 성능 분석: 쿼리 실행 계획을 통해 쿼리 실행 속도가 느린 원인을 분석하고 최적화한다.
  • 잠금(lock) 현상 분석: 데이터베이스 잠금으로 인해 트랜잭션이 지연되는 문제를 확인하고 잠금이 걸리지 않도록 구조를 조정한다.

c) 네트워크 및 API 트러블슈팅

  • 패킷 캡처 도구 활용: Wireshark, tcpdump 등을 이용해 네트워크 패킷을 분석하고 성능 병목 구간을 파악한다.
  • API 성능 테스트 및 타임아웃 설정: API 응답 시간이 긴 경우에는 시간 초과 설정을 조정하거나, API 속도를 높이기 위한 최적화 방안을 모색한다.

d) 코드 최적화

  • 메모리 누수 방지: GC 로그를 통해 메모리 누수가 발생하는지 확인하고, 메모리 누수를 일으키는 코드 수정 및 객체 관리를 개선한다.
  • 코드 프로파일링: CPU 및 메모리 사용량을 분석해 성능에 영향을 주는 비효율적인 코드(예: 중복 연산)를 최적화한다.

e) 서버 및 인프라 문제 해결

  • 서버 리소스 조정: 서버 스펙 업그레이드 또는 서버 대수를 조정하여 병목 현상을 해결한다.
  • 컨테이너 상태 확인 및 재배포: 오작동하는 컨테이너가 있다면 상태를 확인하고 재배포하여 안정성을 확보한다.

2024년 10월 22일 화요일

부동산 셀프 등기 필요 서류

부동산 셀프 등기 서류

  • 매매계약서 원본 / 사본
  • 부동산 거래계약 신고필증
  • 주민등록등본 초본(주소변동사항, 주민등록 뒷자리 포함) - 본인. 배우자(공동명의)
  • 가족관계부 - 본인. 배우자(공동명의)
  • 매도인 인감증명서
  • 매도인 주민등록초본
  • 매도인 위임장 - 인감도장 직인
  • 토지대장 1부 > 구청. 온라인
  • 건축물대장 1부 > 구청. 온라인
  • 생애최초 취득세 감면 신청서 > 구청
  • 주민등록등본 초본(주소변동사항, 주민등록 뒷자리 포함) - 본인. 배우자(공동명의)
  • 가족관계부 - 본인. 배우자(공동명의)
  • 취득세 납부 영수증 > 구청
  • 국민주택채권 매입 영수증 > 은행
  • 수입인지 영수증 > 은행
  • 등기신청수수료 납부 영수증 (수수료 지불 후 바로 발급 가능) > 등기소

2024년 9월 24일 화요일

헤드리스 아키텍처 Headless

"헤드리스"라는 단어는 본래의 의미에서 "머리(Head)가 없는" 것을 가리킨다. 헤드리스 아키텍처에서 이 용어를 사용한 이유는, 전통적인 소프트웨어 아키텍처와 비교할 때 프론트엔드(사용자 인터페이스, 즉 '머리'에 해당하는 부분)가 백엔드와 분리되어 독립적으로 동작하기 때문이다.

이유 및 배경:

  1. 전통적인 시스템 구조:
    • 과거의 전통적인 커머스 시스템이나 CMS(콘텐츠 관리 시스템)에서는 프론트엔드(웹페이지 UI)와 백엔드(서버, 데이터베이스, 비즈니스 로직)가 긴밀하게 결합된 형태로 동작했다. 백엔드는 데이터 및 로직을 처리하고, 동시에 프론트엔드를 통해 사용자에게 데이터를 표시하는 역할까지 담당했다. 여기서 프론트엔드가 일종의 "머리" 역할을 했다.
  2. 헤드리스 구조:
    • 헤드리스 아키텍처에서는 백엔드가 "머리(프론트엔드)"가 없는 상태로, 단순히 데이터를 처리하고 API로 결과를 제공한다. 프론트엔드는 백엔드와 독립적으로 존재하며, REST API 또는 GraphQL을 통해 필요한 데이터를 받아서 처리하고 사용자에게 보여준다.
    • 여기서 "머리(Head)"에 해당하는 프론트엔드가 사라졌으므로, 이를 "헤드리스"라고 부른다.

장점:

  • 프론트엔드와 백엔드의 독립성: 프론트엔드를 다양한 기술로 자유롭게 구축할 수 있다. 웹, 모바일 앱, IoT 등 다양한 플랫폼에 적용 가능.
  • 유연성: 하나의 백엔드에 여러 "헤드"(웹사이트, 모바일 앱 등)를 연결할 수 있어 다양한 사용자 경험을 제공할 수 있다.

"헤드리스"라는 용어는 백엔드가 특정 프론트엔드에 종속되지 않고, 다양한 프론트엔드로 확장 가능한 상태를 나타내기 위해 사용된다.

Node.js 서버가 사용하는 포트를 확인하고 해당 포트를 종료하는 방법


1. Node 서버가 사용하는 포트 확인 방법

방법 1: lsof 명령어 사용 (리눅스/맥OS)

lsof -i :<포트번호>

이 명령어는 특정 포트 번호로 열려 있는 프로세스를 확인할 수 있다.

예시:

lsof -i :3000

출력 예시:

COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
node     1234 user   22u  IPv4  56789      0t0  TCP *:3000 (LISTEN)

여기서 PID 값이 해당 포트를 사용하는 프로세스의 ID이다.

방법 2: netstat 명령어 사용 (리눅스/맥OS)

netstat -tuln | grep :<포트번호>

이 명령어는 특정 포트 번호가 열려 있는지 확인할 수 있다.

2. 특정 포트 사용하는 프로세스 종료 방법

방법 1: kill 명령어 사용

lsof 또는 netstat 명령어로 확인한 PID 값을 이용해 해당 프로세스를 종료할 수 있다.

kill -9 <PID>

예시:

kill -9 1234

이 명령어는 PID가 1234인 프로세스를 강제 종료한다.

방법 2: fuser 명령어 사용 (리눅스)

fuser -k <포트번호>/tcp

예시:

fuser -k 3000/tcp

이 명령어는 TCP 포트 3000을 사용하는 프로세스를 종료한다.

3. 여러 포트를 동시에 확인하는 방법

방법 1: lsof 명령어 사용 (리눅스/맥OS)

여러 포트를 한 번에 확인할 수는 없지만, 원하는 포트마다 각각 명령어를 실행할 수 있다.

lsof -i :3000 -i :8080 -i :5000

각 포트에 대해 사용 중인 프로세스 정보를 확인할 수 있다.

방법 2: netstat 명령어 사용 (리눅스/맥OS)

여러 포트의 정보를 한번에 확인할 수는 없지만, 네트워크 상태 전체를 조회 후 필요한 정보를 추출할 수 있다.

netstat -tuln | grep ':3000\\\\|:8080\\\\|:5000'

이 명령어는 포트 3000, 8080, 5000에서 동작 중인 프로세스들을 필터링한다.

4. 여러 포트를 사용하는 프로세스 종료 방법

방법 1: lsofkill 조합

각 포트별로 확인한 PID를 모아 아래와 같이 한꺼번에 종료할 수 있다.

kill -9 $(lsof -t -i :3000 -i :8080 -i :5000)

이 명령어는 포트 3000, 8080, 5000에서 실행 중인 프로세스를 한 번에 종료한다.

방법 2: fuser 명령어 사용 (리눅스)

fuser -k 3000/tcp 8080/tcp 5000/tcp

이 명령어는 지정한 여러 포트에서 동작 중인 프로세스를 한꺼번에 종료한다.

2024년 9월 19일 목요일

chmod : 읽기, 쓰기, 실행 권한, 하위 폴더 디렉토리

chmod 명령어는 파일이나 디렉토리의 권한을 변경할 때 사용된다. UNIX 및 Linux 시스템에서 파일의 읽기, 쓰기, 실행 권한을 관리하는 중요한 도구다.

1. 기본 개념

파일이나 디렉토리의 권한은 세 가지 범주로 나뉜다:

  • 소유자(owner): 파일을 소유한 사용자
  • 그룹(group): 파일을 소유한 사용자가 속한 그룹
  • 기타(others): 위 두 범주에 속하지 않는 모든 사용자

각 범주에 대해 세 가지 권한을 설정할 수 있다:

  • 읽기(read, r): 파일을 읽을 수 있는 권한 (디렉토리인 경우 목록 확인 가능)
  • 쓰기(write, w): 파일을 수정할 수 있는 권한 (디렉토리인 경우 파일 추가/삭제 가능)
  • 실행(execute, x): 파일을 실행할 수 있는 권한 (디렉토리인 경우 접근 가능)

2. 숫자 형식

권한은 8진수로 나타내며, 각 권한은 특정 숫자로 매핑된다:

  • 읽기(r) = 4
  • 쓰기(w) = 2
  • 실행(x) = 1

세 개의 권한을 조합하여 숫자로 표현한다. 예를 들어:

  • 읽기 + 쓰기 + 실행: 7 (4 + 2 + 1)
  • 읽기 + 쓰기: 6 (4 + 2)
  • 읽기 + 실행: 5 (4 + 1)
  • 읽기만: 4
  • 실행만: 1
  • 권한 없음: 0

숫자 조합 예시:

  • chmod 755 file.txt
    • 소유자: 읽기, 쓰기, 실행 (7)
    • 그룹: 읽기, 실행 (5)
    • 기타: 읽기, 실행 (5)

3. 문자 형식

숫자 대신 문자로도 권한을 설정할 수 있다. 이때 u(소유자), g(그룹), o(기타), a(전체)를 사용한다.

  • +: 권한을 추가
  • ``: 권한을 제거
  • =: 특정 권한만 설정

문자 형식 예시:

  • chmod u+x file.txt: 소유자에게 실행 권한을 추가
  • chmod g-w file.txt: 그룹의 쓰기 권한을 제거
  • chmod o=r file.txt: 기타 사용자에게 읽기 권한만 부여

4. 디렉토리 권한

디렉토리에 권한을 설정할 때는 파일과 동일한 방법으로 설정되지만, 의미는 조금 다르다.

  • 읽기 권한(r): 디렉토리의 파일 목록을 볼 수 있음.
  • 쓰기 권한(w): 디렉토리 내에서 파일을 추가하거나 삭제할 수 있음.
  • 실행 권한(x): 디렉토리 내에서 파일에 접근 가능.

하위 디렉토리 및 파일에 권한 적용

  • chmod -R 755 directory/
    : directory/와 그 하위 파일 및 디렉토리 모두에 재귀적으로 755 권한을 적용.

5. 실습 예시

예시 1: 파일에 권한 추가

chmod 644 example.txt
  • 소유자는 읽기/쓰기 권한 (6)
  • 그룹과 기타는 읽기만 가능 (4)

예시 2: 실행 권한 추가

chmod +x script.sh
  • 모든 사용자에게 실행 권한을 추가

예시 3: 디렉토리에 재귀적으로 권한 변경

chmod -R 755 /var/www/html
  • /var/www/html 디렉토리와 그 하위 모든 파일 및 디렉토리에 755 권한 적용

6. 기타 옵션

  • R: 재귀적으로 하위 파일 및 디렉토리 모두에 권한을 적용.

  • -reference: 다른 파일의 권한을 기준으로 설정.

    chmod --reference=otherfile.txt myfile.txt
    

요약

  • chmod는 파일과 디렉토리의 권한을 설정하는 명령어로, 8진수(숫자)와 문자 형식으로 설정할 수 있다.
  • 권한은 소유자, 그룹, 기타 사용자로 나누어 설정되며, 읽기, 쓰기, 실행 권한을 조합해 적용한다.

2024년 9월 10일 화요일

저작권 없이 무료로 오디오 소스, 배경음악, 효과음을 제공하는 사이트

저작권 없이 무료로 오디오 소스, 배경음악, 효과음을 제공하는 사이트들이 여러 곳 있다. 대부분 상업적 및 비상업적 용도로도 사용 가능하다.

  1. Free Music Archive (FMA)
    • 저작권 무료 음악을 제공하며, 상업적 용도로도 사용 가능하다. Creative Commons 라이선스 기반으로 음악을 제공.
    • https://freemusicarchive.org
  2. Bensound
    • 무료로 사용할 수 있는 다양한 배경음악을 제공하며, 상업적 용도로 사용하려면 출처를 표기해야 한다.
    • https://www.bensound.com
  3. Incompetech
    • 영화, 비디오, 팟캐스트 등을 위한 무료 음악을 제공. 상업적 사용 시 크레딧 필요.
    • https://incompetech.com
  4. FreePD
    • 저작권이 만료된 퍼블릭 도메인 음악을 제공하는 사이트로, 상업적, 비상업적 용도로 모두 사용 가능하다.
    • https://freepd.com
  5. Pixabay Music
  6. YouTube Audio Library
  7. Mixkit
    • 비디오, 영화, 게임 등을 위한 무료 배경음악과 효과음을 제공하며, 상업적 용도로도 사용 가능하다.
    • https://mixkit.co/free-stock-music
  8. Josh Woodward
  9. Chosic
    • 무료 음악 및 배경음악을 제공하며, 크리에이티브 커먼즈 라이선스와 퍼블릭 도메인 음악을 찾을 수 있다.
    • https://www.chosic.com/free-music
  10. Audionautix
    • 무료로 다양한 장르의 음악을 제공하며, 크레딧만 표기하면 상업적 용도로도 사용 가능하다.
    • https://audionautix.com
  11. Freesound
    • 사용자들이 업로드한 다양한 소리와 효과음을 제공하며, 크리에이티브 커먼즈 라이선스 기반으로 제공된다.
    • https://freesound.org
  12. SoundBible
    • 무료 효과음과 사운드 클립을 제공하며, 상업적 용도로도 사용 가능하다.
    • http://soundbible.com

이 사이트들은 무료로 저작권 걱정 없이 사용할 수 있는 오디오와 음악을 제공한다. 다만 각 오디오 파일마다 라이선스 조건이 다를 수 있으므로, 사용 전에 라이선스를 확인하는 것이 중요하다.

저작권 없이 무료로 이미지를 제공하는 사이트 : 상업적 및 개인적 용도로 사용 가능

 저작권 없이 무료로 이미지를 제공하는 사이트들은 여러 가지가 있으며, 대부분 상업적 및 개인적 용도로 사용할 수 있다.

  1. Unsplash
    • 수많은 고퀄리티 이미지를 제공하며, 상업적 이용이 가능하다.
    • https://unsplash.com
  2. Pixabay
    • 사진, 일러스트, 벡터 이미지, 비디오 등 다양한 미디어 파일을 무료로 제공한다.
    • https://pixabay.com
  3. Pexels
    • 다양한 사진과 비디오 클립을 무료로 제공하며, 상업적 용도로도 사용 가능하다.
    • https://www.pexels.com
  4. Burst by Shopify
    • 온라인 상점 운영자와 크리에이터를 위한 무료 이미지 사이트. 상업적 용도 가능.
    • https://burst.shopify.com
  5. FreeImages
  6. StockSnap
    • 상업적 용도로도 사용할 수 있는 무료 이미지가 매일 업데이트된다.
    • https://stocksnap.io
  7. Picjumbo
    • 무료 고해상도 이미지를 제공하며, 상업적 용도로도 사용 가능하다.
    • https://picjumbo.com
  8. Reshot
    • 고유한 이미지를 찾는 크리에이터들을 위한 플랫폼으로, 상업적 및 개인적 용도로 무료 사용 가능하다.
    • https://www.reshot.com
  9. Life of Pix
  10. Kaboompics
    • 라이프스타일, 인테리어, 패션 등의 고품질 이미지를 무료로 제공하며 상업적 용도로도 사용 가능하다.
    • https://kaboompics.com
  11. Gratisography
    • 독특하고 유쾌한 고해상도 이미지를 제공하며 상업적 용도로도 사용 가능하다.
    • https://gratisography.com
  12. Flickr
    • Creative Commons 라이선스가 적용된 이미지를 찾을 수 있는 대규모 이미지 공유 사이트.
    • https://www.flickr.com (라이선스 확인 필수)
  13. Freepik
    • 벡터 일러스트레이션과 PSD 파일이 강점. 일부 프리미엄 콘텐츠는 유료.
    • https://www.freepik.com

이 사이트들은 저작권이 없는 무료 이미지를 제공하며, 상업적 용도로 사용 가능한 경우가 많다. 다만 각 이미지마다 라이선스 조건이 다를 수 있으므로 사용 전에 라이선스를 확인하는 것이 좋다.

2024년 8월 14일 수요일

마우스 클릭과 더블클릭 구분하여 더블클릭 방지 처리 : JavaScript ES6 React

click 이벤트와 dblclick 이벤트를 사용하고, 클릭을 setTimeout으로 일정 시간 동안 지연시켜 더블클릭인지 단일 클릭인지 확인하는 방법을 사용한다.

JavaScript로 작성한 후 React 버전으로 수정 작성하였다.

모듈 파일 (handlers.js)

setupDblClickHandler 함수는 더블 클릭 시 클릭 타이머를 정리하는 역할만 수행한다.

// handlers.js

const clickTimeouts = new WeakMap();

export function setupClickHandler(element, handleClick) {
    element.addEventListener("click", function(event) {
        clearTimeout(clickTimeouts.get(element)); // 기존 타이머 클리어
        const timeout = setTimeout(function() {
            handleClick(event);
        }, 500); // 500ms 후에 클릭 이벤트로 처리
        clickTimeouts.set(element, timeout); // 새로운 타이머 저장
    });
}

export function setupDblClickHandler(element) {
    element.addEventListener("dblclick", function() {
        clearTimeout(clickTimeouts.get(element)); // 기존 타이머 클리어
    });
}

메인 파일 (main.js)

// main.js

import { setupClickHandler, setupDblClickHandler } from './handlers.js';

document.addEventListener("DOMContentLoaded", function() {
    // 예제 핸들러 함수
    function exampleClickHandler(event) {
        alert(`Single Click Detected on element: ${event.target.id}`);
    }

    // 요소를 동적으로 선택
    var button1 = document.getElementById("myButton1");
    var button2 = document.getElementById("myButton2");

    // 동적 핸들러를 각 요소에 설정
    setupClickHandler(button1, exampleClickHandler);
    setupClickHandler(button2, exampleClickHandler);
    setupDblClickHandler(button1);
    setupDblClickHandler(button2);
});

HTML 파일

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dynamic Element and Handler</title>
</head>
<body>
    <button id="myButton1">Click or Double Click Me</button>
    <button id="myButton2">Another Button</button>

    <script type="module" src="main.js"></script>
</body>
</html>

코드 설명

  1. setupClickHandler: 클릭 이벤트가 발생하면 타이머를 설정하여 단일 클릭 이벤트를 처리한다.
    • 이전 타이머를 정리하고 새로운 타이머를 설정하여 500ms 후에 handleClick 함수를 호출한다.
  2. setupDblClickHandler: 더블 클릭 이벤트가 발생하면 타이머를 정리한다.
    • 클릭 타이머를 정리하여 단일 클릭 이벤트가 호출되지 않도록 한다.
  3. exampleClickHandler: 단일 클릭 이벤트 핸들러로, 클릭된 요소의 ID를 alert 창에 표시한다.
  4. HTML 파일: main.jstype="module"로 포함하여 모듈 시스템을 사용한다.

주의 사항

  • 이 구조에서는 더블 클릭 시 추가 로직을 처리하지 않는다. 만약 더블 클릭에 다른 동작을 추가하고 싶다면, setupDblClickHandler 함수를 다시 확장해야 한다.
  • 최신 브라우저 환경에서 작동하도록 설계되었기 때문에 구형 브라우저에서는 ES6 모듈과 WeakMap 지원을 확인해야 한다.

React에서 사용 가능하도록 수정한 버전 1

React에서 setupClickHandlersetupDblClickHandler 기능을 제공하는 방법으로, 커스텀 훅을 사용하여 단일 클릭과 더블 클릭을 구분하는 로직을 구현할 수 있다. 커스텀 훅은 재사용 가능한 로직을 추상화하고 컴포넌트에서 쉽게 사용할 수 있도록 도와준다.

아래는 useClickHandler라는 커스텀 훅을 작성하고 이를 React 컴포넌트에서 사용하는 예시이다.

1. useClickHandler 커스텀 훅 작성

hooks/useClickHandler.js

단일 클릭과 더블 클릭을 처리할 수 있는 useClickHandler 훅을 작성한다. 이 훅은 clickHandler를 인자로 받아서 클릭 이벤트와 더블 클릭 이벤트를 처리한다.

// hooks/useClickHandler.js

import { useRef, useEffect } from 'react';

export function useClickHandler(clickHandler) {
    const clickTimeoutRef = useRef(null);

    useEffect(() => {
        // 타이머를 클리어하는 함수
        const clearClickTimeout = () => {
            if (clickTimeoutRef.current) {
                clearTimeout(clickTimeoutRef.current);
                clickTimeoutRef.current = null;
            }
        };

        const handleClick = (event) => {
            clearClickTimeout();
            clickTimeoutRef.current = setTimeout(() => {
                clickHandler(event);
            }, 300); // 300ms 후에 클릭 이벤트로 처리
        };

        const handleDblClick = () => {
            clearClickTimeout(); // 더블 클릭 시 타이머 클리어
        };

        return { handleClick, handleDblClick };
    }, [clickHandler]);

    return { handleClick, handleDblClick };
}

2. React 컴포넌트에서 훅 사용

useClickHandler 훅을 사용하여 클릭 이벤트와 더블 클릭 이벤트를 처리하는 예제 컴포넌트를 작성한다.

components/ClickHandlerComponent.js

// components/ClickHandlerComponent.js

import React from 'react';
import { useClickHandler } from '../hooks/useClickHandler';

export function ClickHandlerComponent() {
    const { handleClick, handleDblClick } = useClickHandler((event) => {
        alert(`Single Click Detected on element: ${event.target.id}`);
    });

    return (
        <div>
            <button
                id="myButton1"
                onClick={handleClick}
                onDoubleClick={handleDblClick}
            >
                Click or Double Click Me
            </button>
            <button
                id="myButton2"
                onClick={handleClick}
                onDoubleClick={handleDblClick}
            >
                Another Button
            </button>
        </div>
    );
}

3. 앱 컴포넌트

React 앱에서 ClickHandlerComponent를 사용.

App.js

// App.js

import React from 'react';
import { ClickHandlerComponent } from './components/ClickHandlerComponent';

function App() {
    return (
        <div className="App">
            <h1>React Click and Double Click Handler</h1>
            <ClickHandlerComponent />
        </div>
    );
}

export default App;

4. 프로젝트 구조

/project-root
    ├── /src
    │    ├── /components
    │    │    └── ClickHandlerComponent.js
    │    ├── /hooks
    │    │    └── useClickHandler.js
    │    └── App.js
    ├── index.html
    └── index.js

코드 설명

  1. useClickHandler:
    • clickTimeoutRef를 사용하여 클릭 타이머를 관리한다.
    • handleClickhandleDblClick 함수는 각각 클릭과 더블 클릭 이벤트를 처리한다.
    • clearClickTimeout 함수는 타이머를 클리어하여 단일 클릭과 더블 클릭을 구분한다.
    • useEffectclickHandler가 변경될 때마다 새로 등록된 핸들러를 업데이트 한다.
  2. ClickHandlerComponent 컴포넌트:
    • useClickHandler 훅을 사용하여 클릭 및 더블 클릭 핸들러를 생성한다.
    • 각 버튼에 handleClickhandleDblClick을 이벤트 핸들러로 설정한다.
  3. App 컴포넌트:
    • ClickHandlerComponent를 사용하여 버튼 클릭 이벤트를 처리합니다.

추가 사항

  • 의존성 관리: useClickHandler 훅은 clickHandler가 변경될 때마다 타이머를 관리하도록 의존성을 설정한다.
  • 타이머 관리: 타이머는 useRef로 관리되어 컴포넌트가 리렌더링되더라도 상태가 유지된다.

React에서 사용 가능하도록 수정한 버전 2

React에서 이벤트 핸들러를 설정하고 단일 클릭과 더블 클릭을 구분하기 위해, Hook을 사용하여 상태를 관리할 수 있다. 아래 예시에서는 useEffectuseRef를 사용하여 DOM 이벤트를 설정하고, useState를 사용하여 클릭 타이머 상태를 관리한다.

1. Custom Hook (useClickHandler.js)

먼저, 클릭과 더블 클릭 핸들러를 설정하는 Custom Hook을 정의한다.

// useClickHandler.js

import { useEffect, useRef } from 'react';

const useClickHandler = (elementRef, handleClick) => {
  const clickTimeoutRef = useRef(null);

  useEffect(() => {
    const element = elementRef.current;

    const handleSingleClick = (event) => {
      clearTimeout(clickTimeoutRef.current);
      clickTimeoutRef.current = setTimeout(() => {
        handleClick(event);
      }, 300); // 300ms 후에 클릭 이벤트로 처리
    };

    const handleDoubleClick = (event) => {
      clearTimeout(clickTimeoutRef.current); // 더블 클릭 시 타이머 클리어
    };

    if (element) {
      element.addEventListener('click', handleSingleClick);
      element.addEventListener('dblclick', handleDoubleClick);
    }

    // Cleanup 이벤트 리스너
    return () => {
      if (element) {
        element.removeEventListener('click', handleSingleClick);
        element.removeEventListener('dblclick', handleDoubleClick);
      }
    };
  }, [elementRef, handleClick]);
};

export default useClickHandler;

2. 컴포넌트 파일 (MyComponent.js)

Custom Hook을 사용하여 단일 클릭 이벤트 핸들러를 설정하는 컴포넌트를 작성한다.

// MyComponent.js

import React, { useRef } from 'react';
import useClickHandler from './useClickHandler';

const MyComponent = () => {
  const buttonRef1 = useRef(null);
  const buttonRef2 = useRef(null);

  const handleClick = (event) => {
    alert(`Single Click Detected on element: ${event.target.id}`);
  };

  useClickHandler(buttonRef1, handleClick);
  useClickHandler(buttonRef2, handleClick);

  return (
    <div>
      <button id="myButton1" ref={buttonRef1}>Click or Double Click Me</button>
      <button id="myButton2" ref={buttonRef2}>Another Button</button>
    </div>
  );
};

export default MyComponent;

코드 설명

  1. Custom Hook (useClickHandler.js):
    • useClickHandlerelementRefhandleClick을 인자로 받는다.
    • clickTimeoutRefuseRef를 사용하여 클릭 타이머를 저장한다.
    • useEffect를 사용하여 컴포넌트가 마운트될 때 이벤트 리스너를 설정하고 언마운트될 때 정리한다.
    • 단일 클릭 이벤트를 처리하는 handleSingleClick과 더블 클릭 시 타이머를 정리하는 handleDoubleClick을 정의한다.
  2. 컴포넌트 파일 (MyComponent.js):
    • buttonRef1buttonRef2는 각각 두 버튼의 참조를 저장한다.
    • handleClick은 단일 클릭 이벤트 핸들러로, 클릭된 요소의 idalert로 표시한다.
    • useClickHandler를 사용하여 각 버튼에 대해 클릭 핸들러를 설정한다.
    • 버튼 요소에 ref 속성을 설정하여 DOM 요소를 참조할 수 있게 한다.

React 애플리케이션 구조

/src
    /components
        MyComponent.js
    /hooks
        useClickHandler.js
    index.js

React 애플리케이션 설정

index.js

애플리케이션의 진입점으로, MyComponent를 렌더링.

import React from 'react';
import ReactDOM from 'react-dom';
import MyComponent from './components/MyComponent';

ReactDOM.render(
  <React.StrictMode>
    <MyComponent />
  </React.StrictMode>,
  document.getElementById('root')
);

kubectl 치트 시트 : config apply get rollout autoscale scale delete logs run exec cp

 

Kubectl 자동 완성

BASH

source <(kubectl completion bash) # bash-completion 패키지를 먼저 설치한 후, bash의 자동 완성을 현재 셸에 설정한다
echo "source <(kubectl completion bash)" >> ~/.bashrc # 자동 완성을 bash 셸에 영구적으로 추가한다

또한, kubectl의 의미로 사용되는 약칭을 사용할 수 있다.

alias k=kubectl
complete -o default -F __start_kubectl k

ZSH

source <(kubectl completion zsh)  # 현재 셸에 zsh의 자동 완성 설정
echo '[[ $commands[kubectl] ]] && source <(kubectl completion zsh)' >> ~/.zshrc # 자동 완성을 zsh 셸에 영구적으로 추가한다.

Kubectl 컨텍스트와 설정

kubectl이 통신하고 설정 정보를 수정하는 쿠버네티스 클러스터를 지정한다. 설정 파일에 대한 자세한 정보는 kubeconfig를 이용한 클러스터 간 인증 문서를 참고한다.

# 동시에 여러 kubeconfig 파일을 사용하고 병합된 구성을 확인한다
KUBECONFIG=~/.kube/config:~/.kube/kubconfig2

kubectl config view

# e2e 사용자의 암호를 확인한다
kubectl config view -o jsonpath='{.users[?(@.name == "e2e")].user.password}'

kubectl config view -o jsonpath='{.users[].name}'     # 첫 번째 사용자 출력
kubectl config view -o jsonpath='{.users[*].name}'    # 사용자 리스트 조회
kubectl config get-contexts                           # 컨텍스트 리스트 출력
kubectl config current-context                        # 현재 컨텍스트 출력
kubectl config use-context my-cluster-name            # my-cluster-name를 기본 컨텍스트로 설정

kubectl config set-cluster my-cluster-name            # kubeconfig에 클러스터 엔트리를 설정

# kubeconfig에 이 클라이언트가 발생시킨 요청에 사용할 프록시 서버의 URL을 구성한다.
kubectl config set-cluster my-cluster-name --proxy-url=my-proxy-url

# 기본 인증을 지원하는 새로운 사용자를 kubeconf에 추가한다
kubectl config set-credentials kubeuser/foo.kubernetes.com --username=kubeuser --password=kubepassword

# 해당 컨텍스트에서 모든 후속 kubectl 커맨드에 대한 네임스페이스를 영구적으로 저장한다
kubectl config set-context --current --namespace=ggckad-s2

# 특정 사용자와 네임스페이스를 사용하는 컨텍스트 설정
kubectl config set-context gce --user=cluster-admin --namespace=foo \\
  && kubectl config use-context gce

kubectl config unset users.foo                       # foo 사용자 삭제

# 컨텍스트/네임스페이스를 설정/조회하는 단축 명령 (bash 및 bash 호환 셸에서만 동작함, 네임스페이스 설정을 위해 kn 을 사용하기 전에 현재 컨텍스트가 설정되어야 함)
alias kx='f() { [ "$1" ] && kubectl config use-context $1 || kubectl config current-context ; } ; f'
alias kn='f() { [ "$1" ] && kubectl config set-context --current --namespace $1 || kubectl config view --minify | grep namespace | cut -d" " -f6 ; } ; f'

Kubectl apply

apply는 쿠버네티스 리소스를 정의하는 파일을 통해 애플리케이션을 관리한다. kubectl apply를 실행하여 클러스터에 리소스를 생성하고 업데이트한다. 이것은 프로덕션 환경에서 쿠버네티스 애플리케이션을 관리할 때 권장된다. Kubectl Book을 참고한다.

오브젝트 생성

쿠버네티스 매니페스트는 JSON이나 YAML로 정의된다. 파일 확장자는 .yaml , .yml.json 이 사용된다.

kubectl apply -f ./my-manifest.yaml            # 리소스(들) 생성
kubectl apply -f ./my1.yaml -f ./my2.yaml      # 여러 파일로 부터 생성
kubectl apply -f ./dir                         # dir 내 모든 매니페스트 파일에서 리소스(들) 생성
kubectl apply -f <https://git.io/vPieo>          # url로부터 리소스(들) 생성
kubectl create deployment nginx --image=nginx  # nginx 단일 인스턴스를 시작

# "Hello World"를 출력하는 잡(Job) 생성
kubectl create job hello --image=busybox:1.28 -- echo "Hello World"

# 매분마다 "Hello World"를 출력하는 크론잡(CronJob) 생성
kubectl create cronjob hello --image=busybox:1.28   --schedule="*/1 * * * *" -- echo "Hello World"

kubectl explain pods                           # 파드 매니페스트 문서를 조회

# stdin으로 다수의 YAML 오브젝트 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: busybox-sleep
spec:
  containers:
  - name: busybox
    image: busybox:1.28
    args:
    - sleep
    - "1000000"
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox-sleep-less
spec:
  containers:
  - name: busybox
    image: busybox:1.28
    args:
    - sleep
    - "1000"
EOF

# 여러 개의 키로 시크릿 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  password: $(echo -n "s33msi4" | base64 -w0)
  username: $(echo -n "jane" | base64 -w0)
EOF

리소스 조회 및 찾기

kubectl get services                          # 네임스페이스 내 모든 서비스의 목록 조회
kubectl get pods --all-namespaces             # 모든 네임스페이스 내 모든 파드의 목록 조회
kubectl get pods -o wide                      # 해당하는 네임스페이스 내 모든 파드의 상세 목록 조회
kubectl get deployment my-dep                 # 특정 디플로이먼트의 목록 조회
kubectl get pods                              # 네임스페이스 내 모든 파드의 목록 조회
kubectl get pod my-pod -o yaml                # 파드의 YAML 조회

# 상세 출력을 위한 Describe 커맨드
kubectl describe nodes my-node
kubectl describe pods my-pod

# Name으로 정렬된 서비스의 목록 조회
kubectl get services --sort-by=.metadata.name

# 재시작 횟수로 정렬된 파드의 목록 조회
kubectl get pods --sort-by='.status.containerStatuses[0].restartCount'

# PersistentVolumes을 용량별로 정렬해서 조회
kubectl get pv --sort-by=.spec.capacity.storage

# app=cassandra 레이블을 가진 모든 파드의 레이블 버전 조회
kubectl get pods --selector=app=cassandra -o \\
  jsonpath='{.items[*].metadata.labels.version}'

# 예를 들어 'ca.crt'와 같이 점이 있는 키값을 검색한다
kubectl get configmap myconfig \\
  -o jsonpath='{.data.ca\\.crt}'

# 밑줄(`_`) 대신 대시(`-`)를 사용하여 base64 인코딩된 값을 조회
kubectl get secret my-secret --template='{{index .data "key-name-with-dashes"}}'

# 모든 워커 노드 조회 (셀렉터를 사용하여 'node-role.kubernetes.io/control-plane'
# 으로 명명된 라벨의 결과를 제외)
kubectl get node --selector='!node-role.kubernetes.io/control-plane'

# 네임스페이스의 모든 실행 중인 파드를 조회
kubectl get pods --field-selector=status.phase=Running

# 모든 노드의 외부IP를 조회
kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="ExternalIP")].address}'

# 특정 RC에 속해있는 파드 이름의 목록 조회
# "jq" 커맨드는 jsonpath를 사용하는 매우 복잡한 변환에 유용하다. <https://stedolan.github.io/jq/> 에서 확인할 수 있다.
sel=${$(kubectl get rc my-rc --output=json | jq -j '.spec.selector | to_entries | .[] | "\\(.key)=\\(.value),"')%?}
echo $(kubectl get pods --selector=$sel --output=jsonpath={.items..metadata.name})

# 모든 파드(또는 레이블을 지원하는 다른 쿠버네티스 오브젝트)의 레이블 조회
kubectl get pods --show-labels

# 어떤 노드가 준비됐는지 확인
JSONPATH='{range .items[*]}{@.metadata.name}:{range @.status.conditions[*]}{@.type}={@.status};{end}{end}' \\
 && kubectl get nodes -o jsonpath="$JSONPATH" | grep "Ready=True"

# 외부 도구 없이 디코딩된 시크릿 출력
kubectl get secret my-secret -o go-template='{{range $k,$v := .data}}{{"### "}}{{$k}}{{"\\n"}}{{$v|base64decode}}{{"\\n\\n"}}{{end}}'

# 파드에 의해 현재 사용되고 있는 모든 시크릿 목록 조회
kubectl get pods -o json | jq '.items[].spec.containers[].env[]?.valueFrom.secretKeyRef.name' | grep -v null | sort | uniq

# 모든 파드의 초기화 컨테이너(initContainer)의 컨테이너ID 목록 조회
# 초기화 컨테이너(initContainer)를 제거하지 않고 정지된 모든 컨테이너를 정리할 때 유용하다.
kubectl get pods --all-namespaces -o jsonpath='{range .items[*].status.initContainerStatuses[*]}{.containerID}{"\\n"}{end}' | cut -d/ -f3

# 타임스탬프로 정렬된 이벤트 목록 조회
kubectl get events --sort-by=.metadata.creationTimestamp

# 모든 Warning 타입 이벤트 조회
kubectl events --types=Warning

# 매니페스트가 적용된 경우 클러스터의 현재 상태와 클러스터의 상태를 비교한다.
kubectl diff -f ./my-manifest.yaml

# 노드에 대해 반환된 모든 키의 마침표로 구분된 트리를 생성한다.
# 복잡한 중첩 JSON 구조 내에서 키를 찾을 때 유용하다.
kubectl get nodes -o json | jq -c 'paths|join(".")'

# 파드 등에 대해 반환된 모든 키의 마침표로 구분된 트리를 생성한다.
kubectl get pods -o json | jq -c 'paths|join(".")'

# 모든 파드에 대해 ENV를 생성한다(각 파드에 기본 컨테이너가 있고, 기본 네임스페이스가 있고, `env` 명령어가 동작한다고 가정).
# `env` 뿐만 아니라 다른 지원되는 명령어를 모든 파드에 실행할 때에도 참고할 수 있다.
for pod in $(kubectl get po --output=jsonpath={.items..metadata.name}); do echo $pod && kubectl exec -it $pod -- env; done

# 디플로이먼트의 status 서브리소스를 조회한다.
kubectl get deployment nginx-deployment --subresource=status

리소스 업데이트

kubectl set image deployment/frontend www=image:v2               # "frontend" 디플로이먼트의 "www" 컨테이너 이미지를 업데이트하는 롤링 업데이트
kubectl rollout history deployment/frontend                      # 현 리비전을 포함한 디플로이먼트의 이력을 체크
kubectl rollout undo deployment/frontend                         # 이전 디플로이먼트로 롤백
kubectl rollout undo deployment/frontend --to-revision=2         # 특정 리비전으로 롤백
kubectl rollout status -w deployment/frontend                    # 완료될 때까지 "frontend" 디플로이먼트의 롤링 업데이트 상태를 감시
kubectl rollout restart deployment/frontend                      # "frontend" 디플로이먼트의 롤링 재시작

cat pod.json | kubectl replace -f -                              # stdin으로 전달된 JSON을 기반으로 파드 교체

# 리소스를 강제 교체, 삭제 후 재생성함. 이것은 서비스를 중단시킴.
kubectl replace --force -f ./pod.json

# 복제된 nginx를 위한 서비스를 생성한다. 80 포트로 서비스하고, 컨테이너는 8000 포트로 연결한다.
kubectl expose rc nginx --port=80 --target-port=8000

# 단일-컨테이너 파드의 이미지 버전(태그)을 v4로 업데이트
kubectl get pod mypod -o yaml | sed 's/\\(image: myimage\\):.*$/\\1:v4/' | kubectl replace -f -

kubectl label pods my-pod new-label=awesome                      # 레이블 추가
kubectl label pods my-pod new-label-                             # 레이블 제거
kubectl annotate pods my-pod icon-url=http://goo.gl/XXBTWq       # 어노테이션 추가
kubectl autoscale deployment foo --min=2 --max=10                # 디플로이먼트 "foo" 오토스케일

리소스 패치

# 노드를 부분적으로 업데이트
kubectl patch node k8s-node-1 -p '{"spec":{"unschedulable":true}}'

# 컨테이너의 이미지를 업데이트. 병합(merge) 키이므로, spec.containers[*].name이 필요
kubectl patch pod valid-pod -p '{"spec":{"containers":[{"name":"kubernetes-serve-hostname","image":"new image"}]}}'

# 위치 배열을 이용한 json 패치를 사용하여, 컨테이너의 이미지를 업데이트
kubectl patch pod valid-pod --type='json' -p='[{"op": "replace", "path": "/spec/containers/0/image", "value":"new image"}]'

# 위치 배열을 이용한 json 패치를 사용하여 livenessProbe 디플로이먼트 비활성화
kubectl patch deployment valid-deployment  --type json   -p='[{"op": "remove", "path": "/spec/template/spec/containers/0/livenessProbe"}]'

# 위치 배열에 새 요소 추가
kubectl patch sa default --type='json' -p='[{"op": "add", "path": "/secrets/1", "value": {"name": "whatever" } }]'

# 디플로이먼트의 scale 서브리소스를 패치하여 레플리카 수 업데이트
kubectl patch deployment nginx-deployment --subresource='scale' --type='merge' -p '{"spec":{"replicas":2}}'

리소스 편집

선호하는 편집기로 모든 API 리소스를 편집할 수 있다.

kubectl edit svc/docker-registry                      # docker-registry라는 서비스 편집
KUBE_EDITOR="nano" kubectl edit svc/docker-registry   # 다른 편집기 사용

리소스 스케일링

kubectl scale --replicas=3 rs/foo                                 # 'foo'라는 레플리카셋을 3으로 스케일
kubectl scale --replicas=3 -f foo.yaml                            # "foo.yaml"에 지정된 리소스의 크기를 3으로 스케일
kubectl scale --current-replicas=2 --replicas=3 deployment/mysql  # mysql이라는 디플로이먼트의 현재 크기가 2인 경우, mysql을 3으로 스케일
kubectl scale --replicas=5 rc/foo rc/bar rc/baz                   # 여러 개의 레플리케이션 컨트롤러 스케일

리소스 삭제

kubectl delete -f ./pod.json                                      # pod.json에 지정된 유형 및 이름을 사용하여 파드 삭제
kubectl delete pod unwanted --now                                 # 유예 시간 없이 즉시 파드 삭제
kubectl delete pod,service baz foo                                # "baz", "foo"와 동일한 이름을 가진 파드와 서비스 삭제
kubectl delete pods,services -l name=myLabel                      # name=myLabel 라벨을 가진 파드와 서비스 삭제
kubectl -n my-ns delete pod,svc --all                             # my-ns 네임스페이스 내 모든 파드와 서비스 삭제
# awk pattern1 또는 pattern2에 매칭되는 모든 파드 삭제
kubectl get pods  -n mynamespace --no-headers=true | awk '/pattern1|pattern2/{print $1}' | xargs  kubectl delete -n mynamespace pod

실행 중인 파드와 상호 작용

kubectl logs my-pod                                 # 파드 로그 덤프 (stdout)
kubectl logs -l name=myLabel                        # name이 myLabel인 파드 로그 덤프 (stdout)
kubectl logs my-pod --previous                      # 컨테이너의 이전 인스턴스 생성에 대한 파드 로그 덤프 (stdout)
kubectl logs my-pod -c my-container                 # 파드 로그 덤프 (stdout, 멀티-컨테이너 경우)
kubectl logs -l name=myLabel -c my-container        # name이 myLabel인 파드 로그 덤프 (stdout)
kubectl logs my-pod -c my-container --previous      # 컨테이너의 이전 인스턴스 생성에 대한 파드 로그 덤프 (stdout, 멀티-컨테이너 경우)
kubectl logs -f my-pod                              # 실시간 스트림 파드 로그(stdout)
kubectl logs -f my-pod -c my-container              # 실시간 스트림 파드 로그(stdout, 멀티-컨테이너 경우)
kubectl logs -f -l name=myLabel --all-containers    # name이 myLabel인 모든 파드의 로그 스트리밍 (stdout)
kubectl run -i --tty busybox --image=busybox:1.28 -- sh  # 대화형 셸로 파드를 실행
kubectl run nginx --image=nginx -n mynamespace      # mynamespace 네임스페이스에서 nginx 파드 1개 실행
kubectl run nginx --image=nginx --dry-run=client -o yaml > pod.yaml
                                                    # nginx 파드에 대한 spec을 생성하고, pod.yaml이라는 파일에 해당 내용을 기록한다.
kubectl attach my-pod -i                            # 실행 중인 컨테이너에 연결
kubectl port-forward my-pod 5000:6000               # 로컬 머신의 5000번 포트를 리스닝하고, my-pod의 6000번 포트로 전달
kubectl exec my-pod -- ls /                         # 기존 파드에서 명령 실행(한 개 컨테이너 경우)
kubectl exec --stdin --tty my-pod -- /bin/sh        # 실행 중인 파드로 대화형 셸 액세스(1 컨테이너 경우)
kubectl exec my-pod -c my-container -- ls /         # 기존 파드에서 명령 실행(멀티-컨테이너 경우)
kubectl top pod POD_NAME --containers               # 특정 파드와 해당 컨테이너에 대한 메트릭 표시
kubectl top pod POD_NAME --sort-by=cpu              # 지정한 파드에 대한 메트릭을 표시하고 'cpu' 또는 'memory'별로 정렬

컨테이너로/컨테이너에서 파일과 디렉터리 복사

kubectl cp /tmp/foo_dir my-pod:/tmp/bar_dir            # 로컬 디렉토리 /tmp/foo_dir 를 현재 네임스페이스의 my-pod 파드 안의 /tmp/bar_dir 로 복사
kubectl cp /tmp/foo my-pod:/tmp/bar -c my-container    # 로컬 파일 /tmp/foo 를 my-pod 파드의 my-container 컨테이너 안의 /tmp/bar 로 복사
kubectl cp /tmp/foo my-namespace/my-pod:/tmp/bar       # 로컬 파일 /tmp/foo 를 my-namespace 네임스페이스의 my-pod 파드 안의 /tmp/bar 로 복사
kubectl cp my-namespace/my-pod:/tmp/foo /tmp/bar       # my-namespace 네임스페이스의 my-pod 파드 안의 파일 /tmp/foo 를 로컬의 /tmp/bar 로 복사

참고:kubectl cp 명령을 사용하려면 컨테이너 이미지에 'tar' 바이너리가 포함되어 있어야 한다. 'tar'가 없으면, kubectl cp는 실패할 것이다. 심볼릭 링크, 와일드카드 확장, 파일 모드 보존과 같은 고급 사용 사례에 대해서는 kubectl exec 를 고려해 볼 수 있다.

tar cf - /tmp/foo | kubectl exec -i -n my-namespace my-pod -- tar xf - -C /tmp/bar           # 로컬 파일 /tmp/foo 를 my-namespace 네임스페이스의 my-pod 파드 안의 /tmp/bar 로 복사
kubectl exec -n my-namespace my-pod -- tar cf - /tmp/foo | tar xf - -C /tmp/bar    # my-namespace 네임스페이스의 my-pod 파드 안의 파일 /tmp/foo 를 로컬의 /tmp/bar 로 복사

디플로이먼트, 서비스와 상호 작용

kubectl logs deploy/my-deployment                         # 디플로이먼트에 대한 파드 로그 덤프 (단일-컨테이너 경우)
kubectl logs deploy/my-deployment -c my-container         # 디플로이먼트에 대한 파드 로그 덤프 (멀티-컨테이너 경우)

kubectl port-forward svc/my-service 5000                  # 로컬 머신의 5000번 포트를 리스닝하고, my-service의 동일한(5000번) 포트로 전달
kubectl port-forward svc/my-service 5000:my-service-port  # 로컬 머신의 5000번 포트를 리스닝하고, my-service의 <my-service-port> 라는 이름을 가진 포트로 전달

kubectl port-forward deploy/my-deployment 5000:6000       # 로컬 머신의 5000번 포트를 리스닝하고, <my-deployment> 에 의해 생성된 파드의 6000번 포트로 전달
kubectl exec deploy/my-deployment -- ls                   # <my-deployment> 에 의해 생성된 첫번째 파드의 첫번째 컨테이너에 명령어 실행 (단일- 또는 다중-컨테이너 경우)

노드, 클러스터와 상호 작용

kubectl cordon my-node                                                # my-node를 스케줄링할 수 없도록 표기
kubectl drain my-node                                                 # 유지 보수를 위해서 my-node를 준비 상태로 비움
kubectl uncordon my-node                                              # my-node를 스케줄링할 수 있도록 표기
kubectl top node my-node                                              # 주어진 노드에 대한 메트릭 표시
kubectl cluster-info                                                  # 마스터 및 서비스의 주소 표시
kubectl cluster-info dump                                             # 현재 클러스터 상태를 stdout으로 덤프
kubectl cluster-info dump --output-directory=/path/to/cluster-state   # 현재 클러스터 상태를 /path/to/cluster-state으로 덤프

# 현재 노드에 존재하고 있는 테인트(taint)들을 확인
kubectl get nodes -o='custom-columns=NodeName:.metadata.name,TaintKey:.spec.taints[*].key,TaintValue:.spec.taints[*].value,TaintEffect:.spec.taints[*].effect'

# 이미 존재하고 있는 key와 effect를 갖는 테인트의 경우, 지정한 값으로 대체
kubectl taint nodes foo dedicated=special-user:NoSchedule

리소스 타입

단축명, API 그룹과 함께 지원되는 모든 리소스 유형들, 그것들의 네임스페이스와 종류(Kind)를 나열:

kubectl api-resources

API 리소스를 탐색하기 위한 다른 작업:

kubectl api-resources --namespaced=true      # 네임스페이스를 가지는 모든 리소스
kubectl api-resources --namespaced=false     # 네임스페이스를 가지지 않는 모든 리소스
kubectl api-resources -o name                # 모든 리소스의 단순한 (리소스 이름만) 출력
kubectl api-resources -o wide                # 모든 리소스의 확장된 ("wide"로 알려진) 출력
kubectl api-resources --verbs=list,get       # "list"와 "get"의 요청 동사를 지원하는 모든 리소스 출력
kubectl api-resources --api-group=extensions # "extensions" API 그룹의 모든 리소스

출력 형식 지정

특정 형식으로 터미널 창에 세부 사항을 출력하려면, 지원되는 kubectl 명령에 -o (또는 --output) 플래그를 추가한다.

출력 형식 세부 사항
-o=custom-columns=<명세> 쉼표로 구분된 사용자 정의 열 목록을 사용하여 테이블 출력
-o=custom-columns-file=<파일명> <파일명>파일에서 사용자 정의 열 템플릿을 사용하여 테이블 출력
-o=json JSON 형식의 API 오브젝트 출력
-o=jsonpath=<템플릿> https://kubernetes.io/ko/docs/reference/kubectl/jsonpath 표현식에 정의된 필드 출력
-o=jsonpath-file=<파일명> <파일명> 파일에서 https://kubernetes.io/ko/docs/reference/kubectl/jsonpath 표현식에 정의된 필드 출력
-o=name 리소스 명만 출력하고 그 외에는 출력하지 않음
-o=wide 추가 정보가 포함된 일반-텍스트 형식으로 출력하고, 파드의 경우 노드 명이 포함
-o=yaml YAML 형식의 API 오브젝트 출력
  • o=custom-columns 의 사용 예시:
# 클러스터에서 실행 중인 모든 이미지
kubectl get pods -A -o=custom-columns='DATA:spec.containers[*].image'

# `default` 네임스페이스의 모든 이미지를 파드별로 그룹지어 출력
kubectl get pods --namespace default --output=custom-columns="NAME:.metadata.name,IMAGE:.spec.containers[*].image"

 # "registry.k8s.io/coredns:1.6.2" 를 제외한 모든 이미지
kubectl get pods -A -o=custom-columns='DATA:spec.containers[?(@.image!="registry.k8s.io/coredns:1.6.2")].image'

# 이름에 관계없이 메타데이터 아래의 모든 필드
kubectl get pods -A -o=custom-columns='DATA:metadata.*'

더 많은 예제는 kubectl 참조 문서를 참고한다.

Kubectl 출력 로그 상세 레벨(verbosity)과 디버깅

Kubectl 로그 상세 레벨(verbosity)은 -v 또는--v 플래그와 로그 레벨을 나타내는 정수로 제어된다. 일반적인 쿠버네티스 로깅 규칙과 관련 로그 레벨이 여기에 설명되어 있다.

로그 레벨 세부 사항
--v=0 일반적으로 클러스터 운영자(operator)에게 항상 보여지게 하기에는 유용함.
--v=1 자세한 정보를 원하지 않는 경우, 적절한 기본 로그 수준.
--v=2 서비스와 시스템의 중요한 변화와 관련이있는 중요한 로그 메시지에 대한 유용한 정상 상태 정보. 이는 대부분의 시스템에서 권장되는 기본 로그 수준이다.
--v=3 변경 사항에 대한 확장 정보.
--v=4 디버그 수준 상세화.
--v=5 트레이스 수준 상세화.
--v=6 요청한 리소스를 표시.
--v=7 HTTP 요청 헤더를 표시.
--v=8 HTTP 요청 내용을 표시.
--v=9 내용을 잘라 내지 않고 HTTP 요청 내용을 표시.

🔗 https://kubernetes.io/ko/docs/reference/kubectl/cheatsheet/

2024년 8월 13일 화요일

docker-compose : 특정 서비스 최신 이미지 강제로 Pull 한 후 다시 시작

docker-compose 명령을 사용하여 특정 서비스의 최신 이미지를 강제로 pull 한 후 재실행하려면 docker-compose pull 명령과 docker-compose up 명령을 결합하여 사용할 수 있다.

단계별 설명

  1. ECR 로그인

    aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <aws_account_id>.dkr.ecr.<region>.amazonaws.com
    
  2. 이미지 Pull

    docker-compose pull 명령을 사용하여 최신 이미지를 pull 한다. 특정 서비스만 pull 할 수 있다:

    docker-compose pull app
    
  3. 서비스 재실행

    docker-compose up 명령을 사용하여 특정 서비스를 재실행:

    docker-compose up -d app
    

두 명령을 한 번에 실행:

docker-compose pull app && docker-compose up -d app

자동화를 위한 스크립트

위의 명령을 쉘 스크립트로 만들어 사용하면 더 편리하게 사용할 수 있다.

#!/bin/bash

# ECR 로그인
aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <aws_account_id>.dkr.ecr.<region>.amazonaws.com

# 최신 이미지 pull
docker-compose pull app

# 서비스 재실행
docker-compose up -d app

2024년 8월 12일 월요일

맥북에서 폴더를 EC2 서버에 업로드 : scp, ssh

 

로컬 맥북에서 "my-project" 프로젝트 폴더를 EC2 서버에 업로드

SCP를 이용한 파일 업로드

  1. 로컬 맥북 터미널을 열고 scp 명령어를 사용하여 프로젝트 폴더를 EC2 서버로 업로드한다.

    scp -i <your-key-pair.pem> -r /path/my-project ec2-user@your-ec2-instance-public-dns:/home/ec2-user/
    

파일 업로드 후 확인

  1. SSH로 EC2 서버에 접속하여 파일이 정상적으로 업로드되었는지 확인한다.

    ssh -i <your-key-pair.pem> ec2-user@your-ec2-instance-public-dns
    cd /home/ec2-user/
    ls
    

EC2 서버 Docker 및 Docker Compose 설치, 삭제 후 재설치

 Amazon Linux 2023 EC2 서버에 도커를 설치하고 자동 재실행 설정하는 절차는 다음과 같다.

1. Amazon Linux 2023 EC2 서버에 도커 설치 및 서비스로 자동 재실행 설정

도커 설치

  1. EC2 인스턴스에 SSH 접속한다.

    ssh -i <your-key-pair.pem> ec2-user@your-ec2-instance-public-dns
    
  2. 도커를 설치한다.

    sudo yum update -y
    sudo yum install docker -y
    
  3. 도커 서비스를 시작하고 부팅 시 자동으로 시작되도록 설정한다.

    sudo systemctl start docker
    sudo systemctl enable docker
    
  4. 도커가 정상적으로 설치되었는지 확인한다.

    docker --version
    

도커 자동 재실행 설정

  1. 도커 서비스가 재부팅 시 자동으로 재실행되도록 설정한다.

    sudo systemctl enable docker
    
  2. 컨테이너가 재부팅 시 자동 재시작되도록 Dockerfile 또는 docker run 명령어에 --restart 옵션을 추가한다.

    docker run -d --restart unless-stopped <image-name>
    

2. Docker Compose 설치

Docker Comopose 설치

  1. Docker Compose 바이너리를 다운로드한다.

    sudo curl -SL "<https://github.com/docker/compose/releases/latest/download/docker-compose-linux-$>(uname -m)" -o /usr/local/bin/docker-compose
    
  2. 바이너리에 실행 권한을 부여한다.

    sudo chmod +x /usr/local/bin/docker-compose
    
  3. 심볼릭 링크를 생성하여 Docker Compose 명령어를 사용할 수 있도록 한다.

    sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
    
  4. Docker Compose가 정상적으로 설치되었는지 확인한다.

    docker-compose --version
    

Docker Compose 서비스 자동 재시작 설정

  1. docker-compose.yml 파일에 restart 정책을 추가하여 컨테이너가 자동으로 재시작되도록 설정한다. 설정 샘플:

    version: '3'
    services:
      web:
        image: nginx
        ports:
          - "80:80"
        restart: unless-stopped
    
  2. Docker-compose 실행

    docker-compose up -d
    

3. Docker 재설치

docker.service가 의존하는 다른 서비스가 실패하거나 알 수 없는 이유로 Docker 서비스를 시작할 수 없는 경우, Docker를 제거하고 다시 설치하는 것도 하나의 방법이다.

  1. Docker 제거

    sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine
    
  2. 위의 도커 설치 참고하여 재설치

<aside> 💡 의존성 확인 및 문제 해결

docker.service의 의존성을 확인하여 관련된 서비스들이 모두 제대로 작동하는지 확인한다.

sudo systemctl list-dependencies docker.service
  • 원인 확인

journalctl -xe 명령어를 사용하여 자세한 오류 메시지를 확인한다.

sudo journalctl -xe

이 명령어는 최근 시스템 로그를 표시하여 Docker 서비스가 왜 실패했는지에 대한 더 구체적인 정보를 제공한다.

  • Docker 서비스의 상태를 확인한다.
sudo systemctl status docker
  • Docker가 의존하는 서비스 중 주로 containerd 서비스가 문제일 수 있다.

containerd 서비스 상태 확인 및 재시작

sudo systemctl status containerd
sudo systemctl restart containerd

이후 Docker 서비스를 다시 시작해본다.

sudo systemctl start docker

</aside>

4. Docker Compose 삭제

현재 설치된 방식에 따라 삭제 방법이 다를 수 있으니, 해당하는 방법을 따라 진행하면 된다.

  1. Docker Compose 바이너리 삭제 (직접 다운로드한 경우)

    만약 Docker Compose를 직접 바이너리 파일로 설치했다면, 아래 명령어를 통해 파일을 삭제할 수 있다.

    sudo rm /usr/local/bin/docker-compose
    

    또는 Docker Compose가 다른 경로에 설치되어 있는 경우, 설치된 경로에서 파일을 삭제하면 된다.

    sudo rm /usr/bin/docker-compose
    
  2. 패키지 관리자를 통한 삭제 (패키지 관리자로 설치한 경우)

    만약 Docker Compose를 패키지 관리자를 통해 설치한 경우, 해당 패키지를 제거하면 된다.

    예를 들어, pip을 사용해 설치한 경우:

    pip uninstall docker-compose
    
  3. 설치된 위치 확인

    설치된 위치를 모르는 경우, 다음 명령어로 Docker Compose의 설치 경로를 확인한 후 해당 파일을 삭제한다.

    which docker-compose
    

    출력된 경로를 확인하고 해당 파일을 삭제하면 된다.

    sudo rm /경로/도커-compose
    

요약

  • 직접 설치한 경우: sudo rm /usr/local/bin/docker-compose 또는 해당 경로에서 파일 삭제
  • 패키지 관리자로 설치한 경우: pip uninstall docker-compose
  • 설치된 위치를 모르는 경우: which docker-compose로 경로 확인 후 삭제

2024년 7월 24일 수요일

쿠버네티스 대시보드 사용하기: user, role, kubectl -n kubernetes-dashboard create token, kubectl proxy

 

대시보드 UI 배포

대시보드 UI는 기본으로 배포되지 않는다. 배포하려면 다음 커맨드를 실행한다.

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.6.1/aio/deploy/recommended.yaml

사용자 만들기

dashboard-adminuser.yaml 파일 생성

apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kubernetes-dashboard

아래 명령 실행

kubectl apply -f dashboard-adminuser.yaml

>> serviceaccount/admin-user created

롤바인딩

dashboard-adminuser-role.yaml 파일 생성

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kubernetes-dashboard

아래 명령 실행

kubectl apply -f dashboard-adminuser-role.yaml

>> clusterrolebinding.rbac.authorization.k8s.io/admin-user created

토큰 생성

kubectl -n kubernetes-dashboard create token admin-user

eyJhbGciOiJSUzI1NiIsImtpZCI6IkVLTnV2WXZpeFJ1elY2S1VMZVYxVjZIeHVVeU43RXNkWVB3S2dtdXZFRnMifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJiNzExMWI5Yy0wMWMxLTQ4OTktYTZhNS1mMmYwZDk5OTE5OWQiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZXJuZXRlcy1kYXNoYm9hcmQ6YWRtaW4tdXNlciJ9.EjBQcub0QvsKv5-TCxSF0ETX6gn4JL0wIFnE2iiCGmhcZjZW0FVA1xtmV_NSipdMAY-ZjWWRf7C1-y-aFm1QVBwk_fj8Pq9CEzcfWSs84X8ROxi8A54luC3_ywXFqhnQWSa7X8Aand0ajUsrn1nnlZJeAdAo3NtPpwVewlfGMBmsk-rpYyACyQkF84-dwwNkgE3xhvGXfK_jl0JH0F_fra1lgQ8WRxDiEvUyX_fId4pRO5Kpm9G9I9babyA5rAWyCqsRJfhNGqcYN-6as3-SuEDNs9IsCbTL5f3dYCymtpFPetc_FMuSk_KivCU8gBeYuyppvTOHvnLSYb0AMUoJTw

Secret 에 토큰 생성

  1. 대시보드 로그인 후 kubernetes-dashboard 네임스페이스에서 생성하거나
  2. 아래 내용으로 yaml 파일 생성 후 kubectl apply -f 실행
apiVersion: v1
kind: Secret
metadata:
  name: admin-user
  namespace: kubernetes-dashboard
  annotations:
    kubernetes.io/service-account.name: "admin-user"   
type: kubernetes.io/service-account-token  

kubectl get secret admin-user -n kubernetes-dashboard -o jsonpath={".data.token"} | base64 -d

eyJhbGciOiJSUzI1NiIsImtpZCI6IkVLTnV2WXZpeFJ1elY2S1VMZVYxVjZIeHVVeU43RXNkWVB3S2dtdXZFRnMifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJiNzExMWI5Yy0wMWMxLTQ4OTktYTZhNS1mMmYwZDk5OTE5OWQiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZXJuZXRlcy1kYXNoYm9hcmQ6YWRtaW4tdXNlciJ9.EjBQcub0QvsKv5-TCxSF0ETX6gn4JL0wIFnE2iiCGmhcZjZW0FVA1xtmV_NSipdMAY-ZjWWRf7C1-y-aFm1QVBwk_fj8Pq9CEzcfWSs84X8ROxi8A54luC3_ywXFqhnQWSa7X8Aand0ajUsrn1nnlZJeAdAo3NtPpwVewlfGMBmsk-rpYyACyQkF84-dwwNkgE3xhvGXfK_jl0JH0F_fra1lgQ8WRxDiEvUyX_fId4pRO5Kpm9G9I9babyA5rAWyCqsRJfhNGqcYN-6as3-SuEDNs9IsCbTL5f3dYCymtpFPetc_FMuSk_KivCU8gBeYuyppvTOHvnLSYb0AMUoJTw

커맨드 라인 프록시

kubectl 커맨드라인 도구를 이용해 다음 커맨드를 실행함으로써 대시보드로의 접속을 활성화할 수 있다.

kubectl proxy


대시보드 접속

http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/

UI는 오직 커맨드가 실행된 머신에서만 접근 가능하다. 상세 내용은 kubectl proxy --help 옵션을 확인한다.

PM2: Node.js 프로세스 관리: 무중단 ecosystem exec_mode cluster instances max

PM2는 Node.js 애플리케이션을 관리하는 데 사용되는 프로세스 관리자입니다. ecosystem.config.js 파일을 사용하여 PM2의 동작을 설정할 수 있습니다. 이 파일에서는 애플리케이션의 실행 환경, 클러스터 모드 및 인스턴스 수를 설정할 수 있습니다. 아래에 ecosystem.config.js 파일의 구성과 리퀘스트 처리량을 고려한 스레드 수 설정 방법을 설명합니다.

ecosystem.config.js 파일 설명

ecosystem.config.js 파일은 PM2 설정을 위한 JavaScript 파일입니다. 이 파일을 통해 애플리케이션을 어떻게 관리할지 설정할 수 있습니다.

기본 구성 예제

module.exports = {
  apps: [
    {
      name: 'cloud-album-backend',
      script: 'dist/main.js',  // 실행할 스크립트 경로
      instances: 'max',       // CPU 코어 수만큼 인스턴스를 실행
      exec_mode: 'cluster',   // 클러스터 모드 사용
      env: {
        NODE_ENV: 'development',
      },
      env_production: {
        NODE_ENV: 'production',
      },
      watch: true,            // 파일 변경 감지하여 재시작 (개발 환경에서 유용)
      max_memory_restart: '200M', // 메모리 사용량 초과 시 재시작
    },
  ],
};

주요 설정 옵션

  • name: 애플리케이션의 이름.
  • script: 실행할 스크립트 파일의 경로.
  • instances: 실행할 인스턴스의 수. max로 설정하면 CPU 코어 수만큼 인스턴스를 실행합니다.
  • exec_mode: 실행 모드. cluster 모드는 여러 인스턴스를 클러스터 모드로 실행하여 로드 분산을 합니다.
  • env: 기본 환경 변수 설정.
  • env_productionpm2 start ecosystem.config.js --env production 명령으로 실행 시 적용될 환경 변수 설정.
  • watch: 파일 시스템을 감시하여 파일이 변경되면 애플리케이션을 재시작합니다.
  • max_memory_restart: 설정된 메모리 사용량을 초과할 때 애플리케이션을 재시작합니다. 예: '200M' 또는 '1G'.

리퀘스트 처리량을 고려한 스레드 수 설정

스레드 수 설정 방법

  • 클러스터 모드 사용exec_mode를 'cluster'로 설정하고 instances를 'max'로 설정하면, PM2가 자동으로 CPU 코어 수에 따라 인스턴스를 생성합니다.
  • 인스턴스 수 지정: 특정 인스턴스 수를 지정할 수도 있습니다. 예를 들어, 4개의 인스턴스를 실행하고 싶다면 instances: 4로 설정할 수 있습니다.

CPU 및 메모리 사용 최적화

  • CPU 코어 수 파악:
    • 일반적으로 인스턴스 수를 CPU 코어 수와 일치시키는 것이 좋습니다. 이는 애플리케이션이 병렬로 실행되어 각 코어를 최대한 활용할 수 있게 합니다.
    • 코어 수가 많은 경우, 애플리케이션이 특정 한계점에서 성능 향상이 멈출 수 있으므로 실제로 사용량을 테스트하여 적정 수를 찾아야 합니다.
  • 메모리 제한 설정:
    • 메모리 사용량 감시max_memory_restart를 설정하여 메모리 사용량이 특정 임계값을 초과하면 프로세스를 재시작합니다.
    • : 메모리 사용량이 200M을 넘으면 재시작하도록 설정: 'max_memory_restart': '200M'.

환경에 따라 인스턴스 수를 조정하는 예제

module.exports = {
  apps: [
    {
      name: 'cloud-album-backend',
      script: 'dist/main.js',
      // 프로덕션에서는 모든 코어 사용, 개발에서는 1개 인스턴스 사용
      instances: process.env.NODE_ENV === 'production' ? 'max' : 1,
      exec_mode: 'cluster',
      env: {
        NODE_ENV: 'development',
      },
      env_production: {
        NODE_ENV: 'production',
      },
      watch: process.env.NODE_ENV !== 'production', // 프로덕션에서는 감시 비활성화
      max_memory_restart: '200M',
    },
  ],
};


PM2 설정 파일 및 실행 예제

ecosystem.config.js 전체

module.exports = {
  apps: [
    {
      name: 'cloud-album-backend',
      script: 'dist/main.js',
      // 프로덕션에서는 모든 코어 사용, 개발에서는 1개 인스턴스 사용
      instances: process.env.NODE_ENV === 'production' ? 'max' : 1, 
      exec_mode: 'cluster',
      env: {
        NODE_ENV: 'development',
        GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
        GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
        PORT: process.env.PORT || 3001,
      },
      env_production: {
        NODE_ENV: 'production',
        GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
        GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
        PORT: process.env.PORT || 3001,
      },
      watch: process.env.NODE_ENV !== 'production', // 프로덕션에서는 감시 비활성화
      //max_memory_restart: '200M',
      log_date_format: 'YYYY-MM-DD HH:mm Z', // 로그 타임스탬프 형식
      output: './logs/out.log', // 일반 로그 파일 경로
      error: './logs/error.log', // 에러 로그 파일 경로
      merge_logs: true, // 여러 인스턴스 로그 병합
    },
  ],
};


PM2를 사용한 실행 방법

# 설정 파일로 애플리케이션 실행 (기본 환경)
pm2 start ecosystem.config.js

# 프로덕션 환경으로 애플리케이션 실행
pm2 start ecosystem.config.js --env production

# 애플리케이션 상태 확인
pm2 status

# 애플리케이션 로그 확인
pm2 logs

# 애플리케이션 중지
pm2 stop cloud-album-backend

# 애플리케이션 재시작
pm2 restart cloud-album-backend

# PM2 실행 목록 저장
pm2 save

 

2024년 6월 28일 금요일

AWS Freetier 프리티어 사용 및 과금 발생 확인: 퍼블릭 IPv4

 

1. 서비스 구성

프런트엔드 Front-end

  • React NextJS SSR
  • S3 + CloudFront

백엔드 Back-end

  • Python
  • Docker + uvicorn
  • ALB + Auto Scaling + EC2 + S3

실 운영환경으로 구성하기 위하여 백엔드 서비스에 ALB와 오토스케일링 그룹까지 생성하였다.

처음 생각한 백엔드 서비스 구성은 ALB + Fargate + ECS + ECR 이었지만 프리티어를 사용해야 하기 때문에 Fargate는 포기함.

NextJS로 구현한 프런트 사이트도 정상적으로 동작하였고 프런트에서 백엔드 API도 정상적으로 호출되어 서비스는 문제없이 구축되었다.

2. 과금 정보 및 비용 관리

AWS 루트 계정의 과금 정보 및 비용 관리 홈으로 이동 후 프리 티어를 확인하였다.

사용 중인 프리 티어 목록을 확인할 수 있다.

청구 예정 금액은 청구서로 이동하여 확인 가능하다.

3. 청구서 과금 발생 !!

며칠 서비스 운영 후 청구서를 확인해 보니 과금이 발생하고 있었다.

  • 위 청구서 캡쳐 참조

Virtual Private Cloud (VPC)

정확히는 VPC에서 사용되는 Public IPv4 Addresses 에 대하여 과금이 발생하였다.

프리 티어로 사용되는 시간만큼 합산하여 무료가 될 것으로 생각하였으나 과금 체계는 다른 것으로 보인다.

  1. $0.00 per In-use public IPv4 address per hour for EC2 Free Tier
  2. $0.005 per In-use public IPv4 address per hour

2번으로 과금되는 서비스가 무엇일까?

4. 과금 원인 파악

EC2 > 네트워크 및 보안 > 네트워크 인터페이스 로 이동하여 목록을 확인해 보니 퍼블릭 IPv4 주소가 3개가 생성되어 있었다.

EC2 인스턴스에서 1개, ALB에 연결된 VPC의 서브넷 2개에서 각각 1개 생성하여 총 3개의 퍼블릭 IP가 생성됨.

EC2의 파이썬 도커에서 외부 공공 API를 호출해야 하기 때문에 EC2에서는 퍼블릭 IP가 필요하였다.

결국 ALB를 삭제하고 프런트 엔드에서 호출하는 백엔드 API의 도메인은 ALB 대신 EC2의 도메인으로 변경하여 다시 배포하였다.

ALB 대신 NLB 사용하여 서브넷을 하나만 사용하도록 하여 LB용 퍼블릭 IP를 1개만 생성하도록 하면 EC2 1개 NLB 1개 사용으로 2개 모두 프리 티어 대상이 될 수도 있을 듯 하다. (구성 후 확인은 하지 않았다)


AWS Support 답변

안녕하세요, AWS 고객지원팀입니다.

프리 티어 사용 중 발생한 VPC 요금과 관련하여 문의해 주신 것으로 이해하였습니다.
답변에 앞서, 2024년 2월 1일자로 서비스 연결 여부에 관계없이 모든 퍼블릭 IPv4 주소에 대해 시간당 IP당 0.005 USD의 요금이 부과되는 점 참고해 주시기 바랍니다. IPv4 주소 요금과 관련한 자세한 내용은 다음 링크에서 확인할 수 있습니다:

https://aws.amazon.com/ko/blogs/korea/new-aws-public-ipv4-address-charge-public-ip-insights/

Elastic Compute Cloud 인스턴스와 연결된 IPv4 주소의 경우, 1년간의 프리 티어 사용량에 해당하여 비용이 발생하고 있지 않습니다. 다만, EC2 외 서비스의 퍼블릭 IPv4 주소에 대해서는 프리 티어 사용량에 해당하지 않아 비용이 발생하고 있습니다.

문의하신 VPC 요금을 중단하기 위해서는 VPC에서 요청자 관리형 네트워크 인터페이스를 생성한 모든 리소스를 종료하거나 삭제해야 합니다. 예를 들어 퍼블릭 IPv4를 사용하는 Application Load Balancer(ALB)를 종료하고 NAT 게이트웨이, 전송 게이트웨이 VPC 연결 및 인터페이스 VPC 엔드포인트를 삭제해야 하는 점 참고해 주시기 바랍니다.


프리 티어 사용 시 청구서를 꼭 확인해야 하고 대부분은 VPC의 퍼블릭 IPv4에서 과금이 발생할 수 있을 것으로 보인다.

2024년 3월 28일 목요일

압구정 라움 : 이즈니카라멜 - 2024.02.08

 

압구정 라움편집샵에서 구찌가방을 사고

랜덤사은품으로 받은 #이즈니카라멜 ㅋㅋ

카라멜주제에 겁나게 비싸서

내돈내산은 안할거 같은디.

맛은 있구만

이마트 : 참이슬 바나나 딸기 하얀계란 세탁망 카누 윈터스노우 반건조 오징어 - 2024.02.06

 

#참이슬후레쉬 댓고리2병

-와~ 우리 소주 많이 마신다

#돌바나나

#김제금실딸기 2팩

#하얀계란

사각3종세탁망 특대용세탁망

-큰게 필요했는데 사이즈가 맘에 안듦

낮잠이불도 쏙 들어가는 세탁망은 다이소에 있음!

크린종이호일

카누

스모키다크로스트 볼드다크로스트

#윈터스노우

-판촉여사님이 서비스로주신 윈터스노우!

요게 젤 맛있었다.

#바로먹는반건조오징어 2팩

-아니 이거 간편한데 맛있잖아?

관련 사이트 및 블로그