Skip to main content

· 2 min read
zaxro

How Gcp IAM works

透過授予主體(principal)相關 role(對特定 resource 有操作權限)來執行操作(開 VM,調安全組規則等),而以上的 role binding 綁定關係被稱為 Policy. 在 GCP 實體如以下

  • Google Account: 例如examplefordtest@gmail.com
  • Service account: 給程式用的,要額外驗證
  • Google group: 透過群組管理多個 google account,方便管理用,但不能驗證個人訊息.
  • Google Workspace account:指組織或企業使用 Google Workspace 的帳戶。每個 Google Workspace 帳戶都與組織的域名相關聯
  • Cloud Identity domain:組織 sso 用來做驗證成員身份用
  • All authenticated users
  • All users

跟 aws 差別在於 aws 沒有特別結合 gmail,然後 gcp 設計的管理架構是 organization-> Folder->Project->resources, 在每一個 Project 的 IAM 全限對 resource 是獨立的,在 aws 部分,則是透過 tag 或者正則表示法在 policy 那邊限制使用者對特定資源的操作.

參考資料

GCP 官網 IAM 介紹

· 5 min read
zaxro

以下資訊是當前查詢資訊並比對其他人的做法,列出覺得不錯的設定方式,之後如果發現錯誤或更好的方式會滾動修改!

主要邏輯就是會先: 建立系統帳號 -> 限制資料夾權限及owner -> 建立service設定檔 -> 透過systemctl託管該service.

  • 建立系統帳號,並不給家目錄且不能登terminal
useradd --no-create-home --shell /bin/false your_target_user
  • 修改指定資料夾owner給該user,且注意是否要給予可寫權限(這邊以promethues需要寫入的資料庫為例),然後要知道安裝東西後會產生哪些東西,再去分配,如果是用yum,apt安裝會幫你安排好並設定,如果是上網自己下載的檔案包就需要再自己去分配位置,以及權限!
chown -R prometheus:prometheus  /var/lib/prometheus
  • 建立service設定檔
/usr/lib/systemd/system/prometheus.service
[Unit]
Description=Monitoring system and time series database
Documentation=https://prometheus.io/docs/introduction/overview/ man:prometheus(1)
After=time-sync.target

[Service]
Type=simple
Restart=on-failure
User=prometheus
Group=prometheus

EnvironmentFile=/etc/default/prometheus
ExecStart=ExecStart=/usr/local/bin/prometheus \
--config.file /etc/prometheus/prometheus.yml \
--storage.tsdb.path /var/lib/prometheus/ \
--web.console.templates=/etc/prometheus/consoles \
--web.console.libraries=/etc/prometheus/console_libraries \
--web.enable-admin-api \
--storage.tsdb.retention.time=30d \
--web.enable-lifecycle \
ExecReload=/bin/kill -HUP $MAINPID
TimeoutStopSec=20s
SendSIGKILL=no
[Install]
WantedBy=multi-user.target

Type預設simple,由ExecStart 接的指令啟動服務並常駐在記憶體!

tip

之前在設定service時候都是設定Type=simple,不過自從在rsync設定daemon服務踢到鐵板就知道必須了解差異,如果程式執行的模式是父process產生子process之後就關閉父process,例如: rsync daemon程式會產生子process作為主要執行process(同理還有nginx這類服務),就必須用Type=forking,否則systemctl會把它當作主程式退出,之後就會hang住然後回報failure狀態. rsync server的service設定檔如下.

/usr/lib/systemd/system/rsyncd.service
[Unit]
Description=rsyncd Server
After=network.target

[Service]
Type=forking

ExecStart=/bin/rsync --daemon --config=/etc/rsyncd.conf
TimeoutStopSec=20s


[Install]
WantedBy=multi-user.target

至於為啥會try出這段,是因為在把它改為forking就成功了,仔細看服務會發現

    Process: 2740 ExecStart=/usr/bin/rsync --daemon --config=/etc/rsyncd.conf (code=exited, status=0/SUCCE>
Main PID: 2741 (rsync)

也就是PID不同的部分,所以要用forking.

Restart此選項決定當服務遭遇錯誤或崩潰時是否自動重新啟動服務。根據設定不同,Systemd 會在不同情況下執行重新啟動。例如,如果設定為 on-failure,表示只有在服務失敗時才會自動重啟。

User Group這個選項確定服務以哪個使用者及群處身份運行。當設定為 prometheus 時,服務將以 prometheus 使用者的身份運行。

EnvironmentFile這個選項用來指定環境變數的設定檔位置。設定檔內包含多個環境變數的定義,這些變數將在服務運行時生效。可以拿來放$ARGS

ExecStart這個選項確定啟動服務所需的命令和參數。當 Systemd 啟動這個服務時,會執行指定的命令。

ExecReload此選項定義重新載入服務所需的命令。當 Systemd 收到重新載入的信號(通常是 systemctl reload 命令),會執行指定的命令。因為prometheus提供熱加載可以透過kill HUP PID,但不代表其他service有~

TimeoutStopSec若這個服務在啟動或者是關閉時,因為某些緣故導致無法順利『正常啟動或正常結束』的情況下,則我們要等多久才會使用SIGKILL信號殺掉剩下進程!

SendSIGKILL這個選項確定停止服務時是否嘗試使用 SIGKILL 信號。如果設定為 no,Systemd 將儘可能避免使用 SIGKILL 信號,以允許服務有時間進行清理操作。

· 2 min read
zaxro

app-development-and-delivery-with-cloud-code-gcb-cd-and-gke.svg

Cloud Build在這個lab會做到

  • Use Build trigger to monitor changes to the source repo.
  • When a change is committed into main branch, Cloud build trigger following:
    • Build artifacts in cloud storage bucket
    • Run test on the container
    • Place the app image in Artifact Registry
    • Call Cloud deploy to deploy the container to the staging environment.
  • If all the build and tests are succesful, you can use Cloud Deploy to promote the container from staging to production.

Cloud deploy會註冊並管理pipline跟targets(指的是staging and production cluster).

這邊整理這個lab的建構過程,詳細步驟可以依照官網資源做學習.

官網大致流程:

  1. 設定預設專案.會用到的指令gcloud config set project PROJECT_ID,可以用gcloud config get-value project檢查目前預設專案,查詢有哪些專案可以用gcloud projects list

後續的lab會拿我自己寫的程式,它原本是部署在docker上,會把它改成gke版本,config的放入會試試看config map跟 headless service等東西!

使用cloud deploy執行環境

  1. cloud build worker pool 會讓deploy在該池中執行部署及驗證
  2. cloud deploy需調用執行操作的帳號
  3. cloud storage 的清單存放位置

· 3 min read
zaxro

在日常運用上,一定會用到很多變數,迴圈,基本上會希望程式語言能做到的事情,在ansible裡也有同樣的實現!

keyword

以下會羅列常用關鍵字:

  • with_items:相當於for迴圈打開list
  • loop:相當於for迴圈打開list(Ansible 2.5 開始已被推薦使用)
- name: Looping using loop (recommended approach)
debug:
msg: "Item: {{ item }}"
loop:
- item1
- item2
- item3
  • loop_control:loop可以配合loop_control做進一步設定,例如設定起始點,取index等動作!
- name: Loop control example
hosts: localhost
vars:
services:
- service1
- service2
- service3
tasks:
- name: Looping over services with custom loop variable name and index
debug:
msg: "Service: {{ custom_service_var }}, Index: {{ my_index }}"
loop: "{{ services }}"
loop_control:
loop_var: custom_service_var
index_var: my_index
  • when: 也就是if的用法
- name: Run a task based on a comparison
command: echo "This task will run if variable is greater than 10"
when: my_variable > 10
  • include 或 include_tasks:用於引用其他的任務文件。
  • import_tasks:用於導入其他的任務文件。
  • tags:用於標記任務,執行ansible-playboook可以指定tags
  • notify:配合handlers在狀態為change時作操作
  • become:用於指定是否切換到超級用戶(root)權限執行任務。
  • become_user: 由root切到哪個user執行,通常配合become一起!

module

以下包含建立變數,做特定事項

  • set_fact: 用於建立變數
- name: Set systemd version fact
set_fact:
node_exporter_systemd_version: "{{ __systemd_version.stdout_lines[0] | regex_replace('^systemd\\s(\\d+).*$', '\\1') }}"
  • register:用於捕獲命令的標準輸出及錯誤輸出然後放到某變數內!
- name: Get systemd version
command: systemctl --version
changed_when: false
check_mode: false
register: __systemd_version
tags:
- skip_ansible_lint
  • assert: 當條件符合會拋錯並終止playbook執行
- name: Check if a variable is greater than 10
assert:
that: my_variable > 10
fail_msg: "my_variable should be greater than 10"
  • file 模組:用於處理文件和目錄,例如創建、刪除、修改權限等操作。
  • find 模組:用於在遠程主機上搜索文件並返回結果。
  • sysctl 模組:用於設置系統內核參數,並在需要時重新載入。
  • stat 模組:用於檢查文件或目錄的狀態,確定它們是否存在以及相關屬性。
  • debug 模組:用於在執行期間輸出訊息,用於調試和檢查變數。
  • block 模組:用於定義一個區塊,其中一part失敗會全部失敗,可以搭配 rescue 和 always 來執行相應操作。

· 5 min read
zaxro

查詢介紹

ELK_query png

  • SPACES selection: 通常會依據產品跟環境名稱命名,請依據你要查詢的產品做選擇,例如:A1-UAT,B2-PROD等,數字1方框在Elastic的術語是space.
  • View selection: 這邊是依據模塊(應用程式)區分,透過往下拉開列表去選擇你可以選要看哪隻服務的log,也可以在展開列表中的查詢框直接search該服務名稱,ex.xxx-api,數字2方框在Elastic的術語是data view.
  • Field selection: 如果這邊留空,查詢結果會給你所有的field資料(例如hostname,路徑位置等等),如果只是單純想看某欄位,這邊可以輸入該欄位名稱然後按加號,這樣就只會顯示出這個field的資料!
  • Log查詢區域: 這邊是主要查詢log地方,透過K(kibana)QL語法去查詢,預設查詢沒有區分大小寫.

ELK_check_time png

  • Select time: 這邊是選擇時間,預設是用相對時間,也可以選擇時間段,ex.2023-8-8 09:00~2023-8-9 10:00.
  • Query: 以上都選定後,透過這格做查詢

KQL語法

Elastic查詢是透過某個data view去查底下的field,一個應用程式的日誌(data view)就像一張表,每個field就像是SQL的欄位,預設會看到很多field,例如:timestamp是標記時間,message標記訊息等.

查詢語法範例

查詢不分大小寫!

2023/08/10 08:42:50 [error] 26457#0: *404504 "/data/info/index.html" is not found (2: No such file or directory), client: 192.168.0.11, server: h5.alloha.com, request: "GET /index/index/info/ HTTP/1.0", host: "www.alloha.com", referrer: "https://www.alloha.com:443/index/index/info/"

以查詢以上為例

  • 查詢message field含有www關鍵字,注意KQL語法是用完全匹配,沒有完全匹配就不回,如果只記得關鍵字要加*
message:  *www*

如果你的關鍵字是中文,就不用加**,不然他會出不來

message:  路由
  • 查詢message field含有這兩個字眼info以及not found的句子(精確匹配)
message:  info AND message: "not found"
  • 查詢message field含有這兩個字眼info或not found的句子(精確匹配)
message:  info OR message: "not found"
  • 查詢時間在2023-08-10九點到十點間的log
@timestamp >= "2023-08-10T09" AND @timestamp <= "2023-08-10T10"

驗證

要確認index是否建立,最直觀可以去看index有沒有跑出來! 比較次之的方法,去看es_log_dir指定的log位置裏面的log-cluster.log他預設每天會壓縮當日的log一次.

如果沒發現有建立,那就要排查了,我一般都會做幾個方向排查

  1. 檢查filebeat之類轉發日誌的服務,他對elastic的網路有沒有通
  2. 檢查filebeat在/var/log/filebeat.log有沒有報錯,/var/lib/filebeat這邊的指針有沒有動(他原理是定時偵測檔案然後用指針方式標示他目前讀的位置,這樣才不會重複出去),如果發現指針根本沒動那一定是你日誌路徑錯了!
  3. 把以上打通,還是掛的就要去確認是不是elastich問題

ELK已知報錯

  • shard爆量報錯如下,預設每個node上限是建立1000個shard,爆了要做調整(這邊先不討論超過1000是否合理)
[ERROR][o.e.x.i.IndexLifecycleRunner] [elastic-1] policy [filebeat] for index [.xxxxxx] failed on step [{"phase":"hot","action":"rollover","name":"attempt-rollover"}]. Moving to ERROR step
org.elasticsearch.common.ValidationException: Validation Failed: 1: this action would add [2] shards, but this cluster currently has [2999]/[3000] maximum normal shards open;

解法就是去調整每個node上限shard

  • 記憶體不足報錯,調整jvm.options中的記憶體量!一般建議是用二分之一記憶體!

· 10 min read
zaxro

why k8s? It allows for easy scalability, high resilience, application portability, automation of many tasks, and standardization of the container platform

先看到以下這張圖,這是官方提供的k8s組成,你會注意到幾個關鍵元件:Kubernetes cluster,Control plan, Node.簡言之,kubernetes cluster由多個worker node(硬體資源)及control plane組成.

k8s-strucuture

Kubernetes基本運作: worker node會託管pod(以容器化建立的應用程式),而control plane會管理cluster內的worker node跟pod.

細部看,這幾個關鍵元件底下也是由多個元件組成.以下簡單介紹control plane,pod,woker node的組成.

control plane

控制平面對集群做出全局決策,並檢測及回應集群事件,例如:檢測到部署的pod不滿足deployment應部署的pod數量時,會起新pod.由kube-apiserver,etcd,kube-scheduler ,kube-controller-manager,cloud-controller-manager組成!

kube-apiserver

kube-apiserver是API伺服器,為 Kubernetes 控制平面的前端,主要負責公開k8s的api.從 Command Line 下 kubectl 指令就會把指令送到這裏,同時也是不同node之間溝通的轉介點.

etcd

etcd是key value存儲庫,用來放k8s的集群數據,這邊資料建議要有備份計畫

kube-scheduler

kube-scheduler它監視新創建的未分配節點的 Pod,並為它們選擇一個節點來運行,這個選擇過程可以與資源需求、硬件/軟件/策略約束、親和性和反親和性規範、數據本地性、工作負載之間的干擾以及截止期限等有關!

kube-controller-manager

kube-controller-manager運行控制器進程,每個控制器是獨立process,這些 Process 會在 Cluster 與預期狀態(desire state)不符時嘗試更新現有狀態(current state).控制器有許多不同類型:

  1. Node 控制器:負責在節點關閉時通知並響應。
  2. Job 控制器:監視代表一次性任務的 Job 對象,然後創建 Pods 來執行這些任務直到完成。
  3. EndpointSlice 控制器:填充 EndpointSlice 對象(用於提供服務和 Pods 之間的鏈接)。
  4. ServiceAccount 控制器:為新的命名空間創建默認的 ServiceAccount。 除以上還有很多其他類型的控制器!

cloud-controller-manager

cloud-controller-manager內置了特定於雲端的控制邏,如果有用到雲端服務才會用到它,cloud-controller-manager 允許將集群與雲提供商的 API 連接起來,並將與該雲平台交互的組件從僅與您的集群交互的組件中分離出來。

pod

一個 Pod 對應到一個應用服務,一班情況最好一個pod一個container,而每個 Pod 都有一個身分證,也就是屬於這個 Pod 的 yaml 檔.同pod的container彼此可以透過port溝通.

worker node

每個 Node 中有這幾個組件kubelet、kube-proxy、Container runtime

kubelet

每個工作節點上運行的 kubelet 首先會註冊到control plane,接著它負責與control plane溝通,管理pod狀態並確保容器在 Pod 內運行.主要透過各種機制提供一組PodSpecs並確保PodSpecs 中描述的容器運行並保持健康。

kube-proxy

kube-proxy 在 Kubernetes 集群中做網路管理,負責維護節點間的通訊和更新 iptables 規則,以確保服務和 Pod 的正確運行和通信。同時,它也促使節點間的資訊同步,使得 Kubernetes 中的各個組件可以準確地獲取節點上所有 Pods 的狀態。

Container Runtime

容器運行時是負責運行容器的軟體。

部署服務過程

k8s如何部署服務?

簡單過程: 透過指令建立deployment,control plane會依據deployment設定出需求的pod.

較詳細過程於以下:

  1. 使用 kubectl 命令建立deployment資源,過程會將 YAML 資源文件轉換為 JSON 格式,然後將其發送到位於控制平面內的 API Server。

  2. API Server 收到請求後,將部署的詳細信息或對象定義持久化到etcd數據庫中。

  3. 當 etcd 存儲中添加了新的 Deployment 資源後,API Server 會將相關的事件通知controller manager,controller manager創建足夠的 Pod 資源以匹配 Deployment 中的副本數量。此時,在 etcd 中的部署狀態為「Pending」。

  4. Scheduler檢測到「Pending」狀態後,會檢查基礎設施的狀態,對節點進行篩選和排序,以決定在哪個節點上建立 Pod。然後,Scheduler根據其業務邏輯填充 Pod 的 Spec 中的「nodeName」字段,並將已調度的 Pod 對象以「Scheduled」狀態持久化到 etcd 中.

  5. 一旦Pod被調度到Wokernode上,kubelet 會負責創建Pod中定義的容器。

  6. Pod 調度: 當 kubelet 從 API Server 獲取到新 Pod 的規範後,它會檢查該規範,然後使用container runtime(如 Docker)創建相應的container。kubelet 會監控容器的運行狀態,並在需要時重新啟動容器。

  7. 健康檢查: kubelet 會執行就緒探針(readiness probe)和存活探針(liveness probe),以確定容器是否就緒並保持運行狀態。如果探針指示容器不健康,kubelet 可能會重新啟動該容器。

  8. 報告狀態: kubelet 會定期向 API Server 報告它所管理的 Pod 的狀態。這包括該 Pod 是否正常運行、就緒狀態等信息。API Server 使用這些狀態信息來更新control plane的資源狀態,無問題Pod 的狀態變為「Running」,Deployment 的狀態變為「Ready」.

pod間的溝通

比較大型的程式會有很多個pod,他們之間要如何傳遞資訊? 在VM用的是固定的ip跟port,而在k8s因為會牽涉到自動擴展,用ip跟port去溝通顯然會遇到找不到ip跟port的問題. 所以k8s發展了抽象層service來抽象化這個過程,並透過kube-proxy做實際的流量轉發.

service類型

服務可以使用不同的類型,如 ClusterIP、NodePort、LoadBalancer 和 ExternalName。這些服務類型提供了不同的方式來實現 Pod 間和集群內外的通訊。

  • ClusterIP: 默認的服務類型,將 Pod 部署到集群內部,並使用集群內部的 IP 地址進行通訊。這對於集群內部的 Pod 之間的通訊非常有用。

  • NodePort: 將一個服務公開到集群節點的某個端口上,並使用該節點的 IP 和該端口進行訪問。Kubernetes 會在每個節點上打開一個高隨機端口(通常在範圍 30000-32767),並將這個端口映射到你指定的服務的目標端口, 當有多個服務使用 NodePort 且指定相同的高隨機端口時。這可能會導致一些服務衝突,或者不正確地路由到其他服務上。

  • LoadBalancer: 在支援的雲提供商上,可以創建外部負載均衡器,並將其配置到服務上。這使得外部流量可以通過負載均衡器轉發到服務內的 Pod。

  • Ingress: Ingress 是一個控制器,用於管理外部對集群中服務的訪問。通過 Ingress,你可以設定不同的路由和規則,並將流量導向到不同的 Service 或 Pod。Ingress 支援 SSL/TLS 加密、主機名和路徑的路由,是一個更高級的方式來管理外部訪問。

  • ExternalName: 通過 CNAME 記錄將服務名稱映射到外部的 DNS 名稱。這對於需要將內部服務與外部服務關聯起來的場景很有用。

小結

Kubernetes 部署程式需要透過 Deployment 創建 Pod 並管理與更新,而各個 Pod 之間的溝通通常透過 Service 提供穩定的 DNS 名稱和 IP 地址,讓 Pod 間可以使用標籤進行通訊,從而實現彈性、可靠的通訊機制。

· 8 min read
zaxro

基礎linux文件管理

inode(index node)是linux系統的一種資料結構,儲存了檔案系統的屬性跟磁碟位置.檔案系統的屬性有以下元數據訊息:

  • 文件類型
  • 文件權限
  • 文件所有者跟所屬組
  • 時間戳記
  • 文件大小
  • 硬鏈接數
  • 數據塊指針(文件系統內部機制)

可以用stat指令查詢.

stat output.xdddd

File: output.xdddd
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: ca01h/51713d Inode: 17058 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2023-08-09 02:11:13.675880181 +0000
Modify: 2023-08-08 11:10:17.005957501 +0000
Change: 2023-08-08 11:10:17.005957501 +0000
Birth: 2023-08-08 11:10:17.005957501 +0000

其中權限這邊,0644,開頭的0代表特殊權限,分別有0(無特別),1(Sticky Bit,只有文件所有者才能刪除文件),2(Setuid,以文件所有者權限執行),4(Setgid,共享群組相關),而後面的644代表分别表示文件所有者、所属组和其他用户的權限,分別是owner可讀可寫,所屬組可讀,其他人可讀.

ps. root 用戶擁有系統的最高權限,它基本上可以繞過文件權限限制,訪問和修改任何文件,除非特殊狀況如加密,selinux,特殊權限immtable

~# chattr  -i output.xdddd # 加注不能刪

~# lsattr output.xdddd
----i---------e------- output.xdddd

file指令看文件型態

~# file testpy.tar.gz
testpy.tar.gz: gzip compressed data, from Unix, original size modulo 2^32 10240
note

用戶在操作文件和目錄時,使用的是文件名,而文件系統則使用inode來跟踪和管理這些文件和目錄.

lab

這邊會測試把log刪除後新增同檔名的空白檔案,後續日誌是否會寫入!原因是因為之前遇到過不小心刪掉log,導致服務需重啟才能繼續寫入日誌的慘劇

  1. 以下指令會創建log,並每秒定時寫入,執行它
import time
import logging

# 配置日志记录
logging.basicConfig(filename="output.log", level=logging.INFO, format="%(asctime)s - %(message)s")

while True:
current_time = time.strftime("%Y-%m-%d %H:%M:%S")
line = f"This is a line of text at {current_time}"
logging.info(line)
time.sleep(1)
  1. 觀察進程開啟檔案狀態,這邊看到pid是46136,這邊會看到有描述符號指向/root/out.log
root@ip-10-0-0-83:~# ps -ef  |grep python3
root 432 1 0 Aug08 ? 00:00:00 /usr/bin/python3 /usr/bin/networkd-dispatcher --run-startup-triggers
root 541 1 0 Aug08 ? 00:00:00 /usr/bin/python3 /usr/share/unattended-upgrades/unattended-upgrade-shutdown --wait-for-signal
root 46136 45975 0 02:46 pts/1 00:00:00 python3 test.py
root 46224 46204 0 02:46 pts/3 00:00:00 grep --color=auto python3
root@ip-10-0-0-83:~# cd /proc/46136/fd/
root@ip-10-0-0-83:/proc/46136/fd# ll
total 0
dr-x------ 2 root root 0 Aug 9 02:47 ./
dr-xr-xr-x 9 root root 0 Aug 9 02:46 ../
lrwx------ 1 root root 64 Aug 9 02:47 0 -> /dev/pts/1
lrwx------ 1 root root 64 Aug 9 02:47 1 -> /dev/pts/1
lrwx------ 1 root root 64 Aug 9 02:47 2 -> /dev/pts/1
l-wx------ 1 root root 64 Aug 9 02:47 3 -> /root/output.log
  1. 接著我把檔案output.log刪除,並觀察進程開啟檔案狀態,發現狀態變deleted
root@ip-10-0-0-83:/proc/46136/fd# rm -f /root/output.log
root@ip-10-0-0-83:/proc/46136/fd# ll
total 0
dr-x------ 2 root root 0 Aug 9 02:47 ./
dr-xr-xr-x 9 root root 0 Aug 9 02:46 ../
lrwx------ 1 root root 64 Aug 9 02:47 0 -> /dev/pts/1
lrwx------ 1 root root 64 Aug 9 02:47 1 -> /dev/pts/1
lrwx------ 1 root root 64 Aug 9 02:47 2 -> /dev/pts/1
l-wx------ 1 root root 64 Aug 9 02:47 3 -> '/root/output.log (deleted)'
  1. 新增同檔名的空白檔,狀態依舊是deleted
root@ip-10-0-0-83:/proc/46136/fd# touch /root/output.log
root@ip-10-0-0-83:/proc/46136/fd# ll
total 0
dr-x------ 2 root root 0 Aug 9 02:47 ./
dr-xr-xr-x 9 root root 0 Aug 9 02:46 ../
lrwx------ 1 root root 64 Aug 9 02:47 0 -> /dev/pts/1
lrwx------ 1 root root 64 Aug 9 02:47 1 -> /dev/pts/1
lrwx------ 1 root root 64 Aug 9 02:47 2 -> /dev/pts/1
l-wx------ 1 root root 64 Aug 9 02:47 3 -> '/root/output.log (deleted)'

由以上得知,在logging紀錄log方式下,程式本身應該是認inode的.你把日誌刪除後,即使新增同樣檔名的空白檔,一樣不會寫入日誌!

logrotate

linux管理日誌系統避免其大小過大,並進行適當rotation的工具是logrotate,他也會遇到日誌檔名的修改問題!那是怎麽做到輪詢這件事?

當一個進程(例如 logrotate)對一個日誌文件進行輪轉時,通常會執行以下步驟,以確保新的日誌條目被寫入新的 inode 中:

  1. 關閉舊的文件描述符:在開始輪轉過程之前,進程會關閉舊的日誌文件的文件描述符。這確保進程不會繼續往舊文件中寫入新的日誌條目。

  2. 重命名或移動舊文件:進程會將舊的日誌文件重命名或移動到備份位置,以便創建一個新的空白文件用於寫入新的日誌。

  3. 創建新的文件:進程會創建一個新的空白文件,這個新文件將具有一個新的 inode。

  4. 打開新文件描述符:進程會打開新創建的文件的文件描述符,以便將新的日誌條目寫入其中。

  5. 寫入新的日誌條目:進程開始將新的日誌條目寫入新的文件描述符所指向的新文件(新 inode)中。

以下是lab測試過程

  1. 確認目前log inode值
root@ip-10-0-0-83:/var/log/nginx# ls -i
309494 access.log 309495 error.log
  1. 強制執行logrotate
root@ip-10-0-0-83:/var/log/nginx# logrotate -f  /etc/logrotate.d/nginx
  1. 確認同檔名error.log的inode值已改變(由309495->341209)
root@ip-10-0-0-83:/var/log/nginx# ls -i
309494 access.log 341209 error.log 309495 error.log.1
  1. 測試確認是否可以寫入log,這邊確認可以寫入
root@ip-10-0-0-83:/var/log/nginx# nginx
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Unknown error)
nginx: [emerg] bind() to [::]:80 failed (98: Unknown error)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Unknown error)
nginx: [emerg] bind() to [::]:80 failed (98: Unknown error)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Unknown error)
nginx: [emerg] bind() to [::]:80 failed (98: Unknown error)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Unknown error)
nginx: [emerg] bind() to [::]:80 failed (98: Unknown error)
root@ip-10-0-0-83:/var/log/nginx# ll
total 16
drwxr-xr-x 2 root adm 4096 Aug 9 03:21 ./
drwxrwxr-x 12 root syslog 4096 Aug 9 03:17 ../
-rw-r----- 1 www-data adm 0 Aug 9 03:17 access.log
-rw-r----- 1 www-data adm 700 Aug 9 03:23 error.log
-rw-r----- 1 www-data adm 603 Aug 9 03:17 error.log.1

· 4 min read
zaxro

安裝

  • 安裝acme.sh
curl https://get.acme.sh | sh

安裝完之後會在 root 目錄下產生一個 .acme.sh 資料夾,也可以從官方git hub 把東西拉下來.

  • set email
cp /root/.acme.sh/acme.sh /sbin/
acme.sh --register-account -m uremail@gmail.com
  • 設定cloudflare token 基本上acme支援很多家api申請,一般來說就用既有的就好.以cloudflare來說,需要產出的token是需要有能編輯dns紀錄的.

    • 先到該連結
    • 建立token
    • 編輯區域dns,客戶端 IP 位址篩選這邊可以限定可操作ip,區域資源可以選指定域名,注意權限需選DNS並授予編輯,token效期依據公司規範
    • 全部設定玩按下一步,然後複製token , ex R4O6rqd3hvGzXXXXXXXXXX
  • 回到主機使用一下指令產出憑證,如果你是用global key要用CF_Key用token則用CF_Token,建議用token限縮權限!

export  CF_Token="R4O6rqd3hvGzXXXXXXXXXX"
acme.sh --issue --dns dns_cf -d "*.948787.store" -d "948787.store"

如果你沒有用argument指令指定憑證產生的位置,他預設會跟著你acme.sh的位置產在同一層資料夾.這邊提供指定位置的指令!

/root/.acme.sh/acme.sh --issue --dns dns_cf -d "*.YourDomainName.com" -d "YourDomainName.com" --log \
--cert-file /etc/nginx/ssl/cert.crt \ # 複製憑證到指定的目錄下面
--key-file /etc/nginx/ssl/key.key \ # 複製憑證到指定的目錄下面
--fullchain-file /etc/nginx/ssl/fullchain.crt \ # 複製憑證到指定的目錄下面
--ca-file /etc/nginx/ssl/ca.crt \ # 複製憑證到指定的目錄下面
--reloadcmd "systemctl reload nginx" # 如果是用 Apache 請自己修改
  • 以我為例,就會在/root/.acme.sh拿到'*.948787.store_ecc'這個folder,folder結構如下
*.948787.store_ecc
├── *.948787.store.cer
├── *.948787.store.conf
├── *.948787.store.csr
├── *.948787.store.csr.conf
├── *.948787.store.key
├── ca.cer
└── fullchain.cer
  1. *.948787.store.cer是放我這個域名的公鑰
  2. *.948787.store.conf放acme.sh之後來renew的conf
  3. *.948787.store.csr加密文件,用以向CA申請憑證的申請單
  4. *.948787.store.key你的憑證的private key.
  5. ca.cer打開來裡面可能會有一個以上的憑證,分別是你的中繼憑證以及根憑證或者只有根憑證,這要依據簽署單位!
  6. fullchain.cer打開來裡面會至少有兩個以上憑證,分別是你憑證以及根憑證,或者也會有中繼憑證憑證,這要依據簽署單位!簡單來說,所有簽發過程都會放在這!

結論

透過acme.sh可以節省憑證申請的費用跟工時,近一步使用案例及指令可以參考以下資源!

· 3 min read
zaxro

使用場景

因為電腦切內外網的情況下,有時候會需要去切換網卡使用的優先順序,相比於去系統設定做調整,使用cmd也可以做到同樣事情且更方便!

2023/8/16新增調整路由的解法!

mac調整路由

另外一個思路是調整到某個網段,例如,10.0.0.0/8網段就會透過指定網關出去,其他走default.

netstat -nr:這個指令可以看目前路由情況,包含ipv4 and ipv6! route add -net 10.0.0.0/8 10.14.33.254 透過10.14.33.254網關到10.0.0.0/8網段

最後確認手段,透過route -n get 10.160.0.3測試他!這邊應該會看到走不同gateway

mac修改

整個流程會是先列出目前順序->調整順序->(optional)建立alias以便之後調整->長久生效化

列出目前順序

networksetup -listnetworkserviceorder

結果會像以下

An asterisk (*) denotes that a network service is disabled.
(1) Ethernet
(Hardware Port: Ethernet, Device: en0)

(2) Wi-Fi
(Hardware Port: Wi-Fi, Device: en1)

(3) iPhone USB
(Hardware Port: iPhone USB, Device: en4)

(4) Thunderbolt Bridge
(Hardware Port: Thunderbolt Bridge, Device: bridge0)

調整順序,這邊把wifi往前調整成為首位

sudo networksetup -ordernetworkservices "Wi-Fi" "Ethernet" "iPhone USB" "Thunderbolt Bridge"

確認結果

networksetup -listnetworkserviceorder

調整後結果如下

An asterisk (*) denotes that a network service is disabled.
(1) Wi-Fi
(Hardware Port: Wi-Fi, Device: en1)

(2) Ethernet
(Hardware Port: Ethernet, Device: en0)

(3) iPhone USB
(Hardware Port: iPhone USB, Device: en4)

(4) Thunderbolt Bridge
(Hardware Port: Thunderbolt Bridge, Device: bridge0)

建立alias

alias insetnet='sudo networksetup -ordernetworkservices "Ethernet" "Wi-Fi"  "iPhone USB" "Thunderbolt Bridge"'
alias outsetnet='sudo networksetup -ordernetworkservices "Wi-Fi" "Ethernet" "iPhone USB" "Thunderbolt Bridge"'

如果是用mac可以把上面那段加入到~/.zshrc的文尾.重新載入!

source ~/.zshrc

這邊建立兩個指令insetnet and outsetnet,用以切換網路使用順序!

· 3 min read
zaxro

kibana

這邊用docker-compose起服務,不過會有conatiner一個使用者,而非預設的root

mkdir -p ./data
chown -R 472:472 ./data
version: "3.3"
services:
grafana:
image: grafana/grafana-enterprise:9.5.6
user: "472"
container_name: grafana
restart: unless-stopped
environment:
- GF_SERVER_ROOT_URL=http://*.948787.store/
- GF_INSTALL_PLUGINS=grafana-clock-panel
ports:
- '3000:3000'
volumes:
- '$PWD/data:/var/lib/grafana'

基本上,grafana只要設定資料源去拉prometheues,然後知道怎麼找適合的dashboard,以及變數如何設定就差不多!