InfoGrab Docs

Teleport로 리소스 자동 등록

요약

Teleport의 API를 사용하여 인프라의 리소스를 Teleport 클러스터에 자동으로 등록할 수 있습니다. Teleport는 이미 AWS, Azure, Google Cloud의 Kubernetes 클러스터 및 Amazon EC2의 서버 자동 검색을 지원합니다.

Teleport의 API를 사용하여 인프라의 리소스를 Teleport 클러스터에 자동으로 등록할 수 있습니다.

Teleport는 이미 AWS, Azure, Google Cloud의 Kubernetes 클러스터 및 Amazon EC2의 서버 자동 검색을 지원합니다. 다른 리소스 및 클라우드 공급자를 지원하려면 API를 사용하여 자체 워크플로를 작성할 수 있습니다.

이 가이드에서는 Teleport로 리소스를 자동으로 등록하는 데 사용할 수 있는 라이브러리를 시연합니다. 워크스테이션에서 로컬로 실행할 수 있는 예제를 사용합니다.

작동 방식#

자동 등록은 다음 단계로 구성됩니다:

  • 서비스 검색 솔루션(예: Kubernetes API 서버, Consul 또는 클라우드 공급자의 API)에서 인프라의 리소스를 조회합니다.
  • Teleport gRPC API를 사용하여 Teleport에 등록된 리소스를 조회합니다.
  • Teleport에 등록되지 않은 인프라의 리소스에 대해 Teleport API를 사용하여 새 Teleport 서비스를 시작하거나 기존 Teleport 서비스에 리소스를 등록합니다.
  • Teleport에는 등록되어 있지만 인프라에는 없는 리소스에 대해 Teleport API를 사용하여 리소스 등록을 해제하고, 필요한 경우 리소스를 프록시하는 Teleport 서비스를 제거합니다.
Warning

이 가이드에서 빌드하는 프로그램은 학습 도구로 의도된 것입니다. 프로덕션 Teleport 클러스터에 연결하지 마세요. 대신 데모 클러스터를 사용하세요.

사전 요구 사항#

  • A running Teleport cluster. If you want to get started with Teleport, sign up for a free trial or set up a demo environment.

  • The tctl and tsh clients.

    Installing `tctl` and `tsh` clients
    1. Determine the version of your Teleport cluster. The tctl and tsh clients must be at most one major version behind your Teleport cluster version. Send a GET request to the Proxy Service at /v1/webapi/find and use a JSON query tool to obtain your cluster version. Replace with the web address of your Teleport Proxy Service:

      $ TELEPORT_DOMAIN=
      $ TELEPORT_VERSION="$(curl -s https://$TELEPORT_DOMAIN/v1/webapi/find | jq -r '.server_version')"
      
    2. Follow the instructions for your platform to install tctl and tsh clients:

  • 워크스테이션에 Docker 설치. Docker 시작하기.
  • 워크스테이션에 Go 버전 [teleport.golang] 이상 설치. Go 다운로드 페이지를 참조하세요. 이 가이드를 완료하기 위해 Go에 익숙할 필요는 없지만, 프로덕션 준비가 된 Teleport 클라이언트 애플리케이션을 빌드하려면 Go 지식이 필요합니다.
Tip

데모 프로젝트 설정을 계획하지 않더라도, 이 가이드를 따라 Teleport 클러스터에 서비스를 자동으로 등록하는 데 사용할 수 있는 라이브러리, 유형, 함수를 확인할 수 있습니다.

1/5단계. Go 프로젝트 설정#

최소한의 API 클라이언트에 대한 소스 코드를 다운로드합니다:

$ git clone https://github.com/gravitational/teleport -b branch/v(=teleport.major_version=)
$ cd examples/service-discovery-api-client

이 가이드의 나머지 부분에서는 이 API 클라이언트를 설정하고 Teleport의 API를 사용하여 Teleport 애플리케이션 서비스 인스턴스를 외부 서비스 검색 솔루션과 동기화하는 방법을 살펴봅니다.

2/5단계. RBAC 리소스 정의#

Teleport의 gRPC API와 통신하는 클라이언트는 Auth 서비스에 인증하기 위해 Teleport 사용자와 역할의 아이덴티티를 가정하며, Auth 서비스는 해당 아이덴티티가 특정 API 작업을 수행할 권한을 부여합니다.

자신의 Teleport 사용자도 클라이언트의 자격 증명을 생성하기 위해 API 클라이언트의 사용자를 가장할 권한이 필요합니다.

클라이언트 애플리케이션을 위한 사용자 및 역할 생성#

클라이언트 애플리케이션은 토큰 생성, 애플리케이션 목록 조회, Auth 서비스에 등록된 애플리케이션 서비스 인스턴스 레코드 삭제 권한을 가진 사용자로 Teleport에 인증합니다. 사용자는 또한 모든 레이블(app_labels)의 애플리케이션에 접근할 권한을 가지며, 이는 애플리케이션 목록 조회에 필요합니다.

register-apps.yaml 파일에 다음 내용을 추가하여 클라이언트 애플리케이션에 적절한 권한을 가진 사용자와 역할을 정의합니다:

kind: role
version: v5
metadata:
  name: register-apps
spec:
  allow:
    app_labels:
      '*': '*'
    rules:
      - resources: ['token']
        verbs: ['create']
      - resources: ['app']
        verbs: ['list']
      - resources: ['app_server']
        verbs: ['delete']
---
kind: user
metadata:
  name: register-apps
spec:
  roles: ['register-apps']
version: v2

사용자와 역할을 생성합니다:

$ tctl create -f register-apps.yaml
role 'register-apps' has been created
user "register-apps" has been created

클라이언트 애플리케이션 가장 활성화#

모든 Teleport 사용자와 마찬가지로 Teleport Auth 서비스는 단기 TLS 자격 증명을 발급하여 register-apps 사용자를 인증합니다. 이 경우 register-apps 역할과 사용자를 가장하여 수동으로 자격 증명을 요청합니다.

자체 호스팅 Teleport Enterprise 배포를 실행 중이고 Auth 서비스 호스트에서 tctl을 사용하는 경우 이미 가장 권한이 있습니다.

register-apps에 대한 가장 권한을 사용자에게 부여하려면 역할을 정의하는 register-apps-impersonator.yaml 파일을 생성합니다:

kind: role
version: v5
metadata:
  name: register-apps-impersonator
spec:
  allow:
    impersonate:
      roles:
      - register-apps
      users:
      - register-apps

register-apps-impersonator 역할을 생성합니다:

$ tctl create -f register-apps-impersonator.yaml
role 'register-apps-impersonator' has been created

Assign the register-apps-impersonator role to your Teleport user by running the appropriate commands for your authentication provider:

이제 register-apps 역할과 사용자에 대한 서명된 인증서를 생성할 수 있습니다.

3/5단계. 클라이언트 애플리케이션의 아이덴티티 내보내기#

모든 Teleport 사용자와 마찬가지로 register-apps는 Teleport 클러스터에 연결하기 위해 서명된 자격 증명이 필요합니다. tctl auth sign 명령을 사용하여 플러그인의 자격 증명을 요청합니다.

다음 tctl auth sign 명령은 register-apps 사용자를 가장하고, 서명된 자격 증명을 생성하며, 아이덴티티 파일을 로컬 디렉터리에 씁니다:

$ tctl auth sign --user=register-apps --out=auth.pem

아이덴티티 파일 auth.pem에는 TLS 및 SSH 자격 증명이 모두 포함됩니다. 클라이언트 애플리케이션은 SSH 자격 증명을 사용하여 프록시 서비스에 연결하고, 이는 Auth 서비스로의 역방향 터널 연결을 설정합니다. 플러그인은 이 역방향 터널과 TLS 자격 증명을 사용하여 Auth 서비스의 gRPC 엔드포인트에 연결합니다.

4/5단계. 클라이언트 애플리케이션 작성#

이 단계에서는 예제 클라이언트 애플리케이션을 살펴봅니다.

데모 애플리케이션은 인기 있는 오픈 소스 메시지 브로커인 RabbitMQ를 실행하는 컨테이너를 감시하여 관리 API를 Teleport에 등록(및 필요한 경우 등록 해제)합니다. 방법:

  • Teleport에 등록된 RabbitMQ 관리 API 엔드포인트 가져오기
  • RabbitMQ 컨테이너 가져오기
  • RabbitMQ 컨테이너가 관리 API 엔드포인트와 일치하지 않는 경우 토큰을 생성하고 새 Teleport 애플리케이션 서비스 컨테이너를 실행하여 컨테이너의 관리 API 엔드포인트를 Teleport에 등록
  • Teleport에 등록되었지만 RabbitMQ 컨테이너와 일치하지 않는 RabbitMQ API 엔드포인트에 대해 해당 애플리케이션 서비스 컨테이너를 삭제하고 RabbitMQ API 엔드포인트 등록 해제
동적 등록

클라이언트 애플리케이션이 대상 애플리케이션의 모든 인스턴스를 프록시하기 위해 새 애플리케이션 서비스 인스턴스를 시작하는 동안, 동일한 애플리케이션 서비스 인스턴스를 통해 여러 애플리케이션을 프록시할 수도 있습니다.

이를 위해 Teleport API를 통해 애플리케이션을 동적으로 등록하는 클라이언트 애플리케이션을 작성합니다. 이 가이드에서 방법을 설명합니다.

임포트#

examples/service-discovery-api-client/main.go에 있는 프로그램은 다음 패키지를 임포트합니다:

패키지 설명
context context.Context 유형을 포함합니다. context.Context는 실패하거나 시간 초과될 수 있는 외부 서비스 연결과 같은 장기 실행 루틴을 제어하기 위한 추상화입니다. 프로그램은 컨텍스트를 취소하거나 타임아웃 및 메타데이터를 할당할 수 있습니다.
crypto/rand 암호화 랜덤화 함수를 포함합니다. 이를 사용하여 애플리케이션 서비스가 Teleport Auth 서비스와 신뢰를 설정하는 데 사용할 토큰을 생성합니다.
encoding/hex crypto/rand에서 사용하는 숫자 생성기는 바이트 데이터를 반환하므로 이 패키지를 사용하여 16진수 문자열로 인코딩합니다.
fmt 인쇄, 문자열 또는 오류를 위한 데이터 형식화.
net 네트워크 I/O 처리.
net/url URL 파싱.
strings 문자열 조작.
time 시간 처리. Auth 서비스 연결 타임아웃 및 루프에서 검색 로직 실행을 위한 타이커를 정의하는 데 사용합니다.

클라이언트는 다음 서드파티 코드를 임포트합니다:

패키지 설명
github.com/docker/docker/api/types Docker 데몬 API에서 사용되는 유형. 여기서 dtypes로 별칭 지정됨.
github.com/docker/docker/api/types/container Docker 데몬 API에서 사용되는 컨테이너 특정 유형.
github.com/docker/docker/api/types/filters Docker 데몬 API에서 컨테이너 필터링에 사용되는 유형.
github.com/docker/docker/api/types/strslice Docker의 API 클라이언트 라이브러리에서 문자열 슬라이스 작업을 위한 유틸리티 패키지. (Go에서 슬라이스는 배열과 유사하지만 가변적인 길이와 용량을 가집니다. 배열은 고정 크기를 가집니다.)
github.com/docker/docker/client Docker API 클라이언트 라이브러리, 여기서 docker로 별칭 지정됨.
github.com/gravitational/teleport/api/client Auth 서비스의 gRPC API에 인증하고 요청을 만들기 위한 라이브러리, teleport로 별칭 지정됨.
github.com/gravitational/api/client/proto Teleport의 프로토콜 버퍼 API 사양.
github.com/gravitational/teleport/api/types Auth 서비스 API에서 사용되는 유형(예: 애플리케이션 서비스 레코드).
github.com/gravitational/trace 표준 라이브러리보다 유용한 세부 정보로 오류를 표시합니다.
google.golang.org/grpc gRPC 클라이언트 및 서버 라이브러리.

전역 선언#

프로그램은 나중에 구성 가능하게 만들기 쉽도록 눈에 잘 보이는 위치에 상수를 정의합니다:

const (
	// proxyAddr에 Teleport 프록시 서비스 인스턴스의 호스트 및 포트 할당
	proxyAddr      string = ""
	teleportImage  string = "(=teleport.latest_oss_docker_image=)"
	initTimeout           = time.Duration(30) * time.Second
	updateInterval        = time.Duration(5) * time.Second
	tokenTTL              = time.Duration(5) * time.Minute
	networkName    string = "bridge"
	managementPort string = "15672"
	tokenLenBytes         = 16
	rabbitMQImage string  = "rabbitmq:3-management"
)

나중에 프로그램에서 이 상수들을 사용합니다. 나중에 변경하고 싶을 수 있는 일부 값을 정의합니다:

상수 설명
proxyAddr Teleport 프록시 서비스의 호스트 및 포트(예: mytenant.teleport.sh:443). 클라이언트를 클러스터에 연결하는 데 사용합니다. 이것을 자신의 프록시 서비스 호스트 및 포트로 할당하세요.
teleportImage Teleport 컨테이너 이미지 이름. 프로그램이 Teleport 애플리케이션 서비스 인스턴스를 실행하는 데 사용합니다.
initTimeout Teleport 클러스터 연결 타임아웃(30초).
updateInterval 프로그램이 애플리케이션 서비스 인스턴스와 애플리케이션 컨테이너를 조정하는 사이에 대기하는 시간(5초).
tokenTTL 애플리케이션 서비스 인스턴스가 Teleport 클러스터와 신뢰를 설정하는 데 사용할 토큰의 TTL. 이 클라이언트 애플리케이션은 토큰을 생성한 직후 사용하므로 이 자격 증명이 유출되는 것을 방지하기 위해 작은 값(5분)으로 설정할 수 있습니다.
networkName 애플리케이션 컨테이너를 검색할 Docker 네트워크 이름. bridge는 Docker 데몬이 관리하는 기본 로컬 네트워크입니다.
managementPort RabbitMQ 컨테이너의 관리 API 포트.
tokenLenBytes 생성할 토큰의 길이(바이트 단위).
rabbitMQImage RabbitMQ 컨테이너 이미지 이름. 여기서 사용하는 컨테이너 이미지는 관리 API가 활성화되어 있습니다.

const 선언 아래에는 다음 유형 선언이 있습니다:

type tokenDemoApp struct {
	dockerClient   *docker.Client
	teleportClient *teleport.Client
}

이것은 프로그램에서 선언할 유일한 유형입니다. Docker 데몬 API와 Teleport Auth 서비스 API용 클라이언트에 대한 포인터를 포함합니다. 프로그램은 클라이언트를 초기화한 다음 tokenDemoApp의 메서드를 호출합니다.

등록된 애플리케이션의 관리 엔드포인트 URL 가져오기#

프로그램은 Teleport에 등록된 애플리케이션을 가져옵니다. 나중에 이 애플리케이션을 현재 실행 중인 애플리케이션 컨테이너와 비교합니다.

Teleport는 등록된 리소스를 두 가지 방식으로 나타냅니다:

  • 동적 리소스: 클러스터에 적용한 구성 문서(예: app, kube_cluster, db 리소스). Teleport는 이를 프록시할 적절한 서비스 인스턴스를 자동으로 찾습니다.
  • 서비스 인스턴스: 서비스의 구성 파일에서 지정하는 인프라의 특정 리소스를 프록시하는 Teleport 서비스.

애플리케이션에서는 앱당 하나의 애플리케이션 서비스 인스턴스를 생성하므로 애플리케이션 서비스 인스턴스를 나열하고 프록시하는 리소스를 검사합니다. 다른 클라이언트 애플리케이션은 동적으로 등록된 리소스를 대신 조회해야 할 수 있습니다.

다음은 애플리케이션이 등록된 애플리케이션의 URL을 가져오는 데 사용하는 메서드입니다:

func (t *tokenDemoApp) listRegisteredAppURLs(ctx context.Context) (map[string]types.AppServer, error) {
	m := make(map[string]types.AppServer)

	for {
		req := proto.ListResourcesRequest{
			ResourceType: "app_server",
			Limit:        10,
		}
		resp, err := t.teleportClient.ListResources(
			ctx,
			req,
		)

		if err != nil {
			return nil, trace.Wrap(err)
		}

		for _, r := range resp.Resources {
			if p, ok := r.(types.AppServer); ok {
				m[p.GetApp().GetURI()] = p
			}
		}

		// No more pages to request
		if resp.NextKey == "" {
			break
		}

		req.StartKey = resp.NextKey
	}

	return m, nil
}

tokenDemoApp.teleportClient는 Teleport API 라이브러리의 *Client 유형입니다. *Client 유형의 ListResources 메서드는 proto.ListResourcesRequest에 명명된 매개변수로 리소스 목록에 대해 Teleport API를 쿼리합니다.

결과가 페이지로 나뉘어지므로 listRegisteredAppURLsfor 루프에서 이 쿼리를 실행합니다. 쿼리의 응답에 다음 페이지의 시작을 조회하는 데 사용할 키를 나타내는 비어 있지 않은 NextKey 필드가 있는 한 함수는 다음 키를 사용하여 쿼리를 다시 실행합니다.

결국 이 프로그램을 두 맵(즉, 해시 테이블)을 비교하여 등록된 애플리케이션을 애플리케이션 컨테이너와 조정하도록 확장합니다:

  • Teleport에 등록된 애플리케이션을 나타내는 URL 문자열에서 types.AppServer로의 맵.
  • RabbitMQ 컨테이너의 관리 API 엔드포인트 URL이 키인 맵.

listRegisteredAppURLs 함수는 ListResources의 결과를 반복하고 각 types.AppServer인 리소스에 대해 프록시된 애플리케이션의 URL을 맵의 키로 삽입하고 해당 types.AppServer를 값으로 할당하여 첫 번째 맵을 생성합니다.

다음 섹션에서는 두 번째 맵을 생성하는 방법을 보여드립니다.

리소스를 가져오는 다른 방법

ListResources는 리소스를 가져오기 위한 범용 메서드이며 결과 정렬 및 필터링을 지원합니다. 클라이언트 애플리케이션의 필요에 따라 리소스별 메서드를 대신 고려할 수 있습니다.

예를 들어, 이 메서드는 동적으로 등록된 애플리케이션만 반환합니다:

func (c *Client) GetApps(ctx context.Context) ([]types.Application, error)

이 메서드는 Kubernetes 서비스 인스턴스를 반환합니다:

func (c *Client) GetKubernetesServers(ctx context.Context) ([]types.KubeServer, error)

일반적으로 Get[A-Za-z]+ 패턴을 따르는 *Client 메서드는 동적으로 등록된 리소스를 검색하고, Get[A-Za-z]+(Servers|Services) 패턴을 따르는 메서드는 Teleport 서비스 레코드를 검색합니다.

애플리케이션 컨테이너의 관리 엔드포인트 URL 가져오기#

다음 함수는 RabbitMQ 컨테이너의 URL을 가져옵니다:

func (t *tokenDemoApp) listAppContainerURLs(ctx context.Context, image string) (map[string]struct{}, error) {
	c, err := t.dockerClient.ContainerList(ctx, dtypes.ContainerListOptions{
		Filters: filters.NewArgs(filters.KeyValuePair{
			Key:   "ancestor",
			Value: image,
		}),
	})
	if err != nil {
		return nil, trace.Wrap(err)
	}

	l := make(map[string]struct{})

	for _, r := range c {
		b, ok := r.NetworkSettings.Networks[networkName]
		// Not connected to the chosen network, so skip it
		if !ok {
			continue
		}

		u, err := url.Parse("http://"+net.JoinHostPort(
			b.IPAddress,
			managementPort,
		))

		if err != nil {
			return nil, trace.Wrap(err)
		}

		l[u.String()] = struct{}{}
	}

	return l, nil
}

이 함수는 Docker API 클라이언트 라이브러리의 Client 유형인 tokenDemoApp.dockerClient 필드를 사용하여 Docker 데몬에 컨테이너 목록 요청을 보냅니다. dtypes.ContainerListOptions 구조체는 Docker 데몬에게 image 매개변수에서 지정한 이미지를 가진 컨테이너만 나열하도록 지시합니다.

Docker 데몬이 반환한 각 컨테이너에 대해 미리 결정된 네트워크(기본 브리지 네트워크) 내에서 컨테이너의 IP 주소를 조회합니다. 모든 RabbitMQ 컨테이너에는 관리 포트가 열려 있다는 것을 미리 알고 있으므로 컨테이너의 IP 주소와 관리 포트를 사용하여 URL을 구성하고 이 함수에서 반환할 맵에 삽입합니다.

이 함수가 반환하는 맵은 URL 문자열을 빈 구조체에 할당합니다. Go에서 빈 구조체는 메모리를 소비하지 않습니다. Go 프로그램은 프로그램이 값을 사용하지 않고 상수 시간에 키를 검색할 수 있으므로 해시 테이블의 값으로 빈 구조체를 자주 사용합니다.

새 애플리케이션 서비스 인스턴스용 토큰 생성#

등록된 애플리케이션 맵을 애플리케이션 컨테이너 맵과 조정한 후 다음이 필요합니다:

  • 등록되지 않은 애플리케이션 컨테이너를 프록시할 애플리케이션 서비스 인스턴스 시작
  • 더 이상 실행 중인 애플리케이션과 일치하지 않는 애플리케이션 서비스 인스턴스 삭제

새 애플리케이션 서비스 인스턴스를 시작하려면 토큰을 생성합니다. Teleport 서비스는 Auth 서비스에 토큰을 제시하여 클러스터에 참여할 수 있습니다.

아래 코드는 새 애플리케이션 서비스 인스턴스용 토큰을 생성합니다:

func cryptoRandomHex(len int) (string, error) {
	randomBytes := make([]byte, len)
	if _, err := rand.Read(randomBytes); err != nil {
		return "", trace.Wrap(err)
	}
	return hex.EncodeToString(randomBytes), nil
}

func (t *tokenDemoApp) createAppToken(ctx context.Context) (string, error) {
	n, err := cryptoRandomHex(tokenLenBytes)
	if err != nil {
		return "", trace.Wrap(err)
	}

	tok, err := types.NewProvisionTokenFromSpec(
		n,
		time.Now().Add(tokenTTL),
		types.ProvisionTokenSpecV2{
			Roles: types.SystemRoles{types.RoleApp},
		})

	if err := t.teleportClient.CreateToken(ctx, tok); err != nil {
		return "", trace.Wrap(err)
	}
	return n, nil
}

위 예제는 *client.Client.CreateToken을 사용하여 토큰으로 리소스를 Teleport 클러스터에 참여시키는 방법을 보여줍니다. 이미 Teleport 서비스 인스턴스(예: 애플리케이션 서비스)가 실행 중인 경우 *client.Client.CreateApp과 유사한 메서드를 사용하여 동적으로 리소스를 클러스터에 참여시키는 것이 더 간단합니다.

애플리케이션 동적 등록

새 애플리케이션 서비스 인스턴스를 시작하는 대신 애플리케이션을 동적으로 등록하려면 *clientClient.CreateApp 메서드를 사용합니다:

func (c *Client) CreateApp(ctx context.Context, app types.Application) error

이것이 작동하려면 이미 애플리케이션 서비스 인스턴스가 실행 중이어야 합니다. API 클라이언트 애플리케이션의 Teleport 사용자도 다음 권한을 가져야 합니다:

spec:
  allow:
    rules:
      - resources: ['app']
        verbs: ['create']

다른 리소스의 경우 다음 메서드를 사용하며, 각각 create 동사와 함께 해당 역할 권한이 필요합니다:

리소스 메서드 역할 내
데이터베이스 *client.Client.CreateDatabase db
Kubernetes 클러스터 *client.Client.CreateKubernetesCluster kube_cluster
Windows 데스크톱 *client.Client.CreateWindowsDesktop windows_desktop

서버는 Teleport 클러스터에 참여하기 위해 Teleport 서비스 인스턴스를 실행해야 하므로 API 클라이언트는 토큰을 사용해서만 서버를 등록할 수 있습니다.

cryptoRandomHex는 Teleport가 내부적으로 정의하는 함수의 복사본입니다. crypto/rand 패키지를 사용하여 무작위 바이트를 생성한 다음 16진수 형식의 문자열로 변환하며, 이를 토큰으로 사용합니다. 보안 암호화 기법을 사용하여 토큰을 생성할 수 있습니다.

*tokenDemoApp.createAppTokencryptoRandomHex를 호출하고 types.NewProvisionTokenFromSpec을 호출하여 결과를 사용합니다. 이는 Teleport API에 요청을 보낼 때 사용할 토큰의 사양을 반환하며, t.teleportClient.CreateToken을 통해 이를 수행합니다.

이 경우 이전에 구성한 TTL과 types.RoleApp 역할을 토큰에 할당합니다. 이는 Auth 서비스에게 토큰이 애플리케이션 서비스 인스턴스용임을 나타냅니다.

CreateToken은 토큰 생성에 실패하면 오류를 반환합니다. 성공하면 토큰을 새 애플리케이션 서비스 인스턴스를 실행하는 데 사용할 수 있도록 *tokenDemoApp.createAppToken의 호출자에게 반환합니다.

토큰 조회

클라이언트 애플리케이션은 다음 함수를 호출하여 생성한 토큰을 조회할 수 있습니다:

func (c *Client) GetTokens(ctx context.Context) ([]types.ProvisionToken, error)

이를 위해 클라이언트 애플리케이션의 Teleport 역할에 다음 권한이 필요합니다:

spec:
  allow:
    rules:
      - resources: ['token']
        verbs: ['list', 'read']

이 가이드에서 시연하는 애플리케이션에서는 애플리케이션이 이미 생성한 토큰을 알고 있으므로 토큰을 조회할 필요가 없습니다.

애플리케이션 서비스 컨테이너 시작#

프로그램은 애플리케이션 서비스 인스턴스를 시작하는 방법이 필요합니다. 이를 위해 이전에 생성한 토큰과 RabbitMQ 관리 API 엔드포인트 URL을 사용하여 컨테이너를 시작합니다:

func (t *tokenDemoApp) startApplicationServiceContainer(
	ctx context.Context,
	token string,
	u url.URL,
) error {

	name := strings.ReplaceAll(u.Hostname(), ".", "-")
	resp, err := t.dockerClient.ContainerCreate(
		ctx,
		&container.Config{
			Image: teleportImage,
			Entrypoint: strslice.StrSlice{
				"/usr/bin/dumb-init",
				"teleport",
				"start",
				"--roles=app",
				"--auth-server=" + proxyAddr,
				"--token=" + token,
				"--app-name=rabbitmq-" + name,
				"--app-uri=" + u.String(),
			},
		},
		nil,
		nil,
		nil,
		"",
	)
	if err != nil {
		return trace.Wrap(err)
	}

	err = t.dockerClient.ContainerStart(
		ctx,
		resp.ID,
		dtypes.ContainerStartOptions{},
	)
	if err != nil {
		return trace.Wrap(err)
	}

	return nil
}

Teleport 애플리케이션 서비스는 Teleport 웹 UI 주소의 하위 도메인에서 등록된 애플리케이션으로 트래픽을 리다이렉션합니다. URL 안전하지만 다른 등록된 애플리케이션과 충돌하지 않는 애플리케이션 이름이 필요합니다. 이 경우 점을 하이픈으로 대체한 RabbitMQ 컨테이너의 IP 주소를 사용합니다.

다음으로 컨테이너를 생성합니다. 컨테이너의 진입점으로 사용되는 실행 파일은 teleport 이미지가 기본적으로 사용하는 것과 동일하지만 컨테이너를 애플리케이션 서비스 인스턴스로 시작하고 RabbitMQ 컨테이너를 프록시하도록 설정하는 추가 플래그가 있습니다.

마지막으로 생성한 컨테이너의 ID를 사용하여 컨테이너를 실행합니다.

애플리케이션 서비스 인스턴스 제거#

애플리케이션 서비스 컨테이너를 생성하는 것과 함께 클라이언트 애플리케이션은 불필요한 애플리케이션 서비스 인스턴스를 제거하여 등록된 애플리케이션과 실행 중인 컨테이너를 조정합니다:

func (t *tokenDemoApp) pruneAppServiceInstance(ctx context.Context, p types.AppServer) error {
	host := p.GetHostname()

	if err := t.teleportClient.DeleteApplicationServer(
		ctx,
		p.GetNamespace(),
		p.GetHostID(),
		p.GetName(),
	); err != nil {
		return trace.Wrap(err)
	}

	fmt.Println("Deleted unnecessary Application Service record:", p.GetName())

	// Don't check errors when removing the container, since it may already
	// have been removed.
	t.dockerClient.ContainerStop(ctx, host, container.StopOptions{})
	t.dockerClient.ContainerRemove(ctx, host, dtypes.ContainerRemoveOptions{})

	fmt.Println("Deleted unnecessary Application Service container:", host)
	return nil
}

이 함수는 types.AppServer를 받아 Teleport에서 등록 해제하고 연결된 애플리케이션 서비스 컨테이너를 제거합니다.

Teleport가 오래된 애플리케이션 서비스 레코드를 자동으로 등록 해제하지만 애플리케이션 서비스 인스턴스를 중지한 후 시간이 걸릴 수 있습니다.

리소스의 TTL 변경

리소스를 백엔드에서 등록 해제하기 전에 Teleport가 리소스 가용성을 확인하는 데 사용하는 간격을 변경할 수 있습니다. 이를 위해 types.Resource 인터페이스의 SetExpiry 메서드를 사용합니다.

예를 들어 다음 SetExpiry 호출은 Teleport가 가용성을 확인하지 않는 한 10분 후에 만료되도록 WindowsDesktopV3 리소스를 구성합니다:

desktop.SetExpiry(time.Now().Add(10 * time.Minute))

애플리케이션 서비스 인스턴스를 수동으로 등록 해제하려면 pruneAppServiceInstance 함수의 p 매개변수를 사용하여 삭제할 애플리케이션 서비스 인스턴스의 네임스페이스, 호스트 ID, 이름을 가져와서 *Client.DeleteApplicationServer 메서드를 호출합니다.

Tip

Teleport 네임스페이스는 더 이상 사용되지 않지만 Teleport API 클라이언트 라이브러리에서 가끔 나타납니다. Teleport가 지원하는 유일한 네임스페이스는 default입니다.

다음으로 이 함수는 제거하려는 types.AppServer와 연결된 애플리케이션 서비스 컨테이너를 중지하고 제거합니다. 애플리케이션 서비스 레코드의 호스트명은 애플리케이션 서비스 컨테이너의 ID와 동일하므로 호스트명을 t.dockerClient.ContainerStopt.dockerClient.ContainerRemove에 전달할 수 있습니다.

등록된 애플리케이션과 애플리케이션 컨테이너 조정#

애플리케이션 서비스 인스턴스와 애플리케이션 컨테이너를 나열, 추가, 제거하는 여러 함수를 선언했습니다. 다음으로 이러한 함수를 사용하여 조정 로직을 구현합니다:

func (t *tokenDemoApp) reconcileApps() error {
	ctx := context.Background()
	apps, err := t.listRegisteredAppURLs(ctx)
	if err != nil {
		return trace.Wrap(err)
	}

	urls, err := t.listAppContainerURLs(ctx, rabbitMQImage)
	if err != nil {
		return trace.Wrap(err)
	}

	for u, _ := range urls {
		if _, ok := apps[u]; ok {
			continue
		}
		tok, err := t.createAppToken(ctx)
		if err != nil {
			return trace.Wrap(err)
		}
		fmt.Println("Created a new application token for URL: " + u)

		r, err := url.Parse(u)
		if err != nil {
			return trace.Wrap(err)
		}

		err = t.startApplicationServiceContainer(ctx, tok, *r)
		if err != nil {
			return trace.Wrap(err)
		}
		fmt.Println("Started an Application Service container to proxy URL: " + u)
	}

	for a, p := range apps {
		_, ok := urls[a]
		if ok {
			continue
		}

		if err := t.pruneAppServiceInstance(ctx, p); err != nil {
			return trace.Wrap(err)
		}
	}
	return nil
}

*tokenDemoApp.reconcileAppslistAppContainerURLslistRegisteredAppURLs를 호출하여 등록된 애플리케이션과 실행 중인 애플리케이션 컨테이너의 맵을 생성합니다. 그런 다음 각 맵의 키 내에 있는 URL을 반복하여 한 맵의 URL이 다른 맵에 있는지 확인합니다.

애플리케이션 컨테이너 맵의 URL이 등록된 애플리케이션 맵에 없는 경우, 하나의 애플리케이션이 아직 등록되지 않았음을 의미하므로 토큰을 생성하고 Teleport 애플리케이션 서비스 인스턴스를 시작합니다.

등록된 애플리케이션 맵의 URL이 애플리케이션 컨테이너 맵에 없는 경우, 불필요한 애플리케이션 서비스 인스턴스가 있음을 의미하므로 pruneAppServiceInstance를 호출하여 제거합니다.

클라이언트 초기화#

*tokenDemoApp.reconcileApps 메서드는 단일 조정을 수행합니다. 다음 단계는 프로그램의 진입점 내에서 루프에서 조정을 실행할 수 있도록 API 클라이언트를 초기화하는 것입니다:

func newTokenDemoApp() *tokenDemoApp {
	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, initTimeout)
	defer cancel()
	creds := teleport.LoadIdentityFile("auth.pem")

	t, err := teleport.New(ctx, teleport.Config{
		Addrs:       []string{proxyAddr},
		Credentials: []teleport.Credentials{creds},
	})
	if err != nil {
		panic(err)
	}
	fmt.Println("Connected to Teleport")

	d, err := docker.NewClientWithOpts(
		docker.WithAPIVersionNegotiation(),
	)
	if err != nil {
		panic(err)
	}
	fmt.Println("Connected to the Docker daemon")

	return &tokenDemoApp{
		teleportClient: t,
		dockerClient:   d,
	}

}

여기서 teleport로 별칭을 지정하는 client는 API 클라이언트 설정을 위한 Teleport의 라이브러리입니다. 플러그인은 client.LoadIdentityFile을 호출하여 client.Credentials를 얻어 Teleport 클라이언트를 초기화합니다. 그런 다음 client.Credentials를 사용하여 client.New를 호출하며, 제공된 아이덴티티 파일을 사용하여 Addrs 필드에 지정된 Teleport 프록시 서비스에 연결합니다.

Warning

이 프로그램은 자격 증명이나 Teleport 클러스터 주소를 검증하지 않습니다. 다음을 확인하세요:

  • 이전에 내보낸 아이덴티티 파일의 TTL이 만료되지 않았는지
  • teleport.ConfigAddrs 필드에 제공한 값이 Teleport 프록시 서비스의 호스트 웹 포트를 모두 포함하는지(예: mytenant.teleport.sh:443)

newTokenDemoApp 함수도 Docker 클라이언트를 초기화합니다. 버전 협상(docker.WithAPIVersionNegotiation)을 사용하여 클라이언트의 API 버전과 Docker 데몬 버전 간의 불일치로 인한 오류를 방지합니다.

진입점#

main 진입점 함수로 애플리케이션을 묶습니다:

func main() {
	fmt.Println("Starting the application")
	app := newTokenDemoApp()

	k := time.NewTicker(updateInterval)
	defer k.Stop()
	for {
		<-k.C
		if err := app.reconcileApps(); err != nil {
			panic(err)
		}
	}
}

main 함수는 newTokenDemoApp을 호출하여 API 클라이언트를 초기화합니다. 그런 다음 time.NewTicker를 호출하며, 이는 진입점이 조정 루틴을 실행하는 데 사용할 Go 채널을 반환합니다. 채널은 동시 루틴 간의 통신을 관리하는 Go 프리미티브입니다.

타이커를 생성한 후 진입점은 updateInterval이 경과할 때마다 타이커의 채널(k.C)에서 수신합니다. 채널에서 수신할 때까지 블록하며 매 간격마다 app.reconcileApps를 호출합니다.

5/5단계. 클라이언트 애플리케이션 테스트#

클라이언트 애플리케이션을 실행하여 Teleport가 인프라의 리소스를 등록하고 등록 해제하여 서비스 검색 솔루션과 동기화하는 방법을 확인합니다.

애플리케이션이 컨테이너를 생성할 수 있도록 Teleport 컨테이너 이미지를 가져왔는지 확인합니다:

$ docker image pull (=teleport.latest_oss_docker_image=)

프로젝트 디렉터리에서 다음 명령을 실행합니다:

$ go run main.go
Starting the application
Connected to Teleport
Connected to the Docker daemon

새 터미널에서 세 개의 새 RabbitMQ 컨테이너를 실행합니다:

$ for i in {1..3}; do docker run -d rabbitmq:3-management; done;

애플리케이션을 실행한 터미널에 다음과 유사한 출력이 표시됩니다:

Created a new application token for URL: http://172.17.0.4:15672
Started an Application Service container to proxy URL: http://172.17.0.4:15672
Created a new application token for URL: http://172.17.0.3:15672
Started an Application Service container to proxy URL: http://172.17.0.3:15672
Created a new application token for URL: http://172.17.0.2:15672
Started an Application Service container to proxy URL: http://172.17.0.2:15672

RabbitMQ 인스턴스가 Teleport에 등록되었는지 확인합니다:

$ tsh apps ls
Application         Description Type Public Address           Labels
------------------- ----------- ---- ------------------------ -------------------
rabbitmq-172-17-0-2             HTTP rabbitmq-172-17-0-2.3... teleport.dev/origin
rabbitmq-172-17-0-3             HTTP rabbitmq-172-17-0-3.3... teleport.dev/origin
rabbitmq-172-17-0-4             HTTP rabbitmq-172-17-0-4.3... teleport.dev/origin

다음으로 RabbitMQ 컨테이너 중 하나를 중지합니다:

$ docker stop $(docker ps --filter "ancestor=rabbitmq:3-management" -q --last 1)

애플리케이션을 실행한 터미널에 다음과 유사한 출력이 표시됩니다:

Deleted unnecessary Application Service record: rabbitmq-172-17-0-4
Deleted unnecessary Application Service container: 63facaa3033a

이제 tsh apps ls를 실행하면 두 개의 등록된 애플리케이션이 표시됩니다.

다음 단계#

실행 중인 Docker 컨테이너와 등록된 애플리케이션을 최신 상태로 유지하는 Teleport API 클라이언트를 구현했습니다. Teleport의 API를 사용하여 등록된 Teleport 리소스를 자체 서비스 검색 솔루션과 자동으로 동기화할 수 있습니다.

예제 참조#

Teleport에는 자체 자동 리소스 검색 솔루션인 Teleport 검색 서비스가 포함되어 있으며, 해당 소스를 참조하여 Teleport가 검색 로직을 구현하는 방법을 확인할 수 있습니다.

Teleport 코드 저장소에는 프로덕션 준비가 된 Teleport API 클라이언트 예제가 포함되어 있습니다. 현재 리소스를 자동 검색하는 플러그인을 유지 관리하지 않지만 이러한 예제를 사용하여 구성 파싱, 재시도 및 기타 작업을 구현하는 방법을 확인할 수 있습니다.

단기 자격 증명으로 클라이언트 애플리케이션 프로비저닝#

이 예제에서는 tctl auth sign 명령을 사용하여 작성한 프로그램의 자격 증명을 가져왔습니다. 프로덕션 사용을 위해서는 이러한 자격 증명이 도난당할 위험을 줄이는 머신 및 워크로드 아이덴티티를 통해 단기 자격 증명을 프로비저닝할 것을 권장합니다. 자세히 알아보려면 머신 및 워크로드 아이덴티티 문서를 참조하세요.

Teleport로 리소스 자동 등록

원문 보기
요약

Teleport의 API를 사용하여 인프라의 리소스를 Teleport 클러스터에 자동으로 등록할 수 있습니다. Teleport는 이미 AWS, Azure, Google Cloud의 Kubernetes 클러스터 및 Amazon EC2의 서버 자동 검색을 지원합니다.

Teleport의 API를 사용하여 인프라의 리소스를 Teleport 클러스터에 자동으로 등록할 수 있습니다.

Teleport는 이미 AWS, Azure, Google Cloud의 Kubernetes 클러스터 및 Amazon EC2의 서버 자동 검색을 지원합니다. 다른 리소스 및 클라우드 공급자를 지원하려면 API를 사용하여 자체 워크플로를 작성할 수 있습니다.

이 가이드에서는 Teleport로 리소스를 자동으로 등록하는 데 사용할 수 있는 라이브러리를 시연합니다. 워크스테이션에서 로컬로 실행할 수 있는 예제를 사용합니다.

작동 방식#

자동 등록은 다음 단계로 구성됩니다:

  • 서비스 검색 솔루션(예: Kubernetes API 서버, Consul 또는 클라우드 공급자의 API)에서 인프라의 리소스를 조회합니다.
  • Teleport gRPC API를 사용하여 Teleport에 등록된 리소스를 조회합니다.
  • Teleport에 등록되지 않은 인프라의 리소스에 대해 Teleport API를 사용하여 새 Teleport 서비스를 시작하거나 기존 Teleport 서비스에 리소스를 등록합니다.
  • Teleport에는 등록되어 있지만 인프라에는 없는 리소스에 대해 Teleport API를 사용하여 리소스 등록을 해제하고, 필요한 경우 리소스를 프록시하는 Teleport 서비스를 제거합니다.
Warning

이 가이드에서 빌드하는 프로그램은 학습 도구로 의도된 것입니다. 프로덕션 Teleport 클러스터에 연결하지 마세요. 대신 데모 클러스터를 사용하세요.

사전 요구 사항#

  • A running Teleport cluster. If you want to get started with Teleport, sign up for a free trial or set up a demo environment.

  • The tctl and tsh clients.

    Installing `tctl` and `tsh` clients
    1. Determine the version of your Teleport cluster. The tctl and tsh clients must be at most one major version behind your Teleport cluster version. Send a GET request to the Proxy Service at /v1/webapi/find and use a JSON query tool to obtain your cluster version. Replace with the web address of your Teleport Proxy Service:

      $ TELEPORT_DOMAIN=
      $ TELEPORT_VERSION="$(curl -s https://$TELEPORT_DOMAIN/v1/webapi/find | jq -r '.server_version')"
      
    2. Follow the instructions for your platform to install tctl and tsh clients:

  • 워크스테이션에 Docker 설치. Docker 시작하기.
  • 워크스테이션에 Go 버전 [teleport.golang] 이상 설치. Go 다운로드 페이지를 참조하세요. 이 가이드를 완료하기 위해 Go에 익숙할 필요는 없지만, 프로덕션 준비가 된 Teleport 클라이언트 애플리케이션을 빌드하려면 Go 지식이 필요합니다.
Tip

데모 프로젝트 설정을 계획하지 않더라도, 이 가이드를 따라 Teleport 클러스터에 서비스를 자동으로 등록하는 데 사용할 수 있는 라이브러리, 유형, 함수를 확인할 수 있습니다.

1/5단계. Go 프로젝트 설정#

최소한의 API 클라이언트에 대한 소스 코드를 다운로드합니다:

$ git clone https://github.com/gravitational/teleport -b branch/v(=teleport.major_version=)
$ cd examples/service-discovery-api-client

이 가이드의 나머지 부분에서는 이 API 클라이언트를 설정하고 Teleport의 API를 사용하여 Teleport 애플리케이션 서비스 인스턴스를 외부 서비스 검색 솔루션과 동기화하는 방법을 살펴봅니다.

2/5단계. RBAC 리소스 정의#

Teleport의 gRPC API와 통신하는 클라이언트는 Auth 서비스에 인증하기 위해 Teleport 사용자와 역할의 아이덴티티를 가정하며, Auth 서비스는 해당 아이덴티티가 특정 API 작업을 수행할 권한을 부여합니다.

자신의 Teleport 사용자도 클라이언트의 자격 증명을 생성하기 위해 API 클라이언트의 사용자를 가장할 권한이 필요합니다.

클라이언트 애플리케이션을 위한 사용자 및 역할 생성#

클라이언트 애플리케이션은 토큰 생성, 애플리케이션 목록 조회, Auth 서비스에 등록된 애플리케이션 서비스 인스턴스 레코드 삭제 권한을 가진 사용자로 Teleport에 인증합니다. 사용자는 또한 모든 레이블(app_labels)의 애플리케이션에 접근할 권한을 가지며, 이는 애플리케이션 목록 조회에 필요합니다.

register-apps.yaml 파일에 다음 내용을 추가하여 클라이언트 애플리케이션에 적절한 권한을 가진 사용자와 역할을 정의합니다:

kind: role
version: v5
metadata:
  name: register-apps
spec:
  allow:
    app_labels:
      '*': '*'
    rules:
      - resources: ['token']
        verbs: ['create']
      - resources: ['app']
        verbs: ['list']
      - resources: ['app_server']
        verbs: ['delete']
---
kind: user
metadata:
  name: register-apps
spec:
  roles: ['register-apps']
version: v2

사용자와 역할을 생성합니다:

$ tctl create -f register-apps.yaml
role 'register-apps' has been created
user "register-apps" has been created

클라이언트 애플리케이션 가장 활성화#

모든 Teleport 사용자와 마찬가지로 Teleport Auth 서비스는 단기 TLS 자격 증명을 발급하여 register-apps 사용자를 인증합니다. 이 경우 register-apps 역할과 사용자를 가장하여 수동으로 자격 증명을 요청합니다.

자체 호스팅 Teleport Enterprise 배포를 실행 중이고 Auth 서비스 호스트에서 tctl을 사용하는 경우 이미 가장 권한이 있습니다.

register-apps에 대한 가장 권한을 사용자에게 부여하려면 역할을 정의하는 register-apps-impersonator.yaml 파일을 생성합니다:

kind: role
version: v5
metadata:
  name: register-apps-impersonator
spec:
  allow:
    impersonate:
      roles:
      - register-apps
      users:
      - register-apps

register-apps-impersonator 역할을 생성합니다:

$ tctl create -f register-apps-impersonator.yaml
role 'register-apps-impersonator' has been created

Assign the register-apps-impersonator role to your Teleport user by running the appropriate commands for your authentication provider:

이제 register-apps 역할과 사용자에 대한 서명된 인증서를 생성할 수 있습니다.

3/5단계. 클라이언트 애플리케이션의 아이덴티티 내보내기#

모든 Teleport 사용자와 마찬가지로 register-apps는 Teleport 클러스터에 연결하기 위해 서명된 자격 증명이 필요합니다. tctl auth sign 명령을 사용하여 플러그인의 자격 증명을 요청합니다.

다음 tctl auth sign 명령은 register-apps 사용자를 가장하고, 서명된 자격 증명을 생성하며, 아이덴티티 파일을 로컬 디렉터리에 씁니다:

$ tctl auth sign --user=register-apps --out=auth.pem

아이덴티티 파일 auth.pem에는 TLS 및 SSH 자격 증명이 모두 포함됩니다. 클라이언트 애플리케이션은 SSH 자격 증명을 사용하여 프록시 서비스에 연결하고, 이는 Auth 서비스로의 역방향 터널 연결을 설정합니다. 플러그인은 이 역방향 터널과 TLS 자격 증명을 사용하여 Auth 서비스의 gRPC 엔드포인트에 연결합니다.

4/5단계. 클라이언트 애플리케이션 작성#

이 단계에서는 예제 클라이언트 애플리케이션을 살펴봅니다.

데모 애플리케이션은 인기 있는 오픈 소스 메시지 브로커인 RabbitMQ를 실행하는 컨테이너를 감시하여 관리 API를 Teleport에 등록(및 필요한 경우 등록 해제)합니다. 방법:

  • Teleport에 등록된 RabbitMQ 관리 API 엔드포인트 가져오기
  • RabbitMQ 컨테이너 가져오기
  • RabbitMQ 컨테이너가 관리 API 엔드포인트와 일치하지 않는 경우 토큰을 생성하고 새 Teleport 애플리케이션 서비스 컨테이너를 실행하여 컨테이너의 관리 API 엔드포인트를 Teleport에 등록
  • Teleport에 등록되었지만 RabbitMQ 컨테이너와 일치하지 않는 RabbitMQ API 엔드포인트에 대해 해당 애플리케이션 서비스 컨테이너를 삭제하고 RabbitMQ API 엔드포인트 등록 해제
동적 등록

클라이언트 애플리케이션이 대상 애플리케이션의 모든 인스턴스를 프록시하기 위해 새 애플리케이션 서비스 인스턴스를 시작하는 동안, 동일한 애플리케이션 서비스 인스턴스를 통해 여러 애플리케이션을 프록시할 수도 있습니다.

이를 위해 Teleport API를 통해 애플리케이션을 동적으로 등록하는 클라이언트 애플리케이션을 작성합니다. 이 가이드에서 방법을 설명합니다.

임포트#

examples/service-discovery-api-client/main.go에 있는 프로그램은 다음 패키지를 임포트합니다:

패키지 설명
context context.Context 유형을 포함합니다. context.Context는 실패하거나 시간 초과될 수 있는 외부 서비스 연결과 같은 장기 실행 루틴을 제어하기 위한 추상화입니다. 프로그램은 컨텍스트를 취소하거나 타임아웃 및 메타데이터를 할당할 수 있습니다.
crypto/rand 암호화 랜덤화 함수를 포함합니다. 이를 사용하여 애플리케이션 서비스가 Teleport Auth 서비스와 신뢰를 설정하는 데 사용할 토큰을 생성합니다.
encoding/hex crypto/rand에서 사용하는 숫자 생성기는 바이트 데이터를 반환하므로 이 패키지를 사용하여 16진수 문자열로 인코딩합니다.
fmt 인쇄, 문자열 또는 오류를 위한 데이터 형식화.
net 네트워크 I/O 처리.
net/url URL 파싱.
strings 문자열 조작.
time 시간 처리. Auth 서비스 연결 타임아웃 및 루프에서 검색 로직 실행을 위한 타이커를 정의하는 데 사용합니다.

클라이언트는 다음 서드파티 코드를 임포트합니다:

패키지 설명
github.com/docker/docker/api/types Docker 데몬 API에서 사용되는 유형. 여기서 dtypes로 별칭 지정됨.
github.com/docker/docker/api/types/container Docker 데몬 API에서 사용되는 컨테이너 특정 유형.
github.com/docker/docker/api/types/filters Docker 데몬 API에서 컨테이너 필터링에 사용되는 유형.
github.com/docker/docker/api/types/strslice Docker의 API 클라이언트 라이브러리에서 문자열 슬라이스 작업을 위한 유틸리티 패키지. (Go에서 슬라이스는 배열과 유사하지만 가변적인 길이와 용량을 가집니다. 배열은 고정 크기를 가집니다.)
github.com/docker/docker/client Docker API 클라이언트 라이브러리, 여기서 docker로 별칭 지정됨.
github.com/gravitational/teleport/api/client Auth 서비스의 gRPC API에 인증하고 요청을 만들기 위한 라이브러리, teleport로 별칭 지정됨.
github.com/gravitational/api/client/proto Teleport의 프로토콜 버퍼 API 사양.
github.com/gravitational/teleport/api/types Auth 서비스 API에서 사용되는 유형(예: 애플리케이션 서비스 레코드).
github.com/gravitational/trace 표준 라이브러리보다 유용한 세부 정보로 오류를 표시합니다.
google.golang.org/grpc gRPC 클라이언트 및 서버 라이브러리.

전역 선언#

프로그램은 나중에 구성 가능하게 만들기 쉽도록 눈에 잘 보이는 위치에 상수를 정의합니다:

const (
	// proxyAddr에 Teleport 프록시 서비스 인스턴스의 호스트 및 포트 할당
	proxyAddr      string = ""
	teleportImage  string = "(=teleport.latest_oss_docker_image=)"
	initTimeout           = time.Duration(30) * time.Second
	updateInterval        = time.Duration(5) * time.Second
	tokenTTL              = time.Duration(5) * time.Minute
	networkName    string = "bridge"
	managementPort string = "15672"
	tokenLenBytes         = 16
	rabbitMQImage string  = "rabbitmq:3-management"
)

나중에 프로그램에서 이 상수들을 사용합니다. 나중에 변경하고 싶을 수 있는 일부 값을 정의합니다:

상수 설명
proxyAddr Teleport 프록시 서비스의 호스트 및 포트(예: mytenant.teleport.sh:443). 클라이언트를 클러스터에 연결하는 데 사용합니다. 이것을 자신의 프록시 서비스 호스트 및 포트로 할당하세요.
teleportImage Teleport 컨테이너 이미지 이름. 프로그램이 Teleport 애플리케이션 서비스 인스턴스를 실행하는 데 사용합니다.
initTimeout Teleport 클러스터 연결 타임아웃(30초).
updateInterval 프로그램이 애플리케이션 서비스 인스턴스와 애플리케이션 컨테이너를 조정하는 사이에 대기하는 시간(5초).
tokenTTL 애플리케이션 서비스 인스턴스가 Teleport 클러스터와 신뢰를 설정하는 데 사용할 토큰의 TTL. 이 클라이언트 애플리케이션은 토큰을 생성한 직후 사용하므로 이 자격 증명이 유출되는 것을 방지하기 위해 작은 값(5분)으로 설정할 수 있습니다.
networkName 애플리케이션 컨테이너를 검색할 Docker 네트워크 이름. bridge는 Docker 데몬이 관리하는 기본 로컬 네트워크입니다.
managementPort RabbitMQ 컨테이너의 관리 API 포트.
tokenLenBytes 생성할 토큰의 길이(바이트 단위).
rabbitMQImage RabbitMQ 컨테이너 이미지 이름. 여기서 사용하는 컨테이너 이미지는 관리 API가 활성화되어 있습니다.

const 선언 아래에는 다음 유형 선언이 있습니다:

type tokenDemoApp struct {
	dockerClient   *docker.Client
	teleportClient *teleport.Client
}

이것은 프로그램에서 선언할 유일한 유형입니다. Docker 데몬 API와 Teleport Auth 서비스 API용 클라이언트에 대한 포인터를 포함합니다. 프로그램은 클라이언트를 초기화한 다음 tokenDemoApp의 메서드를 호출합니다.

등록된 애플리케이션의 관리 엔드포인트 URL 가져오기#

프로그램은 Teleport에 등록된 애플리케이션을 가져옵니다. 나중에 이 애플리케이션을 현재 실행 중인 애플리케이션 컨테이너와 비교합니다.

Teleport는 등록된 리소스를 두 가지 방식으로 나타냅니다:

  • 동적 리소스: 클러스터에 적용한 구성 문서(예: app, kube_cluster, db 리소스). Teleport는 이를 프록시할 적절한 서비스 인스턴스를 자동으로 찾습니다.
  • 서비스 인스턴스: 서비스의 구성 파일에서 지정하는 인프라의 특정 리소스를 프록시하는 Teleport 서비스.

애플리케이션에서는 앱당 하나의 애플리케이션 서비스 인스턴스를 생성하므로 애플리케이션 서비스 인스턴스를 나열하고 프록시하는 리소스를 검사합니다. 다른 클라이언트 애플리케이션은 동적으로 등록된 리소스를 대신 조회해야 할 수 있습니다.

다음은 애플리케이션이 등록된 애플리케이션의 URL을 가져오는 데 사용하는 메서드입니다:

func (t *tokenDemoApp) listRegisteredAppURLs(ctx context.Context) (map[string]types.AppServer, error) {
	m := make(map[string]types.AppServer)

	for {
		req := proto.ListResourcesRequest{
			ResourceType: "app_server",
			Limit:        10,
		}
		resp, err := t.teleportClient.ListResources(
			ctx,
			req,
		)

		if err != nil {
			return nil, trace.Wrap(err)
		}

		for _, r := range resp.Resources {
			if p, ok := r.(types.AppServer); ok {
				m[p.GetApp().GetURI()] = p
			}
		}

		// No more pages to request
		if resp.NextKey == "" {
			break
		}

		req.StartKey = resp.NextKey
	}

	return m, nil
}

tokenDemoApp.teleportClient는 Teleport API 라이브러리의 *Client 유형입니다. *Client 유형의 ListResources 메서드는 proto.ListResourcesRequest에 명명된 매개변수로 리소스 목록에 대해 Teleport API를 쿼리합니다.

결과가 페이지로 나뉘어지므로 listRegisteredAppURLsfor 루프에서 이 쿼리를 실행합니다. 쿼리의 응답에 다음 페이지의 시작을 조회하는 데 사용할 키를 나타내는 비어 있지 않은 NextKey 필드가 있는 한 함수는 다음 키를 사용하여 쿼리를 다시 실행합니다.

결국 이 프로그램을 두 맵(즉, 해시 테이블)을 비교하여 등록된 애플리케이션을 애플리케이션 컨테이너와 조정하도록 확장합니다:

  • Teleport에 등록된 애플리케이션을 나타내는 URL 문자열에서 types.AppServer로의 맵.
  • RabbitMQ 컨테이너의 관리 API 엔드포인트 URL이 키인 맵.

listRegisteredAppURLs 함수는 ListResources의 결과를 반복하고 각 types.AppServer인 리소스에 대해 프록시된 애플리케이션의 URL을 맵의 키로 삽입하고 해당 types.AppServer를 값으로 할당하여 첫 번째 맵을 생성합니다.

다음 섹션에서는 두 번째 맵을 생성하는 방법을 보여드립니다.

리소스를 가져오는 다른 방법

ListResources는 리소스를 가져오기 위한 범용 메서드이며 결과 정렬 및 필터링을 지원합니다. 클라이언트 애플리케이션의 필요에 따라 리소스별 메서드를 대신 고려할 수 있습니다.

예를 들어, 이 메서드는 동적으로 등록된 애플리케이션만 반환합니다:

func (c *Client) GetApps(ctx context.Context) ([]types.Application, error)

이 메서드는 Kubernetes 서비스 인스턴스를 반환합니다:

func (c *Client) GetKubernetesServers(ctx context.Context) ([]types.KubeServer, error)

일반적으로 Get[A-Za-z]+ 패턴을 따르는 *Client 메서드는 동적으로 등록된 리소스를 검색하고, Get[A-Za-z]+(Servers|Services) 패턴을 따르는 메서드는 Teleport 서비스 레코드를 검색합니다.

애플리케이션 컨테이너의 관리 엔드포인트 URL 가져오기#

다음 함수는 RabbitMQ 컨테이너의 URL을 가져옵니다:

func (t *tokenDemoApp) listAppContainerURLs(ctx context.Context, image string) (map[string]struct{}, error) {
	c, err := t.dockerClient.ContainerList(ctx, dtypes.ContainerListOptions{
		Filters: filters.NewArgs(filters.KeyValuePair{
			Key:   "ancestor",
			Value: image,
		}),
	})
	if err != nil {
		return nil, trace.Wrap(err)
	}

	l := make(map[string]struct{})

	for _, r := range c {
		b, ok := r.NetworkSettings.Networks[networkName]
		// Not connected to the chosen network, so skip it
		if !ok {
			continue
		}

		u, err := url.Parse("http://"+net.JoinHostPort(
			b.IPAddress,
			managementPort,
		))

		if err != nil {
			return nil, trace.Wrap(err)
		}

		l[u.String()] = struct{}{}
	}

	return l, nil
}

이 함수는 Docker API 클라이언트 라이브러리의 Client 유형인 tokenDemoApp.dockerClient 필드를 사용하여 Docker 데몬에 컨테이너 목록 요청을 보냅니다. dtypes.ContainerListOptions 구조체는 Docker 데몬에게 image 매개변수에서 지정한 이미지를 가진 컨테이너만 나열하도록 지시합니다.

Docker 데몬이 반환한 각 컨테이너에 대해 미리 결정된 네트워크(기본 브리지 네트워크) 내에서 컨테이너의 IP 주소를 조회합니다. 모든 RabbitMQ 컨테이너에는 관리 포트가 열려 있다는 것을 미리 알고 있으므로 컨테이너의 IP 주소와 관리 포트를 사용하여 URL을 구성하고 이 함수에서 반환할 맵에 삽입합니다.

이 함수가 반환하는 맵은 URL 문자열을 빈 구조체에 할당합니다. Go에서 빈 구조체는 메모리를 소비하지 않습니다. Go 프로그램은 프로그램이 값을 사용하지 않고 상수 시간에 키를 검색할 수 있으므로 해시 테이블의 값으로 빈 구조체를 자주 사용합니다.

새 애플리케이션 서비스 인스턴스용 토큰 생성#

등록된 애플리케이션 맵을 애플리케이션 컨테이너 맵과 조정한 후 다음이 필요합니다:

  • 등록되지 않은 애플리케이션 컨테이너를 프록시할 애플리케이션 서비스 인스턴스 시작
  • 더 이상 실행 중인 애플리케이션과 일치하지 않는 애플리케이션 서비스 인스턴스 삭제

새 애플리케이션 서비스 인스턴스를 시작하려면 토큰을 생성합니다. Teleport 서비스는 Auth 서비스에 토큰을 제시하여 클러스터에 참여할 수 있습니다.

아래 코드는 새 애플리케이션 서비스 인스턴스용 토큰을 생성합니다:

func cryptoRandomHex(len int) (string, error) {
	randomBytes := make([]byte, len)
	if _, err := rand.Read(randomBytes); err != nil {
		return "", trace.Wrap(err)
	}
	return hex.EncodeToString(randomBytes), nil
}

func (t *tokenDemoApp) createAppToken(ctx context.Context) (string, error) {
	n, err := cryptoRandomHex(tokenLenBytes)
	if err != nil {
		return "", trace.Wrap(err)
	}

	tok, err := types.NewProvisionTokenFromSpec(
		n,
		time.Now().Add(tokenTTL),
		types.ProvisionTokenSpecV2{
			Roles: types.SystemRoles{types.RoleApp},
		})

	if err := t.teleportClient.CreateToken(ctx, tok); err != nil {
		return "", trace.Wrap(err)
	}
	return n, nil
}

위 예제는 *client.Client.CreateToken을 사용하여 토큰으로 리소스를 Teleport 클러스터에 참여시키는 방법을 보여줍니다. 이미 Teleport 서비스 인스턴스(예: 애플리케이션 서비스)가 실행 중인 경우 *client.Client.CreateApp과 유사한 메서드를 사용하여 동적으로 리소스를 클러스터에 참여시키는 것이 더 간단합니다.

애플리케이션 동적 등록

새 애플리케이션 서비스 인스턴스를 시작하는 대신 애플리케이션을 동적으로 등록하려면 *clientClient.CreateApp 메서드를 사용합니다:

func (c *Client) CreateApp(ctx context.Context, app types.Application) error

이것이 작동하려면 이미 애플리케이션 서비스 인스턴스가 실행 중이어야 합니다. API 클라이언트 애플리케이션의 Teleport 사용자도 다음 권한을 가져야 합니다:

spec:
  allow:
    rules:
      - resources: ['app']
        verbs: ['create']

다른 리소스의 경우 다음 메서드를 사용하며, 각각 create 동사와 함께 해당 역할 권한이 필요합니다:

리소스 메서드 역할 내
데이터베이스 *client.Client.CreateDatabase db
Kubernetes 클러스터 *client.Client.CreateKubernetesCluster kube_cluster
Windows 데스크톱 *client.Client.CreateWindowsDesktop windows_desktop

서버는 Teleport 클러스터에 참여하기 위해 Teleport 서비스 인스턴스를 실행해야 하므로 API 클라이언트는 토큰을 사용해서만 서버를 등록할 수 있습니다.

cryptoRandomHex는 Teleport가 내부적으로 정의하는 함수의 복사본입니다. crypto/rand 패키지를 사용하여 무작위 바이트를 생성한 다음 16진수 형식의 문자열로 변환하며, 이를 토큰으로 사용합니다. 보안 암호화 기법을 사용하여 토큰을 생성할 수 있습니다.

*tokenDemoApp.createAppTokencryptoRandomHex를 호출하고 types.NewProvisionTokenFromSpec을 호출하여 결과를 사용합니다. 이는 Teleport API에 요청을 보낼 때 사용할 토큰의 사양을 반환하며, t.teleportClient.CreateToken을 통해 이를 수행합니다.

이 경우 이전에 구성한 TTL과 types.RoleApp 역할을 토큰에 할당합니다. 이는 Auth 서비스에게 토큰이 애플리케이션 서비스 인스턴스용임을 나타냅니다.

CreateToken은 토큰 생성에 실패하면 오류를 반환합니다. 성공하면 토큰을 새 애플리케이션 서비스 인스턴스를 실행하는 데 사용할 수 있도록 *tokenDemoApp.createAppToken의 호출자에게 반환합니다.

토큰 조회

클라이언트 애플리케이션은 다음 함수를 호출하여 생성한 토큰을 조회할 수 있습니다:

func (c *Client) GetTokens(ctx context.Context) ([]types.ProvisionToken, error)

이를 위해 클라이언트 애플리케이션의 Teleport 역할에 다음 권한이 필요합니다:

spec:
  allow:
    rules:
      - resources: ['token']
        verbs: ['list', 'read']

이 가이드에서 시연하는 애플리케이션에서는 애플리케이션이 이미 생성한 토큰을 알고 있으므로 토큰을 조회할 필요가 없습니다.

애플리케이션 서비스 컨테이너 시작#

프로그램은 애플리케이션 서비스 인스턴스를 시작하는 방법이 필요합니다. 이를 위해 이전에 생성한 토큰과 RabbitMQ 관리 API 엔드포인트 URL을 사용하여 컨테이너를 시작합니다:

func (t *tokenDemoApp) startApplicationServiceContainer(
	ctx context.Context,
	token string,
	u url.URL,
) error {

	name := strings.ReplaceAll(u.Hostname(), ".", "-")
	resp, err := t.dockerClient.ContainerCreate(
		ctx,
		&container.Config{
			Image: teleportImage,
			Entrypoint: strslice.StrSlice{
				"/usr/bin/dumb-init",
				"teleport",
				"start",
				"--roles=app",
				"--auth-server=" + proxyAddr,
				"--token=" + token,
				"--app-name=rabbitmq-" + name,
				"--app-uri=" + u.String(),
			},
		},
		nil,
		nil,
		nil,
		"",
	)
	if err != nil {
		return trace.Wrap(err)
	}

	err = t.dockerClient.ContainerStart(
		ctx,
		resp.ID,
		dtypes.ContainerStartOptions{},
	)
	if err != nil {
		return trace.Wrap(err)
	}

	return nil
}

Teleport 애플리케이션 서비스는 Teleport 웹 UI 주소의 하위 도메인에서 등록된 애플리케이션으로 트래픽을 리다이렉션합니다. URL 안전하지만 다른 등록된 애플리케이션과 충돌하지 않는 애플리케이션 이름이 필요합니다. 이 경우 점을 하이픈으로 대체한 RabbitMQ 컨테이너의 IP 주소를 사용합니다.

다음으로 컨테이너를 생성합니다. 컨테이너의 진입점으로 사용되는 실행 파일은 teleport 이미지가 기본적으로 사용하는 것과 동일하지만 컨테이너를 애플리케이션 서비스 인스턴스로 시작하고 RabbitMQ 컨테이너를 프록시하도록 설정하는 추가 플래그가 있습니다.

마지막으로 생성한 컨테이너의 ID를 사용하여 컨테이너를 실행합니다.

애플리케이션 서비스 인스턴스 제거#

애플리케이션 서비스 컨테이너를 생성하는 것과 함께 클라이언트 애플리케이션은 불필요한 애플리케이션 서비스 인스턴스를 제거하여 등록된 애플리케이션과 실행 중인 컨테이너를 조정합니다:

func (t *tokenDemoApp) pruneAppServiceInstance(ctx context.Context, p types.AppServer) error {
	host := p.GetHostname()

	if err := t.teleportClient.DeleteApplicationServer(
		ctx,
		p.GetNamespace(),
		p.GetHostID(),
		p.GetName(),
	); err != nil {
		return trace.Wrap(err)
	}

	fmt.Println("Deleted unnecessary Application Service record:", p.GetName())

	// Don't check errors when removing the container, since it may already
	// have been removed.
	t.dockerClient.ContainerStop(ctx, host, container.StopOptions{})
	t.dockerClient.ContainerRemove(ctx, host, dtypes.ContainerRemoveOptions{})

	fmt.Println("Deleted unnecessary Application Service container:", host)
	return nil
}

이 함수는 types.AppServer를 받아 Teleport에서 등록 해제하고 연결된 애플리케이션 서비스 컨테이너를 제거합니다.

Teleport가 오래된 애플리케이션 서비스 레코드를 자동으로 등록 해제하지만 애플리케이션 서비스 인스턴스를 중지한 후 시간이 걸릴 수 있습니다.

리소스의 TTL 변경

리소스를 백엔드에서 등록 해제하기 전에 Teleport가 리소스 가용성을 확인하는 데 사용하는 간격을 변경할 수 있습니다. 이를 위해 types.Resource 인터페이스의 SetExpiry 메서드를 사용합니다.

예를 들어 다음 SetExpiry 호출은 Teleport가 가용성을 확인하지 않는 한 10분 후에 만료되도록 WindowsDesktopV3 리소스를 구성합니다:

desktop.SetExpiry(time.Now().Add(10 * time.Minute))

애플리케이션 서비스 인스턴스를 수동으로 등록 해제하려면 pruneAppServiceInstance 함수의 p 매개변수를 사용하여 삭제할 애플리케이션 서비스 인스턴스의 네임스페이스, 호스트 ID, 이름을 가져와서 *Client.DeleteApplicationServer 메서드를 호출합니다.

Tip

Teleport 네임스페이스는 더 이상 사용되지 않지만 Teleport API 클라이언트 라이브러리에서 가끔 나타납니다. Teleport가 지원하는 유일한 네임스페이스는 default입니다.

다음으로 이 함수는 제거하려는 types.AppServer와 연결된 애플리케이션 서비스 컨테이너를 중지하고 제거합니다. 애플리케이션 서비스 레코드의 호스트명은 애플리케이션 서비스 컨테이너의 ID와 동일하므로 호스트명을 t.dockerClient.ContainerStopt.dockerClient.ContainerRemove에 전달할 수 있습니다.

등록된 애플리케이션과 애플리케이션 컨테이너 조정#

애플리케이션 서비스 인스턴스와 애플리케이션 컨테이너를 나열, 추가, 제거하는 여러 함수를 선언했습니다. 다음으로 이러한 함수를 사용하여 조정 로직을 구현합니다:

func (t *tokenDemoApp) reconcileApps() error {
	ctx := context.Background()
	apps, err := t.listRegisteredAppURLs(ctx)
	if err != nil {
		return trace.Wrap(err)
	}

	urls, err := t.listAppContainerURLs(ctx, rabbitMQImage)
	if err != nil {
		return trace.Wrap(err)
	}

	for u, _ := range urls {
		if _, ok := apps[u]; ok {
			continue
		}
		tok, err := t.createAppToken(ctx)
		if err != nil {
			return trace.Wrap(err)
		}
		fmt.Println("Created a new application token for URL: " + u)

		r, err := url.Parse(u)
		if err != nil {
			return trace.Wrap(err)
		}

		err = t.startApplicationServiceContainer(ctx, tok, *r)
		if err != nil {
			return trace.Wrap(err)
		}
		fmt.Println("Started an Application Service container to proxy URL: " + u)
	}

	for a, p := range apps {
		_, ok := urls[a]
		if ok {
			continue
		}

		if err := t.pruneAppServiceInstance(ctx, p); err != nil {
			return trace.Wrap(err)
		}
	}
	return nil
}

*tokenDemoApp.reconcileAppslistAppContainerURLslistRegisteredAppURLs를 호출하여 등록된 애플리케이션과 실행 중인 애플리케이션 컨테이너의 맵을 생성합니다. 그런 다음 각 맵의 키 내에 있는 URL을 반복하여 한 맵의 URL이 다른 맵에 있는지 확인합니다.

애플리케이션 컨테이너 맵의 URL이 등록된 애플리케이션 맵에 없는 경우, 하나의 애플리케이션이 아직 등록되지 않았음을 의미하므로 토큰을 생성하고 Teleport 애플리케이션 서비스 인스턴스를 시작합니다.

등록된 애플리케이션 맵의 URL이 애플리케이션 컨테이너 맵에 없는 경우, 불필요한 애플리케이션 서비스 인스턴스가 있음을 의미하므로 pruneAppServiceInstance를 호출하여 제거합니다.

클라이언트 초기화#

*tokenDemoApp.reconcileApps 메서드는 단일 조정을 수행합니다. 다음 단계는 프로그램의 진입점 내에서 루프에서 조정을 실행할 수 있도록 API 클라이언트를 초기화하는 것입니다:

func newTokenDemoApp() *tokenDemoApp {
	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, initTimeout)
	defer cancel()
	creds := teleport.LoadIdentityFile("auth.pem")

	t, err := teleport.New(ctx, teleport.Config{
		Addrs:       []string{proxyAddr},
		Credentials: []teleport.Credentials{creds},
	})
	if err != nil {
		panic(err)
	}
	fmt.Println("Connected to Teleport")

	d, err := docker.NewClientWithOpts(
		docker.WithAPIVersionNegotiation(),
	)
	if err != nil {
		panic(err)
	}
	fmt.Println("Connected to the Docker daemon")

	return &tokenDemoApp{
		teleportClient: t,
		dockerClient:   d,
	}

}

여기서 teleport로 별칭을 지정하는 client는 API 클라이언트 설정을 위한 Teleport의 라이브러리입니다. 플러그인은 client.LoadIdentityFile을 호출하여 client.Credentials를 얻어 Teleport 클라이언트를 초기화합니다. 그런 다음 client.Credentials를 사용하여 client.New를 호출하며, 제공된 아이덴티티 파일을 사용하여 Addrs 필드에 지정된 Teleport 프록시 서비스에 연결합니다.

Warning

이 프로그램은 자격 증명이나 Teleport 클러스터 주소를 검증하지 않습니다. 다음을 확인하세요:

  • 이전에 내보낸 아이덴티티 파일의 TTL이 만료되지 않았는지
  • teleport.ConfigAddrs 필드에 제공한 값이 Teleport 프록시 서비스의 호스트 웹 포트를 모두 포함하는지(예: mytenant.teleport.sh:443)

newTokenDemoApp 함수도 Docker 클라이언트를 초기화합니다. 버전 협상(docker.WithAPIVersionNegotiation)을 사용하여 클라이언트의 API 버전과 Docker 데몬 버전 간의 불일치로 인한 오류를 방지합니다.

진입점#

main 진입점 함수로 애플리케이션을 묶습니다:

func main() {
	fmt.Println("Starting the application")
	app := newTokenDemoApp()

	k := time.NewTicker(updateInterval)
	defer k.Stop()
	for {
		<-k.C
		if err := app.reconcileApps(); err != nil {
			panic(err)
		}
	}
}

main 함수는 newTokenDemoApp을 호출하여 API 클라이언트를 초기화합니다. 그런 다음 time.NewTicker를 호출하며, 이는 진입점이 조정 루틴을 실행하는 데 사용할 Go 채널을 반환합니다. 채널은 동시 루틴 간의 통신을 관리하는 Go 프리미티브입니다.

타이커를 생성한 후 진입점은 updateInterval이 경과할 때마다 타이커의 채널(k.C)에서 수신합니다. 채널에서 수신할 때까지 블록하며 매 간격마다 app.reconcileApps를 호출합니다.

5/5단계. 클라이언트 애플리케이션 테스트#

클라이언트 애플리케이션을 실행하여 Teleport가 인프라의 리소스를 등록하고 등록 해제하여 서비스 검색 솔루션과 동기화하는 방법을 확인합니다.

애플리케이션이 컨테이너를 생성할 수 있도록 Teleport 컨테이너 이미지를 가져왔는지 확인합니다:

$ docker image pull (=teleport.latest_oss_docker_image=)

프로젝트 디렉터리에서 다음 명령을 실행합니다:

$ go run main.go
Starting the application
Connected to Teleport
Connected to the Docker daemon

새 터미널에서 세 개의 새 RabbitMQ 컨테이너를 실행합니다:

$ for i in {1..3}; do docker run -d rabbitmq:3-management; done;

애플리케이션을 실행한 터미널에 다음과 유사한 출력이 표시됩니다:

Created a new application token for URL: http://172.17.0.4:15672
Started an Application Service container to proxy URL: http://172.17.0.4:15672
Created a new application token for URL: http://172.17.0.3:15672
Started an Application Service container to proxy URL: http://172.17.0.3:15672
Created a new application token for URL: http://172.17.0.2:15672
Started an Application Service container to proxy URL: http://172.17.0.2:15672

RabbitMQ 인스턴스가 Teleport에 등록되었는지 확인합니다:

$ tsh apps ls
Application         Description Type Public Address           Labels
------------------- ----------- ---- ------------------------ -------------------
rabbitmq-172-17-0-2             HTTP rabbitmq-172-17-0-2.3... teleport.dev/origin
rabbitmq-172-17-0-3             HTTP rabbitmq-172-17-0-3.3... teleport.dev/origin
rabbitmq-172-17-0-4             HTTP rabbitmq-172-17-0-4.3... teleport.dev/origin

다음으로 RabbitMQ 컨테이너 중 하나를 중지합니다:

$ docker stop $(docker ps --filter "ancestor=rabbitmq:3-management" -q --last 1)

애플리케이션을 실행한 터미널에 다음과 유사한 출력이 표시됩니다:

Deleted unnecessary Application Service record: rabbitmq-172-17-0-4
Deleted unnecessary Application Service container: 63facaa3033a

이제 tsh apps ls를 실행하면 두 개의 등록된 애플리케이션이 표시됩니다.

다음 단계#

실행 중인 Docker 컨테이너와 등록된 애플리케이션을 최신 상태로 유지하는 Teleport API 클라이언트를 구현했습니다. Teleport의 API를 사용하여 등록된 Teleport 리소스를 자체 서비스 검색 솔루션과 자동으로 동기화할 수 있습니다.

예제 참조#

Teleport에는 자체 자동 리소스 검색 솔루션인 Teleport 검색 서비스가 포함되어 있으며, 해당 소스를 참조하여 Teleport가 검색 로직을 구현하는 방법을 확인할 수 있습니다.

Teleport 코드 저장소에는 프로덕션 준비가 된 Teleport API 클라이언트 예제가 포함되어 있습니다. 현재 리소스를 자동 검색하는 플러그인을 유지 관리하지 않지만 이러한 예제를 사용하여 구성 파싱, 재시도 및 기타 작업을 구현하는 방법을 확인할 수 있습니다.

단기 자격 증명으로 클라이언트 애플리케이션 프로비저닝#

이 예제에서는 tctl auth sign 명령을 사용하여 작성한 프로그램의 자격 증명을 가져왔습니다. 프로덕션 사용을 위해서는 이러한 자격 증명이 도난당할 위험을 줄이는 머신 및 워크로드 아이덴티티를 통해 단기 자격 증명을 프로비저닝할 것을 권장합니다. 자세히 알아보려면 머신 및 워크로드 아이덴티티 문서를 참조하세요.