컨테이너·Docker와 데이터 지속성
컨테이너는 격리된 프로세스 환경입니다. 이미지, 실행 중인 컨테이너, 영속 데이터와 공개 포트를 따로 관리합니다.
개념부터 찾아보기
정의·역할·동작 원리를 독립 페이지에서 확인하세요. 본문의 밑줄 용어에도 설명이 있습니다.
이 노트에서 설명할 수 있게 되는 것
- 이미지 계층·컨테이너 쓰기 계층·볼륨을 구분한다.
- 컨테이너 재생성 전후 데이터 위치와 공개 포트를 추적한다.
- 실행·건강 상태·호스트 권한의 차이를 설명한다.
01namespace와 cgroup은 서로 다른 질문에 답한다
- 별 분리 대상mount 는 가 보는 마운트 트리를, PID 는 번호 공간을, 는 인터페이스·경로·포트 공간을 분리한다.
- 따라서 안의 PID 1과 호스트의 PID 1은 같은 가 아닐 수 있다.
- ·user 는 의 UID를 호스트의 다른 UID 범위에 매핑할 수 있다.
- 이 격리는 무엇을 보고 어떤 이름으로 접근하는지를 제한한다.
- 은 CPU·메모리· 수 등의 사용을 집계하고 제한한다.
- 격리와 자원 제한만 분리한 가 호스트 자원을 무제한 사용하는 문제는 별도 자원 제어 없이는 남는다.
- capabilitycapability는 세분화된 관리자 권능, seccomp는 시스템 호출 제한, /AppArmor는 객체 접근 정책을 제공한다.
- ·호스트 커널는 호스트 커널을 공유하므로 처럼 독립 커널을 부팅한 환경과 격리의 방식이 다르다.
- 격리 기능격리 기능이 있다는 사실만으로 모든 공격이 차단되지는 않는다.
- 과도한 권한, 호스트 디렉터리 공유, 런타임 제어 소켓 노출은 호스트에 큰 영향을 줄 수 있다.
02이미지 계층과 컨테이너 쓰기 계층의 상태 변화
- 의 구성는 사용자 공간 파일과 실행 메타데이터를 재사용 가능한 계층으로 담는다.
- ·를 만들면 읽기 전용 위에 해당 의 쓰기 계층이 생긴다.
- 과 삭제 표시OverlayFS 같은 구현에서는 lowerdir의 기존 파일을 수정할 때 upperdir 쪽으로 복사한 뒤 변경을 적용할 수 있고, 삭제 표시를 통해 아래 파일이 보이지 않게 한다.
- 저장 드라이버와 계층실제 저장 드라이버에 따라 구현은 다르지만 와 인스턴스의 변경을 분리한다는 관계는 같다.
- ·같은 로 새 를 만들면 기존 의 쓰기 계층이 자동으로 복제되지 않는다.
- stop은 를 중지할 뿐 해당 의 계층을 제거하지 않지만 rm으로 를 없애면 그 인스턴스의 쓰기 계층도 사라진다.
- 따라서 DB 데이터·사용자 업로드 같은 지속 자료는 이나 의도한 외부 저장소로 분리한다.
docker image ls
docker ps -a
docker inspect demo
docker diff demo
# 이미지, 인스턴스 상태, 인스턴스 파일 변경은 서로 다른 조회다.교육용 예시. 결과와 주소는 설명용이며 실제 시스템에서 실행한 기록이 아니다.
03ENTRYPOINT·CMD와 PID 1의 종료
- 는 실행할 기본 프로그램을 정하고 CMD는 기본 인자 또는 기본 명령 역할을 한다.
- · run실제 조합은 설정과 run 인자의 형태에 따라 결정된다.
- 형식JSON 배열의 형식은 불필요한 셸을 거치지 않고 인자를 전달한다.
- shell 형식shell 형식은 중간 셸이 생겨 신호 전달과 변수 확장의 의미가 달라질 수 있다.
- 초기 가 종료되면 실행도 끝난다.
- run run -d는 클라이언트를 분리하는 것이지 애플리케이션을 전통적 데몬으로 바꾸는 옵션이 아니다.
- 는 이미 실행 중인 안에 추가 를 만든다.
- PID 1·그 셸에서 exit해도 원래 PID 1이 살아 있으면 는 계속 실행한다.
- 초기 초기 는 종료 신호 처리와 고아 자식 회수에 적절히 대응해야 하며 필요한 경우 init 역할을 따로 둔다.
FROM python:3.12-slim
WORKDIR /app
COPY app.py /app/app.py
USER 10001:10001
ENTRYPOINT ["python", "/app/app.py"]
CMD ["--port", "8000"]교육용 예시. 결과와 주소는 설명용이며 실제 시스템에서 실행한 기록이 아니다.
04컨테이너 포트와 호스트 공개 포트
- ·포트각 는 자체 포트 공간을 가질 수 있으므로 서로 다른 에서 같은 8000번 포트를 사용할 수 있다.
- bridge 네트워크에서는 veth와 브리지를 통해 를 연결하고 호스트 공개 포트에서 주소로 전달하는 규칙을 구성할 수 있다.
- run run -p 127.0.0.1:8080:8000은 호스트 loopback의 8080을 의 8000에 연결하는 예다.
- ·EXPOSE의 EXPOSE 메타데이터는 앱이 사용하는 포트를 설명하지만 그것만으로 호스트 외부에 포트가 공개되지는 않는다.
- 반대로 포트를 공개했어도 앱이 그 의 127.0.0.1에만 하면 외부 인터페이스로 들어온 전달 요청을 받지 못할 수 있다.
- 호스트의 패킷 경로호스트 방화벽에서 INPUT만 보아서는 브리지·DNAT·FORWARD가 관여하는 전체 경로를 설명하지 못한다.
docker port demo
docker network inspect bridge
# 예시 전달 관계: host 127.0.0.1:8080 -> container:8000교육용 예시. 결과와 주소는 설명용이며 실제 시스템에서 실행한 기록이 아니다.
호스트 9090·4651 포트와 컨테이너의 8080을 연결하기
브라우저 응답을 의 포트 공개 설정과 연결하고 서로 다른 의 서비스를 구별한다.
실습 구성과 전제
- 첫 화면은 자체 Go 웹 애플리케이션, 두 번째는 Jenkins 의 별도 실행 사례다. 같은 의 전후 화면이 아니다.
- 구성 예시는 주소· 이름을 재구성한다. Jenkins의 오래된 화면 버전은 관찰 자료이며 현재 설치 권장 버전으로 사용하지 않는다.
컨테이너가 수신하는 포트와 호스트 포트를 구별한다
- Go 웹 애플리케이션은 내부 8080에서 동작하고, 실행 설정의 -p 9090:8080이 호스트 9090을 그 포트로 연결한다. 브라우저는 호스트 주소의 9090으로 요청한다.
- run의 -t는 가상 TTY를 할당하는 옵션이다. 태그를 지정하는 옵션이 아니며, 의 :태그 표기와 구별한다.
bashdocker ps --format "table {{.Names}}\t{{.Image}}\t{{.Ports}}" docker port lab-weblab-web은 재구성한 이름이다. 포트 매핑을 조회하는 명령이다.
원본 크기로 보기 ↗- 브라우저 주소에는 호스트의 9090 포트가 사용된다.
- 본문에 Hello Docker!!가 보여 해당 웹 요청에 애플리케이션 응답이 돌아왔음을 확인할 수 있다.
확인 기준9090 응답만으로 어떤 가 담당하는지 확정하지 않는다. port와 실행 목록으로 매핑을 대조한다.
다른 호스트 포트에 별도 애플리케이션을 공개한다
- Jenkins 예시의 Compose 설정은 호스트 4651을 8080에 연결한다. 서로 다른 는 각 네트워크 공간에서 같은 내부 포트를 사용할 수 있다.
- Jenkins의 설정 화면은 HTTP 요청 처리와 초기 설치 UI가 동작한 증거다. 빌드 작업이나 자료 영속성이 검증되었다는 뜻은 아니다.
yamlservices: app: image: example/lab-web:verified ports: - "127.0.0.1:9090:8080"구조 설명용 재구성 설정이다. example/lab-web:verified는 준비한 실습 로 바꾸며, 로컬 호스트에서만 공개하는 예다.
원본 크기로 보기 ↗- 브라우저는 4651 포트를 사용한다.
- Jenkins의 Customize Jenkins와 플러그인 설치 선택 화면이 표시되어 첫 화면과 다른 애플리케이션임을 알 수 있다.
확인 기준두 가 같은 호스트 IP·포트를 동시에 점유하려 하면 충돌할 수 있다. 내부 포트가 같은 것과 호스트 공개 포트가 같은 것을 구별한다.
HTTP 성공과 컨테이너·저장 상태를 분리한다
- 실행 중 여부, 포트 공개, 의 실제 리슨, 애플리케이션 응답을 차례로 확인한다. 가 Up이어도 서버가 다른 주소나 포트에서 기다릴 수 있다.
- Jenkins의 작업 자료를 유지하려면 별도의 또는 mount를 확인해야 한다. 화면의 설치 UI만으로 재생성 후 데이터 보존까지 증명되지는 않는다.
bashcurl -i http://127.0.0.1:9090/ docker inspect --format "{{json .NetworkSettings.Ports}}" lab-web docker inspect --format "{{json .Mounts}}" lab-web응답·포트·마운트를 서로 다른 관찰 지점에서 확인한다.
확인 기준재구성 환경에서 curl의 응답과 inspect의 매핑·마운트를 함께 기록한다. 로그에는 초기 비밀 값이 포함될 수 있어 본문에 전재하지 않는다.
실습에서 확인한 동작 원리
- HOST_PORT:CONTAINER_PORT는 요청이 어느 네트워크 경계로 전달되는지 나타낸다.
- 브라우저 응답, 실행, 초기화 완료, 자료 영속성은 각각 별도로 검증해야 한다.
설정·출력 추가로 확인하기 2장
아래 화면을 펼쳐 위 실습의 설정값, 명령과 확인 기준을 비교해 보세요. 각 묶음은 별도 실습 기록입니다.
실습 화면 묶음 01 1장
원본 크기로 보기 ↗- 학습 원본 · Docker·Compose 서비스 초기화의 Docker-composes에 수록된 화면·도식이다.
실습 화면 묶음 02 1장
원본 크기로 보기 ↗- 학습 원본 · Docker·Compose 서비스 초기화의 Docker-composes에 수록된 화면·도식이다.
동작·명령 참고 문서
05volume·bind mount·secret의 소유권
- 은 가 관리하는 저장 단위이고 mount는 지정한 호스트 경로를 경로에 연결한다.
- 마운트로 가려진 파일마운트 지점에 파일이 있었다면 연결된 저장소가 그것을 가릴 수 있다.
- 읽기 전용· 레이블:ro는 쪽 쓰기를 제한하며 환경의 :z/:Z는 레이블에 영향을 준다.
- 호스트 경로 노출특히 호스트의 중요한 디렉터리를 넓게 마운트하면 격리와 별도로 그 자원에 접근하게 된다.
- ·호스트 UID 안의 UID 10001이 호스트에서 어떤 UID로 해석되는지에 따라 파일 소유권 결과가 달라진다.
- /user 와 일반 실행을 같은 숫자로 단정할 수 없다.
- 에 남는 비밀비밀값을 의 RUN이나 ENV에 넣으면 계층·메타데이터·inspect 결과 등에 남을 수 있으므로 접근이 제한된 파일이나 외부 비밀 관리 방식으로 전달하고 수명을 관리한다.
- 복사도 DB가 쓰는 중이면 일관되지 않을 수 있어 애플리케이션 백업 절차와 연결해야 한다.
06파일 변경과 CPU 제한이 서로 다른 커널 계층에 걸린다
- OverlayFS 읽기overlay2 계열에서 upperdir에 같은 파일이 있으면 그 내용이 lowerdir의 를 가린다. merged는 두 곳의 내용을 하나의 경로로 보여 주는 관점이다.
- 첫 쓰기의 에만 있는 파일을 처음 수정하면 쓰기 계층으로 복사한 뒤 변경한다. 작은 부분 수정이라도 파일 단위 복사가 일어날 수 있어 첫 쓰기 비용과 후속 쓰기 비용이 다르다.
- 삭제 표시 파일을 삭제해도 읽기 전용 계층 자체를 지우는 것은 아니다. 쓰기 계층의 whiteout 등이 아래 이름을 보이지 않게 표현한다.
- 의 별도 책임파일 계층과 달리 은 집합의 자원 사용을 계측·제한한다. 에 다른 PID가 보이지 않는 것과 CPU 시간 제한을 받는 것은 별개의 효과다.
- 구현 범위OverlayFS 설명은 해당 저장 드라이버·snapshotter 경로에 한정한다. 버전·설치 모드에 따라 저장 구현과 버전이 다르므로 먼저 실제 사용 방식을 확인한다.
07cpu.max의 50000 100000을 시간으로 계산하기
- 단위와 조건 v2의 cpu.max가 50000 100000이고 cpu.max.burst=0인 fair-class 태스크를 가정한다. 값의 단위는 마이크로초이며 quota와 period를 뜻한다.
- 허용 시간100000µs=100ms 주기마다 그룹이 사용할 총 CPU 시간은 최대 50000µs=50ms다. 평균적으로 CPU 하나의 0.5배에 해당하는 한도다.
- 여러 실행 흐름스레드 두 개가 서로 다른 CPU에서 동시에 계산하면 합산 CPU 시간이 더 빨리 소모될 수 있다. 벽시계 50ms 동안 각각 50ms를 공짜로 받는 구조가 아니다.
- 한도 도달할당량을 소모하면 다음 주기까지 실행이 제한될 수 있다. 처리량이 떨어져도 가 죽거나 예외를 발생시키는 것과는 다른 상태다.
- 보장과 상한한도는 사용할 수 있는 상한이지 반드시 공급되는 최소 CPU가 아니다. 호스트 경쟁·다른 상위 제한이 있으면 실제 사용량은 더 적을 수 있다.
cpu.max: 50000 100000
계산: 50000 / 100000 = 0.5
주기: 100ms, 그룹 전체 quota: 50msv2 값의 가상 계산. 특정 의 측정 출력이 아니다.
08종료·느림·자료 소실을 다른 자원 경계로 분류하기
- CPU throttlingCPU 제한 때문에 느리면 의 제한 설정과 throttling 통계를 부하 시간대에 대조한다. 낮은 평균 CPU만 보고 앱이 계산하지 않았다고 단정하지 않는다.
- 메모리 종료메모리 한계와 OOM 관련 종료는 앱이 자발적으로 오류 코드로 끝난 상황과 다르다. 상태·커널/ 메모리 사건·앱 로그를 연결한다.
- 셸 종료 로 추가한 셸만 끝난 경우와 초기 PID 1이 끝난 경우의 상태는 다르다. 사용자가 보고 있던 터미널의 종료를 생애로 대신하지 않는다.
- 마운트가 가린 파일· mount가 경로 위에 연결되면 안의 기존 파일이 가려질 수 있다. 파일을 삭제했다고 판단하기 전에 의 실제 mount 구성을 본다.
- 재생성의 자료 경계쓰기 계층에 둔 자료는 해당 인스턴스 제거와 함께 사라질 수 있다. 의 존속 여부와 백업 시점은 를 재시작한 성공 여부와 별도로 검증한다.
09실행 상태와 정상 동작
- 로그 종료, 로그, 자원 제한, 헬스 체크를 함께 관찰한다.
- ·PID 1의 PID 1은 종료 신호 처리와 자식 정리에서 주의가 필요하며, 실행 중이라는 표시만으로 요청을 정상 처리한다고 단정할 수 없다.
- 제어 권한은 단순한 조회 권한보다 강할 수 있다.
- 운영 계정과 자동화 계정이 실행·마운트·권한 옵션을 어디까지 바꿀 수 있는지 확인한다.
docker ps
docker image ls
docker volume ls현재 환경의 실행 ·· 목록만 조회한다.
10같은 경로에 써도 저장 위치가 달라지는 이유
- 쓰기 계층에서 를 만들면 읽기 전용 계층 위에 해당 의 쓰기 계층이 연결된다.
- 쓰기 계층일반 경로에 새 파일을 쓰면 보통 쓰기 계층에 변화가 기록되고, 그 를 삭제하면 해당 계층의 데이터도 제거된다.
- 그러나 그 경로가 이나 mount로 연결돼 있으면 데이터는 그 마운트가 가리키는 별도 저장 위치에 기록된다.
- ·쓰기 계층경로 문자열이 같다고 내부 쓰기 계층에 들어간다고 가정하면 안 된다.
- 웹 애플리케이션의 실행 파일은 에서 공급하고 업로드 자료는 에 두는 구성을 생각한다.
- 교체와 재사용새 로 를 교체해도 같은 을 연결하면 업로드 자료를 이어 사용할 수 있다.
- 반대로 새 에 다른 빈 을 붙이면 기존 자료가 사라진 것처럼 보인다.
- 이름·실제 연결 경로·애플리케이션 설정을 함께 대조해야 한다. 을 유지하는 것은 수명 분리이며 삭제·오염에 대비한 과거 백업을 자동으로 생성하는 기능은 아니다.
- 생성 기반으로 실행 인스턴스와 쓰기 계층이 만들어진다.
- 업로드마운트된 경로의 데이터는 으로 기록된다.
- 기존 인스턴스 제거해당 의 쓰기 계층은 함께 사라진다. 제거는 별도 작업이다.
- 새 인스턴스 연결같은 데이터 을 연결해 자료를 다시 읽는다.
설명과 함께 보는 보조 도식
컨테이너 교체와 데이터 수명
이미지 파일과 업로드용 볼륨을 나눠 사용하는 구성이다.
생성
이미지 기반으로 실행 인스턴스와 쓰기 계층이 만들어진다.
- 관계
- 컨테이너별 쓰기 계층
전체 단계를 한 번에 읽기
- 01
생성
이미지 기반으로 실행 인스턴스와 쓰기 계층이 만들어진다.
- 관계
- 컨테이너별 쓰기 계층
- 02
업로드
마운트된 경로의 데이터는 볼륨으로 기록된다.
- 저장 위치
- 연결된 볼륨
- 03
기존 인스턴스 제거
해당 컨테이너의 쓰기 계층은 함께 사라진다. 볼륨 제거는 별도 작업이다.
- 볼륨
- 별도 유지 가능
- 04
새 인스턴스 연결
같은 데이터 볼륨을 연결해 자료를 다시 읽는다.
- 확인
- 같은 볼륨·같은 대상 경로
11컨테이너 장애를 계층별로 관찰하기
- Running과 서비스 정상 상태가 running이면 주 가 살아 있다는 단서지만 HTTP 응답, DB 연결, 파일 저장까지 정상임을 의미하지 않는다.
- 로그종료 코드와 로그를 확인하고 헬스 체크가 무엇을 검사하는지 읽는다.
- 루프백 바인딩서비스가 내부 루프백에만 바인딩돼 있으면 포트를 공개해도 기대한 인터페이스로 응답하지 않을 수 있다.
- 포트·호스트의 공개 주소·포트와 의 실제 리스닝 포트는 한 쌍으로 기록한다.
- 실행 계정의 파일 접근호스트에서 파일을 볼 수 있어도 실행 계정이 읽지 못할 수 있다.
- mount mount의 소유권, 레이블과 읽기 전용 옵션을 따로 확인한다.
- 메모리 제한으로 종료된 경우와 애플리케이션 예외 종료도 구분해야 한다.
- 런타임 제어 권한 제어 소켓이나 높은 권한을 주는 방식으로 모든 오류를 피하면 격리 경계가 크게 약해질 수 있다.
- 재생성과 데이터 보존재생성 실습에서는 코드 버전, 환경 변수, 마운트, 네트워크와 종료 후 데이터가 무엇인지 각기 확인한다.
| 구성·관찰 대상 | 관찰 내용 | 해석 |
|---|---|---|
| 빌드 버전·기본 파일 | 변경이 에 반영됐는지 | |
| 쓰기 계층·마운트 | 데이터가 실제로 기록되는 곳 | 재생성 후 데이터 유실 |
| 포트 매핑 | 호스트 주소와 포트 | 공개와 리스닝의 불일치 |
| 상태·제한 | 종료 코드·헬스·자원 | 살아 있음과 정상 처리의 차이 |
함께 보는 학습 자료
아래 공식 자료를 본문의 개념·예제와 함께 읽어 보세요. 자료별로 확인할 위치와 관찰할 내용을 적었습니다.
Docker 클라이언트·데몬·레지스트리 구조
architecture 그림에서 CLI가 어느 데몬에 요청하는지 따라간 다음 Example run command의 순서를 읽는다. 가져오기와 생성·시작을 분리해 이해하는 경로다.
- CLI와 데몬이 같은 호스트에 있어야 하는지
- ··레지스트리가 각각 보관하는 것
- 삭제와 외부 저장소 데이터의 수명
이해했는지 확인하기
- 이미지·컨테이너·볼륨의 수명을 구분한다.
- 호스트에서 공개되는 실제 주소와 포트를 확인한다.
- 재생성 뒤에도 필요한 데이터와 설정이 유지되는지 검토한다.

