RHEL 7 has quite a few changes over it's predecessor. The most significant ones are explored here. If you're looking for more of an overview, see our blog article

systemd

systemd replaces the unix system V init / upstart system initialisation framework in previous RHEL releases. It is very different.

- gone: /etc/init.d scripts, service, chkconfig and runlevels (still available for compatibility but should not be used)

Instead of init.d scripts, systemd uses “service units” (files end with .service file extension)

systemctl command is used to stop/start/restart/enable/disable system services. When working with system services the .service extension can be omitted, e.g.

 # systemctl stop bluetooth.service
 # systemctl stop bluetooth

both work

To check all services

 # systemctl list-units --type service --all (- all service whatever state)
 UNIT                                  LOAD   ACTIVE   SUB     DESCRIPTION
 auditd.service                        loaded active   running Security Auditing Service
 brandbot.service                      loaded inactive dead    Flexible Branding Service
 cpupower.service                      loaded inactive dead    Configure CPU power related settings
 crond.service                         loaded active   running Command Scheduler
 dbus.service                          loaded active   running D-Bus System Message Bus
 display-manager.service               not-found inactive dead    display-manager.service
 ....

To see just the running ones:

 # systemctl list-units --type service
 UNIT                               LOAD   ACTIVE SUB     DESCRIPTION
 auditd.service                     loaded active running Security Auditing Service
 crond.service                      loaded active running Command Scheduler
 dbus.service                       loaded active running D-Bus System Message Bus
 getty@tty1.service                 loaded active running Getty on tty1
 kdump.service                      loaded failed failed  Crash recovery kernel arming
 kmod-static-nodes.service          loaded active exited  Create list of required static device nodes for the current kernel
 lvm2-lvmetad.service               loaded active running LVM2 metadata daemon
 lvm2-monitor.service               loaded active exited  Monitoring of LVM2 mirrors, snapshots etc. using dmeventd or progress polling
 .....
LOAD   = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB    = The low-level unit activation state, values depend on unit type.
34 loaded units listed. Pass --all to see loaded but inactive units, too.
To show all installed unit files use 'systemctl list-unit-files'.

To check whether a service unit is enabled:

 # systemctl list-unit-files --type service
 UNIT FILE                                   STATE
 arp-ethers.service                          disabled
 auditd.service                              enabled
 auth-rpcgss-module.service                  static
 autovt@.service                             disabled
 blk-availability.service                    disabled
 brandbot.service                            static
 console-getty.service                       disabled
 console-shell.service                       disabled
 cpupower.service                            disabled
 crond.service                               enabled
 dbus-org.freedesktop.hostname1.service      static
 ...

(N.B. for systemd a unit is a type of object it can control of which service is one. There's quite a few other unit types as we'll see later)

You can check dependencies:

 systemctl list-dependencies

systemctl enable/disable does the job of chkconfig. e.g.

  • chkconfig sshd on - systemctl enable sshd.service
  • chkconfig sshd off - systemctl disable sshd.service
  • chkconfig –list sshd - systemctl status sshd.service

e.g.

 ip-10-51-72-53:root> #systemctl status sshd
 sshd.service - OpenSSH server daemon
 Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled)
 Active: active (running) since Thu 2016-02-18 14:05:44 GMT; 32min ago
  Main PID: 923 (sshd)
    CGroup: /system.slice/sshd.service
         +-923 /usr/sbin/sshd -D
 Feb 18 14:05:44 ip-10-51-72-53 systemd[1]: Started OpenSSH server daemon.
 Feb 18 14:05:44 ip-10-51-72-53 sshd[923]: Server listening on 0.0.0.0 port 22.
 Feb 18 14:05:44 ip-10-51-72-53 sshd[923]: Server listening on :: port 22.
 Feb 18 14:16:20 ip-10-51-72-53 sshd[2039]: Accepted password for tony from 10.44.163.107 port 36403 ssh2
 Feb 18 14:28:46 ip-10-51-72-53 sshd[2111]: Accepted password for tony from 10.44.163.107 port 37930 ssh2

This is really nice, everything you need to know about a process from one command :)

When you enable a system service so that it is restarted on a reboot, the command reads the [Install] section for the service in /usr/lib/systemd/system/xxxx.service and creates a symbolic link in the appropriate /etc/systemd/system sub-directory. e.g. looking at sshd

/usr/lib/systemd/system/sshd.service has the following [Install] section

 [Install]
 WantedBy=multi-user.target

and in /etc/systemd/system/multi-user.target.wants there's the following symbolic link

sshd.service -> /usr/lib/systemd/system/sshd.service
systemctl disable sshd - will remove the link

Targets are a group of units that can be grouped together based on dependencies and use the .target extension. Runlevels have been replaced by targets although a bunch of targets exist that correspond to the old runlevels for compatibility reasons:

   Runlevel	          Target Units	        Description
   0	runlevel0.target, poweroff.target	Shut down and power off the system.
   1	runlevel1.target, rescue.target	Set up a rescue shell.
   2	runlevel2.target, multi-user.target	Set up a non-graphical multi-user system.
   3	runlevel3.target, multi-user.target	Set up a non-graphical multi-user system.
   4	runlevel4.target, multi-user.target	Set up a non-graphical multi-user system.
   5	runlevel5.target, graphical.target	Set up a graphical multi-user system.
   6	runlevel6.target, reboot.target	Shut down and reboot the system.

systemctl get-default - gives the default target

 ip-10-51-72-53:root> #systemctl get-default
 multi-user.target

To get the currently loaded targets

To change the default target use: systemctl set-default name.target

This command replaces the symbolic link /etc/systemd/system/default.target to point to the new target in /usr/lib, e.g

 ip-10-51-72-53:root> #pwd
 /etc/systemd/system
 ip-10-51-72-53:root> #ls -l
 total 4
 drwxr-xr-x. 2 root root   30 Aug 21  2015 basic.target.wants
 lrwxrwxrwx. 1 root root   46 Aug 18  2015 dbus-org.freedesktop.NetworkManager.service -> /usr/lib/systemd/system/NetworkManager.service
 lrwxrwxrwx. 1 root root   57 Aug 18  2015 dbus-org.freedesktop.nm-dispatcher.service -> /usr/lib/systemd/system/NetworkManager-dispatcher.service
 lrwxrwxrwx. 1 root root   37 Aug 18  2015 default.target -> /lib/systemd/system/multi-user.target
 ....

If I wanted to change the target for this current session I could do the following:

 systemctl isolate graphical.target (this replaces commands like telinit 5)
ip-10-51-72-53:root> #systemctl list-units --type target
UNIT                LOAD   ACTIVE SUB    DESCRIPTION
basic.target        loaded active active Basic System
cryptsetup.target   loaded active active Encrypted Volumes
getty.target        loaded active active Login Prompts
local-fs-pre.target loaded active active Local File Systems (Pre)
local-fs.target     loaded active active Local File Systems
multi-user.target   loaded active active Multi-User System
network.target      loaded active active Network
paths.target        loaded active active Paths
remote-fs.target    loaded active active Remote File Systems
slices.target       loaded active active Slices
sockets.target      loaded active active Sockets
swap.target         loaded active active Swap
sysinit.target      loaded active active System Initialization
timers.target       loaded active active Timers
LOAD   = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB    = The low-level unit activation state, values depend on unit type.
14 loaded units listed. Pass --all to see loaded but inactive units, too.
To show all installed unit files use 'systemctl list-unit-files'.

To change to rescue mode systemctl rescue or emergency mode systemctl emergency

Power management commands are also now called via systemctl:

   Old Command	New Command	        Description
   halt	        systemctl halt	        Halts the system.
   poweroff	        systemctl poweroff	Powers off the system.
   reboot	        systemctl reboot	Restarts the system.
   pm-suspend	        systemctl suspend	Suspends the system.
   pm-hibernate	systemctl hibernate	Hibernates the system.
   pm-suspend-hybrid	systemctl hybrid-sleep	Hibernates and suspends the system.

However, the shutdown command can still be used to shutdown the server e.g. to shutdown at a certain time: shutdown –poweroff hh:mm

systemctl can also be used to interact with systemd on remote servers in the format (requires sshd running):

 systemctl --host user_name@host_name command

To create a custom startup service, vi /etc/systemd/system/name.service , where name is the name of your process (it is recommended to create custom scripts in /etc/systemd/system rather the /usr/lib/systemd)

A typical (simple) entry will look like the following:

 [Unit]
 Description=service_description
 After=network.target
 [Service]
 ExecStart=path_to_executable
 Type=forking
 PIDFile=path_to_pidfile
 [Install]
 WantedBy=default.target

Set permissions, chmod 644 /etc/systemd/system/name.service

 systemctl daemon-reload
 systemctl start name.service

NOTE: ALWAYS RUN systemctl daemon-reload AFTER CREATTING NEW UNIT FILES OR MODIFYING THEM other systemctl start/enable commands could fail due to mismatches.

See http://0pointer.de/blog/projects/systemd-for-admins-3.html for example of converting init.d script

If you need to convert an existing unit file, for minor changes create a /etc/systemd/system/unit.d/ file with the amendments or for bigger changes copy file to /etc/systemd/system and update

Configuration files in /etc/systemd/system take precedence over /usr/lib/systemd/system

To display modified unit files

 systemd-delta

Networking

With RHEL 7 the default networking service is NetworkManager. ifcfg config files are still supported but there are number of ways to interact with NetworkManager, nmtui, nmcli, control-center (gnome GUI) or nm-connection-editor. These apparently mean that you don't need to edit the config file anymore…

Some useful nmcli commands

 ip-10-51-72-53:root> #nmcli con show
 NAME         UUID                                  TYPE            DEVICE
 eth0         eaf52206-2f0c-441e-96b8-06c54f603b2f  802-3-ethernet  --
 System eth0  5fb06bd0-0bb0-7ffb-45f1-d6edd65f3e03  802-3-ethernet  eth0
 ip-10-51-72-53:root> #nmcli dev status
 #DEVICE  TYPE      STATE      CONNECTION
 eth0    ethernet  connected  System eth0
 lo      loopback  unmanaged  --

To configure an interface with nmcli

 nmcli connection add type ethernet con-name connection-name ifname interface-name ip4 address gw4 address

A new type of network bonding is now available (RHEL 6 type bond is still available) called Network Teaming. This is supposed to have a lower overhead and has a modular design that can be interacted with via an API

to convert an existing bond: /usr/bin/bond2team –master bond0

To set up a new connection, nmtui is probably the easiest option, Edit a connection > Add , select Team on the New Connection screen and follow the prompts

Journald/rsyslog

Journald is part of systemd and can be used with or instead of rsyslogd. Usually only root can see all the messages but addinng a user to the adm group will allow that user to read the logs

  • journalctl - shows the messages from all the log files in a format similiar to /var/log/messages
  • journalctl -o verbose gibes detailed metadata about each message
  • journalctl -f view in live mode (like tail -f)
  • journalctl -p priority (priority corresponds to syslog levels e.g journalctl -p err - err and above)
  • journalctl -b - messages since the last boot
  • journalctl -p warning –since=“2016-1-1 00:00:01” messages this year

By default journald only stores the latest log entries in the non persistent /run/log/journal directory. syslog reads the journal logs and stores them in /var/log directory. Persistent logging can be be enabled and then rsyslog can be replaced. To enable persistent journal retention

 mkdir -p /var/log/journal; systemctl restart systemd-journald

gnome-system-log provides a GUI to display log files

GRUB

grub2 is used in RHEL 7.

GRUB 2 has three main parts:

  • /etc/default/grub - the file containing GRUB 2 menu settings.
  • /etc/grub.d/ - the directory containing GRUB 2 menu creating scripts.
  • /boot/grub/grub.cfg - the GRUB 2 configuration file, not editable.

As mentioned, grub.cfg shouldn't be edited as it's automatically built. RedHat recommended to use grubby for configuration

  • grubby –default-kernel - lists the default kernel
  • grubby –default-index - lists the default index
  • grubby –info=ALL - list all kernel menu entries

To make changes, the format is

grubby --remove-args="argX argY" --args="argA argB" --update-kernel /boot/kernel

Scripts in /etc/grub.d examine the system and build the boot menu dependent on what is found automatically when the kernel is updated or a new one installed

NOTE: If GRUB_TIMEOUT is set to 0, no boot menu is displayed. To display it, press and hold any alphanumeric key when the BIOS info is displayed

/etc/default/grub specifies the default kernel which is usually saved:

ip-10-51-72-53:root> #cat /etc/default/grub
GRUB_TIMEOUT=30
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=vg00/lvswap rd.lvm.lv=vg00/lvroot rhgb quiet audit=1 console=ttyS0"
GRUB_DISABLE_RECOVERY="true"

This points to the entry in /boot/grub2/grubenv with the saved_entries directive. This will be the latest installed kernel. You can change this using grub2-set-default e.g.

grub2-set-default 2

This will select the third “menuentry” line in grub.cfg ( remembering the first menu entry is 0 ). You can edit /etc/default/grub with this entry to make it a permanent. This will require a rebuild of grub.cfg

grub2-mkconfig -o /boot/grub2/grub.cfg

To add custom menu entries use /etc/grub.d/40_custom and add your script in there. This is the general format of an entry

 #!/bin/sh -e
 echo "Some string"
 cat << EOF
 menuentry "Something" {
 set root=(hdX,Y)
 -- boot parameters --
 }
 EOF

If grub breaks for some reason it can be reinstalled e.g.

 grub2-install /dev/sda (assuming /boot is on /dev/sda)

To reset and reinstall (this clears all grub settings and configs)

 1. rm /etc/grub.d/*
 2. rm /etc/sysconfig/grub
 3. grub2-mkconfig -o /boot/grub2/grub.cfg
 4. grub2-install /dev/sda

NOTE: The procedure for EFI based servers is slightly different so don't forget to RTFM!!

To use the serial interface for grub boots

 grubby --remove-args="rhgb quiet" --args=console=ttyS0,115200 --update-kernel=DEFAULT

This is just for the default kernel, you can also specify –update-kernel=ALL or a comma seperated list of kerne index numbers

If a new grub.cfg file is going to get built, update /etc/default/grub

 GRUB_TERMINAL="serial"
 GRUB_SERIAL_COMMAND="serial --speed=9600 --unit=0 --word=8 --parity=no --stop=1"

(adjust the serial settings according to your hardware)

then rebuild using grub2-mkconfig command

To boot into rescue mode, at the GRUB 2 boot screen, press e (for edit) and add the following to the linux16 line

systemd.unit=rescue.target

CTRL-a goes to start of line, CTRL-e to the end. Press CTRL-x to boot with the new parameter

To go to emergency add systemd.unit=emergency.target instead. For a debug shell add systemd.debug-shell

BRTFS & XFS

BRTFS is marked as a technology preview in RHEL 7 which I suppose is a bit like the Olympic games demonstration event, you can play as hard as you like but you won't win a medal.

Uses a COW style update that prevents filesystem corruption in the event of power failures. Features include built in RAID, multiple device support and online add/removal/replacement of devices. These features can remove the requirement for LVM. It is also said to have a lower overhead than LVM.

  • mkfs.brtfs /dev/device - create a brtfs file system
  • brtfs filesystem resize amount /mount-point - increase or decrease a brtfs filesystem

if the filesystem is made up of multiple devices, you need to specify the device to be increased, e.g.

 # btrfs filesystem show /btrfstest
 Label: none  uuid: 755b41b7-7a20-4a24-abb3-45fdbed1ab39
   Total devices 4 FS bytes used 192.00KiB
 	devid    1 size 1.00GiB used 224.75MiB path /dev/vdc
 	devid    2 size 524.00MiB used 204.75MiB path /dev/vdd
 	devid    3 size 1.00GiB used 8.00MiB path /dev/vde
  devid    4 size 1.00GiB used 8.00MiB path /dev/vdf
 # btrfs filesystem resize 2:+200M /btrfstest

brtfs filesystems can be made up of multiple devices and different raid configurations,e.g. setting up a raid10 fs

 # mkfs.btrfs /dev/device1 /dev/device2 /dev/device3 /dev/device4

XFS is the new default filesystem. It can scale to 500TB and is billed as a high performance fs with quick recovery times.

Creating an xfs filesystem

 mkfs.xfs /dev/device

Increasing a filesystem

 xfs_growfs /mount-point -D size (where size in the size the filesystem will be grown to specified in blocks)

To repair a filesystem

 xfs_repair/dev/device

XFS can be frozen so that consitent snapshots can be taken

 xfs_freeze -f /mount-point (freeze)
 xfs_freeze -u /mount-point (unfreeze)

However, this isn't necessary if your using LVM snapshots as they'll do the the freezing for you. Which does beg the question as to how else you'd do a snapshot as there's no XFS utility. You can use the freeze/unfreeze with xfs_copy though (assuming you don't mind having your filesystem frozen for a while….)

There are xfsdump & xfsrestore utilities to back up and restore XFS filesystems

 xfs_fsr - defrag filesystem
 xfs_admin - change filesystem parameters (must be unmounted)

Firewalld

iptables service has been replaced by firewalld in RHEL 7. It is a dynamic firewall, i.e. rules can be added or removed without having to restart it. It does actually use iptables commands to talk to kernel netfilter packet filter.

Uses the confusing (for me with my storage background) concept of zones. These are basically a set of rules. The pre-defined zones within firewalld are:

  • drop: The lowest level of trust. All incoming connections are dropped without reply and only outgoing connections are possible.
  • block: Similar to the above, but instead of simply dropping connections, incoming requests are rejected with an icmp-host-prohibited or icmp6-adm-prohibited message.
  • public: Represents public, untrusted networks. You don't trust other computers but may allow selected incoming connections on a case-by-case basis.
  • external: External networks in the event that you are using the firewall as your gateway. It is configured for NAT masquerading so that your internal network remains private but reachable.
  • internal: The other side of the external zone, used for the internal portion of a gateway. The computers are fairly trustworthy and some additional services are available.
  • dmz: Used for computers located in a DMZ (isolated computers that will not have access to the rest of your network). Only certain incoming connections are allowed.
  • work: Used for work machines. Trust most of the computers in the network. A few more services might be allowed.
  • home: A home environment. It generally implies that you trust most of the other computers and that a few more services will be accepted.
  • trusted: Trust all of the machines in the network. The most open of the available options and should be used sparingly.

To start:

systemctl start firewalld.service
 firewall-cmd --state
 firewall-cmd --get-default-zone - find out which zone is currently selected, e.g.
 ip-10-51-72-53:root> #firewall-cmd --get-default-zone
 public

So what does “public” mean

 ip-10-51-72-53:root> #firewall-cmd --list-all
 public (default, active)
   interfaces: eth0
   sources:
   services: dhcpv6-client ssh
   ports:
   masquerade: no
   forward-ports:
   icmp-blocks:
   rich rules:

So the rule affects eth0 (the only interface on this server) and allows ssh and DHCP operations.

To find out about other available zones, to list zones:

 ip-10-51-72-53:root> #firewall-cmd --get-zones
 block dmz drop external home internal public trusted work

To find out what one does:

 ip-10-51-72-53:root> #firewall-cmd --zone=home --list-all
 home
   interfaces:
   sources:
   services: dhcpv6-client ipp-client mdns samba-client ssh
   ports:
   masquerade: no
   forward-ports:
   icmp-blocks:
   rich rules:

You could specify this zone to be the active one as follows:

 firewall-cmd --zone=home --change-interface=eth0

If you now rebooted the server, it would change back to the default zone. To make this permanent you could update ifcfg-eth0 script, e.g.

 DEVICE=eth0
 ONBOOT=yes
 BOOTPROTO=dhcp
 TYPE=Ethernet
 ZONE=home

If you want to customise a zone, you can get a list of predefined services as follows:

 ip-10-51-72-53:root> #firewall-cmd --get-services
 RH-Satellite-6 amanda-client bacula bacula-client dhcp dhcpv6 dhcpv6-client dns ftp high-availability http https imaps ipp 
 ipp-client ipsec kerberos kpasswd ldap ldaps libvirt libvirt-tls mdns mountd ms-wbt mysql nfs ntp openvpn pmcd pmproxy 
 pmwebapi pmwebapis pop3s postgresql proxy-dhcp radius rpc-bind samba samba-client smtp ssh telnet tftp tftp-client 
 transmission-client vnc-server wbem-https

(if you need more information about what these mean, check the corresponding xml file in /usr/lib/firewalld/services)

To add a serivce

 firewall-cmd --zone=public --add-service=https

if this is working as planned, make the change permanent

 sudo firewall-cmd --zone=public --permanent --add-service=https

For bespoke services that do not have a predefined service, use the port number e.g.:

 firewall-cmd --zone=public --add-port=9100/tcp

or a range

 firewall-cmd --zone=public --add-port=9100-9999/udp

if they're working OK, don't forget to make them permanent!

 firewall-cmd --zone=public --permanent --add-port=9100-9102/udp

If you want to define a service, rather than use port number (which makes it easier to remember whet they're for later), copy an existing service and edit it

 cp /usr/lib/firewalld/services/ftp.xml /etc/firewalld/services/my-bespoke-service.xml

(Note: similiar to systemd, you don't make changes in the system defined location but in a folder in /etc )

 vi /etc/firewalld/services/my-bespoke-service.xml
 <?xml version="1.0" encoding="utf-8"?>
 <service>
   <short>Bespoke</short>
   <description>A bespoke service that doesn't do anything useful at all</description>
   <port protocol="udp" port="9100"/>
   <port protocol="udp" port="9101"/>
   <port protocol="udp" port="9101"/>
 </service>

Then reload the firewall

 firewall-cmd --reload; firewall-cmd --get-services

service bespoke should now be listed. You can then add it to your zone as required.

To create a custom zone:

 firewall-cmd --permanent --new-zone=myZone

This will obviously contain nothing yet, so add some services:

 firewall-cmd --zone=myZone --add-service=ssh
 firewall-cmd --zone=myZone --add-service=http
 firewall-cmd --zone=myZone --add-service=https

Assign it to an interface

 firewall-cmd --zone=myZone --change-interface=eth0

If all is well, make the changes permanent

 firewall-cmd --zone=myZone --permanent --add-service=ssh
 firewall-cmd --zone=myZone --permanent --add-service=http
 firewall-cmd --zone=myZone --permanent --add-service=https

And edit ifcfg-eth0

 DEVICE=eth0
 ONBOOT=yes
 BOOTPROTO=dhcp
 TYPE=Ethernet
 ZONE=myZone

Restart network & firewall

 systemctl restart network
 systemctl restart firewalld

And check

 firewall-cmd --get-active-zones
 firewall-cmd --zone=myZone --list-services

Now you're all sorted, you'll probably want the firewall to restart on boot

 systemctl enable firewalld

This section (firewalld) is based on this excellent Digitalocean page: https://www.digitalocean.com/community/tutorials/how-to-set-up-a-firewall-using-firewalld-on-centos-7

Time

timedatectl can be used to show and control time zones

tony@ip-10-51-72-53 ~]$ timedatectl
    Local time: Wed 2016-03-02 12:49:41 GMT
    Universal time: Wed 2016-03-02 12:49:41 UTC
      Timezone: Europe/London (GMT, +0000)
      NTP enabled: yes
      NTP synchronized: no
      RTC in local TZ: no
      DST active: no
      Last DST change: DST ended at
                Sun 2015-10-25 01:59:59 BST
                Sun 2015-10-25 01:00:00 GMT
      Next DST change: DST begins (the clock jumps one hour forward) at
                Sun 2016-03-27 00:59:59 GMT
                Sun 2016-03-27 02:00:00 BST

NTP remains the default time synchronisation package for servers, config file is still /etc/ntp.conf

To set the date from NTP

 # systemctl stop ntpd
 # ntpdate pool.ntp.org
  5 Jul 10:36:58 ntpdate[2190]: adjust time server 95.81.173.74 offset -0.005354 sec
 # systemctl start ntpd

For mobile and virtual systems, the new chrony service is recommended. This is because chronyd can tolerate time sources being unavailable for periods of time. It is also faster than ntpd. However ntpd is fully compliant with NTP version 4 standard and has many drivers for reference clocks. the package can be installed using yum in the usual way and enabled & started using systemctl enable chronyd; systemctl start chronyd

The config file is /etc/chrony.conf.

To get information about the time reference:

 # chronyc tracking
 Reference ID    : 94.23.44.157 (merzhin.deuza.net)
 Stratum         : 3
 Ref time (UTC)  : Thu Jul  3 22:26:27 2014
 System time     : 0.000265665 seconds fast of NTP time
 Last offset     : 0.000599796 seconds
 RMS offset      : 3619.895751953 seconds
 Frequency       : 0.070 ppm slow
 Residual freq   : 0.012 ppm
 Skew            : 0.164 ppm
 Root delay      : 0.030609 seconds
 Root dispersion : 0.005556 seconds
 Update interval : 1026.9 seconds
 Leap status     : Normal
 chronyc sources -v is the equivalent of ntpq command

Also available is PTP, Precision Time Protocol. This is capable of sub microsecond accuracy (better that NTP). With PTP capable NICs and switches, PTP can account for delays in the message transfer giving much greater accuracy. To check for NIC PTP support use ethtool, e.g.

 ethtool -T eth0

If time stamping you should see either

 SOF_TIMESTAMPING_SOFTWARE 
 SOF_TIMESTAMPING_TX_SOFTWARE 
 SOF_TIMESTAMPING_RX_SOFTWARE 

For software time stamping or

 SOF_TIMESTAMPING_RAW_HARDWARE 
 SOF_TIMESTAMPING_TX_HARDWARE 
 SOF_TIMESTAMPING_RX_HARDWARE 

For hardware time stamping

Config file is /etc/ptp4l.conf To start

 systemctl start ptp4l

Docker

Not really an integral part of RHEL 7 but it is now bundled with RHEL 7. This container technology provides a kind of cut down VM that has become very popular in devops environments.

To start: systemctl enable docker && systemctl start docker

Now you can use docker commands:

  • docker version - version information
  • docker info - information about your docker installation

e.g.

 MicroServer # docker info
 Containers: 3
  Running: 0
  Paused: 0
  Stopped: 3
 Images: 56
 Server Version: 1.2.0
 Storage Driver: aufs
  Root Dir: /var/lib/docker/aufs
  Backing Filesystem: extfs
  Dirs: 64
  Dirperm1 Supported: false
 Execution Driver: native-0.2
 Logging Driver: json-file
 Plugins: 
  Volume: local
  Network: null host bridge
 Kernel Version: 3.10.0-123.9.3.el7.x86_64
 Operating System: CentOS Linux 7 (Core)
 OSType: linux
 Architecture: x86_64
 CPUs: 2
 Total Memory: 7.798 GiB
 Name: MicroServer
 ID: Y6RN:6GUX:R3VH:URX2:DUGW:NASS:XJCP:EPRZ:YCII:MKIK:7S7S:HMSJ
 WARNING: No swap limit support

The info shows that I already have 3 containers, all of which are stopped. To check what I have

 MicroServer ~ # docker images
 REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
 phpwithmysql        v2                  b5a8a29f293e        5 months ago        483.7 MB
 phpwithmysql        latest              d206203dd8ea        5 months ago        483.7 MB
 wordpress           latest              ba297914d5df        5 months ago        512.3 MB
 php                 5.6-apache          e0cd35a1d2d5        5 months ago        480.7 MB
 mysql               5.7                 93220007617a        5 months ago        321.2 MB
 hello-world         latest              0ebda6c3e276        10 months ago       910 B

Confusing, because that lists 6 images. That's because an image only becomes a container when you run it. To check my containers:

 MicroServer ~ # docker ps -a
 CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                    PORTS               NAMES
 e477da63b574        wordpress           "/entrypoint.sh apach"   5 months ago        Exited (0) 5 months ago                       docker_web_1
 a1b2a664086e        mysql:5.7           "/entrypoint.sh mysql"   5 months ago        Exited (0) 5 months ago                       docker_mysql_1
 7d4a1601f379        hello-world         "/hello"                 8 months ago        Exited (0) 8 months ago                       prickly_babbage

As can be seen, I haven't used either the containers or images for 5 months so of course I can't remember what I was doing with them. Luckily I can use docker inspect to see what's in the image, e.g.

 docker inspect phpwithmysql

This shows the latest image, the key parts of which are

 ....
 "ExposedPorts": {
              "80/tcp": {}
          },
          "Tty": false,
          "OpenStdin": false,
          "StdinOnce": false,
 "Env": [
              "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
              "PHP_INI_DIR=/usr/local/etc/php",
              "PHP_EXTRA_BUILD_DEPS=apache2-dev",
              "PHP_EXTRA_CONFIGURE_ARGS=--with-apxs2",
              "GPG_KEYS=0BD78B5F97500D450838F95DFE857D9A90D90EC1 6E4F6AB321FDC07F2C332E3AC2BF0BC433CFC8B3",
              "PHP_VERSION=5.6.13"
          ],
          "Cmd": [
              "/bin/sh",
              "-c",
              "#(nop) CMD [\"apache2-foreground\"]"
          ],
          "Image": "3adfa294d123ed1942416061687454cb69c690684189ad548d00ea97b2d197a6",
          "Volumes": null,
          "WorkingDir": "/var/www/html",
          "Entrypoint": null,
          "OnBuild": [],
          "Labels": {}

So if I launched the image as a container it will execute apache with PHP enabled using /var/www/html as the document root. Port 80 will be an open port in the container. so lets try running it:

 MicroServer docker # docker run phpwithmysql
 AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.1. Set the 'ServerName' directive globally to suppress this message
 AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.1. Set the 'ServerName' directive globally to suppress this message
 [Fri Mar 04 12:43:46.297483 2016] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.10 (Debian) PHP/5.6.13 configured -- resuming normal operations
 [Fri Mar 04 12:43:46.297701 2016] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'

Now a new network interface has appeared:

 MicroServer ~ # ifconfig -a
 docker0   Link encap:Ethernet  HWaddr 56:84:7a:fe:97:99  
        inet addr:172.17.42.1  Bcast:0.0.0.0  Mask:255.255.0.0
        inet6 addr: fe80::5484:7aff:fefe:9799/64 Scope:Link
        UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
        RX packets:8 errors:0 dropped:0 overruns:0 frame:0
        TX packets:44 errors:0 dropped:0 overruns:0 carrier:0
        collisions:0 txqueuelen:0 
        RX bytes:536 (536.0 B)  TX bytes:11611 (11.6 KB)

And if I point my browser at this IP address I see the index.html page in /var/www/html

Useful commands to check out your docker instance:

 MicroServer ~ # docker ps -l
 CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS               NAMES
 d34d4890b473        phpwithmysql        "apache2-foreground"   6 minutes ago       Up 6 minutes        80/tcp              kickass_williams
 MicroServer ~ # docker logs d34d4890b473
 AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.1. Set the 'ServerName' directive globally to suppress this message
 AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.1. Set the 'ServerName' directive globally to suppress this message
 [Fri Mar 04 12:43:46.297483 2016] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.10 (Debian) PHP/5.6.13 configured -- resuming normal operations
 [Fri Mar 04 12:43:46.297701 2016] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'
 MicroServer ~ # docker stop d34d4890b473
 d34d4890b473
 MicroServer docker # docker ps -a
 CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS               NAMES
 d34d4890b473        phpwithmysql        "apache2-foreground"     34 minutes ago      Exited (0) 4 minutes ago                        kickass_williams
 e477da63b574        wordpress           "/entrypoint.sh apach"   5 months ago        Exited (0) 5 months ago                         docker_web_1
 a1b2a664086e        mysql:5.7           "/entrypoint.sh mysql"   5 months ago        Exited (0) 5 months ago                         docker_mysql_1
 7d4a1601f379        hello-world         "/hello"                 8 months ago        Exited (0) 8 months ago                         prickly_babbage

I don't want the hello-world container any more, it was a bit boring:

 MicroServer docker # docker rm 7d4a1601f379
 7d4a1601f379
 MicroServer docker # docker ps -a
 CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS               NAMES
 d34d4890b473        phpwithmysql        "apache2-foreground"     35 minutes ago      Exited (0) 26 minutes ago                       kickass_williams
 e477da63b574        wordpress           "/entrypoint.sh apach"   5 months ago        Exited (0) 5 months ago                         docker_web_1
 a1b2a664086e        mysql:5.7           "/entrypoint.sh mysql"   5 months ago        Exited (0) 5 months ago                         docker_mysql_1
 MicroServer docker # 

Docker run a repository of prebuilt images, to search:

 MicroServer docker # docker search coreos
 NAME                                DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
 bhuisgen/docker-zabbix-coreos       Zabbix agent for CoreOS server                  9                    [OK]
 radial/coreos-pxe                   Spoke container for running dnsmasq as PXE...   6                    [OK]
 million12/linode-coreos-api         Deploy CoreOS on Linode.                        3                    [OK]
 centurylink/coreos-cli-wetty        This image provides a Wetty terminal with ...   2                    [OK]
 olalond3/coreos-bitcoind            coreos bitcoind                                 2                    [OK]
 ccll/coreos-pypy                                                                    1                    [OK]
 steigr/coreos                       CoreOS Docker Container                         1                    
 pingles/consul-coreos                                                               1                    [OK]
 shift/coreos-ubuntu-etcd                                                            1                    [OK]
 christianbladescb/newrelic-coreos   Run newrelic's sysmond in a container on C...   1                    [OK]
 allen13/coreos-ansible-toolbox      Control CoreOS boxes with ansible using a ...   1                    [OK]
 gregsymons/zookeeper-coreos                                                         1                    [OK]
 pablocouto/coreos-sshguard          sshguard for CoreOS                             1                    [OK]
 tleyden5iwx/sync-gateway-coreos                                                     1                    [OK]
 yummly/consul-coreos                Consul using etcd on CoreOS for bootstrap....   0                    [OK]
 geowa4/coreos-toolbox               Replace the default toolbox image on CoreO...   0                    [OK]
 docku/pxe-coreos                                                                    0                    [OK]
 majidaldoiongithub/coreos-nvidia    run privileged to install nvidia and cuda ...   0                    [OK]
 shift/coreos-ubuntu-confd                                                           0                    [OK]
 zumbrunnen/coreos-gce               Google Cloud SDK for CoreOS. Useful for dy...   0                    [OK]
 nizq/docker-pfq-build-coreos        A docker image for building pfq kernel mod...   0                    [OK]
 havardline/coreos                   This code is not ready for production. The...   0                    [OK]
 andrewrothstein/coreos-ipxeserver   flask based webserver for hosting artifact...   0                    [OK]
 alino/coreos-mongodb-cluster        mongo 3.0                                       0                    [OK]
 chriskite/mesos-on-coreos                                                           0                    [OK]
 MicroServer docker #

To get an image

 MicroServer docker # docker pull centos
 Using default tag: latest
 latest: Pulling from library/centos
 a3ed95caeb02: Pull complete 
 a07226856d92: Pull complete 
 Digest: sha256:1272ae53bac7bf054dd209a0b4a8629bcc39526c2a767427c7639b630a224a9e
 Status: Downloaded newer image for centos:latest
 MicroServer docker # 

To run it

 MicroServer docker # docker run -t -i centos /bin/bash
 [root@3b596dff0087 /]# ls
 anaconda-post.log  bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

I'm going to install git

 [root@3b596dff0087 /]# yum install git
 Loaded plugins: fastestmirror, ovl
 Loading mirror speeds from cached hostfile
  * base: mirror.econdc.com
  * extras: mirror.as29550.net
  * updates: mirror.ukhost4u.com
 Resolving Dependencies
 --> Running transaction check
 ---> Package git.x86_64 0:1.8.3.1-6.el7 will be installed
 --> Processing Dependency: perl-Git = 1.8.3.1-6.el7 for package: git-1.8.3.1-6.el7.x86_64
 ....
 ....
 [root@3b596dff0087 /]# ls /usr/bin/git
 /usr/bin/git
 [root@3b596dff0087 /]# 

Now I want to save my centos image + git

 MicroServer docker # docker commit -m "Added git" -a AnthonyJDavis 3b596dff0087 centos-with-git
 sha256:37533ba9a45af5a44c03aa9a2b57c0641768aade3042e8315e26c44796468bdf
 MicroServer docker # docker images
 REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
 centos-with-git     latest              37533ba9a45a        14 seconds ago      318.8 MB
 centos              latest              0f0be3675ebb        2 weeks ago         196.6 MB
 phpwithmysql        v2                  b5a8a29f293e        5 months ago        483.7 MB
 phpwithmysql        latest              d206203dd8ea        5 months ago        483.7 MB
 wordpress           latest              ba297914d5df        5 months ago        512.3 MB
 php                 5.6-apache          e0cd35a1d2d5        5 months ago        480.7 MB
 mysql               5.7                 93220007617a        5 months ago        321.2 MB
 hello-world         latest              0ebda6c3e276        10 months ago       910 B
 MicroServer docker # 

I want to change the tag

 MicroServer docker # docker tag 37533ba9a45a test
 MicroServer docker # docker images
 REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
 centos-with-git     latest              37533ba9a45a        2 minutes ago       318.8 MB
 test                latest              37533ba9a45a        2 minutes ago       318.8 MB
 centos              latest              0f0be3675ebb        2 weeks ago         196.6 MB
 phpwithmysql        v2                  b5a8a29f293e        5 months ago        483.7 MB
 phpwithmysql        latest              d206203dd8ea        5 months ago        483.7 MB
 wordpress           latest              ba297914d5df        5 months ago        512.3 MB
 php                 5.6-apache          e0cd35a1d2d5        5 months ago        480.7 MB
 mysql               5.7                 93220007617a        5 months ago        321.2 MB
 hello-world         latest              0ebda6c3e276        10 months ago       910 B

Oops, made a mistake, let's get rid of test

 MicroServer docker # docker rmi test
 Untagged: test:latest

And do it the right way

 MicroServer docker # docker tag 37533ba9a45a centos-with-git:test
 MicroServer docker # docker images
 REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
 centos-with-git     latest              37533ba9a45a        3 minutes ago       318.8 MB
 centos-with-git     test                37533ba9a45a        3 minutes ago       318.8 MB
 centos              latest              0f0be3675ebb        2 weeks ago         196.6 MB
 phpwithmysql        v2                  b5a8a29f293e        5 months ago        483.7 MB
 phpwithmysql        latest              d206203dd8ea        5 months ago        483.7 MB
 wordpress           latest              ba297914d5df        5 months ago        512.3 MB
 php                 5.6-apache          e0cd35a1d2d5        5 months ago        480.7 MB
 mysql               5.7                 93220007617a        5 months ago        321.2 MB
 hello-world         latest              0ebda6c3e276        10 months ago       910 B

And that's the highlights of the new features of RHEL7 . There are lots of other minor changes of course but I think this covers the main features. As already mentioned, an overview is available in our blog

Recent Changes