GCP에서 GKE를 구성하여 앱을 배포해볼 것이다.
(개인적으로 클러스터 구축하기가 너무 편하다..)
간단하게 웹페이지 방문 횟수가 Count되어 뜨는 app을 배포할 것이고 DB는 redis를 사용할 것이다.
Flask 애플리케이션은 Redis를 사용하여 데이터를 저장한다.
Redis Master에 데이터를 쓰고 여러 Redis Slave에서 데이터를 읽는 구조.
redis-master.yaml
apiVersion: v1
kind: Namespace
metadata:
name: backend
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-master
namespace: backend
spec:
selector:
matchLabels:
app: redis
role: master
tier: backend
replicas: 1
template:
metadata:
labels:
app: redis
role: master
tier: backend
spec:
containers:
- name: master
image: k8s.gcr.io/redis:e2e
ports:
- containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
name: redis-master
namespace: backend
annotations:
cloud.google.com/load-balancer-type: "Internal"
labels:
app: redis
role: master
tier: backend
spec:
type: LoadBalancer
ports:
- port: 6379
targetPort: 6379
selector:
app: redis
role: master
tier: backend
redis가 배포될 backend namespace가 따로 생성되고 Service를 통해 Internal LB가 생성될 것이다.
redis svc의 external-ip가 나오면 이 external-ip를 밑에 redis-slave.yaml 안에 입력해서 redis-slave들과의 connection을 만들어줘야 한다.
redis-slave.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-slave
namespace: backend
spec:
selector:
matchLabels:
app: redis
role: slave
tier: backend
replicas: 3
template:
metadata:
labels:
app: redis
role: slave
tier: backend
spec:
containers:
- name: slave
image: gcr.io/google_samples/gb-redisslave:v1
env:
- name: GET_HOSTS_FROM
value: "<redis EXTERNAL-IP>"
ports:
- containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
name: redis-slave
namespace: backend
labels:
app: redis
role: slave
tier: backend
spec:
ports:
- port: 6379
selector:
app: redis
role: slave
tier: backend
redis master 하나와 slave 3개 모두 배포된 것 확인
app.py
from __future__ import unicode_literals
__author__ = "Anil Saravade"
import time
import redis
from flask import Flask
import os
app = Flask(__name__)
cache = redis.Redis(host=os.getenv('REDIS_SERVER'), port=6379)
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hit():
count = get_hit_count()
return 'Hello, you have %i visitors on this page' % int(count)
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
requirements.txt
flask
redis
이제 이 앱을 yaml로 쿠버네티스에 배포하기 위해 Dockerization할 것이다.
여기선 GCP에서 지원하는 이미지 저장소인 Container Registry로 올릴 것이다.
Dockerfile
FROM python:2-alpine
MAINTAINER mhkim
WORKDIR /usr/src/app
COPY ./requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY ./app.py .
이미지 생성
docker build . -t <지정할 이미지 이름>:v1
docker tag <지정할 이미지 이름>:v1 gcr.io/<프로젝트 ID>/<지정할 이미지 이름>:v1
docker push gcr.io/<프로젝트 ID>/<지정할 이미지 이름>:v1
gcr.io 즉 Google Container Registry에 v1이라는 태그가 붙은 내 이미지가 생긴 것 확인.
front-app.yaml
apiVersion: v1
kind: Namespace
metadata:
name: frontend
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend-deployment
namespace: frontend
labels:
app: frontend
spec:
replicas: 3
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: <gcr 이미지 이름>
ports:
- containerPort: 5000
env:
- name: FLASK_APP
value: "app.py"
- name: REDIS_SERVER
value: "<redis EXTERNAL-IP>"
command: ["sh"]
args: ["-c","flask run --host=0.0.0.0"]
---
apiVersion: v1
kind: Service
metadata:
name: frontend-public
namespace: frontend
spec:
type: LoadBalancer
ports:
- port: 80
protocol: TCP
targetPort: 5000
selector:
app: frontend
여기서 gcr 이미지 이름이란 내가 생성한 이미지를 아래처럼 그대로 복사해서 가져오면 된다.
콘솔에서 확인해보면 내부, 외부 LB 두개가 생성된 것을 확인할 수 있고 외부 LB의 IP로 접속해보면 배포한 app이 잘 뜨는 것을 확인할 수 있다.
새로고침을 몇 번 누르면 2 3 4 점점 visitor의 숫자가 늘어가는 것을 볼 수 있다.
[Kubernetes Multi-Tier Architecture 참고]
https://blog.searce.com/multi-tier-application-using-multiple-kubernetes-clusters-8df4c940576e