# Linux
[TOC]
## LazyVim
- Installation
- `curl -LO https://github.com/neovim/neovim/releases/download/v0.11.1/nvim-linux-arm64.appimage`
- `chmod u+x nvim-linux-arm64.appimage`
- `mv nvim-linux-arm64.appimage /usr/bin/nvim`
- color
- `colorscheme habamax`
- usable types : `ls ~/.local/share/nvim/lazy/`
- Settings
- `LazyExtras`
## tcpdump
- `sudo tcpdump -i <interface> dst host 192.168.50.150 and dst port 514`
- display all packets send to port 514 : `sudo tcpdump -X -i <interface> udp port 514 -w ~/b.pcap`
## nfs
### 概念
- 讓 node 之間透過網路共享檔案
### 安裝
- server : `sudo apt install nfs-kernel-server`
- client : `sudo apt install nfs-common`
### 設定
- server
- 確保 port 開啟 on firewall
- Port 111 (TCP and UDP) and 2049 (TCP and UDP) for the NFS server.
- `sudo mkdir /var/nfs`
- `sudo vim /etc/exports`
```=
/var/nfs/ 163.22.17.153(rw,sync)
```
- `sudo systemctl restart nfs-kernel-server`
- `sudo exportfs` : 確認設定
- 
- client
- mount
- `sudo mkdir /var/nfs`
- `sudo mount 163.22.17.116:/var/nfs /var/nfs`
- unmount
- `sudo unmount 163.22.17.116:/var/nfs`
- <b>當前 shell 不能在 mount 的 path</b>
### ref
- https://www.akiicat.com/2019/03/09/DevOps/setup-nfs-server/
## wildcard dns
```=
sudo apt-get install dnsmasq
sudo echo "address=/.example.com/127.0.0.1" >> /etc/dnsmasq.d/weblocal
```
- `vi /etc/resolv.conf`
```=
nameserver 127.0.0.1
nameserver 8.8.8.8
nameserver 8.8.4.4
```
- `vi /etc/dnsmasq.conf`
```=
address=/.s3.hicloud.net.tw/192.168.17.138
address=/s3.hicloud.net.tw/192.168.17.138
```
- `sudo /etc/init.d/dnsmasq restart`
## GlusterFS
### 概念
- 這敘述不太正確
- 分散式儲存和分散式檔案系統最大的區別,檔案系統是可以掛載的,而儲存則是無檔案系統入口的,所以只可以透過API來訪問,常見的分散式儲存有:
- GFS: Google File System
- 分散式的鼻祖等級系統,由Google內部開發,發表有技術實作論文,為後來的分散式儲存系統提供了技術實作基礎
- HDFS: Hadoop Distribution File System
- Hadoop的分散式儲存系統,透過GFS的論文的開源實現
- GlusterFS
- 不溫不火的分散式儲存系統,被redhat收購,去中心化,沒有元資料節點
- Ceph
- Linux內核級的實現的分散式檔案系統,收錄Linux內核,正在被廣泛的試驗,未來的新貴
- MogileFS
- 適合儲存海量小文件,使用perl語言編寫,有人使用c語言重寫並開源為FastDFS
- TFS
- Taobao FileSystem,基於HDFS開發適合儲存海量小文件
### 架構
- 
- brick
- 在意思上為儲存區,也就是設定好 LVM 並完成格式化之後的可用邏輯磁碟區。
- 所有 Client 的檔案都會被放在 Brick 上,Brick 所提供的容量大小是由 LVM 設定而來,所以當單一個 Brick 損毀或空間滿的時候,只會照成部份的檔案不見或無法寫入,而不是所有檔案都會遺失。
- volume
- Volume 也就是最終 Client 可以使用的項目,設定好 Volume 之後只要在 Client 端使用 mount 指令就可以使用 Gluster 檔案系統,而在 Client 所看到的可用容量自動計算其 Brick 成員加總而成的總空間大小。
- 一個 Volume 是由多個 Brick 所組成的,依據其 Volume 的分散式狀態所得的可用空間也不一樣,比如設定 Volume A 是由 brick1(10GB) 與 brick2(10GB) 使用分散式設定,那麼會得到 10GB + 10GB = 20GB 的總容量;若是使用複寫式,則會因為資料同時保留兩份在不同 Bricks 中,所以其容量也只會有 10GB。
### ref
- https://www.l-penguin.idv.tw/book/Gluster-Storage_GitBook/gluster_intra/gluster_arch.html
- https://glusterdocs.readthedocs.io/en/latest/Administrator%20Guide/GlusterFS%20Introduction/
## 筆記
1. 軟連結
- 建立, 把現在這個目錄下的 text.txt 連結到 root 下的 text.txt
ln -s text.txt /root/text.txt
- 刪除, 把現在這個目錄下的 text.txt 的軟連結刪除
rm -rf ./text.txt
- 全部刪除, 把現在這個目錄下的 text.txt 的軟連結刪除以外, 還會把原本連結到的檔案刪除
rm -rf ./test_chk_ln/
2. 刪除目錄
- 空目錄
rmdir
- 不一定要空的目錄
rm -rf
3. 查看監聽 port
netstat -tulpn grep :80
4. 複製文件
- ex. 複製這個目錄下的 a.py 到 ~ 下的 a.py
cp a.py ~/a.pyi
5. tab*2 查詢指令、資料夾
- 搜尋指令,第一個字的後面兩個 tab
ex.
ca[tab][tab]
- 搜尋檔案,第二個字後面兩個 tab
cat ~/[tab][tab]
6. 虛擬環境 pip install 出現 permissionError denied
- 教學
https://blog.csdn.net/Yonggie/article/details/114327306
- 原因
該帳號不是最高級別用戶
- 變更為 root 權限帳號(需輸入 root 密碼)
su -
- 若沒有 root 密碼,需先建立一個
sudo passwd root
7. scp 到指定 port
- 複製本地檔案到遠端 ex.
scp -P 6653 /file root@xxx.xxx.xx:/file
- 複製遠端檔案到本地 ex.
scp -P 6653 root@xxx.xxx.xx:/file /file
- not a regular file
加上 -r 參數
scp -r -P 6653 /file root@xxx.xxx.xx:/file
8. 清理 journal
- 教學
https://www.peterdavehello.org/2020/01/clean-up-linux-systemd-journal-logs/
9. 查看 host 容量
- `df -h`
10. 查看檔案大小
- ex. `du -sh ./*`
- `-h` : human readable
- `-s` : 只顯示總和不顯示其子目錄下的檔案
10. 檔案權限
- 查看
ls -l
- 意義
r = 可讀, 權限值 = 4
w = 可改, 權限值 = 2
e = 可執行, 權限值 = 1
順序分別為 user, group, others
有時候執行也需要可讀
ex.
-rwxr----- = 相同 user 可讀寫執行、相同 group 可以讀、其他不能寫讀執行
- 增加權限 : chmod
- 規則 : r屬性值:4, w屬性值:2, x屬性值:1
ex.
chmod 750 = user 可讀寫執行、相同 group 可以讀寫、其他不能寫讀執行
- 四碼代表的意思 :
4000 Sets user ID on execution
2000 Sets group ID on execution
1000 Sets the link permission to directoires or sets the save-text attribute for files.
ex. 4771 : 代表其他使用者在執行此檔案的時候會有 user 的權限。
11. 找檔案內容
https://www.codingsusu.com/find-all-files-containing-specific-text-on-linux/
`grep -rnw 'path' -e 'text'`
12. 查看 ram 狀況
- free
- 看幾 g : `free -g`
13. 查看目前使用者
w
14. 寫入內容到那個使用者的 terminal
sudo write user TTY
15. 查看指定 port
netstat -tulpn | grep :80
16. 管理 gitlab
- 開啟
sudo gitlab-ctl start
- 關閉
sudo gitlab-ctl stop
17. ln, 連結
ln test.py test2.py # 把 test2.py 硬連結到 test.py
ln -s test.py test2.py # 把 test2.py 軟連結到 test.py
- 軟連結
inode 不同,可以連結整個資料夾,原檔案刪除後被連結的檔案內容會消失,不太佔硬體空間
- 硬連結
inode 相同,不可以連結整個資料夾,原檔案刪除後被連結的檔案不會消失,原檔案消失前不會佔硬體空間
18. sync, 資料同步
linux 會把資料放在 ram 的 buffer,所以在關機之前需要把資料同步回去硬碟。
19. passwd, 改密碼
20. 壓縮
- gzip
gzip test.py # 壓縮
gzip -d test.py.gz # 解壓縮
- xz
xz test.py # 壓縮
xz -d test.py.xz # 解壓縮
- tar
tar cvf trat.tar test.py # 壓縮
tar xvf test.tar # 解壓縮
tar xvf test.tar.gz # 也可以直接解 .gz
tar zcvf FileName.tar.gz DirName
壓縮不包含父目錄 : `tar -czvf xxx.tar.gz -C /ebs1/xxx/aaa/bbb .`
- 7zip
- 解壓縮,依照原本檔案格式 : `7z x file.7z`
- 這樣會全部解在當前目錄 `7z e file.7z`
- grep
grep -r 'tommy' * # 要找資料夾要 -r, * 代表從根目錄找
- flush dns
sudo systemd-resolve --flush-caches
- 安裝 vm : 如果看不到安裝的按鈕,可以用 tab,要注意的是有些是取消、上一步、下一步、安裝
- 查看使用者帳號密碼
- 有加密過的, 不須 sudo
``cat /etc/passwd``
- 沒加密的, 要 sudo
``sudo cat /etc/shadow``
- 軟連結(soft link) vs 硬連結(hard link)
- 軟連結 : 常用於當作捷徑
1. 刪掉母體, 分身會消失
2. 改母體或分身, 兩個的內容都會一起變
- 硬連結 : 常用於備份
1. 刪掉母體, 分身不會消失
2. 改母體或分身, 兩個的內容都會一起變
- 把檔案變成執行檔 : `sudo chmod +x xxx` # xxx = 檔案名稱
- 執行 : 在和執行檔同個路徑下 `./xxx`
- 變成所有使用者都可以直接呼叫檔名執行 : 把執行檔複製到 /usr/local/bin
- `sudo cp xxx /usr/local/bin/`
- 網段
- 判斷此網段有多少 ip : 看遮罩到第幾個 bit,總共會有 4*8 = 32 個 bits,若剩下可以用的有 n 個 bit,就是 2 的 n 次方
- network id : 該網段的第一個 ip。
- broadcast id : 該網段的最後一個 ip
- netmask : 遮罩, ip = 不能使用的 bit 用 1 填滿, 代表總共有多少個 ip 可以使用
- 教學
https://ithelp.ithome.com.tw/questions/10153316
- ex.
1. 255.255.255.0 有 255 個 ip(255.255.255.255-255.255.255.0)
2. 255.255.255.248 有 7 個 ip(255.255.255.255 - 255.255.255.248 = 7)
- 實際範例 : 有一個網段 123.45.67.89/26
- 此網段總共有多少 ip : 被遮掉 26 bits,所以剩 6 個 bits,ans : 2^6 = 64。
- network id : 123.45.67.89 = x.x.x.01011001,ans : 123.45.67.01000000 = 123.45.67.64
- broadcast id : 123.45.67.89 = x.x.x.01011001,ans : 123.45.67.01111111 = 123.45.67.127
- netmask 遮罩 : ans : 11111111.11111111.11111111.11000000 = 255.255.255.192
- iptables :
- Chain : 時間點
- `FORWARD` : 轉送封包,沒有進去 firewall 的主機
- `INPUT` : 進去 firewall 的主機。ex. 直接 ssh 進這台主機
- `OUTPUT` : 主機送出的封包
- `PREROUTING` : 在接收封包之前
- `POSTROUTING` : 在送出封包之前
- table :
- filter : `sudo iptables -t filter` # 預設是 filter,所以也可以不寫
- nat : `sudo iptables -t nat` # 只有當需要換 ip 位址才要用,ex. SNAT、DNAT、MASQUERADE
- MASQUERADE : 把送出去的封包的內網 ip,轉成外部 ip,讓 destination 可以知道要送回的 ip
- 範例 :
`sudo iptables -t nat -I POSTROUTING -o {wan nic} -j MASQUERADE`
- 紀錄連線的狀態 : `conntrack`
- 規則 :
```=
NEW: 新的連線
ESTABLISHED: 已建立的連線
RELATED: 與 ESTABLISHED 連線有關的連線
INVALID: 無效
```
- 範例 : 允許轉送已建立連線的封包(允許送出去的封包回的來)
`sudo iptables -A FORWARD -m conntrack --ctstate "ESTABLISHED,RELATED" -j ACCEPT`
- 列出每個 chain 的規則 : `iptables -L INPUT -n --line-numbers` # -n --line-numbers = 印出行號
- 刪除對應 chain 的規則 : `sudo iptables -D CHAIN的種類 列出的num`
- ex. : `sudo iptables -D INPUT 1`
- debug : `sudo iptables -nvL`,可以看bytes有沒有增加
- sudo iptables -L
列出 filter 三個 chain 的規則
- proxy, 代理
- 正向代理 : 客戶藉由一台正向代理的機器向伺服器端取得資料。代理客戶端去和伺服器端拿資料,有點類似跳板機。
- 反向代理 : 客戶向一台反向代理的機器拿資料,那台再向伺服器端(通常會有多台伺服器)拿資料。代理伺服器端回傳資料給客戶,通常用於負載平衡。
- 雲端 vm
- 教學 : https://ithelp.ithome.com.tw/articles/10219735
- nginx
- check config nginx syntax
`sudo nginx -t`
- 改 port :
1.`sudo vi /etc/nginx/sites-available/xxx` # xxx = 設定檔名,預設有一個 default 設定檔
2. 把 listen 的後面的數字改為要去的 port
- 讓 available 寫的 conf 檔生效 : 把設定檔軟連結到 sites-enable
`ln -s /etc/nginx/sites-available/www.example.org.conf /etc/nginx/sites-enabled/` # 一定要用絕對路徑
- 改預設 html 位置
- ex. 把預設(/var/www/html/index_nginx.html)改為 /var/www/nginx/xxx.html # xxx = 某個 html 檔
```=
root /var/www/nginx;
index xxx;
server_name lsa.lab;
location / {
try_files $uri /xxx.html =404; # 這行記得把第二個 $uri 改成要去的 .html
}
```
- proxy settings :
- 教學 : https://sharefunyeh.gitbooks.io/webdev/content/articles/understand-nginx-proxy-load-balancing-buffer-and-cache.html
- 實作 :
```=
# the url on upstream and server no need to add the https or http, as will add default
upstream ammon.bluet.org { # this is like domain name, so must be same as the proxy_pass's url without the http/s
server tw.search.yahoo.com.;
server ammon.bluet.org;
}
server {
listen 8070;
server_name localhost;
location / {
# 這邊也可以改為 http://w/ or http://s 之類的,但上面 upstream 就要改為 w or s
proxy_pass http://ammon.bluet.org/;
}
}
```
- certbot :
- 教學 : https://abstreamace.com/sglab/2021/02/04/%E6%9B%BF-ubuntu-nginx-%E5%8F%96%E5%BE%97%E5%85%8D%E8%B2%BB-ssl-%E6%86%91%E8%AD%89%EF%BC%88%E4%BD%BF%E7%94%A8-certbot%EF%BC%89/
- `sudo snap install --classic certbot`
- `sudo ln -s /snap/bin/certbot /usr/bin/certbot
`
- 把目前的 .conf 檔中的內容產生憑證 : `sudo certbot --nginx`
- ssh
- 一般連線 : `ssh ann@172.0.2.15` # 使用者名稱@ip
- local tunel : 把本地的某個 port 連到另外一台機器的 port 上。
- 語法 : `ssh -L <local IP>:<local port>:<target IP>:<target port> <target server user name>@<target IP>
- 範例 : 要把本地的 8080 port 連到 163.22.32.40 的 5000 port
- `ssh -L 8080:0.0.0.0:5000 wang@163.22.32.40`
- remote tunel : 和 local tunel 相反,把本地的某個 port 讓另一台機器的 port 連上。
- 語法 :
`ssh -R <server IP>:<server port>:localhost:<localhost port> jump@192.168.0.105 -Nf`
- 只綁定遠端的 127.0.0.1
- By editing the SSH config file (usually `/etc/ssh/sshd_config` on Linux), you can set the `GatewayPorts` to `yes` and then it should allow SSH tunnels to listen on the other interfaces.
- 用憑證連線,不用輸入密碼 :
1. (在本機) `ssh-keygen -t rsa` # 用 tsa 加密
2. (在本機複製公鑰) 複製 `~/.ssh/id_rsa.pub`
3. (在要連線的) `ssh-keygen -t rsa` # 用 tsa 加密
4. (在要連線的) `sudo vi ~/.ssh/authorized_keys`,貼上剛剛在本機複製的 public key
- forward agent :
- 說明 :
把私鑰帶者跑, 就不用重新輸入密碼。
- 實際情況 : 先 ssh 到一台 server, 再 ssh 到另一台的時候, 如果沒有 ssh forwarding agent 把私鑰帶著跑, 另一台不會知道你原本(本地)是誰, 所以會需要再輸入密碼。
- 步驟 :
1. `eval $(ssh-agent)`: 啟動 ssh-agent
2. `ssh-add ~/.ssh/id_rsa`: 將要 forwarding 的 private key 加到清單內
3. `ssh-add -L`: 檢視是否更新成功
4. `ssh -A user@<server1 ip>` : -A 代表用 forward agent 的方式 ssh
- 背景執行 : `-f`
- ex. 想要 3009 port 導向其他 vm 的 3009 port 上的服務
- `ssh -fNL 0.0.0.0:3009:0.0.0.0:3009 tommygood@163.22.32.40`
- ssh server 會 listen 若 client 超過一定時間沒送 data 會斷開連線。所以若要保持連線就要調整一下:
- 從 client 端
- `vi ~/.ssh/config`
```=
# 每60秒發送一次keep alive封包
ServerAliveInterval 60
# 斷開時重新連接的次數
ServerAliveCountMax 5
```
- 或是僅此次連線帶參數就好
- `ssh -o ServerAliveInterval=60 user@remote_host`
- 從 server 端
- `vi /etc/ssh/sshd_config`
```=
# 將超時設定為600秒(10分鐘), 最大允許3次
ClientAliveInterval 600
ClientAliveCountMax 3
```
- ref
- https://vocus.cc/article/64cb99ecfd89780001cf3ede
- apache2
- 改 port :
1. sudo vi /etc/apache2/ports.conf
2. sudo vi /etc/apache2/sites-available/000-default.conf
- 改預設 html 位址: `sudo vi /etc/apache2/sites-available/000-default.conf` 的 DocumentRoot(先確定有這個資料夾)
- check config syntax : `sudo apachectl configtest`
- lighttpd
- 改 port : `sudo vi /etc/lighttpd/lighttpd.conf` 的 server.port
- 改預設 html 位址: `sudo vi /etc/lighttpd/lighttpd.conf` 的 server.document-root
- useradd vs adduser :
- useradd : 只會新增使用者,不會新增他的家目錄。
- ex.
1. `sudo useradd tommy` # 設帳號
2. `sudo passwd tommy` # 設密碼
- adduser : 新增使用者,也會新增他的家目錄。
- ex. `sudo adduser tommy`
- 給 root 權限
- `sudo vim /etc/sudoers`
- ex. 新增 jerry
- 
- 覆寫 : `wq!`
- 一登入就執行指令 :
- `sudo vi /etc/profile`
- 輸出文字 : 加 `echo "Welcome"` 在最後一行
- 執行指令 : 直接加在最後一行,ex. `exit`
- 讓加入的使用者自動新增檔案
- 在 /etc/skel 裡新增要讓新的使用者有的檔案,就可以讓新的使用者的家目錄下自動產生一個一樣的檔案
- ex : `sudo vi /etc/skel/readme.txt`
- 從 log 分析 request 次數
- https://serverfault.com/questions/226982/how-to-measure-req-sec-by-analyzing-apache-logs
- ftp
- 指令教學 : https://www.ltsplus.com/linux/linux-ftp-command
- 教學 :
- http://old.linux.vbird.org/linux_server/0410proftpd.php
- https://www.ltsplus.com/linux/linux-ftp-command
- 登入方式
- `ftp localhost`
- vsftpd
- 安裝
- `sudo apt install vsftpd`
- 設定檔
- 路徑 : `/etc/vsftpd.conf`
- 可以寫入
- `write_enable=YES`
- 限制 port
```
pasv_min_port=2121
pasv_max_port=2142
```
- 限制使用者只能用自己的家目錄
```
chroot_local_user=YES
allow_writeable_chroot=YES
```
- 開放防火牆
- `sudo ufw allow ftp`
- fail2ban
- `sudo vi /etc/fail2ban/jail.conf`
```
[vsftpd]
enabled = true
```
- `sudo systemctl restart fail2ban`
- rkhunter : 檢查是否有 rootkit(惡意攻擊取得 root 權限的軟體)
- 教學 : https://linux.vbird.org/linux_server/others/0420rkhunter.php
- 安裝 : `sudo apt install rkhunter`
- 執行 : `sudo rkhunter -c` # -c = check all
- 固定 ip
教學 : https://www.ltsplus.com/linux/rhel-centos8-setup-static-ip
- 先找出 default gateway : `ip route | grep default`
- 再找出網卡 : `ifconfig`
- 設定固定 ip 及 default gateway(上面找到的) : `nmtui edit enp0s3` # 用剛剛找到的網卡
- 重啟網路
- 檢查對外流量 : `netstat -anpt`
- 給 root 權限 : `sudo usermod -aG sudo user_name`
- ufw :
- 教學 : https://blog.tarswork.com/article/ubuntu-firewall-setting-using-ufw/
- 查看規則 : `sudo ufw status`
- 新增規則 :
- 允許特定 ip 到特定 port : `ufw allow from 192.168.0.1 to any port 3306`
- 允許特定 ip 到所有 port : `ufw allow from 192.168.0.1`
- 允許通過 80 Port 使用 tcp 與 udp 連線 : `ufw allow 80`
- 允許通過 80 Port 使用 tcp : `ufw allow 80/tcp`
- 刪除規則
- `sudo ufw status numbered`
- `sudo ufw delete rule_num`
- firewalld
- 安裝
- `sudo dnf install firewalld -y`
- `sudo systemctl enable firewalld`
- `sudo systemctl start firewalld`
- 查看規則 : `sudo firewall-cmd --list-all`
- 新增 : `sudo firewall-cmd --permanent --add-port=1883/tcp`
- 刪除 : `firewall-cmd --zone=public --remove-port=12345/tcp --permanent`
- 修改皆要 reload : `sudo firewall-cmd --reload`
- 關掉後無法打開
- `sudo systemctl stop firewalld`
- `sudo pkill -f firewalld`
- `systemctl start firewalld`
- DNS : 17.131
- 更新步驟
- `sudo rndc freeze im.ncnu.edu.tw`
- 編輯設定網域 zones:`sudo vim /etc/bind/zones/db.im.ncnu.edu.tw`
A. 新增Domain
B. 修改Serial 年/月/日/修改次數
* 查 A(v4) AAA(v6) CNAME
C. 存檔離開
`sudo rndc reload im.ncnu.edu.tw`
`sudo rndc thaw im.ncnu.edu.tw`
`sudo service bind9 reload`
- im
- 
- 移除目前使用者
- 先查看 : `w`
- 踢除 : `sudo pkill -kill -t pts/2`
- 釋放 snapd 的 cache
- `sudo bash -c 'rm /var/lib/snapd/cache/*'`
- https://askubuntu.com/questions/1371833/howto-free-up-space-properly-on-my-var-lib-snapd-filesystem-when-snapd-is-unava
- 更新 java 版本
- 看一下目前版本 : `java --version`
- 下載新版 java : ex. `sudo dnf install java-11-openjdk-devel -y`
- 更新 java 版本 : `update-alternatives --config java`
- 
- 選擇自己要的版本
- 有 process id 可以去 `/proc/{process_id}` 查看資訊
- get ip : `hostname -I`
- 導向修理網址
- 建立修理的內容
- `sudo mkdir /var/www/fixed`
- `sudo vi /var/www/fixed/index.html`
- 新增以下 : `vi /etc/apache2/sites-available/xxx.conf`
```conf=
<LocationMatch ".*">
Alias "/var/www/fixed/index.html"
</LocationMatch>
<LocationMatch "/fixed.jpg">
Alias "/var/www/fixed/fixed.jpg"
</LocationMatch>
```
- `sudo systemctl reload apache2`
- arp
- arping : `arping ip`
- 有時候 ping 不到可以用此,比較底層的協定
- 因 ping 是 icmp,可以被關掉
## vim
- tab = 4 space
```config=
set smartindent
set tabstop=4
set shiftwidth=4
set expandtab
```
## C
- 編譯
``gcc -g -Wall HelloWorld.c -o HelloWorld``
當中的「-g」是加入除錯資訊;「-Wall」是顯示所有的警告訊息 ;「-o」用來指定所生成的可執行程式的文件名(Helloworld)
- &
- 意義
- 取該變數的位址
- scanf 因為要把值存入此變數的位址,所以要用 &。而 printf 因為要取這個變數的值,所以不用
- pointer (指標)
- 意義
- 變數的位址
- ex.
```c=
int b = 2;
// 兩種都可
int* pointer = &b;
int *pointer = &b;
```
- array
- 宣告
```c=
int test[5]; // 大小為 5 的 int array
int arr[] = {3, 4, 5, 6, 7};
```
- parameter in funcion
```c=
int test(int now[]) {
}
```
# Docker
- 安裝 :
- `sudo apt-get update`
- `sudo apt install docker.io`
- 把使用者加入 docker 群組
- `sudo usermod -aG docker $USER && newgrp docker
`
- 基本指令 :
- 檢查有沒有啟動 : `docker status`
- 下載映像檔 : `sudo docker pull ubuntu`
- 查看當前映像檔 : `sudo docker images`
- 查看有哪些可用的映像檔 : `sudo docker search`
- ex. `sudo docker search httpd`
- 刪除映像檔 : `sudo docker rmi image_id`
- 複製本地檔案到 container : `sudo docker cp myfile.txt container_name:/usr/share`
- 把 image 跑起來 :
```
sudo docker run -itd \
--name test_web_server \
-p 8085:80 \
-v /home/user/website/:/usr/local/apache2/htdocs/ \
docker.io/httpd:latest
```
- 查看當前在跑的 container : `sudo docker ps -a`
- 停止目前在跑的 container :
- 單一的 : `docker stop container_id`
- 所有的 : `docker stop $(docker ps -a -q)`
- 刪除目前 container :
- 單一的 : `docker rm container_id`
- 所有的 : `docker rm $(docker ps -a -q)`
- 查看 container log : `sudo docker logs container_id`
- 進入 container : `sudo docker exec -it ubuntu bash`
- docker-compose :
- 安裝 :
- `sudo curl -L "https://github.com/docker/compose/releases/download/1.10.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose`
- `sudo chmod +x /usr/local/bin/docker-compose`
- `sudo docker-compose -v`
- 跑起來 : `sudo docker-compose up -d`
- 若有更新 docker-compose,也是下這指令,docker 會只針對有更新的地方更新,舊的不會動。
- 暫時停止(不刪除): `sudo docker-compose stop`
- 停止並刪除 container、network... : `sudo docker-compose down`
- 安裝套件 :
- 先進 cotainer : `sudo exec -it container_name bash`
- 更新 apt : `apt-get update`
- 安裝套件 : `apt install xxx`
- 查看 ip :
- `docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' container_name_or_container_id
`
- wordpress : `docker-compose.yml` + `uploads.ini`
- `mkdir wordpress`
- `cd wordpress`
- `vi docker-compose.yml` :
- 注意 :
- 第四行 db 和 16 行 wordpress <b>不</b>要改。外接的 ports 要改,不能重複、container_name要改、volumes 的外部路徑也要改。: https://ithelp.ithome.com.tw/articles/10281462
- 如果原本有 db_data,要先 rm 掉。(重要!如果沒 rm 會有問題 ex. 空白畫面)
```=
version: '3'
services:
db:
image: mysql:5.7
container_name: mysql_wordpress_3
volumes:
- /var/www/docker/wp_4/db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: wordpress_root_password
MYSQL_DATABASE: wordpress_db
MYSQL_USER: wordpress_user
MYSQL_PASSWORD: wordpress_password
wordpress:
container_name: wordpress_3
depends_on:
- db
image: wordpress:latest
ports:
- "8891:80"
restart: always
volumes:
- ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
- ./ai1wm-backups:/var/www/html/wp-content/ai1wm-backups
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress_user
WORDPRESS_DB_PASSWORD: wordpress_password
WORDPRESS_DB_NAME: wordpress_db
volumes:
db_data: {}
```
- `- ./ai1wm-backups:/var/www/html/wp-content/ai1wm-backups` : 掛載備份檔。不是英文 `l`,是數字 `1`
- 如果是之後才加
- 要先進去 container 把原本的 `ai1wm-backups` 改成別的名字,`docker-compose up -d` 後再改回來。
- `docker-compose up -d` 完後,要進 container 把 `ai1wm-backups` 的權限改回來 `chown www-data:www-data ai1wm-backups`
- ` vi uploads.ini` : 更改傳輸檔案上限 :
- `vi /usr/local/etc/php/conf.d/uploads.ini`
```
file_uploads = On
upload_max_filesize = 2000M
post_max_size = 2000M
memory_limit = 128M
```
- 如果是一開始沒有一起 up 的話,就要新增和更新
- `sudo docker exec -it container_name bash`
- `vi /usr/local/etc/php/conf.d/uploads.ini`
```
file_uploads = On
upload_max_filesize = 2000M
post_max_size = 2000M
memory_limit = 128M
```
- `exit`
- `sudo docker restart container_name`
- 最後用 docker-compose 跑起來 : `sudo docker-compose up -d`
- 訪問該位址 : `http://ip:port`
- 改用 domain name :
- dns server 先將新的 domain name 指向裝此 wordpress 的 server 的 ip
- 在 config 變更 domain name :
- `sudo docker exec -it wordpress_im_department`
- `sudo vi wp-config.php` : 要加在最上面(`<?php` 後面)
```php=
<?php
define( 'WP_HOME', 'http://example.com' );
define( 'WP_SITEURL', 'http://example.com' );
```
- `service apache2 reload`
- nginx domain name : https://www.youtube.com/watch?v=SLejN0yC8sM
- `sudo vi /etc/nginx/sites-available/xxx`
```conf=
server {
server_name test.im.ncnu.edu.tw;
location / {
proxy_pass http://163.22.17.234:8888;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto $scheme;
}
listen 80;
}
```
- `sudo ln -s /etc/nginx/sites-available/xxx /etc/nginx/sites-enabled/`
- `sudo systemctl reload nginx`
- 訪問該 domain : `http://{domain_name}`
- 用 certbot 獲得 ssl 憑證 : `sudo certbot --nginx`
- 選擇該 domain name 的號碼
- ssl 教學 : https://www.youtube.com/watch?v=T7flzYEtMGA
- `vi wp-config.php` : 最上面新增
```php=
if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
$_SERVER['HTTPS']='on';
if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
$_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST'];
}
```
- `service apache2 reload`
- 訪問 https : `https://{domain_name}`
- elementer 進不去 : https://juhui.com.tw/elementor-%E4%B8%80%E7%9B%B4%E8%BD%89%E5%9C%88%E5%9C%88-%E7%84%A1%E6%B3%95%E7%B7%A8%E8%BC%AF/
- 暫時解
- `sudo vi /var/www/intern/wp-includes/default-constants.php` : 改成 512 M
- 
- `sudo systemctl reload apache2`
- 可能會因為 wordpress 更新版本而設定檔跑掉就又要重調
- 應該是根本解
- `sudo vi /var/www/intern/wp-config.php`
- 新增 : `define('WP_MEMORY_LIMIT', '512M');`
- 
- `sudo systemctl reload apache2`
- 無法手動新增外掛
- 自己丟到 wp-content/plugins
- `sudo docker cp plugin_name docker_name:/var/www/html/wp-content/plugins`
- 記得更改權限
- `sudo dokcer exec -it container_name bash`
- `cd /var/www/html/wp-content/plugins`
- 看一下新增的套件,權限應該是 root
- `ls -l`
- 改權限成 web server
- `chown -R www-data:www-data plugin_name`
- all in one migration
- 手動放還原檔
- `sudo docker cp xxx.wpress container_name:/var/www/html/wp-content/ai1wm-backups`
- 會有某些圖檔不見
- 因為檔名中包含中文,所以編碼後會有問題
- 解法
1. 把 `wp-content/uploads/` 直接複製到新的主機
- https://www.johntool.com/all-in-one-wp-migration-limit/
2. 重新上傳圖片
3. 把原本的 wordpress 有用到中文的檔名改為英文後再輸出一次
- 要加在其在 nginx 的設定檔
```conf=
location = /xmlrpc.php {
deny all;
access_log off;
log_not_found off;
return 444;
}
location ~ /\.ht {
deny all;
}
client_max_body_size 64m;
```
- 更換 domain name
- 新增其 nginx 的 設定檔
- `sudo ln -s /etc/nginx/sites-available/xxx /etc/nginx/sites-enabled/`
- `sudo systemctl reload nginx`
- 開無痕 get 一下該 url
- 可以的話就 : `sudo certbot --nginx`
- 最後進到 container 改 define 的 domain name : `vi wp-config.php`
- 新增寄信功能
- 教學 : https://weblai.co/smtp-tutorial/
- ex. `SMTP 使用者名稱` : tommy50508@gmail.com
- nginx manager 教學 : https://medium.com/@jmpinney/multiple-wordpress-sites-on-one-server-with-docker-6fb53adc4bfe
- docker-compose :
```=
version: "3"
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
# These ports are in format <host-port>:<container-port>
- '80:80' # Public HTTP Port
- '443:443' # Public HTTPS Port
- '81:81' # Admin Web Port
# Add any other Stream port you want to expose
# - '21:21' # FTP
# Uncomment the next line if you uncomment anything in the section
# environment:
# Uncomment this if you want to change the location of
# the SQLite DB file within the container
# DB_SQLITE_FILE: "/data/database.sqlite"
# Uncomment this if IPv6 is not enabled on your host
# DISABLE_IPV6: 'true'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
```
- php + apache :
- docker-compose.yml
```config=
version = '3'
services :
server :
image : php:apache
container_name : server_im_warehouse
ports :
- 8890:80
volumes :
- ./test:/var/www/html
db:
image: mysql:5.7
container_name: mysql_im_warehouse
volumes:
- /var/www/im_warehouse/db_data:/var/lib/mysql
restart: always
ports:
- "3308:3306"
environment:
MYSQL_ROOT_PASSWORD: jim_tsai
MYSQL_DATABASE: im_warehouse
MYSQL_USER: im_warehouse
MYSQL_PASSWORD: jim_tsai
```
- install mysqli :
- `sudo docker exec -it server_im_warehouse bash`
- `docker-php-ext-install mysqli`
- `docker-php-ext-enable mysqli`
- `sudo docker restart server_im_warehouse`
- mysql :
- 備份 : https://gist.github.com/spalladino/6d981f7b33f6e0afe6bb
`docker exec CONTAINER /usr/bin/mysqldump -u root --password=root DATABASE > backup.sql`
- 還原 : https://gist.github.com/spalladino/6d981f7b33f6e0afe6bb
`cat backup.sql | docker exec -i CONTAINER /usr/bin/mysql -u root --password=root DATABASE`
- ex.
`sudo cat im_warehouse.bak | sudo docker exec -i mysql_im_warehouse /usr/bin/mysql -u im_warehouse --password=jim_tsai im_warehouse`
- 中文亂碼問題 :
- 編碼改成 utf-8 : `sudo vi mysql.cnf`
```conf=
[mysql]
default-character-set=utf8
[mysqld]
collation-server = utf8_unicode_ci
init-connect='SET NAMES utf8'
character-set-server = utf8
[client]
default-character-set=utf8
```
- 複製到 container 裡面 : `sudo docker cp mysql.cnf mysql_im_warehouse:/etc/mysql/mysql.conf.d`
- `sudo docker restart mysql_im_warehouse`
- 登入
- `sudo docker exec -it container_name bash`
- `mysql -u 帳號 -p`
- 帳號密碼在 docker-compose.yml
- 重灌 docker(把 image, container... 都砍光)
- `docker system prune`
- 如果系統容量滿到動不了,然後 docker 這時又是關的也開不起來
- 自己去 `/var/lib/docker/containers` 砍一些 container,再把 docker 打開後再 `prune`
## note
- container 沒有 ping
- `apt update`
- `apt install iputils-ping`
## dockerfile
- multi stage
- 在專案開發時,通常會執行代碼檢查、單元測試、編譯、執行等等,將這些流程步驟撰寫成 dockerfile 直接在容器裡執行衍伸出了新的問題。
- 為了執行代碼檢查和編譯,下載了許多套件或是相依工具,造成映像檔肥大,很多套件或是相依工具在部署時是不需要的。
- ex.
```=
FROM golang:1.16.0-alpine3.13 AS builder
WORKDIR /workspace/example
ENV GO111MODULE=on CGO_ENABLED=0
# download dependency
COPY go.mod go.sum .
RUN go mod download
RUN go install honnef.co/go/tools/cmd/staticcheck@v0.1.2
COPY . .
RUN go vet ./... && staticcheck ./... && go test ./... && go build -o /bin/server
FROM alpine:latest AS release
# Copy from builder
COPY --from=builder /bin/server /bin/server
ENTRYPOINT ["./bin/server"]
```
- 教學
- https://ithelp.ithome.com.tw/articles/10191016?sc=hot
- `docker build -t mytomcat . --no-cache`
-
- 上傳到 docker hub
1. 先到 docker hub 開一個 repo
2. `docker tag {image_name} {dockerhub_account}/{repo_name}:{version_num}`
- 如果沒有加 `:{version_num}` 預設是 `:latest`
- ex.
- 
- `docker tag backend_node tommygood/node_server`
3. `docker push {dockerhub_account}/{repo_name}`
- ex. `docker push tommygood/node_server`
- ref
- https://ithelp.ithome.com.tw/articles/10192824
- 補充
- k8s 的 image 如果沒有用到 publish 的 image 會有問題
- https://ithelp.ithome.com.tw/articles/10193944
- run vs cmd : cmd 是當 `docker run` 這個 image 會執行的命令(也就是已經建立完 image 時),run 是在建立 image 的時候會執行的命令
## docker-compose
- 多個 command
```yml=
command: >
sh -c "python manage.py wait_for_db &&
python manage.py migrate &&
python manage.py runserver 0.0.0.0:8000"
```
- ref
- https://stackoverflow.com/questions/30063907/docker-compose-how-to-execute-multiple-commands
- volumes
- bind : host 的<b>相同</b>路徑檔案內容對應到 container 的<b>相同</b>路徑檔案內容
- volume : container 的檔案內容存到 host 的 docker area 當中(通常會在 `/var/local`)
- ex.
```conf=
volumes:
- type: bind
source: /var/mosquitto/mosquitto.conf
target: /mosquitto/config/mosquitto.conf
- type: bind
source: /var/mosquitto/log/
target: /mosquitto/log/
- type: volume
source: data
target: /mosquitto/data/
```