ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 쿠버네티스와 go로 에코 서버 만들기 -1
    개발기록/쿠버네티스 2023. 3. 12. 02:01

    에코서버란?

    에코 서버는 사용의 입력을 그대로 돌려주는 서버를 말한다.
    이번 포스팅에서는 쿠버네티스 설정에 더 집중하고 싶어서, 입력 없이 단순히 hello world라는 문자열만 돌려주게 했다.

    쿠버네티스 설정

    데스크탑에 VirtualBox를 설치한 뒤, On-premise처럼 직접 노드들을 만들었다.
    쿠버네티스는 1.24 버전을 설치했다.
    설치하는데 정말 애를 먹었다.
    계속 "did you specify the right host or port?"라는 에러에 시달렸다.
    정말 며칠동안 해결을 하지 못하다가, admin.conf 설정과 systemd, containerd등을 잘 맞추고, 설치하는 쿠버네티스 버전을 1.24로 내린 뒤 해결했다.
    containerd 공식 홈페이지를 가보니 쿠버네티스 1.24를 지원한다고 적혀있던데, 그 때문인듯 하다.

    Vagrant

    오류가 계속 나다보니, 한땀한땀 VirtualBox에 우분투 이미지를 올리는게 너무 귀찮았다.
    아무리 스냅샷을 찍는다고 해도, 오류의 원인을 파악할 수 없어서 찍어놓은 스냅샷 이전으로 돌아가야하나? 하면서 스냅샷을 수없이 삭제했다.
    그래서 쿠버네티스 책에서 소개한 Vagrant를 사용했고, 한번에 머신을 만들수 있게 되었다.

    Vagrant는 가상 머신 환경을 관리하는 도구다.
    VM의 환경 설정을 적어놓은 파일(아마 yaml)을 설정하면, vagrant up 명령어로 간단하게 적힌대로 VM을 만들어준다.

    Vagrant와 쿠버네티스는 글을 따로 작성해 보도록 하겠다.

    Go언어로 echo 서버 만들기

    Go의 net/http모듈을 이용해 특정 포트로 들어오는 request를 handle / listen할 수 있다.
    Go의 다른 웹 프레임워크(echo...)등도 이 http모듈이 기본이라고 한다.

    package main
    
    import (
        "log"
        "net/http"
    )
    
    func main() {
        http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
            log.Println("request")
            w.Write([]byte("hello wolrd"))
        })
        http.ListenAndServe(":8000", nil)
    }

    위의 내용으로 main.go 파일을 생성한다
    go run main.go 를 실행하보면, listen중인것을 볼 수 있다.
    curl localhost:8000 을 터미널에서 실행하면, hello world를 출력하 것을 볼 수 있다.

    Dockerfile로 docker image 만들어서 docker hub에 업로드하기

    쿠버네티스에 pod를 생성하기 위해서는, 컨테이너 이미지가 필요하다.

    FROM golang:1.20
    COPY main.go /go/src/main.go
    EXPOSE 8000
    WORKDIR /go/src
    ENTRYPOINT [ "go", "run",  "main.go"]

    위와 같은 내용으로 Dockerfile을 작성한다.
    간단히 설명을 하자면

    1. golang 1.20을 기반으로,
    2. 현재 위치에 있는 main.go를 컨테이너의 /go/src/main.go에 복사한다
    3. 8000번 포트를 open하고
    4. cd 명령처럼, 뒤의 명령어가 실행될 경로를 /go/src/ 로 변경한다
    5. 해당 컨테이너가 실행될 때, go run main.go 명령어를 실행하겠다
      는 뜻이다.명령어로 컨테이너 이미지를 만들었다.
      dockerfile과 main.go 가 둘다 있는 디렉토리에서 실행해야 한다.
    6. docker build --platform linux/amd64 -t castleflag/eggo_server:1.0 .

    docker hub 업로드

    docker login으로 도커에 로그인한다
    user_name은 docker hub 닉네임을 적으면 된다.

    나의 경우엔, 아래와 같은 이미지가 만들어졌다
    https://hub.docker.com/repository/docker/castleflag/eggo_server/general

    쿠버네티스에 echo 서버 배포하기

    github에 설정 파일 올리기

    master-node에서 github으로 파일을 편하게 내려받기 위해 프로젝트 안에 쿠버네티스에서 사용할 yaml들을 모아놓도록 했다.
    일단 처음에는 단순히 pod와 service(nodeport)만 가지고 실행해보자.
    https://github.com/CastleFlag/eggo_server/tree/main/1.simple_echo

    master-node에서 파드 생성하기

    git clone을 이용해서 위의 저장소를 받아오고, k8s폴더 안의 yaml들을 배포해보자.
    pod용 yaml의 내용은 다음과 같다

    apiVersion: v1
    kind: Pod
    metadata:
      labels:
        app: server
    spec:
      containers:
      - name: echo-server
        image: castleflag/eggo_server:1.0
        ports:
        - containerPort: 8000

    pod용 yaml을 배포하기 위해 아래 명령어를 실행했다.

    kubectl apply -f echo_server.yaml

    아래 명령어를 실행하면 pod의 정보를 볼 수 있다

    kubectl get pod -o wide

    일단 default 네임스페이스에 있기 때문에, 조회가 가능하다.`
    그러면 파드의 ip를 볼 수 있는데, master-node의 터미널에서 아래 명령어를 실행하면 hello world가 출력되는걸 볼 수 있다.

    curl http://[파드ip]

    클러스터 외부에서 접근할 수 있게 해주기

    쿠버네티스 서비스 배포

    파드는 계속 생성되고 삭제되기 때문에, 고정적인 ip를 받기 어렵다.
    또한 파드는 한 클러스터 안에 존재하기 때문에, 다른 클러스터나 외부(유저)와 바로 연결할 수 없다
    따라서, 외부에서의 요청을 적당한 파드로 보내기 위한 오브젝트가 필요한데, 그게 바로 서비스다.
    외부에서 오는 요청을 처리할 수 있는 가장 간단한 서비스가 노드포트라서, 노드포트를 사용했다.
    노드포트용 yaml은 다음과 같다

    apiVersion: v1
    kind: Service
    metadata:
      name: echoserver-service
    spec:
      type: NodePort
      ports:
      - targetPort: 8000
        port: 8000
        nodePort: 30001
      selector:
        app: server

     

     

    kubectl apply를 통해 배포해보면 virtualbox가 실행되고 있는 데스크탑(host)의 크롬 창에 192.168.56.30:30001을 입력하면 hello world를 확인할 수 있다.

    맞닥뜨렸던 문제들

    1. kubectl apply -f 했는데, status가 계속 pending임
      1. kubectl get nodes 했는데, status=NotReady
      2. kubectl describe node node-worker1 해보니까, 나머지는 괜찮은데 networkReady=false임. 아마도 calico를 master에만 깔아서인걸로 추측됨
      3. master-node도 똑같은 에러가 발생하는걸 확인함
      4. kubectl get pods를 해보니, coredns가 pending 상태임
      5. kubectl logs -n tigera-operator tigera-operator-65d6bf4d4f-h9bjh로 확인해봄
      6. IPPool 192.168.0.0/16 is not within the platform's configured pod network CIDR(s) [20.96.0.0/12]"오류를 확인
      7. 이거 vagrant 설정파일을 내가 여러군데서 짬뽕한거여서, CIDR 설정이 calico랑 안 맞는 거였음
        1. 내가 ip대역을 192.168.56.30을 node대역으로 써서, 해당CIDR을 피했던 것 같은데...

    vagrant를 다시 해서 vm을 다시 프로비저닝 해봐야겠다고 생각함
    -> 해결함!master-node도 ready 상태이고, coredns도 Running상태임.
    그런데...

    1. pod를 배포해보니 ImagePullBackOff에러
      1. 이미지를 확인해보니 m1 mac에서 이미지를 만들어서 arm64임
      2. docker build --platform linux/amd64 -t castleflag/eggo_server:1.0으로 다시만들어서 push함

    해결함!

    1. 서버의 응답(hello world)에 개행이 없어서 좀 보기 싫음
      1. docker image를 다시 빌드해서 push함
      2. 파드를 지우고 다시 생성해봤는데, 아마 이미지가 남아있는지 여전히 개행이 없음
      3. 도커라면 이미지를 지우면 될거같은데, 컨테이너 런타임이 containerd라서 명령어를 잘 모르겠음
      4. 도커 허브에 이미지를 똑같은 태그로 푸시해서(castleflag/eggo_server:1.0), yaml파일의 image를 수정해서 고칠 수가 없음
      5. 대신, 임시로 imagePullPolicy를 Always로 바꿔서, 파드를 지우고 다시 배포함
      6. 근데 그러면 이제 이미지를 배포할때마다 새로 가져올텐데, 그걸 원하지는 않음
      7. yaml파일에서 imagePullPolicy를 지우고, 파드를 다시 배포하면 에러가남(yaml파일에서 속성이 없어졌으니까)
      8. 그래서 다시 파드를 지우고, 다시 배포함...
        해결함. 그래도 이미지 파일 관리를 좀 잘할 수 있는 방법을 찾아야겠음(tag를 바꾸는게 제일 빨랐을 것 같음)
-