개발

Docker(1) - image 와 container 사용법

autocat 2021. 8. 4. 16:23

Dokcer Image & Container

Docker Image

  • 도커 컨테이너를 구성하는 파일 시스템과 실행할 애플리케이션 설정을 하나로 합친것으로 컨테이너를 생성하는 템플릿 역할

Docker Container

  • 도커 이미지를 기반으로 생성되며, 파일 시스템과 애플리케이션이 구체화돼 실행되는 상태

Docker Image 만들어보기

목표 : Go언어로 만든 간단한 웹서버를 도커 컨테이너에서 실행

폴더를 하나 생성해서 main.go 파일과 Dockerfile 파일을 아래와 같이 생성해준다.

dev/Personal/docker/main.go

/**
    * 모든 HTTP 요청에 대해 Hello Docker!! 라는 응답
    * 모든 8080로 요청을 받는 서버 애플리케이션으로 동작
    * 요청을 받으면 received request 라는 메세지를 출력한다.
**/
package main

  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: ":8000"}

  if err:= server.ListenAndServe(); err != nil {
      log.Println(err)
    }
  }

dev/Personal/docker/Dockerfile

FROM golang:1.9

RUN mkdir /echo
COPY main.go /echo

CMD ["go", "run", "/echo/main.go"]

FROM instruction

FROM golang:1.9

FROM은 도커 이미지의 바탕이 될 베이스 이미지를 저장한다.

FROM에서 받아오는 도커 이미지는 도커허브라는 레지스트리에 공개된것이고 이를 참조한다.

main.go 를 실행하기 위해선 Go 언어의 런타임이 설치된 이미지가 있어야한다. 같은 이미지 에서도 버전을 나타내는 :1.9 를 토대로 버전을 가져오게된다. 대부분의 언어에는 위와 같은 태그가 붙어있고 그 태그는 대부분 버전등을딴다.

RUN instruction

RUN mkdir /echo

RUN은 도커 이미지를 실행할 때 컨테이너 안에서 실행할 명령을 정의하는 인스트럭션이다.

여기서는 main.go 애플리케이션을 배치하기위한 /echo 디렉터리를 mkdir 명령으로 만든것이다.

COPY instruction

COPY main.go

COPY는 도커가 동작중인 호스트 머신의 파일이나 디렉토리를 도커 컨테이너 안으로 복사하는 인스트럭션이다.

예제에서는 go파일을 컨테이너 네에서 실행할수 있도록 복사하는데 사용했다.

CMD instruction

CMD ["go", "run", "/echo/main.go"]

CMD는 도커컨테이너를 실행할 때 컨테이너 안에서 실행할 프로세스를 지정한다.

RUN 은 이미지를 빌드할 때 실행되고, 애플리케이션 업데이트 및 배치에 사용된다.

CMD는 컨테이너를 시작할때 한번 실행되고 애플리케이션 자체를 실행하는 명령이다.

ENV instruction

도커 컨테이너 안에서 사용할 수 있는 환경변수를 지정한다.

ARG instruction

이미지를 빌드할 때 정보를 함께 넣기 위해 사용한다. 이미지를 빌드할 때만 사용할 수 있는 일시적인 환경변수다.


도커 이미지 빌드하기

 docker image build

이 명령은 도커 이미지를 빌드하기위한 명령어이다.

자주 사용하는 Options

  • -t : 이미지명을 지정한다 태그명도 지정할 수 있으며 생략시에는 latest 태그가 붙는다.
    이미지명을 별도로 지정하지 않는다면 해시값만으로 이미지를 구별해야하므로 항상 지정해주는것이 편리
    docker image build -t 이미지명[:태그명] DockerFile의_경로
  • -f : 빌드 명령은 기본으로 Dockerfile을 찾는데, 그 외 파일명으로 된 dockerfile을 사용할때 이 옵션을 사용한다. 사용될 파일명을 지정해주면 된다.
    docker image build -f Dockerfile-alter

이제 명령어로 이미지를 빌드를 해본다.

$ docker image build -t example/echo:latest .
[+] Building 22.0s (9/9) FINISHED
...
=> [1/3] FROM docker.io/library/golang:1.9@sha256:8b5968585131604a92af02f5690713efadf029cc8dad53f79280b87a80eb1  18.2s
...                                                                                 0.0s
=> [2/3] RUN mkdir /echo                                                                                          0.5s
=> [3/3] COPY main.go /echo                                                                                       0.0s
...
=> naming to docker.io/example/echo:latest

Dockerfile에서 지정한 명령들이 단계적으로 실행되고 이미지가 빌드가 된것을 아래의 명령어로 확인할 수 있다.

$ docker image ls
REPOSITORY              TAG       IMAGE ID       CREATED         SIZE
example/echo            latest    6e175404b4ee   2 minutes ago   750MB

도커 컨테이너 실행

이미지를 컨테이너로 실행하는 명령어는 아래와 같다.

# docker container run {image REPOSITORY:TAG}
$ docker container run example/echo:latest
2021/08/04 04:41:00 Start server

이렇게 실행하게되면 포어그라운드에서 동작한다.

-d 옵션을 통해 컨테이너를 백그라운드로 실행할 수 있다.

$ docker container run -d example/echo:latest
ffe3626a5cdda9a7bfed09a0f7f37022700c078a975d029f437c8349ec194bbb
$ docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED         STATUS         PORTS                                                  NAMES
ffe3626a5cdd   example/echo:latest   "go run /echo/main.go"   2 minutes ago   Up 2 minutes                                                          objective_thompson

명령어 이후로 나오는 해시값문자열은 도커컨테이너의 ID이다.

현재 실행중인 도커 컨테이너의 목록을 표시하는 명령어는 docker ps 이며, 모든 컨테이너 목록을 표기하고 싶을때는 -a 옵션을 추가하여 사용한다.


포트 포워딩

컨테이너 안의 애플리케이션이 잘 실행되었는지 확인하기 위해 8080포트로 GET 요청을 보내본다.

$ curl http://localhost:8080/
curl: (7) Failed to connect to localhost port 8080: Connection refused

Connection refused가 출력됬다. 이 지점에 도커만의 특징이 드러난다.

도커 컨테이너는 가상환경이지만, 외부에서 봤을때는 독립된 하나의 머신처럼 다룰 수 있다는 특징이다.
echo 애플리케이션은 8080포트를 리스닝하지만 이는 컨테이너 내에 한정된 포트이다.
curl을 컨테이너 안에서 실행하면 응답을 받을 수 있겠지만, 컨테이너 밖에서 한 요청이기 때문에 위와 같은 메세지가 출력된것이다.

이처럼 http요청을 받는 애플리케이션이라면 컨테이너 밖의 요청을 컨테이너 안으로 전달해줘야한다. 이 역할을 담당하는것이 도커의 포트 포워딩이다.
포트포워딩이란 호스트 머신의 포트를 컨테이너 포트와 연결해 컨테이너 밖에서 온 통신을 컨테이너 포트로 전달한다.

포트포워딩을 사용하기 전에 실행한 컨테이너를 다음과 같이 정지시킨다.

$ docker container stop ffe3626a5cdd
# docker container stop {CONTAINER ID}

docker container run 명령에서 -p 옵션을 붙이면 포트포워딩을 지정할 수 있다.
-p 호스트_포트:컨테이너_포트 형식으로 사용하면 된다.

호스트포트를 9000으로 컨테이너 포트 8080에 연결하도록 아래와 같이 포트포워딩을 적용한다.

$ docker container run -d -p 9000:8000 example/echo:latest
32a96a19a95b0f34c4f7244660e8f7b1a8b870bc9da7e10e605264509ac824b2
$ curl http://localhost:9000/                                                                                                                                  3 ✘  14:05:29
Hello Docker!!

호스트 포트는 생략이 가능한데(... p- 8080 ... ), 이런 경우에는 빈포트가 에페메랄포트로 자동 할당된다. 이는 docker ps 에서 PORTS 칼럼 내에서 확인할수있다.


참고도서 : 도커/쿠버네티스를 활용한 컨테이너 개발 실전 입문