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는 먼저 대상 환경에 executor별 프로토콜을 통해 연결한 다음 제공된 proxy 명령어를 사용하여 gRPC 서비스에 연결하고, Runner에서 Step Runner로 gRPC 요청을 투명하게 터널링합니다(자세한 내용은 프록시 명령어 참조). 이는 Nesting이 전용 Mac 인스턴스에서 gRPC 서비스를 제공하는 방식과 동일합니다. 이 서비스에는 Run, FollowSteps, FollowLogs, Finish, Status라는 다섯 개의 RPC가 있습니다.

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

Step Runner gRPC 서비스는 여러 Run 페이로드를 동시에 실행할 수 있습니다. 다시 말해, Run을 호출할 때마다 새로운 고루틴이 시작되어 그 단계를 실행합니다. 동시에 여러 개의 Run을 호출할 수 있습니다.

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

Status를 제외한 모든 API는 멱등성을 가지며, 동일한 매개변수로 동일한 API 호출을 여러 번 한 경우 동일한 결과를 반환해야 합니다. 예를 들어 Run이 동일한 id로 여러 번 호출된 경우 첫 번째 호출만 작업 요청을 처리를 시작하고, 후속 호출은 성공 상태를 반환하지만 그 외에는 아무것도 하지 않아야 합니다. 마찬가지로 동일한 idFinish를 여러 번 호출하면 첫 번째 호출에서 해당 작업을 완료하고 제거하며, 후속 호출은 아무 작업도 수행하지 않아야 합니다.

서비스는 클라이언트가 잘 행동하지 않을 것으로 가정하고, 임산을 종료하지 않거나 Finish를 호출하지 않고 해당 Run 요청을 조기에 연결 해제하는 클라이언트를 처리할 수 있어야 합니다. 이를 위해 Step Runner 프로세스는 일정 간격으로 스캔하여 만료 또는 비정상적으로 실행되고 있는(중단되지 않은) 작업을 식별하고 정리할 수 있어야 합니다. 만료된 작업은 특정 시간 전에(그리고 Finish되지 않았을 경우) 완료된 작업일 수 있습니다. 비정상적으로 실행되는 작업은 일정 기간 동안(아마도 아웃풋을 생성하지 않은) 실행되는 작업일 수 있습니다.

마지막으로 아래의 Runner 실행자들에 단계를 통합하기 쉽도록, 단계가 Run/Follow*/Finish API를 실행하고 Follow* 호출이 연결성 문제로 연결이 끊어질 경우 Step Runner 서비스에 다시 연결하는 클라이언트 라이브러리를 제공하는 것이 권장됩니다.

RunRequest 매개변수

단계는 Runner로 전달되는 RunRequest.Steps 필드 내에서 step.go의 JSON 직렬화 버전으로 Step 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 필드는 삭제되어야 합니다. 변수는 실행 환경 내의 개체를 참조할 수 있기 때문에, 파일 유형의 변수 또한 전통적인 Runner 작업 실행과 동일한 경로에 쓰여져야 합니다. 마찬가지로, Job.Variables에서 마스킹할 구절이 추출되어 Masking.Phrases를 작성하고, Job.TokenPrefixesMasking.TokenPrefixes로 복사되어야 합니다.

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

로그 형식

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

    <타임스탬프> <스트림> <표준출력/표준에러> <추가 플래그> <메시지>

이 형식은 이 Merge Request에서 Runner에 도입된 동일한 로그 형식입니다. 이 형식으로 메시지를 형식화하기 전에 민감한 변수나 토큰을 가려주는 작업은 Step Runner가 담당할 것입니다. 가려진 라이브러리는 GitLab RunnerStep Runner 간에 공유되어야 합니다.

마스킹

Step Runner는 민감한 변수나 토큰을 가릴 책임이 있을 것입니다. 이 작업은 위의 로그 형식으로 메시지를 형식화하기 전에 수행되어야 합니다. 변수를 가리기 위해 사용된 라이브러리는 GitLab RunnerStep Runner 간에 공유되어야 합니다. (관련 모듈들은 여기를 참조하세요).

프록시 명령

Step Runner 이진 파일에는 (일반적으로 텍스트 기반) stdin/stdout/stderr 기반 프로토콜에서 gRPC 서비스로 데이터를 프록시하는 명령이 포함될 것입니다. 이 명령은 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 서비스로부터의 응답을 쓰기 위해 예약되어야 하며, stderr프록시 명령 자체에서 발생하는 오류를 위해 예약되어야 합니다.

실행자

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

Instance

Instance 실행자는 오늘과 동일하게 SSH를 통해 접근됩니다. 그러나 명령어를 입력하는 대신 프록시 명령을 호출하여 Step Runner 소켓에 연결합니다. 그러면 Runner가 직접 gRPC 호출을 하고, SSH 연결을 통해 gRPC 서비스로 투명하게 터널링할 수 있습니다. 이는 Runner가 전용 Mac 인스턴스에서 Nesting 서버를 호출하여 VM을 만드는 방식과 동일합니다.

이는 Step Runner가 작업 실행 환경에 존재하고 시작되어 있어야 한다는 요구사항이 있습니다.

Docker

Step Runner가 존재하고 gRPC 서비스가 실행 중이라는 동일한 요구사항이 Docker 실행자(및 docker-autoscaler)에도 적용됩니다. 그러나 컨테이너 내부의 gRPC 서비스에 연결하려면 Runner가 컨테이너에 docker exec를 하고, gRPC 서비스에 연결하기 위해 프록시 명령을 실행해야 합니다. 그리고 클라이언트는 docker execstdin에 쓸 수 있으며, 이는 투명하게 gRPC 서비스로 프록시될 것이며, 그것의 stdout/stderr에서는 gRPC 서비스로부터의 응답이 포함될 것입니다.

Kubernetes

Kubernetes 노드의 Kubelet은 실행 중인 Pod의 컨테이너에서 프로세스를 시작할 수 있는 exec API를 노출합니다. 이를 사용하여 호출자가 Pod 내에서 gRPC 호출을 할 수 있는 브릿지 프로세스를 exec create할 것입니다. 이는 Docker 실행자와 유사한 방식으로 동작합니다.

보호된 Kubelet API에 접근하기 위해 우리는 Pod에 exec 하위 리소스를 제공하는 Kubernetes API를 사용해야 합니다. 호출자는 Pod의 URL에 /exec를 추가하여 POST하고, 그 후에 양방향 바이트 스트리밍을 위해 SPDY 프로토콜까지 연결해야 합니다. 따라서 GitLab Runner는 Kubernetes API를 사용하여 Step Runner 서비스에 연결하고 작업 페이로드를 전달할 수 있습니다.

이는 kubectl exec 동작 방식과 동일합니다. 사실 대부분의 내부 요소(예: SPDY 협상)는 client-go 라이브러리로 제공됩니다. 따라서 Runner는 kubectl을 외부에서 실행하는 대신에 필요한 라이브러리를 가져와서 Kubernetes API에 직접 호출할 수 있습니다.

과거의 Kubernetes Executor의 약점 중 하나는 단일 exec를 통해 전체 작업을 실행하는 것이었습니다. 이를 완화하기 위해 Runner는 기존의 쉘 프로세스에 “재연결”할 수 있는 attach 명령을 대신 사용합니다.

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