티아카데미 Jenkins를 활용한 CICD TIL - CICD & Jenkins 기본개념

2020-12-13

.

Data_Engineering_TIL(20201213)

study_program : 티아카데미 Jenkins를 활용한 CI/CD

URL : https://tacademy.skplanet.com/frontMain.action

  • 강의목표

1) CI/CD 파이프라인의 기본개념에 대해서 이해한다.

2) 기본적인 운영환경 (DEV, QA, PROD)이 어떻게 구성되고 운영되는지 이해한다.

사용자가 쓰는 프로덕션 환경뿐만 아니라 개발을 할때 개발자들은 일반적으로 본인들의 로컬 피시에서 개발을 하고, 개발을 한 결과물을 내부사용자 (프론트엔드 엔지니어, 백앤드 엔지니어, QA 엔지니어 등등) 들에게 전달을 하기 위해서 DEV 또는 QA 환경에서 공유가 될 것이다. 따라서 단순히 사용자가 사용하는 프로덕션 환경뿐만 아니라 개발자들이 쓰는 환경들까지 일반적으로 구축을 해서 개발을 하게된다. 그래서 CI/CD 파이프라인이라고 하면 단순하게 개발을 해서 사용자에게 배포하는것 뿐만 아니라 우리가 개발한 product를 내부사용자(QA 엔지니어, 백엔드 엔지니어, 프론트앤드 엔지니어 등등)에게 지속적으로 공유하는 것이 CI/CD의 목적이라고 할 수 있다.

3) Jenkins의 기본개념에 대해 이해한다.

4) Jenkins를 통해 기본적인 배포 파이프라인을 직접 구축할 수 있다.

5) 실제 운영기에서 특히 AWS 기반의 클라우드 환경에서 Jenkins가 어떻게 활용되는지 알 수 있다.

  • CI/CD가 뭐냐

영어로는 Continuous Integration 인데 그러면 뭘 통합한다는 거야?

–> 여러 개발자들의 개발한 코드를 계속해서 통합하는 것을 말한다.

다수의 개발자들이 개발한 코드를 직접 이 개발자들이 모여 앉아서 통합하고, 코드를 고치고 이런게 아니라 여러명이 작성한 코드를 가능하면 빠르게 취합해서 배포하고자 함이 목적인 것이다.

continuous integration (CI) is the practice of merging all developers’ working copies to a shared mainline several times a day

그러면 Continuous Delivery 는 뭘 딜리버리를 한다는 거야?

–> 사용자에게 product(서비스)를 지속적으로 딜리버리한다는 것이다. 개발자들이 개발한 코드를 항상 배포가능한 상태를 유지하는 것.

사용자에게만 딜리버리 아니라 내부 개발자들에게도 지속적으로 딜리버리 한다는 의미도 포함하는 개념이다.

Continuous Deployment라는 용어도 있는데 이거는 무슨의미냐?

–> 개발한 코드를 사용자가 사용가능한 환경에 배포하는 것을 자동화함.

즉, CI/CD 란 각각의 개발자들이 개발을 하는 개발 환경을 사용자가 사용 가능한 서비스로 전달하는 모든 과정을 지속 가능한 형태로 또 가능하다면 자동으로 해서 개발자와 사용자 사이의 격차를 없애는 것이다. 이러한 과정에는 코드를 빌드하고, 테스트하고 배포하는 활동이 있다.

이를 도식화하면 아래와 같다.

1

  • CI가 왜 필요하냐? 만약에 CI가 없을때의 문제를 생각해보자.

100명의 개발자가 각자 개발 –> 1주 후 –> 매니저가 중간점검을 한다고 코드들을 취합하는데 각자 개발한 코드를 통합하는 엄청난 고통 –> 또 100명의 개발자가 각자 개발 –> 1주 후 –> “마지막에 누가 취합한거야? 내꺼 코드 안되잖아!”

취합하는 과정자체도 고통이고, 오류도 많이 발생하고, 각각의 오류 잡기도 매우 힘들고, 배포도 어려워지게 된다.

가장 이상적인 시나리오는 100명의 개발자가 각자 개발한 코드를 각자 개발이 될때 마다 통합을 시켜주는 것이 좋다.

  • 그럼 CI를 적용하면 어떻게 될까

100명의 개발자가 열심히 개발 –> 커밋 –> 로컬 테스트 통과 실패 –> 문제가 되는 부분만 코드 수정 –> 커밋 –> 코드베이스 머지 –> next 작업

–> 가능한 최대한 많이 빨리빨리 내 코드를 코드베이스에 안착시키자, 테스트 코드 없는 무서운 코드 버그 더미 코드를 애초에 코드베이스에서쫒아내자

  • 그러면 CD는 왜 필요하냐?

백엔드 코드 개발 –> 백엔드 엔지니어 : “프론트와 협업해야하니 배포를 해볼까?” –> 프론트엔드 개발자 : “저기 배포좀 해주세요…” –> 백엔드 엔지니어 : “버그났는데요” –> 프론트 엔드 개발자가 코드 수정 후 다시 배포 해달라고 요청 –> 또 다른 프론트 엔드 개발자 “데브 서버에 누가 배포했나요? 제꺼 안되는데요;;” –> 개판

프로덕션 배포시 초긴장을 유지하는 안타까운 상황발생

프론트엔드 개발자 입장에서는 코드만 짜면 되지 뭐이리 할게 많냐. 우리 코드만 짜게 해달라. 라는 요구사항이 나올 수 밖에 없다.

  • 그러면 CD를 적용하면 어떻게 될까

100명의 프론트엔드 개발자가 열심히 개발 –> 머지 –> 끝. (프론트 엔드 개발자 : “머지됬으니까 내 역할은 여기까지 peace…”)

QA 엔지니어와 같은 내부사용자 혹은 실제 production 환경의 사용자에게 지속적이고 안정적으로 서비스를 제공한다.

  • 개발자 입장에서 코드 개발만 할건데 나머지 테스트 및 배포 같은 귀찮은 일들은 누가할거냐 라는 의문에서 나온것이 바로 젠킨스이다.

2

어떤 서비스 개발시 웬만한 단순 반복작업은 전부 젠킨스에 시킬 수 있다. 농담삼아 어떤 개발자가 퇴사할때 ‘젠킨스한테 인수인계하세요’ 라는 말도 있을정도다.

  • 젠킨스 기본특징

1) Java Runtime Environment 에서 동작

젠킨스는 자바 런타임에서 돈다. 그래서 젠킨스가 도는 서버에 자바 런타임(젠킨스를 사용하기 위해 자바가 깔려 있어야 한다는 말이다)이 깔려있어도 되고, 그게 귀찮으면 젠킨스 도커이미지를 다운받아서 써도 된다.

2) 다양한 플러그인들을 활용해서 각종 자동화 작업을 처리할 수 있음

젠킨스가 도는 기본원리는 젠킨스에서 제공하는 다양한 플러그인들을 조합해서 원하는 파이프라인을 구성하는 것이다.

코드가 잘 도는지 테스트도 해야하고, 도커 빌드해서 백업도 해야하고, AWS에 배포도 해야하고 , 기타등등 할게 너무 많으니 이런 각각의 컴포넌트들을 하나의 플러그인으로 모듈화를 시켜놓은 것이다.

젠킨스는 수많은 플러그인들을 지원한다. 대표적인 플러그인으로는 Credentials Plugin, Git Plugin, Docker plugin and Docker Pipeline , Pipeline (핵심 기능인 파이프라인 마저도 플러그인!) 등이 있다.

Credentials Plugin

Jenkins 는 그냥 단지 서버일 뿐이기 때문에 배포에 필요한 각종 리소스(예를들어 클라우드 리소스 혹은 베어메탈에 ssh 접근 등) 에 접근하기 위해서는 여러가지 그 해당 리소스에 접근하기 위한 권한을 갖고 있어야 한다. 그래서 이런 권한(AWS token, Git access token, etc…) 들을 저장해 주는 플러그인

젠킨스가 깃에서 어떤걸 가져오고, aws에 배포를 하고 뭐 이런 다양한 일을 할텐데 아무 권한없이 할 수는 없다. 그래서 ssh 키나 credential, access token 이런것들을 저장해주는 플러그인이다. 보안에 대한 이슈가 있을수도 있는데 내부개발자들도 볼수 없도록 RSA 암호화를 알아서 해주는 등 보안적인 대책도 있는 기능이다.

Pipeline Plugin : 젠킨스의 핵심 기능인 Pipeline 을 관리할 수 있게 해주는 플러그인

Docker plugin and Docker Pipeline

Docker agent 를 사용하고 jenkins 에서 도커를 사용하기 위함. 결론은 젠킨스가 도커 이미지 다운받고, 빌드하고 이런것들을 하기위한 플러그인이다.

젠킨스 설치할때 처음에 젠킨스에서 Recommend 해주는 것을 깔면 CI/CD에 필요한 웬만한 플러그인은 다 있다고 생각해도 무방할 정도다.

결론적으로 젠킨스는 빈껍데기이고, 수많은 플러그인들 중에서 필요한 것들을 조합해서 원하는 파이프라인 시나리오를 구현하는 것이다.

3) 일련의 자동화 작업의 순서들의 집합인 Pipeline 을 통해 CI/CD 파이프라인을 구축함

  • 젠킨스 WebUI에서 플러그인 목록 화면

아래 화면과 같이 플러그인 목록들이 있고 여기서 필요한 것들을 설치해서 쓸 수 있다.

3

  • 젠킨스의 핵심 파이프라인이란

개발자가 파이프라인으로 어떤 product 서비스를 배포하는데 이 파이프라인을 구성하는 것이 다양한 플러그인들의 조합이다.

이 파이프라인은 Pipeline DSL(Domain Specific Language)이라는 젠킨스 파이프라인에서만 쓰는 언어로 작성할 수 있다.

파이프라인이란 CI/CD 파이프라인을 젠킨스에 구현하기 위한 일련의 플러그인들의 집합이자 구성.

즉 여러 플러그인들을 이 파이프라인에서 용도에 맞게 사용하고 정의함으로써 파이프라인을 통해 서비스가 배포됨

4

  • Pipeline을 구성하는 요소

파이프라인이란 CI/CD 파이프라인을 젠킨스에 구현하기 위한 일련의 플러그인들의 집합이자 구성.

즉 여러 플러그인들을 이 파이프라인에서 용도에 맞게 사용하고 정의함으로써 파이프라인을 통해 서비스가 배포됨

결론적으로 젠킨스에서 제공하는 각종 플러그인을 이용하여 CI/CD 파이프라인을 script로 작업 명세서 형태로 정의하면 된다.

script로 작업 명세서 형태로 만드려면 두가지 형태의 Pipeline syntax(Declarative 또는 Scripted Pipeline)를 이용해서 작성하면 된다.

  • Pipeline Syntax

section, Declaratives, stage으로 크게 구성이 된다.

Section은 어떤일들을 할 것이고, 누가할 것인지 같은 것들을 정의할 수 있다.

1) Sections

섹션은 크게 Agent section, Post section, Stages section, Steps section로 구성된다.

Agent Section

젠킨스는 많은 일들을 해야 하기 때문에 혼자 하기 버겁다.

여러 slave node 를 두고 일을 시킬 수 있는데, 이처럼 어떤 젠킨스가 일을하게 할 것인지를 지정한다.

젠킨스 노드 관리에서 새로 노드를 띄우거나 혹은 docker 이미지 등을 통해서 처리할 수 있음

어떤 노드안에서 도커 컨테이너를 띄워서 걔한테 어떤 일을 시킬수도 있다.

예를들어서 자바빌드를 해야하는데 젠킨스 서버에 접속해서 자바를 일일히 깔 수 없기 때문에 자바 도커이미지를 하나 받아서 그 안에서 빌드를 시킬수도 있는 것이다. 도커에이전트에게 노드js 컨테이너 안에서 어떤 작업을 하라고 지정할 수 있다. 그러면 도커 이미지를 다운받아서 도커 컨테이너 띄우고 그 안에서 작업하고 이런 일을 할 수 있다.

Post section

각각의 스테이지가 끝난 이후의 결과에 따라서 후속 조치를 취할 수 있다.

Ex) success, failure, always, cleanup

Ex) 성공시에 성공 이메일, 실패하면 중단 혹은 건너뛰기 등등

6

Stages Section

어떤 일들을 처리할 건지 일련의 stage 를 정의함

Steps Section

한 스테이지 안에서의 단계로 일련의 스텝을 보여줌

5

2) Declaratives

각 스테이지에서 어떤일을 할건지에 대해서 정의하는 것이 Declaratives이다.

Environment, stage, options, parameters, triggers, when 등의 Declarative 가 있음

Environment -> 어떤 pipeline 이나 stage scope 의 환경 변수 설정

Parameter -> 파이프라인 실행시 파라미터 받음

Triggers -> 어떤 형태로 트리거 되는가

When -> 언제 실행되는가

아래에 두번째 그림이 when을 활용한 예시인데 프로덕션 환경에서만 해야하는 어떤 일이 있는 것이다. 그래서 내가 git에서 땡겨온 브랜치가 프로덕션이고, environment가 production일때만 어떤 일을 하라고 정의한 것이다.

예시

7

3) steps

스테이지 안에서는 해야할일이 많은데 플러그인들을 이용해서 활용해서 해야할일도 많이 있을것이다.

예를들어서 빌드를 할때 디렉토리를 옮겨서 빌드를 한다던가, 아니면 어떤 다른 플러그인을 깔아서 거기에서 제공하는 어떤 메소드를 이용해서 일을 처리한다던가 이런식으로 할수 있다.

예를들어서 깃플러그인을 설치하면 깃의 credential을 가져오는 어떤 스텝을 할 수 있는 것이고, 도커플러그인을 설치하면 도커를 가져와서 빌드할 수 있는 어떤 메소드를 활용해서 스텝을 구성할 수 있는 것이다.

Steps 내부는 여러가지 스텝들로 구성

여러 작업들을 실행가능

플러그인을 깔면 사용할 수 있는 스텝들이 생겨남

플러그인별 스텝 종류들은 https://www.jenkins.io/doc/pipeline/steps/ 에서 참고해보자.

  • pipeline 예제 샘플

파이프라인이라는 제일 큰 덩어리를 선언하고, 이 안에서 에이전트로 어떤 젠킨스한테 일을 시킬건지 정해주고, 이 파이프라인에는 어떤작업이 있는지 선언했다. 여기에는 prepare하고 build하는 스테이지로 구성되어 있다. 그리고 prepare라는 스테이지에서 해야하는 step을 정의해줬는데 깃에서 브랜치 땡겨오고, 디렉토리 바꿔서 s3에 업로드를 하는 이런 것들을 정의했다.

8

  • 리눅스에서 젠킨스 설치하는 bash 명령어
$ sudo yum update -y

# Jenkins 패키지 추가
# 젠킨스를 다운받을 수 있는 yum repo를 다운을 받고, 그 레포에 접근할 수 있는 키를 받는다.
$ sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins.io/redhat/jenkins.repo && sudo rpm --import https://pkg.jenkins.io/redhat/jenkins.io.key

# Install java, docker, git
$ sudo yum install -y java-1.8.0-openjdk jenkins git docker

# 자바 버전 8 로 설정
$ sudo alternatives --config java

$ sudo service jenkins start
  • 일반적인 개발환경 종류

1) 개발자가 개발을 하는 Local 환경

개발자 자신의 workspace 안에서 일을함

2) 개발자들끼리 개발 내용에 대한 통합 테스트를 하는 Development 환경

3) 개발이 끝나고 QA 엔지니어 및 내부 사용자들이 사용해 보기 위한 QA 환경

개발이 퀄리티있게 잘 되었는지 검증하는 환경

4) 실제 유저가 사용하는 Production 환경

쉽게 DEV, QA, PROD 환경으로 부르자!

  • CICD의 전체적인 outline

step 1) 개발자가 자신의 PC 에서 개발을 진행한다.

step 2) 다른 개발자가 작성한 코드와 차이가 발생하지 않는지 내부 테스트를 진행한다.

젠킨스 또는 git hook을 통해서 git commit을 하기전에 로컬에서 테스트를 해본다.

테스트도 통과못한 코드는 애초에 git에 commit 조차 못하게 하겠다라는 취지이다.

이런부분도 CICD의 일부분이다.

step 3) 진행한 내용을 다른 개발자들과 공유하기 위해 git 과 같은 SCM에 올린다.

코드도 다 짰고, 로컬에서 테스트도 통과했다 그러면 dev branch에 올릴 것이다.

step 4) Dev브랜치의 내용을 개발 환경에 배포하기 전에 테스트와 Lint 등 코드 포맷팅을 한다.

그러면 젠킨스가 해당 repository를 계속 보고 있다가 코드 변화가 생겼다 그러면 repository를 가져와서 테스트를하고 lint를 하는 등 포맷팅하고 배포까지 하게 된다.

step 5) 배포하기 위한 빌드과정을 거친다.

step 6) 코드를 배포한다.

step 7) 테스트를 진행한다.

step 8) 위 모든 과정을 DEV, QA, PROD 환경에서 모두 하고 각각에 맞는 환경에 배포한다.

  • 여러 배포 환경의 관리

여러 배포환경의 관리에서 핵심은 인프라를 모듈화 하여 어떤것이 변수인지 잘 설정하고 이를 잘 설계하는것!

가령 APP_ENV 처럼 현재 배포하고자 하는 것이 무슨 환경인지 설정하고 앱 내에서 사용하는 다양한 변수들을 APP_ENV 에 맞게 잘 가져다 쓰는것이 핵심.

dev/qa/prod 환경의 차이는 몇가지 안된다. 예를들어서 database 계정정보나, 백앤드 서버 URL, 암호화 hash 키 등 이런것들이 달라지는 건데 그런것들을 잘 설계하고 배포를 할때 어떤것을 쓰도록 잘 설계하는게 배포환경의 핵심이다.

서비스 내부의 변수 뿐만 아니라 클라우드 리소스를 많이 활용해서 개발하는 요즘에는 클라우드 리소스 내에서 인프라별 키관리가 매우 중요해서 aws system manager 의 parameter store 와 같은 키 관리 서비스를 쓰는것을 추천한다.

핵심은 뭐냐 prod 젠킨스가 있고, dev 젠킨스 등 각각의 환경에 젠킨스가 있는데 이것들이 필요한 정보를 어디서 가져오냐가 핵심이다. 필요한 정보들을 어디서 가져오냐를 저장하는 곳을 클라우드 서비스를 활용할 수 있다. 그리고 젠킨스는 이런명령만 받으면 된다. 배포할때 프로덕션 버전으로 배포해라고 하면 각각의 환경에 맞게 키들만 가져와서 빌드해서 배포하면 된다.

  • 배포 시나리오 예시

개발하는 코드를 작성해서 git add, commit, push하면 테스트랑 빌드가 전부 다 되어서 프로덕션 환경에 배포까지 되는것이 우리의 최종목표이다.

그래서 프론트앤드 서버와, 백앤드 서버 두대를 띄울건데 프론트앤드는 s3에 index.html을 올려서 띄우는 것을 젠킨스에서 하고, 백앤드 서버는 ECS나 쿠버네티스를 띄울 것이다. 젠킨스가 하는 것은 빌드만 하는 것이다. 빌드된 이미지가 있으면 오케스트레이션 하는 것은 아마존 ECS나 쿠버네티스가 할 것이다. 그래서 우리는 도커이미지를 우리가 원하는 형태로 빌드해서 원하는 repository에 집어넣기만 하면 된다.

그러면 웹사이트는 s3에 뜨고 서버는 젠킨스가 올라간 ec2 인스턴스 형태가 될 것이다.

step by step으로 얘기한다면 아래와 같다.

step 1) 웹사이트 코드를 작성한다.

step 2) 웹사이트 코드를 린트, 웹팩 빌드 해서 AWS S3 bucket 에 html 파일을 업로드한다

step 3) Node.js 백엔드 코드를 typescript 작성한다.

step 4) 위 코드를 javascript compile 하고, 테스트 코드를 돌려서 도커 이미지를 만들어 ECR 에 올린다.

step 5) 업로드한 ECR 이미지로 ECS 서비스를 재시작한다(rolling deploy) => continuouse deploy

  • AWS 리소스 간단 리뷰 : S3

1) Simple Storage Service 의 약자로 그냥 클라우드 스토리지

2) 정적 웹사이트 코드배포에 용이

3) 정적 웹사이트 호스팅에 필요한 다양한 기능 제공

4) AWS Cloudfront와 함께 사용해서 최적화 가능하고 DNS 관리도 가능

cloudfront가 뭐냐면 웹사이트를 호스팅한다는 것은 도메인을 붙이고, 그 도메인에서 어떤 리소스를 가져오는지만 정해주면 되는 것인데 aws는 이미 이런 리소스를 전부 올려놨기 때문에 도메인 이름만 붙여주면 실제 서비스가 돌아가는데 문제가 없다.

  • AWS 리소스 간단 리뷰 : ECR

1) Elastic Container Registry

2) 도커 이미지를 저장하는 프라이빗 레포지토리

그냥 아무거나 프라이빗하게 레포지토리를 만들고 도커이미지를 쓸 수도 있지만, 아마존 차원에서 제공하는 서비스를 이용하면 다른 아마존 서비스에서 참고할때 매우 편해지기 때문에 ECR을 쓰는 것이다.

3) 실제 프로덕션 환경에서는 container 기반의 배포(ECS 등을 활용) 할 것이기 때문에 반드시 repository 가 있어야 함.

  • AWS 리소스 간단 리뷰 : ECS

1) Elastic Container Service

container 서비스에 대한 롤링업데이트, 스케일업, 로드밸런싱 이런거를 다해주는 서비스이다.

2) 도커 컨테이너 기반으로 서비스 운용을 가능하게 해주는 간단한 서비스

3) 무중단 배포(rolling update)를 제공하며 scale up 이 가능한 특징

4) 백엔드 서비스를 스케일업 가능한 형태로 배포하는데 최적화

5) 수많은 도커 컨테이너 서버를 띄우고 LB 가 이들 사이에 밸런싱을 해줌.

6) fargate, ec2 모드가 있어서 docker container 리소스만 띄우거나 혹은 물리적인 EC2 instance 클러스터로 구성 가능

ec2 모드 : 컨테이너 서비스를 물리적인 ec2 안에 띄워서 사용자에게 할당해준다. 그래서 사용자가 ec2에 접속해서 컨테이너 서비스를 전부 다 확인할 수 있다.

fargate : 조금 더 도커친화적으로 ec2모드보다 추상화 레벨이 높은 개념으로 사용자가 ec2 이런거 잘 몰라, ec2 관리하기 싫어 그러면 ec2가 아니라 아마존 내부적으로 컨테이너를 띄워서 서비스를 제공하고 관리해주는 모드다.

결국에는 젠킨스가 하는 역할은 ECS 혹은 k8s 등을 통해 rolling deploy가 처리되기 때문에 jenkins로 하여금 배포 명령만 내려주면 된다!

Ex) aws ecs update-service ‘서비스 이름’

  • 개발환경 인프라 관리 양상

회사마다 다 다르지만 아래와 같은 개발환경 양상이 일반적이다.

유저들이 사용하는 프로덕션 환경은 매우 안전해야 하기 때문에 개발환경의 어떠한 요소던지 프로덕션 환경에는 영향을 주면 안된다. 따라서 극단적으로 프로덕션 환경과 개발환경의 aws 계정을 아예 따로만들고 네트워크 환경도 다르게해서 물리적으로 아예 분리해서 구성하는 경우가 있다.

이런 경우처럼 프로덕션 환경이 극단적으로 안전해야 할 필요가 없다면 아래 그림과 같이 프로덕션과 개발 환경이 같은 계정안에 있는 경우도 있다.

9