Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
rhzs committed May 21, 2019
0 parents commit 64fb604
Show file tree
Hide file tree
Showing 14 changed files with 449 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.terraform
*tfvars*
*tfplan*
*tfstate*
.key
63 changes: 63 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# GCP & GKE Terraform CI/CD Setup

This is demonstration how to make basic CI/CD pipeline setup using Jenkins and SonarQube in GCP.

### Terraform Setup
- Make sure terraform exist in OS path. To test, type `which terraform`.
- In terminal, create new file `terraform.tfvars`.

```
project = "project_id" # set your GCP progect id
region = "us-central1"
zone = "us-central1-c"
size = "n1-standard-2"
public_key = ".key/user.pub"
private_key = ".key/user.pem"
master_user = "user"
master_pass = "abcdefghij123" # password must be at least 16 characters
app_repo = "https://github.com/incubus8/gcp-cicd-terraform-jenkins.git"
```

### Create SSH .key
- create ssh key `ssh-keygen -f user.pem -N""` # leave an empty password
- rename public to `.pub`
- place both in `.key` subdir

### IAM Setup
- enable Kubernetes Engine API by visiting service console
- Console -> IAM & admin -> Service accounts -> select default account (or create new) -> Edit -> Create Key -> Json
- place the service account key file into the `.key/account.json`

### GCP Build Server Setup

- In terminal, type `terraform init`, then `terraform apply`
- Login to build server http://{terraform.output.build}:8080/
- Create SERVICE_ACCOUNT_KEY secret file (Jenkins -> Credentials -> Global Credentials -> Add Credentials)
* SERVICE_ACCOUNT_KEY { type = secret file } For secrets file, use '.key/account.json'
- Create new project using `pipeline` template and paste `jenkins/Jenkinsfile` content
- Manage Jenkins -> Manage Plugins -> Available -> filter: sonar -> SonarQube Scanner for Jenkins -> Install and restart
- Manage Jenkins -> Configure System -> SonarQube servers -> Add SonarQube -> Name: sonar, Host: http://{terraform.output.sonar}:9000 -> Save
- Manage Jenkins -> Global Tool Configuration -> Add SonarQube Scanner -> Name: scanner -> Save
- Enable Poll SCM, if CI mode is required

### Test
- App url `http://{terraform.output.app}:5000`
- Jenkins URL server `http://{terraform.output.build}:8080`
- SonarQube `http://{terraform.output.sonar}:9000`

### Destroy

In terminal, type:

```
terraform destroy
```

## Attention

This is just a demo pipeline. You may want to setup more strict security if you want to apply in production.

## Further Improvement
- Deploy using Kubernetes Control Plane

Jakarta (c) 2019
65 changes: 65 additions & 0 deletions app.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Defining pod & policy for a node.js hellow-world app

locals {
port = "5000"
app = "app"
}

resource "kubernetes_deployment" "app" {
metadata {
name = "${local.app}"

labels {
app = "${local.app}"
}
}

spec {
replicas = 3

selector {
match_labels {
app = "${local.app}"
}
}

template {
metadata {
labels {
app = "${local.app}"
}
}

spec {
container {
image = "malferov/app:3" # temporary "gcr.io/google-samples/node-hello:1.0"
name = "${local.app}"
}
}
}
}
}

resource "kubernetes_service" "app" {
metadata {
name = "${local.app}"
}

spec {
selector {
app = "${kubernetes_deployment.app.metadata.0.labels.app}"
}

session_affinity = "ClientIP"

port {
port = "${local.port}"
}

type = "LoadBalancer"
}
}

output "app" {
value = "${kubernetes_service.app.load_balancer_ingress.0.ip}:${local.port}"
}
9 changes: 9 additions & 0 deletions app/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM node:8

ARG port

EXPOSE $port

COPY app.js .

ENTRYPOINT ["node", "app.js"]
24 changes: 24 additions & 0 deletions app/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const http = require('http');
const os = require("os");
const bind = '0.0.0.0';
const port = process.argv[2] || 5000;

const version = '1.6';

const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end(
JSON.stringify({
data: 'welcome',
version: version,
build: process.argv[3],
lang: 'js',
hostname: os.hostname(),
})
);
});

server.listen(port, bind, () => {
console.log(`Server running at http://${bind}:${port}/`)
})
65 changes: 65 additions & 0 deletions build.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Build server - Jenkins Docker container on GCP VM

variable "app_repo" {}
variable "size" {}
variable "public_key" {}
variable "private_key" {}

locals {
yum = "sudo yum -y -d 1 install"
image = "centos-7"
user = "centos"
}

resource "google_compute_instance" "build" {
name = "build"
machine_type = "${var.size}"

boot_disk {
initialize_params {
image = "${local.image}"
}
}

network_interface {
network = "${data.google_compute_network.net.name}"
access_config = {}
}

metadata {
sshKeys = "${local.user}:${file(var.public_key)}"
}

connection {
user = "${local.user}"
private_key = "${file(var.private_key)}"
}

provisioner "file" {
source = "jenkins"
destination = "~"
}

provisioner "file" {
content = "${local.kubeconfig}"
destination = "config"
}

provisioner "remote-exec" {
inline = [
"${local.yum} docker git mc",
"sudo systemctl start docker",
"sudo git clone ${var.app_repo} && cd cicd-tools-gcp-terraform/jenkins",
"sudo docker build -t jenkins . && cd ~",
"sudo chmod 777 /var/run/docker.sock",
"sudo docker run -d -p 8080:8080 -v /var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock --privileged jenkins",
"sudo docker exec jenkins kubectl config set-cluster kube",
"sudo docker cp config jenkins:/var/jenkins_home/.kube",
]
}

}

output "build" {
value = "${google_compute_instance.build.network_interface.0.access_config.0.assigned_nat_ip}:8080"
}
28 changes: 28 additions & 0 deletions jenkins/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
FROM jenkins/jenkins:lts

ENV JAVA_OPTS -Djenkins.install.runSetupWizard=false

# Note: Latest version of kubectl may be found at:
# https://aur.archlinux.org/packages/kubectl-bin/
ENV KUBE_LATEST_VERSION="v1.11.9"

# Note: Latest version of helm may be found at:
# https://github.com/kubernetes/helm/releases
ENV HELM_VERSION="v2.13.1"

RUN install-plugins.sh github-branch-source workflow-aggregator

USER root

RUN apt-get update && apt-get install -y apt-transport-https

ADD https://storage.googleapis.com/kubernetes-release/release/${KUBE_LATEST_VERSION}/bin/linux/amd64/kubectl /usr/local/bin/kubectl

RUN chmod +x /usr/local/bin/kubectl
RUN curl -sSL https://get.docker.com/ | sh
RUN usermod -aG docker jenkins

RUN wget -q https://storage.googleapis.com/kubernetes-helm/helm-${HELM_VERSION}-linux-amd64.tar.gz -O - | tar -xzO linux-amd64/helm > /usr/local/bin/helm \
RUN chmod +x /usr/local/bin/helm

USER jenkins
36 changes: 36 additions & 0 deletions jenkins/Jenkinsfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
pipeline {
agent any
environment {
app_repo = "https://github.com/incubus8/gcp-cicd-terraform-jenkins.git"
project_id = "PROJECT_ID"
}
stages {
stage('Build') {
steps {
git branch: 'master', url: "${env.app_repo}"
sh "echo 'CMD [\"5000\", \"$BUILD_NUMBER\"]' >> app/Dockerfile"
sh "docker build -t app --build-arg port=5000 app/."
sh "docker tag app gcr.io/${env.project_id}/app:$BUILD_NUMBER"
withCredentials([file(credentialsId: 'SERVICE_ACCOUNT_KEY', variable: 'FILE')]) {
sh "docker login -u _json_key --password-stdin https://gcr.io < $FILE"
}
sh "docker push gcr.io/${env.project_id}/app:$BUILD_NUMBER"
}
}
stage('SonarQube') {
steps {
script {
scannerHome = tool 'scanner'
}
withSonarQubeEnv('sonar') {
sh "${scannerHome}/bin/sonar-scanner"
}
}
}
stage('Deploy') {
steps {
sh "kubectl set image deployment/app app=gcr.io/${env.project_id}/app:$BUILD_NUMBER"
}
}
}
}
52 changes: 52 additions & 0 deletions kube.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Creates a GKE cluster with KUBECONFIG

variable "master_user" {}
variable "master_pass" {}

locals {
kubeconfig = <<KUBECONFIG
apiVersion: v1
clusters:
- cluster:
insecure-skip-tls-verify: true
server: https://${google_container_cluster.kube.endpoint}
name: kube
contexts:
- context:
cluster: kube
user: admin
name: kube
current-context: kube
kind: Config
preferences: {}
users:
- name: admin
user:
username: ${var.master_user}
password: ${var.master_pass}
KUBECONFIG
}

resource "google_container_cluster" "kube" {
name = "kube"
zone = "us-central1-a"
initial_node_count = 2

additional_zones = [
"us-central1-b",
]

master_auth {
username = "${var.master_user}"
password = "${var.master_pass}"
}

node_config {
oauth_scopes = [
"https://www.googleapis.com/auth/compute",
"https://www.googleapis.com/auth/devstorage.read_only",
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring",
]
}
}
17 changes: 17 additions & 0 deletions network.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Defining ingress firewall rules

data "google_compute_network" "net" {
name = "default"
}

resource "google_compute_firewall" "fw" {
name = "fw"
network = "${data.google_compute_network.net.name}"

allow {
protocol = "tcp"
ports = ["8080"]
}

source_ranges = ["0.0.0.0/0"]
}
21 changes: 21 additions & 0 deletions provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Defining google/kubernetest providers

variable "project" {}
variable "region" {}
variable "zone" {}

provider "google" {
credentials = "${file(".key/account.json")}"
project = "${var.project}"
region = "${var.region}"
zone = "${var.zone}"
}

provider "kubernetes" {
host = "https://${google_container_cluster.kube.endpoint}"
insecure = true
load_config_file = false

username = "${google_container_cluster.kube.master_auth.0.username}"
password = "${google_container_cluster.kube.master_auth.0.password}"
}
1 change: 1 addition & 0 deletions secrets.key
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
terraform-secret
Loading

0 comments on commit 64fb604

Please sign in to comment.