服务容器

Azure DevOps Services

本文介绍如何在 Azure Pipelines 中使用 服务容器 。 如果管道需要一个或多个服务的支持,则可能需要为每个作业创建、连接及清理这些服务。 例如,管道可能会运行集成测试,这些测试需要访问管道中每个作业的新创建的数据库和内存缓存。

服务容器提供了一种简单且可移植的方式,用于在管道中运行服务。 只有需要服务容器的作业才能访问它。

使用服务容器,可以自动创建、网络和管理管道所依赖的服务生命周期。 服务容器适用于任何类型的作业,但最常与容器作业一起使用

注意

经典管道不支持服务容器。

条件和限制

  • 服务容器必须定义 CMDENTRYPOINT。 管道在没有提供容器的参数的情况下运行 docker run

  • Azure Pipelines 可以运行 Linux 或 Windows 容器。 使用适用于 Linux 容器的托管 Ubuntu 池或适用于 Windows 容器的托管 Windows 池。 托管的 macOS 池不支持运行容器。

  • 服务容器共享与容器作业相同的容器资源,因此它们可以使用相同的 启动选项

  • 如果服务容器指定 HEALTHCHECK,则代理可以选择等到容器运行作业前保持正常状态。

单容器作业

以下示例 YAML 管道定义使用服务容器的单个容器作业。 管道从buildpack-deps提取buildpack-deps容器,然后启动所有容器。 容器已建立网络,以便它们可以通过名称 services 相互访问。

从作业容器内部,nginx 主机名通过 Docker 网络解析到正确的服务。 网络上的所有容器会自动向对方公开所有端口。

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

单个非容器作业

还可以在非容器作业中使用服务容器。 管道启动最新容器,但由于任务未在容器中运行,因此没有自动名称解析。 而是通过使用localhost来访问服务。 以下示例管道显式地指定了 8080:80 的端口 nginx

另一种方法是在运行时动态分配随机端口。 为了允许作业访问端口,管道会创建一个形式的变量。 可以在 Bash 脚本中使用此 环境变量 来访问动态端口。

在以下管道中, redis 获取主机上的随机可用端口,变量 agent.services.redis.ports.6379 包含端口号。

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

多个作业

对于针对同一服务的多个版本运行相同的步骤,服务容器也很有用。 在以下示例中,将针对多个版本的 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

端口

直接在主机上运行的作业需要 ports 访问服务容器。 ports如果作业在容器中运行,则不需要指定,因为同一 Docker 网络上的容器默认会自动向彼此公开所有端口。

端口采用窗体 <hostPort>:<containerPort> 或末尾的 <containerPort> 可选 /<protocol> 端口。 例如,6379/tcp通过端口tcp公开6379,绑定到主机上的随机端口。

调用容器资源或内联容器时,可以指定要在容器上公开的 ports 数组,如以下示例所示。

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

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

对于绑定到主机上随机端口的端口,管道将创建窗体 agent.services.<serviceName>.ports.<port> 的变量,以便作业可以访问端口。 例如,agent.services.redis.ports.6379 解析为主机上随机分配的端口。

卷可用于在服务之间共享数据,或者用于在作业的多个运行之间保存数据。 将卷装载指定为数组 volumes

每个卷采用格式 <source>:<destinationPath>,其中 <source> 命名卷或主机上的绝对路径,并且 <destinationPath> 是容器中的绝对路径。 卷可以命名为 Docker 卷、匿名 Docker 卷或在主机上绑定装载。

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

注意

Microsoft托管的资源池不会在作业之间保留卷,因为每次作业后都会清理宿主机器。

包含服务的多个容器示例

以下示例管道包含连接到 PostgreSQL 和 MySQL 数据库容器的 Django Python Web 容器。

  • PostgreSQL 数据库是主数据库,其容器命名 db
  • 容器 db 使用卷 /data/db:/var/lib/postgresql/data,并通过该容器将三个数据库变量传递给容器 env
  • 容器mysql使用端口3306:3306,并通过env传递数据库变量。
  • 容器 web 使用端口 8000打开。

在步骤中, pip 安装依赖项,然后运行 Django 测试。

若要设置工作示例,需要使用两个 数据库设置 Django 站点。 该示例假定 manage.py 文件和 Django 项目位于根目录中。 否则,可能需要在 .. 中/__w/1/s/更新/__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