首页 > 解决方案 > 在远程 docker swarm 集群上运行手动 GitLab CI 作业时遇到问题

问题描述

这里有一些背景:我有一个在 DigitalOcean Droplet 上运行的 Django 应用程序,预装了 Docker 19.03.1~3。我正在运行一个单节点 swarm 集群,并且该应用程序在大多数情况下运行良好。

这是我遇到的问题:我正在尝试将一些管理命令放入手动触发的 GitLab CI 作业中,因此我不必通过 SSH 连接到 Droplet 并运行命令(migratecollectstatic)。当 SSH 进入 Droplet 时,我可以成功运行这些命令。例如,要运行,collectstatic我会查找 Django 容器 ( e710394e5449) 的容器 ID,然后运行:

docker exec -it e710394e5449 python3 manage.py collectstatic

要运行管理命令,我想我可以这样做:

docker exec $(docker ps -q -f name="backend") python3 manage.py collectstatic --no-input

这是我在 GitLab CI 作业日志中看到的错误:

error during connect: Get http://docker/v1.40/containers/e710394e5449/json: command [ssh -l root 123.45.6.789 -- docker system dial-stdio] has exited with exit status 255, please make sure the URL is valid, and Docker 18.09 or later is installed on the remote host: stderr=ssh: connect to host 123.45.6.789 port 22: Connection refused

你可以docker exec从远程 docker 主机上的 GitLab CI 作业运行吗?我能够获取容器 ID ( $(docker ps -q -f name="backend")),但外部docker exec命令失败。 docker exec $(docker ps -q -f name="backend") python3 manage.py collectstatic --no-input我可以毫无问题地从我的笔记本电脑上运行命令 ( )。

这是我的.gitlab-ci.yml文件。该django-collectstatic作业对于我如何运行命令有两种选择:一种用于docker exec在现有容器中运行命令,另一种使用docker run. 这些选项都不起作用,这只是为了展示我尝试过的内容。

stages:
  - build
  - deploy
  - management

workflow:
  rules:
    - if: '$CI_COMMIT_BRANCH == "master"'
      when: always

image: docker:19.03.1
services:
  - docker:19.03.5-dind

.add-ssh-key: &add-ssh-key
  before_script:
    - apk update && apk add openssh-client bash
    - mkdir -p ~/.ssh
    - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
    - chmod 600 ~/.ssh/id_rsa
    - eval "$(ssh-agent -s)"
    - ssh-add ~/.ssh/id_rsa
    - ssh-keyscan -H $DROPLET_IP >> ~/.ssh/known_hosts
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY

build-backend:
  stage: build
  before_script:
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
  script:
    - |
      docker build \
        -t $CI_REGISTRY_IMAGE/backend:$CI_COMMIT_SHORT_SHA \
        -f backend/docker/Dockerfile.prod \
        ./backend/
    - docker push $CI_REGISTRY_IMAGE/backend:$CI_COMMIT_SHORT_SHA

build-nginx:
  stage: build
  before_script:
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
  script:
    - |
      docker build \
        -t $CI_REGISTRY_IMAGE/nginx:$CI_COMMIT_SHORT_SHA \
        -f nginx/prod/Dockerfile \
        .
    - docker push $CI_REGISTRY_IMAGE/nginx:$CI_COMMIT_SHORT_SHA

docker-stack-deploy:
  <<: *add-ssh-key
  rules:
    - if: '$CI_COMMIT_BRANCH == "master"'
      when: delayed
      start_in: 1 minute
  stage: deploy
  variables:
    DOCKER_HOST: "ssh://root@$DROPLET_IP"
  script:
    - docker stack deploy --with-registry-auth -c stack.yml my-stack

.task-base: &task-base
  <<: *add-ssh-key
  stage: management
  variables:
    DOCKER_HOST: "ssh://root@$DROPLET_IP"
  rules:
    - when: manual

django-collectstatic:
  <<: *task-base
  script:
    # should I do this?
    - docker exec $(docker ps -q -f name="backend") python3 manage.py collectstatic --no-input
    # or this? or something else?
    # - |
    #   docker run \
    #     --rm \
    #     --network main \
    #     -e POSTGRES_PASSWORD=$POSTGRES_PASSWORD \
    #     -e SECRET_KEY=$SECRET_KEY \
    #     -e DEBUG=$DEBUG \
    #     -e DOMAIN_NAME=$DOMAIN_NAME \
    #     -v backendassets:/code/assets \
    #     $CI_REGISTRY_IMAGE/backend:$CI_COMMIT_SHORT_SHA \
    #     python3 manage.py collectstatic --no-input

django-migrate:
  <<: *task-base
  script:
    - docker exec $(docker ps -q -f name="backend") python3 manage.py migrate --no-input

django-createsuperuser:
  <<: *task-base
  script:
    - docker exec $(docker ps -q -f name="backend") python3 manage.py createsuperuser --no-input

请让我知道我是否可以提供任何其他信息来帮助澄清我的问题。

标签: dockersshgitlabgitlab-cidocker-swarm

解决方案


目前,我认为最简单的方法是直接通过 SSH 运行 docker 命令,而不是使用DOCKER_HOST环境变量连接我的 docker CLI(在 GitLab CI 中)。

这最终看起来像这样:

django-migrate:
  before_script:
    - apk update && apk add openssh-client bash
    - mkdir -p ~/.ssh
    - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
    - chmod 600 ~/.ssh/id_rsa
    - eval "$(ssh-agent -s)"
    - ssh-add ~/.ssh/id_rsa
    - ssh-keyscan -H $DROPLET_IP >> ~/.ssh/known_hosts
  stage: management
  rules:
    - when: manual
  script:
    - ssh root@$DROPLET_IP \
      'docker exec $(docker ps -q -f name="backend") python3 manage.py migrate --no-input'

无论出于何种原因,运行docker exec $(docker ps -q -f name="backend") python3 manage.py migrate --no-input都会失败。我将能够获取容器 ID(该docker ps命令将成功完成,但该docker exec命令将因连接被拒绝而失败。

我不确定通过 SSH 连接运行 docker 命令的安全性。

该命令的输出确实很好地显示在 GitLab CI 作业的日志中,这正是我想要的。


推荐阅读