[Hands On] EC2 CI/CD

00. Intro

00-01. CI/CD란?

CI(Continous Integration)
  • 지속적인 통합을 의미한다.
  • 코드 변경사항이 자동으로 빌드 및 테스트 되어 하나의 레포지토리로 통합되어 관리될 수 있는 것을 의미한다.
CD(Continuous Delivery)
  • 지속적인 서비스 제공을 의미한다.
  • 개발자들이 적용한 변경 사항이 테스트를 거쳐 레파지토리에 업로드 되는 것이 자동으로 이루어진다.
CD(Continuous Deployment)
  • 지속적 배포를 의미한다.
  • 고객이 사용가능한 프로덕션 환경까지 자동으로 릴리즈 한다.
  • Continuous Delivery와의 차이는 프로덕션 배포까지 자동화 하는가의 차이이다.

00-02. CI/CD 도구

Jenkins
  • 무료 오픈 소스이다.
  • 플러그인이 많고 커스터마이징을 다양하게 할 수 있다.
  • 많은 사용자를 가지고 있기 때문에 관련 문서가 다양하다.
GitLab
  • Private repository를 무료로 사용 가능하다.
  • 프로젝트 당 repository 저장공간을 많이 허용한다.
  • 자체적으로 지원하는 CI 서비스가 있다.

00-03. CI/CD Work Flow


01.사전 작업

  • Node.js 설치
  • MySQL 설치
  • Node.js app 생성
  • docker image로 생성 후 docker hub에 push
public docker image 참고 : kicoskillup/node-app:v0.1


02. Kubernetes 환경 구성

Kubernetes 설치
  • Master node, Worker node 공통
# sudo hostnamectl set-hostname devops-master
# sudo apt update
# sudo apt upgrade -y
# sudo swapoff -a
# sudo apt install ntp -y
# sudo service ntp restart
# sudo ntpq -p

# sudo apt-get update && sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common gnupg2

# curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key --keyring /etc/apt/trusted.gpg.d/docker.gpg add -

# sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

# sudo apt-get update && sudo apt-get install -y containerd.io=1.2.13-2 docker-ce=5:19.03.11~3-0~ubuntu-$(lsb_release -cs) docker-ce-cli=5:19.03.11~3-0~ubuntu-$(lsb_release -cs)

# sudo mkdir /etc/docker


# cat <<EOF | sudo tee /etc/docker/daemon.json
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}
EOF


# sudo mkdir -p /etc/systemd/system/docker.service.d

# sudo systemctl daemon-reload
# sudo systemctl restart docker
# sudo systemctl enable docker



# curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -

# cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF

# sudo apt-get update
# sudo apt-get install -y kubelet kubeadm kubectl
# sudo apt-mark hold kubelet kubeadm kubectl
  • Master node
# sudo kubeadm init --apiserver-advertise-address <ip 주소> --pod-network-cidr=<포드 네트워크 IP 주소 범위>


# mkdir -p $HOME/.kube
# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
# sudo chown $(id -u):$(id -g) $HOME/.kube/config
  • Worker node
[Master에서 생성 된 join Hash Key로 Master에 조인]

# kubeadm join 10.0.1.43:6443 --token qf1bao.ioqs0kckwchqmb66 \
        --discovery-token-ca-cert-hash sha256:16704ccc8d93859ef1012e4f532230982c03ede8e7b510b726f73a07f57fa5ff
  • Master node
[Network plugin 설치]

# wget https://docs.projectcalico.org/manifests/calico.yaml
# sed -i -e 's?192.168.0.0/16?10.0.1.0/24?g' calico.yaml
# kubectl apply -f calico.yaml
  • node 확인
2개의 worker node가 master node에 Join 된 것을 알 수 있다.


03. Jenkins 설치

구성
  • EC2 (ubuntu 18.04)
  • Security Group : Allow 22, 8080
Jenkins 설치
  • EC2에 Jenknins 설치
[Open JDK 8 설치]
# sudo apt-get update
# sudo apt-get install openjdk-8-jdk

[Jenkins 리포지토리 키 다운]
rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key

[apt 데이터베이스에 공식 젠킨스 리포지토리 추가]
# sudo sh -c 'echo deb https://pkg.jenkins.io/debian-stable binary/ > \
    /etc/apt/sources.list.d/jenkins.list'

# apt-get update
# apt-get install -y jenkins

[만약 포트 번호 변경 원할 시]
vi /etc/default/jenkins
# port for HTTP connector 
HTTP_PORT=8080 <- 8080포트를 원하는 포트로 변경

[Jenkins 실행 및 상태 확인]
# systemctl start jenkins
# systenctl status jenkins

[Jenkins 초기 설정을 위헤 패스워드 가져오기]
# cat /var/lib/jenkins/secrets/initialAdminPassword


[브라우저로 Jenkins 접속]
초기 설정
  • 위에서 불러온 AdminPassword 입력
  • Plugin 설치
  • User 생성


04. GitLab 설치

구성
  • EC2 (ubuntu 18.04)
  • Security Group : Allow 22, 8001
GitLab 설치
[시스템 업데이트]
# apt update
# apt upgrade -y



[gitlab ce 레파지토리 추가]
# curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bash


[gitlab 설치]
# apt install -y gitlab-ce
Port 변경
  • externer url 포트는 80, 8080을 피하는 것이 좋다.
  • gitlab_worhorse & puma는 80, 8080을 피하고 반드시 externer url 포트와 달라야한다.
[port 변경]
# vi /etc/gitlab/gitlab.rb

<gitlab.rb 내용 수정>
external_url '<IP 또는 url:port1>'
gitlab_workhorse['auth_backend'] = "http://localhost:<port2>"
puma['enable'] = true
puma['port'] = <port2>

[gitlab-ctl 설정 적용]
# gitlab-ctl reconfigure
초기 설정
  • 초기 비밀번호 확인
# cat /etc/gitlab/initial_root_password  | grep Pass
  • root 계정으로 로그인
  • 로그인 후 비밀번호 재설정
  • user 생성
  • User로 로그인 후 비밀번호 재설정


05. GitLab Connection 설정

Jenkins에서 ssh 키 생성, public 키 확인
# ssh-keygen
# cat ~/.ssh/id_rsa.pu

=> ssh-rsa @#$!#% 출력
GitLab에서 SSH Keys 등록
프로젝트 연동을 위해 GitLab에서 프로젝트 생성

Jenkins Plugin
  • Jenkins에서 연동을 위해 필요한 Plugin 추가
  • git plugin, gitlab plugin
Jenkins Creditials 추가
  • Jenkins에서 GitLab에 대한 Creditials 추가
  • Dashboard > Jenkins 관리 > Manage Creditials
GitLab에서 Access token 생성
  • access token은 다시 확인할 수 없으므로 따로 복사해둔다.
GitLab Connection 설정
  • Jenkins 시스템 설정에서 GitLab connection 설정
  • API tocken 부분에 GitLab에서 받은 Access token 입력
  • Credentials 적용 후 Test Connection 시도


06. Webhook을 이용한 Build Triggers 설정

Webhook Plugin 추가
Jenkins Pipeline 생성
  • 새로운 Item 생성
  • Dashboard > 새로운 Item > pipeline
  • Build Triggers 설정
GitLab에서 Webhook 설정
  • Project > Settings > Webhook
  • URL은 Jenkins Build Triggers의 ①번 URL
  • Secret token은 Jenkins Build Triggers의 ④번 Secret token
  • Push events Test
  • Test 후 Jenkins에 Build 되는 것 확인한다.


07. Jenkinsfile을 이용한 pipeline

07-01. Docker hub와 연결하는 pipeline

Jenkins pipeline 구성
  • ① Definition : GitLab에서 코드를 관리 하고자 하면 ‘Pipeline script from SCM’ 선택, Jenkins에서 직접 script를 작성하고자 할 때는 ‘pipelins script’를 선택하면 된다.
  • ② Repository URL : 코드 관리를 할 자신의 GibLab URL
  • ③Credentials : 앞서 생성한 GitLab용 Credential 선택
  • ④Branch Specifier: Jenkinsfile이 있는 origin branch 입력
  • ⑤Sscipt Path: 자신의 Jenkinsfile 이름 입력 (ex. Jenkinsfile or Jenkinsfile.groovy)
Docker Hub 연결을 위한 Credential 생성
Docker Plugin 추가
  • docker image build, push를 위해 docker 관련 plugin을 추가한다.
Jenkins Server에 docker 설치
# sudo apt-get update
# sudo apt-get install \
   ca-certificates \
   curl \
   gnupg \
   lsb-release
   
# curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
# apt-get update
# sudo apt-get install -y docker-ce docker-ce-cli containerd.io
GitLab에 Jenkinsfile 추가
  • Jenkins.groovy
node {
  stage('Clone repository') {
    checkout scm
  }
  stage('Build image') {
    app = docker.build("<dockerhub id>/node-server:$BUILD_NUMBER")
  }
  stage('Push image') {
    docker.withRegistry('https://registry.hub.docker.com', 'dockerhub') {
      app.push("${env.BUILD_NUMBER}")
    }
  }
}
Build 확인
  • login 접속 오류 시 docker.sock 변경
# chmod 666 /var/run/docker.sock

07-02. Kubernetes와 연결하는 pipeline

Jenkins에 Kubernetes 설치
# sudo apt-get update
# sudo apt-get install -y apt-transport-https ca-certificates curl
# sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
# echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
# sudo apt-get update
# sudo apt-get install -y kubectl
Jenkins에 kubernetes Plugin 추가
Kubernetes Credentials 생성
  • Kubernetes master node (.kube/config)
[config파일 보는 법]
# cat /root/.kube/config
  • Credential 생성
  • Jenkinsfile.groovy 추가 변경
node {
  stage('Clone repository') {
    checkout scm
  }
  stage('Build image') {
    app = docker.build("dmswjd9997/node-server:$BUILD_NUMBER")
  }
  stage('Push image') {
    docker.withRegistry('https://registry.hub.docker.com', 'dockerhub') {
      app.push("${env.BUILD_NUMBER}")
    }
  }
  stage('Kubernetes deploy') {
    steps {
      kubernetesDeploy configs: "deployment.yaml", kubeconfigId: 'kube'
      sh "kubectl --kubeconfig=/root/.jenkins/.kube/config rollout restart deployment/node-deployment"
    }
  }
}