구성 환경 🧐
도커 실습 시 실시했던 3계층 ( 프론트엔드, 백엔드, DB ) 아키텍처를 쿠버네티스로 구현해보려 한다.
각각의 서버의 Pod 는 Deployment의 ReCreate 방식을 사용하여 생성할 예정이다.
쿠버네티스는 지난 글에서 설치한 상태에서 이어서 진행한다.
🐶 DB 서버 설정하기
1. PV ( Persistent Volume ) 를 생성한다.
➡ PV는 원래 스토리지 서버를 따로 두는데, 지금은 그럴 수 없기 때문에 worker01
컴퓨터를 스토리지 서버라 생각하고 설정한다.
apiVersion: v1
kind: PersistentVolume
metadata:
name: db-pv // 원하는 이름으로 지정
spec:
capacity:
storage: 5G // 컴퓨터 사양 고려 설정
accessModes:
- ReadWriteOnce
local:
path: /mysql-vol // 원하는 경로로 지정
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- {key: kubernetes.io/hostname, operator: In, values: ['worker01']}
➡ 이때 지정한 /mysql-vol
디렉토리를 실제로 worker01
컴퓨터에서 생성해줘야 된다.
2. PVC ( Persistent Volume Claim ) 를 생성한다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: db-pvc // 원하는 이름으로 지정
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5G // 컴퓨터 사양 고려 설정
3. DB 서버용 "ConfigMap" 을 생성한다.
➡ ConfigMap 은 환경변수들을 설정하기 위한 것이라고 보면 된다.
➡ 하지만, 암호화 기능이 없어서 비밀번호 등 민감한 변수가 그대로 노출되기 때문에,
Secret을 실제론 사용한다고 한다.
apiVersion: v1
kind: ConfigMap
metadata:
name: db-config // 원하는 이름으로 지정
data:
MYSQL_ROOT_PASSWORD: qwer1234 // root 계정 원하는 패스워드 입력
MYSQL_DATABASE: lonua // 초기 생성 데이터베이스 지정 (백엔드 서버와 연동해주기 위해 설정)
4. DB 서버 Deployment 를 생성한다.
➡ Deployment에는 ReplicaSet 이 포함되어 있어서, 주로 사용한다고 한다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: db-deployment // 원하는 이름으로 지정
spec:
replicas: 1 // 초기에 생성할 Pod 개수 입력
strategy:
type: Recreate
revisionHistoryLimit: 1 // 버전 업데이트 시 레플리카 셋을 지정한 개수 만큼 남겨놓도록 하는 옵션 설정
selector:
matchLabels:
type: mysql // 바로 아래의 "labels" 와 서비스 생성 시에도 동일하게 설정해줄 라벨
template:
metadata:
labels:
type: mysql // 바로 위에서 지정한 라벨명과 동일하게 설정
spec:
nodeSelector:
kubernetes.io/hostname: worker01 // 원래는 필요없으나, 스토리지 서버가 없어서 설정
containers:
- name: mysql
image: mysql:latest // 도커 허브의 mysql 이미지
envFrom:
- configMapRef:
name: db-config // 위에서 생성한 ConfigMap 이름
volumeMounts:
- name: db-vol // 원하는 이름으로 지정하나 아래와 동일하게 맞춰줘야함
mountPath: /var/lib/mysql // mysql에서 데이터를 다루는 경로
terminationGracePeriodSeconds: 5
volumes:
- name: db-vol // 원하는 이름으로 지정하나 위와 동일하게 맞춰줘야함
persistentVolumeClaim:
claimName: db-pvc // 2번에서 생성한 PVC 이름
아래처럼 정상적으로 Deployment가 생성되었고,
생성된 Pod의 bash 창으로 mysql 서버에 로그인해서 데이터베이스를 확인해보면 지정한 "lonua" 데이터베이스가 생성되어 있는 것을 확인할 수 있다.
5. DB 서버에 대한 서비스를 생성한다.
➡ 백엔드 서버에서 DB 서버로 접근하기 위해 서비스를 생성해준다.
➡ 이때, Pod 간의 통신이므로 서비스 유형은 기본값인 Cluster Ip
로 구성한다.
apiVersion: v1
kind: Service
metadata:
name: db-svc // 원하는 이름으로 지정
spec:
selector:
type: mysql // 위에서 Deployment 생성시 지정한 라벨명과 동일하게 적어준다
ports:
- port: 3306
targetPort: 3306
🐱 백엔드 서버 설정하기
1. 백엔드 서버용 "ConfigMap" 을 생성한다.
apiVersion: v1
kind: ConfigMap
metadata:
name: backend-config
data:
APP_PASSWORD:
AWS_S3_ACCESS_KEY:
AWS_S3_SECRET_KEY:
BRAND_BUCKET:
CLIENT_ID:
EXPIRED_TIME:
JWT_SECRET_KEY:
MAIL_SENDER:
MASTER: root
MASTER_PW: qwer1234
// 여기서 DB 주소는 Pod 간의 통신이므로 위에서 생성한 DB 서비스의 이름으로 설정해줄 수 있다.
MASTER_URL: jdbc:mysql://db-svc:3306/lonua
PORTONE_KEY:
PORTONE_SECRETKEY:
PRODUCT_BUCKET:
PRODUCT_INTROD_BUCKET:
REGION:
REVIEW_BUCKET:
SLAVE: root
SLAVE_PW: qwer1234
SLAVE_URL: jdbc:mysql://db-svc:3306/lonua
➡ 환경변수들을 적어줄때, 숫자로만 되어있는 값은 " "
를 해줘야된다. 예를 들어,
EXPIRED_TIME 을 300000으로 설정하고 싶다면 "300000"
으로 적어야 오류가 안난다.
2. 배포할 백엔드 서버 이미지를 생성한다.
3. 생성한 이미지를 도커 허브에 푸쉬한다.
➡ 이미지를 생성하고 푸쉬하는 방법은 2가지 방법이 있을 것 같다.
1) 컴퓨터 사양때문에 쿠버네티스와 도커 데스크탑을 동시에 운용을 못한다면,
쿠버네티스 master 컴퓨터로 jar 파일을 옮기고, Dockerfile을 생성하여 명령어로
이미지 생성 및 푸쉬하는 방법
2) 도커 데스크탑을 이용하여 이미지 생성 및 푸쉬하는 방법
➡ 도커 데스크탑을 이용하면 간단하겠지만, 이미지 생성하는 것을 연습하는 차원에서
1번 방식으로 해보겠다.
1) 먼저 Filezilla 프로그램을 이용하여 jar 파일을 master 리눅스 컴퓨터로 옮긴다.
2) 옮긴 경로에서 도커파일을 생성한다 : vi Dockerfile
FROM openjdk:11-jdk-slim-stretch
COPY ./lonua-0.0.1-SNAPSHOT.jar lonua-0.0.1-SNAPSHOT.jar
CMD ["java", "-jar", "/lonua-0.0.1-SNAPSHOT.jar"]
3) 이미지를 생성한다 : docker build --tag [도커허브 계정명]/[레포지토리명]:버전 .
4) 생성한 이미지를 도커허브에 푸쉬한다
➡ docker push [도커허브 계정명]/[레포지토리명]:버전
➡ 푸쉬하기 위해서는 docker login 을 해야지만 가능하다.
➡ 만약, 이메일 계정을 사용하는데 도커 로그인이 안된다면 도커 허브 웹사이트에서
로그인 후, My Account 클릭 - Change Password로 비밀번호를 새로 설정해줘야
가능하다.
4. 백엔드 서버 Deployment 를 생성한다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-deployment
spec:
replicas: 1
strategy:
type: Recreate
revisionHistoryLimit: 1
selector:
matchLabels:
type: backend
template:
metadata:
labels:
type: backend
spec:
containers:
- name: backend
image: [위에서 푸쉬한 이미지]
envFrom:
- configMapRef:
name: backend-config
정상적으로 실행이 되었고, DB의 배쉬창으로 조회 해보니 데이터도 잘 들어가있었다.
한글이 지원이 안되다 보니 데이터가 ? 로 출력되긴 하는데, 프론트엔드와 연결했을때 잘 불러와지는지 설정을 마치고 테스트 해보겠다.
5. 백엔드 서버 서비스를 생성한다. ( DB와 마찬가지로 Cluster IP 로 생성 )
apiVersion: v1
kind: Service
metadata:
name: backend-svc
spec:
selector:
type: backend
ports:
- port: 8080
targetPort: 8080
Cluster IP 로 설정하는 사유는, 프론트엔드 서버에서 요청을 하면, 리버스 프록시 설정으로
백엔드 서버 Pod 로 요청을 보내줘 Pod간의 통신이 되기 때문이다.
여기까지 하면, 백엔드 서버의 설정이 끝난다.
🐹 프론트엔드 서버 설정하기
1. Vue 프로젝트에서 배포하기 위해 빌드 : npm run build
➡ 이때, 백엔드 서버를 호출하는 주소는
http://[아래에서 생성된 로드밸런서 서비스의 엔드포인트 IP]:80/api
로 설정하였다.
➡ 이부분은 순서를 고려해봐야될 것 같다. 여기서 설정되는 프론트서버의 IP는
아래에서 서비스를 생성할때 알 수 있다. 하지만, Deployment의 경우 일단 만들어
놓고, 나중에 백엔드 호출 주소를 수정 후 이미지를 새로 생성해서, 리소스 편집으로
이미지 버전만 바꿔주면 적용되긴 한다.
2. 생성된 dist 폴더안의 파일들을 FileZilla를 이용하여 미리 생성해둔 /frontend 경로로 옮겨준다.
3. nginx 설정파일을 생성한다 : vi default.conf
server {
listen 80;
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
location /api {
rewrite ^/api(.*)$ $1 break;
proxy_pass http://backend-svc:8080; // 위에서 생성한 백엔드 서버 서비스 이름
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
}
location / {
alias /usr/share/nginx/html/;
try_files $uri $uri/ /index.html;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
4. 도커파일을 생성한다 : vi Dockerfile
FROM nginx:latest
ADD ./css /usr/share/nginx/html/css
ADD ./fonts /usr/share/nginx/html/fonts
ADD ./img /usr/share/nginx/html/img
ADD ./js /usr/share/nginx/html/js
ADD ./styles.css /usr/share/nginx/html/styles.css
ADD ./logo.png /usr/share/nginx/html/logo.png
RUN rm -rf /usr/share/nginx/html/index.html
ADD ./index.html /usr/share/nginx/html/index.html
RUN rm -rf /etc/nginx/conf.d/default.conf
ADD ./default.conf /etc/nginx/conf.d/default.conf
CMD ["nginx", "-g", "daemon off;"]
5. 이미지를 생성한다 : docker build --tag [도커허브 계정명]/[레포지토리명]:버전 .
6. 생성한 이미지를 도커 허브에 푸쉬한다.
➡ docker push [도커허브 계정명]/[레포지토리명]:버전
7. 프론트엔드 서버 Deployment 생성한다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend-deployment
spec:
replicas: 1
strategy:
type: Recreate
revisionHistoryLimit: 1
selector:
matchLabels:
type: frontend
template:
metadata:
labels:
type: frontend
spec:
containers:
- name: frontend
image: [위에서 푸쉬한 이미지]
8. 프론트엔드 서버 서비스를 생성한다. ( 운영을 위한 것으로 LoadBalencer 로 생성 )
➡ 개발 및 테스트 목적으로 서비스를 생성할때는 NodePort
유형으로 서비스를
생성했었는데, 이제는 운영을 위한 목적이라 생각하고 LoadBalencer
유형으로
생성해보겠다.
LoadBalencer 설정을 위한 사전 설치 : 무료로 사용 가능한 MetalLB
이용
➡ 설치 가이드 바로가기 / 가이드에 나와있는 순서대로 진행하면 된다.
1) 현재 모드 확인
kubectl get configmap kube-proxy -n kube-system -o yaml | \
grep strictARP
➡ strictARP: false 라고 출력됨
2) 아래를 실행하여 true로 변경
kubectl get configmap kube-proxy -n kube-system -o yaml | \
sed -e "s/strictARP: false/strictARP: true/" | \
kubectl apply -f - -n kube-system
➡ 정상적으로 수행되면 아래와 같이 출력된다.
Warning: resource configmaps/kube-proxy is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
configmap/kube-proxy configured
3) MetalLB 설치
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.11.0/manifests/namespace.yaml
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.11.0/manifests/metallb.yaml
➡ 정상설치 확인 : kubectl get pod -n metallb-system
실행 시 전부 "Running" 상태
➡ 만약, 시간이 지나도 계속 아래 사진과 같이 출력된다면
kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)"
4) 설정파일 생성 : vi metallb_config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
address-pools:
- name: default
protocol: layer2
addresses:
- 77.77.77.50-77.77.77.90 // master와 worker01/02 에서 쓰는 IP 대역 피해서 지정
5) 설정한 내용 적용 : kubectl apply -f metallb_config.yaml
apiVersion: v1
kind: Service
metadata:
name: frontend-svc
spec:
selector:
type: frontend
ports:
- port: 80
targetPort: 80
type: LoadBalancer
➡ 로드밸런서로 서비스를 생성하면 아래처럼 외부 엔드포인트가 설정되어있다.
➡ 그럼 해당 엔드포인트로 접속해보면, 프론트엔드 서버가 정상적으로 접속이 될 것이고
아래처럼 DB에서 상품 데이터를 잘 불러오고 있는 것을 확인할 수 있다.
🐮 Metrics Server 설치하기
마지막으로, CPU 와 메모리 사용량에 따라 자동으로 서버를 늘려줄 수 있도록 설정하는 내용이다.
일단, 지금은 아래처럼 CPU 와 메모리가 아무것도 표시되어 있지 않은 상태다.
이제 Metrics Server를 설치해보겠다.
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.6.3/components.yaml
kube-system
네임스페이스로 들어가서 파드를 보면 metrice-server 파드가 있을 것이다. ( 디플로이먼트도 생겨있을 것이다. )- '--kubelet-insecure-tls'
오늘은 Deployment 의 여러가지 배포 방식 중 ReCreate 를 이용한 방식을 실습해봤다. 하지만 ReCreate 방식은 버전 수정 또는 업그레이드 시 기존 Pod가 삭제되고, 새로 생성되는 동안 서비스가 잠시 중단되는 문제가 있다.
따라서, 이것을 해결하여 무중단 배포를 하기 위한 실습이 이어질 예정이다.
실습 진행 간 발생한 오류 내용 정리 ✍
Failed to create pod sandbox: rpc error: code = Unknown desc = [failed to set up sandbox container "35d064b6feedd643f61af4fb2ae7ff7582b003bb32bf16ffbe7265eb80fc98bf" network for pod "deployment-1-56b547b87f-x8tc6": networkPlugin cni failed to set up pod "deployment-1-56b547b87f-x8tc6_kube-public" network: error getting ClusterInformation: connection is unauthorized: Unauthorized, failed to clean up sandbox container "35d064b6feedd643f61af4fb2ae7ff7582b003bb32bf16ffbe7265eb80fc98bf" network for pod "deployment-1-56b547b87f-x8tc6": networkPlugin cni failed to teardown pod "deployment-1-56b547b87f-x8tc6_kube-public" network: error getting ClusterInformation: connection