프로젝트 리드미 2차 수정 #105
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: kernelsquare prod deploy | |
on: | |
pull_request: | |
types: | |
- closed | |
permissions: | |
contents: read | |
jobs: | |
if-merge: | |
if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main' | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v3 | |
# - run: touch ./domain-mysql/src/main/resources/application.yml | |
- run: echo "${{ secrets.APPLICATION }}" > ./domain-mysql/src/main/resources/application.yml | |
- uses: actions/upload-artifact@v2 | |
with: | |
name: application.yml | |
path: ./domain-mysql/src/main/resources/application.yml | |
- run: echo "${{ secrets.MEMBER_APPLICATION }}" > ./member-api/src/main/resources/application.yml | |
- uses: actions/upload-artifact@v2 | |
with: | |
name: application.yml | |
path: ./member-api/src/main/resources/application.yml | |
- name: Set up JDK 21 | |
uses: actions/setup-java@v3 | |
with: | |
java-version: '21' | |
distribution: 'temurin' | |
# - name: Set up member-api application.yml | |
# uses: microsoft/variable-substitution@v1 | |
# with: | |
# files: ./member-api/src/main/resources/application.yml | |
# env: | |
# spring.datasource.url: ${{ secrets.LOCAL_DB_URL }} | |
# spring.datasource.username: ${{ secrets.LOCAL_DB_HOST }} | |
# spring.datasource.password: ${{ secrets.LOCAL_DB_PASSWORD }} | |
# spring.redis.host: ${{ secrets.REDIS_HOST }} | |
# spring.redis.port: ${{ secrets.REDIS_PORT }} | |
# spring.data.mongodb.uri: ${{ secrets.MONGO_URI }} | |
# spring.security.jwt.secret: ${{ secrets.JWT_SECRET_KEY }} | |
# cloud.aws.s3.bucket: ${{ secrets.S3_BUCKET_NAME }} | |
# cloud.aws.region.static: ${{ secrets.S3_REGION }} | |
# cloud.aws.credentials.accessKey: ${{ secrets.AWS_ACCESS_KEY }} | |
# cloud.aws.credentials.secretKey: ${{ secrets.AWS_ACCESS_SECRET }} | |
# kafka.url: ${{ secrets.KAFKA_URL }} | |
# custom.domain.image.baseUrl: ${{ secrets.BASE_URL }} | |
# chatgpt.rest-api-key: ${{ secrets.CHAT_GPT_API_KEY }} | |
# - name: Set up domain-mysql application.yml | |
# uses: microsoft/variable-substitution@v1 | |
# with: | |
# files: ./domain-mysql/src/main/resources/application.yml | |
# env: | |
# spring.datasource.url: ${{ secrets.LOCAL_DB_URL }} | |
# spring.datasource.username: ${{ secrets.LOCAL_DB_HOST }} | |
# spring.datasource.password: ${{ secrets.LOCAL_DB_PASSWORD }} | |
- name: Set up domain-mongodb application.yml | |
uses: microsoft/variable-substitution@v1 | |
with: | |
files: ./domain-mongodb/src/main/resources/application.yml | |
env: | |
spring.data.mongodb.uri: ${{ secrets.MONGO_URI }} | |
- name: Build member-api with Gradle | |
run : | | |
cd member-api | |
chmod +x ./gradlew | |
./gradlew clean build | |
- name: Login to DockerHub | |
uses: docker/login-action@v1 | |
with: | |
username: ${{ secrets.DOCKERHUB_USERNAME }} | |
password: ${{ secrets.DOCKERHUB_TOKEN }} | |
- name: Build member-api with Docker | |
run : docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/kernelsquare ./member-api | |
- name: member-api Docker push | |
run : docker push ${{ secrets.DOCKERHUB_USERNAME }}/kernelsquare:latest | |
deploy: | |
needs: if-merge | |
runs-on: ubuntu-latest | |
steps: | |
# 깃허브 액션 러너의 아이피를 얻어온다. | |
- name: Get Github action IP | |
id: ip | |
uses: haythem/[email protected] | |
# 환경변수 설정. | |
- name: Setting environment variables | |
run: | | |
echo "AWS_DEFAULT_REGION=ap-northeast-2" >> $GITHUB_ENV | |
# AWS 설정 | |
- name: Configure AWS credentials | |
uses: aws-actions/configure-aws-credentials@v1 | |
with: | |
# 아이엠 키 설정, 인바운드 룰 수정을 위해 설정합니다. | |
# IAM 설정하실 때, AWSEC2FullAccess 권한을 부여해주세요. | |
# 더 자세한 사항은 제 블로그 이전 Github Actions 게시물을 참고해주세요. | |
aws-access-key-id: ${{ secrets.AWS_IAM_ACCESS_KEY_ID }} | |
aws-secret-access-key: ${{ secrets.AWS_IAM_SECRET_KEY }} | |
aws-region: ${{ env.AWS_DEFAULT_REGION }} | |
# 깃허브 액션의 아이피를 인바운드 룰에 임시 등록 | |
- name: Add Github Actions IP to Security group | |
run: | | |
aws ec2 authorize-security-group-ingress --group-name ${{ secrets.AWS_SG_NAME }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32 | |
aws ec2 authorize-security-group-ingress --group-name ${{ secrets.AWS_SG_NAME }} --protocol tcp --port 80 --cidr ${{ steps.ip.outputs.ipv4 }}/32 | |
aws ec2 authorize-security-group-ingress --group-name ${{ secrets.AWS_SG_NAME }} --protocol tcp --port 443 --cidr ${{ steps.ip.outputs.ipv4 }}/32 | |
aws ec2 authorize-security-group-ingress --group-name ${{ secrets.AWS_SG_NAME }} --protocol tcp --port 8501 --cidr ${{ steps.ip.outputs.ipv4 }}/32 | |
aws ec2 authorize-security-group-ingress --group-name ${{ secrets.AWS_SG_NAME }} --protocol tcp --port 8502 --cidr ${{ steps.ip.outputs.ipv4 }}/32 | |
# 블루/그린 헬스체크로 변수 초기화 | |
- name: Blue/Green health check | |
run: | | |
echo "INSTANCE_ENV=$(curl -s "https://${{ secrets.NGINX_IP }}/environment")" >> $GITHUB_ENV | |
# 헬스 체크를 통한 인스턴스 체크, 어떤 환경이 운영중인가? | |
- name: Set target ip | |
# prod1, prod2 둘 다 검사해야 하지만, 현재는 prod1만 검사 | |
# CURRENT_UPSTREAM : env로 요청 보내서 blue 인지, green인지 알아옴. 이걸 curl로 해서 변수로 만든다. 즉 blue,green,오류 중에 하나 | |
# CURRENT_IP : 만약 환경이 blue라면 blue 인스턴스의 prod1의 ip(111.111.111.111:8080)가 들어감 | |
# STOPPED_IP : 만약 환경이 blue라면 멈춰있는 green의 prod1 ip가 들어감. | |
# TARGET_UPSTREAM : 바꿀 env | |
run: | | |
CURRENT_UPSTREAM=$(curl -s "https://${{ secrets.NGINX_IP }}/environment") | |
echo $CURRENT_UPSTREAM | |
if [ $CURRENT_UPSTREAM = "blue" ]; then | |
echo "CURRENT_IP=${{ secrets.BLUE_IP }}" >> $GITHUB_ENV | |
echo "STOPPED_IP=${{ secrets.GREEN_IP }}" >> $GITHUB_ENV | |
echo "TARGET_UPSTREAM=green" >> $GITHUB_ENV | |
echo "blue" | |
elif [ $CURRENT_UPSTREAM = "green" ]; then | |
echo "CURRENT_IP=${{ secrets.GREEN_IP }}" >> $GITHUB_ENV | |
echo "STOPPED_IP=${{ secrets.BLUE_IP }}" >> $GITHUB_ENV | |
echo "TARGET_UPSTREAM=blue" >> $GITHUB_ENV | |
echo "green" | |
else | |
echo "error" | |
exit 1 | |
fi | |
# 멈춰있는 서버에 있는 도커 컴포즈 실행, 혹시 실행되고 있는 인스턴스가 있을 수 있으므로, 추후에 중단하는 작업도 추가해야함. | |
- name: Execute Server Docker compose | |
uses: appleboy/ssh-action@master | |
with: | |
username: ${{ secrets.SSH_USER }} | |
host: ${{ env.STOPPED_IP }} | |
# key: ${{ secrets.BLUE_SSH_KEY }} | |
password: ${{ secrets.SSH_PASSWORD }} | |
script_stop: true | |
script: | | |
sudo docker pull ${{ secrets.DOCKERHUB_USERNAME }}/kernelsquare | |
sudo docker-compose up -d | |
if: ${{ env.TARGET_UPSTREAM == 'blue' }} | |
- name: Execute Server Docker compose | |
uses: appleboy/ssh-action@master | |
with: | |
username: ${{ secrets.SSH_USER }} | |
host: ${{ env.STOPPED_IP }} | |
key: ${{ secrets.GREEN_SSH_KEY }} | |
script_stop: true | |
script: | | |
sudo docker pull ${{ secrets.DOCKERHUB_USERNAME }}/kernelsquare | |
sudo docker-compose up -d | |
if: ${{ env.TARGET_UPSTREAM == 'green' }} | |
- name: Check the deployed prod1 service URL | |
uses: jtalk/url-health-check-action@v3 | |
with: | |
url: http://${{ env.STOPPED_IP }}:8501/environment | |
# 총 5번 하는데, 30초의 간격을 두고함. 이때까지 응답이 정상이 아니라면 배포 실패 | |
max-attempts: 5 # Optional, defaults to 1 | |
retry-delay: 30s # Optional, only applicable to max-attempts > 1 | |
- name: Check the deployed prod2 service URL | |
uses: jtalk/url-health-check-action@v3 | |
with: | |
url: http://${{ env.STOPPED_IP }}:8502/environment | |
# 총 5번 하는데, 30초의 간격을 두고함. 이때까지 응답이 정상이 아니라면 배포 실패 | |
max-attempts: 5 # Optional, defaults to 1 | |
retry-delay: 30s # Optional, only applicable to max-attempts > 1 | |
# 엔진엑스의 프록시 변경 | |
- name: Change nginx upstream | |
uses: appleboy/ssh-action@master | |
with: | |
username: ${{ secrets.SSH_USER }} | |
host: ${{ secrets.NGINX_HOST }} | |
# key: ${{ secrets.NGINX_SSH_KEY }} | |
password: ${{ secrets.SSH_PASSWORD }} | |
script_stop: true | |
# 도커로 들어가서 service_env를 바꿔주고 reload | |
# 여기서 -i가 아닌 -it로 진행하면 오류가 발생하고, -c가 없으면 도커가 아닌 호스트에서 경로를 찾는다. 주의 | |
script: | | |
sudo docker exec -i webserver bash -c 'echo "set \$service_env ${{ env.TARGET_UPSTREAM }};" > /etc/nginx/conf.d/service-env.inc && nginx -s reload' | |
# 기존 인스턴스 중단 | |
- name: Terminate prev instance | |
uses: appleboy/ssh-action@master | |
with: | |
username: ${{ secrets.SSH_USER }} | |
host: ${{ env.CURRENT_IP }} | |
# key: ${{ secrets.BLUE_SSH_KEY }} | |
password: ${{ secrets.SSH_PASSWORD }} | |
script_stop: true | |
script: | | |
sudo docker stop prod1 | |
sudo docker stop prod2 | |
sudo docker rm prod1 | |
sudo docker rm prod2 | |
if: ${{ env.TARGET_UPSTREAM == 'green' }} | |
# 기존 인스턴스 중단 | |
- name: Terminate prev instance | |
uses: appleboy/ssh-action@master | |
with: | |
username: ${{ secrets.SSH_USER }} | |
host: ${{ env.CURRENT_IP }} | |
key: ${{ secrets.GREEN_SSH_KEY }} | |
script_stop: true | |
script: | | |
sudo docker stop prod1 | |
sudo docker stop prod2 | |
sudo docker rm prod1 | |
sudo docker rm prod2 | |
if: ${{ env.TARGET_UPSTREAM == 'blue' }} | |
# 깃허브 러너 아이피를 인바운드 룰에서 제거 | |
- name: Remove Github Actions IP from security group | |
# if: always()를 해놓으면 무조건 실행됨. 따라서 위에서 deploy가 실패해도 인바운드 룰로 열어놨던 ip를 모두 닫음. | |
if: always() | |
run: | | |
aws ec2 revoke-security-group-ingress --group-name ${{ secrets.AWS_SG_NAME }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32 | |
aws ec2 revoke-security-group-ingress --group-name ${{ secrets.AWS_SG_NAME }} --protocol tcp --port 80 --cidr ${{ steps.ip.outputs.ipv4 }}/32 | |
aws ec2 revoke-security-group-ingress --group-name ${{ secrets.AWS_SG_NAME }} --protocol tcp --port 443 --cidr ${{ steps.ip.outputs.ipv4 }}/32 | |
aws ec2 revoke-security-group-ingress --group-name ${{ secrets.AWS_SG_NAME }} --protocol tcp --port 8501 --cidr ${{ steps.ip.outputs.ipv4 }}/32 | |
aws ec2 revoke-security-group-ingress --group-name ${{ secrets.AWS_SG_NAME }} --protocol tcp --port 8502 --cidr ${{ steps.ip.outputs.ipv4 }}/32 |