WellSpring

3주차 - Jenkins CI/ArgoCD + K8S 본문

CICD

3주차 - Jenkins CI/ArgoCD + K8S

daniel00324 2024. 12. 16. 21:47

※ 본 게재 글은 gasida님의 '약식 CI/CD' 강의내용과 실습예제 및 GitHub, ArgoCD 관련 공식문서 및 Blog 등을 참고하여 작성하였습니다.

 


☞ 과정목표 : 이번 실습과정에서는 Jenkins 와 Argo CD의 연계를 통해 K8S 의 배포의 자동화까지 구현하는 과정을 따라하며 이해할 수 있다. 

 

[ 각 Tool 에 대한 Simple Definition ]

 

1. Jenkins

소프트웨어 빌드, 테스트, 제공 또는 배포와 관련된 모든 종류의 작업을 자동화하는 데 사용할 수 있는 독립형 오픈 소스 자동화 서버이다.

 

 개발 프로세스에 대한 CICD 파이프 라인을 통해 지속적인 Feedback 과 가시성을 제공하여 Team의 협업 프로세스에서 효율적으로 버전관리와 배포를 할 수 있도록 도와준다.

 

2. Argo CD

ArgoCD는 쿠버네티스를 위한 CD 툴이며 GitOps 방식으로 관리되는 manifest(yaml) 파일의 변경사항을 감시하여, 현재 배포된 상태를 Git에 정의해둔 Manfiest 상태와 동일하게 유지 시켜주는 역할을 수행한다.

 

 Argo CD는 지정된 대상 환경에서 원하는 애플리케이션 상태의 배포를 자동화합니다. 애플리케이션 배포는 Git 커밋에서 브랜치, 태그 또는 특정 버전의 매니페스트에 고정된 업데이트를 추적할 수 있습니다.

 

[ More ... ]

더보기

[ 아키텍쳐 ] - Link

[ 출처 : argocd 공식 홈페이지 ]

 

[ Argo CD의 특징 ]

  • 지정된 대상 환경에 대한 애플리케이션의 자동 배포
  • 다양한 구성 관리/템플릿 도구 지원(Kustomize, Helm, Jsonnet, 일반 YAML)
  • 여러 클러스터를 관리하고 배포할 수 있는 기능
  • SSO 통합(OIDC, OAuth2, LDAP, SAML 2.0, GitHub, GitLab, Microsoft, LinkedIn)
  • 권한 부여를 위한 다중 테넌시 및 RBAC 정책
  • Git 저장소에 커밋된 모든 애플리케이션 구성에 대한 롤백/롤어디서나 가능
  • 애플리케이션 리소스의 상태 분석
  • 자동화된 구성 드리프트 감지 및 시각화
  • 애플리케이션을 원하는 상태로 자동 또는 수동으로 동기화
  • 애플리케이션 활동을 실시간으로 볼 수 있는 웹 UI
  • 자동화 및 CI 통합을 위한 CLI
  • Webhook 통합(GitHub, BitBucket, GitLab)
  • 자동화를 위한 액세스 토큰
  • 복잡한 애플리케이션 롤아웃(예: 블루/그린 및 카나리아 업그레이드)을 지원하기 위한 PreSync, Sync, PostSync 후크
  • 애플리케이션 이벤트 및 API 호출에 대한 감사 추적
  • 프로메테우스 메트릭
  • Git에서 helm 매개변수를 재정의하기 위한 매개변수 재정의

 


0. 실습환경 구성

컨테이터 2대(Jenkins, gogs) : 호스트 OS 포트 노출(expose)로 접속 및 사용 

    - 참고 : 1주차 환경구성

더보기

Step1. Jenkins, gogs 컨테이너 기동

# 작업 디렉토리 생성 후 이동
mkdir cicd-labs
cd cicd-labs

# 
cat <<EOT > docker-compose.yaml
services:

  jenkins:
    container_name: jenkins
    image: jenkins/jenkins
    restart: unless-stopped
    networks:
      - cicd-network
    ports:
      - "8080:8080"
      - "50000:50000"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - jenkins_home:/var/jenkins_home

  gogs:
    container_name: gogs
    image: gogs/gogs
    restart: unless-stopped
    networks:
      - cicd-network
    ports:
      - "10022:22"
      - "3000:3000"
    volumes:
      - gogs-data:/data

volumes:
  jenkins_home:
  gogs-data:

networks:
  cicd-network:
    driver: bridge
EOT


# 배포
docker compose up -d
docker compose ps

# 기본 정보 확인
for i in gogs jenkins ; do echo ">> container : $i <<"; docker compose exec $i sh -c "whoami && pwd"; echo; done

# 도커를 이용하여 각 컨테이너로 접속
docker compose exec jenkins bash
exit

docker compose exec gogs bash
exit

 

Step2. Jenkins 컨테이너 초기 설정

# Jenkins 초기 암호 확인
docker compose exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
09a21116f3ce4f27a0ede79372febfb1

# Jenkins 웹 접속 주소 확인 : 계정 / 암호 입력 >> admin / qwe123
open "http://127.0.0.1:8080" # macOS
웹 브라우저에서 http://127.0.0.1:8080 접속 # Windows

# (참고) 로그 확인 : 플러그인 설치 과정 확인
docker compose logs jenkins -f

  

Step3. Jenkins URL 설정 : 각자 자신의 PC IP를 입력한다.

 


(참고) 실습 완료 후 해당 컨테이너 중지 상태로 둘 경우 → 재부팅 및 이후에 다시 실습을 위해 컨테이너 시작 시

# 실습 완료 후 해당 컨테이너 중지 상태로 둘 경우
docker compose stop
docker compose ps
docker compose ps -a

# mac 재부팅 및 이후에 다시 실습을 위해 컨테이너 시작 시
docker compose start
docker compose ps

 

(참고) 특정 컨테이너만 삭제 후 다시 초기화 상태로 기동 시

# gogs : 볼륨까지 삭제
docker compose down gogs -v
docker compose up gogs -d

# jenkins : 볼륨까지 삭제
docker compose down jenkins -v
docker compose up jenkins -d

[ 실행 결과 - 한 눈에 보기 ] 

▶ Jenkins 컨테이너에서 호스트에 도커 데몬 사용 설정 (Docker-out-of-Docker)

더보기
# Jenkins 컨테이너 내부에 도커 실행 파일 설치
docker compose exec --privileged -u root jenkins bash
-----------------------------------------------------
id

curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update && apt install docker-ce-cli curl tree jq yq -y

docker info
docker ps
which docker

# Jenkins 컨테이너 내부에서 root가 아닌 jenkins 유저도 docker를 실행할 수 있도록 권한을 부여
groupadd -g 2000 -f docker
chgrp docker /var/run/docker.sock
ls -l /var/run/docker.sock
usermod -aG docker jenkins
cat /etc/group | grep docker

exit
--------------------------------------------

# jenkins item 실행 시 docker 명령 실행 권한 에러 발생 : Jenkins 컨테이너 재기동으로 위 설정 내용을 Jenkins app 에도 적용 필요
docker compose restart jenkins
sudo docker compose restart jenkins  # Windows 경우 이후부터 sudo 붙여서 실행하자

# jenkins user로 docker 명령 실행 확인
docker compose exec jenkins id
docker compose exec jenkins docker info
docker compose exec jenkins docker ps

Gogs 컨테이너 초기 설정 : Repo(Private) - dev-app , ops-deploy

더보기

Step1. 초기 웹 접속

# 초기 설정 웹 접속
open "http://127.0.0.1:3000/install" # macOS
웹 브라우저에서 http://127.0.0.1:3000/install 접속 # Windows

 

Step2. 초기 설정 - 세부 항목 선택

  • 데이터베이스 유형 : SQLite3
  • 애플리케이션 URL : http://<각자 자신의 PC IP>:3000/
  • 기본 브랜치 : main
  • 관리자 계정 설정 클릭 : 이름(계정명 - 닉네임 사용 devops), 비밀번호(계정암호 qwe123), 이메일 입력

Gogs 설치하기 클릭 ⇒ 관리자 계정으로 로그인 후 접속

더보기
  • 로그인 후 → Your Settings → Applications : Generate New Token 클릭 - Token Name(devops) ⇒ Generate Token 클릭 : 메모해두기!

 

 

  • New Repository
    • Repository Name : dev-app
    • Visibility : (Check) This repository is Private
    • .gitignore : Python
    • Readme : Default → (Check) initialize this repository with selected files and template
    ⇒ Create Repository 클릭 : Repo 주소 확인
  • New Repository
    • Repository Name : ops-deploy
    • Visibility : (Check) This repository is Private
    • .gitignore : Python
    • Readme : Default → (Check) initialize this repository with selected files and template
    ⇒ Create Repository 클릭 : Repo 주소 확인
## 개인 주소 확인 및 복사
http://192.168.56.1:3000/devops/ops-deploy.git
http://192.168.56.1:3000/devops/dev-app.git

[ 실행 결과 - 한 눈에 보기 ]

 

Step1. 2개의 Private Repository 생성

 

Step2. Repository 주소 복사

 

Gogs 실습을 위한 저장소 설정 : 호스트에서 직접 git 작업

더보기
# (옵션) GIT 인증 정보 초기화
git credential-cache exit

#
git clone <각자 Gogs dev-app repo 주소>
git clone http://192.168.254.124:3000/devops/dev-app.git
Cloning into 'dev-app'...
Username for 'http://192.168.254.124:3000': devops  # Gogs 계정명
Password for 'http://devops@192.168.254.124:3000': <토큰> # 혹은 계정암호
...

#
cd dev-app

#
git config user.name "devops"
git config user.email "a@a.com"
git config init.defaultBranch main
git config credential.helper store

#
git branch
git remote -v

# server.py 파일 작성
cat > server.py <<EOF
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
from datetime import datetime
import socket

class RequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/plain')
        self.end_headers()
        
        now = datetime.now()
        hostname = socket.gethostname()
        response_string = now.strftime("The time is %-I:%M:%S %p, VERSION 0.0.1\n")
        response_string += f"Server hostname: {hostname}\n"
        self.wfile.write(bytes(response_string, "utf-8")) 

def startServer():
    try:
        server = ThreadingHTTPServer(('', 80), RequestHandler)
        print("Listening on " + ":".join(map(str, server.server_address)))
        server.serve_forever()
    except KeyboardInterrupt:
        server.shutdown()

if __name__ == "__main__":
    startServer()
EOF


# Dockerfile 생성
cat > Dockerfile <<EOF
FROM python:3.12
ENV PYTHONUNBUFFERED 1
COPY . /app
WORKDIR /app 
CMD python3 server.py
EOF


# VERSION 파일 생성
echo "0.0.1" > VERSION

#
git status
git add .
git commit -m "Add dev-app"
git push -u origin main
...
  • Gogs Repo 에서 확인

[ 실행 결과 - 한 눈에 보기 ]

 

 

▶ 자신의 도커 허브 계정에 dev-app (Private) repo 생성, (옵션) Token 발급

더보기

자신의 도커 허브에 Token 발급

 

1. 계정 → Account settings

 2. Security → Personal access tokens

 

3. Generate new token : 만료일(편한대로), Access permissions(Read, Write, Delete)  

 

4. 발급된 Token 메모

 

 

 


1. Jenkins CI + K8S(Kind)

kind 소개 : ‘도커 IN 도커 docker in docker’로 쿠버네티스 클러스터 환경을 구성 - Link

더보기

> 소개 : ‘도커 IN 도커 docker in docker’로 쿠버네티스 클러스터 환경을 구성 - Link

o kind or kubernetes in docker is a suite of tooling for local Kubernetes “clusters” where each “node” is a Docker container

o kind is targeted at testing Kubernetes , kind supports multi-node (including HA) clusters

o kind uses kubeadm to configure cluster nodes.

 

[ 그림 출처: https://kind.sigs.k8s.io/ ]

 

kind 설치 : Windows 사용자 기준  (KANS - 2주차 글 참고 : https://daniel00324.tistory.com/4#toc1 )

 

Step0. docker Desktop 설치 - Link

  • 실습 환경 : Kind 사용하는 도커 엔진 리소스에 최소 vCPU 4, Memory 8GB 할당을 권고 - 링크

Step1. VirtualBox 설치 : 6.x 버전 이상 설치(현재 최신 버전 7.0.20) - https://www.virtualbox.org/wiki/Downloads

더보기
D:\Vagrant-Lab\kind> "C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" --version
7.0.20r163906

Step2. Vagrant 설치 : 윈도우용 설치 - https://developer.hashicorp.com/vagrant/install?product_intent=vagrant

더보기
D:\Vagrant-Lab\kind>vagrant version
Installed Version: 2.4.1
Latest Version: 2.4.1

[ vagrant 및 docker 호환성 이슈 해소를 위해 최신 버전으로 업그레이드-재설치 진행 ]

Step3. Vagrant 로 ubuntu(kind 등 실습 툴 설치) 가상 머신 배포

   ☞ 윈도우 터미널창 신규 열기(관리자 권한으로 열기)
  - vagrant 배포 시 여러 파일들이 생성되므로 관리하기 편하게 (아무) 폴더 생성 후 해당 폴더에서 아래 명령어를 실행하자
  > (참고) Vagrantfile : ubuntu 22.04 , vCPU 4 , Mem 8 GiB ⇒ vCPU는 8로 수정 사용 권장, 단 호스트 PC에 PC Spec 고려

 

[ Vagrantfile 상세 내역 - vm.box  호환 버전으로 업데이트 ] - Ref. Link

더보기
# Base Image : https://portal.cloud.hashicorp.com/vagrant/discover?query=ubuntu%2Fjammy64

Vagrant.configure("2") do |config|
    config.vm.box = "ubuntu/jammy64"
    config.vm.box_version = "20240823.0.1"
    config.vm.hostname = "kind"
    config.vm.synced_folder "./", "/vagrant", disabled: true
    config.vm.provision "shell", path: "init_cfg.sh"
    config.vm.provider "virtualbox" do |vb1|
      vb1.customize ["modifyvm", :id, "--nested-hw-virt", "on"]
      vb1.memory = 8192
      vb1.cpus = 4
      vb1.name = "kind"
      vb1.linked_clone = true
    end
    config.vm.network "private_network", ip: "192.168.50.10"
    config.vm.network "forwarded_port", guest: 22, host: 60000, auto_correct: true, id: "ssh"
  end

 

☞ 기 검증된 vagrantfile :

Vagrantfile
0.00MB

☞ Box Image 찾기 :

[ https://developer.hashicorp.com/vagrant/vagrant-cloud/boxes/using ]

 

[ 설치 진행 ]  * 파일 다운로드 이후, 명령은 CMD (어드민 권한) 으로 수행!! (권장)

더보기
# Ubuntu 배포 관련 Vagrantfile, init_cfg.sh 파일 다운로드
curl -O https://raw.githubusercontent.com/gasida/KANS/main/kind/Vagrantfile
curl -O https://raw.githubusercontent.com/gasida/KANS/main/kind/init_cfg.sh

# vagrant 로 ubuntu 가상머신 1대 프로비저닝
vagrant up
... # 설치 스크립트 init_cfg.sh 실행되어 다소 시간 소요

# vagrant 상태 확인
vagrant status

# Ubuntu SSH 접속
vagrant ssh
-----------
# 기본 정보 확인
whoami
hostnamectl
htop
ip -c a

# 툴 설치 확인
docker info
kind --version
kubectl version --client=true
helm version

# 외부 통신 확인
ping -c 3 8.8.8.8
curl ipinfo.io

# 실습 완료 후 전원 off
poweroff
-----------

# vagrant 상태 확인
vagrant status

# 추후 다시 실습 시 Ubuntu (재)시작 후 Ubuntu SSH 접속
vagrant reload
vagrant ssh

[ 실행 결과 - 한 눈에 보기 ]

[ 참고 - init_config.sh 상세 ]

더보기
#!/usr/bin/env bash

echo "[TASK 0] Setting SSH with Root"
printf "qwe123\nqwe123\n" | passwd >/dev/null 2>&1
sed -i "s/^#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config
sed -i "s/^PasswordAuthentication no/PasswordAuthentication yes/g" /etc/ssh/sshd_config.d/60-cloudimg-settings.conf
systemctl restart sshd  >/dev/null 2>&1

echo "[TASK 1] Profile & Bashrc"
echo 'alias vi=vim' >> /etc/profile
echo "sudo su -" >> .bashrc
ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime

echo "[TASK 2] Disable AppArmor"
systemctl stop ufw && systemctl disable ufw >/dev/null 2>&1
systemctl stop apparmor && systemctl disable apparmor >/dev/null 2>&1

echo "[TASK 3] Install Packages"
apt update -qq >/dev/null 2>&1
apt-get install bridge-utils net-tools jq tree unzip kubecolor -y -qq >/dev/null 2>&1

echo "[TASK 4] Install Kind"
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.24.0/kind-linux-amd64 >/dev/null 2>&1
chmod +x ./kind
mv ./kind /usr/bin

echo "[TASK 5] Install Docker Engine"
curl -fsSL https://get.docker.com | sh >/dev/null 2>&1

echo "[TASK 6] Install kubectl"
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" >/dev/null 2>&1
install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl

echo "[TASK 7] Install Helm"
curl -s https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash >/dev/null 2>&1

echo "[TASK 8] Source the completion"
source <(kubectl completion bash)
echo 'source <(kubectl completion bash)' >> /etc/profile

echo "[TASK 9] Alias kubectl to k"
echo 'alias k=kubectl' >> /etc/profile
echo 'complete -F __start_kubectl k' >> /etc/profile
echo 'alias kubectl=kubecolor' >> /etc/profile
echo 'compdef kubecolor=kubectl' >> /etc/profile

echo "[TASK 10] Install Kubectx & Kubens"
git clone https://github.com/ahmetb/kubectx /opt/kubectx >/dev/null 2>&1
ln -s /opt/kubectx/kubens /usr/local/bin/kubens
ln -s /opt/kubectx/kubectx /usr/local/bin/kubectx

echo "[TASK 11] Install Kubeps & Setting PS1"
git clone https://github.com/jonmosco/kube-ps1.git /root/kube-ps1 >/dev/null 2>&1
cat <<"EOT" >> ~/.bash_profile
source /root/kube-ps1/kube-ps1.sh
KUBE_PS1_SYMBOL_ENABLE=true
function get_cluster_short() {
  echo "$1" | cut -d . -f1
}
KUBE_PS1_CLUSTER_FUNCTION=get_cluster_short
KUBE_PS1_SUFFIX=') '
PS1='$(kube_ps1)'$PS1
EOT

echo "[TASK 12] To increase Resource limits"
# cat /proc/sys/fs/inotify/max_user_watches >> 8192
# cat /proc/sys/fs/inotify/max_user_instances >> 128
sysctl fs.inotify.max_user_watches=524288 >/dev/null 2>&1
sysctl fs.inotify.max_user_instances=512 >/dev/null 2>&1
echo 'fs.inotify.max_user_watches=524288' > /etc/sysctl.d/99-kind.conf
echo 'fs.inotify.max_user_instances=512'  > /etc/sysctl.d/99-kind.conf
sysctl -p >/dev/null 2>&1
sysctl --system >/dev/null 2>&1

 

[ Tip & Tips - Vagrant 통한 Kind 설치 시, 발생 에러 Trouble-shooting ]

더보기

☞ Virtual Box 및 Vagrant 연계, Kind 설치 시 하기와 같은 권한에러가 발생할 수 있다. 

    ( ※ 해당 옵션 설정 시, WSL 사용 불가함 !! )

    hypervisor 관련 옵션 조정으로 문제 해결할 수 있다!!  ( 중요 : CMD 는 관리자 권한으로 !! )

   > bcedit /set hypervisorlauntype off   >  컴퓨터 재기동

 


☞ docker /WSL2 와 Virtual Box - vagrant 동시에 사용하기 위해서는

    하기와 같이 Virtual Box 최신버전 (7.1.4 r ~) 설치가 필요하다. 

 > bcedit /set hypervisorlauntype auto   >  컴퓨터 재기동

 

[ Tip & Tips - Vagrant 기본 명령어 ]

더보기
<< 명령어 설명 >> 

 

vagrant up vm을 만들어주고 실행시켜준다. 그리고 이미 설치된 상태에서 vagrant up 하면 실행만 시켜준다.
vagrant status 상태를 확인 가능
vagrant halt 전원을 꺼준다
vagrant -h 커맨드들의 용도나 사용법 확인 가능
vagarnt ssh 가상컴퓨터 내부로 접속 가능
vagrant destroy 가상 컴퓨터를 지워주게 된다.

▶ 추가 Tool 설치

더보기

1) krew 설치 - 참조 : 링크

☞  git 설치여부 확인 (미설치 시, 설치해 준다.)

git version
# 설치
apt install git-all

  

☞  krew 설치

(
  set -x; cd "$(mktemp -d)" &&
  OS="$(uname | tr '[:upper:]' '[:lower:]')" &&
  ARCH="$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2/' -e 's/aarch64$/arm64/')" &&
  KREW="krew-${OS}_${ARCH}" &&
  curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/${KREW}.tar.gz" &&
  tar zxvf "${KREW}.tar.gz" &&
  ./"${KREW}" install krew
)

 

☞  PATH를 추가한다. krew 명령어를 자주 사용할 것 같다면 .bashrc (.bash_profile) 등의 파일에 추가한다.

export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"

  

☞  정상적으로 설치되었는지 확인한다.

kubectl krew

  

 2) krew 플러그인 ( neat / stern ) 설치 ( 참고 : Link )

kubectl krew install neat stern

 

 

kind 로 k8s 배포  ( ** 환경변수는 .bash_profile 에 넣어둔다!! )

더보기
# 클러스터 배포 전 확인
docker ps

# 방안1 : 환경변수 지정
export KUBECONFIG=$PWD/kubeconfig

# Create a cluster with kind
MyIP=<각자 자신의 PC IP>
MyIP=192.168.254.124

cd ..
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
kind create cluster --config kind-3node.yaml --name myk8s --image kindest/node:v1.30.6

# 확인
kind get nodes --name myk8s
kubens default

# kind 는 별도 도커 네트워크 생성 후 사용 : 기본값 172.18.0.0/16
docker network ls
docker inspect kind | jq

# k8s api 주소 확인 : 어떻게 로컬에서 접속이 되는 걸까?
kubectl cluster-info

# 노드 정보 확인 : CRI 는 containerd 사용
kubectl get node -o wide

# 파드 정보 확인 : CNI 는 kindnet 사용
kubectl get pod -A -o wide

# 네임스페이스 확인 >> 도커 컨테이너에서 배운 네임스페이스와 다릅니다!
kubectl get namespaces

# 컨트롤플레인/워커 노드(컨테이너) 확인 : 도커 컨테이너 이름은 myk8s-control-plane , myk8s-worker/worker-2 임을 확인
docker ps
docker images

# 디버그용 내용 출력에 ~/.kube/config 권한 인증 로드
kubectl get pod -v6

# kube config 파일 확인
cat $KUBECONFIG
ls -l $KUBECONFIG

[ 실행 결과 - 한 눈에 보기 ]

 

 

[ 참조 - 생성 된 cluster의 KUBECONFIG 정보 ]

(⎈|kind-myk8s:default) root@kind:~# cat $KUBECONFIG
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURCVENDQWUyZ0F3SUJBZ0lJSW51bUhSNDIxVU13RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TkRFeU1UZ3hNRE00TURsYUZ3MHpOREV5TVRZeE1EUXpNRGxhTUJVeApFekFSQmdOVkJBTVRDbXQxWW1WeWJtVjBaWE13Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLCkFvSUJBUURMc2VWWVVFbkE0TWlLRGZrT3BZV0ZtU3dHUmpLK0RMc2FXRW5EdkdIa3B5ZDMySmE4eG5JN0lxU2QKT2tKQnNQSW8wcGNpZ3BodGdocDd4emdKQWpqK083UTNZZmtoSXZSZVg2V0dPWEc4Qk5DMTNRNks3TzlxQXB1MwpGRVY1VytISi9POHBPOS83WFNDbEJNS2ZhbGxyTU5rM1NTQ3JOK3ZQUk9WTndTRTMva0REYWptaDdmSzIrdWl3ClFTWWd4QThVZ3VVUVVtNHlyNmpPTDU1bDlFeTBLWnBKdEFQSWhacUxNSW9wZkdFWjliREN2cXVPRnBlakIrYUoKak5PU2xCRHV1MDZiMERxUzJEem52Wkd3blVpbHlYYUEvMVg5SEIzRXl3KzhCZDU5ZmcrcGhvNUZDT0d4YzR1eQpsNTJiNlpFQ085NHdLNzc5eHI0dFZxV2ZDSTRmQWdNQkFBR2pXVEJYTUE0R0ExVWREd0VCL3dRRUF3SUNwREFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJSY0pQNVNzd3lHY2JSN21pbm15SjRPWUd1OTN6QVYKQmdOVkhSRUVEakFNZ2dwcmRXSmxjbTVsZEdWek1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQXZIZ0ZNMFh0QwpWSG45K1crcm5kMzJ2ZnBaRy9EQ2FId2NJaXNjWTRMV1N6UVVtUXNVcUFJaU5BYTB6VVdMakZudk4zKzdzUjM5ClluSDFmRVZmdldOdlN4T3ZTWlJxWWdKMTJzWDRJZlFaM3QwRVIzaGI1eDUrQzhZaWFCb1M1SzhLWTRtbW1ENDYKb0RMbVRlSUNHbG4yRlB4Z3BSZ3Nrd0FuUkpndE01ckhyaHhWc3hiNVpwUUgrR1cwWnI3NmpPNW9yOWZobk9UOQovMk9qWm9NSWEwSkcxc2lUeE13NjlNK0NmR2dadzlpeHBnN0VETmRpRWZVbTVVYWpUbUkxbVhUNytxbXkxVVFJCks0WHdBV3piL3YyWGdxRmY1STg1SSs0NmlNNDY5bko2RmRIeVd3ejFjUmsvVGlXOTBVdnRHQnI0MzlOaEtvLzUKdUtxamdtMzI4c284Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
    server: https://192.168.50.10:35335
  name: kind-myk8s
contexts:
- context:
    cluster: kind-myk8s
    namespace: default
    user: kind-myk8s
  name: kind-myk8s
current-context: kind-myk8s
kind: Config
preferences: {}
users:
- name: kind-myk8s
  user:
    client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURLVENDQWhHZ0F3SUJBZ0lJQ1J3WjZUSVJKZ1F3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TkRFeU1UZ3hNRE00TURsYUZ3MHlOVEV5TVRneE1EUXpNVEphTUR3eApIekFkQmdOVkJBb1RGbXQxWW1WaFpHMDZZMngxYzNSbGNpMWhaRzFwYm5NeEdUQVhCZ05WQkFNVEVHdDFZbVZ5CmJtVjBaWE10WVdSdGFXNHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDOGpCODYKazM2dTN5Y0ZCMDVNckVQMFM3NTRQUzdNM1gwZ05HTklPK3dKL0VFbHJrWU5DcCsrWU9wRWRZR2NvY3RMNFVBMAoyNkFmeVFURHRBalo4RC9JellJNHFHNEw4NWFNdTB6ZlcxR0xJaWlwOUl6UGQwRnhFYWdlb29BRGJDaWw5cm1vClRjWEpnQ3JxZEl0bnJzWHltVWx0MkpUeHJCcG95ZlhNZk1mZ1R5dEdRS3lPVUZmYmd3TW81SHBjOG1YSC9XY1oKSjc1bFcyZTNGWnZ3ME1oTndrVlJWRFAvT0JVVTRBR1hucXZ3QWVUbmJ5NjNreDZONFc1MStkd1RMVDBOOFI4QgpmVUpnOXBrQ2dVSUJWOFR5RUxnUVBISFBvU0xJVHJzZ0I5MDJjYU5jczFCNno5MEpHdzhxdU1Lblp1RE4rWWE4CjlHNkNTOTlLaFVpN2lVMHJBZ01CQUFHalZqQlVNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUsKQmdnckJnRUZCUWNEQWpBTUJnTlZIUk1CQWY4RUFqQUFNQjhHQTFVZEl3UVlNQmFBRkZ3ay9sS3pESVp4dEh1YQpLZWJJbmc1Z2E3M2ZNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUMra2dHVWFzSjFBUTFBVkgvQ29kNEkrb21tCk9JZWs3QnpQbFpxQ05YNEdzWDFhSXBwckUyMkJ1T281QjlwalY1d0RNdlQ1RUZWdGpMNlRkT0ZWRnc1NjBiVVkKNWhmdUgvSjNITHBHMVRIdy9PNjZjc3Y5dHNmU0YxUFpZeU1Wd2xhb3lFOW12TmtQNmhaNGVmQ1RuaVVRdnk0egpuTVpsdTI0VjcrUTFDclEvQzYrcnRYTTNyQUhiaXNCR01wdXphUytKNkxhWEZ6NmVJc1ZiVUFKaWZKaVhwY2dYCi9nRHNLRTRjR0VzRmd4RlpsN1N4RWFJdVFldDBOd0p0OGsvTndaZ2hLK1JEeTZzbnRhelpvUzh5V0FFK1BldlQKYi85YkxZWExMdmpaMEtVenBid2IybG1XcGl0V2kzcG5pcXJJNjZ5RC9pL2diS24wWVE0QW9YOGRkakorCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
    client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBdkl3Zk9wTitydDhuQlFkT1RLeEQ5RXUrZUQwdXpOMTlJRFJqU0R2c0NmeEJKYTVHCkRRcWZ2bURxUkhXQm5LSExTK0ZBTk51Z0g4a0V3N1FJMmZBL3lNMkNPS2h1Qy9PV2pMdE0zMXRSaXlJb3FmU00KejNkQmNSR29IcUtBQTJ3b3BmYTVxRTNGeVlBcTZuU0xaNjdGOHBsSmJkaVU4YXdhYU1uMXpIekg0RThyUmtDcwpqbEJYMjRNREtPUjZYUEpseC8xbkdTZStaVnRudHhXYjhORElUY0pGVVZRei96Z1ZGT0FCbDU2cjhBSGs1Mjh1CnQ1TWVqZUZ1ZGZuY0V5MDlEZkVmQVgxQ1lQYVpBb0ZDQVZmRThoQzRFRHh4ejZFaXlFNjdJQWZkTm5HalhMTlEKZXMvZENSc1BLcmpDcDJiZ3pmbUd2UFJ1Z2t2ZlNvVkl1NGxOS3dJREFRQUJBb0lCQVFDWEJURWFUUHVpZS9FWQpFUU1CWE5YTlBrSWdzdmppZE9IOVFwam5TYi9GTzd5K3Zya3pSdmd1bUJmc242WWlYR0dtdEpRbUFtaGVMVGZZCnZVSjl0ZmFSRXpLanlSemtDOGJoa0VDQmd2WnNsLzYwS3NJQ2h3WHUxL0t1SHdDa1BTVE4wakpoQVhKVDlJbnIKWTFBcHl3NllRM2xTTjdFTlpnbDlONDdlckcrNFZMQ1JKUys3NFRSSWRTUWZFMHUxdkpxRHRwampSWHdlcjl4YwpaMWVHdGF1RkN2aVk2ZitHNTZsM3kzQURDK0YzdXNUQXYyR3ljL3JkTXZ4dDZPS1FTVWZmaVNoRHZNcmFDdlJ5CjhraDkxRk9sdTNNdmtTaWZpanM5WXIyZk02U3VBRFFpNm5wbWZhbll2TXFxdTdNbVBqbkl4SjF1QkpyQTl0alUKd1N4M2pNbmhBb0dCQU0wcEs2MkRGbUJNdU05VFkva3dNQ2ZBME5ha3ViQll2UmhWbHR0Q3N6OE92dFZ3UUZlWQoxL205c2tvSUx0MXF3NDJmcnQ2blMreWcyQlJGZ0xSRUpPT0lndFJ3WGlyQlVJNzdiV3dwTTFpOFZNZFBSaFZiCkJrREtqZG96dExsWW1vamFaZFFmakhCa2FzWXFBMzBESnNOckJRUHNkYnVLdDlCRi90ZkFkajF4QW9HQkFPdEYKQ2dkaUI1VmwrN2JXNmdwY05ybHpGcGlYQ3owSmJKWG9qa2ttQlhRQ1I1eTFLeUdOMFlHVGp5aEJ3UXJwakpvawo1VklvRkNOSTV2M3N4cnZsNVdDSTlpVjZvUkprUHFjU2VKazF3azJmUDc0S0ZzSFA5VzFpQ1RkOHdzZ0VrOXNyCkFsVUZJaGdyYXdDM05scFRGcGFldFlmUFkwRGJJM2tSQjRQOTlkWmJBb0dBR3ZXaUM3Y0NKYjF2TzY5Vm45VTUKUHNBVHNXRU1GYVJUbFBNS1U5MjcrRGxUMG5CaHhDRU5vSC9SQWZGNVZ4ZEVnMFBNa3UyaENJTktZYitRWGgvbQpTUkFUUVI5VEFJTk90cnRMY2I4b1JDemJaeVYvckNhOTk1Mm1ibG9GRWFZWENJektmTWw0aHZ4ckc0ZEIxaGVuCjdGNFAxcWdvQUhFR0hwUlVITDNweThFQ2dZQVpkM0dzZHVvSE8vRFJTOVl0cE9tdmpVdW0zTUswcXE1OFU1NEQKUVFCYUhGMFFFM1JGdjRvc0VvTHVncCtZeE1zWmJHRCtVYVJNWlhuTGhyUEVaeEE3TU5na0duQVJBNVRjbE4xRgpDRkt1QUV6d0R6dURZelZYejhMYnBMK1kweFlHSlZPdFRFN3lldENzVlNpbkFsTnhubUJjQVJEblZsRXlxNnM2Cm1HVGgvUUtCZ0E2WDh1Tk5LeExtUmw1V0hiMlpldnpaZWJqcjU5WVQraEdmVXNMTHVyOXNrNlZZMVBpZWxNSmsKRmk3R2dEdG13SitOemRQNTFVc1JFOWJTbzlwNWp0Mzh4WnV5RVFTNVVpWXlxWlNRamZTUWZtaUVQaU0vSUJCVApZc0pQV3dTMjdqaExkV2x3Vm9pSHRDWjBaanJvdUZ2TXhMTnNzQ0ZpYUl0TVNIRFhXMmN5Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==

▶ (옵션) kube-ops-view

더보기
# helm show values geek-cookbook/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=30000 --set env.TZ="Asia/Seoul" --namespace kube-system

# 설치 확인
kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view

# myk8s-control-plane 노드?에 배치
kubectl get nodes myk8s-control-plane -o jsonpath={.metadata.labels} | jq
kubectl describe node myk8s-control-plane | grep Taints
kubectl -n kube-system get deploy kube-ops-view -o yaml | k neat
kubectl -n kube-system edit deploy kube-ops-view
---
spec:
  ...
  template:
    ...
    spec:
      nodeSelector:
        mynode: control-plane
      tolerations:
      - key: "node-role.kubernetes.io/control-plane"
        operator: "Equal"
        effect: "NoSchedule"
---

kubectl -n kube-system get pod -o wide -l app.kubernetes.io/instance=kube-ops-view

# kube-ops-view 접속 URL 확인 (1.5 , 2 배율) : macOS 사용자
echo -e "KUBE-OPS-VIEW URL = http://localhost:30000/#scale=1.5"
echo -e "KUBE-OPS-VIEW URL = http://localhost:30000/#scale=2"

# kube-ops-view 접속 URL 확인 (1.5 , 2 배율) : Windows 사용자
echo -e "KUBE-OPS-VIEW URL = http://192.168.50.10:30000/#scale=1.5"
echo -e "KUBE-OPS-VIEW URL = http://192.168.50.10:30000/#scale=2"

helm uninstall -n kube-system kube-ops-view

 

[ 실행 결과 - 한 눈에 보기 ]

 

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

더보기
  • Jenkins Plugin 설치 : Jenkins 관리 → Plugins  Available Plugins  → (검색)  
    • Pipeline Stage View - Docs
    • Docker Pipeline : building, testing, and using Docker images from Jenkins Pipeline - Docs
    • Gogs : Webhook Plugin - Docs
      • 예시 : http(s)://<< jenkins-server >>/gogs-webhook/?job=<<jobname>>

  • 자격증명 설정 : Jenkins 관리 → Credentials → Globals → Add Credentials
    1. Gogs Repo 자격증명 설정 : gogs-crd
      • Kind : Username with password
      • Username : devops
      • Password : <Gogs 토큰>
      • ID : gogs-crd
    2. 도커 허브 자격증명 설정 : dockerhub-crd
      • Kind : Username with password
      • Username : <도커 계정명>
      • Password : <도커 계정 암호 혹은 토큰>
      • ID : dockerhub-crd
    3. k8s(kind) 자격증명 설정 : k8s-crd
      • Kind : Secret file
      • File : <kubeconfig 파일 업로드>
      • ID : k8s-crd

Windows 사용자 경우, kubeconfig 파일은 메모장으로 직접 작성 후 업로드 하자!!

 


[ 실행 결과 - 한 눈에 보기 ]

Step1. Plug-In 설치

 

Step2. Credential 생성

▶ 연동 테스트 : Jenkins Item 생성(Pipeline) : item name(pipeline-ci)

더보기
pipeline {
    agent any
    environment {
        DOCKER_IMAGE = '<자신의 도커 허브 계정>/dev-app' // Docker 이미지 이름
    }
    stages {
        stage('Checkout') {
            steps {
                 git branch: 'main',
                 url: 'http://192.168.56.1: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."
        }
    }
}

 

  • 지금 빌드 → 콘솔 Output 확인
  • 도커 허브 확인

[ 실행 결과 - 한 눈에 보기 ]

 

Jenkins Manage(관리) →  New Item 파이프라인 생성 (내용 수정) → 지금 Build -> 도커허브 확인

k8s Deploying an application  

 

[ Simple Concept ]

더보기

 K8S는 Deployment 와 ReplicaSet 을 통해 POD의 갯를 Scalable 하게 관리할 수 있다!!

 

☞  하기와 같은 명세를 통해 원하는 형상을 유지할 수 있다.

- 원하는 상태 설정 시 k8s  충족을 위해 노력함 : Kubernetes uses declarative configuration, where you declare the state you want (like “I want 3 copies of my container running in the cluster”) in a configuration file. Then, submit that config to the cluster, and Kubernetes will strive to meet the requirements you specified.

[ 실습하며 이해하기 ]

더보기

Step1. 2개 POD 생성 시도 !!  ( Error 발생 ?! )

# 디플로이먼트 오브젝트 배포 : 리플리카(파드 2개), 컨테이너 이미지 >> 아래 도커 계정 부분만 변경해서 배포해보자
DHUSER=<도커 허브 계정명>
DHUSER=dragonball590n

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
watch -d kubectl get deploy,pod -o wide

# 배포 상태 확인 : kube-ops-view 웹 확인
kubectl get deploy,pod -o wide
kubectl describe pod
...
Events:
  Type     Reason     Age                From               Message
  ----     ------     ----               ----               -------
  Normal   Scheduled  53s                default-scheduler  Successfully assigned default/timeserver-7cf7db8f6c-mtvn7 to myk8s-worker
  Normal   BackOff    19s (x2 over 50s)  kubelet            Back-off pulling image "docker.io/gasida/dev-app:latest"
  Warning  Failed     19s (x2 over 50s)  kubelet            Error: ImagePullBackOff
  Normal   Pulling    4s (x3 over 53s)   kubelet            Pulling image "docker.io/gasida/dev-app:latest"
  Warning  Failed     3s (x3 over 51s)   kubelet            Failed to pull image "docker.io/gasida/dev-app:latest": failed to pull and unpack image "docker.io/gasida/dev-app:latest": failed to resolve reference "docker.io/gasida/dev-app:latest": pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed
  Warning  Failed     3s (x3 over 51s)   kubelet            Error: ErrImagePull

 

Step2. Trouble Shooting :  image pull error (ErrImagePull / ErrImagePullBackOff)

  • 보통 컨테이너 이미지 정보를 잘못 기입하는 경우에 발생
    • This error indicates that Kubernetes was unable to download the container image.
  • 혹은 이미지 저장소에 이미지가 없거나, 이미지 가져오는 자격 증명이 없는 경우에 발생
    • This typically means that the image name was misspelled in your configuration, the image doesn’t exist in the image repository, or your cluster doesn’t have the required credentials to access the repository.
  • Check the spelling of your image and verify that the image is in your repository.
# k8s secret : 도커 자격증명 설정 
kubectl get secret -A  # 생성 시 타입 지정

DHUSER=<도커 허브 계정>
DHPASS=<도커 허브 암호 혹은 토큰>
echo $DHUSER $DHPASS

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

# 확인
kubectl get secret
kubectl describe secret
kubectl get secrets -o yaml | kubectl neat  # base64 인코딩 확인

SECRET=eyJhdXRocyI6eyJodHRwczovL2luZGV4LmRvY2tlci5pby92MS8iOnsidXNlcm5hbWUiOiJnYXNpZGEiLCJwYXNzd29yZCI6ImRja3JfcGF0X0tXeC0wTjI3aUVkMWxrOGFOdlJ6OHBEclFsSSIsImF1dGgiOiJaMkZ6YVdSaE9tUmphM0pmY0dGMFgwdFhlQzB3VGpJM2FVVmtNV3hyT0dGT2RsSjZPSEJFY2xGc1NRPT0ifX19
echo "$SECRET" | base64 -d ; echo


# 디플로이먼트 오브젝트 업데이트 : 시크릿 적용 >> 아래 도커 계정 부분만 변경해서 배포해보자
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
watch -d kubectl get deploy,pod -o wide

# 확인
kubectl get deploy,pod

# 접속을 위한 curl 파드 생성
kubectl run curl-pod --image=curlimages/curl:latest --command -- sh -c "while true; do sleep 3600; done"
kubectl get pod -owide

# timeserver 파드 IP 1개 확인 후 접속 확인
PODIP1=<timeserver-Y 파드 IP>
PODIP1=10.244.1.3

kubectl exec -it curl-pod -- curl $PODIP1
kubectl exec -it curl-pod -- curl $PODIP1

# 로그 확인
kubectl logs deploy/timeserver
kubectl logs deploy/timeserver -f
kubectl stern deploy/timeserver
kubectl stern -l pod=timeserver-pod

 

☞ POD는 언제든지 죽고 살아날 수 있다. (고정된 진입점 : service IP가 필요!!) 

#
POD1NAME=<파드 1개 이름>
POD1NAME=timeserver-7954b8f6df-l25wx

kubectl get pod -owide
kubectl delete pod $POD1NAME && kubectl get pod -w

# 셀프 힐링 , 파드 IP 변경 -> 고정 진입점(고정 IP/도메인네임) 필요 => Service
kubectl get deploy,rs,pod -owide

 

Step3. 서비스 생성

# 서비스 생성
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

#
kubectl get service,ep timeserver -owide
NAME                 TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE   SELECTOR
service/timeserver   NodePort   10.96.236.37   <none>        80:30000/TCP   25s   pod=timeserver-pod

NAME                   ENDPOINTS                                   AGE
endpoints/timeserver   10.244.1.2:80,10.244.2.2:80,10.244.3.2:80   25s

# Service(ClusterIP)로 접속 확인 : 도메인네임, ClusterIP
kubectl exec -it curl-pod -- curl timeserver
kubectl exec -it curl-pod -- curl $(kubectl get svc timeserver -o jsonpath={.spec.clusterIP})

# Service(NodePort)로 접속 확인 "노드IP:NodePort"
curl http://127.0.0.1:30000
curl http://127.0.0.1:30000

# 반복 접속 해두기 : 부하분산 확인
while true; do curl -s --connect-timeout 1 http://127.0.0.1:30000 | grep name ; sleep 1 ; done
for i in {1..100};  do curl -s http://127.0.0.1:30000 | grep name; done | sort | uniq -c | sort -nr

# 파드 복제복 증가 : service endpoint 대상에 자동 추가
kubectl scale deployment timeserver --replicas 4
kubectl get service,ep timeserver -owide

# 반복 접속 해두기 : 부하분산 확인
while true; do curl -s --connect-timeout 1 http://127.0.0.1:30000 | grep name ; sleep 1 ; done
for i in {1..100};  do curl -s http://127.0.0.1:30000 | grep name; done | sort | uniq -c | sort -nr

[ 실행 결과 - 한 눈에 보기 ]

▶ 어플리케이션 업데이트 관리

더보기

 샘플 앱 server.py 코드 변경 → 새 0.0.2 버전 태그로 컨테이너 이미지 빌드 → 컨테이너 저장소 Push ⇒ k8s deployment 업데이트 배포  

# VERSION 변경 : 0.0.2
# server.py 변경 : 0.0.2
git add . && git commit -m "VERSION $(cat VERSION) Changed" && git push -u origin main

 

태그버전 정보 사용을 권장 : You can make this tag anything you like, but it’s a good convention to use version numbers.

# 파드 복제복 증가
kubectl scale deployment timeserver --replicas 4
kubectl get service,ep timeserver -owide

# 반복 접속 해두기 : 부하분산 확인
while true; do curl -s --connect-timeout 1 http://127.0.0.1:30000 | grep name ; sleep 1 ; done
for i in {1..100};  do curl -s http://127.0.0.1:30000 | grep name; done | sort | uniq -c | sort -nr

#
kubectl set image deployment timeserver timeserver-container=$DHUSER/dev-app:0.0.Y && watch -d "kubectl get deploy,ep timeserver; echo; kubectl get rs,pod"
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"

# 롤링 업데이트 확인
kubectl get deploy,rs,pod,svc,ep -owide

# kubectl get deploy $DEPLOYMENT_NAME
kubectl get deploy timeserver
kubectl get pods -l pod=timeserver-pod

#
curl http://127.0.0.1:30000

 

  • The READY column shows how many Pods are serving traffic and how many we requested.
  • In this case, all three are ready. The UP-TO-DATE column, however, indicates that only one of these Pods is the current version.
  • This is because, rather than replacing all the Pods at once, causing some downtime to the application, by default, Pods are updated with a so-called rolling update strategy—that is, one or several at a time.

[ 실행 결과 - 한 눈에 보기 ]

 

▶ /dev-app 에서 server.py 와 VERSION 파일을 0.0.2 로 업데이트 후, Jenkins 에서 기존에 생성해 둔 pipeline-ci 를 통해 저장소 파일 업데이트 → 

 

Gogs Webhooks 설정 : Jenkins Job Trigger

  • gogs 에 app.ini 파일 수정 후 컨테이너 재기동 - issue
[security]
INSTALL_LOCK = true
SECRET_KEY   = j2xaUPQcbAEwpIu
LOCAL_NETWORK_ALLOWLIST = 192.168.254.124 # 각자 자신의 PC IP
  • docker compose restart gogs
  • Webhooks are much like basic HTTP POST event triggers. Whenever something occurs in Gogs,
  • we will handle the notification to the target host you specify. %!(EXTRA string=https://gogs.io/docs/features/webhook.html)
더보기
  • Payload URL : http://192.168.254.124:8080/gogs-webhook/?job=**SCM-Pipeline**/
  • Content Type : application/json
  • Secret : qwe123
  • When should this webhook be triggered? : Just the push event
  • Active : Check

Add webhook

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

더보기

☞ New Item > 명칭 : SCM-Pipeline > type: pipeline 선택 > 생성 > [ 상세 스펙 설정 ]

  • GitHub project : http://***<mac IP>***:3000/***<Gogs 계정명>***/dev-app ← .git 은 제거
  • Use Gogs secret : qwe123
  • Build Triggers : Build when a change is pushed to Gogs 체크
  • Pipeline script from SCM
    • SCM : Git
      • Repo URL(http://***<mac IP>***:3000/***<Gogs 계정명>***/dev-app)
      • Credentials(devops/***)
      • Branch(*/main)
    • Script Path : Jenkinsfile

 

Jenkinsfile 작성 후 Git push : 호스트에서 직접 git 작업

더보기

Step1.  호스트에서 작업  ( CICD-LAB\dev-app 이하 작업 )

# Jenkinsfile 빈 파일 작성
touch Jenkinsfile

# VERSION 파일 : 0.0.3 수정
# server.py 파일 : 0.0.3 수정

 

Step2. VSCODE 로 jenkins 컨테이너 내부 파일 Open 후 작성

pipeline {
    agent any
    environment {
        DOCKER_IMAGE = '<자신의 도커 허브 계정>/dev-app' // Docker 이미지 이름
    }
    stages {
        stage('Checkout') {
            steps {
                 git branch: 'main',
                 url: 'http://192.168.254.124: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."
        }
    }
}

 

Step3. 작성된 파일 Push

git add . && git commit -m "VERSION $(cat VERSION) Changed" && git push -u origin main

[ 이 후, Step ]

  • Jenkins 트리거 빌드 확인
  • 도커 저장소 확인
  • Gogs WebHook 기록 확인
  • k8s 에 신규 버전 적용
kubectl set image deployment timeserver timeserver-container=$DHUSER/dev-app:0.0.3 && watch -d "kubectl get deploy,ep timeserver; echo; kubectl get rs,pod"

 

☞ 앞단에서 정의된 Pipeline 에 의해 업데이트 된 버전의 server.py 가 k8s 에 순차 적용된다. ( v0.0.2 -> v0.0.3 )

 


2. Jenkins CI/CD + K8S(Kind)

☞ 1번 과정에서 Jenkins Pipeline 을 통해 업로드한 파일을 k8s와 연동하여 배포하는 실습을 해 보자!!

 

2-1. 컨테이너 내부에 툴 설치 (kubectl, heml) 

 

더보기
# Install kubectl, helm
docker compose exec --privileged -u root jenkins bash
--------------------------------------------
#curl -LO "https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubectl" 
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubectl"  # macOS
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"  # WindowOS

install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
kubectl version --client=true

#
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version

exit
--------------------------------------------
docker compose exec jenkins kubectl version --client=true
docker compose exec jenkins helm version

 

[ 실행 결과 - 한 눈에 보기 ]

2-2. Jenkins Item 생성(Pipeline) : item name(k8s-cmd)

더보기

☞  Jenkins > New Item > k8s-cmd (pipeline) > 파이프라인 에 하기 복사+붙여넣기

pipeline {
    agent any
    environment {
        KUBECONFIG = credentials('k8s-crd')
    }
    stages {
        stage('List Pods') {
            steps {
                sh '''
                # Fetch and display Pods
                kubectl get pods -A --kubeconfig "$KUBECONFIG"
                '''
            }
        }
    }
}

 

2-3. Jenkins 를 이용한 blue-green 배포 준비

1) 디플로이먼트 / 서비스 yaml 파일 작성 - http-echo 및 코드 push

더보기

[ 코드 설명 ]

1) /dev-app/deploy 디렉토리 생성 후,

2) Blue / Green / Echo - 서비스 호출 대상 deployment  yaml 생성

3) gogs   [dev-app] Repository 에 해당 생성 yaml  push

# 
cd dev-app

#
mkdir deploy

#
cat > deploy/echo-server-blue.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo-server-blue
spec:
  replicas: 2
  selector:
    matchLabels:
      app: echo-server
      version: blue
  template:
    metadata:
      labels:
        app: echo-server
        version: blue
    spec:
      containers:
      - name: echo-server
        image: hashicorp/http-echo
        args:
        - "-text=Hello from Blue"
        ports:
        - containerPort: 5678
EOF

cat > deploy/echo-server-service.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
  name: echo-server-service
spec:
  selector:
    app: echo-server
    version: blue
  ports:
  - protocol: TCP
    port: 80
    targetPort: 5678
    nodePort: 30000
  type: NodePort
EOF

cat > deploy/echo-server-green.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo-server-green
spec:
  replicas: 2
  selector:
    matchLabels:
      app: echo-server
      version: green
  template:
    metadata:
      labels:
        app: echo-server
        version: green
    spec:
      containers:
      - name: echo-server
        image: hashicorp/http-echo
        args:
        - "-text=Hello from Green"
        ports:
        - containerPort: 5678
EOF

#
git add . && git commit -m "Add echo server yaml" && git push -u origin main

[

 

 

2) (참고) 직접 블루-그린 업데이트 실행

더보기

[ 코드 설명 ]  * label selector 를 통한 서비스 통제

1) 실습을 위해 기존 생성해 놓은 deploy,svc 삭제 => 신규 생성해 놓은 yaml 기반 자원 생성

2) Blue / Green / Echo 대상 자원 상세 확인 및 curl TEST 

☞ Echo 서버는 version=blue  만 관심있다!!

3) Patch 명령어를 사용하여, echo 서버의 관심대상을 변경 ( blue -> green ) 후, 호출 테스트

☞ Green 대상 서비스만 호출되는 것을 볼 수 있다.

4) Patch 명령어로 다시 원복 ( green -> blue ) 후, 호출 테스트

5) 생성 자원 삭제

#
cd deploy
kubectl delete deploy,svc --all
kubectl apply -f .

#
kubectl get deploy,svc,ep -owide
curl -s http://127.0.0.1:30000

#
kubectl patch svc echo-server-service -p '{"spec": {"selector": {"version": "green"}}}'
kubectl get deploy,svc,ep -owide
curl -s http://127.0.0.1:30000

#
kubectl patch svc echo-server-service -p '{"spec": {"selector": {"version": "blue"}}}'
kubectl get deploy,svc,ep -owide
curl -s http://127.0.0.1:30000

# 삭제
kubectl delete -f .
cd ..

 

2-4. Jenkins Item 생성(Pipeline) : item name(k8s-bluegreen) - Jenkins 통한 k8s 기본 배포

1) 이전 실습 대상 deployment, service 삭제

kubectl delete deploy,svc timeserver

 

2) 반복 접속 미리 실행

while true; do curl -s --connect-timeout 1 http://127.0.0.1:30000 ; echo ; sleep 1  ; kubectl get deploy -owide ; echo ; kubectl get svc,ep echo-server-service -owide ; echo "------------" ; done
혹은 
while true; do curl -s --connect-timeout 1 http://127.0.0.1:30000 ; date ; echo "------------" ; sleep 1 ; done

 

3) Jenkins 파이프라인 생성 : k8sbluegreen

더보기

[ 코드 설명 ]

☞ Jenkins pipeline 통한 B/G 배포

1) gogs-crd 로 main branch 에 접근허용 체크

2)  Image Build  & Upload 는 echo 체크만 !!

3) KUBECONFIG 인증으로 B/G deploy 배포

4) 승인 절차 : approve green version => green 배포

5) Switching ( Patch : version=blue => green )

6) 문제 있으면, Rollback 할 수있도록 stage 제공

pipeline {
    agent any

    environment {
        KUBECONFIG = credentials('k8s-crd')
    }

    stages {
        stage('Checkout') {
            steps {
                 git branch: 'main',
                 url: 'http://192.168.254.124:3000/devops/dev-app.git',  // Git에서 코드 체크아웃
                 credentialsId: 'gogs-crd'  // Credentials ID
            }
        }

        stage('container image build') {
            steps {
                echo "container image build"
            }
        }

        stage('container image upload') {
            steps {
                echo "container image upload"
            }
        }

        stage('k8s deployment blue version') {
            steps {
                sh "kubectl apply -f ./deploy/echo-server-blue.yaml --kubeconfig $KUBECONFIG"
                sh "kubectl apply -f ./deploy/echo-server-service.yaml --kubeconfig $KUBECONFIG"
            }
        }

        stage('approve green version') {
            steps {
                input message: 'approve green version', ok: "Yes"
            }
        }

        stage('k8s deployment green version') {
            steps {
	        	sh "kubectl apply -f ./deploy/echo-server-green.yaml --kubeconfig $KUBECONFIG"
            }
        }

        stage('approve version switching') {
            steps {
                script {
                    returnValue = input message: 'Green switching?', ok: "Yes", parameters: [booleanParam(defaultValue: true, name: 'IS_SWITCHED')]
                    if (returnValue) {
                        sh "kubectl patch svc echo-server-service -p '{\"spec\": {\"selector\": {\"version\": \"green\"}}}' --kubeconfig $KUBECONFIG"
                    }
                }
            }
        }

        stage('Blue Rollback') {
            steps {
                script {
                    returnValue = input message: 'Blue Rollback?', parameters: [choice(choices: ['done', 'rollback'], name: 'IS_ROLLBACk')]
                    if (returnValue == "done") {
                        sh "kubectl delete -f ./deploy/echo-server-blue.yaml --kubeconfig $KUBECONFIG"
                    }
                    if (returnValue == "rollback") {
                        sh "kubectl patch svc echo-server-service -p '{\"spec\": {\"selector\": {\"version\": \"blue\"}}}' --kubeconfig $KUBECONFIG"
                    }
                }
            }
        }
    }
}

[ 실행 결과 - 한 눈에 보기 ]

1) jenkins - pipeline 생성 : URL 수정

 

 2) Jenkins 파이프 라인 상 단계 별 배포 진행 승인

 

 

4) 지금 배포 후 동작 확인

5) 실습 완료 후 삭제 ( 중요!! )

더보기
kubectl delete deploy echo-server-blue echo-server-green
kubectl delete svc echo-server-service

3. Jenkins CI + Argo CD + K8S(Kind)

Argo CD 소개 : Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes

더보기
  • Application definitions, configurations, and environments should be declarative and version controlled.
  • Application deployment and lifecycle management should be automated, auditable, and easy to understand.
  • How it works : Argo CD follows the GitOps pattern of using Git repositories as the source of truth for defining the desired application state. Kubernetes manifests can be specified in several ways:
    • kustomize applications
    • helm charts
    • jsonnet files
    • Plain directory of YAML/json manifests
    • Any custom config management tool configured as a config management plugin

Architecture - Docs

더보기
[ 출처 : https://argo-cd.readthedocs.io/en/stable/ ]

 

  • API Server : Web UI 대시보드, k8s api 처럼 API 서버 역할
    • The API server is a gRPC/REST server which exposes the API consumed by the Web UI, CLI, and CI/CD systems. It has the following responsibilities:
    • application management and status reporting
    • invoking of application operations (e.g. sync, rollback, user-defined actions)
    • repository and cluster credential management (stored as K8s secrets)
    • authentication and auth delegation to external identity providers
    • RBAC enforcement
    • listener/forwarder for Git webhook events
  • Repository Server : Git 연결 및 배포할 yaml 생성
    • The repository server is an internal service which maintains a local cache of the Git repository holding the application manifests. It is responsible for generating and returning the Kubernetes manifests when provided the following inputs:
    • repository URL
    • revision (commit, tag, branch)
    • application path
    • template specific settings: parameters, helm values.yaml
  • Application Controller : k8s 리소스 모니터링, Git과 비교
    • The application controller is a Kubernetes controller which continuously monitors running applications and compares the current, live state against the desired target state (as specified in the repo). It detects OutOfSync application state and optionally takes corrective action. It is responsible for invoking any user-defined hooks for lifecycle events (PreSync, Sync, PostSync)
  • Redis : k8s api와 git 요청을 줄이기 위한 캐싱
  • Notification : 이벤트 알림, 트리거
  • Dex : 외부 인증 관리
  • ApplicationSet Controller : 멀티 클러스터를 위한 App 패키징 관리

▶ Argo 주요 기능요약

더보기
  • Automated deployment of applications to specified target environments
  • Support for multiple config management/templating tools (Kustomize, Helm, Jsonnet, plain-YAML)
  • Ability to manage and deploy to multiple clusters
  • SSO Integration (OIDC, OAuth2, LDAP, SAML 2.0, GitHub, GitLab, Microsoft, LinkedIn)
  • Multi-tenancy and RBAC policies for authorization
  • Rollback/Roll-anywhere to any application configuration committed in Git repository
  • Health status analysis of application resources
  • Automated configuration drift detection and visualization
  • Automated or manual syncing of applications to its desired state
  • Web UI which provides real-time view of application activity
  • CLI for automation and CI integration
  • Webhook integration (GitHub, BitBucket, GitLab)
  • Access tokens for automation
  • PreSync, Sync, PostSync hooks to support complex application rollouts (e.g.blue/green & canary upgrades)
  • Audit trails for application events and API calls
  • Prometheus metrics
  • Parameter overrides for overriding helm parameters in Git

▶ Argo 핵심 컨셉정리

더보기

참고 링크 : Core Concepts - Docs

  • Application A group of Kubernetes resources as defined by a manifest. This is a Custom Resource Definition (CRD).
  • Application source type Which Tool is used to build the application.
  • Target state The desired state of an application, as represented by files in a Git repository.
  • Live state The live state of that application. What pods etc are deployed.
  • Sync status Whether or not the live state matches the target state. Is the deployed application the same as Git says it should be?
  • Sync The process of making an application move to its target state. E.g. by applying changes to a Kubernetes cluster.
  • Sync operation status Whether or not a sync succeeded.
  • Refresh Compare the latest code in Git with the live state. Figure out what is different.
  • Health The health of the application, is it running correctly? Can it serve requests?
  • Tool A tool to create manifests from a directory of files. E.g. Kustomize. See Application Source Type.
  • Configuration management tool See Tool.
  • Configuration management plugin A custom tool.

 

3-1. Argo CD 설치 및 기본 설정 - helm_chart

1) Argo CD 설치  ( **  별도 Virtual Box + Vagrant 로 띄웠을 경우, 127.0.0.1 -> 해당 구성 IP:30002 로 호출해야 함!! )

더보기
# 네임스페이스 생성 및 파라미터 파일 작성
kubectl create ns argocd
cat <<EOF > argocd-values.yaml
dex:
  enabled: false

server:
  service:
    type: NodePort
    nodePortHttps: 30002
EOF

# 설치
helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd argo/argo-cd --version 7.7.10 -f argocd-values.yaml --namespace argocd

# 확인
kubectl get pod,svc,ep -n argocd
kubectl get crd | grep argo
applications.argoproj.io                     2024-04-14T08:12:16Z
applicationsets.argoproj.io                  2024-04-14T08:12:17Z
appprojects.argoproj.io                      2024-04-14T08:12:16Z

# 최초 접속 암호 확인
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d ;echo
PCdOlwZT8c4naBWK

# Argo CD 웹 접속 주소 확인 : 초기 암호 입력 (admin 계정)
open "https://127.0.0.1:30002" # macOS
## Windows OS경우 직접 웹 브라우저에서 https://127.0.0.1:30002 접속

[ 실행 결과 - 한 눈에 보기 ] * helm 명령어 참조 : Blog

 

☞ argocd secret 키 저장해 놓기!!

 

☞ argocd 접속 : https://192.168.50.10:30002   ( 나의 경우, Virtual Box - vagrant 로 kind 구성하여 EKS를 띄웠다. 이 경우, 해당 kind VM의 지정 IP로 대체하여 호출해야 한다!! )

2) Argo CD 웹 접속 확인

User info → UPDATE PASSWORD 로 admin 계정 암호 변경 (qwe12345)

 

3)  기본 정보 확인 (Settings) : Clusters, Projects, Accounts

 

4) ops-deploy Repo 등록 : Settings → Repositories → CONNECT REPO 클릭

더보기

⇒ 입력 후 CONNECT 클릭



3-2. (기초) helm chart 를 통한 배포 실습

▶helm chart 를 통한 기본적인 manual 배포 실습을 통해 동작 방식을 이해할 수 있다.

더보기
#
mkdir nginx-chart
cd nginx-chart

mkdir templates

cat > templates/configmap.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}
data:
  index.html: |
{{ .Values.indexHtml | indent 4 }}
EOF

cat > templates/deployment.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ .Release.Name }}
  template:
    metadata:
      labels:
        app: {{ .Release.Name }}
    spec:
      containers:
      - name: nginx
        image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
        ports:
        - containerPort: 80
        volumeMounts:
        - name: index-html
          mountPath: /usr/share/nginx/html/index.html
          subPath: index.html
      volumes:
      - name: index-html
        configMap:
          name: {{ .Release.Name }}
EOF

cat > templates/service.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}
spec:
  selector:
    app: {{ .Release.Name }}
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
    nodePort: 30000
  type: NodePort
EOF

cat > values.yaml <<EOF
indexHtml: |
  <!DOCTYPE html>
  <html>
  <head>
    <title>Welcome to Nginx!</title>
  </head>
  <body>
    <h1>Hello, Kubernetes!</h1>
    <p>Nginx version 1.26.1</p>
  </body>
  </html>

image:
  repository: nginx
  tag: 1.26.1

replicaCount: 1
EOF

cat > Chart.yaml <<EOF
apiVersion: v2
name: nginx-chart
description: A Helm chart for deploying Nginx with custom index.html
type: application
version: 1.0.0
appVersion: "1.26.1"
EOF

# 이전 timeserver/service(nodeport) 삭제
kubectl delete deploy,svc --all

# 직접 배포 해보기
helm install dev-nginx . -f values.yaml
helm list
kubectl get deploy,svc,ep,cm dev-nginx -owide

#
curl http://127.0.0.1:30000
curl -s http://127.0.0.1:30000 | grep version
open http://127.0.0.1:30000


# value 값 변경 후 적용 해보기 : version/tag, replicaCount
cat > values.yaml <<EOF
indexHtml: |
  <!DOCTYPE html>
  <html>
  <head>
    <title>Welcome to Nginx!</title>
  </head>
  <body>
    <h1>Hello, Kubernetes!</h1>
    <p>Nginx version 1.26.2</p>
  </body>
  </html>

image:
  repository: nginx
  tag: 1.26.2

replicaCount: 2
EOF

# helm chart 업그레이드 적용
helm upgrade dev-nginx . -f values.yaml

# 확인
helm list
kubectl get deploy,svc,ep,cm dev-nginx -owide
curl http://127.0.0.1:30000
curl -s http://127.0.0.1:30000 | grep version
open http://127.0.0.1:30000

# 확인 후 삭제
helm uninstall dev-nginx

3-3. Repo(ops-deploy) 에 nginx helm chart 를 Argo CD를 통한 배포 1

1) git 작업

더보기
#
cd ~/cicd-labs
git clone http://192.168.254.124:3000/devops/ops-deploy.git
cd ops-deploy

#
git config user.name "devops"
git config user.email "a@a.com"
git config init.defaultBranch main
git config credential.helper store

#
VERSION=1.26.1
mkdir nginx-chart
mkdir nginx-chart/templates

cat > nginx-chart/VERSION <<EOF
$VERSION
EOF

cat > nginx-chart/templates/configmap.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}
data:
  index.html: |
{{ .Values.indexHtml | indent 4 }}
EOF

cat > nginx-chart/templates/deployment.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ .Release.Name }}
  template:
    metadata:
      labels:
        app: {{ .Release.Name }}
    spec:
      containers:
      - name: nginx
        image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
        ports:
        - containerPort: 80
        volumeMounts:
        - name: index-html
          mountPath: /usr/share/nginx/html/index.html
          subPath: index.html
      volumes:
      - name: index-html
        configMap:
          name: {{ .Release.Name }}
EOF

cat > nginx-chart/templates/service.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}
spec:
  selector:
    app: {{ .Release.Name }}
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
    nodePort: 30000
  type: NodePort
EOF

cat > nginx-chart/values-dev.yaml <<EOF
indexHtml: |
  <!DOCTYPE html>
  <html>
  <head>
    <title>Welcome to Nginx!</title>
  </head>
  <body>
    <h1>Hello, Kubernetes!</h1>
    <p>DEV : Nginx version $VERSION</p>
  </body>
  </html>

image:
  repository: nginx
  tag: $VERSION

replicaCount: 1
EOF

cat > nginx-chart/values-prd.yaml <<EOF
indexHtml: |
  <!DOCTYPE html>
  <html>
  <head>
    <title>Welcome to Nginx!</title>
  </head>
  <body>
    <h1>Hello, Kubernetes!</h1>
    <p>PRD : Nginx version $VERSION</p>
  </body>
  </html>

image:
  repository: nginx
  tag: $VERSION

replicaCount: 2
EOF

cat > nginx-chart/Chart.yaml <<EOF
apiVersion: v2
name: nginx-chart
description: A Helm chart for deploying Nginx with custom index.html
type: application
version: 1.0.0
appVersion: "$VERSION"
EOF

tree nginx-chart
nginx-chart
├── Chart.yaml
├── VERSION
├── templates
│   ├── configmap.yaml
│   ├── deployment.yaml
│   └── service.yaml
├── values-dev.yaml
└── values-prd.yaml

#
git status && git add . && git commit -m "Add nginx helm chart" && git push -u origin main

 

2) Argo CD에 App 등록 : Application → NEW APP

더보기
  • GENERAL
    • App Name : dev-nginx
    • Project Name : default
    • SYNC POLICY : Manual
    • SYNC OPTIONS : AUTO-CREATE NAMESPACE(Check)
  • Source
    • Repo URL : 설정되어 있는 것 선택
    • Revision : HEAD
    • PATH : nginx-chart
  • DESTINATION
    • Cluster URL : <기본값>
    • NAMESPACE : dev-nginx
  • HELM
    • Values files : values-dev.yaml

⇒ 작성 후 상단 CREATE 클릭


3) dev-nginx App 클릭 후 상세 정보 확인 → DIFF 클릭 확인

더보기
#
kubectl get applications -n argocd
NAME        SYNC STATUS   HEALTH STATUS
dev-nginx   OutOfSync     Missing

kubectl describe applications -n argocd dev-nginx

# 반복 접속 시도
while true; do curl -s --connect-timeout 1 http://127.0.0.1:30000 ; date ; echo "------------" ; sleep 1 ; done

 

☞ ArgoCD APP & Repository 에는 설정파일이 있지만, 정작 Destination 인 Cluster 내에는 없기 때문에 발생하는 현상임!!

[ AS-IS : Sync 수행 전]

 [ TO-BE : Sync 수행 후 ]

4) SYNC 클릭 으로 반영 확인

더보기
# 아래 처럼 yaml 로 APP 생성 가능
kubectl get applications -n argocd
kubectl get applications -n argocd -o yaml | kubectl neat

# 배포 확인
kubectl get all -n dev-nginx -o wide

5) 코드 수정 후, 반영 확인

더보기
#
VERSION=1.26.2

cat > nginx-chart/VERSION <<EOF
$VERSION
EOF

cat > nginx-chart/values-dev.yaml <<EOF
indexHtml: |
  <!DOCTYPE html>
  <html>
  <head>
    <title>Welcome to Nginx!</title>
  </head>
  <body>
    <h1>Hello, Kubernetes!</h1>
    <p>DEV : Nginx version $VERSION</p>
  </body>
  </html>

image:
  repository: nginx
  tag: $VERSION

replicaCount: 2
EOF

cat > nginx-chart/values-prd.yaml <<EOF
indexHtml: |
  <!DOCTYPE html>
  <html>
  <head>
    <title>Welcome to Nginx!</title>
  </head>
  <body>
    <h1>Hello, Kubernetes!</h1>
    <p>PRD : Nginx version $VERSION</p>
  </body>
  </html>

image:
  repository: nginx
  tag: $VERSION

replicaCount: 2
EOF

#
git status && git add . && git commit -m "Update nginx version $(cat nginx-chart/VERSION)" && git push -u origin main

6) Argo CD 웹 확인 → REFRESH 클릭 - Interval

더보기

 

☞ How often does Argo CD check for changes to my Git or Helm repository ?

    The default polling interval is 3 minutes (180 seconds). You can change the setting by updating the timeout.reconciliation value in the argocd-cm config map


7) SYNC 클릭 → SYNCHRONIZE 클릭

더보기
# 배포 확인
kubectl get all -n dev-nginx -o wide

8) Argo CD 웹에서 App 삭제

더보기
watch -d kubectl get all -n dev-nginx -o wide

3-4. Repo(ops-deploy) 에 nginx helm chart 를 Argo CD를 통한 배포 2 : ArgoCD Declarative Setup - ArgoCD 애플리케이션 자체를 yaml로 생성

☞ ArgoCD Declarative Setup - Project, applications, ArgoCD Settings - Docs

1)  dev-nginx App 생성 및 Auto SYNC

더보기
#
cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: dev-nginx
  namespace: argocd
  finalizers:
  - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    helm:
      valueFiles:
      - values-dev.yaml
    path: nginx-chart
    repoURL: http://192.168.254.124:3000/devops/ops-deploy
    targetRevision: HEAD
  syncPolicy:
    automated:
      prune: true
    syncOptions:
    - CreateNamespace=true
  destination:
    namespace: dev-nginx
    server: https://kubernetes.default.svc
EOF

#
kubectl get applications -n argocd dev-nginx
kubectl get applications -n argocd dev-nginx -o yaml | kubectl neat
kubectl describe applications -n argocd dev-nginx
kubectl get pod,svc,ep,cm -n dev-nginx

#
curl http://127.0.0.1:30000
open http://127.0.0.1:30000

# Argo CD App 삭제
kubectl delete applications -n argocd dev-nginx

2) prd-nginx App 생성 및 Auto SYNC

더보기
#
cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: prd-nginx
  namespace: argocd
  finalizers:
  - resources-finalizer.argocd.argoproj.io
spec:
  destination:
    namespace: prd-nginx
    server: https://kubernetes.default.svc
  project: default
  source:
    helm:
      valueFiles:
      - values-prd.yaml
    path: nginx-chart
    repoURL: http://192.168.254.124:3000/devops/ops-deploy
    targetRevision: HEAD
  syncPolicy:
    automated:
      prune: true
    syncOptions:
    - CreateNamespace=true
EOF

#
kubectl get applications -n argocd prd-nginx
kubectl describe applications -n argocd prd-nginx
kubectl get pod,svc,ep,cm -n prd-nginx

#
curl http://127.0.0.1:30000
open http://127.0.0.1:30000

# Argo CD App 삭제
kubectl delete applications -n argocd prd-nginx

 

 

1) ops-deploy Repo 코드 작업

더보기
#
cd ops-deploy
mkdir dev-app

# 도커 계정 정보
DHUSER=<도커 허브 계정>
DHUSER=gasida

# 버전 정보 
VERSION=0.0.1

#
cat > dev-app/VERSION <<EOF
$VERSION
EOF

cat > dev-app/timeserver.yaml <<EOF
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:$VERSION
      imagePullSecrets:
      - name: dockerhub-secret
EOF

cat > dev-app/service.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
  name: timeserver
spec:
  selector:
    pod: timeserver-pod
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
    nodePort: 30000
  type: NodePort
EOF

#
git status && git add . && git commit -m "Add dev-app deployment yaml" && git push -u origin main

 

2) Argo CD app 생성

더보기
#
cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: timeserver
  namespace: argocd
  finalizers:
  - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    path: dev-app
    repoURL: http://192.168.254.124:3000/devops/ops-deploy
    targetRevision: HEAD
  syncPolicy:
    automated:
      prune: true
    syncOptions:
    - CreateNamespace=true
  destination:
    namespace: default
    server: https://kubernetes.default.svc
EOF

#
kubectl get applications -n argocd timeserver
kubectl get applications -n argocd timeserver -o yaml | kubectl neat
kubectl describe applications -n argocd timeserver
kubectl get deploy,rs,pod
kubectl get svc,ep timeserver

#
curl http://127.0.0.1:30000
open http://127.0.0.1:30000

 

3) dev-app Repo 코드 작업

 

Step1. dev-app Repo에 VERSION 업데이트 시 → ops-deploy Repo 에 dev-app 에 파일에 버전 정보 업데이트 작업 추가

더보기

 

  1. 기존 버전 정보는 VERSION 파일 내에 정보를 가져와서 변수 지정 : OLDVER=$(cat dev-app/VERSION)
  2. 신규 버전 정보는 environment 도커 태그 정보를 가져와서 변수 지정 : NEWVER=$(echo ${DOCKER_TAG})
  3. 이후 sed 로 ops-deploy Repo 에 dev-app/VERSION, timeserver.yaml 2개 파일에 ‘기존 버전’ → ‘신규 버전’으로 값 변경
  4. 이후 ops-deploy Repo 에 git push ⇒ Argo CD app 가 최대 3분 사이에 변경 확인 후 AutoSync 로 신규 버전 업데이트 진행

 

 Step2. dev-app 에 위치한 Jenkinsfile 로 젠킨스에 SCM-Pipeline(SCM:git) 으로 사용되고 있는 파일을 수정해서 실습에 사용

더보기
pipeline {
    agent any
    environment {
        DOCKER_IMAGE = 'gasida/dev-app' // Docker 이미지 이름
        GOGSCRD = credentials('gogs-crd')
    }
    stages {
        stage('dev-app Checkout') {
            steps {
                 git branch: 'main',
                 url: 'http://192.168.254.124: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")
                    }
                }
            }
        }
        stage('ops-deploy Checkout') {
            steps {
                 git branch: 'main',
                 url: 'http://192.168.254.124:3000/devops/ops-deploy.git',  // Git에서 코드 체크아웃
                 credentialsId: 'gogs-crd'  // Credentials ID
            }
        }
        stage('ops-deploy version update push') {
            steps {
                sh '''
                OLDVER=$(cat dev-app/VERSION)
                NEWVER=$(echo ${DOCKER_TAG})
                sed -i -e "s/$OLDVER/$NEWVER/" dev-app/timeserver.yaml
                sed -i -e "s/$OLDVER/$NEWVER/" dev-app/VERSION
                git add ./dev-app
                git config user.name "devops"
                git config user.email "a@a.com"
                git commit -m "version update ${DOCKER_TAG}"
                git push http://${GOGSCRD_USR}:${GOGSCRD_PSW}@192.168.254.124:3000/devops/ops-deploy.git
                '''
            }
        }
    }
    post {
        success {
            echo "Docker image ${DOCKER_IMAGE}:${DOCKER_TAG} has been built and pushed successfully!"
        }
        failure {
            echo "Pipeline failed. Please check the logs."
        }
    }
}

Step3. dev-app (Repo) 에서 git push 수행

더보기
# VERSION 파일 수정 : 0.0.3
# server.py 파일 수정 : 0.0.3

# git push : VERSION, server.py, Jenkinsfile
git add . && git commit -m "VERSION $(cat VERSION) Changed" && git push -u origin main

4) 동작 확인

  • Argo CD app 가 최대 3분 사이에 변경 확인 후 AutoSync 로 신규 버전 업데이트 진행 → Argo CD WebHook 설정 시 즉시 반영 가능
  • dev-app Repo 에서 한번 더 버전 업데이트 수행
더보기

☞ 개발팀 dev-app Repo 에서만 코드 업데이트 작업 시, jenkins pipeline 에서 ops-deploy Repo 에 버전 정보 업데이트를 하고, 이후 Argo CD가 자동으로 신규 버전 정보로 배포를 하게 된다.

# VERSION 파일 수정 : 0.0.4
# server.py 파일 수정 : 0.0.4

# git push : VERSION, server.py, Jenkinsfile
git add . && git commit -m "VERSION $(cat VERSION) Changed" && git push -u origin main

 

# VERSION 파일 수정 : 0.0.5
# server.py 파일 수정 : 0.0.5

# git push : VERSION, server.py, Jenkinsfile
git add . && git commit -m "VERSION $(cat VERSION) Changed" && git push -u origin main

4. Argo Rollout + K8S(Kind)

4-1. Argo Rollouts 소개 : Kubernetes Progressive Delivery Controller - Docs

더보기
  • Argo Rollouts : Argo Rollouts is a Kubernetes controller and set of CRDs which provide advanced deployment capabilities such as blue-green, canary, canary analysis, experimentation, and progressive delivery features to Kubernetes.
    • Argo Rollouts (optionally) integrates with ingress controllers and service meshes, leveraging their traffic shaping abilities to gradually shift traffic to the new version during an update. Additionally, Rollouts can query and interpret metrics from various providers to verify key KPIs and drive automated promotion or rollback during an update.
  • Why Argo Rollouts?
    • The native Kubernetes Deployment Object supports the RollingUpdate strategy which provides a basic set of safety guarantees (readiness probes) during an update. However the rolling update strategy faces many limitations:
    • Few controls over the speed of the rollout
    • Inability to control traffic flow to the new version
    • Readiness probes are unsuitable for deeper, stress, or one-time checks
    • No ability to query external metrics to verify an update
    • Can halt the progression, but unable to automatically abort and rollback the update
  • Controller Features
    • Blue-Green update strategy
[ 출처 : https://argoproj.github.io/argo-rollouts/concepts/ ]
  • Canary update strategy
[ 출처 : https://argoproj.github.io/argo-rollouts/concepts/ ]

 

  • Fine-grained, weighted traffic shifting
  • Automated rollbacks and promotions
  • Manual judgement
  • Customizable metric queries and analysis of business KPIs
  • Ingress controller integration: NGINX, ALB, Apache APISIX
  • Service Mesh integration: Istio, Linkerd, SMI
  • Simultaneous usage of multiple providers: SMI + NGINX, Istio + ALB, etc.
  • Metric provider integration: Prometheus, Wavefront, Kayenta, Web, Kubernetes Jobs, Datadog, New Relic, Graphite, InfluxDB

 

아키텍처 - Docs

더보기
[ 출처: https://argoproj.github.io/argo-rollouts/architecture/ ]

 

  • Argo Rollouts controller :
  • Rollout resource :
  • Replica sets for old and new version :
  • Ingress/Service :
  • AnalysisTemplate and AnalysisRun :
  • Metric providers :
  • CLI and UI :

 

4-2. Argo Rollouts 설치 및 Sample 테스트 - Docs

1) Argo Rollouts 설치

더보기
# 네임스페이스 생성 및 파라미터 파일 작성
cd $PWD

kubectl create ns argo-rollouts
cat <<EOT > argorollouts-values.yaml
dashboard:
  enabled: true
  service:
    type: NodePort
    nodePort: 30003
EOT

# 설치
helm install argo-rollouts argo/argo-rollouts --version 2.35.1 -f argorollouts-values.yaml --namespace argo-rollouts

# 확인
kubectl get all -n argo-rollouts
kubectl get crds

# Argo rollouts 대시보드 접속 주소 확인
echo "http://127.0.0.1:30003"
open "http://127.0.0.1:30003"

[ 실행결과 - 한 눈에 보기 ]

 

2) Deploying a Rollout

더보기
spec:
  replicas: 5
  strategy:
    canary:
      steps:
      - setWeight: 20
      - pause: {}
      - setWeight: 40
      - pause: {duration: 10}
      - setWeight: 60
      - pause: {duration: 10}
      - setWeight: 80
      - pause: {duration: 10}
# Run the following command to deploy the initial Rollout and Service:
kubectl apply -f https://raw.githubusercontent.com/argoproj/argo-rollouts/master/docs/getting-started/basic/rollout.yaml
kubectl apply -f https://raw.githubusercontent.com/argoproj/argo-rollouts/master/docs/getting-started/basic/service.yaml

# 확인
kubectl get rollout
kubectl describe rollout

kubectl get pod -l app=rollouts-demo
kubectl get svc,ep rollouts-demo
kubectl get rollouts rollouts-demo -o json | grep rollouts-demo
...
   "image": "argoproj/rollouts-demo:blue"
...

 

▶ default 네임스페이스 선택 → rollout-demo 클릭

 

 

3) Updating a Rollout

더보기

Step1. 

# Run the following command to update the rollouts-demo Rollout with the "yellow" version of the container:
kubectl argo rollouts set image rollouts-demo rollouts-demo=argoproj/rollouts-demo:yellow
kubectl edit rollouts rollouts-demo
..
     - image: argoproj/rollouts-demo:yellow
...

# 파드 label 정보 확인
watch -d kubectl get pod -l app=rollouts-demo -owide --show-labels

 

 

# 아래 입력 혹은 UI에서 Promote Yes 클릭
kubectl argo rollouts promote rollouts-demo

# 정보 확인
kubectl argo rollouts get rollout rollouts-demo --watch
kubectl get rollouts rollouts-demo -o json | grep rollouts-demo
watch -d kubectl get pod -l app=rollouts-demo -owide --show-labels

▶ (참고) 클러스터 삭제  ( 실습 완료 시, 자원 정리합시다 !! )

더보기
# 클러스터 삭제
kind delete cluster --name myk8s
docker ps
cat $KUBECONFIG
unset KUBECONFIG

[ 마무리 ]

DevOps 대표적인 툴 중 ArgoCD를 통해서 CICD 를 전략적으로, 보다 손쉽게 접근해 볼 수 있는 과정이었다. 처음에 Windows 환경 구성 시, 컨셉을 잘못 잡아서 Oracle Virtual Box + Vagrant = Kind 가상화 에서 k8s 를 띄우려 엄청(?) 뜨거운 맛을 봤습니다. ㅠㅠ 그래도, 돌이켜 보면 많은 공부가 된 것 같습니다. 짧은 시간이지만, CICD 분야에 문외한 이던 저에게 가시다님의 열강은 정말 도움이 많이 되었습니다. ArgoCD의 특성도 역시, 선언적 기능 조합을 바탕으로 막강한 CICD 배포 라인을 관리 할 수 있는 지라 시간을 좀더 두고 깊게 파 봐야 겠습니다~ ^^


[ 참고 - 링크모음 ]

더보기

▶ 공식 사이트

 - Jenkins : https://www.jenkins.io/

 - Argo CD : https://argoproj.github.io/cd/

 - Vagrant :


▶ Argo CD 와 솔루션 연계 파이프라인 구성 관련 참고자료

 - Jenkins와 Argo CD 연계 : Blog

 - Argo와 Kustomize 연계 : Blog

 - Terraform 및 Argo + Jenkins 연계 : Blog

 - 악분님 Blog : https://malwareanalysis.tistory.com/tag/ArgoCD

 


▶기타 실습 참조자료

- Vagrant 명령어 참조 : Blog

- helm 명령어 참조 : Blog

 

[ Tip & Tips : WSL ]

더보기

 가상머신과 WSL 같이 사용하는 방법

# 상태 확인하는 법
bcdedit | findstr "hypervisorlaunchtype"

# VirtualBox 실행시
bcdedit /set hypervisorlaunchtype off

# docker, wsl 실행시
bcdedit /set hypervisorlaunchtype auto

'CICD' 카테고리의 다른 글

2주차 - GitHub Actions CI/CD  (1) 2024.12.09
1주차 - Jenkins CI/CD + Docker  (1) 2024.12.01