diff --git a/.gitignore b/.gitignore index 3d8b36a..3a97242 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,8 @@ npm-debug.log* yarn-debug.log* yarn-error.log* + +# binaries +kompose +skaffold +minikube* diff --git a/_history.md b/_history.md new file mode 100644 index 0000000..97435e9 --- /dev/null +++ b/_history.md @@ -0,0 +1,15 @@ +2022-06-21 +clone repo and add deploy key +make run added version to docker file + errors: + +gcloud auth configure-docker + + + +STEPS: +1. Local Development: +npm install +npm run start + +docker run -d -p 3030:3000 --name reactle-dev reactle:dev \ No newline at end of file diff --git a/docker-compose-k8s.yml b/docker-compose-k8s.yml new file mode 100644 index 0000000..2de1a43 --- /dev/null +++ b/docker-compose-k8s.yml @@ -0,0 +1,18 @@ +# To build the entire stack run 'make run' +version: '3.4' + +services: + reactle: + container_name: reactle + restart: unless-stopped + build: + context: . + dockerfile: ./src/Dockerfile + ports: + - 8999:3000 + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost/8999"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 15s \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index b6aaa0a..ce86bed 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,5 @@ # To build the entire stack run 'make run' +version: '3.4' services: reactle: @@ -6,12 +7,12 @@ services: restart: unless-stopped build: context: . - dockerfile: ./docker/app/Dockerfile + dockerfile: ./src/Dockerfile ports: - - 3000:3000 + - 8081:8081 healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:3000"] + test: ["CMD", "curl", "-f", "http://localhost:8081"] interval: 30s timeout: 5s retries: 3 - start_period: 15s + start_period: 15s \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..9d789ce --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,24 @@ +FROM node:16.14.0-alpine3.14 AS node_modules +WORKDIR /app +COPY package-lock.json package.json ./ +RUN npm install +COPY . . + +FROM node_modules AS prod_builder +RUN npm run build + +## Production image +FROM nginx:1.20.2-alpine AS prod +COPY docker/etc/nginx/nginx.conf /etc/nginx/nginx.conf +COPY docker/etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf +COPY --from=prod_builder /app/build /usr/share/nginx/html +COPY docker/build_system.sh . +RUN ./build_system.sh && rm ./build_system.sh +# port use by Nginx within docker network. +EXPOSE 8080 +USER reactle + +## Development image +FROM node_modules AS dev +EXPOSE 3000 +CMD npm run start diff --git a/docker/build_system.sh b/docker/build_system.sh new file mode 100755 index 0000000..8cf1f8f --- /dev/null +++ b/docker/build_system.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +# Strict shell settings. +set -e pipefall; +set -u pipefall; + +UID=1001 +GID=1001 +USER=reactle +GROUP=reactle + +# Enable print for all executed commands +trace_on() { set -x;} +# Disable print for all executed commands +trace_off() { set +x;} + +add_custom_user_group() { + addgroup -g $GID -S $GROUP + adduser -S -D -H \ + -u $UID \ + -G $GROUP \ + -s /sbin/nologin \ + -h /var/cache/nginx \ + $USER +} + +add_user_permission() { + chown -R $USER:$GROUP \ + /usr/share/nginx \ + /var/cache/nginx \ + /var/log/nginx \ + /etc/nginx +} + +trace_on + add_custom_user_group + add_user_permission +trace_off diff --git a/docker/etc/nginx/conf.d/default.conf b/docker/etc/nginx/conf.d/default.conf new file mode 100644 index 0000000..fe3f915 --- /dev/null +++ b/docker/etc/nginx/conf.d/default.conf @@ -0,0 +1,19 @@ +server { + # Since non-root user cannot bind to port below 1024 + # We will use the docker port 8080 instead of port 80. + listen 8080; + listen [::]:8080; + server_name localhost; + + # Location of build files. + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + # Redirect server error pages to the static page /50x.html + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} diff --git a/docker/etc/nginx/nginx.conf b/docker/etc/nginx/nginx.conf new file mode 100644 index 0000000..b4c3a44 --- /dev/null +++ b/docker/etc/nginx/nginx.conf @@ -0,0 +1,36 @@ +# Don't use default nginx user. +# Intead let the non-root user run the nginx. +# user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; + +# The nginx.pid file will be created when the nginx process starts. +# This file can be deleted and created at any number of times during production. +# So, we will store it in /tmp where non-root user have permission to access it. +pid /tmp/nginx.pid; + + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/k8s-manifests/reactle-deployment.yaml b/k8s-manifests/reactle-deployment.yaml new file mode 100644 index 0000000..12b84e4 --- /dev/null +++ b/k8s-manifests/reactle-deployment.yaml @@ -0,0 +1,43 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.1 (a9d05d509) + creationTimestamp: null + labels: + io.kompose.service: reactle + name: reactle +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: reactle + strategy: {} + template: + metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.1 (a9d05d509) + creationTimestamp: null + labels: + io.kompose.service: reactle + spec: + containers: + - image: reactle + livenessProbe: + exec: + command: + - curl + - -f + - http://localhost:8080 + failureThreshold: 3 + initialDelaySeconds: 15 + periodSeconds: 30 + timeoutSeconds: 5 + name: reactle + ports: + - containerPort: 3000 + resources: {} + restartPolicy: Always +status: {} diff --git a/k8s-manifests/reactle-service.yaml b/k8s-manifests/reactle-service.yaml new file mode 100644 index 0000000..24c539f --- /dev/null +++ b/k8s-manifests/reactle-service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.1 (a9d05d509) + creationTimestamp: null + labels: + io.kompose.service: reactle + name: reactle +spec: + ports: + - name: "8001" + port: 8001 + targetPort: 3000 + selector: + io.kompose.service: reactle +status: + loadBalancer: {} diff --git a/k8s/reactle-deployment.yaml b/k8s/reactle-deployment.yaml new file mode 100644 index 0000000..4398855 --- /dev/null +++ b/k8s/reactle-deployment.yaml @@ -0,0 +1,43 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.1 (a9d05d509) + creationTimestamp: null + labels: + io.kompose.service: reactle + name: reactle +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: reactle + strategy: {} + template: + metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.1 (a9d05d509) + creationTimestamp: null + labels: + io.kompose.service: reactle + spec: + containers: + - image: reactle + livenessProbe: + exec: + command: + - curl + - -f + - http://localhost:8081 + failureThreshold: 3 + initialDelaySeconds: 15 + periodSeconds: 30 + timeoutSeconds: 5 + name: reactle + ports: + - containerPort: 8081 + resources: {} + restartPolicy: Always +status: {} diff --git a/k8s/reactle-service.yaml b/k8s/reactle-service.yaml new file mode 100644 index 0000000..04dca87 --- /dev/null +++ b/k8s/reactle-service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.26.1 (a9d05d509) + creationTimestamp: null + labels: + io.kompose.service: reactle + name: reactle +spec: + ports: + - name: "8081" + port: 8081 + targetPort: 8081 + selector: + io.kompose.service: reactle +status: + loadBalancer: {} diff --git a/skaffold.yaml b/skaffold.yaml new file mode 100644 index 0000000..9212826 --- /dev/null +++ b/skaffold.yaml @@ -0,0 +1,36 @@ +apiVersion: skaffold/v2beta28 +kind: Config +metadata: + name: devops + +build: + artifacts: + - image: reactle + context: . + +profiles: + - name: dev + activation: + - command: dev + build: + artifacts: + - image: reactle + context: . + docker: + dockerfile: docker/Dockerfile + buildArgs: + ENV: development + sync: + manual: + - src: 'src/**/**' + dest: . +deploy: + kubectl: + manifests: + - k8s/reactle-deployment.yaml + - k8s/reactle-service.yaml +portForward: + - resourceType: deployment + resourceName: reactle + port: 3000 + localPort: 9000 # *Optional* \ No newline at end of file diff --git a/slides/assets/codespaces-connect.png b/slides/assets/codespaces-connect.png new file mode 100644 index 0000000..d9b1ef7 Binary files /dev/null and b/slides/assets/codespaces-connect.png differ diff --git a/slides/assets/codespaces-launch.png b/slides/assets/codespaces-launch.png new file mode 100644 index 0000000..5623ddd Binary files /dev/null and b/slides/assets/codespaces-launch.png differ diff --git a/slides/assets/containers.png b/slides/assets/containers.png new file mode 100644 index 0000000..60fe0e2 Binary files /dev/null and b/slides/assets/containers.png differ diff --git a/slides/assets/devops-loop.svg b/slides/assets/devops-loop.svg new file mode 100644 index 0000000..630ffe1 --- /dev/null +++ b/slides/assets/devops-loop.svg @@ -0,0 +1 @@ + devops-loop diff --git a/slides/assets/devops.png b/slides/assets/devops.png new file mode 100644 index 0000000..af668b6 Binary files /dev/null and b/slides/assets/devops.png differ diff --git a/slides/assets/discord-bot-server-members-intent.png b/slides/assets/discord-bot-server-members-intent.png new file mode 100644 index 0000000..35858bd Binary files /dev/null and b/slides/assets/discord-bot-server-members-intent.png differ diff --git a/slides/assets/discord-bot-token-copy.png b/slides/assets/discord-bot-token-copy.png new file mode 100644 index 0000000..4e873d5 Binary files /dev/null and b/slides/assets/discord-bot-token-copy.png differ diff --git a/slides/assets/discord-profile-id.png b/slides/assets/discord-profile-id.png new file mode 100644 index 0000000..4cda605 Binary files /dev/null and b/slides/assets/discord-profile-id.png differ diff --git a/slides/assets/grafana-with-skaffold.png b/slides/assets/grafana-with-skaffold.png new file mode 100644 index 0000000..630a1d7 Binary files /dev/null and b/slides/assets/grafana-with-skaffold.png differ diff --git a/slides/assets/k8s-diagram.png b/slides/assets/k8s-diagram.png new file mode 100644 index 0000000..5a19e8e Binary files /dev/null and b/slides/assets/k8s-diagram.png differ diff --git a/slides/assets/pages-workflow.png b/slides/assets/pages-workflow.png new file mode 100644 index 0000000..4ad2ba9 Binary files /dev/null and b/slides/assets/pages-workflow.png differ diff --git a/slides/assets/pages.png b/slides/assets/pages.png new file mode 100644 index 0000000..5f31875 Binary files /dev/null and b/slides/assets/pages.png differ diff --git a/slides/assets/pipeline-example.png b/slides/assets/pipeline-example.png new file mode 100644 index 0000000..58cc137 Binary files /dev/null and b/slides/assets/pipeline-example.png differ diff --git a/slides/assets/project-directories.png b/slides/assets/project-directories.png new file mode 100644 index 0000000..0783efe Binary files /dev/null and b/slides/assets/project-directories.png differ diff --git a/slides/assets/tests-passing.png b/slides/assets/tests-passing.png new file mode 100644 index 0000000..f68e897 Binary files /dev/null and b/slides/assets/tests-passing.png differ diff --git a/slides/slides.md b/slides/slides.md new file mode 100644 index 0000000..b145c8f --- /dev/null +++ b/slides/slides.md @@ -0,0 +1,1009 @@ +--- +# try also 'default' to start simple +theme: geist +# random image from a curated Unsplash collection by Anthony +# like them? see https://unsplash.com/collections/94734566/slidev +background: https://source.unsplash.com/collection/94734566/1920x1080 +# apply any windi css classes to the current slide +class: 'text-center' +# https://sli.dev/custom/highlighters.html +highlighter: shiki +# show line numbers in code blocks +lineNumbers: false +# some information about the slides, markdown enabled +info: | + ## Intro to DevOps with Errbot + An introduction to the world of DevOps + +# persist drawings in exports and build +drawings: + persist: false +--- + +# DevOps for Fun and Profit +## Intro to the software development lifecycle + +
+ + Press Space to begin the journey + +
+ +
+ + + + +
+ + + + + +--- + +# What is DevOps? + +DevOps is a set of combined practices that merge development (Dev) and operations (Ops) together + +- πŸ’‘ Continuous planning +- ⚑ Rapid application development +- πŸ‘― Sharable development environments +- πŸ€– Automated and repeatable builds & tests +- πŸš€ CI/CD pipelines +- πŸ”­ Obervability +- πŸ”’ Security + +
+
+ +Read more about [what is DevOps](https://en.wikipedia.org/wiki/DevOps) + + + + + +--- + +# DevOps Visualized + +![DevOps Visualized](assets/devops-loop.svg) + +--- + +# Plan πŸ’‘ + +The planning phase of DevOps is often [agile software development](https://en.wikipedia.org/wiki/Agile_software_development) + +- β€’ Iterative +- β€’ Continuous +- β€’ Can (and will) change frequently +- β€’ No "big bang" launch, an iterative approach + +> For this demo we will be planning the launch of a Wordle clone. + + + +--- + +# Code πŸ’» + +The coding phase of DevOps is where ideas come to life... and where nightmares are born + +```python +if production == "down": + print("This does not bring joy") +``` + +- πŸ‘― Develop in sharable and automated environments +- πŸ–ŒοΈ Adopt a common code style (use a linter) +- βš™οΈ Use a version control system +- πŸ’Ύ Commit and push often +- πŸ‘€ Work in the open, get feedback, request reviews + +> For the DevOpsDaysLA demo, we will be writting code in [GitHub codespaces](https://github.com/features/codespaces) + + + +--- + +# Build πŸ“¦ + +The build phase of DevOps is where the code is compiled and often saved as an artifact for later deployment + +- πŸ› οΈ Build your application / binaries in a repeatable environment (CI/CD) +- 🐳 Containerize applications and services where you can +- πŸ”’ Secure your software supply chain when building applications +- πŸ”‘ Seperate packages, containers, and binaries from configurations and secrets + +> For the DevOpsDaysLA demo, we will be using Docker to containerize and build our application + + + +--- + +# Test πŸ§ͺ + +The test phase of DevOps is where the application is... tested! + +- πŸ‘― Repeatable test environment (CI/CD + Docker) +- πŸ‘¨β€πŸ”¬ Unit tests +- 🏝️ Staging Environment +- 🌐 Integration tests +- πŸ”¬ Container scanning +- πŸ”Ž SAST & DAST - Code scanning + +> For the DevOpsDaysLA demo, you will get exposure to unit tests, container scanning, and static analysis + + + +--- + +# Release 🏷️ + +The release stage of DevOps is where the application is released to users. + +Examples include: + +- πŸ“¦ Publishing a binary to an opensource repository +- 🏷️ Publishing a release label and Git tag for an application +- πŸ”’ Releasing a new version with an installer for a desktop application + +--- + +# Deploy πŸš€ + +The deploy stage of DevOps is my personal favorite. This is where code is deployed into systems in production + +- 🧱 Infrastructure as Code +- ⏩ CI/CD Pipelines +- πŸš€ Deployment + +--- + +# Operate 🧰 + +The operate stage of DevOps is where the application is used by real users and we react to that usage + +- πŸ—οΈ Scaling +- πŸ› Bug Reports +- πŸ’¬ Continuous feedback +- πŸ”’ Rolling updates for security + +--- + +# Monitor πŸ”Ž + +The monitor stage of DevOps is where the application is "monitored" via its metrics, events, and logs + +- πŸ“Š Gather Metrics +- πŸ“ˆ Visualize and Dashboard all the things +- πŸ“ Collect logs and events +- πŸ” Search and audit for anomalies +- πŸ”” Trigger alerts and notifications + +--- + +# About our Application? πŸ€– + +[Reactle](https://github.com/cwackerfuss/react-wordle) is a based off of an open source repository from [cwackerfuss](https://github.com/cwackerfuss). + +Reactle is built using + +- β€’ React +- β€’ Typescript +- β€’ Tailwind + +> Reactle can be configured and customized for different languages-from Japanese to Latin, Themes, including birdle and Trekle, and even Morse Code! + + + +--- + +# Hands-on Workshop Time πŸ‘ + +In this workshop, we will be doing the following: + +- πŸ“š Setup and explore our IDE +- πŸ’» Get hands-on with different open source development tools +- πŸ“¦ Build the app using [npm](link here), [docker](link here), then finally [skaffold](https://skaffold.dev/) +- ☸️ Deploy the app to Kubernetes +- πŸ§ͺ Ensure our test suite is passing +- πŸ”’ Run SAST on our code and container scanning on our container image +- 🀹 Interact with our new "production" application +- πŸ”­ Observe reactle's usage with Grafana & Loki +- πŸš€ Run a real world CI/CD pipeline and deployment + +
+Plan > Code > Build > Test > Release > Deploy > Operate > Monitor + +--- +# Tech and Tools + +We will be using or learning the following tools during our workshop: +- github + codespaces + actions +- react +- npm +- Docker +- minikube + kubernetes +- skaffold + + +--- +# Getting Started πŸ’‘ + +- 1: Go to [github.com/DevOpsDaysLA/devops](https://github.com/DevOpsDaysLA/devops) and click "Fork" in the upper right corner +- 2: Ensure you fork the repo into the [DevOpsDaysLA](https://github.com/DevOpsDaysLA) organization. Note: If you are not part of the DevOpsDaysLA workshop, you can fork the repo to your personal GitHub account instead +- 3: Upon the successful fork, you will notice a GitHub action workflow has started for your documentation page +- 4: Go to your repo settings and ensure your GitHub pages site is accessible to view your documentation + +![GitHub Pages](assets/pages.png) + +--- + +# GitHub Pages πŸ“„ + +[GitHub Pages](https://pages.github.com/) is a free service for hosting static website content on GitHub.com + +Commits / Pushes to the `main` branch automatically update our documentation site + +```yaml +name: pages +on: + push: + branches: + - main +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # pin@v2 + - uses: actions/setup-python@f38219332975fe8f9c04cca981d674bf22aea1d3 # pin@v2 + with: + python-version: 3.x + - run: pip install mkdocs-material + - run: mkdocs gh-deploy --force +``` + +--- + +# Pages GitHub Actions Workflow ⏩ + +![Pages Workflow](assets/pages-workflow.png) + +Hooray! We not have all the nitty gritty documentation for our bot publically hosted on GitHub Pages! + +--- + +# Setup + +To begin implementing our `Reactle` clone, we need to first start our dev environment: + +**DevOpsDaysLA Workshop:** + +Simply create a new GitHub Codespace + +![GitHub Codespace](assets/codespaces-launch.png) + +**Non-DevOpsDaysLA Workshop:** + +[Public setup documentation](https://errbot.birki.io/) +
+Plan > Code > Build > Test > Release > Deploy > Operate > Monitor + +--- + +# Codespaces πŸ’» + +If you are apart of the DevOpsDaysLA workshop, you will be able to create a new GitHub Codespace for development + +**What is GitHub Codespaces?** + +- ☁️ Cloud hosted development environment +- πŸ‘― Consistent, repeatable, and shareable +- 🚧 Removes the "it works on my machine" barrier +- πŸ”₯ Saves us from dependency hell +- ⭐ Allows developers to deploy a dev environement in one-click and begin working on a project + +You can read more about GitHub Codespaces [here](https://github.com/features/codespaces) + +> Note: If you are not apart of the DevOpsDaysLA workshop, you will **not** have free access to GitHub codespaces and will need to setup errbot [locally for development](https://errbot.birki.io/setup/) + + + +--- + +# Connect to Codespace + +![Connect to Codespace](assets/codespaces-connect.png) + +You will now connect to Codespaces through your browser. If you have [Visual Studio Code](https://code.visualstudio.com/) installed, you can optionally attach there as well. + +Our Codespace environment comes pre-installed with all the dependencies we need to run errbot locally and develop new features. + +> Note: If you are **not** apart of the DevOpsDaysLA workshop, you will be doing all the following steps from here on locally and not in GitHub Codespaces + + + +--- + +# Build and run the App with npm πŸ‘¨β€πŸ”¬ + +Simply run the following commands to start the app with npm: + +```bash +npm install +npm run start +``` + +> Checkout the application running in your browser at [localhost:3000](http://localhost:3000). +> Port 3000 is the default port for a react development server. + + + +--- +# Build and run the App with Docker πŸ‘¨β€πŸ”¬ + +## Development +``` +$ docker build -t reactle:dev -f docker/app/Dockerfile . +$ docker run -d -p 3000:3000 --name reactle-dev reactle:dev +``` +Open http://localhost:3000 in browser. + +## Production +``` +$ docker build --target=prod -t reactle:prod -f docker/app/Dockerfile . +$ docker run -d -p 80:8080 --name reactle-prod reactle:prod +``` +--- +# Run the App with Docker Compose ☸️ + +Open the Dockerfile + +``` +$ make run +``` + +--- +# BREAK +--- +# Install Tools +Install Skaffold, Minikube and Kompose. +- [Skaffold](https://skaffold.dev/docs/install/) +``` +curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64 && +sudo install skaffold /usr/local/bin/ +``` +- [Minikube](https://minikube.sigs.k8s.io/docs/start/) +``` +curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 +sudo install minikube-linux-amd64 /usr/local/bin/minikube +``` +- [Kompose](https://kompose.io/) +Kompose is used by Skaffold to convert the docker-compose.yml into Kubernetes manifests. +``` +curl -L https://github.com/kubernetes/kompose/releases/download/v1.26.1/kompose-linux-amd64 -o kompose +sudo install kompose /usr/local/bin +``` +--- +# Initialize Skaffold +`skaffold init --compose-file docker-compose.yml` + +> ? Choose the builder to build image reactle [Use arrows to move, type to filter] + Buildpacks (package.json) + Docker (docker/app/Dockerfile) + None (image not built from these sources) + + +? Which builders would you like to create kubernetes resources for? [Use arrows to move, space to select, to all, to none, type to filter] + [ ] Buildpacks (package.json) +> [ ] Docker (src/Dockerfile) + +Select Docker + +--- +# Start minikube +``` +minikube start +minikube status +``` + +--- +# Open Skaffold.yaml + +--- +--- +# Run the App with Skaffold ☸️ + +We will be using [Skaffold](https://skaffold.dev/) to run our bot for development (either in Codespaces or locally) + +Start our [minikube](https://minikube.sigs.k8s.io/docs/start/), cluster: + +> Note: You may need to run `sudo chown -R $USER $HOME/.minikube; chmod -R u+wrx $HOME/.minikube` to start your minikube cluster in Codespaces + +```text +minikube start --profile custom + +skaffold config set --global local-cluster true + +eval $(minikube -p custom docker-env) +``` + +Run the bot with Skaffold (and tail the logs): + +```text +skaffold dev --tail=true +``` + + + +--- + +# Development Environment 1,000 Foot Overview πŸ¦… + +While the Skaffold command is running, let's look at our dev env ([source](https://errbot.birki.io/deployment/)): + +![Project Directories](assets/project-directories.png) + +TODO: UPDATE IMAGE +Intermission! +--- + +# What are Containers? 🐳 + +![Containers](assets/containers.png) +Intermission! +--- + +# What is Kubernetes? ☸️ + +Kubernetes, or k8s, is an open source platform that automates container orchestration and application deployment. + +- β€’ Declarative language for defining containers and workloads (k8s manifests) +- β€’ Scalable, highly available, and resilient +- β€’ Open source +- β€’ Creating by Google +Intermission! +--- + +# Kubernetes Diagram πŸ—ΊοΈ + +![k8s diagram](assets/k8s-diagram.png) + +--- + +# Kubernetes Files πŸ“‚ + +The `script/k8s/` directory contains all the files we need to deploy our bot to Kubernetes locally (using Skaffold) + +**Benefits of using Skaffold:** + +- β€’ Live reload (changes to source files or k8s manifests) +- β€’ Handles all `kubectl` commands for us +- β€’ Faster dev cycles +- β€’ Very close to our production environment (if not identical) + +**Alternative dev building options:** + +- 🐳 Docker-compose - The `make run` command from the root of this repo is a wrapper command for building locally with Docker-compose +- ☸️ Minikube - The `make kube` command from the root of this repo is a wrapper command for building locally with minikube using `kubectl` +Intermission! +--- + +# Back to the Terminal πŸ’» + +Going back to our terminal, we should see live output from our bot running in k8s via skaffold. + +Additionally, the following has happened: + +- β€’ 🟒 The configured bot owner got a ping that the bot is now online +- β€’ πŸ’¬ The bot can now been as online on our Discord server +- β€’ ⌨️ We can now type a command and our bot will respond -> `!uptime` + +Let's go ahead and press `ctrl+c` in our terminal running skaffold to stop the bot + +> Note: You can also test out other commands that are available. Try `!help` + + + +--- + +# Creating our very own chat command! + +> πŸ’‘ Plan Stage of DevOps + +We want a new command that returns a link to the DevOps life-cycle + +Discord will conveniently render this link as an image for us. + +This will be a brand new chat command so we will need to implement it and add the associated tests for our new code + + + +--- + +# Code for the Chat Command πŸ“ + +> πŸ’» Code Stage of DevOps + +Another engineer has already written most of the code for our new command. + +Let's copy that code into our `src/errbot/plugins/` directory: + +```text +mkdir src/errbot/plugins/devops + +cp demo/code/devops/* src/errbot/plugins/devops/ +``` + + + +--- + +# Build with Skaffold ☸️ + +> πŸ“¦ Build Stage of DevOps + +Now that we have our new command, we need to build our bot with Skaffold: + +```text +skaffold dev --tail=true +``` + +Once the container starts up, run the new command: `!devops` + + + +--- + +# Fix our Command 🚨 + +> πŸ’» Code Stage of DevOps + +Oh no! Our command is borked! Back to our editor to fix the bad code: + +```python +# Delete these lines +if self.chaos(): + return "CHAOS" +... +# Delete this function +def chaos(self): + """ + This does not bring joy + """ + if random() > 0.5: + return True + else: + return False +``` + +Redeploy and test with `skaffold dev --tail=true` -> `!devops` + + + +--- + +# Lint 🧹 + +> πŸ’» Code Stage of DevOps + +A best pratice for all projects is to use a linter or some form of code standard. + +For this project, we are using [Black](https://black.readthedocs.io/en/stable/). + +**Run the linter:** + +```text +script/lint +``` + + + +--- + +# Run our Test Suite πŸ”¬ + +> πŸ§ͺ Test Stage of DevOps + +The other engineer also wrote some tests for us, let's copy those over as well: + +```text +cp demo/code/tests/devops_test_example.py tests/plugins/test_devops.py +``` + +Run the test suite using [pytest](https://docs.pytest.org/en/latest/): + +```text +script/test +``` + +> Note: Tests like to live in CI/CD pipelines + + + +--- + +# Fix our Test 🚨 + +> πŸ§ͺ Test Stage of DevOps + +Checking our test output, we expect that a a url with a `.jpg` extension is returned. However, the actual image we are returning is a `.png`. Let's fix that: + +```python +# tests/plugins/test_devops.py +assert "demo/assets/devops.png" in testbot.pop_message() +``` + +πŸŽ‰ + +![Tests Passing](assets/tests-passing.png) + + + +--- + +# SAST πŸ”Ž + +> πŸ”’ Security Stage of DevOps - Shift Left! + +[SAST](https://en.wikipedia.org/wiki/Static_application_security_testing) is a testing methodology for detecting security vulnerabilities in software. + +It generally takes place by scanning files and looking for misconfigurations, bad practices, and other security issues. + +> Note: SAST likes to live in CI/CD pipelines + + + +--- + +# KUBESEC πŸ”’ + +> πŸ”’ Security Stage of DevOps - Shift Left! + +[KUBESEC](https://github.com/controlplaneio/kubesec) is another SAST tool for scanning kubernetes manifests. + +**Example Usage:** + +```text +kubesec scan script/k8s/errbot/deployment.yaml | jq +``` + +**Make a check fail and then fix it again:** + +1. 1: Edit: `script\k8s\errbot\deployment.yaml` -> comment out: `runAsUser: 10001` +2. 2: Run: `kubesec scan script/k8s/errbot/deployment.yaml | jq` +3. 3: Observe the output warning that the `runAsUser` check is now failing +4. 4: Fix it back by uncommenting the `runAsUser` field in the k8s manifest + +> Note: KUBESEC likes to live in CI/CD pipelines + + + +--- + +# Trivy πŸ”’ + +> πŸ”’ Security Stage of DevOps - Shift Left! + +[Trivy](https://github.com/aquasecurity/trivy) is a container / misconfiguration / IaC vulnerability scanner. + +**Let's hunt for some vulnerabilities in our container image with Trivy:** + +```text +docker build -t scan-errbot:latest src/errbot/ + +trivy image scan-errbot:latest +``` + +**If no vulnerabilities are found, let's create one πŸ›:** + +Edit: `src/errbot/requirements.txt` -> add: `markdown2==2.2.2` anywhere to that file + +Run the build + scan again (as seen above) + +> Note: Trivy likes to live in CI/CD pipelines + + + +--- + +# Pull Request 🏷️ + +> 🏷️ Release Stage of DevOps + +For the **Release** and **Deploy** steps, we will have to do a bit of *pretending* since we do not have a production environement. + +Our new `!devops` command is all ready to be released to production and used by the world! + +**So far we have done the following:** + +- βœ… Created our new chat command +- βœ… Ran our linter +- βœ… Ran our test suite +- βœ… Built the image and tested locally with Skaffold +- βœ… Ran security checks with TFSEC, Kubesec, and Trivy +- πŸ’‘ Opened a pull request and got proper approvals / peer reviews (pretend) +- 🏷️ Tagged our release with a new version number and updated the change log with our new feature (pretend) + + + +--- + +# Deploy πŸš€ + +> πŸš€ Deploy Stage of DevOps + +We now press the **Merge** button on our pull request and this will automatically trigger our GitHub Actions pipeline to deploy our changes to production! + +Again, you will not have a production environement with a cloud provider as this will incur costs. In the next slides I will give an overview of our GitHub Actions pipeline and do a live demo of a deployment to Azure. + + + +--- + +# GitHub Actions ⏩ + +> πŸš€ Deploy Stage of DevOps + +[GitHub Actions](https://github.com/features/actions) runs two types of pipelines for this project: + +- πŸ’‘ - **Plan:** This is the pipeline that runs before the pull request is merged to plan changes +- πŸš€ - **Deploy:** This is the pipeline that runs after the pull request is merged to `main` to deploy changes + +All GitHub Actions workflow definitions can be found in the `.github/workflows/` directory. + +![Pipeline Example](assets/pipeline-example.png) + + + +--- +# Live Deploy Demo πŸŽ₯ + +> πŸš€ Deploy Stage of DevOps + +TODO: Notes for this slide will be added after the first live demo. Public Pull Requests will be used + + + +--- + +# Operate the Bot πŸ€– + +> 🧰 Operate Stage of DevOps + +Usually, this is customers, clients, or the public would interact with your service. For this demo, we will be those users. + +Go ahead and interact with your running bot and have some fun! + +**Commands to try:** + +- β€’ `!remember is ` - Stores a value in DynamoDB +- β€’ `!insult @user` - Use at your own risk, its crowd sourced +- β€’ `!lmf ` - Get the last League of Legends match for a summoner +- β€’ `!play ` - Join a voice channel and run this command to get some tunes +- β€’ `!crypto ` - Get the current price of a crypto currency +- β€’ `!sparkle @user for ` - Show your appreciation to someone + +> `!help` for a full list of available commands + + + +--- + +# Operations and Scaling + +> 🧰 Operate Stage of DevOps + +Another part of the DevOps journey is to scale your service to meet your needs. + +For this demo we won't be scaling but it is important to know how to do so and what your options are: + +- β€’ **Autoscaling** - Scale your service based on the number of requests (e.g. `kubectl autoscale deployment errbot --cpu-percent=50`) +- β€’ **Horizontal Scaling** - Scale your service based on the number of replicas (e.g. `kubectl scale deployment errbot --replicas=3`) +- β€’ **Vertical Scaling** - Scale your service by making each container use more resources (e.g. `kubectl scale deployment errbot --cpu=2 --memory=2Gi`) + +> Note: Horizontal scaling doesn't work well for traditional chat bots since they will repond multiple times to the same message. + + + +--- + +# Monitoring and Logging with Grafana πŸ“Š + +> πŸ”Ž Monitor Stage of DevOps + +For this demo, we are using Promtail + Loki + Grafana to collect logs and metrics for our chatbot + +1. **1:** Open a new terminal window while `skaffold dev` is still running + +2. **2:** Get your Grafana password (username will be `admin`): + + ```text + kubectl get secret --namespace observability grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo + ``` + +3. **3:** Port forward to your Grafana instance + + ```text + kubectl port-forward --namespace observability service/grafana 3000:80 + ``` + + > If your IDE does not automatically direct you, simply go to http://127.0.0.1:3000/login after port forwarding with kubectl + + + +--- + +# Logs in Grafana + +![Logs in Grafana](assets/grafana-with-skaffold.png) + +--- + +# You made it! + +If you have made it this far, you should have accomplished the following: + +- βœ… Self-hosted your own documenation +- βœ… Added a new feature to your very own chatbot +- βœ… Built, tested, and deployed your container with Skaffold in k8s +- βœ… Checked your application for security vulnerabilities +- βœ… Learned a bit about Kubernetes, Terraform, and tying them together with GitHub Actions through CI/CD +- βœ… Monitored your application with Promtail and Grafana +- βœ… Learned about a new tool or two +- βœ… Gained some insight into the DevOps life-cycle +- βœ… Had fun! + +--- + +# Learn More πŸ“– + +Curious to learn more about DevOps? + +- β€’ [awesome-devops](https://github.com/wmariuss/awesome-devops) - A curated list of DevOps resources +- β€’ [90 Days of DevOps](https://github.com/MichaelCade/90DaysOfDevOps) - A journey through DevOps in 90 days +- β€’ [DevOps Exercises](https://github.com/bregman-arie/devops-exercises) - Questions and interview challenges about DevOps +- β€’ [DevOps Resources](https://github.com/bregman-arie/devops-resources) - A curated repo packed full of DevOps resources +- β€’ [8 Phases of DevOps](https://medium.com/taptuit/the-eight-phases-of-a-devops-pipeline-fda53ec9bba) - The 8 phases of DevOps defined diff --git a/docker/app/Dockerfile b/src/Dockerfile similarity index 96% rename from docker/app/Dockerfile rename to src/Dockerfile index 2692832..93ba8ab 100644 --- a/docker/app/Dockerfile +++ b/src/Dockerfile @@ -10,6 +10,6 @@ FROM base AS prod RUN npm run build RUN chown -R nonroot:nonroot /app/node_modules/.cache -EXPOSE 3000 +EXPOSE 8080 USER nonroot CMD npm run start diff --git a/workshop.md b/workshop.md new file mode 100644 index 0000000..65ad77f --- /dev/null +++ b/workshop.md @@ -0,0 +1,72 @@ +# DRAFT +# DevOps Days LA Workshop +DevOps for Fun and Profit + + Plan > Code > Build > Test > Release > Deploy > Operate > Monitor +## Overview +## Plan + + Sprint Planning Meeting + Feature Design + LETS (BUY) MAKE WORDLE (Again) + but this time for fun and not profit + should we do it for profit? --> Lets Decide Later. [Deploy to Production] + +## Code + Intro: + Coding starts with your IDE + Popular Frameworks and Tools + Front End and back end + + IDE: + What is this? + What are some popular IDEs? + POLL + what will we use today? + +## HANDS ON #1: Setup your IDE adn get ready to code! + +1. Install Visual Studio Code +- Go to https://code.visualstudio.com/ +Download and install locally + + +APPENDIX: If you want to + + +2. + VSCode + git + Skaffold + Docker + Minikube + Codespaces + +## Some Theory + App Patterns: + Three Tier + FE BE etc + Monolith & Microservices + Code vs Containers + Containerization and K8S + +## Build + Manual (?) + Docker Build + Kubernetes + -- Skaffold + -- Reload with a different version + +## Test + +## Release <> Deploy + +## Operate + heroku options + +## Monitor + +--- + +## To-Do +- Quiz or Polls (Forms? Kahoot?)