From 4748b09f4696ffcf90a6743143809b20bd1bd575 Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Mon, 23 Sep 2024 16:28:05 +0300 Subject: [PATCH 01/36] feat: add terraform config files --- .github/workflows/terraform.yaml | 65 +++++++++++++++++++++++++ .gitignore | 1 + README.md | 83 ++++++++++++++++++++++++++++++++ iam.tf | 26 ++++++++++ main.tf | 11 +++++ s3.tf | 33 +++++++++++++ terraform.tfvars.example | 5 ++ variables.tf | 29 +++++++++++ 8 files changed, 253 insertions(+) create mode 100644 .github/workflows/terraform.yaml create mode 100644 .gitignore create mode 100644 iam.tf create mode 100644 main.tf create mode 100644 s3.tf create mode 100644 terraform.tfvars.example create mode 100644 variables.tf diff --git a/.github/workflows/terraform.yaml b/.github/workflows/terraform.yaml new file mode 100644 index 0000000..80e99f8 --- /dev/null +++ b/.github/workflows/terraform.yaml @@ -0,0 +1,65 @@ +name: Terraform Deployment + +on: + pull_request: + branches: + - main + push: + branches: + - main + +jobs: + terraform-check: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v1 + + - name: Terraform Format Check + run: terraform fmt -check + + terraform-plan: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v1 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GithubActionsRole + aws-region: ${{ secrets.AWS_REGION }} + + - name: Terraform Init + run: terraform init + + - name: Terraform Plan + run: terraform plan + + terraform-apply: + if: github.event_name == 'push' + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v1 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GithubActionsRole + aws-region: ${{ secrets.AWS_REGION }} + + - name: Terraform Init + run: terraform init + + - name: Terraform Apply + run: terraform apply -auto-approve diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..174bcac --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +terraform.tfvars \ No newline at end of file diff --git a/README.md b/README.md index 8b64261..f064eba 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,85 @@ # rsschool-devops-course-tasks + Repo for RS School AWS DevOps course + +## Overview + +This repository contains Terraform configuration files to set up AWS infrastructure, including an S3 bucket for storing Terraform state and an IAM role for GitHub Actions. + +## Prerequisites + +- [AWS CLI](https://aws.amazon.com/cli/) installed and configured +- [Terraform](https://www.terraform.io/downloads.html) installed +- A GitHub account + +## Installation + +1. **Clone the Repository**: + +```bash +1. git clone https://github.com/elian-cheng/rsschool-devops-course-tasks.git +2. cd rsschool-devops-course-tasks +``` + +2. **Configure the AWS CLI**: + Ensure you have configured the AWS CLI with the credentials of your IAM user: + +```bash +aws configure +``` + +3. **Set Up GitHub Secrets**: + In your GitHub repository, navigate to Settings > Secrets and Variables > Actions. Add the following secrets: + - AWS_ACCOUNT_ID: Your AWS account ID. + - AWS_REGION: The AWS region (e.g., us-east-1). + +# Terraform Configuration + +## Variables + +The Terraform configurations use the following variables: + +- **aws_region**: The AWS region where resources will be deployed. +- **terraform_state_bucket**: The S3 bucket for storing Terraform state. +- **account_id**: Your AWS account ID. +- **github_org**: Your GitHub organization or username. +- **github_repo**: The name of your GitHub repository. + +## File Structure + +- **.github/workflows/terraform.yaml**: GitHub Actions workflow configuration. +- **main.tf**: Provider configuration and backend settings. +- **variables.tf**: Variable definitions. +- **s3.tf**: S3 bucket resource definition. +- **iam.tf**: IAM role resource definition. + +## Workflow Overview + +The GitHub Actions workflow consists of three jobs: + +- **terraform-check**: Checks the formatting of Terraform files. +- **terraform-plan**: Initializes Terraform and creates an execution plan. +- **terraform-apply**: Applies the changes to the AWS infrastructure (only on push to main). + +## Usage + +To deploy the infrastructure: + +1. Make changes to your Terraform files. +2. Push your changes to the main branch or create a pull request. +3. Monitor the Actions tab for the workflow run status. + +## Testing Your Workflow + +To verify that your GitHub Actions workflow works: + +1. **Push Your Changes**: + - Make any change (e.g., a comment in a README file) and push it to the main branch or create a pull request. + +2. **Check the Workflow Run**: + - Navigate to the Actions tab in your GitHub repository to view the list of workflow runs. + - Click on the most recent run to view details, checking the status of each job. + +3. **Verify Outputs**: + - Ensure that the terraform plan job runs successfully and outputs the planned actions correctly. + - If you're pushing to the main branch, check that terraform apply executes without errors. diff --git a/iam.tf b/iam.tf new file mode 100644 index 0000000..d77d600 --- /dev/null +++ b/iam.tf @@ -0,0 +1,26 @@ +resource "aws_iam_role" "github_actions_role" { + name = "GithubActionsRole" + + assume_role_policy = < Date: Mon, 23 Sep 2024 16:45:05 +0300 Subject: [PATCH 02/36] fix: variables formatting --- variables.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variables.tf b/variables.tf index 527e628..a12de35 100644 --- a/variables.tf +++ b/variables.tf @@ -13,7 +13,7 @@ variable "terraform_state_bucket" { variable "account_id" { description = "The AWS Account ID where resources will be deployed" type = string - default = "656732674839" + default = "656732674839" } variable "github_org" { From 18908e3c061b825d0ca96423688ac64160afc9c1 Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Mon, 23 Sep 2024 17:04:55 +0300 Subject: [PATCH 03/36] fix: aws configs --- .github/workflows/terraform.yaml | 8 ++++---- README.md | 7 ++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/terraform.yaml b/.github/workflows/terraform.yaml index 80e99f8..541e1d0 100644 --- a/.github/workflows/terraform.yaml +++ b/.github/workflows/terraform.yaml @@ -33,8 +33,8 @@ jobs: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v2 with: - role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GithubActionsRole - aws-region: ${{ secrets.AWS_REGION }} + role-to-assume: arn:aws:iam::656732674839:role/GithubActionsRole + aws-region: eu-north-1 - name: Terraform Init run: terraform init @@ -55,8 +55,8 @@ jobs: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v2 with: - role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GithubActionsRole - aws-region: ${{ secrets.AWS_REGION }} + role-to-assume: arn:aws:iam::656732674839:role/GithubActionsRole + aws-region: eu-north-1 - name: Terraform Init run: terraform init diff --git a/README.md b/README.md index f064eba..b03001e 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,6 @@ This repository contains Terraform configuration files to set up AWS infrastruct aws configure ``` -3. **Set Up GitHub Secrets**: - In your GitHub repository, navigate to Settings > Secrets and Variables > Actions. Add the following secrets: - - AWS_ACCOUNT_ID: Your AWS account ID. - - AWS_REGION: The AWS region (e.g., us-east-1). - # Terraform Configuration ## Variables @@ -74,9 +69,11 @@ To deploy the infrastructure: To verify that your GitHub Actions workflow works: 1. **Push Your Changes**: + - Make any change (e.g., a comment in a README file) and push it to the main branch or create a pull request. 2. **Check the Workflow Run**: + - Navigate to the Actions tab in your GitHub repository to view the list of workflow runs. - Click on the most recent run to view details, checking the status of each job. From 96171d29b76a38cba7b50819dc9180bf0a9b2665 Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Mon, 23 Sep 2024 17:30:06 +0300 Subject: [PATCH 04/36] fix: workflow credentials access --- .github/workflows/terraform.yaml | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/.github/workflows/terraform.yaml b/.github/workflows/terraform.yaml index 541e1d0..181d964 100644 --- a/.github/workflows/terraform.yaml +++ b/.github/workflows/terraform.yaml @@ -8,15 +8,19 @@ on: branches: - main +permissions: + id-token: write + contents: read + jobs: terraform-check: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup Terraform - uses: hashicorp/setup-terraform@v1 + uses: hashicorp/setup-terraform@v3 - name: Terraform Format Check run: terraform fmt -check @@ -25,16 +29,17 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup Terraform - uses: hashicorp/setup-terraform@v1 + uses: hashicorp/setup-terraform@v3 - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v2 + uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::656732674839:role/GithubActionsRole aws-region: eu-north-1 + audience: sts.amazonaws.com - name: Terraform Init run: terraform init @@ -47,16 +52,17 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup Terraform - uses: hashicorp/setup-terraform@v1 + uses: hashicorp/setup-terraform@v3 - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v2 + uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::656732674839:role/GithubActionsRole aws-region: eu-north-1 + audience: sts.amazonaws.com - name: Terraform Init run: terraform init From 962d8573c5b1b381ecd6189b8f8d32b886075006 Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Mon, 23 Sep 2024 17:35:04 +0300 Subject: [PATCH 05/36] fix: replace variables in main.tf --- main.tf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main.tf b/main.tf index 40dca13..d7535c7 100644 --- a/main.tf +++ b/main.tf @@ -1,11 +1,11 @@ provider "aws" { - region = var.aws_region + region = "eu-north-1" } terraform { backend "s3" { - bucket = var.terraform_state_bucket + bucket = "elian-terraform-s3-state" key = "terraform.tfstate" - region = var.aws_region + region = "eu-north-1" } } From d1949903bcace383832821f2950d265d49c6300a Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Mon, 23 Sep 2024 17:42:05 +0300 Subject: [PATCH 06/36] feat: add workflow secrets --- .github/workflows/terraform.yaml | 9 ++++----- README.md | 5 +++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/terraform.yaml b/.github/workflows/terraform.yaml index 181d964..c0a6788 100644 --- a/.github/workflows/terraform.yaml +++ b/.github/workflows/terraform.yaml @@ -37,8 +37,8 @@ jobs: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: - role-to-assume: arn:aws:iam::656732674839:role/GithubActionsRole - aws-region: eu-north-1 + role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GithubActionsRole + aws-region: ${{ secrets.AWS_REGION }} audience: sts.amazonaws.com - name: Terraform Init @@ -48,7 +48,6 @@ jobs: run: terraform plan terraform-apply: - if: github.event_name == 'push' runs-on: ubuntu-latest steps: - name: Checkout code @@ -60,8 +59,8 @@ jobs: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: - role-to-assume: arn:aws:iam::656732674839:role/GithubActionsRole - aws-region: eu-north-1 + role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GithubActionsRole + aws-region: ${{ secrets.AWS_REGION }} audience: sts.amazonaws.com - name: Terraform Init diff --git a/README.md b/README.md index b03001e..4a23d70 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,11 @@ This repository contains Terraform configuration files to set up AWS infrastruct aws configure ``` +3. **Set Up GitHub Secrets**: + In your GitHub repository, navigate to Settings > Secrets and Variables > Actions. Add the following secrets: + - AWS_ACCOUNT_ID: Your AWS account ID. + - AWS_REGION: The AWS region (e.g., us-east-1). + # Terraform Configuration ## Variables From 032f9881b6a4ec17735f34fca7298b8707944cd2 Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Mon, 23 Sep 2024 17:55:16 +0300 Subject: [PATCH 07/36] fix: skip exising bucket and iam role creation --- iam.tf | 6 ++++++ s3.tf | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/iam.tf b/iam.tf index d77d600..bb88bee 100644 --- a/iam.tf +++ b/iam.tf @@ -1,4 +1,10 @@ +data "aws_iam_role" "github_actions_role" { + name = "GithubActionsRole" +} + resource "aws_iam_role" "github_actions_role" { + # Only create if the data source doesn't return a role + count = length(data.aws_iam_role.github_actions_role) == 0 ? 1 : 0 name = "GithubActionsRole" assume_role_policy = < Date: Mon, 23 Sep 2024 17:59:19 +0300 Subject: [PATCH 08/36] fix: format check --- iam.tf | 40 ++++++++++++++++++++-------------------- s3.tf | 8 ++++---- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/iam.tf b/iam.tf index bb88bee..bf8548b 100644 --- a/iam.tf +++ b/iam.tf @@ -3,30 +3,30 @@ data "aws_iam_role" "github_actions_role" { } resource "aws_iam_role" "github_actions_role" { - # Only create if the data source doesn't return a role + # Only create if the data source doesn't return a role count = length(data.aws_iam_role.github_actions_role) == 0 ? 1 : 0 - name = "GithubActionsRole" + name = "GithubActionsRole" assume_role_policy = < Date: Mon, 23 Sep 2024 18:03:06 +0300 Subject: [PATCH 09/36] fix: s3 bucket count --- s3.tf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/s3.tf b/s3.tf index 35d2747..c3bece6 100644 --- a/s3.tf +++ b/s3.tf @@ -1,6 +1,7 @@ data "aws_s3_bucket" "terraform_state" { bucket = var.terraform_state_bucket } + resource "aws_s3_bucket" "terraform_state" { # Only create if the data source doesn't return a bucket count = length(data.aws_s3_bucket.terraform_state) == 0 ? 1 : 0 @@ -13,8 +14,7 @@ resource "aws_s3_bucket" "terraform_state" { resource "aws_s3_bucket_versioning" "enabled" { count = length(data.aws_s3_bucket.terraform_state) == 0 ? 1 : 0 - bucket = aws_s3_bucket.terraform_state.id - + bucket = aws_s3_bucket.terraform_state[count.index].id # Updated to use count.index versioning_configuration { status = "Enabled" } @@ -22,7 +22,7 @@ resource "aws_s3_bucket_versioning" "enabled" { resource "aws_s3_bucket_server_side_encryption_configuration" "default" { count = length(data.aws_s3_bucket.terraform_state) == 0 ? 1 : 0 - bucket = aws_s3_bucket.terraform_state.id + bucket = aws_s3_bucket.terraform_state[count.index].id # Updated to use count.index rule { apply_server_side_encryption_by_default { @@ -33,7 +33,7 @@ resource "aws_s3_bucket_server_side_encryption_configuration" "default" { resource "aws_s3_bucket_public_access_block" "public_access" { count = length(data.aws_s3_bucket.terraform_state) == 0 ? 1 : 0 - bucket = aws_s3_bucket.terraform_state.id + bucket = aws_s3_bucket.terraform_state[count.index].id # Updated to use count.index block_public_acls = true block_public_policy = true ignore_public_acls = true From ecbb6a001b1c7ca32d2d23acadb8bbd5fafc144e Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Mon, 30 Sep 2024 14:36:50 +0300 Subject: [PATCH 10/36] fix: attach role policies --- iam.tf | 62 +++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/iam.tf b/iam.tf index bf8548b..2d779ee 100644 --- a/iam.tf +++ b/iam.tf @@ -1,32 +1,54 @@ data "aws_iam_role" "github_actions_role" { name = "GithubActionsRole" } +resource "aws_iam_openid_connect_provider" "GitHub" { + url = "https://token.actions.githubusercontent.com" + client_id_list = ["sts.amazonaws.com"] + thumbprint_list = ["1c58a3a8518e8759bf075b76b750d4f2df264fcd"] +} -resource "aws_iam_role" "github_actions_role" { - # Only create if the data source doesn't return a role +resource "aws_iam_role" "GitHub_Actions" { count = length(data.aws_iam_role.github_actions_role) == 0 ? 1 : 0 - name = "GithubActionsRole" + name = "GitHub_Actions" assume_role_policy = < Date: Mon, 30 Sep 2024 14:40:17 +0300 Subject: [PATCH 11/36] fix: existing role check --- iam.tf | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/iam.tf b/iam.tf index 2d779ee..71f1e01 100644 --- a/iam.tf +++ b/iam.tf @@ -1,12 +1,18 @@ data "aws_iam_role" "github_actions_role" { name = "GithubActionsRole" } +data "aws_iam_openid_connect_provider" "existing" { + url = "https://token.actions.githubusercontent.com" +} + resource "aws_iam_openid_connect_provider" "GitHub" { + count = length(data.aws_iam_openid_connect_provider.existing) == 0 ? 1 : 0 url = "https://token.actions.githubusercontent.com" client_id_list = ["sts.amazonaws.com"] - thumbprint_list = ["1c58a3a8518e8759bf075b76b750d4f2df264fcd"] + thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1", "1c58a3a8518e8759bf075b76b750d4f2df264fcd"] } + resource "aws_iam_role" "GitHub_Actions" { count = length(data.aws_iam_role.github_actions_role) == 0 ? 1 : 0 name = "GitHub_Actions" From 4f4fe92a8410a8e416ee36d6cac100e201c20bbf Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Fri, 11 Oct 2024 14:15:06 +0300 Subject: [PATCH 12/36] feat: add K8s cluster basic networking infrastructure --- .../{terraform.yaml => terraform.yml} | 141 ++++++------- .gitignore | 2 +- README.md | 186 ++++++++++-------- bastion.tf | 42 ++++ ec2-private.tf | 21 ++ iam.tf | 60 ------ ig.tf | 7 + main.tf | 38 ++-- nacl.tf | 71 +++++++ nat-gateway.tf | 13 ++ outputs.tf | 24 +++ routing.tf | 44 +++++ s3.tf | 41 ---- sg.tf | 75 +++++++ subnets.tf | 25 +++ terraform.tfvars | 10 + terraform.tfvars.example | 5 - variables.tf | 83 +++++--- vpc.tf | 11 ++ 19 files changed, 595 insertions(+), 304 deletions(-) rename .github/workflows/{terraform.yaml => terraform.yml} (85%) create mode 100644 bastion.tf create mode 100644 ec2-private.tf delete mode 100644 iam.tf create mode 100644 ig.tf create mode 100644 nacl.tf create mode 100644 nat-gateway.tf create mode 100644 outputs.tf create mode 100644 routing.tf delete mode 100644 s3.tf create mode 100644 sg.tf create mode 100644 subnets.tf create mode 100644 terraform.tfvars delete mode 100644 terraform.tfvars.example create mode 100644 vpc.tf diff --git a/.github/workflows/terraform.yaml b/.github/workflows/terraform.yml similarity index 85% rename from .github/workflows/terraform.yaml rename to .github/workflows/terraform.yml index c0a6788..8dc9d8c 100644 --- a/.github/workflows/terraform.yaml +++ b/.github/workflows/terraform.yml @@ -1,70 +1,71 @@ -name: Terraform Deployment - -on: - pull_request: - branches: - - main - push: - branches: - - main - -permissions: - id-token: write - contents: read - -jobs: - terraform-check: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Terraform - uses: hashicorp/setup-terraform@v3 - - - name: Terraform Format Check - run: terraform fmt -check - - terraform-plan: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Terraform - uses: hashicorp/setup-terraform@v3 - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GithubActionsRole - aws-region: ${{ secrets.AWS_REGION }} - audience: sts.amazonaws.com - - - name: Terraform Init - run: terraform init - - - name: Terraform Plan - run: terraform plan - - terraform-apply: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Terraform - uses: hashicorp/setup-terraform@v3 - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GithubActionsRole - aws-region: ${{ secrets.AWS_REGION }} - audience: sts.amazonaws.com - - - name: Terraform Init - run: terraform init - - - name: Terraform Apply - run: terraform apply -auto-approve +name: Terraform Deployment + +on: + pull_request: + branches: + - main + push: + branches: + - main + +permissions: + id-token: write + contents: read + +jobs: + terraform-check: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + - name: Terraform Format Check + run: terraform fmt -check + + terraform-plan: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GithubActionsRole + aws-region: ${{ secrets.AWS_REGION }} + audience: sts.amazonaws.com + + - name: Terraform Init + run: terraform init + + - name: Terraform Plan + run: terraform plan -var="private_key=${{ secrets.AWS_EC2_PRIVATE_KEY }}" + + terraform-apply: + runs-on: ubuntu-latest + needs: terraform-plan + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GithubActionsRole + aws-region: ${{ secrets.AWS_REGION }} + audience: sts.amazonaws.com + + - name: Terraform Init + run: terraform init + + - name: Terraform Apply + run: terraform apply -auto-approve -var="private_key=${{ secrets.AWS_EC2_PRIVATE_KEY }}" diff --git a/.gitignore b/.gitignore index 174bcac..2eea525 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -terraform.tfvars \ No newline at end of file +.env \ No newline at end of file diff --git a/README.md b/README.md index 4a23d70..b0001bb 100644 --- a/README.md +++ b/README.md @@ -1,87 +1,99 @@ -# rsschool-devops-course-tasks - -Repo for RS School AWS DevOps course - -## Overview - -This repository contains Terraform configuration files to set up AWS infrastructure, including an S3 bucket for storing Terraform state and an IAM role for GitHub Actions. - -## Prerequisites - -- [AWS CLI](https://aws.amazon.com/cli/) installed and configured -- [Terraform](https://www.terraform.io/downloads.html) installed -- A GitHub account - -## Installation - -1. **Clone the Repository**: - -```bash -1. git clone https://github.com/elian-cheng/rsschool-devops-course-tasks.git -2. cd rsschool-devops-course-tasks -``` - -2. **Configure the AWS CLI**: - Ensure you have configured the AWS CLI with the credentials of your IAM user: - -```bash -aws configure -``` - -3. **Set Up GitHub Secrets**: - In your GitHub repository, navigate to Settings > Secrets and Variables > Actions. Add the following secrets: - - AWS_ACCOUNT_ID: Your AWS account ID. - - AWS_REGION: The AWS region (e.g., us-east-1). - -# Terraform Configuration - -## Variables - -The Terraform configurations use the following variables: - -- **aws_region**: The AWS region where resources will be deployed. -- **terraform_state_bucket**: The S3 bucket for storing Terraform state. -- **account_id**: Your AWS account ID. -- **github_org**: Your GitHub organization or username. -- **github_repo**: The name of your GitHub repository. - -## File Structure - -- **.github/workflows/terraform.yaml**: GitHub Actions workflow configuration. -- **main.tf**: Provider configuration and backend settings. -- **variables.tf**: Variable definitions. -- **s3.tf**: S3 bucket resource definition. -- **iam.tf**: IAM role resource definition. - -## Workflow Overview - -The GitHub Actions workflow consists of three jobs: - -- **terraform-check**: Checks the formatting of Terraform files. -- **terraform-plan**: Initializes Terraform and creates an execution plan. -- **terraform-apply**: Applies the changes to the AWS infrastructure (only on push to main). - -## Usage - -To deploy the infrastructure: - -1. Make changes to your Terraform files. -2. Push your changes to the main branch or create a pull request. -3. Monitor the Actions tab for the workflow run status. - -## Testing Your Workflow - -To verify that your GitHub Actions workflow works: - -1. **Push Your Changes**: - - - Make any change (e.g., a comment in a README file) and push it to the main branch or create a pull request. - -2. **Check the Workflow Run**: - - - Navigate to the Actions tab in your GitHub repository to view the list of workflow runs. - - Click on the most recent run to view details, checking the status of each job. - -3. **Verify Outputs**: - - Ensure that the terraform plan job runs successfully and outputs the planned actions correctly. - - If you're pushing to the main branch, check that terraform apply executes without errors. +# rsschool-devops-course-tasks + +Repo for RS School AWS DevOps course + +## Overview + +This repository contains Terraform configuration files to set up basic networking infrastructure for a Kubernetes cluster in AWS. The setup includes a VPC, public and private subnets, routing configurations, a NAT Gateway, and optional resources like a bastion host. An S3 bucket is used to store the Terraform state, and an IAM role is configured to allow GitHub Actions to interact with AWS. + +## Prerequisites + +- [AWS CLI](https://aws.amazon.com/cli/) installed and configured +- [Terraform](https://www.terraform.io/downloads.html) installed +- A GitHub account + +## Installation + +1. **Clone the Repository**: + +```bash +1. git clone https://github.com/elian-cheng/rsschool-devops-course-tasks.git +2. cd rsschool-devops-course-tasks +``` + +2. **Configure the AWS CLI**: + Ensure you have configured the AWS CLI with the credentials of your IAM user: + +```bash +aws configure +``` + +3. **Set Up GitHub Secrets**: + In your GitHub repository, navigate to Settings > Secrets and Variables > Actions. Add the following secrets: + - AWS_ACCOUNT_ID: Your AWS account ID. + - AWS_REGION: The AWS region (e.g., us-east-1). + - AWS_EC2_PRIVATE_KEY: private key to connect to private subnet instances from bastion host + +# Terraform Configuration + +## Variables + +The Terraform configurations use the following variables: + +- **aws_region**: The AWS region where resources will be deployed. +- **terraform_state_bucket**: The S3 bucket for storing Terraform state. +- **account_id**: Your AWS account ID. +- **github_org**: Your GitHub organization or username. +- **github_repo**: The name of your GitHub repository. +- **vpc_cidr**: CIDR block for the VPC. +- **public_subnet_cidrs**: CIDR blocks for the public subnets. +- **private_subnet_cidrs**: CIDR blocks for the private subnets. +- **private_key**: The private key used for SSH access to the private instance. + +## File Structure + +- **.github/workflows/terraform.yml**: GitHub Actions workflow configuration. +- **main.tf**: Provider configuration and backend settings. +- **variables.tf**: Variable definitions. +- **vpc.tf**: VPC definition. +- **subnets.tf**: Subnets definitions. +- **sg.tf**: Security groups definitions. +- **routing.tf**: Routes definitions. +- **nacl.tf**: Network ACL definitions. +- **ig.tf**: Internet gateway definition. +- **bastion.tf**: Bastion host definition. +- **ec2-private.tf**: Private instance definition (in private subnet). +- **outputs.tf**: Resources outputs. + +## Workflow Overview + +The GitHub Actions workflow consists of three jobs: + +- **terraform-check**: Checks the formatting of Terraform files. +- **terraform-plan**: Initializes Terraform and creates an execution plan. +- **terraform-apply**: Applies the changes to the AWS infrastructure (only on push to main). + +## Usage + +To deploy the infrastructure: + +1. Make changes to your Terraform files. +2. Push your changes to the main branch or create a pull request. +3. Monitor the Actions tab for the workflow run status. + +## Testing Your Workflow + +To verify that your GitHub Actions workflow works: + +1. **Push Your Changes**: + + - Make any change (e.g., a comment in a README file) and push it to the main branch or create a pull request. + +2. **Check the Workflow Run**: + + - Navigate to the Actions tab in your GitHub repository to view the list of workflow runs. + - Click on the most recent run to view details, checking the status of each job. + +3. **Verify Outputs**: + - Ensure that the terraform plan job runs successfully and outputs the planned actions correctly. + - If you're pushing to the main branch, check that terraform apply executes without errors. diff --git a/bastion.tf b/bastion.tf new file mode 100644 index 0000000..4bd27a0 --- /dev/null +++ b/bastion.tf @@ -0,0 +1,42 @@ +data "aws_ami" "ubuntu_ami" { + most_recent = true + owners = ["099720109477"] + + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"] + } +} + +resource "aws_key_pair" "ec2_auth_key" { + key_name = "aws-ec2" + public_key = file("~/.ssh/aws-ec2.pub") +} + + +resource "aws_instance" "K8S_bastion_host" { + ami = data.aws_ami.ubuntu_ami.id + instance_type = "t3.micro" + availability_zone = element(data.aws_availability_zones.available.names, 0) + subnet_id = aws_subnet.K8S_public_subnet[0].id + + associate_public_ip_address = true + + key_name = aws_key_pair.ec2_auth_key.id + vpc_security_group_ids = [aws_security_group.K8S_public_sg.id] + + user_data = <<-EOF + #!/bin/bash + sudo apt update -y + mkdir -p /home/ubuntu/.ssh + echo "${var.private_key}" > /home/ubuntu/.ssh/aws-ec2 + chmod 400 /home/ubuntu/.ssh/aws-ec2 + chown ubuntu:ubuntu /home/ubuntu/.ssh/aws-ec2 + EOF + + + tags = { + Name = "K8S Bastion Host" + } +} + diff --git a/ec2-private.tf b/ec2-private.tf new file mode 100644 index 0000000..37ac064 --- /dev/null +++ b/ec2-private.tf @@ -0,0 +1,21 @@ +resource "aws_instance" "K8S_private_instance" { + ami = data.aws_ami.ubuntu_ami.id + instance_type = "t3.micro" + subnet_id = aws_subnet.K8S_private_subnet[0].id # Use the first private subnet + availability_zone = element(data.aws_availability_zones.available.names, 0) + + key_name = aws_key_pair.ec2_auth_key.id + vpc_security_group_ids = [aws_security_group.K8S_private_sg.id] + + user_data = <<-EOF + #!/bin/bash + sudo apt update -y + sudo apt install apache2 -y + sudo systemctl start apache2 + sudo bash -c 'echo Private Instance Server > /var/www/html/index.html' + EOF + + tags = { + Name = "K8S Private Instance" + } +} diff --git a/iam.tf b/iam.tf deleted file mode 100644 index 71f1e01..0000000 --- a/iam.tf +++ /dev/null @@ -1,60 +0,0 @@ -data "aws_iam_role" "github_actions_role" { - name = "GithubActionsRole" -} -data "aws_iam_openid_connect_provider" "existing" { - url = "https://token.actions.githubusercontent.com" -} - -resource "aws_iam_openid_connect_provider" "GitHub" { - count = length(data.aws_iam_openid_connect_provider.existing) == 0 ? 1 : 0 - url = "https://token.actions.githubusercontent.com" - client_id_list = ["sts.amazonaws.com"] - thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1", "1c58a3a8518e8759bf075b76b750d4f2df264fcd"] -} - - -resource "aws_iam_role" "GitHub_Actions" { - count = length(data.aws_iam_role.github_actions_role) == 0 ? 1 : 0 - name = "GitHub_Actions" - - assume_role_policy = < Date: Fri, 11 Oct 2024 14:19:20 +0300 Subject: [PATCH 13/36] fix: duplicate data error --- main.tf | 2 +- sg.tf | 2 +- subnets.tf | 2 -- terraform.tfvars | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/main.tf b/main.tf index d1761ab..5dfb677 100644 --- a/main.tf +++ b/main.tf @@ -13,7 +13,7 @@ terraform { data "aws_availability_zones" "available" {} locals { - name = "${basename(path.cwd)}" + name = basename(path.cwd) region = "eu-north-1" vpc_cidr = var.vpc_cidr diff --git a/sg.tf b/sg.tf index 33ef7cc..a7089fe 100644 --- a/sg.tf +++ b/sg.tf @@ -64,7 +64,7 @@ resource "aws_security_group" "K8S_private_sg" { egress { from_port = 0 to_port = 0 - protocol = -1 + protocol = -1 cidr_blocks = ["0.0.0.0/0"] description = "Allow all outbound traffic" } diff --git a/subnets.tf b/subnets.tf index ee7bcae..08a7bf0 100644 --- a/subnets.tf +++ b/subnets.tf @@ -21,5 +21,3 @@ resource "aws_subnet" "K8S_private_subnet" { Name = "K8S Private Subnet ${count.index + 1}" } } - -data "aws_availability_zones" "available" {} diff --git a/terraform.tfvars b/terraform.tfvars index 5bd327f..c297e6d 100644 --- a/terraform.tfvars +++ b/terraform.tfvars @@ -7,4 +7,4 @@ vpc_cidr = "10.0.0.0/16" public_subnet_cidrs = ["10.0.1.0/24", "10.0.2.0/24"] private_subnet_cidrs = ["10.0.3.0/24", "10.0.4.0/24"] # using GitHub Secrets for the private key -private_key = "" \ No newline at end of file +private_key = "" \ No newline at end of file From f1f960f0d7336593d5b5050bf3243037a7360aae Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Fri, 11 Oct 2024 14:24:55 +0300 Subject: [PATCH 14/36] fix: add cache clearing --- .github/workflows/terraform.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/terraform.yml b/.github/workflows/terraform.yml index 8dc9d8c..f92ea47 100644 --- a/.github/workflows/terraform.yml +++ b/.github/workflows/terraform.yml @@ -41,6 +41,9 @@ jobs: aws-region: ${{ secrets.AWS_REGION }} audience: sts.amazonaws.com + - name: Clear Terraform cache + run: rm -rf .terraform + - name: Terraform Init run: terraform init @@ -64,6 +67,9 @@ jobs: aws-region: ${{ secrets.AWS_REGION }} audience: sts.amazonaws.com + - name: Clear Terraform cache + run: rm -rf .terraform + - name: Terraform Init run: terraform init From f4a834b99d0850e277d83faa45dd46e6250ae78a Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Fri, 11 Oct 2024 14:34:49 +0300 Subject: [PATCH 15/36] fix: add public SSH key to GitHub secrets --- .github/workflows/terraform.yml | 4 ++-- README.md | 2 ++ bastion.tf | 3 +-- terraform.tfvars | 5 +++-- variables.tf | 5 +++++ 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/terraform.yml b/.github/workflows/terraform.yml index f92ea47..f35f85b 100644 --- a/.github/workflows/terraform.yml +++ b/.github/workflows/terraform.yml @@ -48,7 +48,7 @@ jobs: run: terraform init - name: Terraform Plan - run: terraform plan -var="private_key=${{ secrets.AWS_EC2_PRIVATE_KEY }}" + run: terraform plan -var="private_key=${{ secrets.AWS_EC2_PRIVATE_KEY }}" -var="public_key=${{ secrets.AWS_EC2_PUBLIC_KEY }}" terraform-apply: runs-on: ubuntu-latest @@ -74,4 +74,4 @@ jobs: run: terraform init - name: Terraform Apply - run: terraform apply -auto-approve -var="private_key=${{ secrets.AWS_EC2_PRIVATE_KEY }}" + run: terraform apply -auto-approve -var="private_key=${{ secrets.AWS_EC2_PRIVATE_KEY }}" -var="public_key=${{ secrets.AWS_EC2_PUBLIC_KEY }}" diff --git a/README.md b/README.md index b0001bb..dbb4ab0 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ aws configure - AWS_ACCOUNT_ID: Your AWS account ID. - AWS_REGION: The AWS region (e.g., us-east-1). - AWS_EC2_PRIVATE_KEY: private key to connect to private subnet instances from bastion host + - AWS_EC2_PUBLIC_KEY: public key to connect to instances # Terraform Configuration @@ -49,6 +50,7 @@ The Terraform configurations use the following variables: - **public_subnet_cidrs**: CIDR blocks for the public subnets. - **private_subnet_cidrs**: CIDR blocks for the private subnets. - **private_key**: The private key used for SSH access to the private instance. +- **public_key**: The public key used for SSH access to instances. ## File Structure diff --git a/bastion.tf b/bastion.tf index 4bd27a0..06b8a55 100644 --- a/bastion.tf +++ b/bastion.tf @@ -10,10 +10,9 @@ data "aws_ami" "ubuntu_ami" { resource "aws_key_pair" "ec2_auth_key" { key_name = "aws-ec2" - public_key = file("~/.ssh/aws-ec2.pub") + public_key = var.public_key } - resource "aws_instance" "K8S_bastion_host" { ami = data.aws_ami.ubuntu_ami.id instance_type = "t3.micro" diff --git a/terraform.tfvars b/terraform.tfvars index c297e6d..7a7e8eb 100644 --- a/terraform.tfvars +++ b/terraform.tfvars @@ -6,5 +6,6 @@ terraform_state_bucket = "elian-terraform-s3-state" vpc_cidr = "10.0.0.0/16" public_subnet_cidrs = ["10.0.1.0/24", "10.0.2.0/24"] private_subnet_cidrs = ["10.0.3.0/24", "10.0.4.0/24"] -# using GitHub Secrets for the private key -private_key = "" \ No newline at end of file +# using GitHub Secrets for the private and public key +private_key = "" +public_key = "" \ No newline at end of file diff --git a/variables.tf b/variables.tf index 35d911b..5bfb153 100644 --- a/variables.tf +++ b/variables.tf @@ -52,3 +52,8 @@ variable "private_key" { sensitive = true # Mark as sensitive to avoid showing the value in logs } +variable "public_key" { + description = "The public key for the EC2 instance" + type = string + sensitive = true +} From c3d65a10526d6fb49c65e05b88410f9223acc0a4 Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Fri, 11 Oct 2024 14:54:52 +0300 Subject: [PATCH 16/36] fix: terraform formatting --- bastion.tf | 2 +- variables.tf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bastion.tf b/bastion.tf index 06b8a55..5616f2f 100644 --- a/bastion.tf +++ b/bastion.tf @@ -10,7 +10,7 @@ data "aws_ami" "ubuntu_ami" { resource "aws_key_pair" "ec2_auth_key" { key_name = "aws-ec2" - public_key = var.public_key + public_key = var.public_key } resource "aws_instance" "K8S_bastion_host" { diff --git a/variables.tf b/variables.tf index 5bfb153..43752ff 100644 --- a/variables.tf +++ b/variables.tf @@ -55,5 +55,5 @@ variable "private_key" { variable "public_key" { description = "The public key for the EC2 instance" type = string - sensitive = true + sensitive = true } From 9693e90d05fb554a4dbee376cd82dd9d518939cb Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Sun, 20 Oct 2024 11:36:46 +0300 Subject: [PATCH 17/36] feat: add K3s cluster configs --- .github/workflows/terraform.yml | 4 +- .gitignore | 6 +- README.md | 105 ++++++++++++++++++++++++++-- ec2-private.tf => ec2-K8s-master.tf | 15 ++-- bastion.tf => ec2-bastion.tf | 16 ++--- outputs.tf | 6 +- scripts/install_k3s.sh | 33 +++++++++ terraform.tfvars | 3 +- variables.tf | 5 -- 9 files changed, 159 insertions(+), 34 deletions(-) rename ec2-private.tf => ec2-K8s-master.tf (52%) rename bastion.tf => ec2-bastion.tf (63%) create mode 100644 scripts/install_k3s.sh diff --git a/.github/workflows/terraform.yml b/.github/workflows/terraform.yml index f35f85b..f92ea47 100644 --- a/.github/workflows/terraform.yml +++ b/.github/workflows/terraform.yml @@ -48,7 +48,7 @@ jobs: run: terraform init - name: Terraform Plan - run: terraform plan -var="private_key=${{ secrets.AWS_EC2_PRIVATE_KEY }}" -var="public_key=${{ secrets.AWS_EC2_PUBLIC_KEY }}" + run: terraform plan -var="private_key=${{ secrets.AWS_EC2_PRIVATE_KEY }}" terraform-apply: runs-on: ubuntu-latest @@ -74,4 +74,4 @@ jobs: run: terraform init - name: Terraform Apply - run: terraform apply -auto-approve -var="private_key=${{ secrets.AWS_EC2_PRIVATE_KEY }}" -var="public_key=${{ secrets.AWS_EC2_PUBLIC_KEY }}" + run: terraform apply -auto-approve -var="private_key=${{ secrets.AWS_EC2_PRIVATE_KEY }}" diff --git a/.gitignore b/.gitignore index 2eea525..2f784ac 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ -.env \ No newline at end of file +.env +.idea/ +.terraform/ +.terraform.lock.hcl +*.pem \ No newline at end of file diff --git a/README.md b/README.md index dbb4ab0..1602aa9 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,6 @@ aws configure - AWS_ACCOUNT_ID: Your AWS account ID. - AWS_REGION: The AWS region (e.g., us-east-1). - AWS_EC2_PRIVATE_KEY: private key to connect to private subnet instances from bastion host - - AWS_EC2_PUBLIC_KEY: public key to connect to instances # Terraform Configuration @@ -50,7 +49,6 @@ The Terraform configurations use the following variables: - **public_subnet_cidrs**: CIDR blocks for the public subnets. - **private_subnet_cidrs**: CIDR blocks for the private subnets. - **private_key**: The private key used for SSH access to the private instance. -- **public_key**: The public key used for SSH access to instances. ## File Structure @@ -63,8 +61,8 @@ The Terraform configurations use the following variables: - **routing.tf**: Routes definitions. - **nacl.tf**: Network ACL definitions. - **ig.tf**: Internet gateway definition. -- **bastion.tf**: Bastion host definition. -- **ec2-private.tf**: Private instance definition (in private subnet). +- **ec2-bastion.tf**: Bastion host definition. +- **ec2-K8s-master.tf**: Private instance definition (in private subnet) for K8s master. - **outputs.tf**: Resources outputs. ## Workflow Overview @@ -97,5 +95,104 @@ To verify that your GitHub Actions workflow works: - Click on the most recent run to view details, checking the status of each job. 3. **Verify Outputs**: + - Ensure that the terraform plan job runs successfully and outputs the planned actions correctly. - If you're pushing to the main branch, check that terraform apply executes without errors. + +4. SSH into the Bastion Host: + +```bash +ssh -i path/to/your/private_key.pem ubuntu@ +``` + +5. SSH into the K3s Master Node from the Bastion Host: + +```bash +ssh -i /home/ubuntu/.ssh/k8s-cluster.pem ubuntu@ +``` + +6. Copy the k3s.yaml File to your local machine: + +```bash +scp -i /home/ubuntu/.ssh/k8s-cluster.pem ubuntu@:/etc/rancher/k3s/k3s.yaml /path/to/local/directory/k3s.yaml +``` + +7. Set the KUBECONFIG Environment Variable on your local machine: + +```bash +export KUBECONFIG=/path/to/local/directory/k3s.yaml +``` + +8. Verify the Cluster: + +```bash +kubectl get nodes +``` + +9. Deploy the Simple Workload: + +```bash +kubectl apply -f https://k8s.io/examples/pods/simple-pod.yaml +``` + +10. Set Up Monitoring with Prometheus and Grafana: + +```bash +kubectl apply -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/master/bundle.yaml +``` + +```bash +kubectl apply -f https://raw.githubusercontent.com/grafana/helm-charts/main/charts/grafana/templates/deployment.yaml +``` + +## Monitoring + +To see the monitoring setup with Prometheus and Grafana actually working, follow these steps: + +1. Get Prometheus Service Information: + kubectl get svc -n monitoring + +2. Port Forward Prometheus Service: + kubectl port-forward -n monitoring svc/prometheus-operated 9090:9090 + +3. Access Prometheus: + Open your web browser and go to http://localhost:9090. +4. Get Grafana Service Information: + kubectl get svc -n monitoring + +5. Port Forward Grafana Service: + kubectl port-forward -n monitoring svc/grafana 3000:3000 +6. Access Grafana: + Open your web browser and go to http://localhost:3000 + +7. Log in to Grafana: + Default Credentials: + Username: admin + Password: admin (or check the Grafana deployment for the actual password) + Change Password: You will be prompted to change the password on the first login. +8. Add Prometheus as a Data Source in Grafana: + +- Navigate to Configuration: + Click on the gear icon (⚙️) on the left sidebar. + Select "Data Sources". +- Add Data Source: + Click "Add data source". + Select "Prometheus". + Set the URL to http://prometheus-operated.monitoring.svc.cluster.local:9090 (or use the appropriate service name and namespace). +- Save & Test: Click "Save & Test" to verify the connection. 9. Create Dashboards in Grafana: +- Import Pre-built Dashboards: + Click on the "+" icon on the left sidebar. + Select "Import". + Use a dashboard ID from Grafana's [dashboard repository](https://grafana.com/grafana/dashboards) (e.g., 6417 for Kubernetes cluster monitoring). +- Configure the Dashboard: + Set the Prometheus data source you added earlier. + Click "Import". + +9. Verify Monitoring + +- View Metrics in Prometheus: + Use the Prometheus web UI to query metrics. + Example query: up to see if all nodes are up. +- View Dashboards in Grafana: + Navigate to the imported dashboard. + Verify that metrics and graphs are displayed correctly. diff --git a/ec2-private.tf b/ec2-K8s-master.tf similarity index 52% rename from ec2-private.tf rename to ec2-K8s-master.tf index 37ac064..16f7c88 100644 --- a/ec2-private.tf +++ b/ec2-K8s-master.tf @@ -1,21 +1,22 @@ -resource "aws_instance" "K8S_private_instance" { +resource "aws_instance" "K8S_K3S_master" { ami = data.aws_ami.ubuntu_ami.id instance_type = "t3.micro" subnet_id = aws_subnet.K8S_private_subnet[0].id # Use the first private subnet availability_zone = element(data.aws_availability_zones.available.names, 0) - key_name = aws_key_pair.ec2_auth_key.id + key_name = k8s-cluster vpc_security_group_ids = [aws_security_group.K8S_private_sg.id] user_data = <<-EOF #!/bin/bash - sudo apt update -y - sudo apt install apache2 -y - sudo systemctl start apache2 - sudo bash -c 'echo Private Instance Server > /var/www/html/index.html' + sudo apt-get update -y + sudo apt-get install -y curl + sudo ufw disable + curl -sfL https://get.k3s.io | sh - + sudo chmod 644 /etc/rancher/k3s/k3s.yaml EOF tags = { - Name = "K8S Private Instance" + Name = "K8S K3s Master" } } diff --git a/bastion.tf b/ec2-bastion.tf similarity index 63% rename from bastion.tf rename to ec2-bastion.tf index 5616f2f..bd8d9fe 100644 --- a/bastion.tf +++ b/ec2-bastion.tf @@ -8,11 +8,6 @@ data "aws_ami" "ubuntu_ami" { } } -resource "aws_key_pair" "ec2_auth_key" { - key_name = "aws-ec2" - public_key = var.public_key -} - resource "aws_instance" "K8S_bastion_host" { ami = data.aws_ami.ubuntu_ami.id instance_type = "t3.micro" @@ -21,16 +16,17 @@ resource "aws_instance" "K8S_bastion_host" { associate_public_ip_address = true - key_name = aws_key_pair.ec2_auth_key.id + key_name = k8s-cluster vpc_security_group_ids = [aws_security_group.K8S_public_sg.id] user_data = <<-EOF #!/bin/bash - sudo apt update -y + sudo apt-get update -y + sudo ufw disable mkdir -p /home/ubuntu/.ssh - echo "${var.private_key}" > /home/ubuntu/.ssh/aws-ec2 - chmod 400 /home/ubuntu/.ssh/aws-ec2 - chown ubuntu:ubuntu /home/ubuntu/.ssh/aws-ec2 + echo "${var.private_key}" > /home/ubuntu/.ssh/k8s-cluster.pem + chmod 400 /home/ubuntu/.ssh/k8s-cluster.pem + chown ubuntu:ubuntu /home/ubuntu/.ssh/k8s-cluster.pem EOF diff --git a/outputs.tf b/outputs.tf index 626df1f..f6fc29a 100644 --- a/outputs.tf +++ b/outputs.tf @@ -18,7 +18,7 @@ output "bastion_host_public_ip" { value = aws_instance.K8S_bastion_host.public_ip } -output "private_ec2_private_ip" { - description = "The private IP of the private instance" - value = aws_instance.K8S_private_instance.private_ip +output "K8s_master_private_ip" { + description = "The private IP of the private instance K8s master" + value = aws_instance.K8S_K3S_master.private_ip } diff --git a/scripts/install_k3s.sh b/scripts/install_k3s.sh new file mode 100644 index 0000000..31c5782 --- /dev/null +++ b/scripts/install_k3s.sh @@ -0,0 +1,33 @@ +#!/bin/bash +set -o errexit # abort on nonzero exit status +set -o nounset # abort on unbound variable +set -o pipefail # don't hide errors within pipes + +# https://www.digitalocean.com/community/tutorials/how-to-setup-k3s-kubernetes-cluster-on-ubuntu +sudo apt-get update + +# installing k3s +sudo ufw disable +curl -sfL https://get.k3s.io | sh - + +# make kubeconfig avaliable without sudo +sudo chmod 644 /etc/rancher/k3s/k3s.yaml + +# check statuse +sudo systemctl status k3s +kubectl get all -n kube-system + +# check nodes +kubectl get nodes + +# deploy simple workload +kubectl apply -f https://k8s.io/examples/pods/simple-pod.yaml + +# Install autocompletion in kubectl +# https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#enable-shell-autocompletion +sudo apt install -y bash-completion +echo 'source <(kubectl completion bash)' >>~/.bashrc +source ~/.bashrc +kubectl get pods + + diff --git a/terraform.tfvars b/terraform.tfvars index 7a7e8eb..36ce78a 100644 --- a/terraform.tfvars +++ b/terraform.tfvars @@ -7,5 +7,4 @@ vpc_cidr = "10.0.0.0/16" public_subnet_cidrs = ["10.0.1.0/24", "10.0.2.0/24"] private_subnet_cidrs = ["10.0.3.0/24", "10.0.4.0/24"] # using GitHub Secrets for the private and public key -private_key = "" -public_key = "" \ No newline at end of file +private_key = "" \ No newline at end of file diff --git a/variables.tf b/variables.tf index 43752ff..35d911b 100644 --- a/variables.tf +++ b/variables.tf @@ -52,8 +52,3 @@ variable "private_key" { sensitive = true # Mark as sensitive to avoid showing the value in logs } -variable "public_key" { - description = "The public key for the EC2 instance" - type = string - sensitive = true -} From 4970fc078680faa7c78d267613ad98dac6fe7659 Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Sun, 20 Oct 2024 11:42:05 +0300 Subject: [PATCH 18/36] fix: key name --- ec2-K8s-master.tf | 2 +- ec2-bastion.tf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ec2-K8s-master.tf b/ec2-K8s-master.tf index 16f7c88..712ee62 100644 --- a/ec2-K8s-master.tf +++ b/ec2-K8s-master.tf @@ -4,7 +4,7 @@ resource "aws_instance" "K8S_K3S_master" { subnet_id = aws_subnet.K8S_private_subnet[0].id # Use the first private subnet availability_zone = element(data.aws_availability_zones.available.names, 0) - key_name = k8s-cluster + key_name = "k8s-cluster" vpc_security_group_ids = [aws_security_group.K8S_private_sg.id] user_data = <<-EOF diff --git a/ec2-bastion.tf b/ec2-bastion.tf index bd8d9fe..2028d5a 100644 --- a/ec2-bastion.tf +++ b/ec2-bastion.tf @@ -16,7 +16,7 @@ resource "aws_instance" "K8S_bastion_host" { associate_public_ip_address = true - key_name = k8s-cluster + key_name = "k8s-cluster" vpc_security_group_ids = [aws_security_group.K8S_public_sg.id] user_data = <<-EOF From 27c6e191cfeda1023608ff0ecc6b2e9a604b214f Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Sun, 20 Oct 2024 14:30:29 +0300 Subject: [PATCH 19/36] docs: update readme --- .gitignore | 3 +- README.md | 82 +++++++++++++++--------------------------------------- 2 files changed, 25 insertions(+), 60 deletions(-) diff --git a/.gitignore b/.gitignore index 2f784ac..ccf8093 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ .idea/ .terraform/ .terraform.lock.hcl -*.pem \ No newline at end of file +*.pem +k3s.yaml \ No newline at end of file diff --git a/README.md b/README.md index 1602aa9..8da8b9a 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ To verify that your GitHub Actions workflow works: - If you're pushing to the main branch, check that terraform apply executes without errors. 4. SSH into the Bastion Host: + ssh -i k8s-cluster.pem -L 6443:10.0.3.63:6443 ubuntu@13.60.79.204 ```bash ssh -i path/to/your/private_key.pem ubuntu@ @@ -113,86 +114,49 @@ ssh -i /home/ubuntu/.ssh/k8s-cluster.pem ubuntu@ 6. Copy the k3s.yaml File to your local machine: +Copy the file to the Bastion Host: + ```bash -scp -i /home/ubuntu/.ssh/k8s-cluster.pem ubuntu@:/etc/rancher/k3s/k3s.yaml /path/to/local/directory/k3s.yaml +scp /etc/rancher/k3s/k3s.yaml ubuntu@:/home/ubuntu/k3s.yaml ``` -7. Set the KUBECONFIG Environment Variable on your local machine: +Copy the k3s.yaml File to Your Local Machine: ```bash -export KUBECONFIG=/path/to/local/directory/k3s.yaml +scp -i path/to/your/private_key.pem ubuntu@:/home/ubuntu/k3s.yaml /path/to/local/directory/k3s.yaml ``` -8. Verify the Cluster: +7. Setup the SSH tunnel to connect to the K8s master node private instance via Bastion Host from your local machine: ```bash -kubectl get nodes +ssh -i path/to/your/private_key.pem -L 6443::6443 ubuntu@ ``` -9. Deploy the Simple Workload: +8. Set the KUBECONFIG Environment Variable on your local machine and verify the cluster (in another terminal, parallel to open SSH tunnel): ```bash -kubectl apply -f https://k8s.io/examples/pods/simple-pod.yaml +export KUBECONFIG=/path/to/local/directory/k3s.yaml ``` -10. Set Up Monitoring with Prometheus and Grafana: +Verify the Cluster: ```bash -kubectl apply -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/master/bundle.yaml +kubectl get nodes ``` +9. Deploy the Simple Workload: + ```bash -kubectl apply -f https://raw.githubusercontent.com/grafana/helm-charts/main/charts/grafana/templates/deployment.yaml +kubectl apply -f https://k8s.io/examples/pods/simple-pod.yaml ``` ## Monitoring -To see the monitoring setup with Prometheus and Grafana actually working, follow these steps: - -1. Get Prometheus Service Information: - kubectl get svc -n monitoring - -2. Port Forward Prometheus Service: - kubectl port-forward -n monitoring svc/prometheus-operated 9090:9090 - -3. Access Prometheus: - Open your web browser and go to http://localhost:9090. -4. Get Grafana Service Information: - kubectl get svc -n monitoring - -5. Port Forward Grafana Service: - kubectl port-forward -n monitoring svc/grafana 3000:3000 -6. Access Grafana: - Open your web browser and go to http://localhost:3000 - -7. Log in to Grafana: - Default Credentials: - Username: admin - Password: admin (or check the Grafana deployment for the actual password) - Change Password: You will be prompted to change the password on the first login. -8. Add Prometheus as a Data Source in Grafana: - -- Navigate to Configuration: - Click on the gear icon (⚙️) on the left sidebar. - Select "Data Sources". -- Add Data Source: - Click "Add data source". - Select "Prometheus". - Set the URL to http://prometheus-operated.monitoring.svc.cluster.local:9090 (or use the appropriate service name and namespace). -- Save & Test: Click "Save & Test" to verify the connection. 9. Create Dashboards in Grafana: -- Import Pre-built Dashboards: - Click on the "+" icon on the left sidebar. - Select "Import". - Use a dashboard ID from Grafana's [dashboard repository](https://grafana.com/grafana/dashboards) (e.g., 6417 for Kubernetes cluster monitoring). -- Configure the Dashboard: - Set the Prometheus data source you added earlier. - Click "Import". - -9. Verify Monitoring - -- View Metrics in Prometheus: - Use the Prometheus web UI to query metrics. - Example query: up to see if all nodes are up. -- View Dashboards in Grafana: - Navigate to the imported dashboard. - Verify that metrics and graphs are displayed correctly. +I use Cloud Grafana (with Prometheus) for the monitoring. Follow these steps: + +1. Make sure Helm is installed and properly configured on your machine. + https://helm.sh/docs/intro/install/ +2. Create a Grafana Cloud Account: + https://grafana.com/products/cloud/ +3. Create a new connection to monitor the K8s cluster and follow the instructions. + This would install and connect Grafana and Prometheus to your cluster. From f6fbe09726c15c687c1f71088a40ea87585602a1 Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Sun, 3 Nov 2024 18:17:08 +0200 Subject: [PATCH 20/36] feat: add jenkins install and configs --- README.md | 99 ++++++++++++++++++------- ec2-K8s-master.tf | 107 +++++++++++++++++++++++++-- ec2-bastion.tf | 37 --------- kubernetes/jenkins-cluster-role.yaml | 27 +++++++ kubernetes/jenkins-pv-pvc.yaml | 22 ++++++ scripts/install_k3s.sh | 103 +++++++++++++++++++++++++- sg.tf | 8 ++ variables.tf | 17 +++++ 8 files changed, 346 insertions(+), 74 deletions(-) delete mode 100644 ec2-bastion.tf create mode 100644 kubernetes/jenkins-cluster-role.yaml create mode 100644 kubernetes/jenkins-pv-pvc.yaml diff --git a/README.md b/README.md index 8da8b9a..1ea4bb2 100644 --- a/README.md +++ b/README.md @@ -99,64 +99,111 @@ To verify that your GitHub Actions workflow works: - Ensure that the terraform plan job runs successfully and outputs the planned actions correctly. - If you're pushing to the main branch, check that terraform apply executes without errors. -4. SSH into the Bastion Host: - ssh -i k8s-cluster.pem -L 6443:10.0.3.63:6443 ubuntu@13.60.79.204 +4. **Get the Public IP of Your K3s Master Node**: + + - Note the public IP address of your K3s master node. You can find this in your AWS EC2 dashboard under instances. + +5. **SSH into the K3s Master Node**: ```bash -ssh -i path/to/your/private_key.pem ubuntu@ +ssh -i path/to/your/private_key.pem ubuntu@ + ``` -5. SSH into the K3s Master Node from the Bastion Host: +Verify the K3s Installation: ```bash -ssh -i /home/ubuntu/.ssh/k8s-cluster.pem ubuntu@ +sudo systemctl status k3s + ``` -6. Copy the k3s.yaml File to your local machine: +6. **Copy the k3s.yaml File to your local machine:** -Copy the file to the Bastion Host: +```bash +scp -i path/to/your/private_key.pem ubuntu@:/etc/rancher/k3s/k3s.yaml /path/to/local/directory/k3s.yaml +``` + +7. **Set the KUBECONFIG Environment Variable on your local machine and verify the cluster (in another terminal, parallel to open SSH tunnel):** ```bash -scp /etc/rancher/k3s/k3s.yaml ubuntu@:/home/ubuntu/k3s.yaml +export KUBECONFIG=/path/to/local/directory/k3s.yaml ``` -Copy the k3s.yaml File to Your Local Machine: +8. **Verify the Cluster and Jenkins:** ```bash -scp -i path/to/your/private_key.pem ubuntu@:/home/ubuntu/k3s.yaml /path/to/local/directory/k3s.yaml +kubectl get nodes +``` + +```bash +kubectl get pods -n jenkins ``` -7. Setup the SSH tunnel to connect to the K8s master node private instance via Bastion Host from your local machine: +9. **Access Jenkins:** + Since we have set the service type to LoadBalancer, we should be able to access Jenkins via the public IP of our master node. + Retrieve the service details to get the external IP: ```bash -ssh -i path/to/your/private_key.pem -L 6443::6443 ubuntu@ +kubectl get svc -n jenkins ``` -8. Set the KUBECONFIG Environment Variable on your local machine and verify the cluster (in another terminal, parallel to open SSH tunnel): +Open a web browser and navigate to http://:8080. You should see the Jenkins setup wizard. + +10. **Unlock Jenkins:** + You’ll need the initial admin password to unlock Jenkins. Retrieve it by running: ```bash -export KUBECONFIG=/path/to/local/directory/k3s.yaml + kubectl exec -n jenkins -- cat /var/jenkins_home/secrets/initialAdminPassword +``` + +Copy the password and paste it into the Jenkins setup wizard to unlock Jenkins. + +11. **Create a Freestyle Project:** + Follow the setup wizard to install recommended plugins. + Create a new Freestyle project: + +- Name it something like "HelloWorld". +- In the build section, add an "Execute shell" build step with the following command: + +```bash +echo "Hello world" ``` -Verify the Cluster: +- Save the project and run it. + +12. **Verify the Build Output:** + After running the job, check the console output to ensure it shows "Hello world". + +13. **Check Persistent Volume Configuration:** + Ensure that the persistent volume (PV) and persistent volume claim (PVC) were created successfully: ```bash -kubectl get nodes +kubectl get pv +kubectl get pvc -n jenkins +``` + +14. **Verify your Helm installation by deploying and removing the Nginx chart from Bitnami:** + First, install the Nginx chart using Helm. You can run the following command to deploy the Nginx server: + +```bash +helm install my-nginx oci://registry-1.docker.io/bitnamicharts/nginx ``` -9. Deploy the Simple Workload: +Verify the Deployment: ```bash -kubectl apply -f https://k8s.io/examples/pods/simple-pod.yaml +kubectl get pods ``` -## Monitoring +Remove the Nginx Chart: + +```bash +helm uninstall my-nginx +``` -I use Cloud Grafana (with Prometheus) for the monitoring. Follow these steps: +Check that the Nginx resources have been removed: -1. Make sure Helm is installed and properly configured on your machine. - https://helm.sh/docs/intro/install/ -2. Create a Grafana Cloud Account: - https://grafana.com/products/cloud/ -3. Create a new connection to monitor the K8s cluster and follow the instructions. - This would install and connect Grafana and Prometheus to your cluster. +```bash +kubectl get pods +kubectl get svc +``` diff --git a/ec2-K8s-master.tf b/ec2-K8s-master.tf index 712ee62..da978ef 100644 --- a/ec2-K8s-master.tf +++ b/ec2-K8s-master.tf @@ -1,19 +1,110 @@ +data "aws_ami" "ubuntu_ami" { + most_recent = true + owners = ["099720109477"] + + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"] + } +} + resource "aws_instance" "K8S_K3S_master" { ami = data.aws_ami.ubuntu_ami.id - instance_type = "t3.micro" - subnet_id = aws_subnet.K8S_private_subnet[0].id # Use the first private subnet + instance_type = var.k8s_master_instance_type + subnet_id = aws_subnet.K8S_public_subnet[0].id availability_zone = element(data.aws_availability_zones.available.names, 0) - key_name = "k8s-cluster" - vpc_security_group_ids = [aws_security_group.K8S_private_sg.id] + associate_public_ip_address = true + + key_name = var.access_key_name + + vpc_security_group_ids = [aws_security_group.K8S_public_sg.id] + + root_block_device { + volume_size = var.k8s_master_node_disk.size + volume_type = var.k8s_master_node_disk.type + delete_on_termination = true + } user_data = <<-EOF #!/bin/bash + hostnamectl set-hostname "master-node" sudo apt-get update -y - sudo apt-get install -y curl - sudo ufw disable - curl -sfL https://get.k3s.io | sh - - sudo chmod 644 /etc/rancher/k3s/k3s.yaml + sudo apt-get install -y curl apt-transport-https + curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--tls-san $(curl -s 2ip.io)" sh - + sudo mkdir -p ~/.kube + sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config + sudo chown $(id -u):$(id -g) ~/.kube/config + kubectl create namespace jenkins + cat < /dev/null + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list + sudo apt-get update -y + sudo apt-get install -y helm + helm repo add jenkins https://charts.jenkins.io + helm repo update + mkdir -p /tmp/jenkins-volume + chown -R 1000:1000 /tmp/jenkins-volume + + helm install jenkins jenkins/jenkins --namespace jenkins \ + --set controller.serviceType=LoadBalancer \ + --set persistence.enabled=true \ + --set persistence.size=8Gi \ + --set persistence.existingClaim=jenkins-pvc + --set controller.installPlugins="cloudbees-credentials,git,workflow-aggregator,jacoco,jacoco,configuration-as-code" EOF tags = { diff --git a/ec2-bastion.tf b/ec2-bastion.tf deleted file mode 100644 index 2028d5a..0000000 --- a/ec2-bastion.tf +++ /dev/null @@ -1,37 +0,0 @@ -data "aws_ami" "ubuntu_ami" { - most_recent = true - owners = ["099720109477"] - - filter { - name = "name" - values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"] - } -} - -resource "aws_instance" "K8S_bastion_host" { - ami = data.aws_ami.ubuntu_ami.id - instance_type = "t3.micro" - availability_zone = element(data.aws_availability_zones.available.names, 0) - subnet_id = aws_subnet.K8S_public_subnet[0].id - - associate_public_ip_address = true - - key_name = "k8s-cluster" - vpc_security_group_ids = [aws_security_group.K8S_public_sg.id] - - user_data = <<-EOF - #!/bin/bash - sudo apt-get update -y - sudo ufw disable - mkdir -p /home/ubuntu/.ssh - echo "${var.private_key}" > /home/ubuntu/.ssh/k8s-cluster.pem - chmod 400 /home/ubuntu/.ssh/k8s-cluster.pem - chown ubuntu:ubuntu /home/ubuntu/.ssh/k8s-cluster.pem - EOF - - - tags = { - Name = "K8S Bastion Host" - } -} - diff --git a/kubernetes/jenkins-cluster-role.yaml b/kubernetes/jenkins-cluster-role.yaml new file mode 100644 index 0000000..b7a2b04 --- /dev/null +++ b/kubernetes/jenkins-cluster-role.yaml @@ -0,0 +1,27 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: jenkins + namespace: jenkins +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: jenkins +rules: + - apiGroups: ["*"] + resources: ["*"] + verbs: ["*"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: jenkins +subjects: + - kind: ServiceAccount + name: jenkins + namespace: jenkins +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: jenkins diff --git a/kubernetes/jenkins-pv-pvc.yaml b/kubernetes/jenkins-pv-pvc.yaml new file mode 100644 index 0000000..9471f47 --- /dev/null +++ b/kubernetes/jenkins-pv-pvc.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: jenkins-pv +spec: + capacity: + storage: 8Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/tmp/jenkins-volume" # Directory on the host where data will be stored +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: jenkins-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 8Gi diff --git a/scripts/install_k3s.sh b/scripts/install_k3s.sh index 31c5782..689996c 100644 --- a/scripts/install_k3s.sh +++ b/scripts/install_k3s.sh @@ -4,16 +4,113 @@ set -o nounset # abort on unbound variable set -o pipefail # don't hide errors within pipes # https://www.digitalocean.com/community/tutorials/how-to-setup-k3s-kubernetes-cluster-on-ubuntu -sudo apt-get update + # Set hostname +hostnamectl set-hostname "master-node" -# installing k3s +# Update and install dependencies +sudo apt-get update -y +sudo apt-get install -y curl apt-transport-https + +# Disable UFW firewall (optional, depending on your security requirements) sudo ufw disable +# installing k3s curl -sfL https://get.k3s.io | sh - +# OR +# Install K3s with external IP for API server access +curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--tls-san $(curl -s 2ip.io)" sh - + +# Configure kubeconfig for kubectl +sudo mkdir -p /home/ubuntu/.kube +sudo cp /etc/rancher/k3s/k3s.yaml /home/ubuntu/.kube/config +sudo chown $(id -u):$(id -g) ~/.kube/config + +# Create Jenkins namespace +kubectl create namespace jenkins + +# Define PersistentVolume and PersistentVolumeClaim in the script directly or download it +cat < /dev/null +echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list +sudo apt-get update -y +sudo apt-get install -y helm + +# Add Jenkins Helm chart repository +helm repo add jenkins https://charts.jenkins.io +helm repo update + +# Create Persistent Volume directory +mkdir -p /tmp/jenkins-volume +chown -R 1000:1000 /tmp/jenkins-volume + +# Install Jenkins using Helm with custom values including security plugins +helm install jenkins jenkins/jenkins --namespace jenkins \ + --set controller.serviceType=LoadBalancer \ + --set persistence.enabled=true \ + --set persistence.size=8Gi \ + --set persistence.existingClaim=jenkins-pvc \ + --set controller.installPlugins="cloudbees-credentials,git,workflow-aggregator,jacoco,jacoco,configuration-as-code" # make kubeconfig avaliable without sudo sudo chmod 644 /etc/rancher/k3s/k3s.yaml -# check statuse +# check status sudo systemctl status k3s kubectl get all -n kube-system diff --git a/sg.tf b/sg.tf index a7089fe..9862877 100644 --- a/sg.tf +++ b/sg.tf @@ -27,6 +27,14 @@ resource "aws_security_group" "K8S_public_sg" { description = "Allow inbound HTTPS" } + ingress { + from_port = 8080 + to_port = 8080 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] # Allow inbound traffic on port 8080 for Jenkins + description = "Allow inbound traffic to Jenkins" + } + ingress { from_port = -1 to_port = -1 diff --git a/variables.tf b/variables.tf index 35d911b..7c4a7d5 100644 --- a/variables.tf +++ b/variables.tf @@ -52,3 +52,20 @@ variable "private_key" { sensitive = true # Mark as sensitive to avoid showing the value in logs } +variable "k8s_master_instance_type" { + description = "instance type for k8s node" + default = "t3.small" +} + +variable "k8s_master_node_disk" { + description = "disk type for k8s node" + default = { + size = 30 + type = "gp3" + } +} + +variable "access_key_name" { + description = "The name of the SSH key pair to use for the instances" + default = "k8s-cluster" +} From 0d70ba680ef4e5088d1e9f0cfc91b35598233341 Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Sun, 3 Nov 2024 18:19:38 +0200 Subject: [PATCH 21/36] fix: remove bastion host reference --- outputs.tf | 5 ----- 1 file changed, 5 deletions(-) diff --git a/outputs.tf b/outputs.tf index f6fc29a..c547498 100644 --- a/outputs.tf +++ b/outputs.tf @@ -13,11 +13,6 @@ output "private_subnets" { value = aws_subnet.K8S_private_subnet[*].id } -output "bastion_host_public_ip" { - description = "The public IP of the bastion host" - value = aws_instance.K8S_bastion_host.public_ip -} - output "K8s_master_private_ip" { description = "The private IP of the private instance K8s master" value = aws_instance.K8S_K3S_master.private_ip From 964213f7b325903251450b496f50a72765ece1d5 Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Sun, 3 Nov 2024 19:40:49 +0200 Subject: [PATCH 22/36] fix: initial script --- README.md | 19 +++++ ec2-K8s-master.tf | 192 ++++++++++++++++++++++++++++------------------ sg.tf | 34 ++------ 3 files changed, 142 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index 1ea4bb2..c9471a5 100644 --- a/README.md +++ b/README.md @@ -117,18 +117,37 @@ sudo systemctl status k3s ``` +Check the cloud init logs: + +```bash +cat /var/log/cloud-init-output.log + +``` + 6. **Copy the k3s.yaml File to your local machine:** ```bash scp -i path/to/your/private_key.pem ubuntu@:/etc/rancher/k3s/k3s.yaml /path/to/local/directory/k3s.yaml ``` +or connected to the instance already: + +```bash +sudo cp /etc/rancher/k3s/k3s.yaml /path/to/local/directory/k3s.yaml +``` + 7. **Set the KUBECONFIG Environment Variable on your local machine and verify the cluster (in another terminal, parallel to open SSH tunnel):** ```bash export KUBECONFIG=/path/to/local/directory/k3s.yaml ``` +OR merge k3s.yaml with Existing Kubeconfig (for long use only): + +```bash +KUBECONFIG=~/.kube/config:/path/to/k3s.yaml kubectl config view --merge --flatten > ~/.kube/config +``` + 8. **Verify the Cluster and Jenkins:** ```bash diff --git a/ec2-K8s-master.tf b/ec2-K8s-master.tf index da978ef..9417d22 100644 --- a/ec2-K8s-master.tf +++ b/ec2-K8s-master.tf @@ -27,85 +27,127 @@ resource "aws_instance" "K8S_K3S_master" { } user_data = <<-EOF - #!/bin/bash - hostnamectl set-hostname "master-node" - sudo apt-get update -y - sudo apt-get install -y curl apt-transport-https - curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--tls-san $(curl -s 2ip.io)" sh - - sudo mkdir -p ~/.kube - sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config - sudo chown $(id -u):$(id -g) ~/.kube/config - kubectl create namespace jenkins - cat </dev/null | grep "Ready" | wc -l) -eq 0 ]]; do + echo "Waiting for node to be ready..." + sleep 10 + done + + # Create Jenkins namespace and resources + kubectl create namespace jenkins + + # Create PV and PVC + cat < /dev/null - echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list - sudo apt-get update -y - sudo apt-get install -y helm - helm repo add jenkins https://charts.jenkins.io - helm repo update - mkdir -p /tmp/jenkins-volume - chown -R 1000:1000 /tmp/jenkins-volume - - helm install jenkins jenkins/jenkins --namespace jenkins \ - --set controller.serviceType=LoadBalancer \ - --set persistence.enabled=true \ - --set persistence.size=8Gi \ - --set persistence.existingClaim=jenkins-pvc - --set controller.installPlugins="cloudbees-credentials,git,workflow-aggregator,jacoco,jacoco,configuration-as-code" - EOF + name: jenkins + EOL + + # Install Helm + curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list + sudo apt-get update -y + sudo apt-get install -y helm + + # Setup Jenkins volume + mkdir -p /tmp/jenkins-volume + chown -R 1000:1000 /tmp/jenkins-volume + + # Add Jenkins repo and update + helm repo add jenkins https://charts.jenkins.io + helm repo update + + # Wait for jenkins namespace to be ready + while ! kubectl get namespace jenkins; do + echo "Waiting for jenkins namespace..." + sleep 5 + done + + # Install Jenkins + helm install jenkins jenkins/jenkins --namespace jenkins \ + --set controller.serviceType=LoadBalancer \ + --set persistence.enabled=true \ + --set persistence.size=8Gi \ + --set persistence.existingClaim=jenkins-pvc \ + --set 'controller.installPlugins={cloudbees-credentials,git,workflow-aggregator,jacoco,configuration-as-code}' + + # Wait for Jenkins pod to be ready + while [[ $(kubectl get pods -n jenkins -l app.kubernetes.io/component=jenkins-controller -o jsonpath='{.items[*].status.containerStatuses[*].ready}' 2>/dev/null) != "true" ]]; do + echo "Waiting for Jenkins pod to be ready..." + sleep 10 + done + EOF tags = { Name = "K8S K3s Master" diff --git a/sg.tf b/sg.tf index 9862877..64f5b7f 100644 --- a/sg.tf +++ b/sg.tf @@ -3,44 +3,22 @@ resource "aws_security_group" "K8S_public_sg" { description = "Allow HTTP and SSH access" vpc_id = aws_vpc.K8S_vpc.id + # Allow all TCP traffic ingress { - from_port = 80 - to_port = 80 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - description = "Allow inbound HTTP" - } - - ingress { - from_port = 22 - to_port = 22 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - description = "Allow inbound HTTP" - } - - ingress { - from_port = 443 - to_port = 443 + from_port = 0 + to_port = 0 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] - description = "Allow inbound HTTPS" - } - - ingress { - from_port = 8080 - to_port = 8080 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] # Allow inbound traffic on port 8080 for Jenkins - description = "Allow inbound traffic to Jenkins" + description = "Allow all inbound TCP traffic" } + # Allow ICMP (ping) ingress { from_port = -1 to_port = -1 protocol = "icmp" cidr_blocks = ["0.0.0.0/0"] - description = "Allow inbound ICMP (ping) traffic" + description = "Allow ICMP" } egress { From 7762347a58ae8f9ba0c9c56250682d0b6ebc0bbc Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Sun, 3 Nov 2024 19:51:48 +0200 Subject: [PATCH 23/36] fix: security group --- sg.tf | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/sg.tf b/sg.tf index 64f5b7f..344335e 100644 --- a/sg.tf +++ b/sg.tf @@ -3,22 +3,12 @@ resource "aws_security_group" "K8S_public_sg" { description = "Allow HTTP and SSH access" vpc_id = aws_vpc.K8S_vpc.id - # Allow all TCP traffic ingress { from_port = 0 to_port = 0 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - description = "Allow all inbound TCP traffic" - } - - # Allow ICMP (ping) - ingress { - from_port = -1 - to_port = -1 - protocol = "icmp" + protocol = -1 cidr_blocks = ["0.0.0.0/0"] - description = "Allow ICMP" + description = "Allow all inbound traffic" } egress { From 7c8a33b7691facf3450d1789b8ab85a6bd1539b8 Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Sun, 3 Nov 2024 20:18:36 +0200 Subject: [PATCH 24/36] fix: volumes folder --- ec2-K8s-master.tf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ec2-K8s-master.tf b/ec2-K8s-master.tf index 9417d22..24d9fb7 100644 --- a/ec2-K8s-master.tf +++ b/ec2-K8s-master.tf @@ -32,6 +32,10 @@ resource "aws_instance" "K8S_K3S_master" { sudo apt-get update -y sudo apt-get install -y curl apt-transport-https + # Create Jenkins volume directory + mkdir -p /tmp/jenkins-volume + chown -R 1000:1000 /tmp/jenkins-volume # Set ownership if needed + # Install k3s curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--tls-san $(curl -s 2ip.io)" sh - From c547124d17a5a36d387009e9c0acb0869a44fdeb Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Sun, 3 Nov 2024 21:31:36 +0200 Subject: [PATCH 25/36] fix: jenkins role management from helm --- README.md | 6 ++++++ ec2-K8s-master.tf | 21 ++++++++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c9471a5..09dce8d 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,12 @@ OR merge k3s.yaml with Existing Kubeconfig (for long use only): KUBECONFIG=~/.kube/config:/path/to/k3s.yaml kubectl config view --merge --flatten > ~/.kube/config ``` +access from the local ps via SSH tunnel: + +```bash +ssh -i /path/to/your/key.pem -L 6443:localhost:6443 ubuntu@ +``` + 8. **Verify the Cluster and Jenkins:** ```bash diff --git a/ec2-K8s-master.tf b/ec2-K8s-master.tf index 24d9fb7..44b1d45 100644 --- a/ec2-K8s-master.tf +++ b/ec2-K8s-master.tf @@ -32,10 +32,6 @@ resource "aws_instance" "K8S_K3S_master" { sudo apt-get update -y sudo apt-get install -y curl apt-transport-https - # Create Jenkins volume directory - mkdir -p /tmp/jenkins-volume - chown -R 1000:1000 /tmp/jenkins-volume # Set ownership if needed - # Install k3s curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--tls-san $(curl -s 2ip.io)" sh - @@ -87,18 +83,28 @@ resource "aws_instance" "K8S_K3S_master" { storage: 8Gi EOL - # Create RBAC resources + # Create RBAC resources with Helm labels and annotations cat < Date: Sun, 10 Nov 2024 19:21:07 +0200 Subject: [PATCH 26/36] feat: change ec2 user data to deploy wordpress app --- README.md | 76 ++++-------------------------- ec2-K8s-master.tf | 115 +++++----------------------------------------- 2 files changed, 19 insertions(+), 172 deletions(-) diff --git a/README.md b/README.md index 09dce8d..ba59e0c 100644 --- a/README.md +++ b/README.md @@ -148,87 +148,27 @@ OR merge k3s.yaml with Existing Kubeconfig (for long use only): KUBECONFIG=~/.kube/config:/path/to/k3s.yaml kubectl config view --merge --flatten > ~/.kube/config ``` -access from the local ps via SSH tunnel: +access from the local pc via SSH tunnel: ```bash ssh -i /path/to/your/key.pem -L 6443:localhost:6443 ubuntu@ ``` -8. **Verify the Cluster and Jenkins:** +8. **Check the Status of the WordPress Application:** ```bash -kubectl get nodes +kubectl get pods -A ``` ```bash -kubectl get pods -n jenkins +kubectl get svc -A ``` -9. **Access Jenkins:** - Since we have set the service type to LoadBalancer, we should be able to access Jenkins via the public IP of our master node. - Retrieve the service details to get the external IP: +9. **Access the WordPress Application:** + Find the external IP or node port of the WordPress service to access the application. If you set the wordpress.service.nodePort to 32000, you can access the application using the public IP of your EC2 instance and the specified node port: ```bash -kubectl get svc -n jenkins +echo "http://:32000" ``` -Open a web browser and navigate to http://:8080. You should see the Jenkins setup wizard. - -10. **Unlock Jenkins:** - You’ll need the initial admin password to unlock Jenkins. Retrieve it by running: - -```bash - kubectl exec -n jenkins -- cat /var/jenkins_home/secrets/initialAdminPassword -``` - -Copy the password and paste it into the Jenkins setup wizard to unlock Jenkins. - -11. **Create a Freestyle Project:** - Follow the setup wizard to install recommended plugins. - Create a new Freestyle project: - -- Name it something like "HelloWorld". -- In the build section, add an "Execute shell" build step with the following command: - -```bash -echo "Hello world" -``` - -- Save the project and run it. - -12. **Verify the Build Output:** - After running the job, check the console output to ensure it shows "Hello world". - -13. **Check Persistent Volume Configuration:** - Ensure that the persistent volume (PV) and persistent volume claim (PVC) were created successfully: - -```bash -kubectl get pv -kubectl get pvc -n jenkins -``` - -14. **Verify your Helm installation by deploying and removing the Nginx chart from Bitnami:** - First, install the Nginx chart using Helm. You can run the following command to deploy the Nginx server: - -```bash -helm install my-nginx oci://registry-1.docker.io/bitnamicharts/nginx -``` - -Verify the Deployment: - -```bash -kubectl get pods -``` - -Remove the Nginx Chart: - -```bash -helm uninstall my-nginx -``` - -Check that the Nginx resources have been removed: - -```bash -kubectl get pods -kubectl get svc -``` +Open a web browser and navigate to http://:32000. You should see the Wordpress setup wizard. diff --git a/ec2-K8s-master.tf b/ec2-K8s-master.tf index 44b1d45..0b1b75a 100644 --- a/ec2-K8s-master.tf +++ b/ec2-K8s-master.tf @@ -28,11 +28,12 @@ resource "aws_instance" "K8S_K3S_master" { user_data = <<-EOF #!/bin/bash + set -e hostnamectl set-hostname "master-node" sudo apt-get update -y - sudo apt-get install -y curl apt-transport-https + sudo apt-get install -y curl apt-transport-https git - # Install k3s + # Install k3s with public IP in TLS SAN curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--tls-san $(curl -s 2ip.io)" sh - # Wait for k3s to be ready @@ -53,118 +54,24 @@ resource "aws_instance" "K8S_K3S_master" { sleep 10 done - # Create Jenkins namespace and resources - kubectl create namespace jenkins - - # Create PV and PVC - cat < /dev/null echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list sudo apt-get update -y sudo apt-get install -y helm - # Setup Jenkins volume - mkdir -p /tmp/jenkins-volume - chown -R 1000:1000 /tmp/jenkins-volume - - # Add Jenkins repo and update - helm repo add jenkins https://charts.jenkins.io - helm repo update + # Clone the WordPress repository + mkdir -p /home/ubuntu/helm + git clone https://github.com/elian-cheng/rsschool-devops-task5-wordpress /home/ubuntu/helm - # Wait for jenkins namespace to be ready - while ! kubectl get namespace jenkins; do - echo "Waiting for jenkins namespace..." - sleep 5 - done + # Install WordPress using Helm + helm install my-wordpress /home/ubuntu/helm/wordpress --set wordpress.service.nodePort=32000 - # Install Jenkins - helm install jenkins jenkins/jenkins --namespace jenkins \ - --set controller.serviceType=LoadBalancer \ - --set persistence.enabled=true \ - --set persistence.size=8Gi \ - --set persistence.existingClaim=jenkins-pvc \ - --set 'controller.installPlugins={cloudbees-credentials,git,workflow-aggregator,jacoco,configuration-as-code}' - - # Wait for Jenkins pod to be ready - while [[ $(kubectl get pods -n jenkins -l app.kubernetes.io/component=jenkins-controller -o jsonpath='{.items[*].status.containerStatuses[*].ready}' 2>/dev/null) != "true" ]]; do - echo "Waiting for Jenkins pod to be ready..." - sleep 10 - done + # Ensure the services are running + kubectl get pods -A EOF tags = { Name = "K8S K3s Master" } -} +} \ No newline at end of file From c94bcfdc0836a9b2089b84e604ad236a25a9dd04 Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Sun, 10 Nov 2024 20:52:19 +0200 Subject: [PATCH 27/36] fix: script issues --- ec2-K8s-master.tf | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ec2-K8s-master.tf b/ec2-K8s-master.tf index 0b1b75a..9b9cfda 100644 --- a/ec2-K8s-master.tf +++ b/ec2-K8s-master.tf @@ -7,7 +7,6 @@ data "aws_ami" "ubuntu_ami" { values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"] } } - resource "aws_instance" "K8S_K3S_master" { ami = data.aws_ami.ubuntu_ami.id instance_type = var.k8s_master_instance_type @@ -43,8 +42,9 @@ resource "aws_instance" "K8S_K3S_master" { done # Setup kubeconfig + export KUBECONFIG=/etc/rancher/k3s/k3s.yaml mkdir -p ~/.kube - sudo chmod 644 /etc/rancher/k3s/k3s.yaml + sudo chmod 600 /etc/rancher/k3s/k3s.yaml sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config sudo chown $(id -u):$(id -g) ~/.kube/config @@ -62,10 +62,14 @@ resource "aws_instance" "K8S_K3S_master" { # Clone the WordPress repository mkdir -p /home/ubuntu/helm - git clone https://github.com/elian-cheng/rsschool-devops-task5-wordpress /home/ubuntu/helm + sudo chown ubuntu:ubuntu /home/ubuntu/helm + git clone https://github.com/elian-cheng/rsschool-devops-task5-wordpress.git /home/ubuntu/helm + + # Wait for the repository to be cloned + sleep 10 # Install WordPress using Helm - helm install my-wordpress /home/ubuntu/helm/wordpress --set wordpress.service.nodePort=32000 + helm install elian-wordpress /home/ubuntu/helm/wordpress --set wordpress.service.nodePort=32000 # Ensure the services are running kubectl get pods -A From a6e78c344ca2df71d0db7ec4594d9219da0296b0 Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Sun, 10 Nov 2024 21:35:32 +0200 Subject: [PATCH 28/36] fix: user-data script --- ec2-K8s-master.tf | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ec2-K8s-master.tf b/ec2-K8s-master.tf index 9b9cfda..81d6938 100644 --- a/ec2-K8s-master.tf +++ b/ec2-K8s-master.tf @@ -42,9 +42,8 @@ resource "aws_instance" "K8S_K3S_master" { done # Setup kubeconfig - export KUBECONFIG=/etc/rancher/k3s/k3s.yaml mkdir -p ~/.kube - sudo chmod 600 /etc/rancher/k3s/k3s.yaml + sudo chmod 644 /etc/rancher/k3s/k3s.yaml sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config sudo chown $(id -u):$(id -g) ~/.kube/config @@ -63,13 +62,13 @@ resource "aws_instance" "K8S_K3S_master" { # Clone the WordPress repository mkdir -p /home/ubuntu/helm sudo chown ubuntu:ubuntu /home/ubuntu/helm - git clone https://github.com/elian-cheng/rsschool-devops-task5-wordpress.git /home/ubuntu/helm + git clone https://github.com/elian-cheng/rsschool-devops-task5-wordpress.git /home/ubuntu/helm/wp # Wait for the repository to be cloned sleep 10 # Install WordPress using Helm - helm install elian-wordpress /home/ubuntu/helm/wordpress --set wordpress.service.nodePort=32000 + helm install elian-wordpress /home/ubuntu/helm/wp/wordpress --set wordpress.service.nodePort=32000 # Ensure the services are running kubectl get pods -A From 61fe25121e91109426a90a2440d37b2a96520db7 Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Sat, 23 Nov 2024 14:54:43 +0200 Subject: [PATCH 29/36] feat: add necessary changes for jenkins --- .gitignore | 3 +- Jenkinsfile | 194 +++++++++++++++++++++++++++++++++++++++++ ec2-K8s-master.tf | 49 +---------- ecr.tf | 65 ++++++++++++++ install_k3s.sh | 187 +++++++++++++++++++++++++++++++++++++++ outputs.tf | 6 +- scripts/install_k3s.sh | 130 --------------------------- 7 files changed, 454 insertions(+), 180 deletions(-) create mode 100644 Jenkinsfile create mode 100644 ecr.tf create mode 100644 install_k3s.sh delete mode 100644 scripts/install_k3s.sh diff --git a/.gitignore b/.gitignore index ccf8093..c5cddda 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ .terraform/ .terraform.lock.hcl *.pem -k3s.yaml \ No newline at end of file +k3s.yaml +goals-app/node_modules/ \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..8c180b2 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,194 @@ +pipeline { + agent { + kubernetes { + yaml ''' + apiVersion: v1 + kind: Pod + metadata: + labels: + some-label: some-label-value + spec: + containers: + - name: node + image: timbru31/node-alpine-git + command: + - cat + tty: true + - name: docker + image: docker:24.0.5 + command: + - cat + tty: true + volumeMounts: + - name: docker-socket + mountPath: /var/run/docker.sock + - name: sonarscanner + image: sonarsource/sonar-scanner-cli + command: + - cat + tty: true + volumes: + - name: docker-socket + hostPath: + path: /var/run/docker.sock + ''' + retries 2 + } + } + triggers { + GenericTrigger( + causeString: 'Triggered by GitHub Push', + token: 'my-git-token', + printPostContent: true, + printContributedVariables: true, + silentResponse: false + ) + } + environment { + AWS_ACCOUNT_ID = '656732674839' + AWS_REGION = 'eu-north-1' + ECR_REPOSITORY = 'goals-app' + IMAGE_TAG = 'latest' + } + stages { + stage('Prepare') { + steps { + container('node') { + script { + echo "Cloning repository..." + sh ''' + git clone https://github.com/elian-cheng/rsschool-task6-docker-app app + cd app + echo "Repo files:" + ls -la + ''' + } + } + } + } + + stage('Install Dependencies') { + steps { + container('node') { + script { + echo "Installing dependencies..." + sh ''' + cd app + npm install + ''' + } + } + } + } + + stage('Run Tests') { + steps { + container('node') { + script { + echo "Running tests..." + sh ''' + cd app + npm test + ''' + } + } + } + } + + stage('Fetch Public IP') { + steps { + script { + env.PUBLIC_IP = sh(script: "curl -s http://169.254.169.254/latest/meta-data/public-ipv4", returnStdout: true).trim() + env.SONAR_HOST_URL = "http://${env.PUBLIC_IP}:9000" + } + } + } + + stage('SonarQube Analysis') { + environment { + SONAR_PROJECT_KEY = credentials('sonar-project-key') + SONAR_LOGIN = credentials('sonar-login-token') + } + steps { + container('sonarscanner') { + script { + sh ''' + sonar-scanner \ + -Dsonar.projectKey=${SONAR_PROJECT_KEY} \ + -Dsonar.sources=. \ + -Dsonar.host.url=${SONAR_HOST_URL} \ + -Dsonar.login=${SONAR_LOGIN} + ''' + } + } + } + } + + stage('Install AWS CLI') { + steps { + container('docker') { + script { + echo "Installing AWS CLI..." + sh ''' + apk add --no-cache python3 py3-pip + pip3 install awscli + aws --version + ''' + } + } + } + } + + stage('Build Docker Image') { + steps { + container('docker') { + script { + echo "Building Docker image..." + sh ''' + cd app + pwd + docker build -t goals-app:latest -f Dockerfile . + ''' + } + } + } + } + + stage('Publish to ECR') { + steps { + container('docker') { + script { + echo "Publishing Docker image to ECR..." + sh ''' + aws ecr get-login-password --region eu-north-1 | docker login --username AWS --password-stdin 656732674839.dkr.ecr.eu-north-1.amazonaws.com + docker tag goals-app:latest 656732674839.dkr.ecr.eu-north-1.amazonaws.com/goals-app:latest + docker push 656732674839.dkr.ecr.eu-north-1.amazonaws.com/goals-app:latest + ''' + } + } + } + } + } + post { + success { + script { + echo "Pipeline completed successfully!" + emailext( + subject: 'Jenkins Pipeline Success', + body: "Pipeline '${env.JOB_NAME}' (#${env.BUILD_NUMBER}) completed successfully.\n\nCheck results: ${env.BUILD_URL}", + to: 'eliang.cheng@gmail.com' + ) + } + } + failure { + script { + echo "Pipeline failed!" + emailext( + subject: 'Jenkins Pipeline Failure', + body: "Pipeline '${env.JOB_NAME}' (#${env.BUILD_NUMBER}) failed.\n\nCheck the details here: ${env.BUILD_URL}", + to: 'eliang.cheng@gmail.com' + ) + } + } + } +} \ No newline at end of file diff --git a/ec2-K8s-master.tf b/ec2-K8s-master.tf index 81d6938..7e58283 100644 --- a/ec2-K8s-master.tf +++ b/ec2-K8s-master.tf @@ -25,54 +25,7 @@ resource "aws_instance" "K8S_K3S_master" { delete_on_termination = true } - user_data = <<-EOF - #!/bin/bash - set -e - hostnamectl set-hostname "master-node" - sudo apt-get update -y - sudo apt-get install -y curl apt-transport-https git - - # Install k3s with public IP in TLS SAN - curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--tls-san $(curl -s 2ip.io)" sh - - - # Wait for k3s to be ready - while ! kubectl get nodes; do - echo "Waiting for k3s to be ready..." - sleep 10 - done - - # Setup kubeconfig - mkdir -p ~/.kube - sudo chmod 644 /etc/rancher/k3s/k3s.yaml - sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config - sudo chown $(id -u):$(id -g) ~/.kube/config - - # Wait for node to be ready - while [[ $(kubectl get nodes --no-headers 2>/dev/null | grep "Ready" | wc -l) -eq 0 ]]; do - echo "Waiting for node to be ready..." - sleep 10 - done - - # Install Helm - curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null - echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list - sudo apt-get update -y - sudo apt-get install -y helm - - # Clone the WordPress repository - mkdir -p /home/ubuntu/helm - sudo chown ubuntu:ubuntu /home/ubuntu/helm - git clone https://github.com/elian-cheng/rsschool-devops-task5-wordpress.git /home/ubuntu/helm/wp - - # Wait for the repository to be cloned - sleep 10 - - # Install WordPress using Helm - helm install elian-wordpress /home/ubuntu/helm/wp/wordpress --set wordpress.service.nodePort=32000 - - # Ensure the services are running - kubectl get pods -A - EOF + user_data = file("install_k3s.sh") tags = { Name = "K8S K3s Master" diff --git a/ecr.tf b/ecr.tf new file mode 100644 index 0000000..c0c8092 --- /dev/null +++ b/ecr.tf @@ -0,0 +1,65 @@ +resource "aws_ecr_repository" "goals_app_repo" { + name = "goals-app" +} + + +resource "aws_iam_role" "ec2_ecr_role" { + name = "ec2-ecr-role" + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Principal = { + Service = "ec2.amazonaws.com" + } + Effect = "Allow" + Sid = "" + } + ] + }) +} + +resource "aws_iam_policy" "ecr_policy" { + name = "ecr-policy" + description = "Policy for ECR access" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = [ + "ecr:GetAuthorizationToken" + ] + Resource = "*" + Effect = "Allow" + }, + { + Action = [ + "ecr:BatchCheckLayerAvailability", + "ecr:GetRepositoryPolicy", + "ecr:ListImages", + "ecr:DescribeImages", + "ecr:PutImage", + "ecr:InitiateLayerUpload", + "ecr:UploadLayerPart", + "ecr:CompleteLayerUpload", + "ecr:BatchGetImage", + "ecr:CreateRepository" + ] + Resource = "*" + Effect = "Allow" + } + ] + }) +} + +resource "aws_iam_role_policy_attachment" "ecr_role_attachment" { + role = aws_iam_role.ec2_ecr_role.name + policy_arn = aws_iam_policy.ecr_policy.arn +} + +resource "aws_iam_instance_profile" "ec2_instance_profile" { + name = "ec2-instance-profile" + role = aws_iam_role.ec2_ecr_role.name +} diff --git a/install_k3s.sh b/install_k3s.sh new file mode 100644 index 0000000..3adea08 --- /dev/null +++ b/install_k3s.sh @@ -0,0 +1,187 @@ +#!/bin/bash +set -e + +max_attempts=10 + +# Function to wait for a condition +wait_for_condition() { + local condition="$1" + local max_attempts=$2 + local attempt_num=1 + while ! eval "$condition" && [ $attempt_num -le $max_attempts ]; do + echo "Waiting for condition: $condition..." + sleep 10 + ((attempt_num++)) + done +} + +# Function to install packages +install_package() { + local package=$1 + if ! command -v "$package" &>/dev/null; then + echo "Installing $package..." + sudo apt-get install -y "$package" || { echo "$package installation failed."; exit 1; } + else + echo "$package is already installed." + fi +} + +# Set hostname +hostnamectl set-hostname "master-node" + +# Update and install dependencies +sudo apt-get update -y +install_package "curl" +install_package "apt-transport-https" +install_package "git" +install_package "docker.io" + +# Start and enable Docker +sudo systemctl start docker +sudo systemctl enable docker +sudo usermod -aG docker ubuntu +docker --version || { echo "Docker installation failed."; exit 1; } +echo "Docker installed successfully." + +# Install k3s with public IP in TLS SAN +curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--tls-san $(curl -s 2ip.io)" sh - + +# Wait for k3s to be ready +while ! kubectl get nodes; do + echo "Waiting for k3s to be ready..." + sleep 10 +done + +# Wait for kubeconfig to be available +wait_for_condition "[ -f /etc/rancher/k3s/k3s.yaml ]" $max_attempts +export KUBECONFIG=/etc/rancher/k3s/k3s.yaml +echo "KUBECONFIG has been set to: $KUBECONFIG" + +kubectl cluster-info || { echo "Kubernetes cluster is not reachable."; exit 1; } + +mkdir -p ~/.kube && chmod 700 ~/.kube +sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config && chmod 600 ~/.kube/config +sudo chmod 644 /etc/rancher/k3s/k3s.yaml +sudo systemctl status k3s + +# Wait for node to be ready +while [[ $(kubectl get nodes --no-headers 2>/dev/null | grep "Ready" | wc -l) -eq 0 ]]; do + echo "Waiting for node to be ready..." + sleep 10 +done + +# Install Helm +curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash +command -v helm &>/dev/null || { echo "Helm installation failed."; exit 1; } + +# Create SonarQube namespace +kubectl create namespace sonarqube || echo "Namespace sonarqube already exists." + +# Add SonarQube Helm repository and update +helm repo add sonarqube https://SonarSource.github.io/helm-chart-sonarqube +helm repo update + +# Install SonarQube using Helm +helm install my-sonarqube sonarqube/sonarqube \ + --namespace sonarqube \ + --set persistence.enabled=true \ + --set persistence.storageClass=local-path \ + --set service.type=LoadBalancer + +# Wait for SonarQube to be ready +while [[ $(kubectl get pod -n sonarqube -l app=sonarqube-sonarqube -o jsonpath='{.items[*].status.containerStatuses[*].ready}' 2>/dev/null) != "true" ]]; do + echo "Waiting for SonarQube pod to be ready..." + sleep 10 +done + +# Expose SonarQube service +cat </dev/null || { + echo "No StorageClass found. Setting up a default StorageClass..." + cat </dev/null) != "true" ]]; do + echo "Waiting for Jenkins pod to be ready..." + sleep 10 +done + +# Install SonarQube plugin in Jenkins +kubectl exec -n jenkins svc/my-jenkins -c jenkins -- /bin/bash -c "jenkins-plugin-cli --plugins sonar" + +# Restart Jenkins to apply the plugin +kubectl exec -n jenkins svc/my-jenkins -c jenkins -- /bin/bash -c "jenkins-plugin-cli --restart" + +# Get public IP +PUBLIC_IP=$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4) +echo "Public IP: $PUBLIC_IP" +kubectl patch svc my-jenkins -n jenkins -p '{"spec": {"type": "LoadBalancer"}}' +kubectl patch svc sonarqube -n sonarqube -p '{"spec": {"type": "LoadBalancer"}}' + +# Get Jenkins admin password +JENKINS_PASSWORD=$(kubectl exec -n jenkins svc/my-jenkins -c jenkins -- cat /run/secrets/additional/chart-admin-password) +[ -n "$JENKINS_PASSWORD" ] && echo "Jenkins admin password: $JENKINS_PASSWORD" || { echo "Failed to retrieve Jenkins admin password."; exit 1; } + +echo "Jenkins is accessible at http://$PUBLIC_IP:8080" + +# SonarQube URL +echo "SonarQube is accessible at http://$PUBLIC_IP:9000" + +# Ensure the services are running +kubectl get pods -A \ No newline at end of file diff --git a/outputs.tf b/outputs.tf index c547498..b9a8509 100644 --- a/outputs.tf +++ b/outputs.tf @@ -14,6 +14,10 @@ output "private_subnets" { } output "K8s_master_private_ip" { - description = "The private IP of the private instance K8s master" + description = "The private IP of the instance K8s master" value = aws_instance.K8S_K3S_master.private_ip } +output "K8s_master_public_ip" { + description = "The public IP of the instance K8s master" + value = aws_instance.K8S_K3S_master.public_ip +} diff --git a/scripts/install_k3s.sh b/scripts/install_k3s.sh deleted file mode 100644 index 689996c..0000000 --- a/scripts/install_k3s.sh +++ /dev/null @@ -1,130 +0,0 @@ -#!/bin/bash -set -o errexit # abort on nonzero exit status -set -o nounset # abort on unbound variable -set -o pipefail # don't hide errors within pipes - -# https://www.digitalocean.com/community/tutorials/how-to-setup-k3s-kubernetes-cluster-on-ubuntu - # Set hostname -hostnamectl set-hostname "master-node" - -# Update and install dependencies -sudo apt-get update -y -sudo apt-get install -y curl apt-transport-https - -# Disable UFW firewall (optional, depending on your security requirements) -sudo ufw disable -# installing k3s -curl -sfL https://get.k3s.io | sh - -# OR -# Install K3s with external IP for API server access -curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--tls-san $(curl -s 2ip.io)" sh - - -# Configure kubeconfig for kubectl -sudo mkdir -p /home/ubuntu/.kube -sudo cp /etc/rancher/k3s/k3s.yaml /home/ubuntu/.kube/config -sudo chown $(id -u):$(id -g) ~/.kube/config - -# Create Jenkins namespace -kubectl create namespace jenkins - -# Define PersistentVolume and PersistentVolumeClaim in the script directly or download it -cat < /dev/null -echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list -sudo apt-get update -y -sudo apt-get install -y helm - -# Add Jenkins Helm chart repository -helm repo add jenkins https://charts.jenkins.io -helm repo update - -# Create Persistent Volume directory -mkdir -p /tmp/jenkins-volume -chown -R 1000:1000 /tmp/jenkins-volume - -# Install Jenkins using Helm with custom values including security plugins -helm install jenkins jenkins/jenkins --namespace jenkins \ - --set controller.serviceType=LoadBalancer \ - --set persistence.enabled=true \ - --set persistence.size=8Gi \ - --set persistence.existingClaim=jenkins-pvc \ - --set controller.installPlugins="cloudbees-credentials,git,workflow-aggregator,jacoco,jacoco,configuration-as-code" - -# make kubeconfig avaliable without sudo -sudo chmod 644 /etc/rancher/k3s/k3s.yaml - -# check status -sudo systemctl status k3s -kubectl get all -n kube-system - -# check nodes -kubectl get nodes - -# deploy simple workload -kubectl apply -f https://k8s.io/examples/pods/simple-pod.yaml - -# Install autocompletion in kubectl -# https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#enable-shell-autocompletion -sudo apt install -y bash-completion -echo 'source <(kubectl completion bash)' >>~/.bashrc -source ~/.bashrc -kubectl get pods - - From 3d6b1fe801764f01d66e36655d71823378e6a74a Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Sat, 23 Nov 2024 15:59:32 +0200 Subject: [PATCH 30/36] feat: change sonar url place --- Jenkinsfile | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 8c180b2..2073383 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -95,19 +95,11 @@ pipeline { } } - stage('Fetch Public IP') { - steps { - script { - env.PUBLIC_IP = sh(script: "curl -s http://169.254.169.254/latest/meta-data/public-ipv4", returnStdout: true).trim() - env.SONAR_HOST_URL = "http://${env.PUBLIC_IP}:9000" - } - } - } - stage('SonarQube Analysis') { environment { SONAR_PROJECT_KEY = credentials('sonar-project-key') SONAR_LOGIN = credentials('sonar-login-token') + SONAR_HOST_URL = credentials('sonar-host-url') } steps { container('sonarscanner') { From a0630f602b7fd567979628169c0ab592d929ea7a Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Sat, 23 Nov 2024 16:18:03 +0200 Subject: [PATCH 31/36] feat: change instance type --- install_k3s.sh | 17 +++++++++++++++++ variables.tf | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/install_k3s.sh b/install_k3s.sh index 3adea08..097a769 100644 --- a/install_k3s.sh +++ b/install_k3s.sh @@ -81,6 +81,23 @@ kubectl create namespace sonarqube || echo "Namespace sonarqube already exists." helm repo add sonarqube https://SonarSource.github.io/helm-chart-sonarqube helm repo update +# Create values.yaml for SonarQube +cat < values.yaml +persistence: + enabled: true + storageClass: local-path + size: 10Gi + +resources: + requests: + memory: "1Gi" + cpu: "500m" + limits: + memory: "2Gi" + cpu: "1" +EOF + + # Install SonarQube using Helm helm install my-sonarqube sonarqube/sonarqube \ --namespace sonarqube \ diff --git a/variables.tf b/variables.tf index 7c4a7d5..b9d3fc6 100644 --- a/variables.tf +++ b/variables.tf @@ -54,7 +54,7 @@ variable "private_key" { variable "k8s_master_instance_type" { description = "instance type for k8s node" - default = "t3.small" + default = "t3.medium" } variable "k8s_master_node_disk" { From 2316ba89c0f22e68c4749333a6d8084ae74c4e5d Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Sat, 23 Nov 2024 16:34:04 +0200 Subject: [PATCH 32/36] fix: checking state for the sonarQube --- install_k3s.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install_k3s.sh b/install_k3s.sh index 097a769..fee4519 100644 --- a/install_k3s.sh +++ b/install_k3s.sh @@ -106,7 +106,7 @@ helm install my-sonarqube sonarqube/sonarqube \ --set service.type=LoadBalancer # Wait for SonarQube to be ready -while [[ $(kubectl get pod -n sonarqube -l app=sonarqube-sonarqube -o jsonpath='{.items[*].status.containerStatuses[*].ready}' 2>/dev/null) != "true" ]]; do +while [[ $(kubectl get pod my-sonarqube-sonarqube-0 -n sonarqube -o jsonpath='{.status.containerStatuses[*].ready}' 2>/dev/null | grep -c "true") -ne 1 ]]; do echo "Waiting for SonarQube pod to be ready..." sleep 10 done From b9f9b8aa2e5e223aa2442746124d49ec6bd7d3aa Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Sat, 23 Nov 2024 18:08:19 +0200 Subject: [PATCH 33/36] fix: scripts --- Jenkinsfile | 19 +++++++------------ install_k3s.sh | 6 +++--- sonar-project.properties | 1 + 3 files changed, 11 insertions(+), 15 deletions(-) create mode 100644 sonar-project.properties diff --git a/Jenkinsfile b/Jenkinsfile index 2073383..fb7e77f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -49,6 +49,9 @@ pipeline { AWS_REGION = 'eu-north-1' ECR_REPOSITORY = 'goals-app' IMAGE_TAG = 'latest' + SONAR_PROJECT_KEY = "Goals-App-Check" + SONAR_LOGIN = "${SONAR_LOGIN}" + SONAR_HOST_URL = "http://51.20.106.145:9000" } stages { stage('Prepare') { @@ -96,21 +99,13 @@ pipeline { } stage('SonarQube Analysis') { - environment { - SONAR_PROJECT_KEY = credentials('sonar-project-key') - SONAR_LOGIN = credentials('sonar-login-token') - SONAR_HOST_URL = credentials('sonar-host-url') - } steps { container('sonarscanner') { script { - sh ''' - sonar-scanner \ - -Dsonar.projectKey=${SONAR_PROJECT_KEY} \ - -Dsonar.sources=. \ - -Dsonar.host.url=${SONAR_HOST_URL} \ - -Dsonar.login=${SONAR_LOGIN} - ''' + def scannerHome = tool 'MySonar' + withSonarQubeEnv('SonarQube') { + sh "${scannerHome}/bin/sonar-scanner" + } } } } diff --git a/install_k3s.sh b/install_k3s.sh index fee4519..9cf957f 100644 --- a/install_k3s.sh +++ b/install_k3s.sh @@ -174,7 +174,7 @@ helm install my-jenkins jenkins/jenkins \ --set controller.containerSecurityContext.readOnlyRootFilesystem=false # Wait for Jenkins to be ready -while [[ $(kubectl get pod -n jenkins -l app.kubernetes.io/component=jenkins-controller -o jsonpath='{.items[*].status.containerStatuses[*].ready}' 2>/dev/null) != "true" ]]; do +while [[ $(kubectl get pod -n jenkins -l app.kubernetes.io/component=jenkins-controller -o jsonpath='{.items[*].status.containerStatuses[*].ready}' 2>/dev/null | grep -c "true") -ne 2 ]]; do echo "Waiting for Jenkins pod to be ready..." sleep 10 done @@ -182,8 +182,8 @@ done # Install SonarQube plugin in Jenkins kubectl exec -n jenkins svc/my-jenkins -c jenkins -- /bin/bash -c "jenkins-plugin-cli --plugins sonar" -# Restart Jenkins to apply the plugin -kubectl exec -n jenkins svc/my-jenkins -c jenkins -- /bin/bash -c "jenkins-plugin-cli --restart" +# Restart Jenkins pod to apply the plugin +kubectl rollout restart statefulset my-jenkins -n jenkins # Get public IP PUBLIC_IP=$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4) diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..396b5fc --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1 @@ +sonar.projectKey=Goals-App-Check \ No newline at end of file From 2f43060c7e3e4ba10bee9a14e0d11d7a9d623d4f Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Sat, 23 Nov 2024 18:32:34 +0200 Subject: [PATCH 34/36] feat: change sonar qube step --- Jenkinsfile | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index fb7e77f..0f3f9e9 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -50,7 +50,7 @@ pipeline { ECR_REPOSITORY = 'goals-app' IMAGE_TAG = 'latest' SONAR_PROJECT_KEY = "Goals-App-Check" - SONAR_LOGIN = "${SONAR_LOGIN}" + SONAR_LOGIN = "squ_f895802a1dc2ee20bf6c1a243850379cbd175c6f" SONAR_HOST_URL = "http://51.20.106.145:9000" } stages { @@ -102,10 +102,13 @@ pipeline { steps { container('sonarscanner') { script { - def scannerHome = tool 'MySonar' - withSonarQubeEnv('SonarQube') { - sh "${scannerHome}/bin/sonar-scanner" - } + sh ''' + sonar-scanner \ + -Dsonar.projectKey=${SONAR_PROJECT_KEY} \ + -Dsonar.sources=. \ + -Dsonar.host.url=${SONAR_HOST_URL} \ + -Dsonar.login=${SONAR_LOGIN} + ''' } } } From 18c686de2c5a8ca0fcd015dd0f37388865f3c7d3 Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Sat, 23 Nov 2024 19:46:16 +0200 Subject: [PATCH 35/36] feat: change scripts --- Jenkinsfile | 2 +- install_k3s.sh | 5 ++++- variables.tf | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 0f3f9e9..e77bc3d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -50,7 +50,7 @@ pipeline { ECR_REPOSITORY = 'goals-app' IMAGE_TAG = 'latest' SONAR_PROJECT_KEY = "Goals-App-Check" - SONAR_LOGIN = "squ_f895802a1dc2ee20bf6c1a243850379cbd175c6f" + SONAR_LOGIN = "sqp_1c229ea811bd6e48b5b08b84f17052332323ea86" SONAR_HOST_URL = "http://51.20.106.145:9000" } stages { diff --git a/install_k3s.sh b/install_k3s.sh index 9cf957f..16c8330 100644 --- a/install_k3s.sh +++ b/install_k3s.sh @@ -169,7 +169,10 @@ helm install my-jenkins jenkins/jenkins \ --namespace jenkins \ --set persistence.enabled=true \ --set persistence.existingClaim=jenkins-pvc \ - --set controller.debug=true \ + --set controller.resources.requests.memory=1Gi \ + --set controller.resources.requests.cpu=500m \ + --set controller.resources.limits.memory=2Gi \ + --set controller.resources.limits.cpu=1 \ --set service.type=LoadBalancer \ --set controller.containerSecurityContext.readOnlyRootFilesystem=false diff --git a/variables.tf b/variables.tf index b9d3fc6..e60c6dd 100644 --- a/variables.tf +++ b/variables.tf @@ -54,7 +54,7 @@ variable "private_key" { variable "k8s_master_instance_type" { description = "instance type for k8s node" - default = "t3.medium" + default = "t3.large" } variable "k8s_master_node_disk" { From 57e34a82f1e85063018eae75d64a673a2f7d6d14 Mon Sep 17 00:00:00 2001 From: Olga Chernega Date: Sun, 24 Nov 2024 18:00:22 +0200 Subject: [PATCH 36/36] docs: update Readme.md --- Jenkinsfile | 59 +++++++++++++++++++++++++++++++++++++++++++------- README.md | 58 ++++++++++++++++++++++++++++++++++++++++++++----- install_k3s.sh | 2 +- 3 files changed, 105 insertions(+), 14 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index e77bc3d..dae972b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -34,6 +34,9 @@ pipeline { ''' retries 2 } + } + parameters { + booleanParam(name: 'SHOULD_PUSH_TO_ECR', defaultValue: false, description: 'Set to true in build with params to push Docker image to ECR') } triggers { GenericTrigger( @@ -47,7 +50,8 @@ pipeline { environment { AWS_ACCOUNT_ID = '656732674839' AWS_REGION = 'eu-north-1' - ECR_REPOSITORY = 'goals-app' + AWS_CREDENTIALS = 'aws-credentials' + REPO_NAME = 'goals-app' IMAGE_TAG = 'latest' SONAR_PROJECT_KEY = "Goals-App-Check" SONAR_LOGIN = "sqp_1c229ea811bd6e48b5b08b84f17052332323ea86" @@ -102,6 +106,7 @@ pipeline { steps { container('sonarscanner') { script { + echo "Running SonarQube analysis..." sh ''' sonar-scanner \ -Dsonar.projectKey=${SONAR_PROJECT_KEY} \ @@ -144,20 +149,58 @@ pipeline { } } - stage('Publish to ECR') { + stage('Push Docker image to ECR') { + when { expression { params.SHOULD_PUSH_TO_ECR == true } } steps { container('docker') { script { - echo "Publishing Docker image to ECR..." - sh ''' - aws ecr get-login-password --region eu-north-1 | docker login --username AWS --password-stdin 656732674839.dkr.ecr.eu-north-1.amazonaws.com - docker tag goals-app:latest 656732674839.dkr.ecr.eu-north-1.amazonaws.com/goals-app:latest - docker push 656732674839.dkr.ecr.eu-north-1.amazonaws.com/goals-app:latest - ''' + echo "Pushing Docker image to ECR..." + withAWS(credentials: "${AWS_CREDENTIALS}", region: "${AWS_REGION}") { + sh ''' + aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${REPO_NAME} + docker tag ${REPO_NAME}:${IMAGE_TAG} ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${REPO_NAME}:${IMAGE_TAG} + docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${REPO_NAME}:${IMAGE_TAG} + ''' + } } } } } + + stage('Create ECR Secret') { + steps { + container('docker') { + script { + echo "Creating ECR secret..." + withAWS(credentials: "${AWS_CREDENTIALS}", region: "${AWS_REGION}") { + sh ''' + aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${REPO_NAME} + kubectl create secret generic ecr-secret --namespace=jenkins --from-file=.dockerconfigjson=\$HOME/.docker/config.json --dry-run=client -o json | kubectl apply -f - + ''' + } + } + } + } + } + + stage('Deploy to Kubernetes with Helm') { + when { expression { params.SHOULD_PUSH_TO_ECR == true } } + steps { + container('helm') { + script { + echo "Deploying to Kubernetes with Helm..." + withAWS(credentials: "${AWS_CREDENTIALS}", region: "${AWS_REGION}") { + sh ''' + helm upgrade --install ${REPO_NAME} ./helm/${REPO_NAME} \\ + --set image.repository=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${REPO_NAME} \\ + --set image.tag=${IMAGE_TAG} \\ + -f ./helm/${REPO_NAME}/values.yaml \\ + --namespace default ''' + } + } + } + } + } } post { success { diff --git a/README.md b/README.md index ba59e0c..4fd7e04 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ The Terraform configurations use the following variables: - **ec2-bastion.tf**: Bastion host definition. - **ec2-K8s-master.tf**: Private instance definition (in private subnet) for K8s master. - **outputs.tf**: Resources outputs. +- **ecr.tf**: ECR role && policy definition ## Workflow Overview @@ -154,7 +155,7 @@ access from the local pc via SSH tunnel: ssh -i /path/to/your/key.pem -L 6443:localhost:6443 ubuntu@ ``` -8. **Check the Status of the WordPress Application:** +8. **Check the Status of the pods & services:** ```bash kubectl get pods -A @@ -164,11 +165,58 @@ kubectl get pods -A kubectl get svc -A ``` -9. **Access the WordPress Application:** - Find the external IP or node port of the WordPress service to access the application. If you set the wordpress.service.nodePort to 32000, you can access the application using the public IP of your EC2 instance and the specified node port: +9. **Login to SonarQube and config:** + You can access the SonarQube using the public IP of your EC2 instance and the specified load balancer port 9000 (default for SonarQube): ```bash -echo "http://:32000" +echo "http://:9000" ``` -Open a web browser and navigate to http://:32000. You should see the Wordpress setup wizard. +Open a web browser and navigate to http://:9000. You should see the login form. Default login credentials are: login - admin, pw - admin. + +After login you need to create a project and generate a project token, which you need to provide in your Jenkinsfile for the pipeline. + +9. **Login to Jenkins and config:** + You can access the Jenkins using the public IP of your EC2 instance and the specified load balancer port 8080 (default for Jenkins): + +```bash +echo "http://:9000" +``` + +Retrieve the Jenkins admin password: + +```bash +kubectl exec --namespace jenkins -it svc/my-jenkins -c jenkins -- /bin/cat /run/secrets/additional/chart-admin-password +``` + +Or get it from EC2 user_data script output: + +```bash +cat /var/log/cloud-init-output.log + +``` + +Open a web browser and navigate to http://:9000. You should see the login form. Default login credentials are: login - admin, pw - retrieved from the jenkins file. + +After login you need to install plugins: Email Extension Plugin (for notifications), SonarQube Scanner for Jenkins, Amazon EC2 plugin (to get custom AWS credentials), Generic Webhook Trigger plugin (for the trigger on pushes to the other repository with app and Dockerfile). + +Then you need to create credentials for the email access (also a google app password in google console, if needed) and aws credentials for the pipeline. Make sure that AWS credentials for this user (role) in IAM include permission to access EC2ContainerRegistry. + +For the detailed screenshots check [PR](https://github.com/elian-cheng/rsschool-devops-course-tasks/pull/9) + +10. **Setup the webhook trigger** + In github settings in other repo (docker application): + Settings > Webhooks > Add webhook > Payload URL ↓ + +```bash +http://:8080/generic-webhook-trigger/invoke? +``` + +11. **Jenkins job config** + Create a new pipeline job, and config build triggers to use generic webhook trigger, add a token name + +12. **Start the Jenkins job**: + You can start the job manually or it would start automatically on push to the app repo. + By default SHOULD_PUSH_TO_ECR is set to false in Jenkins file, so you can manually override it + by using "Build with Parameters" option in Jenkins and set it to true, so it would also + push the image to ECR and deploy to the Kubernetes cluster with Helm. diff --git a/install_k3s.sh b/install_k3s.sh index 16c8330..fda313e 100644 --- a/install_k3s.sh +++ b/install_k3s.sh @@ -177,7 +177,7 @@ helm install my-jenkins jenkins/jenkins \ --set controller.containerSecurityContext.readOnlyRootFilesystem=false # Wait for Jenkins to be ready -while [[ $(kubectl get pod -n jenkins -l app.kubernetes.io/component=jenkins-controller -o jsonpath='{.items[*].status.containerStatuses[*].ready}' 2>/dev/null | grep -c "true") -ne 2 ]]; do +while [[ $(kubectl get pod my-jenkins-0 -n jenkins -l app.kubernetes.io/component=jenkins-controller -o jsonpath='{.items[*].status.containerStatuses[*].ready}' 2>/dev/null | grep -c "true") -ne 1 ]]; do echo "Waiting for Jenkins pod to be ready..." sleep 10 done