문제

Actions에서 도커 이미지를 빌드하여 AWS의 ECR에 업로드 하기위해 하던 작업에는

기존에 AWS의 Access key와 secret을 github secrets에 저장해두고 사용하던 방식이 있지요.

해당 액션은 아래처럼 github secret에 IAM에서 생성한 유저의 액세스키를 등록하여 사용하였지요.

...
    steps:
    - name: Checkout
      uses: actions/checkout@v3

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v2
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: ap-northeast-2

    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v2

    - name: Build, tag, and push the image to Amazon ECR
      run: |
        TAG=$(git rev-parse --short HEAD)
        echo "TAG=${TAG}" >> $GITHUB_ENV
...

AWS Console에서는 해당 액세스 키를 위해 별도의 유저를 만들고 해당 유저의 역할에 맞는 권한만 부여해서 사용했지요.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecr:GetAuthorizationToken",
                "ecr:BatchCheckLayerAvailability",
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchGetImage",
                "ecr:InitiateLayerUpload",
                "ecr:UploadLayerPart",
                "ecr:CompleteLayerUpload",
                "ecr:PutImage"
            ],
            "Resource": "*"
        }
    ]
}

기존 방식대로 해도 문제는 되지 않지요, 하지만 관리적인 요소가 필요할뿐

유출되었을때의 문제점, 그리고 revoke이후의 재등록 과정이 귀찮을 수 있지요.

방향

이걸 해결 하기 위한 방법이 OIDC(링크)를 활용한 방법이지요.

자격 증명이 긴 시간의 매체 대신, 그때그때 짧은 시간동안만(설정에따라 기본값 3600초) 유효한 자격을 얻어 AWS에 액세스 하는 방식이지요.

그럼 어떻게 하는지 한번 진행해 봅시다.

진행

해당 내용은 위 링크에도 잘 나와있지만, 익숙하지 않다면, 조금 불편할수도 있기에 아래 이미지를 잘 보시지요.

IAM > Access management > Identity providers로 이동하여 Add provider를 클릭

 

 

그리고 아래와 같이 입력을 후

Get thumbprint 클릭

Add provider 클릭 -> 저장

 

Thumbprint는?

이는 SSL/TLS 인증성의 지문 같은거라고 보면 되지요, AWS에서는 이 값을 사용해서 응답에 대한 신뢰성을 검증 하지요.

이 값이 일치하지 않으면 요청은 거절되지요.

이걸 구하는 방법은 openssl로 해서 aws-cli로 등록하는 방식도 있는데, 복잡해서, 이렇게 클릭 몇번으로 해결하지요.

 

 

IAM > Policies > Create policy 클릭

기존에 액션에서 사용하던 사용자의 권한정책을 그대로 가져오지요.

그리고 Next를 눌러 적당한 정책 이름(github-action-policy)을 넣어주고 생성하지요

 

AWS에서는 마지막으로 IAM > Roles > Create role 클릭

Web Identity를 선택

공급자를 아까 등록했던 공급자로 지정하지요.

조직과 레포, 브랜치는 해당 조직의 모든걸 허용한다는 전제로 아래처럼

조직 or 개인계정을 써주고

이하 *을 작성합니다.

그리고 Next

아까 미리 만들어준 정책을 주입하고 Next

 

Role name을 적당히 작성해주고

신뢰요소를 확인하지요.

자 여기서 신뢰요소는 수정할 필요가 있지요!

조건이 StringEquals가 아니라, StringLike로 해야하지요

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::xxxxxxxxx:oidc-provider/token.actions.githubusercontent.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringLike": {
                    "token.actions.githubusercontent.com:sub": [
                        "repo:org or github id/*:*"
                    ]
                },
                "StringEquals": {
                    "token.actions.githubusercontent.com:aud": [
                        "sts.amazonaws.com"
                    ]
                }
            }
        }
    ]
}

 

그럼 이제 AWS 에서 할 작업은 끝났지요.

이제 Actions를 수정할 차례 이지요.

 

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v2
      with:
        role-to-assume: arn:aws:iam::xxxxxxxx:role/github-action-role
        aws-region: ap-northeast-2

    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v2

    # - name: Configure AWS credentials
    #   uses: aws-actions/configure-aws-credentials@v2
    #   with:
    #     aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    #     aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    #     aws-region: ap-northeast-2
        
    # - name: Login to Amazon ECR
    #   id: login-ecr
    #   uses: aws-actions/amazon-ecr-login@v2

 

그리고 액션을 실행하면

끝! 이지요

 

 

주의할점

fork 된 레포에서는 작동하지 않지요.

그리고 이글(링크)에서 보면 나오듯이 포크에 대한 악용 문제도 나오니 참고하세요.

role-to-assume에 할당하는 arn을 시크릿에 보관하면 해결가능!

https://school.programmers.co.kr/learn/courses/30/lessons/131534

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

-- 코드를 입력하세요 

select a.YEAR
     , a.MONTH * 1 as MONTH
     , count(a.USER_ID) as PUCHASED_USERS
     , round(count(a.USER_ID)/(select count(u.USER_ID) from USER_INFO u where date_format(u.JOINED, '%Y') = '2021'), 1) as PUCHASED_RATIO
  from (
        SELECT distinct b.USER_ID
             , date_format(b.SALES_DATE, '%Y') YEAR
             , date_format(b.SALES_DATE, '%m') MONTH
             , date_format(b.SALES_DATE, '%Y-%m') YM
          from ONLINE_SALE b
         inner join USER_INFO u
            on b.USER_ID = u.USER_ID
           and date_format(u.JOINED, '%Y') = '2021'
       ) a
 group by a.YM
 order by YEAR, MONTH

# select a.YEAR
#      , a.MONTH * 1 as MONTH
#      , count(distinct a.USER_ID) as PUCHASED_USERS
#      , round(count(distinct a.USER_ID)/count(distinct b.USER_ID), 1) as PUCHASED_RATIO
#   from (
#         SELECT distinct b.USER_ID
#              , date_format(b.SALES_DATE, '%Y') YEAR
#              , date_format(b.SALES_DATE, '%m') MONTH
#              , date_format(b.SALES_DATE, '%Y-%m') YM
#           from ONLINE_SALE b
#        ) a
#  inner join USER_INFO b
#     on date_format(b.JOINED, '%Y-%m') <= a.YM
#    and b.GENDER is not null
#  group by a.YM
#  order by YEAR, MONTH

 

문제 제대로 안읽음;;

이게 레벨 5던데, 문제 자체가 그냥 헷갈림...내가 대충 읽은건가;;

첨에 아래 쿼리 만들었는데,,

아래 쿼리는 (매월 구매회원 / 매월 회원수)을 구하는거임...아 이정도 되니까 레벨5 구나 싶었음.

근데 아무리 돌려도 정답이 아니라길래;;

뭐 2021년 회원 중...이라는;; 이걸 읽지도 않고, 반올림 하는것도 안읽고;;

 

위 쿼리에선 스칼라서브쿼리를 사용하였다, 2021년 이라는 데이터에 대해서 반복적으로 가져올테니, 매 행마다 실행하는게 아니라,

한번 실행하고 메모리에 올려두고 재사용할테니 요게 더 빠를 거라 판단,

 

아래 쿼리의 경우 매년 매월 이라는 그나마 좀 다양한 경우의 수가 있기에, 범위가 커질수록 스칼라서브쿼리가 불리할거라 판단했고,

조인을 통해 가져오는 영역은 count(distinct b.USER_ID) 뿐일거라, 범위의 데이터를 통으로 가져오는게 아니라, 저걸 위한 데이터를 알아서 옵티마이저가 잘 연산해 주겠지 하는 막연한 믿음으로 작성한 쿼리,

동등 조인이 아니기에 일반적인 상황에선 쓰일수 없는 쿼리;

https://school.programmers.co.kr/learn/courses/30/lessons/131532

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

-- 코드를 입력하세요
SELECT date_format(b.SALES_DATE, '%Y') as YEAR
     # , cast(date_format(b.SALES_DATE, '%m') as unsigned) as MONTH
     , date_format(b.SALES_DATE, '%m')*1 as MONTH
     , a.GENDER
     , count(distinct a.USER_ID)as USERS
  from USER_INFO a
 inner join ONLINE_SALE b
    on a.USER_ID = b.USER_ID
 where a.GENDER is not null
 group by date_format(b.SALES_DATE, '%Y%m'), a.GENDER
 order by YEAR, MONTH, GENDER, USERS

 

프로그래머스 쿼리 문제가 난도가 낮은편 인거 같다

https://school.programmers.co.kr/learn/courses/30/lessons/131116

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

select r.CATEGORY
     , r.PRICE as MAX_PRICE
     , r.PRODUCT_NAME
  from (
        select @rank := if(@cate = t.CATEGORY, @rank + 1, 1) as num
             , @cate := t.CATEGORY
             , t.CATEGORY
             , t.PRICE
             , t.PRODUCT_NAME
          from (
                SELECT a.PRODUCT_ID
                     , a.PRODUCT_NAME
                     , a.PRODUCT_CD
                     , a.CATEGORY
                     , a.PRICE
                  from FOOD_PRODUCT a
                 where a.CATEGORY in ('과자', '국', '김치', '식용유')
                 order by a.CATEGORY, a.PRICE desc
               ) t
       ) r
  where r.num = 1
 order by r.PRICE desc
 
 
 select t.CATEGORY
     , t.PRICE as MAX_PRICE
     , t.PRODUCT_NAME
  from (
        SELECT a.PRODUCT_ID
             , a.PRODUCT_NAME
             , a.PRODUCT_CD
             , a.CATEGORY
             , a.PRICE
             , rank() over(partition by a.CATEGORY order by max(a.PRICE) desc) as r
          from FOOD_PRODUCT a
         where a.CATEGORY in ('과자', '국', '김치', '식용유')
         group by a.PRODUCT_ID
       ) t
 where t.r = 1
 order by t.PRICE desc

 

 

두가지 버전,

MYSQL 8부터 오라클 처럼 partition by를 지원하는 윈도우 함수가 가능하다. 핵 편함.

 

하지만, 5.7까지는 안되니깐, 여기선 변수를 사용한 처리가 가능하다.(자료구조상 사이즈 1개 짜리 큐를 이용하는 느낌?)

 

 

https://school.programmers.co.kr/learn/courses/30/lessons/131537

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

SELECT left(SALES_DATE, 10) as SALES_DATE
     , PRODUCT_ID
     , USER_ID
     , SALES_AMOUNT
  from ONLINE_SALE A
 where left(SALES_DATE, 7) = '2022-03'
 UNION ALL
SELECT left(SALES_DATE, 10) as SALES_DATE
     , PRODUCT_ID
     , null
     , SALES_AMOUNT
  from OFFLINE_SALE A
 where left(SALES_DATE, 7) = '2022-03'
 order by SALES_DATE, PRODUCT_ID, USER_ID

SELECT date_format(SALES_DATE, '%Y-%m-%d') as SALES_DATE
     , PRODUCT_ID
     , USER_ID
     , SALES_AMOUNT
  from ONLINE_SALE A
 where SALES_DATE between date('2022-03-01') and date('2022-03-31')
 UNION ALL
SELECT date_format(SALES_DATE, '%Y-%m-%d') as SALES_DATE
     , PRODUCT_ID
     , null
     , SALES_AMOUNT
  from OFFLINE_SALE A
 where SALES_DATE between date('2022-03-01') and date('2022-03-31')
 order by SALES_DATE, PRODUCT_ID, USER_ID

 

하나는 스트링 함수로 하나는 데이트 함수로

일단 너무 궁금했고, 물리적으로나 논리적으로 최대값이 있지 않을까? 라는 점에서 출발한 질문이다.

답은 없다. 각 MySQL 버전 또는 OS 환경에 따라 달라진다.

 

InnoDB에서 별다른 설정없이 페이지의 크기는 기본값 16KB다.

그렇다면 일반적으로 단일 레코드 기준 최대값은 페이지 크기의 절반인, 8KB가 되겠다.

 

단, 페이지 크기를 초과하는 레코드도 있긴한다. 그런 경우 알아서 잘 여러 페이지로 분할한다는데,

그건 난 모르겠고, 중요한건 그럴경우 당연하게도 오버헤드가 발생할거고, 느려지겠지?

 

자 여튼 그리고 행의 수를 결정하는건 "테이블스페이스 / 페이지크기" 인데

테이블스페이스는 8 기준 144TB, 5.7 기준 64TB 라고 한다. 

 

와 그럼 대충 계산해 보면 어마어마한양의 레코드가 입력 가능하다는 거다.

(앗, 근데 다중 테이블스페이스도 가능하다는데, 그럼 어떻게 되는거지?!)

 

물론 실제로 저걸 다 쓴다면 매우 느리겠지만, 그러니까 파티셔닝도 필요하고...또...여기까지(아는게..)

 

페이지가 뭔지 모른다면?!

페이지는 테이블보다 작은 단위, 페이지가 모여서 테이블이 된다고 생각하면 됨.

레코드보다는 큰 단위 혹은 같거나.

레코드나 테이블(물론 MySQL은 테이블이 파일 단위이긴 하지만, 테이블스페이스)이 논리적 단위라면, 페이지는 물리적 단위 이다.

 

그냥 문득 궁금해서 찾아본건데, 나름 지식을 얻은거 같아서 기분 좋음.

왜 이게 궁금했는지는 기억 안남.

 

질문에 대한 답은 (지)PT샘이 해줬음.

정말 이름 처럼 지식을 퍼스널 트레이닝 시켜주는 분 인거 같다.

알면서 귀찮아서 안했던 작업

 

AWS쓰면 CodeDeploy 통해서 그나마 쉽게 할 수 있을것 같은데,

젠킨스 써도 쉽게 할 수 있겠지?

 

젠킨스에서 쉽게 하는 방법 저는 잘 몰라용.

그냥 서버 여러군데 배포할때 한방에 하는 방법

여러개 돌아가며 누르는것도 괜찮지만, 그것마저 귀찮은게 사람이니까.

특히나, L4 스위칭까지 하려면 여간 귀찮은게.

 

물론 git에서 웹훅 날려서 하는 방법도 있긴한데, 막 모델이나 데이터 같은거 변경되었을때 이러면 위험하니까

클릭으로 하는 방법!

요거 하나 누르면 땡!

 

일단 현재 회사에서는 L4에서 특정 REST API의 상태값을 통해 연결할지 말지 여부를 결정짓는다.

그래서 해당 상태값을 "점검"으로 바꿔주면 해당 WAS로 연결은 끊기고 배포가 가능하다는 점.

 

젠킨스에서 보통 프로젝트 만들어서 하는 것과 과정은 유사하다.

  1. Git Clone
  2. 빌드
  3. 배포 및 실행

여러 WAS에 배포하기 위해 상태를 확인하고, 변경하는 작업이 추가로 들어가면 될것 같다.

 

젠킨스에서 제공해주는 파이프라인 스크립트는 두가지 방식으로 작성 할 수 있다.

  • 선언적 파이프라인 => 여기선 이걸로 진행한다. pipeline { } 이 감싸고 있음.
  • 스크립티드 파이프라인(노드) => 얘가 뭔가 간단해 보이는데, 담에 시간날때 시도해 봐야지, 다음 생에...

 

New Item 해서 Pipeline 선택하고 다음으로 넘어가면! Pipeline 란 하단에 Pipeline Syntax 라는 링크가 있다. 요 녀석을 눌러서 페이지에 가보면 파이프라인 스크립트 작성에 도움을 주는 툴이 있는걸 확인 할 수 있다. 제네레이터.

 

기본적은 스크립트는 위 처럼 자동으로 생성 할 수 있다.

  • agent
    • 스크립트를 실행할 에이전트를 지정하는데, 라벨로 지정이 가능하며, any로 하면 그냥 됨.
  • 구조
    • stages > stage > steps 이며, 별도로 jenkins에서 제공해주는, environment, tools 등을 사용 할 수 있다.
    • 이 부분은 Declarative Directive Generator 에서 생성 가능하다.
    • tools 같은게, 선언적 파이프라인과 스크립티드 파이프라인에서 쓰는 작성 법이 다르더라.
    • 각 스테이지의 이름은 알아보기 쉽게 작성하는게 좋긴하다.
    • Git Clone과 Publish on SSH에서 사용하는 Credential 관련된 정보는 사전에 등록해두면 제네레이터에서 사용 가능하다.
  • Git Clone 작성

제네레이터를 이용해 작성해 보자, 빈칸만 채우면 된다.

  • 빌드
    • 위와 마찬가지로 제네레이터를 이용해 tools의 Gradle을 사용하는 방법도 있지만, gradlew를 사용하면 더 편하다.
    • 보통 젠킨스가 설치된 서버 특성상 자바 버전도 여러개 일거고, 그런거 선택 자체를 gradlew 안에 넣어두면 더 용이하다.
  • 체크 및 스위칭 
    • 이건 CURL을 활용해서 하면 된다.
    • 결국 쉘 스크립트를 실행하는건데, 제네레이터의 script를 써서 쉘을 실행 할 수 있다.(groovy 문법이라고 한다. 얼핏 본거 같은데...)
    • 추가적으로 curl을 통해 얻어오는 Json값을 어떻게 가져올까 하고 grep 명령어 옵션을 엄청 찾았는데, jq 라는 간단한 커맨드 처리자가 있다. 대부분의 OS를 지원하고, 사용법도 간단하다. https://stedolan.github.io/jq/
script{         
    TOKEN = sh (
        script: '''
            curl -X POST "https://domain/user/sign-in" -H "Content-Type: application/json" -d '{"email":"email@account.com", "password":"xxxxxxxx", "remember": "false"}' | jq '.response.accessToken'
        ''',
        returnStdout: true
    ).trim()
}
  • 배포
    • 배포는 젠킨스의 publish on ssh를 사용했다.
    • 물론, 이것도 제네레이터를 사용해 작성 가능하다.
sshPublisher(
    publishers: 
        [
            sshPublisherDesc(
                configName: '설정된 SSH 접속 대상 서버 이름', 
                transfers: 
                    [
                        sshTransfer(
                            cleanRemote: false, 
                            excludes: '', 
                            execCommand: '''
                                            bash deploy.sh // 실행할 쉘 내용?
                                        ''', 
                            execTimeout: 120000, 
                            flatten: false, 
                            makeEmptyDirs: false, 
                            noDefaultExcludes: false, 
                            patternSeparator: '[, ]+', 
                            remoteDirectory: '파일을 업로드 할 폴더', 
                            remoteDirectorySDF: false, 
                            removePrefix: 'build/libs', // 제거할 프리픽스
                            sourceFiles: 'build/libs/result.jar' // 업로드할 파일
                        )
                    ], 
                usePromotionTimestamp: false, 
                useWorkspaceInPromotion: false, 
                verbose: true // 과정을 로그로 출력한다.
                )
        ]
    )

처음 해보면 어려울 수도 있겠지만, 단계적 절차를 밟아가며 수정 보완하며 작성하다보면 결국 잘 되기 마련이다.

이러한 파이프라인 코드를 Git에 올려두고 수정 & 사용 하는 것도 가능하다.

 

  • 작성 결과
pipeline {
    agent any

    stages {
        
        stage('git clone') {
            steps {
                git branch: '${branch}', credentialsId: '${credentialsId}', url: '${git_url}'
            }
        }
        
        stage('build') {
            steps{
                sh '''
                    echo build start
                    ./gradlew clean build
                    echo build end
                '''
            }
        }
        
        stage('get Token') {
            steps {
                script{
                    
                    TOKEN = sh (
                        script: '''
                            curl -X POST "https://domain/user/sign-in" -H "Content-Type: application/json" -d '{"email":"email@domain.com", "password":"xxxxxxxx", "remember": "false"}' | jq '.response.accessToken'
                        ''',
                        returnStdout: true
                    ).trim()
                    
                }
            }
        }
        
        stage('check status server1') {
            steps {
                script{ 
                    HTTP_CODE_GET = "200"
                    while(HTTP_CODE_GET == "200"){
                        
                        HTTP_CODE_POST = sh (
                            script: """
                                curl -X PATCH "http://server1ip/config/loadBalance/server1ip" -H "Content-Type: application/json" -H "authorization: Bearer $TOKEN" -o /dev/null -w "%{http_code}"
                            """,
                            returnStdout: true
                        ).trim()
                        
                        sleep 5
                        
                        HTTP_CODE_GET = sh (
                            script: '''
                                curl -o /dev/null -w "%{http_code}" "http://server1ip/config/isMaintenance"
                            ''',
                            returnStdout: true
                        ).trim()
                        
                        echo "${HTTP_CODE_GET} while"
                    }
                    
                    sleep 45
                        
                }
                echo "${HTTP_CODE_GET} break"
            }
        }
        
        stage('publish on ssh for server-1') {
            steps {
                sshPublisher(
                    publishers: 
                        [
                            sshPublisherDesc(
                                configName: '${Server1}', 
                                transfers: 
                                    [
                                        sshTransfer(
                                            cleanRemote: false, 
                                            excludes: '', 
                                            execCommand: '''
                                                            bash deploy.sh;
                                                        ''', 
                                            execTimeout: 120000, 
                                            flatten: false, 
                                            makeEmptyDirs: false, 
                                            noDefaultExcludes: false, 
                                            patternSeparator: '[, ]+', 
                                            remoteDirectory: '${remoteDir}', 
                                            remoteDirectorySDF: false, 
                                            removePrefix: 'build/libs', 
                                            sourceFiles: 'build/libs/deploy.jar'
                                        )
                                    ], 
                                usePromotionTimestamp: false, 
                                useWorkspaceInPromotion: false, 
                                verbose: true
                                )
                        ]
                    )
            }
        }
        
        stage('resotre status server1') {
            steps {
                script{ 
                    sleep 60
                    
                    while(HTTP_CODE_GET != "200"){
                        
                        HTTP_CODE_POST = sh (
                            script: """
                                curl -X PATCH "http://server1ip/config/loadBalance/server1ip" -H "Content-Type: application/json" -H "authorization: Bearer $TOKEN" -o /dev/null -w "%{http_code}"
                            """,
                            returnStdout: true
                        ).trim()
                        
                        sleep 5
                        
                        HTTP_CODE_GET = sh (
                            script: '''
                                curl -o /dev/null -w "%{http_code}" "http://server1ip/config/isMaintenance"
                            ''',
                            returnStdout: true
                        ).trim()
                        
                        echo "${HTTP_CODE_GET} while"
                    }
                    
                    
                }
            }
        }
        
        
        stage('check status server2') {
            steps {
                script{ 
                    HTTP_CODE_GET = "200"
                    while(HTTP_CODE_GET == "200"){
                        
                        HTTP_CODE_POST = sh (
                            script: """
                                curl -X PATCH "http://server2ip/config/loadBalance/server2ip" -H "Content-Type: application/json" -H "authorization: Bearer $TOKEN" -o /dev/null -w "%{http_code}"
                            """,
                            returnStdout: true
                        ).trim()
                        
                        sleep 5
                        
                        HTTP_CODE_GET = sh (
                            script: '''
                                curl -o /dev/null -w "%{http_code}" "http://server2ip/config/isMaintenance"
                            ''',
                            returnStdout: true
                        ).trim()
                        
                        echo "${HTTP_CODE_GET} while"
                    }
                    
                    sleep 45
                        
                }
                echo "${HTTP_CODE_GET} break"
            }
        }
        
        stage('publish on ssh for api-2') {
            steps {
                sshPublisher(
                    publishers: 
                        [
                            sshPublisherDesc(
                                configName: 'server2', 
                                transfers: 
                                    [
                                        sshTransfer(
                                            cleanRemote: false, 
                                            excludes: '', 
                                            execCommand: '''
                                                bash deploy.sh;
                                                ''', 
                                            execTimeout: 120000, 
                                            flatten: false, 
                                            makeEmptyDirs: false, 
                                            noDefaultExcludes: false, 
                                            patternSeparator: '[, ]+', 
                                            remoteDirectory: '${remoteDir}', 
                                            remoteDirectorySDF: false, 
                                            removePrefix: 'build/libs', 
                                            sourceFiles: 'build/libs/deploy.jar'
                                            )
                                    ], 
                                usePromotionTimestamp: false, 
                                useWorkspaceInPromotion: false, 
                                verbose: true
                            )
                        ]
                )
            }
        }
        
        stage('resotre status server2') {
            steps {
                script{ 
                    sleep 60
                    
                    while(HTTP_CODE_GET != "200"){
                        
                        HTTP_CODE_POST = sh (
                            script: """
                                curl -X PATCH "http://server2ip/config/loadBalance/server2ip" -H "Content-Type: application/json" -H "authorization: Bearer $TOKEN" -o /dev/null -w "%{http_code}"
                            """,
                            returnStdout: true
                        ).trim()
                        
                        sleep 5
                        
                        HTTP_CODE_GET = sh (
                            script: '''
                                curl -o /dev/null -w "%{http_code}" "http://server2ip/config/isMaintenance"
                            ''',
                            returnStdout: true
                        ).trim()
                        
                        echo "${HTTP_CODE_GET} while"
                    }
                    
                    
                }
            }
        }
    }
}
$ find . -type f|xargs grep -r --color=auto -i jenkins

형식이 파일인 것중 내용에 jenkins라는 문자열을 포함하고 있는걸 토해내라... 

'서버 > 리눅스' 카테고리의 다른 글

Ubuntu 14, 16 tomcat service 등록  (0) 2019.09.20
Ubuntu 계정 생성 및 sudo 권한 부여  (0) 2019.04.05

docker compose

 

version: '3.7'

services:
    mariadb:
        image: mariadb
        ports:
            - "3306:3306"
        volumes:
            - ./db/conf.d:/etc/mysql/conf.d
            - ./db/data:/var/lib/mysql
            - ./db/initdb.d:/docker-entrypoint-initdb.d
        environment:
            MYSQL_HOST: localhost
            MYSQL_ROOT_PASSWORD: rootpw
            MYSQL_ROOT_HOST: '%'
            TZ: Asia/Seoul
        networks:
            - default
        restart: always
        stdin_open: true
        tty: true
    redis:
        image: redis
        command: redis-server --requirepass redispw --port 6379
        container_name: redis6379
        hostname: redis6379
        labels:
            - "name=redis"
            - "mode=standalone"
        ports:
            - "6379:6379"
        networks:
            - default
        volumes:
            - ./redis:/data
        restart: always
        stdin_open: true
        tty: true
    rabbitmq:
        image: 'rabbitmq'
        container_name: rabbitmq-stream
        ports:
            - "5672:5672"
            - "15672:15672"
        networks:
            - default
        volumes:
            - ./rabbitmq/data:/var/lib/rabbitmq
            - ./rabbitmq/log:/var/log/rabbitmq
        environment:
            RABBITMQ_ERLANG_COOKIE: "RabbitMQ-My-Cookies"
            RABBITMQ_DEFAULT_USER: "admin"
            RABBITMQ_DEFAULT_PASS: "adminpw"
        restart: always
        stdin_open: true
        tty: true
    elasticsearch:
        container_name: elasticsearch
        image: elasticsearch:8.0.0
        ports:
            - "9200:9200"
            - "9300:9300"
        networks:
            - default
        volumes:
            - ./elasticsearch:/usr/share/elasticsearch/data
        environment:
            - ELASTIC_USERNAME=elastic
            - ELASTIC_PASSWORD=elasticpw
            - xpack.security.enabled=false
            - xpack.security.transport.ssl.enabled=false
            - http.cors.enabled=true
            - http.cors.allow-origin=*
            - discovery.type=single-node
            - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
            - TZ=Asia/Seoul
        restart: always
        privileged: true
        stdin_open: true
        tty: true
    kibana:
        container_name: kibana
        image: kibana:8.0.0
        ports:
            - "5601:5601"
        networks:
            - default
        environment:
            ELASTICSERCH_UTL: http://elasticsearch:9200
            ELASTICSEARCH_HOSTS: http://elasticsearch:9200
            TZ: Asia/Seoul
        restart: always
        privileged: true
        stdin_open: true
        tty: true

networks:
    default:
        external:
            name: local-env-network
# docker network create local-env-network
# 컨테이너ID 확인
$ docker ps

# 해당 컨테이너 bash 실행
docker exec -it -u root ${CONTAINER ID} /bin/bash

 

+ Recent posts