
➡ 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)에 설치되어 있을 경우, 공개/비공개 레지스트리에서 이미지를 가져와서 이를 기반으로 컨테이너를 실행할 수 있다.