TIL(Today I Learned)

Docker 명령어 1

Happy._. 2024. 6. 28. 22:36

Docker

  • 컨테이너 기술: 컨테이너를 생성하고 관리하기 위한 도구
  • 컨테이너의 생성 및 관리 프로세스를 단순화하는 도구
  • 라이브러리, 시스템 도구, 코드, 런타임 등 소프트웨어를 실행하는 데 필요한 모든 것이 포함된 패키지
  • 이미지: 읽기/쓰기 액세스 권한이 있는 인스턴스를 실행하는 컨테이너의 “블루프린트”
  • 컨테이너
    • 이미지의 실행 인스턴스
    • 이미지를 기반으로 하는 실행 애플리케이션(격리된 소프트웨어 유닛)
    • 일단 실행되면 실행 중인 다른 컨테이너와는 독립적인 스탠드얼론
    • 여러 컨테이너가 동일한 이미지를 기반으로 할 수 있지만, 서로 완전히 격리되어 있음
    • 디폴트로 공유 데이터나 상태가 없음
  • 레이어
    • 이미지의 내부 내용(context)에서의 레이어
    • 이미지의 모든 명령은 캐시 가능한 레이어를 생성
    • 이미지 재구축 및 공유를 도움

Docker를 사용하는 이유

What is a Container? | Docker

  • 프로그램의 특정 버전을 도커 컨테이너에 고정(lock)할 수 있으므로 코드가 항상 정확한 버전으로 실행되도록 할 수 있음
  • 협업 시 팀 내 서로 다른 개발 환경을 통일할 수 있음
  • 버전 충돌을 방지할 수 있음(작업 환경을 격리하기 때문)

Container와 Virtual Machine

Container

  • 코드와 종속성을 함께 패키징하는 앱 계층의 추상화
  • 여러 컨테이너가 동일한 머신에서 실행
  • 다른 컨테이너와 OS 커널을 공유할 수 있음
  • 각각 격리된 프로세스로 실행
  • VM보다 공간을 적게 차지함(컨테이너 이미지는 일반적으로 수십 MB 크키)
  • 더 많은 애플리케이션을 처리 및 더 적은 VM과 운영체제가 필요

Virtual Machine

  • 하나의 서버를 여러 서버로 바꾸는 물리적 하드웨어 추상화
  • 하이퍼바이저를 사용하면 단일 시스템에서 여러 VM을 실행할 수 있음
  • 각 VM에는 운영체제, 애플리케이션, 필수 바이너리 및 라이브러리의 전체 복사본 포함(수십 GB를 차지)
  • VM의 부팅 속도가 느려질 수 있음

Docker Engine

  • 도커를 실행하는데 필요한 Linux를 호스팅하는 가상머신에 설정
  • 가상 머신이 필요한 이유는 운영체제가 기본적으로 도커를 지원하지 않기 때문
  • 운영체제가 도커를 지원한다면 가상 머신이 필요없음
  • 가상 머신에서 컨테이너가 실행

Docker 명령어

Dockerfile을 이미지로 생성(빌드)

실행하는 현재 디렉토리에서 Dockerfile을 찾아서 빌드

docker build .

💡 이미지는 읽기 전용이므로 만약 소스 코드를 수정했다면 이미지를 새로 생성(다시 빌드)해야 함!!

빌드한 이미지를 실행

성공적으로 빌드한 후 이미지 결과물에 대한 ID를 얻으면 ID를 사용해 다음 명령어로 이미지 기반 컨테이너를 실행할 수 있음

이미지 ID 예: writing image sha245:<이미지ID>

docker run <이미지ID>

💡 실행하려는 이미지 설정에 컨테이너에서 통신하려는 포트가 있다면 실제로 실행하려는 컨테이너에 해당 포트를 게시(publish)해야 한다. 컨테이너와 호스트 운영 체제 사이에는 디폴트 연결이 없다.

# docker run -p <액세스 하려는 로컬 포트번호>:<내부 도커 컨테이너 노출 포트번호> <이미지ID>
docker run -p 3000:80 <이미지ID> # 로컬의 3000 포트로 도커 컨테이너 내 노출된 80번 포트로 연결

컨테이너 중지

새 터미널을 열고 다음 명령어를 입력하면 실행 중인 모든 컨테이너를 보여줌

  • ps는 프로세스를 표시(실행 중인 컨테이너)
  • -a는 도커가 생성한 모든(all) 컨테이너, 모든 프로세스를 표시(종료된 컨테이너 등)
docker ps
docker ps -a

다음 명령어를 입력하면 컨테이너가 중지되고 종료됨

컨테이너 이름은 자동으로 할당되는 이름

docker stop <컨테이너 이름>

컨테이너 내부에서 호스팅 머신으로 대화형 세션을 노출(Nodejs)

기본 노드 명령을 실행할 수 있는 인터렉티브 노드 터미널에 들어가게 됨

docker run -it node

Docker 이미지 파일 생성을 위한 Dockerfil 생성 1

Dockerfile이란 이름의 확장자 없는 파일 생성

# 일반적으로 FROM으로 시작
FROM <이미지 이름>

# 도커 컨테이너의 작업 디렉토리 설정
# 도커에게 모든 후속 명령(이후 명령들)이 작업 디렉토리 내부에서 실행될 것임을 알림
WORKDIR /app

# COPY: 도커에게 로컬 머신에 있는 파일이 이미지에 들어가야 하는지 알려줌
# 첫 번째 .은 컨테이너의 외부, 이미지의 외부 경로로 이미지로 복사되어야 할 파일들이 있는 경로를 가리킴
# 도커에게 기본적으로 Dockerfile이 포함된 동일한 폴더, 즉 프로젝트의 모든 폴더 및 하위 폴더와 파일들을 복사해야 함을 뜻함
# 두 번째 .은 컨테이너 내부에 이미지를 저장할 경로
# 모든 이미지와 이미지를 기반으로 생성된 모든 컨테이너에는 로컬 머신의 파일 시스템에서 완전히 분리된 자체 내부 파일 시스템이 있음(도커 내부에 숨겨져 있음)
# 즉, 도커 컨테이너의 루트 엔트리를 사용하지 않고 사용자가 직접 서브 폴더를 지정하는 것이 좋음
# 하지만 위 WORKDIR에 작업 디렉토리(/app)을 설정했다면 .으로 입력해도 /app 디렉토리에 이미지가 복사 됨
COPY . /app

# 모든 로컬 파일을 이미지에 복사 후 이미지에서 RUN 명령을 수행
# 디폴트로 이러한 모든 명령은 도커 컨테이너 및 이미지의 작업 디렉토리에서 실행 됨
# 디폴트 작업 디렉토리: 컨테이너 파일 시스템의 루트 폴더
# 위에서 /app 폴더에 이미지를 복사하기 때문에 
RUN <실행할 명령(예: npm install)>

# EXPOSE: 컨테이너가 시작될 때 로컬 시스템에 특정 포트를 노출하고 싶다는 것을 도커에게 알림
# 실제 포트 노출은 docker run에서 -p를 사용하여 노출
# EXPOSE는 컨테이너의 프로세스가 이 포트를 노출할 것임을 문서화 하는 것으로 모범적인 사용법 
EXPOSE 80

# 모든 작업이 완료되어 서버를 실행해야 할 때
# 이미지는 컨테이너의 템플릿어야 함을 명심해야 함
# 이미지를 실행하는 것이 아닌 이미지를 기반으로 컨테이너를 실행하는 것
# RUN node server.js는 잘못된 명령어
# CMD: 이미지가 생성될 때 실행되지 않고 이미지를 기반으로 컨테이너가 시작될 때 명령어를 실행
# 도커에게 이미지를 기반으로 컨테이너가 생성될 때마다 그 컨테이너 내부에 있는 node 명령어를 사용하여 server.js 파일을 실행하도록 지시
# CMD에 특정하지 않으면 베이스 이미지가 실행되며, 그게 없는 경우 에러 발생
CMD ["node", "server.js"]

Docker 이미지 파일 생성을 위한 Dockerfil 생성 2 (***)

Dockerfile이란 이름의 확장자 없는 파일 생성

💡 소스 코드 변경 후 재 빌드 시 캐시 효과를 보기 위해 다음과 같이 작성을 추천!!

FROM <이미지 이름>

WORKDIR /app

COPY package.json /app

RUN <실행할 명령(예: npm install)>

----------------------------- 
# 소스 코드만 변경한 경우 npm install은 다시 실행될 필요가 없으므로
# 위 코드들은 캐시된 결과를 사용하고 아래 명령어(복사)부터는 다시 실행된다. (소스 코드에 변경된 부분이 있기 때문) 
# *** 변경이 있는 부분부터 다음 명령어들은 모두 다시 실행 ***
-----------------------------

COPY . /app

EXPOSE 80

CMD ["node", "server.js"]
  • 도커는 동일한 상태(동일한 작업 디렉토리, 동일한 소스코드 등 변경된 파일이 없을 때)로 빌드할 때 출력되는 내용을 보면 Using cache(캐쉬 사용)이라는 메시지를 볼수 있다.
  • 도커는 기본적으로 모든 명령어에 대해 명령어를 다시 실행했을 때의 결과가 이전과 동일하다고 인식되면 캐시된 결과를 사용하는데 이것을 레이어 기반 아키텍처라고 함 (이미지를 빌드할 때마다 도커는 모든 명령 결과를 캐시)
  • 모든 명령어를 기반으로 하는 이미지 레이어는 레이어를 생성하고, 이러한 레이어는 캐시된다.
  • 이미지를 기반으로 컨테이너를 실행하면 그 컨테이너는 기본적으로 Dockerfile에 지정한 명령을 실행한 결과로 코드를 실행 중인 애플리케이션인 이미지 위에 새로운 추가 레이어를 추가한다.
  • 이렇게 하면 이미지를 레이어로 실행할 때만 활성화되는 최종 레이어가 추가된다.
  • 최종 명령 이전의 모든 명령은 이미 이미지의 일부이지만 별도의 레이어이다.
  • 아무것도 변경되지 않으면 이러한 모든 레이어를 캐시에서 사용할 수 있다.
  • 변경되는 부분이 있으면 캐시의 일부 결과만 사용하기 때문에 빌드 시간이 오래 걸린다.
  • 위 캐시의 작업 디렉토리 명령 결과를 사용했지만 복사 명령(COPY)의 경우 다시 실행해야 됨을 알아채고 모든 파일을 다시 복사한다.(변경된 파일에 대한 심층 분석 X)
  • 결론은 도커는 다시 실행해야 하는 항목만 다시 빌드 및 실행해 이미지 생성 속도를 높이기 위해 존재(매우 유용한 매커니즘)

Docker ID 사용

  • docker의 명령어를 수행할 때 항상 전체 ID를 복사할 필요 X
  • ID의 첫 번째 또는 몇 개의 문자로 고유 식별자를 구분할 수 있으면 됨
  • 예를 들어, “a”로 시작하는 다른 이미지 ID가 없으면 docker run a로 실행할 수도 있음
  • 이는 ID를 필요로 하는 모든 Docker의 명령어에 적용됨

기존 중지된 컨테이너 실행

docker run 명령어는 실행 시 터미널을 차단하지만 docker start는 차단하지 않음(컨테이너는 백그라운드로 실행됨)

  • docker start로 시작하는 경우 detached(분리) 모드가 디폴트
  • docker run으로 실행하는 경우 attached(연결) 모드가 디폴트
  • 연결 모드는 콘솔에 무언가 기록되고 분리 모드는 기록되지 않음
docker start <컨테이너 ID or 이미지 이름>
docker start -a <컨테이너 ID or 이미지 이름> # attached 모드

💡 docker run으로 시작할 때 -d 옵션을 주면 detached 모드로 설정

💡 분리된 컨테이너를 다시 연결할 때는 docker container attach <컨테이너 이름>

컨테이너에 출력되는 log를 가져오기

출력된 과거의 로그를 확인할 수 있음

  • -f 옵션을 추가하면 follow 모드로 진입, 계속 수신 대기를 할 수 있음
docker logs <컨테이너 이름>

이미 실행 중인 컨테이너에 연결하기

  • 디폴트로 -d 없이 컨테이너를 실행하면, attached 모드로 실행된다.
  • detached 모드로 컨테이너를 시작한 경우에는 다음 명령을 사용해 컨테이너를 다시 시작하지 않고도 컨테이너에 연결할 수 있다.
docker attache <컨테이너 ID or 컨테이너 이름>

컨테이너에 입력 및 출력(상호작용이 필요한 경우)

  • -i, --interactive: 표준 입력을 열린 상태로 유지
  • -t: pseudo tty가 할당, 터미널을 생성
docker run -i -t <컨테이너 ID or 컨테이너 이름>
docker run -it <컨테이너 ID or 컨테이너 이름>
docker start -a -i <컨테이너 ID or 컨테이너 이름>