자체 Kubernetes 구축 - 파트 4 - 노드 설정

지난 포스트에서는 팟(Pod)에 대해 다루었으니, 이번 포스팅에서는 노드(Node)에 대해 집중적으로 다뤄보도록 하겠습니다.
하지만 먼저 노드란 무엇입니까?
노드는 물리적 또는 가상 머신일 수 있습니다. 컨트롤 플레인과 etcd를 포함하는 마스터 노드와 실행 중인 포드, kubelet 및 k-proxy를 포함하는 작업자 노드가 있습니다. 이 기사에서는 작업자 노드 생성에 중점을 둘 것입니다.

이 기사 이전에 코드를 리팩터링했습니다. 포드의 생성 및 실행 방법, STDIO가 아닌 로그 파일로 설정된 출력, 포드 로그를 가져오는 새로운 방법 추가, 포드 생성 명령은 이제 생성하는 NewPodAndRun 함수를 호출합니다. 코드를 실행합니다.

나는 노드 OS에 적합한 플랫폼을 찾는 데 어려움을 겪었고, 처음에는 백그라운드에서 에이전트(kubelet)와 containerd 서비스를 실행할 적절한 도커 컨테이너 이미지를 찾았지만 운이 없었습니다. 따라서 자체적으로 기본 이미지를 구축해야 합니다. 이 노드 VM은 KVM으로도 생성할 수 있습니다. 더 쉬운 접근 방식을 위해 도커를 결정했습니다.
먼저 Golang으로 Dockerfile을 빌드해야 합니다. 기본 이미지 우분투를 사용하고(Alpine을 사용해 보았지만 go 소스 코드를 빌드하는 데 약간의 문제가 있었습니다 😔) go 및 containerd를 설치하여 모든 것이 작동하는지 확인합니다. 간단한 HTTP를 사용했습니다. 섬기는 사람:

package main

import (
    "fmt"
    "net/http"
    "log"
    "os"
    "os/exec"
)

func hello(w http.ResponseWriter, req *http.Request) {

    fmt.Fprintf(w, "hello\n")
}

func headers(w http.ResponseWriter, req *http.Request) {

    for name, headers := range req.Header {
        for _, h := range headers {
            fmt.Fprintf(w, "%v: %v\n", name, h)
        }
    }
}

func startContainerd() {
    cmd := exec.Command("containerd")
    cmd.Stdout = os.Stdout
    err := cmd.Start()
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("just ran subprocess %d", cmd.Process.Pid)
}

func main() {
    startContainerd()

    http.HandleFunc("/hello", hello)
    http.HandleFunc("/headers", headers)

    http.ListenAndServe(":8090", nil)
}


서버는 먼저 containerd 서비스 프로세스를 시작한 다음 서버를 시작합니다.
Dockerfile의 경우 Go, containerd 및 복사 및 빌드 또는 main.go를 설치했습니다.

FROM ubuntu

WORKDIR /agent

RUN apt-get update \
    && apt-get install -y wget git gcc \
    && wget -P /tmp https://go.dev/dl/go1.19.2.linux-amd64.tar.gz \
    && tar -C /usr/local -xzf "/tmp/go1.19.2.linux-amd64.tar.gz"

ENV GOPATH /go
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH"

COPY main.go .

RUN go build -o main main.go

RUN apt-get install -y containerd

EXPOSE 8090

ENTRYPOINT [ "./main" ]


확장된 CPU 및 메모리 제한(minikube 구현에서 얻은 CPU 및 메모리의 크기)으로 빌드하고 실행하므로 내부에서 생성할 포드에 충분합니다.

sudo docker build . -t containerd_test
❯ sudo docker run -it --memory="2900MB" --cpus="2" -p 8090:8090 containerd_test
❯ sudo docker ps
CONTAINER ID   IMAGE             COMMAND    CREATED          STATUS          PORTS                                       NAMES
e26fd47de621   containerd_test   "./main"   25 seconds ago   Up 24 seconds   0.0.0.0:8090->8090/tcp, :::8090->8090/tcp   reverent_solomon
❯ sudo docker exec e26 ctr c ls
CONTAINER    IMAGE    RUNTIME


보시다시피 HTTP 서버가 실행 중이고 containerd 프로세스도 실행 중입니다.


이제 에이전트를 빌드해야 합니다. 팟(Pod) 생성의 기본 기능은 이미 설정되어 있고 일부 API 엔드포인트만 있으면 됩니다.
REST API에 echo framework을 사용하여 다양한 API 엔드포인트를 구현합니다. 즉, 포드 생성을 위한 POST, 삭제를 위한 DELETE, 포드 가져오기 및 포드 로그 가져오기를 위한 GET입니다.
에이전트 바이너리의 주요 기능부터 시작하겠습니다. 컨테이너 프로세스를 시작하고 echo REST API를 구성하는 pkg/agent/agent.go에 있습니다.

package main

import (
    "fmt"
    "log"
    "os"
    "os/exec"

    "github.com/jonatan5524/own-kubernetes/pkg/agent/api"
    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
)

func initRoutes(e *echo.Echo) {
    e.POST("/pod", api.CreatePod)
    e.GET("/pod/:id/log", api.LogPod)
    e.GET("/pod", api.GetAllPods)
    e.DELETE("/pod/:id", api.DeletePod)
}

func initMiddlewares(e *echo.Echo) {
    e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
        Format: "method=${method}, uri=${uri}, status=${status}\n",
    }))

    e.HTTPErrorHandler = func(err error, c echo.Context) {
        c.Logger().Error(err)

        e.DefaultHTTPErrorHandler(err, c)
    }
}

func startContainerd() {
    cmd := exec.Command("containerd")
    cmd.Stdout = os.Stdout
    err := cmd.Start()
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("containerd run on %d", cmd.Process.Pid)
}

func main() {
    startContainerd()

    e := echo.New()

    initMiddlewares(e)
    initRoutes(e)

    e.Logger.Fatal(e.Start(fmt.Sprintf(":%s", api.PORT)))
}


Kubernetes 에이전트에서 사용되는 것과 동일한 포트이기 때문에 에이전트 API에 포트 10250을 사용했습니다.
다음은 포드 끝점을 처리할 다양한 기능을 포함하는 pkg/agent/api/pod.go입니다.

package api

import (
    "net/http"

    "github.com/jonatan5524/own-kubernetes/pkg/pod"
    "github.com/labstack/echo/v4"
)

type podDTO struct {
    ImageRegistry string `json:"image registry"`
    Name          string `json:"name"`
}

func CreatePod(c echo.Context) error {
    podDto := new(podDTO)
    if err := c.Bind(podDto); err != nil {
        return err
    }

    id, err := pod.NewPodAndRun(podDto.ImageRegistry, podDto.Name)
    if err != nil {
        return err
    }

    return c.JSON(http.StatusCreated, podDTO{
        ImageRegistry: podDto.ImageRegistry,
        Name:          id,
    })
}

func LogPod(c echo.Context) error {
    logs, err := pod.LogPod(c.Param("id"))
    if err != nil {
        return err
    }

    return c.String(http.StatusOK, logs)
}

func GetAllPods(c echo.Context) error {
    pods, err := pod.ListRunningPods()
    if err != nil {
        return err
    }

    return c.JSON(http.StatusCreated, pods)
}

func DeletePod(c echo.Context) error {
    if _, err := pod.KillPod(c.Param("id")); err != nil {
        return err
    }

    return c.NoContent(http.StatusNoContent)

}


그리고 위에서 언급했듯이 노드 컨테이너의 노드 이미지를 생성하고 있으며 Dockerfile은 프로젝트 루트에 있으므로 모든 프로젝트 폴더를 복사할 수 있습니다.

# Dockerfile for node image
FROM ubuntu

WORKDIR /agent

RUN apt-get update \
    && apt-get install -y wget git gcc \
    && wget -P /tmp https://go.dev/dl/go1.19.2.linux-amd64.tar.gz \
    && tar -C /usr/local -xzf "/tmp/go1.19.2.linux-amd64.tar.gz"

ENV GOPATH /go
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH"

RUN apt-get install -y containerd

COPY go.mod .
COPY go.sum .
RUN go mod download  

COPY . .

RUN go build -o main pkg/agent/agent.go

EXPOSE 8090

ENTRYPOINT [ "./main" ]


작업을 빌드하고 테스트해 봅시다!
이전 요구 사항으로 컨테이너 노드를 빌드하고 실행합니다.

sudo docker build -t containerd_test .sudo docker run -it --memory="2900MB" --cpus="2" -p 10250:10250 --privileged --name test --rm containerd_test


이제 Redis 포드 생성 요청을 보내면 다음과 같습니다.

❯ curl -X POST localhost:10250/pod -H 'Content-Type: application/json' -d '{"name": "redis", "image registry": "docker.io/library/redis:alpine"}'
{"image registry":"docker.io/library/redis:alpine","name":"redis-c5ad79db-0e8e-4526-a9d1-366cefd0e9d5"}


포드가 생성되었습니다!
에이전트 로그에서:

2022/10/10 15:36:07 pod created: redis-c5ad79db-0e8e-4526-a9d1-366cefd0e9d5
2022/10/10 15:36:07 starting po


다른 모든 엔드포인트도 작동하는지 테스트하고 확인할 수 있습니다.

❯ curl localhost:10250/pod
["redis-c5ad79db-0e8e-4526-a9d1-366cefd0e9d5"]

❯ curl localhost:10250/pod/redis-c5ad79db-0e8e-4526-a9d1-366cefd0e9d5/log
1:C 10 Oct 2022 15:36:07.757 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 10 Oct 2022 15:36:07.757 # Redis version=7.0.5, bits=64, commit=00000000, modified=0, pid=1, just started
...
❯ curl -X DELETE  localhost:10250/pod/redis-c5ad79db-0e8e-4526-a9d1-366cefd0e9d5


모든 것이 작동합니다!


다음 기사에서는 포드와 같은 명령을 사용하여 노드에서 노드 생성 및 기타 방법을 자동화하는 데 중점을 둘 것입니다.

전체 소스 코드는 here 찾을 수 있으며 변경 사항은 pkg/agent 및 Dockerfile에 있습니다.

좋은 웹페이지 즐겨찾기