Recently I use Job to run some one time tasks (later switch back to regular pod because we want to control it’s start point), it’s just like other pod controller in K8s.
# check mounted filesystem of directory df -h <directory> # inode usage of directory df -i <directory>
Shows total size of the directory and it’s subdirectories
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# du: estimate file space usage
# 统计当前目录下文件大小,并按照大小排序 # -s: display only a total # -m: --block-size=1M # *: shell globs du -sm * | sort -nr # -BG: blocksize as G du -s -BG * | sort -nr
By default oc/kubectl edit will use vi in Linux if environment variable OC _EDITOR is empty, if you have .vimrc that is not compatible with vi, will get error message, to use vim set export OC_EDITOR=vim or export KUBE_EDITOR=vim.
Other resources
How I booted my Vim
Informative!! I grab lots of configurations from it, go and poke around the author’s vimrc file.
Actually you can learn Vim by running command vimtutor in your terminal, it is a simple and quick tutorial. For a quick review, just see summary after each lesson.
Daily Foundation
掌握好这些操作基本上就满足日常需求了。
Configuration
.vimrc is just like .bashrc. you create a .vimrc (even it’s empty) to tell vim use vim not vi-compatiable mode!
Which mode is currently use should be clear: Normal mode, Insert mode and Visual mode, Command-Line mode, Replace mode, etc. Visual mode lets us select text in the buffer and then operate on the selection.
If you suspect that your customizations are causing interference, here’s a quick test. Try quitting Vim and then launching it with these options:
1
vim -u NONE -N
The -u NONE flag tells Vim not to source your vimrc on startup. That way, your customizations won’t be applied and plugins will be disabled. In older versions of Vim, not loading a vimrc file activates vi compatible mode, which causes many useful features to be disabled. The -N flag prevents this by setting the nocompatible option. Since version 8.0 of Vim, the nocompatible option is set by default, making the -N flag unnecessary.
vim -N file: -N not using vi compatible mode, use newer vim mode. or can configuring in .vimrc file.
:set list, :set nolist, :set list?, :set list&: set, unset, check value, set as default. 以此类推.
1 2
set autochdir "auto set current dir as working dir set wildmode=list:longest "activate TAB auto-complete file path
Reload .vimrc
Reload .vimrc without restarting vim: :source ~/.vimrc. 已经加入vimrc了。
Plugin
Vim 8.0 has its own built-in package manager. For Vim version less than 8.0, I use vim-plug as the plugin manager:
https://github.com/junegunn/vim-plug
You can even specify git address of the plugin, for example:
1
Plug 'https://github.com/tfnico/vim-gradle.git'
If the plugin does not get auto installed, run :PlugInstall, check the plug status by :PlugStatus. 这些在vim-plug README中都有说明。
:bn go to next buffer
:bp go to previous buffer
:bd delete buffer, such as :bd5:bf go to first buffer
:bl go to last buffer
:b3 go to 3rd buffer
gf jump to the file under the cursor, use ctrl+o jump back
可以在vim :e <path> 中直接创建,删除文件或者文件夹,显示long format, sort等
:x the same as :wq, but write only when changes have been made.
:w filename used to save file to filename.
:w !sudo tee % write to sudo with current file name, % represent current file name. (用在保存更改read-only文件的内容)
Quick MovementA append at end of the line, a appends after cursor.
I insert ata head of the line, i insert at cursor.
W jump contiguous words, w jump one word.
o will open a line below cursor, O open above.
3w move 3 words forward, 0 move to the start of the line.
^ go to first non-empty char in line
3fn find 3rd n char at this line. repeat by ;
Screen line movement
screen line指被terminal因宽度限制产生的行,并不是指原始的很长的那一行。
g0, gm, g$: start, middle, end of a line.
gk, gj: move up/down in screan line (can use arrow instead k/j ).
Scrolling
Press G to move you to the bottom of the file.
Type gg to move you to the start of the file.
line number + G or line number + gg will go to the line specified.
To go back to where you came from press Ctrl-o (Keep Ctrl down while pressing the letter o). To go forward Ctrl-iH, M and L move the cursor to top, medium and bottom of current page
zt, zz, zb: move cursor line to top, middle, bottom of screen
" shift with tab set tabstop=2 " Global tab width. set shiftwidth=2 " And again, related. set expandtab " Use spaces instead of tabs " extremely useful to edit yaml file set autoindent " always set autoindenting on set copyindent " copy the previous indentation on autoindentin
:retab: replace all tabs with spaces in current buffer.
>>, << shift current line, 4>>, 4<< block shift with 4 lines together.
In insert mode, can use tab itself to shift (now override by auto-complete in my vimrc file), or using ctrl+t, ctrl+d: shift right and left.
/ search forward, ? search backword. when hit the search, n go forward, N go backword. If the cursor is at search word, use * to search forward, # search back, use g*, g# do partial match search.
/case\c search case-insensitive, /CaSe\C search case-sensitive. \c and \C can be anywhere in pattern.
:s/old/new/g to substitute old by new in a line for all occrurences. you must place the cursor in that line.
To change every occurrence of a character string between two lines,
:#,#s/old/new/g where #,# are the line numbers of the range of lines where the substitution is to be done.
:%s/old/new/g to change every occurrence in the whole file.
:%s/old/new/gc to find every occurrence in the whole file, with a prompt whether to substitute or not, c is confirmation.
For case-sensitive: :%s/old/new/Igc, but actually can be :%s/old\C/new/gc.
Copy and Paste
这里和vim 的 register有关. Default is unnamed register, delete, change, substitute, search and yank all use registers.
y is copy operator, for example, yw copy one word, y$ copy to end of the line and yy used to copy a line, then you can paste through p. you can use v to select and copy. By default it uses unnamed register. Register 0 always stores the yank content, so you can use "0p to paste.
"ayy, "ap use register a to yank and paste. Usually we use a~z and A~Z register, the uppercase register can append text to current content.
使用上不同文件之间copy/paste没啥问题,但如果需要copy到system clipboard,需要设置:
https://stackoverflow.com/questions/3961859/how-to-copy-to-clipboard-in-vim
vim has to be compiled with clipboard support for any of the suggestions mentioned here to work. Mine wasn’t configured that way on Mac OS X by default and I had to rebuild vim. Use this the command to find out whether you have it or not:
1
vim --version | grep 'clipboard'
+clipboard means you’re good and the suggestions here will work for you, while -clipboard means you have to recompile and rebuild vim.
d is the delete operator, use motion to specify the action, for example dw, de, d$. Without d, vim just move the cursor: w, e, $2dw delete 2 words ahead
4dd delete 4 lines in a row
Type p to put previously deleted text after the cursor. for example, you delete a line and replace it in another place
v and then you move the cursor to select the text you want, if you want to delete them next, type d, if you want to save the selected text to another file, press :, then type w filename. V is moved linewise.
Folding
这个非常有用,比如编辑yaml 文件。
Fold by syntax or indent (yaml). :sed foldmethod=indent/syntaxzM, zR: fold and unfold all
zi: toggle fold all
zc, zo: close, open fold block
za: toggle fold block
zk, zj: move fold focus up and down
还可以根据file type去设定, for example:
1 2 3
" Automatic fold settings for specific files. Uncomment to use. " autocmd FileType ruby setlocal foldmethod=syntax " autocmd FileType css setlocal foldmethod=indent shiftwidth=2 tabstop=2
use arrow keys select the lines, it won’t highlight everthing, it’s ok
shift+i
insert the text you want, e.g: #, //, %
press Esc twice
uncomment:
press Esc
hit ctrl+v, enter visual block mode
use arrow keys select text you want to delete
press x to delete the text
Others
Press u to undo the last commands, U to undo all the changes in a line.
crtl+r to undo undo.
Type rx to replace the one character at the cursor with x. R is used to replace more then one chars, type R then get into insert mode.
ce deletes the word and places you in Insert mode, so you can type new word, it will replace old ones. c is change operator, just like d.
比如要更改3个连续的words 在某行中: c3w, then type.
Ctrl+g will show cursor location in the file and the file status, also the file name.
% is used tp match another part of (, [ or {. This is very useful in debugging a program with unmatched parentheses. 也可以用来match其他block语句,比如if-end。
:!<commands> is used to execute external commands, for example :!ls -ltr /tmp. (the same as IPython)
:r filename will retrive the file placed under the cursor. you can also read the output of an external command, :r !ls -ltr.
"""Retrieve and print words from a URL. Usage: python3 words.py <URL> """
import sys from urllib.request import urlopen
deffetch_words(url): """Fetch a list of words from a URL. Args: url: The URL of a UTF-8 text document. Returns: A list of strings containing the words from the document. """ ## can use with-block here story = urlopen(url) story_words = [] for line in story: line_words = line.decode('utf8').split() for word in line_words: story_words.append(word) ## file like object, still need to close story.close() return story_words
defprint_items(items): """Print items one per line. Args: An iterable series of printable items. """
for item in items: print(item)
defmain(url): """Print each word from a text document from at a URL. Args: url: The URL of a UTF-8 text document. """
words = fetch_words(url) print_items(words)
if __name__ == '__main__': ## pass parameter from command line ## argv[0] is the module name main(sys.argv[1])
On Linux, if you add shebang #!/usr/bin/env python3 at the top of the script, then you can run it by:
defconvert(s): try: number = '' for token in s: number += DIGIT_MAP[token] returnint(number) except (KeyError, TypeError) as e: ## !r is a shortcut to call the repr of the value supplied. print(f"Conversion error: {e!r}", file=sys.stderr) ## re-raising exception raise else: print("I will be called only if exception didn't occur!") finally: ## will execute no matter normal or not pass
defsqrt(x): """Compute square roots using the method of Heron of Alexandria. Args: x: The number for which the square root is to be computed. Returns: The square root of x. Raises: ValueError: If x is negative. """ if x < 0: ## upper level can catch this exception raise ValueError( "Cannot compute square root of " f"negative number {x}")
guess = x i = 0 while guess * guess != x and i < 20: guess = (guess + x / guess) / 2.0 i += 1 return guess
Common exception types:
indexError: index out of boundary
keyError: mapping
TypeError: usually avoiding check this, increase function usability.
valueError: int(“hello”)
OSError: os module open file
Modularity
Import module and attribute, 掌握import的语法, module normally is a single python source file, e.g. hello.py. When import, it is represented by module objects.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
## import custom module import hello ## Hello is a class in hello.py from hello import Hello
## system modules import math math.factorial(10)
from math import factorial factorial(10)
from math import factorial as fac fac(10)
# other import forms from math import (factorial, exp) from math import *
In the interactive python console, use help to explore modules:
1 2 3 4 5 6 7 8 9 10 11 12 13
# you can search for modules, keywords, symbols, topics help() >>> math >>> urllib.request
# or check math module by first import it import math help(math) ## request is a nested module after urllib import urllib.request ## or from urllib import request as req help(urllib.request)
You can check the attributes of an object:
1 2 3 4 5 6
## show all methods dir(math) dir(time.ctime) ## show type type(math) type(time.ctime)
Commonly used modules:
requests: simple http library
urllib: urllib is a package that collects several modules for working with URLs
sys: access argv, stdin, stdout, etc.
time: principally for working with unix time stamps
datetime: UTC datetime, Unix timestamp, timedetla
pprint: pretty print
os: interface to system services
itertools: iteration processing
contextlib: use with with statement
typing: type hints, built-in after python 3.5
functools: functools.wraps() 用来 copy original function metadata
最近在做project的时候遇到几个新的modules:
threading: threading operation
subprocess: spawn new processes
time vs datetime modules:
https://stackoverflow.com/questions/7479777/difference-between-python-datetime-vs-time-modules
the time module is principally for working with unix time stamps; expressed as a floating point number taken to be seconds since the unix epoch. the datetime module can support many of the same operations, but provides a more object oriented set of types, and also has some limited support for time zones.
Function
Function name in python uses lowercase and - as delimiter. def keywork bind a function to a name, function in Python is treated as object.
Extended arguments, for example: *args(act as tuple), **kwargs(act as dict). This is called parameters packing, these applies to all types of callables, for example lambda.
The parameter type order must follow: regular positional -> *args -> keyword -> **kwargs, for example:
Correspondingly, we have extended call syntax, unpacking when pass the parameters to function call, * is for tuple or list, ** is for dict.
Unpacking parameters:
1 2 3 4 5 6 7 8 9
deffun(a, b, c, d): print(a, b, c, d)
my_list = [1, 2, 3, 4] # Unpacking list into four arguments # 在变量前加单星号表示将元组(列表、集合)拆分为单个元素 # 双星号同上,区别是目标为字典,字典前加单星号的话可以得到“键” fun(*my_list)
Positional-only arguments:
1 2 3
## no kwarg can be used here deffunction_name(x, /): print(x)
If no return, then will implicitly return None.
1 2 3 4 5
deffunction_name(para1 = "hello", para2 = 34): ## rebind the global variable to local global count ## return None return
Notice that always use immutable value for default value!! Default value的赋值会在最初执行函数的时候运行一次,之后调用不会再重新赋值,看样子是一直存在内存里了。
1 2 3 4 5 6 7
defappend_word(org=[]): org.append("!") return org ## if you call multiple times with default value, the "!" get accumulated print(append_word()) # ["!"] print(append_word()) # ["!", "!"] print(append_word()) # ["!", "!", "!"]
Another example:
1 2 3 4 5 6 7
import time defshow_time(t = time.ctime()): print(t)
## the print will not get updated show_time() show_time()
Function is also an object, can be used as parameters:
from resolver import Resolver res = Resolver() ## just like call function res("www.google.com") ## call second time, the execution time reduces a lot res("www.google.com")
How to know object is callable, use callable() function to test.
Lambda
Create anonymous callable objects, syntax: lambda [args]: expr, the args are separated by commas or empty, the body is a single expression.
1 2 3
## the key is assigned a callable function ## similar to Java sorted(name_list, key=lambda name: name.split()[-1])
Functional-style Tools
map(): maps function to a sequence, lazy implementation, return iterator.
filter(): remove elements from sequence which don’t meet some criteria, lazily.
functools.reduce(): 2-arguments function with a sequence, reduce the sequence to one result.
## f: the target decorated function defescape_unicode(f): ## local function wrap defwrap(*args, **kwargs): ## in below, f is `city_name` res = f(*args, **kwargs) returnascii(res) ## wrap uses a closure to access f after escape_unicode returns return wrap
## original function defcity_name(): return"Tomの"
## the city_name pass to decorator @escape_unicode defcity_name(): return"Tomの"
## now unicode is translated to ascii print(city_name())
You can have multiple decorators, act in order 3->2->1:
defcheck_non_negative(index): ## real decorating part defvalidator(f): defwrap(*args): if args[index] < 0: raise ValueError( 'Argument {} must be non-negative.'.format(index)) return f(*args) return wrap return validator
Python will not perform implicit type conversion, for example "123" + 56 is wrong. Exception is in if and while condition.
Notice that == vs is when compare strings, == compare the value but is compares the identity equality, you can check the unique number by id(). And comparsion by value can be controlled programatically.
The logic operators are and, or and not. 这里注意它们会返回最后eval的值,可以利用这个特点:
1 2 3 4
## 999 "hello"and999 ### "world" 0or'world'
Check if an object is None using is operator.
Function parameters and return are transferred using pass-by-object-reference.
Notice that sequence of the same type also support comparison, just like string comparison, item by item from left to right
## condition if x > 100and x <= 999: pass elif x >= 34or x < -23: pass elifnot x: pass else: pass
## ternary res = "big"if x >= 100else"small"
## loop whileTrue: pass
for item in iterable: pass
break/continue
String
Unicode characters.
Python does not have a character data type, a single character is simply a string with a length of 1. Square brackets can be used to access elements of the string or slice string.
a = 23 b = "apple" print(f"I have {a}{b}s") ## others print("{1} and {0}".format(a, b)) print("%d and %s" % (a, b))
# Example of accessing the attributes of an object import os file = "/etc" info = os.stat(file) print("file {0} uid {1.st_uid}, size {1.st_size}".format(file, info))
# Example of specifying field width and precision # Print table of powers of two and their square roots import math x = 1 for i inrange(10): x = x * 2 y = math.sqrt(x) print("{0:4}{1:10}{2:10.3f}".format(i, x, y)) # print("%4d%10d%10.3f" % (i, x, y))
Bytes
In python3, Strings are represented by sequences of unicodes, but textual data in Linux is a sequence of bytes, we need to use encode() and decode() to convert python string to/from bytes.
You get byte object from HTTP request, need to convert to str to use.
1 2 3 4 5 6 7 8 9 10
x = "hello world" type(x) ##convert to byte stream data = x.encode("utf-8") ## convert back x = data.decode("utf-8")
## can have comma at end a = [1, 2, "abc", 34, 76.887, "jark", -84.124] a = [] ## slice a[1: -1: 3] a[: 5: 3] a[2:] ## reverse the sequence a[::-1] ## remove element from list defa[3]
a.append(998) ## join list, not append, they are different! a += [1, 2, 3] a.append([1, 2, 3])
a.pop() a.index("abc") a.count(34) a.remove("abc") a.insert(3, "apple") a.reverse() ## reverse sort a.sort(reverse = True) ## sort by length of each object a.sort(key=len)
## copy the list, but they are all shallow copy!! a[:] a.copy() list(a)
除了list自带的sort and reverse, out-of-place functions: sorted(), reversed() can also be used, they create a new list, reversed() will return a reversed iterator.
Dict
1 2 3 4 5 6 7 8 9 10 11 12
d = dict() d = {} ## add or update d["one"] = 34 del d["one"]
## key can be string, number and bool d = {"k1": "v1", "k2", 123} d = dict(a="343", b="uuys", c=123) ## can even be generated by tuple a = [("sure", 643), (98, "341"), ("name", "monkey")] d = dict(a)
The copy of dict is shallow.
1 2
d.copy() dict(d)
merge dict:
1 2
## if keys are overlapped, the value will be updated by the merged one d.update({"away": 998})
iterate dict via foreach loop:
1 2 3
for k in d.keys() for v in d.values() for k, v in d.items()
Use in and not in to check the existence.
Set
Immutable collection with unique immutable objects.
1 2 3 4 5 6 7 8
## s = () is tuple! s = set() s = {3, 676, 1, 34, 89} type(s)
If you want to have index pair with the item, use enumerate():
1 2 3
t = [3, 35, 546, 76, 123] for i, v inenumerate(t): print(f"i = {i}, v = {v}")
Iteration and Iterables
Comprehensions with filtering
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
## list comprehension [expr(item) for item in iterable if filtering(item)] ## set comprehension, no meaningful order {expr(item) for item in iterable if filtering(item)} ## dict {key_expr(item): val_expr(item) for item in iterable if filtering(item)}
## can also be mixed and nested ## this will create a list with 3 identical dict that value is None ## _ here is just for count [{letter: Nonefor letter in"ABCDEFG"} for _ in"123"ifint(_) % 2 != 0]
## y is nested ## x is outer loop [(x, y) for x inrange(3) for y inrange(5)]
Iterable can be passed to iter() to produce a iterator.
Iterator can be passed to next() to get the next value in the sequence.
Generator function, stateful, laziness with yield:
1 2 3 4 5 6 7 8 9 10 11 12
defgen(): yield0 a = 1 b = 2 whileTrue: yield a a, b = b, a + b
for i in gen(): print(i) if i > 1000: break
Generator expression, can save big memory than list comprehension:
1 2 3 4
(expr(item) for item in iterable if filtering(item)) ## if used as function parameter, no parenthesis is needed ## 多余的()可以省掉 sum(x * x for x inrange(10000000) if x % 2 == 0)
Generator is only single use object, 用完就没了,需要重新造一个。
There are several aggregation functions: bool: any(), all(); sync unlimited number of iterables zip():
1 2
for i, j inzip([1,2,3],[4,5,6]): print(f"output: {i}, {j}")
Class
Python does not have public, private, protected key word, everything is public.
Polymorphism is implemented by late binding in Python, 并不是和Java 通过继承实现多态,Python中你可以对一个Object 调用任意method,只要它有这个method就行,用的时候才会检查。 并且继承在Python中主要用来分享共用的方法,当然也可以来多态, 但是继承在python中用得不多。
## no number() in this class, but child class has defget_treble_number(self): """ description """ ## editor会报错,但构造parent()不会,除非你调用这个方法才会出错 returnself.get_number() * 3
## inheritance classChild(Parent): """ A Child class """
## class variable, shared among all instances class_variable = 0 ## initializer not quite a constructor ## self is similar to this in Java def__init__(self, number): """ description """
## _ prefix variable is class property, treated as private, although you can access it self._number = number ## still needs self to call method self._lst = self._gen_list() Child.class_variable += 1
classTest: def__init__(other, number): other._number = number
File I/O and Resource Management
Check default encoding, if not specified in open(), will use this default encoding format.
1 2
import sys sys.getdefaultencoding()
Open file with options, for example:
1 2 3
## mode can be any combination of `crwa`|`tb` ## r+ read/write f = open('words.txt', mode = 'rwt', encoding = 'utf-8')
Some useful methods after open the file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
## read all return string f.read() ## read one line f.readline() ## read all lines return a list f.readlines()
## use print, f is the file descriptor print("wirte this line", file = f) ## write lines into file f.writelines(["first\n", "second\n"]) ## rewind to beginning f.seek(0) ## close file after use, to flush updates to disk f.close(0)
For reading file, you can also use loop:
1 2 3
for line in f: ## will not print \n, or you can use print(line.strip()) sys.stdout.wirte(line)
Use with-block to auto close the resource (or you can use finally block),不仅仅是用在file上,比如网络上的读写也可以,它们背后的实现都遵循了同样的规则,所以可以使用with-block:
1 2
withopen(...) as f: pass
注意这个as 是可以省略的,在threading module的lock使用中,就没有as.
Data Structure
这里主要是和Java 对比,一些常用的数据结构,比如Stack, Queue, Deque, priorityQueue, Map, etc.
I have a post talks about IPC <<Linux IPC>>, it’s about IPC resource requirements for DB2 and how to adjust the ipc parameters permanently or temporarily. This time we face different ipc issue, interesting.
Description
We bring up DataStage container in particular order to make them function correctly, but when Engine is running, Xmeta DB2 went down silently whihout any warning or error messages. unquiesce will make DB2 up again, but it’s not stable.
This did not happen in K8s cluster, things get weired.
Reason
We update docker run command for Xmeta and Engine with --ipc=host, so they will share the ipc resouces with host machine (They reside on the same machine) and each other.
The trick is there are some ipcrm -a in the Engine start and quiesce scripts. So when Engine is up, it cleans host ipc resource and DB2 goes down.
we remove all ipcrm -a commands inside container and things get work.
Another thing is before start up the containers, make sure the ipc is clean, we encountered the problem that the old non-used ipc overstep in new containers, so run ipcrm -a in host machine to clean them.
Why K8s does not have this problem? Because we schedule Xmeta and Engine in different nodes, the ipcrm -a will not affect each other, but docker we hold all containers in one node.
Some usefully commands
1 2 3 4
ipcs -l ## list ipc configuration
ipcrm -a ## remove all ipc resources ipcs -a ## list ipc in use
I have talked about setting up pod network in Kubernetes cluster using Calico network add-on in this post <<Set Up K8s Cluster by Kubeadm>>. Recently I was involved in one issue from performance team, they complained that the network has bottleneck in calico, let’s see what happened and learn new things!
Description
The performance team set up a 6 nodes cluster with 1 master and 5 workers. Each machine has 48 cpu cores, 128GB memory, 2T+ disk and 10000Mb/s network speed.
They ran concurrent users with N compute pods and found that the bottleneck is in calico network:
BTY, there are still enough resources(CPU, Memory and Disk I/O) to support DataStage scale up for DS concurrent users to run jobs on nodes and pods. But the network bandwidth between pods is not enough to support it.
iperf3 Command
They use iperf3, a TCP, UDP, and SCTP network throughput measurement tool, measure memory-to-memory performance access a network, server-client mode.
This will transfer /large_file in client to /large_file in server, time interval is 40 seconds.
1 2 3 4 5 6 7 8 9 10
## server # -s: server # -F: read data from network and write to the file iperf3 -s -F /large_file ## client # -c: client mode connect to server ip # -i 1: 1 sec report # -t 40: transfer time 40 sec # -F: read from file and write to network iperf3 -c <server ip> -i 1 -t 40 -F /large_file
UDP traffic benchmark:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
## server # -s: server # -f M: format Mbytes/sec # -V: verbose # -p 5999: port iperf3 -s -f M -V -p 5999
## client # -p 5999: port # -c: client mode connect to server ip # -u: udp # -b 24M: bandwidth 24Mbits/sec -> 3Mbytes/sec # -t 600: time 600 sec # -l 300: packet len 300 bytes # so IP packet rate is around 3Mbytes/300bytes = 10K/sec iperf3 -p 5999 -c 10.19.1.15 -u -b 24M -t 600 -l 300
Pod to Pod
The same as Node to Node, but wget and build iperf3 inside pod container and use the container’s IP (in container run hostname -I), for example, I flood data from is-en-conductor-0 pod to is-engine-compute-12 pod, they reside in different host machine.
Thinking
After I reproducing the tests, I was thinking Calico is a widely used add-on that shouldn’t have such obvious bottleneck, otherwise many people will complain and improve it.
Is there any improper configuration?
Configuring IP-in-IP
By default, the manifests enable IP-in-IP encapsulation across subnets (additional overhead compare to non IP-in-IP), if don’t need it (when? I am not very clear), disable it in calico manifest yaml file:
Which Network Interface is Used
Another question I have is which network interface is used for node-to-node and pod-to-pod test?
There are several network interfaces in host machine, one is for public IP with MTU 9000 and in K8s we use private IP interface with MTU 1500. This will have impact on iperf3 testing.
It shows that pod-to-pod test uses MTU 1500 but node-to-node uses MTU 9000.
Need to test after enlarging MTU size to see if that change improves network throughput, also remember to update Calico manifest yaml, refer this document
1 2
# Configure the MTU to use veth_mtu:"9000"
ethtool Command
The ethtool can display speed property of the network interface, for example:
Settings for eno1: Supported ports: [ TP ] Supported link modes:10baseT/Half10baseT/Full 100baseT/Half100baseT/Full 1000baseT/Half1000baseT/Full Supported pause frame use:No Supports auto-negotiation:Yes Supported FEC modes:Notreported Advertised link modes:10baseT/Half10baseT/Full 100baseT/Half100baseT/Full 1000baseT/Half1000baseT/Full Advertised pause frame use:Symmetric Advertised auto-negotiation:Yes Advertised FEC modes:Notreported Link partner advertised link modes:10baseT/Full 100baseT/Full 1000baseT/Full Link partner advertised pause frame use:Transmit-only Link partner advertised auto-negotiation:Yes Link partner advertised FEC modes:Notreported Speed:1000Mb/s Duplex:Full Port:TwistedPair PHYAD:1 Transceiver:internal Auto-negotiation:on MDI-X:on Supports Wake-on:g Wake-on:d Current message level:0x000000ff(255) drvprobelinktimerifdownifuprx_errtx_err Link detected:yes
The output depends on the what the network driver can provide, you may get nothing if the virtual machine does not have much data available (for example, in IBM softlayer cluster), refer to this question
In a virtual machine the link speed or duplex mode is usually meaningless, as the network interface is most often just a virtual link to the host system, with no actual physical Ethernet layer. The speed is as high as the CPU and memory can handle (or as low, as the connection rate limit is configured), cabling type does no exist, as there is no cable, etc. This virtual interface is bridged or routed to the actual physical network by the host system and only on the host system the physical port parameters can be obtained.
you can check if the network interface is virtual or not:
1 2 3 4 5 6
ls -l /sys/class/net
# output example # both virtual: virtio1 lrwxrwxrwx. 1 root 0 Apr 27 16:11 eth0 -> ../../devices/pci0000:00/0000:00:04.0/virtio1/net/eth0 lrwxrwxrwx. 1 root 0 Apr 27 16:11 lo -> ../../devices/virtual/net/lo
How can Docker supply each container a hostname and DNS configuration without having to build a custom image with the hostname written inside? The trick is to overlay three crucial /etc files inside the container with virtual files where it can write fresh information.
In container, run:
1 2 3 4 5
# mount | grep etc
/dev/mapper/rhel-root on /etc/resolv.conf type xfs (rw,relatime,attr2,inode64,noquota) /dev/mapper/rhel-root on /etc/hostname type xfs (rw,relatime,attr2,inode64,noquota) /dev/mapper/rhel-root on /etc/hosts type xfs (rw,relatime,attr2,inode64,noquota)
Four different options affect container domain name services, please see official documentation for explanation:
-h HOSTNAME or --hostname=HOSTNAME
--link=CONTAINER_NAME or ID:ALIAS
--dns=IP_ADDRESS
--dns-search=DOMAIN
--dns-opt=OPTION
So what should dnsdomainname return exactly? for non-root engine docker container, the hostname is is-en-conductor-0.en-cond, but why sometimes dnsdomainname return empty but sometimes return en.cond.
I find the return depends on /etc/hosts file first non-self-loop IP hostname pair, for example:
From the CloudPak certification requirments, the uid for non-root user in container should be set in a higher range. We change it from 1000 to 100321000 but the image build got stuck and failed.
The reason is there is a range for uid number, it’s described in /etc/login.defs file (you can edit this file).
1 2 3 4
cat /etc/login.defs | grep -i UID
UID_MIN 1000 UID_MAX 60000
So if you create a user by useradd without specify user id explicitly, its uid will start from 1000. Also when change the uid by usermod -u <new uid> <user name>, you need to follow the limitation.
09/29/2021 docker commit is not a good way to create docker image as it makes image size bigger, using multi-stage build instead.
From major security hardening items: Try to explicitly set a USER/uid – to avoid even accidental startup as root. This is a good point I agree. But in our case it’s impossible to use USER directive with non-root user in dockerfile at beginning, as we need root user to install and configure services in containers, then alter the settings that cater to non-root user.
The solution is in the last docker commit, use --change 'USER 1000' to set default user as non-root. For example:
Note that the non-root user must exist in image, if it belongs to multiple groups (one primary and several supplementaries), only specify id is enough.
If later we need to run as root, just specify runAsUser: 0 in K8s yaml or --user 0 in docker run command, it will overwrite the default setting.
+-----------------------+ +----------------------+ +------------------------+ | | | | | | | continuous | | continuous | | continuous | | integration +-----------> delivery +-----------+ deployment | | | | | | | +---------+-------------+ +--------------+-------+ +---------------------+--+ ^ ^ ^ | | | | | | +---------------------+----------+ +--------------+----------------------+ +-----------+--------------------+ | kick off depends on commits | | platform specific testings: | | ready to be used by customers | | or schedule | | security, performance, API... | | | +-----------+--------------------+------+ | | +--------------------------------+ | code version control | +-------------------------------------+ | branching strategy | +-----------+-------------+-------------+ | regression testings | | | +-------------------------+
Regression testing: re-running functional and non-functional tests to ensure that previously developed and tested software still performs after a change. If not, that would be called a regression. Changes that may require regression testing include bug fixes, software enhancements, configuration changes, etc.
Continuous delivery automates the delivery of applications to selected infrastructure environments.
A mature CI/CD practice has the option of implementing continuous deployment where application changes run through the CI/CD pipeline and passing builds are deployed directly to production environments.
A best practice is to enable and require developers to run all or a subset of regressions tests in their local environments (or use Travis). This step ensures that developers only commit code to version control after regression tests pass on the code changes.
Test that require a full delivery environment such as performance and security testing are often integrated into CD and performed after builds are delivered to target environments.
To recap, CI packages and tests software builds and alerts developers if their changes failed any unit tests. CD is the automation that delivers changes to infrastructure and executes additional tests.
CI/CD is a devops best practice because it addresses the misalignment between developers who want to push changes frequently, with operations that want stable applications.