티스토리 뷰
CloudNet@ 가시다님이 진행하는 AWS EKS Hands-on Study 내용 참고.
[AWS EKS CI/CD 추천 영상] GS리테일 Amazon EKS의 모든 것: 무중단 운영부터 배포 자동화까지 - Youtube
목표 구성도
0. 실습 환경 구성
Windows (WSL2) 기준 진행
아래 1번 과정에 kind(k8s) 설치를 먼저 진행하여 docker network(kind) 생성 후 아래 Jenkins,gogs 생성
☞ 컨테이터 2대(Jenkins, gogs) : 호스트 OS 포트 노출(expose)로 접속 및 사용
- Jenkins, gogs 컨테이너 기동
# 작업 디렉토리 생성 후 이동
mkdir cicd-labs
cd cicd-labs
# cicd-labs 작업 디렉토리 IDE(VSCODE 등)로 열어두기
# kind 설치를 먼저 진행하여 docker network(kind) 생성 후 아래 Jenkins,gogs 생성 할 것
# docker network 확인 : kind 를 사용
docker network ls
...
7e8925d46acb kind bridge local
...
#
cat <<EOT > docker-compose.yaml
services:
jenkins:
container_name: jenkins
image: jenkins/jenkins
restart: unless-stopped
networks:
- kind
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:
- kind
ports:
- "10022:22"
- "3000:3000"
volumes:
- gogs-data:/data
volumes:
jenkins_home:
gogs-data:
networks:
kind:
external: true
EOT
# 배포
docker compose up -d
docker compose ps
docker inspect kind
# 기본 정보 확인
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
- Jenkins 컨테이너 초기 설정
# Jenkins 초기 암호 확인
docker compose exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
2345e3b3a2544c7993682b6d21cfc1c6
# Jenkins 웹 접속 주소 확인 : 계정 / 암호 입력 >> admin / qwe123
웹 브라우저에서 http://127.0.0.1:8080 접속 # Windows
# (참고) 로그 확인 : 플러그인 설치 과정 확인
docker compose logs jenkins -f
- Jenkins URL 설정 : 각자 http://<각자 자신의 WSL2 Ubuntu eth0 IP>:8080/ 를 입력
- Windows WSL2(Ubuntu) 에서 eth0 IP 확인 ifconfig eth0
☞ (참고) 실습 완료 후 해당 컨테이너 중지 상태로 둘 경우 → 재부팅 및 이후에 다시 실습을 위해 컨테이너 시작 시
# 실습 완료 후 해당 컨테이너 중지 상태로 둘 경우
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
- 참고) 모든 실습 후 삭제 시 : docker compose down --volumes --remove-orphans
☞ Jenkins : The open source automation server, support building deploying and automating any project - DockerHub , Github , Docs
- 최신 코드 가져오기 : 개발을 위해 중앙 코드 리포지터리에서 로컬 시스템으로 애플리케이션의 최신 코드를 가져옴
- 단위 테스트 구현과 실행 : 코드 작성 전 단위 테스트 케이스를 먼저 작성
- 코드 개발 : 실패한 테스트 케이스를 성공으로 바꾸면서 코드 개발
- 단위 테스트 케이스 재실행 : 단위 테스트 케이스 실행 시 통과(성공!)
- 코드 푸시와 병합 : 개발 소스 코드를 중앙 리포지터리로 푸시하고, 코드 병합
- 코드 병합 후 컴파일 : 변경 함수 코드가 병함되면 전체 애플리케이션이 컴파일된다
- 병합된 코드에서 테스트 실행 : 개별 테스트뿐만 아니라 전체 통합 테스트를 실행하여 문제 없는지 확인
- 아티팩트 배포 : 애플리케이션을 빌드하고, 애플리케이션 서버의 프로덕션 환경에 배포
- 배포 애플리케이션의 E-E 테스트 실행 : 셀레늄 Selenium과 같은 User Interface 자동화 도구를 통해 애플리케이션의 전체 워크플로가 정상 동작하는지 확인하는 종단간 End-to-End 테스트를 실행.
- 소프트웨어 개발 프로세스의 다양한 단계를 자동화하는 도구로서 중앙 소스 코드 리포지터리에서 최신 코드 가져오기, 소스 코드 컴파일, 단위 테스트 실행, 산출물을 다양한 유형으로 패키징, 산출물을 여러 종류의 환경으로 배포하기 등의 기능을 제공.
- 젠킨스는 아파치 톰캣처럼 서블릿 컨테이너 내부에서 실행되는 서버 시스템이다. 자바로 작성됐고, 소프트웨어 개발과 관련된 다양한 도구를 지원.
- 젠킨스는 DSL Domain Specific Language (jenkins file)로 E-E 빌드 수명 주기 단계를 구축한다.
- 젠킨스는 파이프라인이라고 부르는 스크립트를 작성할 수 있는데, 이를 사용해서 각 빌드 단계마다 젠킨스가 수행할 태스트 및 하위 태스크의 순서를 정의.
- 순차적이고 종속적인 단계가 시작부터 끝까지 실행되면 최종적으로 사용자가 실행할 수 있는 빌드가 생성됨.
- 만약 빌드 프로세스를 진행하는 중에 특정 단계에서 실패가 발생하며, 이 단계의 출력 결과를 사용하는 다음 단계는 실행되지 않으며 빌드 프로세스 전체가 실패한다.
- 다양한 Plugins 연동
- Build Plugins : Maven, Ant, Gradle …
- VCS Plugins : Git, SVN …
- Languages Plugins : Java, Python, Node.js …
- CI(지속적 제공)/CD(지속적 배포) 워크플로 예제 : Continuous Integration Server + Continuous Development, Build, Test, Deploy - Link
☞ 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 1001 -f docker # Windows WSL2(Container) >> cat /etc/group 에서 docker 그룹ID를 지정
chgrp docker /var/run/docker.sock
ls -l /var/run/docker.sock
usermod -aG docker jenkins
cat /etc/group | grep docker
exit
--------------------------------------------
# Jenkins 컨테이너 재기동으로 위 설정 내용을 Jenkins app 에도 적용 필요
docker compose restart jenkins
# jenkins user로 docker 명령 실행 확인
docker compose exec jenkins id
docker compose exec jenkins docker info
docker compose exec jenkins docker ps
- (참고) Windows : 도커 소켓을 Jenkins 컨테이너 공유 시 ⇒ groupadd -g 1001 -f docker # Windows WSL2(Container) >> cat /etc/group 에서 docker 그룹ID를 지정.
☞ Gogs 컨테이너 초기 설정 : Repo(Private) - dev-app , ops-deploy
- 초기 설정 웹 접속
# 초기 설정 웹 접속
웹 브라우저에서 http://127.0.0.1:3000/install 접속 # Windows
- 초기 설정
- 데이터베이스 유형 : SQLite3
- 애플리케이션 URL : http://**<각자 자신의 WSL2 Ubuntu eth0 IP>**:3000/
- 기본 브랜치 : main
- 관리자 계정 설정 클릭 : 이름(계정명 - 닉네임 사용 devops), 비밀번호(계정암호 qwe123), 이메일 입력
→ Gogs 설치하기 클릭 ⇒ 관리자 계정으로 로그인 후 접속
[Token 생성] 로그인 후 → Your Settings → Applications : Generate New Token 클릭 - Token Name(devops) ⇒ Generate Token 클릭 : 메모해두기!
5550f327f10665f428ab5719ed7f462628cf785a
- New Repository 1 : 개발팀용
- Repository Name : dev-app
- Visibility : (Check) This repository is Private
- .gitignore : Python
- Readme : Default → (Check) initialize this repository with selected files and template
- New Repository 2 : 데브옵스팀용
- Repository Name : ops-deploy
- Visibility : (Check) This repository is Private
- .gitignore : Python
- Readme : Default → (Check) initialize this repository with selected files and template
☞ Gogs 실습을 위한 저장소 설정 : 호스트에서 직접 git 작업 , Windows 경우 WSL2 혹은 호스트에서 작업 둘 다 가능
# (옵션) GIT 인증 정보 초기화
git credential-cache exit
#
git config --list --show-origin
#
TOKEN=<각자 Gogs Token>
TOKEN=5550f327f10665f428ab5719ed7f462628cf785a
MyIP=<각자 자신의 PC IP> # Windows (WSL2) 사용자는 자신의 WSL2 Ubuntu eth0 IP 입력 할 것!
MyIP=172.24.254.126
git clone <각자 Gogs dev-app repo 주소>
git clone http://devops:$TOKEN@$MyIP:3000/devops/dev-app.git
Cloning into 'dev-app'...
...
#
cd dev-app
#
git --no-pager config --local --list
git config --local user.name "devops"
git config --local user.email "a@a.com"
git config --local init.defaultBranch main
git config --local credential.helper store
git --no-pager config --local --list
cat .git/config
#
git --no-pager 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):
match self.path:
case '/':
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.respond_with(200, response_string)
case '/healthz':
self.respond_with(200, "Healthy")
case _:
self.respond_with(404, "Not Found")
def respond_with(self, status_code: int, content: str) -> None:
self.send_response(status_code)
self.send_header('Content-type', 'text/plain')
self.end_headers()
self.wfile.write(bytes(content, "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
# (참고) python 실행 확인
python3 server.py
curl localhost
curl localhost/healthz
CTRL+C 실행 종료
# 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
#
tree
git status
git add .
git commit -m "Add dev-app"
git push -u origin main
...
- Gogs Repo 에서 확인
- (예시) App Version - Blog
☞ 도커 허브 소개 - Docs사용자들은 도커 허브에 이미지를 업로드하고, 다른 곳에서 자유롭게 재사용(다운로드)할 수 있습니다.
- 단, 도커 허브는 누구나 이미지를 올릴 수 있기 때문에 공식(Official) 라벨이 없는 이미지는 사용법을 찾을 수 없거나 제대로 동작하지 않을 수 있습니다.
- [정보] 도커 악성 이미지를 통한 취약점 공격 - 기사 모음
- Docker Hub is also where you can go to carry out administrative tasks for organizations. If you have a Docker Team or Business subscription, you can also carry out administrative tasks in the Docker Admin Console.
- Key features
- Repositories: Push and pull container images.
- Builds: Automatically build container images from GitHub and Bitbucket and push them to Docker Hub.
- Webhooks: Trigger actions after a successful push to a repository to integrate Docker Hub with other services.
- Docker Hub CLI tool (currently experimental) and an API that allows you to interact with Docker Hub.
- Browse through the Docker Hub API documentation to explore the supported endpoints.
- Administrative task
- Create and manage teams and organizations
- Create a company
- Enforce sign in
- Set up SSO and SCIM
- Use Group mapping
- Carry out domain audits
- Use Image Access Management to control developers' access to certain types of images
- Turn on Registry Access Management
- Docker Hub Webhooks - Docs
- You can use webhooks to cause an action in another service in response to a push event in the repository. Webhooks are POST requests sent to a URL you define in Docker Hub.
- Set up Automated Builds : 저장소를 구성하여 소스 공급자에게 새 코드를 푸시할 때마다 자동으로 이미지를 빌드 - Docs
- Automated builds require a Docker Pro, Team, or Business subscription.
- 여러 사용자가 자신이 만든 도커 이미지를 서로 자유롭게 공유할 수 있는 장을 마련해 줍니다.
- 도커 허브(Docker Hub)는 도커 이미지 원격 저장소입니다.
1. Jenkins CI + K8S(Kind)
Windows (WSL2) 기준 진행
☞ kind 설치
- WSL 2 설치 + Ubuntu 배포판 설치 - Docs , 설명서
- [Linux] Windows 10에 WSL2 우분투 설치하기
- Powershell 관리자 권한으로 실행
- [Linux] Windows 10에 WSL2 우분투 설치하기
# DISM(배포 이미지 서비스 및 관리) 명령어로 Microsoft-Windows-Subsystem-Linux 기능을 활성화
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
# DISM 명령어로 VirtualMachinePlatform 기능을 활성화
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
# wsl 설치
wsl --install
# 기본값 WSL 2 버전 설정
wsl --set-default-version 2
# wsl 업데이트
wsl --update
- 윈도우 OS 재부팅
- Powershell 관리자 권한으로 실행
# 설치 가능한 배포판 확인
wsl --list --online
# Ubuntu 배포판 설치
wsl --install Ubuntu-24.04
...
Enter new UNIX username: <각자 Ubuntu 사용 계정>
New password: <해당 계정 암호>
Retype new password: <해당 계정 암호>
passwd: password updated successfully
Installation successful!
To run a command as administrator (user "root"), use "sudo <command>".
---------------------------------------
# 기본 정보 확인
hostnamectl
whoami
id
pwd
# apt 업데이트
sudo apt update
sudo apt install jq htop curl wget ca-certificates net-tools -y
ifconfig eth0
ping -c 1 8.8.8.8
# 빠져나오기
$ exit
---------------------------------------
# 설치된 배포판 확인
wsl -l -v
# Ubuntu 재진입
wsl
- WSL2 에 Docker 설치 - Docs
# WSL2 에 Docker 설치 : 아래 스크립트 실행 후 20초 대기하면 스크립트 실행 됨
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh ./get-docker.sh
...
# 설치 확인
docker info
docker ps
sudo systemctl status docker
cat /etc/group | grep docker
- (옵션) VSCODE 설치 - Download , WSL-VSCODE
- WSL 배포에서 프로젝트를 열려면 배포의 명령줄을 열고 다음을 입력합니다. code .
code .
Installing VS Code Server for Linux x64 (f1a4fb101478ce6ec82fe9627c43efbf9e98c813)
Downloading: 100%
Unpacking: 100%
Unpacked 1773 files and folders to /home/gasida/.vscode-server/bin/f1a4fb101478ce6ec82fe9627c43efbf9e98c813.
Looking for compatibility check script at /home/gasida/.vscode-server/bin/f1a4fb101478ce6ec82fe9627c43efbf9e98c813/bin/helpers/check-requirements.sh
Running compatibility check script
Compatibility check successful (0)
- VSCODE 개발 편리 설정 : auto-save 등등
- WSL2 에 kind 및 관리 툴 설치
# 기본 사용자 디렉터리 이동
cd $PWD
pwd
#
sudo systemctl stop apparmor && sudo systemctl disable apparmor
#
sudo apt update && sudo apt-get install bridge-utils net-tools jq tree unzip kubectx kubecolor -y
# Install Kind
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.27.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind
kind --version
# Install kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv ./kubectl /usr/bin
sudo kubectl version --client=true
# Install Helm
curl -s https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
helm version
# Source the completion
source <(kubectl completion bash)
echo 'source <(kubectl completion bash)' >> ~/.bashrc
# Alias kubectl to k
echo 'alias k=kubectl' >> ~/.bashrc
echo 'complete -o default -F __start_kubectl k' >> ~/.bashrc
# Install Kubeps & Setting PS1
git clone https://github.com/jonmosco/kube-ps1.git
echo -e "source $PWD/kube-ps1/kube-ps1.sh" >> ~/.bashrc
cat <<"EOT" >> ~/.bashrc
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
# .bashrc 적용을 위해서 logout 후 터미널 다시 접속 하자
exit
- kind 기본 사용 - 클러스터 배포 및 확인
# 클러스터 배포 전 확인
docker ps
# Create a cluster with kind
kind create cluster
# 클러스터 배포 확인
kind get clusters
kind get nodes
kubectl cluster-info
# 노드 정보 확인
kubectl get node -o wide
# 파드 정보 확인
kubectl get pod -A
kubectl get componentstatuses
# 컨트롤플레인 (컨테이너) 노드 1대가 실행
docker ps
docker images
# kube config 파일 확인
cat ~/.kube/config
혹은
cat $KUBECONFIG # KUBECONFIG 변수 지정 사용 시
# nginx 파드 배포 및 확인 : 컨트롤플레인 노드인데 파드가 배포 될까요?
kubectl run nginx --image=nginx:alpine
kubectl get pod -owide
# 노드에 Taints 정보 확인
kubectl describe node | grep Taints
Taints: <none>
# 클러스터 삭제
kind delete cluster
# kube config 삭제 확인
cat ~/.kube/config
혹은
cat $KUBECONFIG # KUBECONFIG 변수 지정 사용 시
☞ kind 로 k8s 배포
- 기본 정보 확인
# 클러스터 배포 전 확인
docker ps
mkdir cicd-labs
cd ~/cicd-labs
# WSL2 Ubuntu eth0 IP를 지정
ifconfig eth0
MyIP=<각자 자신의 WSL2 Ubuntu eth0 IP>
MyIP=172.24.254.126
# cicd-labs 디렉터리에서 아래 파일 작성
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.32.2
# 확인
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 파일 확인 : "server: https://172.19.21.65:35413" 부분에 접속 주소 잘 확인해두자!
cat ~/.kube/config
ls -l ~/.kube/config
- kube-ops-view
# 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=30001 --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
# kube-ops-view 접속 URL 확인 (1.5 , 2 배율) : 아래 접속 주소를 자신의 웹 브라우저에서 접속할 것!
echo "http://127.0.0.1:30001/#scale=1.5"
echo "http://127.0.0.1:30001/#scale=2"
- (참고) 클러스터 삭제
# 클러스터 삭제
kind delete cluster --name myk8s
docker ps
cat ~/.kube/config
☞ 작업 소개 (프로젝트, Job, Item) : 3가지 유형의 지시 사항 포함
- 작업을 수행하는 시점 Trigger
- 작업 수행 태스크 task가 언제 시작될지를 지시
- 작업을 구성하는 단계별 태스크 Built step
- 특정 목표를 수행하기 위한 태스크를 단계별 step로 구성할 수 있다.
- 이것을 젠킨스에서는 빌드 스텝 build step이라고 부른다.
- 태스크가 완료 후 수행할 명령 Post-build action
- 예를 들어 작업의 결과(성공 or 실패)를 사용자에게 알려주는 후속 동작이나, 자바 코드를 컴파일한 후 생성된 클래스 파일을 특정 위치로 복사 등
- (참고) 젠킨스의 빌드 : 젠킨스 작업의 특정 실행 버전
- 사용자는 젠킨스 작업을 여러번 실행할 수 있는데, 실행될 때마다 고유 빌드 번호가 부여된다.
- 작업 실행 중에 생성된 아티팩트, 콘솔 로드 등 특정 실행 버전과 관련된 모든 세부 정보가 해당 빌드 번호로 저장된다.
☞ Jenkins 설정 : Plugin 설치, 자격증명 설정
- Jenkins Plugin 설치
- 자격증명 설정 : 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 파일은 메모장으로 직접 작성 후 업로드 하자
☞ Jenkins Item 생성(Pipeline) : item name(pipeline-ci)
- Pipeline script : 자신의 도커 허브 계정과 IP는 환경에 맞게 수정 할 것!
- 신의 WSL2 Ubuntu eth0 IP 입력 할 것!
pipeline {
agent any
environment {
DOCKER_IMAGE = '<자신의 도커 허브 계정>/dev-app' // Docker 이미지 이름
}
stages {
stage('Checkout') {
steps {
git branch: 'main',
url: 'http://<자신의 집 IP>: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."
}
}
}
pipeline {
agent any
environment {
DOCKER_IMAGE = 'sspp30578/dev-app' // Docker 이미지 이름
}
stages {
stage('Checkout') {
steps {
git branch: 'main',
url: 'http://172.24.254.126: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 확인
- 도커 허브 확인
☞ k8s Deploying an application with Jenkins(pipeline-ci)
Deploying to Kubernetes
- 원하는 상태 설정 시 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.
# 디플로이먼트 오브젝트 배포 : 리플리카(파드 2개), 컨테이너 이미지 >> 아래 도커 계정 부분만 변경해서 배포해보자
DHUSER=<도커 허브 계정명>
DHUSER=sspp30578
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
livenessProbe:
initialDelaySeconds: 30
periodSeconds: 30
httpGet:
path: /healthz
port: 80
scheme: HTTP
timeoutSeconds: 5
failureThreshold: 3
successThreshold: 1
EOF
watch -d kubectl get deploy,rs,pod -o wide
# 배포 상태 확인 : kube-ops-view 웹 확인
kubectl get events -w --sort-by '.lastTimestamp'
kubectl get deploy,pod -o wide
kubectl describe pod
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 8m11s default-scheduler Successfully assigned default/timeserver-79b6b454cc-vfljw to myk8s-worker2
Normal Pulling 5m30s (x5 over 8m10s) kubelet Pulling image "docker.io/sspp30578/dev-app:0.0.1"
Warning Failed 5m29s (x5 over 8m11s) kubelet Failed to pull image "docker.io/sspp30578/dev-app:0.0.1": failed to pull and unpack image "docker.io/sspp30578/dev-app:0.0.1": failed to resolve reference "docker.io/sspp30578/dev-app:0.0.1": pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed
Warning Failed 5m29s (x5 over 8m11s) kubelet Error: ErrImagePull
Warning Failed 3m25s (x20 over 8m10s) kubelet Error: ImagePullBackOff
Normal BackOff 3m13s (x21 over 8m10s) kubelet Back-off pulling image "docker.io/sspp30578/dev-app:0.0.1"
TROUBLESHOOTING : 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
DHUSER=sspp30578
DHPASS=dckr_pat_1q66VHPXvtPcacQp6Wx9GSxxxxx
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=eyJhdXRocyI6eyJodHRwczovL2luZGV4LmRvY2tlci5pby92MS8iOnsidXNlcm5hbWUiOiJzc3BwMzA1NzgiLCJwYXNzd29yZCI6ImRja3JfcGF0XzFxNjZWSFBYdnRQY2FjUXA2V3g5R1NiS1M1cyIsImF1dGgiOiJjM053Y0RNd05UYzRPbVJqYTNKZmNHRjBYekZ4TmpaV1NGQllkblJRWTJGalVYQTJWM2c1UjFOaVxxxxx==
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
livenessProbe:
initialDelaySeconds: 30
periodSeconds: 30
httpGet:
path: /healthz
port: 80
scheme: HTTP
timeoutSeconds: 5
failureThreshold: 3
successThreshold: 1
imagePullSecrets:
- name: dockerhub-secret
EOF
watch -d kubectl get deploy,rs,pod -o wide
# 확인
kubectl get events -w --sort-by '.lastTimestamp'
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.4
kubectl exec -it curl-pod -- curl $PODIP1
kubectl exec -it curl-pod -- curl $PODIP1/healthz
# 로그 확인
kubectl logs deploy/timeserver
kubectl logs deploy/timeserver -f
kubectl stern deploy/timeserver
kubectl stern -l pod=timeserver-pod
- 파드 1개 삭제 후 동작 확인 → 접속 확인
#
POD1NAME=<파드 1개 이름>
POD1NAME=timeserver-58cdf9f495-hhq97
kubectl get pod -owide
kubectl delete pod $POD1NAME && kubectl get pod -w
# 셀프 힐링 , 파드 IP 변경 -> 고정 진입점(고정 IP/도메인네임) 필요 => Service
kubectl get deploy,rs,pod -owide
Publishing your Service
- 각 Pod는 클러스터 내부에서 통신할 수 있도록 클러스터-로컬(내부) IP 주소를 부여받습니다. 이 IP는 클러스터 내의 다른 Pod들과의 통신에 사용됩니다.
- Pod를 인터넷에 직접 노출하거나 노드의 IP를 통해 노출하는 것(hostPort 필드 사용)도 가능하지만, 실시간 게임 서버처럼 특별한 경우가 아니라면 이런 방식은 잘 사용되지 않습니다.
- 일반적으로는, 특히 Deployment를 사용할 경우, 여러 개의 Pod를 하나의 Service로 묶어서 내부 IP(필요에 따라 외부 IP 포함)를 가진 단일 접근 지점을 제공하고, 이 서비스가 요청을 여러 Pod로 로드 밸런싱합니다.
- 디플로이먼트에 파드가 1개뿐이더라도 서비스를 통해 안정적인 주소를 제공하는 것이 좋습니다.
- Deployment에 단 하나의 Pod만 있어도, 안정적인 주소를 제공하기 위해 Service를 생성하는 것이 좋습니다.
- 로드 밸런싱 외에도, Service는 어떤 Pod가 실행 중이며 트래픽을 받을 수 있는지 추적합니다. 예를 들어, Deployment에서 복제 수를 3으로 설정했더라도, 항상 3개의 복제본이 사용 가능한 것은 아닙니다.
- 예를 들어, 노드 업그레이드 등의 이벤트가 발생하면 실제로는 2개만 실행 중일 수도 있고, 새 버전을 배포 중이라면 일시적으로 3개를 초과할 수도 있습니다. Service는 정상적으로 실행 중인 Pod에만 트래픽을 라우팅합니다. (다음 챕터에서는 이를 원활히 작동시키기 위해 제공해야 할 주요 정보들을 다룰 것입니다.)
- Service는 클러스터 내부에서 여러 애플리케이션 간 통신(소위 마이크로서비스 아키텍처)을 가능하게 하기 위해 사용되며, 서비스 디스커버리 같은 편리한 기능을 제공합니다.
- Kubernetes에서 각 Pod와 Service는 자신만의 내부 클러스터 IP를 가지므로, Pod 간 포트 충돌에 대해 걱정할 필요가 없습니다.
- 또한, 이 Service에는 selector라는 섹션이 있다는 점을 주목하십시오. 이는 우리의 Deployment에도 있던 것입니다.
- Service는 Deployment를 직접 참조하지 않으며, Deployment에 대한 정보를 알지 못합니다.
- 대신, Service는 주어진 label을 가진 Pod 집합을 참조합니다. 이 경우에는 우리의 Deployment가 생성한 Pod들이 해당 label을 가지게 됩니다.
- Deployment 오브젝트와는 달리, selector 섹션에는 matchLabels 하위 섹션이 없습니다.
- 그러나 두 방식은 동일한 역할을 합니다. Deployment는 Kubernetes에서 더 표현력이 풍부한 최신 문법을 사용하는 것일 뿐입니다.
- Deployment와 Service에 있는 selector는 같은 목적, 즉 해당 오브젝트가 참조할 Pod 집합을 지정하는 것을 수행합니다.
# 서비스 생성
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.190.225 <none> 80:30000/TCP 4s pod=timeserver-pod
NAME ENDPOINTS AGE
endpoints/timeserver 10.244.1.6:80,10.244.2.5:80 4s
# Service(ClusterIP)로 접속 확인 : 도메인네임, ClusterIP
kubectl exec -it curl-pod -- curl timeserver
kubectl exec -it curl-pod -- curl timeserver/healthz
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
curl http://127.0.0.1:30000/healthz
# 반복 접속 해두기 : 부하분산 확인
while true; do curl -s --connect-timeout 1 http://127.0.0.1:30000 ; 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 ; 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
Updating your application**
- 샘플 앱 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 ; 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"
# 롤링 업데이트 확인
watch -d kubectl get deploy,rs,pod,svc,ep -owide
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
- 이 경우에는 세 개 모두 준비되어 있습니다. 하지만 UP-TO-DATE 열은 이 중 하나의 Pod만 현재 버전임을 나타냅니다.
- 이는 모든 Pod를 한 번에 교체하여 애플리케이션에 중단 시간(downtime)이 발생하지 않도록 하기 위해, 기본적으로 Kubernetes는 롤링 업데이트 전략(rolling update strategy)을 사용하기 때문입니다. 즉, Pod을 한 번에 하나씩 또는 일부씩 순차적으로 업데이트합니다.
☞ Gogs Webhooks 설정 : Jenkins Job Trigger
- gogs 에 /data/gogs/conf/app.ini 파일 수정 후 컨테이너 재기동 - issue
[security]
INSTALL_LOCK = true
SECRET_KEY = 3a1Cdkncvs0WoTa
LOCAL_NETWORK_ALLOWLIST = 172.24.254.126 # 각자 자신의 PC IP , Windows (WSL2) 사용자는 자신의 WSL2 Ubuntu eth0 IP
- docker compose restart gogs
- gogs 에 Webhooks 설정 : Jenkins job Trigger - Setting → Webhooks → 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://<자신의 집 IP>:8080/gogs-webhook/?job=SCM-Pipeline/ , http://172.24.254.126:8080/gogs-webhook/?job=SCM-Pipeline/
- Windows (WSL2) 사용자는 자신의 WSL2 Ubuntu eth0 IP
- Content Type : application/json
- Secret : qwe123
- When should this webhook be triggered? : Just the push event
- Active : Check
⇒ Add webhook
- (TS) gogs 에 Webhooks 동작이 잘 되지 않을 경우 /data/gogs/conf/app.ini 파일 내에 아래 항목 확인 해볼것.
[server]
DOMAIN = localhost
HTTP_PORT = 3000
EXTERNAL_URL = http://172.24.254.126:3000/
DISABLE_SSH = false
SSH_PORT = 22
START_SSH_SERVER = false
OFFLINE_MODE = false
[email]
ENABLED = false
[auth]
REQUIRE_EMAIL_CONFIRMATION = false
DISABLE_REGISTRATION = false
ENABLE_REGISTRATION_CAPTCHA = true
REQUIRE_SIGNIN_VIEW = false
[user]
ENABLE_EMAIL_NOTIFICATION = false
[picture]
DISABLE_GRAVATAR = false
ENABLE_FEDERATED_AVATAR = false
[session]
PROVIDER = file
[log]
MODE = file
LEVEL = Info
ROOT_PATH = /app/gogs/log
[security]
INSTALL_LOCK = true
SECRET_KEY = 3a1Cdkncvs0WoTa
LOCAL_NETWORK_ALLOWLIST = 172.24.254.126
☞ Jenkins Item 생성(Pipeline) : item name(SCM-Pipeline)
설정 항목 | GitHub Project | 소스 관리 - Git |
위치 | Item 설정 상단 | Source Code Management 섹션 |
주 목적 | GitHub 프로젝트 웹 연동, Webhook 설정 | 빌드를 위한 소스 코드 관리 |
필수 여부 | 선택적 설정 | 필수 설정 |
주요 기능 | 프로젝트 링크, GitHub Webhook | 리포지토리 URL, 브랜치 선택, 인증 설정 |
by ChatGPT
- GitHub project : http://<자신의 IP>:3000/<Gogs 계정명>/dev-app ← .git 은 제거
- *GitHub project : http://172.24.254.126:3000/devops/dev-app*
- 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
- SCM : Git
☞ Jenkinsfile 작성 후 Git push
- git 작업
# Jenkinsfile 빈 파일 작성
touch Jenkinsfile
# VERSION 파일 : 0.0.3 수정
# server.py 파일 : 0.0.3 수정
- IDE(VSCODE..) 로 Jenkinsfile 파일 작성
pipeline {
agent any
environment {
DOCKER_IMAGE = '<자신의 도커 허브 계정>/dev-app' // Docker 이미지 이름
}
stages {
stage('Checkout') {
steps {
git branch: 'main',
url: 'http://<자신의 집 IP>: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."
}
}
}
pipeline {
agent any
environment {
DOCKER_IMAGE = 'sspp30578/dev-app' // Docker 이미지 이름
}
stages {
stage('Checkout') {
steps {
git branch: 'main',
url: 'http://172.24.254.126: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."
}
}
}
- 작성된 파일 push
git add . && git commit -m "VERSION $(cat VERSION) Changed" && git push -u origin main
- Gogs WebHook 기록 확인
- 도커 저장소 확인
- Jenkins 트리거 빌드 확인
- k8s 에 신규 버전 적용
# 신규 버전 적용
kubectl set image deployment timeserver timeserver-container=$DHUSER/dev-app:0.0.3 && while true; do curl -s --connect-timeout 1 http://127.0.0.1:30000 ; sleep 1 ; done
# 확인
watch -d "kubectl get deploy,ep timeserver; echo; kubectl get rs,pod"
2. Jenkins CI/CD + K8S(Kind)
☞ Jenkins 컨테이너 내부에 툴 설치 : kubectl(v1.32), helm
# Install kubectl, helm
docker compose exec --privileged -u root jenkins bash
--------------------------------------------
#curl -LO "https://dl.k8s.io/release/v1.32.2/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
☞ Jenkins Item 생성(Pipeline) : item name(k8s-cmd)
pipeline {
agent any
environment {
KUBECONFIG = credentials('k8s-crd')
}
stages {
stage('List Pods') {
steps {
sh '''
# Fetch and display Pods
kubectl get pods -A --kubeconfig "$KUBECONFIG"
'''
}
}
}
}
☞ [K8S CD 실습] Jenkins 를 이용한 blue-green 배포 준비
- 디플로이먼트 / 서비스 yaml 파일 작성 - http-echo 및 코드 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
#
tree
git add . && git commit -m "Add echo server yaml" && git push -u origin main
☞ (참고) 직접 블루-그린 업데이트 실행
#
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 ..
☞ Jenkins Item 생성(Pipeline) : item name(k8s-bluegreen) - Jenkins 통한 k8s 기본 배포
- 이전 실습에 디플로이먼트, 서비스 삭제
kubectl delete deploy,svc timeserver
- 반복 접속 미리 실행
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
- pipeline script
pipeline {
agent any
environment {
KUBECONFIG = credentials('k8s-crd')
}
stages {
stage('Checkout') {
steps {
git branch: 'main',
url: 'http://<자신의 집 IP>: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"
}
}
}
}
}
}
pipeline {
agent any
environment {
KUBECONFIG = credentials('k8s-crd')
}
stages {
stage('Checkout') {
steps {
git branch: 'main',
url: 'http://172.24.254.126: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"
}
}
}
}
}
}
- 지금 배포 후 동작 확인
- 실습 완료 후 삭제
kubectl delete deploy echo-server-blue echo-server-green && kubectl delete svc echo-server-service
'AEWS study' 카테고리의 다른 글
9주차 - EKS Upgrade (0) | 2025.03.30 |
---|---|
8주차 - K8S CI/CD - #2 (0) | 2025.03.29 |
7주차 - EKS Mode/Nodes - #2 (0) | 2025.03.22 |
7주차 - EKS Mode/Nodes - #1 (0) | 2025.03.20 |
6주차 - EKS Security - #2 (0) | 2025.03.15 |