Upload
naver-labs
View
1.379
Download
14
Embed Size (px)
Citation preview
Docker + Kubernetes를 이용한 빌드 서버 가상화 사례
Naver Labs 김훈민
Docker + Kubernetes를 이용한 빌드 가상화 사례
Kubernetes 분석/삽질기
누구?
• 남편이자 두 아이의 아빠
• ~ 2011 웹개발
• ~ 2014 성능팀, Arcus 캐시 개발/운영
• 2015 ~ nDeploy 빌드 배포 시스템
Naver Labs & D2 Program
• Naver Labs
• 네이버의 핵심기술에 대한 Research & Engineering
• http://labs.naver.com
• D2 Program (For Developers, By Developers)
• 가치 있는 기술 지식 생산을 돕고 경험을 나누며, 개발자를 지원합니다.
오늘의 주제
• nDeploy
• Docker, CoreOS
• Kubernetes
오늘의 주제
• nDeploy
• Docker, CoreOS
• Kubernetes
• 경험으로 얻은 소소한 이야기
nDeploy
• 네이버와 라인, NHNEnt.에서 사용하는 빌드/배포 시스템.
• 사용자가 관리하는 Jenkins 서버를 좀 더 쉽고 강력하게 쓸 수 있게 해줍니다.
• 접근 권한 관리, 배포할 서버 관리, 시나리오에 기반한 자유도 높은 빌드 프로세스 정의가 가능합니다.
• In-house 앱 배포 기능도 있습니다.
번거로운 Jenkins 설치
• 자유도가 높아질 수록 운영 비용이 올라 갔습니다.
• Jenkins 설치는 어떻게 하나요?
• 플러그인은 뭘 깔아야 하나요?
• 서버 검색이 안되요 ㅠ_ㅜ
• VM을 쓰고 있는데 너무 느려요 ㅠ_ㅜ
Jenkins를 풀(pool)로 제공해봐
• VM만으로는 해결이 되지 않는다.
• 고사양의 공용 빌드 서버에 Jenkins를 여러 개 띄우려면?
• 각 사용자 별로 독립적인 환경을 어떻게 제공하지?
이 녀석들로 시작했지만…
여러 호스트에서 컨테이너를 관리하려면 어떻게 해야하지?
Host
Host
Host
Host
Host
Host
Controller
Docker Orchestration
CoreOS Fleet
• CoreOS에 내장. systemd를 클러스터 레벨로 지원.
• 단순한 스케줄러.
• 도커 컨테이너에 대한 별도의 지원은 없음.
• 서비스 디스커버리를 직접 구현해야 함.
• 하지만 도커 컨테이너를 여러 호스트에서 간단히 실행하기에는 좋은 플랫폼.
도커 Swarm
• 0.1.0 릴리즈 직후 테스트.
• Balanced 스케줄러가 없음.
• 컨테이너 리밸런싱 안됨.
• 컨테이너 리스트 캐싱 버그.
• swarm-agent가 죽어도 알아 차리지 못함.
• 0.2.0에서 어느 정도 개선되었습니다.
타임 오버
http://kubernetes.io/
Kubernetes
• 구글에서 공개한 리눅스 컨테이너 관리 시스템.
• GCE, AWS 같은 클라우드 환경과 물리 장비를 모두 지원.
• 구글의 리눅스 컨테이너 운용 경험을 녹였다고 함.
그리스어로 배의 조타수(Helmsman)
kubectl
• kubectl create -f controller.yaml
• kubectl create -f ./jenkins
• kubectl update -f ./jenkins
• kubectl delete -f ./jenkins
https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/design/architecture.md
아키텍처
Master
ApiServer
ReplicationController
Scheduler
Minion
Kubelet
Proxy
Minion
POD
Container
Container
Minion
POD 컨테이너들의 집합
JSON, YAML로 정의name:����������� ������������������ jenkins����������� ������������������ image:����������� ������������������ registry.ndeploy.com:5000/ndeploy/jenkins:latest����������� ������������������ command:����������� ������������������ ����������� ������������������ ����������� ������������������ -����������� ������������������ start����������� ������������������ ports:����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ -����������� ������������������ name:����������� ������������������ jenkins����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ containerPort:����������� ������������������ 8080����������� ������������������ env:����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ -����������� ������������������ name:����������� ������������������ JAVA_OPTS����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ value:����������� ������������������ -Xmx2g����������� ������������������ -Xms2g����������� ������������������ -Duser.timezone=Asia/Seoul����������� ������������������ memory:����������� ������������������ 4294967296����������� ������������������ volumeMounts:����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ -����������� ������������������ name:����������� ������������������ app-home����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ mountPath:����������� ������������������ /home/ndeploy/app_home����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ readOnly:����������� ������������������ true����������� ������������������ ����������� ������������������ ����������� ������������������ -����������� ������������������ name:����������� ������������������ localtime����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ mountPath:����������� ������������������ /etc/localtime
도커 네트워킹
• 호스트 환경과 독립적인 컨테이너를 외부에 노출하는 방법
• bridge (디폴트)
• host (netns)
• MACVLAN (향후 지원)
컨테이너 하나
docker0 (10.1.15.1/24)
veth2c8f4f4 (10.1.15.2/24)
NGINX
veth13f8c56
eth0 (192.168.0.100)
컨테이너 둘
docker0 10.1.15.1/24
eth0 (veth2c8f4f4) 10.1.15.2/24
NGINX
veth13f8c56
eth0 192.168.0.100
eth0 (veth80d8ec5) 10.1.15.3/24
JENKINS
vetha451781
NGINX 포트를 노출
docker0 10.1.15.1/24
eth0 (veth2c8f4f4) 10.1.15.2/24
NGINX:80
eth0 192.168.0.100
eth0 (veth80d8ec5) (10.1.15.3/24)
JENKINS:8080
DNAT :80 —> 10.1.15.2:80 (iptables)
-p 80:80
NGINX와 JENKINS 연결
docker0 10.1.15.1/24
eth0 (veth2c8f4f4) 10.1.15.2/24
NGINX:80
eth0 192.168.0.100
eth0 (veth80d8ec5) (10.1.15.3/24)
JENKINS:8080
DNAT :80 —> 10.1.15.2:80 (iptables)
link
JENKINS_PORT_8080_TCP
호스트가 다르다면?
docker0 10.1.15.1/24
eth0 (veth2c8f4f4) 10.1.15.2/24
NGINX:80
eth0 192.168.0.100
docker0 10.1.20.1/24
eth0 (veth80d8ec5) 10.1.20.2/24
JENKINS:8080
eth0 192.168.0.200
DNAT 80 DNAT 8080
포트가 여러 개 필요하다면?
docker0 10.1.15.1/24
eth0 (veth2c8f4f4) 10.1.15.2/24
NGINX:80
eth0 192.168.0.100
DNAT 80
docker0 10.1.20.1/24
eth0 10.1.20.2/24
JENKINS:8080
eth0 192.168.0.200
eth0 (10.1.20.3/24)
JENKINS:8080
?
Overlay Network
docker0 10.1.15.1/24
eth0 (veth2c8f4f4) 10.1.15.2/24
NGINX:80
eth0 192.168.0.100
docker0 10.1.20.1/24
eth0 10.1.20.2/24
JENKINS:8080
eth0 192.168.0.200
eth0 (10.1.20.3/24)
JENKINS:8080
CoreOS Flannel
docker0 10.1.15.1/24
eth0 (veth2c8f4f4) 10.1.15.2/24
NGINX:80
eth0 192.168.0.100
docker0 10.1.20.1/24
eth0 10.1.20.2/24
JENKINS:8080
eth0 192.168.0.200
eth0 (10.1.20.3/24)
JENKINS:8080
flannel.1 10.1.15.0/16
flannel.1 10.1.20.0/16flanneld
vxlan
여러 개의 컨테이너를 묶으려면?
docker0 10.1.15.1/24
eth0 (veth2c8f4f4) 10.1.15.2/24
NGINX:80
eth0 192.168.0.100
docker0 10.1.20.1/24
eth0 10.1.20.2/24
JENKINS:8080
eth0 192.168.0.200
eth0 (10.1.20.3/24)
AGENT:8081
flannel.1 10.1.15.0/16
flannel.1 10.1.20.0/16
여러 개의 컨테이너를 묶으려면?
docker0 10.1.15.1/24
eth0 (veth2c8f4f4) 10.1.15.2/24
NGINX:80
eth0 192.168.0.100
docker0 10.1.20.1/24
eth0 10.1.20.2/24
JENKINS:8080
eth0 192.168.0.200
AGENT:8081
flannel.1 10.1.15.0/16
flannel.1 10.1.20.0/16
netns
—net=container:pause
docker0 10.1.15.1/24
eth0 (veth2c8f4f4) 10.1.15.2/24
NGINX:80
eth0 192.168.0.100
docker0 10.1.20.1/24
eth0 10.1.20.2/24
JENKINS:8080
eth0 192.168.0.200
AGENT:8081
flannel.1 10.1.15.0/16
flannel.1 10.1.20.0/16
pause
POD = 컨테이너의 그룹
docker0 10.1.15.1/24
eth0 (veth2c8f4f4) 10.1.15.2/24
NGINX:80
eth0 192.168.0.100
docker0 10.1.20.1/24
eth0 10.1.20.2/24
JENKINS:8080
eth0 192.168.0.200
AGENT:8081
flannel.1 10.1.15.0/16
flannel.1 10.1.20.0/16
pause
POD
ReplicationController 복제를 담당합니다.
controller.yamlapiVersion:����������� ������������������ v1beta3����������� ������������������ kind:����������� ������������������ ReplicationController����������� ������������������ id:����������� ������������������ nginx����������� ������������������ desiredState:����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ replicas:����������� ������������������ 3����������� ������������������ ����������� ������������������ ����������� ������������������ replicaSelector:����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ name:����������� ������������������ nginx����������� ������������������ ����������� ������������������ ����������� ������������������ podTemplate:����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ desiredState:����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ manifest:����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ version:����������� ������������������ v1beta3����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ id:����������� ������������������ jenkins����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ containers:
앞서 보셨던 pod 정보를 기록합니다.
Replicas = 3
POD 10.1.30.2:8080
POD 10.1.20.2:8080
POD 10.1.40.2:8080
Kubelet Kubelet Kubelet
ApiServer
ReplicationController
Host A Host B Host C
POD 하나가 죽었다면?
POD 10.1.30.2:8080
POD 10.1.20.2:8080
POD 10.1.40.2:8080
Kubelet Kubelet Kubelet
ApiServer
ReplicationController
Host A Host B Host C
1. notify
2. periodic check (10s)
다시 살려줍니다
POD 10.1.30.2:8080
NEW POD 10.1.20.5:8080
POD 10.1.40.2:8080
Kubelet Kubelet Kubelet
ApiServer
ReplicationController
Host A Host B Host C
4. create
3. create하지만 IP가 바뀌었네요
같은 POD들을 묶을 수 있으면 좋겠네요
POD 10.1.30.2:8080
POD 10.1.40.2:8080
Proxy & Load Balancer 10.100.0.1:8080
Host A Host B Host C
POD 10.1.20.5:8080
Client
Service & Proxy
service.yaml
apiVersion:����������� ������������������ v1beta3����������� ������������������ kind:����������� ������������������ Service����������� ������������������ id:����������� ������������������ nginx-service����������� ������������������ containerPort:����������� ������������������ nginx����������� ������������������ port:����������� ������������������ 8080����������� ������������������ labels:����������� ������������������ ����������� ������������������ ����������� ������������������ name:����������� ������������������ nginx����������� ������������������ ����������� ������������������ ����������� ������������������ role:����������� ������������������ nginx����������� ������������������ selector:����������� ������������������ ����������� ������������������ ����������� ������������������ name:����������� ������������������ nginx
service를 생성하면 별도의 IP가 할당됩니다
POD 10.1.30.2:8080
POD 10.1.40.2:8080
nginx-service 10.100.0.1:8080
Host A Host B Host C
POD 10.1.20.5:8080
Client
PROXY PROXY PROXY
Portal IP IP가 충돌할 수 있으니 다른 subnet을 사용
각 호스트에 있는 Proxy가 iptables 정책을 변경하여 어디서든 접속할 수 있도록 합니다.
POD 10.1.30.2:8080
POD 10.1.40.2:8080
nginx-service 10.100.0.1:8080
Host A Host B Host C
POD 10.1.20.5:8080
Client
PROXY PROXY PROXY
port
42356port
33615port
55178
(REDIRECT / DNAT) 10.100.0.1
tcp dpt:8080 to:<ip:port>
정리하면…
Master
ApiServer
ReplicationController
Scheduler
Minion
Kubelet
Proxy
Minion
POD
Container
Container
Minion
사례: nDeploy 빌드 풀
요구 사항
• 고성능 SSD 장비를 이용한 공용 빌드 서버 제공.
• 발급 요청과 동시에 독립된 Jenkins 서버와 접근 URL 생성.
• Docker 클러스터를 통한 고가용성.
• 사용자 데이터에 대한 백업 지원.
Jenkins 빌드 풀(pool)
아키텍처
• https://coreos.com/
• 최신 커널과 리눅스 플랫폼을 지원하는 경량 리눅스 배포본.
• 선택한 이유
• system, journald, fleetd, etcd, flanneld
• CentOS 7이 아직 초기 단계.
• cloudinit을 이용한 쉬운 OS 형상 관리.
사실 한번 써보고 싶어서…
보스
Ansible
• CoreOS 및 Kubernetes 클러스터 운영을 자동화.
• CoreOS 호스트 별 OS/플랫폼 설정을 템플릿 화 할 수 있음.
• CoreOS에는 파이썬이 설치되어 있지 않기 때문에 직접 설치해야 합니다.
• https://github.com/defunctzombie/ansible-coreos-bootstrap
[coreos-kubernetes] master01 fleet_metadata=master=true,minion=true master02 fleet_metadata=master=true,minion=true minion01 fleet_metadata=master=true,minion=true minion02 fleet_metadata=master=false,minion=true
[coreos-kubernetes:vars] ansible_ssh_user=coreos ansible_python_interpreter="PATH=/home/coreos/bin:$PATH python"
# coreos update coreos_update_policy=off coreos_update_channel=stable
# kubernetes kubernetes_version=v0.15.0 kubernetes_master=master.kubernetes.ndeploy.com:8080
Jenkins 도커 이미지
• All-in-one 이미지를 하나만 관리.
• 사내에서 주로 사용하는 빌드 도구나 SDK를 미리 설치.
• JDK7/8, maven, gradle, nodejs, android SDK 등.
• 필수 플러그인을 미리 설정.
• 인증, 빌드 도구, nDeploy 등.
Jenkins 도커 이미지
• official jenkins Dockerfile을 기반으로 사내 환경에 맞춤.
• 사내 계정, 사내 플러그인 설치 등.
• 기본 빌드 도구 및 SDK는 호스트에 설치하여 볼륨 마운트.
• 로컬 라이브러리 저장소(maven, gradle)도 공유.
Jenkins Reverse Proxy
• 도메인에 연결된 전용 Jenkins URL 제공.
• /<jenkins-id>
• Golang으로 구현한 간단한 reverse proxy로 역시 Kubernetes pod/service로 구동. 80 포트를 노출.
• 요청된 jenkins-id에 대해 Kubernetes의 service discovery 정보를 확인하여 실제 endpoint IP를 확인.
Jenkins Reverse Proxy
• ApiServer에서 지원하는 Proxy를 쓰지 않은 이유
• apiserver로 모든 요청이 집중되기 때문. (요청량 제한)
• 어차피 ApiServer로 부터 discovery 정보를 얻지 않나?
• 메모리에 캐싱하여 주기적으로 갱신해야 함.
• ApiServer의 Watch API를 이용, 리얼타임 갱신도 수행.
Private Docker Registry
• nDeploy 전용의 도커 저장소가 필요.
• 사내 환경에 맞는 도커 이미지 서비스: OS, Jenkins 등
• 사용자별 Jenkins 도커 이미지 저장.
• V1: https://github.com/docker/docker-registry
• V2: https://github.com/docker/distribution
V1 버전은 쓰지 마세요
• 매우 느립니다.
• 자주 죽습니다. (심지어 sleep 하다가도…)
• 검색 엔진(alchemy) 붙일 경우 UPDATE lock에 시달립니다.
• 하지만 도커 1.5 버전 까지는 어쩔 수 없이 써야 합니다ㅠ_ㅜ
V2로 업그레이드
• 마이그레이션
• Docker 1.6과 Registry V2를 설치하여 실행.
• docker pull을 이용하여 registry1의 모든 이미지를 pull.
• 도커 업그레이드 (1.5 -> 1.6)
• 이미지 호환되지 않음.
• 1.6 설치 후, 다른 storage path를 지정하여 실행.
Kubernetes Master 이중화
• Kubernetes는 아직 master 이중화를 지원하지 않습니다.
• 저희는 L4와 fleet을 이용해 이중화 하고 있습니다.
• fleet에 대한 자세한 내용은 아래 링크를 참고 해주세요.
• https://github.com/coreos/fleet
Kubernetes Master 이중화
L4(VIP)
Master1 (fleet_metadata=master=true,minion=true)
Master2 (fleet_metadata=master=true,minion=true)
Master3 (fleet_metadata=master=true,minion=true)
모든 master 프로세스가 3대 중 하나의 호스트에서만 실행되도록 합니다.
Kubernetes Master 이중화
kube-apiserver.service
[Service] ExecStart=/kube-apiserver
[X-Fleet] MachineMetadata=master=true
kube-controller-manager.service
[Service] ExecStart=/kube-controller-manager
[X-Fleet] MachineOf=kube-apiserver.service MachineMetadata=master=true
apiserver가 실행되는 호스트에서 함께 실행되도록 스케줄링 합니다.
Kubernetes Master 이중화- name: submit kubernetes master services to fleet command: "{{ item }}" with_items: - fleetctl submit /etc/systemd/system/kube-apiserver.service - fleetctl submit /etc/systemd/system/kube-controller-manager.service - fleetctl submit /etc/systemd/system/kube-scheduler.service - fleetctl submit /etc/systemd/system/kube-register.service
- name: start kubernetes master services command: "{{ item }}" with_items: - fleetctl start kube-apiserver.service - fleetctl start kube-controller-manager.service - fleetctl start kube-scheduler.service - fleetctl start kube-register.service
Kubernetes 클러스터 관리
운영 작업을 위해 간단한 웹 UI 구현
컨테이너 모니터링
• 지금은 초보적인 모니터링만 수행
• fluentd, elasticsearch를 이용하여 컨테이너 로그 수집
• cadvisor, heapster, grafana를 이용한 통계 확인
• git clone https://github.com/GoogleCloudPlatform/heapster
• kubectl create -f heapster/deploy/kube-config
부록: 소소한 경험담
발전 가능성 <= 성숙도
Go 언어를 익히면 좋다.
최신 OS/플랫폼은 공짜가 아니다.
journald는 기본적으로 메모리에 로그를 저장한다.
리소스 제한은 먼저 고민하지 말자. 나중에 할 수 있다.
가능하면 컨테이너에 사용자 데이터를 넣지 말자
Docker Timezone
운영 도구는 직접 만들어 써야한다.
Q & A