CI/CD 환경 구축을 끝내서 최종 정리하려고 함
이번 포스팅으로 인프라 세팅은 끝내고 이제 다시 front-end 와 back-end 작업에 들어갈 듯 하다
웹서비스 인프라 설계 및 구축
글은 각 제목 아래 설계했던 내용 - 구축한 내용 으로 작성하였음
목차
- subnet, SG, EIP,
- install & config docker
- create AMI
- github + hub.docker
- jenkins config
- ansible
- Webhook
- Jenkinsfile
- playbook.yaml
1. Infrastructure Diagram
- github 와 hub.docker 를 이용한 ci/cd 파이프라인을 구성
- S3 버킷은 크롤링한 이미지를 저장하고 s3 url 을 메타데이터로 저장해서 불러오도록 했다.
- 최근 배웠던 k8s 를 적용하기엔 규모가 작기때문에 ansible 로 원격지(web-server) 를 제어하기로 했다
- db-server 는 프라이빗 서브넷에 배치해서 보안성을 높였다 (사실 별로 든게 없긴 하다)
2. AWS resources
인스턴스 환경
Jenkins server | Web server | DB server | |
OS | ubuntu 22.04 | ubuntu 22.04 | ubuntu 22.04 |
인스턴스 유형 | t3a.medium | t3a.small | t2.micro |
key pair | dev-all | prod-blackpink | dev-all |
VPC | default | default | default |
subnet | 2a | 2a | 2a |
public ip | EIP | yes | yes |
SG-inbound | 22,8080 | 22,80,443, 8080 | 22,3306 |
SG-outbound | anywhere | anywhere | anywhere |
storage | 16GiB | 16GiB | 30GiB |
container | jenkins | php-apache, python | mysql |
리소스 태그
Jenkins server | Web server | DB server | private subnet | S3 bucket | |
tag - Name | ec2-jenkins_server-01 | ec2-web_server_01 | ec2-db_server_01 | subnet-ap_northeast_2a-private_01 |
s3bucket-ap_northeast_2-public_image_storage
|
tag - Project | General | ResponsiveWeb-blackpinkinyourarea | General | General | ResponsiveWeb-blackpinkinyourarea |
tag - Environment | Develop | Production | General | Production | Production |
보안그룹 태그
SG-ssh | SG-for-jenkins_server | SG-for-web_server |
SG-for-db_server
|
|
tag - Name | sg-default_vpc-ec2-ssh | sg-default_vpc-ec2-jenkins | sg-default_vpc-ec2-webserver |
sg-default_vpc-ec2-dbserver
|
tag - Project | General | General | ResponsiveWeb-blackpinkinyourarea | General |
tag - Environment | General | Develop | Production | General |
- jenkins server 는 앞으로도 cicd 환경을 구축할때 사용하고 또 빌드가 실행될 곳이기 때문에 medium 에 EIP 를 할당해주려고 한다
- db server 의 스토리지는 프리티어 최대 사용량인 30GiB 로 설정해줬다
- 인스턴스의 들의 보안그룹은 SG-ssh, SG-for-jenkins_server, SG-for-web_server, SG-for-db_server 이렇게 4개 사용할 예정이다
- S3 bucket 은 사진 다운로드를 위해 퍼블릭 액세스 오픈해서 사용한다
subnet
먼저 서브넷을 만들어줬다
CIDR 는 기본 vpc 에서 남은 대역중 다음 대역을 선택해줬다
EIP
그리고 위 설계에 맞춰 jenkins server 를 만들어주고
ubuntu 22.04 이미지를 선택해 준다음
EIP 를 연결해줬다
EIP 태그
Name : eip-ap_northeast_2-conn_jenkins
Project : General
Environment : Develop
Name : eip-ap_northeast_2-conn_blackpink
Project : ResponsiveWeb-blackpinkinyourarea
Environment : Production
3. jenkins-server
docker 설치
업데이트 및 필요 패키지 설치
sudo apt update
sudo apt-get update
sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release
Docker GPG key 추가
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
리포지토리 설정
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Docker 엔진 설치
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io -y
Docker daemon 설정
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 systemctl enable --now docker
- CRI container runtime 설치
cri-docker 패키지 다운로드
https://github.com/Mirantis/cri-dockerd/releases/tag/v0.2.6
cri-docker 패키지 설치
sudo apt install ./<해당버전>.deb
cri-docker 패키지 설치 후
sudo systemctl enable cri-docker.service
sudo systemctl enable --now cri-docker.socket
sudo 없이 docker 명령어 실행
그룹 추가
sudo groupadd docker
그룹에 사용자 추가
sudo usermod -aG docker {user_id}
docker 재시작
sudo service docker restart
권한오류 Got permission denied while trying to ...
sudo chown root:docker /var/run/docker.sock
혹은
sudo chmod 666 /var/run/docker.sock
AMI
ami tag
Name : ami-docker_in_ubuntu
Project : General
Environment : General
인스턴스 생성
sudo 없이 docker 명령이 잘 실행 되는 모습
4. db-server
인스턴스 환경
Jenkins server | Web server | DB server | |
OS | ubuntu 22.04 | ubuntu 22.04 | ubuntu 22.04 |
인스턴스 유형 | t3a.medium | t3a.small | t2.micro |
key pair | dev-all | prod-blackpink | dev-all |
VPC | default | default | default |
subnet | 2a | 2a | 2a |
public ip | EIP | yes | yes |
SG-inbound | 22,8080 | 22,80,443, 8080 | 22,3306 |
SG-outbound | anywhere | anywhere | anywhere |
storage | 16GiB | 16GiB | 30GiB |
container | jenkins | php-apache, python | mysql |
리소스 태그
Jenkins server | Web server | DB server | private subnet | S3 bucket | |
tag - Name | ec2-jenkins_server-01 | ec2-web_server_01 | ec2-db_server_01 | subnet-ap_northeast_2a-private_01 |
s3bucket-ap_northeast_2-public_image_storage
|
tag - Project | General | ResponsiveWeb-blackpinkinyourarea | General | General | ResponsiveWeb-blackpinkinyourarea |
tag - Environment | Develop | Production | General | Production | Production |
위 설계에 맞춰서 인스턴스 생성
private subnet 에 위치한 db server 에 mysql container 환경설정
먼저 개발 환경에서 docker image 를 tar 로 압축해서 보내줌
그리고 private subnet 에 위치한 ec2 에 ssh 접속해서
docker load 로 image 에 저장해줌
이후 mysql container 생성해줌
ubuntu@ip-172-31-78-182:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ubuntu@ip-172-31-78-182:~$
ubuntu@ip-172-31-78-182:~$ docker run -it --name mysql-db -e MYSQL_ROOT_PASSWORD=비밀번호 -d -p 3306:3306 mysql:latest
f30f097dd22fdd38581b9e7c9cc44991a5440423d6b6262fa4fe9408569e7810
ubuntu@ip-172-31-78-182:~$
ubuntu@ip-172-31-78-182:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f30f097dd22f mysql:latest "docker-entrypoint.s…" 7 seconds ago Up 4 seconds 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp mysql-db
ubuntu@ip-172-31-78-182:~$
ubuntu@ip-172-31-78-182:~$
db 랑 테이블을 만들어보자
DB : blackpink | |||
table : test | table : blackpink_img_data | ||
column | type | column | type |
sample_no | int | DB_MEM_NAME | varchar(10) |
sample_name | varchar(100) | DB_YEAR | int |
DB_MONTH | int | ||
DB_DAY | int | ||
DB_FILENUM | int | ||
DB_S3URL | varchar(300) | ||
DB_DOWNLOAD | int |
위처럼 간단하게 2개 테이블만 만들어보자
create table test (
sample_no int,
sample_name varchar(100)
);
create table blackpink_img_data (
DB_MEM_NAME varchar(10),
DB_YEAR int,
DB_MONTH int,
DB_DAY int,
DB_FILENUM int,
DB_S3URL varchar(300),
DB_DOWNLOAD int
);
테스트 데이터 넣어보고 확인하기
액셀 백업 파일 안의 데이터 db로 옮기기
이전에 액셀파일로 백업해놨던 데이터를
private subnet 의 mysql container 안으로 옮겨보자
private subnet 에 위치한 db server 로 mysql connect 해야하기 때문에
기존 개발 환경인 instance 에서 접속해볼 예정이다.
import pandas as pd
import pymysql
con = pymysql.connect(host='172.31.78.182', user='root', password='비밀번호',
db='blackpink', charset='utf8') # 한글처리 (charset = 'utf8')
cur = con.cursor()
excel_sheet = pd.read_excel('/home/ubuntu/db-backup.xlsx',
sheet_name = 'backup-data', engine='openpyxl')
sql_insert_1 = 'insert into blackpink_img_data values(%s, %s, %s, %s, %s, %s, %s)'
for idx in range(len(excel_sheet)):
cur.execute(sql_insert_1, tuple(excel_sheet.values[idx]))
con.commit()
con.close()
db server 와 같은 vpc 에 위치한 dev server 에서 실행할 내용
ubuntu@blackpinkinyourarea:~$ python3 migration_backup_data.py
ubuntu@blackpinkinyourarea:~$
실행
select * from blackpink_img_data;
2170 개 데이터 모두 잘 옮겨진 모습
5. web-server
인스턴스 환경
Jenkins server | Web server | DB server | |
OS | ubuntu 22.04 | ubuntu 22.04 | ubuntu 22.04 |
인스턴스 유형 | t3a.medium | t3a.small | t2.micro |
key pair | dev-all | prod-blackpink | dev-all |
VPC | default | default | default |
subnet | 2a | 2a | 2a |
public ip | EIP | yes | yes |
SG-inbound | 22,8080 | 22,80,443, 8080 | 22,3306 |
SG-outbound | anywhere | anywhere | anywhere |
storage | 16GiB | 16GiB | 30GiB |
container | jenkins | php-apache, python | mysql |
리소스 태그
Jenkins server | Web server | DB server | private subnet | S3 bucket | |
tag - Name | ec2-jenkins_server-01 | ec2-web_server_01 | ec2-db_server_01 | subnet-ap_northeast_2a-private_01 |
s3bucket-ap_northeast_2-public_image_storage
|
tag - Project | General | ResponsiveWeb-blackpinkinyourarea | General | General | ResponsiveWeb-blackpinkinyourarea |
tag - Environment | Develop | Production | General | Production | Production |
위 설계에 맞춰 인스턴스 생성
6. CI/CD pipeline
git repository 만들기
일단 jenkins 서버 환경 설정 해 놓고
로컬에 클론시킬 예정
hub.docker repository 생성
jenkins 에 연결할 hub.docker repo
Jenkins 설치
sudo docker run -itd --name jenkins -p 8080:8080 -p 50000:50000 -v /docker/jenkins:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock -e TZ=Asia/Seoul -u root jenkins/jenkins:latest
Jenkins container에 docker 설치
docker exec -it 젠킨스컨테이너이름 bash
curl -fsSL get.docker.com -o get-docker.sh
sh get-docker.sh
Jenkins 초기 비밀번호
젠킨스 컨테이너 안에서
cat /var/jenkins_home/secrets/initialAdminPassword
권장 플러그인 설치
자격 증명 생성
Github 자격증명 생성
kind : Username with Password
Username : Github 계정명
Password : github persnal access token
ID : 젠킨스 내에서 자격증명을 가리키기 위해 사용할 변수 이름
Docker Hub 자격증명 생성
kind : Username with Password
Username : Docker Hub 계정명
Password : Docker Hub persnal access token ( hub.docker - account settings - security - new access tokens )
ID : 젠킨스 내에서 자격증명을 가리키기 위해 사용할 변수 이름
Webhook
hook trigger for GITScm polling
Jenkinsfile
pipeline {
agent any
// 해당 스크립트 내에서 사용할 로컬 변수들 설정-
// 레포지토리가 없으면 생성됨
// Credential들에는 젠킨스 크레덴셜에서 설정한 ID를 사용
environment {
dockerhubRegistry = 'cyaninn/prod-web-blackpinkinyourarea'
dockerhubRegistryCredentail = 'cred-dockerhub'
githubCredential = 'cred-github'
gitEmail = 'sounddevice3@gmail.com'
gitName = 'cyaninn-entj'
}
stages {
// 깃허브 계정으로 레포지토리를 클론한다.
stage('Checkout Application Git Branch') {
steps {
checkout([$class: 'GitSCM', branches: [[name: '*/main']], extensions: [], \
userRemoteConfigs: [[credentialsId: githubCredential, url: 'https://github.com/cyaninn-entj/prod-web-blackpinkinyourarea.git']]])
sh "pwd"
sh "ls"
}
// steps 가 끝날 경우 실행한다.
// steps 가 실패할 경우에는 failure 를 실행하고 성공할 경우에는 success 를 실행한다.
post {
failure {
echo 'Repository clone failure'
}
success {
echo 'Repository clone success'
}
}
}
//python-crawling-server build
stage('backend - Docker Image Build') {
steps {
sh "docker build ./back-end -t ${dockerhubRegistry}:backend.${currentBuild.number}"
sh "docker build ./back-end -t ${dockerhubRegistry}:backend.latest"
}
// 성공, 실패 시 슬랙에 알람오도록 설정
post {
failure {
echo 'Docker image build failure'
//slackSend (color: '#FF0000', message: "FAILED: Docker Image Build '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
success {
echo 'Docker image build success'
//slackSend (color: '#0AC9FF', message: "SUCCESS: Docker Image Build '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
}
}
//python-crawling-server push
stage('backend - Docker Image Push') {
steps {
withDockerRegistry([credentialsId: "${dockerhubRegistryCredentail}", url:""]) {
sh "docker push ${dockerhubRegistry}:backend.${currentBuild.number}"
sh "docker push ${dockerhubRegistry}:backend.latest"
// 10초 쉰 후에 다음 작업 이어나가도록 함
sleep 10
}
}
post {
failure {
echo 'Docker Image Push failure'
sh "docker rmi ${dockerhubRegistry}:backend.${currentBuild.number}"
sh "docker rmi ${dockerhubRegistry}:backend.latest"
//slackSend (color: '#FF0000', message: "FAILED: Docker Image Push '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
success {
echo 'Docker Image Push success'
sh "docker rmi ${dockerhubRegistry}:backend.${currentBuild.number}"
sh "docker rmi ${dockerhubRegistry}:backend.latest"
//slackSend (color: '#0AC9FF', message: "SUCCESS: Docker Image Push '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
}
}
//php-apache-server build
stage('frontend - Docker Image Build') {
steps {
// 도커 이미지 빌드
sh "docker build ./front-end -t ${dockerhubRegistry}:frontend.${currentBuild.number}"
sh "docker build ./front-end -t ${dockerhubRegistry}:frontend.latest"
}
// 성공, 실패 시 슬랙에 알람오도록 설정
post {
failure {
echo 'Docker image build failure'
//slackSend (color: '#FF0000', message: "FAILED: Docker Image Build '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
success {
echo 'Docker image build success'
//slackSend (color: '#0AC9FF', message: "SUCCESS: Docker Image Build '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
}
}
//php-apache-server push
stage('frontend - Docker Image Push') {
steps {
// 젠킨스에 등록한 계정으로 ECR 에 이미지 푸시
withDockerRegistry([credentialsId: "${dockerhubRegistryCredentail}", url:""]) {
sh "docker push ${dockerhubRegistry}:frontend.${currentBuild.number}"
sh "docker push ${dockerhubRegistry}:frontend.latest"
// 10초 쉰 후에 다음 작업 이어나가도록 함
sleep 10
}
}
post {
failure {
echo 'Docker Image Push failure'
sh "docker rmi ${dockerhubRegistry}:frontend.${currentBuild.number}"
sh "docker rmi ${dockerhubRegistry}:frontend.latest"
//slackSend (color: '#FF0000', message: "FAILED: Docker Image Push '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
success {
echo 'Docker Image Push success'
sh "docker rmi ${dockerhubRegistry}:frontend.${currentBuild.number}"
sh "docker rmi ${dockerhubRegistry}:frontend.latest"
//slackSend (color: '#0AC9FF', message: "SUCCESS: Docker Image Push '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
}
}
// updated docker image 태그를 git push
stage('Deploy') {
steps {
//sh "ansible prod-web-blackpink -m ping -i Inventory.ini"
sh "ansible-playbook playbook.yaml -i Inventory.ini"
} /*
post {
failure {
echo 'K8S Manifest Update failure'
slackSend (color: '#FF0000', message: "FAILED: K8S Manifest Update '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
success {
echo 'K8s Manifest Update success'
slackSend (color: '#0AC9FF', message: "SUCCESS: K8S Manifest Update '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
} */
}
}
}
이전 쿠버네티스 프로젝트에서 적용했었던 파일을 조금 수정했다.
2개의 컨테이너를 빌드하고 hub.docker 로 푸시하도록 했고 배포는 ansible playbook 을 사용하게끔 했다.
ansible 연결
ansible 설치
sudo apt-get install software-properties-common
sudo apt-add-repository ppa:ansible/ansible
sudo apt-get update
sudo apt-get install ansible
ansible -h
jenkins-server 에 설치해줬다
inventory 등록
private ip 주소로 등록해주려고 한다
기본 inventory 파일 경로 - /etc/ansible/hosts
나는 새로 만들어서 사용할꺼다
#Inventory.ini
[prod]
web-blackpink ansible_host=172.31.14.71
[prod:vars]
ansible_connection=ssh
ansible_port=22
ansible_user=ubuntu
ansible_private_key_file=/home/ubuntu/keypairs/prod-blackpink.pem
ping test
prod-ansible 디렉토리로 이동해서
ubuntu@jenkins-server:~/prod-ansible$ ansible prod -m ping -i Inventory.ini
[DEPRECATION WARNING]: COMMAND_WARNINGS option, the command warnings feature is being removed. This
feature will be removed from ansible-core in version 2.14. Deprecation warnings can be disabled by
setting deprecation_warnings=False in ansible.cfg.
web-blackpink | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
ubuntu@jenkins-server:~/prod-ansible$
이렇게 실행해주면 ping 결과가 잘 확인되었다
playbook.yaml
---
- name: start docker container
hosts: prod-web-blackpink
remote_user: root
gather_facts: false
tasks:
- name: Stop old-version web-server container
community.docker.docker_container:
name: web-server
state: stopped
- name: Stop old-version crawling-server container
community.docker.docker_container:
name: crawling-server
state: stopped
- name: remove old-version web-server container
community.docker.docker_container:
name: web-server
state: absent
- name: remove old-version crawling-server container
community.docker.docker_container:
name: crawling-server
state: absent
- name: remove image frontend.latest
command: "docker image rm cyaninn/prod-web-blackpinkinyourarea:frontend.latest"
- name: remove image backend.latest
command: "docker image rm cyaninn/prod-web-blackpinkinyourarea:backend.latest"
- name: running web-server
command: "docker run -d -it --name web-server -p 80:80 cyaninn/prod-web-blackpinkinyourarea:frontend.latest"
- name: running python-server
command: "docker run -d -it --name crawling-server cyaninn/prod-web-blackpinkinyourarea:backend.latest"
playbook은 2개의 컨테이너를 각각 멈춘 후 삭제하고
이전 버전의 latest 이미지를 삭제해주고
다시 최신 버전의 컨테이너 이미지로 docker run 하게 해줬다.
(latest 라는 같은 태그로 계속 남아있어서 업데이트가 되지 않았었따..)
7. Route53
ASDF
'Archive > [Infra+Web] fanpage:BLACKPINKINYOURAREA' 카테고리의 다른 글
[Web]BLACKPINK IN YOUR AREA-28-html 이벤트 핸들러 (0) | 2022.12.30 |
---|---|
[Web]BLACKPINK IN YOUR AREA-27-dockerfile ㅡㅡ (0) | 2022.12.27 |
[Web]BLACKPINK IN YOUR AREA-25-CI/CD 환경 구축-3 (0) | 2022.12.18 |
[Web]BLACKPINK IN YOUR AREA-24-CI/CD 환경 구축 + 부분 문서화-2 (0) | 2022.12.07 |
[Web]BLACKPINK IN YOUR AREA-23-CI/CD 환경 구축 + 부분 문서화 (0) | 2022.12.01 |