set $service_url http://127.0.0.1:8081;
# Your App
upstream yourapp {
server localhost:5442;
}
upstream openviduserver {
server localhost:5443;
}
server {
listen 80;
listen [::]:80;
server_name pokerface-server.ddns.net;
# Redirect to https
location / {
rewrite ^(.*) https://pokerface-server.ddns.net:443$1 permanent;
}
# letsencrypt
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location /nginx_status {
stub_status;
allow 127.0.0.1; #only allow requests from localhost
deny all; #deny all other hosts
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name pokerface-server.ddns.net;
# SSL Config
ssl_certificate /etc/letsencrypt/live/pokerface-server.ddns.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/pokerface-server.ddns.net/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/pokerface-server.ddns.net/fullchain.pem;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 5m;
ssl_stapling on;
ssl_stapling_verify on;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
ssl_prefer_server_ciphers off;
add_header Strict-Transport-Security "max-age=63072000" always;
# Proxy
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Proto https;
proxy_headers_hash_bucket_size 512;
proxy_redirect off;
# Websockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
include /etc/nginx/conf.d/service-url.inc;
# Your App
location / {
root /home/build;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass $service_url;
}
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
nginx:
image: openvidu/openvidu-proxy:2.28.0
restart: always
network_mode: host
volumes:
- ./certificates:/etc/letsencrypt
- ./owncert:/owncert
- ./custom-nginx-vhosts:/etc/nginx/vhost.d/
- ./custom-nginx-locations:/custom-nginx-locations
- ${OPENVIDU_RECORDING_CUSTOM_LAYOUT}:/opt/openvidu/custom-layout
- ./custom-nginx.conf:/custom-nginx/custom-nginx.conf
- ./nginx.conf:/etc/nginx/nginx.conf
- ./service-url.inc:/etc/nginx/conf.d/service-url.inc
- ~/app/build:/home/build
environment:
- DOMAIN_OR_PUBLIC_IP=${DOMAIN_OR_PUBLIC_IP}
- CERTIFICATE_TYPE=${CERTIFICATE_TYPE}
- LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL}
- PROXY_HTTP_PORT=${HTTP_PORT:-}
- PROXY_HTTPS_PORT=${HTTPS_PORT:-}
- PROXY_HTTPS_PROTOCOLS=${HTTPS_PROTOCOLS:-}
- PROXY_HTTPS_CIPHERS=${HTTPS_CIPHERS:-}
- PROXY_HTTPS_HSTS=${HTTPS_HSTS:-}
- ALLOWED_ACCESS_TO_DASHBOARD=${ALLOWED_ACCESS_TO_DASHBOARD:-}
- ALLOWED_ACCESS_TO_RESTAPI=${ALLOWED_ACCESS_TO_RESTAPI:-}
- PROXY_MODE=CE
- WITH_APP=true
- SUPPORT_DEPRECATED_API=${SUPPORT_DEPRECATED_API:-false}
- REDIRECT_WWW=${REDIRECT_WWW:-false}
- WORKER_CONNECTIONS=${WORKER_CONNECTIONS:-10240}
- PUBLIC_IP=${PROXY_PUBLIC_IP:-auto-ipv4}
logging:
options:
max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
124,20 Bot
#!/bin/bash
echo "> 현재 구동중인 profile 확인"
CURRENT_PROFILE=$(curl -s http://localhost/utils/profile)
sleep 5
echo "> $CURRENT_PROFILE"
if [ $CURRENT_PROFILE == set1 ]
then
IDLE_PROFILE=set2
IDLE_PORT=8082
elif [ $CURRENT_PROFILE == set2 ]
then
IDLE_PROFILE=set1
IDLE_PORT=8081
else
echo "> 일치하는 Profile이 없습니다. Profile: $CURRENT_PROFILE"
echo "> default로 set1을 할당합니다. IDLE_PROFILE: set1"
IDLE_PROFILE=set1
IDLE_PORT=8081
fi
IMAGE_NAME=pokerface_server
TAG_ID=$(docker images | sort -r -k2 -h | grep "${IMAGE_NAME}" | awk 'BEGIN{tag = 1} NR==1{tag += $2} END{print tag}')
sleep 3
echo "> 도커 build 실행 : docker build --build-arg IDLE_PROFILE=${IDLE_PROFILE} -t ${IMAGE_NAME}:${TAG_ID} ."
docker build --build-arg IDLE_PROFILE=${IDLE_PROFILE} -t ${IMAGE_NAME}:${TAG_ID} /home/ubuntu/app
sleep 3
echo "> $IDLE_PROFILE 배포"
echo "> 도커 run 실행 : sudo docker run --name $IDLE_PROFILE -d --rm -p $IDLE_PORT:${IDLE_PORT} ${IMAGE_NAME}:${TAG_ID}"
docker run --name $IDLE_PROFILE -d --rm -p ${IDLE_PORT}:${IDLE_PORT} ${IMAGE_NAME}:${TAG_ID}
sleep 3
sudo docker ps
echo "> $IDLE_PROFILE 10초 후 Health check 시작"
echo "> curl -s http://localhost:$IDLE_PORT/actuator/health "
sleep 10
for retry_count in {1..10}
do
response=$(curl -s http://localhost:$IDLE_PORT/actuator/health)
up_count=$(echo $response | grep 'UP' | wc -l)
if [ $up_count -ge 1 ]
then
echo "> Health check 성공"
break
else
echo "> Health check의 응답을 알 수 없거나 혹은 status가 UP이 아닙니다."
echo "> Health check: ${response}"
fi
if [ $retry_count -eq 10 ]
then
echo "> Health check 실패. "
echo "> Nginx에 연결하지 않고 배포를 종료합니다."
exit 1
fi
echo "> Health check 연결 실패. 재시도..."
sleep 10
done
echo "> 스위칭을 시도합니다..."
sleep 5
/home/ubuntu/app/switch.sh
현재 실행 중인 프로파일을 조회해서, 새로운 프로젝트가 배포될 IDLE_PROFILE을 지정
생성할 이미지의 이름 지정 및 tag 생성
IMAGE_NAME=pokerface_server
TAG_ID=$(docker images | sort -r -k2 -h | grep "${IMAGE_NAME}" | awk 'BEGIN{tag = 1} NR==1{tag += $2} END{print tag}')
IMAGE_NAME
: 생성할 이미지 이름TAG_ID
: 이미지에 붙일 태그 지정docker images | sort -r -k2 -h | grep "${IMAGE_NAME}"
: docker 이미지들을 생성일자의 내림차순(제일 최근 이미지가 가장 위에 뜸)으로 정렬 후 IMAGE_NAME
변수에 들어있는 문자열을 이미지 이름에 지닌 레코드를 가져온다.(like 정규표현식)awk 'BEGIN{tag = 1} NR==1{tag += $2} END{print tag}'
: awk 문법begin & end
: 명령의 시작과 끝에 수행NR == 1{tag += $2}
: NR이 특정 위치의 레코드 값을 들고 오는 역할로 여기선 첫번째 레코드를 들고 온후 tag에 2번 째 필드 값(이전 태그 숫자)을 가져와서 더해준다.이미지 생성
docker build --build-arg IDLE_PROFILE=${IDLE_PROFILE} -t ${IMAGE_NAME}:${TAG_ID} /home/ubuntu/app
생성한 이미지를 컨테이너로 실행
docker run --name $IDLE_PROFILE -d -t --rm -p $IDLE_PORT:${IDLE_PORT} ${IMAGE_NAME}:${TAG_ID}
새로 생성된 프로젝트에 대해 health check를 수행한다.
#!/bin/bash
echo "> 현재 구동중인 Port 확인"
CURRENT_PROFILE=$(curl -s http://localhost/utils/profile)
sudo docker ps
if [ $CURRENT_PROFILE == set1 ]
then
CURRENT_PORT=8081
IDLE_PORT=8082
elif [ $CURRENT_PROFILE == set2 ]
then
CURRENT_PORT=8082
IDLE_PORT=8081
else
echo "> 일치하는 Profile이 없습니다. Profile:$CURRENT_PROFILE"
echo "> 8081을 할당합니다."
IDLE_PORT=8081
fi
echo "> 현재 구동중인 Port: $CURRENT_PORT"
echo "> 전환할 Port : $IDLE_PORT"
echo "> Port 전환"
echo "set \$service_url http://127.0.0.1:${IDLE_PORT};" | sudo tee /etc/nginx/conf.d/service-url.inc
sleep 3
echo "> 기존에 실행 중이던 ${CURRENT_PROFILE} 컨테이너 삭제"
sudo docker rm -f ${CURRENT_PROFILE}
echo "> Nginx Reload"
sudo service nginx reload
sudo service nginx restart
switch.sh 스크립트를 실행하여 서버 스위칭(ex. 8081 -> 8082)
echo "> 스위칭을 시도합니다..."
sleep 5
/home/ubuntu/app/switch.sh
#!/bin/bash
echo "> 현재 구동중인 Port 확인"
CURRENT_PROFILE=$(curl -s http://localhost/utils/profile)
if [ $CURRENT_PROFILE == set1 ]
then
CURRENT_PORT=8081
IDLE_PORT=8082
elif [ $CURRENT_PROFILE == set2 ]
then
CURRENT_PORT=8082
IDLE_PORT=8081
else
echo "> 일치하는 Profile이 없습니다. Profile:$CURRENT_PROFILE"
echo "> 8081을 할당합니다."
IDLE_PORT=8081
fi
echo "> 현재 구동중인 Port: $CURRENT_PORT"
echo "> 전환할 Port : $IDLE_PORT"
echo "> Port 전환"
echo "set \$service_url http://127.0.0.1:${IDLE_PORT};" | sudo tee /etc/nginx/conf.d/service-url.inc
echo "> ${CURRENT_PROFILE} 컨테이너 삭제"
sudo docker stop $CURRENT_PROFILE
sudo docker rm $CURRENT_PROFILE
echo "> Nginx Reload"
sudo service nginx reload
CURRENT_PROFILE
), 새롭게 가동할 서버 포트(IDLE_PORT
) 식별/etc/nginx/conf.d/service-url.inc
경로에 있는 nginx가 리버스 프록시 할 포트를 새롭게 가동할 서버의 포트번호로 수정tee
: 표준 입력(standard input)에서 읽어서 표준 출력(standard output) 과 파일에 쓰는 명령어FROM openjdk:11
ARG IDLE_PROFILE
ARG JAR_FILE=*.jar
ENV ENV_IDLE_PROFILE=$IDLE_PROFILE
COPY ${JAR_FILE} app.jar
RUN echo $ENV_IDLE_PROFILE
ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=${ENV_IDLE_PROFILE}", "/app.jar"]
FFOM
: 이미지 생성 시 사용할 이미지ARG
: Dockerfile 내부에서 사용할 변수 지정ENV
: Dockerfile 내부에서 사용할 환경 변수COPY
: 호스트OS의 파일 또는 디렉토리를 컨테이너 안의 경로로 복사*.jar(~~.jar)
를 복사하여 컨테이너 내부에 app.jar
를 생성ENTRYPOINT
: 컨테이너 시작 후 실행할 명령어를 지정CMD
명령어와 유사하지만 ENTRYPOINT
는 강제성을 지님(무조건 수행됨)-Dspring.profiles.active=${ENV_IDLE_PROFILE}
: 수행할 서버 타입(포트)를 지정Started by GitLab push by 서지오
Started by GitLab push by 서지오
Running as SYSTEM
Building in workspace /var/jenkins_home/workspace/pokerface
The recommended git tool is: NONE
using credential d36e63ee-e889-49ff-95d4-485fda318fcb
> git rev-parse --resolve-git-dir /var/jenkins_home/workspace/pokerface/.git # timeout=10
Fetching changes from the remote Git repository
> git config remote.origin.url https://lab.ssafy.com/s09-webmobile1-sub2/S09P12A603 # timeout=10
Fetching upstream changes from https://lab.ssafy.com/s09-webmobile1-sub2/S09P12A603
> git --version # timeout=10
> git --version # 'git version 2.30.2'
using GIT_ASKPASS to set credentials
> git fetch --tags --force --progress -- https://lab.ssafy.com/s09-webmobile1-sub2/S09P12A603 +refs/heads/*:refs/remotes/origin/* # timeout=10
skipping resolution of commit remotes/origin/feature/issue-116, since it originates from another repository
> git rev-parse refs/remotes/origin/feature/issue-116^{commit} # timeout=10
Checking out Revision c120fa7c82368531110ff737d6c9e3b2eff57218 (refs/remotes/origin/feature/issue-116)
> git config core.sparsecheckout # timeout=10
> git checkout -f c120fa7c82368531110ff737d6c9e3b2eff57218 # timeout=10
Commit message: "Fix : Gitlab & Webhook & Jenkins & Nginx 무중단 배포 최종"
> git rev-list --no-walk 996d0e0873673dcbc7db4caa8a3ce29b310b1ce5 # timeout=10
[Gradle] - Launching build.
[backend] $ /var/jenkins_home/tools/hudson.plugins.gradle.GradleInstallation/gradle-8.1.1/bin/gradle clean build
Starting a Gradle Daemon (subsequent builds will be faster)
> Task :clean
> Task :compileJava
> Task :processResources
> Task :classes
> Task :bootJarMainClassName
> Task :bootJar
> Task :jar SKIPPED
> Task :assemble
> Task :compileTestJava
> Task :processTestResources NO-SOURCE
> Task :testClasses
> Task :test
> Task :check
> Task :build
BUILD SUCCESSFUL in 11s
7 actionable tasks: 7 executed
Build step 'Invoke Gradle script' changed build result to SUCCESS
SSH: Connecting from host [de6773f73db5]
SSH: Connecting with configuration [server-ec2] ...
SSH: EXEC: completed after 35,621 ms
SSH: Disconnecting configuration [server-ec2] ...
SSH: Transferred 1 file(s)
Finished: SUCCESS
ubuntu@ip-172-26-1-239:~/app$ ./deploy.sh
> 현재 구동중인 profile 확인
> set1
> 도커 build 실행 : docker build --build-arg IDLE_PROFILE=set2 -t pokerface_server:2 .
[+] Building 0.7s (8/8) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 266B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/openjdk:11 0.6s
=> [internal] load build context 0.0s
=> => transferring context: 52B 0.0s
=> [1/3] FROM docker.io/library/openjdk:11@sha256:99bac5bf83633e3c7399aed725c8415e7b569b54e03e4599e580fc9cdb7c21ab 0.0s
=> CACHED [2/3] COPY *.jar app.jar 0.0s
=> CACHED [3/3] RUN echo set2 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:611a23c9bcf19235bcef4447b5880daabe3df5d2d0a0bc561ea83660d4b45789 0.0s
=> => naming to docker.io/library/pokerface_server:2 0.0s
> set2 배포
> 도커 run 실행 : sudo docker run --name set2 -d --rm -p 8082:8082 pokerface_server:2
022affdf96e3fd148a39b8715b02a6bd1ffefdc5f6d0c2c53e96368a48516cbf
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
022affdf96e3 pokerface_server:2 "java -jar -Dspring.…" 3 seconds ago Up 3 seconds 0.0.0.0:8082->8082/tcp, :::8082->8082/tcp set2
13ebae1049ec pokerface_server:1 "java -jar -Dspring.…" 12 minutes ago Up 12 minutes 0.0.0.0:8081->8081/tcp, :::8081->8081/tcp set1
de6773f73db5 jenkins/jenkins:lts "/usr/bin/tini -- /u…" 3 days ago Up 2 days 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp, 50000/tcp jenkins
> set2 10초 후 Health check 시작
> curl -s http://localhost:8082/actuator/health
> Health check 성공
> 스위칭을 시도합니다...
> 현재 구동중인 Port 확인
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
022affdf96e3 pokerface_server:2 "java -jar -Dspring.…" 18 seconds ago Up 18 seconds 0.0.0.0:8082->8082/tcp, :::8082->8082/tcp set2
13ebae1049ec pokerface_server:1 "java -jar -Dspring.…" 12 minutes ago Up 12 minutes 0.0.0.0:8081->8081/tcp, :::8081->8081/tcp set1
de6773f73db5 jenkins/jenkins:lts "/usr/bin/tini -- /u…" 3 days ago Up 2 days 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp, 50000/tcp jenkins
> 현재 구동중인 Port: 8081
> 전환할 Port : 8082
> Port 전환
set $service_url http://127.0.0.1:8082;
> 기존에 실행 중이던 set1 컨테이너 삭제
set1
> Nginx Reload
ubuntu@ip-172-26-1-239:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
af0bec666875 pokerface_server:3 "java -jar -Dspring.…" About a minute ago Up About a minute 0.0.0.0:8081->8081/tcp, :::8081->8081/tcp set1
de6773f73db5 jenkins/jenkins:lts "/usr/bin/tini -- /u…" 3 days ago Up 2 days 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp, 50000/tcp jenkins
# 이 사이에 깃 랩에 push event 발생
ubuntu@ip-172-26-1-239:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
500e0af2817b pokerface_server:4 "java -jar -Dspring.…" About a minute ago Up About a minute 0.0.0.0:8082->8082/tcp, :::8082->8082/tcp set2
de6773f73db5 jenkins/jenkins:lts "/usr/bin/tini -- /u…" 3 days ago Up 2 days 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp, 50000/tcp jenkins
ubuntu@ip-172-26-1-239:~$
deploy.sh, siwtch.sh, Dockerfile
을 사용하여 ec2로 전송된 jar파일을 Docker 컨테이너로 실행