1. rocky linux 컨테이너로 띄워보기

$ docker pull rockylinux/rockylinux

위와 같이 pull 명령어로 로키 리눅스를 받아온다.

이미지 용량을 보면 iso 파일에 비해 훨씬 경량화되어 있는 것을 확인할 수 있다.

$ docker run -it --name=rocky-container rockylinux/rockylinux 내부에서수행할명령어

run 명령어로 특정 컨테이너를 실행하고, -it 옵션을 사용하여 사용자의 명령어를 받을 수 있는 상태로 들어가도록 한다.

※ -it 옵션 : interactive 및 tty의 약자, 컨테이너 실행 시 대화형으로 실행하고 터미널을 할당하도록 지시하는 옵션

                  → 컨테이너 내부에서 사용자와 상호작용하거나 명령어를 입력할 수 있음

위와 같이 도커 엔진을 통해 rocky linux로 명령어가 전달되고, 도커 엔진을 통해 host1 Ubuntu에 결과를 전달해준다.

$ docker run -it --name=rocky-container rockylinux/rockylinux bash

 

일회성 실행이 아닌 지속적으로 관리하고 싶다면 쉘이나 배쉬 등을 켜주는 명령어를 -it 옵션으로 넘겨주면 된다.

위와 같이 bash 쉘로 잘 들어가고 종료는 exit 명령어를 이용한다. exit으로 빠져나오는 경우는 컨테이너의 상태가 exit 상태가 된다.

위와 같이 충돌이 날 경우 컨테이너를 재기동하는 start 명령을 사용한 후 실행된 컨테이너에 exec를 이용해 명령어를 전달한다.

위와 같이 start 명령을 사용하면 up 상태로 바뀐 것을 확인할 수 있다.

$ docker exec -it rocky-container bash

이후 exec 명령어를 전달하여 재기동할 수 있고, 백그라운드에서 컨테이너를 돌게 하고 싶다면 ctrl+p+q를 입력한다.

 

 

2. 우분투 띄워보기 

마찬가지로 pull 명령어로 우분투를 설치하고 확인해보면 이미지가 경량화된 것을 볼 수 있다.

우분투의 경우 apt update를 수행해야 기본적인 기능들을 추가로 사용할 수 있고, 대신 잡아먹는 용량도 늘어난다.

ifconfig 명령어를 사용하기 위해 net-tools를 설치한 후 확인해보면 위와 같이 잘 출력되는 것을 확인할 수 있다.

 

 

3. 도커 내부 아이피 배정 순서

도커는 특별한 설정이 없다면 무조건 docker0의 ip를 게이트웨이로 삼는 특성을 갖는다.

docker0의 ip 주소가 172.17.0.1이고, 가운데 17과 0은 무작위 숫자가 부여되고, 맨 마지막 숫자는 1로 시작하고 이후 요소들은 2, 3과 같이 배정한다.

$ docker inspect 컨테이너명 | grep -i ipaddress

도커 호스트 자체에서 컨테이너들에게 배정된 내부 ip를 조회할 수 있다.

위와 같이 처음 생성했던 rocky-container는 ip 주소 마지막 숫자가 2인 것을 확인할 수 있다.

위와 같이 컨테이너 ip는 마지막 숫자만 다른 것을 확인할 수 있다.

ifconfig 조회 시 가상 이더넷이 조회되는 것을 볼 수 있고,이는 호스트와 컨테이너 사이에서 매 컨테이너마다 하나씩 가상으로 생성되어 내부망 접속을 도와주며 컨테이너 개수 파악용도로 쓸 수도 있다.

 

 

4. 이미지 실존 여부 확인하기

pull이나 run으로 다운받아 실행하려는 이미지가 실제로 있는 버전인지 파악하는 것이 중요하다.

위와 같이 dockerhub에 접속하여 document를 체크하면 된다.

이처럼 공식 마크, 인증 마크가 있는 여러 레포를 볼 수 있다.

대부분의 벤더들이 이제 OS에 사용자가 설치할 수 있는 수단을 제공하는 것도 좋지만, 컨테이너로 배포하는 것이 더 효율적이라고 판단했기 때문에 많은 서드파티들은 도커 이미지로 업데이트되어있다.

Tags 탭에서 제공되는 많은 버전을 확인할 수 있다.

또한,  overview 탭에서 Dockerfile을 제공하는 링크로 와서 아무 버전이나 클릭하면 깃허브 레포로 연결되고, 해당 이미지를 구성하기 위해 어떤 도커파일이 작성되어 있는지도 공개되어 있다.

 

 

5. alpine 버전과 그냥 버전의 차이

일반 리눅스는 iso 파일만 GB단위, 컨테이너 목적의 이미지도 용량이 크기 때문에 프로세스 구동에만 필요한 경량화 os를 사용할 수도 있음

→ alpine 리눅스

무려 5MB 정도로 해결해준다. 

즉, 용량이 중요한 상황이라면 alpine 버전을 사용하여 이미지 크기를 줄일 수 있다.

 

 

6. nginx를 활용해 오픈 포트 확인하기

pull 명령어로 nginx alpine 버전으로 받아준다.

$ docker image history 방금받은이미지

위 명령어를 수행하면 EXPOSE라는 구문과 함께 오픈 포트가 공개되어 있는 것을 확인할 수 있다.

EXPOSE는 방화벽에서 외부접근을 허용하기 위해 노출시킨 포트 번호를 의미하고 위 이미지에서는 80번 포트를 기반으로 돌아가게 설계한 것을 확인할 수 있다.

다운로드했던 해당 이미지를 실행한다.

컨테이너 삭제 시 stop 상태여야만 rm 명령어를 수행할 수 있다.

8000번 포트로 포트포워딩 진행

포트포워딩이 잘 진행되었고, 실행 시 이름을 따로 지정하지 않으면 자동으로 생성해준다.

$ docker port 컨테이너명

위와 같이 추후 포트바인딩에 대한 정보 조회도 가능하다.

$ netstat -nlp | grep 포트번호

위 명령어를 통해 호스트의 몇 번 포트가 포트바인딩에 사용되고 있는지도 조회가 가능하다.

$ ps -ef | grep 프록시번호

위에서 조회한 도커프록시의 번호를 이용하여 ps -ef 명령어를 사용하면 좀 더 상세한 정보를 얻을 수 있다.

위와 같이 해당 IP와 8000번 포트로 외부에서 접속이 가능한 것을 확인할 수 있다. (NAT 이용)

위와 같이 가상머신(내부)에서는 두 ip 모두 같은 결과를 출력해내는 것을 확인할 수 있다.

위와 같은 구조이므로 윈도우 입장에서는 host1 Ubuntu를 거쳐야만 한다.

192.68.56.101:8000 → 172.17.0.6:80으로 포트포워딩을 했기 때문에 위와 같이 접속이 가능하다.

외부에서는 172.17.0.6으로 바로 접속하는 것이 불가능하다.

 

 

7. index.html을 생성해 nginx에 적용해보기

nginx의 메인페이지를 변경하기 위해 다음과 같은 작업을 수행한다.

작업을 편리하게 하기 위해 위와 같이 markup 디렉터리 생성 후 index.html 파일을 생성해준다.

 

nginx 서버는 /usr/share/nginx/html/ 경로에 해당 파일을 저장한다.

$ docker cp 파일명 컨테이너명:/usr/share/nginx/html/index.html

호스트에서 컨테이너로 파일을 보내는 방법은 바인드마운트(잘 사용하지 않음), 볼륨마운트 등이 있지만 지금은 복사 명령어로 진행한다.

cp 명령어를 통해 현재 작업폴더에서 복사할 파일을 컨테이너의 해당 경로로 복사해준다.

서버에 다시 접속해보면 위와 같이 입력한 내용대로 잘 변경되는 것을 확인할 수 있다.

 

 

8. 갱신한 nginx 이미지 만들어보기

Dockerfile을 생성하여 직접 이미지를 만들어볼 수 있다.

FROM nginx:1.25.3-alpine # 어떤 이미지 위에 갱신할 파일을 올릴지
COPY index.html /usr/share/nginx/html/index.html # 작업중인 nginx 서버에 파일 갱신
EXPOSE 80 # 80번 포트 노출
CMD ["nginx", "-g", "daemon off;"] # nginx -g daemon off; 구문 실행

위와 같이 도커파일을 생성해준다.

$ docker build -t 이미지명(지정하기):버전명(지정하기) .

복사할 파일인 index.html과 Dockerfile을 같은 경로에 둔 후 위 명령어를 사용하면 먼저 로컬에 이미지를 설치해준다.

위와 같이 로컬에 이미지가 잘 생성된 것을 확인할 수 있다.

run으로 실행한 후 해당 포트로 접속해보면 위와 같이 잘 작동하는 것을 확인할 수 있다.

추후에는 dockerhub와 같은 원격 레포지토리에 업로드까지 하면 외부에서도 마음대로 받아서 사용이 가능하다.

 

 

9. MySQL 컨테이너 생성 및 파라미터 전달 실습

$ docker pull mysql:5.7-debian

먼저, 위와 같이 mysql 이미지를 받아준다. MySQL은 환경변수가 중요하기 때문에 공식 문서를 보고 진행하는 것이 좋다.

$ docker run -it -e MYSQL_ROOT_PASSWORD=초기비번 mysql:5.7-debian /bin/bash

위와 같이 ROOT  비번을 -e 옵션으로 환경변수 처리하여 넘겨줘야 작동한다.

위와 같이 루트계정 제어창으로 잘 넘어간 것을 확인할 수 있다.

버전 확인

위와 같이 MySQL 데몬을 기동하여 외부에서 접속 가능한 상태로 만들어준다. (MySQL 서버 실행)

아까 설정했던 비밀번호를 입력하면 위와 같이 mysql이 잘 실행되는 것을 확인할 수 있다.

위와 같이 테이블 조회, 생성 등 데이터 입출력이 모두 가능하다.

exit하면 위와 같이 mysql이 잘 종료되어 있는 것을 확인할 수 있다.

이후 docker run을 하면 새로운 컨테이너가 생성되기 때문에 다시 실행할 때는 docker start로 해줘야 한다.

$ docker start 컨테이너명
$ docker exec -it 컨테이너명 bash

start로 실행한 경우 exec로 해당 인스턴스의 bash 창으로 진입해야 다시 명령을 내릴 수 있다.

이와 같이 잘 접속이 된다.

또한, MySQL 데몬은 새로 켤 때마다 실행시켜주어야 한다.

 

 

10. 컨테이너로 띄운 DB엔진 워크벤치로 다뤄보기

$ docker run --name mariadb -e MYSQL_ROOT_PASSWORD=비번 -d -e MARIADB_DATABASE=item -p 3306:3306 mariadb:10.2

 

마리아디비를 실행하고 item이라는 데이터베이스도 같이 생성하라는 의미이다.

 

워크벤치는 오라클 사이트에서 다운받거나 DBeaver 같은 오픈소스를 사용해도 된다.

위와 같이 MariaDB에 잘 접속된 것을 확인할 수 있다.

초기에 생성했던 item이라는 데이터베이스도 잘 들어있는 것을 확인할 수 있다.

위와 같이 테이블 생성, 입력, 조회 모두 가능하다.

워크벤치에 접속하여 아까 만든 DB 등이 있는지 조회할 수 있다.

이미지 삭제 시 rmi 명령어를 이용한다.

1. 프로세스와 쓰레드

1) 애플리케이션이란?

→ 하나의 프로그램 단위

 

2) 프로세스란?

→ 하드디스크에 저장되어 있다가 메모리에서 실행 중인 프로그램

→ 각 프로세스는 하나하나가 독립된 코드, 스태틱, 스택, 힙 영역을 가짐

 

3) 멀티 프로세스란?

→ 하나의 애플리케이션을 여러 개 실행했을 때 각각의 애플리케이션은 다중 프로세스를 생성

→ 애플리케이션 단위로 멀티태스킹을 할 수 있도록 도와줌

 

4) 쓰레드란?

→ 하나의 프로세스 내부에서 실행되는 하나의 코드 실행 단위

 

5) 싱글쓰레드와 멀티쓰레드

- 싱글쓰레드 : 순차적으로 실행되는 코드

- 멀티쓰레드 : 한 번에 여러 작업을 동시에 수행 → 코드, 스태틱, 힙은 공유, 스택은 쓰레드 하나당 하나씩 생성

 

6) 멀티프로세스와 멀티쓰레드

- 멀티프로세스 : 독립된 프로세스 영역이기 때문에 하나가 오류가 나도 다른 하나는 영향을 받지 않음

   ex) 워드와 엑셀 실행 시 워드가 오류가 나서 종류된다고 해도 엑셀은 영향을 받지 않음

- 멀티쓰레드 : 하나의 프로세스 내에서 여러 개의 쓰레드가 돌아가기 때문에 프로세스 자체에 문제 발생 시 모든 쓰레드가 영향을 받음

 

※ 쓰레드 = 경량 프로세스

→ 멀티쓰레드 프로그램이 멀티프로세스에 비해 자원을 훨씬 더 먹기 때문에 위와 같은 단점에도 불구하고 멀티쓰레드를 사용

 

 

2. 자바의 쓰레드

1) 자바의 쓰레드

- 모든 자바 애플리케이션은 메인쓰레드가 메인메서드를 실행하며 시작

- 메인메서드는 순차적으로 코드 실행, return이나 마지막 코드 실행 후 종료

- 메인쓰레드는 필요에 따라 보조 쓰레드를 추가로 실행 가능

- 작업쓰레드 : 메인쓰레드가 호출한 보조 쓰레드

- 싱글쓰레드에서는 메인쓰레드 종료 시 즉시 프로세스 종료

- 멀티쓰레드에서는 실행중인 쓰레드가 하나라도 남아있다면 프로세스 유지

 

2) 작업쓰레드 생성해보기

- 쓰레드에 대한 정의 시 클래스파일을 쓰레드 개수만큼 생성

- 쓰레드 정의를 목적으로 생성된 클래스는 java.lang.Thread 클래스의 인스턴스를 직접 생성

- Thread 객체 생성, Runnable을 파라미터로 갖는 생성자 호출, Runnable 인터페이스 구현, run() 메서드 작성

 

// 보조쓰레드에서 실행할 내용을 정의하기 위해
// 1. Runnable 인터페이스 구현
public class MultiThread implements Runnable{
	// 2. Runnable 인터페이스의 run() 메서드를 오버라이딩해 실행할 내용 적음
	@Override
	public void run() {
		// 지금까지 코드는 순차적으로 쉬는시간 없이 실행되었음
		// Thread.sleep(밀리초);는 해당 쓰레드실행을 입력한 초만큼 중단
		// 쓰레드 중지는 try~catch 블럭에 반드시 넣어야 함
		try {
			for(int i = 0; i < 500; i++) {
				System.out.println("보조쓰레드 실행 : " + i);
				Thread.sleep(200); // 0.2초
			}
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
}
public class MainThread {

	public static void main(String[] args) {
		// 보조쓰레드 실행을 위해서는 먼저 Thread 객체를 생성해야 함
		
		// 1. Runnable 구현체 생성
		Runnable trd = new MultiThread();
		
		// 2. Thread 클래스의 인스턴스 생성
		// 생성자 호출 시 위의 Runnable 구현체를 파라미터로 넘김
		Thread sTrd = new Thread(trd);
		
		System.out.println("보조쓰레드 준비 완료!");
		
		// 3. 보조쓰레드는 .start()로 호출 가능
		sTrd.start();
		System.out.println("먼저 끝나도 프로그램은 계속 돌아가는 메인메서드");
	}
}

메인쓰레드는 종료되어도 보조쓰레드는 계속 실행되고 있는 것을 확인할 수 있다.

 

- 활용 예시 코드

public class HighChef implements Runnable {

	@Override
	public void run() {
		System.out.println("후임들에게 일하라고 시킵니다.");
		try {
			Thread.sleep(1000);
			mainMenu();
		} catch(Exception e) {}
	}
	
	public void mainMenu() {
		System.out.println("주메뉴를 만들기 시작합니다.");
		try {
			Thread.sleep(5000);
			System.out.println("주메뉴 완성");
		} catch(Exception e) {}
	}
}
public class MiddleChef implements Runnable {

	@Override
	public void run() {
		subMenu();
		boil();
	}
	
	public void subMenu() {
		System.out.println("부찬을 만들기 시작합니다.");
		try {
			Thread.sleep(5000);
			System.out.println("부찬 완성");
		} catch(Exception e) {}
	}
	
	public void boil() {
		System.out.println("국을 끓이기 시작합니다.");
		try {
			Thread.sleep(6000);
			System.out.println("국 완성");
		} catch(Exception e) {}
	}
}
public class LowChef implements Runnable {

	@Override
	public void run() {
		side();
		rice();
		ingredient();
	}
	
	public void side() {
		System.out.println("반찬을 만들기 시작합니다.");
		try {
			Thread.sleep(2000);
			System.out.println("반찬 완성");
		} catch(Exception e) {}
	}
	
	public void rice() {
		System.out.println("밥을 짓기 시작합니다.");
		try {
			Thread.sleep(10000);
			System.out.println("밥 완성");
		} catch(Exception e) {}
	}
	
	public void ingredient() {
		System.out.println("재료 손질을 시작합니다.");
		try {
			Thread.sleep(3000);
			System.out.println("재료 손질 완성");
		} catch(Exception e) {}
	}
}
public class MultiRestaurant {
	public static void main(String[] args) {
		Runnable highR = new HighChef();
		Runnable middleR = new MiddleChef();
		Runnable lowR = new LowChef();
	
		Thread t1 = new Thread(highR);
		Thread t2 = new Thread(middleR);
		Thread t3 = new Thread(lowR);
		
		// 세 명의 요리사가 동시에 작업
		t1.start();
		t2.start();
		t3.start();
	}
}

위와 같이 여러 개의 쓰레드가 생성되어 코드가 순차적으로 실행되지 않고 동시에 여러 작업을 수행하는 것을 확인할 수 있다.

쓰레드 실행은 start() 함수를 이용하여 호출할 수 있다.

 

- Runnable과 Thread 객체를 따로 만드는게 불편하다면 아래와 같이 익명객체를 생성해 사용해도 된다.

Thread thread = new Thread(new Runnable(){
	public void run(){
    	// 해당 쓰레드가 실행할 코드
    }
});

 

 

3) 쓰레드의 우선순위

- 동시성 : 싱글코어 CPU를 이용한 멀티쓰레드는 실제로는 순차적으로 번갈아가며 실행하지만, 속도가 빨라서 우리가 보기엔 동시에 작업이 이뤄지는 것처럼 보임

- 병렬성 : 멀티코어 CPU를 이용한 멀티쓰레드는 진짜 동시에 여러 작업 수행

 

4) 쓰레드의 공유객체 문제

public class ThreadNotSafety extends Thread {

	// static으로 선언된 변수는 자동으로 0 할당
	static int share;
	
	public static void main(String[] args) {
		ThreadNotSafety t1 = new ThreadNotSafety();
		ThreadNotSafety t2 = new ThreadNotSafety();

		t1.start();
		t2.start();
	}
	
	@Override
	public void run() {
		for(int cnt = 0; cnt < 10; cnt++) {
			System.out.println(share++);
			
			try {Thread.sleep(10);}
			catch(Exception e) {}
		}
	}
}

위와 같이 순차적으로 진행되지 않고 2개의 쓰레드가 static 변수에 동시에 접근하여 의도와 다른 결과가 나올 확률이 있음

※ Race Conditon : 공용 자원을 두고 멀티쓰레드나 멀티프로세스가 경쟁상태에 돌입

 

∴ 특정 로직에 대해서는 동시에 하나의 쓰레드만 접근할 수 있도록 처리할 필요가 있음 → synchronized 키워드

public synchronized void 메서드(){
	// 실행문
}

위 메서드는 동시에 하나의 쓰레드만 접근 가능, 다른 메서드가 접근할 준비가 되어도 대기하기 때문에 공유객체문제 해결 가능

 

public void 메서드(){
	// 실행코드
    synchronized(공유자원){
    	// 단 하나의 쓰레드 단위로만 실행 가능한 실행코드
    }
    // 실행코드
}

위와 같이 특정 코드블럭만 임계영역으로 설정하는 것도 가능하다.

 

public class ThreadSafety extends Thread {
	
	static int share;
	
	public static void main(String[] args) {
		ThreadSafety t1 = new ThreadSafety();
		ThreadSafety t2 = new ThreadSafety();
	
		t1.start();
		t2.start();
	}
	
	// 쓰레드 안전을 위해서 사용하는 키워드
	public synchronized static void sharePlus() {
		System.out.println(share++);
	}
	
	@Override
	public void run() {
		for(int cnt = 0; cnt < 10; cnt++) {
			sharePlus(); // 동시성 제어가 되는 메서드로 1씩 증가
			try {Thread.sleep(1);}
			catch(Exception e) {}
		}
	}
}

위와 같이 동시에 접근해도 제어가 되어 1씩 잘 증가되는 것을 확인할 수 있다.

'네트워크캠퍼스 > JAVA' 카테고리의 다른 글

객체지향적 코드 작성  (0) 2024.01.29
객체지향 정리  (0) 2024.01.25
자바 API  (0) 2024.01.18
자바의 예외처리전략  (0) 2024.01.18
예외처리  (0) 2024.01.17

1. 도커 이미지

1) 개요

- 도커 컨테이너를 생성하기 위한 바이너리 파일

- 도커 이미지 이름

① 저장소 이름 (Repository name)

② 이미지 이름 (Image name)

③ 이미지 태그 (Image tag) : 버전 정보나 릴리즈 태그같은 저보

 

2) 도커 이미지 받아오기

$ docker pull 이미지명 or DockerHub의 레포지토리명:버전 -기타옵션

 

 

2. 도커 컨테이너

1) 개요

- 도커 이미지를 통해 실행되는 하나의 프로세스

- 원본 저장소의 Docker File로부터 Docker Image를 내려받고, 해당 이미지를 실행하여 여러 개의 컨테이너가 실행되는 구조

- 도커 이미지 : 도커 컨테이너 = 1 : N 의 구조

- 도커 이미지는 읽기 전용으로 도커 컨테이너에서 수정이 일어나도 원본 이미지에는 영향을 주지 않음

 

2) 도커 컨테이너 실행방법

① 도커 컨테이너 생성 및 시작을 한 번에 하는 방법

$ docker run [image]

 

② 도커 컨테이너 생성과 시작을 따로하는 방법

$ docker create [image]    // 컨테이너 생성
$ docker start [container] // 컨테이너 시작

 

 

3. 도커 명령어 써보기

1) 이미지 생성

$ docker pull nginx:1.23.1-alpine

1.23.1-alpine 버전의 nginx 이미지를 받아오라는 의미

 

2) 이미지 조회

$ docker images

 

3) 컨테이너 실행

$ docker run -d -p 8001:80 --name=webserver1 nginx:1.23.1-alpine

-d : detach 모드로 해당 컨테이너가 백그라운드에서 돌아감을 의미

-p : 포트바인딩, 8001(호스트포트) 포트로 접속 시 컨테이너의 80번 포트와 연결된다는 의미

--name : 컨테이너명 지정 

 

4) 컨테이너 조회

$ docker ps

CONTAINER_ID : 랜덤하게 배정

NAMES : 랜덤하게 배정

IMAGE : 컨테이너를 띄우는 데 사용한 이미지

PORTS : 호스트포트→연동된컨테잉너 포트

 

5) 컨테이너 정지

$ docker stop

컨테이너 정지 상태 → 정지 상태인 컨테이너는 ps -a로 조회해야 함

컨테이너가 제거된 상태는 아님

 

6) 도커 도큐먼트 받아오기

$ docker run -d -p 80:80 --name=pwd-container docker/getting-started:pwd

dockerhub 사이트의 해당 레포지토리에서 받아오겠다는 의미

실행 시 pull을 진행한 후 Status 부분에 새 이미지를 생성했다고 나오는 것을 확인할 수 있다.

위와 같이 이미지가 로컬에 있는지 조사한 후 없다면 받아오고, 레이어를 겹쳐서 구성하게 된다.

매번 이미지의 모든 구성요소를  받아온다면 부담이 되기 때문에 이를 방지하기 위해 각 레이어를 부품처럼 취급한다.

1. Protocol

1) 개념

- 원활한 통신을 위한 규칙

- 정보의 송수신측 또는 네트워크 내에서 사전에 약속된 규약 또는 규범

- 연결과정, 통신회선에서 접속방식, 통신회선을 통해 전달되는 정보의 형태, 오류발생에 대한 제어, 송수신측 간의 동기 방식 등에 대한 약속

 

2) 주요요소

- 구문(Syntax) : 데이터의 형식, 부호화, 신호레벨 등이 어떠한 구조와 순서로 표현되어있는지

- 의미(Semantics) : 각 비트가 갖는 의미로 패턴에 대한 해석과 해석에 따른 전송제어, 오류수정 등에 관한 제어정보를 규정

- 타이밍(Timing) : 두 개체 간의 통신속도를 조정하거나 메시지의 전송시간 및 순서 등에 대한 특성

 

 

2. OSI 7계층

1) 개념

- ISO에서는 개방형 시스템 간 상호접속을 위해 표준화된 네트워크 구조를 제공하는 기본 참조모델을 제정

- 다른 기종 간의 상호 접속을 위한 가이드라인 제시

- 상위 계층 : 사용자가 통신을 쉽게 이용할 수 있도록 도와주는 역할

- 하위 계층 : 효율적이고 정확한 전송과 관계된 일을 담당

 

2) 계층구조

OSI 7 Layers

- 7계층 구조는 비슷한 기능을 갖는 모듈을 동일 계층으로 분할

- 각 계층 간의 독립성 유지

- 한 모듈에 대한 변경이 다른 전체 모듈에 미치는 영향 최소화

- 2개의 그룹으로 분리 → 상위 3계층 : 이용자가 메시지를 교환할 때 사용, 하위 4계층 : 메시지가 호스트에서 사용

계층 특징 데이터 종류
7 응용계층 - 각종 응용서비스 제공
- 네트워크 관리
메시지
6 표현계층 - 네트워크 보안(암/복호화)
- 압축/압축해제, 포맷 변환 수행
5 세션계층 - 소켓 프로그램
- 동기화
- 세션 연결/관리/종료
4 전송계층 - 데이터 전송보장
- 흐름 제어
- Quality Of Services(QOS)
세그먼트
3 네트워크계층 - 통신경로 설정, 중계기능 담당
- 라우팅, 혼잡제어
- 데이터그램, 가상회선 방식
- IPv4, IPv6
패킷
2 데이터링크계층 - 오류제어, Frame화
- 매체제어(MAC)
- 에러검출, 에러정정, 흐름제어
프레임
1 물리계층 - 물리적 연결설정, 해제
- 전송방식, 전송매체
비트 스트림

 

① 물리 계층

- 데이터를 물리 매체 상으로 전송하는 역할을 담당

- 물리적 링크의 설정/유지/해제 담당

- 상용자 장비와 네트워크 종단장비 사이의 물리적, 전기적 인터페이스 규정에 초점

- 전송 선로의 종류에 따른 전송 방식과 인코딩 방식 결정

- 송신 측 물리 계층은 데이터링크 계층으로부터 받은 데이터를 비트단위로 변환

- 수신 측 물리 계층은 전송 받은 비트를 데이터링크 계층의 데이터로 올림

 

② 데이터링크 계층

- 물리 계층에서 전송하는 비트들의 동기 및 식별 기능, 원활한 데이터 전송을 위한 흐름제어 기능, 안전한 데이터 전송을 위한 오류제어 기능

- 헤더 : 데이터의 시작을 나타내는 표시와 목적지 주소 포함

- 트레일러 : 데이터에 발생한 전송 오류를 검출하기 위한 오류 검출 코드 포함

- 두 sub 계층으로 구성 

→ LLC(Logical Link Control sublayer) : 논리적 연결제어

MAC(Media Access Control) : 장비와 장비 간의 물리적인 접속

 

③ 네트워크 계층

- 송신 측에서 수신 측까지 데이터를 안전하게 전달하기 위해 논리적 링크 설정

- 상위 계층 데이터를 작은 크기의 패킷으로 분할하여 전송

- 개방형 시스템 사이에서 네트워크의 연결을 관리하고 유지하며 해제

- 스위칭 : 패킷의 수신 주소를 보고 정해진 방향으로 전송, 동작속도 빠름

- 라우터 : 라우팅 테이블을 찾아 알고리즘으로 최단 경로 계산 계산을 통해 전송경로를 결정 후 전송하여 스위치보다 동작 속도 느림

- 네트워크 주소는 발신지로부터 목적지까지 동일 (물리주소는 패킷이 이동될 때마다 변경)

 

④ 전송 계층

- 하위 계층의 첫 단계

- 세션을 맺고 있는 두 사용자 사이의 데이터 전송을 위한 종단간 제어

- 송신 컴퓨터의 응용프로그램(프로세스)에서 최종 수신 응용프로그램(프로세스)으로 전달

 

⑤ 세션 계층

- 세션이라 불리는 연결 확립 및 유지

- 효율적인 세션 관리를 위해 짧은 데이터 단위로 나눈 후 전송계층으로 내림

 

⑥ 표현 계층

- 송수신자가 공통으로 정보를 이해할 수 있도록 데이터 표현방식을 바꾸는 기능

- 데이터의 보안과 효율적인 전송을 위해 암호화와 압축을 수행하여 세션 계층으로 내림

 

⑦ 응용 계층

- 최상위 계층으로 응용 프로세스 네트워크 환경에 접근하는 수단을 제공

- 응용 프로세스들이 상호 간에 유용한 정보교환을 할 수 있도록 하는 창구 역할을 담당

 

3) 피어-투-피어(Peer-to-Peer) 간의 통신

- OSI 참조모델의 i번째 계층에서 다른 시스템의 i번째 계층과 통신하기 위해서는 상위계층의 메시지와 더불어 프로토콜 - 제어정보(PCI)를 이용

- 피어-투-피어 프로세스 : 임의의 계층에서 상대편 동일 계층의 모듈과 통신하는 프로세스

 

4) 캡슐화와 역캡슐화

① 캡슐화

- 어떤 것을 다른 것에 포함시킴으로써 포함된 것이 외부에서 보이지 않도록 함

- 프로토콜 데이터 단위를 다른 프로토콜 데이터 단위의 데이터 필드 부분에 위치시키는 기술

 

② 역캡슐화

- 캡슐화 이전으로 복원시키거나 제거 

- 캡슐화의 반대 동작

- 프로토콜 데이터 필드에 위치하고 있는 데이터 단위를 추출

※ 각 레벨의 데이터 PDU(Protocol Data Unit)

PDU 이름 계층
데이터(data) 응용 계층 PDU
세그먼트(segment) 전송 계층 PDU
패킷(packet) 인터페이스 계층 PDU
프레임(frame) 네트워크 접근 계층 PDU
비트(bit) 매체를 통해 이진 데이터로 물리적 전송을 위해 사용되는 PDU

 

→ 송신 단에서는 7계층에서 1계층으로 각각의 PDU를 추가하여 데이터를 캡슐화하고, 수신 단에서는 1계층에서 7계층으로 각각의 PDU를 제거하여 데이터를 얻는 비캡슐화 과정을 겪게 된다.

 

 

3. TCP/IP 4계층

1) 개념

- 미국 국방부 고등 연구 계획국에서 만든 연구 네트워크의 일부로 설계 (ARPAnet)

- 현재는 TCP와 IP가 공식 표준

- 다양한 기종의 컴퓨터가 하나로 묶이는 인터네트워킹 구조를 만듦

- 여러가지 프로토콜의 조합을 의미

- 4계층으로 구성 : 데이터링크, 네트워크, 전송, 응용계층

 

2) 계층구조

TCP/IP 4 Layers

① 네트워크 연결 계층

- 데이터를 송수신하는 역할

 

② 인터넷 계층

- 주소 관리, 포장, 라우팅하는 역할

- IP(Internet Protocol) : 호스트들과 네트워크에서 주소 관리, 패킷 라우팅

- ARP(Address Resolution Protocol) : 같은 네트워크에 위치한 호스트들의 하드웨어 주소를 얻는 데 사용

- ICMP(Internet Control Message Protocol) : 패킷 전송에 관한 에러 메시지 처리

 

③ 전송 계층

- 호스트들 간의 통신 제공, 2개의 프로토콜 존재

TCP(Transmission Control Protocol)

- 연결지향

- 데이터의 확실한 전송을 위해 수신 측으로 받았다는 확인 메시지 요구

UDP(User Datagram Protocol)

- 비연결지향

- 실시간으로 패킷을 전송하여 빠르나 패킷의 정확한 전달을 보장하지 않음

※ TCP와 UDP의 차이점

서비스 TCP UDP
신뢰성   - 패킷이 목적지까지 도달했는지 확인
  - 패킷이 도달할 때마다 ACK를 수신
  - 신뢰성 있는 프로토콜
  - ACK를 사용하지 않음
  - 패킷이 그들의 목적지에 도달되는 것을 보장하지 않음
  - 신뢰성 없는 프로토콜
연결   - 연결 지향적
  - 핸드쉐이킹 과정 수행
  - 비연결지향적
패킷 순서   - 패킷 내에 순서 번호 사용 X
혼잡 제어 O X
용도   - 신뢰성 있는 전송   - 스트리밍 비디오와 브로드캐스트 등 실시간 전송
속도의 오버헤드   - 상당한 양의 자원을 사용하며 UDP보다 느림   - 더 적은 자원을 사용하고 TCP보다 빠름

 

④ 응용 계층 

- 어플리케이션이 네트워크에 접근 가능하도록 함

 

 

4. Port, well known port

1) 포트 주소의 의미와 할당 원칙

① 포트

- 일종의 논리적인 접속 장소

- 포트 번호는 인터넷이나 기타 다른 네트워크 메시지가 서버에 도착했을 때 전달돼야 할 특정 프로세스를 인식하기 위한 방법

- TCP와 UDP에서 포트번호는 15비트 정수의 형태

 

② 포트번호와 소켓

- 통신을 위해 TCP 헤더에 송수신자 포트 정보를 삽입하여 패킷 생성

- 패킷을 서버로 전달하여 프로세스와 연결되면 서비스 이용 가능

- 포트 번호는 16비트, 0~65535번까지 존재, 0~1023번은 가능하면 사용 X (웰 노운 포트)

- 주요 포트번호 : root의 권한이 필요한 포트

 

2) well known port (잘 알려진 포트)

- 어떤 특권을 가진 서비스에 의해 사용될 수 있도록 예약되어 있음

- 루트 권한으로만 포트를 열 수 있음

- 루트 권한으로 실행된 프로그램만이 이 포트에서 데이터를 수신할 수 있지만, 권한에 상관없이 모든 프로그램이 이 포트로 데이터를 보낼 수 있음

프로토콜 포트 용도
ECHO 7 두 장비의 연결 확인
FTP data 20 파일 전송 프로토콜, 데이터 포트, FTP는 두 개의 포트 사용
FTP 21 PUT, GET 등의 FTP 명령 전송 시 사용
SSH 22 암호화된 원격 로그인에 사용
TELNET 23 대화 방식의 원격 명령 라인 세션에 사용, 암호화되지 않느 텍스트 송신
SMTP 25 장비 간의 메일 전송
TIME 37 1990년 1월 1일 자정 이후의 경과 시간을 초로 반환
DNS 53 호스트 도메인의 이름을 네트워크 주소로 바꾸거나 그 반대의 변환 수행
HTTP 80 www 기반의 프로토콜
HTTPS 43 HTTP over SSL (암호화된 전송)

 

 

5. HTTP, URL

1) 개념

- 인터넷 상에서 데이터를 주고 받기 위해 클라이언트와 서버 사이에 이루어지는 요청/응답 프로토콜

- 어떤 종류의 데이터든지 전송 가능, 주로 HTML 문서를 주고받음

- 사용하는 포트 번호 : 80번

- 주로 웹브라우저를 이용하여 통신

- 단점 : 인터넷 상에서 받은 데이터는 평문이 그대로 노출 → 네트워크 상에서 패킷이 그대로 노출되는 취약점 발생

 

2) HTTP Header

- HTTP 통신을 위해 필요한 정보들이 입력되어 있는 데이터 영역

Host 요청이 전송되는 타겟의 host URL 주소
Accept 클라이언트가 허용할 수 있는 파일 형식
User-Agent 요청을 보내는 클라이언트의 정보
Referer 현재 요청된 페이지 이전의 페이지 주소
Cookie 클라이언트에게 설정된 쿠키 정보
Content-Type Request에 보내는 데이터의 type 정보
Content-Length Request에 보내는 데이터의 길이

 

3) HTTP Method

- HTTP 통신의 형태를 결정

GET   - URI(URL)가 가진 정보를 검색하기 위해 서버 측에 요청하는 형태
POST   - URI(URL)에 폼 입력을 처리하기 위해 구성한 서버 측 스크립트 혹은 CGI 프로그램으로 구성
  - Form Action과 함께 전송
  - 데이터 부분에 요청 정보가 들어감
HEAD   - GET과 유사한 방식
  - 헤더 정보 이외에는 어떤 데이터도 보내지 않음
  - 웹 서버의 다운 여부 점검이나 웹 서버 정보를 얻기 위해 사용
OPTIONS   - 시스템에서 지원되는 메소드 종류 확인 가능
PUT   - POST와 유사한 전송 구조
  - 헤더 이외에 메세지가 함께 전송
  - 원격지 서버에 지정한 콘텐츠를 저장하기 위해 사용
  - 홈페이지 변조에 악용
DELETE   - PUT과 반대 개념
  - 원격지 웹 서버에 파일을 삭제하기 위해 사용
TRACE   - 원격지 서버에 루프백 메세지를 호출하기 위해 사용
CONNECT   - 웹 서버에 프록시 기능을 요청할 때 사용

 

4) HTTP Content-Type

- HTTP 헤더에 보내지는 데이터

- 표준 MIME Type의 하나

     → 브라우저는 데이터를 나타내는데 어떤 종류의 파일 stream인지 알게 됨

     → 서버에서 데이터를 해석할 때 중요한 역할을 함

Text 사람이 읽고 이해할 수 있는 문자열
Image 그림 데이터
Audio 음성 데이터
Video 동영상 데이터
Application 모든 종류의 이진 데이터
Multipart 복수의 데이터로 이루어진 복합 데이터
Message 전자 메일 메세지
Model 복수 차원으로 구성하는 모델 데이터

 

5) URL

- URI (URL) : Uniform Resource Identifier의 약자로 리소스를 식별하기 위한 식별자

- URL은 Uniform Resource Locator의 약자로 리소스의 위치를 식별하기 위한 URI의 하위 개념

- URI는 Scheme, Authority (Userinfo, Host, Port), Path, Query, Fragment의 구성 요소를 가짐

- 자주 쓰이는 웹 URI 구성 요소

Scheme 웹 서버에 접속할 때 어떤 프로토콜을 이용할지에 대한 정보를 담고 있음
Host 접속할 웹 서버의 호스트(서버 주소)에 대한 정보를 가지고 있음
Port 접속할 웹 서버의 포트에 대한 정보를 가지고 있음
Path 접속할 웹 서버의 경로에 대한 정보를 가지고 있음, '/' 문자로 구분
Query 웹 서버에 전달하는 파라미터이며 URI에서 '?' 문자 뒤에 붙음
Fragment 메인 리소스 내에 존재하는 서브 리소스에 접근할 때 이를 식별하기 위한 정보를 담고 있으며
URI에서 '#' 문자 뒤에 붙음

 

 

7. TCP/IP

→ 인터넷에 연결된 다른 종류의 컴퓨터끼리 상호 데이터를 주고받을 수 있도록 한 인터넷 표준 프로토콜

     (TCP : 데이터를 패킷으로 나누고 묶는 역할, IP : 명령이 올바르게 전송되도록 하며 전달되지 못한 패킷은 재전송)

1) TCP(Transmission Control Protocol) 주요 특징

- 신뢰성 있음 : 패킷 손실, 중복, 순서바뀜 등이 없도록 보장

- 연결지향적

    : 느슨한 연결(Loosly Connected)을 가짐

    : 연결 관리를 위한 연결 설정 및 연결 해제 필요

    : 양단간 어플리케이션/프로세스는 TCP가 제공하는 연결성 회선을 통하여 서로 통신

- TCP 연결의 식별, 다중화, 포트번호

- 전이중 전송방식/양방향성

- 멀티캐스트 불가능

- 세그먼트화 처리 : 데이터를 패키징 처리

- 흐름제어, 혼잡제어

- 비실시간적 응용

 

2) IP(Internet Protocol) 주요 특징

- 신뢰성 및 흐름제어 기능이 전혀 없음

- 비연결성 데이터그램 방식

- 패킷의 완전한 전달을 보장하지 않음

- IP 헤더 내에 수신 및 발신 주소를 포함

- IP 헤더 내 최상위 바이트(MSB)를 먼저 보냄

- 경우에 따라 단편화가 필요함

- 모든 상위 계층 프로토콜들이 IP 데이터그램에 살려서 전송됨

 

3) IPv4 주소 체제

- 32비트의 IP 주소를 보기 쉽게 표시하기 위해 4바이트 단위로 나누고 10진수로 표시하는 표현 방식이 널리 사용

- IP 주소는 네트워크 식별자 필드와 호스트 식별자 필드의 두 부분으로 구성되며 각 필드에서 사용되는 비트 수에 따라 5개의 클래스로 나눔

 

① 클래스별 IP 주소 내용

클래스 A - 첫번째 비트가 0인 IP 주소
- 상위 1바이트 : 네트워크 주소, 하위 3바이트 : 호스트 주소
- 큰 규모의 호스트를 갖는 기관에 할당
클래스 B - 처음 두 비트의 값이 10인 주소
- 상위 2바이트 : 네트워크 주소, 하위 2바이트 : 호스트 주소
클래스 C - 처음 3비트의 값이 110인 주소
- 상위 3바이트 : 네트워크 주소, 하위 1바이트 : 호스트 주소
- 작은 규모의 네트워크에 할당
클래스 D - 처음 4비트의 값이 1110인 주소
- 전체 주소가 멀티캐스트용으로 사용
클래스 E - 처음 4비트의 값이 1111인 주소
- 추후 사용을 위해 예약된 주소

 

② 패킷의 전송방법

전송 방식 설명
유니캐스트 - 하나의 송신자가 하나의 수신자에게 패킷을 보내는 방식
멀티캐스트 - 하나의 송신자가 다수의 수신자에게 패킷을 보내는 방식
- 특정 다수에게 전송
브로드캐스트 - 같은 네트워크에 잇는 모든 호스트에게 패킷을 보내는 방식
- 호스트 주소를 모두 1로 설정
- 불특정 다수에게 전송

 

③ IPv4 주소 관리방식

- 서브네팅 : 이진수로 1인 부분은 네트워크 부분, 0인 부분은 호스트

- 슈퍼네팅 : 부족한 IP를 효율적으로 사용하기 위해 여러 개의 C클래스 주소를 묶어 하나의 네트워크로 구성하는 방식

 

④ CIDR(Classes InterDomain Routing)

- 표기법

     : 비트마스크를 사용하여 점으로 구분된 10진 표기법 지정

     : 서브넷 마스크에서 연속된 1의 수가 몇개인지 지정, 연속된 1은 서브넷 마스크의 맨 왼쪽 비트부터 시작

     : IP 주소에서 네트워크 ID 구성, /x로 비트 수 표현

- 장점

     : IPv4의 주소 공간을 효율적으로 할당

     : 인터넷 라우팅 테이블의 비대화를 줄임

 

⑤ VLSM(Variable Length Subnet Mask)

- IP를 효율적으로 할당하여 활용

- 서로 다른 크기의 서브넷 지원

- IP 주소 공간의 일부를 잘라서 사용, 한 기관에 이미 할당된 주소 공간을 나눔

 

⑥ 사설 네트워크를 위한 주소할당

공인 IP - 인터넷 상에 하나밖에 없는 IP로 유일
- 각 나라의 관할 기관에서 할당
사설 IP - 인터넷 상에서 확인할 수 없으며 내부 네트워크에서만 활용
- 홈 LAN이나 회사 내부에서 마음대로 할당

- 사설주소 영역 : 사설 인터넷을 위해 IANA가 할당한 IP 주소 블록

클래스 IP 주소 블록 주소 범위
class A 24비트 블록 10.0.0.0 ~ 10.255.255.255
class B 20비트 블록 172.16.0.0 ~ 172.31.255.255
class C 16비트 블록 192.168.0.0 ~ 192.168.255.255

- NAT(Network Address Translation) : 사설 IP 주소를 공인 IP 주소로 변환하는 주소 변환기

 

4) IPv6

- 128비트 주소 길이를 사용

- 보안문제, 라우팅 효율성 문제, QoS 보장, 무선 인터넷 지원과 같은 다양한 기능 제공

 

5) IPv4와 IPv6 특징 비교

구분 IPv4 IPv6
주소 길이 32비트 128비트
표시 방법 8비트 4부분 10진수 표시 16비트 8부분 16진수 표시
주소 개수 약 43억개 2^128개
주소 할당방식 A, B, C, D 등의 클래스 단위 비순차 할당 네트워크 규모, 단말기 수에 따라 순차 할당
브로드캐스트 주소 있음 없음
헤더 크기 가변 고정
QoS 제공 미흡 제공
보안 IPSec 프로토콜 별도로 설치 IPSec 자체 지원
서비스 품질 제한적 품질 보장 확장된 품질 보장

 

'네트워크캠퍼스 > NETWORK' 카테고리의 다른 글

네트워크 쉽게, 더 쉽게  (0) 2024.02.01
3주차  (0) 2024.02.01
포트포워딩 실습  (0) 2024.01.26
2주차  (0) 2024.01.26
모두의 네트워크  (0) 2024.01.22

1. 컨테이너 기술

 

1) 컨테이너란?

- 애플리케이션을 언제든 실행 가능하도록 필요한 모든 요소를 하나의 런타임 환경으로 패키징한 논리적 공간

- 도커 : 컨테이너를 쓸 수 있도록 도와주는 기술 중 하나

- docker에서는 Dockerfile이라는 이름의 파일(대소문자 구분)에 적힌 스크립트를 통해 구현 → 코드형 인프라

 

2) 컨테이너 기술의 역사

① Traditional Deployment

- 물리적 컴퓨터에 애플리케이션을 배포하는 형태

- 하나의 하드웨어에 여러 애플리케이션이 관리되는 경우 앱간의 라이브러리 의존성 문제 발생 가능

 

②  Virtualized Deployment

- Hypervisor : 운영체제 위에서 운영체제를 독립적으로 격리시켜 실행할 수 있도록 만드는 기술 ex) vmware, virtualbox 등

- 프로세스 개수만큼 서버를 사야할 필요가 없어짐

- Guest OS를 하나하나 올리는 것에 많은 자원이 소모되는 문제 발생

 

③ Container Deployment

- 애플리케이션과 종속 항목을 하나로 묶어 실행하게 해주는 운영 시스템을 가상화한 경량의 격리된 프로세스

- microVM이라고도 부름

- 운영체제 수준의 가상화 제공

- 독립성을 갖기 때문에 다른 컨테이너에 영향을 주지 않는 stateless 환경 제공

 

3) 컨테이너 기술의 장점

- 개인 데스크탑 뿐만 아니라 기업 내의 온프레미스 서버, AWS와 같은 퍼블릭 클라우드까지 언제 어디서든 빠르고 효율적으로 배포 가능

→ 서버구성, OS설치, 네트워크, 개발 도구 구성 등의 반복적이고 불편한 작업에 시간을 낭비하지 않고 개발 그 자체에 집중 가능

- 컨테이너는 우리가 개발한 최소한의 Image(OS+실행할 프로그램)를 통해 실행되므로 경량

→ 컨테이너 이미지 생성의 Best Practice 중 하나는 이미지 경량화

- 일반 서버의 애플리케이션 실행과 달리 프로세스 수준의 속도로 빠르게 실행할 수 있고, 한 번에 여러 개의 컨테이너를 동시에 실행 가능

→ Docker의 docker compose 기술을 통해 구현

- 어떤 OS, 어떤 환경에서도 동작 가능한 이식성 보유

- 컨테이너 자체 애플리케이션 환경에 대한 관리만 요구되므로 지속적 서버관리 비용 절감 가능

- 개발팀과 운영팀의 업무 분리로 각자의 업무와 세분화된 관리에 집중 가능

→ 컨테이너는 Devops workflow 구성에 최적

 

4) 컨테이너의 사례

- 구글 웹, 앱 서비스

- 에어비엔비 추천서비스

- 넷플릭스 추천서비스

- 당근마켓 딥러닝 기반 추천서비스

- 엔씨소프트 게임서비스

- 삼성전자 헬스케어서비스

- 타다 배차서비스

- 토스 금융서비스

 

5) 컨테이너 타입

① 시스템 (or OS) 컨테이너 : OS까지만 설치해서 실행되는 유형

- 호스트 OS 위에 Ubuntu와 같은 배포판 리눅스 Image를 통해 배포되는 컨테이너

- 또다른 VM의 형태(Hypervisor와 유사), 내부에 다양한 애플리케이션 및 라이브러리 도구를 설치&실행 가능

- ex) LXC, LXD, OpenVZ, Linux Server, BSD Jails 등

 

② 애플리케이션 컨테이너 : OS와 OS 위에서 돌아갈 애플리케이션까지 같이 설치하여 실행되는 유형

- 단일 애플리케이션 실행을 위해 해당 서비스를 패키징하고 실행하도록 설계된 컨테이너

- 3-tier 애플리케이션과 같은 경우 각 tier(frondend-backend-DB)를 개별 컨테이너로 실행하여 연결

- ex) Docker container runtime, Rocket 등

 

 

2. Docker

 

1) 도커란?

- 여러 계층의 애플리케이션을 컨테이너로 분리, 연결하여 실행하는 마이크로서비스 아키텍처 프로젝트에 유용

- 애플리케이션의 인프라는 이미지를 통해 제공하고, 퍼블릭 혹은 프라이빗하게 공유 가능

- 깃허브와 유사한 방식으로 Docker Hub에서 제공

- 제공된 Image를 기반으로 Application 서비스를 제공, 이를 컨테이너화할 수 있음

 

2) 가상화(Virtualization)란?

- 서버, 스토리지, 네트워크, 애플리케이션 등을 가상화하여 하드웨어 리소스를 효율적으로 사용하려는 목적

→ 효율적인 자원 활용, 자동화된 IT 관리, 빠른 재해 복구 가능

- 물리적 하드웨어 유지 관리 대신 소프트웨어적으로 추상화된 가상화를 통해 제한된 부분을 쉽게 관리하고 유지 가능

- 하이퍼바이저 기반의 가상머신을 통해 수행 ex) VMware, VirtualBox 등

 

3) 컨테이너 가상화 vs VM 가상화

  컨테이너 VM
공통점 실행하고자 하는 애플리케이션 프로세스 및 종속 요소와 소스 등을 패키지, 즉 이미지화하여 HostOS와 격리된 환경 제공
차이점 VM 가상화에 비해 경량이면서 호스트 운영체제의 커널을 공유하는 운영체제 수준의 가상화를 구현 → 원하는 애플리케이션 환경을 빠르게 번들링하여 패키징 실제 호스트 운영체제와 같이 별도의 GuestOS를 두고 원하는 애플리케이션을 설치하는 하드웨어 수준의 가상화를 구현
운영체제가 1개 운영체제가 적어도 2개 이상

 

※ 컨테이너는 가상머신인가?

→ Container는 hypervisor와 완전히 다른데, 궁극적으로는 hypervisor와 유사한 형태의 "가상화"를 목표로 하고 있다.

→ hypervisor는 OS 및 커널이 통째로 가상화되는 반면, container는 filesystem의 가상화만을 이룬다.

→ 컨테이너는 호스트 PC의 커널을 공유하고 따르기 때문에 init(1) 등의 프로세스가 떠있을 필요가 없으며 가상화 프로그램과는 다르게 적은 메모리 사용량, 적은 오버헤드를 보인다.

 

4) 컨테이너화(containerization) 기술

① LXC를 이용한 시스템 컨테이너화로 시작

→ cgroups, namespaces : Linux Kernel 위에서 LXC를 격리시키기 위해 필요

→ cgroup, namespace 등의 커널 기술을 공유해 컨테이너에 제공

 

※ namespaces

→ 각 게스트 머신별로 독립적인 공간을 제공하고, 서로가 충돌하지 않도록 하는 기능 (현재 리눅스 커널에서는 6가지 지원)

mnt (파일시스템 마운트)  호스트 파일시스템에 구애받지 않고 독립적으로 파일시스템을 마운트하거나 언마운트 가능
pid (프로세스) 독립적인 프로세스 공간 할당
net (네트워크) namespace 간에 network 충돌 방지
ipc (SystemV IPC) 프로세스 간의 독립적인 통신통로 할당
uts (hostname) 독립적인 hostname 할당
user (UID) 독립적인 사용자 할당

 

※ cgroups (Control Groups) 

→ 자원에 대한 제어를 가능하게 해주는 리눅스 커널의 기능

→ 메모리, CPU, I/O, 네트워크, device 노드(/dev/)를 제어

 

② containerd, runC를 이용한 도커

초기 도커는 LXC를 그대로 응용하여 사용

현재는 containerd, runC를 이용하도록 변경

- runC : 커널 기술 공유를 통해 컨테이너를 생성하도록 도와줌

- containerd : 생성된 컨테이너의 라이프사이클 관리

- dockerd : 사용자 환경에서 명령 전달

- docker 1.11 버전부터 위와 같은 구조로 작동

- containerd : OCI 구현체(주로 runC)를 이용해 컨테이너를 관리해주는 daemon

- Docker Engine 자체는 이미지, 네트워크, 디스크 등의 관리 역할

- 여기서 Docker Engine과 containerd 각각이 완전 분리

  → Docker Engine 버전을 올릴 때 Docker Engine을 재시작해도 container의 재시작 없이 사용 가능

1. JAVA API(Application Programming Interface) 

→ 프로그램 개발에 자주 사용되는 클래스 및 인터페이스의 모음으로 라이브러리라고 부름

※ 자바 표준 API 문서 : https://docs.oracle.com/javase/8/docs/api

 

1) java.lang 패키지

자바의 기본적인 클래스들을 담고 있는 패키지로 import 구문 없이 사용 가능

① Object : 자바클래스의 최상위 클래스

Object 클래스는 모든 클래스의 부모클래스이다.

public class MainClass {

	public static void main(String[] args) {
		ObjectInformation oi = new ObjectInformation();
		
		System.out.println(oi);
	}
}

Object 클래스의 toString 메서드

Object 클래스를 확인해보면 toString 메서드가 이미 정의되어 있기 때문에 위와 같은 출력 결과를 확인할 수 있다. 

public class ObjectInformation {
	// toString 오버라이딩 후 System.out.println() 등으로 객체변수 조회 시
	// 해당 인스턴스의 클래스 경로와 주소값 대신 toString에서 리턴한 문자가 콘솔에 찍힘
	
	@Override 
	public String toString() {
		return "조회 시 이게 나올거임";
	}
}

ObjectInformation 클래스에 toString() 메서드를 오버라이딩하면 오버라이딩한 내용으로 바뀌어 출력되는 것을 확인할 수 있다.

 

② System : 표준입력장치(키보드)로부터 데이터를 입력받거나 표준출력장치(모니터)로 출력하기 위해 사용

- exit() : 현재 실행하고 있는 프로세스 강제 종료, 정상 종료일 경우 매개값으로 0을 줌

- currentTimeMills() : 컴퓨터의 시계로부터 현재 시간을 읽어서 밀리세컨드 단위와 나노세컨드 단위의 long 값 리턴

                                  → 프로그램의 실행 소요 시간 측정으로 성능을 테스트할 때 사용

public class SystemTimeEx {
	public static void main(String[] args) {
		// currentTimeMilllis()와 nanoTime() 메서드는 UNIX 시간 사용
		// UNIX 시간 : 1970/01/01 00:00:00을 기점으로 얼마나 시간이 지났는지 숫자로 표현
		// 현재까지의 시간을 1000분의 1초로 변환한 에폭시간과 10억분의 1초로 변환한 에폭시간을 long 타입으로 리턴
		
		long start = System.currentTimeMillis();
		System.out.println("시작시간 : " + start);
		long sum = 0;
		
		for(long i = 1; i < 10_000_000_000L; i++) {
			sum += i;
		}
		long end = System.currentTimeMillis();
		System.out.println("종료시간 : " + end);
		System.out.println("계산에 소요된 시간 : " + (end - start));
	}
}

위와 같이 해당 로직이 수행되는데 얼마나 시간이 걸리는지 알 수 있다.

- getProperty() : JVM이 시작할 때 자동 설정되는 시스템의 속성값 구함

- gc() : Garbage Collector 실행

 

③ Class : 클래스를 메모리에 로딩할 때 사용

 

④ String : 문자열을 저장하고 문자열의 여러 가지 정보를 얻을 때 사용

→ 생성자를 사용하여 객체를 만들 필요 없이 기초 데이터처럼 바로 초기화 가능

- 주요 메서드

charAt() 특정 인덱스 글자 리턴
indexOf() 특정 문자열의 시작 인덱스 반환, 해당 문자가 없으면 -1 리턴
length() 문자열 길이 리턴
replace() 특정 문자열 변경
substring() 인덱스를 기준으로 그 앞의 문자열을 잘라줌
ex) 매개값으로 인덱스 2개 주면 처음 매개값~두번째 매개값의 문자열을 제외하고 삭제
toUpperCase() 문자열을 대문자로 치환
toLowerCase() 문자열을 소문자로 치환
trim() 문자열의 앞, 뒤 공백 제거
equals() 문자열의 값 비교
valueOf() 기본 데이터 타입의 값들을 문자열로 변환

 

-charAt( ) 메서드 예시

public class StringCharAt {
	public static void main(String[] args) {
		// 일반 배열은 아래와 같이 인덱싱 가능
		int [] iArr = {1, 2, 3, 4};
		System.out.println(iArr[2]);
		
		// 문자열은 일반 인덱싱([ ]을 사용하는 인덱싱)이 불가능
		String str = "가나다라";
		//System.out.println(str[2]);
		System.out.println(str.charAt(2));
	}
}

 

- indexOf( ) 메서드 예시

public class StringIndexOf {
	public static void main(String[] args) {
		// 특정 문자열의 시작 인덱스값을 반환
		String to = "tomatos";
		
		// 단일 파라키터로 조회만 할 경우 0번에서 제일 가까운 단어 하나만 조회
		System.out.println(to.indexOf(to));

		// 두 번째 파라미터로 조회 시작 파라미터 지정 가능
		System.out.println(to.indexOf("to", 1)); // 0번부터가 아닌 1번부터 조회
		
		// 없는 단어 조회 시 -1
		System.out.println(to.indexOf("na"));
	}
}

 

- replace( ) 메서드 예시

public class StringReplace {

	public static void main(String[] args) {
		// 자바는 15버전부터 멀티라인 문자열 지원
		// 원래 자바 문자열은 여닫는 "를 한 줄에 작성해야했음
		// String a = "가나다라"; (허용) 
		// String a = "가나다라
		//					"마바사";	(허용 X)
		
		// 여닫는 따옴표가 다른 줄에 위치한 경우 " 세 개 이용
		// """를 사용한 줄에는 문자열을 작성할 수 없고, 다음 줄부터 작성가능
		String notice = """
						<공지사항>
						1. 복습 철저히 해주세요
						2. 회고록 작성 해주세요
				""";
		System.out.println(notice);
		
		// replace()는 String을 리턴하고, 첫 단어를 두 번째 단어로 바꿔줌
		String newNotice = notice.replace("해주세요", "해주십시오");
		System.out.println(newNotice);
	}
}

 

- substring( ) 메서드 예시

public class StringSubstring {

	public static void main(String[] args) {
		// 주민번호 양식
		String ssn = "010808-4987654";
		System.out.println(ssn);

		// substring() 메서드에 매개값으로 인덱스를 1개 지정 시 
		// 해당 인덱스부터 마지막 지점까지의 문자 추출
		String last = ssn.substring(7); // 7번부터
		System.out.println(last);
		
		// substring() 메서드에 매개값으로 인덱스를 2개 지정 시
		// 첫 번째 매개값 이상, 두 번째 매개값 미만 범위 문자 추출
		String first = ssn.substring(0, 6); // 0 1 2 3 4 5까지 조회, 6은 범위 X
		System.out.println(first);
	}
}

 

- trim() 메서드 예시

public class StringTrim {

	public static void main(String[] args) {
		// trim은 좌측, 우측에서 다른 단어가 나오기 직전까지 공백을 전부 제거
		String trimBefore = "              옆에   거슬     려요       ";
		System.err.println(trimBefore);
		String trimAfter = trimBefore.trim();
		System.out.println(trimAfter);
	}
}

 

- valueOf() 메서드 예시

public class StringValueOf {

	public static void main(String[] args) {
		int a = 10;
		double b = 8.79;
		System.out.println(a + b);
		
		String str1 = String.valueOf(a); // int -> String
		String str2 = String.valueOf(b); // double -> String
		System.out.println(str1 + str2);
	}
}

 

★ String 클래스 단점 : 처음 초기화된 데이터 변경 시 기존 객체 재활용이 아닌 새로운 객체 생성 → 메모리 과소비

위와 같이 String 클래스는 문자 내용이 변경될 때마다 새로운 영역에 다시 할당되어 속도가 느려지는 현상이 발생한다.

→ 속도적인 측면에서 개선된 StringBuffer와 StringBuilder 사용

 

⑤ StringBuffer, StringBuilder : 문자열을 저장하고 내부 문자열을 조작할 때 사용

- StringBuilder가 성능적으로는 좀 더 낫지만, StringBuffer는 Thread safety를 보장한다.

- 정말 세밀한 성능 이슈를 따지지 않는 환경에서는 StringBuffer를 쓰면 된다.

public class StringMemory {
	public static void main(String[] args) {
		// String의 경우 내용이 다른 문자는 항상 새롭게 할당
		//String a = "0";
		
		// StringBuilder, StringBuffer는 문자도 메모리 저장 시 변경 가능하게 저장
		// 따라서 문자 내용이 바뀌어도 새로운 할당이 잘 일어나지 않으므로 성능상 우위에 있음
		// StringBuilder는 Thread safety하지 않지만 StringBuffer보다 근소하게 성능이 좋음
		//StringBuilder sb = new StringBuilder("0");
		StringBuffer sb = new StringBuffer("0");
		
		long start = System.currentTimeMillis();
		
		for(int i = 0; i < 1_000_000; i++) {
			//a += "0"; // a 문자에 0을 100만번 더함 -> 힙 할당도 100만번
			sb.append("0"); // sb에 0을 100만번 더함 -> 힙 할당은 거의 새롭게 일어나지 않음
		}
		 
		long end = System.currentTimeMillis();
		System.out.println("소요시간(밀리초 : " + (end - start));
	}
}

 

- StringBuilder 클래스의 주요 메서드

append() 기존 문자열의 뒤에 문자열 추가
insert() 특정 위치에 문자열 추가
delete() 문자열 삭제
deleteCharAt() 특정 인덱스의 문자 하나 삭제
replace() 문자열의 일부분을 다른 문자열로 대체
setCharAt() 문자열에서 주어진 index의 문자를 다른 문자로 대체
toString() stringBuilder 객체의 값을 문자열로 반환
reverse() 문자열을 거꾸로 뒤집기
public class StringBuilderEx {
	public static void main(String[] args) {
		// 이 코드에서 StringBuilder는 전부 StringBuffer로 대체해도 잘 동작한다.
		// 둘의 차이는 쓰레드 안전이 보장되는지 아닌지의 여부뿐
		// 따라서 쓰레드 안전을 보장받고 싶다면 StringBuffer만 사용하면 됨
		//StringBuilder sb = new StringBuilder("JAVA");
		StringBuffer sb = new StringBuffer("JAVA");
		
		// 문자열 끝에 추가하는 메서드 append()
		sb.append(" Program Study");; // sb += " Program Study"와 동일
		System.out.println(sb);
		
		// 문자열을 특정 인덱스 위치에 삽입하는 메서드 insert()
		sb.insert(12, "ming");
		System.out.println(sb);
		
		// 특정 인덱스 범위 문자열을 교체하는 메서드 replace()
		sb.replace(5, 16, "book"); // 5~15번 인덱스 11글자를 book 4글자로 치환
		System.out.println(sb);
		
		// 문자열을 삭제하는 메서드 delete(begin, end)
		sb.delete(4, 9);
		System.out.println(sb);
		System.out.println(sb.length());
	}
}

 

☆ 매번 할당하지 않는 원리

StringBuilder와 StringBuffer는 생성 시 할당하는 저장용량 capacity(16)를 가지고 있으며, append 연산으로 저장 용량을 초과하는 경우 기존 저장 용량을 가진 객체를 재할당하게 된다.

 

⑥ Math : 수학 함수를 이용할 때 사용

- 주요 메서드

abs() 절대값
ceil() 올림값
floor() 내림값
max() 최대값
min() 최소값
random() 랜덤값 (0.0 <= 값 <1.0)
rint() 현재 수에서 가까운 정수를 실수 형태로 구함
round() 반올림값
public class MathEx {
	public static void main(String[] args) {
		// 수학 관련된 연산이나 혹은 상수를 저장해둔 Math 클래스는 정적 변수와 메서드를 가지므로
		// 굳이 Math 객체를 생성하지 않아도 활용 가능
		
		// 절대값 : 부호 무시
		System.out.println(Math.abs(-15.294));
		
		// 올림 : 소수점 아래자리가 존재하면 1 증가
		System.out.println(Math.ceil(10.0));
		System.out.println(Math.ceil(10.00001));
		
		// 내림 : 소수점 아래자리가 존재하면 없애버림
		System.out.println(Math.floor(10.0));
		System.out.println(Math.floor(10.99999));
		
		// 최대값 : 제시된 수 중 가장 큰 값 하나만 남김
		System.out.println(Math.max(99.9, 12.34));
		
		// 최소값 : 제시된 수 중 가장 작은 값 하나만 남김
		System.out.println(Math.min(99.9, 12.34));
		
		// 랜덤값 : 컴퓨터 시스템은 완벽한 난수가 아닌 시득닶에 따른 의사난수를 사용
		System.out.println(Math.random());
		
		// 가장 가까운 실수 구하기
		System.out.println(Math.rint(12.500001));
		
		// 반올림 : 소수점 아래값이 0.5 미만이면 정수를 그대로, 이상이면 1 증가
		System.out.println(Math.round(24.604));
        
        	// 혹은 Math 클래스 내에 정적변수로 자주 사용하는 상수값(원주율, 자연상수 등)도 제공
		System.out.println(Math.PI);
		System.out.println(Math.E);
	}
}

 

 

⑦ Wrapper(Byte, Short, Integer, Long, Float, Double, Boolean, Character) : 기본 데이터 타입의 객체를 만들 때 사용

 

 

 

 

 

 

 

 

 

 

 

 

'네트워크캠퍼스 > JAVA' 카테고리의 다른 글

객체지향 정리  (0) 2024.01.25
프로세스와 쓰레드  (0) 2024.01.24
자바의 예외처리전략  (0) 2024.01.18
예외처리  (0) 2024.01.17
인터페이스  (0) 2024.01.16

+ Recent posts