1
0
mirror of https://github.com/raseels-repos/golang-saas-starter-kit.git synced 2025-06-08 23:56:37 +02:00

392 lines
17 KiB
Markdown
Raw Permalink Normal View History

2019-08-21 14:31:28 -08:00
cicd
===
_cicd_ is a simple command line tool that facilitates build and deployment for your project. The goal is to help enable
developers to easily setup a continuous build pipeline using [GitLab CI/CD](https://docs.gitlab.com/ee/ci/) and code
driven deployment.
<!-- toc -->
- [Overview](#overview)
* [Deployment Environments](#deployment-environments)
* [Services](#services)
* [Functions](#functions)
* [Schema Migrations](#schema-migrations)
- [Installation](#installation)
- [Getting Started](#getting-started)
- [Usage](#usage)
* [Commands](#commands)
* [Examples](#examples)
- [Join us on Gopher Slack](#join-us-on-gopher-slack)
<!-- tocstop -->
## Overview
The command line tool provides the functionality to configure, build and deploy your code. When new code is push to GitLab,
this tool will enable building, testing and deploying your code to [Amazon AWS](https://aws.amazon.com/).
Deploying your code to production always requires additional tooling and configuration. Instead of patching together a
system of of existing tools and configuration files. This tool centralizes configuration for the application and any
additional deployment resources needed.
Configuration is define with code. AWS resources are created/maintained using the [AWS SDK for Go](https://docs.aws.amazon.com/sdk-for-go/api/).
**This tool is used by GitLab CI/CD** and is configured by a file called `.gitlab-ci.yml` placed at the repository’s root.
**All code is deployed to Amazon AWS**.
Check out the [full presentation](https://docs.google.com/presentation/d/1sRFQwipziZlxBtN7xuF-ol8vtUqD55l_4GE-4_ns-qM/edit?usp=sharing)
that covers how to setup your [GitLab CI/CD](https://docs.gitlab.com/ee/ci/) pipeline that uses autoscaling GitLab
Runners on AWS.
Support is provided for both services and functions. The build process for both relies on docker and thus, neither are
required to be written in go.
Configuration for build and deploy is provided by
[gitlab.com/geeks-accelerator/oss/devops/pkg/devdeploy](https://godoc.org/gitlab.com/geeks-accelerator/oss/devops/pkg/devdeploy)
For additional details regarding this tool, refer to
[gitlab.com/geeks-accelerator/oss/devops](https://gitlab.com/geeks-accelerator/oss/devops)
### Deployment Environments
All configuration for the deployment environments is defined in code that is located in the
[internal/config](https://gitlab.com/geeks-accelerator/oss/saas-starter-kit/tree/master/build/cicd/internal/config)
package. This includes configuration for the following deployment resources:
* [AWS ECR Repository](https://godoc.org/gitlab.com/geeks-accelerator/oss/devops/pkg/devdeploy#AwsEcrRepository)
named `saas-starter-kit`
* [AWS EC2 VPC](https://godoc.org/gitlab.com/geeks-accelerator/oss/devops/pkg/devdeploy#AwsEc2Vpc) defined as using the
default for the AWS Region.
* [AWS EC2 Security Group](https://godoc.org/gitlab.com/geeks-accelerator/oss/devops/pkg/devdeploy#AwsEc2SecurityGroup)
named `saas-starter-kit-[dev|stage|prod]`
* The name of the GitLab runner security group as `gitlab-runner` that will be added to the security group as ingress.
* Private [AWS S3 Bucket](https://godoc.org/gitlab.com/geeks-accelerator/oss/devops/pkg/devdeploy#AwsS3Bucket)
named `saas-starter-kit-private` used by the `web-app` and `web-api` for large object storage.
* A lifecycle policy is applied to the key prefix `tmp/` that will expire objects after 1 day for temporary storage
like exports.
* Configured to [block all public access](https://aws.amazon.com/blogs/aws/amazon-s3-block-public-access-another-layer-of-protection-for-your-accounts-and-buckets/)
* Public [AWS S3 Bucket](https://godoc.org/gitlab.com/geeks-accelerator/oss/devops/pkg/devdeploy#AwsS3Bucket)
named `saas-starter-kit-public` used to serve static files primary for the `web-app`.
* CORS rules for GET and POST to support static files served directly from the S3 Bucket or via Cloudfront.
* Defined key prefix of `public/` used by deployment for uploading static files.
* AWS CloudFront configured for the `prod` environment for serving static files from the S3 Bucket as origin.
* Redis [AWS Elastic Cache Cluster](https://godoc.org/gitlab.com/geeks-accelerator/oss/devops/pkg/devdeploy#AwsElasticCacheCluster)
named `saas-starter-kit-[dev|stage|prod]` for ephemeral storage.
* Configured using Redis version 5.0.4.
* Deployed as a single node cache cluster using the instance type `cache.t2.micro`, 1vCPU with 512mbs of memory.
* `maxmemory-policy` parameter set to `allkeys-lru` which will evict keys by trying to remove the less recently used
(LRU) keys first, in order to make space for the new data added. This will prevent the cache cluster from ever
running out of memory.
* Postgres [AWS RDS Database Instance](https://godoc.org/gitlab.com/geeks-accelerator/oss/devops/pkg/devdeploy#AwsRdsDBInstance)
named `saas-starter-kit-[dev|stage|prod]`.
* Configured with the default database `shared`, username `god` on port 5432.
* The password is randomly generated during creation and stored in [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/).
* Deployed as a single instance using the instance type `db.t2.small`, 1vCPU with 2GiB of memory.
* 20GiB of disk space has been allocated.
* [AWS Iam Policy](https://godoc.org/gitlab.com/geeks-accelerator/oss/devops/pkg/devdeploy#AwsIamPolicy) named
`saasStarterKitService[Dev|Stage|Prod]` that will be used to grants permissions for AWS ECS tasks and AWS Lambda
functions to access to the defined AWS resources listed above.
* Support for datadog can be enabled by added your Datadog API key to [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/)
using the key `saas-starter-kit/[dev|stage|prod]/datadog`
Multiple development environments can easily be configured for more control. This tool supports three target deployment
environments:
* dev
* stage
* prod
`.gitlab-ci.yml` only has prod enabled.
### Services
Services are generally applications that will need to be long running or continuously available. The configured services
are:
* [web-app](https://gitlab.com/geeks-accelerator/oss/saas-starter-kit/tree/master/cmd/web-app) - Publicly accessible
website and web application.
* [web-api](https://gitlab.com/geeks-accelerator/oss/saas-starter-kit/tree/master/cmd/web-api) - Publicly accessible web
API and documentation.
The `Dockerfile` for both services is defined as [multi-stage build](https://docs.docker.com/develop/develop-images/multistage-build/)
that includes building a base layer, running unittests and compiling the go application as static binary. The final
layer in the multi-stage uses [alpine:3.9](https://hub.docker.com/_/alpine?tab=description) as its base image and copies
in the compiled binary resulting in a docker container that is around 50mbs excluding any additional static assets. It's
possible to swap out `alpine:3.9` with [busybox](https://willschenk.com/articles/2019/building_a_slimmer_go_docker_container/)
for an even small resulting docker image.
A service is built using the defined Dockerfile. The resulting image is pushed to
[Amazon Elastic Container Registry](https://aws.amazon.com/ecr/).
Amazon Elastic Container Registry (ECR) is a fully-managed Docker container registry that makes it easy for
developers to store, manage, and deploy Docker container images. Amazon ECR is integrated with Amazon Elastic
Container Service (ECS) simplifying the development to production workflow.
A service is configured for deployment in [services.go](https://gitlab.com/saas-starter-kit/oss/devops/blob/master/build/cicd/internal/config/service.go).
Services are deployed to [AWS Fargate](https://aws.amazon.com/fargate/) based on the defined task definition.
AWS Fargate is a compute engine for Amazon ECS that allows you to run containers without having to manage servers or
clusters. With AWS Fargate, you no longer have to provision, configure, and scale clusters of virtual machines to
run containers.
If the docker file is a multi-stage build and it contains a stage with the name `build_base_golang`, additional caching will
be implemented to reduce build times. The build command assumes for a stage named `build_base_golang` assumes that the
stage will run `go mod download` to pull down all package dependencies. The build command computes a checksum for the
project go.mod and then executes a docker build that targets the specific stage `build_base_golang`. The built container
image is tagged with the go.mod hash and pushed to the projects
[GitLab repository](https://docs.gitlab.com/ee/user/project/repository/).
### Functions
Functions are applications that can be executed in short period of time. The configured function is:
*
An python script for
[Datadog Log Collection](https://gitlab.com/geeks-accelerator/oss/devops/tree/master/examples/aws-lambda-python-ddlogs)
deployed as a function is provided by the devops project in
[examples]((https://gitlab.com/geeks-accelerator/oss/devops/tree/master/examples).
A function is built using the defined
[Dockerfile](https://gitlab.com/geeks-accelerator/oss/devops/blob/master/examples/aws-lambda-python-ddlogs/Dockerfile).
The `Dockerfile` should use a [lambdaci image](https://hub.docker.com/r/lambci/lambda/) as the base image.
Lambdaci images provide a sandboxed local environment that replicates the live AWS Lambda environment almost
identically – including installed software and libraries, file structure and permissions, environment variables,
context objects and behaviors – even the user and running process are the same.
The build command then uses `docker cp` to extract all files from the resulting container image that are located in
`/var/task`. These files are zipped and uploaded to AWS S3 for deployment.
A function is configured for deployment in [functions.go](https://gitlab.com/geeks-accelerator/oss/devops/blob/master/build/cicd/internal/config/function.go).
Functions are deployed to [AWS Lambda](https://aws.amazon.com/lambda/).
AWS Lambda lets you run code without provisioning or managing servers. You pay only for the compute time you consume
- there is no charge when your code is not running.
### Schema Migrations
_cicd_ includes a minimalistic database migration script that implements
[github.com/geeks-accelerator/sqlxmigrate](https://godoc.org/github.com/geeks-accelerator/sqlxmigrate). It provides
schema versioning and migration rollback. The schema for the entire project is defined globally and is located inside
internal: [internal/schema](https://gitlab.com/geeks-accelerator/oss/devops/tree/master/build/cicd/internal/schema)
The example schema package provides two separate methods for handling schema migration:
* [Migrations](https://gitlab.com/geeks-accelerator/oss/devops/blob/master/build/cicd/internal/schema/migrations.go) -
List of direct SQL statements for each migration with defined version ID. A database table is created to persist
executed migrations. Upon run of each schema migration run, the migration logic checks the migration database table to
check if it’s already been executed. Thus, schema migrations are only ever executed once. Migrations are defined as a
function to enable complex migrations so results from query manipulated before being piped to the next query.
* [Init Schema](https://gitlab.com/geeks-accelerator/oss/devops/blob/master/build/cicd/internal/schema/init_schema.go) -
If you have a lot of migrations, it can be a pain to run all them. For example, when you are deploying a new instance of
the app into a clean database. To prevent this, use the initSchema function that will run as-if no migration was run
before (in a new clean database).
Another bonus with the globally defined schema is that it enables your testing package the ability to dynamically spin
up database containers on-demand and automatically include all the migrations. This allows the testing package to
programmatically execute schema migrations before running any unit tests.
## Installation
Make sure you have a working Go environment. Go version 1.2+ is supported. [See
the install instructions for Go](http://golang.org/doc/install.html).
To install _cicd_, simply run:
```
$ go get geeks-accelerator/oss/saas-starter-kit/build/cicd
```
Make sure your `PATH` includes the `$GOPATH/bin` directory so your commands can
be easily used:
```
export PATH=$PATH:$GOPATH/bin
```
## Getting Started
_cicd_ requires AWS permissions to be executed locally. For the GitLab CI/CD build pipeline, AWS roles will be used. This
user is only nessissary for running _cicd_ locally.
1. You will need an existing AWS account or create a new AWS account.
2. Define a new [AWS IAM Policy](https://console.aws.amazon.com/iam/home?region=us-west-2#/policies$new?step=edit)
called `saas-starter-kit-deploy` with a defined JSON statement instead of using the visual
editor. The statement is rather large as each permission is granted individually. A copy of
the statement is stored in the devops repo at
[configs/aws-aim-deploy-policy.json](https://gitlab.com/geeks-accelerator/oss/devops/blob/master/configs/aws-aim-deploy-policy.json)
3. Create new [AWS User](https://console.aws.amazon.com/iam/home?region=us-west-2#/users$new?step=details)
called `saas-starter-kit-deploy` with _Programmatic Access_ and _Attach existing policies directly_ with the policy
created from step 1 `saas-starter-kit-deploy`
4. Try running the build for a single service.
```bash
cicd --env=dev build service --name=aws-ecs-go-web-api --release-tag=testv1
```
4. Try running the deploy for a single service.
```bash
cicd --env=dev deploy service --name=aws-ecs-go-web-api --release-tag=testv1
```
## Usage
```bash
$ cicd [global options] command [command options] [arguments...]
```
### Global Options
* Target Environment - __required__
`--env [dev|stage|prod]`
* AWS Access Key - optional or can be set via env variable `AWS_ACCESS_KEY_ID`
`--aws-access-key value`
* AWS Secret Key - optional, can be set via env variable `AWS_SECRET_ACCESS_KEY`
`--aws-secret-key value`
* AWS Region - optional, can be set via env variable `AWS_REGION`
`--aws-region value`
* AWS Use Role - optional, can be set via env variable `AWS_USE_ROLE`, when enabled an IAM Role else AWS
Access/Secret Keys are required
* Show help
`--help, -h`
* Print the version
`--version, -v`
### Commands
* `build service` - Executes a build for a single service
```bash
$ cicd -env [dev|stage|prod] build service -name NNNNN [command options]
```
Options:
```bash
--name value, -n value target service, required
--release-tag value, --tag value optional tag to override default CI_COMMIT_SHORT_SHA
--dry-run print out the build details
--no-cache skip caching for the docker build
--no-push disable pushing release image to remote repository
```
* `build function` - Executes a build for a single function
```bash
$ cicd -env [dev|stage|prod] build function -name NNNNN [command options]
```
Options:
```bash
--name value, -n value target function, required
--release-tag value, --tag value optional tag to override default CI_COMMIT_SHORT_SHA
--dry-run print out the build details
--no-cache skip caching for the docker build
--no-push disable pushing release image to remote repository
```
* `deploy service` - Executes a deploy for a single service
```bash
$ cicd -env [dev|stage|prod] deploy service -name NNNNN [command options]
```
Options:
```bash
--name value, -n value target service, one of [aws-ecs-go-web-api]
--release-tag value, --tag value optional tag to override default CI_COMMIT_SHORT_SHA
--dry-run print out the deploy details
```
* `deploy function` - Executes a deploy for a single function
```bash
$ cicd -env [dev|stage|prod] deploy function -name NNNNN [command options]
```
Options:
```bash
--name value, -n value target function, required
--release-tag value, --tag value optional tag to override default CI_COMMIT_SHORT_SHA
--dry-run print out the deploy details
```
* `schema` - Runs the database migration
```bash
$ cicd -env [dev|stage|prod] schema
```
* `help` - Shows a list of commands
```bash
$ cicd help
```
Or for one command:
```bash
$ cicd build help
```
### Examples
Build the example service _aws-ecs-go-web-api_
```bash
$ cicid --env=dev build service --name=aws-ecs-go-web-api --release-tag=testv1 --dry-run=false
```
Deploy the example service _aws-ecs-go-web-api_
```bash
$ cicid --env=dev deploy service --name=aws-ecs-go-web-api --release-tag=testv1 --dry-run=false
```
## Join us on Gopher Slack
If you are having problems installing, troubles getting the project running or would like to contribute, join the
channel #saas-starter-kit on [Gopher Slack](http://invite.slack.golangbridge.org/)