Backend
home
🕛

2024. 9. 30 (Github Action)

생성일
2025/01/24 05:52
태그

Github Action 실습 (복습)

구조도
Github Actions
Github에서 제공하는 CI/CD 도구
Repository에서 어떤 이벤트가 발생하면, 특정 작업이 수행되도록 함
Github Actions의 구성
Workflow(워크플로)
Github Actions의 상위 개념
YAML 파일로 작성 (Repository > .github/workflows)
Event(이벤트)
Workflow를 실행하게 하는 규칙이나 조건 (Trigger)
on 속성을 통해 정의
Job(잡)
독립된 가상머신 or 컨테이너에서 동작하는 하나의 처리 단위
Job들은 병렬적으로 실행됨
Job은 여러 개의 Step으로 구성
Step(스텝)
여러 단계를 순차적으로 실행하는 작업 순서
직접 작성하거나 github에서 가져와 사용도 가능
Github Action work-flow 예제
# WORKFLOW의 이름 지정 name: my-first-workflow # Event 지정 on: push # JOB 정의 jobs: # JOB 이름 지정 my-first-job: # RUNNER 정의 runs-on: ubuntu-latest #STEP 정의 steps: # uses : ACTION을 사용 # GIT에 있는 코드 받아오는 ACTION - name: my-git-step uses: actions/checkout@v3 # RUN : 커맨드 명령어 실행 - name: my-cmd-step run: ls - name: my-cmd-step run: echo "Hello World"
Shell
복사
AWS 설정
EC2 접속 시 참고사항
Docker
sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc # Add the repository to Apt sources: echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin ubuntu@ip-172-31-34-164:~$ sudo usermod -aG docker $USER ubuntu@ip-172-31-34-164:~$ newgrp docker ubuntu@ip-172-31-34-164:~$ docker info
Shell
복사
Github action 정리
name: FRONT-WORKFLOW on: push: branches: [ "main" ] jobs: front-job: runs-on: ubuntu-latest steps: - name: SSH uses: appleboy/ssh-action@master with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} key: ${{ secrets.KEY }} script: | # 오류 발생 시 스크립트 중단 set -e # SSH 접속 후 작업 디렉토리로 이동 echo "SSH 접속" cd 2024-aws-kostagram/front # 환경 설정 파일 생성 echo "환경 설정 파일 생성" echo -e "REACT_APP_REST_SERVER=${{ secrets.REACT_APP_REST_SERVER }}" >> .env echo -e "REACT_APP_SERVER=${{ secrets.REACT_APP_SERVER }}" >> .env echo -e "REACT_APP_GOOGLE_ID=${{ secrets.REACT_APP_GOOGLE_ID }}" >> .env echo -e "REACT_APP_GOOGLE_REDIRECT_URI=${{ secrets.REACT_APP_GOOGLE_REDIRECT_URI }}" >> .env echo -e "REACT_APP_KAKAO_ID=${{ secrets.REACT_APP_KAKAO_ID }}" >> .env echo -e "REACT_APP_KAKAO_REDIRECT_URI=${{ secrets.REACT_APP_KAKAO_REDIRECT_URI }}" >> .env # GIT 최신 코드 가져오기 echo "GIT PULL 작업" git_output=$(sudo git pull origin main 2>&1) echo "$git_output" # Docker 빌드 echo "Docker build" docker compose up -d --build front-image
Shell
복사

Organization 생성

실습

repository 생성

환경 설정 파일 작성 (Github Action)

docker 주소: https://docs.docker.com/engine/install/ubuntu/ HOST=3.35.219.146 USERNAME=ubuntu KEY=my-blog JWT_KEY=mysecrethmsmysecrethmsmysecrethmsmysecret REST_SERVER="https://dodream.store/api" GOOGLE_ID=530115480345-84amh51vqf0h8iq9h06o9qu3g8e0s9np.apps.googleusercontent.com GOOGLE_KEY=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 GOOGLE_REDIRECT_URI=https://dodream.store/oauth/google KAKAO_ID=a0af8d5965cdea46c2506401963ea186 KAKAO_KEY=SJNpFkzY7dXn7v7D6MrrMmXytWZUPpjn KAKAO_REDIRECT_URI=https://dodream.store/oauth/kakao MYSQL_USER=dodream MYSQL_PASSWORD=1234 MYSQL_DATABASE=rest_blog-db TZ=Asia/Seoul SPRING_DATASOURCE_URL=jdbc:mysql://mysql-container:3306/rest_blog-db SPRING_DATASOURCE_USERNAME=root
YAML
복사

EC2 인스턴스 생성 + 시작

네트워크 설정
스토리지 구성
sudo 없이 명령어 실행
sudo usermod -aG docker $USER newgrp docker docker info
Shell
복사
git clone
ubuntu@ip-172-31-2-73:~$ git clone https://github.com/2024-kosta-inst/front-hms.git Cloning into 'front-hms'... remote: Enumerating objects: 73, done. remote: Counting objects: 100% (73/73), done. remote: Compressing objects: 100% (63/63), done. remote: Total 73 (delta 7), reused 73 (delta 7), pack-reused 0 (from 0) Receiving objects: 100% (73/73), 236.39 KiB | 14.77 MiB/s, done. Resolving deltas: 100% (7/7), done.
Shell
복사
nginx.conf
server { listen 80; server_name localhost; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html } }
Shell
복사
Dockerfile
FROM node:20 AS build WORKDIR /app COPY package.json yarn.lock ./ RUN yarn install COPY . . FROM nginx:alpine COPY nginx.conf /etc/nginx/conf.d/default.conf RUN rm -rf /usr/share/nginx/html/* COPY --from=build /app/build/ /usr/share/nginx/html EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]
Shell
복사
name: FRONT-WORKFLOW on: push: branches: [ "main" ] jobs: front-job: runs-on: ubuntu-latest steps: - name: SSH uses: appleboy/ssh-action@master with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} key: $ {{ secrets.KEY }} script: | set -e cd front-hms echo -e "REACT_APP_REST_SERVER=${{ secrets.REST_SERVER }}" >> .env echo -e "REACT_APP_GOOGLE_ID=${{ secrets.GOOGLE_ID }}" >> .env echo -e "REACT_APP_GOOGLE_REDIRECT_URI={{ secrets.GOOGLE_REDIRECT_URI }}" >> .env echo -e "REACT_APP_KAKAO_ID={{ secrets.KAKAO_ID }}" >> .env echo -e "REACT_APP_KAKAO_REDIRECT_URI={{ secrets.KAKAO_REDIRECT_URI }}" >> .env git_output=$(sudo git pull origin main 2>&1) docker compose up -d --build
Shell
복사

Route53

호스팅 영역 > 호스팅 영역 생성
name: dodream.store
가비아 > 네임서버 설정
ns-727.awsdns-26.net ns-1292.awsdns-33.org ns-382.awsdns-47.com ns-1848.awsdns-39.co.uk
Shell
복사
가비아 인증 진행
레코드 생성

Backend 배포 세팅

Back 코드 수정
package com.kosta.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @EnableWebMvc public class WebMvcConfig implements WebMvcConfigurer { // application.yml 파일의 location 정보 가져오기 @Value("${spring.upload.location}") private String uploadPath; @Override public void addCorsMappings(CorsRegistry registry) { registry .addMapping("/**") .allowedOrigins( "http://3.35.219.146", "http://dodream.store") .allowedMethods("OPTIONS", "GET", "POST", "PUT", "PATCH", "DELETE"); } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry .addResourceHandler("/api/img/**") .addResourceLocations("file:"+ uploadPath + "\\"); } }
Shell
복사
package com.kosta.config; import com.kosta.repository.UserRepository; import com.kosta.security.*; import com.kosta.util.TokenUtils; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import java.util.Collections; import java.util.List; @Configuration @EnableWebSecurity @RequiredArgsConstructor public class WebSecurityConfig { private final UserDetailsService userDetailsService; private final UserRepository userRepository; private final JwtProperties jwtProperties; // JWT Provider private JwtProvider jwtProvider() { return new JwtProvider(jwtProperties, userDetailsService); } private TokenUtils tokenUtils() { return new TokenUtils(jwtProvider()); } private JwtAuthenticationService jwtAuthenticationService() { return new JwtAuthenticationService(tokenUtils(), userRepository); } @Bean // 인증 관리자 (AuthenticationManager) 설정 AuthenticationManager authenticationManager() { DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); authProvider.setUserDetailsService(userDetailsService); authProvider.setPasswordEncoder(bCryptPasswordEncoder()); return new ProviderManager(authProvider); } // 암호화 빈 @Bean BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } // HTTP 요청에 따른 보안 구성 @Bean SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // 경로 권한 설정 http.authorizeHttpRequests(auth -> // 특정 URL 경로에 대해서는 인증 없이 접근 가능 auth.requestMatchers( new AntPathRequestMatcher("/api/oauth/**"), // oAuth 처리 (20240919) new AntPathRequestMatcher("/api/auth/signup"), // 회원가입 new AntPathRequestMatcher("/api/auth/duplicate"), // 이메일 중복체크 new AntPathRequestMatcher("/api/img/**"), // 이미지 new AntPathRequestMatcher("/api/auth/refresh-token"), // 토큰 재발급 new AntPathRequestMatcher("/api/post/**", "GET") ).permitAll() // AuthController 중 나머지들은 "ADMIN"만 가능 .requestMatchers( new AntPathRequestMatcher("/api/auth/") // "ADMIN"만 가능 ).hasRole("ADMIN") // 그 밖의 다른 요청들은 인증을 통과한(로그인한) 사용자라면 모두 접근할 수 있도록 한다. .anyRequest().authenticated() ); // 무상태성 세션 관리 http.sessionManagement((sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))); // 특정 경로(로그인)에 대한 필터 추가 http.addFilterBefore(new LoginCustomAuthenticationFilter(authenticationManager(), jwtAuthenticationService()), UsernamePasswordAuthenticationFilter.class); // (토큰을 통해 검증할 수 있도록) 필터 추가 http.addFilterBefore(new JwtAuthenticationFilter(jwtProvider()), UsernamePasswordAuthenticationFilter.class); // HTTP 기본 설정 http.httpBasic(HttpBasicConfigurer::disable); // CSRF 비활성화 http.csrf(AbstractHttpConfigurer::disable); // CORS 비활성화 (나중에 변경) // http.cors(AbstractHttpConfigurer::disable); // CORS 설정 http.cors(corsConfig -> corsConfig.configurationSource(corsConfigurationSource())); return http.getOrBuild(); } @Bean CorsConfigurationSource corsConfigurationSource() { return request -> { CorsConfiguration config = new CorsConfiguration(); config.setAllowedHeaders(Collections.singletonList("*")); config.setAllowedMethods(Collections.singletonList("*")); config.setAllowedOriginPatterns(Collections.singletonList("http://192.168.233.128")); config.setAllowedOrigins(List.of("http://3.35.219.146", "http://dodream.store")); config.setAllowCredentials(true); return config; }; } }
Shell
복사
Dockerfile
FROM gradle:8-jdk-17-alpine As build WORKDIR /app COPY build.gradle settings.gradle gradlew ./ COPY gradle ./gradle COPY . . RUN chmod +x gradlew RUN ./gradlew clean build -x test FROM openjdk:17-jdk-alpine WORKDIR /app COPY --from=build /app/build/libs/*.jar /app/app.jar EXPOSE 8080 ENTRYPOINT [ "java", "-jar", "/app/app.jar" ]
Docker
복사
build.gradle
.. 중략 .. // executable jar 파일만 생성 jar { enabled = false } ...
Docker
복사
docker-compose.yml
version: '3' services: mysql: image: mysql:8 container_name: mysql-container environment: - MYSQL_ROOT_PASSWORD=${MYSQL_PASSWORD} - MYSQL_USER=${MYSQL_USER} - MYSQL_PASSWORD=${MYSQL_PASSWORD} - MYSQL_DATABASE=${MYSQL_DATABASE} - TZ=${TZ} ports: - "3306:3306" networks: - server-net volumes: - mysql-data:/var/lib/mysql back: build: context: . container_name: back-container ports: - "8080:8080" environment: - SPRING_DATASOURCE_URL=${SPRING_DATASOURCE_URL} - SPRING_DATASOURCE_USERNAME=${MYSQL_USER} - SPRING_DATASOURCE_PASSWORD=${MYSQL_PASSWORD} networks: - server-net depends_on: - mysql restart: always networks: server-net: driver: bridge volumes: mysql-data:
Docker
복사
인증
sudo apt-get install -y certbot python3-certbot-nginx' ubuntu@ip-172-31-2-73:~$ sudo certbot --nginx -d dodream.store -d www.dodream.store ubuntu@ip-172-31-2-73:~$ sudo certbot --nginx -d dodream.store -d www.dodream.store Saving debug log to /var/log/letsencrypt/letsencrypt.log Enter email address (used for urgent renewal and security notices) (Enter 'c' to cancel): codesche@gmail.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Please read the Terms of Service at https://letsencrypt.org/documents/LE-SA-v1.4-April-3-2024.pdf. You must agree in order to register with the ACME server. Do you agree? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (Y)es/(N)o: y Would you be willing, once your first certificate is successfully issued, to share your email address with the Electronic Frontier Foundation, a founding partner of the Let's Encrypt project and the non-profit organization that develops Certbot? We'd like to send you email about our work encrypting the web, EFF news, campaigns, and ways to support digital freedom. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (Y)es/(N)o: y
Shell
복사
절차 성공 후
ubuntu@ip-172-31-2-73:/etc/letsencrypt$ sudo certbot --nginx -d dodream.store -d www.dodream.store Saving debug log to /var/log/letsencrypt/letsencrypt.log Requesting a certificate for dodream.store and www.dodream.store Successfully received certificate. Certificate is saved at: /etc/letsencrypt/live/dodream.store/fullchain.pem Key is saved at: /etc/letsencrypt/live/dodream.store/privkey.pem This certificate expires on 2024-12-29. These files will be updated when the certificate renews. Certbot has set up a scheduled task to automatically renew this certificate in the background. Deploying certificate Successfully deployed certificate for dodream.store to /etc/nginx/sites-enabled/default Successfully deployed certificate for www.dodream.store to /etc/nginx/sites-enabled/default Congratulations! You have successfully enabled HTTPS on https://dodream.store and https://www.dodream.store - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - If you like Certbot, please consider supporting our work by: * Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate * Donating to EFF: https://eff.org/donate-le - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ubuntu@ip-172-31-2-73:/etc/letsencrypt$ ls accounts archive cli.ini live options-ssl-nginx.conf renewal renewal-hooks ssl-dhparams.pem ubuntu@ip-172-31-2-73:/etc/letsencrypt$ cd live bash: cd: live: Permission denied ubuntu@ip-172-31-2-73:/etc/letsencrypt$ sudo chown -R $USER:$USER /etc/letsencrypt ubuntu@ip-172-31-2-73:/etc/letsencrypt$ sudo chmod -R 755 /etc/letsencrypt ubuntu@ip-172-31-2-73:/etc/letsencrypt$ cd live ubuntu@ip-172-31-2-73:/etc/letsencrypt/live$ ls README dodream.store ubuntu@ip-172-31-2-73:/etc/letsencrypt/live$ cd dodream.store/ ubuntu@ip-172-31-2-73:/etc/letsencrypt/live/dodream.store$ ls README cert.pem chain.pem fullchain.pem privkey.pem ubuntu@ip-172-31-2-73:/etc/letsencrypt/live/dodream.store$ sudo chmod 644 /etc/letsencrypt/live/dodream.store/fullchain.pem ubuntu@ip-172-31-2-73:/etc/letsencrypt/live/dodream.store$ sudo chmod 644 /etc/letsencrypt/live/dodream.store/privkey.pem
Shell
복사
HTTPS 인증 위한 nginx.conf 수정
server { # HTTP 요청이 들어오면 listen 80; server_name dodream.store www.dodream.store; location / { # 모든 요청을 HTTPS로 리디렉션 return 301 https://$host$request_uri; } } server { # 443 포트에서 SSL(HTTPS) 요청을 처리 listen 443 ssl; server_name dodream.store www.dodream.store; # SSL 인증서 경로 설정 ssl_certificate /etc/ssl/certs/fullchain.pem; ssl_certificate /etc/ssl/certs/privkey.pem; # SSL 설정 ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers HIGH:!aNULL:!MD5; root /usr/share/nginx/html; index index.html; location / { try_files $uri $ruri/ /index.html; } }
Shell
복사

구성도

도메인 주소변경

Louter53 설정 변경
Nginx 설정 변경