직접 VPC 이그레스를 사용하여 내부 Cloud Run 서비스에 액세스하도록 Cloud Run 서비스를 구성하는 방법

1. 소개

개요

많은 조직에서 서비스와 애플리케이션의 네트워크 트래픽을 보호하기 위해 경계 제어와 함께 Google Cloud의 가상 프라이빗 클라우드 (VPC) 네트워크를 사용하여 데이터 무단 반출을 방지합니다. VPC 네트워크는 Google의 프로덕션 네트워크 내에서 구현되는 물리적 네트워크의 가상 버전입니다. VPC 네트워크는 Compute Engine 가상 머신 (VM) 인스턴스에 연결을 제공하고, 내부 패스 스루 네트워크 부하 분산기와 내부 애플리케이션 부하 분산기용 프록시 시스템을 기본적으로 제공하며, Cloud VPN 터널과 Cloud Interconnect용 VLAN 연결을 사용하여 온프레미스 네트워크에 연결하고, Google Cloud 외부 부하 분산기의 트래픽을 백엔드로 분산합니다.

VM과 달리 Cloud Run 서비스는 기본적으로 특정 VPC 네트워크와 연결되지 않습니다. 이 Codelab에서는 VPC에서 들어오는 트래픽만 Cloud Run 서비스 (예: 백엔드 서비스)에 액세스할 수 있도록 인그레스 (인바운드 연결) 설정을 변경하는 방법을 보여줍니다. 또한 이 Codelab에서는 두 번째 서비스 (예: 프런트엔드 서비스)가 VPC를 통해 백엔드 Cloud Run 서비스에 액세스하는 방법을 보여줍니다.

이 예시에서 백엔드 Cloud Run 서비스는 hello world를 반환합니다. 프런트엔드 Cloud Run 서비스는 URL을 수집하기 위해 UI에 입력 필드를 제공합니다. 그런 다음 프런트엔드 서비스가 해당 URL(예: 백엔드 서비스)에 GET 요청을 하므로 브라우저에서 서비스로의 요청이 아닌 서비스 간 요청이 됩니다. 프런트엔드 서비스가 백엔드에 도달할 수 있으면 브라우저에 hello world 메시지가 표시됩니다.

학습할 내용

  • VPC의 트래픽만 Cloud Run 서비스에 허용하는 방법
  • 내부 인그레스 전용 Cloud Run 서비스와 통신하도록 Cloud Run 서비스에서 이그레스를 구성하는 방법

2. 설정 및 요구사항

기본 요건

Cloud Shell 활성화

  1. Cloud Console에서 Cloud Shell 활성화d1264ca30785e435.png를 클릭합니다.

cb81e7c8e34bc8d.png

Cloud Shell을 처음 시작하는 경우 설명이 포함된 중간 화면이 제공됩니다. 중간 화면이 표시되면 계속을 클릭합니다.

d95252b003979716.png

Cloud Shell을 프로비저닝하고 연결하는 작업은 몇 분이면 끝납니다.

7833d5e1c5d18f54.png

이 가상 머신에는 필요한 개발 도구가 모두 로드되어 있습니다. 영구적인 5GB 홈 디렉터리를 제공하고 Google Cloud에서 실행되므로 네트워크 성능과 인증이 크게 개선됩니다. 이 Codelab에서 대부분의 작업은 브라우저로 수행할 수 있습니다.

Cloud Shell에 연결되면 인증이 완료되었고 프로젝트가 해당 프로젝트 ID로 설정된 것을 확인할 수 있습니다.

  1. Cloud Shell에서 다음 명령어를 실행하여 인증되었는지 확인합니다.
gcloud auth list

명령어 결과

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Cloud Shell에서 다음 명령어를 실행하여 gcloud 명령어가 프로젝트를 알고 있는지 확인합니다.
gcloud config list project

명령어 결과

[core]
project = <PROJECT_ID>

또는 다음 명령어로 설정할 수 있습니다.

gcloud config set project <PROJECT_ID>

명령어 결과

Updated property [core/project].

3. Cloud Run 서비스 만들기

환경 변수 설정

이 Codelab 전체에서 사용할 환경 변수를 설정할 수 있습니다.

REGION=<YOUR_REGION, e.g. us-central1>
FRONTEND=frontend
BACKEND=backend

백엔드 Cloud Run 서비스 만들기

먼저 소스 코드 디렉터리를 만들고 해당 디렉터리로 cd합니다.

mkdir -p internal-codelab/frontend internal-codelab/backend && cd internal-codelab/backend

그런 다음 다음 콘텐츠로 package.json 파일을 만듭니다.

{
    "name": "backend-service",
    "version": "1.0.0",
    "description": "",
    "scripts": {
        "start": "node index.js"
    },
    "dependencies": {
        "express": "^4.18.1"
    }
}

그런 다음 아래 콘텐츠로 index.js 소스 파일을 만듭니다. 이 파일에는 서비스의 진입점이 포함되어 있으며 앱의 기본 로직이 포함되어 있습니다.

const express = require('express');

const app = express();

app.use(express.urlencoded({ extended: true }));

app.get('/', function (req, res) {
    res.send("hello world");
});

const port = parseInt(process.env.PORT) || 8080;
app.listen(port, () => {
    console.log(`helloworld: listening on port ${port}`);
});

마지막으로 다음 명령어를 실행하여 Cloud Run 서비스를 배포합니다.

gcloud run deploy $BACKEND --source . --allow-unauthenticated --region $REGION

프런트엔드 Cloud Run 서비스 만들기

프런트엔드 디렉터리로 이동합니다.

cd ../frontend

그런 다음 다음 콘텐츠로 package.json 파일을 만듭니다.

{
  "name": "frontend",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "start": "node index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^1.6.6",
    "express": "^4.18.2"
  }
}

그런 다음 아래 콘텐츠로 index.js 소스 파일을 만듭니다. 이 파일에는 서비스의 진입점이 포함되어 있으며 앱의 기본 로직이 포함되어 있습니다.

const express = require("express");
const app = express();
const port = 8080;
const path = require('path');
const axios = require('axios');

// serve static content (index.html) using
// built-in middleware function in Express 
app.use(express.static('public'));
app.use(express.urlencoded({ extended: true }));

// this endpoint receives a URL in the post body
// and then makes a get request to that URL
// results are sent back to the caller
app.post('/callService', async (req, res) => {

    const url = req.body.url;
    let message = "";

    try {
        console.log("url: ", url);
        const response = await axios.get(url);
        message = response.data;

    } catch (error) {
        message = error.message;
        console.error(error.message);
    }

    res.send(`
        ${message}
        <p>
        </p>
    `);
});

app.listen(port, () => {
    console.log(`Example app listening on port ${port}`);
});

index.html 파일의 공개 디렉터리 만들기

mkdir public
touch public/index.html

다음 내용을 포함하도록 index.html을 업데이트합니다.

<html>
  <script
    src="https://unpkg.com/htmx.org@1.9.10"
    integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC"
    crossorigin="anonymous"
  ></script>
  <body>
    <div style="margin-top: 100px; margin-left: 100px">
      <h1>I'm the Frontend service on the Internet</h1>
      <form hx-trigger="submit" hx-post="/callService" hx-target="#message">
        <label for="url"> URL:</label>
        <input
          style="width: 308px"
          type="text"
          id="url"
          name="url"
          placeholder="The backend service URL"
          required
        />
        <button hx-indicator="#loading" type="submit">Submit</button>
        <p></p>
        <span class="htmx-indicator" id="loading"> Loading... </span>
        <div id="message" style="white-space: pre-wrap"></div>
        <p></p>
      </form>
    </div>
  </body>
</html>

마지막으로 다음 명령어를 실행하여 Cloud Run 서비스를 배포합니다.

gcloud run deploy $FRONTEND --source . --allow-unauthenticated --region $REGION

백엔드 서비스 호출

두 개의 Cloud Run 서비스가 성공적으로 배포되었는지 확인합니다.

웹브라우저에서 프런트엔드 서비스의 URL을 엽니다.

텍스트 상자에 백엔드 서비스의 URL을 입력합니다. 이 요청은 브라우저에서 라우팅되는 것이 아니라 프런트엔드 Cloud Run 인스턴스에서 백엔드 Cloud Run 서비스로 라우팅됩니다.

'hello world'가 표시됩니다.

4. 내부 인그레스 전용 백엔드 서비스 설정

다음 gcloud 명령어를 실행하여 VPC 네트워크 내의 트래픽만 백엔드 서비스에 액세스하도록 허용합니다.

gcloud run services update $BACKEND --ingress internal --region $REGION

백엔드 서비스가 VPC의 트래픽만 수신할 수 있는지 확인하려면 프런트엔드 서비스에서 백엔드 서비스를 다시 호출해 보세요.

이번에는 '상태 코드 404로 요청이 실패했습니다'가 표시됩니다.

프런트엔드 Cloud Run 서비스 아웃바운드 요청 (즉, 이그레스)이 먼저 인터넷으로 전송되므로 Google Cloud에서 요청의 출처를 알 수 없어 이 오류가 발생했습니다.

다음 섹션에서는 VPC에 액세스하도록 프런트엔드 서비스를 구성합니다. 그러면 Google Cloud에서 요청이 내부 소스로 인식되는 VPC에서 왔음을 알 수 있습니다.

5. VPC에 액세스하도록 프런트엔드 서비스 구성

이 섹션에서는 VPC를 통해 백엔드 서비스와 통신하도록 프런트엔드 Cloud Run 서비스를 구성합니다.

이렇게 하려면 프런트엔드 Cloud Run 인스턴스에 직접 VPC 이그레스를 추가하여 서비스가 VPC 내에서 사용할 내부 IP를 제공해야 합니다. 그런 다음 프런트엔드 서비스의 모든 아웃바운드 연결이 VPC로 이동하도록 이그레스를 구성합니다.

먼저 다음 명령어를 실행하여 직접 VPC 이그레스를 사용 설정합니다.

gcloud beta run services update $FRONTEND \
--network=default \
--subnet=default \
--vpc-egress=all-traffic \
--region=$REGION

이제 프런트엔드 서비스가 VPC에 액세스할 수 있는지 확인할 수 있습니다.

gcloud beta run services describe $FRONTEND \
--region=$REGION

다음과 비슷한 출력이 표시됩니다.

VPC access:
    Network:         default
    Subnet:          default
    Egress:          all-traffic

이제 프런트엔드 서비스에서 백엔드 서비스를 다시 호출해 보세요.

이번에는 'hello world'가 표시됩니다.

참고: 모든 이그레스가 VPC로 라우팅되었으므로 프런트엔드 서비스에는 인터넷 액세스 권한이 없습니다. 예를 들어 프런트엔드 서비스가 https://curlmyip.org/에 액세스하려고 하면 시간이 초과됩니다.

6. 축하합니다.

축하합니다. Codelab을 완료했습니다.

Cloud Run 문서와 Cloud Run 서비스용 비공개 네트워킹 구성 방법을 검토하는 것이 좋습니다.

학습한 내용

  • VPC의 트래픽만 Cloud Run 서비스에 허용하는 방법
  • 내부 인그레스 전용 Cloud Run 서비스와 통신하도록 Cloud Run 서비스에서 이그레스를 구성하는 방법

7. 삭제

의도치 않은 요금이 청구되지 않도록 하려면(예: Cloud Run 서비스가 무료 등급의 월별 Cloud Run 호출 할당량보다 더 많이 호출되는 경우) Cloud Run을 삭제하거나 2단계에서 만든 프로젝트를 삭제하면 됩니다.

Cloud Run 서비스를 삭제하려면 https://console.cloud.google.com/run에서 Cloud Run Cloud Console로 이동하여 $FRONTEND 및 $BACKEND 서비스를 삭제합니다.

전체 프로젝트를 삭제하려면 https://console.cloud.google.com/cloud-resource-manager로 이동하여 2단계에서 만든 프로젝트를 선택하고 삭제를 선택합니다. 프로젝트를 삭제하면 Cloud SDK에서 프로젝트를 변경해야 합니다. gcloud projects list를 실행하여 사용 가능한 모든 프로젝트의 목록을 볼 수 있습니다.