1. Введение
Обзор
В этой лаборатории кода вы настроите Cloud Run для автоматического создания и развертывания новых версий вашего приложения каждый раз, когда вы отправляете изменения исходного кода в репозиторий GitHub.
Это демонстрационное приложение сохраняет пользовательские данные в Firestore, однако только часть данных сохраняется должным образом. Вы настроите непрерывное развертывание таким образом, чтобы при отправке исправления ошибки в репозиторий GitHub вы автоматически видели, что исправление становится доступным в новой версии.
Что вы узнаете
- Напишите веб-приложение Express с помощью редактора Cloud Shell.
- Подключите свою учетную запись GitHub к Google Cloud для непрерывного развертывания.
- Автоматически развертывайте свое приложение в Cloud Run
- Узнайте, как использовать HTMX и TailwindCSS.
2. Настройка и требования
Предварительные условия
- У вас есть учетная запись GitHub, и вы знакомы с созданием и отправкой кода в репозитории.
- Вы вошли в облачную консоль.
- Ранее вы развернули службу Cloud Run. Например, вы можете выполнить развертывание веб-службы из краткого руководства по исходному коду, чтобы начать работу.
Активировать Cloud Shell
- В Cloud Console нажмите «Активировать Cloud Shell». .
Если вы запускаете Cloud Shell впервые, вы увидите промежуточный экран с описанием того, что это такое. Если вам был представлен промежуточный экран, нажмите «Продолжить» .
Подготовка и подключение к Cloud Shell займет всего несколько минут.
Эта виртуальная машина загружена всеми необходимыми инструментами разработки. Он предлагает постоянный домашний каталог объемом 5 ГБ и работает в Google Cloud, что значительно повышает производительность сети и аутентификацию. Большую часть, если не всю, работу в этой лаборатории кода можно выполнить с помощью браузера.
После подключения к Cloud Shell вы увидите, что вы прошли аутентификацию и что для проекта установлен идентификатор вашего проекта.
- Выполните следующую команду в 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`
- Выполните следующую команду в Cloud Shell, чтобы убедиться, что команда gcloud знает о вашем проекте:
gcloud config list project
Вывод команды
[core] project = <PROJECT_ID>
Если это не так, вы можете установить это с помощью этой команды:
gcloud config set project <PROJECT_ID>
Вывод команды
Updated property [core/project].
3. Включите API и установите переменные среды.
Включить API
Для этой лаборатории кода требуется использование следующих API. Вы можете включить эти API, выполнив следующую команду:
gcloud services enable run.googleapis.com \ cloudbuild.googleapis.com \ firestore.googleapis.com \ iamcredentials.googleapis.com
Настройка переменных среды
Вы можете установить переменные среды, которые будут использоваться в этой лаборатории кода.
REGION=<YOUR-REGION> PROJECT_ID=<YOUR-PROJECT-ID> PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)') SERVICE_ACCOUNT="firestore-accessor" SERVICE_ACCOUNT_ADDRESS=$SERVICE_ACCOUNT@$PROJECT_ID.iam.gserviceaccount.com
4. Создайте учетную запись службы.
Эта учетная запись службы будет использоваться Cloud Run для вызова API Vertex AI Gemini. Эта учетная запись службы также будет иметь разрешения на чтение и запись в Firestore, а также на чтение секретов из Secret Manager.
Сначала создайте учетную запись службы, выполнив следующую команду:
gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name="Cloud Run access to Firestore"
Теперь предоставьте сервисной учетной записи доступ на чтение и запись в Firestore.
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role=roles/datastore.user
5. Создайте и настройте проект Firebase.
- В консоли Firebase нажмите «Добавить проект» .
- Введите <YOUR_PROJECT_ID>, чтобы добавить Firebase в один из существующих проектов Google Cloud.
- При появлении запроса прочтите и примите условия Firebase .
- Нажмите Продолжить .
- Нажмите «Подтвердить план» , чтобы подтвердить тарифный план Firebase.
- Включать Google Analytics для этой лаборатории кода необязательно.
- Нажмите «Добавить Firebase» .
- Когда проект будет создан, нажмите «Продолжить» .
- В меню «Создать» выберите «База данных Firestore» .
- Нажмите Создать базу данных .
- Выберите свой регион в раскрывающемся списке «Местоположение» , затем нажмите «Далее» .
- Используйте режим запуска по умолчанию в рабочем режиме , затем нажмите «Создать» .
6. Напишите заявку
Сначала создайте каталог для исходного кода и перейдите в этот каталог.
mkdir cloud-run-github-cd-demo && cd $_
Затем создайте файл package.json
со следующим содержимым:
{ "name": "cloud-run-github-cd-demo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "node app.js", "nodemon": "nodemon app.js", "tailwind-dev": "npx tailwindcss -i ./input.css -o ./public/output.css --watch", "tailwind": "npx tailwindcss -i ./input.css -o ./public/output.css", "dev": "npm run tailwind && npm run nodemon" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@google-cloud/firestore": "^7.3.1", "axios": "^1.6.7", "express": "^4.18.2", "htmx.org": "^1.9.10" }, "devDependencies": { "nodemon": "^3.1.0", "tailwindcss": "^3.4.1" } }
Сначала создайте исходный файл app.js
с содержимым, указанным ниже. Этот файл содержит точку входа для службы и основную логику приложения.
const express = require("express"); const app = express(); app.use(express.urlencoded({ extended: true })); app.use(express.json()); const path = require("path"); const { get } = require("axios"); const { Firestore } = require("@google-cloud/firestore"); const firestoreDb = new Firestore(); const fs = require("fs"); const util = require("util"); const { spinnerSvg } = require("./spinnerSvg.js"); const service = process.env.K_SERVICE; const revision = process.env.K_REVISION; app.use(express.static("public")); app.get("/edit", async (req, res) => { res.send(`<form hx-post="/update" hx-target="this" hx-swap="outerHTML"> <div> <p> <label>Name</label> <input class="border-2" type="text" name="name" value="Cloud"> </p><p> <label>Town</label> <input class="border-2" type="text" name="town" value="Nibelheim"> </p> </div> <div class="flex items-center mr-[10px] mt-[10px]"> <button class="btn bg-blue-500 text-white px-4 py-2 rounded-lg text-center text-sm font-medium mr-[10px]">Submit</button> <button class="btn bg-gray-200 text-gray-800 px-4 py-2 rounded-lg text-center text-sm font-medium mr-[10px]" hx-get="cancel">Cancel</button> ${spinnerSvg} </div> </form>`); }); app.post("/update", async function (req, res) { let name = req.body.name; let town = req.body.town; const doc = firestoreDb.doc(`demo/${name}`); //TODO: fix this bug await doc.set({ name: name /* town: town */ }); res.send(`<div hx-target="this" hx-swap="outerHTML" hx-indicator="spinner"> <p> <div><label>Name</label>: ${name}</div> </p><p> <div><label>Town</label>: ${town}</div> </p> <button hx-get="/edit" class="bg-blue-500 text-white px-4 py-2 rounded-lg text-sm font-medium mt-[10px]" > Click to update </button> </div>`); }); app.get("/cancel", (req, res) => { res.send(`<div hx-target="this" hx-swap="outerHTML"> <p> <div><label>Name</label>: Cloud</div> </p><p> <div><label>Town</label>: Nibelheim</div> </p> <div> <button hx-get="/edit" class="bg-blue-500 text-white px-4 py-2 rounded-lg text-sm font-medium mt-[10px]" > Click to update </button> </div> </div>`); }); const port = parseInt(process.env.PORT) || 8080; app.listen(port, async () => { console.log(`booth demo: listening on port ${port}`); //serviceMetadata = helper(); }); app.get("/helper", async (req, res) => { let region = ""; let projectId = ""; let div = ""; try { // Fetch the token to make a GCF to GCF call const response1 = await get( "http://metadata.google.internal/computeMetadata/v1/project/project-id", { headers: { "Metadata-Flavor": "Google" } } ); // Fetch the token to make a GCF to GCF call const response2 = await get( "http://metadata.google.internal/computeMetadata/v1/instance/region", { headers: { "Metadata-Flavor": "Google" } } ); projectId = response1.data; let regionFull = response2.data; const index = regionFull.lastIndexOf("/"); region = regionFull.substring(index + 1); div = ` <div> This created the revision <code>${revision}</code> of the Cloud Run service <code>${service}</code> in <code>${region}</code> for project <code>${projectId}</code>. </div>`; } catch (ex) { // running locally div = `<div> This is running locally.</div>`; } res.send(div); });
Создайте файл с именем spinnerSvg.js
module.exports.spinnerSvg = `<svg id="spinner" alt="Loading..." class="htmx-indicator animate-spin -ml-1 mr-3 h-5 w-5 text-blue-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" > <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" ></circle> <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" ></path> </svg>`;
Создайте файл input.css
для TailwindCSS.
@tailwind base; @tailwind components; @tailwind utilities;
И создайте файл tailwind.config.js
для TailwindCSS.
/** @type {import('tailwindcss').Config} */ module.exports = { content: ["./**/*.{html,js}"], theme: { extend: {} }, plugins: [] };
И создайте файл .gitignore
.
node_modules/ npm-debug.log coverage/ package-lock.json .DS_Store
Теперь создайте новый public
каталог.
mkdir public cd public
И в этом общедоступном каталоге создайте файл index.html
для внешнего интерфейса, который будет использовать htmx.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <script src="https://unpkg.com/htmx.org@1.9.10" integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC" crossorigin="anonymous" ></script> <link href="./output.css" rel="stylesheet" /> <title>Demo 1</title> </head> <body class="font-sans bg-body-image bg-cover bg-center leading-relaxed" > <div class="container max-w-[700px] mt-[50px] ml-auto mr-auto"> <div class="hero flex items-center"> <div class="message text-base text-center mb-[24px]"> <h1 class="text-2xl font-bold mb-[10px]"> It's running! </h1> <div class="congrats text-base font-normal"> Congratulations, you successfully deployed your service to Cloud Run. </div> </div> </div> <div class="details mb-[20px]"> <p> <div hx-trigger="load" hx-get="/helper" hx-swap="innerHTML" hx-target="this">Hello</div> </p> </div> <p class="callout text-sm text-blue-700 font-bold pt-4 pr-6 pb-4 pl-10 leading-tight" > You can deploy any container to Cloud Run that listens for HTTP requests on the port defined by the <code>PORT</code> environment variable. Cloud Run will scale automatically based on requests and you never have to worry about infrastructure. </p> <h1 class="text-2xl font-bold mt-[40px] mb-[20px]"> Persistent Storage Example using Firestore </h1> <div hx-target="this" hx-swap="outerHTML"> <p> <div><label>Name</label>: Cloud</div> </p><p> <div><label>Town</label>: Nibelheim</div> </p> <div> <button hx-get="/edit" class="bg-blue-500 text-white px-4 py-2 rounded-lg text-sm font-medium mt-[10px]" > Click to update </button> </div> </div> <h1 class="text-2xl font-bold mt-[40px] mb-[20px]"> What's next </h1> <p class="next text-base mt-4 mb-[20px]"> You can build this demo yourself! </p> <p class="cta"> <button class="bg-blue-500 text-white px-4 py-2 rounded-lg text-center text-sm font-medium" > VIEW CODELAB </button> </p> </div> </body> </html>
7. Запустите приложение локально
В этом разделе вы запустите приложение локально, чтобы убедиться в наличии ошибки в приложении, когда пользователь пытается сохранить данные.
Во-первых, вам либо потребуется роль пользователя хранилища данных для доступа к Firestore (если вы используете свою личность для аутентификации, например, вы работаете в Cloud Shell), либо вы можете выдать себя за ранее созданную учетную запись пользователя.
Использование ADC при локальном запуске
Если вы работаете в Cloud Shell, вы уже работаете на виртуальной машине Google Compute Engine. Ваши учетные данные, связанные с этой виртуальной машиной (как показано при запуске gcloud auth list
), будут автоматически использоваться учетными данными приложения по умолчанию (ADC), поэтому нет необходимости использовать команду gcloud auth application-default login
. Однако для вашей личности по-прежнему потребуется роль пользователя хранилища данных. Вы можете перейти к разделу «Запустить приложение локально» .
Однако если вы работаете на локальном терминале (т. е. не в Cloud Shell), вам потребуется использовать учетные данные приложения по умолчанию для аутентификации в API Google. Вы можете 1) войти в систему, используя свои учетные данные (при условии, что у вас есть роль пользователя хранилища данных) или 2) вы можете войти в систему, олицетворяя учетную запись службы, используемую в этой лаборатории кода.
Вариант 1) Использование ваших учетных данных для ADC
Если вы хотите использовать свои учетные данные, вы можете сначала запустить gcloud auth list
чтобы проверить, как вы прошли аутентификацию в gcloud. Далее вам может потребоваться предоставить себе роль пользователя Vertex AI. Если у вас есть роль владельца, у вас уже есть роль пользователя хранилища данных. Если нет, вы можете запустить эту команду, чтобы предоставить вам роль пользователя Vertex AI и роль пользователя хранилища данных.
USER=<YOUR_PRINCIPAL_EMAIL> gcloud projects add-iam-policy-binding $PROJECT_ID \ --member user:$USER \ --role=roles/datastore.user
Затем выполните следующую команду
gcloud auth application-default login
Вариант 2) Олицетворение учетной записи службы для ADC
Если вы хотите использовать учетную запись службы, созданную в этой лаборатории кода, ваша учетная запись пользователя должна иметь роль создателя токена учетной записи службы . Вы можете получить эту роль, выполнив следующую команду:
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member user:$USER \ --role=roles/iam.serviceAccountTokenCreator
Далее вы запустите следующую команду, чтобы использовать ADC с учетной записью службы.
gcloud auth application-default login --impersonate-service-account=$SERVICE_ACCOUNT_ADDRESS
Запустите приложение локально
Затем убедитесь, что вы находитесь в корневом каталоге cloud-run-github-cd-demo
для вашей лаборатории кода.
cd .. && pwd
Теперь вы установите зависимости.
npm install
Наконец, вы можете запустить приложение, запустив следующий скрипт. Этот скрипт также сгенерирует файл output.css из TailwindCSS.
npm run dev
Теперь откройте веб-браузер по адресу http://localhost:8080. Если вы находитесь в Cloud Shell, вы можете открыть веб-сайт, нажав кнопку «Просмотр в Интернете» и выбрав «Просмотр порта 8080».
Введите текст в поля ввода имени и города и нажмите «Сохранить». Затем обновите страницу. Вы заметите, что поле города не сохранилось. Вы исправите эту ошибку в следующем разделе.
Остановите локальный запуск экспресс-приложения (например, Ctrl^c в MacOS).
8. Создайте репозиторий GitHub.
В вашем локальном каталоге создайте новый репозиторий с именем main в качестве имени ветки по умолчанию.
git init git branch -M main
Зафиксируйте текущую кодовую базу, содержащую ошибку. Вы исправите ошибку после настройки непрерывного развертывания.
git add . git commit -m "first commit for express application"
Перейдите на GitHub и создайте пустой репозиторий, который будет либо частным, либо общедоступным. В этой лаборатории кода рекомендуется назвать ваш репозиторий cloud-run-auto-deploy-codelab
Чтобы создать пустой репозиторий, вы оставите все настройки по умолчанию неотмеченными или не установите их в значение «Нет», чтобы при создании в репозитории по умолчанию не было контента, например
Если вы выполнили этот шаг правильно, на пустой странице репозитория вы увидите следующие инструкции:
Вы будете следить за отправкой существующего репозитория из инструкций командной строки, выполнив следующие команды:
Сначала добавьте удаленный репозиторий, запустив
git remote add origin <YOUR-REPO-URL-PER-GITHUB-INSTRUCTIONS>
затем переместите основную ветку в восходящий репозиторий.
git push -u origin main
9. Настройка непрерывного развертывания
Теперь, когда у вас есть код на GitHub, вы можете настроить непрерывное развертывание. Перейдите в облачную консоль для Cloud Run .
- Нажмите Создать услугу.
- Нажмите «Непрерывно развертывать из репозитория».
- Нажмите НАСТРОЙКА CLOUD BUILD .
- В репозитории исходного кода
- Выберите GitHub в качестве поставщика репозитория.
- Нажмите «Управление подключенными репозиториями» , чтобы настроить доступ Cloud Build к репозиторию.
- Выберите свой репозиторий и нажмите «Далее».
- В конфигурации сборки
- Оставьте ветку как ^main$
- В качестве типа сборки выберите Go, Node.js, Python, Java, .NET Core, Ruby или PHP через пакеты сборки Google Cloud.
- Оставьте каталог контекста сборки как
/
- Нажмите « Сохранить».
- Под аутентификацией
- Нажмите «Разрешить неаутентифицированные вызовы».
- В разделе «Контейнеры», «Тома», «Сеть», «Безопасность».
- На вкладке «Безопасность» выберите учетную запись службы, которую вы создали на предыдущем этапе, например,
Cloud Run access to Firestore
- На вкладке «Безопасность» выберите учетную запись службы, которую вы создали на предыдущем этапе, например,
- Нажмите СОЗДАТЬ
При этом будет развернута служба Cloud Run, содержащая ошибку, которую вы исправите в следующем разделе.
10. Исправьте ошибку
Исправьте ошибку в коде
В редакторе Cloud Shell откройте файл app.js
и перейдите к комментарию //TODO: fix this bug
измените следующую строку с
//TODO: fix this bug await doc.set({ name: name });
к
//fixed town bug await doc.set({ name: name, town: town });
Проверьте исправление, запустив
npm run start
и откройте веб-браузер. Снова сохраните данные для города и обновите. Вы увидите, что вновь введенные данные о городе правильно сохранились при обновлении.
Теперь, когда вы проверили свое исправление, вы готовы его развернуть. Сначала зафиксируйте исправление.
git add . git commit -m "fixed town bug"
а затем отправить его в вышестоящий репозиторий на GitHub.
git push origin main
Cloud Build автоматически развернет ваши изменения. Вы можете перейти в облачную консоль своей службы Cloud Run, чтобы отслеживать изменения в развертывании.
Проверка исправления в рабочей среде
Как только облачная консоль для вашей службы Cloud Run покажет, что вторая редакция теперь обслуживает 100 % трафика, например https://console.cloud.google.com/run/detail/<YOUR_REGION>/<YOUR_SERVICE_NAME>/revisions, вы можете открыть URL-адрес службы Cloud Run в вашем браузере и убедитесь, что вновь введенные данные о городе сохраняются после обновления страницы.
11. Поздравляем!
Поздравляем с завершением работы над кодом!
Рекомендуем просмотреть документацию по Cloud Run и непрерывному развертыванию из git .
Что мы рассмотрели
- Напишите веб-приложение Express с помощью редактора Cloud Shell.
- Подключите свою учетную запись GitHub к Google Cloud для непрерывного развертывания.
- Автоматически развертывайте свое приложение в Cloud Run
- Узнайте, как использовать HTMX и TailwindCSS.
12. Очистка
Чтобы избежать непреднамеренных расходов (например, если службы Cloud Run по неосторожности вызываются больше раз, чем вам ежемесячно выделено количество вызовов Cloud Run на уровне бесплатного пользования ), вы можете либо удалить Cloud Run, либо удалить проект, созданный на шаге 2.
Чтобы удалить службу Cloud Run, перейдите в облачную консоль Cloud Run по адресу https://console.cloud.google.com/run и удалите службу Cloud Run, созданную в этой лаборатории кода, например удалите cloud-run-auto-deploy-codelab
-сервис cloud-run-auto-deploy-codelab
.
Если вы решите удалить весь проект, вы можете перейти на https://console.cloud.google.com/cloud-resource-manager , выбрать проект, созданный на шаге 2, и нажать «Удалить». Если вы удалите проект, вам придется изменить проекты в Cloud SDK. Вы можете просмотреть список всех доступных проектов, запустив gcloud projects list
.