


[{"content":" Home Office / Lab I needed a place to have the HomeLab and my own Home Office/Lab. After all, they sent us to work from home and couldn\u0026rsquo;t go to the office or school for months because of the COVID pandemic.\nI was lucky to have a room in the basement I could use for this purpouse, a 2,40m x 3,12m room with no enough power and no network infrastructure what so ever.\nThe first thing I wanted to do was to install some power sockets and some network RJ45 sockets for all the devices I was planning to have at the office. I was doing some renovations on the first floor and I had access to an electrician, so I took the chance and he did this jobb and some upgrades in the main distribution board at home. This is what we did to prepare this room:\nInstallation of a PVC cable tray to install all power and network sockets.\nInstallation of 18 x RJ45 sockets using keystone ports and CAT-6a ethernet cable with support for up to 10Gbps. All the cables terminated in a \u0026ldquo;TeleSafe 19\u0026rdquo; 24-ports aluminium keystone-patchpanel\u0026quot;1 from Lanse(no) installed in a 600x800x988mm 18U rack cabinet. The rack cabinet has wheels and with the extra length of all the ethernet cables I can move the rack without problems for maintenance or installation of new components.\nInstallation of a CAT-6a ethernet cable from the keystone-patchpanel in the basement to the ceiling in the living room on the ground floor to be used by a WiFi AP via POE.\nThe Ethernet cable used was the \u0026ldquo;R\u0026amp;M freenet U/FTP Cat.6A 650 MHz 4PxAWG23\u0026rdquo;2, capable among other things of being used for 10/100/1000Base-T, 10GBase-T, POE, POE+, 4PPOE, UPOE and UPOE+.\nInstallation of 20 x (EU) power sockets connected to a new/dedicated 220V/16A circuit using 2,5mm cable with a dedicated \u0026ldquo;Eaton PKPM2-16/2/C/003-A\u0026rdquo;3 ground fault breaker.\nA 220V/16A circuit has a maximum capacity of 3.520W. Taking into account that we should have a 20 percent safety margin, the maximun load I can use with this circuit is around 2.816W. More than enough for my used, even using all the equipment I plan to use in this room at the same time. This circuit is used exclusively for electronic equipment.\nAfter the power and network infrastructure were in place, I built myself the workbench and bookshelves from solid oak wood.\nWorkBench: L-shape with 2 parts: 1) 2,40m x 0,90m 2) 1,00m x 0,62m, almost 2,80m2 of workspace. Thickness:40mm. Height:0,75m. Solid metal legs capable of supporting several hundred kilos Bookshelves: 2,40m x 0,30m. Thickness: 20mm. Here you have some pictures from the installation work and the final result:\nPower and network RJ45 sockets installation Distribution board upgrade Extra length - ethernet cables Extra length - ethernet cables Patchpanel back / inside rack cabinet Patchpanel front Upgraded distribution board TeleSafe 19\u0026quot; 24-ports aluminium keystone-patchpane https://e-mc2.net/files/patchpanel-telesafe.pdf\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nR\u0026amp;M freenet U/FTP Cat.6A 650 MHz 4PxAWG23 https://e-mc2.net/files/DATA_SH-C6A-U-FTP-650MHz-LSFRZH-GY-R833675-B.pdf\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nEaton PKPM2-16/2/C/003-A https://e-mc2.net/files/Eaton-108111-PKPM2-16-2-C-003-A-en_GB.pdf\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2023/04/08","externalUrl":null,"permalink":"/homelab/office/","section":"Emc2Net HomeLab - Intro","summary":"I needed a place to have the HomeLab and my own Home Office/Lab. After all, they sent us to work from home and couldn’t go to the office or school for months because of the COVID pandemic.","title":"Emc2Net HomeLab - Home Office / Lab","type":"homelab"},{"content":"A 19-inch rack is a standardized frame or enclosure for mounting multiple electronic equipment modules. Each module has a front panel that is 19 inches (482.6 mm) wide.1\nA rack unit (abbreviated U) is a unit of measure defined as 1+3⁄4 inches (44.45 mm). It is most frequently used as a measurement of the overall height of 19-inch rack frames, as well as the height of equipment that mounts in these frames, whereby the height of the frame or equipment is expressed as multiples of rack units. 2\nThe Rack cabinet I chose for my HomeLab was the Toten G6 19\u0026quot; 18U 600x800x988mm3 with Glass door.\nEmc2Net HomeLab rack cabinet The most important question when buying a standard 19\u0026quot; cabinet is: How much space do I have for the cabinet? And then base on the space you have available, find out the heigh and depth of the cabinet you can affort to have ;)\nThe depth will define the type of servers you will be able to use with the rack, and the height the amount of servers and other components you will be able to mount in the rack.\nIn my case, I didn\u0026rsquo;t have a lot extra space in my Home Office, so I decided to have a \u0026ldquo;half-height\u0026rdquo; rack with 18U and a total depth of 800mm so I could place it under the Main distribution board in one of the corners of my office.\nI decided to cover the inside of the rack with a 20mm soundproofing material to dampen sound and possible vibrations. The outer layer of this material is aluminium-coated polyester foil with self-adhesive reverse side. It withstands temperatures between -30 °C to +120 °C. ISO 3795 and the flammability is [mm/min \u0026lt; 100 / Standard: F MVSS302]\nThis material does not allow heat from the electronics in the rack to dissipate through the metal of the rack body. Therefore, you will need good ventilation and airflow inside the rack to help cooling the electronics. I have not had problems with this, living in Norway and having around 21-22C in the basement probably helps a lot.\nThe distribution of the rack nowadays looks like this:\nEmc2Net HomeLab rack cabinet distribution I still have some extra space for future upgrades, in the meantime the empty rack units help with better air flow and the cooling of the rack.\nI also got some accessories to use in the cabinet:\n2 x Rack cable management rings 2 x Brush Strip Cable Management Panels 1 x Rack Power Distribution Unit (rPDU) 10A/250V 50/60Hz, Max.2.500W 1 x Startech Rack-shelf 19\u0026quot; 22.6kg Some pictures of the Rack cabinet:\nEmc2Net HomeLab rack cabinet open front Emc2Net HomeLab rack cabinet open back Covering Rack cabinet with soundproofing material 19-inch rack https://en.wikipedia.org/wiki/19-inch_rack\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nRack unit https://en.wikipedia.org/wiki/Rack_unit\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nToten G6 19\u0026quot; 18U 600x800x988mm https://aurdel.com/no/no/19-g6822gm\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2023/04/08","externalUrl":null,"permalink":"/homelab/rack-cabinet/","section":"Emc2Net HomeLab - Intro","summary":"A 19-inch rack is a standardized frame or enclosure for mounting multiple electronic equipment modules. Each module has a front panel that is 19 inches (482.6 mm) wide.","title":"Emc2Net HomeLab - Rack cabinet","type":"homelab"},{"content":" PowerWalker VI 3000 RLE One of the components one should have in a HomeLab is an UPS (Uninterruptible Power Supply)1. An UPS will protect your HomeLab when the input power source or mains power fails, giving you time to save your data/work and shutdown your HomeLab without data lost or data corruption.\nDepending of the model, an UPS can also protect your HomeLab from voltage spike / overvoltage, reductions in input voltage, line noise, voltage dips, etc. A stable power supply will protect all your electric components and will give you a more stable infrastructure.\nThe model I chose for my HomeLab was the PowerWalker VI 3000 RLE2, a line-interactive UPS from BlueWalker GmbH3. The reasons for choosing this UPS were a very competitive price, functionality, capacity, silent operation and 2U rack design.\nSome of the features of the \u0026ldquo;PowerWalker VI 3000 RLE\u0026rdquo; are:\nPower Capacity of 3000VA/ 1800W Pure Sine Wave Output Human Interface Device (HID) Compatible USB Connection Automatic Voltage Regulator Fully Automatic and Silent Operation 8 x IEC C13 Outlets This UPS has been working without problems since day one and it has enough capacity for all the components in my HomeLab, usually it has a load between 14-20% and a runtime on battery of around 40min. So I have enough free capacity for future expansion.\nThe UPS is connected via USB to the main router in my HomeLab and monitored using \u0026ldquo;Network UPS Tools\u0026rdquo; (NUT)4 with the usbhid driver. The main router, the KVM server, all VMs and the main Desktop computer in my office are configured to shutdown automatically in a proper way if the battery charge goes under 10% or the left runtime on battery is less than 5min.\nI have written the article \u0026ldquo;UPS control with NUT, PFSense+ and Zabbix \u0026ldquo; about how this has been done and how I monitor it via Zabbix.\nNowadays the UPS supplies power to these components:\nDell PowerEdge R210 II - Main router / firewall Ubiquiti UniFi USW 24 PoE Switch Ubiquiti Unifi AP-AC HD Inter-Tech IPC 4U-4408 4U - Main KVM server Main Desktop computer in my office. Alarm system gateway Sonos Sound system gateway Rack fans/leds Console monitor UPS (Uninterruptible Power Supply) https://en.wikipedia.org/wiki/Uninterruptible_power_supply\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nPowerWalker VI 3000 RLE homepage https://powerwalker.com/product/10121101/\nPowerWalker VI 3000 RLE Datasheet https://e-mc2.net/files/PowerWalker_VI_RLE_1200-3000.pdf\nPowerWalker VI 3000 RLE Quick guide https://e-mc2.net/files/PowerWalker_VI_RLE_1200-3000VA_Quick-Guide_EN.pdf\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nBlueWalker GmbH https://powerwalker.com/bluewalker/\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;Network UPS Tools\u0026rdquo; (NUT) https://networkupstools.org/\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2023/04/08","externalUrl":null,"permalink":"/homelab/ups/","section":"Emc2Net HomeLab - Intro","summary":"One of the components one should have in a HomeLab is an UPS (Uninterruptible Power Supply). An UPS will protect your HomeLab when the input power source or mains power fails, giving you time to save your data/work and shutdown your HomeLab without data lost or data corruption.","title":"Emc2Net HomeLab - UPS","type":"homelab"},{"content":" Old infrastructure One of the main components in a HomeLab is the network infrastructure.\nFor years I used a standard WiFi router at home, the last one I had was an Asus RT-AC87U Wireless-AC24001 from 2015. This router did its job but with some deficiencies. The WiFi coverage of the whole house was far from perfect, it didn\u0026rsquo;t have Wlan functionality, the firewall was very simple, and the robustness of the infrastructure decreased as the number of devices connected to the router continued to increase.\nTogether with the new HomeLab I wanted to implement a high-performance and robust WiFi for the rest of the house.\nSo the main requirements I had for the network (wired/wireless) were:\nStable and robust Maximum WiFi coverage As high throughput rate as possible for multiple clients running concurrently at high speed. Security in mind, support for Wlans, firewalls, VPNs. I considered implementing a 10Gbps core network at the begynning but decided to drop this requirement because of the higher cost. I used 1Gbps connections everywhere (including the main Fiber Internet link) but I used high quality CAT-6a ethernet cable to support future upgrades.\nThe performance of the network is very satisfactory although some backups could take much less time with a higher network speed, but this has not been a problem given that all backups are taken at night when I am sleeping.\nThese are the main components of the network infrastructure:\nRouter / Firewall / ++ # Dell PowerEdge R210 II - Router In the begynning I used an UniFi Dream Machine Pro2 from Ubiquiti to deliver the router and firewall functionality in my network. I used this router for almost 2 years but in the end I sold it and bought a second hand Dell PowerEdge R210 II3 and installed PFsense+ on it as my router/firewall solution.\nI did this because I was not satisfy with the level of control I had and the functionality available with the Dream Machine Pro. I know the Dream Machine is a nice solution for many users out there and it did its job in my homelab for a while, but in my opinion, the level of control and functionality I have with PFsense+ cannot be compared with the solution from Ubiquiti.\nOne negative thing about this change was that I had to install the UniFi Controller software in a virtual machine to be able to control and configure my UniFi USW 24 PoE Switch4 and UniFi AP-AC HD5. The Dream Machine Pro came with the UniFi Controller software installed and available out of the box.\nThe Dell PowerEdge R210 II has an Intel(R) Xeon(R) CPU E31220 @ 3.10GHz, 8GB ECC RAM and 2 x 1TB SAS HHDs and is running PFSense+ 23.01-RELEASE6 based on FreeBSD 14.0-CURRENT. This is an old small 1U Dell server with more than enough power and resources to be the central brain of the network and it does this job without problems.\nUbiquiti UniFi USW 24 PoE Switch # UniFi USW 24 PoE Switch This switch from Ubiquiti is a managed 24-port, Layer2 switch with support for VLANs and aggregation per port. This switch does its job very well and it has been very stable.\nSome information about this switch:\n19\u0026quot; 1U Rackmountable\nInterfaces: 24 x 10/100/1000 RJ45 Ports 2 x 1G SFP Ethernet Ports\nThroughput: 26 Gbps\nSwitching Capacity: 52 Gbps\nForwarding Rate: 38.69 Mpps\nTotal Available PoE: 95W\nPoE Interfaces: Ports 1-16 PoE+ IEEE 802.3af/at\nMax. PoE Wattage per Port by PSE 802.3at: 32W\nMax Power Consumption(Excluding PoE Output): 25W\nUbiquiti Unifi AP-AC HD # I spent sometime thinking and reading about different WiFi components, and trying to find a good solution for the WiFi infrastructure at home. Some of the requirements I had were:\nTo have the least number of Access Points (AP) as possible with the maximum WiFi coverage of my house (Basement + 2 floors + garden). Support of Power over Ethernet (PoE) to avoid the need of power sockets close to the AP (I wanted to install the AP on the ceiling) As high throughput rate as possible to support multiple clients running concurrently at high speed. Multi-user MIMO so the AP could communicate with multiple clients at the same time increasing the multi‑user throughput and overall user experience I wanted. Support for multiple SSIDs and VLANs so I could segment all the traffic from the devices accesing the network via WiFi APs. I finally found a nice deal for an access point from Ubiquiti, the Unifi AP-AC-HD. This AP is a high-performance, indoor/outdoor, 802.11ac WiFi access point that utilizes Wave 2 technology and had all the requirements I was looking for.\nI mounted the AP-AC-HD in the living room ceiling on the ground floor (the center point of my house) and connected it to the UniFi USW 24 PoE Switch in the basement via a CAT-6a ethernet cable.\nThe WiFi coverage at home is no problem now, no matter where you are, the stability and the performance of this AP is impresive. I have 30+ devices connected at all times to this AP, including a HD-TV in the basement and two workplaces on the second floor and I have never had problems with it. Probably an overspec AP for home use, but I would buy it again, the WiFi experience at home has never been better, and only with one AP.\nSome of the features of this AP:\n802.11ac Wave 2 WiFi technology Simultaneous Dual-Band 5 GHz (4x4 MU-MIMO) band with a 1.7 Gbps throughput rate 2.4 GHz (4x4 MIMO) band with a 800 Mbps throughput rate PoE Mode 802.3at PoE Ceiling Mount Support for multiple SSIDs and VLANs Asus RT-AC87U Wireless-AC2400 https://www.asus.com/us/networking-iot-servers/wifi-routers/asus-wifi-routers/rtac87u/\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nUbiquiti UniFi Dream Machine Pro https://store.ui.com/collections/unifi-network-unifi-os-consoles/products/udm-pro\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nDell PowerEdge R210 II https://www.dell.com/us/en/business/servers/poweredge-r210-2/pd.aspx?refid=poweredge-r210-2 https://e-mc2.net/files/R210II-SpecSheet.pdf\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nUniFi USW 24 PoE Switch https://store.ui.com/collections/unifi-network-switching/products/usw-24-poe https://e-mc2.net/files/usw-poe-datasheet.pdf\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nUniFi AP-AC HD https://store.ui.com/products/unifi-hd?variant=31217325773 https://e-mc2.net/files/UniFi_UAP-AC-HD_DS.pdf\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nPFSense+ 23.01-RELEASE https://www.pfsense.org/\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2023/04/08","externalUrl":null,"permalink":"/homelab/network/","section":"Emc2Net HomeLab - Intro","summary":"One of the main components in a HomeLab is the network infrastructure.","title":"Emc2Net HomeLab - Network Infrastructure","type":"homelab"},{"content":" Linux KVM server The Linux KVM server is one of the central components in the HomeLab. The server was going to run KVM1 and all the virtual machines in my HomeLab so I spent some time trying to find out a good balance between number og CPU cores, memory size and fast storage before I decided what I needed.\nI wanted to be able to run several virtual machines with some load and good performance without spending thousands of krones on new enterprise level hardware. A new enterprise server was out of the scope because of the costs.\nI considered to buy an old second hand server for a while but I didn\u0026rsquo;t make up my mind at the end, especially because enterprise servers usually consume a lot of power and produce a lot of noise when running, and two of the main requirements for my HomeLab were to have as low power consumption and as low noise level as possible.\nI decided to build the \u0026ldquo;server\u0026rdquo; myself with some quality components, specially when it came to the motherboard, memory and storage. And this is what I put together as my \u0026ldquo;HomeLab server\u0026rdquo; in April 2021.\nServer rack case # Inter-Tech IPC 4U-4408 4U Chassis I wanted to have a server case that used around 3-4U of the 18U I had available in my rack cabinet. I wanted to have also as many external hot swap drive bays as possible and enough internal space to have several components and fans so I could have enough cooling.\nFinally I decided to get a case from Inter-Tech, the Inter-Tech IPC 4U-4408 4U Storage Chassis2. This case had decend quality, it was not to expensive, it had enough internal space and 8 external hot swap drive bays. The case came with a Hot-Swap Backplane with SFF-8087 connectors supporting SATA and SAS harddrives.\nMy Rack cabinet has a depth of 80cm so having a 52cm depth server chassis was also perfect so I could have enough space in the back for connections and air flow. The 3-4U gave me the opportunity of using bigger fans, so I could have a higher airflow inside the case with less RPMs and less noise.\nMotherboard # ASUS Pro WS X570-Ace ATX To build this server I wanted to have a stable and reliable motherboard with support for the AMD Ryzen CPU, at least 64GB of memory and if possible ECC support. I didn\u0026rsquo;t need wifi, many extra USB ports or fancy colors. After multiple searches I found the ASUS Pro WS X570-Ace ATX Motherboard3 aimed mainly at professional users and workstations use.\nThis is a high-end motherboard, and it had the main requirements I needed for my \u0026ldquo;HomeLab server\u0026rdquo;. Efficient heatsinks and an enhanced power solution with alloy chokes and durable capacitors for stable power delivery was also important as this machine was going to be powered almost 24/7.\nCPU # I had two main requirements for the CPU, it had to consume as little power as possible and it had to have as many cores as possible without expending a fortune. AMD Ryzen 9 3900X I checked some Intel Xeon CPUs but they were too expensive for my budget and they needed server motherboards, I also checked some Intel core i9 CPUs, but at the time none of them were cheaper, faster or had more cores than the CPU I finally chose for the server, the AMD Ryzen 9 3900X 3.8GHz Socket AM4 Prosessor4.\nThe AMD Ryzen 9 3900X is a CPU with a TDP of 105W and 12 cores/24 threads, aimed at the desktop marked but with plenty of power for my HomeLab server.\nMemory # To choose the memory was not difficult, I just read the motherboard documentation and chose ECC memory from a supported vendor with available DIMMS at the moment, in total 96G of memory installed in a Dual Channel Memory Architecture.\n2 x Kingston 32GB 3200MHz DDR4 ECC CL22 DIMM 2Rx8 MicronE [KSM32ED8/32ME]5 2 x Kingston 16GB 3200MHz DDR4 ECC CL22 DIMM [KSM32ED8/16HD]6 Power supply # Again, I wanted to have a stable and reliable power supply, silent, modular, efficient and able to deliver as clean power as possible to the components in the server. I chose the Corsair HX750i 750W 80 PLUS Platinum7, a 750W / 80 PLUS Platinum power supply with 10 years warranty and enough capacity to power all the components in the server and to grow in the future.\nAt full CPU and storage load, the server consumes around 240-250W, that is only 32-33% of the total capacity of this power supply. Under normal load the server consumes between 80W and 100W.\nController # Megaraid SAS 9341-8i The controller I chose for the server was the Broadcom Megaraid SAS 9341-8i8 with 2 ports and the possibility of connecting all 8 drive bays to it with two Mini-SAS-HD (SFF-8643) to Mini-SAS (SFF-8087) cables between the controller and the backplane.\nThe controller does a decent job running all the disks as JBOD devices so they are ready to be used with the ZFS filesystem running in the system.\nMore information about this controller and what I did with it when I installed it in the server can be read in this article I wrote some time ago \u0026ldquo;Megaraid SAS 9341-8i on Linux - Cooling and initialization issues\u0026rdquo;\nStorage # The server case has 8 external hot swap drive bays and a Backplane with 2 x SFF-8087 connectors (1 per 4 disks) that can be used to connect 8 disks to a controller.\nI installed 7 x \u0026ldquo;HPE 1,92TB SAS 12G VO1920JEUQQ SSD\u0026rdquo;9 second hand enterprise disks, one for the operative system and six in a \u0026ldquo;ZFS pool configuration\u0026rdquo;10 using raidz1-0 with one extra spare disk and a total of 6,7TB of netto diskspace.\nThe last drive bay is used by 1 x WD Ultrastar DC HC310 6TB 3.5\u0026quot; Serial ATA-60011 disk used for local backups.\nIn addition the server has 1 x Samsung 980 Pro / PCIe 4.0 / 1TB / NVMe M.2 228012 used as cache for the ZFS pool and as a fast disk for virtual machines that need high speed/IO storage.\nFans / cooling # Noctua NF-A8 ULN 80mm The server case has space for 2 x 80mm fans on the back and 4 x 80mm fans on the front, the case came with 2 fans installed on the back of the case and none in the front. I decided to upgrade the 2 fans on the back and install 4 new fans on the front, all of them from Noctua:\n2 x Noctua NF-A8 PWM 80mm13 4 x Noctua NF-A8 ULN 80mm14 The 4x Noctua NF-A8 ULN 80mm were installed on the front of the case (They deliver an intake airflow of around 101m³/h at the lowest speed and around 138m³/h at full speed) and the 2x Noctua NF-A8 PWM 80mm were installed on the back of the case (They deliver an outtake airflow of around 88m³/h at the lowest speed and around 111m³/h at full speed.)\nNoctua NH-CS14 CPU cooler This configuration gives between 73,8 and 100,8 LFM through the cross sectional area of the case. The idea was to have a server case with positive air pressure, this prevents dust from penetrating into the chassis by using filters on the intake fans and forces air out of the server case through unfiltered vents and gaps.\nFor cooling the CPU I used a Noctua NH-CS1415 CPU cooler with an extra Noctua NF-A14 PWM 140mm16 fan, both fans blowing the air away from the CPU. The idle Tctl temperature of the CPU is around 31C. When using the CPU at 100% with all 12cores/24threads at full speed, the Tctl temperature gets up to 70-71C.\nFor cooling the Megaraid SAS 9341-8i disk controller I used a Noctua NF-A4x10 FLX 40mm17 fan. I wrote an article about this some time ago \u0026ldquo;Megaraid SAS 9341-8i on Linux - Cooling and initialization issues\u0026rdquo;. The idle ROC temperature of the LSISAS3008 chip in this controller is around 52C and 54-55C under load.\nVideo card # For several years I had an MSI GeForce GT 710 1GB18 video card installed in this server for its low power consumption and cooling needs.\nIn July 2026 I upgraded the server with an Asus Dual Nvidia GeForce RTX 5060 Ti OC19 card. This is a PCI Express 5.0 card with 16GB GDDR7 SDRAM/128-bit/28Gbps, and 4608 CUDA cores at 2602MHz, using the newer Blackwell architecture from Nvidia. It has been configured so it can be used for AI tasks and has a theoretical AI performance of 759 TOPs.\nThe server has got Ollama20 installed so it can be used as an AI server running local AI models for testing, prototyping, and local work.\nThe dimensions of the card are 229×120×50mm, so it was perfect for the server. It has been installed in the PCIe x16_1 slot of the motherboard to communicate directly with the CPU, while the MegaRAID controller has been moved to the PCIe x16_2 slot.\nKVM https://www.linux-kvm.org/page/Main_Page\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nInter-Tech IPC 4U-4408 4U Storage Chassis https://www.inter-tech.de/productdetails-142/4U-4408_EN.html https://e-mc2.net/files/D-IPC-4U-4408-e.pdf https://e-mc2.net/files/T-4U-4408.pdf\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nASUS Pro WS X570-ACE: https://www.asus.com/us/Motherboards-Components/Motherboards/Workstation/Pro-WS-X570-ACE/\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nAMD Ryzen 9 3900X 3.8GHz Socket AM4 Prosessor https://www.amd.com/en/products/cpu/amd-ryzen-9-3900xt\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nKinsgston KSM32ED8/32ME https://e-mc2.net/files/KSM32ED8_32ME.pdf\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nKinsgston KSM32ED8/16HD https://e-mc2.net/files/KSM32ED8_16HD.pdf\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nCorsair HX750i 750W 80 PLUS Platinum https://www.corsair.com/us/en/Categories/Products/Power-Supply-Units/hxi-series-config/p/CP-9020072-NA\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nBroadcom Megaraid SAS 9341-8i https://e-mc2.net/files/megaraid_sas_9341-4i_9341_8i_091815.pdf https://e-mc2.net/files/MegaRAID_SAS_9341-4i_-8i_QIG.pdf\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nHPE 1,92TB SAS 12G VO1920JEUQQ SSD https://support.hpe.com/connect/s/product?language=en_US\u0026l5oid=3802118\u0026kmpmoid=1010908600\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nRunning OpenZFS raidz on Linux instead of hardware RAID5 https://e-mc2.net/blog/openzfs-on-ubuntu/\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nWD Ultrastar DC HC310 6TB 3.5\u0026quot; Serial ATA-600 https://www.westerndigital.com/products/internal-drives/data-center-drives/ultrastar-dc-hc310-hdd#0B35946\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nSamsung 980 Pro PCIe 1TB NVMe M.2 2280 https://www.samsung.com/us/computing/memory-storage/solid-state-drives/980-pro-pcie-4-0-nvme-ssd-1tb-mz-v8p1t0b-am/\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nNoctua NF-A8 PWM 80x80mm: https://noctua.at/en/nf-a8-pwm\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nNoctua NF-A8 ULN 80x80mm: https://noctua.at/en/nf-a8-uln\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nNoctua NH-CS14 CPU cooler https://noctua.at/en/nh-c14s\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nNoctua NF-A14 PWM 140mm https://noctua.at/en/products/fan/nf-a14-pwm\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nNoctua NF-A4x10 FLX 40x40mm: https://noctua.at/en/nf-a4x10-flx\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nMSI GeForce GT 710 1GB https://www.msi.com/Graphics-Card/GT-710-1GD3H-LP/Specification\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nAsus Dual Nvidia GeForce RTX 5060 Ti OC https://www.asus.com/motherboards-components/graphics-cards/dual/dual-rtx5060ti-o16g/\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nOllama https://ollama.com/\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2023/04/08","externalUrl":null,"permalink":"/homelab/kvm-server/","section":"Emc2Net HomeLab - Intro","summary":"The Linux KVM server is one of the central components in the HomeLab. The server was going to run KVM[^k] and all the virtual machines in my HomeLab so I spent some time trying to find out a good balance between number og CPU cores, memory size and fast storage before I decided what I needed.","title":"Emc2Net HomeLab - Linux KVM/AI server","type":"homelab"},{"content":"","date":"2024/09/11","externalUrl":null,"permalink":"/tags/electronics/","section":"Tags","summary":"","title":"Electronics","type":"tags"},{"content":" $\u0026gt;whoami # My name is Rafael Martinez Guerrero and I am currently working as a Chief Engineer at the IT Department (Formerly Center for Information Technology / USIT) at the University of Oslo. Over the past years, I have been a member of the Division for IT Infrastructure, focusing on system monitoring, data collection/analytics, automation and trending.\nI got my first computer in the 1980s, an Amstrad CPC 6128 with a Z80 processor and 128Kb of RAM, running CP/M Plus and AMSDOS. Since then, I have used and tried various systems and vendors, but my primary operative system for the past 28 years has always been Linux.\nI am specialized in Linux system administration, PostgreSQL database administration, monitoring and capacity planning, automation, performance tuning, security hardening, high availability and disaster recovery.\nAbout Emc2Net # Emc2Net is my homepage where I have consolidated some of the projects, articles and presentations I had scattered across the Internet since the 1990s. All future articles and projects will also be published on this website.\nFor a long time, I was tired with maintaining different frameworks, modules and technologies for these simple pages, not to mention all the security problems I had to deal with when running dynamic pages. So, in early 2022, I moved Emc2Net from a Drupal/PHP/PostgreSQL system to a simpler solution using HuGo, an open-source static site generator. The theme used in Emc2Net is based on the Blowfish theme. You can visit the old version of Emc2Net in the \u0026ldquo;Internet Archive\u0026rdquo;.\nIf there is one thing I have learned over the years is to keep things as simple as possible to try to avoid problems and over complicated, unstable and unsecure systems.\nThis web is Licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.\n","date":"2024/09/11","externalUrl":null,"permalink":"/","section":"Emc2Net universe","summary":"","title":"Emc2Net universe","type":"page"},{"content":"","date":"2024/09/11","externalUrl":null,"permalink":"/tags/english/","section":"Tags","summary":"","title":"English","type":"tags"},{"content":"","date":"2024/09/11","externalUrl":null,"permalink":"/tags/ibm/","section":"Tags","summary":"","title":"Ibm","type":"tags"},{"content":" History # I acquired my IBM 5150 from a bankruptcy estate, discovering it in the depot of the now-defunct Ring Mekanikk factory in Moelv, Norway. The factory was in the process of being dismantled when I made the trip to pick up this piece of computing history. Despite its years in storage, the 5150 is in surprisingly good shape. There’s no corrosion, and all the original components, including the keyboard and monitor, are intact.\nOriginal Status # While the power supply starts up, the rest of the machine remains unresponsive.\nCurrent status # Machine remains unresponsive\nSpecifications # Model: IBM 5150 / SN: Motherboard: - CPU: Intel P8088 (\u0026lsquo;78 \u0026lsquo;81) L3309985 / 4Mhz RAM: 256Kb Floppy drive: 5 1/4 Harddisk: Yes Monitor: IBM 5151002 / SN:1131102 Keyboard: IBM INDibm 5150o :515X-55-J6952 / PartNo: 4733200 (NO) Power supply: 130W Graphic Card: - Disk Controller: - Floppy Disk Controller: - Troubleshooting # This is the procedure I have used to troubleshoot this computer:\nDisconnect the power supply P8 / P9 connectors to the motherboard and measure voltage values with a multimeter. Result: Normal values within specifications +4.90V, 11,98V, -12V\nMeasure continuity between pins 5,6,7,8 (GND) and pins 1,2,3,4,9,10,11,12 in P8/P9 on the motherboard. Result: Short circuit on pins 10,11,12 (5V)\nDisconect all cards connected to the ISA bus.\nMeasure continuity between pins 5,6,7,8 (GND) and pins 1,2,3,4,9,10,11,12 in P8/P9 on the motherboard. Result: Short circuit on pins 10,11,12 (5V). Short circuit present on the motherboard.\nMeasure System Board Resistance according to specifications in fig.5 / page: 0020-09 of the \u0026ldquo;MAP 0020: Power Start\u0026rdquo; in the \u0026ldquo;IBM Hardware Maintenance Service Manual part number 6280087\u0026rdquo; Result: Feil values on pins 10,11,12.\n","date":"2024/09/11","externalUrl":null,"permalink":"/retrocomputing/ibm_5150_1/","section":"RetroComputing","summary":"I acquired my IBM 5150 from a bankruptcy estate, discovering it in the depot of the now-defunct Ring Mekanikk factory in Moelv, Norway. The factory was in the process of being dismantled when I made the trip to pick up this piece of computing history. Despite its years in storage, the 5150 is in surprisingly good shape. There’s no corrosion, and all the original components, including the keyboard and monitor, are intact.","title":"IBM 5150","type":"retrocomputing"},{"content":"","date":"2024/09/11","externalUrl":null,"permalink":"/tags/ibm5150/","section":"Tags","summary":"","title":"Ibm5150","type":"tags"},{"content":"","date":"2024/09/11","externalUrl":null,"permalink":"/tags/retrocomputing/","section":"Tags","summary":"","title":"Retrocomputing","type":"tags"},{"content":" I’m an 80s kid, which means I had a front-row seat when microcomputers and the first PCs started taking over the world. It was a wild, exciting time to grow up. Everything felt brand new, everyone was sharing knowledge, and there was this incredible sense of freedom in what technology could do for us.\nAs the years went by, I found myself looking back on those days with a lot of nostalgia. That eventually turned into a hobby: I started tracking down and collecting the actual machines that shaped my youth—the ones I was lucky enough to play around with back then, and the ones I spent years dreaming about owning.\nHere are a few of the classic computers I’ve managed to bring home so far.\n","date":"2024/09/11","externalUrl":null,"permalink":"/retrocomputing/","section":"RetroComputing","summary":"","title":"RetroComputing","type":"retrocomputing"},{"content":"","date":"2024/09/11","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"2023/10/09","externalUrl":null,"permalink":"/tags/dataops/","section":"Tags","summary":"","title":"Dataops","type":"tags"},{"content":" Sikker Oktober conference - University of Oslo 2023, Oslo, Norway 2023-10-09 sikker-oktober-2023-pdf.pdf Not available\nSummary # \u0026ldquo;Sikker Oktober (Safe October) at UiO is organized every year in connection with the National Security Month from NORSIS (Norwegian Center for Information Security). The purpose is to increase engagement, awareness, and knowledge about digital security, both among the public and in organizations.\nThis year\u0026rsquo;s national theme focuses on social manipulation, where criminals exploit social media, emails, text messages, phone calls, and other digital channels to scam potential victims, hijack accounts, and steal information and personal data.\nThe presentation introduces UiO\u0026rsquo;s new analytics engine, DataOPS, which is used for logging, monitoring, and security analytics. It begins with a journey back to the 1980s, a time of significant changes in society, politics, the music industry, and especially the computer world, marking the era when \u0026ldquo;ordinary\u0026rdquo; people had the opportunity to own a computer at home, along with all the associated benefits and threats.\nLater on, in the 1990s, another revolution in computing occurred with people gaining home access to the Internet. However, this also introduced new risks, providing those with malicious intent a medium to execute large-scale attacks and profit quickly.\nTherefore, it\u0026rsquo;s crucial to have an overview of our digital environment. We need a platform to assess the effectiveness of our security measures and detect any breaches. DataOPS will assist us in this endeavor.\u0026rdquo;\n","date":"2023/10/09","externalUrl":null,"permalink":"/presentations/dataops-sikker-oktober/","section":"Presentations","summary":"The presentation introduces UiO’s new analytics engine, DataOPS, which is used for logging, monitoring, and security analytics. It begins with a journey back to the 1980s, a time of significant changes in society, politics, the music industry, and especially the computer world, marking the era when “ordinary” people had the opportunity to own a computer at home, along with all the associated benefits and threats.","title":"DataOPS - Data Observability, Protection and Security","type":"presentations"},{"content":"","date":"2023/10/09","externalUrl":null,"permalink":"/tags/fluent-bit/","section":"Tags","summary":"","title":"Fluent-Bit","type":"tags"},{"content":"","date":"2023/10/09","externalUrl":null,"permalink":"/tags/haproxy/","section":"Tags","summary":"","title":"HAproxy","type":"tags"},{"content":"","date":"2023/10/09","externalUrl":null,"permalink":"/tags/logstash/","section":"Tags","summary":"","title":"Logstash","type":"tags"},{"content":"","date":"2023/10/09","externalUrl":null,"permalink":"/tags/opensearch/","section":"Tags","summary":"","title":"Opensearch","type":"tags"},{"content":"These are some of the presentations I have done in Conferences during the years.\n","date":"2023/10/09","externalUrl":null,"permalink":"/presentations/","section":"Presentations","summary":"","title":"Presentations","type":"presentations"},{"content":"","date":"2023/10/09","externalUrl":null,"permalink":"/tags/rabbitmq/","section":"Tags","summary":"","title":"RabbitMQ","type":"tags"},{"content":"","date":"2023/10/09","externalUrl":null,"permalink":"/tags/sysadm/","section":"Tags","summary":"","title":"Sysadm","type":"tags"},{"content":"These are some of the blog articles I have published during the years. Many of the old articles still have a lot of valid and current information even though they were written years ago.\n","date":"2023/08/05","externalUrl":null,"permalink":"/blog/","section":"Blog articles","summary":"","title":"Blog articles","type":"blog"},{"content":"","date":"2023/08/05","externalUrl":null,"permalink":"/tags/linux/","section":"Tags","summary":"","title":"Linux","type":"tags"},{"content":"","date":"2023/08/05","externalUrl":null,"permalink":"/tags/nut/","section":"Tags","summary":"","title":"Nut","type":"tags"},{"content":"","date":"2023/08/05","externalUrl":null,"permalink":"/tags/pfsense/","section":"Tags","summary":"","title":"Pfsense","type":"tags"},{"content":"","date":"2023/08/05","externalUrl":null,"permalink":"/tags/ups/","section":"Tags","summary":"","title":"Ups","type":"tags"},{"content":" Introduction # In this article I am going to explain how I got control of my UPS (Uninterruptible Power Supply) installation using NUT via PFSense+1 so I can monitor and manage the UPS from the main router in my HomeLab. In addition I will show how I have configured Zabbix with a new NUT template for monitoring, alarm generation and long term metrics for my UPS.\nThe UPS I am using with my HomeLab is a PowerWalker VI 3000 RLE2, a line-interactive UPS from BlueWalker GmbH. This UPS is connected via USB to the main router, both the main router and the KVM server together with all VMs and the main Desktop computer in my office are configured to shutdown automatically in a proper way if the battery charge goes under 10% or the left runtime on battery is less than 5min.\nNUT (Network UPS Tools) # As the \u0026ldquo;Network UPS Tools\u0026rdquo; project3 says on its pages, \u0026ldquo;NUT is a collection of programs which provide a common interface for monitoring and administering UPS, PDU and SCD hardware\u0026rdquo;.\nThis collection of programs are divided in three main areas:\nNUT drivers # They are used to communicate with the hardware and provide support for specific UPS models. They implement protocols and port specifications and make it possible for the upsd server program to communicate with the hardware.\nYou have multiple types of drivers supporting different protocols and connection types, SNMP, USBHID, Megatec/Q1 protocol, bridges to PowerMan, Apcupsd daemons, etc. Depending on your hardware and how it is connected to the machine running NUT you will have to use one or the other.\nTo configure the drivers you must use this configuration file:\nups.conf: In this file you will have to define a section per UPS/PDU that the machine is responsible for managing. Details and documentation in UPS.CONF(5)4 NUT server # The NUT server is called upsd. This program is responsible for passing data between the drivers and the client programs via the network.\nTo configure the NUT server you can use these two configuration files:\nupsd.conf - upsd uses this file to control access to the server and set some other miscellaneous configuration values. Details and documentation in UPSD.CONF(5)5 upsd.users - Administrative user definitions for NUT upsd. Details and documentation in UPSD.USERS(5)6 NUT clients # They are used to communicate with the NUT server.\nOne of the most important clients is the upsmon7 program, it provides an essential feature, \u0026ldquo;safe shutdowns when the power fails\u0026rdquo;. Details and documentation in UPSMON(8)7\nNUT and PFSense+ # The PFSense+ router in my HomeLab was the right place to control my UPS. The router should be the last component going down when shuting down the infrastructure and it is usually the one with the longest uptime.\nThe UPS/NUT infrastructure I wanted to implement looks like this:\nThe PowerWalker VI 3000 RLE UPS is connected via an USB cable to the Main PFSense+ router. The Main PFSense+ router, the KVM server and the Desktop PC are connected with Power cables to the UPS. The Main PFSense+ router is responsible for managing the UPS. It uses the usbhid-ups driver to communicate with the UPS, it runs the upsd server and uses the upsmon client in master (primary) mode. The KVM server and the Desktop PC are connected to the PFSense+ router via TCP/IP and using the upsmon client in slave (secondary) mode. PFSense+ has support for NUT with the system package \u0026ldquo;nut / sysutils / 2.8.0_2\u0026rdquo;. After installing this package via \u0026quot;System \u0026gt; Package Manager \u0026gt; Available Packages\u0026quot; I could configure NUT for the described infrastructure at \u0026quot;Services \u0026gt; UPS\u0026quot;.\nUnder the \u0026quot;UPS Settings\u0026quot; tab on that page I could define the type of UPS (USB), the name I wanted to give to the UPS (PowerWalker-VI-3000-RLE) and the type of driver (usbhid-ups) it used.\nAfter this and under the \u0026quot;Advanced settings\u0026quot; section on that page I had to define some extra parameters to implement the infrastructure I was planing to use. This part was a bit tricky because you have sections for additional configuration for upsmon.conf, ups.conf, upsd.conf and upsd.users but not information or documentation about what \u0026ldquo;Additional configuration\u0026rdquo; means at this moment, additional to what?\nLuckily we can check via \u0026quot;Diagnostics \u0026gt; Command Prompt\u0026quot; what these files contain at this time. They can be found under /usr/local/etc/nut/ and a simple cat gives us the information generated and saved by default by PFSense/NUT so we can find out the \u0026ldquo;additional configuration\u0026rdquo; we need in our system.\n# cat /usr/local/etc/nut/upsmod.conf MONITOR PowerWalker-VI-3000-RLE 1 local-monitor x2x2x2x2x2x2x2x2x2x2 master SHUTDOWNCMD \u0026#34;/sbin/shutdown -p +0\u0026#34; POWERDOWNFLAG /etc/killpower # cat /usr/local/etc/nut/ups.conf [PowerWalker-VI-3000-RLE] driver=usbhid-ups port=auto # cat /usr/local/etc/nut/upsd.conf LISTEN 127.0.0.1 LISTEN ::1 # cat /usr/local/etc/nut/upsd.users [admin] password=x3x3x3x3x3x3x3x3x3xj actions=set instcmds=all [local-monitor] password=x2x2x2x2x2x2x2x2x2x2 upsmon master Well, this was informative. At first it looked like everything needed to start getting some information from the UPS locally was in place. The only problem was that the \u0026quot;UPS Status\u0026quot; tab at \u0026quot;Services \u0026gt; UPS\u0026quot; failed to get any information from the UPS.\nAfter searching the Internet I found a message in a forum saying that you need this parameter user = root as \u0026ldquo;Additional configuration\u0026rdquo; for ups.conf for NUT to work with PFSense+. After adding this parameter, NUT started getting information from the UPS without problems.\nThe only things left to configure now were:\nTo define an user in upsd.users I could use to connect with the clients running in the KVM server and my Desktop PC. Configure upsd.conf to listen via the router IP (10.100.100.1) in addition to the localhost interface so it could be available from the network. Add some extra parameters to upsmod.conf so I could send some notifications to SYSLOG for logging purposes. Configure nut.conf and upsmod.conf on the KVM server and my Desktop PC so they could comunicate with the NUT server running in the router and send some notifications locally to SYSLOG for logging purposes. This is what I did under \u0026quot;Services \u0026gt; UPS / Advanced settings\u0026quot; to implement these last changes:\nUnder \u0026quot;Additional configuration lines for upsmon.conf\u0026quot; in PFSense+ I defined these extra lines:\nFINALDELAY 10 HOSTSYNC 15 NOTIFYCMD \u0026#34;/usr/local/sbin/upssched\u0026#34; NOTIFYMSG ONLINE \u0026#34;UPS State[ONLINE] - Normal state\u0026#34; NOTIFYMSG ONBATT \u0026#34;UPS State[ONBATT] - On battery\u0026#34; NOTIFYMSG LOWBATT \u0026#34;UPS State[LOWBATT] - Battery low\u0026#34; NOTIFYMSG FSD \u0026#34;UPS State[FSD] - Starting \u0026#39;Forced Shutdown\u0026#39;\u0026#34; NOTIFYMSG COMMOK \u0026#34;UPS State[COMMOK] - Communication restored\u0026#34; NOTIFYMSG COMMBAD \u0026#34;UPS State[COMMBAD] - Communication lost\u0026#34; NOTIFYMSG SHUTDOWN \u0026#34;UPS State[SHUTDOWN] - Shutting down\u0026#34; NOTIFYMSG REPLBATT \u0026#34;UPS State[REPLBATT] - Replace battery\u0026#34; NOTIFYFLAG ONLINE SYSLOG NOTIFYFLAG ONBATT SYSLOG NOTIFYFLAG LOWBATT SYSLOG NOTIFYFLAG FSD SYSLOG NOTIFYFLAG COMMOK SYSLOG NOTIFYFLAG COMMBAD SYSLOG NOTIFYFLAG SHUTDOWN SYSLOG NOTIFYFLAG REPLBATT SYSLOG Under \u0026quot;Additional configuration lines for ups.conf\u0026quot; in PFSense+ I defined this extra line:\nuser = root Under \u0026quot;Additional configuration lines for upsd.conf\u0026quot; in PFSense+ I defined this extra line:\nLISTEN 10.100.100.1 3493 And under \u0026quot;Additional configuration lines for upsd.users\u0026quot; in PFSense+ I defined these extra lines:\n[remote-monitor] password = x1x1x1x1x1x1x1x1x1x1 upsmon slave These are the changes I did on/etc/nut/nut.conf and /etc/nut/upsmod.conf on the KVM server and my Desktop PC so they could comunicate with the NUT server running in the router and send some notifications locally to SYSLOG for logging purposes:\nroot@server:/etc/nut# cat /etc/nut/nut.conf MODE=netclient root@server:/etc/nut# cat /etc/nut/upsmon.conf MONITOR PowerWalker-VI-3000-RLE@10.100.100.1 1 remote-monitor x1x1x1x1x1x1x1x1x1x1 slave SHUTDOWNCMD \u0026#34;/sbin/shutdown -P +0\u0026#34; POWERDOWNFLAG /etc/killpower NOTIFYCMD \u0026#34;/sbin/upssched\u0026#34; NOTIFYMSG ONLINE \u0026#34;UPS State[ONLINE] - Normal state\u0026#34; NOTIFYMSG ONBATT \u0026#34;UPS State[ONBATT] - On battery\u0026#34; NOTIFYMSG LOWBATT \u0026#34;UPS State[LOWBATT] - Battery low\u0026#34; NOTIFYMSG FSD \u0026#34;UPS State[FSD] - Starting \u0026#39;Forced Shutdown\u0026#39;\u0026#34; NOTIFYMSG COMMOK \u0026#34;UPS State[COMMOK] - Communication restored\u0026#34; NOTIFYMSG COMMBAD \u0026#34;UPS State[COMMBAD] - Communication lost\u0026#34; NOTIFYMSG SHUTDOWN \u0026#34;UPS State[SHUTDOWN] - Shutting down\u0026#34; NOTIFYMSG REPLBATT \u0026#34;UPS State[REPLBATT] - Replace battery\u0026#34; NOTIFYFLAG ONLINE SYSLOG NOTIFYFLAG ONBATT SYSLOG NOTIFYFLAG LOWBATT SYSLOG NOTIFYFLAG FSD SYSLOG NOTIFYFLAG COMMOK SYSLOG NOTIFYFLAG COMMBAD SYSLOG NOTIFYFLAG SHUTDOWN SYSLOG NOTIFYFLAG REPLBATT SYSLOG The two important parameters on the client side are MODE=netclient and MONITOR PowerWalker-VI-3000-RLE@10.100.100.1 1 remote-monitor x1x1x1x1x1x1x1x1x1x1 slave on these configuration files.\nAfter these changes I had NUT up and running and my infrastructure configured to shutdown automatically in a proper way if the power supply disappears and my UPS doesn\u0026rsquo;t have enough battery charge.\nZabbix monitoring using NUT # You can implement some level of monitoring with upsmon and send notifications via multiple channels with upssched. I have done this in all my servers and receive information about different UPS status changes via SYSLOG.\nBut in addition I wanted to implement some real monitoring of several parameters from the UPS so I could have some graphs and proper alarms depending of the status reported by the UPS.\nPFSense+ has support for the Zabbix-agent with the system package \u0026ldquo;zabbix-agent6 / net-mgmt / 1.0.6\u0026rdquo;. After installing the package via \u0026quot;System \u0026gt; Package Manager \u0026gt; Available Packages\u0026quot; I could configure the zabbix-agent at \u0026quot;Services \u0026gt; Zabbix Agent 6\u0026quot;\nAfter this, I created a simple Zabbix Template and linked it to the PFSense+ server host I created in my Zabbix server. The template I created can be found under https://github.com/rafaelma/zabbix-template-ups-nut 8. For this template to work with PFSense+ you have to define these lines in the \u0026quot;Advanced features-\u0026gt;Users Parameters\u0026quot; under \u0026quot;Services \u0026gt; Zabbix Agent 6\u0026quot;:\nUserParameter=get.ups.nut.output[*],[ -f /usr/local/bin/upsc ] \u0026amp;\u0026amp; /usr/local/bin/upsc $2@$1 2\u0026gt;/dev/null UserParameter=get.ups.nut.autodiscovery[*],[ -f /usr/local/bin/upsc ] \u0026amp;\u0026amp; /usr/local/bin/upsc -l $1 2\u0026gt;/dev/null This is the default dashboard I get in Zabbix for my UPS using this template:\nThis template defines some alerts/triggers that will be generated if the UPS is \u0026ldquo;On Battery\u0026rdquo; and discharging, \u0026ldquo;On Line\u0026rdquo; and charging the battery, if the load or charge of the UPS are over/under some limits or if the battery should be replaced. This is a good starting point with a minimum monitoring of the UPS.\nWell, this is all for today, enjoy your PFSense+ with NUT if you have it, and if not, I recommend you to get an UPS and install PFSense+ as your router/firewall with NUT to protect your infrastructure when the power supply disappears.\nFootnotes\nPFSense+ 23.05-RELEASE https://www.pfsense.org/\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nUPS PowerWalker VI 3000 RLE https://e-mc2.net/homelab/ups/\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;Network UPS Tools\u0026rdquo; project https://networkupstools.org/\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nUPS.CONF(5) https://networkupstools.org/docs/man/ups.conf.html\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nUPSD.CONF(5) https://networkupstools.org/docs/man/upsd.conf.html\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nUPSD.USERS(5) https://networkupstools.org/docs/man/upsd.users.html\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nUPSMON(8) https://networkupstools.org/docs/man/upsmon.html\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nZabbix template Template-UPS-NUT-simple https://github.com/rafaelma/zabbix-template-ups-nut\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2023/08/05","externalUrl":null,"permalink":"/blog/ups-nut-pfsense-zabbix/","section":"Blog articles","summary":"In this article I am going to explain how I got control of my UPS (Uninterruptible Power Supply) installation using NUT via PFSense+ so I can monitor and manage the UPS from the main router in my HomeLab. In addition I will show how I have configured Zabbix with a new NUT template for monitoring, alarm generation and long term metrics for my UPS.","title":"UPS control with NUT, PFSense+ and Zabbix","type":"blog"},{"content":" HomeLab delivered After years of working within IT and having a very simple infrastructure at home, I decided to invest in some equipment in 2020 so I could have a proper infrastructure and my own HomeLab.\nWhat triggered this decision was probably de COVID pandemic, the extra free time I had to expend at home and the months my family and I had to spent working/studing from home.\nI had some requirements when I started planning for this:\nAll the equipment installed in a rack. A network infrastructure that could be able to cope with the bandwidth, speed and stability requirements needed by several workplaces, with multiple video conferences, VPNs, online school solutions, PCs, mobile phones, etc running concurrently, and from anywhere on the 3 floors of my house. HomeLab installed A server where I could run multiple virtual machines for my home services and testing. More control of what was happening in my network, be able to segment the network into different VLANs and have a proper router/firewall to control the internal traffic and the traffic from/to Internet. Access to my home infrastructure from Internet in a secured and controlled way. As low power consumption as possible. As low noise level as possible. Advance functionality without spending more money than necessary in all this. After several weeks reading, thinking and planning, I started building and realizing this project and in these pages you have information about the process, the components, why they were chosen and how they are used.\n","date":"2023/04/08","externalUrl":null,"permalink":"/homelab/","section":"Emc2Net HomeLab - Intro","summary":"","title":"Emc2Net HomeLab - Intro","type":"homelab"},{"content":"","date":"2023/04/08","externalUrl":null,"permalink":"/tags/hardware/","section":"Tags","summary":"","title":"Hardware","type":"tags"},{"content":"","date":"2023/04/08","externalUrl":null,"permalink":"/tags/home-office/","section":"Tags","summary":"","title":"Home Office","type":"tags"},{"content":"","date":"2023/04/08","externalUrl":null,"permalink":"/tags/homelab/","section":"Tags","summary":"","title":"Homelab","type":"tags"},{"content":"","date":"2023/04/08","externalUrl":null,"permalink":"/tags/kvm/","section":"Tags","summary":"","title":"KVM","type":"tags"},{"content":"","date":"2023/04/08","externalUrl":null,"permalink":"/tags/networking/","section":"Tags","summary":"","title":"Networking","type":"tags"},{"content":"","date":"2023/04/08","externalUrl":null,"permalink":"/tags/racks/","section":"Tags","summary":"","title":"Racks","type":"tags"},{"content":"","date":"2023/02/28","externalUrl":null,"permalink":"/tags/ai/","section":"Tags","summary":"","title":"AI","type":"tags"},{"content":"","date":"2023/02/28","externalUrl":null,"permalink":"/tags/ml/","section":"Tags","summary":"","title":"ML","type":"tags"},{"content":"","date":"2023/02/28","externalUrl":null,"permalink":"/tags/openai/","section":"Tags","summary":"","title":"OpenAI","type":"tags"},{"content":" Introduction # A couple of days ago I read in the specialized press an article1 about how the University of Oslo had implemented an automatic video subtitle creation service. They had used a system called OpenAI Whisper2.\nI don\u0026rsquo;t know why, maybe because I also work at the University of Oslo, but this time I got curious and decided to investigate a little more on the subject. \u0026ldquo;Artificial intelligence\u0026rdquo;3 and \u0026ldquo;Machine learning\u0026rdquo;4 are terms that we are constantly hearing in recent times and often sound like science fiction to many.\nAccording to OpenAI, Whisper is an automatic voice recognition system and has been trained with more than 680,000 hours of audio multilingual and multitasking compiled from the internet. It can be used to transcribe audio in a multitude of languages ​​and to translate these audios into English.\nWhisper uses the Python programming language, the machine learning library PyTorch5, the NumPy6 numerical analysis library, the deep learning model of HuggingFace7 Transformers8 and FFmpeg9 to encode and convert different audio formats and video.\nI am a neophyte in the field and I only have fairly basic theoretical knowledge of how these systems work. I have no practical experience in how to program them internally. Could I install a system like this in my home laboratory without using a lot of resources? Could I get a practical result from its use? Let\u0026rsquo;s see it in this article.\nAbout Whisper internals # According to the project website, \u0026ldquo;Whisper is a general-purpose speech recognition model. It is trained on a large dataset of diverse audio and is also a multi-task model that can perform multilingual speech recognition as well as speech translation and language identification.\u0026rdquo;\n\u0026ldquo;A Transformer sequence-to-sequence model is trained on various speech processing tasks, including multilingual speech recognition, speech translation, spoken language identification, and voice activity detection. All of these tasks are jointly represented as a sequence of tokens to be predicted by the decoder, allowing for a single model to replace many different stages of a traditional speech processing pipeline. The multitask training format uses a set of special tokens that serve as task specifiers or classification targets.\u0026rdquo;\nThat\u0026rsquo;s a very short explanation, The paper \u0026ldquo;Robust Speech Recognition via Large-Scale Weak Supervision\u0026rdquo;10 has the full explanation of how the system works.\nInstallation and configuration # The first thing I did was to create a virtual machine running Ubuntu 22.04.2 LTS in the KVM server at my homeLab. I didn\u0026rsquo;t know how many resources I would need so I assigned 24G RAM and 10x vCPUs to this VM. The KVM server has 96G RAM in total and an \u0026ldquo;AMD Ryzen™ 9 3900X 3.8GHz\u0026rdquo;11 with 12 CPU cores and 24 threads, so the machine had plenty of resources still available.\nUbuntu 22.04.2 comes with Python 3.10.6 so this version was more than enough for running Whisper. FFmpeg and PIP were not installed by default so I installed them:\nsudo apt update \u0026amp;\u0026amp; sudo apt install ffmpeg python3-pip Then and according to the short documentation in the OpenAI/Whisper github pages12 I installed Whisper via PIP. This command installed whisper and all the Python modules needed by Whisper\npip install -U openai-whisper Was this all?, in theory I had everything I needed installed and ready to be used.\nUse and Performance # To begin this section I have to say that the introduction section of this article has been generated by OpenAI Whisper, this was my test case. I created an audio file with my voice and the introduction in Spanish. Then I used Whisper, first to transcribe the audio to text and see if it recognized my \u0026ldquo;Andalusian accent\u0026rdquo;13 when I speak Spanish, and second to translate the audio into English, before copying and pasting the text without modifications into this article.\nI must say that I am impressed with the result after so little effort on my part.\nThe introduction I read in Spanish into an audio file had 311 words and 1979 characters. The audio file had a duration of 2min 12s, a size of 2.4M and it was created with Audacity14:\nrafael@server:~$ file introduction-es.mp3 introduction-es.mp3: MPEG ADTS, layer III, v1, 128 kbps, 44.1 kHz, JntStereo rafael@server:~$ ls -lh total 2,4M -rw-rw-r-- 1 rafael rafael 2,4M Feb 28 13:28 introduction-es.mp3 The next thing I had to do was to use Whisper to generate the text transcription of the audio. I used the large model in whisper to get better results. The first time you run this command Whisper will download the model (around 2.87G) used to analyze the audio and generate the transcription.\nrafael@test-whisper:~$ time whisper introduction-es.mp3 --model large --language es --task transcribe real\t11m34.645s user\t85m41.837s sys\t27m55.946s It took 11m34s to finish, the Whisper process used 100% of all CPUs in the VM, 12G VIRT and 9G RES memory and used between 160-170W during the execution, around 0.032759 kWh to generate the transcription.\nThe result was perfect, Whisper created a perfect transcription15 of the audio I had produced in Spanish16. Impressive, taking into account that I use my Andalusian accent when creating the audio.\nBy default, Whisper creates trascription files in these formats JSON, SRT, TSV, TXT, VTT\nrafael@test-whisper:~/art-es$ ls -lh total 2.4M -rw-rw-r-- 1 rafael rafael 2.4M Feb 28 12:29 introduction-es.mp3 -rw-rw-r-- 1 rafael rafael 12K Feb 28 12:42 introduction-es.mp3.json -rw-rw-r-- 1 rafael rafael 2.7K Feb 28 12:42 introduction-es.mp3.srt -rw-rw-r-- 1 rafael rafael 2.3K Feb 28 12:42 introduction-es.mp3.tsv -rw-rw-r-- 1 rafael rafael 2.0K Feb 28 12:42 introduction-es.mp3.txt -rw-rw-r-- 1 rafael rafael 2.6K Feb 28 12:42 introduction-es.mp3.vtt For example, you could use one of these files to embedded the transcription as subtitles in a video.\nThe translation of the audio from Spanish to english was generated with this command:\nrafael@test-whisper:~$ time whisper introduction-es.mp3 --model large --language es --task translate real\t9m3.948s user\t67m29.470s sys\t21m7.019s It took less time to generate the translation, around 9min but the resource use was almost the same as under the transcription process.\nThe result as you can see in the \u0026ldquo;Introduction\u0026rdquo; section of this article was also very good and it was an accurate translation of the original in Spanish.\nThe performance was not great in my VM with 10x vCPUs, if the generation time grows linearly, it would have taken around 5,2hours to transcript a 1hour audio and around 4hours to translate the same audio.\nI suppose this is the reason the University of Oslo is using the HPC17 infrastructure at the university to generate the transcriptions of the video production they have. They manage to produce 20 hours of transcriptions in 1 hour, that is impressive also, it would have taken my VM around 104hours and 17.68kWh of energy to do the same jobb.\nAre you ready for some magic in your PC with minimal effort? OpenAI/Whisper is waiting for you.\nFootnotes\nArticle: \u0026ldquo;Bygde tjeneste som sparer dem for 20 millioner i året: − Dette er ny og sjokkerende teknologi\u0026rdquo;: https://www.digi.no/artikler/bygde-tjeneste-som-sparer-dem-for-20-millioner-i-aret-dette-er-ny-og-sjokkerende-teknologi-br/526412?key=6gN6miwh\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;OpenAI / Whisper\u0026rdquo;: https://openai.com/research/whisper\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;Artificial intelligence\u0026rdquo;: https://en.wikipedia.org/wiki/Artificial_intelligence\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;Machine learning\u0026rdquo;: https://en.wikipedia.org/wiki/Machine_learning\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;PyTorch\u0026rdquo;: https://en.wikipedia.org/wiki/PyTorch\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;NumPy\u0026rdquo;: https://en.wikipedia.org/wiki/NumPy\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;Hugging face\u0026rdquo;: https://huggingface.co/\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;Transformers - Machine learning models\u0026rdquo;: https://en.wikipedia.org/wiki/Transformer_(machine_learning_model)\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;FFmpeg\u0026rdquo;: https://en.wikipedia.org/wiki/FFmpeg\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;Robust Speech Recognition via Large-Scale Weak Supervision\u0026rdquo; https://cdn.openai.com/papers/whisper.pdf\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;AMD Ryzen™ 9 3900X 3.8GHz\u0026rdquo;: https://www.amd.com/en/products/cpu/amd-ryzen-9-3900x\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;OpenAI/Whisper github\u0026rdquo;: https://github.com/openai/whisper\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;Andalusian Spanish\u0026rdquo;: https://en.wikipedia.org/wiki/Andalusian_Spanish https://en.wikipedia.org/wiki/Andalusians\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;Audacity\u0026rdquo;: https://www.audacityteam.org/\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;Introduction in spanish - transcription\u0026rdquo; https://e-mc2.net/files/whisper-introduction-in-es-transcription.txt\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;Introduction in spanish - original\u0026rdquo; https://e-mc2.net/files/whisper-introduction-in-es-original.txt\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;HPC\u0026rdquo;: https://en.wikipedia.org/wiki/High-performance_computing https://www.uio.no/english/services/it/research/hpc/\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2023/02/28","externalUrl":null,"permalink":"/blog/openai-speech-recognition/","section":"Blog articles","summary":"Will OpenAI/Whisper recognized my “Andalusian accent” when I speak Spanish and transcribe the audio to text without problems? I had to use 0.032759 kWh of energi to find this out.","title":"OpenAI/Whisper, Machine learning and speech recognition on my HomeLab","type":"blog"},{"content":"","date":"2022/12/15","externalUrl":null,"permalink":"/tags/openzfs/","section":"Tags","summary":"","title":"Openzfs","type":"tags"},{"content":" Introduction # For a long time I have had the idea of installing and using the file system ZFS on my Linux home server. ZFS has been available for many years, first on Sun Microsystems’ OpenSolaris in 2005 and later on FreeBSD in 2008, but for copyright reasons it was never included in the official Linux kernel and because of this the adoption of ZFS on Linux was delayed for years.\nShortly after Oracle purchased Sun Microsystems in 2010, ZFS became closed source, and many of the ZFS developers were not happy about this, some of them left the company and started the OpenZFS(1) project (More history at the \u0026ldquo;ZFS Wikipedia page\u0026rdquo;(2))\nAs the OpenZFS project says on its web: \u0026ldquo;OpenZFS is an outstanding storage platform that encompasses the functionality of traditional filesystems, volume managers, and more, with consistent reliability, functionality and performance across all distributions\u0026rdquo;. In other words, it is easy to administrate and it improves security, reliability and performance on your system.\nSome of the main features of OpenZFS are:\nPooled storage Copy-on-write Snapshots Data integrity verification and automatic repair RAID-Z Maximum 16 Exabyte file size Maximum 256 Quadrillion Zettabytes storage The best of all is that some Linux distributions have started supporting OpenZFS in their core distribution and they patch and maintain the kernel and tools needed to use OpenZFS, so we don\u0026rsquo;t have to do that jobb.\nUbuntu introduced OpenZFS support in 2019 so I am in luck because my home server is running Ubuntu-server 22.04 LTS, now it is time to get my hands dirty with this. Is OpenZFS as good as its reputation says? In this article I install and test OpenZFS on Linux and see how it replaces my hardware RAID5 installation.\nPreparing the disks # I have a RAID5 device on my server using 5 SSD disks and 1 dedicated hot spare disk. All of them are connected to a Megaraid SAS 9341-8i (Check my article \u0026ldquo;Megaraid SAS 9341-8i on Linux - Cooling and initialization issues\u0026rdquo;(3) for more information). This is the RAID5 I am going to replace with OpenZFS although I will continue using the same controller.\nThis is my RAID5 configuration:\nroot@server:~# /opt/MegaRAID/storcli/storcli64 /c0 /dall show ...... TOPOLOGY : ======== --------------------------------------------------------------------------- DG Arr Row EID:Slot DID Type State BT Size PDC PI SED DS3 FSpace TR --------------------------------------------------------------------------- 0 - - - - RAID5 Optl N 6.983 TB dflt N N dflt N N 0 0 - - - RAID5 Optl N 6.983 TB dflt N N dflt N N 0 0 0 62:1 2 DRIVE Onln N 1.745 TB dflt N N dflt - N 0 0 1 62:2 1 DRIVE Onln N 1.745 TB dflt N N dflt - N 0 0 2 62:3 0 DRIVE Onln N 1.745 TB dflt N N dflt - N 0 0 3 62:4 5 DRIVE Onln N 1.745 TB dflt N N dflt - N 0 0 4 62:5 6 DRIVE Onln N 1.745 TB dflt N N dflt - N 0 - - 62:6 4 DRIVE DHS - 1.745 TB - - - - - N --------------------------------------------------------------------------- This configuration has been working stable and without problems for several months, so let\u0026rsquo;s hope it continues like that after all the changes I am going to do to the system.\nThese are the disks I have available on my home server:\nroot@server:~# /opt/MegaRAID/storcli/storcli64 /c0 /eall /sall show ...... Drive Information : ================= -------------------------------------------------------------------------------- EID:Slt DID State DG Size Intf Med SED PI SeSz Model Sp Type -------------------------------------------------------------------------------- 62:0 7 JBOD - 5.458 TB SATA HDD N N 512B HGST HUS726T6TALE6L4 U - 62:1 2 Onln 0 1.745 TB SAS SSD N N 512B VO1920JEUQQ U - 62:2 1 Onln 0 1.745 TB SAS SSD N N 512B VO1920JEUQQ U - 62:3 0 Onln 0 1.745 TB SAS SSD N N 512B VO1920JEUQQ U - 62:4 5 Onln 0 1.745 TB SAS SSD N N 512B VO1920JEUQQ U - 62:5 6 Onln 0 1.745 TB SAS SSD N N 512B VO1920JEUQQ U - 62:6 4 DHS 0 1.745 TB SAS SSD N N 512B VO1920JEUQQ U - 62:7 3 JBOD - 1.746 TB SAS SSD N N 512B VO1920JEUQQ U - -------------------------------------------------------------------------------- 62:0 is a backup disk and 62:7 my OS disk. The rest 62:1-6 are in use by the RAID5 device and are the ones I am going to use with OpenZFS.\nThe first thing I have to do is to destroy the RAID5 and define the disks as JBOD disks so OpenZFS can have full access to them without any interference from the controller. I hope this will work because the documentation says that you should not use raid controllers with OpenZFS at all. My controller does not have memory for caching, the OS can see my physical disks without problems and I can get all the S.M.A.R.T information from the disks when defined as JBOD disks by the controller. I think it will work without problems, we\u0026rsquo;ll see. I have tried to find extended information about JBOD vs HBA modes without any luck. It seems to me that it depends a lot on the controller.\nAfter deleting the RAID5 and defining the disks as JBOD disks the system looks like this:\nroot@server:~# /opt/MegaRAID/storcli/storcli64 /c0 /v0 show ...... Virtual Drives : ============== --------------------------------------------------------------- DG/VD TYPE State Access Consist Cache Cac sCC Size Name --------------------------------------------------------------- 0/0 RAID5 Optl RW Yes NRWTD - ON 6.983 TB RAID-5 --------------------------------------------------------------- root@server:~# /opt/MegaRAID/storcli/storcli64 /c0 /v0 del Status = Success Description = Delete VD succeeded root@server:~# /opt/MegaRAID/storcli/storcli64 /c0 /e62 /s1-6 set jbod root@server:~# /opt/MegaRAID/storcli/storcli64 /c0 /e62 /sall show ...... Drive Information : ================= -------------------------------------------------------------------------------- EID:Slt DID State DG Size Intf Med SED PI SeSz Model Sp Type -------------------------------------------------------------------------------- 62:0 7 JBOD - 5.458 TB SATA HDD N N 512B HGST HUS726T6TALE6L4 U - 62:1 2 JBOD - 1.746 TB SAS SSD N N 512B VO1920JEUQQ U - 62:2 1 JBOD - 1.746 TB SAS SSD N N 512B VO1920JEUQQ U - 62:3 0 JBOD - 1.746 TB SAS SSD N N 512B VO1920JEUQQ U - 62:4 5 JBOD - 1.746 TB SAS SSD N N 512B VO1920JEUQQ U - 62:5 6 JBOD - 1.746 TB SAS SSD N N 512B VO1920JEUQQ U - 62:6 4 JBOD - 1.746 TB SAS SSD N N 512B VO1920JEUQQ U - 62:7 3 JBOD - 1.746 TB SAS SSD N N 512B VO1920JEUQQ U - -------------------------------------------------------------------------------- The next thing I have to do is to identify the disks I am going to use with OpenZFS. For that I need the deviceID the OS sees and uses to identify these disks (62:1-6):\n-------------------------------------------------------------------------------- EID:Slt DID State DG Size Intf Med SED PI SeSz Model Sp Type -------------------------------------------------------------------------------- 62:1 2 JBOD - 1.746 TB SAS SSD N N 512B VO1920JEUQQ U - 62:2 1 JBOD - 1.746 TB SAS SSD N N 512B VO1920JEUQQ U - 62:3 0 JBOD - 1.746 TB SAS SSD N N 512B VO1920JEUQQ U - 62:4 5 JBOD - 1.746 TB SAS SSD N N 512B VO1920JEUQQ U - 62:5 6 JBOD - 1.746 TB SAS SSD N N 512B VO1920JEUQQ U - 62:6 4 JBOD - 1.746 TB SAS SSD N N 512B VO1920JEUQQ U - -------------------------------------------------------------------------------- The DID column (Device ID) gives us the IDs the OS sees. So IDs 0,1,2,4,5 and 6 are the IDs I have to identify and use with OpenZFS. I can use the information in the directories /dev/disk/by-path/ and /dev/disk/by-id/ to identify the disks and use the correct disk identifier from /dev/disk/by-id/ when creating my first OpenZFS Pool:\nroot@server:~# ls -l /dev/disk/by-path/ total 0 lrwxrwxrwx 1 root root 13 Dec 10 20:28 pci-0000:01:00.0-nvme-1 -\u0026gt; ../../nvme0n1 lrwxrwxrwx 1 root root 9 Dec 11 16:31 pci-0000:06:00.0-scsi-0:0:0:0 -\u0026gt; ../../sda lrwxrwxrwx 1 root root 9 Dec 11 16:31 pci-0000:06:00.0-scsi-0:0:1:0 -\u0026gt; ../../sdb lrwxrwxrwx 1 root root 9 Dec 11 16:31 pci-0000:06:00.0-scsi-0:0:2:0 -\u0026gt; ../../sdc lrwxrwxrwx 1 root root 9 Dec 11 16:31 pci-0000:06:00.0-scsi-0:0:3:0 -\u0026gt; ../../sdd lrwxrwxrwx 1 root root 9 Dec 11 16:31 pci-0000:06:00.0-scsi-0:0:4:0 -\u0026gt; ../../sde lrwxrwxrwx 1 root root 9 Dec 11 16:31 pci-0000:06:00.0-scsi-0:0:5:0 -\u0026gt; ../../sdf lrwxrwxrwx 1 root root 9 Dec 11 16:31 pci-0000:06:00.0-scsi-0:0:6:0 -\u0026gt; ../../sdg lrwxrwxrwx 1 root root 9 Dec 11 16:31 pci-0000:06:00.0-scsi-0:0:7:0 -\u0026gt; ../../sdh root@server:~# ls -l /dev/disk/by-id/ lrwxrwxrwx 1 root root 13 Dec 10 20:28 nvme-Samsung_SSD_980_PRO_1TB_S5GXXXXXXXXXXXX -\u0026gt; ../../nvme0n1 lrwxrwxrwx 1 root root 15 Dec 10 20:29 nvme-Samsung_SSD_980_PRO_1TB_S5GXXXXXXXXXXXX-part1 -\u0026gt; ../../nvme0n1p1 lrwxrwxrwx 1 root root 15 Dec 10 20:29 nvme-Samsung_SSD_980_PRO_1TB_S5GXXXXXXXXXXXX-part2 -\u0026gt; ../../nvme0n1p2 lrwxrwxrwx 1 root root 9 Dec 11 16:31 scsi-SATA_HGST_HUS726T6TAL_V9HXXXX7 -\u0026gt; ../../sdh lrwxrwxrwx 1 root root 9 Dec 11 16:31 scsi-SHP_VO1920JEUQQ_0SYXXXX5 -\u0026gt; ../../sdf lrwxrwxrwx 1 root root 9 Dec 11 16:31 scsi-SHP_VO1920JEUQQ_0SYXXXX2 -\u0026gt; ../../sdc lrwxrwxrwx 1 root root 9 Dec 11 16:31 scsi-SHP_VO1920JEUQQ_0SYXXXX0 -\u0026gt; ../../sda lrwxrwxrwx 1 root root 9 Dec 11 16:31 scsi-SHP_VO1920JEUQQ_0SYXXXX4 -\u0026gt; ../../sde lrwxrwxrwx 1 root root 9 Dec 11 16:31 scsi-SHP_VO1920JEUQQ_0SYXXXX3 -\u0026gt; ../../sdd lrwxrwxrwx 1 root root 9 Dec 11 16:31 scsi-SHP_VO1920JEUQQ_0SYXXXX6 -\u0026gt; ../../sdg lrwxrwxrwx 1 root root 9 Dec 11 16:31 scsi-SHP_VO1920JEUQQ_0SYXXXX1 -\u0026gt; ../../sdb These are the disks I will use with OpenZFS: scsi-SHP_VO1920JEUQQ_0SYXXXX0, scsi-SHP_VO1920JEUQQ_0SYXXXX1, scsi-SHP_VO1920JEUQQ_0SYXXXX2, scsi-SHP_VO1920JEUQQ_0SYXXXX4, scsi-SHP_VO1920JEUQQ_0SYXXXX5,scsi-SHP_VO1920JEUQQ_0SYXXXX6\nInstalling and configuring ZFS # As I say at the beginning, Ubuntu has full OpenZFS support in the core system and the only thing I have to do to install and activate OpenZFS is to run this command as root:\nroot@server:~# apt install zfsutils-linux This installs all necessary packages and activates the OpenZFS modules in the kernel. I can check the version and if the modules have been loaded with:\nroot@server:~# zfs version zfs-2.1.4-0ubuntu0.1 zfs-kmod-2.1.4-0ubuntu0.1 root@server:~# lsmod |grep zfs zfs 3825664 8 zunicode 348160 1 zfs zzstd 491520 1 zfs zlua 163840 1 zfs zavl 20480 1 zfs icp 323584 1 zfs zcommon 106496 2 zfs,icp znvpair 98304 2 zfs,zcommon spl 118784 6 zfs,icp,zzstd,znvpair,zcommon,zavl After this, everything should be ready to start using OpenZFS.\nThe first thing I need to do is to define an OpenZFS Pool. This can be done with the command zpool create. I am going to call this pool data-zfs-pool01 and use raidz with 5 data disks and 1 spare disk. I use the disk identifiers from /dev/disk/by-id/\nroot@server:~# zpool create data-zfs-pool01 raidz scsi-SHP_VO1920JEUQQ_0SYXXXX0 scsi-SHP_VO1920JEUQQ_0SYXXXX1 scsi-SHP_VO1920JEUQQ_0SYXXXX2 scsi-SHP_VO1920JEUQQ_0SYXXXX4 scsi-SHP_VO1920JEUQQ_0SYXXXX5 spare scsi-SHP_VO1920JEUQQ_0SYXXXX6 root@server:~# zpool status pool: data-zfs-pool01 state: ONLINE config: NAME STATE READ WRITE CKSUM data-zfs-pool01 ONLINE 0 0 0 raidz1-0 ONLINE 0 0 0 scsi-SHP_VO1920JEUQQ_0SYXXXX0 ONLINE 0 0 0 scsi-SHP_VO1920JEUQQ_0SYXXXX1 ONLINE 0 0 0 scsi-SHP_VO1920JEUQQ_0SYXXXX2 ONLINE 0 0 0 scsi-SHP_VO1920JEUQQ_0SYXXXX4 ONLINE 0 0 0 scsi-SHP_VO1920JEUQQ_0SYXXXX5 ONLINE 0 0 0 spares scsi-SHP_VO1920JEUQQ_0SYXXXX6 AVAIL errors: No known data errors As simple as this, I have my first OpenZFS Pool created, available and ready to use.\nWith OpenZFS I also have the option of having dedicated disks for cache and logs. I am not sure how this works yet, I will have to investigate the impact of this on how resources are used when using this functionality. But that is content for another article, for now I just activate this functionality using a NVME device I have on this server.\nroot@server:~# zpool add data-zfs-pool01 cache nvme-Samsung_SSD_980_PRO_1TB_S5GXXXXXXXXXXXX-part1 root@server:~# zpool add data-zfs-pool01 log nvme-Samsung_SSD_980_PRO_1TB_S5GXXXXXXXXXXXX-part2 root@server:~# zpool status pool: data-zfs-pool01 state: ONLINE config: NAME STATE READ WRITE CKSUM data-zfs-pool01 ONLINE 0 0 0 raidz1-0 ONLINE 0 0 0 scsi-SHP_VO1920JEUQQ_0SYXXXX0 ONLINE 0 0 0 scsi-SHP_VO1920JEUQQ_0SYXXXX1 ONLINE 0 0 0 scsi-SHP_VO1920JEUQQ_0SYXXXX2 ONLINE 0 0 0 scsi-SHP_VO1920JEUQQ_0SYXXXX4 ONLINE 0 0 0 scsi-SHP_VO1920JEUQQ_0SYXXXX5 ONLINE 0 0 0\tlogs\tnvme-Samsung_SSD_980_PRO_1TB_S5GXXXXXXXXXXXX-part2 ONLINE 0 0 0 cache nvme-Samsung_SSD_980_PRO_1TB_S5GXXXXXXXXXXXX-part1 ONLINE 0 0 0 spares scsi-SHP_VO1920JEUQQ_0SYXXXX6 AVAIL errors: No known data errors With an OpenZFS Pool in place the next step is to create a ZFS file system with the zfs create command, I call it libvirt:\nroot@server:~# zfs create data-zfs-pool01/libvirt root@server:~# zfs list NAME USED AVAIL REFER MOUNTPOINT data-zfs-pool01 132G 6.71T 447K /data-zfs-pool01 data-zfs-pool01/libvirt 132G 6.71T 132G /data-zfs-pool01/libvirt The file system and the pool are mounted automatically after they are created:\nroot@server:~# df -h |grep zfs data-zfs-pool01 6.8T 512K 6.8T 1% /data-zfs-pool01 data-zfs-pool01/libvirt 6.9T 133G 6.8T 2% /data-zfs-pool01/libvirt And finally I activate ZFS compression on the new file system with the zfs set command:\nroot@server:~# zfs set compression=on data-zfs-pool01/libvirt root@server:~# zfs get compression NAME PROPERTY VALUE SOURCE data-zfs-pool01 compression off default data-zfs-pool01/libvirt compression on local This is actually all I need to do to start using OpenZFS on Linux.\nTesting a disk failure # Before I start using my new OpenZFS infrastructure I want to test what would happen if a disk in my OpenZFS Pool stops working.\nThe first thing I need to do is to activate the autoreplace functionality in OpenZFS so the resilvering process with the spare disk starts automatically.\nroot@server:~# zpool set autoreplace=on data-zfs-pool01 root@server:~# zpool get autoreplace NAME PROPERTY VALUE SOURCE data-zfs-pool01 autoreplace on local What better test than removing one disk from its enclosure. Right after doing this the ZFS pool command shows how the pool goes into a DEGRADED state, the disk I have pulled out goes into an UNAVAIL state and the hot spare disk replaces the failed disk. It also starts what they call resilvering, which is the process of rebuilding the data saved on the disk that failed, from the parity information in the raidz1.\nThe resilvering process takes around 1 minutte and rebuilds around 33G of data, that is a rebuild speed of around 563MB/s. Last time I had to rebuild a RAID5 on this home server it took several hours.\nroot@server:~# zpool status pool: data-zfs-pool01 state: DEGRADED status: One or more devices is currently being resilvered. The pool will continue to function, possibly in a degraded state. action: Wait for the resilver to complete. scan: resilver in progress since Sat Dec 10 20:35:17 2022 165G scanned at 4.86G/s, 84.7G issued at 2.49G/s, 165G total 17.0G resilvered, 51.24% done, 00:00:32 to go config: NAME STATE READ WRITE CKSUM data-zfs-pool01 DEGRADED 0 0 0 raidz1-0 DEGRADED 0 0 0 scsi-SHP_VO1920JEUQQ_0SYXXXX0 ONLINE 0 0 0 spare-1 DEGRADED 0 0 0 scsi-SHP_VO1920JEUQQ_0SYXXXX1 UNAVAIL 3 36 0 scsi-SHP_VO1920JEUQQ_0SYXXXX6 ONLINE 0 0 0 (resilvering) scsi-SHP_VO1920JEUQQ_0SYXXXX2 ONLINE 0 0 0 scsi-SHP_VO1920JEUQQ_0SYXXXX4 ONLINE 0 0 0 scsi-SHP_VO1920JEUQQ_0SYXXXX5 ONLINE 0 0 0 logs\tnvme-Samsung_SSD_980_PRO_1TB_S5GXNF0NC32025M-part2 ONLINE 0 0 0 cache nvme-Samsung_SSD_980_PRO_1TB_S5GXNF0NC32025M-part1 ONLINE 0 0 0 spares scsi-SHP_VO1920JEUQQ_0SYXXXX6 INUSE currently in use errors: No known data errors As soon as the resilvering process is finished, I detache the failed disk from the pool with this command:\nroot@server:/data-zfs-pool01# zpool detach data-zfs-pool01 scsi-SHP_VO1920JEUQQ_0SYXXXX1 And the OpenZFS Pool gets the ONLINE state back.\nroot@server:/data-zfs-pool01# zpool status pool: data-zfs-pool01 state: ONLINE scan: resilvered 33.1G in 00:01:07 with 0 errors on Sat Dec 10 20:36:24 2022 config: NAME STATE READ WRITE CKSUM data-zfs-pool01 ONLINE 0 0 0 raidz1-0 ONLINE 0 0 0 scsi-SHP_VO1920JEUQQ_0SYXXXX0 ONLINE 0 0 0 scsi-SHP_VO1920JEUQQ_0SYXXXX6 ONLINE 0 0 0 scsi-SHP_VO1920JEUQQ_0SYXXXX2 ONLINE 0 0 0 scsi-SHP_VO1920JEUQQ_0SYXXXX4 ONLINE 0 0 0 scsi-SHP_VO1920JEUQQ_0SYXXXX5 ONLINE 0 0 0 logs\tnvme-Samsung_SSD_980_PRO_1TB_S5GXNF0NC32025M-part2 ONLINE 0 0 0 cache nvme-Samsung_SSD_980_PRO_1TB_S5GXNF0NC32025M-part1 ONLINE 0 0 0 errors: No known data errors After this test, I reverse all the changes and my OpenZFS Pool has again all the disks available and an AVAIL hot spare disk.\nSome performance numbers # Before I destroyed the hardware RAID5 to use the disks with OpenZFS I run a few test, nothing fancy, just a simple dd command to write/read a 100G file.\nroot@server:/data-raid/test# /sbin/sysctl -w vm.drop_caches=3 vm.drop_caches = 3 root@server:/data-raid/test# dd if=/dev/zero of=./test.img bs=256k count=409600 409600+0 records in 409600+0 records out 107374182400 bytes (107 GB, 100 GiB) copied, 57.5399 s, 1.9 GB/s root@server:/data-raid/test# /sbin/sysctl -w vm.drop_caches=3 vm.drop_caches = 3 root@server:/data-raid/test# rm test.img root@server:/data-raid/test# dd if=/dev/zero of=./test.img bs=256k count=409600 oflag=direct 409600+0 records in 409600+0 records out 107374182400 bytes (107 GB, 100 GiB) copied, 480.16 s, 224 MB/s root@server:/data-raid/test# /sbin/sysctl -w vm.drop_caches=3 vm.drop_caches = 3 root@server:/data-raid/test# dd if=./test.img of=/dev/null bs=256k count=409600 409600+0 records in 409600+0 records out 107374182400 bytes (107 GB, 100 GiB) copied, 53.2423 s, 2.0 GB/s The same tests with OpenZFS are faster than with the old hardware RAID5:\nroot@server:/data-zfs-pool01# /sbin/sysctl -w vm.drop_caches=3 vm.drop_caches = 3 root@server:/data-zfs-pool01# dd if=/dev/zero of=./test.img bs=256k count=409600 409600+0 records in 409600+0 records out 107374182400 bytes (107 GB, 100 GiB) copied, 48.164 s, 2.2 GB/s root@server:/data-zfs-pool01# rm test.img root@server:/data-zfs-pool01# /sbin/sysctl -w vm.drop_caches=3 vm.drop_caches = 3 root@server:/data-zfs-pool01# dd if=/dev/zero of=./test.img bs=256k count=409600 oflag=direct 409600+0 records in 409600+0 records out 107374182400 bytes (107 GB, 100 GiB) copied, 48.0719 s, 2.2 GB/s root@server:/data-zfs-pool01# /sbin/sysctl -w vm.drop_caches=3 vm.drop_caches = 3 root@server:/data-zfs-pool01# dd if=./test.img of=/dev/null bs=256k count=409600 409600+0 records in 409600+0 records out 107374182400 bytes (107 GB, 100 GiB) copied, 34.6761 s, 3.1 GB/s Well, this is all for today. Now I have to learn how to use all the cool features that OpenZFS has to offer.\nFootnotes\nOpenZFS project: https://openzfs.org/\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nZFS history: https://en.wikipedia.org/wiki/ZFS#History\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;Megaraid SAS 9341-8i on Linux - Cooling and initialization issues\u0026rdquo;: https://e-mc2.net/blog/megaraid-sas-9341-8i-not-working-with-linux/\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2022/12/15","externalUrl":null,"permalink":"/blog/openzfs-on-ubuntu/","section":"Blog articles","summary":"Is OpenZFS as good as its reputation says? In this article I install and test OpenZFS on Linux and see how it replaces my hardware RAID5 installation.","title":"Running OpenZFS raidz on Linux instead of hardware RAID5","type":"blog"},{"content":"","date":"2022/12/15","externalUrl":null,"permalink":"/tags/zfs/","section":"Tags","summary":"","title":"Zfs","type":"tags"},{"content":" History # I acquired my IBM 5155 from a retired IBM employee at the main IBM office in Norway. This particular unit was provided to him in the 80s by IBM and served as his personal computer at home for many years. When I picked it up, he was well into his retirement, and the machine had been stored in a dry basement in his house for a long time.\nOriginal Status # Remarkably, the 5155 started up without any issues despite its long period of inactivity. My next steps will involve giving it a thorough cleaning and conducting a series of tests to ensure everything functions as it should.\nCurrent status # The computers starts and work without problems.\n","date":"2022/10/15","externalUrl":null,"permalink":"/retrocomputing/ibm_5155_1/","section":"RetroComputing","summary":"I acquired my IBM 5155 from a retired IBM employee at the main IBM office in Norway. This particular unit was provided to him in the 80s by IBM and served as his personal computer at home for many years. When I picked it up, he was well into his retirement, and the machine had been stored in a dry basement in his house for a long time.","title":"IBM 5155","type":"retrocomputing"},{"content":"","date":"2022/10/15","externalUrl":null,"permalink":"/tags/ibm5155/","section":"Tags","summary":"","title":"Ibm5155","type":"tags"},{"content":"","date":"2022/04/13","externalUrl":null,"permalink":"/tags/megaraid/","section":"Tags","summary":"","title":"Megaraid","type":"tags"},{"content":"Not long ago I got some nice second-hand SAS drives that I could use with my home server. I didn\u0026rsquo;t have a SAS controller that I could use with these drives at that time, so I started looking for one. I didn\u0026rsquo;t want to spend too much money and the specifications I wanted were not very advanced. I wanted a controller with these caracteristics:\nPCIe 3.0 or 4.0. Support for 6Gb/s SATA and 12Gb/s SAS disks. Support for JBOD, RAID-0,1,5. RAID-10,50 would be nice to have. Support for at least 8 disk (Disk slots in my home server) Support for Linux Working with my \u0026ldquo;ASUS Pro WS X570-ACE\u0026rdquo; motherboard running an \u0026ldquo;AMD Ryzen9 3900X\u0026rdquo; CPU. Stable and reliable. Controllers with battery backup and extra memory were to expensive for my budget and needs.\nAfter some investigation I decided to purchase a Broadcom MegaRaid SAS 9341-8i controller. The SAS 9341-8i is aim to medium business (SMB) owners and gaming enthusiasts, and it had all the caracteristics in my list. This controller is a bit old but it had the functionality I was looking for at an affordable price and it could be delivered in a reasonable time in these days of delivery problems and disruptions in global supply chains.\nCooling the controller # I had read some reports of the controller getting very hot sometimes as this controller is aim for servers where the airflow is usually higher than on a home system. The documentation1 of the controller says that the minimum airflow must be at least 75 linear feet per minute (LFPM) at an ambient/operational temperature range of 0C to +55C. It doesn\u0026rsquo;t say if the 75 LFPM is:\nThrough the cross sectional area of the server case2 (W:44cm H:17cm) Through the chip heat sink (ca 40x40mm) Through the cross sectional area of the PCIe slot used by the controller. It doesn\u0026rsquo;t say either at which temperature you need at least 75 FPM. I am inclined to think that we are talking about an inlet air temperature of around 20-25C and either alternative [2] or [3].\nI decided to contact Broadcom support to get some more information about the temperature and airflow requirements. I didn\u0026rsquo;t get all the information I asked for but I got enough after some enquiries. They said that the LSISAS3008 SAS controller used by the SAS 9341-8i has a maximum junction temperature of 115C (+/- 5°C) and that it will start throttling the performance of the chip and will slow down between 105C and 110C, producing a significant performance loss when this occurs. In the logs from the controller it will be noted that the controller is too hot.\nSo the first thing I did when I received the controller was to install an extra fan3 on top of the heat sink of the LSISAS3008 chip. I installed a Noctua NF-A4x10 FLX 40x40mm fan4. This fan could deliver 8,2m³/h at top speed right into the heat sink. This gives us a value of 280.23 LFM for the rectangular area of the heat sink.\nIn addition, I took into consideration the airflow going through the case. My home server has 4x Noctua NF-A8 ULN 80mm5 on the front of the case (They deliver an intake airflow of around 101m³/h at the lowest speed and around 138m³/h at full speed) and 2x Noctua NF-A8 PWM 80mm6 on the back of the case (They deliver an outtake airflow of around 88m³/h at the lowest speed and around 111m³/h at full speed.) This configuration gives between 73,8 and 100,8 LFM through the cross sectional area of the case. The idea was also to have a server case with positive air pressure, this prevents dust from penetrating into the chassis by using filters on the intake fans and forces air out of the server case through unfiltered vents and gaps.\nWith this configuration I am geting a ROC temperature (LSISAS3008 junction temperature) of 56C at idle and around 58C under heavy load with an ambient temperature of 22C. According to Broadcom support this temperature is optimal for the LSISAS3008 SAS controller.\nroot@server:~$ /opt/MegaRAID/storcli/storcli64 /c0 show temperature ....... Controller Properties : ===================== -------------------------------------- Ctrl_Prop Value -------------------------------------- ROC temperature(Degree Celsius) 56 -------------------------------------- Problems initializing the controller # After installing the cooling fan on the controller, mounting the card into one of the PCIe 16x slots on my motherboard and connecting the two Mini-SAS-HD (SFF-8643) to Mini-SAS (SFF-8087) cables between the controller and the backplane for the 8 drive slots, I was ready to start the server and configurate the controller and my virtual drives. The server was already installed with an OS and was running at that time Ubuntu-server 21.10 with a kernel-5.13.\nIt was a surprise when the Linux kernel discovered the controller but could not initialize it. I thought this controller was 100% supported by Linux, but after some output about the megaraid card I got these two errors and the controller was definitely not operational:\nmegaraid_sas 0000:06:00.0: Failed to transition controller to ready from megasas_init_fw! megaraid_sas 0000:06:00.0: Failed from megasas_init_fw 639 It was time to start searching the internet, I didn\u0026rsquo;t get smarter from the results and I found very lite information about what I could do with this problem. I few posts about it, but without a clear solution, only people talking about BIOS, UEFI boot and different kernel versions.\nI tried different parameters in my BIOS and some combinations with different kernels, but I didn\u0026rsquo;t get anywhere. Then, to activate UEFI I had to reinstall the server, what was my surprise to see the installation crashing because it could not initialize the raid controller.\nI came across an article in the Linux kernel list from an user with the same symptoms I had with my system https://lkml.kernel.org/linux-scsi/ce228d78-8651-d958-d1e5-2f82cbb8113d@qubes-os.org/ I contacted him to find out if he got his system up and running but he confirmed that he never got any answer from Broadcom even after several requests.\nI was going to give up, but in a last desperate attempt before uninstalling the card, I came across this page https://forum.proxmox.com/threads/raid-controller-9341-8i.88667/ and this gave me the clue to solve the problem I was having with my Megaraid SAS 9341-8i, I have to use iommu=soft when booting the kernel.\nWhat I did then was this:\nIn BIOS: Just in case, I defined GEN3 (instead of auto) for the PCIe slot where I had the card installed (My motherboard is an \u0026ldquo;ASUS Pro WS X570-ACE\u0026rdquo;7 with PCIe-4.0 and the SAS 9341-8i uses PCIe-3.0) In BIOS: \u0026ldquo;IOMMU is enable\u0026rdquo; was enable. In BIOS: Under boot configuration, CSM for storage devices was \u0026ldquo;Only uefi\u0026rdquo;. Installed the OS with UEFI activated. In OS: Defined this line in /etc/default/grub: GRUB_CMDLINE_LINUX_DEFAULT=\u0026quot;iommu=soft\u0026quot; and ran update-grub to install the changes. After these changes, a reboot of the server got everything recognized and initialized.\nNOTE If you are installing the OS from scratch with the controller already inserted into a PCIe slot, you will have to pass the option iommu=soft to the kernel when starting the installation program to avoid a crash and be able to install the OS. An alternative will be to do all the changes before inserting the controller into a PCIe slot.\nWhen booting with \u0026ldquo;Storage devices in Only uefi modus\u0026rdquo; you will not get the option to start the bios from the card to configurate it. I have used storcli8 to do this and it works without problems.\n# /opt/MegaRAID/storcli/storcli64 -v StorCli SAS Customization Utility Ver 007.1912.0000.0000 Nov 23, 2021 UPDATE July 2026 PROBLEMS UPGRADING TO A 6.x kernel\nDuring the years the server was upgraded to Ubuntu 22.04.5 LTS running a 5.15.x kernel and everything worked properly and without problems.\nIn July 2026 I installed a new Nvidia GPU in the server that needed a 6.x kernel to work with the latest Nvidia drivers. After this upgrade the Megaraid controller stopped working and refused to work no matter what I tried.\nThe error i got from the megaraid after rebooting with a 6.8.x kernel was:\nkernel: megaraid_sas 0000:08:00.0: Waiting for FW to come to ready state kernel: megaraid_sas 0000:08:00.0: FW in FAULT state, Fault code:0x40000 subcode:0x0 func:megasas_transition_to_ready kernel: megaraid_sas 0000:08:00.0: System Register set: kernel: megaraid_sas 0000:08:00.0: Failed to transition controller to ready from megasas_init_fw! kernel: megaraid_sas 0000:08:00.0: Failed from megasas_init_fw 6548 Reboots after this moment failed with:\nkernel: megaraid_sas 0000:08:00.0: Waiting for FW to come to ready state kernel: megaraid_sas 0000:08:00.0: FW in FAULT state, Fault code:0x40000 subcode:0x0 func:megasas_transition_to_ready kernel: megaraid_sas 0000:08:00.0: System Register set: kernel: megaraid_sas 0000:08:00.0: Failed to transition controller to ready from megasas_init_fw! kernel: megaraid_sas 0000:08:00.0: Failed from megasas_init_fw 6545 When the controller gets in this state, it will not work anymore, no matter what you change in your system. Booting the server back with a 5.15.x kernel won\u0026rsquo;t work either.\nIt looks like the firmware in the controller is put into a locked and crashed state that persists across reboots, and it won\u0026rsquo;t come out of this state. Powering down the server and pulling out the power cord doesn\u0026rsquo;t help either.\nThe 6.8 kernel fails because changed default settings for IOMMU and DMA mapping block older MegaRAID controllers from initializing.\nThe procedure to fix this situation is:\nReboot the server Enter the server BIOS and change the Boot configuration: CSM for storage devices to \u0026ldquo;Legacy support ..\u0026rdquo; Reboot the server Enter the Megaraid BIOS and choose the \u0026ldquo;Factory reset\u0026rdquo; meny Reboot the server Enter the server BIOS and change the Boot configuration: CSM for storage devices to “Only UEFI” Reboot the server and choose a 5.15 kernel in GRUB Defined this line in /etc/default/grub: GRUB_CMDLINE_LINUX_DEFAULT=\u0026quot;iommu=pt\u0026quot; and ran update-grub to install the changes. Reboot the server and choose a 6.8 kernel in GRUB. The megaraid controller should be recognized now. Updating the firmware # The first thing I did when I got the SAS 9341-8i working was to upgrade the firmware9 to the newest version I could find, and reboot the server to verify that everything was working.\nroot@server:~/megaraid/firmware-4.680.01-8554# unzip 24.21.0-0148_SAS_iMR_FW_IMAGE_APP_4.680.01-8554.zip Archive: 24.21.0-0148_SAS_iMR_FW_IMAGE_APP_4.680.01-8554.zip inflating: 24.21.0-0148_SAS_iMR_FW_IMAGE_APP_4.680.01-8554.txt inflating: 240_VD_Feature_Limitations_Known_Issues_Addendum_v1.0.pdf inflating: iMR_4MB.rom root@server:~/megaraid/firmware-4.680.01-8554# /opt/MegaRAID/storcli/storcli64 /c0 download file=./iMR_4MB.rom Download Completed. Flashing image to adapter... CLI Version = 007.1912.0000.0000 Nov 23, 2021 Operating system = Linux 5.13.0-39-generic Controller = 0 Status = Success Description = F/W Flash Completed. Please reboot the system for the changes to take effect Current package version = 24.21.0-0132 New package version = 24.21.0-0148 Interacting with the controller # After a reboot to get the last firmware in production, I could begin interacting with the 9341-8i via the storcli software. The amount of commands and options available is enormous, check the StorCLI documentation10 for full and detail information.\nHere are some examples of how to interact with the controller. The first thing we want to do is get some information about the controller ID and status:\nroot@server:~# /opt/MegaRAID/storcli/storcli64 show ....... Number of Controllers = 1 Host Name = server Operating System = Linux 5.13.0-39-generic System Overview : =============== ------------------------------------------------------------------------------------ Ctl Model Ports PDs DGs DNOpt VDs VNOpt BBU sPR DS EHS ASOs Hlth ------------------------------------------------------------------------------------ 0 AVAGOMegaRAIDSAS9341-8i 8 8 0 0 0 0 N/A On 1\u0026amp;2 Y 2 Opt ------------------------------------------------------------------------------------ ....... Now that we know that our controller has an ID 0. We could get more detailed information about our system with these two commands: /opt/MegaRAID/storcli/storcli64 /c0 show and /opt/MegaRAID/storcli/storcli64 /c0 show all.\nTo get a list of all the disks attached to the controller we can run this:\nroot@server:~# /opt/MegaRAID/storcli/storcli64 /c0 /eall /sall show ....... Drive Information : ================= -------------------------------------------------------------------------------- EID:Slt DID State DG Size Intf Med SED PI SeSz Model Sp Type -------------------------------------------------------------------------------- 62:0 7 JBOD - 5.458 TB SATA HDD N N 512B HGST HUS726T6TALE6L4 U - 62:1 2 UGood - 1.745 TB SAS SSD N N 512B VO1920JEUQQ U - 62:2 1 UGood - 1.745 TB SAS SSD N N 512B VO1920JEUQQ U - 62:3 0 UGood - 1.745 TB SAS SSD N N 512B VO1920JEUQQ U - 62:4 5 UGood - 1.745 TB SAS SSD N N 512B VO1920JEUQQ U - 62:5 6 UGood - 1.745 TB SAS SSD N N 512B VO1920JEUQQ U - 62:6 4 UGood - 1.745 TB SAS SSD N N 512B VO1920JEUQQ U - 62:7 3 JBOD - 1.746 TB SAS SSD N N 512B VO1920JEUQQ U - -------------------------------------------------------------------------------- We could define a RAID-5 virtual disk with the disks 62:1 to 62:5 with:\nroot@server:~# /opt/MegaRAID/storcli/storcli64 /c0 add vd type=raid5 name=RAID-5 drives=62:1-5 And a HotSpare disk 62:6 with:\nroot@server:~# /opt/MegaRAID/storcli/storcli64 /c0/e62/s6 add hotsparedrive Our system would look like this after these changes:\nroot@server:~# /opt/MegaRAID/storcli/storcli64 /c0 /eall /sall show ....... Drive Information : ================= -------------------------------------------------------------------------------- EID:Slt DID State DG Size Intf Med SED PI SeSz Model Sp Type -------------------------------------------------------------------------------- 62:0 7 JBOD - 5.458 TB SATA HDD N N 512B HGST HUS726T6TALE6L4 U - 62:1 2 Onln 0 1.745 TB SAS SSD N N 512B VO1920JEUQQ U - 62:2 1 Onln 0 1.745 TB SAS SSD N N 512B VO1920JEUQQ U - 62:3 0 Onln 0 1.745 TB SAS SSD N N 512B VO1920JEUQQ U - 62:4 5 Onln 0 1.745 TB SAS SSD N N 512B VO1920JEUQQ U - 62:5 6 Onln 0 1.745 TB SAS SSD N N 512B VO1920JEUQQ U - 62:6 4 DHS 0 1.745 TB SAS SSD N N 512B VO1920JEUQQ U - 62:7 3 JBOD - 1.746 TB SAS SSD N N 512B VO1920JEUQQ U - -------------------------------------------------------------------------------- ....... root@server~# /opt/MegaRAID/storcli/storcli64 /c0 /dall show ....... TOPOLOGY : ======== --------------------------------------------------------------------------- DG Arr Row EID:Slot DID Type State BT Size PDC PI SED DS3 FSpace TR --------------------------------------------------------------------------- 0 - - - - RAID5 Optl N 6.983 TB dflt N N dflt N N 0 0 - - - RAID5 Optl N 6.983 TB dflt N N dflt N N 0 0 0 62:1 2 DRIVE Onln N 1.745 TB dflt N N dflt - N 0 0 1 62:2 1 DRIVE Onln N 1.745 TB dflt N N dflt - N 0 0 2 62:3 0 DRIVE Onln N 1.745 TB dflt N N dflt - N 0 0 3 62:4 5 DRIVE Onln N 1.745 TB dflt N N dflt - N 0 0 4 62:5 6 DRIVE Onln N 1.745 TB dflt N N dflt - N 0 - - 62:6 4 DRIVE DHS - 1.745 TB - - - - - N --------------------------------------------------------------------------- This is all for today, I hope this information will help you to use your Megaraid controller with new versions of Linux. I recommend that you familiarize yourself with StorCLI and all the possibilities you have to interact with the Megaraid SAS 9341-8i before you start using it in production.\nFootnotes\n12Gb/s MegaRAID® SAS RAID Controllers: https://e-mc2.net/files/pub-005183_12Gbs_MegaRAID_SAS_RAID_Controllers_User_Guide.pdf\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nInter-Tech IPC 4U-4408 4U Storage Chassis: https://www.inter-tech.de/en/products/ipc/storage-cases/4u-4408\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nMegaraid SAS 9341-8i with Noctua NF-A4x10 FLX 40x40mm fan: https://e-mc2.net/img/_DSC7473.JPG https://e-mc2.net/img/_DSC7474.JPG https://e-mc2.net/img/_DSC7475.JPG\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nNoctua NF-A4x10 FLX 40x40mm: https://noctua.at/en/nf-a4x10-flx\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nNoctua NF-A8 ULN 80x80mm: https://noctua.at/en/nf-a8-uln\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nNoctua NF-A8 PWM 80x80mm: https://noctua.at/en/nf-a8-pwm\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nASUS Pro WS X570-ACE: https://www.asus.com/us/Motherboards-Components/Motherboards/Workstation/Pro-WS-X570-ACE/\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nStorCLI: https://docs.broadcom.com/docs/007.2007.0000.0000_Unified_StorCLI.zip\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nMegaraid SAS 9341-8i Firmware: https://docs.broadcom.com/docs/24.21.0-0148_SAS_iMR_FW_IMAGE_APP_4.680.01-8554.zip\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nStorCLI manual: https://e-mc2.net/files/megaraid-TM-StorCLI-UG108.pdf\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2022/04/13","externalUrl":null,"permalink":"/blog/megaraid-sas-9341-8i-not-working-with-linux/","section":"Blog articles","summary":"Not long ago I got some nice second-hand SAS drives that I could use with my home server. I didn’t have a SAS controller that I could use with these drives at that time, so I started looking for one. I didn’t want to spend too much money and the specifications I wanted were not very advanced.\nAfter some investigation I decided to purchase a Broadcom MegaRaid SAS 9341-8i controller. This controller is a bit old but it had the functionality I was looking for at an affordable price.\n","title":"Megaraid SAS 9341-8i on Linux - Cooling and initialization issues","type":"blog"},{"content":" History # The IBM P70 386 is the first of several IBM machines I have acquired in recent years. I purchased it from someone in Oslo who had owned it since the late 80s. As he was cleaning out his attic, he decided to sell the computer rather than dispose of it.\nOriginal Status # The P70 386 powers up and runs, but the floppy disk drive currently does not work. My project with this machine will involve cleaning it thoroughly and testing its various components to ensure everything functions correctly.\nCurrent status # The computers starts and work without problems.\n","date":"2021/09/01","externalUrl":null,"permalink":"/retrocomputing/ibm_p70_386/","section":"RetroComputing","summary":"The IBM P70 386 is the first of several IBM machines I have acquired in recent years. I purchased it from someone in Oslo who had owned it since the late 80s. As he was cleaning out his attic, he decided to sell the computer rather than dispose of it.","title":"IBM P70/386","type":"retrocomputing"},{"content":"","date":"2021/09/01","externalUrl":null,"permalink":"/tags/ibmp70/","section":"Tags","summary":"","title":"IbmP70","type":"tags"},{"content":" LinuxConf-AU 2020 - Gold Coast, Australia 2020-01-17 linuxconfau-2020-rafael-elk.pdf https://www.youtube.com/watch?v=4X0bmnb4tVI\nSummary # Behind every security measure you take, you should have an information management system helping you take decisions. If you work with security, you need a way to collect, process, save and analyze huge amounts of data that should be used to control how your systems are behaving, find anomalies and evaluate the results of your actions.\nHave you ever wondered how to manage billions of logs and metrics from thousands of devices in your infrastructure? If you need high-availability and a resilient and stable system to process your data this is the tutorial for you.\nBased on the experience obtained in the past 4 years at the University of Oslo processing billions of logs a day from more than 15000 devices, this tutorial will give some inside information and many tips about how to achieve this with Linux and open source software.\nYou will learn how to put together HAProxy, agents, Logstash, Elasticsearch and RabbitMQ to work at scale. You will also hear about the problems and pitfalls we have experienced during these years and what we learned from them.\nThe tutorial format is a lecture with time for questions and short discussions as we move along. Due to the nature of the subject treated and time available, no hands-on or practical exercises will be done during the lecture. The tutorial should provide background, essential theory and the details needed to enable you to work further on your own afterwards.\n","date":"2020/01/17","externalUrl":null,"permalink":"/presentations/behind-scenes-elk-system/","section":"Presentations","summary":"Have you ever wondered how to manage billions of logs and metrics from thousands of devices in your infrastructure? If you need high-availability and a resilient and stable system to process your data this is the tutorial for you.","title":"Behind the scenes of an ELK system","type":"presentations"},{"content":"","date":"2020/01/17","externalUrl":null,"permalink":"/tags/elasticsearch/","section":"Tags","summary":"","title":"Elasticsearch","type":"tags"},{"content":"","date":"2020/01/17","externalUrl":null,"permalink":"/tags/elk/","section":"Tags","summary":"","title":"Elk","type":"tags"},{"content":"After several weeks of intense testing, fixing configuration problems, re-indexing data and experiencing problems when upgrading our Kibana indices, we managed to upgrade our 36 Kibana instances and our Elasticsearch cluster in production from version 5.6.16 to 6.7.1 a couple of weeks ago.\nWe could not believe it but finally we had over 1.300 indices, 100TB of data, 102 000 000 000 documents and 18 Elasticsearch nodes running the last version of the elastic 6.x series at the moment.\nOur joy did not last long, after years of stability with old versions all our hot-nodes, the ones indexing new data, with tons of CPU and state of the art SSD disks went bananas. In a matter of a few hours after the upgrade was finished we were in hell and we did not know what we had done wrong to deserve the situation we were in.\nOur indexing rate was between 10 000 and 20 000 new documents per second during normal operation with 40 000 - 50 000 peaks when something special happened.\nThis is how our CPU use and the old/young collection count / sec looked like during a normal period before we upgraded.\nWe still could index all logs arriving in our system but the performance of the system was seriously affected. Moving data around was a pain and normal operations took ages to finish. Not to mention that we went from having a lot of extra resources and no plans of investing in new hardware to a very uncertain situation.\nWhat had we done wrong during the upgrade? Was the new version so inefficient that it needed 4 or 5 times more resources than the old version? Everything happened so suddenly that we did not see it coming, we did not have a clue of what was happening.\nWe started investigating the situation. The first thing we did was to check all our Grafana dashboards with information about all the components in our system.\nThe amount of data arriving in the system was normal, the network and disk activity was also normal, as well as the amount of searches running. But we had a huge amount of CPU use in some of the hot-nodes in the cluster. Some of them were using 80-90% of their capacity, we are talking about servers with 48CPUs. Another graph that went bananas was the one showing the amount of java heap in use, the nodes using all the CPU power were using also 80-90% of the heap available. I addition the graph showing the amount of GC/sec showed that something was happening in this area also.\nAfter this first evaluation it was clear to us that garbage collection was probably the reason why the servers were using so many resources. But so suddenly? Why? What could be do with this?\nWe are not java experts, we have been running Elasticsearch for years using all the default JVM values except for -Xms and -Xmx, these two values have always been 31G in our servers. We started reading tons of documentation, blogs and forums without finding out something concrete to do. A lot of information about old versions of elasticsearch and even more information about old JVM versions, not to mention the amount of different versions out there, all of them behaving different and using different parameters. This was not going to be easy and in the mean time our production system was in hell.\nIt was obvious that the new version had triggered something in the JVM and/or was using memory in a different way. Maybe we had to much HEAP allocated?, but we were not using more than 50% of the total memory in the servers and 31G was well under the 32G recommended limit, the cutoff that the JVM uses for compressed object pointers (compressed oops), the logs confirmed that:\n[INFO][o.e.e.NodeEnvironment][hostname] heap size [29.9gb], compressed ordinary object pointers [true] Maybe the problem was that the garbage collector used by Elasticsearch with Java 8, ConcMarkSweepGC (CMS) could not cope with 31G of HEAP and how the application was using memory. At least everybody was saying that this GC collector is not optimal if you have a lot of HEAP allocated. The newer G1GC collector was in theory much better with our configuration. The only problem was that Elasticsearch does not support this collector if you are running Java 8, you have to upgrade to Java 11 and we did not think it was a good idea to upgrade to version 11 without testing it before.\nWe tried to decrease the heap size to 28G to see if this was the problem, maybe ConcMarkSweepGC (CMS) could do its job with less heap size allocated. But just as we did this and restarted the nodes, another problem arose. We had been using indices.breaker.total.limit = 55% for years to avoid out og memory problems and when we started the nodes with 28G heap size we started seeing this type of errors:\n[internal:index/shard/recovery/start_recovery]]; nested: CircuitBreakingException[[parent] Data too large, data for [\u0026lt;transport_request\u0026gt;] would be [16215377502/15.1gb], which is larger than the limit of [15984463052/14.8gb], usages [request=0/0b, fielddata=0/0 b, in_flight_requests=64216/62.7kb, accounting=16215313286/15.1gb]]; ], markAsStale [true]] Wow, now we could not recover all indices and for the same reason some of the nodes were \u0026ldquo;disappearing\u0026rdquo; from the cluster. We changed the heap size back to 31G and started the cluster again while we continued thinking about what to do.\nWe were getting also a lot of logs of this type:\n[INFO][o.e.m.j.JvmGcMonitorService][hostname][gc][533] overhead, spent [390ms] collecting in the last [1s] And just a few of this type:\n[WARN][o.e.m.j.JvmGcMonitorService][hostname][gc][old][6546][116] duration [21.3s], collections [1]/[21.4s], total [21.3s]/[16.3m], memory [30.4gb]-\u0026gt;[24.3gb]/[30.7gb], all_pools {[young] [2gb]-\u0026gt;[1.1gb]/[2.1gb]}{[survivor] [176mb]-\u0026gt;[0b]/[274.5mb]}{[old] [28.2gb]-\u0026gt;[23.1gb]/[28.3gb]} Why were we getting so many \u0026ldquo;gc overhead\u0026rdquo; logs? We did not find out the different between these two type of logs, the second one is generated when the GC does its job, but what about the first one?. And why were we using only around 2G out of 31G for the young pool memory? What was the relationship between the young and the old pool? One of the things we were seeing was that the GC count for the young pool was almost 5 times higher than before. Maybe the young pool memory was to small?\nWe spent some time trying to understand how the young and the old pool work and the relationship between them. Again, a lot of old information very dependent of the version you were using. But one thing was common in many places, the ratio between the young and the old pool was normally 1:2 or 1:3 and the default ratio was 1:2 (33% young / 66% old). Well, if this was right our system was not configured right because 2G is around 6.5% of 31G, or almost a 1:14 ratio. What was happening here?\nWe found a place with some good information about how to find the values used by the JVM in your system, we needed to find out which default configuration values the JVM was using. With this command we got a list with all the parameters the JVM was using and their values. Many of these values are computed dynamically by the JVM when Elasticsearch is started if you do not define them explicitly.\njava -Xms31G -Xmx31G -XX:+UseConcMarkSweepGC -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -version In our system, 873 parameters, 77 of them only to configure the ConcMarkSweepGC (CMS) collector we were using.\nToo many options, we focused on the parameters defining the young / old pools, the ratio and the one defining the number of threads used by CG (ParallelGCThreads). This last parameter had to be equal to the number of CPUs in our servers according to the information we found.\n# java -Xms31G -Xmx31G -XX:+UseConcMarkSweepGC -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -version | egrep -i \u0026#34;( NewSize | OldSize | NewRatio | ParallelGCThreads )\u0026#34; uintx NewRatio = 2 {product} uintx NewSize := 2878930944 {product} uintx OldSize := 30407065600 {product} uintx ParallelGCThreads = 33 {product} First surprise, the value of ParallelGCThreads was completely wrong, it showed a value of 33 when the servers had 48CPUs available. The default ratio value was right (NewRatio: 2) but the young (NewSize) and old (OlsSize) pool sizes were wrong, they were not defined using a 1:2 ratio.\nDid the JVM know something that we did not know or was it completely wrong? Well, we had nothing to loose and we had a feeling the JVM was wrong. We run the command again with the parameters we wanted to try and things began to look better:\n# java -Xms31G -Xmx31G -XX:ParallelGCThreads=48 -XX:NewRatio=3 -XX:+UseConcMarkSweepGC -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -version | egrep -i \u0026#34;(NewSize | OldSize | NewRatio | ParallelGCThreads)\u0026#34; uintx NewRatio := 3 {product} uintx NewSize := 8321499136 {product} uintx OldSize := 24964497408 {product} uintx ParallelGCThreads := 48 {product} We read somewhere that 1:3 was a good ratio for Elasticsearch so we tried this first and defined -XX:NewRatio=3 and -XX:ParallelGCThreads=48 explicitily in /etc/elasticsearch/jvm.options file.\nAfter restarting the cluster things began to happen. The amount of GC/sec for young pool decreased over 50% in the hot-nodes but increased for old pool in the cold-nodes. Things got a little better in the hot-nodes but worst in the cold-nodes.\nWe had to continue trying things and the next thing we did was to increase even more the young pool defining -XX:NewRatio=2 (ratio1:2 - 33% young / 66% old).\n# java -Xms31G -Xmx31G -XX:ParallelGCThreads=48 -XX:NewRatio=2 -XX:+UseConcMarkSweepGC -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -version | egrep -i \u0026#34;(NewSize | OldSize | NewRatio | ParallelGCThreads)\u0026#34; uintx NewRatio := 2 {product} uintx NewSize := 11095310336 {product} uintx OldSize := 22190686208 {product} uintx ParallelGCThreads := 48 {product} We also increased the indices.memory.index_buffer_size to 20% to have more memory available for all the shards receiving new data and index.refresh_interval to 30s to decrease the expensive operation of making new documents visible. These three changes had a huge impact in how the cluster worked afterwards. As we can see in the graphs below, the amount of CPU used and GC/sec decreased dramatically and the HEAP used got very stable and predictable.\nIt has been one week since we fixed our Elasticsearch cluster and the situation is still stable and without any problems.\nThings we have learned on the way:\nWe still do not know why the CPU/GC behavior changed radically after upgrading to version 6.7.1. Memory management in Java is not a trivial thing when you have a large system processing a lot of data. The documentation available about GC tuning is nontrivial, very version dependent, old in many cases and not accurate. We need to learn more about how to tune and configure a JVM. You can get in trouble with GC very fast and without prior notice. Garbage collection hell is not a nice place to be. Links:\nhttps://github.com/elastic/elasticsearch/issues/29504#issuecomment-381126640 https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html https://www.elastic.co/blog/a-heap-of-trouble https://www.elastic.co/guide/en/elasticsearch/guide/current/heap-sizing.html ","date":"2019/05/30","externalUrl":null,"permalink":"/blog/elasticsearch-garbage-collection-hell/","section":"Blog articles","summary":"After several weeks of intense testing, fixing configuration problems, re-indexing data and experiencing problems when upgrading our Kibana indices, we managed to upgrade our 36 Kibana instances and our Elasticsearch cluster in production from version 5.6.16 to 6.7.1 a couple of weeks ago.","title":"Elasticsearch in garbage collection hell","type":"blog"},{"content":"","date":"2019/05/30","externalUrl":null,"permalink":"/tags/tunning/","section":"Tags","summary":"","title":"Tunning","type":"tags"},{"content":"","date":"2018/03/06","externalUrl":null,"permalink":"/tags/keepalive/","section":"Tags","summary":"","title":"Keepalive","type":"tags"},{"content":"I am writing this article with contradictory feelings, Am I having a bad day on top of The Oracle at Google not giving me answers, or is the documentation of the Keepalived software totally outdated and old?\nWe are installing a new log receiver for our ELK infrastructure and one of the components in the new system is a load balancer using HAProxy in a cluster configuration for redundancy. At first we considered using pacemaker/corosync to implement the high-availability of HAProxy but you need an extra subscription to use these products with Red Hat Enterprise Linux Server 7.4, so we chose keepalived insteed of pacemaker/corosync. All of them have been around for a long time and they do their job although the level of difficulty when configuring them is different.\nMy odyssey began when I needed some information about how to configure keepalived with both IPV4 and IPV6 addresses. An easy task in theory turned into a couple of hours of tests and failures and reading forum entries in the most remote corners of the internet. Neither the official documentation of Keepalived nor the RedHat documentation of the product had the information needed to do what I wanted to do. What we needed was:\nTwo HAProxies for TCP load balancing, one active and one standby ready to take over if the active one fails. Keepalived using the \u0026ldquo;Virtual Redundancy Routing Protocol (VRRP)\u0026rdquo; to perform failover tasks between the two HAProxies. IPV4 and IPV6 \u0026ldquo;Virtual IP addresses\u0026rdquo; (VIP) The standby HAProxy server should take over the VIP addresses if the active node goes completely down, or if the haproxy service goes down on the active node. This was going to be a piece of cake after reading the \u0026ldquo;Red Hat Enterprise Linux - Load Balancer Administration\u0026rdquo; documentation. We could use \u0026ldquo;vrrp_script\u0026rdquo;, \u0026ldquo;vrrp_instance\u0026rdquo; and \u0026ldquo;track_script\u0026rdquo; to configure keepalived in the way we wanted and Keepalived would take care of moving our VIPs between the HAProxies if the haproxy service went down.\nvrrp_script chk_haproxy { # Check if the haproxy process is running script \u0026#34;killall -0 haproxy\u0026#34; # Check every 2 seconds interval 2 # Add 2 points to priority if OK weight 2 } vrrp_instance VI_1 { # interface to monitor interface ens192 virtual_router_id 1 priority 101 virtual_ipaddress { 10.1.1.100 # VIP-1 ipv4 10.1.1.101 # VIP-2 ipv4 0:0:0:0:0:FFFF:0A01:0164 # VIP-3 ipv6 0:0:0:0:0:FFFF:0A01:0165 # VIP-4 ipv6 } track_script { chk_haproxy } } First surprise, only the IPV4 VIPs got configured, no trace of the IPV6 addresses when we started keepalived.\n[root@elk-lb01]# ip addr sh ens192 2: ens192: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc mq state UP qlen 1000 link/ether 00:50:54:a8:b5:9b brd ff:ff:ff:ff:ff:ff inet 10.1.1.10/24 brd 10.1.1.255 scope global ens192 valid_lft forever preferred_lft forever inet 10.1.1.100/32 scope global ens192 valid_lft forever preferred_lft forever inet 10.1.1.101/32 scope global ens192 valid_lft forever preferred_lft forever This simple configuration did not work with the version of keepalived (1.3.5-1) we were using from the official RedHat repository. The reason, you cannot have IPV4 and IPV6 addresses in the same virtual_ipaddress{} block. I found out in the keepalived github repository why this did not work after googling about it.\n\u0026ldquo;\u0026hellip;. It is not possible to configure both IPv4 and IPv6 addresses as virtual_ipaddresses in a single vrrp_instance; the reason is that the VRRP protocol doesn\u0026rsquo;t support it \u0026hellip;.. Although earlier versions of keepalived didn\u0026rsquo;t complain if IPv4 and IPv6 addresses were both configured, it didn\u0026rsquo;t work properly. \u0026hellip;..\u0026rdquo;\nThe same page had this tip:\n\u0026ldquo;\u0026hellip;. If you need to associate both IPv4 and IPv6 addresses with a single vrrp_instance, then configure the addresses of one family in a virtual_ipaddress_excluded block. Probably a better solution than using a virtual_ipaddress_excluded block is to configure two vrrp instances, one for IPv4 and one for IPv6 \u0026hellip;.\u0026rdquo;\nIt could not be that difficult, two vrrp instances, one for IPv4 and one for IPv6:\nvrrp_script chk_haproxy { # Check the haproxy process script \u0026#34;killall -0 haproxy\u0026#34; # Check every 2 seconds interval 2 # Add 2 points to priority if OK weight 2 } vrrp_instance VI_1 { # interface to monitor interface ens192 virtual_router_id 1 priority 101 virtual_ipaddress { 10.1.1.100 # VIP-1 ipv4 10.1.1.101 # VIP-2 ipv4 } track_script { chk_haproxy } } vrrp_instance VI_2 { # interface to monitor interface ens192 virtual_router_id 2 priority 101 virtual_ipaddress { 0:0:0:0:0:FFFF:0A01:0164 # VIP-3 ipv6 0:0:0:0:0:FFFF:0A01:0165 # VIP-4 ipv6 } track_script { chk_haproxy } } Well, this configured both IPV4 and IPV6 addresses but the 2 keepalived instances configured the two IPV6 addresses at the same time in the two servers, so of course the log files started screaming about it:\nICMPv6: NA: someone advertises our address 0:0:0:0:0:FFFF:0A01:0164 on ens192! Probably this was my fault, because I did not use \u0026ldquo;vrrp_sync_group\u0026rdquo; when having two \u0026ldquo;vrrp_instance\u0026rdquo;. So I added this block to the configuration:\nvrrp_sync_group VI_1_2 { group { VI_1 VI_2 } } The only problem with this was that when using \u0026ldquo;vrrp_sync_group\u0026rdquo; you cannot use \u0026ldquo;track_script\u0026rdquo; in your \u0026ldquo;vrrp_instance\u0026rdquo; to find out if haproxy is running or not, so the VIPs will not get moved to the standby node if haproxy stops working. By the way, the documentation says nothing about it, I found out this because another error in the log file:\nVRRP_Instance(VI_1) : ignoring tracked script with weights due to SYNC group VRRP_Instance(VI_2) : ignoring tracked script with weights due to SYNC group What if I moved the \u0026ldquo;track_script\u0026rdquo; from the \u0026ldquo;vrrp_instance\u0026rdquo; blocks to the \u0026ldquo;vrrp_sync_group\u0026rdquo; block? Well that did not work because \u0026ldquo;vrrp_sync_group\u0026rdquo; does not support \u0026ldquo;track_script\u0026rdquo; blocks. So I was in square one again.\nAt this point I started losing confidence in myself, was it me? what was I missing? How could it be possible that a software that had been widely used for years in production did not have a proper documentation?. My last chance, What about trying the other tip I found in Github?:\n\u0026ldquo;\u0026hellip;. configure the addresses of one family in a virtual_ipaddress_excluded block \u0026hellip;.\u0026rdquo;\nt did not sound logical at first if you asked me. But \u0026ldquo;virtual_ipaddress_excluded\u0026rdquo; contains a list of IP addresses that keepalived will bring up and down on the server, however they are not included in the VRRP packet itself, that is the meaning of this parameter, excluded from the VRRP packet but moved anyway. Well, I tried:\nglobal_defs { router_id elk1 } vrrp_script chk_haproxy { # Check the haproxy process script \u0026#34;killall -0 haproxy\u0026#34; # Check every 2 seconds interval 2 # Add 2 points to priority if OK weight 2 } vrrp_instance VI_1 { # interface to monitor interface ens192 virtual_router_id 1 priority 101 virtual_ipaddress { 10.1.1.100 # VIP-1 ipv4 10.1.1.101 # VIP-2 ipv4 } virtual_ipaddress_excluded { 0:0:0:0:0:FFFF:0A01:0164 # VIP-3 ipv6 0:0:0:0:0:FFFF:0A01:0165 # VIP-4 ipv6 } track_script { chk_haproxy } } Yes, it worked. Both IPV4 and IPV6 addresses got moved to the standby server when the active server went down, or if haproxy/keepalived stopped working in the active server. I could not believe it but everything was working properly at last.\nTo finish I want to thank the developers of keepalived for developing and maintaining this software that works without problems when you configure it right, but the documentation in the official website of the project really has to be updated to avoid high levels of frustration when trying to configure the software.\nPS.- I have found an updated documentation file in a github repository that would really had made a big difference to me when I was trying to configure my case: https://github.com/acassen/keepalived/blob/master/doc/keepalived.conf.SYNOPSIS\nEnjoy it.\nLinks:\nhttps://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/l… https://www.keepalived.org https://github.com/acassen/keepalived/blob/master/doc/keepalived.conf.SYNOPSIS ","date":"2018/03/06","externalUrl":null,"permalink":"/blog/keepalived-documentation-nightmare/","section":"Blog articles","summary":"I am writing this article with contradictory feelings, Am I having a bad day on top of The Oracle at Google not giving me answers, or is the documentation of the Keepalived software totally outdated and old?","title":"Keepalived - A documentation nightmare","type":"blog"},{"content":"","date":"2018/02/01","externalUrl":null,"permalink":"/tags/amstrad/","section":"Tags","summary":"","title":"Amstrad","type":"tags"},{"content":" History # The Amstrad CPC 6128 holds a special place in my heart as it was the first 8-bit computer I ever owned. I received it as a gift from my parents around 1986, and it became my gateway into the world of programming. I spent countless hours on this machine, learning BASIC and CP/M Plus, as well as playing an array of games and writing my own code.\nOriginal Status # After nearly 30 years of being stored in a closet at my parents\u0026rsquo; home in Spain, the CPC 6128 started without problems. However, not all the keys on the keyboard worked, and the floppy disk drive was non-functional.\nCurrent status # While the computer still powers up and functions, the floppy disk drive is currently not operational. My goal is to repair the floppy disk drive and restore the CPC 6128 to its former glory, allowing it to operate as it did back in the day. This project is not just about fixing hardware; it’s a journey back to where my passion for computing all began.\nSpecifications # Model: Amstrad CPC618 / SN: Motherboard: - CPU: Z80 / 4 MHz RAM: 128Kb (64Kb + 64Kb) ROM: 48Kb Floppy drive: Built-in 3\u0026quot; (2 x 180 KB) Harddisk: - Monitor: Color Keyboard: Integrated Power supply: - Graphic Card: - Disk Controller: - Floppy Disk Controller: - Troubleshooting # This is the procedure I have used to troubleshoot this computer:\nDisassemble the computer, clean the interior of dust, and clean the internal keyboard connections.\nConfirm that the floppy disk drive is out of order due to a broken internal drive belt.\n","date":"2018/02/01","externalUrl":null,"permalink":"/retrocomputing/amstrad_cpc6128/","section":"RetroComputing","summary":"The Amstrad CPC 6128 holds a special place in my heart as it was the first 8-bit computer I ever owned. I received it as a gift from my parents around 1986, and it became my gateway into the world of programming. I spent countless hours on this machine, learning BASIC and CP/M Plus, as well as playing an array of games and writing my own code.","title":"Amstrad CPC 6128","type":"retrocomputing"},{"content":"","date":"2018/02/01","externalUrl":null,"permalink":"/tags/cpc6128/","section":"Tags","summary":"","title":"CPC6128","type":"tags"},{"content":"","date":"2017/09/15","externalUrl":null,"permalink":"/tags/automation/","section":"Tags","summary":"","title":"Automation","type":"tags"},{"content":"","date":"2017/09/15","externalUrl":null,"permalink":"/tags/devops/","section":"Tags","summary":"","title":"Devops","type":"tags"},{"content":"","date":"2017/09/15","externalUrl":null,"permalink":"/tags/zabbix/","section":"Tags","summary":"","title":"Zabbix","type":"tags"},{"content":" ZabbixConf 2017 - Riga, Latvia 2017-09-15 zabbixconf2017_uio.pdf https://www.youtube.com/watch?v=fl1vuG_vmkQ\nSummary # A case study showing how Zabbix can deliver monitoring of 60+ web applications across 50+ servers running in a devops environment, where changes happen fast and no one can have control of everything. When developers don\u0026rsquo;t want to spend time configurating a monitoring system, and operations don\u0026rsquo;t have time to learn all the details around systems using the IT infrastructure, they have to talk to each other to find out how to get a system that does the dirty work for them.\nDevops environments can deliver results in an efficient way, but they depend on a high level of automation to reach the goals. Monitoring is only one of the aspects they have to work with and Zabbix can help to achieve the expected result.\n","date":"2017/09/15","externalUrl":null,"permalink":"/presentations/zabbix-devops-environment/","section":"Presentations","summary":"A case study about how the University of Oslo uses Zabbix in a DEVOPS environment where changes happen fast and no one can have control of everything.","title":"Zabbix in a DevOps environment","type":"presentations"},{"content":" Intro # If you have to administrate an Elasticsearch cluster, there are some common maintenance tasks that you will have to run to keep your data growth under control, backup your indexes and keep the cluster updated.\nAt the University of Oslo we have a 14 nodes Elasticsearch 5.x cluster (3 master + 2 clients + 4 SSD data+ 5 SAS data). We use it to manage, search, analyze, and explore our logs. It has around 100TB of total storage, around 1,300 indexes and we keep from 3 to 6-12 months of data per index type depending of the type of data they have.\nSome of the maintenance tasks we are running in our system are:\nMoving old indexes from the fast SSD nodes to the slower ARCHIVE nodes. Deleting old indexes from the ARCHIVE nodes base on a defined retention time. Taking snapshot backups of our indexes Deleting old snapshots backups Upgrades and restarts of nodes/elasticsearch via Ansible. Many of these tasks can be done with a software distributed by Elastic and called \u0026ldquo;Curator\u0026rdquo;. This software has been available for a long time but you need the last version to work with, and take full advantage of the API functionality available with ElasticSearch 5.x. Old versions and examples of Curator available on the Internet will not work with ElasticSearch 5.x.\nMoving old indexes # We do this to have only new data on the fast SSD nodes so the most common searches work faster and to not use expensive SSD storage with old data that is not accessed as often.\nTo move indexes between nodes we use the routing functionality in Elasticsearch. We have tagged our different data nodes with two tags, SSD and ARCHIVE. This is done in the Elasticsearch configuration file with these two parameters:\ncluster.routing.allocation.awareness.attributes: group node.attr.group: SSD cluster.routing.allocation.awareness.attributes: group node.attr.group: ARCHIVE We have created a default index template where we define, among other things, that all new indexes created in the cluster get tagged with the SSD tag so they are created in the SSD nodes.\n{ \u0026#34;order\u0026#34; : 0, \u0026#34;template\u0026#34; : \u0026#34;*\u0026#34;, \u0026#34;settings\u0026#34; : { \u0026#34;index\u0026#34; : { \u0026#34;routing\u0026#34; : { \u0026#34;allocation\u0026#34; : { \u0026#34;include\u0026#34; : { \u0026#34;group\u0026#34; : \u0026#34;SSD\u0026#34; } } }, \u0026#34;refresh_interval\u0026#34; : \u0026#34;5s\u0026#34;, \u0026#34;number_of_shards\u0026#34; : \u0026#34;2\u0026#34;, \u0026#34;number_of_replicas\u0026#34; : \u0026#34;1\u0026#34; } } } An finally we use the program Curator to change the tag of an index from SSD to ARCHIVE, when the index is older than 6 weeks. This will automatically activate the transfer of the index from the SSD to the ARCHIVE data nodes.\nCurator has multiple parameters to configure what to do, read the documentation for full details. In our case, we have this entry in our crontab:\n00 07 * * * root LOGFILE=/var/log/curator/archived-indices-$(/bin/date +\\%Y_\\%m_\\%d-\\%Hh\\%Mm\\%Ss) \u0026amp;\u0026amp; \\ /usr/bin/curator_cli --logfile $LOGFILE \\ --logformat logstash \\ --host es-client.example.net allocation \\ --key group \\ --value ARCHIVE \\ --allocation_type include \\ --filter_list \\ \u0026#39;[{\u0026#34;filtertype\u0026#34;:\u0026#34;age\u0026#34;,\u0026#34;source\u0026#34;:\u0026#34;creation_date\u0026#34;,\u0026#34;direction\u0026#34;:\u0026#34;older\u0026#34;,\u0026#34;unit\u0026#34;:\u0026#34;weeks\u0026#34;,\u0026#34;unit_count\u0026#34;:6}, \\ {\u0026#34;filtertype\u0026#34;:\u0026#34;pattern\u0026#34;,\u0026#34;kind\u0026#34;:\u0026#34;prefix\u0026#34;,\u0026#34;value\u0026#34;:\u0026#34;^\\\\.\u0026#34;,\u0026#34;exclude\u0026#34;:\u0026#34;True\u0026#34;}]\u0026#39; Deleting old indexes # Another important maintenance task in our system is the deletion of old indexes. The amount of data coming into the system is high and it is essential to delete old data to avoid using all the resources.\nOur indexes have the following name pattern, \u0026lt;name-id-YYYY.WW\u0026gt; . We have a python script that connects to the Elasticsearch cluster, gets a list of all the indexes in the cluster and generates a cron file with a cron job for every in the system. These cron jobs use Curator again to delete indexes older than a defined retention time, e.g:\n58 5 * * * root LOGFILE=/var/log/curator/name-id-$(/bin/date +\\%Y_\\%m_\\%d-\\%Hh\\%Mm\\%Ss) \u0026amp;\u0026amp; \\ /usr/bin/curator_cli \\ --logfile $LOGFILE \\ --logformat logstash \\ --host es-client.example.org delete_indices \\ --filter_list \u0026#39;[{\u0026#34;filtertype\u0026#34;:\u0026#34;pattern\u0026#34;,\u0026#34;kind\u0026#34;:\u0026#34;prefix\u0026#34;,\u0026#34;value\u0026#34;:\u0026#34;name-id\u0026#34;},\\ {\u0026#34;filtertype\u0026#34;:\u0026#34;count\u0026#34;,\u0026#34;use_age\u0026#34;:\u0026#34;True\u0026#34;,\u0026#34;source\u0026#34;:\u0026#34;creation_date\u0026#34;,\u0026#34;count\u0026#34;:12}]\u0026#39; \u0026amp;\u0026amp; \\ /bin/rm -f /var/log/curator/name-id-latest \u0026amp;\u0026amp; \\ /usr/bin/ln -s $LOGFILE /var/log/curator/name-id-latest Taking snapshot backups # Elasticsearch has functionality to take backup (snapshots) of the indexes in the cluster.\nFirst, you will have to create a \u0026ldquo;Snapshot Repository\u0026rdquo; in the cluster. You can define different types of repositories, we use the FS type (FileSystem) on a NFS disk available and mounted in all the Elasticsearch nodes.\nSecond, you can define a cron job and use \u0026ldquo;Curator\u0026rdquo; to generate a snapshot of all indexes in your cluster. In our case, we have this entry in our crontab:\n00 23 * * * root LOGFILE=/var/log/curator/snapshots-daily-$(/bin/date +\\%Y_\\%m_\\%d-\\%Hh\\%Mm\\%Ss) \u0026amp;\u0026amp; \\ /usr/bin/curator_cli \\ --host es-client.example.org snapshot \\ --repository=\u0026#34;Daily_snapshot\u0026#34; \\ --wait_for_completion=\u0026#34;true\u0026#34; \\ --include_global_state=\u0026#34;true\u0026#34; \\ --filter_list \u0026#39;[{\u0026#34;filtertype\u0026#34;:\u0026#34;age\u0026#34;,\u0026#34;source\u0026#34;:\u0026#34;creation_date\u0026#34;,\u0026#34;direction\u0026#34;:\u0026#34;older\u0026#34;,\u0026#34;unit\u0026#34;:\u0026#34;days\u0026#34;,\u0026#34;unit_count\u0026#34;:1}]\u0026#39; \u0026amp;\u0026amp; \\ /bin/rm -f /var/log/curator/snapshots-daily-latest \u0026amp;\u0026amp; \\ /usr/bin/ln -s $LOGFILE /var/log/curator/snapshots-daily-latest To be fair, these snapshots can be very practical if you want or need to restore one or a few indexes, but I do not want to think how long it will take to restore a whole cluster in case of a mayor disaster. We are getting around 1.50-2.00GBps of data into the NFS disk when taking snapshots. Assuming that we could get the same speed when restoring data, we would need more than 72 hours for the 50TB of log data we have today.\nDeleting old snapshots backups # Of course, if you take snapshots of your indexes, you should think about how long you want to keep this backups. We are keeping one week of snapshots and you can also use \u0026ldquo;Curator\u0026rdquo; to delete old snapshots. In our case, we have this entry in our crontab:\n01 05 * * * root LOGFILE=/var/log/curator/snapshots-delete-$(/bin/date +\\%Y_\\%m_\\%d-\\%Hh\\%Mm\\%Ss) \u0026amp;\u0026amp; \\ /usr/bin/curator_cli \\ --timeout=86400 \\ --logfile $LOGFILE \\ --host es-client.example.org delete_snapshots \\ --repository=\u0026#34;Daily_snapshot\u0026#34; \\ --retry_interval=600 \\ --retry_count=4 \\ --filter_list \u0026#39;[{\u0026#34;filtertype\u0026#34;:\u0026#34;age\u0026#34;,\u0026#34;source\u0026#34;:\u0026#34;creation_date\u0026#34;,\u0026#34;direction\u0026#34;:\u0026#34;older\u0026#34;,\u0026#34;unit\u0026#34;:\u0026#34;days\u0026#34;,\u0026#34;unit_count\u0026#34;:7}]\u0026#39; \u0026amp;\u0026amp; \\ /bin/rm -f /var/log/curator/snapshots-delete-latest \u0026amp;\u0026amp; \\ /usr/bin/ln -s $LOGFILE /var/log/curator/snapshots-delete-latest Ansible playbooks for upgrades and restarts # From time to time, you need to execute some maintenance tasks in your Elasticsearch cluster to keep it updated or to change your configuration. When running these tasks you will have to restart Elasticsearch, and this means that the nodes will have to leave the cluster and join it again. The good news are that we can keep the cluster online and operational when running these maintenance tasks as long as we follow the right procedure.\nThe most common tasks involving the restart of the Elasticsearch process are:\n(A) Minor Elasticsearch upgrade to a new version.\nDisable shard allocation on the cluster Run an index flush sync Upgrade Elasticsearch to a new version Restart the Elasticsearch process Wait for the node to rejoin the cluster Enable shard allocation on the cluster Wait for the Elasticsearch cluster state to become green (B) Restart of Elasticsearch process to set a new configuration in production.\nDisable shard allocation on the cluster Run an index flush sync Restart the Elasticsearch process Wait for the node to rejoin the cluster Enable shard allocation on the cluster Wait for the Elasticsearch cluster state to become green (C) Restart of Elasticsearch node to e.g. set in production a new kernel.\nDisable shard allocation on the cluster Run an index flush sync Stop Elasticsearch Restart the Elasticsearch node Wait for the Elasticsearch node to reboot Start Elasticsearch Wait for the node to rejoin the cluster Enable shard allocation on the cluster Wait for the Elasticsearch cluster state to become green We use Ansible to run these procedures on all our Elasticsearch nodes sequentially. As you can see, the procedures to execute these tasks are very similar, they only differ from each other on a few steps. We use Ansible to run these procedures on all our Elasticsearch nodes sequentially. Here you have an example of Ansible code you can use to implement them. You should implement this code via Ansible roles/task and handlers to reuse all the common code.\nFor (A) you could use something like this:\n- hosts: prod-all-nodes # Important to run the procedure sequentially in all servers defined in prod-all-nodes serial: 1 vars: es_client_server: es-client.example.net elasticsearch_version: 0.0.0-1 tasks: # Disable shard allocation - name: \u0026#34;Set cluster routing allocation to none {{ansible_hostname}}\u0026#34; action: \u0026#34;shell curl -XPUT http://{{ es_client_server }}:9200/_cluster/settings -d \u0026#39;{\\\u0026#34;transient\\\u0026#34; : {\\\u0026#34;cluster.routing.allocation.enable\\\u0026#34; : \\\u0026#34;none\\\u0026#34; }}\u0026#39;\u0026#34; register: result until: result.stdout.find(\u0026#39;\u0026#34;acknowledged\u0026#34;\u0026#39;) != -1 retries: 200 delay: 3 changed_when: result.stdout.find(\u0026#39;\u0026#34;acknowledged\u0026#34;:true\u0026#39;) != -1 # Index Flush sync - name: \u0026#34;Index Flush sync - {{ansible_hostname}}\u0026#34; action: \u0026#34;shell curl -s -m 2 http://{{ es_client_server }}:9200/_flush/synced?pretty\u0026#34; register: result until: result.rc == 0 retries: 200 delay: 3 # Upgrade elasticsearch version via YUM - name: \u0026#34;Elasticsearch upgrade on {{ansible_hostname}}\u0026#34; yum: name=elasticsearch-{{ elasticsearch_version }} state=present enablerepo=elasticsearch-5.x retries: 1000 delay: 10 # restart elasticsearch process - name: \u0026#34;Elasticsearch restart {{ansible_hostname}}\u0026#34; service: name=elasticsearch state=restarted # Wait for Elasticsearch node to come back into cluster - name: \u0026#34;Wait for elasticsearch running on node {{ansible_hostname}}\u0026#34; action: \u0026#34;shell curl -s -m 2 http://{{ es_client_server }}:9200/_cat/nodes?h=name | tr -d \u0026#39; \u0026#39; | grep -E \u0026#39;^{{ansible_hostname}}\u0026#39; \u0026#34; register: result until: result.rc == 0 retries: 200 delay: 3 # Enable shard allocation - name: \u0026#34;Set cluster routing allocation to all {{ansible_hostname}}\u0026#34; action: \u0026#34;shell curl -s -m 2 -XPUT http://{{ es_client_server }}:9200/_cluster/settings -d \u0026#39;{\\\u0026#34;transient\\\u0026#34; : {\\\u0026#34;cluster.routing.allocation.enable\\\u0026#34; : \\\u0026#34;all\\\u0026#34; }}\u0026#39;\u0026#34; register: result until: result.stdout.find(\u0026#34;acknowledged\u0026#34;) != -1 retries: 200 delay: 3 changed_when: result.stdout.find(\u0026#39;\u0026#34;acknowledged\u0026#34;:true\u0026#39;) != -1 # Wait until cluster status is green - name: \u0026#34;Wait for green cluster status {{ansible_hostname}}\u0026#34; action: \u0026#34;shell curl -s -m 2 http://{{ es_client_server }}:9200/_cat/health | cut -d \u0026#39; \u0026#39; -f 4\u0026#34; register: result until: result.stdout.find(\u0026#34;green\u0026#34;) != -1 retries: 5000 delay: 10 For (B) you can use the same code as (A) without the install block:\n- hosts: prod-all-nodes # Important to run the procedure sequentially in all servers defined in prod-all-nodes serial: 1 vars: es_client_server: es-client.example.net tasks: # Disable shard allocation - name: \u0026#34;Set cluster routing allocation to none {{ansible_hostname}}\u0026#34; action: \u0026#34;shell curl -XPUT http://{{ es_client_server }}:9200/_cluster/settings -d \u0026#39;{\\\u0026#34;transient\\\u0026#34; : {\\\u0026#34;cluster.routing.allocation.enable\\\u0026#34; : \\\u0026#34;none\\\u0026#34; }}\u0026#39;\u0026#34; register: result until: result.stdout.find(\u0026#39;\u0026#34;acknowledged\u0026#34;\u0026#39;) != -1 retries: 200 delay: 3 changed_when: result.stdout.find(\u0026#39;\u0026#34;acknowledged\u0026#34;:true\u0026#39;) != -1 # Index Flush sync - name: \u0026#34;Index Flush sync - {{ansible_hostname}}\u0026#34; action: \u0026#34;shell curl -s -m 2 http://{{ es_client_server }}:9200/_flush/synced?pretty\u0026#34; register: result until: result.rc == 0 retries: 200 delay: 3 # restart elasticsearch process - name: \u0026#34;Elasticsearch restart {{ansible_hostname}}\u0026#34; service: name=elasticsearch state=restarted # Wait for Elasticsearch node to come back into cluster - name: \u0026#34;Wait for elasticsearch running on node {{ansible_hostname}}\u0026#34; action: \u0026#34;shell curl -s -m 2 http://{{ es_client_server }}:9200/_cat/nodes?h=name | tr -d \u0026#39; \u0026#39; | grep -E \u0026#39;^{{ansible_hostname}}\u0026#39; \u0026#34; register: result until: result.rc == 0 retries: 200 delay: 3 # Enable shard allocation - name: \u0026#34;Set cluster routing allocation to all {{ansible_hostname}}\u0026#34; action: \u0026#34;shell curl -s -m 2 -XPUT http://{{ es_client_server }}:9200/_cluster/settings -d \u0026#39;{\\\u0026#34;transient\\\u0026#34; : {\\\u0026#34;cluster.routing.allocation.enable\\\u0026#34; : \\\u0026#34;all\\\u0026#34; }}\u0026#39;\u0026#34; register: result until: result.stdout.find(\u0026#34;acknowledged\u0026#34;) != -1 retries: 200 delay: 3 changed_when: result.stdout.find(\u0026#39;\u0026#34;acknowledged\u0026#34;:true\u0026#39;) != -1 # Wait until cluster status is green - name: \u0026#34;Wait for green cluster status {{ansible_hostname}}\u0026#34; action: \u0026#34;shell curl -s -m 2 http://{{ es_client_server }}:9200/_cat/health | cut -d \u0026#39; \u0026#39; -f 4\u0026#34; register: result until: result.stdout.find(\u0026#34;green\u0026#34;) != -1 retries: 5000 delay: 10 And for (C) you could use something like this:\n- hosts: prod-all-nodes # Important to run the procedure sequentially in all servers defined # in prod-all-nodes serial: 1 vars: es_client_server: es-client.example.net tasks: # Disable shard allocation - name: \u0026#34;Set cluster routing allocation to none {{ansible_hostname}}\u0026#34; action: \u0026#34;shell curl -XPUT http://{{ es_client_server }}:9200/_cluster/settings -d \u0026#39;{\\\u0026#34;transient\\\u0026#34; : {\\\u0026#34;cluster.routing.allocation.enable\\\u0026#34; : \\\u0026#34;none\\\u0026#34; }}\u0026#39;\u0026#34; register: result until: result.stdout.find(\u0026#39;\u0026#34;acknowledged\u0026#34;\u0026#39;) != -1 retries: 200 delay: 3 changed_when: result.stdout.find(\u0026#39;\u0026#34;acknowledged\u0026#34;:true\u0026#39;) != -1 # Index Flush sync - name: \u0026#34;Index Flush sync - {{ansible_hostname}}\u0026#34; action: \u0026#34;shell curl -s -m 2 http://{{ es_client_server }}:9200/_flush/synced?pretty\u0026#34; register: result until: result.rc == 0 retries: 200 delay: 3 # Stop elasticsearch process - name: \u0026#34;Elasticsearch stop {{ansible_hostname}}\u0026#34; service: name=elasticsearch state=stopped # Restart server - name: \u0026#34;Restart server {{ansible_hostname}}\u0026#34; command: \u0026#34;/sbin/shutdown -r +1\u0026#34; async: 0 poll: 0 ignore_errors: true # Wait for server to restart successfully - name: \u0026#34;Wait for server {{ansible_hostname}} to restart successfully\u0026#34; local_action: wait_for host={{ansible_hostname}} port=22 state=started timeout=600 delay=120 sudo: false # Start elasticsearch process - name: \u0026#34;Elasticsearch start {{ansible_hostname}}\u0026#34; service: name=elasticsearch state=started # Wait for Elasticsearch node to come back into cluster - name: \u0026#34;Wait for elasticsearch running on node {{ansible_hostname}}\u0026#34; action: \u0026#34;shell curl -s -m 2 http://{{ es_client_server }}:9200/_cat/nodes?h=name | tr -d \u0026#39; \u0026#39; | grep -E \u0026#39;^{{ansible_hostname}}\u0026#39; \u0026#34; register: result until: result.rc == 0 retries: 200 delay: 3 # Enable shard allocation - name: \u0026#34;Set cluster routing allocation to all {{ansible_hostname}}\u0026#34; action: \u0026#34;shell curl -s -m 2 -XPUT http://{{ es_client_server }}:9200/_cluster/settings -d \u0026#39;{\\\u0026#34;transient\\\u0026#34; : {\\\u0026#34;cluster.routing.allocation.enable\\\u0026#34; : \\\u0026#34;all\\\u0026#34; }}\u0026#39;\u0026#34; register: result until: result.stdout.find(\u0026#34;acknowledged\u0026#34;) != -1 retries: 200 delay: 3 changed_when: result.stdout.find(\u0026#39;\u0026#34;acknowledged\u0026#34;:true\u0026#39;) != -1 # Wait until cluster status is green - name: \u0026#34;Wait for green cluster status {{ansible_hostname}}\u0026#34; action: \u0026#34;shell curl -s -m 2 http://{{ es_client_server }}:9200/_cat/health | cut -d \u0026#39; \u0026#39; -f 4\u0026#34; register: result until: result.stdout.find(\u0026#34;green\u0026#34;) != -1 retries: 5000 delay: 10 Links:\nhttps://www.elastic.co/guide/en/elasticsearch/client/curator/current/index.html https://www.elastic.co/guide/en/elasticsearch/reference/5.x/shard-allocation-fi… https://www.elastic.co/guide/en/elasticsearch/reference/current/rolling-upgrade… https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapsho… https://www.ansible.com/ ","date":"2017/07/10","externalUrl":null,"permalink":"/blog/elasticsearch-common-maintenance-tasks/","section":"Blog articles","summary":"If you have to administrate an Elasticsearch cluster, there are some common maintenance tasks that you will have to run to keep your data growth under control, backup your indexes and keep the cluster updated.","title":"Elasticsearch - Common maintenance tasks","type":"blog"},{"content":"One of the main plugins we were using with our 2.x Elasticsearch cluster was KOPF. This plugin was a web interface to the Elasticsearch API and it was an easy way of performing common tasks on our Elasticsearch cluster.\nWhen we upgraded our Elasticsearch cluster to version 5.x, we could not continue using this plugin because it was not longer supported. The good thing was that the author of KOPF, Leonardo Menezes, had a new project called \u0026ldquo;CEREBRO\u0026rdquo; to offer an alternative to KOPF when running Elasticsearch 5.x.\n\u0026ldquo;CEREBRO\u0026rdquo; is an Elasticsearch web admin tool built using Scala, Play Framework, AngularJS and Bootstrap. It is equivalent to KOPF with some extra new features.\n\u0026ldquo;CEREBRO\u0026rdquo; is easy to install and use but at the moment of writing this article it lacks a number of things to be accessed and administrated in a professional way:\nCEREBRO will start a service on a defined port, being available on this port but without SSL support. It has a partial LDAP integration, but we could not make work in our infrastructure. This could be our fault, we did not use time to try to debug this issue. It doesn\u0026rsquo;t have any type of package RPM/DEB to be installed and maintain. It doesn\u0026rsquo;t have any configuration to administrate the service in a easy way. We use a server with RHEL7 to run CEREBRO and this is what we have done to try to fix all these administrative shortcomings without using so much time. Maybe you will have to adjust some of the steps if you are using another linux distribution:\nDownload the last version of CEREBRO from https://github.com/lmenezes/cerebro and save it under /opt/cerebro.\n[root@elastic ~]# ls -l /opt/cerebro/ total 50012 drwxr-xr-x. 6 apache apache 99 Jun 19 16:28 cerebro-0.6.5 lrwxrwxrwx. 1 apache apache 13 May 9 15:52 current -\u0026gt; cerebro-0.6.5 Create the CEREBRO configuration file /opt/cerebro/current/conf/application.conf with this content (you will have to customize some parameters to work in your infrastructure):\n# Secret will be used to sign session cookies, CSRF tokens and for other encryption utilities. # It is highly recommended to change this value before running cerebro in production. secret = \u0026#34;ki:s:[[@=Ag?QI`W2jMwsdjhhkljqwiuiueqeakjhJHKJASDKJ123kjASTSAHGhghga\u0026#34; # Application base path basePath = \u0026#34;/ # Defaults to RUNNING_PID at the root directory of the app. # To avoid creating a PID file set this value to /dev/null #pidfile.path = \u0026#34;/var/run/cerebro.pid\u0026#34; # Rest request history max size per user rest.history.size = 50 // defaults to 50 if not specified # Path of local database file data.path = \u0026#34;./cerebro.db\u0026#34; # Authentication auth = { } # A list of known hosts hosts = [ { host = \u0026#34;http://elasticsearch-prod.example.org:9200\u0026#34; name = \u0026#34;ES PRODUCTION\u0026#34; } { host = \u0026#34;http://elasticsearch-utv.example.org:9200\u0026#34; name = \u0026#34;ES UTV\u0026#34; } ] Install the packages httpd, mod_ssl and java-1.8.0-openjdk\nConfigure SElinux with these commands.\n# semanage port -a -t http_port_t -p tcp 9200 # setsebool -P httpd_can_network_connect 1 # setsebool -P nis_enabled 1 Create the file /etc/systemd/system/cerebro.service with this content:\n[Unit] Description=Elasticsearch - Cerebro Wants=network-online.target After=network-online.target [Service] Environment=HOST=127.0.0.1 Environment=PORT=9200 WorkingDirectory=/opt/cerebro/current User=apache Group=apache ExecStart=/opt/cerebro/current/bin/cerebro \\ -Dhttp.port=${PORT} \\ -Dhttp.address=${HOST} # Connects standard output to /dev/null StandardOutput=null # Connects standard error to journal StandardError=journal # Shutdown delay in seconds, before process is tried to be killed with KILL (if configured) TimeoutStopSec=0 # SIGTERM signal is used to stop the Java process KillSignal=SIGTERM # Java process is never killed SendSIGKILL=no # When a JVM receives a SIGTERM signal it exits with code 143 SuccessExitStatus=143 [Install] WantedBy=multi-user.target ``` Run the command systemctl daemon-reload\nConfigure apache as a proxy to handle all access via HTTPS, provide LDAP authentication, and to forward the traffic to the CEREBRO service running on 127.0.0.1:9200. Create the file \u0026lsquo;/etc/httpd/conf.d/elastic-mgmt.conf\u0026rsquo; with this content (you will have to customize some parameters to work in your infrastructure):\n\u0026lt;virtualhost *:80\u0026gt; ServerName elastic-mgmt.example.org ServerAlias elastic-mgmt RewriteEngine On RewriteCond %{HTTPS} off RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} \u0026lt;/virtualhost\u0026gt; \u0026lt;virtualhost *:443\u0026gt; ServerName elastic-mgmt.example.org ServerAlias elastic-mgmt ProxyRequests Off ProxyPreserveHost On ProxyReceiveBufferSize 4096 \u0026lt;proxy\u0026gt; Order deny,allow Allow from all \u0026lt;/proxy\u0026gt; AllowEncodedSlashes On ProxyPass / http://127.0.0.1:9200/ ProxyPassReverse / http://127.0.0.1:9200/ \u0026lt;location /\u0026gt; Order allow,deny Allow from \u0026lt;YOUR_CLIENT_IP\u0026gt; \u0026lt;/location\u0026gt; \u0026lt;ifmodule authnz_ldap_module\u0026gt; \u0026lt;locationmatch \u0026#34;/\u0026#34;\u0026gt; AuthType Basic AuthName \u0026#34;Elasticsearch Cerebro [Note: Authenticate with your LDAP-user]\u0026#34; AuthBasicProvider ldap AuthLDAPURL \u0026lt;YOUR_LDAP_URL\u0026gt; TLS AuthLDAPGroupAttribute memberUid AuthLDAPGroupAttributeIsDN off require ldap-group \u0026lt;YOUR_LDAP_GROUP\u0026gt; \u0026lt;/locationmatch\u0026gt; \u0026lt;/ifmodule\u0026gt; SSLEngine on SSLProtocol -ALL +TLSv1.2 +TLSv1.1 SSLProxyEngine on SSLCipherSuite HIGH:!MEDIUM:!aNULL:!MD5:!RC4:!DES SSLHonorCipherOrder on SSLCertificateFile /etc/pki/tls/certs/elastic-mgmt.example.org.crt SSLCertificateKeyFile /etc/pki/tls/private/elastic-mgmt.example.org.key SSLCACertificateFile /etc/pki/tls/certs/CA.crt BrowserMatch \u0026#34;MSIE [2-5]\u0026#34; \\ nokeepalive ssl-unclean-shutdown \\ downgrade-1.0 force-response-1.0 \u0026lt;/virtualhost\u0026gt; Finally run these commands to configure and start the services:\n# systemctl enable cerebro # systemctl enable httpd # systemctl start cerebro # systemctl start httpd Now you should be able to access CEREBRO via the ServerName you have defined in /etc/httpd/conf.d/elastic-mgmt.conf, in our case: https://elastic-mgmt.example.org/.\nA graphical representation of this configuration is available in this diagram:\nRemember that you should always run your Elasticsearch cluster in a dedicated network and that the access to this network should be restricted only to servers under your control.\nThanks to Leonardo Menezes for this open source software that give us the opportunity of interacting with the Elasticsearch API in a easy way.\nLinks:\nhttps://github.com/lmenezes/cerebro https://github.com/lmenezes/elasticsearch-kopf https://www.elastic.co/ ","date":"2017/06/25","externalUrl":null,"permalink":"/blog/access-elasticsearch-cerebro-sslldap/","section":"Blog articles","summary":"“CEREBRO” is an Elasticsearch web admin tool built using Scala, Play Framework, AngularJS and Bootstrap. It is equivalent to KOPF with some extra new features.","title":"Access to Elasticsearch with Cerebro via SSL+LDAP","type":"blog"},{"content":"","date":"2017/06/19","externalUrl":null,"permalink":"/tags/postgres/","section":"Tags","summary":"","title":"Postgres","type":"tags"},{"content":"We started using Zabbix to monitor the IT infrastructure at The University of Oslo in 2014. During all this time we have been running all our Zabbix servers on VMware virtual servers with an acceptable level of performance. This situation changed some months ago when the VMvare+Storage we were using did not have more available resources for us to grow and it was slowing down the future development of our monitoring system.\nThis is why we decided to move some parts of our Zabbix infrastructure to dedicated servers. The Zabbix database, server and proxies are running on pyshical servers now while the web/API servers are still running on virtual servers.\nThe server we got to run the zabbix-server and database was a Dell PowerEdge R730xd with these main specifications:\n2 x E5-2690 v4 @ 2.60GHz (28cores) 512GB ECC RAM Controller PERC H730 OS: 2 x 200GB RAID-1 SSD SATA 6Gbps (SSDSC2BX200G4R). DATA: 2 x 1.6TB RAID-1 SSD SAS 12Gbps (Toshiba PX05SMB160Y). With a powerful server and the amount of data our Zabbix installation was processing it was very important to tune PostgreSQL to take advantage of the resources we had available. One of the main components in Zabbix is the database and a poor performance will affect the whole system, we used the information we had in our old virtual database server in production to calculate most of the configuration values for the new one and we focused on 4 areas:\nCheckpoints Autovacuum \u0026amp; autoanalyze Parallel processing Temp files. Checkpoints # PostgreSQL uses WAL (write-ahead log) to provide durability of our data and to perform a database recovery in case of a crash.\nWhen a change is done in the database, PostgreSQL registers the change to the WAL and flushes this data to disk right away while changes to data files are done only in memory and flushed to disk later in the background. This is done to increase performance.\nThe process of flushing the changes in memory to the data files and registering this moment into the WAL stream is called a checkpoint. When a checkpoint is finished, old WAL information is not necessary anymore because the changes registered in the WAL before the checkpoint have been saved into the data files.\nBecause a checkpoint can be very demanding and can generate a high IO in your server if it is not done properly, we do not want them to occur too often, to avoid IO load, or wait too much, to minimize the recovery time in case of a crash. A checkpoint can be triggered i.a. by:\nReaching an amount of time since the last checkpoint. Generating an amount of WAL since the last checkpoint. We want to trigger a checkpoint by time so we can control when they occur, values around 30 min. between checkpoints are common in busy databases. We also want to configure enough WAL space so we do not use all the WAL space available before our time limit.\nDepending of the amount of data your Zabbix installation collects, you can generate large amounts of WAL files. The good thing with Zabbix is that the data flow is very predictable, it depends in particular on the amount of items you have and the update interval of these items. You can see in the following graphs the amount of values and transactions processed by Zabbix in our system, as you can see they are very predictable.\nZabbix values processed / sec\nPostgreSQL transactions / sec - ZabbixDB\nWhat we did to find out the amount of WAL data generated during an hour was to use the WAL information available in our database:\n# psql -c \u0026#34;SELECT pg_current_xlog_insert_location()\u0026#34; \u0026amp;\u0026amp; sleep 3600 \u0026amp;\u0026amp; psql -c \u0026#34;SELECT pg_current_xlog_insert_location()\u0026#34; pg_current_xlog_insert_location --------------------------------- FC26/8993A100 (1 row) pg_current_xlog_insert_location --------------------------------- FC2C/2AAF1DE8 (1 row) SELECT pg_size_pretty(pg_xlog_location_diff(\u0026#39;FC2C/2AAF1DE8\u0026#39;,\u0026#39;FC26/8993A100\u0026#39;)); pg_size_pretty ---------------- 23 GB (1 row) As you can see we generated around 23GB of WALs during this hour, including a zabbix housekeeping process execution. This means that if we are going to use a 30 min. checkpoint timeout, we would need 12GB or more of WAL space to not trigger a checkpoint before our 30 min. timeout. There are two PostgreSQL parameters to control this:\nAs you can see we generated around 23GB of WALs during this hour, including a zabbix housekeeping process execution. This means that if we are going to use a 30 min. checkpoint timeout, we would need 12GB or more of WAL space to not trigger a checkpoint before our 30 min. timeout. There are two PostgreSQL parameters to control this:\ncheckpoint_timeout: In our case checkpoint_timeout = 30min max_wal_size: In our case max_wal_size = 75GB. This parameter defines the WAL size for 2-3 checkpoints (3 x 12GB = 36GB). And because we are planning to double the amount of data we are going to monitor, 36GB x 2 = 72GB ≈ 75GB. Another thing we wanted to do was to spread the checkpoint work to minimize IO. For this we used the PG parameter checkpoint_completion_target to control how much time the database had to write all the dirty buffers in memory to disk.\nWe used checkpoint_completion_target = 0.93 in our system. This means that PostgreSQL will throttle the writes so the last one will be done after 28min. (30min. x 0.93) and the OS will still have around 2 min. to flush the data to disk in the background, more than enough if we use the default value of 30 sec. for the linux kernel parameter vm.dirty_expire_centisecs = 3000.\nThis is a graph that shows a healthy checkpoint configuration. You can see that most of the checkpoints are triggered by time occurring every 30 min.\nHealthy PostgreSQL checkpoints configuration\nAutovacuum \u0026amp; autoanalyze # Autovacuum and autoanalyze is another area we had to take care of to avoid performance problems with Zabbix.\nPostgreSQL uses Multiversion Concurrency Control (MVCC) to provide concurrent access to the database. One of the side effects of this technique is that all deletes and updates done in the database will leave \u0026lsquo;dead\u0026rsquo; tuples (rows) marked as deleted but actually not deleted from the disk. Dead tuples will not be visible to transactions but will use and waste disk space, what we call “bloat” in PostgreSQL. This will affect how much disk space we use and will slow down queries if we do not do something to reclaim this \u0026lsquo;dead\u0026rsquo; space.\nZabbix has some tables that usually have a lot of data and suffer many deletes/updates queries, e.g. the tables events, history, history_uint, trends and trends_uint. These tables will be specially affected by bloat and a correct configuration of autovacuum is essential for a smooth operation.\nThere are two things we have to take care of when configuring autovacuum:\nHow often we cleanup dead tuples. How many dead tuples we can clean up in a period of time minimizing the cleanup impact in our system. How often we clean up dead tuples can be defined globally or per table. We decided to define it globally to avoid extra administration and to avoid changing the default DDL definition of the database delivered by Zabbix.\nThere are two parameters that decide when to start vacuuming a table, autovacuum_vacuum_threshold and autovacuum_vacuum_scale_factor. The first one defines the minimum number of tuples that have to be dead and the second the percentage of dead tuples in the table before PostgreSQL starts autovacuuming the table. In our case we used autovacuum_vacuum_threshold = 50 and autovacuum_vacuum_scale_factor = 0.01. This means that PostgreSQL will start cleaning a table if the amount of dead tuples is over 1% and at least 50 tuples are dead.\nHow many dead tuples we can clean up in a period of time and the cleanup impact in our system is controlled by the autovacuum throttling functionality. The cleanup is a maintenance task running in the background and it should have a minimum impact on user queries.\nThe autovacuum throttling is controlled by the parameters autovacuum_vacuum_cost_delay and autovacuum_vacuum_cost_limit. The first one defines the length of time that the autovacuum process will sleep when a cost limit has been exceeded, and the second one defines the accumulated cost that will cause the autovacuum process to sleep when it reach the limit.\nThe cost is estimated using the values of these parameters:\nvacuum_cost_page_hit = 1 vacuum_cost_page_miss = 10 vacuum_cost_page_dirty = 20 In our case we used autovacuum_vacuum_cost_delay = 20ms and autovacuum_vacuum_cost_limit = 3000. This means that we will be able to process 150,000 (1000/20*3000) cost units per second. This translates to 1,171MB/s (150,000 x 8 / 1024) reads from shared_buffers, 117MB/s (150,000 x 8 / 1024 / 10) reads from OS (possibly from disk) and 58MB/s (150,000 x 8 / 1024 / 20) writes of pages dirtied by the autovacuum process. These values are more than enough for our load and can be processed without problems by our SSD disks.\nIn addition we defined autovacuum_max_workers = 6 and maintenance_work_mem = 8G , this will use in the worst case around 48GB of memory for autovacuum jobs.\nThis SQL query shows the amount and percent of dead tuples in your tables:\nSELECT relname, n_live_tup, n_dead_tup, n_dead_tup*100/n_live_tup::float AS dead_percent, last_vacuum, last_autovacuum, autovacuum_count FROM pg_stat_user_tables WHERE n_dead_tup \u0026lt;\u0026gt; 0 AND n_live_tup \u0026lt;\u0026gt;0 ORDER BY relname; We also defined these two parameters to control how often we analyze the tables, autovacuum_analyze_scale_factor = 0.01 and autovacuum_analyze_threshold = 200.They use the same logic as the ones used by autovacuum.\nParallel processing # PostgreSQL 9.6 has the possibility of running parallel queries under certain conditions. Some scans, joins and aggregation will be executed in parallel if this functionality is activated and if the database query planner decides it is a good plan. We wanted to activate this functionality but we did not have experience with it, so we were conservative when configuring this in our system.\nAt least these two parameters have to be defined to activate parallel processing:\nmax_parallel_workers_per_gather: In our case max_parallel_workers_per_gather=4. We found out in the logs that the slow queries in our Zabbix infrastructure had at most 4 joins and we thought this was a good value to start with. max_worker_processes: In our case max_worker_processes=16. This is 57% of all the available cores in our system. We defined also effective_io_concurrency = 4 to match the amount of parallel workers we can use in a query. There is very little information about this parameter when using SSD disks. The documentation says \u0026quot;\u0026hellip; the best value might be in the hundreds \u0026hellip;\u0026quot;, so we really do not know how this change impacts performance, we will have to investigate and test this further to get any conclusion.\nTemp files # One of the problems we had in our old Zabbix database was the generation of large temporary files when running SQL statements. Most of these files were created because we did not have enough memory, controlled by the parameter work_mem, for sort operations and hash tables. In PostgreSQL, sort operations are used for ORDER BY, DISTINCT, and merge joins, and hash tables are used in hash joins, hash-based aggregation, and hash-based processing of IN subqueries.\nThe use of many and large temporary files was impacting the performance of our SQL queries so we activated logging of temporary files with the parameter log_temp_files = 0 to find out when we were generating these files. This gave us a lot of log lines of this type:\nLOG: temporary file: path \u0026#34;base/pgsql_tmp/pgsql_tmp16501.0\u0026#34;, size 147677184 Then we found out the biggest size logged for these temporary files, in our case around 140MB. What we did was to define the parameter work_mem = 150MB to have enough memory available for these sort operations and hash tables.\nYou have to be careful with this parameter because it will be used not per SQL query but per sort operation or hash table needed to return the query result. So you need enough memory in your system to cope with the value defined by this parameter. We calculated that in our system, with around 8 sort operation/hash table per SQL query, a maximum of 16 subprocesses in 4 parallel queries, and around a maximum of 20-30 active connections, we would use around 49GB in the worst case (150MB x ((8 x 16) + (8 x (30-4)))). That is around 9.5% of our total memory (512GB), something we can live with to avoid the huge impact in performance when your system generates temporary files.\nOther parameters # Other PostgreSQL parameters regarding performance that we changed are:\nhuge_pages = on random_page_cost = 1.1 shared_buffers = 32GB synchronous_commit = off You can check the PostgreSQL documentation to get more information about them.\nConclusion # Right now and with this configuration we are processing between 2,000 and 3,000 Zabbix items per second and we are generating between 1,000 and 13,000 transactions per second in the database. The database is around 500GB, we have around 2 months of monitoring data and one year of alarms and events. The server is working perfectly and we have still a lot of resources available to grow.\nIf you are using Zabbix to monitor your IT infrastructure, you really have to take care of your database. PostgreSQL is an excellent database, stable and with a great performance but you have to configure it properly if you are processing large amounts of data. We can definitely recommend it as the database backend for your Zabbix installation.\nLinks:\nhttps://blog.2ndquadrant.com/basics-of-tuning-checkpoints/ https://blog.2ndquadrant.com/autovacuum-tuning-basics/ https://en.wikipedia.org/wiki/Multiversion_concurrency_control https://www.postgresql.org/docs/9.6/static/parallel-plans.html https://www.postgresql.org/docs/9.6/static/runtime-config-resource.html ","date":"2017/06/19","externalUrl":null,"permalink":"/blog/using-zabbix-postgresql-database-backend/","section":"Blog articles","summary":"We started using Zabbix to monitor the IT infrastructure at The University of Oslo in 2014. During all this time we have been running all our Zabbix servers on VMware virtual servers with an acceptable level of performance. This situation changed some months ago when the VMvare+Storage we were using did not have more available resources for us to grow and it was slowing down the future development of our monitoring system.","title":"Using Zabbix with PostgreSQL as the database backend","type":"blog"},{"content":" UNINETT fagdager 2017 - Trondheim, Norway 2017-05-02 uninett2017_short.pdf Not available\nSummary # A short presentation focused on how we automate the Zabbix configuration at the Universitity of Oslo.\n","date":"2017/05/02","externalUrl":null,"permalink":"/presentations/zabbix-automation-uio/","section":"Presentations","summary":"A short presentation focused on how we automate the Zabbix configuration at the Universitity of Oslo","title":"ZABBIX automation @ UiO","type":"presentations"},{"content":" ZabbixConf 2016 - Riga, Latvia 2016-09-10 zabbixconf2016_zabbix_cli.pdf https://youtu.be/A9yY-XMa-LY?t=516\nSummary # This presentation is a 5 minuttes lightning talk to present Zabbix-CLI.\nZabbix-cli is a tool for managing some administration tasks in monitoring systems running Zabbix.\nIt is a terminal client written in Python that uses the Zabbix-API to connect to your Zabbix installation. It has been developed and tested by members of the Department for IT Infrastructure at the Center for Information Technology at the University of Oslo, Norway.\n","date":"2016/09/10","externalUrl":null,"permalink":"/presentations/lightning-talk-zabbix-cli/","section":"Presentations","summary":"A 5 minuttes lightning talk to present Zabbix-CLI.","title":"Lightning Talk - Zabbix-CLI","type":"presentations"},{"content":"","date":"2016/09/10","externalUrl":null,"permalink":"/tags/zabbix-cli/","section":"Tags","summary":"","title":"Zabbix-Cli","type":"tags"},{"content":" ZabbixConf 2016 - Riga, Latvia 2016-09-10 zabbixconf2016_uio.pdf https://www.youtube.com/watch?v=Y0vmDE5w4Jw\nSummary # A case study showing the problems we have resolved with Zabbix and the challenges we had when we implemented Zabbix as the main monitoring tool at the University of Oslo.\nThe number of challenges is not low in an organization as heterogenous as ours, with many thousands of servers and clients, all kinds of devices connected to our infrastructure, different operating systems, multiple locations and hundreds of IT staff. Full automation and delegation of privileges are the key words in the work we have done during the past year and a half.\n","date":"2016/09/10","externalUrl":null,"permalink":"/presentations/zabbix-uio/","section":"Presentations","summary":"A case study showing the problems we have resolved with Zabbix and the challenges we had when we implemented Zabbix as the main monitoring tool at the University of Oslo.","title":"Zabbix@UiO","type":"presentations"},{"content":"","date":"2016/04/21","externalUrl":null,"permalink":"/tags/logging/","section":"Tags","summary":"","title":"Logging","type":"tags"},{"content":" UiO-IT konferanse 2016 - Sweden 2016-04-21 elk_presentasjon_itk2016.pdf Not available\nSummary # A presentation about why and how we are using data analysis of log information at the University of Oslo with elasticsearch, logstash and kibana.\n","date":"2016/04/21","externalUrl":null,"permalink":"/presentations/sentralisert-logging-ved-uio/","section":"Presentations","summary":"A short presentation about why and how we are using data analysis of log information at the University of Oslo with elasticsearch, logstash and kibana.","title":"Sentralisert logging ved UiO","type":"presentations"},{"content":"","date":"2015/10/29","externalUrl":null,"permalink":"/tags/pgbackman/","section":"Tags","summary":"","title":"Pgbackman","type":"tags"},{"content":" PGConfEU-2015 - Vienna, Austria 2015-10-29 pgbackman_pgconfeu2015.pdf Not available\nSummary # A 5 minuttes lightning talk that presents PgBackMan.\nPgBackMan is a tool for managing PostgreSQL logical backups created with pg_dump and pg_dumpall. It is designed to manage backups from thousands of databases running in multiple PostgreSQL nodes, and it supports a multiple backup server topology.\nIt also manages role and database configuration information when creating a backup of a database.\n","date":"2015/10/29","externalUrl":null,"permalink":"/presentations/postgresql-backup-manager/","section":"Presentations","summary":"A 5 minuttes lightning talk to present PgBackMan.","title":"PostgreSQL Backup Manager","type":"presentations"},{"content":" UiO-IT konferanse 2015 - Sweden 2015-04-24 gid_it_konf_2015_2.pdf Not available\nSummary # This presentation is a tour of the job done at the University of Oslo in the areas of automation, monitoring, data analysis and trending during 2015.\n","date":"2015/04/24","externalUrl":null,"permalink":"/presentations/grunnlagsdata-overvaking-og-trending/","section":"Presentations","summary":"A tour of the job done at the University of Oslo in the areas of automation, monitoring, data analysis and trending during 2015.","title":"Grunnlagsdata, overvåking og trending","type":"presentations"},{"content":"","date":"2015/04/24","externalUrl":null,"permalink":"/tags/monitoring/","section":"Tags","summary":"","title":"Monitoring","type":"tags"},{"content":" PGConfEU-2014 - Madrid, Spain 2014-10-22 el_guardian_del_tesoro_pgconfeu2014.pdf Not available\nSummary # The primary purpose of a database administrator (DBA) is to protect a precious treasure, our data.\nA DBA must ensure the integrity of the data in the database, the availability of this data when needed, the durability over time and that only authorized personal have access to this data.\nUnfortunately this is not always true and very often we can see systems with severe security problems in the way that ensure and protect the data.\nThis presentation is a journey through measures and management techniques that a DBAs should be familiar with when working with valued data.\n","date":"2014/10/22","externalUrl":null,"permalink":"/presentations/el-guardian-del-tesoro/","section":"Presentations","summary":"This presentation is a journey through measures and management techniques that a DBAs should be familiar with when working with valued data.","title":"El guardian del tesoro","type":"presentations"},{"content":"","date":"2014/10/22","externalUrl":null,"permalink":"/tags/espa%C3%B1ol/","section":"Tags","summary":"","title":"Español","type":"tags"},{"content":"These are some of the OpenSource projects I have worked with during the years:\n","date":"2014/10/09","externalUrl":null,"permalink":"/projects/","section":"Projects","summary":"","title":"Projects","type":"projects"},{"content":" unioslo/zabbix-cli Command-line interface for Zabbix Python 241 101 Zabbix-cli is a tool for managing some administration tasks in monitoring systems running Zabbix.\nIt is a terminal client written in Python that uses the Zabbix-API to connect to your Zabbix installation. It has been developed and tested by members of the Department for IT Infrastructure at the Center for Information Technology at the University of Oslo, Norway.\nZabbix-CLI is distributed under the GNU General Public License 3.\nDocumentation # The Zabbix-CLI manual is available at: https://unioslo.github.io/zabbix-cli/\nDownloads # The source code, RPM and DEB packages for some main distributions are available at GitHub. Check the Zabbix-CLI documentation for information about how to install and configurate this software.\nhttps://github.com/unioslo/zabbix-cli/releases\nBug report / Feature request # https://github.com/unioslo/zabbix-cli/issues\n","date":"2014/10/09","externalUrl":null,"permalink":"/projects/zabbix-cli/","section":"Projects","summary":"Zabbix-cli is a tool for managing some administration tasks in monitoring systems running Zabbix. It is a terminal client written in Python that uses the Zabbix-API to connect to your Zabbix installation.","title":"ZABBIX-CLI","type":"projects"},{"content":" CAUTION This project is no longer maintained due to lack of free time to do so. The source code is still available and it was supported up to PostgreSQL 9.6.\nPgBackMan is a tool for managing PostgreSQL logical backups created with pg_dump and pg_dumpall.\nIt is designed to manage backups from thousands of databases running in multiple PostgreSQL nodes, and it supports a multiple backup server topology.\nPgBackMan is distributed under the GNU General Public License 3.\nMain Features # Central database with metadata information.\nPgBackMan shell for interaction with the system.\nManagement of multiple backup servers.\nManagement of multiple PostgreSQL servers.\nManagement of thousands of backups dumps through a catalogue.\nManual and scheduled backups.\nManagement of retention policies for backups dumps.\nFully detailed backup reports.\nMultiple predefined database backup types, CLUSTER, FULL, SCHEMA, DATA.\nFull backup of role information for a database.\nFull backup of database configuration for a database.\nAutomatic definitions of backups for all databases running in a PgSQL node.\nAutomatic definitions of backups for all databases without definitions in a PgSQL node.\nAutomatic deletion after a quarantine period of backup definitions and associated files for databases than have been deleted in a PgSQL node.\nPossibility of pausing / resuming replication on slaves/standby nodes when taking large backups.\nAutomatic restore procedures.\nAutonomous pgbackman_dump program that functions even if the central database with metadata is not available.\nPossibility of sending alerts via SMTP when an error happens.\nHandling of error situations.\nWritten in Python and PL/PgSQL\nDocumentation # The PgBackMan manual is available in HTML and PDF formats. The source code of the documentation in RST format is also available at GitHub: https://github.com/rafaelma/pgbackman/blob/master/docs/manual.rst\nDownloads # The current version is 1.2.0. The source code, RPM and DEB packages for some main distributions are available at GitHub. Check the PgBackMan documentation for information about how to install and configurate this software.\nhttps://github.com/rafaelma/pgbackman/releases\nBug report / Feature request # https://github.com/rafaelma/pgbackman/issues\n","date":"2014/01/12","externalUrl":null,"permalink":"/projects/pgbackman/","section":"Projects","summary":"PgBackMan is a tool for managing PostgreSQL logical backups created with pg_dump and pg_dumpall. It is designed to manage backups from thousands of databases running in multiple PostgreSQL nodes, and it supports a multiple backup server topology.","title":"PGBACKMAN","type":"projects"},{"content":"","date":"2013/10/17","externalUrl":null,"permalink":"/es/tags/postgresql-es/","section":"Tags","summary":"","title":"Postgresql-Es","type":"tags"},{"content":"Hace unos dias instalamos un nuevo servidor PostgreSQL 9.2 que se va a utilizar por diferentes cursos de bases de datos que se imparten en la facultad de informática de la Universidad de Oslo.\nLa idea es que cada alumno registrado en alguno de estos cursos tenga acceso a su base de datos postgreSQL para realizar sus prácticas.\nHasta aquí todo sin problemas, una instalación que sigue nuestros estándares de configuración, backup, seguridad y alta disponibilidad. La única diferencia con respecto a otros sistemas de los que estamos a cargo es el número de bases de datos que estamos ejecutando en el servidor.\nNada más crear las casi 400 bases de datos que necesitabamos y sin empezar a utilizar el servidor, el disco en donde se alojan estas bases de datos empezó a trabajar sin parar.\n¿Entre 50MB/s y 60MB/s constantes de escritura sin nadie utilizando el servidor?\nEmpezamos a investigar y a buscar en Internet si alguien habia tenido el mismo problema y pronto dimos con la explicación.\nPostgreSQL mantiene por defecto un fichero en el directorio $PGDATA/pg_stat_tmp/ con información sobre las estadísticas internas de todas las bases de datos en el servidor. Estas estadísticas se pueden configurar con los parámetros definidos en la sección \u0026ldquo;18.9. Run-time Statistics\u0026rdquo; de la documentación.\nEl problema hasta la versión 9.2 es que todas las estadísticas de todas las bases de datos se graban en el mismo fichero. Cuando se tienen muchas bases de datos el proceso \u0026ldquo;stats collector\u0026rdquo; accede a este fichero muchas veces por segundo creando una carga I/O considerable en el disco de datos.\nEn un servidor de pruebas usando postgreSQL 9.2.5, con 500 bases de datos que contienen una simple tabla vacia de dos columnas, obtenemos estos datos de utilización de discos sin ningun usuario conectado al servidor:\nLinux 2.6.32-358.18.1.el6.x86_64 _x86_64_ (32 CPU) Device: tps MB_read/s MB_wrtn/s MB_read MB_wrtn sdb 184.00 0.00 84.08 0 84 sdb 176.00 0.00 81.47 0 81 sdb 195.00 0.00 91.61 0 91 sdb 161.00 0.00 78.18 0 78 sdb 204.00 0.00 98.00 0 97 sdb 173.00 0.00 82.80 0 82 sdb 171.00 0.00 82.84 0 82 sdb 186.00 0.00 90.01 0 90 sdb 166.00 0.00 80.68 0 80 Datos strace del proceso stats collector despues de aprox. 30 segundos: % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 97.51 1.470643 5978 246 rename 2.01 0.030306 0 642919 write 0.33 0.005000 7 708 poll 0.14 0.002098 8 247 open 0.01 0.000108 0 1941 708 recvfrom 0.00 0.000048 0 246 munmap 0.00 0.000006 0 247 fstat 0.00 0.000000 0 708 708 read 0.00 0.000000 0 246 close 0.00 0.000000 0 247 mmap 0.00 0.000000 0 1 restart_syscall ------ ----------- ----------- --------- --------- ---------------- 100.00 1.508209 647756 1416 total Como podeis ver, con 500 bases de datos el problema se agrava todavía más y producimos sin utilizar el sistema aproxidamente entre 80MB/s y 90MB/s de carga en el disco.\nUna solución es empezar a utilizar la reciente versión 9.3 de postgreSQL. Con esta versión la cosa ha mejorado muchisimo al crearse un fichero por base de datos en vez de uno común.\nLa utilización del disco de datos en vacio disminuye aproximadamente un 98,5% en nuestro ejemplo. Y el número de llamadas write del sistema desciende considerablemente, casi un 99%.\nLinux 2.6.32-358.18.1.el6.x86_64 _x86_64_ (32 CPU) Device: tps MB_read/s MB_wrtn/s MB_read MB_wrtn sdb 20.00 0.00 1.11 0 1 sdb 20.00 0.00 1.09 0 1 sdb 20.00 0.00 1.09 0 1 sdb 20.00 0.00 1.11 0 1 sdb 22.00 0.00 1.19 0 1 sdb 22.00 0.00 1.21 0 1 sdb 18.00 0.00 0.98 0 0 sdb 20.00 0.00 1.11 0 1 sdb 22.00 0.00 1.21 0 1 Datos strace del proceso stats collector despues de aprox. 30 segundos: % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 60.15 0.008014 7 1180 poll 29.63 0.003948 6 622 rename 4.32 0.000576 1 622 open 3.70 0.000493 0 8754 write 1.44 0.000192 0 2524 1181 recvfrom 0.29 0.000038 0 622 mmap 0.27 0.000036 0 622 close 0.16 0.000021 0 622 munmap 0.04 0.000005 0 1181 1181 read 0.00 0.000000 0 622 fstat 0.00 0.000000 0 1 restart_syscall ------ ----------- ----------- --------- --------- ---------------- 100.00 0.013323 17372 2362 total Por desgracia no todos podemos actualizar a la versión 9.3 ahora mismo.\nLa solución que se encuentra en Internet para sistemas menores que la versión 9.3 y que nosotros hemos implementado es crear un sistema de ficheros tmpfs en memoria para no tener que cargar el sistema de almacenamiento con el I/O producido por el proceso \u0026ldquo;stats collector\u0026rdquo; de postgres.\nSupongo que tmpfs estará disponible por defecto en la mayoria de distribuciones linux de hoy en día. En nuestra Red Hat ELS 6.4 solamente hemos tenido que hacer lo siguiente para empezar a utilizarlo:\nCrear un directorio donde montar nuestro sistema de ficheros tmpfs:\nmkdir -p /var/lib/pgsql/9.2/pg_stat_tmp Actualizar el fichero /etc/fstab con esta linea, para crear una partición de 1GB en memoria:\ntmpfs /var/lib/pgsql/9.2/pg_stat_tmp tmpfs size=1G,uid=postgres,gid=postgres 0 0 Y ejecutar:\nmount /var/lib/pgsql/9.2/pg_stat_tmp Una vez que tenemos el el area tmpfs configurada y disponible tenemos que actualizar el fichero de configuración postgresql.conf de postgreSQL con la siguiente linea:\nstats_temp_directory = \u0026#39;/var/lib/pgsql/9.2/pg_stat_tmp\u0026#39; Y ponemos este cambio en producción haciendo un reload del servidor postgreSQL.\nDespués de esto el problema desaparece y tenemos todos los recursos del disco disponible para los usuarios, incluso sin poder utilizar la versión 9.3 todavia.\nLinux 2.6.32-358.18.1.el6.x86_64 _x86_64_ (32 CPU) Device: tps MB_read/s MB_wrtn/s MB_read MB_wrtn sdb 9.00 0.00 0.14 0 0 sdb 11.00 0.00 0.94 0 0 sdb 9.00 0.00 0.14 0 0 sdb 7.00 0.00 0.11 0 0 sdb 10.00 0.00 0.16 0 0 sdb 7.00 0.00 0.11 0 0 sdb 13.00 0.00 1.28 0 1 sdb 8.00 0.00 0.12 0 0 sdb 9.00 0.00 0.14 0 0 Datos strace del proceso stats collector despues de aprox. 30 segundos: % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 94.53 0.404986 1607 252 rename 3.07 0.013168 0 657173 write 2.10 0.008997 13 719 poll 0.22 0.000929 4 253 open 0.03 0.000125 0 719 719 read 0.03 0.000116 0 1981 719 recvfrom 0.02 0.000081 0 252 munmap 0.01 0.000023 0 252 close 0.00 0.000000 0 253 fstat 0.00 0.000000 0 253 mmap 0.00 0.000000 0 1 restart_syscall ------ ----------- ----------- --------- --------- ---------------- 100.00 0.428425 662108 1438 total El I/O producido en el disco de datos ha desaparecido aunque el sistema sigue haciendo aproximadamente el mismo número de llamadas write de sistema.\n","date":"2013/10/17","externalUrl":null,"permalink":"/es/blog/problema-con-el-io-cuando-tenemos-muchas-bases-de-datos-en-un-mismo-servidor/","section":"Blogs","summary":"Nada más crear las casi 400 bases de datos que necesitabamos y sin empezar a utilizar el servidor, el disco en donde se alojan estas bases de datos empezó a trabajar sin parar.","title":"Problema con el I/O cuando tenemos muchas bases de datos en un mismo servidor","type":"blog"},{"content":"Con la versión 9.1 de PostgreSQL tenemos disponible una nueva funcionalidad llamada SQL/MED mediante la cual se puede acceder a datos externos a nuestra base de datos mediante comandos SQL.\nEn SQL/MED existen los llamados \u0026ldquo;Foreign Data Wrapper (FDW)\u0026rdquo; que es una especie de \u0026ldquo;driver\u0026rdquo; para acceder a un tipo de datos externos. Existen diferentes tipos y con la versión 9.1 existe uno en los modulos contrib que se llama file_fdw. Este FDW se puede utilizar para acceder ficheros en formato CSV.\nHace unos meses lei en las listas de ayuda de postgreSQL y en un artículo sobre como poder acceder al fichero de registros (Log) de un servidor PostgreSQL mediante comandos SQL y sin tener que acceder al sistema operativo. Esta semana he tenido que instalar esto en un servidor y aquí os dejo los pasos a seguir para que veais lo fácil que es.\nLo primero es configurar PostgreSQL para que grabe todos los registros en un fichero en formato CSV. Tenemos que actualizar el fichero postgresql.conf de nuestro servidor con los siguientes parámetros y arrancar PostgreSQL de nuevo:\nlog_destination=\u0026#39;csvlog\u0026#39; logging_collector=\u0026#39;on\u0026#39; log_filename=\u0026#39;postgresql.csv\u0026#39; Despues hay que instalar en nuestra base de datos el FDW que vayamos a utilizar :\npostgres=# CREATE DATABASE test; CREATE DATABASE postgres=# \\c test test=# CREATE EXTENSION file_fdw; CREATE EXTENSION A continuación hay que definir un servidor foraneo (en este caso un fichero de datos en formato CSV):\ntest=# CREATE SERVER logserver FOREIGN DATA WRAPPER file_fdw; CREATE SERVER Y por último definir una tabla foranea en nuestro servidor foraneo. En nuestro caso, esta tabla tendrá que tener tantas columnas como atributos por fila tenga nuestro fichero CSV:\nCREATE FOREIGN TABLE postgres_log ( log_time timestamp(3) with time zone, user_name text, database_name text, process_id integer, connection_from text, session_id text, session_line_num bigint, command_tag text, session_start_time timestamp with time zone, virtual_transaction_id text, transaction_id bigint, error_severity text, sql_state_code text, message text, detail text, hint text, internal_query text, internal_query_pos integer, context text, query text, query_pos integer, location text, application_name text ) SERVER logserver OPTIONS (filename \u0026#39;pg_log/postgresql.csv\u0026#39;, format \u0026#39;csv\u0026#39;); Esto es todo. Ya podemos acceder a nuestro fichero de registros (Logs) via comandos SQL.\ntest=# SELECT log_time,database_name,process_id,error_severity,message FROM postgres_log ; log_time | database_name | process_id | error_severity | message ----------------------------+---------------+------------+----------------+---------------------------------------------------------- 2012-02-04 14:15:58.266+01 | | 3110 | LOG | database system was shut down at 2012-02-04 14:15:56 CET 2012-02-04 14:15:58.354+01 | | 3108 | LOG | database system is ready to accept connections 2012-02-04 14:15:58.354+01 | | 3113 | LOG | autovacuum launcher started 2012-02-04 14:17:01.26+01 | template1 | 3214 | FATAL | role \u0026#34;nagios\u0026#34; does not exist 2012-02-04 14:18:00.44+01 | | 3108 | LOG | received fast shutdown request 2012-02-04 14:18:00.44+01 | | 3108 | LOG | aborting any active transactions 2012-02-04 14:18:00.44+01 | | 3113 | LOG | autovacuum launcher shutting down 2012-02-04 14:18:00.441+01 | | 3111 | LOG | shutting down 2012-02-04 14:18:00.521+01 | | 3111 | LOG | database system is shut down (9 rows) test=# SELECT error_severity,count(*) as cnt FROM postgres_log GROUP BY error_severity; error_severity | cnt ----------------+----- FATAL | 1 LOG | 8 Por supuesto no podreis modificar nada en la tabla que habeis creado ya que en realidad estais accediendo a un fichero CSV.\ntest=# DELETE FROM postgres_log; ERROR: cannot change foreign table \u0026#34;postgres_log\u0026#34; No olvidar configurar la rotación de vuestro fichero postgresql.csv para reciclar y borrar las entradas antiguas.\nEnlaces:\nhttp://www.postgresql.org/docs/9.1/static/sql-createforeigndatawrapper.html http://www.postgresql.org/docs/9.1/static/sql-createforeigntable.html http://wiki.postgresql.org/wiki/SQL/MED http://wiki.postgresql.org/wiki/Foreign_data_wrappers ","date":"2012/02/04","externalUrl":null,"permalink":"/es/blog/logs-sqlmed/","section":"Blogs","summary":"Con la versión 9.1 de PostgreSQL tenemos disponible una nueva funcionalidad llamada SQL/MED mediante la cual se puede acceder a datos externos a nuestra base de datos mediante comandos SQL.","title":"Logs via SQL/MED","type":"blog"},{"content":"Esta mañana leyendo los mensajes de Twitter que me llegaron por la noche, me encontre con uno que me llamó la atención, \u0026ldquo;Steward Smith blogs on Optimazing InnoDB for creating 30.000 tables (and nothing else)\u0026rdquo;.\nEmpece a leerlo y estuvo entretenido. Trataba de lo que se podia hacer para acelerar la creación de muchos objetos en MySQL usando InnoDB, esto es especialmente importante cuando se vaya a importar una nueva base de datos que sea grande.\nUna de las cosas que decia, con la que estoy de acuerdo, es que en estos casos la durabilidad de los datos no es importante hasta que terminemos con la creación de todos los objetos, por lo que podemos apagar todos los mecanismos implementados en la base de datos para garantizar esta durabilidad mientras que estemos importando.\nEl test del que hablaba este artículo consistía en crear 30.000 tablas, insertar una fila en cada tabla y destruir las 30.000 tablas. Como me pico la curiosidad, me puse manos a la obra para ver como PostgreSQL funcionaba ejecutando un test similar.\nPrimero ejecuté el test con todos los parametros que garantizan la durabilidad de los datos activados.\nEstos son los valores de los parametros relevantes para este test en una máquina con 8GB de memoria y postgreSQL 9.1.1 (especialmente importante es max_locks_per_transaction si no quereis que el test falle estrepitosamente):\nmax_connections = 100 shared_buffers = 2GB work_mem = 16MB maintenance_work_mem = 256MB max_stack_depth = 4MB wal_level = archive fsync = on synchronous_commit = on wal_sync_method = fdatasync checkpoint_segments = 128 max_locks_per_transaction = 1000 Y esto lo ejecutado para realizar el test:\nCREATE DATABASE pgtest2; \\c pgtest2 \\timing DO $$ BEGIN FOR i IN 1..30000 LOOP EXECUTE \u0026#39;CREATE TABLE table\u0026#39; || i || \u0026#39;(id INT, PRIMARY KEY (id))\u0026#39;; END LOOP; END$$; DO $$ BEGIN FOR i IN 1..30000 LOOP EXECUTE \u0026#39;INSERT INTO table\u0026#39; || i || \u0026#39; VALUES (\u0026#39; || i || \u0026#39;)\u0026#39;; END LOOP; END$$; DO $$ BEGIN FOR i IN 1..30000 LOOP EXECUTE \u0026#39;DROP TABLE table\u0026#39; || i; END LOOP; END$$; La creación de todas las tablas duró 49818.792 ms, la inserción de una fila en cada una de ellas 37681.864 ms y la destrucción de las 30.000 tablas 548185.154 ms. Bueno, la creación e inserción de datos en 87.5 segundos no esta nada mal, me dejo un poco sorprendido que la destrucción de todas las tablas durase 9.1 minutos. En total 10.5 minutos, no estaba nada mal para tener todo lo referente a durabilidad activado.\nSi lo comparamos con los números conseguidos con MySQL usando InnoDB con las opciones por defecto de \u0026ldquo;MySQL test suite\u0026rdquo;, está pero que muy bien, ya que en el mejor de los casos estaban consiguiendo unas 20-25 tablas por segundo, con lo cual hubieran tardado unos 20 minutos en terminar.\nEra hora de empezar a apagar parámetros relacionados con la durabilidad de los datos. El primero synchronous_commit, lo apagamos y volvemos a ejecutar el test.\nPara este test en concreto, el apagado de este parámetro no afecta prácticamente en nada al resultado final, (47834.357 ms, 37889.342 ms y 546201.158 ms respectivamente). La explicación que yo le encuentro a esto es que todo lo ejecutado en un bloque DO se ejecuta dentro de una misma transacción, con lo que en realidad estamos ejecutando solamente 3 transacciones.\nSeguimos, definimos wal_level = minimal, fsync = off y vamos a ver si conseguimos una mejora en la velocidad.\nCon este cambio si que obtenemos una importante mejora en la creación y la inserción (31823.101 ms y 35474.310 ms respectivamente), en total 67.2 segundos, que supone una reducción de 20.3 segundos o lo que es lo mismo un 23.2% menos de tiempo con respecto a la primera vez. La destrucción de las tablas no mejora en absoluto (547486.861 ms)\nEn una situación real en la que estemos importando una base de datos grande no vamos a destruir seguidamente los datos importados, asi que el uso de wal_level = minimal, fsync = off y synchronous_commit = off nos puede ahorrar en general una gran cantidad de tiempo. No olvidar volver a cambiar estos parámetros al terminar para asegurar la durabilidad de los datos.\nMe quedo con la duda de porque la destrucción de todas las tablas dura tanto, especialmente cuando la creación de las mismas ha tardado solamente 31 segundos. Haciendo un strace al proceso que está ejecutando las ordenes, podemos ver que una serie de operaciones de este tipo se repiten durante todo el tiempo que el proceso se está ejecutando:\nopen(\u0026#34;base/42616854/42941855\u0026#34;, O_RDWR) = 10 close(10) = 0 open(\u0026#34;base/42616854/42941855\u0026#34;, O_RDWR) = 10 ftruncate(10, 0) = 0 close(10) = 0 unlink(\u0026#34;base/42616854/42941855.1\u0026#34;) = -1 ENOENT (No such file or directory) open(\u0026#34;base/42616854/42941855_fsm\u0026#34;, O_RDWR) = -1 ENOENT (No such file or directory) open(\u0026#34;base/42616854/42941855_vm\u0026#34;, O_RDWR) = -1 ENOENT (No such file or directory) open(\u0026#34;base/42616854/42941855_init\u0026#34;, O_RDWR) = -1 ENOENT (No such file or directory) Parece ser que está intentando limpiar y encontrar una serie de ficheros asociados a cada tabla que está destruyendo y esto es lo que consume todo el tiempo, especialmente cuando estos ficheros todavía no se encuentran en el sistema. ¿Quizás alguien tenga alguna información de porque esto es así y si se puede hacer algo para mejorar los tiempos de destrucción de tablas?\nLos tiempos totales conseguidos con strace -c -p son:\nCREACION de tablas: % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 61.12 0.087196 0 184077 64014 open 18.07 0.025772 1 44762 write 12.43 0.017730 633 28 munmap 3.20 0.004565 0 120074 close 2.41 0.003438 0 164859 lseek 1.97 0.002811 0 60003 1 stat 0.46 0.000655 0 3340 sendto 0.29 0.000419 0 4652 brk 0.03 0.000042 0 139 read 0.01 0.000019 0 820 semop 0.01 0.000012 0 430 kill 0.00 0.000000 0 1 fstat 0.00 0.000000 0 59 mmap 0.00 0.000000 0 1 mprotect 0.00 0.000000 0 1 rt_sigreturn 0.00 0.000000 0 1 select 0.00 0.000000 0 2 setitimer 0.00 0.000000 0 4 recvfrom 0.00 0.000000 0 1 unlink 0.00 0.000000 0 1 futex ------ ----------- ----------- --------- --------- ---------------- 100.00 0.142659 583255 64015 total INSERCION de filas: % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 40.93 0.047498 1 60000 write 39.83 0.046216 0 120004 60000 open 12.85 0.014908 0 30002 read 3.06 0.003555 0 119021 lseek 2.38 0.002763 0 60005 close 0.95 0.001103 0 6671 sendto 0.00 0.000000 0 22 brk 0.00 0.000000 0 1 rt_sigreturn 0.00 0.000000 0 2 setitimer 0.00 0.000000 0 4 recvfrom 0.00 0.000000 0 13 kill 0.00 0.000000 0 20 semop ------ ----------- ----------- --------- --------- ---------------- 100.00 0.116043 395765 60000 total DESTRUCCION de tablas: % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 69.45 0.330171 6 60000 ftruncate 17.79 0.084591 0 300009 180000 open 8.29 0.039411 1 60000 60000 unlink 2.81 0.013347 404 33 brk 1.65 0.007838 0 120008 close 0.00 0.000011 0 369 kill 0.00 0.000010 0 707 semop 0.00 0.000000 0 8 read 0.00 0.000000 0 1 write 0.00 0.000000 0 6 lseek 0.00 0.000000 0 1 rt_sigreturn 0.00 0.000000 0 2 setitimer 0.00 0.000000 0 6 sendto 0.00 0.000000 0 4 recvfrom ------ ----------- ----------- --------- --------- ---------------- 100.00 0.475379 541154 240000 total Casi un 70% del tiempo es consumido por ftruncate\nQueda decir que en este pequeño test realizado con MySQL usando InnoDB, consiguieron reducir los tiempos de ejecución a 180 segundos. PostgreSQL consigue ejecutar las dos partes relevantes del test en 67 segundos, pero tarda mucho en la destrucción de los datos.\nTambién hay que decir que esta comparación entre los tiempos conseguidos con MySQL y PostgreSQL es totalmente inútil al haberse realizado en máquinas diferentes, y al no saber la definición de la tabla que utilizaron, con lo que no podemos comparar los resultados. De todas maneras ahí quedan los datos para el que le interese estas cosas.\n","date":"2011/12/23","externalUrl":null,"permalink":"/es/blog/creando-30000-tablas-con-postgresql/","section":"Blogs","summary":"Esta mañana leyendo los mensajes de Twitter que me llegaron por la noche, me encontre con uno que me llamó la atención, “Steward Smith blogs on Optimazing InnoDB for creating 30.000 tables (and nothing else)”.","title":"Creando 30.000 tablas con PostgreSQL","type":"blog"},{"content":"Al final del día, una base de datos tiene toda la información que necesita para funcionar grabada en nuestro disco duro. PostgreSQL no es una excepción.\nEn este artículo vamos a intentar dar una introducción sobre como PostgreSQL 9.1 graba nuestros datos en el disco y como se las arregla para encontrar los mismos cuando los necesitamos. Tener las cosas claras en lo que respecta a este tema nos puede ayudar en momentos difíciles como administradores de bases de datos, en el caso que nuestros datos se corrompan por alguna causa.\nOrganizacion en el disco # Para empezar vamos a ver como PostgreSQL organiza los ficheros con nuestros datos en el disco. Todos los ficheros usados por PostgreSQL se encuentran en el directorio que hayamos definido como directorio de datos (data_directory) en nuestro sistema.\npostgres=# SHOW data_directory; data_directory ---------------- /var/pgsql (1 row) Dentro del directorio de datos encontraremos varios subdirectorios con diferentes cometidos.\npostgres@server01:~$ cd /var/pgsql/ postgres@server01:/var/pgsql$ ls -l total 92 drwx------ 7 postgres nogroup 4096 2011-10-04 18:01 base drwx------ 2 postgres nogroup 4096 2011-10-04 18:24 global drwx------ 2 postgres nogroup 4096 2011-10-04 17:53 pg_clog -rw------- 1 postgres nogroup 4476 2011-10-04 17:53 pg_hba.conf -rw------- 1 postgres nogroup 1636 2011-10-04 17:53 pg_ident.conf drwx------ 4 postgres nogroup 4096 2011-10-04 17:53 pg_multixact drwx------ 2 postgres nogroup 4096 2011-10-04 17:53 pg_notify drwx------ 2 postgres nogroup 4096 2011-10-04 17:53 pg_serial drwx------ 2 postgres nogroup 4096 2011-10-05 11:23 pg_stat_tmp drwx------ 2 postgres nogroup 4096 2011-10-04 17:53 pg_subtrans drwx------ 2 postgres nogroup 4096 2011-10-04 17:53 pg_tblspc drwx------ 2 postgres nogroup 4096 2011-10-04 17:53 pg_twophase -rw------- 1 postgres nogroup 4 2011-10-04 17:53 PG_VERSION drwx------ 3 postgres nogroup 4096 2011-10-04 17:53 pg_xlog -rw------- 1 postgres nogroup 19129 2011-10-04 17:53 postgresql.conf -rw------- 1 postgres nogroup 42 2011-10-04 17:53 postmaster.opts -rw------- 1 postgres nogroup 68 2011-10-04 17:53 postmaster.pid El que nos interesa en este artículo es uno que se llama base. Dentro de este subdirectorio se graban todos los datos contenidos en nuestras bases de datos.\nEn el sistema utilizado para este artículo tenemos lo siguiente en este directorio:\npostgres@server01:/var/pgsql$ ls -l base/ total 28 drwx------ 2 postgres nogroup 12288 2011-10-04 17:53 1 drwx------ 2 postgres nogroup 4096 2011-10-04 17:53 11939 drwx------ 2 postgres nogroup 4096 2011-10-04 17:53 11947 Como podeis ver, dentro del subdirectorio base existen otros subdirectorios com nombres numéricos. Cada subdirectorio dentro del directorio base, es una base de datos diferente. Para saber a que base de datos corresponden estos subdirectorios podeis ejecutar este comando SQL en vuestro cliente:\npostgres=# SELECT datid,datname from pg_stat_database; datid | datname -------+----------- 1 | template1 11939 | template0 11947 | postgres (5 rows) Los valores en la columna datid, corresponden a los valores listados en el subdirectorio base y la columna datname es el nombre de la base de datos asociada al identificador numérico.\nVamos a crear una base de datos para nuestros ejemplos y a ver como esto afecta a nuestro sistema.\npostgres=# CREATE DATABASE testing_internals; CREATE DATABASE Podemos ver como se ha creado un nuevo subdirectorio con el nombre 16407, correspondiente a la nueva base de datos creada.\npostgres@server01:/var/pgsql$ ls -l base/ total 24 drwx------ 2 postgres nogroup 12288 2011-10-04 17:53 1 drwx------ 2 postgres nogroup 4096 2011-10-04 17:53 11939 drwx------ 2 postgres nogroup 4096 2011-10-04 17:53 11947 drwx------ 2 postgres nogroup 4096 2011-10-05 11:31 16407 postgres=# SELECT datid,datname from pg_stat_database; datid | datname -------+------------------- 1 | template1 11939 | template0 11947 | postgres 16407 | testing_internals (4 rows) Si haceis un listado de este nuevo subdirectorio vereis que ya tiene una serie de ficheros aunque no hayais creado ninguna tabla todavía. Estos ficheros pertenecen al sistema y son necesarios para que la base de datos que habeis creado funcione. Para este artículo no necesitamos saber nada más sobre los mismos, solo que estan ahí y que son necesarios.\nAhora creamos una tabla muy simple en nuestra base de datos con un par de columnas de tipo integer:\npostgres=# \\c testing_internals You are now connected to database \u0026#34;testing_internals\u0026#34; as user \u0026#34;postgres\u0026#34;. testing_internals=# CREATE TABLE test001 ( id INTEGER, code INTEGER, primary key(id)); NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index \u0026#34;test001_pkey\u0026#34; for table \u0026#34;test001\u0026#34; CREATE TABLE testing_internals=# \\d test001 Table \u0026#34;public.test001\u0026#34; Column | Type | Modifiers --------+---------+----------- id | integer | not null code | integer | Indexes: \u0026#34;test001_pkey\u0026#34; PRIMARY KEY, btree (id) El identificador de la tabla test001 y el fichero correspondiente lo podemos obtener por ejemplo de esta manera:\ntesting_internals=# SELECT pg_relation_filenode(\u0026#39;test001\u0026#39;),pg_relation_filepath(\u0026#39;test001\u0026#39;); pg_relation_filenode | pg_relation_filepath ----------------------+---------------------- 16465 | base/16407/16465 (1 row) Y el del índice test001_pkeycreado para clave primaria de esta tabla con:\ntesting_internals=# SELECT pg_relation_filenode(\u0026#39;test001_pkey\u0026#39;),pg_relation_filepath(\u0026#39;test001_pkey\u0026#39;); pg_relation_filenode | pg_relation_filepath ----------------------+---------------------- 16468 | base/16407/16468 (1 row) Si hacemos un listado del contenido del directorio de nuestra base de datos (base/16407) podremos ver que se han creado dos nuevos ficheros con los nombres 16465 y 16468.\npostgres@server01:/var/pgsql$ ls -l base/16407/16465 -rw------- 1 postgres nogroup 0 2011-10-06 12:09 base/16407/16465 postgres@server01:/var/pgsql$ ls -l base/16407/16468 -rw------- 1 postgres nogroup 8192 2011-10-06 12:09 base/16407/16468 Si esta tabla o índice llegasen a ser mayores que 1GB, se dividirian a nivel del sistema de ficheros en ficheros con un máximo de 1GB cada uno. Si por ejemplo, nuestra tabla llegase a ser de 3,5GB, veriamos algo similar a esto:\n[postgres@server]$ ls -l base/16407/16465* -rw------- 1 postgres pgdba 1073741824 Jul 5 15:11 base/16407/16465 -rw------- 1 postgres pgdba 1073741824 Jul 6 15:11 base/16407/16465.1 -rw------- 1 postgres pgdba 1073741824 Jul 7 15:11 base/16407/16465.2 -rw------- 1 postgres pgdba 536870912 Jul 8 15:11 base/16407/16465.3 Bloques de datos en el disco # Una vez visto como encontrar en nuestro sistema de ficheros nuestras bases de datos con sus tablas e índices, tenemos que saber como se graban nuestros datos en estos ficheros.\nLo primero que tenemos que decir es que la unidad mínima de almacenamiento en PostgreSQL se denomina, indistintamente, página (page) o bloque (block). Un bloque en PostgreSQL ocupa siempre por defecto 8K si no se ha definido un valor diferente durante la compilación. Esto independientemente de si se usa en su totalidad o solo parcialmente.\nA continuación vamos a ver como se divide internamente el espacio en una página o bloque:\nUn bloque (8K / 8192 bytes) está compuesto por diferentes elementos:\nPage_header: Cabecera de bloque. Ocupa 24 bytes. ItemId: Matriz de pares de valores ItemId(offset,length) con la información necesaria para localizar los elementos (Items) grabados en el bloque. Cada par ocupa 4 bytes. Item (row/index): Cabecera de elemento más los datos en si. Tamaño variable. Espacio especial: Usado cuando el bloque pertenece a un índice. Tamaño variable El espacio usado por una cabecera de bloque (Page_header) se usa para guardar diferentes parámetros que nos ayudarán a localizar diferentes partes del bloque y guardar cierta información asociada al bloque.\nEn cada elemento (Item) se guardan una cabecera de datos con un tamaño fijo (23 bytes), una pequeña cabecera opcional de datos con tamaño variable y los datos en si de nuestras tablas o índices.\nPara una completa descripción de estas cabeceras y las estructuras usadas para almacenar los datos en el disco, podeis consultar la documentación y el código fuente. Teneis los enlaces al final del artículo. En este artículo vamos a ver solamente las más relevantes para el mismo.\nObteniendo información de los bloques de datos # Antes de seguir vamos a instalar unos cuantos módulos contrib que se distribuyen con PostgreSQL y que nos servirán de mucha ayuda en este artículo para consultar y analizar la información que tenemos grabada en el disco. A partir de la versión 9.1, podemos instalar estos módulos de manera muy fácil con la nueva funcionalidad CREATE EXTENSION:\npostgres=# \\c testing_internals You are now connected to database \u0026#34;testing_internals\u0026#34; as user \u0026#34;postgres\u0026#34;. testing_internals=# CREATE EXTENSION pgstattuple; CREATE EXTENSION testing_internals=# CREATE EXTENSION pageinspect; CREATE EXTENSION A continuación vamos a insertar algunas filas en nuestra tabla de pruebas test001:\ntesting_internals=# INSERT INTO test001 (id,code) VALUES (1,100); INSERT 0 1 testing_internals=# SELECT ctid,* from test001 ; ctid | id | code -------+----+------ (0,1) | 1 | 100 (1 row) El valor ctid(0,1) nos indica que esta fila está en el bloque numero 0 y que es el elemento 1 (Item) en ese bloque.\nYa hemos grabado los primeros datos en nuestra tabla y sabemos que ocupará por defecto 1 bloque 8K. Si listamos el fichero correspondiente a nuestra tabla podemos ver que efectivamente ocupa exactamente 8K. Este fichero seguirá teniendo 8K hasta que utilicemos todo el espacio libre en el mismo con sucesivas actualizaciones de la tabla:\npostgres@server01:/var/pgsql$ ls -l base/16407/16465 -rw------- 1 postgres nogroup 8192 2011-10-06 12:30 base/16407/16465 Utilizando una de las funciones disponibles en la extensión pgstattuple, podemos obtener cierta información sobre nuestra tabla:\ntesting_internals=# SELECT * from pgstattuple(\u0026#39;test001\u0026#39;); table_len | tuple_count | tuple_len | tuple_percent | dead_tuple_count | dead_tuple_len | dead_tuple_percent | free_space | free_percent -----------+-------------+-----------+---------------+------------------+----------------+--------------------+------------+-------------- 8192 | 1 | 32 | 0.39 | 0 | 0 | 0 | 8128 | 99.22 (1 row) Como podeis ver, los datos corresponden a lo que hemos hecho con la tabla. El tamaño actual de la tabla (table_len) es 8192 bytes (1 bloque), solamente tenemos 1 fila (tuple_count), la única fila que tenemos ocupa 32 bytes (23 bytes de cabeceras fijas + 1 byte de alineación hasta el comienzo de los datos + 4 bytes de la primera columna integer + 4 bytes de la segunda columna), no hay filas muertas y el espacio libre en el bloque es de 8128 bytes.\nUsando la extensión pageinspect podemos obtener más información sobre el primer bloque en disco de nuestra tabla. Sabemos que solamente hemos usado un bloque por el valor ctid de nuestra primera y única fila.\nVamos a utilizar las funciones page_header() y heap_page_items() junto con la función get_raw_page() para obtener información sobre los datos disponibles en la cabecera del bloque, en las entradas itemId y en las cabeceras de los elementos (Item):\ntesting_internals=# SELECT * FROM page_header(get_raw_page(\u0026#39;test001\u0026#39;, 0)); lsn | tli | flags | lower | upper | special | pagesize | version | prune_xid -----------+-----+-------+-------+-------+---------+----------+---------+----------- 0/17F7A00 | 1 | 0 | 28 | 8160 | 8192 | 8192 | 4 | 0 (1 row) testing_internals=# SELECT * FROM heap_page_items(get_raw_page(\u0026#39;test001\u0026#39;, 0)); lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid ----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+------- 1 | 8160 | 1 | 32 | 755 | 0 | 0 | (0,1) | 2 | 2304 | 24 | | (1 row) Aquí los datos también corresponden a lo que hemos hecho.\nDe la cabecera de bloque tenemos el parámetro lower = 28 que nos indica la siguiente posición libre en donde se grabará el próximo itemId. upper = 8160 nos indica el final del espacio libre en el bloque (a partir de esta posición tendremos todos los elementos (Items) grabados en este bloque. special = 8192 indica el principio (offset) del espacio especial (en este caso al ser una tabla y no un índice, special es igual al tamaño de bloque porque el espacio especial esta vacio)\nDe las entradas itemId (tambien llamadas Line Pointer) y las cabeceras de los elementos (Item), podemos destacar lp = 1 que nos indica que es el itemId número 1, lp_off = 8160 nos indica el inicio (offset) del primer elemento (item), lp_flags = 1 nos indica que el itemId está en uso, lp_len = 32 es el tamaño en bytes del elemento al que el itemId está apuntando, t_xmin = 755 nos indica la transacción en donde este elemento fue creado, t_xmax es la transacción en la que el elemento ha sido borrado (0 en este caso porque todavía no se ha borrado), t_ctid = (0,1) nos dice que este elemento es el primero del bloque 0 en esta tabla y t_hoff = 24 es la posición relativa en donde empezamos a grabar los datos en si de las columnas de la primara fila (la posicion absoluta seria lp_off + t_hoff = 8184).\nCon todos estos datos podemos tener mas control sobre donde están nuestros datos grabados y como. Puede que este conocimiento nos ayude en momentos críticos. Vamos a seguir haciendo cambios en nuestra tabla y ver como se van cambiando los valores de los que hemos hablado.\nEs un buen ejercicio para ir familiarizandose con el tema. Intentar comprender los valores que van cambiando y porque.\nInsertamos un par de filas nuevas:\ntesting_internals=# INSERT INTO test001 (id,code) VALUES (2,200); INSERT 0 1 testing_internals=# INSERT INTO test001 (id,code) VALUES (3,300); INSERT 0 1 testing_internals=# SELECT ctid,* from test001 ; ctid | id | code -------+----+------ (0,1) | 1 | 100 (0,2) | 2 | 200 (0,3) | 3 | 300 (3 rows) testing_internals=# SELECT * from pgstattuple(\u0026#39;test001\u0026#39;); ```text table_len | tuple_count | tuple_len | tuple_percent | dead_tuple_count | dead_tuple_len | dead_tuple_percent | free_space | free_percent -----------+-------------+-----------+---------------+------------------+----------------+--------------------+------------+-------------- 8192 | 3 | 96 | 1.17 | 0 | 0 | 0 | 8056 | 98.34 (1 row) testing_internals=# SELECT * FROM page_header(get_raw_page(\u0026#39;test001\u0026#39;, 0)); lsn | tli | flags | lower | upper | special | pagesize | version | prune_xid -----------+-----+-------+-------+-------+---------+----------+---------+----------- 0/18BA7F8 | 1 | 0 | 36 | 8096 | 8192 | 8192 | 4 | 0 (1 row) testing_internals=# SELECT * FROM heap_page_items(get_raw_page(\u0026#39;test001\u0026#39;, 0)); lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid ----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+------- 1 | 8160 | 1 | 32 | 755 | 0 | 0 | (0,1) | 2 | 2304 | 24 | | 2 | 8128 | 1 | 32 | 756 | 0 | 0 | (0,2) | 2 | 2304 | 24 | | 3 | 8096 | 1 | 32 | 846 | 0 | 0 | (0,3) | 2 | 2304 | 24 | | (3 rows) Borramos la fila con id = 2.\ntesting_internals=# DELETE FROM test001 WHERE id = 2; DELETE 1 testing_internals=# SELECT ctid,* from test001 ; ctid | id | code -------+----+------ (0,1) | 1 | 100 (0,3) | 3 | 300 (2 rows) testing_internals=# SELECT * from pgstattuple(\u0026#39;test001\u0026#39;); table_len | tuple_count | tuple_len | tuple_percent | dead_tuple_count | dead_tuple_len | dead_tuple_percent | free_space | free_percent -----------+-------------+-----------+---------------+------------------+----------------+--------------------+------------+-------------- 8192 | 2 | 64 | 0.78 | 1 | 32 | 0.39 | 8056 | 98.34 (1 row) testing_internals=# SELECT * FROM page_header(get_raw_page(\u0026#39;test001\u0026#39;, 0)); lsn | tli | flags | lower | upper | special | pagesize | version | prune_xid -----------+-----+-------+-------+-------+---------+----------+---------+----------- 0/18BA920 | 1 | 0 | 36 | 8096 | 8192 | 8192 | 4 | 847 (1 row) testing_internals=# SELECT * FROM heap_page_items(get_raw_page(\u0026#39;test001\u0026#39;, 0)); lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid ----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+------- 1 | 8160 | 1 | 32 | 755 | 0 | 0 | (0,1) | 2 | 2304 | 24 | | 2 | 8128 | 1 | 32 | 756 | 847 | 0 | (0,2) | 2 | 1280 | 24 | | 3 | 8096 | 1 | 32 | 846 | 0 | 0 | (0,3) | 2 | 2304 | 24 | | (3 rows) Ejecutamos un VACCUM.\ntesting_internals=# VACUUM ; VACUUM testing_internals=# SELECT * from pgstattuple(\u0026#39;test001\u0026#39;); table_len | tuple_count | tuple_len | tuple_percent | dead_tuple_count | dead_tuple_len | dead_tuple_percent | free_space | free_percent -----------+-------------+-----------+---------------+------------------+----------------+--------------------+------------+-------------- 8192 | 2 | 64 | 0.78 | 0 | 0 | 0 | 8088 | 98.73 (1 row) testing_internals=# SELECT * FROM page_header(get_raw_page(\u0026#39;test001\u0026#39;, 0)); lsn | tli | flags | lower | upper | special | pagesize | version | prune_xid -----------+-----+-------+-------+-------+---------+----------+---------+----------- 0/18C1740 | 1 | 1 | 36 | 8128 | 8192 | 8192 | 4 | 0 (1 row) testing_internals=# SELECT * FROM heap_page_items(get_raw_page(\u0026#39;test001\u0026#39;, 0)); lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid ----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+------- 1 | 8160 | 1 | 32 | 755 | 0 | 0 | (0,1) | 2 | 2304 | 24 | | 2 | 0 | 0 | 0 | | | | | | | | | 3 | 8128 | 1 | 32 | 846 | 0 | 0 | (0,3) | 2 | 2304 | 24 | | (3 rows) Volvemos a introducir otra fila.\ntesting_internals=# SELECT ctid,* from test001 ; ctid | id | code -------+----+------ (0,1) | 1 | 100 (0,2) | 4 | 400 (0,3) | 3 | 300 (3 rows) testing_internals=# INSERT INTO test001 (id,code) VALUES (4,400); INSERT 0 1 testing_internals=# SELECT * from pgstattuple(\u0026#39;test001\u0026#39;); table_len | tuple_count | tuple_len | tuple_percent | dead_tuple_count | dead_tuple_len | dead_tuple_percent | free_space | free_percent -----------+-------------+-----------+---------------+------------------+----------------+--------------------+------------+-------------- 8192 | 3 | 96 | 1.17 | 0 | 0 | 0 | 8056 | 98.34 (1 row) testing_internals=# SELECT * FROM page_header(get_raw_page(\u0026#39;test001\u0026#39;, 0)); lsn | tli | flags | lower | upper | special | pagesize | version | prune_xid -----------+-----+-------+-------+-------+---------+----------+---------+----------- 0/18C1870 | 1 | 1 | 36 | 8096 | 8192 | 8192 | 4 | 0 (1 row) testing_internals=# SELECT * FROM heap_page_items(get_raw_page(\u0026#39;test001\u0026#39;, 0)); lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid ----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+------- 1 | 8160 | 1 | 32 | 755 | 0 | 0 | (0,1) | 2 | 2304 | 24 | | 2 | 8096 | 1 | 32 | 848 | 0 | 0 | (0,2) | 2 | 2304 | 24 | | 3 | 8128 | 1 | 32 | 846 | 0 | 0 | (0,3) | 2 | 2304 | 24 | | (3 rows) Seguir \u0026ldquo;jugando\u0026rdquo; y probando cosas e intentar familiarizaros con el tema. Han quedado varias cosas en el tintero, como por ejemplo, como interpretar estos datos con índices o con columnas de tamaño variable y valores TOAST. Más adelante intentaremos hablar del tema.\nEn un próximo artículo veremos como podremos aplicar lo que hemos aprendido en este artículo en la vida real.\nEnlaces:\nhttp://www.postgresql.org/docs/current/interactive/storage-file-layout.html http://www.postgresql.org/docs/current/interactive/storage-page-layout.html http://www.postgresql.org/docs/current/interactive/contrib.html http://www.postgresql.org/docs/current/interactive/pageinspect.html http://www.postgresql.org/docs/current/interactive/pgstattuple.html http://www.postgresql.org/docs/current/interactive/datatype.html ","date":"2011/10/07","externalUrl":null,"permalink":"/es/blog/donde-estan-nuestros-datos-en-el-disco/","section":"Blogs","summary":"Al final del día, una base de datos tiene toda la información que necesita para funcionar grabada en nuestro disco duro. PostgreSQL no es una excepción.","title":"Dónde están nuestros datos en el disco?","type":"blog"},{"content":"Hace unos dias escribi una entrada sobre como generar un gráfico de las llaves foráneas de una base de datos. El método utilizado fue escribir una consulta SQL que utilizando datos contenidos en el esquema information_schema generase una salida que se pudiese utilizar con Graphviz para generar un gráfico.\nHoy tenia ganas de seguir \u0026ldquo;jugando\u0026rdquo; con este tema y probar nuevas posibilidades. Partiendo de la consulta SQL utilizada en la entrada anterior, vamos a modificarla para generar un gráfico con todas las tablas contenidas en una base de datos y las relaciones entra ellas.\nEsta consulta SQL solo funciona con PostgreSQL 9.0 o posterior y tiene en cuenta si existen multiples esquemas (schemas) en la base de datos en donde la ejecutemos. Al contrario que la consulta en la entrada anterior, esta genera todo lo necesario por Graphviz, asi que no tendremos que copiar/pegar y editar ficheros para obtener nuestro gráfico.\nLa consulta SQL de hoy es esta:\nSELECT E\u0026#39;digraph database_schema { concentrate=true; nodesep=1; graph [ overlap=false ]; \\n\u0026#39; || (SELECT string_agg(\u0026#39;\u0026#34;\u0026#39; || a.table_schema || \u0026#39;.\u0026#39; || a.table_name || E\u0026#39;\u0026#34; [\\n\u0026#39; || \u0026#39;label = \u0026lt;\u0026lt;TABLE BORDER=\u0026#34;0\u0026#34; CELLBORDER=\u0026#34;1\u0026#34; CELLSPACING=\u0026#34;0\u0026#34; CELLPADDING=\u0026#34;4\u0026#34;\u0026gt;\u0026#39; || (SELECT \u0026#39;\u0026lt;TR\u0026gt;\u0026lt;TD BGCOLOR=\u0026#34;grey\u0026#34; PORT=\u0026#34;0\u0026#34;\u0026gt;\u0026lt;B\u0026gt;\u0026#39; || a.table_schema || \u0026#39;.\u0026#39; || a.table_name || \u0026#39;\u0026lt;/B\u0026gt;\u0026lt;/TD\u0026gt;\u0026lt;/TR\u0026gt;\u0026#39; || string_agg(\u0026#39;\u0026lt;TR\u0026gt;\u0026lt;TD BGCOLOR=\u0026#34;white\u0026#34; PORT=\u0026#34;\u0026#39; || ordinal_position || \u0026#39;\u0026#34; ALIGN=\u0026#34;LEFT\u0026#34;\u0026gt;\u0026#39; || column_name || \u0026#39; (\u0026#39; || upper(data_type) || \u0026#39;)\u0026lt;/TD\u0026gt;\u0026lt;/TR\u0026gt;\u0026#39;,\u0026#39;\u0026#39;) FROM information_schema.columns WHERE table_schema NOT IN (\u0026#39;pg_catalog\u0026#39;,\u0026#39;information_schema\u0026#39;) AND table_schema = a.table_schema AND table_name = a.table_name) || E\u0026#39; \u0026lt;/TABLE\u0026gt;\u0026gt;, shape = none];\\n\\n\u0026#39;,\u0026#39;\u0026#39;) FROM information_schema.tables a WHERE a.table_schema NOT IN (\u0026#39;pg_catalog\u0026#39;,\u0026#39;information_schema\u0026#39;) AND a.table_type = \u0026#39;BASE TABLE\u0026#39;) || (SELECT string_agg(output,\u0026#39;\u0026#39;) FROM ( SELECT DISTINCT ON (\u0026#39;\u0026#34;\u0026#39; || fkey_table_schema || \u0026#39;.\u0026#39; || fkey_table || \u0026#39;\u0026#34;:\u0026#39; || fkey_position || \u0026#39; -\u0026gt; \u0026#34;\u0026#39; || pkey_table_schema || \u0026#39;.\u0026#39; || pkey_table || \u0026#39;\u0026#34;:\u0026#39; || pkey_position) \u0026#39;\u0026#34;\u0026#39; || fkey_table_schema || \u0026#39;.\u0026#39; || fkey_table || \u0026#39;\u0026#34;:\u0026#39; || fkey_position || \u0026#39; -\u0026gt; \u0026#34;\u0026#39; || pkey_table_schema || \u0026#39;.\u0026#39; || pkey_table || \u0026#39;\u0026#34;:\u0026#39; || pkey_position || E\u0026#39;;\\n\u0026#39; AS output FROM ( SELECT a.constraint_name, b.table_schema AS fkey_table_schema, b.table_name AS fkey_table, b.column_name AS fkey_column, c.ordinal_position AS fkey_position FROM information_schema.referential_constraints a INNER JOIN information_schema.key_column_usage b ON a.constraint_name = b.constraint_name INNER JOIN information_schema.columns c ON (b.column_name = c.column_name AND b.table_name = c.table_name) ) AS fkey_constraints INNER JOIN ( SELECT a.unique_constraint_name, b.table_schema AS pkey_table_schema, b.table_name AS pkey_table, b.column_name AS pkey_column, c.ordinal_position AS pkey_position, a.constraint_name FROM information_schema.referential_constraints a INNER JOIN information_schema.constraint_column_usage b ON a.unique_constraint_name = b.constraint_name INNER JOIN information_schema.columns c ON (b.column_name = c.column_name AND b.table_name = c.table_name) ) AS pkey_constraints ON fkey_constraints.constraint_name = pkey_constraints.constraint_name) AS node_relations ) || E\u0026#39;\\n}\\n\u0026#39;; La podeis grabar en un fichero llamado, por ejemplo, relaciones_tablas.sql y ejecutar este comando para obtener vuestro gráfico:\npsql -At \u0026#34;tu_base_de_datos\u0026#34; \u0026lt; relaciones_tablas.sql | dot -Tpng -o relaciones_tablas.png El resultado que yo he conseguido ejecutando este comando en la base de datos de NAV - Network Administration Visualized es el siguiente:\n","date":"2011/04/15","externalUrl":null,"permalink":"/es/blog/grafico-del-esquema-de-una-base-de-datos/","section":"Blogs","summary":"Hace unos dias escribi una entrada sobre como generar un gráfico de las llaves foráneas de una base de datos. El método utilizado fue escribir una consulta SQL que utilizando datos contenidos en el esquema information_schema generase una salida que se pudiese utilizar con Graphviz para generar un gráfico.","title":"Gráfico del esquema de una base de datos","type":"blog"},{"content":"","date":"2011/04/15","externalUrl":null,"permalink":"/es/tags/graphviz/","section":"Tags","summary":"","title":"Graphviz","type":"tags"},{"content":"Ayer, uno de los sistemas de monitorización de red que utilizamos en la universidad NAV - Network Administration Visualized tuvo problemas con una de las consultas DELETE que mandaba a la base de datos PostgreSQL que utiliza.\nLa base de datos que utiliza NAV es un poco complicada, con muchas claves foráneas, triggers y rules para garantizar la integridad de los datos. En el proceso de busqueda del fallo, una de las cosas que queria tener era una relación de todas las tablas y sus claves foráneas para hacerme una idea de como las diferentes tablas estaban relacionadas entre si. En una base de datos pequeña y sin muchas relaciones esto no seria un problema pero a medida que la cosa crece, se va complicando rápidamente.\nNecesitaba algo rápido y fácil que me diera esta información sin tener que instalar ningún programa adicional, además solamente me interesaba la relación de claves foráneas y no una representación completa de la base de datos.\nY como no hay nada más rápido que una consola con psql y una consulta SQL apropiada, me puse manos a la obra para generar una consulta SQL que me generase una salida que pudiese utilizar con Graphviz, para crear un gráfico con las relaciones. En debian/ubuntu, Graphviz se puede instalar con: sudo apt-get install graphviz\nLa consulta que se me ocurrio es la siguiente:\nSELECT DISTINCT ON (\u0026#39;\u0026#34;\u0026#39; || fkey_table || \u0026#39;\u0026#34; -\u0026gt; \u0026#34;\u0026#39; || pkey_table || \u0026#39;(\u0026#39; || pkey_column || \u0026#39;)\u0026#34;;\u0026#39;) \u0026#39;\u0026#34;\u0026#39; || fkey_table || \u0026#39;\u0026#34; -\u0026gt; \u0026#34;\u0026#39; || pkey_table || \u0026#39;(\u0026#39; || pkey_column || \u0026#39;)\u0026#34;;\u0026#39; FROM ( SELECT a.constraint_name, b.column_name AS fkey_column, b.table_name AS fkey_table FROM information_schema.referential_constraints a INNER JOIN information_schema.key_column_usage b ON a.constraint_name = b.constraint_name ) AS fkey_constraints INNER JOIN ( SELECT a.unique_constraint_name, b.column_name AS pkey_column, b.table_name AS pkey_table, a.constraint_name FROM information_schema.referential_constraints a INNER JOIN information_schema.constraint_column_usage b ON a.unique_constraint_name = b.constraint_name ) AS pkey_constraints ON fkey_constraints.constraint_name = pkey_constraints.constraint_name; Esta consulta te devuelve una lista con lineas del tipo:\n\u0026#34;accountalertqueue\u0026#34; -\u0026gt; \u0026#34;account(id)\u0026#34;; \u0026#34;accountalertqueue\u0026#34; -\u0026gt; \u0026#34;account(id)\u0026#34;; \u0026#34;accountalertqueue\u0026#34; -\u0026gt; \u0026#34;alertq(alertqid)\u0026#34;; \u0026#34;accountalertqueue\u0026#34; -\u0026gt; \u0026#34;alertsubscription(id)\u0026#34;; \u0026#34;accountgroup_accounts\u0026#34; -\u0026gt; \u0026#34;account(id)\u0026#34;; \u0026#34;accountgroup_accounts\u0026#34; -\u0026gt; \u0026#34;accountgroup(id)\u0026#34;; ........... \u0026#34;type\u0026#34; -\u0026gt; \u0026#34;vendor(vendorid)\u0026#34;; \u0026#34;vlan\u0026#34; -\u0026gt; \u0026#34;nettype(nettypeid)\u0026#34;; \u0026#34;vlan\u0026#34; -\u0026gt; \u0026#34;org(orgid)\u0026#34;; \u0026#34;vlan\u0026#34; -\u0026gt; \u0026#34;usage(usageid)\u0026#34;; En donde el primer termino antes del simbolo \u0026ldquo;-\u0026gt;\u0026rdquo; es el nombre de una tabla en vuestra base de datos y el segundo una clave foránea (tabla(columna)) definida en esta tabla.\nAhora lo único que nos queda es copiar y pegar el resultado en un editor de texto, añadirle un par de lineas extras, grabarlo en un fichero y ejecutar Graphviz sobre el mismo. Podemos grabar un fichero llamado claves_foraneas.dot con estos datos:\ndigraph fkey_pkey_relacion{ K=1; Aqui vendria la salida de ejecutar el comando SQL en vustro sistema } y ejecutar este comando para generar un fichero PNG:\nfdp -Tpng -o claves_foraneas.png claves_foraneas.dot El resultado que conseguí lo podeis ver a continuación:\n","date":"2011/04/13","externalUrl":null,"permalink":"/es/blog/grafico-de-llaves-foraneas/","section":"Blogs","summary":"Ayer, uno de los sistemas de monitorización de red que utilizamos en la universidad “NAV - Network Administration Visualized” tuvo problemas con una de las consultas DELETE que mandaba a la base de datos PostgreSQL que utiliza.","title":"Gráfico de llaves foráneas","type":"blog"},{"content":"Como muchos ya sabeis, hace unos días unos hackers atacaron los servidores de MySQL.com. Mediante este ataque consiguieron, entre otras cosas, acceder a varias bases de datos de esta compañia y a los datos contenidos en las mismas.\nParece ser que una de las bases de datos MySQL comprometidas contenia documentos internos y clasificados de Oracle, la compañia dueña de MySQL. Esta base de datos era utilizada, por su rapidez, por una parte de un sistema interno de gestión de documentos de Oracle y muchos de los documentos obtenidos han sacado a la luz temas muy interesantes.\nEntre los documentos que se han obtenido existen varios que afectan directamente a la comunidad postgreSQL. Estos documentos tienen información detallada y concreta de como Oracle ha estado actuando durante el ultimo año para hacerse con el control de PostgreSQL y parar el avance de este proyecto.\nParece ser que la causa de esta acción es la cuota de mercado que PostgreSQL ha conseguido en los últimos tiempos. Oracle estima que esta cuota de mercado es el equivalente a un 15% anual de los ingresos antes de impuestos obtenidos en su último periodo fiscal.\nHay que admitir que el plan que han llevado a cabo ha sido minuciosamente planeado hasta el último detalle para asegurar su éxito. Oracle ha usado una parte de los más de 6.000 millones de dólares de beneficios obtenidos en el 2010 para pagar una \u0026lsquo;pensión anticipada\u0026rsquo; a la gran mayoría de desarrolladores principales de PostgreSQL, tanto los pertenecientes al grupo central (core team), como al grupo de desarrolladores principales y habituales, en total estamos hablando de más de 50 desarrolladores .\nEl importe de esta transacción económica no se conoce todavía pero se estima que asciende a una cantidad de entre 800 y 1.000 millones de dólares. Entre las cláusulas que los \u0026rsquo;ex-desarrolladores\u0026rsquo; han tenido que aceptar podemos citar el no poder trabajar por un periodo de 10 años en ningún tipo de proyecto o desarrollo asociado a motores de bases de datos relacionales. Otras de las cláusulas del acuerdo era que el plan no podia anunciarse antes de finales de abril, momento este en que se iba a presentar el plan estrategico de la empresa para los próximos 5 años.\nAl no comprar ninguna empresa, las autoridades y órganos antimonopolio no pueden hacer nada sobre el tema. Tanto la \u0026lsquo;Division antimonopolio del Departamento de justicia de los Estados Unidos\u0026rsquo;, como el \u0026lsquo;Grupo antimonopolio de la Comisión europea\u0026rsquo; han confirmado que este es un caso especial y que no existe legislación al respecto.\nLas reacciones no se han hecho esperar, las acciones de Oracle subieron varios puntos en Nasdaq durante la jornada de ayer. La noticia ha caido como una bomba y parece ser que las primeras confirmaciones y explicaciones por parte de los implicados están viendo la luz. Tom Lane, Bruce Momjian, Marc G. Fournier, Dave Page, Joshua Drake, Robert Haas, Magnus Hagander, Fujii Masao, Heikki Linnakangas, Simon Riggs y Greg Smith, son solo algunos de los que han aceptado este plan de pensión anticipada.\nJosh Berkus, uno de los miembros del core team, parece ser que no ha aceptado el acuerdo, ya que como anuncio hace unas semanas, su futuro estará muy ligado a CouchDB y bases de datos NoSQL y no tiene intención de retirarse del mundo de las bases de datos por el momento.\nNo hemos podido contactar con ninguno de los desarrolladores hispanoamericanos del proyecto, ni Alvaro Herrera, ni Jaime Casanova están disponibles. Se rumorea en las listas del proyecto que ya se encuentran en alguna isla del Caribe, junto con otros \u0026rsquo;ex-desarrolladores\u0026rsquo; organizando la que será la gran fiesta de despedida del proyecto..\nAquí teneis algunas de las declaraciones que hemos podido obtener en las últimas horas:\nLarry Ellison, \u0026ldquo;Este plan no deberia de haber salido a la luz ahora, pero ya que lo ha hecho solamente tenemos que decir que estamos muy orgullosos de haber podido cerrar este acuerdo en un tiempo record. Lo hemos hecho porque podiamos y porque eliminando la competencia que PostgreSQL representa podremos aumentar en un 10-15% nuestros beneficios anuales cuando asimilemos la cuota de mercado que usa PostgreSQL hoy en día \u0026hellip;.\u0026rdquo;\nBruce Momjian, \u0026ldquo;\u0026hellip; Después de más de 15 años trabajando para el proyecto PostgreSQL, esta es una oportunidad única para dejarlo con estilo. Ultimamente no hemos podido encontrar aspectos interesantes en el desarrollo de PostgreSQL. El nivel de calidad y características disponibles en PostgreSQL es tal, que poco mas podemos hacer para mejorarlo. Una de las cláusulas del acuerdo que hemos firmado es que la versión 9.1 sera la última de este producto, aunque seguirá siendo distribuida bajo una licencia que permita que los usuarios puedan seguir usandola como quieran \u0026hellip;.\u0026rdquo;\nTom Lane, \u0026ldquo;\u0026hellip; Como Bruce ya os ha comentado, no podemos hacer mucho más para mejorar la que será la última versión de PostgreSQL. Además, según varios estudios recientes, los motores de bases de datos relacionales tienen los dias contados. Gracias a este acuerdo y a la cuenta bancaria de Oracle, muchos de nosotros podremos empezar a trabajar en nuevas tecnologías.\nEstamos organizando a un grupo de desarrolladores para un proyecto que estamos pensando empezar. Este proyecto estará destinado a crear una nueva tecnología capaz de afrontar los retos a los que nos enfrentaremos en los próximos 40-50 años en lo que se refiere al proceso de grandes cantidades de información..\nNo tendrá nada que ver con las bases de datos actuales. Podemos adelantar que estará basado en \u0026lsquo;Redes neuronales artificiales (RNA)\u0026rsquo; y un motor de control de calidad basado en \u0026lsquo;Inteligencia artificial (IA)\u0026rsquo; que se hará cargo de garantizar la calidad del código que generemos. El proceso de desarrollo será mucho más dinámico cuando no tengamos que preocuparnos de revisar el código producido \u0026hellip;\u0026rdquo;.\nEsto es todo desde PostgreSQL-es, seguiremos informando sobre el tema a medida que recibamos mas información. En los próximos meses tendremos que decidir que hacer con el portal. Os mantendremos informados.\nNota: \u0026ldquo;Este artículo ha sido una broma por el \u0026ldquo;Día de los bufones de abril\u0026rdquo; (April fools\u0026rsquo; day). Todo lo expresado en el mismo sobre el proyecto PostgreSQL es pura ficción y no tiene nada que ver con la realidad.\u0026rdquo;\n","date":"2011/04/01","externalUrl":null,"permalink":"/es/blog/documentos-internos-de-oracle-y-el-futuro-de-postgresql/","section":"Blogs","summary":"Como muchos ya sabeis, hace unos días unos hackers atacaron los servidores de MySQL.com. Mediante este ataque consiguieron, entre otras cosas, acceder a varias bases de datos de esta compañia y a los datos contenidos en las mismas.","title":"Documentos internos de Oracle y el futuro de PostgreSQL","type":"blog"},{"content":"","date":"2011/04/01","externalUrl":null,"permalink":"/es/tags/mysql/","section":"Tags","summary":"","title":"Mysql","type":"tags"},{"content":"","date":"2011/04/01","externalUrl":null,"permalink":"/es/tags/oracle/","section":"Tags","summary":"","title":"Oracle","type":"tags"},{"content":"","date":"2011/03/16","externalUrl":null,"permalink":"/es/tags/gnuplot/","section":"Tags","summary":"","title":"Gnuplot","type":"tags"},{"content":"Este artículo es el segundo de una serie de artículos sobre monitorización que estamos publicando en PostgreSQL-es. En el vamos a ver como podemos generar gráficos de una manera fácil a partir de los datos generados usando monitorización Ad Hoc.\nEn el artículo anterior de la serie, \u0026ldquo;Monitorización\u0026rdquo;, hablamos de dos tipos de monitorización, Ad Hoc y Preventiva.\nEn el tipo de monitorización Ad Hoc utilizabamos diferentes programas para obtener información de nuestros sistemas. Ahora vamos a ver como podemos procesar estos datos de una manera fácil, rápida y efectiva para crear gráficos que podamos utilizar en informes o simplemente que nos ayuden a entender lo que ha ocurrido durante un periodo de monitorización Ad Hoc.\nEn este artículo solamente vamos a generar con gnuplot unos gráficos sobre el uso de algunos recursos del sistema, pero no olvidar que el mismo método explicado aquí se puede también utilizar para generar gráficos de los datos internos o procesados en PostgreSQL.\nPara nuestros ejemplos hemos generado cierta carga en nuestro sistema con el programa bonnie++ y hemos utilizado dos programas, iostat y vmstat para recoger información de nuestro sistema durante el periodo de pruebas. Todo lo presentado en este artículo está realizado en un sistema Linux y ejecutado desde la linea de comandos en una terminal.\nSi no teneis instalado gnuplot, bonnie++, iostat y/o vmstat podeis instalarlos (en un sistema debian/ubuntu) con:\n$ sudo apt-get install procps $ sudo apt-get install sysstat $ sudo apt-get install bonnie++ $ sudo apt-get install gnuplot Recolectando datos # Primero creamos un directorio en nuestro sistema para grabar todos los datos que vamos a generar en nuestras pruebas:\n$ cd $ mkdir monitorizacion_pruebas A continuación ejecutamos iostat y vmstat cada segundo durante aproximadamente 14 minutos (850 seg), y redireccionamos el resultado a dos ficheros con los que despues trabajaremos. La ejecución completa por defecto de bonnie++ en mi portatil tarda unos 12 o 13 minutos, asi que si recogemos datos durante 14 minutos tendremos datos de toda la ejecución. Si no sabemos el tiempo exacto que deberiamos recoger datos, podriamos omitir el valor 850 y parar manualmente los dos procesos iostat y vmstat cuando decidamos que tenemos suficientes datos recogidos.\n$ cd ~/monitorizacion_pruebas/ $ `iostat -k -p 1 850 \u0026gt; iostat.log \u0026amp;` \u0026amp;\u0026amp; `vmstat 1 850 \u0026gt; vmstat.log \u0026amp;` Una vez que hemos empezado a recoger datos con iostat y vmstat, arrancamos el programa bonnie++ para generar una buena carga en el sistema de almacenamiento. Este programa genera I/O con lecturas/escrituras sequenciales y peticiones al azar de datos.\n$ cd ~/monitorizacion_pruebas/ $ time bonnie++ -d /tmp/ \u0026gt; bonnie.output Cuando la ejecución de bonnie++, iostat y vmstat termine tendremos tres ficheros en nuestro directorio de pruebas ~/monitorizacion_pruebas/\n$ cd ~/monitorizacion_pruebas/ $ ls -l total 576 -rw-r--r-- 1 rafael rafael 1027 2011-03-16 20:11 bonnie.output -rw-r--r-- 1 rafael rafael 459063 2011-03-16 20:13 iostat.log -rw-r--r-- 1 rafael rafael 73812 2011-03-16 20:13 vmstat.log Con el programa bon_csv2html podriamos generar un fichero html con el resultado de la ejecución de bonnie++, para su posterior consulta en un navegador si estais interesados en estos datos.\n$ cd ~/monitorizacion_pruebas/ $ bon_csv2html bonnie.output \u0026gt; bonnie.html 2\u0026gt; /dev/null Un pequeño ejemplo del contenido del fichero iostat.log seria:\nLinux 2.6.35-25-generic (E6410) 02/28/2011 _x86_64\t(4 CPU) avg-cpu: %user %nice %system %iowait %steal %idle 2.98 0.06 1.38 1.82 0.00 93.76 Device: tps kB_read/s kB_wrtn/s kB_read kB_wrtn sda 24.18 379.80 87.36 998814 229744 sda1 11.52 260.27 31.28 684457 82260 sda2 11.75 116.29 46.46 305820 122192 sda3 0.02 0.12 0.00 328 0 sda4 0.75 3.05 9.62 8025 25292 Y un pequeño ejemplo del contenido del fichero iostat.log:\nprocs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---- r b swpd free buff cache si so bi bo in cs us sy id wa 0 0 0 5703648 216500 1156552 0 0 93 21 90 407 3 1 94 2 0 0 0 5703568 216500 1156816 0 0 0 0 225 658 0 1 99 0 0 0 0 5703648 216500 1156804 0 0 0 24 231 639 0 0 99 0 0 0 0 5703296 216508 1156800 0 0 0 28 211 585 0 0 98 1 1 0 0 5703472 216508 1156644 0 0 0 0 246 727 0 1 99 0 Procesando datos # Una vez que hemos llegado a este punto tenemos que empezar a procesar los ficheros iostat.log y vmstat.log para extraer los datos que nos interesan y de los cuales queremos crear gráficos. Esto se podria hacer de muchas maneras, incluso editando manualmente estos ficheros con vuestro editor favorito.\nComo estamos trabajando en sistemas Linux/Unix y en terminal, podemos hacer uso de un gran número de herramientas disponibles por defecto en nuestros sistemas. Estas herramientas nos van a facilitar enormemente el procesamiento de nuestros datos y nos evitarán un tedioso trabajo manual. También vamos a utilizar \u0026ldquo;tuberias\u0026rdquo; (|) para mandar el resultado de un comando a otro comando y a redireccionar(\u0026gt;) los resultados a ficheros.\nPara generar los gráficos que queremos con gnuplot tendremos que generar series de datos que se puedan representar en un sistema de coordenadas cartesianas en el plano o espaciales.\nPrincipalmente tendremos que realizar dos operaciones con nuestros datos, seleccionar las filas que nos interesen y dentro de estas filas elegir las columnas que necesitemos.\nPara elegir las filas que nos interesan utilizaremos el comando grep o egrep (grep -E). Y para elegir las columnas utilizaremos el comando awk.\nPrimero vamos a extraer los datos del número de Kb/s que hemos leido (kB_read/s)y escrito (kB_wrtn/s) en la partición /dev/sda1 (donde bonnie++ ha generado I/O en nuestro sistema). Estos datos están representados en la columna 3 y 4 del fichero iostat.log.\nPara extraer todas las filas que nos interesan, elegimos con egrep todas las filas que contienen sda1.\nY para elegir las columnas usamos awk y extraemos las columnas en las posiciones 3 y 4 empezando a contar desde la izquierda. A mi me gusta trabajar más en MBytes, como los datos de estas columnas están en Kbytes, voy a dividir los valores por 1024 para transformarlos a Mbytes.\nComo mencionamos en el primer artículo de la serie, la primera fila del resultado por defecto de iostat y vmstat da los valores medios desde la última vez que se arranco el sistema. Esta fila no la queremos, una manera de evitar esta fila es utilizar tail para escoger todas las filas a partir de la segunda.\nAhora solo queda poner todo esto junto con tuberias y redireccionar el resultado a un fichero llamado iostat_read_write.log\n$ cd ~/monitorizacion_pruebas/ $ egrep sda1 iostat.log | awk -F \u0026#39; \u0026#39; \u0026#39;{print $3/1024,$4/1024}\u0026#39; | tail -n+2 \u0026gt; iostat_read_write.log El fichero iostat_read_write.log tendrá en nuestro caso 849 filas con dos columnas en cada fila. Los valores en vuestro sistema no serán, por supuesto, los mismos que en este artículo.\n$ cd ~/monitorizacion_pruebas/ $ cat iostat_read_write.log 0.0351562 0 0 0.03125 0 0 0 0 0 0 ........... ........... 0 0 Segundo vamos a extraer los datos referentes al uso de CPU. Estos datos están representados en las últimas cuatro columnas (us sy id wa)del fichero vmstat.log\nPara extraer todas las filas que no son cabeceras, elegimos con egrep todas las filas que no contienen la palabra procs o swpd.\nY para elegir las ultimas cuatro columnas usamos awk y extraemos las columnas en las posiciones 13,14,15 y 16 empezando a contar desde la izquierda.\nAl igual que con iostat, utilizamos tail para desechar la primera fila de nuestro resultado.\nAhora solo queda poner todo esto junto con tuberias y redireccionar el resultado a un fichero llamado vmstat_cpu.log\n$ cd ~/monitorizacion_pruebas/ $ egrep -v \u0026#34;(procs|swpd)\u0026#34; vmstat.log | awk -F \u0026#39; \u0026#39; \u0026#39;{print $13,$14,$15,$16}\u0026#39; | tail -n+2 \u0026gt; vmstat_cpu.log El fichero vmstat_cpu.log tendra en nuestro caso 849 filas con cuatro columnas en cada fila.\n$ cd ~/monitorizacion_pruebas/ $ cat vmstat_cpu.log 3 2 95 0 4 2 92 2 4 2 95 0 2 3 95 0 4 3 93 1 ........ ........ 1 2 97 0 Generando gráficos # Una vez que hemos recolectado los datos y los hemos procesado para elegir lo que queremos representar gráficamente, podemos usar el programa gnuplot para generar estos gráficos.\nLas posibilidades de representación que nos brinda gnuplot son enormes y en un principio la cantidad de comandos y parámetros disponibles son abrumadores y nos puede parecer una tarea y un programa muy difícil de usar. En este artículo no tenemos tiempo de profundizar en el uso de gnuplot, pero veremos que no necesitamos tanto para empezar a utilizarlo y generar nuestros primeros gráficos.\ngnuplot se puede utilizar de manera interactiva o via scripts. Para empezar a usarlo de manera interactiva podeis escribir gnuplot en vuestra terminal y empezar a escribir comandos.\nAquí teneis un ejemplo, escribir el comando plot sin(x) una vez arrancado el programa, ¿Qué pasa cuando haceis esto?:\n$ gnuplot G N U P L O T Version 4.4 patchlevel 0 last modified March 2010 System: Linux 2.6.35-27-generic Copyright (C) 1986-1993, 1998, 2004, 2007-2010 Thomas Williams, Colin Kelley and many others gnuplot home: http://www.gnuplot.info faq, bugs, etc: type \u0026#34;help seeking-assistance\u0026#34; immediate help: type \u0026#34;help\u0026#34; plot window: hit \u0026#39;h\u0026#39; Terminal type set to \u0026#39;wxt\u0026#39; gnuplot\u0026gt; plot sin(x) gnuplot\u0026gt; quit El comando help os puede ayudar mucho cuando empeceis a profundizar en el uso de este programa.\nComo hemos dicho, nosotros vamos a usar el programa via scripts. Lo primero que vamos a hacer es crear un pequeño y simple script para generar un fichero PNG con la gráfica correspondiente a los MB/s de lectura en sda1.\nCreamos un fichero llamado iostat_mb_s_read.gnuplot con este contenido:\n# Definimos que los resultados sean # un fichero PNG en vez de en pantalla set term png font \u0026#34;freesans\u0026#34; set autoscale # Definimos el nombre de nuestro grafico set output \u0026#34;iostat_mb_s_read.png\u0026#34; # Definimos el titulo del grafico set title \u0026#34;Uso /dev/sda1\u0026#34; # Definimos que las leyendas del grafico # esten fuera del sistema de coordenadas set key outside # Definimos que se utilicen todas las muestras set xrange[0:849] # Definimos que se muestre un borde y # se cuadricule el sistema de coordenadas set border set grid # Definimos una leyenda para el eje de # las X y las Y del grafico de lecturas set ylabel \u0026#34;MBytes/s lectura\u0026#34; set xlabel \u0026#34;Muestras\u0026#34; # Generamos el grafico utilizando la # columna 1 del fichero iostat_read_write.log plot \u0026#34;iostat_read_write.log\u0026#34; using 1 title \u0026#34;Mb/s read\u0026#34; smooth bezier with lines linewidth 2 Y ejecutamos este comando para generar el gráfico PNG que hemos definido:\n$ cd ~/monitorizacion_pruebas/ $ gnuplot iostat_mb_s_read.gnuplot Este comando generara un gráfico PNG llamado iostat_mb_s_read.png con este contenido:\nPodemos seguir complicando la cosa. Ahora vamos a generar un solo gráfico con dos curvas, una para las lecturas y otra para las escrituras. Primero creamos otro script llamado iostat_mb_s_read_write.gnuplot con este contenido:\n# Definimos que los resultados sean # un fichero PNG en vez de en pantalla set term png font \u0026#34;freesans\u0026#34; set autoscale # Definimos el nombre de nuestro grafico set output \u0026#34;iostat_mb_s_read_write.png\u0026#34; # Definimos el titulo del grafico set title \u0026#34;Uso /dev/sda1\u0026#34; # Definimos que las leyendas del grafico # esten fuera del sistema de coordenadas set key outside # Definimos que se utilicen todas las muestras set xrange[0:849] # Definimos que se muestre un borde y # se cuadricule el sistema de coordenadas set border set grid # Definimos una leyenda para el eje de # las X y las Y del grafico set ylabel \u0026#34;MBytes/s\u0026#34; set xlabel \u0026#34;Muestras\u0026#34; # Generamos el grafico utilizando la # columna 1 del fichero iostat_read_write.log # para lecturas y la columna 2 para escrituras plot \u0026#34;iostat_read_write.log\u0026#34; using 1 title \u0026#34;Mb/s read\u0026#34; smooth bezier with lines linewidth 2,\u0026#34;iostat_read_write.log\u0026#34; using 2 title \u0026#34;Mb/s write\u0026#34; smooth bezier with lines linewidth 2 Y ejecutamos este comando para generar el gráfico PNG que hemos definido:\n$ cd ~/monitorizacion_pruebas/ $ gnuplot iostat_mb_s_read_write.gnuplot Esto comando generara un grafico PNG llamado iostat_mb_s_read_write.png con este contenido:\nComo podeis ver tenemos las dos curvas representadas en el mismo gráfico.\nPara terminar vamos a dar un último ejemplo en donde generamos un gráfico con varios subgráficos dentro del mismo. Vamos a representar los valores de CPU que tenemos en el fichero vmstat_cpu.log.\nPara esto creamos otro script llamado vmstat_cpu_multi.gnuplot con este contenido:\n# Definimos que los resultados sean # un fichero PNG en vez de en pantalla set term png font \u0026#34;freesans\u0026#34; set autoscale # Definimos el nombre de nuestro grafico set output \u0026#34;vmstat_cpu_multi.png\u0026#34; # Definimos que las leyendas del grafico # no se muestren set key off # Definimos cada cuantas muestras se # muestran los valores en el eje X set xtics 150 # Definimos que el rango de los valores # en el eje de las Y sea de 0 a 100 set yrange[0:100] # Definimos que se muestre un borde y # se cuadricule el sistema de coordenadas set border set grid # Definimos una leyenda para el eje de # las X y las Y del grafico de lecturas set ylabel \u0026#34;Porcentaje de uso (%)\u0026#34; set xlabel \u0026#34;Muestras\u0026#34; # Definimos que vamos a crear un grafico con # multiples subgraficos en el mismo. set multiplot # Definimos que el tamaño relativo del primer subgrafico # sea el 50% del espacio disponible en el ancho y alto # del grafico principal set size 0.5, 0.5 # Definimos que el punto (0,0) de este subgrafico # este en punto relativo (0,0) del grafico principal set origin 0, 0 # Definimos el titulo de este subgrafico set title \u0026#34;CPU-user\u0026#34; # Generamos el subgrafico utilizando la # columna 1 del fichero vmstat_cpu.log plot \u0026#34;vmstat_cpu.log\u0026#34; using 1 title \u0026#34;cpu-user\u0026#34; smooth bezier with filledcurves x1 linecolor rgbcolor \u0026#34;#ffa500\u0026#34; # Definimos que el tamaño relativo del segundo subgrafico # sea el 50% del espacio disponible en el ancho y alto # del grafico principal set size 0.5, 0.5 # Definimos este subgrafico a la derecha del primero set origin 0.5, 0 # Definimos el titulo de este subgrafico set title \u0026#34;CPU-system\u0026#34; # Generamos el subgrafico utilizando la # columna 2 del fichero vmstat_cpu.log plot \u0026#34;vmstat_cpu.log\u0026#34; using 2 title \u0026#34;cpu-system\u0026#34; smooth bezier with filledcurves x1 linecolor rgbcolor \u0026#34;#228b22\u0026#34; # Definimos que el tamaño relativo del tercer subgrafico # sea el 50% del espacio disponible en el ancho y alto # del grafico principal set size 0.5, 0.5 # Definimos este subgrafico arriba del primero set origin 0, 0.5 # Definimos el titulo de este subgrafico set title \u0026#34;CPU-idle\u0026#34; # Generamos el subgrafico utilizando la # columna 3 del fichero vmstat_cpu.log plot \u0026#34;vmstat_cpu.log\u0026#34; using 3 title \u0026#34;cpu-idle\u0026#34; smooth bezier with filledcurves x1 linecolor rgbcolor \u0026#34;#483d8b\u0026#34; # Definimos que el tamaño relativo del cuarto subgrafico # sea el 50% del espacio disponible en el ancho y alto # del grafico principal set size 0.5, 0.5 # Definimos este subgrafico arriba y a la derecha # del primero set origin 0.5, 0.5 # Definimos el titulo de este subgrafico set title \u0026#34;CPU-waiting\u0026#34; # Generamos el subgrafico utilizando la # columna 4 del fichero vmstat_cpu.log plot \u0026#34;vmstat_cpu.log\u0026#34; using 4 title \u0026#34;cpu-waiting\u0026#34; smooth bezier with filledcurves x1 linecolor rgbcolor \u0026#34;#ffd700\u0026#34; unset multiplot Y ejecutamos este comando para generar el gráfico PNG que hemos definido:\n$ cd ~/monitorizacion_pruebas/ $ gnuplot vmstat_cpu_multi.gnuplot Esto comando generará un gráfico PNG llamado vmstat_cpu_multi.png con este contenido:\nComo podeis ver las posibilidades son infinitas y todo depende de lo que necesitais representar en el gráfico y el tipo de gráfico que quereis generar. Lo normal es crearse unos scripts en BASH u otro lenguaje de alto nivel que podais utilizar para generar los diferentes tipos de gráficos, sin necesidad de tener que estar modificando/creando todo el tiempo scripts con los comandos de gnuplot.\nY hasta aquí el contenido de este artículo. Si en vez de generar ficheros PNG con los gráficos quereis mostrar la información en pantalla, podeis borrar los commandos set term png \u0026hellip;. y set output \u0026hellip;. de todos vuestros ficheros *.gnuplot\nYa solo os queda leer más documentación sobre gnuplot y practicar.\nEnlaces:\nhttp://gnuplot.sourceforge.net/ http://es.wikipedia.org/wiki/Coordenadas_cartesianas http://www.tayloredmktg.com/rgb/ ","date":"2011/03/16","externalUrl":null,"permalink":"/es/blog/monitorizacion-ii-generando-graficos-de-datos-ad-hoc/","section":"Blogs","summary":"Este artículo es el segundo de una serie de artículos sobre monitorización que estamos publicando en PostgreSQL-es. En el vamos a ver como podemos generar gráficos de una manera fácil a partir de los datos generados usando monitorización Ad Hoc.","title":"Monitorización II - Generando gráficos de datos Ad Hoc","type":"blog"},{"content":"Una de las tareas más importantes de un administrador de bases de datos es monitorizar los sistemas a su cargo para saber como están funcionando y planear futuras modificaciones y actualizaciones de los mismos.\nEste artículo es una introducción a la monitorización de sistemas de bases de datos en sistemas Linux/Unix y está basado en un entrenamiento especializado que se impartio en el PGDay Latinoamericano 2011 en Cuba. Más adelante escribiremos otros artículos más específicos que nos ayuden a usar e interpretar los datos obtenidos de monitorizar nuestros sistemas.\nEn nuestro caso, monitorizar significa vigilar el funcionamiento de un sistema, servicio o actividad. Bajo nuestro punto de vista existen dos tipos de monitorización:\nAd Hoc: Monitorización específica en caso de problemas o pruebas. Se utiliza generalmente para investigar una situación puntual en la que intentamos encontrar una explicación a un suceso, cambio o problema. Preventiva: Detecta interrupciones de servicios, alerta sobre posibles problemas y crea gráficos con tendencias y datos históricos sobre nuestros sistemas. Este tipo de monitorización está automatizada y nos ayuda a descubrir cambios en nuestros sistemas que provocan o pueden provocar problemas en un futuro cercano. Existen numerosos programas que nos pueden ayudar a realizar la monitorización de nuestros sistemas PostgreSQL. A continuación teneis los mas usados en el mundo de programas de código abierto.\nAd Hoc: vmstat, iostat, sar, ps, top, iotop, htop, etc en la linea de comandos de vuestro sistema operativo, e información interna de vuestra instalacion postgreSQL via las vistas y tablas de sistema en la base de datos. Preventiva: \u0026ldquo;Nagios\u0026rdquo; para detectar y alertar sobre posibles problemas y \u0026ldquo;Munin\u0026rdquo; para crear gráficas históricas con las tendencias y uso de nuestros sistemas. Los elementos a monitorizar son todos aquellos que nos puedan dar información sobre como nuestro sistema está funcionando y comportandose. Tendremos que monitorizar los componentes de la infraestructura necesaria para que nuestro sistema funcione, los servidores que estamos utilizando, el uso de los recursos que se están utilizando y la información interna en la base de datos que nos de datos sobre como se está usando PostgreSQL.\nAlgunos de los elementos más importantes que se suelen monitorizar son los siguientes:\nServidor: Disponibilidad y posibles problemas del hardware CPU: Carga del sistema y uso de la CPU Memoria: Carga y uso de la memoria, uso de la memoria de intercambio (swap) Red: Disponibilidad de los componentes de red, tráfico de entrada y salida. Discos / almacenamiento: Espacio utilizado, I/O (Input/Output) del sistema PostgreSQL: Número de conexiones, número de transacciones, transacciones abiertas, bloqueos, espacio usado, tipo de comandos usados, etc, etc Ademas de estos elementos, suelen existir características específicas a cada sistema que cada administrador deberá identificar y monitorizar de la mejor manera posible.\nMonitorización Ad Hoc # Como ya hemos comentado, este tipo de monitorización se utiliza generalmente para investigar una situacion puntual, en la que entramos en el sistema, recogemos los datos deseados por un periodo de tiempo y pasamos a su posterior análisis para intentar encontrar una explicación a la situación o problema que estamos intentado resolver.\nA continuación teneis una breve introducción a las herramientas del sistema operativo usadas más a menudo en esta monitorización. Todas estas herramientas se pueden usar con multitud de opciones y parámetros, para obtener información detallada sobre los mismos usar el comando \u0026ldquo;man programa\u0026rdquo;\nvmstat: Este comando se suele usar para obtener, entre otras cosas, información sobre el uso de memoria, swap, I/O total y cpu del sistema. La primera fila del resultado por defecto da los valores medios desde la última vez que se arranco el sistema. Es un programa que te da información global e instantanea del sistema en un momento determinado.\nEn sistemas debian/ubuntu este programa esta incluido en el paquete procps y si no esta instalado se puede instalar con el siguiente comando:\n$ sudo apt-get install procps Un resultado típico de ejecutar este comando podria ser el siguiente:\n$ vmstat -n 1 10 procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---- r b swpd free buff cache si so bi bo in cs us sy id wa 0 0 1368 401736 529112 4844136 0 0 15 26 24 6 5 1 94 1 0 0 1368 397884 529112 4844140 0 0 0 124 523 1399 1 0 99 0 0 0 1368 401976 529112 4844140 0 0 0 0 566 1648 1 0 99 0 0 0 1368 398752 529112 4844140 0 0 0 0 488 1447 1 0 99 0 0 0 1368 403588 529112 4844140 0 0 0 0 520 1550 2 0 98 0 0 0 1368 398720 529112 4844140 0 0 0 0 565 1525 1 0 99 0 0 0 1368 400232 529116 4844144 0 0 0 124 551 1609 1 0 99 0 0 0 1368 396132 529116 4844144 0 0 0 0 519 1395 1 0 99 0 0 0 1368 400720 529116 4844144 0 0 0 440 673 1892 1 0 98 1 1 0 1368 397076 529116 4844144 0 0 0 0 500 1443 1 1 99 0 El tamaño de bloque por defecto es 1024 bytes (1Kb) y el significado de las distintas columnas es el siguiente:\nr: Procesos esperando por tiempo de ejecución (run time)\nb: Procesos en estado de espera sin interrupciones(uninterruptible sleep)\nswpd: Cantidad de memoria virtual en uso\nfree: Cantidad de memoria ociosa\nbuff: Cantidad de memoria usada por buffers\ncache: Cantidad de memoria usada caches\nsi: Cantidad de memoria grabada en disco (swap-in)\nso: Cantidad de memoria leida del disco (swap-out)\nbi: Bloques recibidos por los dispositivos de bloques (bloques/s)\nbo: Bloques enviados por los dispositivos de bloques (bloques/s)\nin: Número de interrupciones por segundo\ncs: Número de cambios de contexto por segundo (context switches)\nus: Tiempo de cpu de procesos de usuario (nice incluido)\nsy: Tiempo de cpu de procesos del kernel\nid: Tiempo de cpu ocioso\nwa: Tiempo de cpu esperando por I/O\nst: Tiempo de cpu \u0026ldquo;robado\u0026rdquo; a una maquina virtual\niostat: Este comando se suele utilizar para obtener información sobre la entrada/salida de datos de todos los dispositivos, particiones y sistemas de ficheros NFS. Por defecto, el primer resultado da los valores medios desde la última vez que se arranco el sistema. Es un programa que te da información sobre el I/O del sistema para todos los dispositivos en un momento determinado.\nEn sistemas debian/ubuntu este programa está incluido en el paquete sysstat y si no esta instalado se puede instalar con el siguiente comando:\n$ sudo apt-get install sysstat Un resultado típico de ejecutar este comando podria ser el siguiente:\n$ iostat -k -p 1 2 avg-cpu: %user %nice %system %iowait %steal %idle 4.78 0.19 0.60 0.79 0.00 93.64 Device: tps kB_read/s kB_wrtn/s kB_read kB_wrtn sda 5.50 59.00 106.86 19921681 36081584 sda1 1.62 6.75 17.93 2279650 6053108 sda2 0.08 3.05 0.02 1031003 7032 sda3 3.80 49.19 88.91 16610176 30020076 sda4 0.00 0.00 0.00 740 1368 sdb 0.00 0.01 0.00 3460 44 sdb1 0.00 0.01 0.00 3340 44 avg-cpu: %user %nice %system %iowait %steal %idle 0.99 0.10 0.54 0.00 0.00 98.37 Device: tps kB_read/s kB_wrtn/s kB_read kB_wrtn sda 0.20 25.60 0.00 128 0 sda1 0.00 0.00 0.00 0 0 sda2 0.20 25.60 0.00 128 0 sda3 0.00 0.00 0.00 0 0 sda4 0.00 0.00 0.00 0 0 sdb 0.00 0.00 0.00 0 0 sdb1 0.00 0.00 0.00 0 0 El tamaño de bloque por defecto usado por este programa es 512 bytes. El parámetro -k nos cambia el valor de bloque a 1024 bytes (1Kb). El significado de las distintas columnas es el siguiente:\ntps: Número de peticiones I/O (transferencias) a un dispositivo kB_read/s: kB por segundo leidos del dispositivo kB_wrtn/s: kB por segundo escritos en el dispositivo kB_read: Número total de kB leidos del dispositivo kB_wrtn: Número total de kB escritos en el dispositivo sar: Este programa se suele utilizar para recolectar y grabar información sobre nuestro sistema durante un periodo de tiempo.\nEn sistemas debian/ubuntu este programa está incluido en el paquete sysstat y si no esta instalado se puede instalar con el siguiente comando:\n$ sudo apt-get install sysstat La recolección de datos se llama desde cron (/etc/cron.d/sysstat), y por defecto no está activado. Para activar la recolección de datos una vez instalado deberemos cambiar un parámetro en el fichero /etc/default/sysstat y arrancar el servicio:\nENABLE=\u0026#34;TRUE\u0026#34; en /etc/default/sysstat $ sudo /etc/init.d/sysstat restart Un resultado típico de ejecutar este comando despues de un tiempo desde que se activa la recolección de datos podria ser el siguiente:\n$ sar Linux 2.6.35-24-generic (core2) 01/29/2011 _x86_64_\t(4 CPU) 12:00:01 AM CPU %user %nice %system %iowait %steal %idle 12:05:01 AM all 0.72 0.14 0.95 0.19 0.00 97.99 12:15:01 AM all 0.74 0.15 0.95 0.09 0.00 98.07 12:25:01 AM all 0.73 0.15 0.90 0.05 0.00 98.17 12:35:01 AM all 0.74 0.14 0.93 0.15 0.00 98.05 12:45:01 AM all 1.08 4.80 3.27 0.07 0.00 90.78 12:55:01 AM all 0.96 3.67 2.59 0.10 0.00 92.68 01:05:01 AM all 0.72 0.14 0.90 0.19 0.00 98.04 01:15:01 AM all 0.76 0.13 0.97 0.07 0.00 98.06 01:25:01 AM all 0.70 0.15 0.90 0.06 0.00 98.20 01:35:01 AM all 0.70 0.13 0.89 0.11 0.00 98.17 01:45:01 AM all 0.68 0.14 0.90 0.10 0.00 98.18 ps: Este programa nos da información sobre los procesos que se están ejecutando en nuestro sistema. En el caso de procesos PostgreSQL nos puede dar información muy valiosa sobre cuantos procesos PostgreSQL se están ejecutando y que están haciendo.\nUn resultado típico de ejecutar este comando para obtener los procesos PostgreSQL del sistema, ordenados por el momento en que empezaron a ejecutarse, podria ser el siguiente:\n$ ps auxww | grep \u0026#34;postgres: \u0026#34; | sort -k 9 postgres 30758 .... 07:10 0:00 postgres: autovacuum launcher process postgres 30759 .... 07:10 0:00 postgres: stats collector process postgres 30757 .... 07:10 0:00 postgres: wal writer process postgres 30756 .... 07:10 0:01 postgres: writer process postgres 862 .... 07:59 0:00 postgres: postgres postgres [local] idle in transaction postgres 1921 .... 09:22 0:00 postgres: postgres postgres 127.0.0.1(39475) idle postgres 21074 .... 14:14 0:04 postgres: postgres pgbench [local] COMMIT postgres 21073 .... 14:14 0:04 postgres: postgres pgbench [local] UPDATE waiting Los que nos suelen interesar en este caso son los procesos creados por conexiones de clientes a nuestro servidor PostgreSQL. Estos procesos se muestran con el siguiente formato:\npostgres: usuario dbase maquina(puerto) actividad Como podeis ver, podemos obtener información sobre que usuario está utilizando el cliente para conectarse, a que base de datos se está conectando, desde que máquina y puerto (o socket [local]) se ha conectado y que está haciendo. Los valores de la actividad que está realizando podrán ser:\nidle in transaction: El proceso ha abierto una transacción pero no la ha cerrado todavia. idle: El proceso está conectado pero sin hacer nada. COMANDO: El proceso está ejecutando el comando COMANDO COMANDO waiting: El proceso quiere ejecutar el comando COMANDO, pero está bloqueado y esperando a poder ejecutarse\nVistas y tablas de sistema internas en PostgreSQL # Además podemos obtener información sobre como esta funcionando PostgreSQL internamente a través de las vistas y tablas de sistema en la base de datos. En sucesivos artículos iremos viendo que tipo de información contienen y como interpretar estas vista/tablas de sistema.\nPara que muchas de estas vistas/tablas funcionen debemos tener activados estos parámetros en nuestro sistema, track_counts, track_functions, track_activities, bien en el fichero postgresql.conf o definidos con SET / ALTER DATABASE en la sesión o base de datos de la que queremos obtener información.\nExisten muchas vistas y tablas internas pero las que se usan mas comunmente para obtener información de PostgreSQL son las siguientes:\npg_roles: Información sobre todos los roles y usuarios definidos en la base de datos.\npostgres=# SELECT * from pg_roles ; rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcatupdate | rolcanlogin | rolconnlimit | rolpassword | rolvaliduntil | rolconfig | oid ----------+----------+------------+---------------+-------------+--------------+-------------+--------------+-------------+---------------+-----------+----- postgres | t | t | t | t | t | t | -1 | ******** | | | 10 (1 row) pg_database Información sobre todas las bases de datos definidas en nuestro sistema.\npostgres=# SELECT * from pg_database ; datname | datdba | encoding | datcollate | datctype | datistemplate | datallowconn | datconnlimit | datlastsysoid | datfrozenxid | dattablespace | datacl -----------+--------+----------+-------------+-------------+---------------+--------------+--------------+---------------+--------------+---------------+------------------------------------- template1 | 10 | 6 | en_US.UTF-8 | en_US.UTF-8 | t | t | -1 | 11866 | 654 | 1663 | {=c/postgres,postgres=CTc/postgres} template0 | 10 | 6 | en_US.UTF-8 | en_US.UTF-8 | t | f | -1 | 11866 | 654 | 1663 | {=c/postgres,postgres=CTc/postgres} postgres | 10 | 6 | en_US.UTF-8 | en_US.UTF-8 | f | t | -1 | 11866 | 654 | 1663 | pgbench | 10 | 6 | en_US.UTF-8 | en_US.UTF-8 | f | t | -1 | 11866 | 654 | 1663 | (4 rows) pg_locks: Información sobre los bloqueos activos en nuestras bases de datos. Vista complicada de entender pero muy valiosa en ciertas situaciones.\npostgres=# SELECT * from pg_locks ; locktype | database | relation | page | tuple | virtualxid | transactionid | classid | objid | objsubid | virtualtransaction | pid | mode | granted ---------------+----------+----------+------+-------+------------+---------------+---------+-------+----------+--------------------+------+------------------+--------- relation | 16385 | 16392 | | | | | | | | 3/2666 | 2711 | AccessShareLock | t relation | 16385 | 16392 | | | | | | | | 3/2666 | 2711 | RowExclusiveLock | t virtualxid | | | | | 4/2692 | | | | | 4/2692 | 2710 | ExclusiveLock | t relation | 11874 | 10985 | | | | | | | | 2/19 | 2401 | AccessShareLock | t relation | 16385 | 16399 | | | | | | | | 3/2666 | 2711 | RowExclusiveLock | t relation | 16385 | 16392 | | | | | | | | 4/2692 | 2710 | AccessShareLock | t relation | 16385 | 16392 | | | | | | | | 4/2692 | 2710 | RowExclusiveLock | t relation | 16385 | 16403 | | | | | | | | 4/2692 | 2710 | AccessShareLock | t relation | 16385 | 16403 | | | | | | | | 4/2692 | 2710 | RowExclusiveLock | t relation | 16385 | 16403 | | | | | | | | 3/2666 | 2711 | AccessShareLock | t relation | 16385 | 16403 | | | | | | | | 3/2666 | 2711 | RowExclusiveLock | t virtualxid | | | | | 3/2666 | | | | | 3/2666 | 2711 | ExclusiveLock | t relation | 16385 | 16401 | | | | | | | | 3/2666 | 2711 | RowExclusiveLock | t relation | 16385 | 16386 | | | | | | | | 3/2666 | 2711 | RowExclusiveLock | t relation | 16385 | 16401 | | | | | | | | 4/2692 | 2710 | RowExclusiveLock | t tuple | 16385 | 16389 | 22 | 97 | | | | | | 4/2692 | 2710 | ExclusiveLock | t relation | 16385 | 16389 | | | | | | | | 4/2692 | 2710 | RowExclusiveLock | t relation | 16385 | 16395 | | | | | | | | 3/2666 | 2711 | RowExclusiveLock | t relation | 16385 | 16389 | | | | | | | | 3/2666 | 2711 | RowExclusiveLock | t transactionid | | | | | | 2793613 | | | | 4/2692 | 2710 | ExclusiveLock | t virtualxid | | | | | 2/19 | | | | | 2/19 | 2401 | ExclusiveLock | t transactionid | | | | | | 2793614 | | | | 3/2666 | 2711 | ExclusiveLock | t transactionid | | | | | | 2793609 | | | | 4/2692 | 2710 | ShareLock | t (23 rows) pg_stat_activity: Información sobre todos los procesos clientes conectados a la base de datos.\npostgres=# SELECT * from pg_stat_activity ; datid | datname | procpid | usesysid | usename | application_name | client_addr | client_port | backend_start | xact_start | query_start | wa iting | current_query -------+----------+---------+----------+----------+------------------+-------------+-------------+-------------------------------+-------------------------------+-------------------------------+---------+----------------------------------------------------------------------- 11874 | postgres | 2401 | 10 | postgres | psql | | -1 | 2011-02-17 22:48:08.090603+01 | 2011-02-17 22:52:31.297037+01 | 2011-02-17 22:52:31.297037+01 | f | SELECT * from pg_stat_activity ; 16385 | pgbench | 2711 | 10 | postgres | | | -1 | 2011-02-17 22:51:12.209724+01 | 2011-02-17 22:52:31.296394+01 | 2011-02-17 22:52:31.296873+01 | f | END; 16385 | pgbench | 2710 | 10 | postgres | | | -1 | 2011-02-17 22:51:12.1963+01 | 2011-02-17 22:52:31.295193+01 | 2011-02-17 22:52:31.29542+01 | t | UPDATE pgbench_tellers SET tbalance = tbalance + -4443 WHERE tid = 1; 16385 | pgbench | 2811 | 10 | postgres | | | | 2011-02-17 22:52:27.549785+01 | 2011-02-17 22:52:31.247453+01 | 2011-02-17 22:52:31.247453+01 | f | autovacuum: ANALYZE public.pgbench_history (4 rows) pg_stat_database: Información global de uso de todas las bases de datos.\npostgres=# SELECT * from pg_stat_database ; datid | datname | numbackends | xact_commit | xact_rollback | blks_read | blks_hit | tup_returned | tup_fetched | tup_inserted | tup_updated | tup_deleted -------+-----------+-------------+-------------+---------------+-----------+----------+--------------+-------------+--------------+-------------+------------- 1 | template1 | 0 | 3612 | 0 | 2840 | 100341 | 935386 | 33760 | 0 | 0 | 0 11866 | template0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 11874 | postgres | 1 | 4859 | 0 | 1304 | 132667 | 1425972 | 38209 | 0 | 0 | 0 16385 | pgbench | 2 | 2978446 | 1 | 659578 | 82868995 | 41262336 | 11863286 | 3178023 | 8934189 | 0 (4 rows) pg_stat_user_tables: Información de uso de todas las tablas de usuario en una base de datos\npgbench=# SELECT * from pg_stat_user_tables ; relid | schemaname | relname | seq_scan | seq_tup_read | idx_scan | idx_tup_fetch | n_tup_ins | n_tup_upd | n_tup_del | n_tup_hot_upd | n_live_tup | n_dead_tup | last_vacuum | last_autovacuum | last_analyze | last_autoanalyze -------+------------+------------------+----------+--------------+----------+---------------+-----------+-----------+-----------+---------------+------------+------------+-------------------------------+-------------------------------+-------------------------------+------------------------------- 16395 | public | pgbench_history | 0 | 0 | | | 3074221 | 0 | 0 | 0 | 283308 | 0 | 2011-01-29 12:31:54.208813+01 | | 2011-01-29 12:31:54.209202+01 | 2011-02-17 22:53:34.126245+01 16386 | public | pgbench_branches | 38821 | 62452 | 3042999 | 3042999 | 2 | 3074222 | 0 | 3072054 | 75 | 493 | 2011-02-17 22:51:12.172321+01 | 2011-02-17 22:53:27.711111+01 | 2011-01-29 12:31:53.945088+01 | 2011-02-17 22:53:27.748393+01 16389 | public | pgbench_tellers | 32474 | 621300 | 3043158 | 3043158 | 20 | 3074222 | 0 | 3052868 | 55 | 810 | 2011-02-17 22:51:12.175635+01 | 2011-02-17 22:53:27.870574+01 | 2011-01-29 12:31:53.946838+01 | 2011-02-17 22:53:27.871727+01 16392 | public | pgbench_accounts | 1 | 200000 | 6148444 | 6148444 | 200000 | 3074222 | 0 | 3033380 | 199999 | 10091 | 2011-01-29 12:31:54.049578+01 | 2011-01-29 12:59:29.982391+01 | 2011-01-29 12:31:54.204138+01 | 2011-02-17 22:53:31.476969+01 (4 rows) pg_stat_user_indexes: Información de uso de todos los índices de usuarios en una base de datos\npgbench=# SELECT * from pg_stat_user_indexes ; relid | indexrelid | schemaname | relname | indexrelname | idx_scan | idx_tup_read | idx_tup_fetch -------+------------+------------+------------------+-----------------------+----------+--------------+--------------- 16386 | 16399 | public | pgbench_branches | pgbench_branches_pkey | 3088826 | 10145852 | 2902404 16389 | 16401 | public | pgbench_tellers | pgbench_tellers_pkey | 3088985 | 24680003 | 2378762 16392 | 16403 | public | pgbench_accounts | pgbench_accounts_pkey | 6240098 | 6319966 | 6240098 pg_stat_user_functions: Información sobre estadísticas de uso de las funciones en uso.\npg_statio_user_tables: Información de acceso a disco y memoria cache de todas las tablas de usuario en una base de datos.\npgbench=# SELECT * from pg_statio_user_tables ; relid | schemaname | relname | heap_blks_read | heap_blks_hit | idx_blks_read | idx_blks_hit | toast_blks_read | toast_blks_hit | tidx_blks_read | tidx_blks_hit -------+------------+------------------+----------------+---------------+---------------+--------------+-----------------+----------------+----------------+--------------- 16392 | public | pgbench_accounts | 547836 | 9289403 | 1361 | 19151376 | | | | 16395 | public | pgbench_history | 135074 | 3208010 | | | | | | 16386 | public | pgbench_branches | 7587 | 17015504 | 6 | 3138237 | | | | 16389 | public | pgbench_tellers | 7160 | 28805005 | 25 | 6021187 | | | | (4 rows) pg_statio_user_indexes: Información de acceso a disco y memoria cache de todos los índices de usuario en una base de datos.\npgbench=# SELECT * from pg_statio_user_indexes ; relid | indexrelid | schemaname | relname | indexrelname | idx_blks_read | idx_blks_hit -------+------------+------------+------------------+-----------------------+---------------+-------------- 16386 | 16399 | public | pgbench_branches | pgbench_branches_pkey | 6 | 3163169 16389 | 16401 | public | pgbench_tellers | pgbench_tellers_pkey | 25 | 6076626 16392 | 16403 | public | pgbench_accounts | pgbench_accounts_pkey | 1361 | 19301010 (3 rows) pg_stat_bgwriter: Información global sobre el proceso \u0026ldquo;background writer\u0026rdquo;\npgbench=# SELECT * from pg_stat_bgwriter ; checkpoints_timed | checkpoints_req | buffers_checkpoint | buffers_clean | maxwritten_clean | buffers_backend | buffers_alloc -------------------+-----------------+--------------------+---------------+------------------+-----------------+--------------- 474 | 81 | 223942 | 554746 | 573 | 85369 | 555848 (1 row) Monitorización preventiva # Como hemos comentado al principio, este tipo de monitorización se utiliza para detectar interrupciones de servicios, alertar sobre posibles problemas y crea gráficos con tendencias y datos históricos sobre nuestros sistemas. Los dos programas de código abierto mas utilizados para este tipo de monitorización son:\nNagios: Se utiliza para detectar interrupciones de servicios y alertar sobre posibles problemas o situaciones que necesitan una atención especial por parte de los administradores del sistema. Su instalación y configuración se escapa al objetivo de este documento. Más adelante escribiremos un artículo sobre como utilizar Nagios.\nAquí teneis un ejemplo de una de la pantallas de este programa:\nMunin: Se utiliza para crear gráficas históricas con las tendencias y uso de nuestros sistemas. Imprescindible para ver como nuestros sistemas se están utilizando en el tiempo y si existen tendencias en el uso que van a requerir nuestra intervención en el futuro. Su instalación y configuración se escapa al objetivo de este documento. Más adelante escribiremos un artículo sobre como utilizar Munin.\nAquí teneis algunos ejemplos de lo que se puede hacer con este programa:\nEnlaces:\nhttp://www.nagios.org/ http://munin-monitoring.org/ http://www.postgresql.org/docs/current/interactive/catalogs.html http://www.postgresql.org/docs/current/interactive/monitoring.html ","date":"2011/02/12","externalUrl":null,"permalink":"/es/blog/monitorizacion/","section":"Blogs","summary":"Una de las tareas más importantes de un administrador de bases de datos es monitorizar los sistemas a su cargo para saber como están funcionando y planear futuras modificaciones y actualizaciones de los mismos.","title":"Monitorización","type":"blog"},{"content":" PgDay Latinoamericano 2011 - Havana, Cuba 2011-02-02 postgresql_monitor.pdf Not available\nSummary # Presentation / workshop about monitoring and PostgreSQL given in the PgDay Latinoamericano 2011\n","date":"2011/02/02","externalUrl":null,"permalink":"/presentations/monitorizacion/","section":"Presentations","summary":"Presentation / workshop about monitoring and PostgreSQL given in the PgDay Latinoamericano 2011","title":"Monitorización","type":"presentations"},{"content":" PgDay Latinoamericano 2011 - Havana, Cuba 2011-02-01 postgresql_seguridad.pdf Not available\nSummary # A general presentation about different aspects and components we have to take care of when securing the data in a PostgreSQL database.\n","date":"2011/02/01","externalUrl":null,"permalink":"/presentations/asegurando-nuestros-datos/","section":"Presentations","summary":"A general presentation about different aspects and components we have to take care of when securing the data in a PostgreSQL database.","title":"Asegurando nuestros datos","type":"presentations"},{"content":"","date":"2011/02/01","externalUrl":null,"permalink":"/tags/recovery/","section":"Tags","summary":"","title":"Recovery","type":"tags"},{"content":"","date":"2011/01/10","externalUrl":null,"permalink":"/tags/sinclair/","section":"Tags","summary":"","title":"Sinclair","type":"tags"},{"content":"","date":"2011/01/10","externalUrl":null,"permalink":"/tags/spectrumzx/","section":"Tags","summary":"","title":"SpectrumZX","type":"tags"},{"content":" History # Back in the early 80s, a good friend of mine owned a ZX Spectrum with 16K, and we spent countless hours loading and playing 8-bit games on it. This machine holds a special place in my memory, representing some of the most enjoyable moments of my early computing days.\nMany years later, another friend found this ZX Spectrum at a secondhand market and generously gave it to me as a present.\nOriginal Status # Unfortunately, this particular unit doesn’t work or start. A closer inspection reveals that it has several component issues that need addressing and some rust. I am not sure it it will be possible to restore this computer.\nWhile time has not yet permitted a restoration, I aspire to eventually bring this ZX Spectrum back to life. This project is both a technical challenge and a nostalgic journey, a chance to revive a piece of my past that meant so much to me.\nCurrent status # It doesn’t work or start.\n","date":"2011/01/10","externalUrl":null,"permalink":"/retrocomputing/zx_sinclair_spectrum/","section":"RetroComputing","summary":"Back in the early 80s, a good friend of mine owned a ZX Spectrum with 16K, and we spent countless hours loading and playing 8-bit games on it. This machine holds a special place in my memory, representing some of the most enjoyable moments of my early computing days.","title":"ZX Sinclair Spectrum","type":"retrocomputing"},{"content":"Dos de las características más importantes incluidas en la versión 9.0 de PostgreSQL que se lanzará a finales de verano del 2010 son Hot Standby (HS) y Streaming Replication (SR).\nEstas dos características implementan en el núcleo de PostgreSQL lo necesario para instalar un sistema de replicación asincrónica maestro-esclavo (master-slave) en el que los nodos esclavos se pueden utilizar para realizar consultas de solo lectura. Un sistema de replicación de estas caracteristicas se podrá usar tanto para añadir redundancia a nuestras bases de datos, como para descargar de trabajo a nuestro servidor principal en lo referente a consultas de solo lectura.\nNOTA: Este artículo está basado en la versión 9.0beta2 de PostgreSQL, todavia quedan por ajustar algunos detalles para la versión 9.0 final, pero lo más importante ya está implementado y decidido. Cuando la versión 9.0 sea lanzada, actualizaremos este artículo si es necesario.\nAntes de seguir hablando en detalle sobre como configurar y usar HS y SR, tenemos que explicar ciertos conceptos fundamentales para poder entender como este sistema de replicación funciona.\nConceptos básicos # Ficheros WAL: PostgreSQL utiliza los denominados ficheros WAL (Write Ahead Log / REDO) para guardar toda la información sobre las transacciones y cambios realizados en la base de datos. Los ficheros WAL se utilizan para garantizar la integridad de los datos grabados en la base de datos. También se utilizan para reparar automáticamente posibles incosistencias en la base de datos después de una caida súbita del servidor.\nEstos ficheros tienen un nombre único y un tamaño por defecto de 16MB y se generan en el subdirectorio pg_xlog que se encuentra en el directorio de datos ($PGDATA) usado por PostgreSQL. El número de ficheros WAL contenidos en pg_xlog dependerá del valor asignado al parámetro checkpoint_segments en el fichero de configuración postgresql.conf.\nLos ficheros WAL generados en pg_xlog se reciclan continuamente y en un sistema muy ocupado solo tendremos disponibles en pg_xlog los últimos cambios ocurridos en la base de datos durante el periodo de tiempo registrado en los ficheros WAL existentes en pg_xlog.\nLos ficheros WAL se pueden archivar automáticamente como copia de seguridad ó para usarlos con PITR - Point in Time Recovery. Para activar el archivo automático de ficheros WAL hay que definir los parámetros wal_level, archive_mode y archive_command en postgresql.conf. Un fichero WAL se archivará antes que sea reciclado en pg_xlog, pero no antes de que tenga registrado 16MB de información en el mismo.\nTransferencia de registros a nivel de ficheros (file-based log shipping): O lo que es lo mismo, transferencia de ficheros WAL completos (16MB de registros) entre servidores de bases de datos.\nTransferencia de registros a nivel de registros (record-based log shipping): Transferencia de registros WAL sobre la marcha entre servidores de bases de datos. Esto es lo que hace Streaming Replication.\nReplicación/transferencia asincrónica: Cuando los datos se transfieren de un sistema A a otro B, sin esperar por el \u0026ldquo;acuse de recibo\u0026rdquo; de B antes de hacer disponibles en A los datos replicados . En un sistema de replicación asincrónico puede existir un cierto retraso ó demora en la disponibilidad de los datos en el sistema esclavo.\nReplicación/transferencia sincrónica: Cuando los datos se transfieren de un sistema A, a otro B, y A espera el \u0026ldquo;acuse de recibo\u0026rdquo; de B antes de hacer disponibles en A los datos replicados. En un sistema de replicación sincrónico, todos los datos disponibles en el maestro están disponibles en los esclavos.\nStreaming replication (SR) # Esta nueva funcionalidad nos permite transferir asincrónicamente registros WAL sobre la marcha (record-based log shipping) entre un servidor maestro y uno/varios esclavos. SR se configura mediante los parámetros primary_conninfo en el fichero recovery.conf y max_wal_senders, wal_sender_delay y wal_keep_segments en postgresql.conf.\nEn la práctica un proceso denominado receptor WAL (WAL receiver) en el servidor esclavo, se conecta mediante una conexion TCP/IP al servidor maestro. En el servidor maestro existe otro proceso denominado remitente WAL (WAL sender) que es el encargado de mandar los registros WAL sobre la marcha al servidor esclavo.\nA continuación teneis un gráfico explicativo de como SR funciona:\nHot Standby (HS) # Esta nueva funcionalidad nos permite acceder en modo de solo-lectura a todos los datos disponibles en el servidor esclavo a donde estamos replicando nuestras bases de datos. HS se configura mediante los parametros hot_standby y max_standby_delay en postgresql.conf\nA continuación teneis un gráfico explicativo de como HS funciona usando SR y transferencia de ficheros WAL:\nIntroduccion al concepto de replicación implementado en el núcleo de PostgreSQL # El tipo de replicación incluido en el núcleo de PostgreSQL está basado en la transferencia de registros WAL entre servidores. Esta transferencia se puede realizar registro a registro (record-based log shipping) ó en ficheros WAL completos (file-based log shipping).\nSi usamos solamente SR tendremos que tener cuidado que los ficheros WAL en el servidor maestro no sean reciclados antes de ser transferidos al servidor esclavo. Y si transferimos solamente ficheros WAL sin utilizar SR, perderemos las últimas transacciones registradas en el servidor maestro en caso de caida del servidor maestro. Es por esto que para tener un sistema más robusto, se suelen usar los dos métodos conjuntamente.\nUna replicación basada en la transferencia de registros WAL significa que se replicaran absolutamente todas las bases de datos y cambios que realicemos en el servidor maestro. Si lo que necesitamos es replicar solamente algunas de las bases de datos existentes en el maestro ó algunas tablas, tendremos que usar otro tipo de replicación.\nPara configurar un sistema HS usando SR y transferencia de registros WAL, tendremos que realizar lo siguiente:\nConfigurar el acceso automático mediante llaves SSH entre el servidor maestro y el esclavo. Configurar uno de los servidores como maestro Activar el archivo de ficheros WAL en el servidor maestro Activar la transferencia al servidor esclavo de ficheros WAL archivados en el maestro Configurar el acceso necesario en el maestro para que el servidor esclavo pueda conectarse via SR Arrancar el servidor maestro Realizar una copia de seguridad \u0026lsquo;base\u0026rsquo;, mediante el mismo procedimiento que se utiliza con PITR Restaurar en el servidor esclavo la copia de seguridad \u0026lsquo;base\u0026rsquo; realizada en el maestro Configurar el servidor esclavo en recuperación continua de registros WAL Configurar el servidor esclavo como nodo Hot Standby Crear un fichero recovery.conf en el esclavo Activar el uso de SR en el esclavo Activar el uso de ficheros WAL transferidos, en el proceso de restauración Arrancar el servidor esclavo Todo este proceso es más fácil de lo que parece. A continuación vamos a ver paso por paso como hacerlo.\nConfiguración de todos los componentes # Lo primero que tenemos que hacer es instalar dos servidores lo más idénticos posibles. En nuestro caso hemos instalado dos servidores Ubuntu 10.04 server idénticos e instalados con la misma configuración.\nA continuación tenemos que instalar la versión PostgreSQL 9.0 en los dos servidores. Podeis utilizar el método de instalación que más os guste, yo soy de la antigua escuela y PostgreSQL lo suelo instalar desde el código fuente y compiladolo yo mismo.\nEn nuestro caso hemos instalado PostgreSQL 9.0 en el directorio /usr/local/pgsql-9.0/ de nuestros sistemas.\nLo primero que tenemos que hacer en el servidor maestro (server01 - 10.1.1.100) y en el esclavo (server02 - 10.1.1.101) es crear los directorios que vamos a utilizar en nuestro sistema de replicación:\nroot@server01:~# mkdir -p /var/pgsql/data root@server01:~# mkdir -p /var/pgsql/wal_arch root@server01:~# chown -R postgres:postgres /var/pgsql root@server01:~# chmod -R 0700 /var/pgsql root@server02:~# mkdir -p /var/pgsql/data root@server02:~# mkdir -p /var/pgsql/wal_arch root@server02:~# mkdir -p /var/pgsql/wal_shipped root@server02:~# chown -R postgres:postgres /var/pgsql root@server02:~# chmod -R 0700 /var/pgsql Después configuramos el acceso mediante llaves SSH entre el maestro y el esclavo. Más información sobre este tema se puede encontrar en el artículo \u0026ldquo;Combinando SSH, cron y at\u0026rdquo;.\nPrimero tenemos que generar nuestras claves pública y privada en el servidor maestro y el esclavo:\nroot@server01:~# su - postgres postgres@server01:~$ ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/home/postgres/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/postgres/.ssh/id_rsa. Your public key has been saved in /home/postgres/.ssh/id_rsa.pub. The key fingerprint is: 97:56:f8:a7:c3:71:90:5d:54:0f:e1:71:98:00:a6:ea postgres@server01 root@server02:~# su - postgres postgres@server02:~$ ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/home/postgres/.ssh/id_rsa): Created directory \u0026#39;/home/postgres/.ssh\u0026#39;. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/postgres/.ssh/id_rsa. Your public key has been saved in /home/postgres/.ssh/id_rsa.pub. The key fingerprint is: 51:8b:37:7c:e3:8f:27:4e:48:c5:df:eb:c2:bb:c1:04 postgres@server02 A continuación creamos el fichero /home/postgres/.ssh/authorized_keys en el maestro con el contenido del fichero /home/postgres/.ssh/id_rsa.pub en el esclavo. Y viceversa, creamos el fichero /home/postgres/.ssh/authorized_keys en el esclavo con el contenido del fichero /home/postgres/.ssh/id_rsa.pub en el maestro.\nEl siguiente paso es inicializar el cluster PostgreSQL en el servidor maestro:\npostgres@server01:~$ /usr/local/pgsql-9.0/bin/initdb -D /var/pgsql/data --locale=C --encoding=UTF8 The files belonging to this database system will be owned by user \u0026#34;postgres\u0026#34;. This user must also own the server process. The database cluster will be initialized with locale C. The default text search configuration will be set to \u0026#34;english\u0026#34;. fixing permissions on existing directory /var/pgsql/data ... ok creating subdirectories ... ok selecting default max_connections ... 100 selecting default shared_buffers ... 32MB creating configuration files ... ok creating template1 database in /var/pgsql/data/base/1 ... ok initializing pg_authid ... ok initializing dependencies ... ok creating system views ... ok loading system objects\u0026#39; descriptions ... ok creating conversions ... ok creating dictionaries ... ok setting privileges on built-in objects ... ok creating information schema ... ok loading PL/pgSQL server-side language ... ok vacuuming database template1 ... ok copying template1 to template0 ... ok copying template1 to postgres ... ok WARNING: enabling \u0026#34;trust\u0026#34; authentication for local connections You can change this by editing pg_hba.conf or using the -A option the next time you run initdb. Success. You can now start the database server using: /usr/local/pgsql-9.0/bin/postgres -D /var/pgsql/data or /usr/local/pgsql-9.0/bin/pg_ctl -D /var/pgsql/data -l logfile start Antes de arrancar el servidor maestro por primera vez tenemos que actualizar el fichero postgresql.conf con ciertos parámetros.\nEn este artículo solamente mostraremos los parametros mínimos que se necesitan para nuestro sistema de replicación. Más información sobre otros parametros extras que tambien se pueden modificar para controlar diversos aspectos de la replicación se pueden consultar en la documentación de PostgreSQL.\nEn un sistema que se vaya a utilizar en producción, tendremos que cambiar tambien otros parámetros, especialmente los que definen como se va a usar la memoria. Una introducción mínima a este tema se puede encontrar en el artículo \u0026ldquo;Configuración básica de PostgreSQL\u0026rdquo;.\nlisten_addresses = \u0026#39;10.1.1.100\u0026#39; wal_level = hot_standby archive_mode = on archive_command = \u0026#39;/usr/local/pgsql-9.0/bin/archive_wal.sh -P %p -F %f\u0026#39; max_wal_senders = 5 wal_keep_segments = 10 Los parámetros que hemos cambiado en el maestro son:\nlisten_addresses: Con este parámetro definimos la IP por la que podremos acceder via TCP/IP a postgreSQL. wal_level: Este parámetro define cuanta información se grabará en los ficheros WAL generados. Se pueden utilizar tres valores, minimal, archive y hot_standby. En nuestro caso utilizamos el valor hot_standby, porque vamos a utilizar esta funcionalidad. archive_mode: Con este parámetro activamos el archivo de ficheros WAL en el maestro. archive_command: Con el comando definido en este parámetro, copiamos los ficheros WAL a archivar al directorio /var/pgsql/wal_arch en el servidor maestro y transferimos los ficheros WAL archivados, al directorio /var/pgsql/wal_shipped en el servidor esclavo. max_wal_senders: Con este parámetro definimos el número máximo de conexiones que se pueden realizar desde servidores esclavos al servidor maestro via SR (1 por servidor esclavo) wal_keep_segments: Este parámetro define el número máximo de ficheros WAL que mantendremos sin reciclar en el servidor maestro en el caso que SR se retrase en la replicación de datos. Si utilizamos ademas de SR, transferencia de ficheros WAL, este parámetro no es tan importante de configurar. El script archive_wal.sh en nuestro artículo es un script en BASH simple y minimo. Mediante el mismo copiamos los archivos WAL a archivar al directorio /var/pgsql/wal_arch en el maestro y al directorio /var/pgsql/wal_shipped en el esclavo.\nEn un sistema de producción este script deberia de comprobar y tener en cuenta posibles fallos ó errores que se pueden producir en el día a día. En un próximo artículo veremos como hacer este script más robusto.\n#!/bin/bash CHMOD=\u0026#34;/bin/chmod\u0026#34; COPY=\u0026#34;/bin/cp\u0026#34; SCP=\u0026#34;/usr/bin/scp\u0026#34; # Directorio usado por PostgreSQL para generar los ficheros WAL PG_XLOG_DIR=\u0026#34;/var/pgsql/data/pg_xlog\u0026#34; # Directorio usado por PostgreSQL para archivar los ficheros WAL PG_WAL_ARCH_DIR=\u0026#34;/var/pgsql/wal_arch\u0026#34; # Servidor PostgreSQL esclavo STANDBY_SERVER=\u0026#34;10.1.1.101\u0026#34; # Directorio en servidor esclavo donde transferimos los ficheros # WAL archivados en el servidor maestro. PG_WAL_SHIPPED=\u0026#34;/var/pgsql/wal_shipped\u0026#34; NO_ARGS=0 E_OPTERROR=65 # ######################################## # ######################################## # # Function archive_wal() # # ######################################## # ######################################## archive_wal(){ if $COPY -dp $ABSOLUTE_PATH $PG_WAL_ARCH_DIR/$WAL_FILE then $CHMOD 400 $PG_WAL_ARCH_DIR/$WAL_FILE $SCP $PG_WAL_ARCH_DIR/$WAL_FILE $STANDBY_SERVER:$PG_WAL_SHIPPED else sleep 1 exit 1 fi } # ######################################## # ######################################## # Script invoked with no command-line args? # ######################################## # ######################################## if [ $# -eq \u0026#34;$NO_ARGS\u0026#34; ] then help exit $E_OPTERROR fi # ######################################## # ######################################## # Getting command options # ######################################## # ######################################## while getopts \u0026#34;P:F:\u0026#34; Option do case $Option in P) ABSOLUTE_PATH=$OPTARG;; F) WAL_FILE=$OPTARG;; esac done shift $(($OPTIND - 1)) # ######################################## # ######################################## # Sanity check # ######################################## # ######################################## if [ -z $ABSOLUTE_PATH ] then echo \u0026#34;Error: Absolute path not defined\u0026#34; echo exit $E_OPTERROR fi if [ -z $WAL_FILE ] then echo \u0026#34;Error: WAL filename not defined\u0026#34; echo exit $E_OPTERROR fi archive_wal exit 0 # # EOF Como vamos a utilizar Streaming Replication (SR), tenemos que definir tambien en el fichero pg_hba.conf del servidor maestro una linea que permita el acceso del proceso receptor WAL (WAL receiver) al servidor maestro.\nhost replication all 10.1.1.101 255.255.255.255 trust Para facilitar las cosas en este artículo, hemos definido un acceso que no use SSL para encriptar el tráfico y que no necesite el uso de ninguna clave de acceso.\nEn un sistema en producción yo personalmente utilizaria SSL para encriptar el tráfico y el uso de certificados para la autentificación del servidor esclavo. Todo dependerá del nivel de seguridad que se quiera implementar. Más informacion sobre el tema se puede encontrar en el artículo \u0026ldquo;PostgreSQL y el uso de SSL\u0026rdquo;.\nSi utilizais un sistema de autentificación que necesite clave de acceso, por ejemplo \u0026ldquo;md5\u0026rdquo;, tendreis que definir un fichero .pgpass en los servidores esclavos.\nEn estos momentos ya estamos preparados para arrancar el servidor maestro:\npostgres@server01:~$ /usr/local/pgsql-9.0/bin/pg_ctl -D /var/pgsql/data -l /var/pgsql/data/logserver.log start Una vez arrancado el servidor maestro vamos a realizar una copia de seguridad \u0026lsquo;base\u0026rsquo; mediante el mismo procedimiento que se utiliza con PITR. A continuación, restauraremos esta copia de seguridad \u0026lsquo;base\u0026rsquo; en el servidor esclavo\npostgres@server01:~$ /usr/local/pgsql-9.0/bin/psql template1 -c \u0026#34;SELECT pg_start_backup(\u0026#39;copia base inicial\u0026#39;)\u0026#34; pg_start_backup ----------------- 0/1000020 (1 row) postgres@server01:~$ tar -cvf /home/postgres/pg_base_backup.tar /var/pgsql/data/ postgres@server01:~$ /usr/local/pgsql-9.0/bin/psql template1 -c \u0026#34;SELECT pg_stop_backup()\u0026#34; pg_stop_backup ---------------- 0/10000D8 (1 row) postgres@server01:~$ scp /home/postgres/pg_base_backup.tar 10.1.1.101:~/tmp/ postgres@server01:~$ ssh postgres@10.1.1.101 \u0026#34;cd / \u0026amp;\u0026amp; tar -xvf /tmp/pg_base_backup.tar\u0026#34; postgres@server01:~$ ssh postgres@10.1.1.101 \u0026#34;rm /var/pgsql/data/postmaster.pid\u0026#34; Para terminar, lo único que nos queda hacer es modificar el fichero postgresql.conf y crear el fichero recovery.conf en el servidor esclavo. En el fichero postgresql.conf definiremos los siguientes parámetros: listen_addresses = \u0026#39;10.1.1.101\u0026#39; hot_standby = on Los parámetros que hemos cambiado en el esclavo son:\nlisten_addresses: Con este parámetro definimos la IP por la que podremos acceder via TCP/IP a postgreSQL. hot_standby: Para definir que este servidor esclavo se podrá utilizar para realizar consultas de solo lectura. Y en el fichero /var/pgsql/data/recovery.conf definiremos los siguientes parámetros:\nstandby_mode = \u0026#39;on\u0026#39; primary_conninfo = \u0026#39;host=10.1.1.100 port=5432 user=postgres\u0026#39; trigger_file = \u0026#39;/var/pgsql/data/pg_failover_trigger\u0026#39; restore_command = \u0026#39;cp /var/pgsql/wal_shipped/%f \u0026#34;%p\u0026#34;\u0026#39; Los parámetros que definimos en este fichero son:\nstandby_mode: Este parámetro define que el servidor no saldrá del modo de recuperación y continuará probando la restauración continua de información WAL primary_conninfo: Este parámetro define el servidor maestro usado por SR para recoger registros WAL trigger_file: Con este parámetro se define un fichero que en caso de crearse/existir sacará al servidor esclavo del modo \u0026ldquo;hot standby\u0026rdquo; y de recuperación continua. restore_command: Con este parámetro definimos el comando a utilizar, si es necesario, para restaurar los ficheros WAL que se encuentran en /var/pgsql/wal_shipped Una vez realizado estos cambios podemos arrancar postgreSQL en el servidor esclavo.\npostgres@server02:~$ /usr/local/pgsql-9.0/bin/pg_ctl -D /var/pgsql/data -l /var/pgsql/data/logserver.log start Y comprobar como el servidor maestro y el esclavo contienen los mismos datos.\npostgres@server01:~$ /usr/local/pgsql-9.0/bin/psql psql (9.0beta2) Type \u0026#34;help\u0026#34; for help. postgres=# \\l List of databases Name | Owner | Encoding | Collation | Ctype | Access privileges -----------+----------+----------+-----------+-------+----------------------- postgres | postgres | UTF8 | C | C | template0 | postgres | UTF8 | C | C | =c/postgres + | | | | | postgres=CTc/postgres template1 | postgres | UTF8 | C | C | =c/postgres + | | | | | postgres=CTc/postgres (3 rows) postgres=# postgres@server02:~$/usr/local/pgsql-9.0/bin/psql psql (9.0beta2) Type \u0026#34;help\u0026#34; for help. postgres=# \\l List of databases Name | Owner | Encoding | Collation | Ctype | Access privileges -----------+----------+----------+-----------+-------+----------------------- postgres | postgres | UTF8 | C | C | template0 | postgres | UTF8 | C | C | =c/postgres + | | | | | postgres=CTc/postgres template1 | postgres | UTF8 | C | C | =c/postgres + | | | | | postgres=CTc/postgres (3 rows) postgres=# Después de ejecutar ciertos comandos en el servidor maestro, podemos comprobar como los cambios son replicados automáticamente al esclavo.\npostgres@server01:~$ /usr/local/pgsql-9.0/bin/psql psql (9.0beta2) Type \u0026#34;help\u0026#34; for help. postgres=# CREATE DATABASE test001; CREATE DATABASE postgres=# \\c test001 You are now connected to database \u0026#34;test001\u0026#34;. test001=# CREATE TABLE test01 (id bigint, value bigint, primary key (id)); NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index \u0026#34;test01_pkey\u0026#34; for table \u0026#34;test01\u0026#34; CREATE TABLE test001=# INSERT INTO test01 (id, value) VALUES (1,1); INSERT 0 1 postgres@server02:~$ /usr/local/pgsql-9.0/bin/psql psql (9.0beta2) Type \u0026#34;help\u0026#34; for help. postgres=# \\l List of databases Name | Owner | Encoding | Collation | Ctype | Access privileges -----------+----------+----------+-----------+-------+----------------------- postgres | postgres | UTF8 | C | C | template0 | postgres | UTF8 | C | C | =c/postgres + | | | | | postgres=CTc/postgres template1 | postgres | UTF8 | C | C | =c/postgres + | | | | | postgres=CTc/postgres test001 | postgres | UTF8 | C | C | (4 rows) postgres=# \\c test001 You are now connected to database \u0026#34;test001\u0026#34;. test001=# \\d test01 Table \u0026#34;public.test01\u0026#34; Column | Type | Modifiers --------+--------+----------- id | bigint | not null value | bigint | Indexes: \u0026#34;test01_pkey\u0026#34; PRIMARY KEY, btree (id) test001=# SELECT * from test01 ; id | value ----+------- 1 | 1 (1 row) test001=# A partir de este momento ya solo os queda disfrutar de vuestro sistema de replicación.\nTareas de administración y mantenimiento # Una vez que todo está funcionando, tendremos que mantener el sistema y administrarlo en caso de fallo del servidor maestro. Las tareas que tendremos que implementar/realizar serán:\nLimpiar el directorio donde se archivan los ficheros WAL en el servidor maestro, borrando los ficheros WAL antiguos que no se necesiten. Limpiar el directorio a donde se transfieren los ficheros WAL en el servidor esclavo, borrando los ficheros WAL antiguos que no se necesiten. Activar automáticamente el servidor esclavo como nuevo servidor maestro en caso de fallo del servidor maestro en uso. Monitorizar el estado de la replicación para saber el retraso del servidor esclavo en relación al maestro Estas tareas las trataremos en próximos artículos. En el momento de escribir este artículo, y como ya hemos dicho, la versión 9.0 está todavía en fase beta y aun quedan algunos detalles por pulir. Además se están preparando por la comunidad algunos programas que ayudarán a la implementación de algunas de estas tareas de mantenimiento.\nEnlaces:\nhttp://www.postgresql.org/docs/9.0/static/high-availability.html http://www.postgresql.org/docs/9.0/static/runtime-config-wal.html http://www.postgresql.org/docs/9.0/static/libpq-pgpass.html ","date":"2010/06/29","externalUrl":null,"permalink":"/es/blog/hot-standby-y-streaming-replication/","section":"Blogs","summary":"Dos de las características más importantes incluidas en la versión 9.0 de PostgreSQL que se lanzará a finales de verano del 2010 son Hot Standby (HS) y Streaming Replication (SR).\nEstas dos características implementan en el núcleo de PostgreSQL lo necesario para instalar un sistema de replicación asincrónica maestro-esclavo (master-slave) en el que los nodos esclavos se pueden utilizar para realizar consultas de solo lectura.\n","title":"Hot Standby y Streaming replication","type":"blog"},{"content":"En este artículo vamos a explicar como podemos configurar PostgreSQL 8.4 para realizar conexiones seguras a nuestras bases de datos utilizando SSL.\nVamos a ver dos aspectos diferentes e independientes en el tema de las conexiones seguras, el primero es como cifrar el tráfico entre nuestros clientes y el servidor, y el segundo, como autentificar a los clientes/usuarios mediante certificados digitales.\nCon el cifrado del tráfico entre los clientes y el servidor evitamos que alguien pueda interceptar el tráfico de red y conseguir los datos que viajan por la conexión. Con la autentificación por medio de certificados digitales evitamos que clientes/usuarios que no tengan instalado el certificado correspondiente puedan conectarse a nuestro servidor, incluso si lo hacen cifrando el tráfico de red.\nPostgreSQL soporta nativamente el uso de conexiones SSL para cifrar las comunicaciones, el único requerimiento es tener OpenSSL instalado en nuestro servidor y PostgreSQL compilado con soporte SSL. El soporte SSL durante la compilación se consigue usando el parametro \u0026ndash;with-ssl con ./configure antes de compilar. Más información sobre la instalación desde el código fuente se puede encontrar en el artículo Instalación e inicialización básica de PostgreSQL desde el código fuente. Los paquetes binarios disponibles para Unix/Linux y Windows suelen ser compilados con soporte SSL.\nPara activar el soporte SSL en un sistema que cumple los requerimientos que hemos nombrado hay que hacer tres cosas:\nDefinir el parámetro ssl en el fichero postgresql.conf Instalar el certificado de nuestro servidor, la clave privada correspondiente, el certificado CA y un fichero crl con la lista de certificados revocados, en el directorio de datos PGDATA Arrancar de nuevo el servidor PostgreSQL Una vez que tenemos el soporte SSL instalado y activado hay que configurar en el fichero pg_hba.conf que conexiones van a utilizar SSL para cifrar el tráfico y/ó autentificarse con un certificado digital.\nUna cosa buena de PostgreSQL en lo referente al uso de SSL para cifrar las conexiones, es que el servidor PostgreSQL escucha por conexiones estándares (no-SSL) y SSL por el mismo puerto y negocia automáticamente con el cliente el uso de SSL ó no dependiendo de lo definido en pg_hba.conf.\nVamos a ver por pasos como configurar todo lo necesario.\nCertificados # Lo primero que necesitamos es el certificado SSL que vamos a usar en nuestro servidor, la correspondiente clave privada, el certificado CA y un fichero crl.\nNormalmente los certificados digitales se suelen comprar a una entidad emisora y están vigentes por un determinado periodo de tiempo. Estos certificados están firmados por una autoridad de certificación (CA) la cual es una entidad de confianza, responsable de emitir y revocar certificados digitales.\nSi las conexiones que van a usar SSL son internas y locales, no hace falta pagar a una autoridad de certificación para que emita y firme nuestros certificados con su certificado CA. Nosotros mismos podemos generar el certificado y firmarlo como un CA local. Para esto tenemos que hacer lo siguiente:\nInstalar la infraestructura necesaria para ser una CA local Generar un certificado y llave CA que utilizaremos para firmar los certificados que vayamos a usar en nuestros sistemas Generar el certificado y la llave que el servidor PostgreSQL necesita Firmar nuestro certificado del servidor con el CA que hemos generado Instalar la infraestructura necesaria para ser una CA local # Primero tenemos que instalar openssl en nuestro ordenador, para esto podemos utilizar el sistema de instalación por defecto de nuestro sistema operativo.\nA continuación creamos como root el directorio que albergará la infraestructura CA:\n# mkdir -m 0755 /etc/CA_local Después creamos los subdirectorios que necesitaremos dentro de /etc/CA_local\n# mkdir -m 0755 \\ /etc/CA_local \\ /etc/CA_local/private \\ /etc/CA_local/certs \\ /etc/CA_local/newcerts \\ /etc/CA_local/crl Ahora copiamos el fichero de configuración global de openssl (/etc/ssl/openssl.cnf en mi sistema) a nuestro directorio CA_local.\n# cp /etc/ssl/openssl.cnf /etc/CA_local/openssl.local.cnf # chmod 0600 /etc/CA_local/openssl.local.cnf A continuación teneis que actualizar el fichero /etc/CA_local/openssl.local.cnf. En la sección [ CA_default ], cambiar los parámetros dir, certificate y private_key a lo siguiente:\ndir = /etc/CA_local # Where everything is kept certificate = $dir/certs/local_ca.crt # The CA certificate private_key = $dir/private/local_ca.key # The private key Tambien necesitamos tres ficheros extras que podemos crear de la siguiente manera:\n# touch /etc/CA_local/index.txt # echo \u0026#39;01\u0026#39; \u0026gt; /etc/CA_local/serial # echo \u0026#39;01\u0026#39; \u0026gt; /etc/CA_local/crlnumber Generar un certificado y llave CA y un fichero crl con la lista de certificados revocados # Primero vamos a crear un certificado CA que utilizaremos para firmar los certificados que usemos localmente en nuestros sistemas.\n# cd /etc/CA_local/ # openssl req -config openssl.local.cnf -new -x509 -extensions v3_ca \\ -keyout private/local_ca.key -out certs/local_ca.crt -days 1825 Este último comando creará un certificado CA autofirmado con una validez de 5 años. Tendreis que definir una contraseña para la llave privada CA (no olvidar esta contraseña!! ya que se necesitará cada vez que usemos el certificado local CA) y rellenar algunos parámetros para vuestro sistema. En mi sistema de ejemplo, estos son los datos introducidos.\nGenerating a 1024 bit RSA private key ...................++++++ writing new private key to \u0026#39;private/local_ca.key\u0026#39; Enter PEM pass phrase: Verifying - Enter PEM pass phrase: ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter \u0026#39;.\u0026#39;, the field will be left blank. ----- Country Name (2 letter code) [AU]:NO State or Province Name (full name) [Some-State]:. Locality Name (eg, city) []:Oslo Organization Name (eg, company) [Internet Widgits Pty Ltd]:PostgreSQL-es.org Organizational Unit Name (eg, section) []:. Common Name (eg, YOUR name) []:postgresql-es.org Email Address []:webmaster@postgresql-es.org Una vez realizado estos pasos se habrán creado dos ficheros en vuestro sistema:\n/etc/CA_local/certs/local_ca.crt: Certificado CA local. Se puede distribuir públicamente y todo el mundo puede acceder al mismo sin problemas de seguridad. /etc/CA_local/private/local_ca.key: Llave CA privada. Se tiene que restringir el acceso a la misma ( chmod 0400 /etc/CA_local/private/local_ca.key) y no se puede distribuir públicamente. El fichero CRL (Certificate Revocation List) con la lista de certificados revocados por este CA lo podeis crear con:\nopenssl ca -config openssl.local.cnf -keyfile private/local_ca.key \\ -cert certs/local_ca.crt -gencrl -out crl/local_ca.crl Y comprobarlo y verificarlo con:\nopenssl crl -in crl/local_ca.crl -text -noout Crear una solicitud de certificado para el servidor # Tenemos que generar una solicitud de certificado (server.csr) para generar un certificado que se pueda usar en nuestro servidor. Para ello podemos utilizar los siguientes comandos:\n# cd /etc/CA_local/ # openssl req -config openssl.local.cnf -new -nodes \\ -keyout private/ejemplo.postgresql-es.org.key \\ -out ejemplo.postgresql-es.org.csr -days 730 Con esto generamos la solicitud de certificado con una validez de 2 años, después de rellenar algunos parámetros de nuestro sistema. En nuestro ejemplo, estos son los datos introducidos.\nGenerating a 1024 bit RSA private key .........++++++ ............................++++++ writing new private key to \u0026#39;private/server.key\u0026#39; ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter \u0026#39;.\u0026#39;, the field will be left blank. ----- Country Name (2 letter code) [AU]:NO State or Province Name (full name) [Some-State]:. Locality Name (eg, city) []:Oslo Organization Name (eg, company) [Internet Widgits Pty Ltd]:PostgreSQL-es.org Organizational Unit Name (eg, section) []:. Common Name (eg, YOUR name) []:ejemplo.postgresql-es.org Email Address []:webmaster@postgresql-es.org Please enter the following \u0026#39;extra\u0026#39; attributes to be sent with your certificate request A challenge password []: An optional company name []: Los parámetros a rellenar más importantes son \u0026ldquo;Common Name\u0026rdquo; y \u0026ldquo;Email Address\u0026rdquo;. \u0026ldquo;Common Name\u0026rdquo; debería de tener el nombre de la máquina que va a estar utilizando el certificado, \u0026ldquo;Email Address\u0026rdquo; es la dirección de contacto de vuestro sistema.\nNo teneis que introducir nada en los parametros \u0026ldquo;challenge password\u0026rdquo; y \u0026ldquo;optional company name\u0026rdquo; del final. Hemos utilizado el parámetro -nodes para no tener que proteger nuestra llave privada con una contraseña y no tener que escribir una contraseña cada vez que arranquemos un servicio que use el certificado. Por ello es muy importante no perder ó publicar el fichero con la llave privada.\nFirmar nuestra solicitud de certificado con nuestro CA para generar el certificado final # Una vez que tenemos la solicitud de certificado (server.csr), vamos a firmarla con nuestro certificado CA. Para ello utilizamos este comando:\n# cd /etc/CA_local/ # openssl ca -config openssl.local.cnf -policy policy_anything \\ -days 730 -out certs/ejemplo.postgresql-es.org.crt \\ -infiles ejemplo.postgresql-es.org.csr Tendremos que utilizar la contraseña que usamos cuando creamos el certificado CA para terminar de firmar nuestro certificado:\nUsing configuration from openssl.local.cnf Enter pass phrase for /etc/CA_local/private/local_ca.key: Check that the request matches the signature Signature ok Certificate Details: Serial Number: 1 (0x1) Validity Not Before: Nov 30 20:05:18 2009 GMT Not After : Nov 30 20:05:18 2011 GMT Subject: countryName = NO localityName = Oslo organizationName = PostgreSQL-es.org commonName = ejemplo.postgresql-es.org emailAddress = webmaster@postgresql-es.org X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Key Identifier: 58:40:FC:0A:85:C7:58:9D:9D:66:8A:7F:0A:CB:46:65:CD:A2:92:20 X509v3 Authority Key Identifier: keyid:54:11:C7:CD:1A:1D:54:01:42:D1:FC:1A:1A:DD:ED:B5:5E:E6:89:BF Certificate is to be certified until Nov 30 20:05:18 2011 GMT (730 days) Sign the certificate? [y/n]:y 1 out of 1 certificate requests certified, commit? [y/n]y Write out database with 1 new entries Data Base Updated Vamos a asegurar mejor la clave privada del certificado. Este fichero es privado y no se debe de distribuir:\n# chown root:root /etc/CA_local/private/ejemplo.postgresql-es.org.key # chmod 0400 /etc/CA_local/private/ejemplo.postgresql-es.org.key Podeis ver la información contenida en el certificado con este comando:\n# cd /etc/CA_local/ # openssl x509 -in certs/ejemplo.postgresql-es.org.crt -noout -text Y comprobar y verificar el certificado con el siguiente comando:\n# cd /etc/CA_local/ # openssl verify -purpose sslserver -CAfile /etc/CA_local/certs/local_ca.crt \\ /etc/CA_local/certs/ejemplo.postgresql-es.org.crt El fichero de solicitud de certificado ya no lo necesitais y lo podeis borrar\n# rm /etc/CA_local/ejemplo.postgresql-es.org.csr Esto es todo lo que necesitais saber sobre certificados si quereis hacerlo todo vosotros para uso local/privado. Al final de todo el proceso tendreis los siguientes ficheros en el directorio /etc/CA_local\n/etc/CA_local/certs/local_ca.crt: Certificado CA local que podeis utilizar para firmar otros certificados. /etc/CA_local/certs/ejemplo.postgresql-es.org.crt: Certificado firmado por el CA local que vamos a usar en ejemplo.postgresql-es.org /etc/CA_local/private/local_ca.key: Llave privada del certificado CA local /etc/CA_local/private/ejemplo.postgresql-es.org.key: Llave privada del certificado firmado por el CA local y que vamos a usar en ejemplo.postgresql-es.org /etc/CA_local/crl/local_ca.crl: Fichero CRL (Certificate Revocation List) con la lista de certificados revocados Configuración de PostgreSQL para usar SSL # Una vez que tenemos todos los certificados que necesitamos, tenemos que configurar PostgreSQL para activar y usar SSL.\nLo primero que hacemos es copiar los ficheros /etc/CA_local/certs/ejemplo.postgresql-es.org.crt, /etc/CA_local/private/ejemplo.postgresql-es.org.key, /etc/CA_local/certs/local_ca.crt y /etc/CA_local/crl/local_ca.crl al directorio de datos de nuestra instalación PostgreSQL. En nuestro ejemplo tenemos el directorio de datos en /var/pgsql-8.4/data\n# cp /etc/CA_local/certs/ejemplo.postgresql-es.org.crt \\ /var/pgsql-8.4/data/server.crt # cp /etc/CA_local/private/ejemplo.postgresql-es.org.key \\ /var/pgsql-8.4/data/server.key # cp /etc/CA_local/certs/local_ca.crt /var/pgsql-8.4/data/root.crt # cp /etc/CA_local/crl/local_ca.crl /var/pgsql-8.4/data/root.crl # chown postgres:postgres /var/pgsql-8.4/data/server.* # chown postgres:postgres /var/pgsql-8.4/data/root.* # chmod 0400 /var/pgsql-8.4/data/server.* # chmod 0400 /var/pgsql-8.4/data/root.* Después tenemos que actualizar nuestro fichero /var/pgsql-8.4/data/postgresql.conf. Tenemos que cambiar el valor del parametro ssl.\nssl = on Y arrancar PostgreSQL de nuevo usando el script det arranque de nuestro sistema. En nuestro servidor ejemplo ejecutamos esto:\n/etc/init.d/postgresql stop /etc/init.d/postgresql start Ahora actualizamos el fichero /var/pgsql-8.4/data/pg_hba.conf con esta linea:\nhostssl all postgres 127.0.0.1/32 md5 Y volvemos a leer el fichero de configuración para activar los cambios.\n/etc/init.d/postgresql reload Si hemos hecho las cosas correctamente deberiamos de poder conectar con nuestro servidor PostgreSQL via SSL y cifrando nuestro tráfico. La linea que hemos definido en pg_hba.conf define que el usuario postgres puede conectarse desde 127.0.0.1/32 a todas las bases de datos de nuestra instalacion solamente via SSL y escribiendo la clave de acceso (Teneis que haber definido la clave del usuario postgres con antelación para que esto funcione). Vosotros tendreis que configurar vuestro sistema para dar acceso a los clientes de vuestro sistema.\npostgres@core4:~$ psql -h 127.0.0.1 password: psql (8.4.1) SSL connection (cipher: DHE-RSA-AES256-SHA, bits: 256) Type \u0026#34;help\u0026#34; for help. postgres=# La linea SSL connection (cipher: DHE-RSA-AES256-SHA, bits: 256) que se muestra al conectar con el servidor nos indica que la conexión está cifrada via SSL.\nSi habeis llegado hasta aquí, ya teneis el servidor PostgreSQL configurado para recibir conexiones via SSL y cifrar el tráfico entre los clientes y el servidor de bases de datos.\nAutentificar a los clientes mediante certificados SSL # También podemos utilizar certificados digitales para autentificar a los clientes que se quieran conectar con nuestro servidor. De esta manera solamente podrán conectarse a nuestro servidor los clientes que tengan instalado un certificado que esté firmado por una autoridad de certificación (CA) registrada en el fichero /var/pgsql-8.4/data/root.crt de nuestro servidor.\nPara activar esta posibilidad lo primero que tenemos que hacer es crear un certificado para nuestro cliente y firmarlo con nuestro certificado CA local. A continuación debemos crear un subdirectorio (~/.postgresql/) en el directorio HOME del usuario que va a ejecutar el programa cliente, y para terminar tenemos que copiar a este subdirectorio cuatro ficheros. En nuestro ejemplo creamos el directorio /home/postgresql/.postgresql y copiamos en el los ficheros necesarios:\n/home/postgresql/.postgresql/postgresql.crt: Certificado firmado por el CA local que hemos creado para nuestro cliente /home/postgresql/.postgresql/postgresql.key: Llave privada del certificado creado para el cliente /home/postgresql/.postgresql/root.crt: Certificado CA local utilizado para firmar el certificado creado para el cliente /home/postgresql/.postgresql/root.crl: Fichero CRL (Certificate Revocation List) con la lista de certificados revocados Definimos los permisos necesarios:\n# chmod 0400 /home/postgresql/.postgresql/* A continuación actualizamos el fichero /var/pgsql-8.4/data/pg_hba.conf con una linea similar a esta:\nhostssl all postgres 127.0.0.1/32 md5 clientcert=1 Y leemos el fichero de configuración para activar los cambios.\n/etc/init.d/postgresql reload Los dos parámetros importantes en esta linea son hostssl y clientcert=1. De esta manera podemos definir que la conexión se pueda realizar solamente desde clientes que tengan instalado un certificado que esté firmado por una autoridad de certificación (CA) que reconozcamos. Si no tuviesemos los ficheros necesarios en /home/postgresql/.postgresql/, nos daria un error al intentar conectarnos.\n# psql -h 127.0.0.1 -U postgres psql: could not open certificate file \u0026#34;/home/postgres/.postgresql/postgresql.crt\u0026#34;: No such file or directory FATAL: connection requires a valid client certificate FATAL: no pg_hba.conf entry for host \u0026#34;127.0.0.1\u0026#34;, user \u0026#34;postgres\u0026#34;, database \u0026#34;postgres\u0026#34;, SSL off Autentificar a los usuarios mediante certificados SSL # Hasta ahora hemos visto como encriptar el trafico de red y como autentificar a clientes con certificados digitales. A partir de la versión 8.4.0 de PostgreSQL tambien se pueden utilizar certificados digitales para autentificar a los usuarios que se quieran conectar con nuestro servidor.\nEste tipo de autentificación se activa usando el metodo \u0026lsquo;cert\u0026rsquo; en la opción método cuando definamos el acceso en pg_hba.conf. Si usamos este método de autentificación no necesitaremos escribir ninguna clave de acceso pero deberemos de proveer un certificado valido.\nEl atributo CN (lo que se define en \u0026lsquo;Common Name (eg, YOUR name) []:\u0026rsquo;) del certificado es el atributo que se comprueba para ver si corresponde con el usuario con el que se intenta conectar. Si el CN no es igual al usuario de la base de datos con el que se intenta conectar podemos usar mapas de usuario en el servidor para hacer la conversión.\nLo primero que tenemos que hacer es crear un certificado para nuestro usuario (\u0026lsquo;Common Name (eg, YOUR name) []: postgres\u0026rsquo;) y firmarlo con nuestro certificado CA local. A continuación copiamos este certificado y la llave privada a /home/postgresql/.postgresql/postgresql.crt y /home/postgresql/.postgresql/postgresql.key en el ordenador cliente.\nDespués tenemos que actualizar el fichero /var/pgsql-8.4/data/pg_hba.conf con una linea similar a esta:\nhostssl all postgres 127.0.0.1/32 cert Y por último leemos el fichero de configuración para activar los cambios.\n/etc/init.d/postgresql reload Si no tuviesemos el certificado correcto instalado, el sistema nos daria un error similar al que se obtiene cuando la autentificación con certificado del ordenador cliente falla.\nEnlaces:\nhttp://www.postgresql.org/docs/current/interactive/ssl-tcp.html http://www.postgresql.org/docs/current/interactive/libpq-ssl.html http://www.postgresql.org/docs/curent/interactive/auth-pg-hba-conf.html http://www.postgresql.org/docs/current/interactive/auth-methods.html ","date":"2009/12/01","externalUrl":null,"permalink":"/es/blog/postgresql-y-el-uso-de-ssl/","section":"Blogs","summary":"En este artículo vamos a explicar como podemos configurar PostgreSQL 8.4 para realizar conexiones seguras a nuestras bases de datos utilizando SSL.\nVamos a ver dos aspectos diferentes e independientes en el tema de las conexiones seguras, el primero es como cifrar el tráfico entre nuestros clientes y el servidor, y el segundo, como autentificar a los clientes/usuarios mediante certificados digitales.\n","title":"PostgreSQL y el uso de SSL","type":"blog"},{"content":"En este artículo vamos a dar una introducción a las \u0026ldquo;funciones ventanas\u0026rdquo; (Window functions), una nueva funcionalidad disponible a partir de PostgreSQL 8.4.\nEsta funcionalidad fue introducida en el estandard SQL2003 y ampliada en SQL2008. Esta disponible en Oracle, SQL server, Sybase y DB2, pero en ninguna base de datos de código abierto exceptuando a PostgreSQL.\nLas \u0026ldquo;funciones ventanas\u0026rdquo; hacen la vida más fácil cuando se necesitan realizar cierto tipo de consultas en donde queremos aplicar una función agregada a una partición o subconjunto de filas, desde cada fila que forma parte de un resultado. Las \u0026ldquo;funciones ventana\u0026rdquo; son similares hasta cierto punto a las típicas funciones agregadas pero con muchas mas posibilidades.\nCon funciones agregadas normales y el uso de GROUP BY obtenemos un resultado con una fila por cada valor diferente del atributo usado en el GROUP BY. El siguiente gráfico muestra como funciona una consulta con funciones agregadas normales.\nSin embargo con el uso de las \u0026ldquo;funciones ventanas\u0026rdquo; la cosa cambia. En el siguiente gráfico podemos ver la diferencia cuando usamos estas funciones.\nVamos a concretar conceptos e ideas.\nUna función ventana es una función agregada aplicada a una partición ó subconjunto del resultado de una consulta Una función ventana se define utilizando la cláusula OVER despues de la función. Una función ventana devuelve un valor por cada fila del resultado de una consulta Para trabajar con ventanas se pueden utilizar las funciones agregadas normales y específicas disponibles en PostgreSQL. Más información sobre las funciones disponibles se encuentra disponible en ingles en 9.19. Window Functions y en 9.18. Aggregate Functions. También se pueden utilizar funciones agregadas creadas por nosotros. Una ventana está formada por una partición (PARTITION) y un marco (FRAME) Una ventana se define aplicando la cláusula OVER a la función agregada La cláusula OVER define la partición ó subconjunto que forma la ventana y un marco(frame) dentro de la partición Una partición se define con la cláusula PARTITION BY Si no utilizamos la cláusula PARTITION BY, todas las filas se consideran dentro de la misma ventana Un marco se define con la cláusula ORDER BY y una cláusula \u0026ldquo;marco\u0026rdquo; Un marco permite definir los límites de una ventana La cláusula OVER puede contener la definición de una ventana ó el nombre de una ventana definida con la cláusula opcional WINDOW La cláusula OVER se define como:\nOVER( [PARTITION BY expresion [, ...]] [ORDER BY expression [ASC|DESC|USING operator][NULLS{FIRST|LAST}][, ...]] [clausula_frame] ) La [clausula_frame] puede contener:\nRANGE UNBOUNDED PRECEDING RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ROWS UNBOUNDED PRECEDING ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING La cláusula OVER también se puede definir como:\nOVER nombre_de_la_ventana En este caso habrá que utilizar una cláusula WINDOW para definir la ventana nombre_de_la_ventana\nWINDOW nombre_de_la_ventana AS (definicion_de_la_ventana) [, ...] Y la definicion_de_la_ventana se definiria como:\n[nombre_de_una_ventana_existente] [PARTITION BY expresion [, ...]] [ORDER BY expression [ASC|DESC|USING operator][NULLS{FIRST|LAST}][, ...]] [clausula_frame] Bueno, hasta ahora solo hemos enumerado conceptos y definiciones que probablemente sean un poco difíciles de asimilar la primera vez que trateis este tema. No hay nada mejor que unos ejemplos prácticos para ver como se utilizan las \u0026ldquo;funciones ventana\u0026rdquo;.\nPara nuestros ejemplos crearemos la tabla empleado con información sobre el departamento, el salario y la edad de cada empleado:\nCREATE TABLE empleado ( empid integer, departamento text, salario integer, edad integer, primary key (empid) ); INSERT INTO empleado (empid,departamento,salario,edad) VALUES (1,\u0026#39;ventas\u0026#39;,3000,24); INSERT INTO empleado (empid,departamento,salario,edad) VALUES (2,\u0026#39;ventas\u0026#39;,3200,26); INSERT INTO empleado (empid,departamento,salario,edad) VALUES (3,\u0026#39;ventas\u0026#39;,3500,35); INSERT INTO empleado (empid,departamento,salario,edad) VALUES (4,\u0026#39;distribucion\u0026#39;,2000,22); INSERT INTO empleado (empid,departamento,salario,edad) VALUES (5,\u0026#39;distribucion\u0026#39;,2100,42); INSERT INTO empleado (empid,departamento,salario,edad) VALUES (6,\u0026#39;distribucion\u0026#39;,2400,40); INSERT INTO empleado (empid,departamento,salario,edad) VALUES (7,\u0026#39;produccion\u0026#39;,2800,41); INSERT INTO empleado (empid,departamento,salario,edad) VALUES (8,\u0026#39;produccion\u0026#39;,2400,29); INSERT INTO empleado (empid,departamento,salario,edad) VALUES (9,\u0026#39;produccion\u0026#39;,1900,19); INSERT INTO empleado (empid,departamento,salario,edad) VALUES (10,\u0026#39;produccion\u0026#39;,3000,45); INSERT INTO empleado (empid,departamento,salario,edad) VALUES (11,\u0026#39;produccion\u0026#39;,3000,40); SELECT empid, departamento, salario, edad FROM empleado; empid | departamento | salario | edad -------+--------------+---------+------ 1 | ventas | 3000 | 24 2 | ventas | 3200 | 26 3 | ventas | 3500 | 35 4 | distribucion | 2000 | 22 5 | distribucion | 2100 | 42 6 | distribucion | 2400 | 40 7 | produccion | 2800 | 41 8 | produccion | 2400 | 29 9 | produccion | 1900 | 19 10 | produccion | 3000 | 45 11 | produccion | 3000 | 40 (11 rows) Ahora vamos a definir nuestra primera \u0026ldquo;función ventana\u0026rdquo;. A la consulta anterior le vamos a añadir una columna con el salario medio del departamento donde el empleado trabaja. Para ellos utilizamos la funcion avg() con la cláusula OVER() y definimos la partición por departamentos.\nSELECT empid, departamento, salario, edad, avg(salario) OVER (PARTITION BY departamento) AS salario_medio FROM empleado ; empid | departamento | salario | edad | salario_medio -------+--------------+---------+------+----------------------- 4 | distribucion | 2000 | 22 | 2166.6666666666666667 6 | distribucion | 2400 | 40 | 2166.6666666666666667 5 | distribucion | 2100 | 42 | 2166.6666666666666667 11 | produccion | 3000 | 40 | 2620.0000000000000000 8 | produccion | 2400 | 29 | 2620.0000000000000000 9 | produccion | 1900 | 19 | 2620.0000000000000000 10 | produccion | 3000 | 45 | 2620.0000000000000000 7 | produccion | 2800 | 41 | 2620.0000000000000000 3 | ventas | 3500 | 35 | 3233.3333333333333333 2 | ventas | 3200 | 26 | 3233.3333333333333333 1 | ventas | 3000 | 24 | 3233.3333333333333333 (11 rows) Particularmente me gusta más utilizar la cláusula WINDOW. La consulta anterior utilizando la clausula WINDOW se puede escribir de la siguiente manera:\nSELECT empid, departamento, salario, edad, avg(salario) OVER ventana_departamento AS salario_medio FROM empleado WINDOW ventana_departamento AS (PARTITION BY departamento); ¿Qué os parece?, no es tan difícil cuando se ve en un ejemplo ¿no?. Vamos a seguir complicando las cosas. Además del salario medio queremos la edad media del departamento donde el empleado trabaja.\nLos valores medios los voy a redondear con la función round() para no tener tantos decimales. Como las dos funciones ventana que calculan los valores medios utilizan la misma ventana, solo habrá que utilizar una sola cláusula WINDOW. Si no utilizaramos la cláusula WINDOW, tendriamos que definir la ventana dos veces con OVER().\nSELECT empid, departamento, salario, edad, round(avg(salario) OVER ventana_departamento) AS sal_medio, round(avg(edad) OVER ventana_departamento) AS ed_media FROM empleado WINDOW ventana_departamento AS (PARTITION BY departamento); empid | departamento | salario | edad | sal_medio | ed_media -------+--------------+---------+------+-----------+---------- 4 | distribucion | 2000 | 22 | 2167 | 35 6 | distribucion | 2400 | 40 | 2167 | 35 5 | distribucion | 2100 | 42 | 2167 | 35 11 | produccion | 3000 | 40 | 2620 | 35 8 | produccion | 2400 | 29 | 2620 | 35 9 | produccion | 1900 | 19 | 2620 | 35 10 | produccion | 3000 | 45 | 2620 | 35 7 | produccion | 2800 | 41 | 2620 | 35 3 | ventas | 3500 | 35 | 3233 | 28 2 | ventas | 3200 | 26 | 3233 | 28 1 | ventas | 3000 | 24 | 3233 | 28 (11 rows) Una vez que sabemos como hallar el salario medio y la edad media en cada de departamento, podriamos calcular la diferencia entre lo que cobra cada empleado y la media del departamento y la diferencia entre la edad de cada empleado y la media del departamento.\nSELECT empid, departamento, salario, edad, salario - round(avg(salario) OVER ventana_departamento) AS sal_diff, edad - round(avg(edad) OVER ventana_departamento) AS ed_diff FROM empleado WINDOW ventana_departamento AS (PARTITION BY departamento); empid | departamento | salario | edad | sal_diff | ed_diff -------+--------------+---------+------+----------+--------- 4 | distribucion | 2000 | 22 | -167 | -13 6 | distribucion | 2400 | 40 | 233 | 5 5 | distribucion | 2100 | 42 | -67 | 7 11 | produccion | 3000 | 40 | 380 | 5 8 | produccion | 2400 | 29 | -220 | -6 9 | produccion | 1900 | 19 | -720 | -16 10 | produccion | 3000 | 45 | 380 | 10 7 | produccion | 2800 | 41 | 180 | 6 3 | ventas | 3500 | 35 | 267 | 7 2 | ventas | 3200 | 26 | -33 | -2 1 | ventas | 3000 | 24 | -233 | -4 (11 rows) Ahora vamos a hallar el salario medio y la edad media de toda la empresa, sin hacer distinción entre departamentos. Para ello definimos la cláusula WINDOW vacia. De esta manera la ventana definida es todo el resultado:\nSELECT empid, departamento, salario, edad, round(avg(salario) OVER ventana_empresa) AS sal_medio, round(avg(edad) OVER ventana_empresa) AS ed_media FROM empleado WINDOW ventana_empresa AS (); empid | departamento | salario | edad | sal_medio | ed_media -------+--------------+---------+------+-----------+---------- 1 | ventas | 3000 | 24 | 2664 | 33 2 | ventas | 3200 | 26 | 2664 | 33 3 | ventas | 3500 | 35 | 2664 | 33 4 | distribucion | 2000 | 22 | 2664 | 33 5 | distribucion | 2100 | 42 | 2664 | 33 6 | distribucion | 2400 | 40 | 2664 | 33 7 | produccion | 2800 | 41 | 2664 | 33 8 | produccion | 2400 | 29 | 2664 | 33 9 | produccion | 1900 | 19 | 2664 | 33 10 | produccion | 3000 | 45 | 2664 | 33 11 | produccion | 3000 | 40 | 2664 | 33 (11 rows) Equivalentemente a uno de los ejemplos anteriores, la diferencia entre lo que cobra cada empleado y la media de la empresa y la diferencia entre la edad de cada empleado y la media de la empresa se obtendria así:\nSELECT empid, departamento, salario, edad, salario - round(avg(salario) OVER ventana_empresa) AS sal_diff, edad - round(avg(edad) OVER ventana_empresa) AS ed_diff FROM empleado WINDOW ventana_empresa AS (); empid | departamento | salario | edad | sal_diff | ed_diff -------+--------------+---------+------+----------+--------- 1 | ventas | 3000 | 24 | 336 | -9 2 | ventas | 3200 | 26 | 536 | -7 3 | ventas | 3500 | 35 | 836 | 2 4 | distribucion | 2000 | 22 | -664 | -11 5 | distribucion | 2100 | 42 | -564 | 9 6 | distribucion | 2400 | 40 | -264 | 7 7 | produccion | 2800 | 41 | 136 | 8 8 | produccion | 2400 | 29 | -264 | -4 9 | produccion | 1900 | 19 | -764 | -14 10 | produccion | 3000 | 45 | 336 | 12 11 | produccion | 3000 | 40 | 336 | 7 (11 rows) Ahora vamos a poner un ejemplo con dos ventanas. Una la vamos a utilizar para hallar en que posición (ranking) se encuentra cada empleado por departamentos, en relación al salario que cobra, y la otra para hallar en que posición (ranking) se encuentra cada empleado por departamentos, en relación a la edad que tiene. Utilizaremos la función dense_rank().\nSELECT empid, departamento, salario, edad, dense_rank() OVER ventana_departamento_salario AS sal_pos, dense_rank() OVER ventana_departamento_edad AS ed_pos FROM empleado WINDOW ventana_departamento_salario AS (PARTITION BY departamento ORDER BY salario DESC), ventana_departamento_edad AS (PARTITION BY departamento ORDER BY edad DESC); empid | departamento | salario | edad | sal_pos | ed_pos -------+--------------+---------+------+---------+-------- 5 | distribucion | 2100 | 42 | 2 | 1 6 | distribucion | 2400 | 40 | 1 | 2 4 | distribucion | 2000 | 22 | 3 | 3 10 | produccion | 3000 | 45 | 1 | 1 7 | produccion | 2800 | 41 | 2 | 2 11 | produccion | 3000 | 40 | 1 | 3 8 | produccion | 2400 | 29 | 3 | 4 9 | produccion | 1900 | 19 | 4 | 5 3 | ventas | 3500 | 35 | 1 | 1 2 | ventas | 3200 | 26 | 2 | 2 1 | ventas | 3000 | 24 | 3 | 3 (11 rows) ¿Y si solamente queremos, por ejemplo, los datos del departamento de producción?. Podemos utilizar una cláusula WHERE para acotar el resultado.\nSELECT empid, departamento, salario, edad, dense_rank() OVER ventana_departamento_salario AS sal_pos, dense_rank() OVER ventana_departamento_edad AS ed_pos FROM empleado WHERE departamento = \u0026#39;produccion\u0026#39; WINDOW ventana_departamento_salario AS (PARTITION BY departamento ORDER BY salario DESC), ventana_departamento_edad AS (PARTITION BY departamento ORDER BY edad DESC); empid | departamento | salario | edad | sal_pos | ed_pos -------+--------------+---------+------+---------+-------- 10 | produccion | 3000 | 45 | 1 | 1 7 | produccion | 2800 | 41 | 2 | 2 11 | produccion | 3000 | 40 | 1 | 3 8 | produccion | 2400 | 29 | 3 | 4 9 | produccion | 1900 | 19 | 4 | 5 (5 rows) Podriamos haber conseguido el mismo resultado escribiendo la consulta anterior de las siguientes maneras:\nSin usar la clausula PARTITION BY, ya que el resultado solo tiene datos de un departamento.\nSELECT empid, departamento, salario, edad, dense_rank() OVER ventana_departamento_salario AS sal_pos, dense_rank() OVER ventana_departamento_edad AS ed_pos FROM empleado WHERE departamento = \u0026#39;produccion\u0026#39; WINDOW ventana_departamento_salario AS (ORDER BY salario DESC), ventana_departamento_edad AS (ORDER BY edad DESC); empid | departamento | salario | edad | sal_pos | ed_pos -------+--------------+---------+------+---------+-------- 10 | produccion | 3000 | 45 | 1 | 1 7 | produccion | 2800 | 41 | 2 | 2 11 | produccion | 3000 | 40 | 1 | 3 8 | produccion | 2400 | 29 | 3 | 4 9 | produccion | 1900 | 19 | 4 | 5 (5 rows) O utilizando un subselect:\nSELECT * FROM ( SELECT empid, departamento, salario, edad, dense_rank() OVER ventana_departamento_salario AS sal_pos, dense_rank() OVER ventana_departamento_edad AS ed_pos FROM empleado WINDOW ventana_departamento_salario AS (PARTITION BY departamento ORDER BY salario DESC), ventana_departamento_edad AS (PARTITION BY departamento ORDER BY edad DESC) ) AS subselect WHERE departamento = \u0026#39;produccion\u0026#39;; empid | departamento | salario | edad | sal_pos | ed_pos -------+--------------+---------+------+---------+-------- 10 | produccion | 3000 | 45 | 1 | 1 7 | produccion | 2800 | 41 | 2 | 2 11 | produccion | 3000 | 40 | 1 | 3 8 | produccion | 2400 | 29 | 3 | 4 9 | produccion | 1900 | 19 | 4 | 5 (5 rows) Aunque no todas estas consultas son igual de eficientes. Las dos primeras tienen un tiempo de ejecución similar en torno a los 0.094 ms, pero la del subselect tiene un tiempo de ejecución de 0.137 ms, un 40% más lenta que las dos primeras.\nLa consulta equivalente para toda la empresa seria:\nSELECT empid, departamento, salario, edad, dense_rank() OVER ventana_empresa_salario AS sal_pos, dense_rank() OVER ventana_empresa_edad AS ed_pos FROM empleado WINDOW ventana_empresa_salario AS (ORDER BY salario DESC), ventana_empresa_edad AS (ORDER BY edad DESC); empid | departamento | salario | edad | sal_pos | ed_pos -------+--------------+---------+------+---------+-------- 10 | produccion | 3000 | 45 | 3 | 1 5 | distribucion | 2100 | 42 | 6 | 2 7 | produccion | 2800 | 41 | 4 | 3 6 | distribucion | 2400 | 40 | 5 | 4 11 | produccion | 3000 | 40 | 3 | 4 3 | ventas | 3500 | 35 | 1 | 5 8 | produccion | 2400 | 29 | 5 | 6 2 | ventas | 3200 | 26 | 2 | 7 1 | ventas | 3000 | 24 | 3 | 8 4 | distribucion | 2000 | 22 | 7 | 9 9 | produccion | 1900 | 19 | 8 | 10 (11 rows) Bueno, espero que os hayais hecho una idea de las posibilidades que nos brindan las Funciones ventana en PostgreSQL. Es un característica que muchos de nosotros no hemos tenido la posibilidad de usar hasta ahora con PostgreSQL y que tendremos que acostumbrarnos a utilizar en el futuro.\nSolamente la practica y nuestra imaginación nos pueden ayudar a entender mejor este tipo de consultas. Yo por mi parte tengo que practicar el uso de la cláusula [clausula_frame] en la definición de la ventana, porque todavía no la tengo asimilada.\nEnlaces:\nhttp://www.postgresql.org/docs/current/interactive/tutorial-window.html http://www.postgresql.org/docs/current/interactive/functions-window.html http://www.postgresql.org/docs/current/interactive/sql-select.html http://www.postgresql.org/docs/current/interactive/functions-aggregate.html ","date":"2009/11/06","externalUrl":null,"permalink":"/es/blog/funciones-ventana-window-functions/","section":"Blogs","summary":"En este artículo vamos a dar una introducción a las “funciones ventanas” (Window functions), una nueva funcionalidad disponible a partir de PostgreSQL 8.4.\nEsta funcionalidad fue introducida en el estandard SQL2003 y ampliada en SQL2008. Esta disponible en Oracle, SQL server, Sybase y DB2, pero en ninguna base de datos de código abierto exceptuando a PostgreSQL.\n","title":"Funciones ventana (Window functions)","type":"blog"},{"content":"","date":"2009/11/06","externalUrl":null,"permalink":"/es/tags/sql/","section":"Tags","summary":"","title":"Sql","type":"tags"},{"content":"Uno de los temas que más cuesta a los que empiezan a aprender SQL son las consultas en las que se recogen diferentes tipos de datos de una ó múltiples tablas. Este artículo es una introducción a como definir consultas de este tipo en PostgreSQL.\nUnos conocimientos básicos de normalización de datos y un poco de álgebra relacional no vienen mal para entender mejor algunos de los términos que vamos a usar en este artículo. La normalización de datos es tema para otro artículo, pero en este veremos brevemente algunos conceptos de álgebra relacional que nos pueden ayudar a entender mejor el tema que estamos tratando.\nAlgebra relacional # El álgebra relacional es un tipo de álgebra con una serie de operadores que trabajan sobre una ó varias relaciones para obtener una relación resultado. Es la base indispensable para poder escribir buenas consultas en SQL.\nLas operaciones más importantes disponibles en álgebra relacional son:\nLas operaciones de conjunto aplicadas a relaciones: unión(∪), intersección(∩) y diferencia(-) Operaciones que eliminan una parte de las relaciones: selección(σ) y proyección(Π) Operaciones que combinan las tuplas de dos relaciones: producto cartesiano(x), combinacion natural (\u0026gt;\u0026lt;) y theta Operación que cambia el nombre de los atributos ó relación: renombre(ρ) A continuación vamos a dar una breve introducción sobre estas operaciones:\nUnión [R∪S]\nLa unión de R y S es el conjunto de elementos que existen en R, ó en S, ó en las dos. Un elemento que existe tanto en R como en S aparece solamente una vez en la unión. En el lenguaje SQL este tipo de operación se puede realizar con la clausula UNION\nIntersección [R∩S]\nLa intersección de R y S es el conjunto de elementos que existen en R y en S. En el lenguaje SQL este tipo de operación se puede realizar con la clausula INTERSECT\nDiferencia [R-S]\nLa diferencia de R y S es el conjunto de elementos que existen en R pero no en S. R-S es diferente a S-R, S-R seria el conjunto de elementos que existen en S pero no en R. En el lenguaje SQL este tipo de operación se puede realizar con la clausula EXCEPT\nSelección [σc(R)]\nEsta operación aplicada a una relacion R, produce una nueva relación con un subconjunto de tuplas de R. Este subconjunto de tuplas satisface y cumple cierta condición(c)\nProyección [Πa1,a2,\u0026hellip;,an(R)]\nEsta operación aplicada a una relación R, produce una nueva relación con solamente los atributos(columnas) especificados por a1,a2,\u0026hellip;,an\nProducto cartesiano [RxS]\nEl producto cartesiano de dos relaciones R y S es la relación que se obtiene de la combinación de todas las tuplas de R con todas las tuplas de S. Las tuplas de la relación que se obtiene están formadas por todos los atributos de R seguidos de todos los atributos de S. En el lenguaje SQL este tipo de operación se puede realizar con la cláusula CROSS JOIN ó separando las relaciones usadas en el producto con comas, en el FROM de la sentencia SQL.\nCombinaciones\nPor medio del operador combinación (JOIN) podemos combinar dos relaciones segun una condición para obtener tuplas compuestas por atributos de las dos relaciones combinadas.\nEn el lenguaje SQL existen diferentes maneras de combinar dos relaciones. A continuación teneis un resumen de las existentes en PostgreSQL:\nCombinaciones internas (R INNER JOIN S): Un INNER JOIN entre dos relaciones R y S, es el resultado que se obtiene despues de aplicar al producto cartesiano de las dos relaciones R y S, una condición para acotar dicho producto. Existen un par de casos especiales:\nDe equivalencia (Equi-join): Es un caso particular de INNER JOIN en el que la condicion que acota el resultado es una comparación de igualdad. R NATURAL JOIN S: Es un caso especial de equi-join en el que en el caso de existir columnas con el mismo nombre en las relaciones que se combinan, solo se incluirá una de ellas en el resultado de la combinación. Combinaciones externas (OUTER JOINS): Un OUTER JOIN entre dos relaciones R y S contiene todas las tuplas que un INNER JOIN devolveria, más una serie de tuplas que no tienen atributos en común en las dos relaciones. Los diferentes tipos son:\nR LEFT OUTER JOIN S: Un LEFT OUTER JOIN entre dos relaciones R y S, retorna todas las tuplas de la combinación que tengan un atributo común, más todas las tuplas de la relación de la izquierda (R) que no tengan un equivalente en la relación de la derecha (S). R RIGHT OUTER JOIN S: Un RIGHT OUTER JOIN entre dos relaciones R y S, retorna todas las tuplas de la combinación que tengan un atributo común, más todas las tuplas de la relación de la derecha (S) que no tengan un equivalente en la relación de la izquierda (R). R FULL OUTER JOIN S: Un FULL OUTER JOIN entre dos relaciones R y S, retorna todas las tuplas de la combinación que tengan un atributo común, más todas las tuplas de la relación de la izquierda (R) que no tenga un equivalente en la relación de la derecha (S) y todas las tuplas de la relación de la derecha (S) que no tenga un equivalente en la relación de la izquierda (R). Renombre [ρa/b(R)]\nEsta operación aplicada a una relación R, produce una nueva relación identica a R en donde el atributo \u0026lsquo;b\u0026rsquo; ha sido renombrado a \u0026lsquo;a\u0026rsquo;.\nEl uso combinado de todas estas operaciones aplicado a nuestras relaciones dará lugar a consultas más ó menos complejas.\nUtilizando los operadores definidos en algebra relacional, podemos definir de manera gráfica (árboles) ó lineal la representación algebraica de nuestra consulta. En consultas muy complicadas es lo que se deberia de hacer antes de empezar a escribir el codigo SQL, pero este es un tema para otro artículo.\nEjemplos prácticos # Nada mejor que unos ejemplos básicos para ver como se aplica la teoria que hemos visto. Lo primero que vamos a hacer es definir un par de tablas que utilizaremos en nuestros ejemplos:\npostgres=# SELECT * FROM relacion_r; a | b | c ---+---+--- 1 | 2 | 3 4 | 5 | 6 (2 rows) postgres=# SELECT * FROM relacion_s; c | d | e ---+---+--- 4 | 5 | 6 7 | 8 | 9 (2 rows) En nuestro primer ejemplo realizamos una unión de las dos relaciones, el resultado obtenido seria:\npostgres=# SELECT * FROM relacion_r UNION SELECT * FROM relacion_s; a | b | c ---+---+--- 4 | 5 | 6 1 | 2 | 3 7 | 8 | 9 (3 rows) A continuación realizamos una intersección entre las dos relaciones:\npostgres=# SELECT * FROM relacion_r INTERSECT SELECT * FROM relacion_s; a | b | c ---+---+--- 4 | 5 | 6 (1 row) La diferencia entre estas dos relaciones daria el siguiente resultado. Como podeis ver, y por definición, no es lo mismo la diferencia entre relacion_r y relacion_s, que entre relacion_s y relacion_r\npostgres=# SELECT * FROM relacion_r EXCEPT SELECT * FROM relacion_s; a | b | c ---+---+--- 1 | 2 | 3 (1 row) postgres=# SELECT * FROM relacion_s EXCEPT SELECT * FROM relacion_r; c | d | e ---+---+--- 7 | 8 | 9 (1 row) Vamos a definir una nueva fila (3,4,5) en la tabla relacion_s para ver unos ejemplos de como combinar estas dos relaciones mediante JOINs.\npostgres=# SELECT * FROM relacion_r; a | b | c ---+---+--- 1 | 2 | 3 4 | 5 | 6 (2 rows) postgres=# SELECT * FROM relacion_s; c | d | e ---+---+--- 4 | 5 | 6 7 | 8 | 9 3 | 4 | 5 (3 rows) La manera más simple de combinar estas dos relaciones es realizar el producto cartesiano de ambas. Esto se puede realizar de dos maneras, ó bien definiendo las dos relaciones separadas por comas despues del FROM.\npostgres=# SELECT * FROM relacion_r,relacion_s; a | b | c | c | d | e ---+---+---+---+---+--- 1 | 2 | 3 | 4 | 5 | 6 1 | 2 | 3 | 7 | 8 | 9 1 | 2 | 3 | 3 | 4 | 5 4 | 5 | 6 | 4 | 5 | 6 4 | 5 | 6 | 7 | 8 | 9 4 | 5 | 6 | 3 | 4 | 5 (6 rows) O utilizando la cláusula CROSS JOIN entre las dos relaciones.\npostgres=# SELECT * FROM relacion_r CROSS JOIN relacion_s; a | b | c | c | d | e ---+---+---+---+---+--- 1 | 2 | 3 | 4 | 5 | 6 1 | 2 | 3 | 7 | 8 | 9 1 | 2 | 3 | 3 | 4 | 5 4 | 5 | 6 | 4 | 5 | 6 4 | 5 | 6 | 7 | 8 | 9 4 | 5 | 6 | 3 | 4 | 5 (6 rows) En realidad un CROSS JOIN es el equivalente a un INNER JOIN ON (true). Esto porque el INNER JOIN es por definición un producto cartesiano al que se le aplica una condición para acotar el resultado. En este caso particular la condición siempre se cumple para todas las tuplas al utilizar el valor TRUE, con lo que obtendremos todas las tuplas del producto cartesiano.\npostgres=# SELECT * FROM relacion_r INNER JOIN relacion_s ON (true); a | b | c | c | d | e ---+---+---+---+---+--- 1 | 2 | 3 | 4 | 5 | 6 1 | 2 | 3 | 7 | 8 | 9 1 | 2 | 3 | 3 | 4 | 5 4 | 5 | 6 | 4 | 5 | 6 4 | 5 | 6 | 7 | 8 | 9 4 | 5 | 6 | 3 | 4 | 5 (6 rows) A continuación podeis ver como definir un INNER JOIN con la condición definida dentro de ON(). Este ejemplo es un Equi-join al utilizarse una comparación de igualdad en la condición.\npostgres=# SELECT * FROM relacion_r AS r INNER JOIN relacion_s AS s ON (r.c = s.c); a | b | c | c | d | e ---+---+---+---+---+--- 1 | 2 | 3 | 3 | 4 | 5 (1 rows) El mismo resultado obtenido con la clausula INNER JOIN se podria haber conseguido obteniendo el producto cartesiano de las dos relaciones y aplicando una condición con WHERE (definicion de INNER JOIN).\npostgres=# SELECT * FROM relacion_r AS r, relacion_s AS s WHERE r.c = s.c; a | b | c | c | d | e ---+---+---+---+---+--- 1 | 2 | 3 | 3 | 4 | 5 (1 rows) postgres=# SELECT * FROM relacion_r as r CROSS JOIN relacion_s as s WHERE r.c = s.c; a | b | c | c | d | e ---+---+---+---+---+--- 1 | 2 | 3 | 3 | 4 | 5 (1 row) Un NATURAL JOIN retorna el mismo resultado que un equi-join, pero sin repetir las columnas comunes.\npostgres=# SELECT * from relacion_r natural join relacion_s; c | a | b | d | e ---+---+---+---+--- 3 | 1 | 2 | 4 | 5 postgres=# SELECT a,b,c,d,e from relacion_r natural join relacion_s; a | b | c | d | e ---+---+---+---+--- 1 | 2 | 3 | 4 | 5 (1 row) Como podeis ver en lo que llevamos de artículo, existen diferentes maneras de obtener un mismo resultado utilizando diferentes tipos de consultas. Teniendo los conceptos claros y con práctica, os aseguro que todos estos tipos de consultas os saldrán de forma natural despues de un tiempo.\nA continuación vamos a ver las combinaciones de tipo OUTER JOIN. En PostgreSQL el uso de la palabra OUTER es opcional, a mi me gusta utilizarla aunque no se necesite.\nLa primera es un LEFT OUTER JOIN.\npostgres=# SELECT * FROM relacion_r AS r LEFT OUTER JOIN relacion_s AS s ON (r.c = s.c); a | b | c | c | d | e ---+---+---+---+---+--- 1 | 2 | 3 | 3 | 4 | 5 4 | 5 | 6 | | | (2 rows) Seguido de un RIGHT OUTER JOIN:\npostgres=# SELECT * FROM relacion_r AS r RIGHT OUTER JOIN relacion_s AS s ON (r.c = s.c); a | b | c | c | d | e ---+---+---+---+---+--- 1 | 2 | 3 | 3 | 4 | 5 | | | 4 | 5 | 6 | | | 7 | 8 | 9 (3 rows) Y para terminar un FULL OUTER JOIN:\npostgres=# SELECT * FROM relacion_r AS r FULL OUTER JOIN relacion_s AS s ON (r.c = s.c); a | b | c | c | d | e ---+---+---+---+---+--- 1 | 2 | 3 | 3 | 4 | 5 | | | 4 | 5 | 6 4 | 5 | 6 | | | | | | 7 | 8 | 9 (4 rows) Un ejemplo casi real # Los ejemplos que hemos visto hasta ahora, nos han mostrado la sintaxis básica de las operaciones que se pueden utilizar para obtener resultados con datos de múltiples relaciones. En la vida real nos encontraremos con casos muchos más complicados en los que tendremos que combinar todas estas operaciones junto con el resto de operadores y cláusulas SQL disponibles.\nEn casos complicados es importante pensar antes de empezar a escribir nuestra consulta SQL. A continuación vamos a ver un ejemplo un poco más complicado para ver como podemos desglosar y resolver la consulta que necesitamos para obtener el resultado deseado.\nUtilizaremos unas tablas creadas únicamente para este ejemplo y no representativas de un sistema real. Tenemos una tabla principal llamada \u0026lsquo;PC\u0026rsquo;, con diferentes columnas conteniendo cadenas de identificación de los diferentes componentes usados para el ensamblado de diferentes modelos de PCs. Las columnas vacias de la tabla \u0026lsquo;PC\u0026rsquo; significan componentes no presentes en dichos modelos. El resto de tablas contienen informacion adicional sobre los diferentes componentes usados para construir un PC.\npostgres=# SELECT * FROM pc; pcid | memoria | cpu | disco | tgrafica | precio ------+---------+---------+-----------+-----------+-------- 1 | mem0001 | cpu0001 | disco0001 | ati001 | 1000 2 | mem0001 | cpu0001 | disco0002 | ati001 | 1100 3 | mem0002 | cpu0002 | disco0003 | nvidia001 | 1400 4 | mem0004 | cpu0003 | disco0004 | nvidia001 | 1600 5 | | cpu0001 | disco0001 | ati001 | 900 6 | | | | ati001 | 400 (6 rows) postgres=# SELECT * FROM cpu ; cpu_id | cpu_fabricante | cpu_tipo ---------+----------------+------------ cpu0001 | intel | Core2 duo cpu0002 | intel | Core2 Quad cpu0003 | amd | Athlon X2 (3 rows) postgres=# SELECT * FROM memoria ; mem_id | mem_capacidad | mem_tipo ---------+---------------+------------ mem0001 | 1024 | DDR SDRAM mem0002 | 1024 | DDR2 SDRAM mem0003 | 1024 | DDR3 SDRAM mem0004 | 2048 | DDR3 SDRAM (4 rows) postgres=# SELECT * FROM disco ; disco_id | disco_fabricante | disco_capacidad -----------+------------------+----------------- disco0001 | seagate | 350 disco0002 | seagate | 500 disco0003 | seagate | 1024 disco0004 | samsung | 500 (4 rows) postgres=# SELECT * FROM tgrafica ; tgraf_id | tgraf_fabricante -----------+------------------ ati001 | ati nvidia001 | nvidia Utilizando estas tablas vamos a realizar varias consultas:\n*Consulta 1\nObtener una relación de solo los modelos de PC \u0026lsquo;completos\u0026rsquo; a la venta. Queremos toda la información disponible sobre los componentes que lo forman. Ordenar el resultado de mayor a menor precio.\nSabemos que al tener que coger datos de diferentes tablas, necesitaremos algun tipo de clausula JOIN. En esta consulta nos piden solamente, PCs completos, con todos sus componentes. Por ello descartamos todas las combinaciones de tipo OUTER JOIN y utilizamos INNER JOIN para obtener solamente las tuplas con atributos en todas las relaciones combinadas.\nCada PC tiene 4 componentes y la información de cada componente se encuentra en una tabla separada. Con estos datos sabemos que tendremos que realizar 4 INNER JOIN entre todas las tablas involucradas en la consulta.\nVamos a empezar a escribir la consulta SQL. Primero realizamos los INNER JOIN y declaramos las condiciones que acotarán el resultado. Tendremos que emparejar los atributos de componentes presentes en la tabla \u0026lsquo;PC\u0026rsquo; con los correspondientes atributos en el resto de tablas de componentes.\nSELECT * FROM pc AS a INNER JOIN memoria AS b ON (a.memoria = b.mem_id) INNER JOIN cpu AS c ON (a.cpu = c.cpu_id) INNER JOIN disco AS d ON (a.disco = d.disco_id) INNER JOIN tgrafica AS e ON (a.tgrafica = e.tgraf_id); Una vez que hemos combinado todas las tablas, vamos a definir los atributos que queremos presentar en nuestro resultado. Utilizaremos los prefijos de tablas definidos en la consulta anterior (a,b,c,d,e), para acceder a los atributos de cada tabla:\nSELECT a.pcid, b.mem_tipo, b.mem_capacidad AS mem_MB, c.cpu_fabricante AS cpu_fab, c.cpu_tipo, d.disco_fabricante AS disco_fab, d.disco_capacidad AS disco_GB, e.tgraf_fabricante AS tgraf_fab, a.precio FROM pc AS a INNER JOIN memoria AS b ON (a.memoria = b.mem_id) INNER JOIN cpu AS c ON (a.cpu = c.cpu_id) INNER JOIN disco AS d ON (a.disco = d.disco_id) INNER JOIN tgrafica AS e ON (a.tgrafica = e.tgraf_id); Y para terminar ordenamos el resultado:\nSELECT a.pcid, b.mem_tipo, b.mem_capacidad AS mem_MB, c.cpu_fabricante AS cpu_fab, c.cpu_tipo, d.disco_fabricante AS disco_fab, d.disco_capacidad AS disco_GB, e.tgraf_fabricante AS tgraf_fab, a.precio FROM pc AS a INNER JOIN memoria AS b ON (a.memoria = b.mem_id) INNER JOIN cpu AS c ON (a.cpu = c.cpu_id) INNER JOIN disco AS d ON (a.disco = d.disco_id) INNER JOIN tgrafica AS e ON (a.tgrafica = e.tgraf_id) ORDER BY precio DESC; El resultado de nuestra consulta presentará las características de solo los PC completos: pcid | mem_tipo | mem_mb | cpu_fab | cpu_tipo | disco_fab | disco_gb | tgraf_fab | precio ------+------------+--------+---------+------------+-----------+----------+-----------+-------- 4 | DDR3 SDRAM | 2048 | amd | Athlon X2 | samsung | 500 | nvidia | 1600 3 | DDR2 SDRAM | 1024 | intel | Core2 Quad | seagate | 1024 | nvidia | 1400 2 | DDR SDRAM | 1024 | intel | Core2 duo | seagate | 500 | ati | 1100 1 | DDR SDRAM | 1024 | intel | Core2 duo | seagate | 350 | ati | 1000 (4 rows) *Consulta 2\nObtener una relación de todos los modelos de PC a la venta. Queremos toda la informacion disponible sobre los componentes que lo forman. Ordenar el resultado de mayor a menor precio.\nEn esta consulta nos piden lo mismo que la consulta 1 pero de todos los modelos de PC, los completos y los que se venden sin algun componente. La tabla PC es la tabla principal de nuestra combinación, y la tabla a la que le faltan valores en ciertos atributos en algunas tuplas. Esta tabla es la primera que se define cuando definimos las cláusulas JOIN y por definición es la que se encuentra más a la izquierda. Por ello utilizaremos el tipo LEFT OUTER JOIN para conseguir el resultado de la consulta 1, mas todos los PC a los que le falta algún componente.\nLo único que tenemos que hacer es cambiar INNER JOIN por LEFT OUTER JOIN. La consulta quedaria asi:\nSELECT a.pcid, b.mem_tipo, b.mem_capacidad AS mem_MB, c.cpu_fabricante AS cpu_fab, c.cpu_tipo, d.disco_fabricante AS disco_fab, d.disco_capacidad AS disco_GB, e.tgraf_fabricante AS tgraf_fab, a.precio FROM pc AS a LEFT OUTER JOIN memoria AS b ON (a.memoria = b.mem_id) LEFT OUTER JOIN cpu AS c ON (a.cpu = c.cpu_id) LEFT OUTER JOIN disco AS d ON (a.disco = d.disco_id) LEFT OUTER JOIN tgrafica AS e ON (a.tgrafica = e.tgraf_id) ORDER BY precio DESC; Y el resultado de nuestra consulta presentará las características de todos los PC:\npcid | mem_tipo | mem_mb | cpu_fab | cpu_tipo | disco_fab | disco_gb | tgraf_fab | precio ------+------------+--------+---------+------------+-----------+----------+-----------+-------- 4 | DDR3 SDRAM | 2048 | amd | Athlon X2 | samsung | 500 | nvidia | 1600 3 | DDR2 SDRAM | 1024 | intel | Core2 Quad | seagate | 1024 | nvidia | 1400 2 | DDR SDRAM | 1024 | intel | Core2 duo | seagate | 500 | ati | 1100 1 | DDR SDRAM | 1024 | intel | Core2 duo | seagate | 350 | ati | 1000 5 | | | intel | Core2 duo | seagate | 350 | ati | 900 6 | | | | | | | ati | 400 (6 rows) Consulta 3\nObtener una relación de solo los modelos de \u0026lsquo;PC NO completos\u0026rsquo; a la venta. Queremos toda la información disponible sobre los componentes que lo forman\nSi os fijais esta consulta la podriamos obtener si al resultado que muestra todos los PCs, le \u0026lsquo;restamos\u0026rsquo; el resultado con solo los PCs completos. Esto lo podriamos realizar combinando la consulta 1 con la consulta 2 mediante el operador EXCEPT (consulta 2 EXCEPT consulta 1):\n( SELECT a.pcid, b.mem_tipo, b.mem_capacidad AS mem_MB, c.cpu_fabricante AS cpu_fab, c.cpu_tipo, d.disco_fabricante AS disco_fab, d.disco_capacidad AS disco_GB, e.tgraf_fabricante AS tgraf_fab, a.precio FROM pc AS a LEFT OUTER JOIN memoria AS b ON (a.memoria = b.mem_id) LEFT OUTER JOIN cpu AS c ON (a.cpu = c.cpu_id) LEFT OUTER JOIN disco AS d ON (a.disco = d.disco_id) LEFT OUTER JOIN tgrafica AS e ON (a.tgrafica = e.tgraf_id) ) EXCEPT ( SELECT a.pcid, b.mem_tipo, b.mem_capacidad AS mem_MB, c.cpu_fabricante AS cpu_fab, c.cpu_tipo, d.disco_fabricante AS disco_fab, d.disco_capacidad AS disco_GB, e.tgraf_fabricante AS tgraf_fab, a.precio FROM pc AS a INNER JOIN memoria AS b ON (a.memoria = b.mem_id) INNER JOIN cpu AS c ON (a.cpu = c.cpu_id) INNER JOIN disco AS d ON (a.disco = d.disco_id) INNER JOIN tgrafica AS e ON (a.tgrafica = e.tgraf_id) ) ORDER BY precio DESC; El resultado seria el esperado:\npcid | mem_tipo | mem_mb | cpu_fab | cpu_tipo | disco_fab | disco_gb | tgraf_fab | precio ------+----------+--------+---------+-----------+-----------+----------+-----------+-------- 5 | | | intel | Core2 duo | seagate | 350 | ati | 900 6 | | | | | | | ati | 400 (2 rows) Consulta 4\nObtener una relación de todos los PC que tengan CPUs de AMD y discos Samsung. Queremos toda la información disponible sobre los componentes que lo forman\nEl trabajo para definir esta consulta está casi hecho. Lo único que tenemos que hacer es aplicar mediante la sentencia WHERE, las dos condiciones que nos piden, a la consulta 2:\nSELECT a.pcid, b.mem_tipo, b.mem_capacidad AS mem_MB, c.cpu_fabricante AS cpu_fab, c.cpu_tipo, d.disco_fabricante AS disco_fab, d.disco_capacidad AS disco_GB, e.tgraf_fabricante AS tgraf_fab, a.precio FROM pc AS a LEFT OUTER JOIN memoria AS b ON (a.memoria = b.mem_id) LEFT OUTER JOIN cpu AS c ON (a.cpu = c.cpu_id) LEFT OUTER JOIN disco AS d ON (a.disco = d.disco_id) LEFT OUTER JOIN tgrafica AS e ON (a.tgrafica = e.tgraf_id) WHERE c.cpu_fabricante = \u0026#39;amd\u0026#39; AND d.disco_fabricante = \u0026#39;samsung\u0026#39; ORDER BY precio DESC; Y el resultado quedaria asi:\npcid | mem_tipo | mem_mb | cpu_fab | cpu_tipo | disco_fab | disco_gb | tgraf_fab | precio ------+------------+--------+---------+-----------+-----------+----------+-----------+-------- 4 | DDR3 SDRAM | 2048 | amd | Athlon X2 | samsung | 500 | nvidia | 1600 (1 row) Consulta 5\nObtener una relación del numero de PCs que tienen CPUs de Intel y de AMD. Ordenar de mayor a menor.\nEsta consulta es un poco diferente a las anteriores. Aquí tenemos que agrupar los resultados según el fabricante de la CPU utilizada, y contar cuantas tuplas existen en cada grupo. Al necesitar solamente información contenida en la tabla \u0026lsquo;CPU\u0026rsquo;, solo tendremos que combinar la tabla \u0026lsquo;PC\u0026rsquo; con la tabla \u0026lsquo;CPU\u0026rsquo;.\nComo queremos obtener solamente los PC que tienen algun tipo de CPU, utilizaremos un INNER JOIN para descartar los PCs sin CPU definida.\nSELECT * FROM pc AS a INNER JOIN cpu AS b ON (a.cpu = b.cpu_id); A continuación vamos a agrupar el resultado obtenido segun el fabricante de la CPU y contaremos el número de tuplas en cada grupo:\nSELECT b.cpu_fabricante, count(*) AS total FROM pc AS a INNER JOIN cpu AS b ON (a.cpu = b.cpu_id) GROUP BY b.cpu_fabricante; Y ordenamos el resultado:\nSELECT b.cpu_fabricante, count(*) AS total FROM pc AS a INNER JOIN cpu AS b ON (a.cpu = b.cpu_id) GROUP BY b.cpu_fabricante ORDER BY total DESC; El resultado obtenido seria:\ncpu_fabricante | total ----------------+------- intel | 4 amd | 1 (2 rows) En fin, espero que os hagais una idea de como funciona el tema de combinar diferentes tablas para obtener un resultado. En la vida real os encontrareis con ejemplos bastantes complicados, lo importante es pensar la estrategia a seguir antes de empezar a escribir la consulta SQL. También es importante hacerlo paso a paso, aplicando las restricciones necesarias hasta conseguir lo que queremos. Solamente la práctica os ayudará a entender y escribir con soltura consultas complejas.\nEnlaces:\nhttps://www.postgresql.org/docs/current/static/sql-select.html http://es.wikipedia.org/wiki/Normalizacion_de_bases_de_datos http://es.wikipedia.org/wiki/Algebra_relacional ","date":"2009/09/21","externalUrl":null,"permalink":"/es/blog/consultas-complejas/","section":"Blogs","summary":"Uno de los temas que más cuesta a los que empiezan a aprender SQL son las consultas en las que se recogen diferentes tipos de datos de una ó múltiples tablas. Este artículo es una introducción a como definir consultas de este tipo en PostgreSQL.\nUnos conocimientos básicos de normalización de datos y un poco de álgebra relacional no vienen mal para entender mejor algunos de los términos que vamos a usar en este artículo. La normalización de datos es tema para otro artículo, pero en este veremos brevemente algunos conceptos de álgebra relacional que nos pueden ayudar a entender mejor el tema que estamos tratando.\n","title":"Consultas complejas","type":"blog"},{"content":"Este artículo está basado e inspirado en el tutorial titulado \u0026ldquo;Performance Whack-a-Mole II\u0026rdquo; que Josh Berkus dio en Ottawa durante la conferencia PGCon2009.\nSi estais administrando pequeños sistemas sin muchos datos ó usuarios, probablemente nunca tendreis que pensar en muchos de los temas que se tratan en este artículo. Pero si teneis ó vais a tener a vuestro cargo sistemas más complejos, os vendrá bien la lectura de lo que se trata aquí. Aunque el artículo está centrado en las bases de datos PostgreSQL, mucha de la información contenida en el mismo es perféctamente válida para sistemas que usen otras bases de datos.\nCualquier administrador de bases de datos ha oido, al menos una vez durante su vida profesional, la siguiente afirmación, \u0026ldquo;la base de datos va muy lenta\u0026rdquo;. Cuando os llegue este momento tendreis que analizar vuestro sistema e intentar identificar cuales son las causas de los problemas de rendimiento que teneis. En este artículo vamos a tratar de explicar técnicas y procedimientos generales que nos pueden ayudar a identificar y aislar los posibles problemas que pueden afectar a el rendimiento global de los sistemas con los que trabajemos.\nPara empezar podemos citar algunas reglas generales que suelen cumplirse cuando tenemos problemas de rendimiento en nuestro sistema:\nTu sistema tendrá el rendimiento del peor de sus componentes La mayoría de los problemas de rendimiento no suelen estar en la base de datos. Menos del 10% de los problemas de rendimiento de tu sistema son los causantes de una reducción del rendimiento de aproximadamente el 90%. En todo momento y normalmente, solo es posible observar e identificar el problema de rendimiento más grande del momento. Diferentes tipos de aplicaciones tienen diferentes problemas característicos y modos de arreglarlos. A continuación vamos a dar una introducción a los diferentes tipos de componentes y diferentes tipos de sistemas con los que nos podemos encontrar, así como las técnicas, procedimientos y herramientas que podemos usar para intentar identificar problemas de rendimiento.\nComponentes de nuestro sistema # Nuestro sistema está formado por numerosos componentes y todos y cada uno de estos componentes influyen en mayor ó en menor medida en el rendimiento de nuestro sistema.\nA continuación teneis un gráfico que ilustra los posibles diferentes componentes de nuestro sistema de forma jerárquica.\nGeneralmente, cuando tengamos un problema de rendimiento empezaremos la búsqueda de la causa en la capa inferior (hardware) e iremos subiendo progresivamente a la siguiente capa superior hasta localizar el problema.\nTipos de aplicaciones # Desde el punto de vista de un administrador de bases de datos, existen a grandes rasgos tres tipos de aplicaciones:\nAplicaciones web (Web) Aplicaciones de procesamiento de transacciones en linea (OLTP) Almacenes de datos (data warehouse - DW) A continuación teneis las características generales diferenciadoras más importantes de estos tipos de aplicaciones:\nAplicaciones web (Web)\nBase de datos más pequeña que el total de RAM El 90% ó más de las consultas, son consultas simples Limitada por la CPU Los problemas típicos que se suelen dar son: cache, pooling, tiempos de conexión Aplicaciones de procesamiento de transacciones en linea (OLTP)\nBase de datos ligeramente más grande que el total de RAM y hasta 1 TB El 20%-40% de las consultas son pequeñas consultas que actualizan datos Unas pocas transacciones grandes y consultas de datos complejas Limitada por CPU ó I/O Los problemas típicos son: bloqueos (locks), cache, transacciones, velocidad de escritura, registros (logs) Almacenes de datos (data warehouse - DW)\nBase de datos muy grande (de 100GB a 100TB) Consultas grandes y complicadas para generar informes Actualización de datos en grandes bloques (bulk load) Limitada por I/O ó RAM Los problemas típicos son: escaneos sequenciales (seq scans), recursos, consultas inefectivas, actualizaciones masivas (bulk load) Técnicas y procedimientos # Una vez que hemos visto los posibles componentes que pueden formar parte de nuestro sistema y los diferentes tipos de aplicaciones con los que nos podemos encontrar, vamos a pasar a ver procedimientos y técnicas que podemos utilizar para identificar y arreglar problemas de rendimiento en nuestro sistema.\nA continuación teneis una estrategia que podeis seguir para localizar problemas:\nRecolección de información:\nEsta es una de las fases más importantes y cruciales, en ella tenemos que intentar entender que está haciendo el sistema que vamos a arreglar, que componentes se están utilizando y como estos componentes funcionan e interactuan entre si. Tendremos que identificar de que tipo de aplicación se trata, que intenta hacer la aplicación, como utiliza la base de datos, que tipo de problemas están teniendo los usuarios, etc. En definitiva, obtener una vision general de como el sistema hace las cosas e intentar identificar posibles areas problemáticas del mismo.\nComprobación general de la configuración básica del sistema:\nEn esta fase realizaremos un recorrido general sobre la configuración del sistema para comprobar en que estado se encuentra. Comprobaremos la configuración del hardware y el sistema operativo, de PostgreSQL, middleware ,si existe, y de la aplicacion/es usando el sistema.\nIdentificación de posibles problemas:\nEn esta fase empezaremos a analizar los diferentes componentes del sistema para tratar de identificar posibles problemas que afecten al rendimiento. Probablemente ya hayamos identificado varios problemas durante el punto 1) y 2)\nArreglo del mayor problema identificado:\nUna vez identificado el mayor problema, tendremos que buscar la solución del mismo.\nRepetición:\nCuando el primer problema más importante esté arreglado volver a repetir los puntos 3) y 4) hasta que estemos contentos con el resultado.\nVamos a ver que tipo de información deberiamos de recolectar y que deberiamos comprobar en los diferentes componentes del sistema que tengan problemas. Como ya hemos dicho, generalmente empezaremos con el hardware e iremos subiendo progresivamente a las capas superiores, aunque esto dependerá del sistema y algunas veces cambiaremos el orden.\nA continuación teneis información sobre los componentes y los puntos más comunes que se deberian comprobar:\nHardware # Servidores\nModelo de CPU, velocidad de la CPU, numero de CPUs disponibles, arquitectura, uso de las mismas Cantidad de RAM, velocidad de la RAM, configuración de la misma ¿Estamos usando servidores dedicados para la base de datos y para los demas componentes? Almacenamiento\nTipos de interfaces (controladores, RAID) Tipos de discos, tamaño y velocidad de los mismos Configuración del Array/SAN ¿Estamos usando una configuración RAID adecuada? ¿Estamos usando cache de escritura (segura/con baterias)? ¿Estamos usando todos los dispositivos disponibles (discos, canales)? Red\nTipo de red y capacidad de la misma Tarjetas de red usadas, modelos Configuración de los conmutadores y enrutadores (switch/router) ¿Estamos usando una red dedicada entre la aplicación y la base de datos? ¿Estamos usando conexiones redundantes y balanceo del tráfico de red? Sistema operativo # Sistema operativo\nTipo de SO, versión, parches aplicados, modificaciones locales Información sobre los controladores de hardware usados ¿Estamos utilizando la última versión del kernel y parches disponibles? ¿Estamos usando la última versión de los controladores de hardware? ¿Qué recursos estamos usando?¿Estamos usando servidores dedicados? ¿Tenemos otras aplicaciones usando los recursos del servidor? Sistema de ficheros\nTipo de sistema de ficheros que estamos utilizando Localización de los ficheros del sistema operativo, PostgreSQL y otras aplicaciones Configuración del sistema de fichero utilizado ¿Está xlog en un disco dedicado? ¿Estamos utilizando una configuración no estandard del sistema de fichero usado? PostgreSQL # Schema\nDiseño, modelo de datos Tamaño de los datos Particionado de los datos, tablespaces Indices usados Procedimientos almacenados en uso Configuración\n¿Qué cambios se han realizado en la configuración estandard? ¿Se ejecutan trabajos de mantenimiento (VACUUM/ANALYZE)? ¿Con que frecuencia y configuración se ejecutan los trabajos de mantenimiento? Comprobar los parametros más importantes, unos valores orientativos que se podrian usar serian : - shared_buffers = 25-30% RAM - work_men = [1] 512k, [2] 2MB, [3] 128MB (nunca mas de RAM/num.conexiones) - maintenance_work_mem = 1/16 RAM - checkpoints_segments = [1] 8, [2][3] 16-64 - wal_buffers = [1] 1MB, [2][3] 8MB - effective_cache_size = 2/3 RAM - random_page_cost = 2.0 - autovacuum = on [1][2] - vacuum_cost_delay = 20ms [1][2] - vacuum/analyze despues de inicializar/actualizar una gran cantidad de datos. [1] Aplicacion Web [2] Tipica app. OLTP [3] Tipica app. datawarehouse Middelware # Controladores,Conexiones,Cache\nTipo y versión usada de los controladores DB Método de conexión usada, pooling usado, configuración del pooling Método de cache usado, herramientas usadas para administrarlo, versión y configuración Software ORM utilizado y versión ¿Estamos utilizando las ultimas versiones de los controladores, cache, etc? ¿Estamos utilizando pooling y cache? ¿Usamos consultas preparadas? Aplicación # Consultas SQL,Transacciones\nTipo de aplicación Modelo y tipo de transacciones Tipos y numeros de consultas ¿Cómo funciona la aplicación, como se usa, tiene un tráfico constante ó momentos con cargas máximas? ¿Estamos usando procedimientos almacenados? Herramientas # Existen numerosas herramientas que se pueden utilizar para intentar localizar diferentes problemas en nuestro sistema. Dependiendo de la parte del sistema que estemos analizando usaremos unas u otras.\nNo vamos a profundizar en como se utilizan estas herramientas en este artículo, esto lo dejamos para otros artículos mas especializados. A continuación teneis una relación de herramientas que podriamos utilizar con las diferentes partes del sistema. Hardware y sistema operativo\nHerramientas del sistema operativo # Estas herramientas suelen ser muy fáciles de usar, no suelen afectar al resto del sistema mientras que las utilizamos y nos pueden ayudar a monitorizar y obtener estadísticas de como esta funcionando el hardware y nuestro sistema en general.\nps: Nos permite ver los procesos postgreSQL que se están ejecutando en nuestro servidor. Nos da una idea de la cantidad de procesos concurrentes y el uso de CPU y memoria que tienen. Tambien nos permite identificar consultas que se han colgado, que estan bloqueadas ó que llevan ejecutandose mucho tiempo. pg_top: Este programa nos suministra mucha de la información que ps nos da y otra relacionada exclusivamente con postgreSQL. mpstat: Nos proporciona información sobre el uso de todas las CPU de nuestro sistema. Podemos encontrar los recursos de CPU que se están usando, si estamos usando todas las CPU disponibles ó si tenemos problemas de \u0026lsquo;cambio de contexto\u0026rsquo;(context-switch) vmstat, free: Nos permite ver el uso que estamos haciendo de la memoria. Podemos ver si la memoria está saturada, si podemos tener más información en cache ó si estamos utilizando el swap de nuestro sistema. iostat: Para monitorizar el uso de los dispositivos de almacenamiento de nuestro sistema. Podemos ver si el subsistema I/O está saturado, si alguno de los dispositivos está causando un embotellamiento del resto de los dispositivos, ó si tenemos subidas puntuales del tráfico en los discos debido a los \u0026lsquo;checkpoint\u0026rsquo; generados por PostgreSQL. sar: Se puede utilizar para obtener información de la actividad del sistema durante un periodo de tiempo definido. Test de rendimientos # Los test de rendimientos suelen ser muy intrusivos y suelen afectar al resto del sistema mientras que se ejecutan. Por esto, no siempre se pueden usar en sistemas en producción que están funcionando con ciertos problemas pero que tienen que seguir funcionando aunque su rendimiento no sea el óptimo. Permiten comparar resultados entre sistemas con mismo hardware y sistema operativo.\ndd: Lee y escribe datos de manera sequencial bonnie++: Para comprobar el rendimiento y posibles problemas con I/O. Comprueba entre otras cosas, la velocidad de busqueda (seek) y escritura aleatoria del sistema. IOzone: Comprueba las velocidades de diferentes operaciones. PostgreSQL # Views y funciones de sistema\nMuy fáciles de usar, su uso no afecta al rendimiento de nuestro sistema. Nos proporcionan información sobre lo que está ocurriendo internamente en nuestra base de datos y nos pueden ayudar a identificar problemas relacionados con el diseño/modelo de datos, consultas, procedimientos almacenados y bloqueos.\npg_stat_database, pg_database_size(): Para conseguir estadísticas de tráfico generales, números de conexiones, número de transacciones válidas (commits) y abortadas (rollback), proporción de aciertos de datos en cache, tamaño de la base de datos. pg_tables, pg_relation_size(): Para obtener información sobre las tablas de nuestra base de datos, cuantas existen, si tienen disparadores, índices ó reglas asociadas, y el tamaño de las tablas e índices de la base de datos pg_stat_activity: Para comprobar la actividad actual en la base de datos, consultas en ejecución, bloquedas, conexiones concurrentes, ociosas (idle), etc pg_locks: Para descubrir y comprobar conflictos con bloqueos. pg_stat[io]_user_tables, pg_stat[io]_user_indexes: Para comprobar la actividad relacionada con las tablas y los índices de nuestra base de datos, informacion sobre \u0026lsquo;seq scans\u0026rsquo;, I/O, etc pg_stat_bgwriter: Para comprobar estadísticas relacionadas con el proceso \u0026lsquo;background writer\u0026rsquo; encargado de los ficheros WAL. pg_stat_user_functions (8.4): Para obtener los tiempos de ejecución de nuestras funciones, diferenciando entre tiempos de llamada y ejecución del código. pg_stat_statements (contrib 8.4): Este módulo \u0026lsquo;contrib\u0026rsquo; se puede utilizar para obtener una relación de consultas en ejecución junto con información sobre las más frecuentes y las más lentas PostgreSQL logs, pgfouine y auto-explain\nNo hay nada como un buen fichero log para obtener información sobre lo que ha ocurrido en nuestro sistema.\npg_log: Existen multitud de opciones en el fichero de configuración postgresql.conf para activar/desactivar los datos que nos interese registrar. La información obtenida mediante el log de postgreSQL se podrá analizar para obtener información en el caso de tener problemas. pgfouine: Se puede utilizar para calcular estadísticas generales de las consultas enviadas a la base de datos. Las consultas más frecuentes y más lentas no serán un misterio despues de utilizar este producto. auto-explain (8.4): Registra en el fichero log de postgreSQL la información sobre \u0026rsquo;explain plans\u0026rsquo; de las consultas que queramos. Se puede activar/desactivar dinámicamente. Explain analyze\nEl comando SQL \u0026rsquo;explain analyze [consulta SQL]\u0026rsquo; se puede utilizar con consultas lentas para obtener información sobre las causas de la falta de velocidad. Muchas veces podremos arreglar la causa rapidamente y otras nos dará información sobre posibles causas a investigar.\nEl resultado de este comando es un arbol invertido de nodos el cual deberiamos de empezar a leer de abajo hacia arriba. Habria que empezar a buscar el nodo más bajo con problemas e intentar interpretar el resultado de manera integral/global, ya que algunos nodos se pueden ejecutar en paralelo e influenciarse mútuamente.\nAlgunas de las cosas que se deberian de comprobar y buscar en el resultado del comando \u0026lsquo;Explain analyze\u0026rsquo; son:\nEstimaciones erroneas del número de filas en una tabla \u0026lsquo;Index scans\u0026rsquo; y \u0026lsquo;seq scans\u0026rsquo; lentos Ordenación de datos en disco en vez de en memoria Test de rendimientos\npgbench: Test de rendimiento muy simple con el que se puede comprobar el subsistema I/O y la velocidad a la que se procesan las conexiones. No comprueba bloqueos, computación de datos ó \u0026lsquo;query planning\u0026rsquo;. Util para demostrar problemas importantes de hardware ó en el sistema operativo. DBT2: Test muy completo y costoso de ejecutar. Basado en TPCC es un completo test de rendimiento para sistemas OLTP. DBT3: Nuevo test OLTP + DW Otros: pgUnitTest, EAstress, BenchmarkSQL Middelware / aplicación # Existen también una serie de herramientas que se pueden utilizar a nivel del middelware/aplicación que nos pueden ayudar a obtener información valiosa que nos ayude a localizar nuestro problema. Enumeramos solo algunas de las más importantes:\nHerramientas para servidores de aplicaciones: Se pueden usar para analizar los tiempos de respuesta, monitorización de la actividad de la base de datos, y monitorización del uso del cache. Simulacion de cargas: Herramientas como lwp y reproductores de información contenida en ficheros log. Herramientas para detectar bugs: Valgrind, MDB, GDB Problemas de rendimiento típicos # A continuación teneis una visión general sobre los problemas de rendimiento más comunes.\nProblemas con el subsistema I/O # Cuando tenemos problemas con el subsistema I/O, lo más normal es que las CPUs estén subutilizadas, tengamos memoria disponible y al menos un dispositivo de almacenamiento con el I/O saturado.\nEstos problemas suelen darse en sistemas del tipo OTLP y DW (data warehouse), bases de datos muy grandes ó bases de datos con un ratio de escritura muy alto.\nLas causas comunes de este tipo de problemas son:\nProblemas con el hardware/software encargado del subsistema I/O Mala configuración del subsistema I/O No suficiente memoria Demasiados datos demandados por la aplicación Schema no óptima, falta de índices ó particionamiento de datos Problemas con CPU # Cuando tenemos problemas con el uso de la CPU, lo más normal es que estemos usando el 90% ó más de la capacidad de CPU disponible, tengamos memoria disponible y el subsistema I/O noe esté saturado.\nEstos problemas suelen darse en sistemas del tipo Web y OTLP, bases de datos con una gran cantidad de consultas de lectura ó consultas en las que se realizan cálculos complejos.\nLas causas comunes de este tipo de problemas son:\nDemasiadas consultas Insuficiente cache/pooling Demasiados datos demandados por la aplicación Consultas mal construidas Schema no óptima, falta de índices Problemas de bloqueos # Cuando tenemos este tipo de problema, lo más normal es que ni la base de datos, ni la aplicacion esten trabajando al máximo, pero tengamos muchas consultas con grandes periodos de espera, un alto índice de \u0026lsquo;cambio de contexto\u0026rsquo; (context switching), y pg_locks mostrando consultas bloqueadas.\nEstos problemas suelen darse en sistemas del tipo OTLP y DW (data warehouse), ó trabajos que usen bloqueos pesimistas ó procedimientos almacenados.\nLas causas comunes de este tipo de problemas son:\nEjecuciones de larga duracion de transacciones ó procedimientos almacenados Cursores mantenidos durante mucho tiempo Bloqueos pesimistas en vez de optimistas ó bloqueos definidos por los usuarios Gestión mala de transacciones Varias configuraciones de buffers en postgresql.conf con valores muy bajos Límites en la escalabilidad de PostgreSQL en sistemas SMP Problemas con la aplicación # Cuando tenemos este tipo de problema, lo más normal es que la base de datos no esté trabajando al máximo, pero el uso de la memoria y/o la CPU en el servidor de aplicación estén al máximo.\nEstos problemas suelen darse en sistemas J2EE\nLas causas comunes de este tipo de problemas son:\nNo tenemos suficientes servidores de aplicaciones Demasiados datos/consultas demandados por la aplicación Mala configuración del cache/pooling Problemas con los controladores DB Más información # Bueno esto es todo en este artículo, la experiencia es vuestra mejor aliada y con el tiempo os será cada vez más fácil identificar los posibles problemas de rendimiento de vuestro sistema.\nTeneis la presentación completa con algunos ejemplos practicos y el video de la misma, en ingles, en esta dirección: http://www.pgcon.org/2009/schedule/track/Tutorial/188.en.html\n","date":"2009/08/27","externalUrl":null,"permalink":"/es/blog/identificando-problemas-de-rendimiento/","section":"Blogs","summary":"Si estais administrando pequeños sistemas sin muchos datos ó usuarios, probablemente nunca tendreis que pensar en muchos de los temas que se tratan en este artículo. Pero si teneis ó vais a tener a vuestro cargo sistemas más complejos, os vendrá bien la lectura de lo que se trata aquí. Aunque el artículo está centrado en las bases de datos PostgreSQL, mucha de la información contenida en el mismo es perféctamente válida para sistemas que usen otras bases de datos.","title":"Identificando problemas de rendimiento","type":"blog"},{"content":"Una de las funcionalidades disponibles en PostgreSQL son los denominados disparadores (triggers). En este artículo vamos a introducirnos en el mundo de los disparadores, como funcionan y como podemos empezar a utilizarlos.\nUn disparador no es otra cosa que una acción definida en una tabla de nuestra base de datos y ejecutada automáticamente por una función programada por nosotros. Esta acción se activará, segun la definamos, cuando realicemos un INSERT, un UPDATE ó un DELETE en la susodicha tabla.\nUn disparador se puede definir de las siguientes maneras:\nPara que ocurra ANTES de cualquier INSERT,UPDATE ó DELETE Para que ocurra DESPUES de cualquier INSERT,UPDATE ó DELETE Para que se ejecute una sola vez por comando SQL (statement-level trigger) Para que se ejecute por cada linea afectada por un comando SQL (row-level trigger) Esta es la definición del comando SQL que se puede utilizar para definir un disparador en una tabla.\nCREATE TRIGGER nombre { BEFORE | AFTER } { INSERT | UPDATE | DELETE [ OR ... ] } ON tabla [ FOR [ EACH ] { ROW | STATEMENT } ] EXECUTE PROCEDURE nombre de funcion ( argumentos ) Antes de definir el disparador tendremos que definir el procedimiento almacenado que se ejecutará cuando nuestro disparador se active.\nEl procedimiento almacenado usado por nuestro disparador se puede programar en cualquiera de los lenguajes de procedimientos disponibles, entre ellos, el proporcionado por defecto cuando se instala PostgreSQL, PL/pgSQL. Este lenguaje es el que utilizaremos en todos los ejemplos de este árticulo. Podeis encontrar mas información sobre procedimientos almacenados en el artículo \u0026ldquo;Procedimientos almacenados y PL/pgSQL\u0026rdquo;\nCaracterísticas y reglas a seguir # A continuación teneis algunas de las características y reglas más importantes a tener en cuenta, cuando definamos un disparador y/ó programemos un procedimiento almacenado que se vaya a utilizar por un disparador:\nEl procedimiento almacenado que se vaya a utilizar por el disparador debe de definirse e instalarse antes de definir el propio disparador.\nUn procedimiento que se vaya a utilizar por un disparador no puede tener argumentos y tiene que devolver el tipo \u0026ldquo;trigger\u0026rdquo;.\nUn mismo procedimiento almacenado se puede utilizar por múltiples disparadores en diferentes tablas.\nProcedimientos almacenados utilizados por disparadores que se ejecutan una sola vez per comando SQL (statement-level) tienen que devolver siempre NULL.\nProcedimientos almacenados utilizados por disparadores que se ejecutan una vez per linea afectada por el comando SQL (row-level) pueden devolver una fila de tabla.\nProcedimientos almacenados utilizados por disparadores que se ejecutan una vez per fila afectada por el comando SQL (row-level) y ANTES de ejecutar el comando SQL que lo lanzó, pueden:\nRetornar NULL para saltarse la operación en la fila afectada. Ó devolver una fila de tabla (RECORD) Procedimientos almacenados utilizados por disparadores que se ejecutan DESPUES de ejecutar el comando SQL que lo lanzó, ignoran el valor de retorno, asi que pueden retornar NULL sin problemas.\nEn resumen, independendientemente de como se defina un disparador, el procedimiento almacenado utilizado por dicho disparador tiene que devolver ó bien NULL, ó bien un valor RECORD con la misma estructura que la tabla que lanzó dicho disparador.\nSi una tabla tiene más de un disparador definido para un mismo evento (INSERT,UPDATE,DELETE), estos se ejecutarán en orden alfabético por el nombre del disparador. En el caso de disparadores del tipo ANTES / row-level, la file retornada por cada disparador, se convierte en la entrada del siguiente. Si alguno de ellos retorna NULL, la operación será anulada para la fila afectada.\nProcedimientos almacenados utilizados por disparadores pueden ejecutar sentencias SQL que a su vez pueden activar otros disparadores. Esto se conoce como disparadores en cascada. No existe límite para el número de disparadores que se pueden llamar pero es responsabilidad del programador el evitar una recursión infinita de llamadas en la que un disparador se llame asi mismo de manera recursiva.\nOtra cosa que tenemos que tener en cuenta es que, por cada disparador que definamos en una tabla, nuestra base de datos tendrá que ejecutar la función asociada a dicho disparador. El uso de disparadores de manera incorrecta ó inefectiva puede afectar significativamente al rendimiento de nuestra base de datos. Los principiantes deberian de usar un tiempo para entender como funcionan y asi poder hacer un uso correcto de los mismos antes de usarlos en sistemas en producción. Variables especiales en PL/pgSQL\nCuando una función escrita en PL/pgSQL es llamada por un disparador tenemos ciertas variable especiales disponibles en dicha función. Estas variables son las siguientes:\nNEW\nTipo de dato RECORD; Variable que contiene la nueva fila de la tabla para las operaciones INSERT/UPDATE en disparadores del tipo row-level. Esta variable es NULL en disparadores del tipo statement-level.\nOLD\nTipo de dato RECORD; Variable que contiene la antigua fila de la tabla para las operaciones UPDATE/DELETE en disparadores del tipo row-level. Esta variable es NULL en disparadores del tipo statement-level.\nTG_NAME\nTipo de dato name; variable que contiene el nombre del disparador que está usando la función actualmente.\nTG_WHEN\nTipo de dato text; una cadena de texto con el valor BEFORE o AFTER dependiendo de como el disparador que está usando la función actualmente ha sido definido\nTG_LEVEL\nTipo de dato text; una cadena de texto con el valor ROW o STATEMENT dependiendo de como el disparador que está usando la función actualmente ha sido definido\nTG_OP\nTipo de dato text; una cadena de texto con el valor INSERT, UPDATE o DELETE dependiendo de la operación que ha activado el disparador que está usando la función actualmente.\nTG_RELID\nTipo de dato oid; el identificador de objeto de la tabla que ha activado el disparador que está usando la función actualmente.\nTG_RELNAME\nTipo de dato name; el nombre de la tabla que ha activado el disparador que está usando la función actualmente. Esta variable es obsoleta y puede desaparacer en el futuro. Usar TG_TABLE_NAME.\nTG_TABLE_NAME\nTipo de dato name; el nombre de la tabla que ha activado el disparador que está usando la función actualmente.\nTG_TABLE_SCHEMA\nTipo de dato name; el nombre de la schema de la tabla que ha activado el disparador que está usando la función actualmente.\nTG_NARGS\nTipo de dato integer; el número de argumentos dados al procedimiento en la sentencia CREATE TRIGGER.\nTG_ARGV[]\nTipo de dato text array; los argumentos de la sentencia CREATE TRIGGER. El índice empieza a contar desde 0. Indices inválidos (menores que 0 ó mayores/iguales que tg_nargs) resultan en valores nulos.\nEjemplos prácticos # Una vez que hemos visto la teoria básica de disparadores nada mejor que unos cuantos ejemplos prácticos para ver como se usan y defininen los disparadores en PostgreSQL. (estos ejemplos han sido comprobados en postgreSQL 8.3.7).\nCreamos una base de datos para utilizarla con nuestros ejemplos:\npostgres@server:~$ psql Welcome to psql 8.3.7, the PostgreSQL interactive terminal. Type: \\copyright for distribution terms \\h for help with SQL commands \\? for help with psql commands \\g or terminate with semicolon to execute query \\q to quit postgres=# CREATE DATABASE test001; CREATE DATABASE postgres=# \\c test001 You are now connected to database \u0026#34;test001\u0026#34;. test001=# Lo prímero que tenemos que hacer es instalar el lenguaje plpgsql si no lo tenemos instalado.\nCREATE PROCEDURAL LANGUAGE plpgsql; Ahora creamos una tabla para poder definir nuestro primer disparador:\nCREATE TABLE numeros( numero bigint NOT NULL, cuadrado bigint, cubo bigint, raiz2 real, raiz3 real, PRIMARY KEY (numero) ); Después tenemos que crear una función en PL/pgSQL para ser usada por nuestro disparador. Nuestra primera función es la más simple que se puede definir y lo único que hará será devolver el valor NULL:\nCREATE OR REPLACE FUNCTION proteger_datos() RETURNS TRIGGER AS $proteger_datos$ DECLARE BEGIN -- -- Esta funcion es usada para proteger datos en un tabla -- No se permitira el borrado de filas si la usamos -- en un disparador de tipo BEFORE / row-level -- RETURN NULL; END; $proteger_datos$ LANGUAGE plpgsql; A continuación definimos en la tabla numeros un disparador del tipo BEFORE / row-level para la operación DELETE. Más adelante veremos como funciona:\nCREATE TRIGGER proteger_datos BEFORE DELETE ON numeros FOR EACH ROW EXECUTE PROCEDURE proteger_datos(); La definición de nuestra tabla ha quedado asi:\ntest001=# \\d numeros Table \u0026#34;public.numeros\u0026#34; Column | Type | Modifiers ----------+--------+----------- numero | bigint | not null cuadrado | bigint | cubo | bigint | raiz2 | real | raiz3 | real | Indexes: \u0026#34;numeros_pkey\u0026#34; PRIMARY KEY, btree (numero) Triggers: proteger_datos BEFORE DELETE ON numeros FOR EACH ROW EXECUTE PROCEDURE proteger_datos() Ahora vamos a definir una nueva función un poco más complicada y un nuevo disparador en nuestra tabla numeros:\nCREATE OR REPLACE FUNCTION rellenar_datos() RETURNS TRIGGER AS $rellenar_datos$ DECLARE BEGIN NEW.cuadrado := power(NEW.numero,2); NEW.cubo := power(NEW.numero,3); NEW.raiz2 := sqrt(NEW.numero); NEW.raiz3 := cbrt(NEW.numero); RETURN NEW; END; $rellenar_datos$ LANGUAGE plpgsql; CREATE TRIGGER rellenar_datos BEFORE INSERT OR UPDATE ON numeros FOR EACH ROW EXECUTE PROCEDURE rellenar_datos(); La definición de nuestra tabla ha quedado asi:\ntest001=# \\d numeros Table \u0026#34;public.numeros\u0026#34; Column | Type | Modifiers ----------+--------+----------- numero | bigint | not null cuadrado | bigint | cubo | bigint | raiz2 | real | raiz3 | real | Indexes: \u0026#34;numeros_pkey\u0026#34; PRIMARY KEY, btree (numero) Triggers: proteger_datos BEFORE DELETE ON numeros FOR EACH ROW EXECUTE PROCEDURE proteger_datos() rellenar_datos BEFORE INSERT OR UPDATE ON numeros FOR EACH ROW EXECUTE PROCEDURE rellenar_datos() Ahora vamos a ver como los disparadores que hemos definido en la tabla numeros funcionan:\ntest001=# SELECT * from numeros; numero | cuadrado | cubo | raiz2 | raiz3 --------+----------+------+-------+------- (0 rows) test001=# INSERT INTO numeros (numero) VALUES (2); INSERT 0 1 test001=# SELECT * from numeros; numero | cuadrado | cubo | raiz2 | raiz3 --------+----------+------+---------+--------- 2 | 4 | 8 | 1.41421 | 1.25992 (1 rows) test001=# INSERT INTO numeros (numero) VALUES (3); INSERT 0 1 test001=# SELECT * from numeros; numero | cuadrado | cubo | raiz2 | raiz3 --------+----------+------+---------+--------- 2 | 4 | 8 | 1.41421 | 1.25992 3 | 9 | 27 | 1.73205 | 1.44225 (2 rows) test001=# UPDATE numeros SET numero = 4 WHERE numero = 3; UPDATE 1 test001=# SELECT * from numeros; numero | cuadrado | cubo | raiz2 | raiz3 --------+----------+------+---------+--------- 2 | 4 | 8 | 1.41421 | 1.25992 4 | 16 | 64 | 2 | 1.5874 (2 rows) Hemos realizado 2 INSERT y 1 UPDATE. Esto significa que por cada uno de estos comandos el sistema ha ejecutado la función rellenar_datos(), una vez por cada fila afectada y antes de actualizar la tabla numeros.\nComo podeis comprobar, nosotros solamente hemos actualizado la columna numero, pero al listar el contenido de nuestra tabla vemos como el resto de columnas (cuadrado, cubo, raiz2 y raiz3) tambien contienen valores.\nDe esta actualización se ha encargado la función rellenar_datos() llamada por nuestro disparador. Vamos a analizar lo que hace esta función:\nNEW.cuadrado := power(NEW.numero,2); NEW.cubo := power(NEW.numero,3); NEW.raiz2 := sqrt(NEW.numero); NEW.raiz3 := cbrt(NEW.numero); RETURN NEW; Cuando ejecutamos el primer INSERT (numero = 2), el disparador rellenar_datos llama a la función rellenar_datos() una vez.\nEl valor de la variable NEW al empezar a ejecutarse rellenar_datos() es numero=2, cuadrado=NULL, cubo=NULL, raiz2=NULL, raiz3=NULL.\nNuestra tabla todavia no contiene ninguna fila.\nA continuación calculamos el cuadrado, el cubo, la raiz cuadrada y la raiz cubica de 2 y asignamos estos valores a NEW.cuadrado, NEW.cubo, NEW.raiz2 y NEW.raiz3.\nEl valor de la variable NEW antes de la sentencia RETURN NEW es ahora numero=2, cuadrado=4, cubo=8, raiz2=1.41421, raiz3=1.25992.\nCon la sentencia RETURN NEW, retornamos la fila (RECORD) almacenada en la variable NEW, y salimos de la función rellenar_datos(). El sistema almacena entonces el RECORD contenido en NEW en la tabla numeros\nComo podeis ver, todo muy lógico.\nDe la misma manera funciona el disparador proteger_datos cuando ejecutamos una sentencia DELETE. Antes de borrar nada ejecutará la función proteger_datos().\nEsta función retorna el valor NULL y esto significa, segun la regla 6.1 definida en este artículo, que para la fila afectada no se ejecutará el comanado DELETE. Por eso y mientras este disparador este instalado será imposible de borrar nada de la tabla numeros.\ntest001=# DELETE FROM numeros; DELETE 0 test001=# SELECT * from numeros; numero | cuadrado | cubo | raiz2 | raiz3 --------+----------+------+---------+--------- 2 | 4 | 8 | 1.41421 | 1.25992 4 | 16 | 64 | 2 | 1.5874 (2 rows) Vamos a continuar complicando las cosas. Primero, vamos a desinstalar nuestros dos disparadores proteger_datos y rellenar_datos.\ntest001=# DROP TRIGGER proteger_datos ON numeros; DROP TRIGGER test001=# DROP TRIGGER rellenar_datos ON numeros; DROP TRIGGER A continuación crearemos un disparador único para las sentencias INSERT, UPDATE y DELETE. Este nuevo disparador utilizará una nueva función en la que tendremos que tener en cuenta que tipo de comando ha activado el disparador, si queremos retornar el valor correcto. Para ello utilizaremos la variable TG_OP.\nCREATE OR REPLACE FUNCTION proteger_y_rellenar_datos() RETURNS TRIGGER AS $proteger_y_rellenar_datos$ DECLARE BEGIN IF (TG_OP = \u0026#39;INSERT\u0026#39; OR TG_OP = \u0026#39;UPDATE\u0026#39; ) THEN NEW.cuadrado := power(NEW.numero,2); NEW.cubo := power(NEW.numero,3); NEW.raiz2 := sqrt(NEW.numero); NEW.raiz3 := cbrt(NEW.numero); RETURN NEW; ELSEIF (TG_OP = \u0026#39;DELETE\u0026#39;) THEN RETURN NULL; END IF; END; $proteger_y_rellenar_datos$ LANGUAGE plpgsql; CREATE TRIGGER proteger_y_rellenar_datos BEFORE INSERT OR UPDATE OR DELETE ON numeros FOR EACH ROW EXECUTE PROCEDURE proteger_y_rellenar_datos(); La definición de nuestra tabla ha quedado asi:\ntest001=# \\d numeros Table \u0026#34;public.numeros\u0026#34; Column | Type | Modifiers ----------+--------+----------- numero | bigint | not null cuadrado | bigint | cubo | bigint | raiz2 | real | raiz3 | real | Indexes: \u0026#34;numeros_pkey\u0026#34; PRIMARY KEY, btree (numero) Triggers: rellenar_datos BEFORE INSERT OR DELETE OR UPDATE ON numeros FOR EACH ROW EXECUTE PROCEDURE proteger_y_rellenar_datos() Y todo seguirá funcionando de la misma manera que con los dos disparadores del comienzo:\ntest001=# SELECT * from numeros; numero | cuadrado | cubo | raiz2 | raiz3 --------+----------+------+---------+--------- 2 | 4 | 8 | 1.41421 | 1.25992 4 | 16 | 64 | 2 | 1.5874 (2 rows) test001=# INSERT INTO numeros (numero) VALUES (5); INSERT 0 1 test001=# INSERT INTO numeros (numero) VALUES (6); INSERT 0 1 test001=# SELECT * from numeros; numero | cuadrado | cubo | raiz2 | raiz3 --------+----------+------+---------+--------- 2 | 4 | 8 | 1.41421 | 1.25992 4 | 16 | 64 | 2 | 1.5874 5 | 25 | 125 | 2.23607 | 1.70998 6 | 36 | 216 | 2.44949 | 1.81712 (4 rows) test001=# UPDATE numeros SET numero = 10 WHERE numero = 6; UPDATE 1 test001=# SELECT * from numeros ; numero | cuadrado | cubo | raiz2 | raiz3 --------+----------+------+---------+--------- 2 | 4 | 8 | 1.41421 | 1.25992 4 | 16 | 64 | 2 | 1.5874 5 | 25 | 125 | 2.23607 | 1.70998 10 | 100 | 1000 | 3.16228 | 2.15443 (4 rows) test001=# DELETE FROM numeros where numero =10; DELETE 0 test001=# SELECT * from numeros; numero | cuadrado | cubo | raiz2 | raiz3 --------+----------+------+---------+--------- 2 | 4 | 8 | 1.41421 | 1.25992 4 | 16 | 64 | 2 | 1.5874 5 | 25 | 125 | 2.23607 | 1.70998 10 | 100 | 1000 | 3.16228 | 2.15443 (4 rows) Por último y antes de terminar, vamos a definir un disparador del tipo statement-level que se ejecute despues de nuestras sentencias INSERT, UPDATE y DELETE. La función ejecutada por este disparador grabará datos de la ejecución en la tabla cambios (esto no sirve para mucho en la vida real, pero como ejemplo esta bien para que veais como funciona)\nPara demostrar como podemos utilizar esto vamos a definir una nueva tabla:\nCREATE TABLE cambios( timestamp_ TIMESTAMP WITH TIME ZONE default NOW(), nombre_disparador text, tipo_disparador text, nivel_disparador text, comando text ); La función la podemos definir asi:\nCREATE OR REPLACE FUNCTION grabar_operaciones() RETURNS TRIGGER AS $grabar_operaciones$ DECLARE BEGIN INSERT INTO cambios ( nombre_disparador, tipo_disparador, nivel_disparador, comando) VALUES ( TG_NAME, TG_WHEN, TG_LEVEL, TG_OP ); RETURN NULL; END; $grabar_operaciones$ LANGUAGE plpgsql; Y el disparador lo instalariamos de la siguiente forma:\nCREATE TRIGGER grabar_operaciones AFTER INSERT OR UPDATE OR DELETE ON numeros FOR EACH STATEMENT EXECUTE PROCEDURE grabar_operaciones(); La definición de nuestra tabla quedaria asi:\ntest001=# \\d numeros; Table \u0026#34;public.numeros\u0026#34; Column | Type | Modifiers ----------+--------+----------- numero | bigint | not null cuadrado | bigint | cubo | bigint | raiz2 | real | raiz3 | real | Indexes: \u0026#34;numeros_pkey\u0026#34; PRIMARY KEY, btree (numero) Triggers: grabar_operaciones AFTER INSERT OR DELETE OR UPDATE ON numeros FOR EACH STATEMENT EXECUTE PROCEDURE grabar_operaciones() proteger_y_rellenar_datos BEFORE INSERT OR DELETE OR UPDATE ON numeros FOR EACH ROW EXECUTE PROCEDURE proteger_y_rellenar_datos() A continuación podeis ver como funcionaria:\ntest001=# SELECT * from cambios ; timestamp_ | nombre_disparador | tipo_disparador | nivel_disparador | comando ------------+-------------------+-----------------+------------------+--------- (0 rows) test001=# INSERT INTO numeros (numero) VALUES (100); INSERT 0 1 test001=# SELECT * from numeros ; numero | cuadrado | cubo | raiz2 | raiz3 --------+----------+---------+---------+--------- 2 | 4 | 8 | 1.41421 | 1.25992 4 | 16 | 64 | 2 | 1.5874 5 | 25 | 125 | 2.23607 | 1.70998 10 | 100 | 1000 | 3.16228 | 2.15443 100 | 10000 | 1000000 | 10 | 4.64159 (5 rows) test001=# SELECT * from cambios ; timestamp_ | nombre_disparador | tipo_disparador | nivel_disparador | comando -------------------------------+--------------------+-----------------+------------------+--------- 2009-06-11 23:05:29.794534+02 | grabar_operaciones | AFTER | STATEMENT | INSERT (1 row) test001=# UPDATE numeros SET numero = 1000 WHERE numero = 100; UPDATE 1 test001=# SELECT * from numeros ; numero | cuadrado | cubo | raiz2 | raiz3 --------+----------+------------+---------+--------- 2 | 4 | 8 | 1.41421 | 1.25992 4 | 16 | 64 | 2 | 1.5874 5 | 25 | 125 | 2.23607 | 1.70998 10 | 100 | 1000 | 3.16228 | 2.15443 1000 | 1000000 | 1000000000 | 31.6228 | 10 (5 rows) test001=# SELECT * from cambios ; timestamp_ | nombre_disparador | tipo_disparador | nivel_disparador | comando -------------------------------+--------------------+-----------------+------------------+--------- 2009-06-11 23:05:29.794534+02 | grabar_operaciones | AFTER | STATEMENT | INSERT 2009-06-11 23:06:08.259421+02 | grabar_operaciones | AFTER | STATEMENT | UPDATE (2 rows) test001=# DELETE FROM numeros where numero =1000; DELETE 0 test001=# SELECT * from numeros ; numero | cuadrado | cubo | raiz2 | raiz3 --------+----------+------------+---------+--------- 2 | 4 | 8 | 1.41421 | 1.25992 4 | 16 | 64 | 2 | 1.5874 5 | 25 | 125 | 2.23607 | 1.70998 10 | 100 | 1000 | 3.16228 | 2.15443 1000 | 1000000 | 1000000000 | 31.6228 | 10 (5 rows) test001=# SELECT * from cambios ; timestamp_ | nombre_disparador | tipo_disparador | nivel_disparador | comando -------------------------------+--------------------+-----------------+------------------+--------- 2009-06-11 23:05:29.794534+02 | grabar_operaciones | AFTER | STATEMENT | INSERT 2009-06-11 23:06:08.259421+02 | grabar_operaciones | AFTER | STATEMENT | UPDATE 2009-06-11 23:06:26.568632+02 | grabar_operaciones | AFTER | STATEMENT | DELETE (3 rows) Y con este último ejemplo, terminamos este artículo sobre disparadores en PostgreSQL. Solamente os queda practicar, leer la documentación y usar vuestra imaginación.\nEnlaces:\nhttp://www.postgresql.org/docs/current/interactive/triggers.html http://www.postgresql.org/docs/current/interactive/plpgsql-trigger.html http://www.postgresql.org/docs/current/interactive/sql-createtrigger.html http://www.postgresql.org/docs/current/interactive/plpgsql.html ","date":"2009/06/11","externalUrl":null,"permalink":"/es/blog/disparadores-triggers-en-postgresql/","section":"Blogs","summary":"Una de las funcionalidades disponibles en PostgreSQL son los denominados disparadores (triggers). En este artículo vamos a introducirnos en el mundo de los disparadores, como funcionan y como podemos empezar a utilizarlos.\nUn disparador no es otra cosa que una acción definida en una tabla de nuestra base de datos y ejecutada automáticamente por una función programada por nosotros. Esta acción se activará, segun la definamos, cuando realicemos un INSERT, un UPDATE ó un DELETE en la susodicha tabla.\n","title":"Disparadores (triggers) en PostgreSQL","type":"blog"},{"content":"En este artículo vamos a dar una introducción a los llamados procedimientos almacenados (stored procedures) en PostgreSQL. Un procedimiento almacenado se puede definir como un programa, procedimiento ó función, el cual está almacenado en la base de datos y listo para ser usado.\nEste artículo es una introducción a este tema, la documentación completa con todos los detalles e información necesaria está disponible en la documentación oficial de PostgreSQL, \u0026ldquo;Capítulo 37. Procedural Languages\u0026rdquo;\nExisten dos ventajas evidentes al utilizar procedimientos almacenados en nuestro sistema:\nLa ejecución del procedimiento ocurre en el servidor de bases de datos. Esto probablemente aumentará el rendimiento de nuestra aplicación al no tenerse que mandar datos entre el cliente y el servidor, y no tener que procesar resultados intermedios en el cliente para obtener el resultado final.\nAl tener la lógica de la aplicación implementada en la base de datos no tendremos que implentarla en los clientes, con el consiguiente ahorro de lineas de código redundante y complejidad.\nSi tenemos diferentes tipos de clientes implementados en diferentes sistemas ó lenguajes de programación y accediendo a la misma base de datos, no tendremos que programar la misma lógica en todos, al estar esta disponible en la base de datos. Tendremos una API a la lógica de la aplicación lista para usarse desde diferentes clientes\nUn procedimiento almacenado en PostgreSQL se puede escribir en multiples lenguajes de programación. En una instalación por defecto de PostgreSQL podremos tener disponibles los siguientes lenguajes: PL/pgSQL, PL/Perl, PL/Tcl y PL/Python.\nEl único lenguaje que está disponible automáticamente es PL/pgSQL. Para utilizar PL/Perl, PL/Tcl o PL/Python tendremos que haber configurado/compilado PostgreSQL con estos parámetros --with-perl --with-tcl --with-python.\nTambien existen muchos otros lenguajes disponibles como módulos adicionales, entre ellos, PL/Java, PL/PHP, PL/R, PL/Ruby, PL/Sheme y PL/sh, pero estos tienen que descargarse e instalarse por separado.\nSuponiendo que tenemos PostgreSQL instalado con los lenguajes que queremos utilizar tendremos que realizar dos operaciones para poder empezar a utilizar un procedimiento almacenado en nuestra base de datos:\n*Instalar, si no lo tenemos instalado, el lenguaje que vayamos a utilizar para programar nuestro procedimiento (solamente necesitamos hacer esto una sola vez por base de datos) *Programar nuestro procedimiento e instalarlo en la base de datos\nPL/pgSQL # En este artículo vamos a utilizar el lenguaje de procedimientos PL/pgSQL por ser el que tendremos seguro disponible. PL/pgSQL es muy parecido al lenguaje PL/SQL utilizado por Oracle y bajo mi punto de vista uno de los mejores lenguajes de procedimientos que podemos usar en PostgreSQL, es fácil de aprender, potente y siempre está disponible.\nLos objetivos de PL/pgSQL cuando se creo fueron:\nPoder ser usado para crear funciones y disparadores (triggers) Añadir estructuras de control al lenguaje SQL Poder realizar cálculos complejos Heredar todos los tipos, funciones y operadores definidos por el usuario Poder ser definido como un lenguaje \u0026ldquo;de confianza\u0026rdquo; Fácil de usar PL/pgSQL es un lenguaje estructurado en bloques. Como mínimo tendremos un bloque principal en nuestro procedimiento almacenado y dentro de este podremos tener subbloques. Un bloque se define de la siguiente manera (Todo entre los corchetes [] es opcional):\n[ \u0026lt;\u0026lt; etiqueta \u0026gt;\u0026gt; ] [ DECLARE declaraciones de variables ] BEGIN codigo END [ etiqueta ]; Podemos definir e instalar un procedimiento en PL/pgSQL de la siguiente manera:\nCREATE [ OR REPLACE ] FUNCTION nombre_funcion([ [ argmodo ] [ argnombre ] argtipo [, ...] ]) RETURNS tipo AS $$ [ DECLARE ] [ declaraciones de variables ] BEGIN codigo END; $$ LANGUAGE plpgsql | IMMUTABLE | STABLE | VOLATILE | CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT | [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER | COST execution_cost | ROWS result_rows | SET configuration_parameter { TO value | = value | FROM CURRENT } ; No os asusteis al ver esto, es más fácil de lo que parece y todas las opciones despues de \u0026ldquo;LANGUAGE plpgsql\u0026rdquo; tienen unos valores por defecto que simplifican mucho la definición de un procedimiento. La documentación completa está disponible en la sección CREATE FUNCTION de la documentación oficial.\nA continuación vamos a ver algunas de las opciones y valores más importantes.\nargmodo: El modo de un argumento puede ser IN, OUT, or INOUT. Por defecto se usa IN si no se define.\nargtipo: Los tipos que podemos utilizar son todos los disponibles en PostgreSQL y todos los definidos por el usuario\ndeclaraciones de variables: Las declaraciones de variables se pueden realizar de la siguiente manera ($n = orden de declaración del argumento.):\nnombre_variable ALIAS FOR $n; nombre_variable [ CONSTANT ] tipo [ NOT NULL ] [ { DEFAULT | := } expresion ]; código: en este artículo no tenemos espacio para ver como podemos escribir la parte de código de un procedimiento. Teneis que leer la sección de la documentación oficial de PostgreSQL que trata sobre el tema, \u0026ldquo;Capítulo 38. PL/pgSQL - SQL Procedural Language\u0026rdquo;, especialmente las secciones 38.3. Declarations, 38.5. Basic Statements y 38.6. Control Structures.\nIMMUTABLE | STABLE | VOLATILE:\nIMMUTABLE: Indica que la función no puede alterar a la base de datos y que siempre devolverá el mismo resultado, dados los mismos valores como argumentos. Este tipo de funciones no pueden realizar consultas en la base de datos.\nSTABLE: Indica que la función no puede alterar a la base de datos y que siempre devolverá el mismo resultado en una consulta individual de una tabla, dados los mismos valores como argumentos. El resultado podria cambiar entre sentencias SQL.\nVOLATILE: Indica que la función puede devolver diferentes valores, incluso dentro de una consulta individual de una tabla (valor por defecto)\nCALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT:\nCALLED ON NULL INPUT: Indica que la función se ejecutará aunque algunos de los argumentos sean NULL. El usuario tiene la responsabilidad de comprobar si algún argumento es NULL cuando sea necesario tener esto en cuenta.(valor por defecto)\nRETURNS NULL ON NULL INPUT / STRICT: Indican que la función no se ejecutará y devolverá el valor NULL si alguno de los argumentos es NULL.\nSECURITY INVOKER | SECURITY DEFINER:\nSECURITY INVOKER: Indica que la función se ejecutará con los privilegios del usuario que la ejecuta (valor por defecto)\nSECURITY DEFINER: Indica que la función se ejecutará con los privilegios del usuario que la creo.\nEl resto de opciones son avanzadas y podeis leer sobre ellas en la documentación oficial.\nAplicando la poca teoria que hemos visto, vamos a ver unos cuantos ejemplos que nos aclaren un poco como definir, instalar y usar un procedimiento almacenado en PL/pgSQL (estos ejemplos han sido comprobados en postgreSQL 8.3.7).\nCreamos una base de datos para utilizarla con nuestros ejemplos:\npostgres@server:~$ psql Welcome to psql 8.3.7, the PostgreSQL interactive terminal. Type: \\copyright for distribution terms \\h for help with SQL commands \\? for help with psql commands \\g or terminate with semicolon to execute query \\q to quit postgres=# CREATE DATABASE test001; CREATE DATABASE postgres=# \\c test001 You are now connected to database \u0026#34;test001\u0026#34;. test001=# Lo primero que tenemos que hacer es instalar el lenguaje plpgsql si no lo tenemos instalado.\nCREATE PROCEDURAL LANGUAGE plpgsql; Si queremos que cualquier usuario con acceso a la base de datos pueda usarlo sin tener que ser el administrador postgres, tendremos que utilizar TRUSTED con el comando anterior.\nCREATE TRUSTED PROCEDURAL LANGUAGE plpgsql; A continuación creamos nuestro primer procedimiento. (Podemos copiar y pegar en el cliente psql, escribirlo a mano ó usar el editor interno en psql (\\e)):\nCREATE OR REPLACE FUNCTION ejemplo() RETURNS integer AS $$ BEGIN RETURN 104; END; $$ LANGUAGE plpgsql; Este procedimiento se puede usar de la siguiente manera:\ntest001=# SELECT ejemplo(); ejemplo1 ---------- 104 (1 row) Ahora definimos la función con un argumento:\nCREATE OR REPLACE FUNCTION ejemplo(integer) RETURNS integer AS $$ BEGIN RETURN $1; END; $$ LANGUAGE plpgsql; Este procedimiento se podria haber escrito tambien de las siguientes maneras:\nCREATE OR REPLACE FUNCTION ejemplo(numero integer) RETURNS integer AS $$ BEGIN RETURN numero; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION ejemplo(integer) RETURNS integer AS $$ DECLARE numero ALIAS FOR $1; BEGIN RETURN numero; END; $$ LANGUAGE plpgsql; Este procedimiento se puede usar de la siguiente manera:\ntest001=# SELECT ejemplo(104); ejemplo --------- 104 (1 row) Vamos a empezar a complicar un poco las cosas usando dos argumentos y definiendo algunas variables:\nCREATE OR REPLACE FUNCTION ejemplo(integer, integer) RETURNS integer AS $$ DECLARE numero1 ALIAS FOR $1; numero2 ALIAS FOR $2; constante CONSTANT integer := 100; resultado integer; BEGIN resultado := (numero1 * numero2) + constante; RETURN resultado; END; $$ LANGUAGE plpgsql; Este procedimiento se puede usar de la siguiente manera:\ntest001=# SELECT ejemplo(2,2); ejemplo --------- 104 (1 row) A continuacion vamos a usar una sentencia IF \u0026hellip; THEN en nuestra función:\nCREATE OR REPLACE FUNCTION ejemplo_txt(integer, integer) RETURNS text AS $$ DECLARE numero1 ALIAS FOR $1; numero2 ALIAS FOR $2; constante CONSTANT integer := 100; resultado INTEGER; resultado_txt TEXT DEFAULT \u0026#39;El resultado es 104\u0026#39;; BEGIN resultado := (numero1 * numero2) + constante; IF resultado \u0026lt;\u0026gt; 104 THEN resultado_txt := \u0026#39;El resultado NO es 104\u0026#39;; END IF; RETURN resultado_txt; END; $$ LANGUAGE plpgsql; Este procedimiento se puede usar de la siguiente manera:\ntest001=# SELECT ejemplo_txt(2,2); ejemplo_txt --------------------- El resultado es 104 (1 row) test001=# SELECT ejemplo_txt(2,3); ejemplo_txt ------------------------ El resultado NO es 104 (1 row) Podriamos seguir modificando y complicando nuestro ejemplo, pero como introducción es suficiente para que os hagais una idea de como funciona.\nSolo queda decir que en la definición de un procedimiento no solo se tiene en cuenta el nombre del mismo para diferenciarlo de otros, los argumentos de la función tambien se tienen en cuenta. ejemplo(), ejemplo(integer), ejemplo(integer, integer) y ejemplo(text) son todos procedimientos diferentes, aunque se llamen igual.\nEn psql existe un comando muy bueno que nos enseña como una función está definida en la base de datos.\ntest001=# \\x Expanded display is on. test001=# \\df+ ejemplo List of functions -[ RECORD 1 ]-------+---------------------- Schema | public Name | ejemplo Result data type | integer Argument data types | Volatility | volatile Owner | postgres Language | plpgsql Source code | : BEGIN : RETURN 104; : END; : Description | -[ RECORD 2 ]-------+---------------------- Schema | public Name | ejemplo Result data type | integer Argument data types | integer Volatility | volatile Owner | postgres Language | plpgsql Source code | : DECLARE : numero ALIAS FOR $1; : : BEGIN : RETURN numero; : END; : Description | -[ RECORD 3 ]-------+----------------------------------------------- Schema | public Name | ejemplo Result data type | integer Argument data types | integer, integer Volatility | volatile Owner | postgres Language | plpgsql Source code | : DECLARE : numero1 ALIAS FOR $1; : numero2 ALIAS FOR $2; : : constante CONSTANT integer := 100; : resultado integer; : : BEGIN : resultado := (numero1 * numero2) + constante; : : RETURN resultado; : END; : Description | test001=# \\df+ ejemplo_txt List of functions -[ RECORD 1 ]-------+---------------------------------------------------- Schema | public Name | ejemplo_txt Result data type | text Argument data types | integer, integer Volatility | volatile Owner | postgres Language | plpgsql Source code | : DECLARE : numero1 ALIAS FOR $1; : numero2 ALIAS FOR $2; : : constante CONSTANT integer := 100; : resultado INTEGER; : : resultado_txt TEXT DEFAULT \u0026#39;El resultado es 104\u0026#39;; : : BEGIN : resultado := (numero1 * numero2) + constante; : : IF resultado \u0026lt;\u0026gt; 104 THEN : resultado_txt := \u0026#39;El resultado NO es 104\u0026#39;; : END IF; : : RETURN resultado_txt; : END; : Description | El resto es solo cuestión de imaginación y de leer la documentación detalladamente. Las posibilidades son infinitas y os aseguro que una vez que empeceis a usar procedimientos almacenados no podreis dejar de usarlos.\n","date":"2009/06/06","externalUrl":null,"permalink":"/es/blog/procedimientos-almacenados-y-plpgsql/","section":"Blogs","summary":"En este artículo vamos a dar una introducción a los llamados procedimientos almacenados (stored procedures) en PostgreSQL. Un procedimiento almacenado se puede definir como un programa, procedimiento ó función, el cual está almacenado en la base de datos y listo para ser usado.","title":"Procedimientos almacenados y PL/pgSQL","type":"blog"},{"content":"La integridad referencial es una funcionalidad disponible en las bases de datos relacionales que garantiza la coherencia de datos entre relaciones aparejadas.\nBajo mi punto de vista, es una de las características básicas y más importantes que una base de datos nos puede proporcionar y siempre se deberia de usar para garantizar la integridad de los datos.\nEs increible ver como muchisimos proyectos de sotfware libre que usan una base de datos, no usan para nada la integridad referencial disponible en la base de datos. Muchos de ellos intentan implementar cierta integridad en la aplicación. Esto es lo mismo que intentar inventar la rueda de nuevo y por supuesto está a la merced de posibles fallos de programación en la aplicación.\nLa integridad referencial se define con el uso combinado de claves primarias (primary keys) ó claves candidatas (candidate key) y clave foráneas (foreign key).\nLas claves primarias y candidatas están formadas por valores únicos y una clave foránea solamente puede estar asociada a una de estas para garantizar la existencia de un solo valor correcto. Las claves candidatas se pueden definir creando un índice único (CREATE UNIQUE INDEX \u0026hellip;.) en la columna pertinente.\nPara poder usar esta funcionalidad es importante tener nuestra base de datos normalizada para:\nEvitar la redundancia de los datos. Evitar problemas de actualización de los datos en las tablas. Proteger la integridad de los datos Nada mejor que un simple ejemplo para ver como podemos implementar la teoria con unos simples comandos.\nEn nuestro ejemplo tenemos dos tablas. Una de clientes, con dos atributos, un número identificador y un nombre. Y otra tabla para facturas con el número de factura y el número de cliente.\nSi no utilizaramos integridad referencial, que ocurriria si:\n¿Intentamos insertar una factura con un número de cliente que no existe? ¿Borramos un cliente que tiene una factura asignada? La respuesta es sencilla, las dos operaciones se podrian realizar sin problemas y tendriamos un sistema con unos datos en los que no podriamos confiar por la falta de consistencia de los mismos.\nEsto lo podemos arreglar con dos sencillas operaciones, creamos una clave primaria en el atributo ID de la tabla clientes y una clave foránea en el atributo CLIENTE de la tabla facturas.\nEsto lo podemos hacer cuando definamos la tabla ó con los siguientes comandos para la clave primaria:\nALTER TABLE clientes ADD CONSTRAINT cliente_pk PRIMARY KEY (id); Y para la foránea, por ejemplo:\nALTER TABLE facturas ADD CONSTRAINT clientes_id_fk FOREIGN KEY (cliente) REFERENCES clientes(id) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE; Esto seria suficiente para implementar integridad referencial en las dos tablas de nuestro ejemplo. Si intentamos hacer algo ilegal con nuestros datos, la base de datos nos lo prohibirá y nos dará un error.\npostgres=# SELECT * from clientes; id | nombre ----+----------- 1 | nombre 1 2 | nombre 2 (2 rows) postgres=# SELECT * from facturas; facnum | cliente --------+--------- 0001 | 1 0002 | 1 0003 | 2 (3 rows) postgres=# SELECT facturas.facnum, clientes.nombre AS cliente FROM clientes JOIN facturas ON (clientes.id = facturas.cliente) ORDER BY facnum; facnum | cliente --------+----------- 0001 | nombre 1 0002 | nombre 1 0003 | nombre 2 (3 rows) postgres=# INSERT INTO facturas (facnum,cliente) VALUES (\u0026#39;0004\u0026#39;,3); ERROR: insert or update on table \u0026#34;facturas\u0026#34; violates foreign key constraint \u0026#34;clientes_id_fk\u0026#34; DETAIL: Key (cliente)=(3) is not present in table \u0026#34;clientes\u0026#34;. postgres=# DELETE FROM clientes WHERE id = 1; ERROR: update or delete on table \u0026#34;clientes\u0026#34; violates foreign key constraint \u0026#34;clientes_id_fk\u0026#34; on table \u0026#34;facturas\u0026#34; DETAIL: Key (id)=(1) is still referenced from table \u0026#34;facturas\u0026#34;. postgres=# UPDATE clientes SET id = 3 where id = 1; UPDATE 1 postgres=# SELECT * from clientes; id | nombre ----+----------- 2 | nombre 2 3 | nombre 1 (2 rows) postgres=# SELECT * from facturas; facnum | cliente --------+--------- 0003 | 2 0001 | 3 0002 | 3 (3 rows) postgres=# SELECT facturas.facnum, clientes.nombre AS cliente FROM clientes JOIN facturas ON (clientes.id = facturas.cliente) ORDER BY facnum; facnum | cliente --------+----------- 0001 | nombre 1 0002 | nombre 1 0003 | nombre 2 (3 rows) Hay tres parametros cuando definimos una clave foránea que son muy importantes y que definen como la base de datos se va a comportar para salvaguardar la integridad de nuestros datos. Estos parametros son:\nMATCH tipo ON DELETE accion ON UPDATE accion En donde tipo puede tener estos valores:\nFULL: No permite que una columna tenga el valor NULL en una clave foránea compuesta por varias columnas SIMPLE: Permite que una columna tenga el valor NULL en una clave foránea compuesta por varias columnas Y accion puede tener estos valores:\nNO ACTION: Produce un error indicando que un DELETE ó UPDATE creará una violación de la clave foránea definida. RESTRICT: Produce un error indicando que un DELETE ó UPDATE creará una violación de la clave foránea definida. CASCADE: Borra ó actualiza automáticamente todas las referencias activas SET NULL: Define las referencias activas como NULL SET DEFAULT: Define las referencias activas como el valor por defecto (si está definido) de las mismas En nuestro ejemplo hemos definido que ninguna columna de nuestra clave foránea puede ser NULL, que no se pueda borrar una clave foránea con referencias activas y que en caso de actualizar el valor de una clave foránea, se actualicen tambien todas las referencias a la misma automáticamente.\nEsto es todo en esta introducción a la integridad referencial en nuestra base de datos.\nPara una información detallada y completa sobre el tema, consultar estos enlaces:\nhttp://www.postgresql.org/docs/current/interactive/ddl-constraints.html http://www.postgresql.org/docs/current/interactive/sql-createtable.html http://www.postgresql.org/docs/current/interactive/sql-altertable.html ","date":"2009/05/06","externalUrl":null,"permalink":"/es/blog/integridad-referencial-con-postgresql/","section":"Blogs","summary":"La integridad referencial es una funcionalidad disponible en las bases de datos relacionales que garantiza la coherencia de datos entre relaciones aparejadas.\nBajo mi punto de vista, es una de las características básicas y más importantes que una base de datos nos puede proporcionar y siempre se deberia de usar para garantizar la integridad de los datos.\n","title":"Integridad referencial con PostgreSQL","type":"blog"},{"content":"Un administrador de bases de datos no siempre tiene acceso ó conoce la aplicación que está accediendo a la base datos que administra. En muchos casos habrá que ayudar a los desarrolladores ó encargados de la aplicación a resolver los problemas que surjan.\nEn este artículo vamos a ver como identificar procesos, tanto en el servidor como en los clientes, que están accediendo a nuestra base datos. El saber identificar los procesos involucrados en una operación nos puede ayudar mucho en situaciones especiales en las que ciertas operaciones ó conexiones tengan ó sean causantes de problemas.\nVamos a dar un ejemplo de como hacer esto con un típico caso que os podeis encontrar en cualquier momento. Este caso es el de una transacción activa que haya bloqueado una parte de la base de datos y que por alguna causa ó problema en el cliente no anule el bloqueo y/ó no acabe la transacción. En este particular caso, todos los procesos que intenten acceder a los datos protegidos por el bloqueo, serán bloqueados y tendrán que esperar a su anulación antes de poder seguir trabajando. En la práctica, tendremos procesos que tardan mucho en terminar ó que nuncan terminan. Si son operaciones usuales y que se repitan a menudo la lista de procesos en espera puede crecer rápidamente.\nPara resolver esta situación tenemos que identificar los procesos involucrados lo más rápidamente posible. En nuestro ejemplo vamos a tener 2 procesos accediendo a la base de datos, uno de ellos ejecutará un SELECT FOR UPDATE en una tabla y el otro intentará modificar datos protegidos por el bloqueo creado por el otro proceso. En nuestro ejemplo estamos de suerte, solamente tendremos un proceso con una transacción abierta y otro esperando. En la realidad tendremos seguramente multiples procesos en esta situación.\nLos puntos a seguir serán:\nIntentar identificar via el sistema operativo los procesos postgres de interes en el servidor, en nuestro caso los que están bloqueando el acceso y los que estén esperando.\nPara ello podemos ejecutar este comando para obtener los posibles candidatos a estar bloqueando:\n[root@server]#\u0026gt; ps ax | grep \u0026#34;postgres:\u0026#34; | grep \u0026#34;idle in transaction\u0026#34; 21954 ? Ss 0:00 postgres: postgres test001 129.240.10.210(49417) idle in transaction Y este para los que estén esperando:\n[root@server]#\u0026gt; ps ax | grep \u0026#34;postgres:\u0026#34; | grep \u0026#34;waiting\u0026#34; 13836 ? Ss 0:00 postgres: postgres test001 129.240.10.210(49745) UPDATE waiting Una vez que tengamos información sobre los posibles procesos involucrados en el problema, usaremos PSQL para verificar y obtener mas información. Vamos a utilizar la información en los catalogos de sistema pg_stat_activity, pg_locks y pg_class.\nPrimero vamos a verificar la información contenida en pg_stat_activity sobre estos dos procesos, 21954 y 13836. La información la ordenaremos por el atributo query_start para tener los procesos ordenados cronológicamente.\n[server:5432/postgres@test001][]# \\x Expanded display is on. [server:5432/postgres@test001][]# SELECT datid, datname, procpid, usename, current_query, waiting, xact_start, query_start, client_addr, client_port FROM pg_stat_activity WHERE procpid IN (21954,13836) ORDER BY query_start; -[ RECORD 1 ]-+-------------------------------------------- datid | 16774 datname | test001 procpid | 21954 usename | postgres current_query | in transaction waiting | f xact_start | 2009-04-30 14:20:27.860236+02 query_start | 2009-04-30 14:20:41.300738+02 client_addr | 129.240.10.210 client_port | 49417 -[ RECORD 2 ]-+-------------------------------------------- datid | 16774 datname | test001 procpid | 13836 usename | postgres current_query | UPDATE tellers SET bid = 100 where tid = 1; waiting | t xact_start | 2009-04-30 14:21:34.370305+02 query_start | 2009-04-30 14:21:45.243395+02 client_addr | 129.240.10.210 client_port | 49745 Podemos confirmar que los dos procesos están accediendo la misma base de datos y que 21954 empezo antes que 13836. Los datos contenidos en client_addr y client_port nos servirán más tarde para averiguar que proceso en la maquina cliente 129.240.10.210 (49417) está usando el proceso 21954 en nuestro servidor.\nA continuación podemos obtener los datos que nos proporciona pg_locks para confirmar los bloqueos generados por estos dos procesos.\n[server:5432/postgres@test001][]# SELECT pg_class.relname, pg_locks.mode, substr(pg_stat_activity.current_query,1,30), age(now(),pg_stat_activity.query_start) as \u0026#34;age\u0026#34;, pg_stat_activity.procpid FROM pg_stat_activity,pg_locks LEFT OUTER JOIN pg_class ON (pg_locks.relation = pg_class.oid) WHERE pg_locks.pid=pg_stat_activity.procpid AND pg_stat_activity.datname = \u0026#39;test001\u0026#39; AND pg_stat_activity.procpid in (21954,13836) ORDER BY age DESC; relname | mode | substr | age | procpid --------------+------------------+--------------------------------+-----------------+--------- tellers_pkey | AccessShareLock | in transaction | 01:06:41.260819 | 21954 | ExclusiveLock | in transaction | 01:06:41.260819 | 21954 | ExclusiveLock | in transaction | 01:06:41.260819 | 21954 tellers | RowShareLock | in transaction | 01:06:41.260819 | 21954 | ShareLock | UPDATE tellers SET bid = 100 w | 01:05:37.318162 | 13836 | ExclusiveLock | UPDATE tellers SET bid = 100 w | 01:05:37.318162 | 13836 tellers | ExclusiveLock | UPDATE tellers SET bid = 100 w | 01:05:37.318162 | 13836 tellers_pkey | RowExclusiveLock | UPDATE tellers SET bid = 100 w | 01:05:37.318162 | 13836 | ExclusiveLock | UPDATE tellers SET bid = 100 w | 01:05:37.318162 | 13836 tellers | RowExclusiveLock | UPDATE tellers SET bid = 100 w | 01:05:37.318162 | 13836 (10 rows) En está información podemos corroborar que el primer proceso en la lista (21954) con una transaccion abierta y sin actividad tiene definido un bloqueo \u0026ldquo;RowShareLock\u0026rdquo; en la tabla \u0026ldquo;tellers\u0026rdquo; (este tipo de bloqueo es el usado cuando ejecutamos SELECT FOR UPDATE).\nEl tipo de bloqueo \u0026ldquo;RowShareLock\u0026rdquo;, bloquea entre otros, a cualquier operación que necesite un bloqueo del tipo \u0026ldquo;ExclusiveLock\u0026rdquo; (como el UPDATE de nuestro proceso 13836)\nUna vez que hemos verificado que el proceso 21954 es el causante de todos nuestros problemas tenemos que hacer algo con el mismo. Una manera de arreglar el problema seria matar al proceso desde la linea de comandos en el servidor. A mi personalmente, esta manera de proceder no me gusta y no me parece adecuada, deberia de usarse solamente como un último recurso y siempre estando de acuerdo con los encargados de la aplicación.\nLo que yo aconsejo hacer es encontrar el proceso en la maquina cliente que está utilizando el proceso 21954 en el servidor y arreglarlo ó matarlo.\nPara encontrar el proceso cliente causante del problema utilizamos la IP y el puerto remoto usado por 21954 (129.240.10.210 : 49417)\nNos conectamos a 129.240.10.210 y ejecutamos un par de comandos que nos diran el proceso que buscamos.\n[root@server ~]$ ssh root@129.240.10.210 [root@129.240.10.210 ~]$ netstat -pn |grep 129.240.10.210 |grep 49417 tcp 0 0 129.240.10.210:49417 129.240.255.222:5432 ESTABLISHED 24509/psql En la linea obtenida podemos ver al final el numero local del proceso que buscamos (24509/psql)\n[root@129.240.10.210 ~]$ ps axu |grep psql | grep 24509 postgres 24509 0.0 0.0 77288 3144 pts/1 S+ 13:53 0:00 psql -h server test001 Lo único que nos queda hacer es contactar con el encargado de este proceso para que arregle la situación ó matarlo controladamente para solucionar todos los problemas en nuestra base de datos. En muchos casos estamos hablando de scripts ó programas que se han colgado y la única manera de arreglarlos es matandolos.\n[root@129.240.10.210 ~]$ kill 24509 De vuelta en el servidor de base de datos podemos comprobar que todos los procesos con problemas de bloqueos han desaparecido despues de matar al causante del bloqueo.\nEsto es todo por hoy. Más información sobre el tema en la documentación oficial.\nEnlaces\nPostgreSQL doc 13.3. Explicit Locking PostgreSQL doc: 44. System Catalogs ","date":"2009/04/30","externalUrl":null,"permalink":"/es/blog/identificando-procesos-postgresql-con-problemas/","section":"Blogs","summary":"Un administrador de bases de datos no siempre tiene acceso ó conoce la aplicación que está accediendo a la base datos que administra. En muchos casos habrá que ayudar a los desarrolladores ó encargados de la aplicación a resolver los problemas que surjan.\nEn este artículo vamos a ver como identificar procesos, tanto en el servidor como en los clientes, que están accediendo a nuestra base datos. El saber identificar los procesos involucrados en una operación nos puede ayudar mucho en situaciones especiales en las que ciertas operaciones ó conexiones tengan ó sean causantes de problemas.\n","title":"Identificando procesos postgreSQL con problemas","type":"blog"},{"content":"Una instalación por defecto de PostgreSQL no necesita ninguna configuración especial del sistema operativo Linux donde se ejecuta.\nPero si vamos a utilizar PostgreSQL en sistemas de producción ó con grandes cantidades de datos, tendremos que ajustar ciertos parametros en el fichero de configuración postgresql.conf y estos cambios con gran probabilidad, harán que PostgreSQL deje de funcionar si no se ajustan ciertos parametros del núcleo de Linux.\nExisten dos elementos que probablemente necesiten ajustes en el núcleo de vuestro sistema Linux. Estos elementos son usados por PostgreSQL para organizar los recursos compartidos entre procesos PostgreSQL.\nMemoria compartida (share memory) Semáforos (Semaphore) Los parámetros del núcleo que se utilizan para configurarlos son:\nMemoria compartida\nSHMMAX: Tamaño máximo de un segmento de memoria compartida (bytes) SHMMIN: Tamaño mínimo de un segmento de memoria compartida (bytes) SHMALL: Cantidad máxima de memoria compartida disponible (bytes ó páfinas) SHMSEG: Número máximo de segmentos de memoria compartida por proceso SHMMNI: Número máximo de segmentos de memoria compartida en todo el sistema Semáforos\nSEMMNI: Número máximo de identificadores de semáforos (grupos) SEMMNS: Número máximo de semáforos en todo el sistema SEMMSL: Número máximo de semáforos por grupo SEMMAP: Número de entradas en el mapa de semáforos SEMVMX: Máximo valor de un semáforo SEMOPM: Número máximo de operaciones con semáforos que pueden realizarse por cada llamada del sistema semop SEM: Es igual a \u0026ldquo;SEMMSL SEMMNS SEMOPM SEMMNI\u0026rdquo; Los valores recomendados para estos parámetros para uso con PostgreSQL son los siguientes:\nMemoria compartida\nSHMMAX: Varios MB. El valor por defecto del núcleo es 32MB, un valor muy bajo en muchos casos. Existen varios parametros en postgresql.conf que determinan cuanta memoria compartida necesitaremos, el más importante de ellos y el principal causante de que necesitemos aumentar SHMMAX es shared_buffers.\nEl valor de SHMMAX (bytes) no puede ser menor que el valor definido en shared_buffers. PostgreSQL se negará a arrancar si el valor de SHMMAX es muy pequeño.\nSHMMIN: Como mínimo 1.\nSHMALL: Como mínimo SHMMAX/PAGE_SIZE. En donde el valor de PAGE_SIZE lo podeis obtener ejecutando el comando getconf PAGESIZE. El valor por defecto del núcleo es 2097152 pages (8192MB)\nSi teneis varios clusters de PostgreSQL ejecutandose en la máquina ó otros programas que usen memoria compartida, tendreis que definir un valor de SHMALL lo suficientemente grande para poder ejecutar todos estos programas a la vez.\nSHMSEG: Solamente se necesita 1. El valor por defecto es mucho más grande.\nSHMMNI: Como mínimo SHMSEG + lo necesario por otras aplicaciones. El valor por defecto del núcleo es 4096.\nPocas veces hay que modificar este valor.\nSemáforos\nSEMMNI: ceil((max_connections + autovacuum_max_workers) / 16). El valor por defecto del núcleo suele ser 128 SEMMNS: ceil((max_connections + autovacuum_max_workers) / 16) * 17 + lo necesario por otras aplicaciones. El valor por defecto del núcleo suele ser 32000 SEMMSL: Como minimo 17. El valor por defecto del núcleo suele ser 250 SEMMAP: En algunos casos tiene que ser igual a SEMMNS SEMVMX: Como mínimo 1000. El valor por defecto del núcleo suele ser 32767 SEMOPM: El valor por defecto del núcleo suele ser 32 SEM: Es igual a \u0026ldquo;SEMMSL SEMMNS SEMOPM SEMMNI\u0026rdquo; Los casos mas comúnes de usuarios con problemas de arranque de PostgreSQL son:\nAumento del parametro shared_buffers en el fichero de configuración postgresql.conf sin cambiar los valores del núcleo de memoria compartida. El error típico registrado es: FATAL: could not create shared memory segment: Invalid argument DETAIL: Failed system call was shmget(key=5440001, size=4011376640, 03600). Aumento de los parámetros max_connections y/ó autovacuum_max_workers en el fichero de configuración postgresql.conf sin cambiar los valores del núcleo de semáforos FATAL: could not create semaphores: No space left on device DETAIL: Failed system call was semget(5440126, 17, 03600). Vamos a ver un ejemplo de como ajustar los valores del núcleo cuando cambiamos algunos valores en postgresql.conf. Vamos a cambiar los valores de shared_buffers, max_connections y autovacuum_max_workers:\nshared_buffers = 2048MB max_connections = 300 autovacuum_max_workers = 6 Probablemente, si intentamos arrancar PostgreSQL con los valores por defecto del núcleo, fallará el arranque. Vamos a ver que valores tenemos definidos en nuestro sistema:\nroot@server:~# getconf PAGESIZE 4096 root@server:~# cat /proc/sys/kernel/shmall 2097152 root@server:~# cat /proc/sys/kernel/shmmax 33554432 root@server:~# cat /proc/sys/kernel/shmmni 4096 root@server:~# cat /proc/sys/kernel/sem 250 32000 32 128 A continuación calculamos los valores que vamos a necesitar y actualizamos, si es necesario, el fichero /etc/sysctl.conf con los mismos.\nCon un valor de 2048MB en shared_bufferes necesitamos como mínimo: shmmax = 2147483648 (bytes = 2048MB * 1024 * 1024) shmall = shmmax / 4096 = 524288 Con un valor de 300 en max_connections y 6 en autovacuum_max_workers necesitamos como mínimo: semmsl = 17 semmns = ceil((300 + 6) / 16) * 17 = 340 semmni = ceil((300 + 6) / 16) = 20 Si comparamos los valores que tenemos definidos por defecto y los que necesitamos para que PostgreSQL funcione, solamente necesitamos alterar SHMMAX, ya que este es el único parametro con un valor definido por debajo del valor que necesitamos.\nPara cambiar el valor de este parametro permanentemente tenemos que editar el fichero /etc/sysctl.conf, y añadir la linea:\nkernel.shmmax = 2147483648 Para instalar los cambios tenemos que ejecutar el comando\nsysctl -p /etc/sysctl.conf Despues de este cambio deberiais de poder arrancar PostgreSQL sin problemas.\nDesde la linea de comandos podeis ver la memoria compartida y los semáforos usados por postgreSQL en vuestro sistema. Aqui teneis un ejemplo de como podeis consultar la memoria compartida y los semáforos usados por postgreSQL\npostgres@bserver:~$ ipcs -m ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x0052e2c1 983040 postgres 600 219332608 5 postgres@server:~$ ipcs -mi 983040 Shared memory Segment shmid=983040 uid=1000 gid=1000 cuid=1000 cgid=1000 mode=0600 access_perms=0600 bytes=219332608 lpid=10125 cpid=22498 nattch=5 att_time=Wed Apr 22 10:33:12 2009 det_time=Wed Apr 22 10:33:13 2009 change_time=Wed Apr 15 21:29:27 2009 postgres@server:~$ ipcs -s ------ Semaphore Arrays -------- key semid owner perms nsems 0x0052e2c1 10944512 postgres 600 17 0x0052e2c2 10977281 postgres 600 17 0x0052e2c3 11010050 postgres 600 17 0x0052e2c4 11042819 postgres 600 17 0x0052e2c5 11075588 postgres 600 17 0x0052e2c6 11108357 postgres 600 17 0x0052e2c7 11141126 postgres 600 17 postgres@server:~$ ipcs -si 10944512 Semaphore Array semid=10944512 uid=1000 gid=1000 cuid=1000 cgid=1000 mode=0600, access_perms=0600 nsems = 17 otime = Wed Apr 15 21:29:27 2009 ctime = Wed Apr 15 21:29:27 2009 semnum value ncount zcount pid 0 1 0 0 22498 1 1 0 0 22498 2 1 0 0 22498 3 1 0 0 22498 4 1 0 0 22498 5 1 0 0 22498 6 1 0 0 22498 7 1 0 0 22498 8 1 0 0 22498 9 1 0 0 22498 10 1 0 0 22498 11 1 0 0 22498 12 1 0 0 22498 13 1 0 0 22498 14 1 0 0 22498 15 1 0 0 22498 16 537 0 0 22498 Bueno esto es todo lo que tenia pensado decir sobre los parametros del kernel y PostgreSQL. Para más información pasaros por la sección 17.4. Managing Kernel Resources de la documentación oficial.\n","date":"2009/04/22","externalUrl":null,"permalink":"/es/blog/configurando-los-parametros-del-kernel-para-postgresql/","section":"Blogs","summary":"Una instalación por defecto de PostgreSQL no necesita ninguna configuración especial del sistema operativo Linux donde se ejecuta.\nPero si vamos a utilizar PostgreSQL en sistemas de producción ó con grandes cantidades de datos, tendremos que ajustar ciertos parametros en el fichero de configuración postgresql.conf y estos cambios con gran probabilidad, harán que PostgreSQL deje de funcionar si no se ajustan ciertos parametros del núcleo de Linux.\n","title":"Configurando los parametros del kernel para PostgreSQL","type":"blog"},{"content":" The PostgreSQL-ES project was a website about PostgreSQL in Spanish that was active between April 2009 and June 2017. For a long period of time It was the website of reference in spanish for the PostgreSQL database.\nPostgreSQL-ES had sections with the PostgreSQL history, features, documentation, articles, links, news and different forums about multiple themes related to this database.\nApril 20, 2009 was the official opening of the PostgreSQL-ES. During the first month online, more than 220,000 document requests from 4,602 different machines were processed, and almost 2GB were transferred. In the best month of the web, around 1,7 million document requests from 104.579 different machines were processed and around 12GB of data were transferred.\nDue to lack of time to maintain the contents and upgrade the web engine used, I decided to close PostgreSQL-ES on June 2017.\nI leave you an archive with a selection of articles published in \u0026ldquo;PostgreSQL-ES\u0026rdquo; during the years.\nOne of the last snapshots of POSTGRESQL.ORG.ES in the \u0026ldquo;Internet Archive\u0026rdquo; is available at this URL: http://web.archive.org/web/20170615202906/http://www.postgresql.org.es/\n","date":"2009/04/20","externalUrl":null,"permalink":"/projects/postgresql.org/","section":"Projects","summary":"The PostgreSQL-ES project was a website about PostgreSQL in Spanish that was active between April 2009 and June 2017. For a long period of time It was the website of reference in spanish for the PostgreSQL database.","title":"POSTGRESQL.ORG.ES","type":"projects"},{"content":"En este pequeño artículo vamos a ver como instalar en postgreSQL una función programada en C por nosotros.\nLa posibilidad que tiene PostgreSQL de poder programar nuestras propias funciones en C y usarlas desde nuestra base de datos es una de las muchas características que hacen a esta base de datos tan potente. Una función programada en C podra tener entre otras cosas, acceso a muchas funciones del sistema y a la velocidad de proceso que C nos brinda.\nHay que reconocer que esto no es una tarea apta para principiantes. Se necesitan conocimientos de C y leer un poco de documentación. La documentación sobre la programación de funciones en C para PostgreSQL esta disponible en el Capítulo 34. Extending SQL y en la Sección 34.9. C-Language Functions del manual de PostgreSQL.\nExiste un libro magnífico sobre la programación avanzada en C en sistemas Unix, llamado \u0026ldquo;Advanced Programming in the UNIX Environment, Second Edition (Addison-Wesley Professional Computing Series)\u0026rdquo; (ISBN-13: 978-0321525949). Totalmente recomendado para los interesados en el tema.\nComo ejemplo vamos a crear una función que acceda a la función uname() del sistema operativo. Tambien veremos como acceder a los datos disponibles mediante uname() desde la base de datos.\nLo primero que tenemos que hacer es programar nuestra función. En nuestro caso vamos a crear un función llamada pg_uname() con la que podamos obtener los parametros sysname, nodename, release, version, machine de nuestro sistema operativo Linux.\nCreamos un fichero llamado pg_uname.c con el siguiente contenido:\n#include \u0026#34;postgres.h\u0026#34; #include \u0026lt;string.h\u0026gt; #include \u0026#34;fmgr.h\u0026#34; #include \u0026lt;sys/utsname.h\u0026gt; #ifdef PG_MODULE_MAGIC PG_MODULE_MAGIC; #endif PG_FUNCTION_INFO_V1(pg_uname); Datum pg_uname(PG_FUNCTION_ARGS) { text *argument = PG_GETARG_TEXT_P(0); size_t argumentlen = VARSIZE(argument)-VARHDRSZ; text *result = (text *) palloc(256); char *option = (char *) palloc(argumentlen+1); char sysname[] = \u0026#34;sysname\u0026#34;; char nodename[] = \u0026#34;nodename\u0026#34;; char release[] = \u0026#34;release\u0026#34;; char version[] = \u0026#34;version\u0026#34;; char machine[] = \u0026#34;machine\u0026#34;; char null[] = \u0026#34;null\u0026#34;; struct utsname uname_pointer; uname(\u0026amp;uname_pointer); memcpy(option,VARDATA(argument),argumentlen); option[argumentlen] = \u0026#39;\\0\u0026#39;; if (strcmp(option,sysname) == 0){ SET_VARSIZE(result, strlen(uname_pointer.sysname) + VARHDRSZ); memcpy(VARDATA(result),uname_pointer.sysname,strlen(uname_pointer.sysname)); } else if (strcmp(option,nodename) == 0){ SET_VARSIZE(result, strlen(uname_pointer.nodename) + VARHDRSZ); memcpy(VARDATA(result),uname_pointer.nodename,strlen(uname_pointer.nodename)); } else if (strcmp(option,release) == 0){ SET_VARSIZE(result, strlen(uname_pointer.release) + VARHDRSZ); memcpy(VARDATA(result),uname_pointer.release,strlen(uname_pointer.release)); } else if (strcmp(option,version) == 0){ SET_VARSIZE(result, strlen(uname_pointer.version) + VARHDRSZ); memcpy(VARDATA(result),uname_pointer.version,strlen(uname_pointer.version)); } else if (strcmp(option,machine) == 0){ SET_VARSIZE(result, strlen(uname_pointer.machine) + VARHDRSZ); memcpy(VARDATA(result),uname_pointer.machine,strlen(uname_pointer.machine)); } else{ memcpy(VARDATA(result),null,sizeof(null)); } pfree(option); PG_RETURN_TEXT_P(result); } A continuación tenemos que compilar la función e instalarla en un directorio donde postgreSQL pueda leerla y cargarla. Para compilarla utilizamos el compilador por defecto en linux, gcc.\nroot@server:~$ gcc -I /usr/local/include -I /usr/local/include/postgresql/server -fpic -cpg_uname.c root@server:~$ gcc -I /usr/local/include -I /usr/local/include/postgresql/server -shared -o pg_uname.so pg_uname.o root@server:~$ cp pg_uname.so /usr/local/lib Una vez que tenemos la función compilada e instalada en nuestro sistema operativo. Utizaremos psql para decirle a PostgreSQL donde encontrarla.\npostgres@server:~$ psql Welcome to psql 8.3.7, the PostgreSQL interactive terminal. Type: \\copyright for distribution terms \\h for help with SQL commands \\? for help with psql commands \\g or terminate with semicolon to execute query \\q to quit postgres=# CREATE OR REPLACE FUNCTION pg_uname(text) RETURNS text AS \u0026#39;/usr/local/lib/pg_uname.so\u0026#39;, \u0026#39;pg_uname\u0026#39; LANGUAGE c STRICT; CREATE FUNCTION Despues de esto podemos empezar a utilizar la función desde comandos SQL:\npostgres=# SELECT pg_uname(\u0026#39;nodename\u0026#39;); pg_uname -------------------- server.ejemplo.com (1 row) postgres=# SELECT pg_uname(\u0026#39;machine\u0026#39;); pg_uname ---------- x86_64 (1 row) Podriamos crear una VIEW llamada sysinfo que utilize pg_uname() junto con otras funciones de PostgreSQL para obtener información sobre nuestro sistema. Por ejemplo:\npostgres=# CREATE OR REPLACE VIEW sysinfo AS SELECT pg_uname(\u0026#39;sysname\u0026#39;) AS sysname, pg_uname(\u0026#39;nodename\u0026#39;) AS nodename, pg_uname(\u0026#39;release\u0026#39;) AS kernel_release, pg_uname(\u0026#39;version\u0026#39;) AS \u0026#34;kernel_version\u0026#34;, pg_uname(\u0026#39;machine\u0026#39;) AS machine, substr(\u0026#34;version\u0026#34;(), 11, 7) AS pgversion, to_char(pg_postmaster_start_time(),\u0026#39;YYYY-MM-DD HH24:MM:SS\u0026#39;) AS pg_start, age(now(),pg_postmaster_start_time()) AS pg_uptime; CREATE VIEW postgres=# \\x Expanded display is on. postgres=# SELECT * from sysinfo ; -[ RECORD 1 ]---------------------------------- sysname | Linux nodename | server.ejemplo.com kernel_release | 2.6.9-67.0.15.ELsmp kernel_version | #1 SMP Tue Apr 22 13:58:43 EDT 2008 machine | x86_64 pgversion | 8.3.6 pg_start | 2009-02-12 07:02:17 pg_uptime | 2 mons 5 days 13:12:30.059856 Bueno esto es todo en este artículo, espero que os haya ayudado a entender mejor como instalar en PostgreSQL nuestras propias funciones en C\n","date":"2009/04/20","externalUrl":null,"permalink":"/es/blog/programando-en-c-pguname/","section":"Blogs","summary":"En este pequeño artículo vamos a ver como instalar en postgreSQL una función programada en C por nosotros.\nLa posibilidad que tiene PostgreSQL de poder programar nuestras propias funciones en C y usarlas desde nuestra base de datos es una de las muchas características que hacen a esta base de datos tan potente. Una función programada en C podra tener entre otras cosas, acceso a muchas funciones del sistema y a la velocidad de proceso que C nos brinda.\n","title":"Programando en C - pg_uname","type":"blog"},{"content":"La cuenta de administrator es la cuenta más importante de nuestro sistema y se merece una atención especial por nuestra parte para evitarnos problemas de seguridad. Un fallo en la configuración de la misma puede poner la integridad de nuestro sistema en peligro.\nPor defecto PostgreSQL instala una cuenta de administrador llamada \u0026lsquo;postgres\u0026rsquo;. La información de esta cuenta y lo que puede hacer se puede acceder en los catálogos de sistema, pg_authid, pg_roles, pg_shadow y pg_users. Más información sobre estos catalogos se encuentra disponible en el Capítulo 44. System Catalogs de la documentación oficial.\nPor defecto despues de instalar postgreSQL, la cuenta \u0026ldquo;postgres\u0026rdquo; no tiene definida ninguna clave de acceso y cualquier usuario que tenga acceso a la maquina que este ejecutando PostgreSQL, podrá acceder a todas nuestras bases de datos como usuario \u0026ldquo;postgres\u0026rdquo; via sockets.\nEsto lo podemos comprobar viendo la información definida, por ejemplo en pg_shadow y en pg_hba.conf\n[postgres@server]~$ psql Welcome to psql 8.3.7, the PostgreSQL interactive terminal. Type: \\copyright for distribution terms \\h for help with SQL commands \\? for help with psql commands \\g or terminate with semicolon to execute query \\q to quit postgres=# SELECT * from pg_shadow; usename | usesysid | usecreatedb | usesuper | usecatupd | passwd | valuntil | useconfig -------------+----------+-------------+----------+-----------+---------------+----------+----------- postgres | 10 | t | t | t | | | (1 rows) postgres=# \\q [postgres@server]~$ grep postgres /var/pgsql/data/pg_hba.conf local all postgres trust Esta configuración por defecto puede ser un gran problema de seguridad en una máquina en la que muchos usuarios tengan acceso y tengamos bases de datos en producción ó con datos confidenciales.\nCualquier usuario con acceso a la máquina en cuestion podria acceder a PostgreSQL como usuario \u0026ldquo;postgres\u0026rdquo; y tener privilegios de administrador para hacer lo que quiera con nuestras bases de datos:\nusuario@server:~$ psql -U postgres Welcome to psql 8.3.7, the PostgreSQL interactive terminal. Type: \\copyright for distribution terms \\h for help with SQL commands \\? for help with psql commands \\g or terminate with semicolon to execute query \\q to quit Existen diferentes maneras de asegurar nuestra cuenta de administrador en PostgreSQL y nuestras bases de datos. A continuación teneis una manera de hacerlo que según nuestra experiencia es una de las mejores y más seguras. Implementaremos las siguientes medidas:\nTener una cuenta de sistema (postgres) en el sistema operativo sin clave definida. Habrá que ser root y utilizar su - postgres para convertirse en el usuario \u0026ldquo;postgres\u0026rdquo;. No será posible acceder directamente al sistema como \u0026ldquo;postgres\u0026rdquo; via tcp/ip El acceso mediante sockets será usado solamente por la cuenta de administrador \u0026ldquo;postgres\u0026rdquo;. El resto de usuarios utilizarán el protocolo tcp/ip. Cambiar los permisos del socket utilizado por PostgreSQL 4.Utilizar el metodo de autentificación \u0026lsquo;ident\u0026rsquo; para la cuenta \u0026ldquo;postgres\u0026rdquo; en nuestro gestor de base de datos. De esta manera solamente el usuario postgres del sistema operativo podrá acceder a PostgreSQL como el usuario \u0026ldquo;postgres\u0026rdquo; de la base de datos Para implementar estas medidas tendremos que cambiar la configuración por defecto siguiendo estos pasos:\nPara convertir la cuenta postgres del sistema operativo en una cuenta sin clave definida ejecutar como root: passwd -d postgres Comprobar que no teneis ninguna definición en pg_hba.conf para usuarios normales usando sockets (local) en el tipo de conexión. Si teneis usuarios usando sockets, cambiar el tipo de conexión a host y usar 127.0.0.1/255.255.255.255 (localhost) como IP de acceso. Con psql tendreis que utilizar psql -h localhost -U usuario para acceder al sistema. Cambiar en el fichero de configuración postgresql.conf las siguientes lineas: listen_addresses = \u0026#39;localhost\u0026#39; unix_socket_permissions = 0700 Si las bases de datos van a ser accedidas desde otras maquinas externas, tendreis que definir tambien en listen_addresses la IP del servidor postgreSQL. Actualizar el fichero pg_ident.conf con la siguiente linea: administrador postgres postgres Y comprobar que la única linea en pg_hba.conf con información sobre el usuario \u0026ldquo;postgres\u0026rdquo; es la siguiente: local all postgres ident administrador No olvidar ejecutar un restart de PostgreSQL para que los cambios se instalen.\nComo podeis ver en el siguiente ejemplo, ni siendo administrador root en nuestro sistema operativo podremos acceder como usuario \u0026ldquo;postgres\u0026rdquo; a nuestro sistema de bases de datos PostgreSQL. Primero tendremos que convertirnos en usuario postgres en nuestro sistema operativo.\n[usuario@server:~] $ psql -U postgres psql: could not connect to server: Permission denied Is the server running locally and accepting connections on Unix domain socket \u0026#34;/tmp/.s.PGSQL.5432\u0026#34;? [usuario@server:~] $ psql -h localhost -U postgres psql: FATAL: no pg_hba.conf entry for host \u0026#34;127.0.0.1\u0026#34;, user \u0026#34;postgres\u0026#34;, database \u0026#34;postgres\u0026#34;, SSL off [root@server:~] # psql psql: FATAL: no pg_hba.conf entry for host \u0026#34;[local]\u0026#34;, user \u0026#34;root\u0026#34;, database \u0026#34;root\u0026#34;, SSL off [root@server:~] # psql -U postgres psql: FATAL: Ident authentication failed for user \u0026#34;postgres\u0026#34; [root@server:~] # su - postgres [postgres@server:~] $ psql Welcome to psql 8.3.7, the PostgreSQL interactive terminal. Type: \\copyright for distribution terms \\h for help with SQL commands \\? for help with psql commands \\g or terminate with semicolon to execute query \\q to quit postgres=# Y por último solo nos queda comentar que en sistemas que estén en producción y con datos importantes, debeis restringir a un mínimo el numero de personas con acceso a la cuenta \u0026ldquo;postgres\u0026rdquo; del sistema. Solamente administradores de confianza y que sepan que es lo que están haciendo deberian de obtener este acceso para evitar problemas mayores.\n","date":"2009/04/04","externalUrl":null,"permalink":"/es/blog/asegurando-la-cuenta-de-administrador-postgres/","section":"Blogs","summary":"La cuenta de administrator es la cuenta más importante de nuestro sistema y se merece una atención especial por nuestra parte para evitarnos problemas de seguridad. Un fallo en la configuración de la misma puede poner la integridad de nuestro sistema en peligro.","title":"Asegurando la cuenta de administrador postgres","type":"blog"},{"content":"El \u0026ldquo;prompt\u0026rdquo; (texto de introducción en la linea de comandos) en psql se puede cambiar y definir de una manera sencilla y rapida para adaptarlo a nuestras necesidades.\nA mi por ejemplo me gusta que me indique en que servidor estoy trabajando, el usuario con el que estoy ejecutando comandos, si estoy en una transacción ó no, etc. Estos datos me ayudan mucho en mi trabajo diario y evitan que cometa errores.\nPor defecto, el prompt del cliente psql solamente muestra el nombre de la base de datos que estais utilizando.\npostgres@server:~$ psql Welcome to psql 8.3.7, the PostgreSQL interactive terminal. Type: \\copyright for distribution terms \\h for help with SQL commands \\? for help with psql commands \\g or terminate with semicolon to execute query \\q to quit postgres=# Para cambiar el \u0026ldquo;prompt\u0026rdquo;, lo único que tenemos que hacer es actualizar el valor de la variable PROMPT1. Esta variable es la que define el \u0026ldquo;prompt\u0026rdquo; principal del cliente psql. Existen dos variables más PROMPT2 y PROMPT3 que definen otros tipos de \u0026ldquo;prompt\u0026rdquo; en determinadas circunstancias, pero en este artículo nos centraremos solamente en PROMPT1.\nEl valor que le demos a esta variable se muestra literalmente excepto cuando el símbolo % es usado. Dependiendo del caracter definido despues del símbolo %, se sustituirán estos por un valor u otro.\nA continuación teneis los códigos que podeis usar:\n%M\nEl nombre completo (con dominio) del servidor ejecutando la base de datos, ó [local] si estamos conectando via \u0026ldquo;Unix domain socket\u0026rdquo;, ó [local:/directorio/nombre] si el socket no se encuentra en el directorio por defecto.\n%m\nEl nombre del servidor ejecutando la base de datos ó [local] si estamos conectando via \u0026ldquo;Unix domain socket\u0026rdquo;\n%\u0026gt;\nEl puerto de red utilizado por nuestra base de datos.\n%n\nEl nombre de usuario que hemos usado para conectarnos a la base de datos. Puede cambiar durante una misma sesión si se utiliza el comando SET SESSION AUTHORIZATION\n%/\nNombre de la base de datos a la que estamos conectados\n%~\nComo %/, pero se muestra ~ (tilde) si la base de datos a la que estamos conectados es la nuestra por defecto.\n%#\nSi el usuario de nuestra sesión es el administrador (postgres por defecto) se muestra el símbolo #, si no, se mostrará \u0026gt;. Puede cambiar durante una misma sesión si se utiliza el comando SET SESSION AUTHORIZATION\n%R\nSi estamos definiendo PROMPT1, mostrará normalmente el simbolo =, el símbolo ^ si estamos en modo de linea simple y el símbolo ! si la sesión es desconectada de la base de datos.\n%x\nMuestra el estado de la transacción. Será una cadena vacia si no estamos dentro de una transacción. El símbolo * si estamos dentro de una transacción. El símbolo ! si estamos dentro de una transacción con errores/fallos y el símbolo ? si el estado de la transacción es indeterminado.\n%digits\nMuestra el caracter con el valor octal definido por digits\n%:name:\nMuestra el valor de la variable psql con nombre \u0026ldquo;name\u0026rdquo;.\n%`command`\nMuestra el resultado de ejecutar el comando \u0026ldquo;command\u0026rdquo;\n%[ \u0026hellip; %]\n\u0026ldquo;Prompts\u0026rdquo; pueden contener caracteres de control de terminal para cambiar el color, el fondo, el título de la ventana de nuestra terminal, etc. Estos caracteres se tienen que definir entre %[ y %]\nSi quereis mostrar el símbolo % en vuestro \u0026ldquo;prompt\u0026rdquo; teneis que escribir %%\nBueno una vez que sabemos los símbolos de sustitución vamos a cambiar nuestro \u0026ldquo;prompt\u0026rdquo; en psql. En nuestro ejemplo vamos a definir uno con la siguiente información:\nEl nombre del servidor ejecutando la base de datos El puerto de red utilizado por nuestra base de datos. El nombre de usuario Nombre de la base de datos El estado de la transacción ¿Somos administradores ó usuarios normales? Para cambiar el valor de la variable PROMPT1 usamos el comando \\set en psql.\npostgres@server:~$ psql Welcome to psql 8.3.7, the PostgreSQL interactive terminal. Type: \\copyright for distribution terms \\h for help with SQL commands \\? for help with psql commands \\g or terminate with semicolon to execute query \\q to quit postgres=# \\set PROMPT1 \u0026#39;[%m:%\u0026gt;/%n@%/][%x]%# \u0026#39; [[local]:5432/postgres@postgres][]# BEGIN ; BEGIN [[local]:5432/postgres@postgres][*]# SELECT asda; ERROR: column \u0026#34;asda\u0026#34; does not exist LINE 1: SELECT asda; ^ [[local]:5432/postgres@postgres][!]# ROLLBACK ; ROLLBACK [[local]:5432/postgres@postgres][]# \\c template1 You are now connected to database \u0026#34;template1\u0026#34;. [[local]:5432/postgres@template1][]# \\set PROMPT1 \u0026#39;%/%R%#\u0026#39; template1=# \\q postgres@server:~$ Esto es todo. Si quereis evitar ejecutar el comando \\set PROMPT1 cada vez que entreis de nuevo en psql, podeis definir una linea con este comando en el fichero .psqlrc en el directorio HOME del usuario que utiliceis para conectaros a PostgreSQL.\n","date":"2009/03/29","externalUrl":null,"permalink":"/es/blog/cambiando-el-prompt-del-cliente-psql/","section":"Blogs","summary":"El “prompt” (texto de introducción en la linea de comandos) en psql se puede cambiar y definir de una manera sencilla y rapida para adaptarlo a nuestras necesidades.\nA mi por ejemplo me gusta que me indique en que servidor estoy trabajando, el usuario con el que estoy ejecutando comandos, si estoy en una transacción ó no, etc. Estos datos me ayudan mucho en mi trabajo diario y evitan que cometa errores.\n","title":"Cambiando el prompt del cliente psql","type":"blog"},{"content":"PostgreSQL se puede empezar a utilizar nada más terminar de instalarlo y despues de inicializar nuestro \u0026ldquo;cluster\u0026rdquo;, sin necesidad de configurar nada. Pero si vamos a utilizar PostgreSQL para algo importante y con cierto volumen de datos y usuarios es imprescindible que lo configuremos para dicho trabajo.\nNo es la primera vez que algun asuario protesta o esta super preocupado de lo mal y lo lento que funciona su cluster de base de datos PostgreSQL en un servidor ultimo modelo con muchisima memoria. Normalmente el problema es que PostgreSQL no ha sido configurado para trabajar con el volumen de datos y usuarios con el que lo estamos usando. No es una gran ayuda tener un servidor con varios GBytes de memoria RAM si le hemos dicho a PostgreSQL, por ejemplo, que no utilice más de 32MBytes.\nTambien tenemos que decir que cualquier base de datos que se este usando activamente, no solo PostgreSQL, es un elemento dinamico y vivo en el que estamos cambiando los datos constantemente y donde el tamaño de los datos almacenados suele ir creciendo con el tiempo. Esto significa que una configuracion que funcione bien con ciertos valores hoy, puede que no funcione tan bien despues de unos meses de uso y que necesite ajustarse para que funcione optimalmente.\nEl comportamiento de PostgreSQL en nuestro sistema se puede controlar con tres ficheros de configuración que se encuentran en el directorio de datos donde inicializamos nuestro cluster PostgreSQL (En nuestro caso /var/pgsql/data). Estos tres ficheros son:\npg_hba.conf: Este fichero se utiliza para definir los diferentes tipos de accesos que un usuario tiene en el cluster. pg_ident.conf: Este fichero se utiliza para definir la información necesaria en el caso que utilicemos un acceso del tipo ident en pg_hba.conf . postgresql.conf: En este fichero podemos cambiar todos los parametros de configuracion que afectan al funcionamiento y al comportamiento de PostgreSQL en nuestra maquina. Pasamos a continuación a explicar los cambios mas importantes que podemos hacer en algunos de estos ficheros.\npg_hba.conf # Este fichero se utiliza para definir como, donde y desde que sitio un usuario puede utilizar nuestro cluster PostgreSQL. Todas las lineas que empiezen con el caracter # se interpretan como comentarios. El resto debe de tener el siguiente formato:\n[Tipo de conexion][database][usuario][IP][Netmask][Tipo de autentificacion][opciones] Dependiendo del tipo de conexion y del tipo de autentificacion, [IP],[Netmask] y [opciones] pueden ser opcionales. Vamos a explicar un poco como definir las reglas de acceso. El tipo de conexion puede tener los siguientes valores, local, host, hostssl y hostnossl. El tipo de metodo puede tener los siguientes valores, trust, reject, md5, crypt, password, krb5, ident, pam o ldap\nUna serie de ejemplos nos ayudaran a comprender mejor como podemos configurar diferentes accesos al cluster PostgreSQL.\nEjemplo 1 .- Acceso por tcp/ip (red) a la base de datos test001, como usuario test desde el ordenador con IP 10.0.0.100, y metodo de autentificacion md5:\nhost test001 test 10.0.0.100 255.255.255.255 md5 Esta misma entrada se podria escribir tambien con la mascara de red en notacion CIDR:\nhost test001 test 10.0.0.100/32 md5 Ejemplo 2 .- Acceso por tcp/ip (red) a la base de datos test001, como usuario test desde todos los ordenadores de la red 10.0.0.0, con mascara de red 255.255.255.0 (254 ordenadores en total) y metodo de autentificacion md5:\nhost test001 test 10.0.0.0 255.255.255.0 md5 Esta misma entrada se podria escribir tambien con la mascara de red en notacion CIDR:\nhost test001 test 10.0.0.0/24 md5 Ejemplo 3 .- Acceso por tcp/ip (red), encriptado, a todas las bases de datos de nuestro cluster, como usuario test desde el ordenador con IP 10.0.0.100, y el ordenador 10.1.1.100 y metodo de autentificacion md5 (necesitamos dos entradas en nuestro fichero pg_hba.conf:\nhostssl all test 10.0.0.100 255.255.255.255 md5 hostssl all test 10.1.1.100 255.255.255.255 md5 Ejemplo 4.- Denegar el acceso a todos las bases de datos de nuestro cluster al usuario test, desde todos los ordenadores de la red 10.0.0.0/24 y dar accesso al resto del mundo con el metodo md5:\nhost all test 10.0.0.0/24 reject host all all 0.0.0.0/0 md5 Asi podriamos seguir jugando con todas las posibilidades que nos brinda este fichero de configuracion. Por supuesto que las bases de datos y usuarios usados en este fichero tienen que existir en nuestro cluster para que todo funcione y algunos de los parametros solo se pueden usar si hemos compilado con las opciones pertinentes en el proceso de instalacion (por ejemplo, hostssl, pam, krb5)\nPara poder en produccion los cambios en este fichero tendremos que decirle a PostgreSQL que vuelva a leerlo. Basta con un simple \u0026lsquo;reload\u0026rsquo; (/usr/local/bin/pg_ctl -D /var/pgsql/data reload) desde la linea de comandos o con la funcion pg_reload_conf() como usuario postgres desde psql, el cliente PostgreSQL.\n[postgres@servidor]# /usr/local/bin/psql Welcome to psql 8.2.4, the PostgreSQL interactive terminal. Type: \\copyright for distribution terms \\h for help with SQL commands \\? for help with psql commands \\g or terminate with semicolon to execute query \\q to quit postgres=# SELECT pg_reload_conf(); pg_reload_conf ---------------- t (1 row) postgres=# Para una documentacion detallada sobre el fichero pg_hba.con, pasaros por la seccion Chapter 20. Client Authentication de la documentacion oficial de PostgreSQL.\npostgresql.conf # Los cambios que realicemos en este fichero afectaran a todas las bases de datos que tengamos definidas en nuestro cluster PostgreSQL. La mayoria de los cambios se pueden poner en produccion con un simple \u0026lsquo;reload\u0026rsquo; (/usr/local/bin/pg_ctl -D /var/pgsql/data reload), otros cambios necesitan que arranquemos de nuevo nuestro cluster (/usr/local/bin/pg_ctl -D /var/pgsql/data restart).\nMas informacion sobre todos los parametros que podemos cambiar en este fichero, que afectan y como se pueden poner en produccion se puede encontrar en la seccion 17. Server Configuration de la documentacion oficial de PostgreSQL.\nA continuacion vamos a ver los parametros mas importantes que deberiamos cambiar si empezamos a usar PostgreSQL para un uso serio y si queremos sacarle el maximo partido a nuestra maquina. Existen muchos mas parametros que se pueden y con el tiempo se deberan de ajustar, aqui nos vamos a centrar en los mas importantes y los cuales deberiamos cambiar antes de empezar a utilizar PostgreSQL de una manera seria.\nmax_connections: Numero maximo de clientes conectados a la vez a nuestras bases de datos. Deberiamos de incrementar este valor en proporcion al numero de clientes concurrentes en nuestro cluster PostgreSQL. Un buen valor para empezar es el 100:\nmax_connections = 100 shared_buffers: Este parametro es importantisimo y define el tamaño del buffer de memoria utilizado por PostgreSQL. No por aumentar este valor mucho tendremos mejor respuesta. En un servidor dedicado podemos empezar con un 25% del total de nuestra memoria. Nunca mas de 1/3 (33%) del total. Por ejemplo, en un servidor con 4Gbytes de memoria, podemos usar 1024MB como valor inicial.\nshared_buffers = 1024MB work_mem: Usada en operaciones que contengan ORDER BY, DISTINCT, joins, \u0026hellip;. En un servidor dedicado podemos usar un 2-4% del total de nuestra memoria si tenemos solamente unas pocas sesiones (clientes) grandes. Como valor inicial podemos usar 8 Mbytes.\nwork_mem = 8MB maintenance_work_mem: Usada en operaciones del tipo VACUUM, ANALYZE, CREATE INDEX, ALTER TABLE, ADD FOREIGN KEY. Su valor dependera mucho del tamaño de nuestras bases de datos. Por ejemplo, en un servidor con 4Gbytes de memoria, podemos usar 256MB como valor inicial.\nmaintenance_work_mem = 256MB effective_cache_size: Parametro usado por el \u0026lsquo;query planner\u0026rsquo; de nuestro motor de bases de datos para optimizar la lectura de datos. En un servidor dedicado podemos empezar con un 50% del total de nuestra memoria. Como maximo unos 2/3 (66%) del total. Por ejemplo, en un servidor con 4Gbytes de memoria, podemos usar 2048MB como valor inicial.\neffective_cache_size = 2048MB checkpoint_segments: Este parametro es muy importante en bases de datos con numerosas operaciones de escritura (insert,update,delete). Para empezar podemos empezar con un valor de 64. En grandes databases con muchos Gbytes de datos escritos podemos aumentar este valor hasta 128-256.\ncheckpoint_segments = 64 Es muy importante tener en cuenta que al aumentar los valores por defecto de muchos de estos parametros, tendremos que aumentar los valores por defecto de algunos parametros del kernel de nuestro sistema. Informacion detallada de como hacer esto se encuentra en la seccion 16.4. Managing Kernel Resources de la documentacion oficial de PostgreSQL.\nEn fin, esto es solo un aperitivo de lo que podemos hacer. Con la practica y la experiencia podremos y tendremos que ajustar otros muchos parametros. Pero esto sera materia de un proximo articulo.\n","date":"2009/03/29","externalUrl":null,"permalink":"/es/blog/configuracion-basica-de-postgresql/","section":"Blogs","summary":"PostgreSQL se puede empezar a utilizar nada más terminar de instalarlo y despues de inicializar nuestro “cluster”, sin necesidad de configurar nada. Pero si vamos a utilizar PostgreSQL para algo importante y con cierto volumen de datos y usuarios es imprescindible que lo configuremos para dicho trabajo.\nNo es la primera vez que algun asuario protesta o esta super preocupado de lo mal y lo lento que funciona su cluster de base de datos PostgreSQL en un servidor ultimo modelo con muchisima memoria. Normalmente el problema es que PostgreSQL no ha sido configurado para trabajar con el volumen de datos y usuarios con el que lo estamos usando. No es una gran ayuda tener un servidor con varios GBytes de memoria RAM si le hemos dicho a PostgreSQL, por ejemplo, que no utilice más de 32MBytes.\n","title":"Configuración básica de PostgreSQL","type":"blog"},{"content":" GoOpen2008 - Oslo, Norway 2008-04-08 postgresql_go_open2008_version2.pdf Not available\nSummary # A general presentation about PostgreSQL database for the GoOpen2008 conference in Oslo.\n","date":"2008/04/08","externalUrl":null,"permalink":"/presentations/postgresql-what-makes-database-so-powerful/","section":"Presentations","summary":"A general presentation about PostgreSQL database for the GoOpen2008 conference in Oslo.","title":"PostgreSQL - What makes this database so powerful","type":"presentations"},{"content":"De nuevo vamos a tratar de un tema en donde la terminal en modo texto y programas desde la linea de comandos son los protagonistas. Probablemente, este articulo no le sirva de mucho a un usuario domestico, con un solo ordenador en casa, pero si quieres ir conociendo tu sistema mas a fondo, y tienes que administrar mas de una maquina/servidor, sigue leyendo que puede que aprendas algo.\nEn este articulo vamos a ver como conectarse sin clave de acceso a otros ordenadores, de una manera segura, con ssh. Tambien veremos como configurar nuestro sistema para que trabaje por nosotros, automatizando tareas con cron y at. Y por ultimo, como combinando estas tecnicas, podemos ejecutar trabajos y recoger informacion de forma automatica de muchos ordenadores a un servidor central.\nSSH sin clave de acceso # No vamos a tratar como instalar SSH (cliente/servidor) en vuestras maquinas. Esto no deberia de ser un problema en las distribuciones de hoy en dia y muchas de ellas instalan este programa por defecto. Suponemos antes de seguir que tanto el cliente como el servidor SSH estan instalados y funcionando en vuestros ordenadores.\nSSH (Security SHell) es un programa que permite conectarse de forma segura entre maquinas que tengan este programa instalado (cliente y servidor). Entre muchas de las cosas que SSH ofrece, la mas importante es que todo el trafico entre dos maquinas (incluido cuentas y claves de acceso) es encriptado. Con esto se evita que informacion importante y confidencial caiga en manos ajenas en el caso que alguien este \u0026rsquo;escuchando\u0026rsquo; lo que viaja por la red con un programa \u0026lsquo;packet sniffer\u0026rsquo;.\nPara conectarse con SSH, como el usuario \u0026lsquo;user\u0026rsquo;, de una maquina llamada \u0026lsquo;servidor1\u0026rsquo; a otra llamada \u0026lsquo;servidor2\u0026rsquo;, podemos escribir en una terminal lo siguiente:\n[user@servidor1]# ssh user@servidor2 user@servidor2\u0026#39;s password: xxxxxxx Last login: Sun Oct 1 15:46:02 2006 [user@servidor2]# Lo normal es que haya que escribir la clave de acceso cuando nos conectamos a otro servidor. Esto no es un problema siempre y cuando estemos trabajando de forma interactiva y podamos escribir la clave, tampoco es un gran problema si nos conectamos pocas veces. Pero si queremos que una maquina se conecte de forma automatica a otra para realizar un trabajo, o tenemos que conectar muchas veces a diferentes maquinas, el tener que escribir la clave todo el tiempo termina siendo un problema. Esto lo podemos solucionar usando lo que se llaman claves privadas y publicas. No vamos a explicar que significa esto, esto se escapa del tema del articulo, pero al final damos unos enlaces que podeis usar para profundizar en el tema si quereis.\nPara generar nuestras claves publica y privada vamos a utilizar el programa ssh-keygen que forma parte de SSH. A continuacion teneis un ejemplo de como podemos generar estas claves:\n[ralf@servidor1]# ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/home/ralf/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/ralf/.ssh/id_rsa. Your public key has been saved in /home/ralf/.ssh/id_rsa.pub. The key fingerprint is: 09:f7:58:bc:07:3f:f4:70:7b:d7:ce:cb:6b:61:f8:9c ralf@servidor1 En este ejemplo hemos generado dos claves, una privada (/home/ralf/.ssh/id_rsa) y otra publica (/home/ralf/.ssh/id_rsa.pub) del tipo RSA con una longitud de 2048 bits. No hemos utilizado \u0026lsquo;passphrase\u0026rsquo; para no tener que escribirla cada vez que nos conectemos.\nA continuacion podeis ver el contenido de la clave privada y publica del ejemplo:\n[ralf@servidor1]# cat /home/ralfm/.ssh/id_rsa -----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAw0a4dn8wGAaRcMuIPY8/pk11YPV5Nk6R5VCtHNLnH2+hmsMw 47wMdJ8PC9Vu6bDYLgSwRIpV83kEJSE1DdHB93qExqjXnA7EWOzgmjjbSTbi2kfN xB4Vh9tS3dXCy/9xfcUrYj8LZH931I3y46YQAswmDqS9nZCvoE9xmzDmt9uecSOE 39e5aZiHwOGXlyNkExRRBT2JIDcL9aeAa1HclnRTbsZVWsxX8+K6koVvsmMhRZNq XoSuU+CWGse/UsLbo2sWHUKaFVMBVc2h0E4j08Rm/R85BgwzgxXr+ql6Tv1qJjyd lFJkUuD6RJ78X/Wp9HqVwM9i9v/hyE6FK9GtMQIBIwKCAQB6vr0XSKHjN1QbA5d3 JteNGr7POzY/ZJY4XpizCDmBeV5EBamzuAfURrkAH8IPO/WZRMaRe4Z7yGkBZVSM V/ZDyVrGA7q53WV5uXc8XkCxrXimdkbTC5iBSAgzqu946bUNOhtFEa9jvdZLF2V5 Jo24nZRDuAIohtTLKp8uWUCQ0hbq+NIg85tZhHuIgQtqFZNBF2YIOTNN6v532Ena YvNZUGfmYwJG1CurA1dUseL06bSB9LpOIaOG2j5bYWnhogshih0CYShzi1S8tJCN tpfMz2WWYGN/Mxa6Itq0xI+kVH0hLFkhoU/ryJ76toklFs7yZR2APPcgCJVxX3b/ DLoTAoGBAOvAd5Raz+Wc1LU5sH5diHNEYz+6etyUR3p5k7cLLMQ9Ye7u1NjBLWzF NRpF3md+I88X4qZCu6OKL/Dj4+ah9uXoi850D/G9rLfUIgtpLwxpx6jeseJuvGLr GB49lE6wFDlf460eaHhB5ndLX2i9UQ0Oqbyp0n9zYSpmqUsyQBqfAoGBANQMTqI5 Vbm+WcguBrWceJ3P3UwPIdrtEPymJGsnnvJK+zORaz7L2QLNKE+LupOAEVXqlCdZ qsxzJuTrVIGyj/tLVQI8r1xaAs5VdQ2FqyXp+JyApXJ7fomHI3HMCwFIBa+F73nu 9OzrUp1THS3QXWrv0uRWm+X/M/gtR4hMiPYvAoGAf/rEkl0vB55HlZRYfxzVC1+j l5+/CgdaAKhmIYm5N1SFnvauD0RL2/YG4mBxa2G7qu+1jXSvAQHfgsTazahhdX49 RDBgbUm1iF03DYI+HK5zs3GTxBA6YZWQv/WLBiUSSwgrI3boQUhYiecWiVDUOknJ 23Ih0CixFwSHybwxbYkCgYEAwd9d1iXKuHOFSU6nDHHNXRXRpKAe9AvyRhQ+jdsU +sg2IIT0Vqu/GIEO6aRS0AAP2YYDzDS5anfpC8/YPBD4qz2PjQRIjvM1w/ZcZCJw l7FYVJLgaKtsYHuOHuZwdjM4ZfbMUjmPeYax7uznelga5W2NnZEDkHRMxaW9vnHc TssCgYEAifwa8oq5eVl5qw/gh4s3Tv3Qjk6SCxIXCF2FwKUfYmkl7v/Yq4xL1Owp kQ7jT6Zi20endNT2a4mCFu79rztDzfPR/7BgOCFxu88IYO+0fgp4VeBZb0G9T3GG g/k0XSpd5Zjgn9SHVD1u5NRacUpeIPqX3cbwPxP5Xh7XlfEXcmw= -----END RSA PRIVATE KEY----- [ralf@servidor1]# cat /home/ralfm/.ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAw0a4dn8wGAaRcMuIPY8/pk11YPV5Nk6R5VC tHNLnH2+hmsMw47wMdJ8PC9Vu6bDYLgSwRIpV83kEJSE1DdHB93qExqjXnA7EWO zgmjjbSTbi2kfNxB4Vh9tS3dXCy/9xfcUrYj8LZH931I3y46YQAswmDqS9nZCvo E9xmzDmt9uecSOE39e5aZiHwOGXlyNkExRRBT2JIDcL9aeAa1HclnRTbsZVWsxX 8+K6koVvsmMhRZNqXoSuU+CWGse/UsLbo2sWHUKaFVMBVc2h0E4j08Rm/R85Bgw zgxXr+ql6Tv1qJjydlFJkUuD6RJ78X/Wp9HqVwM9i9v/hyE6FK9GtMQ==ralf@desktop Antes de seguir, hay dos cosas muy importantes a tener en cuenta con respecto a la seguridad de nuestros sistemas cuando utilicemos SSH sin clave de acceso:\nLa primera es que, nuestra clave privada nunca se debe hacer publica. Tenemos que tener cuidado de que nadie, tenga acceso a nuestra clave privada para no comprometer la seguridad de nuestro sistema. La segunda es que, al no utilizar \u0026lsquo;passphrase\u0026rsquo; (vacia), tenemos que estar seguros que la maquina servidor (maestra) desde donde nos vamos a conectar a los demas servidores, debe de ser segura. Tenemos que tener un cuidado extra con esta maquina, ya que si alguien indeseado consigue acceder a la misma como \u0026lsquo;root\u0026rsquo;, obtendra a su vez acceso (sin necesidad de clave) a todos los demas servidores de nuestra red. Para aumentar la seguridad del sistema, y si no necesitamos acceso \u0026lsquo;root\u0026rsquo;, podemos crear un usuario de sistema con privilegios restringidos, el cual podra ser utilizado para acceder a los demas servidores. Una vez que hemos generado nuestra clave privada y publica en nuestro servidor principal (el que tendra acceso al resto de nuestras maquinas), tenemos que copiar el contenido del fichero /home/ralf/.ssh/id_rsa.pub (clave publica) a el fichero ~/.ssh/authorized_keys del usuario que va a tener acceso sin clave. Esto se debe de hacer en todas las maquinas a las que queremos tener acceso.\nEl contenido de ~/.ssh/authorized_keys en las diferentes maquinas se puede actualizar, por ejemplo, de la siguiente manera:\n[ralf@servidor1]# scp /home/ralf/.ssh/id_rsa.pub ralf@servidor2:~/ [ralf@servidor1]#$ ssh ralf@servidor2 [ralf@servidor2]#$ cat ~/id_rsa.pub \u0026gt;\u0026gt; ~/.ssh/authorized_keys Despues de hacer esto, podremos conectarnos desde \u0026lsquo;servidor1\u0026rsquo; a \u0026lsquo;servidor2\u0026rsquo;, como el usuario \u0026lsquo;ralf\u0026rsquo; y sin clave, de la siguiente manera:\n[ralf@servidor1]# ssh ralf@servidor2 Last login: Thu Sep 28 17:00:17 2006 [ralf@servido2]# Este procedimiento lo tenemos que repetir en todos los servidores a los que deseemos acceder sin clave desde la maquina \u0026lsquo;servidor1\u0026rsquo;.\nPara mejorar todavia mas la seguridad, podemos actualizar en todos nuestros servidores los ficheros /etc/hosts.allow y /etc/hosts.deny con la IP o el nombre completo de el \u0026lsquo;servidor1\u0026rsquo; (servidor maestro/principal). Estas son las lineas que deberiamos de añadir o actualizar si ya existen:\nEn /etc/hosts.allow: sshd: IP o hostname de \u0026#39;servidor1\u0026#39; Y en /etc/hosts.deny: sshd: ALL Con todo lo que hemos hecho hasta ahora, podremos ejecutar comandos remotamente en \u0026lsquo;servidor2\u0026rsquo; desde \u0026lsquo;servidor1\u0026rsquo; sin necesidad de claves. El formato seria el siguiente:\n[user@servidor1]# ssh user@servidor2 \u0026#39;comando a ejecutar en servidor2\u0026#39; CRON # Para los que no lo saben, cron es un administrador de procesos en segundo plano que ejecuta trabajos a intervalos regulares. Cron se utiliza para automatizar tareas que hay que realizar periodicamente. Los procesos que deben ejecutarse y la hora en la que deben hacerlo se especifican en el archivo crontab del usuario que ejecutara los procesos.\nPara editar este fichero podemos utilizar nuestro editor favorito. Para ello tenemos que tener la variable de entorno EDITOR definida y usar crontab -e para editar nuestro crontab. Un ejemplo usando el editor emacs:\n[ralf@servidor1]# export EDITOR=/usr/bin/emacs [ralf@servidor1]# crontab -e En el fichero crontab se define una linea por tarea/trabajo a ejecutar y el formato de la misma es el siguiente:\n------------- minutos (0 - 59) | ----------- horas (0 - 23) | | --------- dia del mes (1 - 31) | | | ------- mes (1 - 12) | | | | ----- dia de la semana (0 - 6) (domingo=0, lunes=1, ... sabado=6) | | | | | * * * * * comando a ejecutar * significa todos los valores validos / permite definir una repeticion - permite definir un rango , permite definir varios valores Las lineas que comienzan con \u0026lsquo;#\u0026rsquo; se consideran comentarios. Podemos utilizar la linea MAILTO=\u0026ldquo;usuario@ejemplo.com\u0026rdquo; al comienzo para que cron nos mande un mensaje cuando se ejecute un trabajo. Un ejemplo nos ayudara a entender todo esto mejor. Listamos el contenido de nuestro crontab despues de haberlo actualizado con crontab -e:\n[ralf@servidor1]# crontab -l MAILTO=\u0026#34;usuario@ejemplo.com\u0026#34; # Generar estadisticas web todos los dias a las 12:01 y als 23:01 1 12,23 * * * /usr/local/bin/webalizer -c /etc/webalizer.conf # Limpiar copias de seguridad de la base de datos (guardar ultima # semana). Ejecutar trabajo de lunes a viernes a la 01:01 01 01 * * 1-5 for files in `/usr/bin/find /backups/pgsql/ -mmin +10000`; do rm -f $files; done # Ejecutar \u0026#39;mi_script.sh\u0026#39; un minuto pasado la hora en punto, cada dos horas. 01 */2 * * * /usr/local/bin/mi_script.sh En fin las posibilidades son muchas. Podeis usar vuestra imaginacion.\nAT # at tambien se puede usar para ejecutar, solamente una vez, un trabajo a una determinada hora. Al contrario de cron que ejecuta de forma periodica hasta que no definamos lo contrario en el crontab del usuario.\nPodemos definir los comandos a ejecutar o bien por la entrada estandar o en un fichero. El formato en uno u otro caso seria, at hora:minuto o at -f fichero hora:minuto. Un ejemplo nos aclarara las cosas. (^D significa que pulsamos las teclas Ctrl + D). Estos dos ejemplos hacen lo mismo (arrancan de nuevo la maquina) aunque se definen de manera distinta.:\n[ralf@servidor1]# at 12.12.2006 21:30 \u0026gt; reboot \u0026gt; ^D [ralf@servidor1]# cat /tmp/at_reboot reboot [ralf@servidor1]# at -f /tmp/at_reboot 12.12.2006 21:30 Para ver y borrar los trabajos definidos se pueden usar los comandos at -l y at -r \u0026lsquo;ID del trabajo a borrar\u0026rsquo;\nCombinando SSH, cron y at # El uso combinado de SSH sin clave y cron/at es una tecnica usada muchisimo por administradores de sistemas Unix/Linux con responsabilidad de muchas maquinas/servidores. Es una manera facil y eficaz de recoger informacion o ejecutar un trabajo en muchas maquinas de manera rapida. Vamos a poner un par de ejemplos para ver como podemos hacer esto.\nEstos ejemplo necesitan acceso como root para funcionar, lo mas facil es actualizar el fichero ~/.ssh/authorized_keys de \u0026lsquo;root\u0026rsquo; en las maquinas a acceder, con nuestra clave publica. Otra alternativa seria usar sudo.\nEn este ejemplo vamos a arrancar de nuevo todas las maquinas de nuestra subred 10.1.1.0/24 para que arranquen con el nuevo kernel que hemos instalado. El trabajo lo vamos a ejecutar mañana a las 23:00 horas.:\n[ralf@servidor1]# at 23:00 tomorrow \u0026gt;for ip in `seq 1 254`; do ssh root@10.1.1.${ip} \u0026#39;/sbin/shutdown -r now\u0026#39;; done \u0026gt;^D job 3 at 2006-10-05 23:00 [ralf@servidor1]# at -l 3 2006-10-05 23:00 a ralf En este otro ejemplo vamos a recoger los datos de uso de disco de todos los directorios /var de nuestros servidores, en nuestra subred 10.1.1.0/24. Esta lista la ordenaremos de mayor a menor uso, la grabaremos en /tmp/uso_var.txt y el trabajo lo ejecutaremos todos los dias a las 03:00:\n[ralf@servidor1]# export EDITOR=/usr/bin/emacs [ralf@servidor1]# crontab -e Actualizamos y grabamos crontab con estas dos lineas:\n# Uso de /var en 10.1.1.0/24 00 03 * * * for ip in `seq 1 254`; do echo -n 10.1.1.${ip}: ;ssh root@10.1.1.${ip} \u0026#39;/usr/bin/du -sk /var \u0026#39;; done | sort -r +2 \u0026gt; /tmp/uso_var.txt Supongo que os podreis hacer una idea de las posibilidades infinitas de automatizacion que tenemos con lo que hemos explicado y la ayuda indispensable que da a los administradores de sistemas Unix/Linux. En fin, espero que este articulo os ayude un poco mas a tener control de vuestros sistemas, esto es todo por hoy.\nEnlaces:\nhttp://en.wikipedia.org/wiki/Ssh http://en.wikipedia.org/wiki/RSA http://en.wikipedia.org/wiki/Public-key_encryption http://en.wikipedia.org/wiki/Cron http://en.wikipedia.org/wiki/At_%28Unix%29 http://es.wikipedia.org/wiki/Packet_sniffer http://es.wikipedia.org/wiki/Criptograf%C3%ADa_asim%C3%A9trica ","date":"2006/10/04","externalUrl":null,"permalink":"/es/blog/combinando-ssh-cron-y/","section":"Blogs","summary":"En este articulo vamos a ver como conectarse sin clave de acceso a otros ordenadores, de una manera segura, con ssh. Tambien veremos como configurar nuestro sistema para que trabaje por nosotros, automatizando tareas con cron y at. Y por ultimo, como combinando estas tecnicas, podemos ejecutar trabajos y recoger informacion de forma automatica de muchos ordenadores a un servidor central.","title":"Combinando SSH, cron y at","type":"blog"},{"content":"","date":"2006/10/01","externalUrl":null,"permalink":"/es/tags/bash/","section":"Tags","summary":"","title":"Bash","type":"tags"},{"content":"En nuestra cuarta entrega sobre la introduccion a el interprete de comandos Bash, vamos a ver una pequeña introduccion a las estructuras de control y bucles en Bash. Estas construcciones nos ayudan a controlar la ejecucion de un script y a obtener diversos resultados dependiendo de las condiciones que se cumplan o no cuando ejecutamos el script.\nEn Bash existen estas construcciones para controlar el flujo de ejecucion de un script:\nif/else: Ejecuta una serie de comandos dependiendo si una cierta condicion se cumple o no. for: Ejecuta una serie de comandos un numero determinado de veces. while: Ejecuta una seria de comandos mientras que una determinada condicion sea cumpla. until: Ejecuta una serie de comandos hasta que una determinada condicion se cumpla. case: Ejecuta una o varias listas de comandos dependiendo del valor de una variable. select: Permite seleccionar al usuario una opcion de una lista de opciones en un menu. La mayoria de condiciones utilizadas con estas construcciones son comparaciones de cadenas alfanumericas o numericas, valores de terminacion de comandos y comprobaciones de atributos de ficheros. Antes de seguir viendo como estas construcciones se pueden utilizar, vamos a ver como las condiciones se pueden definir.\nComparaciones de cadenas alfanumericas # Operador\tVerdad (TRUE) si: ------------------------------------------ cadena1 = cadena2\tcadena1 es igual a cadena2 cadena1 != cadena2\tcadena1 no es igual a cadena2 cadena1 \u0026lt; cadena2\tcadena1 es menor que cadena2 cadena1 \u0026gt; cadena 2\tcadena1 es mayor que cadena 2 -n cadena1\tcadena1 no es igual al valor nulo (longitud mayorque 0) -z cadena1\tcadena1 tiene un valor nulo (longitud 0) Comparacion de valores numericos # Operador\tVerdad (TRUE) si: ------------------------------------------ x -lt y\tx menor que y x -le y\tx menor o igual que y x -eq y\tx igual que y x -ge y\tx mayor o igual que y x -gt y\tx mayor que y x -ne y\tx no igual que y Comprobacion de atributos de fichero # Operador\tVerdad (TRUE) si: ------------------------------------------ -d fichero\tfichero existe y es un directorio -e fichero\tfichero existe -f fichero\tfichero existe y es un fichero regular (no un directorio, u otro tipo de fichero especial) -r fichero\tTienes permiso de lectura en fichero -s fichero\tfichero existe y no esta vacio -w fichero\tTienes permiso de escritura en fichero -x fichero\tTienes permiso de ejecucion en fichero (o de busqueda si es un directorio) -O fichero\tEres el dueño del fichero -G fichero\tEl grupo del fichero es igual al tuyo. fichero1 -nt fichero2\tfichero1 es mas reciente que fichero2 fichero1 -ot fichero2\tfichero1 es mas antiguo que fichero2 Podemos combinar varias condiciones con los simbolos \u0026lsquo;\u0026amp;\u0026amp;\u0026rsquo; (AND) y \u0026lsquo;||\u0026rsquo; (OR), y negar una condicion con \u0026lsquo;!\u0026rsquo;. Unos ejemplos mas adelante aclararan como utilizarlos.\nif/else # La sintaxis de esta construccion es la siguiente:\nif \u0026#34;condicion\u0026#34; then \u0026#34;comandos\u0026#34; [elif \u0026#34;condicion\u0026#34; then \u0026#34;comandos\u0026#34;] [else \u0026#34;comandos\u0026#34;] fi Como ya hemos dicho, podemos comprobar los valores de terminacion de un comando, y comparar cadenas alfanumericas/numericas y atributos de ficheros. Nada mejor que unos ejemplos para aclararnos las ideas.\n#!/bin/bash # # Comprobando terminacion de un comando # DIRECTORIO=\u0026#34;/tmp/test\u0026#34; COMANDO=\u0026#34;/bin/mkdir $DIRECTORIO\u0026#34; if $COMANDO then echo \u0026#34;$DIRECTORIO ha sido creado\u0026#34; else echo \u0026#34;$DIRECTORIO no pudo ser creado\u0026#34; fi #!/bin/bash # # Comparacion de cadenas alfanumericas # CADENA1=\u0026#34;uno\u0026#34; CADENA2=\u0026#34;dos\u0026#34; CADENA3=\u0026#34;\u0026#34; if [ $CADENA1 = $CADENA2 ]; then echo \u0026#34;\\$CADENA1 es igual a \\$CADENA2\u0026#34; elif [ $CADENA1 != $CADENA2 ]; then echo \u0026#34;\\$CADENA1 no es igual a \\$CADENA2\u0026#34; fi if [ -z $CADENA3 ]; then echo \u0026#34;\\$CADENA3 esta vacia\u0026#34; fi #!/bin/bash # # Comparacion de valores numericos # let NUM1=1 let NUM2=2 let NUM3=3 if [ $NUM1 -ne $NUM2 ] \u0026amp;\u0026amp; [ $NUM1 -ne $NUM3 ]; then echo \u0026#34;\\$NUM1 es diferente a \\$NUM2 y \\$NUM3\u0026#34; fi if [ $NUM1 -lt $NUM3 ]; then echo \u0026#34;\\$NUM1 es menor que \\$NUM3\u0026#34; fi for # La sintaxis de esta construccion es la siguiente:\nfor nombre [in lista] do comandos que pueden utilizar $nombre done Un ejemplo nos aclarara las cosas. Vamos a listar informacion en el DNS de una lista de direcciones web:\n#!/bin/bash for HOST in www.google.com www.altavista.com www.yahoo.com do echo \u0026#34;-----------------------\u0026#34; echo $HOST echo \u0026#34;-----------------------\u0026#34; /usr/bin/host $HOST echo \u0026#34;-----------------------\u0026#34; done while # La sintaxis de esta construccion es la siguiente:\nwhile condicion do comandos done\nUn ejemplo simple con while en donde escribimos el valor de una variable 10 veces, despues de incrementar su valor:\n#!/bin/bash NUM=0 while [ $NUM -le 10 ]; do echo \u0026#34;\\$NUM: $NUM\u0026#34; let NUM=$NUM+1 done until # La sintaxis de esta construccion es la siguiente:\nuntil condicion; do comandos done Un ejemplo simple con until en donde escribimos el valor de una variable 10 veces, despues de incrementar su valor:\n#!/bin/bash NUM=0 until [ $NUM -gt 10 ]; do echo \u0026#34;\\$NUM: $NUM\u0026#34; let NUM=$NUM+1 done case # La sintaxis de esta construccion es la siguiente:\ncase expresion in caso_1 ) comandos;; caso_2 ) comandos;; ...... esac Un ejemplo simple con case para aclarar las cosas:\n#!/bin/bash for NUM in 0 1 2 3 do case $NUM in 0) echo \u0026#34;\\$NUM es igual a cero\u0026#34;;; 1) echo \u0026#34;\\$NUM es igual a uno\u0026#34;;; 2) echo \u0026#34;\\$NUM es igual a dos\u0026#34;;; 3) echo \u0026#34;\\$NUM es igual a tres\u0026#34;;; esac done select # La sintaxis de esta construccion es la siguiente:\nselect nombre [in lista] do comandos que pueden utilizar $nombre done Un ejemplo simple para aclarar las cosas.\n#!/bin/bash select OPCION in opcion_1 opcion_2 opcion_3 do if [ $OPCION ]; then echo \u0026#34;Opcion elegida: $OPCION\u0026#34; break else echo \u0026#34;Opcion no valida\u0026#34; fi done Bueno esto es todo por hoy en nuestra introduccion a Bash. En el proximo articulo de esta serie veremos diferentes aspectos de la entrada y salida de datos en un script Bash.\n","date":"2006/10/01","externalUrl":null,"permalink":"/es/blog/bash-iv-estructuras-de-control-y-bucles/","section":"Blogs","summary":"En nuestra cuarta entrega sobre la introduccion a el interprete de comandos Bash, vamos a ver una pequeña introduccion a las estructuras de control y bucles en Bash. Estas construcciones nos ayudan a controlar la ejecucion de un script y a obtener diversos resultados dependiendo de las condiciones que se cumplan o no cuando ejecutamos el script.","title":"Bash (IV) - Estructuras de control y bucles","type":"blog"},{"content":"En este articulo vamos a ver una introduccion de diferentes tecnicas que se utilizan para que los sistemas informaticos esten disponibles y se puedan acceder incluso cuando alguna parte del sistema falla.\nCuando se tienen sistemas criticos que tienen que estar disponibles y funcionando 24 horas al dia, 365 dias al año, hay que intentar minimizar los fallos que puedan afectar al funcionamiento normal del sistema. Fallos van a ocurrir, pero existen tecnicas y configuraciones que ayudan a tener sistemas redundantes, en los que ciertas partes pueden fallar sin que esto afecte al funcionamiento del mismo.\nEn un sistema informatico actual, existen muchos componentes necesarios para que este funcione, cuantos mas componentes, mas probabilidad tenemos de que algo falle. Estos problemas pueden ocurrir en el propio servidor, fallos de discos, fuentes de alimentacion, tarjetas de red, etc y en la infraestructura necesaria para que el servidor se pueda utilizar, componentes de red, acceso a internet, sistema electrico, \u0026hellip;.\nA continuacion vamos a ir comentando algunas de las tecnicas usadas para obtener sistemas redundantes. El grado de redundancia de un sistema, dependera de su importancia y del dinero que perdamos cuando el sistema no esta disponible por un fallo. No nos merecera la pena invertir en \u0026lsquo;redundancia\u0026rsquo;, si la inversion necesaria para tener un sistema redundante cuesta mas de lo que perderiamos en dinero, reputacion y horas de trabajo, si el sistema fallara.\nLas tecnicas y configuraciones de las que hablamos a continuacion no son exclusivas de sistemas Linux. Se pueden aplicar en su gran mayoria a otros sistemas operativos y plataformas. Nosotros nos centraremos en Linux por ser el tema principal de \u0026ldquo;El rincon de Linux\u0026rdquo;.\nRedundancia de componentes en el servidor # Los componentes redundantes mas normales en un servidor suelen ser, los discos, las tarjetas de red y las fuentes de alimentacion. Existen servidores con multiples CPUs que incluso siguen trabajando sin problemas con alguna CPU o modulo de memoria estropeado.\nDiscos # Los discos duros son los dispositivos donde se graban los datos. El fallo mas comun en un servidor es el fallo de un disco duro. Si el servidor tiene solamemente un disco y este falla, fallara el servidor al completo y no podremos acceder a los datos contenidos en el mismo. Existen por ello tecnicas que nos ayudan a minimizar este problema y a que el servidor siga funcionando y no pierda datos incluso cuando falle algun disco duro. Lo mas normal tambien, es que se puedan sustituir los discos que fallan sin necesidad de apagar el servidor (HotSwap)\nLa tecnica mas comun es la llamada RAID (redundant array of independent disks). Con esta tecnica creamos un conjunto de discos redundantes que nos pueden ayudar, tanto a aumentar la velocidad y el rendimiento del sistema de almacenamiento, como a que el sistema siga funcionando aunque algun disco falle. Existen implementaciones por software y hardware y diferentes configuraciones RAID, siendo las mas comunes RAID1, RAID5 y RAID10.\nTarjetas de red # La tarjeta de red es el dispositivo que permite al servidor comunicarse con el resto del mundo. Es por ello muy comun que los servidores tengan como minimo 2 tarjetas de red, para garantizar que esta comunicacion no se corte en caso de fallo de una de las tarjetas.\nEn Linux existe ademas una tecnica llamada \u0026lsquo;Bonding\u0026quot;, por la cual podemos utilizar 2 o mas tarjetas de red como si fueran un unico dispositivo, sumando las capacidades de las mismas y teniendo redundancia en el caso que alguna de las tarjetas falle.\nFuentes de alimentacion # La fuente de alimentacion es la encargada de proporcionar electricidad al servidor. Tambien es comun que los servidores tengan 2 o mas fuentes de alimentacion conectadas a diferentes sistemas electricos, para garantizar el suministro en el caso que una de las fuentes o uno de los sistemas electricos fallen. Lo mas normal es que se puedan sustituir las fuentes de alimentacion que fallan sin necesidad de apagar el servidor (HotSwap). Otros componentes del sistema como routers, switches, cabinetes de discos, etc suelen utilizar la misma tecnica de redundancia.\nRedundancia en el suministro electrico # Todo componente electrico, y un servidor no podia ser menos, necesita un suministro constante de electricidad para funcionar. Fallos en este suministro, aunque sean por periodos muy cortos de tiempo, tendra consecuencias catastrofales para nuestro sistema. Y no solo necesitamos un suministro constante, tambien necesitamos que no tenga subidas y bajadas brusquedas que puedan estropear componentes electronicos.\nPara conseguir esto se pueden utilizar diferentes componentes segun el grado de proteccion que deseemos.\nSAI (UPS): Son baterias mas o menos avanzadas que se conectan entre el servidor y la fuente de suministro electrico. Garantizan un suministro constante y estable por un tiempo, dependiendo este de la capacidad de las mismas. Generadores electricos: Funcionan generalmente con diesel y se conectan entre los UPS y la red de suministro electrico. Solo entran en funcionamiento cuando el suministro se corta por mas de un determinado tiempo. Pueden suministrar electricidad por un tiempo indefinido siempre que tengan carburante en el tanque. Lineas independientes de suministros: En centros de datos grandes, se suelen tener al menos 2 conexiones diferentes e independientes a la red de suministro electrico. Si queremos redundancia en el sistema electrico, no hace falta decir que no solo los servidores tienen que tener dobles conexiones, routers, switches y en definitiva cualquier componente del sistema que utilice electricidad deberia de tener fuentes de alimentacion redundantes (conectadas). Como se suele decir, tu sistema solo sera tan seguro, estable y redundante como el componente mas debil del mismo. No es la primera vez, por ejemplo, que en un centro de datos, grupos de servidores con redundancia a todos lo niveles han quedado incomunicados porque estaban conectados a un switch que ha fallado por no tener un sistema redundante de suministro electrico.\nRedundancia en los componentes de red # De nada sirve tener servidores con componentes duplicados y redundantes y un suministro electrico constante y equilibrarado si algunos de los componentes de la red fallan y no podemos acceder al servidor.\nLos componentes mas normales en una red son:\nRouters (enrutador): Es un dispositivo que interconecta segmentos de red o redes enteras Switch (Conmutador): Es un dispositivo que interconecta dos o más segmentos de red Tarjeta de red o NIC: Es un dispositivo electrónico que permite a una DTE (Data Terminal Equipment), ordenador o impresora, acceder a una red y compartir recursos Cables de red: Para interconectar los diferentes componentes, existen muchos y variados tipos, siendo los mas comunes el cable de par trenzado y el de fibra optica Lineas de conexion: a la red de area amplia, WAN (por ejemplo Internet) Cualquiera de estos componentes puede fallar, dejando al sistema incomunicado. Pero existen tecnicas para evitar que esto ocurra, lo que se suele hacer es configurar la red, para que al menos existan 2 caminos diferentes entre dos componentes A y B. En el grafico siguiente teneis un esquema, en el que podeis ver como configurar una red con redundancia doble desde el servidor hasta Internet. De esta manera se puede estropear un router, un switch y una tarjeta de red a la vez sin que perdamos conectividad. El mismo esquema se podria ampliar para tener redundancia triple o cuadruple de los componentes.\nRedundancia de servidores, balanceo de cargas # Que ocurre si el suministro electrico funciona y la red funciona, pero nuestro servidor falla de tal manera que ninguno de los componentes redundantes que tiene pueda evitar el fallo y la caida del mismo. Existen diferentes tipos de configuraciones con varios servidores, que pueden ayudarnos con este problema. Son los llamados clusters, los hay de diferentes tipos, pero entre los mas usales esta el de balanceo de cargas con tolerancia a fallos. En este tipo de clusters, no solo no importa que uno o varios de los servidores deje de funcionar, sino que si necesitamos mas recursos para proporcionar un servicio, podemos incorporar nuevos servidores que incrementen la capacidad de proceso del cluster.\nLos componentes mas importantes de este tipo de clusters son, los sistemas de almacenamiento unicos entre todos los servidores que proporcionan un servicio y el dispositivo de balanceo de cargas, el cual puede ser un hardware especifico para este trabajo o implemtarse por software en un servidor normal. El proyecto para Linux mas importante sobre este tema es el denominado Linux virtual server (LVS).\nA continuacion teneis una serie de ejemplos de como se pueden organizar estos clusters, en donde el fallo de un servidor, no para el funcionamiento de un servicio. Cuando falla uno o varios servidores en el cluster, la capacidad de proceso del mismo se reduce, por lo que es importante tener siempre cierta capacidad sin usar para que en el caso de un fallo no se reduzca el tiempo de respuesta mucho.\nUn ejemplo de cluster con balaceo de cargas conectado a un cabinete de discos (Disk array) para almacenar la informacion. Tipico uso para servidores de ficheros y web.\nUn ejemplo de cluster con balaceo de cargas conectado una base de datos para almacenar la informacion. Tipico uso para web.\nUn ejemplo de cluster con balaceo de cargas para un sistema de correo que proporcione IMAP y SMTP a sus usuarios.\nEn fin, esto es todo lo que tenia pensado contar en esta introduccion a sistemas informaticos redundantes. Existe mucha informacion en Internet si quereis profundizar en el tema. Lo mas importante es tener conocimientos, de red y administracion y saber como funcionan los diferentes componentes de un sistema. La experiencia y estudios de estas materias os ayudaran a tener sistemas mas estables y redundantes.\n","date":"2006/09/23","externalUrl":null,"permalink":"/es/blog/sistemas-informaticos-redundantes/","section":"Blogs","summary":"En este articulo vamos a ver una introduccion de diferentes tecnicas que se utilizan para que los sistemas informaticos esten disponibles y se puedan acceder incluso cuando alguna parte del sistema falla.\nCuando se tienen sistemas criticos que tienen que estar disponibles y funcionando 24 horas al dia, 365 dias al año, hay que intentar minimizar los fallos que puedan afectar al funcionamiento normal del sistema. Fallos van a ocurrir, pero existen tecnicas y configuraciones que ayudan a tener sistemas redundantes, en los que ciertas partes pueden fallar sin que esto afecte al funcionamiento del mismo.\n","title":"Sistemas informaticos redundantes","type":"blog"},{"content":"En nuestra tercera entrega sobre el interprete de comandos Bash vamos a empezar a ver como podemos usar de forma practica la informacion que hemos visto en los articulos anteriores. Para empezar y antes de entrar en materia, nada mejor que un ejemplo del clasico \u0026ldquo;Hola Mundo\u0026rdquo; en Bash.\n#!/bin/bash # # Esto es un ejemplo en Bash del clasico \u0026#34;Hola Mundo\u0026#34; # echo \u0026#34;Hola Mundo\u0026#34; Como podeis ver, nada dificil para empezar. Empecemos a explicar un poco que significa cada linea:\n#!/bin/bash: Esta linea indica donde se encuentra el interprete de comandos en nuestro sistema. Por defecto todos los sistemas que tengan Bash instalado, lo tendran en el directorio /bin. Al utilizar esta linea, podremos ejecutar el script como un programa normal, ya que el sistema sabra que es un script en Bash y que tiene que hacer con el.\nSi el script de ejemplo lo hubiesemos grabado como ejemplo.sh, lo podriamos ejecutar de la siguiente manera:\n[ralfm@desktop]# chmod ugo+x ejemplo.sh [ralfm@desktop]# ./ejemplo.sh Hola Mundo # Esto es un ejemplo en Bash del clasico \u0026ldquo;Hola Mundo\u0026rdquo;: Esto es un comentario. Todas las lineas que empiecen con el simbolo \u0026lsquo;#\u0026rsquo; seran tratadas como comentarios y no se ejecutaran.\necho \u0026ldquo;Hola Mundo\u0026rdquo;: Esto es el comando que imprime la cadena de texto en pantalla.\nVariables # En todo script tendreis que trabajar con variables, mas tarde o mas temprano. Vamos a ver como se definen y usan. Una buena costumbre cuando definamos variables en Bash es utilizar letras mayusculas, esto no es necesario, pero nos ayudara a tener un script mas facil de entender.\nDefiniendo una variable # #!/bin/bash # # Esto es un ejemplo en Bash del clasico \u0026#34;Hola Mundo\u0026#34; # MENSAJE=\u0026#34;Hola Mundo\u0026#34; echo $MENSAJE Hemos definido una variable llamada MENSAJE con el valor \u0026ldquo;Hola Mundo\u0026rdquo;, y la hemos usado con el comando echo para escribir el valor de la misma. Las variables en Bash se definen como NOMBRE=valor (sin espacios antes o despues del simbolo \u0026lsquo;=\u0026rsquo;) y su valor se usa, poniendo el simbolo \u0026lsquo;$\u0026rsquo; delante del nombre de la variable, $NOMBRE.\nSi al utilizar el valor de una variable, el nombre de variable esta seguido de un caracter que sea otra letra, numero o el simbolo \u0026lsquo;_\u0026rsquo;, tendremos que utilizar los simbolos \u0026lsquo;{}\u0026rsquo; alrededor del nombre de la variable.\n#!/bin/bash FICHERO=\u0026#34;registro\u0026#34; echo ${FICHERO}_2006.txt Usando variables de entorno # #!/bin/bash echo \u0026#34;El usuario \u0026#39;$USERNAME\u0026#39; ha ejecutado el script $0, en el ordenador \u0026#39;$HOSTNAME\u0026#39;. \u0026#34; Tambien se pueden usar en nuestro scripts, variables que no hemos definido nosotros. Estas variables son las llamadas \u0026lsquo;variables de entorno\u0026rsquo; del sistema. Teneis una lista con las mas importantes por defecto, en el segundo articulo de esta serie, Bash (II) - Comandos, variables de entorno y combinaciones de teclas. En nuestro ejemplo hemos utilizado $USERNAME y $HOSTNAME para obtener el nombre de usuario y del ordenador. La variable $0 contiene el nombre del script, mas adelante explicaremos esto.\nAsignado resultados de comandos a variables # #!/bin/bash ATRIBUTOS_SCRIPT=`/bin/ls -l $0` echo \u0026#34;El usuario \u0026#39;$USERNAME\u0026#39; ha ejecutado el script $0, en el ordenador \u0026#39;$HOSTNAME\u0026#39;. \u0026#34; echo \u0026#34;Los atributos del script son: \u0026#34; echo $ATRIBUTOS_SCRIPT Tambien podemos asignar la salida que producen los comandos del sistema a una variable. En nuestro ejemplo hemos asignado la salida del comando \u0026rsquo;ls -l a una variable llamada ATRIBUTOS_SCRIPT. Esto nos sera muy util en nuestros scripts para obtener informacion del sistema que podremos utilizar en nuestros scripts.\nUsando caracteres especiales en variables # Existen una serie de caracteres que tienen un significado especial en Bash, por ejemplo $ y \u0026ldquo;. Si queremos usar literalmente estos caracteres en el valor de una variable tendremos que usar el simbolo \u0026lsquo;' delante de ellos.\n#!/bin/bash MENSAJE=\u0026#34;\\\u0026#34;Hola Mundo ...\\\u0026#34;\u0026#34; echo \u0026#34;El valor de la variable \\$MENSAJE es $MENSAJE\u0026#34; Variables numericas # Si queremos definir variables numericas para su utilizacion en scripts Bash podemos utilizar el comando let. Nada mejor que un ejemplo para ver como se trabaja con variables numericas.\n#!/bin/bash let A=100 let B=200 let C=$A+$B echo \u0026#34;A: $A | B: $B | C: $C\u0026#34; Funciones # En Bash se pueden definir funciones. Una funcion en Bash (denominada subrutina o procedimiento en otros lenguajes de programacion) se podria definir como un script dentro de un script. Sirve para organizar un script en unidades logicas de manera que sea mas facil mantenerlo y programarlo. En Bash las funciones se pueden definir de la siguiente manera:\nfunction nombre_de_funcion(){ comandos_del_shell } Un ejemplo de funcion en un script:\n#!/bin/bash let A=100 let B=200 # # Funcion suma() # Suma los variables A y B # function suma(){ let C=$A+$B echo \u0026#34;Suma: $C\u0026#34; } # # Funcion resta() # Resta los variables A y B # function resta(){ let C=$A-$B echo \u0026#34;Resta: $C\u0026#34; } suma resta En fin, esto es todo por hoy. En la proxima entrega hablaremos de las estructuras condicionales: if/else, for, case, select y while/until.\n","date":"2006/09/03","externalUrl":null,"permalink":"/es/blog/bash-iii-variables-y-funciones/","section":"Blogs","summary":"En nuestra tercera entrega sobre el interprete de comandos Bash vamos a empezar a ver como podemos usar de forma practica la informacion que hemos visto en los articulos anteriores. Para empezar y antes de entrar en materia, nada mejor que un ejemplo del clasico “Hola Mundo” en Bash.","title":"Bash (III) - Variables y funciones","type":"blog"},{"content":"En este corto articulo tratamos el tema de como cambiar los permisos de ficheros y directorios en nuestro sistema Linux. Todo los comandos y ejemplos que se citan deben ejecutarse desde la linea de comandos en una terminal. Tambien decir que existen programas en modo grafico donde se puede conseguir lo mismo que aqui se explica a golpe de raton.\nLo primero que hay que decir es que para conseguir toda la información sobre los comandos involucrados en el tema de permisos podeis consultar los comandos man chmod, man chown y man chgrp\nInformación de un fichero/directorio # Cuando obtienes información sobre un fichero/directorio con el comando ls, existen diferentes campos que te dicen que clase de permisos el fichero/directorio tiene.\nEjemplo: [user@localhost]# ls -l -rwxr-x--- 1 pepito depart1 4348 Nov 24 16:19 test En la primera columna se pueden ver una serie de letras y guiones -rwxr-x\u0026mdash;, estas letras nos dicen quien en el sistema, y que clases de permisos tiene el fichero test.\nEstas letras están agrupadas en tres grupos con tres posiciones cada uno, más una primera posición que nos dice de que clase de archivo se trata (los mas normales (d) directorios, o (-) archivos de datos). En nuestro ejemplo la primera posición es (-) con lo cual el archivo test, es un archivo de datos (binario/ejecutable en este ejemplo).\nEl primer grupo de tres (rwx en nuestro caso) nos dice que clase de permisos tiene el dueño del fichero (u)(user/owner)\nEl segundo grupo de tres (r-x en nuestro caso) nos dice que clase de permisos tiene el grupo del fichero (g)(group).\nY el último grupo de tres (\u0026mdash; en nuestro caso) nos dice que clase de permisos tienen todos los demás usuarios del sistema sobre este fichero (o)(others).\nr :significa permiso para leer w :significa permiso para escribir x :significa permiso para ejecutar La segunda columna pepito, nos dice quien es el dueño del fichero,(pepito en este caso).\nLa tercera columna depart1, nos dice cual es el grupo del fichero (depart1 en este caso).\nLa cuarta columna 4348, nos dice el tamaño del fichero.\nLa quinta columna Nov 24 16:19, nos dice cual es la fecha y hora de la última modificación.\nLa sexta columna test, nos dice cual es el nombre del fichero/directorio.\nAsi pues, el fichero test de nuestro ejemplo tiene los siguientes permisos:\npepito puede leer, escribir/modificar, y ejecutar el fichero test. Los usuarios pertenecientes al grupo depart1 puede leer, y ejecutar pero no escribir/modificar. Los demás usuarios no pueden hacer nada, ni leerlo, ni escribir/modificar, ni ejecutarlo. Como cambiar los permisos/dueño/grupo de un fichero/directorio? # Para cambiar el dueño del fichero se utiliza el comando : chown usuario fichero\nPara cambiar el grupo del fichero se utiliza el comando: chgrp grupo fichero\nPara cambiar los permisos se utiliza el comando: chmod permisos fichero\nLos permisos se pueden especificar de diferentes maneras, una serie de ejemplos, es lo mejor para comprenderlo:\nchmod ugo+rwx test (da permisos rwx a todos, user,group,others) chmod ugo-x test (quita permiso x (ejecucion) a todos, user,group,others) chmod o-rwx test (quita permisos rwx a others) chmod u=rwx,g=rx test (da permisos rwx a user, rx a group y ninguno a others) Asi podriamos continuar con todas las posibles combinaciones de letras, es cuestión de usar la imaginación ;-)\nExiste otro metodo que utiliza numeros, en vez de letras para asignar permisos, la siguiente tabla nos puede ayudar un poco a comprender esta manera:\nr w x VALOR DECIMAL 0 0 0 0 (000 binario es 0 en decimal) 0 0 1 1 ......... 0 1 0 2 ......... 0 1 1 3 ......... 1 0 0 4 (100 binario es 4 en decimal) 1 0 1 5 ......... 1 1 0 6 ......... 1 1 1 7 (111 binario es 7 en decimal) 1 significa activado y 0 desactivado, o sea 101, activa r y x, y desactiva w. Sabiendo esto solo tenemos que usar el valor decimal para dar solo permisos de lectura y ejecucion, un ejemplo aclarara esto.\nchmod 750 test da permisos rwx al usuario (7=111) da permisos r-x al grupo (5=101) da permisos --- a los demas (0=000) Esto es todo por hoy, esperamos que tengais un poco mas claro lo de los permisos de ficheros en Linux y que le vayais perdiendo el miedo a la linea de comandos\n","date":"2006/08/25","externalUrl":null,"permalink":"/es/blog/como-se-cambian-los-permisos-de-ficheros-y-directorios-en-linux/","section":"Blogs","summary":"En este corto articulo tratamos el tema de como cambiar los permisos de ficheros y directorios en nuestro sistema Linux. Todo los comandos y ejemplos que se citan deben ejecutarse desde la linea de comandos en una terminal. Tambien decir que existen programas en modo grafico donde se puede conseguir lo mismo que aqui se explica a golpe de raton.","title":"Cómo se cambian los permisos de ficheros y directorios en Linux?","type":"blog"},{"content":"En este articulo intentaremos explicar lo mas brevemente posible, como los directorios de un sistema Linux/Unix estan organizados y para que se usan. Uno de los problemas que tienen los nuevos usuarios de un sistema Linux/Unix es el no saber que significan y para que se utilizan los diferentes directorios del sistema. No preocuparos, en un principio puede pareceros dificil y sin logica, pero una vez que empeceis a usarlos os acostumbrais pronto.\nExiste un estandard, el \u0026ldquo;Estándar de jerarquía de ficheros\u0026rdquo; (FHS - Filesystem Hierarchy Standard) que intenta definir unas bases, para que tanto los programas del sistema, como los usuarios y administradores, sepan donde encontrar lo que buscan. Este estandard se encuentra en su version 2.3 y el documento del mismo se puede encontrar en su totalidad en esta direccion: http://www.pathname.com/fhs/pub/fhs-2.3.html. Se recomienda su lectura a los deseen profundizar en el tema.\nEste estandard esta mantenido por la \u0026lsquo;Free Standards Group\u0026rsquo;, una organización sin fines de lucro constituida por compañías de hardware y software como AMD, Computer Associates, Debian, Dell, Fujitsu, Google, HP, IBM, Intel, MySQL, NEC, Novell, Red Flag, Red Hat, Sun Microsystems, Veritas y otros muchos. La mayoría de las distribuciones de Linux, inclusive las que forman parte de Free Software Standards, no aplican de forma estricta y al 100% el estándar, aunque las diferencias son minimas.\nExisten dos tipos de distinciones cuando hablamos del tipo de contenido de un directorio: Estaticos/dinamicos y compartibles/no compartibles.\nEstaticos: Contiene binarios, bibliotecas, documentacion y otros ficheros que no cambian sin intervencion del administrador. Pueden estar en dispositivos de solo lectura (read-only) y no necesitan que se hagan copias de seguridad tan a menudo como con ficheros dinamicos Dinamicos: Contiene ficheros que no son estaticos. Deben de encontrase en dispositivos de lectura-escritura (read-write). Necesitan que se hagan copias de seguridad a menudo Compartibles: Contiene ficheros que se pueden encontrar en un ordenador y utilizarse en otro No compartibles: Contiene ficheros que no son compartibles A continuacion teneis algunos ejemplos para aclarar ideas:\nEstaticos: /bin, /sbin, /opt, /boot, /usr/bin Dinamicos: /var/mail, /var/spool, /var/run, /var/lock, /home Compartibles: /usr/bin, /opt No compartibles: /etc, /boot, /var/run, /var/lock Todos los ficheros y directorios aparecen debajo del directorio raíz «/» (El equivalente en el mundo Unix al C:\\ de Windows) aunque se encuentren en discos/dispositivos distintos. En Linux/Unix no existen letras de discos (C:, D:, etc) Los dispositivos se \u0026lsquo;montan\u0026rsquo; (empiezan a formar parte) del arbol de directorios del sistema, pero esto lo explicaremos en otra ocasion.\nA continuacion teneis una lista con los directorios mas importantes del sistema y para que se usan. Para acceder a los mismos podeis usar el comando cd \u0026rsquo;nombre del directorio\u0026rsquo;. Para ver el contenido de los mismos podeis usar el comando ls -l \u0026rsquo;nombre del directorio\u0026rsquo;.\nDirectorio Descripción ----------------------------------------------------------------------------------------- /bin/\tComandos/programas binarios esenciales (cp, mv, ls, rm, etc.), /boot/\tFicheros utilizados durante el arranque del sistema (núcleo y discos RAM) /dev/\tDispositivos esenciales, discos duros, terminales, sonido, video, lectores dvd/cd, etc /etc/\tFicheros de configuración utilizados en todo el sistema y que son específicos del ordenador /etc/opt/\tFicheros de configuración utilizados por programas alojados dentro de /opt/ /etc/X11/\tFicheros de configuración para el sistema X Window (Opcional) /etc/sgml/\tFicheros de configuración para SGML (Opcional) /etc/xml/\tFicheros de configuración para XML (Opcional) /home/\tDirectorios de inicios de los usuarios (Opcional) /lib/\tBibliotecas compartidas esenciales para los binarios de /bin/, /sbin/ y el núcleo del sistema. /mnt/\tSistemas de ficheros montados temporalmente. /media/\tPuntos de montaje para dispositivos de medios como unidades lectoras de discos compactos. /opt/\tPaquetes de aplicaciones estáticas. /proc/\tSistema de ficheros virtual que documenta sucesos y estados del núcleo. Contiene principalmente ficheros de texto. /root/\tDirectorio de inicio del usuario root (super-usuario) (Opcional) /sbin/\tComandos/programas binarios de administración de sistema. /tmp/\tFicheros temporales /srv/\tDatos específicos de sitio servidos por el sistema. /usr/\tJerarquía secundaria para datos compartidos de solo lectura (Unix system resources). Este directorio puede ser compartido por múltiples ordenadores y no debe contener datos específicos del ordenador que los comparte. /usr/bin/\tComandos/programas binarios. /usr/include/\tFicheros de inclusión estándar (cabeceras de cabecera utilizados para desarrollo). /usr/lib/\tBibliotecas compartidas. /usr/share/\tDatos compartidos independientes de la arquitectura del sistema. Imágenes, ficheros de texto, etc. /usr/src/\tCódigos fuente (Opcional) /usr/X11R6/\tSistema X Window, versión 11, lanzamiento 6 (Opcional) /usr/local/\tJerarquía terciaria para datos compartidos de solo lectura específicos del ordenador que los comparte. /var/\tFicheros variables, como son logs, bases de datos, directorio raíz de servidores HTTP y FTP, colas de correo, ficheros temporales, etc. /var/cache/\tCache da datos de aplicaciones. /var/crash/\tDepósito de información referente a caidas del sistema (Opcional) /var/games/\tDatos variables de aplicaciones para juegos (Opcional) /var/lib/\tInformación de estado variable. Algunos servidores como MySQL y PostgreSQL almacenan sus bases de datos en directorios subordinados de éste. /var/lock/\tFicheros de bloqueo. /var/log/\tFicheros y directorios de registro del sistemas (logs). /var/mail/\tBuzones de correo de usuarios (Opcional) /var/opt/\tDatos variables de /opt/. /var/spool/\tColas de datos de aplicaciones. /var/tmp/\tFicheros temporales preservados entre reinicios. Espero que esta informacion os sirva para comprender un poco mas donde encontrar informacion en vuestro sistema.\n","date":"2006/08/21","externalUrl":null,"permalink":"/es/blog/organizacion-de-los-directorios-en-linux/","section":"Blogs","summary":"En este articulo intentaremos explicar lo mas brevemente posible, como los directorios de un sistema Linux/Unix estan organizados y para que se usan. Uno de los problemas que tienen los nuevos usuarios de un sistema Linux/Unix es el no saber que significan y para que se utilizan los diferentes directorios del sistema. No preocuparos, en un principio puede pareceros dificil y sin logica, pero una vez que empeceis a usarlos os acostumbrais pronto.","title":"Organizacion de los directorios en Linux","type":"blog"},{"content":"En este segundo articulo sobre el interprete de comandos bash, vamos a ver tres cosas importantes cuando trabajamos con bash:\nLos comandos y palabras reservadas Las variables de entorno Combinaciones especiales de teclas Estas tres cosas nos van a ayudar a trabajar y a escribir scripts y ficheros de configuracion en bash, a conseguir informacion sobre el interprete de comandos y a hacernos nuestro dias como administradores mucho mas faciles y llevaderos (siempre que usemos Bash como nuestro interprete de comandos).\nComandos y palabras reservadas # Aqui tenemos los comandos y palabras reservadas mas importantes que se pueden utilizar con bash, tanto desde scripts como desde la linea de comandos. Mas adelante en esta serie de articulos explicaremos y daremos ejemplos de como usarlos.\nComando\tExplicacion ----------------------------------------------------------------------------- !\tPalabra reservada. Valor logico NOT del codigo de retorno de un comando :\tNo hace nada (expande cualquier argumento) .\tLee un fichero y ejecuta su contenido en el interprete de comando actual\talias\tConfigura un \u0026#39;alias\u0026#39; para un comando o linea de comandos bg\tPone un trabajo en \u0026#39;background\u0026#39; bind\tAsigna una secuencia de teclas a una funcion \u0026#39;readline\u0026#39; o macro break\tSale de un bucle for, select, while o until builtin\tEjecuta el interprete de comandos especificado case\tPalabra reservada. Construccion condicional cd\tCambia el directorio de trabajo actual. command\tEjecuta un comando sin pasar por la funcion de busqueda del interprete de comandos. continue\tSalta a la siguiente interacion en un bucle for, select, while o until declare\tDefine variables y les da atributos dirs\tMuestra la lista actual de directorios recordados disown\tRemueve un trabajo/proceso de la tabla de trabajod/procesos do\tPalabra reservada. Parte de un bucle for, select, while o until done\tPalabra reservada. Parte de un bucle for, select, while o until echo\tExpande e imprime cualquier argumento elif\tPalabra reservada. Parte de una construccion if else\tPalabra reservada. Parte de una construccion if enable\tEnable and disable built-in shell commands esac\tPalabra reservada. Parte de una construccion case. eval\tEjecuta los argumentos dados a traves de la linea de comandos exec\tReemplaza el interprete de comandos con el programa definido exit\tSale de el interprete de comandos export\tCrea variables de entorno fc\tEdita el fichero con la historia de comandos usados fg\tPone un trabajo/proceso en background a foreground fi\tPalabra reservada. Parte de un construccion if. for\tPalabra reservada. Bucle de tipo for. function\tDefine una funcion. getopts\tProcesa opciones de la linea de comandos. hash\tRutas de acceso completas son determinadas y recordadas help\tMuestra informacion sobre comandos embedidos. history\tMuestra la historia de comandos usados if\tPalabra reservada. Construccion condicional de tipo if in\tPalabra reservada. Parte de una construccion condicional de tipo case jobs\tMuestra una lista con trabajos/procesos ejecutandose en background kill\tManda una signal a un proceso let\tAsigna una variable aritmetica local\tcrea una variable local logout\tSale de un interprete de comando de tipo login popd\tRemueve un directorio del \u0026#39;stack\u0026#39; de directorios pushd\tAñade un directorio al \u0026#39;stack\u0026#39; de directorios pwd\tMuestra el directorio de trabajo actual. read\tLee una linea en el \u0026#39;standard input\u0026#39; readonly\tHace las variable del tipo solo lectura return\tRetorna de una funcion o script select\tPalabra reservada. Construccion del tipo generacion de menus. set\tDefine opciones shift\tCambia argumentos de la linea de comandos. suspend\tSuspende la ejecucion de un interprete de comandos. test\tEvalua una expresion condicional. then\tPalabra reservada. Parte de una construccion if. time\tPalabra reservada. Ejecuta un comando y muestra los tiempos de ejecucion. El formato de salida puede ser controlado con TIMEFORMAT times\tMuestra los tiempos de usuario y sistema acumulados para procesos ejecutados desde el interprete de comandos trap\tDefine una rutina para atrapar una \u0026#39;signal\u0026#39; type\tIdentifica la fuente de un comando typeset\tDefine variables y les da atributos. Igual que \u0026#39;declare\u0026#39; ulimit\tDefine/muestra los limites de recursos para los procesos umask\tDefine/muestra la mascara de los permisos de ficheros unalias\tRemueve definiciones de alias unset\tRemueve definiciones de variables o funciones until\tPalabra reservada. Bucle de tipo until wait\tEspera a que trabajos/procesos en background terminen de ejecutarse while\tPalabra reservada. Bucle de tipo while Variables de entorno # A continuacion tenemos la lista de variables reservadas por el interprete de comandos mas comunes. Todas ellas tienen un significado especial para el mismo, algunas de ellas solo se pueden leer, a otras se le asignan ciertos valores automaticamente y algunas pierden su significado si le cambiamos los valores que tienen por defecto.\nVariable Explicacion --------------------------------------------------------------------------- CDPATH\tUna lista de directorios separados por el signo \u0026#39;:\u0026#39; usada como ruta de acceso por el comando cd HOME\tEl directorio principal de usuario IFS\tUna lista de caracteres para separar campos; usado cuando el interprete de comandos separa palabras como parte de una expansion. MAIL\tSi este parametro tiene un fichero definido y la variable MAILPATH no esta definida, bash informa al usuario de la llegada de correo al fichero especificado. MAILPATH\tUna lista de ficheros separada por comas, en los cuales el interprete de comandos comprueba periodicamente de la llegada de correo.\tOPTARG\tEl valor del ultimo argumento procesado por getopts. OPTIND\tEl indice del ultimo argumento procesado por getopts PATH\tUna lista de directorios, separados por comas, en los cuales el interprete de comandos busca por comandos PS1\tPrompt principal. El valor por defecto es “\u0026#39;\\s-\\v\\$ \u0026#39; PS2\tEl prompt secundario. El valor por defecto es \u0026#39;\u0026gt; \u0026#39;\tauto_resume\tEsta variable controla como el interprete de comandos interaciona con el control de usuario y trabajos/procesos BASH\tLa ruta de acceso completa usada para ejecutar la instancia actual de bash BASH_ENV\tSi esta variable esta definida cuando bash es llamado para ejecutar un script, su valor es expandido y usado como el nombre del fichero leido antes de ejecutar el script. BASH_VERSION\tEl numero de version de bash usada BASH_VERSINFO\tUna matriz de solo lectura con informacion sobre la version de bash usada. COLUMNS\tUsada por \u0026#39;select\u0026#39; para determinar el ancho de la terminal cuando imprime listas de menus. COMP_CWORD\tUn indice en ${COMP_WORDS} de la palabra conteniendo la posicion del puntero actual COMP_LINE\tLa linea de comando actual COMP_POINT\tEl indice de la posicion relativa del puntero actual con respecto al comienzo del comando actual COMP_WORDS\tUna matriz con las palabras individuales en la linea de comando actual COMPREPLY\tUna matriz de donde bash lee las palabras posibles generadas por una funcion del interprete de comandos usada por la utilidad de generacion de terminos posibles. DIRSTACK\tUna matriz que contiene los contenidos actuales del stack de directorios EUID\tEl identificador numerico de usuario del usuario actual FCEDIT\tEl editor usado por defecto por la opcion -e del comando \u0026#39;fc\u0026#39; FIGNORE\tUna lista separada por comas de sufijos a ignorar cuando se efectua la generacion de posibles nombres de ficheros. FUNCNAME\tEl nombre de la funcion que se esta ejecutando actual GLOBIGNORE\tUna lista separada por comas de los patrones que definen el conjunto de nombres de ficheros a ignorar cuando se efectua la generacion de posibles nombres GROUPS\tUna matriz que contiene la lista de los grupos a que pertenece el usuario actual HISTCMD\tEl indice del comando actual en la historia de comandos HISTCONTROL\tDefine si un comando es ańadido a la historia de comandos HISTFILE\tEl nombre del fichero en el cual se graba la historia de comandos de comandos. El valor por defecto es ~/.bash_history HISTFILESIZE\tEl numero maximo de lineas contenidas en la historia de comandos, por defecto 500 HISTIGNORE\tUna lista separada por comas de los patrones usados para definir que comandos deben de grabarse en la historia de comandos HISTSIZE\tEl maximo numero de comandos a recordar en la historia de comandos, por defecto 500 HOSTFILE\tContiene el nombre de un fichero en el mismo formato que /etc/hosts que deberia de usarse cuando el interprete de comandos necesita completar un nombre de maquina (hostname) HOSTNAME\tEl nombre de maquina actual HOSTTYPE\tCadena describiendo la maquina que esta ejecutando Bash IGNOREEOF\tControla la accion a tomar cuando el interprete de comandos recibe un caracter EOF INPUTRC\tNombre del fichero de inicializacion de \u0026#39;Readline\u0026#39;, sobreescribiendo el valor por defecto /etc/inputrc. LINES\tUsada para determinar la anchura de la columna usada para imprimir listas MACHTYPE\tCadena describiendo el tipo de sistema que esta ejecutando Bash MAILCHECK\tFrecuencia de comprobacion (en segundos) del correo electronico en el fichero definido en las variables MAILPATH o MAIL OLDPWD\tDirectorio previo definido por el comando \u0026#39;cd\u0026#39; OSTYPE\tCadena describiendo el sistema operativo que esta ejecutando Bash PPID\tEl numero de proceso del proceso padre del interprete de comandos PS3\tEl valor de esta variable se usa como \u0026#39;prompt\u0026#39; PWD\tDirectorio actual definido por el comando \u0026#39;cd\u0026#39; RANDOM\tCuando se llama esta variable un numero entero entre 0 32767 es generado SECONDS\tNumero de segundos desde que Bash fue arrancado SHELLOPTS\tLista con opciones de Bash activadas UID\tEl valor numerico real del usuario actual Combinaciones especiales de teclas # Cuando usamos bash existen una serie de combinaciones de teclas que se pueden utilizar para editar y realizar operaciones usuales.\nExisten dos modos de edicion, mode emacs y modo vi. El modo por defecto es emacs, pero para los que estan acostumbrados a utilizar el editor \u0026lsquo;vi\u0026rsquo;, no es dificil cambiar entre los modos.\nPara cambiar de modos podeis ejecutar estos comandos:\n$ set -o emacs $ set -o vi Nosotros nos vamos a centrar en el modo de edicion emacs, al ser el modo por defecto y el mas usado. A continuacion teneis las combinaciones mas usuales (aunque no son las unicas):\nPara moverse por la linea de comandos: # Ctrl + A Ir al principio de linea Ctrl + E Ir al final de linea ESC + B\tIr una palabra hacia atras ESC + F\tIr una palabra hacia adelante Ctrl + B Ir una letra hacia atras Ctrl + F Ir una letra hacia adelante Para moverse por el historial de comandos ejecutados: # Ctrl + N Proxima linea en el historial Ctrl + P Previa linea en el historial Ctrl + R Busqueda atras en el historial Ctrl + S Busqueda adelante en el historial Para borrar parte de la linea de comandos: # Ctrl + U Borra de la posicion actual al principio de la linea Ctrl + K Borra de la posicion actual al final de la linea Ctrl + W Borra de la posicion actual al principio de la palabra ESC + D\tBorra de la posicion actual al final de la palabra Ctrl + D Borra el caracter actual hacia adelante Ctrl + Y Deshace el ultimo borrado Transformaciones: # Ctrl + T Intercambiar dos letras ESC + C\tCambiar a mayuscula la primera letra de la primera palabra despues de la posicion actual ESC + L\tCambiar a minusculas la primera palabra despues de la posicion actual ESC + T\tIntercambiar dos palabras ESC + U\tCambiar a mayusculas la primera palabra despues de la posicion actual TAB + TAB Autocompleta palabras (comandos, ficheros, directorios, variables etc) con posibles valores En nuestra proxima entrega empezaremos a ver como usar la informacion de este articulo para trabajar con bash y empezar a escribir nuestros primeros scripts.\n","date":"2006/08/19","externalUrl":null,"permalink":"/es/blog/bash-ii-comandos-variables-de-entorno-y-combinaciones-de-teclas/","section":"Blogs","summary":"En este segundo articulo sobre el interprete de comandos bash, vamos a ver tres cosas importantes cuando trabajamos con bash: Los comandos y palabras reservadas, Las variables de entorno, Combinaciones especiales de teclas","title":"Bash (II) - Comandos, variables de entorno y combinaciones de teclas","type":"blog"},{"content":"En esta serie de articulos sobre el interprete de comandos Bash, intentaremos explicar de una manera sencilla como configurar, utilizar y programar en Bash. Existen otros interpretes de comandos totalmente validos y potentes, pero nosotros nos vamos a centrar en Bash por ser el mas usado.\nTodo administrador de sistemas UNIX en general y Linux en particular, deberia de aprender un minimo de programacion en Bash para automatizar y administrar tareas y trabajos en el sistema. Las posibilidades som muchas y una vez que se le coge el gusto a este lenguaje de programacion, no te puedes imaginar un dia como administrador sin hacer uso del mismo.\nProgramar en Bash es facil (aunque a algunos no se lo parezca al principio) y las posibilidades de uso casi infinitas.\nIntroduccion # Un interprete de comandos es un programa que funciona como interfaz de usuario con el sistema operativo. Es el encargado de traducir los comandos de los usuarios a instrucciones que el sistema operativo pueda entender y viceversa, traducir el resultado devuelto por el sistema operativo a un lenguaje que los usuarios podamos entender.\nBash ha sido escrito por el Proyecto GNU y pertenece a la categoria de interfaz de usuarios en modo caracter o texto.\nEste articulo supone que ya teneis Bash instalado en vuestro sistema (por defecto la mayoria de distribuciones instalan y configuran Bash como interprete de comandos del sistema) Para utilizar Bash teneis que usar el sistema en modo texto o abrir una consola de texto en modo grafico.\nComo podemos configurar nuestro interprete de comandos # Por defecto, la distribucion que utilicemos configura Bash para que podamos empezar a utilizarlo inmediatamente. Pero si queremos cambiar o añadir otros parametros de configuracion, es bueno saber donde tenemos que hacerlo.\nExisten tres ficheros en el directorio de un usuario que tienen un significado especial para el shell Bash. Estos ficheros permiten al usuario configurar el entorno de su cuenta automaticamente cuando entra en el sistema, cuando arranca un subshell o ejecutar comandos cuando sale del sistema.\nLos nombres de estos ficheros son .bash_profile, .bashrc y .bash_logout. Si ninguno de estos ficheros existe en el directorio del usuario, /etc/profile es utilizado por el sistema como fichero de configuracion de bash.\n.bash_profile es el el mas importante de los tres. Es leido y los comandos incluidos en el, ejecutados, cada vez que el usuario entra en el sistema. Cualquier cambio hecho en este fichero no tendra efecto hasta que salgamos y entremos en el sistema de nuevo. Una alternativa para no tener que salir del sistema es ejecutar el comando source .bash_source.\nBash permite dos sinonimos para este fichero, .bash_login (derivado del C shell) y .profile (derivado del Bourne y Korn shell). Si .bash_profile no existe, el sistema buscara primero .bash_login y luego .profile. Solamente uno de estos ficheros es leido, en el caso que existan simultaneamente.\n# Ejemplo de .bash_profile # Get the aliases and functions if [ -f ~/.bashrc ]; then . ~/.bashrc fi # User specific environment and startup programs BASH_ENV=$HOME/.bashrc USERNAME=\u0026#34;\u0026#34; PATH=$PATH:/usr/local/pgsql/bin MANPATH=$MANPATH:/usr/local/pgsql/man PGLIB=/usr/local/pgsql/lib PGDATA=/usr/local/pgsql/data export USERNAME BASH_ENV PATH MANPATH PGLIB PGDATA .bashrc es leido cuando el usuario arranca un subshell, escribiendo por ejemplo bash en la linea de comandos. Esto nos permite ejecutar diferentes comandos para la entrada al sistema o para la ejecucion de un subshell. Si el usuario necesita los mismos comandos tanto a la entrada como en subshells, podemos incluir la siguiente linea en .bash_profile:\nsource .bashrc # Ejemplo de .bashrc # User specific aliases and functions alias ll=\u0026#34;ls -l --color\u0026#34; alias lal=\u0026#34;ls -la --color\u0026#34; alias faq=\u0026#34;cd /home/rafael/EL_RINCON/FAQ/\u0026#34; alias php=\u0026#34;cd /home/rafael/EL_RINCON/PHP/\u0026#34; # Source global definitions if [ -f /etc/bashrc ]; then . /etc/bashrc fi .bash_logout es el fichero leido por Bash, cuando salimos del sistema. Podemos definir, por ejemplo que se borren los ficheros temporales creados en nuestra ultima sesion o registrar el tiempo que hemos estado utilizando el sistema. Si .bash_logout no existe, ningun comando sera ejcutado a nuestra salida.\n# ejemplo de .bash_logout clear En la proxima entrega veremos una serie de parametros que podemos utilizar en nuestros ficheros e configuracion y como podemos utilizar una serie de combinaciones especiales de teclas para realizar tareas que nos haran nuestro dias como administradores mucho mas faciles y llevaderos.\n","date":"2006/07/12","externalUrl":null,"permalink":"/es/blog/bash-i-introduccion-y-ficheros-de-configuracion/","section":"Blogs","summary":"Todo administrador de sistemas UNIX en general y Linux en particular, deberia de aprender un minimo de programacion en Bash para automatizar y administrar tareas y trabajos en el sistema. Las posibilidades som muchas y una vez que se le coge el gusto a este lenguaje de programacion, no te puedes imaginar un dia como administrador sin hacer uso del mismo.","title":"Bash (I) - Introducción y ficheros de configuración","type":"blog"},{"content":"El contenido del fichero /etc/passwd determina quien puede acceder al sistema de manera legitima y que se puede hacer una vez dentro del sistema. Este fichero es la primera linea de defensa del sistema contra accesos no deseados. Debe de mantenerse escrupulosamente y libre de errores y fallos de seguridad. En el tenemos registrados las cuentas de usuarios, asi como las claves de accesos y privilegios.\nUna linea ejemplo en este fichero:\nusuario1:FXWUuZ.vwXttg:500:501:usuario pepito:/home/usuario1:/bin/bash Los diferentes campos(7) estan separados por dos puntos (:) y el significado de los mismos es el siguiente:\nusuario1: Nombre de la cuenta (Login) FXWUuZ.vwXttg: Clave de acceso encriptada (password) 500: UID de esta cuenta 501: GID del grupo principal al que pertenece la cuenta usuario pepito: Nombre del usuario /home/usuario1: Directorio de trabajo de usuario1 /bin/bash: Interprete de comando (shell) de usuario pepito Una serie de reglas a tener en cuenta sobre el contenido de este fichero:\nEl UID de cuenta 0, pertenece al administrador (root), por debajo de UID 500 esta reservado para el sistema y por encima de UID 500 para los usuarios del sistema (Nota: la frontera del 500 puede variar dependiendo del sistema).\nEl GID del grupo principal esta definido en el archivo /etc/group y este sera el grupo por defecto cuando un usuario crea un fichero.\nNo hace falta decir que solo el administrador del sistema tiene que tener ID\u0026rsquo;s 0 en estos dos campos. Lo contrario significaria estar dando permisos de administracion (root) a la cuenta en cuestion.\nLo unico que identifica a una cuenta root del resto es una identificacion UID igual a 0. Podemos tener por ejemplo una cuenta llamada \u0026ldquo;pepito\u0026rdquo; pero con UID igual a 0, esta cuenta tendria permisos de administrador (root) y muchos programas que hacen referencia al nombre de la cuenta (ej: who, w, etc) no nos darian informacion sobre que la cuenta \u0026ldquo;pepito\u0026rdquo; tiene permisos de root.\nEsto es lo primero que un hacker suele hacer para instalar una puerta trasera en un sistema. Para averiguar cuentas con nombre diferente de root, pero permisos de root existen programas, pero a falta de uno podemos utilizar el siguiente comando:\nawk -F: \u0026#39;{if ($3==0) print $1}\u0026#39; /etc/passwd Lo mismo (con un pequeno cambio) se puede utilizar para ver cuentas con GID igual a 0:\nawk -F: \u0026#39;{if ($4==0) print $1}\u0026#39; /etc/passwd Es muy importante verificar asiduamente que toda cuenta (login) tiene asignada una clave valida. Existen programas para comprobar que no existen problemas de seguridad en /etc/passwd, pero a falta de uno se puede utilizar el siguiente comando para averiguar si existen cuentas sin claves:\nawk -F: \u0026#39;{if ($2==\u0026#34;\u0026#34;) print $1}\u0026#39; /etc/passwd Nunca dejar una cuenta con el campo de clave vacio, esto significa que no es necesario una clave para entrar en el sistema. Las cuentas de pseudo-usuarios (ej: daemon, lp, etc) y cuentas de usuarios cerradas temporalmente, tienen que tener un asterisco (*) en el campo de la clave.\nOtro punto a tener en cuenta es la eleccion de una buena clave. No se deberian utilizar claves que sean palabras de diccionario, nombres, datos personales, matriculas, etc, existen programas que son capaces de descifrar este tipo de claves. Utilizar al menos 7 caracteres (8 recomendable) e interpolar numeros y letras, mayusculas y minusculas. Existen programas que sustituyen el clasico \u0026ldquo;passwd\u0026rdquo; para crear/cambiar claves, que comprueban que la clave es suficientemente buena.\nLa explicacion de porque no se deberian utilizar palabras de diccionario, nombres, etc como claves de acceso, es la siguiente:\nCuando una clave es generada, esta, es codificada con la funcion \u0026ldquo;crypt\u0026rdquo;, esta funcion se puede definir como una funcion \u0026ldquo;hash\u0026rdquo; de una sola direccion, esto es, un algoritmo que es facil de computar en una direccion pero muy dificil de calcular en direccion opuesta. La funcion crypt utiliza un valor aleatorio llamado \u0026ldquo;salt\u0026rdquo; el cual esta formado por una cadena de dos caracteres [a-z A-Z 0-9 ./]. Este valor aleatorio permite codificar una misma clave de 4096 maneras distintas (Los dos primeros caracteres de una clave codificada, son los valores de \u0026ldquo;salt\u0026rdquo;, el resto hasta un total de 13 caracteres ASCII es la clave codificada segun el valor de \u0026ldquo;salt\u0026rdquo;).\nUna vez que sabemos un poco de teoria de como las claves son codificadas, nos podemos imaginar como se podria descifrar un clave de cuenta que es un palabra de diccionario, nombre, matricula, etc. Existen programas que codifican sistematicamente diccionarios de palabras de las 4096 maneras posibles (segun el valor \u0026ldquo;salt\u0026rdquo;) y comparan cada codificacion con los valores encriptados en /etc/passwd, si algun valor coincide, significaria que una clave ha sido descifrada. Este es uno de los metodos utilizados por hackers para descifrar claves y la razon de porque no se deben utilizar claves que sean palabras de diccionarios, nombres, etc.\nNunca usar scripts/programas como interprete de comandos en cuentas sin clave. Un ejemplo que lei una vez en un grupo de noticias, hablaba sobre como apagar el ordenador sin necesidad de ser root. Una de las soluciones que daban era el tener la siguiente linea en el fichero /etc/passwd:\nshutdown::0:0:shutdown:/sbin:/sbin/shutdown Podeis ver que el campo de clave esta vacio, con esta linea en tu /etc/passwd cualquier usuario, local o no local, puede apagar tu ordenador haciendo un simple telnet a la maquina en cuestion y escribiendo shutdown como login. No hace falta explicar las consecuencias que esto puede tener para tu sistema. ;-)\nLos ficheros /etc/passwd y /etc/group deben tener permisos de lectura para todos para que muchos programas puedan funcionar y permisos de escritura solo para root.\n-rw-r--r-- 1 root root 11594 Nov 9 12:53 /etc/passwd -rw-r--r-- 1 root root 1024 Nov 9 12:53 /etc/group Con estos permisos, cualquiera que tenga acceso al sistema puede leer el contenido de estos ficheros e intentar descifrar la clave encriptada de las cuentas. En pequenos sistemas, donde todos los usuarios se conocen y existe confianza entre ellos, esto no es un gran problema, pero en sistemas con un gran numero de usuarios, no es recomendable tener el sistema configurado de esta manera.\nPara evitar esto se puede instalar \u0026ldquo;Shadow passwords\u0026rdquo;. Con shadow passwords el fichero /etc/passwd puede ser leido por cualquier usuario con acceso, pero la informacion con las claves del sistema queda guardada en un fichero que solo puede ser leido por el administrador (root). Mas informacion sobre Shadow Password en el Howto correspondiente Shadow password HOWTO.\n","date":"2006/05/30","externalUrl":null,"permalink":"/es/blog/sobre-el-archivo-etcpasswd/","section":"Blogs","summary":"El contenido del fichero /etc/passwd determina quien puede acceder al sistema de manera legitima y que se puede hacer una vez dentro del sistema. Este fichero es la primera linea de defensa del sistema contra accesos no deseados. Debe de mantenerse escrupulosamente y libre de errores y fallos de seguridad. En el tenemos registrados las cuentas de usuarios, asi como las claves de accesos y privilegios.","title":"Sobre el archivo /etc/passwd","type":"blog"},{"content":" NUUG - Oslo, Norway 2005-10-13 postgresql_nuug.pdf Not available\nSummary # An extended presentation about PostgreSQL, the history of the project, features, administration and the most important tuning parameters.\n","date":"2005/10/13","externalUrl":null,"permalink":"/presentations/postgresql-most-advanced-database-world/","section":"Presentations","summary":"An extended presentation about PostgreSQL, the history of the project, features, administration and the most important tuning parameters.","title":"PostgreSQL - The most advanced database in the world","type":"presentations"},{"content":"","date":"1998/10/28","externalUrl":null,"permalink":"/tags/linux-es/","section":"Tags","summary":"","title":"Linux-Es","type":"tags"},{"content":" The Linux-ES project, also known as \u0026ldquo;The Linux Corner\u0026rdquo; was inaugurated in 1998 and was one of the first websites about Linux in Spanish.\nAt that time the Linux project was in its beginnings, the need for information, documentation and help regarding this operating system and related issues was enormous. This situation was the reason for creating a website about Linux. Along with other websites like the \u0026ldquo;Lucas project\u0026rdquo;, \u0026ldquo;Insflug\u0026rdquo; or multitude of \u0026ldquo;Spanish Linux users groups\u0026rdquo;, Linux-ES was part of the Spanish-speaking ecosystem that was created around Linux in those years.\n\u0026ldquo;The Linux Corner\u0026rdquo; hosted for years mirror copies of all the documentation from the \u0026ldquo;Lucas project\u0026rdquo;, \u0026ldquo;Insflug\u0026rdquo; and \u0026ldquo;The Linux Documentation Project (TLDP)\u0026rdquo;, hosted and coordinated the document \u0026ldquo;FAQ about Linux\u0026rdquo; and also hosted and coordinated the translation of the PHP manual to Spanish.\n\u0026ldquo;The Linux Corner\u0026rdquo; went through different stages during the years it was online, with some periods more active than others. October 28, 1998 at 20:04:20 local time the official opening of this website was announced in different mailing lists and newsgroups about LINUX and the first visitor was at 20:21:25 from a computer in Spain. During the first month online, more than 25,000 document requests from 1,151 different machines were processed, and 558MB were transferred. During the best years of the web, more than 4 million document requests from 150,000 different machines were processed and around 40GB of data were transfered in a month.\nOver the years, Linux grew to unimaginable levels in the 90\u0026rsquo;s, and different distributions of Linux tok over the responsibility of supporting their users. Because of this, the need for a website with general information about Linux became less and less important and this is the reason why \u0026ldquo;The Linux Corner\u0026rdquo; after a few years in decline closed in June 2017 after more than 18 years online.\nI leave you an archive with a selection of articles published in \u0026ldquo;The Linux Corner\u0026rdquo; during the years.\nOne of the last snapshots of LINUX-ES in the \u0026ldquo;Internet Archive\u0026rdquo; is available at this URL: http://web.archive.org/web/20170615121611/http://www.linux-es.org/\n","date":"1998/10/28","externalUrl":null,"permalink":"/projects/linux-es.org/","section":"Projects","summary":"The Linux-ES project, also known as “The Linux Corner” was inaugurated in 1998 and was one of the first websites about Linux in Spanish.","title":"LINUX-ES","type":"projects"},{"content":" PGP-keys # rafael@e-mc2.net\n-----BEGIN PGP PUBLIC KEY BLOCK----- xjMEYg0c+xYJKwYBBAHaRw8BAQdAMTmDpwlgM7hFEKRVzqjT6zMsKTiOA9Xd 4Ig495zlaGjNI3JhZmFlbEBlLW1jMi5uZXQgPHJhZmFlbEBlLW1jMi5uZXQ+ wo8EEBYKACAFAmINHPsGCwkHCAMCBBUICgIEFgIBAAIZAQIbAwIeAQAhCRDO zIcgdVhnHRYhBFvmLX8ltUbOLOHdCs7MhyB1WGcd3WYBAJ6OWCiaiU0YxrWn o1a7YcQz7FQITnBtU+bSenf399qmAP0fQQJD2dbMTIIKMjM8RCyUGaaYFhJe Rvxsb0hag8HSD844BGINHPsSCisGAQQBl1UBBQEBB0CYq0U0GLrvtMkGUjEG KnugcZg6/LFcr1b0I1l23kZHEQMBCAfCeAQYFggACQUCYg0c+wIbDAAhCRDO zIcgdVhnHRYhBFvmLX8ltUbOLOHdCs7MhyB1WGcdBOwA/RCjZ6FJ8GJ2x8XZ hVGxDloc4wlU4iXcjo0LNTsR5uIpAPwMgbRX3CqIDTMp2+yHBl83L8TrRTX4 vB0o5Z70SPiKDA== =nSxv -----END PGP PUBLIC KEY BLOCK----- rafael@3.14159.tech\n-----BEGIN PGP PUBLIC KEY BLOCK----- xjMEYg9qdxYJKwYBBAHaRw8BAQdAtlARRRk7ironBpd1S+d2HIvhlxEw+PSh URJAJYiUHUvNKXJhZmFlbEAzLjE0MTU5LnRlY2ggPHJhZmFlbEAzLjE0MTU5 LnRlY2g+wo8EEBYKACAFAmIPancGCwkHCAMCBBUICgIEFgIBAAIZAQIbAwIe AQAhCRAtdiHLBdbzPBYhBJJfeYndr/H4WLJdGS12IcsF1vM8K58A/0Fv3o7+ b2leAAPR84bmzB4XVMBohOpHuC+7dvtcLtOvAQCWN6auOLTpSq2RlvT+kAsS dtxWnBF4ttiTf9esNu7KC844BGIPancSCisGAQQBl1UBBQEBB0Dn/W3tlB9U N/D/S7e7I/ejj53llqE8vO6e6mNlpfSMSAMBCAfCeAQYFggACQUCYg9qdwIb DAAhCRAtdiHLBdbzPBYhBJJfeYndr/H4WLJdGS12IcsF1vM8u2gA/15kR3ye I5MByWo3sckNHEqW33Cv2s5fMxc/BtAzxOv/AP4ms+rfummfNFoZrpr7mf41 zQlPb459bVxcyEwWwGB+AQ== =CbOL -----END PGP PUBLIC KEY BLOCK----- ","externalUrl":null,"permalink":"/contact/","section":"Emc2Net universe","summary":"","title":"","type":"page"},{"content":"","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"}]