시작하기에 앞서, 컨테이너와 이미지를 모두 삭제하는 명령어를 짚고 넘어가겠다.
이전에 실습하던 컨테이너와 이미지들이 있다면, 다음과 같은 명령어를 통해 삭제하여 정리하는 것을 추천한다.
0. 컨테이너와 이미지 모두 삭제
도커 환경에서 사용하지 않는 컨테이너와 이미지를 삭제하는 과정이다. 이를 통해 저장 공간을 확보하고, 불필요한 리소스를 정리할 수 있다.
- 모든 컨테이너 중지:이 명령어는 현재 실행 중인 모든 컨테이너를 중지시킨다.
docker stop $(docker ps -a -q)
- 모든 컨테이너 삭제:중지된 모든 컨테이너를 삭제한다. 실행 중인 컨테이너는 삭제할 수 없기 때문에, 먼저 중지해야 한다.
docker rm $(docker ps -a -q)
- 모든 이미지 삭제:모든 도커 이미지를 삭제하는 명령어다. 삭제할 이미지가 사용 중일 경우, 삭제되지 않으므로 주의해야 한다.
docker rmi $(docker images -q)
1. docker-compose 명령어
1) 주의
docker-compose
명령어는 docker-compose
관련 YAML 파일이 있는 디렉토리에서 수행해야 한다.
이 파일에는 여러 서비스의 설정이 포함되어 있어, 단일 명령으로 복수의 컨테이너를 관리할 수 있다.
2) 명령어: docker-compose help
docker-compose
의 기본 명령어와 사용법을 확인할 수 있는 도움말이다.
주요 명령어는 다음과 같다:
- build: Dockerfile을 이용해 이미지를 빌드하거나 재빌드하는 명령이다.
- config: 도커 컴포즈 구성 파일의 내용을 확인할 수 있다. 이는 오류를 찾거나 설정을 검토하는 데 유용하다.
- create: 지정된 서비스의 컨테이너를 생성한다.
- down: 도커 컴포즈 자원을 일괄 정지하고 삭제한다. 모든 서비스와 네트워크를 동시에 정리할 수 있다.
- events: 컨테이너에서 발생하는 실시간 이벤트를 수신하는 명령이다. 주로 클라우드 관리자가 사용한다.
- exec: 실행 중인 컨테이너에 명령을 실행할 수 있다. 예를 들어,
/bin/bash
명령으로 셸에 접속할 수 있다. - help: 각 명령어의 도움말을 출력한다.
- images: 현재 사용 중인 이미지의 정보를 출력한다.
- kill: 실행 중인 컨테이너를 강제로 중지시킨다.
- logs: 컨테이너의 실행 로그를 출력하여, 디버깅이나 상태 확인에 유용하다.
- pause: 실행 중인 컨테이너를 일시 정지한다.
- port: 컨테이너의 포트가 외부로 어떻게 바인딩되었는지 출력한다.
- ps: 현재 실행 중인 컨테이너와 서비스를 출력한다.
- pull: 서비스에 필요한 이미지를 Docker Hub 등에서 가져온다.
- push: 빌드된 이미지를 Docker Hub 등으로 업로드할 수 있다.
- restart: 컨테이너 서비스를 재시작한다. 설정 파일이 변경된 경우 유용하다.
- rm: 지정된 서비스를 삭제한다.
- run: 새로운 컨테이너를 생성하고 실행하며, 일회성 명령어를 실행할 수 있다.
- scale: 특정 서비스에 대해 컨테이너의 개수를 조절할 수 있다.
- start: 중지된 컨테이너 서비스를 시작한다.
- stop: 실행 중인 컨테이너 서비스를 중지한다.
- top: 실행 중인 프로세스의 정보를 출력한다.
- unpause: 일시 정지된 컨테이너 서비스를 재개한다.
- up: 지정된 서비스의 컨테이너를 생성하고 시작하는 명령이다.
-d
옵션을 추가하면 백그라운드에서 실행된다. - version: 현재 도커 컴포즈의 버전 정보를 표시한다.
실제 서비스를 구축할 때에는 도커 컴포즈를 이용하여 이벤트나 로그들을 모니터링하여 사용자에게 푸시 알림을 보낼 수 있다.
2. Flask와 Redis를 연동한 엑세스 카운팅 앱을 Docker Compose로 생성
1) 디렉토리 생성 및 프롬프트 이동
새로운 프로젝트를 위한 디렉토리를 생성하고, 해당 디렉토리로 이동하는 과정이다.
$ mkdir flask_redis && cd $_
2) 파이썬 파일 생성 및 작성
Flask 애플리케이션 코드를 담을 py_app.py
파일을 생성하는 과정이다.app
디렉토리를 만들고, 그 안에 파일을 생성한다.
$ mkdir app
$ nano app/py_app.py
이후 아래의 코드를 작성한다.
import time
import redis
from flask import Flask
py_app = Flask(__name__)
db_cache = redis.Redis(host='redis', port=6379)
def web_hit_cnt():
return db_cache.incr('hits')
@py_app.route('/')
def python_flask():
cnt = web_hit_cnt()
return '''<h1 style="text-align:center; color:deepskyblue;">docker-compose app: Flask & Redis</h1>
<p style="text-align:center; color:deepskyblue;">Good Container Service</p>
<p style="text-align:center; color:red;">Web access count: {} times</p>'''.format(cnt)
if __name__ == '__main__':
py_app.run(host='0.0.0.0', port=9000, debug=True)
위 코드는 기본적인 Flask 애플리케이션으로, Redis를 이용하여 웹 페이지에 접속한 횟수를 카운팅한다.hits
라는 키의 값을 증가시키는 함수 web_hit_cnt
를 정의하고, 이를 통해 카운트를 출력하는 간단한 웹 페이지를 생성한다.
3) requirements.txt 파일 생성
Flask와 Redis 라이브러리를 포함한 의존성 목록을 작성하는 과정이다.
$ nano app/requirements.txt
이 파일에 아래의 내용을 입력한다.
Flask
redis
4) Dockerfile 생성 및 작성
도커파일을 만드는 경우는 애플리케이션 코드를 직접 작성한 경우나 별도의 개발 환경을 만들기위해서이다.
기본 이미지를 바탕으로 소스 코드를 추가해서 새로운 이미지를 만들거나 팀 단위 개발에서 동일한 개발 환경을 만들고자 할 때 사용한다.
애플리케이션을 도커 이미지로 빌드하기 위한 Dockerfile
을 생성한다.
$ nano Dockerfile
이후 아래의 내용을 입력한다.
FROM python:3.8-alpine
RUN apk update && apk add --no-cache bash
RUN apk add --update build-base python3-dev py-pip
ENV LIBRARY_PATH=/lib:/usr/lib
ENV FLASK_APP=py_app
ENV FLASK_ENV=development
EXPOSE 9000
WORKDIR /py_app
COPY ./app/ .
RUN pip install -r requirements.txt
ENTRYPOINT ["python"]
CMD ["py_app.py"]
이 Dockerfile
은 파이썬 3.8을 기반으로 하여, Flask 애플리케이션을 실행하는 환경을 설정한다. 의존성 설치와 실행에 필요한 설정을 포함하고 있다.
- Apple M1 주의사항: 로컬에서는 잘 작동하지만, 클라우드에 배포할 경우 다른 운영체제에서는 작동하지 않는 호환성 문제가 발생할 수 있으니 주의해야 한다.
5) 앱 빌드 및 로컬 테스트
로컬에서 Redis와 Flask 애플리케이션을 실행하는 과정이다.
- 레디스 컨테이너 실행:이 명령어는 Redis 컨테이너를 실행하여, 로컬 환경에서 Redis를 사용할 수 있도록 한다.
$ docker run --name myredis -d -p 6379:6379 redis
- 애플리케이션 실행:로컬에서 Flask 애플리케이션을 실행한다. 이후 웹 브라우저에서
http://localhost:9000
에 접속하면 웹 페이지를 확인할 수 있다.
$ python ./app/py_app.py
- 에러 발생 시: Redis의 접속 위치를 자신의 IP로 수정해야 할 수도 있다.
6) Dockerfile 빌드 및 이미지 생성
도커 이미지를 빌드하여 애플리케이션을 컨테이너로 실행할 수 있는 상태로 만드는 과정이다.
- 이미지 빌드: 현재 디렉토리의
Dockerfile
을 기반으로 이미지를 빌드한다.
$ docker build -t flaskapp .
- 이미지가 생성되었는지 확인: 생성된 이미지를 확인할 수 있다.
$ docker images
- 이미지를 컨테이너로 실행: 이미지를 기반으로 컨테이너를 생성하고 실행한다.
$ docker run -d -p 9000:9000 --name=flaskapp flaskapp
- 컨테이너 확인:현재 실행 중인 컨테이너를 확인할 수 있다.
$ docker ps
- 브라우저에서 확인: 자신의 IP와 포트번호를 통해 웹 페이지를 확인할 수 있다.
- 자신의IP:9000
- 모든 컨테이너 중지: `docker stop $(docker ps -aq)`
7) docker-compose.yml 를 이용한 실행
이제 여러 서비스를 동시에 관리하기 위해 docker-compose.yml
파일을 생성하는 과정이다.
$ nano docker-compose.yml
이 파일에 아래의 내용을 작성한다.
version: '3'
services:
redis:
image: redis:alpine
container_name: myredis
ports:
- "6379:6379"
flask:
build: .
container_name: flaskapp
ports:
- "9000:9000"
depends_on:
- redis
이 docker-compose.yml
파일은 Redis와 Flask 서비스를 정의하며, 두 서비스가 서로 의존하는 관계를 설정한다.
8) 도커 컴포즈로 실행
하나의 docker-compose 파일에 묶인 서비스들은 하나의 네트워크로 만들어지게 된다.
이때 서브넷 마스크만 조정되는 것이 아니고, 하나의 DNS가 만들어져서 서비스 이름을 이용해서 접근할 수 있다.
docker-compose up -d
먼저, 현재 실행 중인 컨테이너를 제거한다.
docker stop $(docker ps -aq)
docker rm $(docker ps -aq)
소스코드에서 redis 접속 IP를 redis로 수정한 후 다시 실행한다.
import time
import redis
from flask import Flask
py_app = Flask(__name__)
db_cache = redis.Redis(host='redis', port=6379)
def web_hit_cnt():
return db_cache.incr('hits')
@py_app.route('/')
def python_flask():
cnt = web_hit_cnt()
return '''<h1 style="text-align:center; color:deepskyblue;">docker-compose app:
Flask & Redis</h1>
<p style="text-align:center; color:deepskyblue;">Good Container Service</p>
<p style="text-align:center; color:red;">Web access count : {} times</p>'''.format(cnt)
if __name__ == '__main__':
py_app.run(host='0.0.0.0', port=9000, debug=True)
docker-compose 파일을 수정해서 redis를 외부에 노출시키지 않고 사용할 수 있다.
동일 네트워크로 묶이면 IP 대신에 서비스 이름을 사용할 수 있고 `ports` 대신에 `expose`를 이용해서 동일 네트워크 내부에서만 접근하도록 할 수 있다.
`docker-compose.yml` 파일을 수정해보자.
version: '3'
services:
redis:
image: redis
expose:
- "6379"
restart: always
networks:
- our_net
flask:
build: .
ports:
- 9000:9000
restart: always
links:
- redis
depends_on:
- redis
networks:
- our_net
networks:
our_net: {}
3. docker-compose 주요 명령어 및 옵션
1) docker-compose up
옵션
- d(detach): 백그라운드 컨테이너 서비스를 실행하고 새로 생성된 컨테이너 이름을 화면에 출력
- build: 컨테이너 서비스를 시작하기 전에 이미지를 빌드하는 것으로 Dockerfile 이나 기타 소스 코드 변동이 있을 때 수행
- force-recreate: 도커 컴포즈 야믈 코드 및 이미지가 변경되지 않은 경우에도 컨테이너를 다시 생성
- t(timeout): 현재 실행 중인 컨테이너를 종료하는 경우 이 시간을 이용해서 타임아웃이 발생(기본값은 10)
- scale 서비스이름=개수: 컨테이너 서비스의 개수를 지정 수 만큼 확장
- f: docker-compose 파일의 경로가 다르거나 이름이 다를 때 이 옵션을 이용해서 지정
scale up down
디렉터리를 생성하고 이동한다.
[실습예제]
docker-compose.yml 파일을 생성하고 다음과 같이 작성한다.
version: '3.8'
services:
server_web:
image: httpd:2
server_db:
image: redis
docker-compose를 실행한다.
docker-compose up -d
docker-compose 실행 후 `docker-compose ps`로 컨테이너가 제대로 실행이 되고 있는지 확인한다.
그리고 다음 명령을 실행한다.
이 명령은 `server_db`와 `server_web` 두 서비스를 각각 3개의 인스턴스로 실행하여, 총 6개의 컨테이너를 생성하고 백그라운드에서 실행한다. 이를 통해 시스템의 처리 능력을 향상시키고, 동시에 여러 요청을 처리할 수 있는 환경을 만든다.
docker-compose up --scale server_db=3 --scale server_web=3 -d
- --scale server_db=3: server_db라는 서비스의 인스턴스를 3개 생성하라는 지시
- 이 옵션은 동일한 서비스의 여러 개의 복제본을 실행하여 부하 분산이나 가용성을 높일 수 있게 해준다.
- --scale server_web=3: server_web이라는 서비스의 인스턴스를 3개 생성하라는 지시
- 마찬가지로 이 옵션은 server_web 서비스의 여러 복제본을 실행하게 된다.
- -d: 이 플래그는 서비스를 백그라운드에서 실행하도록 한다. 즉, 터미널을 차지하지 않고 서비스가 실행된다. 사용자는 터미널에서 다른 작업을 계속할 수 있다.
아래는 각 명령에 대한 보충 설명이다:
2) docker-compose down
이 명령은 실행 중인 모든 컨테이너 서비스를 정지하고 삭제하는 데 사용된다.
이때, 해당 서비스에 속하는 네트워크와 볼륨도 함께 제거된다. 이를 통해 개발 중에 필요하지 않은 자원들을 깨끗하게 정리할 수 있다.
--rmi all
: 이 옵션을 추가하면, 사용했던 이미지도 삭제된다. 즉, 컨테이너와 함께 관련된 이미지까지 정리하여 디스크 공간을 확보하는 데 도움을 준다.--volumes
: 이 옵션을 사용하면, 생성된 모든 볼륨도 삭제된다. 볼륨은 데이터의 지속성을 위해 사용되지만, 이 옵션을 통해 볼륨까지 함께 제거하여 불필요한 데이터 저장소를 정리할 수 있다.
3) docker-compose stop
이 명령은 특정 컨테이너를 중지하는 데 사용된다.
모든 컨테이너를 정지하는 것이 아니라, 사용자가 지정한 특정 서비스만을 멈출 수 있는 유용한 방법이다.
예를 들어, 서비스에 문제가 발생했거나 유지보수가 필요할 때 사용된다.
4) docker-compose start
이 명령은 이전에 정지된 컨테이너를 다시 시작하는 데 사용된다.
중지된 상태의 서비스를 다시 활성화하고, 이전에 사용했던 설정과 상태를 유지하면서 실행된다.
이를 통해 서비스의 연속성을 보장할 수 있다.
5) docker-compose logs
이 명령은 실행 중인 서비스의 로그를 출력한다.
각 서비스에서 발생한 로그를 확인하여 서비스 상태를 파악하거나 문제를 진단하는 데 유용하다. 로그는 개발 및 운영 환경에서 중요한 정보를 제공하므로, 주기적으로 확인하는 것이 좋다.
-f
옵션: 이 옵션을 추가하면, 로그를 실시간으로 출력하는 기능이 활성화된다. 따라서 로그가 새로 추가될 때마다 즉시 확인할 수 있어, 실시간 모니터링 및 문제 해결에 유리하다.