Monthly Archives: May 2017

使用 Docker 构建

使用 Docker 构建

原文:https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/ci/docker/using_docker_build.md

GitLab CI 允许你使用 Docker Engine 构建和测试基于 Docker 的项目。

这也允许您使用docker-compose和其他 docker-enabled 的工具。

持续集成/持续部署的新趋势之一是:

  1. 创建应用程序镜像,
  2. 针对创建的镜像运行测试,
  3. 将镜像推送到远程 Registry,并
  4. 从推送的镜像部署到服务器。

当您的应用程序已经具有可用于创建和测试镜像的Dockerfile时,它也很有用:

$ docker build -t my-image dockerfiles/
$ docker run my-docker-image /script/to/run/tests
$ docker tag my-image my-registry:5000/my-image
$ docker push my-registry:5000/my-image

这需要 GitLab Runner 的特殊配置,以在作业期间启用docker支持。

Runner 配置

在作业中有三种方法可以使用docker builddocker run,每个都有自己的考虑。

使用 Shell 执行器

最简单的方法是在shell执行模式下安装 GitLab Runner。然后 GitLab Runner 作为gitlab-runner用户执行作业脚本。

  1. 安装 GitLab Runner

  2. 在 GitLab Runner 安装期间,选择shell作为执行作业脚本或使用命令的方法:

    sudo gitlab-ci-multi-runner register -n \
      --url https://gitlab.com/ \
      --registration-token REGISTRATION_TOKEN \
      --executor shell \
      --description "My Runner"
    
  3. 在服务器上安装 Docker Engine。

    有关如何在不同系统上安装 Docker Engine 的更多信息,请参阅 Supported installations

  4. 新增 gitlab-runner 用户到 docker 组:

    sudo usermod -aG docker gitlab-runner
    
  5. 验证gitlab-runner是否可以访问Docker:
    sudo -u gitlab-runner -H docker info
    

    现在你可以通过将docker info添加到.gitlab-ci.yml中来验证一切是否正常:

    before_script:
      - docker info
    
    build_image:
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests
    
  6. 现在可以使用docker命令,如果需要可以安装docker-compose

注:
* 通过在docker组中添加gitlab-runner,你可以有效地授予gitlab-runner的完整的 root 权限。有关更多信息,请阅读 On Docker security: docker group considered harmful

使用 docker-in-docker 执行器

第二种方法是使用专门的 Docker 镜像 docker-in-docker(dind),它安装了所有工具(dockerdocker-compose),并以特权模式在该镜像的上下文中运行作业脚本。

为了做到这一点,请按以下步骤操作:

  1. 安装 GitLab Runner

  2. 从命令行注册 GitLab Runner 以使用dockerprivileged模式:

    sudo gitlab-ci-multi-runner register -n \
      --url https://gitlab.com/ \
      --registration-token REGISTRATION_TOKEN \
      --executor docker \
      --description "My Docker Runner" \
      --docker-image "docker:latest" \
      --docker-privileged
    

    上面的命令将注册一个新的 Runner 来使用 Docker 所提供的特殊docker:latest镜像。请注意,它使用privileged模式启动构建和服务容器。如果要使用docker-in-docker模式,您始终必须在 Docker 容器中使用privileged = true

    上面的命令将创建一个类似于这个的config.toml条目:

    [[runners]]
      url = "https://gitlab.com/"
      token = TOKEN
      executor = "docker"
      [runners.docker]
        tls_verify = false
        image = "docker:latest"
        privileged = true
        disable_cache = false
        volumes = ["/cache"]
      [runners.cache]
        Insecure = false
    
  3. 您现在可以在构建脚本中使用docker(请注意包含docker:dind服务)
    image: docker:latest
    
    # When using dind, it's wise to use the overlayfs driver for
    # improved performance.
    variables:
      DOCKER_DRIVER: overlay
    
    services:
      - docker:dind
    
    before_script:
      - docker info
    
    build:
      stage: build
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests
    

Docker-in-Docker 运行良好,是推荐的配置,但并不是没有挑战:

  • 启用--docker-privileged禁用了容器的所有安全机制,并使你的主机由于特权升级而暴露,从而导致容器突破(主机-容器屏障)。有关更多信息,请查看官方 Docker 文档 Runtime privilege and Linux capabilities
  • 当使用 docker-in-docker 时,每个作业都处于一个干净的环境中,没有过去的历史。并发作业工作正常,因为每个构建都获得自己的 Docker Engine 实例,因此不会相互冲突。但这也意味着作业可能会更慢,因为没有缓存层。
  • 默认情况下,docker:dind使用--storage-driver vfs,这是最慢的形式。要使用其他驱动程序,请参阅使用 overlayfs 驱动程序

使用这种方法的示例项目可以在这里找到:https://gitlab.com/gitlab-examples/docker.

Use Docker socket binding

第三种方法是将/var/run/docker.sock绑定装载到容器中,以便 docker 在该镜像的上下文中可用。

为了做到这点,遵循以下步骤:

  1. 安装 GitLab Runner.

  2. 从命令行注册 GitLab Runner 以使用docker并共享/var/run/docker.sock

    sudo gitlab-ci-multi-runner register -n \
      --url https://gitlab.com/ \
      --registration-token REGISTRATION_TOKEN \
      --executor docker \
      --description "My Docker Runner" \
      --docker-image "docker:latest" \
      --docker-volumes /var/run/docker.sock:/var/run/docker.sock
    

    上面的命令将注册一个新的 Runner 来使用 Docker 提供的特殊docker:latest镜像。请注意,它正在使用 Runner 本身的 Docker 守护进程,docker 命令产生的任何容器都将是 Runner 的兄弟,而不是所运行程序的子进程。这可能会有不适合您的工作流程的复杂性和局限性。

    上面的命令将创建一个类似于这个的config.toml条目:

    [[runners]]
      url = "https://gitlab.com/"
      token = REGISTRATION_TOKEN
      executor = "docker"
      [runners.docker]
        tls_verify = false
        image = "docker:latest"
        privileged = false
        disable_cache = false
        volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
      [runners.cache]
        Insecure = false
    
  3. 您现在可以在构建脚本中使用docker(请注意,在 Docker 执行器中使用 Docker 时,不需要包含docker:dind服务):
    image: docker:latest
    
    before_script:
    - docker info
    
    build:
      stage: build
      script:
      - docker build -t my-docker-image .
      - docker run my-docker-image /script/to/run/tests
    

虽然上述方法避免在特权模式下使用 Docker,但您应该了解以下影响:

  • 共享 docker 守护进程,禁用了容器的所有安全机制,并将主机暴露给特权提升,从而导致容器突破屏障。例如,如果一个项目运行docker rm -f $(docker ps -a -q),它将删除 GitLab Runner 容器。
  • 并发作业可能无效;如果你的测试创建了具有特定名称的容器,它们可能会相互冲突。
  • 将文件和目录从源代码库共享到容器中可能无法正常工作,因为卷装载是在主机上下文中完成的,而不是在构建容器中,例如:
    docker run --rm -t -i -v $(pwd)/src:/home/app/src test-image:latest run_app_tests
    

使用 OverlayFS 驱动程序

默认情况下,使用docker:dind时,Docker 使用vfs存储驱动程序,每次运行时都会拷贝文件系统。磁盘操作非常密集,如果使用不同的驱动程序(例如overlay),则可以避免这种情况。

  1. 确保使用最近的内核,最好是>= 4.2
  2. 检查overlay模块是否加载:
    sudo lsmod | grep overlay
    

    如果没有结果,那就没有加载。加载之:

    sudo modprobe overlay
    

    如果一切顺利,您需要确保在系统重启时也加载该模块。Ubuntu 系统上是通过编辑/etc/modules完成的。添加以下行:

    overlay
    
  3. .gitlab-ci.yml顶部定义一个变量以使用该驱动:
    variables:
      DOCKER_DRIVER: overlay
    

使用 GitLab 容器 Registry

注:
- 此功能需要 GitLab 8.8 和 GitLab Runner 1.2。
- 从 GitLab 8.12 开始,如果你的帐户启用了两步认证,则需要传递个人访问令牌而不是密码,才能登录到 GitLab 的 Container Registry。

一旦构建了 Docker 镜像,就可以将其推送到内置的 GitLab 容器 Registry 中。例如,如果你在 runner 上使用 docker-in-docker,那.gitlab-ci.yml可能如下:

 build:
   image: docker:latest
   services:
   - docker:dind
   stage: build
   script:
     - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.example.com
     - docker build -t registry.example.com/group/project/image:latest .
     - docker push registry.example.com/group/project/image:latest

必须使用为你创建的特殊gitlab-ci-token用户,才能推送到连接到项目的 Registry。它的密码由$CI_JOB_TOKEN变量提供。这允许您自动构建和部署 Docker 镜像。

您也可以利用其他变量来避免硬编码:

services:
  - docker:dind

variables:
  IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME

before_script:
  - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY

build:
  stage: build
  script:
    - docker build -t $IMAGE_TAG .
    - docker push $IMAGE_TAG

在这里,$CI_REGISTRY_IMAGE将解析为与该项目相关联的 Registry 地址,$CI_COMMIT_REF_NAME将解析为该作业所在分支或标签的名称。还声明了我们自己的变量$IMAGE_TAG,将两者结合起来,以节省我们在script部分中的输入。

这是一个更详细的例子,将任务分解为 4 个流水线(pipeline)阶段,包括并行运行的两个测试。build存储在容器 Registry 中,并在后续阶段使用,需要时则下载该镜像。对master的更改也被标记为latest,并使用特定于应用程序的部署脚本进行部署:

image: docker:latest
services:
  - docker:dind

stages:
  - build
  - test
  - release
  - deploy

variables:
  CONTAINER_TEST_IMAGE: registry.example.com/my-group/my-project/my-

image:$CI_COMMIT_REF_NAME
  CONTAINER_RELEASE_IMAGE: registry.example.com/my-group/my-project/my-image:latest

before_script:
  - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.example.com

build:
  stage: build
  script:
    - docker build --pull -t $CONTAINER_TEST_IMAGE .
    - docker push $CONTAINER_TEST_IMAGE

test1:
  stage: test
  script:
    - docker pull $CONTAINER_TEST_IMAGE
    - docker run $CONTAINER_TEST_IMAGE /script/to/run/tests

test2:
  stage: test
  script:
    - docker pull $CONTAINER_TEST_IMAGE
    - docker run $CONTAINER_TEST_IMAGE /script/to/run/another/test

release-image:
  stage: release
  script:
    - docker pull $CONTAINER_TEST_IMAGE
    - docker tag $CONTAINER_TEST_IMAGE $CONTAINER_RELEASE_IMAGE
    - docker push $CONTAINER_RELEASE_IMAGE
  only:
    - master

deploy:
  stage: deploy
  script:
    - ./deploy.sh
  only:
    - master

使用容器 Registry 的注意事项:

  • 在运行命令之前必须先登录到容器 Registry。把它放在before_script里,会在每个作业之前运行它。
  • 使用docker build --pull可以确保 Docker 在构建前获取 base 镜像的任何更改,以防您的缓存失效。这需要运行更长时间,但意味着不会遇到未打安全补丁的 base 镜像。
  • 在每个docker run之前做一个明确的docker pull,确保获取刚构建的最新镜像。如果您正在使用多个会在本地缓存镜像的 Runner,这一点尤为重要。在镜像标签中使用 git SHA 又使得这不太必要,因为每个作业都将是唯一的,并且您不应该有一个过时的镜像,但是如在依赖更改后,重新构建给定的 Commit,这仍然可能(有过时的镜像)。
  • 同时发生多个作业的情况下,你不会想直接构建为latest

GitLab Runners

GitLab Runners

Original URL: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/ci/runners/README.md

GitLab CI 中,Runners 运行你的 yaml。Runner 是一个独立/隔离的(虚拟)机器,它通过 GitLab CI 的协调器(coordinator) API 拾取作业。

Runner 可以服务特定的项目,或为 GitLab CI 的任何项目提供服务。为所有项目提供服务的 Runner 称为 Shared Runner

理想情况下,GitLab Runner 不应与 GitLab 安装在相同机器上。阅读需求文档以获取更多信息。

Shared vs. Specific Runners

具体的 Runner 仅针对指定的项目运行。共享的 Runner 可以为启用了 Allow shared Runners 的项目运行作业。

Shared Runners 对多个项目间需求类似的作业很有用。您可以使用一个或多个 Runner 来处理多个项目,而不是让多个 Runner 闲置等待多个项目。这样可以更容易维护和更新 Runner。

Specific Runners 对于要求特殊的作业,或有特定需求的项目很有用。如果一个作业有一定的要求,你可以设置一个 Specific Runner 来搞,而不必为所有 Runner 这样做。例如,如果要部署某个项目,可以设置 Specific Runner 以获取正确的凭据。

CI 活动频繁旺盛的项目也可以从使用 Specific Runner 中受益。通过 Specific Runners 可确保 Runner 不被其他项目的作业阻塞。

您可以设置 Specific Runner 供多个项目使用。与 Shared Runner 的区别在于,必须为 Runner 显式地启用上述每个项目,才能运行这些项目的作业。

Specific Runner 不会自动与 fork 出去的项目共享。fork 将复制所克隆的代码仓库 CI 设置(jobsallow shared 等)。

创建并注册 Runner

有几种方法可以创建 Runner。只有创建后,注册才能确定其为SharedSpecific状态。

安装 Runner 实例的不同方法,请参阅文档

安装 Runner 后,可将其注册为SharedSpecific。如果您有 GitLab 实例的管理员访问权限,则只能注册一个Shared Runner

注册Shared Runner

如果您是链接的 GitLab 实例上的管理员,则只能注册一个Shared Runner
(You can only register a shared Runner if you are an admin on the linked GitLab instance.)

在 GitLab CI 实例的admin/runners页面上获取shared-Runner令牌。

shared token

现在只需注册 Runner 即可:

sudo gitlab-ci-multi-runner register

从 GitLab 8.2 起,Shared Runners 默认启用,但可以使用DISABLE SHARED RUNNERS按钮禁用。以前版本的 GitLab 中,Shared Runners 默认为禁用。

注册Specific Runner

Registering a specific can be done in two ways:

  1. Creating a Runner with the project registration token
  2. Converting a shared Runner into a specific Runner (one-way, admin only)

There are several ways to create a Runner instance. The steps below only concern registering the Runner on GitLab CI.

Registering a Specific Runner with a Project Registration token

To create a specific Runner without having admin rights to the GitLab instance, visit the project you want to make the Runner work for in GitLab CI.

Click on the Runner tab and use the registration token you find there to setup a specific Runner for this project.

project Runners in GitLab CI

To register the Runner, run the command below and follow instructions:

sudo gitlab-ci-multi-runner register

Lock a specific Runner from being enabled for other projects

You can configure a Runner to assign it exclusively to a project. When a Runner is locked this way, it can no longer be enabled for other projects. This setting is available on each Runner in Project Settings > Runners.

Making an existing Shared Runner Specific

If you are an admin on your GitLab instance, you can make any shared Runner a specific Runner, but you can not make a specific Runner a shared Runner.

To make a shared Runner specific, go to the Runner page (/admin/runners) and find your Runner. Add any projects on the left to make this Runner run exclusively for these projects, therefore making it a specific Runner.

making a shared Runner specific

Using Shared Runners Effectively

If you are planning to use shared Runners, there are several things you should keep in mind.

Use Tags

You must setup a Runner to be able to run all the different types of jobs that it may encounter on the projects it's shared over. This would be problematic for large amounts of projects, if it wasn't for tags.

By tagging a Runner for the types of jobs it can handle, you can make sure shared Runners will only run the jobs they are equipped to run.

For instance, at GitLab we have Runners tagged with "rails" if they contain the appropriate dependencies to run Rails test suites.

Prevent Runner with tags from picking jobs without tags

You can configure a Runner to prevent it from picking jobs with tags when the Runner does not have tags assigned. This setting is available on each Runner in Project Settings > Runners.

Be careful with sensitive information

If you can run a job on a Runner, you can get access to any code it runs and get the token of the Runner. With shared Runners, this means that anyone that runs jobs on the Runner, can access anyone else's code that runs on the Runner.

In addition, because you can get access to the Runner token, it is possible to create a clone of a Runner and submit false jobs, for example.

The above is easily avoided by restricting the usage of shared Runners on large public GitLab instances and controlling access to your GitLab instance.

Forks

Whenever a project is forked, it copies the settings of the jobs that relate to it. This means that if you have shared Runners setup for a project and someone forks that project, the shared Runners will also serve jobs of this project.

Attack vectors in Runners

Mentioned briefly earlier, but the following things of Runners can be exploited. We're always looking for contributions that can mitigate these Security Considerations.

在容器内运行 GitLab Runner

在容器内运行 GitLab Runner

这描述如何在 Docker 容器中运行 GitLab Runner。

Docker 镜像安装及配置

首先安装 Docker:

curl -sSL https://get.docker.com/ | sh

需要在gitlab-runner容器中装载一个用于配置和其他资源的配置存储卷,:

docker run -d --name gitlab-runner --restart always \
  -v /srv/gitlab-runner/config:/etc/gitlab-runner \
  -v /var/run/docker.sock:/var/run/docker.sock \
  gitlab/gitlab-runner:latest

或者,你可以使用配置容器来装载自定义数据卷:

docker run -d --name gitlab-runner-config \
    -v /etc/gitlab-runner \
    busybox:latest \
    /bin/true

docker run -d --name gitlab-runner --restart always \
    --volumes-from gitlab-runner-config \
    gitlab/gitlab-runner:latest

如果打算使用 Docker 作为产生 Runners 的方法,则要这样装载 docker socket:

docker run -d --name gitlab-runner --restart always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /srv/gitlab-runner/config:/etc/gitlab-runner \
  gitlab/gitlab-runner:latest

注册 runner (查看 Runner 文档了解如何获取令牌):

docker exec -it gitlab-runner gitlab-runner register

Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com )
https://gitlab.com
Please enter the gitlab-ci token for this runner
xxx
Please enter the gitlab-ci description for this runner
my-runner
INFO[0034] fcf5c619 Registering runner... succeeded
Please enter the executor: shell, docker, docker-ssh, ssh?
docker
Please enter the Docker image (eg. ruby:2.1):
ruby:2.1
INFO[0037] Runner registered successfully. Feel free to start it, but if it's
running already the config should be automatically reloaded!

Runner 应该已经启动了,已准备好构建项目!

请确保阅读了有关 GitLab Runner 最常见问题的 FAQ

更新

拉取最新的(latest)版本:

docker pull gitlab/gitlab-runner:latest

停止并删除现有容器:

docker stop gitlab-runner && docker rm gitlab-runner

像原来一样启动容器:

docker run -d --name gitlab-runner --restart always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /srv/gitlab-runner/config:/etc/gitlab-runner \
  gitlab/gitlab-runner:latest

注意:您需要使用与原来装载数据卷相同的方法(-v /srv/gitlab-runner/config:/etc/gitlab-runner--volumes-from gitlab-runner

安装受信的 SSL 服务器证书

如果你的 GitLab CI 服务器在使用自签名 SSL 证书,那应确保 GitLab CI 服务器证书被 gitlab-ci-multi-runner 容器信任,以便它们能够相互通信。

The gitlab/gitlab-runner image is configured to look for the trusted SSL certificates at /etc/gitlab-runner/certs/ca.crt, this can however be changed using the -e "CA_CERTIFICATES_PATH=/DIR/CERT" configuration option.

Copy the ca.crt file into the certs directory on the data volume (or container).
The ca.crt file should contain the root certificates of all the servers you want gitlab-ci-multi-runner to trust. The gitlab-ci-multi-runner container will import the ca.crt file on startup so if your container is already running you may need to restart it for the changes to take effect.

gitlab/gitlab-runner镜像被配置为在/etc/gitlab-runner/certs/ca.crt上查找受信的 SSL 证书,但可以使用-e "CA_CERTIFICATES_PATH=/DIR/CERT"来配置。

ca.crt文件复制到数据卷(或容器)上的certs目录中。
ca.crt文件应包含所有需要 gitlab-ci-multi-runner 信任的服务器根证书。启动时,gitlab-ci-multi-runner 容器将导入ca.crt文件,因此如果你的容器已经运行,则需重启生效。

Alpine Linux

你还可以使用替代的基于 Alpine Linux 的镜像,文件体积更小:

gitlab/gitlab-runner    latest              3e8077e209f5        13 hours ago        304.3 MB
gitlab/gitlab-runner    alpine              7c431ac8f30f        13 hours ago        25.98 MB

Alpine Linux image is designed to use only Docker as the method of spawning runners.

原本的 gitlab/gitlab-runner:latest 基于 Ubuntu 14.04 LTS。

SELinux

某些发行版(CentOS,RedHat,Fedora)默认使用 SELinux 来增强底层系统的安全性。

处理这种配置时必须特别小心。

  1. If you want to use Docker executor to run builds in containers you need to access the /var/run/docker.sock.
    However, if you have a SELinux in enforcing mode, you will see the Permission denied when accessing the /var/run/docker.sock.
    Install the selinux-dockersock and to resolve the issue: https://github.com/dpw/selinux-dockersock.

  2. Make sure that persistent directory is created on host: mkdir -p /srv/gitlab-runner/config.

  3. Run docker with :Z on volumes:

docker run -d --name gitlab-runner --restart always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /srv/gitlab-runner/config:/etc/gitlab-runner:Z \
  gitlab/gitlab-runner:latest

More information about the cause and resolution can be found here:
http://www.projectatomic.io/blog/2015/06/using-volumes-with-docker-can-cause-problems-with-selinux/

GitLab Runner Docker Executor

GitLab Runner

执行器(Executor )的比较

Shell 是最易于配置的执行器。构建中所需的依赖得你手工装在 Runner 所在机器上。

更好的方式是使用 Docker,它让你拥有干净的构建环境,以及简易的依赖管理——所有的编译项目所需的依赖都可以放进 Docker 镜像中。Docker 执行器很容易就能创建带有依赖服务的编译环境,比如 MySQL。

通常不建议使用 Docker-SSH,这是 Docker 执行器的特殊版本。该执行器可以连接到 Docker 容器,其中运行 SSH 守护进程。如果你的 Docker 镜像要复制一个完整的工作系统,这会很有用:使用进程管理系统(init),暴露 SSH 进程,并含有系列安装好的服务。这类镜像是肥镜像,Docker 社区通常也不建议使用。

以下原文:https://docs.gitlab.com/runner/executors/docker.html

Docker 执行器

GitLab Runner 能在用户提供的镜像上,使用 Docker 来执行编译。这通过 Docker 执行器完成。

Docker 执行器与 GitLab CI 一起使用时,它连到 Docker Engine,并在独立且隔离的容器中运行每个构建。它会使用在 .gitlab-ci.yml中预设好的镜像,并按照 config.toml 所定来执行。

这样可以拥有一个简单可重复的构建环境,这也可以在你的工作站上运行。附加的好处是可以测试我们将在以后从 Shell 中探索的所有命令,而不必在专用的 CI 服务器上进行测试。

工作流程

Docker 执行器将构建分为多个步骤:

  1. Prepare:创建并启动服务。
  2. Pre-build:克隆,恢复 Cache 并从以前的阶段下载工件(Artifacts)。这是在特殊的 Docker 镜像上运行的。
  3. Build:用户构建。这是在用户提供的 Docker 镜像上运行的。
  4. Post-build:创建缓存,将工件上传到 GitLab。这是在特殊的 Docker 镜像上运行的。

此特殊的 Docker 镜像基于 Alpine Linux,并包含运行 prepare 步骤所需的所有工具:Git 二进制文件和用于支持 Cache 和工件(Artifacts)的 Runner 二进制文件。 您可以在官方的 Runner 仓库中找到此特殊镜像的定义。

image 关键字

image 关键字是本地 Docker Engine 中存在的 Docker 镜像的名称(docker image 可列出所有docker 镜像),或是可在 Docker Hub 中找到的任何镜像。有关镜像和 Docker Hub 的更多信息,请阅读 Docker Fundamentals文档。

简言之,使用image引用 docker 镜像来创建容器,构建将在此容器上运行。

如果不指定命名空间,Docker 默认命名空间为包含所有官方镜像library。这就是为什么你会看到很多次.gitlab-ci.ymlconfig.toml中省略的library部分。例如,可以定义像image: ruby:2.1这样的镜像,它是image: library/ruby:2.1的快捷方式。

然后,每个 Docker 镜像都有标签(tag),表示镜像版本。这些在镜像名称后面用冒号(:)定义。 例如,对于Ruby,可以在 https://hub.docker.com/_/ruby/ 上看到支持的标签。如果没有指定标签(如image:ruby),则表示latest

services 关键词

services关键字定义了在构建期间运行的另一个 Docker 镜像,并链接到image关键字所定义的 Docker 镜像。这允许你在构建时访问服务镜像。

服务镜像可以运行任何应用程序,但最常见的使用场景是运行数据库容器,例如mysql。用已有的镜像来运行额外容器会更加容易快速,而不是每次构建项目时都安装mysql

你可以在 CI 服务示例的相关文档中看到一些广泛使用的服务示例。

服务如何链接到构建

要更好地了解容器链接的工作原理,请阅读 Linking containers together

总而言之,如果你将mysql作为服务添加到应用程序中,则该镜像将用于创建一个链接到构建容器的容器。根据工作流程,这是运行实际构建之前执行的第一步。

MySQL 的服务容器可使用主机名mysql访问。因此,为了访问你的数据库服务,您必须连接到名为mysql的主机而不是套接字(socket)或localhost

.gitlab-ci.yml定义imageservices

你可以简单地定义将用于所有作业的镜像,和要在构建时使用的服务列表。

image: ruby:2.2

services:
  - postgres:9.3

before_script:
  - bundle install

test:
  script:
  - bundle exec rake spec

也可以为每个作业定义不同的镜像和服务:

before_script:
  - bundle install

test:2.1:
  image: ruby:2.1
  services:
  - postgres:9.3
  script:
  - bundle exec rake spec

test:2.2:
  image: ruby:2.2
  services:
  - postgres:9.4
  script:
  - bundle exec rake spec

Define image and services in config.toml

查找[runners.docker]部分:

[runners.docker]
  image = "ruby:2.1"
  services = ["mysql:latest", "postgres:latest"]

这里定义的镜像和服务将添加到 Runner 运行的所有构建中,因此即使在.gitlab-ci.yml内没有定义imageconfig.toml中定义的将被使用。

从私有 Docker Registry 中定义镜像

从 GitLab Runner 0.6.0 开始,你可以定义位于私有 Registry 的镜像,这可能需要身份验证。

.gitlab-ci.yml的镜像定义中显式声明即可。

image: my.registry.tld:5000/namepace/image:tag

上例中,GitLab Runner 到my.registry.tld:5000寻找namespace/image:tag镜像。

如果存储库是私有的,则需要提供 GitLab Runner 的身份验证凭据 。更多内容参见:使用私有 Docker Registry

访问服务

假设需要一个 WordPress 实例,来测试它和你应用的 API 集成。

你可以在.gitlab-ci.yml中使用如tutum/wordpress作为服务镜像:

services:
  - tutum/wordpress:latest

当构建运行时,tutum/wordpress将首先启动,你可以用主机名为tutum__wordpresstutum-wordpress的构建容器来访问它。

GitLab Runner 为你可以使用的服务创建两个主机名别名。别名取自镜像名称,遵循以下规则:

  1. :后的一切都被舍弃
  2. 第一个别名,斜杠(/)替换为双下划线(__
  3. 第二个别名,斜杠(/)替换为单破折号(-

使用私有镜像服务将剥离给出的任何端口,并应用上述规则。服务registry.gitlab-wp.com:4999/tutum/wordpress将生成主机名registry.gitlab__wp.com__tutum__wordpressregistry.gitlab-wp.com-tutum-wordpress

配置服务

许多服务接受环境变量,允许你根据环境来轻松更改数据库名或设置帐户名。

GitLab Runner 0.5.0 及以上版本将所有 YAML 定义的变量传递给创建的服务容器。

对于所有可能的配置变量,请检查其对应Docker集线器页面中提供的每个映像的文档。

所有变量将被传递到所有服务容器。没有某服务专用的变量。

安全变量(Secure Variables)只传递给构建容器。

服务中的构建目录

从 1.5 版本开始,GitLab Runner 将一个/build目录装载到所有的共享服务中。

参见这个 issue:https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/issues/1520

PostgreSQL 服务示例

请参阅具体文档使用 PostgreSQL 作为服务

MySQL 服务示例

请参阅具体文档使用 MySQL 作为服务

服务健康检查

服务启动后,GitLab Runner 等待一段时间的服务响应。当前,Docker 执行程序会尝试打开与服务容器中第一个暴露服务的 TCP 连接。

你可以看到它 在这个 Dockerfile 中是如何实现的。

构建和缓存存储

Docker 执行器默认将所有构建存储在/builds/<namespace>/<project-name>中,所有缓存在/cache(在容器内)中。

您可以在config.toml文件的[[runners]]部分下,定义builds_dircache_dir选项,来覆盖/builds/cache目录。这将修改数据存储在容器内的位置。

如果你修改/cache存储路径,你还需要更改config.toml[runners.docker]部分的volumes = ["/my/cache/"],确保把上述目录标记为 persistent。

要了解更多信息,请阅读下一部分持久存储。

持久存储

Docker执行器可以在运行容器时提供持久存储。
volumes =下定义的所有目录将在构建之间持久化。

volumes指令支持2种类型的存储:

  1. <path> - 动态存储<path>在该项目的同一并发作业的后续运行中持续存在。数据附加到自定义缓存容器:runner-<short-token>-project-<id>-concurrent-<job-id>-cache-<unique-id>
  2. <host-path>:<path>[:<mode>] - 主机绑定存储<path>绑定到主机系统上的<host-path>。可选的<mode>可以指定该存储是只读或读写(默认)。

构建的持久存储

如果你将/builds作为主机绑定的存储,你的构建将被存储在:/builds/<short-token>/<concurrent-id>/<namespace>/<project-name>,其中:

  • <short-token>是 Runner Token 的缩写版(前 8 个字母)
  • <concurrent-id>是个唯一数字,用于标识项目上下文中特定 Runner 的本地作业 ID

特权(privileged)模式

Docker 执行器支持许多选项来微调构建容器。其中之一是privileged模式

使用具有特权模式的 docker-in-docker

配置的privileged标志被传递给构建容器和所有服务,从而允许轻松使用 docker-in-docker 方法。

首先,将您的 Runner(config.toml)配置为以privileged模式运行:

[[runners]]
  executor = "docker"
  [runners.docker]
    privileged = true

然后,使你的构建脚本(.gitlab-ci.yml)使用 docker-in-docker 容器:

image: docker:git
services:
  - docker:dind

build:
  script:
    - docker build -t my-image .
    - docker push my-image

ENTRYPOINT

Docker 执行器不会覆盖 Docker 镜像的ENTRYPOINT

这意味着,如果你的镜像定义了ENTRYPOINT,并且不允许使用CMD来运行脚本,那么该镜像将不能与 Docker 执行器一起使用。

使用ENTRYPOINT可以创建在自定义环境或安全模式下运行构建脚本的特殊 Docker 镜像。

您可能会想到创建一个使用不执行构建脚本的ENTRYPOINT的 Docker 镜像,但执行一组预定义的命令,例如从你的目录里构建 Docker 镜像。在这种情况下,您可以以特权模式运行构建容器,并使 Runner 的构建环境安全。

请考虑以下例子:

  1. 创建一个新的 Dockerfile:
    FROM docker:dind
    ADD / /entrypoint.sh
    ENTRYPOINT ["/bin/sh", "/entrypoint.sh"]
    
  2. 创建一个用作ENTRYPOINT的 bash 脚本(entrypoint.sh):
    #!/bin/sh
    
    dind docker daemon
        --host=unix:///var/run/docker.sock \
        --host=tcp://0.0.0.0:2375 \
        --storage-driver=vf &
    
    docker build -t "$BUILD_IMAGE" .
    docker push "$BUILD_IMAGE"
    
  3. 推送镜像到 Docker Registry。

  4. privileged模式下运行 Docker 执行器。在config.toml中定义:

    [[runners]]
      executor = "docker"
      [runners.docker]
        privileged = true
    
  5. 在你的项目中使用以下.gitlab-ci.yml
    variables:
      BUILD_IMAGE: my.image
    build:
      image: my/docker-build:image
      script:
      - Dummy Script
    

这只是一个例子。通过这种方法,是有无限可能的。

镜像拉取策略如何工作

当使用dockerdocker-sshdocker+machinedocker-ssh+machine执行器时,可以设置pull_policy参数来定义 Runner 在拉取 Docker 镜像时的工作方式(imageservices关键字)。

注:
如果没有为pull_policy参数设置任何值,则 Runner 将默认使用always拉取策略。

现在看看这些策略如何工作。

使用拉取策略never

never策略完全禁止镜像拉取。如果 Runner 的pull_policy参数设为never,那用户只能使用 Runner 运行的 docker 主机上已经手工拉取的镜像。

如果在本地找不到指定的镜像,则 Runner 将会使构建失败,错误类似于:

Pulling docker image local_image:latest ...
ERROR: Build failed: Error: image local_image:latest not found

何时使用此拉取策略?

如果您希望或需要完全控制 Runner 用户使用哪些镜像,则应使用此拉取策略。对于专用于只能使用特定镜像(在任何 registry 上都不公开)的项目的私有 Runner 来说,这是一个不错的选择。

何时不使用此拉取策略?

大部分 auto-scaled 的 Docker 执行器使用场景下,该拉取策略无法正常工作。由于自动伸缩的工作原理,只有在为特定云服务提供商使用预定义的云实例镜像时,never拉取策略才可用。镜像需要包含预装的 Docker Engine 和已使用镜像的本地副本。

使用拉取策略if-not-present

使用if-not-present拉取策略时,Runner 首先检查本地是否有该镜像。如果有则使用本地版本的镜像。否则 Runner 将尝试拉取镜像。

何时使用此拉取策略?

如果想使用远程 Registry 拉取的镜像,但当使用重型和很少更新的镜像时,又希望减少分析镜像层差异费时,这种拉取策略是一个不错的选择。在这种情况下,您需要一段时间才能从本地 Docker Engine 存储区手动删除镜像以强制更新镜像。

如果您需要只在本地构建和本地可用的镜像,那么这也是一个不错的选择,但另一方面,还需要允许从远程 Registry 中拉取镜像。

何时不使用此拉取策略?

如果你的构建使用经常更新并需要在最新版本中使用的镜像,则不应使用此拉取策略。在此情况下,策略所减少的网络负载,与非常频繁地删除本地镜像副本的必要性相比,可能不太值得。(In such situation, the network load reduction created by this policy may be less worthy than the necessity of the very frequent deletion of local copies of images.)

如果 Runner 可以被不同用户使用,而这些用户又不能访问彼此使用的私有镜像,那么此拉取策略也不应该被使用。特别不要为共享 Runner 使用这个拉取策略。

要了解为什么if-not-present拉取策略在与私有镜像一起使用时会产生安全问题,请阅读安全注意事项文档

使用拉取策略always

always拉取策略确保镜像始终被拉取。当使用always时,即使本地副本可用,Runner 也会尝试拉取镜像。如果未找到镜像,则构建将失败,并显示类似以下错误:

Pulling docker image registry.tld/my/image:latest ...
ERROR: Build failed: Error: image registry.tld/my/image:latest not found

注:
对于v1.8之前的版本,当使用always拉取策略时,可能会返回(fall back)到镜像的本地副本并警告:

Pulling docker image registry.tld/my/image:latest ...
WARNING: Cannot pull the latest version of image registry.tld/my/image:latest : Error: image registry.tld/my/image:latest not found
WARNING: Locally found image will be used instead.

这在v1.8版本中已更改。To understand why we changed this and how incorrect usage of may be revealed please look into issue #1905

何时使用此拉取策略?

如果您的 Runner 公开可用,并将其配置为 GitLab 实例中的共享 Runner,则应使用此拉策略。当 Runner 将与私有镜像一起使用时,它是唯一可以被视为安全的拉取策略。

如果要强制用户始终使用最新的镜像,这也是一个不错的选择。

此外,这将是 Runner 自动伸缩(auto-scaled) 配置的最佳解决方案。

何时不使用此拉取策略?

如果你需要使用本地存储的镜像,则此拉取策略将无法正常工作。在这种情况下,Runner 将跳过镜像的本地副本,并尝试将其从远程 Registry 中拉出。如果镜像是本地构建的,并且不存在于任何公共 Registry 中(尤其是在默认的 Docker Registry 中),则构建将会失败:

Pulling docker image local_image:latest ...
ERROR: Build failed: Error: image local_image:latest not found

Docker vs Docker-SSH

注意
docker-ssh 执行器已被弃用,并且不会添加任何新功能。

我们提供一种特殊类型的 Docker 执行器的支持,即 Docker-SSH。Docker-SSH 使用与 Docker 执行器相同的逻辑,但不是直接执行脚本,它使用 SSH 客户端连接到构建容器。

然后,Docker-ssh 使用其内部 IP 连接到容器内部运行的 SSH 服务器。

GitLab Flow 的 11 条规则

使用 Git 进行版本管理,是对 Git 出现前所有方法的改进。然而,很多组织最终会出现凌乱的工作流程,或过于复杂的流程。这种问题对从其他版本控制系统转过来的组织来说尤为突出。

本文为 GitLab 工作流程制定了11条规则,以帮助简化和清晰该流程。规则的主要好处(或说希望的好处)是它简化过程,并产生更加有效和更清晰的结果。

改善空间永存,一切皆为草稿。一如既往,人人皆可贡献!欢迎反馈!

1. 使用功能分支,不直接提交(commit)到 master 分支

如果从 SVN 转过来,举例来说,你会习惯基于主干(trunk)的工作流。而使用 Git 时,任何正进行的操作,都应为之创建一个分支(branch),以便在最终合并(merge)之前进行代码审查(code review)。

2. 测试所有提交,而不仅只在 master 分支上

有些人将 CI 系统设置为只测试合并到 master 分支的东西。这太迟了!总是拥有绿色的 master 测试(译者注:绿色在 CI 里通常意味着测试通过,而红色意味着测试失败),人们会觉得有信心。例如,在开始开发新功能之前,人们不得不测试 master 分支那是没有意义和荒谬的。CI 并不昂贵,所以这样做是最好的。

3. 在所有的提交上,运行所有的测试(如果你的测试时间长于 5 分钟则使其并行)

如果功能分支有新的提交(commit),请随之运行测试。如果测试需要很长时间,请尝试并行运行。合并请求(merge requests)时,在服务器端来执行此操作以运行完整的测试套件。如果有一个用于开发的测试套件,而另一个测试套件只是为新版本运行,那么设置并行测试并运行全部测试套件是值得的。

4. 在合并到 master 之前执行代码审查,而不是事后审查

不要在一周结束时测试,应该当场就搞!这样更有可能抓住可能导致问题的东西,而其他人也会努力提出解决方案。

5. 部署是自动的,并基于分支或标签(tag)。

如果不想每次都部署 master 分支,那可以创建一个 production 分支;但没理由用脚本(script)或登录到某个地方去手动操作。请自动化一切,或用特定的分支来触发生产部署

6. 由用户创建标签(tag),而不应由 CI 创建

由用户来打标签tag),并基于此,CI 将执行一个动作。不应让 CI 去更改代码库。如需非常详细的指标,那应该有一个服务器报告来详细说明新版本。

7. 发布(release)是基于标签(tag)的

如果打了 tag,则意味着创建了一个新版本。

8. 永不对已推送的提交(pushed commits)进行变基(rebase

当推送(push)到公共分支后,就不该对其变基,因为这样会很难跟进你正在改进的内容,很难跟进测试结果,而且这样打破了 cherry-picking。有时我们在审查流程的后期要求代码贡献者合并及变基(git merge --squash),以使一些东西容易还原时,我们也会违背这一规则。但一般而言,准则是:代码应干净,修改历史应真实。

9. 每个人都从 master 分支开始工作,目标也是 master 分支

这意味着没有任何长期分支。你可以检出 master 分支,构建功能,创建合并请求,并再次指向到 master 分支。在合并之前要做完整的代码审查,不应在代码审查和合并间搞任何的中间阶段。

10. 在 master 分支中修正错误,其次再到发布分支

如果发现了 bug,最糟糕的事莫过于在刚发布的版本里修复了它,但却没在 master 里修复。为避免这种情况,应总是向前修复(fix forward)。在 master 中修复,然后 cherry-pick 到其他补丁发布分支(比如 production 分支)。

11. 提交信息(commit message)应体现意图

不仅要说清楚做了什么,还要说明为什么这么做。
如果解释一下为什么用这种方式,而不是其他方式,则会更加有用。

更多内容请移步 GitLab Flow 文档。

原文:https://about.gitlab.com/2016/07/27/the-11-rules-of-gitlab-flow/