├── .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 | Item |
15 | Practice content |
16 |
17 |
18 |  |
19 | AWS |
20 | aws |
21 |
22 |
23 |  |
24 | K8s |
25 | k8s |
26 |
27 |
28 |  |
29 | Ansible |
30 | ansible |
31 |
32 |
33 |  |
34 | Terraform |
35 | terraform |
36 |
37 |
38 |  |
39 | Python |
40 | python |
41 |
42 |
43 |  |
44 | Groovy |
45 | groovy |
46 |
47 |
48 |  |
49 | Shell |
50 | shell |
51 |
52 |
53 |  |
54 | Monitoring |
55 | monitoring |
56 |
57 |
58 |  |
59 | MySQL |
60 | mysql |
61 |
62 |
63 |  |
64 | Jenkins |
65 | jenkins |
66 |
67 |
68 |  |
69 | Redis |
70 | redis |
71 |
72 |
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 | 
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 |
--------------------------------------------------------------------------------