Fast Forword와 merge 그리고 rebase 정리

IT 지식/Git|2019. 12. 13. 00:01

5년차가 될정도로 개발을 어느정도 진행했지만 아직까지도 git에 대한 정리가 잘 되지 않는다.

 

공부도 하고 정리도 했었지만 실상 회사에서 주구장창 브랜치 만들고 머지만 해서 진행했기 때문에 fast forword를 하고 merge, rebase하는 것과 안하고 하는것의 방법을 잘 알지 못했다.

 

개념은 알아도 직접 많이 해보지 않으면 그게 와닿지 않는 것 같다.

그럼 현재 이직한 회사에서는 이 방법을 많이 사용하기에 정리를 해서 까먹지 않도록 해보자.

 

Merge

Merge는 이미 우리가 익히 알고 있듯이 두 개의 브랜치를 합치는 걸 의미한다. 그럼 fast forword를 하고 merge를 하면 어떻게 되는지 보자.

현재 이렇게 되어있는 상황에서 feature/si를 fast forword 포함해서 merge 해보겠다.  우선 master로 checkout하고 머지하려고 하는 feature/test 브랜치 우측클릭하여 머지를 해보자.

그럼 master가 feature/si의 커밋 위치로 가있는 것을 알 수 있다.

그럼 다시 마스터를 커밋하기 전으로 돌리고 다시 non fast forword를 해보자.

우선 다시 돌리자.

그리고 source tree에서는 global옵션 - Git에서 "병합 시 fast-forword를 사용하지 않고 항상 커밋을 생성"을 선택하고 다시 merge 해보자.

그러면 아래와 같은 모양으로 feature/si의 브랜치는 그대로 있고 master의 브랜치는 더 앞으로 가있는 것을 볼 수 있다.

 

그럼 과연 fast forword가 정확히 무엇인가?? 

궁금하다.

 

찾아보니 fast forword는 병합하려는 브랜치에 새로운 커밋이 없는 상황에서 발생되며 merge가 된 브랜치의 최신 커밋으로 head가 옮겨지는 것을 말한다.

 

위의 예에서 보면 master에서 분기된 feature/si브랜치가 새로운 커밋을 가지고 있고 master는 feature/si가 분기 된 이후에 새로운 커밋이 없는 상황이었다. 이때 git merge feature/si를 하게 되면 master는 feature/si 마지막 커밋으로 옮겨지고 한줄로 이어서 보여지는 아래 그림 오른쪽 처럼 되게 된다. 

 

하지만 이는 이력관리하기가 어렵기 때문에 merge하는것처럼 보여주고 이력관리를 위해서 --no-ff 옵션을 붙여서 머지하게 되면 왼쪽처럼 히스토리도 남고 master와 breanch의 위치가 구분되게 된다.

Rebase

rebase는 feature 브랜치의 커밋을 새로 rebase되는 커밋의 parent commit으로 된다. 이렇게 하게 되면 히스토리가 깔끔하게 보인다.

우선 rebase를 진행하기 위해 feature 브랜치로 checkout하고 rebase를 진행할 마스터 브랜치를 누르고 리베이스를 진행한다.

그럼 아래처럼 master위에 feature 브랜치가 위로 붙는 구조가 될것이다.  

이 상태에서 마스터로 체크아웃 하고 feature/si로 merge를 하면 fast forword가 되어 둘이 동일한 커밋을 바라보게 된다.

명령어로 정리하면 다음과 같다.

git checkout feature/si
git rebase master
git checkout master
git merge feature/si

 

 

하지만 이렇게 되면 브랜치의 히스토리를 확인하기가 어렵다. 만약 히스토리를 확인하고 싶으면 아래 명령어를 사용하면 히스토리를 확인할 수 있다.

git checkout feature/si
git rebase master
git checkout master
git merge --no-ff feature/si

 

 

어렵다 ㅋㅋ git은 해도해도 헷갈린다. 

만약 참고하시고 이상한 부분 있으면 말씀 부탁드립니다~

댓글()

nginx 서버에 filebeat를 이용하여 ELK에 로그 기록하기

IT 지식/Docker|2019. 10. 15. 22:00
git clone https://github.com/deviantony/docker-elk

nginx를 설치하고 docker 기반으로 ELK (elasticsearch, logstash, kibana)를 설치하고 nginx 로그를 filebeat를 설치하여 acces.log, error.log, syslog등을 전송해보자.

 

설치 

ELK를 도커에 설치하는 스크립트를 아래 github에 잘 정리되어 제공해주고 있다.
https://github.com/deviantony/docker-elk

ELK는 이걸로 설치하면 되는데 docker-compose로 nginx와 filebeat까지 함께 설치하기 위해서 아래 저장소에서 제공하는 nginx-filebeat 스크립트를 혼합해서 사용해보자.
https://github.com/spujadas/elk-docker/tree/master/nginx-filebeat

1. 우선 ELK 설치 스크립트를 가져오자.

git clone https://github.com/deviantony/docker-elk



2. 그리고 nginx-filebeat 파일을 다운 받아서 docker-elk 디렉토리 내부에 추가한다.

git clone https://github.com/spujadas/elk-docker
mv ./elk-docker/nginx-filebeat ./docker-elk



3. 그리고 nginx-filebeat까지 사용할 수 있도록 docker-compose.yml 스크립트를 수정해준다. 그리고 nginx.conf 파일을 쉽게 보고 생성된 log도 로컬에서 보기 위해서 mount를 로컬 폴더로 진행한다.

version: '3.2'

services:
  elasticsearch:
    container_name: "elasticsearch"
    build:
      context: elasticsearch/
      args:
        ELK_VERSION: $ELK_VERSION
    volumes:
      - type: bind
        source: ./elasticsearch/config/elasticsearch.yml
        target: /usr/share/elasticsearch/config/elasticsearch.yml
        read_only: true
      - type: volume
        source: elasticsearch
        target: /usr/share/elasticsearch/data
    ports:
      - "9200:9200"
      - "9300:9300"
    environment:
      ES_JAVA_OPTS: "-Xmx256m -Xms256m"
      ELASTIC_PASSWORD: changeme
    networks:
      - mynet

  logstash:
    container_name: "logstash"
    build:
      context: logstash/
      args:
        ELK_VERSION: $ELK_VERSION
    volumes:
      - type: bind
        source: ./logstash/config/logstash.yml
        target: /usr/share/logstash/config/logstash.yml
        read_only: true
      - type: bind
        source: ./logstash/pipeline
        target: /usr/share/logstash/pipeline
        read_only: true
    ports:
      - "5000:5000"
      - "9600:9600"
    environment:
      LS_JAVA_OPTS: "-Xmx256m -Xms256m"
    networks:
      - mynet
    depends_on:
      - elasticsearch

  kibana:
    container_name: "kibana"
    build:
      context: kibana/
      args:
        ELK_VERSION: $ELK_VERSION
    volumes:
      - type: bind
        source: ./kibana/config/kibana.yml
        target: /usr/share/kibana/config/kibana.yml
        read_only: true
    ports:
      - "5601:5601"
    networks:
      - mynet
    depends_on:
      - elasticsearch

  nginx:
    container_name: "nginx"
    build:
      context: nginx-filebeat/
    volumes:
      - type: bind
        source: /Users/we/Documents/docker/nginx
        target: /etc/nginx
      - type: bind
        source: /Users/we/Documents/docker/nginx_log
        target: /var/log
    ports:
      - "8080:80"
    networks:
      - mynet

networks:
  mynet:
    driver: bridge

volumes:
  elasticsearch:

 

4. 마지막으로 filebeat.xml에서 ssl 통신을 하지 않기 때문에 ssl 부분을 제거해준다. (이유는 하단에 나온다.)

output:
  logstash:
    enabled: true
    hosts:
      - logstash:5044
    timeout: 15

filebeat:
  inputs:
    -
      paths:
        - /var/log/syslog
        - /var/log/auth.log
      document_type: syslog
    -
      paths:
        - "/var/log/nginx/*.log"
      document_type: nginx-access

 

5. 그럼 지금까지 수정한 내용을 이용해서 docker-compose up -d 통해 설치 진행해보자. 설치후 제거 하고 싶으면 (docker-compose -v down) 명령어를 통해 제거 할 수 있다.

docker-compose up -d

 

6. 설치가 완료되면 키바나에 접속해서 확인해보면 logstash, elasticsearch, kibana 모두 설치 된 것을 알 수 있다. 초기 계정은 elastic / changeme이다.

ELK와 nginx docker process

7. logstash pipeline을 만들어줘야하는데 kibana에서 management → logstash → pipeline에서 설정해주면된다. 간단하게 5044포트로 받고 nginx_log 인덱스로 넣게 설정한다.

input {
    beats {
        client_inactivity_timeout => 19909
        port => "5044"
        ssl => false
    }
}
filter {
  if [type] == "nginx-access" {
    grok {
      match => { "message" => "%{NGINXACCESS}" }
    }
  }
}
output {
    elasticsearch {
        hosts => ["elasticsearch:9200"]
        index => "nginx_log"
        user => "elastic"
        password => "changeme"
    }
   stdout { codec => rubydebug }
}

 

8. 그럼 filebeat가 정상적으로 동작하는지 확인해보자.

filebeat 실행 상태 확인

/etc/init.d/filebeat status

 

filebeat.xml 기준으로 설정 정상 적용 상태 확인

filebeat test config

 

filebeat.xml에 설정된 output 정상 여부 확인

filebeat test output

filebeat 테스트 결과

위에 테스트를 진행하면 위에 화면처럼 나오는게 나와야 정상이다. 정상적으로 가동된걸 확인했다.

그럼 실제 nginx에서 나온 로그가 filebeat로 수집되어 logstash > elasticsearch로 정상적으로 적재되는지 보자.
localhost:8080에 접속하여 로그 발생시킨 후 filebeat 로그를 확인했는데 왠걸 다음과 같은 오류가 발생했다.

[2019-10-15T06:12:48,844][INFO ][org.logstash.beats.BeatsHandler] [local: 172.28.0.4:5044, remote: 172.28.0.2:55946] Handling exception: org.logstash.beats.BeatsParser$InvalidFrameProtocolException: Invalid Frame Type, received: 1
[2019-10-15T06:12:48,845][WARN ][io.netty.channel.DefaultChannelPipeline] An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
io.netty.handler.codec.DecoderException: org.logstash.beats.BeatsParser$InvalidFrameProtocolException: Invalid Frame Type, received: 1
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:459) ~[logstash-input-tcp-6.0.3.jar:?]
at io.netty.handler.codec.ByteToMessageDecoder.channelInputClosed(ByteToMessageDecoder.java:392) ~[logstash-input-tcp-6.0.3.jar:?]
at io.netty.handler.codec.ByteToMessageDecoder.channelInputClosed(ByteToMessageDecoder.java:359) ~[logstash-input-tcp-6.0.3.jar:?]
at io.netty.handler.codec.ByteToMessageDecoder.channelInactive(ByteToMessageDecoder.java:342) ~[logstash-input-tcp-6.0.3.jar:?]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:245) ~[logstash-input-tcp-6.0.3.jar:?]
at io.netty.channel.AbstractChannelHandlerContext.access$300(AbstractChannelHandlerContext.java:38) ~[logstash-input-tcp-6.0.3.jar:?]
at io.netty.channel.AbstractChannelHandlerContext$4.run(AbstractChannelHandlerContext.java:236) ~[logstash-input-tcp-6.0.3.jar:?]
at io.netty.util.concurrent.DefaultEventExecutor.run(DefaultEventExecutor.java:66) ~[logstash-input-tcp-6.0.3.jar:?]
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858) [logstash-input-tcp-6.0.3.jar:?]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) [logstash-input-tcp-6.0.3.jar:?]
at java.lang.Thread.run(Thread.java:834) [?:?]
Caused by: org.logstash.beats.BeatsParser$InvalidFrameProtocolException: Invalid Frame Type, received: 1
at org.logstash.beats.BeatsParser.decode(BeatsParser.java:92) ~[logstash-input-beats-6.0.0.jar:?]
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:489) ~[logstash-input-tcp-6.0.3.jar:?]
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:428) ~[logstash-input-tcp-6.0.3.jar:?]
... 10 more

이 문제는 logStash 또는 filebeat가 동시에 ssl을 사용하지 않는데 한쪽만 ssl 통신을 했을 때 발생되는 오류이다.

그래서 아까 위에 filebeat에 ssl 부문을 지우고 pipeline에 ssl 설정을 false로 지정한 것이다. 그럼 다시한번 localhost:8080에 들어가보자.

지금은 index.html을 만들어 놓지 않아서 404 에러가 발생된다.

GET nginx_log/_search
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "nginx_log",
        "_type" : "_doc",
        "_id" : "vc04zm0BZBO8vgBrBmV7",
        "_score" : 1.0,
        "_source" : {
          "ecs" : {
            "version" : "1.1.0"
          },
          "@version" : "1",
          "tags" : [
            "beats_input_codec_plain_applied"
          ],
          "message" : """2019/10/15 07:00:31 [error] 25#25: *16 "/etc/nginx/html/index.html" is not found (2: No such file or directory), client: 172.28.0.1, server: , request: "GET / HTTP/1.1", host: "localhost:8080"""",
          "host" : {
            "name" : "a31d3333d22b"
          },
          "agent" : {
            "type" : "filebeat",
            "version" : "7.4.0",
            "hostname" : "a31d3333d22b",
            "id" : "cc4eb582-e09c-4a83-bb2e-9721c39ee508",
            "ephemeral_id" : "a5918a0b-4688-458f-bbc2-4eb49a3fff03"
          },
          "log" : {
            "file" : {
              "path" : "/var/log/nginx/error.log"
            },
            "offset" : 8524
          },
          "@timestamp" : "2019-10-15T07:00:38.443Z"
        }
      },
      {
        "_index" : "nginx_log",
        "_type" : "_doc",
        "_id" : "vs04zm0BZBO8vgBrBmV8",
        "_score" : 1.0,
        "_source" : {
          "ecs" : {
            "version" : "1.1.0"
          },
          "@version" : "1",
          "tags" : [
            "beats_input_codec_plain_applied"
          ],
          "message" : """172.28.0.1 - - [15/Oct/2019:07:00:31 +0000] "GET / HTTP/1.1" 404 555 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"""",
          "host" : {
            "name" : "a31d3333d22b"
          },
          "agent" : {
            "type" : "filebeat",
            "version" : "7.4.0",
            "hostname" : "a31d3333d22b",
            "id" : "cc4eb582-e09c-4a83-bb2e-9721c39ee508",
            "ephemeral_id" : "a5918a0b-4688-458f-bbc2-4eb49a3fff03"
          },
          "log" : {
            "file" : {
              "path" : "/var/log/nginx/access.log"
            },
            "offset" : 8528
          },
          "@timestamp" : "2019-10-15T07:00:38.443Z"
        }
      }
    ]
  }
}

 

정상적으로 적재가 잘되는것을 확인할 수 있다.

이로써 docker로 구성된 elk로 로그 적재를 진행해봤다. 

 

관련 패키지는 github에 올려놓았다.

https://github.com/weduls/elk_with_nginx

댓글()

spring boot api를 쿠버네티스로 deployment 해보기

IT 지식/Kubernetes|2019. 9. 11. 00:26

저번 글에서 기본적인 쿠버네티스 관련 개념과 자원에 대해 공부했다.

이제 실질적으로 api애플리케이션을 하나 만들어보고 배포까지 진행해보자.

 

로컬 이미지를 담을 registry 생성

쿠버네티스의 노드들은 외부와 연결되는 경우도 있지만 그렇지 못하는 환경도 많이 존재한다. 그럴 경우 이미지를 내려받을 수 없고 로컬에서만 만들어서 사용할 이미지를 등록할 registry가 필요하다.

# registry 이미지 가져오기
docker pull registry:latest

# 레지스트리 실행
docker run --name MyPrivateRegistry -d -p 5000:5000 registry

 

애플리케이션 생성

우선 spring boot gradle 프로젝트로 아무것도 만들지 않고 바로 빌드해서 사용해보자. 그 이유는 나중에 컨트롤러를 추가해서 엔드포인트가 늘어난 api로 이미지 버전을 변경해서 Rolling update 배포가 되는 것을 확인해보기 위해서이다.

그럼 만들어진 스프링 부트파일을 빌드하는 Dockerfile을 만들어보자.

FROM openjdk:8
ENV APP_HOME=/usr/app/
WORKDIR $APP_HOME
COPY ./build/libs/* ./app.jar
EXPOSE 8080
CMD ["java","-Dspring.profiles.active=development","-jar","app.jar"]

그리고 빌드를 진행하고 registry에 등록해놓자.

# 이미지 빌드 v1 tag
docker build -t demo/api:v1 .
docker image tag demo/api:v1 localhost:5000/demo/api:v1

# registry에 등록
docker image push localhost:5000/demo/api:v1

그리고 registry에서 해당 내용을 확인해보면 정상적으로 등록된 것을 알 수있다. http://localhost:5000/v2/_catalog

 

 

로드밸런서 서비스 실행

서비스의 경우 pod들의 외부와 통신을 담당한다는걸 저번시간에 공부로 알게되었다. 이제 pod를 배포하기전에 배포된 api 컨테이너들을 적절하게 로드밸런싱 해줄 서비스를 동작시켜보자. 

# node-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: node-svc
spec:
  selector:
    app: demo
  ports:
    - port: 80
      protocol: TCP
      targetPort: 8080
  type: LoadBalancer

v1 버전의 node-svc이름을 가지고 있고 demo 애플리케이션에 적용이 되며 tcp프로토콜을 가지고 타겟 포트가 8080을 가지고 있다.

kubectl create -f node-svc.yaml

지금은 endpoints에 아무것도 없다. 왜냐하면 아직 이 로드밸런서에 연결된 pods가 없기 때문이다. 그럼 이제 pods를 배포해서 연결해보자.

 

Pod 배포 (deployment)

그럼 pods를 배포해보자. 이미지는 아까 registry에 적용했던 api를 사용하고 버전은 우선 v1을 사용해보자.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-api
  labels:
    app: demo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
    spec:
      containers:
      - name: demo-api
        image: localhost:5000/demo/api:v1
        ports:
        - containerPort: 8080

그리고 로드밸런서 서비스를 보면 정상적으로 엔드포인트가 보이는걸 알 수있다.

그럼 minikube ip와 NodePort를 사용해서 한번 api에 붙어보자.

Minikube ip:nodeport

아무것도 만들지 않아서 whitelabel error가 발생한다. 

 

 

Rolling update pod교체

그럼 엔드포인트를 하나 만들어서 새로 image를 넣어보자.

@RestController
@RequestMapping("")
public class DemoController {

    @GetMapping("/test")
    public ResponseEntity test() {
        return ResponseEntity.ok("test");
    }
}

그리고 엔드포인트를 추가하고 나서 새로 이미지를 만들어준다.

docker build -t demo/api:latest .
docker image tag demo/api:latest localhost:5000/demo/api:latest
docker image push localhost:5000/demo/api:latest

그리고 deployments에 있는 demo-api 컨테이너의 api를 바꿔줘보자.

kubectl set image deployments/demo-api demo-api=localhost:5000/demo/api:v1
→ kubectl set image deplyments/{deployments 이름} {container 이름}={새로운 이미지}

그럼 이전 pod들이 꺼지고 새로운 이미지가 적용된 파드로 대체되는걸 볼 수 있다.

변경된 image가 잘 적용이 되었는지 확인해보면 잘나오는걸 확인할 수 있다.

 

굳굳 이렇게 쿠버네티스를 공부하고 실질적으로 한번 배포해봤다. 이제 이걸 wedul timeline에 한번 적용해보면서 더 운영을 해봐야겠다.

댓글()
  1. Favicon of https://lascrea.tistory.com BlogIcon Lascrea 2019.09.17 21:56 신고 댓글주소  수정/삭제  댓글쓰기

    기본적으로 Deployment는 Rolling Update를 사용하는걸로 알고 있어요!

  2. 2019.09.18 11:25 댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

kubernetes 기본 개념정리와 구성 알아보기 (설치 포함)

IT 지식/Kubernetes|2019. 9. 10. 22:16

docker를 사용하면서 그 편리함을 느끼고 있었다. 그리고 요 근래 it회사에서 docker와 kubernets를 이용하여 인프라를 운영을 하는 것을 많이 들었다.

나는 그런 환경을 접해보지는 못했기 때문에 정확하게 kuberntes가 무엇인지 잘 모른다. 그래서 이번 기회에 kubernets(이하 쿠버네티스)에 대한 기본 개념을 정리하고 설치해서 공부를 위한 초석을 닦아보자.

 

쿠버네티스 (kubernetes)

쿠버네티스는 도커 컨테이너 운영을 자동화 하기위한 오케스트레이션 도구이다. 구글에서 만들었으며 컨테이너를 운영하고 다루기 위한 api와 cli등을 제공한다. 컨테이너 배포 이외에도 효율적인 컨테이너 배치 및 스케일링, 로드밸런싱, 헬스 체크, secure등의 기능을 제공한다.

AWS ECS 와 GClound에서 도커 관리 기능을 제공하면서 컨테이너를 사용한 애플리케이션 개발이 점차 보급 되었다.  기존에도 docker에 스웜(swarm)이라는 기능이 있었으나 사용해 봤을 때 불편한 점이 많았는데 쿠버네티스가 더 편리하게 이 점이 보완되었다. 

 

쿠버네티스 설치 (MacOs)

실습을 진행할 컴퓨터가 mac os이기 때문에 mac에서 설치하는 방법을 알아보자. 우선 docker에서 preference에 들어가면 kubernetes 탭이 존재한다. 여기서 아래와 같이 체크하고 Apply를 누르면 설정이 우선 완료된다.

그리고 cli로 쿠버네티스를 다루기 위한 도구인 kubectl을 설치한다.

curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/darwin/amd64/kubectl
chmod +x kubectl
mv kubectl /usr/local/bin

위에 명령어를 통해 kubectl을 명령어로 등록해주는데 자세한 방법은 홈페이지에 잘 나와있다.

https://kubernetes.io/docs/tasks/tools/install-kubectl/

설치가 완료된 후 버전을 확인해보면 출력되면 정상적으로 설치가 된 것 이다.

 

단일 노드 쿠버네티스 클러스터 Minikube 설치

쿠버네티스 클러스터에는 여러 노드들이 존재하지만 실습을 위해서 단일 노드로 되어 있는 Minikube를 설치해서 사용한다. 맥 기준으로 brew로 설치하면 되고 자세한 방법은 아래를 참조하면 된다.

brew cask install minikube

https://kubernetes.io/ko/docs/tasks/tools/install-minikube/

 

쿠버네티스 구성요소

쿠버네티스로 실행되는 애플리케이션은 노드, 네임스페이스, 파드, 레플리카세트, 디플로이먼트 등 다양한 리소스가 함께 연동해 동작한다. 

그 구성 요소들에 대해 간단하게 정리해보자.

1. 노드

-> 노드는 쿠버네티스 클러스터의 관리 대상으로 등록된 도커 호스트로 컨테이너가 배치되는 대상이다. 노드는 마스터와 일반 노드들로 구성되는데 쿠버네티스 클러스터 전체를 관리하는 서버에는 마스터가 적어도 하나 이상 있어야 한다. 마스터는 클러스터를 상호 조정하는 장치이고 노드는 애플리케이션이 실제로 돌아가는 곳이다. 노드에는 여러 파드들이 위치할 수 있고 그 파드에는 컨테이너들이 존재한다.

구글 이미지 출처

또한 쿠버네티스는 노드의 리소스 사용 현황 및 배치 전략을 근거로 컨테이너를 적절하게 배치한다. 클러스터에 배치된 노드 수, 노드의 사양 등에 따라서 노드에 배치할 수 있는 컨테이너의 수를 결정한다. 클러스터의 처리 능력은 노드에 의해서 결정된다.

kubectl get nodes

 

 현재 노드는 아까 설치한 단일 노드 Minikubes가 마스터로 위치해 있는 걸 볼 수 있다.

 

2. 네임스페이스

쿠버네티스 클러스터 내부에는 여러개의 가상 클러스터를 만들 수 있는데 이 네임스페이스는 하나의 공간으로써 사람또는 상황에 따라서 나눠서 사용할 수 있다. 기본적으로는 default로 되어있다.

 

3. 파드 (pod)

 pod는 컨테이너가 모인 집합체로써 하나 이상의 컨테이너로 이루어 진다. nginx, was처럼 서로간의 연결고리가 있는 컨테이너들은 하나로 묶어 일괄배포한다. pod는 노드내에 배치되고 같은 파드는 여러 노드에 배치할 수도 있고 한 노드에 여러개 배치할 수도 있다. pod는 하나 또는 그 이상의 애플리케이션 컨테이너 그룹을 나타내는 쿠버네티스의 추상적 개념으로 컨테이너에 대해 서로 자원을 공유한다.

  • 볼륨과 같은 공유 스토리지
  • 클러스터 IP 주소와 같은 네트워킹
  • 컨테이너 이미지 버전 또는 사용할 특정포트와 같은 각 컨테이너가 동작하는 방식에 대한 정보

 

apiVersion: v1
kind: Pod
metadata:
  name: sample-echo
spec:
  containers:
  - name: nginx
    image: ddd/nginx:latest
    env:
    - name: BACKEND_HOST
      value: localhost:8080
    ports:
    - containerPort: 80
  - name: echo
    image: dd/echo:latest
    ports:
    - containerPort: 8080
    
 # 파드 yaml 예제

 

4. 레플리카 세트

파드를 하나만 사용하여 가동할 경우에 실제 가용성이 떨어지기 때문에 레플리카 세트를 만들어서 여러 파드를 함께 구성해준 것이다.

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: echo
  labels:
    app: echo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: echo
  template: # template하단은 pad.yaml과 같음

 

5. 서비스 (servce, svc)

서비스를 이용해서 각 Pod에 있는 애플리케이션을 외부에서 접근하게 할 수있다.

쿠버네티스에서 서비스는 하나의 논리적인 파드 셋과 그 파드들에 대한 접근할 수 있는 정책을 정의하는 추상적인 개념이다. 서비스는 종속적인 파드들 사이를 느슨하게 결합되도록 도와준다. 서비스는 모든 쿠버네티스 오브젝트들과 같이 yaml로써 정의 할 수있다. 각 서비스가 되는 대상을 labelSelector를 통해 지정할 수 있다.

각 파드들이 고유의 IP를 갖고 있기는 하지만, 그 IP들은 서비스의 도움없이 클러스터의 외부로 노출될 수 없다. 서비스들은 애플리케이션들에 트래픽이 실릴 수 있도록 허용해준다. 서비스들은 ServiceSpec에서 type을 지정함으로써 다양한 방식들로 노출시킬 수 있다.

clusterIp(기본값) - 클러스터 내에서 내부 IP에 대해 서비스를 노출해준다. 이 방식은 오직 클러스터 내에서만 서비스가 접근될 수 있도록 해준다.

NodePort - NAT가 이용되는 클러스터 내에서 각각 선택된 노드들의 동일한 포트에 서비스를 노출시켜준다.
<NodeIP>:<NodePort>를 이용하여 클러스터 외부로부터 서비스가 접근할 수 있도록 해준다. CluserIP의 상위 집합이다.

LoadBalancer - (지원 가능한 경우) 기존 클라우드에서 외부용 로드밸런서를 생성하고 서비스에 고정된 공인 IP를 할당해준다. NodePort의 상위 집합이다.

ExternalName - 이름으로 CNAME 레코드를 반환함으로써 임의의 이름(스펙에서 externalName으로 명시)을 이용하여 서비스를 노출시켜준다. 프록시는 사용되지 않는다. 이 방식은 kube-dns 버전 1.7 이상에서 지원 가능하다.

 

6. 디플로이먼트 (deployment)

서비스, 파드, 레플리카세트의 집합체로써 애플리케이션의 기초가 되는 단위이다. 디플로이먼트 내부에는 서비스, 파드, 레플리카세트 등을 한번에 구성하여 적용 시킬 수 있다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo
  labels:
    app: echo
spec:
  replicas: 3
  selector:
    matchLabels
      app: echo
  template: # template는 pod와 동일

 

서비스와 디플로이먼트에 차이는 서비스는 내부에 여러 pod들을 외부와 연결해주는 역할을 담당하고 디플로이먼트는 쿠버네티스에서 돌아가는 pod들의 상태를 확인하면서 재시작 등등을 진행하는 담당을 한다.

다음 시간에는 실제 애플리케이션 하나를 이미지로 빌드하고 그것을 minikube에 디플로이먼트를 통해 파드로 배포를 진행해본다. 그리고 스케일적용과 이미지 변경 시 자동으로 새로 배포되는 블루그린 배포도 적용해보자.

 

참고 싸이트

https://kubernetes.io/ko/docs/tasks/administer-cluster/highly-available-master/
https://kubernetes.io/ko/docs/tutorials/kubernetes-basics/deploy-app/deploy-intro/
https://kubernetes.io/ko/docs/concepts/overview/working-with-objects/labels/
https://devopscube.com/kubernetes-deployment-tutorial/

댓글()

[공유] 우분투 설치 시 boot efi 관련 오류 수정 방법

IT 지식/기타지식|2019. 9. 9. 08:25

와이프가 결혼전에 집에서 사용하던 노트북 lenovo 제품이 있어서 이걸 간단하게 사용할 서버로 쓰고 싶었다.

그래서 우분투를 설치하려고 하는데 계속 the 'grub-efi-amd64-signed' package failed to install into /target/. Without the GRUB boot loader, the installed system will not boot. 라는 오류만 발생했다.

구글링을 하면서 efl boot 영역을 설정해주고 별짓 을 다해도 안되었는데

 

오늘 아침에 이 유튜브를 보고 해결했다. 나중을 위해서 공유해논다.

https://www.youtube.com/watch?v=DWlB0_f3GAY

태그 : BIOS, boot, efl, grub, Target, ubuntu

댓글()

[토이 프로젝트 소개] 개발자 채용, 기술 블로그 정보를 모아주는 TimeLine

IT 지식/기타지식|2019. 8. 18. 21:46

혼자 공부를 집에서 어떻게 하면 효율적일까 고민을 많이했다.

집에서 주구장창 책을 읽고 해보면 스킬이 늘까? 그렇게 해봤지만 그게 정답은 아니었다. 남들에게는 모르겠으나 나에게는 아니었다. 

 

회사에서 하는 업무는 한정적이니 내가 회사에서 하지 못하지만 알고 싶고 잊고 싶지 않은 내용에 대해서 프로그램을 직접 만들면서 공부할 내용을 정리하고 싶었다.

그래서 만들게 된게 타임라인인데, 개발자 채용정보나 기술 블로그를 rss등을 사용해서 모아볼 수는 있으나 별도의 관리 툴이나 브라우저에서 확인해야해서 좀 불편했다. 그래서 그것을 한번에 볼수 있게 하는 사이트가 있으면 좋을 것 같아서 만들어봤다.

우선 주소는 http://wedul.space이다. aws에 도입하고 싶었으나 비용도 걱정되니해서 집에있는 간이 서버에 도입하였다.

화면은 잘 그릴지 몰라서 vue.js 책을 사서 간단하게 읽고 구성했다.

 

홈 / JOB / TECH로 페이지는 구성되어 있고 앞으로 로그인하여 본인이 구독하고 싶은 회사만 보는 기능을 추가할 예정이다. 그리고 자세히보기 누르면 현재는 해당 페이지로 이동하지만 timeline 내부에서 볼 수 있는 페이지를 추가할 예정이다.

 

원래는 혼자 저 정도 까지 구성하다가 크롤링 사이트를 확장하는데 혼자하기에는 조금 역부족이고 함께하고 하고자 하는 친구가 있어서 요청했다. 그래서 테크쪽과 프론트는 그 친구가 담당해주고 있다. 

저장소는 공개 되어있고 참여하고 싶으신 분들은 pull-request 보내주셔도 좋다.

https://github.com/weggdul/timeline

 

 

백엔드 구성


아무래도 나는 백엔드 개발자이다 보니 백엔드에만 사실 집중했다. 크롤링 양이 많지 않아서 별도 배치를 만들거나 큐에 쌓거나 할 필요는 없었으나 공부를 위해서 모두 구성해봤다.

우선 java8, spring-boot2.1.5로 구성되어 있고 멀티모듈로 관리하고 있다. (https://github.com/weggdul/timeline)

api 서버는 spring-batch를 통해 긁어온 데이터가 쌓여있는 mariadb에서 정보를 가져와 보여주며 중간에 redis가 위치해 있어 데이터를 캐시하고 있다.

 

batch 서버는 spring-batch에서 하루 두번 모든 JOB, tech사이트에 들러 크롤링해오고 그 정보를 kafka로 전송한다. kafka를 리스닝 하고 있다가 kafka에 데이터가 들어오면 읽어서 mariadb로 적재시키고 있다.

 

아직 미완 단계지만 다 만들어지고 나면 배포 자동 구성도 하고 seo도 달고 광고도 한번 달아봐야겠다.

공부도 확실히 되는거 같고.. 연말까지는 하고자 했던거 다 붙여보자.

댓글()
  1. 지나가는개발자 2019.08.19 14:19 댓글주소  수정/삭제  댓글쓰기

    토이프로젝트 깃 주소도 그렇고 서비스주소도 없다고뜨네요?

    • Favicon of https://wedul.site BlogIcon 위들 wedul 2019.08.19 14:26 신고 댓글주소  수정/삭제

      안녕하세요. 주소 정보는 아래와 같구요. 다시한번 확인해주세요!

      서비스 사이트 : http://wedul.space
      github : https://github.com/weggdul/timeline

  2. Favicon of https://wedul.site BlogIcon 위들 wedul 2019.10.30 15:01 신고 댓글주소  수정/삭제  댓글쓰기

    잠시 서비스 중단합니다.

Github repository 위치 변경

IT 지식/Git|2019. 8. 1. 23:16

github에서 개인적으로 하고 있는 토이프로젝트 wedul_timeline을 친구와 함께 작업하기로 해서 그룹을 생성했다.

그룹 이름은 우리의 아이덴티티에 맞는 potato로 지정했다. ㅋㅋ

 

그런데 이렇게 지정하다보니 기존에 내 repository에 위치해있던 소스를 그룹으로 옮겨야 했다. 

그 과정에서 삽질했던 내용을 다음에는 삽질 하지 않도록 기록해봤다. 

 

현재 Git Repository 저장소 clone

우선 현재 있는 repository를 복사 해야한다.

git clone --mirror https://github.com/weduls/wedul_timeline

복사가 완료되었다. 그럼 이제 새로 이전할 레포지토리가 필요하다.

그룹에 들어가서새로운 레포지토리를 생성한다.

 

새로운 remote origin 설정

변경을 진행할 새로운 remote origin을 설정해준다. 새로운 remote 주소는 당연히 새로 생성한 레포지토리여야 한다.

git remote set-url --push origin https://github.com/weggdul/timeline_m

 

새로운 레포지토리에 복사한 저장소 내역 push

그럼 마지막으로 아까전에 mirror를 진행한 내역을 push로 서버에 밀어 넣어주자.

git push --mirror

 

결과를 확인해보면 레포지토리가 히스토리까지 그대로 옮겨진 것을 확인할 수 있다.

흠 편한군 ㅋㅋ

댓글()
  1. 2019.08.02 10:24 댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

git ssh로 받은 프로젝트를 https 방식으로 변경하기.

IT 지식/Git|2019. 3. 20. 21:19

회사에서 ssh 방식으로 git을 사용하고 있었으나 정책상 ssh가 막혀서 https로 전환해야 했다.


근데 사실 ssh로만 주로 사용했지 이를 바꿔서 진행해본적이 없어서 난감했다.(기존에 작업중이던거 어떻게 ...)


그래서 알아보던중 같은팀 개발자분이 힌트를 주셔서 그대로 해보니 해결되었다. ㅋㅋ


간단하게 정리해보자.


- 프로젝트 내에 .git의 config파일 열어서 수정

터미널을 이용하든 편집기를 이용해서 config파일을 열어본다.

그럼 밑줄 친 부분과 같이 ssh주소로 되어있는데 이를 repository의 https 주소로 바꿔주고 저장한다.


그리고 다시한번 git 명령어를 시도해보면 다음과 같이 계정 정보를 입력하라고 나오는데 이곳에는 해당 레포지토리가 있는 github이나 gitlab의 계정정보를 입력하면된다.


그럼 끝!

아주 간단하다.


댓글()
  1. 지지리 2019.08.04 21:44 댓글주소  수정/삭제  댓글쓰기

    좋은 정보네요