- Docker는 애플리케이션을 개발, 출시, 실행하는 데 사용하는 개방형 플랫폼입니다. Docker를 사용하면 인프라에서 애플리케이션을 분리하고 인프라를 관리형 애플리케이션처럼 처리할 수 있습니다. Docker는 코드를 더욱 빠르게 출시, 테스트, 배포하고 코드 작성과 실행 주기를 단축하는 데 도움이 됩니다.
- Google Cloud Shell은 다양한 개발 도구가 탑재된 가상 머신으로, 5GB의 영구 홈 디렉토리를 제공하며 Google Cloud에서 실행됩니다. Google Cloud Shell을 사용하면 명령줄을 통해 GCP 리소스에 액세스할 수 있습니다.
- GCP Console의 오른쪽 상단 툴바에서 Cloud Shell 열기 버튼을 클릭합니다.
- 다음 명령어를 통해 사용중인 계정 이름 목록과 프로젝트 ID 목록을 표시할 수 있습니다.
(gcloud는 Google Cloud Platform의 명령줄 도구입니다. Cloud Shell에 사전 설치되어 있으며 탭 자동 완성을 지원합니다.)gcloud auth list gcloud config list project
✔ 명령어 출력 결과
- Cloud Shell을 열고 다음 명령어를 실행하여 hello world 컨테이너를 실행합니다.
docker run hello-world
- 이 간단한 컨테이너는 화면에 Hello from Docker! 를 반환합니다.
✔ 명령어 출력 결과
코드 실행 과정을 살펴보면 다음과 같습니다.
1. Docker 데몬이 hello-world 이미지를 검색했으나 로컬에서 이미지를 찾지 못했다.
2. Docker Hub라는 공개 레지스트리에서 이미지를 가져온다.
3. 가져온 이미지에서 컨테이너를 생성한다.
4. 컨테이너를 실행한다.
- 다음 명령어를 실행하여 Docker Hub에서 가져온 컨테이너 이미지를 확인합니다.
docker images
✔ 명령어 출력 결과
이미지를 Docker Hub 공개 레지스트리에서 가져왔습니다.
이미지 ID는 SHA256 해시 형식입니다.
이 필드에서는 프로비저닝된 Docker 이미지를 지정합니다.※ SHA-256 : 현재 블록체인에서 가장 많이 채택하여 사용되고 있는 암호 방식이다. 단방향성의 성질을 띄고 있는 암호화 방법으로 복호화가 불가능하며 출력 속도가 빠르다는 장점을 갖고 있다.
※ 프로비저닝(provisioning) : 사용자의 요구에 맞게 시스템 자원을 할당, 배치, 배포해 두었다가 필요 시 시스템을 즉시 사용할 수 있는 상태로 미리 준비해 두는 것을 말한다.
📌 핵심 : Docker 데몬이 로컬에서 이미지를 찾을 수 없으면 기본적으로 공개 레지스트리에서 이미지를 검색합니다.
- 다음 명령어를 실행하여 다시 한 번 컨테이너를 실행합니다.
docker run hello-world
✔ 명령어 출력 결과
두 번째 실행했을 때는 Docker 데몬이 로컬 레지스트리에서 이미지를 찾고 해당 이미지에서 컨테이너를 실행하였음을 확인할 수 있습니다.
※ 레지스트리 : 윈도우계열 시스템에서 사용하는 시스템 구성 정보를 저장한 데이터베이스를 말한다.
📌 핵심 : Docker 데몬이 반드시 Docker Hub에서 이미지를 가져올 필요는 없습니다.
- 다음 명령어를 통해 실행 중인 컨테이너를 확인합니다.
docker ps
✔ 명령어 출력 결과
실행 중인 컨테이너가 없습니다.
앞서 실행한 hello-world 컨테이너는 이미 종료되었습니다.
- 실행이 완료된 컨테이너를 포함하여 모든 컨테이너를 보기 위해 다음 명령어를 실행 합니다.
docker ps -a
✔ 명령어 출력 결과
실행이 완료된 컨테이너의 UUID와 실행에 관한 추가 메타데이터를 확인할 수 있습니다.
이때, 컨테이너 Names는 무작위로 생성되지만 docker run --name [container-name] hello-world를 사용하여 지정할 수도 있습니다.
- 다음 명령어를 통해 test라는 이름의 폴더를 만들고 해당 폴더로 전환합니다.
mkdir test && cd test
- 전환된 폴더에서 Dockerfile을 만들어 줍니다.
cat > Dockerfile <<EOF # 공식 노드 런타임을 상위 이미지로 사용합니다. FROM node:6 # 컨테이너의 작업 디렉토리를 /app으로 설정합니다. WORKDIR /app # 현재 디렉토리 내용을 /app에 있는 컨테이너에 복사합니다. ADD . /app # 컨테이너의 포트 80을 외부에 공개합니다. EXPOSE 80 # 컨테이너가 시작될 때 노드를 사용하여 app.js를 실행합니다. CMD ["node", "app.js"] EOF
✔ 명령어 입력
첫 번째 행은 기본 상위 이미지를 지정합니다. 이 경우에는 노드 버전 6의 공식 Docker 이미지입니다.
두 번째 행은 컨테이너의 (현재) 작업 디렉토리를 설정합니다.
세 번째 행은 현재 디렉토리의 내용("."으로 표시)을 컨테이너에 추가합니다.
그런 다음 컨테이너의 포트를 공개하여 공개된 컨테이너 포트에서의 연결을 허용하고 마지막으로 노드 명령어를 실행하여 애플리케이션을 시작합니다.
- 이미지 빌드를 위해 다음 명령을 실행시켜 노드 애플리케이션을 생성합니다.
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 서버로 포트 80을 수신하고 'Hello World'를 반환합니다.
- 다음 명령어를 통해 이미지를 빌드합니다.
(이때, Dockerfile이 있는 디렉토리에서 실행해야 하기 때문에 현재 디렉토리를 의미하는 ' . '을 사용합니다.)
docker build -t node-app:0.1 .
✔ 명령어 출력 결과
-t는 name:tag 구문을 사용하여 이미지의 이름과 태그를 지정하는 역할을 합니다.
(이미지 이름 : "node-app", 태그 : "0.1")
태그를 지정하지 않으면 태그가 기본값인 latest로 지정되어 최신 이미지와 기존 이미지를 구분하기 어려워집니다.
따라서, Docker 이미지를 빌드할 때는 태그를 사용하는 것이 좋습니다.
- 다음 명령어를 통해 빌드한 이미지를 확인합니다.
docker images
✔ 명령어 출력 결과
node는 기본 이미지이고 node-app은 사용자가 빌드한 이미지입니다.
정상적으로 이름과 태그가 지정된 것을 확인할 수 있습니다.
- 다음 명령어를 통해 빌드한 이미지를 기반으로 하는 컨테이너를 실행합니다.
docker run -p 4000:80 --name my-app node-app:0.1
✔ 명령어 출력 결과
-p는 Docker가 컨테이너의 포트 80에 호스트의 포트 4000을 매핑하도록 지시하는 플래그입니다.
--name 플래그를 사용하면 컨테이너 이름을 지정할 수 있습니다.
이제 http://localhost:4000에서 서버에 접속할 수 있습니다.
(만약 포트 매핑이 없으면 localhost에서 컨테이너에 접속할 수 없습니다.)
- 다른 터미널을 열고 서버를 테스트합니다.
curl http://localhost:4000
✔ 명령어 출력 결과
이 경우에는 초기 터미널이 실행되는 동안 컨테이너가 실행되는 모습을 볼 수 있습니다.
- 컨테이너를 터미널 세션에 종속시키지 않고 백그라운드에서 실행시키기위해 -d 플래그를 지정합니다.
docker run -p 4000:80 --name my-app -d node-app:0.1 docker ps
✔ 명령어 출력 결과
docker ps의 결과를 통해 컨테이너가 실행 중임을 확인할 수 있습니다.
- docker logs [container_id]를 실행하여 로그를 확인합니다.
(만약 컨테이너가 실행 중이라면 -f 옵션을 추가로 사용합니다.)docker logs [container_id]
✔ 명령어 출력 결과
초기 문자가 컨테이너를 고유하게 식별하는 경우 전체 컨테이너 ID를 작성할 필요는 없습니다.
예를 들어 컨테이너 ID가 '47bffefaf6ab'일 경우 'docker logs 47b'를 실행할 수 있습니다.
- 아래의 과정을 통해 app.js 애플리케이션을 수정하여 새 이미지를 빌드하고 0.2로 태그를 지정합니다.
1) 텍스트 편집기(nano 또는 vim)로 app.js의 'Hello World'를 'Welcome to Cloud'로 수정합니다.
.... const server = http.createServer((req, res) => { res.statusCode = 200; res.setHeader('Content-Type', 'text/plain'); res.end('Welcome to Cloud\n'); # 수정 부분 }); ....
2) 새 이미지를 빌드하고 0.2로 태그를 지정한 후 다른 컨테이너를 실행합니다.
이때, 호스트 포트를 80 대신 8080으로 매핑합니다.
(호스트 포트 4000은 이미 사용 중이므로 사용할 수 없음)docker build -t node-app:0.2 . docker run -p 8080:80 --name my-app-2 -d node-app:0.2 docker ps curl http://localhost:8080
✔ 명령어 출력 결과
실행 중인 컨테이너를 확인한 결과 app.js 애플리케이션이 tag 0.1에 port 번호 4000과 tag 0.2에 port 번호 8080으로 각각 실행 중인 것을 볼 수 있습니다.
📌 실습환경 변경으로 인하여 [container_id]가 기존 '47bffefaf6ab'에서 'd3a4ddbc3a67'로 변경됨을 유의하여 주세요.
- docker exec를 사용하여 실행 중인 컨테이너에서 대화식 Bash 세션을 시작합니다.
docker exec -it [container_id] bash
✔ 명령어 출력 결과
-it 플래그는 pseudo-tty를 할당하고 stdin을 열린 상태로 유지하여 컨테이너와 상호작용할 수 있도록 합니다.
Dockerfile에 지정된 WORKDIR 디렉토리(/app)에서 bash가 실행된 것을 확인할 수 있습니다.
이제 디버깅할 컨테이너 내에서 대화형 셸 세션을 사용할 수 있습니다.
- 대화식 Bash 세션에서 다음 명령어를 통해 디렉토리를 확인하고 종료합니다.
ls exit
✔ 명령어 출력 결과
- Docker inspect를 통해 Docker에서 컨테이너의 메타데이터를 전부 검토할 수 있습니다.
이때, --format을 사용하여 반환된 JSON의 특정 필드를 검사합니다.docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' [container_id]
✔ 명령어 출력 결과
- Docker 컨테이너의 이식성을 시연하기 위해 다음과 같은 과정을 거칩니다.
- 이미지를 Google Container Registry(GCR)로 푸시합니다.
- 모든 컨테이너와 이미지를 제거하여 새로운 환경을 시뮬레이션합니다.
- 컨테이너를 가져와서 실행합니다.
- GCR에서 호스팅하는 비공개 레지스트리에 이미지를 푸시하려면 이미지에 레지스트리 이름으로 태그를 지정해야 합니다.
📌 이때, 양식은 [hostname]/[project-id]/[image]:[tag]입니다.
- GCR의 경우
[hostname]= gcr.io
[project-id]= 프로젝트의 ID
[image]= 이미지 이름
[tag]= 원하는 임의의 문자열 태그입니다. (지정하지 않으면 기본값인 'latest'로 설정)
1) 이미지를 Google Container Registry(GCR)로 푸시합니다.
- 다음 명령어를 통해 프로젝트 ID를 찾습니다.
gcloud config list project
✔ 명령어 출력 결과
- 다음 명령어를 통해 node-app:0.2를 태그하고 [project-id]를 내 구성으로 바꾼 후 컨테이너 이미지를 확인합니다.
docker tag node-app:0.2 gcr.io/[project-id]/node-app:0.2 docker images
✔ 명령어 출력 결과
- 해당 이미지를 GCR로 푸시합니다.
docker push gcr.io/[project-id]/node-app:0.2
✔ 명령어 출력 결과
- 웹 브라우저의 이미지 레지스트리로 이동하여 GCR에 이미지가 있는지 확인합니다.
- 콘솔에서 도구 > Container Registry로 이동하거나 http://gcr.io/qwiklabs-gcp-01-eeab60ed66f4/node-app을 방문하여 확인할 수 있습니다.
✔ 확인 결과
2) 모든 컨테이너와 이미지를 제거하여 새로운 환경을 시뮬레이션합니다.
- 다음 명령어를 통해 모든 컨테이너를 중지하고 제거합니다.
- 추가로 노드 이미지를 제거하기 전에 (node:6의) 하위 이미지를 제거해야 합니다.
docker stop $(docker ps -q) docker rm $(docker ps -aq) #추가 docker rmi node-app:0.2 gcr.io/[project-id]/node-app node-app:0.1 docker rmi node:6 docker rmi $(docker images -aq) # remove remaining images docker images
✔ 명령어 출력 결과
즉, 새로운 환경이나 다름없음을 확인할 수 있습니다.
3) 컨테이너를 가져와서 실행합니다.
- 이미지를 푸시하여 실행합니다.
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
✔ 명령어 출력 결과
tag가 0.2인 이미지를 푸시하여 port번호 4000으로 컨테이너를 실행한 후 localhost:4000을 확인해보니 기존의 'Hello World'가 아닌 'Welcome to Cloud'가 출력되는 것을 확인할 수 있습니다.
- Docker Hub의 공개 이미지를 기반으로 하여 컨테이너 실행
- 컨테이너 이미지를 빌드하고 Google Container Registry로 푸시
- 실행 중인 컨테이너를 디버그하는 방법 숙지
- Google Container Registry에서 가져온 이미지를 기반으로 하여 컨테이너 실행
🎉 평가 완료 인증 🎉
이 글은 "Google Cloud Study Jam"의 [Kubernetes 입문반] 강좌 실습 후 개인적으로 학습하기 위해 정리한 글입니다. 🙂
클라우드 맛집이네요 :D
잘 먹고 갑니다 ^^