1

I have a very specific script I wrote about two years ago. It runs fantastically and has never failed ... However the more websites we put into the system the script gets slower and slower (as expected) ... The cron that runs the script every 10 mins now takes about 3 minutes to complete. I am looking to reduce that time. So let me explain what the script does.

1) Here is the MAIN LOOP -- It checks the database for all websites for paying customers:

mysql --login-path=main-data -e "SELECT user_file FROM database" | while read user_file; do

2) Inside the main loop I build the individual conf files :

p=$user_file
echo "<VirtualHost *:80>" > /etc/apache2/sites-available/$p.conf
.........

3) Still inside the main loop, I check to see if they have extra domains and create their aliases :

mysql --login-path=main-data -e "SELECT domain 
    FROM database 
    WHERE user_file = '$user_file';" | while read domain; do
        echo "  ServerAlias $domain" >> /etc/apache2/sites-available/$p.conf
        echo "  ServerAlias www.$domain" >> /etc/apache2/sites-available/$p.conf
    done

4) Still inside the main loop -- I check for SSL and create that part of the conf file. NOTE that there's some openssl config tests here that may slow things down:

mysql --login-path=main-data --skip-column-names -e"SELECT ssl FROM database
    WHERE user_file = '$user_file' 
    AND a.primary_domain = '1' 
    AND b.https = '1'" | while read ssl; do


    if [ $ssl = 1 ]
    then
        ########################### START SSL TEST
        crt="/var/www/liveSites/websites/$user_file/ssl/$domain.crt"
        key="/var/www/liveSites/websites/$user_file/ssl/$domain.key"

       key_test=$(openssl x509 -in $crt -pubkey -noout -outform pem | sha256sum 2>&1)
       crt_test=$(openssl pkey -in $key -pubout -outform pem | sha256sum 2>&1)

       if [ "$key_test" = "$crt_test" ]
       then
           echo "\n - Matched -- Cert Good - \n";
       else
           echo "SSL match failed for $user_file -> $domain" > /etc/apache2/websitesCron/ssl_fail.txt
           cat /etc/apache2/websitesCron/ssl_fail.txt | mail -s "SSL INSTALLATION ERROR" it@mycompany.com
        fi
        ####################### END SSL TEST

       echo "<VirtualHost *:443>
             ServerName    $domain
             ServerAlias   www.$domain
             DocumentRoot /var/www/liveSites/websites/$user_file/public_html
             ........
    fi
done

5) Finally, still inside main loop I check to see if the site is enabled, if not enable it

if test -f "/etc/apache2/sites-enabled/$p.conf"; then
    echo "Configuration exists \n"
else
    A2ENSITE=/usr/sbin/a2ensite
    ${A2ENSITE} $p.conf
fi

After the main loop -- I check the Apache config, to make sure it will reload gracefully and I check that ssl didn't fail the openssl comparison check:

/usr/sbin/apachectl configtest > /etc/apache2/websitesCron/configtest 2>&1

if grep "failed" /etc/apache2/websitesCron/configtest
then
    # Do failed stuff stop script and turn cron off
elif grep "failed" /etc/apache2/websitesCron/ssl_fail.txt
then
    # Do failed SSL stuff stop script and turn cron off
else
    # gracefully reload Apache
    /etc/init.d/apache2 reload
fi

It's a pretty simple script, but there's a few moving parts -- For 1,000+ websites, does it seem like 3 mins for it to run is reasonable? Should I fork? Is there logic changes that could "clean it up"?

Zak
  • 6,976
  • 2
  • 26
  • 48
  • Is it necessary to regenerate all config files every time? It seems to me you should make it check for changes and only regenerate files for sites that've actually changed. – Gordon Davisson Jun 02 '20 at 19:44
  • Try to figure ways not to run the same commands over and over inside the loop. Can you run one larger script of SQL and have it write all those outputs to a file per? Maybe something similar for the mail? – Paul Hodges Jun 02 '20 at 19:57
  • Rather than mess about with 1,000 jobs and waiting for them, just use **GNU Parallel** https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwixmOOphuTpAhWBQRUIHY8hDesQFjACegQIAhAB&url=https%3A%2F%2Fzenodo.org%2Frecord%2F1146014%2Ffiles%2FGNU_Parallel_2018.pdf%3Fdownload%3D1&usg=AOvVaw35x0WxcO2IE5NhKvyL9JXP – Mark Setchell Jun 02 '20 at 21:14
  • 1
    Make a copy of the file, add `echo "$SECONDS some comment"` on a lot of places and make a timeline which part takes a lot of time. – Walter A Jun 02 '20 at 21:25

2 Answers2

1

I would not bother changing the logic if its working for you. But you can make these changes to run it in parallel:

  • Move the entire main loop into a Bash function that takes one argument user_file.
  • Change the main loop to invoke this function in the background.

Your outer loop would look something like this:

mysql --login-path=main-data -e "SELECT user_file FROM database" | while read user_file; do
    mainFunction "$user_file" &
done

Note: If the 1000+ background processes swamp your machine, then consider applying some of these techniques to limit the number of concurrent jobs.

Eric Bolinger
  • 2,722
  • 1
  • 13
  • 22
  • This is definitely worth a look, Thank you .. It's a pretty stout server, so I don't think 1000+ concurrent jobs wil harm it every 10 mins .. I am going to experiment with this .. – Zak Jun 02 '20 at 18:51
1

Avoid long tests you have done before. You can look for different ways, something like

sha_dir=/var/shadir
crt="/var/www/liveSites/websites/$user_file/ssl/$domain.crt"
key="/var/www/liveSites/websites/$user_file/ssl/$domain.key"
sha_crt="${sha_dir}/${user_file}_${domain}.crt"
sha_key="${sha_dir}/${user_file}_${domain}.key"
if [[ "${crt}" -nt "${sha_crt}" ]]; then
   crt_test=$(openssl pkey -in $key -pubout -outform pem | sha256sum 2>&1 | tee "${sha_crt}")
else
   crt_test=$(cat ${sha_crt})
fi
if [[ "${key}" -nt "${sha_key}" ]]; then
   key_test=$(openssl x509 -in $crt -pubkey -noout -outform pem | sha256sum 2>&1 | tee "${sha_key}")
else
   key_test=$(cat ${sha_key})
fi
Walter A
  • 19,067
  • 2
  • 23
  • 43