This page contains information related to upcoming products, features, and functionality. It is important to note that the information presented is for informational purposes only. Please do not rely on this information for purchasing or planning purposes. The development, release, and timing of any products, features, or functionality may be subject to change or delay and remain at the sole discretion of GitLab Inc.
Status Authors Coach DRIs Owning Stage Created
proposed devops verify -

Runner 통합

비 목표

이 제안은 Step Runner 바이너리를 대상 환경으로 배포하거나, 아래에 설명된 Step Runner gRPC 서비스를 시작하는 것과 같은 것은 다루지 않습니다. 이 제안의 나머지 부분은 Step Runner 바이너리가 대상 환경에 존재하고 gRPC 서비스가 실행되고 로컬 소켓에서 수신되어 있다고 가정합니다. 비슷하게, 이 제안은 Step Runner 서비스의 수명주기를 다루지 않으며, 서비스가 종료되는 경우와 업그레이드와 같은 사항을 어떻게 다룰지에 대해서도 다루지 않습니다.

관련된 청사진은 배포 및 수명주기 관리 를 참조하세요.

단계 서비스 gRPC 정의

Step Runner 서비스 gRPC 정의는 다음과 같습니다:

service StepRunner {
    rpc Run(RunRequest) returns (RunResponse);
    rpc FollowSteps(FollowStepsRequest) returns (stream FollowStepsResponse);
    rpc FollowLogs(FollowLogsRequest) returns (stream FollowLogsResponse);
    rpc Finish(FinishRequest) returns (FinishResponse);
    rpc Status(StatusRequest) returns (StatusResponse);
}

message Variable {
    string key = 1;
    string value = 2;
    bool file = 3;
    bool masked = 4;
}

message Job {
    repeated Variable variables = 1;
    string job_id = 2;
    string pipeline_id = 3;
    string build_dir = 4;
    repeated string token_prefixes = 5;
}

message Masking {
    repeated string phrases = 1;
    repeated string token_prefixes = 2;
}

message RunRequest {
    string id = 1;
    string work_dir = 2;
    map<string,string> env = 3;
    Masking masking = 4;
    Job job = 5;
    string steps = 6;
}

message RunResponse {
}

message FollowStepsRequest {
    string id = 1;
}

message FollowStepsResponse {
    StepResult result = 1;
}

message FollowLogsRequest {
    string id = 1;
    int32 offset = 2;
}

message FollowLogsResponse {
    bytes data = 1;
}

message FinishRequest {
    string id = 1;
}

message FinishResponse {
}

message Status {
    string id = 1;
    bool finished = 2;
    int32 exit_code = 3;
    google.protobuf.Timestamp start_time = 4;
    google.protobuf.Timestamp end_time = 5;
}

message StatusRequest {
    string id = 1;
}

message StatusResponse {
    repeated Status jobs = 1;
}

Runner는 위의 gRPC 서비스를 통해 Step Runner와 상호작용합니다. 이 서비스는 실행 환경의 로컬 소켓에서 시작됩니다. Runner는 우선 실행기별 프로토콜을 통해 대상 환경에 연결한 다음, 제공된 proxy 명령을 사용하여 gRPC 서비스에 연결하고, Runner에서 Step RunnergRPC 요청을 터널링합니다(자세한 내용은 Proxy Command 참조). 이는 Nesting이 전용 Mac 인스턴스에서 gRPC 서비스를 제공하는 방식과 동일합니다. 이 서비스에는 Run, FollowSteps, FollowLogs, Finish, Status라는 다섯 가지 RPC가 있습니다.

Run은 단계의 초기 전달입니다. FollowSteps는 단계 결과 트레이스의 스트리밍 응답을 요청합니다. FollowLogs는 단계 실행 중에 실행되는 프로세스에 의해 작성된 출력(stdout/stderr)과 Step Runner 자체가 생성한 로그의 스트리밍 응답을 동일하게 요청합니다. Finish는 요청의 실행을 중단하고 가능한 한 빨리 리소스를 정리합니다. Status는 지정된 작업의 상태를 나열하며, 지정된 작업이 없는 경우 Step Runner 서비스 내의 모든 활성 작업(완료된 작업을 포함하지만 Finish되지는 않음)을 나열합니다. 예를 들어, Status는 크래시 후 Runner가 복구하는 데 사용될 수 있습니다.

Step Runner gRPC 서비스는 여러 Run 페이로드를 동시에 실행할 수 있습니다. 즉, 각 Run 호출은 그에 해당하는 새로운 고루틴을 시작하고 완료될 때까지 단계를 실행합니다. 동시에 여러 Run 호출을 할 수 있습니다.

단계가 실행되는 동안 단계 결과 트레이스와 하위 프로세스 로그를 GitLab Runner로 스트리밍할 수 있습니다. 이를 통해 Runner(또는 다른 호출자)는 단계 결과 트레이스(FollowSteps) 수준에서 실행을 따르는 것과 하위 프로세스 및 Step Runner 로그(FollowLogs)를 작성한 방식으로 실행을 따를 수 있습니다. 로그는 특정 형식으로 작성되며, 민감한 토큰은 스트리밍되기 전에 Step Runner에서 마스킹됩니다.

Status를 제외한 모든 API는 동일함을 보장하는 멱등성을 가집니다. 즉, 동일한 매개변수로 동일한 API에 대해 여러 호출을 한 경우 동일한 결과를 반환해야 합니다. 예를 들어, 동일한 id로 여러 차례 Run을 호출할 경우 첫 번째 호출만 작업 요청의 처리가 시작되어야 하며, 후속 호출은 성공 상태를 반환하지만 그 외에는 아무것도 수행해서는 안 됩니다. 마찬가지로, 동일한 id로 여러 차례 Finish를 호출하면 첫 번째 호출에서 관련 작업을 종료하고 제거하고, 후속 호출에서는 아무것도 수행해서는 안 됩니다.

서비스는 클라이언트가 잘 행동할 것이라고 가정해서는 안 되며, Follow API 중 하나에서 절대 호출하지 않거나 일찍 연결을 끊는 클라이언트뿐만 아니라, 대응하는 Run 요청에 대해 절대 Finish를 호출하지 않는 클라이언트도 처리할 수 있어야 합니다. 이를 위해 Step Runner 프로세스는 일정 간격으로 작동하지 않거나 오랫동안 작동된 작업을 식별하고 정리합니다. 작동하지 않는 작업은 몇 시간 전에 완료되었거나(Finish되지는 않았음)로 지정된 작업일 수 있습니다. 오랫동안 작동된 작업은 해당 기간동안 실행된 일련의 작업(아마도 출력을 생성하지 않은)일 수 있습니다.

마지막으로, 아래 실행기 실행기에 단계를 통합하는 데 도움이 되도록, 단계가 Run/Follow*/Finish API를 실행하는 클라이언트 라이브러리를 제공하고 이벤트 중에 Follow* 호출의 연결을 처리하도록 권장합니다.

RunRequest 매개변수

단계들은 RunRequest.Steps 필드에 step.go 의 JSON 직렬화 버전으로 Step Runner로 전달됩니다. Runner 자체에서는 단계 정의를 처리할 필요가 없습니다. id 필드는 Step Runner 서비스에서 실행 중인 각 요청을 고유하게 식별합니다. RunRequest.Env 필드는 각 단계가 실행될 때 환경 변수를 보관합니다.

옵션인 Job 매개변수는 해당 CI 작업에서 선택된 매개변수를 포함할 것입니다. Job에는 해당 CI 작업의 빌드 디렉터리가 포함될 것이며, Job.BuildDirRunRequest.WorkDir로 복사되어야 하며, 요청의 모든 단계를 유지하기 위해 그 디렉터리에서 호출되어야 합니다. RunRequest는 또한 CI 작업의 환경 변수(즉, CI 구성의 작업 및 전역 수준에서 정의된 variables)를 포함할 것입니다. Runner에서 RunRequest를 수행할 때, 변수는 Job.Variables에 포함되어야 하며, RunRequest.Env는 비워두어야 합니다. 실행 요청이 처리될 때, 파일 타입의 변수는 파일에 작성될 것이며, 변수는 확장되어 RunRequest.Env에 복사되며, 나머지 요청에서 Job 필드는 버릴 것입니다. 변수는 Step Runner 서비스에서 환경 변수나 경로와 같은 실행 환경의 객체를 참조할 수 있기 때문에, 수행시 변수가 확장되어야 하며, 예를 들어 파일 유형의 변수는 전통적인 Runner 작업 실행과 같은 경로에 쓰여야 합니다. 비슷하게, Job.Variables에서 마스킹되어야 하는 구절들은 추출되어 Masking.Phrases로 사용되며, Job.TokenPrefixesMasking.TokenPrefixes로 복사되어야 합니다.

단계를 실행하려는 Runner 이외의 클라이언트는 Job 필드를 생략할 수 있으며, 이 경우 MaskingEnv 필드는 호출자에 의해 직접 채워져야 합니다.

로그 형식

FollowLogs API에서 발행된 로그 라인은 다음 형식을 가져야합니다.

    <timestamp> <stream> <stdout/stderr> <append flag> <message>

이것은 동일한 로그 형식이 이 Merge Request에서 실행으로 도입되었습니다. 이 형식으로 서식이 지정된 원시 로그 메시지가 포맷되기 전에 민감한 변수나 토큰을 마스킹하는 책임은 Step Runner에 있을 것입니다. 변수 마스킹에 사용되는 라이브러리는 GitLab RunnerStep Runner 사이에서 공유되어야 합니다. (관련 모듈들)를 확인하세요.

프록시 명령

Step Runner 바이너리에는 (일반적으로 텍스트 기반) stdin/stdout/stderr 기반 프로토콜에서 데이터를 프록시하는 명령이 포함될 것입니다. 이 명령은 gRPC 서비스와 동일한 호스트에서 실행되며, stdin에서 입력을 읽어 이를 로컬 소켓을 통해 gRPC 서비스로 전달하고, 동일한 소켓을 통해 gRPC 서비스로부터의 출력을 받아들이며, 이를 stdout/stderr를 통해 클라이언트에게 전달합니다. 이 명령은 클라이언트(예: Runner)가 stdin/stderr/stdout 기반 프로토콜을 통해 gRPC 서비스로 투명하게 터널링할 수 있도록 하여 Docker 이미지에서 Step Runner 서비스의 gRPC 포트를 노출할 필요를 제거하고 VM에서 SSH 포트 포워딩을 설정하는 번거로움을 없애주며, Runner가 SSH 및 docker exec와 같은 이미 구축된 프로토콜을 사용하여 Step Runner와 상호작용할 수 있도록 할 것입니다. stdoutStep Runner 서비스에서의 응답 작성에 예약되어야 하며, stderrproxy 명령 자체에서 발생하는 오류를 위해 예약되어야 합니다.

실행자

다음은 GitLab Runner가 각 Runner 실행자에서 Step Runner에 연결하는 방법입니다.

인스턴스

인스턴스 실행자는 현재와 동일하게 SSH를 통해 액세스됩니다. 그러나 명령을 파이핑하는 대신 프록시 명령을 호출하고, 이 명령은 다시 알려진 위치에 있는 Step Runner 소켓에 연결됩니다. 그런 다음 Runner는 직접 gRPC 호출을 수행하고 SSH 연결을 통해 gRPC 서비스로 투명하게 터널링할 수 있습니다. 이는 전용 Mac 인스턴스에서 Runner가 VM을 만들기 위해 Nesting 서버를 호출하는 방식과 동일합니다.

이는 Step Runner가 작업 실행 환경에 존재하고 시작되어 있어야 하는 것을 요구합니다.

도커

Step Runner가 존재하고 gRPC 서비스가 실행 중인 요구는 도커 실행자(및 docker-autoscaler)에 대해서도 마찬가지입니다. 그러나 컨테이너 내부의 gRPC 서비스에 연결하기 위해 Runner는 컨테이너로 docker exec를 수행하고 컨테이너 내부에서 gRPC 서비스로 연결하기 위해 프록시 명령을 실행할 것입니다. 클라이언트는 그런 다음 docker execstdin에 쓸 수 있으며, 이는 투명하게 gRPC 서비스로 프록시될 것이며, 그의 stdout/stderr에서는 gRPC 서비스로부터의 응답이 포함될 것입니다.

쿠버네티스

쿠버네티스 노드의 Kubelet은 실행중인 Pod의 컨테이너에서 프로세스를 시작할 exec API를 노출합니다. 이를 사용하여 호출자가 Pod 내부에서 gRPC 호출을 할 수 있는 브릿지 프로세스를 exec create하여 연결할 것입니다. 이는 도커 실행자와 유사하게 Runner가 gRPC 서비스에 대한 연결을 수행할 수 있도록 합니다.

이 보호된 Kubelet API에 액세스하기 위해서는 Pod에 대한 쿠버네티스 API를 사용해야 합니다. 호출자는 /exec로 끝나는 pod의 URL에 POST할 수 있으며, 그 후에 이를 양방향 바이트 스트리밍을 위한 SPDY 프로토콜로 협상할 수 있습니다. 따라서 GitLab Runner는 쿠버네티스 API를 사용하여 Step Runner 서비스에 연결하고 작업 페이로드를 전달할 수 있습니다.

이것은 kubectl exec가 작동하는 방식과 동일합니다. 사실, SPDY 협상과 같은 대부분의 내부 기능은 client-go 라이브러리로 제공됩니다. 따라서 Runner는 Kubectl로 쉘에 직접 셸을 띄우는 대신에 필요한 라이브러리를 가져와서 쿠버네티스 API에 직접 호출할 수 있습니다.

역사적으로 쿠버네티스 실행자의 약점 중 하나는 단일 exec를 통해 전체 작업을 실행하는 것이었습니다. 이를 완화하기 위해 Runner는 기존의 셸 프로세스에 “다시 연결”할 수 있는 attach 명령을 사용합니다.

그러나 이는 Step Runner에 대해서는 필요하지 않습니다. 왜냐하면 exec는 장시간 실행되는 gRPC 프로세스로의 브릿지를 구축하기 때문입니다. 연결이 끊기면 Runner는 또 다른 연결을 exec하여 계속해서 follow와 같은 RPC 호출을 수행할 것입니다.