3.1 DevOps 서비스를 이용한 Spring Boot 앱을 OKE에 배포 자동화하기
DevOps 서비스 사용을 위한 IAM Policy 설정
DevOps 서비스를 사용하기 위해서는 DevOps 자원들에 권한 설정이 필요합니다. 공식 문서를 참조하여 권한 설정을 위한 Dynamic Group 및 Group에 대한 Policy를 설정합니다.
아래 Dynamic Group 및 Policy는 위 문서의 예제를 기준으로 작성한 내용으로 요구사항에 따라 일부 변경이 될 수 있습니다.
Dynamic Group 만들기
주어진 Compartment 내에서 DevOps 서비스를 사용할 수 있도록 Compartment에 대한 Dynamic Group을 먼저 생성합니다.
OCI 콘솔에 로그인합니다.
좌측 상단 햄버거 메뉴에서 Identity & Security > Identity > Compartments로 이동합니다.
DevOps를 사용할 Compartment로 이동하여 OCID를 복사해 둡니다.
Identity > Dynamic Groups로 이동합니다.
Create Dynamic Group을 클릭합니다.
복사해둔 Compartment OCID를 이용해 필요한 Dynamic Group을 만듭니다.
CoderepoDynamicGroup
ALL {resource.type = 'devopsrepository', resource.compartment.id = '<YourCompartmentOCID>'}
ConnectionDynamicGroup
ALL {resource.type = 'devopsconnection', resource.compartment.id = '<YourCompartmentOCID>'}
BuildDynamicGroup
ALL {resource.type = 'devopsbuildpipeline', resource.compartment.id = '<YourCompartmentOCID>'}
DeployDynamicGroup
All {resource.type = 'devopsdeploypipeline', resource.compartment.id = '<YourCompartmentOCID>'}
DevOps 서비스를 위한 Policy 설정하기
Identity > Policies로 이동합니다.
Create Policy을 클릭하여 새 Policy를 만듭니다.
Compartment 레벨로 다음 Policy를 만듭니다.
- Name: 예) DevOps-compartment-policy
Allow dynamic-group CoderepoDynamicGroup to manage devops-family in compartment <YourCompartmentName> Allow dynamic-group BuildDynamicGroup to manage repos in compartment <YourCompartmentName> Allow dynamic-group BuildDynamicGroup to read secret-family in compartment <YourCompartmentName> Allow dynamic-group BuildDynamicGroup to manage devops-family in compartment <YourCompartmentName> Allow dynamic-group BuildDynamicGroup to manage generic-artifacts in compartment <YourCompartmentName> Allow dynamic-group BuildDynamicGroup to use ons-topics in compartment <YourCompartmentName> Allow dynamic-group CoderepoDynamicGroup to read secret-family in compartment <YourCompartmentName> Allow dynamic-group DeployDynamicGroup to manage all-resources in compartment <YourCompartmentName> Allow dynamic-group ConnectionDynamicGroup to read secret-family in compartment <YourCompartmentName>
Root Compartment 레벨로 다음 Policy를 만듭니다.
- Name: 예) DevOps-root-policy
OCIR에 Repository를 Push하기 전에 미리 생성하지 않으면 기본적으로 Root Compartment에 이미지가 Push됩니다. 이때 권한으로 에러가 발생하며, Root Compartment에도 허용하고자 하면 다음을 추가합니다.
Allow dynamic-group BuildDynamicGroup to manage repos in tenancy
DevOps 서비스를 통한 CI/CD 배포 자동화 하기
Notification Topic 만들기
DevOps 파이프 라인 실행이 발생하는 주요 이벤트를 알려주기 위한 용도로 Notification Topic 설정이 필요합니다. DevOps 프로젝트 생성시 필수 요구 사항이라 미리 만듭니다
OCI 콘솔에 로그인합니다.
좌측 상단 햄버거 메뉴에서 Developer Services > Application Integration > Notifications으로 이동합니다.
Create Topic을 클릭하여 Topic을 생성합니다.
- Name: 예) oke-labs-devops-topic
Notification을 위해 생성한 Topic 이벤트를 가져갈 Subscrition을 일단 생략합니다. 필요시 구성하시면 됩니다.
DevOps 프로젝트 만들기
OCI 콘솔에 로그인합니다.
좌측 상단 햄버거 메뉴에서 Developer Services > DevOps로 이동합니다.
프로젝트 생성을 위해 Projects로 이동하여 Create DevOps project를 클릭합니다.
생성 정보를 입력하여 프로젝트를 만듭니다.
- Project name: 예) my-devops-project
- Notification Topic: 앞서 생성한 Topic 선택
프로젝트 생성완료
Enable Logging
프로젝트 생성 직후 Enable Logging 관련 정보가 보이는 것을 볼 수 있습니다. 설명문에서 보는 것 처럼 Logging을 활성화하지 않을 경우, 파이프라인 실행 화면에서 오른쪽에 보이는 실행 로그가 안보입니다. 그래서 Enable Logging은 필수입니다.
Project Overview에서 Enable Log을 클릭하거나 왼쪽 메뉴에서 Logs를 클릭합니다.
로그를 활성화 버튼을 토글합니다.
대상 Compartment에 이미 Log Group이 있는 경우 나열된 것 중에 선택이 가능합니다. 미리 생성된 Log Group이 없는 경우 아래와 같이 자동입력된 정보를 바탕으로 Enable Log 버튼 클릭시 새로 Log Group과 Log가 만들어 지게 됩니다. 필요시 설정을 수정하고 그렇치 않으면, Enable Log 버튼을 클릭합니다.
Code Repository를 사용하여 애플리케이션 코드 관리하기
샘플로 Spring Boot Helloworld 앱을 만들어 테스트하겠습니다.
코드 저장소 생성을 위해 왼쪽 메뉴에서 Code Repositories를 클릭합니다.
Create repository를 클릭하여 저장소를 만듭니다.
- Repository name: 예) spring-boot-hello-repo
생성된 코드 저장소 입니다. 일반적인 Git Repository입니다.
실제 개발 작업은 git 명령을 통해 개발 PC에서 진행하면 됩니다. 저장소 상세정보 위에 있는 Clone 버튼을 하면 Clone 명령어가 아래 그림처럼 뜨게 됩니다. 여기서는 Clone with HTTPS를 사용하겠습니다.
개발 PC에 복사한 주소를 사용해 git clone 명령어를 통해 복제합니다.
git clone <YourClonewithHTTPS URL>
이때 사용자 인증이 필요합니다. HTTPS기반 사용자 인증시 아래 유저명 형식과 AuthToken을 사용합니다.
인증 유저명
- Oracle Identity Cloud Service상의 유저:
<tenancy-name>/oracleidentitycloudservice/<username>
- OCI Local 유저:
<tenancy-name>/<username>
- 이전 가이드들과 달리 tenancy-namespace가 아닌 tenacy-name인 것에 주의합니다.
- Oracle Identity Cloud Service상의 유저:
AuthToken: 생성에 대한 내용은 이전 가이드들을 참고합니다.
Code Repository의 HTTPS 인증관련 문서
예시
$ git clone https://devops.scmservice.ap-seoul-1.oci.oraclecloud.com/namespaces/cnrlxx3w0wgq/projects/my-devops-project/repositories/spring-boot-hello-repo Cloning into 'spring-boot-hello-repo'... Username for 'https://devops.scmservice.ap-seoul-1.oci.oraclecloud.com': thekoguryo/oke-developer Password for 'https://oreozz/oke-admin@devops.scmservice.ap-seoul-1.oci.oraclecloud.com': remote: Counting objects: 2, done remote: Finding sources: 100% (2/2) remote: Getting sizes: 100% (1/1) Unpacking objects: 100% (2/2), done. remote: Total 2 (delta 0), reused 2 (delta 0)
현재 복제된 저장소는 비어 있습니다. 아래 가이드를 통해 spring-boot-hello 샘플 코드를 작성합니다.
작성된 코드를 git 명령어를 통해서 Code Repository에 저장합니다.
예시
git add . git commit -m "init" git push
코드 작성 및 반영 완료
Build Pipeline 만들기
CI/CD 중에 코드를 빌드하여 배포 산출물을 만드는 CI 과정에 해당되는 부분을 Build Pipeline을 통해 구성이 가능합니다.
my-devops-project 프로젝트 페이지로 이동하여 왼쪽 메뉴의 Build Pipelines로 이동합니다.
Create build pipeline을 클릭하여 파이프라인을 생성합니다.
Name: 예) spring-boot-hello-build-pipeline
생성된 파이프라인을 클릭합니다.
그림과 같이 Stage를 추가하여 파이프라인 흐름을 구성할 수 있습니다. Add Stage를 클릭합니다.
제공 Stage
- Managed Build: 빌드스펙에 정의된 내용에 따라 빌드 과정을 실행합니다.
- Delivery Artifacts: 빌드 산출물(예시, 컨테이너 이미지)를 Artifact Repository에 저장합니다.
- Trigger Deployment: 빌드가 끝나고 Deployment Pipeline을 호출합니다.
- Wait: 일정시간 대기합니다.
Build Stage 만들기
빌드를 위해 먼저 Managed Build Stage를 추가합니다.
Managed Build Stage 설정
- Stage name: 예) build-stage
- Build Spec File Path: 빌드 스크립트 경로를 지정합니다. 기본적으로 소스 루트에 있는 build_spec.yaml을 파일을 사용합니다.
- Primary Code Repository: 빌드할 메인 소스가 있는 코드 저장소를 지정합니다.
Primary Code Repository 설정 화면
- 대상 소스 코드가 있는 저장소를 지정합니다.
설정된 Stage를 Add를 클릭하여 추가합니다.
아래 그림과 같이 build-stage가 추가되었습니다. Start Manual Run을 클릭하면 테스트를 해 볼수 있습니다.
테스트처럼 소스 코드상의 Build Spec의 정의가 필요합니다.
Build Spec은 다음 문서를 참조합니다.
https://docs.oracle.com/en-us/iaas/Content/devops/using/build_specs.htm
문서에 있는 Example 2 기준 예시
- steps: 실행할 스크립트를 정의하는 부분입니다. 예제이는 Build Source, Dockerizer 2개의 step이 정의되어 있고 각각 command에서 실행할 스크립트를 정의하고 있습니다. 정의된 순서대로 실행됩니다.
- env.exportedVariables: 전역으로 선언된 환경변수로 이전 step에서 값을 변경하면 그다음 step에도 적용됩니다. Deployment Pipeline을 호출시에도 전달됩니다.
- outputArtifacts: 빌드 산출물의 정의하는 부분으로, 이후 Delivery Artifact Stage를 통해 Artifact Storage에 저장할 때 여기서 정의된 이름을 통해 지정 가능합니다.
version: 0.1 component: build timeoutInSeconds: 6000 shell: bash env: exportedVariables: - BuildServiceDemoVersion steps: - type: Command name: "Build Source" timeoutInSeconds: 4000 command: | echo $PATH mvn clean install - type: Command timeoutInSeconds: 400 name: "Dockerizer" command: | BuildServiceDemoVersion=`echo ${OCI_BUILD_RUN_ID} | rev | cut -c 1-7` echo $BuildServiceDemoVersion docker build -t build-service-demo . outputArtifacts: - name: build-service-demo type: DOCKER_IMAGE location: build-service-demo - name: build-service-demo-kube-manifest type: BINARY location: deployment/app.yml
Build Spec 정의
개발한 spring-boot-hello 소스 코드의 root 경로에 build_spec.yaml을 다음과 같이 정의하고 코드 저장소에 저장합니다.
build_spec.yaml
version: 0.1 component: build timeoutInSeconds: 6000 shell: bash env: variables: appName: "spring-boot-hello" exportedVariables: - APP_NAME - OCIR_PATH - TAG steps: - type: Command name: "Init exportedVariables" timeoutInSeconds: 4000 command: | APP_NAME=$appName echo $APP_NAME - type: Command name: "Build Source" timeoutInSeconds: 4000 command: | echo "build" mvn clean install - type: Command timeoutInSeconds: 400 name: "Build Source - Post" command: | echo "add dependency" mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar) - type: Command name: "Define Image Tag - Commit ID" timeoutInSeconds: 30 command: | COMMIT_ID=`echo ${OCI_TRIGGER_COMMIT_HASH} | cut -c 1-7` BUILDRUN_HASH=`echo ${OCI_BUILD_RUN_ID} | rev | cut -c 1-7` [ -z "$COMMIT_ID" ] && TAG=$BUILDRUN_HASH || TAG=$COMMIT_ID - type: Command name: "Define OCIR Path" timeoutInSeconds: 30 command: | TENANCY_NAMESPACE=`oci os ns get --query data --raw-output` REPO_NAME=$appName OCIR_PATH=$OCI_RESOURCE_PRINCIPAL_REGION.ocir.io/$TENANCY_NAMESPACE/$REPO_NAME - type: Command timeoutInSeconds: 400 name: "Containerize" command: | docker build -t new-generated-image . docker images - type: Command name: "Check exportedVariables" timeoutInSeconds: 30 command: | [ -z "$APP_NAME" ] && APP_NAME=unknown [ -z "$OCIR_PATH" ] && OCIR_PATH=unknown [ -z "$TAG" ] && TAG=unknown echo "APP_NAME: " $APP_NAME echo "OCIR_PATH: " $OCIR_PATH echo "TAG: " $TAG outputArtifacts: - name: output-image type: DOCKER_IMAGE location: new-generated-image
Start Manual Run을 통해 다시 실행하면 아래와 같이 스크립트가 수행되는 것을 볼 수 있습니다.
ExportVariables 확인
실행 결과 화면에서 오른쪽 위쪽 점3개를 클릭하여 상세 화면으로 이동하면 Build Output에서 실행결과로 나온 변수값을 볼 수 있습니다. 이 변수들은 이후 Stage 또는 연결되어 호출된 Deployment Pipeline으로 전달되어 사용할 수 있게 됩니다.
컨테이너 이미지 OCIR 등록 Stage 만들기
Build Pipeline 탭으로 이동합니다.
플러스 버튼을 클릭하여 build-stage 다음에 stage를 추가합니다.
Delivery Artifact Stage를 선택합니다.
stage 이름을 입력하고 Create Artifact를 선택합니다.
Container image 유형으로 Artifact 추가합니다.
- 이미지 경로: docker tag를 달때 사용하는 이미지 경로입니다. 직접 입력해도 되지만 여기서는 build-stage에서 넘어온 exportedVariable을 사용하여
${OCIR_PATH}:${TAG}
과 같이 입력합니다.
- 이미지 경로: docker tag를 달때 사용하는 이미지 경로입니다. 직접 입력해도 되지만 여기서는 build-stage에서 넘어온 exportedVariable을 사용하여
같은 방식으로 하나 더 추가 합니다.
- Name: generated_image_with_latest
- Image Path: ${OCIR_PATH}:latest
Artifact 매핑
Associate Artifact에서 방금 추가한 2개의 Artifact에 실제 컨테이너 이미지 파일을 매핑해 줍니다. 앞서 build-stage에서 build_spec.yaml에서 정의한 outputArtifacts의 이름을 입력합니다.
outputArtifacts: - name: output-image type: DOCKER_IMAGE location: new-generated-image
이제 delivery stage까지 추가하였습니다.
파이프라인을 다시 실행해 봅니다. 실제 소스코드로 빌드된 컨테이너 이미지가 OCIR에 자동으로 등록됩니다.
OCIR 리파지토지(예시, spring-boot-hello)가 사전에 없는 경우 Root Compartment에 Private 형태로 만들어집니다. OKE에서 가져오기 위해서는 imagepullsecret을 사전에 생성하거나, 리파지토리를 미리 Public으로 생성합니다.
Deploy Pipeline 만들기
CI/CD 중에 빌드된 산출물을 가지고 실제 서버에 배포하는 CD 과정에 해당되는 부분을 Deployment Pipeline을 통해 구성이 가능합니다.
my-devops-project 프로젝트 페이지로 이동하여 왼쪽 메뉴의 Deployment Pipelines로 이동합니다.
Create pipeline을 클릭하여 파이프라인을 생성합니다.
Name: 예) spring-boot-hello-deployment-pipeline
생성된 파이프라인을 클릭합니다.
Add Stage를 클릭하여 Stage를 추가합니다.
제공 Stage
Deploy: OKE, Compute 인스턴스 배포, Oracle Function에 배포 기능을 제공합니다.
Control: 승인 대기, 트래픽 변경, 대기 등을 지원합니다.
Integration: 커스텀 로직 수행을 위한 Oracle Function 실행을 지원합니다.
Kubernetes에 배포할 manifest 파일 준비
Kubernetes에 배포할 Stage 유형을 사용하기 위해서는 사전에 배포할 manifest yaml 파일을 준비해야 합니다.
my-devops-project 프로젝트 페이지로 이동하여 왼쪽 메뉴의 Artifacts로 이동합니다.
Artifacts로 앞서 빌드 파이프라인 만들때 등록한 2개가 있는 것을 볼수 있습니다. 여기에 등록된 Artifact는 재사용이 가능합니다.
manifest 파일을 등록하기 위해 Add artifact를 클릭합니다.
4 가지 등록 유형을 제공합니다. 이중에 Kubernetes manifest를 선택합니다.
Kubernetes manifest 유형에는 Artifact Source로 2가지 유형을 제고합니다.
- Artifact Registry Repository: Container Registry로 OCIR을 제공하고 있듯시 Artifact Registry를 서비스로 제공하고 있습니다. 그곳에 있는 자원을 참조할 경우에 선택합니다.
- Inline: 인라인은 현재 DevOps 프로젝트에 있는 여기 Artifact에 직접 입력하는 것을 말합니다.
Artifact Source로 Inline 유형으로 다음과 같이 등록합니다.
- Name: 예) k8s_spring_boot_deploy_template
Value
앞 서와 같이 build-stage에서 export한 변수값들을 사용할 수 있습니다.
apiVersion: apps/v1 kind: Deployment metadata: labels: app: ${APP_NAME} name: ${APP_NAME} spec: replicas: 1 selector: matchLabels: app: ${APP_NAME} template: metadata: labels: app: ${APP_NAME} spec: containers: - name: ${APP_NAME} image: ${OCIR_PATH}:${TAG} --- apiVersion: v1 kind: Service metadata: name: ${APP_NAME}-service annotations: service.beta.kubernetes.io/oci-load-balancer-shape: "10Mbps" spec: type: LoadBalancer ports: - port: 80 protocol: TCP targetPort: 8080 selector: app: ${APP_NAME}
Kubernetes Environment 등록하기
my-devops-project 프로젝트 페이지로 이동하여 왼쪽 메뉴의 Environments로 이동하여 배포할 OKE 환경을 등록합니다.
OKE 유형을 선택합니다.
배포할 클러스터를 선택합니다.
Kubernetes manifest 배포 Stage 만들기
등록한 Deployment Pipeline(spring-boot-hello-deployment-pipeline) 설정 페이지로 이동합니다.
Add Stage를 클릭하여 Apply manifest to your Kubernetes cluster Stage를 추가합니다.
배포할 환경 및 manifest 파일을 선택합니다
파이프라인 완성
Build Pipeline에서 Deployment Pipeline 호출하기
앞서 만든 Build Pipeline에서 컨테이너 이미지 까지 OCIR에 등록하고 나면, OKE에 배포할 Deployment Pipeline을 기동되어야 전체 빌드에서 배포까지가 완료됩니다. 이제 Deployment Pipeline을 등록하였으므로, Build Pipeline에서 호출할 수 있습니다.
앞서 만든 Build Pipelines(spring-boot-hello-build-pipeline)으로 이동합니다.
파이프라인 마지막에 Stage를 추가합니다.
Trigger Deployment 유형을 선택합니다.
설정한 Deployment Pipeline을 지정합니다.
전체 흐름이 완료되었습니다.
Trigger 설정하기
지금 까지는 테스트를 하기 위해 Build Pipeline에서 Start Manual Run을 통해 시작하였습니다. 실제로는 개발자가 코드를 코드 저장소에 반영이 될 때 자동으로 빌드, 배포 파이프라인이 동작할 필요가 있습니다. Trigger는 코드 저장소에 발생한 이벤트를 통해 빌드 파이프라인을 시작하게 하는 역할을 하게 됩니다.
my-devops-project 프로젝트 페이지로 이동하여 왼쪽 메뉴의 Trigger로 이동합니다.
Create trigger을 클릭합니다.
Trigger를 설정합니다.
- Name: 예) spring-boot-hello-trigger
- Source Code Repository: OCI Code Repository, GitHub, GitLab 연동을 지원하며, 예제에서는 앞서 만든 OCI Code Repository상의 spring-boot-hello-repo를 선택합니다.
- Actions: 트리거링 되었을 때 호출하는 액션으로 작성한 빌드 파이프라인인 spring-boot-hello-build-pipeline을 선택합니다.
설정이 완료되었습니다.
테스트
Trigger에서 지정한 spring-boot-hello 소스 코드에 임의의 변경사항을 발생시키고 Code Repository에 반영합니다.
저는 Application.java에 있는 응답메시지를 “Hello OCI DevOps"로 변경하고 반영하셨습니다.
빌드 실행 내역을 보면, 그림과 같이 Trigger 된것은 Commit ID가 함께 보이며, Code Repository와 링크되어 있습니다.
Commit ID를 클릭하면 Code Repository상의 코드 변경 분을 확인할 수 있습니다.
빌드 파이프라인이 정상적으로 코드 빌드 부터 컨테이너 이미지 생성, 배포 파이프라인 호출까지 실행되었습니다.
배포 파이프라인도 정상 실행되었습니다.
OKE 클러스터를 조회해 보면 정상 배포 되었습니다.
oke_admin@cloudshell:~ (ap-seoul-1)$ kubectl get all NAME READY STATUS RESTARTS AGE pod/spring-boot-hello-54848fcfd5-5jpxh 1/1 Running 0 5m39s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 16h service/spring-boot-hello-service LoadBalancer 10.96.186.158 146.56.186.172 80:32224/TCP 41m NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/spring-boot-hello 1/1 1 1 15h NAME DESIRED CURRENT READY AGE replicaset.apps/spring-boot-hello-54557d9c47 0 0 0 41m replicaset.apps/spring-boot-hello-54848fcfd5 1 1 1 5m39s
서비스 주소로 접속시 정상 동작을 확인할 수 있습니다.
이 글은 개인으로서, 개인의 시간을 할애하여 작성된 글입니다. 글의 내용에 오류가 있을 수 있으며, 글 속의 의견은 개인적인 의견입니다.