💡 django, db, web server 등 다양한 어플리케이션을 컨테이너 환경에서 테스트 및 배포할 수 있는 소프트웨어 플랫폼이다.
<가상머신과 컨테이너 환경의 차이>
sudo apt install docker.io -y
관리자 권한(sudo)으로 패키지를 설치할 건데 이 패키지를 진짜 설치할거냐라고 물어보는 과정을 생략하고 이 패키지를 무조건 설치하겠다.(-y 옵션)
만약 아래와 같은 에러가 발생한다면
E: Package 'docker.io' has no installation candidate
sudo apt update 명령어 실행 후 docker 패키지를 다시 설치해야 한다!!
doker는 관리자 권한으로만 동작하기 때문에 항상 sudo를 붙여서 실행시켜줘야 한다!
sudo docker --version
# 도커 버전이 출력되는지 확인한다.
Docker version 20.10.12, build 20.10.12-0ubuntu2~20.04.1
- 정상
command not found: docker
와 같은 문구가 출력될 경우 docker가 설치되었는지 확인해야 한다.
sudo docker run -d -p 80:80 httpd:latest
run : 이미지를 사용해 컨테이너를 실행
-d : 컨테이너를 데몬(백그라운드)으로 실행
80:80 : 80번 포트로 접속했을 때 컨테이너에 접근할 수 있도록 포트포워딩을 설정
httpd:latest : httpd의 가장 최신 이미지를 사용해 컨테이너를 생성
sudo docker ps
# 실행중인 컨테이너 목록 확인하기
CONTAINER ID : 컨테이너가 가지고 있는 고유한 id
IMAGE : 컨테이너가 생성될 때 사용된 이미지
COMMAND : 컨테이너가 생성될 때 실행되는 명령어
CREATED : 생성 후 경과 시간
STATUS : 컨테이너 상태
PORTS : 사용중인 포트
sudo docker ps -a
-a : 중지된 컨테이너 목록까지 포함해서 모두 확인하기
sudo docker images
REPOSITORY : 이미지 저장소 이름
TAG : 이미지 버전
IMAGE ID : 이미지의 고유한 id
CREATED : 이미지 생성일(마지막 업데이트 일)
SIZE : 이미지 용량
sudo docker exec -it $container_id /bin/bash
$containser_id : sudo docker ps를 쳤을 때 확인되는 container_id를 입력합니다. (다른 컨테이너 id와 겹치지 않는 선까지만 입력해주면 알아서 인식)
/bin/bash : 컨테이너에 접속할 때 사용되는 쉘을 입력합니다.
이미지에 따라 /bin/bash라는 쉘이 존재하지 않을 수 있는데, 이 경우에는 /bin/sh를 사용해 접속합니다.
💡 docker 2개 이상의 컨테이너를 더 간편하게 관리하기 위해 사용되는 툴!🔧
docker-compose를 사용할 때에는 docker-compose.yml(혹은 .yaml)이라는 파일에 컨테이너에서 사용 될 이미지, 옵션 등을 작성한 후 사용하게 된다.
이를 통해 docker 명령어만 사용할 때보다 여러 컨테이너를 더 간편하고 직관적으로 컨테이너를 관리할 수 있다!!
sudo mkdir -p /usr/lib/docker/cli-plugins
#/usr/lib/docker 경로에 cli-plugins라는 디렉토리를 생성
-p : 만약 상위 디렉토리가 없다면 함께 생성
sudo curl -SL https://github.com/docker/compose/releases/download/v2.11.2/docker-compose-linux-x86_64 -o /usr/lib/docker/cli-plugins/docker-compose
github에 release 된 docker-compose 파일을 /usr/lib/docker/cli-plugins/ 경로에 다운로드
v2.11.2는 docker-compose의 버전이며, 최신 버전은 여기서 확인 가능
sudo chmod +x /usr/lib/docker/cli-plugins/docker-compose
다운받은 docker-compose 파일에 실행 권한을 부여(기본적으로 linux에서는 파일에 대한 실행 권한이 없기 때문)
sudo docker compose version
docker-compose가 정상적으로 설치되었는지 확인
Docker Compose version v2.11.2 정상적으로 설치 된 경우 버전이 출력
docker-compose는 실행할 때 사용자가 작성한 docker-compose.yml 파일의 내용에 맞게 컨테이너를 설정하고 실행하게 된다.
때문에 docker-compose.yml파일이 존재하지 않는 경로에서 docker compose 명령어를 실행시킬 경우 에러가 발생할 수 있다.
sudo docker ps -a
: docker에 존재하는 컨테이너 목록을 확인
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
54445308314d httpd:latest "httpd-foreground" 23 hours ago Up 23 hours 0.0.0.0:80->80/tcp, :::80->80/tcp sweet_engelbart
sudo docker rm -f $container_id
: 컨테이너의 실행중 여부와 관계 없이 강제로 삭제
version: '3.8' # docker-compose.yml에 사용될 문법 버전을 정의
services:
example: # 서비스 이름을 지정, 서비스 이름은 컨테이너끼리 통신할 때 사용
container_name: example # 컨테이너 이름을 지정
image: 'httpd:latest' # 컨테이너를 생성할 때 사용될 이미지를 지정
restart: always # 컨테이너가 종료됐을 때 다시 실행
버전마다 문법이 다르기 때문에 다른 버전이면 다른 문법으로 작성해야 한다!!
docker compose 명령어는 docker-compose.yml 파일이 존재하는 자리에서 실행해야 한다.
sudo docker compose up -d
: 컨테이너 실행
up
: docker-compose.yml 파일을 읽어 정의된 서비스들을 실행시킵니다.
-d
: 컨테이너를 데몬(백그라운드)으로 실행시킵니다.
sudo docker compose stop
: 컨테이너 일시 정지sudo docker compose down
: 컨테이너 삭제docker compose up
명령어로 컨테이너를 다시 생성해야 한다.💡 외부에서 서버의 특정 포트에 접근했을 때 지정한 서비스로 전달해주는 것을 의미
예) 포트 포워딩 설정을 80:8000으로 했다면, 외부에서 80포트로 접속했을 때 해당 컨테이너의 8000번 포트로 접속하겠다는 뜻!!
웹 브라우저같은 경우에는 http프로토콜은 80포트, https프로토콜은 443포트를 사용하는데 주소를 입력할 때 생략되어 보이지 않는것이다!
version: '3.8' # docker-compose.yml에 사용될 문법 버전을 정의
services:
example: # 서비스 이름을 지정합니다. 서비스 이름은 컨테이너끼리 통신할 때 사됩니다.
container_name: example # 컨테이너 이름을 지정
image: 'httpd:latest' # 컨테이너를 생성할 때 사용될 이미지를 지정
ports: # 포트포워딩을 설정
- 80:80 # 외부에서 80 포트로 접속했을 때 컨테이너의 80 포트로 연결
restart: always # 컨테이너가 종료됐을 때 다시 실행
sudo docker compose restart
배포가 정상적으로 이루어지고 있는지 확인해야 하는 경우 docker compose logs 명령어를 이용해 확인할 수 있다.
sudo docker compose logs
이 때 -f 옵션을 추가하면 로그를 실시간으로 확인 가능하다.
sudo docker compose logs -f
실시간 로그를 끝내고 싶으면 ctrl+c 누르기!
docker 컨테이너는 컨테이너가 종료될 때 변경된 데이터는 모두 초기화 된다는 특징이 있는데, volume 옵션을 사용해 데이터를 보존시키는 방법을 알아보자!
sudo docker exec -it 컨테이너이름 /bin/bash
: 컨테이너 안에 접속echo "test" >> /usr/local/apache2/htdocs/index.html
: test라는 문구 추가sudo docker compose down
: 컨테이너 삭제sudo docker compose up -d
: 컨테이너 실행위 처럼 컨테이너를 재시작 하면 test 문구가 사라진다!!
💡 volume은 컨테이너에 저장되는 데이터의 일부를 host와 공유해 주는 역할을 한다.
host에 저장 된 데이터는 사용자가 직접 삭제하지 않는 이상 계속해서 유지되며, 때문에 컨테이너가 종료된다 하더라도 데이터는 유실되지 않는다!!
이와 같은 특성 덕분에, volume은 컨테이너 내부에서 변경되는 내용들을 유지해야 할 때 주로 사용된다.
💡 docker volume의 종류는 docker volume, bind mount, tmpfs mount방식이 있으며 docker volume 방식이 주로 사용된다.
바인드 마운드 방식은 호스트 경로에 있는 파일이나 디렉토리를 컨테이너 내부에 공유해주는 방식이다!
version: '3.8' # docker-compose.yml에 사용될 문법 버전을 정의
services:
example: # 서비스 이름을 지정합니다. 서비스 이름은 컨테이너끼리 통신할 때 사용
container_name: example # 컨테이너 이름을 지정
image: 'httpd:latest' # 컨테이너를 생성할 때 사용될 이미지를 지정
ports: # 포트포워딩을 설정
- 80:80 # 외부에서 80 포트로 접속했을 때 컨테이너의 80 포트로 연결
volumes: # volume을 설정
- ./example_http_code/:/usr/local/apache2/htdocs/ # 정의한 volume의 mount할 경로를 지정
왼쪽은 host 경로(현재경로, docker compose가 있는 경로), 오른쪽은 컨테이너 경로
restart: always # 컨테이너가 종료됐을 때 다시 실행
version: '3.8' # docker-compose.yml에 사용될 문법 버전을 정의
volumes:
example_http_code: {} # docker volume을 정의
services:
example: # 서비스 이름을 지정, 서비스 이름은 컨테이너끼리 통신할 때 사용
container_name: example # 컨테이너 이름을 지정
image: 'httpd:latest' # 컨테이너를 생성할 때 사용될 이미지를 지정
ports: # 포트포워딩을 설정
- 80:80 # 외부에서 80 포트로 접속했을 때 컨테이너의 80 포트로 연결
volumes: # volume을 설정
- example_http_code:/usr/local/apache2/htdocs/ # 정의한 volume의 mount할 경로를 지정
restart: always # 컨테이너가 종료됐을 때 다시 실행
sudo docker volume ls
: volume 목록 보기
sudo docker volume inspect $volume_name
: volume의 이름으로 설정 정보 확인하기
ex) sudo docker volume inspect ubuntu_example_http_code
💡 docker 의 이미지를 직접 생성하기 위한 용도로 작성하는 파일
Dockerfile을 작성할 때는 기본이 되는 이미지를 지정한 후, 특정 패키지를 설치하거나 파일을 추가하는 등의 작업을 통해 사용자가 직접 이미지를 빌드하고 사용할 수 있다.
💡 사용자가 개발한 프로젝트 혹은 설정파일 등을 이미지에 포함시키거나 이미지에 기본적으로 특정 패키지를 설치하고싶을 때 등 다양한 용도로 사용
예를 들어 docker에서 django를 배포한다고 가정 했을 때, 기본 python 이미지를 불러온 후 django 패키지를 pip install 한 후 이미지를 생성하게 된다.
FROM httpd:latest
: 빌드할 때 사용할 이미지를 지정COPY ./index.html /usr/local/apache2/htdocs/index.html
: 현재 경로에 존재하는 index.html 파일을 컨테이너 내부로 복사 version: '3.8' # docker-compose.yml에 사용될 문법 버전을 정의합니다.
services:
example: # 서비스 이름을 지정합니다. 서비스 이름은 컨테이너끼리 통신할 때 사용됩니다.
container_name: example # 컨테이너 이름을 지정합니다.
build: . # 현재 경로에 있는 Dockerfile을 사용해 이미지를 생성합니다.
ports: # 포트포워딩을 설정해줍니다.
- 80:80 # 외부에서 80 포트로 접속했을 때 컨테이너의 80 포트로 연결해줍니다.
restart: always # 컨테이너가 종료됐을 때 다시 실행시켜 줍니다.
vi index.html
: 내용 추가sudo docker compose up --build -d
: 컨테이너를 실행하며 새로운 이미지 빌드※ Dockerfile로 빌드된 이미지가 없을 경우, --build 옵션을 추가하지 않더라도 이미지를 빌드 하게 된다. 하지만, 기존에 빌드된 이미지가 있을 경우에는 새로 빌드하지 않고 기존에 빌드 된 이미지를 사용한다. 때문에, Dockerfile을 수정하고 이미지를 새로 빌드해야 할 때는 --build 옵션을 추가해서 사용해야 한다.(Dockerfile에 변경사항이 생기면 --build를 해주어야 한다!)
💡 docker 컨테이너가 생성될 때 기본적으로 실행 할 명령어를 지정해 주는 옵션
예를 들어, 데이터베이스를 실행시키기 위해 만든 이미지는, 컨테이너가 생성될 때 데이터베이스 서비스를 실행시켜야 한다. 이 때 사용되는 옵션이 entrypoint이다.
entrypoint는 Dockerfile과 docker-compose.yml 모두 작성할 수 있다.
만약 Dockerfile, docker-compose.yml 모두 entrypoint가 작성되어 있다면 Dockerfile의 entrypoint는 무시되고 docker-compose.yml의 명령어가 우선적으로 수행된다.
FROM python:3.9.15
# .pyc 파일을 생성하지 않도록 설정
ENV PYTHONDONTWRITEBYTECODE 1
# 파이썬 로그가 버퍼링 없이 즉각적으로 출력하도록 설정
ENV PYTHONUNBUFFERED 1
# /app/ 디렉토리를 생성
RUN mkdir /app/
# /app/ 경로를 작업 디렉토리로 설정
WORKDIR /app/
# main.py 파일을 /app/ 경로로 복사
COPY ./main.py /app/
version: '3.8' # docker-compose.yml에 사용될 문법 버전을 정의합니다.
services:
example: # 서비스 이름을 지정합니다. 서비스 이름은 컨테이너끼리 통신할 때 사용됩니다.
container_name: example # 컨테이너 이름을 지정합니다.
build: . # 현재 경로에 있는 Dockerfile을 사용해 이미지를 생성합니다.
entrypoint: sh -c "python3 main.py" # 작업 디렉토리에 존재하는 main.py 파일을 실행시킵니다.
restart: always # 컨테이너가 종료됐을 때 다시 실행시켜 줍니다.
vi main.py
: vi 편집기로 main.py 파일을 열고 수정from time import sleep
for i in range(100):
print(f"print number : {i}")
sleep(1)
sudo docker compose up --build -d
: 컨테이너&빌드 생성sudo docker compose logs -f
: 잘 실행 되는지 log을 찍어줌💡 컨테이너가 1개일 때와 2개 이상일 때 docker-compose.yml을 작성하는 방법은 기존과 크게 다르지 않다.
다만, ports 혹은 서비스 이름 등 중복되면 안되는 몇몇 옵션이 존재하기 때문에 중복되는 값이 있는지 확인해야 하며 만약 특정 값이 중복되어 들어간다면 컨테이너가 정상적으로 생성되지 않거나 생성하는 과정에서 에러가 발생할 수 있다.
container_name, ports, services 등은 중복되어서 들어가면 오류가 난다.
docker compose 파일 안에 서비스 이름을 다르게 나누어서 2개를 지정해주었을 뿐 1개를 생성할 때와 다른점은 크게 없다!
version: '3.8'
services:
example1:
container_name: example1
image: 'httpd:latest'
ports:
- 80:80
restart: always
example2: # 서비스 이름이 동일하면 컨테이너가 정상적으로 생성되지 않을 수 있습니다.
container_name: example2 # 컨테이너 이름이 동일하면 컨테이너 생성 시 에러가 발생합니다.
build: .
entrypoint: sh -c "python3 main.py"
restart: always
💡 docker-compose.yml 작성 이후 컨테이너를 실행시켜 보면 두 개의 컨테이너가 실행되는 것을 확인할 수 있다.
sudo docker compose up
💡 docker-compose.yml에 두 개 이상의 컨테이너를 생성하도록 작성한 경우, 기본적으로는 작성 한 순서대로 컨테이너가 실행된다.
하지만 경우에 따라 특정 컨테이너가 먼저 실행되어야 하는 경우가 있다.
예를 들어, django 컨테이너와 데이터베이스 컨테이너를 같이 띄우는 경우에는 데이터베이스 컨테이너가 먼저 생성되어야 django 컨테이너에서 데이터베이스에 연결할 수 있다.
이 때, depends_on 옵션을 사용해 컨테이너간 실행 순서를 컨트롤 할 수 있다.
version: '3.8'
services:
example1:
container_name: example1
image: 'httpd:latest'
ports:
- 80:80
depends_on:
- example2 # 해당 컨테이너보다 먼저 실행되어야 하는 컨테이너를 지정합니다.
restart: always
example2: # 서비스 이름이 동일하면 컨테이너가 정상적으로 생성되지 않을 수 있습니다.
container_name: example2 # 컨테이너 이름이 동일하면 컨테이너 생성 시 에러가 발생합니다.
build: .
entrypoint: sh -c "python3 main.py"
restart: always
이후 컨테이너가 생성되는 로그를 보면 실행 순서가 바뀐 것을 확인할 수 있다.
※depends_on을 설정하지 않은 경우
※depends_on을 설정해준 경우