---
# System prepended metadata

title: Linux

---

# 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` : 確認設定
        - ![image](https://hackmd.io/_uploads/SyaTO8cx1g.png)
- 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開發適合儲存海量小文件
### 架構
- ![image](https://hackmd.io/_uploads/S1MABv9lkl.png)
- 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
        - ![](https://i.imgur.com/vHbdcoy.png)
    - 覆寫 : `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
        - ![](https://hackmd.io/_uploads/rkJQGegG6.png)

- 移除目前使用者
    - 先查看 : `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`
        - ![image](https://hackmd.io/_uploads/Bk5mxjFexe.png)
        - 選擇自己要的版本

- 有 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
                - ![](https://i.imgur.com/cC3CPVm.png)
            - `sudo systemctl reload apache2`
            - 可能會因為 wordpress 更新版本而設定檔跑掉就又要重調
        - 應該是根本解 
            - `sudo vi /var/www/intern/wp-config.php`
            - 新增 : `define('WP_MEMORY_LIMIT', '512M');`
                - ![](https://hackmd.io/_uploads/rkaOGwVr3.png)
            - `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. 
            - ![](https://hackmd.io/_uploads/S1Xd-kU_n.png)
            - `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/
        ```

