├── .gitignore ├── README.md └── topics ├── ansible └── ansible-practice01 │ ├── Dockerfile │ ├── README.md │ ├── ansible.cfg │ ├── inventory │ └── hosts.ini │ ├── playbooks │ ├── site.yml │ └── tasks │ │ ├── common.yml │ │ ├── install_docker.yml │ │ ├── start_containers.yml │ │ └── verify.yml │ ├── requirements.yml │ └── roles │ └── docker │ ├── tasks │ └── main.yml │ └── templates │ └── docker-compose.yml.j2 ├── aws └── README.md ├── groovy └── practice01.groovy ├── jenkins ├── jenkins-practice01 │ └── Jenkinsfile01_1 └── setup │ ├── README.md │ └── docker-compose.yaml ├── k8s ├── README.md └── write-own-manifest │ └── deployment.yaml ├── monitoring ├── assets │ └── grafana.png └── setup │ ├── README.md │ ├── alertmanager │ └── config.yml │ ├── docker-compose.yml │ ├── grafana │ ├── dashboards │ │ └── node_exporter_dashboard.json │ └── provisioning │ │ ├── dashboards │ │ └── dashboard.yml │ │ └── datasources │ │ └── datasource.yml │ ├── loki │ ├── loki-config.yml │ └── promtail-config.yml │ └── prometheus │ └── prometheus.yml ├── mysql ├── README.md ├── mysql-practice01 │ └── backup-restore.md ├── mysql-practice02 │ ├── practice-02.md │ └── practice02.sql └── setup │ ├── README.md │ ├── backup.sql │ ├── docker-compose.yaml │ └── init.sql ├── python ├── python-practice01 │ ├── practice01.py │ └── samples │ │ └── example.txt └── python-practice02 │ ├── README.md │ ├── practice02.py │ └── requirements.txt ├── redis └── redis-practice01 │ ├── README.md │ └── docker-compose.yaml ├── shell ├── shell-practice01 │ ├── backup.tar.gz │ ├── backup │ │ ├── example.txt │ │ └── test.txt │ └── practice01.sh └── shell-practice02 │ └── string-compare.sh └── terraform └── terraform-practice01 ├── README.md ├── assets └── result_01.png ├── main.tf ├── modules ├── network │ ├── main.tf │ ├── outputs.tf │ └── variables.tf └── vm │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── outputs.tf ├── provider.tf ├── terraform.tfvars.sample └── variables.tf /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | crash.*.log 11 | 12 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 13 | # password, private keys, and other secrets. These should not be part of version 14 | # control as they are data points which are potentially sensitive and subject 15 | # to change depending on the environment. 16 | *.tfvars 17 | *.tfvars.json 18 | 19 | # Ignore override files as they are usually used to override resources locally and so 20 | # are not checked in 21 | override.tf 22 | override.tf.json 23 | *_override.tf 24 | *_override.tf.json 25 | 26 | # Ignore transient lock info files created by terraform apply 27 | .terraform.tfstate.lock.info 28 | 29 | # Include override files you do wish to add to version control using negated pattern 30 | # !example_override.tf 31 | 32 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 33 | # example: *tfplan* 34 | 35 | # Ignore CLI configuration files 36 | .terraformrc 37 | terraform.rc 38 | 39 | *.out 40 | 41 | .terraform.lock.hcl 42 | 43 | # Redis 44 | **/redis/**/data 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # devops-practice 2 | 3 | A place to learn and practice DevOps in the hands-on way 4 |
5 | This is the **second** repo of my DevOps trio repositories: [devops-basics](https://github.com/tungbq/devops-basics) ↔️ [**devops-practice**](https://github.com/tungbq/devops-practice) ↔️ [devops-project](https://github.com/tungbq/devops-project), designed to help you learn, practice, and apply DevOps. 6 | 7 | ## DevOps topics 🔥 8 | 9 | We cover a wide range of practice for various DevOps topics in our content library, explore them under [**topics**](./topics/) 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 |
ItemPractice content
AWSaws
K8sk8s
Ansibleansible
Terraformterraform
Pythonpython
Groovygroovy
Shellshell
Monitoringmonitoring
MySQLmysql
Jenkinsjenkins
Redisredis
73 | 74 | And **more upcoming topics...⏩** you can star/follow this repository to get more up-to-dated content ⭐ 75 | -------------------------------------------------------------------------------- /topics/ansible/ansible-practice01/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | # Install dependencies 4 | RUN apt-get update && apt-get install -y \ 5 | python3-pip \ 6 | python3-apt \ 7 | software-properties-common \ 8 | && apt-get clean 9 | 10 | # Install Ansible 11 | RUN pip3 install ansible 12 | 13 | # Copy the Ansible files 14 | COPY . /ansible 15 | 16 | # Set working directory 17 | WORKDIR /ansible 18 | 19 | # Entry point 20 | CMD ["ansible-playbook", "playbooks/site.yml"] 21 | -------------------------------------------------------------------------------- /topics/ansible/ansible-practice01/README.md: -------------------------------------------------------------------------------- 1 | ## Structure 2 | 3 | ```bash 4 | . 5 | ├── Dockerfile 6 | ├── README.md 7 | ├── ansible.cfg 8 | ├── inventory 9 | │ └── hosts.ini 10 | ├── playbooks 11 | │ ├── site.yml 12 | │ └── tasks 13 | │ ├── common.yml 14 | │ ├── install_docker.yml 15 | │ ├── start_containers.yml 16 | │ └── verify.yml 17 | ├── requirements.yml 18 | └── roles 19 | └── docker 20 | ├── tasks 21 | │ └── main.yml 22 | └── templates 23 | └── docker-compose.yml.j2 24 | ``` 25 | 26 | ## Run the practice01 27 | 28 | ```bash 29 | cd topics/ansible/ansible-practice01 30 | 31 | docker build -t ansible-controller . 32 | 33 | docker run -v $(pwd):/ansible ansible-controller 34 | ``` 35 | -------------------------------------------------------------------------------- /topics/ansible/ansible-practice01/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | inventory = ./inventory/hosts 3 | host_key_checking = False 4 | -------------------------------------------------------------------------------- /topics/ansible/ansible-practice01/inventory/hosts.ini: -------------------------------------------------------------------------------- 1 | [target] 2 | target-host ansible_host=172.18.0.2 ansible_user=root ansible_python_interpreter=/usr/bin/python3 3 | -------------------------------------------------------------------------------- /topics/ansible/ansible-practice01/playbooks/site.yml: -------------------------------------------------------------------------------- 1 | - name: Deploy Docker on target host 2 | hosts: target 3 | become: true 4 | roles: 5 | - docker 6 | 7 | - name: Start application containers 8 | hosts: target 9 | become: true 10 | tasks: 11 | - include_tasks: start_containers.yml 12 | 13 | - name: Verify setup 14 | hosts: target 15 | become: true 16 | tasks: 17 | - include_tasks: verify.yml 18 | -------------------------------------------------------------------------------- /topics/ansible/ansible-practice01/playbooks/tasks/common.yml: -------------------------------------------------------------------------------- 1 | - name: Ensure dependencies are installed 2 | apt: 3 | name: "{{ item }}" 4 | state: present 5 | with_items: 6 | - apt-transport-https 7 | - ca-certificates 8 | - curl 9 | - software-properties-common 10 | -------------------------------------------------------------------------------- /topics/ansible/ansible-practice01/playbooks/tasks/install_docker.yml: -------------------------------------------------------------------------------- 1 | - name: Install Docker GPG key 2 | apt_key: 3 | url: https://download.docker.com/linux/ubuntu/gpg 4 | state: present 5 | 6 | - name: Add Docker APT repository 7 | apt_repository: 8 | repo: deb https://download.docker.com/linux/ubuntu focal stable 9 | state: present 10 | 11 | - name: Install Docker 12 | apt: 13 | name: docker-ce 14 | state: present 15 | update_cache: yes 16 | -------------------------------------------------------------------------------- /topics/ansible/ansible-practice01/playbooks/tasks/start_containers.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tungbq/devops-practice/6120e52bbf43b949153513bf009d73060b4ceaab/topics/ansible/ansible-practice01/playbooks/tasks/start_containers.yml -------------------------------------------------------------------------------- /topics/ansible/ansible-practice01/playbooks/tasks/verify.yml: -------------------------------------------------------------------------------- 1 | - name: Check Docker service 2 | systemd: 3 | name: docker 4 | state: started 5 | enabled: true 6 | 7 | - name: Ensure application containers are running 8 | docker_container: 9 | name: app_container 10 | state: started 11 | -------------------------------------------------------------------------------- /topics/ansible/ansible-practice01/requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | roles: 3 | - name: geerlingguy.docker 4 | version: 4.0.2 5 | -------------------------------------------------------------------------------- /topics/ansible/ansible-practice01/roles/docker/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Include common tasks 2 | include_tasks: "{{ item }}" 3 | with_items: 4 | - common.yml 5 | - install_docker.yml 6 | -------------------------------------------------------------------------------- /topics/ansible/ansible-practice01/roles/docker/templates/docker-compose.yml.j2: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | app: 4 | image: nginx 5 | ports: 6 | - "80:80" 7 | -------------------------------------------------------------------------------- /topics/aws/README.md: -------------------------------------------------------------------------------- 1 | # AWS practice place 2 | 3 | k8s practice place with various samples 4 | 5 | ## Practice content💥 6 | 7 | - Practice 10+ hands-on example in [aws-lab-with-terraform](https://github.com/tungbq/aws-lab-with-terraform) 8 | -------------------------------------------------------------------------------- /topics/groovy/practice01.groovy: -------------------------------------------------------------------------------- 1 | // 1. Variables and Data Types 2 | def greeting = "Hello, Groovy!" 3 | def number = 42 4 | def decimalNumber = 3.14 5 | def isGroovyFun = true 6 | 7 | println(greeting) 8 | println("Number: $number, Decimal: $decimalNumber, Is Groovy fun? $isGroovyFun") 9 | 10 | // 2. Lists and Maps (Collections) 11 | def fruits = ['Apple', 'Banana', 'Cherry'] 12 | fruits << 'Mango' // Adding an item to the list 13 | println("Fruits: $fruits") 14 | 15 | def person = [name: 'John Doe', age: 30, city: 'New York'] 16 | person['profession'] = 'Developer' // Adding a key-value pair to the map 17 | println("Person Details: $person") 18 | 19 | // 3. Looping 20 | println("Loop through the list:") 21 | fruits.each { fruit -> 22 | println(fruit) 23 | } 24 | 25 | println("Loop through the map:") 26 | person.each { key, value -> 27 | println("$key: $value") 28 | } 29 | 30 | // 4. Closures 31 | def greet = { name -> 32 | return "Hello, $name!" 33 | } 34 | println(greet('World')) 35 | 36 | def multiply = { x, y -> 37 | return x * y 38 | } 39 | println("Multiplication: 5 * 3 = ${multiply(5, 3)}") 40 | 41 | // 5. Classes and Inheritance 42 | class Animal { 43 | String name 44 | 45 | def speak() { 46 | println("The animal makes a sound") 47 | } 48 | } 49 | 50 | class Cat extends Animal { 51 | def catch_mouse() { 52 | println("Catching the mouses...") 53 | } 54 | } 55 | 56 | class Dog extends Animal { 57 | def speak() { 58 | println("$name barks") 59 | } 60 | } 61 | 62 | def myDog = new Dog(name: 'Rex') 63 | myDog.speak() 64 | 65 | def myCat = new Cat(name: "ABC") 66 | myCat.catch_mouse() 67 | 68 | // 6. Exception Handling 69 | def divide = { a, b -> 70 | try { 71 | return a / b 72 | } catch (ArithmeticException e) { 73 | println("Cannot divide by zero") 74 | } 75 | } 76 | println("Division result: ${divide(10, 0)}") 77 | 78 | // 7. File I/O 79 | def filePath = "example.txt" 80 | def file = new File(filePath) 81 | file.write("This is a sample file.\n") 82 | file.append("Adding a new line to the file.\n") 83 | 84 | println("File Content:") 85 | file.eachLine { line -> 86 | println(line) 87 | } 88 | 89 | // 8. Custom Methods 90 | def sayHello(String name) { 91 | return "Hello, $name!" 92 | } 93 | println(sayHello("Groovy Developer")) 94 | 95 | // 9. Working with Ranges 96 | def range = 1..5 97 | println("Range: $range") 98 | 99 | range.each { number -> 100 | println("Number in range: $number") 101 | } 102 | 103 | // 10. String Manipulation 104 | def text = "Welcome here, have a good day!" 105 | println("Uppercase: ${text.toUpperCase()}") 106 | println("Substring: ${text.substring(0, 6)}") 107 | println("Contains 'awesome': ${text.contains('awesome')}") 108 | -------------------------------------------------------------------------------- /topics/jenkins/jenkins-practice01/Jenkinsfile01_1: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent any 3 | 4 | stages { 5 | stage('Build') { 6 | steps { 7 | echo 'Building...' 8 | } 9 | } 10 | stage('Test') { 11 | steps { 12 | echo 'Testing...' 13 | } 14 | } 15 | stage('Deploy') { 16 | steps { 17 | echo 'Deploying...' 18 | } 19 | } 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /topics/jenkins/setup/README.md: -------------------------------------------------------------------------------- 1 | # Setup Jenkins 2 | 3 | ## With docker compose 4 | 5 | - Run 6 | 7 | ```bash 8 | cd topics/jenkins/setup 9 | 10 | docker-compose up -d 11 | ``` 12 | 13 | - Open your browser and go to http://localhost:8020. You should see the Jenkins setup page. 14 | 15 | - 16 | ```bash 17 | docker exec jenkins_controller_practice cat /var/jenkins_home/secrets/initialAdminPassword 18 | 19 | # 1d86cf0e5fe14608970274adb54ec3ac 20 | ``` -------------------------------------------------------------------------------- /topics/jenkins/setup/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | jenkins: 5 | image: jenkins/jenkins:lts 6 | container_name: jenkins_controller_practice 7 | ports: 8 | # You can replace by your own port 9 | - "8020:8080" 10 | - "50000:50000" 11 | volumes: 12 | - jenkins_data:/var/jenkins_home 13 | # environment: 14 | # - JAVA_OPTS=-Djenkins.install.runSetupWizard=false 15 | 16 | volumes: 17 | jenkins_data: 18 | -------------------------------------------------------------------------------- /topics/k8s/README.md: -------------------------------------------------------------------------------- 1 | # k8s practice place 2 | 3 | k8s practice place with various samples 4 | 5 | ## Practice content💥 6 | 7 | | ID | Content | URL | Status | 8 | | :-- | :----------------------------- | :---------------------------------------------------------- | :----- | 9 | | 01 | Create a fresh k8s cluster | [create-a-fresh-k8s-cluster](./create-a-fresh-k8s-cluster/) | TO-DO | 10 | | 02 | Write your own deployment file | [write-own-manifest](./write-own-manifest) | TO-DO | 11 | | 03 | Deploy sample microservice | [deploy-sample-microservice](./deploy-sample-microservice) | TO-DO | 12 | -------------------------------------------------------------------------------- /topics/k8s/write-own-manifest/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Deployment 3 | metadata: 4 | name: nginx-demo 5 | 6 | spec: 7 | replicas: 3 8 | selector: 9 | matchLabels: 10 | aaa: bbb 11 | template: 12 | metadata: 13 | labels: 14 | app: nginx-demo 15 | 16 | spec: 17 | containers: 18 | - name: nginx-demo 19 | image: nginx:latest 20 | ports: 21 | - containerPort: 80 22 | 23 | -------------------------------------------------------------------------------- /topics/monitoring/assets/grafana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tungbq/devops-practice/6120e52bbf43b949153513bf009d73060b4ceaab/topics/monitoring/assets/grafana.png -------------------------------------------------------------------------------- /topics/monitoring/setup/README.md: -------------------------------------------------------------------------------- 1 | # Prometheus/Grafana/Alert 2 | 3 | ## Deploy 4 | 5 | ```bash 6 | cd topics/monitoring/setup 7 | 8 | docker-compose up -d 9 | ``` 10 | 11 | ## Access 12 | 13 | - http://localhost:9100/metrics 14 | - http://localhost:3000/ (admin/admin) 15 | - http://localhost:9100/metrics 16 | - http://localhost:9090/ -------------------------------------------------------------------------------- /topics/monitoring/setup/alertmanager/config.yml: -------------------------------------------------------------------------------- 1 | route: 2 | group_by: ['alertname'] 3 | group_wait: 10s 4 | group_interval: 10s 5 | repeat_interval: 1h 6 | receiver: 'email' 7 | 8 | receivers: 9 | - name: 'email' 10 | email_configs: 11 | - to: 'you@example.com' 12 | from: 'alertmanager@example.com' 13 | smarthost: 'smtp.example.com:587' 14 | auth_username: 'user' 15 | auth_password: 'password' 16 | -------------------------------------------------------------------------------- /topics/monitoring/setup/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | prometheus: 5 | image: prom/prometheus:latest 6 | container_name: prometheus 7 | volumes: 8 | - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml 9 | ports: 10 | - "9090:9090" 11 | 12 | grafana: 13 | image: grafana/grafana:latest 14 | container_name: grafana 15 | ports: 16 | - "3000:3000" 17 | environment: 18 | - GF_SECURITY_ADMIN_PASSWORD=admin 19 | volumes: 20 | - grafana_data:/var/lib/grafana 21 | - ./grafana/provisioning:/etc/grafana/provisioning 22 | - ./grafana/dashboards:/var/lib/grafana/dashboards 23 | 24 | alertmanager: 25 | image: prom/alertmanager:latest 26 | container_name: alertmanager 27 | volumes: 28 | - ./alertmanager/config.yml:/etc/alertmanager/config.yml 29 | ports: 30 | - "9093:9093" 31 | 32 | loki: 33 | image: grafana/loki:latest 34 | container_name: loki 35 | ports: 36 | - "3100:3100" 37 | volumes: 38 | - ./loki/loki-config.yml:/etc/loki/local-config.yaml 39 | 40 | promtail: 41 | image: grafana/promtail:latest 42 | container_name: promtail 43 | volumes: 44 | - /var/log:/var/log 45 | - ./loki/promtail-config.yml:/etc/promtail/config.yml 46 | command: -config.file=/etc/promtail/config.yml 47 | 48 | node_exporter: 49 | image: prom/node-exporter:latest 50 | container_name: node_exporter 51 | ports: 52 | - "9100:9100" 53 | command: 54 | - '--path.procfs=/host/proc' 55 | - '--path.sysfs=/host/sys' 56 | - '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($|/)' 57 | volumes: 58 | - /proc:/host/proc:ro 59 | - /sys:/host/sys:ro 60 | 61 | volumes: 62 | grafana_data: 63 | -------------------------------------------------------------------------------- /topics/monitoring/setup/grafana/provisioning/dashboards/dashboard.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: 'Node Exporter Dashboards' 5 | orgId: 1 6 | folder: '' 7 | type: file 8 | disableDeletion: false 9 | editable: true 10 | updateIntervalSeconds: 10 11 | options: 12 | path: /var/lib/grafana/dashboards 13 | -------------------------------------------------------------------------------- /topics/monitoring/setup/grafana/provisioning/datasources/datasource.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | datasources: 4 | - name: Prometheus 5 | type: prometheus 6 | access: proxy 7 | url: http://prometheus:9090 8 | isDefault: true 9 | editable: true 10 | -------------------------------------------------------------------------------- /topics/monitoring/setup/loki/loki-config.yml: -------------------------------------------------------------------------------- 1 | auth_enabled: false 2 | 3 | server: 4 | http_listen_port: 3100 5 | 6 | ingester: 7 | lifecycler: 8 | address: 127.0.0.1 9 | ring: 10 | kvstore: 11 | store: inmemory 12 | replication_factor: 1 13 | chunk_idle_period: 5m 14 | chunk_retain_period: 30s 15 | max_transfer_retries: 0 16 | 17 | schema_config: 18 | configs: 19 | - from: 2020-10-24 20 | store: boltdb-shipper 21 | object_store: filesystem 22 | schema: v11 23 | index: 24 | prefix: index_ 25 | period: 24h 26 | 27 | storage_config: 28 | boltdb_shipper: 29 | active_index_directory: /loki/index 30 | cache_location: /loki/cache 31 | shared_store: filesystem 32 | filesystem: 33 | directory: /loki/chunks 34 | 35 | limits_config: 36 | enforce_metric_name: false 37 | reject_old_samples: true 38 | reject_old_samples_max_age: 168h 39 | 40 | chunk_store_config: 41 | max_look_back_period: 0s 42 | 43 | table_manager: 44 | retention_deletes_enabled: true 45 | retention_period: 168h 46 | -------------------------------------------------------------------------------- /topics/monitoring/setup/loki/promtail-config.yml: -------------------------------------------------------------------------------- 1 | server: 2 | http_listen_port: 9080 3 | grpc_listen_port: 0 4 | 5 | positions: 6 | filename: /tmp/positions.yaml 7 | 8 | clients: 9 | - url: http://loki:3100/loki/api/v1/push 10 | 11 | scrape_configs: 12 | - job_name: system 13 | static_configs: 14 | - targets: 15 | - localhost 16 | labels: 17 | job: varlogs 18 | __path__: /var/log/*log 19 | -------------------------------------------------------------------------------- /topics/monitoring/setup/prometheus/prometheus.yml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 15s 3 | 4 | scrape_configs: 5 | - job_name: 'prometheus' 6 | static_configs: 7 | - targets: ['prometheus:9090'] 8 | 9 | - job_name: 'alertmanager' 10 | static_configs: 11 | - targets: ['alertmanager:9093'] 12 | 13 | - job_name: 'grafana' 14 | static_configs: 15 | - targets: ['grafana:3000'] 16 | 17 | - job_name: 'node_exporter' 18 | static_configs: 19 | - targets: ['node_exporter:9100'] -------------------------------------------------------------------------------- /topics/mysql/README.md: -------------------------------------------------------------------------------- 1 | # MYSQL practice 2 | - TODO: Add content for this front page 3 | -------------------------------------------------------------------------------- /topics/mysql/mysql-practice01/backup-restore.md: -------------------------------------------------------------------------------- 1 | ## Prerequisite 2 | 3 | - Deploy and connect to MYSQL DB document: [setup](../setup/) 4 | 5 | ## Backup the Database 6 | 7 | - To create a backup of the exampledb database: 8 | 9 | ```bash 10 | docker exec mysql_container mysqldump -u root -p'rootpassword' exampledb > backup.sql 11 | 12 | # [Warning] Using a password on the command line interface can be insecure. Just for PoC - Not recommended for the prod usage 13 | ``` 14 | 15 | This will create a backup.sql file in your project directory. 16 | 17 | ## Restore the Database 18 | 19 | To restore the database from a backup: 20 | 21 | ```bash 22 | docker exec -i mysql_container mysql -u root -p'rootpassword' exampledb < backup.sql 23 | 24 | # [Warning] Using a password on the command line interface can be insecure. Just for PoC - Not recommended for the prod usage 25 | ``` 26 | -------------------------------------------------------------------------------- /topics/mysql/mysql-practice02/practice-02.md: -------------------------------------------------------------------------------- 1 | ## Prerequisite 2 | 3 | - Deploy and connect to MYSQL DB document: [setup](../setup/) 4 | 5 | ## Loading the New SQL Script 6 | 7 | - To load the practice02.sql script into your existing MySQL container: 8 | 9 | ```bash 10 | cd mysql/ 11 | 12 | docker cp mysql-practice02/practice02.sql mysql_container:/practice02.sql 13 | docker exec -it mysql_container bash -c "mysql -u root -p'rootpassword' exampledb < /practice02.sql" 14 | ``` 15 | 16 | This command copies the practice02.sql file into the running container and then executes it to load the new database schema and data. 17 | 18 | ## Testing the New SQL Commands 19 | 20 | - Connect to MYSQL 21 | 22 | ```bash 23 | docker exec -it mysql_container mysql -u exampleuser -p 24 | 25 | # Enter 'examplepassword' when prompted. 26 | ``` 27 | 28 | - Once you are in the MYSQL container instance, check 29 | 30 | ```sql 31 | USE exampledb; 32 | SELECT * FROM user_details; 33 | 34 | -- Call the Stored Procedure: 35 | CALL GetUsersByDepartment(1); 36 | 37 | -- Test the Trigger: 38 | INSERT INTO users (username, email) VALUES ('mike_ross', 'mike@example.com'); 39 | SELECT * FROM users WHERE username = 'mike_ross'; 40 | ``` 41 | -------------------------------------------------------------------------------- /topics/mysql/mysql-practice02/practice02.sql: -------------------------------------------------------------------------------- 1 | USE exampledb; 2 | 3 | -- Add new table 4 | CREATE TABLE IF NOT EXISTS departments ( 5 | id INT AUTO_INCREMENT PRIMARY KEY, 6 | department_name VARCHAR(100) NOT NULL 7 | ); 8 | 9 | -- Add new data to the existing 'users' table 10 | INSERT INTO users (username, email) VALUES ('jane_doe', 'jane@example.com'); 11 | 12 | -- Insert data into the new 'departments' table 13 | INSERT INTO departments (department_name) VALUES ('Engineering'), ('HR'), ('Finance'); 14 | 15 | -- Add foreign key to users table 16 | ALTER TABLE users ADD COLUMN department_id INT; 17 | 18 | -- Create a view 19 | CREATE VIEW user_details AS 20 | SELECT u.username, u.email, d.department_name 21 | FROM users u 22 | LEFT JOIN departments d ON u.department_id = d.id; 23 | 24 | -- Create a stored procedure 25 | DELIMITER $$ 26 | CREATE PROCEDURE GetUsersByDepartment(IN dept_id INT) 27 | BEGIN 28 | SELECT username, email 29 | FROM users 30 | WHERE department_id = dept_id; 31 | END $$ 32 | DELIMITER ; 33 | 34 | -- Create a trigger 35 | DELIMITER $$ 36 | CREATE TRIGGER before_user_insert 37 | BEFORE INSERT ON users 38 | FOR EACH ROW 39 | BEGIN 40 | IF NEW.department_id IS NULL THEN 41 | SET NEW.department_id = 1; -- Default to 'Engineering' 42 | END IF; 43 | END $$ 44 | DELIMITER ; 45 | -------------------------------------------------------------------------------- /topics/mysql/setup/README.md: -------------------------------------------------------------------------------- 1 | ## Deploy 2 | 3 | ```bash 4 | cd topics/mysql/setup 5 | 6 | docker-compose up -d 7 | ``` 8 | 9 | ## Connect 10 | 11 | - To connect to the MySQL database, you can use a MySQL client or connect from within the container: 12 | 13 | ```bash 14 | docker exec -it mysql_container mysql -u exampleuser -p 15 | # Enter 'examplepassword' when prompted. 16 | ``` 17 | 18 | - Alternatively, connect using a MySQL client from your host: 19 | 20 | ```bash 21 | mysql -h 127.0.0.1 -P 3306 -u exampleuser -p 22 | # Enter 'examplepassword' when prompted. 23 | ``` 24 | -------------------------------------------------------------------------------- /topics/mysql/setup/backup.sql: -------------------------------------------------------------------------------- 1 | -- MySQL dump 10.13 Distrib 8.0.39, for Linux (x86_64) 2 | -- 3 | -- Host: localhost Database: exampledb 4 | -- ------------------------------------------------------ 5 | -- Server version 8.0.39 6 | 7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 10 | /*!50503 SET NAMES utf8mb4 */; 11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 12 | /*!40103 SET TIME_ZONE='+00:00' */; 13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 17 | 18 | -- 19 | -- Table structure for table `users` 20 | -- 21 | 22 | DROP TABLE IF EXISTS `users`; 23 | /*!40101 SET @saved_cs_client = @@character_set_client */; 24 | /*!50503 SET character_set_client = utf8mb4 */; 25 | CREATE TABLE `users` ( 26 | `id` int NOT NULL AUTO_INCREMENT, 27 | `username` varchar(50) NOT NULL, 28 | `email` varchar(50) NOT NULL, 29 | `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, 30 | PRIMARY KEY (`id`) 31 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; 32 | /*!40101 SET character_set_client = @saved_cs_client */; 33 | 34 | -- 35 | -- Dumping data for table `users` 36 | -- 37 | 38 | LOCK TABLES `users` WRITE; 39 | /*!40000 ALTER TABLE `users` DISABLE KEYS */; 40 | INSERT INTO `users` VALUES (1,'john_doe','john@example.com','2024-08-21 04:13:34'); 41 | /*!40000 ALTER TABLE `users` ENABLE KEYS */; 42 | UNLOCK TABLES; 43 | /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; 44 | 45 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; 46 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; 47 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; 48 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 49 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 50 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 51 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 52 | 53 | -- Dump completed on 2024-08-21 4:15:42 54 | -------------------------------------------------------------------------------- /topics/mysql/setup/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | mysql: 5 | image: mysql:8.0 6 | container_name: mysql_container 7 | restart: always 8 | environment: 9 | MYSQL_ROOT_PASSWORD: rootpassword 10 | MYSQL_DATABASE: exampledb 11 | MYSQL_USER: exampleuser 12 | MYSQL_PASSWORD: examplepassword 13 | ports: 14 | - "3306:3306" 15 | volumes: 16 | - mysql_data:/var/lib/mysql 17 | - ./init.sql:/docker-entrypoint-initdb.d/init.sql 18 | 19 | volumes: 20 | mysql_data: 21 | -------------------------------------------------------------------------------- /topics/mysql/setup/init.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS exampledb; 2 | 3 | USE exampledb; 4 | 5 | CREATE TABLE IF NOT EXISTS users ( 6 | id INT AUTO_INCREMENT PRIMARY KEY, 7 | username VARCHAR(50) NOT NULL, 8 | email VARCHAR(50) NOT NULL, 9 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 10 | ); 11 | 12 | INSERT INTO users (username, email) VALUES ('john_doe', 'john@example.com'); 13 | 14 | CREATE USER 'exampleuser'@'%' IDENTIFIED BY 'examplepassword'; 15 | GRANT ALL PRIVILEGES ON exampledb.* TO 'exampleuser'@'%'; 16 | FLUSH PRIVILEGES; 17 | -------------------------------------------------------------------------------- /topics/python/python-practice01/practice01.py: -------------------------------------------------------------------------------- 1 | # 1. Variables and Data Types 2 | greeting = "Hello, Python!" 3 | number = 42 4 | decimal_number = 3.14 5 | is_python_fun = True 6 | 7 | print(greeting) 8 | print(f"Number: {number}, Decimal: {decimal_number}, Is Python fun? {is_python_fun}") 9 | 10 | # 2. Lists and Dictionaries (Collections) 11 | fruits = ['Apple', 'Banana', 'Cherry'] 12 | fruits.append('Mango') # Adding an item to the list 13 | print(f"Fruits: {fruits}") 14 | 15 | person = {'name': 'John Doe', 'age': 30, 'city': 'New York'} 16 | person['profession'] = 'Developer' # Adding a key-value pair to the dictionary 17 | print(f"Person Details: {person}") 18 | 19 | # 3. Looping 20 | print("Loop through the list:") 21 | for fruit in fruits: 22 | print(fruit) 23 | 24 | print("Loop through the dictionary:") 25 | for key, value in person.items(): 26 | print(f"{key}: {value}") 27 | 28 | # 4. Functions 29 | def greet(name): 30 | return f"Hello, {name}!" 31 | 32 | print(greet('World')) 33 | 34 | def multiply(x, y): 35 | return x * y 36 | 37 | print(f"Multiplication: 5 * 3 = {multiply(5, 3)}") 38 | 39 | # 5. Classes and Inheritance 40 | class Animal: 41 | def __init__(self, name): 42 | self.name = name 43 | 44 | def speak(self): 45 | print("The animal makes a sound") 46 | 47 | class Dog(Animal): 48 | def speak(self): 49 | print(f"{self.name} barks") 50 | 51 | my_dog = Dog(name='Rex') 52 | my_dog.speak() 53 | 54 | # 6. Exception Handling 55 | def divide(a, b): 56 | try: 57 | return a / b 58 | except ZeroDivisionError: 59 | print("Cannot divide by zero") 60 | return None 61 | 62 | print(f"Division result: {divide(10, 0)}") 63 | 64 | # 7. File I/O 65 | file_path = "samples/example.txt" 66 | with open(file_path, 'w') as file: 67 | file.write("This is a sample file.\n") 68 | file.write("Adding a new line to the file.\n") 69 | 70 | print("File Content:") 71 | with open(file_path, 'r') as file: 72 | for line in file: 73 | print(line.strip()) 74 | 75 | # 8. List Comprehensions 76 | squares = [x**2 for x in range(10)] 77 | print(f"Squares of numbers 0-9: {squares}") 78 | 79 | # 9. Custom Functions 80 | def say_hello(name): 81 | return f"Hello, {name}!" 82 | 83 | print(say_hello("Python Developer")) 84 | 85 | # 10. String Manipulation 86 | text = "Python is awesome!" 87 | print(f"Uppercase: {text.upper()}") 88 | print(f"Substring: {text[0:6]}") 89 | print(f"Contains 'awesome': {'awesome' in text}") 90 | 91 | # 11. Working with Sets 92 | unique_numbers = {1, 2, 3, 4, 4, 5} 93 | unique_numbers.add(6) 94 | print(f"Unique numbers: {unique_numbers}") 95 | 96 | # 12. Working with Tuples 97 | coordinates = (10, 20) 98 | print(f"Coordinates: {coordinates}") 99 | -------------------------------------------------------------------------------- /topics/python/python-practice01/samples/example.txt: -------------------------------------------------------------------------------- 1 | hello -------------------------------------------------------------------------------- /topics/python/python-practice02/README.md: -------------------------------------------------------------------------------- 1 | ### Install dependencies 2 | ```bash 3 | pip install -r requirements.txt 4 | ``` 5 | -------------------------------------------------------------------------------- /topics/python/python-practice02/practice02.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | from datetime import datetime, timedelta 4 | import matplotlib.pyplot as plt 5 | import pandas as pd 6 | 7 | # 1. Using the 'requests' library to make an HTTP GET request 8 | def fetch_data(url): 9 | response = requests.get(url) 10 | if response.status_code == 200: 11 | print(f"Data fetched successfully from {url}") 12 | return response.json() 13 | else: 14 | print(f"Failed to fetch data. Status code: {response.status_code}") 15 | return None 16 | 17 | url = "https://jsonplaceholder.typicode.com/posts" 18 | data = fetch_data(url) 19 | 20 | # 2. Handling JSON Data 21 | if data: 22 | print(f"First Post Title: {data[0]['title']}") 23 | print(f"Total Posts Fetched: {len(data)}") 24 | 25 | # Saving JSON data to a file 26 | with open('posts.json', 'w') as file: 27 | json.dump(data, file, indent=4) 28 | 29 | print("Data saved to posts.json") 30 | 31 | # 3. Using 'datetime' to work with dates and times 32 | today = datetime.now() 33 | one_week_ago = today - timedelta(days=7) 34 | 35 | print(f"Today's date: {today.strftime('%Y-%m-%d')}") 36 | print(f"One week ago: {one_week_ago.strftime('%Y-%m-%d')}") 37 | 38 | # 4. Data Analysis with 'pandas' 39 | # Creating a DataFrame from JSON data 40 | if data: 41 | df = pd.DataFrame(data) 42 | print(f"DataFrame Head:\n{df.head()}") 43 | print(f"DataFrame Info:\n{df.info()}") 44 | 45 | # Filtering the DataFrame 46 | user_1_posts = df[df['userId'] == 1] 47 | print(f"Posts by User 1:\n{user_1_posts[['id', 'title']]}") 48 | 49 | # 5. Plotting with 'matplotlib' 50 | # Let's plot the number of posts per user 51 | if data: 52 | posts_per_user = df['userId'].value_counts() 53 | posts_per_user.sort_index(inplace=True) 54 | 55 | plt.figure(figsize=(10, 5)) 56 | posts_per_user.plot(kind='bar', color='skyblue') 57 | plt.title('Number of Posts per User') 58 | plt.xlabel('User ID') 59 | plt.ylabel('Number of Posts') 60 | plt.xticks(rotation=0) 61 | plt.grid(axis='y', linestyle='--') 62 | plt.tight_layout() 63 | 64 | # Save the plot to a file 65 | plt.savefig('posts_per_user.png') 66 | print("Plot saved to posts_per_user.png") 67 | 68 | # Show the plot 69 | plt.show() 70 | 71 | # 6. Using 'pandas' for more advanced analysis 72 | # Adding a column with the length of each post's title 73 | if data: 74 | df['title_length'] = df['title'].apply(len) 75 | print(f"Updated DataFrame with Title Length:\n{df[['id', 'title', 'title_length']].head()}") 76 | 77 | # Calculate the average title length per user 78 | avg_title_length_per_user = df.groupby('userId')['title_length'].mean() 79 | print(f"Average Title Length per User:\n{avg_title_length_per_user}") 80 | -------------------------------------------------------------------------------- /topics/python/python-practice02/requirements.txt: -------------------------------------------------------------------------------- 1 | matplotlib 2 | pandas 3 | -------------------------------------------------------------------------------- /topics/redis/redis-practice01/README.md: -------------------------------------------------------------------------------- 1 | # Create and access the Redis cluster 2 | 3 | This practice sample helps you learn how to set up and access a redis cluster with docker compose 4 | 5 | ## Steps to Create the Redis Cluster 6 | 7 | - Bring up the Redis nodes: 8 | 9 | ```bash 10 | cd topics/redis/redis-practice01 11 | docker-compose up -d 12 | ``` 13 | 14 | - Check that all Redis containers are running: 15 | 16 | ```bash 17 | docker ps 18 | ``` 19 | 20 | ## Create the Redis Cluster: 21 | 22 | We will need to execute the following command from one of the Redis nodes, connecting the 4 nodes to form the cluster. In this example, we’ll connect from redis-node-1. 23 | 24 | - Enter the Redis container: 25 | 26 | ```bash 27 | docker exec -it sh 28 | ``` 29 | 30 | - Then run the following Redis CLI command to create the cluster: 31 | 32 | ```bash 33 | redis-cli --cluster create 172.18.0.2:6379 172.18.0.3:6379 172.18.0.4:6379 172.18.0.5:6379 172.18.0.6:6379 172.18.0.7:6379 --cluster-replicas 1 34 | 35 | # This command will create a cluster with 3 master nodes and 3 replica nodes, where each master will have one replica. 36 | # >>> Performing hash slots allocation on 6 nodes... 37 | # Master[0] -> Slots 0 - 5460 38 | # Master[1] -> Slots 5461 - 10922 39 | # Master[2] -> Slots 10923 - 16383 40 | # Adding replica 172.18.0.6:6379 to 172.18.0.2:6379 41 | # Adding replica 172.18.0.7:6379 to 172.18.0.3:6379 42 | # Adding replica 172.18.0.5:6379 to 172.18.0.4:6379 43 | ``` 44 | 45 | - Check cluster status: 46 | After the cluster is created, you can check its status by connecting to any Redis node and running: 47 | 48 | ```bash 49 | redis-cli -c -h 172.18.0.2 -p 6379 cluster nodes 50 | ``` 51 | 52 | ## Example of Using the Redis Cluster 53 | 54 | Once the cluster is set up, you can use the following steps to interact with it: 55 | 56 | - Connect to the Redis CLI: 57 | 58 | ```bash 59 | redis-cli -c -h 172.18.0.2 -p 6379 60 | ``` 61 | 62 | - Add key-value pairs: 63 | 64 | ```bash 65 | SET key1 "value1" 66 | ``` 67 | 68 | - Retrieve the value: 69 | 70 | ```bash 71 | GET key1 72 | # Check cluster key distribution: Redis will automatically distribute the keys across the nodes in the cluster. 73 | ``` 74 | -------------------------------------------------------------------------------- /topics/redis/redis-practice01/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | redis-node-1: 5 | image: redis:alpine 6 | command: ["redis-server", "--port", "6379", "--cluster-enabled", "yes", "--cluster-config-file", "/data/nodes.conf", "--cluster-node-timeout", "5000", "--appendonly", "yes"] 7 | volumes: 8 | - ./data/redis-node-1:/data 9 | networks: 10 | redis-cluster: 11 | ipv4_address: 172.18.0.2 12 | 13 | redis-node-2: 14 | image: redis:alpine 15 | command: ["redis-server", "--port", "6379", "--cluster-enabled", "yes", "--cluster-config-file", "/data/nodes.conf", "--cluster-node-timeout", "5000", "--appendonly", "yes"] 16 | volumes: 17 | - ./data/redis-node-2:/data 18 | networks: 19 | redis-cluster: 20 | ipv4_address: 172.18.0.3 21 | 22 | redis-node-3: 23 | image: redis:alpine 24 | command: ["redis-server", "--port", "6379", "--cluster-enabled", "yes", "--cluster-config-file", "/data/nodes.conf", "--cluster-node-timeout", "5000", "--appendonly", "yes"] 25 | volumes: 26 | - ./data/redis-node-3:/data 27 | networks: 28 | redis-cluster: 29 | ipv4_address: 172.18.0.4 30 | 31 | redis-node-4: 32 | image: redis:alpine 33 | command: ["redis-server", "--port", "6379", "--cluster-enabled", "yes", "--cluster-config-file", "/data/nodes.conf", "--cluster-node-timeout", "5000", "--appendonly", "yes"] 34 | volumes: 35 | - ./data/redis-node-4:/data 36 | networks: 37 | redis-cluster: 38 | ipv4_address: 172.18.0.5 39 | 40 | 41 | redis-node-5: 42 | image: redis:alpine 43 | command: ["redis-server", "--port", "6379", "--cluster-enabled", "yes", "--cluster-config-file", "/data/nodes.conf", "--cluster-node-timeout", "5000", "--appendonly", "yes"] 44 | volumes: 45 | - ./data/redis-node-5:/data 46 | networks: 47 | redis-cluster: 48 | ipv4_address: 172.18.0.6 49 | 50 | 51 | redis-node-6: 52 | image: redis:alpine 53 | command: ["redis-server", "--port", "6379", "--cluster-enabled", "yes", "--cluster-config-file", "/data/nodes.conf", "--cluster-node-timeout", "5000", "--appendonly", "yes"] 54 | volumes: 55 | - ./data/redis-node-6:/data 56 | networks: 57 | redis-cluster: 58 | ipv4_address: 172.18.0.7 59 | networks: 60 | redis-cluster: 61 | driver: bridge 62 | ipam: 63 | config: 64 | - subnet: 172.18.0.0/16 65 | -------------------------------------------------------------------------------- /topics/shell/shell-practice01/backup.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tungbq/devops-practice/6120e52bbf43b949153513bf009d73060b4ceaab/topics/shell/shell-practice01/backup.tar.gz -------------------------------------------------------------------------------- /topics/shell/shell-practice01/backup/example.txt: -------------------------------------------------------------------------------- 1 | This is a sample file. 2 | Adding another line. 3 | -------------------------------------------------------------------------------- /topics/shell/shell-practice01/backup/test.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tungbq/devops-practice/6120e52bbf43b949153513bf009d73060b4ceaab/topics/shell/shell-practice01/backup/test.txt -------------------------------------------------------------------------------- /topics/shell/shell-practice01/practice01.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 1. Variables and Basic Operations 4 | greeting="Hello, Bash!" 5 | number=42 6 | name="DevOps Engineer" 7 | 8 | echo "$greeting" 9 | echo "Your name is $name, and your favorite number is $number." 10 | 11 | # 2. Conditionals (if-else) 12 | if [ $number -gt 50 ]; then 13 | echo "The number $number is greater than 50." 14 | else 15 | echo "The number $number is less than or equal to 50." 16 | fi 17 | 18 | # 3. Loops 19 | echo "Counting from 1 to 5:" 20 | for i in {1..5}; do 21 | echo "Number: $i" 22 | done 23 | 24 | # While loop 25 | counter=5 26 | while [ $counter -gt 0 ]; do 27 | echo "Countdown: $counter" 28 | counter=$((counter - 1)) 29 | done 30 | 31 | # 4. Functions 32 | function greet_user() { 33 | local user_name=$1 34 | echo "Hello, $user_name!" 35 | } 36 | 37 | greet_user "Alice" 38 | 39 | # 5. File Manipulation 40 | file_name="example.txt" 41 | 42 | # Create a file and write some text into it 43 | echo "Creating and writing to $file_name" 44 | echo "This is a sample file." > $file_name 45 | echo "Adding another line." >> $file_name 46 | 47 | # Read the file 48 | echo "Reading the contents of $file_name" 49 | cat $file_name 50 | 51 | # Search for a string in the file 52 | if grep -q "sample" $file_name; then 53 | echo "The word 'sample' was found in $file_name." 54 | else 55 | echo "The word 'sample' was not found in $file_name." 56 | fi 57 | 58 | # 6. Working with Directories 59 | echo "Creating a directory named 'backup'" 60 | mkdir -p backup 61 | 62 | # Move the file to the directory 63 | echo "Moving $file_name to the 'backup' directory" 64 | mv $file_name backup/ 65 | 66 | # List the contents of the directory 67 | echo "Contents of the 'backup' directory:" 68 | ls backup/ 69 | 70 | # 7. Using External Commands 71 | echo "Listing all files in the current directory with detailed information:" 72 | ls -la 73 | 74 | # Checking disk usage 75 | echo "Checking disk usage:" 76 | df -h 77 | 78 | # 8. Handling User Input 79 | read -p "Enter a number: " user_number 80 | 81 | if [ $user_number -eq $user_number ] 2>/dev/null; then 82 | echo "You entered a valid number: $user_number" 83 | else 84 | echo "That's not a valid number!" 85 | fi 86 | 87 | # 9. Using Command-Line Arguments 88 | if [ $# -gt 0 ]; then 89 | echo "You provided $# argument(s):" 90 | for arg in "$@"; do 91 | echo " - $arg" 92 | done 93 | else 94 | echo "No arguments provided." 95 | fi 96 | 97 | # 10. Handling Errors and Exit Status 98 | command="ls non_existent_file" 99 | 100 | if $command 2>/dev/null; then 101 | echo "Command succeeded." 102 | else 103 | echo "Command failed with exit status $?." 104 | fi 105 | 106 | # 11. Scheduling Tasks with cron (concept) 107 | # Note: To actually use cron, you'd edit the crontab file using `crontab -e`. 108 | echo "To schedule this script to run daily, add the following line to your crontab:" 109 | # echo "0 0 * * * ./practice01.sh" 110 | 111 | # 12. Compressing and Archiving Files 112 | echo "Compressing the 'backup' directory into 'backup.tar.gz'" 113 | tar -czf backup.tar.gz backup/ 114 | 115 | echo "Script execution completed." 116 | -------------------------------------------------------------------------------- /topics/shell/shell-practice02/string-compare.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 1. Basic String Comparison 4 | string1="hello" 5 | string2="world" 6 | 7 | if [ "$string1" == "$string2" ]; then 8 | echo "String1 is equal to String2" 9 | else 10 | echo "String1 is not equal to String2" 11 | fi 12 | 13 | # 2. Case-Insensitive String Comparison 14 | if [[ "${string1,,}" == "${string2,,}" ]]; then 15 | echo "String1 is equal to String2 (case-insensitive)" 16 | else 17 | echo "String1 is not equal to String2 (case-insensitive)" 18 | fi 19 | 20 | # 3. Checking if a string is empty 21 | empty_string="" 22 | 23 | if [ -z "$empty_string" ]; then 24 | echo "The string is empty" 25 | else 26 | echo "The string is not empty" 27 | fi 28 | 29 | # 4. Checking if a string is non-empty 30 | if [ -n "$string1" ]; then 31 | echo "String1 is non-empty" 32 | else 33 | echo "String1 is empty" 34 | fi 35 | 36 | # 5. String Inequality Check 37 | if [ "$string1" != "$string2" ]; then 38 | echo "String1 is not equal to String2" 39 | else 40 | echo "String1 is equal to String2" 41 | fi 42 | 43 | # 6. Pattern Matching with Wildcards 44 | filename="example.txt" 45 | 46 | if [[ "$filename" == *.txt ]]; then 47 | echo "The file is a .txt file" 48 | else 49 | echo "The file is not a .txt file" 50 | fi 51 | 52 | # 7. Regular Expression Matching (using =~) 53 | email="user@example.com" 54 | 55 | if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then 56 | echo "The email address is valid" 57 | else 58 | echo "The email address is invalid" 59 | fi 60 | 61 | # 8. Comparing Strings Lexicographically 62 | string3="apple" 63 | string4="banana" 64 | 65 | if [[ "$string3" < "$string4" ]]; then 66 | echo "\"$string3\" comes before \"$string4\" in lexicographical order" 67 | elif [[ "$string3" > "$string4" ]]; then 68 | echo "\"$string3\" comes after \"$string4\" in lexicographical order" 69 | else 70 | echo "\"$string3\" and \"$string4\" are equal in lexicographical order" 71 | fi 72 | 73 | # 9. String Length Comparison 74 | string5="short" 75 | string6="muchlongerstring" 76 | 77 | if [ "${#string5}" -lt "${#string6}" ]; then 78 | echo "\"$string5\" is shorter than \"$string6\"" 79 | elif [ "${#string5}" -gt "${#string6}" ]; then 80 | echo "\"$string5\" is longer than \"$string6\"" 81 | else 82 | echo "\"$string5\" and \"$string6\" are of equal length" 83 | fi 84 | -------------------------------------------------------------------------------- /topics/terraform/terraform-practice01/README.md: -------------------------------------------------------------------------------- 1 | ## Terraform practice01 2 | 3 | - Doc: TODO 4 | - Setup with Azure: https://github.com/TheDevOpsHub/TerraformHub/blob/main/Azure/docs/terraform-azure-setup.md 5 | 6 | ## What? 7 | 8 | At the end of this practice, you will be able to create srouce group as below: 9 | ![result_01](./assets/result_01.png) 10 | 11 | ## Structure 12 | 13 | ```bash 14 | . 15 | ├── README.md 16 | ├── main.tf 17 | ├── modules 18 | │ ├── network 19 | │ │ ├── main.tf 20 | │ │ ├── outputs.tf 21 | │ │ ├── variables.tf 22 | │ │ └── vnet.tf 23 | │ └── vm 24 | │ ├── main.tf 25 | │ ├── outputs.tf 26 | │ ├── variables.tf 27 | │ └── vm.tf 28 | ├── outputs.tf 29 | ├── provider.tf 30 | ├── terraform.tfvars 31 | └── variables.tf 32 | ``` 33 | 34 | ## Run 35 | 36 | ```bash 37 | cd cd topics/terraform/terraform-practice01 38 | 39 | # Create terraform.tfvars file 40 | cp terraform.tfvars.sample terraform.tfvars 41 | ## Change the password to your own choice :) 42 | 43 | terraform init 44 | 45 | terraform plan -out="tfplan.out" 46 | 47 | terraform apply "tfplan.out" 48 | ``` 49 | 50 | ## Verify 51 | 52 | - Check on Azure portal: https://portal.azure.com/ 53 | - Get PublicIP and try SSH to the new VM 54 | 55 | ```bash 56 | ssh azureuser@public_IP 57 | # Login with password defined in your `terraform.tfvars` file 58 | ``` 59 | 60 | ## Cleanup 61 | To avoid the unexpected cost, be sure to cleanup your resource if not used anymore 62 | ```bash 63 | terraform destroy 64 | ``` 65 | -------------------------------------------------------------------------------- /topics/terraform/terraform-practice01/assets/result_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tungbq/devops-practice/6120e52bbf43b949153513bf009d73060b4ceaab/topics/terraform/terraform-practice01/assets/result_01.png -------------------------------------------------------------------------------- /topics/terraform/terraform-practice01/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_resource_group" "main" { 2 | name = var.resource_group_name 3 | location = var.location 4 | } 5 | 6 | module "network" { 7 | source = "./modules/network" 8 | resource_group_name = azurerm_resource_group.main.name 9 | location = var.location 10 | vnet_address_space = var.vnet_address_space 11 | subnet_address_prefix = var.subnet_address_prefix 12 | } 13 | 14 | module "vm" { 15 | source = "./modules/vm" 16 | resource_group_name = azurerm_resource_group.main.name 17 | location = var.location 18 | vm_size = var.vm_size 19 | admin_username = var.admin_username 20 | admin_password = var.admin_password 21 | vnet_subnet_id = module.network.subnet_id 22 | } 23 | -------------------------------------------------------------------------------- /topics/terraform/terraform-practice01/modules/network/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_virtual_network" "main" { 2 | name = "terraform-vnet" 3 | address_space = var.vnet_address_space 4 | location = var.location 5 | resource_group_name = var.resource_group_name 6 | } 7 | 8 | resource "azurerm_subnet" "main" { 9 | name = "terraform-subnet" 10 | resource_group_name = var.resource_group_name 11 | virtual_network_name = azurerm_virtual_network.main.name 12 | address_prefixes = [var.subnet_address_prefix] 13 | } 14 | -------------------------------------------------------------------------------- /topics/terraform/terraform-practice01/modules/network/outputs.tf: -------------------------------------------------------------------------------- 1 | output "subnet_id" { 2 | description = "The ID of the subnet" 3 | value = azurerm_subnet.main.id 4 | } 5 | -------------------------------------------------------------------------------- /topics/terraform/terraform-practice01/modules/network/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_name" { 2 | description = "The name of the resource group" 3 | type = string 4 | } 5 | 6 | variable "location" { 7 | description = "The Azure region to deploy resources in" 8 | type = string 9 | } 10 | 11 | variable "vnet_address_space" { 12 | description = "The address space of the virtual network" 13 | type = list(string) 14 | } 15 | 16 | variable "subnet_address_prefix" { 17 | description = "The address prefix for the subnet" 18 | type = string 19 | } 20 | -------------------------------------------------------------------------------- /topics/terraform/terraform-practice01/modules/vm/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_network_security_group" "main" { 2 | name = "terraform-nsg" 3 | location = var.location 4 | resource_group_name = var.resource_group_name 5 | 6 | # Inbound rule for SSH (port 22) 7 | security_rule { 8 | name = "Allow-SSH" 9 | priority = 1000 10 | direction = "Inbound" 11 | access = "Allow" 12 | protocol = "Tcp" 13 | source_port_range = "*" 14 | destination_port_range = "22" 15 | source_address_prefix = "*" 16 | destination_address_prefix = "*" 17 | } 18 | 19 | # Inbound rule for HTTPS (port 443) 20 | security_rule { 21 | name = "Allow-HTTPS" 22 | priority = 1001 23 | direction = "Inbound" 24 | access = "Allow" 25 | protocol = "Tcp" 26 | source_port_range = "*" 27 | destination_port_range = "443" 28 | source_address_prefix = "*" 29 | destination_address_prefix = "*" 30 | } 31 | 32 | # Outbound rule to allow all traffic 33 | security_rule { 34 | name = "Allow-All-Outbound" 35 | priority = 1002 36 | direction = "Outbound" 37 | access = "Allow" 38 | protocol = "*" 39 | source_port_range = "*" 40 | destination_port_range = "*" 41 | source_address_prefix = "*" 42 | destination_address_prefix = "*" 43 | } 44 | 45 | tags = { 46 | environment = "Terraform Demo" 47 | } 48 | } 49 | 50 | resource "azurerm_network_interface" "main" { 51 | name = "terraform-nic" 52 | location = var.location 53 | resource_group_name = var.resource_group_name 54 | 55 | ip_configuration { 56 | name = "internal" 57 | subnet_id = var.vnet_subnet_id 58 | private_ip_address_allocation = "Dynamic" 59 | public_ip_address_id = azurerm_public_ip.main.id 60 | } 61 | } 62 | 63 | # Manages the association between a Network Interface and a Application Security Group 64 | # https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_interface_application_security_group_association 65 | 66 | resource "azurerm_network_interface_security_group_association" "example" { 67 | network_interface_id = azurerm_network_interface.main.id 68 | network_security_group_id = azurerm_network_security_group.main.id 69 | } 70 | 71 | resource "azurerm_public_ip" "main" { 72 | name = "terraform-pubip" 73 | location = var.location 74 | resource_group_name = var.resource_group_name 75 | allocation_method = "Dynamic" 76 | } 77 | 78 | resource "azurerm_virtual_machine" "main" { 79 | name = "terraform-vm" 80 | location = var.location 81 | resource_group_name = var.resource_group_name 82 | network_interface_ids = [azurerm_network_interface.main.id] 83 | vm_size = var.vm_size 84 | 85 | # os_disk { 86 | # name = "myosdisk1" 87 | # caching = "ReadWrite" 88 | # create_option = "FromImage" 89 | # managed_disk_type = "Standard_LRS" 90 | # } 91 | 92 | # https://azuremarketplace.microsoft.com/en-gb/marketplace/apps/kinvolk.flatcar-container-linux-free?tab=Overview 93 | storage_image_reference { 94 | publisher = "Canonical" 95 | offer = "UbuntuServer" 96 | sku = "18.04-LTS" 97 | version = "latest" 98 | } 99 | 100 | storage_os_disk { 101 | name = "osdisk" 102 | caching = "ReadWrite" 103 | create_option = "FromImage" 104 | managed_disk_type = "Standard_LRS" 105 | } 106 | 107 | os_profile { 108 | computer_name = "terraformvm" 109 | admin_username = var.admin_username 110 | admin_password = var.admin_password 111 | } 112 | 113 | os_profile_linux_config { 114 | disable_password_authentication = false 115 | } 116 | 117 | tags = { 118 | environment = "Terraform Demo" 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /topics/terraform/terraform-practice01/modules/vm/outputs.tf: -------------------------------------------------------------------------------- 1 | output "public_ip_address" { 2 | description = "The public IP address of the VM" 3 | value = azurerm_public_ip.main.ip_address 4 | } 5 | -------------------------------------------------------------------------------- /topics/terraform/terraform-practice01/modules/vm/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_name" { 2 | description = "The name of the resource group" 3 | type = string 4 | } 5 | 6 | variable "location" { 7 | description = "The Azure region to deploy resources in" 8 | type = string 9 | } 10 | 11 | variable "vm_size" { 12 | description = "The size of the Virtual Machine" 13 | type = string 14 | } 15 | 16 | variable "admin_username" { 17 | description = "The admin username for the VM" 18 | type = string 19 | } 20 | 21 | variable "admin_password" { 22 | description = "The admin password for the VM" 23 | type = string 24 | } 25 | 26 | variable "vnet_subnet_id" { 27 | description = "The ID of the subnet" 28 | type = string 29 | } 30 | -------------------------------------------------------------------------------- /topics/terraform/terraform-practice01/outputs.tf: -------------------------------------------------------------------------------- 1 | output "vm_public_ip" { 2 | description = "The public IP address of the Virtual Machine" 3 | value = module.vm.public_ip_address 4 | } 5 | -------------------------------------------------------------------------------- /topics/terraform/terraform-practice01/provider.tf: -------------------------------------------------------------------------------- 1 | provider "azurerm" { 2 | features {} 3 | 4 | # Authenticate using the Azure CLI 5 | } 6 | -------------------------------------------------------------------------------- /topics/terraform/terraform-practice01/terraform.tfvars.sample: -------------------------------------------------------------------------------- 1 | admin_password = "YOURPASSHERE!" 2 | -------------------------------------------------------------------------------- /topics/terraform/terraform-practice01/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_name" { 2 | description = "The name of the resource group" 3 | type = string 4 | default = "terraform-rg" 5 | } 6 | 7 | variable "location" { 8 | description = "The Azure region to deploy resources in" 9 | type = string 10 | default = "East US" 11 | } 12 | 13 | variable "vm_size" { 14 | description = "The size of the Virtual Machine" 15 | type = string 16 | default = "Standard_B1s" 17 | } 18 | 19 | variable "admin_username" { 20 | description = "The admin username for the VM" 21 | type = string 22 | default = "azureuser" 23 | } 24 | 25 | variable "admin_password" { 26 | description = "The admin password for the VM" 27 | type = string 28 | } 29 | 30 | variable "vnet_address_space" { 31 | description = "The address space of the virtual network" 32 | type = list(string) 33 | default = ["10.0.0.0/16"] 34 | } 35 | 36 | variable "subnet_address_prefix" { 37 | description = "The address prefix for the subnet" 38 | type = string 39 | default = "10.0.1.0/24" 40 | } 41 | --------------------------------------------------------------------------------