cd /tmp # slow ddif=/dev/zero of=./ddfile bs=1G count=3 # fastest truncate -s 3G ./trunfile # fast fallocate -l 3G ./fallfile
# sync, depends on you needs # if no sync, files may be still in memory sync
# ls -ltrh # in logical view, they are all 3GB -rw-r--r--. 1 root root 3.0G Dec 30 07:01 ddfile -rw-r--r--. 1 root root 3.0G Dec 30 07:02 trunfile -rw-r--r--. 1 root root 3.0G Dec 30 07:02 fallfile
# check physical storage # truncate space almost does not count # because of sparse df -h .
# time test # several seconds time /bin/cp ./ddfile ~ time /bin/cp ./fallfile ~
# near instant time /bin/cp ./trunfile ~
注意这里sync之后,file cache是仍然存在的,如果要彻底drop file cache, run echo 3 > /proc/sys/vm/drop_caches
ddif=/dev/zero of=sparse_file bs=1 count=0 seek=3G # Block is 0 stat sparse_file Size: 3221225472 Blocks: 0 IO Block: 4096 regular file
dd can be configured write to disk rather as well:
1
ddif=/dev/urandom of=/dev/sdb1 bs=1M count=2048
You can also shrink a file with many empty blocks with fallocate command:
1 2 3 4 5 6 7 8 9 10
ddif=/dev/zero of=./empty bs=1G count=3 # see the block number before and after change stat ./empty Size: 3221225472 Blocks: 6291544 IO Block: 4096 regular file
# activate the venv first # need to be the same yamllint pylint version as the team use # different version has different output maybe pip install yamllint==1.17.0 pip install pylint==2.12.2
# filter and get updated yaml and python file name list # diff between HEAD~1 HEAD, order matters! # the between is for endpoint not for range! YAML_FILES=$(git diff --name-only --diff-filter=ACMR HEAD~1 HEAD | grep -E "(.+\.yaml$|.+\.yml$)" || echo"") PY_FILES=$(git diff --name-only --diff-filter=ACMR HEAD~1 HEAD | grep -E ".+\.py" || echo"")
# .pylintrc and .yamllint # should be in the code repo # -r: no run if input is empty # xargs default input will be placed at end echo$PY_FILES | xargs -r pylint -sn --rcfile=.pylintrc >> linter_results_tmp echo$YAML_FILES | xargs -r yamllint -c .yamllint >> linter_results_tmp
[x] ordinary file can also be formatted and mounted
[x] terraform mount disk /dev/sdb, why this name?
[x] do experiment, using vagrant mount extra disk
[x] blkid, lsblk 使用场景
[x] fstab mount 设置
Vagrant demo please see vagrant-storage. This is for VirtualBox provider, at the time of writting this is a experimental feature.
After launch the vagrant machine:
1 2 3 4
# up VAGRANT_EXPERIMENTAL="disks" vagrant up # down vagrant destroy -f
# check block device, their name, type, size and mount point # sdb and sdc are the additional disks added in Vagrantfile lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 40G 0 disk ├─sda1 8:1 0 1G 0 part /boot └─sda2 8:2 0 31G 0 part ├─centos_centos7-root 253:0 0 29G 0 lvm / └─centos_centos7-swap 253:1 0 2G 0 lvm [SWAP] sdb 8:16 0 2G 0 disk sdc 8:32 0 2G 0 disk
# /dev/sda is not used up, let's add one more partition of 3GB fdisk /dev/sda Command (m forhelp): n Partition type: # we have sda1 sda2 2 primart already p primary (2 primary, 0 extended, 2 free) e extended Select (default p): Using default response p Partition number (3,4, default 3): First sector (67108864-83886079, default 67108864): Using default value 67108864 Last sector, +sectors or +size{K,M,G} (67108864-83886079, default 83886079): +3GB Partition 3 of type Linux and of size 2.8 GiB is set
Command (m forhelp): w The partition table has been altered!
Calling ioctl() to re-read partition table.
WARNING: Re-reading the partition table failed with error 16: Device or resource busy. The kernel still uses the old table. The new table will be used at the next reboot or after you run partprobe(8) or kpartx(8) Syncing disks.
# re-read partition table for /dev/sda partprobe -s /dev/sda
# now see lsblk, sda3 NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 40G 0 disk ├─sda1 8:1 0 1G 0 part /boot ├─sda2 8:2 0 31G 0 part │ ├─centos_centos7-root 253:0 0 29G 0 lvm / │ └─centos_centos7-swap 253:1 0 2G 0 lvm [SWAP] └─sda3 8:3 0 2.8G 0 part sdb 8:16 0 2G 0 disk sdc 8:32 0 2G 0 disk
# format sda3 mkfs -t ext4 /dev/sda3
# see type blkid | grep sda3 /dev/sda3: UUID="7d4a365c-1639-41de-a7c7-ebe79ea2830c" TYPE="ext4"
# mount to /data3 folder mkdir /data3 mount /dev/sda3 /data3 mount | grep sda3 # check lsblk # now see lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 40G 0 disk ├─sda1 8:1 0 1G 0 part /boot ├─sda2 8:2 0 31G 0 part │ ├─centos_centos7-root 253:0 0 29G 0 lvm / │ └─centos_centos7-swap 253:1 0 2G 0 lvm [SWAP] └─sda3 8:3 0 2.8G 0 part /data3 sdb 8:16 0 2G 0 disk sdc 8:32 0 2G 0 disk
# check space and inode usage cd /data3 df -k . df -i .
If you copy a big file to /data3, its use% may not change because flush does not happen, run sync to flush file system buffer.
# list partitions and see type of a disk # `p` to see part # `n` to add new part # `d` to delete part # `w` to confirm # `q` to quit # /dev/sda is disk type fdisk /dev/sda
# or ls /dev/sda*
Formating
Formatting partition or disk is to make a filesystem on it.
1 2 3 4 5
# format as ext4 type to /dev/sdb1 partition # -t: type # -f: force reformat mkfs -t ext4 -f /dev/sdb1 mkfs -t xfs -f /dev/sdb1
Mounting
Mounting is associating a filesystem with a directory, mornally we would mount an empty directory, otherwise the content in existing directory will be hidden.
1 2 3 4 5 6 7 8 9
mount /dev/sdb1 /data
# non-disk filesystem, system will do special mounts for you # -t: type mount -t proc proc /proc mount -t sysfs sysfs /sys mount -t debugfs debugfs /sys/kernel/debug # NFS mount server:dir /nfs-data
The persistent mounts are in /etc/fstab file, If you type mount command on shell, you will see several mount points, like above proc, sysfs, debugfs. These are special mounts not in /etc/fstab, systemd mounts them on boot automatically.
For the mount options specific to file system type, see man page fstab and mount.
You can unmount by umount command, filesystem cannot be unmounted while in use, for example files are open, process has dir in it. Can check by lsof command.
1
umount /data
Filesystem Types
Commonly used ones: ext2/3/4, xfs, btrfs. they have different properties.
1 2 3 4
man 5 filesystemd man 5 ext4 man 5 xfs man 5 btrfs
Note that sometime you cannot create file because of no space left on device, but when you check df -k ., it is not full, check df -i . may help, you may use up the inode capacity even if there are lots of space remain.
LVM
logical volume manager, a layer above physical partitions that allows multiple partitions to appear as one. Provides for growing a filesystem by adding physical space, can stripe and mirror.
There are 3 levels of abstraction, one onto another:
physical volumes: disk or disk partitions
volume groups: collections of physical volumes
logical volumes: partitioning up a volume group
Assume we have new disk partitions /dev/sdc1, /dev/sdc2, /dev/sdc3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
lvm # show all physical volume > pvs -a # find lv on physical volume > pvck /dev/sda2 # show logical volume group > vgs
# list physical volume # you can see volume group name pvscan
# display detail pvdisplay /dev/sdc1
Let’s create volume group:
1 2 3 4 5 6 7 8 9 10 11 12 13
# -c n: cluster no # vg1: group name vgcreate -c n vg1 /dev/sdc1 /dev/sdc2 pvscan # add /dev/sdc3 to group vgextend vg1 /dev/sdc3 pvscan # remove unused pv vgreduce vg1 /dev/sdc3 pvscan # remove group vgremove vg1 pvscan
Then create logical volume:
1 2 3 4 5 6 7
# create a logical volume 600M from group vg1 # -L: size # -n: lv name lvcreate -L 600M -n apple vg1 # format and mount it mkfs -t ext4 /dev/vg1/apple mkdir /apple && mount /dev/vg1/apple /apple
1 2 3 4 5 6 7
// apple will across 2 physocal volume sdc 8:320 2G 0 disk ├─sdc1 8:330 500M 0 part │ └─vg1-apple 253:20 600M 0 lvm /apple ├─sdc2 8:340 500M 0 part │ └─vg1-apple 253:20 600M 0 lvm /apple └─sdc3 8:350 500M 0 part
1 2 3 4 5 6 7 8 9 10 11 12 13 14
umount /apple # extend # 注意不能超过所在group的总大小 # it will also extend filesystem automatically lvextend -L +200M -r /dev/vg1/apple
# shrink, first resuze filesystem # then lv fsadm -e resize /dev/vg1/apple 300M lvresize --size 300M /dev/vg1/apple
# remove and reduce for logical volume lvremove lvreduce
现在就明白了,在Vagrantfile demo中,最开始已经有了2个logical volume: root and swap:
可以查看这2个lv 的信息, 这表示/dev/sda2是一个physical volume,centos_centos7 是volume group name.
对于logical volume的每一个抽象层,都有对应的command:
1 2 3 4 5 6
# show physical volume info pvdisplay # show lv info lvdisplay # show volume group info vgdisplay
Swapping
Swapping 对于很多应用和服务都不太好,影响性能,所以注意是否需要关闭。
Usually swapping is set in /etc/fstab file, for example, in Vagrant machine:
1 2 3
// as mentioned, /dev/mapper/centos_centos7-swap // is a logical volume /dev/mapper/centos_centos7-swap swap swap defaults 00
To disable it, just comment out and run swapoff /dev/mapper/centos_centos7-swap or swapoff -a
A partition or file can be configured as swap space:
1 2 3 4 5 6 7 8
# a partition mkswap /dev/sdc2 # on and off, not persistent swapon /dev/sdc2 swapoff /dev/sdc2
# check swap components swapon -s
Uses a file as swap space (loop device也是类似的情况)
1 2 3 4 5 6 7 8 9 10
# generate 1G file # or using truncate or fallocate ddif=/dev/zero of=/tmp/myswap bs=1G count=1 chown root:root /tmp/myswap chmod 0600 /tmp/myswap # enable swap mkswap /tmp/myswap swapon /tmp/myswap # off swapoff /tmp/myswap
Then if you check free -h, swap space gets extended 1G.
Loop Device
这里不得不说到Pseudo-devices, 也就是常用的/dev/null, /dev/zero, /dev/full, /dev/random, etc. 它们都是character based devices.
What is loop device wiki, is a pseudo-device that makes a file accessible as block device. files of this kind are often used for CD, ISO images, etc. So after mounting them you can access the content.
In my blog <<What's New in CentOS 8>>, I have mentioned the commands:
# shrink or extend file to specific size # much faster than 'dd' command truncate -s 1g /tmp/loop.img # you can make partition on it or skip this step # for example, make 2 partitions 200M and 400M each fdisk /tmp/loop.img
# create loopback device from a file, will associate file with /dev/loop0 # -f: find unused loop device # -P: force kernel to scan partition table on newly create loop device losetup -fP /tmp/loop.img # now from lsblk, you will see the partitions under /dev/loop0
# format mkfs -t ext4 /dev/loop0p1 mkfs -t xfs /dev/loop0p2
# mount it mkdir /loop{0,1} mount /dev/loop0p1 /loop0 mount /dev/loop0p2 /loop1 mount | grep loop0 # unmount umount /loop0 umount /loop1
Or without losetup command:
1 2 3 4 5 6 7 8 9 10 11
truncate -s 1g /tmp/loop.img2 mkdir /loop2 # must first format the file mkfs -t xfs /tmp/loop.img2 # -o: option is loop # asociate /dev/loop1 to the file # no need losetup command mount -o loop /tmp/loop.img2 /loop2
Filesystem client based on ssh. Fuse-based in user space, not privileged. Communication securely over SSH. Using standard SSH port (you can specify other ports).
# uname -r sudo yum list installed kernel-* # see new kernel available sudo yum list available kernel
# update sudo yum update -y kernel # then reboot and check kernel version sudo reboot
The above steps usually cannot help much because the lack of latest version in official repo. We need third-party repo, see this artical for help:
How to Upgrade Linux Kernel in CentOS 7
Because we use SSH session to upgrade the kernal, so we are not able to select the kernel version on boot menu, we can do it by configuring the grub2:
1 2 3 4 5 6 7
# check kernel index list, index starts from 0 sudo awk -F\' '$1=="menuentry " {print $2}' /etc/grub2.cfg
CentOS Linux (5.4.125-1.el7.elrepo.x86_64) 7 (Core) CentOS Linux (3.10.0-1160.31.1.el7.x86_64) 7 (Core) CentOS Linux (3.10.0-1160.25.1.el7.x86_64) 7 (Core) CentOS Linux (0-rescue-adbe471f40421bfbf841690042db23fd) 7 (Core)
Switch to 5.4.125-1 version:
1 2 3 4 5
# set kernel index 0 sudo grub2-set-default 0 # reconfig boot loader code sudo grub2-mkconfig -o /boot/grub2/grub.cfg sudo reboot
After rebooting, check the kernel version:
1
uname -r
Switch back to old kernel version is easy:
1 2 3 4 5
# # set kernel index 2, see above index list sudo grub2-set-default 2 # reconfig boot loader code sudo grub2-mkconfig -o /boot/grub2/grub.cfg sudo reboot
注意kernel version 和 OS version不一样,比如查看CentOS OS version:
1 2 3
cat /etc/centos-release # or rpm -qa centos-release
Linux Logging
Linux has 2 logging systems,这 2 个logging systems can run parallelly, or you can use journal alone.
rsyslog (persistent logs, can log remotely)
journald (nonpersistent by default)
syslog vs rsyslog vs syslog-ng: Basically, they are all the same, in the way they all permit the logging of data from different types of systems in a central repository, each project trying to improve the previous one with more reliability and functionalities.
Different logs for differnet purpose, some for failed jobs, some for cron jobs, etc. The rsyslog is a daemon:
1
systemctl status rsyslog
/etc/rsyslog.conf is the configuration file, see section under #### RULES ####. For example, anything beyond mail, authpriv and cron is logged in /var/log/messages, in the below file, cron.* means messages of all priorities will be logged (debug, info, notice, warn, err, crit, alert, emerg), cron.warn will log warn and above:
# Save news errors of level crit and higher in a special file. uucp,news.crit /var/log/spooler
# Save boot messages also to boot.log local7.* /var/log/boot.log
Log rotate config is in /etc/logrotate.conf and there is a cron job for rotate /etc/cron.daily/logrotate.
If you want to log message to system log file, use logger command:
1 2 3 4
# you will see it in /var/log/messages logger "hello" # -p: priority logger -p local4.info " This is a info message from local 4"
How to search and view rsyslog, see this article:
Linux uses a set of configuration files, directories, programs, commands and daemons to create, store and recycle these log messages.
The default location for log files in Linux is /var/log.
If you check with ls -ltr -S /var/log, the lastlog file may have a big size, way bigger than the disk space, it is a sparse file.
At the heart of the logging mechanism is the rsyslog daemon. This service is responsible for listening to log messages from different parts of a Linux system and routing the message to an appropriate log file in the /var/log directory. It can also forward log messages to another Linux server.
/var/log/messages 可以用vim等工具正常查看. command who, last其实是使用了/var/run/utmp and /var/run/wtmp
journalctl have the same logs as in rsyslogd, from here, persistent journal can replace rsyslogd.
1 2 3 4 5
# persist journald by making a dir sudomkdir -p /var/log/journal sudo systemctl restart systemd-journald # you will see journal records here ls -l /var/log/journal
Or enable in /etc/systemd/journald.conf, set Storage=persistent.
You can specify date ranges:
1 2 3 4 5
journalctl --since "2020-12-11 15:44:32" # time left off is 00:00:00 midight journalctl --since "2020-10-01" --until"2020-10-03 03:00" journalctl --since yesterday journalctl --since 09:00 --until"1 hour ago"
You can use right arrow key to see full entry if it is too long.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# print all on stdout, no pager with less journalctl --no-pager
# -o output format #cat: Displays only the message field itself. #export: A binary format suitable for transferring or backing up. #json: Standard JSON with one entry per line. #json-pretty: JSON formatted for better human-readability #json-sse: JSON formatted output wrapped to make add server-sent event compatible #short: The default syslog style output #short-iso: The default format augmented to show ISO 8601 wallclock timestamps. #short-monotonic: The default format with monotonic timestamps. #short-precise: The default format with microsecond precision #verbose: Shows every journal field available for the entry, including those usually hidde #internally. journalctl -b -u nginx -o json journalctl -b -u nginx -o json-pretty
Linux Session
Other capabilities, like log management and user sessions are handled by separated daemons and management utilities (journald/journalctl and logind/loginctl respectively).
Get info about user and the processes he is running before:
1 2 3 4 5 6 7 8
# list sessions loginctl list-sessions
# session status # you can see the user action history loginctl session-status [session id] loginctl show-session [session id] loginctl kill-session [session id]
The systemd, system and service manager, is an init system used to bootstrap
the user space and to manage system processes after boot. Use systemctl
command to manage the service on a systemd enabled system.
The fundamental purpose of an init system is to initialize the components that
must be started after the Linux kernel is booted (traditionally known as
“userland” components). The init system is also used to manage services and
daemons for the server at any point while the system is running.
Main commands, for example using nginx, some commands have to use sudo if you
are non-root user, since it will affect te state of the operating system, you
can leave off the .service suffix in command.
# start on boot # This hooks it up to a certain boot “target” # causing it to be triggered when that target is started. sudo systemctl enable nginx.service sudo systemctl disable nginx.service
# when change the configuration of service # To attempt to reload the service without # interrupting normal functionality sudo systemctl reload nginx.service # if reload is not available, restart instead sudo systemctl reload-or-restart nginx.service
# status overview, you can see: # unit file path # drop-in # enabled or disabled at vendor and custom # up time # Cgroup systemctl status nginx.service
# find overridden config files for all units # check unit drop in config snippets sudo systemd-delta
enable will create a soft link into the location on disk where systemd looks
for autostart files (usually /etc/systemd/system/some_target.target.wants).
The exit code can be used for shell script:
1 2 3 4
# may need sudo systemctl is-active nginx.service systemctl is-enabled nginx.service systemctl is-failed nginx.service
Check system states managed by systemd:
1 2 3 4 5 6 7 8 9 10 11
# list enabled units only systemctl [list-units]
# list all of the units that systemd has loaded or attempted to load into memory # include not currently active systemctl list-units --all [--state=active|inactive|failed] [--type=service|target] # list failed daemons, this is useful when reboot VM but daemons not systemctl list-units --state failed
# show every available units installed on the system systemctl list-unit-files
list-unit-files state column: The state will usually be enabled, disabled,
static, or masked. In this context, static means that the unit file does
not contain an [Install] section, which is used to enable a unit. As such,
these units cannot be enabled. Usually, this means that the unit performs a
one-off action or is used only as a dependency of another unit and should not be
run by itself.
1 2 3
# after mask, cannot enable or start service sudo systemctl mask nginx.service sudo systemctl unmask nginx.service
To see full content and path of a unit file, vanilla unit files(don’t touch
them, override them if needed in other places, for example /etc/systemd/system)
are in /usr/lib/systemd/system and customized are in /etc/systemd/system
folder:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
# show unit file content and path # if has overriding snippet, will show them as well systemctl cat nginx.service
# list dependencies systemctl list-dependencies nginx.service [--all] [--reverse] [--before] [--after]
# low-level detail of unit # all key=values # -p: display a single property systemctl show nginx.service [-p ExecStart]
# edit unit file sudo systemctl edit --full nginx.service # append unit file snippet sudo systemctl edit nginx.service # then reload to pick up changes sodu systemctl daemon-reload
You can also further override by creating override.conf file in
/etc/systemd/system/xxx.service.d folder, there are some principles for
overriding, see here.
In systemd, service and other unit files can be tied to a target.
Targets are special unit files that describe a system state or synchronization
point. Like other units, the files that define targets can be identified by
their suffix, which in this case is .target. Targets do not do much by
themselves, but are instead used to group other units together.
This can be used in order to bring the system to certain states, much like other
init systems use runlevels. (仍然可以显示系统的runlevel的)
For instance, there is a swap.target that is used to indicate that swap is
ready for use. Units that are part of this process can sync with this target by
indicating in their configuration that they are WantedBy= or RequiredBy= the
swap.target. Units that require swap to be available can specify this condition
using the Wants=, Requires=, and After= specifications to indicate the nature of
their relationship.
1 2 3 4 5 6 7 8 9 10 11 12
# all available targets systemctl list-unit-files --type=target
# see what units are tied to a target systemctl list-dependencies multi-user.target
Unlike runlevels, multiple targets can be active at one time. An active target
indicates that systemd has attempted to start all of the units tied to the
target and has not tried to tear them down again.
1 2
# show all active targets systemctl list-units --type=target
This is similar to changing the runlevel in other init systems. For instance,
if you are operating in a graphical environment with graphical.target active,
you can shutdown the graphical system and put the system into a multi-user
command line state by isolating the multi-user.target. Since graphical.target
depends on multi-user.target but not the other way around, all of the graphical
units will be stopped.
1 2 3 4
# check units that will be kept alive systemctl list-dependencies multi-user.target # transition sudo systemctl isolate multi-user.target
Stopping and rebooting system, note shutdown, reboot, poweroff are
actually softlink to systemctl!
The journal is implemented with the journald daemon, which handles all of the
messages produced by the kernel, initrd, services, etc.
这里介绍了关于systemd service journal的使用,更详细的介绍Journal 可以参考我的blog
<<Linux System Admin>>.
Set time of the prompt:
1 2 3 4 5 6 7 8 9 10
# available time zone timedatectl list-timezones # set time zone sudo timedatectl set-timezone America/Los_Angeles # check timedatectl status
# or using UTC # --utc: time zone journalctl --utc
To see log of a specific service:
1 2 3 4 5 6 7 8 9 10 11 12 13
# full log journalctl -e
# -u: unit, but some log may be missing in ExecStartPre # -b: limit to current boot # -e: show ending logs # -r: show in reverse order # -f: tail log journalctl -u nginx.service [--since] [--until] [-e]
# combine related units # good for debug journalctl -u nginx.service -u php-fpm.service --since today
Here only focus on .service unit. Systemd manages a broad range of resources,
such as .target, .socket, .device, .mount, .swap, etc. They may have
different section blocks.
Section block names are case-sensitive. Non-standard section name [X-name] has
a X- prefix.
[Unit] #This is generally used for defining metadata for the unit # and configuring the relationship of the unit to other units Description= Documentation=
Requires= BindsTo=
Wants= Before= After=
Conflicts= Conditionxxx= Assertxxx=
[Service] # provide configuration that is only applicable for services Environment= Type=simple(default)|forking|oneshot|dbus|notify|idle
PIDFile=
# deprecated syntax # if true, User and Group only applied to ExecStart PermissionsStartOnly=true
[Install] # only units that can be enabled will have this section WantedBy= RequiredBy=
Alias= Also= DefaultInstance=
key=value pairs 不止这些,遇到新的可以补充。
Systemd does not use a shell to execute commands and does not perform $PATH
lookup, so for example in ExecStartPre, must specify shell context to run
command:
And because of no $PATH lookup, must use absolute path to binary or executable.
Setting PermissionsStartOnly=true means that User & Group are only applied to
ExecStart. So switching to the new syntax will be :
1 2 3 4 5 6
ExecStartPre=+/bin/bash -c '/bin/journalctl -b -u ntpdate | /bin/grep -q -e "adjust time server" -e "step time server"' ExecStartPre=+/bin/mkdir -p /path/to/somedir ExecStartPre=-/usr/bin/<command that may fail> ExecStart=/path/to/myservice ExecStop=+/bin/kill -INT ${MAINPID} ExecReload=+/bin/kill -INT ${MAINPID} && /path/to/myservice
Prefix with + will be executed with higher privilege as root. Prefix - means
if any of those commands fail, the remaining exec will not be interrupted,
otherwise the unit is considered failed.
Also note that per ExecStartPre is running in isolation.
If needs to check the log of latest daemon start process, this will show you all
log instead of only unit, I found if use -u to specify unit, some log will
miss:
1 2 3 4
# use `b` and `f` to move page # -e: tail # -x: more details journalctl -ex
这段话解释得很清楚了:
Cloud images are operating system templates and every instance starts out as an identical clone of every other instance. It is the user data that gives every cloud instance its personality and cloud-init is the tool that applies user data to your instances automatically.
To use cloud-init, need to install packages, for example in CentOS:
1 2 3 4 5 6
yum install -y cloud-init # you can see these services systemctl cat cloud-init-local systemctl cat cloud-init systemctl cat cloud-config.service systemctl cat cloud-final.service
See this IBM post for how to install cloud-init on Centos
When to use tag: every time you push to production, tag the release.
Release tag points to a specific commit in history.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
# fetch all remote tags git fetch --tags # list all tags git tag [--list] # delete tag git tag -d <tag name> # Delete a tag from the server with push tags git push --delete origin <tag name>
# in your current branch tag latest commit # create annotated tag, add more info # will open editor git tag -a 0.1.0
# tag specific commit git tag -a 0.1.0 <commit hash>
# push tag to remote git push origin <branch> --tags
这个git command很有用
1 2
## can see commit detail, tag annotation, and so on. git show [tag|or anything]
Generally you only need tags for releases.
But when you need to make changes to production release without affecting master, you need release branch. For example, make hotfix. Release branch can be updated with new commits.
Use cases for release branches:
Manual QA.
Long running releases.
On demand hot fixes.
Workflow is like
1 2 3 4 5 6 7
# we have some hot fixes on release branch, then create a release tag on it for production # finally merge into master master ------ + ----- + -----+ --- + --------------- + ------> \ / \ hotfix hotfix / \-------- + ----- + --------/ release branch (release tag)
[x] This is why we have double-commit back to master branch. 我们把release tag放在master branch中了。
You can also create release branch on the fly from release tags, the workflow:
# on master branch # checkout to a tag (detched HEAD) git checkout v1.1 # create release branch on release tag git checkout -b rb1.1
# or in one command to checkout a tag to a new branch git checkout tags/v1.1 -b rb1.1
# make fix <hot fix>
# commit git commit -m "hotfix" git tag -a v1.1.1 -m "hotfix" # merge git checkout master # or rebase/squash or fast-forward git merge rb1.1 -m "merged hotfix" # delete branch, the tag is still there, because you merged to master git branch -d rb1.1
目前使用的source code management tool 是gitlab, 除了行使git 的功能外,对每次 merge request 都做了额外的CI/CD操作,这里记录一下相关语法和总结 from course Continuous Delivery with GitLab
CI: code and feature integraion, combining updates into existing code base, testing with automation.
CD: delivery can mean deployment, the process of building and deploying the app, for example, upload the object to somewhere that customer can download.
Gitlab uses pipelines to do both CI/CD, defined in .gitlab-ci.yml file at your branch.
Tips
[x] To navigate the source code in gitlab repo, try launch the Web IDE, will show you a structure tree on left side of the files.
[x] Use snippet to share code or file block for issue solving, the same as gPaste.
[x] To-do list is someone mentions you in some events.
[x] Milestone is a goal that needs to track.
[x] Merge request (pull request in github) after merged can auto close the issue, deponds on setting.
Setup self-managed Gitlab
You can experiment with gitlab community edition locally by bringing up a gitlab server through Vagrant.
For example, Vagrantfile, there are 2 VMs, one VM for configuring docker gitlab runner:
runner.vm.provider "virtualbox"do |v| v.memory = 1024 v.cpus = 2 end end
end
Vagrant quick commands:
1 2 3 4 5 6 7
vagrant up # ssh to server vagrant ssh [gitlab] # ssh to worker vagrant ssh runner # destroy uses vagrant destroy -f
Install the packages references from there, but it uses enterprise edition, we use community edition.
1 2 3 4 5 6 7 8 9 10 11 12
# Update package manager and install prerequisites sudo apt-get update sudo apt-get install -y curl openssh-server ca-certificates # we don't need email in this case, so skip it
# Set up gitlab apt repository curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bash
# Install gitlab # this is the IP address in vagrant file # gitlab-ce is community edition sudo EXTERNAL_URL="http://192.168.50.10" apt-get install gitlab-ce
After install, go to browser and hit http://192.168.50.10, reset root password and login as root with the reseted password.
Experiment
[1] Create a new project hello world (You can also create it by setting jenkins pipeline)
Use root user to create a private project, check RAEDME added option.
[2] Create a admin user, so don’t need to use root user anymore.
Grant the new user as admin, edit the password that will be used as temporary password next time you login.
sign out and sign in again with new admin user.
[3] Setup SSH for your user
The same process as setup SSH on github, go to setting -> SSH keys.
[4] Create new project under admin user, set as priviate scope.
[4] Create anthos vagrant VM as gitlab client
To avoid messing up system git global configuration, then vagrant ssh and git clone the project.
Go to project dashboard, in the left menu:
The CI/CD tab is what we will focus on
The Operations tab is where gitlab integrate other systems in your stack, for example kubernetes.
The Settings -> CI/CD is about configuration.
CI test levels, each of them is a stage in pipeline, should fail early and fail often.
syntax and linting
unit and integration
acceptance
Gitlab runner is similar to jenkins, support run on VM, bare metal system or docker container or kubernetes.
Here we use docker, so install docker first, can reference here
Here we install docker on gitlab server VM.
[x] You can spin up another VM with 2GB, install docker and run gitlab runner container there. But make sure the VM can ping each other, just like what I did in Vagrantfile.
This docker install is on Ubuntu, Centos or other linux distro please see different way to install docker:
# later gitlab-runner is command # register is argument sudo docker exec -it gitlab-runner gitlab-runner register
# command prompt: Enter the GitLab instance URL (for example, https://gitlab.com/): # from runner expand http://192.168.50.10/ Enter the registration token: # from runner expand K5G9S5e5wmcdoANUGLF4 Enter a description for the runner: [5922b65a9261]: docker Enter tags for the runner (comma-separated): # gitlab-ci will refer this tag docker-tag Registering runner... succeeded runner=K5G9S5e5 Enter an executor: docker, docker-ssh, virtualbox, docker+machine, docker-ssh+machine, custom, parallels, shell, ssh, kubernetes: docker Enter the default Docker image (for example, ruby:2.6): # this can be overrided later alpine:latest
Then reload the gitlab runner page, you will see the registered runner is there, click runner name to see specific. This runner is locked to this project, but you can alter it (the edit icon right near runner).
Create .gitlab-ci.yml in your repo to specify the pipeline, if you create it on web IDE, you can choose a template for it, for example the bash template, more advanced syntax please see gitlab-ci doc:
--- # will override the image alpine:latest above image:busybox:latest
# global variable, used by ${CHART_NAME} variables: CHART_NAME:xxxx VERSION_NUM:xxxx
# specify order or skip some stages stages: -test -build -deploy
before_script: -echo"Before script section" -echo"For example you might run an update here or install a build dependency" -echo"Or perhaps you might print out some debugging details"
after_script: -echo"After script section" -echo"For example you might do some cleanup here"
# execute in order if no stages list build1: # tags means run on the docker runner I installed above that taged as `docker-tag` tags: -docker-tag stage:build script: -echo"Do your build here"
test1: tags: -docker-tag stage:test script: -echo"Do a test here" -echo"For example run a test suite"
test2: tags: -docker-tag stage:test script: -echo"Do another parallel test here" -echo"For example run a lint test"
deploy1: tags: -docker-tag stage:deploy script: -echo"Do your deploy here"
In the Pipeline page, CI Lint is the tool can edit and validate the .gitlab-ci yaml file syntax.
You can also use Settings -> CI/CD -> Environment variables expand to set the env variables.
[x] where is the run-dev-check.sh script hosted? it is git cloned from another repo.
这是在script 中处理string, number 数据的常用方法. 可以用来代替sed, cut这些external programs, speed up significantly. As our experience with scripting grows, the ability to effectively manipulate strings and numbers will prove extremely valuable.
Bash’s various forms of parameter expansion can also distinguish between unset and null values:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# `w` can be literal or another variable
# if a is unset, value of w is used a=${a-w} # if a is unset or null(empty), value of w is used # for example, used for positional parameter passed from outside a=${a:-w}
# if a is unset or null(empty), value of w is assigned to a # 不能用于positional parameters的赋值, 比如 ${3:=hello} ${a:=w}
# if a is unset or null(empty), w is written to stderr # and script exits with err a=${a:?w}
Expansion that return variable names:
1 2 3 4 5 6
# return variable name starts with prefix # these 2 are identical ${!prefix*} ${!prefix@} # return all BASH prefixed variables echo${!BASH*}
# remove leading space or blank # note that double [[]] wrapper! shopt -s extglob ${1##*([[:blank:]]|[[:space:]])} # remove trailing space or blank ${1%%*([[:blank:]]|[[:space:]])}
# remove .tar.gz or .tgz suffix ${1%%(.tgz|.tar.gz)}
foo=JPG.JPG # replace first match # jpg.JPG echo${foo/JPG/jpg} # replace all matches # jpg.jpg echo${foo//JPG/jpg} # replace only start # jpg.JPG echo${foo/#JPG/jpg} # replace only end # JPG.jpg echo${foo/%JPG/jpg}
A glob is a wildcard that is processed by the shell and expands into a list of arguments.
Glob is like regular expression but less expressive and eaiser to use. Glob match file names, for example ls [0-9]?file*.txt, whereas regular expression match text, for example ls | grep '[0-9].file.*\.txt'. Sometimes the funtionality can look blurred depending on how you use it. 都可以用在if condition [[ =~ ]], case condition中.
In ls [0-9]?file*.txt, ls does not support regular expression, shell expands the glob and used by ls.
grep '^A.*\.txt' *.txt, grep is using regular expression on the files context that file name is expanded by shell from glob.
Shell expansion types and execution order (precedence high to low from up to bottom):
brace expansion touch file{1..2}
tilde expansion ls ~
parameter and variable expansion ${1:1:1}, ${PATH}
command substitution $() or ``
word splitting
arithmetic expansion echo $((11 + 22))
filename expansion echo file{1..2}.*
quote removal echo "$USER"
Wildcards
1 2 3 4
ls *.txt # ? is any one char ls file?.txt ls file??.txt
ls /usr/sbin/[A-Z]* # 默认字典顺序 is actually in order # aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ # 所以[A-Z] 不含a
解决办法是用POSIX Character class(见下一节), this standards introduced a concept called a locale, which could be adjusted to select the character set needed for a particular location. We can see the language setting of our system using the following command:
1 2 3 4 5
echo$LANG # usually is en_US.UTF-8 # 所以对于上面[A-Z]*的正确写法是 ls /usr/sbin/[[:upper:]]*
# character set # match one of them ls [123abc]file.txt ls file[0-9].txt ls file[a-z9].txt
# 这个显示的结果和bash设置有关, 和上面提到的问题一样,不过这里更改了LC_COLLATE的值 # locale, In bash terminal, set LC_COLLATE=C (collation) ls file[A-G].txt # ! is inversion, not include a-z ls file[!a-z].txt # put at end to match !, it is a special char ls file[a-z!].txt ls file[az-].txt
Character classes
1 2 3 4 5
# [:upper:] is the character class # put char class in char set [] ls file[[:upper:]?].txt ls file[[:lower:]?].txt ls file[![:lower:][:space:]].txt
Others class useful:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# numbers [:digit:] # upper and lower case [:alpha:] # upper and lower and numbers [:alnum:] # upper [:upper:] # lower [:lower:] # space, tab, carriage return, newline, vertical tab, and form feed. # is superset of [:blank:] [:space:] # space and tab characters [:blank:]
Shell globbing Options
使用shopt command的设置 glob的一些特性,比如设置nullglob, extglob, etc.
shopt -s extglob, when using extended pattern matching operators. see hereshopt -s nocasematch, set bash case-insensitive match in case or [[ ]] condition.
这个是从bash tutorial 中文版中学到的, shopt is bash built-in setting, unlike set is from POSIX.
Regular Expression Info
POSIX regular expression has basic regular expression(BRE) and extended regular expression(ERE).
ERE Syntax, note that ERE has () {} ? + | expression that BRE does not:
. matches one char
[ ] character set
\ escape single char
| alternation: match to occur from among a set of expressions
( ) pattern grouping, for example, separate | with others: ^(AA|BB|CC)+
? * + { } repetition operators
^abc leading anchor
abc$ trailing anchor
[^abc] netates pattern, ^ must appear at beginning
Use ERE whenever possible!!! The support in GNU tools are:
grep -E ERE, grep [-G] default is BRE
sed -E ERE, sed default BRE
awk only supports ERE
[[ =~ ]] ERE
1 2 3 4 5 6
# match one char with zero to 3 occurances .{,3} # match one char with 3 to 7 occurances .{3,7} # match one char with 3 to more occurances .{3,}
Backreferences
A pattern stored in a buffer to be recalled later, limit of nine for example: \1 to \9:
1 2 3 4 5 6 7
# \1 is (ss) pattern (ss).*\1 (ss).*\1.*\1
# radar # opapo ^(.)(.).\2\1$
POSIX ERE does not support backreferences, GNU version supports it.
Bash Extended Regexp
Used in [[ =~ ]] in if condition, it is simple to write then extended globs but less efficiency.
BASH_REMATCH: regular expression match, the matched text is placed into array BASH_REMATCH:
1 2 3 4
[[ abcdef =~ b.d ]] # the matched is bcd in BASH_REMATCH[0] echo${BASH_REMATCH[0]} # if no match BASH_REMATCH[0] is null
${BASH_REMATCH[0]}很有用,因为存了match的内容,如果是多个group ( ) pattern的组合,则每个group一次存放在${BASH_REMATCH[n]}, n is 0/1/2/3…。
Grep EREs
Grep is global regular expression print. Stick to grep -E 'xxxx'.
grep -E -w only match a whole word.
grep -E -x only match whole line, same as using anchors.
grep -E -o only return the text that match the expression.
grep -E -q quiet mode, used to verfiy existence of search item, 用于以前没有[[ =~ ]]的时候.
Sed EREs
See my Sed blog.
Awk EREs
Only support ERE by default, see my awk dedicated blog.