Jenkins CI/CD + Docker

Twoeyes·2024년 12월 8일
0

환경

  • WSL2 (윈도우에서 리눅스 환경을 사용하기 위함)
  • Ubuntu
  • Docker

  • Ubuntu 에서 vscode 실행하기
code .

도커 환경 테스트

python으로 특정 문자열 출력

ubuntu 에서 진행함.

# 코드 작성
mkdir 1.1 && cd 1.1

echo "print ('Hello Docker')" > hello.py

cat > Dockerfile <<EOF
FROM python:3
COPY . /app
WORKDIR /app 
CMD python3 hello.py
EOF

# 컨테이너 이미지 빌드
docker pull python:3
docker build . -t hello
docker image ls -f reference=hello

# 컨테이너 실행
docker run --rm hello

docker build . -t hello:1
docker image ls -f reference=hello
docker tag hello:1 hello:latest
docker image ls -f reference=hello

docker run --rm hello:1

자그마치 21초나 걸렸다.

21초나 걸렸다.

  • docker tag hello:1 hello:latest
    태그 1이 달린 놈을 latest를 달아서 하나 더!
    사실상 태그를 하나 더 붙혀주는 느낌.

  • docker run --rm
    도커 컨테이너 종료시 컨테이너가 자동으로 삭제
    (기존엔 그냥 종료 처리함. 남아있음 / print 하는 내용이 전부였으므로, ps 시 확인되지 않음.)

?? 고민해봐

로컬로 애플리케이션을 미리 컴파일 하고 추가 할 수 있고, 실습 예시 처럼 Dockerfile 에서 직접 컴파일 할 수 있음.
어떤 방법이 각자 회사에 개발자분들이 선호하는지, 그리고 CI/CD 전체 과정에서 보면 어떤 방법이 적합한지, 보안적으로 문제는 없는지 고민해보세요.

개인적으로 멀티스테이지로 컴파일하는게 제일 좋을 것 같다.

  • 동작 환경을 가볍게
  • Docker의 목표인 일관된 환경을 유지
  • 보안이 중요한 부분은 이미지에 같이 섞여 들어가지 않도록 신경쓰기

도커 이미지 레이어 분석 도구

'dive'
github : https://github.com/wagoodman/dive/releases

# 맥 환경
brew install dive

# 윈도우 환경(gpt 자료 -> scoop 사용 or github[https://github.com/wagoodman/dive/releases])
Set-ExecutionPolicy RemoteSigned -scope CurrentUser
iwr -useb get.scoop.sh | iex

scoop install dive

# dive 사용
dive hello:latest

멀티 플랫폼

여러 운영체제와 하드웨어 아키텍쳐 별 도커이미지 빌드.
요즈음 amd 의 시대에서 arm이 대두되는 만큼, 아키텍쳐 별 자동으로 구동될 수 있게, 멀티 플랫폼을 설정할 수 있다.

# 활성화
docker buildx create --use
# 플랫폼 설정
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .
[참고] 우아한 테크코스 발표 유튜브

https://www.youtube.com/watch?v=dLap7vclQvo

Jenkins / gogs 설치

Jenkins:
가장 널리 사용되는 오픈소스 CI/CD(지속적 통합/지속적 배포) 도구입니다
빌드, 테스트, 배포 과정을 자동화할 수 있게 해주는 자동화 서버입니다
플러그인 생태계가 매우 풍부하여 다양한 개발 도구들과 쉽게 통합할 수 있습니다
웹 인터페이스를 통해 파이프라인을 쉽게 구성하고 모니터링할 수 있습니다

Gogs:
Git 저장소를 자체 호스팅할 수 있게 해주는 가벼운 오픈소스 도구입니다
GitHub와 유사한 웹 인터페이스를 제공합니다
Go 언어로 작성되어 있어 가볍고 설치가 쉽습니다
이슈 트래킹, 위키, 풀 리퀘스트 등 기본적인 협업 기능을 제공합니다
작은 팀이나 개인 프로젝트에 적합한 Git 호스팅 솔루션입니다

# 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
  • Docker 올리기
docker compose up -d

  • Docker 를 통해 컨테이너 접속
docker compose exec jenkins bash
docker compose exec gogs bash
==
docker exec -it jenkins bash
docker exec -it gogs bash

Jenkins

  • jenkins 초기 암호 확인
docker compose exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword

# b67846bb4f444fc88d2a22d91c96acfc

http://localhost:8080

  • 플러그인 설치 로그 확인
docker compose logs jenkins -f

  • 플러그인 설치 이후 초기 세팅

Dind VS DooD

도커 컨테이너 안에서 도커를 실행하는 방식 비교.

DinD (Docker in Docker)

특징:
Jenkins 컨테이너 내부에 완전한 Docker 엔진을 설치하여 실행
완전히 격리된 환경에서 Docker 작업 수행
장점:
보안상 더 안전한 격리 환경 제공
호스트 Docker와 완전히 독립적으로 동작
단점:
성능 오버헤드가 큼
캐시 레이어를 공유할 수 없어 빌드 속도가 느림
권한 설정이 복잡하고 privileged 모드 필요

DooD (Docker outside of Docker)

특징:
Jenkins 컨테이너에서 호스트의 Docker 소켓을 마운트하여 사용
/var/run/docker.sock을 볼륨으로 마운트
장점:
성능이 더 좋음
호스트의 Docker 캐시 활용 가능
설정이 비교적 간단
단점:
보안상 취약할 수 있음 (호스트 Docker에 직접 접근)
호스트 시스템에 영향을 미칠 수 있음

Dood 차용

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

docker info
docker ps
which docker

jenkins 컨테이너 안에서 호스트의 도커를 사용할 수 있게 되었다.

# 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

permission denied가 발생한다.

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

근데 여기서 permission denied가 발생한다.
sudo를 붙히면 해결되긴 한다.(??)
-> 그룹을 1001로 수정하면 해결된다.(호스트의 docker 그룹과, 해당 컨테이너의 docker 그룹 id가 다른게 원인이다.

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

Gogs

http://127.0.0.1:3000/install

아래 상자중 application URL 부분은 본인의 로컬 IP로 채운다.

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

25e3e2d03691116a10253aabcd1786dd139ef97f

  • 신규 레포지토리 생성

  • Gogs 사용을 위한 저장소 설정 (Jenkins 안에서 git 작업)

#
docker compose exec jenkins bash
-----------------------------------
whoami
pwd

cd /var/jenkins_home/
tree

#
git config --global user.name "<Gogs 계정명>"
git config --global user.name "devops"
git config --global user.email "a@dkd.com"
git config --global init.defaultBranch main

#
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': <토큰> # 혹은 계정암호

#
tree dev-app
cd dev-app
git branch
git remote -v

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

class RequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/plain')
        self.end_headers()
        now = datetime.now()
        response_string = now.strftime("The time is %-I:%M:%S %p, CloudNeta Study.\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 add .
git commit -m "Add dev-app"
git push -u origin main

Docker hub

https://hub.docker.com/
접속해서 로그인하고 private 레포 하나 만들자.

Jenkins 알아보기

  • 작업 소개 (프로젝트, Job, Item) : 3가지 유형의 지시 사항 포함
    1. 작업을 수행하는 시점 Trigger
      • 작업 수행 태스크 task가 언제 시작될지를 지시
    2. 작업을 구성하는 단계별 태스크 Built step
      • 특정 목표를 수행하기 위한 태스크를 단계별 step로 구성할 수 있다.
      • 이것을 젠킨스에서는 빌드 스텝 build step이라고 부른다.
    3. 태스크가 완료 후 수행할 명령 Post-build action
      • 예를 들어 작업의 결과(성공 or 실패)를 사용자에게 알려주는 후속 동작이나, 자바 코드를 컴파일한 후 생성된 클래스 파일을 특정 위치로 복사 등
    • (참고) 젠킨스의 빌드 : 젠킨스 작업의 특정 실행 버전
      • 사용자는 젠킨스 작업을 여러번 실행할 수 있는데, 실행될 때마다 고유 빌드 번호가 부여된다.
      • 작업 실행 중에 생성된 아티팩트, 콘솔 로드 등 특정 실행 버전과 관련된 모든 세부 정보가 해당 빌드 번호로 저장된다.

Gogs REpo 자격증명 설정

Jenkins 관리 > Credentials > (global) > Add Credential

아까 gogs에서 가져온 토큰 값을 password에 넣는다.

profile
아 배고파

0개의 댓글