코드 분석을 통한 ServiceAccount 로직 이해
- 쿠버네티스에서 네임스페이스를 생성하면 자동으로 default 라는 이름의 서비스 어카운트가 생성됨.
- 이 작업은 쿠버네티스 내부의 ServiceAccountsController에 의해 처리됨.
ServiceAccountsController 작동 방식
1. ServiceAccountsController는 kube-controller-manager의 일부로 실행됩니다.
2. ServiceAccountsController는 모든 네임스페이스에 default 서비스 어카운트가 존재하는지 확인하고, 없을 경우 자동으로 생성합니다.
3. 코드 내에서 DefaultServiceAccountsControllerOptions() 함수는 기본적으로 생성해야 할 서비스 어카운트 목록(기본적으로 default 서비스 어카운트)을 정의합니다.
ServiceAccount - Create 코드 부분
15. [작업 처리 루프] - runWorker 및 processNextWorkItem 함수
실행순서: Run() → go wait.UntilWithContext() → runWorker() → processNextWorkItem()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `for c.processNextWorkItem(ctx) { }`
설명: 워커 고루틴이 지속적으로 큐를 모니터링하면서 작업 처리 루프 실행
15-1. [작업 가져오기] - 큐에서 작업 가져오기
15-2. [작업 처리] - syncHandler(syncNamespace) 호출
15-3. [ServiceAccount 생성] - syncNamespace 함수 내부 로직
15-4. [실제 생성 작업] - Kubernetes API 호출
실행순서: syncHandler() → syncNamespace() → Create()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `if _, err := c.client.CoreV1().ServiceAccounts(ns.Name).Create(ctx, &sa, metav1.CreateOptions{}); ...`
설명: Kubernetes API 서버에 요청하여 "default" 서비스어카운트 실제 생성
코드 호출 흐름
진입점: kube-controller-manager 시작
main()
## 파일위치 : cmd/kube-controller-manager/controller-manager.go
## 함수 : main()
main() → NewControllerManagerCommand()
## 파일위치 : cmd/kube-controller-manager/app/controllermanager.go
## 함수 : NewControllerManagerCommand()
컨트롤러 매니저 실행
RunE
[컨트롤러 매니저 실행]
## 파일위치 : cmd/kube-controller-manager/app/controllermanager.go
## 실행 순서 : NewControllerManagerCommand() → RunE
## 함수 : RunE
## 함수 설명 :
RunE → Run(ctx, c.Complete())
1. [컴포넌트 초기화] - createClientBuilders() 클라이언트 빌더 생성
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → createClientBuilders()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `clientBuilder, rootClientBuilder := createClientBuilders(c)`
설명: 컨트롤러에서 사용할 클라이언트 빌더를 생성
2. [컴포넌트 초기화] - newServiceAccountTokenControllerDescriptor() 실행
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → newServiceAccountTokenControllerDescriptor()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `saTokenControllerDescriptor := newServiceAccountTokenControllerDescriptor(rootClientBuilder)`
설명: 서비스 어카운트 토큰 컨트롤러 디스크립터 생성
3. [실행준비] - run 함수 정의 (아직 실행되지 않음)
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete())
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `run := func(ctx context.Context, controllerDescriptors map[string]*ControllerDescriptor) { ... }`
설명: 컨트롤러 실행을 위한 함수 정의
4. [실행준비] - leader election 필요한지 판단
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete())
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `if !c.ComponentConfig.Generic.LeaderElection.LeaderElect { ... }`
설명: 리더 선출 필요 여부 확인
5. [컨트롤러 등록] - NewControllerDescriptors() 실행
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → NewControllerDescriptors()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `controllerDescriptors := NewControllerDescriptors()`
설명: 모든 컨트롤러 디스크립터를 등록하는 맵 생성
5. [컨트롤러 등록] - NewControllerDescriptors() 실행
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → NewControllerDescriptors()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `controllerDescriptors := NewControllerDescriptors()`
설명: 모든 컨트롤러 디스크립터를 등록하는 맵 생성
a. [컨트롤러 등록] - ServiceAccountTokenController 디스크립터 등록
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → NewControllerDescriptors() → register(newServiceAccountTokenControllerDescriptor(nil))
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `register(newServiceAccountTokenControllerDescriptor(nil))`
설명: 토큰 컨트롤러 디스크립터 등록
5. [컨트롤러 등록] - NewControllerDescriptors() 실행
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → NewControllerDescriptors()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `controllerDescriptors := NewControllerDescriptors()`
설명: 모든 컨트롤러 디스크립터를 등록하는 맵 생성
a. [컨트롤러 등록] - ServiceAccountTokenController 디스크립터 등록
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → NewControllerDescriptors() → register(newServiceAccountTokenControllerDescriptor(nil))
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `register(newServiceAccountTokenControllerDescriptor(nil))`
설명: 토큰 컨트롤러 디스크립터 등록
b. [컨트롤러 등록] - ServiceAccountController 디스크립터 등록
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → NewControllerDescriptors() → register(newServiceAccountControllerDescriptor())
파일: [cmd/kube-controller-manager/app/core.go]
코드: `register(newServiceAccountControllerDescriptor())`
설명: 서비스 어카운트 컨트롤러 디스크립터 등록
c. [컨트롤러 등록] - ServiceAccountController 디스크립터 생성
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → NewControllerDescriptors() → newServiceAccountControllerDescriptor()
파일: [cmd/kube-controller-manager/app/core.go]
코드: `return &ControllerDescriptor{name: names.ServiceAccountController, aliases: []string{"serviceaccount"}, initFunc: startServiceAccountController}`
설명: 서비스 어카운트 컨트롤러 이름, 별칭, 초기화 함수 설정
중요
이 단계에서는 initFunc로 startServiceAccountController 함수를 참조만 등록하고 실제로는 실행되지 않음.
startServiceAccountController 함수가 실행되지 않고 포인터만 저장됨.
`go sac.Run(ctx, 1)` 코드도 아직 실행되지 않음
나중에 10번 단계(StartController 호출 시)에서 실행됨
6. [컨트롤러 설정] - saTokenControllerDescriptor를 맵에 추가
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete())
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `controllerDescriptors[names.ServiceAccountTokenController] = saTokenControllerDescriptor`
설명: 토큰 컨트롤러 디스크립터를 컨트롤러 맵에 추가
7. [실행 & 컨트롤러 디스크립터 전달] - run 함수 호출시 컨트롤러 디스크립터 맵 전달
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `run(ctx, controllerDescriptors)`
설명: 5번에서 생성한 컨트롤러 디스크립터 맵을 run 함수에 전달하여 실행
8. [컨텍스트 생성] - CreateControllerContext() 실행
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → CreateControllerContext()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `controllerContext, err := CreateControllerContext(ctx, c, rootClientBuilder, clientBuilder)`
설명: 컨트롤러 컨텍스트 생성 (인포머, 클라이언트 등 포함)
9. [컨트롤러 시작] - StartControllers() 실행
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `err := StartControllers(ctx, controllerContext, controllerDescriptors, unsecuredMux, healthzHandler)`
설명: 7번에서 전달받은 컨트롤러 디스크립터 맵을 사용하여 모든 컨트롤러 시작
a. [컨트롤러 순회] - 등록된 모든 컨트롤러 순회
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `for _, controllerDesc := range controllerDescriptors { ... }`
설명: 5번에서 등록한 모든 컨트롤러 디스크립터를 순회하며 각각 시작
b. [개별 컨트롤러 시작] - StartController 호출
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers() → StartController()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `check, err := StartController(ctx, controllerCtx, controllerDesc, unsecuredMux)`
설명: 각 컨트롤러 디스크립터에 대해 StartController 함수 호출 (ServiceAccountController 포함)
b-1. [초기화 함수 가져오기] - 디스크립터에서 초기화 함수 가져오기
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers() → StartController()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `initFunc := controllerDescriptor.GetInitFunc()`
설명: 디스크립터에서 초기화 함수 참조를 가져옴 (ServiceAccountController의 경우 5b에서 저장한 startServiceAccountController)
b-2. [초기화 함수 실행] - ServiceAccountController의 초기화 함수 실행
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers() → StartController() → startServiceAccountController()
파일: [cmd/kube-controller-manager/app/core.go]
코드: `ctrl, started, err := initFunc(klog.NewContext(ctx, ...), controllerCtx, controllerName)`
설명: 5b에서 저장했던 startServiceAccountController 함수가 실제로 호출되어 실행됨
b-3. [ServiceAccount 컨트롤러 생성] - NewServiceAccountsController 호출
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers() → StartController() → startServiceAccountController() → NewServiceAccountsController()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `sac, err := serviceaccountcontroller.NewServiceAccountsController(...)`
설명: ServiceAccountsController 인스턴스 생성 및 이벤트 핸들러 구성
b-4. [ServiceAccount 컨트롤러 실행] - 비동기 실행
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers() → StartController() → startServiceAccountController()
파일: [cmd/kube-controller-manager/app/core.go]
코드: `go sac.Run(ctx, 1)`
설명: ServiceAccountsController를 고루틴으로 비동기 실행 (1: 워커 수)
9. [컨트롤러 시작] - StartControllers() 실행
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `err := StartControllers(ctx, controllerContext, controllerDescriptors, unsecuredMux, healthzHandler)`
설명: 7번에서 전달받은 컨트롤러 디스크립터 맵을 사용하여 모든 컨트롤러 시작
9. [컨트롤러 시작] - StartControllers() 실행
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `err := StartControllers(ctx, controllerContext, controllerDescriptors, unsecuredMux, healthzHandler)`
설명: 7번에서 전달받은 컨트롤러 디스크립터 맵을 사용하여 모든 컨트롤러 시작
a. [특별 컨트롤러] - ServiceAccountTokenController 먼저 시작
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers() → StartController()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `check, err := StartController(ctx, controllerCtx, serviceAccountTokenControllerDescriptor, unsecuredMux)`
설명: 토큰 컨트롤러 우선 시작 (다른 컨트롤러에서 필요)
b. [컨트롤러 순회] - 등록된 모든 컨트롤러 순회
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `for _, controllerDesc := range controllerDescriptors { ... }`
설명: 5번에서 등록한 모든 컨트롤러 디스크립터를 순회하며 각각 시작
c. [개별 컨트롤러 시작] - StartController 호출
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers() → StartController()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `check, err := StartController(ctx, controllerCtx, controllerDesc, unsecuredMux)`
설명: 각 컨트롤러 디스크립터에 대해 StartController 함수 호출 (ServiceAccountController 포함)
9. [컨트롤러 시작] - StartControllers() 실행
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `err := StartControllers(ctx, controllerContext, controllerDescriptors, unsecuredMux, healthzHandler)`
설명: 7번에서 전달받은 컨트롤러 디스크립터 맵을 사용하여 모든 컨트롤러 시작
a. [특별 컨트롤러] - ServiceAccountTokenController 먼저 시작
b. [컨트롤러 순회] - 등록된 모든 컨트롤러 순회
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `for _, controllerDesc := range controllerDescriptors { ... }`
설명: 5번에서 등록한 모든 컨트롤러 디스크립터를 순회하며 각각 시작
c. [개별 컨트롤러 시작] - StartController 호출
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers() → StartController()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `check, err := StartController(ctx, controllerCtx, controllerDesc, unsecuredMux)`
설명: 각 컨트롤러 디스크립터에 대해 StartController 함수 호출 (ServiceAccountController 포함)
for _, controllerDesc
controllerDesc 변수가 중요함
9. [컨트롤러 시작] - StartControllers() 실행
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `err := StartControllers(ctx, controllerContext, controllerDescriptors, unsecuredMux, healthzHandler)`
설명: 7번에서 전달받은 컨트롤러 디스크립터 맵을 사용하여 모든 컨트롤러 시작
a. [특별 컨트롤러] - ServiceAccountTokenController 먼저 시작
b. [컨트롤러 순회] - 등록된 모든 컨트롤러 순회
c. [개별 컨트롤러 시작] - StartController 호출
c-1. [초기화 함수 가져오기] - 디스크립터에서 초기화 함수 가져오기
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers() → StartController()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `initFunc := controllerDescriptor.GetInitFunc()`
설명: 디스크립터에서 초기화 함수 참조를 가져옴 (ServiceAccountController의 경우 5b에서 저장한 startServiceAccountController)
9. [컨트롤러 시작] - StartControllers() 실행
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `err := StartControllers(ctx, controllerContext, controllerDescriptors, unsecuredMux, healthzHandler)`
설명: 7번에서 전달받은 컨트롤러 디스크립터 맵을 사용하여 모든 컨트롤러 시작
a. [특별 컨트롤러] - ServiceAccountTokenController 먼저 시작
b. [컨트롤러 순회] - 등록된 모든 컨트롤러 순회
c. [개별 컨트롤러 시작] - StartController 호출
c-1. [초기화 함수 가져오기] - 디스크립터에서 초기화 함수 가져오기
c-2. [초기화 함수 실행] - newServiceAccountControllerDescriptor 실행
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers() → StartController() → newServiceAccountControllerDescriptor → initFunc: startServiceAccountController 실행
파일: [cmd/kube-controller-manager/app/core.go]
코드: `ctrl, started, err := initFunc(klog.NewContext(ctx, ...), controllerCtx, controllerName)`
설명: 5b에서 저장했던 startServiceAccountController 함수가 실제로 호출되어 실행됨
9. [컨트롤러 시작] - StartControllers() 실행
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers()
파일: [cmd/kube-controller-manager/app/controllermanager.go]
코드: `err := StartControllers(ctx, controllerContext, controllerDescriptors, unsecuredMux, healthzHandler)`
설명: 7번에서 전달받은 컨트롤러 디스크립터 맵을 사용하여 모든 컨트롤러 시작
a. [특별 컨트롤러] - ServiceAccountTokenController 먼저 시작
b. [컨트롤러 순회] - 등록된 모든 컨트롤러 순회
c. [개별 컨트롤러 시작] - StartController 호출
c-1. [초기화 함수 가져오기] - 디스크립터에서 초기화 함수 가져오기
c-2. [초기화 함수 실행] - newServiceAccountControllerDescriptor 실행
c-3. [ServiceAccount 컨트롤러 생성] - NewServiceAccountsController 호출
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers() → StartController() → startServiceAccountController() → NewServiceAccountsController()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `sac, err := serviceaccountcontroller.NewServiceAccountsController(...)`
설명: ServiceAccountsController 인스턴스 생성 및 이벤트 핸들러 구성
c-4. [ServiceAccount 컨트롤러 실행] - 비동기 실행
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers() → StartController() → startServiceAccountController()
파일: [cmd/kube-controller-manager/app/core.go]
코드: `go sac.Run(ctx, 1)`
설명: ServiceAccountsController를 고루틴으로 비동기 실행 (1: 워커 수)
10. [ServiceAccount 컨트롤러 초기화] - startServiceAccountController() 실행
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers() → StartController() → startServiceAccountController()
파일: [cmd/kube-controller-manager/app/core.go]
코드: `sac, err := serviceaccountcontroller.NewServiceAccountsController(..., serviceaccountcontroller.DefaultServiceAccountsControllerOptions())`
설명: ServiceAccount 컨트롤러 초기화 함수가 4가지 주요 파라미터로 호출됨
- ServiceAccount 인포머: 클러스터의 모든 서비스어카운트 변경 감시
- Namespace 인포머: 클러스터의 모든 네임스페이스 변경 감시
- 클라이언트: API 서버와 통신하기 위한 클라이언트
- 기본 옵션: "default" 서비스어카운트를 포함한 옵션
a. [기본 옵션 설정] - DefaultServiceAccountsControllerOptions() 호출
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers() → StartController() → startServiceAccountController() → DefaultServiceAccountsControllerOptions()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `return ServiceAccountsControllerOptions{ServiceAccounts: []v1.ServiceAccount{{ObjectMeta: metav1.ObjectMeta{Name: "default"}}}}`
설명: 기본 옵션 생성 - "default"라는 이름의 서비스어카운트가 포함됨
11. [ServiceAccount 컨트롤러 생성] - NewServiceAccountsController() 실행
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers() → StartController() → startServiceAccountController() → NewServiceAccountsController()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `e := &ServiceAccountsController{client: cl, serviceAccountsToEnsure: options.ServiceAccounts, ...}`
설명: ServiceAccount 컨트롤러 객체 생성 및 이벤트 핸들러 등록
- serviceAccountsToEnsure: "default" 서비스어카운트 목록 저장
- 서비스어카운트 삭제 이벤트 핸들러 등록 (serviceAccountDeleted)
- 네임스페이스 추가/업데이트 이벤트 핸들러 등록 (namespaceAdded, namespaceUpdated)
12. [ServiceAccount 컨트롤러 실행] - Run() 함수 고루틴으로 시작
실행순서: NewControllerManagerCommand() → RunE() → Run(ctx, c.Complete()) → run() → StartControllers() → StartController() → startServiceAccountController() → go sac.Run(ctx, 1)
파일: [cmd/kube-controller-manager/app/core.go]
코드: `go sac.Run(ctx, 1)`
설명: core.go에서 ServiceAccount 컨트롤러를 비동기 고루틴으로 실행 (1은 워커 수)
13. [컨트롤러 초기화] - Run 함수 내부 실행
실행순서: startServiceAccountController() → go sac.Run() → ServiceAccountsController.Run()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `if !cache.WaitForNamedCacheSync("service account", ctx.Done(), c.saListerSynced, c.nsListerSynced) { return }`
설명: 인포머 캐시가 동기화될 때까지 대기 (실제 작업 시작 전 준비)
14. [워커 시작] - 워커 고루틴 시작
실행순서: startServiceAccountController() → go sac.Run() → ServiceAccountsController.Run()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `go wait.UntilWithContext(ctx, c.runWorker, time.Second)`
설명: 워커 고루틴 시작 - 1초마다 c.runWorker 함수를 주기적으로 호출
15. [작업 처리 루프] - runWorker 및 processNextWorkItem 함수
실행순서: Run() → go wait.UntilWithContext() → runWorker() → processNextWorkItem()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `for c.processNextWorkItem(ctx) { }`
설명: 워커 고루틴이 지속적으로 큐를 모니터링하면서 작업 처리 루프 실행
15. [작업 처리 루프] - runWorker 및 processNextWorkItem 함수
실행순서: Run() → go wait.UntilWithContext() → runWorker() → processNextWorkItem()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `for c.processNextWorkItem(ctx) { }`
설명: 워커 고루틴이 지속적으로 큐를 모니터링하면서 작업 처리 루프 실행
15-1. [작업 가져오기] - 큐에서 작업 가져오기
실행순서: runWorker() → processNextWorkItem() → queue.Get()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `key, quit := c.queue.Get()`
설명: 워커가 큐에서 처리할 네임스페이스 이름을 가져옴
15. [작업 처리 루프] - runWorker 및 processNextWorkItem 함수
실행순서: Run() → go wait.UntilWithContext() → runWorker() → processNextWorkItem()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `for c.processNextWorkItem(ctx) { }`
설명: 워커 고루틴이 지속적으로 큐를 모니터링하면서 작업 처리 루프 실행
15-1. [작업 가져오기] - 큐에서 작업 가져오기
15-2. [작업 처리] - syncHandler(syncNamespace) 호출
실행순서: runWorker() → processNextWorkItem() → syncHandler()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `err := c.syncHandler(ctx, key)`
설명: 가져온 네임스페이스 이름으로 syncNamespace 함수 호출
15. [작업 처리 루프] - runWorker 및 processNextWorkItem 함수
실행순서: Run() → go wait.UntilWithContext() → runWorker() → processNextWorkItem()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `for c.processNextWorkItem(ctx) { }`
설명: 워커 고루틴이 지속적으로 큐를 모니터링하면서 작업 처리 루프 실행
15-1. [작업 가져오기] - 큐에서 작업 가져오기
15-2. [작업 처리] - syncHandler(syncNamespace) 호출
15-3. [ServiceAccount 생성] - syncNamespace 함수 내부 로직
실행순서: processNextWorkItem() → syncHandler() → syncNamespace()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `for _, sa := range c.serviceAccountsToEnsure { ... }`
설명: serviceAccountsToEnsure에 포함된 각 서비스어카운트(여기서는 "default") 처리
15. [작업 처리 루프] - runWorker 및 processNextWorkItem 함수
실행순서: Run() → go wait.UntilWithContext() → runWorker() → processNextWorkItem()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `for c.processNextWorkItem(ctx) { }`
설명: 워커 고루틴이 지속적으로 큐를 모니터링하면서 작업 처리 루프 실행
15-1. [작업 가져오기] - 큐에서 작업 가져오기
15-2. [작업 처리] - syncHandler(syncNamespace) 호출
15-3. [ServiceAccount 생성] - syncNamespace 함수 내부 로직
15-4. [실제 생성 작업] - Kubernetes API 호출
실행순서: syncHandler() → syncNamespace() → Create()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `if _, err := c.client.CoreV1().ServiceAccounts(ns.Name).Create(ctx, &sa, metav1.CreateOptions{}); ...`
설명: Kubernetes API 서버에 요청하여 "default" 서비스어카운트 실제 생성
15. [작업 처리 루프] - runWorker 및 processNextWorkItem 함수
실행순서: Run() → go wait.UntilWithContext() → runWorker() → processNextWorkItem()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `for c.processNextWorkItem(ctx) { }`
설명: 워커 고루틴이 지속적으로 큐를 모니터링하면서 작업 처리 루프 실행
15-1. [작업 가져오기] - 큐에서 작업 가져오기
15-2. [작업 처리] - syncHandler(syncNamespace) 호출
15-3. [ServiceAccount 생성] - syncNamespace 함수 내부 로직
15-4. [실제 생성 작업] - Kubernetes API 호출
15-4-1. [클라이언트 호출 경로] - API 클라이언트 메서드 호출 체인
실행순서: c.client.CoreV1() → ServiceAccounts() → Create()
파일: [staging/src/k8s.io/client-go/kubernetes/typed/core/v1/core_client.go]
코드: `func (c *CoreV1Client) ServiceAccounts(namespace string) ServiceAccountInterface { return newServiceAccounts(c, namespace) }`
설명: CoreV1Client에서 네임스페이스에 맞는 ServiceAccount 클라이언트 인터페이스 반환
15. [작업 처리 루프] - runWorker 및 processNextWorkItem 함수
실행순서: Run() → go wait.UntilWithContext() → runWorker() → processNextWorkItem()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `for c.processNextWorkItem(ctx) { }`
설명: 워커 고루틴이 지속적으로 큐를 모니터링하면서 작업 처리 루프 실행
15-1. [작업 가져오기] - 큐에서 작업 가져오기
15-2. [작업 처리] - syncHandler(syncNamespace) 호출
15-3. [ServiceAccount 생성] - syncNamespace 함수 내부 로직
15-4. [실제 생성 작업] - Kubernetes API 호출
15-4-1. [클라이언트 호출 경로] - API 클라이언트 메서드 호출 체인
15-4-2. [인터페이스 구현체] - serviceaccounts 구조체 생성
실행순서: ServiceAccounts() → newServiceAccounts()
파일: [staging/src/k8s.io/client-go/kubernetes/typed/core/v1/serviceaccount.go]
코드: `return &serviceAccounts{ gentype.NewClientWithListAndApply[*corev1.ServiceAccount, ...] }`
설명: ServiceAccountInterface를 구현하는 serviceaccounts 객체 생성
15. [작업 처리 루프] - runWorker 및 processNextWorkItem 함수
실행순서: Run() → go wait.UntilWithContext() → runWorker() → processNextWorkItem()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `for c.processNextWorkItem(ctx) { }`
설명: 워커 고루틴이 지속적으로 큐를 모니터링하면서 작업 처리 루프 실행
15-1. [작업 가져오기] - 큐에서 작업 가져오기
15-2. [작업 처리] - syncHandler(syncNamespace) 호출
15-3. [ServiceAccount 생성] - syncNamespace 함수 내부 로직
15-4. [실제 생성 작업] - Kubernetes API 호출
15-4-1. [클라이언트 호출 경로] - API 클라이언트 메서드 호출 체인
15-4-2. [인터페이스 구현체] - serviceaccounts 구조체 생성
15-4-3. [API 호출] - Create 메서드 실행
실행순서: serviceAccounts.Create() → ClientWithListAndApply[T].Create() (상속) → Client[T].Create()
파일: [staging/src/k8s.io/client-go/gentype/client.go]
코드: `func (c *Client[T]) Create(ctx context.Context, obj T, opts metav1.CreateOptions) (T, error)`
설명: 제네릭 Client 구조체의 Create 메서드가 ServiceAccount 객체를 전달받아 API 서버로 요청 생성
15-4-4. [HTTP 요청] - REST 클라이언트 호출
실행순서: Create() → RESTClient.Post()
파일: [staging/src/k8s.io/client-go/rest/request.go]
코드: REST 클라이언트가 HTTP POST 요청 생성 및 전송
설명: `/api/v1/namespaces/{namespace}/serviceaccounts` 엔드포인트로 POST 요청 전송
15-4-5. [API 서버 처리] - 요청 처리 및 저장
실행경로: kube-apiserver → ServiceAccount 어드미션 컨트롤러 → etcd 저장
설명: API 서버가 요청을 검증한 후 ServiceAccount 객체를 etcd에 저장
특징: 이 부분은 클라이언트가 아닌 API 서버에서 처리됨
A. [이벤트 핸들러] - 네임스페이스 이벤트 발생 시 처리
실행순서: (인포머에서 이벤트 발생 시) → namespaceAdded() / namespaceUpdated()
파일: [pkg/controller/serviceaccount/serviceaccounts_controller.go]
코드: `c.queue.Add(namespace.Name)`
설명: 네임스페이스 생성/업데이트 이벤트 발생 시 해당 네임스페이스 이름을 작업 큐에 추가