이 문서에서는 `terraform apply`이후 할 일들에 대해서 서술한다.
먼저 AWS 콘솔에 접속 후 로그인한 뒤, Terraform으로 만들어진 인스턴스들의 IP 주소를 확인한다.
(근데 귀찮으니까 `연결`클릭해서 ` ssh -i "KDT_Project2_AWS.pem" ubuntu@52.78.100.13 `코드를 복붙 하자)
1. SSH 접속 포트 변경
user_data = <<-EOF
#!/bin/bash
...
hostnamectl set-hostname jenkins-instance
echo "127.0.1.1 jenkins-instance" >> /etc/hosts
# SSH 포트 변경
sed -i 's/#Port 22/Port 51228/' /etc/ssh/sshd_config
systemctl restart ssh
...
이런 식으로 Terraform 코드로 포트를 변경하는 코드를 짰었는데, 막상 접속하려고 보니 접속이 안 되어서(이거 왜 이런지 모르겠다...)
그냥 수동으로 작성해주기로 했다.
sudo nano /etc/ssh/sshd_config
# 포트 번호 변경 진행
sudo systemctl restart ssh
띠용; 그래도 안 되네.. 일단 이건 빼두고
애초에 특정 IP로만 접속 가능하게 해뒀으니 다음 과정으로 넘어가자.
# Java 설치
sudo apt update
sudo apt install -y openjdk-17-jdk
2. Jenkins 세팅
sudo cat /var/lib/jenkins/secrets/initialAdminPassword
- 초기 관리자 비밀번호 출력하여 초기 설정 설치 진행
- Jenkins 관리 - Tools - Gradle 설치 (ID는 임의로 'GRADLE'로 설정)
- Jenkins 관리 - Plugins - Available plugins에서 SSH Agent, Pipeline: Stage View 설치
- Jenkins 관리 - Credentials - Github 웹 훅 토큰과 AWS에서 인스턴스 생성 시 지정한 .pem 설정
- 깃허브 레포지토리 - webhook의 주소를 Jenkins 인스턴스의 Public IP로 변경
(ex: http://`52.78.100.13:8080`/github-webhook/) - 파이프라인 코드 입력
def deployApp(targetServerIp, targetServerPort) {
def jarPath = 'build/libs/inclass-spring-security-0.0.1-SNAPSHOT.jar'
def deployPath = '/home/ubuntu'
def runAppCommand = "nohup java -jar $deployPath/inclass-spring-security-0.0.1-SNAPSHOT.jar > nohup.log 2>&1 &"
def checkLogCommand = "grep -q 'Started inclass-spring-security in' $deployPath/nohup.log"
def checkProcessCommand = "pgrep -f inclass-spring-security-0.0.1-SNAPSHOT.jar"
// 서버에 파일을 SCP로 전송
sshagent(['KDT_Project2_AWS']) { // 이 부분에서 'KDT_Project2_AWS'는 Jenkins에 등록한 Credential ID (AWS에서 발급한 키 페어)
// Terminate any existing application process
sh "ssh -o StrictHostKeyChecking=no -p $targetServerPort ubuntu@$targetServerIp 'ps -ef | grep java | grep -v grep | awk \'{print \$2}\' | sudo xargs kill -9 || echo \"No process found\"'"
// Deploy the new JAR file and start the application
sh "scp -o StrictHostKeyChecking=no -P $targetServerPort $jarPath ubuntu@$targetServerIp:$deployPath/"
sh "ssh -o StrictHostKeyChecking=no -p $targetServerPort ubuntu@$targetServerIp '$runAppCommand'"
}
}
pipeline {
tools {
gradle "GRADLE" // Jenkins에서 설정한 Gradle의 이름
}
agent any
stages {
stage('Clone') {
steps { // main인지 master인지 확인하고 Pipeline Syntax 돌려서 이중으로 확인해보기
git 'https://github.com/lcl1380/KDT_Project2.git'
}
}
stage('Build') {
steps {
sh 'chmod +x ./gradlew'
sh './gradlew clean build'
}
}
stage('Test') {
steps {
script {
sh './gradlew test'
}
}
}
stage('Deploy A-Private Instance') {
steps {
script {
deployApp('10.0.2.56', 22)
}
}
}
stage('Deploy B-Private Instance') {
steps {
script {
deployApp('10.0.5.193', 22)
}
}
}
}
post {
success {
echo "This will run when the run finished successfully"
}
failure {
echo "This will run if failed"
}
}
}
3. DB 관련 세팅
Caused by: java.sql.SQLException: null, message from server: "Host '10.0.2.207' is not allowed to connect to this MySQL server"
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:130) ~[mysql-connector-j-8.3.0.jar!/:8.3.0]
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-j-8.3.0.jar!/:8.3.0]
at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:815) ~[mysql-connector-j-8.3.0.jar!/:8.3.0]
at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:438) ~[mysql-connector-j-8.3.0.jar!/:8.3.0]
at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:241) ~[mysql-connector-j-8.3.0.jar!/:8.3.0]
at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:189) ~[mysql-connector-j-8.3.0.jar!/:8.3.0]
at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:138) ~[HikariCP-5.0.1.jar!/:na]
at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:359) ~[HikariCP-5.0.1.jar!/:na]
at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:201) ~[HikariCP-5.0.1.jar!/:na]
at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:470) ~[HikariCP-5.0.1.jar!/:na]
at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:561) ~[HikariCP-5.0.1.jar!/:na]
at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:100) ~[HikariCP-5.0.1.jar!/:na]
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:112) ~[HikariCP-5.0.1.jar!/:na]
at org.springframework.jdbc.datasource.DataSourceUtils.fetchConnection(DataSourceUtils.java:160) ~[spring-jdbc-6.1.6.jar!/:6.1.6]
at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:118) ~[spring-jdbc-6.1.6.jar!/:6.1.6]
at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:81) ~[spring-jdbc-6.1.6.jar!/:6.1.6]
... 44 common frames omitted
다음과 같은 오류 메시지가 출력되었다.
해당 오류 메시지는 MySQL 서버가 `10.0.2.207` IP주소를 가진 호스트의 연결을 허용하지 않기 때문에 발생한 것이다.
주 원인은 다음과 같다.
- 사용자 권한 문제 : MySQL 서버에서 특정 사용자에게 특정 호스트에서의 접속을 허용하지 않았을 경우
- 방화벽 설정 : MySQl 서버로의 외부 접근을 방지하는 방화벽이 설정되어 있을 경우
- MySQL 설정 문제 : MySQL 서버의 설정 파일에서 외부 접속을 허용하지 않도록 설정되어 있을 경우
// 특정 IP에서의 접속을 허용 (예: '10.0.2.207'에서 접속 허용)
mysql> GRANT ALL PRIVILEGES ON *.* TO 'username'@'10.0.2.207' IDENTIFIED BY 'password';
// MySQL 8.0 이상 부터는 IDENTIFIED BY 대신 CREATE USER 명령어를 사용하여 비밀번호를 설정해야 한다
// 데이터베이스 생성
CREATE DATABASE spring_security_inclass;
// 사용자 생성
CREATE USER 'username'@'%' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'username'@'%';
FLUSH PRIVILEGES;
// 기존 사용자 변경
ALTER USER 'username'@'%' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'cherish'@'%';
FLUSH PRIVILEGES;
MySQL 서버에 접속하여 사용자에게 외부 접속 권한을 부여해주자.
이후 로컬에서 직접 Jenkins를 통하지 않고 하드 빌드 해 봤는데, 오류 로그는 위 파일에 올려두었다.
오류를 분석해보니 MySQL DB 연결과 관련된 오류를 확인할 수 있었다.
오류 분석 내용 (더보기)
로그 파일에서 주요 예외 메시지를 분석한 결과, 가장 두드러지는 문제는 MySQL 데이터베이스 연결과 관련된 오류입니다. 다음과 같은 예외가 발견되었습니다:
- java.sql.SQLNonTransientConnectionException: Public Key Retrieval is not allowed
- org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jdbcSessionDataSourceScriptDatabaseInitializer'
문제 1: Public Key Retrieval is not allowed
이 오류는 MySQL 서버에 연결할 때 "Public Key Retrieval"이 허용되지 않아서 발생하는 문제입니다. 이는 MySQL 8.0 이상 버전에서 SSL 연결 또는 RSA 기반의 암호화된 비밀번호 사용 시 발생할 수 있습니다.
해결 방법:
- JDBC URL 수정: JDBC URL에 allowPublicKeyRetrieval=true 옵션을 추가합니다. 예를 들어, application.properties 또는 application.yml 파일에서 JDBC URL을 다음과 같이 수정합니다.
`spring.datasource.url=jdbc:mysql://your-database-host:3306/your-database-name?allowPublicKeyRetrieval=true&useSSL=false` - SSL 설정: useSSL=false를 추가하여 SSL 사용을 비활성화할 수 있습니다. 보안이 중요한 환경에서는 SSL 설정을 다시 고려해야 합니다.
문제 2: Failed to determine DatabaseDriver
이 오류는 Spring Boot 애플리케이션이 데이터베이스 드라이버를 찾을 수 없어서 발생하는 문제입니다. 이는 데이터베이스 연결 설정이 올바르게 구성되지 않았기 때문에 발생할 수 있습니다.
해결 방법:
1. 데이터베이스 드라이버 의존성 확인: pom.xml 또는 build.gradle 파일에 MySQL 커넥터 의존성이 추가되어 있는지 확인합니다.
<!-- For Maven -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
// For Gradle
implementation 'mysql:mysql-connector-java:8.0.30'
2. 애플리케이션 설정 확인: application.properties 또는 application.yml 파일에서 데이터베이스 설정을 올바르게 구성합니다.
spring.datasource.url=jdbc:mysql://your-database-host:3306/your-database-name
spring.datasource.username=your-username
spring.datasource.password=your-password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
요약
- Public Key Retrieval 오류: JDBC URL에 allowPublicKeyRetrieval=true와 useSSL=false 옵션을 추가합니다.
- DatabaseDriver 문제: MySQL 커넥터 의존성이 제대로 추가되었는지 확인하고, 데이터베이스 설정을 올바르게 구성합니다.
저번에 접속할 때에는 DB생성하고 유저 생성만 하면 됐었는데, 이번에는 좀 헤매다 된 거라 뭐가 뭔지 모르겠다.
일단 한 것들 정리해보자 ...
1. DB 생성함
2. 사용자 생성함
3. 권한 부여함
4. nginx 리버스 프록시 설정
sudo nano /etc/nginx/nginx.conf
# ----------------------------------------------------------------------------
http {
upstream inclass-spring-security {
server 10.0.2.67:8080 weight=100 max_fails=3 fail_timeout=3s;
}
upstream grafana {
server 10.0.2.192:3000 weight=100 max_fails=3 fail_timeout=3s;
}
server {
listen 80;
location / {
proxy_pass http://inclass-spring-security;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /grafana/ {
proxy_pass http://grafana;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
}
# ----------------------------------------------------------------------------
sudo systemctl restart nginx
+ 24. 07. 24 : 모니터링 인스턴스로 접속하는 부분 추가.
Nginx 전체 코드 백업 (더보기)
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
upstream inclass-spring-security {
server 10.0.2.67:8080 weight=100 max_fails=3 fail_timeout=3s;
}
upstream grafana {
server 10.0.2.192:3000 weight=100 max_fails=3 fail_timeout=3s;
}
server {
listen 80;
location / {
proxy_pass http://inclass-spring-security;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /grafana/ {
proxy_pass http://grafana;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
5. sudo 권한 설정
젠킨스는 기본적으로 jenkins라는 계정으로 스크립트 명령을 내리는데, ubuntu계정의 소유 요소에는 쓰기 작업 등에 대해 허가가 나지 않는 경우가 있다.
이 때, jenkins 계정으로 sudo 명령을 비밀번호 없이 사용할 수 있도록 사전에 권한을 부여해보자.
단순 파일 전송을 진행하는 경우엔 jenkins 인스턴스에만 설정해주면 되고, 접속해서 sudo 권한이 필요한 행동을 할 경우에는 행위대상 인스턴스에도 설정해주자.
혹시 jenkins쪽에서 사용하는 계정명이 jenkins가 아닐 수도 있으니 파이프라인 실행시 `sh "whoami"`로 계정명을 꼭 확인해주자!
# Jenkins 인스턴스에서
sudo visudo
# root 권한 밑에 추가
jenkins ALL=(ALL) NOPASSWD: ALL
'🌥️Cloud Study🌥️ > AWS' 카테고리의 다른 글
[ AWS ] 파이프라인을 이용한 무중단 배포 (0) | 2024.07.23 |
---|---|
[ AWS ] 아틸러리를 이용한 부하 테스트 및 부하 분산 확인(I/O bound) (1) | 2024.07.23 |
[ AWS-Terraform ] 테라폼을 이용한 아키텍처 구축 (0) | 2024.07.18 |
[ AWS ] 배포 파이프라인 만들기 (0) | 2024.07.17 |
[ AWS ] 보안그룹-인바운드 규칙 편집, 개별 인스턴스 설정 (0) | 2024.07.16 |