【问题标题】:blue/green deployment to portainer using gitlab CI/CD使用 gitlab CI/CD 将蓝/绿部署到搬运工
【发布时间】:2022-01-01 17:54:59
【问题描述】:

我有使用 websockets 的 webservice,需要实现零停机部署。因为我不想在部署时删除现有连接,所以我决定实施蓝/绿部署。我的实际解决方案如下:

  1. 我在 portainer 中创建了两个相同的服务,监听不同的端口。每个服务都在节点环境中设置了一些标识符,例如 alfabeta
  2. 这两个服务都隐藏在负载均衡器后面,并且均衡器会定期检查每个服务的状态。如果服务在特定路由(/balancer-keepalive-check)上响应字符串“OK”,则该服务处于活动状态,并且平衡器可以路由到该服务。如果服务响应字符串“STOP”,平衡器将此服务标记为不可访问,但将保留活动连接
  3. 哪个服务处于活动状态,哪个服务已停止通过 redis 同步。在 redis 中有键 lb.service.alfalb.service.beta 可以包含值 1 表示活动和 0 表示非活动。 Nestjs中实现/balancer-keepalive-check路由的例子:
    import {Controller, Get} from '@nestjs/common';
    import {RedisClient} from "redis";
    const { promisify } = require("util");
    
    
    @Controller()
    export class AppController {
    
        private redisClient = new RedisClient({host: process.env.REDIS_HOST});
        private serviceId:string = process.env.ID;  //alfa, beta
    
        @Get('balancer-keepalive-check')
        async balancerCheckAlive(): Promise<string> {
            const getAsync = promisify(this.redisClient.get).bind(this.redisClient);
            return getAsync(`lb-status-${this.serviceId}`).then(status => {
                const reply: string = status == 1 ? 'OK' : 'STOP';
                return `<response>${reply}</response>`;
            })
        }
    }
  1. 在 gitlab CI 中,在提交时创建按标签标记的 docker 映像,并重新启动服务调用 portainer webhook 以获取特定服务。这适用于 1 个服务,但不知道如何使用 2 个不同的 DEPLOY_WEBHOOK CI 变量并在它们之间切换。
image: registry.rassk.work/pokec/pokec-nodejs-build-image:p1.0.1
services:
  - name: docker:dind

variables:
  DOCKER_TAG: platform-websocket:$CI_COMMIT_TAG

deploy:
  tags:
    - dtm-builder
  environment:
    name: $CI_COMMIT_TAG
  script:
    - npm set registry http://some-private-npm-registry-url.sk
    - if [ "$ENV_CONFIG" ]; then cp $ENV_CONFIG $PWD/.env; fi
    - if [ "$PRIVATE_KEY" ]; then cp $PRIVATE_KEY $PWD/privateKey.pem; fi
    - if [ "$PUBLIC_KEY" ]; then cp $PUBLIC_KEY $PWD/publicKey.pem; fi
    - docker build -t $DOCKER_TAG .
    - docker tag $DOCKER_TAG registry.rassk.work/community/$DOCKER_TAG
    - docker push registry.rassk.work/community/$DOCKER_TAG
    - curl --request POST $DEPLOY_WEBHOOK
  only:
    - tags

我不知道如何解决的问题是

  • 当我有 2 个服务时,我有 2 个不同的部署 webhook,我需要在部署后调用其中一个,因为我不想重新启动这两个服务。如何确定是哪一个?如果此部署是针对“alfa”或“beta”服务,如何实现某种计数器?每次部署后我应该使用gitlab api and updateDEPLOY_WEBHOOK 吗?或者我应该摆脱这个 gitlab CI/CD 变量并在服务上使用一些 API 来告诉我 webhook url?
  • 如何更新redis中的值?我应该为此实现自定义 API 吗?
  • 是否有更好的方法来实现这一目标?

附加信息:无法使用服务中的 gitlab api,因为我们的 gitlab 是自托管在只能从我们的专用网络访问的域上。

【问题讨论】:

  • 1.阅读负载均衡器,无需发明轮子。 2.如果您坚持自己实施,例如因为只有你有一个遗留系统,其中只允许 1 个实例处于活动状态,最好在 Redis 中保存一个处于活动状态的值而不是多个标志,以减少竞争条件并使用键上的 TTL 来确保一个已启动例如implementation here
  • 这与负载平衡器无关。这部分我有工作。试着再读一遍我的问题。这是关于从gitlab CI/CD部署后如何找到需要重启的服务

标签: architecture microservices gitlab-ci continuous-deployment blue-green-deployment


【解决方案1】:

我已经修改了我的 AppController。现在有 2 个新的端点,一个用于识别正在运行的服务,第二个用于 redis 中的开关值:

private serviceId:string = process.env.ID || 'alfa';

    @Get('running-service-id')
    info(){
        return this.serviceId
    }

    @Get('switch')
    switch(){
        const play = this.serviceId == 'alfa' ? `lb-status-beta` : `lb-status-alfa`;
        const stop = `lb-status-${this.serviceId}`;
        this.redisClient.set(play, '1', (err) => {
            if(!err){
                this.redisClient.set(stop, '0');
            }
        })
    }

之后,我修改了我的 gitlab-ci.yml 如下:

image: registry.rassk.work/pokec/pokec-nodejs-build-image:p1.0.1
services:
  - name: docker:dind

stages:
  - build
  - deploy
  - switch

variables:
  DOCKER_TAG: platform-websocket:$CI_COMMIT_TAG

test:
  stage: build
  allow_failure: true
  tags:
    - dtm-builder
  script:
    - npm set registry http://some-private-npm-registry-url.sk
    - npm install
    - npm run test

build:
  stage: build
  tags:
    - dtm-builder
  environment:
    name: $CI_COMMIT_TAG
  script:
    - if [ "$ENV_CONFIG" ]; then cp $ENV_CONFIG $PWD/.env; fi
    - if [ "$PRIVATE_KEY" ]; then cp $PRIVATE_KEY $PWD/privateKey.pem; fi
    - if [ "$PUBLIC_KEY" ]; then cp $PUBLIC_KEY $PWD/publicKey.pem; fi
    - docker build -t $DOCKER_TAG .
    - docker tag $DOCKER_TAG registry.rassk.work/community/$DOCKER_TAG
    - docker push registry.rassk.work/community/$DOCKER_TAG
  only:
    - tags

deploy:
  stage: deploy
  needs: [build, test]
  environment:
    name: $CI_COMMIT_TAG
  script:
    - 'SERVICE_RUNNING=$(curl --request GET http://172.17.101.125/running-service-id)'
    - echo $SERVICE_RUNNING
    - if [ "$SERVICE_RUNNING" == "1" ]; then curl --request POST $DEPLOY_WEBHOOK_2; fi
    - if [ "$SERVICE_RUNNING" == "2" ]; then curl --request POST $DEPLOY_WEBHOOK_1; fi
  only:
    - tags

switch:
  stage: switch
  needs: [deploy]
  environment:
    name: $CI_COMMIT_TAG
  script:
    - sleep 10
    - curl --request GET http://172.17.101.125/switch
  only:
    - tags

在工作 build 中构建 docker 映像。之后运行作业 deploy,该作业向 /running-service-id 发出请求并识别正在运行的服务。然后将镜像部署到停止的服务。最后一个是job switch,它会向/switch 路由发出请求,会在redis 中切换值。

这很好用。我需要实现的最后一件事是这两条路线的某种秘密(例如 jwt)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-30
    • 2020-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    相关资源
    最近更新 更多