
안녕하세요 일루넥스 개발팀 김형진입니다.
기존에 Spring Cloud와 Docker Swarm 으로 구축된 테스트 서버에서 Spring Cloud를 걷어내고
Kubernetes로 변경한 이야기를 할려고 합니다.
1. 젠킨스에서 도커 빌드 가능하게 설정하기
기존에 Docker Compose로 구성해서 서버에서 바로 docker 이미지를 만들었다면 이번에는 Jenkins에서 빌드를 해서 이미지를 생성하고 해당 이미지를 넥서스에 올린 후 각각의 서버에서 해당 이미지를 사용방식으로 만들려고 합니다. 가장 먼저 젠킨스에서 도커를 빌드하고 빌드된 이미지를 사내 dockerhub(Nexus)에 올릴 수 있게 해주어야 합니다. 도커로 구동된 젠킨스에서 도커를 실행하려면 docker.sock 파일을 볼륨해줘야 합니다.
sudo docker run -d -p 8080:8080 -v /home/jenkins:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock
1.1 젠킨스 백업하기
만약 기존에 사용하던 젠킨스가 있다면 해당 젠킨스에 workspace폴더를 복사해 놓은 후에 새로 설치된 젠킨스에 덮어 씌우시면 됩니다.
2. 쿠버네티스 설정 사전작업
2.1 React 빌드 및 도커 이미지 만들기
먼저 프론트쪽 배포를 위해서 리액트 빌드를 해야 합니다. 그런데 이번에는 테스트 서버에만 배포하는 게 아닌 테스트와 실섭 두개로 구분을
해서 배포를 해야 하기 때문에 배포 설정을 해주어야 합니다. 그리고 빌드된 React파일은 Nginx 아래에서 실행이 됩니다.
또한 도커 이미지를 만들 때 빌드된 (build) 폴더가 실행되는 Nginx 서버에 설정을 위해 default.conf 설정파일을 미리 프로젝트 안에 준비
를 해놓습니다. 그리고 Dockerfile에 실행될 도커이미지의 설정을 작성합니다.
2.1.1. 서버에 따른 빌드 스크립트 package.json에 설정하기
"scripts": {
.
.
"build-test": "env-cmd -f .env.test react-scripts build",
"build-dev": "env-cmd -f .env.dev react-scripts build",
"build-prod": "env-cmd -f .env.prod react-scripts build",
.
.
},
2.1.2. nginx 설정파일 만들기: conf/conf.d/default.conf 파일 만들기
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
2.1.3. Dockerfile 만들기
FROM nginx:1.13.9-alpine
EXPOSE 80
RUN rm -rf /etc/nginx/conf.d
COPY ./conf /etc/nginx
COPY ./build /usr/share/nginx/html
CMD ["nginx", "-g", "daemon off;"]
Nginx를 기반으로 설정한 conf폴더와 빌드된 React폴더를 복사해 이미지 안에 넣고 Nginx를 시작한다.
2.2 Java 빌드 및 도커 이미지 만들기
자바 프로젝트를 Maven으로 빌드를 해서 도커이미지를 만들고 실행을 시킨다.
빌드
mvn clean install -Dmaven.test.skip=true -Pprod
기존에 빌드되어 있던 파일을 지우고 빌드를 한다. 옵션으로는 테스트를 Skip하는 옵션과 프로파일을 설정했다.
Dockerfile
FROM openjdk:14-slim
EXPOSE 8081
ADD ./target/*.jar vc.jar
ENTRYPOINT ["java","-jar","/vc.jar"]
오픈 JDK를 기반으로 각각의 프로젝트에 맞는 포트를 열어주고 빌드된 jar파일을 복사해서 이미지에 넣은 후 실행을 한다.
3. 쿠버네티스 설정하기
쿠버네티스를 AWS에서 설치해서 사용할 생각이며 사용할 툴로는 Kops를 사용할 생각이다.
3.1 kops 설치하기
먼저 kops를 사용할 인스턴스(CI서버)에 kops를 설치한다.
설치에 관련된 내용은 https://kops.sigs.k8s.io/getting_started/install/ 이 사이트에 자세히 나와있다.
일단 회사 CI 서버에 경우 우분투로 구성되어 있어서 리눅스 설치 명령어를 복사해서 실행시킨다.
curl -LO https://github.com/kubernetes/kops/releases/download/$(curl -s https://api.github.com/repos/kubernetes/kops/releases/latest | grep tag_name | cut -d '"' -f 4)/kops-linux-amd64
chmod +x kops-linux-amd64
sudo mv kops-linux-amd64 /usr/local/bin/kops
3.2 AWS IAM 설정
AWS에서 IAM을 하나 설정한다. 필요한 권한으로는 아래 5가지 권한을 설정해 주면 된다.
- AmazonEC2FullAccess
- AmazonRoute53FullAccess
- AmazonS3FullAccess
- IAMFullAccess
- AmazonVPCFullAccess
3.3 AWS CLI 설치&설정
기존에 설치된 AWSCLI가 없다면 설치를 해준다.
apt install awscli # awscli 설치
# aws 설정
aws configure
Access Key: [입력]
Secret Access Key: [입력]
Default region name: ap-northeast-2 # 한국 1은 일본
Default output format: [엔터]
3.4 route53 설정
쿠버네티스 클러스터 정보가 저장될 S3 저장소를 하나 생성한다. 그리고 버킷에 버저닝을 기록하도록 설정한다.
aws s3api create-bucket --bucket [버킷이름] --create-bucket-configuration LocationConstraint=ap-northeast-1
aws s3api put-bucket-versioning --bucket [버킷이름] --versioning-configuration Status=Enabled
3.5 ssh-key 설정
ssh-keygen -t rsa -N "" -f ./id_rsa
3.6 쿠버네티스 클러스터 생성하기
kops create cluster --state [s3:저장소이름] --zones ap-northeast-1a - -networking calico --ssh-public-key ./id_rsa.pub [도메인 이름]
3.7 노드 설정
kops edit ig master-ap-northeast-1a --name [도메인 이름] --state [s3:저장소이름] # 마스터 노드 설정
kops edit ig nodes --name [도메인 이름] --state [s3:저장소이름] # 노드 설정\
3.8 쿠버네티스 클러스터 생성하기
kops update cluster --yes [도메인 이름] # 클러스터 생성하기
kops validate cluster --state [s3:저장소이름] # 진행사항 확인하기
3.9 쿠버네티스에 private docker hub 설정하기
사내에 Docker Hub가 구축되어 있다면 해당 저장소에 이미지를 가져올 수 있게 설정을 해주어야 한다.
kubectl create secret docker-registry [시크릿 이름]
--docker-server=[사내 저장소 주소]
--docker-username=[유저 이름]
--docker-password=[비밀번호]
--docker-email=[이메일]
그리고 CI 서버에서 사내 Docker Hub에 이미지를 올려야 하기 때문에 저장소 설정을 해준다.
sudo vi /etc/docker/daemon.json
{
"insecure-registries": ["저장소 주소"]
}
마지막으로 쿠버네티스 pod을 만드는 설정안에 사내 저장소에 접근할 수 있게 설정을 추가한다.
imagePullSecrets:
- name: [설정된 시크릿 이름]
3.10 health 설정
쿠버네티스에 이미지가 배포 되었을 때 잘 실행이이 되었는 지 그리고 실행 중에 정지가 되지 않았는 지 확인을 하기 위한 설정을 해야합니다. Spring에 Actuator을 설정했고 쿠버네티스 설정파일에 해당 내용 관련 코드를 추가해준다.
livenessProbe:
httpGet:
path: /actuator/info
port: 80
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 10
successThreshold: 1
readinessProbe:
httpGet:
scheme: HTTP
path: /actuator/health
port: 80
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
successThreshold: 1
4. 젠킨스에서 배포 설정하기
자바 프로젝트의 경우 프로젝트를 빌드를 하고 빌드한 파일을 바탕으로 도커이미지 파일을 생성한다.
생성된 도커이미지파일을 사내 Docker-Hub에 올리고 쿠버네티스 노드로 배포한다.
올라가는 버전의 경우에는 따로 파라미터로 받아 설정한다.
#!/bin/bash
mvn clean install -Dmaven.test.skip=true -Pdev
docker build --tag docker.effectmall.com/startup-dev:v1.${major}.${minor} ./
docker push docker.effectmall.com/startup-dev:v1.${major}.${minor}
kubectl set image deployment startup startup-con=docker.effectmall.com/startup-dev:v1.${major}.${minor} --record --namespace=invest-dev
5. 일래스틱 서치 설정해서 로그 가져오기
filebeat를 사용해서 일래스틱에 로그를 가져올 수 있게 한다.
아래 내용에 일래스틱 호스트 주소와 아이디 비밀번호를 설정해서 쿠버네티스에 등록한다.
---
apiVersion: v1
kind: ConfigMap
metadata:
name: filebeat-config
namespace: default
labels:
k8s-app: filebeat
data:
filebeat.yml: |-
filebeat.inputs:
- type: container
paths:
- /var/log/containers/*.log
processors:
- add_kubernetes_metadata:
host: ${NODE_NAME}
matchers:
- logs_path:
logs_path: "/var/log/containers/"
processors:
- add_cloud_metadata:
- add_host_metadata:
cloud.id: ${ELASTIC_CLOUD_ID}
cloud.auth: ${ELASTIC_CLOUD_AUTH}
output.elasticsearch:
hosts: ['${ELASTICSEARCH_HOST:elasticsearch}:${ELASTICSEARCH_PORT:9200}']
username: ${ELASTICSEARCH_USERNAME}
password: ${ELASTICSEARCH_PASSWORD}
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: filebeat
namespace: default
labels:
k8s-app: filebeat
spec:
selector:
matchLabels:
k8s-app: filebeat
template:
metadata:
labels:
k8s-app: filebeat
spec:
serviceAccountName: filebeat
terminationGracePeriodSeconds: 30
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
containers:
- name: filebeat
image: docker.elastic.co/beats/filebeat:7.5.2
args: [
"-c", "/etc/filebeat.yml",
"-e",
]
env:
- name: ELASTICSEARCH_HOST
value: [일래스틱 호스트 주소]
- name: ELASTICSEARCH_PORT
value: "9200"
- name: ELASTICSEARCH_USERNAME
value: elastic
- name: ELASTICSEARCH_PASSWORD
value: [비밀번호]
- name: ELASTIC_CLOUD_ID
value:
- name: ELASTIC_CLOUD_AUTH
value:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
securityContext:
runAsUser: 0
# If using Red Hat OpenShift uncomment this:
#privileged: true
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 100Mi
volumeMounts:
- name: config
mountPath: /etc/filebeat.yml
readOnly: true
subPath: filebeat.yml
- name: data
mountPath: /usr/share/filebeat/data
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: varlog
mountPath: /var/log
readOnly: true
volumes:
- name: config
configMap:
defaultMode: 0600
name: filebeat-config
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: varlog
hostPath:
path: /var/log
# data folder stores a registry of read status for all files, so we don't send everything again on a Filebeat pod restart
- name: data
hostPath:
path: /var/lib/filebeat-data
type: DirectoryOrCreate
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: filebeat
subjects:
- kind: ServiceAccount
name: filebeat
namespace: default
roleRef:
kind: ClusterRole
name: filebeat
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: filebeat
labels:
k8s-app: filebeat
rules:
- apiGroups: [""] # "" indicates the core API group
resources:
- namespaces
- pods
verbs:
- get
- watch
- list
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: filebeat
namespace: default
labels:
k8s-app: filebeat
---
마치며
여기까지 쿠버네티스로 운영서버 구축에 관한 내용이었습니다.
읽어주셔서 감사합니다.