# Linux 自動化運維
> 這是一個關於Linux 自動化運維的筆記[name=簡志融111010501]
> [[第一篇] Linux 作業系統實務](https://hackmd.io/@Jung217/nqu_linux1)
> [[第二篇] Linux 伺服器架設](https://hackmd.io/@Jung217/nqu_linux2)
## 摘要
[TOC]
## 第一週
### Linux Kernel 重新編譯
> 參考 [Automatic Operation and Maintenance for Linux System](https://github.com/NubletZ/myNotes/blob/master/allNotes/Linux%20Maintenance/Week7.md#update-kernel)
> 建議弄一台新的虛擬機操作,比較容易成功
1. 安裝需要的模組
```
yum install -y ncurses-devel make gcc bc bison flex elfutils-libelf-devel openssl-devel grub2
```
2. 下載壓縮檔 & 解壓縮
```
# wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.19.237.tar.xz
# tar xvJf linux-4.19.237.tar.xz
```
3. 將當前的配置檔複製到新的 CentOS Kernel 配置中 (版本不同可先用 `ls /boot` 查看)
```
cp -v /boot/config-3.10.0-957.el7.x86_64 .config
```
4. 進入資料夾並叫出編譯選單 : `cd ~/linux-4.19.237/`、`make menuconfig`

> `[*]` : 表示將此功能編譯到 kernel 中,但體積會更大
> `<M>` : 將功能變成模組,在需要時將功能載入 kernel 中
> `[ ]` : 功能不會載到 kernel 中
5. 繼續編譯 (# make 之前耗時較長)
```
# make bzImage
# make modules
# make
# make modules_install
# make install
# grub2-mkconfig -o /boot/grub2/grub.cfg
```
6. 重啟 : `reboot`


## 第二週
> 二二八連假
## 第三週
### 好用的擴充軟體
* [沉浸式翻譯: 雙語對照網頁翻譯 & PDF文檔翻譯](https://chromewebstore.google.com/detail/%E6%B2%89%E6%B5%B8%E5%BC%8F%E7%BF%BB%E8%AD%AF-%E9%9B%99%E8%AA%9E%E5%B0%8D%E7%85%A7%E7%B6%B2%E9%A0%81%E7%BF%BB%E8%AD%AF-pdf%E6%96%87%E6%AA%94%E7%BF%BB%E8%AD%AF/bpoadfkcbjbfhfodiogcnhhhpibjhbnh?utm_source=ext_app_menu)
* 線上/離線 PDF 翻譯

* 有 CC 字幕可翻譯

* [Felo字幕: ChatGPT跨語言翻譯實時字幕](https://chromewebstore.google.com/detail/felo%E5%AD%97%E5%B9%95-chatgpt%E8%B7%A8%E8%AA%9E%E8%A8%80%E7%BF%BB%E8%AD%AF%E5%AF%A6%E6%99%82%E5%AD%97%E5%B9%95/ponokiofkijoolhebggofhhibnafebna?utm_source=ext_app_menu)
* 要收費(NTD319/100mins)
* 翻譯相對準確的
* [通義聽悟-語音轉文字,雙語字幕翻譯](https://chromewebstore.google.com/detail/%E9%80%9A%E4%B9%89%E5%90%AC%E6%82%9F-%E8%AF%AD%E9%9F%B3%E8%BD%AC%E6%96%87%E5%AD%97%EF%BC%8C%E5%8F%8C%E8%AF%AD%E5%AD%97%E5%B9%95%E7%BF%BB%E8%AF%91/omlgpaciclcjgbligehccipcikleeiea?hl=zh-CN)
* 須大陸門號
* 有較多免費時數
* 翻譯不如 Felo 準確
### SSH 進階
> 一般的 SSH 使用帳號、密碼登入,除非密碼很強,不然其實容易被攻破
> [SSH CVEs](https://www.cvedetails.com/vulnerability-list/vendor_id-120/SSH.html) : ssh 漏洞回報
* Nmap
1. 安裝 Nmap : `yum install nmap -y`
2. 掃描網路 : `nmap -sS -P0 -sV 192.168.56.0/24`
掃描結果可看到 **IP** 及 **啟用的協定、埠號**
```=
...
Nmap scan report for mycentos7-2 (192.168.56.101)
Host is up (0.00074s latency)
Not shown: 996 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.4 (protocol 2.0)
111/tcp open rpcbind 2-4 (RPC #100000)
2049/tcp open nfs 3-4 (RPC #100003)
6000/tcp open X11 (access denied)
MAC Address: 08:00:27:83:89:47 (Cadmus Computer Systems)
Service Info: OS: Unix
...
```
* Hydra ([參考資料](https://www.cnblogs.com/ellisonzhang/p/13440614.html))
1. 安裝套件
```
yum -y install gcc libssh-devel openssl-devel
yum install -y unzip zip
```
2. 抓檔案 : `wget https://github.com/vanhauser-thc/thc-hydra/archive/master.zip`
3. 解壓縮 >> 編譯 >> 安裝
```
unzip master.zip
cd thc-hydra-master/
./configure
make &&make install
```
4. 測試 : `hydra -l user -p user 192.168.56.101 ssh`
* 關閉密碼登入(只有無密碼可以登入)
`[root@mycentos7-2 ~]# vim /etc/ssh/sshd_config`

### Linux Driver
> 參考資料 : [動手寫 Linux Driver](https://blog.logan.tw/2013/01/linux-driver.html)
> 寫核心時,如果程式出錯,會導致嚴重錯誤,所以要非常小心
> 不要把所有程式寫入核心,會讓核心運行負載太重,因此將程式模組化,需要時再載入核心,不須重新編譯

```
#include <linux/init.h>
#include <linux/module.h>
MODULE_DESCRIPTION("Hello_world");
MODULE_LICENSE("GPL");
static int hello_init(void) {
printk(KERN_INFO "Hello world !\n");
return 0;
}
static void hello_exit(void) {
printk(KERN_INFO "ByeBye !\n");
}
module_init(hello_init); // 初始化
module_exit(hello_exit); // 離開
```

## 第四週
> Linux 三劍客 : grep、sed、awk
> sed,stream editor,會一行一行的讀指令
### sed
> 參考資料 : [Linux 以 sed 指令搜尋、取代檔案內容教學與範例
](https://officeguide.cc/linux-sed-find-and-replace-text-in-file-tutorial-examples/)、[Linux 指令 SED 用法教學、取代範例、詳解](https://terryl.in/zh/linux-sed-command/)
* `sed -e 's/word1/word2/g' input`
-e : 結果只在螢幕顯示(檔案內容不變)
s : substitute,取代
g : gobal,匹配到的全換
* `sed -e 's/word1/word2/gi' input`
gi : 不分大小寫
* `sed -i 's/word1/word2/gi' input`
-i : 直接改寫檔案內容
* `sed -e 's/word1/word2/2' input`
2 : 匹配到的第2個換(空白預設為1)
* `sed -e '3s/word1/word2/g' input`
3s : 只在第3行執行
* `sed -e '1,3s/word1/word2/g' input`
1,3s : 第1-3行執行
* `sed '1a HelloWorld' input`
第一行之後插入
* `sed -e 's/word1/word2/2' -e 's/bbb/aaa/'input`
兩種條件
* 很多指令也能用 -i

* 
^# : 以#開頭
^$ : 空白行
/^ [] : 除了...
* 
#開頭,word1結尾
* 
.* : 匹配任意字元
\U : uper case,大寫
& : 原本的東西
* 
### SSH 憑證登入
> 全程在 user 下進行
* Server端 (192.168.56.101)
1. 將原本的設定刪除 : `rm -rf .ssh`
2. 將資料夾設定回來 : `mkdir -p .ssh`、`chmod 700 .ssh`

3. Client 的第三步完成後,將憑證放入指定檔案
`cat centos7-1.pub > authorized_keys`、`chmod 600 authorized_keys`
* Client 端 (192.168.56.100)
1. 將原本的設定刪除 : `rm -rf .ssh`
2. 產生憑證 : `ssh-keygen -t rsa -b 4096` (路徑預設 Enter,密碼 123456)
3. 將憑證複製給 Server : `scp id_rsa.pub user@192.168.56.101:/home/user/.ssh/centos7-1.pub`
4. Server 的第三步完成後,連線 (密碼如上)
`ssh -i /home/user/.ssh/id_rsa user@192.168.56.101`

### Putty 憑證登入
1. 找出下圖程式

2. 按下**Generate**,並在空白區域亂動滑鼠產生憑證

3. 產生完複製金鑰後,按下**Sava private key**儲存檔案

4. 在虛擬機 Server 開啟*authorized_keys*,貼上剛剛複製的金鑰

5. 在 Putty 的 Auth 放入儲存的檔案,並連線

* 連線成功

### WebDAV
> [WebDAV](https://zh.wikipedia.org/zh-tw/WebDAV),Web-based Distributed Authoring and Versioning
> 將伺服器變成網路磁碟空間,參考資料 : [2/22 W3](https://hackmd.io/@jenny126/By8OS6Fas/%2FiiMobAxzR3--0JSa_JNwVw)、[Linux將WebDAV為本地磁盤](https://blog.lincloud.pro/archives/36.html)
#### Windows
1. 確認需要的套件都有安裝(下列三個都要有) : `httpd -M | grep dav`

2. 新增網路空間資料夾
`mkdir /var/www/html/webdav`、`cd /var/www/html/webdav`、`touch {a..c}.txt`
3. http設定調整 : `vim /etc/httpd/conf.d/webdav.conf`
```
DavLockDB /var/www/html/DavLock
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html/webdav/
ErrorLog /var/log/httpd/error.log
CustomLog /var/log/httpd/access.log combined
Alias /webdav /var/www/html/webdav
<Directory /var/www/html/webdav>
DAV On
#AuthType Basic
#AuthName "webdav"
#AuthUserFile /etc/httpd/.htpasswd
#Require valid-user
</Directory>
</VirtualHost>
```
4. 將權限給予 apache
`chmod -R 755 /var/www/html`、`chown -R apache:apache /var/www/html`
5. 重啟 httpd : `systemctl restart httpd`
6. 在檔案總管中的**網路**右鍵,**連線至網路磁碟機**,輸入位置並按完成


#### Linux
1. 安裝套件 : `yum install -y davfs2`
2. `mkdir /cloud`、`mount -t davfs http://192.168.56.101 /webdav`

## 第五週
### 通配符 & 正規表達式
> **通配符(wildcard)**用來匹配**檔案名稱**
> **正規表達式(Regular expression)**用來匹配**內容**
#### 通配符
* `ls a?`

匹配一個字元
<hr/>
* `ls a*`

匹配0-n個字元
<hr/>
* `ls a[1-9]`

匹配[]中的任一個字元
<hr/>
* `ls a[[:upper:]][[:upper:]]`

大小寫
<hr/>
* `ls a[^1-9]`

非1-9的
#### 正規表達式
* 
-n : 標註行數
* 
^ : 以...為開頭
* 
$ : 以...為結尾
* 
^r. : r 開頭,匹配任一字元
^r.. : r 開頭,匹配任二字元
^r.* : r 開頭,匹配 0-n 個字元 (* : 重複前面動作)
* 
^ro* : 0-n 個 o 都可以
* 
^r[opt] : ro、rp、rt
* 
egrep : 擴充 grep
"10{1,}" : 10、100、1000、1000...,1 後面至少一個 0
* 
* 
? : 0-1 個
*+* : 至少一個 (1-n)
* 
| : or
### 雲端虛擬機
> [IBM LinuxONE Community Cloud 免費試用申請教程](https://blog.csdn.net/imtech/article/details/126240694)
> 收不到email,改用Azure做
1. 複製**本地虛擬機**的**公開金鑰**

2. 在 Azure 建立虛擬機器,並加入公鑰

3. 等建立完成後在本地虛擬機連線 : `ssh azureuser@172.171.233.160`


> 後來用學校的帳號收到信了 :)
1. 用指令將公鑰複製到虛擬機桌面 : `cp .ssh/id_rsa.pub .`
2. 用 Winscp 把檔案複製到 PC 桌面

3. 在網站上創建虛擬機並加入公鑰


4. 等待創建完成並連線

### grep、sed、awk
> [Linux文字三劍客超詳細教學---grep、sed、awk](https://tw511.com/a/01/11537.html)
> [【CHT智能稅務案】教學筆記 03:awk 指令介紹](https://hackmd.io/@cht-fia-project/ryuiv7O2Y)
#### grep
* 
-B1 : 找到 AA,前面的兩行
* 
-A1 : 找到 AA,之後一行
* 
-A1 -B1 : 顯示前後各一行 (等於 -C1)
* 
多重匹配
#### sed
* 
/aaa : 找aaa
/p : 印出來
* 
-n : 不再印出全部內容
* 
每行第一個 a/b 轉 A/B
* 
寫成腳本帶入
* 
"1itest" : 第一行 insert test
* 
"2cHello" : 第二行 change Hello
#### awk
* 
取1、3、4欄位
$0 : 代表全部欄位
* 
第一欄=user才顯示其1、3欄位
* 
更多條件
* 
NF : number of field ; 有幾欄
NR : number of record ; 第幾行
## 第六週
> 由上往下讀取,*舊的image* 是 ***read only***
> 新增、修改、刪除等...,都會在最上面新增一層,底下相同的內容會被覆蓋
> 一個 image 可有多個 container


### 安裝 Docker
> [第一千零一篇的 cgroups 介紹](https://medium.com/starbugs/%E7%AC%AC%E4%B8%80%E5%8D%83%E9%9B%B6%E4%B8%80%E7%AF%87%E7%9A%84-cgroups-%E4%BB%8B%E7%B4%B9-a1c5005be88c)
> 本身沒有的軟體,沒安裝的伺服器或資料庫卻想使用,可抓容器直接使用
> 並可依本身需求客製化,產生新的鏡像,可快速轉移
> [Install Docker Engine on CentOS](https://docs.docker.com/engine/install/centos/)
1. 測試是否有舊版 Docker : `rpm -qa | grep docker`
* 刪除舊版
```
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
```
2. 安裝 Docker 元件
```
yum install -y yum-utils
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
```
3. 安裝 Docker 引擎
```
yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```
4. 啟動 : `systemctl start docker`
5. 測試 : `docker run hello-world`

### Docker 指令
> [docker hub](https://hub.docker.com/)
> [Docker commit 命令](https://www.runoob.com/docker/docker-commit-command.html)
> 操作 Docker 可用 CONTAINER ID(不需全打) 或 Name(需打全名)
* 列出所有容器資訊 :`docker ps -a`
* 列出所有容器 ID : `docker pa -a -q`
* 用名稱刪除 : `docker rm hello-world`
* 用 ID 刪除鏡像 : `docker rmi d2c...`
* 刪除所有未執行的容器 : `docker rm 'docker ps -a -q'`
* 強制刪除所有容器 : `docker rm -f 'docker ps -a -q'`
* 執行容器 : `docker run -it --name=centos1 centos:centos7 /bin/bash`
* 啟用未執行的容器 : `docker start 1e7...`
* 離開但可能會關掉 : `exit`
* 暫時離開背景執行 : CTRL+P 然後 CTRL+Q
* 終端機進入容器 : `docker exec -it le7... /bin/bash`
* 製造鏡像 : `docker commit 1e7 centos:7.1`
* 查看鏡像 : `docker images`
## 第七週
> 清明連假
## 第八週
### Docker httpd
> 一個Docker做一件事,否則升級擴容會變很大
> [dockerhub / mysql](https://hub.docker.com/_/mysql) (期末IOT cloud db)
> 如果連不上,可先檢查 `ifconfig` 中 docker0 是否有 IP(172...)
> 沒有可重啟取得 IP 在連線
1. 抓 httpd docker : `docker pull httpd`
2. 到根目錄下,創建要連結的資料夾
```
# cd /
# mkdir mydata1
# cd mydata1
# echo "Hello World" > hi.htm
3. 回家目錄下,創建 docker,查看
```
# cd
# docker run -dit --name www1 -p 8080:80 -v /mydata1:/usr/local/apache2/htdocs/ httpd
[root@mycentos7-1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8b27fee8c57d httpd "httpd-foreground" 17 minutes ago Up 17 minutes 0.0.0.0:8080->80/tcp, :::8080->80/tcp www1
```
4. 用 8080 開啟 hi.htm

### Dockerfile 建立 image
> [Docker Dockerfile](https://www.runoob.com/docker/docker-dockerfile.html)
1. 創建資料夾 : `cd ~` `mkdir testdocker`
2. 新增需要的檔案 (Dockerfile 需一模一樣)
```
# echo "Hello 2024" > index.html
# vim Dockerfile
FROM centos:centos7
RUN yum -y install httpd
EXPOSE 80
ADD index.html /var/www/html/
//CMD ["/usr/sbin/apachectl", "-DFOREGROUND"] 加入這行執行指令後面可刪除
```
3. 建立 image
```
# docker built -t centos7:web .
[root@mycentos7-1 testdocker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos7 web bc9b30b83945 6 minutes ago 482MB
```
4. 啟動 docker : `docker run -d -p 8088:80 centos7:web /usr/sbin/apachectl -DFOREGROUND`


### 用腳本自動新增 docker
1. 到根目錄下新增資料
```
# cd /
# mkdir webdata
# cd /webdata
# echo "web data" > index.html
```
2. 新增腳本 : `vim testweb.sh`
```
#!/usr/bin/bash
for i in {1..5}
do
portno=`expr 8000 + $i`
docker run -d -p $portno:80 -v /webdata:/var/www/html centos7:web1
done
```
將腳本設為可執行 : `chmod +x testweb.sh`
3. 執行 : `./testweb.sh`

## 第九週
> [Grafana](https://grafana.com/) : Linux繪圖軟體 (期末可)
### Docker Network
> [Docker Network](https://www.cnblogs.com/liugp/p/16328904.html)
> 用預設的 docker0 橋接網路,docker 間只能用 IP 互聯
> 如果自己創造的橋接網路,docker 間通訊可以用 IP 或 Name互聯
* 列出 : `docker network ls`
* 內容 : `docker network inspect ID`
* 建立橋接網路 : `docker network create -d bridge mybr`

* 指定 **bridge** 網路跑 docker : `docker run --name test1 --network mybr busybox /bin/sh`
* 指定 **none** 網路跑 docker (只有 lo,無其他網路功能) : `docker run --name test1 --network none busybox /bin/sh`
* 指定 **host** 網路跑 docker (與 host 網路配置完全一樣) : `docker run --name test1 --network host busybox /bin/sh`
* 指定 **container** 網路跑 docker (與指定 container 網路配置完全一樣) : `docker run --name test1 --network container busybox /bin/sh`
### Docker Backup-1
1. `docker save hello-world:latest > hello.tar`
2. `scp hello.tar user@192.168.56.101:/tmp`
3. `docker load < hello.tar`

### Docker Backup-2
> docker tag : 給鏡像別名 ; docker commit : 產生新的鏡像
> 前綴與**用戶名稱**一定要一樣
1. 抓鏡像 : `docker pull busybox`
2. 跑 docker : `docker run -it --name test1 busybox /bin/sh`
3. 做成新的鏡像 : `docker commit test1 ccjung/mybusybox:0.1`
4. 登入 Docker Hub (沒有要註冊) : `docker login`
5. 推送本地鏡像到 Docker Hub`docker push ccjung/mybusybox:0.1`

### 負載均衡
> 一般的應該所有伺服器顯示相同內容,此處為示範而不同
1. `vim createweb.sh`
```
for i in {1..5}
do
mkdir -p /web$i
echo $i > /web$i/hi.htm
portno=`expr 8000 + $i`
docker run -d -p $portno:80 -v /web$i:/var/www/html centos7:web /usr/sbin/apachectl -DFOREGROUND
done
```
2. 將腳本設為可執行並執行 : `chmod +x ./createweb.sh`、`./createweb.sh`

3. 測試

### Haproxy-1
> roundrobin : 第一個請求進來,給第一個,以此類推,輪流接受請求
> [How to Install HAProxy on CentOS (7, 8 or 9)](https://webhostinggeeks.com/howto/how-to-install-haproxy-on-centos/)
1. 在test-docker下創建haproxy
```
[root@mycentos7-1 test-docker]# vim create-web.sh
[root@mycentos7-1 test-docker]# mkdir haproxy
[root@mycentos7-1 test-docker]# cd haproxy/
[root@mycentos7-1 haproxy]#
```
2. 編輯設定檔 : `vim haproxy.cfg`
```
defaults
mode http
timeout client 10s
timeout connect 5s
timeout server 10s
timeout http-request 10s
frontend myfrontend
bind 0.0.0.0:8080
default_backend myservers
backend myservers
balance roundrobin
server server1 127.0.0.1:8001
server server2 127.0.0.1:8002
server server3 127.0.0.1:8003
server server4 127.0.0.1:8004
server server5 127.0.0.1:8005
```
3. 安裝 haproxy : `yum -y install haproxy`
4. 覆寫設定檔並啟動 : `cp haproxy.cfg /etc/haproxy/haproxy.cfg`、`systemctl start haproxy`

5. 測試

### Haproxy-2
>[note/110-2自動化運維/2022_03_16/note.md](https://github.com/FUYUHSUAN/note/blob/master/110-2%E8%87%AA%E5%8B%95%E5%8C%96%E9%81%8B%E7%B6%AD/2022_03_16/note.md#haproxy)
> 一定要寫本機 IP
1. 在test-docker下創建haproxy
```
[root@mycentos7-1 test-docker]# vim create-web.sh
[root@mycentos7-1 test-docker]# mkdir haproxy
[root@mycentos7-1 test-docker]# cd haproxy/
[root@mycentos7-1 haproxy]#
```
2. 編輯設定檔 : `vim haproxy.cfg`
```
defaults
mode http
timeout client 10s
timeout connect 5s
timeout server 10s
timeout http-request 10s
frontend myfrontend
bind 0.0.0.0:8080
default_backend myservers
backend myservers
balance roundrobin
server server1 192.168.56.100:8001
server server2 192.168.56.100:8002
server server3 192.168.56.100:8003
server server4 192.168.56.100:8004
server server5 192.168.56.100:8005
```
3. 關 haproxy : `systemctl stop haproxy`
4. 跑 docker : `docker run -d -p 8080:8080 --name my-running-haproxy -v /root/test-docker/haproxy:/usr/local/etc/haproxy:ro haproxy`
5. 檢視、測試

## 第十週
### Network Namespace
> 參考 [理解Docker容器网络之Linux Network Namespace](https://tonybai.com/2017/01/11/understanding-linux-network-namespace-for-docker-network/)
> 新系統會捨棄 `ifconfig`,可改用 `ip addr show` 查看
1. 建兩個 network namespace : `ip netns add container_ns1`、`ip netns add container_ns2`

2. 查看配置 : `ip netns exec container_ns1 ip addr show`
一開始只有 lo (loopback)

3. 增加一個橋接器 : `brctl addbr mydocker0`
給予IP : `ip addr add 172.16.1.254/16 dev mydocker0`
查看 : `ip addr show mydocker0`

啟動 : `ip link set dev mydocker0 up`
4. 創建 VETH (Virtual Ethernet)
創建一對相連的網路卡,一張連 mydocker0,一張連 container_ns1
`ip link add veth1 type veth peer name veth1p`
`ip link add veth2 type veth peer name veth2p`
5. 將一端連結到 mydocker0
`brctl addif mydocker0 veth1`
`brctl addif mydocker0 veth2`

6. 將另一端連結到 container
`ip link set veth1p netns container_ns1`
`ip link set veth2p netns container_ns2`

7. 將網卡改名
`ip netns exec container_ns1 ip link set veth1p name eth0`
`ip netns exec container_ns2 ip link set veth2p name eth0`

8. 設定 IP 位置
`ip netns exec container_ns1 ip addr add 172.16.1.1/16 dev eth0`
`ip netns exec container_ns2 ip addr add 172.16.1.2/16 dev eth0`
`ip netns exec container_ns1 ip link set eth0 up`
`ip netns exec container_ns2 ip link set eth0 up`

`ip netns exec container_ns1 ip route`
`ip netns exec container_ns2 ip route`

### Docker volume
> 可以做到數據持久化,關掉 docker 仍保有數據
> 參考 [Docker 實戰系列(三):使用 Volume 保存容器內的數據](https://larrylu.blog/using-volumn-to-persist-data-in-container-a3640cc92ce4)
> named volume : 先在 Linux host 'create' volume 再用
> hosat volume : 直接對應現有的資料夾
* 創建 (named volume): `docker volume create --name mydata`
* 列出 : `docker volume ls`
* 執行對應 (所有docker都共享):
`docker run -it --name test1 -v mydata:/mydata busybox sh`
`docker run -it --name test2 -v mydata:/mydata busybox sh`
* 刪除 (docker未使用才能刪): `docker volume rm mydata`
### Docker MySQL using mybr
> 多台機器一起提供網頁服務
> DB 的埠號 3306,如果安裝過 mariadb,可先 stop,不然可能占用埠號
1. 安裝 MySQL 的 docker
```
docker run -itd --name mydb -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 --network mybr mysql:5.7.24`
```
3. 執行 docker : `docker exec -it mydb bash`
4. 對 mysql 進行操作 (指令參考 [Linux 伺服器架設/LAMP server](https://hackmd.io/@Jung217/Hk3hF_aR2#LAMP-server) )
```
mysql -uroot -p123456 #進入mysql
create database testdb; #創建資料庫
use testdb; #進入使用資料庫
create table addrbook(name varchar(50) not null, phone char(10)); #創建資料表
insert into addrbook(name, phone) values ("tom", "0912123456"); #加入資料
insert into addrbook(name, phone) values ("mary", "0912123567"); #加入資料
select name,phone from addrbook; #選擇資料
```

5. 到另一台機器上,於`root/testdocker/php-code`,新增 test.php
```
<?php
$servername="mydb";
$username="root";
$password="123456";
$dbname="testdb";
$conn = new mysqli($servername, $username, $password, $dbname);
if($conn->connect_error){
die("connection failed: " . $conn->connect_error);
}
else{
echo "connect OK!" . "<br>";
}
$sql="select name,phone from addrbook";
$result=$conn->query($sql);
if($result->num_rows>0){
while($row=$result->fetch_assoc()){
echo "name: " . $row["name"] . "\tphone: " . $row["phone"] . "<br>";
}
} else {
echo "0 record";
}
?>
```
6. 啟動 docker
```
docker run -d -p 8080:80 --name my-apache-php-app --network mybr -v "/root/testdocker/php-code":/var/www/html php:7.2-apache
```
7. 測試 : `交127.0.0.1:8080/test.php`
會出現這個錯誤 (mysqli : php 及 mysql版本無法對應)

改成這這樣可排錯 (radys/php-apache:7.4)
```
docker run -d -p 8080:80 --name my-apache-php-app --network mybr -v "/root/testdocker/php-code":/var/www/html radys/php-apache:7.4
```

> NOT COMPLETE
## 第十一週
### CI/CD
> CI/CD (Continuous Delivery/Continuous Deployment) 持續集成/持續部署
> 參考 [Linux_note/109-1 Docker/W7-20201027.md](https://github.com/linjiachi/Linux_note/blob/master/109-1%20Docker/W7-20201027.md#gitlab---cicd-continuous-deliverycontinuous-deployment)
> 補充 [Use CI/CD to build your application](https://docs.gitlab.com/ee/topics/build_your_application.html)
#### 安裝&訓練模型
1. 安裝 python2 及 sklearn
```
yum install python-pip
python -m pip install pip==20.3.4
pip install sklearn==
```
2. 建立資料夾,寫入程式碼
```
mkdir iris
cd iris
vim train_model.py
```
```py
# coding: utf-8
import pickle
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn import tree
# simple demo for traing and saving model
iris=datasets.load_iris()
x=iris.data
y=iris.target
#labels for iris dataset
labels ={
0: "setosa",
1: "versicolor",
2: "virginica"
}
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=.25)
classifier=tree.DecisionTreeClassifier()
classifier.fit(x_train,y_train)
predictions=classifier.predict(x_test)
#export the model
model_name = 'model.pkl'
print("finished training and dump the model as {0}".format(model_name))
pickle.dump(classifier, open(model_name,'wb'))
```
3. 執行程式 : `python train_model.py`

#### Flask 執行
> Flask 埠號 5000
1. 安裝 flask : `pip install flask`
2. 新增 server 程式
`vim server.py`
```py
# coding: utf-8
import pickle
from flask import Flask, request, jsonify
app = Flask(__name__)
# Load the model
model = pickle.load(open('model.pkl', 'rb'))
labels = {
0: "versicolor",
1: "setosa",
2: "virginica"
}
@app.route('/api', methods=['POST'])
def predict():
# Get the data from the POST request.
data = request.get_json(force = True)
predict = model.predict(data['feature'])
return jsonify(predict[0].tolist())
if __name__ == '__main__':
app.run(debug = True, host = '0.0.0.0')
```
3. 新增 client 程式
`vim client.py`
```py
# coding: utf-8
import requests
# Change the value of experience that you want to test
url = 'http://127.0.0.1:5000/api'
feature = [[5.8, 2.0, 4.2, 3.2]]
labels ={
0: "setosa",
1: "versicolor",
2: "virginica"
}
r = requests.post(url,json={'feature': feature})
print(labels[r.json()])
```
7. 開兩台終端機個別執行程式

#### 本地 Docker 執行
1. 下載 docker : `docker pull nitincypher/docker-ubuntu-python-pip`
2. 在同資料夾下新增 dockerfile
`vim Dockerfile`
```she
FROM nitincypher/docker-ubuntu-python-pip
COPY ./requirements.txt /app/requirements.txt
WORKDIR /app
RUN pip install -r requirements.txt
COPY server.py /app
COPY train_model.py /app
CMD python /app/train_model.py && python /app/server.py
```
3. 建立 requirements
`vim requirements.txt`
```
sklearn
flask
```
4. 建立鏡像 : `docker build -t iris:1.0 .`
5. 執行 : `docker run -itd --name iris -p 5000:5000 iris:1.0`

#### 雲端 CI/CD
> 需先註冊 [GitLab](https://about.gitlab.com/) 帳號
1. 在兩台虛擬機產生 ssh key : `ssh-keygen`
```
[root@mycentos7-2 ~]# ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
/root/.ssh/id_rsa already exists.
Overwrite (y/n)? y
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:Ibpm6MQytgMquelnDAwq4B80EwrY8NkmKGLPLN5dTmc root@mycentos7-2
The key's randomart image is:
+---[RSA 2048]----+
|oo |
|oo.+ |
|=.= + . . |
|*.+* . . . |
|*..++ oSE |
|==oo..+ o |
|*+B.=. . |
|=B.B |
|=+= |
+----[SHA256]-----+
[root@mycentos7-2 ~]# cat /root/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDiottYOHkqUxAlewgWosYgazBNLvWD7Uzp...
```
2. 複製所產生的公鑰,放到gitlab


3. 新增空白專案,輸入名字即可


4. 設定虛擬機的git (**兩台都要裝 git**)
```
yum install git
git config --global user.name "Jung217"
git config --global user.email alex24922665@gamil.com
```
5. 將專案透過 git push 回去
```
[root@mycentos7-1 iris]# git init
Initialized empty Git repository in /root/testdocker/iris/.git/
[root@mycentos7-1 iris]# git remote add origin https://gitlab.com/jung6135418/iris2024.git
[root@mycentos7-1 iris]# git add .
[root@mycentos7-1 iris]# git commit -m "Initial Commit"
[master (root-commit) 9777abf] Initial Commit
6 files changed, 323 insertions(+)
create mode 100644 Dockerfile
create mode 100644 client.py
create mode 100644 model.pkl
create mode 100644 requirements.txt
create mode 100644 server.py
create mode 100644 train_model.py
[root@mycentos7-1 iris]# git push -uf origin master
Username for 'https://gitlab.com': Jung217
Password for 'https://Jung217@gitlab.com':
Counting objects: 8, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (7/7), done.
...
remote:
To https://gitlab.com/jung6135418/iris2024.git
* [new branch] master -> master
Branch master set up to track remote branch master from origin.
```
#### 連結 gitlab-runner
> 讓第二台機器扮演類似代理人的角色,只要 VM1 上傳檔案,VM2 會自動更新
1. 在第二台機器建立 gitlab-runner
```
curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
chmod +x /usr/local/bin/gitlab-runner
useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
usermod -aG docker gitlab-runner
```
成功可進入

2. 從 **專案 / Settings / CI/CD / Runners** 複製 token

3. 註冊 gitlab-runner
```
[root@mycentos7-2 ~]# gitlab-runner register
Runtime platform arch=amd64 os=linux pid=6471 revision=91a27b2a version=16.11.0
Running in system-mode.
Enter the GitLab instance URL (for example, https://gitlab.com/):
https://gitlab.com/ #要連結的網站
Enter the registration token:
GR1348941W9T39147f1XqLJQ7BzxH #從 gitlab 取得的 token
Enter a description for the runner:
[mycentos7-2]: mycentos7-2
Enter tags for the runner (comma-separated):
mycentos7-2
Enter optional maintenance note for the runner:
WARNING: Support for registration tokens and runner parameters in the 'register' command has been deprecated in GitLab Runner 15.6 and will be replaced with support for authentication tokens. For more information, see https://docs.gitlab.com/ee/ci/runners/new_creation_workflow
Registering runner... succeeded runner=GR1348941W9T39147
Enter an executor: parallels, docker, docker-autoscaler, instance, custom, shell, ssh, virtualbox, docker-windows, docker+machine, kubernetes:
shell
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
Configuration (with the authentication token) was saved in "/etc/gitlab-runner/config.toml"
```

4. 在第一台機器的 /iris 新增 .gitlab-ci.yml
> 在有更新時,另一台自動更新
```she
stages:
- deploy
docker-deploy:
stage: deploy
script:
- docker build -t iris .
- if [ $(docker ps -aq --filter name=iris) ]; then docker rm -f iris; fi
- docker run -d -p 5000:5000 --name iris iris
tags:
- mycentos7-2
```
5. 把專案推回去
```
git add .
git commit -m "Second Commit"
git push -uf origin master
```
6. 到 **專案 / Build / Pipelines**

7. Passed 之後,將第一台機器的 client.py 的 ip 改成 第二台的ip 執行

> 成功

## 第十二週
### nohup
> 讓程式於背景執行,終端機關閉也不會終止
1. 寫腳本
`vim test.sh`
```she
/!usr/bin/bash
for i in {1..200}
do
echo $i
sleep 1
done
```
2. 加執行 : `chomd +x test.sh`
3. 執行 : `nohup ./test.sh >log 2>&1 &`
4. 查看 : `ps -ef | grep test`

5. 看 log (-f : follow,有新的就印) : `tail -f log`

### Docker-Compose
> 用於一台機器上,管理多個 docker 間的合作
> 1 v.s. 1
> 1 v.s. n
> n v.s. n
* 安裝 docker-compose
```
curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose && docker-compose --version
```
> 參考 [Docker Compose 的基本使用方式](https://magiclen.org/docker-compose/)
#### test1
> 在 **test-docker/test-docker-compose/test1** 下進行
1. 新增設定檔
`vim docker-compose.yml`
```she
services:
app:
image: hello-world
```
2. 啟動 : `docker-compose up`
3. 查看 : `docker-compose ps -a`

4. 關掉 : `docker-compose down`
#### test2
> 在 **test-docker/test-docker-compose/test2** 下進行
1. 新增設定檔
`vim Dockerfile`
```she
FROM alpine
RUN apk add --no-cache bash
CMD bash -c 'for((i=1;;i+=1)); do sleep 1 && echo "Counter: $i"; done'
```
2. 新增設定檔
`vim docker-compose.yml`
```she
services:
app:
build:
context: .
```
3. 建立映像 : `docker-compose build`
4. 查看 : `docker images`

5. 啟動(如伺服器在背景執行) : `docker-compose up -d`
6. 查看日誌 : `docker-compose log`

7. 關掉 : `docker-compose down`
#### 改變映像預設執行的指令
> 在 **test-docker/test-docker-compose/test3** 下進行
1. 新增設定檔
`vim docker-compose.yml`
```she
services:
app:
build:
context: .
image: counter
command: >
bash -c 'for((i=1;;i+=2)); do sleep 1 && echo "Counter: $$i"; done'
```
2. 複製 Dockerfile : `cp ../test2/Dockerfile .`
3. 啟動(省略 build,程式會自己先 build 再啟動) : `docker-compose up -d`
4. 查看日誌(-f : follow) : `docker-compose log -f`

5. 關掉 : `docker-compose down`
#### 暴露連接埠
> 在 **test-docker/test-docker-compose/test5** 下進行
> 水平擴容 : 相同的機器新增很多台
1. 新增 .html : `echo HelloWorld > index.html`
2. 新增設定檔
`vim Dockerfile`
```she
FROM centos:centos7
RUN yum install -y httpd
EXPOSE 80
ADD index.html /var/www/html
CMD ["/usr/sbin/apachectl","-DFOREGROUND"]
```
3. 新增設定檔
`vim docker-compose.yml`
```she
services:
app:
build:
context: .
ports:
- "3000-3063:80"
```
4. 啟動 : `docker-compose up -d --scale app=5`

5. 查看 : `docker-compose ps`

6. 測試 : `curl 127.0.0.1:30XX`

7. 關掉 : `docker-compose down`
#### 掛載外部檔案系統的目錄
> 在 **test-docker/test-docker-compose/test5** 下進行
1. 更改設定檔
`vim docker-compose.yml`
```she
services:
app:
build:
context: .
volumes:
- /mydata:/docker-mydata
```
2. 啟動 : `docker-compose up -d`
3. 進入 docker : `docker-compose exec -it app bash`
4. 看掛載的資料夾

5. 關掉 : `docker-compose down`
#### flask & redis
> 在 **test-docker/test-docker-compose/test6** 下進行
1. 新增 requirements
`vim requirements.txt`
```she
flask
redis
```
2. 新增 Dockerfile
`vim Dockerfile`
```she
FROM python
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
```
3. 新增設定檔
`vim docker-compose.yml`
```she
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
depends_on:
- redis
redis:
image: "redis:alpine"
```
4. 新增 app
`vim app.py`
```py
import time
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def get_index():
count = get_hit_count()
return 'Yo! You have browsed {} times\n'.format(count)
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
```
5. 啟動 : `docker-compose up -d`
6. 查看 : `docker-compose ps`

7. 測試 : `curl 127.0.0.1:5000`

> 補充[利用 Dockfile、Docker Compose 建立 LAMP 環境 (PHP、Apache、MySQL)](https://hackmd.io/@titangene/docker-lamp)
## 第十三週
> [Jump server](https://search.bilibili.com/all?keyword=jumpserver&from_source=webtop_search&spm_id_from=333.1007&search_source=5),管理帳號密碼、權限,稽核使用者動作
### 1Panel
> [1panel.cn](https://1panel.cn/)
1. 安裝
```
curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sh quick_start.sh
```
2. 記住

```
[root@mycentos7-1 ~]# 1pctl user-info
面板地址: http://$LOCAL_IP:28186/9406c6ff7e
面板用户: 839c6ec405
面板密码: 5206264181
提示:修改密码可执行命令:1pctl update password
```
3. 使用上面帳密連線 : http://192.168.56.109:28186/9406c6ff7e

### Docker Swarm
> 參考 [2022/11/08 week10](https://hackmd.io/@1U480GKVQO2iXM-lvHmvBQ/B1WlNHk9i)
> 遷移服務,讓機器出錯時也能服務
1. 讓第一台機器成為 manager : `docker swarm init --advertise-addr 192.168.56.109`

2. 第二、三台使用反白處加入 swarm as worker


3. 在 manager 輸入指令,使用視覺化頁面(非必要)
```
docker service create --name=viz --publish=8080:8080/tcp --constraint=node.role==manager --mount=type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock dockersamples/visualizer
```

* SHUTDOWN 一台

#### 指令
> 多在 manager 下操作
* 列出 swarm 裡的節點 : `docker node ls`
* 創建一個服務叫 myweb 使用 httpd 鏡像 : `docker service create --name myweb httpd`
* 查看所有服務 : `docker service ls`
* 擴縮容,增減副本數 : `docker service scale myweb=3`
* 讓節點放掉所有工作 : `docker node update --availability drain centos7-1c1`
* 讓節點能接受新工作 : `docker node update --availability active centos7-1c1`
* 移除服務 : `docker service rm myweb`
* 讓服務跟外部連接 : `docker service update --publish-add 8081:80 myweb`

#### Rolling Updates
> 參考 [docker.doc / Apply rolling updates to a service](https://docs.docker.com/engine/swarm/swarm-tutorial/rolling-update/)
1. 抓鏡像 : `docker pull redis:7.0`
2. 創建服務 : `docker service create --replicas 3 --name redis redis:7.0`


3. 更新服務的鏡像版本 : `docker service update --image redis:7.2 redis`

## 第十四週
### 串接 ChatGPT
> LLM (Large Language Model) : 大型語言模型
> 使用嵌入的資料(客製、隱私的資料),達成 AI 在地化
> 專案 : [github/n4ze3m/dialoqbase](https://github.com/n4ze3m/dialoqbase)
> 補充 [github/eosphoros-ai/DB-GPT](https://github.com/eosphoros-ai/DB-GPT),專注於資料庫,用自然語言查詢資料庫
1. 登入網站 : [OpenAI Platform](https://platform.openai.com/playground)
2. Create API keys

* 如果API不能用,要新增付款資訊(先儲值5美元,自動儲值關閉)

3. 在虛擬機下載專案 : `git clone https://github.com/n4ze3m/dialoqbase.git`
4. 進入資料夾 : `cd dialoqbase/docker`
5. 編輯環境變數新增 API key : `vim .env`

6. 啟動 : `docker-compose up -d`
7. 查看 : `docker-compose ps`

8. 用 IP 網址登入 (admin/admin) : `192.168.56.101:3000`

9. 新增聊天機器人&資料

10. 問問題

11. 在 Telegram 的 BotFather 新增一個聊天機器人

12. 將得到的 token 貼在下方

13. 問機器人問題

### Docker Swarm with DB
> 參考 : [建立 NFS Server](https://hackmd.io/@Jung217/Hk3hF_aR2#%E5%BB%BA%E7%AB%8B-NFS-Server),**所有機器都先安裝 NFS**
#### 第一台 (Manager)
1. `cd /`
2. `mkdir /mydb`、`mkdir /myphp`
3. `vim /etc/exports`
```ps
/mydb 192.168.56.0/24(rw,sync,no_root_squash,no_all_squash)
/myphp 192.168.56.0/24(rw,sync,no_root_squash,no_all_squash)
```

#### 第二台 (Worker1) & 第三台 (Worker2)
1. `cd /`
2. `mkdir /mydb`、`mkdir /myphp`
3. `mount -t nfs 192.168.56.109:/mydb /mydb`、`mount -t nfs 192.168.56.109:/myphp /myphp`
#### Manager
`docker network create -d overlay mynet`


#### mydb 運行的機器
> 參考 [LAMP server : SQL commands & test.php](https://hackmd.io/@Jung217/Hk3hF_aR2#LAMP-server)
1. 查看網路 : `docker network ls`

2. 執行 docker : `docker exec -it bb1 bash`

4. 進入資料庫(passwd:123456) : `mysql -uroot -p`,創建 DB
```
create database testdb; #創建資料庫
use testdb; #進入使用資料庫
create table addrbook(name varchar(50) not null, phone char(10)); #創建資料表
insert into addrbook(name, phone) values ("tom", "0912123456"); #加入資料
insert into addrbook(name, phone) values ("mary", "0912123567"); #加入資料
show databases; #顯示目前有的資料庫
```
5. 建立網頁 : `cd /myphp`、`vim test.php`
```ps
<?php
$servername="mydb";
$username="root";
$password="123456";
$dbname="testdb";
$conn = new mysqli($servername, $username, $password, $dbname);
if($conn->connect_error){
die("connection failed: " . $conn->connect_error);
}
else{
echo "connect OK!" . "<br>";
}
$sql="select name,phone from addrbook";
$result=$conn->query($sql);
if($result->num_rows>0){
while($row=$result->fetch_assoc()){
echo "name: " . $row["name"] . "\tphone: " . $row["phone"] . "<br>";
}
} else {
echo "0 record";
}
?>
```
5. 查看網頁 : `curl 192.168.56.109:8888/test.php`


## 第十五週
> OpenAI : ChatGPT
> Google : Gemini
> Meta : Llama 3
> AWS : Anthropic (Claude)
> [HuggingFace](https://huggingface.co/) : 人工智慧的開源社群
> [THUDM/chatglm3-6b](https://huggingface.co/THUDM/chatglm3-6b)
> [Git LFS (Large File Storage)](https://haway.30cm.gg/git-lfs/)
### Ansible
> 底層透過 SSH 操作其他機器
* 簡單指令 : `ansible server1 -m command -a ifconfig`
* 複雜指令 : `ansible server1 -m shell -a "ifconfig > ifg.txt"`
1. 在第一台虛擬機 (主控) 產生 SSH 金鑰 : `ssh-keygen`
2. 讓另外兩台虛擬機 (被控端) 可以無密碼登入 : `ssh-copy-id root@192.168.56.110`、`ssh-copy-id root@192.168.56.111`

3. 修改檔案 : `vim /etc/hosts`
```ps
192.168.56.110 centos7-1c2 centos7-1c2.test.com
192.168.56.111 centos7-1c3 centos7-1c3.test.com
```
4. 設定名稱登入
```
scp /etc/hosts root@centos7-1c2:/etc/hosts
scp /etc/hosts root@centos7-1c3:/etc/hosts
```
6. 在第一台安裝 Ansible (被控端不用) : `yum install ansible`
7. 編輯檔案 : `vim /etc/ansible/hosts`
```ps
[server1]
192.168.56.110 # centos7-1c2
[server2]
192.168.56.111 # centos7-1c3
[allservers]
centos7-1c2
centos7-1c3
```
7. ping
```
ansible server1 -m ping
ansible server -m ping
ansible allservers -m ping
```

### Playbook
> @/root/test-ansible/test1
1. `vim test1.yml`
```ps
---
- hosts: server1
tasks:
- name: test ping
ping:
```
2. `ansible-playbook test1.yml`

> @/root/test-ansible/test2
1. `vim test2.yml`
```ps
---
- hosts: server1
gather_facts: no
tasks:
- name: create an empty file under /tmp
command:
chdir: /tmp
cmd: touch a.txt
- name: list all files under /tmp and grep a
shell:
chdir: /tmp
cmd: "ls -l | grep a.txt"
register: results
- name: show the results
debug:
msg: "{{ results['stdout'] }}"
```
2. `ansible-playbook test2.yml`

## 第十六週
### Ansible with Ubuntu
> [安裝 ubuntu](https://hackmd.io/@Jung217/ryCFXSSap#%E9%99%84-%E5%AE%89%E8%A3%9D-Ubuntu)
#### Ubuntu
1. 安裝 SSH : `apt install openssh-server`
2. 更改設定讓 root 無密碼登入 : `vim /etc/ssh/sshd_config`
```
PermitRootLogin yes # 加在檔案最後就可以
```
3. 重啟 : `systemctl restart ssh`
#### Ansible Master
1. 讓 ubuntu 可以無密碼登入 : `ssh-copy-id root@192.168.56.112`
2. 修改檔案 : `vim /etc/hosts`
```ps
...
192.168.56.112 ubuntu22 ubuntu22.test.com
```
3. 設定名稱登入 : `scp /etc/hosts root@cubuntu22:/etc/hosts`
4. 編輯檔案 : `vim /etc/ansible/hosts`
```ps
[server1]
192.168.56.110 # centos7-1c2
[server2]
192.168.56.111 # centos7-1c3
[server3]
192.168.56.112 # ubuntu22
[allservers]
centos7-1c2
centos7-1c3
ubuntu22
```
5. ping : `ansible allservers -m ping`

### Hostname
1. 編輯 Shell Script 檔 : `vim a.sh`
```ps
hostname
```
* 加上執行權限 : `chmod +x a.sh`
2. `ansible all -m script -a "./a.sh"`

### Ansible 進階
#### Dynamic Parameter
> @root/test-ansible/test3
1. 新增 hi.j2 : `vim hi.j2`
```
Hello "{{ dynamic_world }}"
```
2. `vim test3.yml`
```ps
---
- hosts: server1
gather_facts: no
vars:
dynamic_world: "World"
tasks:
- name: test template
template:
src: hi.j2
dest: /tmp/hello_world.txt
```
2. `ansible-playbook test3.yml`、`ansible server1 -m command -a "cat /tmp/hello_world.txt"`

#### Install Software
> @root/test-ansible/test4
1. `vim test4.yml`
```ps
---
- hosts: www
gather_facts: yes
tasks:
- name: test ping
ping:
- name: install software packages in Centos
yum:
name:
- vim
- curl
state: present
when: ansible_facts['distribution'] == "CentOS"
```
2. `ansible-playbook test4.yml`

#### Httpd
> centos >>> httpd
> ubuntu >>> apache2
> 跳錯誤可能是 port 占用,可以留意一下
> @root/test-ansible/test5
1. 新增網頁 : `echo "Hi there" > hi.htm`
2. 新增設定檔 : `cp /etc/httpd/conf/httpd.conf httpd.conf.j2`
```ps
Listen {{ portnum }}
```
4. `vim test5.yml`
```ps
---
- hosts: server1
gather_facts: no
vars:
portnum: 9999
tasks:
- name: install httpd
yum:
name:
- httpd
state: present
- name: copy configuration file
template:
src: httpd.conf.j2
dest: /etc/httpd/conf/httpd.conf
- name: copy hi.htm
copy:
src: hi.htm
dest: /var/www/html/hi.htm
- name: restart httpd service
service:
name: httpd
state: restarted
```
2. `ansible-playbook test5.yml`

## 第十七週
* 範例一 : 運用 **notify** 跟 **handlers**,配置檔改變時,重啟服務
* http.conf
```
Listen 8082
```
* playbook.yml
```
- hosts: server1
tasks:
- name: install httpd server
yum: name=httpd state=present
- name: configure httpd server
copy: src=./httpd.conf dest=/etc/httpd/conf/httpd.conf
notify: restart httpd server
- name: start httpd server
service: name=httpd state=started enabled=yes
handlers:
- name: restart httpd server
service: name=httpd state=restarted
```

* 範例二 : 變數安裝 app (var in playbook)
```
- hosts: server1
vars:
- app1: httpd
- app2: vsftpd
tasks:
- name: install {{ app1 }} and {{ app2 }}
yum:
name:
- "{{ app1 }}"
- "{{ app2 }}"
state: present
```

* 範例三 : 變數安裝 app (從其他檔案引用 var )
* vars_public.yml
```
app1: wget
app2: gedit
```
* playbook.yml
```
- hosts: server1
vars_files: ./vars_public.yml
tasks:
- name: install {{ app1 }} and {{ app2 }}
yum:
name:
- "{{ app1 }}"
- "{{ app2 }}"
state: present
```

* 範例四 : 群組變數
* group_vars
* server1
```
app1: httpd
app2: vsftpd
```
* host_vars
* playbook.yml
```
- hosts: server1
tasks:
- name: install {{ app1 }} and {{ app2 }}
yum:
name:
- "{{ app1 }}"
- "{{ app2 }}"
state: present
```

* 範例五 : 主機變數
* group_vars
* host_vars
* 192.168.56.110
```
app1: httpd
app2: vsftpd
```
* 192.168.56.111
```
app1: wget
app2: curl
```
* playbook.yml
```
- hosts: 192.168.56.110
tasks:
- name: install {{ app1 }} and {{ app2 }}
yum:
name:
- "{{ app1 }}"
- "{{ app2 }}"
state: present
- hosts: 192.168.56.111
tasks:
- name: install {{ app1 }} and {{ app2 }}
yum:
name:
- "{{ app1 }}"
- "{{ app2 }}"
state: present
```

* 範例六
`shell: ps aux|grep httpd` : 查看行程是否跑起來
`register: check_httpd` : 將輸出設成變數,並顯示結果
* http.conf
```
Listen 8082
```
* playbook.yml
```
- hosts: server1
tasks:
- name: install httpd server
yum: name=httpd state=present
- name: configure httpd server
copy: src=./httpd.conf dest=/etc/httpd/conf/httpd.conf
notify: restart httpd server
- name: start httpd server
service: name=httpd state=started enabled=yes
- name: Check httpd server
shell: ps aux|grep httpd
register: check_httpd
- name: output variable
debug:
msg: "{{ check_httpd.stdout_lines }}"
handlers:
- name: restart httpd server
service: name=httpd state=restarted
```

* 範例七 : jinja2版,使用 **template** 寫入配置檔
* http.conf.j2
```
ServerAdmin root@{{ ansible_fqdn }}
```
* playbook.yml
```
- hosts: server1
tasks:
- name: install httpd server
yum: name=httpd state=present
- name: configure httpd server
template: src=./httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
notify: restart httpd server
- name: start httpd server
service: name=httpd state=started enabled=yes
handlers:
- name: restart httpd server
service: name=httpd state=restarted
```

* 範例八 : 建立 memory cache,根據機器本身狀況配置空間大小
* memcached.j2
```
PORT="11211"
USER="memcached"
MAXCONN="1024"
CACHESIZE="{{ ansible_memtotal_mb //2 }}"
OPTIONS=""
```
* playbook.yml
```
- hosts: server1
tasks:
- name: install memcached server
yum: name=memcached state=present
- name: configure memcached server
template: src=./memcached.j2 dest=/etc/sysconfig/memcached
- name: service memcached server
service: name=memcached state=started enabled=yes
- name: check memcached server
shell: ps aux | grep memcached
register: check_mem
- name: debug memcached variables
debug:
msg: "{{ check_mem.stdout_lines }}"
```

## 第十八週
> 報告
## 期末報告
> 參考 [蘇彥庭-Redis資料庫安裝筆記](https://hackmd.io/@suyenting/SyrcLvYyd#%E4%BA%8C%E3%80%81%E5%AE%89%E8%A3%9D%E8%88%87%E9%80%A3%E7%B7%9ARedis%E8%B3%87%E6%96%99%E5%BA%AB)、[Python redis 使用介绍](https://www.runoob.com/w3cnote/python-redis-intro.html)、[Redis常用命令(超详细整理)](https://blog.csdn.net/Lzy410992/article/details/116094703)
### 介紹
Redis(Remote Dictionary Server)是一個高性能、NoSQL的記憶體鍵值儲存系統,使用 ANSI C 編寫的,適用於大多數POSIX系統,如Linux、MacOS等,主要用於作為資料庫、緩存和消息代理。若有短時間內大量存取的需求,卻又不想如此頻繁的對硬碟讀寫,這種場合可以使用 Redis 資料庫暫存資料,等一段時間後再一齊寫入硬碟裡。
### 特點
> Redis是單線程提供的鍵值存取的主要服務
但其他功能,如:持久化、異步刪除等需要額外線程完成。
* 優點
* 數據在記憶體裡,運算速度與記憶體相當
* 單線程避免了線程切換的時間損失
* 缺點
* 使用耗時較長的命令時容易造成阻塞
* 無法處理複雜關係的查詢
### 資料結構
* String 是 Redis 中最基礎的資料型別,透過 binary 形式儲存,且保證不更改資料內容 (binary-safe),因此 String 可儲存任何資料,例如一般字串、數字、檔案等。
* List 是由多個 String 所組成,List 中成員會依照插入的順序儲存,其提供由頭尾插入、拿出的功能,List 能實作例如任務隊列、優先權隊列等。
* Hash 非常適合儲存物件型資料,例如'使用者'有姓名、年齡、信箱等。當物件很小時,Hash 會將資料壓縮後儲存,因此單台 Redis 可以儲存數百萬個小物件。
* Set 是多個 String 以無序的方式所組成,保證內部不會有重複的元素,此外也提供了多個 Set 之間交集、差集、聯集的操作。
* Sorted Set 是有序的 Set,其順序會依照給定的權重排序,在查找資料時,可使用 二元搜尋,由於搜尋效率高,Sorted Set 可當作一組 Hash 資料的索引,單筆物件的完整資料儲存在 Hash。
### 一般安裝
1. 在虛擬機安裝 gcc
```
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
```
2. 在當前終端啟用 Devtoolset-9 開發工具集 : `scl enable devtoolset-9 bash`
3. 確認 gcc 已安裝 : `gcc --version`

4. 下載 Redis 壓縮檔 : `wget https://download.redis.io/releases/redis-6.0.10.tar.gz`
5. 解壓縮 : `tar xzf redis-6.0.10.tar.gz`
6. 進入 Redis 安裝資料夾 & 編譯 : `cd redis-6.0.10`、`make`
**看到下圖文字就代表成功了**

7. 執行 Redis server : `src/redis-server`

8. 開啟另一個終端機執行 client : `cd redis-6.0.10` 、 `src/redis-cli`

9. 啟動 server :`src/redis-server --protected-mode no`
10. Host 連線
### Docker 安裝
1. 創建 & 進入資料夾 : `mkdir redis`、`cd redis`
2. 抓 Redis 映像檔 : `docker pull redis`
3. 啟動 Docker
```
docker run --name redis-lab -p 6379:6379 -d -v $(pwd)/data:/data --restart unless-stopped redis
```
4. 查看 : `docker ps`

5. Host 連線
### Host 連線
* 在 vs code 寫程式並執行
```py
import redis
r = redis.StrictRedis(host='192.168.56.107', port=6379, db=0)
print(r.get('s01'))
```

### 指令
* 列出伺服器資訊 : `info`

* 檢查是否與伺服器連接 : `ping`

* 關閉連線 & 退出 client : `shutdown`、`quit`

* 選擇 DB (預設有16個 DB(0~15),預設使用的第0個) : `select n`

* 查看目前使用 DB 的 key 的數量 : `dbsize`

* 刪除**目前** DB 所有資料 : `flushdb`

* 刪除**所有** DB 所有資料 : `flushall`

* 將**現DB**的**指定key**移到**別的DB** : `move key n`

* 列出所有 keys : `keys *`
* 檢查 key 是否存在 : `exists key`
* 設定 key 的值`set key value`
* key data 的 type : `type key`
* 刪除 key : `del key`
* 設定 key 的有效期限 : `expire key seconds`
* 獲取 key 的有效時長 : `ttl key`
* 移除 key 的有效期限 : `persist key`
* 當2不存在時,將1改為2 : `renamex key1 key2`
* Example 1 : 測試 key 是否存在並刪除

* Example 2 : 設定有效期限

* Example 3 : set 交集、聯集、差集

* Example 4 : zset 範圍查詢與排列

* Example 5 : hash 應用

## 附-安裝 Ubuntu
1. 到[官網](https://ubuntu.com/download/desktop)下載映像檔
2. 新增虛擬機

3. 設定細節 (名稱、密碼記得改)




* 跑完就可以使用了

* 處理 terminal 無法開啟
ctrl-alt-F1 ~ F6(任一) 開啟終端介面
登入使用者,使用 su 變成 root (密碼跟 vboxuser 一樣)
`vim /etc/default/locale`
```
LANG=en_US.UTF-8
LANGUAGE=en_US.UTF-8
```
* 建議先裝
```
apt install -y vim
apt install -y net-tools
```