Category: Varnish Part 1
Mastering Podman: A Comprehensive Guide with Detailed Command Examples
Mastering Podman on Ubuntu: A Comprehensive Guide with Detailed Command Examples
Podman has become a popular alternative to Docker due to its flexibility, security, and rootless operation capabilities. This guide will walk you through the installation process and various advanced usage scenarios of Podman on Ubuntu, providing detailed examples for each command.
Table of Contents
1. How to Install Podman
To get started with Podman on Ubuntu, follow these steps:
Update Package Index
Before installing any new software, it’s a good idea to update your package index to ensure you’re getting the latest version of Podman:
sudo apt update
Install Podman
With your package index updated, you can now install Podman. This command will download and install Podman and any necessary dependencies:
sudo apt install podman -y
Example Output:
kotlin
Reading package lists… Done
Building dependency tree
Reading state information… Done
The following additional packages will be installed:
…
After this operation, X MB of additional disk space will be used.
Do you want to continue? [Y/n] y
…
Setting up podman (4.0.2) …
Verifying Installation
After installation, verify that Podman is installed correctly:
podman –version
Example Output:
podman version 4.0.2
2. How to Search for Images
Before running a container, you may need to find an appropriate image. Podman allows you to search for images in various registries.
Search Docker Hub
To search for images on Docker Hub:
podman search ubuntu
Example Output:
lua
INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED
docker.io docker.io/library/ubuntu Ubuntu is a Debian-based Linux operating sys… 12329 [OK]
docker.io docker.io/ubuntu-upstart Upstart is an event-based replacement for the … 108 [OK]
docker.io docker.io/tutum/ubuntu Ubuntu image with SSH access. For the root p… 39
docker.io docker.io/ansible/ubuntu14.04-ansible Ubuntu 14.04 LTS with ansible 9 [OK]
This command will return a list of Ubuntu images available in Docker Hub.
3. How to Run Rootless Containers
One of the key features of Podman is the ability to run containers without needing root privileges, enhancing security.
Running a Rootless Container
As a non-root user, you can run a container like this:
podman run –rm -it ubuntu
Example Output:
ruby
root@d2f56a8d1234:/#
This command runs an Ubuntu container in an interactive shell, without requiring root access on the host system.
Configuring Rootless Environment
Ensure your user is added to the subuid and subgid files for proper UID/GID mapping:
echo “$USER:100000:65536” | sudo tee -a /etc/subuid /etc/subgid
Example Output:
makefile
user:100000:65536
user:100000:65536
4. How to Search for Containers
Once you start using containers, you may need to find specific ones.
Listing All Containers
To list all containers (both running and stopped):
podman ps -a
Example Output:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d13c5bcf30fd docker.io/library/ubuntu:latest 3 minutes ago Exited (0) 2 minutes ago confident_mayer
Filtering Containers
You can filter containers by their status, names, or other attributes. For instance, to find running containers:
podman ps –filter status=running
Example Output:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
No output indicates there are no running containers at the moment.
5. How to Add Ping to Containers
Some minimal Ubuntu images don’t come with ping installed. Here’s how to add it.
Installing Ping in an Ubuntu Container
First, start an Ubuntu container:
podman run -it –cap-add=CAP_NET_RAW ubuntu
Inside the container, install ping (part of the iputils-ping package):
apt update
apt install iputils-ping
Example Output:
mathematica
Get:1 http://archive.ubuntu.com/ubuntu focal InRelease [265 kB]
…
Setting up iputils-ping (3:20190709-3) …
Now you can use ping within the container.
6. How to Expose Ports
Exposing ports is crucial for running services that need to be accessible from outside the container.
Exposing a Port
To expose a port, use the -p flag with the podman run command:
podman run -d -p 8080:80 ubuntu -c “apt update && apt install -y nginx && nginx -g ‘daemon off;'”
Example Output:
54c11dff6a8d9b6f896028f2857c6d74bda60f61ff178165e041e5e2cb0c51c8
This command runs an Ubuntu container, installs Nginx, and exposes port 80 in the container as port 8080 on the host.
Exposing Multiple Ports
You can expose multiple ports by specifying additional -p flags:
podman run -d -p 8080:80 -p 443:443 ubuntu -c “apt update && apt install -y nginx && nginx -g ‘daemon off;'”
Example Output:
wasm
b67f7d89253a4e8f0b5f64dcb9f2f1d542973fbbce73e7cdd6729b35e0d1125c
7. How to Create a Network
Creating a custom network allows you to isolate containers and manage their communication.
Creating a Network
To create a new network:
podman network create mynetwork
Example Output:
mynetwork
This command creates a new network named mynetwork.
Running a Container on a Custom Network
podman run -d –network mynetwork ubuntu -c “apt update && apt install -y nginx && nginx -g ‘daemon off;'”
Example Output:
1e0d2fdb110c8e3b6f2f4f5462d1c9b99e9c47db2b16da6b2de1e4d9275c2a50
This container will now communicate with others on the mynetwork network.
8. How to Connect a Network Between Pods
Podman allows you to manage pods, which are groups of containers sharing the same network namespace.
Creating a Pod and Adding Containers
podman pod create mypod
podman run -dt –pod mypod ubuntu -c “apt update && apt install -y nginx && nginx -g ‘daemon off;'”
podman run -dt –pod mypod ubuntu -c “apt update && apt install -y redis-server && redis-server”
Example Output:
f04d1c28b030f24f3f7b91f9f68d07fe1e6a2d81caeb60c356c64b3f7f7412c7
8cf540eb8e1b0566c65886c684017d5367f2a167d82d7b3b8c3496cbd763d447
4f3402b31e20a07f545dbf69cb4e1f61290591df124bdaf736de64bc3d40d4b1
Both containers now share the same network namespace and can communicate over the mypod network.
Connecting Pods to a Network
To connect a pod to an existing network:
podman pod create –network mynetwork mypod
Example Output:
f04d1c28b030f24f3f7b91f9f68d07fe1e6a2d81caeb60c356c64b3f7f7412c7
This pod will use the mynetwork network, allowing communication with other containers on that network.
9. How to Inspect a Network
Inspecting a network provides detailed information about the network configuration and connected containers.
Inspecting a Network
Use the podman network inspect command:
podman network inspect mynetwork
Example Output:
json
[
{
“name”: “mynetwork”,
“id”: “3c0d6e2eaf3c4f3b98a71c86f7b35d10b9d4f7b749b929a6d758b3f76cd1f8c6”,
“driver”: “bridge”,
“network_interface”: “cni-podman0”,
“created”: “2024-08-12T08:45:24.903716327Z”,
“subnets”: [
{
“subnet”: “10.88.1.0/24”,
“gateway”: “10.88.1.1”
}
],
“ipv6_enabled”: false,
“internal”: false,
“dns_enabled”: true,
“network_dns_servers”: [
“8.8.8.8”
]
}
]
This command will display detailed JSON output, including network interfaces, IP ranges, and connected containers.
10. How to Add a Static Address
Assigning a static IP address can be necessary for consistent network configurations.
Assigning a Static IP
When running a container, you can assign it a static IP address within a custom network:
podman run -d –network mynetwork –ip 10.88.1.100 ubuntu -c “apt update && apt install -y nginx && nginx -g ‘daemon off;'”
Example Output:
f05c2f18e41b4ef3a76a7b2349db20c10d9f2ff09f8c676eb08e9dc92f87c216
Ensure that the IP address is within the subnet range of your custom network.
11. How to Log On to a Container with
Accessing a container’s shell is often necessary for debugging or managing running applications.
Starting a Container with
If the container image includes , you can start it directly:
podman run -it ubuntu
Example Output:
ruby
root@e87b469f2e45:/#
Accessing a Running Container
To access an already running container:
podman exec -it <container_id>
Replace <container_id> with the actual ID or name of the container.
Example Output:
ruby
root@d2f56a8d1234:/#
How to setup a NFS server on Debian
DEBIAN SETUP
Make sure you have NFS server support in your server’s kernel (kernel module named “knfsd.ko” under your /lib/modules/uname -r/ directory structure)
$ grep NFSD /boot/config-`uname -r`
or similar (wherever you’ve stashed your config file, for example, perhaps in /usr/src/linux/.config.)
There are at ltwo mainstream NFS server implementations that people use (excluding those implemented in Python and similar): one implemented in user space, which is slower however easier to debug, and the other implemented in kernel space, which is faster. Below shows the setup of the kernel-space one. If you wish to use the user-space server, then install the similarly-named package.
First, the packages to begin with:
- $ aptitude install nfs-kernel-server portmap
Note that portmap defaults to only listening for NFS connection attempts on 127.0.0.1 (localhost), so if you wish to allow connections on your local network, then you need to edit /etc/default/portmap, to comment out the “OPTIONS” line. Also, we need to ensure that the /etc/hosts.allow file allows connections to the portmap port. For example:
2. Now run the following commands. This will edit the portmap configuration file and all
the subnet in your hosts.allow for which ever subnet is nfs server is on
- $ perl -pi -e ‘s/^OPTIONS/#OPTIONS/’ /etc/default/portmap
- $ echo “portmap: 192.168.1.” >> /etc/hosts.allow
- $ /etc/init.d/portmap restart
- $ echo “rpcbind: ALL” >> /etc/hosts.allow
See ‘man hosts.allow’ for examples on the syntax. But in general, specifying only part of the IP address like this (leaving the trailing period) treats the specified IP address fragment as a wildcard, allowing all IP addresses in the range 192.168.1.0 to 192.168.1.255 (in this example.) You can do more “wildcarding” using DNS names, and so on too.
- Then, edit the /etc/exports file, which lists the server’s filesystems to export over NFS to client machines. The following example shows the addition of a line which adds the path “/example”, for access by any machine on the local network (here 192.168.1.*).
- $ echo “/example 192.168.1.0/255.255.255.0(rw,no_root_squash,subtree_check)” >> /etc/exports
- $ /etc/init.d/nfs-kernel-server reload
This tells the server to serve up that path, readable/writable, with root-user-id connecting clients to use root access instead of being mapped to ‘nobody’, and to use the ‘subtree_check’ to silence a warning message. Then, reloads the server.
6. On the Client server you wish to mount to the NFS share type the following
- $ mount 192.168.1.100:/example /mnt/example
Result should look like this if you type
- $mount <enter>
/dev/sda3 on / type ext4 (rw,errors=remount-ro)
tmpfs on /lib/init/rw type tmpfs (rw,nosuid,mode=0755)
proc on /proc type proc (rw,noexec,nosuid,nodev)
sysfs on /sys type sysfs (rw,noexec,nosuid,nodev)
udev on /dev type tmpfs (rw,mode=0755)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev)
devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=620)
/dev/sda1 on /tmp type ext4 (rw)
rpc_pipefs on /var/lib/nfs/rpc_pipefs type rpc_pipefs (rw)
nfsd on /proc/fs/nfsd type nfsd (rw)
192.168.1.100:/nicktest on /mnt/nfs type nfs (rw,nolock,addr=192.168.1.100)
Understanding how Varnish works (Part 1)
I put this post together because you kind of need to understand these things before you try and setup varnish, otherwise you will be trial and error like I was which took a bit a longer. If I had known these things it would of helped.
Varnish 3.0 How it works
I am writing this blog post because when I setup Varnish is very painful to learn, because varnish does not work out of the box. It needs to be configured to even start on redhat. Although there are some great posts out there on how to setup, they all fail to mention key details that every newb wants to know and ends up digging all over the net to find. So I have decided to save everyone the trouble and I’m writing it from beginning to end with descriptions and why and how it all works.
Understanding The Architecture and process model
Varnish has two main processes: the management process and the child process. The management process apply configuration changes (VCL and parameters), compile VCL, monitor Varnish, initialize Varnish and provides a command line interface, accessible either directly on the terminal or through a management interface.
The management process polls the child process every few seconds to see if it’s still there. If it doesn’t get a reply within a reasonable time, the management process will kill the child and start it back up again. The same happens if the child unexpectedly exits, for example from a segmentation fault or assert error.
This ensures that even if Varnish does contain a critical bug, it will start back up again fast. Usually within a few seconds, depending on the conditions.
The child process
The child process consist of several different types of threads, including, but not limited to:
Acceptor thread to accept new connections and delegate them. Worker threads – one per session. It’s common to use hundreds of worker threads. Expiry thread, to evict old content from the cache Varnish uses workspaces to reduce the contention between each thread when they need to acquire or modify memory. There are multiple workspaces, but the most important one is the session workspace, which is used to manipulate session data. An example is changing www.example.com to example.com before it is entered into the cache, to reduce the number of duplicates.
It is important to remember that even if you have 5MB of session workspace and are using 1000 threads, the actual memory usage is not 5GB. The virtual memory usage will indeed be 5GB, but unless you actually use the memory, this is not a problem. Your memory controller and operating system will keep track of what you actually use.
To communicate with the rest of the system, the child process uses a shared memory log accessible from the file system. This means that if a thread needs to log something, all it has to do is grab a lock, write to a memory area and then free the lock. In addition to that, each worker thread has a cache for log data to reduce lock contention.
The log file is usually about 90MB, and split in two. The first part is counters, the second part is request data. To view the actual data, a number of tools exist that parses the shared memory log. Because the log-data is not meant to be written to disk in its raw form, Varnish can afford to be very verbose. You then use one of the log-parsing tools to extract the piece of information you want – either to store it permanently or to monitor Varnish in real-time.
All of this is logged to syslog. This makes it crucially important to monitor the syslog, otherwise you may never even know unless you look for them, because the perceived downtime is so short.
VCL compilation
Configuring the caching policies of Varnish is done in the Varnish Configuration Language (VCL). Your VCL is then interpreted by the management process into to C and then compiled by a normal C compiler – typically gcc. Lastly, it is linked into the running Varnish instance.
As a result of this, changing configuration while Varnish is running is very cheap. Varnish may want to keep the old configuration around for a bit in case it still has references to it, but the policies of the new VCL takes effect immediately.
Because the compilation is done outside of the child process, there is no risk of affecting the running Varnish by accidentally loading an ill-formated VCL.
A compiled VCL file is kept around until you restart Varnish completely, or until you issue vcl.discard from the management interface. You can only discard compiled VCL files after all references to them are gone, and the amount of references left is part of the output of vcl.list.
Storage backends
Varnish supports different methods of allocating space for the cache, and you choose which one you want with the -s argument.
file
malloc
persistent (experimental)
Rule of thumb: malloc if it fits in memory, file if it doesn’t
Expect around 1kB of overhead per object cached
They approach the same basic problem from two different angles. With the malloc-method, Varnish will request the entire size of the cache with a malloc() (memory allocation) library call. The operating system divides the cache between memory and disk by swapping out what it can’t fit in memory.
The alternative is to use the file storage backend, which instead creates a file on a filesystem to contain the entire cache, then tell the operating system through the mmap() (memory map) system call to map the entire file into memory if possible.
The file storage method does not retain data when you stop or restart Varnish! This is what persistent storage is for. When -s file is used, Varnish does not keep track of what is written to disk and what is not. As a result, it’s impossible to know whether the cache on disk can be used or not — it’s just random data. Varnish will not (and can not) re-use old cache if you use -s file.
While malloc will use swap to store data to disk, file will use memory to cache the data instead. Varnish allow you to choose between the two because the performance of the two approaches have varied historically.
The persistent storage backend is similar to file, but experimental. It does not yet gracefully handle situations where you run out of space. We only recommend using persistent if you have a large amount of data that you must cache and are prepared to work with us to track down bugs.
Tunable parameters
In the CLI:
param.show -l
Varnish has many different parameters which can be adjusted to make Varnish act better under specific workloads or with specific software and hardware setups. They can all be viewed with param.show in the management interface and set with the -p option passed to Varnish – or directly in the management interface.
Remember that changes made in the management interface are not stored anywhere, so unless you store your changes in a startup script, they will be lost when Varnish restarts.
The general advice with regards to parameters is to keep it simple. Most of the defaults are very good, and even though they might give a small boost to performance, it’s generally better to use safe defaults if you don’t have a very specific need.
A few hidden commands exist in the CLI, which can be revealed with help -d. These are meant exclusively for development or testing, and many of them are downright dangerous. They are hidden for a reason, and the only exception is perhaps debug.health, which is somewhat common to use.
The shared memory log
Varnish’ shared memory log is used to log most data. It’s sometimes called a shm-log, and operates on a round-robin capacity.
There’s not much you have to do with the shared memory log, except ensure that it does not cause I/O. This is easily accomplished by putting it on a tmpfs.
This is typically done in ‘/etc/fstab’, and the shmlog is normally kept in ‘/var/lib/varnish’ or equivalent locations. All the content in that directory is safe to delete.
The shared memory log is not persistent, so do not expect it to contain any real history.
The typical size of the shared memory log is 80MB. If you want to see old log entries, not just real-time, you can use the -d argument for varnishlog: varnishlog -d.
Warning: Some packages will use -s file by default with a path that puts the storage file in the same directory as the shmlog. You want to avoid this.
Threading model
The child process runs multiple threads
Worker threads are the bread and butter of the Varnish architecture
Utility-threads
Balance
The child process of Varnish is where the magic takes place. It consists of several distinct threads performing different tasks. The following table lists some interesting threads, to give you an idea of what goes on. The table is not complete.Thread-name Amount of threads Task
cache-worker One per active connection Handle requests
cache-main One Startup
ban lurker One Clean bans
acceptor One Accept new connections
epoll/kqueue Configurable, default: 2 Manage thread pools
expire One Remove old content
backend poll One per backend poll Health checks
Most of the time, we only deal with the cache-worker threads when configuring Varnish. With the exception of the amount of thread pools, all the other threads are not configurable.
For tuning Varnish, you need to think about your expected traffic. The thread model allows you to use multiple thread pools, but time and experience has shown that as long as you have 2 thread pools, adding more will not increase performance.
The most important thread setting is the number of worker threads.
Note: If you run across tuning advice that suggests running one thread pool for each CPU core, res assured that this is old advice. Experiments and data from production environments have revealed that as long as you have two thread pools (which is the default), there is nothing to gain by increasing the number of thread pools.
Threading parameters
Thread pools can safely be ignored
Maximum: Roughly 5000 (total)
Start them sooner rather than later
Maximum and minimum values are per thread pool
Details of threading parameters
While most parameters can be left to the defaults, the exception is the number of threads.Varnish will use one thread for each session and the number of threads you let Varnish use is directly proportional to how many requests Varnish can serve concurrently.The available parameters directly related to threads are:Parameter
Default value
thread_pool_add_delay 2 [milliseconds]
thread_pool_add_threshold 2 [requests]
thread_pool_fail_delay 200 [milliseconds]
thread_pool_max 500 [threads]
thread_pool_min 5 [threads]
thread_pool_purge_delay 1000 [milliseconds]
thread_pool_stack 65536 [bytes]
thread_pool_timeout 300 [seconds]
thread_pools 2 [pools]
thread_stats_rate 10 [requests]
Among these, thread_pool_min and thread_pool_max are most important. The thread_pools parameter is also of some importance, but mainly because it is used to calculate the final number of threads.
Varnish operates with multiple pools of threads. When a connection is accepted, the connection is delegated to one of these thread pools. The thread pool will further delegate the connection to available thread if one is available, put the connection on a queue if there are no available threads or drop the connection if the queue is full. By default, Varnish uses 2 thread pools, and this has proven sufficient for even the most busy Varnish server.
For the sake of keeping things simple, the current best practice is to leave thread_pools at the default 2 [pools].
Number of threads
Varnish has the ability to spawn new worker threads on demand, and remove them once the load is reduced. This is mainly intended for traffic spikes. It’s a better approach to try to always keep a few threads idle during regular traffic than it is to run on a minimum amount of threads and constantly spawn and destroy threads as demand changes. As long as you are on a 64-bit system, the cost of running a few hundred threads extra is very limited.
The thread_pool_min parameter defines how many threads will be running for each thread pool even when there is no load. thread_pool_max defines the maximum amount of threads that will be used per thread pool.
The defaults of a minimum of 5 [threads] and maximum 500 [threads] threads per thread pool and 2 [pools] will result in:
At any given time, at least 5 [threads] * 2 [pools] worker threads will be running
No more than 500 [threads] * 2 [pools] threads will run.
We rarely recommend running with more than 5000 threads. If you seem to need more than 5000 threads, it’s very likely that there is something not quite right about your setup, and you should investigate elsewhere before you increase the maximum value.
For minimum, it’s common to operate with 500 to 1000 threads minimum (total). You can observe if this is enough through varnishstat, by looking at the N queued work requests (n_wrk_queued) counter over time. It should be fairly static after startup.
Timing thread growth
Varnish can use several thousand threads, and has had this capability from the very beginning. Not all operating system kernels were prepared to deal with this, though, so the parameter thread_pool_add_delay was added which ensures that there is a small delay between each thread that spawns. As operating systems have matured, this has become less important and the default value of thread_pool_add_delay has been reduced dramatically, from 20ms to 2ms.
There are a few, less important parameters related to thread timing. The thread_pool_timeout is how long a thread is kept around when there is no work for it before it is removed. This only applies if you have more threads than the minimum, and is rarely changed.
An other is the thread_pool_fail_delay, which defines how long to wait after the operating system denied us a new thread before we try again.
System parameters
As Varnish has matured, fewer and fewer parameters require tuning. The sess_workspace is one of the parameters that could still pose a problem.
sess_workspace – incoming HTTP header workspace (from client)
Common values range from the default of 16384 [bytes] to 10MB
ESI typically requires exponential growth Remember: It’s all virtual – not physical memory.
Workspaces are some of the things you can change with parameters. The session workspace is how much memory is allocated to each HTTP session for tasks like string manipulation of incoming headers. It is also used to modify the object returned from a web server before the precise size is allocated and the object is stored read-only.
Some times you may have to increase the session workspace to avoid running out of workspace.
As most of the parameters can be left unchanged, we will not go through all of them, but take a look at the list param.show gives you to get an impression of what they can do.
TimersParameter Default Description Scope
connect_timeout 0.700000 [s] OS/network latency Backend
first_byte_timeout 60.000000 [s] Page generation? Backend
between_bytes_timeout 60.000000 [s] Hiccoughs? Backend
send_timeout 60 [seconds] Client-in-tunnel Client
sess_timeout 5 [seconds] keep-alive timeout Client
cli_timeout 10 [seconds] Management thread->child Management
The timeout-parameters are generally set to pretty good defaults, but you might have to adjust them for strange applications. The connection timeout is tuned for a geographically close web server, and might have to be increased if your Varnish server and web server are not close.
Keep in mind that the session timeout affects how long sessions are kept around, which in turn affects file descriptors left open. It is not wise to increase the session timeout without taking this into consideration.
The cli_timeout is how long the management thread waits for the worker thread to reply before it assumes it is dead, kills it and starts it back up. The default value seems to do the trick for most users today.
Now that you have read this you can go read My
Varnish Configuration for Drupal in HA on Redhat