2020年2月

用 KRAWL 脚本来识别 Kubernetes Pod 和容器中的错误。

 title=

当你使用 Kubernetes 运行容器时,你通常会发现它们堆积在一起。这是设计使然。它是容器的优点之一:每当需要新的容器时,它们启动成本都很低。你可以使用前端工具(如 OpenShift 或 OKD)来管理 Pod 和容器。这些工具使可视化设置变得容易,并且它具有一组丰富的用于快速交互的命令。

如果管理容器的平台不符合你的要求,你也可以仅使用 Kubernetes 工具链获取这些信息,但这需要大量命令才能全面了解复杂环境。出于这个原因,我编写了 KRAWL,这是一个简单的脚本,可用于扫描 Kubernetes 集群命名空间下的 Pod 和容器,并在发现任何事件时,显示事件的输出。它也可用作为 Kubernetes 插件使用。这是获取大量有用信息的快速简便方法。

先决条件

  • 必须安装 kubectl
  • 集群的 kubeconfig 配置必须在它的默认位置($HOME/.kube/config)或已被导出到环境变量(KUBECONFIG=/path/to/kubeconfig)。

使用

$ ./krawl

 title=

脚本

#!/bin/bash
# AUTHOR: Abhishek Tamrakar
# EMAIL: [email protected]
# LICENSE: Copyright (C) 2018 Abhishek Tamrakar
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
##
#define the variables
KUBE_LOC=~/.kube/config
#define variables
KUBECTL=$(which kubectl)
GET=$(which egrep)
AWK=$(which awk)
red=$(tput setaf 1)
normal=$(tput sgr0)
# define functions

# wrapper for printing info messages
info()
{
  printf '\n\e[34m%s\e[m: %s\n' "INFO" "$@"
}

# cleanup when all done
cleanup()
{
  rm -f results.csv
}

# just check if the command we are about to call is available
checkcmd()
{
  #check if command exists
  local cmd=$1
  if [ -z "${!cmd}" ]
  then
    printf '\n\e[31m%s\e[m: %s\n' "ERROR"  "check if $1 is installed !!!"
    exit 1
  fi
}

get_namespaces()
{
  #get namespaces
  namespaces=( \
          $($KUBECTL get namespaces --ignore-not-found=true | \
          $AWK '/Active/ {print $1}' \
          ORS=" ") \
          )
#exit if namespaces are not found
if [ ${#namespaces[@]} -eq 0 ]
then
  printf '\n\e[31m%s\e[m: %s\n' "ERROR"  "No namespaces found!!"
  exit 1
fi
}

#get events for pods in errored state
get_pod_events()
{
  printf '\n'
  if [ ${#ERRORED[@]} -ne 0 ]
  then
      info "${#ERRORED[@]} errored pods found."
      for CULPRIT in ${ERRORED[@]}
      do
        info "POD: $CULPRIT"
        info
        $KUBECTL get events \
        --field-selector=involvedObject.name=$CULPRIT \
        -ocustom-columns=LASTSEEN:.lastTimestamp,REASON:.reason,MESSAGE:.message \
        --all-namespaces \
        --ignore-not-found=true
      done
  else
      info "0 pods with errored events found."
  fi
}

#define the logic
get_pod_errors()
{
  printf "%s %s %s\n" "NAMESPACE,POD_NAME,CONTAINER_NAME,ERRORS" > results.csv
  printf "%s %s %s\n" "---------,--------,--------------,------" >> results.csv
  for NAMESPACE in ${namespaces[@]}
  do
    while IFS=' ' read -r POD CONTAINERS
    do
      for CONTAINER in ${CONTAINERS//,/ }
      do
        COUNT=$($KUBECTL logs --since=1h --tail=20 $POD -c $CONTAINER -n $NAMESPACE 2>/dev/null| \
        $GET -c '^error|Error|ERROR|Warn|WARN')
        if [ $COUNT -gt 0 ]
        then
            STATE=("${STATE[@]}" "$NAMESPACE,$POD,$CONTAINER,$COUNT")
        else
        #catch pods in errored state
            ERRORED=($($KUBECTL get pods -n $NAMESPACE --no-headers=true | \
                awk '!/Running/ {print $1}' ORS=" ") \
                )
        fi
      done
    done< <($KUBECTL get pods -n $NAMESPACE --ignore-not-found=true -o=custom-columns=NAME:.metadata.name,CONTAINERS:.spec.containers[*].name --no-headers=true)
  done
  printf "%s\n" ${STATE[@]:-None} >> results.csv
  STATE=()
}
#define usage for seprate run
usage()
{
cat << EOF

  USAGE: "${0##*/} </path/to/kube-config>(optional)"

  This program is a free software under the terms of Apache 2.0 License.
  COPYRIGHT (C) 2018 Abhishek Tamrakar

EOF
exit 0
}

#check if basic commands are found
trap cleanup EXIT
checkcmd KUBECTL
#
#set the ground
if [ $# -lt 1 ]; then
  if [ ! -e ${KUBE_LOC} -a ! -s ${KUBE_LOC} ]
  then
    info "A readable kube config location is required!!"
    usage
  fi
elif [ $# -eq 1 ]
then
  export KUBECONFIG=$1
elif [ $# -gt 1 ]
then
  usage
fi
#play
get_namespaces
get_pod_errors

printf '\n%40s\n' 'KRAWL'
printf '%s\n' '---------------------------------------------------------------------------------'
printf '%s\n' '  Krawl is a command line utility to scan pods and prints name of errored pods   '
printf '%s\n\n' ' +and containers within. To use it as kubernetes plugin, please check their page '
printf '%s\n' '================================================================================='

cat results.csv | sed 's/,/,|/g'| column -s ',' -t
get_pod_events

此文最初发布在 KRAWL 的 GitHub 仓库下的 README 中,并被或许重用。


via: https://opensource.com/article/20/2/kubernetes-scanner

作者:Abhishek Tamrakar 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

准备好你喜欢的饮料、编辑器和编译器,放一些音乐,然后开始构建一个由多个文件组成的 C 语言程序。

大家常说计算机编程的艺术部分是处理复杂性,部分是命名某些事物。此外,我认为“有时需要添加绘图”是在很大程度上是正确的。

在这篇文章里,我会编写一个小型 C 程序,命名一些东西,同时处理一些复杂性。该程序的结构大致基于我在 《如何写一个好的 C 语言 main 函数》 文中讨论的。但是,这次做一些不同的事。准备好你喜欢的饮料、编辑器和编译器,放一些音乐,让我们一起编写一个有趣的 C 语言程序。

优秀 Unix 程序哲学

首先,你要知道这个 C 程序是一个 Unix 命令行工具。这意味着它运行在(或者可被移植到)那些提供 Unix C 运行环境的操作系统中。当贝尔实验室发明 Unix 后,它从一开始便充满了设计哲学。用我自己的话来说就是:程序只做一件事,并做好它,并且对文件进行一些操作。虽然“只做一件事,并做好它”是有意义的,但是“对文件进行一些操作”的部分似乎有点儿不合适。

事实证明,Unix 中抽象的 “文件” 非常强大。一个 Unix 文件是以文件结束符(EOF)标志为结尾的字节流。仅此而已。文件中任何其它结构均由应用程序所施加而非操作系统。操作系统提供了系统调用,使得程序能够对文件执行一套标准的操作:打开、读取、写入、寻址和关闭(还有其他,但说起来那就复杂了)。对于文件的标准化访问使得不同的程序共用相同的抽象,而且可以一同工作,即使它们是不同的人用不同语言编写的程序。

具有共享的文件接口使得构建可组合的的程序成为可能。一个程序的输出可以作为另一个程序的输入。Unix 家族的操作系统默认在执行程序时提供了三个文件:标准输入(stdin)、标准输出(stdout)和标准错误(stderr)。其中两个文件是只写的:stdoutstderr。而 stdin 是只读的。当我们在常见的 Shell 比如 Bash 中使用文件重定向时,可以看到其效果。

$ ls | grep foo | sed -e 's/bar/baz/g' > ack

这条指令可以被简要地描述为:ls 的结果被写入标准输出,它重定向到 grep 的标准输入,grep 的标准输出重定向到 sed 的标准输入,sed 的标准输出重定向到当前目录下文件名为 ack 的文件中。

我们希望我们的程序在这个灵活又出色的生态系统中运作良好,因此让我们编写一个可以读写文件的程序。

喵呜喵呜:流编码器/解码器概念

当我还是一个露着豁牙的孩子懵懵懂懂地学习计算机科学时,学过很多编码方案。它们中的有些用于压缩文件,有些用于打包文件,另一些毫无用处因此显得十分愚蠢。列举最后这种情况的一个例子:哞哞编码方案

为了让我们的程序有个用途,我为它更新了一个 21 世纪 的概念,并且实现了一个名为“喵呜喵呜” 的编码方案的概念(毕竟网上大家都喜欢猫)。这里的基本的思路是获取文件并且使用文本 “meow” 对每个半字节(半个字节)进行编码。小写字母代表 0,大写字母代表 1。因为它会将 4 个比特替换为 32 个比特,因此会扩大文件的大小。没错,这毫无意义。但是想象一下人们看到经过这样编码后的惊讶表情。

$ cat /home/your_sibling/.super_secret_journal_of_my_innermost_thoughts
MeOWmeOWmeowMEoW...

这非常棒。

最终的实现

完整的源代码可以在 GitHub 上面找到,但是我会写下我在编写程序时的思考。目的是说明如何组织构建多文件 C 语言程序。

既然已经确定了要编写一个编码和解码“喵呜喵呜”格式的文件的程序时,我在 Shell 中执行了以下的命令 :

$ mkdir meowmeow
$ cd meowmeow
$ git init
$ touch Makefile     # 编译程序的方法
$ touch main.c       # 处理命令行选项
$ touch main.h       # “全局”常量和定义
$ touch mmencode.c   # 实现对喵呜喵呜文件的编码
$ touch mmencode.h   # 描述编码 API
$ touch mmdecode.c   # 实现对喵呜喵呜文件的解码
$ touch mmdecode.h   # 描述解码 API
$ touch table.h      # 定义编码查找表
$ touch .gitignore   # 这个文件中的文件名会被 git 忽略
$ git add .
$ git commit -m "initial commit of empty files"

简单的说,我创建了一个目录,里面全是空文件,并且提交到 git。

即使这些文件中没有内容,你依旧可以从它的文件名推断每个文件的用途。为了避免万一你无法理解,我在每条 touch 命令后面进行了简单描述。

通常,程序从一个简单 main.c 文件开始,只有两三个解决问题的函数。然后程序员轻率地向自己的朋友或者老板展示了该程序,然后为了支持所有新的“功能”和“需求”,文件中的函数数量就迅速爆开了。“程序俱乐部”的第一条规则便是不要谈论“程序俱乐部”,第二条规则是尽量减少单个文件中的函数。

老实说,C 编译器并不关心程序中的所有函数是否都在一个文件中。但是我们并不是为计算机或编译器写程序,我们是为其他人(有时也包括我们)去写程序的。我知道这可能有些奇怪,但这就是事实。程序体现了计算机解决问题所采用的一组算法,当问题的参数发生了意料之外的变化时,保证人们可以理解它们是非常重要的。当在人们修改程序时,发现一个文件中有 2049 函数时他们会诅咒你的。

因此,优秀的程序员会将函数分隔开,将相似的函数分组到不同的文件中。这里我用了三个文件 main.cmmencode.cmmdecode.c。对于这样小的程序,也许看起来有些过头了。但是小的程序很难保证一直小下去,因此哥忒拓展做好计划是一个“好主意”。

但是那些 .h 文件呢?我会在后面解释一般的术语,简单地说,它们被称为头文件,同时它们可以包含 C 语言类型定义和 C 预处理指令。头文件中不应该包含任何函数。你可以认为头文件是提供了应用程序接口(API)的定义的一种 .c 文件,可以供其它 .c 文件使用。

但是 Makefile 是什么呢?

我知道下一个轰动一时的应用都是你们这些好孩子们用 “终极代码粉碎者 3000” 集成开发环境来编写的,而构建项目是用 Ctrl-Meta-Shift-Alt-Super-B 等一系列复杂的按键混搭出来的。但是如今(也就是今天),使用 Makefile 文件可以在构建 C 程序时帮助做很多有用的工作。Makefile 是一个包含如何处理文件的方式的文本文件,程序员可以使用其自动地从源代码构建二进制程序(以及其它东西!)

以下面这个小东西为例:

00 # Makefile
01 TARGET= my_sweet_program
02 $(TARGET): main.c
03    cc -o my_sweet_program main.c

# 符号后面的文本是注释,例如 00 行。

01 行是一个变量赋值,将 TARGET 变量赋值为字符串 my_sweet_program。按照惯例,也是我的习惯,所有 Makefile 变量均使用大写字母并用下划线分隔单词。

02 行包含该 步骤 recipe 要创建的文件名和其依赖的文件。在本例中,构建 目标 target my_sweet_program,其依赖是 main.c

最后的 03 行使用了一个制表符号(tab)而不是四个空格。这是将要执行创建目标的命令。在本例中,我们使用 C 编译器 C compiler 前端 cc 以编译链接为 my_sweet_program

使用 Makefile 是非常简单的。

$ make
cc -o my_sweet_program main.c
$ ls
Makefile  main.c  my_sweet_program

构建我们喵呜喵呜编码器/解码器的 Makefile 比上面的例子要复杂,但其基本结构是相同的。我将在另一篇文章中将其分解为 Barney 风格。

形式伴随着功能

我的想法是程序从一个文件中读取、转换它,并将转换后的结果存储到另一个文件中。以下是我想象使用程序命令行交互时的情况:

$ meow < clear.txt > clear.meow
$ unmeow < clear.meow > meow.tx
$ diff clear.txt meow.tx
$

我们需要编写代码以进行命令行解析和处理输入/输出流。我们需要一个函数对流进行编码并将结果写到另一个流中。最后,我们需要一个函数对流进行解码并将结果写到另一个流中。等一下,我们在讨论如何写一个程序,但是在上面的例子中,我调用了两个指令:meowunmeow?我知道你可能会认为这会导致越变越复杂。

次要内容:argv[0] 和 ln 指令

回想一下,C 语言 main 函数的结构如下:

int main(int argc, char *argv[])

其中 argc 是命令行参数的数量,argv 是字符指针(字符串)的列表。argv[0] 是包含正在执行的程序的文件路径。在 Unix 系统中许多互补功能的程序(比如:压缩和解压缩)看起来像两个命令,但事实上,它们是在文件系统中拥有两个名称的一个程序。这个技巧是通过使用 ln 命令创建文件系统链接来实现两个名称的。

在我笔记本电脑中 /usr/bin 的一个例子如下:

$ ls -li /usr/bin/git*
3376 -rwxr-xr-x. 113 root root     1.5M Aug 30  2018 /usr/bin/git
3376 -rwxr-xr-x. 113 root root     1.5M Aug 30  2018 /usr/bin/git-receive-pack
...

这里 gitgit-receive-pack 是同一个文件但是拥有不同的名字。我们说它们是相同的文件因为它们具有相同的 inode 值(第一列)。inode 是 Unix 文件系统的一个特点,对它的介绍超越了本文的内容范畴。

优秀或懒惰的程序可以通过 Unix 文件系统的这个特点达到写更少的代码但是交付双倍的程序。首先,我们编写一个基于其 argv[0] 的值而作出相应改变的程序,然后我们确保为导致该行为的名称创建链接。

在我们的 Makefile 中,unmeow 链接通过以下的方式来创建:

# Makefile
...
$(DECODER): $(ENCODER)
        $(LN) -f $< $@
       ...

我倾向于在 Makefile 中将所有内容参数化,很少使用 “裸” 字符串。我将所有的定义都放置在 Makefile 文件顶部,以便可以简单地找到并改变它们。当你尝试将程序移植到新的平台上时,需要将 cc 改变为某个 cc 时,这会很方便。

除了两个内置变量 $@$< 之外,该 步骤 recipe 看起来相对简单。第一个便是该步骤的目标的快捷方式,在本例中是 $(DECODER)(我能记得这个是因为 @ 符号看起来像是一个目标)。第二个,$< 是规则依赖项,在本例中,它解析为 $(ENCODER)

事情肯定会变得复杂,但它还在管理之中。


via: https://opensource.com/article/19/7/structure-multi-file-c-part-1

作者:Erik O'Shaughnessy 选题:lujun9972 译者:萌新阿岩 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

一个简单又原始的脚本来用 Vim 打开你选择的文件。

像大多数开发者一样,我整天都在搜索和阅读源码。就我个人而言,我从来没有习惯过集成开发环境 (IDE),多年来,我主要使用 grep (找到文件),并复制/粘贴文件名来打开 Vi(m)。

最终,我写了这个脚本,并根据需要缓慢地对其进行了完善。

它依赖 Vimrlwrap,并使用 Apache 2.0 许可证开源。要使用该脚本,请将它放到 PATH 中,然后在文本目录下运行:

grepgitvi <grep options> <grep/vim search pattern>

它将返回搜索结果的编号列表,并提示你输入结果编号并打开 Vim。退出 Vim 后,它将再次显示列表,直到你输入除结果编号以外的任何内容。你也可以使用向上和向下箭头键选择一个文件。(这对我来说)更容易找到我已经看过的结果。

与现代 IDE 甚至与 Vim 的更复杂的用法相比,它简单而原始,但它对我有用。

脚本

#!/bin/bash

# grepgitvi - grep source files, interactively open vim on results
# Doesnt really have to do much with git, other than ignoring .git
#
# Copyright Yedidyah Bar David 2019
#
# SPDX-License-Identifier: Apache-2.0
#
# Requires vim and rlwrap
#
# Usage: grepgitvi <grep options> <grep/vim pattern>
#

TMPD=$(mktemp -d /tmp/grepgitvi.XXXXXX)
UNCOLORED=${TMPD}/uncolored
COLORED=${TMPD}/colored

RLHIST=${TMPD}/readline-history

[ -z "${DIRS}" ] && DIRS=.

cleanup() {
        rm -rf "${TMPD}"
}

trap cleanup 0

find ${DIRS} -iname .git -prune -o \! -iname "*.min.css*" -type f -print0 > ${TMPD}/allfiles

cat ${TMPD}/allfiles | xargs -0 grep --color=always -n -H "$@" > $COLORED
cat ${TMPD}/allfiles | xargs -0 grep -n -H "$@" > $UNCOLORED

max=`cat $UNCOLORED | wc -l`
pat="${@: -1}"

inp=''
while true; do
        echo "============================ grep results ==============================="
        cat $COLORED | nl
        echo "============================ grep results ==============================="
        prompt="Enter a number between 1 and $max or anything else to quit: "
        inp=$(rlwrap -H $RLHIST bash -c "read -p \"$prompt\" inp; echo \$inp")
        if ! echo "$inp" | grep -q '^[0-9][0-9]*$' || [ "$inp" -gt "$max" ]; then
                break
        fi

        filename=$(cat $UNCOLORED | awk -F: "NR==$inp"' {print $1}')
        linenum=$(cat $UNCOLORED | awk -F: "NR==$inp"' {print $2-1}')
        vim +:"$linenum" +"norm zz" +/"${pat}" "$filename"
done

via: https://opensource.com/article/20/2/no-ide-script

作者:Yedidyah Bar David 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

在 2020 年用开源实现更高生产力的二十种方式的第十八篇文章中,使用 Emacs 文本编辑器管理电子邮件和查看日程安排。

去年,我给你们带来了 2019 年的 19 天新生产力工具系列。今年,我将采取一种不同的方式:建立一个新的环境,让你使用已用或未用的工具来在新的一年里变得更有效率。

使用 Emacs 做(几乎)所有的事情,第 1 部分

两天前,我曾经说过我经常使用 VimEmacs,在本系列的 1617 天,我讲解了如何在 Vim 中做几乎所有的事情。现在,Emacs 的时间到了!

 title=

在深入之前,我需要说明两件事。首先,我这里使用默认的 Emacs 配置,而不是我之前写过Spacemacs。为什么呢?因为这样一来我使用的就是默认快捷键,从而使你可以参考文档,而不必将“原生的 Emacs” 转换为 Spacemacs。第二,在本系列文章中我没有对 Org 模式进行任何设置。Org 模式本身几乎可以自成一个完整的系列,它非常强大,但是设置可能非常复杂。

配置 Emacs

配置 Emacs 比配置 Vim 稍微复杂一些,但以我之见,从长远来看,这样做是值得的。首先我们创建一个配置文件,并在 Emacs 中打开它:

mkdir ~/.emacs.d
emacs ~/.emacs.d/init.el

接下来,向内置的包管理器添加一些额外的包源。在 init.el 中添加以下内容:

(package-initialize)
(add-to-list 'package-archives '("melpa" . "<http://melpa.org/packages/>"))
(add-to-list 'package-archives '("org" . "<http://orgmode.org/elpa/>") t)
(add-to-list 'package-archives '("gnu" . "<https://elpa.gnu.org/packages/>"))
(package-refresh-contents)

使用 Ctrl+x Ctrl+s 保存文件,然后按下 Ctrl+x Ctrl+c 退出,再重启 Emacs。Emacs 会在启动时下载所有的插件包列表,之后你就可以使用内置的包管理器安装插件了。输入 Meta+x 会弹出命令提示符(大多数键盘上 Meta 键就是的 Alt 键,而在 MacOS 上则是 Option)。在命令提示符下输入 package-list-packages 就会显示可以安装的包列表。遍历该列表并使用 i 键选择以下包:

bbdb
bbdb-vcard
calfw
calfw-ical
notmuch

选好软件包后按 x 安装它们。根据你的网络连接情况,这可能需要一段时间。你也许会看到一些编译错误,但是可以忽略它们。安装完成后,使用组合键 Ctrl+x Ctrl+f 打开 ~/.emacs.d/init.el,并在 (package-refresh-packages) 之后、 (custom-set-variables 之前添加以下行到文件中。 (custom-set-variables 行由 Emacs 内部维护,你永远不应该修改它之后的任何内容。以 ;; 开头的行则是注释。

;; Set up bbdb
(require 'bbdb)
(bbdb-initialize 'message)
(bbdb-insinuate-message)
(add-hook 'message-setup-hook 'bbdb-insinuate-mail)
;; set up calendar
(require 'calfw)
(require 'calfw-ical)
;; Set this to the URL of your calendar. Google users will use
;; the Secret Address in iCalendar Format from the calendar settings
(cfw:open-ical-calendar "<https://path/to/my/ics/file.ics>")
;; Set up notmuch
(require 'notmuch)
;; set up mail sending using sendmail
(setq send-mail-function (quote sendmail-send-it))
(setq user-mail-address "[[email protected]][9]"
      user-full-name "My Name")

现在,你已经准备好使用自己的配置启动 Emacs 了!保存 init.el 文件(Ctrl+x Ctrl+s),退出 Emacs(Ctrl+x Ctrl+c),然后重启之。这次重启要多花些时间。

使用 Notmuch 在 Emacs 中读写电子邮件

一旦你看到了 Emacs 启动屏幕,你就可以使用 Notmuch 来阅读电子邮件了。键入 Meta+x notmuch,你将看到 notmuch 的 Emacs 界面。

 title=

所有加粗的项目都是指向电子邮件视图的链接。你可以通过点击鼠标或者使用 tab 键在它们之间跳转并按回车来访问它们。你可以使用搜索栏来搜索 Notmuch 的数据库,语法与 Notmuch 命令行上的语法 相同。如果你愿意,还可以使用 [save] 按钮保存搜索以便未来使用,这些搜索会被添加到屏幕顶部的列表中。如果你进入一个链接就会看到一个相关电子邮件的列表。你可以使用箭头键在列表中导航,并在要读取的消息上按回车。按 r 可以回复一条消息,f 转发该消息,q 退出当前屏幕。

你可以通过键入 Meta+x compose-mail 来编写新消息。撰写、回复和转发都将打开编写邮件的界面。写完邮件后,按 Ctrl+c Ctrl+c 发送。如果你决定不发送它,按 Ctrl+c Ctrl+k 关闭消息撰写缓冲区(窗口)。

使用 BBDB 在 Emacs 中自动补完电子邮件地址

 title=

那么通讯录怎么办?这就是 BBDB 发挥作用的地方。但首先我们需要从 abook 导入所有地址,方法是打开命令行并运行以下导出命令:

abook --convert --outformat vcard --outfile ~/all-my-addresses.vcf --infile ~/.abook/addresses

Emacs 启动后,运行 Meta+x bbdb-vcard-import-file。它将提示你输入要导入的文件名,即 ~/all-my-address.vcf。导入完成后,在编写消息时,可以开始输入名称并使用 Tab 搜索和自动完成 “to” 字段的内容。BBDB 还会打开一个联系人缓冲区,以便你确保它是正确的。

既然在 vdirsyncer 中已经为每个地址都生成了对应的 .vcf 文件了,为什么我们还要这样做呢?如果你像我一样,有许多地址,一次处理一个地址是很麻烦的。这样做,你就可以把所有的东西都放在 abook 里,做成一个大文件。

使用 calfw 在 Emacs 中浏览日历

 title=

最后,你可以使用 Emacs 查看日历。在上面的配置中,你安装了 calfw 包,并添加了一些行来告诉它在哪里可以找到要加载的日历。Calfw 是 “ Emacs 日历框架 Calendar Framework for Emacs ”的简称,它支持多种日历格式。我使用的是谷歌日历,这也是我放在配置中的链接。日历将在启动时自动加载,你可以通过 Ctrl+x+b 命令切换到 cfw-calendar 缓冲区来查看日历。

Calfw 提供日、周、双周和月视图。你可以在日历顶部选择视图,并使用箭头键导航日历。不幸的是,calfw 只能查看日历,所以你仍然需要使用 khal 之类的工具或通过 web 界面来添加、删除和修改事件。

这就是 Emacs 中的邮件、日历和邮件地址。明天我会展示更多。


via: https://opensource.com/article/20/1/emacs-mail-calendar

作者:Kevin Sonney 选题:lujun9972 译者:lujun9972 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

命令行工具 dig 是用于解析域名和故障排查的一个利器。

从主要功能上来说,dignslookup 之间差异不大,但 dig 更像一个加强版的 nslookup,可以查询到一些由域名服务器管理的信息,这在排查某些问题的时候非常有用。总的来说,dig 是一个既简单易用又功能强大的命令行工具。(LCTT 译注:dignslookup 行为的主要区别来自于 dig 使用是是操作系统本身的解析库,而 nslookup 使用的是该程序自带的解析库,这有时候会带来一些行为差异。此外,从表现形式上看,dig 返回是结果是以 BIND 配置信息的格式返回的,也带有更多的技术细节。)

dig 最基本的功能就是查询域名信息,因此它的名称实际上是“ 域名信息查询工具 Domain Information Groper ”的缩写。dig 向用户返回的内容可以非常详尽,也可以非常简洁,展现内容的多少完全由用户在查询时使用的选项来决定。

我只需要查询 IP 地址

如果只需要查询某个域名指向的 IP 地址,可以使用 +short 选项:

$ dig facebook.com +short
31.13.66.35

在查询的时候发现有的域名会指向多个 IP 地址?这其实是网站提高其可用性的一种措施。

$ dig networkworld.com +short
151.101.2.165
151.101.66.165
151.101.130.165
151.101.194.165

也正是由于这些网站通过负载均衡实现高可用,在下一次查询的时候,或许会发现这几个 IP 地址的排序有所不同。(LCTT 译注:浏览器等应用默认会使用返回的第一个 IP 地址,因此这样实现了一种简单的负载均衡。)

$ dig networkworld.com +short
151.101.130.165
151.101.194.165
151.101.2.165
151.101.66.165

标准返回

dig 的标准返回内容则包括这个工具本身的一些信息,以及请求域名服务器时返回的响应内容:

$ dig networkworld.com

; <<>> DiG 9.11.5-P4-5.1ubuntu2.1-Ubuntu <<>*gt; networkworld.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39932
;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;networkworld.com.              IN      A

;; ANSWER SECTION:
networkworld.com.       300     IN      A       151.101.194.165
networkworld.com.       300     IN      A       151.101.130.165
networkworld.com.       300     IN      A       151.101.66.165
networkworld.com.       300     IN      A       151.101.2.165

;; Query time: 108 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Thu Feb 13 13:49:53 EST 2020
;; MSG SIZE  rcvd: 109

由于域名服务器有缓存机制,返回的内容可能是之前缓存好的信息。在这种情况下,dig 最后显示的 查询时间 Query time 会是 0 毫秒(0 msec):

;; Query time: 0 msec        <==
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Thu Feb 13 15:30:09 EST 2020
;; MSG SIZE  rcvd: 109

向谁查询?

在默认情况下,dig 会根据 /etc/resolv.conf 这个文件的内容决定向哪个域名服务器获取查询结果。你也可以使用 @ 来指定 dig 请求的域名服务器。

在下面的例子中,就指定了 dig 向 Google 的域名服务器 8.8.8.8 查询域名信息。

$ dig @8.8.8.8 networkworld.com

; <<>> DiG 9.11.5-P4-5.1ubuntu2.1-Ubuntu <<>> @8.8.8.8 networkworld.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 21163
;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;networkworld.com.              IN      A

;; ANSWER SECTION:
networkworld.com.       299     IN      A       151.101.130.165
networkworld.com.       299     IN      A       151.101.66.165
networkworld.com.       299     IN      A       151.101.194.165
networkworld.com.       299     IN      A       151.101.2.165

;; Query time: 48 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Thu Feb 13 14:26:14 EST 2020
;; MSG SIZE  rcvd: 109

想要知道正在使用的 dig 工具的版本,可以使用 -v 选项。你会看到类似这样:

$ dig -v
DiG 9.11.5-P4-5.1ubuntu2.1-Ubuntu

或者这样的返回信息:

$ dig -v
DiG 9.11.4-P2-RedHat-9.11.4-22.P2.el8

如果你觉得 dig 返回的内容过于详细,可以使用 +noall(不显示所有内容)和 +answer(仅显示域名服务器的响应内容)选项,域名服务器的详细信息就会被忽略,只保留域名解析结果。

$ dig networkworld.com +noall +answer

; <<>> DiG 9.11.5-P4-5.1ubuntu2.1-Ubuntu <<>> networkworld.com +noall +answer
;; global options: +cmd
networkworld.com.       300     IN      A       151.101.194.165
networkworld.com.       300     IN      A       151.101.130.165
networkworld.com.       300     IN      A       151.101.66.165
networkworld.com.       300     IN      A       151.101.2.165

批量查询域名

如果你要查询多个域名,可以把这些域名写入到一个文件内(domains),然后使用下面的 dig 命令遍历整个文件并给出所有查询结果。

$ dig +noall +answer -f domains
networkworld.com.       300     IN      A       151.101.66.165
networkworld.com.       300     IN      A       151.101.2.165
networkworld.com.       300     IN      A       151.101.130.165
networkworld.com.       300     IN      A       151.101.194.165
world.std.com.          77972   IN      A       192.74.137.5
uushenandoah.org.       1982    IN      A       162.241.24.209
amazon.com.             18      IN      A       176.32.103.205
amazon.com.             18      IN      A       176.32.98.166
amazon.com.             18      IN      A       205.251.242.103

你也可以在上面的命令中使用 +short 选项,但如果其中有些域名指向多个 IP 地址,就无法看出哪些 IP 地址对应哪个域名了。在这种情况下,更好地做法应该是让 awk 对返回内容进行处理,只留下第一列和最后一列:

$ dig +noall +answer -f domains | awk '{print $1,$NF}'
networkworld.com. 151.101.66.165
networkworld.com. 151.101.130.165
networkworld.com. 151.101.194.165
networkworld.com. 151.101.2.165
world.std.com. 192.74.137.5
amazon.com. 176.32.98.166
amazon.com. 205.251.242.103
amazon.com. 176.32.103.205

via: https://www.networkworld.com/article/3527430/digging-up-ip-addresses-with-the-dig-command.html

作者:Sandra Henry-Stocker 选题:lujun9972 译者:HankChow 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

一个使用 tmux 的 kubectl 插件可以使 Kubernetes 疑难问题变得更简单。

Kubernetes 是一个活跃的开源容器管理平台,它提供了可扩展性、高可用性、健壮性和富有弹性的应用程序管理。它的众多特性之一是支持通过其主要的二进制客户端 kubectl 运行定制脚本或可执行程序,kubectl 很强大的,允许用户在 Kubernetes 集群上用它直接做很多事情。

使用别名进行 Kubernetes 的故障排查

使用 Kubernetes 进行容器编排的人都知道由于设计上原因带来了其功能的复杂性。举例说,迫切需要以更快的速度并且几乎不需要手动干预的方式来简化 Kubernetes 中的故障排除(除过特殊情况)。

在故障排查功能方面,有很多场景需要考虑。在一种场景下,你知道你需要运行什么,但是这个命令的语法(即使作为一个单独的命令运行)过于复杂,或需要一、两次交互才能起作用。

例如,如果你需要经常进入一个系统命名空间中运行的容器,你可能发现自己在重复地键入:

kubectl --namespace=kube-system exec -i -t <your-pod-name>

为了简化故障排查,你可以用这些指令的命令行别名。比如,你可以增加下面命令到你的隐藏配置文件(.bashrc.zshrc):

alias ksysex='kubectl --namespace=kube-system exec -i -t'

这是来自于 Kubernetes 常见别名存储库的一个例子,它展示了一种简化 kubectl 中的功能的方法。像这种场景下的简单情形,使用别名很有用。

切换到 kubectl 插件

更复杂的故障排查场景是需要一个一个的执行很多命令,调查环境,最后得出结论。仅仅用别名方法是不能解决这种情况的;你需要知道你所部署的 Kubernetes 之间逻辑和相关性,你真正需要的是自动化,以在更短的时间内输出你想要的。

考虑到你的集群有 10 ~ 20 或 50 ~ 100 个命名空间来提供不同的微服务。一般在进行故障排查时,什么对你有帮助?

  • 你需要能够快速分辨出抛出错误的是哪个 命名空间的哪个 Pod 的东西。
  • 你需要一些可监视一个命名空间的所有 Pod 日志的东西。
  • 你可能也需要监视特定命名空间的出现错误的某个 Pod 的日志。

涵盖这些要点的解决方案对于定位生产环境的问题有很大的帮助,以及在开发和测试环节中也很有用。

你可以用 kubectl 插件创建比简单的别名更强大的功能。插件类似于其它用任何语言编写的独立脚本,但被设计为可以扩充 Kubernetes 管理员的主要命令。

创建一个插件,你必须用 kubectl-<your-plugin-name> 的正确的语法来拷贝这个脚本到 $PATH 中的导出目录之一,并需要为其赋予可执行权限(chmod +x)。

创建插件之后将其移动到路径中,你可以立即运行它。例如,我的路径下有一个 kubectl-krawlkubectl-kmux

$ kubectl plugin list
The following compatible plugins are available:

/usr/local/bin/kubectl-krawl
/usr/local/bin/kubectl-kmux

$ kubectl kmux

现在让我们见识下带有 tmux 的 Kubernetes 的有多强大。

驾驭强大的 tmux

Tmux 是一个非常强大的工具,许多管理员和运维团队都依赖它来解决与易操作性相关的问题:通过将窗口分成多个窗格以便在多台计算机上运行并行的调试来监视日志。它的主要的优点是可在命令行或自动化脚本中使用。

我创建一个 kubectl 插件,使用 tmux 使故障排查更加简单。我将通过注释来解析插件背后的逻辑(插件的完整代码留待给你实现):

# NAMESPACE 是要监控的名字空间
# POD 是 Pod 名称
# Containers 是容器名称

# 初始化一个计数器 n 以计算循环计数的数量,
# 之后 tmux 使用它来拆分窗格。
n=0;

# 在 Pod 和容器列表上开始循环
while IFS=' ' read -r POD CONTAINERS
do
    # tmux 为每个 Pod 创建一个新窗口
    tmux neww $COMMAND -n $POD 2>/dev/null
    # 对运行中的 Pod 中 的所有容器启动循环
    for CONTAINER in ${CONTAINERS//,/ }
    do
        if [ x$POD = x -o x$CONTAINER = x ]; then
            # 如果任何值为 null,则退出。
                     warn "Looks like there is a problem getting pods data."
            break
        fi
           
        # 设置要执行的命令
        COMMAND=”kubectl logs -f $POD -c $CONTAINER -n $NAMESPACE”
        # 检查 tmux 会话
        if tmux has-session -t <会话名> 2>/dev/null;
        then
            <设置会话退出>
        else
            <创建会话>
        fi
        # 在当前窗口为每个容器切分窗格
        tmux selectp -t $n \; \
        splitw $COMMAND \; \
        select-layout tiled \;
        # 终止容器循环
    done
    
    # 用 Pod 名称重命名窗口以识别
    tmux renamew $POD 2>/dev/null
    
    # 增加计数器
    ((n+=1))

# 终止 Pod 循环
done<<(<从 kubernetes  集群获取 Pod 和容器的列表>)

# 最后选择窗口并附加会话
tmux selectw -t <会话名>:1 \; \
attach-session -t <会话名>\;

运行插件脚本后,将产生类似于下图的输出。每个 Pod 有一个自己的窗口,每个容器(如果有多个)被分割到其窗口中 Pod 窗格中,并在日志到达时输出。Tmux 之美如下可见;通过正确的配置,你甚至会看到哪个窗口正处于激活运行状态(可看到标签是白色的)。

总结

别名是在 Kubernetes 环境下常见的也有用的简易故障排查方法。当环境变得复杂,用高级脚本生成的kubectl 插件是一个更强大的方法。至于用哪个编程语言来编写 kubectl 插件是没有限制。唯一的要求是该名字在路径中是可执行的,并且不能与已知的 kubectl 命令重复。

要阅读完整的插件源码,或试试我创建的插件,请查看我的 kube-plugins-github 存储库。欢迎提交提案和补丁。


via: https://opensource.com/article/20/2/kubernetes-tmux-kubectl

作者:Abhishek Tamrakar 选题:lujun9972 译者:guevaraya 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出