Ubuntu 26.04 강좌 / 컨테이너 앱 실행과 데이터·포트 관리하기
컨테이너 앱은 이미지로 실행 환경을 만들되 영속 데이터와 공개 포트는 따로 관리해야 합니다. 이 실습은 Ubuntu 26.04 전용 VM에서 Docker Engine과 Compose를 설치하고, 호스트에서만 접근하는 Nginx 앱의 데이터를 컨테이너 재생성 후에도 유지하는 방법을 다룹니다.
설치 경로와 권한 정하기
이미 Docker·containerd·Kubernetes를 사용하는 서버에서는 패키지를 지우거나 설치 경로를 섞지 않습니다. 기존 컨테이너·볼륨·저장소를 조사하고 별도의 변경 계획을 세우세요. 아래는 Docker 저장소가 아직 없는 새 Ubuntu VM용 절차이며 기존 /etc/apt/sources.list.d/docker.sources와 /etc/apt/keyrings/docker.asc가 있으면 실행하지 않습니다.
Docker의 Ubuntu 설치 문서에서 26.04 지원, 충돌 패키지와 저장소 설치 절차를 확인할 수 있습니다.
docker 그룹과 Docker 소켓 접근은 호스트에서 매우 강한 권한을 줍니다. 이 글은 필요한 명령에 sudo를 명시하며 소켓에 777 권한을 주거나 모든 사용자를 docker 그룹에 추가하지 않습니다. 신뢰할 수 없는 이미지를 실행하는 일은 단순 파일 열기와 다릅니다.
공식 저장소로 설치하기
다음 전체 코드를 ~/install-docker-lab.sh에 저장하고 bash ~/install-docker-lab.sh로 실행합니다. Ubuntu 26.04 확인과 기존 파일 검사에 실패하면 쓰기 전에 중단합니다. 저장소 키는 해당 저장소에만 Signed-By로 연결합니다.
set -eu
. /etc/os-release
if [ "$ID" != ubuntu ] || [ "$VERSION_ID" != 26.04 ]; then
printf "This lab requires Ubuntu 26.04.\n" >&2
exit 1
fi
if [ -e /etc/apt/sources.list.d/docker.sources ] || [ -e /etc/apt/keyrings/docker.asc ]; then
printf "Inspect the existing Docker repository first.\n" >&2
exit 1
fi
sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: ${UBUNTU_CODENAME:-$VERSION_CODENAME}
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo docker version
sudo docker compose version
sudo docker run --rm hello-world
설치가 끝나면 클라이언트·서버 버전과 hello-world 성공 메시지를 확인합니다. 실패한 apt·서명·네트워크 오류를 무시하고 다음 단계로 진행하지 않습니다. 실제 설치 버전을 운영 기록에 남기며 저장소 제거만으로 설치된 엔진이나 데이터가 없어지지는 않습니다.
Nginx 사이트 구성 예제는 8080을 유지하고 컨테이너의 호스트 포트는 8081로 분리합니다. 컨테이너 내부 80은 유지합니다. 8081도 사용 중이면 ss와 compose ps로 소유자를 확인하고 다른 검증한 포트를 Compose·curl 예시 전체에 일관되게 사용합니다.
Compose 파일과 볼륨 만들기
새 ~/compose-lab 디렉터리를 만들고 그 안에 compose.yaml을 아래 내용으로 저장합니다. 127.0.0.1:8081이 비어 있는지 먼저 ss -lnt로 확인합니다. ports의 앞 주소를 생략하면 다른 인터페이스에도 노출될 수 있으므로 실습에서는 명시적으로 루프백을 사용합니다.
services:
web:
image: nginx:stable-alpine
ports:
- "127.0.0.1:8081:80"
volumes:
- lab-html:/usr/share/nginx/html
restart: unless-stopped
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
volumes:
lab-html:
lab-html은 컨테이너의 쓰기 계층과 별도인 이름 있는 볼륨입니다. 초기 콘텐츠에서 시작해 이 실습의 index.html을 기록합니다. stable-alpine 태그가 가리키는 이미지는 바뀔 수 있으므로 재현 가능한 운영에는 검증한 다이제스트와 갱신 절차가 필요합니다. 예제 로그 제한도 서비스 부하에 맞게 조정합니다.
실행·재생성·데이터 유지 확인하기
(
set -eu
cd ~/compose-lab
sudo docker compose config
sudo docker compose pull
sudo docker compose up -d
sudo docker compose ps
sudo docker compose logs --tail 30 web
)
config 검사와 이미지 다운로드가 성공한 뒤 실행합니다. 컨테이너가 실행 중인지와 로그를 확인하고 아래 실습 내용을 기록합니다. 운영 데이터가 있는 기존 프로젝트나 볼륨에서 예제 파일을 덮어쓰지 않습니다.
(
set -eu
cd "$HOME/compose-lab"
sudo docker compose exec web sh -c 'printf "volume-lab\n" > /usr/share/nginx/html/index.html'
curl --fail http://127.0.0.1:8081/
sudo docker compose up -d --force-recreate
curl --fail http://127.0.0.1:8081/
sudo docker image inspect nginx:stable-alpine --format '{{json .RepoDigests}}'
)
실행·재생성·종료의 각 코드 블록은 ~/compose-lab으로 이동한 뒤 실행합니다. 괄호 안의 작업 폴더 변경은 블록이 끝나면 유지되지 않습니다. 모든 Compose 명령이 같은 compose.yaml을 사용해야 같은 프로젝트와 볼륨을 다룹니다. 재생성 전후 응답이 모두 volume-lab이면 같은 볼륨의 파일을 읽은 것입니다. 이것은 백업 성공이나 애플리케이션 전체의 정상 동작을 증명하지 않습니다. 다른 장치에서는 이 루프백 게시 포트에 연결되지 않아야 하며 호스트의 실제 바인딩도 ss와 docker compose ps로 확인합니다.
Docker의 포트 게시와 방화벽 문서를 확인하세요. 공개 포트는 UFW 규칙만으로 보호된다고 가정하면 안 됩니다.
정지와 데이터 삭제 구분하기
(
set -eu
cd "$HOME/compose-lab"
sudo docker compose down
sudo docker volume ls
)
down은 이 프로젝트의 컨테이너·네트워크를 정리하지만 이름 있는 볼륨은 기본적으로 남깁니다. 다시 up -d 한 뒤 같은 응답이 나오는지 확인할 수 있습니다. down -v와 volume prune은 자료를 삭제할 수 있으므로 단순 종료 명령으로 사용하지 않습니다. 볼륨 백업은 앱 쓰기를 조정하고 별도 저장 위치에서 복원 시험까지 해야 합니다.




