Udostępnij za pośrednictwem


Omówienie języka JavaScript w usłudze Azure Container Apps

Usługa Azure Container Apps może uruchamiać dowolną konteneryzowaną aplikację JavaScript w chmurze, zapewniając elastyczne opcje wdrażania aplikacji.

Konfiguracja

Usługa Azure Container Apps umożliwia usprawnianie wdrażania aplikacji JavaScript za pomocą efektywnej konteneryzacji, w tym konfigurowania zmiennych środowiskowych, projektowania wydajnych plików Dockerfile i organizowania procesu kompilacji aplikacji.

Zmienne środowiskowe

Zmienne środowiskowe mają kluczowe znaczenie dla konfigurowania aplikacji. .env Użyj pliku do zarządzania tymi zmiennymi lokalnie i upewnij się, że są one bezpiecznie zarządzane w środowisku produkcyjnym za pomocą usługi, takiej jak Azure Key Vault.

W poniższym przykładzie pokazano, jak utworzyć zmienne dla aplikacji.

# .env
NODE_ENV=production
PORT=3000
AZURE_COSMOS_DB_ENDPOINT=https://<YOUR_COSMOSDB_RESOURCE_NAME>.documents.azure.com:443/

Containers

Dobrze skonfigurowany plik Dockerfile jest niezbędny do konteneryzowania aplikacji:

  • Użyj podstawowego pliku Dockerfile: jeśli wiele projektów współużytkuje wspólną konfigurację, możesz utworzyć podstawowy plik Dockerfile zawierający te typowe kroki. Plik Dockerfile każdego projektu może następnie rozpocząć się od FROM tego obrazu podstawowego i dodać konfiguracje specyficzne dla projektu.

  • Parametryzacja argumentów kompilacji: możesz użyć argumentów kompilacji (ARG) w pliku Dockerfile, aby zwiększyć elastyczność. W ten sposób można przekazać różne wartości dla tych argumentów podczas kompilowania dla środowiska deweloperskiego, testowego lub produkcyjnego.

  • Zoptymalizowany obraz podstawowy Node.js: upewnij się, że używasz odpowiedniego obrazu podstawowegoNode.js. Rozważ użycie mniejszych, zoptymalizowanych obrazów, takich jak warianty Alpine, aby zmniejszyć obciążenie.

  • Minimalne pliki — kopiuj tylko podstawowe elementy: skoncentruj się na kopiowaniu tylko niezbędnych plików do kontenera. Utwórz plik .dockerignore, aby upewnić się, że pliki programistyczne nie są kopiowane do plików takich jak .env i node_modules. Ten plik ułatwia przyspieszenie kompilacji w przypadkach, w których deweloperzy skopiowali niepotrzebne pliki.

  • Oddzielaj kompilacje i środowisko uruchomieniowe przy użyciu kompilacji wieloetapowych: użyj kompilacji wieloetapowych, aby utworzyć pochyły obraz końcowy, oddzielając środowisko kompilacji od środowiska uruchomieniowego.

  • Artefakty wstępnego kompilowania i tworzenia pakietów: wstępne kompilowanie artefaktów aplikacji (takich jak kompilowanie języka TypeScript lub tworzenie pakietów języka JavaScript) przed skopiowaniem ich do etapu środowiska uruchomieniowego może zminimalizować rozmiar obrazu, przyspieszyć wdrażanie kontenerów i zwiększyć wydajność zimnego startu. Uważne porządkowanie instrukcji w Twoim pliku Dockerfile również optymalizuje buforowanie i czasy przebudowy.

  • Docker Compose dla środowisk deweloperskich: platforma Docker Compose umożliwia definiowanie i uruchamianie aplikacji platformy Docker z wieloma kontenerami. To wielokontenerowe podejście jest przydatne do konfigurowania środowisk deweloperskich. Kontekst kompilacji i plik Dockerfile można uwzględnić w pliku compose. Ten poziom hermetyzacji umożliwia używanie różnych plików Dockerfile dla różnych usług w razie potrzeby.

Podstawowy plik Dockerfile

Ten plik służy jako wspólny punkt wyjścia dla obrazów Node.js. Można go używać z dyrektywą FROM w plikach Dockerfile odwołujących się do tego obrazu podstawowego. Użyj numeru wersji lub identyfikatora zatwierdzenia (commit), aby wspierać najnowszą i bezpieczną wersję obrazu.

# Dockerfile.base

FROM node:22-alpine

# Set the working directory
WORKDIR /usr/src/app

# Define build arguments with default values
ARG PORT_DEFAULT=3000
ARG ENABLE_DEBUG_DEFAULT=false

# Set environment variables using the build arguments
ENV PORT=${PORT_DEFAULT}
ENV ENABLE_DEBUG=${ENABLE_DEBUG_DEFAULT}

# Copy package manifests and install dependencies
COPY package*.json ./
RUN npm install

# Expose the application and debugging ports
EXPOSE $PORT
EXPOSE 9229

# This image focuses on common steps; project-specific Dockerfiles can extend this.

Podczas przekazywania wartości przy użyciu flagi --build-arg w procesie budowania, zastępują one wartości domyślne zakodowane w twoim pliku Dockerfile.

Na przykład:

docker build \
  --build-arg PORT_DEFAULT=4000 \
  --build-arg ENABLE_DEBUG_DEFAULT=true \
  --tag <IMAGE>:<TAG> \
  --file Dockerfile.base .

W tym przykładzie zmienne PORT środowiskowe i ENABLE_DEBUG są ustawione na jawne wartości zamiast ich wartości domyślnych.

Konwencje tagowania obrazów kontenera, takie jak użycie latest, same w sobie są konwencją. Dowiedz się więcej na temat zaleceń dotyczących tagowania i przechowywania wersji obrazów kontenerów.

Konfigurowanie środowiska deweloperskiego przy użyciu narzędzia Docker Compose

W poniższej przykładowej konfiguracji użyto dedykowanego pliku Dockerfile (Dockerfile.dev) wraz z instalacjami woluminów na potrzeby ponownego ładowania na żywo i lokalnej synchronizacji źródła.

version: "3.8"
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.base
      args:
        PORT_DEFAULT: ${PORT:-3000}
        ENABLE_DEBUG_DEFAULT: ${ENABLE_DEBUG:-false}
    ports:
      - "${PORT:-3000}:3000"
      - "9229:9229"  # Expose debug port if needed
    volumes:
      - .:/usr/src/app
      - /usr/src/app/node_modules
    environment:
      - NODE_ENV=development
      - PORT=${PORT:-3000}
      - ENABLE_DEBUG=${ENABLE_DEBUG:-false}

Aby uruchomić aplikację Docker Compose z wartościami niestandardowymi, możesz wyeksportować zmienne środowiskowe w wierszu polecenia. Na przykład:

PORT=4000 ENABLE_DEBUG=true docker compose up

Produkcyjny plik Dockerfile

Ten wieloetapowy plik Dockerfile kompiluje aplikację i tworzy obraz środowiska uruchomieniowego lean. Upewnij się, że plik .dockerignore znajduje się już w kodzie źródłowym, aby polecenie COPY . . nie kopiowało żadnych plików specyficznych dla środowiska programistycznego, których nie potrzebujesz w środowisku produkcyjnym.

# Stage 1: Builder
FROM node:22 AS build

WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .

# Build your project (e.g., compile TypeScript or bundle JavaScript)
RUN npm run build

# Stage 2: Runtime
FROM my-base-image:latest AS runtime

WORKDIR /usr/src/app

# Copy only the compiled output and essential files from the build stage
COPY --from=build /usr/src/app/dist ./dist
COPY --from=build /usr/src/app/package*.json ./

# Install only production dependencies
RUN npm ci --omit=dev

# Copy the entrypoint script for remote debugging
COPY entrypoint.sh /usr/src/app/entrypoint.sh
RUN chmod +x /usr/src/app/entrypoint.sh

# Expose the application port (using the PORT environment variable) and the debug port (9229)
EXPOSE $PORT
EXPOSE 9229

# Use the entrypoint script to conditionally enable debugging
ENTRYPOINT ["sh", "/usr/src/app/entrypoint.sh"]

Skrypt punktu wejścia umożliwia nawiązanie połączenia z aplikacją kontenera na potrzeby zdalnego debugowania.

Aby uruchomić kontener z utworzonego obrazu produkcyjnego z niestandardowymi zmiennymi środowiskowymi, uruchom polecenie:

docker run \
  --env PORT=4000 \
  --env ENABLE_DEBUG=true \
  --publish 4000:4000 \
  --publish 9229:9229 \
  <IMAGE>:<TAG>

W przypadku kompilacji produkcyjnych upewnij się, że używasz poprawnego tagu wersji, który może nie być latest. Konwencje tagowania obrazów kontenerów, takie jak użycie latest, są standardem. Dowiedz się więcej na temat zaleceń dotyczących tagowania i przechowywania wersji obrazów kontenerów.

Wdrożenie

Aby obsługiwać ciągłą integrację/ciągłe wdrażanie (CI/CD), skonfiguruj potok ciągłej integracji/ciągłego wdrażania przy użyciu funkcji GitHub Actions, usługi Azure DevOps lub innego narzędzia ciągłej integracji/ciągłego wdrażania w celu zautomatyzowania procesu wdrażania.

# .github/workflows/deploy.yml
name: Deploy to Azure

on:
push:
    branches:
    - main

jobs:
build-and-deploy:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4

    - name: Set up Node.js
      uses: actions/setup-node@v4
      with:
          node-version: '22'

    - name: Install dependencies
      run: npm ci

    - name: Build the app
      run: npm run build

    - name: Log in to Azure
      uses: azure/login@v2
      with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}

    - name: Deploy to Azure Container Apps
      run: |
          az containerapp up \
          --name my-container-app \
          --resource-group my-resource-group \
          --image my-image:my_tag \
          --environment my-environment \
          --cpu 1 --memory 2Gi \
          --env-vars NODE_ENV=production PORT=3000

Gdy używasz rejestru platformy Docker, zaloguj się do rejestru, a następnie wypchnij obrazy platformy Docker do rejestru kontenerów, takiego jak Azure Container Registry (ACR) lub Docker Hub.

# Tag the image
docker tag \
  <IMAGE>:<TAG> \
  <AZURE_REGISTRY>.azurecr.io/<IMAGE>:<TAG>

# Push the image
docker push <AZURE_REGISTRY>.azurecr.io/<IMAGE>:<TAG>

Zimne starty

Zoptymalizuj kompilację produkcyjną, uwzględniając tylko podstawowy kod i zależności. Aby zapewnić, że ładunek jest tak zoptymalizowany, jak to możliwe, użyj jednego z następujących podejść:

  • Wieloetapowe kompilacje lub pakiety platformy Docker: użyj narzędzi kompilacji i tworzenia pakietów, takich jak pakiet webpack lub pakiet zbiorczy, aby ułatwić tworzenie najmniejszego ładunku możliwego dla kontenera. Kompilując i pakując tylko to, co jest potrzebne w środowisku produkcyjnym, pomagasz zminimalizować rozmiar kontenera i skracać czasy zimnego startu.

  • Uważnie zarządzaj zależnościami: Utrzymuj folder w prostocie, dołączając tylko pakiety wymagane do uruchamiania kodu produkcyjnego. Nie wymieniaj zależności programistycznych ani testowych w sekcji dependenciespackage.json. Usuń wszelkie nieużywane zależności i upewnij się, że package.json oraz plik blokady pozostają spójne.

Zabezpieczenia

Zagadnienia dotyczące zabezpieczeń dla deweloperów języka JavaScript korzystających z usługi Azure Container Apps obejmują zabezpieczanie zmiennych środowiskowych (na przykład poprzez użycie usługi Azure Key Vault), zapewnienie protokołu HTTPS z odpowiednim zarządzaniem certyfikatami, utrzymywanie aktualnych zależności poprzez regularne audyty oraz implementowanie niezawodnego rejestrowania i monitorowania w celu szybkiego wykrywania zagrożeń i reagowania na nie.

Zabezpieczanie zmiennych środowiskowych

Upewnij się, że poufne informacje, takie jak parametry połączenia bazy danych i klucze interfejsu API, są bezpiecznie przechowywane. Zarządzaj tajnymi danymi i zmiennymi środowiskowymi w sposób bezpieczny przy użyciu Azure Key Vault.

Przed uruchomieniem tego polecenia pamiętaj o zastąpieniu symboli zastępczych otoczonych <> wartościami.

az keyvault secret set \
  --vault-name <KEY_VAULT_APP> \
  --name "<SECRET_NAME>" \
  --value "<CONNECTION_STRING>"

Protokół HTTPS i certyfikaty

Upewnij się, że aplikacja jest obsługiwana za pośrednictwem protokołu HTTPS. Usługa Azure Container Apps może zarządzać certyfikatami . Skonfiguruj domenę niestandardową i certyfikat w witrynie Azure Portal.

Zarządzanie zależnościami

Regularnie aktualizuj zależności, aby uniknąć luk w zabezpieczeniach. Użyj narzędzi, takich jak npm audit, do sprawdzania pod kątem luk w zabezpieczeniach.

npm audit

Obsługa błędów

Implementowanie niezawodnej obsługi błędów w aplikacji Node.js. Używaj middleware w aplikacjach Express lub Fastify, aby elegancko obsłużyć błędy.

// src/middleware/errorHandler.ts
import { Request, Response, NextFunction } from 'express';

export function errorHandler(err: any, req: Request, res: Response, next: NextFunction) {
  console.error(err.stack);
  res.status(500).send('Something broke!');
}

Łaskawe wyłączania

Prawidłowe zamykanie aplikacji ma kluczowe znaczenie dla upewnienia się, że żądania w locie zostały ukończone, a zasoby są prawidłowo zwalniane. Pomaga to zapobiegać utracie danych i utrzymywać bezproblemowe środowisko użytkownika podczas wdrożeń lub zdarzeń skalowanych w poziomie. W poniższym przykładzie pokazano jedno podejście korzystające z Node.js i Express w celu bezproblemowej obsługi sygnałów zamknięcia.

import express from 'express';
import healthRouter from './health.js';

const app = express();

app.use(healthRouter);

const server = app.listen(process.env.PORT || 3000);

// Graceful shutdown
process.on('SIGTERM', () => {
  console.log('SIGTERM received, shutting down...');
  server.close(() => {
    console.log('Server closed');
    process.exit(0);
  });

  // Force close after 30s
  setTimeout(() => {
    console.error('Could not close connections in time, forcing shutdown');
    process.exit(1);
  }, 30000);
});

Przemysł drzewny

W usłudze Azure Container Apps zarówno wywołania console.log, jak i console.error są automatycznie przechwytywane i rejestrowane. Usługa Azure Container Apps przechwytuje standardowe strumienie danych wyjściowych (stdout) i błędów standardowych (stderr) z aplikacji i udostępnia je w usługach Azure Monitor i Log Analytics.

Konfigurowanie rejestrowania w usłudze Azure Container Apps

Aby upewnić się, że dzienniki są prawidłowo przechwytywane i dostępne, należy skonfigurować ustawienia diagnostyczne dla aplikacji kontenera platformy Azure. Konfiguracja jest procesem dwuetapowym.

  1. Włącz ustawienia diagnostyczne: użyj interfejsu wiersza polecenia platformy Azure, aby włączyć ustawienia diagnostyczne dla aplikacji kontenera platformy Azure.

    Przed uruchomieniem tego polecenia pamiętaj o zastąpieniu symboli zastępczych otoczonych <> wartościami.

    az monitor diagnostic-settings create \
    --resource /subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCE_GROUP>/providers/Microsoft.Web/containerApps/<CONTAINER_APP_NAME> \
    --name "containerapp-logs" \
    --workspace <LOG_ANALYTICS_WORKSPACE_ID> \
    --logs '[{"category": "ContainerAppConsoleLogs","enabled": true}]'
    
  2. Uzyskaj dostęp do dzienników w portalu, przechodząc do obszaru roboczego usługi Log Analytics i wykonując zapytanie dotyczące dzienników.

Korzystanie z bibliotek logowania

Chociaż console.log i console.error są automatycznie przechwytywane, korzystanie z biblioteki rejestrowania, takiej jak Winston, zapewnia większą elastyczność i kontrolę nad rejestrowaniem. Ta elastyczność umożliwia formatowanie dzienników, ustawianie poziomów dzienników oraz wyprowadzanie dzienników do wielu miejsc docelowych, takich jak pliki lub zewnętrzne usługi logowania.

W poniższym przykładzie pokazano, jak skonfigurować Winston do przechowywania logów o wysokiej dokładności.

// src/logger.ts
import { createLogger, transports, format } from 'winston';

const logger = createLogger({
  level: 'info',
  format: format.combine(
    format.timestamp(),
    format.json()
  ),
  transports: [
    new transports.Console(),
    new transports.File({ filename: 'app.log' })
  ]
});

export default logger;

Aby użyć rejestratora, użyj następującej składni w aplikacji:

import logger from './logger';

logger.info('This is an info message');
logger.error('This is an error message');

Zdalne debugowanie

Aby włączyć zdalne debugowanie, możesz użyć wbudowanego inspektora node. Zamiast ręcznie wpisywać ustawienia debugowania w pliku Dockerfile CMD, można dynamicznie włączyć debugowanie zdalne przy użyciu skryptu shellowego jako punktu wejścia kontenera.

Poniższy skrypt sprawdza zmienną środowiskową (na przykład ENABLE_DEBUG) po uruchomieniu kontenera. Jeśli zmienna jest ustawiona na true, skrypt uruchamia Node.js w trybie debugowania (przy użyciu --inspect lub --inspect-brk). W przeciwnym razie kontener uruchamia aplikację normalnie.

Debugowanie zdalne można zaimplementować, wykonując następujące czynności:

  1. Utwórz skrypt punktu wejścia w pliku o nazwie entrypoint.sh w katalogu głównym projektu z następującą zawartością:

    #!/bin/sh
    # If ENABLE_DEBUG is set to "true", start Node with debugging enabled
    if [ "$ENABLE_DEBUG" = "true" ]; then
      echo "Debug mode enabled: starting Node with inspector"
      exec node --inspect=0.0.0.0:9229 dist/index.js
    else
      echo "Starting Node without debug mode"
      exec node dist/index.js
    fi
    
  2. Zmodyfikuj plik Dockerfile, aby skopiować entrypoint.sh skrypt do kontenera i ustawić go jako punkt wejścia. Ponadto w razie potrzeby uwidocznij port debugowania:

    # Copy the entrypoint script to the container
    COPY entrypoint.sh /usr/src/app/entrypoint.sh
    
    # Ensure the script is executable
    RUN chmod +x /usr/src/app/entrypoint.sh
    
    # Expose the debugging port (if using debug mode)
    EXPOSE 9229
    
    # Set the shell script as the container’s entrypoint
    ENTRYPOINT ["sh", "/usr/src/app/entrypoint.sh"]
    
  3. Wyzwalanie trybu debugowania przez ustawienie zmiennej środowiskowej ENABLE_DEBUG na true. Na przykład przy użyciu interfejsu wiersza polecenia platformy Azure:

    az containerapp update \
      --name <CONTAINER_APP> \
      --env-vars ENABLE_DEBUG=true
    

Przed uruchomieniem tego polecenia pamiętaj o zastąpieniu symboli zastępczych otoczonych <> wartościami.

Takie podejście oferuje elastyczne rozwiązanie, które umożliwia ponowne uruchomienie kontenera w trybie debugowania przez zaktualizowanie zmiennej środowiskowej podczas uruchamiania. Pozwala uniknąć konieczności tworzenia nowej poprawki z różnymi CMD ustawieniami za każdym razem, gdy trzeba debugować aplikację.

Zagadnienia dotyczące konserwacji i wydajności

Aby zachować i zoptymalizować wydajność aplikacji w czasie, upewnij się, że efektywnie zarządzasz zmianami zmiennych środowiskowych, monitorujesz swoje zasoby, utrzymuj zależności aktualne, skonfiguruj skalowanie prawidłowo i ustaw alerty monitorowania.

Zmiany zmiennych środowiskowych

Ponieważ każda zmiana zmiennych środowiskowych wymaga nowej wdrożonej poprawki, wprowadź wszystkie zmiany wpisów tajnych aplikacji jednocześnie. Po zakończeniu wprowadzania zmian połącz tajne dane ze zmiennymi środowiskowymi wersji. Takie podejście minimalizuje liczbę poprawek i pomaga zachować czystą historię wdrażania.

Alokacja zasobów

Monitorowanie i dostosowywanie alokacji procesora CPU i pamięci dla kontenerów na podstawie wzorców wydajności i użycia aplikacji. Nadmierne rezerwowanie zasobów może prowadzić do niepotrzebnych kosztów, podczas gdy niedostateczne rezerwowanie zasobów może powodować problemy z wydajnością.

Aktualizacje zależności

Regularnie aktualizuj zależności, aby korzystać z ulepszeń wydajności i poprawek zabezpieczeń. Użyj narzędzi, takich jak npm-check-updates, do automatyzowania tego procesu.

npm install -g npm-check-updates
ncu -u
npm install

Skalowanie

Skonfiguruj skalowanie automatyczne na podstawie obciążenia aplikacji. Usługa Azure Container Apps obsługuje skalowanie w poziomie, co automatycznie dostosowuje liczbę wystąpień kontenerów na podstawie użycia procesora CPU lub pamięci.

W poniższym przykładzie pokazano, jak ustawić regułę skalowania opartą na procesorze CPU. Przed uruchomieniem tego polecenia pamiętaj o zastąpieniu symboli zastępczych otoczonych <> wartościami.

az containerapp revision set-scale \
  --name <CONTAINER_APP> \
  --resource-group <RESOURCE_GROUP> \
  --min-replicas 1 \
  --max-replicas 10 \
  --cpu 80

Monitorowanie alertów

Skonfiguruj monitorowanie i alerty w celu śledzenia wydajności i kondycji aplikacji. Usługa Azure Monitor umożliwia tworzenie alertów dla określonych metryk, takich jak użycie procesora CPU, użycie pamięci i czasy odpowiedzi.

Przed uruchomieniem tego polecenia pamiętaj o zastąpieniu symboli zastępczych otoczonych <> wartościami.

az monitor metrics alert create \
  --name "HighCPUUsage" \
  --resource-group <RESOURCE_GROUP> \
  --scopes /subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCE_GROUP>/providers/Microsoft.ContainerInstance/containerGroups/<CONTAINER_GROUP> \
  --condition "avg Percentage CPU > 80" \
  --description "Alert when CPU usage is above 80%"

Zarządzanie zasobami

Użyj rozszerzenia Azure Container Apps dla programu Visual Studio Code, aby szybko tworzyć, edytować i wdrażać konteneryzowane aplikacje bezpośrednio z poziomu programu Visual Studio Code.

Rozwiązywanie problemów

Gdy aplikacja napotka problemy z czasem wykonywania w usłudze Azure Container Apps, możesz użyć rejestrowania, zdalnego debugowania i alertów sprawdzania kondycji, aby znaleźć i rozwiązać ten problem.

Przemysł drzewny

Włączanie i konfigurowanie rejestrowania w celu przechwytywania dzienników aplikacji. Używanie usług Azure Monitor i Log Analytics do zbierania i analizowania dzienników. Przed uruchomieniem tych poleceń pamiętaj, aby zastąpić znaczniki zastępcze zawierające <> swoimi wartościami.

  1. Utwórz nowy obszar roboczy.

    az monitor log-analytics workspace create \
        --resource-group <RESOURCE_GROUP> \
        --workspace-name <WORKSPACE_NAME>
    
  2. Następnie utwórz nowe ustawienie obszaru roboczego.

    az monitor diagnostic-settings create \
        --resource <CONTAINER_APP> \
        --workspace <WORKSPACE_NAME> \
        --logs '[{"category": "ContainerAppConsoleLogs","enabled": true}]'
    

Debugowanie

Aby nawiązać połączenie z uruchomionym kontenerem, użyj narzędzi do zdalnego debugowania . Upewnij się, że plik Dockerfile uwidacznia niezbędne porty do debugowania.

# Expose the debugging port
EXPOSE 9229

Kontrole kondycji

Konfigurowanie kontroli kondycji w celu monitorowania kondycji aplikacji. Ta funkcja zapewnia, że usługa Azure Container Apps może ponownie uruchomić kontener, jeśli nie odpowiada.

# Azure Container Apps YAML configuration
properties:
configuration:
    livenessProbe:
    httpGet:
        path: /health
        port: 3000
    initialDelaySeconds: 30
    periodSeconds: 10