๋ฌด์ค๋จ ๋ฐฐํฌ๋?
๋ฌด์ค๋จ ๋ฐฐํฌ๋ ์ฌ์ฉ์์๊ฒ ์๋น์ค ์ค๋จ ์์ด ์๋ก์ด ๋ฒ์ ์ ์ํํธ์จ์ด๋ฅผ ๋ฐฐํฌํ๋ ๋ฐฉ๋ฒ์ ๋๋ค.
์๋น์ค๋ฅผ ์ด์ ์ค์ผ ๋ ์๋ก์ด ๋ฒ์ ์ ๋ฐฐํฌํ๊ธฐ ์ํด์๋
1. ๊ธฐ์กด ์๋น์ค๋ฅผ ์ข ๋ฃ
2. ์๋ก์ด ์๋น์ค ์์
๋๊ฐ์ ํ๋ก์ธ์ค๋ฅผ ์ง๋์ณ์ผ ํ๋ค.
์ด๋ ๊ฒ ๋๋ฉด 1๋ฒ๊ณผ 2๋ฒ ์ฌ์ด์ ๊ณผ์ ์์ ๋ค์ดํ์์ด ํ์ฐ์ ์ผ๋ก ๋ฐ์ํ๋ฉฐ, ๋ค์ดํ์ ๋์ ์ฌ์ฉ์๋ค์ ์๋น์ค๋ฅผ ์ด์ฉํ ์ ์๋ค. ์ด ๋ค์ดํ์์ ํด๊ฒฐํด์ฃผ๋ ๋ฐฉ๋ฒ์ด ๋ฐ๋ก ๋ฌด์ค๋จ ๋ฐฐํฌ์ด๋ค. ์ฆ, ๋ง๊ทธ๋๋ก ์ค๋จ์ด ์๋ ๋ฐฐํฌ๋ฅผ ๋ฌด์ค๋จ ๋ฐฐํฌ์ด๋ค.
๋ค์ดํ์
์์คํ ์ ์ด์ฉํ ์ ์๋ ์๊ฐ.
๋ฌด์ค๋จ ๋ฐฐํฌ ๊ตฌํ ๋ฐฉ์์๋ ์ฌ๋ฌ๊ฐ์ง ๊ธฐ์ ์ด ์กด์ฌํ๋ค.
- AWS์์ Blue-Greem ๋ฌด์ค๋จ ๋ฐฐํฌ
- ๋์ปค๋ฅผ ์ด์ฉํ ๋ฌด์ค๋จ ๋ฐฐํฌ
- L4, L7 ์ค์์น๋ฅผ ์ด์ฉํ ๋ฌด์ค๋จ ๋ฐฐํฌ
- Nginx๋ฅผ ์ด์ฉํ ๋ฌด์ค๋จ ๋ฐฐํฌ
๋๋ Nginx๋ฅผ ์ด์ฉํ์ฌ ๋ฌด์ค๋จ ๋ฐฐํฌ๋ฅผ ์งํํ์๊ณ , ๋ฐฐํฌ ์ ๋ต์ผ๋ก Blue/Green ์ ์ฌ์ฉํ์๋ค.
๋ฌด์ค๋จ ๋ฐฐํฌ ๊ณผ์
1. ๋ฆฌ๋ฒ์ค ํ๋ก์๋ก nginx์ 80ํฌํธ๋ฅผ ์คํ๋ง ์ฑ์ผ๋ก ์ฐ๊ฒฐ์์ผ์ค ๊ฒ์ด๋ค. ์ด๊ธฐ์๋ 8080ํฌํธ๋ฅผ ๋ฐ๋ผ๋ณด๊ณ ์๋ค๊ณ ๊ฐ์ ํ๋ค.
2. ์คํ๋ง ์ฑ์ 8080ํฌํธ ๋๋ 8081ํฌํธ๋ก ๋ฐฐํฌํ ๊ฒ์ด๋ค.
3. ๋ง์ฝ ์คํ๋ง A(8080)๋ก ๋ฐฐํฌ ์ค ์ด์๋ค๋ฉด, ์คํ๋ง B(8081)๋ฅผ ์๋ก ์คํํ๋ค.
4. ์คํ๋ง B๊ฐ ์ ์์ ์ผ๋ก ๋ค ์คํ๋๋ฉด, nginx๊ฐ 8081ํฌํธ๋ฅผ ๋ฐ๋ผ๋ณด๋๋ก ํ๊ณ , nginx๋ฅผ ์ฌ์์ํ๋ค.
5. ๊ตฌ๋ ์ค์ด์๋ ์คํ๋ง A(8080)๋ ์ข ๋ฃํ๋ค.
6. ๋ง์ฝ ์คํ๋ง B(8081)๋ก ๋ฐฐํฌ ์ค ์ด์๋ค๋ฉด, ์คํ๋ง A(8080)๋ฅผ ์๋ก ์คํํ๋ค.
7. ์คํ๋ง A๊ฐ ์ ์์ ์ผ๋ก ๋ค ์คํ๋๋ฉด, nginx๊ฐ 8080ํฌํธ๋ฅผ ๋ฐ๋ผ๋ณด๋๋ก ํ๊ณ , nginx๋ฅผ ์ฌ์์ํ๋ค.
8. ๊ตฌ๋ ์ค์ด์๋ ์คํ๋ง B(8081)๋ ์ข ๋ฃํ๋ค.
๋จผ์ , ์คํ๋ง ๋ถํธ๊ฐ ์ ์คํ๋์๋์ง ํ์ธํ๊ธฐ ์ํด ๋ค์ dependency๋ฅผ ์ถ๊ฐํด์ฃผ์๋ค.
implementation 'org.springframework.boot:spring-boot-starter-actuator'
actuaor ๋ ์คํ๋ง ๋ถํธ๊ฐ ์ ๊ณตํ๋ ๊ธฐ๋ฅ์ผ๋ก ์งํ ์์ง, ์ถ์ , ๊ฐ์ฌ ๋ฑ์ ๋ชจ๋ํฐ๋ง์ ์ฝ๊ฒ ํ ์ ์๋ ๋ค์ํ ํธ์ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
# application.yml
management:
endpoint:
endpoints:
web:
base-path: /tickerbell
์์ ๊ฐ์ด application.yml์ ์ค์ ํด์ฃผ์ด /tickerBell/health ๋ก ์ ์ํ ์ ์๋ฒ๊ฐ ์ผ์ ธ์๋์ง ๊บผ์ ธ์๋์ง ์๋ต์ ๋ฐ๋๋ก ํ์๋ค.
์๋ต์ ์๋์ ๊ฐ์ด ๋์จ๋ค.
{"status":"UP"}
์ด์ nginx ์ค์ ์ ํด๋ณด์.
# /etc/nginx/sites-available/tickerBell.conf
server {
include /etc/nginx/conf.d/service-url.inc;
server_name tickerBell;
charset utf-8;
location / {
proxy_pass $service_url;
proxy_set_header X-Real-Ip $remote_addr;
proxy_set_header x-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
}
์๋ฒ ๋ธ๋ก ๋ด์ include /etc/nginx/conf.d/service-url.inc; ๋ผ๋ ์ฝ๋๊ฐ ์๋๋ฐ,
service-url.inc๋ ๊ตฌ๋ ์ค์ธ ์คํ๋ง ์ฑ์ url์ ๊ธฐ์ ํด๋ ํ์ผ์ด๋ค.
ํด๋น ํ์ผ์ includeํ๋ฉด, ๊ทธ ํ์ผ์ ์์ฑ๋ ๋ณ์๋ฅผ ์ด์ฉํ ์ ์๋ค.
proxy_pass $service_url; ์์ service_url์ service-url.inc ์ ์ค์ ํ ๋ณ์ ๊ฐ์ด๋ค.
service-url.inc
# /etc/nginx/conf.d/service-url.inc
set $service_url http://127.0.0.1:8080;
์ฌ๊ธฐ๊น์ง ๋์์ผ๋ฉด, ln -s ๋ก tickerBell.conf์ ์ฌ๋ณผ๋ฆญ๋งํฌ๋ฅผ sites-enabled์ ๋ฑ๋กํด์ฃผ๊ณ nginx๋ฅผ ์์ํด์ค๋ค.
# ln -s ์๋ณธํ์ผ ๊ฒฝ๋ก/๋๋ ํ ๋ฆฌ ์ฌ๋ณผ๋ฆญ๋งํฌ์ด๋ฆ
sudo ln -s /etc/nginx/sites-available/tickerBell.conf /etc/nginx/sites-enabled/
sudo service nginx restart
spring boot ํ๋ก์ ํธ์ ๋ฃจํธ๊ฒฝ๋ก์ Dockerfile์ ์์ฑํด์ฃผ์๋ค.
FROM openjdk:17-jdk-alpine
# JAR_FILE ๋ณ์ ์ ์ -> ๊ธฐ๋ณธ์ ์ผ๋ก jar file์ด 2๊ฐ์ด๊ธฐ ๋๋ฌธ์ ์ด๋ฆ์ ํน์ ํด์ผํจ
ARG JAR_FILE=./build/libs/tickerBell-0.0.1-SNAPSHOT.jar
# JAR ํ์ผ ๋ฉ์ธ ๋๋ ํ ๋ฆฌ์ ๋ณต์ฌ
COPY ${JAR_FILE} app.jar
# ์์คํ
์ง์
์ ์ ์
ENTRYPOINT ["java","-Duser.timezone=Asia/Seoul","-Dspring.profiles.active=prod", "-jar","/app.jar"]
docker-compose ํ์ผ์ ๋ ๊ฐ ์์ฑํด์ค๋ค.
- docker-compose.a.yml
version: "3.8"
services:
tickerbell-a:
container_name: tickerbell-a
image: ${DOCKERHUB_USERNAME}/tickerbell
volumes: ...
environment: ...
ports:
- "8081:8080"
restart: always
networks:
default:
external:
name: tickerbellnet
- docker-compose.b.yml
version: "3.8"
services:
tickerbell-b:
container_name: tickerbell-b
image: ${DOCKERHUB_USERNAME}/tickerbell
volumes: ...
environment: ...
ports:
- "8081:8080"
restart: always
networks:
default:
external:
name: tickerbellnet
๋ ๊ฐ์ docker-compose ํ์ผ์ port ๋ง๊ณ ๋ ์ ๋ถ ๋์ผํ๋ค.
์ฐธ๊ณ )
๊ธฐ์กด์ docker compose๋ฅผ ํตํด redis ์ spring ์ปจํ ์ด๋๋ฅผ ๋์ ๋๋ฐ redis๋ ๊ตณ์ด ๋ค์ ๋์ธ ํ์๊ฐ ์๊ธฐ ๋๋ฌธ์ docker compose์์ ๋นผ๊ณ ec2์ ๋ฐ๋ก ๋์ ๋ค.
ํ์ง๋ง ์ปจํ ์ด๋๋ผ๋ฆฌ ํต์ ํ๊ธฐ ์ํด์ ๊ฐ์ ๋คํธ์ํฌ์ ์์ด์ผ ํ๋ค.
docker-compose๋ฅผ ์ฌ์ฉํ์ฌ ๋์ด ์ฌ๋ฌ ๊ฐ์ ์ปจํ ์ด๋๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๊ฐ์ ๋คํธ์ํฌ์ ๋ฌถ์ฌ ์๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก network๋ฅผ ์ง์ ํด์ฃผ์ง ์์๋ ๋์ง๋ง, ๋๊ฐ์ ๊ฒฝ์ฐ์ redis๋ฅผ ๋จผ์ ec2์ ๋์๋์๊ธฐ ๋๋ฌธ์ network๋ฅผ ์ง์ ํด์ฃผ์ด์ผ ํ๋ค. tickerbellnet ์ผ๋ก ๋คํธ์ํฌ๋ฅผ ์์ฑํด์ฃผ์๊ณ redis์ docker-compose์์ ๋์ฐ๋ ์คํ๋ง์ด ๊ฐ์ network๋ฅผ ์ฌ์ฉํ๋๋ก ํด์ฃผ์๋ค.
์ด์ ๋ฐฐํฌ๋ฅผ ์ํ ์คํฌ๋ฆฝํธ ํ์ผ deploy.sh๋ฅผ ์์ฑํด์ฃผ์๋ค.
์ฝ๋์ ๋ํ ์ค๋ช ์ ์ฃผ์์ ๋ฌ์๋์๋ค.
RUNNING_CONTAINER=$(sudo docker ps)
echo "์คํ์ค์ธ ์ปจํ
์ด๋ ๋ชฉ๋ก: ${RUNNING_CONTAINER}"
# ์คํ ์ค์ธ ๋์ปค ์ปดํฌ์ฆ ํ์ธ
EXIST_A=$(sudo docker ps -q -f name=tickerbell-a)
echo "EXIST_A ๊ฐ: ${EXIST_A}"
if [ -z "${EXIST_A}" ] # -z๋ ๋ฌธ์์ด ๊ธธ์ด๊ฐ 0์ด๋ฉด true. A๊ฐ ์คํ ์ค์ด์ง ์๋ค๋ ์๋ฏธ.
then
# B๊ฐ ์คํ ์ค์ธ ๊ฒฝ์ฐ
START_CONTAINER=a
TERMINATE_CONTAINER=b
START_PORT=8080
TERMINATE_PORT=8081
else
# A๊ฐ ์คํ ์ค์ธ ๊ฒฝ์ฐ
START_CONTAINER=b
TERMINATE_CONTAINER=a
START_PORT=8081
TERMINATE_PORT=8080
fi
echo "tickerbell-${START_CONTAINER} up"
# ์คํํด์ผํ๋ ์ปจํ
์ด๋ docker-compose๋ก ์คํ. -p๋ docker-compose ํ๋ก์ ํธ์ ์ด๋ฆ์ ๋ถ์ฌ
# -f๋ docker-composeํ์ผ ๊ฒฝ๋ก๋ฅผ ์ง์
sudo docker-compose -f docker-compose.${START_CONTAINER}.yml up -d --build
RUNNING_CONTAINER=$(sudo docker ps)
echo "์คํ์ค์ธ ์ปจํ
์ด๋ ๋ชฉ๋ก: ${RUNNING_CONTAINER}"
for cnt in {1..10} # 10๋ฒ ์คํ
do
echo "check server start.."
# ์คํ๋ง๋ถํธ์ ๋ฑ๋กํ๋ actuator๋ก ์คํ๋์๋์ง ํ์ธ
UP=$(curl -s http://127.0.0.1:${START_PORT}/tickerBell/health | grep 'UP')
if [ -z "${UP}" ] # ์คํ๋์๋ค๋ฉด break
then
echo "server not start.."
else
break
fi
echo "wait 10 seconds" # 10 ์ด๊ฐ ๋๊ธฐ
sleep 10
done
if [ $cnt -eq 10 ] # 10๋ฒ๋์ ์คํ์ด ์๋์์ผ๋ฉด ๋ฐฐํฌ ์คํจ, ๊ฐ์ ์ข
๋ฃ
then
echo "deployment failed."
exit 1
fi
echo "server start!"
echo "change nginx server port"
# sed ๋ช
๋ น์ด๋ฅผ ์ด์ฉํด์ ์๊น ์ง์ ํด์คฌ๋ service-url.inc์ url๊ฐ์ ๋ณ๊ฒฝํด์ค๋๋ค.
# sed -i "s/๊ธฐ์กด๋ฌธ์์ด/๋ณ๊ฒฝํ ๋ฌธ์์ด" ํ์ผ๊ฒฝ๋ก ์
๋๋ค.
# ์ข
๋ฃ๋๋ ํฌํธ๋ฅผ ์๋ก ์์๋๋ ํฌํธ๋ก ๊ฐ์ ๋ณ๊ฒฝํด์ค๋๋ค.
sudo sed -i "s/${TERMINATE_PORT}/${START_PORT}/" /etc/nginx/conf.d/service-url.inc
# ์๋ก์ด ํฌํธ๋ก ์คํ๋ง๋ถํธ๊ฐ ๊ตฌ๋ ๋๊ณ , nginx์ ํฌํธ๋ฅผ ๋ณ๊ฒฝํด์ฃผ์๋ค๋ฉด, nginx ์ฌ์์ํด์ค๋๋ค.
echo "nginx reload.."
sudo service nginx reload
# ๊ธฐ์กด์ ์คํ ์ค์ด์๋ docker-compose๋ ์ข
๋ฃ์์ผ์ค๋๋ค.
echo "tickerbell-${TERMINATE_CONTAINER} down"
sudo docker-compose -f docker-compose.${TERMINATE_CONTAINER}.yml down
echo "success deployment"
์์ฑํ deploy.sh ํ์ผ์ ์คํํ๊ธฐ ์ํด github action deploy.yml ํ์ผ์ ์๋์ ๊ฐ์ด ์์ฑํด์ฃผ์๋ค. (๊ด๋ จ๋ ์ผ๋ถ ์ฝ๋์)
cd-pipeline:
needs: build-docker-image
runs-on: ubuntu-latest
steps:
# docker-compose.a.yml ํ์ผ์ ec2 /home/ubuntu ์ scp
- uses: actions/checkout@master
- name: copy docker-compose.a.yml to remote server
uses: appleboy/scp-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ubuntu
key: ${{ secrets.SSH_KEY }}
port: 22
source: "./docker-compose.a.yml"
target: "/home/ubuntu/"
# docker-compose.b.yml ํ์ผ์ ec2 /home/ubuntu ์ scp
- uses: actions/checkout@master
- name: copy docker-compose.b.yml to remote server
uses: appleboy/scp-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ubuntu
key: ${{ secrets.SSH_KEY }}
port: 22
source: "./docker-compose.b.yml"
target: "/home/ubuntu/"
- name: copy deploy.sh to remote server
uses: appleboy/scp-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ubuntu
key: ${{ secrets.SSH_KEY }}
port: 22
source: "./deploy.sh"
target: "/home/ubuntu/"
- name: Execute deploy.sh script remotely
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ubuntu
key: ${{ secrets.SSH_KEY }}
port: 22
script: |
chmod +x /home/ubuntu/deploy.sh
/home/ubuntu/deploy.sh
์์์ ์ค๋ช ํ ๋ฌด์ค๋จ ๋ฐฐํฌ ๊ณผ์ ์ฒ๋ผ ๊ธฐ์กด์ ์คํ๋งB ๊ฐ ๊ตฌ๋์ค์ด์๊ธฐ์ ์คํ๋งA๊ฐ ์๋ก ๋์์ก๊ณ ,
์คํ๋งA์ ์๋ฒ๊ฐ ์์ ํ ์ผ์ง ํ ์คํ๋งB๋ ์ข ๋ฃ๋๋ ๋ชจ์ต์ ๋ณผ ์ ์๋ค.
'devOps ๐ก' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[ELK] elastic stack ๊ตฌ์ถํ์ฌ ๋ก๊ทธ ์์ง, ๊ด๋ฆฌ ํ๊ธฐ (+ spring boot) (0) | 2024.01.30 |
---|---|
Prometheus & Grafana ๋ฅผ ํตํด ๋ชจ๋ํฐ๋ง์ ํด๋ณด์ (Spring boot) (2) | 2024.01.22 |
์คํ๋ง Docker, githubaction ์ฌ์ฉํ์ฌ ๋ฐฐํฌ ์๋ํ(CI/CD) ํ์ดํ๋ผ์ธ ๊ตฌ์ถ (1) | 2023.12.01 |
๋ฐฐํฌ ๊ณผ์ ์์ ์์๋ ์ผ๋ค (1) | 2023.10.07 |
[GIT] Git Flow | Github Flow ์ ๋ต (1) | 2023.08.24 |