Contenitori del servizio

Azure DevOps Services

Se la pipeline richiede il supporto di uno o più servizi, in molti casi si vuole creare, connettersi e pulire ogni servizio in base al processo. Ad esempio, una pipeline può eseguire test di integrazione che richiedono l'accesso a un database e a una cache di memoria. Per ogni processo nella pipeline è necessario creare il database e la cache di memoria.

Un contenitore offre un modo semplice e portabile per eseguire un servizio da cui dipende la pipeline. Un contenitore di servizi consente di creare, collegare in rete e gestire automaticamente il ciclo di vita del servizio in contenitori. Ogni contenitore di servizi è accessibile solo dal processo che lo richiede. I contenitori di servizi funzionano con qualsiasi tipo di processo, ma vengono usati più comunemente con i processi contenitore.

Requisiti

I contenitori di servizio devono definire un oggetto CMD o ENTRYPOINT. La pipeline verrà eseguita docker run per il contenitore fornito senza argomenti aggiuntivi.

Azure Pipelines può eseguire contenitori Linux o Windows. Usare Ubuntu ospitato per i contenitori Linux o il pool Di contenitori Windows ospitati per i contenitori Windows. Il pool macOS ospitato non supporta i contenitori in esecuzione.

Processo contenitore singolo

Esempio semplice di uso dei processi contenitore:

resources:
  containers:
  - container: my_container
    image: buildpack-deps:focal
  - container: nginx
    image: nginx


pool:
  vmImage: 'ubuntu-latest'

container: my_container
services:
  nginx: nginx

steps:
- script: |
    curl nginx
  displayName: Show that nginx is running

Questa pipeline recupera i nginx contenitori e buildpack-deps da Docker Hub e quindi avvia i contenitori. I contenitori vengono collegati insieme in modo che possano raggiungere l'uno dall'altro con il loro services nome.

Dall'interno di questo contenitore di processi, il nginx nome host si risolve nei servizi corretti usando la rete Docker. Tutti i contenitori nella rete espongono automaticamente tutte le porte tra loro.

Processo singolo

È anche possibile usare i contenitori di servizio senza un contenitore di processi. Un semplice esempio:

resources:
  containers:
  - container: nginx
    image: nginx
    ports:
    - 8080:80
    env:
      NGINX_PORT: 80
  - container: redis
    image: redis
    ports:
    - 6379

pool:
  vmImage: 'ubuntu-latest'

services:
  nginx: nginx
  redis: redis

steps:
- script: |
    curl localhost:8080
    echo $AGENT_SERVICES_REDIS_PORTS_6379

Questa pipeline avvia i contenitori più recenti nginx . Poiché il processo non è in esecuzione in un contenitore, non esiste una risoluzione automatica dei nomi. In questo esempio viene illustrato come raggiungere i servizi usando localhost. Nell'esempio precedente viene specificata in modo esplicito la porta , ad esempio 8080:80.

Un approccio alternativo consiste nell'consentire l'assegnazione dinamica di una porta casuale in fase di esecuzione. È quindi possibile accedere a queste porte dinamiche usando le variabili. In uno script Bash è possibile accedere a una variabile usando l'ambiente di elaborazione. Queste variabili prendono il formato: agent.services.<serviceName>.ports.<port>. Nell'esempio precedente viene redis assegnata una porta disponibile casuale nell'host. La agent.services.redis.ports.6379 variabile contiene il numero di porta.

Più processi

I contenitori di servizi sono utili anche per eseguire gli stessi passaggi rispetto a più versioni dello stesso servizio. Nell'esempio seguente vengono eseguiti gli stessi passaggi su più versioni di PostgreSQL.

resources:
  containers:
  - container: my_container
    image: ubuntu:22.04
  - container: pg15
    image: postgres:15
  - container: pg14
    image: postgres:14

pool:
  vmImage: 'ubuntu-latest'

strategy:
  matrix:
    postgres15:
      postgresService: pg15
    postgres14:
      postgresService: pg14

container: my_container

services:
  postgres: $[ variables['postgresService'] ]
steps:
- script: printenv

Porte

Quando si specifica una risorsa contenitore o un contenitore inline, è possibile specificare una matrice di da ports esporre nel contenitore.

resources:
  containers:
  - container: my_service
    image: my_service:latest
    ports:
    - 8080:80
    - 5432

services:
  redis:
    image: redis
    ports:
    - 6379/tcp

ports Se il processo è in esecuzione in un contenitore, la specifica non è necessaria perché i contenitori nella stessa rete Docker espongono automaticamente tutte le porte tra loro per impostazione predefinita.

Se il processo è in esecuzione nell'host, ports è necessario accedere al servizio. Una porta accetta la forma <hostPort>:<containerPort> o semplicemente <containerPort>, con un facoltativo /<protocol> alla fine, ad esempio 6379/tcp per esporre tcp sulla porta 6379, associata a una porta casuale nel computer host.

Per le porte associate a una porta casuale nel computer host, la pipeline crea una variabile del modulo agent.services.<serviceName>.ports.<port> in modo che possa essere accessibile dal processo. Ad esempio, agent.services.redis.ports.6379 risolve la porta assegnata in modo casuale nel computer host.

Volumi

I volumi sono utili per condividere i dati tra i servizi o per rendere persistenti i dati tra più esecuzioni di un processo.

È possibile specificare i montaggi del volume come matrice di volumes. I volumi possono essere denominati volumi Docker, volumi Docker anonimi o montaggi di associazione nell'host.

services:
  my_service:
    image: myservice:latest
    volumes:
    - mydockervolume:/data/dir
    - /data/dir
    - /src/dir:/dst/dir

I volumi accettano il formato <source>:<destinationPath>, dove <source> può essere un volume denominato o un percorso assoluto nel computer host ed <destinationPath> è un percorso assoluto nel contenitore.

Nota

Se si usano i pool ospitati, i volumi non verranno mantenuti tra processi perché il computer host viene pulito dopo il completamento del processo.

Altre opzioni

I contenitori di servizio condividono le stesse risorse del contenitore dei processi del contenitore. Ciò significa che è possibile usare le stesse opzioni aggiuntive.

Controllo integrità

Facoltativamente, se qualsiasi contenitore di servizio specifica un healthCHECK, l'agente attende fino a quando il contenitore non è integro prima di eseguire il processo.

Più contenitori con un esempio di servizi

In questo esempio è presente un contenitore Web Python Django connesso a due contenitori di database, PostgreSQL e MySQL. Il database PostgreSQL è il database primario e il relativo contenitore ha il nome db. Il db contenitore usa il volume /data/db:/var/lib/postgresql/data e sono presenti tre variabili di database passate al contenitore tramite env. Il mysql contenitore usa la porta 3306:3306 e sono presenti anche variabili di database passate tramite env. Il web contenitore è aperto con la porta 8000. Nei passaggi vengono pip installate le dipendenze e quindi vengono eseguiti test Django. Se si vuole ottenere un esempio di lavoro configurato, è necessario configurare un sito Django con due database. In questo esempio si presuppone che il manage.py file si trova nella directory radice e che il progetto Django si trova all'interno di tale directory. Potrebbe essere necessario aggiornare il /__w/1/s/ percorso in /__w/1/s/manage.py test.

resources:
  containers:
    - container: db
      image: postgres
      volumes:
          - '/data/db:/var/lib/postgresql/data'
      env:
        POSTGRES_DB: postgres
        POSTGRES_USER: postgres
        POSTGRES_PASSWORD: postgres
    - container: mysql
      image: 'mysql:5.7'
      ports:
         - '3306:3306'
      env:
        MYSQL_DATABASE: users
        MYSQL_USER: mysql
        MYSQL_PASSWORD: mysql
        MYSQL_ROOT_PASSWORD: mysql
    - container: web
      image: python
      volumes:
      - '/code'
      ports:
        - '8000:8000'

pool:
  vmImage: 'ubuntu-latest'

container: web
services:
  db: db
  mysql: mysql

steps:
    - script: |
        pip install django
        pip install psycopg2
        pip install mysqlclient
      displayName: set up django
    - script: |
          python /__w/1/s/manage.py test