반응형

 

* 결론: '=='는 '값'만 비교하는 반면, '==='는 '값' 및 '자료형'을 동시에 비교하는 더 strict한 연산자이다.

구글링 중에 설명이 정말 잘 되어 있는 블로그가 있어서 내용을 그대로 가져왔다. (fileoscoder님 감사합니다!)

출처: https://velog.io/@filoscoder/-%EC%99%80-%EC%9D%98-%EC%B0%A8%EC%9D%B4-oak1091tes

  • ☝ '==' 연산자를 이용하여 서로 다른 유형의 두 변수의 [값] 비교
  • ✌ '==='는 엄격한 비교를 하는 것으로 알려져 있다 ([값 & 자료형] -> true).

 

 

🔍 간단 비교

✔ 0값은 false와 동일하므로 -> true 출력

0 == false // true 

✔ 두 피연산자의 유형이 다르기 때문에 ->false

0 === false // expected output: false 
console.log(typeof 0); 	// expected output: "number"
console.log(typeof false); // expected output: "boolean"

 

🔍 숫자와 문자열 비교

✔ 자동 유형변화 비교

2 == "2" // expected output: true

✔ 두 피연산자의 유형이 다르기 때문에 ->false

2 === "2" // expected output: false 
console.log(typeof 2);	// expected output: "number"
console.log(typeof "2");  // expected output: "string"

 

🤔 #궁굼한 케이스들

🔍 1) null 와 undefined 비교했을 때 어떤 결과가 나올 것인가?

✔ 자동 유형변화 비교

null == undefined // expected output: true

✔ 두 피연산자의 유형이 다르기 때문에 ->false

null == undefined // expected output: false 
console.log(typeof null);	// expected output: "object"
console.log(typeof undefined);  // expected output: "undefined"

 

 

🕺 변수를 비교하거나 어떤 비교를 위해 항상 '===' 연산자를 사용 할 것을 권장한다.

💃 가능한 '==' 연산자를 사용하지 않도록 하고, 대신 직접 자료형을 변환하여(casting) 보다 코드 가독성을 높이도록 한다.

 

 

 

자바스크립트: '==' 와 '===' 는 다르다!

무엇이 다른가? 저는 자바 언어를 먼저 배운 입장에서 자바스크립트에 깊게 노출되지 않았기 때문에 충격을 받았다. 비록 자바스크립트의 어떤 기능, 사건 처리, 그리고 어떤 jQuery 속임수에 상

velog.io

반응형
반응형

이전 포스팅에서, cloudwatch에서 알람이 발생할 때마다 SNS, 그리고 lambda를 trigger해서 slack으로 알람을 보냈다.

* 이전 포스팅:

https://countrymouse.tistory.com/entry/awscloudwatchslack

 

[AWS] cloudwatch, lambda를 통해 slack으로 알람 보내기 (Nodejs)

Cloudwatch에서 알람 조건을 만들어서, 해당 알람이 발생할 때 slack으로 notify하는 환경을 만들어보자. 1. AWS > CloudWatch > 경보 생성 1) 지표 선택 2) 지표에서 원하는 tracking값 및 임계치 선택 (알람..

countrymouse.tistory.com

 

그런데, alarm으로 받는 text의 포맷이 아래와 같이 가독성이 떨어진다. 그래서, 보다 가독성 있게 수정해보기로 한다.

 

위 가독성 없는 텍스트를 아래 포맷으로 수정한다.

 

코드는 아래 티스토리 블로거 향로님의 코드를 사용하였고 아래는 한줄한줄 해석을 달아보았다. 

(직접 하나씩 만들어보려고 했는데, 반나절의 삽질을 해도 원하는 대로 잘 안돼서 다른 분의 코드를 사용하기로 했다...)

https://jojoldu.tistory.com/586

 

CloudWatch 이상 지표를 슬랙 알람으로 받기 (feat. SNS, Lambda)

AWS 서비스를 이용하면 CloudWatch를 통해 서비스의 이상 지표를 손쉽게 확인할 수 있습니다. 이를테면 다음과 같은 경우인데요. 평소보다 로드밸런서로 들어오는 요청양이 2배이상 높다거나 RDS의 C

jojoldu.tistory.com

 

향로님 코드에 cause부분과 관련된 코드는 삭제하였고(나는 '원인'을 '설명'으로 대체했다), 일부분에 주석을 추가하였다.

// 구성 -> 환경변수로 webhook을 받도록 합니다.
const ENV = process.env
if (!ENV.webhook) throw new Error('Missing environment variable: webhook')

const webhook = ENV.webhook;
const https = require('https')

const statusColorsAndMessage = {
    ALARM: {"color": "danger", "message":"위험"},
    INSUFFICIENT_DATA: {"color": "warning", "message":"데이터 부족"},
    OK: {"color": "good", "message":"정상"}
}


// 1. 여기서부터 시작 
exports.handler = async (event) => {
    await exports.processEvent(event);
}


// 2. processEvent로 넘어간다.
exports.processEvent = async (event) => {
    console.log('Event:', JSON.stringify(event))
    const snsMessage = event.Records[0].Sns.Message;
    console.log('SNS Message:', snsMessage);
    const postData = exports.buildSlackMessage(JSON.parse(snsMessage))  // 3. buildSlackMessage
    await exports.postSlack(postData, webhook);
}

// snsMessage는 이전 포스팅 값들이 담겨 있는데, 참고해보자.


// 3. buildSlackMessage(위 jane_bot2에서 출력한 snsMessage를 받아와서 각각 처리한다.)
exports.buildSlackMessage = (data) => {
    const newState = statusColorsAndMessage[data.NewStateValue];  // 위 jane_bot2 기준 ALARM
    const oldState = statusColorsAndMessage[data.OldStateValue];     // 위 jane_bot2 기준 OK
    const executeTime = exports.toYyyymmddhhmmss(data.StateChangeTime); // 2022-05-23T03:16:02.297+0000
    const description = data.NewStateReason;  // 기존 코드엔 description, cause가 둘 다 있지만 나는 description에 대체했다.
    return {
        attachments: [
            {
                title: `[${data.AlarmName}]`,
                color: newState.color,
                fields: [
                    {
                        title: 'Timestamp',
                        value: executeTime
                    },
                    {
                        title: 'Alarm',
                        value: description
                    },
                    {
                        title: 'Previous Status',
                        value: oldState.message,
                        short: true
                    },
                    {
                        title: 'Current Status',
                        value: `*${newState.message}*`,
                        short: true
                    },
                    {
                        title: 'Link',
                        value: exports.createLink(data)
                    }
                ]
            }
        ]
    }
}

// CloudWatch 알람 바로 가기 링크
exports.createLink = (data) => {
    return `https://console.aws.amazon.com/cloudwatch/home?region=${exports.exportRegionCode(data.AlarmArn)}#alarm:alarmFilter=ANY;name=${encodeURIComponent(data.AlarmName)}`;
}

exports.exportRegionCode = (arn) => {
    return  arn.replace("arn:aws:cloudwatch:", "").split(":")[0];
}


// 타임존 UTC -> KST (2022-05-23T03:16:02.297+0000 --> 2022-05-23 13:14:48)
exports.toYyyymmddhhmmss = (timeString) => {

    if(!timeString){
        return '';
    }

    const kstDate = new Date(new Date(timeString).getTime() + 32400000);

    function pad2(n) { return n < 10 ? '0' + n : n }

    return kstDate.getFullYear().toString()
        + '-'+ pad2(kstDate.getMonth() + 1)
        + '-'+ pad2(kstDate.getDate())
        + ' '+ pad2(kstDate.getHours())
        + ':'+ pad2(kstDate.getMinutes())
        + ':'+ pad2(kstDate.getSeconds());
}  

exports.postSlack = async (message, slackUrl) => {
    return await request(exports.options(slackUrl), message);
}

exports.options = (slackUrl) => {
    const {host, pathname} = new URL(slackUrl);
    return {
        hostname: host,
        path: pathname,
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
    };
}

function request(options, data) {

    return new Promise((resolve, reject) => {
        const req = https.request(options, (res) => {
            res.setEncoding('utf8');
            let responseBody = '';

            res.on('data', (chunk) => {
                responseBody += chunk;
            });

            res.on('end', () => {
                resolve(responseBody);
            });
        });

        req.on('error', (err) => {
            console.error(err);
            reject(err);
        });

        req.write(JSON.stringify(data));
        req.end();
    });
}

 

완성!

반응형
반응형

Cloudwatch에서 알람 조건을 만들어서, 해당 알람이 발생할 때 slack으로 notify하는 환경을 만들어보자.

 

 

1. AWS > CloudWatch > 경보 생성

1) 지표 선택

 

2) 지표에서 원하는 tracking값 및 임계치 선택 (알람 발생을 위해 임계값은 1로 설정)

 

3) SNS 연동 (이전 포스팅에서 만든 jane-test-sns2)

 - 이전 포스팅: https://countrymouse.tistory.com/entry/awsslack

 

[AWS] Lambda와 slack 연동하기 (Nodejs)

1. slack에서 app을 생성한다.  1) slack에 접속하여 메뉴 중 앱 > 앱 디렉터리를 클릭하면 된다. https://api.slack.com/apps?new_app=1 2) 그럼 아래 창이 나오는데, 오른쪽 상단의 '구축'을 누르면 된다. 3)..

countrymouse.tistory.com

+ 여기서 알림을 추가하고, 정상 상태에서도 알람이 발생하도록 정상 알림도 추가한다.

 

 

4) 이름 설정 후 미리보기, 경보 생성

5) 경보가 생성되었음. 데이터가 5분 주기므로 기다려보기.

 

 

2. SNS 앱 확인

 : 아까 cloudwatch에서 연동해둔 SNS(jane-test-sns2)를 보니 lambda가 구독되어 있다.

그렇다면, cloudwatch에 알람이 발생하면, SNS를 통해, Lambda가 트리거 될 것이다.

 

3. lambda의 index.json 코드

cloudwatch를 통해 event를 받을 때 어떤 값들이 수신되는지 확인해봤다. // console.log(message)

exports.handler = async (event, context) => {
    //console.log('Received event:', JSON.stringify(event, null, 2));
    const message = event.Records[0].Sns.Message;
    console.log('From SNS:', message);
    return message;
};

 

4. Slack

 (jane_bot2 is testing은 무시하시길... 테스트 했던 console log다.)

그럼 예상했던 대로, cloudwatch에서 생성한 알람이 발생할 때마다 SNS를 호출시키고, 그게 lambda를 호출시켜서, slack으로 온다.

EC2 -> Cloudwatch -> SNS -> Lambda -> slack 구조다.

 

그럼 다음 포스팅에서는 이 text들을 예쁘게 만들어보기로 한다. (삽질 엄청했다....)

반응형
반응형

1. slack에서 app을 생성한다.

 1) slack에 접속하여 메뉴 중 앱 > 앱 디렉터리를 클릭하면 된다.

https://api.slack.com/apps?new_app=1

 

 

2) 그럼 아래 창이 나오는데, 오른쪽 상단의 '구축'을 누르면 된다.

 

 

3) Create an app을 클릭한다.

 

 

4) From scratch 후 이름 및 workspace 생성

 

5) 기능추가: Incoming Webhooks 클릭.

 

6) Activate Incoming Webhooks을 On 설정한 후, 아래에 있는 Webhook URI에 워크스페이스 추가를 클릭한다.

 

 

7) 이 app이 동작될 채널을 선택한다.

 

8) 그럼 이렇게 webhook URL이 생겼고, 이건 이따가 index.js에서 사용한다.

 

9) jane_bot2가 #clone-prision에 초대되었다.

 

 

2. 자 다음, AWS로 이동한다.

1) Amazon SNS > 주제 > 주제생성

설정에서는 표준선택 및 이름 외 별도 설정할 게 없고, lambda에서 설정한다.

 

 

2) blueprint로 sns를 검색해서 lambda를 생성한다.

 

3) 아까 생성한 sns를 여기서 선택해준다. (jane-test-sns2) 

그리고 함수 생성.

 

4) 함수를 생성하면 이와 같이 출력되는데, 코드는 로컬에서 작성 후 zip파일으로 압축 해 업로드 해준다. 

코드 수정 전

 

 

5) 로컬에서 생성한 코드를 zip으로 압축해 업로드 했다.

slack-node를 설치해야하기 때문에 node_modules까지 가져와야 한다.

webhookUri는 아까 1 -> 8)에 있었던 Webhook URI를 그대로 복붙해주면 된다.

 

6) Test를 클릭하면 아까 설정한 워크스페이스 > 채널로 jane_bot2가 동작한다 :)

반응형
반응형

배경 설명

lambda에 S3를 연동시켜 두고, 해당 S3에 이벤트가 있을 때마다 람다를 실행시키는 코드를 해석해본다.

 

코드

const aws = require('aws-sdk');
const fs = require('fs');
var s3 = new aws.S3({ apiVersion: '2006-03-01' });


exports.handler = async(event, context,  callback) => {
    const bucket = event.Records[0].s3.bucket.name;

    console.log(event);   // 실행결과 테스트 1번
    console.log(event.Records);  // 실행결과 테스트 2번
    console.log(event.Records[0]);  // 실행결과 테스트 3번

    console.log(bucket); // 그럼 이건 .s3.bucket.name인 jane-bkt만 출력됨.
    const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' ')); // 여기부턴 실행결과 2로 이동
    const params = {
        Bucket: bucket,
        Key: key,
    };
    var theObject = await s3.getObject(params).promise();
    const data = JSON.parse(theObject.Body);
    console.log(data);
    // console.log(theObject.Body.toString('utf8'));  // 위와 결과값이 같지만, 보기 좋은 형태로
        try {
        const { ContentType } = await s3.getObject(params).promise();
        console.log('CONTENT TYPE:', ContentType);
        console.log('********** Event generated by S3 ************* ');
        return ContentType;
        
    } catch (err) {
        console.log(err);
        const message = `Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`;
        console.log(message);
        throw new Error(message);
    }
};

 

 

실행 결과 1

1) console.log(event)

Function Logs
START RequestId: a4ff7ef5-b647-40b7-9e6d-8eb2fb95360f Version: $LATEST
2022-05-22T02:23:56.937Z a4ff7ef5-b647-40b7-9e6d-8eb2fb95360f INFO {
  Records: [
    {
      eventVersion: '2.0',
      eventSource: 'aws:s3',
      awsRegion: 'us-east-1',
      eventTime: '1970-01-01T00:00:00.000Z',
      eventName: 'ObjectCreated:Put',
      userIdentity: [Object],
      requestParameters: [Object],
      responseElements: [Object],
      s3: [Object]
    }
  ]
}

 2) console.log(event.Records);  // 실행결과 테스트 2번. 좀 더 자세히 나온다.

Function Logs
START RequestId: 35e8439b-4ac0-44fa-a006-85a3fdd6d4aa Version: $LATEST
2022-05-22T02:27:20.107Z 35e8439b-4ac0-44fa-a006-85a3fdd6d4aa INFO [
  {
    eventVersion: '2.0',
    eventSource: 'aws:s3',
    awsRegion: 'us-east-1',
    eventTime: '1970-01-01T00:00:00.000Z',
    eventName: 'ObjectCreated:Put',
    userIdentity: { principalId: 'EXAMPLE' },
    requestParameters: { sourceIPAddress: '127.0.0.1' },
    responseElements: {
      'x-amz-request-id': 'EXAMPLE123456789',
      'x-amz-id-2': 'EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH'
    },
    s3: {
      s3SchemaVersion: '1.0',
      configurationId: 'testConfigRule',
      bucket: [Object],
      object: [Object]
    }
  }
]


 3) console.log(event.Records[0]);  // 테스트 3번. 테스팅 json에서 언급한 jane-bkt, s3test.json을 포함한 결과값이 나온다.

Function Logs
START RequestId: cda13909-6eab-410a-9582-dc00d25732f2 Version: $LATEST
2022-05-22T02:28:47.364Z cda13909-6eab-410a-9582-dc00d25732f2 INFO {
  eventVersion: '2.0',
  eventSource: 'aws:s3',
  awsRegion: 'us-east-1',
  eventTime: '1970-01-01T00:00:00.000Z',
  eventName: 'ObjectCreated:Put',
  userIdentity: { principalId: 'EXAMPLE' },
  requestParameters: { sourceIPAddress: '127.0.0.1' },
  responseElements: {
    'x-amz-request-id': 'EXAMPLE123456789',
    'x-amz-id-2': 'EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH'
  },
  s3: {
    s3SchemaVersion: '1.0',
    configurationId: 'testConfigRule',
    bucket: {
      name: 'jane-bkt',
      ownerIdentity: [Object],
      arn: 'arn:aws:s3:::jane-bkt'
    },
    object: {
      key: 's3test.json',
      size: 1024,
      eTag: '0123456789abcdef0123456789abcdef',
      sequencer: '0A1B2C3D4E5F678901'
    }
  }
}

 

 

 

다시 코드

   const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' ')); // 여기부턴 실행결과 2로 이동
   console.log(event.Records[0].s3.object.key.replace(/\+/g.''));  //  object.key에 +제거(/+) 및 전역으로 확장(/g) 

    const params = {
        Bucket: bucket,
        Key: key,
    };
    var theObject = await s3.getObject(params).promise();
    console.log(theObject);  // 결과 1)
    const data = JSON.parse(theObject.Body);
    console.log(data);
    // console.log(theObject.Body.toString('utf8'));  // 위와 결과값이 같지만 보기 좋은 형태로
        try {
        const { ContentType } = await s3.getObject(params).promise();
        console.log('CONTENT TYPE:', ContentType);
        console.log('********** Event generated by S3 ************* ');
        return ContentType;
        
    } catch (err) {
        console.log(err);
        const message = `Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`;
        console.log(message);
        throw new Error(message);
    }
};

 

실행결과 2

1)  console.log(theObject);  // 결과 1)

Function Logs
START RequestId: ed9a9525-9994-48da-8834-16ec0ab5c2d6 Version: $LATEST
2022-05-22T02:41:07.808Z ed9a9525-9994-48da-8834-16ec0ab5c2d6 INFO {
  AcceptRanges: 'bytes',
  LastModified: 2022-05-20T02:49:10.000Z,
  ContentLength: 80,
  ETag: '"5704c18c8279716da4b82c028dddb5d4"',
  ContentType: 'application/json',
  Metadata: {},
  Body: <Buffer 7b 0a 20 20 22 6e 61 6d 65 22 3a 20 22 4a 61 6e 65 22 2c 0a 20 20 22 61 67 65 22 3a 20 22 32 38 22 2c 0a 20 20 22 63 6f 6d 70 61 6e 79 22 3a 20 22 53 ... 30 more bytes> 
// 이 body를 아래 코드에서 JSON으로 파싱 후 출력함.
 const data = JSON.parse(theObject.Body);

}

 

Note

1) try - catch: 에러 핸들링 코드 (출처:https://ko.javascript.info/try-catch)

  1. try { } 안의 코드가 먼저 실행된다.
  2. 에러가 없다면, try 안의 마지막 줄까지 실행, catch 블록은 건너뜀
  3. 에러가 있다면, try 안 코드의 실행이 중단, catch(err) 블록으로 제어 흐름이 넘어감. 변수 err(아무 이름이나 사용 가능)는 무슨 일이 일어났는지에 대한 설명이 담긴 에러 객체를 포함. 
  try {
        const { ContentType } = await s3.getObject(params).promise();
        console.log('CONTENT TYPE:', ContentType);
        console.log('********** Event generated by S3 ************* ');
        return ContentType;
        
    } catch (err) {
        console.log(err);
        const message = `Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`;
        console.log(message);
        throw new Error(message);  // 에러 던지기.
    }
};

 

- event: 호출상황에 대한 정보.

- context: 실행되는 함수 환경에 대한 정보 

- callback: 응답을 전송하기 위한 함수. (Error 및 응답을 사용)

      -> 이벤트 루프가 비워질 때까지 기다린 다음, 응답이나 오류를 호출자에게 반환. 

- async: 비동기 핸들러를 sync 맞춰주기 위함. 

반응형
반응형

1. Trigger를 S3로 설정한 lambda를 만든다.

   (S3는 미리 만들어놔야 함)

   

1) Lambda > 함수생성 > 블루프린트 사용(aws에서 제공하는 템플릿) > s3 검색 > nodejs의 경우 s3-get-object

 

 2) 구성 누른 후 나머지 설정은 기존 사용하던 값으로 설정,

     S3 트리거에서 버킷을 만들어놓은 버킷으로 설정 + 이벤트 유형: 모든 객체 생성 이벤트

     // 이렇게 하면, 모든 객체 생성 이벤트(생성삭제수정 등)가 일어날 때마다 lambda 함수가 호출된다.

 

3) 만들어진 람다

 

2. index.js 코드

const aws = require('aws-sdk');
const fs = require('fs');
var s3 = new aws.S3({ apiVersion: '2006-03-01' });

exports.handler = async(event, context,  callback) => {
    const bucket = event.Records[0].s3.bucket.name;
    const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));
    const params = {
        Bucket: bucket,
        Key: key,
    };

// json 파일을 읽어와서 json 형태로 출력
    var theObject = await s3.getObject(params).promise();
    const data = JSON.parse(theObject.Body);
    console.log(data);
   // console.log(theObject.Body.toString('utf8'));  // 보기 예쁜 형태로
        

 

 // S3에서 이벤트가 발생할 때마다 로그에 찍어줄 값.

  try {
        const { ContentType } = await s3.getObject(params).promise();
        console.log('CONTENT TYPE:', ContentType);
        console.log('********** Event generated by S3 ************* ');
        return ContentType;
        
    } catch (err) {
        console.log(err);
        const message = `Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`;
        console.log(message);
        throw new Error(message);
    }
};

 

3. 람다의 테스트에서 awsRegion, bucket-name, arn, key를 바꾸어주어야 정상 테스팅됨.

람다 테스트

      "awsRegion": “ap-northeast-2",

      "s3": {

        "s3SchemaVersion": "1.0",

        "configurationId": "testConfigRule",

        "bucket": {

          "name": "jane-bkt”,

          "ownerIdentity": {

            "principalId": "EXAMPLE"

          },

          "arn": "arn:aws:s3:::jane-bkt"

        },

        "object": {

          "key": "s3test.json",

          "size": 1024,

          "eTag": "0123456789abcdef0123456789abcdef",

          "sequencer": "0A1B2C3D4E5F678901"

        }

 

 

4. 테스팅 결과

1) json 파일이 예쁘게 나온다.

2) S3의 해당 버킷에 새로운 파일을 업로드할 경우, 람다가 실행되어 Cloudwatch에서 로그를 확인할 수도 있다.

 

반응형
반응형

 iDRAC의 Password를 쉽게 변경한다.

jane:~$ ipmitool user list 1  
Could not open device at /dev/ipmi0 or /dev/ipmi/0 or /dev/ipmidev/0: No such file or directory
jane:~$ // 이 작업은 root 계정에서 진행해야 한다.
jane:~$ su -
Password:
jane:~#
jane:~# ipmitool user list 1
ID  Name             Callin  Link Auth  IPMI Msg   Channel Priv Limit
1                    true    false      false      NO ACCESS
2   root             true    true       true       ADMINISTRATOR
3                    true    false      false      NO ACCESS
4                    true    false      false      NO ACCESS
5                    true    false      false      NO ACCESS
6                    true    false      false      NO ACCESS
7                    true    false      false      NO ACCESS
8                    true    false      false      NO ACCESS
9                    true    false      false      NO ACCESS
10                   true    false      false      NO ACCESS
11                   true    false      false      NO ACCESS
12                   true    false      false      NO ACCESS
13                   true    false      false      NO ACCESS
14                   true    false      false      NO ACCESS
15                   true    false      false      NO ACCESS
16                   true    false      false      NO ACCESS
jane:~#
jane:~# ipmitool user set password 2 password_test  // ID=2의 password를 password_test로 바꾼다.

끝-!

반응형

'직장생활 > Linux, OS' 카테고리의 다른 글

LVM(Logical Volume Manager) 설명  (0) 2021.10.27
반응형

몇 달전 Prometheus를 설치했었다.

그런데 오랜만에 사용하려고 보니, ImagePullBackOff가 떠있는 것이다.

controller-0:~/Jane# kubectl get po -n jane-infra-monitoring
NAME READY STATUS RESTARTS AGE
alertmanager-jane-prometheus-kube-promet-alertmanager-0 2/2 Running 0 24m
jane-prometheus-grafana-5d7d5b55dd-hrdcn 0/3 ImagePullBackOff 0 4m59s
jane-prometheus-grafana-66b98448b8-ddb7d 3/3 Running 0 23m
jane-prometheus-kube-promet-operator-5579fff8bf-rbtcl 1/1 Running 0 23m
jane-prometheus-kube-promet-operator-6c84f4b45c-f2p89 0/1 ImagePullBackOff 0 4m59s
jane-prometheus-kube-state-metrics-59c698f9d6-zjl6f 1/1 Running 0 20m
jane-prometheus-prometheus-node-exporter-rbmgx 1/1 Running 0 10d
jane-prometheus-prometheus-node-exporter-szcnj 1/1 Running 0 157m
jane-prometheus-prometheus-node-exporter-twlwt 1/1 Running 0 24d
prometheus-jane-prometheus-kube-promet-prometheus-0 0/2 CrashLoopBackOff 0 4m51s

 

한 pod를 describe로 조회해보니 image가 없다고 한다.

controller-0:~/Jane# kubectl describe po -n jane-infra-monitoring jane-prometheus-grafana-5d7d5b55dd-hrdcn
Warning Failed 89s (x3 over 2m9s) kubelet, controller-1 Failed to pull image "registry.infra.jane.cluster.local:19092/quay.io/prometheus/prometheus:v2.33.1": rpc error: code = NotFound desc = failed to pull and unpack image "registry.infra.jane.cluster.local:19092/quay.io/prometheus/prometheus:v2.33.1": failed to resolve reference "registry.infra.jane.cluster.local:19092/quay.io/prometheus/prometheus:v2.33.1": registry.infra.jane.cluster.local:19092/quay.io/prometheus/prometheus:v2.33.1: not found

 

그래서 image를 다른 registry에서 가져오고, deployment에서 image에 맞는 tag값까지 수정해줬다.

(관련 포스팅은  https://countrymouse.tistory.com/entry/docker  )

 

 

그런데, 맨 밑 pod(prometheus-jane-prometheus-kube-promet-prometheus)는 deployment가 아닌 statefulset이였다.

그래서 이 또한 수정을 했는데,

controller-0:~$ kubectl edit statefulsets.apps -n dso-infra-monitoring prometheus-dso-prometheus-kube-promet-prometheus
statefulset.apps/prometheus-dso-prometheus-kube-promet-prometheus edited


노답. 수정하자마자 기존 image tag로 값이 원복되는 것이다.

그래서 pod가 안 산다.

 

 

해결책

결국 해결책은, cr(custom resource)에서 직접 tag값을 수정하는 것이었다.

1. statfulset owner 확인: Prometheus, dso-prometheus-kube-promet-prometheus

controller-0:~# kubectl get sts -n jane-infra-monitoring  prometheus-jane-prometheus-kube-promet-prometheus -oyaml
  ownerReferences:
  - apiVersion: monitoring.coreos.com/v1
    blockOwnerDeletion: true
    controller: true
    kind: Prometheus
    name: jane-prometheus-kube-promet-prometheus
    uid: fb999cb7-55e2-4756-8f74-eeeba6e943c1

 

2. cr 수정 : 이미지 tag 변경
# kubectl edit Prometheus  -n jane-infra-monitoring

...
spec:
  alerting:
    alertmanagers:
    - apiVersion: v2
      name: jane-prometheus-kube-promet-alertmanager
      namespace: jane-infra-monitoring
      pathPrefix: /
      port: http-web
  enableAdminAPI: false
  externalUrl: http://jane-prometheus-kube-promet-prometheus.jane-infra-monitoring:9090
  image: registry.infra.jane.cluster.local:19092/quay.io/prometheus/prometheus:v2.22.1
  imagePullSecrets:
  - name: registrykey
  listenLocal: false
  logFormat: logfmt
  logLevel: info
  paused: false
  podMonitorNamespaceSelector: {}
  podMonitorSelector:
    matchLabels:
      release: jane-prometheus
  portName: http-web
  probeNamespaceSelector: {}
  probeSelector:
    matchLabels:
      release: jane-prometheus
  replicas: 1
  retention: 10d
  routePrefix: /
  ruleNamespaceSelector: {}
  ruleSelector:
    matchLabels:
      release: jane-prometheus
  securityContext:
    fsGroup: 2000
    runAsGroup: 2000
    runAsNonRoot: true
    runAsUser: 1000
  serviceAccountName: jane-prometheus-kube-promet-prometheus
  serviceMonitorNamespaceSelector: {}
  serviceMonitorSelector:
    matchLabels:
      release: jane-prometheus
  shards: 1
  version: v2.22.1   // 여기서 변경

 

반응형
반응형

업무 중에 Prometheus 구동을 위한 이미지가 삭제되어 다시 불러오는 과정이 있었다.

정리해두면 유용할 것 같아 아래와 같이 정리했다 :)

 

 

1. 이미지 불러오기: docker pull (from harbor)
# docker pull harbor.jane.net/dependency/quay.io/prometheus/prometheus:v2.22.1


2. 이미지 저장(다운받은 image를 tar파일로 추출): docker save 
# docker save -o [파일명].tar [image id] // image id는 docker image list로 조회 (ex. 7cc97b58fb0e)

controller-0:~# docker image list | grep v2.22.1
harbor.jane.net/dependency/quay.io/prometheus/prometheus                                         v2.22.1                7cc97b58fb0e        17 months ago       168MB


3. 이미지 넣기: docker load 
# docker load -i [파일명.tar]


4. 넣은 image를 내부 registry로 변경(기존 prometheus의 cr에 image주소가 내부 registry라서 진행): docker tag 
# docker tag harbor.jane.net/dependency/quay.io/prometheus/prometheus:v2.22.1 registry.infra.jane.cluster.local:19092/quay.io/prometheus/prometheus:v2.22.1


5. tag가 변경된 image를 서버로 넣기: docker push 
# docker push registry.infra.jane.cluster.local:19092/quay.io/prometheus/prometheus:v2.28.1
The push refers to repository [registry.infra.jane.cluster.local:19092/dependency/kube-state-metrics/kube-state-metrics]
a7b3323785fe: Pushed
07363fa84210: Pushed
v2.2.0: digest: sha256:8008b06ed82e8517add62ae9a7893518244890de6efa6517adf45a6dd54eae2d size: 739


6. curl로 push 여부 확인
controller-0:~# curl -u jane:jane_pwd  https://registry.infra.jane.cluster.local:19092/v2/dependency/kube-state-metrics/kube-state-metrics/tags/list  -k
{"name":"dependency/kube-state-metrics/kube-state-metrics","tags":["v2.2.0"]}

반응형
반응형

강의가 거의 끝나고, Troubleshooting 섹션이 시작됐다.

크게 아래와 같이 4개의 Failure를 다루는데 하나씩 정리해보겠다.

- Application Failure

- Worker Node Failure

- Control Plain Failure

- Networking

 

1. Application Failure

먼저, 문제가 발생했을 경우엔 오른쪽과 같이 architecture를 알아야 한다.

어떤 pod와 어떤 service로 구성되어 있는지 말이다. 

그래서 FrontEnd부터 (그림에서는 WEB-Service) 천천히 내려가보는 것이다.

 

그래서 왼쪽과 같이 curl http://web-service.ip:node-port를 를 통해 해당 service가 문제없는지 확인해볼 수 있고,

이후엔 describe를 통해 web-service의 상태 및 config값을 조회할 수 있다.

여기서는 web pod와 연결되기 위해 Selector가 초록으로 마킹되어 있는데, 이게 pod와 맞는지 확인하는 것도 잊지 말아야한다.

 

서비스에 이상이 없다면, 이제 pod로 넘어간다.

kubectl get pod를 통해 pod상태를 조회하고, describe로 상세 상태를 보고, logs를 통해서도 문제를 확인할 수 있다.

여기서 -f, --previous 옵션을 붙이면 이전 pod의 상태에 대해서도 확인할 수 있다.

#kubectl logs [pod] -f --previous

 

여기서도 문제가 없다면, 이제 DB-Service / DB로 내려간다.

이론 설명 후 Practice 강의가 있었는데 대부분은 Selector, DB ID/PW(Root pw까지도), NodePort, Target Endpoint port error(Target Port), ep(endpoint) 문제이다.

ep는 여기서 처음 들어봤는데 # kubectl get po -n [namespace] ep를 통해 조회할 수 있고,

service가 selector를 통해 쳐다보고 있는 Endpoint(pod)를 보여준다. 

 

2. Control Plain Failure

1) Node, pods상태, kube-system에 속한 controlplain pods상태를 확인한다.

  # kubectl get nodes 

  # kubectl get pods

  # kubectl get pods -n kube-system

 

 

만약 Controlplain 컴포넌트들이 service 형태라면, service를 위와 같이 조회할 수 있다.

# service kube-apiserver status

# service kube-controller-manager status

# service kube-scheduler status

 

 

service logs를 통해서도 문제를 확인 가능하다.

kube-apiserver의 경우 journalctl를 사용한다.

# kubectl log kube-apiserver-master -n kube-system

# sudo journalctl -u kube-apiserver

 

Practice test에서는 아래와 같은 내용을 다룬다.

1) pod 한 개가 pending 상태. 근데 pod엔 문제가 없어보임.

 -> scheduler issue일 수 있음.

# kubectl get po -n kube-system

  // 를 통해 scheduler가 Crashloopbackoff 상태인 것을 확인.

그래서, scheduler를 describe하여 조회한 결과, 'failed to start container "kube-scheduler": Error response from daemon: OCI runtime create failed: container_linux.go:345: starting containre process caused "Exec: \"kube-schedulerrrr\": executable file not found in $PATH: unknown" error가 존재하였음.

=> kube-scheduler-master yaml파일에 가서 수정한다. 

# cat /etc/systemd/system/kubelet.service.d/10-kubeadm.conf   

   // kubectl config.yaml 경로 조회 가능 (/var/lib/kubelet/config.yaml)

# grep -i staticPodPath /var/lib/kubelet/config.yaml

  : staticPodPath: /etc/kubernetes/manifest // default path

# cd /etc/kubernets/manifests/

# vi kube/scheduler.yaml

 여기서 spec: containers:의 command에 'kube-schedulerrrr'가 있다. 이걸 정상 값(kube-scheduler)으로 수정해준다.

 

2) deployment를 통해 pod를 2개로 scale 

 # kubectl scale deployment app --replicas=2 // 그런데 scale이 되지 않음! 그래서 tshoot 진행.

 # kubelet get pods -n kube-system // replica를 담당하는 kubec-controller-manager이 역시나 Crashloopbackoff 상태.

 # kubectl -n kube-system describe kube-controller-manager 조회 // 문제 없음

 # kubectl -n kube-system logs kube-controller-manager-master 

  // 'stat /etc/kubernetes/controller-manager-xxxx.conf: no such file or directory' 출력

# cd /etc/kubernetes 가보니 controller-manager 파일이 있음. (-xxxx.conf 가 아님)

 # vi /etc/kubernetes/manifests/kube-controller-manager.yaml 파일 조회

  // spec: containers: command에 있는 kueconfig=/etc/kuberenets/controller-manager-xxxx.conf 파일 수정

 

이후 deployment 및 controller가 정상 동작되고 replica도 정상 동작하는 것을 확인했다.

 

3) controller-manager의 re-broken

 # kubectl get pods -n kube-system // kube-controller-manager-matser = Crashloopbackoff

 # kubectl describe po kube-controller-manager-master -n kube-system // 이슈 없어보임

 # kubectl logs kube-controller-manager-matser -n kube-sysetm 

  // 'unable to load client CA file: unable to load client CA file: open /etc/kuberenetes/pki/ca.crt: no such file or directory' 라는 에러 존재

# vi /etc/kubernetes/manifest/kube-controller-manager.yaml 확인 

  // ca.crt를 쓰고 있는 부분 확인. mountpath가 /etc/kubernetes/pki를 가지고 있는 것의 name이 'k8s-certs'임.

이걸로 다시 파일 내 조회. 그리고 그것들의 path 확인. WRONG-PKI-DIRECTORY가 있음.

 # cd /etc/kubernetes/WRONG-PKI-DIRECTORY/ 

  // 직접 해당 디렉토리 내 ca.crt가 있는지 확인. 그런데 파일이 없음.

 # cd /etc/kubernetes/pki/

  // 여기에 key값들 있는 걸 확인

# vi /etc/kubernetes/manifest/kube-controller-manager.yaml 확인 

  // k8s-cert를 쓰고 있는 path를 확인. hostPath가 /etc/kubernetes/WRONG-PKI-DIRECTORY로 설정되어 있음.

     이걸 /etc/kubernetes/pki로 변경

 

 

3. Worker Node Failure

Worker node가 문제있을 땐 node 상태 및 kubelet, Certificates (만료 여부 등)를 확인해볼 필요가 있다. 

 

- Practice에서는 kubelet과 kube-proxy에 대해 다룬다.

1. node 01 Not ready  with kubelet

1) kubelet get nodes를 조회했더니, node01이 Not Ready 상태이다.

2) ssh node01로 들어간다.

3) ps -ef | grep kubelet  // kubelet이 running중이 아니었다.

4) systemctl status kubelet.service // inactive 확인

5) systemctl restart kubelet // active가 되었다.

6) kubelet get nodes // node01이 ready 상태로 변했다.

 

2. Node 01 Not Ready with kubelet

1) kubelet get node // node01 = not ready

2) kubectl describe node node01 // 별다른 게 없다.

3) ssh node01

4) sytsemctl status kubelet.service -l // activating 상태

5) journalctl -u kubelet // kube-proxy log 조회

  'open /etc/kubernetes/pki/WRONG-CA-FILE.crt: no such file or directory 확인

6) cd /etc/systemd/system/kubelet.service.d/    // kubelet config 조회. 10-kubeadm.conf가 존재

7) cat 10-kubeadm.conf // Environment=KUBELET_CONFIG_ARGS 및 KUBELET_KUBECONFIG_ARGS 존재

 // KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml 조회

8) vi /var/lib/kubelet/config.yaml

9) /pki/WRONG-CA-FILE.crt 존재, 이부분 수정

10) cd /etc/kubernetes/

11) ls 조회 시 pki/ 및 kubelet.conf뿐.

pki/에 들어가보니 ca.crt가 있음. 즉, 경로는 /etc/kubernetes/pki/ca.crt가 되어야 함.

12) vi /var/lib/kubelet/config.yaml에서 /etc/kubernetes/pki/WRONG-CA-FILE.crt -> /etc/kubernetes/pki/ca.crt로 변경

13) systemctl daemon-reload

14) systemctl restart kubelet

15) kubelet은 active상태가 되었고 node는 ready 상태가 되었다.

 

3.  Node01 Not Ready with kubelet

1) kubelet describe node node01 // 도움되는 정보 없음

2) ssh node01

3) sytsemctl status kubelet // active & running, but error exist

  'dial tcp 172.17.0.22:6553: connect: conenction refused'

4) journalctl -u kubelet // kube-proxy log 조회, 동일한 err 존재

5) kubelet cluster-info // master node에서 master ip 및 port 확인 (172.17.0.22:6443)

6) ssh node01 

7) cd /etc/systemd/system/kubelet.service.d/    // kubelet config 조회. 10-kubeadm.conf가 존재

8) cat 10-kubeadm.conf // kubeconfig=/etc/kubernetes/kubelet.conf 확인

9) vi /etc/kubernetes/kubelet.conf // server section에서 port 변경 ( 6553-> 6443) 

10) system daemon-reload

11) system restart kubelet

12) kubelet은 active 및 error가 존재하지 않으며 node또한 ready 상태가 됨.

 

 

 

출처: Udemy 사이트의 Certified Kubernetes Administrator (CKA) with Practice Tests 강의

 

 

반응형

+ Recent posts