Devops

  • Remove git submodule created by mistake

    Make sure that the sub-directory/sub-module doesn't have .git directory. If it does, delete the .git directory inside the sub-directory/sub-module rm -rf [sub-module]/.git   Clear the git cache git rm --cached [sub-module]  
    0Read more
  • Apache Reverse Proxy to Docker Nginx with SSL (Let's Encrypt)

    Docker Compose and Nginx Configuration docker-compose.yml ... nginx: image: wodby/nginx:$NGINX_TAG container_name: "${PROJECT_NAME}_nginx" depends_on: - php environment: NGINX_STATIC_OPEN_FILE_CACHE: "off" NGINX_ERROR_LOG_LEVEL: debug NGINX_BACKEND_HOST: php NGINX_SERVER_ROOT: /var/www/html/web NGINX_VHOST_PRESET: $NGINX_VHOST_PRESET volumes: - ./:/var/www/html:cached - /etc/letsencrypt/:/etc/letsencrypt/ - ./assets/nginx-ssl-vhost.conf:/etc/nginx/conf.d/nginx-ssl-vhost.conf ports: - 8001:80 - 8444:443 labels: - 'traefik.backend=${PROJECT_NAME}_nginx' - 'traefik.frontend.rule=HostRegexp:{subdomain:[a-z]+}.${PROJECT_BASE_URL}' ... The volume /etc/letsencrypt/:/etc/letsencrypt/ is to copy the whole Let's Encrypt keys. The volume ./assets/nginx-ssl-vhost.conf:/etc/nginx/conf.d/nginx-ssl-vhost.conf is to copy the custom nginx ssl virtual host. The port 8444:443 is to use port 8443 as a proxy of port 443.   assets/nginx-ssl-vhost.conf server { listen 443 ssl; server_name default; ssl_certificate /etc/letsencrypt/live/www.mycoolsite.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/www.mycoolsite.com/privkey.pem; include preset.conf; include defaults.conf; root /var/www/html/web; } This virtual host configuration will listen to port 443 and will use the keys that was transferred from the docker-compose Nginx volume.   Apache Virtual Host <IfModule mod_ssl.c> <VirtualHost *:443> ServerName www.mycoolsite.com ProxyPreserveHost On # setup the proxy <Proxy *> Order allow,deny Allow from all </Proxy> ProxyPass / https://0.0.0.0:8444/ ProxyPassReverse / https://0.0.0.0:8444/ SSLEngine On SSLProxyEngine On SSLCertificateFile /etc/letsencrypt/live/www.mycoolsite.com/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/www.mycoolsite.com/privkey.pem Include /etc/letsencrypt/options-ssl-apache.conf </VirtualHost> </IfModule> This Apache virtual host will listen to port 443 and will call the Docker Nginx port 8444 using the same SSL certificates that we use from Nginx.
  • drupal + docker

    Drupal 8 Docker with multisite setup

    1. Setup Docker Download latest release of Docker4Drupal and install as Vanilla Drupal or to an existing codebase. Follow the comprehensive tutorial from https://wodby.com/docs/1.0/stacks/drupal/local/#usage   2. Update Docker configurations Set the default site domain name in .env  ... PROJECT_BASE_URL=mycoolsite.localhost ...   Create multiple instances of database servers in docker-compose.yml ... default: image: wodby/mariadb:$MARIADB_TAG container_name: "${PROJECT_NAME}_default" stop_grace_period: 30s environment: MYSQL_ROOT_PASSWORD: $DB_ROOT_PASSWORD MYSQL_DATABASE: $DB_NAME MYSQL_USER: $DB_USER MYSQL_PASSWORD: $DB_PASSWORD ports: - 42330:3306 # Port that we can access outside of the container volumes: - ./assets/default.sql:/docker-entrypoint-initdb.d/dump.sql # Place init .sql file(s) here. subsite: image: wodby/mariadb:$MARIADB_TAG container_name: "${PROJECT_NAME}_subsite" stop_grace_period: 30s environment: MYSQL_ROOT_PASSWORD: $DB_ROOT_PASSWORD MYSQL_DATABASE: $DB_NAME MYSQL_USER: $DB_USER MYSQL_PASSWORD: $DB_PASSWORD ports: - 42331:3306 # Port that we can access outside of the container volumes: - ./assets/subsite.sql:/docker-entrypoint-initdb.d/dump.sql # Place init .sql file(s) here. ...   Set web server (nginx) configurations ... nginx: image: wodby/nginx:$NGINX_TAG container_name: "${PROJECT_NAME}_nginx" depends_on: - php environment: NGINX_STATIC_OPEN_FILE_CACHE: "off" NGINX_ERROR_LOG_LEVEL: debug NGINX_BACKEND_HOST: php NGINX_SERVER_ROOT: /var/www/html/web NGINX_VHOST_PRESET: $NGINX_VHOST_PRESET # NGINX_DRUPAL_FILE_PROXY_URL: http://example.com volumes: - ./:/var/www/html:cached ports: - "8001:80" # port to use for proxy labels: - 'traefik.backend=${PROJECT_NAME}_nginx' - 'traefik.frontend.rule=HostRegexp:{subdomain:[a-z]+}.${PROJECT_BASE_URL}' # subdomain ...   3. Drupal site configurations In this tutorial we will have these 2 sites: Default Name: default Domain: mycoolsite.localhost Port: 80001 Subsite Name: subsite Domain: subsite.mycoolsite.localhost Port: 80001     First thing is we need to make changes on sites.php file base on the site informations above.  ... $sites['8001.mycoolsite.localhost'] = 'default'; // Docker default site $sites['8001.subsite.mycoolsite.localhost'] = 'subsite'; // Docker subsite site ...   Next is we need to make sure we have these 2 folders under the sites folder: default subsite   Add the database configurations for both sites using the hostname that we configured from docker-compose.yml.   sites/default/settings.php (default) ... $databases['default']['default'] = [ 'database' => 'drupal', 'username' => 'drupal', 'password' => 'drupal', 'prefix' => '', 'host' => 'default', 'port' => '3306', 'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql', 'driver' => 'mysql', ]; ...   sites/subsite/settings.php (default) ... $databases['default']['default'] = [ 'database' => 'drupal', 'username' => 'drupal', 'password' => 'drupal', 'prefix' => '', 'host' => 'subsite', 'port' => '3306', 'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql', 'driver' => 'mysql', ]; ...    
  • Generate Free Wildcard SSL certificate using Let's Encrypt/Certbot

     1. Generate the wildcard SSL certificate /opt/certbot/certbot-auto certonly --manual --preferred-challenges=dns --email my@email.com --server https://acme-v02.api.letsencrypt.org/directory --agree-tos -d *.mydomain.com -d mydomain.com Note: You need to replace my@email.com, *.mydomain.com and mydomain.com with your actual information. 2. Verify domain's ownership Let’s Encrypt Wildcard certificates only accepts DNS challenge method, which we can invoke by using the preferred-challenges=dns flag. After executing the command on step 1, the Certbot will return a text record that you should add on your DNS. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Please deploy a DNS TXT record under the name _acme-challenge.vhinandrich.com with the following value: vE7k91-8K9XPyMcNYFXP19Ijv7T4o0GAkJnRlwW7af0 Before continuing, verify the record is deployed. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Host: _acme-challenge Value: vE7k91-8K9XPyMcNYFXP19Ijv7T4o0GAkJnRlwW7af0 Create TXT record via DNS console and setup key and value 3. Get your Certificate After adding the dns challange, you can proceed with the generate certificate. It will return you the path of ssl certicates and chain. IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/mydomain.com/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/mydomain.com/privkey.pem Your cert will expire on 2020-09-07. To obtain a new or tweaked version of this certificate in the future, simply run certbot-auto again. To non-interactively renew *all* of your certificates, run "certbot-auto renew" - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le 4. Verify the Certificate To check the validity of all your certificates, you can run this in your command line /opt/certbot/certbot-auto certificates Return:  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Found the following certs: Certificate Name: mydomain.com Serial Number: 3e53264dd24388560fb3dd95e2aa5970bbd Domains: *.mydomain.com mydomain.com Expiry Date: 2020-09-07 02:43:05+00:00 (VALID: 89 days) Certificate Path: /etc/letsencrypt/live/mydomain.com/fullchain.pem Private Key Path: /etc/letsencrypt/live/mydomain.com/privkey.pem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -     Note: Renewal will always have to be done manually for wildcard certificates because of the dns challenge requirment. Thus you won't be able to create cronjobs to renew wildcard certificates.
  • Error pip install mysqlclient on MacOSX

     If you have the similar issue as below ERROR: Command errored out with exit status 1: command: /.../bin/python -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/private/var/folders/53/l74q51892zjgsql64hkvhw180000gn/T/pip-install-e_x8dx0d/mysqlclient/setup.py'"'"'; __file__='"'"'/private/var/folders/53/l74q51892zjgsql64hkvhw180000gn/T/pip-install-e_x8dx0d/mysqlclient/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' bdist_wheel -d /private/var/folders/53/l74q51892zjgsql64hkvhw180000gn/T/pip-wheel-8pw_7779 cwd: /private/var/folders/53/l74q51892zjgsql64hkvhw180000gn/T/pip-install-e_x8dx0d/mysqlclient/ Complete output (30 lines): running bdist_wheel running build running build_py creating build creating build/lib.macosx-10.9-x86_64-3.7 creating build/lib.macosx-10.9-x86_64-3.7/MySQLdb copying MySQLdb/__init__.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb copying MySQLdb/_exceptions.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb copying MySQLdb/compat.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb copying MySQLdb/connections.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb copying MySQLdb/converters.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb copying MySQLdb/cursors.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb copying MySQLdb/release.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb copying MySQLdb/times.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb creating build/lib.macosx-10.9-x86_64-3.7/MySQLdb/constants copying MySQLdb/constants/__init__.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb/constants copying MySQLdb/constants/CLIENT.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb/constants copying MySQLdb/constants/CR.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb/constants copying MySQLdb/constants/ER.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb/constants copying MySQLdb/constants/FIELD_TYPE.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb/constants copying MySQLdb/constants/FLAG.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb/constants running build_ext building 'MySQLdb._mysql' extension creating build/temp.macosx-10.9-x86_64-3.7 creating build/temp.macosx-10.9-x86_64-3.7/MySQLdb gcc -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch x86_64 -g -Dversion_info=(1,4,2,'post',1) -D__version__=1.4.2.post1 -I/usr/local/Cellar/mysql/8.0.19/include/mysql -I/Library/Frameworks/Python.framework/Versions/3.7/include/python3.7m -c MySQLdb/_mysql.c -o build/temp.macosx-10.9-x86_64-3.7/MySQLdb/_mysql.o gcc -bundle -undefined dynamic_lookup -arch x86_64 -g build/temp.macosx-10.9-x86_64-3.7/MySQLdb/_mysql.o -L/usr/local/Cellar/mysql/8.0.19/lib -lmysqlclient -lssl -lcrypto -o build/lib.macosx-10.9-x86_64-3.7/MySQLdb/_mysql.cpython-37m-darwin.so ld: library not found for -lssl clang: error: linker command failed with exit code 1 (use -v to see invocation) error: command 'gcc' failed with exit status 1 ---------------------------------------- ERROR: Failed building wheel for mysqlclient The easiest and fastest solution to try is: Update your ~/.bash_profile or ~/.bashrc and add this line: export PATH="/usr/local/opt/openssl/bin:$PATH" Reload ~/.bash_profile or ~/.bashrc source ~/.bash_profile  
  • Transfer files from or to linux and unix server

    SCP scp command will copy and replace everything from source to destination. Copy files recursively from local to server: scp -rp ~/Downloads user@server.com:/path Copy files recursively from server to local: scp -rp user@server.com:/path ~/Downloads     RSYNC rsync will copy and check and compare files before copying files from source to destination. Copy files recursively from local to server: rsync -av ~/Downloads user@server.com:/path Copy files recursively from server to local: rsync -av user@server.com:/path​ ~/Downloads  
  • rsyslog

    rsyslog: Log messages spamming console

    Remove Emergency Messages to all users This selector-action states "Emergency messages often go to all users currently online to notify them that something strange is happening with the system. To specify this wall(1)-feature use an ":omusrmsg:*"." Comment this line out. Here's are the steps: Stop rsyslog service -  Due to your console being flooded by messages, you will have a hard time executing commands so I suggest you stop the service first. service rsyslog stop Edit rsyslog configuration file /etc/rsyslog.conf vim /etc/rsyslog.conf Find the line: *.emerg :omusrmsg:* Comment out the whole line and save the file Start the rsyslog service again service rsyslog start     Check and fix configuration file (if there's any) If the above steps still didn't resolve the issue, maybe there's some other problems from the configuration file that's causing it to ignore the configurations set.   You can try following these steps to find out and fix the configuration issue:  Stop rsyslog service -  Due to your console being flooded by messages, you will have a hard time executing commands so I suggest you stop the service first. service rsyslog stop Check rsyslog status service rsyslog status It should return something like this: Redirecting to /bin/systemctl status rsyslog.service ● rsyslog.service - System Logging Service Loaded: loaded (/usr/lib/systemd/system/rsyslog.service; enabled; vendor preset: enabled) Active: inactive (dead) since Thu 2020-04-23 11:52:15 +08; 7s ago Docs: man:rsyslogd(8) http://www.rsyslog.com/doc/ Process: 22063 ExecStart=/usr/sbin/rsyslogd -n $SYSLOGD_OPTIONS (code=exited, status=0/SUCCESS) Main PID: 22063 (code=exited, status=0/SUCCESS) Apr 23 11:50:23 site.com systemd[1]: Starting System Logging Service... Apr 23 11:50:23 site.com rsyslogd[22063]: [origin software="rsyslogd" swVersion="8.24.0-34.el7" x-pid="22063" x-info="http://www.rsyslog.com"] start Apr 23 11:50:23 site.com rsyslogd[22063]: action '*' treated as ':omusrmsg:*' - please use ':omusrmsg:*' syntax instead, '*' will not be supported in the futu...m/e/2184 ] Apr 23 11:50:23 site.com systemd[1]: Started System Logging Service. Apr 23 11:50:23 site.com rsyslogd[22063]: error during parsing file /etc/rsyslog.conf, on or before line 95: warnings occured in file '/etc/rsyslog.conf' arou...m/e/2207 ] Apr 23 11:50:23 site.com rsyslogd[22063]: invalid character in selector line - ';template' expected [v8.24.0-34.el7] Apr 23 11:50:23 site.com rsyslogd[22063]: error during parsing file /etc/rsyslog.conf, on or before line 95: errors occured in file '/etc/rsyslog.conf' around...m/e/2207 ] Apr 23 11:52:15 site.com systemd[1]: Stopping System Logging Service... Apr 23 11:52:15 site.com rsyslogd[22063]: [origin software="rsyslogd" swVersion="8.24.0-34.el7" x-pid="22063" x-info="http://www.rsyslog.com"] exiting on signal 15. Apr 23 11:52:15 site.com systemd[1]: Stopped System Logging Service. Hint: Some lines were ellipsized, use -l to show in full. Analyze the status messages to see which line to fix. On this case, I have an error on line 95 of /etc/rsyslog.conf. Edit and fix the configuration file according to the status message. Start the service again service rsyslog start    
  • Change Virtual Environment Python Version

    Remove existing virtual environment Python files cd [EXISTING_ENV_PATH] rm .Python rm bin/pip{,2,2.7} rm bin/python{,2,2.7} rm -r include/python2.7 rm lib/python2.7/* rm -r lib/python2.7/distutils rm lib/python2.7/site-packages/easy_install.* rm -r lib/python2.7/site-packages/pip rm -r lib/python2.7/site-packages/pip-*.dist-info rm -r lib/python2.7/site-packages/setuptools rm -r lib/python2.7/site-packages/setuptools-*.dist-info Run the commands above to delete all python files (assuming that the old python version is python 2). Initialise Virtual Environment with new Python version virtualenv -p `which python3` . Run the command above inside the existing environment path.
  • Schedule command using at command

    Creating commands Type in your terminal: at [schedule] e.g.: at 10:30 am Then inside the at command you can type in your commands like: at> php /var/www/html/yii-application/yii job/my-custom-job param1 param2 Then when you are done adding commands, presst CTRL+D Deleting commands If you don't know yet the schedule id, you can execute atq It will show you the list of scheduled commands like below: job 12 at Wed Feb 20 01:00:00 2019 To see the actual command you can execute at -c 12 To delete the schedule, simply use atrm command with the schedule id like this: atrm 12 And just in case you were thinking how to close the command that is already running, atrm will not stop the command. Instead you need to kill manually. In your terminal, run: ps -ef | grep 'YOUR_COMMAND' It will return something like this: UID PID PPID C STIME TTY TIME CMD root 361 1 0 Oct19 ? 00:00:04 YOUR_COMMAND Get the PID to kill the command kill 361