다음을 통해 공유


관리되지 않는 수신 컨트롤러 만들기

수신 컨트롤러는 역방향 프록시, 구성 가능한 트래픽 라우팅, Kubernetes 서비스에 대한 TLS 종료를 제공하는 소프트웨어입니다. Kubernetes 수신 리소스는 개별 Kubernetes 서비스에 대한 수신 규칙 및 라우팅을 구성하는 데 사용됩니다. 수신 컨트롤러 및 수신 규칙을 사용하면 단일 IP 주소를 사용하여 Kubernetes 클러스터의 여러 서비스에 트래픽을 라우팅할 수 있습니다.

이 문서에서는 AKS(Azure Kubernetes Service) 클러스터에 NGINX 수신 컨트롤러를 배포하는 방법을 보여 줍니다. 두 애플리케이션이 AKS 클러스터에서 실행되며 단일 IP 주소를 통해 각 애플리케이션에 액세스할 수 있습니다.

Important

애플리케이션 라우팅 추가 기능은 AKS의 수신에 권장됩니다. 자세한 내용은 애플리케이션 라우팅 추가 기능을 사용하여 관리되는 nginx 수신을 참조하세요.

참고 항목

Nginx를 기반으로 하는 Kubernetes에는 두 개의 오픈 소스 수신 컨트롤러가 있습니다. 하나는 Kubernetes 커뮤니티(kubernetes/ingress-nginx)에 의해 유지 관리되고 하나는 NGINX, Inc.(nginxinc/kubernetes-ingress)에 의해 유지 관리됩니다. 이 문서에서는 Kubernetes 커뮤니티 수신 컨트롤러를 사용합니다.

시작하기 전에

  • 이 문서에서는 Helm 3을 사용하여 지원되는 Kubernetes 버전에 NGINX 수신 컨트롤러를 설치합니다. Helm의 최신 릴리스를 사용하고 있으며 ingress-nginx Helm 리포지토리에 액세스할 수 있는지 확인합니다. 이 문서에 설명된 단계는 이전 버전의 Helm 차트, NGINX 수신 컨트롤러 또는 Kubernetes와 호환되지 않을 수 있습니다.
  • 이 문서에서는 통합 ACR(Azure Container Registry)이 있는 기존 AKS 클러스터가 있다고 가정합니다. 통합 ACR을 포함하는 AKS 클러스터를 만드는 방법에 대한 자세한 내용은 Azure Kubernetes Service의 Azure Container Registry를 사용하여 인증을 참조하세요.
  • Kubernetes API 상태 엔드포인트 healthz는 Kubernetes v1.16에서 더 이상 사용되지 않습니다. 대신 이 엔드포인트를 livezreadyz 엔드포인트로 바꿀 수 있습니다. 시나리오에 사용할 엔드포인트를 확인하려면 상태에 대한 Kubernetes API 엔드포인트를 참조하세요.
  • Azure CLI를 사용하는 경우 이 문서에서는 Azure CLI 버전 2.0.64 이상을 실행해야 합니다. az --version을 실행하여 버전을 찾습니다. 설치 또는 업그레이드해야 하는 경우 Azure CLI 설치를 참조하세요.
  • Azure PowerShell을 사용하는 경우 이 문서에서는 Azure PowerShell 버전 5.9.0 이상을 실행해야 합니다. Get-InstalledModule -Name Az을 실행하여 버전을 찾습니다. 설치 또는 업그레이드해야 하는 경우 Azure PowerShell 설치를 참조하세요.

기본 구성

기본값을 사용자 지정하지 않고 기본 NGINX 수신 컨트롤러를 만들려면 Helm을 사용합니다. 다음 구성은 단순성을 위해 기본 구성을 사용합니다. 배포를 사용자 지정하기 위한 매개 변수(예: --set controller.replicaCount=3)를 추가할 수 있습니다.

참고 항목

클러스터의 컨테이너에 대한 요청에 대해 클라이언트 원본 IP 유지를 사용하도록 설정하려면 --set controller.service.externalTrafficPolicy=Local을 Helm 설치 명령에 추가합니다. 클라이언트 원본 IP가 X-Forwarded-For 아래 요청 헤더에 저장됩니다. 클라이언트 원본 IP 유지가 활성화된 수신 컨트롤러를 사용하는 경우 TLS 통과는 작동하지 않습니다.

NAMESPACE=ingress-basic

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

helm install ingress-nginx ingress-nginx/ingress-nginx \
  --create-namespace \
  --namespace $NAMESPACE \
  --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz \
  --set controller.service.externalTrafficPolicy=Local

참고 항목

이 자습서에서는 service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path을(를) /healthz(으)로 설정합니다. 즉, /healthz에 대한 요청의 응답 코드가 200이(가) 아닌 경우 전체 수신 컨트롤러가 다운됩니다. 사용자 고유의 시나리오에서 값을 다른 URI로 수정할 수 있습니다. 이 부분을 삭제하거나 값을 설정 해제할 수 없거나 수신 컨트롤러가 계속 다운됩니다. Kubernetes 관계자가 제공하는 이 자습서에서 사용되는 ingress-nginx 패키지는 수신 규칙이 덮어쓰지 않는 한 사용자가 빠른 시작을 할 수 있도록 기본 백 엔드로 설계되었으므로 /healthz을(를) 요청하는 경우 항상 200 응답 코드를 반환합니다.

사용자 지정된 구성

위의 섹션에서 제공하는 기본 구성 대신 다음 단계 세트에서는 사용자 지정 수신 컨트롤러를 배포하는 방법을 보여 줍니다. 내부 고정 IP 주소를 사용하거나 동적 공용 IP 주소를 사용할 수 있습니다.

Helm 차트에서 사용하는 이미지를 ACR로 가져오기

이미지 버전을 제어하려면 자신의 Azure Container Registry로 가져와야 합니다. NGINX 수신 컨트롤러 Helm 차트는 세 개의 컨테이너 이미지를 사용합니다. az acr import를 사용하여 해당 이미지를 ACR로 가져옵니다.

REGISTRY_NAME=<REGISTRY_NAME>
SOURCE_REGISTRY=registry.k8s.io
CONTROLLER_IMAGE=ingress-nginx/controller
CONTROLLER_TAG=v1.8.1
PATCH_IMAGE=ingress-nginx/kube-webhook-certgen
PATCH_TAG=v20230407
DEFAULTBACKEND_IMAGE=defaultbackend-amd64
DEFAULTBACKEND_TAG=1.5

az acr import --name $REGISTRY_NAME --source $SOURCE_REGISTRY/$CONTROLLER_IMAGE:$CONTROLLER_TAG --image $CONTROLLER_IMAGE:$CONTROLLER_TAG
az acr import --name $REGISTRY_NAME --source $SOURCE_REGISTRY/$PATCH_IMAGE:$PATCH_TAG --image $PATCH_IMAGE:$PATCH_TAG
az acr import --name $REGISTRY_NAME --source $SOURCE_REGISTRY/$DEFAULTBACKEND_IMAGE:$DEFAULTBACKEND_TAG --image $DEFAULTBACKEND_IMAGE:$DEFAULTBACKEND_TAG

참고 항목

컨테이너 이미지를 ACR로 가져오는 것 외에도 Helm 차트를 ACR로 가져올 수도 있습니다. 자세한 내용은 Azure Container Registry에 Helm 차트 푸시 및 끌어오기를 참조하세요.

수신 컨트롤러 만들기

수신 컨트롤러를 만들려면 Helm을 사용하여 ingress-nginx를 설치합니다. 수신 컨트롤러를 Linux 노드에서 예약해야 합니다. Windows Server 노드가 수신 컨트롤러를 실행해서는 안 됩니다. --set nodeSelector 매개 변수를 사용하여 노드 선택기를 지정하면 Linux 기반 노드에서 NGINX 수신 컨트롤러를 실행하도록 Kubernetes 스케줄러에 지시할 수 있습니다.

중복성을 추가하기 위해 NGINX 수신 컨트롤러의 두 복제본이 --set controller.replicaCount 매개 변수와 함께 배포됩니다. 수신 컨트롤러의 복제본을 실행하는 이점을 최대한 활용하려면 AKS 클러스터에 둘 이상의 노드가 있어야 합니다.

다음 예제는 ingress-basic이라는 수신 리소스에 대한 Kubernetes 네임스페이스를 만들고 해당 네임스페이스 내에서 작동하도록 작성되었습니다. 필요에 따라 사용자 환경에 대한 네임스페이스를 지정합니다. AKS 클러스터가 Kubernetes 역할 기반 액세스 제어를 사용하도록 설정되어 있지 않으면 --set rbac.create=false를 Helm 명령에 추가합니다.

참고 항목

클러스터의 컨테이너에 대한 요청에 대해 클라이언트 원본 IP 유지를 사용하도록 설정하려면 --set controller.service.externalTrafficPolicy=Local을 Helm 설치 명령에 추가합니다. 클라이언트 원본 IP가 X-Forwarded-For 아래 요청 헤더에 저장됩니다. 클라이언트 원본 IP 유지가 활성화된 수신 컨트롤러를 사용하는 경우 TLS 통과는 작동하지 않습니다.

# Add the ingress-nginx repository
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

# Set variable for ACR location to use for pulling images
ACR_LOGIN_SERVER=<REGISTRY_LOGIN_SERVER>

# Use Helm to deploy an NGINX ingress controller
helm install ingress-nginx ingress-nginx/ingress-nginx \
    --version 4.7.1 \
    --namespace ingress-basic \
    --create-namespace \
    --set controller.replicaCount=2 \
    --set controller.nodeSelector."kubernetes\.io/os"=linux \
    --set controller.image.registry=$ACR_LOGIN_SERVER \
    --set controller.image.image=$CONTROLLER_IMAGE \
    --set controller.image.tag=$CONTROLLER_TAG \
    --set controller.image.digest="" \
    --set controller.admissionWebhooks.patch.nodeSelector."kubernetes\.io/os"=linux \
    --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz \
    --set controller.service.externalTrafficPolicy=Local \
    --set controller.admissionWebhooks.patch.image.registry=$ACR_LOGIN_SERVER \
    --set controller.admissionWebhooks.patch.image.image=$PATCH_IMAGE \
    --set controller.admissionWebhooks.patch.image.tag=$PATCH_TAG \
    --set controller.admissionWebhooks.patch.image.digest="" \
    --set defaultBackend.nodeSelector."kubernetes\.io/os"=linux \
    --set defaultBackend.image.registry=$ACR_LOGIN_SERVER \
    --set defaultBackend.image.image=$DEFAULTBACKEND_IMAGE \
    --set defaultBackend.image.tag=$DEFAULTBACKEND_TAG \
    --set defaultBackend.image.digest=""

내부 IP 주소를 사용하여 수신 컨트롤러 구성

기본적으로는 동적 공용 IP 주소 할당을 통해 NGINX 수신 컨트롤러를 만듭니다. 일반적인 구성 요구 사항은 내부 프라이빗 네트워크 및 IP 주소를 사용하는 것입니다. 이 접근 방식을 사용하면 외부 액세스 없이 서비스 액세스를 내부 사용자로 제한할 수 있습니다.

--set controller.service.loadBalancerIP--set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-internal"=true 매개 변수를 사용하여 수신 컨트롤러에 내부 IP 주소를 할당합니다. 수신 컨트롤러와 함께 사용할 고유한 내부 IP 주소를 제공합니다. 가상 네트워크 내에서 이 IP 주소가 이미 사용되고 있지 않은지 확인합니다. 기존 가상 네트워크 및 서브넷을 사용하는 경우 가상 네트워크 및 서브넷을 관리할 수 있는 올바른 권한을 사용하여 AKS 클러스터를 구성해야 합니다. 자세한 내용은 AKS(Azure Kubernetes service)에서 고유한 IP 주소 범위와 함께 kubenet 네트워킹 사용 또는 AKS(Azure Kubernetes Service)에서 Azure CNI 네트워킹 구성을 참조하세요.

# Add the ingress-nginx repository
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

# Set variable for ACR location to use for pulling images
ACR_LOGIN_SERVER=<REGISTRY_LOGIN_SERVER>

# Use Helm to deploy an NGINX ingress controller
helm install ingress-nginx ingress-nginx/ingress-nginx \
    --version 4.7.1 \
    --namespace ingress-basic \
    --create-namespace \
    --set controller.replicaCount=2 \
    --set controller.nodeSelector."kubernetes\.io/os"=linux \
    --set controller.image.registry=$ACR_LOGIN_SERVER \
    --set controller.image.image=$CONTROLLER_IMAGE \
    --set controller.image.tag=$CONTROLLER_TAG \
    --set controller.image.digest="" \
    --set controller.admissionWebhooks.patch.nodeSelector."kubernetes\.io/os"=linux \
    --set controller.service.loadBalancerIP=10.224.0.42 \
    --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-internal"=true \
    --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz \
    --set controller.admissionWebhooks.patch.image.registry=$ACR_LOGIN_SERVER \
    --set controller.admissionWebhooks.patch.image.image=$PATCH_IMAGE \
    --set controller.admissionWebhooks.patch.image.tag=$PATCH_TAG \
    --set controller.admissionWebhooks.patch.image.digest="" \
    --set defaultBackend.nodeSelector."kubernetes\.io/os"=linux \
    --set defaultBackend.image.registry=$ACR_LOGIN_SERVER \
    --set defaultBackend.image.image=$DEFAULTBACKEND_IMAGE \
    --set defaultBackend.image.tag=$DEFAULTBACKEND_TAG \
    --set defaultBackend.image.digest="" 

부하 분산 장치 서비스 확인

kubectl get services를 사용하여 부하 분산 장치 서비스를 확인합니다.

kubectl get services --namespace ingress-basic -o wide -w ingress-nginx-controller

NGINX 수신 컨트롤러에 대해 Kubernetes 부하 분산 장치 서비스를 만든 경우 다음 예제 출력에 표시된 대로 EXTERNAL-IP 아래에 IP 주소가 할당됩니다.

NAME                       TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)                      AGE   SELECTOR
ingress-nginx-controller   LoadBalancer   10.0.65.205   EXTERNAL-IP     80:30957/TCP,443:32414/TCP   1m   app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx

이 단계에서 외부 IP 주소로 이동하면 404 페이지가 표시됩니다. 그 이유는 아직 외부 IP에 대한 연결을 설정해야 하기 때문이며, 이는 다음 섹션에서 수행됩니다.

데모 애플리케이션 실행

작동 중인 수신 컨트롤러를 확인하려면 AKS 클러스터에서 두 개의 데모 애플리케이션을 실행합니다. 이 예제에서는 kubectl apply을 사용하여 간단한 Hello world 애플리케이션의 두 인스턴스를 배포합니다.

  1. aks-helloworld-one.yaml 파일을 만들고 다음 예제 YAML을 복사합니다.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: aks-helloworld-one  
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: aks-helloworld-one
      template:
        metadata:
          labels:
            app: aks-helloworld-one
        spec:
          containers:
          - name: aks-helloworld-one
            image: mcr.microsoft.com/azuredocs/aks-helloworld:v1
            ports:
            - containerPort: 80
            env:
            - name: TITLE
              value: "Welcome to Azure Kubernetes Service (AKS)"
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: aks-helloworld-one  
    spec:
      type: ClusterIP
      ports:
      - port: 80
      selector:
        app: aks-helloworld-one
    
  2. aks-helloworld-two.yaml 파일을 만들고 다음 예제 YAML을 복사합니다.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: aks-helloworld-two  
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: aks-helloworld-two
      template:
        metadata:
          labels:
            app: aks-helloworld-two
        spec:
          containers:
          - name: aks-helloworld-two
            image: mcr.microsoft.com/azuredocs/aks-helloworld:v1
            ports:
            - containerPort: 80
            env:
            - name: TITLE
              value: "AKS Ingress Demo"
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: aks-helloworld-two  
    spec:
      type: ClusterIP
      ports:
      - port: 80
      selector:
        app: aks-helloworld-two
    
  3. kubectl apply을 사용하여 두 개의 데모 애플리케이션을 실행합니다.

    kubectl apply -f aks-helloworld-one.yaml --namespace ingress-basic
    kubectl apply -f aks-helloworld-two.yaml --namespace ingress-basic
    

수신 경로 만들기

이제 두 애플리케이션이 모두 Kubernetes 클러스터에서 실행됩니다. 트래픽을 각 애플리케이션으로 라우팅하려면 Kubernetes 수신 리소스를 만듭니다. 수신 리소스는 두 애플리케이션 중 하나로 트래픽을 라우팅하는 규칙을 구성합니다.

다음 예제에서 EXTERNAL_IP/hello-world-one으로의 트래픽은 aks-helloworld-one이라는 서비스로 라우팅됩니다. EXTERNAL_IP/hello-world-two로 향하는 트래픽은 aks-helloworld-two 서비스로 라우팅됩니다. EXTERNAL_IP/static으로 향하는 트래픽은 고정적인 자산의 aks-helloworld-one이라는 서비스로 라우팅됩니다.

  1. hello-world-ingress.yaml 파일을 만들고 다음 예제 YAML을 복사합니다.

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: hello-world-ingress
      annotations:
        nginx.ingress.kubernetes.io/ssl-redirect: "false"
        nginx.ingress.kubernetes.io/use-regex: "true"
        nginx.ingress.kubernetes.io/rewrite-target: /$2
    spec:
      ingressClassName: nginx
      rules:
      - http:
          paths:
          - path: /hello-world-one(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: aks-helloworld-one
                port:
                  number: 80
          - path: /hello-world-two(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: aks-helloworld-two
                port:
                  number: 80
          - path: /(.*)
            pathType: Prefix
            backend:
              service:
                name: aks-helloworld-one
                port:
                  number: 80
    ---
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: hello-world-ingress-static
      annotations:
        nginx.ingress.kubernetes.io/ssl-redirect: "false"
        nginx.ingress.kubernetes.io/rewrite-target: /static/$2
    spec:
      ingressClassName: nginx
      rules:
      - http:
          paths:
          - path: /static(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: aks-helloworld-one
                port: 
                  number: 80
    
  2. kubectl apply 명령을 사용하여 수신 리소스를 만듭니다.

    kubectl apply -f hello-world-ingress.yaml --namespace ingress-basic
    

수신 컨트롤러 테스트

수신 컨트롤러의 경로를 테스트하려면 두 개의 애플리케이션으로 이동합니다. NGINX 수신 컨트롤러의 IP 주소(예: EXTERNAL_IP)에 대한 웹 브라우저를 엽니다. 첫 번째 데모 애플리케이션은 다음 예제와 같이 웹 브라우저에 표시됩니다.

수신 컨트롤러 뒤에서 실행 중인 첫 번째 앱

이제 EXTERNAL_IP/hello-world-two와 같은 IP 주소에 /hello-world-two 경로를 추가합니다. 사용자 지정 제목이 있는 두 번째 데모 애플리케이션이 표시됩니다.

수신 컨트롤러 뒤에서 실행 중인 두 번째 앱

내부 IP 주소 테스트

  1. 테스트 Pod를 만들고 여기에 터미널 세션을 연결합니다.

    kubectl run -it --rm aks-ingress-test --image=mcr.microsoft.com/dotnet/runtime-deps:6.0 --namespace ingress-basic
    
  2. apt-get을 사용하여 Pod에 curl을 설치합니다.

    apt-get update && apt-get install -y curl
    
  3. http://10.224.0.42와 같은 curl을 사용하여 Kubernetes 수신 컨트롤러의 주소에 액세스합니다. 수신 컨트롤러를 배포할 때 지정한 고유한 내부 IP 주소를 제공합니다.

    curl -L http://10.224.0.42
    

    주소와 함께 경로가 제공되지 않았으므로 수신 컨트롤러는 기본값인 / 경로로 설정됩니다. 첫 번째 데모 애플리케이션은 다음 축소된 예제 출력에 표시된 대로 반환됩니다.

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <link rel="stylesheet" type="text/css" href="/static/default.css">
        <title>Welcome to Azure Kubernetes Service (AKS)</title>
    [...]
    
  4. http://10.224.0.42/hello-world-two와 같은 주소에 /hello-world-two 경로를 추가합니다.

    curl -L -k http://10.224.0.42/hello-world-two
    

    사용자 지정 제목이 있는 두 번째 데모 애플리케이션은 다음 축소된 예제 출력에 표시된 대로 반환됩니다.

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <link rel="stylesheet" type="text/css" href="/static/default.css">
        <title>AKS Ingress Demo</title>
    [...]
    

리소스 정리

이 문서에서는 Helm을 사용하여 수신 구성 요소 및 샘플 앱을 설치했습니다. Helm 차트를 배포하면 다수의 Kubernetes 리소스가 생성됩니다. 이러한 리소스에는 Pod, 배포 및 서비스가 포함됩니다. 이러한 리소스를 정리하려면 전체 샘플 네임스페이스 또는 개별 리소스를 삭제하면 됩니다.

샘플 네임스페이스 및 모든 리소스 삭제

전체 샘플 네임스페이스를 삭제하려면 kubectl delete 명령을 사용하고 네임스페이스 이름을 지정합니다. 네임스페이스의 모든 리소스가 삭제됩니다.

kubectl delete namespace ingress-basic

리소스를 개별적으로 삭제하거나

또는 보다 세분화된 접근 방식으로서 생성된 개별 리소스를 삭제합니다.

  1. helm list 명령을 사용하여 Helm 릴리스를 나열합니다.

    helm list --namespace ingress-basic
    

    다음 예제 출력과 같이 ingress-nginxaks-helloworld라는 차트를 찾습니다.

    NAME                    NAMESPACE       REVISION        UPDATED                                 STATUS          CHART                   APP VERSION
    ingress-nginx           ingress-basic   1               2020-01-06 19:55:46.358275 -0600 CST    deployed        nginx-ingress-1.27.1    0.26.1  
    
  2. helm uninstall 명령으로 해당 릴리스를 제거합니다.

    helm uninstall ingress-nginx --namespace ingress-basic
    
  3. 두 개의 샘플 애플리케이션을 제거합니다.

    kubectl delete -f aks-helloworld-one.yaml --namespace ingress-basic
    kubectl delete -f aks-helloworld-two.yaml --namespace ingress-basic
    
  4. 트래픽을 샘플 앱으로 유도한 수신 경로를 제거합니다.

    kubectl delete -f hello-world-ingress.yaml
    
  5. kubectl delete 명령을 사용하고 네임스페이스 이름을 지정하여 네임스페이스를 삭제합니다.

    kubectl delete namespace ingress-basic
    

다음 단계

기존 수신 구성 요소를 사용하여 TLS를 구성하려면 수신 컨트롤러에서 TLS 사용을 참조하세요.

HTTP 애플리케이션 라우팅을 사용하도록 AKS 클러스터를 구성하려면 HTTP 애플리케이션 라우팅 추가 기능 사용을 참조하세요.

이 문서에는 AKS의 몇 가지 외부 구성 요소가 포함되었습니다. 이러한 구성 요소에 대한 자세한 내용은 다음 프로젝트 페이지를 참조하세요.