Sdílet prostřednictvím


Spusťte samohostovaného agenta v Dockeru

Azure DevOps Services | Azure DevOps Server | Azure DevOps Server 2022 | Azure DevOps Server 2020

Tento článek obsahuje pokyny ke spuštění agenta Azure Pipelines v Dockeru. V Azure Pipelines můžete nastavit agenta v místním prostředí tak, aby běžel uvnitř jádra Windows Serveru (pro hostitele s Windows) nebo kontejneru Ubuntu (pro hostitele s Linuxem) pomocí Dockeru. To je užitečné, když chcete spouštět agenty s vnější orchestrací, jako Azure Container Instances. V tomto článku si projdete kompletní příklad kontejneru, včetně automatické aktualizace agenta.

Windows i Linux se podporují jako hostitelé kontejnerů. Kontejnery Windows by měly běžet ve Windows vmImage. Pokud chcete spustit agenta v Dockeru, předáte do něho několik docker run, které nakonfiguruje agenta pro připojení ke službě Azure Pipelines nebo Azure DevOps Serveru. Nakonec kontejner přizpůsobíte tak, aby vyhovoval vašim potřebám. Úlohy a skripty můžou záviset na dostupnosti konkrétních nástrojů v kontejneru PATHa je vaší zodpovědností zajistit, aby byly tyto nástroje k dispozici.

Windows

Povolení Hyper-V

Ve Windows není ve výchozím nastavení povolená technologie Hyper-V. Pokud chcete zajistit izolaci mezi kontejnery, musíte povolit Technologii Hyper-V. Jinak se Docker pro Windows nespustí.

Poznámka:

Na počítači musíte povolit virtualizaci. Obvykle je povolená ve výchozím nastavení. Pokud se ale instalace Hyper-V nezdaří, projděte si dokumentaci k systému a zjistěte, jak povolit virtualizaci.

Instalace Dockeru pro Windows

Pokud používáte Windows 10, můžete nainstalovat Docker Community Edition. Pro Windows Server 2016 nainstalujte Docker Enterprise Edition.

Přepnutí Dockeru na používání kontejnerů Windows

Ve výchozím nastavení je Docker pro Windows nakonfigurovaný tak, aby používal kontejnery Linuxu. Chcete-li povolit spuštění kontejneru Windows, ověřte, že Docker pro Windows spouští démon Windows.

Vytvoření a sestavení souboru Dockerfile

Dále vytvořte soubor Dockerfile.

  1. Otevřete příkazový řádek.

  2. Vytvořte nový adresář:

    mkdir "C:\azp-agent-in-docker\"
    
  3. Přejděte do tohoto nového adresáře:

    cd "C:\azp-agent-in-docker\"
    
  4. Uložte následující obsah do souboru s názvem C:\azp-agent-in-docker\azp-agent-windows.dockerfile:

    FROM mcr.microsoft.com/windows/servercore:ltsc2022
    
    WORKDIR /azp/
    
    COPY ./start.ps1 ./
    
    CMD powershell .\start.ps1
    
  5. Uložte následující obsah do C:\azp-agent-in-docker\start.ps1:

    function Print-Header ($header) {
      Write-Host "`n${header}`n" -ForegroundColor Cyan
    }
    
    if (-not (Test-Path Env:AZP_URL)) {
      Write-Error "error: missing AZP_URL environment variable"
      exit 1
    }
    
    if (-not (Test-Path Env:AZP_TOKEN_FILE)) {
      if (-not (Test-Path Env:AZP_TOKEN)) {
        Write-Error "error: missing AZP_TOKEN environment variable"
        exit 1
      }
    
      $Env:AZP_TOKEN_FILE = "\azp\.token"
      $Env:AZP_TOKEN | Out-File -FilePath $Env:AZP_TOKEN_FILE
    }
    
    Remove-Item Env:AZP_TOKEN
    
    if ((Test-Path Env:AZP_WORK) -and -not (Test-Path $Env:AZP_WORK)) {
      New-Item $Env:AZP_WORK -ItemType directory | Out-Null
    }
    
    New-Item "\azp\agent" -ItemType directory | Out-Null
    
    # Let the agent ignore the token env variables
    $Env:VSO_AGENT_IGNORE = "AZP_TOKEN,AZP_TOKEN_FILE"
    
    Set-Location agent
    
    Print-Header "1. Determining matching Azure Pipelines agent..."
    
    $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$(Get-Content ${Env:AZP_TOKEN_FILE})"))
    $package = Invoke-RestMethod -Headers @{Authorization=("Basic $base64AuthInfo")} "$(${Env:AZP_URL})/_apis/distributedtask/packages/agent?platform=win-x64&`$top=1"
    $packageUrl = $package[0].Value.downloadUrl
    
    Write-Host $packageUrl
    
    Print-Header "2. Downloading and installing Azure Pipelines agent..."
    
    $wc = New-Object System.Net.WebClient
    $wc.DownloadFile($packageUrl, "$(Get-Location)\agent.zip")
    
    Expand-Archive -Path "agent.zip" -DestinationPath "\azp\agent"
    
    try {
      Print-Header "3. Configuring Azure Pipelines agent..."
    
      .\config.cmd --unattended `
        --agent "$(if (Test-Path Env:AZP_AGENT_NAME) { ${Env:AZP_AGENT_NAME} } else { hostname })" `
        --url "$(${Env:AZP_URL})" `
        --auth PAT `
        --token "$(Get-Content ${Env:AZP_TOKEN_FILE})" `
        --pool "$(if (Test-Path Env:AZP_POOL) { ${Env:AZP_POOL} } else { 'Default' })" `
        --work "$(if (Test-Path Env:AZP_WORK) { ${Env:AZP_WORK} } else { '_work' })" `
        --replace
    
      Print-Header "4. Running Azure Pipelines agent..."
    
      .\run.cmd
    } finally {
      Print-Header "Cleanup. Removing Azure Pipelines agent..."
    
      .\config.cmd remove --unattended `
        --auth PAT `
        --token "$(Get-Content ${Env:AZP_TOKEN_FILE})"
    }
    
  6. V tomto adresáři spusťte následující příkaz:

    docker build --tag "azp-agent:windows" --file "./azp-agent-windows.dockerfile" .
    

    Konečný obrázek je označen azp-agent:windows.

Spustit obrázek

Teď, když jste vytvořili image, můžete spustit kontejner. Tím se nainstaluje nejnovější verze agenta, nakonfiguruje ji a spustí agenta. Cílí na zadaný fond agentů ( Default ve výchozím nastavení fond agentů) zadané instance Azure DevOps nebo Azure DevOps Serveru podle vašeho výběru:

docker run -e AZP_URL="<Azure DevOps instance>" -e AZP_TOKEN="<Personal Access Token>" -e AZP_POOL="<Agent Pool Name>" -e AZP_AGENT_NAME="Docker Agent - Windows" --name "azp-agent-windows" azp-agent:windows

Pokud narazíte na problémy se sítí, budete možná muset zadat --network parametr.

docker run --network "Default Switch" < . . . >

Pokud chcete mít možnost zastavit kontejner a odebrat agenta, budete možná muset zadat příznaky --interactive a --tty (nebo jednoduše -it), abyste to mohli udělat s Ctrl + C.

docker run --interactive --tty < . . . >

Pokud chcete nový kontejner agenta pro každou úlohu kanálu, předejte příkazu příznak .

docker run < . . . > --once

--once Pomocí příznaku můžete chtít použít systém orchestrace kontejnerů, jako je Kubernetes nebo Azure Container Instances, ke spuštění nové kopie kontejneru po dokončení úlohy.

Název agenta, fond agentů a pracovní adresář agenta můžete řídit pomocí volitelných proměnných prostředí.

Operační systém Linux

Instalace Dockeru

V závislosti na distribuci Linuxu můžete nainstalovat Docker Community Edition nebo Docker Enterprise Edition.

Vytvoření a sestavení souboru Dockerfile

Dále vytvořte soubor Dockerfile.

  1. Otevřete terminál.

  2. Vytvořte nový adresář (doporučeno):

    mkdir ~/azp-agent-in-docker/
    
  3. Přejděte do tohoto nového adresáře:

    cd ~/azp-agent-in-docker/
    
  4. Uložte následující obsah do ~/azp-agent-in-docker/azp-agent-linux.dockerfile:

    • Pro Alpine je pro tento problém použita technika popsaná v:

      FROM python:3-alpine
      ENV TARGETARCH="linux-musl-x64"
      
      # Another option:
      # FROM arm64v8/alpine
      # ENV TARGETARCH="linux-musl-arm64"
      
      RUN apk update && \
        apk upgrade && \
        apk add bash curl gcc git icu-libs jq musl-dev python3-dev libffi-dev openssl-dev cargo make
      
      # Install Azure CLI
      RUN pip install --upgrade pip
      RUN pip install azure-cli
      
      WORKDIR /azp/
      
      COPY ./start.sh ./
      RUN chmod +x ./start.sh
      
      RUN adduser -D agent
      RUN chown agent ./
      USER agent
      # Another option is to run the agent as root.
      # ENV AGENT_ALLOW_RUNASROOT="true"
      
      ENTRYPOINT [ "./start.sh" ]
      
    • Pro Ubuntu 24.04:

      FROM ubuntu:24.04
      ENV TARGETARCH="linux-x64"
      # Also can be "linux-arm", "linux-arm64".
      
      RUN apt update && \
        apt upgrade -y && \
        apt install -y curl git jq libicu74
      
      # Install Azure CLI
      RUN curl -sL https://aka.ms/InstallAzureCLIDeb | bash
      
      WORKDIR /azp/
      
      COPY ./start.sh ./
      RUN chmod +x ./start.sh
      
      # Create agent user and set up home directory
      RUN useradd -m -d /home/agent agent
      RUN chown -R agent:agent /azp /home/agent
      
      USER agent
      # Another option is to run the agent as root.
      # ENV AGENT_ALLOW_RUNASROOT="true"
      
      ENTRYPOINT [ "./start.sh" ]
      
    • Pro Ubuntu 22.04:

      FROM ubuntu:22.04
      ENV TARGETARCH="linux-x64"
      # Also can be "linux-arm", "linux-arm64".
      
      RUN apt update && \
        apt upgrade -y && \
        apt install -y curl git jq libicu70
      
      # Install Azure CLI
      RUN curl -sL https://aka.ms/InstallAzureCLIDeb | bash
      
      WORKDIR /azp/
      
      COPY ./start.sh ./
      RUN chmod +x ./start.sh
      
      # Create agent user and set up home directory
      RUN useradd -m -d /home/agent agent
      RUN chown -R agent:agent /azp /home/agent
      
      USER agent
      # Another option is to run the agent as root.
      # ENV AGENT_ALLOW_RUNASROOT="true"
      
      ENTRYPOINT [ "./start.sh" ]
      

    Odkomentujte řádek ENV AGENT_ALLOW_RUNASROOT="true" a odstraňte přidání uživatele agent před tento řádek, pokud chcete spustit agenta jako root.

    Poznámka:

    Úlohy můžou záviset na spustitelných souborech, u kterých se očekává, že je váš kontejner má poskytovat. Pokud chcete například ke zip příkazu přidat unzip a RUN apt install -y balíčky, musíte to udělat, abyste mohli spouštět ArchiveFiles a ExtractFiles úlohy. Vzhledem k tomu, že se jedná o image Ubuntu linuxu, kterou má agent používat, můžete image přizpůsobit podle potřeby. Například: Pokud potřebujete sestavit aplikace .NET, můžete postupovat podle dokumentu Instalace sady .NET SDK nebo modulu runtime .NET na Ubuntu a jeho přidání do image.

  5. Uložte následující obsah do ~/azp-agent-in-docker/start.sh, ujistěte se, že používáte konce řádků ve stylu Unix (LF):

    #!/bin/bash
    set -e
    
    if [ -z "${AZP_URL}" ]; then
      echo 1>&2 "error: missing AZP_URL environment variable"
      exit 1
    fi
    
    if [ -n "$AZP_CLIENTID" ]; then
      echo "Using service principal credentials to get token"
      az login --allow-no-subscriptions --service-principal --username "$AZP_CLIENTID" --password "$AZP_CLIENTSECRET" --tenant "$AZP_TENANTID"
      # adapted from https://learn.microsoft.com/en-us/azure/databricks/dev-tools/user-aad-token
      AZP_TOKEN=$(az account get-access-token --query accessToken --output tsv)
      echo "Token retrieved"
    fi
    
    if [ -z "${AZP_TOKEN_FILE}" ]; then
      if [ -z "${AZP_TOKEN}" ]; then
        echo 1>&2 "error: missing AZP_TOKEN environment variable"
        exit 1
      fi
    
      AZP_TOKEN_FILE="/azp/.token"
      echo -n "${AZP_TOKEN}" > "${AZP_TOKEN_FILE}"
    fi
    
    unset AZP_CLIENTSECRET
    unset AZP_TOKEN
    
    if [ -n "${AZP_WORK}" ]; then
      mkdir -p "${AZP_WORK}"
    fi
    
    cleanup() {
      trap "" EXIT
    
      if [ -e ./config.sh ]; then
        print_header "Cleanup. Removing Azure Pipelines agent..."
    
        # If the agent has some running jobs, the configuration removal process will fail.
        # So, give it some time to finish the job.
        while true; do
          ./config.sh remove --unattended --auth "PAT" --token $(cat "${AZP_TOKEN_FILE}") && break
    
          echo "Retrying in 30 seconds..."
          sleep 30
        done
      fi
    }
    
    print_header() {
      lightcyan="\033[1;36m"
      nocolor="\033[0m"
      echo -e "\n${lightcyan}$1${nocolor}\n"
    }
    
    # Let the agent ignore the token env variables
    export VSO_AGENT_IGNORE="AZP_TOKEN,AZP_TOKEN_FILE"
    
    print_header "1. Determining matching Azure Pipelines agent..."
    
    AZP_AGENT_PACKAGES=$(curl -LsS \
        -u user:$(cat "${AZP_TOKEN_FILE}") \
        -H "Accept:application/json" \
        "${AZP_URL}/_apis/distributedtask/packages/agent?platform=${TARGETARCH}&top=1")
    
    AZP_AGENT_PACKAGE_LATEST_URL=$(echo "${AZP_AGENT_PACKAGES}" | jq -r ".value[0].downloadUrl")
    
    if [ -z "${AZP_AGENT_PACKAGE_LATEST_URL}" -o "${AZP_AGENT_PACKAGE_LATEST_URL}" == "null" ]; then
      echo 1>&2 "error: could not determine a matching Azure Pipelines agent"
      echo 1>&2 "check that account "${AZP_URL}" is correct and the token is valid for that account"
      exit 1
    fi
    
    print_header "2. Downloading and extracting Azure Pipelines agent..."
    
    curl -LsS "${AZP_AGENT_PACKAGE_LATEST_URL}" | tar -xz & wait $!
    
    source ./env.sh
    
    trap "cleanup; exit 0" EXIT
    trap "cleanup; exit 130" INT
    trap "cleanup; exit 143" TERM
    
    print_header "3. Configuring Azure Pipelines agent..."
    
    # Despite it saying "PAT", it can be the token through the service principal
    ./config.sh --unattended \
      --agent "${AZP_AGENT_NAME:-$(hostname)}" \
      --url "${AZP_URL}" \
      --auth "PAT" \
      --token $(cat "${AZP_TOKEN_FILE}") \
      --pool "${AZP_POOL:-Default}" \
      --work "${AZP_WORK:-_work}" \
      --replace \
      --acceptTeeEula & wait $!
    
    print_header "4. Running Azure Pipelines agent..."
    
    chmod +x ./run.sh
    
    # To be aware of TERM and INT signals call ./run.sh
    # Running it with the --once flag at the end will shut down the agent after the build is executed
    ./run.sh "$@" & wait $!
    

    Poznámka:

    Pokud chcete spustit nové kopie kontejneru po dokončení práce, musíte také použít systém orchestrace kontejnerů, jako je Kubernetes nebo Azure Container Instances.

  6. V tomto adresáři spusťte následující příkaz:

    docker build --tag "azp-agent:linux" --file "./azp-agent-linux.dockerfile" .
    

    Konečný obrázek je označen azp-agent:linux.

Spustit obrázek

Teď, když jste vytvořili image, můžete spustit kontejner. Tím se nainstaluje nejnovější verze agenta, nakonfiguruje ji a spustí agenta. Cílí na zadaný fond agentů ( Default ve výchozím nastavení fond agentů) zadané instance Azure DevOps nebo Azure DevOps Serveru podle vašeho výběru:

docker run -e AZP_URL="<Azure DevOps instance>" -e AZP_TOKEN="<Personal Access Token>" -e AZP_POOL="<Agent Pool Name>" -e AZP_AGENT_NAME="Docker Agent - Linux" --name "azp-agent-linux" azp-agent:linux

Pokud chcete mít možnost zastavit kontejner a odebrat agenta, budete možná muset zadat příznaky --interactive a --tty (nebo jednoduše -it), abyste to mohli udělat s Ctrl + C.

docker run --interactive --tty < . . . >

Pokud chcete nový kontejner agenta pro každou úlohu kanálu, předejte příkazu příznak .

docker run < . . . > --once

--once Pomocí příznaku můžete chtít použít systém orchestrace kontejnerů, jako je Kubernetes nebo Azure Container Instances, ke spuštění nové kopie kontejneru po dokončení úlohy.

Název agenta, fond agentů a pracovní adresář agenta můžete řídit pomocí volitelných proměnných prostředí.

Proměnné prostředí

Proměnná prostředí Popis
AZP_URL Adresa URL instance Azure DevOps nebo Azure DevOps Serveru.
AZP_TOKEN Osobní přístupový token (PAT)
AZP_CLIENTID ID klienta služby principal
AZP_CLIENTSECRET Tajný klíč klienta služebního principálu
AZP_TENANTID ID tenanta instančního objektu služby
AZP_AGENT_NAME Název agenta (výchozí hodnota: název hostitele kontejneru).
AZP_POOL Název fondu agentů (výchozí hodnota: Default).
AZP_WORK Pracovní adresář (výchozí hodnota: _work).

Autentizace

Vyžaduje se jedna z následujících možností:

  • Pokud používáte PAT: AZP_TOKEN
  • Pokud používáte principál služby: AZP_CLIENTID, AZP_CLIENTSECRETa AZP_TENANTID

Oprávnění

Token nebo instanční objekt musí mít na úrovni organizace rozsah AZP_URL. Pokud používáte token PAT, musí ho vytvořit uživatel, který má oprávnění ke konfiguraci agentů.

Přidání nástrojů a přizpůsobení kontejneru

Vytvořili jste základního build agenta. Soubor Dockerfile můžete rozšířit tak, aby zahrnoval další nástroje a jejich závislosti, nebo pomocí tohoto souboru vytvořit vlastní kontejner jako základní vrstvu. Stačí se ujistit, že následující položky zůstanou nedotčené:

  • Skript start.sh je volán souborem Dockerfile.
  • Skript start.sh je poslední příkaz v souboru Dockerfile.
  • Ujistěte se, že derivátové kontejnery neodeberou žádné z uvedených závislostí v souboru Dockerfile.

Použití Dockeru v rámci kontejneru Dockeru

Pokud chcete použít Docker z kontejneru Dockeru, připojte soket Dockeru.

Upozornění

To má vážné bezpečnostní důsledky. Kód uvnitř kontejneru se teď může na hostiteli Dockeru spustit jako root.

Pokud jste si jistí, že to chcete udělat, přečtěte si dokumentaci k připojení úložiště na Docker.com.

Použití clusteru Azure Kubernetes Service

Upozornění

Vezměte, prosím, v úvahu, že úlohy používající Docker nebudou fungovat na AKS 1.19 nebo novější kvůli omezení dockeru v dockeru. Docker byl nahrazen kontejnerem v Kubernetes 1.19 a Docker-in-Docker začal být nedostupný.

Nasazení a konfigurace služby Azure Kubernetes Service

Postupujte podle kroků v rychlém startu: Nasazení clusteru Azure Kubernetes Service (AKS) pomocí webu Azure Portal. Potom může konzola PowerShell nebo Shell použít příkazový řádek kubectl.

Nasazení a konfigurace služby Azure Container Registry

Postupujte podle kroků v rychlém startu: Vytvoření registru kontejneru Azure pomocí webu Azure Portal. Potom můžete nahrávat a stahovat kontejnery ze služby Azure Container Registry.

Konfigurace tajemství a nasazení sady replik

  1. Vytvořte tajné kódy v clusteru AKS.

    kubectl create secret generic azdevops \
      --from-literal=AZP_URL=https://dev.azure.com/yourOrg \
      --from-literal=AZP_TOKEN=YourPAT \
      --from-literal=AZP_POOL=NameOfYourPool
    
  2. Spuštěním tohoto příkazu odešlete kontejner do služby Container Registry:

    docker push "<acr-server>/azp-agent:<tag>"
    
  3. Nakonfigurujte integraci služby Container Registry pro existující clustery AKS.

    Poznámka:

    Pokud máte na webu Azure Portal více předplatných, nejprve pomocí tohoto příkazu vyberte předplatné.

    az account set --subscription "<subscription id or subscription name>"
    
    az aks update -n "<myAKSCluster>" -g "<myResourceGroup>" --attach-acr "<acr-name>"
    
  4. Uložte následující obsah do ~/AKS/ReplicationController.yml:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: azdevops-deployment
      labels:
        app: azdevops-agent
    spec:
      replicas: 1 # here is the configuration for the actual agent always running
      selector:
        matchLabels:
          app: azdevops-agent
      template:
        metadata:
          labels:
            app: azdevops-agent
        spec:
          containers:
          - name: kubepodcreation
            image: <acr-server>/azp-agent:<tag>
            env:
              - name: AZP_URL
                valueFrom:
                  secretKeyRef:
                    name: azdevops
                    key: AZP_URL
              - name: AZP_TOKEN
                valueFrom:
                  secretKeyRef:
                    name: azdevops
                    key: AZP_TOKEN
              - name: AZP_POOL
                valueFrom:
                  secretKeyRef:
                    name: azdevops
                    key: AZP_POOL
            volumeMounts:
            - mountPath: /var/run/docker.sock
              name: docker-volume
          volumes:
          - name: docker-volume
            hostPath:
              path: /var/run/docker.sock
    

    Tento YAML pro Kubernetes vytvoří repliku setu a nasazení, kde replicas: 1 označuje počet agentů spuštěných v clusteru.

  5. Spusťte tento příkaz:

    kubectl apply -f ReplicationController.yml
    

Teď budou vaši agenti spouštět AKS cluster.

Nastavení vlastního parametru MTU

Povolit zadání hodnoty MTU pro sítě využívané kontejnery v rámci úloh (užitečné pro konfigurace docker-in-docker v klastru k8s).

Abyste nastavili hodnotu MTU, musíte nastavit proměnnou prostředí AGENT_DOCKER_MTU_VALUE a poté restartovat agenta v místním prostředí. Další informace o restartování agenta najdete tady a o nastavení různých proměnných prostředí pro každého jednotlivého agenta zde.

To vám umožní nastavit parametr sítě pro kontejner úloh, použití tohoto příkazu se podobá použití dalšího příkazu při konfiguraci sítě kontejneru:

-o com.docker.network.driver.mtu=AGENT_DOCKER_MTU_VALUE

Připojení svazků pomocí Dockeru uvnitř kontejneru Docker

Pokud se kontejner Dockeru spouští v dalším kontejneru Dockeru, oba používají démona hostitele, takže všechny připojovací cesty odkazují na hostitele, nikoli kontejner.

Pokud například chceme vložit cestu z hostitele do vnější vrstvy kontejneru Docker, můžeme použít tento příkaz:

docker run ... -v "<path-on-host>:<path-on-outer-container>" ...

A pokud chceme připojit cestu z hostitelského systému do Docker kontejneru, můžeme použít tento příkaz:

docker run ... -v "<path-on-host>:<path-on-inner-container>" ...

Ale nemůžeme připojit cesty z vnějšího kontejneru do vnitřního; abychom to obešli, musíme deklarovat proměnnou ENV.

docker run ... --env DIND_USER_HOME=$HOME ...

Potom můžeme vnitřní kontejner spustit z vnějšího kontejneru pomocí tohoto příkazu:

docker run ... -v "${DIND_USER_HOME}:<path-on-inner-container>" ...

Běžné chyby

Pokud používáte Windows a zobrazí se následující chyba:

standard_init_linux.go:178: exec user process caused "no such file or directory"

Nainstalujte Git Bash stažením a instalací git-scm.

Spusťte tento příkaz:

dos2unix ~/azp-agent-in-docker/Dockerfile
dos2unix ~/azp-agent-in-docker/start.sh
git add .
git commit -m "Fixed CR"
git push

Zkuste to ještě jednou. Už se vám chyba nezobrazí.