github.com/GoogleContainerTools/skaffold/v2@v2.13.2/docs-v1/content/en/docs/tutorials/ci_cd.md (about)

     1  ---
     2  title: "Using Skaffold for CI/CD with GitLab"
     3  linkTitle: "Skaffold in CI/CD"
     4  weight: 100
     5  ---
     6  Contributed by: [@tvvignesh](https://github.com/tvvignesh)
     7  
     8  Skaffold is a tool which is non-opinionated about the CI/CD tool you should use and acts as a platform agnostic solution for both development and deploying to production.
     9  
    10  To facilitate building and deploying images in CI/CD, skaffold offers docker images which you can build on top of. If that doesn’t work for your use-case, you can make your own Dockerfile and pull in skaffold and its dependencies using curl.
    11  
    12  Let us have a look at how we can use Skaffold with GitLab CI.
    13  
    14  ## Step 1: Getting the project and Dockerfile ready
    15  
    16  The first step is to obviously have your project which you want to deploy ready with a Dockerfile setup for the same. 
    17  
    18  Skaffold supports multiple builders like Docker, Kaniko, Bazel and more and if the builder you are looking for is not supported out of the box, you can [script it out](https://skaffold.dev/docs/tutorials/custom-builder/) as well. We will use Docker in this demonstration.
    19  
    20  For instance, if you would like to deploy nginx, your Dockerfile would look something like this:
    21  
    22  
    23  ```Dockerfile
    24  FROM nginxinc/nginx-unprivileged:1.19.3-alpine
    25  WORKDIR /app/server
    26  
    27  COPY ./web ./web
    28  
    29  EXPOSE 8080
    30  
    31  CMD [ "/bin/bash", "-c", "nginx -g 'daemon off;'" ]
    32  ```
    33  
    34  
    35  Skaffold can use your dockerfile to automatically build, tag and push images to the registry when needed.
    36  
    37  ## Step 2: Choosing the deployment method
    38  
    39  Skaffold supports deployment through kubectl, helm and a lot of other mechanisms. You can have a look at the complete list of deployers available [here](https://skaffold.dev/docs/pipeline-stages/deployers/).
    40  
    41  If you choose to deploy through kubectl, you must have all your yaml files ready, or if you would like to deploy through helm charts, you must have all your charts ready.
    42  
    43  ## Step 3: Choosing the image and tag strategy
    44  
    45  Skaffold automatically tags images based on different strategies as documented here: [https://skaffold.dev/docs/pipeline-stages/taggers/](https://skaffold.dev/docs/pipeline-stages/taggers/). The tagging strategy used is configurable, so choose the mechanism which is right for you. By default, skaffold uses the git sha tagger.
    46  
    47  If you are using helm as your deployer, you might want to use the helm image strategy if you would like to follow helm specific conventions, as skaffold will pass in all the details for you.
    48  
    49  ## Step 4: Plugins (optional)
    50  
    51  If you want to do some kind of processing before deployment like decrypting the secrets in the CI/CD pipeline, skaffold also supports plugins like [Helm Secrets](https://github.com/zendesk/helm-secrets/). So, if you would like to deploy secrets that have been encrypted using KMS or any other mechanism supported by [SOPS](https://github.com/mozilla/sops) you can actually set **useHelmSecrets** option to true and skaffold will handle everything automatically for you.
    52  
    53  ## Step 5: The `skaffold.yaml` file
    54  
    55  Your `skaffold.yaml` file will change depending on the way you would like to build and deploy your project. It is recommended that you set up skaffold and run it manually before you setup your CI pipeline since the CI pipeline can just call skaffold to do all the builds and deployments which can be tested out locally via skaffold.
    56  
    57  A sample skaffold file can look something like this:
    58  
    59  
    60  ```yml
    61  apiVersion: skaffold/v2beta8
    62  kind: Config
    63  profiles:
    64    # Specify the profile name which you can run later using skaffold run -p <profilename>
    65    - name: my-profile-name
    66      build:
    67        artifacts:
    68          # Skaffold will use this as your image name and push it here after building
    69          - image: asia.gcr.io/my-project/my-image
    70            # We are using Docker as our builder here
    71            docker:
    72              # Pass the args we want to Docker during build
    73              buildArgs:
    74                NPM_REGISTRY: '{{.NPM_REGISTRY}}'
    75                NPM_TOKEN: '{{.NPM_TOKEN}}'
    76      deploy:
    77        # Using Helm as the deployment strategy
    78        helm:
    79          # Pass the parameters according to https://skaffold.dev/docs/references/yaml/
    80          releases:
    81            - name: my-release
    82              namespace: default
    83              # Using Helm secrets plugin to process secrets before deploying
    84              useHelmSecrets: true
    85              # Location of the chart - here, we use a local chart
    86              chartPath: ./charts/my-chart
    87              # Path to the values file
    88              valuesFiles:
    89                - ./deployment/dev/values.yaml
    90                - ./deployment/dev/secrets.yaml
    91              skipBuildDependencies: true
    92          flags:
    93            upgrade:
    94              - --install
    95  ```
    96  
    97  Please have a look at the comments above to understand what the yaml does.
    98    
    99  Rather, if you want to deploy via kubectl, your skaffold file can look something like this:
   100  
   101  
   102  ```yml
   103  apiVersion: skaffold/v2beta8
   104  kind: Config
   105  profiles:
   106      # Specify the profile name which you can run later using skaffold run -p <profilename>
   107      - name: dev-svc
   108        build:
   109            artifacts:
   110                # Skaffold will use this as your image name and push it here after building
   111                - image: asia.gcr.io/my-project/my-image
   112                  # We are using Docker as our builder here
   113                  docker:
   114                      # Pass the args you want to Docker during build
   115                      buildArgs:
   116                          # Pass the args we want to Docker during build
   117                          NPM_REGISTRY: '{{.NPM_REGISTRY}}'
   118                          NPM_TOKEN: '{{.NPM_TOKEN}}'
   119        deploy:
   120            # In case we enable status check, this will be the timeout till which skaffold will wait
   121            statusCheckDeadlineSeconds: 600
   122            # Using kubectl as our deployer
   123            kubectl:
   124                # Location to our yaml files
   125                # Refer https://skaffold.dev/docs/references/yaml/ for more options
   126                manifests:
   127                    - k8/dev/*.yml
   128  ```
   129  
   130  ## Step 6: Development & Testing locally
   131  
   132  Before we move on to setting up the CI pipeline, we should test it out locally. Use `skaffold run` if you are deploying it to the cluster, `skaffold build` to just build and push the artifacts, `skaffold dev` if you are developing (this will enable auto reload on changes) or debug using `skaffold debug`
   133  
   134  You can find docs about all these workflows [here](https://skaffold.dev/docs/workflows/)
   135  
   136  Also, note that you can also set up file synchronization to enable faster development workflows. You can read more about that [here](https://skaffold.dev/docs/pipeline-stages/filesync/).
   137  
   138  Enabling file synchronization would avoid the need to rebuild the images repeatedly during development and testing and this can accelerate the inner dev loop.
   139  
   140  ## Step 7: Setting up authentication with the registry
   141  
   142  As you may already know, there are a lot of places where you can host images namely [Docker Hub](https://hub.docker.com/), [GCR](https://cloud.google.com/container-registry), [GitLab Registry](https://docs.gitlab.com/ee/user/packages/container_registry/), [Quay](https://quay.io/) and so on.
   143  
   144  The way you authenticate is specific to the registry provider of your choice. For eg. you may choose to do a `username-password` authentication with DockerHub or authenticate using service accounts in `GCR` and so on.
   145  
   146  **NOTE:** Username-Password authentication is not recommended for CI/CD pipelines since they can be easily compromised and can sometimes be logged as well.
   147  
   148  We will be using GCR in our example. You can refer [this page](https://cloud.google.com/container-registry/docs/advanced-authentication) to understand how to authenticate with it.
   149  
   150  ## Step 8: Using DIND Service (Optional)
   151  
   152  Docker images are typically not cached if you are running your builds/pipeline within Docker containers(i.e. [DIND](https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker-workflow-with-docker-executor) or Docker-in-Docker) and this can be very slow since your CI runner has to download the images again and again for every build even if some layers are already available.
   153  
   154  **NOTE:** If you are not looking to run your pipeline within Docker containers or are fine with slower pipelines/pipelines without caching, you can completely skip this step and proceed to Step 9.
   155  
   156  To avoid this, we can set up a DIND service in our cluster to enable caching and storage of image layers for us. A sample dind deployment file as deployed in Kubernetes can look something like this:
   157  
   158  
   159  ```yml
   160  # Setup a DIND deployment within the Kubernetes cluster to cache images as we build images in our pipelines
   161  apiVersion: apps/v1
   162  kind: Deployment
   163  metadata:
   164      name: my-dind
   165  spec:
   166      selector:
   167          matchLabels:
   168              app: my-dind
   169      strategy:
   170          type: Recreate
   171      template:
   172          metadata:
   173              labels:
   174                  app: my-dind
   175          spec:
   176              containers:
   177                  - image: docker:dind
   178                    imagePullPolicy: Always
   179                    name: my-dind
   180                    ports:
   181                        - containerPort: 2375
   182                          name: my-dind
   183                    env:
   184                        - name: DOCKER_HOST
   185                          value: tcp://localhost:2375
   186                        - name: DOCKER_TLS_CERTDIR
   187                          value: ''
   188                    securityContext:
   189                        privileged: true
   190                    volumeMounts:
   191                        - name: my-dind-storage
   192                          mountPath: /var/lib/docker
   193                    resources:
   194                        limits:
   195                            memory: '2Gi'
   196                            cpu: '1000m'
   197                        requests:
   198                            memory: '1Gi'
   199                            cpu: '250m'
   200              volumes:
   201                  - name: my-dind-storage
   202                    persistentVolumeClaim:
   203                        claimName: my-dind-pv-claim
   204  ```
   205  
   206  
   207  Service File:
   208  
   209  
   210  ```yml
   211  # Expose DIND as a service so that all the pipelines can access it
   212  apiVersion: v1
   213  kind: Service
   214  metadata:
   215      name: my-dind-svc
   216  spec:
   217      selector:
   218          app: my-dind
   219      type: LoadBalancer
   220      ports:
   221          - port: 2375
   222            targetPort: 2375
   223            protocol: TCP
   224            name: http
   225  ```
   226  
   227  
   228  **PV Claim:**
   229  
   230  
   231  ```yml
   232  # Provision a Persistent Volume Claim to be used to store the cached artifacts by DIND
   233  apiVersion: v1
   234  kind: PersistentVolumeClaim
   235  metadata:
   236      name: my-dind-pv-claim
   237      labels:
   238          app: my-dind
   239  spec:
   240      accessModes:
   241          - ReadWriteOnce
   242      resources:
   243          requests:
   244              storage: 200Gi
   245      storageClassName: standard
   246  ```
   247  
   248  
   249  And we would need to pass the URL to the DIND service when we do builds in the pipeline and all the caching will happen in the DIND storage speeding up the pipeline a lot. Make sure that you clean up the DIND storage periodically or setup cleanup scripts for the same else it might fill up soon.
   250  
   251  ## Step 9: Getting the CI pipeline setup
   252  
   253  Now that we have Skaffold ready and working locally, we look at the CI pipeline.
   254  
   255  Here is a sample pipeline using GitLab CI and if you are new to it, you can refer the docs [here](https://docs.gitlab.com/ee/ci/quick_start/). Also, you might want to look at how to define GitLab CI/CD environment variables [here](https://docs.gitlab.com/ee/ci/variables/)
   256  
   257  This pipeline might look complicated at first glance, but most of it related to configuring access for deploying to GCP and configure GitLab's Docker-in-Docker support to cache artifacts between builds. What is important to note is that we use Skaffold to do all the builds and deployments.
   258  
   259  
   260  ```yml
   261  stages:
   262      # The name of the pipeline stage
   263      - my-stage
   264  
   265  # The name of the job
   266  my-job:
   267      # The image to be used as the base for this job
   268      image:
   269          name: gcr.io/k8s-skaffold/skaffold:v1.15.0
   270      # Pipeline tags (if you have specified tags to your GitLab Runner)
   271      tags:
   272          - development
   273      stage: my-stage
   274      retry: 2
   275      script:
   276            # Logging in to our gcp account using the service account key (specified in GITLAB variables)
   277          - echo "$GCP_DEV_SERVICE_KEY" > gcloud-service-key.json
   278          - gcloud auth activate-service-account --key-file gcloud-service-key.json
   279            # Specifying the project, zone, cluster to deploy our application in.
   280          - gcloud config set project $PROJECT_DEV_NAME
   281          - gcloud config set compute/zone $PROJECT_DEV_REGION
   282          - gcloud container clusters get-credentials $CLUSTER_DEV_NAME
   283          - kubectl config get-contexts
   284            # Pass in all the environment variables to be passed to skaffold during build and deployment with all the args as documented in https://skaffold.dev/docs/references/cli/ (in our case our cluster context, env vars, namespace and some labels)
   285          - DOCKER_HOST="$DIND_DEV" NPM_REGISTRY="$NPM_REGISTRY_DEV" NPM_TOKEN="$NPM_TOKEN_DEV" skaffold run --kube-context $CLUSTER_DEV_CONTEXT -n default -l skaffold.dev/run-id=deploydep -p dev-svc --status-check
   286      only:
   287          # Run this only for changes in the main branch
   288          refs:
   289              - main
   290  ```
   291  
   292  So, ultimately our GitLab CI pipeline does not know anything about the helm charts or build strategy or any deployment methods and relies completely on skaffold to do the job for us.
   293  
   294  This works equally well irrespective of whether you choose the [Docker Executor](https://docs.gitlab.com/runner/executors/docker.html) or the [Kubernetes Executor](https://docs.gitlab.com/runner/executors/kubernetes.html) and would require very little changes between the two.
   295  
   296  While GitLab supports many executors (including Docker, Kubernetes, etc.) as documented [here](https://docs.gitlab.com/runner/executors/), we are working with Kubernetes executor here since it is vendor-agnostic, has the ability to auto-scale up/down, private (if you deploy the Runner in your Kubernetes cluster) and also is well supported for the forseeable future.
   297  
   298  **NOTE:** If you are running Skaffold using the Kubernetes executor, make sure that you are running the runner with appropriate permissions or pod security policies. If you are running using DIND, it requires access to the Docker socket and being in privileged mode which might actually tend to be insecure. An alternative can be to use [kaniko](https://github.com/GoogleContainerTools/kaniko), [buildkit](https://github.com/moby/buildkit) or other custom builders like [Buildah](https://github.com/containers/buildah) for building the image.
   299  
   300  ## Step 10: That’s all folks
   301  
   302  If all went well so far, you can try pushing a commit and see your pipeline running, picking up the changes, building and pushing the image and also deploying the same. 
   303  
   304  Hope this post was informative. Good luck with your journey with Skaffold.