Contents

Chapter 01. Docker 소개

   Apr 26, 2023     11 min read

시작 전.

  • Google Cloud 기본 창

사진첨부

  • Cloud BashShell

사진첨부

사진첨부
잘 열린 것을 확인할 수 있다.

개요

  • Docker : 애플리케이션을 개발, 출시, 실행하는 데 사용하는 개방형 플랫폼.

  1. Docker 컨테이너 빌드, 실행, 디버그하기
  2. Docker Hub 및 Google Artifact Registry에서 Docker 이미지 가져오기
  3. Docker 이미지를 Google Artifact Registry로 푸시하기

시작

  • 사용 중인 계정 이름 목록 표시
gcloud auth list


사진첨부

  • 프로젝트 ID 목록 표시
gcloud config list project


사진첨부

작업 01. Hello World

  1. 먼저 Cloud Shell을 열고 hello world 컨테이너를 실행한다.
docker run hello-world


사진첨부

  • 결과는 Hello from Docker!를 반환한다.
  • Docker 데몬이 hello-world 이미지를 검색했으나 로컬에서 이미지를 찾지 못했고, Docker Hub라는 공개 레지스트리에서 이미지를 가져오고, 가져온 이미지에서 컨테이너를 생성하고, 컨테이너를 실행했다.

  1. Docker Hub에서 가져온 컨테이너 이미지를 확인하기.
docker images


사진첨부

  • 위와 같은 결과는 Docker Hub 공개 레지스트리에서 가져온 이미지이다.
  • 이미지 ID는 SHA256 해시 형식임.
  • 여기서는 프로비저닝된 Docker 이미지를 지정한다.
  • Docker 데몬이 로컬에서 이미지를 찾을 수 없으면 기본적으로 공개 레지스트리에서 이미지를 검색함.

  1. 컨테이너 다시 실행하기
docker run hello-world


사진첨부

  • 두 번째 실행했을 때, Docker 데몬이 로컬 레지스트리에서 이미지를 찾은 뒤 이 이미지에서 컨테이너를 실행하였음을 확인할 수 있음.
  • Docker 데몬이 반드시 Docker Hub에서 이미지를 가져올 필요는 없음.

  1. 실행 중인 컨테이너 확인
docker ps


사진첨부

  • 실행중인 컨테이너가 없다. 전에 실행한 hello-world 컨테이너를 이미 종료했다.


  1. 실행이 완료된 컨테이너를 포함하여 모든 컨테이너 보기.
docker ps -a


사진첨부

  • Docker가 컨테이너를 식별하기 위해 생성한 UUID인 CONTAINER ID와 실행에 관한 추가 메타데이터가 표시된다.
  • 컨테이너 Names도 무작위로 생성되지만 docker run --name [container-name] hello-world를 사용하여 지정할 수 있음.

작업 02. 빌드

  • 간단한 노드 애플리케이션을 기반으로 한 Docker 이미지를 빌드하기.

  1. test라는 이름의 폴더를 만들고 이 폴더로 이동하기.
mkdir test && cd test


사진첨부

  1. Dockerfile 만들기
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


  • 첫 번째 줄 : 기본 상위 이미지를 지정함. 이 경우 기본 상위 이미지는 노드 버전 장기적 지원(LTS)의 공식 Docker 이미지임.
  • 두 번째 줄 : 컨테이너의 (현재) 작업 디렉터리를 설정.
  • 세 번째 줄 : 현재 디렉터리의 콘텐츠("."로 표시)를 컨테이너에 추가함.
  • 컨테이너의 포트를 공개하여 해당 포트에서의 연결을 허용하고 마지막으로 노드 명령어를 실행하여 애플리케이션을 시작함.

  1. 노드 애플리케이션 생성
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


  • 포트 80에서 수신대기하고 ‘Hello World’를 반환하는 간단한 HTTP 서버임.

사진첨부

  1. Dockerfile이 있는 디렉터리에서 다음 명령어를 실행한다. 현재 디렉터리를 의미하는 "."를 주의하자.
docker build -t node-app:0.1 .


사진첨부

  • 이 명령어는 시간이 조금 걸린다.

  • 결과는 다음과 비슷하게 출력됨.

Sending build context to Docker daemon 3.072 kB
Step 1 : FROM node:lts
6: Pulling from library/node
...
...
...
Step 5 : CMD node app.js
 ---> Running in b677acd1edd9
 ---> f166cd2a9f10
Removing intermediate container b677acd1edd9
Successfully built f166cd2a9f10


  • -t : name:tag 문법을 사용하여 이미지의 이름과 태그를 지정하는 역할을 함. 이미지 이름은 node-app이고 태그0.1임. Docker 이미지를 빌드할 때는 태그를 사용하는 것이 좋음. 태그를 지정하지 않으면 태그가 기본값인 latest로 지정되어 최신 이미지와 기존 이미지를 구분하기 어려워짐.

  1. 빌드한 이미지 확인하기.
docker images


사진첨부

  • node는 기본 이미지, node-app은 빌드한 이미지.
  • node를 삭제하려면 우선 node-app을 삭제해야 함. 이미지의 크기는 VM에 비해 상대적으로 작음. node:slimnode:alpine과 같은 노드 이미지의 다른 버전을 사용하면 더 작은 이미지를 제공하여 이식성을 높일 수 있음.
  • 노드의 공식 저장소에서 모든 버전을 확인할 수 있음.

작업 03. 실행

  1. 빌드한 이미지를 기반으로 하는 컨테이너를 실행함.
docker run -p 4000:80 --name my-app node-app:0.1


사진첨부

  • --name 플래그를 사용하면 원하는 경우 컨테이너 이름을 지정할 수 있음.
  • -p는 Docker가 컨테이너의 포트 80에 호스트의 포트 4000을 매핑하도록 지시하는 플래그임.
  • http://localhost:4000에서 서버에 접속할 수 있다. 포트 매핑이 없으면 localhost에서 컨테이너에 접속할 수 없다.

  1. 다른 터미널을 열고(Cloud Shell에서 +아이콘을 클릭) 서버를 테스트하기.
curl http://localhost:40


사진첨부

  • 초기 터미널이 실행되는 동안 컨테이너가 실행됨. 컨테이너를 터미널 세션에 종속시키지 않고 백그라운드에서 실행하려면 -d 플래그를 지정해야 함.

  1. 초기 터미널을 닫은 후 컨테이너를 중지하고 삭제하기.
docker stop my-app && docker rm my-app


사진첨부

  1. 백그라운드에서 컨테이너를 시작하기.
docker run -p 4000:80 --name my-app -d node-app:0.1 docker ps


사진첨부

  1. docker ps의 출력된 결과에서 컨테이너가 실행중임을 확인할 수 있음. docker logs [container_id]를 실행하면 로그를 볼 수 있음.

  • 참고) 앞부분에 입력한 문자들로 컨테이너를 고유하게 식별할 수 있다면 전체 컨테이너 ID를 입력할 필요는 없다. 예를 들어 컨테이너 ID가 17bcaca6f.... 인 경우 docker logs 17b를 실행할 수 있다.

docker logs d534dc739701


사진첨부

애플리케이션 수정하기.

  1. Cloud Shell에 앞서 실습에서 만든 테스트 디렉터리를 열기.
cd test



  1. 원하는 텍스트 편집기(예: nano 또는 vim)fh app.js를 편집하고 ‘Hello World`를 다른 문자열로 바꾸기.
vi app.js



사진첨부

사진첨부

  1. 새 이미지를 빌드하고 0.2로 태그를 지정하기.
docker build -t node-app:0.2 .


사진첨부

  • 2단계에서 기존 캐시 레이어를 사용하고 있음을 확인할 수 있다. 3단계 이해부터는 app.js를 변경했기 때문에 레이어가 수정 되었다.

  1. 새 이미지 버전으로 다른 컨테이너를 실행하기. 이때 호스트 포트를 80 대신 8080으로 매핑하는 방법을 확인하기. 호스트 포트 4000은 이미 사용 중이므로 사용할 수 없음.
docker run -p 8080:80 --name my-app-2 -d node-app:0.2
docker ps



사진첨부

  1. 컨테이너 테스트하기.
curl http://localhost:80


사진첨부

  1. 처음 작성한 컨테이너를 테스트하기.
curl http://localhost:4000


사진첨부

작업 04. 디버그

  • 디버깅 사례 살펴보기

  1. docker logs [container_id]를 사용해 컨테이너의 로그를 볼 수 있음. 컨테이너가 실행 중일 때 로그 출력을 확인하려면 -f 옵션을 사용함.

docker logs -f [container_id]



사진첨부

  • 실행 중인 컨테이너에서 대화형 Bash 세션을 시작해야 할 수 있다.

  1. 이 경우 docker exec를 사용함. 다른 터미널을 열고(Cloud Shell에서 + 아이콘을 클릭) 다음 명령어를 작성하기.

docker exec -it [container_id] bash


  • -it 플래그는 pseudo-tty를 할당하고 stdin을 열린 상태로 유지하여 컨테이너와 상호작용할 수 있도록 함. Dockerfile에 지정된 WORKDIR 디렉터리(/app)에서 bash가 실행된 것을 확인할 수 있음. 그럼 이제 디버깅할 컨테이너 내에서 대화형 셸세션을 사용할 수 있음.

사진첨부

  1. 디렉터리 확인
ls


  1. Bash 세션 종료
exit


사진첨부

  1. Docker inspect를 통해 Docker에서 컨테이너의 메타데이터를 검토할 수 있음.
docker inspect [container_id]


사진첨부

  1. --format을 사용하여 반환된 JSON의 특정 필드를 검사함.
docker inspect --format='' [container_id]



사진첨부

작업 05. 게시

  • 이미지를 Google Artifact Registry로 푸시함.
  • 모든 컨테이너와 이미지를 삭제하여 새로운 환경을 시뮬레이션하고 컨테이너를 가져와서 실행함.
  • 이를 통해 Docker 컨테이너의 이식성을 확인할 수 있음.
  • Artifact Registry에서 호스팅하는 비공개 레지스트리에 이미지를 푸기하려면 이미지에 레지스트리 이름으로 태그를 지정해야 함. 형식은 <regional-repository>-docker.pkg.dev/my-project/my-repo/my-image 임.

대상 Docker 저장소 만들기

  • 이미지를 푸시하려면 먼저 저장소를 만들어야 함.
  • 이미지를 푸시해도 저장소 만들기가 트리거되지 않으며 Cloud Build 서비스 계정에는 저장소를 만들 권히 없다.

  1. 타색 메뉴의 CI/CD에서 Artifact Registry > 저장소로 이동하기

사진첨부

  1. 저장소 만들기 클릭.

사진첨부

  1. 저장소 이름 : my-respository
  2. 형식 : Docker
  3. 위치 유형 - 리전 : us-central1 (Iowa)
  4. 만들기 클릭.

사진첨부

사진첨부
잘 만들어진 것을 확인할 수 있다.

인증 구성하기

  • 이미지를 푸시하거자 가져오려면 먼저 Docker가 Artifact Registry에 대한 요청을 인증하는데 Google Cloud CLI를 사용하도록 구성해야 함.

  1. us-central1 리전의 Docker 저장소에 인증을 설정하려면 Cloud Shell에서 다음 명령어를 실행함.
gcloud auth configure-docker us-central1-docker.pkg.dev


  1. 메시지가 표시되면 Y를 입력.

  • 이 명령어는 Docker 구성을 업데이트함.
  • 이제 Google Cloud 프로젝트의 Artifact Registry와 연결하여 이미지를 푸시하고 가져올 수 있음.

사진첨부

컨테이너를 Artifact Registry로 푸시하기

  1. 프로젝트 ID를 설정하고 Dockerfile이 포함된 디렉터리로 변경한다.
export PROJECT_ID=$(gcloud config get-value project)
cd ~/test


사진첨부

  1. node-app:0.2에 태그를 지정하기
docker build -t us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/node-app:0.2 .



  1. 빌드된 Docker 이미지를 확인하기
docker images


사진첨부

  1. 이 이미지를 Artifact Registry로 푸시하기
docker push us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/node-app:0.2


사진첨부
시간이 조금 걸린다.

사진첨부
빌드가 완료가 된 것을 확인할 수 있다.

  1. 탐색 메뉴의 CI/CD에서 Artifact Registry > 저장소로 이동한다.
  2. my-repository를 클릭하면 node-app Docker 컨테이너가 생성된 것을 볼 수 있다.

사진첨부

이미지 테스트하기

  • 새로운 VM을 시작하고 SSH로 새 VM에 접속한 다음 gcloud를 설치할 수도 있지만 여기서는 간단하게 모든 컨테이너와 이미지를 삭제하여 새로운 환경을 시뮬레이션할 것이다.

  1. 모든 컨테이너를 중지하고 삭제한다.
docker stop $(docker ps -q)
docker rm $(docker ps -aq)


사진첨부

  • 노드 이미지를 삭제하기 전에 (node:lts의) 하위 이미지를 삭제헤야 한다.

  1. 모든 Docker 이미지를 삭제한다.
docker rmi us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/node-app:0.2
docker rmi node:lts
docker rmi -f $(docker images -aq) # remove remaining images
docker images


  • 이제 새로운 환경이나 다름 없다.

  1. 이미지를 가져와서 실행한다.
docker pull us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/node-app:0.2
docker run -p 4000:80 -d us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/node-app:0.2
curl http://localhost:4000



  • 결과)
Welcome to Cloud