Google Cloud Run 포트에 대해 생각해 보세요.

당 기사에 대해서



2019/11/15에 GA가 되었다 GCP의 컨테이너 서버리스 실행 환경인 Google Cloud Run의 포트에 대해 이해를 깊게 하는 기사입니다.
공식 문서의 퀵 스타트에서 포트에 관한 부분을 읽어내면서 해설합니다.

참고로 한 사이트


  • 빠른 시작: 빌드 및 배포
  • Cloud Run 제약

  • 컨테이너 내부에서 사용할 포트



    먼저, 컨테이너 내부에서 사용하는 포트를 이행하기 위해 빠른 시작 애플리케이션을 살펴 보겠습니다.

    /helloworld.go
    package main
    
    import (
            "fmt"
            "log"
            "net/http"
            "os"
    )
    
    func handler(w http.ResponseWriter, r *http.Request) {
            log.Print("Hello world received a request.")
            target := os.Getenv("TARGET")
            if target == "" {
                    target = "World"
            }
            fmt.Fprintf(w, "Hello %s!\n", target)
    }
    
    func main() {
            log.Print("Hello world sample started.")
    
            http.HandleFunc("/", handler)
    
            port := os.Getenv("PORT")
            if port == "" {
                    port = "8080"
            }
    
            log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
    }
    

    handler 함수



    handler 함수에서는, 환경 변수 "TARGET"로부터 값을 취득해, Hello 뒤에 붙여 인수의 ResponseWriter에 출력하고 있는 것을 알 수 있습니다. 환경 변수 "TARGET"이 없으면 "World"가 들어갑니다.

    main 함수



    main 함수에서는 다음 두 가지를 수행하는 것을 알 수 있습니다.
    1. 루트에서 요청을 기다리고 요청이 왔을 때 위의 handler 함수를 호출합니다.
    2. 환경 변수 "PORT"에서 값을 검색하여 검색된 값의 포트 번호로 요청을 수락합니다. 환경 변수 "PORT"가 없으면 "8080"이 들어갑니다.

    컨테이너 내부에서 움직이는 어플리케이션은 환경 변수 "PORT"의 값으로 움직이고 있는 것을 알 수 있습니다.

    환경 변수 "PORT"의 내용



    Google Cloud Run의 내부 환경 변수 "PORT"가 어떻게 되어 있는지, 쿡 스타트에 있는 샘플 소스의 main 함수를 다음과 같이 빌드 & 배포해 봅니다.

    /helloworld.go
    func main() {
            log.Print("Hello world sample started.")
    
            http.HandleFunc("/", handler)
    
            port := os.Getenv("PORT")
            if port == "" {
                    port = "8080"
            } else {
                log.Printf("PORT is %s", port)
            }
    
            log.Printf("Hello world sample is listening on port %s.", port)
    
            log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
    }
    
    $ gcloud builds submit --tag gcr.io/[PROJECT_ID]/helloworld
    (省略)
    $ gcloud  run deploy --image gcr.io/[PROJECT_ID]/helloworld
    (省略)
    Service [helloworld] revision [helloworld-XXXXXX-vof] has been deployed and is serving 100 percent of traffic at https://helloworld-XXXXXXXXX-an.a.run.app
    

    배포 후 Logging에서 로그를 확인하면 다음 이미지와 같습니다.


    Google Cloud Run에서 실행되는 컨테이너의 환경 변수 "PORT"는 8080임을 알 수 있습니다.

    외부에서 연결된 포트



    외부로부터 접속되는 포트를 조사하기 위해서, 실제로 cURL을 사용해 액세스 해 봅니다.
    $ curl https://helloworld-XXXXXXXXX-an.a.run.app
    Hello World!
    $ curl https://helloworld-XXXXXXXXX-an.a.run.app:443
    Hello World!
    $ curl https://helloworld-XXXXXXXXX-an.a.run.app:80
    curl: (35) error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol
    $ curl https://helloworld-XXXXXXXXX-an.a.run.app:8080
    ^C
    $ curl https://helloworld-XXXXXXXXX-an.a.run.app:8081
    ^C
    

    포트 번호 443을 사용하고 있음을 알 수 있습니다. 8080 또는 8081과 같이 관련이 없는 포트에는 응답이 없었습니다.

    외부 포트와 내부 포트의 연결



    외부 443 포트와 컨테이너 내부 8080 포트가 환경 변수 "PORT"를 통해 연결되어 있음을 알 수 있습니다.
    그림과 같이 됩니다.



    배포시 명령에 포트를 연결하는 옵션은 없기 때문에 Docker 명령에서 말하는 "-p 443:8080"과 같은 매개 변수는 배포시 자동으로 수행됩니다.

    대기 포트 구현 방법



    배포 시 자동으로 연결할 수 있다는 것은 컨테이너 내부에서 대기하는 포트가 Google Cloud Run의 제약으로 정해져 있다는 것입니다. Cloud Run 제약 을 읽어 보면, 컨테이너 내부에서 대기하는 포트는 환경 변수 "PORT"에 들어가 있다고 쓰고 있습니다.
    Google Cloud Run에서 실행되는 애플리케이션은 환경 변수 "PORT"를 가져와 해당 번호의 포트에서 기다리도록 합시다.

    또한 이미 구축된 컨테이너를 Google Cloud Run에 배포하는 경우 대기 포트가 무엇인지 확인하는 것이 좋습니다.

    좋은 웹페이지 즐겨찾기