[2024] 처음 시작하는 도커와 컨테이너 [천강민] 강의 수강 후 정리한 내용입니다.
처음 시작하는 도커와 컨테이너 강의 | 천강민 - 인프런
천강민 | , 실무에서, '도커 잘하네!' 라는 얘기가 나오도록 짧고 굵게![사진]실제 서비스 제공을 기반으로 고민하며 해결할 수 있다면, 이 강의는 수강하지 마세요.실무 요구사항을 해결해보는
www.inflearn.com
우리는 보통 소프트웨어를 설치하고 실행할 때, 직접 프로그램을 다운로드하고 환경을 설정해야 한다.
하지만 이런 방식은 환경 설정이 복잡하고, 운영체제나 버전에 따라 동작 방식이 달라지는 문제가 발생할 수 있다.
Docker 컨테이너를 사용하면 위와 같은 문제를 방지할 수 있다.
- 소프트웨어 실행에 필요한 모든 환경이 컨테이너 내부에 패키징 되어 있음
- 실행하는 시스템(OS, 라이브러리 차이)에 영향을 받지 않기 때문에 어디서든 동일한 환경에서 실행 가능하다
- 손쉽게 배포하고, 여러 개의 서비스(NGINX, PostgreSQL 등)를 격리된 상태에서 실행 가능하다
위와 같은 효과를 얻기 위해 다양한 서비스의 컨테이너를 띄워보는 실습을 해보자!
NGINX 컨테이너 띄워보기
Nginx라는 것을 컨테이너로 띄워보자. 간단히 말하면 웹서버를 띄우는 것이다.
NGINX는 고성능 웹 서버 및 리버스 프록시 서버입니다. 오픈소스로 제공되며, 웹 서버 외에도 로드 밸런서, HTTP 캐시, 미디어 스트리밍 서버 등 다양한 기능을 수행할 수 있습니다. NGINX는 특히 높은 동시 접속 처리 능력으로 잘 알려져 있으며, 대규모 트래픽을 처리하는 데 탁월한 성능을 발휘합니다.
Nginx 컨테이너 실행 명령어
일반적으로 프로그램을 실행할 때 더블 클릭으로 실행하지만, Docker에서는 명령어를 입력하여 실행한다.
docker run -p 8080:80 nginx
- docker run : 컨테이너를 실행하는 명령어 (이미지가 없는 경우 자동으로 다운로드)
- -p 8080:80 :
- 컨테이너 내부에서 NGINX는 80번 포트에서 실행됨
- 사용자는 8080번 포트로 접근
- Docker가 8080 → 80 포트로 전달하도록 설정
- 컨테이너 자체가 격리된 환경이기 때문에 네트워크도 격리되어 있어서 전달이 필요하다.
- nginx : 사용할 이미지 이름 (NGINX 컨테이너 실행)
위 명령어를 실행하게 되면 docker가 이미지를 가져와서 컨테이너가 띄워질 것이다. 이 상태에서 사용자가 http://localhost:8080으로 접근하게 되면 Welcome to nginx!라는 페이지를 보게 된다.
이렇게 Docker를 활용하여 손쉽게 웹 서버를 띄울 수 있음을 확인할 수 있다.
실습
docker run --rm -d -p 8080:80 nginx
curl http://localhost:8080
# 컨테이너들 정보 및 상태 확인
docker ps
# -f = --force, 강제 삭제 옵션
docker rm -f [CONTAINER_ID 또는 CONTAINER_NAME]
- 위 명령어를 실행하면 Docker가 해당 이미지를 다운로드한 후, 컨테이너를 실행한다.
- 다운로드가 완료되면 컨테이너가 자동으로 실행되며, 가장 하단에 실행된 컨테이너의 ID가 표시된다.
- curl http://localhost:8080 명령어는 curl이라는 명령줄 도구를 사용해 http://localhost:8080 주소로 HTTP 요청을 보내는 명령어다.
- 이 요청을 처리하는 웹 서버(Nginx 등)가 해당 주소에서 제공하는 콘텐츠를 응답으로 반환하며, 기본 설정이라면 Nginx의 기본 페이지 HTML 코드가 출력된다.
- 컨테이너 정보는 사용된 이미지, 생성 시점 등을 요약하여 보여준다.
- PORTS는 8080 포트로 접근하면 내부 80 포트로 전달됨을 나타낸다.
- 컨테이너 이름을 지정하지 않으면 NAMES가 랜덤으로 할당된다.
- STATUS가 UP이면 현재 실행 중임을 의미한다.
브라우저에서 localhost:8080으로 접근하면 HTML 코드가 브라우저에 의해 렌더링되어 출력된다.
💡 컨테이너를 띄운다는 것은 이미지를 가져와 실행하는 것을 의미한다!
- 컨테이너를 강제 삭제한 후 docker ps 명령어로 확인하면 아무것도 존재하지 않음을 확인할 수 있다.
PostgreSQL 컨테이너 띄워보기
Docker를 사용해 PostgreSQL 이미지를 레지스트리에서 가져오고, 이를 기반으로 컨테이너를 실행하는 구조다.
PostgreSQL 컨테이너를 띄워보자.
PostgreSQL(포스트그레SQL)은 오픈 소스 객체-관계형 데이터베이스 관리 시스템(ORDBMS) 으로, 데이터를 저장, 관리, 검색, 수정, 삭제하는 기능을 제공한다. SQL(Structured Query Language)을 사용하며, 다양한 확장 기능도 지원한다.
PostgreSQL 컨테이너 실행 명령어
docker volume create PG
- 컨테이너가 종료되더라도 데이터가 삭제되지 않도록 영구 저장용 볼륨을 생성한다. 생성된 볼륨의 이름은 PG로 지정된다.
docker run --name PG \
--memory="512m" --cpus="0.5" \
-v pg:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=rex \
-d postgres
- --name PG : 컨테이너의 이름을 PG로 지정한다. 이름을 지정하지 않으면 랜덤으로 할당된다.
- --memory="512m" --cpus="0.5": 컨테이너의 리소스를 제한하여 메모리는 512MB, CPU는 0.5코어까지만 사용 가능하도록 설정한다.
- -v pg:/var/lib/postgresql/data: pg 볼륨을 PostgreSQL 데이터 저장 경로(/var/lib/postgresql/data)에 연결하여 데이터가 유지되도록 설정한다.
- -e POSTGRES_PASSWORD=rex: 환경변수를 통해 PostgreSQL의 유저 비밀번호를 설정한다.
이렇게 실행하면 PostgreSQL 컨테이너가 볼륨을 사용하여 데이터를 영구 저장하면서, 리소스 제한이 적용된 상태로 실행된다.
실습
docker volume create pg
# -d 옵션이 없다면? foreground로 동작
docker run --name pg --rm \
--memory="512m" --cpus="0.5" \
-v pg:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=rex postgres
# 컨테이너들 정보 및 상태 확인
docker ps
# 컨테이너 내부에 프로세스를 실행(exec)할건데,
# 직접 터미널을 사용하는 것처럼(-it) 설정해서,
# bash를 실행해줘
docker exec -it pg bash
psql -U postgres
exit
exit
# 컨테이너들 자원 사용량 확인
docker stats
# 볼륨이 이미 사용중이어서 삭제 불가
docker volume rm pg
docker rm -f pg
docker volume rm pg
- 현재 환경에 존재하는 이미지 목록을 확인하려면 docker images 명령어를 사용하고,
- 볼륨 현황을 확인하려면 docker volume ls 명령어를 사용하면 된다.
- docker volume create를 통해 볼륨을 만들어준다.
- -d 옵션을 사용하지 않았기 때문에 docker run 명령어로 실행하면 포그라운드(foreground) 모드에서 동작하게 되어, 다른 명령어를 입력할 수 없다.
- 따라서, 새 터미널 창을 열어 필요한 명령어를 입력해야 한다.
- PORTS와 같은 경우는 5432/tcp로 표시가 되어 있는데 이 상태면 외부에서 접근을 할 수 없는 형태이다.
- exec는 컨테이너 내부에서 프로세스를 실행하는 명령어다.bash는 Bash 쉘을 실행하여 컨테이너 내부에서 명령을 입력할 수 있도록 해준다.
- -it 옵션은 직접 터미널을 사용하는 것처럼 인터랙티브 모드로 실행하도록 설정한다.
- docker stats 명령어를 통해 현재 동작하고 있는 docker들의 상태, 리소스 사용량 등을 확인할 수 있다.
- 볼륨은 사용 중일 때 삭제할 수 없으므로, 먼저 컨테이너를 삭제한 후 삭제해야 한다.
- 이미지를 지우기 위해서는 docker rmi 명령어를 사용한다.
💡 Docker를 사용하면 데이터베이스 설치 과정이 매우 간단해지며, 메모리 및 CPU 제한 같은 설정도 손쉽게 적용할 수 있다.
httpd 컨테이너 띄워보기
httpd를 실행해보자.
httpd는 웹 서버 소프트웨어의 한 종류로, 주로 Apache HTTP Server를 의미합니다. Apache HTTP Server는 오픈 소스 웹 서버로, 전 세계에서 가장 널리 사용되는 웹 서버 중 하나입니다.
httpd 컨테이너 실행 명령어
-v 옵션과 Bind Mount
- -v 옵션을 사용할 때 특정 경로를 지정하면 볼륨이 아닌 bind mount 방식을 사용할 수 있다.
- 현재 PC의 디렉토리(.)를 콜론(:) 뒤에 지정된 경로와 연결한다.
- 예를 들어, 현재 디렉토리의 파일들을 /usr/local/apache2/htdocs/:ro에 연결하면, 컨테이너 내부에서 해당 파일을 읽을 수 있지만 수정(ro = read-only)은 불가능하다.
Bind mount는 리눅스 및 유닉스 계열 운영체제에서 사용되는 파일 시스템 기능으로, 하나의 디렉토리를 다른 디렉토리에 마운트하여 동일한 파일 및 디렉토리 구조를 공유하는 방법입니다. 원본 파일 시스템 구조를 변경하지 않고도, 특정 디렉토리를 다른 경로에서도 접근할 수 있도록 합니다.
Bind Mount를 사용하면 원본 디렉토리와 바인드 마운트된 디렉토리가 동일한 파일과 디렉토리 구조를 공유하며, 원본에서 변경된 내용이 즉시 반영됩니다. 또한, 별도의 저장 공간을 차지하지 않고 기존 파일 시스템을 그대로 참조하기 때문에 중복 저장 공간이 필요하지 않습니다. 이 방식은 특정 파일 시스템이나 하드웨어에 종속되지 않고 독립적으로 동작하여 유연한 관리가 가능합니다.
httpd:2.4
- 특정 이미지를 생성하는 사람(관리자 또는 개발자)이 태그(tag)를 지정하여 별칭을 설정할 수 있다.
- httpd:2.4는 Apache HTTP Server 2.4 버전을 의미하며, 특정 버전을 명시적으로 사용하고자 할 때 유용하다.
실습
💡 Docker 사용 시 주의할 점
Docker를 사용할 때는 올바른 경로에서 터미널을 실행하는 것이 중요하다. 잘못된 경로에서 실행하면 예상치 못한 결과가 발생할 수 있으므로, 작업하려는 디렉토리를 먼저 확인한 후 실행하는 것이 좋다.
# httpd.sh
docker run --rm --name httpd -p 8080:80 \
-v .:/usr/local/apache2/htdocs/:ro \
-d httpd:2.4
curl http://localhost:8080
# 로컬 index.html 수정해보기
curl http://localhost:8080
# 컨테이너 내부에서 index.html 수정해보기
docker exec -it httpd bash
# bash: /usr/local/apache2/htdocs/index.html: Read-only file system
# 오류 발생! 왜냐하면 Read-Only(ro) 라서
echo 123 >/usr/local/apache2/htdocs/index.html
docker rm -f httpd
# index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>My Website with a Whale & Docker!</title>
</head>
<body>
<h1>Whalecome!!</h1>
<p>Look! There's a friendly whale greeting you!</p>
<pre id="docker-art">
## .
## ## ## ==
## ## ## ## ## ===
/"""""""""""""""""\___/ ===
{ / ===-
\______ O __/
\ \ __/
\____\_______/
Hello from Docker!
</pre
>
</body>
</html>

- -d 옵션을 사용하면 컨테이너가 백그라운드에서 실행된다.
- 호스트 머신의 현재 디렉토리(.)를 컨테이너의 /usr/local/apache2/htdocs/ 디렉토리에 마운트하면, 이 경로가 Apache HTTP 서버의 기본 문서 루트 디렉토리로 사용된다.
- 따라서, 현재 디렉토리에 index.html 파일이 존재한다면 컨테이너 실행 후 웹 브라우저에서 http://localhost:8080으로 접속하면 해당 index.html 파일이 웹 서버를 통해 제공된다.

- curl 명령어를 통해서 접속하게 되면 바로 index.html이 반환되는 것을 확인할 수 있다.
- 저장되어 있는 html의 내용을 변경하게 될 경우 수정사항이 바로 반영되어 변환된 index.html을 확인할 수 있다.

- exec 명령어를 사용하면 컨테이너 내부에서 프로세스를 실행할 수 있으며, 이를 통해 bash 셸을 실행할 수도 있다.
- docker exec 명령어를 사용하면 격리된 컨테이너 환경 내에서 명령어를 실행할 수 있다.
- 예를 들어, 컨테이너 내부의 쉘을 열거나 특정 프로그램을 실행하는 것이 가능하다.
- 컨테이너 내부에서 ls 명령어를 실행하면 /usr/local/apache2 아래의 디렉토리 목록을 확인할 수 있으며, htdocs 디렉토리로 이동하면 index.html 파일이 존재하는 것을 확인할 수 있다.

- index.html 파일에 echo를 통해 내용을 덮어쓸려고 하면 Read-only이기 때문에 에러가 발생한다.
- 이를 통해 컨테이너 내부에서는 파일을 수정할 수 없고, 외부에서만 수정할 수 있는 환경을 만들 수 있다는 것을 확인할 수 있다.

- docker stats 명령어를 통해 확인해보면 docker run을 할 때 메모리 및 리소스 사용량에 대한 제한을 하지 않았기 때문에 전체를 사용하는 것을 확인할 수 있다.

- 볼륨을 생성한 것이 아니라 링크와 같은 방식으로 연결했기 때문에, 별도의 볼륨은 존재하지 않는다.
- 바인드 마운트를 사용했기 때문에 현재 디렉토리의 페이지가 웹 서버를 통해 제공되며, 이를 통해 웹 브라우저에서 해당 웹 페이지를 확인할 수 있다!
node 컨테이너 띄워보기
빌드를 하고 이미지를 만들어서 docker run을 띄워보는 실습을 해보자!
이번 실습에서는 Node.js 이미지를 레지스트리에서 가져와 사용하게 된다.
이후, 웹이라는 이름의 이미지를 직접 만들어 보고, docker run 명령어를 통해 실행하는 과정을 진행한다.
Node.js(줄여서 Node)는 서버 측에서 JavaScript를 실행할 수 있도록 해주는 런타임 환경입니다. 일반적으로 JavaScript는 웹 브라우저에서 실행되지만, Node.js는 이를 브라우저 외부에서도 실행 가능하도록 지원합니다. 이를 활용하면 웹 서버를 구축하거나 다양한 서버 사이드 애플리케이션을 개발할 수 있습니다.

httpd 컨테이너 실행 명령어
docker bulid -t web
- 명령어를 실행하면 현재 디렉토리(.)에 있는 Dockerfile을 참고하여 web이라는 이름의 이미지를 생성한다.
- 별도로 파일 경로를 지정하지 않으면, 기본적으로 현재 디렉토리의 Dockerfile을 사용한다.
# Dockerfile
# 21 별칭이 붙은 node라는 이미지로부터
FROM node:21
# work directory 지정
WORKDIR /usr/src/app
# package.json이라는 것을 workdir로 지정한 경로에 복사
# run을 통해서 실행.
# npm install을 통해 package.json에 정의된 종속성을 설치
COPY package.json ./
RUN npm install
COPY ./server.js ./
# CMD를 통해 nmm start를 실행해줌
CMD ["npm","start"]
- Dockerfile을 통해 Node.js가 설치된 이미지를 기반으로 종속성과 패키지를 설치하고, 컨테이너 실행 시 npm start를 실행하도록 설정한다.
- 이 과정을 거쳐 web이라는 이름의 이미지가 생성된다.
docker run -d --rm -p 8080:5000 --name web web
- 명령어를 실행하면, 해당 이미지를 사용하여 컨테이너를 실행할 수 있다.
- web 이미지를 기반으로, web이라는 이름의 컨테이너를 생성하고
- 백그라운드(-d)에서 실행하며, 종료 시 자동 삭제(--rm)되도록 설정한다.
- 호스트의 8080 포트를 컨테이너의 5000 포트에 연결(-p 8080:5000)
- 이제 http://localhost:8080에서 실행 중인 웹 애플리케이션을 확인할 수 있다.
실습
// server.js, 자바스크립트 파일
const express = require("express");
const app = express();
app.get("/", function (req, res) { // 루트 경로를 통해 들어오면 res.json 리스폰스를 json 형태로 주겠다는 것
return res.json({ "docker-and-containers-from-scratch": req.ip }); // 사용자 IP 그대로 전달
});
app.listen(5000, function () { // 포트 5000을 통해 listen을 하고 있겠다는 것
console.log("Web application is listening on port 5000");
});
# 8080 포트 사용중인 컨테이너 없는지 확인
docker ps
# 현재경로(.)를 기준으로 Dockerfile에 명시된 명렁을 실행해 web 이라는 이름의 이미지 생성
docker build -t web .
# docker image ls와 동일
docker images
docker run -d --rm -p 8080:5000 --name web web
docker rm -f web
# 재밌는 장난 해보기
# --rm 옵션이 없으면 컨테이너가 끝까지 삭제되지 않음
docker run -p 8080:5000 --memory="10m" --name web web
# 실행 중인(running) 컨테이너 목록에 보이지 않음
docker ps
# 모든 상태를 보여주는(-a) 목록에 보임
docker ps -a
# 컨테이너에 대한 자세한(inspect) 정보 보기
docker inspect web
docker rm -f web

- 8080포트로 컨테이너를 실행시킬 것이기 때문에 기존에 8080포트로 실행되고 있는 컨테이너가 있는지 확인이 필요하다.

- Dockerfile에 명시된 내용들이 순차적으로 실행이 되면서 표시가 된다.
- CMD를 통해 실행되는 npm start는 원래 표시가 되지 않는다.

- 이미지를 생성하고 난 후, docker images 명령어를 통해 확인해보면 이미지가 생성된 것을 확인할 수 있다.

- 만든 web이라는 이미지를 통해 web이라는 이름의 컨테이너를 실행해준다.
- 8080포트로 접근을 할 건데, 5000으로 전달을 하도록 요청하는 것이다.

- curl 명령어를 통해 접근하면 server.js에 명시된대로 요청자의 IP 주소가 반환된다.

- --rm 옵션이 없으면 컨테이너가 종료되더라도 완전히 삭제되지 않고 남아 불필요한 컨테이너가 쌓일 수 있다.
- 또한, docker run 실행 시 메모리를 10MB로 제한했기 때문에 Out Of Memory(OOM) 문제가 발생하여 컨테이너가 강제 종료되었다.
- 이후 docker ps 명령어를 실행하면 실행 중인 컨테이너 목록만 표시되므로, 아무 목록도 나타나지 않는다.
- 하지만 docker ps -a 명령어를 사용하면 모든 상태(실행 중 + 종료된 컨테이너)의 목록을 확인할 수 있으며, 이때 삭제되지 않고 종료 상태로 남아 있는 컨테이너를 볼 수 있다.
- 이러한 불필요한 컨테이너가 남는 문제를 방지하기 위해 docker run 실행 시 --rm 옵션을 사용하는 것이 좋다.

- 컨테이너의 자세한 정보를 확인하기 위해 docker inspect 컨테이너명 명령어를 사용할 수 있다.
- 이 명령어를 실행하면 컨테이너의 설정, 상태, 네트워크 정보 등을 포함한 상세 정보를 확인할 수 있으며,
- OOMKilled: true 항목을 통해 메모리 부족(OOM, Out Of Memory)로 인해 컨테이너가 종료되었음을 확인할 수 있다.
'Study > Docker' 카테고리의 다른 글
[Docker 입문] Registry & Repository (0) | 2025.03.09 |
---|---|
[Docker 입문] 컨테이너는 작은 운영체제다 (0) | 2025.02.27 |
[Docker 입문] 도커, 컨테이너, 쿠버네티스 (0) | 2025.02.27 |