OSX 환경에서 CI/CD 자동화

현 시점에선 이 구성을 사용 안하지만 조사해 둔 내용이 아까워서 osx 환경에서 CI / CD 자동화 관련 참고할만한 문서 링크들을 정리해 둔다.

결론부터…

  • 여러 버전의 ios sdk 관리는 xcode-select / xserver 사용
  • unreal 이나 unity 같이 gpu / 하드웨어 특성을 타는 빌드 환경이 아니라면 vagrant
  • 하드웨어 종속성 탓에 물리적인 빌드환경이 필요하다면 ansible

ios sdk 빌드 관련 문서

원칙적으로 sdk는 하위 호환성을 보장하여야 한다. 허나 현실에서 반드시 최신 버전의 빌드도구가 하위 버전을 지원한다는 보장은 없다. 여러 프로젝트를 하나의 머신에서 빌드해야 하는데 sdk나 서드파티 라이브러리 버전이 서로 배타적일 때, 모든 프로젝트의 빌드환경을 만족하는 구성을 관리하는건 손이 많이 가는 일이다. 특히 ios sdk 의 패키지 / 종속성 관리는 애플에서 제공하는 대로 따르는 수 밖에 없기 때문에 더욱 귀찮다.애플에서 제공하는 문서를 잘 찾아보고 읽어야 한다.

인프라 자동화 관련 문서

osx가 리눅스나 윈도우처럼 docker 환경을 지원한다면 빌드환경을 구성하는게 편했을텐데 아쉽게도 osx 는 docker를 지원하지 않는다. vm 환경 또한 구성하는게 편하지는 않다. 공식적으로 os image 다운로드 링크를 제공해주지도 않을 뿐더러, osx 이외에 다른 host os 에서 osx vm을 실행하는건 불법이기 때문이다. 하지만 어떻게든 vm 환경을 구성할 수 있으며, vagrant 같은 vm 관리도구로 인프라를 자동화할 수 있다.

하지만 unreal 이나 unity 처럼 gpu 하드웨어에 종속적인 개발환경을 병행하여야 한다면 vm으로 빌드 인프라를 구성할 수는 없다. 이런 경우에 쓸 수 있는 좋은 솔루션으로 ansible을 추천한다. ansible galaxy 에는 ios 개발환경을 쉽게 자동화할 수 있는 양질의 레시피들이 제공되므로 참고해 보길 바란다.

kubernetes multi-node cluster 구성하기

kubernetes 공식 문서에서는 다양한 kubernetes cluster 구성 전략 을 제공하고 있다.

그 중 docker를 이용한 cluster 구성 방식(portable multi-node)으로 multi-node cluster 를 구성하는 방법을 기술한다.

설치 후 알게 된 사실

portable multi-node 는 kubernetes developer community 에서 업데이트가 뜸하다.  대신 kubeadm 활동이 활발하다.(관련링크) 아직 alpha release 이므로 좀 더 안정화될 때까지 기다렸다가(얼마 안남은 듯) kubeadm 으로 cluster 구성하는 것을 추천한다.

prerequisite
docker 만 있으면 됨
필자는 centos 7 에 설치하였다.

docker 설치
docker 설치는 이 사이트에서 하란대로 하면 쉽게 설치가 가능하다.

환경에 따라 docker daemon 이 올라오지 않는 문제가 있다.
필자의 경우에는 docker0 라는 가상 bridge 가 생성되지 않아서 강제로 docker0 브릿지를 생성하였다.

brctl addbr docker0
ip addr add 192.168.5.1/24 dev docker0
ip link set dev docker0 up
iptables -t nat -L -n

gnome gvfs 에서 io 를 엄청나게 점유해 버리는 문제가 있다.
관련 app / service 를 모조리 uninstall 하고 reboot 하자.

kubernetes docker image 설치

이 글을 쓰는 도중 kubernetes documentation 에서 docker multi-node 구성이 지워졌다.

앞으로는 kubeadm 으로 multi node 구성하면 된다. (링크 : https://kubernetes.io/docs/getting-started-guides/kubeadm/)

내가 이 글을 쓴 이유가 공홈 다큐멘테이션 대로 docker multi node 구성하면 설치가 안되기 때문이었는데 글 쓸 이유가 사라졌다.(ㅠㅠ)

글은 여기서 줄인다. 아마도 kubeadm 으로 설치하면 한방에 잘 설치 될거다.

 

kubernetes 로 flask web app 배포하기

kubernetes?

고대 그리스어로 조타수? 키잡이? 라는 의미라고 한다.
구글에서 개발한 container orchestration tool(vm 또는 container 관리를 자동화해주는 도구)이다.

 

why kubernetes?

사실 이 부분은 충분한 경험이 없어서 자신있게 말을 못하겠다.
온라인에서 이것 저것 찾아보면 몇 가지 후보군이 더 있다.

  • swarm
  • mesos / marathon
  • 기타(kotena, nomad …)

이 중에서 kubernetes 를 선택한 이유는
기본적으로 제공하는 기능과 설계구조가 전반적으로 낫다는 평(카더라)
업계의 선택 – 실무 활용 사례가 많다는 점

때문이다. 다른 오케스트레이션 툴 중에 더 나은 것이 있다면 얼마든지 변경할 수 있다.

 

설치

는 생략… 공식 홈페이지에 tutorial 이 잘 나와 있다.

클러스터링 전략을 어떻게 가져가느냐에 따라 다양한 방법으로 설치가 가능하므로
문서를 잘 읽어보고 본인의 환경에 맞게 설치하길 바란다.

처음 시작하는 사람들은 로컬 개발환경에 docker / minikube / kubectl 설치하면 된다.
https://kubernetes.io/docs/getting-started-guides/minikube/

 

flask app 만들기

아래와 같이 간단하게 동작하는 flask app을 만들자.

중요!
host 설정을 0.0.0.0 으로 하여야 가상 컨테이너 외부에서 별도 proxy 없이 접근 가능하다.

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello!!"

if __name__ == "__main__":
    app.run(host='0.0.0.0')

특별한 설정 없이 실행하는 것 만으로 웹 서비스를 제공한다.

$ python3 hello.py
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [11/Apr/2017 08:40:53] "GET / HTTP/1.1" 200 -

이 flask app 을 docker image 로 만들어 보자.

docker image / container 만들기

다음과 같이 Dockerfile 을 만들자.
기존에 제공된 python image 기반으로 조금 전에 만든 flask app 을 복사/실행하도록 하자.

FROM python:3.6
EXPOSE 5000
COPY hello.py .
RUN pip install flask
CMD ["python",  "./hello.py"]

만든 Dockerfile로부터 docker image 를 만들자.

$ docker build -t hello-flask:v1 .

이 docker image 가 잘 동작하는지 확인해 보자.

docker run -it --rm -p 5000:5000 hello-flask:v1
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
172.17.0.1 - - [10/Apr/2017 23:54:50] "GET / HTTP/1.1" 200 -

 

 

kubernetes 로 docker container(pod 또는 deployment) 만들기

다음 2가지를 주의한다.
kubernetes 는 Dockerfile 로부터 container를 생성하는 기능은 제공하지 않는다. docker image를 미리 만들어 두어야 한다.
kubernetes 하위에서 실행되는 docker 환경은 시스템에 설치된 docker 환경과는 완전히 독립적이다. docker image / container 를 공유하지 않으므로 image를 다시 만들어야 한다.

$ eval $(minikube docker-env) //kubernetes docker 실행환경 전환
$ eval $(minikube docker-env -u) //kubernetes docker 실행환경 원복

kubernetes docker 실행환경으로 전환하여 docker image 를 만든 후, kubectl을 이용하여 다음과 같이 deployment(pod) 를 생성한다.

$ kubectl run hello-flask --image=hello-flask:v1 --port=5000
deployment "hello-flask" created

kubectl 을 이용해 deployment / pod 정보를 보면 아래와 같다.

$ kubectl get deployments
NAME             DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-flask      1         1         1            1           24s

$ kubectl get pods
NAME                              READY     STATUS    RESTARTS   AGE
hello-flask-2475082718-xw1s3      1/1       Running   0          3m

 

 

pod vs deployment ?

kubernetes 는 container / container group 의 단위로 pod 를 사용한다.
반면 deployment 는 pod 의 상태를 체크하여 pod 생성/재시작/scaling 을 담당한다.
pod 가 Object 라면 deployment 는 Factory 나 Builder 와 같은 관계랄까.

위와 같은 이유로 보통 kubectl 로 container 를 생성/삭제할 때에는 deployment 로 호칭하므로 참고하기 바란다.

 

deployment 로 service 만들기

deployment 만 생성하여서는 아직 서비스를 할 수가 없다.
다음과 같이 kubectl 명령을 사용하여 deployment 를 service 로 실행한다.

kubectl expose deployment hello-flask --type=LoadBalancer --name=hello-flask

kubectl 을 이용해 service 정보를 확인해 보자

$ kubectl get service hello-flask
NAME          CLUSTER-IP   EXTERNAL-IP   PORT(S)          AGE
hello-flask   10.0.0.245   <pending>     5000:30276/TCP   <invalid>

$ kubectl describe service hello-flask
Name:                   hello-flask
Namespace:              default
Labels:                 run=hello-flask
Selector:               run=hello-flask
Type:                   LoadBalancer
IP:                     10.0.0.245
Port:                   <unset> 5000/TCP
NodePort:               <unset> 30276/TCP
Endpoints:              172.17.0.2:5000
Session Affinity:       None
No events.

다음 minikube 명령을 이용하면 flask app 이 어느 external ip 와 mapping 되어 있는지 브라우저로 확인할 수 있다. 현재 별도로 kubernetes 에게 external ip/port 할당 관련한 설정을 하지 않았으므로 아무렇게나 할 것이다.

$ minikube service hello-flask
Opening kubernetes service default/hello-flask in default browser...

다음과 같이 stdout log 도 확인 가능하다.

$ kubectl logs -f hello-flask-2475082718-xw1s3
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
172.17.0.1 - - [11/Apr/2017 00:36:00] "GET / HTTP/1.1" 200 -
172.17.0.1 - - [11/Apr/2017 00:36:00] "GET /favicon.ico HTTP/1.1" 404 -
172.17.0.1 - - [11/Apr/2017 00:38:15] "GET / HTTP/1.1" 200 -
172.17.0.1 - - [11/Apr/2017 00:40:47] "GET / HTTP/1.1" 200 -
172.17.0.1 - - [11/Apr/2017 00:42:22] "GET / HTTP/1.1" 200 -
172.17.0.1 - - [11/Apr/2017 00:47:26] "GET / HTTP/1.1" 200 -
172.17.0.1 - - [11/Apr/2017 00:47:27] "GET / HTTP/1.1" 200 -
172.17.0.1 - - [11/Apr/2017 00:47:27] "GET / HTTP/1.1" 200 -
172.17.0.1 - - [11/Apr/2017 00:47:27] "GET / HTTP/1.1" 200 -

중요!
container 기반 app 을 작성할 때에는 log를 반드시 stdout 으로 남기도록 한다.
그래야 log 를 container 와 별도로 수집 가능하다.