From f9dc034abf998135e64bdaef45904b6ceed71671 Mon Sep 17 00:00:00 2001 From: Dwi Fahni Denni Date: Tue, 14 Feb 2023 16:09:22 +0700 Subject: [PATCH 01/12] Update documentation for sample script cicd --- docs/deployment-aws-developer-tools.md | 55 +++++++++++++++++- docs/deployment-azure-devops.md | 58 +++++++++++++++++++ docs/deployment-bitbucket.md | 39 +++++++++++++ docs/deployment-github.md | 78 ++++++++++++++++++++++++++ docs/deployment-gitlab.md | 53 +++++++++++++++++ docs/deployment-jenkins.md | 73 ++++++++++++++++++++++++ 6 files changed, 355 insertions(+), 1 deletion(-) diff --git a/docs/deployment-aws-developer-tools.md b/docs/deployment-aws-developer-tools.md index df595a4f..6f9c870a 100644 --- a/docs/deployment-aws-developer-tools.md +++ b/docs/deployment-aws-developer-tools.md @@ -198,4 +198,57 @@ artifacts: - ecr-tag.sh - Makefile name: "artifact-$(date '+%Y%m%d-%H%M%S')" -``` \ No newline at end of file +``` + +## Example CI/CD Script `cicd-aws-codepipeline.yml` + +``` +version: 0.2 + +phases: + install: + runtime-versions: + docker: 19 + build: + commands: + - go build -o app + - | + if [[ "$CODEBUILD_WEBHOOK_TRIGGER" == "branch/main" ]]; then + semver=1.0.0-${CODEBUILD_SOURCE_VERSION} + elif [[ "$CODEBUILD_WEBHOOK_TRIGGER" == "branch/features/"* ]]; then + semver=1.0.0-${CODEBUILD_WEBHOOK_TRIGGER#branch/features/}.${CODEBUILD_SOURCE_VERSION} + elif [[ "$CODEBUILD_WEBHOOK_TRIGGER" == "branch/bugfix/"* ]]; then + semver=1.1.0-${CODEBUILD_WEBHOOK_TRIGGER#branch/bugfix/}.${CODEBUILD_SOURCE_VERSION} + elif [[ "$CODEBUILD_WEBHOOK_TRIGGER" == "branch/hotfix/"* ]]; then + semver=1.1.1-${CODEBUILD_WEBHOOK_TRIGGER#branch/hotfix/}.${CODEBUILD_SOURCE_VERSION} + fi + echo "Semantic version: $semver" + echo "imageTag=$semver" >> $CODEBUILD_SRC_DIR/variables.env + $(aws ecr get-login --no-include-email --region $AWS_REGION) + docker build -t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver . + docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver + docker tag $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + post_build: + commands: + - | + echo "Deploying to Kubernetes using Helm" + curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash + helmfile sync + +artifacts: + files: + - _infra/* + - .aws/* + - docs/* + - src/* + - dockerhub-build.sh + - dockerhub-push.sh + - dockerhub-tag.sh + - ecr-build.sh + - ecr-push.sh + - ecr-tag.sh + - Makefile + name: "artifact-$(date '+%Y%m%d-%H%M%S')" + discard-paths: yes +``` diff --git a/docs/deployment-azure-devops.md b/docs/deployment-azure-devops.md index 06c7516c..31f2a431 100644 --- a/docs/deployment-azure-devops.md +++ b/docs/deployment-azure-devops.md @@ -16,3 +16,61 @@ Kubernetes Deployment for Simple Golang API [![license](https://img.shields.io/github/license/devopscorner/golang-deployment)](https://img.shields.io/github/license/devopscorner/golang-deployment) --- + +## Example CI/CD Script `cicd-azure-devops.yml` + +``` +trigger: + branches: + include: + - features/* + - bugfix/* + - hotfix/* + +pool: + vmImage: 'ubuntu-latest' + +variables: + imageName: 'devopcorner/bookstore' + ecrRegistry: '0987612345.dkr.ecr.ap-southeast-1.amazonaws.com' + helmReleaseName: 'bookstore' + +steps: +- script: | + go build -o app + displayName: 'Build Go Application' + +- script: | + if [[ "$(Build.SourceBranch)" == "refs/heads/features/"* ]]; then + semver=1.0.0-$(Build.SourceBranchName).$(Build.SourceVersion) + elif [[ "$(Build.SourceBranch)" == "refs/heads/bugfix/"* ]]; then + semver=1.1.0-$(Build.SourceBranchName).$(Build.SourceVersion) + elif [[ "$(Build.SourceBranch)" == "refs/heads/hotfix/"* ]]; then + semver=1.1.1-$(Build.SourceBranchName).$(Build.SourceVersion) + fi + echo "Semantic version: $semver" + echo "##vso[task.setvariable variable=imageTag]$semver" + displayName: 'Set Semantic Version' + +- task: Docker@2 + inputs: + containerRegistry: 'ECR' + repository: $(imageName) + command: 'build' + Dockerfile: '$(System.DefaultWorkingDirectory)/Dockerfile' + tags: | + $(imageTag) + $(Build.SourceBranchName)-latest + latest + displayName: 'Build and Push Docker Image' + +- task: HelmInstaller@1 + inputs: + helmVersionToInstall: 'v3.7.0' + displayName: 'Install Helm' + +- script: | + helmfile sync + workingDirectory: '$(System.DefaultWorkingDirectory)/helm' + displayName: 'Deploy to EKS using Helmfile' +``` \ No newline at end of file diff --git a/docs/deployment-bitbucket.md b/docs/deployment-bitbucket.md index 8385144b..b3e732a1 100644 --- a/docs/deployment-bitbucket.md +++ b/docs/deployment-bitbucket.md @@ -16,3 +16,42 @@ Kubernetes Deployment for Simple Golang API [![license](https://img.shields.io/github/license/devopscorner/golang-deployment)](https://img.shields.io/github/license/devopscorner/golang-deployment) --- + +## Example CI/CD Script `cicd-bitbucket.yml` + +``` +pipelines: + default: + - step: + name: Build and Deploy + image: golang:1.17 + script: + - go build -o app + - | + if [[ "$BITBUCKET_BRANCH" == "features/"* ]]; then + semver=1.0.0-${BITBUCKET_BRANCH#features/}.${BITBUCKET_COMMIT:0:8} + elif [[ "$BITBUCKET_BRANCH" == "bugfix/"* ]]; then + semver=1.1.0-${BITBUCKET_BRANCH#bugfix/}.${BITBUCKET_COMMIT:0:8} + elif [[ "$BITBUCKET_BRANCH" == "hotfix/"* ]]; then + semver=1.1.1-${BITBUCKET_BRANCH#hotfix/}.${BITBUCKET_COMMIT:0:8} + fi + echo "Semantic version: $semver" + echo "imageTag=$semver" >> $BITBUCKET_CLONE_DIR/variables.env + aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com + docker build -t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver . + docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver + docker tag $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + artifacts: + - app + services: + - docker + caches: + - docker + - go + deployment: production + trigger: manual + environment: + name: production + url: $BITBUCKET_DEPLOYMENT_ENVIRONMENT_URL +``` \ No newline at end of file diff --git a/docs/deployment-github.md b/docs/deployment-github.md index a8fdf242..62d6a79d 100644 --- a/docs/deployment-github.md +++ b/docs/deployment-github.md @@ -16,3 +16,81 @@ Kubernetes Deployment for Simple Golang API [![license](https://img.shields.io/github/license/devopscorner/golang-deployment)](https://img.shields.io/github/license/devopscorner/golang-deployment) --- + +## Example CI/CD Script `cicd-github.yml` + +``` +name: CI/CD Pipeline + +on: + push: + branches: + - features/* + - bugfix/* + - hotfix/* + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + + env: + imageName: 'devopcorner/bookstore' + ecrRegistry: '0987612345.dkr.ecr.ap-southeast-1.amazonaws.com' + helmReleaseName: 'bookstore' + + steps: + - name: Checkout Code + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: 1.17 + + - name: Build Go Application + run: go build -o app + + - name: Set Semantic Version + run: | + if [[ "$GITHUB_REF" == "refs/heads/features/"* ]]; then + semver=1.0.0-${GITHUB_REF#refs/heads/features/}.${GITHUB_SHA::8} + elif [[ "$GITHUB_REF" == "refs/heads/bugfix/"* ]]; then + semver=1.1.0-${GITHUB_REF#refs/heads/bugfix/}.${GITHUB_SHA::8} + elif [[ "$GITHUB_REF" == "refs/heads/hotfix/"* ]]; then + semver=1.1.1-${GITHUB_REF#refs/heads/hotfix/}.${GITHUB_SHA::8} + fi + echo "::set-env name=imageTag::$semver" + + - name: Build and Push Docker Image + uses: docker/build-push-action@v2 + with: + context: . + push: true + tags: | + ${{ env.imageName }}:${{ env.imageTag }} + ${{ env.imageName }}:${{ github.ref }}-latest + ${{ env.imageName }}:latest + registry: ${{ env.ecrRegistry }} + username: ${{ secrets.ECR_USERNAME }} + password: ${{ secrets.ECR_PASSWORD }} + + - name: Install Helm + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.EKS_HOST }} + username: ${{ secrets.EKS_USERNAME }} + key: ${{ secrets.EKS_PRIVATE_KEY }} + script: | + curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash + + - name: Deploy to EKS using Helmfile + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.EKS_HOST }} + username: ${{ secrets.EKS_USERNAME }} + key: ${{ secrets.EKS_PRIVATE_KEY }} + script: | + helmfile sync +``` diff --git a/docs/deployment-gitlab.md b/docs/deployment-gitlab.md index 7d4c955c..bba78015 100644 --- a/docs/deployment-gitlab.md +++ b/docs/deployment-gitlab.md @@ -16,3 +16,56 @@ Kubernetes Deployment for Simple Golang API [![license](https://img.shields.io/github/license/devopscorner/golang-deployment)](https://img.shields.io/github/license/devopscorner/golang-deployment) --- + +## Example CI/CD Script `cicd-gitlab.yml` + +``` +stages: + - build + - deploy + +variables: + imageName: 'devopcorner/bookstore' + ecrRegistry: '0987612345.dkr.ecr.ap-southeast-1.amazonaws.com' + helmReleaseName: 'bookstore' + +build: + stage: build + image: golang:1.17 + script: + - go build -o app + - | + if [[ "$CI_COMMIT_REF_NAME" == "features/"* ]]; then + semver=1.0.0-${CI_COMMIT_REF_NAME#features/}.${CI_COMMIT_SHORT_SHA} + elif [[ "$CI_COMMIT_REF_NAME" == "bugfix/"* ]]; then + semver=1.1.0-${CI_COMMIT_REF_NAME#bugfix/}.${CI_COMMIT_SHORT_SHA} + elif [[ "$CI_COMMIT_REF_NAME" == "hotfix/"* ]]; then + semver=1.1.1-${CI_COMMIT_REF_NAME#hotfix/}.${CI_COMMIT_SHORT_SHA} + fi + echo "Semantic version: $semver" + echo "imageTag=$semver" >> $CI_ENVIRONMENT_URL/variables.env + docker build -t $ecrRegistry/$imageName:$semver . + docker push $ecrRegistry/$imageName:$semver + docker tag $ecrRegistry/$imageName:$semver $ecrRegistry/$imageName:latest + docker push $ecrRegistry/$imageName:latest + artifacts: + paths: + - app + +deploy: + stage: deploy + image: alpine/helm:3.7.0 + script: + - apk add --update openssh-client + - ssh-keyscan $EKS_HOST >> ~/.ssh/known_hosts + - ssh -i $EKS_PRIVATE_KEY $EKS_USERNAME@$EKS_HOST "curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash" + - ssh -i $EKS_PRIVATE_KEY $EKS_USERNAME@$EKS_HOST "helmfile sync" + environment: + name: production + url: $CI_ENVIRONMENT_URL + dependencies: + - build + only: + - main + when: manual +``` \ No newline at end of file diff --git a/docs/deployment-jenkins.md b/docs/deployment-jenkins.md index 06bea590..8d2da3ff 100644 --- a/docs/deployment-jenkins.md +++ b/docs/deployment-jenkins.md @@ -20,3 +20,76 @@ Kubernetes Deployment for Simple Golang API ## Jenkins CI/CD - [Jenkins CI/CD](../.jenkins/jenkins-cicd.jenkinsfile) + +## Example CI/CD Script `cicd-jenkins.jenkinsfile` + +``` +pipeline { + agent any + + environment { + AWS_REGION = 'ap-southeast-1' + AWS_ACCOUNT_ID = '0987612345' + IMAGE_NAME = 'devopscorner/bookstore' + } + + stages { + stage('Build') { + steps { + sh 'go build -o app' + script { + if (env.BRANCH_NAME ==~ /features\/.*/) { + def semver = "1.0.0-${env.BRANCH_NAME.replace('features/', '')}.${env.GIT_COMMIT}" + echo "Semantic version: ${semver}" + sh "echo imageTag=${semver} >> variables.env" + } else if (env.BRANCH_NAME ==~ /bugfix\/.*/) { + def semver = "1.1.0-${env.BRANCH_NAME.replace('bugfix/', '')}.${env.GIT_COMMIT}" + echo "Semantic version: ${semver}" + sh "echo imageTag=${semver} >> variables.env" + } else if (env.BRANCH_NAME ==~ /hotfix\/.*/) { + def semver = "1.1.1-${env.BRANCH_NAME.replace('hotfix/', '')}.${env.GIT_COMMIT}" + echo "Semantic version: ${semver}" + sh "echo imageTag=${semver} >> variables.env" + } else if (env.BRANCH_NAME == 'main') { + def semver = "1.0.0-${env.GIT_COMMIT}" + echo "Semantic version: ${semver}" + sh "echo imageTag=${semver} >> variables.env" + } + } + withCredentials([usernamePassword(credentialsId: 'aws-ecr-login', usernameVariable: 'AWS_ACCESS_KEY_ID', passwordVariable: 'AWS_SECRET_ACCESS_KEY')]) { + sh "aws ecr get-login-password --region ${env.AWS_REGION} | docker login --username AWS --password-stdin ${env.AWS_ACCOUNT_ID}.dkr.ecr.${env.AWS_REGION}.amazonaws.com" + sh "docker build -t ${env.AWS_ACCOUNT_ID}.dkr.ecr.${env.AWS_REGION}.amazonaws.com/${env.IMAGE_NAME}:${imageTag} ." + sh "docker push ${env.AWS_ACCOUNT_ID}.dkr.ecr.${env.AWS_REGION}.amazonaws.com/${env.IMAGE_NAME}:${imageTag}" + sh "docker tag ${env.AWS_ACCOUNT_ID}.dkr.ecr.${env.AWS_REGION}.amazonaws.com/${env.IMAGE_NAME}:${imageTag} ${env.AWS_ACCOUNT_ID}.dkr.ecr.${env.AWS_REGION}.amazonaws.com/${env.IMAGE_NAME}:latest" + sh "docker push ${env.AWS_ACCOUNT_ID}.dkr.ecr.${env.AWS_REGION}.amazonaws.com/${env.IMAGE_NAME}:latest" + } + stash includes: 'app', name: 'app' + } + } + stage('Deploy') { + steps { + unstash 'app' + withAWS(region: env.AWS_REGION, roleAccount: "${env.AWS_ACCOUNT_ID}") { + sh "curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash" + sh "helmfile sync" + } + } + environment { + DEPLOYMENT_ENVIRONMENT_URL = "http://your-deployment-environment-url" + } + post { + success { + script { + currentBuild.description = "Deployment completed successfully. Visit ${env.DEPLOYMENT_ENVIRONMENT_URL} for details." + } + } + failure { + script { + currentBuild.description = "Deployment failed. Visit ${env.DEPLOYMENT_ENVIRONMENT_URL} for details." + } + } + } + } + } +} +``` \ No newline at end of file From c5534fc2c88a6e9cf39c4493a69c569035721211 Mon Sep 17 00:00:00 2001 From: Dwi Fahni Denni Date: Tue, 14 Feb 2023 16:12:51 +0700 Subject: [PATCH 02/12] Added sample script cicd --- .aws/cicd-aws-codepipeline.yml | 37 ++++++++++++ .azure-devops/cicd-azure-devops.yml | 53 ++++++++++++++++ .bitbucket/cicd-bitbucket.yml | 34 +++++++++++ .github/workflows/cicd-github.yml | 73 ++++++++++++++++++++++ .gitlab/cicd-gitlab.yml | 48 +++++++++++++++ .jenkins/cicd-jenkins.jenkinsfile | 94 +++++++++++++++++++++++++++++ 6 files changed, 339 insertions(+) create mode 100644 .aws/cicd-aws-codepipeline.yml create mode 100644 .azure-devops/cicd-azure-devops.yml create mode 100644 .bitbucket/cicd-bitbucket.yml create mode 100644 .github/workflows/cicd-github.yml create mode 100644 .gitlab/cicd-gitlab.yml create mode 100644 .jenkins/cicd-jenkins.jenkinsfile diff --git a/.aws/cicd-aws-codepipeline.yml b/.aws/cicd-aws-codepipeline.yml new file mode 100644 index 00000000..55b08c26 --- /dev/null +++ b/.aws/cicd-aws-codepipeline.yml @@ -0,0 +1,37 @@ +version: 0.2 + +phases: + install: + runtime-versions: + docker: 19 + build: + commands: + - go build -o app + - | + if [[ "$CODEBUILD_WEBHOOK_TRIGGER" == "branch/main" ]]; then + semver=1.0.0-${CODEBUILD_SOURCE_VERSION} + elif [[ "$CODEBUILD_WEBHOOK_TRIGGER" == "branch/features/"* ]]; then + semver=1.0.0-${CODEBUILD_WEBHOOK_TRIGGER#branch/features/}.${CODEBUILD_SOURCE_VERSION} + elif [[ "$CODEBUILD_WEBHOOK_TRIGGER" == "branch/bugfix/"* ]]; then + semver=1.1.0-${CODEBUILD_WEBHOOK_TRIGGER#branch/bugfix/}.${CODEBUILD_SOURCE_VERSION} + elif [[ "$CODEBUILD_WEBHOOK_TRIGGER" == "branch/hotfix/"* ]]; then + semver=1.1.1-${CODEBUILD_WEBHOOK_TRIGGER#branch/hotfix/}.${CODEBUILD_SOURCE_VERSION} + fi + echo "Semantic version: $semver" + echo "imageTag=$semver" >> $CODEBUILD_SRC_DIR/variables.env + $(aws ecr get-login --no-include-email --region $AWS_REGION) + docker build -t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver . + docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver + docker tag $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + post_build: + commands: + - | + echo "Deploying to Kubernetes using Helm" + curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash + helmfile sync + +artifacts: + files: + - app + discard-paths: yes diff --git a/.azure-devops/cicd-azure-devops.yml b/.azure-devops/cicd-azure-devops.yml new file mode 100644 index 00000000..4c2804e7 --- /dev/null +++ b/.azure-devops/cicd-azure-devops.yml @@ -0,0 +1,53 @@ +trigger: + branches: + include: + - features/* + - bugfix/* + - hotfix/* + +pool: + vmImage: 'ubuntu-latest' + +variables: + imageName: 'devopcorner/bookstore' + ecrRegistry: '0987612345.dkr.ecr.ap-southeast-1.amazonaws.com' + helmReleaseName: 'bookstore' + +steps: +- script: | + go build -o app + displayName: 'Build Go Application' + +- script: | + if [[ "$(Build.SourceBranch)" == "refs/heads/features/"* ]]; then + semver=1.0.0-$(Build.SourceBranchName).$(Build.SourceVersion) + elif [[ "$(Build.SourceBranch)" == "refs/heads/bugfix/"* ]]; then + semver=1.1.0-$(Build.SourceBranchName).$(Build.SourceVersion) + elif [[ "$(Build.SourceBranch)" == "refs/heads/hotfix/"* ]]; then + semver=1.1.1-$(Build.SourceBranchName).$(Build.SourceVersion) + fi + echo "Semantic version: $semver" + echo "##vso[task.setvariable variable=imageTag]$semver" + displayName: 'Set Semantic Version' + +- task: Docker@2 + inputs: + containerRegistry: 'ECR' + repository: $(imageName) + command: 'build' + Dockerfile: '$(System.DefaultWorkingDirectory)/Dockerfile' + tags: | + $(imageTag) + $(Build.SourceBranchName)-latest + latest + displayName: 'Build and Push Docker Image' + +- task: HelmInstaller@1 + inputs: + helmVersionToInstall: 'v3.7.0' + displayName: 'Install Helm' + +- script: | + helmfile sync + workingDirectory: '$(System.DefaultWorkingDirectory)/helm' + displayName: 'Deploy to EKS using Helmfile' diff --git a/.bitbucket/cicd-bitbucket.yml b/.bitbucket/cicd-bitbucket.yml new file mode 100644 index 00000000..68f025e4 --- /dev/null +++ b/.bitbucket/cicd-bitbucket.yml @@ -0,0 +1,34 @@ +pipelines: + default: + - step: + name: Build and Deploy + image: golang:1.17 + script: + - go build -o app + - | + if [[ "$BITBUCKET_BRANCH" == "features/"* ]]; then + semver=1.0.0-${BITBUCKET_BRANCH#features/}.${BITBUCKET_COMMIT:0:8} + elif [[ "$BITBUCKET_BRANCH" == "bugfix/"* ]]; then + semver=1.1.0-${BITBUCKET_BRANCH#bugfix/}.${BITBUCKET_COMMIT:0:8} + elif [[ "$BITBUCKET_BRANCH" == "hotfix/"* ]]; then + semver=1.1.1-${BITBUCKET_BRANCH#hotfix/}.${BITBUCKET_COMMIT:0:8} + fi + echo "Semantic version: $semver" + echo "imageTag=$semver" >> $BITBUCKET_CLONE_DIR/variables.env + aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com + docker build -t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver . + docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver + docker tag $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + artifacts: + - app + services: + - docker + caches: + - docker + - go + deployment: production + trigger: manual + environment: + name: production + url: $BITBUCKET_DEPLOYMENT_ENVIRONMENT_URL diff --git a/.github/workflows/cicd-github.yml b/.github/workflows/cicd-github.yml new file mode 100644 index 00000000..97604fa0 --- /dev/null +++ b/.github/workflows/cicd-github.yml @@ -0,0 +1,73 @@ +name: CI/CD Pipeline + +on: + push: + branches: + - features/* + - bugfix/* + - hotfix/* + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + + env: + imageName: 'devopcorner/bookstore' + ecrRegistry: '0987612345.dkr.ecr.ap-southeast-1.amazonaws.com' + helmReleaseName: 'bookstore' + + steps: + - name: Checkout Code + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: 1.17 + + - name: Build Go Application + run: go build -o app + + - name: Set Semantic Version + run: | + if [[ "$GITHUB_REF" == "refs/heads/features/"* ]]; then + semver=1.0.0-${GITHUB_REF#refs/heads/features/}.${GITHUB_SHA::8} + elif [[ "$GITHUB_REF" == "refs/heads/bugfix/"* ]]; then + semver=1.1.0-${GITHUB_REF#refs/heads/bugfix/}.${GITHUB_SHA::8} + elif [[ "$GITHUB_REF" == "refs/heads/hotfix/"* ]]; then + semver=1.1.1-${GITHUB_REF#refs/heads/hotfix/}.${GITHUB_SHA::8} + fi + echo "::set-env name=imageTag::$semver" + + - name: Build and Push Docker Image + uses: docker/build-push-action@v2 + with: + context: . + push: true + tags: | + ${{ env.imageName }}:${{ env.imageTag }} + ${{ env.imageName }}:${{ github.ref }}-latest + ${{ env.imageName }}:latest + registry: ${{ env.ecrRegistry }} + username: ${{ secrets.ECR_USERNAME }} + password: ${{ secrets.ECR_PASSWORD }} + + - name: Install Helm + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.EKS_HOST }} + username: ${{ secrets.EKS_USERNAME }} + key: ${{ secrets.EKS_PRIVATE_KEY }} + script: | + curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash + + - name: Deploy to EKS using Helmfile + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.EKS_HOST }} + username: ${{ secrets.EKS_USERNAME }} + key: ${{ secrets.EKS_PRIVATE_KEY }} + script: | + helmfile sync diff --git a/.gitlab/cicd-gitlab.yml b/.gitlab/cicd-gitlab.yml new file mode 100644 index 00000000..3e636a1e --- /dev/null +++ b/.gitlab/cicd-gitlab.yml @@ -0,0 +1,48 @@ +stages: + - build + - deploy + +variables: + imageName: 'devopcorner/bookstore' + ecrRegistry: '0987612345.dkr.ecr.ap-southeast-1.amazonaws.com' + helmReleaseName: 'bookstore' + +build: + stage: build + image: golang:1.17 + script: + - go build -o app + - | + if [[ "$CI_COMMIT_REF_NAME" == "features/"* ]]; then + semver=1.0.0-${CI_COMMIT_REF_NAME#features/}.${CI_COMMIT_SHORT_SHA} + elif [[ "$CI_COMMIT_REF_NAME" == "bugfix/"* ]]; then + semver=1.1.0-${CI_COMMIT_REF_NAME#bugfix/}.${CI_COMMIT_SHORT_SHA} + elif [[ "$CI_COMMIT_REF_NAME" == "hotfix/"* ]]; then + semver=1.1.1-${CI_COMMIT_REF_NAME#hotfix/}.${CI_COMMIT_SHORT_SHA} + fi + echo "Semantic version: $semver" + echo "imageTag=$semver" >> $CI_ENVIRONMENT_URL/variables.env + docker build -t $ecrRegistry/$imageName:$semver . + docker push $ecrRegistry/$imageName:$semver + docker tag $ecrRegistry/$imageName:$semver $ecrRegistry/$imageName:latest + docker push $ecrRegistry/$imageName:latest + artifacts: + paths: + - app + +deploy: + stage: deploy + image: alpine/helm:3.7.0 + script: + - apk add --update openssh-client + - ssh-keyscan $EKS_HOST >> ~/.ssh/known_hosts + - ssh -i $EKS_PRIVATE_KEY $EKS_USERNAME@$EKS_HOST "curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash" + - ssh -i $EKS_PRIVATE_KEY $EKS_USERNAME@$EKS_HOST "helmfile sync" + environment: + name: production + url: $CI_ENVIRONMENT_URL + dependencies: + - build + only: + - main + when: manual diff --git a/.jenkins/cicd-jenkins.jenkinsfile b/.jenkins/cicd-jenkins.jenkinsfile new file mode 100644 index 00000000..d1dda46a --- /dev/null +++ b/.jenkins/cicd-jenkins.jenkinsfile @@ -0,0 +1,94 @@ +# Golang Deployment - CI/CD with Jenkins + +Kubernetes Deployment for Simple Golang API + +![goreport](https://goreportcard.com/badge/github.com/devopscorner/golang-deployment) +![all contributors](https://img.shields.io/github/contributors/devopscorner/golang-deployment) +![tags](https://img.shields.io/github/v/tag/devopscorner/golang-deployment?sort=semver) +[![docker pulls](https://img.shields.io/docker/pulls/devopscorner/bookstore.svg)](https://hub.docker.com/r/devopscorner/bookstore/) +![download all](https://img.shields.io/github/downloads/devopscorner/golang-deployment/total.svg) +![view](https://views.whatilearened.today/views/github/devopscorner/golang-deployment.svg) +![clone](https://img.shields.io/badge/dynamic/json?color=success&label=clone&query=count&url=https://github.com/devopscorner/golang-deployment/blob/master/clone.json?raw=True&logo=github) +![issues](https://img.shields.io/github/issues/devopscorner/golang-deployment) +![pull requests](https://img.shields.io/github/issues-pr/devopscorner/golang-deployment) +![forks](https://img.shields.io/github/forks/devopscorner/golang-deployment) +![stars](https://img.shields.io/github/stars/devopscorner/golang-deployment) +[![license](https://img.shields.io/github/license/devopscorner/golang-deployment)](https://img.shields.io/github/license/devopscorner/golang-deployment) + +--- + +## Jenkins CI/CD + +- [Jenkins CI/CD](../.jenkins/jenkins-cicd.jenkinsfile) + +## Example CI/CD Script `cicd-jenkins.jenkinsfile` + +``` +pipeline { + agent any + + environment { + AWS_REGION = 'ap-southeast-1' + AWS_ACCOUNT_ID = '0987612345' + IMAGE_NAME = 'devopscorner/bookstore' + } + + stages { + stage('Build') { + steps { + sh 'go build -o app' + script { + if (env.BRANCH_NAME ==~ /features\/.*/) { + def semver = "1.0.0-${env.BRANCH_NAME.replace('features/', '')}.${env.GIT_COMMIT}" + echo "Semantic version: ${semver}" + sh "echo imageTag=${semver} >> variables.env" + } else if (env.BRANCH_NAME ==~ /bugfix\/.*/) { + def semver = "1.1.0-${env.BRANCH_NAME.replace('bugfix/', '')}.${env.GIT_COMMIT}" + echo "Semantic version: ${semver}" + sh "echo imageTag=${semver} >> variables.env" + } else if (env.BRANCH_NAME ==~ /hotfix\/.*/) { + def semver = "1.1.1-${env.BRANCH_NAME.replace('hotfix/', '')}.${env.GIT_COMMIT}" + echo "Semantic version: ${semver}" + sh "echo imageTag=${semver} >> variables.env" + } else if (env.BRANCH_NAME == 'main') { + def semver = "1.0.0-${env.GIT_COMMIT}" + echo "Semantic version: ${semver}" + sh "echo imageTag=${semver} >> variables.env" + } + } + withCredentials([usernamePassword(credentialsId: 'aws-ecr-login', usernameVariable: 'AWS_ACCESS_KEY_ID', passwordVariable: 'AWS_SECRET_ACCESS_KEY')]) { + sh "aws ecr get-login-password --region ${env.AWS_REGION} | docker login --username AWS --password-stdin ${env.AWS_ACCOUNT_ID}.dkr.ecr.${env.AWS_REGION}.amazonaws.com" + sh "docker build -t ${env.AWS_ACCOUNT_ID}.dkr.ecr.${env.AWS_REGION}.amazonaws.com/${env.IMAGE_NAME}:${imageTag} ." + sh "docker push ${env.AWS_ACCOUNT_ID}.dkr.ecr.${env.AWS_REGION}.amazonaws.com/${env.IMAGE_NAME}:${imageTag}" + sh "docker tag ${env.AWS_ACCOUNT_ID}.dkr.ecr.${env.AWS_REGION}.amazonaws.com/${env.IMAGE_NAME}:${imageTag} ${env.AWS_ACCOUNT_ID}.dkr.ecr.${env.AWS_REGION}.amazonaws.com/${env.IMAGE_NAME}:latest" + sh "docker push ${env.AWS_ACCOUNT_ID}.dkr.ecr.${env.AWS_REGION}.amazonaws.com/${env.IMAGE_NAME}:latest" + } + stash includes: 'app', name: 'app' + } + } + stage('Deploy') { + steps { + unstash 'app' + withAWS(region: env.AWS_REGION, roleAccount: "${env.AWS_ACCOUNT_ID}") { + sh "curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash" + sh "helmfile sync" + } + } + environment { + DEPLOYMENT_ENVIRONMENT_URL = "http://your-deployment-environment-url" + } + post { + success { + script { + currentBuild.description = "Deployment completed successfully. Visit ${env.DEPLOYMENT_ENVIRONMENT_URL} for details." + } + } + failure { + script { + currentBuild.description = "Deployment failed. Visit ${env.DEPLOYMENT_ENVIRONMENT_URL} for details." + } + } + } + } + } +} From d11e741e4eaa888d084937889fdb201eeeabcc04 Mon Sep 17 00:00:00 2001 From: Dwi Fahni Denni Date: Tue, 14 Feb 2023 16:13:28 +0700 Subject: [PATCH 03/12] Update version to 3.2 --- CHANGELOG.md | 15 +++++++++++++++ README.md | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d890b1d4..228d46bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,19 @@ ## Changelog GO App +### version 3.2 + +- All features in version 3.1 +- Added sample script CI/CD Pipeline with semantic version (semver) docker build images + - AWS CodePipeline (`cicd-aws-codepipeline.yml`) + - Azure DevOps (`cicd-azure-devops.yml`) + - Bitbucket (`cicd-bitbucket.yml`) + - GitHub Action (`cicd-github.yml`) + - GitLab (`cicd-gitlab.yml`) + - Jenkins CI/CD (`cicd-jenkins.jenkinsfile`) + ### version 3.1 +- All features in version 3.0 - Upgrade golang version 1.19.5 - Update `Dockerfile.alpine-3.15` for using golang version 1.19.3 for alpine-3.15 (`golang:1.19.3-alpine3.15`) - Update `Dockerfile.alpine-3.16` for using golang version 1.19.5 for alpine-3.16 (`golang:1.19.5-alpine3.16`) @@ -14,12 +26,15 @@ - Bitbucket (`.bitbucket`) - GitHub (`.github`) - GitLab (`.gitlab`) + - Jenkins CI & Spinnaker CD + - Jenkins CI/CD - Refactoring build, tag, push & pull script for DockerHub - Refactoring build, tag, push & pull script for ECR - Refactoring `makefile` script automation for build, tag, push & pull ### version 3.0 +- All features in version 2.3 - Upgrade golang version 1.19 - Update Dockerfile for using golang version 1.19 (`golang:1.19.2-alpine3.15` & `golang:1.19.2-alpine3.16`) - Added docker-compose configuration & running script docker-compose diff --git a/README.md b/README.md index 36d963d7..c498836b 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Kubernetes Deployment for Simple Golang API ![tags](https://img.shields.io/github/v/tag/devopscorner/golang-deployment?sort=semver) [![docker pulls](https://img.shields.io/docker/pulls/devopscorner/bookstore.svg)](https://hub.docker.com/r/devopscorner/bookstore/) ![download all](https://img.shields.io/github/downloads/devopscorner/golang-deployment/total.svg) -![download latest](https://img.shields.io/github/downloads/devopscorner/golang-deployment/3.1/total) +![download latest](https://img.shields.io/github/downloads/devopscorner/golang-deployment/3.2/total) ![view](https://views.whatilearened.today/views/github/devopscorner/golang-deployment.svg) ![clone](https://img.shields.io/badge/dynamic/json?color=success&label=clone&query=count&url=https://github.com/devopscorner/golang-deployment/blob/master/clone.json?raw=True&logo=github) ![issues](https://img.shields.io/github/issues/devopscorner/golang-deployment) From 2a01d9cf886282d73fe4c43a125a1f97e46d18e1 Mon Sep 17 00:00:00 2001 From: Dwi Fahni Denni Date: Tue, 14 Feb 2023 16:31:00 +0700 Subject: [PATCH 04/12] Fixing jenkinsfile cicd pipeline --- .jenkins/cicd-jenkins.jenkinsfile | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/.jenkins/cicd-jenkins.jenkinsfile b/.jenkins/cicd-jenkins.jenkinsfile index d1dda46a..99958f3d 100644 --- a/.jenkins/cicd-jenkins.jenkinsfile +++ b/.jenkins/cicd-jenkins.jenkinsfile @@ -1,29 +1,3 @@ -# Golang Deployment - CI/CD with Jenkins - -Kubernetes Deployment for Simple Golang API - -![goreport](https://goreportcard.com/badge/github.com/devopscorner/golang-deployment) -![all contributors](https://img.shields.io/github/contributors/devopscorner/golang-deployment) -![tags](https://img.shields.io/github/v/tag/devopscorner/golang-deployment?sort=semver) -[![docker pulls](https://img.shields.io/docker/pulls/devopscorner/bookstore.svg)](https://hub.docker.com/r/devopscorner/bookstore/) -![download all](https://img.shields.io/github/downloads/devopscorner/golang-deployment/total.svg) -![view](https://views.whatilearened.today/views/github/devopscorner/golang-deployment.svg) -![clone](https://img.shields.io/badge/dynamic/json?color=success&label=clone&query=count&url=https://github.com/devopscorner/golang-deployment/blob/master/clone.json?raw=True&logo=github) -![issues](https://img.shields.io/github/issues/devopscorner/golang-deployment) -![pull requests](https://img.shields.io/github/issues-pr/devopscorner/golang-deployment) -![forks](https://img.shields.io/github/forks/devopscorner/golang-deployment) -![stars](https://img.shields.io/github/stars/devopscorner/golang-deployment) -[![license](https://img.shields.io/github/license/devopscorner/golang-deployment)](https://img.shields.io/github/license/devopscorner/golang-deployment) - ---- - -## Jenkins CI/CD - -- [Jenkins CI/CD](../.jenkins/jenkins-cicd.jenkinsfile) - -## Example CI/CD Script `cicd-jenkins.jenkinsfile` - -``` pipeline { agent any From a137d73067c96cbd622580855c5f2f84c5445c92 Mon Sep 17 00:00:00 2001 From: Dwi Fahni Denni Date: Tue, 14 Feb 2023 19:09:44 +0700 Subject: [PATCH 05/12] Added more cicd pipeline --- .argocd/cicd-argocd.yml | 31 ++++ .circleci/cicd-circleci.yml | 56 ++++++ .droneci/cicd-droneci.yml | 62 +++++++ .openshift/cicd-openshift.yml | 83 +++++++++ .semaphoreci/cicd-semaphoreci.yml | 38 ++++ .spinnaker/cicd-spinnaker.yml | 32 ++++ .travisci/cicd-travisci.yml | 43 +++++ terraform/cicd-terraform.tf | 289 ++++++++++++++++++++++++++++++ 8 files changed, 634 insertions(+) create mode 100644 .argocd/cicd-argocd.yml create mode 100644 .circleci/cicd-circleci.yml create mode 100644 .droneci/cicd-droneci.yml create mode 100644 .openshift/cicd-openshift.yml create mode 100644 .semaphoreci/cicd-semaphoreci.yml create mode 100644 .spinnaker/cicd-spinnaker.yml create mode 100644 .travisci/cicd-travisci.yml create mode 100644 terraform/cicd-terraform.tf diff --git a/.argocd/cicd-argocd.yml b/.argocd/cicd-argocd.yml new file mode 100644 index 00000000..ecd5e2a5 --- /dev/null +++ b/.argocd/cicd-argocd.yml @@ -0,0 +1,31 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: bookstore + namespace: argocd +spec: + project: default + source: + repoURL: https://github.com/devopscorner/golang-deployment.git + path: manifests + targetRevision: HEAD + destination: + server: https://your-kubernetes-server + namespace: bookstore + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - Validate=false + healthChecks: + - type: DeploymentRollout + deploymentName: bookstore-deployment + ignoreDifferences: + - kind: Service + jsonPointers: + - /spec/clusterIP + - kind: Deployment + jsonPointers: + - /spec/selector/matchLabels + appVersion: ${VERSION} diff --git a/.circleci/cicd-circleci.yml b/.circleci/cicd-circleci.yml new file mode 100644 index 00000000..14eb900c --- /dev/null +++ b/.circleci/cicd-circleci.yml @@ -0,0 +1,56 @@ +version: 2.1 +jobs: + build-and-deploy: + docker: + - image: circleci/golang:1.19.5 + + environment: + AWS_REGION: ap-southeast-1 + AWS_ACCOUNT_ID: 0987612345 + IMAGE_NAME: devopscorner/bookstore + + steps: + - checkout + - run: + name: Build and push Docker image + command: | + go build -o app + if [[ "$CIRCLE_BRANCH" == "main" ]]; then + semver=1.0.0-${CIRCLE_SHA1:0:8} + elif [[ "$CIRCLE_BRANCH" == "features/"* ]]; then + semver=1.0.0-${CIRCLE_BRANCH#features/}.${CIRCLE_SHA1:0:8} + elif [[ "$CIRCLE_BRANCH" == "bugfix/"* ]]; then + semver=1.1.0-${CIRCLE_BRANCH#bugfix/}.${CIRCLE_SHA1:0:8} + elif [[ "$CIRCLE_BRANCH" == "hotfix/"* ]]; then + semver=1.1.1-${CIRCLE_BRANCH#hotfix/}.${CIRCLE_SHA1:0:8} + fi + echo "Semantic version: $semver" + echo "imageTag=$semver" >> $HOME/variables.env + aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com + docker build -t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver . + docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver + docker tag $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + + - run: + name: Deploy to Kubernetes using Helmfile + command: | + curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash + helmfile sync + + - store_artifacts: + path: app + +workflows: + build-and-deploy: + jobs: + - build-and-deploy: + requires: + - checkout + filters: + branches: + only: + - main + - /^features\/.*$/ + - /^bugfix\/.*$/ + - /^hotfix\/.*$/ diff --git a/.droneci/cicd-droneci.yml b/.droneci/cicd-droneci.yml new file mode 100644 index 00000000..34d33e9d --- /dev/null +++ b/.droneci/cicd-droneci.yml @@ -0,0 +1,62 @@ +kind: pipeline +type: docker +name: bookstore + +platform: + os: linux + arch: amd64 + +steps: + - name: build + image: golang:1.19.5 + commands: + - go mod download + - if [ "${DRONE_BRANCH}" = "main" ]; then + semver=1.0.0-${DRONE_COMMIT_SHA:0:8}; + elif [[ "${DRONE_BRANCH}" == "features/"* ]]; then + semver=1.0.0-${DRONE_BRANCH#features/}.${DRONE_COMMIT_SHA:0:8}; + elif [[ "${DRONE_BRANCH}" == "bugfix/"* ]]; then + semver=1.1.0-${DRONE_BRANCH#bugfix/}.${DRONE_COMMIT_SHA:0:8}; + elif [[ "${DRONE_BRANCH}" == "hotfix/"* ]]; then + semver=1.1.1-${DRONE_BRANCH#hotfix/}.${DRONE_COMMIT_SHA:0:8}; + fi + - echo "Semantic version: $semver" + - docker build -t ${PLUGIN_REGISTRY}/${PLUGIN_REPO}:${semver} . + - docker tag ${PLUGIN_REGISTRY}/${PLUGIN_REPO}:${semver} ${PLUGIN_REGISTRY}/${PLUGIN_REPO}:latest + environment: + - PLUGIN_REGISTRY=your-registry-url + - PLUGIN_REPO=bookstore + when: + event: + - push + - tag + + - name: publish + image: plugins/ecr + settings: + region: ap-southeast-1 + access_key: + from_secret: aws_access_key + secret_key: + from_secret: aws_secret_key + repo: ${PLUGIN_REGISTRY}/${PLUGIN_REPO} + tags: latest,${semver} + when: + event: + - push + - tag + + - name: deploy + image: dtzar/helm-kubectl + settings: + kubernetes_server: your-kubernetes-server + kubernetes_cert: + from_secret: kubernetes_cert + kubernetes_token: + from_secret: kubernetes_token + kubernetes_namespace: bookstore + command: helmfile sync + when: + event: + - push + - tag diff --git a/.openshift/cicd-openshift.yml b/.openshift/cicd-openshift.yml new file mode 100644 index 00000000..10baf154 --- /dev/null +++ b/.openshift/cicd-openshift.yml @@ -0,0 +1,83 @@ +apiVersion: build.openshift.io/v1 +kind: BuildConfig +metadata: + name: bookstore-build + labels: + app: bookstore +spec: + source: + git: + uri: https://github.com/devopscorner/golang-deployment.git + contextDir: app + strategy: + dockerStrategy: + dockerfilePath: Dockerfile + type: Docker + output: + to: + kind: ImageStreamTag + name: devopscorner/bookstore:${IS_TAG} + triggers: + - type: ConfigChange + - type: GitHub + github: + secret: my-secret + - type: Generic + generic: + secret: my-secret + - type: ImageChange + imageChange: + from: + kind: ImageStreamTag + name: devopscorner/bookstore:latest + resources: + limits: + cpu: 1 + memory: 1Gi + requests: + cpu: 500m + memory: 500Mi + +--- + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: bookstore-deployment + labels: + app: bookstore +spec: + replicas: 1 + selector: + matchLabels: + app: bookstore + template: + metadata: + labels: + app: bookstore + spec: + containers: + - name: bookstore + image: devopscorner/bookstore:${IS_TAG} + imagePullPolicy: Always + ports: + - containerPort: 8080 + imagePullSecrets: + - name: my-registry-credentials + +--- + +apiVersion: v1 +kind: Service +metadata: + name: bookstore-service + labels: + app: bookstore +spec: + ports: + - name: http + port: 80 + targetPort: 8080 + selector: + app: bookstore + type: ClusterIP diff --git a/.semaphoreci/cicd-semaphoreci.yml b/.semaphoreci/cicd-semaphoreci.yml new file mode 100644 index 00000000..2fa3683d --- /dev/null +++ b/.semaphoreci/cicd-semaphoreci.yml @@ -0,0 +1,38 @@ +version: v1.0 + +agent: + machine: + type: e1-standard-2 + +blocks: + - name: Build and Deploy + task: + jobs: + - name: Build and Push Docker Image + commands: + - checkout + - sem-version set + - if [[ "$SEMAPHORE_BRANCH_NAME" == "main" ]]; then + semver=1.0.0-${SEMAPHORE_GIT_SHA:0:8} + elif [[ "$SEMAPHORE_BRANCH_NAME" == "features/"* ]]; then + semver=1.0.0-${SEMAPHORE_BRANCH_NAME#features/}.${SEMAPHORE_GIT_SHA:0:8} + elif [[ "$SEMAPHORE_BRANCH_NAME" == "bugfix/"* ]]; then + semver=1.1.0-${SEMAPHORE_BRANCH_NAME#bugfix/}.${SEMAPHORE_GIT_SHA:0:8} + elif [[ "$SEMAPHORE_BRANCH_NAME" == "hotfix/"* ]]; then + semver=1.1.1-${SEMAPHORE_BRANCH_NAME#hotfix/}.${SEMAPHORE_GIT_SHA:0:8} + fi + - echo "Semantic version: $semver" + - echo "imageTag=$semver" >> $SEMAPHORE_WORKSPACE/variables.env + - sem-service aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com + - docker build -t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver . + - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver + - docker tag $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + - name: Deploy to Kubernetes using Helmfile + commands: + - curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash + - helmfile sync + environment: + AWS_REGION: ap-southeast-1 + AWS_ACCOUNT_ID: 0987612345 + IMAGE_NAME: devopscorner/bookstore diff --git a/.spinnaker/cicd-spinnaker.yml b/.spinnaker/cicd-spinnaker.yml new file mode 100644 index 00000000..0a76570b --- /dev/null +++ b/.spinnaker/cicd-spinnaker.yml @@ -0,0 +1,32 @@ +pipelines: + - application: bookstore + name: bookstore-pipeline + triggers: + - type: git + branch: main + repo: https://github.com/devopscorner/golang-deployment.git + secretName: git-secret + stages: + - name: build + type: docker + inputArtifacts: + - name: source + account: github-account + id: '{{build.id}}' + docker: + dockerfile: Dockerfile + account: dockerhub-account + tags: + - ${TRAVIS_COMMIT:0:8} + - name: deploy + type: deployManifest + manifestArtifactAccount: kubernetes-account + manifestArtifactId: '{{build.id}}' + account: kubernetes-account + cloudProvider: kubernetes + source: + account: github-account + manifestName: bookstore.yml + trafficManagement: + options: + enableTraffic: true diff --git a/.travisci/cicd-travisci.yml b/.travisci/cicd-travisci.yml new file mode 100644 index 00000000..8fc81a02 --- /dev/null +++ b/.travisci/cicd-travisci.yml @@ -0,0 +1,43 @@ +language: go + +services: + - docker + +env: + global: + - AWS_REGION=ap-southeast-1 + - AWS_ACCOUNT_ID=0987612345 + - IMAGE_NAME=devopscorner/bookstore + +before_script: + - curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh + - dep ensure + +script: + - if [[ "$TRAVIS_BRANCH" == "main" ]]; then + semver=1.0.0-${TRAVIS_COMMIT:0:8}; + elif [[ "$TRAVIS_BRANCH" == "features/"* ]]; then + semver=1.0.0-${TRAVIS_BRANCH#features/}.${TRAVIS_COMMIT:0:8}; + elif [[ "$TRAVIS_BRANCH" == "bugfix/"* ]]; then + semver=1.1.0-${TRAVIS_BRANCH#bugfix/}.${TRAVIS_COMMIT:0:8}; + elif [[ "$TRAVIS_BRANCH" == "hotfix/"* ]]; then + semver=1.1.1-${TRAVIS_BRANCH#hotfix/}.${TRAVIS_COMMIT:0:8}; + fi + - echo "Semantic version: $semver" + - echo "imageTag=$semver" >> $HOME/variables.env + - docker build -t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver . + - docker tag $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + - aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com + - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver + - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + +after_success: + - curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash + - helmfile sync + +branches: + only: + - main + - /^features\/.*$/ + - /^bugfix\/.*$/ + - /^hotfix\/.*$/ diff --git a/terraform/cicd-terraform.tf b/terraform/cicd-terraform.tf new file mode 100644 index 00000000..3fe3261b --- /dev/null +++ b/terraform/cicd-terraform.tf @@ -0,0 +1,289 @@ +### Golang Terraform Pipeline (GitHub, AWS CodeBuild, AWS Pipeline, Amazon SNS) ### + +# This script includes: +# - ECR repository creation +# - SNS topic creation and subscription +# - CodeBuild project creation for production and staging environments +# - CodePipeline creation with four stages: Source, Build, ManualApproval, and Deploy (two times) +# - ECS task definition and service creation for staging environment +# - CloudWatch Event rule and target for manual approval notification +# - Outputs for the staging and production URLs, build number, and semantic versions for staging and production. +# - Using custom image AWS CodeBuild `devopscorner/cicd:4.0` + +terraform { + required_version = ">= 1.0.9" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 3.63.0, < 4.0" + } + } +} + +provider "aws" { + region = "ap-southeast-1" +} + +data "aws_caller_identity" "current" {} + +resource "aws_ecr_repository" "bookstore" { + name = "bookstore" +} + +resource "aws_sns_topic" "bookstore-topic" { + name = "bookstore-topic" +} + +resource "aws_sns_topic_subscription" "bookstore-subscription" { + topic_arn = aws_sns_topic.bookstore-topic.arn + protocol = "email" + endpoint = "youremail@example.com" +} + +resource "aws_codebuild_project" "bookstore-prod" { + name = "bookstore-prod" + description = "My Golang app for production" + service_role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/CodeBuildServiceRole" + build_timeout = "60" + artifacts { + type = "NO_ARTIFACTS" + } + environment { + compute_type = "BUILD_GENERAL1_SMALL" + # image = "aws/codebuild/standard:5.0" + image = "devopscorner/cicd:4.0" + type = "LINUX_CONTAINER" + environment_variable { + name = "AWS_REGION" + value = "ap-southeast-1" + } + environment_variable { + name = "ENVIRONMENT" + value = "prod" + } + environment_variable { + name = "ECR_REPOSITORY_URI" + value = aws_ecr_repository.bookstore.repository_url + } + } + source { + type = "GITHUB" + location = "https://github.com/devopscorner/golang-deployment" + git_clone_depth = 1 + buildspec = file("${path.module}/buildspec.yaml") + auth { + type = "OAUTH" + } + } +} + +resource "aws_codebuild_project" "bookstore-staging" { + name = "bookstore-staging" + description = "My Golang app for staging" + service_role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/CodeBuildServiceRole" + build_timeout = "60" + artifacts { + type = "NO_ARTIFACTS" + } + environment { + compute_type = "BUILD_GENERAL1_SMALL" + # image = "aws/codebuild/standard:5.0" + image = "devopscorner/cicd:4.0" + type = "LINUX_CONTAINER" + environment_variable { + name = "AWS_REGION" + value = "ap-southeast-1" + } + environment_variable { + name = "ENVIRONMENT" + value = "staging" + } + environment_variable { + name = "ECR_REPOSITORY_URI" + value = aws_ecr_repository.bookstore.repository_url + } + } + source { + type = "GITHUB" + location = "https://github.com/devopscorner/golang-deployment" + git_clone_depth = 1 + buildspec = file("${path.module}/buildspec.yaml") + auth { + type = "OAUTH" + } + } +} + +resource "aws_codepipeline" "bookstore" { + name = "bookstore" + role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/CodePipelineServiceRole" + artifact_store { + type = "S3" + location = "your-artifact-store-bucket" + + encryption_key { + type = "KMS" + id = "your-kms-key-id" + } + } + + stage { + name = "Source" + action { + name = "Source" + category = "Source" + owner = "AWS" + provider = "GitHub" + version = "1" + output_artifacts = ["SourceArtifact"] + configuration = { + Owner = "your-username" + Repo = "your-repo" + Branch = "main" + OAuthToken = "your-github-token" + } + } + } + + stage { + name = "Build" + actions { + name = "Build" + category = "Build" + owner = "AWS" + provider = "CodeBuild" + version = "1" + input_artifacts = ["SourceArtifact"] + output_artifacts = ["BuildArtifact"] + configuration = { + ProjectName = aws_codebuild_project.bookstore-staging.name + } + } + } + + stage { + name = "ManualApproval" + actions { + name = "ManualApproval" + category = "Approval" + owner = "AWS" + provider = "Manual" + version = "1" + input_artifacts = ["BuildArtifact"] + configuration = { + NotificationArn = aws_sns_topic.bookstore-topic.arn + CustomData = "Do you want to deploy the latest changes to production?" + } + } + } + + stage { + name = "Deploy-Staging" + actions { + name = "Deploy-Staging" + category = "Deploy" + owner = "AWS" + provider = "ECS" + version = "1" + input_artifacts = ["BuildArtifact"] + configuration = { + ClusterName = "my-ecs-cluster" + ServiceName = "bookstore-staging" + FileName = "imagedefinitions.json" + ImageUri = "${aws_ecr_repository.bookstore.repository_url}:${var.semver_staging}" + } + } + } + + stage { + name = "Deploy-Prod" + actions { + name = "Deploy-Prod" + category = "Deploy" + owner = "AWS" + provider = "ECS" + version = "1" + input_artifacts = ["BuildArtifact"] + configuration = { + ClusterName = "my-ecs-cluster" + ServiceName = "bookstore-prod" + FileName = "imagedefinitions.json" + ImageUri = "${aws_ecr_repository.bookstore.repository_url}:${var.semver_prod}" + } + condition { + type = "StartsWith" + variable = "ApprovalStatus" + value = "Approved" + } + } + } +} + +resource "aws_ecs_task_definition" "bookstore" { + family = "bookstore" + container_definitions = jsonencode([ + { + name = "bookstore" + image = "${aws_ecr_repository.bookstore.repository_url}:${var.semver_staging}" + cpu = 512 + memory_reservation = 512 + port_mappings = { + container_port = 8080 + host_port = 0 + } + }]) +} + +resource "aws_ecs_service" "bookstore-staging" { + name = "bookstore-staging" + cluster = "my-ecs-cluster" + task_definition = aws_ecs_task_definition.bookstore.arn + desired_count = 2 + launch_type = "EC2" + load_balancer { + target_group_arn = "your-target-group-arn" + container_name = "bookstore" + container_port = 8080 + } +} + +resource "aws_cloudwatch_event_rule" "bookstore-manual-approval" { + name = "bookstore-manual-approval" + description = "Manual approval rule for my Golang app" + event_pattern = jsonencode({ + source = ["aws.codepipeline"] + detail_type = ["CodePipeline Action Execution State Change"] + detail = { + pipeline = ["${aws_codepipeline.bookstore.name}"] + stage = ["ManualApproval"] + action = ["ManualApproval"] + state = ["STARTED", "SUCCEEDED", "FAILED"] + } + }) +} + +resource "aws_cloudwatch_event_target" "bookstore-manual-approval" { + rule = aws_cloudwatch_event_rule.bookstore-manual-approval.name + arn = aws_sns_topic.bookstore-topic.arn +} + +output "bookstore-staging-url" { + value = aws_alb.bookstore-staging.dns_name +} + +output "bookstore-prod-url" { + value = aws_alb.bookstore-prod.dns_name +} + +output "bookstore-build-number" { + value = aws_codebuild_project.bookstore-staging.build_number +} + +output "bookstore-semver-staging" { + value = var.semver_staging +} + +output "bookstore-semver-prod" { + value = var.semver_prod +} \ No newline at end of file From 5c6d61d85e44db63f0e2fb413eedfd917dd5b607 Mon Sep 17 00:00:00 2001 From: Dwi Fahni Denni Date: Tue, 14 Feb 2023 19:11:31 +0700 Subject: [PATCH 06/12] Update documentation for new features cicd pipeline --- docs/README.md | 9 + docs/deployment-argocd.md | 54 ++++++ docs/deployment-circleci.md | 79 +++++++++ docs/deployment-droneci.md | 85 +++++++++ docs/deployment-openshift.md | 106 +++++++++++ docs/deployment-semaphoreci.md | 61 +++++++ docs/deployment-spinnaker.md | 61 +++++++ docs/deployment-terraform.md | 311 +++++++++++++++++++++++++++++++++ docs/deployment-travisci.md | 66 +++++++ 9 files changed, 832 insertions(+) create mode 100644 docs/deployment-argocd.md create mode 100644 docs/deployment-circleci.md create mode 100644 docs/deployment-droneci.md create mode 100644 docs/deployment-openshift.md create mode 100644 docs/deployment-semaphoreci.md create mode 100644 docs/deployment-spinnaker.md create mode 100644 docs/deployment-terraform.md create mode 100644 docs/deployment-travisci.md diff --git a/docs/README.md b/docs/README.md index 88eb3e81..27e3012d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -32,13 +32,22 @@ Kubernetes Deployment for Simple Golang API - GitOps & GitOps DevSecOps Flow (Azure DevOps Pipeline), detail [here](gitops-devsecops-flow-azure.md) link - Deployments: + - **ArgoCD**, detail [here](deployment-argocd.md) link - **AWS Developer Tools** (AWS CodeCommit, AWS CodeBuild & AWS CodePipeline), detail [here](deployment-aws-developer-tools.md) link - **Azure DevOps Pipeline**, detail [here](deployment-azure-devops.md) link - **Bitbucket Pipeline**, detail [here](deployment-bitbucket.md) link + - **CircleCI Pipeline**, detail [here](deployment-circleci.md) link + - **DroneCI Pipeline**, detail [here](deployment-droneci.md) link - **GitHub Action**, detail [here](deployment-github.md) link - **GitLab CI/CD**, detail [here](deployment-gitlab.md) link - **Jenkins CI & Spinnaker CD**, detail [here](deployment-jenkins-spinnaker.md) link - **Jenkins CI/CD**, detail [here](deployment-jenkins.md) link + - **OpenShift CI/CD**, detail [here](deployment-openshift.md) link + - **SemaphoreCI**, detail [here](deployment-semaphoreci.md) link + - **Spinnaker CD**, detail [here](deployment-spinnaker.md) link + - **Terraform AWS CodeBuild, AWS CodePipeline & Amazon SNS**, detail [here](deployment-terraform.md) link + - **TravisCI**, detail [here](deployment-travisci.md) link + ## Reproduce Testing diff --git a/docs/deployment-argocd.md b/docs/deployment-argocd.md new file mode 100644 index 00000000..8f8ade22 --- /dev/null +++ b/docs/deployment-argocd.md @@ -0,0 +1,54 @@ +# Golang Deployment - Deployment with ArgoCD Pipeline + +Kubernetes Deployment for Simple Golang API + +![goreport](https://goreportcard.com/badge/github.com/devopscorner/golang-deployment) +![all contributors](https://img.shields.io/github/contributors/devopscorner/golang-deployment) +![tags](https://img.shields.io/github/v/tag/devopscorner/golang-deployment?sort=semver) +[![docker pulls](https://img.shields.io/docker/pulls/devopscorner/bookstore.svg)](https://hub.docker.com/r/devopscorner/bookstore/) +![download all](https://img.shields.io/github/downloads/devopscorner/golang-deployment/total.svg) +![view](https://views.whatilearened.today/views/github/devopscorner/golang-deployment.svg) +![clone](https://img.shields.io/badge/dynamic/json?color=success&label=clone&query=count&url=https://github.com/devopscorner/golang-deployment/blob/master/clone.json?raw=True&logo=github) +![issues](https://img.shields.io/github/issues/devopscorner/golang-deployment) +![pull requests](https://img.shields.io/github/issues-pr/devopscorner/golang-deployment) +![forks](https://img.shields.io/github/forks/devopscorner/golang-deployment) +![stars](https://img.shields.io/github/stars/devopscorner/golang-deployment) +[![license](https://img.shields.io/github/license/devopscorner/golang-deployment)](https://img.shields.io/github/license/devopscorner/golang-deployment) + +--- + +## Example CI/CD Script `cicd-argocd.yml` + +``` +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: bookstore + namespace: argocd +spec: + project: default + source: + repoURL: https://github.com/devopscorner/golang-deployment.git + path: manifests + targetRevision: HEAD + destination: + server: https://your-kubernetes-server + namespace: bookstore + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - Validate=false + healthChecks: + - type: DeploymentRollout + deploymentName: bookstore-deployment + ignoreDifferences: + - kind: Service + jsonPointers: + - /spec/clusterIP + - kind: Deployment + jsonPointers: + - /spec/selector/matchLabels + appVersion: ${VERSION} +``` \ No newline at end of file diff --git a/docs/deployment-circleci.md b/docs/deployment-circleci.md new file mode 100644 index 00000000..c4b9a767 --- /dev/null +++ b/docs/deployment-circleci.md @@ -0,0 +1,79 @@ +# Golang Deployment - Deployment with CircleCI Pipeline + +Kubernetes Deployment for Simple Golang API + +![goreport](https://goreportcard.com/badge/github.com/devopscorner/golang-deployment) +![all contributors](https://img.shields.io/github/contributors/devopscorner/golang-deployment) +![tags](https://img.shields.io/github/v/tag/devopscorner/golang-deployment?sort=semver) +[![docker pulls](https://img.shields.io/docker/pulls/devopscorner/bookstore.svg)](https://hub.docker.com/r/devopscorner/bookstore/) +![download all](https://img.shields.io/github/downloads/devopscorner/golang-deployment/total.svg) +![view](https://views.whatilearened.today/views/github/devopscorner/golang-deployment.svg) +![clone](https://img.shields.io/badge/dynamic/json?color=success&label=clone&query=count&url=https://github.com/devopscorner/golang-deployment/blob/master/clone.json?raw=True&logo=github) +![issues](https://img.shields.io/github/issues/devopscorner/golang-deployment) +![pull requests](https://img.shields.io/github/issues-pr/devopscorner/golang-deployment) +![forks](https://img.shields.io/github/forks/devopscorner/golang-deployment) +![stars](https://img.shields.io/github/stars/devopscorner/golang-deployment) +[![license](https://img.shields.io/github/license/devopscorner/golang-deployment)](https://img.shields.io/github/license/devopscorner/golang-deployment) + +--- + +## Example CI/CD Script `cicd-circleci.yml` + +``` +version: 2.1 +jobs: + build-and-deploy: + docker: + - image: circleci/golang:1.19.5 + + environment: + AWS_REGION: ap-southeast-1 + AWS_ACCOUNT_ID: 0987612345 + IMAGE_NAME: devopscorner/bookstore + + steps: + - checkout + - run: + name: Build and push Docker image + command: | + go build -o app + if [[ "$CIRCLE_BRANCH" == "main" ]]; then + semver=1.0.0-${CIRCLE_SHA1:0:8} + elif [[ "$CIRCLE_BRANCH" == "features/"* ]]; then + semver=1.0.0-${CIRCLE_BRANCH#features/}.${CIRCLE_SHA1:0:8} + elif [[ "$CIRCLE_BRANCH" == "bugfix/"* ]]; then + semver=1.1.0-${CIRCLE_BRANCH#bugfix/}.${CIRCLE_SHA1:0:8} + elif [[ "$CIRCLE_BRANCH" == "hotfix/"* ]]; then + semver=1.1.1-${CIRCLE_BRANCH#hotfix/}.${CIRCLE_SHA1:0:8} + fi + echo "Semantic version: $semver" + echo "imageTag=$semver" >> $HOME/variables.env + aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com + docker build -t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver . + docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver + docker tag $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + + - run: + name: Deploy to Kubernetes using Helmfile + command: | + curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash + helmfile sync + + - store_artifacts: + path: app + +workflows: + build-and-deploy: + jobs: + - build-and-deploy: + requires: + - checkout + filters: + branches: + only: + - main + - /^features\/.*$/ + - /^bugfix\/.*$/ + - /^hotfix\/.*$/ +``` \ No newline at end of file diff --git a/docs/deployment-droneci.md b/docs/deployment-droneci.md new file mode 100644 index 00000000..b43c70bd --- /dev/null +++ b/docs/deployment-droneci.md @@ -0,0 +1,85 @@ +# Golang Deployment - Deployment with DroneCI Pipeline + +Kubernetes Deployment for Simple Golang API + +![goreport](https://goreportcard.com/badge/github.com/devopscorner/golang-deployment) +![all contributors](https://img.shields.io/github/contributors/devopscorner/golang-deployment) +![tags](https://img.shields.io/github/v/tag/devopscorner/golang-deployment?sort=semver) +[![docker pulls](https://img.shields.io/docker/pulls/devopscorner/bookstore.svg)](https://hub.docker.com/r/devopscorner/bookstore/) +![download all](https://img.shields.io/github/downloads/devopscorner/golang-deployment/total.svg) +![view](https://views.whatilearened.today/views/github/devopscorner/golang-deployment.svg) +![clone](https://img.shields.io/badge/dynamic/json?color=success&label=clone&query=count&url=https://github.com/devopscorner/golang-deployment/blob/master/clone.json?raw=True&logo=github) +![issues](https://img.shields.io/github/issues/devopscorner/golang-deployment) +![pull requests](https://img.shields.io/github/issues-pr/devopscorner/golang-deployment) +![forks](https://img.shields.io/github/forks/devopscorner/golang-deployment) +![stars](https://img.shields.io/github/stars/devopscorner/golang-deployment) +[![license](https://img.shields.io/github/license/devopscorner/golang-deployment)](https://img.shields.io/github/license/devopscorner/golang-deployment) + +--- + +## Example CI/CD Script `cicd-droneci.yml` + +``` +kind: pipeline +type: docker +name: bookstore + +platform: + os: linux + arch: amd64 + +steps: + - name: build + image: golang:1.19.5 + commands: + - go mod download + - if [ "${DRONE_BRANCH}" = "main" ]; then + semver=1.0.0-${DRONE_COMMIT_SHA:0:8}; + elif [[ "${DRONE_BRANCH}" == "features/"* ]]; then + semver=1.0.0-${DRONE_BRANCH#features/}.${DRONE_COMMIT_SHA:0:8}; + elif [[ "${DRONE_BRANCH}" == "bugfix/"* ]]; then + semver=1.1.0-${DRONE_BRANCH#bugfix/}.${DRONE_COMMIT_SHA:0:8}; + elif [[ "${DRONE_BRANCH}" == "hotfix/"* ]]; then + semver=1.1.1-${DRONE_BRANCH#hotfix/}.${DRONE_COMMIT_SHA:0:8}; + fi + - echo "Semantic version: $semver" + - docker build -t ${PLUGIN_REGISTRY}/${PLUGIN_REPO}:${semver} . + - docker tag ${PLUGIN_REGISTRY}/${PLUGIN_REPO}:${semver} ${PLUGIN_REGISTRY}/${PLUGIN_REPO}:latest + environment: + - PLUGIN_REGISTRY=your-registry-url + - PLUGIN_REPO=bookstore + when: + event: + - push + - tag + + - name: publish + image: plugins/ecr + settings: + region: ap-southeast-1 + access_key: + from_secret: aws_access_key + secret_key: + from_secret: aws_secret_key + repo: ${PLUGIN_REGISTRY}/${PLUGIN_REPO} + tags: latest,${semver} + when: + event: + - push + - tag + + - name: deploy + image: dtzar/helm-kubectl + settings: + kubernetes_server: your-kubernetes-server + kubernetes_cert: + from_secret: kubernetes_cert + kubernetes_token: + from_secret: kubernetes_token + kubernetes_namespace: bookstore + command: helmfile sync + when: + event: + - push + - tag +``` \ No newline at end of file diff --git a/docs/deployment-openshift.md b/docs/deployment-openshift.md new file mode 100644 index 00000000..abbdc833 --- /dev/null +++ b/docs/deployment-openshift.md @@ -0,0 +1,106 @@ +# Golang Deployment - Deployment with OpenShift Pipeline + +Kubernetes Deployment for Simple Golang API + +![goreport](https://goreportcard.com/badge/github.com/devopscorner/golang-deployment) +![all contributors](https://img.shields.io/github/contributors/devopscorner/golang-deployment) +![tags](https://img.shields.io/github/v/tag/devopscorner/golang-deployment?sort=semver) +[![docker pulls](https://img.shields.io/docker/pulls/devopscorner/bookstore.svg)](https://hub.docker.com/r/devopscorner/bookstore/) +![download all](https://img.shields.io/github/downloads/devopscorner/golang-deployment/total.svg) +![view](https://views.whatilearened.today/views/github/devopscorner/golang-deployment.svg) +![clone](https://img.shields.io/badge/dynamic/json?color=success&label=clone&query=count&url=https://github.com/devopscorner/golang-deployment/blob/master/clone.json?raw=True&logo=github) +![issues](https://img.shields.io/github/issues/devopscorner/golang-deployment) +![pull requests](https://img.shields.io/github/issues-pr/devopscorner/golang-deployment) +![forks](https://img.shields.io/github/forks/devopscorner/golang-deployment) +![stars](https://img.shields.io/github/stars/devopscorner/golang-deployment) +[![license](https://img.shields.io/github/license/devopscorner/golang-deployment)](https://img.shields.io/github/license/devopscorner/golang-deployment) + +--- + +## Example CI/CD Script `cicd-openshift.yml` + +``` +apiVersion: build.openshift.io/v1 +kind: BuildConfig +metadata: + name: bookstore-build + labels: + app: bookstore +spec: + source: + git: + uri: https://github.com/devopscorner/golang-deployment.git + contextDir: app + strategy: + dockerStrategy: + dockerfilePath: Dockerfile + type: Docker + output: + to: + kind: ImageStreamTag + name: devopscorner/bookstore:${IS_TAG} + triggers: + - type: ConfigChange + - type: GitHub + github: + secret: my-secret + - type: Generic + generic: + secret: my-secret + - type: ImageChange + imageChange: + from: + kind: ImageStreamTag + name: devopscorner/bookstore:latest + resources: + limits: + cpu: 1 + memory: 1Gi + requests: + cpu: 500m + memory: 500Mi + +--- + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: bookstore-deployment + labels: + app: bookstore +spec: + replicas: 1 + selector: + matchLabels: + app: bookstore + template: + metadata: + labels: + app: bookstore + spec: + containers: + - name: bookstore + image: devopscorner/bookstore:${IS_TAG} + imagePullPolicy: Always + ports: + - containerPort: 8080 + imagePullSecrets: + - name: my-registry-credentials + +--- + +apiVersion: v1 +kind: Service +metadata: + name: bookstore-service + labels: + app: bookstore +spec: + ports: + - name: http + port: 80 + targetPort: 8080 + selector: + app: bookstore + type: ClusterIP +``` \ No newline at end of file diff --git a/docs/deployment-semaphoreci.md b/docs/deployment-semaphoreci.md new file mode 100644 index 00000000..e641a164 --- /dev/null +++ b/docs/deployment-semaphoreci.md @@ -0,0 +1,61 @@ +# Golang Deployment - Deployment with SemaphoreCI Pipeline + +Kubernetes Deployment for Simple Golang API + +![goreport](https://goreportcard.com/badge/github.com/devopscorner/golang-deployment) +![all contributors](https://img.shields.io/github/contributors/devopscorner/golang-deployment) +![tags](https://img.shields.io/github/v/tag/devopscorner/golang-deployment?sort=semver) +[![docker pulls](https://img.shields.io/docker/pulls/devopscorner/bookstore.svg)](https://hub.docker.com/r/devopscorner/bookstore/) +![download all](https://img.shields.io/github/downloads/devopscorner/golang-deployment/total.svg) +![view](https://views.whatilearened.today/views/github/devopscorner/golang-deployment.svg) +![clone](https://img.shields.io/badge/dynamic/json?color=success&label=clone&query=count&url=https://github.com/devopscorner/golang-deployment/blob/master/clone.json?raw=True&logo=github) +![issues](https://img.shields.io/github/issues/devopscorner/golang-deployment) +![pull requests](https://img.shields.io/github/issues-pr/devopscorner/golang-deployment) +![forks](https://img.shields.io/github/forks/devopscorner/golang-deployment) +![stars](https://img.shields.io/github/stars/devopscorner/golang-deployment) +[![license](https://img.shields.io/github/license/devopscorner/golang-deployment)](https://img.shields.io/github/license/devopscorner/golang-deployment) + +--- + +## Example CI/CD Script `cicd-semaphoreci.yml` + +``` +version: v1.0 + +agent: + machine: + type: e1-standard-2 + +blocks: + - name: Build and Deploy + task: + jobs: + - name: Build and Push Docker Image + commands: + - checkout + - sem-version set + - if [[ "$SEMAPHORE_BRANCH_NAME" == "main" ]]; then + semver=1.0.0-${SEMAPHORE_GIT_SHA:0:8} + elif [[ "$SEMAPHORE_BRANCH_NAME" == "features/"* ]]; then + semver=1.0.0-${SEMAPHORE_BRANCH_NAME#features/}.${SEMAPHORE_GIT_SHA:0:8} + elif [[ "$SEMAPHORE_BRANCH_NAME" == "bugfix/"* ]]; then + semver=1.1.0-${SEMAPHORE_BRANCH_NAME#bugfix/}.${SEMAPHORE_GIT_SHA:0:8} + elif [[ "$SEMAPHORE_BRANCH_NAME" == "hotfix/"* ]]; then + semver=1.1.1-${SEMAPHORE_BRANCH_NAME#hotfix/}.${SEMAPHORE_GIT_SHA:0:8} + fi + - echo "Semantic version: $semver" + - echo "imageTag=$semver" >> $SEMAPHORE_WORKSPACE/variables.env + - sem-service aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com + - docker build -t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver . + - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver + - docker tag $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + - name: Deploy to Kubernetes using Helmfile + commands: + - curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash + - helmfile sync + environment: + AWS_REGION: ap-southeast-1 + AWS_ACCOUNT_ID: 0987612345 + IMAGE_NAME: devopscorner/bookstore +``` \ No newline at end of file diff --git a/docs/deployment-spinnaker.md b/docs/deployment-spinnaker.md new file mode 100644 index 00000000..e641a164 --- /dev/null +++ b/docs/deployment-spinnaker.md @@ -0,0 +1,61 @@ +# Golang Deployment - Deployment with SemaphoreCI Pipeline + +Kubernetes Deployment for Simple Golang API + +![goreport](https://goreportcard.com/badge/github.com/devopscorner/golang-deployment) +![all contributors](https://img.shields.io/github/contributors/devopscorner/golang-deployment) +![tags](https://img.shields.io/github/v/tag/devopscorner/golang-deployment?sort=semver) +[![docker pulls](https://img.shields.io/docker/pulls/devopscorner/bookstore.svg)](https://hub.docker.com/r/devopscorner/bookstore/) +![download all](https://img.shields.io/github/downloads/devopscorner/golang-deployment/total.svg) +![view](https://views.whatilearened.today/views/github/devopscorner/golang-deployment.svg) +![clone](https://img.shields.io/badge/dynamic/json?color=success&label=clone&query=count&url=https://github.com/devopscorner/golang-deployment/blob/master/clone.json?raw=True&logo=github) +![issues](https://img.shields.io/github/issues/devopscorner/golang-deployment) +![pull requests](https://img.shields.io/github/issues-pr/devopscorner/golang-deployment) +![forks](https://img.shields.io/github/forks/devopscorner/golang-deployment) +![stars](https://img.shields.io/github/stars/devopscorner/golang-deployment) +[![license](https://img.shields.io/github/license/devopscorner/golang-deployment)](https://img.shields.io/github/license/devopscorner/golang-deployment) + +--- + +## Example CI/CD Script `cicd-semaphoreci.yml` + +``` +version: v1.0 + +agent: + machine: + type: e1-standard-2 + +blocks: + - name: Build and Deploy + task: + jobs: + - name: Build and Push Docker Image + commands: + - checkout + - sem-version set + - if [[ "$SEMAPHORE_BRANCH_NAME" == "main" ]]; then + semver=1.0.0-${SEMAPHORE_GIT_SHA:0:8} + elif [[ "$SEMAPHORE_BRANCH_NAME" == "features/"* ]]; then + semver=1.0.0-${SEMAPHORE_BRANCH_NAME#features/}.${SEMAPHORE_GIT_SHA:0:8} + elif [[ "$SEMAPHORE_BRANCH_NAME" == "bugfix/"* ]]; then + semver=1.1.0-${SEMAPHORE_BRANCH_NAME#bugfix/}.${SEMAPHORE_GIT_SHA:0:8} + elif [[ "$SEMAPHORE_BRANCH_NAME" == "hotfix/"* ]]; then + semver=1.1.1-${SEMAPHORE_BRANCH_NAME#hotfix/}.${SEMAPHORE_GIT_SHA:0:8} + fi + - echo "Semantic version: $semver" + - echo "imageTag=$semver" >> $SEMAPHORE_WORKSPACE/variables.env + - sem-service aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com + - docker build -t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver . + - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver + - docker tag $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + - name: Deploy to Kubernetes using Helmfile + commands: + - curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash + - helmfile sync + environment: + AWS_REGION: ap-southeast-1 + AWS_ACCOUNT_ID: 0987612345 + IMAGE_NAME: devopscorner/bookstore +``` \ No newline at end of file diff --git a/docs/deployment-terraform.md b/docs/deployment-terraform.md new file mode 100644 index 00000000..7f22f024 --- /dev/null +++ b/docs/deployment-terraform.md @@ -0,0 +1,311 @@ +# Golang Deployment - CI/CD with Terraform AWS CodeBuild, AWS CodePipeline & Amazon SNS + +Kubernetes Deployment for Simple Golang API + +![goreport](https://goreportcard.com/badge/github.com/devopscorner/golang-deployment) +![all contributors](https://img.shields.io/github/contributors/devopscorner/golang-deployment) +![tags](https://img.shields.io/github/v/tag/devopscorner/golang-deployment?sort=semver) +[![docker pulls](https://img.shields.io/docker/pulls/devopscorner/bookstore.svg)](https://hub.docker.com/r/devopscorner/bookstore/) +![download all](https://img.shields.io/github/downloads/devopscorner/golang-deployment/total.svg) +![view](https://views.whatilearened.today/views/github/devopscorner/golang-deployment.svg) +![clone](https://img.shields.io/badge/dynamic/json?color=success&label=clone&query=count&url=https://github.com/devopscorner/golang-deployment/blob/master/clone.json?raw=True&logo=github) +![issues](https://img.shields.io/github/issues/devopscorner/golang-deployment) +![pull requests](https://img.shields.io/github/issues-pr/devopscorner/golang-deployment) +![forks](https://img.shields.io/github/forks/devopscorner/golang-deployment) +![stars](https://img.shields.io/github/stars/devopscorner/golang-deployment) +[![license](https://img.shields.io/github/license/devopscorner/golang-deployment)](https://img.shields.io/github/license/devopscorner/golang-deployment) + +--- + +## Example CI/CD Terraform Script `cicd-terraform.tf` + +### This script includes: + + - ECR repository creation + - SNS topic creation and subscription + - CodeBuild project creation for production and staging environments + - CodePipeline creation with four stages: Source, Build, ManualApproval, and Deploy (two times) + - ECS task definition and service creation for staging environment + - CloudWatch Event rule and target for manual approval notification + - Outputs for the staging and production URLs, build number, and semantic versions for staging and production. + - Using custom image AWS CodeBuild `devopscorner/cicd:4.0` + +``` +terraform { + required_version = ">= 1.0.9" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 3.63.0, < 4.0" + } + } +} + +provider "aws" { + region = "ap-southeast-1" +} + +data "aws_caller_identity" "current" {} + +resource "aws_ecr_repository" "bookstore" { + name = "bookstore" +} + +resource "aws_sns_topic" "bookstore-topic" { + name = "bookstore-topic" +} + +resource "aws_sns_topic_subscription" "bookstore-subscription" { + topic_arn = aws_sns_topic.bookstore-topic.arn + protocol = "email" + endpoint = "youremail@example.com" +} + +resource "aws_codebuild_project" "bookstore-prod" { + name = "bookstore-prod" + description = "My Golang app for production" + service_role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/CodeBuildServiceRole" + build_timeout = "60" + artifacts { + type = "NO_ARTIFACTS" + } + environment { + compute_type = "BUILD_GENERAL1_SMALL" + # image = "aws/codebuild/standard:5.0" + image = "devopscorner/cicd:4.0" + type = "LINUX_CONTAINER" + environment_variable { + name = "AWS_REGION" + value = "ap-southeast-1" + } + environment_variable { + name = "ENVIRONMENT" + value = "prod" + } + environment_variable { + name = "ECR_REPOSITORY_URI" + value = aws_ecr_repository.bookstore.repository_url + } + } + source { + type = "GITHUB" + location = "https://github.com/devopscorner/golang-deployment" + git_clone_depth = 1 + buildspec = file("${path.module}/buildspec.yaml") + auth { + type = "OAUTH" + } + } +} + +resource "aws_codebuild_project" "bookstore-staging" { + name = "bookstore-staging" + description = "My Golang app for staging" + service_role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/CodeBuildServiceRole" + build_timeout = "60" + artifacts { + type = "NO_ARTIFACTS" + } + environment { + compute_type = "BUILD_GENERAL1_SMALL" + # image = "aws/codebuild/standard:5.0" + image = "devopscorner/cicd:4.0" + type = "LINUX_CONTAINER" + environment_variable { + name = "AWS_REGION" + value = "ap-southeast-1" + } + environment_variable { + name = "ENVIRONMENT" + value = "staging" + } + environment_variable { + name = "ECR_REPOSITORY_URI" + value = aws_ecr_repository.bookstore.repository_url + } + } + source { + type = "GITHUB" + location = "https://github.com/devopscorner/golang-deployment" + git_clone_depth = 1 + buildspec = file("${path.module}/buildspec.yaml") + auth { + type = "OAUTH" + } + } +} + +resource "aws_codepipeline" "bookstore" { + name = "bookstore" + role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/CodePipelineServiceRole" + artifact_store { + type = "S3" + location = "your-artifact-store-bucket" + + encryption_key { + type = "KMS" + id = "your-kms-key-id" + } + } + + stage { + name = "Source" + action { + name = "Source" + category = "Source" + owner = "AWS" + provider = "GitHub" + version = "1" + output_artifacts = ["SourceArtifact"] + configuration = { + Owner = "your-username" + Repo = "your-repo" + Branch = "main" + OAuthToken = "your-github-token" + } + } + } + + stage { + name = "Build" + actions { + name = "Build" + category = "Build" + owner = "AWS" + provider = "CodeBuild" + version = "1" + input_artifacts = ["SourceArtifact"] + output_artifacts = ["BuildArtifact"] + configuration = { + ProjectName = aws_codebuild_project.bookstore-staging.name + } + } + } + + stage { + name = "ManualApproval" + actions { + name = "ManualApproval" + category = "Approval" + owner = "AWS" + provider = "Manual" + version = "1" + input_artifacts = ["BuildArtifact"] + configuration = { + NotificationArn = aws_sns_topic.bookstore-topic.arn + CustomData = "Do you want to deploy the latest changes to production?" + } + } + } + + stage { + name = "Deploy-Staging" + actions { + name = "Deploy-Staging" + category = "Deploy" + owner = "AWS" + provider = "ECS" + version = "1" + input_artifacts = ["BuildArtifact"] + configuration = { + ClusterName = "my-ecs-cluster" + ServiceName = "bookstore-staging" + FileName = "imagedefinitions.json" + ImageUri = "${aws_ecr_repository.bookstore.repository_url}:${var.semver_staging}" + } + } + } + + stage { + name = "Deploy-Prod" + actions { + name = "Deploy-Prod" + category = "Deploy" + owner = "AWS" + provider = "ECS" + version = "1" + input_artifacts = ["BuildArtifact"] + configuration = { + ClusterName = "my-ecs-cluster" + ServiceName = "bookstore-prod" + FileName = "imagedefinitions.json" + ImageUri = "${aws_ecr_repository.bookstore.repository_url}:${var.semver_prod}" + } + condition { + type = "StartsWith" + variable = "ApprovalStatus" + value = "Approved" + } + } + } +} + +resource "aws_ecs_task_definition" "bookstore" { + family = "bookstore" + container_definitions = jsonencode([ + { + name = "bookstore" + image = "${aws_ecr_repository.bookstore.repository_url}:${var.semver_staging}" + cpu = 512 + memory_reservation = 512 + port_mappings = { + container_port = 8080 + host_port = 0 + } + }]) +} + +resource "aws_ecs_service" "bookstore-staging" { + name = "bookstore-staging" + cluster = "my-ecs-cluster" + task_definition = aws_ecs_task_definition.bookstore.arn + desired_count = 2 + launch_type = "EC2" + load_balancer { + target_group_arn = "your-target-group-arn" + container_name = "bookstore" + container_port = 8080 + } +} + +resource "aws_cloudwatch_event_rule" "bookstore-manual-approval" { + name = "bookstore-manual-approval" + description = "Manual approval rule for my Golang app" + event_pattern = jsonencode({ + source = ["aws.codepipeline"] + detail_type = ["CodePipeline Action Execution State Change"] + detail = { + pipeline = ["${aws_codepipeline.bookstore.name}"] + stage = ["ManualApproval"] + action = ["ManualApproval"] + state = ["STARTED", "SUCCEEDED", "FAILED"] + } + }) +} + +resource "aws_cloudwatch_event_target" "bookstore-manual-approval" { + rule = aws_cloudwatch_event_rule.bookstore-manual-approval.name + arn = aws_sns_topic.bookstore-topic.arn +} + +output "bookstore-staging-url" { + value = aws_alb.bookstore-staging.dns_name +} + +output "bookstore-prod-url" { + value = aws_alb.bookstore-prod.dns_name +} + +output "bookstore-build-number" { + value = aws_codebuild_project.bookstore-staging.build_number +} + +output "bookstore-semver-staging" { + value = var.semver_staging +} + +output "bookstore-semver-prod" { + value = var.semver_prod +} +``` \ No newline at end of file diff --git a/docs/deployment-travisci.md b/docs/deployment-travisci.md new file mode 100644 index 00000000..18e8fc17 --- /dev/null +++ b/docs/deployment-travisci.md @@ -0,0 +1,66 @@ +# Golang Deployment - Deployment with TravisCI Pipeline + +Kubernetes Deployment for Simple Golang API + +![goreport](https://goreportcard.com/badge/github.com/devopscorner/golang-deployment) +![all contributors](https://img.shields.io/github/contributors/devopscorner/golang-deployment) +![tags](https://img.shields.io/github/v/tag/devopscorner/golang-deployment?sort=semver) +[![docker pulls](https://img.shields.io/docker/pulls/devopscorner/bookstore.svg)](https://hub.docker.com/r/devopscorner/bookstore/) +![download all](https://img.shields.io/github/downloads/devopscorner/golang-deployment/total.svg) +![view](https://views.whatilearened.today/views/github/devopscorner/golang-deployment.svg) +![clone](https://img.shields.io/badge/dynamic/json?color=success&label=clone&query=count&url=https://github.com/devopscorner/golang-deployment/blob/master/clone.json?raw=True&logo=github) +![issues](https://img.shields.io/github/issues/devopscorner/golang-deployment) +![pull requests](https://img.shields.io/github/issues-pr/devopscorner/golang-deployment) +![forks](https://img.shields.io/github/forks/devopscorner/golang-deployment) +![stars](https://img.shields.io/github/stars/devopscorner/golang-deployment) +[![license](https://img.shields.io/github/license/devopscorner/golang-deployment)](https://img.shields.io/github/license/devopscorner/golang-deployment) + +--- + +## Example CI/CD Script `cicd-travisci.yml` + +``` +language: go + +services: + - docker + +env: + global: + - AWS_REGION=ap-southeast-1 + - AWS_ACCOUNT_ID=0987612345 + - IMAGE_NAME=devopscorner/bookstore + +before_script: + - curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh + - dep ensure + +script: + - if [[ "$TRAVIS_BRANCH" == "main" ]]; then + semver=1.0.0-${TRAVIS_COMMIT:0:8}; + elif [[ "$TRAVIS_BRANCH" == "features/"* ]]; then + semver=1.0.0-${TRAVIS_BRANCH#features/}.${TRAVIS_COMMIT:0:8}; + elif [[ "$TRAVIS_BRANCH" == "bugfix/"* ]]; then + semver=1.1.0-${TRAVIS_BRANCH#bugfix/}.${TRAVIS_COMMIT:0:8}; + elif [[ "$TRAVIS_BRANCH" == "hotfix/"* ]]; then + semver=1.1.1-${TRAVIS_BRANCH#hotfix/}.${TRAVIS_COMMIT:0:8}; + fi + - echo "Semantic version: $semver" + - echo "imageTag=$semver" >> $HOME/variables.env + - docker build -t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver . + - docker tag $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + - aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com + - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$semver + - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest + +after_success: + - curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash + - helmfile sync + +branches: + only: + - main + - /^features\/.*$/ + - /^bugfix\/.*$/ + - /^hotfix\/.*$/ +``` \ No newline at end of file From e2edaca116af30437e4d2c8ffeb2006e36256343 Mon Sep 17 00:00:00 2001 From: Dwi Fahni Denni Date: Tue, 14 Feb 2023 19:11:40 +0700 Subject: [PATCH 07/12] Update change history --- CHANGELOG.md | 20 ++++++++++++++++++++ README.md | 8 ++++++++ 2 files changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0df35f4c..af77b5b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ ## Changelog GO App +### version 3.3 + +- All features in version 3.2 +- Deployment for CI/CD Pipeline: + - **ArgoCD**, detail [here](docs/deployment-argocd.md) link + - **AWS Developer Tools** (AWS CodeCommit, AWS CodeBuild & AWS CodePipeline), detail [here](docs/deployment-aws-developer-tools.md) link + - **Azure DevOps Pipeline**, detail [here](docs/deployment-azure-devops.md) link + - **Bitbucket Pipeline**, detail [here](docs/deployment-bitbucket.md) link + - **CircleCI Pipeline**, detail [here](docs/deployment-circleci.md) link + - **DroneCI Pipeline**, detail [here](docs/deployment-droneci.md) link + - **GitHub Action**, detail [here](docs/deployment-github.md) link + - **GitLab CI/CD**, detail [here](docs/deployment-gitlab.md) link + - **Jenkins CI & Spinnaker CD**, detail [here](docs/deployment-jenkins-spinnaker.md) link + - **Jenkins CI/CD**, detail [here](docs/deployment-jenkins.md) link + - **OpenShift CI/CD**, detail [here](docs/deployment-openshift.md) link + - **SemaphoreCI**, detail [here](docs/deployment-semaphoreci.md) link + - **Spinnaker CD**, detail [here](docs/deployment-spinnaker.md) link + - **Terraform AWS CodeBuild, AWS CodePipeline & Amazon SNS**, detail [here](docs/deployment-terraform.md) link + - **TravisCI**, detail [here](deployment-travisci.md) link + ### version 3.2 - All features in version 3.1 diff --git a/README.md b/README.md index c498836b..2ac5e58c 100644 --- a/README.md +++ b/README.md @@ -51,13 +51,21 @@ Kubernetes Deployment for Simple Golang API - Workflow CI/CD Pipeline, go to [this](docs/workflow-cicd-bookstore-pipeline.md) link - GitOps & GitOps DevSecOps Flow (Azure DevOps Pipeline), go to [this](docs/gitops-devsecops-flow-azure.md) link - Deployments: + - **ArgoCD**, detail [here](docs/deployment-argocd.md) link - **AWS Developer Tools** (AWS CodeCommit, AWS CodeBuild & AWS CodePipeline), detail [here](docs/deployment-aws-developer-tools.md) link - **Azure DevOps Pipeline**, detail [here](docs/deployment-azure-devops.md) link - **Bitbucket Pipeline**, detail [here](docs/deployment-bitbucket.md) link + - **CircleCI Pipeline**, detail [here](docs/deployment-circleci.md) link + - **DroneCI Pipeline**, detail [here](docs/deployment-droneci.md) link - **GitHub Action**, detail [here](docs/deployment-github.md) link - **GitLab CI/CD**, detail [here](docs/deployment-gitlab.md) link - **Jenkins CI & Spinnaker CD**, detail [here](docs/deployment-jenkins-spinnaker.md) link - **Jenkins CI/CD**, detail [here](docs/deployment-jenkins.md) link + - **OpenShift CI/CD**, detail [here](docs/deployment-openshift.md) link + - **SemaphoreCI**, detail [here](docs/deployment-semaphoreci.md) link + - **Spinnaker CD**, detail [here](docs/deployment-spinnaker.md) link + - **Terraform AWS CodeBuild, AWS CodePipeline & Amazon SNS**, detail [here](docs/deployment-terraform.md) link + - **TravisCI**, detail [here](deployment-travisci.md) link ## Reproduce Testing From 1f26d57b03968573b04a6df352976ee8c339dc34 Mon Sep 17 00:00:00 2001 From: Dwi Fahni Denni Date: Tue, 14 Feb 2023 19:14:00 +0700 Subject: [PATCH 08/12] Update badges to version 3.3 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2ac5e58c..51520285 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Kubernetes Deployment for Simple Golang API ![tags](https://img.shields.io/github/v/tag/devopscorner/golang-deployment?sort=semver) [![docker pulls](https://img.shields.io/docker/pulls/devopscorner/bookstore.svg)](https://hub.docker.com/r/devopscorner/bookstore/) ![download all](https://img.shields.io/github/downloads/devopscorner/golang-deployment/total.svg) -![download latest](https://img.shields.io/github/downloads/devopscorner/golang-deployment/3.2/total) +![download latest](https://img.shields.io/github/downloads/devopscorner/golang-deployment/3.3/total) ![view](https://views.whatilearened.today/views/github/devopscorner/golang-deployment.svg) ![clone](https://img.shields.io/badge/dynamic/json?color=success&label=clone&query=count&url=https://github.com/devopscorner/golang-deployment/blob/master/clone.json?raw=True&logo=github) ![issues](https://img.shields.io/github/issues/devopscorner/golang-deployment) From c663ca577b4f2b230c8c36d90d2ac5d11d478bb4 Mon Sep 17 00:00:00 2001 From: Dwi Fahni Denni Date: Tue, 14 Feb 2023 19:17:32 +0700 Subject: [PATCH 09/12] Fixing path docs --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af77b5b5..948a513e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ - **SemaphoreCI**, detail [here](docs/deployment-semaphoreci.md) link - **Spinnaker CD**, detail [here](docs/deployment-spinnaker.md) link - **Terraform AWS CodeBuild, AWS CodePipeline & Amazon SNS**, detail [here](docs/deployment-terraform.md) link - - **TravisCI**, detail [here](deployment-travisci.md) link + - **TravisCI**, detail [here](docs/deployment-travisci.md) link ### version 3.2 From 64d1dcd2a51205f5702ea39357c9333df453b5c1 Mon Sep 17 00:00:00 2001 From: Dwi Fahni Denni Date: Tue, 14 Feb 2023 19:20:53 +0700 Subject: [PATCH 10/12] Fixing path docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 51520285..85b965b9 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Kubernetes Deployment for Simple Golang API - **SemaphoreCI**, detail [here](docs/deployment-semaphoreci.md) link - **Spinnaker CD**, detail [here](docs/deployment-spinnaker.md) link - **Terraform AWS CodeBuild, AWS CodePipeline & Amazon SNS**, detail [here](docs/deployment-terraform.md) link - - **TravisCI**, detail [here](deployment-travisci.md) link + - **TravisCI**, detail [here](docs/deployment-travisci.md) link ## Reproduce Testing From 0ab1553c946406525f5d196a013c71d7860e074a Mon Sep 17 00:00:00 2001 From: Dwi Fahni Denni Date: Thu, 16 Feb 2023 08:15:55 +0700 Subject: [PATCH 11/12] Added CloudFormation & Refactoring Terraform script --- .../jenkins-ci-aws-codecommit.jenkinsfile | 2 +- CHANGELOG.md | 64 +++- README.md | 2 +- .../cicd-cloudformation-aws-codecommit.yml | 313 ++++++++++++++++++ cloudformation/cicd-cloudformation-github.yml | 297 +++++++++++++++++ docs/deployment-terraform.md | 6 +- terraform/cicd-terraform-aws-codecommit.tf | 279 ++++++++++++++++ ...-terraform.tf => cicd-terraform-github.tf} | 38 +-- 8 files changed, 975 insertions(+), 26 deletions(-) create mode 100644 cloudformation/cicd-cloudformation-aws-codecommit.yml create mode 100644 cloudformation/cicd-cloudformation-github.yml create mode 100644 terraform/cicd-terraform-aws-codecommit.tf rename terraform/{cicd-terraform.tf => cicd-terraform-github.tf} (91%) diff --git a/.jenkins/jenkins-ci-aws-codecommit.jenkinsfile b/.jenkins/jenkins-ci-aws-codecommit.jenkinsfile index 31441756..40603908 100644 --- a/.jenkins/jenkins-ci-aws-codecommit.jenkinsfile +++ b/.jenkins/jenkins-ci-aws-codecommit.jenkinsfile @@ -1,6 +1,6 @@ def PATH_KUBECONFIG = '/home/ubuntu/.kube/config' def ECR_REPO = '0987612345.dkr.ecr.ap-southeast-1.amazonaws.com/devopscorner/bookstore' -def VCS_REPO = 'ssh://git-codecommit.ap-southeast-1.amazonaws.com/v1/repos/bookstore' +def VCS_REPO = 'ssh://git-codecommit.ap-southeast-1.amazonaws.com/v1/repos/golang-deployment' def SPINNAKER_HOOK = 'https://spinnaker.awscb.id/webhooks/webhook/bookstore' def skipRemainingStages = false def nextVersionFromGit(scope) { diff --git a/CHANGELOG.md b/CHANGELOG.md index 948a513e..eb3b4712 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,48 @@ -## Changelog GO App +# Golang Deployment + +Kubernetes Deployment for Simple Golang API + +![goreport](https://goreportcard.com/badge/github.com/devopscorner/golang-deployment) ![all contributors](https://img.shields.io/github/contributors/devopscorner/golang-deployment) ![tags](https://img.shields.io/github/v/tag/devopscorner/golang-deployment?sort=semver) [![docker pulls](https://img.shields.io/docker/pulls/devopscorner/bookstore.svg)](https://hub.docker.com/r/devopscorner/bookstore/) ![download all](https://img.shields.io/github/downloads/devopscorner/golang-deployment/total.svg) ![download latest](https://img.shields.io/github/downloads/devopscorner/golang-deployment/3.1/total) ![view](https://views.whatilearened.today/views/github/devopscorner/golang-deployment.svg) ![clone](https://img.shields.io/badge/dynamic/json?color=success&label=clone&query=count&url=https://github.com/devopscorner/golang-deployment/blob/master/clone.json?raw=True&logo=github) ![issues](https://img.shields.io/github/issues/devopscorner/golang-deployment) ![pull requests](https://img.shields.io/github/issues-pr/devopscorner/golang-deployment) ![forks](https://img.shields.io/github/forks/devopscorner/golang-deployment) ![stars](https://img.shields.io/github/stars/devopscorner/golang-deployment) [![license](https://img.shields.io/github/license/devopscorner/golang-deployment)](https://img.shields.io/github/license/devopscorner/golang-deployment) + +## Available Tags + +### Alpine + +| Image name | Size | +|------------|------| +| `devopscorner/bookstore:latest` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/bookstore/latest.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/bookstore/tags?page=1&ordering=last_updated&name=latest) | +| `devopscorner/bookstore:alpine` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/bookstore/alpine.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/bookstore/tags?page=1&ordering=last_updated&name=alpine) | +| `devopscorner/bookstore:alpine-latest` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/bookstore/alpine-latest.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/bookstore/tags?page=1&ordering=last_updated&name=alpine-latest) | +| `devopscorner/bookstore:alpine-3.15` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/bookstore/alpine-3.15.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/bookstore/tags?page=1&ordering=last_updated&name=alpine-3.15) | +| `devopscorner/bookstore:go1.19-alpine3.15` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/bookstore/go1.19-alpine3.15.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/bookstore/tags?page=1&ordering=last_updated&name=go1.19-alpine3.15) | +| `devopscorner/bookstore:go1.19.3-alpine3.15` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/bookstore/go1.19.3-alpine3.15.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/bookstore/tags?page=1&ordering=last_updated&name=go1.19.3-alpine3.15) | +| `devopscorner/bookstore:alpine-3.16` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/bookstore/alpine-3.16.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/bookstore/tags?page=1&ordering=last_updated&name=alpine-3.16) | +| `devopscorner/bookstore:go1.19-alpine3.16` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/bookstore/go1.19-alpine3.16.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/bookstore/tags?page=1&ordering=last_updated&name=go1.19-alpine3.16) | +| `devopscorner/bookstore:go1.19.5-alpine3.16` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/bookstore/go1.19.5-alpine3.16.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/bookstore/tags?page=1&ordering=last_updated&name=go1.19.5-alpine3.16) | +| `devopscorner/bookstore:alpine-3.17` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/bookstore/alpine-3.17.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/bookstore/tags?page=1&ordering=last_updated&name=alpine-3.17) | +| `devopscorner/bookstore:go1.19-alpine3.17` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/bookstore/go1.19-alpine3.17.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/bookstore/tags?page=1&ordering=last_updated&name=go1.19-alpine3.17) | +| `devopscorner/bookstore:go1.19.5-alpine3.17` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/bookstore/go1.19.5-alpine3.17.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/bookstore/tags?page=1&ordering=last_updated&name=go1.19.5-alpine3.17) | + + +### Alpine (Depreciated) +| Image name | Size | +|------------|------| +| `devopscorner/bookstore:go1.18-alpine3.15` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/bookstore/go1.18-alpine3.15.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/bookstore/tags?page=1&ordering=last_updated&name=go1.18-alpine3.15) | +| `devopscorner/bookstore:go1.18-alpine3.16` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/bookstore/go1.18-alpine3.16.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/bookstore/tags?page=1&ordering=last_updated&name=go1.18-alpine3.16) | + +--- + +### version 3.3 + +- All features in version 3.2 +- Added Terraform script: + - GitHub, AWS CodeBuild, AWS CodePipeline & Amazon SNS + - AWS CodeCommit, AWS CodeBuild, AWS CodePipeline & Amazon SNS +- Added CloudFormation script: + - GitHub, AWS CodeBuild, AWS CodePipeline & Amazon SNS + - AWS CodeCommit, AWS CodeBuild, AWS CodePipeline & Amazon SNS + +--- ### version 3.3 @@ -20,6 +64,8 @@ - **Terraform AWS CodeBuild, AWS CodePipeline & Amazon SNS**, detail [here](docs/deployment-terraform.md) link - **TravisCI**, detail [here](docs/deployment-travisci.md) link +--- + ### version 3.2 - All features in version 3.1 @@ -31,6 +77,8 @@ - GitLab (`cicd-gitlab.yml`) - Jenkins CI/CD (`cicd-jenkins.jenkinsfile`) +--- + ### version 3.1 - All features in version 3.0 @@ -51,6 +99,8 @@ - Refactoring build, tag, push & pull script for ECR - Refactoring `makefile` script automation for build, tag, push & pull +--- + ### version 3.0 - All features in version 2.3 @@ -61,11 +111,15 @@ - Refactoring source code (moving) dependencies to `devopscorner/golang-deployment` - Update `gorm` model & sqlite connection driver +--- + ### version 2.3 - All features in version 2.2 - Refactoring path & references docs +--- + ### version 2.2 - Add multiple container registry (DockerHub & ECR) deployment @@ -75,6 +129,8 @@ - Add documentation for build, tag & push container to **Amazon ECR (Elastic Container Registry)**, go to [this](docs/container-bookstore-ecr.md) link - Refactoring workflow documentation, go to [this](docs/workflow-cicd-bookstore-pipeline.md) link +--- + ### version 2.1 - Add Configuration Pipeline Synchronize for Mirroring Repository into AWS CodeCommit @@ -89,6 +145,8 @@ - Azure DevSecOps Pipeline ![Azure DevSecOps Pipeline](docs/assets/gitops-devsecops-azure.png) +--- + ### version 2.0 - IAM Role sample for CodeBuild & CodePipeline @@ -100,6 +158,8 @@ - Setup `~/.ssh/config` for authorization config ssh key 3rd party repository - Dynamic Tags with COMMIT_HASH +--- + ### version 1.0 - Golang API Rest (bookstore) @@ -109,6 +169,8 @@ - Deploy Kubernetes with Helm Values - Buildspec for AWS CodeBuild & AWS CodePipeline +--- + ### version 0.1 - First deployment GO Apps diff --git a/README.md b/README.md index 85b965b9..80021901 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Kubernetes Deployment for Simple Golang API ![tags](https://img.shields.io/github/v/tag/devopscorner/golang-deployment?sort=semver) [![docker pulls](https://img.shields.io/docker/pulls/devopscorner/bookstore.svg)](https://hub.docker.com/r/devopscorner/bookstore/) ![download all](https://img.shields.io/github/downloads/devopscorner/golang-deployment/total.svg) -![download latest](https://img.shields.io/github/downloads/devopscorner/golang-deployment/3.3/total) +![download latest](https://img.shields.io/github/downloads/devopscorner/golang-deployment/3.4/total) ![view](https://views.whatilearened.today/views/github/devopscorner/golang-deployment.svg) ![clone](https://img.shields.io/badge/dynamic/json?color=success&label=clone&query=count&url=https://github.com/devopscorner/golang-deployment/blob/master/clone.json?raw=True&logo=github) ![issues](https://img.shields.io/github/issues/devopscorner/golang-deployment) diff --git a/cloudformation/cicd-cloudformation-aws-codecommit.yml b/cloudformation/cicd-cloudformation-aws-codecommit.yml new file mode 100644 index 00000000..96802992 --- /dev/null +++ b/cloudformation/cicd-cloudformation-aws-codecommit.yml @@ -0,0 +1,313 @@ +AWSTemplateFormatVersion: '2010-09-09' +Description: A CloudFormation template for deploying a Golang app using AWS CodePipeline and AWS CodeBuild, sourced from an AWS CodeCommit repository. + +Parameters: + RepositoryName: + Type: String + Description: The name of the AWS CodeCommit repository. + BranchName: + Type: String + Default: master + Description: The branch to use for the source code. + ECRRepoName: + Type: String + Description: The name of the Elastic Container Registry (ECR) repository to use for storing the Docker image. + S3Bucket: + Type: String + Description: The name of the S3 bucket to use for storing pipeline artifacts. + KMSKeyArn: + Type: String + Description: The ARN of the KMS key to use for encrypting pipeline artifacts. + StagingClusterName: + Type: String + Description: The name of the ECS cluster to use for the staging environment. + StagingServiceName: + Type: String + Description: The name of the ECS service to use for the staging environment. + ProductionClusterName: + Type: String + Description: The name of the ECS cluster to use for the production environment. + ProductionServiceName: + Type: String + Description: The name of the ECS service to use for the production environment. + +Resources: + ECRRepo: + Type: AWS::ECR::Repository + Properties: + RepositoryName: !Ref ECRRepoName + + SNSTopic: + Type: AWS::SNS::Topic + Properties: + DisplayName: !Sub "${AWS::StackName}-SNSTopic" + TopicName: !Sub "${AWS::StackName}-SNSTopic" + + SNSTopicSubscription: + Type: AWS::SNS::Subscription + Properties: + Protocol: email + TopicArn: !Ref SNSTopic + Endpoint: support@devopscorner.id + + CodeCommitRepo: + Type: AWS::CodeCommit::Repository + Properties: + RepositoryName: !Ref RepositoryName + + CodeBuildProject: + Type: AWS::CodeBuild::Project + Properties: + Name: !Sub "${AWS::StackName}-CodeBuildProject" + Description: My Golang app + ServiceRole: !GetAtt CodeBuildServiceRole.Arn + TimeoutInMinutes: 60 + Artifacts: + Type: NO_ARTIFACTS + Environment: + ComputeType: BUILD_GENERAL1_SMALL + ## Image: aws/codebuild/standard:5.0 + Image: devopscorner/cicd:codebuild-4.0 + Type: LINUX_CONTAINER + EnvironmentVariables: + - Name: AWS_REGION + Value: ap-southeast-1 + - Name: ENVIRONMENT + Value: !If [IsStaging, "staging", "production"] + - Name: ECR_REPOSITORY_URI + Value: !Join ['', [!GetAtt ECRRepo.RepositoryUri, ':', !Ref ImageTag]] + Source: + Type: CODECOMMIT + Location: !Sub "https://git-codecommit.${AWS::Region}.amazonaws.com/v1/repos/${RepositoryName}" + GitCloneDepth: 1 + ReportBuildStatus: true + # BuildSpec: | + # version: 0.2 + + # phases: + # install: + # commands: + # - echo "Install phase" + # build: + # commands: + # - echo "Build phase" + # - export IMAGE_TAG=$CODEBUILD_RESOLVED_SOURCE_VERSION + # - echo $IMAGE_TAG > image_tag.txt + # - echo "IMAGE_TAG=$IMAGE_TAG" >> build.env + # finally: + # - echo "Pushing Docker image to ECR..." + # - $(aws ecr get-login --no-include-email --region ap-southeast-1) + # - docker push !Join ['', [!GetAtt ECRRepo.RepositoryUri, ':', !Ref Image + # Tag]] + artifacts: + files: + - image_tag.txt + name: build-artifact + + CodePipeline: + Type: AWS::CodePipeline::Pipeline + Properties: + Name: !Sub "${AWS::StackName}-CodePipeline" + RoleArn: !GetAtt CodePipelineServiceRole.Arn + ArtifactStore: + Type: S3 + Location: !Ref S3Bucket + EncryptionKey: + Type: KMS + Id: !Ref KMSKeyArn + Stages: + - Name: Source + Actions: + - Name: Source + ActionTypeId: + Category: Source + Owner: AWS + Provider: CodeCommit + Version: '1' + OutputArtifacts: + - Name: SourceArtifact + Configuration: + RepositoryName: !Ref RepositoryName + BranchName: !Ref BranchName + RunOrder: 1 + + - Name: Build + Actions: + - Name: Build + ActionTypeId: + Category: Build + Owner: AWS + Provider: CodeBuild + Version: '1' + InputArtifacts: + - Name: SourceArtifact + OutputArtifacts: + - Name: BuildArtifact + Configuration: + ProjectName: !Ref CodeBuildProject + EnvironmentVariables: + - Name: IMAGE_TAG + Value: !Ref ImageTag + RunOrder: 2 + + - Name: ManualApproval + Actions: + - Name: ManualApproval + Category: Approval + Owner: AWS + Provider: Manual + Version: '1' + InputArtifacts: + - Name: BuildArtifact + OutputArtifacts: + - Name: ApproveArtifact + Configuration: + NotificationArn: !Ref SNSTopic + CustomData: "Do you want to deploy the latest changes to production?" + RunOrder: 3 + + - Name: DeployStaging + Actions: + - Name: DeployStaging + ActionTypeId: + Category: Deploy + Owner: AWS + Provider: ECS + Version: '1' + InputArtifacts: + - Name: BuildArtifact + - Name: ApproveArtifact + Configuration: + ClusterName: !Ref StagingClusterName + ServiceName: !Ref StagingServiceName + FileName: imagedefinitions.json + ImageUri: !Join ['', [!GetAtt ECRRepo.RepositoryUri, ':', !Ref ImageTag]] + RunOrder: 4 + Condition: + StringEquals: + - !Ref IsStaging + - "true" + + - Name: DeployProd + Actions: + - Name: DeployProd + ActionTypeId: + Category: Deploy + Owner: AWS + Provider: ECS + Version: '1' + InputArtifacts: + - Name: BuildArtifact + - Name: ApproveArtifact + Configuration: + ClusterName: !Ref ProductionClusterName + ServiceName: !Ref ProductionServiceName + FileName: imagedefinitions.json + ImageUri: !Join ['', [!GetAtt ECRRepo.RepositoryUri, ':', !Ref ImageTag]] + RunOrder: 4 + Condition: + StringEquals: + - !Ref IsStaging + - "false" + StartsWith: + ApprovalStatus: "Approved" + + CodePipelineServiceRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: codepipeline.amazonaws.com + Action: sts:AssumeRole + ManagedPolicyArns: + - arn:aws:iam::aws:policy/AWSCodePipelineFullAccess + Policies: + - PolicyName: AllowECRWriteAccess + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - ecr:BatchCheckLayerAvailability + - ecr:GetDownloadUrlForLayer + - ecr:BatchGetImage + - ecr:PutImage + Resource: !GetAtt ECRRepo.Arn + + CodeBuildServiceRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: codebuild.amazonaws.com + Action: sts:AssumeRole + ManagedPolicyArns: + - arn:aws:iam::aws:policy/AWSCodeBuildAdminAccess + - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser + - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess + Policies: + - PolicyName: AllowECRReadAccess + + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - ecr:GetAuthorizationToken + - ecr:GetDownloadUrlForLayer + - ecr:BatchGetImage + Resource: '*' + + S3ArtifactStoreBucket: + Type: AWS::S3::Bucket + Properties: + BucketName: !Ref S3Bucket + + S3Object: + Type: AWS::S3::Object + Properties: + Bucket: !Ref S3Bucket + Key: !Sub "${AWS::StackName}/buildspec.yml" + ContentType: text/x-yaml + Body: |- + version: 0.2 + phases: + install: + commands: + - echo "Install phase" + build: + commands: + - echo "Build phase" + - export IMAGE_TAG=$CODEBUILD_RESOLVED_SOURCE_VERSION + - echo $IMAGE_TAG > image_tag.txt + - echo "IMAGE_TAG=$IMAGE_TAG" >> build.env + post_build: + commands: + - echo "Post build phase" + - echo "Pushing Docker image to ECR..." + - $(aws ecr get-login --no-include-email --region ap-southeast-1) + - docker push !Join ['', [!GetAtt ECRRepo.RepositoryUri, ':', !Ref ImageTag]] + - echo "Pushed Docker image to ECR successfully." + +Outputs: + CodePipelineUrl: + Description: The URL of the AWS CodePipeline. + Value: !Sub "https://${AWS::Region}.console.aws.amazon.com/codesuite/codepipeline/pipelines/${AWS::StackName}-CodePipeline/view?region=${AWS::Region}" + ECRRepositoryUrl: + Description: URL of the Elastic Container Registry (ECR) repository. + Value: !GetAtt ECRRepo.RepositoryUri + SNSTopicArn: + Description: ARN of the SNS topic. + Value: !Ref SNSTopic + PipelineName: + Description: Name of the CodePipeline. + Value: !Ref CodePipeline + CodeBuildProjectName: + Description: Name of the CodeBuild project. + Value: !Ref CodeBuildProject diff --git a/cloudformation/cicd-cloudformation-github.yml b/cloudformation/cicd-cloudformation-github.yml new file mode 100644 index 00000000..47a1f86e --- /dev/null +++ b/cloudformation/cicd-cloudformation-github.yml @@ -0,0 +1,297 @@ +AWSTemplateFormatVersion: 2010-09-09 +Description: A CloudFormation template for deploying a Golang app using AWS CodePipeline and AWS CodeBuild, sourced from GitHub. + +Parameters: + GitHubOwner: + Type: String + Description: The GitHub user or organization that owns the repository. + GitHubRepoName: + Type: String + Description: The name of the GitHub repository. + GitHubBranch: + Type: String + Description: The name of the branch to use as the source for the pipeline. + ECRRepoName: + Type: String + Description: The name of the Elastic Container Registry (ECR) repository to use for storing the Docker image. + S3Bucket: + Type: String + Description: The name of the S3 bucket to use for storing pipeline artifacts. + KMSKeyArn: + Type: String + Description: The ARN of the KMS key to use for encrypting pipeline artifacts. + StagingClusterName: + Type: String + Description: The name of the ECS cluster to use for the staging environment. + StagingServiceName: + Type: String + Description: The name of the ECS service to use for the staging environment. + ProductionClusterName: + Type: String + Description: The name of the ECS cluster to use for the production environment. + ProductionServiceName: + Type: String + Description: The name of the ECS service to use for the production environment. + +Resources: + ECRRepo: + Type: AWS::ECR::Repository + Properties: + RepositoryName: !Ref ECRRepoName + + SNSTopic: + Type: AWS::SNS::Topic + Properties: + DisplayName: !Sub "${AWS::StackName}-SNSTopic" + TopicName: !Sub "${AWS::StackName}-SNSTopic" + + SNSTopicSubscription: + Type: AWS::SNS::Subscription + Properties: + Protocol: email + TopicArn: !Ref SNSTopic + Endpoint: support@devopscorner.id + + CodeBuildProject: + Type: AWS::CodeBuild::Project + Properties: + Name: !Sub "${AWS::StackName}-CodeBuildProject" + Description: My Golang app + ServiceRole: !GetAtt CodeBuildServiceRole.Arn + TimeoutInMinutes: 60 + Artifacts: + Type: NO_ARTIFACTS + Environment: + ComputeType: BUILD_GENERAL1_SMALL + ## Image: aws/codebuild/standard:5.0 + Image: devopscorner/cicd:codebuild-4.0 + Type: LINUX_CONTAINER + EnvironmentVariables: + - Name: AWS_REGION + Value: your-aws-region + - Name: ENVIRONMENT + Value: !If [IsStaging, "staging", "production"] + - Name: ECR_REPOSITORY_URI + Value: !Join ['', [!GetAtt ECRRepo.RepositoryUri, ':', !Ref ImageTag]] + Source: + Type: GITHUB + Location: !Sub "https://github.com/devopscorner/golang-deployment" + GitCloneDepth: 1 + BuildSpec: !Sub "${S3ObjectUrl}" + GitCloneDepth: 1 + ReportBuildStatus: true + SourceVersion: !Ref GitHubBranch + artifacts: + files: + - image_tag.txt + name: build-artifact + + CodePipeline: + Type: AWS::CodePipeline::Pipeline + Properties: + Name: !Sub "${AWS::StackName}-CodePipeline" + RoleArn: !GetAtt CodePipelineServiceRole.Arn + ArtifactStore: + Type: S3 + Location: !Ref S3Bucket + EncryptionKey: + Type: KMS + Id: !Ref KMSKeyArn + Stages: + - Name: Source + Actions: + - Name: Source + ActionTypeId: + Category: Source + Owner: ThirdParty + Provider: GitHub + Version: '1' + OutputArtifacts: + - Name: SourceArtifact + Configuration: + Owner: !Ref GitHubOwner + Repo: !Ref GitHubRepoName + Branch: !Ref GitHubBranch + OAuthToken: !Ref GitHubToken + RunOrder: 1 + + - Name: Build + Actions: + - Name: Build + ActionTypeId: + Category: Build + Owner: AWS + Provider: CodeBuild + Version: '1' + InputArtifacts: + - Name: SourceArtifact + OutputArtifacts: + - Name: BuildArtifact + Configuration: + ProjectName: !Ref CodeBuildProject + EnvironmentVariables: + - Name: IMAGE_TAG + Value: !Ref ImageTag + RunOrder: 2 + + - Name: ManualApproval + Actions: + - Name: ManualApproval + Category: Approval + Owner: AWS + Provider: Manual + Version: '1' + InputArtifacts: + - Name: BuildArtifact + OutputArtifacts: + - Name: ApproveArtifact + Configuration: + NotificationArn: !Ref SNSTopic + CustomData: "Do you want to deploy the latest changes to production?" + RunOrder: 3 + + - Name: DeployStaging + Actions: + - Name: DeployStaging + ActionTypeId: + Category: Deploy + Owner: AWS + Provider: ECS + Version: '1' + InputArtifacts: + - Name: BuildArtifact + - Name: ApproveArtifact + Configuration: + ClusterName: !Ref StagingClusterName + ServiceName: !Ref StagingServiceName + FileName: imagedefinitions.json + ImageUri: !Join ['', [!GetAtt ECRRepo.RepositoryUri, ':', !Ref ImageTag]] + RunOrder: 4 + Condition: + StringEquals: + - !Ref IsStaging + - "true" + + - Name: DeployProd + Actions: + - Name: DeployProd + ActionTypeId: + Category: Deploy + Owner: AWS + Provider: ECS + Version: '1' + InputArtifacts: + - Name: BuildArtifact + - Name: ApproveArtifact + Configuration: + ClusterName: !Ref ProductionClusterName + ServiceName: !Ref ProductionServiceName + FileName: imagedefinitions.json + ImageUri: !Join ['', [!GetAtt ECRRepo.RepositoryUri, ':', !Ref ImageTag]] + RunOrder: 4 + Condition: + StringEquals: + - !Ref IsStaging + - "false" + StartsWith: + ApprovalStatus: "Approved" + + CodePipelineServiceRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: codepipeline.amazonaws.com + Action: sts:AssumeRole + ManagedPolicyArns: + - arn:aws:iam::aws:policy/AWSCodePipelineFullAccess + Policies: + - PolicyName: AllowECRWriteAccess + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - ecr:BatchCheckLayerAvailability + - ecr:GetDownloadUrlForLayer + - ecr:BatchGetImage + - ecr:PutImage + Resource: !GetAtt ECRRepo.Arn + + CodeBuildServiceRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: codebuild.amazonaws.com + Action: sts:AssumeRole + ManagedPolicyArns: + - arn:aws:iam::aws:policy/AWSCodeBuildAdminAccess + - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser + - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess + Policies: + - PolicyName: AllowECRReadAccess + + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - ecr:GetAuthorizationToken + - ecr:GetDownloadUrlForLayer + - ecr:BatchGetImage + Resource: '*' + + S3ArtifactStoreBucket: + Type: AWS::S3::Bucket + Properties: + BucketName: !Ref S3Bucket + + S3Object: + Type: AWS::S3::Object + Properties: + Bucket: !Ref S3Bucket + Key: !Sub "${AWS::StackName}/buildspec.yml" + ContentType: text/x-yaml + Body: |- + version: 0.2 + phases: + install: + commands: + - echo "Install phase" + build: + commands: + - echo "Build phase" + - export IMAGE_TAG=$CODEBUILD_RESOLVED_SOURCE_VERSION + - echo $IMAGE_TAG > image_tag.txt + - echo "IMAGE_TAG=$IMAGE_TAG" >> build.env + post_build: + commands: + - echo "Post build phase" + - echo "Pushing Docker image to ECR..." + - $(aws ecr get-login --no-include-email --region your-aws-region) + - docker push !Join ['', [!GetAtt ECRRepo.RepositoryUri, ':', !Ref ImageTag]] + - echo "Pushed Docker image to ECR successfully." + +Outputs: + CodePipelineUrl: + Description: The URL of the AWS CodePipeline. + Value: !Sub "https://${AWS::Region}.console.aws.amazon.com/codesuite/codepipeline/pipelines/${AWS::StackName}-CodePipeline/view?region=${AWS::Region}" + ECRRepositoryUrl: + Description: URL of the Elastic Container Registry (ECR) repository. + Value: !GetAtt ECRRepo.RepositoryUri + SNSTopicArn: + Description: ARN of the SNS topic. + Value: !Ref SNSTopic + PipelineName: + Description: Name of the CodePipeline. + Value: !Ref CodePipeline + CodeBuildProjectName: + Description: Name of the CodeBuild project. + Value: !Ref CodeBuildProject diff --git a/docs/deployment-terraform.md b/docs/deployment-terraform.md index 7f22f024..a4c64f21 100644 --- a/docs/deployment-terraform.md +++ b/docs/deployment-terraform.md @@ -28,7 +28,7 @@ Kubernetes Deployment for Simple Golang API - ECS task definition and service creation for staging environment - CloudWatch Event rule and target for manual approval notification - Outputs for the staging and production URLs, build number, and semantic versions for staging and production. - - Using custom image AWS CodeBuild `devopscorner/cicd:4.0` + - Using custom image AWS CodeBuild `devopscorner/cicd:codebuild-4.0` ``` terraform { @@ -73,7 +73,7 @@ resource "aws_codebuild_project" "bookstore-prod" { environment { compute_type = "BUILD_GENERAL1_SMALL" # image = "aws/codebuild/standard:5.0" - image = "devopscorner/cicd:4.0" + image = "devopscorner/cicd:codebuild-4.0" type = "LINUX_CONTAINER" environment_variable { name = "AWS_REGION" @@ -110,7 +110,7 @@ resource "aws_codebuild_project" "bookstore-staging" { environment { compute_type = "BUILD_GENERAL1_SMALL" # image = "aws/codebuild/standard:5.0" - image = "devopscorner/cicd:4.0" + image = "devopscorner/cicd:codebuild-4.0" type = "LINUX_CONTAINER" environment_variable { name = "AWS_REGION" diff --git a/terraform/cicd-terraform-aws-codecommit.tf b/terraform/cicd-terraform-aws-codecommit.tf new file mode 100644 index 00000000..12c2dc5c --- /dev/null +++ b/terraform/cicd-terraform-aws-codecommit.tf @@ -0,0 +1,279 @@ +### Golang Terraform Pipeline (AWS CodeCommit, AWS CodeBuild, AWS Pipeline, Amazon SNS) ### + +# This script includes: +# - ECR repository creation +# - SNS topic creation and subscription +# - CodeBuild project creation for production and staging environments +# - CodePipeline creation with four stages: Source, Build, ManualApproval, and Deploy (two times) +# - ECS task definition and service creation for staging environment +# - CloudWatch Event rule and target for manual approval notification +# - Outputs for the staging and production URLs, build number, and semantic versions for staging and production. +# - Using custom image AWS CodeBuild `devopscorner/cicd:codebuild-4.0` + +terraform { + required_version = ">= 1.0.9" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 3.63.0, < 4.0" + } + } +} + +provider "aws" { + region = "ap-southeast-1" +} + +data "aws_caller_identity" "current" {} + +resource "aws_ecr_repository" "bookstore" { + name = "devopscorner/bookstore" +} + +resource "aws_sns_topic" "bookstore-topic" { + name = "bookstore-topic" +} + +resource "aws_sns_topic_subscription" "bookstore-subscription" { + topic_arn = aws_sns_topic.bookstore-topic.arn + protocol = "email" + endpoint = "support@devopscorner.id" +} + +resource "aws_codebuild_project" "bookstore-prod" { + name = "bookstore-prod" + description = "My Golang app for production" + service_role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/CodeBuildServiceRole" + build_timeout = "60" + artifacts { + type = "NO_ARTIFACTS" + } + environment { + compute_type = "BUILD_GENERAL1_SMALL" + # image = "aws/codebuild/standard:5.0" + image = "devopscorner/cicd:codebuild-4.0" + type = "LINUX_CONTAINER" + environment_variable { + name = "AWS_REGION" + value = "ap-southeast-1" + } + environment_variable { + name = "ENVIRONMENT" + value = "prod" + } + environment_variable { + name = "ECR_REPOSITORY_URI" + value = aws_ecr_repository.bookstore.repository_url + } + } + source { + type = "CODECOMMIT" + location = "your-repo-name" + git_clone_depth = 1 + buildspec = file("${path.module}/buildspec.yaml") + } +} + +resource "aws_codebuild_project" "bookstore-staging" { + name = "bookstore-staging" + description = "My Golang app for staging" + service_role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/CodeBuildServiceRole" + build_timeout = "60" + artifacts { + type = "NO_ARTIFACTS" + } + environment { + compute_type = "BUILD_GENERAL1_SMALL" + # image = "aws/codebuild/standard:5.0" + image = "devopscorner/cicd:codebuild-4.0" + type = "LINUX_CONTAINER" + environment_variable { + name = "AWS_REGION" + value = "ap-southeast-1" + } + environment_variable { + name = "ENVIRONMENT" + value = "staging" + } + environment_variable { + name = "ECR_REPOSITORY_URI" + value = aws_ecr_repository.bookstore.repository_url + } + } + source { + type = "CODECOMMIT" + location = "your-repo-name" + git_clone_depth = 1 + buildspec = file("${path.module}/buildspec.yaml") + } +} + +resource "aws_codepipeline" "bookstore" { + name = "bookstore" + role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/CodePipelineServiceRole" + artifact_store { + type = "S3" + location = "your-artifact-store-bucket" + encryption_key { + type = "KMS" + id = "your-kms-key-id" + } + } + + stage { + name = "Source" + action { + name = "Source" + category = "Source" + owner = "AWS" + provider = "CodeCommit" + version = "1" + output_artifacts = ["SourceArtifact"] + configuration = { + RepositoryName = "your-repo-name" + BranchName = "main" + } + } + } + + stage { + name = "Build" + actions { + name = "Build" + category = "Build" + owner = "AWS" + provider = "CodeBuild" + version = "1" + input_artifacts = ["SourceArtifact"] + output_artifacts = ["BuildArtifact"] + configuration = { + ProjectName = aws_codebuild_project.bookstore-staging.name + } + } + } + + stage { + name = "ManualApproval" + actions { + name = "ManualApproval" + category = "Approval" + owner = "AWS" + provider = "Manual" + version = "1" + input_artifacts = ["BuildArtifact"] + configuration = { + NotificationArn = aws_sns_topic.bookstore-topic.arn + CustomData = "Do you want to deploy the latest changes to production?" + } + } + } + + stage { + name = "Deploy-Staging" + actions { + name = "Deploy-Staging" + category = "Deploy" + owner = "AWS" + provider = "ECS" + version = "1" + input_artifacts = ["BuildArtifact"] + configuration = { + ClusterName = "bookstore-cluster" + ServiceName = "bookstore-staging" + FileName = "imagedefinitions.json" + ImageUri = "${aws_ecr_repository.bookstore.repository_url}:${var.semver_staging}" + } + } + } + + stage { + name = "Deploy-Prod" + actions { + name = "Deploy-Prod" + category = "Deploy" + owner = "AWS" + provider = "ECS" + version = "1" + input_artifacts = ["BuildArtifact"] + configuration = { + ClusterName = "bookstore-cluster" + ServiceName = "bookstore-prod" + FileName = "imagedefinitions.json" + ImageUri = "${aws_ecr_repository.bookstore.repository_url}:${var.semver_prod}" + } + condition { + type = "StartsWith" + variable = "ApprovalStatus" + value = "Approved" + } + } + } +} + +resource "aws_ecs_task_definition" "bookstore" { + family = "bookstore" + container_definitions = jsonencode([{ + name = "bookstore" + image = "${aws_ecr_repository.bookstore.repository_url}:${var.semver_staging}" + cpu = 512 + memory_reservation = 512 + port_mappings = { + container_port = 8080 + host_port = 0 + } + }]) +} + +resource "aws_ecs_service" "bookstore-staging" { + name = "bookstore-staging" + cluster = "bookstore-cluster" + task_definition = aws_ecs_task_definition.bookstore.arn + desired_count = 2 + launch_type = "EC2" + load_balancer { + target_group_arn = "your-target-group-arn" + container_name = "bookstore" + container_port = 8080 + } +} + +resource "aws_cloudwatch_event_rule" "bookstore-manual-approval" { + name = "bookstore-manual-approval" + description = "Manual approval rule for my Golang app" + event_pattern = jsonencode({ + source = ["aws.codepipeline"] + detail_type = ["CodePipeline Action Execution State Change"] + detail = { + pipeline = ["${aws_codepipeline.bookstore.name}"] + stage = ["ManualApproval"] + action = ["ManualApproval"] + state = ["STARTED", "SUCCEEDED", "FAILED"] + } + }) +} + +resource "aws_cloudwatch_event_target" "bookstore-manual-approval" { + rule = aws_cloudwatch_event_rule.bookstore-manual-approval.name + arn = aws_sns_topic.bookstore-topic.arn +} + +output "bookstore-staging-url" { + value = aws_alb.bookstore-staging.dns_name +} + +output "bookstore-prod-url" { + value = aws_alb.bookstore-prod.dns_name +} + +output "bookstore-build-number" { + value = aws_codebuild_project.bookstore-staging.build_number +} + +output "bookstore-semver-staging" { + value = var.semver_staging +} + +output "bookstore-semver-prod" { + value = var.semver_prod +} diff --git a/terraform/cicd-terraform.tf b/terraform/cicd-terraform-github.tf similarity index 91% rename from terraform/cicd-terraform.tf rename to terraform/cicd-terraform-github.tf index 3fe3261b..2ec0d431 100644 --- a/terraform/cicd-terraform.tf +++ b/terraform/cicd-terraform-github.tf @@ -8,7 +8,7 @@ # - ECS task definition and service creation for staging environment # - CloudWatch Event rule and target for manual approval notification # - Outputs for the staging and production URLs, build number, and semantic versions for staging and production. -# - Using custom image AWS CodeBuild `devopscorner/cicd:4.0` +# - Using custom image AWS CodeBuild `devopscorner/cicd:codebuild-4.0` terraform { required_version = ">= 1.0.9" @@ -28,7 +28,7 @@ provider "aws" { data "aws_caller_identity" "current" {} resource "aws_ecr_repository" "bookstore" { - name = "bookstore" + name = "devopscorner/bookstore" } resource "aws_sns_topic" "bookstore-topic" { @@ -38,7 +38,7 @@ resource "aws_sns_topic" "bookstore-topic" { resource "aws_sns_topic_subscription" "bookstore-subscription" { topic_arn = aws_sns_topic.bookstore-topic.arn protocol = "email" - endpoint = "youremail@example.com" + endpoint = "support@devopscorner.id" } resource "aws_codebuild_project" "bookstore-prod" { @@ -52,7 +52,7 @@ resource "aws_codebuild_project" "bookstore-prod" { environment { compute_type = "BUILD_GENERAL1_SMALL" # image = "aws/codebuild/standard:5.0" - image = "devopscorner/cicd:4.0" + image = "devopscorner/cicd:codebuild-4.0" type = "LINUX_CONTAINER" environment_variable { name = "AWS_REGION" @@ -89,7 +89,7 @@ resource "aws_codebuild_project" "bookstore-staging" { environment { compute_type = "BUILD_GENERAL1_SMALL" # image = "aws/codebuild/standard:5.0" - image = "devopscorner/cicd:4.0" + image = "devopscorner/cicd:codebuild-4.0" type = "LINUX_CONTAINER" environment_variable { name = "AWS_REGION" @@ -121,7 +121,6 @@ resource "aws_codepipeline" "bookstore" { artifact_store { type = "S3" location = "your-artifact-store-bucket" - encryption_key { type = "KMS" id = "your-kms-key-id" @@ -188,7 +187,7 @@ resource "aws_codepipeline" "bookstore" { version = "1" input_artifacts = ["BuildArtifact"] configuration = { - ClusterName = "my-ecs-cluster" + ClusterName = "bookstore-cluster" ServiceName = "bookstore-staging" FileName = "imagedefinitions.json" ImageUri = "${aws_ecr_repository.bookstore.repository_url}:${var.semver_staging}" @@ -206,7 +205,7 @@ resource "aws_codepipeline" "bookstore" { version = "1" input_artifacts = ["BuildArtifact"] configuration = { - ClusterName = "my-ecs-cluster" + ClusterName = "bookstore-cluster" ServiceName = "bookstore-prod" FileName = "imagedefinitions.json" ImageUri = "${aws_ecr_repository.bookstore.repository_url}:${var.semver_prod}" @@ -222,22 +221,21 @@ resource "aws_codepipeline" "bookstore" { resource "aws_ecs_task_definition" "bookstore" { family = "bookstore" - container_definitions = jsonencode([ - { - name = "bookstore" - image = "${aws_ecr_repository.bookstore.repository_url}:${var.semver_staging}" - cpu = 512 - memory_reservation = 512 - port_mappings = { - container_port = 8080 - host_port = 0 - } + container_definitions = jsonencode([{ + name = "bookstore" + image = "${aws_ecr_repository.bookstore.repository_url}:${var.semver_staging}" + cpu = 512 + memory_reservation = 512 + port_mappings = { + container_port = 8080 + host_port = 0 + } }]) } resource "aws_ecs_service" "bookstore-staging" { name = "bookstore-staging" - cluster = "my-ecs-cluster" + cluster = "bookstore-cluster" task_definition = aws_ecs_task_definition.bookstore.arn desired_count = 2 launch_type = "EC2" @@ -286,4 +284,4 @@ output "bookstore-semver-staging" { output "bookstore-semver-prod" { value = var.semver_prod -} \ No newline at end of file +} From 1a149a031c16a5ac5ea4b0bd9fb1e62e849788cf Mon Sep 17 00:00:00 2001 From: Dwi Fahni Denni Date: Thu, 16 Feb 2023 08:26:43 +0700 Subject: [PATCH 12/12] Added cloudformation script --- ...> cicd-cloudformation-aws-codecommit.yaml} | 0 ...ub.yml => cicd-cloudformation-github.yaml} | 0 terraform/cicd-terraform.tf | 289 ------------------ 3 files changed, 289 deletions(-) rename cloudformation/{cicd-cloudformation-aws-codecommit.yml => cicd-cloudformation-aws-codecommit.yaml} (100%) rename cloudformation/{cicd-cloudformation-github.yml => cicd-cloudformation-github.yaml} (100%) delete mode 100644 terraform/cicd-terraform.tf diff --git a/cloudformation/cicd-cloudformation-aws-codecommit.yml b/cloudformation/cicd-cloudformation-aws-codecommit.yaml similarity index 100% rename from cloudformation/cicd-cloudformation-aws-codecommit.yml rename to cloudformation/cicd-cloudformation-aws-codecommit.yaml diff --git a/cloudformation/cicd-cloudformation-github.yml b/cloudformation/cicd-cloudformation-github.yaml similarity index 100% rename from cloudformation/cicd-cloudformation-github.yml rename to cloudformation/cicd-cloudformation-github.yaml diff --git a/terraform/cicd-terraform.tf b/terraform/cicd-terraform.tf deleted file mode 100644 index 3fe3261b..00000000 --- a/terraform/cicd-terraform.tf +++ /dev/null @@ -1,289 +0,0 @@ -### Golang Terraform Pipeline (GitHub, AWS CodeBuild, AWS Pipeline, Amazon SNS) ### - -# This script includes: -# - ECR repository creation -# - SNS topic creation and subscription -# - CodeBuild project creation for production and staging environments -# - CodePipeline creation with four stages: Source, Build, ManualApproval, and Deploy (two times) -# - ECS task definition and service creation for staging environment -# - CloudWatch Event rule and target for manual approval notification -# - Outputs for the staging and production URLs, build number, and semantic versions for staging and production. -# - Using custom image AWS CodeBuild `devopscorner/cicd:4.0` - -terraform { - required_version = ">= 1.0.9" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 3.63.0, < 4.0" - } - } -} - -provider "aws" { - region = "ap-southeast-1" -} - -data "aws_caller_identity" "current" {} - -resource "aws_ecr_repository" "bookstore" { - name = "bookstore" -} - -resource "aws_sns_topic" "bookstore-topic" { - name = "bookstore-topic" -} - -resource "aws_sns_topic_subscription" "bookstore-subscription" { - topic_arn = aws_sns_topic.bookstore-topic.arn - protocol = "email" - endpoint = "youremail@example.com" -} - -resource "aws_codebuild_project" "bookstore-prod" { - name = "bookstore-prod" - description = "My Golang app for production" - service_role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/CodeBuildServiceRole" - build_timeout = "60" - artifacts { - type = "NO_ARTIFACTS" - } - environment { - compute_type = "BUILD_GENERAL1_SMALL" - # image = "aws/codebuild/standard:5.0" - image = "devopscorner/cicd:4.0" - type = "LINUX_CONTAINER" - environment_variable { - name = "AWS_REGION" - value = "ap-southeast-1" - } - environment_variable { - name = "ENVIRONMENT" - value = "prod" - } - environment_variable { - name = "ECR_REPOSITORY_URI" - value = aws_ecr_repository.bookstore.repository_url - } - } - source { - type = "GITHUB" - location = "https://github.com/devopscorner/golang-deployment" - git_clone_depth = 1 - buildspec = file("${path.module}/buildspec.yaml") - auth { - type = "OAUTH" - } - } -} - -resource "aws_codebuild_project" "bookstore-staging" { - name = "bookstore-staging" - description = "My Golang app for staging" - service_role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/CodeBuildServiceRole" - build_timeout = "60" - artifacts { - type = "NO_ARTIFACTS" - } - environment { - compute_type = "BUILD_GENERAL1_SMALL" - # image = "aws/codebuild/standard:5.0" - image = "devopscorner/cicd:4.0" - type = "LINUX_CONTAINER" - environment_variable { - name = "AWS_REGION" - value = "ap-southeast-1" - } - environment_variable { - name = "ENVIRONMENT" - value = "staging" - } - environment_variable { - name = "ECR_REPOSITORY_URI" - value = aws_ecr_repository.bookstore.repository_url - } - } - source { - type = "GITHUB" - location = "https://github.com/devopscorner/golang-deployment" - git_clone_depth = 1 - buildspec = file("${path.module}/buildspec.yaml") - auth { - type = "OAUTH" - } - } -} - -resource "aws_codepipeline" "bookstore" { - name = "bookstore" - role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/CodePipelineServiceRole" - artifact_store { - type = "S3" - location = "your-artifact-store-bucket" - - encryption_key { - type = "KMS" - id = "your-kms-key-id" - } - } - - stage { - name = "Source" - action { - name = "Source" - category = "Source" - owner = "AWS" - provider = "GitHub" - version = "1" - output_artifacts = ["SourceArtifact"] - configuration = { - Owner = "your-username" - Repo = "your-repo" - Branch = "main" - OAuthToken = "your-github-token" - } - } - } - - stage { - name = "Build" - actions { - name = "Build" - category = "Build" - owner = "AWS" - provider = "CodeBuild" - version = "1" - input_artifacts = ["SourceArtifact"] - output_artifacts = ["BuildArtifact"] - configuration = { - ProjectName = aws_codebuild_project.bookstore-staging.name - } - } - } - - stage { - name = "ManualApproval" - actions { - name = "ManualApproval" - category = "Approval" - owner = "AWS" - provider = "Manual" - version = "1" - input_artifacts = ["BuildArtifact"] - configuration = { - NotificationArn = aws_sns_topic.bookstore-topic.arn - CustomData = "Do you want to deploy the latest changes to production?" - } - } - } - - stage { - name = "Deploy-Staging" - actions { - name = "Deploy-Staging" - category = "Deploy" - owner = "AWS" - provider = "ECS" - version = "1" - input_artifacts = ["BuildArtifact"] - configuration = { - ClusterName = "my-ecs-cluster" - ServiceName = "bookstore-staging" - FileName = "imagedefinitions.json" - ImageUri = "${aws_ecr_repository.bookstore.repository_url}:${var.semver_staging}" - } - } - } - - stage { - name = "Deploy-Prod" - actions { - name = "Deploy-Prod" - category = "Deploy" - owner = "AWS" - provider = "ECS" - version = "1" - input_artifacts = ["BuildArtifact"] - configuration = { - ClusterName = "my-ecs-cluster" - ServiceName = "bookstore-prod" - FileName = "imagedefinitions.json" - ImageUri = "${aws_ecr_repository.bookstore.repository_url}:${var.semver_prod}" - } - condition { - type = "StartsWith" - variable = "ApprovalStatus" - value = "Approved" - } - } - } -} - -resource "aws_ecs_task_definition" "bookstore" { - family = "bookstore" - container_definitions = jsonencode([ - { - name = "bookstore" - image = "${aws_ecr_repository.bookstore.repository_url}:${var.semver_staging}" - cpu = 512 - memory_reservation = 512 - port_mappings = { - container_port = 8080 - host_port = 0 - } - }]) -} - -resource "aws_ecs_service" "bookstore-staging" { - name = "bookstore-staging" - cluster = "my-ecs-cluster" - task_definition = aws_ecs_task_definition.bookstore.arn - desired_count = 2 - launch_type = "EC2" - load_balancer { - target_group_arn = "your-target-group-arn" - container_name = "bookstore" - container_port = 8080 - } -} - -resource "aws_cloudwatch_event_rule" "bookstore-manual-approval" { - name = "bookstore-manual-approval" - description = "Manual approval rule for my Golang app" - event_pattern = jsonencode({ - source = ["aws.codepipeline"] - detail_type = ["CodePipeline Action Execution State Change"] - detail = { - pipeline = ["${aws_codepipeline.bookstore.name}"] - stage = ["ManualApproval"] - action = ["ManualApproval"] - state = ["STARTED", "SUCCEEDED", "FAILED"] - } - }) -} - -resource "aws_cloudwatch_event_target" "bookstore-manual-approval" { - rule = aws_cloudwatch_event_rule.bookstore-manual-approval.name - arn = aws_sns_topic.bookstore-topic.arn -} - -output "bookstore-staging-url" { - value = aws_alb.bookstore-staging.dns_name -} - -output "bookstore-prod-url" { - value = aws_alb.bookstore-prod.dns_name -} - -output "bookstore-build-number" { - value = aws_codebuild_project.bookstore-staging.build_number -} - -output "bookstore-semver-staging" { - value = var.semver_staging -} - -output "bookstore-semver-prod" { - value = var.semver_prod -} \ No newline at end of file