본문 바로가기
정리

개발환경 구축하기 - 쿠버네티스

by 난파선 2022. 11. 11.

예전에 했던 서버 구축글에서 docker swam을 쿠버네티스로 바꾸던 걸 정리했던 글입니다.

 

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에 실행될 도커이미지의 설정을 작성합니다.

  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",
            .
            .
  },
  1. 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;
  }
}
  1. 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.3 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.4 ssh-key 설정

ssh-keygen -t rsa -N "" -f ./id_rsa

3.5 쿠버네티스 클러스터 생성하기

kops create cluster --state [s3:저장소이름] --zones ap-northeast-1a - -networking calico --ssh-public-key ./id_rsa.pub [도메인 이름]

3.6 노드 설정

kops edit ig master-ap-northeast-1a --name [도메인 이름] --state [s3:저장소이름]  # 마스터 노드 설정
kops edit ig nodes --name [도메인 이름] --state [s3:저장소이름] # 노드 설정\

3.7 쿠버네티스 클러스터 생성하기

kops update cluster --yes [도메인 이름] # 클러스터 생성하기
kops validate cluster --state [s3:저장소이름] # 진행사항 확인하기

3.3 쿠버네티스에 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.4 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

3.5 aws 로드밸런서&인증서 설정

외부에서 접근할 수 있게 쿠버네티스 서비스를 설정을 한다.

실서버에 배포되는 프로젝트에 경우 https을 설정해 주어야 하기 때문에 443포트를 열고 aws에서 제공하는 인증서를 등록 후에 인증서 정보를 입력한다.

apiVersion: v1
kind: Service
metadata:
  name: [서비스 이름]
  namespace: [namespace 이름]
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
    service.beta.kubernetes.io/aws-load-balancer-ssl-cert: [보안 인증서]
    service.beta.kubernetes.io/aws-load-balancer-ssl-ports: https
spec:
  type: LoadBalancer
  selector:
    app: [프로젝트 이름]
  ports:
  - name: http
    port: 80
    targetPort: 80
  - name: https
    port: 443
    targetPort: 80

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
---

'정리' 카테고리의 다른 글

PostgreSQL에서다중 속성 값 처리(Array)  (0) 2022.11.12

댓글