kind 설치 및 클러스터 배포

kind는 docker in docker로 쿠버네티스 클러스터 환경을 구성할 수 있다.

그래서 각 노드는 컨테이너로 이루어져 있다.

image 1

참고 링크 ) kind

로컬 환경에 쿠버네티스 환경을 구성하기 위해 kind 툴을 먼저 설치해 준다.

brew install kind

image 1

kubectl도 설치해 준다.

brew install kubernetes-cli

image 2

helm도 설치해 준다.

brew install helm

image 3

각종 편리한 툴도 설치해 준다.

# 툴 설치
brew install krew
brew install kube-ps1
brew install kubectx

# kubectl 출력 시 하이라이트 처리
brew install kubecolor
echo "alias kubectl=kubecolor" >> ~/.zshrc
echo "compdef kubecolor=kubectl" >> ~/.zshrc

# krew 플러그인 설치
kubectl krew install neat stren

아래 클러스터 배포용 파일을 하나 생성한다.

cat > kind-3node.yaml <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
  apiServerAddress: "$MyIP"
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 30000
    hostPort: 30000
  - containerPort: 30001
    hostPort: 30001
  - containerPort: 30002
    hostPort: 30002
  - containerPort: 30003
    hostPort: 30003
- role: worker
- role: worker
EOF
  • apiServerAddress: api 서버는 현재 내 PC ip로 지정해 준다.
  • control-plane 한 대, worker 두 대 생성해 준다.

kind create cluster --config kind-3node.yaml --name myk8s --image kindest/node:v1.30.6로 myk8s 이름으로 클러스터를 생성해 준다.

image 4

docker ps로 확인해 보면 각 노드는 컨테이너 형태로 구동되어 있으며, control plane은 외부에서 접근할 수 있는 포트로 매핑되어 있다.

image 5

kind는 별도 네트워크를 생성해서 사용하고 172.19.0.0/16 대역을 사용하고 있다

image 6

image 7

kube-ops-view도 설치해서 현재 클러스터의 컨테이너 생성 여부를 가시성있게 확인할 수 있다.

helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=**NodePort**,service.main.ports.http.nodePort=**30001** --set env.TZ="Asia/Seoul" --namespace kube-system

http://127.0.0.1:30001/#scale=1.5로 접속하면 된다

image 8

Jenkins 설정 : Plugin 설치, 자격증명 설정

Jenkins Dashboard > Jenkins 관리 > Plugins에서 플러그인 세 개를 설치해 준다 (Pipeline Stage View, Docker Pipeline, Gogs)

Jenkins 관리 > Credentials > Globals > Add Credentials에 들어가 아래 세 가지 자격증명도 설정해 준다.

image 9

Jenkins Item 생성(Pipeline)

간단하게 git checkout 진행하여 VERSION 파일에 명시된 버전과 latest 버전을 붙여서 docker hub에 이미지 푸시하는 과정이다.

pipeline {
    agent any
    environment {
        DOCKER_IMAGE = '6ain/dev-app'
    }
    stages {
        stage('Checkout') {
            steps {
                 git branch: 'main',
                 url: 'http://192.168.219.100:3000/devops/dev-app.git',  // Git에서 코드 체크아웃
                 credentialsId: 'gogs-crd'  // Credentials ID
            }
        }
        stage('Read VERSION') {
            steps {
                script {
                    // VERSION 파일 읽기
                    def version = readFile('VERSION').trim()
                    echo "Version found: ${version}"
                    // 환경 변수 설정
                    env.DOCKER_TAG = version
                }
            }
        }
        stage('Docker Build and Push') {
            steps {
                script {
                    docker.withRegistry('https://index.docker.io/v1/', 'dockerhub-crd') {
                        // DOCKER_TAG 사용
                        def appImage = docker.build("${DOCKER_IMAGE}:${DOCKER_TAG}")
                        appImage.push()
                        appImage.push("latest")
                    }
                }
            }
        }
    }
    post {
        success {
            echo "Docker image ${DOCKER_IMAGE}:${DOCKER_TAG} has been built and pushed successfully!"
        }
        failure {
            echo "Pipeline failed. Please check the logs."
        }
    }
}

처음에 credentials를 유저 쪽에 만들어서 authentication failed 에러가 발생했었다. 다시 system 쪽으로 만들어서 배포 성공하였다!

image 10

image 11

k8s Deploying an application

deployment 1, pod 2개 생성을 위한 yaml파일 하나 작성하자!

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: timeserver
spec:
  replicas: 2
  selector:
    matchLabels:
      pod: timeserver-pod
  template:
    metadata:
      labels:
        pod: timeserver-pod
    spec:
      containers:
      - name: timeserver-container
        image: docker.io/$DHUSER/dev-app:0.0.1
EOF

ImagePullBackOff 에러가 발생한다

image 12

이벤트를 확인해 보면 Failed to pull image "[docker.io/6ain/dev-app:0.0.1](http://docker.io/6ain/dev-app:0.0.1)": failed to pull and unpack image "[docker.io/6ain/dev-app:0.0.1](http://docker.io/6ain/dev-app:0.0.1)": failed to resolve reference "[docker.io/6ain/dev-app:0.0.1](http://docker.io/6ain/dev-app:0.0.1)": pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed 라는 로그로 권한 문제임을 알 수 있다.

image 13

DHUSER와 DHPASS에 계정 정보를 설정하고 secret값을 생성해 준다.

kubectl create secret docker-registry dockerhub-secret \
  --docker-server=https://index.docker.io/v1/ \
  --docker-username=$DHUSER \
  --docker-password=$DHPASS

image 14

kubectl get secrets -o yaml로 확인해 보면 인코딩된 값으로 확인할 수 있다

image 15

imagePullSecrets: 값을 추가하고 다시 배포해 보자

cat <<EOF | kubectl apply -f -
*apiVersion: apps/v1
kind: Deployment
metadata:
  name: timeserver
spec:
  replicas: 2
  selector:
    matchLabels:
      pod: timeserver-pod
  template:
    metadata:
      labels:
        pod: timeserver-pod
    spec:
      containers:
      - name: timeserver-container
        image: docker.io/$DHUSER/dev-app:0.0.1
      imagePullSecrets:
      - name: dockerhub-secret*
EOF

이제 정상적으로 컨테이너를 생성할 수 있다!

image 16

curl 테스트할 수 있는 pod를 생성해서 curl 테스트를 해 보니 정상적으로 현재 시간을 출력해 준다

image 17

pod를 하나 삭제해 보면 신규 pod가 생성되면서 ip가 변경되는 것을 확인할 수 있었다

image 18

pod의 고정 진입점을 만들어 주기 위해 service를 하나 생성해 주자

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: timeserver
spec:
  selector:
    pod: timeserver-pod
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
    nodePort: 30000
  type: NodePort
EOF

image 19

service는 도메인으로 접속 가능하고

image 20

cluster ip로도 접속 가능하다

image 21

node port를 통해 외부 접근도 가능하다

image 22

service를 통해 각각의 pod에 부하분산도 가능하다

image 23

Updating your application

버전 정보를 바꿔서 push해 보자

kubectl set image deployment timeserver timeserver-container=$DHUSER/dev-app:0.0.2 && watch -d "kubectl get deploy,ep timeserver; echo; kubectl get rs,pod"

정상적으로 0.0.2 버전으로 이미지가 교체되었다

image 24

항상 최신의 이미지를 가지고 오려면 image:latest 태그를 붙이고 imagePullPolicy: Always를 추가해 준다.

imagePullPolicy: Always를 사용함으로써 컨테이너는 캐시된 이미지를 사용하는 것이 아니라 항상 이미지저장소에서 최신 이미지를 가지고 올 수 있다.

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: timeserver
spec:
  replicas: 2
  selector:
    matchLabels:
      pod: timeserver-pod
  template:
    metadata:
      labels:
        pod: timeserver-pod
    spec:
      containers:
      - name: timeserver-container
        image: docker.io/$DHUSER/dev-app:latest
        imagePullPolicy: Always
      imagePullSecrets:
      - name: dockerhub-secret
EOF

Gogs Webhooks 설정 : Jenkins Job Trigger

Gogs Webhooks를 사용하기 위해 app.ini 파일 수정 후 컨테이너 재기동한다

gogs > data > gogs > conf > app.ini > security 부분 맨 아래에 LOCAL_NETWORK_ALLOWLIST = 내PC IP를 추가해 준다

image 25

Jenkins Item 생성(Pipeline) : item name(SCM-Pipeline)

Gogs Webhook을 사용하기 위해 젠킨스 파이프라인을 하나 생성한다.

project url은 gogs 레포 주소를 넣어주고 Secret은 위 Gogs Webhook Secret 값을 넣어준다

image 26

Build Triggers는 Build when a change is pushed to Gogs로 선택해 준다

image 27

Pipeline script from SCM으로 선택해서 해당되는 값을 기입해 준다.

image 28

로컬로 돌아가 Jenkinsfile을 생성해서 아래 대로 넣어준다.

파이프라인 과정은 위에 ci 파이프라인과 동일하다

pipeline {
    agent any
    environment {
        DOCKER_IMAGE = '6ain/dev-app' // Docker 이미지 이름
    }
    stages {
        stage('Checkout') {
            steps {
                 git branch: 'main',
                 url: 'http://192.168.219.100:3000/devops/dev-app.git',  // Git에서 코드 체크아웃
                 credentialsId: 'gogs-crd'  // Credentials ID
            }
        }
        stage('Read VERSION') {
            steps {
                script {
                    // VERSION 파일 읽기
                    def version = readFile('VERSION').trim()
                    echo "Version found: ${version}"
                    // 환경 변수 설정
                    env.DOCKER_TAG = version
                }
            }
        }
        stage('Docker Build and Push') {
            steps {
                script {
                    docker.withRegistry('https://index.docker.io/v1/', 'dockerhub-crd') {
                        // DOCKER_TAG 사용
                        def appImage = docker.build("${DOCKER_IMAGE}:${DOCKER_TAG}")
                        appImage.push()
                        appImage.push("latest")
                    }
                }
            }
        }
    }
    post {
        success {
            echo "Docker image ${DOCKER_IMAGE}:${DOCKER_TAG} has been built and pushed successfully!"
        }
        failure {
            echo "Pipeline failed. Please check the logs."
        }
    }
}
  • Jenkins 트리거 빌드 확인

    image 29

  • 도커 저장소 확인

    image 30

  • Gogs WebHook 기록 확인

    image 31

kubectl set image deployment timeserver timeserver-container=$DHUSER/dev-app:0.0.3로 이미지를 교체해서 pod에 신규 버전을 적용한다

image 32

Categories:

Updated: