Categories
AWS Development Linux

Docker Anwendung in AWS (EC2)

In meinem letzten Blogeintrag habe ich eine geDockerte Anwendung auf einem Server mit Ubuntu 18 zum laufen gebracht. Aus verschiedenen Gründen war das aber nur ein Zwischenschritt, um zu testen, ob die Anwendung grundsätzlich in solch einer Umgebung lauffähig ist. Neben den beschriebenen Problemen gab es noch viele weitere, die gelöst werden mussten.

Als nächsten Schritt möchte ich die Anwendung in die AWS umziehen, immerhin bin ich ja inzwischen ein zertifizierter Cloud Practitioner.

AWS User

Mit dem Stammbenutzer einen neuen IAM Nutzer für die Anwendung anlegen. Dieser bekommt erstmal umfangreiche Rechte, was nicht best Practice ist und später sollte ich diese Rechte auf das unbedingt benötigte zurücksetzen.

EC2 Server

Die Anwendung soll erstmal mit dem Docker Setup auf einem EC2 Server laufen.

Mit dem neuen IAM Nutzer wechsele ich zuerst auf die Europa Zone ec-central-1.

Ich lege eine neue EC2 Server Instanz an, wobei ich als Sparfuchs nach "nur kostenloses Kontingent" filtere und ein AMI für Ubuntu Server 20.04 LTS (x64) und Typ t2.micro auswähle.
Es wird ein neues Schlüsselpaar erzeugt und ich speichere den privaten Schlüssel.

Über EC2 > Instances > Server-Instanz auswählen.

Über Verbinden lässt sich im Browser ein Terminal öffnen. Hier lässt sich aber auch am einfachsten die öffentliche IP und vor allem der Benutzername finden:

Ich habe allerdings nicht die Web Shell verwendet, sondern die Daten, sowie den privaten Schlüssel genommen, um eine Verbindung in WinSCP einzurichten. So kann ich später leicht die Daten auf den Server kopieren und per Klick eine PuTTY-Shell öffnen.

Port Freigabe

Standardmäßig ist für den Server nur Port 22 für SSH frei gegeben.

Weitere Ports, wie zB der benötigte HTTP Port 80 oder HTTPS 443, lassen sich über die AWS Management Console frei geben.

Die EC2-Server-Instanz auswählen und unter Sicherheit findet sich die Sicherheitsgruppe:

In der Sicherheitsgruppe können die Regeln für den eingehenden Datenverkehr erweitert werden.
Dabei ist zu beachten, dass man weitere Regeln hinzufügen muss und nicht den bestehenden Typ SSH auf zB HTTP ändert und speichert, weil das diesen nur ändert und nicht als neue, weitere Regel hinzufügt. Dann kann man zwar die Seiten des Webservers bewundern, aber sich nicht mehr per SSH einloggen.

Server einrichten

Auf der Linux Konsole des EC2-Servers wird dieser eingerichtet, dazu wird Docker Compose installiert, was als Abhängigkeit Docker mitbringt.

apt list --upgradable
sudo apt update
sudo apt upgrade -y
sudo apt install docker-compose -y

sudo docker version         # -> 20.10.7
sudo docker-compose version # -> 1.25.0
sudo service docker status  # -> running

sudo docker run hello-world

Docker läuft und es werden die Daten der Anwendung auf den Server kopiert und anschließend über Docker Compose gestartet.

sudo docker-compose up 

Leider führte das zu einem Fehler, wie er schon bei der Ubuntu 18 Installation aufgetreten ist. Das zuvor gewonnene Wissen kann ich jetzt zur schnellen Fehlerbehebung anwenden:

sudo apt-get remove docker-compose -y
sudo curl -L https://github.com/docker/compose/releases/download/v2.2.3/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version
# Output:
-bash: /usr/bin/docker-compose: No such file or directory
# Lösung: neue Shell, zb per tmux, starten
# und dann nochmals testen
docker-compose --version
# Output:
Docker Compose version v2.2.3

Anschließend ließ sich die Anwendung per Docker Compose starten und per cURL, bzw. HTTPie, über localhost:80 und <öffentlicheIP>:80 aufrufen. Der Aufruf <öffentlicheIP>:80 vom Entwickler Laptop funktioniert auch.

Der Start dauerte etwas länger, die Webanwendung selbst ließ sich anschließend aber angenehm schnell bedienen. Zumindest als Test-Server scheint der "Gratis"-EC2-Server völlig auszureichen.

Ausblick

Auf dem kostenfreien Server laufen ein Tomcat Webserver, eine PostgreSQL Datenbank und PGAdmin und das, zumindest den ersten Tests nach, mit völlig ausreichender Performance.

Als nächstes möchte ich dem Docker Compose Konstrukt noch um einen Reverse Proxy erweitern, der eine (vermutlich nur selbstsignierte) verschlüsselte Verbindung per HTTPS anbietet und über Port 80 und 443 die Anwendung und den PGAdmin erreichbar macht. Außerdem soll es einen einfachen Authentifizierungs- und ggf. Authorisierungsmechanismus geben. Das wird mit einem Apache HTTP Server realisiert werden und sollte keinen besonderen Ressourcenbedarf haben.

Falls sich die Zeit findet, möchte ich das um Keycloak erweitern und den Zugriff auf Anwendung und PGAdmin erst nach erfolgreicher Authentifizierung und Authorisierung erlauben. Vielleicht ist das noch mit dem Apache HTTP Server realisierbar, ggf. werde ich aber auf zB Traefik umstellen.
Bei dem Setup kann ich mir schon vorstellen, dass die Ressourcen des kleinen Server nicht mehr ausreichen und es zu spürbaren Performanceeinbrüchen kommen wird.

Eine ansprechendere URL, anstelle der generierten AWS URL, wäre wünschenswert.

Categories
Development Linux

Docker Compose und Ubuntu 18

Docker Compose Datei vom Entwicklungsrechner auf den Server kopieren, kleinere Anpassungen vornehmen und ausführen. So einfach habe ich es mir vorgestellt, aber es gab dann leider doch noch Herausforderungen zu bewältigen:

Docker Compose Updaten

Ich habe einen Server mit dem nicht mehr ganz taufrischen Ubuntu 18 am laufen und wollte dort ein Docker Compose Skript ausführen.

Das Skript läuft auf meinem Entwicklungsrechner, aber auf dem Server wurde lediglich eine Fehlermeldung ausgegeben:

dockeruser@myServer:~/myproject$ docker-compose up
ERROR: Version in "./docker-compose.yml" is unsupported. You might be seeing this error because you're using the wrong Compose file version. Either specify a supported version (e.g "2.2" or "3.3") and place your service definitions under the `services` key, or omit the `version` key and place your service definitions at the root of the file to use version 1.
For more on the Compose file format versions, see https://docs.docker.com/compose/compose-file/

Wie sich herausstellte, war für Ubuntu 18 bei Docker Compose 1.17.1 Schluss und ich muss händisch upgraden:

Docker Compose entfernen:

sudo apt-get remove docker-compose

Die aktuelle Docker Compose Version ermitteln (heute: 2.2.3): https://github.com/docker/compose/releases

Auf dieser Seite kann man auch den direkten Link zum Download finden, falls es beim ausführen des nächsten Befehls zu Problemen kommt.

Beispielsweise ist die Versionsnummer v2.2.3, also mit einem kleinen "v" am Anfang und wenn das fehlt, schlägt der Download fehl.

So lautet der Link für mein Ubuntu:
https://github.com/docker/compose/releases/download/v2.2.3/docker-compose-linux-x86_64

Der Befehl zum Download:

sudo curl -L https://github.com/docker/compose/releases/download/v2.2.3/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose

Rechte setzen:

sudo chmod +x /usr/local/bin/docker-compose

Installation und Version checken:

docker-compose --version
# Output:
Docker Compose version v2.2.3

Docker Compose ohne GUI

Mit der neuesten Docker Compose Version gibt es einen neuen Fehler:

error getting credentials - err: exit status 1, out: `Cannot autolaunch D-Bus without X11 $DISPLAY`

Die Erklärung lautet:

Looks like this is because it defaults to use the secretservice executable which seems to have some sort of X11 dependency for some reason. If you install and configure pass docker will use that instead which seems to solve the problem.

Zu erst muss die aktuelle Version des Docker Credential Helpers ermittelt werden: v0.6.4

Install the Docker Credential Helper for pass:

# substitute with the latest version
url=https://github.com/docker/docker-credential-helpers/releases/download/v0.6.4/docker-credential-pass-v0.6.4-amd64.tar.gz

# download and untar the binary
wget $url
tar -xzvf $(basename $url)

# move the binary to a dir in your $PATH
sudo mv docker-credential-pass /usr/local/bin

# verify it works
docker-credential-pass list

# cleanup
rm docker-credential-pass-v0.6.4-amd64.tar.gz

Install and configure pass:

sudo apt install pass

# create a gpg2 key
gpg2 --gen-key

Und der nächste Fehler:

gpg: agent_genkey failed: Keine Berechtigung
Schlüsselerzeugung fehlgeschlagen: Keine Berechtigung

Und eine Erklärung mit Lösungsvorschlag findet sich hier:

Expected behavior. Here's why.
At the point of failure, gen-key is about to ask the user for a passphrase. For security purposes, rather than using stdin/stdout, it wants to directly open the controlling terminal for the session and use that handle to write the prompt and receive the passphrase. When you use su to switch to some other user, the owner of the controlling terminal device file does not change; it remains associated with the user who actually logged in (i.e. received a real terminal from getty or got a pty from telnet or ssh or whatever). That device file is protected mode 600, so it can't be opened by anyone else.

The solution is to sudo-chown the device file to the user-who-needs-to-gen-the-key before su'ing to that user. Create the key within the su'd environment, then exit back to the original environment. Then, finally, sudo-chown the terminal back to yourself.

Glücklicherweise geht es auch einfacher, indem man einfach das Programm tmux verwendet. 🙂

tmux

# create a gpg2 key
gpg2 --gen-key

# list key information
gpg2 -k

# Copy the key id (from the line labelled [uid]) and do
pass init "whatever key id you have"

Jetzt sollte der Docker Login funktionieren, aber:

docker login
# Output:
Error saving credentials: error storing credentials - err: fork/exec /usr/local/bin/docker-credential-pass: permission denied, out: ``

Auch wieder kein neues Problem, dass zB bereits hier und hier diskutiert wurde.

mkdir ~/.docker
touch ~/.docker/config.json
# brachte jeweils keine Änderung


/usr/local/bin/docker-credential-pass
# Output:
-bash: /usr/local/bin/docker-credential-pass: Keine Berechtigung


# Erfolg kam mit diesem Befehl:
sudo chmod +x /usr/local/bin/docker-credential-pass

#Zumindest funktioniert dieser Aufruf:
docker-credential-pass list


# Ein weiterer Fehler ließ sich beheben durch:
export GPG_TTY=$(tty)

Ich musste die einzelnen Images per docker pull imagename ziehen, erst danach konnte ich docker-compose ausführen.

Categories
Linux

NFS4

In my post to NFS I decribed how to create and attach a NFS folder.

Last week a colleague asked me to double check the version of this NS folder on the client. It turns out, that it was NFS3 only and not NFS4.

Interestingly this depends on the file permissions of the parent folder. The parend folder has to be read- and executable for others.

myAppServer1 – Server

chmod o+rx /app/myApp
exportfs -r
exportfs -s

myAppServer2 – Client

umount /app/myApp/uploads
mount /app/myApp/uploads
mount
-> myAppServer1.mynetwork.net:/app/myApp/uploads on /misc/myapp_uploads type nfs4 
Categories
Linux

NFS

NFS - Network File System

We have two Linux servers, each with one Tomcat application server. Both application servers must have access to one folder to up- and download files.

We will create a folder on the first server and enable access through the network on this server for the second server.

Operating System: RedHat Enterprise Linux 7

myAppServer1 - Server

vim /etc/exports
 
systemctl list-units *nfs-server*
# if 0 loaded units, you have to enable and start
 
systemctl enable nfs-server.service
systemctl start nfs-server.service
systemctl list-units *nfs-server*
exportfs -r
exportfs -s
/app/myApp/uploads myAppServer2.mynetwork.net(rw,sync)

myAppServer2 - Client

vim /etc/fstab
vim /etc/auto.master
vim /etc/auto.misc
 
systemctl enable autofs.service
systemctl start autofs.service
 
su tomcat
ls -lisah /app/myApp/uploads
# add:
myAppServer1.mynetwork.net:/app/myApp/uploads   /app/myApp/uploads     nfs     user,timeo=3,bg 0       0
# add:
/misc   /etc/auto.misc  --timeout 0 --negative-timeout 5
# add:
myapp_uploads     -fstype=nfs,bg,timeout=-1,timeo=5,rw,tcp,soft,intr,nosuid myAppServer1.mynetwork.net:/app/myApp/uploads

Test

Reboot both servers and doublecheck that the folder is still available and accessible from both servers.

reboot now
 
su tomcat
ls -lisah /app/myApp/uploads

Open webapplication in your browser on myAppServer1 and upload a file. Test download of the file.
Switch to myAppServer2 and download file.

Open webapplication in your browser on myAppServer2 and upload a file. Test download of the file.
Switch to myAppServer1 and download file.

NFS4 - Update

Please also note this post: NFS4

Categories
Java Linux

Setup Tomcat manager app

In my last post I set up a Tomcat application server in general, now I enable Tomcat manager app for deployment.

# Tomcat Users
mv /app/myApp/tomcat/conf/tomcat-users.xml /app/myApp/tomcat/conf/tomcat-users.xml_original
vim /app/myApp/tomcat/conf/tomcat-users.xml

vim/app/myApp/tomcat/conf/server.xml

# By default the Manager is only accessible from a browser running on the same machine as Tomcat. If you wish to modify this restriction, you'll need to edit the Manager's context.xml file.
vim /app/myApp/tomcat/webapps/manager/META-INF/context.xml
<?xml version="1.0" encoding="UTF-8"?>
<tomcat-users xmlns="http://tomcat.apache.org/xml"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
              version="1.0">
 
  <role rolename="manager-gui"/>
  <role rolename="manager-script"/>
  <user username="tomcat" password="tomcat" roles="manager-gui,manager-script"/>
 
</tomcat-users>
<Server port="7010" shutdown="SHUTDOWN">
  <GlobalNamingResources>
    <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" />
  </GlobalNamingResources>
 
  <Service name="Catalina">
    <Connector port="7011" />
   
    <!-- Define an AJP 1.3 Connector on port 7012 -->
    <Connector port="7012" protocol="AJP/1.3" secretRequired="false" />
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="myApp-dev">
      <Host name="localhost" appBase="webapps" />
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <!-- This Realm uses the UserDatabase configured in the global JNDI
             resources under the key "UserDatabase".  Any edits
             that are performed against this UserDatabase are immediately
             available for use by the Realm.  -->
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>
    </Engine>
  </Service>
</Server>
<Context antiResourceLocking="false" privileged="true" >
    <!-- ## uncomment this Block ##
    <Valve className="org.apache.catalina.valves.RemoteAddrValve"
         allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
    -->
</Context>

Test Tomcat manager app

Open http://myAppServer:7011/manager and login with username: tomcat and password: tomcat.

Categories
Java Linux

Setup Tomcat application server

Setup files

su myUser
 
# Java
cd /app/java
tar -xzf /app/files/OpenJDK8U-jre_x64_linux_hotspot_8u265b01.tar.gz
tar -xzf /app/files/OpenJDK11U-jre_x64_linux_hotspot_11.0.8_10.tar.gz
tar -xzf /app/files/OpenJDK15U-jre_x64_linux_hotspot_15_36.tar.gz
 
# myApp Tomcat
cd /app/myApp
tar -xzf /app/files/apache-tomcat-8.5.59.tar.gz
tar -xzf /app/files/apache-tomcat-9.0.39.tar.gz
 
# Certificate
cp /app/files/corporate_truststore.jks /app/certs/
 
# SAP JCO
cp /app/files/_sapjco3-64/3.0.19/linuxx86/libsapjco3.so /app/library/
cp /app/files/_sapjco3-64/3.0.19/linuxx86/sapjco3.jar /app/library/

Setup Tomcat

su myUser
 
# Symlink to actual Tomcat version
ln -s /app/ccp/apache-tomcat-8.5.59 /app/myApp/tomcat
 
# remove sample application
# but keep the Tomcat Manager app for deployment
rm -rf /app/myApp/tomcat/webapps/docs
rm -rf /app/myApp/tomcat/webapps/examples
rm -rf /app/myApp/tomcat/webapps/ROOT
 
# configure Tomcat
vim /app/myApp/tomcat/bin/setenv.sh
mv /app/myApp/tomcat/conf/server.xml /app/myApp/tomcat/conf/server.xml_original
vim /app/myApp/tomcat/conf/server.xml
 
# expand Classpath
vim /app/myApp/tomcat/conf/catalina.properties
# tomcat/bin/setenv.sh
CATALINA_HOME=/app/myApp/tomcat
CATALINA_BASE=/app/myApp/tomcat
CATALINA_PID=/app/myApp/tomcat/tomcat.pid
JAVA_HOME=/app/java/jdk8u265-b01-jre
CATALINA_OPTS="$CATALINA_OPTS -Djava.library.path=/app/library"
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/app/library
export LD_LIBRARY_PATH
JAVA_OPTS="${JAVA_OPTS} -Djavax.net.ssl.trustStore=/app/certs/corporate_truststore.jks -Djavax.net.ssl.trustStorePassword=notchangeit -Xms512M -Xmx2048M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/app/myApp/dump"

# tomcat/conf/catalina.properties
common.loader= [...] ,"/app/library/sapjco3.jar"
# server.xml
<Server port="7010" shutdown="SHUTDOWN">
  <Service name="Catalina">
    <Connector port="7011" />
   
    <!-- Define an AJP 1.3 Connector on port 7012 -->
    <Connector port="7012" protocol="AJP/1.3" secretRequired="false" />
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="myApp-dev">
      <Host name="localhost" appBase="webapps" />
    </Engine>
  </Service>
</Server>

Tomcat as a Service

# As root
vim /etc/systemd/system/tomcat.service
# enable script:
systemctl enable tomcat.service
# tomcat.service

# Systemd unit file for myApp tomcat
#
# To create clones of this service:
   
# Systemd unit file for tomcat
 [Unit]
 Description=myApp Tomcat Web Application Container
 After=syslog.target network.target
   
   
 [Service]
 Type=forking
   
 ExecStart=/app/myApp/tomcat/bin/startup.sh
 ExecStop=/app/myApp/tomcat/bin/shutdown.sh
   
 User=myUser
 Group=myUser
   
 [Install]
 WantedBy=multi-user.target

Service control

Enable user myUser to control Tomcat services:

visudo -f /etc/sudoers
##################################################
## Allow user myUser to control (apache & tomcat) services
%%myUser ALL=(root) NOPASSWD: /bin/systemctl
%%myUser ALL=(root) NOPASSWD: /usr/sbin/service

Test Tomcat

For Tomcat testing I use my ShowHeaders app (GitHub).
ShowHeaders is a minimalistic webapp that is not much more than a "Hello World", but it shows the HTTP headers, what is quite useful when testing reverse proxy integration.

# copy ShowHeaders App for Testing (also for Reverse Proxy Configuration Testing)
[myUser@DEV ~]$ cp /app/files/ShowHeaders/ROOT.war /app/myApp/tomcat/webapps/
# start tomcat:
[myUser@DEV ~]$ sudo systemctl start tomcat

#
curl localhost:7011

Test connection from outside the server itself: http://myAppServer:7011/

Categories
Linux

Copy files with rsync

I have to setup and configure a DEV server and afterwards the QA/staging and PROD servers.

I will download required software with my laptop and upload to DEV server into folder /app/files. For other servers I just want to copy this folder from the DEV server.

Setup server

DEV

# install software
yum install rsync -y

# create user
adduser myUser

# create folder
mkdir /app/files
chown myUser:myUser /app/files

# switch to myUser
su myUser

# Create Private & Public keys
ssh-keygen -t rsa -b 4096            # generates id_rsa & id_rsa.pub in  /home/myUser/.ssh/

# authorize yourself
cat /home/myUser/.ssh/id_rsa.pub >> /home/myUser/.ssh/authorized_keys
chmod 0600 ~/.ssh/authorized_keys

QA / others

nearly the same setup, but we copy the keys from the DEV user, as he is the same as on the other servers.

# install software
yum install rsync -y

# create user
adduser myUser

# create folder
mkdir /app/files
chown myUser:myUser /app/files

# switch to myUser
su myUser

# create .ssh folder
mkdir ~/.ssh
chmod 0700 ~/.ssh

# create files, copy content from DEV server!
vi ~/.ssh/id_rsa
chmod 0600 ~/.ssh/id_rsa
vi ~/.ssh/id_rsa.pub
chmod 0600 ~/.ssh/id_rsa.pub
vi ~/.ssh/authorized_keys
chmod 0600 ~/.ssh/authorized_keys

Test SSH login

# login from DEV to QA
[myUser@DEV] ssh QA
# login from QA to DEV
[myUser@QA] ssh DEV

# loggin with explicit port
[myUser@DEV] ssh QA -p 22

Download software

Copy all files to /app/files

Java

Open URL https://adoptopenjdk.net/

Click on "Andere Plattformen"

Select for each:

  • JVM: HotSpot
  • Operating System: Linux
  • Architecture: x64

Java Versions:

  • OpenJDK 8 (LTS)
  • OpenJDK 11 (LTS)
  • OpenJDK 15 (Latest)

Download the JRE files.

Tomcat

Open URL: http://tomcat.apache.org/

Download Binary Distribution → Core → tar.gz-Version of:

  • Tomcat 8.5
  • Tomcat 9

SAP JCO

Copy the latest version from the corporate repository.

Path: _sapjco3-64/versions/3.0.19

Corporate Root CA

Copy the password ("notchangeit") protected truststore: corporate_truststore.jks

Copy from server to server

[myUser@QA ~]$ rsync --progress -avhe 'ssh -p 22' myUser@DEV:/app/files/ /app/files
Categories
Linux

Logical Volume Creation

In one of my current projects I have to attach a (virtual) disk to a (cloud) server.

Gather information

To get an overview of the system I used this commands:

pvs
pvdisplay

vgs
vgdisplay

lvs
lvdisplay

fdisk -c -l /dev/sdb

Interpretation of information

Physical VolumePartitionVolume GroupLogical VolumesSize
/dev/sda107 GB
/dev/sda/dev/sda10,5 GB
/dev/sda/dev/sda2106,5 GB
/dev/sda/dev/sda2vg00<99.50g
/dev/sda/dev/sda2vg00lv_home2 GB
/dev/sda/dev/sda2vg00lv_usr2 GB
/dev/sda/dev/sda2vg00"free"<76.50g
/dev/sdb107 GB

Technically it is possible to create a new logical volume inside volume group vg00, because there are > 76,5 GB unassigned and free storage.

But the vg00 is reserved for the Operating System and should not used for data from application.

Therefore the additional disk is needed for applicatin data.

Volume creation

Physical Volume with ~ 100 GB is attached to the server and added as /dev/sdb.

A volume group "vg_app" will be created with two logical volumes. One logical volume for logfiles, named "lv_logs" with 20 GB and the other with remaining free space, named "lv_app", for application data.

# create volume group
vgcreate -s 32M vg_app /dev/sdb

# create logical volume for logfiles
lvcreate -L 20G -n lv_log vg_app

# create logical volume for application data
lvcreate -l 100%%VG -n lv_app vg_app

# show information
vgdisplay -v /dev/vg_app

mount -a
Physical VolumeVolume GroupLogical VolumeSize
/dev/sdb107 GB
/dev/sdbvg_app107 GB
/dev/sdbvg_applv_log20 GB
/dev/sdbvg_applv_app> 79 GB

Create File system

mkfs -t ext4 /dev/vg_app/lv_app
mkfs -t ext4 /dev/vg_app/lv_log

Mount logical volumes

mkdir /app
mkdir /app/log
 
mount /dev/mapper/vg_app-lv_log /app/log
mount /dev/mapper/vg_app-lv_app /app
 
mount -a
vi /etc/fstab
mount -a
# /etc/fstab
# [...] existing files code [...]
/dev/mapper/vg_app-lv_app /app     ext4 defaults 0 0
/dev/mapper/vg_app-lv_log /app/log ext4 defaults 0 0

Test

Reboot and doublecheck that mounted volumes are still there.

reboot now
##########
df -h
Filesystem                 Size  Used Avail Use%% Mounted on
[...]
/dev/mapper/vg_app-lv_app   79G   57M   75G   1%% /app
/dev/mapper/vg_app-lv_log   20G   45M   19G   1%% /app/log

-> Both logical volumes are mounted --> Success!