컨테이너로 애플리케이션 실행하기
학습 목표
- 도커의 이미지, 컨테이너, 포트포워딩을 이해하고 HTTP 응답을 반환하는 간단한 애플리케이션을 만든다.
- 도커 이미지를 만들기 위한 Dockerfile을 작성한다.
- reference 도커/쿠버네티스를 활용한 컨테이너 개발 실전 입문
목차
도커이미지와 도커 컨테이너는 무엇인가?
개념 | 역할 |
---|---|
이미지 | 도커 컨테이너를 구성하는 파일 시스템과 실행할 애플리케이션 설정을 하나로 합친것으로, 컨테이너를 생성하는 템플릿 역할을 한다. |
컨테이너 | 도커 이미지를 기반으로 생성되며, 파일 시스템과 애플리케이션이 구체화 돼 실행되는 상태 |
도커 이미지 받아오기
docker image pull [받아올 이미지]
ex 1)
docker image pull debian:buster
ex2)
docker image gihyodocker/echo:latest
내려받은 이미지를
docker container run
명령으로 실행할 수 있다.
ex)
테스트 시
docker container run -t -p 9000:8080 gihyodocker/echo:latest
이 컨테이너는 옵션을 통해 포트 포워딩이 적용되어 있다. (-p 9000:8080부분, 나중에 다시 나올것이니 일단 넘어가자) 도커 실행환경의 포트 9000을 거쳐 HTTP 요청을 전달받는다.
docker 터미널을 하나 더 띄워 curl명령으로 접근해본다.
curl http://localhost:9000/
책을 그대로 따라했지만 연결이 안된다.
netstat -a
명령어로 포트가 연결 되었는지 확인 해봤으나 연결이 되어있지 않다.
이유는 역시 윈도우에서 가상머신을 띄워서 진행했기 때문이다.
Toolbox에선 default 주소가 localhost가아닌, 192.168.99.100이다.
curl http://192.168.99.100:9000/
로 명령어를 바꾸니 정상 작동 한다.
컨테이너를 만들고 실행하는법에 대한 감을 잡기 위해 Go언어로 간단한 웹 서버를 만들어 보았다.
(책에 있는거 그대로 따라썼다.. Go언어로 프로젝트를 하나 진행해 볼 생각이다.)
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: ":8080"}
if err := server.ListenAndServe(); err != nil {
log.Println(err)
}
}
간단한 함수설명
- 모든 HTTP 요청에 대해 Hello Docker !! 라는 응답을 보낸다.
- 포트 8080로 요청을 받는 서버 애플리케이션으로 동작한다.
- 클라이언트로부터 요청을 받으면 received request라는 메시지를 표준으로 출력한다.
그리고 DockerFile을 만든다.
FROM golang:1.9
RUN mkdir /echo
COPY main.go /echo
CMD ["go", "run", "/echo/main.go"]
FROM
From 인스트럭션은 도커 이미지의 바탕이 될 베이스 이미지를 지정한다. Dockerfile로 이미지를 빌드할 때 가장 먼저 From 인스트럭션에 지정된 이미지를 내려받는다.
기본적으로 이 이미지는 “DockerHub”라는 레지스트리에서 참조한다.
main.go를 실행하려면 Go 언어의 런타임이 설치된 이미지가 있어야 한다.
이 런타임이 설치된 golang:1.9 이미지를 받아온다.
- dockerfile에서 image를 pull하는것으로 이해하였다.
RUN
RUN 인스트럭션은 도커 이미지를 실행 할때 컨테이너 안에서 실행할 명령을 정의하는 인스트럭션이다. 인자로 도커 컨테이너 안에서 실행할 명령을 그대로 기술한다. 여기서는 main.go 애플리케이션을 배치하기 위한 /echo 디렉터리를 mkdir 명령으로 만들었다.
COPY
COPY 인스트럭션은 도커가 동작 중인 호스트 머신의 파일이나 디렉터리를 도커 컨테이너 안으로 복사하는 인스트럭션이다.
예제에서는 main.go파일을 컨테이너 안으로 복사하였다. 복사 위치는 RUN으로 만든 /echo 디렉터리이다.
ADD
COPY 인스트럭션과 유사하게 ADD 인스트럭션이 있다. 보통 COPY인스트럭션을 주로 쓴다. 그래도 알아놓자
아래는 COPY 인스트럭션과의 차이점이다.
- URL을 복사할 source로 사용할 수 있다. remote에 있는 파일을 받아서 복사하게 된다.
-
source 파일이 gzip과 같이 일반적으로 잘 알려진 압축형태인 경우, 압축을 풀어준다.
- 압축된 remote 파일인 경우, 압축을 풀어주지는 않는다.
CMD
CMD 인스트럭션은 도커 컨테이너를 실행할때 컨테이너 안에서 실행할 프로세스를 지정한다.
즉, RUN 인스트럭션은 이미지를 빌드할때 실행되고, CMD 인스트럭션은 컨테이너를 시작할 때 한번 실행된다. RUN은 애플리케이션 업데이트 및 배치에, CMD는 애플리케이션 자체를 실행하는 명령어라고 생각하면 된다.
go run /echo/main.go
은 CMD 인스트럭션으로 기술하면
CMD ["go", "run", "/echo/main.go"]
이렇게 된다. 명령을 공백으로 나눈 배열로 나타낸다.
CMD go run/echo/main.go
처럼 그대로 작성할 수도 있지만 되도록이면 인자 배열방식을 사용하라고 한다.
구글링을 해보니 인자 배열 방식이 조금 더 단순하게 빌드된다.
CMD에 지정한 명령을 docker container run에서 지정한 인자로 오버라이드 할 수 있다.
즉
CMD["uname"]
$ docker container run $(docker image build -q . ) echo yay
을 하면 uname이아닌 echo yay명령이 실행된다.
인자 표기 방식 | 동작 |
---|---|
CMD [“실행파일,” 인자1”, 인자2] | 실행파일에 인자를 전달한다. 사용을 권장하는 방식 |
CMD 명령 인자1 인자2 | 명령과 인자를 지정한다. 셸에서 실행되므로 셸에 정의된 변수를 참조할 수 있다. |
CMD [“인자1”, “인자2”] | ENTRYPOINT에 지정된 명령에 사용된 인자를 전달한다. |
ENTRYPONT
ENTRYPOINT 인스트럭션을 사용하면 컨테이너의 명령 실행 방식을 조정할 수 있다.
ENTRYPOINT는 CMD와 마찬가지로 컨테이너 안에서 실행할 프로세스를 지정하는 인스트럭션이다.
ENTRYPOINT를 지정하면 CMD의 인자가 ENTRYPOINT에서 실행하는 파일에 주어진다.
ex)
$ docker container run -it golang:1.10 go version
으로 go version을 실행할 수 있다 하지만
docker file에
FROM golang:1.10
ENTRYPOINT ["go"]
CMD [""]
라 쓰고
docker image build -t ch02/golang:latest .
빌드 하면
docker container run ch02/golang:latest version
으로 go를 쓰지 않아도 된다 이로써 컨테이너를 어느정도 제한 할 수 있다.
하지만 docker container run –entrypoint 옵션으로 오버라이드가 가능하다.
그 밖의 DockerFile 인스트럭션
LABEL - 제작자 이름을 적을 수 있다.
ENV - 도커 컨테이너 내부의 환경변수를 지정한다.
ARG - 이미지를 빌드할때 일시적으로 환경변수를 만들 수 있다.
상세설명 및 모든 명령 - https://docs.docker.com/engine/reference/builder/
도커 이미지 빌드하기
이미지 빌드는
docker image build -t 이미지명[:태그명] Dockerfile의 경로
로 이루어진다.
docker image build 명령은 도커 이미지를 빌드하기 위한 명령이다.
-t 옵션
-
이미지명을 지정한다. 태그명도 지정할 수 있으며, 생략 시에는 latest 태그가 붙는다.
-
-t옵션과 이미지명은 반드시 지정해야 한다고 생각하는 편이 좋다. -t 옵션 없이도 빌드 자체는 가능하지만, 이미지명 없이는 해시값만으로 이미지를 구별해야하므로 사용하기가 상당히 번거롭다.
docker image build -t dochoi/echo:latest .
이런식으로 이미지명의 충돌을 방지하기 위해 네임스페이스를 추가할수 있다. (dochoi)
SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.
윈도우에서는 위와 같은 워닝이 발생한다. 무시하자.
docker image ls
로 생성된 이미지의 정보를 알 수있다.
-
REPOSITORY : 같은 이름을 가졌지만 서로 다른 태그가 달린 이미지의 집합으로, 일반적으로 깃허브 저장소처럼 [소유자명/애플리케이션명]과 같이 이름이 붙는다.
docker container run
명령으로 컨테이너를 실행할 때 이 Repository를 지정한다.
-
TAG : 특정이미지를 식별하기 위해 사용하는 값이다.
-
IMAGE ID : 이미지를 유일하게 식별하기 위해 사용하는 식별자
이미지를 생성했으니
docker container run example/echo:latest
으로 컨테이너를 실행한다. 이 컨테니너는 계속 포어그라운드에서 동작한다 컨테이너를 종료하려면 Ctrl+C 로 나온다. 백그라운드로 컨테이너를 실행하기위해
-d 옵션을 준다
docker container run -d example/echo:latest
포어그라운드 프로세스
사용자가 입력한 명령이 실행되어 결과가 출력될 때 까지 기다려야 하는 포어그라운드 방식으로 처리되는 프로세스
백그라운드 프로세스
명령을 실행하면 명령의 처리가 끝나는 것과 관계없이 곧바로 프롬프트가 출력되어 사용자가 다른 작업을 할 수 있음
-d 옵션을 붙여 컨테이너를 실행하면 컨테이너의 ID가 출력된다. 이는 유일 식별자이다.
docker container ls
를 실행하면 나오는 CONTAINER ID 칼럼에 나온다 (앞 12자리)
docker container, docker run 과 docker run, docker image pull 은 같다.
하지만 명령의 의도가 드러나는것을 생각해 축약되지 않은 명령을 사용할 것이다.
docker container run -it -p 80:80 -p 443:443 dochoi:fin
채점시
포트 포워딩
Go 언어로 이전의 포트 8080을 리스닝 하는 코드를 작성하였다.
curl http://192.168.99.100:8080/
로 GET 요청을 보내보았다. 연결이 되지 않는다. 왜 그럴까? 도커 컨테이너는 가상환경이지만 외부에서 봤을땐 독립된 하나의 머신이다. 분명 echo 애플리케이션은 8080포트를 리스닝하고 있지만, 이 포트는 컨테이너 안에 한정된 포트이다. curl을 컨테이너 안에서 실행하면 올바른 응답을 받을 수 있다. 하지만 외부에서는 컨테이너 포트를 바로 사용할 수 없다. 이처럼 HTTP 요청을 받는 애플리케이션을 이용하려면 컨테이너 밖에서 온 요청을 컨테이너 안에 있는 애플리케이션에 전달해줘야 한다.
이 역할을 하는것이 포트 포워딩이다.
포트포워딩을 사용하기 전에 앞서 실행한 컨테이너를 정지시킨다.
docker container stop $(docker container ls --filter "ancestor=example/echo" -q)
docker container run -d -p 9000:8080 example/echo:latest
그다음 -p 옵션으로 호스트 포트 9000을 컨테이너포트 8080으로 연결한다.
curl http://192.168.99.100:9000/
그리고 호스트포트 9000에 GET 요청을 보내면 정상적으로 응답이 받아진다.
이로써 도커 이미지 생성, 컨테이너 실행, 포트 포워딩에 대해 이해하였다.
reference
- 도커/쿠버네티스를 활용한 컨테이너 개발 실전 입문