SSAFY 2학기 첫 프로젝트를 하면서 배포 파트를 내가 맡게 되었다. 백엔드에서 API를 개발하고 로컬에서만 배포하고 테스트해봤기 때문에 실제로 인터넷에서 내가 만든 웹 사이트의 결과물을 보고 싶었다. 그래서 꼭 배포를 해보고자 생각했다.
SpringBoot 서버를 배포했어야 했는데 Docker랑 리눅스에 대해선 어느정도 이전에 사용해봤기 때문에 수동으로 배포하는건 사실 어렵지 않았다. 도커를 사용하지 않을거면 EC2에 java를 설치하고 빌드한 jar 파일을 리눅스 scp 명령어로 파일을 옮겨 실행시키면 끝이고 Docker 를 사용할 것이면 Dockerfile을 작성해서 이미지를 생성하고 Container를 생성하면 된다.
여기까지가 기본적인 수동 배포 방법인데 우리 팀에서는 이렇게 배포하는 사람을 인간 젠킨스라고 불렀다. 처음에는 백엔드 개발할 것도 바쁘고 해서 인간 젠킨스로 활동하다가 점차 배포할 일이 많아져서 빨리 자동 배포를 구축하고자 했다. 사진이 많아서 글이 매우 길다.
우리 팀의 최종 배포 아키텍처이다. 뭔가 오브젝트가 많아서 복잡해보이지만 크게 단계를 나누자면 아래와 같다.
# sudo ssh -i [pem키 위치] [접속 계정]@[접속할 도메인]
$ sudo ssh -i I8A601T.pem ubuntu@i8a601.p.ssafy.io
ssh 전용 폴더 생성
mkdir ~/.ssh
cd ~/.ssh // ssh 폴더 생성 및 이동
cp [로컬 pem 키 위치] ~/.ssh // pem 키 옮기기
vi config // config 파일 생성
config 내용 추가
Host ssafy
HostName [서버 ip 주소]
User ubuntu
IdentityFile ~/.ssh/[pem키 파일 명].pem
ssafy 계정에 접속
ssh ssafy
sudo apt update
sudo apt upgrade -y
sudo apt install -y build-essential
sudo ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
# 시간 확인
date
sudo apt -y update
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common
sudo wget -qO- https://get.docker.com/ | sh
sudo systemctl start docker
sudo systemctl enable docker
sudo systemctl enable docker
sudo systemctl restart docker
docker -v
$ sudo apt install jq
$ VERSION=$(curl --silent https://api.github.com/repos/docker/compose/releases/latest | jq .name -r)
$ DESTINATION=/usr/bin/docker-compose
$ sudo curl -L https://github.com/docker/compose/releases/download/${VERSION}/docker-compose-$(uname -s)-$(uname -m) -o $DESTINATION
$ sudo chmod 755 $DESTINATION
// 터미널 재접속 하기!
$ docker-compose -v
Docker Compose version v2.x.x
$ sudo dd if=/dev/zero of=/swapfile bs=128M count=16
$ sudo chmod 600 /swapfile
$ sudo mkswap /swapfile
$ sudo swapon /swapfile
$ sudo swapon -s
$ sudo vi /etc/fstab
/swapfile swap swap defaults 0 0 // 제일 아랫줄에 추가 ! (이 주석은 빼고)
$ docker pull mariadb
$ docker run --name mariadb -d -p 3306:3306 -v /var/lib/mysql_main:/var/lib/mysql --restart=always -e MYSQL_ROOT_PASSWORD=root mariadb
docker exec -it mariadb /bin/bash
mysql -u root -p
비밀번호는 "root"
Mariadb 사용자 추가하기
예시) create user 'user_name'@'XXX.XXX.XXX.XXX' identified by 'user_password';
create user 'ssafy601'@'%' identified by 'ssafy601';
Mariadb - 사용자 권한 부여하기
예시) grant all privileges on db_name.* to 'user_name'@'XXX.XXX.XXX.XXX';
flush privileges;
grant all privileges on *.* to 'ssafy601'@'%';
flush privileges;
Mariadb - 데이터 베이스 만들기
예시) create database [db_name];
create database ssafy601;
docker pull redis:alpine
docker network create redis-network
docker inspect redis-network
docker run --name local-redis -p 6379:6379 --network redis-network -v /redis_temp:/data -d redis:alpine redis-server --appendonly yes
docker ps -a
# 실행 중인 redis 컨테이너에 대해 docker redis-cli 로 직접 진입
docker run -it --network redis-network --rm redis:alpine redis-cli -h local-redis
# bash로도 진입 가능하다.
docker run -it --network redis-network --rm redis:alpine bash
redis-cli
# slaveof no one : 현재 슬레이브(복제)인 자신을 마스터로 만듭니다.
127.0.0.1:6379> slaveof no one
# 폴더 생성
RUN mkdir config && cd config
# 아래 내용 작성
$ vi Dockerfile
FROM jenkins/jenkins:jdk17
#도커를 실행하기 위한 root 계정으로 전환
USER root
#도커 설치
COPY docker_install.sh /docker_install.sh
RUN chmod +x /docker_install.sh
RUN /docker_install.sh
#설치 후 도커그룹의 jenkins 계정 생성 후 해당 계정으로 변경
RUN groupadd -f docker
RUN usermod -aG docker jenkins
USER jenkins
#!/bin/sh
apt-get update && \
apt-get -y install apt-transport-https \
ca-certificates \
curl \
gnupg2 \
zip \
unzip \
software-properties-common && \
curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey && \
add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \
$(lsb_release -cs) \
stable" && \
apt-get update && \
apt-get -y install docker-ce
docker build -t jenkins/myjenkins .
$ mkdir /var/jenkinsDir/
$ sudo chown 1000 /var/jenkinsDir/
docker run -d -p 9090:8080 --name=jenkinscicd \
-e TZ=Asia/Seoul
-v /var/jenkinsDir:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
jenkins/myjenkins
-d
: 는 백그라운드에서 실행을 의미 -p
는 매핑할 포트를 의미합니다. ( p가 port의 단축어가 아니었음 .. ) :
기준으로 왼쪽은 로컬포트, 오른쪽은 도커 이미지의 포트를 의미합니다. 도커 이미지에서의 8080 포트를 로컬 포트 9090으로 매핑한다는 뜻입니다.-v /var/run/docker.sock:/var/run/docker.sock \
jenkins/myjenkins
이 옵션은 로컬의 도커와 젠킨스 내에서 사용할 도커 엔진을 동일한 것으로 사용하겠다는 의미입니다. v
옵션은 ":"를 기준으로 왼쪽의 로컬 경로를 오른쪽의 컨테이너 경로로 마운트 해줍니다. 즉, 제 컴퓨터의 사용자경로/jenkinsDir
을 컨테이너의 /var/jenkins_home
과 바인드 시켜준다는 것입니다. 물론, 양방향으로 연결됩니다. 컨테이너가 종료되거나 알 수없는 오류로 정지되어도, jenkins_home에 남아있는 소중한 설정 파일들은 로컬 경로에 남아있게 됩니다./var/run/docker.sock
에 대한 권한을 설정해주어야 합니다./var/run/docker.sock
의 권한이 소유자와 그룹 모두 root였기 때문에 이제 그룹을 root에서 docker
로 변경해줄겁니다.docker ps -a
docker exec -it -u root 컨테이너ID /bin/bash
exec
는 컨테이너에 명령어를 실행시키는 명령어인데, /bin/bash와 옵션 -it를 줌으로써 컨테이너의 쉘에 접속할 수 있습니다.
이제 정말로 root 계정으로 컨테이너에 접속하기 위해 컨테이너ID에 0bc를 입력해 실행합니다.
sudo chown root:docker /var/run/docker.sock
docker restart [컨테이너 ID]
docker logs [jenkins 컨테이너 ID]
인바운드 규칙 편집
클릭
규칙 추가
클릭 → 유형: TCP
, 포트 범위: {등록할 포트 번호}
bash start-prod.sh
빌드 유발
→ Build when …
→ 고급
→ 하단에 Secret token Generate
→ 토큰 발급 완료!docker-compose -f docker-compose-prod.yml pull
COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker-compose -f docker-compose-prod.yml up --build -d
docker rmi -f $(docker images -f "dangling=true" -q) || true
version: "3"
services:
server:
container_name: server
build:
context: ./SHabit-back
args:
SERVER_MODE: prod
ports:
- 8080:8080
environment:
- TZ=Asia/Seoul
client:
container_name: client
build:
context: ./shabit-front
dockerfile: Dockerfile.dev
ports:
- 3000:3000
depends_on:
- server
nginx:
container_name: nginx
build: ./.nginx
depends_on:
- server
- client
volumes:
- .nginx/conf.d:/etc/nginx/conf.d
- .nginx/zerossl:/var/www/zerossl/.well-known/pki-validation
- .nginx/cert:/cert
ports:
- 80:80
- 443:443
https://github.com/wlwlsus/shabit
https://velog.io/@hind_sight/Docker-Jenkins-도커와-젠킨스를-활용한-Spring-Boot-CICD