[도커] 심화
레이어 캐싱 (Layer Caching)
Dockerfile의 줄 순서에 따라 빌드 속도가 바뀔 수 있음. 이는 도커에 캐시가 생각보다 많은 역할을 하기 때문이다. 예를 들어보자.
1
2
3
4
5
6
7
8
COPY . . # 만약 여기서 파일이 바뀌게 된다면 캐시가 깨시게 되기 때문에
RUN pip install -r requirements.txt # 아래까지 설치가 다시된다
---------------------------------------------------------------------
COPY requirements.txt . # 여기서 먼저 복사하게 되면
RUN pip install -r requirements.txt # 이 파일의 내용을 확인 후 캐시가 존재하면 넘어감
COPY . . # 여기서 파일이 변경된거기 때문에 이 줄은 실행
즉, 소스코드가 변경되어서 새로 빌드해도 라이브러리가 변경되지 않으면 관련된 내용은 그대로 넘어간다. 물론 WORKDIR 이 다르다면 완전 다른 장소이기 때문에 캐시와 상관없이 실행되게 된다.
보안, User Switching
리눅스에서도 그렇고 데이터베이스에서도 그렇고 항상 root에서 일반 유저로 변경해야함
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FROM python:3.9-slim
WORKDIR /app
# 1. 보안을 위해 'appuser'라는 유저 생성 (시스템 유저)
RUN useradd -m appuser
COPY . .
# 2. 파일 소유권을 appuser에게 변경
RUN chown -R appuser:appuser /app
# 3. Root 권한 박탈, appuser로 유저 변경
USER appuser
CMD ["python", "main.py"]
Health Check
컨테이너 상태가 up으로 되어있어도 정상 작동중은 아닐 수 있음. 예를 들어, 웹 서버 프로세스는 켜져 있지만, 내부 오류로 500 에러를 뱉고 있을 수 있기 때문에 도커에게 확인시키는 작업.
1
2
3
# 30초마다 curl로 접속 시도, 3초 안에 응답 없으면 실패 처리, 3번 실패하면 'Unhealthy' 판정
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl -f http://localhost:8000/ || exit 1
CMD vs ENTRYPOINT
Dockfile의 마지막 줄에 들어가는 cmd나 entrypoint는 비슷한 기능을 하지만 명확한 차이가 존재한다.
1
2
CMD ["python"]
ENTRYPOINT ["python"]
- CMD : 기본으로 파이썬을 실행하지만 다른것을 넣는다면 해당하는것을 실행
- ENTRYPOINT : 일단 파이썬부터 실행시키고 그 후에 들어온 인자 실행
좀 더 예를 들어보자
1
CMD ["echo", "Hello"]
- 그냥 실행 : docker run my-image —> echo Hello를 실행, 결과는 Hello
- 명령어 변경 : docker run my-image Hi —> echo Hi가 실행, 결과는 Hi
1
ENTRYPOINT ["echo", "Hello"]
- 그냥 실행 : docker run my-image —> echo Hello를 실행, 결과는 Hello
- 명령어 변경 : docker run my-image Hi —> echo Hello Hi가 실행, 결과는 ?
결국 가장 위처럼 CMD와 ENTRYPOINT를 적절하게 혼합하여 사용하는것이 제일 좋다
리소스 제한
지금까지 공부할때는 컨테이너를 실행할 때 CPU나 RAM을 제한하지 않음. 문제는 제한하지 않고 메모리를 다 사용하면 서버가 다운된다.
1
docker run -d --memory="512m" --cpus="0.5" nginx
- 메모리 사용을 512m, cpu는 코어 한개의 절반으로 제한
도커 네트워크와 DNS
Docker compose를 사용 시, DB를 찾을 때 IP 주소가 아니라 이름으로 찾을 수 있음. 이는 도커 내부 자체에 DNS 서버가 있기 때문.
일반적으로 run 할 시에 bridge라는 네트워크에 붙는데, 이때는 이름으로 통신이 되지 않는다. 만약 위의 docker compose로 만들면 그 안에서 서로의 컨테이너 이름으로 호출 가능.
예)
1
2
3
4
5
6
7
8
9
# 1. 네트워크 만들기
docker network create my-net
# 2. 컨테이너 두 개 띄우기 (같은 네트워크에 소속시킴)
docker run -d --name my-db --network my-net redis
docker run -it --name my-app --network my-net alpine sh
# 3. my-app 안에서 my-db를 이름으로 찾아보기
(alpine 내부)# ping my-db
This post is licensed under CC BY 4.0 by the author.