Docker 기반 Jenkins를 이용한 애플리케이션 자동 배포

KangWook·2024년 10월 10일
0

소개

CI/CD 파이프라인에서 Jenkins는 자동화 도구로 널리 사용됩니다. 특히, Docker를 사용하면 Jenkins를 손쉽게 설치하고 실행할 수 있으며, Docker 컨테이너에서 일관된 환경에서 애플리케이션을 빌드하고 배포할 수 있습니다.
Docker 컨테이너로 Jenkins를 실행하고, 이를 통해 애플리케이션을 빌드하여 Docker 이미지로 생성하고 AWS에 배포하는 하는 중 권한 관련 문제 트러블이 생겨 정리 해보고자 합니다

요구 사항

  • Docker가 설치된 서버
  • GitHub 저장소 (애플리케이션 소스 코드)
  • AWS S3 및 IAM 설정 (배포 용도)

Jenkins Docker 컨테이너 실행

1. Jenkins Docker 이미지 실행

먼저 Docker를 이용해 Jenkins를 실행합니다. 공식 Jenkins 이미지를 사용하여 Docker 컨테이너를 실행할 수 있습니다.

docker run -d -p 8080:8080 -p 50000:50000 \
  --name jenkins \
  -v jenkins_home:/var/jenkins_home \
  -v /var/run/docker.sock:/var/run/docker.sock \
  jenkins/jenkins:lts

-v /var/run/docker.sock:/var/run/docker.sock 옵션을 통해 Jenkins가 호스트의 Docker 데몬에 접근할 수 있게 합니다.
이 설정은 Jenkins가 Docker 컨테이너를 관리하고 빌드할 수 있도록 도와줍니다.
jenkins_home 볼륨은 Jenkins 설정과 데이터를 지속적으로 유지하기 위해 필요합니다.

2. Jenkins 초기 설정

Jenkins가 처음 실행되면 브라우저를 통해 초기 설정을 완료해야 합니다.

  1. 브라우저에서 http://<서버_IP>:8080으로 접속합니다.
  2. Jenkins가 제공하는 초기 암호를 입력합니다. 초기 암호는 컨테이너 내 /var/jenkins_home/secrets/initialAdminPassword에 저장되어 있습니다.
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword

Jenkins Pipeline 설정

Jenkins에서 애플리케이션을 빌드하고, Docker 이미지를 생성한 후, 이를 AWS에 배포하는 파이프라인을 구성합니다.

  1. Jenkinsfile 작성
    프로젝트의 루트 디렉터리에 Jenkinsfile을 추가하여 파이프라인을 정의합니다. 아래는 Gradle 기반의 애플리케이션을 빌드하고 Docker 이미지를 생성한 후 AWS에 배포하는 파이프라인 예시입니다.
pipeline {
    agent any

    environment {
        DOCKER_IMAGE = 'my-docker-repo/my-app:latest'
        AWS_S3_BUCKET = credentials('AWS_S3_BUCKET')
        AWS_REGION = credentials('AWS_REGION')
        AWS_ACCESS_KEY = credentials('AWS_ACCESS_KEY')
        AWS_SECRET_KEY = credentials('AWS_SECRET_KEY')
    }

    stages {
        stage('Clone Repository') {
            steps {
                git branch: 'main', url: 'https://github.com/your-repo/your-app.git'
            }
        }

        stage('Build Application') {
            steps {
                sh './gradlew clean build'
            }
        }

        stage('Build Docker Image') {
            steps {
                script {
                    withCredentials([usernamePassword(credentialsId: 'dockerhub', usernameVariable: 'DOCKER_USER', passwordVariable: 'DOCKER_PASS')]) {
                        sh '''
                            echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin
                            docker build -t $DOCKER_IMAGE .
                            docker push $DOCKER_IMAGE
                        '''
                    }
                }
            }
        }

        stage('Configure AWS') {
            steps {
                sh '''
                    echo "cloud:" > src/main/resources/application.yml
                    echo "  aws:" >> src/main/resources/application.yml
                    echo "    s3:" >> src/main/resources/application.yml
                    echo "      bucket: $AWS_S3_BUCKET" >> src/main/resources/application.yml
                    echo "    region:" >> src/main/resources/application.yml
                    echo "      static: $AWS_REGION" >> src/main/resources/application.yml
                    echo "    stack:" >> src/main/resources/application.yml
                    echo "      auto: false" >> src/main/resources/application.yml
                    echo "    credentials:" >> src/main/resources/application.yml
                    echo "      accessKey: $AWS_ACCESS_KEY" >> src/main/resources/application.yml
                    echo "      secretKey: $AWS_SECRET_KEY" >> src/main/resources/application.yml
                '''
            }
        }

        stage('Deploy Application') {
            steps {
                sh '''
                    docker stop my-app || true
                    docker rm my-app || true
                    docker run -d --name my-app -p 8080:8080 \
                    -e AWS_ACCESS_KEY=$AWS_ACCESS_KEY \
                    -e AWS_SECRET_KEY=$AWS_SECRET_KEY \
                    -e AWS_REGION=$AWS_REGION \
                    -e AWS_S3_BUCKET=$AWS_S3_BUCKET \
                    $DOCKER_IMAGE
                '''
            }
        }
    }

    post {
        always {
            sh 'docker logout'
        }
    }
}
  1. Jenkins 파이프라인 실행
    Jenkins 대시보드에서 새로운 Pipeline Job을 생성하고, 위에서 작성한 Jenkinsfile을 통해 파이프라인을 실행합니다.

Jenkins 대시보드에서 "New Item"을 클릭하고, 파이프라인을 선택하여 새로운 Job을 생성합니다.
소스 코드 관리에서 GitHub 저장소 URL을 입력하고, Jenkinsfile의 위치를 지정합니다.
파이프라인이 실행되면 Jenkins가 GitHub에서 소스 코드를 가져와 애플리케이션을 빌드하고, Docker 이미지를 생성 및 배포합니다.

Docker 권한 문제 해결

Jenkins가 Docker 데몬에 접근할 수 없는 경우, 다음과 같은 권한 오류가 발생할 수 있습니다:

WARNING! Your password will be stored unencrypted in /var/jenkins_home/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credential-stores
Login Succeeded
+ docker build -t kanguk148/jenkins:test .
DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
            Install the buildx component to build images with BuildKit:
            https://docs.docker.com/go/buildx/
permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post "http://%2Fvar%2Frun%2Fdocker.sock/v1.47/build?buildargs=%7B%7D&cachefrom=%5B%5D&cgroupparent=&cpuperiod=0&cpuquota=0&cpusetcpus=&cpusetmems=&cpushares=0&dockerfile=Dockerfile&labels=%7B%7D&memory=0&memswap=0&networkmode=default&rm=1&shmsize=0&t=kanguk148%2Fjenkins%3Atest&target=&ulimits=%5B%5D&version=1": dial unix /var/run/docker.sock: connect: permission denied
script returned exit code 1

이 문제는 Jenkins 사용자가 Docker에 접근할 권한이 없기 때문에 발생합니다. 이를 해결하려면 Jenkins 사용자를 docker 그룹에 추가해야 합니다.

Docker 소켓의 권한을 확인합니다

docker exec -it -u root jenkins bash
ls -l /var/run/docker.sock

소켓의 권한이 적절하지 않다면, 다음 명령어로 권한을 수정합니다:

chmod 666 /var/run/docker.sock

Jenkins 컨테이너 내부의 Docker 그룹 GID가 호스트 시스템의 Docker 그룹 GID와 일치하는지 확인합니다. 일치하지 않는다면, 다음 명령어로 수정합니다:

groupmod -g <호스트_도커_GID> docker

Jenkins 사용자를 Docker 그룹에 다시 추가합니다:

usermod -aG docker jenkins

Jenkins 컨테이너를 재시작합니다:

docker restart jenkins
profile
꾸준히 성장하는 개발자

0개의 댓글