Is it possible to use runners to deploy a Testing branch to a testing server and a Stable branch to a production server or container?

We like to deploy new features to a testing server as part of our user acceptance testing process.

Is it possible to deploy our Testing branch to a testing server and our Stable branch to a production server?

Let’s come back around to tags and rules for a minute and talk about server setup. I think there might be two approaches? If there’s a third I’m all ears.

I think one approach is to have a runner on testing.example.com and a runner on example.com. That seems to suggest I would also require two different .gitlab-ci.yml files and some way to determine which one to run on which server. I haven’t seen any examples of this anywhere so I’m going to assume things are not done this way. I may have missed something.

Alternative option would be to put one runner on a third server such as runner.example.com that would then make use of tags and/or rules eg, if: '$CI_COMMIT_BRANCH == "testing"' to determine the location to deploy to. Which would then require some way of getting the built code from the runner server to the target server; possibly via ssh, possibly using a scp command? I’m not sure if I’m going down the right path here, but that would require me to restart the service on the testing server once the files are there and that’s getting into an area I’m not sure I can do without some serious bash scripting. For spinning up new containers this is totally different scenario.

Some other things I read online included putting a private SSH key into an environment variable on GitLab to facilitate SSH transfer via a runner, but I am not at all comfortable doing that. The same goes for anything FTP related.

If I can only reliably use gitlab-runner to deploy to one or the other, that’s fine. I can manually handle production or use a different tool stack, whatever. If there’s another way to do this for both I’m happy to learn. Trying to grok an elephant isn’t easy. I do have a single runner up and working, but as soon as I try to use tags or rules I get the usual no runners online error. Not yet sure what’s up with that, but I’m sure it can be solved. This testing server situation is warping my brain a little.

Thanks in advance.

I just discovered environments. I think this might be the 3rd option?

So in the .gitlab-ci.yml I can tell a job to run if it’s in a certain environment, eg. environment: testing

I would expect then that I would have to tell a runner what environment it is. Not sure how to go about that? I’m guessing that would be in the config.toml yes?

Information relating to this step appears to be missing on the page, Environments and deployments and I think this is an important piece of the puzzle, if I’m right. If I’m wrong then I’m chasing another wild goose.

Nope. That’s not at all what I thought it was.

It’s hard to find the best solution for you without knowing more about your environments and software stack.

I’m going to explain what we do for a project, and then you can see if something is useful for you too.

We have two different AWS accounts, one for testing, one for production.

In each of them, we have a runner that deploys the infrastructure: in this way, we can use IAM profiles on the runners without having to share credentials around.

On Gitlab CI, we have two different sets of jobs: one for production, and one testing. We use the if: '$CI_COMMIT_BRANCH == "testing" approach to choose which ones we want to run.

To choose the target runner, we use the runners tags: https://docs.gitlab.com/ee/ci/runners/#use-tags-to-limit-the-number-of-jobs-using-the-runner. In this way the runner in the testing environment picks up only the jobs tagged with “testing” that have the '$CI_COMMIT_BRANCH == “testing”` rule.

To do not duplicate too much code in Gitlab CI, we use the “extends” keyword

1 Like

I can see that working, but if both runners have the same gitlab-ci.yml file, they would both pass the same if check. Do you have multiple yml files? If so, how do you manage them or more to the point how does one runner know to pick one file over the other? Are they excluded from the repository and manually installed / maintained somehow? Something like a .env with a .gitignore entry?

We have several servers, all are droplets on Digital Ocean. Most of them provide different services from different code bases with different repositories. Each are different micro services, except the main site, which is a monolith containing front and backend code together. (They’re easy to separate if need be). I am able to set most of them up to build, test and deploy. When it comes to having our testing branch built and deployed to testing server and the stable branch deployed to production, I’m not sure how to make that happen from a single repository. Single repo to single server, no problem. Single repo to multiple servers, problem.

The if: '$CI_COMMIT_BRANCH == "testing" seems like it’s part of the solution, but if I put that in a single gitlab-ci.yml file that file will get sent to both servers. Both will check and both pass in both cases. Both branches would then be deployed to both servers by both runners.

Maybe I’m not asking the right question or I’m thinking about this backwards? I don’t see how the connection is made from a branch (ie. testing vs stable) to a runner on a server (ie. testing vs production) using only a single .gitlab-ci.yml file? There must be a way to check which environment or server or host the runner is running on? I think that’s my hurdle. How do I tell a runner which server or host (environment?) it’s representing? I would think the total solution would look something like this pseudo code, if: '$ENV == "production" && $CI_COMMIT_BRANCH == "stable" perhaps?

Though that would require a runner to accept a config somehow. Which brought me around to the whole concept of environment that I found in the docs. Which I was thinking paralleled the whole .env file thing.

I was trying to figure out how $ENV (or whatever it might be) could get to the runner. I looked into config.toml. That’s got to be the way, or at least a file like that would be intended to play a similar role, yes? That approach seems to work for other software with config files in /etc. I tried working with environment = "testing", but that didn’t have the effect I was hoping for.

Perhaps I’m looking at this the wrong way?

You need to tag the runners: https://docs.gitlab.com/ee/ci/runners/#use-tags-to-limit-the-number-of-jobs-using-the-runner, and then instruct the runners to run only the jobs tagged with the same runner.

You will have a runner in testing, with a tag testing, and a job like this:

testing:
  script:
    - this is run only by the runner in the testing environment
  if:  '$CI_COMMIT_BRANCH == "testing"'
  tags:
    - testing

Then for the production environment you will have another runner, with the tag “production”, and a job like

production:
  script:
    - this is run only by the runner in the production environment
  if:  '$CI_COMMIT_BRANCH == "production"'
  tags:
    - production

but if both runners have the same gitlab-ci.yml file,

The job is sent only to the runner with the appropriate tag!

Once I finally figured out that GitLab CI tags and Git tags are not related entities, things started to make more sense.

I thank you for your answer! That does connect the dots.

1 Like

For future reference the syntax works like this…

lintWebTesting:
  stage: lintFront
  script:
    - cd web
    - touch package-lock.json
    - rm package-lock.json
    - npm config set loglevel error
    - npm install
  rules:
    - if: '$CI_COMMIT_BRANCH == "testing"'
  tags:
    - testing