도커를 사용한 컨테이너 이미지 생성, 실행, 공유하기
https://docs.docker.com/desktop/install/windows-install/ 에서 운영체제에 맞는 도커를 설치할 수 있다.
설치가 완료되면 도커 클라이언트 실행파일로 다양한 도커 명령을 실행할 수 있다.
도커 허브는 잘 알려진 소프트웨어 패키지를 위한 즉시 실행 가능한 이미지를 보유하고 있다. 그 가운데 하나가 busybox 이미지이며 간단하게 echo "Hello world" 명령을 실행하는데 사용된다.
busybox 이미지를 실행하기 위해 어떤 것도 다운로드하거나 설치할 필요없다.
docker run 커맨드를 사용하여 이미지를 다운로드하고 실행한다.
$ docker run busybox echo "Hello world"
app.js
const http = require('http');
const os = require('os');
console.log("Kubia server starting...");
var handler = function(request, response) {
console.log("Received request from " + request.connection.remoteAddress);
response.writeHead(200);
response.end("You've hit " + os.hostname() + "\n");
};
var www = http.createServer(handler);
www.listen(8080);
위 코드는 포트 8080으로 HTTP 서버를 시작하고, 서버는 모든 요청에 대해 상태코드 200 OK 와 "You've hit [hostname]" 택스트를 HTTP 응답으로 한다.
요청 핸들러는 나중에 필요한 경우를 위해 클라이언트 IP 주소를 표준 출력에 로깅한다.
도커를 통해 애플리케이션을 컨테이너 이미지로 패키징하여 어디에서든 실행할 수 있다.
패키징을 하기위해 이미지를 위한 Dockerfile 생성을 해보자.
Dockerfile
FROM node:7
ADD app.js /app.js
ENTRYPOINT ["node", "app.js"]
FROM 줄은 시작점으로 사용할 컨테이너 이미지를 정의한다.
이 경우 node 컨테이너 태그 7을 사용한다.
두번째 줄은 로컬 디렉터리의 app.js 파일을 이미지의 루트 디렉터리에 동일한 이름으로 추가한다.
마지막으로 세번째 줄에서는 이미지를 실행했을 때 수행돼야 할 명령어를 정의한다.
이 경우 node app.js 이다.
Dokerfile과 app.js 파일을 생성했으므로 이미지를 빌드하려면 다음 도커 명령을 실행한다.
$ docker build -t kubia .
빌드 프로세스가 완료되면, 새로운 이미지가 로컬에 저장된다.
images 명령을 사용하면, kubia 라는 레포지토리로 빌드 한것을 확인할수 있다.
$ docker images
다음 명령어를 사용해 이미지를 실행할 수 있다.
$ docker run --name kubia-container -p 8080:8080 -d kubia
이 명령어는 도커가 kubia 이미지에서 kubia-container라는 이름의 새로운 컨테이너를 실행하도록 한다. 컨테이너는 콘솔에서 분리돼 백그라운드 에서 실행됨을 의미한다.
로컬머신의 8080포트가 컨테이너 내부의 8080포트와 매핑되므로 애플리케이션에 접근할 수 있다.
curl 명령어를 사용해 접근해보자
이 작은 애플리케이션은 격리된 컨테이너 내부에서 실행중인것을 확인할 수 있다.
$ curl localhost:8080
You've hit 44d76963e81
모든 컨테이너를 조회해서 리스트를 확인하다.
$ docker ps
$ docker inspect kubia-container
실행 중인 컨테이너의 기본 이미지인 node.js는 bash 셸을 포함하고 있으므로 다음과 같은 컨테이너 내부에서 셸을 실행할 수 있다.
$ docker exec -it kubia-container bash
-it 옵션은 두 옵션을 축약한 것이다
-i : 표준 입력을 오픈상태로 유지한다. 셸에 명령어를 입력하기 위해 필요하다
-t : 의사 터미널을 할당한다.
그러므로 일반적인 셸을 사용하는 것과 동일하게 사용하고 싶다면 두 옵션이 모두 필요하다.
컨테이너 내부에서 프로세스 조회하려면 다음과 같은 명령을 사용한다
root@44d76963e81"/# ps aux
호스트 운영체제에서 실행 중인 컨테이너 프로세스 확인하는 방법이다
$ ps aux | grep app.js
중지
$ docker stop kubia-container
삭제
$ docker rm kubia-container
이미지를 푸시하기 전에 도커허브의 규칙에 따라 이미지 태그를 지정해야한다.
추가 태그로 이미지 태그 지정을 한다.
$ docker tag kubia luksa/kubia
docker images 명령으로 시스템에 저장된 이미지를 조회해 추가된 태그를 확인 할 수있다.
다음으로 도커허브에 이미지를 푸시한다.
$ docker push luksa/kubia
이미지 푸시가 완료되면 모든 사람이 이미지를 사용할 수 있다.
$ docker run -p 8080:8080 -d luksa/kubia
이제 컨테이너 이미지에 애플리케이션을 패키징하고 도커 허브를 통해 사용할 수 있게 됐다. 도커에서 직접 실행하는대신 쿠버네티스 클러스터에 배포할 수 있다.
하지만 먼저 쿠버네티스 클러스터를 설치해야한다.
https://cloud.google.com/kubernetes-engine/docs/deploy-app-cluster
사이트에서 안내에 따라 설치한다.
대략 절차는 아래와 같다
설치가 완료되면 워커노드 세 개를 가진 쿠버네티스 클러스터를 생성할 수 있다.
$ gcloud container clusters create kubia --num-nodes 3
NAME STATUS ROLES AGE VERSION
gke-kubia-default-pool-c2f41b01-37p5 Ready <none> 3m10s v1.22.11-gke.400
gke-kubia-default-pool-c2f41b01-3gd0 Ready <none> 3m10s v1.22.11-gke.400
gke-kubia-default-pool-c2f41b01-wgt5 Ready <none> 3m10s v1.22.11-gke.400
$ kubectl get nodes
오브젝트에 대한 상세 정보를 보려면 describe 명령을 사용하여
세부 정보를 가져올 수 있다.
모든 노드의 상세 정보 출력
$ kubectl describe node
특정 노드의 상세 정보 출력
$ kubectl describe node gke-kubia-default-pool-c2f41b01-wgt5
node.js 애플리케이션 구동하는 방법으로 가장 간단한 방법은 kubectl run 명령어를 사용해 JSON 이나 YAML을 사용하기 않고 필요한 모든 구성 요소를 생성하는 방법이다.
이전에 생성해 도커 허브에 푸시한 이미지를 실행해보자
$ kubectl run kubia --image=luksa/kubia --port=8080
여기선 쿠버네티스에서 디플로이먼트 대신 파드를 생성하도록 했다
(디플로이먼트에 관해서는 추후에 다루도록 하겠다)
파드는 컨테이너 그룹이다 kubectl get pods 로 파드를 조회한다.
Pending 상태에서 파드가 할당된 워커 노드가 컨테이너를 실행하기 전에 컨테이너 이미지를 다운로드하고, 다운로드가 완료되면 Running 상태로 전환된다.
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
kubia 1/1 Running 0 68s
각 파드는 자체 ip 주소를 가지고 있지만 이 주소는 클러스터 내부에 있으며 외부에는 접근이 불가하다.
외부에서 파드에 접근을 가능하게 하려면 서비스 오브젝트 로드밸런서를 통해 노출해야한다. (서비스도 추후에 자세하게 다룬다.)
로드 밸런서의 퍼블릭 ip를 통해 파드에 연결할 수 있다.
서비스를 생성하기 위해 쿠버네티스에게 앞서 생성한 파드를 노출하도록 명령한다.
$ kubectl expose po kubia --type=LoadBalancer --name kubia-http
service/kubia-http exposed
expose 명령어 출력 결과를 보면 kubia-http 라는 서비스가 표시된다.
서비스는 파드나 노드 같은 오브젝트로 새로 생성된 오브젝트를 볼수 있다.
로드 밸런서의 가동이 완료되면 서비스의 외부 IP 주소가 표시된다.
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.36.0.1 <none> 443/TCP 23m
kubia-http LoadBalancer 10.36.5.126 35.247.77.246 8080:32614/TCP 47s
이제 서비스의 외부 IP와 포트를 통해 파드에 요청을 보낼 수 있다.
$ curl 35.247.77.246:8080
You've hit kubia
-o wide 옵션을 사용하면 추가 열을 요청할 수 있다.
파드 IP와 파드가 실행중인 노드를 표시한다.
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kubia 1/1 Running 0 10m 10.32.1.9 gke-kubia-default-pool-c2f41b01-37p5 <none> <none>
kubectl decribe pod kubia 를 사용하면 파드의 상세정보를 보여준다.
$ kubectl describe pod kubia
Name: kubia
Namespace: default
Priority: 0
Node: gke-kubia-default-pool-c2f41b01-37p5/10.138.0.3
Start Time: Sun, 04 Sep 2022 05:59:34 +0000
Labels: run=kubia
Annotations: <none>
Status: Running
...
보통 레플리케이션컨트롤러는 파드를 복제하고 항상 실행 상태로 만든다
여기서는 파드의 레플리카를 지정하지 않고 레플리케이션컨트롤러 파드하나를 생성해보자.
kubia.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: kubia
labels:
run: kubia
spec:
replicas: 1
selector:
run: kubia
template:
metadata:
labels:
run: kubia
spec:
containers:
- name: kubia
image: luksa/kubia:latest
ports:
- containerPort: 8080
protocol: TCP
kubia.yaml 파일을 만들고 kubectl create -f kubia.yaml 명령으로 레플리케이션컨트롤러를 생성한다. rc는 레플리케이션컨트롤러의 줄임말로 아래의 명령어로 조회가 가능하다
(yaml 디스크럽터에 관해서는 다음 챕터에 상세히 다루도록 하겠다.)
$ kubectl get rc
NAME DESIRED CURRENT READY AGE
kubia 1 1 1 2m1s
기존의 있던 파드를 제거한다.
$ kubectl delete po kubia
pod "kubia" deleted
여기서 kubectl get po 를 조회해보면
레플리케이션컨트롤러로 만들어진 파드가 다시 생성되어있는 것을 볼 수 있다.
$ kubectl get po
NAME READY STATUS RESTARTS AGE
kubia-lzbwf 1/1 Running 0 2m28s
이제 실행 중인 애플리케이션은 레플리케이션컨트롤러에의해 모니터링되고 실행되고 있다.
스케일을 이용하여 파드의 레플리카 수를 늘린다.
$ kubectl scale rc kubia --replicas=3
다음 명령으로 파드가 세 개로 표시되는 것을 볼 수 있다.
$ kubectl get po
NAME READY STATUS RESTARTS AGE
kubia-b5fxm 1/1 Running 0 66s
kubia-lzbwf 1/1 Running 0 13m
kubia-p8vdx 1/1 Running 0 66s
우리가 이전에 서비스를 통해 외부호출을 하도록 로드밸런싱 설정을 했었다.
실행 중인 애플리케이션이 다수의 인스턴스를 갖기 때문에 서비스 URL를 호출하면
다음 과 같이 무작위로 다른 파드를 호출하고 있는것을 확인할 수 있다.
$ curl 35.247.77.246:8080
You've hit kubia-lzbwf
$ curl 35.247.77.246:8080
You've hit kubia-b5fxm
$ curl 35.247.77.246:8080
You've hit kubia-b5fxm
$ curl 35.247.77.246:8080
You've hit kubia-p8vdx
🧨 도커와 쿠버네티스의 기본적인 명령어들을 살펴봤다.
다음 챕터에서는 파드에 집중적으로 탐구해보자