1. Docker Volume

필요성

  • Docker Container가 실행되었다면 중단되면 데이터들은 어떻게 될까?
  • 특정 소프트웨어가 컨테이너를 통해 일회성으로 동작하는 것이 아니라 계속해서 동작해야 한다면 데이터가 영구적으로 보관되어야 함
    • ex) 데이터베이스가 컨테이너 안에서 동작한다면?
  • 이를 보장하는 기능이 Docker Volumes => Docker Persistence
  • Docker Container 내의 가상 파일 시스템과 호스트 시스템의 파일 시스템을 맵핑
    • ex) 호스트 파일 시스템의 /home/bokyung/logs를 Docker Contiainer의 /var/lib/airflow/logs로 맵핑
    • 이 경우 Docker Container가 중단되더라도 모든 Airflow log는 기록이 남게 됨
  • Docker Container로 MySQL을 실행하는 경우 데이터가 저장되는 공간을 Docker Volume으로 설정

Docker Volume

  • 호스트 파일 시스템 폴더를 Docker Container 파일 시스템의 폴더로 마운트하는 것
    • 호스트쪽에서 내용을 바꾸면 바로 Docker Container쪽에도 반영됨
    • 이를 통해 Docker Container의 상태와 상관없이 데이터를 영구적으로 보관
  • 파일 시스템의 마운트
    • 원래 마운트란 디스크와 같은 물리적인 장치를 파일 시스템의 특정 위치(폴더)에 연결해주는 것을 말함

Docker Volume 타입

  • Host Volumes : docker run -v를 실행할 때 페어로 지정
    • docker run -v [host folder]:[container folder]
    • 호스트 파일 시스템 path : 컨테이너 파일 시스템 path
  • Anonymous Volumes : docker run -v를 실행할 때 컨테이너 경로만 지정
    • docker run -v [container folder]
    • 데이터는 영구적으로 저장하고 싶지만, 호스트 파일 시스템에서 그 내용을 볼 일은 없는 경우
    • Dockerfile에서 사용되는 방식
    • 호스트쪽에 액세스되지는 않지만 재시작해도 유지됨
  • Named Volumes : docker run -v를 실행할 때 이름과 컨테이너 경로만 지정
    • docker run -v name:[container folder]
    • 가장 선호되는 방식!
    • 이 방식이 하나의 Volume을 다수의 컨테이너에서 공유하는 것도 가능하게 해줌
    • 이 포맷으로 뒤에서 살펴볼 docker-compose에서도 사용됨


  • Volume을 Readonly로 지정하고 싶다면
    • path에 :ro 옵션 추가
    • name:/var/lib/mysql/date:ro

이미지 생성시 Docker Volume 사용법

  • Dockerfile
    • VOLUME 명령으로 Anonymous Volume만 사용 가능
  • docker-compose
    • Host Volume이나 Named Volume을 사용하는 것이 일반적

Docker Volume 사용 예 1) nginx 실행

  • nginx
    • 경량화된 웹서버
    • 많은 경우 로드밸런서로 사용됨
  • docker run -d --name=nginx -p 8081:80 nginx
  • 브라우저 방문: http://localhost:8081/
  • docker exec --user=root -it nginx sh
    • apt update
    • apt install nano
    • nano /usr/share/nginx/html/index.html 내용 편집
    • exit
  • docker restart nginx
  • 위 과정을 반복해 /usr/share/nginx/html/index.html 내용 확인시 원상복구된 것을 볼 수 있음

Docker Volume 사용 예 2) -v 옵션과 함께 nginx 실행

  • ls -tl /Users/jobox/Downloads/grepp/kdt/nginx/html
    • index.html
    • test.html
  • docker run -p 8081:80 -d --name nginx_demo -v /Users/jobox/Downloads/grepp/kdt/nginx/html:/usr/share/nginx/html nginx
  • http://localhost:8081/
  • index.html 내용을 수정하고 브라우저에서 재방문
  • nginx를 재시작하고 내용이 유지되는지 확인

Docker Volume을 docker-compose.yml에서 사용한 예

  • Airflow 예
volumes:
    - ${AIRFLOW_PROJ_DIR:-.}/dags:/opt/airflow/dags
    - ${AIRFLOW_PROJ_DIR:-.}/logs:/opt/airflow/logs
    - ${AIRFLOW_PROJ_DIR:-.}/plugins:/opt/airflow/plugins


  • 호스트 파일 시스템: airflow-setup/dags
    • MySQL_to_Redshift_v2.py
    • UpdateSymbol_v3.py
    • UpdateSymbol.py
  • Docker Container 파일 시스템: /opt/airflow/dags
    • docker exec [container id] ls -tl /opt/airflow/dags
    • MySQL_to_Redshift_v2.py
    • UpdateSymbol_v3.py
    • UpdateSymbol.py
  • 위 둘의 내용이 동일해야 함!

2. Docker 환경 클린업

커맨드라인

  • Docker Desktop에서 모두 삭제하는 것이 가장 직관적이긴 함
    • 이미지를 삭제하려면 먼저 실행 중인 컨테이너가 없어야 함
    • 먼저 컨테이너를 중단하고 다음으로 이미지 삭제
  • 컨테이너 삭제
    • 원래) docker container ls -> docker container rm [container id]
      • 동시에 삭제도 가능
      • but 여러 개를 삭제할 때 모두 copy하는 것이 번거로움
    • 한번에) docker container rm -f ${docker container ls -aq}
      • -q 옵션: ID만 반환
  • 이미지 삭제
    • 원래) docker image ls -> docker image rm [image id]
    • 한번에) docker image rm -f ${docker image ls -q}
  • 다 삭제되었는지 확인
    • docker ps
    • docker images


스크린샷 2023-12-22 오후 6 46 30

전부 삭제됨!

zsh: bad substitution 오류

  • MAC 터미널에서 ${} 사용 시 해당 오류 발생
  • $() 으로 사용해야 함!

스크린샷 2023-12-22 오후 6 44 47

Desktop

  • 가장 직관적
  • [Troubleshoot 메뉴] - [Clean/Purge data] 선택

스크린샷 2023-12-22 오후 6 40 59

3. 다수의 Container로 구성된 소프트웨어 실행 (voting application)

Voting application

https://github.com/dockersamples/example-voting-app

  • Docker에서 제공해주는 예제 프로그램

스크린샷 2023-12-22 오후 6 48 06

build

git clone https://github.com/dockersamples/example-voting-app
cd example-voting-app

docker build -t vote ./vote
docker build -t result ./result
docker build -t worker ./worker

docker images

스크린샷 2023-12-24 오전 3 42 50

네트워크 관련 이슈

  • vote/app.py
def get_redis():
    if not hasattr(g, 'redis'):
        g.redis = Redis(host="redis", db=0, socket_timeout=5)
    return g.redis
  • vote에 로그인해서 iputils-ping 설치 후 ping 명령으로 redis 호스트 이름이 연결되는지 확인
ping redis

스크린샷 2023-12-24 오전 4 12 19


  • result/server.js
var pool = new pg.Pool({
    connectionString:
    'postgres://postgres:postgres@db/postgres'
})
  • Postgres 연결 시 postgres:postgres를 사용하고 있음을 주의깊게 볼 것!


  • worker/Program.cs
var pgsql = OpenDbConnection("Server=db;Username=postgres;Password=postgres;");
var redisConn = OpenRedisConnection("redis");

network 이슈 해결

  • docker의 네트워크 기능 사용
    • 이전에는 docker run의 link 옵션 사용
  • network를 하나 만들고 모든 컨테이너들을 이 네트워크 안으로 지정
    • 연결 상황에 따라 별개의 네트워크를 만들어 사용도 가능함
      • back-tier
      • front-tier
    • 매뉴얼 예제에서는 mynetwork을 하나 만들고 진행 예정

run

  • 일일히 하나씩 실행하면 각 컴포넌트들 간의 네트워크 연결이 되지 않음!
docker network create mynetwork
docker run -d --name=redis --network mynetwork redis
docker run -d --name=db -e POSTGRES_PASSWORD=pwd --network mynetwork postgres
docker run -d --name=vote -p 5001:80 --network mynetwork vote
docker run -d --name=result -p 5002:80 --network mynetwork result
docker run -d --name=worker --network mynetwork worker   
  # 외부에서 access할 필요가 없는 모듈 -> 포트 맵핑 X

docker container ls

스크린샷 2023-12-24 오전 3 48 58


http://localhost:5001

스크린샷 2023-12-24 오전 4 07 32

스크린샷 2023-12-24 오전 4 14 12


http://localhost:5002

스크린샷 2023-12-24 오전 4 08 56