Dockerfile은 도커 이미지를 만드는 설계도다. 이 글에서는 Dockerfile로 nginx 기반 커스텀 이미지를 빌드해 컨테이너로 실행하고, 직접 만든 로컬 레지스트리에 그 이미지를 푸시하는 전 과정을 정리한다. Dockerfile 지시어 이해 → docker build 빌드 → 컨테이너 실행 확인 → registry:2로 로컬 레지스트리 구축 → docker tag·push·pull 순서로 다룬다. 실습 환경은 윈도우 11 + 도커 데스크톱이다.
Dockerfile이란
Dockerfile은 이미지를 어떻게 만들지 한 줄씩 적어 둔 텍스트 파일이다. docker build는 이 파일을 위에서 아래로 읽으며 각 지시어를 한 층(layer)씩 쌓아 최종 이미지를 완성한다. 자주 쓰는 핵심 지시어는 다음과 같다.
| 지시어 | 역할 |
|---|---|
FROM | 기반이 되는 베이스 이미지 지정 (모든 Dockerfile의 첫 줄) |
RUN | 빌드 중 컨테이너 안에서 명령 실행 (패키지 설치 등) |
COPY | 호스트의 파일·디렉터리를 이미지 안으로 복사 |
WORKDIR | 이후 명령이 실행될 작업 디렉터리 지정 |
EXPOSE | 컨테이너가 사용하는 포트를 문서화 (실제 개방은 아님) |
CMD | 컨테이너 실행 시 기본으로 돌릴 명령 (덮어쓰기 가능) |
ENTRYPOINT | 컨테이너를 실행 파일처럼 고정 동작시키는 진입점 |
여기서 EXPOSE는 자주 오해하는 지시어다. 도커 공식 문서에 따르면 EXPOSE는 어떤 포트를 쓰는지 알려 주는 문서·메타데이터 역할일 뿐 실제로 포트를 개방하지는 않는다. 외부에서 접속하려면 컨테이너를 실행할 때 -p 옵션으로 직접 포트를 게시(publish)해야 한다. 또 CMD와 ENTRYPOINT는 ["nginx", "-g", "daemon off;"]처럼 대괄호로 쓰는 exec 형식이 권장된다. exec 형식은 셸(/bin/sh -c)을 거치지 않고 명령을 직접 실행해 신호 처리가 정확하다.
Dockerfile 작성
먼저 nginx 최신 버전을 기반으로 하는 Dockerfile을 작성한다. 파일 이름은 확장자 없이 정확히 Dockerfile로 만든다.
# 기본 이미지로 nginx 최신 버전 사용
FROM nginx:latest
# (선택) 호스트의 파일을 컨테이너 경로로 복사해 커스텀 설정/정적 파일 추가
# COPY ./your-custom-nginx-config.conf /etc/nginx/nginx.conf
# COPY ./your-website-static-files /usr/share/nginx/html
# Nginx가 포트 80을 사용함을 명시 (문서화 목적, 기본 이미지에 이미 선언되어 생략 가능)
EXPOSE 80
# Nginx 실행 (nginx 베이스 이미지의 기본 CMD와 동일하므로 생략 가능)
CMD ["nginx", "-g", "daemon off;"]
이번 실습에서 만들 이미지와 컨테이너 사양은 다음과 같다.
| 컨테이너 이름 | 베이스 이미지 | 빌드할 이미지 이름 | 포트 |
|---|---|---|---|
| my-running-nginx | nginx:latest | my-nginx | 80 |
Dockerfile로 이미지 빌드
Dockerfile이 위치한 디렉터리로 이동해 다음 명령으로 이미지를 빌드한다. 끝의 .은 현재 디렉터리를 빌드 컨텍스트로 사용하라는 의미다.
docker build -t my-nginx .
빌드가 진행되면 각 지시어가 단계(STEP)별로 실행되는 로그가 출력되고, 마지막에 이미지가 만들어진다.
# 빌드 로그 예시
[+] Building 3.2s (5/5) FINISHED
=> [internal] load build definition from Dockerfile
=> [1/1] FROM docker.io/library/nginx:latest
=> exporting to image
=> => naming to docker.io/library/my-nginx
주의할 점은 파일 이름을 정확히 Dockerfile로 지정해야 한다는 것이다. DockerFile이나 dockerfile처럼 다르게 쓰면 빌드가 파일을 찾지 못한다. 빌드가 끝나면 docker images로 생성된 이미지를 확인할 수 있다.
docker images
# 출력 예시
REPOSITORY TAG IMAGE ID CREATED SIZE
my-nginx latest a1b2c3d4e5f6 10 seconds ago 187MB
nginx latest f1e2d3c4b5a6 2 weeks ago 187MB
컨테이너 실행 확인
빌드한 이미지로 컨테이너를 실행한다. 명령 형식은 다음과 같다.
docker run --name <컨테이너 이름> -d -p <호스트포트:컨테이너포트> <이미지 이름>
이 글에서는 다음과 같이 이름과 포트를 지정해 컨테이너를 실행했다. -d는 백그라운드 실행, -p 80:80은 호스트 80번 포트를 컨테이너 80번 포트에 연결한다.
docker run --name my-running-nginx -d -p 80:80 my-nginx
컨테이너가 떴는지 docker ps로 확인한다. 그런 다음 브라우저에서 http://localhost/로 접속하면 nginx 기본 페이지(“Welcome to nginx!”)가 나타난다.
docker ps
# 출력 예시
CONTAINER ID IMAGE COMMAND PORTS NAMES
b7c8d9e0f1a2 my-nginx "/docker-entrypoint.…" 0.0.0.0:80->80/tcp my-running-nginx
도커 데스크톱의 Containers 탭에서도 my-running-nginx가 실행 중(running)으로 표시된다.
Docker 레지스트리란
지금까지는 이미지를 내려받아 컨테이너를 만들었다. 그 이미지들은 도커 허브에서 다운로드한 것이다. 이렇게 이미지가 저장·배포되는 곳을 통틀어 도커 레지스트리라고 한다. 공개·비공개 여부와는 무관하다.
도커 허브(Docker Hub)는 도커 제작사가 운영하는 공식 공개 레지스트리다. nginx·MariaDB·Tomcat 같은 공식 이미지도 각 제작사가 도커 허브에 올려 둔 것을 우리가 내려받아 쓴 것이다. 이번에는 도커 허브 대신 로컬 레지스트리를 직접 만들어 앞서 빌드한 nginx 이미지를 푸시해 본다. 사내 환경처럼 이미지를 외부에 공개하지 않고 내부에서만 공유할 때 이런 프라이빗 레지스트리를 쓴다.
로컬 레지스트리 구축 (registry:2)
1. 레지스트리 컨테이너 실행
도커는 레지스트리 자체를 컨테이너 이미지(registry)로 제공한다. 다음 명령으로 registry:2 이미지를 받아 로컬 레지스트리를 띄운다. 레지스트리는 기본적으로 5000번 포트를 사용한다.
docker run -d -p 5000:5000 --name registry registry:2
실행 후 docker ps로 보면 레지스트리 컨테이너가 5000번 포트에 떠 있다. 레지스트리 주소는 localhost:5000이다. 다만 이 주소를 브라우저에 입력해도 사람이 볼 화면은 없다. 대신 REST API로 저장된 이미지 목록을 확인할 수 있다.
# 레지스트리에 저장된 리포지토리 목록 조회 (아직 비어 있음)
curl http://localhost:5000/v2/_catalog
# 출력 예시
{"repositories":[]}
도커 데스크톱의 Containers 탭에서도 registry 컨테이너가 실행 중인 것을 확인할 수 있다.
2. 이미지에 태그 지정 (docker tag)
my-nginx처럼 간단히 붙였던 이름에 레지스트리 주소·리포지토리 이름·버전을 더해 정식 명칭으로 바꾼다. 도커는 태그의 앞부분이 호스트:포트 형태이면 그곳을 레지스트리 주소로 해석한다. 태그 형식은 레지스트리주소/리포지토리이름:버전이다.
docker tag <이미지 이름> <레지스트리 주소>/<리포지토리 이름>:<버전>
실습에서는 다음과 같이 태그를 지정했다. docker images로 보면 같은 IMAGE ID에 새 이름이 추가된 것을 확인할 수 있다.
docker tag my-nginx:latest localhost:5000/my_nginx_240306:1
# 확인
docker images
# 출력 예시
REPOSITORY TAG IMAGE ID SIZE
my-nginx latest a1b2c3d4e5f6 187MB
localhost:5000/my_nginx_240306 1 a1b2c3d4e5f6 187MB
3. 레지스트리에 푸시 (docker push)
docker push로 태그를 붙인 이미지를 레지스트리에 업로드한다. 각 레이어가 전송된 뒤 다이제스트가 출력되면 성공이다.
docker push localhost:5000/my_nginx_240306:1
# 출력 예시
The push refers to repository [localhost:5000/my_nginx_240306]
1: digest: sha256:9a8b7c6d... size: 1778
다시 _catalog를 조회하면 방금 푸시한 리포지토리가 보인다.
curl http://localhost:5000/v2/_catalog
# 출력 예시
{"repositories":["my_nginx_240306"]}
4. 레지스트리에서 내려받기 (docker pull)
다른 머신에서 이미지를 쓰려면 같은 방식으로 docker pull하면 된다. 푸시가 잘 됐는지 확인하려면 로컬 이미지를 지운 뒤 레지스트리에서 다시 받아 본다.
docker pull localhost:5000/my_nginx_240306:1
다른 호스트에서 접근 — insecure-registries
localhost:5000은 예외적으로 그냥 동작하지만, 다른 PC에서 서버IP:5000 형태로 접속하면 기본적으로 HTTPS를 요구해 푸시·풀이 막힌다. 테스트 환경에서 TLS 없이 쓰려면 클라이언트 쪽 도커 데몬 설정(/etc/docker/daemon.json 또는 도커 데스크톱 Settings → Docker Engine)에 해당 주소를 insecure-registries로 등록하고 도커를 재시작한다.
{
"insecure-registries": ["192.168.0.10:5000"]
}
다만 도커 공식 문서는 TLS 없는 insecure-registries 설정은 통제된 테스트 환경에만 적합하다고 명시한다. 운영 환경에서는 반드시 TLS 인증서를 적용한 레지스트리를 사용해야 한다.
Docker Hub에 푸시하기
로컬 레지스트리 대신 공식 레지스트리인 도커 허브에 올릴 수도 있다. 이메일 주소만 있으면 누구나 https://hub.docker.com에서 가입할 수 있고, 먼저 써 보기만 한다면 무료 플랜으로 충분하다. 도커 허브에 푸시하는 흐름은 로컬 레지스트리와 거의 같으며, 태그 앞에 레지스트리 주소 대신 본인의 도커 허브 사용자명을 붙인다는 점만 다르다.
# 1. 로그인
docker login
# 2. 사용자명/리포지토리:태그 형식으로 태그 지정
docker tag my-nginx:latest <사용자명>/my-nginx:1
# 3. 푸시
docker push <사용자명>/my-nginx:1
무료 플랜이라도 리포지토리를 여러 개 만들어 방치하지 않는 것이 좋다.
자주 묻는 질문 (FAQ)
EXPOSE를 쓰면 포트가 열리나요?
아니다. EXPOSE는 어떤 포트를 쓰는지 알려 주는 문서·메타데이터일 뿐이다. 외부 접속을 열려면 docker run -p 호스트포트:컨테이너포트로 직접 포트를 게시해야 한다.
CMD와 ENTRYPOINT는 어떻게 다른가요?
CMD는 docker run 뒤에 명령을 적으면 쉽게 덮어쓸 수 있는 기본값이고, ENTRYPOINT는 컨테이너를 특정 실행 파일처럼 고정해 두는 진입점이다. 둘은 함께 쓰여 ENTRYPOINT로 실행 파일을, CMD로 기본 인자를 지정하는 패턴이 흔하다.
localhost:5000으로 푸시가 안 됩니다.
① docker ps로 레지스트리 컨테이너가 떠 있는지, ② 태그를 localhost:5000/... 형식으로 정확히 붙였는지 확인한다. 다른 호스트 IP로 접속한다면 앞서 설명한 insecure-registries 설정과 도커 재시작이 필요하다.
정리
Dockerfile로 nginx 커스텀 이미지를 빌드하고, registry:2로 로컬 레지스트리를 띄운 뒤 docker tag로 정식 명칭을 부여해 docker push·pull하는 흐름을 살펴봤다. 핵심 순서는 Dockerfile 작성 → docker build → 컨테이너 실행 확인 → 레지스트리 컨테이너 실행 → 태그·푸시·풀이다. 도커 설치 단계부터 다시 확인하고 싶다면 RHEL 8 리눅스에 도커(Docker) 설치하기를 참고하면 된다.



💬 댓글 0