안녕하세요. 회사와 함께 성장하고 싶은 KOSE입니다.

 

이번 포스팅은 도커를 공부하여 이해한 Docker Container에 대한 정리를 작성하고자 합니다.

내용이 다르거나 잘못된 점이 있다면, 댓글 부탁드립니다!

 

 

1. Docker

 

도커 컨테이너는 말 그대로 물건을 담는 그릇과 비슷합니다.

애플리케이션이 있는 상자가 있다면, 이 상자 안에는 애플리케이션과 애플리케이션을 실행할 컴퓨터의 정보가 주어져 있습니다.컴퓨터 정보는 IP, 컴퓨터 이름, 디스크 드라이브 등이 포함됩니다.도커 컨테이너는 사용하는 호스트 운영체제나 다른 환경에 종속적이지 않은 환경에서 애플리케이션을 동작할 수 있는 장점이 있습니다.

 

도커를 실행하고자 하는 Host OS의 종류에 따라 도커가 다른 방식으로 운용될 수 있습니다.

저는 맥 OS를 사용하고 있으므로, 맥을 기준으로 설명하고자 합니다!

 

 

2. Mac의 운영체제에서 동작하는 Docker의 VM

 

윈도우, 맥 OS는 리눅스 운영체제에서 사용하는 커널과 별개의 시스템입니다. 

윈도우는 Windows NT 커널을 기반으로 하고, 맥 OSsms UNIX 계열의 BSD를 기반으로 하는 XNU 커널을 사용합니다.

따라서, 리눅스 커널과 별개의 시스템이므로 이를 호환시켜 줄 기능이 필요합니다.

 

맥은 Docker Desktop for Mac 가상화 기술을 이용해 내부적으로 리눅스 VM을 실행하고 이 VM 위에서 도커 컨테이너를 구동합니다.

경량화된 VM은 도커를 운용하기 위해 필요한 기능만 적용되어 운영체제 위에 하이퍼바이저로 각자 별도의 운영체제를 가지고 운영되는 가상 머신과 비교하여 리소스 낭비가 적습니다.

 

 

Host OS 관점에서는 Docker 경량화 VM은 맥 OS에서 하나의 프로세스로 실행되므로 고유한 PID를 가집니다.

즉, 맥 OS 관점에서는 Docker 경량화 VM은 하나의 애플리케이션 혹은 프로세스로 처리됩니다.

 

 

2. Docker 경량화 VM에서 동작하는 개별 컨테이너

 

Docker 경량화 VM이 Host OS의 개별 PID였다면, Docker 경량화 VM에서 컨테이너는 개별 PID 입니다.

VM 내부에서 실행되는 Docker 컨테이너들은 리눅스 커널 위에서 각기 독립된 프로세스로 실행이 됩니다.

Docker VM은 리눅스 기반이므로 Docker 컨테이너는 이 VM의 리눅스 커널을 공유하게 되는 것입니다.

 

그 결과, 프로세스를 격리할 수 있습니다.

각 컨테이너는 독립된 프로세스로 실행되지만, 동일한 커널 인스턴스를 사용합니다.

 

또한, 네임스페이스라는 커널 기능을 사용하여 격리됩니다.

네임스페이스는 프로세스 ID, 네트워크 인터페이스, 마운트 포인터 등을 컨테이너별로 분리하여

각 컨테이너가 자신만의 격리된 환경을 가지는 것처럼 동작합니다.

 

 

여기서 중요한 점은 각 컨테이너는 VM 내부에서 고유한 PID를 가지는데,

이 PID는 호스트 OS(Mac)의 PID 공간과는 분리되어 있습니다.

따라서, 호스트 OS는 이 내부 PID를 직접적으로는 알 수 없습니다.

 

이 경량화된 VM은 개별적인 운영체제를 가지고 운용되는 가상 머신 아키텍처와는 다른 구조를 보입니다.

위의 사진처럼 Docker VM은 각 컨테이너가 격리된 환경에서 운용되지만, 공통된 Docker VM 내부에서 동작합니다.

이는 운영체제를 공유함으로써 불필요한 리소스를 막을 수 있습니다.

 

도커는 이러한 점에서 격리밀집이라는 두 가지 특징을 충족하게 됩니다.

  • 격리: 각 컨테이너가 격리된 환경에서 운용
  • 밀집: 컴퓨터에 CPU와 메모리가 허용하는 한 되도록 많은 애플리케이션을 실행

 

하지만, 각자 별도의 운영체제를 갖는 가상머신은

 

 

별도의 운영체제로 인해, 애플리케이션이 사용할 CPU, 메모리 자원을 많이 사용하게 됩니다.

또한, 운영체제의 라이선스 비용 및 운영체제 설치 비용 등 복잡한 문제가 추가로 발생합니다.

이러한 특징은 격리된 환경은 보장하지만, 밀집의 특성을 충족하지 못하는 단점이 있습니다.

 

 

3. 정리하며

 

도커 및 쿠버네티스를 공부할 때,  "도커는 OS를 공유한다"라는 의미가 사실 많이 혼란스러웠습니다.

Host OS - 도커 경량화 VM - 각 도커 컨테이너의 연관성을 다시 생각하고 정리하니 어떠한 의미인지 이해할 수 있었습니다.

 

정리하면 다음과 같습니다.

  • 리눅스 환경에서는 컨테이너가 직접적으로 호스트 OS의 리눅스 커널을 공유합니다.
    • 각 컨테이너는 별도의 사용자 공간을 가지지만, 모두 같은 커널 공간을 사용합니다.
    • 각 컨테이너 프로세스는 호스트 시스템에서 고유한 PID를 가지며 실행됩니다.
  • 하지만, 운영체제가 맥이나 윈도우인 경우 직접적인 커널 공유가 불가능합니다.
    • Docker는 리눅스 가상 머신을 구동하고 이 가상 머신에 내장된 리눅스 OS에서 컨테이너를 실행합니다.
    • OS를 공유한다는 의미는 이 VM 내부에서 실행되는 컨테이너가 VM의 리눅스 OS 커널을 공유한다는 의미입니다.
    • 각 컨테이너들은 리눅스 가상 머신 내에서 고유한 PID를 가지며 관리됩니다.

 

이상으로 Docker Container에 대한 정리를 마치도록 하겠습니다!

읽어주셔서 감사합니다!

안녕하세요! 회사와 함께 성장하고 싶은 KOSE입니다.

이번 포스팅은 깃 액션을 활용한 배포에 대한 내용을 정리하고자 합니다.

 

개발에서 CI/CD (Continuous Integration/Continuous Delivery) 중요한 부분입니다.

지속적으로 통합하고, 지속적인 배포로 사용자에게 하여금 지속적인 서비스를 제공하는 것이 중요합니다.

이를 위해 적용할 수 있는 깃 액션과 코드 디플로이를 활용한 배포를 설명하겠습니다.

 

 

1. 목표 플로우

 

 

코드를 배포해야 할 때, 배포를 위한 브랜치로 변경하면 깃 액션이 동작하여 S3에 jar를 비롯한 배포 관련 yml, sh 압축하여 전송합니다.

이어서, Code Deploy를 호출하여 Code Deploy가 S3에 필요한 배포 정보를 바탕으로 EC2에 데이터를 복사합니다.

EC2에서는 앞 서 정의한 배포 관련 yml, sh를 통해 System Manager로부터 필요한 환경 변수를 로드합니다.

이어서, 엔진엑스가 스프링의 포트를 리버스 프록시 하여 배포가 완성되게 됩니다.

 

엔진엑스를 적용하는 과정을 ALB를 도입하여 안정적이고 관리가 용이하게 바꿀 수 있습니다.

(하지만 비용 문제로 인해 NGINX를 적용하였습니다!)

 

 

2. 깃 액션 정의하기

 

저는 main branch에 PR을 요청할 때는 테스트를 실행하는 깃 액션을 정의하였습니다. 배포는 deploy라는 브랜치를 생성하여

배포가 될 수 있도록 정의하였습니다.

 

 

작성하는 워크 플로우는 위에 사진에 자세하게 작성한 파트로 나눌 수 있습니다.

어떠한 브랜치에 적용할 것인지, 어떠한 환경에서 실행할 것인지, 어떻게 단계를 정의할 것인지로 크게 구분할 수 있습니다.

 

main에 머지된 코드로 deploy 브랜치를 생성하면 하단처럼 깃액션이 활성화됩니다.

 

 

다음은 S3에 파일을 올리기 위해 최상위 루트 디렉터리로 tar를 만드는 과정입니다.

jar 혹은 배포를 위한 yml, sh 등을 개별적으로 S3에 올릴 수 있지만 Code Deploy를 활용하려면 tar 혹은 압축 파일로 만들어야 합니다.

 

 

위의 코드처럼, 각 build/libs에 있는 파일과 배포를 위해 정의한 sh, yml에서 파일을 복사한 후 압축하여 tar로 만들어 줍니다.

이제, S3에 파일을 올리는 과정을 정의하면 다음과 같습니다.

 

 

S3에 올리기 위해서는 S3 버킷의 Region 및  URL을 입력합니다.

이어서, 접근하기 위한 액세스 키와 시크릿 키를 입력해야 합니다.

여기에 필요한 정보를 yml 등에 정의하게 된다면 큰 보안 위협을 받을 수 있습니다.

깃 액션에서는 필요한 환경 정보를 시크릿으로 유지할 수 있도록 정의할 수 있는 공간이 있습니다.

 

 

리포지토리의 settings - 좌측 메뉴의 Secrets and variables의 Actions를 클릭하면 New repository secret을 생성할 수 있습니다.

이곳에 S3에 접근 권한을 IAM 권한을 받은 사용자의 액세스키와 시크릿키를 입력하면 됩니다.

(IAM 관련 설명은 좋은 강의가 있어서 링크드립니다! https://www.youtube.com/watch?v=lcly_aIq1KI)

 

 

마지막으로 Code Deploy를 실행하기 위해 Code Deploy에 정의한 배포 그룹 및 애플리케이션 이름, 복사할 버킷 이름 등을 작성합니다.

Code Deploy를 설정하는 과정은 다소 복잡하여 다른 포스팅에서 작성하도록 하겠습니다!

 

이렇게 git Action의 활동에서 처리할 각 역할을 설정해주었습니다.

이제, Code Deploy가 실행될 때, 해야 할 역할을 정의하도록 하겠습니다.

 

 

2. Code Deploy appspec.yml 정의

 

Code Deploy는 정의한 appspec의 내용에 따라, 배포 시나리오를 설정할 수 있습니다.

먼저 Code Deploy는 EC2에 대한 접근 권한, S3에 대한 접근 권한이 있어야 합니다

 

 

작성하는 appspec.yml은 다음처럼 복사하고자 하는 위치, 접근 권한, 스크립트 단계별 실행 등으로 

구성할 수 있습니다.

 

이렇게 작성한 appspec.yml은

code deploy가 실행될 때 앞 서 만든 tar를 압축 해제하고 최상위 루트에 작성된 appspec.yml에 따라 해당 시나리오가

동작하는 방식으로 처리가 됩니다.

 

 

3. Hooks에 적용할 스크립트

 

appspec.yml에 작성한 스크립트는 EC2에 적용되어 실행이 됩니다.

먼저 스프링 서버를 실행하기 위한 java 설치 등은 before_install.sh 에 정의할 수 있습니다.

 

 

자바가 설치되면, application_start.sh를 수행하여 스프링 서버를 배포할 수 있습니다.

 

 

EC2에 스프링을 배포할 때 필요한 환경 변수들이 있을 수 있습니다. 이 정보는 AWS에서 제공하는 System Manager의 파라미터 스토어에 정보를 입력할 수 있습니다. 필요한 정보를 파라미터 스토어에 입력한 후 EC2가 System Manager에 대한 권한을 가지도록 역할을 설정해주면, 이를 안전하게 활용할 수 있습니다.

 

 

다음은, 헬스체크를 통해 현재 실행 중인 포트를 확인하여, 다른 포트로 배포하기 위한 과정입니다.

서버에 정의한 헬스체크 url로 먼저 8080 포트로 요청을 보내서, 만약 해당 포트가 정상 응답이 온다면

배포해야 하는 포트는 8081이 됩니다(사용하지 않는 포트)

 

반면 헬스체크가 안된다면 8080 포트를 사용하지 않는 것이므로 8080 포트로 jar를 실행해야 합니다.

 

 

이어서, 정상 일정 시간 기다린 후 jar를 실행한 포트로 헬스체크를 보냅니다. 만약 성공한다면 이전에 배포한 서버를 graceful로 안정적으로 종료하고, 사용한 PID를 KILL 합니다.(graceful은 스프링의 application.yml에 정의되어야 합니다!)

 

 

마지막으로, 80 포트로 오는 유저의 요청을 현재 실행 중인 스프링 서버로 연결될 수 있도록,

엔진엑스의 리버스 프록시를 바꿔줍니다.

 

 

이렇게 함으로써, 깃 액션과 코드 디플로이를 바탕으로 한 배포 과정을 설정할 수 있습니다!

 

엔진엑스 리버스 프록시를 연결하는 과정은 향로님이 작성하신 엔진엑를 활용한 무중단 배포 구축하기에 상세하게 작성되어 있어서 링크를 남기겠습니다 (https://jojoldu.tistory.com/267)

 

이상으로 깃 액션과 코드 디플로이로 배포하는 과정 정리를 마치겠습니다.

읽어주셔서 감사합니다!

'DevOps' 카테고리의 다른 글

[DevOps] Docker Container  (1) 2023.11.04
[DevOps] Jenkins - Docker - AWS 자동 배포 1편  (0) 2023.03.06

안녕하세요. 회사와 함께 성장하고 싶은 KOSE입니다.
이번 글은 Jenkins - Docker -AWS를 활용하여 CI/CD를 구축하는 과정을 정리하고자 합니다.
제 운영 환경은 ubuntu22.04이므로 linux 기준으로 정리하도록 하겠습니다.

 

 

1. Jenkins를 왜 써야 할까요?

 

Jenkins는 빌드와 배포를 자동화해주는 기능을 가지고 있습니다. 
저는 AWS로 배포를 할 때 jenkins를 사용하지 않았었습니다. 사용하기 복잡하고 jar로 빌드하고 도커 이미지로 만들고
바로 서버로 배포하면 되기 때문에 초기 설정하는 과정을 생략하고 진행했습니다.
하지만, 한 두번은 할 수 있지만, 실제 local 서버와 AWS dev 혹은 prod 환경은 매우 다르기 때문에 빌드한 결과물이 실제
dev 서버에서는 제대로 작동하지 않는 문제가 발생했습니다.
이때마다 다시 코드를 수정하고 jar로 빌드하고, 도커로 이미지 생성 후 tar 파일로 변환하고 ssh로 aws로 전송 후
다시 aws 에서 해당 도커 이미지를 실행하는 과정이 매우 복잡했습니다.

이미지로 보면 다음과 같습니다.

 

만약 실제 서버에서 error가 발생한다면 Jar 빌드부터 다시 개발자가 진행해야 했습니다.
그 결과 정말 많은 시간을 투입하게 되었습니다. 시간도 시간이지만, AWS EC2의 경우, 비용이 발생하기 때문에
비효율적인 방법이었습니다.
따라서, 이러한 빌드 -> 배포 -> 운영 하는 과정을 하나로 통합하여 관리할 수 있는 Jenkins를 선택하여 활용하고자 하였습니다. 

 

2. Jenkins 설치

 

sudo apt-get install jenkins

 

설치가 완료되면, localhost:8080으로 접속할 수 있습니다.
접속하면 다음과 같은 화면이 나옵니다.

 

 

만약 도커로 jenkins를 실행하면 console로 확인할 수 있지만, jenkins가 설치되면서 바로 실행된 경우는
직접 terminal에서 password를 확인해야 합니다.

 

sudo cat /var/lib/jenkins/secrets/initialAdminPassword

 

해당 코드를 터미널에 입력하면 패스워드를 확인할 수 있습니다. 터미널 내용을 복사하여 접속하면 초기 플러그인 설정 관련 나옵니다. 
왼쪽에 기본적으로 필요한 플러그인을 설치할 수 있도록 돕는 것을 선택하면 로딩과 함께 플러그인이 설치됩니다.

 

 

도중에 플러그인 설치를 실패할 수 있는데, 넘어가서 다시 플러그인을 재설치할 수 있습니다. 따라서, 기본 플러그인 설치가 실패하더라도 크게 걱정하지 않으셔도 괜찮습니다.!

 

설치가 완료되면 다음과 같은 화면을 확인할 수 있습니다.

 

 

 

하단으로 내리면 plugin을 추가로 설치할 수 있는 항목이 있습니다. 해당 항목을 클릭한 후, 이전에 실패했던 모듈을 추가로 
설치하였습니다.  또한, post build task를 추가하여 빌드 후 작업을 수행할 수 있도록 플러그인을 설정하였습니다.

 

 

 

3. 필요한 SSH 키 준비하기


보통 깃허브를 사용하시면 ssh로 전송을 많이 이용하셨을 것입니다.
ssh 관련 키는 아래 코드로 접속하여 확인할 수 있습니다.

cat id_rsa를 입력하면 해당 private key를 확인할 수 있습니다. 

추후, ssh 인증을 하기 위해 미리 복사를 해두도록 하겠습니다.

(중요한 점은 -----BEGIN OPENSSH PRIVATE KEY-----  -----END OPENSSH PRIVATE KEY-----

jenkins에 ssh 인증 정보를 입력할 때 이것까지 모두 복사하셔야 합니다!)

 

cd ~/.ssh
cat id_rsa

만약 ssh 키가 등록되어 있지 않다면 키를 생성한 후, id_rsa.pub를 github에 등록하여야 합니다.
이번 포스팅에서는 ssh 키를 등록하는 방법은 다루지 않도록 하겠습니다.

 

 

4. Security 및 Credential 등록하기 

 

 

먼저 Configure Global Security를 클릭하여 Git Host Key Verification Configuration 설정을 Accept first Connection으로 설정합니다. Accept first Connection은 최초 요청 검증 후에 추후 요청을 검증하지 않는다는 것입니다. 만약 production 환경이라면 
문제가 발생하지만 현재 저는 dev 환경을 테스트하고 있기 때문에 해당 설정을 하였습니다.

 

 

이후, Manage Credentials을 클릭하여 새로운 Credential을 생성합니다.

 

 

하단의 Global credentials을 클릭하면 아래의 환경으로 이동합니다. 이후 Add Credentials를 클릭합니다.

 

 

현 버전은 최신 Jenkins 버전이기 때문에, username - password 방식은 지원하지 않는다고 하여, SSH 연결을 하는 Credential을 생성하였습니다.

 

 

하단의 key를 등록하는 과정에서 앞 선 3. 번에서 진행했던 id_rsa private key를 복사하여 입력합니다.
이후 등록을 누르면 키가 생성된 것을 확인할 수 있었습니다.

 

 

 

 

5. Item 등록하기

 

대시보드로 이동 후 add Item을 클릭하면 다음의 화면으로 이동합니다.

해당 프로젝트명과 Freestyle project를 클릭합니다.

 

 

이 후, Git repository에서 관리하고 있는 repository를 등록합니다.
(private repository는 ssh 링크와 credential이 필요합니다) 
이 후, private Repository에서 빌드할 브랜치를 설정하여 확인을 누릅니다.
저는 main 브랜치를 사용하였기 때문에 */main으로 설정하였습니다.

 

 

 

빌드 테스트 결과 성공하였습니다.

 

추가적인 build script를 작성하여 실제 jar 파일로 빌드를 하면 다음과 같습니다.

Build Steps에 ./gradlew clean build를 작성하여 저장합니다.

 

이 후 다시 빌드하면, 빌드 성공과 동시에 작업 공간에 빌드 폴더와 파일이 생성되게 됩니다.!

 

script로 빌드를 자동화할 수 있는 것의 장점은 configuration이나 yml 혹은 기타 스프링 빈들이 환경에 따라 다르게 작동될 수 있습니다. 따라서 젠킨스로 여러개의 프로젝트를 개별적으로 빌드하고 관리할 수 있으므로 효율적인 관리 체계를 유지할 수 있습니다.!

 

이 후 2편에서 jenkins에서 빌드 후 AWS로 배포하는 과정을 작성하도록 하겠습니다.!
부족하지만 읽어주셔서 감사드립니다.!

'DevOps' 카테고리의 다른 글

[DevOps] Docker Container  (1) 2023.11.04
[DevOps] 깃 액션/코드 디플로이를 활용한 배포  (0) 2023.10.29

+ Recent posts