Contentores de serviço
Azure DevOps Services
Se o seu oleoduto necessitar do apoio de um ou mais serviços, em muitos casos irá querer criar, conectar-se e limpar cada serviço por trabalho. Por exemplo, um gasoduto 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 precisam de ser criadas recentemente para cada trabalho no oleoduto.
Um recipiente fornece uma forma simples e portátil de executar um serviço do que o seu oleoduto depende. Um recipiente de serviço permite-lhe criar, rede e gerir automaticamente o ciclo de vida do seu serviço contentorizado. Cada recipiente de serviço é acessível apenas pelo trabalho que o exija. Os contentores de serviço trabalham com qualquer tipo de trabalho, mas são mais usados com trabalhos de contentores.
Requisitos
Os recipientes de serviço devem definir um CMD
ou .ENTRYPOINT
O gasoduto funcionará docker run
para o recipiente fornecido sem argumentos adicionais.
Os gasodutos Azure podem executar Linux ou Windows Containers. Utilize ubuntu alojado para recipientes Linux ou a piscina de contentores hosted Windows para recipientes Windows. (A piscina do macOS hospedada não suporta recipientes de funcionamento.)
Trabalho de contentor único
Um exemplo simples de utilização de trabalhos de contentores:
resources:
containers:
- container: my_container
image: buildpack-deps:focal
- container: nginx
image: nginx
pool:
vmImage: 'ubuntu-20.04'
container: my_container
services:
nginx: nginx
steps:
- script: |
curl nginx
displayName: Show that nginx is running
Este oleoduto recolhe os nginx
contentores e buildpack-deps
os contentores do Docker Hub e, em seguida, inicia os contentores. Os contentores estão em rede para que possam alcançar-se pelo nome services
.
A partir de dentro deste recipiente de trabalho, o nome do nginx
anfitrião resolve-se para os serviços corretos usando a rede Docker.
Todos os contentores da rede expõem automaticamente todas as portas umas às outras.
Trabalho único
Também pode utilizar recipientes de serviço sem um recipiente de trabalho. 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-18.04'
services:
nginx: nginx
redis: redis
steps:
- script: |
curl localhost:8080
echo $AGENT_SERVICES_REDIS_PORTS_6379
Este oleoduto inicia os mais recentes nginx
contentores. Como o trabalho não está a funcionar num contentor, não há resolução automática de nomes.
Este exemplo mostra como pode, em vez disso, chegar aos serviços utilizando localhost
.
No exemplo acima, fornecemos a porta explicitamente (por exemplo, 8080:80
).
Uma abordagem alternativa é permitir que uma porta aleatória seja atribuída dinamicamente no tempo de execução. Em seguida, pode aceder a estas portas dinâmicas utilizando variáveis.
Num script Bash, você pode aceder a uma variável usando o ambiente do processo. Estas variáveis assumem o formulário: agent.services.<serviceName>.ports.<port>
.
No exemplo acima, redis
é atribuída uma porta disponível aleatoriamente no anfitrião.
A agent.services.redis.ports.6379
variável contém o número da porta.
Múltiplos empregos
Os recipientes de serviço também são úteis para executar os mesmos passos contra várias versões do mesmo serviço. No exemplo seguinte, os mesmos passos correm contra várias versões de PostgreSQL.
resources:
containers:
- container: my_container
image: ubuntu:18.04
- container: pg12
image: postgres:12
- container: pg11
image: postgres:11
pool:
vmImage: 'ubuntu-18.04'
strategy:
matrix:
postgres12:
postgresService: pg12
postgres11:
postgresService: pg11
container: my_container
services:
postgres: $[ variables['postgresService'] ]
steps:
- script: printenv
Portas
Ao especificar um recurso de contentor ou um recipiente de linha, pode especificar uma matriz para ports
expor no recipiente.
resources:
containers:
- container: my_service
image: my_service:latest
ports:
- 8080:80
- 5432
services:
redis:
image: redis
ports:
- 6379/tcp
Não é necessário especificar ports
se o seu trabalho está a funcionar num contentor porque os contentores da mesma rede Docker expõem automaticamente todas as portas umas às outras por defeito.
Se o seu trabalho estiver a decorrer no anfitrião, então ports
é necessário aceder ao serviço. Uma porta toma o formulário <hostPort>:<containerPort>
ou apenas <containerPort>
, com uma opcional /<protocol>
no final, por exemplo 6379/tcp
, para expor tcp
sobre a porta 6379
, ligada a uma porta aleatória na máquina hospedeira.
Para portas ligadas a uma porta aleatória na máquina hospedeira, o gasoduto cria uma variável do formulário agent.services.<serviceName>.ports.<port>
para que possa ser acedido pelo trabalho. Por exemplo, agent.services.redis.ports.6379
resolve-se a porta atribuída aleatoriamente na máquina hospedeira.
Volumes
Os volumes são úteis para a partilha de dados entre serviços, ou para dados persistentes entre várias execuções de um trabalho.
Pode especificar os suportes de volume como uma matriz de volumes
. Os volumes podem ser chamados volumes Docker, volumes anónimos do Docker, ou montar suportes no hospedeiro.
services:
my_service:
image: myservice:latest
volumes:
- mydockervolume:/data/dir
- /data/dir
- /src/dir:/dst/dir
Os volumes tomam a forma <source>:<destinationPath>
, onde <source>
pode ser um volume nomeado ou um caminho absoluto na máquina hospedeira, e <destinationPath>
é um caminho absoluto no recipiente.
Nota
Se utilizar as nossas piscinas hospedadas, então os seus volumes não serão persistidos entre os trabalhos porque a máquina hospedeira é limpa após o trabalho estar concluído.
Outras opções
Os contentores de serviço partilham os mesmos recursos de contentores que os postos de trabalho dos contentores. Isto significa que pode usar as mesmas opções adicionais.
Verificação de saúde
Opcionalmente, se algum recipiente de serviço especificar um HEALTHCHECK, o agente aguarda até que o recipiente esteja saudável antes de executar o trabalho.
Vários contentores com exemplo de serviços
Neste exemplo, há um recipiente web 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 seu contentor tem o nome db
. O db
recipiente utiliza volume /data/db:/var/lib/postgresql/data
e há três variáveis de base de dados passadas para o contentor via env
. O mysql
contentor utiliza a porta 3306:3306
e há também variáveis de base de dados passadas via env
. O web
recipiente está aberto com porta 8000
. Nos degraus, pip
instala-se dependências e, em seguida, o teste de Django são executados. Se quiser ter 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 o seu projeto Django está dentro desse diretório. Pode ser necessário 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