Contentores de serviço

Azure DevOps Services

Se o pipeline necessitar do suporte de um ou mais serviços, em muitos casos, irá querer criar, ligar e limpar cada serviço numa base por tarefa. Por exemplo, um pipeline pode executar testes de integração que requerem acesso a uma base de dados e a uma cache de memória. A base de dados e a cache de memória têm de ser criadas recentemente para cada tarefa no pipeline.

Um contentor proporciona uma forma simples e portátil de executar um serviço do qual o pipeline depende. Um contentor de serviço permite criar, funcionar em rede e gerir automaticamente o ciclo de vida do serviço de contentores. Cada contentor de serviço é acessível apenas pelo trabalho que o requer. Os contentores de serviço funcionam com qualquer tipo de trabalho, mas são utilizados com maior frequência com trabalhos de contentor.

Requisitos

Os contentores de serviço têm de definir um CMD ou ENTRYPOINT. O pipeline será executado docker run para o contentor fornecido sem argumentos adicionais.

Os Pipelines do Azure podem executar Contentores Linux ou Windows. Utilize o Ubuntu alojado para contentores do Linux ou o Conjunto de Contentores do Windows alojado para contentores do Windows. (O conjunto de macOS alojado não suporta contentores em execução.)

Tarefa de contentor único

Um exemplo simples de utilização de tarefas de contentor:

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

Este pipeline obtém os nginx contentores e buildpack-deps do Docker Hub e, em seguida, inicia os contentores. Os contentores são agrupados em rede para que possam alcançar-se entre si pelo respetivo services nome.

A partir deste contentor de tarefas, o nome do nginx anfitrião é resolvido para os serviços corretos através da rede do Docker. Todos os contentores na rede expõem automaticamente todas as portas entre si.

Tarefa única

Também pode utilizar contentores de serviço sem um contentor de tarefas. Um exemplo simples:

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

Este pipeline inicia os contentores mais recentes nginx . Uma vez que a tarefa não está em execução num contentor, não existe resolução automática de nomes. Este exemplo mostra como pode, em vez disso, aceder aos serviços com localhost. No exemplo acima, fornecemos explicitamente a porta (por exemplo, 8080:80).

Uma abordagem alternativa é permitir que uma porta aleatória seja atribuída dinamicamente no runtime. Em seguida, pode aceder a estas portas dinâmicas com variáveis. Num script do Bash, pode aceder a uma variável com o ambiente de processo. Estas variáveis assumem o formulário: agent.services.<serviceName>.ports.<port>. No exemplo acima, redis é atribuída uma porta disponível aleatória no anfitrião. A agent.services.redis.ports.6379 variável contém o número da porta.

Várias tarefas

Os contentores de serviço também são úteis para executar os mesmos passos em várias versões do mesmo serviço. No exemplo seguinte, os mesmos passos são executados em várias versões do 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

Portas

Ao especificar um recurso de contentor ou um contentor inline, pode especificar uma matriz de ports para expor no contentor.

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

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

A especificação ports não é necessária se a sua tarefa estiver em execução num contentor porque os contentores na mesma rede do Docker expõem automaticamente todas as portas entre si por predefinição.

Se a sua tarefa estiver em execução no anfitrião, será ports necessário aceder ao serviço. Uma porta assume o formulário <hostPort>:<containerPort> ou apenas <containerPort>, com uma opcional /<protocol> no final, por exemplo 6379/tcp , para expor tcp através da porta 6379, vinculada a uma porta aleatória no computador anfitrião.

Para portas vinculadas a uma porta aleatória no computador anfitrião, o pipeline cria uma variável do formulário agent.services.<serviceName>.ports.<port> para que possa ser acedido pela tarefa. Por exemplo, agent.services.redis.ports.6379 resolve para a porta atribuída aleatoriamente no computador anfitrião.

Volumes

Os volumes são úteis para partilhar dados entre serviços ou para dados persistentes entre várias execuções de uma tarefa.

Pode especificar montagens de volume como uma matriz de volumes. Os volumes podem ser denominados volumes do Docker, volumes do Docker anónimos ou montagens de enlace no anfitrião.

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

Os volumes assumem o formulário <source>:<destinationPath>, onde <source> pode ser um volume com nome ou um caminho absoluto no computador anfitrião e <destinationPath> é um caminho absoluto no contentor.

Nota

Se utilizar os nossos conjuntos alojados, os volumes não serão mantidos entre tarefas porque o computador anfitrião é limpo após a conclusão da tarefa.

Outras opções

Os contentores de serviço partilham os mesmos recursos de contentor que as tarefas de contentor. Isto significa que pode utilizar as mesmas opções adicionais.

Verificação de estado de funcionamento

Opcionalmente, se um contentor de serviço especificar um HEALTHCHECK, o agente aguarda até que o contentor esteja em bom estado de funcionamento antes de executar a tarefa.

Exemplo de vários contentores com serviços

Neste exemplo, existe um contentor Web do Django Python ligado a dois contentores de base de dados - PostgreSQL e MySQL. A base de dados PostgreSQL é a base de dados primária e o respetivo contentor tem o nome db. O db contentor utiliza o volume /data/db:/var/lib/postgresql/data e existem três variáveis de base de dados transmitidas para o contentor através de env. O mysql contentor utiliza a porta 3306:3306 e também existem variáveis de base de dados transmitidas através do env. O web contentor está aberto com a porta 8000. Nos passos, pip instala dependências e, em seguida, o teste django é executado. Se quiser configurar um exemplo de trabalho, precisará de um site django configurado com duas bases de dados. Este exemplo pressupõe que o seu manage.py ficheiro está no diretório de raiz e que o projeto Django está dentro desse diretório. Poderá ter de atualizar o /__w/1/s/ caminho em /__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