Good description for daily use:

One time I was in the ocean and a wave crashed on top of me and took my breath away as it pulled me deeper into the ocean. Just as I started to recover my breath, another wave dropped on top of me and extracted much of my remaining energy and pulled me even deeper into the ocean. Just as I started to recover, yet another wave crashed down on top of me. The more I would fight the waves and the ocean, the more energy I drained. I seriously wondered if I would die at that moment. I couldn’t breath, my body ached and I was terrified I was going to drown.

Being close to death helped me focus on the only thing that could save me, which was conserving my energy and using the waves not fighting them.

Install and Configure

Usually python2 is pre-installed, need to install python3, can refer this blog Install Python 3.7 on centos 7 This installation will not disturb original python2 pre-installed (it is the dependency of some other packages).

install pip3

1
2
3
4
5
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
## install pip3
python3 get-pip.py
## or install pip
python get-pip.py

then you can use pip3 to install other packages, otherwise pip is still use python2.

Make python command execute python3, you can use alias.

Installing packages using pip and virtual environments

Chapter 1 Introduction

Install ipython or ipython3, powerful mixed interactive shell: https://ipython.org/index.html#

1
2
3
pip install ipython
## or
pip3 install ipython

then run it as

1
2
3
ipython
## or
ipython3

Python variables use dynamic typing. In practice, this means reassigned to values of different types or classes

Built-in Functions

  • print
  • range Use spaces instead of tabs to indent.

Functions

functions can be object, can put it in the list.

1
2
3
>>> functions = [double, triple]
>>> for function in functions:
... print(function(3))

Wrapping Functions with Decorators, there are some other python command line build tools. click package:

1
pip install click
1
2
3
4
5
6
7
8
9
10
11
12
'''A example of using the click package to develop a command line tool'''

import click

@click.command()
@click.argument('name')
def hello(name):
'''Say hello to name'''
print(f"Hello {name}")

if __name__ == '__main__':
hello()

call it:

1
2
$ python simple_cli.py Sue
Hello Sue

lambda function, just like java use lambda to create comparator for priority queue.

1
sorted(items, key=lambda item: item[1])

RE package

The re module uses \ to delineate special character for matching, for example \., \n, etc. To avoid confusion with regular string escape sequences, raw strings are recommended in defining regular expressions. Raw strings are prepended with a r before the first quotation mark.

Similar as grep:

1
2
3
4
5
6
import re

re.search(r'Rostam', cc_list)
re.search(r'Chr[a-z][a-z]', cc_list)
re.search(r'[A-Za-z]{6}', cc_list)
re.search(r'[A-Za-z]+@[a-z]+\.[a-z]+', cc_list)

Lazy Evaluation

This will have footprint in memory, generate values as needed, will not take much memory.

Create generator, using () instead of [](list comprehension)

1
gen_o_nums = (x for x in range(100))

More IPYTHON features

Using IPython To Run Unix Shell Commands, add ! before command, but sometime does not need (default setting):

1
In [2]: !ls -l

can assign to a variable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
In [6]: res = !df -h | head -n 7

## list format
In [7]: res
Out[7]:
['Filesystem Size Used Avail Use% Mounted on',
'/dev/mapper/rhel-root 241G 73G 169G 31% /',
'devtmpfs 3.9G 0 3.9G 0% /dev',
'tmpfs 3.9G 0 3.9G 0% /dev/shm',
'tmpfs 3.9G 403M 3.5G 11% /run',
'tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup',
'/dev/vda1 1014M 208M 807M 21% /boot']

In [8]: res.grep("dev")
Out[8]:
['/dev/mapper/rhel-root 241G 73G 169G 31% /',
'devtmpfs 3.9G 0 3.9G 0% /dev',
'tmpfs 3.9G 0 3.9G 0% /dev/shm',
'/dev/vda1 1014M 208M 807M 21% /boot']

magic commands:

1
2
3
4
## enter bash 
In [13]: %%bash
## write into a file
In [14]: %%writefile test.sh

Make IPython import shell alias

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python
import re
import os.path
c = get_config()
with open(os.path.expanduser('~/.bashrc')) as bashrc:
for line in bashrc:
if not line.startswith('alias'):
continue
parts = re.match(r'^alias (\w+)=([\'"]?)(.+)\2$', line.strip())
if not parts:
continue
source, _, target = parts.groups()
c.AliasManager.user_aliases.append((source, target))

Drop this code in ~/.ipython/profile_default/ipython_config.py, just like .bashrc and .vimrc, the configuration file for ipython.

How to import shell functions in .bashrc? Or wirte python function instead.

Chapter 2 Automating Text and Files

In the DevOps world, you are continually parsing, searching, and changing the text in files, whether it’s searching application logs or propagating configuration files.

read regular file:

1
2
3
4
5
6
7
8
## don't need to close explicitly
with open("/root/DS/tmp.txt", "r") as handler:
data = handler.read()

## one char
data[0]
## file size
len(data)

or use

1
2
3
4
## this will parse lines by `\n`
data = readlines()
## i-th line
data[i]

Different operating systems use different escaped characters to represent line-endings. Unix systems use \n and Windows systems use \r\n. Python converts these to \n when you open a file as text. If you are opening a binary file, such as a jpeg image, you are likely to corrupt the data by this conversion if you open it as text. You can, however, read binary files by appending a b to mode:

1
2
3
file_path = 'bookofdreamsghos00lang.pdf'
with open(file_path, 'rb') as open_file:
btext = open_file.read()

write file:

1
2
3
4
5
content='''export a=123
export b=456
'''
with open("/root/DS/.envrc", "w") as handler:
handler.write(content)

The open function creates the file if it does not already exist and overwrites if it does. if want to append, use a mode instead of w. For binary file use bw or ba.

JSON

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import json
with open('xx.json', 'r') as handler:
data = json.load(handler)

json.load() is used to load file
## Deserialize fp (a .read()-supporting text file or binary file containing a JSON document) to a Python object using this conversion table.

json.loads() is used to load other object
##Deserialize s (a str, bytes or bytearray instance containing a JSON document) to a Python object using this conversion table.

pprint
Pretty printing has been turned ON
## then print data is good
## or
print(json.dumps(data, indent=2))

## update
data["workerNodeHosts"][0]["name"] = "localhost"
## write file
with open('xx.json', 'w') as handler:
json.dump(data, handler, indent=2)

## the same as load() and loads()
json.dump() is for file
json.dumps() is for general object

actually you can use data pretty printer:

1
2
3
import pprint

pprint.pprint(data)

YAML

The most commonly used library for parsing YAML files in Python is PyYAML. It is not in the Python Standard Library, but you can install it using pip:

1
2
3
4
5
6
7
8
9
10
11
12
13
pip install pyyaml

import yaml
## read
with open("xx.yml", "r") as handler:
data = yaml.safe_load(handler)

## python convert data as a dict, so you can edit it

print(yaml.dump(data, indent=2))
## write
with open("xx.yml", "w") as handler:
yaml.dump(data, handler, indent=2)

XML

Historically, many web systems used XML to transport data. One use is for RSS feeds. RSS (Really Simple Syndication) feeds are used to track and notify users of updates to websites. These feeds have been used to track the publication of articles from various sources. RSS uses XML formatted pages. Python offers the xml library for dealing with XML documents. It maps the XML documents hierarchical structure to a tree-like data structure.

1
2
3
4
5
import xml.etree.ElementTree as ET
tree = ET.parse('/tmp/test.xml')
root = tree.getroot()
for child in root:
print(child.tag, child.attrib)

CSV

data stored as comma-separated values.

1
2
3
4
5
6
7
8
In [16]: import csv
In [17]: file_path = '/tmp/user.csv'

In [18]: with open(file_path, newline='') as handler:
...: off_reader = csv.reader(handler, delimiter=',')
...: for _ in range(5):
...: print(next(off_reader))
..

pandas packages is mainstay to do data science work. Pandas has many more methods for analyzing and manipulating table like data, and there are many books on its use. You should be aware that it is available if you need to do data analysis.

Search Text

One widely used format is the Common Log Format (CLF). A variety of log analysis tools can understand this format:

1
2
<IP Address> <Client Id> <User Id> <Time> <Request> <Status> <Size>
127.0.0.1 - swills [13/Nov/2019:14:43:30 -0800] "GET /assets/234 HTTP

Just give some examples:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
line = '127.0.0.1 - swills [13/Nov/2019:14:43:30 -0800] "GET /assets/234 HTTP/1.0" 200 2326'
## use name groups
r = r'(?P<IP>\d+\.\d+\.\d+\.\d+) - (?P<User>\w+) \[(?P<Time>\d\d/\w{3}/\d{4}:\d{2}:\d{2}:\d{2} [-+]\d{4})\] (?P<Request>".+")'
m = re.search(r, line)

In [11]: m.group('IP')
Out[11]: '129.0.0.1'

In [12]: m.group('Time')
Out[12]: '13/Nov/2019:14:43:30 -0800'

In [13]: m.group('User')
Out[13]: 'swills'

In [14]: m.group('Request')
Out[14]: '"GET /assets/234 HTTP/1.0"'

Note: Python automatically allocates and frees memory.The Python garbage collector can be controlled using the gc package, though this is rarely needed.

For large files. If the files contain data that can be processed one line at a time, the task is easy with Python. You can read one line at a time, process the line, and then move to the next. The lines are removed from memory automatically by Python’s garbage collector, freeing up memory.

1
2
3
4
In [23]: with open('big-data.txt', 'r') as source_file:
...: with open('big-data-corrected.txt', 'w') as target_file:
...: for line in source_file:
...: target_file.write(line)

Chapter 3 Command Line

Python offers tools for interacting with systems and shells. You should become familiar with the sys, os, and subprocess modules, as are all essential tools.

1
2
3
4
5
6
7
8
9
10
11
import sys

## little or big endian
sys.byteorder
## python object size
sys.getsizeof(1)
## platform
sys.platform
## python version
sys.version_info.major
sys.version_info.minor

The most common usage of the os module is to get settings from environment variables.

1
2
3
4
5
6
7
8
9
10
import os

## pwd and cd
os.getcwd()
os.chdir('/tmp')
## get and set env var
os.environ.get('HOME')
os.environ['HOME'] = '/tmp'
## login user
os.getlogin()

With subprocess you can run your favorite shell command or other command line software and collect its output from within Python. For the majority of use-cases, you should use the subprocess.run function to spawn processes

1
2
3
4
5
6
7
8
import subprocess
## text = True: convert to string
sub = subprocess.run(['ls', '-ltr'], capture_output=True, universal_newlines=True [,text=True, stdout=<file>])
sub.stdout
sub.stderr
print(sub.stdout.decode())
## exception will be raised when use
sub = subprocess.run(['ls', '/non'], capture_output=True, universal_newlines=True, check=True)

Creating Command Line Tools

Invoke python script usually by:

1
python xx.py

or you can eliminate python by adding #!/usr/bin/env python(or python3) at first line of the script, then chmod the script to executable:

1
./xx.py

The simplest and most basic way to process arguments from the command line is to use the argv attribute of the sys module:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python
"""
Simple command line tool using sys.argv
"""
import sys

if __name__ == '__main__':
## sys.argv is a list
sys.argv[0]

if '--help' in sys.argv:
help_message = f"Usage: {sys.argv[0]} ..."
print(help_message)
sys.exit()
## can get index
idx = sys.argv.index('--namespace')
namespace = sys.argv[idx]

This is not enough, we need argument parser! Luckily there are modules and packages designed for the creation of command line tools. These packages provide frameworks to design the user interface for your module when running in a shell. Three popular solutions argparse, click, and fire. All three include ways to design required arguments, optional flags, and means to display help documentation. The first, argparse, is part of the Python standard library, and the other two are third-party packages that need to be installed separately (using pip).

argparse

这个有专门的tutorial,大概看了一下,it does take much work on your part but you get lots of control.

Automatically generates help and usage messages and issues errors when users give the program invalid arguments.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python
"""
Command line tool using argparse
"""
import argparse

parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('integers', metavar='N', type=int, nargs='+',
help='an integer for the accumulator')

## If the name begins with a dash, it is treated as an optional, flag, argument, otherwise as a position-dependent command.
parser.add_argument('--sum', dest='accumulate', action='store_const',
const=sum, default=max,
help='sum the integers (default: find the max)')

args = parser.parse_args()
print(args.accumulate(args.integers))

You can also define sub-commands, like git stash ....

click

It uses Python Function Decorators to bind the command line interface directly with your functions.

Python decorators are a special syntax for functions take other functions as arguments. Python functions are objects, so any function can take a function as an argument. The decorator syntax provides a clean and easy way to do this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env python
"""
Simple Click example
"""
import click

@click.command()
@click.option('--greeting', default='Hiya', help='How do you want to greet?')
@click.option('--name', default='Tammy', help='Who do you want to greet?')
def greet(greeting, name):
print(f"{greeting} {name}")


if __name__ == '__main__':
greet()

Please refer to click documents.

fire

fire document.

for example:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python
"""
Simple Fire example
"""
import fire

def greet(greeting='Hiya', name='Tammy'):
print(f"{greeting} {name}")
def goodbye(goodbye='Bye', name='Tammy'):
print(f"{goodbye} {name}")

if __name__ == '__main__':
fire.Fire()

An exciting feature of fire is the ability to enter an interactive mode easily. By using the --interactive flag, fire opens an IPython shell with the object and functions of your script available:

1
./fire_example.py <command> <args> -- --interactive

Overall, We recommend click for most use cases. It balances ease and control. In the case of complex interfaces where you want to separate the UI code from business logic, argparse is the way to go. Moreover, if you need to access code that does not have a command line interface quickly, fire is right for you.

Implementing plugins

Once you’ve implemented your applications command line user interface you might want to consider a plugin system. Plugins are pieces of code supplied by the user of your program to extend functionality.

A key part of any plugin system is plugin discover. Your program needs to know what plugins are available to load and run. Create a file named add_plugins.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/env python
import fire
import pkgutil
import importlib

def find_and_run_plugins(plugin_prefix):
plugins = {}

# Discover and Load Plugins
print(f"Discovering plugins with prefix: {plugin_prefix}")
# pkgutil.iter_modules returns all modules available in the current sys.path
for _, name, _ in pkgutil.iter_modules():
# Check if the module uses our plugin prefix
if name.startswith(plugin_prefix):
# Use importlib to load the module, saving it in a dict for later use.
module = importlib.import_module(name)
plugins[name] = module

# Run Plugins
for name, module in plugins.items():
print(f"Running plugin {name}")
# Call the run method on the plugin.
module.run()

if __name__ == '__main__':
fire.Fire()

then you can wirte modules for example: module1.py and put it in sys.path directory. If you run ./add_plugins.py find_and_run_plugins module, then it will search, load and run the module1.py module.

Turbocharging Python with Command Line Tools

Here are the raw ingredients that will be used to make several solutions:

  • Click Framework
  • Python CUDA Framework
  • Numba Framework
  • Scikit-learn Machine Learning Framework

These are tools to speed up the performance.

Chapter 4 Useful Linux Utilities

This chapter will go through some common patterns in the shell and will include some useful Python commands that should enhance the ability to interact with a machine.

As a seasoned performance engineer once said, it depends on what is measured and how.

disk utility

If we had to work in an isolated environment with a server that doesn’t have access to the internet or that we don’t control and therefore can’t install packages, we would have to say that the dd tool can provide help.

This will get thoughput of the new device, for example, the throughput is 1.4GB/s

1
2
3
4
5
dd if=/dev/zero of=<new device> count=10 bs=100M

10+0 records in
10+0 records out
10506731520 bytes (11 GB) copied, 3.12099 s, 1.4 GB/s

how to get IOPS, update every 1 second:

1
iostat -d <device> 1

Other common test tool is fio, you may need to install this package. It can help clarify the performance behavior of a device in a read-heavy or write-heavy environment (and even adjust the percentages of reads vs. writes).

network utility

  • ssh tunneling (ssh port forwarding)

For example, the server hello.com can only access by ssh with port 3345 and not exposed,let’s forward hello.com:3345 to a local port in my machine: https://www.youtube.com/watch?v=AtuAdk4MwWw

1
ssh -f -L 12333:hello.com:3345 root@hello.com -N

-f means run in bachground -L is forwarding rule -N means don’t get into remote shell root@hello.com is the username and address of that server

This tech can also be used to bypass firewall for some ports. Then we can access localhost:12333 to access the server. 疑问:如果port已经被firewall block了,ssh是怎么连接上的呢?

This blog shows how double level softlink can be a good workaround for some situations.

We need to link persistent data to a directory under / in a container or pod, for example: /data, this folder is owned by demo, demo is also the start user of the container.

在pod中有个/data folder, owned by demo,我们想persist这个folder的内容. 在这个pod中有个mount point /mnt. 于是想把/data map到 /mnt/data.

1
2
3
# ln -s <target> <link name>
# 这样相当于/mnt/data 是个link
ln -s /data /mnt/data

这样是不对的,从pod外部的storage provisioner看 /min/data仅仅是个borken link.

The correct way is first remove /data then ln -s /mnt/data /data (/data变成了快捷方式,所以写入/data的内容实际上被写入了/mnt/data), but demo is a non-root user without super privilege, it cannot remove /data (/ is owned by root).

Let’s see how double level softlink can help:

  1. first in docker build time remove /data: rm -rf /data
  2. create a intermediary: mkdir -p /home/demo/data && chown demo:demo /home/demo/data
  3. link: ln -s /home/demo/data /data

then commit the changes into image.

when container start, in the entrypoint:

  1. first remove /home/demo/data: rm -rf /home/demo/data, this will make link to /data break.
  2. create another link: ln -s /mnt/data /home/demo/data, now link connected and fixed.

So finally the link structure is:

1
/mnt/data -> /home/demo/data -> /data

/home/demo/data is a agent between persistent mount/mnt/data and /data.

skopeo is a command line utility that performs various operations on container images and image repositories. see it’s git repos. This is really a fantastic tool! Other two complementaries are buildah and podman.

Command usage see here.

In Red Hat/Centos, you can use yum to install skopeo.

Note the docker registry should be secured by SSL/TLS, basic authentication can also apply.

Skopeo can do paralelly copy, you can run several skopeo processes in background then wait.

Docker Installed

In docker environmet, skopeo will check $HOME/.docker/config.json file for authentication (created by docker login command). If the auth file is there, you are good and you can directly copy image tar (or gzip tar) to docker registry and copy image directly from docker registry to local docker daemon for example (can use docker hub to do test):

1
2
3
4
5
6
7
skopeo copy \
docker-archive:<absolute or relative path>/<tar name>.tar.gz \
docker://<registry>[:5000]/<image name>:tag

skopeo copy \
docker://<registry>[:5000]/<image name>:tag \
docker-daemon:<image name>:tag

This is extremely efficient compare to old load/tag/push method. Many other benefits like no docker daemon needed, rootless please see doc.

Inspect docker image in registry:

1
2
skopeo inspect \
docker://<registry>[:5000]/<image name>:tag

Delete image in registry

1
2
skopeo delete \
docker://<registry>[:5000]/<image name>:tag

You must enable deletion in docker registry configuration:

No Docker Installed

If there is no docker daemon, skopeo will still work, but you need to explicitly give the auth creds and ssl/tls certificates path of the target registry, for example, the destination registry login user name and password are both demo, and the certificates path for ssl/tls is /root/certs (must include *.key, *.crt and *.cert, the *.crt and *.cert could be the same content if it’s self-signed).

1
2
3
4
5
6
7
8
9
10
11
skopeo copy \
--dest-creds demo:demo \
--dest-cert-dir /root/certs \
docker-archive:<absolute or relative path>/<tar name>.tar.gz \
docker://<registry>[:5000]/<image name>:tag

skopeo copy \
--src-creds demo:demo \
--src-cert-dir /root/certs \
docker://<registry>[:5000]/<image name>:tag \
docker-daemon:<image name>:tag

Inspect docker image in registry:

1
2
3
4
skopeo inspect \
--creds demo:demo \
--cert-dir /root/certs \
docker://<registry>[:5000]/<image name>:tag

Delete image in registry

1
2
3
4
skopeo delete \
--creds demo:demo \
--cert-dir /root/certs \
docker://<registry>[:5000]/<image name>:tag

You must enable deletion in docker registry configuration:

Other Use Case

One important use case is we pre-load image to target host machine because we pre-assign some application runs on some dedicated nodes. Pre-load will save much time from pull if the image is big, the application pod will up and run instantly.

So, how to copy image tarball from local to remote machine docker daemon? Yes there are --src-daemon-host and --dest-daemon-host options, but how?

Refer document from docker: By default, the Docker daemon listens for connections on a UNIX socket to accept requests from local clients. It is possible to allow Docker to accept requests from remote hosts by configuring it to listen on an IP address and port as well as the UNIX socket.

So, let first open the port in target host: It is conventional to use port 2375 for un-encrypted, and port 2376 for encrypted communication with the daemon.

注意,这里我只考虑了un-encrypted,因为设置encrypted connection可能会影响kubelet对docker的操控,我在使用完后关闭了这一端口,并且如果集群有单独的网关且访问worker nodes的IP是内部的,影响也不大。

Here we can configure remote access with systemd unit file or with daemon.json, I prefer the second one, because after systemd unit file update, I need to reload then restart docker:

1
2
systemctl daemon-reload
systemctl restart docker

Using daemon json file only need to restart, just add this line to it:

1
2
3
{
"hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2375"]
}

You can listen on port 2375 on all network interfaces(eg: 0.0.0.0), or on a particular network interface using its IP address(eg: 172.192.10.1).

After restart docker service, check the port status:

1
netstat -lntp | grep 2375

Now we can execute copy:

1
2
3
4
skopeo copy \
--dest-daemon-host http://<target machine hostname or ip>:2375 \
docker-archive:<path>/<image tarball name>.tar.gz \
docker-daemon:<image name>:<tag>

Check these articles: Skopeo Copy to the Rescue

Today I learn a new method to operate on permission of files and directories, usually I use chmod and chown.

One thing you need to be clear is if for example /etc is owned by root, and /etc/xxx is owned by demo (non-root) user, demo cannot remove /etc/xxx because of permission deny, but demo can create soft link from /etc/xxx and do all other operations inside /etc/xxx.

What if demo want to remove /etc/xxx without changing permissiond of /etc by chmod or chown and without sudo? setfacl is a good choice.

Note that docker will not allow commit the change of any permission of / directory into image.

Each file and directory in a Linux filesystem is created with Access Control Lists (ACLs). The permissions can be set using the setfacl utility. In order to know the access permissions of a file or directory we use getfacl.

For example:

1
2
3
4
5
6
7
8
9
# getfacl /etc

getfacl: Removing leading '/' from absolute path names
# file: etc/
# owner: root
# group: root
user::rwx
group::r-x
other::r-x

then we add demo full permission to /etc

1
2
## run as root
setfacl -m u:demo:rwx /etc

check again:

1
2
3
4
5
6
7
8
9
10
11
# getfacl /etc

getfacl: Removing leading '/' from absolute path names
# file: etc
# owner: root
# group: root
user::rwx
user:demo:rwx
group::r-x
mask::rwx
other::r-x

I have this question: Difference between chmod vs ACL

Under Linux, ls -l puts a + at the end of the permissions characters to indicate that ACL are present. If ACL are presenting then the basic permissions do not tell the full story: ACL override POSIX permissions:

1
2
3
# ls -l /etc

drwxrwxr-x+ 89 root root 8192 Sep 25 16:24

我的临时驾照过期了快一周了,忘记去renew,新的驾照也没寄过来(也不知道是不是给我办的real ID?, 准备明天去DMV。仔细观察了一下,当时办事人员在我的临时驾照上写了一个Legal Presence号码(916-657-7790),应该是快过期的时候打过去查询进度和催促用的,但是我忘了。。。我印象里以为10月 份才过期😌。

所以说,明天去了之后有几件事需要确认:

  1. 继续renew
  2. 查询驾照审批进度
  3. 再次确认地址

保险起见还是带上[必要的材料]:

  • 护照
  • I-94
  • Old DL
  • SSN Card
  • EAD
  • H1B

后续: 上次去renew的时候用的是OPT学生身份,但在审批过程中我的身份变成了H1B,不一致导致了pending,这 次去更新了一下,所以一定要用最新的身份去办理。

09/30/2019, 今天收到了新的driver license,居然不是Real ID, 之后还得再去更换,否则2020以后 乘机还得带护照了。决定2020之后再去办理了,因为会搬家。记得要2份不同的住址证明,否则不给办.

10/18/2019, 过了一个月sticker还是没有收到,去DMV一问才知道地址居然还是以前的,原来car registration的地址和Drive license的地址都要修改。花了22刀现场办了一个,算是了结一件事.

2022-12 DL Renewal

I have received the notice from DMV to renew my DL(expiration date 2023/01). Book a appointment if in person renew is needed.

I have also completed the H1B extension application, the file has been received.

I did not see any documents that are required for renewal, in case, just bring them together:

  • Renewal Notice from DMV
  • Application fee
  • Passport
  • H1B Extenstion Notice (I-94 included)
  • Old DL
  • SSN Card
  • New Address Memo

I was not required to redo the test, just needs to finish the application, inputed your personal attributes such has eye color, hight, weight, etc (from the old DL).

For my case, only passport, H1B(I-94) were collected, whole process took about 1 hour, pretty efficient.

When I check the cmd and entrypoint of one docker image, I see something like this:

1
2
cmd: zkServer.sh start-foreground
entrypoint: /docker-entrypoint.sh

It actually by default works as:

1
/docker-entrypoint.sh zkServer.sh start-foreground

Let’s see what is inside /docker-entrypoint.sh:

1
2
3
4
5
6
# Generate Zookeeper configuration
# zkGenConfig.sh is in PATH, it will be executed if condition matches
[ "$1" = 'zkServer.sh' ] && (zkGenConfig.sh || exit 1)

# execute zkServer.sh start-foreground in current process
exec "$@"

It just like a wrapper and executes the passed parameters as a new command. This pattern gives me some inspirations. Actually lots of containers use this pattern, like Envoy, please search my blog <<Docker Entrypoint Script>>

About $@, reference from https://stackoverflow.com/questions/9994295/what-does-mean-in-a-shell-script: $@ is nearly the same as $*, both meaning all command line arguments. They are often used to simply pass all arguments to another program (thus forming a wrapper around that other program).

The difference between the two syntaxes shows up when you have an argument with spaces in it:

1
2
3
4
5
6
7
8
9
10
11
12
wrappedProgram "$@"
# ^^^ this is correct and will hand over all arguments in the way
# we received them, i. e. as several arguments, each of them
# containing all the spaces and other uglinesses they have.
wrappedProgram "$*"
# ^^^ this will hand over exactly one argument, containing all
# original arguments, separated by single spaces.
wrappedProgram $*
# ^^^ this will join all arguments by single spaces (IFS)as well and
# will then split the string as the shell does on the command
# line, thus it will split an argument containing spaces into
# several arguments.

For example:

1
2
# in terminal, type:
wrapper "one two three" four five "six seven"

What you will get:

1
2
3
4
5
6
7
# the same format as we passed
"$@": "one two three" "four" "five" "six seven"
# joined each argument by IFS shell variable and produce one final argument
"$*": "one two three four five six seven"
# all separated
$*: one two three four five six seven
$@: one two three four five six seven

"$@" is by far the most useful for most situations because it preserves the integrity of each positional parameter.

//TODO: [ ] how to setup dashboard can be accessed from outside of the cluster [ ] cert things? [ ] user admission, for example: cluster admin vs regular user [ ] authenticate method: token or kubeconfig

Kubernetes version 1.13.2

Dashboard relies on Metrics Server or Heapster to sample and provide running parameters of the cluster. https://github.com/kubernetes/dashboard

Metrics server: https://github.com/kubernetes-incubator/metrics-server other tools: https://blog.containership.io/kubernetes-metrics-collection-options/

06/12/2020, 打算下半年把K8s CKA/CKAD 证书考了,要review一下这本书。

这是最近几周看的书(中文),年初从国内带过来的。

最近的项目涉及到很多微服务架构设计的问题,和组里大佬讨论的时候发现自己有的地方理解得不太正确甚至一张白纸,给不出一个完善的设计方案和想法,赶紧更新和归纳一下自己之前学到的知识点。

其实接触Kubernetes也快一年了,但一直是对之前项目的维护和更新操作,这次难得机会要集成一个新组件到已有的集群里,从头开始设计这些功能组件的各种配置,结构,生命周期,存储卷,依赖等。这本书算是一个由点到面的总结,提供了不少的帮助。

最近逐渐体会到一个好的,完善的,充分考虑的顶层设计是多么重要,否则每次设计改动对应到底层可能耗时耗力,甚至积重难返,不得不相互妥协。

这本书不打算做细致的笔记,但会记录一些以前没意识到或没见过的概念和工具。之后打算更细致的看一看<<Kubernetes in Action>>。我看最近O'REILLY出版了或即将出版很多关于Kubernetes的新书,其中比较吸引我的是patternbest practices, Operator以及一些cloud native devops相关的知识点,当然还包括一些重要系统组件和插件,比如SSL/TLS, CoreDNSetcd等。看来2019剩下的日子是真的会很忙了。

笔记

按照计划在2019年10月之前把这本书看完了第一遍,在书中做了不少标记和勾画,接下来快速记录一下其中的我比较注意的地方:


Kubernetes通过其附加组件之一的CoreDNS为系统内置了服务注册和服务发现功能,为每个Service配置了DNS名称,允许客户端通过此名称发出访问,并且Service通过kube-proxy creates iptabls or ipvs内建了负载均衡机制。Service本质上讲是一个四层代理服务,也可将外部流量引入集群内部。

网络存储系统,诸如:NFS, GlusterFS, Ceph, Cinder…目前项目里面其实用的是本地存储,只不过额外设置了NFS将本地存储连接了起来,并没有直接只用K8s中的NFS配置。

API Server是整个集群的网关。

etcd是由CoreOS基于raft协议开发的分布式键值存储,可用于服务发现,共享配置以及一致性保障,它是独立的服务组件,并不隶属于K8s。etcd中键值发生变化时会通知API Server,并通过watch API向客户端输出,实现高效协同。

K8s supports container runtime: Docker, cri-o, RKT, Fraki cri-o use gRPC(Remote procedure call)

K8s Dashborad Prometheus and it’s add-ons Ingress controller: 应用层负载均衡机制: Nginx, HAProxy…

Pod网络由网络插件实现(for example: fannel, calico, weave…), Service网络由K8s指定。


This is a good script template which returns JSON response, it has exit hook, logs and arguments checking. I will continuously update it if I find good patterns.

Use Python script is more developer friendly and easy to maintain.

对于一般用途的script,有几点需要注意:

  1. shebang 必须有 #!/usr/bin/env bash or #!/usr/bin/env sh
  2. debug set 放开头, 如同这里一样
  3. usage 函数格式要清晰,可以参考这个例子,且必须有-h/--help flag
  4. 传入的argument 要检查个数,以及是否为空
  5. 参数输入多多测试不同情况
  6. 可能需要log,可用temp 以及 exit hook解决,log放 /tmp directory
  7. 输出信息用[date time] prefix, 可以用LogMsg function wrap一下

Using # for comment is preferred, but you can comment multi-line by heredoc:

1
2
3
: <<'END_COMMENT'
...
END_COMMENT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#!/usr/bin/env bash
#########################################################################
# Please insert the update at top of the logs
# Date Author Description
#
#########################################################################
## syntax check only, dry run
#set -n
## add line number for -x
export PS4='$LINENO + '
## pipeline subcommand failure check
set -o pipefail
## e: command line failure check
## u: undefined variable check
## x: print command executed
set -eux

## find canonical file path
## or you can restrict run script in it's current directory
CUR_PATH=$(dirname $(readlink -f $0))
source $CUR_PATH/<env file>
JQ="$CUR_PATH/jq"

## create log file for this script
OUT_FILE=$(mktemp /tmp/log.XXXX)
#OUT_FILE=/tmp/log

########################### function #########################
function exitHook {
rm -f $OUT_FILE
rm -f ${OUT_FILE}.out
rm -f ${OUT_FILE}.err
}
## clean job for log file, you can add other signals
trap exitHook EXIT

function usage() {
echo "<Description>"
echo
echo "Usage:"
echo " $0 [...]"
echo
echo "Flags:"
echo " -h, --help help info"
}

## this is for return json format messages
function returnError() {
msg="$1"
echo "{\"result\": \"Failure\", \"message\": \"$msg\"}" |${JQ} '.'
exit 1
}

function returnInfo() {
msg="$1"
echo "{\"result\": \"Success\", \"message\": \"$msg\"}" |${JQ} '.'
exit 0
}

########################## main ##############################
## declare input parameters
P1=""
P2=""

## check script parameters
if [[ $# -eq 0 ]]; then
msg="No command-line arguments were specified..."
returnError "$msg"
fi

while [[ $# -gt 0 ]]
do
case "$1" in
-p1|--p11)
shift
P1=$1
shift;;

-p2|--p22)
shift
P2=$1
shift;;

*) msg="$0: Bad Usage..."
returnError "$msg"
esac
done

## double check or restrict parameters
if [[ "X${P1}" = "X" ]]; then
msg="-p1 was not specified"
returnError "$msg"
elif [[ "X${P2}" = "X" ]]; then
msg="-p2 was not specified"
returnError "$msg"
fi

## then do the job, when you run some commands in script
## you can redirect the normal output to ${OUT_FILE}.out
## and error output to ${OUT_FILE}.err by:
command 1>>${OUT_FILE}.out 2>>${OUT_FILE}.err

if [[ $? = 0 ]]; then
msg="Run command successfully..."
returnInfo "$msg"
else
## parse err log file and return message
msg=$(cat ${OUT_FILE}.err|sed -e 's/"/\\"/g')
returnError "$msg"
fi

Let me record the workflow about how to create your own working branch from master.

I git clone the original repo to local, then git checkout -b to a develop branch on top of master branch, for example:

1
(cd /GitRepos; git clone git@github.xxx.com:xxx.git)

Then go to local git repository

1
git checkout -b develop

After checkout to new branch, if you do nothing and get files changed, this is sometimes a format issue, go to edit .gitattributes, comment out the regex, for example:

1
# * text=auto

Save and run git status, you will see the change now is on .gitattributes, then undo the change and save.

Now you are in develop branch, let’s check the config information:

1
git config --list

Focus on the user.name and user.email, if they are empty, set them by

1
2
3
git config --global user.name xxx
git config --global user.email xxx
git commit --amend --reset-author

Then working on develop branch, when you run git push origin develop, this will create a pull request (you can decide merge to which branch) and a remote develop branch, then you can review and merge to master branch on git web.

Because there is no git repos fork, so run git pull origin master in develop branch to get sync up with latest updates from master branch.

0%