# kubectl get --raw "/api/v1/nodes/kind-worker/proxy/stats/summary" | grep imageFs -A 5
"imageFs": {
"time": "2024-02-26T14:40:12Z",
"availableBytes": 21507072000,
"capacityBytes": 31025332224,
"usedBytes": 541495296,
"inodesFree": 3668005,
上圖可以看到 imageFS 目前顯示
Kubelet 本身是沒有去紀錄以及計算這些,而是透過 CRI 的標準去問底下 contaienr runtime 來處理 https://github.com/kubernetes/cri-api/blob/c75ef5b/pkg/apis/runtime/v1/api.proto#L120-L136
service ImageService {
// ListImages lists existing images.
rpc ListImages(ListImagesRequest) returns (ListImagesResponse) {}
// ImageStatus returns the status of the image. If the image is not
// present, returns a response with ImageStatusResponse.Image set to
// nil.
rpc ImageStatus(ImageStatusRequest) returns (ImageStatusResponse) {}
// PullImage pulls an image with authentication config.
rpc PullImage(PullImageRequest) returns (PullImageResponse) {}
// RemoveImage removes the image.
// This call is idempotent, and must not return an error if the image has
// already been removed.
rpc RemoveImage(RemoveImageRequest) returns (RemoveImageResponse) {}
// ImageFSInfo returns information of the filesystem that is used to store images.
rpc ImageFsInfo(ImageFsInfoRequest) returns (ImageFsInfoResponse) {}
}
既然 CRI 有提供,就可以使用 crictl 嘗試挖掘看看,果然有找到一個 imagefsinfo 的資訊
# crictl imagefsinfo
{
"status": {
"timestamp": "1708958572632331985",
"fsId": {
"mountpoint": "/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs"
},
"usedBytes": {
"value": "541495296"
},
"inodesUsed": {
"value": "18150"
}
}
}
該指令回報了目前使用了 "541495296" Bytes,與 K8s 回報的一樣,但是並沒有解釋怎麼計算 available 以及 capacity。 其中還有提到一個 fsId(FilesystemIdentifier)
接下來從 kubelet 的原始碼可以抓到
...
imageFsInfo, err := p.getFsInfo(fs.GetFsId())
if err != nil {
return nil, nil, fmt.Errorf("get filesystem info: %w", err)
}
if imageFsInfo != nil {
// The image filesystem id is unknown to the local node or there's
// an error on retrieving the stats. In these cases, we omit those
// stats and return the best-effort partial result. See
// https://github.com/kubernetes/heapster/issues/1793.
imageFsRet.AvailableBytes = &imageFsInfo.Available
imageFsRet.CapacityBytes = &imageFsInfo.Capacity
imageFsRet.InodesFree = imageFsInfo.InodesFree
imageFsRet.Inodes = imageFsInfo.Inodes
}
...
透過 imageFsInfo 內的 GetFsId 獲得相關資訊,往下去翻 getFsInfo 函式
func (p *criStatsProvider) getFsInfo(fsID *runtimeapi.FilesystemIdentifier) (*cadvisorapiv2.FsInfo, error) {
if fsID == nil {
klog.V(2).InfoS("Failed to get filesystem info: fsID is nil")
return nil, nil
}
mountpoint := fsID.GetMountpoint()
fsInfo, err := p.cadvisor.GetDirFsInfo(mountpoint)
if err != nil {
msg := "Failed to get the info of the filesystem with mountpoint"
if errors.Is(err, cadvisorfs.ErrNoSuchDevice) ||
errors.Is(err, cadvisorfs.ErrDeviceNotInPartitionsMap) ||
errors.Is(err, cadvisormemory.ErrDataNotFound) {
klog.V(2).InfoS(msg, "mountpoint", mountpoint, "err", err)
} else {
klog.ErrorS(err, msg, "mountpoint", mountpoint)
return nil, fmt.Errorf("%s: %w", msg, err)
}
return nil, nil
}
return &fsInfo, nil
}
透過 fsID.GetMountpoint() 來取得對應的 mountPoint。 https://github.com/kubernetes/cri-api/blob/v0.25.16/pkg/apis/runtime/v1alpha2/api.pb.go#L7364
func (m *FilesystemIdentifier) GetMountpoint() string {
if m != nil {
return m.Mountpoint
}
return ""
}
由於上述的路徑是 '/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs',搭配我的 'df' 結果去比對
# df -BKB
Filesystem 1kB-blocks Used Available Use% Mounted on
overlay 31025333kB 9502487kB 21506069kB 31% /
tmpfs 67109kB 0kB 67109kB 0% /dev
shm 67109kB 0kB 67109kB 0% /dev/shm
/dev/root 31025333kB 9502487kB 21506069kB 31% /var
tmpfs 16794874kB 9552kB 16785322kB 1% /run
將上述 /var 的大小與之前去比對,幾乎吻合,所以看起來就是根據路徑找到 mountPoint 並且得到目前的使用量以及用量。
"availableBytes": 21507072000, "capacityBytes": 31025332224,
]]>解決方式就是加入 resolver 並且透過變數的方式去設定 proxy_pass
使用情境特別是 k8s 內的 headless
參考: https://rajrajhans.com/2022/06/force-dns-resolution-nginx-proxy/
之後再來寫一篇長篇文章記錄 source code 的閱讀心得
]]>Multus 那有相關的專案來解決這個問題,以下專案提供介面 https://github.com/k8snetworkplumbingwg/multi-networkpolicy
該專案被用於 openshift 環境內,實作的專案(iptables)如下 https://github.com/openshift/multus-networkpolicy
其會動態的進入到目標 Pod 內去下 iptables 的規則來控管封包的進出
專案內的 deploy.yaml 可以直接安裝,不過下列參數需要修改
- "--host-prefix=/host"
# uncomment this if runtime is docker
# - "--container-runtime=docker"
- "--network-plugins=bridge"
- "--v=9"
- "--container-runtime-endpoint=/run/containerd/containerd.sock"
(1) 的部分要特別注意 --networks-plugins=bridge 以及 --container-runtime-endpoint 前者要跟 multus 串連的 multus 一致,這樣才會運作
接者就要部署專屬的 MultiNetworkPolicy 的物件,用法與傳統的 Network Policy 一樣
apiVersion: k8s.cni.cncf.io/v1beta1
kind: MultiNetworkPolicy
metadata:
name: test-network-policy
namespace: default
annotations:
k8s.v1.cni.cncf.io/policy-for: bridge-network
spec:
podSelector:
matchLabels:
app: debug
policyTypes:
- Ingress
- Egress
ingress:
- from:
- ipBlock:
cidr: 10.10.0.5/24
egress:
- to:
- ipBlock:
cidr: 10.10.0.7/32
設定完成後就有機會於符合規則的 container 內看到下列規則
[142:11928] -A INPUT -i net1 -j MULTI-INGRESS
[478:40152] -A OUTPUT -o net1 -j MULTI-EGRESS
[0:0] -A MULTI-0-EGRESS -j MARK --set-xmark 0x0/0x30000
[0:0] -A MULTI-0-EGRESS -j MULTI-0-EGRESS-0-PORTS
[0:0] -A MULTI-0-EGRESS -j MULTI-0-EGRESS-0-TO
[0:0] -A MULTI-0-EGRESS -m mark --mark 0x30000/0x30000 -j RETURN
[0:0] -A MULTI-0-EGRESS -j DROP
[0:0] -A MULTI-0-EGRESS-0-PORTS -m comment --comment "no egress ports, skipped" -j MARK --set-xmark 0x10000/0x10000
[0:0] -A MULTI-0-EGRESS-0-TO -d 10.10.0.7/32 -o net1 -j MARK --set-xmark 0x20000/0x20000
[0:0] -A MULTI-0-INGRESS -j MARK --set-xmark 0x0/0x30000
[0:0] -A MULTI-0-INGRESS -j MULTI-0-INGRESS-0-PORTS
[0:0] -A MULTI-0-INGRESS -j MULTI-0-INGRESS-0-FROM
[0:0] -A MULTI-0-INGRESS -m mark --mark 0x30000/0x30000 -j RETURN
[0:0] -A MULTI-0-INGRESS -j DROP
[0:0] -A MULTI-0-INGRESS-0-FROM -s 10.10.0.0/24 -i net1 -j MARK --set-xmark 0x20000/0x20000
[0:0] -A MULTI-0-INGRESS-0-PORTS -m comment --comment "no ingress ports, skipped" -j MARK --set-xmark 0x10000/0x10000
[0:0] -A MULTI-EGRESS -o net1 -m comment --comment "policy:test-network-policy net-attach-def:default/bridge-network" -j MULTI-0-EGRESS
[0:0] -A MULTI-INGRESS -i net1 -m comment --comment "policy:test-network-policy net-attach-def:default/bridge-network" -j MULTI-0-INGRESS
COMMIT
其透過 mark 的方式來標示封包是否需要被 DROP,同時也支援針對 ip & port 的方式去判斷
]]>int br_add_if(struct net_bridge *br, struct net_device *dev,
struct netlink_ext_ack *extack)
{
struct net_bridge_port *p;
int err = 0;
unsigned br_hr, dev_hr;
bool changed_addr, fdb_synced = false;
/* Don't allow bridging non-ethernet like devices. */
if ((dev->flags & IFF_LOOPBACK) ||
dev->type != ARPHRD_ETHER || dev->addr_len != ETH_ALEN ||
!is_valid_ether_addr(dev->dev_addr))
return -EINVAL;
/* No bridging of bridges */
if (dev->netdev_ops->ndo_start_xmit == br_dev_xmit) {
NL_SET_ERR_MSG(extack,
"Can not enslave a bridge to a bridge");
return -ELOOP;
}
/* Device has master upper dev */
if (netdev_master_upper_dev_get(dev))
return -EBUSY;
/* No bridging devices that dislike that (e.g. wireless) */
if (dev->priv_flags & IFF_DONT_BRIDGE) {
NL_SET_ERR_MSG(extack,
"Device does not allow enslaving to a bridge");
return -EOPNOTSUPP;
}
p = new_nbp(br, dev);
if (IS_ERR(p))
return PTR_ERR(p);
call_netdevice_notifiers(NETDEV_JOIN, dev);
err = dev_set_allmulti(dev, 1);
if (err) {
br_multicast_del_port(p);
netdev_put(dev, &p->dev_tracker);
kfree(p); /* kobject not yet init'd, manually free */
goto err1;
}
err = kobject_init_and_add(&p->kobj, &brport_ktype, &(dev->dev.kobj),
SYSFS_BRIDGE_PORT_ATTR);
if (err)
goto err2;
err = br_sysfs_addif(p);
if (err)
goto err2;
err = br_netpoll_enable(p);
if (err)
goto err3;
err = netdev_rx_handler_register(dev, br_get_rx_handler(dev), p);
if (err)
goto err4;
dev->priv_flags |= IFF_BRIDGE_PORT;
err = netdev_master_upper_dev_link(dev, br->dev, NULL, NULL, extack);
if (err)
goto err5;
dev_disable_lro(dev);
list_add_rcu(&p->list, &br->port_list);
nbp_update_port_count(br);
if (!br_promisc_port(p) && (p->dev->priv_flags & IFF_UNICAST_FLT)) {
/* When updating the port count we also update all ports'
* promiscuous mode.
* A port leaving promiscuous mode normally gets the bridge's
* fdb synced to the unicast filter (if supported), however,
* `br_port_clear_promisc` does not distinguish between
* non-promiscuous ports and *new* ports, so we need to
* sync explicitly here.
*/
fdb_synced = br_fdb_sync_static(br, p) == 0;
if (!fdb_synced)
netdev_err(dev, "failed to sync bridge static fdb addresses to this port\n");
}
netdev_update_features(br->dev);
br_hr = br->dev->needed_headroom;
dev_hr = netdev_get_fwd_headroom(dev);
if (br_hr < dev_hr)
update_headroom(br, dev_hr);
else
netdev_set_rx_headroom(dev, br_hr);
if (br_fdb_add_local(br, p, dev->dev_addr, 0))
netdev_err(dev, "failed insert local address bridge forwarding table\n");
if (br->dev->addr_assign_type != NET_ADDR_SET) {
/* Ask for permission to use this MAC address now, even if we
* don't end up choosing it below.
*/
err = dev_pre_changeaddr_notify(br->dev, dev->dev_addr, extack);
if (err)
goto err6;
}
err = nbp_vlan_init(p, extack);
if (err) {
netdev_err(dev, "failed to initialize vlan filtering on this port\n");
goto err6;
}
spin_lock_bh(&br->lock);
changed_addr = br_stp_recalculate_bridge_id(br);
if (netif_running(dev) && netif_oper_up(dev) &&
(br->dev->flags & IFF_UP))
br_stp_enable_port(p);
spin_unlock_bh(&br->lock);
br_ifinfo_notify(RTM_NEWLINK, NULL, p);
if (changed_addr)
call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);
br_mtu_auto_adjust(br);
br_set_gso_limits(br);
kobject_uevent(&p->kobj, KOBJ_ADD);
return 0;
err6:
if (fdb_synced)
br_fdb_unsync_static(br, p);
list_del_rcu(&p->list);
br_fdb_delete_by_port(br, p, 0, 1);
nbp_update_port_count(br);
netdev_upper_dev_unlink(dev, br->dev);
err5:
dev->priv_flags &= ~IFF_BRIDGE_PORT;
netdev_rx_handler_unregister(dev);
err4:
br_netpoll_disable(p);
err3:
sysfs_remove_link(br->ifobj, p->dev->name);
err2:
br_multicast_del_port(p);
netdev_put(dev, &p->dev_tracker);
kobject_put(&p->kobj);
dev_set_allmulti(dev, -1);
err1:
return err;
}
其中上述的重點是 br_mtu_auto_adjust
,該 function 的內容如下,基本上就去找出最小MTU並且設定
void br_mtu_auto_adjust(struct net_bridge *br)
{
ASSERT_RTNL();
/* if the bridge MTU was manually configured don't mess with it */
if (br_opt_get(br, BROPT_MTU_SET_BY_USER))
return;
/* change to the minimum MTU and clear the flag which was set by
* the bridge ndo_change_mtu callback
*/
dev_set_mtu(br->dev, br_mtu_min(br));
br_opt_toggle(br, BROPT_MTU_SET_BY_USER, false);
}
安裝指令來檢查 qemu 相關狀態
sudo apt install libvirt-clients
使用 virt-host-validate 檢查相關
$ virt-host-validate qemu
QEMU: Checking for hardware virtualization : PASS
QEMU: Checking if device /dev/kvm exists : PASS
QEMU: Checking if device /dev/kvm is accessible : FAIL (Check /dev/kvm is world writable or you are in a group that is allowed to access it)
QEMU: Checking if device /dev/vhost-net exists : PASS
QEMU: Checking if device /dev/net/tun exists : PASS
QEMU: Checking for cgroup 'cpu' controller support : PASS
QEMU: Checking for cgroup 'cpuacct' controller support : PASS
QEMU: Checking for cgroup 'cpuset' controller support : PASS
QEMU: Checking for cgroup 'memory' controller support : PASS
QEMU: Checking for cgroup 'devices' controller support : WARN (Enable 'devices' in kernel Kconfig file or mount/enable cgroup controller in your system)
QEMU: Checking for cgroup 'blkio' controller support : PASS
QEMU: Checking for device assignment IOMMU support : WARN (No ACPI DMAR table found, IOMMU either disabled in BIOS or not supported by this hardware platform)
QEMU: Checking for secure guest support : WARN (Unknown if this platform has Secure Guest support)
可以看到中間有一個錯誤,這時候需要安裝 sudo apt install qemu-kvm
並且調整權限 sudo usermod -aG kvm $USER
.
$ virt-host-validate qemu
QEMU: Checking for hardware virtualization : PASS
QEMU: Checking if device /dev/kvm exists : PASS
QEMU: Checking if device /dev/kvm is accessible : PASS
QEMU: Checking if device /dev/vhost-net exists : PASS
QEMU: Checking if device /dev/net/tun exists : PASS
QEMU: Checking for cgroup 'cpu' controller support : PASS
QEMU: Checking for cgroup 'cpuacct' controller support : PASS
QEMU: Checking for cgroup 'cpuset' controller support : PASS
QEMU: Checking for cgroup 'memory' controller support : PASS
QEMU: Checking for cgroup 'devices' controller support : WARN (Enable 'devices' in kernel Kconfig file or mount/enable cgroup controller in your system)
QEMU: Checking for cgroup 'blkio' controller support : PASS
QEMU: Checking for device assignment IOMMU support : WARN (No ACPI DMAR table found, IOMMU either disabled in BIOS or not supported by this hardware platform)
QEMU: Checking for secure guest support : WARN (Unknown if this platform has Secure Guest support)
透過 minikube 搭建一個 k8s (provider採用 docker 減少第二層虛擬化)
$ minikube start --cni=flannel
叢集準備好後,安裝 kubevirt-operator
$ export VERSION=$(curl -s https://api.github.com/repos/kubevirt/kubevirt/releases | grep tag_name | grep -v -- '-rc' | sort -r | head -1 | awk -F': ' '{print $2}' | sed 's/,//' | xargs)
$ echo $VERSION
$ kubectl create -f https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/kubevirt-operator.yaml
namespace/kubevirt created
customresourcedefinition.apiextensions.k8s.io/kubevirts.kubevirt.io created
priorityclass.scheduling.k8s.io/kubevirt-cluster-critical created
clusterrole.rbac.authorization.k8s.io/kubevirt.io:operator created
serviceaccount/kubevirt-operator created
role.rbac.authorization.k8s.io/kubevirt-operator created
rolebinding.rbac.authorization.k8s.io/kubevirt-operator-rolebinding created
clusterrole.rbac.authorization.k8s.io/kubevirt-operator created
clusterrolebinding.rbac.authorization.k8s.io/kubevirt-operator created
deployment.apps/virt-operator created
實驗當下使用的版本是 v1.1.0-alpha.0,安裝完畢後檢查 kubevirt namespace 的資源
$ kubectl -n kubevirt get pods
NAME READY STATUS RESTARTS AGE
virt-operator-57f9fb965d-5lnqf 1/1 Running 0 46m
virt-operator-57f9fb965d-f5zg4 1/1 Running 0 46m
接下來安裝 CRD 物件
$ kubectl create -f https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/kubevirt-cr.yaml
安裝完畢後可以看到有一個名為 kubevirt
的物件(CRD為 kubevirt,簡稱 kv)被創立,因此 operator 就會針對該物件去創立 kubevirt 相關的服務 Pod
$ kubectl -n kubevirt get kv kubevirt -o yaml
apiVersion: kubevirt.io/v1
kind: KubeVirt
metadata:
annotations:
kubevirt.io/latest-observed-api-version: v1
kubevirt.io/storage-observed-api-version: v1
creationTimestamp: "2023-10-10T14:35:55Z"
finalizers:
- foregroundDeleteKubeVirt
generation: 2
name: kubevirt
namespace: kubevirt
resourceVersion: "1490"
uid: bc621d93-4910-4b1f-b3c8-f8f1f4e27a38
spec:
certificateRotateStrategy: {}
configuration:
developerConfiguration: {}
customizeComponents: {}
imagePullPolicy: IfNotPresent workloadUpdateStrategy: {}
$ kubectl -n kubevirt get pods
NAME READY STATUS RESTARTS AGE
virt-api-77f8d679fc-hntws 1/1 Running 0 49m
virt-controller-6689488456-4jtv8 1/1 Running 0 48m
virt-controller-6689488456-68hnz 1/1 Running 0 48m
virt-handler-psc4w 1/1 Running 0 48m
基本上就是預設的設定檔案,然後對應的 API, Controller 以及 Handler 都被創建出來處理後續的操作。
透過官方指令直接抓取對應版本的 virtctl
VERSION=$(kubectl get kubevirt.kubevirt.io/kubevirt -n kubevirt -o=jsonpath="{.status.observedKubeVirtVersion}")
ARCH=$(uname -s | tr A-Z a-z)-$(uname -m | sed 's/x86_64/amd64/') || windows-amd64.exe
echo ${ARCH}
curl -L -o virtctl https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/virtctl-${VERSION}-${ARCH}
chmod +x virtctl
sudo install virtctl /usr/local/bin
-> % virtctl version
Client Version: version.Info{GitVersion:"v1.1.0-alpha.0", GitCommit:"67902ed9de43d7a0b94aa72b8fd7f48f31ca4285", GitTreeState:"clean", BuildDate:"2023-09-18T10:45:14Z", GoVersion:"go1.19.9", Compiler:"gc", Platform:"darwin/arm64"}
Server Version: version.Info{GitVersion:"v1.1.0-alpha.0", GitCommit:"67902ed9de43d7a0b94aa72b8fd7f48f31ca4285", GitTreeState:"clean", BuildDate:"2023-09-18T12:03:45Z", GoVersion:"go1.19.9", Compiler:"gc", Platform:"linux/arm64"}
'''info
官方文件有說明可以透過 kubectl krew 的平台來安裝 virtctl 指令,透過 kubectl krew install virt
來安裝並使用,但是目前並沒有支援 darwin-arm64 (MacOS M1/M2)
'''
透過官方示範檔案部署第一個 VM
$ kubectl apply -f https://kubevirt.io/labs/manifests/vm.yaml
virtualmachine.kubevirt.io/testvm created
$ kubectl get vm
NAME AGE STATUS READY
testvm 7s Stopped False
預設情況下,創建好 VM 並不代表 VM 已經啟動,這時候可以透過 virtctl
將該 VM 給運行起來
$ virtctl start testvm
VM testvm was scheduled to start
當 VM 啟動後,對應的 Pod 就會正式被部署到環境內
$ kubectl get pods -o wide
這時候來研究一下該 Pod 的一些架構
先透過 virtctl console testvm
登入後觀察一下 VM IP
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
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 1450 qdisc pfifo_fast qlen 1000
link/ether 52:54:00:0c:00:55 brd ff:ff:ff:ff:ff:ff
inet 10.0.2.2/24 brd 10.0.2.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::5054:ff:fe0c:55/64 scope link
valid_lft forever preferred_lft forever
$ ip r
default via 10.0.2.1 dev eth0
10.0.2.0/24 dev eth0 src 10.0.2.2
IP 是 10.0.2.2
並且 Gateway 是 10.0.2.1
這時候進入到對應的 Pod 去觀察
$ kubectl exec -it virt-launcher-testvm-pnn4j -- bash
bash-5.1$ ip a
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
2: eth0@if14: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
link/ether 12:37:77:cf:6d:63 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.244.0.10/24 brd 10.244.0.255 scope global eth0
valid_lft forever preferred_lft forever
3: k6t-eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default qlen 1000
link/ether 02:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
inet 10.0.2.1/24 brd 10.0.2.255 scope global k6t-eth0
valid_lft forever preferred_lft forever
4: tap0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc fq_codel master k6t-eth0 state UP group default qlen 1000
link/ether d6:0e:c5:6f:41:f1 brd ff:ff:ff:ff:ff:ff
bash-5.1$
這邊可以看到 Pod 上面的 k6t-eth0
是有 IP 10.0.2.1
同時可以看到下方有一個 tap0 的網卡,該網卡有設定 master k6t-eth0
因此可以推斷 k6t-eth0
是 Linux Bridge, tap0 則是 bridge 下的一個 Port,透過下列指令可以確認
bash-5.1$ ls /sys/class/net/k6t-eth0/brif
tap0
bash-5.1$ ls /sys/class/net/k6t-eth0/bridge/
ageing_time group_fwd_mask multicast_last_member_count multicast_query_response_interval nf_call_arptables root_port vlan_protocol
bridge_id hash_elasticity multicast_last_member_interval multicast_query_use_ifaddr nf_call_ip6tables stp_state vlan_stats_enabled
default_pvid hash_max multicast_membership_interval multicast_router nf_call_iptables tcn_timer vlan_stats_per_port
flush hello_time multicast_mld_version multicast_snooping no_linklocal_learn topology_change
forward_delay hello_timer multicast_querier multicast_startup_query_count priority topology_change_detected
gc_timer max_age multicast_querier_interval multicast_startup_query_interval root_id topology_change_timer
group_addr multicast_igmp_version multicast_query_interval multicast_stats_enabled root_path_cost vlan_filtering
bash-5.1$
k6t-eth0 底下有眾多 bridge 的設定,並且 brif 底下有 tap0,而實務上該 tap0 則是 kvm 創建 vm 後將其綁到 VM 內,因此會與 VM 內的 eth0 掛勾,可以想成是一條大水管,一邊進去另外一邊出來 看來詳細細節還是需要閱讀interface networks,似乎提供不同網路模式來達成不同功能,有空來玩看看彼此差異研究下實作細節。
]]>sed '/^keywords:/d' input > output
刪除符合字串後的所有行數
sed '/^keywords/,$d' input > output
搭配 Find 達到大量修改所有檔案
統一刪除所有檔案
find . -type f -exec sed -i '' '/^authors:/d' {} +
Append 一行新的,換行要特別注意處理
find . -type f -exec sed -i '' '/^title/a\
authors: hwchiu\
' {} +
大量換名稱 https://hackmd.io/_uploads 變成 ./assets/
find *.md -type f -exec sed -i '' -e 's/https:\/\/hackmd\.io\/_uploads\//\.\/assets\//g' {} +
假設環境中有大量檔案需要改名稱,透過 rename 這個工具可以快速達成 譬如以下範例會先用正規表達式找尋所有符合的檔案名稱,接者將所有 read-notes 都改名為 reading-notes
rename 's/read-notes/reading-notes/' *read-notes-*
navbar__link--active
的屬性。
仔細研究後發現官方有相關 Issue,根據 issue 所述針對 items 內補上 activeBaseRegex: '^/$',
即可。最後呈現
{
to: '/',
label: '短篇筆記',
position: 'left',
activeBaseRegex: '^/$',
},
$ gcloud config configurations list
NAME IS_ACTIVE ACCOUNT PROJECT COMPUTE_DEFAULT_ZONE COMPUTE_DEFAULT_REGION
default False aaaa@yyyyy.com first-project
name2 False bbbb@yyyyy.com
name3 True ccccc@yyyy-admin.iam.gserviceaccount.com
如果要切換可以使用
$ gcloud config configurations activate name2
進行切換,這時候 gcloud command 就可以轉移過去了。
如果有用 GKE 的,還要額外呼叫一次 gcloud container clusters update
去更新 KUBECONFIG
採用 Kustomize + Helm 的方式 (ArgoCD)
$ cat kustomization.yaml
helmCharts:
- name: redis-cluster
includeCRDs: false
valuesFile: redis.yaml
releaseName: redis-cluster
namespace: production
version: 9.0.5
repo: https://charts.bitnami.com/bitnami
$ cat redis.yaml
global:
storageClass: standard-rwo
existingSecret: redis-cluster
existingSecretPasswordKey: password
redis:
resources:
limits:
cpu: 2
memory: 2Gi
requests:
cpu: 0.1
memory: 0.5Gi
podAnnotations:
'xxxxx': 'yyyyy'
這種部署方式就會啟動密碼驗證機制,而密碼則來自於同 namespace 的 secret 物件 redis-cluster
內的 key password
.
如果想要無密碼驗證,可以使用下列方式
usePassword: false
'''note 如果已經先行有密碼驗證,則修改此欄位對於運行中的 Redis Cluster 不會有任何效果,這部分需要更多額外操作來完成,砍掉 PVC 重新部署是一個簡單暴力的方式。 '''
由於此架構會部署 Redis-cluster,預設情況下會部署三組(master+worker)的 statefulset,並且給兩組 servicee
redis-cluster ClusterIP 10.2.5.248 <none> 6379/TCP 213d
redis-cluster-headless ClusterIP None <none> 6379/TCP,16379/TCP 213d
另外要注意的是,redis-cluster 並不適用 kubectl port-forward
的方式連接,因為 redis-cluster 的溝通過程會回傳其他 Pod 的 IP 給你,而每個 Pod 的 IP 都用相同的 Port,因此除非你有辦法於本地產生一些虛擬網卡並且搭配多組 kubectl port-forawrd
幫每個 Pod 都打通,否則存取上會出問題。
舉例來說
$ kubectl port-forward --address 0.0.0.0 pod/redis-cluster-0 6379:6379
Forwarding from 0.0.0.0:6379 -> 6379
$ redis-benchmark -n 1 --cluster
Cluster has 3 master nodes:
Master 0: 1020525d25c7ad16e786a98e1eb7419d609b8847 10.4.0.119:6379
Could not connect to Redis at 10.4.0.119:6379: Connection refused
可以看到透過 port-forward 打進去後,接下來的連線就會轉到其他的 pod 然後就會失敗,因此這種情況要簡單使用還是部署一個包含 redis 指令的 Pod 到同樣的 namespace 並且用 kubectl exec
進去操作會比較順
$ kubectl run --image=hwchiu/netutils test
$ kubectl exec -it test -- bash
root@test:/# redis-benchmark -h redis-cluster -q -c 20 -n 30000
PING_INLINE: 44977.51 requests per second
PING_BULK: 48154.09 requests per second
SET: 45317.22 requests per second
GET: 47169.81 requests per second
INCR: 50251.26 requests per second
LPUSH: 48465.27 requests per second
LPOP: 41265.48 requests per second
SADD: 37878.79 requests per second
SPOP: 49833.89 requests per second
LPUSH (needed to benchmark LRANGE): 51724.14 requests per second
LRANGE_100 (first 100 elements): 43041.61 requests per second
LRANGE_300 (first 300 elements): 35842.29 requests per second
LRANGE_500 (first 450 elements): 36014.41 requests per second
LRANGE_600 (first 600 elements): 33259.42 requests per second
MSET (10 keys): 42796.01 requests per second
但是如果今天該物件的安裝條件則是根據 K8s 版本而定,特別是當某些 API 於新版被移除時,這時候要如何撰寫一個兼容兩個版本的 Helm Chart。 舉例來說,以最近被移除的 PSP(PodSecurityPolicy) 物件為範例。
.Capabilities.APIVersions.Has
去判斷目標 K8s API Resource 是否有包含目標版本以 kube-prometheus-stack 為範例 其 psp-clusterorle.yaml 中的開頭使用了下列語法
{{- if and .Values.prometheus.enabled .Values.global.rbac.create .Values.global.rbac.pspEnabled }}
{{- if .Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy" }}
kind: ClusterRole
...
{{- end }}
{{- end }}
透過 .Capabilities.APIVersions.Has
語法去判斷該物件是否支援,若支援則安裝否則跳掉,這機制帶來的好處就是可以打造出一個兼容更多版本 K8s 叢集的 Helm Chart,但是實務上真的需要這樣控管?還是應該要用不同版本的來管理會更好應該就見仁見智。
分類的設定可以從
這幾個分類對消費者來說最大的影響可能就是存取與維護成本 以Cloud Storage pricing來說
存放本身的價格就是 STANDARD > NEARLINE > COLDLINE > ARCHIVE
但是如果今天想要存取資料(Retrieval fees)來說則要特別注意 STANDARD 本身免費,後面三者價格依序提高,其中以 COLDLINE 來說是 $0.02 GB
因此若需要存取 GCS 的話,則特別要注意目前檔案的屬性以及存取量,然後評估一下可能的花費 若有需要大量長期存取的,記得要切換成 STANDARD 模式,若幾乎不存取的就直接往後搬移減少儲存花費。
]]>...
spec:
values:
global:
proxy:
privileged: false
readinessFailureThreshold: 30
readinessInitialDelaySeconds: 1
readinessPeriodSeconds: 2
resources:
limits:
cpu: 2000m
memory: 1024Mi
requests:
cpu: 100m
memory: 128Mi
這個設定是 global 的設定,如果是單一的 Pod 要自行調整,可以於 Pod annotations 中加入列下資訊調整
annotations:
sidecar.istio.io/proxyCPU: 50m
sidecar.istio.io/proxyMemory: 128Mi
如果要更新 istio,建議參考官方 Canary Approach 的步驟,使用金絲雀部署的方式逐步調整 其原理很簡單
基本上安裝過程要透過 "--revision=1-14-2" 的方式去打版本,安裝完畢後就是單純只有 control plane
接下來就取決當初如何設定 sidecare 的,如果是 namespace 的話,就可以直接改 namespace 裡面的
istio.io/rev=1-14-2
接下來就逐步重啟 Pod 就可以切換到新的 istio 版本。
另外可以透過 istioctl proxy-status
觀察每個 Pod 目前搭配的版本,透過此指令觀察升級進度
一旦全部升級完畢可以用 istioctl uninstall --revision 1-13-1 -y
來移除舊版本
當使用支援 Lock 的遠端 Backend 時,每次執行 Terraform 操作都會嘗試去 Lock,並且指令結束後去釋放 Lock 若執行到一半就透過 CTRL+C 強制離開可能會導致 Lock 沒有辦法順利結束,這時候下次執行就會遇到下列的問題
$ terraform apply
Acquiring state lock. This may take a few moments...
╷
│ Error: Error acquiring the state lock
│
│ Error message: writing "gs://xxxxx/xxxxxxx/default.tflock" failed: googleapi: Error 412: At least one of the pre-conditions you specified did not hold., conditionNotMet
│ Lock Info:
│ ID: 1696991555387294
│ Path: gs://xxxxx/xxxxxxx/default.tflock
│ Operation: OperationTypeApply
│ Who: your_name@hostname.local
│ Version: 1.5.6
│ Created: 2022-10-11 02:32:35.12734 +0000 UTC
│ Info:
│
│ Terraform acquires a state lock to protect the state from being written
│ by multiple users at the same time. Please resolve the issue above and try
│ again. For most commands, you can disable locking with the "-lock=false"
│ flag, but this is not recommended.
當然上述原因也有可能是同時間真的有人其他人正在運行指令,把 lock 搶走,所以要先釐清 lock 卡住的情況是否如預期 如果是不預期的,就需要執行下列指令手動移除 lock
以上面輸出的 ID 當作內容,透過 terraform force-unlock
來解除
$ terraform force-unlock 1696991555387294
Do you really want to force-unlock?
Terraform will remove the lock on the remote state.
This will allow local Terraform commands to modify this state, even though it
may still be in use. Only 'yes' will be accepted to confirm.
Enter a value: yes
如果需要調整 Terraform State 的內容的話,通常可以使用
手動將不需要的內容從 state 中移除
但是如果今天有更強硬的要求需要手動去修改內容的話,則需要
這招很危險,要 100% 清楚自己做什麼同時也要有備份的 state 檔案,大意就是把 state 檔案抓下來並且直接修改,然後強行寫入回去,完全不需要額外 terraform plan/apply 的介入。 通常是 migration 過程希望可以順利轉移,同時又不希望遠方資源被影響,就可能會採用這種機制來直接修改 state.
此外轉移過程中如果有 provider 要處理,也可以透過使用 terraform state replace-provider
的方式來轉移,如下範例
terraform state replace-provider "registry.terraform.io/-/aws" "hashicorp/aws"
基本上只要節點的資源使用率過低,該節點就會被嘗試回收並且將所有的 Workload 都轉移到其他的節點
如果有特別特別重要的 Pod 希望該 Pod 能夠抑制 CA 的行為,有該 Pod 運行的節點都不能被踢除回收的話,可以於 annotations 中加入下列設定
cluster-autoscaler.kubernetes.io/safe-to-evict: 'false'
該節點就會讓節點沒有辦法順利踢除因此最後不會回收該節點,該指令也要小心使用,若用不好可能會導致節點資源使用率過低最後產生額外的花費。
應用程式本身需要更長時間去調整 GracePeriod (預設 30 秒),可以直接修改 pod.spec.terminationGracePeriodSeconds 此欄位即可
$ kc explain pod.spec.terminationGracePeriodSeconds
KIND: Pod
VERSION: v1
FIELD: terminationGracePeriodSeconds <integer>
DESCRIPTION:
Optional duration in seconds the pod needs to terminate gracefully. May be
decreased in delete request. Value must be non-negative integer. The value
zero indicates stop immediately via the kill signal (no opportunity to shut
down). If this value is nil, the default grace period will be used instead.
The grace period is the duration in seconds after the processes running in
the pod are sent a termination signal and the time when the processes are
forcibly halted with a kill signal. Set this value longer than the expected
cleanup time for your process. Defaults to 30 seconds.
簡易 bash 腳本可以備份目前環境中的所有 secret 物件
function dump_secret {
for i in $(kubectl -n $1 get --no-headers secret | awk '{print $1}'); do
kubectl -n $1 get secret -o yaml $i > $i.yaml;
done
}
function dump_secrets {
for i in $(kubectl get ns --no-headers | awk '{print $1}'); do
if [ ! -d "./$i" ]; then
mkdir $i
fi
echo "Dump $i"
cd $i
dump_secret $i
cd ..
done
}
$ git commit -m "Test" --author "HungWei Chiu<xxxxxx@gmail.com>"
$ git commit --amend --author "HungWei Chiu<xxxxx@gmail.com>" --no-edit
如果想要連 commit 一起修改且長期使用,比較簡單的方式就是直接設定 local user/email
$ git config --local user.email "xxxxx@gmail.com"
$ git config --local user.name "HungWei Chiu"
針對當前 commit 可以採用 --reset-author
的方式來修正
git commit --amend --no-edit --reset-author
Cloud NAT 上有一個設定稱為 Port Reservation 該設定會影響 Cloud NAT 要如何幫後方所有流量進行 SNAT,要用哪個 Source IP 以及哪個 Source Port 去處理。
其中有一個設定是 "Minimum Ports per VM",這個欄位的意思是每個 VM 上可以對相同目標 (IP + Port) 同時發起多少條連線 舉例來說,假設今天想要連接 1.2.3.4:2345 這個網站,且設定為 32,那就代表這個 VM 上最多只能有 32 條連線,超過的就會被 Cloud NAT 丟掉而無法處理,最後產生 timeout
如果今天 VM 規格夠大,上面部署 GKE 同時有多個相同副本的 Pod 同時運行,那就有可能會踩到這個數字導致連線 timeout,可以到 Cloud NAT 的設定將其調整,預設應該是 64。
另外 Cloud NAT 本身對外流量都會收費,要計算流量資訊需要到 VPC 去打開 Logging 紀錄,這個 Logging 也需要特別設定取樣頻率,因為會收費 所以設定完成後,就可以於 Cloud Logging 收到相關資訊,可以把 Logging 轉換為 Metrics 去計算流量的走向,譬如以 IP/hostname 為基準去分析到底流量都跑去那,再透過這個資訊來除錯省錢
]]>gcloud compute ssh
等方式登入到沒有 public IP 的機器上,但是每次設定上總是卡各種權限
而預設的 IAM Roles 裡面又沒有相關的身份可以一次搞定,常常要到處找到底缺哪個角色
經過一番努力跟嘗試後,確認只要給予下列權限就可以執行 gcloud compute ssh
compute.instances.osLogin
compute.instances.setMetadata
compute.instances.use
iam.serviceAccounts.actAs
iap.tunnelInstances.accessViaIAP
networkmanagement.connectivitytests.create
serviceusage.services.enable
因此創立一個新角色給予上面的權限,然後再把該角色綁定到目標使用者或群組,應該就可以透過 gcloud compute ssh
到遠方機器了。
其中 (1) 的主要是由 vCPU 與 Memory 的用量來決定價格,詳細資訊可以參閱網頁介紹
另外 CloudSQL 本身是可以直接升級機器強度的,可以手動也可以透過 Terraform 來管理,不過升級過程中 副會處於 downtime 不能存取階段,升級時間處決於當前機器的強度與資料量,短則一分鐘,長20分鐘都有可能。
另外硬碟使用量的部分有兩種設定機制
另外硬碟用量也會有收費的問題,假如當硬碟用量清空想要縮小硬碟用量,這部分目前還沒有辦法操作,需要開 Support ticket 請 GCP 幫忙縮小硬碟空間。
]]>too many unhealthy instances in the ring
這種情況的解法有兩個
手動移除的部分需要存取 loki-distributor
的 UI,譬如
$ kubectl port-forward svc/loki-distributor 3100:3100
接者存取 localhost:3100/ring 的網頁,就可以看到所有 instance 的資訊,針對不健康的 instance 從網頁中將其遺忘即可。
另外也可以部署安裝 Loki(Helm) 的過程中設定 ingester 的參數讓其自動忘記,未來就不需要手動設定
ingester:
autoforget_unhealthy: true
另外部署效能上有不少參數需要調整,通常都是 limits_config,新舊版本有些設定有差 然後 querier 以及 ingester 需要額外調整自己的 resource 與 HPA 的數量,根據使用者習慣以及用量來調整已提升整體吞吐量
loki:
config: |
server:
grpc_server_max_recv_msg_size: 104857600
grpc_server_max_send_msg_size: 104857600
http_server_read_timeout: 10m
http_server_write_timeout: 10m
ingester:
chunk_idle_period: 10m
chunk_block_size: 262144
chunk_encoding: snappy
chunk_retain_period: 1m
max_transfer_retries: 0
wal:
dir: /var/loki/wal
limits_config:
max_global_streams_per_user: 15000
enforce_metric_name: false
reject_old_samples: true
reject_old_samples_max_age: 168h
max_cache_freshness_per_query: 10m
retention_period: 2160h
split_queries_by_interval: 30m
ingestion_rate_mb: 32
ingestion_burst_size_mb: 64
max_query_parallelism: 256
max_cache_freshness_per_query: 10m
per_stream_rate_limit_burst: 15MB
gateway:
nginxConfig:
httpSnippet: client_max_body_size 50M;
serverSnippet: |-
client_max_body_size 50M;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
$ cat kustomization.yaml
helmCharts:
- name: redis-cluster
includeCRDs: false
valuesFile: redis.yaml
releaseName: redis-cluster
namespace: dev
version: 9.0.5
repo: https://charts.bitnami.com/bitnami
準備一個名為 redis.yaml 的檔案,就如同平常使用 helm values 一樣
接下來可以使用 kustomize 來嘗試產生最後部署的 YAML
$ kustomize build --enable-helm > temp
$ ls -l w
-rw-r--r-- 1 hwchiu momo 123257 Oct 11 11:18 temp
想嘗試使用 kubectl
但是目前都會失敗
$ kubectl apply --dry-run=client -o yaml -k .
error: trouble configuring builtin HelmChartInflationGenerator with config: `
name: redis-cluster
namespace: dev
releaseName: redis-cluster
repo: https://charts.bitnami.com/bitnami
valuesFile: redis.yaml
version: 9.0.5
`: must specify --enable-helm
$ kubectl apply --dry-run=client -o yaml -k . --enable-helm
error: unknown flag: --enable-helm
See 'kubectl apply --help' for usage.
另外如果要於 ArgoCD 中使用,需要修改 argocd-cm 加入下列參數
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cm
data:
kustomize.buildOptions: --enable-helm
這類型的事件可以透過下列的方式去觀測
以 Loki 來說,可以採用下列語法去過ㄌㄩ
count_over_time({container="kube-event-exporter"}[1m] | json | __error__ != "JSONParserErr" | reason="OOMKilling")
然而這類型的設定卻有一些限制
但是這類型的操作與 gcloud 的整合非常順,可以很輕鬆的就讓所有團得人員獲得 GKE 的存取權限,進而使用 kubectl 等指令
若今天想要修改權限改使用 Kubernetes RBAC 的方式,達到稍微細緻以 namespace 為基底的權限, 並且以群組信箱來當作 subjet 的話則需要一些額外步驟
gke-security-groups@xxxx.com
) 一個 Kubernetes Engine Cluster Viewer
的權限,因為所有人都至少要能夠透過 gcloud 去認證獲得 KUBECONFIG 來存取,因此至少要可讀另外群組的部分,可以採用 nested group,就是所有真正的 group 都加入到上述的 gke-security-groups
內,因此 RBAC 設定的部分就可以採用 dev@xxx.com, qa@xxxx.com 等方式來設定。
一切完畢後就依照 Kubernetes RBAC 的設定,準備 Role/RoleBinding,其中 RoleBinding 的 Subjects 改成
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: xxxxxxxxxx
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: xxxxxxxxxxxx
roleRef:
kind: Role
name: xxxxxxxxxx
apiGroup: rbac.authorization.k8s.io
快速產生的方式可以採用 nodejs 來搞定
先準備一個下列檔案
$ cat jwt.js
var jwt = require('jsonwebtoken');
var token = jwt.sign({
"data": "my value",
"permissions": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
],
}, 'secret_file', {expiresIn: "365 days"});
console.log(token)
$ node jwt.js
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnZyI6Im5vbm9ubyIsInBlcm1pc3Npb25zIjpbMSwyLDMsNCw1LDYsNyw4LDksMTBdLCJpYXQiOjE2OTY5OTM1NjksImV4cCI6MTcyODUyOTU2OX0.b-5UiuaNdFPuwgJMn8Ji3v803OA00qA8aSwetY7XDEY
使用 https://github.com/argoproj/argo-cd/tree/master/manifests/cluster-install 這個簡易安裝
準備一個 kustomizatiom.yaml,然後如果想要客製化就準備其他的 yaml 來調整 最後搭配一個 Application Yaml 來控管自己
$ cat kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/argoproj/argo-cd/manifests/cluster-install?ref=v2.7.7
patchesStrategicMerge:
- argocd-rbac-cm.yaml
- argocd-application-controller.yaml
- argocd-applicationset-controller.yaml
- argocd-cm.yaml
- argocd-cmd-params-cm.yaml
- argocd-dex-server.yaml
- argocd-redis.yaml
- argocd-repo-server.yaml
- argocd-server.yaml
- argocd-notifications-cm.yaml
而所有運行 CircleCI 的專案都可以繼承該 Context 來自動獲取所有環境變數
假設環境需要存取雲端資源,可以把用到的所有資訊寫到一個 Context 內,並且設定好所有環境變數 接下來每個專案的 CircleCI 就只需要使用類似下面的語法
workflows:
test:
jobs:
- test:
filters:
branches:
only: master
context:
- AWS
就會把 AWS Context 內的環境變數都繼承近來,因此未來要維護只需要針對 Context 去維護,就不需要每個專案都去設定
使用上不要使用任何員工的帳號去綁定 Project,不然員工離職很麻煩,最好創建一個 machine account,用該 account 的 ssh key 來串連 GitHub 與 CircleCI,這樣離職才不會產生太多問題
]]>流程需要
前三個步驟可以參考官網設定 Enabling IAP for GKE
apiVersion: v1
kind: Service
metadata:
annotations:
cloud.google.com/backend-config: '{"default": "my-backend"}'
name: dev-service
namespace: dev
spec:
ports:
- name: http2
port: 80
protocol: TCP
targetPort: 8080
- name: https
port: 443
protocol: TCP
targetPort: 8443
selector:
app: service
sessionAffinity: None
type: ClusterIP
一切設定完畢後,接下來還要到 GKE 那邊開啟 IAP 的設定,參考 Setting up IAP access
]]>本篇是簡體中文的文章,所以就不針對文章進行導讀。
文內我覺得有趣的部分是從各個面向來探討 SRE 這個職位的可能內容,畢竟一直以來 DevOps, SRE 等相關概念的詢問就沒有停止過,而事實上每個公司的 DevOps 工程師, SRE 工程師的工作內容也都不盡相同。
也因為不盡相同,本來就很難一言概括到底 SRE 的可能內容,因此個人算是滿推崇本篇文章的分析與整理方式,從不同角度出發去列舉各種可能的工作內容,譬如文章中分成三大類
三種不同類型的 SRE 面對的對象不同,專注的事物也不同,我猜想很多人看完架構類型的論述可能第一個想法就跟以前的 SA/NA 網管有什麼不同?
也許從 SRE 的 R 出發,去探討你今天到底想要針對什麼樣的目標去提供高可用性,也許更能夠方便探討彼此對於 SRE 的需求與認知
]]>本篇文章作者從不同的角度來聊聊 DevOps 這個詞所代表的含義與實作意義
第一段作者先閒聊一下自己與 DevOps 詞的歷史,接者直接拋出一個作者長期好奇的觀點 「每個人都一定聽過 DevOps 是一個需要 Dev + Ops 共同參與的文化,但是作者自己參與的 DevOps 相關會議與討論,與會者大部分都是 Ops 人員,而不是那些真正參與開發的 Dev 人」
接者作者聊聊自身多年前的經驗,當時的開發團隊宣稱該團隊是「true devops」,同時不跟作者的維運團隊討論各種維運需求,這過程讓作者非常困惑,為什麼對方會說自己是 true devops 而又不找自己探討維運需求
作者後來與該開發團隊深聊後終於理解對方的意思,原來該開發團隊身兼開發與維運,該團隊使用 boto3 加上一些腳本來管理應用程式的生命週期,同時該團隊招募的 「full stack engineer」除了基本的後端技術外,也要對 AWS 有不少的熟悉與經驗。
對方的舉動更加困惑了作者,畢竟公司當時採取類似 Netflix 的方式來打造一個平台來讓所有開發者可以更輕鬆的去管理這些東西,而該開發團隊的舉動完全是反其道而行,到底為什麼要這麼做??
當作者加入 Pulumi 時期時,作者開始使用一些知名工具如 GitLab, Terraform, Kubernetes 等工具來打造一個適合開發者的好用平台,然而每次想要將該平台給推廣給開發者時總是屢屢碰壁,總是會聽到如「你們的東西我不熟悉,我們還是習慣自己打造的工具」等類似說詞給打發掉。
作者接下來不斷嘗試說服開發團隊來使用自己打造的超級平台,鼓勵他們參加 DevOps 相關活動等各種方式,最終得到的還是類似「我們會按照我們自己的方式去嘗試~謝囉」之類的回覆
回顧過往,作者發現錯的是自己,一直以來相信的 DevOps 願景「讓 Ops 停止說 No, 讓 Dev 停止說"yo~ 今天部署吧"」 其實並不真實,作者認為 2022 的今天, DevOps 真正的含義是 「維運端的人努力說服開發人員按照維運人員的想法去做事情」
綜觀所有號稱跟 DevOps 有關的工具,你會發現幾乎都跟維運有關,每個跟 DevOps 有關的職缺列舉的技能也是滿滿的跟維運有關,對作者來說, DevOps 工程師跟過往的 System Admin 根本沒有太大分別,差異只有把「實體機房建置,上架機器」 v.s 「雲端機器建置,創立VM」而已。
文章內後半部分還有一些作者的想法,有興趣的可以閱讀完畢
本篇文章的想法滿有趣的,譬如作者提到想要幫開發團隊建立一個維運平台卻屢屢碰壁。
Ops 可能會覺得 Dev 一直不停重複打造工具沒有效率,不如使用自己打造的好平台 Dev 可能會覺得 Ops 不懂自己的需求,不如自己根據需求打造
同樣的敘述放到不同的規模,譬如
dev -> 5 人的專職開發團隊 dev -> 50 人的專職產品團隊
後者的角度也許會覺得團隊人數夠多,可以自己處理自己的需求,不需要仰賴公司提供一個萬能平台來處理一切,同時跨 team 合作可能還會使得很多事情效率低落,溝通成本過大。
歡迎留言探討你的想法
]]>本篇文章是一個面試技術文,探討開發一個類似 Job Scheduler 的專案時應該要如何去設計整體系統來完成需求,整體的架構基於 KISS 的原則,就是簡單為主。
整個流程原則基本上是
功能面常見類型如
非直接功能面如
有了功能面的需求,接下來就是數量大小的需求,譬如該架構要可以達到每秒 1000 個 Job(1000QPS), 從這些需求下去估算大概需要多少 CPU 以及多少 Memory,同時這些數量還可以滿足功能面的需求,譬如每個 Job 可以運行最多五分鐘。
所以也許會得到需要 10,000 台的 (16C) 機器,以及 100 台(16GB) 的機器來提供服務 基本的運算可以快速的理解該需求到底需不需要分散式的架構來處理,本文的範例資料量就很明顯是 scale up 沒有辦法完成的。
接下來就基於分散式的架構去設計相關架構,包含如
逐步的規劃這些架構,並且探討彼此元件之間的溝通方式,這些方式是如何互相組合來滿足功能面/非功能面的需求
詳細需求可以參考全文
]]>Cloudflare 官方文章詳細解釋 06/21/2022 當天到底發生什麼事情導致用戶受到影響,
這次的問題影響範圍概括了 Cloudflare 底下的 19 個資料中心,而很不幸的這 19 個資料中心剛好都是負責處理繁忙的全球流量,所以受到影響的用戶數量才會如此的多。 問題主因是網路設定的調整(有問題先猜BGP,不行再猜DNS...),整體的發生時間沒有非常長
過去 18 個月以來, Cloudflare 致力於將其底下繁忙的資料中心進行架構改造來達成更為堅韌與彈性的網路架構,內部稱該架構為 Multi-Colo POP(MCP),影響的 19 個資料中心包含 Tokyo, Singapore ... 等
新架構最重要的部分就是其網路的部分是基於 Clos network 的架構設計,透過多層次的設計達成類似 mesh network 般的網路連結,該架構使得未來要維護與調整時能夠更輕鬆針對部分網路設備去處理而不會影響到整體網路(文章有架構圖片)。
這次的問題主要跟 BGP 有關,Cloudflare 更新 BGP 的過程中有部分的 subnet 沒有順利的被傳遞出去,最終使得部分 subnet 的流量無法被順利轉發,進而導致整個網路問題。
文章內部有針對 BGP 問題更詳細的介紹,熟悉 BGP 的朋友可以花點時間看一下
這次的問題影響範圍很廣,Cloudflare 針對下列三面向反思了一下問題的發生原因
雖然嶄新的 MCP 架構其目的就是要提供更好更強的可用性,但是將舊架構給升級到新架構的過程中還是不夠完善。整體的更新流程直到最後一步驟才算是真正的接觸到全新 MCP 架構,這使得如果中間更新流程有錯必須要到最後才會觀察到 MCP 資料中心的網路炸了。 改善的方式則是未來的這些流程與自動化必須要加入更多關於 MCP 架構的測試來確保整體部署不會遇到預期外的結果。
路由器的錯誤設定使得正確的路由規則沒有辦法順利的被傳達下去,最終使得網路封包無法如預期般地到達這些資料中心。 所以修復過程中就是要找出這些錯誤的設定並且修正,最終使得這些 BGP 能夠將正確的路由政策給轉發下去。
當前的自動化流程中有非常多的部分可以改進,這些改進有機會完全或是部分的去減緩問題發生時的影響程度。 有兩個目標是想要透過改善自動化機制達成的
CDN 不通先上社群看同業有沒有哀嚎,大概就可以知道是不是自己的問題了?
]]>本篇文章探討的是一個關於 Ubuntu kernel(5.13+) bug 產生的各種悲劇,已知受害的雲端業者包含
linux-oracle linux-azure linux-gcp linux-aws
等常見大廠。
簡單來說,預設設定下只要簡單跑一個 container 譬如
docker run -it ubuntu bash
就可以直接觸發 kernel panic,直接讓你系統死亡強迫重啟
整個 bug 結論來說就是,一連串的操作最後有機會導致使用到一個 null pointer,然後 kernel 就炸拉...
相關的修復可以參閱這個連結,裡面有大概提到問題發生點以及修復方式。 https://kernel.ubuntu.com/git/ubuntu/ubuntu-impish.git/commit/?id=6a6dd081d512c812a937503d5949e4479340accb
]]>本篇文章是探討分散式系統上很常被開發者所忽略的網路情況,這些情境都容易被忽略與考慮,但是每個點實際上都會影響整個系統的效能與功能
這些常常被忽略的網路情況包含
開發分散式系統的時候,一定要去考慮網路壞掉的情況,切記網路中的任何傳輸都不是 100% 穩定的。千萬不要假設所有封包與傳輸都沒有問題,必要時還要考慮重新連線,重新傳輸的情況。
網路時間還有一個要注意的就是延遲時間,通常 Client/Server 如果都是同一個系統內的服務時,這類型的時間可能非常短,如 ms 等級。 但是當 client 可能是來自真實使用者的手機裝置時,就要將 latency 這些因素給考慮進去,不能假設所有的 API 與網路請求都是秒回的情況。
更常見的還有導入 CDN 等方式透過地理性的位置來減少 client/server 之間要傳輸的距離。
文章內針對剩下的類別都有簡單的圖文並茂來解釋,淺顯易懂,有興趣的可以參閱全文
]]>工程師想必對於 DRY, KISS(Keep It Simple, Stupid), YAGNI(You Ain’t Gonna Need It) 這些廣為流傳的開發原則並不陌生,這些原則都是過去許許多多優秀工程師透過自己的經驗而濃縮的開發準則。
但是作者觀察到有滿多工程師對於這些 開發原則/命名標準/最佳實驗經驗 等採取一個不信任態度,甚至覺得導入這些東西都是浪費時間。 因此本文章是作者探討什麼樣的工程師可能會不太願意去學習與導入這些廣為流傳的開發原則與經驗
作者開宗明義地說,小專案的成功經驗基本上是沒有辦法導入到大專案的開發的,小專案的特型譬如 1) 合作人員很少 2)專案時間少 這類型的特性只得技術債或是欠缺設計的程式架構不太會影響整個專案,畢竟專案太小,時間太短,後續的人不一定有機會真的觀察到這些潛在的問題。
而小專案還有一個很大的特性就是後續維護的人很有可能跟當初的開發者不同,所以對於開發者來說非常難去感受後續維護的痛苦與需求。
上述特性有可能會使得開發者覺得自己的開發經驗非常足夠且堪用,因此就會基於這樣的經驗來抵抗其他更多被推廣且推崇的開發原則與最佳實戰經驗。
因此對於一些只開發過小專案且沒有後續維護的工程師來說,其有可能就不會想要學習這些原則與經驗,畢竟自己的工作流程根本沒有機會感受到好處。
簡單來說就是「劣幣逐良幣」的概念,從工程師開發的角度來看,寫一個「可能比較好維護,未來比較不會有問題,高品質的程式碼」如果實務上不會帶來任何好處,甚至可能「績效表現比較不好」的情況下,那為什麼工程師要努力寫出高品質的程式碼?
軟體團隊除了開發者之外,還會有相關的專案管理人員,產品管理人員以及最終使用者。 對於非技術人員來說,其在意的點更有可能專注於「程式開發速度,產品上線速度」,至於專案的後續維護性,開發靈活性等長時間才會看到的問題都不是他們所在意的點。
這種情況下,如何評價一個工程師的能力很有可能就變成「能夠多快的滿足專案需求,而不是寫出的程式碼品質」,所以對於工程師來說,快速寫出功能不考慮後續其他維護等長期問題反而更可能受到團隊重視,因為「功能出的快」。
所以如果團隊沒有辦法好好的去重視「高品質程式碼等考慮長期問題的開發原則」就有可能會導致工程師不會想要好好的去撰寫好的程式碼,長期下來這些工程師就可能會開始拒絕學習各種開發原則與最佳實踐的經驗,畢竟導入這些東西沒有任何實質上的幫助,反而初期可能會降低功能的上線速度。
「知彼知己,百戰百勝」,沒有花時間去學習這些開發原則的前後脈絡與適用情景就沒有辦法很順利的導入到開發專案中,所以實際上這些導入都要工程師花時間去學習與理解,然後嘗試導入與使用。
然而並不是每個工程師都願意花時間去學習,畢竟平常工作就是寫寫程式碼,寫寫文件,結果來說能動就好。花這些時間去學習這些東西
作者認為很多工程師「其實都不知道自己不知道什麼」,這導致他們很難去學習新技術與新概念,畢竟未知領域帶來的好處與優勢不是他們工作經驗中有機會去體驗到的,就如同前面所述,對一個擅長開發短期專案就拋棄給別人維護的人來說,其很難體會到各種長期維護與技術債的問題。 practices.
另外一個非常常見的問題就是「導入開發原則與好的開發經驗」與否對於初期開發來說很有可能沒有很任何明顯的差異。 從短期目標來看,兩者開發角度產生的結果不會差異太大,但是對於只有幾個月的小專案來說,後者甚至能夠更快的完成需求然後拍拍屁股結束閃人。
架構複雜性,技術債以及其他的爛程式碼可能產生的後續問題都需要時間發酵,所以團隊事主如果沒有辦法以長期觀念來看到程式開發的話,上述的問題就沒有辦法被重視,就如同前面所述,開發者就會「速度為主,品質為輔」的概念來開發,至於後續維護痛苦與否就是後續接手人的事情。
剩下其他論點就可以到原文去觀賞
本篇文章是來自於 Chaos Mesh 內的官方文章,主要是想要探討為什麼使用 Chaso Mesh 來測試記憶體狀況時結果實際狀況與設定的狀況不一致 文章一步一步的探討所有問題最後同時也整理了一些關於 Kubernetes 內的 Memory 相關機制
文章開頭,作者先部署了一個簡單的 Pod(只有一個 container),該 Pod 針對 Memory 的部分設定 request: 200Mi, limits: 500Mi 結果作者到該 Container 中透過 free 與 top 的指令,都觀察到顯示的記憶體使用量高達 4G,這部分明顯與設定的 limits 500Mi 有所衝突 因此這邊產生了第一個點要特別注意
Kubernetes 是透過 cgroup 來計算與控管 Pod 的 Memory 用量,然而 free/top 等指令並沒有跟 cgroup 整合,因此就算跑到 container 中執行這兩個 指令看到的輸出其實都是 host 相關的,如果想要知道真正 container 相關的數量,還是要使用 cgroup 相關的指令來取得,譬如 cat /sys/fs/cgroup/memory/memory.usage_in_bytes
文章還有特別提到 Kubernetes 會針對 Request/Limit 的設定方式來將你的 Pod 分為三個等級,分別是 BestEffort, Burstable 以及 Guaranteed 其中當系統因為 OOM 不足要開始找受害者下手時,被設定為 Guaranteed 的應用程式則會是最低優先度,只有真的找不到其他受害者時才會來處理 Guaranteed 類型的 Pod。
最後則是更細部的去探討 Kubernetes 關於 Memory 的使用與管理 對於 Kubernetes 來說, 當系統上 Memory 數量不足時,可能會觸發 Evict 的行為,開始將部分運行的 Pod 給踢出該節點,而如同前面所述, Kubernetes 是依賴 Cgroup 來處理的,因此 /sys/fs/cgroup/memory/memory.usage_in_bytes 自然而然就成為其決策的重要參數
其中要注意的是 /sys/fs/cgroup/memory/memory.usage_in_bytes 代表的並不是 "剛剛好系統上正在被使用的 Memory 數量",其數值則是由 "resident set", "cache", "total_inactive_file" 等三個面向組合而成,因此 Kubernetes 實際上會先從 /sys/fs/cgroup/memory/memory.usage_in_bytes 與 /sys/fs/cgroup/memory/memory.stat 取得相關參數,其中後者可以得到如 total_inactive_file 的數量 最後透過下列算式 working_set = usage_in_bytes - total_inactive_file 來得到一個名為 working_set 變數,該變數實際上也可以由 kubectl top 獲取,這也是 kubernetes 用來判斷是否執行 evict 的主要指標。
一個節點還有多少可用的 Memory 則是透過 memory.available = nodes.status.capacity[memory] - working_set 所以每個節點的總共量扣掉 workign_set 就是當前可用量,一旦當前可用量低於門檻時,也就是 k8s 執行 evict 之時 官網文件中其實有滿仔細的去描述這些操作行為 有興趣的可以花點時間全部看完 https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/
]]>本篇文章要探討的是到底 /proc/meminfo 與 free 這個指令所列出來的 memory 相關資訊到底該怎麼匹配
雖然文章有特別強調主要是針對 RedHat Enterprise Linux 5,6,7,8,9,但是我認為大部分的 Linux 發行版的差異不會太大,畢竟整體都是來自於 Kernel 內的實作,我認為還是值得閱讀與理解。
對於大部分的系統管理員來說,勢必都有聽過 free 這個指令,該指令可以列出系統上當前的 memory 使用狀況,舉例來說通常會有 Total, Used, Free, Shared, Buffers, Cached 之類的欄位(不同版本可能會有些許差異)。 不熟悉的人可能會認為系統上的記憶體就只有“全部“,"使用中","閒置" 等三種類型,而實際上的記憶體處理遠比這些複雜,這也是為什麼 free 的輸出欄位會比較多的原因
除了 Free 指令外, Kernel 本身還有提供一個特殊的檔案位置讓使用者可以讀取當前的 memory 狀況,該位置為 /proc/memifno,其會提供如 MemTotal, MemFree, Buffers, Cached 等相關欄位
本文並不會針對每個欄位去探討實際上的意義,取而代之的是簡單的比對,透過幾個列表讓你清楚的知道 free 指令輸出的每個欄位要如何與 /proc/meminfo 去比較,要如何轉換等 特別要注意的是文章內有仔細地針對不同 RedHat Enterprise Linux 版本去分別探討,所以如果是 RedHat 系列的使用者更要好得閱讀並確保能夠理解自己當前使用版本的狀況
]]>今天要介紹的是一個驗證工具 goss,該工具的目的非常簡單,讓系統管理員可以透過 YAML 的方式幫機器上的服務撰寫 Unit Testing 什麼情況會需要使用這類型工具?
舉例來說,當你今天部署了一個全新機器(手動/自動後),你安裝了下列軟體
同時你也根據需求事先創建了一些使用者,接者你想要驗證這些軟體與相關設定是否設定完成 最直覺的方式就是手動檢查,一個一個服務與設定人工檢查
而 goss 這套軟體的目的就是讓你用 YAML 的方式去撰寫你想要驗證的所有服務,可以用來驗證包含
Goss 除了基本用法外,也有人基於其概念往上疊加 dgoss,用來驗證 Docker 的運行狀態,還有類似的 dcgoss,針對 docker-compose 來使用。 當然目前也很多人會透過 Ansible 的方式來自動化部屬,而 Ansible 本身其實也有相關的測試框架可以用來測試部署結果,所以到底要用哪類型的工具 來驗證 Server 等級的狀態就根據團隊需求與現有流程而定,比較沒有一個獨大的工具用法。
]]>本篇文章非常短,大意就是探討透過文字討論事項時如何讓這些訊息更有意義,能夠讓目標受眾可以更快的理解該訊息的意義 假如今天有人想要反應「This is not worded correctly.」的概念,作者認為相對於直接撰寫「該文字措辭不當」,可以適當的加上一些是先定義好的前綴形容詞 譬如 「suggestion: This is not worded correctly. Can we change this to match the wording of the marketing page?」
「nitpick (non-blocking): This is not worded correctly.」
透過這些有共識的形容詞可以讓團隊之間的溝通速度快,減少誤解與猜測的可能性,讓整體的溝通效率更高,譬如 「suggestion: Let's avoid using this specific function… If we reference much of a function marked Deprecated, it is almost certain to disagree with us, sooner or later.」
「issue (ux,non-blocking): These buttons should be red, but let's handle this in a follow-up.」
透過這些形容詞能夠提醒目標受眾該討論的一些概念,同時也能夠讓對方更有想法下一步驟可以怎麼做。 作者就自己的習慣列舉了幾個下列前綴形容詞
說到底這類型的討論都是一個習慣,就如同 coding style 一樣,所有共事者有一個共識原則,大家合作起來會更加有效率有方便 文中說的方法也不是唯一的辦法,但是團隊內有一個準則文化絕對會帶來好處的
]]>作者開門見山提到,如果團隊中沒有任何 code review 文化的話,請直接忽略這篇文章。 當團隊真的有 code review 的經驗時,才有機會透過本篇文章分享的一些概念來改善整個 code review 的流程,高效率低耗時。
作者認為一個好品質的 code review 能夠幫助團隊帶來下列好處
為了讓上述概念可以充分的導入到團隊專案中,作者分享了一些自己常用的概念與招式
事先準備一份 Checklist 一個好的 review 流程就是要有一份檢查清單,這份清單上面描述的是每次程式碼合併都“必須”要符合的規則,同時也是團隊很重視的規則 這份清單沒有絕對標準,主要是根據團隊去思考哪些東西是最重要的,舉例來說
這份清單的重點是只要列入那些被視為是非常必須且重要的項目就好,不然整個清單落落長其實意義也不高
盡可能的自動化上述檢查 準備好前述清單後,下一個步驟就是想辦法將上述清單規範給自動化,譬如
當有辦法自動化這些操作後,下一個步驟就是要思考什麼時候執行?
一切都準備完畢後就可以將其整合到整個 git 工具中,譬如只有當 CI pipeline 通過的 PR 才有被人 review 的需求,如果連自動化測試都沒有辦法通過,那就是開發者的 責任要去將其完成,一切準備就緒後才要開始最後一步
作者也推薦採用 IDE 來進行 code review,很多 IDE 強大的功能都能夠幫助開發者更有效率地去檢視程式碼,譬如快速找到宣告點,被呼叫點以及整個資料結構的面貌等 這些都可以省下不少時間
最後最重要的是每次 PR 的大小不能太大,這點其實也是 Linux Kernel 內一直奉行的原則,過大的修改有太多檔案要看,同時也有更多可能潛在的不相容問題要注意 這對開發者與 reviewer 來說都是個沈重的負擔,因此能的話將修改以拆分成數個有意義的 PR 分別檢視會使得整體流程更講有效率,同時也可以避免 檔案太多時可能看不下去就直接無腦 +2 的蓋章行為
]]>Mizu 是一個專門針對 Kubernetes 開發的流量分析工具,該工具透過簡單好用的 UI 讓你檢視叢集內的流量走向,其支持的協定有 HTTP, REST, gRPC, Kafka, AMQP 以及 Redis 等相關的應用程式封包。
雖然說透過大部分的 Service Mesh 也都可以提供一樣的功能,但是 Mizu 的特色就是其輕量的架構設計,就是針對流量分析而已,所以如果團隊目前沒有現成的解決方案時, 可以考慮試試看 Mizu 這套輕量級的解決方案。
Mizu 本身由兩個元件組成,分別是 CLI 以及 Agent,當你安裝 Mizu 的 Kubernetes 內時,其會安裝兩個元件
Agent 會針對需求去抓取節點上特定的 TCP 封包(目前也只有支援 TCP 流量,這意味如 ICMP, UDP, SCTP 等就沒有辦法),此外要特別注意這類型的解決方案為了能夠 抓取到節點上的所有流量,通常都會讓這類型的 Agent 都設定為 hostnetwork: true,如此一來該 Agent 才有辦法觀察到節點上的所有網卡來進行流量擷取。
有些 k8s 環境會透過如 OPA(Gatekeeper) 等機制去控管要求所有的 Pod 不准使用 hostnetwork,有這些規範的可能就要注意一下整合上的問題。
有興趣的可以稍微玩看看,看看這類型的工具是否可以幫忙除錯
]]>Cillium 的開發團隊 isovalent 最近公布其內部一直使用的資安相關專案, Teragon (可愛的蜜蜂戰士)。
Teragon 底層是基於 eBPF 的技術,其目的就是讓你的 Kubernetes 於資安方面可以獲得超級強大的能力,包含
探討 Teragon 前,要先理解以前目前已知的相關解決方案有哪些,而這些解決方案又有什麼樣的優缺點,包含
上述六個方式都有各自的特點,這邊簡單敘述
App Instrumentation O 效率高,可以看到非常細部的資訊 X 程式碼需要修改,不夠透明 X 單純的視覺化,不能套入資安規則來防護應用程式 X 應用程式為主,不能理解整個系統的狀況
LD_PRELOAD (動態切換載入的 Library ) O 效率高 O 應用程式不需要修改 X 如果是 Static Llinking 的應用程式那就沒有用了 X 幾乎沒有什麼觀察性可言
ptrace (透過 kernel 提供的功能來檢視使用的 syscall) O 透明,應用程式不用修改 X 效能負擔比較高 X 應用程式有辦法偵測到自己目前被 ptrace 給監控 X 整體範圍只能針對 syscall(系統呼叫)
seccomp (可以過濾應用程式呼叫的 syscall) O 有效率,應用程式不需要修改 X 規則只能針對 syscall 去阻擋 X 沒有很好的視覺化方式
SELinux/LSM (Kernel 內建的 security 框架,可以針對存取去控制) O 有效率,應用程式不需要修改 O 可防 TOCTTOU 攻擊 X 針對 Contaienr/Kubernetes 的整合很有限 X 不容易擴充 X 要針對攻擊類型去設定
Kernel Module O 有效率,應用程式不需要修改 O 不用修改 Kernel 就可以擴充功能 X 不是每個環境都允許使用者去載入 kenrel Module X Module 有問題會打爆你的 Kernel X 沒辦法無縫升級,意味你升級功能的過程中必須要將kernel module給 uninstall ,然後重新安裝
上列六個解決方案有的只能檢視相關流程,有的只能設定規則去防護,但是就是沒有一個工具可以全面處理,而基於 eBPF 實作的 Tetragon 則是一個 能夠提供兩項功能的全新解決方案。
首先資安防護方面, Tetragon 採取的是更底層的概念,不去探討特定的 CVE 操作手法,取而代之的是從幾個常見的攻擊方式來防禦。 假如有任何應用程式有不預期的下列行為,就可以直接將該 Process 移除
這些規則都可以透過 Kubernetes CRD 來描述,當這些規則被送到 Kubernetes 後,相關的 Controller 就會將規則給轉換後續讓 eBPF 來處理 此外因為 eBPF 以及 kprobe 的架構,Tetragon 能夠看到非常多 kernel 的資源存取與操作,譬如
Tetragon 收集上列不同資訊的資料後進行二次處理,透過精美的網頁來顯示系統中的各種資訊,這些資訊可以提供包含
eBPF 的應用愈來愈多,而目前看起來 isovalent 更是 Kubernetes 生態系中的領頭羊,雖然不確定未來是否能夠被廣泛採用,但是至少這方面還沒有看到其他解決方案有這麼積極的基於 eBPF 來開發 有餘力的話花點時間學習一下 eBPF 的概念可以加強自己對於這類型文章的速度與理解度
]]>如同各類程式語言的測試框架, Kubernetes 的部署文件(YAML)實際上也是可以導入 CI 的概念,那到底 YAML 檔案有什麼東西需要檢驗? 最基本的概念大致上可以分成三種
除了基本的 YAML 部署外,還要考慮一下團隊是採用何種方式來管理 Kubernetes App,譬如原生 YAML, Helm, Kustomize 等各種不同方法。
(1) 的話其實最基本的方式就是使用 yq 指令,其本身就可以檢查基本的 YAML 語法,如果是 Helm 的使用者也可以透過 Helm template 的方式來嘗試渲染,渲染的過程也會幫忙檢查 YAML 的合法性。 (2) 的話其實也有其他如 kubeval 等類型的工具去幫忙檢驗 YAML 內容是否符合 Kubernees Scheme,這邊要特別注意的還有版本問題,畢竟每次升級都會有很多 API Version 被調整 (3) 的話講究的是規範,譬如要求所有 workload 都必須要描述 CPU/Memory 的Request/Limit,或是要求所有容器都要以 non-root 的身份運行, 這部分有如 kube-score,或是基於 REGO 的 conftest 等工具可以檢測。
而今天分享的這個工具 datree 基本上就是一個人包辦上述三個工具,該工具基本上有兩種模式使用
基本上這類型的工具愈來愈多,找到一個適合團隊的工具將其整合到 CI 中,讓團隊的 Kubernetes YAML 都能夠符合團隊規範,同時也透過 CI 的流程盡可能提早地找出問題
]]>本篇文章是 2021末 由 Cilium 背後的 isovalent 公司團隊所發表的文章,主要探討一個全新的 Service Mesh 的架構可能帶來的好處,整篇文章以 Cillium + eBPF 為背景去探討 我認為如果對於 eBPF 沒有全面理解的情況下,其實只能讀懂這篇文章想要帶來的果,沒有辦法去理解到底整體實作與運作原理,同時因為 eBPF 本身的用途除了網路(Cilium)之外有愈來愈多的底層除錯工具都是透過 eBPF 的概念來實作的,因此學習 eBPF 的概念其實帶來的好處很多,有空的都推薦大家花點時間去學習。
本文主要分成幾個部分
隨者分散式應用程式架構的興起,如何針對這些散落各地的應用程式提供關於網路連線方面的資訊一直以來都是維運上的問題,過往最簡單的方式就是針對各種開發環境導入相關框架 每個應用程式都需要修改來整合這些框架,但是隨者整個架構發展與要求愈來愈多,譬如開發環境有不同程式語言,甚至有不可修改的第三方應用程式,除了網路監控外還想要導入認證授權,負載平衡等各種功能 要求每個應用程式開發者引用這些框架已經沒有辦法漂亮的滿足所有需求,因此一個能夠無視應用程式本體的透明性框架架構就變成眾人追捧與渴望的解決方案。
現今大部分的 Service Mesh 就是採取這種透明性的架構,透過額外 Proxy 來攔截應用程式的封包進行後續管理與監控,使得
以 kubernetes 來說,目前主流都是透過 sidecar 的概念,讓每個應用程式旁邊都放一個 Proxy 的應用程式,同時基於 Pod 內 Containers 可以使用 localhost 互通的方式來處理連線。 應用程式本身都透過 localhost 打到 Proxy,而所有對外連線都讓 Proxy 幫忙處理,因此所有的進階功能都實作於該 Proxy 上。
Isovalent 認為這種方式功能面上可行,但是認為如果導入 Sidecar 其實有很多隱性成本
所以 Cillium/Isovalent 想要引入基於 eBPF 的架構來打造一個不同架構的 Service Mesh。透過 eBPF 的架構使得整個 Service Mesh 的發生點是發生於 Kernel 階段,而非一個獨立的 Uses Proxy。 這邊帶來的改變有
非常對於這系列戰爭有興趣的人花點時間去把 eBPF 的概念補齊,接下來針對這系列的大戰與討論就能夠有更多的背景去理解
]]>本篇文章的重點很簡單
這邊節錄文章中列出的所有書籍
Kubernetes Up & Running — Dive into the Future of Infrastructure Kubernetes 從 2014 發行以來的八個年頭席捲全世界,作為一個 DevOps 不論你當下的環境適不適合使用 Kubernetes,你都必須要瞭解到底這個容器管理平台的魅力是什麼 為什麼可以打趴眾多競爭者成為所有容器管理平台的主要首選。 本書從開發者(Dev)以及維運者(Ops)的角度來看到底 Kubernetes 是如何提升整體工作的效率,速度與整體的靈活度
Designing Distributed Systems — Patterns and Paradigms for Scalable, Reliable Services 這本由 Brendan Burns 所攥寫的書籍探討了分散式系統架構上幾個常見的設計模式,事實上這些設計模式有些都可以於 Kubernetes 的設計與用法中反覆發現 所以花點時間去研究一下大師所分享的分散式系統模式的設計理念,對於未來去學習理解新系統,或是設計一套系統都會有所幫助
97 Things Every Cloud Engineer Should Know — Collective Wisdom from the Experts 這本有紅帽所發行的免費書籍,書中收集了眾多資深雲端工程師的經驗,列舉了 97 個每個雲端工程師都應該要知道的事情,這 97 項包含很多東西,譬如 資料,自動化,網路,公司文化,個人發展,軟體開發以及雲端預算評估等眾多常見議題
Linux — Notes for Professionals
Production Kubernetes — Building Successful Application Platforms
Git — Notes for Professionals
Automate The Boring Stuff with Python — Practical Programming For Total Beginners
剩下的書本也都非常有趣,大家有需要時可以閱讀下列書籍
]]>本篇文章作者分享自己學習與使用 Terraform 多年來遇過的各種雷,也希望藉由這類型的文章可以讓每個踏入 Terraform 的人都不要走冤枉路
Make sure you have a terraform block in your configuration TF 檔案中可以透過 Terraform 區塊來描述關於 Terraform 本身的一些限制,譬如版本條件,相關的 provider 來源以及版本。 這個區塊非常重要但是本身是一個 optional 選項,所以不寫其實不影響整體功能,但是沒有去限制使用的版本範圍其實就跟任何的軟體環境一樣非常危險, 很容易踩到「昨天還可以,今天就不行的」通靈現象,所以作者希望每個人都好好的將 Terraform 區塊描述清楚,確定當前支援的版本是哪個確保該 TF 能夠用正確的版本於任何環境執行
Statefile 實際上本身是純文字格式,作者想要提醒的是 State 檔案作為 Terraform 同步上最重要的檔案,其本身是一個純文字明碼的格式,這意味你運行過程中的任何帳號密碼其實都是純文字的格式存放於該檔案中。 所以 State 檔案的保存非常重要,需要用很嚴肅的資安態度來保護這個檔案,否則該檔案被人取得則你 TF 中的各種資訊都會被對方取得。 作者直接於文章中展示一個範例,該範例會創建一個 AWS aws_secretsmanager_secret_version,而該物件的 secret_id, secret_string 都會以明碼的方式被存放於 State 檔案中。
Have verbose variables and outputs blocks TF 中的所有變數都可以用非常簡易的方式去宣告,但是如果妥善地利用這些內建的功能將可以使得變數的使用變得更加方便,特別是當該變數要跨 Module 使用時,呼叫者可以透過更輕易的方式 去理解該變數的格式與用法。 其中最為重要的則是 validation 的內容,作者以 AWS image_id 為範例,該變數基本上就是一個字串,所以使用者可以傳遞任何變數到該欄位去使用,但是如果搭配 validation,就可以讓 TF Apply 提早 先觀察到這些變數是否合法,能夠降低與避免不必要的失敗。 所以針對每個變數都好好的撰寫相關敘述與驗證,能夠讓團隊使用上減少無謂的猜想與溝通。
Integrate your environment with a pipeline early Terraform 的入門非常容易,但是當你想要將 Terraform 導入到團隊中並且與其他人共同合作時,整個使用上的複雜度會大幅度增加。 作者認為如果真的要導入 Terraform 到整個團隊中,則要盡快且盡可能地將 Terraform 導入到現有的 pipeline 架構中,譬如 Terraform Cloud 服務 能夠幫你妥善的管理這些 Lock/State 並且透過 Terraform Apply 來執行變化。
作者還有第二篇探討剩下的用法,包含 Keep your code together as much as possible Have clear lines of demarcation on responsibility Use multiple environment files for the same code Familiarise yourself with HCL’s functions and meta-arguments Terraform is not a golden bullet
有興趣的讀者建議兩篇文章都閱讀一下
]]>作者提到大部分遇到 Container 權限問題時,最無腦的一招就是 --privileged 直接硬上權限,但是其實大家都不知道自己到底缺少什麼權限,盲目地使用 --privileged 的確可以解決問題 但是實務上卻是犧牲 Security 換來的,因為不知道缺少什麼而直接硬開,其實就是硬生生的將幾乎所有保護功能都關閉。
本篇文章就來探討當遇到權限問題時有可能是什麼造成的,以及應該如何精準地去設定這些權限而不是用一招 --privileged 跳過。 此外由於作者本身就是 Podman 開發團隊,因此文章之後的介紹與範例都會基於 Podman 來完成,
如果你的容器問題透過 --privileged 也不能解決,那至少你的問題跟本篇文章的關聯性不大,或是說你的問題其實根本不是安全性方面的設定問題,只有當妳確認你的問題 可以因為 --privileged 而解決時本篇文章的內容才會對你有幫助
除了上述五個安全性設定外,作者也針對 namespace 探討可能會出現的問題,包含
最後就是不免俗的推薦大家使用看看 rootless container,畢竟大部分的應用程式其實都沒有要寫入系統的需求,理論上來說應該都要可以運行於 rootless 的模式
整篇文章整理的非常的好,每個類別都有指令操作來介紹概念,對於這些資安控管不熟的人來說可以說是一個溫習的好機會
]]>作者認為網路上有很多文章分享想要成為一個軟體架構師應該要閱讀哪些書籍來補充知識,但是這些文章都沒有提供一個好的閱讀路徑,沒有告訴你說 這些書有什麼樣的前置條件,這群書有什麼樣的閱讀順序等,這很容易造成讀者沒有系統的四處閱讀,容易導致無聊與沮喪。
作者根據自己的經驗整理特這些書籍,並且從中找到一個閱讀順序,透過這些閱讀順序可以讓你掌握每本書籍的前置知識同時也能夠有更好的知識去思考書本所談論的內容。
作者認為軟體架構實際上還可以根據領域進行二次細分,包含
不同專項其內榮與知識都不同,因此閱讀時的路徑也會不同。所以本篇文章實際是個系列文,總共會有四篇 本篇是一個探討大綱的文章,探討一下基本概念,而後續系列文則是會針對上述三個不同面向去深度探討該怎麼閱讀
要認真踏入軟體架構前,必須要先掌握基本概念,如相關技術與工具,而作者認為學習這些基本概念的路徑就是所謂的 Design Path. Design Path 中將會學習到
針對這 Design Path,作者推薦依照順序閱讀下列書籍
掌握好 Design Path 後,下一個就是 Architecture Fundamentals 的技術掌握,該過程要學習關於架構的基本概念,原則,模式與實踐方式,閱讀書籍如下
本文作者想要分享自己過去五年來使用 Serveless 的經驗談,從不同角度切入導入 Serveless 後的痛點。 作者的 serverless 環境是基於 AWS 環境,使用了包含
作者提及了幾個痛點,包含
這篇文章最有趣的點不是文章本身,而是底下的留言討論,雖然有少數留言是支持作者但是大部分的人都是秉持反對的意見來看這篇文章。 我自己的角度是這篇文章提出非常多問題,但是這些問題我看不太出來跟 Serveless 的關係是什麼,更多的是公司的文化,工程品質與開發工具有關 譬如作者說團隊內有很多非資深工程師會因為 serveless 的易用而依賴自己的想法去攥寫,譬如光 Auth 就有十種不同方式。 但是仔細思考這個問題,似乎 server-based 的架構也會有這問題,完全是公司的文化與規範問題。 其他問題還有很多寫 serveless 的人都沒有 HTTP 的深厚底子,所以 200,400,500 想回就回,然後回傳格式也都沒有統一固定 這些東西其實跟 serverless 也沒有直接關係,更多依然是 Code Review 的問題,工程師品質的問題。
所以有時候看文章除了單純閱讀外,也要思考一下作者講的東西自己是否認同,同時也可以看下留言處,來自不同文化與團隊的留言往往能夠帶來更大的啟發,也是閱讀網路文章上我覺得非常有價值的地方
]]>熟悉 Kubernetes 的使用者一定對於各式各樣的資源格式感到不陌生,譬如描寫一個 Pod 需要準備些關於 containers 的基本資料,其餘還有 Label, Annotation 等 各種資料需要填寫。
Kubernetes 內透過 apimachinery 的方式來驗證每個欄位是不是合法,譬如最常見的就是創建資源名稱時有時候會因為等出現格式不符合,準確來說是 Pod 的方式來驗證每個欄位是不是合法,譬如最常見的就是創建資源名稱時有時候會因為等出現格式不符合,準確來說是 透過 DNS RFC 1123 來驗證 Pod 是否合法。 部分的數值資料可能會於 Controller 中額外去檢查,至於自定義的 CRD(Customer Resource Definition) 則是創建時可以透過 openAPIV3Schema 去定義每個欄位的合法數值。
今天這篇文章要介紹的問題是跟 istio 環境的問題,當使用者創建一個名為 Gateway 的資源到叢集中時, istio 會去讀取該 Gateway 資料並且轉換為 Service/Deployment 兩個底層資源。 作者仔細研究發現創建 Service 時會從 Gateway 中的 Annotation 找到名為 "networking.istio.io/service-type" 的資料,並用其作為 Serivce 的 type.
然而 Annotation 的數值沒有並沒有任何檢查機制,所以使用者可以於該欄位 "networking.istio.io/service-type" 填入各種數值,因此作者就嘗試撰寫一個非常長的 Annotation,譬如
annotations:
networking.istio.io/service-type: |-
"LoadBalancer"
apiVersion: apps/v1
kind: Deployment
metadata:
name: pwned-deployment
namespace: istio-ingress
spec:
selector:
matchLabels:
app: nginx
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.3
ports:
- containerPort: 80
securityContext:
privileged: true
結果非常順利的, isio 最終創造了一個作者故意描述的 deployment,而該 deployment 還特別的設定 privileged: true 的選項並且透過這次的測試證明該 YAML 的檢查問題導致使用者有機會插入任何想要的資源到環境中 對本文有興趣的可以觀看一下
]]>講到多套 kubernetes 的情況下,目前大部分的文章都會推薦用三套獨立的 kubernetes 叢集而非架設一套同時管理三個地點的 kubernetes 叢集。 本篇文章作者從不同的面向分享為什麼要選擇一個 kubernetes 管全部,而不是要架設三套 kubernetes 叢集。
一套 kubernetes 最令人詬病且很難處理的就是 Latency 的問題,作者提到 Latency 的問題會影響 ETCD ETCD 被影響後會影響整個叢集的運作,甚至連應用程式相關的處理都會變慢。
作者提到其實這個問題能夠採取兩個步驟來解決
註: 我是覺得這說法不能解決問題,一般應用程式要是被分散到不同地區你的存取還是有機會跨地區,除非要很認真地針對不同地區去設計 label,讓應用程式的部屬都只會固定同個地區,但是要這樣搞跟我直接搞三套不覺得後者會比較累。
作者一直強調使用 mesh VPN 來打通底層所有網路封包處理,讓你一個大 k8s 管理多個地區,就不用擔心底層網路問題
單套 k8s 的好處有什麼?作者認為有
作者提到 2021 年的 KubeConf 有各種管理多套 k8s 叢集的工具,如 KubeEdge, OpenShift Edge, Akri, Baetyl, Kubermatic, Rancher, KubeFed... 等,如果用一套大 k8s 就可以不使用這些工具,直接減少與這類型複雜工具的依賴性 一套 k8s 叢集可以讓你使用最簡單也是最習慣的方式來管理所有環境
每套 K8s 環境中都會有如監控,日誌, registry 等各種工具,多套 k8s 的架構就是每個叢集都要安裝一份,但是如果採用一個大 k8s 的架構就只要維護一份即可 所以可以減少很多不必要的重複安裝。
這段其實不很理解,為什麼作者這麼想要推廣 mesh VPN ...
註: 這篇文章底下有留言說探討到說 RBAC 等相關權限問題是個很大的問題,你一套 k8s 很難處理這些,事情沒有想像的這麼簡單
]]>Helm 作為現在包裝與安裝 Kubernetes 應用服務的主流方式,單單使用 Helm 很多時候不能滿足部署需求,譬如公司的業務是由多套 Helm Chart 同時組成的,這時候可能會有幾種做法
而作者長期使用 Helmfile 來管理各種 Helm 的安裝方式,而今天作者終於發現一個相對於 Helmfile 來說更容易使用,而且整體使用方式更為簡潔的解決方案,helmwave.
Helmwave 的官方介紹很簡單, Helmwave is like docker-compoose for helm.
其本身的實作更為簡潔,直接使用 Helm Library 於整個實作中,所以下載單獨的 binary 即可,不需要如同 helmfile 一樣還要於系統中先安裝 helm 等相關工具。 文章中透過範例來示範如何滿足
整個使用的方式跟 docker-compose 有點類似,可以透過 helmwave up, helmwave down 的概念來啟動與停止服務,只不過所有的服務都是基於 k8s + helm-charts 來完成。
有使用 helmfile 的人可能會對這類型的工具比較有感覺,也許可以看看其差異性是否真的有如作者所提這麼好
標題: 「DevOps 的 2022 學習之路」
類別: others
連結: https://medium.com/faun/devops-roadmap-2022-340934d360f9
本篇文章是作者根據自己的觀察與經驗,列出 2022 需要繼續學習與觀察的 13 項技能與概念,希望讓每個 DevOps(SRE) 相關領域的人有一個方向去精進自己。
Network Technologies 網路的概念短時間內很難被顛覆,所以掌握基本的 L4/L7, HTTP2/, HTTP3/(QUIC), DNS, BGP, Load-Balancing 等基本網路概念絕對不吃虧,作為一個熟悉架構的專家,能夠描述環境中的封包流向是不可缺少的能力。
OS, particularly Linux Linux 很重要,請學習系統上的各種基本概念, CPU/Memory 基本概念, Init, cgroup 等
CI/CD Jenkins 作為老牌的解決方案,能夠使用其實也很好,不過要注意的是現在有愈來愈多的環境嘗試使用其他的 pipeline 來搭建,所以有時間的話也可以學習一下其他的解決方式,讓自己能夠有能力去面對各種需求
Containerlization/Virtualization 除了最知名的 Docker 環境外,也嘗試看看 containerd, podman 等不同專案,同時也考慮如何將 container security 的概念給導入到日常生活中
Container Orchestration K8s 幾乎變成容器管理維運的 de facto 標準,單純的 k8s 叢集還不足以面對所有正式環境的問題,所以還需要搭配各個面向的概念將其整合才可以打造出一個適合團隊的 k8s 叢集。
Observability at Scale 除了最基本常見的 Prometheus 之外,也看一下其他基於 Prometheus 所打造更適合大規模的架構,如 Thanos, Cortex, VictoriaMetrics 等 此外可以試試看 Continuous Profiling 等持續觀察系統效能的工具,如 Parca, Pyroscope, hypertrace 以及順便試試看導入 Open Telemetry。
Platform team as a Product team 稍微有規模的團隊可能會慢慢的感覺到 Platform 逐漸轉型成為一個 Product 的概念,只不過該 Product 的面向對象是內部開發與測試人員而並非外部使用者。 整體目標就是打造一個更好的協同平臺,讓開發與測試人員能夠更有效地去滿足日常工作需求,同時 Platform team 除了維護產品之外也要教授使用人員讓他們有能力去使用該平台來滿足需求 而不是所有問題都要一直讓 Platform 的人來幫忙處理,這種模式小團隊可行,但是當團隊過大時就沒有辦法處理。
Security
Programming
Infrastructure as Code
Cloud
Technical Writing
Site Reliability Engineering
剩下的內容就留給有興趣的人自行到文章去觀看,每個類別都有舉出幾個趨勢與值得關注的專案,其中特別注意的是 Technical Writing 這項技能非常重要 遠端工作的趨勢使得透過文字交流的機會比過往多很多,所以如何寫出一個有效不會浪費彼此時間的設計文件,架構,開發文件等則是一個很重要的技能,所以即使是個開發人員也要努力練習 將腦中的想法有系統地呈現出來
]]>作者本篇文章想要分享一個其用來讓一個 Kubernetes 變得能夠真正上戰場的相關工具,因此文章中特別強調是 Production-Ready 的情況。 一個 Production Ready 的 K8s 叢集必須對於下列每個大項目都要有相關處理方式,譬如
Reliability and Availability: 該領域的兩個指標代表的意義不太一樣,但是對於一個提供服務的叢集來說都一樣重要
這邊作者列舉了幾個工具譬如
Backup/Recovery 有不少人團隊都對於對於叢集的備份與還原感到頭痛,目前最知名的開源專案莫過於 Velero,其支援不同的儲存設備如 Cloud Storage 等來存放,讓不同環境的 k8s 使用者都有辦法去備份其叢集內的資料
Cost Optimization
對於雲端架構來說,基本上雲端業者的內建功能已經可以針對如 VM, 底層架構等各種服務去列舉出各自的花費金錢,將此概念套入到 Kubernetes 本身大抵上只能理解到 Master Node, Worker Node 等之類的花費, 因此透過 Kubecost 之類的專案來將成本的洞察範圍擴充到 Kubernetes 內部,以 namespace, pod 等各種 k8s 的資源為單位來列舉實際花費的金額,能夠讓團隊更有效地去管理相關花費
]]>SSH 基本上是每個系統管理員都熟悉不過的工具,而本文的作者指出 SSH 使用上有一些小缺陷,譬如
舉了一些問題後,作者點出能夠真正駕馭 SSH 的應該是採取 SSH Certificate 而非使用 SSH Public Key 來進行身份驗證。 作者團隊開發了些許工具來幫助其他人能夠更輕鬆的使用 SSH Certificate 但是卻發現這類型的工具卻沒有受到歡迎與採用,因此也針對這個現象 進行問卷調查,想瞭解這類型的工具為什麼不受青睞,原因包含
文章後半開始介紹 SSH Public Key 與 SSH Certificate 的差異 Public Key 的概念非常簡單,就是透過一組 Private/Public Key 並且將 Public Key 給寫入到目標節點帳戶上的 ~/.ssh/authorrized_keys. 節點數量爆炸多的情況下要如何有效率的去管理這些檔案則是一個非常麻煩但是又不能不處理的事情,這也是作者為什麼要推廣 SSH Certificate 的原因之一
SSH Certificate 的方式移除了關於 SSH Public Key 不停重複上傳與設定的情境,相反的則是將自身的 Public Key 給綁到 Certificate 內,同時也包含如過期時間,名稱等其他資料。 目標 Certificate 本身會由一個 CA 簽署,而每台 Server 都需要去修改 /etc/ssh/sshd_config 來指定相關的 CA Key 讓該 SSH 能夠信任。 文章後半部分介紹更多關於 SSH Certificate 的好處以及用法
]]>Kubernetes 內已經有 HPA 的物件可以讓 K8s 根據一些基本的指標來動態調整 Pod 的數量,而 KEDA 這款 CNCF 的孵化專案則是完全強化 HPA 的效果 KEDA 的概念很簡單,就是應用程式應該要可以有更多的指標來幫忙動態擴充,除了最基本的 CPU/Memory 等基本指標外, KEDA 來支援了下列各種不同指標,讓 k8s 可以使用更為廣泛的指標,譬如
使用方式很單純,一切的規則都是基於 K8s 的 CRD 來描述與管理,因此團隊可以使用 YAML 的方式來定義這些擴充的規則 文章中有基於 CPU/Memory 的基本介紹使用,同時文章中也有官方的連結來介紹各種不同指標的示範用法
]]>隨者各大公有雲逐步支援 Kubernetes 1.22,相關使用者可能都會開始進入升級的準備階段,而每次 Kubernetes 升級除了單純 思考 Kubernetes 本身的升級順利與否外,也要確認正在運行的所有 Kubernetes 資源與相關工具是否也能夠順利運行,這使得整個準備工作變得複雜與龐大。
從 Kubernetes 的角度來看,每次的升級除了基本的穩定性與相關功能修正外,最重要的還有 Kubernetes API 的改變,該改變影響巨大,譬如所有 Manifest 的內容,譬如眾多透過 YAML 所描述的各種資源 API 的改變都會提早通知所有社群,於先前的版本先將該 API 標為 deprecated 接者後續版本才會正式移除,譬如 networking.k8s.io/v1beta1 於 1.19 被標示為 deprecated 然後正式於 1.22 移除。 正式的版本 networking.k8s.io/v1 則從 1.19 正式啟用,讓管理者有大概有三個版本的時間轉移。
因此升級前一定要先架設一個測試環境,嘗試部署所有現存的資源來確保升級不會出現不預期的錯誤。
作者整理出關於 1.22 升級要注意的版本變化,如下(幾乎都是從 v1beta 變成 v1)
熟悉 Linux 系統的人想必都了解 Signal 的概念,特別是幾個常見的如 SIGTERM, SIGKILL 等,
作者的團隊嘗試透過 SIGKILL 的行為來驗證與測試團隊內部署的 Kubernetes Pod,特別是當遇到 ungraceful shutdown 的情境時這些 Pod 會如何運作。 團隊嘗試透過 kubectl delete 的方式來刪除這些 Pod,但是實驗過程中發現 --grace-period 這個參數的運作行為與團隊的預期行為不同。 kubectl delete 得說明文件中特別指出
--grace-period=-1: Period of time in seconds given to the resource to terminate gracefully.
Ignored if negative. Set to 1 for immediate shutdown. Can only be set to 0 when --force is true
(force deletion).
作者看文這段文字說明後滿腦問號,提出兩個問題
作者認為文件沒有辦法解決這些問題,所以設計了一些實驗來測試
--grace-period=1 的實驗結果是
作者對於這個行為感到不解,認為 "immediate shutdown" 應該就是要馬上關閉呀,怎麼可以送 SIGTERM 給 Pod 讓 Pod 有機會可以優雅的結束一切? 因為對於這行為的認知不同導致作者團隊的測試行為沒有辦法順利完成。
接下來測試 --grace-period=0 & --force=true
文件中說明這樣設定會立刻將該資源從 API Server 內給刪除並且避開 graceful 的階段。 最後測試的結果是
作者表示又糊塗了,沒想到設定 grace-period=0 竟然中間還有 30 秒的時間,這完全與預料的不同,更麻煩的是文件也沒有講得非常清楚到底什麼是正確的行為, 此外還提到 Docker 就支援真正的 immediate shutdown,直接送 SIGKILL。
另外作者發現 K8s GitHub 中的也有人提出類似的 issue,對於這些 graceful 的行為感到不解同時文件說明不夠精準。
這件事情很難說誰正確誰不正確,畢竟不同的系統架構下的設計方式與條件都不同,不過的確 K8s 的指令文件有時候是真的不是精準,需要仔細測試才可以理解到底運作行為為何
]]>本篇文章是作者探討自己建制 Image 中所發現的一些有趣事實。 作者使用一個大概 70MB 的 image,並且安裝與運行大概 90MB 左右的額外空間,結果最後整個 image 高達 267 70MB 因此作者就花了一些時間來研究整體原因並且嘗試理解到底發生什麼事情
作者首先檢視自己的 Dockerfile,其內容簡單不複雜,包含如 COPY 一個 Binary (該 Binary 80 MB 左右) RUN xxxxx 等常見用法。
詳細檢視所有的 layer 資訊後,作者發現 RUN 這個指令竟然產生了 94.4MB 的全新 layer,而就是這個 layer 導致整體空間變成 267 MB. 作者的 RUN 指令執行
作者檢查過安裝的套件,大概只有 6MB 左右,但是整個 layer 很明確就是多了 94.4 MB 出來,因此經過測試與研究後,作者觀察到 當移除第二步(修給檔案給予執行的權限)後整個空間瞬間變得很小,整體 image 最後的大小就符合預期的 174MB。
所以整個問題就出來了,為什麼單純執行一個 RUN chmod 就會使得整個 image layer 變大? 簡單來說 image 的底層是基於 OverlayFS,而 OverlayFS 的一大特色就是 CoW, Copy on Write,作者起初覺得 我只是透過 chmod 去修改該 Binary 一個屬性而以,本身並沒有寫入檔案到檔案系統中,怎麼會產生這麼大的檔案變化?
仔細研究 OverlayFS 的文件後終於水落石出,原來除了寫入檔案外,修改檔案的某些 metadata 也會觸發 CoW 的機制
When a file in the lower filesystem is accessed in a way the requires write-access, such as opening for write access, changing some metadata etc., the file is first copied from the lower filesystem to the upper filesystem (copy_up).
至於為什麼修改個 metadata 也要觸發 CoW 主要是跟安全性有關,文章中有關於這部分的額外連結,有興趣的可以參考
]]>疫情這兩年影響全球,其中對於勞動力來說更有甚巨的變化,而科技業更是其中的佼佼者,是所有行業中離職率最高的行業。 2021 的離職率相對於 2020 來說提升了 4.5%。
StackOverflow 基於想要理解科技領域的這個趨勢及原因,所以發起了一個調查想研究工程師工作是否開心,並且將 基於 350 位來自全球開發者的回應統整為報告於三月份釋出。
結果來看
以最令人感到開心的地區排名來看,前五名分別為
那到底哪些因素會影響開心與否? 報告中列舉了相關的指標 前五個最令人感到開心的因素有
而令人感到不開心的五個最重要指標其實就是上述指標的反轉,依序為
調查的全部指標除了上述五個之外還有
其實面試找工作也就是針對這些條件進行排序,與其跟風看大家去什麼公司就想去什麼公司,不如好好跟自己對話,瞭解自己對於工作的目的以及追求是什麼,才有辦法找到一個自己喜歡且舒適的工作環境
]]>雖然可以使用非 root 的方式去安裝 Docker 服務,但是 Docker 本身服務中還有其他各種元件需要透過 root 身份去運行,譬如 dockerd, containerd, runc 等元件, 而本篇文章則是探討要如何以真正 rootless 的方式來運行一個 docker container 。
使用 rootless container 有一些要注意的事情,譬如 port number 沒有辦法使用 1024 以下,所以如果你的服務有需要被外界存取時要使用大於 1024 的 port number。 此外 AppArmor, host network mode 這些都不支援,因此使用上會有一些情境要注意。
安裝其實滿簡單的, Docker 官網有提供 rootless 的安裝檔案,安裝後需要針對一個使用者 ID 進行處理,這個處理主要是因為要將 container 內的 root 使用者給轉換到系統上的非 root 使用者,所以才會有相關的 userID 要設定。 當然如果真的要完全追求 rootless 的容器解決方案可以考慮使用 Podman 來使用,其本身的設定就是針對 rootless 去開發的,使用上會相對於 docker 來說更為簡單。
]]>作者因應過去於 Kubernetes 的教學與開源過程中,必須要一直不停地去安裝各式各樣必備的工具而感到厭煩,譬如每次都要安裝 kubectl, kind, kubectx 等各種常見工具 而每個工具又會有不同的版本,每次都要專寫相關的安裝流程都很麻煩,因此作者萌生出開發一個能夠安裝這些工具的開源工具, arakde.
該工具用起來非常簡單,同時也支援不同版本的工具,除了基本 CLI 工具外也支援 Helm App 的安裝,我個人認為光工具本身就非常好用了,譬如可以透過該指令輕鬆的安裝不同版本的下列工具
如果你常常需要撰寫文件去分享安裝各種文件的需求,也許可以考慮使用看看此工具來簡化流程
]]>熟悉 C++ 語言的讀者必定聽過 STL,而本篇推特系列文則是用來解釋為什麼 3A 大作的遊戲室都不愛使用 STL,這邊節錄一些概念與想法
覺得本篇推文非常有興趣,對這系列有興趣的可以看看大家的討論
]]>隨者 IaC 的概念落地開花,愈來愈多團隊嘗試使用 Terraform 來管理各式各樣的 infrastructure,作者本篇文章分享五個自己每天使用的 Terraform 輔佐工具,分別是
TFSwitch: 如果環境中目前因為歷史因素沒有辦法統一轉移到相同版本的 Terraform 使得你必須要用不同版本的 Terraform 來處理不同的專案的話,可以透過 TFSwitch 來幫助你快速地切換版本
TFLint: 就如同大部分的 Lint 工具一樣, TFLint 針對 Terraform 的工具,特別是跟特定 CloudProvider 整時候會有更多的錯誤偵錯,將該工具整合到 CI/CD pipeline 中更可以幫助團隊避免合併一個有問題的 Terraform code.
Terraform-docs: 這是一套能夠將你的 Terraform module 直接產生對應 Markdown 格式文件的工具,如果本身有撰寫 Terraform Module 的團隊都可以使用這工具試試看,看看產生的文件是否可以滿足基本需求
Checkov: 這是一套支援 Terraform 的靜態程式碼掃描工具,可以用來檢查是否有可能的安全性漏洞與不良好的設定,目前預設大概有 750+ 以上的預設規則,
Infracost: 這工具會的目的就如同專案名稱一樣,根據創造的雲端資源幫你估計這些資源的實際花費,對於要控管成本的團隊來說,可以提供一個粗略的金額概念,畢竟如網路流量等相關付費還是要實際上線才知道,但是可以快速地針對不同的 infra 直接列出大概的金額差異,搭配得宜對於整體工作流程還是有幫助的。
]]>作者作為一個 Meta 工作七年的員工,分享了一些認為 Facebook 頗有特色的文化,這些特色文化並沒有辦法直接斷言是好是壞,一切都是看用什麼角度去看待。
年度績效評估時要如何去評估一個工程師的績效一直都是個不簡單的問題,作者提到對於 Meta 內部的高級工程師(不確定正確級別代號是什麼)來說,其績效並不是單單的只去看技術用的好不好,程式寫的好不好 更多的反而是這個產品是否有真正的商業成長結果。
作者認為這種鼓勵從下而上解決問題的思路能夠讓產品的發展更佳有效率與有意義,舉例來說 假如今天工程師的績效是完全基於技術方面的呈現,而專案負責人(PM)的績效可能是該專案對於使用者的黏著度,兩者績效不一致的情況下很容易發展出不同的開發與演進策略 工程師為了達到自己的績效其發展的路線就不一定可以為產品帶來更好的使用者黏著度,反之亦然,為產品帶來更好使用者黏著度的改善並不一定可以讓工程師看到很好的表現績效。
但是一旦當工程師與 PM 的目標一致,整體的合作就會更加融洽也目標,這也是為什麼 Meta 的工程師非常了解自己產品的指標跟數據,還會花時間去分析產品數據與使用者分析報告,透過自己的理解來思考到底要怎麼去改善產品的方向,而不是完全等待 PM 發號司令。
Meta 內部的基礎架構某程度也被視為是一個產品,公司內的其他工程團隊則是該產品的潛在用戶,所以開發該產品的團隊本身也要努力的去推銷這個產品,去說服為什麼要使用這個架構,使用這個架構能夠帶來什麼樣的好處。 作者以早期的 Reat, React Native 為範例,早期該產品於公司內推廣也是四處碰壁,並非外部所想像的一推廣就廣受歡迎與嘗試。
由於基礎架構被視作是產品,所以如果其「商業」表現不如預期的話,該專案也是會被砍掉的,這類型的模式搭配上述的概念其實非常有趣
所有的專案都想要長期存活都必須要證明其有價值,就算是內部架構也要證明其對內部其他工程團隊有價值 這種方式也降低了「只專注技術而不考慮使用者需求的」的開發方式,這讓我想到大家最愛講的「Service Mesh」.... 帶來什麼效應不確定,但是很潮就是享用...,
]]>本篇文章的作者是 python3 urllib3 套件的主要維護者,由於近年來軟體供應鏈的資安議題逐漸受到重視,特別是這些已經被廣泛使用的套件,一套受到入侵與修改,其影響危害程度難以想像 作者撰寫本篇文章分享自己的想法希望能夠讓所有套件維護者有一些基本的資安觀念,同時也讓所有使用 OSS 的使用者一起學習
作者提到本文提到的所有資安意識都需要花時間精力去實作與維護,如果你的組織使用了大量的開源專案但是本身在意資安卻又不想要花時間研究資安,那就花點小錢捐贈給這些開源貢獻者,讓這些貢獻者有更多的動力去幫你維護這些套件。 如果該開源專案對於你公司來說有非常舉足輕重的角色,那甚至可以考慮雇用一兩個該專案的主要維護者讓他們定期分配時間來維護,對公司來說只是花一些小錢但是卻能夠有更有信心的使用這些開源專案,以免哪天這些專案一炸整個公司產品全炸
文章主要分成兩大類,分別是
對於一個套件維護者來說,你的個人帳號由於有超級大的權力,所以該帳號的資安管理必須是最高層級的注意,一但這個帳號被攻破,攻擊者就可以很輕鬆的去發布新版本,加入惡意程式碼等各種行徑,而通常使用者如果本身使用時沒有很好的限制版本,譬如採用大於1.1.0 這種比較寬鬆的用法就會不自覺升級而使用到危險版本 作者強調就算你本身不是套件維護者,這些帳戶保護方式對你來說也是非常實用的,良好的資安保護永遠不吃虧
接下來作者列出幾個大項,分別是
Email Securiy Is Your Top Priority Email 地址很重要,很多情況下這些地址都會是重設密碼的一個途徑,所以妥善保存 Email 地址是非常重要的,所以作者推薦使用那些大公司服務如 Gmail 與 Outlook。 如果你真的想要使用私人域名作為你的聯絡信箱,你就要保證你不會有忘記付費的那天,不會剛好有人買走你的域名然後順利的取走你的 Email 地址。
2FA 2FA 要求提供一個除了密碼以外的認證方式,常見的有手機或是一些硬體裝置,使用妥當的話基本上攻擊者很難登入你的帳號。 作者認為所有跟程式碼有關的帳號密碼最終都需要有一個不使用 SMS(簡訊) 的 2FA 機制保護,譬如 NPM 最近才宣布其 Top 500 的套件管理都需要強迫使用 2FA,作者希望 Python 有天也可以跟上這種趨勢。
Password Managers
Hardware Keys
Why Not SMS 2FA
Where Do I Put My Recovery Codes
What to do if your account is compromised?
上述範例我就不列出來了,每個項目都沒有很長,非常推薦大家閱讀,甚至可以讓團隊的所有工程師都有這些基本概念
]]>本篇文章是一個經驗探討文,想要探討近年來非常熱門的網路網格(Service Mesh) 到底導入時要怎麼抉擇與判斷。 Service Mesh 如果用得正確與適當,能夠為團隊帶來很多優勢,可以讓團隊更專注於軟體的服務上,讓 Service Mesh 幫忙提供各種方便的功能。 但是如果使用錯誤則可能只會造成整體架構更加複雜,同時也沒有解決什麼真的重點問題,一切只是疊床架屋的空殼而已。
採用 Service Mesh 要儘早 作者認為到底要不要導入 Service Mesh 是一個專案初期就要決定的事情,即使 Istio 網站有特別教學如何將專案從 non-MTLS 給轉移到基於 Istio MTLS 的過程 但是作者說真的跑過這些流程就知道絕對不是官網寫的三言兩語這麼簡單,有太多額外的事情要考慮,譬如上層安裝的服務,網路分層設計等,這些會因為有沒有 Service Mesh 而有不同的決定
不要當 Yes Man 作者體驗過最多的案例就是每個團隊看到下列問題都是不停的說 YES,然後最後就直接無腦導入 Service Mesh
我們需不需要強化資安
使用 mTLS 能不能強化資安
mTLS 是不是很難管理
Service Mesh 是不是可以讓 mTLS 便於管理
連續四個 YES 下來就直接無懸念的導入 Service Mesh,殊不知
因此作者接下來就列出幾個要導入 Service Mesh 前需要仔細思考過的問題
是否有計畫於當下或是未來使用到 Serivce Mesh 的所有功能 Service Mesh 的功能除了 mTLS 外還有各式各樣跟流量有關的管理,譬如 A/B Testing, 金絲雀部署等。 透過 Service Mesh 能夠讓應用程式不需要實作這些功能而依然可以享有這些功能的好處。 所以作者認為團隊中的所有人都要仔細的注意,到底你們即將採用的 Service Mesh 有哪些功能可以用,這樣可以避免應用程式重複開發相同功能。 作者也提到不需要第一天就決定好要採用什麼功能,但是至少要仔細理解自己採用的解決方案到底有什麼功能,然後未來改善架構的時候可以即時的想起來這功能有提供
團隊中是否有人對於 Service Mesh 有足夠的理論或是實戰理解? 作者看到的非常多團隊很多人根本不理解 Kubernetes 以及 Service Mesh 但是就想要導入 Service Mesh。 由於對 Service Mesh 完全不理解,連其實作的概念都不同,所以當問題發生的時候就什麼都不能做,因為根本不懂也不知道該從何下手 請花時間學習與理解你要使用的專案,以確保你有足夠的背景知識去使用與除錯
除了問題之外,作者也認為要導入 Service Mesh 到生產環境並不是單純的建構一個 Hello World 這麼簡單,還有很多事情要考慮,譬如
整篇文章非常的棒,有興趣的可以詳細閱讀
]]>本篇文章是過工具介紹文,探討如何基於 Helm 與 Terraform 這兩個不同層級的工具來處理 Cloudflare 的憑證。
根據 W3Techs 的調查顯示, 81.2% 的網站都使用 Cloudflare 來提升讀取速度或安全防護。 透過 CDN 的概念與機制,網站可以讓全球使用者有更快的讀取速度,此外也愈來愈多的網站會透過 Cloudflare 來處理如機器人, DDOS 之類的流量攻擊,畢竟要自己架設網站處理這些攻擊非常困難 因此讓 Cloudflare 這類型的網站來幫忙過濾與處理能夠讓團隊更專注於本身的業務開發與維運
想要在 Kubernetes 內妥善管理所有使用的憑證其實也是一個麻煩事情,除了要能夠設置正確來創立憑證外,能夠於到期前自動 re-new 也是一個不可或區的功能。 Kubernetes 內跟憑證有關的最知名專案我想就是 Cert-Manager,而 Cloudflare 也基於此專案撰寫了相關的 Kubernetes Controller,如 Origin CA 等 因此本文使用的功能與示範都會基於 cert-manager 與 Cloudflare 的架構。
本文的目的是希望能夠將過往手動的繁瑣步驟給自動化,讓 Kubernetes 可以獲得 Cloudflare 提供的好處,如憑證與相關域名等。 內文是基於 Terraform 作為出發點,然後透過 Kubernetes Provider 的方式來與之互動,一步一步的安裝各種資源最後成功於叢集內獲得相關域名的 SSL 憑證以及其他資源
]]>本篇文章是作者談談自己工程生涯近 20 年的經驗,聊聊 20 個作者覺得很值得跟大家分享的一些想法與經驗。 開頭作者就說,所有的建議與經驗分享最重要的就是脈絡,沒有了脈絡所有的建議可能都沒有任何用處,甚至可能會對你有害 所以作者先表明自己的一些經驗,包含
這邊節錄幾個經驗,整體來說我認為全部都滿不錯的,都推薦當作心法去看待,甚至也可以作為一些討論的主題。
I still don’t know very much: 不知道你有沒有很常聽到別人說「你怎麼會不知道 BGP 怎麼運作?」「你怎麼沒有聽過 RUST ?」,軟體世界的有趣點不論你怎麼學習,每天都有會展新的技術被發展出來 你於你專精的領域努力了十年,對於其他人的領域你會發現還是有很大一片空白需要學習。 所以請認知這一點,軟體世界龐大本來就很容易有不知道的事情,然後保持謙虛的去面對這一切,不要用一種別人都是白癡的態度來質疑別人
The best software engineers think like designers 最好的軟體工程師其思路不單純只是滿足功能,而是如同一個設計師一樣,會去思考設計的這個軟體各種使用,包含外部 API的設計,使用者介面,甚至要去考慮 使用者會怎麼使用,使用者為什麼會想要用等,將使用需求放到開發的第一順位才能夠打造一款真的是很適合使用者使用的軟體。
Look for technological sharks 那些發展許久至今仍活躍的技術能夠於現在這個迭代快速的世代存活下來必定有其價值與原因,如果今天想要替換掉這些技術一定要有很好的理由與評估,不要單純冒然基於 新技術好像比較好就去替換。這些舊有的技術與工具也許用起來沒有現在這麼潮,但是其穩定性與良好的表現絕對能夠讓你晚上好好睡覺
Every system eventually sucks, get over it 軟體開發中沒有一個正確的架構,所有的人無時無刻都在處理相關的技術債,所有開發的介面過一段時間都會因為情境不同而需要改動與重寫,你撰寫的測試永遠都不足夠。 但是這些概念並不能當作一個不往前邁進的理由,相反的就是因為知道沒有一個完美的架構,所以才要更有系統地去持續精進整個架構,永遠都有值得改善與變好的地方 透過不停的循環這個開發流程才能夠讓整個軟體愈來愈好。
文章中還有其他 16 個非常實用的建議,非常推薦大家閱讀閱讀
]]>來自歐洲的 Honeypot.io 與 RedHat, Google 以及 CNCF 合作完成一個長達一小時的 Kubernetes 紀錄片,該紀錄片分成上下兩集,探討 Kubernetes 的發展及過程 被戲稱就像是給開發人員的 Netflix 影片 如果英聽還行的話,非常推薦當個小品影片去聽聽看 Kubernetes 的今生前後
]]>本篇文章是 rsc 來仔細介紹 golang 的發展歷史,主要是針對整個開發過程的版本控制轉移以及一些有趣的 Commmit 舉例來說,如果你去看 golang 的 commit 會發現第一筆 commit 是 1972 年的內容,並且該 commit 增加了一個 src/pkg/debug/macho/testdata/hello.b 的檔案
而以實際狀況來說,前面四筆都是假的 commit,第五筆 commit 才是 golang 開發的第一筆 commit,這之間的緣由牽扯到版本控制的轉變。 以 Golang 來說,其經歷了四次轉變化,從最初的 Subversion 到 Perforce 到 Mercurial 到 Gerrit
其中 golang 正式對外公開是發生於 Mercurial 的過程中,而這些假的 commit 也是這個時間點由 rsc 自己產生的,當作一個復活節彩蛋的概念 有興趣的可以閱讀全文
]]>本篇是一個心態探討文,作者探討了到底一天的有效工作時間有多少,並且認為人們宣稱的工作時間通常都不准,很多時候只是一種想要讓人看到自己工作很久的心態而已。 從作者的經驗來說,作者認為軟體開發,作家,顧問等行業都會有這類型的特性,一個有效率的工作者其實根本不可能一整天連續工作八小時。 一開始作者先列舉自己一天可以達到的事情有哪些(可達到不代表全部都會做)
總體來說,作者一天認真工作的時間大概就是四小時左右,當然有需求時當然可以提升整體的工作量,不過作者認為這會影響身心健康,所以不是很喜歡。作者也認為「愈需要創作的工作其實真正工作的時間愈少」 很多工程師看起來工時很長其實有時候上班也都在滑 Twitter, Reddit 等各種網站,真正的工作時長其實都不如自己宣稱的。因為每個人的精力有限,不可能長時間執行高度專心的工作,很多時候花一堆時間去拼工作量結果得到的是每件事情都辦不好, 與其如此不如找到一個適合自己的工作方式,讓自己能夠有效率地去做好每件事情。
這邊要特別注意的是
如果今天有人要丟工作給你,請友善禮貌地回答「我很願意幫忙你,不過我目前手頭上正在忙 XYZ,你想要做的事情優先度有比較高嗎?」
最後作者針對工程師列出一個不同類型工作的適合工作時數
這篇文章內容非常精彩,從不同層面去探討 Elasticsearch 與 ClickHouse 這兩套解決方案的差異,探討了包含
文章很長且滿多技術坑,適合對於 Elasticsearch 有維運與管理有經驗的使用者
]]>摘要: Paypal 過去一直都使用 Apache Mesos 來運行其大部分的服務,而其最近正在針對 Kubernetes 進行一個評估與測試,想瞭解如果需要轉移到 Kubernetes 會有哪些問題需要挑戰與克服。 本篇文章著重的是效能問題,原先的 Apache Mesos 可以同時支持一萬個節點,因此 Kubernetes 是否可以拿到相同的效能 而本文節錄的就是擴充 Kubernetes 節點中遇到的各種問題以及 Paypal 是如何修正與調整讓 Kubernetes 可能容納盡可能更多的節點。
效能測試方面是基於 k-bench 去開發的測試工具,基於平行與依序等不同方式來創建 Pod/Deployment 兩種資源。
文章接下來開始針對 API Server, Controller Manager, Scheduler, ETCD 元件遇到的問題並且如何解決,中間提到了不少參數,這部分應該是大部分使用者都比較不會去研究與使用的參數 因此我認為本篇文章非常值得閱讀。 ETCD 的部分遇到很嚴重的效能問題,作者團隊觀察到大量的 Raft 溝通失敗個訊息,觀測到跟硬碟寫入速度有關,然而 GCP 沒有辦法單純增加效能,必須要同時提升硬碟空間,所以使用上彈性不變。 不過就算採用 1TB 的 PD-SSD ,當 4 千個節點同時加入到 Kubernetes 時依然會遇到效能上的問題,團隊最後決定使用本地端的 SSD 來想辦法改善寫入速度,結果又遇到 ext4 的一些設定 過程很多問題也很多解決方式。
結論來說: k8s 複雜
]]>以下節錄自該留言串 「As it turns out, macOS cheats. On Linux, fsync() will both flush writes to the drive, and ask it to flush its write cache to stable storage.
But on macOS, fsync() only flushes writes to the drive. Instead, they provide an F_FULLSYNC operation to do what fsync() does on Linux.」
簡單來說就是 Linux 的 fsync 會執行兩次動作,最終讓當次修改給寫回到底層儲存設備,而 macOS 的版本卻不會真的確認寫回硬碟,所以這個導致很多仰賴 fsync 的應用程式就會有一些不預期的行為,這也是為什麼首篇發文的內容 「Well, this is unfortunate. It turns out Apple's custom NVMe drives are amazingly fast - if you don't care about data integrity.
If you do, they drop down to HDD performance. Thread.」
]]>作者認為 Docker Desktop 是一個非常好的開發環境工具,能夠簡化很多設定讓開發者更容易的開發應用程式,但是對於 Windows/Mac 的使用者來說 Docekr Desktop 實際上也是先運行一個基於 Linux 的 VM 並且於其中運行 Docker Container。這種架構實際上帶來了一些使用上的缺陷,包含
作者的公司 Partoo 採取了 VM + Vagrant + Ansible 的方式來創建開發者環境,讓每個加入團隊的開發者都可以輕鬆簡單的建設期開發環境 並且文章中也探討如何於本地端使用 Vistual Studio Code, PyCharm 來編輯 VM 的檔案並且順利的進行應用程式開發。
從效率來看,相對於直接使用 Dodcker Desktop 來看,作者團隊的測試程式基於自己的 VM 提升了將近 30% 的效能,主要的問題就是儲存系統與 Docker Container 之間的掛載關係差異。
]]>根據 VMware 2020 的一個研究報告指出,如何存取 Kubernetes 叢集是影響開發者生產效率的最大要素,所以本篇文章就是就會針對如何去評估與挑選一個適合開發者的 Kubernetes 叢集與存取方式。
作者將 Kubernetes 叢集分成四大類,分別是
為了比較這四種不同的叢集,作者定義了幾個不同的面向,針對這幾個面向來評比,分別是
文章一開始就有列出一個結論表,對於這個議題有興趣的歡迎閱讀
]]>作者團隊於 2019 年要開發一個全新的 API 應用程式,當時部門的 IT 團隊計畫要將既有的 VM-Based 應用程式給轉換到 Container-Based,最後團隊使用了 RedHat 的系統,並且使用 OpenShift 做為容器管理平台。
從結果來看,該專案從 2020/05 一路到 2021/05 花了整整一年也沒有順利的將應用程式轉移到 OpenShift 中,其中的一些重點有
最後團隊內又針對不同團隊給予不同的想法
譬如 Application Team 的開發人員都只滿足自身的技能,而且拒絕學習新技能,誇張的是一年過後團隊內的人也沒有辦法撰寫 dockerfile 或是使用 docker build.
後續還有一些針對不同團隊的想法與總體建議,整體來說非常真實,一個血淋淋的轉換案例。
]]>每過一段時間都可以於 GitHub 上面看到一些看起來很嚇人的 Commit,最經典莫過於 Linux Kernel 中的各種內容,譬如檔案被砍光,README 加入一些驚嚇言論 不知道情的使用者可能會想說這個內容是真正的 Github Repo 上的東西,鐵定是真正被認可而合併進去的,所以相信不疑。 殊不知這一切其實都只是 Git 的底層設計使得一些有心人可以打造出一些以假亂真的內容,文章中就有列出兩個關於 Linux Kernel 的有趣 Commit.
文章內詳細的去解釋整個來龍去賣以及底層 Git 的設計,包含 blob, tree, commit 之間的關係,並且說明為什麼有心人可以輕鬆的產生這些以假亂真的 Commit。 舉個範例來說,Linux Kernel 的整個 Git 專案大概有 3GB 的大小,然後被 Fork 的次數高達 40000 次,請問從實作方面來考量,你會希望
如果是選項(1)的話,那這樣至少要準備 120TB 的資料,從儲存方面來說完全不是一個可接受的實作方式,因此自然而然的都會是基於(2)的方式去實作 因此該 Linux Kernel 的 Git 專案實際上裡面記錄了所有的 Fork 資料,而每個人針對自己的 Fork 去進行更新等行為也都會記錄到 Linux Kernel 本身的專案上。 換句話說, 那 40000 個 Fork 出來的專案實際上還是共用同一份 Git 專案,因此每個人的 Commit 都只要該 Hash 被知道,其他人都有機會去檢視與瀏覽。
而 GitHub 的 UI 又允許使用者以 Commit Hash 的方式去瀏覽每一個 Commit 的內容,因此就可以跑到主要專案去輸入自己 Fork 產生的 Commit 來產生以假亂真的 Commit 內容。
對於整體有興趣的可以觀看全文
]]>標題: 「透過一點小技巧讓你的 Makefile 有一個更好的 Help說明」 類別: tools 連結: https://daniel.feldroy.com/posts/autodocumenting-makefiles
本篇文章使用 python 搭配 Makefile 的內建語法來輕鬆幫你的 Makefile 加上各種 Help 訊息,整個概念滿簡單的
有興趣的可以看看,整個寫法非常簡單有趣。
]]>這是一個非常有趣的小工具,目標就是視覺化系統中的 iptables 規則,把 chain 之間的關聯給視覺化呈現出來 整個專案非常小,該專案主要會透過 awk 來解析系統中的規則並且產生特定輸出,接者使用別的軟體將該輸出給轉換成圖檔
有興趣的都可以使用看看
]]>摘要: Linux Kernel 的長期貢獻者 Ingo Molnar 花了一年多的時間整理 Kernel 內的 Header 架構,一口氣提交了 2297 個 patches,其中影響 的檔案數量有 25,288 個,並且加入了 178,024 行數,移除了 74,720 行。 這一系列的改動直接重新整理 Linux Kernel 內將近 10,000 個不同的 header 檔案,這次的整理將過去 30 年累積的各種你呼叫我,我呼叫你這 種「Dependency Hell」問題給一起處理掉,結果論來說提升了整體建置時間 50% ~ 80 %
]]>作者認為 kubectl 本身提供的 label-selector, go-templates, jsonpath, custom-colume 等各式各樣功能使這個工具變得強大,能夠找到符合自己需求的各式各樣資源 然而上述的這些指令使用起來還是會覺得卡卡的,並沒有辦法滿足所有條件,而且不同選項的語法也完全不同,所以對於操作者來說其實不太便利。
順利的是 kubectl 可以透過 -o json 以 json 的格式輸出結果,這時候就可以搭配 jq 這個指令來使用,相對於前述的各種用法,作者更加推薦使用 jq 來處理,因為 jq 是一個更為廣泛的工具, 除了 kubectl 可以搭配外,很多時候都可以搭配 jq 來處理其他情況,因此掌握 jq 的語法實務上會有更多用途。
文章幾乎都是以 kubectl 為範例來介紹 jq 內的各種用法,除了基本的 read/write/filter 之外,還有各式各樣 jq 的內建函式, 有興趣的都可以使用看看
]]>CVE-2021-4034 講述的是 pkexec 此工具的 vulnerability,其影響範圍非常廣大,主要原因有
漏洞細節文章中有解釋非常多,主要是記憶體位置的處理沒有處理,當運行參數為空的時候,程式會意外地去讀取到後面的 envp 這塊用來存放環境變數的區塊,搭配 pkexec 後續的程式邏輯就有機會觸發本次 CVE 的安全性問題。 所以請趕緊更新系統上的 pkexec,確保該版本已經更新,否則任何一個使用者都可以輕鬆變成 root。
Ubuntu 使用者可參考 https://ubuntu.com/security/CVE-2021-4034
]]>Linux Kernel 內亂數子系統的維護者近期遞交了一個將 SAH1 給全面替換為 BLAKE2s 的相關 Patch
相對於 SHA1 來說, BLAKE2s 本身更為安全,同時計算速度也更快,這邊也可以參考下列這篇 2017 的文章 https://valerieaurora.org/hash.html 來探討不同 HASH 演算法的一些狀態,雖然沒有及時更新到 2022 的狀態,但是如果 2017 都 不安全的東西現在就更不應該使用,譬如文章中提到 SAH1 於 2017 就被 Google 用(6500 CPU或是110 GPU)的實驗來證實有衝突,建議停止使用。
]]>很多人都會使用 Kubernetes 的 Ingress 讓外部可以存取的部署的應用程式,同時會透過 Ingress 搭配 Cert Manager 來處理 TLS 的憑證 大部分情況下都會使用 HTTP-01 的方式來進行域名擁有性的認證,而某些情況可能不方便透過 HTTP 來驗證的話就會採取 DNS-01 的方式透過 DNS 創建一個 TXT 的資訊來驗證域名的擁有權,本篇文章則是作者分享 DNS-01 的一些心得分享
對 DNS-01 使用有興趣的可以看看本篇文章
]]>作者表示過往很多教學文章當探討到 Kubernetes 部署議題的時候,通常都不會去探討如何部署 Kubernetes 而是專注於應用程式的部署, 理由非常直觀,文章就是要專注於 Deployment 的議題,能夠讓讀者更容易地去閱讀與參考,另外一個背後的原因其實是因為 Kubernetes 部署的方式 太多種,常見的方式使用 Terraform 透過 IaC 的概念來管理,而應用程式都使用 Helm/Kustomize 完全不同的方式來管理
而作者今天想要探討的是如何透過 ArgoCD 來建設一個 GitOps 的環境,並且於上面使用 Crossplan 這個解決方案來處理各種底層基礎建設的需求,如此一來 就可以統一透過 Helm/Kustomize 的方式來描述這些基礎建設
Crossplan 很類似 Terraform 但是有者一些些微的差異
由於整個 Crossplan 可以視為一個 Kubernetes 應用程式,所以直接使用 ArgoCD 的方式來部署 有興趣的可以閱讀全問
]]>作者認為很多文章都闡述 GitOps 對於部署帶來的好處,但是軟體世界沒有十全十美的東西,所以作者就探討了 12 個其認為 GitOps 的缺點
註:
這邊就節錄幾個文章中探討的議題,剩下有興趣的可以閱讀全文
作者認為 GitOps 的精神「我想要將 Git 專案內的所描述的狀態給同步到叢集中」這點只能處理應用程式部署的問題,但是其他的流程 譬如編譯程式碼,運行單元測試,安全性檢查,靜態掃描等過程都沒有辦法被 GitOps 給處理。
作者不滿的點主要是很多 GitOps 的工具好像都會宣傳自己是個全能的解決方案,能夠處理所有事情,但是實際上卻沒有辦法。 實際上其專注的點就是應用程式部署策略為主的部分,其餘部分還是團隊要有自己的方式去處理
過往很多團隊都會將 CI/CD 給整合到相同的 pipeline 中去處理,通常是最後一個階段就會將應用程式給部署到目標叢集,然而有外部 Controller 實作的 GitOps 解決方案會使得 CI/CD 兩者脫鉤,好處來說就是 pipeline 不需要去處理部署,只需要專心維護 Git 內的資訊,後續都讓 Controller 來處理。
然後某些團隊本來的 CI/CD 流程會於部署完畢後還會進行一些測試或是相關操作,這部分會因為 GitOps 將部署給弄走導致整個流程不太好處理,畢竟要如何讓 GitOps 部署完畢後又要可以觸發其他的工作也是額外要處理的事情
雖然 GitOps 的核心是透過 Git Commit 去控制當前部署的版本,那發生問題時到底該怎麼處理,如何去 rollback? 作者舉兩種範例
作者認為目前現有的 GitOps 工具都沒有辦法提供下列答案
註: 有什麼概念是天生就可以有這些東西的..? GitOps 有點無妄之災
]]>NPM 上一個著名的 Module Color 以及 Faker 的作者這幾天生氣氣的修改這兩個 module,於 Color 內塞入了無限循環並且印出各種亂碼 然後所有使用這兩個 module 的工具一旦更新就會發現自己的 Terminal 輸出整個爆炸,完全看不懂了。
這篇文章是 Golang 的作者 Russ Cox 分享關於這件事情的一些看法,簡單來說每個開放原始碼的授權都有提到並沒有保固這種事情,所以任何現代化的模組管理者 設計時都必須要考量到這種版本更新的可能性,並且盡可能地去減少。
文章中以 aws-cdk 作為範例, aws-cdk 最初描述時是使用 ^1.4.0 的方式來參考各種 ^1.4.0 版本的 color,結果 color 的作者就直接爆氣來一個炸彈,aws-cdk 直接更新然後建置,最後 產生出一個令人崩潰的版本。
作者認為任何一個系統要更新的時候應該都需要緩慢與穩健的去逐步更新,並且這些更新都要經過一段時間的觀察與測試來降低各種可能放到生產系統出包的可能性。
以下節錄自文章中的重點 「High-fidelity builds solve both the testing problem and the gradual rollout problem. A new version of colors wouldn’t get picked up by an aws-cdk install until the aws-cdk authors had gotten a chance to test it and push a new version configuration in a new version of aws-cdk. At that point, all new aws-cdk installs would get the new colors, but all the other tools would still be unaffected, until they too tested and officially adopted the new version of colors.」
]]>摘要:
2021 對於 DB 行業來說發生了許多風風雨雨,作者列出幾個觀察到的現象並且給予一些評論
「Dominance of PostgreSQL」 愈多愈多的人開發一個新的應用程式時首選都是 PostgreSQL 這個穩定可信賴且一直不停加入新功能的資料庫。 2010 年時 PostgreSQL 的開發團隊決定採取更為激進的方式來每年都要釋出一個主要版本的演進,其相容性的能力使得 PostgreSQL 能夠跟很多系統整合。 譬如前端介面如 Amazon Aurora, YugaByte, Yellowbrick 甚至 Google 都於 2021/10 宣布要讓 Cloud Spanner 支援 PostgreSQL
作者也嘗試從 Database Subreddit 上去爬文搜尋,基於過去一年所有發文去統計每個資料庫的出現次數,以結論來看 PostgreSQL -> MySQL -> MongoDB -> Oracle -> SQLite 等 這個過程不是非常嚴謹的統計分析,只是一個簡單的方式去觀察該論壇上大家都喜歡討論什麼資料庫而已。
「Benchmark Violence」 Benchmark 一直以來都是各個廠商展示軍火的地方,想辦法利用這些數據去說服大眾自己才是最棒的,作者列出去年三個激烈的 benchmark 討論
作者也有參與上述血流成河的 Benchmark 的戰場,但是這些爭論的過程中作者失去了不少朋友,甚至連女朋友也一起離開了,作者回過頭來看這一切都 不值得,此外由於現在雲端的 DBMS 也有許多可最佳化的參數,要直接去比較彼此的優劣其實沒有這麼簡單。
後面還有「Big Data, Big Money」以及「In Memoriam」 兩個不同的議題,有興趣的可以點選全文閱讀
]]>Openkruise 1.0 版本釋出,該專案是 CNCF 沙盒層級的專案,主要是由阿里巴巴開發與維護,不久前的 Kubeconf 中阿里巴巴的議題也有 分享到有將此專案部署到期內部的 Kubernetes 管理平台
該專案主要是強化 Kubernetes 內應用程式的自動化,包含部署,升級,維運等面向,此外其架構是基於 Operator 去設計的,因此任何的 Kubernetes 叢集都可以安裝這個功能。 相對於原生的 Deployment 等部署方式, Openkruise 提供了
有興趣的可以研究看看此專案
]]>摘要: 本篇文章要介紹 Kubefarm 這個專案,該專案的目的是希望能夠於大量的實體機器上去創建各式各樣的 Kubernetes 叢集供不同團隊使用 為了讓整體的運作更加自動化,作者先行介紹何謂 Kubernetes in Kubernetes 這個專案,如何透過 Kubeadm 的方式於一個現存的 Kubernetes 專案 去部署 control-plane 並且透過這個 control-plane 去控管其他的 kubernetes 叢集,基本上達到的效果就如同各種 kubernetes service 服務一樣,使用者完全看不到 control-plane 的元件。
雖然透過這個方式可以很輕鬆地去創建新的 Kubernetes 叢集來使用,但是使用上覺得還是不夠方便,特別是這些實體機器還是會有不少手動的過程要處理, 為了讓整體流程更加自動化,作者團隊又基於 Kubernetes in Kubernetes 這個專案為基礎再開發更上層的專案,稱為 Kubefarm,一個如農場般可以快速於實體機器創建各式各樣 kubernetes 叢集的解決方案
Kubefarm 由三個專案組成,分別是 Kubernetes in Kubernetes, LTSP (PXE-Server) 以及 Dnsmasq-Controller 透過這三者專案的結合,實體機器會自動取得 DHCP 的 IP 地址並且透過 PXE 系統自動化安裝 OS,待一切都安裝完畢後又會自動地加入到現存的 Kubernetes 叢集中
整篇文章滿長的,是一過非常有趣的用法與研究,如果團隊是大量實體非虛擬化機器的讀者可以研究看看別人遇到什麼問題以及透過何種思路去解決的。
]]>本篇文章是 Meta 公司的技術分享文,探討內部如何搭建一個觀測 SLO 的大平台,讓不同的應用程式團隊都可以更方便地去觀察是否有達到其設定的 SLO。
文章內容有點長,這邊稍微節錄一些重點,非常推薦大家花點時間看完全文
透過 SLICK 可以讓整個 Meta 達到
文章後半部分包含
本篇是一個面試經驗探討文,作者闡述自己雖然已經有十年多的工作經驗,但是部分面試工作上還是沒有很辦法的去展現自己的能力 特別是那些用電話面試的經驗,而此文就是關於電話面試的小小抱怨文
滿多的電話面試都會搭配 Coderpad 這個網站來要求面試者線上進行程式撰寫,而該網站就要求你使用線上編輯器並且將所有的程式碼都統一到一個檔案中 ,同時也不一定有辦法去撰寫相關的測試規則,這對於作者來說非常不喜歡。 作者平常習慣開啟多個視窗進行開發,一邊撰寫程式一邊透過測試來驗證當前撰寫的程式是否往正確的方向前進,同時也花費大量時間去調整自己喜歡的工具來輔助所有 程式碼的撰寫。而上述所有習慣都沒有辦法於 Coderpad 的單一編輯器上去完成。 此外面試過程中還會被問各種問題,譬如為什麼這個變數這樣命名,為什麼blablabla... 對於作者來說,有些概念要到快完成時才會最佳化,就很明顯不符合電話面試這種要一次完美的特色。
最後作者也分享了一下關於電話面試的問題想法,相較於問一些實際上工作根本用不到的演算法問題,不如問一些更貼切真正工作會用到的經驗與概念,譬如
這讓我想到多年前也有接過電話面試直接問我 Linux 用幾個bit實作權限,但是面試官本身也非這個專頁,是哪個權限也沒辦法回答,也不能給清楚的 context,就如同教科書一樣一個問題一個答案,沒辦法針對面試者的疑問去解惑...
]]>Tinder 這種交友應用程式有幾個特點
微軟文件中的系列好文,探討雲端方面的各種設計模式,而本篇探討的是 Ambassador 模式 想法:
本部落格是一個基於 Python & Container 的系列介紹文,而本篇文章是其中一篇探討容器化 Python 要注意的事項。 標題非常簡單:「是時候停止使用 python 3.6了」 Python 3.6 的 EOL 時間就剛好是 2021/12,這也意味到 2022 年後 python 3.6 就不再享受官方的任何維護與更新。 文章中提到建議趕快升級,同時也要注意不要每次都拖到最後一刻才升級相關軟體,應該要把升級軟體的流程給整合到日常流程中,定期掃描定期更新, 否則只會不停的被不同版本的 EOL 追趕而已。
]]>Docker 基本介紹文,不知道常寫 Dockerfile 的讀者能不能分清楚 Dockerfile 內 Shell 與 Exec 兩種格式的差異 RUN, CMD, ENTRYPOINT 等指令都同時支援這兩種格式 Shell 格式就是 RUN command arg1 arg2 arg3 這種直接描述的格式,而 Exec 則是用 [] 包起來,每個參數單獨敘述,譬如 RUN ["command", "arg1", "arg2", "arg3"] 等。 本篇文章推薦 RUN 指令採取 Shell 格式而 CMD/ENTRYPOINT 都應該採用 EXEC 格式。 如果自己不清楚差異以及沒有想法為什麼平常自己這麽寫的話可以參考全文
]]>基本 Bash 介紹文,探討 trap 的用法,如何於不同的情況下正確攔截 SIGNAL,同時如果 script 中運行的程式有無背景執行 會有什麼差異,推薦給對 Bash 不熟的讀者閱讀重新複習
]]>本文是作者踩過的各種 Infrastructure 雷,希望讀者能夠避免這些雷。
總共有幾大類,分別
其中第六點簡短扼要,大概就是「沒有人知道如何正確地去安裝與打包你的 python apps, 你真的要寫一個內部的 python 工具就給我讓他完全跨平台不然就給我改用 go/rust, 不要浪費生命去思考到底該如何安裝那些東西」
這讓我想到你的 Python 跟我的 Python 每次都不一樣,有已經不支援的 python 2.x, 還有各種可能會互相衝突的 python 3.x....
]]>本篇文章分享的是建置 Container 中的 Anti-Patterns,不講哪些好反而探討哪些不好。
文內列舉了不同的主題,包含
以下針對內文幾個部分摘錄一下為什麼作者認為是個不好的模式
Image 小本身不是什麼問題,但是有時候過度追求容量會使得一些常用有幫助的工具沒有辦法於容器內執行,這可能會導致未來要除錯時要花費更多的時間去處理,可能要研究如何重新安裝該工具等。
作者有強調這個議題是非常看環境與需求的,有些情況可能團隊根本不需要進入到容器內去執行 shell 來處理,有些可能會需要到容器內執行 ps, netstat, ss 等指令來觀察不同狀態。 作者推薦可以使用 gcr.io/distroless/static-debian11 這個 image 做為基礎然後將其之間的 busybox 給複製環境中,至少確保有基本工具可以使用
BuildKit 是 docker build 的新版建置方式,相對於舊版方式來說 Buildkit 提供了更多功能,譬如平行建置,跨平台建置甚至效能上也會比過往的更好。 為了讓舊有使用者可以無痛轉移,所以 BuildKit 完全相容既有的 Dockerfile 的語法,所以切換方面是完全無腦的。 目前新版的 Docker Desktop 基本上已經預設採用 BuildKit 來進行建置,不過某些系統譬如 Linux 的環境下,還是需要透過設定環境變數來啟用這個功能,譬如 DOCKER_BUILDKIT=1 docker build . 等方式來建置。
此外透過 BuildKit 建置的產生結果跟過往不同,所以只要看建置結果的輸出就可以判別自己是否使用 BuildKit。
剩下的8個項目就留給有興趣的讀者自行閱讀
]]>歷史講古: https://blog.cloudflare.com/inside-the-log4j2-vulnerability-cve-2021-44228/
]]>今天這篇文章作者跟大家分享一些如何加強 Kubernetes 服務穩定的方式,這篇文章這邊做個簡單摘要一下 發生問題: 作者的 k8s 是基於 Google Kubernetes Service (GKE)的叢集,運作過程中有時候會發現部分節點當掉,最後導致部分的服務不能正確使用。這邊作者團隊從兩個角度出發去改善
今天跟大家分享一個 UDP 於 Linux Kernel 內的 Race Condition 問題。這問題我以前於 Linux Kernel 3.14 也有採過一樣的雷,但是到今日都還沒有一個很漂亮的解決方案,這邊就快速的跟大家介紹一下這個問題> 是什麼,以及跟 k8s 有什麼關係
相同的 Client 短時間內透過 UDP (也許是不同 thread) 送出兩個 UDP 封包到外面,對於 Linux Kernel 來說,會希望透過 conntrack 來追蹤每一條連線,但是底層建立的時候會有一些會有一些機制,因此當兩個封 包同時進入的時候,有可能就會因為先後順序導致第二個封包被丟棄
DNS 的請求封包預設情況下會同時透過 UDP 送出 A & AAAA 兩個封包,而這兩個封包如果很巧的採到這個情況,然後你的 A 封包就沒有辦法順利解出 DNS,最後就要等五秒的 timeout 來重新發送 下偏這篇文章就是 weave works 遇到 DNS 5秒 timeout 的問題,然後仔細的將我上面所寫的總結給解釋清楚,每一個步驟發生什麼事情,什麼是 conntrack 以及暫時的 workaround 是什麼 之後會在跟大家分享目前一些解決方法怎麼做
]]>今天這篇文章探討的則是 resources 底下的 request/limit 問題。 本文作者之前遇到一個非常規律的服務警告問題,花了非常多時間與步驟去查詢,最後才發現是 Pod 裡面 Resource 的設定不夠嚴謹與完善。 舉例來說, resources: limit: cpu: 1000m request: cpu: 100m 今天假設有一個服務描述,我對 cpu 的最低要求是 0.1顆,但是極限是 1顆 且有一個節點本身有 3 顆 CPU,這種情況下,我們對該服務設定多副本運行(10個). 那根據 request 的要求,10個副本頂多只需要 1 顆 cpu,所以非常輕鬆的可以將 10 個服務運行起來,但是如何今天遇到尖峰流量 ,每個 pod 都瘋狂使用 CPU會發生什麼事情? 每個副本的極限都是 1 顆,因此 10 個副本就可以衝到 10 顆 CPU..而系統上只有 3顆,這就會造成 CPU 完全不夠使用,最後導致每個應用程式都在搶 CPU 使用,如果沒有特別設定相關的 nice 值來處理,可能會造 成關鍵 process 無法回應(案例中就是kubelet)。 這案例中 limit/request = 10x,作者認為這數字太大,它覺得合理的大概是 2x ~ 5x,並且最重要的是要定期去檢視系統上資源的用量, limit 要設定的合理,如果本身有很大量需求,建議還要搭配 node select, affinity/anti-affinity 讓每個 pod 最好找到適合的配置方式,然後也要避免尖峰流量到來時,系統資源被吃光甚至影響到 kubelet/kube-proxy 等底層服務的運作。
]]>不知道大家有沒有遇過本地儲存空間滿了,再也抓不了 docker image 的慘痛經驗呢? 本文就想要探討的是遠方 Container Image 上的管理問題,隨者時間演進,愈來愈多的版本產生,那作為管理者,我們要怎麼去> 看待這些 image,放任他們無限擴張嘛? 這些資源背後都代表一個儲存空間,也就意味額外的成本開銷。 作者想要解決的問題是,如何設計一套自動機制去刪除用不到的 image tag,保留會用到的,為了解決這個問題,要先定義什麼叫做 "用得到的 image tag". 本文列舉了四種需要保留 image tag的情況 1) Production 環境正在使用的 image tag, 如果刪除了,遇到 ImagePullPolicy:Always 的情況那可真的麻煩了 2) 遇到緊急情況,應用程式需要退版,因此保留的 image tag 可不能只有當前版本,過往穩定版本也都要保留 3) 從開發角度來看需要的 image tag, 譬如我們開了一個 PR,這個 PR 有一個對應的 image tag, 再這個 PR 還沒有結束前,這個 image tag 應該都要保留讓開發者去驗證與使用 4) 最後則是特定版本號或是code name等專屬名稱 作者使用 werf 這套 k8s 建置佈署工具來幫忙,這工具除了常見的 build/deploy 外,還可以刪除遠方的 container image。 因此作者整合一套演算法,將其與 werf 整合,讓整個 CI/CD 的過程中能夠自動去產生新 的 image,並且根據需求去移除用不到的 image. 有興趣的記得點選下列原文來學習更多
]]>不知道大家有沒有聽過 Open Policy Agent (OPA) 這個 CNCF 專案? 有滿多專案的背後都使用基於 OPA 的語言 Rego 來描述各式各樣的 Policy,譬如可以使用 conftest 來幫你的 kubernetes yaml 檢查語意是否有符合事先設定的 Policy。 本篇文章則是跟大家分享如何使用 OPA 來針對 Ingress 資源進行相關防呆與除錯,一個最基本的範例就是如何避免有多個 Ingress 使用相同的 hostname 卻指向不同的 backend service. 過往可能都是靠人工去維護 ,確保沒有一致的名稱,但是透過 OPA 的概念我們可以再佈署 Ingress 到 Kubernetes 前先進行一次動態的比對,確保當前設定符合所有 Policy,得到所謂的 Approved 後才能夠佈署進去。 有興趣的人可以看看這篇文章,甚至學習一下 OPA 的使用方式
]]>LWN.net 這幾天有一個文章是關於基於安全性考量下,改用其他工具取代 scp, 譬如使用 sftp 以及 rsync. 有常常使用 scp 這個工具的人可以看一下這篇文章討論的原因,以及如果要改成使用 rsync, 可以有什麼樣的參數使用
]]>這邊跟大家分享一篇 GitOps 實作心路歷程,這篇文章中總共使用下列工具
不知道大家第一次接觸 kubernetes 的時候都是使用哪套解決方案來打造你的 K8s 叢集? 亦或是作為一個開發者,你平常都怎麼架設 K8s 來本地測試? 這篇文章提到了作為一個 Local Kubernetes Cluster 幾個選擇,並且點出了三個需要解決的問題
今天要來跟大家分享一個單一節點如何提高應用程式吞吐量與服務能力的方式 這個方式主要探討的是應用程式對於網路連線的 I/O 模型,試想一個常見的使用範例。 一個主要的 Process 會去聽取一個固定的 port number (ex port 80),並且通知後面眾多的 worker 來幫忙處理這些封包連線,而這些 worker 的工作就是處理連線。 整個架構中是一個 1 v.s N 的狀況, 一個負責 Listen ,N個負責處理連線內容 而今天要分享的則是想要讓架構變成 N v.s N 的狀況, 會有 N 個 Process, 每個 Process 配上一個 Worker。 而這 N個 process 同時共享一樣的 Port (ex, port 80) 這種情況下可以減少多個 worker 共享一個 listen socket 時的各種保護機制,取而代之的則是每個 listen socket 配上一個專屬的 worker 來處理。 要達成這樣的架構非常簡單,只要透過 SO_REUSEPORT 這個 socket option 告 訴 Kernel 當前這個 PORT 可以重複使用。 當封包送到 kernel 後則是由 kernel 幫你分配封包到所有使用相同地址的 Listen Socket (Process) 根據 nginx 官方文章的測試,這種架構下對於 RPS (Request per second) 有顯著的提升,有興趣的可以看看下列兩篇文章
]]>這邊跟大家分享一篇關於 Kubernetes 多租戶的相關文章,該文章中探討到底多租戶的定義,以及實現上的難易程度
如果你機會跑過 kubernetes 1.18 版本,一定要試試看最基本的 kubectl get pods -o yaml,看看是不是內容裡面多出了非常多 f:{} 系列的檔案,導致整個 Yaml 變得非常冗長,閱讀不易,甚至想要抓取到最原始> 的內容都非常麻煩。 Kubernetes 官方 Github 上還有相關的 issue 再討論這個欄位,詢問是否有辦法能夠清除。不少人都提出了一些希望的用法來處理 Issue: https://github.com/kubernetes/kubernetes/issues/90066 目前看下來最簡單的做法還是透過 kubectl plugin, kubectl-neat 來幫忙完成,可以透過 krew 這個 kubectl 管理工具來安裝管理 https://github.com/itaysk/kubectl-neat 此工具可以將 Server 上得到 Yaml 的內容給整理最後得到最初的檔案 至於到底什麼是 managedFiles? 這個由欄位的出現是因為 1.18 以後,已經將 Server Side Apply 更新策略預設啟用而導致的,而 Server Side Apply 則是一種用來管理 Declarative 設定檔案的方式,對使用者來> 說基本上完全無感,因為一切都還是透過 kubectl apply 來使用,只是到底如何判斷 當前檔案內容與系統上內容誰先誰後,誰對誰錯,甚至當有人透過 kubectl edit 去編輯內容的時候,到底該怎麼更新。
]]>Rancher v2.5 版本與過往的差異,這邊就來重點節錄一些改變
如果有在 Kubernetes 內部署 Java 應用程式的人,千萬不要錯過這篇文章,此文章中分享 Java 應用程式關於 Thread Pool Size 的問題,同時當 Java 應用程式容器化並且部署到 Kubernettes 內之後,該怎麼設定 JVM 來讓其能夠更高效率的於容器化環境下工作
]]>想必大家應該都聽過 Operator 的概念,透過 CRD 自定義資源格式並且配上程式化的運作邏輯來控管相關資源的操作。甚至有廠商針對 Operator 的概念來設計一個 Framework 讓大家能夠更輕鬆或是有效率的撰寫屬> 於自己的 Operator。 然而 Operator 真的一定好嗎? 底下這則推文則是來自於 Darren Shepherd(CTO/Co-founder Rancher Lab ) 對於一篇由 RedHat 所發表關於 Operator 好處文章的反面看法。 其推文最後表示:「Right now invest your IT teams time in GitOps, not operators.」 快來看看 Darren 與其他網友針對這些議題的討論,並且分享看看你的想法
]]>Terraform 這個工具想必大家都玩過也聽過,這邊非常推薦大家升級到 0.13 版本,這個版本中解決了關於 Module 之間依賴性的問題,能夠使用原先就有的 depends_on 的語法來直接描述,而不需要按照過往以前用> 各種 fake resource 等機制來完成,整個 Terraform 程式碼會更佳清晰與簡單!
]]>想必大家一定都有使用過 CPU Limit 的經驗,透過這個機制能夠確保每個 Container 使用的 CPU 資源量,也可以保證每個節點上面會有足夠 CPU 供 Kubernetes 原生服務 (kubelet) 使用。 然而本篇文章就要來跟大家分享一個設定 CPU Limit 反而造成效能更差的故事,故事中當 CPU 設定為 800ms 的時候,卻發現實際運行的 Container 最高大概就只有 200ms 左右,這一切的一切都是因為 Liniux Kernel 的臭蟲導致! 一個直接的做法就是針對那些本來就沒有過高 CPU 使用量服務取消其 CPU Limit,作者於文章中也探討了一些機制要如何保護與應對這些被移除 CPU 限制的服務。 這個臭蟲於 Linux Kernel 4.19 後已經修復,但是要注意你使用的發行版本是否有有包含這個修復,作者列出一些已知的發行版本修復狀況 Debian: The latest version buster has the fix, it looks quite recent (august 2020). Some previous version might have get patched. Ubuntu: The latest version Ubuntu Focal Fosa 20.04 has the fix. EKS has the fix since December 2019, Upgrade your AMI if necessary. kops: Since June 2020, kops 1.18+ will start using Ubuntu 20.04 as the default host image. GKE: THe kernel fix was merged in January 2020. But it does looks like throttling are still happening.
]]>這篇分享一篇非常有趣的問題,從 Container 的實作開始介紹,最後介紹幾個與 Container 相關的 CVE 對於資安有興趣的可以研究看看
]]>本篇文章列出了七個企業想要踏入 Cloud Native 之路上最常遇到的問題 以下幫大家總結並節錄一點小內文
一篇關於 Service Mesh 的好文,發布已經有段時間了不過還是值得一讀, 文章作者是非常早期 Service Mesh 項目: Linkerd 的核心開發成員之一也是新創公司 Buoyant 公司的 CEO 相信大家應該對於 Service Mesh 一詞已經不陌生,可能對於這個名詞比較熟悉的朋友大多是從另一個 Service Mesh 項目: Istio 去了解 Service Mesh 的面貌,從這篇文章你可以從不同觀點認識 Service Mesh , 全文非常長內容涵蓋:
不知道大家是否都有使用 Network Policy 來設定 Kubernetes 內部的 ACL? 這邊有個叫做 OPA 的工具可以用幫你驗證你的 Network Policy 是否運作良好,甚至當有新的應用服務要部署的時候,也會確定是否有跟 Network Policy 衝突 有興趣的人可以研究看看
]]>這邊跟大家分享一篇 EKS 升級的心得文章,該文章記錄了 EKS 從 k8s 1.17 到 1.18 的過程,並且先分享了幾個 1.18 主要新功能,包含了
2021 展望
結語
2020 受到疫情影響,導致有八個月的時間都長期遠端工作,整體工作生活習慣改變,包含工作,煮飯,運動等彼此的時間調整。
也因為如此有更多的彈性時間去思考自己要的東西,同時準備課程與鐵人賽文章。整體的時間管理能力目前還算滿意
]]>今天就來跟大家分享一下,這六個多月以來遠端工作 (Work From Home, WFH) 的一些想法與心得
個人目前對於遠端工作是秉持利大於弊的心得,接下來從不同點來探討利弊
矽谷灣區最著名的一個特色就是塞車,上班塞車,下班塞車,永遠都在塞車,所以過往很多人都會採取彈性上班的方式來避免車流,或是採取大眾運輸工具的方式來通勤。
以我個人為例,過往每天上班的通勤時間大概平均兩個小時,主要分成開車與大眾運輸
遠端工作後,每天可以省下大約兩個小時的時間,這部分可以拿來上班工作,也可以拿來處理私人事情,時間上更加彈性
遠端在家工作後,最大的幾個變化就是
工作自律 基本上一整天就是在電腦前面,所以沒有開會的時候,其實都是自己去控制自己的工作節奏,這部分好壞的意見都有聽過,大家也會彼此分享如何讓自己工作更加專心,而不會被家裡的舒適環境給影響。
會議全面遠端化且數量上升 過往一些小事情要討論時,可能就直接現場約一約,到小會議室直接討論。然而目前則是因為遠端會議,所以全部都要事先預約時間來確保對方目前在電腦前。有時候一個小事情卻要花更多的時間來定案,我個人認為效率是不如過往的。 此外之前也出現如 Zoom Fatigue 這樣的說法,過多的會議內容導致大家開會疲倦,分心
就我個人的心得來看,自己能夠維持好工作的節奏,該完成的工作事項能完成就好,此外被打擾中斷工作的機會也更少了,整體而言利大於弊,
員工之間的會面都是透過線上會議,缺少實體見面的互動,這部分也是見仁見智。有些人不喜歡交流,覺得 「上班當同事,下班不認識」是其工作原則,那應該會滿喜歡這種模式的。但是也有人喜歡實體交流,每天吃個一起吃個午餐,聊聊一些工作以外的事情,分享彼此的事情也都是一種生活。
這部分我就沒有太大意見,有好有壞,偶而還是會透過 slack 與同事聊聊幹話,分享最近的生活,只是少了聲音的互動。
遠端工作以後,公司內本來的福利近乎全滅,什麼咖啡,零食,飲料,冰箱內各種飲品,甚至免費午餐等都不再擁有,一切都要依賴自己處理。
對於一個自住的邊緣人來說,變成每天都要煩惱該怎麼處理今天的飲食,「每餐叫外送,荷包先消瘦」是一個顧慮點,自己煮飯又是一個額外的挑戰,買菜/備料/烹飪/善後四個步驟往往也花費不少時間在處理,如果將這個時間與通勤時間組合起來,未必可以省下時間。
我個人平常就習慣煮飯,已經練就如何快速料理與一次準備多日便當,所以這個情況比較不會有太大問題。 倒是我有聽過朋友因為沒有公司零食的迫害,遠端工作期間被迫減肥,也算是一個附加好處
一個美好的夢想就是辦公室可以縮編,「公司省租金,員工可加薪」
疫情以來,各大公司陸陸續續宣布相關的遠端工作政策,而規範的工作日期也隨者疫情的擴散而延後,下面就整理目前我知道部分公司的工作內容,此外政策不一定適用於所有員工,必要性的情況下,部分員工還是要定期前往辦公室。
Apple: 員工可以遠端工作直到 2021 初期 Google: 員工至少可以遠端工作到 2021/07 Facebook: 員工至少可以遠端工作到 2021/07 Twitter: 員工可以選擇永遠遠端工作 Fujitsu: 員工可以選擇永遠遠端工作 Microsoft: 可以遠端工作直到 2021/02 Amazon: 員工至少可以遠端工作到 2021/01 Netflix: 員工遠端工作直到全面接受疫苗
此外,也有人對於遠端工作提出了一些負面說詞,譬如 Netflix 董事長則認為遠端工作沒有帶來正面效果,反而使得討論工作更加困難。
這部分真的就是沒有定案,完全見仁見智,而 Google 最近的調查則顯示只有 10% 不到的員工想要回到過往天天進辦公室的日子,
以下節錄自 Google 官方推特
除了公司本身政策的變動外,居住地遷移最近也是活動頻繁,譬如 Oklahoma 洲則有 Tulsa Remote 計畫,向所有遠端工作者提供一萬美元的獎勵,只要你願意搬來這邊即可。 根據報導指出,舊金山的兩房公寓每個月可能略低於四千美金,而 Tusla 則不到一千美金,再租金花費方面可以說是節省不少開銷。
就我過去五年的台灣工作經驗,我認為台灣公司要跟進遠端工作政策實屬不易,幾個原因如下
Costco
愛好者,每個禮拜的蛋白質基本上都是從 Costco
取得常買的有
這篇文章主要用來記錄冷凍鮭魚排的資訊,每一份的大小以及熱量等資訊
Costco
基本上有滿多的鮭魚可以購買,有在冷藏處比較新鮮的挪威鮭魚,也有在冷凍庫的冷凍鮭魚。
由於我大部分都是製作便當,因此任何的食材在烹飪完畢後都會冷藏並且隔天透過微波爐來加熱飲食
因此在選擇上我都還是以冷凍鮭魚為主,只有特別想要當天吃才會選擇冷藏鮭魚
本篇主要以冷凍鮭魚為主,其包裝如下
根據 包裝背後的標示,本包裝大概含有 2KG 的鮭魚,然而實際上裡面的包裝物並不是真的如其所說有 20 份這麼多,其實每片鮭魚大小都頗大的。
實際上拿出一片使用電子秤來進行實測,測出來的重量大概是 300g 左右
因此一整包的份量大概會落在7片左右,畢竟每片鮭魚的大小都會有點差距,不過大概可以記住一片300g就好
本篇就直接使用該包裝背後所提供的成份表來計算每片鮭魚排的營養素
將常見的營養素基於 100
以及 300
公克列舉出來
100公克 | 300公克 | |
---|---|---|
熱量 | 149 KCal | 447 KCal |
蛋白質 | 17.9 g | 53.7 g |
粗脂肪 | 8.5 g | 25.5 g |
飽和脂肪 | 2.2 g | 6.6 g |
碳水化合物 | 0.1 g | 0.3 g |
鈉 | 59 mg | 177 mg |
這樣來看如果今天一餐吃一片冷凍鮭魚排的話,,攝取的蛋白質大概是 54 g 左右,熱量450卡,如果單純考慮價錢與蛋白質的比例的話,還是雞胸/雞腿的比例比較好,不過換換口味吃個鮭魚也不錯
基本上好好的煎就沒有什麼問題了,唯一要注意的是要確認內部比較厚的部分需要比較長的時間才會熟透,可以切半去料理會比較快。
]]>Costco
愛好者,每個禮拜的蛋白質基本上都是從 Costco
取得常買的有
這篇文章主要用來記錄雞腿排的資訊,包含來源,熱量等資訊
基本上 Costco
雞腿肉的來源有兩家,分別大成
以及 卜蜂
這兩家供應商。
購買時包裝上面都會顯示這兩家的名稱與Logo,除了供應商名稱不同外,其價格與大小大致上並沒有差異。
根據 Costco 線上官網 的圖示說明,每包 Costco
去骨雞腿都是一次六包為一個單位
如下圖
六包的重量大概都是落在 2.5kg 上下左右,平均下來每包平均 400多克左右。
但是實際上在食用時,每一包內的去骨雞腿牌數量則是 2-3 片,這部份就是隨機的。
為了能夠簡單的估算每一包雞腿排的營養素,我們必須要先知道每一塊的重量
這邊實際拿電子秤來實測看看這兩片雞腿排的重量
根據上述的結果,每片雞腿排的重量大概落在 170 公克左右
根據食品藥物消費者知識服務網 提供的食品營養素估算表
我們使用肉類
->雞類
->清腿
這個分類來估算營養成分
將常見的營養素基於 100
以及 170
公克列舉出來
100公克 | 170公克 | |
---|---|---|
熱量 | 157 | 267 |
粗蛋白 | 18.5 | 31.4 |
粗脂肪 | 8.7 | 14.8 |
飽和脂肪 | 2.5 | 4.3 |
總碳水化合物 | 0 | 0 |
這樣來看如果今天一餐吃包,攝取的蛋白質則介於 60
~ 90
公克之間,取決於你那一包裡面有多少片雞腿排
雞腿肉相對於雞胸肉來說料理非常簡單,基本上只要現煮(烤/煎/)等各式料理方法都不會太難吃,所以就邊就不多述相關的料理方式了
#參考來源
]]>blkparse will combine streams of events for various devices on various CPUs, and produce a formatted output the the event information. It take the output of above tool blktrace and convert those information into fency readable form.
In the following, We will use those tools blktrace and blkparse to help us to observe sector numbers which has been written by fio requests. We will use the fil to generate two diffenrt IO pattern requests, sequence write and random write.
OS: Ubuntu 14.04 Storage: NVME FIO: fio-2.19-12-gb94d blktrace: 2.0.0 blkparse: 1.1.0
you can use following commands to install blktrace and blkparse
apt-get install -y blktrace
In order to make the output of blkparse more easily to read, we set the numjobs to 1. Following is my fio config
[global]
iodepth=256
numjobs=1
direct=1
time_based
runtime=120
group_reporting
size=5G
ioengine=libaio
filename=/dev/nvme1n1
[rw]
bs=4k
rw=randwrite
[sw]
bs=64k
rw=write
After we setup the fio config, use the fio to generate the IO request. In this example, we ask the fio to generate the IO via sequence write pattern.
fio ${path_of_config} section=sw
During the experiment, you can use the tool iostat
to monitor the I/O information about the device we want to observe.
Open other terminal and use blktrace
to collection the data, there are two parameter we need to use,
First one is -d, which indicate what target device blktrace will monitor to.
Second, is -w, we use it to limit the time (seconds) how long blktrace will run.
So, our final command looks like below.
blktrace -d /dev/nvme1n1 -w 60
In the end of blktrace, you can discover some new files has created by blktrace and its prefix name is nvme1n1.blktrac.xx The number of files is depends how may CPUs in your system.
-rw-r--r-- 1 root root 821152 Jun 2 10:39 nvme1n1.blktrace.0
-rw-r--r-- 1 root root 21044368 Jun 2 10:39 nvme1n1.blktrace.1
-rw-r--r-- 1 root root 462864 Jun 2 10:39 nvme1n1.blktrace.10
-rw-r--r-- 1 root root 737960 Jun 2 10:39 nvme1n1.blktrace.11
-rw-r--r-- 1 root root 865872 Jun 2 10:39 nvme1n1.blktrace.12
-rw-r--r-- 1 root root 755248 Jun 2 10:39 nvme1n1.blktrace.13
-rw-r--r-- 1 root root 4675176 Jun 2 10:39 nvme1n1.blktrace.14
-rw-r--r-- 1 root root 4471480 Jun 2 10:39 nvme1n1.blktrace.15
-rw-r--r-- 1 root root 5070264 Jun 2 10:39 nvme1n1.blktrace.16
-rw-r--r-- 1 root root 5075040 Jun 2 10:39 nvme1n1.blktrace.17
-rw-r--r-- 1 root root 5062104 Jun 2 10:39 nvme1n1.blktrace.18
-rw-r--r-- 1 root root 5586936 Jun 2 10:39 nvme1n1.blktrace.19
-rw-r--r-- 1 root root 3718848 Jun 2 10:39 nvme1n1.blktrace.2
Now, we can use the blkparse to regenerate human-readable output form the output we get via blktrace before.
We need to indicate source files, you can just use the device name without .blktrace.xx, for example, nvmen1, it will search all files which match the pattern nvmen1.blktrace.xx and put together to analyze. Then, the -f option used to foramt the output data, you can find more about it via man blkparse
OUTPUT DESCRIPTION AND FORMATTING
The output from blkparse can be tailored for specific use -- in particular, to ease parsing of output, and/or limit output fields to those the user wants to see. The data for fields which can be output include:
a Action, a (small) string (1 or 2 characters) -- see table below for more details
c CPU id
C Command
d RWBS field, a (small) string (1-3 characters) -- see section below for more details
D 7-character string containing the major and minor numbers of the event's device (separated by a comma).
e Error value
m Minor number of event's device.
M Major number of event's device.
n Number of blocks
N Number of bytes
p Process ID
P Display packet data -- series of hexadecimal values
s Sequence numbers
S Sector number
t Time stamp (nanoseconds)
T Time stamp (seconds)
u Elapsed value in microseconds (-t command line option)
U Payload unsigned integer
For our observation, we use %5T.%9t, %p, %C, %a, %S\n to format our result containing timestamp, command, process ID, action and sequence number.
Since the data I/O contains many action, such as complete, queued, inserted..ect. we can use option -a to filter actions, you can find more info via man blktrace. In this case, we use the write to filter the actions.
In the end, use the -o options to indicate the output file name.
barrier: barrier attribute
complete: completed by driver
fs: requests
issue: issued to driver
pc: packet command events
queue: queue operations
read: read traces
requeue: requeue operations
sync: synchronous attribute
write: write traces
notify: trace messages
drv_data: additional driver specific trace
The command will look like below and it will output the result to file output.txt.
blkparse nvme1n1 -f "%5T.%9t, %p, %C, %a, %S\n" -a write -o output.txt
open the file, the result looks like
0.000000000, 22890, fio, Q, 1720960
0.000001857, 22890, fio, G, 1720960
0.000005803, 22890, fio, I, 1720960
0.000009234, 22890, fio, D, 1720960
0.000036821, 0, swapper/0, C, 1996928
0.000067519, 22890, fio, Q, 1721088
0.000068538, 22890, fio, G, 1721088
0.000071531, 22890, fio, I, 1721088
0.000073102, 22890, fio, D, 1721088
0.000093464, 0, swapper/0, C, 1994624
0.000123806, 0, swapper/0, C, 1785472
0.000147436, 22892, fio, C, 1784576
0.000159977, 22891, fio, C, 1997312
0.000166653, 22891, fio, Q, 2006912
0.000167632, 22891, fio, G, 2006912
0.000169422, 22891, fio, I, 2006912
0.000171178, 22891, fio, D, 2006912
0.000188830, 22892, fio, Q, 1817728
0.000189783, 22892, fio, G, 1817728
0.000191405, 22892, fio, I, 1817728
0.000192830, 22892, fio, D, 1817728
0.000202367, 22891, fio, Q, 2007040
0.000203160, 22891, fio, G, 2007040
0.000205969, 22891, fio, I, 2007040
0.000207524, 22891, fio, D, 2007040
0.000227655, 22892, fio, Q, 1817856
0.000228457, 22892, fio, G, 1817856
0.000231936, 22892, fio, I, 1817856
....
Since the fio will fork to two process to handle the process, we use the grep to focus on one specific process (pid=22892).
grep "22892, fio" output.txt | more
Now, the result seems good, we can discover the sequence number (fifth column) is increasing. One thing we need to care about is the row which action is "C", which means the completed, since we don't know how NVME handle those request and reply to upper layer. we only need to focus on other action. such as "Q (queued This notes intent to queue i/o at the given location. No real requests exists yet.)" or "I (inserted A request is being sent to the i/o scheduler for addition to the internal queue and later service by the driver. The request is fully formed at this time)".
0.000147436, 22892, fio, C, 1784576
0.000188830, 22892, fio, Q, 1817728
0.000189783, 22892, fio, G, 1817728
0.000191405, 22892, fio, I, 1817728
0.000192830, 22892, fio, D, 1817728
0.000227655, 22892, fio, Q, 1817856
0.000228457, 22892, fio, G, 1817856
0.000231936, 22892, fio, I, 1817856
0.000233530, 22892, fio, D, 1817856
0.000360361, 22892, fio, Q, 1817984
0.000361310, 22892, fio, G, 1817984
0.000364163, 22892, fio, I, 1817984
0.000366696, 22892, fio, D, 1817984
0.000536731, 22892, fio, Q, 1818112
0.000537758, 22892, fio, G, 1818112
0.000539371, 22892, fio, I, 1818112
0.000541407, 22892, fio, D, 1818112
0.000670209, 22892, fio, Q, 1818240
0.000671345, 22892, fio, G, 1818240
0.000673383, 22892, fio, I, 1818240
0.000676260, 22892, fio, D, 1818240
0.001885543, 22892, fio, Q, 1818368
0.001887444, 22892, fio, G, 1818368
0.001891353, 22892, fio, I, 1818368
0.001895917, 22892, fio, D, 1818368
0.001934546, 22892, fio, Q, 1818496
0.001935468, 22892, fio, G, 1818496
0.001936891, 22892, fio, I, 1818496
0.001938742, 22892, fio, D, 1818496
0.001965818, 22892, fio, Q, 1818624
Now, we can do all above command again and change the section to rw for fio using the randon write pattern. The blkparse result will show the random sequence number.
In this article, we try to use tools blktrace and blkparse to analysiz the block level I/O for fio request. We observe the filed sequence number to make sure thhat the fio can generate the sequence or random according to its config.
為了進行效能上的分析,首要條件就是先將 DRBD 給衝到效能瓶頸才有機會去觀察,所以本文採用下列的環境與工具來進行這項作業
CPU: Intel(R) Xeon(R) CPU E5-2695 v3 @ 2.30GHz Storage: Non-Volatile memory controller(NVME) Tool: fio OS: Ubuntu 16.04 with linux 4.4.0-78-generic
為了更方便觀察 drbd 的運行,我們將 drbd 創造的 kernel thread 都分別綁在不同的 cpu 上,這樣可以讓每隻 kernel thread 盡可能去使用cpu。
ps
or htop
取得 kernel thread 的 pid,這邊可以關注的有taskset
這個指令將上述程式分別綁到不同的 cpu 上taskset -p 0x100 18888
...
本文使用 fio 來進行資料的讀取,下方提供一個簡易的 fio 設定檔,可依照自行的環境變換修改。
[global]
iodepth=512
numjobs=3
direct=1
time_based
runtime=30
group_reporting
size=5G
ioengine=libaio
filename=/mnt/beegfs/fio1.test
[rrw]
bs=4k
rw=randrw
rwmixread=75
[rr]
bs=4k
rw=randread
[rw]
bs=4k
rw=randwrite
[sr]
bs=64k
rw=read
[sw]
bs=64k
rw=write
我們 fio 採用 client/server 的架構,要是可支援多台 client 同時一起進行資料讀取,提供更高的壓力測試。
假設該設定檔名稱為 fio.cfg,並且放置於 /tmp/fio.cfg 則首先在 node-1 上面執行下列指令以再背景跑一個 fio server
fio -S &
接下來要運行的時候,執行下列指令來運行 fio,其中若想要改變測試的類型,可透過 --secion進行切換。
/fio --client=node-1 /tmp/fio.cfg --section=rw
這時候可以透過 htop 以及 iostat 的資訊去觀察,如下圖 當前透過 iostat 觀察到的確對 drbd0 有大量的讀寫動作 同時由 htop (記得開啟 kernel thread觀察功能),可以看到 drbd_s_r0 以及 drbd_a_r0 都各自吃掉一個 cpu,大概都快接近 100% 的使用率。
有了上述的環境後,我們就可以準備來分析 drbd 程式碼運行狀況。
這邊使用 perf 這套程式來分析,基本上 kernel 新一點的版本都已經內建此功能了,比較舊的 kernel 則需要自己重新開啟該 kernel config然後重新 build kernel,所以這邊使用 Ubuntu 16.04 with linux 4.4.0-78-generic 相對起來非常簡單。
直接執行 perf
這個指令,若系統上有缺少 linux-tools-4.4.0-78 相關 tool 的話會有文字提示你,如下所示,按照提示使用 apt-get 將相關的套件安裝完畢後,就可以使用 perf 了。
WARNING: perf not found for kernel 4.4.0.78
You may need to install the following packages for this specific kernel:
linux-tools-4.4.0-78-4.4.0-78
linux-cloud-tools-4.4.0-78-4.4.0-78
perf 的功能非常強大,可以參考 wiki, 這邊我們使用 perf top 的方式來觀察結果。
為了可以順便觀看 call graph 的過程,再執行perf
的時候要多下-g
這個參數
指令為 perf top -g -p $PID,如 perf top -g -p 18888。
在這邊我嘗試觀察 drbd_a 這隻,結果如下列
這邊可以觀察到三隻吃比較兇的 function 都吃很兇,分別是 native_queue_spin_lock_slowpath 、 tr_release 以及 idr_get_next。
這邊比較麻煩的是你看這個只能知道就是卡在 spin_locK,系統上是不是 multithread,然後有太多的資料要搬移導致 spin_lock ? 這些搬移的資料是誰放進去的?,這些資料是什麼?
以及更多的問題都必須要看程式碼去理解其整體設計架構,才有辦法講出一套完整的流程說明這個結果。
這部份等之後能夠完整理解整個 drbd 的 write-path 或是 read-path 時再重新來看一次這張圖,到時候應該會有完全不一樣的思維。
]]>使用 curl http://localhost/512M
當作第一個比較
原始版本的 curl 關於 malloc 相關數據如下
Mallocs: 33901
Reallocs: 5
Callocs: 24
Strdups: 31
Wcsdups: 0
Frees: 33956
Allocations: 33961
Maximum allocated: 160385
而修改後的版本為
Mallocs: 69
Reallocs: 5
Callocs: 24
Strdups: 31
Wcsdups: 0
Frees: 124
Allocations: 129
Maximum allocated: 153247
比較起來可以發現, malloc 呼叫的次數有急遽的下降,從 33901 降落到 69,而整體使用的記憶體也少了 7KB 左右 此外,若比較兩者傳輸的速度,抓取一個 80GB 的檔案
Original: 2200MB/sec
Modified: 2900MB/sec
在傳輸方面提升了 30% 左右的速率,非常驚人 若使用 time 指令來比較新舊版本抓取 80G 檔案的差別
Old code:
real 0m36.705s
user 0m20.176s
sys 0m16.072s
New code:
real 0m29.032s
user 0m12.196s
sys 0m12.820s
修改相關的 commit 如下
簡單來說就是將 malloc 的部分都拔除,盡量使用 stack 來提供記憶體,藉此減少呼叫 malloc 的部分。 主要是因為 curl 在傳輸的過程中,會有非常大量且小空間的 malloc 被呼叫到,這部分拖慢的整體的運行速度
]]>原題目是付費題目,有興趣看到完整的請自行付費觀賞,在此就不提供超連結了。
0
/ \
1 4
/ \ / \
2 35 6
/ \
7 8
輸出為 [2][1] [0,3,5][4,7] [6][8]
這題不太困難,基本上可以採用 BFS 來搜尋整個 tree,然後加入一個 index 的欄位,root 的 index 是 0,往左遞減,往右遞增,在 BFS 的過程中,就把相同 index 都收集起來,最後再一口氣輸出即可。
pseudo code 如下
queue.push(pair(0, root));
while (!queue.empty()) {
index = queue.front().first;
node = queue.front().second;
ans[index].push(node->val)
if (node->left)
queue.push(pair(index-1, node->left);
if (node->right)
queue.push(pair(index+1, node->right);
}
return ans;
沒想到過了幾年後,Chun Norris 竟然出書了!!! 英、日語同步Anki自學法:我是靠此神器,最短時間通過日檢N1、多益975分
看到這個消息後,就馬上預購了一本這個書,不但捧朋友的場,同時也順便瞭解看看到底 Anki 是什麼樣的東西。
對於想瞭解 Anki 基本操作的,可以參考這邊 經過了一陣摸索後,也開始使用了 Anki 來幫助我背單字,不過由於內建的一些卡片集(Deck)大都偏向特定主題,如托福、多益等 ,所以我後來也自己創建了一個卡片集給我自己使用。 在卡片編輯的部分,原本都是透過Anki application上面的 GUI 去操作編輯,填寫正反兩面的卡片資訊。 所以本來的流程是這樣
上面第三步驟最花費時間,當卡片數量一多的時候,其實要非常可怕的 那時候APP內大概有五六百個查詢過的單字,每個單字花20秒去填寫,也要整整三個小時不間斷才有辦法處理完畢。
有鑑於未來單字量只會愈來愈多,這樣手動下去實在不是辦法,因此腦筋就轉了彎一下,看有沒有辦法讓上面的步驟簡單化 上述(1),(2)這兩個步驟是不可避免的,那(3),(4)這兩個步驟就是主要處理的對象了。
針對我的目標,我將其拆成兩部分
針對第一點,我使用 python 作為我的程式語言,然後很偷懶的就拿了 yahoo 字典當作我的搜索來源 於是用了 python + BeautifulSoup + urllib 來爬網頁, 於是就網頁爬阿爬~就爬出了發音跟解釋了,這邊一切搞定
再來第二點,想要自動加入倒 Anki 的資料庫中,於是就到github去翻一翻,還真的翻到有人寫好已經可以用的工具 addToAnki,這個作者使用 python 撰寫,提供一個 tool 讓第三方可以將卡片的內容輸入進去後,自動轉為卡片並且塞到對應的卡片集中。 雖然我個人是更偏向去呼叫 library 而不是只接呼叫 tool 來處理,不過我的目的能達成就好,所以就將我前面的程式碼跟他的結合起來。目前將此結果放在這邊 同時我也發送了一個 pull request 到原先作者那邊,把我的使用情境當作一個多的範例使用,不過我看作者也兩三年沒碰了,應該也不會去接收我的 PR XDD
最後我的流程就是
上次一口氣加了四百多個單字,總共花費十五分鐘左右,大部分的時間應該都是消耗再去爬 yahoo dict這邊,目前這樣已經滿足我的需求。
]]>commit所使用的編輯器會依照下列優先度去選擇,
變動檔案請用 git mv
,使用git rm
要注意檔案系統內的檔案會被真的刪除。
git log
可以列出簡略的coommit資訊
git show [commit id]
可以看詳細的commit資訊,可以加上commit ID來指定特定的commit
git show-branch --more=10
可以看當前bracnh的詳細commit資訊,由--more控制數量
總共有三種設定方式,優先度如順序
--file
或是預設的方式操作--global
操作--system
操作git config --global user.name "hwchiu" (2)
git config user.email "hwchiu@cs.nctu.edu.tw" (1)
git config -l
列出當前所有的設定--unset
來移除設定git config --unset --global user.name
用Unix的指令,透過pipe的方式完成下列要求
$$
;
||
&
>
>>
<
ls
來取得所有資料夾跟檔案的資訊.
跟..
給顯示出來,因為這種當前目錄的東西我們不需要sort
來幫忙排序,找出檔案大小前五個awk
作最後的處理,找出前五大,印出所有檔案大小和ls -l
的結果中,會有很多資訊,包含 ./cs/.svn/pristine/74:
或者 total 28
,所以awk再處理的時候,先用NF判斷該行的欄位數,至少要有9個欄位才處理 if(NF>=9)
-rw-r--r--
的第一個欄位來決定,如果是d就代表資料夾,否則就是檔案。 這邊我使用 regular expression來判斷 ($1 ~/^d/)? (dir=dir+1) : (file=file+1)(size=size+$5)
,此外如果是檔案得話,就順便把大小也計算一下if(NR<6) print NR": "$5" "$9}
ls -RlA | sort -rnk 5 | awk '{ if(NF>=9) ($1 ~/^d/)? (dir=dir+1) : (file=file+1)(size=size+$5); if(NR<6) print NR": "$5" "$9} END{ print "Dir = "dir"\n" " File = " file"\n" "total = "size}'
sudo dd if=/dev/zero of=/zfs1 bs=1M count=256
sudo dd if=/dev/zero of=/zfs2 bs=1M count=256
zpool create ftphome mirror /zfs1 /zfs2
zpool destroy ftphome
zpool status <pool>
zpool export ftphome
zpool import -d / ftphome
(用-d指定你檔案的位置,預設會去吃/dev/)//zfs1
, //zfs2
,多了一個/,原因不明中。zpool attach ftphome /xxx
zpool detach ftphome /zfs1
還有offline
,online
,remove
...,剩下的就要用的時候去man zpool,還滿詳細說明的。
zfs set key=value <filesystem|volume|snapshot>
zfs get compression ftphome
zfs set mountpoint=/home/ftp ftphome
zfs get key <filesystem|volume|snapshot>
zfs get compression ftphome
zfs snapshot ftphome@today
zfs list -t snapshot
snatshot 該zfs
zfs snapshot ftphome@today
zfs list -t snapshot
看一下是否有成功塞爆該空間
zfs list
看一下還剩下多少空間dd if=/dev/random of=/home/ftp/file bs=1M count=256
cd /home/ftp
rm file
=> 應該會得到 No space left on device
空間不足的訊息。ZFS 變大容易(多塞個硬碟即可),變小困難(幾乎無法),因此當ZFS的硬碟滿的時候,有兩種做法
先幫本來的pool加入一個檔案,增加本來的空間,如此一來才可以做更多操作
dd if=/dev/zero of=/zfs5 bs=1M count=128
dd if=/dev/zero of=/zfs6 bs=1M count=128
zpool add ftphome mirror /zfs5 /zfs6
zfs list
(此時可以看到本來的空間變大了)創造一個更大的zpool來取代
dd if=/dev/zero of=/zfs3 bs=1M count=512
dd if=/dev/zero of=/zfs4 bs=1M count=512
zpool create ftphome3 mirror /zfs3 /zfs4
zfs set compression=gzip-9 ftphome2
把資料複製過去
zfs snapshot ftphome@send
zfs send ftphome@send | zfs receive -F ftphome2
zfs list
看一下大小是否相同mount新的,舊的砍掉
zfs umount ftphome
zfs set mountpoint=/home/ftp/ ftphome2
zpool destroy ftphome
做到這邊,就算完成了,成功的把本來的資料複製過去。
如果想要改變zpool的名稱,可以用export
跟import
來改名稱。
set encoding=utf-8
set fileencodings=ucs-bom,utf-8,big5,latin1
set fileencoding=utf-8
set termencoding=utf-8
set number " 行號
set statusline=%<\ %n:%f\ %m%r%y%=%-35.(line:\ %l\ of\ %L,\ col:\ %c%V\ (%P)%)
set ai " 自動縮排
syntax on " 色彩標示
set tabstop=4 " tab使用四個空白取代
set shiftwidth=4 " 縮排空白數,要搭配set cin使用
set cin
set cursorline " 該行的線
set t_Co=256 " 支援 256 色
set textwidth=0
set backspace=2 "按下backspace會後退,道行首後會刪除到前一行
set showmatch "顯示括號配對情況
set nocompatible "用vim的特性去運行,捨棄vi的特性
" Pathogen
call pathogen#infect()
call pathogen#helptags()
filetype plugin indent on
" Nerdtree
autocmd VimEnter * NERDTree
autocmd VimEnter * wincmd p
let NERDTreeShowBookmarks=1
let NERDTreeChDirMode=0
let NERDTreeQuitOnOpen=0
let NERDTreeMouseMode=2
let NERDTreeShowHidden=1
let NERDTreeIgnore=['\.pyc','\~$','\.swo$','\.swp$','\.git','\.hg','\.svn','\.bzr']
let NERDTreeKeepTreeInNewTab=1
let g:nerdtree_tabs_open_on_gui_startup=0
set background=dark "背景顏色
colorscheme wombat
nnoremap <silent> <F5> :NERDTree<CR>
"normal mode的時候+數字 可以切換tab
nnoremap <Esc>1 gt1
nnoremap <Esc>2 gt2
nnoremap <Esc>3 gt3
nnoremap <Esc>4 gt4
nnoremap <Esc>5 gt5
nnoremap <Esc>6 gt6
nnoremap <Esc>7 gt7
nnoremap <Esc>8 gt8
nnoremap <silent> <F5> :NERDTree<CR>
Cscope 是一個用來trace code還滿方便的工具 我通常都用他來trace linuxe kernel code,雖然說有網頁版的reference可以使用,但是用起來不順手,網頁會卡卡的 因此還是習慣使用這種互動式的trace tools
sudo apt-get install cscope
on Ubuntu
portmaster devel/cscope
on FreeBSd
詳細的可以參考man page. 通常我只有使用 -R 來觀看而已
第一次執行的時候,會花比較久的時間去建立一個cscope.out的檔案,會把一些相關資訊放進去
下次執行的時候就會利用該out檔案來作查詢。
預設的情況下,cscope只能讀取
想要讓他讀取java或是cpp的專案,就必須要先自己建置該資料庫
前面兩行會把所有的檔案路徑都寫入倒cscope.files裡面
接下來只要使用cscope就可以了
]]>直接透過atp-get 安裝即可
sudo apt-get install sphinx
安裝完畢後,執行
sphinx-quickstart
就可以基本設定了
每個選項都有說明,基本上都採用預設值即可
設定檔: conf.py
主要的檔案: index.rst -. 檔案的結構 -. toctree
Lab Meetgins
=============
.. toctree::
:maxdepth: 4
:titlesonly:
20130924.rst
20131001.rst
國科會 meetings
===============
.. toctree::
:maxdepth: 4
:titlesonly:
20130925.rst
這邊我定義兩個toctree,每個toctree底下又會有其他的rst,結構大概是這樣
總共兩個分類,每個分類底下的文章都是一個額外的rst檔案
在toctree底下的都是一些設定參數
Sphinx採用的reStructuredText
格式跟markdown很類似,但是複雜了一些
官方網站有滿詳細的介紹,有需要時再去參考即可
如果想要轉成html網頁,有兩種方法可以執行
sphinx-build -b html . NSLMeeting 意思是建置html的網頁, 然後以當前目錄為source 來源,然後把檔案build到NSLMetting去。
make html
在Makefile中定義了相關得動作,當執行make html
的時候,其實就是執行
sphinx-build -b html . _build/html
這邊因為我想要直接弄到別的資料夾,所以我直接設定aliase去執行方法1
目前對於這套軟體還在學習階段,有任何學習會繼續紀錄。
]]>--configFile ${configuration path}
-Dlogback.configurationFile=${FL_LOGBACK}
java -Dlogback.configurationFile=logback.xml floodlight.jar --configFile floodlightdefault.properties
根據特定的格式來讀取或封裝資料。
格式部份分成兩個部份
byte-order: 這邊可以決定採用big-endian 或是 little endian, 如果沒有給的話,預設是採用系統的方式去做,那這邊比較要注意到的是 以前再寫C語言的時候,都會有所謂的htons...類的轉換,在這邊可以使用'!' 這個符號來解決這個問題,他會自己使用network的Byte order rule去解讀資料,所以有在用網路連線傳資料的話,一定要用! 避免資料解讀錯誤的問題。
format-characters:
常用的有
詳細的格式資訊請參考官網 Python struct
這邊來個簡單範例 假設今天我們撰寫屬於自己的網路遊戲 然後我們玩家每次上線時,SERVER都會傳送一份玩家的資料給Client
這份資料包含了
每個資料所需要的型態跟大小如下敘述
Myheader(){
uint8:version
uint8:playerID
uint16:x
uint16:y
uint32:momey
char10:profession
uint8:level
uint32:experience
}
所以傳送資料過來的時候,我們必須要謹慎的按照這個規格去放置我們的資料。
假設
data = pack('2B2HI10sBI',version,playerID,x,y,momey,profession,level,experience)
//'\x018{\x00%\t\x00\x00\t\xefT\x00warrior\x00\x00\x00\x80\x00\xdb\xff\xff\x7f'
unpack('2B2HI10sBI',data)
(1, 56, 123, 2341, 5566217, 'warrior\x00\x00\x00', 128, 2147483611)
nmap - Network exploration tool and security / port scanner
這邊記錄一下nmap的用法
nmap -sP 140.113.214.79/27
-sP: Ping Scan - go no further than determining if host is online 用ping去掃目標內的所有IP,並顯示有回應的IP,所以若對方是windows7且沒有打開ping的回應,則也會被當作host down
nmap -sL 140.113.214.79/27
-sL: List Scan - simply list targets to scan 只是單純的列出對方的hostname以及IP,不送出任何封包去檢測
nmap -O 140.113.214.94 nmap -A 140.113.214.94
-O: Enable OS detection -A: Enables OS detection and Version detection, Script scanning and Traceroute
掃描對方主機的OS系統
nmap -PS/PA/PU/PY[portlist] 140.113.214.94
-PS/PA/PU/PY[portlist]: TCP SYN/ACK, UDP or SCTP discovery to given ports
用不同的方式去掃描特定的PORT。
nmap -sS/sT/sU 140.113.214.94
採用不同的方式去掃描所有port。
nmap -v 140.113.214.94 顯示出詳細一點的資訊
]]>str.translate(table[, deletechars]);
Parameters
table -- You can use the maketrans() helper function in the string module to create a translation table.
deletechars -- The list of characters to be removed from the source string.
字串中只要有符合deletechars中的字元都會被刪除,然後剩下的字元就會依照table裡面的mapping來做轉換。
這個mapping的就要利用string.maketrans()來幫忙產生囉,
str.maketrans(intab, outtab]);
Parameters
intab -- This is the string having actual characters.
outtab -- This is the string having corresponding mapping character.
intab跟outtab兩者的長度必須要一樣,會把intab中每一個字元與outtab中相同位置的字元做mapping。
舉例來說
intab = "aeiou"
outtab = "12345"
trantab = maketrans(intab, outtab)
就會產生一個mapping,把aeiou分別轉換成12345。
input="abcdefgh"
input = input.translate(trantab)
input就會變成 "1bcd2fgh"
那如果改成
input="abcdefgh"
input = input.translate(trantab,"fgh")
input就會變成 "1bcd2"
再來個簡單範例,希望能夠把所有的小寫轉成大寫,並把非英文字母外的所有字元都給刪除掉。
import string
#取得所有英文大小寫的集合
lower = ''.join(map(chr,range(97,123)))
upper = lower.upper()
#創立一個對照表,可以把所有小寫轉成大寫
ltu = string.maketrans(lower,upper)
#接下來要利用捕集的方式取得非英文字母以外的所有字元,因此就用所有字元-英文字母 #創立一個代表所有字元的字元表
allchars = string.maketrans('','')
#利用translate的方式,取得所有非英文字母的集合
delete = allchars.translate(allchars,lower+upper)
#定義一個對應的function,傳入的字串利用ltu跟delete,就能夠把所有非英文字母都刪除,並且小寫轉大寫了。
def makefilter(input):
print input.translate(ltu,delete)
<Message Date="2012/3/23" Time="下午 11:33:12" DateTime="2012-03-23T15:33:12.790Z" SessionID="1">
<From><User FriendlyName="邱 渣"/></From>
<To><User FriendlyName="XXX"/></To>
<Text>明天會到否</Text>
</Message>
每一則訊息,本身的屬性會包含該訊息的發送時間 ,有兩種格式,後面的790Z就不清楚是什麼意思了,SessionID這個屬性
也不是很清楚,但是這些都不重要
利用Date跟Time就可以取得基本時間了。
接者可以看到底下有三個屬性,代表訊息發送者,訊息接收者,以及發送的訊息為何
如果有啟動顏色跟字型的話,TEXT欄位就會變成下列樣子,會有屬性標示其顏色與字型
<Text Style="font-family:Microsoft JhengHei; color:#000000; "> test </Text>
在C#中,我這次使用XmlElement來做為解析XML的工具,載入檔案後,因為我們只關心訊息的傳送,
所以先利用GetElementsByTagName("message")來取得所有Message有關的nodes
接者針對這個結點內的所有資料去進行資料抓取,我們的目標有 時間、發送者、傳送文字
先將XmlNode轉型為XmlElement的類型,這樣方便處理,然後利用GetAttribute來取得Message的屬性
我們就可以知道每個對話的Date跟Time。接者要存取其child(From,To,Text)這些的值
這邊比較要注意的是這兩種的差別
<From>"邱渣"</From>
<From><User FriendlyName="邱渣"/></From>
以Type1來說,邱渣是From這個結點的值,可以利用childList[0].value 取得發送者的名稱
但是對Type2來說,邱渣是From這個結點底下的一個結點中的屬性,所以就要利用childList[0].FirstChild
的方式來取得<User>
這個結點,再搭配Attributes[0].Value來取得第一個屬性的值,如此才可以取得"邱渣"的值
所以利用childList[2].FirstChild.Attributeds[0].Value就可以取得文字訊息了!
另外,如果要取得文字的顏色跟字型的話,利用
childList[2].GetAttribute("Style")
接者在去自己處理字串來取得字型跟顏色。
範例code如下
xml = new XmlDocument();
xml.Load(filename);
XmlNodeList nodeList = xml.GetElementsByTagName("Message");
foreach (XmlNode parentNode in nodeList)
{
XmlElement element = (XmlElement)parentNode;
string Date = element.GetAttribute("Date");
string Time = element.GetAttribute("Time");
XmlNodeList childList = element.ChildNodes;
data += childList[0].FirstChild.Attributes[0].Value + " 說 (" + Time + ")\r\n";
}
不論是bash,tcsh,又或者是windows的cmd,都有一種叫做PIPE的功能
能夠將兩個獨立的程式給串接起來,把前面程式的輸出當作下一個程式的輸入
擁有這個指令,就能將本來當一功能的程式給組合起來變成複雜的工具了
舉例來說,我想要知道我當前目錄下有多少個檔案
就可以使用ls跟wc兩個指令合作完成,
使用 ls | wc 就會將ls的結果(檔案列表)當作輸入傳給wc這隻程式,然後就可以輕鬆地算出當前目錄的檔案數量
或者是有時候想要搜尋某些特定的字串,都會使用grep這個指令,譬如想要搜尋某個特定使用者正在執行的所有程序
ps auxww | grep username
所以pipe對於系統管理來說,是個非常重要的概念,能夠將每個獨立細小的程式給串接起來完成複雜的工作。
#[程式設計]
在FreeBSD(linux)上,shell能夠辦得到這樣的功能,實際上是利用了kernel中pipe的功能,這邊就已linux kernel 3.5.4為架構。
在程式中,pipe的概念就是一個水管,這個水管有兩個端口,一端負責寫資料到pipe,一端負責將資料從pipe中讀出來,所以我們可以做個簡單的測試。
int main(){
int rand1,rand2;
int fd[2];// declare a two-d array, store file_descriptor of the pipe (two side)
// fd[0] mease read side, fd[1] means write side
pid_t pid;//child process的pid
pipe(fd); //call system call (pipe) to create a pipe
//use fork to create a child process
//child process will wrtie data to pipe, and parent will read data from pipe
//child process
if((pid=fork())==0){
srand(getpid());
close(fd[READ_END]);//child won't use read size, so close it
rand1=rand()%RANGE; //create random number
write(fd[WRITE_END],&rand1,sizeof(rand1)); //write to pipe
close(fd[WRITE_END]);//close write side when write over
printf("%d has been created In Child Process \n",rand1);
exit(1);
}
else if(pid>0){
srand(getpid());
close(fd[WRITE_END]);//parent won't use write size, so close it。
rand2=rand()%RANGE;//create random number
read(fd[READ_END],&rand1,sizeof(rand1));//read the data from pipe
printf("%d has been created In Parent Process \n",rand2);
wait();
printf("Parent Process calulate sum is :%d \n",rand1+rand2);
close(fd[READ_END]);//close read side
exit(1);
}
return 0;
}
執行結果: 8 has been created In Child Process
5 has been created In Parent Process
Parent Process calulate sum is :13
3 has been created In Child Process
3 has been created In Parent Process
Parent Process calulate sum is :6
實際上,如果想要對同個端口去進行寫跟讀的動作,是行不通的,乍看之下會覺得PIPE只是一個
buffer,放置資料而已,實際上在kernel中,pipe被視為是一個file,當我們呼叫pipe時,真正最後會
呼叫到do_pipe這個function,在這個function中,會針對pipe的兩個端口分別去設定
O_RDONLY;O_WRONLY的標籤,這樣的設定使得pipe的端口就真的一邊只能讀,一邊只能寫。
有空在來講述一下file_descriptor file file_operation三者的關係,以及到底 file,socket,pipe...等這些device到底在kernel中如何運作。
]]>需要能夠ˋ彈出對話框選擇一個資料夾,讀取資料夾底下的影像檔,然後與某個特定的影像檔做相減,並命名輸出
這部分用到了一些指令,在這邊紀錄下來
%choose directory
target_path = uigetdir();
file_path = [target_path '/C00*.tif'];
background = [target_path '/REF.tif'];
file_struct = dir(file_path);
back_struct = dir(background);
%load background image
back = imread([target_path '/' back_struct.name]);
for i=1:length(file_struct)
temp_image = imread([target_path '/' file_struct(i).name]);
result_image = imsubtract(temp_image,back);
imwrite(result_image,[target_path '/new' file_struct(i).name]);
end
====END=======
首先使用到了uigetdir,與其類似的還有uigetfile
呼叫此函數後,會彈出directoryOpenDialog的介面,選擇完畢後,會把選擇的路徑回傳
接下來我想要移動到該路徑,於是希望透過 cd 這個指令,無奈 cd這個指令沒有辦法吃參數,只能吃完整路徑,所以就必須要改換成其他的方法
由於我已經知道圖檔的命名規則,於是先用 [] 的方式,把字串給連接起來,這邊使用regular的方式,之後再搜尋檔案的時候會更方便
接者使用dir這個指令,就可以得到我想要的所有檔案,dir回傳的是一個struct,內容包含了檔案的
name -- 檔案名稱 date -- 修改日期 bytes -- 檔案大小 isdir -- 是否為資料夾 datenum -- Matlab特定的修改日期
這邊我只需要它的名稱,於是透過一個迴圈,把所有的路徑檔案都以圖片的方式(imread)給讀取近來 在與事先讀取好的背景圖片(back)使用imsubtract做相減,得到新的圖片,再透過imwrite給寫出檔案
]]>這個概念其實不難,寫一個可以連線的batch file,每次開機的時候,自動去執行該batch file,就可以達到連線的功能了。
在網際網路那邊手動增加一個VPN連線,假設該VPN連線名稱為 vpn_connection。
寫一個batch file,內容增加一行
rasdial "my_vpn_connection" "myname" "mypasswd"
這時候可以手動執行看看,看會不會連線成功,如果連線不會成功,就根據錯誤代碼去解決。
重開機測試!
]]>在宣告時,加入explicit 這個關鍵字,可以禁止使用顯性轉換,以下為例
using namespace std;
class Stack {
public:
Stack(int a){};
};
void Test(Stack b){
}
int main()
{
Stack s1 = 1; //ok
Stack s2 = Stack(12); //ok
Stack s3(s1); //ok
Test(123); //ok
return 0;
}
using namespace std;
class Stack {
public:
explicit Stack(int){};
};
void Test(Stack b){
}
int main()
{
Stack s1 = 123; //error
Stack s2 = Stack(123); //ok
Stack s3(s1); //ok
Test(123); // error
return 0;
}
#[FreeBSD]
可以使用 sockstat 這個command 來檢查系統上port的使用。
USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS
root cron 93468 4 udp4 :638 :*
在預設的情況下,會輸出
使用者名稱,執行的程序,該程序的pid,在該程序中使用該port的file descriptor是多少 使用何種協定,以及address
如果使用 sockstat -4lP tcp 就可以找出 使用tcp & ipv4 ,並且正在listen的port
這對於要尋找是否有人在寫Socket programming來說是很方便的。
詳細的可以man sockstat
#[Linux] 可以使用 netstat 這個工具來檢視,搭配一些參數還可以看到該 port 被那些 process 使用
netstat -anptn
tcp 1 0 127.0.0.1:40147 127.0.0.1:36524 CLOSE_WAIT 7147/vim
tcp 1 0 127.0.0.1:58289 127.0.0.1:52849 CLOSE_WAIT 19421/vi
...
#[Windows]
可以使用netstat來檢視,netstat能夠顯示的資訊非常的多,為了精簡我們的需求,必須去過濾這些資訊
在windows上使用find這個指令,類似於UNIX中grep的功能
舉例來說,netstat -an |find /i “listening" 這個指令
netstat -an 會顯示所有連線以及正在監聽的port,並且以數字的形式來顯示IP以及PORT
find /i “listening" 則會以不區分的方式去搜尋每一行,若包含listening則將該行印出
EX:
TCP 192.168.1.116:139 0.0.0.0:0 LISTENING
]]>TCP 192.168.1.116:49156 216.52.233.65:12975 ESTABLISHED