코딩하고자용 블로그

Docker docs 정리 (2) - Get Started 본문

Computer Science/Server

Docker docs 정리 (2) - Get Started

코딩하고자용 2022. 8. 24. 19:57

본 글은 Docker docs 내용을 한글로 작성해가면서 이해하고자 하였으며, 이에 대한 결과물입니다.

글 내용 중 필자의 생각을 첨가하여 docs와 완전히 일치하지 않음을 알려드립니다.

따라서 정확한 이해를 위해 아래의 docs를 정독하시고, 참고용으로만 사용하시길 바랍니다.

https://docs.docker.com/
 

Docker Documentation

 

docs.docker.com


Start the tutorial

docker run -d -p 80:80 docker/getting-started
# -d : detached mode(in the background)
# -p 80:80 : 호스트의 port 80번과 컨테이너의 port 80qjsdmf aovld
# docker/getting-started : 사용할 이미지 예시
# -dp 라고 한번에 적어도 됨.
  • Docker Desktop을 통해, 컨테이너의 shell에 접근하여 라이프사이클의 변동을 줄 수 있다.

What is a container?

  • host machine의 다른 모든 프로세스와 격리된 machine의 샌드박스? 프로세스.
  • image의 instance. DockerAPI or CLI를 통해 create, start, stop, move, delete 등 조작 가능
  • 모든 os에서 실행 가능

Sample application

  • 간단한 TO-DO List를 통해 사용법을 익혀보자

  • 앱 빌드를 위해, DockerFile을 사용해야 한다.
  • DockerFile은 컨테이너 이미지를 만들기 위해 사용되는 스크립트이다.
  • 다운받은 프로젝트의 container image를 빌드해보자.
docker build -t getting-started .
  • 위 명령어로 도커파일을 통해 새 컨테이너 이미지를 생성한다.
  • -t : 이미지에 태그 지정. 최종 이미지의 name이라 생각하자.
  • 위 명령으로 이미지가 생성되었으므로, app을 실행해보자.(컨테이너 생성)
docker run -dp 3000:3000 getting-started
  • 포트 매핑없이는, app 접근이 불가함을 명심하자.
  • 몇 초 후, http://localhost:3000 을 열면, 앱이 열리고, item을 추가/삭제가 잘 될 것이다.

생성된 container

Update the application

  • app의 update가 일어날 때, 이에 따라 컨테이너도 update 될 필요가 있을 것이다.
  • 이미지를 재빌드하더라도, 컨테이너가 업데이트된 것이 아니기에, 현재 실행 중인 컨테이너의 제거가 필요하다.
docker ps
# docker ps를 통해 container-id를 알아낸다.
docker stop <the-container-id>
docker rm <the-container-id>
# 두가지 일을 동시에 할 수도 있다.
docker rm -f <the-container-id>
# 삭제 후, 다시 컨테이너를 생성하면 된다.
docker run -dp 3000:3000 getting-started

Share the application

  • 이미지를 docker registry를 통해 공유해보자.
docker login -u YOUR-USER-NAME
docker tag getting-started YOUR-USER-NAME/getting-started
# docker tag를 통해 image에게 새로운 name을 부여
docker push YOUR-USER-NAME/getting-started

Persist the DB

  • 컨테이너가 실행될 때 파일 시스템에 대한 이미지의 다양한 계층을 사용한다
  • 각 컨테이너에는 파일 생성/업데이트/제거를 위한 자체 scratch space도 있다. 따라서 동일한 이미지를 사용한들 변경 사항은 다른 컨테이너에서 볼 수 없다
# 참고: container에서 terminal로 바로 명령을 실행하기 위해, 아래와 같이 사용할 수도 있다.
# docker ps를 통해 container-id를 알아내도록 하자
docker exec <container-id> cat /data.txt
  • 이에 도커에서는 Volume을 사용한다.
  • Volume : 컨테이너의 특정 파일 시스템 경로를 호스트 시스템에 다시 연결하는 기능을 제공
  • 해당 디렉토리의 변경 사항을 호스트 시스템에서도 볼 수 있게 되는 것
  • named volumes
    • /etc/todos/todo.db에 있는 SQLite 데이터베이스에 데이터를 저장한다고 하자.
    • 이 파일을 다른 host에서도 접근이 가능하다면, persist할 수 있을 것이다.
    • name volume을 단순히 데이터 버킷(bucket of data)로 생각해보자
    • 도커는 이 물리적인 공간을 유지해줄 것이고, 우리는 volume의 이름만 기억하면 된다
docker volume create todo-db
# volume mount를 명명해주면서 container를 생성해보자.
docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started
# data가 쌓이는 곳에 대한 정보를 알기 원한다면
docker volume inspect todo-db
# 결과
[
    {
        "CreatedAt": "2022-08-23T07:04:43Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/todo-db/_data", # 데이터가 저장되는 디스크의 실제 위치
        "Name": "todo-db",
        "Options": {},
        "Scope": "local"
    }
]

 

  • bind mounts
    • named volumes과 달리 호스트에서 정확한 Mountpoint를 제어할 수 있다.
  Named Volumes Bind Mounts
Host Location Docker chooses You control
Mount Example(using -v) my-volume:/usr/local/data /path/to/data:/usr/local/data
Populates new volume with container contents Yes No
Support Volume Drivers Yes No
  • 개발 workflow를 지원하기 위한 컨테이너를 실행해보자.
# m1 mac 기준
docker run -dp 3000:3000 \
     -w /app -v "$(pwd):/app" \
     node:12-alpine \
     sh -c "apk add --no-cache python2 g++ make && yarn install && yarn run dev"
# -w /app : working directory 설정
# node:12-alpine : 사용할 이미지

# 컨테이너 생성 후 log 확인
docker logs -f <container-id>
  • 이후에 변경사항이 있다면 변경 후 컨테이너를 중지하고 새 이미지를 빌드하면 refresh를 통해 간단히 로컬 개발 가능!
  • 이처럼 Bind Mount를 사용하는 것은 로컬 개발 설정에서 매울 일반적이다.
  • 장점 : 개발 머신에 모든 빌드 도구와 환경을 설치할 필요가 없음!
  • Docker Compose를 통해 명령을 단순화할 수 있음(추후 설명)

Multi container app

  • 지금까지는 단일 컨테이너 앱으로 작업해왔지만 이번엔 MySQL 자체를 추가해보고자 한다.
  • 음? MySQL은 그럼 어디서 실행되는거에요? 컨테이너에 설치해서 사용해야되나요?
  • 답변은 No. 각 컨테이너는 한 가지 작업을 수행하고 잘 수행하게 설계해야된다.
    • API와 Frontend를 DB와 다르게 확장해야 할 가능성이 큼
    • 별도의 컨테이너를 사용하여 버전을 격리하고 업데이트할 수 있음
    • 다중 프로세스 실행은 process manager를 필요로 해서
  • Container networking
    • 컨테이너는 기본적으로 격리되어 실행되며 동일한 시스템의 다른 프로세스나 컨테이너에 대해 알지 못함.
    • 각각의 컨테이너들이 통신하도록 -> Networking!
  • 네트워크에 컨테이너를 두는 방법은 두 가지이다.(두 컨테이너가 같은 네트워크에 없다면 통신할 수 없다)
    1. 시작할 때 할당
    2. 기존 컨테이너를 연결
docker network create todo-app
docker run -d \
     --network todo-app --network-alias mysql \
     --platform "linux/amd64" \
     -v todo-mysql-data:/var/lib/mysql \
     -e MYSQL_ROOT_PASSWORD=secret \
     -e MYSQL_DATABASE=todos \
     mysql:5.7
# docker가 volume을 자동 생성해줌
docker exec -it <mysql-container-id> mysql -u root -p
mysql> SHOW DATABASES;
  • connect to MySQL
    • MySQL 컨테이너를 생성하였다면, 이제 사용해보자.
    • 앱을 MySQL이라는 호스트에 연결하기만 하면 됨.
docker run -it --network todo-app nicolaka/netshoot
dig mysql # dig? 유용한 DNS tool
#answer section을 통해 IP를 알 수 있다
  • TODO 앱은 MySQL 연결 설정을 지정하기 위해 몇 가지 환경 변수 설정을 지원한다.
    • MYSQL_HOST
    • MYSQL_USER
    • MYSQL_PASSWORD
    • MYSQL_DB
  • 이러한 환경 설정을 통해 나의 MySQL 환경에 접근할 수 있다.
docker run -dp 3000:3000 \
   -w /app -v "$(pwd):/app" \
   --network todo-app \
   -e MYSQL_HOST=mysql \
   -e MYSQL_USER=root \
   -e MYSQL_PASSWORD=secret \
   -e MYSQL_DB=todos \
   node:12-alpine \
   sh -c "yarn install && yarn run dev"

Use Docker Compose

  • Docker Compose : 다중 컨테이너 애플리케이션(multi-container application)을 정의하고 공유하는 것을 돕는 tool
  • compose로 파일에 application stack을 정의할 수 있고, project repo의 루트에 보관할 수 있으며, 다른 사람이 쉽게 프로젝트에 기여할 수 있다.
  • docker desktop 설치 시, 함께 Docker Compose가 설치된다.
 docker-compose version
  • 이제, compose file을 생성해보자.
  • app project의 root에 docker-compose.yml을 생성하자.
 version: "3.7" # schema vesion 설정

 services: # 우리의 앱에서 실행되기를 원하는 컨테이너(서비스) 리스트
   app:
     image: node:12-alpine
     command: sh -c "yarn install && yarn run dev"
     ports:
       - 3000:3000 #service에 대한 port 정의
     working_dir: /app # 작업 디렉토리
     volumes: # volume mapping
       - ./:/app
     environment:
       MYSQL_HOST: mysql
       MYSQL_USER: root
       MYSQL_PASSWORD: secret
       MYSQL_DB: todos
  • MySQL 서비스 정의
 version: "3.7"

 services:
   app:
     # The app service definition
   mysql:
     image: mysql:5.7
     volumes:
       - todo-mysql-data:/var/lib/mysql
     environment:
       MYSQL_ROOT_PASSWORD: secret
       MYSQL_DATABASE: todos

 volumes:
   todo-mysql-data:
  • 이처럼 docker-compose.yml 작성을 완료하였다면, 이를 실행시켜보자.
  • app/db copy본이 실행되고 있어선 안됨을 명심하자.
  • 아래의 명령으로 application stack을 실행한다.
 docker-compose up -d # -d : background 실행
 
 #output
 Creating network "app_default" with the default driver
 Creating volume "app_todo-mysql-data" with default driver
 Creating app_app_1   ... done
 Creating app_mysql_1 ... done

Image-building best practices

  • 이미지를 생성했을 때 docker scan 명령을 사용하여 보안 취약점을 스캔하는 것이 좋다.
  • 또한, docker image history 명령을 사용해 이미지 내에서 각 레이어를 생성하는데 사용된 명령을 볼 수 있음.
  • 컨테이너의 이미지 빌드 시간을 줄이려면 어떻게 해야 좋을까?
    • 우리가 이미지를 변경할 때, yarn dependencies를 다시 설치해야하는 번거로움이 있다.
    • 이를 해결하기 위해 종속성 캐싱을 지원하도록 Dockerfile을 재구성해야한다.
    • .dockerignore 파일은 이미지 관련 파일만 선택적으로 복사하는 쉬운 방법임.
    • 이를 활용하면, 빌드가 훨씬 빨라질 것이다!
  • 특히, 다단계 빌드(multi-stage build)에서 이미지를 생성할 때 훨씬 효과적이다.
  • Maven/Tomcat 예제
    • Java 기반 응용 프로그램을 빌드할 때 소스 코드를 Java 바이트 코드로 컴파일하려면 JDK가 필요함.
    • 그러나 해당 JDK는 프로덕션엔 필요하지 않음.
    • 또한 Maven이나 Gradle과 같은 도구를 사용하여 앱을 빌드할 수도 있다.
# syntax=docker/dockerfile:1
FROM maven AS build
WORKDIR /app
COPY . .
RUN mvn package

FROM tomcat
COPY --from=build /app/target/file.war /usr/local/tomcat/webapps 
  • React 예제
# syntax=docker/dockerfile:1
FROM node:12 AS build
WORKDIR /app
COPY package* yarn.lock ./
RUN yarn install
COPY public ./public
COPY src ./src
RUN yarn run build

FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html

'Computer Science > Server' 카테고리의 다른 글

ElasticStack에 대한 정리  (0) 2023.04.22
Docker docs 정리 (1) - Overview  (0) 2022.08.22