AWS

🚴🏽 Github Action, 왜 빌드는 성공했는데 API는 작동을 안할까?

배포용 shell 스크립트까지 작성하고 나서 보니 빌드가 성공했다고 나왔는데 API 확인을 해보니 동작하지 않았습니다. ps -ef | grep java로 확인해보니 .jar 파일이 구동되고 있지 않다는 것을 알았습니다.
정확한 내용을 확인하기 위해 CodeDeploy 이벤트 로그를 확인했습니다.

CodeDeploy agent was not able to receive the lifecycle event. Check the CodeDeploy agent logs on your host and make sure the agent is running and can connect to the CodeDeploy server.

일단 S3 버킷에는 객체가 zip 파일 형태로 잘 만들어져 있었습니다. 그리고 CodeDeploy agent 상태(sudo service codedeploy-agent status)는 이상이 없었습니다.
그렇다면, CodeDeploy에서 배포에 문제가 있는 것 같아 agent log과 shell 스크립트를 확인했습니다.

2023-08-08T03:17:15 INFO [codedeploy-agent(24653)]: On Premises config file does not exist or not readable

직접 해결하기 위해서 CLI로 Github action에 등록한 Code Deploy를 진행해봤습니다. (run 내용에 변수 대입해서 진행)

- name: Code Deploy
  run: aws deploy create-deployment --application-name $CODE_DEPLOY_APP_NAME --deployment-config-name CodeDeployDefault.OneAtATime --deployment-group-name $DEPLOYMENT_GROUP_NAME --s3-location bucket=$BUCKET_NAME,bundleType=zip,key=$PROJECT_NAME/$GITHUB_SHA.zip

aws configure에 리전을 설정해야 해서 설정했습니다. 이 부분은 Github action .yml 조건과의 일치여부 때문에 발생하는 문제입니다.

You must specify a region. You can also configure your region by running "aws configure".
ubuntu@ip-172-31-34-122:~$ aws configure
AWS Access Key ID [****************XGTY]: 
AWS Secret Access Key [****************LL3f]: 
Default region name [None]: ap-northeast-2
- name: Configure AWS credentials
  uses: aws-actions/configure-aws-credentials@v1
  with:
    aws-access-key-id: $
    aws-secret-access-key: $
    aws-region: ap-northeast-2

이번에는 다른 배포 실패 이벤트 로그가 발생했습니다.

The overall deployment failed because too many individual instances failed deployment, too few healthy instances are available for deployment, or some instances in your deployment group are experiencing problems.
The specified key does not exist.

이벤트 로그 상세를 보니 S3 .zip 파일의 키값이 달라 인식하지 못하는 문제인 것 같아 전역변수로 GITHUB_SHA를 등록해주었습니다. CodeDeploy에서 DownloadBundle 상태가 진행이 되는 것을 볼 수 있었습니다.

env:
  GITHUB_SHA: $



AWS 다양한 배포 방법들

1. Github Action을 사용하는 무중단 배포

수동 배포 말고 자동 빌드를 위한 Github Action과 CI, CD가 가능하도록 배포를 진행했습니다.
CI & CD란?
CI/CD는 개발자들이 애플리케이션을 더욱 빠르게 개발하고 배포할 수 있도록 지원하는 방식입니다. CI는 코드 변경 사항을 지속적으로 통합하고 빌드하며, CD는 지속적으로 배포합니다. 이를 통해 개발자는 더욱 빠른 피드백을 받고, 애플리케이션의 품질을 유지 및 개선할 수 있습니다.


Group 50.png


Github Action 설정
자동 빌드를 위한 Github Action을 CI/CD 툴로 설정했습니다. 이미 버전관리가 Github으로 되어 있고, Travis는 일부 유료버전으로 변경되면서 접근이 안되는 부분이 있었기 때문입니다.
Actions 탭에서 구동을 위한 .yml을 작성하면 .github/workflows 위치에 파일이 생성됩니다. 그리고 빌드가 성공적으로 되는지까지 확인합니다.
현재는 --exclude-task test로 테스트 실패시에도 빌드가 가능하게 해놓은 상태입니다.

name: BoardApi CI/CD 

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Set up JDK 17
      uses: actions/setup-java@v3
      with:
        java-version: '17'
        distribution: 'temurin'
    - name: Grant execute permission for gradlew
      run: chmod +x gradlew
    - name: Build with Gradle
      run : ./gradlew clean build --exclude-task test

그리고 AWS 사용을 위해 AWS ACCESS KEY ID, SECRET KEY를 해당 레포 설정 Actions secrets and variables에 등록합니다.

Jenkins, Travis와의 차이점

  • GitHub Actions는 GitHub와 함께 제공되는 내장형 CI/CD 도구입니다. 설정과 사용이 쉽고, 다양한 GitHub 기능과 통합이 가능합니다. 작은 규모에서 중간 규모의 GitHub 프로젝트에 가장 적합합니다.
  • Jenkins는 많은 해가 지난 인기 있는 오픈소스 CI/CD 도구입니다. 매우 유연하며 다양한 프로그래밍 언어와 도구와 함께 사용할 수 있습니다. 많은 맞춤 설정이 필요한 대규모, 복잡한 프로젝트에 가장 적합합니다.
  • Travis CI는 GitHub 프로젝트에 특화된 클라우드 기반 CI/CD 도구입니다. 설정과 사용이 쉽고, 다양한 GitHub 기능과 통합이 가능합니다. 작은 규모에서 중간 규모의 GitHub 프로젝트에 가장 적합합니다.


S3 생성하기
필요한 리전을 선택하고 버전 관리는 필요하지 않기 때문에 사용하지 않았습니다. 그리고 접근 가능한 IAM 사용자를 사용하기 때문에 모든 퍼블릭 액세스 차단을 선택해줍니다.


EC2 환경설정
EC2 기본 환경설정을 진행합니다. 제가 만든 인스턴스의 사양은 Ubuntu 22.04 LTS, t2.micro(프리티어), 30GiB 스토리지로 설정했습니다.
보안에는 S3, Codedeploy에 full access가 가능하도록 만든 IAM 역할을 부여합니다.
EC2 콘솔에서 JDK와 Amazon correto를 프로젝트 JDK와 일치하는 것으로 설치합니다.

$ sudo apt update
$ sudo apt-get install openjdk-17-jdk
$ sudo wget https://corretto.aws/downloads/latest/amazon-corretto-17-x64-linux-jdk.tar.gz

/etc/profile 가장 마지막 줄에 환경변수를 설정합니다.

$ sudo nano /etc/profile
$ export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
$ export PATH=$JAVA_HOME/bin/:$PATH
$ export CLASS_PATH=$JAVA_HOME/lib:$CLASS_PATH
$ sudo reboot now

Ubuntu Server용 CodeDeploy 에이전트 설치 가이드를 보고 에이전트를 설치합니다. status가 active라면 성공!

$ sudo apt install ruby
$ cd /home/ubuntu
$ wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
$ chmod +x ./install
$ sudo ./install auto
$ sudo service codedeploy-agent status

Github Action 설정변경
앞에서 만들어준 .yml 파일에 envjobs 내역을 수정합니다. push 후 진행되는 순서는 다음과 같습니다.

  1. Set up JDK 17
  2. Grant execute permission for gradlew
  3. Build with Gradle
  4. Make Zip File
  5. Configure AWS credentials
  6. Upload to S3
  7. Code Deploy 로직의 각 부분은 cicd 오픈소스를 참고해서 만들었습니다.


appspec.yml 파일 작성
CodeDeploy 동작을 위해 appspec.yml 파일 작성합니다. CodeDeploy agent가 다운로드 코드를 어디 경로에서 다운받을지, EC2 서버 어디에 코드를 저장할지 등을 정합니다.
afterinstall는 배포 스크립트에서 설치 프로세스가 완료된 후 실행되어야 하는 명령 또는 스크립트 목록을 포함하는 섹션입니다. 예시에서 afterinstall 섹션에는 AfterInstall이라는 이름의 훅이 포함되어 있습니다. 이 훅은 scripts/deploy.sh 쉘 스크립트의 위치를 지정하며, 60초의 타임아웃으로 실행되며 ubuntu 사용자로 실행됩니다.

version: 0.0
os: ubuntu

files:
  - source: /
    destination: /home/ubuntu/boardApi
permissions:
  - object: /home/ubuntu/boardApi/
    owner: ubuntu
    group: ubuntu
hooks:
  AfterInstall:
    - location: scripts/deploy.sh
      timeout: 60
      runas: ubuntu


배포용 shell 스크립트
빌드된 jar 파일을 실행시키고 이미 실행되어 있다면 새로 빌드된 jar파일로 실행되도록 설정해줍니다.

#!/usr/bin/env bash

REPOSITORY=/home/ubuntu/boardApi
cd $REPOSITORY

APP_NAME=boardApi
JAR_NAME=$(ls $REPOSITORY/build/libs/ | grep 'SNAPSHOT.jar' | tail -n 1)
JAR_PATH=$REPOSITORY/build/libs/$JAR_NAME

CURRENT_PID=$(pgrep -f $APP_NAME)

if [ -z $CURRENT_PID ]
then
  echo "> 종료할것 없음."
else
  echo "> kill -9 $CURRENT_PID"
  kill -15 $CURRENT_PID
  sleep 5
fi

echo "> $JAR_PATH 배포"
nohup java -jar $JAR_PATH > /dev/null 2> /dev/null < /dev/null &

nohup java -jar $JAR_PATH > /dev/null 2> /dev/null < /dev/null & 부분은 무중단으로 실행하고자 할 때 nohup을 적용하는 부분이라 git clone으로 .jar 파일 배포 방식(이하 포스팅 됨)과 유사한 것을 볼 수 있었습니다.


2. Git clone을 이용한 수동배포

Github에서 직접 소스를 내려받아 EC2에서 실행하는 방법입니다. 별도 프로그램을 사용하지 않고 AWS 인스턴스로 쉽게 연동할 수 있다는 장점이 있습니다.
EC2 인스턴스에서 소스코드가 있는 Github과 연결하기 위해서는 ssh 키를 만들어 퍼블릭 키를 github에 등록해줘야 합니다.

ssh-keygen -t rsa -C github계정 메일(example@github.com)

다음 명령어를 통해 얻은 키를 Github 설정의 SSH and GPG keys에 등록해줍니다.

cat /home/ubuntu/.ssh/id_rsa

이제 레포의 SSH 주소를 복사해서 clone을 진행합니다.

git clone {SSH 주소}(git@github.com:프로젝트명.git)

소스 파일이 받아지면 프로젝트 안에서 gradlew build를 진행할 수 있습니다.

cd 프로젝트/build/libs
chmod +x gradlew

nohup으로 무중단 배포를 진행합니다. 이 때 배포과정에 문제가 있다면 .jar 파일과 같은 디렉토리에서 nohup.out 파일에서 에러를 확인할 수 있습니다.



이 경우 프리티어에서 메모리 용량 부족으로 gradlew build하면 무한로딩에 빠지는 이슈가 발생했습니다. Swap 메모리 사용해서 해결했지만 소스코드 변경시마다 수동 배포를 해야 하기 때문에 번거로운 방법이었습니다.

Swap 메모리란?
RAM의 메모리가 부족하므로, 리눅스의 HDD 공간을 RAM처럼 사용하는 것을 말하면, 이를 통해 부족한 RAM을 증설한 것처럼 사용할 수 있습니다.


3. Filezila를 이용한 war 파일 수동배포

Filezila의 설정 SFTP에서 private key를 설정해서 배포서버에 접근할 수 있습니다. 아래 포스팅한 tomcat 이하 webapps에 war 파일을 이동시키면, 자동으로 war 파일명의 디렉토리 파일이 생성됩니다.

777은 소유자, 그룹, 그리고 다른 사용자 모두가 읽기, 쓰기, 실행 권한을 가진다는 것을 의미합니다. 따라서 이런 설정은 보안상 취약합니다. 이하 tomcat10 세팅과정에서 톰캣 사용자에게 권한을 주었지만 파일질라에서 파일 전송시에는 권한을 더 넓게 주는 것이 필요해서 777 임시 설정을 두었습니다.

sudo chmod -R 777 /opt/tomcat/apache-tomcat-10.1.11/webapps

디렉토리와 그 하위 디렉토리, 파일의 모든 소유자를 tomcat 사용자로, 그룹을 tomcat 그룹으로 변경합니다. 이렇게 함으로써 해당 디렉토리와 파일들은 tomcat 사용자와 그룹에 속하게 됩니다.

sudo chown -R tomcat:tomcat /opt/tomcat/apache-tomcat-10.1.11/webapps



AWS EC2 Ubuntu22.04LTS tomcat10 세팅하기

들어가며
사전미션을 하게 되어 오랫만에 EC2 환경 구축을 하게 되었는데 버전 변경으로 인한 차이가 발생해서 이번에 알게 된 내용을 정리했습니다.
아래 버전을 사용하는 경우, 이전 제 블로그에 Ubuntu 20.x tomcat9 구축 내용을 참고해주세요.


설치과정
톰캣 구동전에 필요한 JDK 파일을 설치해 주어야 합니다. 참고로 저는 이번 프로젝트에서 JDK 17을 사용했습니다.
“tomcat”이라는 이름의 사용자를 /opt/tomcat 경로에 홈 디렉토리를 생성하며, 로그인 쉘이 비활성화된 상태로 추가합니다. 이러한 설정은 보안 상의 이유로 “tomcat” 사용자가 일반적인 시스템 로그인을 하지 못하게 하면서 특정 작업에만 제한된 권한으로 사용할 수 있도록 하는 데 사용됩니다. 일반적으로 Tomcat과 같은 웹 서버 또는 어플리케이션 서버의 실행을 위해 특정 사용자와 그룹을 생성하는 데 자주 사용됩니다.

sudo useradd -m -U -d /opt/tomcat -s /bin/false tomcat

원하는 아파치 톰캣 버전을 확인하고 설치합니다.

VERSION=10.1.11
wget https://www-eu.apache.org/dist/tomcat/tomcat-10/v${VERSION}/bin/apache-tomcat-${VERSION}.tar.gz -P /tmp
sudo tar -xf /tmp/apache-tomcat-${VERSION}.tar.gz -C /opt/tomcat/

/opt/tomcat 디렉토리가 없는 경우는 미리 생성하고 압축을 풀어줍니다.

sudo mkdir -p /opt/tomcat

/opt/tomcat/latest 경로에 apache-tomcat-${VERSION}라는 심볼릭 링크를 생성합니다. 이렇게 심볼릭 링크를 사용하면 Tomcat이나 다른 프로그램의 버전을 업데이트할 때 새로운 버전을 /opt/tomcat/latest에 링크로 연결하여 기존에 사용 중이던 스크립트나 설정 등이 새로운 버전을 가리키도록 할 수 있습니다.

sudo ln -s /opt/tomcat/apache-tomcat-${VERSION} /opt/tomcat/latest

해당 경로의 접근, 파일 수정 등을 용이하게 하기 위해 소유권과 권한을 설정합니다.

sudo chown -R tomcat: /opt/tomcat
sudo sh -c 'chomd +x /opt/tomcat/latest/bin/*.sh' 

tomcat.service 파일을 생성해 시스템이 부팅될 때 자동으로 시작되고 관리할 수 있도록 데몬으로 실행하게 설정합니다.

sudo nano /etc/systemd/system/tomcat.service

설정 파일 안에 내용은 다음과 같습니다.

[Unit]
Description=Apache Tomcat Web Application Container
After=network.target

[Service]
Type=forking
User=tomcat
Group=tomcat
Environment="JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64"
Environment="JAVA_OPTS=-Djava.security.egd=file:///dev/urandom -Djava.awt.he>

Environment="CATALINA_BASE=/opt/tomcat/latest"
Environment="CATALINA_HOME=/opt/tomcat/latest"
Environment="CATALINA_PID=/opt/tomcat/latest/temp/tomcat.pid"
Environment="CATALINA_OPTS="-Xms512M -Xmx1024M -server -XX:+UserParallelGC"

ExecStart=/opt/tomcat/latest/bin/startup.sh
ExecStop=/opt/tomcat/latest/bin/shutdown.sh

[Install]
WantedBy=multi-user.target

service 파일을 추가했으므로 daemon-reload해 변경 내용을 감지하도록 합니다.

sudo systemctl daemon-reload

이제 systemctl 명령어로 톰캣을 구동합니다. status로 tomcat의 상태를 확인할 수 있습니다.

sudo systemctl enable --now tomcat
sudo systemctl status tomcat

20230805_213359.png

tomcat이 제대로 구동하지 않는 경우에는 log 디렉토리에 catalina.out을 확인해 설정사항에 문제가 있는지 확인할 수 있습니다. 저의 경우에는 tomcat.service 상에 입력사항이 잘못되어 톰캣이 제대로 동작하지 않았었습니다.

sudo vi /opt/tomcat/latest/log/catalina.out


톰캣 구동장면
완료하면 다음과 같이 퍼블릭IP 주소:8080으로 제대로 구동되는 모습을 확인할 수 있습니다.

20230805_211921.png



AWS 서비스 분류

컴퓨팅 서비스

  • EC2, Auto Scaling, Lambda, Lightsail, WorkSpaces, ECS, EB

네트워킹 서비스

  • VPC, Route53, Direct Connect, ELB, CloudFront, Transit Gateway

스토리지 서비스

  • S3, EBS, Glacier, Storage Gateway, Snowball

데이터베이스 서비스

  • RDS, DynamoDB, ElastiCache, DocumentDB

관리 툴

  • CloudWatch, CloudFormation

분석 플랫폼

  • Kinesis, Redshift, EMR

애플리케이션 서비스

  • CloudSearch, SES, Elastic Transcoder



AWS Lambda

AWS Lambda를 사용하면 서버를 프로비저닝하거나 관리할 필요 없이 코드를 실행할 수 있는 서버리스 컴퓨팅 시스템입니다.
AWS Lambda는 코틀린도 지원하는가?
지원으로 명시하지 않지만 사용할 수 있습니다. AWS Lambda는 기본적으로 Java, Go, PowerShell, Node.js, C#, Python 및 Ruby 코드를 지원한다고 명시되어 있습니다. Lambda는 kotlinx.serialization 라이브러리를 지원하지 않기 때문에 Kotlin 데이터 클래스로 JSON 객체의 직렬화하는 것을 지원하지 않습니다. 하지만, Java 라이브러리인 Jackson, Gson과 같은 라이브러리는 제공하기 때문에 이 라이브러리를 사용해서 역직렬화를 진행할 수 있습니다.

import kotlinx.serialization.*
import kotlinx.serialization.json.*

@Serializable
data class Person(val name: String, val age: Int)

fun main() {
    val person = Person("Alice", 30)
    val jsonString = Json.encodeToString(person)
    println(jsonString) // 출력: {"name":"Alice","age":30}

    val json = Json { ignoreUnknownKeys = true }
    val personObj = json.decodeFromString<Person>(jsonString)
    println(personObj) // 출력: Person(name=Alice, age=30)
}

AWS Lambda FAQ
Kotlin and Groovy JVM Languages with AWS Lambda

results matching ""

    No results matching ""