Lab Manual CICD
Overview
At this point, its assumed that you've successfully:
- Setup and started a DNS, CA, HTTPS Service, Gitea, and a Gitea Runner.
- Setup, built, and deployed a Docusaurus instance via a Git Repo and Git Docker Registry.
With the runner up and running, our goal will be to setup our repository such that when we push updates to the deploy branch of its Git repo, it'll automatically build and deploy the site to the production server. Its recommended to use a non-main branch for this kind of behavior to discourage folks from being afraid of pushing updates and potentially messing up a deployed build. Developers should always be comfortable to commit, push, and then allow some brain rest before making the commitment to deploy the code.
The plan is to trigger an action when a push event occurs on the deploy branch. Our action will then:
- Build the static-site docker image from the gitea_sys_runner.
- Push the static-site docker image to the central Gitea Docker Registry.
- Pull the static-site docker image to the production server (via SSH).
- Restart the static-site docker image on the production server (via SSH).
Note: Since GitOps controls the configuration above the baseline system, it is accepted that some repositories will contain credentials or keys required for system automation. These configuration specific repositories are kept separate from the source code repositories. (By convention, all repositories that contain this kind of configuration information will be suffixed with -config to indicate their sensitivity and machine specific nature).
Precondition For CICD Access
For our CICD process to succeed, we need to create a special user that it can operate as. By convention, I use cicd as the username (often with the UID 1000) for this purpose.
In the Gitea Runner:
- rootlessuser should have access to the private SSH key for- cicdso that it can login to the production server (as- cicd) to perform relevant operations.
In Gitea:
- Must have a cicduser with it's public SSH key registered for access.
- The cicduser should be given membership and read-only access to all repositories you plan for it to build.
- The cicduser must have access to push container images into its Docker registry. This is done at the organization packages level and may require creating a CICD team to give it membership of.
- The cicdGitea login username/password must be included as Action-Runner secrets at the organization level.
In the production server:
- The cicduser must exist and have SSH keys enabled as well as it's public key within theauthorized_keysSSH configuration.
- The cicduser must have thedocker loginperformed and saved within its profile.
- The cicduser must be in thedockergroup.
- The cicduser must have sufficientsudopermissions to accomplish its required actions.
Note: The cicd private and public keys as well as its username and password would be a good thing to store in the Vaultwarden for safe keeping and management.
The Action Descriptor
Action descriptors are complex yaml's that ultimately are shell scripts with a set of conditions. You can read more about them in GitHub documentation. (Most GitHub action fields are compatible with Gitea.)
Create the following yaml in the file path .gitea/workflows/build-app.yml from the top of the project folder (i.e. /opt/manuals/system_manual). Gitea will read all of the yaml files in ./gitea/workflows, so you can really name the yaml file itself whatever you want.
name: manuals
run-name: ${{ gitea.actor }} is building lab/manuals image.
on:
  push: 
    branches: [deploy]
jobs:
  build-oci:
    runs-on: [system]
    steps:
    - name: Check out repository code
      uses: https://git.lab/actions/checkout@v4
    
    - name: Checkout lab_services-config for rollout script
      run: |
        NOKNOWNHOSTS="-o UserKnownHostsFile=/dev/null" \
        NOHOSTCHECKS="-o StrictHostKeyChecking=no" \
        GIT_SSH_COMMAND="ssh $NOKNOWNHOSTS $NOHOSTCHECKS" \
        git clone --depth 1 [email protected]:lab/lab_services-config.git \
          --branch deploy --single-branch lab_services-config
    - name: Dump environment variables
      run: env
    
    - name: Login to Gitea Docker Registry
      uses: https://git.lab/docker/login-action@v3
      with:
        registry: git.lab
        username: ${{ secrets.GITEADOCKER_USERNAME }}
        password: ${{ secrets.GITEADOCKER_TOKEN }}
    - name: Build system_manual image
      run: ./do cicd
    
    # Assumed that /opt/services is pre-created and owned by cicd.
    # Assumed that cicd has run docker login on destination machine.
    - name: Deploy system_manual image
      run: |
        ls && \
        ssh -p 2222 -o StrictHostKeyChecking=no [email protected] \
          /bin/sh < lab_services-config/rollout.sh
In the above descriptor:
- We're triggered on pushevent todeploybranch.
- We checkout the relevant code into the working directory with the uses: https://git.lab/actions/checkoutline. I suggest trying not to worry what the working directory actually is. Instead work relative to whatever it happens to be.
- Shallow clone lab_service-configrepo to use itsrollout.shscript. The above example ignores host key checking, which is not the best for security. If possible, its better to use-o StrictHostKeyChecking=accept-new.
- From the working directory, dump the environment variable for logging and troubleshooting purposes.
- Utilize the docker/login-actionaction to login to our Docker registry. Note: This is not the same as logging into our Docker registry in our production server (as used in the final step). This only logs in for things running from the runner service, and is generally used so that we candocker pushour final product.
- Run our ./doscript'scicdtarget. If you recall, this is equivalent to./do build && ./do pushin a single command.
- Finally, we login to the production server then pull and deploy the image via SSH on the production server. (Note: If there was more equity at stake, you'd want to add some unit testing between the build and the deployment.) We depend on our assumption that lab_services-configrollout.sh script automatically pulls updated images and starts them up.
The Current Workflow
So now the workflow is:
- Clone or checkout the mainbranch of the system manual on a developer machine.
- Start the author container to see changes to the Markdown live with ./do start.
- Make Markdown changes as desired.
- Commit & push changes to mainbranch andorigin.
- Merge changes to local and upstream deploybranch via./do deploy.
- Wait ~5 minutes and verify changes are live. Optionally watch the progress via the project's Actions menu in Gitea.
- Rinse and Repeat as required.