服务容器

Azure DevOps Services

如果管道需要一个或多个服务的支持,在许多情况下,需要按作业创建、连接和清理每个服务。 例如,管道可以运行需要访问数据库和内存缓存的集成测试。 需要为管道中的每个作业重新创建数据库和内存缓存。

容器提供了一种简单且可移植的方式来运行管道所依赖的服务。 使用服务容器可以自动创建、联网和管理容器化服务的生命周期。 每个服务容器只能由需要它的作业访问。 服务容器适用于任何类型的作业,但它们最常用于容器作业

要求

服务容器必须定义 CMDENTRYPOINT。 管道将为提供的容器运行 docker run ,而无需其他参数。

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

单个容器作业

使用 容器作业的简单示例:

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

此管道从 Docker Hub 提取 nginxbuildpack-deps 容器,然后启动容器。 容器联网在一起,以便可以按名称 services 相互访问。

在此作业容器内部, nginx 主机名使用 Docker 网络解析为正确的服务。 网络上的所有容器会自动向对方公开所有端口。

单个作业

还可以在没有作业容器的情况下使用服务容器。 一个简单的示例:

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

此管道启动最新的 nginx 容器。 由于作业未在容器中运行,因此没有自动名称解析。 此示例演示如何改为使用 localhost访问服务。 在上面的示例中,我们显式 (提供端口, 8080:80 例如,) 。

另一种方法是在运行时动态分配随机端口。 然后,可以使用 变量访问这些动态端口。 在 Bash 脚本中,可以使用进程环境访问变量。 这些变量采用以下形式: agent.services.<serviceName>.ports.<port>。 在上面的示例中, redis 在主机上分配了一个随机可用端口。 变量 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 数组。

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

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

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

如果作业在主机上运行,则需要 ports 访问服务。 端口采用 形式<hostPort>:<containerPort>或仅<containerPort>采用 ,末尾为可选/<protocol>,例如6379/tcp通过端口 6379公开 tcp ,绑定到主机上的随机端口。

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

卷可用于在服务之间共享数据,或用于在作业的多个运行之间保留数据。

可以将卷装载指定为 的 volumes数组。 卷可以命名为 Docker 卷、匿名 Docker 卷或主机上的绑定装载。

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

卷采用 的形式 <source>:<destinationPath>,其中 <source> 可以是主机上的命名卷或绝对路径,是 <destinationPath> 容器中的绝对路径。

注意

如果使用托管池,则不会在作业之间保留卷,因为在作业完成后会清理主机。

其他选项

服务容器与容器作业共享相同的容器资源。 这意味着可以使用相同的 附加选项

Healthcheck

(可选)如果任何服务容器指定 了 HEALTHCHECK,则代理将等待容器正常运行,然后再运行作业。

包含服务的多个容器示例

在此示例中,有一个 Django Python Web 容器连接到两个数据库容器 - PostgreSQL 和 MySQL。 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