[LG CNS AM Inspire Camp 1기] Docker (1) - Docker 시작하기

정성엽·2025년 2월 12일
0

LG CNS AM Inspire 1기

목록 보기
47/53
post-thumbnail

INTRO

이번 포스팅부터는 Docker와 관련된 기초 개념부터 어떻게 우리가 만든 프로젝트를 컨테이너 기반으로 관리할 수 있는지를 정리해보려고 한다.

사실 필자는 도커를 제대로 사용해본 경험이 없다.

이번에 LG CNS AM Inspire Camp에서 도커 관련 내용을 수강하고 있는데, 생각보다 커맨드를 통해 컨테이너를 관리하는 것도 꽤 재미있다.

이번 포스팅에서는 우선 Docker란 무엇인지부터 간단하게 살펴보자 👀


1. 개념 복습

Velog에 포스팅을 시작하면서 초반에 클라우드와 컨테이너 관련해서 정리한 경험이 있다.

이때, 포트 개념도 정리하고 넘어갔는데 이번 기회에 다시 한번 정리해보자

이전에 작성한 포스팅
1. [LG CNS AM Inspire Camp 1기] Cloud Native 한번에 이해하기
2. [LG CNS AM Inspire Camp 1기] MSA(Microservices Architecture)와 컨테이너의 관계
3. [LG CNS AM Inspire Camp 1기] Port개념 이것도 알고 있나요?

💡 Port Number

간단하게 정리하면 Port Number는 호스트 내에서 동작하는 어플리케이션 (프로그램)을 식별하기 위한 번호이다.

외부에서 요청이 들어오면 가상서버 혹은 컨테이너 내부에서 동작하는 어플리케이션은 독립적으로 동작하고 있다.

외부 요청에 대해 호스트는 독립적으로 수행되고 있는 이 어플리케이션과 연결을 해줘야 한다.

이때, 포트 번호를 사용하여 포트번호와 어플리케이션을 묶어주는 포트 바인딩을 통해 외부에서 어플리케이션에 접근할 수 있도록 도와줄 수 있다.

이렇게 포트 바인딩까지 완료되면, 호스트의 운영체제는 각 포트번호에 들어오는 요청을 Listening하고 있다가 요청이 들어오면 어플리케이션 (프로그램 / 프로세스)의 포트에 요청을 넣어주는 작업을 수행한다.

도커 강의를 수강해보니 이런 큰 맥락적인 부분이 중요한 것 같으니 기억해두자!

💡 Container

컨테이너는 어플리케이션과 그 실행에 필요한 모든 것(라이브러리, 환경변수, 설정 파일 등)을 하나로 패키징한 유닛을 의미한다.

이를 통해 어플리케이션을 어떤 환경에서도 동일하게 실행할 수 있다.

또한, 호스트의 운영체제를 사용하며 컨테이너는 빠르게 배포가 가능하다는 특징이 있다.

이제부터 필자가 정리할 Docker는 레이어 기반 아키텍처를 사용하여 이미지를 구성한다는 특징이 있다.

여기서 각 레이어는 이미지 생성 과정의 한 단계를 나타낸다.

앞으로 사용할 Dockerfile의 예시를 한번 살펴보자

FROM golang:1.23.6  # Base Layer
RUN mkdir /goapp 	# Layer 1
COPY main.go /goapp # Layer 2
CMD ["go", "run", "/goapp/main.do"] # Layer 3

레이어는 당연히 엄청 많겠지만, 우리가 Dockerfile을 생성한다면 이처럼 레이어 단위로 이미지를 생성하게 된다.

예를들어 우리가 이미 golang:1.23.6이라는 이미지를 가지고 있다면 외부에서 이를 다운로드할 필요 없이 기존에 설치된 내용을 가져다 사용하기 때문에 빌드 시간 단축 & 저장 공간의 효율성 측면에서 장점을 갖게 된다.


2. 도커 이미지 생성 방법

Docker에서 이미지를 생성하는 방법은 총 3가지가 있다.

1. Dockfile
2. Container 생성 후 커밋
3. Container 생성 -> 그 상태로 export하여 Tar files 생성 -> import로 이미지 생성

지금까지 강의를 수강해보니 주로 Dockfile을 이용하여 이미지를 생성하고, 생성된 이미지를 기반으로 컨테이너를 실행시키는 방법을 사용하고 있다.

실제로도 Dockfile을 사용하는 방법을 권장한다고 하는데, 그 이유는 바로 Inspection이 가능하기 때문이다.

(Dockfile을 살펴보면 이미지에서 어떤 작업을 수행하는지를 간단하게 확인할 수 있다! - Inspection)

💡 Docker 사용 X

우선 Docker를 사용하지 않는다면 어떻게 배포가 진행될까?

우선 Go 언어로 작성된 코드를 하나 살펴보자

Sample Code

/* 8080 포트로 HTTP 요청을 대기하다가, /로 요청이 들어오면 Hello Docker!!!를 응답 */

/* 프로그램의 진입점인 main 패키지를 지정 */
package main

/* 표준 입출력과 문자열 형식을 처리하는 Go 패키지, 
   로그를 출력하기 위한 패키지, 
   HTTP 서버와 클라이언트 관련 기능을 제공하는 패키지를 임포트 */
import (
	"fmt"
	"log"
	"net/http"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		log.Println("received request")
		fmt.Fprintf(w, "Hello Docker!!")
	})

	log.Println("start server")
	server := &http.Server{Addr: ":8080"}
	if err := server.ListenAndServe(); err != nil {
		log.Println(err)
	}
}

이 go파일을 다른 PC에서 실행해야한다면 다음과 같은 과정을 반복해야 한다.

Go 파일 실행 과정
Go 설치 -> go run main.go로 서버 띄우기 -> 외부 서버에서 curl http://localhost:8080 으로 접속 등등..

이런 불필요한 과정을 생략하기 위해 어플리케이션과 어플리케이션 실행에 필요한 환경(go)를 묶어서 배포하는 기술이 필요하게 되었다.

그게 바로 컨테이너 이다.

💡 Docker 사용 O

우선 이전에 설명한 방법과 같이 Docker image를 생성하는 방법은 총 3가지가 존재한다.

우선 Dockerfile을 생성해보자

Sample Code

FROM golang:1.23.6
RUN mkdir /goapp
COPY main.go /goapp
CMD ["go", "run", "/goapp/main.do"]

FROM 명령어를 통해 go를 실행하기 위한 Base Layer인 golang을 우선 가져온다.

다음으로 RUN은 우리가 이미지를 생성하는 과정에서 수행할 커맨드 라인을 추가할 수 있는데 goapp 이라는 폴더를 생성하는 과정이다.

다음으로 COPY 명령어를 통해 현재 디렉토리에 있는 main.go 를 RUN 명령어로 생성한 goapp 디렉토리에 copy를 진행한다.

마지막으로 go run /goapp/main.do 커맨드를 추가하여 실행시키는 이미지이다.

사실 필자도 Dockfile을 이번에 처음 접하기 때문에 문법이 너무나도 생소하다.

하지만, 자꾸 실습을 통해서 보다보니 눈에 익어가는 중이다.

(우선 내용을 잘 모르더라도 대충 맥락만 이해하고 넘어가도 괜찮을 것 같다!)

다음으로 이미지를 빌드하기 위해서는 다음과 같은 커맨드를 입력하면 된다.

이미지 빌드 커맨드
docker image build -t example/echo:latest .


-t : 태그를 설정하는 옵션이다. 여기서는 example/echo:latest가 태그가 된다.

해당 커맨드를 설명하자면 현재 디렉토리에 있는 Dockerfile을 기반으로 example/echo:latest 태그를 가진 이미지를 생성하라는 의미이다.

아무래도 이런 커맨드도 자꾸 보다보면 익숙해질 것이다.

💡 Docker 이미지 생성 후 로그 살펴보기

위 커맨드를 입력하면 다음과 같은 로그가 발생한다.

여기서 각각 어떤 의미를 갖는지 정리해보자

docker.io

  • github와 같은 registry 역할이다.
  • 즉, docker.io라는 저장소를 의미한다.

library

  • repository를 의미한다.
  • github에서 사용하는 repository와 의미가 동일하다.

golang

  • 이미지 이름을 의미한다.

1.23.6

  • 우리가 사용하는 이미지의 태그 (tag)를 의미한다.

다음으로 나오는 로그를 확인해보자

sha256으로 해싱된 값들이 나오면서 용량과 다운로드 시간을 보여주는 로그가 발생한다.

이 부분이 바로 Layered Architecture인데, 하나의 파일을 통째로 다운로드 하는 것이 아니라 각각의 Layer를 다운로드 한 결과이다.

이처럼 Layered Architecture를 사용하여 배포 속도에서 장점을 가져간다.

다음으로 로그를 쭉 살펴보자

나머지 로그를 살펴보면 레이어드 아키텍쳐 덕분에 우리가 추가한 코드도 기존 레이어 위에 쌓이면서 실행되는 모습을 볼 수 있다!

마지막으로 exporting to image 가 실행되어 도커 이미지를 생성하고 있다.

💡 생성한 도커 이미지 확인하기

도커 이미지를 확인하기 위해서는 다음과 같은 커맨드를 입력하면 된다.

도커 이미지 확인 커맨드
docker image ls

아마 리눅스를 많이 접해본 사람이라면 ls와 같은 기본적인 문법은 알고 있을 것이다.

실행결과는 다음과 같다.

이처럼 Repository / tag /image id 등의 정보를 포함하여 방금 생성했던 example/echo 이미지가 생성되어있는 모습을 확인할 수 있다.


3. 도커 컨테이너 생성 방법

컨테이너를 생성하기 위해서는 create 명령어를 사용하면 된다.

우선 커맨드를 살펴보면 다음과 같다.

도커 컨테이너 생성 커맨드
docker container create -d -p 8282:8080 example/echo:latest


-d : detach 모드로 컨테이너를 생성한다. 여기서 detach 모드는 백그라운드 프로세스로 프로그램을 실행시킨다는 의미를 갖는다.
백그라운드 프로세스는 기존에 사용하던 언어의 표현식을 빌리자면 비동기 방식 으로 동작함을 의미한다.
반면, -d 옵션을 사용하지 않는다면 attach 모드로 컨테이너가 생성된다.
attach모드는 간단하게 동기 방식 이라고 이해하면 될 것 같다.


-p : 포트 바인딩을 의미한다. 즉, 호스트의 8282 포트를 컨테이너의 8080포트와 연결하는 것이다.
만약, 호스트 포트를 지정하지 않더라도 호스트에서 사용가능한 포트를 자동으로 지정한다.

-d 옵션에 대해서는 조금 더 설명이 필요할 수 있다.

포그라운드 방식으로 실행되는 attach 모드는 컨테이너가 제어권을 반납하면 종료된다.

즉, 포그라운드로 동작하게 되어 컨테이너의 제어권이 반납되지 않는다면 동일한 터미널에서 다른 작업을 수행할 수 없다.

주로 개발할 때, 출력되는 로그들을 확인하기 위해서 포그라운드로 사용한다고 한다.

우리가 도커 컨테이너를 생성하기 위해서 커맨드 중간에 create 키워드를 사용했다.

만약, 컨테이너를 생성하고 실행하는 것을 동시에 진행하고 싶다면 create 부분을 run 으로 변경해주면 된다!

(앞으로 계속 마주할 커맨드니까 간단하게 읽고 넘어가자)


4. 컨테이너 이미지를 도커 허브에 등록

Docker를 사용하면 Docker Hub라는 공용 저장소가 존재한다.

여기는 사용자 계정별로 사용할 수 있는 디렉토리가 나뉘어져 있는 공간이다.

이전 과정에서 생성한 이미지를 우리의 개인 공간에서 저장하고 관리하고 싶다면 허브에 등록해두면 된다.

등록 커맨드는 다음과 같다.

컨테이너 이미지를 도커 허브에 등록
docker image push example/echo:latest

우리가 생성한 도커 이미지를 push하면 된다.
example/echo:latest 는 이미지 식별자를 의미하는데, repository(example/echo) + tag(latest) 혹은 image id를 넣어서 사용할 수 있다.

결과는 다음과 같다.

push가 거절된 모습을 볼 수 있다.

위 로그에서 repository를 살펴보면 docker.io/example/echo 로 되어있다.

즉, 해당 repository를 그대로 사용한다면 우리의 도커 아이디가 example로 되어있어야 한다.

하지만, 우리는 개인 계정이 존재하기 때문에 이미지의 이름을 우리 개인 계정에 맞게 변경해주자

이미지 Repository 변경 커맨드
docker image tag example/echo:latest jjabc3758/echo:latest


Result View

이미지의 repository 이름을 변경하고 image 목록을 찍어본 결과, 아이디는 같지만 repository명이 서로 다른 이미지 2개를 확인할 수 있다.

즉, repository를 변경한다고 해서 기존 이미지를 삭제하지는 않는다!

그러면 다시 푸쉬를 진행해보자

Result View


보다시피 나의 허브에 제대로 이미지가 등록된 모습을 확인할 수 있다!


5. 도커 허브에 등록된 이미지를 가져와서 실행

우리는 다른 사람이 배포한 이미지를 가져와서 컨테이너로 실행하여 동작시킬 수 있다.

커맨드는 다음과 같다.

외부 이미지를 가져와 컨테이너를 실행하는 커맨드
docker container run -d -p 9999:8080 myanjini/echo:latest

myanjini/echo:latest 는 강사님께서 도커 허브에 등록한 컨테이너 이미지이다.

위 커맨드를 통해 가져와서 실행할 수 있다.

Result View


첫번째 사진을 살펴보면 Already exists라는 내용을 볼 수 있다.

즉, 레이어드 아키텍처를 사용하기 때문에 먼저 로컬에 이미지가 있는지 확인한다.

만약 존재한다면 해당 레이어를 가져와서 사용하고 만약 없다면 도커 허브에 있는 레포지토리에서 Pull로 땡겨온다.

이제 두번째 사진을 살펴보면 curl http://localhost:9999 커맨드로 요청을 보냈더니 강사님께서 작성한 내용이 출력되는 모습을 볼 수 있다.

우리는 이 부분을 통해 다른 프로그램을 설치하지 않고도 단순히 레포지토리에서 이미지를 가져와서 도커로 실행시키면, 이미지 배포자가 생성한 내용대로 코드가 실행된다는 것을 알 수 있다!


6. 컨테이너 삭제

컨테이너를 삭제하는 커맨드는 다음과 같다.

컨테이너 삭제 커맨드
docker container rm 컨테이너식별자

여기서 컨테이너 식별자는 컨테이너 생성 시, --name 옵션을 추가하여 이름을 지정하고 사용할 수 있다.

만약, 이름을 지정하지 않았다면 컨테이너 ID를 사용하면 된다.

기본적으로 컨테이너를 삭제하기 위해서는 컨테이너가 다운된 상태여야 한다.

만약, 컨테이너가 실행중인 상태이더라도 삭제하고 싶다면 -f 옵션을 사용하도록 하자

💡 모든 컨테이너 삭제

모든 컨테이너를 간단하게 삭제하기 위해서는 2가지 옵션에 대해서 알아둘 필요가 있다.

-a : 모든 상태의 컨테이너를 조회
-q : 컨테이너 아이디만 조회

따라서, 모든 컨테이너의 아이디를 조회하는 커맨드는 다음과 같다.

모든 컨테이너의 아이디 조회 커맨드
docker container ls -aq

이를 이용해서 모든 상태의 컨테이너를 삭제할 수 있다.

모든 컨테이너 삭제
docker container rm -f $(docker container ls -aq)

$ 를 사용하면 커맨드의 결과를 매개변수로 사용할 수 있다.


7. 그 외의 기본 커맨드

기본적으로 사용되는 커맨드들에 대해서 간단하게 정리해보자

💡 특정 컨테이너의 로그를 확인하는 커맨드

docker container logs 컨테이너식별자

만약, 특정 컨테이너의 로그를 확인하고 싶다면 위의 커맨드를 입력하자

💡 다운되어있는 특정 컨테이너를 실행시키는 커맨드

docker container start 컨테이너식별자

start를 사용하면 종료된 컨테이너를 다시 실행시킬 수 있다.

💡 특정 컨테이너의 내부 쉘을 실행시키는 커맨드

docker container exec -it 컨테이너식별자 /bin/bash

여기서 exec는 뒤에 있는 명령어를 컨테이너에 전달해서 실행시키도록 도와주는 명령어이다.

중간에 -it 라는 옵션을 사용했는데, 이는 가상 터미널을 실행시켜서 컨테이너 내부에서 직접 명령을 실행할 수 있도록 설정하는 옵션이다.

/bin/bash 가 exec 명령어로 실행될 커맨드를 의미하는데, 이건 내부 쉘을 실행시키는 명령어이다.

보는바와 같이 가상터미널이 실행되어 있는 모습을 볼 수 있다.

이는 앞으로도 자주 사용될 내용이기 때문에 숙지하면 좋을 것 같다!


OUTRO

도커를 처음 사용해보면서 어색했던 도커 관련 커맨드들을 조금 정리해봤다.

처음에는 생소했던 Dockerfile 문법이나 각종 커맨드들도 하나씩 실습
해보니 이제는 점점 익숙해지는 것 같다.

이번 포스팅을 통해서 레이어드 아키텍처를 사용하여 이미지를 효율적으로 관리하고, Docker Hub를 통해 이미지를 공유하는 방식을 알아두면 좋겠다.

또한, 기본적인 커맨드들에 대해서 정리해봤는데 이는 기본적인 커맨드니까 알아두자 👊

profile
코린이

0개의 댓글

관련 채용 정보