﻿# Ubuntu 20.04 LTS 伺服器如何調整網路介面設定從 DHCP 變更為固定 IP 地址

Ubuntu 20.04 LTS 伺服器預設採用 [Netplan](https://netplan.io/) 作為網路設定的工具，但是要變更網路介面設定實在是有點麻煩，沒找到有好用的 TUI (Terminal UI) 工具可以用。我想透過這篇文章記錄一下變更 IP 設定的過程。如果有不同的網路情境導致設定步驟不同，我會陸續補充到這篇文章裡。

### 從 DHCP 取得動態 IP 地址變更為固定 IP 地址

1.  先透過 `ip add show` (`ip a`) 取得當前 IP 地址
    
    你可以從下方的 `eth0` 看到一個 `dynamic` 字眼，這代表這個 `eth0` 介面是從 DHCP 伺服器取得 IP 地址：
    
    ```
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
          valid_lft forever preferred_lft forever
        inet6 ::1/128 scope host
          valid_lft forever preferred_lft forever
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
        link/ether 00:16:2d:02:0a:32 brd ff:ff:ff:ff:ff:ff
        inet 192.168.1.11/24 brd 192.168.1.255 scope global dynamic eth0
          valid_lft 83597sec preferred_lft 83597sec
        inet6 fe80::215:5dff:fe01:a32/64 scope link
          valid_lft forever preferred_lft forever
    ```
    
2.  你可以用以下命令擷取當前的網路資訊：
    
    ```bash
    sudo apt install net-tools -y
    ```
    
    ```bash
    # 如果 VM 只有一張網路介面可以這樣取得網路介面名稱 (eth0)
    NetworkInterface=$(ifconfig | grep 'flags' | cut -d ':' -f 1 | grep -v 'lo')
    
    # 取得當前 IP
    CurrentIP=$(ip a | grep inet | grep $NetworkInterface | sed -e 's/^[[:space:]]*//' | cut -d ' ' -f 2 | cut -d '/' -f 1)
    # 取得當前 Netmask
    CurrentNM=$(ip a | grep inet | grep $NetworkInterface | sed -e 's/^[[:space:]]*//' | cut -d ' ' -f 2 | cut -d '/' -f 2)
    # 取得當前 Gateway
    CurrentGW=$(ip r | grep 'default' | sed -e 's/^.* via //' | cut -d ' ' -f 1)
    # 取得當前 DNS 資訊 (systemd-resolve --status | grep -B 9 -A 6 "Current DNS Server")
    CurrentDNS=$(systemd-resolve --status | grep -A 6 "Current DNS Server" | sed 1d | sed -e 's/^[[:space:]]*//' | sed -e 's/[[:space:]]*$//' | sed -e 's/^DNS Servers: //' | tr '\n' ',' | sed -e 's/,$//')
    ```
    
    > 我這裡用了許多 Linux 常見工具(`grep`, `sed`, `cut`, `tr`)的操作技巧，細節請自行查閱 man page 說明。
    
3.  直接拿現有的網路介面設定轉換成固定 IP 的設定值，並直接套用更新
    
    當你透過 [subiquity](https://github.com/canonical/subiquity) (Ubuntu Server Installer) 安裝 OS 的過程，如果是透過 DHCP 伺服器取得 IP 的話，預設 [cloud-init](https://cloud-init.io/) 可能會用來管理伺服器的 IP 設定，我們要先確認目前主機沒有透過 `cloud-init` 管理網路介面(network interface)。
    
    ```bash
    NewIP=$CurrentIP
    NewGW=$CurrentGW
    NewNM=$CurrentNM
    NewNS=$CurrentDNS
    
    # 確認沒有透過 cloud-init 管理網路介面(network interface)
    grep 'network: {config: disabled}' /etc/cloud/cloud.cfg.d/subiquity-disable-cloudinit-networking.cfg
    if [ "$?" = "0" ]; then
    cat <<EOF | sudo tee /etc/netplan/00-installer-config.yaml > /dev/null
    network:
      ethernets:
        $NetworkInterface:
          addresses: [$NewIP/$NewNM]
          gateway4: $NewGW
          nameservers:
            addresses: [$NewNS]
      version: 2
    EOF
    fi
    
    sudo netplan apply
    ```
    
4.  如果要設定為全新的網路設定，可以參考以下設定：
    
    ```bash
    NetworkInterface=$(ifconfig | grep 'flags' | cut -d ':' -f 1 | grep -v 'lo')
    NewIP='192.168.1.201'
    NewGW='192.168.1.1'
    NewNM='24'
    NewNS='168.95.1.1,8.8.8.8'
    
    # 確認沒有透過 cloud-init 管理網路介面(network interface)
    grep 'network: {config: disabled}' /etc/cloud/cloud.cfg.d/subiquity-disable-cloudinit-networking.cfg
    if [ "$?" = "0" ]; then
    cat <<EOF | sudo tee /etc/netplan/00-installer-config.yaml > /dev/null
    network:
      ethernets:
        $NetworkInterface:
          addresses: [$NewIP/$NewNM]
          gateway4: $NewGW
          nameservers:
            addresses: [$NewNS]
      version: 2
    EOF
    fi
    ```
    
    如果 IP 發生變更，會導致目前的 SSH 遠端連線被中斷，所以建議在 Console 執行這個命令！
    
    ```bash
    sudo netplan apply
    ```
    
    如果 IP 沒有發生變更，你也可以利用利用 `sudo netplan try` 命令，先測試一下網路設定是否套用後還可以正常連線，如果在 `--timeout` 指定的秒數內沒有人按下 Enter，那就代表網路已經斷線了，此時 netplan 會自動復原網路設定，讓你可以繼續用原本的網路設定進行連線，以免遠端再也連不上該主機。
    
    ```bash
    sudo netplan try --timeout 3
    ```
    

### 從固定 IP 地址轉為 DHCP 伺服器取得動態 IP 地址

1.  直接將網路介面設定轉換成從 DHCP 伺服器動態取得 IP 地址
    
    ```bash
    # 如果 VM 只有一張網路介面可以這樣取得網路介面名稱 (eth0)
    NetworkInterface=$(ifconfig | grep 'flags' | cut -d ':' -f 1 | grep -v 'lo')
    
    # 確認沒有透過 cloud-init 管理網路介面(network interface)
    grep 'network: {config: disabled}' /etc/cloud/cloud.cfg.d/subiquity-disable-cloudinit-networking.cfg
    if [ "$?" = "0" ]; then
    cat <<EOF | sudo tee /etc/netplan/00-installer-config.yaml > /dev/null
    # This is the network config written by 'subiquity'
    network:
      ethernets:
        $NetworkInterface:
          dhcp4: true
      version: 2
    EOF
    ```
    
2.  套用 netplan 更新
    
    如果 IP 發生變更，會導致目前的連線被中斷，所以建議在 Console 執行這個命令：
    
    ```bash
    sudo netplan apply
    ```
    
3.  取得新取得的 IP 地址
    
    ```bash
    ifconfig 'eth0' | grep 'inet '
    ```
    

### 相關連結

-   [Netplan | Backend-agnostic network configuration in YAML](https://netplan.io/)
-   [cloud-init - The standard for customising cloud instances](https://cloud-init.io/)
-   [How to Assign Static IP Address on Ubuntu 20.04 LTS](https://www.linuxtechi.com/assign-static-ip-address-ubuntu-20-04-lts/)
-   [How to Configure Static IP Address on Ubuntu 20.04](https://linuxize.com/post/how-to-configure-static-ip-address-on-ubuntu-20-04/)
