Debian/Ubuntu 自動更新

要如何設定 Debian/Ubuntu 自動做更新呢?這可以使用 dpkg-reconfigure -plow unattended-upgrade 來設定

sudo dpkg-reconfigure -plow unattended-upgrades

執行指令後,會跳出視窗詢問是否要自動更新,選擇 Yes 就可以了。執行以後,會更新 /etc/apt/apt.conf.d/20auto-upgrades 這個檔案的內容

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";

如果有不想自動更新的套件,可以用黑白名單來設置,這邊可以參考 /etc/apt/apt.conf.d/50unattended-upgrades 檔案裡的範例來設定。

有些套件在更新後,會需要重新開機,unattended-upgrade 也可以做到自動重開機,主要是在 /etc/apt/apt.conf.d/20auto-upgrades 加入以下設定

//Unattended-Upgrade::Automatic-Reboot "false";
//Unattended-Upgrade::Automatic-Reboot-WithUsers "true";
//Unattended-Upgrade::Automatic-Reboot-Time "02:00";

除了可以指定是否自動重開機外,也可以指定自動重開機的時間。

了解 Debian/Ubuntu 如何自動更新以後,相信在管理主機上會更為輕鬆。

參考資料

RHEL8/CentOS8 grubby

上週幫客戶處理問題時,對 RHEL8/CentOS8 的 grub 又多了解了一些,記錄一下。

Ubuntu 的選單是放在 /boot/grub/grub.cfg ,所以看這個檔案,就大略知道選單有哪些選項,修改可以直接改這個檔案。RHEL8 跟 CentOS8 使用 grubby 來管理開機時的 grub 選單,所以可以用 grubby 指令來做管理。

常用的指令可以參考 12 practical grubby command examples (cheat sheet)

我這次主要使用到的指令有

  • 列出開機選單項目的資訊
  • 修改該選單項目的 kernel options
  • 取得目前預設開機項目
  • 設定預設開機項目

列出開機選單項目的資訊

列出選單項目,可以用下面指令

grubby --info=ALL | grep -E "^kernel|^index"

取得 index 跟 kernel ,就可以修改指定項目的 kernel options 了。

修改指定選單項目的 kernel options

修改的指令如下

# 修改指定的 kernel 的 kernel options,路徑可參考上面指令
grubby --update-kernel=<kernel路徑> --args="ipv6.disable=1"
# 修改目前的
grubby --update-kernel=/boot/vmlinuz-$(uname -r) --args="ipv6.disable=1"

若要移除 kernel options,要用 –remove-args

# 移除指定的 kernel 的 kernel options
grubby --update-kernel=<kernel路徑> --remove-args="ipv6.disable=1"
# 移除目前 kernel 的 kernel options
grubby --update-kernel=/boot/vmlinuz-$(uname -r) --remove-args="ipv6.disable=1"

這裡的修改,會影響到 /boot/loader/entries 目錄下的檔案。

取得目前預設開機項目

grubby --default-index
grubby --default-title

設定預設開機項目

用以下指令設定

grubby --set-default="/boot/vmlinuz-4.18.0-193.1.2.el8_2.x86_64"
grubby --set-default-index=2

這裡設置以後,會影響到 /boot/grub/grubenv 的檔案內容。

可惜 ubuntu 沒有 grubby,感覺這在管理開機選單上還蠻方便的。

archlinux keyserver

昨晚用 yay 更新套件時,出現 gnupg key 問題,說套件無法驗證,試著用下面指令去更新 key 也有問題。

sudo pacman-key --refresh-keys

後來查到這篇:gpg – sks-keyservers gone. What to use instead? – Unix & Linux Stack Exchange

文章說可能是 keyserver 問題,就去改 /etc/pacman.d/gnupg/gpg.conf 裡的 keyserver,改了還是有問題。

最後才找到這篇:PSA: you need to update your keyserver : archlinux ,提到不是只改一個地方就好,以下這幾個位置的設定檔都要改。

  • /etc/pacman.d/gnupg/gpg.conf
  • ~/.gnupg/gpg.conf

我後來是修改為 keyserver hkp://keyserver.ubuntu.com ,經過這樣修改以後,就可以順利更新 key 跟套件了。

好用的新Python函式庫

It’s Time to Say Goodbye to These Obsolete Python Libraries 看到的幾個好用的新函式庫跟新功能,雖說用新的怕舊的 Python 不能用,但這些新的函式庫或新功能其實也出蠻長一段時間了,現在大部分的 Linux 發行版也都有跟到。

摘錄如下

Pathlib

處理路徑的函式庫,直接看看這幾種用法,就能體會,比起之前的 os.path 直覺太多。

from pathlib import Path

readme = Path("README.md").resolve()

print(f"Absolute path: {readme.absolute()}")
# Absolute path: /home/martin/some/path/README.md
print(f"File name: {readme.name}")
# File name: README.md
print(f"Path root: {readme.root}")
# Path root: /
print(f"Parent directory: {readme.parent}")
# Parent directory: /home/martin/some/path
print(f"File extension: {readme.suffix}")
# File extension: .md
print(f"Is it absolute: {readme.is_absolute()}")

etc = Path('/etc')

joined = etc / "cron.d" / "anacron"
print(f"Exists? - {joined.exists()}")

Secrets

處理密碼的函式庫,簡單很多,而且語意很清楚。

import secrets

value = secrets.token_bytes(length)
value = secrets.token_hex(length)

dataclass

若之前有使用過 named tuple 的話,會覺得這個函式庫很好用,而且語意更清楚。

from dataclasses import dataclass

@dataclass()
class User:
    name: str
    surname: str

u = User("John", "Doe")

f-string

Python 從最早期的 “%s” % (name, ) 再到 “{}”.format(name) ,再到 f-string,f-string 讓格式化字串更為清楚,也不容易搞錯。

name = "John"
print(f"Hello {name}!")

結論

定期升級語法,可以帶來效能提升,也可以讓程式更具可讀性,是蠻值得安排時間去逐步更新的。

同場加映:Python 3.11–5個新功能 – anfinance – Medium

bash 檢查檔案是否為空的

我忘記為什麼會要找這個,總之,要檢查檔案不是空的,可以用 -s 。

這用 man test 可以查到, -s 的說明是這樣的:True if file exists and has a size greater than zero.

當檔案存在而且長度大於 0 時,回傳 True。換言之,False 時,就代表檔案的長度是 0 (空的)。

我是在 StackOverflow 找到的:linux – How to check if a file is empty in Bash? – Stack Overflow

if [ -s diff.txt ]; then
        # The file is not-empty.
        rm -f empty.txt
        touch full.txt
else
        # The file is empty.
        rm -f full.txt
        touch empty.txt
fi

近日學到的幾個 awk 用法

傳遞環境變數到 awk script 裡

之前傻傻的以為在 awk script 裡用 ${var} 就可以使用環境變數,但實測結果是不行的,後來才找到 Can we use shell variable in awk?

使用方法很簡單,執行 awk 時用 -v var=${env_var} ,在 awk script 裡就可以使用 var 變數來取得環境變數。

echo "" | awk -v HOME=${HOME} '{print HOME}'

找出指定欄位符合條件的列

想找出其中一欄符合條件的所有列,又不想用 grep,找到這篇 Using awk with column value conditions 跟這篇 AWK 判斷有符合的 字串 或 條件 再印出資料,就直接用指定欄位的變數來判斷就可以,例如 $1

awk '$1 == "findtext" {print $0}' input.txt

忽略註解

下面範例是忽略註解,計算行數的,從 AWK: is there some flag to ignore comments? 看來的

awk '/^[[:space:]]*#/ { NR-- } {sum+=$3} END { ... }' coriolis_data
awk '{ if ($0 ~ /^[[:space:]]*#/) {NR--} else {sum+=$3} END { ... }' coriolis_data

vim基本款設定與常用指令、按鍵整理

去客戶那邊的時候,vim 設定一定都要重來過的,如何在最快的時間設定成自己想要的樣子?

目前自己的基本設定

set et
set ts=2
set sw=2
set autoindent
set nu
colorscheme desert

這樣就足夠了,太多也記不住。

再來是常用指令

指令說明
:set wrap超過螢幕寬度就換行
:set nowrap超過螢幕寬度不換行
/\ccopyright
:set ignorecase
搜尋不分大小寫,可參閱 :help ignorecase:help smartcase
:set nu顯示行號
:set nu!隱藏行號
:tabedit filename在新頁籤開啟指定檔案
:tabnew開新頁籤
:ls列出所有開啟的檔案 (buffer)
:b1切到第一個檔案 (buffer)
:set paste設定為貼上,不要管 auto indent
:set nopaste不要設定為貼上,這樣若有 autoindent 時,會自動縮排
:wq最重要的指令,存檔然後離開。若加上 ! 表示強制存檔後離開。
:set mouse= 或 :set mouse=r 或 :set mouse=v 設定不要為 a 就可以使用標準系統的選取、複製模式

常用按鍵

Mode按鍵說明
Normalctrl+w s分割畫面-橫切
Normalctrl+w v分割畫面-直切
Normalctrl+w c關閉當前窗格
Normalctrl+w j / ctrl+w k / ctrl+w h / ctrl+w l移到下面/上面/左邊/右邊的窗格
Normalgt到下個頁籤
NormalgT到上個頁籤
Insertctrl+x ctrl+p表示往前找有輸入過的來作自動完成
Insertctrl+x ctrl+f自動完成檔名

參考資料

更新sudoers檔案內容

一般更新 /etc/sudoers 檔案內容都是使用 visudo 指令進行;若是要管理 sudoer ,Ansible 可以用 community.general.sudoers 模組來管理。

那如果要在腳本裡更新,該怎麼做呢?這時候可以參考 How do I edit /etc/sudoers from a script? 裡的作法來進行。

這作法蠻 tricky 的,主要是利用 EDITOR 環境變數把 visudo 預設的編輯器換為 tee -a ,這樣就不會開啟 vi 編輯器,而會改用 tee 來處理。來看看這行指令

echo 'foobar ALL=(ALL:ALL) ALL' | sudo EDITOR='tee -a' visudo
  1. | 的左邊是很單純的 echo,這裡可以放置要放進去的設定
  2. | 的右邊就是 sudo 跟 visudo ,這是用 sudo 執行 visudo 指令,並且把編輯器換為 tee -a

這樣組合以後,就可以把設定放到 /etc/sudoers 裏面去了。

另外一種比較單純的作法就是在 /etc/sudoers.d 目錄下去新增檔案,新增以後,因為 /etc/sudoers 裏面有寫 #includedir /etc/sudoers.d ,所以會引用到 /etc/sudoers.d 目錄下的檔案。

如何建立YUM mirror server

在企業裡,因為不會讓所有伺服器都對外,一般會在內部架設一台 YUM mirror server ,定期去 mirror 外部的 RHEL 套件庫。

坦白說,我不知道用哪個名字比較好,知道的名字有

  • Local mirror
  • YUM repository server
  • YUM mirror server

總之,用途都是一致的。

作法

首先這台要作為 mirror 的伺服器必須要對外,接著在伺服器裡,以 subscription-manager 註冊到 RHEL 服務去。

安裝做 mirror 的套件,這邊選用 apache,也可以用 nginx,路徑跟設定調整一下就可以。

yum install yum-utils createrepo httpd

使用 subscription-manager 查看可以下載的 repository

subscription-manager repos --list

接著對需要做 mirror 的 repository 去做 mirror

mkdir -p /var/www/html/{baseos,appstream}
reposync -p /var/www/html/baseos \
        --download-metadata \
        --repo=rhel-8-for-x86_64-baseos-rpms \
        --delete \
        --downloadcomps

reposync -p /var/www/html/appstream \
        --download-metadata \
        --repo=rhel-8-for-x86_64-appstream-rpms \
        --delete \
        --downloadcomps

這邊可以寫成一個腳本,然後設定 cron,定期去執行 mirror。

一般會再多寫一個 repo 檔案,放在 /var/www/html ,後續到其他主機時,可以直接下載這個 repo 檔案,放到 /etc/yum.repos.d 裡面,就可以直接 yum update 。

[baseos]
name=baseos
baseurl=http://<ip>/rhel-8-for-x86_64-baseos-rpms/
enabled=1
gpgcheck=0

[appstream]
name=appstream
baseurl=http://<ip>/rhel-8-for-x86_64-appstream-rpms/
enabled=1
gpgcheck=0

最後啟用 httpd,就大功告成了。

systemctl enable --now httpd

限制

使用這個方法,基本上一台主機就是對應一個版本,例如 7.9, 8, 8.5, 8.6 這樣,沒辦法有多個版本,若需要有多個版本,建議使用 Foreman 或是 Satellite 會比較方便。

參考資料

bash arguments cheatsheet

記錄關於 bash argument 的幾則常用語法。

argument 一般翻譯為引數或參數,帶有 — 的,會被稱為選項。

prog arg1 arg2 arg3
prog --output option1 arg1

arg1, arg2, arg3 就是引數;–output option1 就是選項。

寫 shell script 時,是不管 — 的,在 prog 之後的,都叫做引數。以下用法均以 prog arg1 arg2 arg3 作為範例

用法說明結果
$#取得引數個數3
$@所有引數arg1 arg2 arg3
myArray=(“$@”)轉為 Array 會印出 arg1,要遍訪,需用 for:
myArray=(“$@”)
echo “${myArray}”
只印出Array第一個元素arg1
for arg in “${myArray[@]}”; do echo -n “${arg},”; done遍訪Array所有元素arg1,arg2,arg3,
myArray=( “$@” )
arraylength=${#myArray[@]}
for (( i=0; i<${arraylength}; i++ ));
do
echo “${myArray[$i]}”
done
遍訪Array所有元素的另一個作法,用索引方式。arg1
arg2
arg3
if ["$#" -eq 0]; then echo "no argument"; fi判斷是有否引數若無引數,印出 “no argument”
B=(“${myArray[@]:1:2}”)
echo “${B[@]}”
做slicearg2 arg3
version=4.7.1
A=( ${version//./ } )
echo “${A[@]}”
split4 7 1
a=”HELLO WORLD”
echo "${a,,}"
echo “${a,,[AEIUO]}”
轉小寫hello world
HeLLo WoRLD
a=”hello world”
echo "${a^^}"
轉大寫HELLO WORLD

參考資料