➡ basic Docker container environment commands에 익숙해지기!
open platform for developing, shipping(출시), and running applications
Docker를 사용했을 때의 장점
도커 컨테이너는 Kubernetes에서 사용할 수 있으므로 Kubernetes Engine에서 쉽게 실행될 수 있다.
도커의 essentails을 배우면 Kubernetes 및 컨테이너 어플리케이션 개발을 시작하는 데 필요한 skillset을 갖출 수 있다.
Docekr 컨테이너 빌드, 실행, 디버그
Docker Hub 및 Google Container Registry에서 Docker 이미지 가져오기(pull)
Docker 이미지를 Google Container Registry로 push
실습 시작 전에 개요에서 설정 및 요구사항
까지 완료된 상태여야 한다!
Cloud Shell를 열고 아래의 명령어를 입력하여 hello world 컨테이너를 실행한다.
$ docker run hello-world
➡ 이 컨테이너는 "Hello from Docker!"를 리턴한다.
출력된 결과에서 실행한 단계 개수에 주목해보자!
- listens for Docker API requests
- manages Docker objects such as images, containers, networks, and volumes
- communicates with other daemons to manage Docker services
아래의 명령을 실행하여 Docker Hub에서 가져온 컨테이너 이미지를 확인해보자.
$ docker images
이미지를 Docker Hub 공개 registry에서 pull해왔다.
이미지 ID는 SHA256 hash format
프로비저닝(provisioning)
: 사용자의 요구에 맞게 시스템 자원을 할당, 배치, 배포해뒀다가 필요 시 시스템을 즉시 사용할 수 있는 상태로 미리 준비해 두는 것
docker daemon이 로컬에서 이미지를 찾을 수 없으면 default로 공개 registry에서 이미지를 검색한다.
$ docker run hello-world
$ docker ps
실행중인 컨테이너가 없다.
실행이 완료된 컨테이너를 포함하여 모든 컨테이너를 보려면 아래의 명령어!
$ docker ps -a
docker가 컨테이너를 식별하기 위해 생성한 UUID인 CONTAINER ID
와 실행에 관한 메타데이터가 표시된다.
범용고유식별자;UUID(Universally Unique IDentifier)
: 기본적으로 어떤 개체(데이터)를 고유하게 식별하는 데 사용되는 16바이트(128비트) 길이의 숫자. 32개의 16진수로 구성되며, 5개의 그룹으로 표시되고 각 그룹은 하이픈으로 구분. 참고
컨테이너 Names
도 무작위로 생성되지만 $ docker run --name [container-name] hello-world
를 사용하여 지정할 수 있다.
간단한 node application 기반 docker image를 빌드해보자!
test
폴더 생성 후 전환
$ mkdir test && cd test
Dockerfile
생성
➡ docker daemon에 이미지를 빌드하는 방법을 지시
cat > Dockerfile <<EOF
# Use an official Node runtime as the parent image
FROM node:lts
# Set the working directory in the container to /app
WORKDIR /app
# Copy the current directory contents into the container at /app
ADD . /app
# Make the container's port 80 available to the outside world
EXPOSE 80
# Run app.js using node when the container launches
CMD ["node", "app.js"]
EOF
❗ Dockerfile 명령어 참조를 자세히 읽어보고 Dockerfile의 각 행에 관해 숙지하기!
cat > app.js <<EOF
const http = require('http');
const hostname = '0.0.0.0';
const port = 80;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello World\n');
});
server.listen(port, hostname, () => {
console.log('Server running at http://%s:%s/', hostname, port);
});
process.on('SIGINT', function() {
console.log('Caught interrupt signal and will exit');
process.exit();
});
EOF
➡ 간단한 HTTP 서버로 port 80을 수신하고 "Hello World"를 반환이미지 빌드
아래의 명령어를 Dockerfile이 있는 디렉토리에서 실행
$ docker build -t node-app:0.1 .
➡ 이미지를 빌드할 때 위 Dockerfile
의 각 행을 통해 중간 컨테이너 레이어가 만들어지는 방식을 확인하자!
-t
는 name:tag
syntax를 사용하여 이미지의 이름과 태그를 지정
docker 이미지를 빌드할 때는 태그를 지정하는 것이 좋다.
태그를 지정하지 않으면 태그가 기본값인latest
로 지정되어 최신 이미지와 기본 이미지를 구분하기 어려워진다.
아래의 명령어를 통해 빌드한 이미지를 확인해보자!
$ docker images
node
: 기본 이미지node-app
: 사용자가 빌드한 이미지node
를 제거하려면 우선 node-app
을 제거해야 한다.node:slim
및 node:alpine
과 같은 노드 이미지의 다른 버전을 사용하면 더 작은 이미지를 제공하여 이식성을 높일 수 있다.컨테이너 크기를 줄이는 주제는 Advanced Topics에서 다룬다. official repository에서 모든 버전 확인 가능.
이 모듈에서는 위의 코드를 사용하여 빌드한 이미지를 기반으로 하는 컨테이너 실행
$ docker run -p 4000:80 --name my-app node-app:0.1
--name
flag를 사용하면 컨테이너 이름을 지정할 수 있다.-p
는 docker가 컨테이너의 포트 80에 호스트의 포트 4000을 매핑하도록 지시하는 flaghttp://localhost:4000
에서 서버에 접속할 수 있다.다른 터미널을 열고 서버 테스트
$ curl http://localhost:4000
-d
flag를 지정해야 한다.$ docker stop my-app && docker rm my-app
$ docker run -p 4000:80 --name my-app -d node-app:0.1
$ docker ps
docker ps
결과에서 실행 중$ docker logs [container_id]
를 실행하면 로그를 볼 수 있다.
Tips) 초기 문자가 컨테이너를 고유하게 식별하는 한, 전체 컨테이너 ID를 작성할 필요는 없다.
예를 들어 컨테이너 ID가 '17bcaca6f...'일 경우 'docker logs 17b'를 실행할 수 있다.
$ cd test
$ nano app.js
를 통해 app.js를 편집새로운 이미지를 빌드하고 0.2로 태그 지정
$ docker build -t node-app:0.2
app.js
를 변경했기 때문에 3단계 이후부터 레이어가 수정$ docker run -p 8080:80 --name my-app-2 -d node-app:0.2
이때 호스트 포트를 80대신 8080으로 매핑
- 호스트 포트 4000은 이미 사용 중이므로 사용 불가
$ docker ps
컨테이너 테스트
처음 작성한 컨테이너 테스트
$ curl http://localhost:4000
두 번째로 작성한 컨테이너 테스트
$ curl http://localhost:8080
컨테이너 로그 확인
$ docker logs [container_id]
-f
옵션 필요$ docker logs -f [container_id]
실행 중인 컨테이너에서 대화식 Bash 세션 시작 가능
다른 터미널을 열고 아래의 명령어 입력하기
$ docker exec -it [container_id] bash
-it
flag는 pseudo-tty를 할당하고 stdin을 열린 상태로 유지하여 컨테이너와 상호작용할 수 있게 함pseudo-tty(pseudoterminal or PTY): used by users and applications to gain access to the shell
stdin: 표준 입력(stdin)은 프로그램으로 들어가는 데이터(보통은 문자열) 스트림
Dockerfile
에 지정된 WORKDIR
디렉토리(/app)에서 bash가 실행된 것을 확인 가능$ ls
$ exit
명령어를 입력하여 Bash 세션 종료$ docker inspect [container_id]
--format
을 사용하여 반환된 JSON의 특정 필드를 검사디버깅에 관한 자세한 내용은 아래의 리소스 참조하기
이제 이미지를 Google Container Registry(GCR)로 push하자!
그 후, 모든 컨테이너와 이미지를 제거하여 새로운 환경을 시뮬레이션하고 컨테이너를 가져와서 실행해보자!
➡ 이를 통해 docker 컨테이너의 이식성(portability)을 시연(demonstrate)
GCR에서 호스팅하는 비공개 registry에 이미지를 push하려면 이미지에 registry 이름으로 태그를 지정해야 한다.
양식: [hostname]/[project-id]/[image]:[tag]
GCR의 경우
[hostname]
= gcr.io
$ gcloud config list project
를 실행하여 프로젝트 ID를 찾을 수 있다.
node-app:0.2
를 태그해보자.
$ docker tag node-app:0.2 gcr.io/[project-id]/node-app:0.2
$ docker images
이 이미지를 GCR로 push하자!
$ docker push gcr.io/[project-id]/node-app:0.2
웹 브라우저의 이미지 레지스트리로 이동하여 GCR에 이미지가 있는지 확인해보자!
방법1) 콘솔에서 도구 > Container Regsitry
방법2) http://gcr.io/[project-id]/node-app
이미지를 테스트해보자!
여기서는 간단하게 모든 컨테이너와 이미지를 제거하여 새로운 환경을 시뮬레이션해보자!
새로운 VM을 시작하고 SSH로 새 VM에 접속한 다음 gcloud를 설치해도 된다.
SSH(Secure SHell): 원격 호스트에 접속하기 위해 사용되는 보안 프로토콜
모든 컨테이너를 중지 & 제거
$ docker stop $(docker ps -q)
$ docker rm $(docker ps -aq)
노드 이미지 제거 전에 (node:lts
의) 하위 이미지 제거
$ docker rmi node-app:0.2 gcr.io/[project-id]/node-app node-app:0.1
$ docker rmi node:lts
$ docker rmi $(docker images -ap)
# remove remaining images
$ docker images
이제 새로운 환경과 같아졌다.
이미지를 푸시하여 실행
$ docker pull gcr.io/[project-id]/node-app:0.2
$ docker run -p 4000:80 -d gcr.io/[project-id]/node-app:0.2
$ curl http://localhost:4000
여기서는 컨테이너의 이식성을 확인해봤다.
Docker가 호스트(사내;on-premise or VM)에 설치되어 있을 경우, 공개/비공개 레지스트리에서 이미지를 가져와서 이를 기반으로 컨테이너를 실행할 수 있다.