티스토리 뷰
안녕하세요. AWS EKS Workshop Study (=AEWS) 3기 모임에서 스터디한 내용을 정리했습니다. 해당 글에서는 K8S CICD 에 대해 자세히 알아보겠습니다.
젠킨스
CICD 오픈소스 도구. 소프트웨어 개발 프로세스의 다양한 단계를 자동화하는 도구로서 중앙 소스 코드 리포지터리에서 최신 코드 가져오기, 소스 코드 컴파일, 단위 테스트 실행, 산출물을 다양한 유형으로 패키징, 산출물을 여러 종류의 환경으로 배포하기 등의 기능을 제공.
The open source automation server, support building deploying and automating any project - DockerHub , Github , Docs
Jenkins User Documentation
Jenkins – an open source automation server which enables developers around the world to reliably build, test, and deploy their software
www.jenkins.io

위와 같은 구성을 진행해보겠습니다.
실습 환경 구성
컨테이터 2대(Jenkins, gogs) : 호스트 OS 포트 노출(expose)로 접속 및 사용
Jenkins, gogs 컨테이너 기동
# 작업 디렉토리 생성 후 이동
mkdir cicd-labs
cd cicd-labs
# cicd-labs 작업 디렉토리 IDE(VSCODE 등)로 열어두기
#
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

Jenkins 컨테이너 초기 설정
# Jenkins 초기 암호 확인
docker compose exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
f347d14b02894bfdb47c37d6642ca04c
# Jenkins 웹 접속 주소 확인 : 계정 / 암호 입력 >> admin / qwe123
open "http://127.0.0.1:8080" # macOS
웹 브라우저에서 http://127.0.0.1:8080 접속 # Windows
# (참고) 로그 확인 : 플러그인 설치 과정 확인
docker compose logs jenkins -f
Jenkins URL 설정 : 각자 자신의 PC의 IP를 입력

Jenkins 컨테이너에서 호스트에 도커 데몬 사용 설정 (Docker-out-of-Docker)
- DooD는 Jenkins 컨테이너 내에서 Docker 명령을 실행할 수 있도록, 호스트 머신의 Docker 데몬을 공유하는 방법입니다. 이를 통해 Jenkins 컨테이너는 별도의 Docker 데몬을 설치하지 않고도 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 # macOS(Container)
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
# jenkins user로 docker 명령 실행 확인
docker compose exec jenkins id
docker compose exec jenkins docker info
docker compose exec jenkins docker ps

mac 재부팅 시에 jenkins 컨테이너에서 docker 실행 실패 시 : 소켓 파일에 docker 그룹을 다시 지정

# 소켓 파일에 그룹이 다시 원상태로 복귀...
docker compose exec --privileged -u root jenkins ls -l /var/run/docker.sock
# 소켓 파일에 docker 그룹을 다시 지정
docker compose exec --privileged -u root jenkins chgrp docker /var/run/docker.sock
# 확인
docker compose exec jenkins docker info
Gogs
Gogs is a painless self-hosted Git service - Github , DockerHub , Docs
Gogs 컨테이너 초기 설정 : Repo(Private) - dev-app , ops-deploy
# 초기 설정 웹 접속
open "http://127.0.0.1:3000/install" # macOS


[Token 생성] 로그인 후 → Your Settings → Applications : Generate New Token 클릭 - Token Name(devops) ⇒ Generate Token 클릭 : 메모

- 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 작업
# (옵션) GIT 인증 정보 초기화
git credential-cache exit
#
git config --list --show-origin
#
TOKEN=<각자 Gogs Token>
TOKEN=3e3882af4b7b732cc1f7a313bc98fa09173ef2bc
MyIP=<각자 자신의 PC IP> # Windows (WSL2) 사용자는 자신의 WSL2 Ubuntu eth0 IP 입력 할 것!
MyIP=192.168.254.127
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
...

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

- 계정 → Account settings → Security → Personal access tokens → Generate new token : 만료일(편한대로), Access permissions(Read, Write, Delete)

Jenkins CI + K8S(Kind)
kind 로 k8s 배포
# 클러스터 배포 전 확인
docker ps
# Create a cluster with kind
MyIP=<각자 자신의 PC IP>
MyIP=192.168.254.127
# cicd-labs 디렉터리에서 아래 파일 작성
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.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 파일 확인
cat ~/.kube/config

Jenkins 설정 : Plugin 설치, 자격증명 설정
- Jenkins Plugin 설치
- 자격증명 설정 : Jenkins 관리 → Credentials → Globals → Add Credentials
- Gogs Repo 자격증명 설정 : gogs-crd
- Kind : Username with password
- Username : devops
- Password : <Gogs 토큰>
- ID : gogs-crd
- 도커 허브 자격증명 설정 : dockerhub-crd
- Kind : Username with password
- Username : <도커 계정명>
- Password : <도커 계정 암호 혹은 토큰>
- ID : dockerhub-crd
- k8s(kind) 자격증명 설정 : k8s-crd
- Kind : Secret file
- File : <kubeconfig 파일 업로드> cp ~/.kube/config ./kube-config
- ID : k8s-crd
- Gogs Repo 자격증명 설정 : gogs-crd
Jenkins Item 생성(Pipeline) : item name(pipeline-ci)

아래 스크립트 입력
pipeline {
agent any
environment {
DOCKER_IMAGE = 'yhshim17/dev-app' // Docker 이미지 이름
}
stages {
stage('Checkout') {
steps {
git branch: 'main',
url: 'http://192.168.0.10: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 확인
- 도커 허브 확인

'IT > Container&k8s' 카테고리의 다른 글
AWS VPC CNI (1) | 2024.11.03 |
---|---|
K8S Cilium CNI (4) | 2024.10.27 |
K8S Service Mesh Istio (6) | 2024.10.20 |
K8s Gateway API (0) | 2024.10.13 |
K8s Ingress(L7) (6) | 2024.10.13 |
- Total
- Today
- Yesterday
- IaC
- controltower
- k8s
- GKE
- OS
- k8s calico
- 도서
- cni
- VPN
- security
- operator
- k8s cni
- 혼공챌린지
- 파이썬
- AWS
- EKS
- NW
- NFT
- S3
- autoscaling
- 혼공단
- SDWAN
- AI
- gcp serverless
- cloud
- terraform
- PYTHON
- GCP
- 혼공파
- handson
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |