비정기적으로 하는 인프라 스터디에서 12월부터 Programming Kubernetes를 보고 있다. 2년 전에 했던 방식과 비슷하게 이번에도 일주일에 한 번씩 1시간은 책의 진도를 나가고 한 시간은 CNCF의 프로젝트(라고 했지만, 딱히 CNCF가 아니더라도...)를 하나씩 살펴보고 공유하고 있다. 2년 전 스터디에서는 Cortex를 살펴봤다.
이번에는 Telepresence를 살펴봤다. Telepresence는 지난 회사에서 Kubernetes 클러스터를 운영하면서 개발팀의 비효율성을 해결하려고 찾아보다가 알게 된 프로젝트였다. Kubernetes를 도입한 지 오래되지 않아서 익숙하지도 않았고 보통 로컬 개발 환경은 docker-compose로 구성해서 사용하기도 하지만 개발 환경이 복잡해 지면서 docker-compose만으로 전체 시스템을 띄우기도 쉽지 않아졌다.
레거시를 개선하면서 레거시 백엔드와 새 백엔드가 다 필요한 경우도 많아졌고 데이터베이스에 캐시서버까지 갈수록 로컬에 띄워야 하는 일은 많아졌고 새로 개발환경을 구성하는 사람들 모두에게 부담이 되었고 로컬 환경이 느려져서 개발하기도 쉽지 않았다. 더군다나 이렇게 해도 Kubernetes 클러스터에 배포한 경우에는 동작하지 않기도 했고 모든 개발자가 자유롭게 Kubernetes 클러스터에 배포해보면서 개발하기에는 Kubernetes에 대한 지식이 너무 많이 필요했다. 특히 프론트엔드 팀은 SPA 앱을 띄우기 위해서 이 모든 환경을 띄워야 했기에 더욱 부담이 컸다.
예전에는 Skaffold에 대해서 들어봤지만 Telepresence가 2018년 5월부터 CNCF의 Sandbox 프로젝트가 되었으므로 Telepresence에 더 관심이 갔다.(난 Skaffold는 안써봤지만 스터디에서 논의해보니 로컬 개발 목적으로는 Teleprecence가 훨씬 편해 보인다고 한다.) 하지만 당시에는 Telepresence를 봐야지 하면서 다른 업무에 밀려서 결국 테스트해보지 못했고 이번 스터디를 기회 삼아서 살펴봤다.
참고로 다 보고 나니 다른 분이 알려주셔서 Naver에서 Telepresence로 Kubernetes 클러스터에서 실행할 애플리케이션을 로컬 환경에서 개발하기라는 글을 2019년에 올린 걸 알게 되었다. 아마 당시에 봤을 텐데 그때는 내가 Kubernetes를 안 쓰던 때라 읽고는 잊었지 싶다.
Telepresence
Telepresence는 Kubernetes에서 마이크로서비스를 개발할 때(Openshift도 된다지만 Openshift는 몰라서...) 빠른 로컬 개발을 위한 도구다. 설명대로 프로덕션 용도는 아니고 개발 및 디버깅 용도의 도구다.
Kubernetes 클러스터에 A라는 서버를 배포했을 때 기능을 변경하고 다시 확인하려면...
- 코드를 수정한다.
- Docker 이미지를 빌드한다.
- Docker 이미지를 레지스트리에 푸시한다.
- Kubernetes에 새 이미지를 적용한다.
보통 2, 3, 4 단계는 CI/CD에 자동화되어 있겠지만 그래도 무척 번거로운 작업이고 특히 Kubernetes에 적용했는데 단순 실수나 설정 오류를 발견해서 다시 처음부터 해야 한다면 피곤하기도 피곤하고 아주 비효율적으로 된다.
Telepresence는 딱 이 과정을 편하게 만들어 준다.
Telepresence 설치
설치 문서에 OS별 설치 방법이 나와 있는데 나는 macOS를 쓰고 있어서 Homebrew로 설치했다.
자주 업데이트하거나 아직 쓸지 안 쓸지 모르는 도구는 직접 설치하는 편이라 소스로 설치하는 방법도 살펴봤지만 의존성이 있어서 수동 설치하기가 좀 번거로워 보여서 그냥 brew
를 이용했다.
현재 최신 버전은 0.108이다.
테스트를 위한 환경 준비
내부 동작을 설명할 정도로 자세히 살펴보지 않았고 이에 대해서는 앞에서 언급한 Telepresence로 Kubernetes 클러스터에서 실행할 애플리케이션을 로컬 환경에서 개발하기가 더 도움이 될 것 같다.
간단히 설명하면 Telepresence가 로컬환경과 Kubernetes 클러스터 사이의 프락시를 만들어 주어서 로컬에서 쉽게 Kubernetes 클러스터 안으로 들어가 보거나 로컬이 Kubernetes 클러스터와 연결된 것처럼 사용할 수 있다.
사용해보니 직접 동작하는 걸 보는 게 이해하기가 좋아서 테스트를 할 수 있는 환경을 준비하자. Telepresence 사이트는 Python으로 설명하고 있지만 Node.js가 더 익숙해서 Node.js로 환경을 준비했다.
먼저 Docker 이미지 2개를 Docker Hub에 배포했다.
outsideris/express-test:latest
outsideris/express-test:hello
태그만 다를 뿐이지만 express를 사용한 웹 애플리케이션으로 동작은 같고 아래의 express-generator를 이용해서 다음과 같이 소스를 생성했다.
다음은 routes/index.js
파일이다.
여기서 6번 라인이 원래는 res.render('index', { title: 'Express' });
로 되어 있었는데 HTML은 CLI에서 보기가 더 어려워서 res.send('Express\n');
로 수정했다.
outsideris/express-test:latest
:res.send('Express\n');
outsideris/express-test:hello
:res.send('Hello World\n');
즉 /
로 요청을 보냈을 때 latest
는 Express
를 응답하고 hello
는 Hello World
를 응답한다. 서버를 구분하기 위해서 응답만 다르게 했을 뿐이다.
이를 배포하기 위해 Kubernetes 설정 파일을 YAML로 만들었다. 이 YAML에는 다음을 배포한다.
express-test
이름의 Deployment- 위 Deployment를 보는
express-test
라는 이름의 Service hello-world
이름의 Deployment- 위 Deployment를 보는
hello-world
라는 이름의 Service
전체 파일은 아래와 같다.
이 파일을 example.yml
로 저장하고 kubectl
로 적용한다.
배포된 상황을 확인해 보면 정상적으로 실행된 것을 볼 수 있다.
간단한 테스트이므로 다른 리소스는 생성하지 않았다. 서비스가 ClusterIP
로 설정되어 있기 때문에 클러스터 밖에서는 접근할 수가 없다.
Telepresence로 Kubernetes 클러스터 접근하기
클러스터에 프락시를 열어서 curl
로 요청을 보내보자.
telepresence --docker-run --rm -it pstauffer/curl curl http://express-test:3000
명령어를 이용하는데 --docker-run
옵션을 주어서 Docker를 실행하는데 pstauffer/curl 이미지를 이용해서 curl http://express-test:3000
을 실행한다. 서비스를 express-test
로 실행했으므로 express-test:3000
으로 요청을 보내는데 당연히 express-test
는 Kubernetes 클러스터 안에서만 인식하는 URL이고 로컬에서는 알 수 없는 URL이다.
여기서 앞에 T:
가 붙은 것은 Telepresence의 로그이고 마지막에 보면 Express
가 출력된 것을 볼 수 있다.
마찬가지로 http://hello-world:4000
으로 요청을 보내면 아래처럼 Hello World
응답이 제대로 온 것을 볼 수 있다.
위의 명령어를 실행하는 중에 kubectl
로 조회해 보면 아래처럼 telepresence-1610561203-7136698-5367
이라는 pod이 실행된 것을 알 수 있다.
이처럼 클러스터 내에 Pod를 띄우고(여기서는 pstauffer/curl
컨테이너) 이 Pod를 실행 결과를 로컬에서 보여준 것이기 때문에 Kubernetes 클러스터 내에 접근한 것처럼 다른 서비스에 요청을 보내서 확인해 볼 수 있다.
참고로 telepresence
를 터미널 세션에서 처음 실행하면 sudo
가 필요하다고 비밀번호를 입력하라고 나오고 macOS 기준으로는 아래처럼 권한 요청이 나와서 이를 허용해 주어야 한다.
Telepresence로 Docker 이미지를 이용한 로컬 개발
위에서는 Kubernetes 클러스터에 접근해서 요청을 보내봤는데 간단한 확인 정도는 가능하겠지만 실제로 개발하려면 이로써는 부족하다.
위에서 express-generator로 생성한 샘플 프로젝트에서 npm install
로 의존성을 설치한다.
Node.js에서는 코드를 변경해도 재시작하기 전에는 변경된 코드가 반영되지 않으므로 로컬 개발을 편하게 하려고 npm install nodemon
으로 nodemon을 설치한다. Dockerfile
에서 CMD npm start
로 서버를 실행하므로 이 부분을 수정해야 하는데 현재 package.json
에서 start
스크립트 부분은 다음과 같이 되어 있다.
nodemon
을 이용하도록 다음과 같이 변경한다.
수정한 코드를 기반으로 다시 Docker 이미지를 생성한다. 이 이미지는 로컬에서만 사용할 것이므로 demo:latest
태그를 사용했다.
참고로 Dockerfile
은 다음과 같다.
이제 이 Docker 이미지를 telepresence
로 연결한다. 명령어는 telepresence --swap-deployment express-test --docker-run --rm -it -v $(pwd):/app demo:latest
를 사용한다.
--swap-deployment
옵션을 사용해서express-test
Deployment를 교체한다.- 교체할 때는 뒤에
--docker-run
으로 실행한Docker
로 바꿔치기한다. 여기서 실행한 이미지는 로컬의demo:latest
이다. - 로컬에서
docker
를 실행할 때는-v $(pwd):/app
로 현재 디렉터리(express 프로젝트의 루트 디렉터리)를 Docker 이미지 내의WORKDIR
로 볼륨을 연결한다.
위를 보면 nodemon
으로 서버가 실행된 것을 볼 수 있다.
터미널을 하나 더 실행해서 kubectl
로 pstauffer/curl
이미지를 띄워서 sh
을 실행한다. 이건 Telepresence와는 상관없고 Kubernetes에 바로 컨테이너를 띄워서 접속한 것이다. 그리고 curl http://express-test:3000
로 요청을 보내면 원래대로 Express
가 응답 오는 걸 알 수 있다.
로컬에서 routes/index.js
파일의 res.send('Express\n');
를 res.send('Express modified\n');
로 수정한다.
그리고 아까 실행한 curl
이미지에서 다시 요청을 보내보면 로컬에서 변경된 내용이 바로 적용된 것을 알 수 있다.
이를 이용하면 로컬에서 내가 원하는 에디터(VS Code나 Web Storm이나)로 수정하면서 변경된 내용을 바로 확인할 수 있다.
Telepresence로 로컬 개발 서버에 바로 연결하기
위에서 Docker 이미지로 로컬 수정사항을 바로 적용할 수 있게 연결했지만, 로컬에 개발하는 것처럼 편하지는 않다. Kubernetes 클러스터 내의 Deployment를 로컬에서 접속할 수 있게 바로 열어보자.
이번에는 telepresence --swap-deployment express-test --expose 3000 --run npm start
명령어를 이용했다.
--swap-deployment
로express-test
라는 Deployment를 교체한다.--expose 3000
으로 pod의3000
포트를 로컬의3000
포트로 포워딩한다.--run npm start
로 앞에서--docker-run
대신 로컬 명령어npm start
를 실행한다.
앞에서 수정했으므로 npm start
를 하면 nodemon
으로 실행된 것을 볼 수 있다.
위에서 Docker 이미지를 이용했을 때와 마찬가지로 telepresence --swap-deployment
를 실행했을 때 pod를 조회해보면 기존 express-test
Pod이 내려가고(express-test-7dcd799b77-xrzdd
) telepresence
가 프락시를 열기 위해서 만드는 새 Pod(express-test-f6a14f473fe34ed3bea9db8fe6cedcab
)이 뜨는 것을 볼 수 있다.(테스트 해봤을 때 replicas
가 여러 대로 지정되어 있으면 --swap-deployment
할 때 replicas
가 여러 대여도 모두 내려가서 telepresence
가 띄운 새 Pod이 실행된다. 다시 말하면 프로덕션에서 사용하면 큰일 난다는 얘기다)
이제 로컬에서 nodemon
으로 서버를 띄웠고 이 서버가 Telepresence로 Kubernetes 클러스터와 연결되었다.
로컬에서 웹브라우저로 접속해 보면 아까 수정한 Express modified
가 잘 나오는 것을 볼 수 있고 당연히 로컬에서 수정하면(nodmon
이 서버를 새 시작하고) 변경된 내용이 나온다. 하지만 Telepresence 없이도 npm start
하면 로컬에선 저렇게 나오는 게 맞기 때문에 제대로 Kubernetes 클러스터와 연결된 건지 확인하기가 어렵다.
routes/index.js
파일을 현재 아래처럼 작성되어 있다.
이를 아래와 같이 수정한다. 이는 http
모듈을 이용해서 앞에 띄워두었던 hello-worl
Pod에 HTTP 요청을 보내고 그 응답을 res.send(
Express with ${data} on ${process.env.KUBERNETES_SERVICE_HOST}\n);
로 같이 내려준다. 내려주면서 Kubernetes 내에 실행될 때 같이 설정되는 환경 변수인 KUBERNETES_SERVICE_HOST
도 같이 내려준다.
다시 로컬에서 웹브라우저로 접속하면 Express with Hello World on 172.20.0.1\n
가 출력된 것을 볼 수 있다. 여기서 Hello World
부분은 hello-world
서버가 응답해 준 것이므로 로컬에 띄운 Node.js 서버가 마치 Kubernetes 클러스터 안에서 실행된 것처럼 클러스터의 다른 리소스에 접근할 수 있는 것을 알 수 있다.
이렇게 실행한 telepresence
명령어는 Ctrl
+ C
같은 인터럽트 명령어로 종료하면 자동으로 Pod이 내려가고 원래 Deployment의 Pod들이 새로 뜨게 된다.
그 외 명령어
앞에서는 기존의 배포한 Deployment를 교체하는 방식을 설명했는데 아직 배포하지 않은 애플리케이션이나 테스트 목적으로 로컬에서 바로 Kubernetes 클러스터에 연결해서 바로 개발하는 것도 가능하다.
아래처럼 --new-deployment
를 이용해서 기존 Deployment와는 상관없이 새로운 Pod이 배포되면서 로컬에 띄운 서버와 프락시로 연결해서 Kubernetes 클러스터 환경에서 개발할 수 있다.
그리고 앞에서는 설명을 위해 curl
을 실행하려고 curl
Docker 이미지를 이용했지만, 로컬에 curl
이 설치되어 있으면 아래처럼 바로 로컬 명령어를 실행해서 확인하는 것도 가능하다.
사용해 본 느낌
아직 업무에서는 사용해 보지 않고 테스트만 해봤지만, 생각보다 너무 쉽게 Kubernetes 클러스터와 연결해서 개발할 수 있어서 상당히 편해 보였다.
그리고 telepresence를 이용하지 않더라도 kubectl
이나 HELM을 이용하면 로컬에서 컨테이너를 바로 배포해서 테스트해보는 것도 가능하지만 이 경우 개발자가 kubectl
과 HELM의 사용법을 상당히 익혀야 하는 것에 반해서 telepresence는 훨씬 쉽게 사용할 수 있을 것으로 보이고 로컬 서버에 연결할 경우 Docker 이미지 빌드조차도 필요 없다는 것은 생산성을 많이 높여줄 것으로 보인다.
추가로 맥북이 1대뿐이라 정확히 테스트는 못 해봤지만 두 터미널에서 telepresence --swap-deployment ...
를 했을 때 기존 Deployment는 내려가지만, telepresence가 띄운 Pod는 둘 다 뜨는 것으로 보아 여러 명이 동시에 사용해도 충돌 없이 사용할 수 있다고 생각된다.
Comments