├── .vscode └── settings.json ├── LICENSE ├── README.md ├── disksSelector.json ├── main.json ├── old ├── README.md ├── WP_20141105_004.jpg ├── loadbalancers_pg.png ├── machine.json ├── postgresql-primer.md └── presentation.pptx ├── postgresHAinExistingSubnet.json ├── postgresHAinExistingSubnet.parameters.json ├── postgresHAstandalone.json ├── postgresHAstandalone.parameters.json └── scripts ├── setup-raid.sh ├── start-pg.sh └── start-zk.sh /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "editor.renderWhitespace": "all", 4 | "editor.useTabStops": true 5 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Christian 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Deploy to Azure Public 4 | 5 | 6 | 7 | 8 | 9 | ## patroni versions 10 | 11 | ``` 12 | 1.1 05951f9b5bbaaf7cd771025f3d8e8f23897d7357 13 | 1.2.4 720d08d1b4bc906fc47ec66ac6d645c75951a1bf 14 | ``` 15 | -------------------------------------------------------------------------------- /disksSelector.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "numDataDisks": { 6 | "type": "string", 7 | "allowedValues": [ 8 | "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "32", "64" 9 | ], 10 | "metadata": { 11 | "description": "This parameter allows the user to select the number of disks they want" 12 | } 13 | }, 14 | "diskStorageAccountName": { 15 | "type": "string", 16 | "metadata": { 17 | "description": "Name of the storage account where the data disks are stored" 18 | } 19 | }, 20 | "diskCaching": { 21 | "type": "string", 22 | "allowedValues": [ 23 | "None", 24 | "ReadOnly", 25 | "ReadWrite" 26 | ], 27 | "metadata": { 28 | "description": "Caching type for the data disks" 29 | } 30 | }, 31 | "diskSizeGB": { 32 | "type": "int", 33 | "minValue": 1, 34 | "maxValue": 1023, 35 | "metadata": { 36 | "description": "Size of the data disks" 37 | } 38 | }, 39 | "storageUrlSuffix": { 40 | "type": "string", 41 | "defaultValue": "', parameters('storageUrlSuffix'), '", 42 | "metadata": { 43 | "description": "Allows the usage of different data centers. (Gov, German, China...)" 44 | } 45 | } 46 | }, 47 | "variables": { 48 | "dataDisks": { 49 | "01": [ { "name": "datadisk01", "lun": 0, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk01.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 50 | "02": [ { "name": "datadisk02", "lun": 1, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk02.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 51 | "03": [ { "name": "datadisk03", "lun": 2, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk03.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 52 | "04": [ { "name": "datadisk04", "lun": 3, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk04.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 53 | "05": [ { "name": "datadisk05", "lun": 4, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk05.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 54 | "06": [ { "name": "datadisk06", "lun": 5, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk06.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 55 | "07": [ { "name": "datadisk07", "lun": 6, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk07.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 56 | "08": [ { "name": "datadisk08", "lun": 7, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk08.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 57 | "09": [ { "name": "datadisk09", "lun": 8, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk09.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 58 | "10": [ { "name": "datadisk10", "lun": 9, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk10.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 59 | "11": [ { "name": "datadisk11", "lun": 10, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk11.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 60 | "12": [ { "name": "datadisk12", "lun": 11, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk12.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 61 | "13": [ { "name": "datadisk13", "lun": 12, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk13.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 62 | "14": [ { "name": "datadisk14", "lun": 13, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk14.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 63 | "15": [ { "name": "datadisk15", "lun": 14, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk15.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 64 | "16": [ { "name": "datadisk16", "lun": 15, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk16.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 65 | "17": [ { "name": "datadisk17", "lun": 16, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk17.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 66 | "18": [ { "name": "datadisk18", "lun": 17, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk18.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 67 | "19": [ { "name": "datadisk19", "lun": 18, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk19.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 68 | "20": [ { "name": "datadisk20", "lun": 19, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk20.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 69 | "21": [ { "name": "datadisk21", "lun": 20, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk21.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 70 | "22": [ { "name": "datadisk22", "lun": 21, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk22.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 71 | "23": [ { "name": "datadisk23", "lun": 22, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk23.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 72 | "24": [ { "name": "datadisk24", "lun": 23, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk24.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 73 | "25": [ { "name": "datadisk25", "lun": 24, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk25.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 74 | "26": [ { "name": "datadisk26", "lun": 25, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk26.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 75 | "27": [ { "name": "datadisk27", "lun": 26, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk27.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 76 | "28": [ { "name": "datadisk28", "lun": 27, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk28.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 77 | "29": [ { "name": "datadisk29", "lun": 28, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk29.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 78 | "30": [ { "name": "datadisk30", "lun": 29, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk30.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 79 | "31": [ { "name": "datadisk31", "lun": 30, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk31.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 80 | "32": [ { "name": "datadisk32", "lun": 31, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk32.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 81 | "33": [ { "name": "datadisk33", "lun": 32, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk33.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 82 | "34": [ { "name": "datadisk34", "lun": 33, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk34.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 83 | "35": [ { "name": "datadisk35", "lun": 34, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk35.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 84 | "36": [ { "name": "datadisk36", "lun": 35, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk36.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 85 | "37": [ { "name": "datadisk37", "lun": 36, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk37.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 86 | "38": [ { "name": "datadisk38", "lun": 37, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk38.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 87 | "39": [ { "name": "datadisk39", "lun": 38, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk39.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 88 | "40": [ { "name": "datadisk40", "lun": 39, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk40.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 89 | "41": [ { "name": "datadisk41", "lun": 40, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk41.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 90 | "42": [ { "name": "datadisk42", "lun": 41, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk42.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 91 | "43": [ { "name": "datadisk43", "lun": 42, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk43.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 92 | "44": [ { "name": "datadisk44", "lun": 43, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk44.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 93 | "45": [ { "name": "datadisk45", "lun": 44, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk45.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 94 | "46": [ { "name": "datadisk46", "lun": 45, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk46.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 95 | "47": [ { "name": "datadisk47", "lun": 46, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk47.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 96 | "48": [ { "name": "datadisk48", "lun": 47, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk48.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 97 | "49": [ { "name": "datadisk49", "lun": 48, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk49.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 98 | "50": [ { "name": "datadisk50", "lun": 49, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk50.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 99 | "51": [ { "name": "datadisk51", "lun": 50, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk51.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 100 | "52": [ { "name": "datadisk52", "lun": 51, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk52.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 101 | "53": [ { "name": "datadisk53", "lun": 52, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk53.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 102 | "54": [ { "name": "datadisk54", "lun": 53, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk54.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 103 | "55": [ { "name": "datadisk55", "lun": 54, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk55.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 104 | "56": [ { "name": "datadisk56", "lun": 55, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk56.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 105 | "57": [ { "name": "datadisk57", "lun": 56, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk57.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 106 | "58": [ { "name": "datadisk58", "lun": 57, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk58.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 107 | "59": [ { "name": "datadisk59", "lun": 58, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk59.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 108 | "60": [ { "name": "datadisk60", "lun": 59, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk60.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 109 | "61": [ { "name": "datadisk61", "lun": 60, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk61.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 110 | "62": [ { "name": "datadisk62", "lun": 61, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk62.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 111 | "63": [ { "name": "datadisk63", "lun": 62, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk63.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ], 112 | "64": [ { "name": "datadisk64", "lun": 63, "vhd": { "uri": "[concat('http://', parameters('diskStorageAccountName'),'.blob.', parameters('storageUrlSuffix'), '/vhds/', 'datadisk64.vhd')]" }, "createOption": "Empty", "caching": "[parameters('diskCaching')]", "diskSizeGB": "[parameters('diskSizeGB')]" } ] 113 | }, 114 | "_comment2": "The delta arrays below build the difference from 0 to 4, 4 to 8, 8 to 12 disks and so on", 115 | "diskDeltas": { 116 | "4delta": [ "[variables('dataDisks')['01'][0]]", "[variables('dataDisks')['02'][0]]", "[variables('dataDisks')['03'][0]]", "[variables('dataDisks')['04'][0]]" ], 117 | "8delta": [ "[variables('dataDisks')['05'][0]]", "[variables('dataDisks')['06'][0]]", "[variables('dataDisks')['07'][0]]", "[variables('dataDisks')['08'][0]]" ], 118 | "12delta": [ "[variables('dataDisks')['09'][0]]", "[variables('dataDisks')['10'][0]]", "[variables('dataDisks')['11'][0]]", "[variables('dataDisks')['12'][0]]" ], 119 | "16delta": [ "[variables('dataDisks')['13'][0]]", "[variables('dataDisks')['14'][0]]", "[variables('dataDisks')['15'][0]]", "[variables('dataDisks')['16'][0]]" ], 120 | "32delta": [ "[variables('dataDisks')['17'][0]]", "[variables('dataDisks')['18'][0]]", "[variables('dataDisks')['19'][0]]", "[variables('dataDisks')['20'][0]]", 121 | "[variables('dataDisks')['21'][0]]", "[variables('dataDisks')['22'][0]]", "[variables('dataDisks')['23'][0]]", "[variables('dataDisks')['24'][0]]", 122 | "[variables('dataDisks')['25'][0]]", "[variables('dataDisks')['26'][0]]", "[variables('dataDisks')['27'][0]]", "[variables('dataDisks')['28'][0]]", 123 | "[variables('dataDisks')['29'][0]]", "[variables('dataDisks')['30'][0]]", "[variables('dataDisks')['31'][0]]", "[variables('dataDisks')['32'][0]]" ], 124 | "64delta": [ "[variables('dataDisks')['33'][0]]", "[variables('dataDisks')['34'][0]]", "[variables('dataDisks')['35'][0]]", "[variables('dataDisks')['36'][0]]", 125 | "[variables('dataDisks')['37'][0]]", "[variables('dataDisks')['38'][0]]", "[variables('dataDisks')['39'][0]]", "[variables('dataDisks')['40'][0]]", 126 | "[variables('dataDisks')['41'][0]]", "[variables('dataDisks')['42'][0]]", "[variables('dataDisks')['43'][0]]", "[variables('dataDisks')['44'][0]]", 127 | "[variables('dataDisks')['45'][0]]", "[variables('dataDisks')['46'][0]]", "[variables('dataDisks')['47'][0]]", "[variables('dataDisks')['48'][0]]", 128 | "[variables('dataDisks')['49'][0]]", "[variables('dataDisks')['50'][0]]", "[variables('dataDisks')['51'][0]]", "[variables('dataDisks')['52'][0]]", 129 | "[variables('dataDisks')['53'][0]]", "[variables('dataDisks')['54'][0]]", "[variables('dataDisks')['55'][0]]", "[variables('dataDisks')['56'][0]]", 130 | "[variables('dataDisks')['57'][0]]", "[variables('dataDisks')['58'][0]]", "[variables('dataDisks')['59'][0]]", "[variables('dataDisks')['60'][0]]", 131 | "[variables('dataDisks')['61'][0]]", "[variables('dataDisks')['62'][0]]", "[variables('dataDisks')['63'][0]]", "[variables('dataDisks')['64'][0]]" ] 132 | }, 133 | "_comment1": "disksArray is ugly :( but it gets the job done. If anyone can make it better, please do!", 134 | "disksArray": { 135 | "1": "[concat(variables('dataDisks')['01'])]", 136 | "2": "[concat(variables('dataDisks')['01'], variables('dataDisks')['02'])]", 137 | "3": "[concat(variables('dataDisks')['01'], variables('dataDisks')['02'], variables('dataDisks')['03'])]", 138 | "4": "[concat(variables('diskDeltas')['4delta'])]", 139 | "5": "[concat(variables('diskDeltas')['4delta'], variables('dataDisks')['05'])]", 140 | "6": "[concat(variables('diskDeltas')['4delta'], variables('dataDisks')['05'], variables('dataDisks')['06'])]", 141 | "7": "[concat(variables('diskDeltas')['4delta'], variables('dataDisks')['05'], variables('dataDisks')['06'], variables('dataDisks')['07'])]", 142 | "8": "[concat(variables('diskDeltas')['4delta'], variables('diskDeltas')['8delta'])]", 143 | "9": "[concat(variables('diskDeltas')['4delta'], variables('diskDeltas')['8delta'], variables('dataDisks')['09'])]", 144 | "10": "[concat(variables('diskDeltas')['4delta'], variables('diskDeltas')['8delta'], variables('dataDisks')['09'], variables('dataDisks')['10'])]", 145 | "11": "[concat(variables('diskDeltas')['4delta'], variables('diskDeltas')['8delta'], variables('dataDisks')['09'], variables('dataDisks')['10'], variables('dataDisks')['11'])]", 146 | "12": "[concat(variables('diskDeltas')['4delta'], variables('diskDeltas')['8delta'], variables('diskDeltas')['12delta'])]", 147 | "13": "[concat(variables('diskDeltas')['4delta'], variables('diskDeltas')['8delta'], variables('diskDeltas')['12delta'], variables('dataDisks')['13'])]", 148 | "14": "[concat(variables('diskDeltas')['4delta'], variables('diskDeltas')['8delta'], variables('diskDeltas')['12delta'], variables('dataDisks')['13'], variables('dataDisks')['14'])]", 149 | "15": "[concat(variables('diskDeltas')['4delta'], variables('diskDeltas')['8delta'], variables('diskDeltas')['12delta'], variables('dataDisks')['13'], variables('dataDisks')['14'], variables('dataDisks')['15'])]", 150 | "16": "[concat(variables('diskDeltas')['4delta'], variables('diskDeltas')['8delta'], variables('diskDeltas')['12delta'], variables('diskDeltas')['16delta'])]", 151 | "32": "[concat(variables('diskDeltas')['4delta'], variables('diskDeltas')['8delta'], variables('diskDeltas')['12delta'], variables('diskDeltas')['16delta'], variables('diskDeltas')['32delta'])]", 152 | "64": "[concat(variables('diskDeltas')['4delta'], variables('diskDeltas')['8delta'], variables('diskDeltas')['12delta'], variables('diskDeltas')['16delta'], variables('diskDeltas')['32delta'], variables('diskDeltas')['64delta'])]" 153 | } 154 | }, 155 | "resources": [], 156 | "outputs": { 157 | "dataDiskArray": { 158 | "type": "array", 159 | "value": "[variables('disksArray')[parameters('numDataDisks')]]" 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /main.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "tenantName": { 6 | "type": "string", 7 | "defaultValue": "barbaz" 8 | }, 9 | "adminUsername": { 10 | "type": "string", 11 | "defaultValue": "chgeuer", 12 | "metadata": { "description": "Admin user name for the Virtual Machines." } 13 | }, 14 | "adminSecureShellKey": { 15 | "type": "string", 16 | "defaultValue": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAk/ViUPrGp7KoJLuN2PgofgMyw7SN9zfLYFDDR0TRYa8cOvJlE8NdZYt6Oqa4aL/fslKr9bmlMCdawhZRL7sHccIIS0I0zG7iD15rQL3/Y5aZOf3ML+bebpSj+SE5OeHT9iobgsYpK8gq72d8tmZZAfKhx6fRJsgC2j2xXH/GveoZ5GkHnhJUYuYPmNjEb/PK7LT43XuP+E9Rderr3LPUTuBeGVW9do0HS7X8I2uTn0+BqgkZLOO4FCnSXxh1u6fuD++ZgOZVmB6Q1xEdHSA7LLnPkjDZqbWezLIh5cSdNPUW2JG7tMxQTAZzVoNMb6vAVsfslB16rqZQ21EdIq+0pw== chgeuer-dcos-1", 17 | "metadata": { "description": "Admin SSH key for the Virtual Machines." } 18 | }, 19 | "postgresqlUsername": { 20 | "type": "string", 21 | "defaultValue": "pgadmin", 22 | "metadata": { "description": "PostgreSQL user name" } 23 | }, 24 | "postgresqlPassword": { 25 | "type": "securestring", 26 | "metadata": { "description": "PostgreSQL password" } 27 | }, 28 | "postgresqlInstanceCount": { 29 | "defaultValue": 2, "minValue": 2, "maxValue": 10, "type": "int", 30 | "metadata": { "description": "Number of postgreSQL servers in the cluster." } 31 | }, 32 | "postgresqlInstanceSize": { 33 | "defaultValue": "Standard_DS2_v2", 34 | "allowedValues": [ 35 | "Standard_DS1", "Standard_DS2", "Standard_DS3", "Standard_DS4", 36 | "Standard_DS11", "Standard_DS12", "Standard_DS13", "Standard_DS14", 37 | "Standard_GS1", "Standard_GS2", "Standard_GS3", "Standard_GS4", "Standard_GS5", 38 | "Standard_DS1_v2", "Standard_DS2_v2", "Standard_DS3_v2", "Standard_DS4_v2", "Standard_DS5_v2", 39 | "Standard_DS11_v2", "Standard_DS12_v2", "Standard_DS13_v2", "Standard_DS14_v2", "Standard_DS15_v2" 40 | ], 41 | "type": "string", 42 | "metadata": { "description": "Size of the postgreSQL servers in the cluster." } 43 | }, 44 | "zookeeperInstanceSize": { 45 | "defaultValue": "Standard_DS1_v2", 46 | "allowedValues": [ 47 | "Standard_DS1", "Standard_DS2", "Standard_DS3", "Standard_DS4", 48 | "Standard_DS11", "Standard_DS12", "Standard_DS13", "Standard_DS14", 49 | "Standard_GS1", "Standard_GS2", "Standard_GS3", "Standard_GS4", "Standard_GS5", 50 | "Standard_DS1_v2", "Standard_DS2_v2", "Standard_DS3_v2", "Standard_DS4_v2", "Standard_DS5_v2", 51 | "Standard_DS11_v2", "Standard_DS12_v2", "Standard_DS13_v2", "Standard_DS14_v2", "Standard_DS15_v2" 52 | ], 53 | "type": "string", 54 | "metadata": { "description": "Size of the ZooKeeeper servers in the cluster." } 55 | }, 56 | "postgresAzureVersion": { 57 | "defaultValue": "master", 58 | "type": "string" 59 | } 60 | }, 61 | "variables": { 62 | "commonSettings": { 63 | "baseUrl": "[concat('https://raw.githubusercontent.com/chgeuer/postgres-azure/',parameters('postgresAzureVersion'),'/')]", 64 | "tenantName": "[parameters('tenantName')]", 65 | "constants": { 66 | "apiVersions": { 67 | "availabilitySets": "2016-04-30-preview" 68 | } 69 | }, 70 | "vnet": { 71 | "name": "[concat(parameters('tenantName'),'-vnet')]", 72 | "address": "10.0.0.0/16", 73 | "subnet": { 74 | "postgresql": { 75 | "name": "subnet-postgresql", 76 | "nsgName": "nsg-postgresql", 77 | "addressRangePrefix": "10.0.0", 78 | "address": "10.0.0.0/24" 79 | }, 80 | "zookeeper": { 81 | "name": "subnet-zookeeper", 82 | "nsgName": "nsg-zookeeper", 83 | "addressRangePrefix": "10.0.1", 84 | "address": "10.0.1.0/24" 85 | } 86 | } 87 | }, 88 | "softwareversions": { 89 | "zookeeper": "3.4.9", 90 | "java4zookeeper1": "7u75", "java4zookeeper2": "b13", 91 | "_testedNiko": { 92 | "name": "tested by Niko", 93 | "patroni": "6eb2e2114453545256ac7cbfec55bda285ffb955", 94 | "postgres": "9.5" 95 | }, 96 | "patroni_1_1": { 97 | "name": "patroni v1.1", 98 | "patroni": "05951f9b5bbaaf7cd771025f3d8e8f23897d7357", 99 | "postgres": "9.5" 100 | }, 101 | "patroni_1_2_4": { 102 | "name": "patroni v1.2.4", 103 | "patroni": "720d08d1b4bc906fc47ec66ac6d645c75951a1bf", 104 | "postgres": "9.6" 105 | } 106 | }, 107 | "instanceCount": { 108 | "postgresql": 2, 109 | "postgresqlDataDiskCount": 3, 110 | "zookeeper": 2 111 | }, 112 | "vm": { 113 | "postgresql": { 114 | "publisher": "Canonical", 115 | "offer": "UbuntuServer", 116 | "sku": "16.04-LTS", 117 | "version": "latest" 118 | }, 119 | "zookeeper": { 120 | "publisher": "Canonical", 121 | "offer": "UbuntuServer", 122 | "sku": "16.04-LTS", 123 | "version": "latest" 124 | } 125 | } 126 | } 127 | }, 128 | "resources": [ 129 | { 130 | "type": "Microsoft.Network/publicIPAddresses", 131 | "name": "[concat(variables('commonSettings').tenantName, '-publicip')]", 132 | "apiVersion": "2016-09-01", 133 | "location": "[resourceGroup().location]", 134 | "properties": { 135 | "publicIPAllocationMethod": "Dynamic", 136 | "idleTimeoutInMinutes": 30, 137 | "dnsSettings": { 138 | "domainNameLabel": "[concat(variables('commonSettings').tenantName, '-publicip')]" 139 | } 140 | } 141 | }, 142 | { 143 | "type": "Microsoft.Network/loadBalancers", 144 | "name": "loadbalancer-postgres", 145 | "apiVersion": "2016-09-01", 146 | "location": "[resourceGroup().location]", 147 | "dependsOn": [ 148 | "[concat('Microsoft.Network/publicIPAddresses/', concat(variables('commonSettings').tenantName, '-publicip'))]" 149 | ], 150 | "properties": { 151 | "frontendIPConfigurations": [ 152 | { 153 | "name": "postgresqlFrontendIPConfiguration", 154 | "properties": { 155 | "publicIPAddress": { "id": "[resourceId('Microsoft.Network/publicIPAddresses', concat(variables('commonSettings').tenantName, '-publicip'))]" } 156 | } 157 | } 158 | ], 159 | "backendAddressPools": [ { "name": "postgresqlBackendAddressPool" } ], 160 | "probes": [ 161 | { 162 | "name": "sshProbe", 163 | "properties": { 164 | "protocol": "Tcp", 165 | "port": 22, 166 | "intervalInSeconds": 5, 167 | "numberOfProbes": 2 168 | } 169 | }, 170 | { 171 | "name": "postgresqlProbe", 172 | "properties": { 173 | "protocol": "Tcp", 174 | "port": 5000, 175 | "intervalInSeconds": 5, 176 | "numberOfProbes": 2 177 | } 178 | } 179 | ], 180 | "loadBalancingRules": [ 181 | { 182 | "name": "loadBalancingRule-ssh", 183 | "properties": { 184 | "protocol": "Tcp", 185 | "frontendPort": 22, 186 | "backendPort": 22, 187 | "enableFloatingIP": false, 188 | "idleTimeoutInMinutes": 30, 189 | "frontendIPConfiguration": { "id": "[concat(resourceId('Microsoft.Network/loadBalancers', 'loadbalancer-postgres'), '/frontendIPConfigurations/', 'postgresqlFrontendIPConfiguration')]" }, 190 | "backendAddressPool": { "id": "[concat(resourceId('Microsoft.Network/loadBalancers', 'loadbalancer-postgres'), '/backendAddressPools/', 'postgresqlBackendAddressPool')]" }, 191 | "probe": { "id": "[concat(resourceId('Microsoft.Network/loadBalancers', 'loadbalancer-postgres'), '/probes/', 'sshProbe')]" } 192 | } 193 | }, 194 | { 195 | "name": "loadBalancingRule-postgresql", 196 | "properties": { 197 | "protocol": "Tcp", 198 | "frontendPort": 5000, 199 | "backendPort": 5000, 200 | "enableFloatingIP": false, 201 | "idleTimeoutInMinutes": 30, 202 | "frontendIPConfiguration": { "id": "[concat(resourceId('Microsoft.Network/loadBalancers', 'loadbalancer-postgres'), '/frontendIPConfigurations/', 'postgresqlFrontendIPConfiguration')]" }, 203 | "backendAddressPool": { "id": "[concat(resourceId('Microsoft.Network/loadBalancers', 'loadbalancer-postgres'), '/backendAddressPools/', 'postgresqlBackendAddressPool')]" }, 204 | "probe": { "id": "[concat(resourceId('Microsoft.Network/loadBalancers', 'loadbalancer-postgres'), '/probes/', 'postgresqlProbe')]" } 205 | } 206 | } 207 | ] 208 | } 209 | }, 210 | { 211 | "type": "Microsoft.Network/networkSecurityGroups", 212 | "name": "[variables('commonSettings').vnet.subnet.postgresql.nsgName]", 213 | "apiVersion": "2016-09-01", 214 | "location": "[resourceGroup().location]", 215 | "properties": { 216 | "securityRules": [ 217 | { 218 | "name": "ssh-in", 219 | "properties": { 220 | "priority": 100, 221 | "description": "Allow TCP/22 Inbound (Internet->PostgreSQL Server)", 222 | "access": "Allow", 223 | "direction": "Inbound", 224 | "protocol": "Tcp", 225 | "sourceAddressPrefix": "Internet", 226 | "sourcePortRange": "*", 227 | "destinationAddressPrefix": "[variables('commonSettings').vnet.subnet.postgresql.address]", 228 | "destinationPortRange": "22" 229 | } 230 | }, 231 | { 232 | "name": "postgresql-in", 233 | "properties": { 234 | "priority": 101, 235 | "description": "Allow TCP/80 Inbound (Internet->PostgreSQL Server)", 236 | "access": "Allow", 237 | "direction": "Inbound", 238 | "protocol": "Tcp", 239 | "sourceAddressPrefix": "Internet", 240 | "sourcePortRange": "*", 241 | "destinationAddressPrefix": "[variables('commonSettings').vnet.subnet.postgresql.address]", 242 | "destinationPortRange": "5000" 243 | } 244 | } 245 | ] 246 | } 247 | }, 248 | { 249 | "type": "Microsoft.Network/networkSecurityGroups", 250 | "name": "[variables('commonSettings').vnet.subnet.zookeeper.nsgName]", 251 | "apiVersion": "2016-09-01", 252 | "location": "[resourceGroup().location]", 253 | "properties": { 254 | "securityRules": [ 255 | { 256 | "name": "zookeeper-in", 257 | "properties": { 258 | "priority": 100, 259 | "description": "Allow TCP/2181 Inbound (Patroni --> ZooKeeper)", 260 | "access": "Allow", 261 | "direction": "Inbound", 262 | "protocol": "Tcp", 263 | "sourceAddressPrefix": "[variables('commonSettings').vnet.subnet.postgresql.address]", 264 | "sourcePortRange": "*", 265 | "destinationAddressPrefix": "[variables('commonSettings').vnet.subnet.zookeeper.address]", 266 | "destinationPortRange": "2181" 267 | } 268 | }, 269 | { 270 | "name": "zookeeper-cluster-port-1", 271 | "properties": { 272 | "priority": 101, 273 | "description": "Allow TCP/2888 Inbound (ZooKeeper --> ZooKeeper)", 274 | "access": "Allow", 275 | "direction": "Inbound", 276 | "protocol": "Tcp", 277 | "sourceAddressPrefix": "[variables('commonSettings').vnet.subnet.zookeeper.address]", 278 | "sourcePortRange": "*", 279 | "destinationAddressPrefix": "[variables('commonSettings').vnet.subnet.zookeeper.address]", 280 | "destinationPortRange": "2888" 281 | } 282 | }, 283 | { 284 | "name": "zookeeper-cluster-port-2", 285 | "properties": { 286 | "priority": 102, 287 | "description": "Allow TCP/3888 Inbound (ZooKeeper --> ZooKeeper)", 288 | "access": "Allow", 289 | "direction": "Inbound", 290 | "protocol": "Tcp", 291 | "sourceAddressPrefix": "[variables('commonSettings').vnet.subnet.zookeeper.address]", 292 | "sourcePortRange": "*", 293 | "destinationAddressPrefix": "[variables('commonSettings').vnet.subnet.zookeeper.address]", 294 | "destinationPortRange": "3888" 295 | } 296 | }, 297 | { 298 | "name": "ssh-in", 299 | "properties": { 300 | "priority": 103, 301 | "description": "Allow TCP/22 Inbound", 302 | "access": "Allow", 303 | "direction": "Inbound", 304 | "protocol": "Tcp", 305 | "sourceAddressPrefix": "*", 306 | "sourcePortRange": "*", 307 | "destinationAddressPrefix": "[variables('commonSettings').vnet.subnet.postgresql.address]", 308 | "destinationPortRange": "22" 309 | } 310 | } 311 | ] 312 | } 313 | }, 314 | { 315 | "type": "Microsoft.Network/virtualNetworks", 316 | "name": "[variables('commonSettings').vnet.name]", 317 | "apiVersion": "2015-05-01-preview", 318 | "location": "[resourceGroup().location]", 319 | "dependsOn": [ 320 | "[concat('Microsoft.Network/networkSecurityGroups/', variables('commonSettings').vnet.subnet.postgresql.nsgName)]", 321 | "[concat('Microsoft.Network/networkSecurityGroups/', variables('commonSettings').vnet.subnet.zookeeper.nsgName)]" 322 | ], 323 | "properties": { 324 | "addressSpace": { 325 | "addressPrefixes": [ "[variables('commonSettings').vnet.address]" ] 326 | }, 327 | "subnets": [ 328 | { 329 | "name": "[variables('commonSettings').vnet.subnet.postgresql.name]", 330 | "properties": { 331 | "addressPrefix": "[variables('commonSettings').vnet.subnet.postgresql.address]", 332 | "networkSecurityGroup": { 333 | "id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('commonSettings').vnet.subnet.postgresql.nsgName)]" 334 | } 335 | } 336 | }, 337 | { 338 | "name": "[variables('commonSettings').vnet.subnet.zookeeper.name]", 339 | "properties": { 340 | "addressPrefix": "[variables('commonSettings').vnet.subnet.zookeeper.address]", 341 | "networkSecurityGroup": { 342 | "id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('commonSettings').vnet.subnet.zookeeper.nsgName)]" 343 | } 344 | } 345 | } 346 | ] 347 | } 348 | }, 349 | { 350 | "type": "Microsoft.Compute/availabilitySets", 351 | "name": "[concat('availabilitySet-postgresql-', variables('commonSettings').tenantName)]", 352 | "apiVersion": "2016-04-30-preview", 353 | "location": "[resourceGroup().location]", 354 | "properties": { 355 | "platformFaultDomainCount": 3, 356 | "platformUpdateDomainCount": 5, 357 | "managed": true 358 | } 359 | }, 360 | { 361 | "type": "Microsoft.Network/networkInterfaces", 362 | "name": "[concat('networkInterface-', 'postgresql', '-', copyIndex())]", 363 | "copy": { "name": "postgresqlNicCopy", "count": "[variables('commonSettings').instanceCount.postgresql]" }, 364 | "apiVersion": "2016-09-01", 365 | "location": "[resourceGroup().location]", 366 | "dependsOn": [ 367 | "[concat('Microsoft.Network/virtualNetworks/', variables('commonSettings').vnet.name)]" 368 | ], 369 | "properties": { 370 | "ipConfigurations": [ 371 | { 372 | "name": "[concat('networkInterface-ipConfiguration-', 'postgresql', '-', copyIndex())]", 373 | "properties": { 374 | "privateIPAllocationMethod": "Static", 375 | "privateIPAddress": "[concat(variables('commonSettings').vnet.subnet.postgresql.addressRangePrefix, '.', copyIndex(10))]", 376 | "subnet": { "id": "[concat(resourceId('Microsoft.Network/virtualNetworks', variables('commonSettings').vnet.name), '/subnets/', variables('commonSettings').vnet.subnet.postgresql.name)]" }, 377 | "loadBalancerBackendAddressPools": [ { "id": "[concat(resourceId('Microsoft.Network/loadBalancers', 'loadbalancer-postgres'), '/backendAddressPools/', 'postgresqlBackendAddressPool')]" } ] 378 | } 379 | } 380 | ] 381 | } 382 | }, 383 | { 384 | "type": "Microsoft.Compute/disks", 385 | "name": "[concat('osDisk-', 'postgresql', '-', copyIndex())]", 386 | "copy": { "name": "postgresqlOsDiskCopy", "count": "[variables('commonSettings').instanceCount.postgresql]" }, 387 | "apiVersion": "2016-04-30-preview", 388 | "location": "[resourceGroup().location]", 389 | "properties": { 390 | "osType": "Linux", 391 | "accountType": "Premium_LRS", 392 | "diskSizeGB": 128, 393 | "creationData": { 394 | "createOption": "FromImage", 395 | "imageReference": { 396 | "id": "[concat('/Subscriptions/', subscription().subscriptionId, '/Providers/Microsoft.Compute/Locations/',resourceGroup().location, '/Publishers/', variables('commonSettings').vm.postgresql.publisher, '/ArtifactTypes/VMImage/Offers/', variables('commonSettings').vm.postgresql.offer, '/Skus/', variables('commonSettings').vm.postgresql.sku, '/Versions/', variables('commonSettings').vm.postgresql.version)]" 397 | } 398 | } 399 | } 400 | }, 401 | { 402 | "type": "Microsoft.Compute/disks", 403 | "name": "dataDisk1", 404 | "apiVersion": "2016-04-30-preview", 405 | "location": "[resourceGroup().location]", 406 | "properties": { 407 | "accountType": "Premium_LRS", 408 | "diskSizeGB": 128, 409 | "creationData": { 410 | "createOption": "Empty" 411 | } 412 | } 413 | }, 414 | { 415 | "type": "Microsoft.Compute/virtualMachines", 416 | "name": "[concat('virtualMachine-', 'postgresql', '-', copyIndex())]", 417 | "copy": { "name": "postgresqlVmCopy", "count": "[variables('commonSettings').instanceCount.postgresql]" }, 418 | "apiVersion": "2016-04-30-preview", 419 | "location": "[resourceGroup().location]", 420 | "dependsOn": [ 421 | "[concat('Microsoft.Compute/availabilitySets/', concat('availabilitySet-postgresql-', variables('commonSettings').tenantName))]", 422 | "[concat('Microsoft.Network/networkInterfaces/', concat('networkInterface-', 'postgresql', '-', copyIndex()))]", 423 | "[concat('Microsoft.Compute/disks/', concat('osDisk-', 'postgresql', '-', copyIndex()))]" 424 | ], 425 | "properties": { 426 | "hardwareProfile": { "vmSize": "[parameters('postgresqlInstanceSize')]" }, 427 | "availabilitySet": { 428 | "id": "[resourceId('Microsoft.Compute/availabilitySets', concat('availabilitySet-postgresql-', variables('commonSettings').tenantName))]" 429 | }, 430 | "osProfile": { 431 | "computerName": "[concat('pg', copyIndex())]", 432 | "adminUsername": "[parameters('adminUsername')]", 433 | "linuxConfiguration": { 434 | "disablePasswordAuthentication": true, 435 | "ssh": { 436 | "publicKeys": [ 437 | { 438 | "path": "[concat('/home/', parameters('adminUsername'), '/.ssh/authorized_keys')]", 439 | "keyData": "[parameters('adminSecureShellKey')]" 440 | } 441 | ] 442 | } 443 | } 444 | }, 445 | "storageProfile": { 446 | "imageReference": 447 | { 448 | "publisher": "[variables('commonSettings').vm.postgresql.publisher]", 449 | "offer": "[variables('commonSettings').vm.postgresql.offer]", 450 | "sku": "[variables('commonSettings').vm.postgresql.sku]", 451 | "version": "[variables('commonSettings').vm.postgresql.version]" 452 | }, 453 | "osDisk": { 454 | "name": "[concat('osDisk-', 'postgresql', '-', copyIndex())]", 455 | "caching": "ReadWrite", 456 | "createOption": "FromImage" 457 | } 458 | }, 459 | "networkProfile": { 460 | "networkInterfaces": [ 461 | { 462 | "id": "[resourceId('Microsoft.Network/networkInterfaces', concat('networkInterface-', 'postgresql', '-', copyIndex()))]" 463 | } 464 | ] 465 | } 466 | } 467 | }, 468 | { 469 | "type": "Microsoft.Compute/virtualMachines/extensions", 470 | "name": "[concat('virtualMachine-', 'postgresql', '-', copyIndex(), '/extension')]", 471 | "apiVersion": "2015-05-01-preview", 472 | "location": "[resourceGroup().location]", 473 | "dependsOn": [ "[concat('Microsoft.Compute/virtualMachines/', concat('virtualMachine-', 'postgresql', '-', copyIndex()))]" ], 474 | "copy": { "name": "postgresqlVmExtensionCopy", "count": "[variables('commonSettings').instanceCount.postgresql]" }, 475 | "properties": { 476 | "publisher": "Microsoft.OSTCExtensions", 477 | "type": "CustomScriptForLinux", 478 | "typeHandlerVersion": "1.2", 479 | "settings": { 480 | "fileUris": [ 481 | "[concat(variables('commonSettings').baseUrl, 'scripts/setup-raid.sh')]", 482 | "[concat(variables('commonSettings').baseUrl, 'scripts/start-zk.sh')]", 483 | "[concat(variables('commonSettings').baseUrl, 'scripts/start-pg.sh')]" 484 | ], 485 | "commandToExecute": "[concat('./start-pg.sh', ' ', parameters('tenantName'), ' ', concat(variables('commonSettings').vnet.subnet.zookeeper.addressRangePrefix, '.10'), ' ', variables('commonSettings').instanceCount.zookeeper, ' ', concat(variables('commonSettings').vnet.subnet.postgresql.addressRangePrefix, '.10'), ' ', variables('commonSettings').instanceCount.postgresql, ' ', copyIndex(), ' ', parameters('postgresqlUsername'), ' ', concat('\"', parameters('postgresqlPassword'), '\"'), ' ', variables('commonSettings').softwareversions._testedNiko.patroni, ' ', variables('commonSettings').softwareversions._testedNiko.postgres)]" 486 | } 487 | } 488 | }, 489 | { 490 | "type": "Microsoft.Compute/availabilitySets", 491 | "name": "[concat('availabilitySet-zookeeper-', variables('commonSettings').tenantName)]", 492 | "apiVersion": "2016-04-30-preview", 493 | "location": "[resourceGroup().location]", 494 | "properties": { 495 | "platformFaultDomainCount": 3, 496 | "platformUpdateDomainCount": 5, 497 | "managed": true 498 | } 499 | }, 500 | { 501 | "type": "Microsoft.Network/networkInterfaces", 502 | "name": "[concat('networkInterface-', 'zookeeper', '-', copyIndex())]", 503 | "copy": { "name": "zookeeperNicCopy", "count": "[variables('commonSettings').instanceCount.zookeeper]" }, 504 | "apiVersion": "2016-09-01", 505 | "location": "[resourceGroup().location]", 506 | "dependsOn": [ 507 | "[concat('Microsoft.Network/virtualNetworks/', variables('commonSettings').vnet.name)]" 508 | ], 509 | "properties": { 510 | "ipConfigurations": [ 511 | { 512 | "name": "[concat('networkInterface-ipConfiguration-', 'zookeeper', '-', copyIndex())]", 513 | "properties": { 514 | "privateIPAllocationMethod": "Static", 515 | "privateIPAddress": "[concat(variables('commonSettings').vnet.subnet.zookeeper.addressRangePrefix, '.', copyIndex(10))]", 516 | "subnet": { "id": "[concat(resourceId('Microsoft.Network/virtualNetworks', variables('commonSettings').vnet.name), '/subnets/', variables('commonSettings').vnet.subnet.zookeeper.name)]" } 517 | } 518 | } 519 | ] 520 | } 521 | }, 522 | { 523 | "type": "Microsoft.Compute/disks", 524 | "name": "[concat('osDisk-', 'zookeeper', '-', copyIndex())]", 525 | "copy": { "name": "zookeeperOsDiskCopy", "count": "[variables('commonSettings').instanceCount.zookeeper]" }, 526 | "apiVersion": "2016-04-30-preview", 527 | "location": "[resourceGroup().location]", 528 | "properties": { 529 | "osType": "Linux", 530 | "accountType": "Premium_LRS", 531 | "diskSizeGB": 128, 532 | "creationData": { 533 | "createOption": "FromImage", 534 | "imageReference": { 535 | "id": "[concat('/Subscriptions/', subscription().subscriptionId, '/Providers/Microsoft.Compute/Locations/',resourceGroup().location, '/Publishers/', variables('commonSettings').vm.zookeeper.publisher, '/ArtifactTypes/VMImage/Offers/', variables('commonSettings').vm.zookeeper.offer, '/Skus/', variables('commonSettings').vm.zookeeper.sku, '/Versions/', variables('commonSettings').vm.zookeeper.version)]" 536 | } 537 | } 538 | } 539 | }, 540 | 541 | { 542 | "type": "Microsoft.Compute/virtualMachines", 543 | "name": "[concat('virtualMachine-', 'zookeeper', '-', copyIndex())]", 544 | "copy": { "name": "zookeeperVmCopy", "count": "[variables('commonSettings').instanceCount.zookeeper]" }, 545 | "apiVersion": "2016-04-30-preview", 546 | "location": "[resourceGroup().location]", 547 | "dependsOn": [ 548 | "[concat('Microsoft.Compute/availabilitySets/', concat('availabilitySet-zookeeper-', variables('commonSettings').tenantName))]", 549 | "[concat('Microsoft.Network/networkInterfaces/', concat('networkInterface-', 'zookeeper', '-', copyIndex()))]", 550 | "[concat('Microsoft.Compute/disks/', concat('osDisk-', 'zookeeper', '-', copyIndex()))]" 551 | ], 552 | "properties": { 553 | "hardwareProfile": { "vmSize": "[parameters('zookeeperInstanceSize')]" }, 554 | "availabilitySet": { 555 | "id": "[resourceId('Microsoft.Compute/availabilitySets', concat('availabilitySet-zookeeper-', variables('commonSettings').tenantName))]" 556 | }, 557 | "osProfile": { 558 | "computerName": "[concat('zk', copyIndex())]", 559 | "adminUsername": "[parameters('adminUsername')]", 560 | "linuxConfiguration": { 561 | "disablePasswordAuthentication": true, 562 | "ssh": { 563 | "publicKeys": [ 564 | { 565 | "path": "[concat('/home/', parameters('adminUsername'), '/.ssh/authorized_keys')]", 566 | "keyData": "[parameters('adminSecureShellKey')]" 567 | } 568 | ] 569 | } 570 | } 571 | }, 572 | "storageProfile": { 573 | "imageReference": 574 | { 575 | "publisher": "[variables('commonSettings').vm.zookeeper.publisher]", 576 | "offer": "[variables('commonSettings').vm.zookeeper.offer]", 577 | "sku": "[variables('commonSettings').vm.zookeeper.sku]", 578 | "version": "[variables('commonSettings').vm.zookeeper.version]" 579 | }, 580 | "osDisk": { 581 | "name": "[concat('osDisk-', 'zookeeper', '-', copyIndex())]", 582 | "caching": "ReadWrite", 583 | "createOption": "FromImage" 584 | } 585 | }, 586 | "networkProfile": { 587 | "networkInterfaces": [ 588 | { 589 | "id": "[resourceId('Microsoft.Network/networkInterfaces', concat('networkInterface-', 'zookeeper', '-', copyIndex()))]" 590 | } 591 | ] 592 | } 593 | } 594 | }, 595 | { 596 | "type": "Microsoft.Compute/virtualMachines/extensions", 597 | "name": "[concat('virtualMachine-', 'zookeeper', '-', copyIndex(), '/extension')]", 598 | "apiVersion": "2015-05-01-preview", 599 | "location": "[resourceGroup().location]", 600 | "dependsOn": [ "[concat('Microsoft.Compute/virtualMachines/', concat('virtualMachine-', 'zookeeper', '-', copyIndex()))]" ], 601 | "copy": { "name": "zookeeperVmExtensionCopy", "count": "[variables('commonSettings').instanceCount.zookeeper]" }, 602 | "properties": { 603 | "publisher": "Microsoft.OSTCExtensions", 604 | "type": "CustomScriptForLinux", 605 | "typeHandlerVersion": "1.2", 606 | "settings": { 607 | "fileUris": [ 608 | "[concat(variables('commonSettings').baseUrl, 'scripts/start-zk.sh')]" 609 | ], 610 | "commandToExecute": "[concat('./start-zk.sh', ' ', copyIndex(), ' ', variables('commonSettings').instanceCount.zookeeper, ' ', concat(variables('commonSettings').vnet.subnet.zookeeper.addressRangePrefix, '.10'), ' ', variables('commonSettings').softwareversions.zookeeper, ' ', variables('commonSettings').softwareversions.java4zookeeper1, ' ', variables('commonSettings').softwareversions.java4zookeeper2)]" 611 | } 612 | } 613 | } 614 | ], 615 | "outputs": { 616 | 617 | } 618 | } -------------------------------------------------------------------------------- /old/README.md: -------------------------------------------------------------------------------- 1 | Setting up PostgreSQL on Microsoft Azure Virtual Machines (IaaS) 2 | ================================================================ 3 | 4 | # Architecture 5 | 6 | 7 | 8 | 9 | 10 | # Login to Azure 11 | 12 | You'll need an Azure subscription. You can get a [free trial](http://www.windowsazure.com/en-us/pricing/free-trial/?WT.mc_id=AA4C1C935). 13 | 14 | - Download publish settings at https://manage.windowsazure.com/publishsettings/index?client=xplat 15 | 16 | ```console 17 | npm install azure-cli 18 | azure account clear 19 | azure account import "Windows Azure MSDN - Visual Studio Ultimate-credentials.publishsettings" 20 | azure account set "internal" 21 | azure account list 22 | ``` 23 | 24 | For more detailed instructions on setting up the Azure CLI tools see https://vmdepot.msopentech.com/help/deploy/cli.html/ 25 | 26 | # Create a base Linux image 27 | 28 | We'll use a Debian image from VM Depot, find the most recent with [this search](https://vmdepot.msopentech.com/List/Index?sort=Date&search=platform%3Adebian) 29 | 30 | There are a number of ways to deploy the image, below is an outline of how to do it with the command line tools. VM Depot has documentation on [other methods](https://vmdepot.msopentech.com/help/deploy.html/). 31 | 32 | ```console 33 | azure vm list --json 34 | azure vm create DNS_PREFIX --community vmdepot-65-6-32 --virtual-network-name -l "West Europe" USER_NAME [PASSWORD] [--ssh] [other_options] 35 | 36 | Create an A5 instance 37 | ``` 38 | 39 | It would be great if people [published a Postgres image](https://vmdepot.msopentech.com/help/contribute.html/) after following this tutorial (it doesn't cost anything). That way those who follow will not have to do this initial configuration work. 40 | 41 | # Command line for creating a PostgreSQL machine 42 | 43 | Command line: 44 | 45 | ```console 46 | azure vm create-from cloudservicename machine.json --connect --verbose --json 47 | ``` 48 | 49 | ## machine.json 50 | 51 | See the [REST API](http://msdn.microsoft.com/en-us/library/azure/jj157194.aspx) for details. 52 | 53 | ```JSON 54 | { 55 | "RoleName": "database-vm-1", 56 | "RoleType": "PersistentVMRole", 57 | "RoleSize": "A5", 58 | "AvailabilitySetName" : "databases", 59 | "OSVirtualHardDisk": { 60 | "OS": "Linux", 61 | "HostCaching": "ReadWrite", 62 | "DiskName": "database-vm-1-disk", 63 | "DiskLabel": "database-vm-1-disk", 64 | "SourceImageName": "Debian-Wheezy-635506180993665396", 65 | "RemoteSourceImageLink": "http://account.blob.core.windows.net/vmdepot-images/TE-2014-11-03-debianwheezy-os-2014-11-03.vhd", 66 | "MediaLink" : "http://account.blob.core.windows.net/vmdepot-images/database-vm-1-disk.vhd" 67 | }, 68 | "DataVirtualHardDisks": [ 69 | { "DiskLabel": "database-vm-1-data1", "Lun": "0", "MediaLink" : "http://account.blob.core.windows.net/vmdepot-images/database-vm-1-data1.vhd", "HostCaching": "ReadOnly", "LogicalDiskSizeInGB": "1023" }, 70 | { "DiskLabel": "database-vm-1-data2", "Lun": "1", "MediaLink" : "http://account.blob.core.windows.net/vmdepot-images/database-vm-1-data2.vhd", "HostCaching": "ReadOnly", "LogicalDiskSizeInGB": "1023" }, 71 | { "DiskLabel": "database-vm-1-xlog1", "Lun": "2", "MediaLink" : "http://account.blob.core.windows.net/vmdepot-images/database-vm-1-xlog1.vhd", "HostCaching": "ReadOnly", "LogicalDiskSizeInGB": "1023" } 72 | ], 73 | "ConfigurationSets": [ 74 | { 75 | "ConfigurationSetType" : "LinuxProvisioningConfiguration", 76 | "HostName" : "database-vm-1", 77 | "UserName" : "ruth", 78 | "UserPassword" : "Supersecret123!!", 79 | "DisableSshPasswordAuthentication" : false 80 | }, 81 | { 82 | "ConfigurationSetType": "NetworkConfiguration", 83 | "SubnetNames": [ "mysubnet" ], 84 | "StaticVirtualNetworkIPAddress": "10.10.0.7", 85 | "InputEndpoints": [], 86 | "PublicIPs": [], 87 | "StoredCertificateSettings": [] 88 | } 89 | ], 90 | "ProvisionGuestAgent": "true", 91 | "ResourceExtensionReferences": [] 92 | } 93 | ``` 94 | 95 | # Bring Linux up to date 96 | 97 | ```console 98 | $ aptitude update && aptitude upgrade 99 | $ aptitude install rsync 100 | $ aptitude install mdadm lvm2 xfsprogs 101 | $ aptitude install pacemaker corosync resource-agents 102 | ``` 103 | 104 | # Setup striping 105 | 106 | - Multiple data disks in a [RAID](http://azure.microsoft.com/en-us/documentation/articles/virtual-machines-linux-configure-raid/) in order to achieve higher I/O, given current limitation of 500 IOPS per data disk. 107 | - One data disk for pg_xlog 108 | 109 | 110 | ## A couple of inputs for fdisk 111 | 112 | ```console 113 | # Standard fdisk partition 114 | fdiskStdin=$(cat <<'END_HEREDOC' 115 | n 116 | p 117 | 1 118 | 119 | 120 | w 121 | END_HEREDOC 122 | ) 123 | 124 | # cfdisk command for 'FD' (RAID autodetect) for RAID 125 | cfdiskStdinFD=$(cat <<'END_HEREDOC' 126 | np 127 | 128 | tFD 129 | Wyes 130 | q 131 | END_HEREDOC 132 | ) 133 | 134 | # cfdisk command for '8E' (LVM) 135 | cfdiskStdin8E=$(cat <<'END_HEREDOC' 136 | np 137 | 138 | t8E 139 | Wyes 140 | q 141 | END_HEREDOC 142 | ) 143 | ``` 144 | 145 | ## cfdisk and amalgamate the disks 146 | 147 | ```console 148 | # Use 'cfdisk' on /dev/sdc and create a primary partition of type 'FD' (RAID autodetect) for RAID for pg_data 149 | echo "$cfdiskStdinFD" | cfdisk /dev/sdc 150 | 151 | # Use 'cfdisk' on /dev/sdd and create a primary partition of type 'FD' (RAID autodetect) for RAID for pg_data 152 | echo "$cfdiskStdinFD" | cfdisk /dev/sdd 153 | 154 | # Use 'cfdisk' on /dev/sde and create a primary partition of type '8E' (LVM) for pg_xlog 155 | echo "$cfdiskStdin8E" | cfdisk /dev/sde 156 | 157 | ################################# 158 | 159 | mdadm --create /dev/md0 --level 0 --raid-devices 2 /dev/sdc1 /dev/sdd1 160 | 161 | # create physical volume 162 | pvcreate /dev/md0 163 | 164 | # create volume group 165 | vgcreate data /dev/md0 166 | 167 | # create logical volume 168 | lvcreate -n pgdata -l100%FREE data 169 | 170 | # show volume group information 171 | vgdisplay 172 | 173 | # Now 174 | ls -als /dev/data/pgdata 175 | 176 | # mkfs -t xfs /dev/data/pgdata 177 | mkfs.xfs /dev/data/pgdata 178 | ``` 179 | 180 | Setup automount 181 | 182 | ```console 183 | $ tail /etc/fstab 184 | 185 | /dev/mapper/data-pgdata /space/pgdata xfs defaults 0 0 186 | /dev/mapper/xlog-pgxlog /space/pgxlog xfs defaults 0 0 187 | ``` 188 | 189 | 190 | 191 | 192 | 193 | 194 | # PostgreSQL base install 195 | 196 | 197 | 198 | ## Install PostgreSQL 199 | 200 | Install PostgreSQL, as documented under https://wiki.postgresql.org/wiki/Apt 201 | 202 | ``` 203 | $ sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' 204 | 205 | $ aptitude install wget ca-certificates 206 | 207 | $ wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - 208 | 209 | $ aptitude install postgresql-9.3 210 | 211 | $ aptitude install repmgr 212 | ``` 213 | 214 | 215 | ## Move database files into striped volume 216 | 217 | ``` 218 | mv /var/lib/postgresql/9.3 /space/pgdata/ 219 | ln -s /space/pgdata/9.3 /var/lib/postgresql/9.3 220 | 221 | mv /space/pgdata/9.3/main/pg_xlog /space/pgxlog/9.3 222 | ln -s /space/pgxlog/9.3 /space/pgdata/9.3/main/pg_xlog 223 | ``` 224 | 225 | 226 | # Distribute SSH keys for user postgres across the cluster 227 | 228 | Whatever it takes 229 | 230 | 231 | 232 | 233 | ## Edit /etc/postgresql/9.3/main/postgresql.conf 234 | 235 | Uncomment listen_addresses (Database only reachable through jump host) 236 | 237 | ``` 238 | listen_addresses = '*' 239 | ``` 240 | 241 | Switch off SSL 242 | 243 | ``` 244 | ssl = false 245 | ``` 246 | 247 | Rule of thumb for shared buffers: 25% of RAM should be shared buffers, on an A5 248 | 249 | ``` 250 | shared_buffers = 4GB 251 | work_mem = 256MB 252 | maintenance_work_mem = 512MB 253 | ``` 254 | 255 | The write-ahead-log needs to be merged at regular checkpoints into the tables: 256 | 257 | ``` 258 | checkpoint_segments = 64 # was 3 previously in logfile segments, min 1, 16MB each 259 | checkpoint_timeout = 1min # range 30s-1h 260 | checkpoint_completion_target = 0.8 # checkpoint target duration, 0.0 - 1.0 261 | ``` 262 | 263 | Put replication configuration into dedicated file 264 | 265 | ``` 266 | include_if_exists = 'replication.conf' 267 | ``` 268 | 269 | # Edit /etc/postgresql/9.3/main/replication.conf 270 | 271 | ## wal_keep_segments 272 | 273 | How many segments to hold in xlog folder. Having a longer value allows slaves to keep up. 500*16 MB = 8 GB 274 | 275 | ``` 276 | wal_keep_segments=500 277 | ``` 278 | ## wal_level 279 | 280 | All nodes (master and slaves) need to be in hot_standby to that slaves can become master. 281 | 282 | ``` 283 | wal_level='hot_standby' 284 | ``` 285 | 286 | ## archive_mode and archive_command 287 | 288 | ``` 289 | archive_mode=on 290 | archive_command='cd .' 291 | ``` 292 | 293 | ## max_wal_senders 294 | 295 | Number of machines, takes away from max_connections. Should be very similar to the size of the cluster. Between 5 and 10 is "OK". Numbers like 500 kill the machine 296 | 297 | ``` 298 | max_wal_senders=5 299 | ``` 300 | 301 | ## hot_standby 302 | 303 | Allows slaves to already answer to read queries. 304 | 305 | ``` 306 | hot_standby=on 307 | ``` 308 | 309 | # Add postgres user with replication priviledge and superuser 310 | 311 | ```console 312 | # username "repl" 313 | # -P = get password from 314 | # -S = super user, in order to run repmgr (otherwise, error "permission denied for language C" comes) 315 | 316 | $ su postgres 317 | $ createuser --replication -P -S repl 318 | ``` 319 | 320 | ## Edit /etc/postgresql/9.3/main/pg_hba.conf 321 | 322 | As postgres user, add the following line to allow subnet 10.10.0.0/16 to do replication with user ID "repl" 323 | 324 | ``` 325 | host all all 10.10.0.0/16 md5 326 | host replication repl 10.10.0.0/16 md5 327 | ``` 328 | 329 | ## Configure /var/lib/postgresql/repmgr.conf on all hosts. 330 | 331 | As postgres user, These settings are written to the DB, and visible in the cluster, so IPs must be real ones . 332 | 333 | ``` 334 | All nodes have the same cluster name 335 | cluster=my_application_cluster 336 | pg_bindir='/usr/lib/postgres/9.3/bin' 337 | 338 | # This must unique be for each respective node 339 | node=1 340 | node_name=postgresvm1 341 | conninfo='host=10.10.0.7 user=repl dbname=repmgr' 342 | ``` 343 | 344 | 345 | ## Setup repmgr internals on master node. 346 | 347 | ### vim /var/lib/postgresql/.pgpass 348 | 349 | ``` 350 | # hostname:port:database:username:password 351 | *:*:*:repl:supersecret123.- 352 | ``` 353 | 354 | Permissions 355 | 356 | ```console 357 | $ chmod 0600 /var/lib/postgresql/.pgpass 358 | ``` 359 | 360 | ### Setup repmgr on master node 361 | 362 | ```console 363 | $ sudo postgres / su - postgres 364 | $ repmgr -f /var/lib/postgresql/repmgr.conf --verbose master register 365 | ``` 366 | 367 | ### Setup repmgr on standby nodes (slaves) *before starting postgres on the slaves* 368 | 369 | ```console 370 | $ sudo postgres / su - postgres 371 | 372 | $ service postgresql stop 373 | 374 | # -d database 375 | # -U user 376 | # -R rsync user 377 | # -D data dir 378 | # -w WAL keep segments (default is 5000, which is too large) 379 | # 10.10.0.7 IP address of master 380 | 381 | $ repmgr -d repmgr \ 382 | -U repl \ 383 | -R postgres -w 500 \ 384 | -D /var/lib/postgres/9.3/main \ 385 | -f /var/lib/postgresql/repmgr.conf \ 386 | --verbose \ 387 | standby clone 10.10.0.7 388 | 389 | $ service postgresql start 390 | 391 | $ repmgr -f /var/lib/postgresql/repmgr.conf \ 392 | --verbose standby register 393 | ``` 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | # Local agent 410 | 411 | ## When the current master goes down 412 | 413 | When master gets shutdown signal, 414 | 415 | 1. Refuse additional (new) connections: 416 | - change file [pg_hba.conf](http://www.postgresql.org/docs/9.1/static/auth-pg-hba-conf.html) to reject new connections 417 | - "SELECT pg_reloadconf();" or "pg_ctl reload conf" or "kill -HUP" to enact configuration 418 | 2. [Drop existing sessions](http://www.devopsderek.com/blog/2012/11/13/list-and-disconnect-postgresql-db-sessions/) 419 | - "SELECT pg_terminate_backend( )" 420 | 3. Instruct PostgreSQL to write (flush) remaining transaction log (WAL records) to tables by creating a checkpoint 421 | 422 | 423 | ### Refuse additional (new) connections 424 | 425 | Edit [pg_hba.conf](http://www.postgresql.org/docs/9.1/static/auth-pg-hba-conf.html): Uncomment the "all/all" line, so that nobody can create additional connections. 426 | 427 | ``` 428 | # host all all 10.10.0.0/16 md5 429 | ``` 430 | 431 | And reload config 432 | 433 | ```SQL 434 | SELECT pg_reload_conf(); 435 | ``` 436 | 437 | ### Drop existing sessions from the web tier 438 | 439 | ```SQL 440 | SELECT pg_terminate_backend(pid) 441 | FROM pg_stat_activity 442 | WHERE usename='webfrontend'; 443 | ``` 444 | 445 | (Yes, it is 'usename', not 'username')... 446 | 447 | 448 | ### Create a checkpoint on master via SQL 449 | 450 | ```SQL 451 | CHECKPOINT; 452 | ``` 453 | 454 | ### Determine xlog location 455 | 456 | Determine xlog location of the current xlog position, something after the previously made checkpoint. Here, we can be sure that after the checkpoint, only non-relevant changes (like vacuuming) happened to the tables. 457 | 458 | Now fetch (once) after the checkpoint operation on the master the XLOG location, and store it in a variable `checkpointXlog`: 459 | 460 | ```SQL 461 | SELECT pg_current_xlog_location(); 462 | ``` 463 | 464 | Determine replication lag for the slaves. When the `pg_xloc_location_diff(...)` function call returns 0, all slaves have catched up. Running below code gives a current view: 465 | 466 | ```SQL 467 | SELECT client_addr, 468 | flush_location, 469 | pg_current_xlog_location(), 470 | pg_xlog_location_diff( 471 | pg_current_xloc_location(), 472 | flush_location) 473 | from pg_stat_replication; 474 | ``` 475 | 476 | Using the post-checkpoint variable `checkpointXlog`, you can now determine whether it is safe to kill the master. 477 | 478 | ```ruby 479 | var checkpointXlog = eval("SELECT pg_current_xlog_location();") 480 | 481 | checkpointXlog == '0/14047810' 482 | ``` 483 | 484 | Now we can determine the concrete replication lag: 485 | 486 | ```SQL 487 | SELECT client_addr, 488 | flush_location, 489 | '0/14047810', 490 | pg_xlog_location_diff( 491 | '0/14047810', 492 | flush_location) 493 | from pg_stat_replication; 494 | ``` 495 | 496 | - When the `pg_xlog_location_diff` column has non-positiv values, it's safe to shoot the master in the head. 497 | - When we compare against `flush_location`, we not it's on the harddisk of the slave. 498 | - When we compare against `replay_location`, we know it's in the actual database tables. 499 | 500 | Stop old master server 501 | 502 | ```console 503 | $ sudo postgres / su - postgres 504 | $ service postgresql stop 505 | ``` 506 | 507 | ## Turn one of the slaves into the new master (`repmgr standby promote`) 508 | 509 | Use either `repmgr standby promote` (as a convenient wrapper) or naked `pg_ctl promote`. 510 | 511 | ```bash 512 | $ sudo postgres / su - postgres 513 | $ repmgr -f /var/lib/postgresql/repmgr.conf --verbose standby promote 514 | ``` 515 | 516 | ## Tell slaves to sync against the new master (`repmgr standby follow`) 517 | 518 | All nodes (master and slaves) know each other. When calling `repmgr standby follow` is forced upon the slaves, they ask around (via SQL) to determine who the new master is. This is done by calling `pg_is_in_recovery()`, which is `false` on a master. This step recreates the `recovery.conf` file, which lists the IP of the new master. 519 | 520 | ``` 521 | $ sudo postgres / su - postgres 522 | $ repmgr -f /var/lib/postgresql/repmgr.conf --verbose standby follow 523 | ``` 524 | 525 | ## Turn old master into new slave (`repmgr standby clone`) 526 | 527 | 1. Stop PostgreSQL 528 | 2. Enable `all/all` in `pg_hba.conf` again 529 | 3. Clone from new master (`repmgr standby clone`) 530 | 4. Start PostgreSQL 531 | 5. Hook up to synchronisation (`repmgr standby register`) 532 | 533 | Make a `repmgr standby clone` against a previous slave, who became master 534 | 535 | ```console 536 | $ sudo postgres / su - postgres 537 | 538 | $ vim /etc/postgresql/9.3/main/pg_hba.conf 539 | 540 | $ service postgresql stop 541 | 542 | $ repmgr -d repmgr \ 543 | -U repl \ 544 | -R postgres -w 500 \ 545 | -D /var/lib/postgres/9.3/main \ 546 | -f /var/lib/postgresql/repmgr.conf \ 547 | --verbose \ 548 | standby clone 10.10.0.5 549 | 550 | $ service postgresql start 551 | 552 | $ repmgr -f /var/lib/postgresql/repmgr.conf \ 553 | --verbose standby register 554 | ``` 555 | 556 | # pgbouncer 557 | 558 | ## /etc/pgbouncer/pgbouncer.ini contents 559 | 560 | Assumptions: 561 | 562 | - pgbouncer service is on 10.10.0.20 (and similarly on other boxes), behind an internal load balancer 563 | - current master is 10.10.0.5 564 | - current slaves are 10.10.0.6 and 10.10.0.7 565 | - The application logic uses two different endpoints for updates (writes) and pure queries (reads). 566 | 567 | 568 | ```ini 569 | [databases] 570 | myapp-write = host=primary port=5433 571 | myapp-readonly = port=5434 572 | 573 | [pgbouncer] 574 | listen_addr = 10.10.0.20, 127.0.0.1 575 | listen_port = 5432 576 | pool_mode = transaction 577 | max_client_conn = 500 578 | default_pool_size = 20 579 | ``` 580 | 581 | ## Configure internal load balancer 582 | 583 | ```powershell 584 | Add-AzureAccount 585 | Set-AzureSubscription -SubscriptionName "BizSpark Plus" -SubscriptionId "8eefc6f2-7216-4aef-8394-fce57df325a3" 586 | Select-AzureSubscription -SubscriptionName "BizSpark Plus" -Default 587 | 588 | Add-AzureInternalLoadBalancer -InternalLoadBalancerName pgbouncer -ServiceName fantasyweb -SubnetName fantasy -StaticVNetIPAddress 10.10.0.100 589 | 590 | Get-AzureVM -ServiceName fantasyweb -Name pooler1 | Add-AzureEndpoint -Name "pgbouncer" -LBSetName "pgbouncer" -Protocol tcp -LocalPort 5432 -PublicPort 5432 -ProbePort 5432 -ProbeProtocol tcp -ProbeIntervalInSeconds 10 -InternalLoadBalancerName pgbouncer | Update-AzureVM 591 | 592 | ``` 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | # PostgreSQL admin stuff 615 | 616 | ```console 617 | $ createuser application_admin 618 | 619 | # -O owner 620 | $ createdb -O application_admin my_database 621 | 622 | # Add application_admin to administrators in /etc/postgresql/9.3/main/pg_hba.conf 623 | $ vim /etc/postgresql/9.3/main/pg_hba.conf 624 | 625 | # -s n scaling factor 626 | # -i initialization 627 | # -U username 628 | % pgbench -s 10 -i -U application_admin my_database 629 | 630 | $ service postgresql start 631 | ``` 632 | 633 | # pg_control.rb 634 | 635 | - A "clone" is a resource that gets active on multiple nodes. There are also stateful clones. 636 | - It seems PostgreSQL is a "[multi-state resource][pacemaker-resource-multi-state]" are clones which can have multiple modes. 637 | - [OCF Operations][pacemaker-ocf-operations] are promote / demote / notify 638 | - CRM resources define three nodes. One resource per node. One master per cluster. 639 | - The "script" which needs to be developed is an "OCF resource agent". 640 | - This script must implement four operations: start / stop / monitor / notify 641 | - A sample script can be seen in `/usr/lib/ocf/resource.d/heartbeat/*` 642 | - Possible commands from CRM which hit the script: 643 | - Node should be a completely new master: this is never the case 644 | - Node should become slave of an existing master: 645 | - Current master VM gets rebootet or shutdown: 646 | - Previous slave should become master: "start MASTER" 647 | - The [OCF return codes][pacemaker-ocf-return-codes] must be returned for operations 648 | - start, stop, monitor, validate-all, promote, demote, notify, meta-data 649 | 650 | 651 | 652 | 653 | 654 | ## Pseudo code for "the script" 655 | 656 | 657 | 658 | ``` 659 | // http://clusterlabs.org/doc/en-US/Pacemaker/1.1-pcs/html-single/Pacemaker_Explained/index.html#_multi_state_resource_agent_requirements 660 | if (monitor) { 661 | OCF_NOT_RUNNING = Stopped 662 | OCF_SUCCESS = Running (Slave) 663 | OCF_RUNNING_MASTER = Running (Master) 664 | OCF_FAILED-master = Failed (Master) 665 | Other = Failed (Slave) 666 | } 667 | 668 | var isRunningAsSlave = sqleval("localhost", "SELECT pg_is_in_recovery()"); 669 | var isRunningAsMaster = ! isRunningAsSlave; 670 | 671 | if (stop && isRunningAsMaster) { 672 | // refuse new connections 673 | modify("pg_hba.conf", remove "all/all") && sqleval("localhost", "SELECT pg_reloadconf();"); 674 | 675 | // drop existing connections 676 | sqleval("localhost", "SELECT pg_terminate_backend(pid) \ 677 | FROM pg_stat_activity \ 678 | WHERE usename='webfrontend';"); 679 | 680 | // create checkoint 681 | sqleval("localhost", "CHECKPOINT;"); 682 | 683 | // determine current location 684 | var flush_location = sqleval("localhost", "SELECT pg_current_xlog_location();"); 685 | 686 | string diffStatement = "pg_xlog_location_diff(pg_current_xloc_location(), " + flush_location + ") from pg_stat_replication;" 687 | string determineReplicationLag = "SELECT client_addr, pg_current_xlog_location()," + diffStatement; 688 | 689 | bool allSlavesSynced = false; 690 | while (!allSlavesSynced) { 691 | bool foundUnsyncedSlave = false; 692 | var replicationStates = sqleval("localhost", determineReplicationLag); 693 | foreach (var replicationState in replicationStates) { 694 | (client,current,diff) = replicationState; 695 | if (diff > 0) { 696 | foundUnsyncedSlave = true; 697 | } 698 | } 699 | allSlavesSynced = !foundUnsyncedSlave; 700 | } 701 | 702 | shutdownPostgreSQL(); 703 | // remove current node from master ILB 704 | configureInternalLoadBalancer("ilb_master", "remove `uname -n`"); 705 | 706 | return 0; // machine can shut down 707 | } 708 | 709 | 710 | if (start && isMaster) { 711 | var isMaster = via CRM; 712 | if (isMaster) { 713 | // add current node to master ILB 714 | configureInternalLoadBalancer("ilb_master", "add `uname -n`"); 715 | } 716 | } 717 | 718 | ``` 719 | 720 | 721 | ``` 722 | database-vm1 shutdown 723 | -> crm standby on (automatisch durch shutdown) 724 | 725 | database-vm2 MASTER (received clean shutdown from crm database-vm1) 726 | -> tecontrolpg.rb start MASTER 727 | -> rpmgr promote -> leave recovery mode, timeline switch happened 728 | -> add to internal MASTER LB and remove from SLAVE LB 729 | -> assure pg_hba.conf is accepting connections 730 | -> local test write query 731 | 732 | database-vm3 SLAVE 733 | -> follow new master (rpmgr standby follow) 734 | -> local test read query 735 | ``` 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | ## Determine whether you're on master or slave 746 | 747 | `SELECT pg_is_in_recovery()` returns `false` on the master (who is not in recovery), and `true` for slaves (who are in constant recovery mode). 748 | 749 | # Enable fiddler to sniff azure-cli 750 | 751 | http://blogs.msdn.com/b/avkashchauhan/archive/2013/01/30/using-fiddler-to-decipher-windows-azure-powershell-or-rest-api-https-traffic.aspx 752 | 753 | ```console 754 | SET HTTP_PROXY=http://127.0.0.1:8888/ 755 | SET HTTPS_PROXY=http://127.0.0.1:8888/ 756 | SET HTTPPROXY=http://127.0.0.1:8888/ 757 | SET HTTPSPROXY=http://127.0.0.1:8888/ 758 | SET NODE_TLS_REJECT_UNAUTHORIZED=0 759 | ``` 760 | 761 | ## CustomRules.js 762 | 763 | ```javascript 764 | static function OnBeforeRequest(oSession: Session) { 765 | oSession["https-Client-Certificate"]= "C:\\Users\\chgeuer\\Desktop\\txxx.cer"; 766 | ``` 767 | 768 | # Questions: 769 | 770 | - For the block device driver for Azure Linux IaaS, what's supported or optimal? open_datasync, fdatasync (default on Linux), fsync, fsync_writethrough, open_sync. This is relevant to configure wal_sync_method 771 | - It seems that the guest OS does not see a detached data disk. An attached disk shows up in dmesg, while a detach process doesn't show up. When trying to open a formerly attached device (with cfdisk), the 772 | 773 | # Arbitrary Unix vodoo :-) 774 | 775 | ## See what's happening 776 | 777 | ```console 778 | watch dmesg \| tail -5 779 | ``` 780 | 781 | # Next steps 782 | 783 | - Understand corosync model and how it should be used for the PostgreSQL cluster (Felix) 784 | - Extend the [OCF resource agent for pgsql](https://github.com/ClusterLabs/resource-agents/blob/master/heartbeat/pgsql) to support multiple slaves, because then the remaining slave needs to follow a new master. The current script only has one master and one slave. (Felix) 785 | - Scripting/API access to reconfiguring the internal load balancer (Christian) 786 | 787 | 788 | 789 | # References 790 | 791 | - Azure 792 | - [Azure - azure-cli / node.js command-line tool for Mac and Linux](http://azure.microsoft.com/en-us/documentation/articles/command-line-tools/) 793 | - [Azure - Exporting and Importing VM settings with the Azure Command-Line Tools](http://blogs.msdn.com/b/silverlining/archive/2012/10/25/exporting-and-importing-vm-settings-with-the-azure-command-line-tools.aspx) 794 | - [Azure - Create Virtual Machine Deployment REST API](http://msdn.microsoft.com/en-us/library/azure/jj157194.aspx) 795 | - [Azure - Linux and Graceful Shutdowns](http://azure.microsoft.com/blog/2014/05/06/linux-and-graceful-shutdowns-2/) 796 | - [Azure - Internal Load Balancing](http://azure.microsoft.com/blog/2014/05/20/internal-load-balancing/) 797 | - [Azure - Load balancing highly available Linux services: OpenLDAP and MySQL](http://channel9.msdn.com/Blogs/Open/Load-balancing-highly-available-Linux-services-on-Windows-Azure-OpenLDAP-and-MySQL) 798 | - [Using load-balanced sets to clusterize MySQL on Linux](http://azure.microsoft.com/en-us/documentation/articles/virtual-machines-linux-mysql-cluster/) 799 | - [Azure STONITH Provider on GitHub](https://github.com/bureado/aztonith/blob/master/azure-vm) 800 | - Pacemaker & Corosync 801 | - [An A-Z guide to Pacemaker's Configuration Options](http://clusterlabs.org/doc/en-US/Pacemaker/1.1-pcs/html-single/Pacemaker_Explained/index.html) 802 | - [Clusters from Scratch - Creating Active/Passive and Active/Active Clusters on Fedora](http://clusterlabs.org/doc/en-US/Pacemaker/1.1-pcs/html-single/Clusters_from_Scratch/index.html) 803 | - [corosync wiki](https://github.com/corosync/corosync/wiki) 804 | - [OCF resource agent for pgsql](https://github.com/ClusterLabs/resource-agents/blob/master/heartbeat/pgsql) 805 | - Ruby 806 | - [Ruby and PostgreSQL](https://bitbucket.org/ged/ruby-pg/wiki/Home) 807 | - [Ruby and Azure Service Bus](https://github.com/Azure/azure-content/blob/master/articles/service-bus-ruby-how-to-use-topics-subscriptions.md) 808 | 809 | [pacemaker-resource-multi-state]: http://clusterlabs.org/doc/en-US/Pacemaker/1.1-pcs/html-single/Pacemaker_Explained/index.html#s-resource-multistate 810 | [pacemaker-ocf-operations]: http://clusterlabs.org/doc/en-US/Pacemaker/1.1-pcs/html-single/Pacemaker_Explained/index.html#_actions 811 | [pacemaker-ocf-return-codes]: http://clusterlabs.org/doc/en-US/Pacemaker/1.1-pcs/html/Pacemaker_Explained/s-ocf-return-codes.html 812 | [stonith]: http://ourobengr.com/ha/ 813 | 814 | # Acronyms 815 | 816 | ``` 817 | OCF - open cluster framework (from Pacemaker) 818 | CRM - cluster resource manager (from Pacemaker) 819 | linbit - company who wrote 820 | ``` 821 | -------------------------------------------------------------------------------- /old/WP_20141105_004.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chgeuer/postgres-azure/5a5e7bfbac01d70c3f25c5b5e4db72a70366ea96/old/WP_20141105_004.jpg -------------------------------------------------------------------------------- /old/loadbalancers_pg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chgeuer/postgres-azure/5a5e7bfbac01d70c3f25c5b5e4db72a70366ea96/old/loadbalancers_pg.png -------------------------------------------------------------------------------- /old/machine.json: -------------------------------------------------------------------------------- 1 | { 2 | "RoleName": "database-vm", 3 | "RoleType": "PersistentVMRole", 4 | "RoleSize": "A5", 5 | "AvailabilitySetName" : "databases", 6 | "OSVirtualHardDisk": { 7 | "OS": "Linux", 8 | "HostCaching": "ReadWrite", 9 | "DiskName": "database-vm-disk", 10 | "DiskLabel": "database-vm-disk", 11 | "SourceImageName": "Debian-Wheezy-635506180993665396", 12 | "RemoteSourceImageLink": "http://account.blob.core.windows.net/vmdepot-images/TE-2014-11-03-debianwheezy-os-2014-11-03.vhd", 13 | "MediaLink" : "http://account.blob.core.windows.net/vmdepot-images/database-vm-disk.vhd" 14 | }, 15 | "DataVirtualHardDisks" : [ 16 | {"HostCaching": "ReadOnly", "DiskLabel": "database-vm-data1", "Lun": "0", "LogicalDiskSizeInGB": "1023", "MediaLink" : "http://account.blob.core.windows.net/vmdepot-images/database-vm-data1.vhd"}, 17 | {"HostCaching": "ReadOnly", "DiskLabel": "database-vm-data2", "Lun": "1", "LogicalDiskSizeInGB": "1023", "MediaLink" : "http://account.blob.core.windows.net/vmdepot-images/database-vm-data2.vhd"}, 18 | {"HostCaching": "ReadOnly", "DiskLabel": "database-vm-xlog1", "Lun": "2", "LogicalDiskSizeInGB": "1023", "MediaLink" : "http://account.blob.core.windows.net/vmdepot-images/database-vm-xlog1.vhd"} 19 | ], 20 | "ConfigurationSets": [ 21 | { 22 | "ConfigurationSetType" : "LinuxProvisioningConfiguration", 23 | "HostName" : "database-vm", 24 | "UserName" : "ruth", 25 | "UserPassword" : "Supersecret123!!", 26 | "DisableSshPasswordAuthentication" : false 27 | }, 28 | { 29 | "ConfigurationSetType": "NetworkConfiguration", 30 | "SubnetNames": [ "mysubnet" ], 31 | "InputEndpoints": [], 32 | "PublicIPs": [], 33 | "StoredCertificateSettings": [] 34 | } 35 | ], 36 | "ProvisionGuestAgent": "true", 37 | "ResourceExtensionReferences": [] 38 | } 39 | -------------------------------------------------------------------------------- /old/postgresql-primer.md: -------------------------------------------------------------------------------- 1 | 2 | # PostgreSQL Primer 3 | 4 | - PostgreSQL developers are employed by a variety of companies, therefore the OSS dev team tries to re-use as much as possible 5 | - PostgreSQL is pretty robust. One customer hat 3-5 bluescreens per week, without data corruption 6 | - Two different data storages 7 | - General ledger == Write Ahead Log (WAL) records, "I will do the following", + fsync. Write caching is really bad here. Pure sequential writes. This must be written before a commit can be done. 8 | - Dann noch die eigentlichen DB files 9 | - Wenn die Tables nicht in Sync mit dem WAL record ist, wird postgres in den recovery modus gesetzt. 10 | - WAL records können auf andere Systeme kopiert werden (in 16MB chunks), WAL shipping, z.B. 11 | - für einen nachlaufenden Slave 12 | - Point in time recovery 13 | - Backup 14 | - Anderen Storage (S3, Glacier) 15 | - WAL records (binäre diffs) haben immer 16MB; können aber schon verschickt werden, wenn die 16MB noch nicht voll sind. (PGX log verzeichnis), rufen eine Shell auf, die irgendwas macht (archive command) 16 | - Seit 9.0 können WAL records auch per "streaming replication" versendet werden (bytes an einen TCP Port senden) 17 | - Wenn man "streaming replication" oder "WAL shipping" auf einen secondary server machen, ist der gewissermaßen immer im Recovery Modus. Auch wenn der Server im "Recovery Modus" ist, kann man lese-Queries dagegen schicken 18 | - 9.0 mach async streaming machen 19 | - 9.1 kann synchronous streaming replication erzwingen (garantiert auf mindestens einem Streaming Slave). Dieser Slave ist immer der erste in der Liste der Slaves. 20 | - "Synchronous streaming replicatoin" kann man auch für einzelne Transaktionen einschalten, also z.B. nur für wichtige Writes. 21 | - Trigger-basierte replication ("Slony" und "Londiste" sind trigger-based replication implementations). Slony (fragile C Code Base), Londiste (Python) . Diesen Trigger (on-update / on-insert) setzt man auf eine Datenbank. Londiste kommt von Skype. Londiste schreibt Änderungen in eine Queue in Postgres (Listen&Notify) rein. 22 | - Londiste nutzt man für "cross-version upgrade", 23 | - Trigger-based replication kann Probleme bei Schema-Updates erzeugen 24 | - Jedes Byte, welches in einer Tabelle geschrieben wird, sind mindestens 2 Bytes Festplatten-I/O 25 | - Ab 9.4 gibt es "Logical decoding", welches aus einem WAL Record rekonstruieren kann, was auf Anwendungsebene wohl passiert ist (menschenlesbar) 26 | - Logical Decoding used for multi-master replication (bi-directional replication sync hin- und her) 27 | - Problem ist in geo-verteilten Datacentern mit high latency. US-Datenbank für US-Kunden, EU-Datenbank für EU Kunden, und mit bi-directional replication alle Daten überall, die Daten der Kollegen halt etwas später (5 mins) 28 | - Streaming Replication ermöglicht "Read Scaling", also schreiben auf eine einzelne DB, und aus mehreren Read-Slaves rauslesen. Man kann in einem View schauen, wie weit der Slave hinterher ist. 29 | - Die Read-Slaves können zum Master promoted werden, wird von Read-Only zu Read-Write. Mit PGBouncer und DNS die Clients umbiegen. Wenn der Master wieder hochkommt, wird per RSYNC die letzten Änderungen vom neuen Master auf den wieder hochkommenden alten Master repliziert. (rep manager http://www.repmgr.org / https://github.com/2ndQuadrant/repmgr) 30 | - PostgreSQL forked bei neuen Verbindungen. pgBouncer ist ein Service, der Connection-Pooling für mis-behaving clients anbietet. pgBouncer authentifiziert Clients. 31 | 32 | -------------------------------------------------------------------------------- /old/presentation.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chgeuer/postgres-azure/5a5e7bfbac01d70c3f25c5b5e4db72a70366ea96/old/presentation.pptx -------------------------------------------------------------------------------- /postgresHAinExistingSubnet.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "environment": { 6 | "type": "string", 7 | "allowedValues": [ 8 | "Azure Cloud", 9 | "Azure German Cloud" 10 | ], 11 | "defaultValue": "Azure Cloud" 12 | }, 13 | "vNetRessourceGroup": { 14 | "type": "string" 15 | }, 16 | "vNetName": { 17 | "type": "string" 18 | }, 19 | "vNetSubnetName": { 20 | "type": "string" 21 | }, 22 | "vNetSubnetPrefix": { 23 | "type": "string", 24 | "defaultValue": "10.0.1." 25 | }, 26 | "vNetSubnetStartIP": { 27 | "type": "int", 28 | "defaultValue": 10 29 | }, 30 | "clusterName": { 31 | "type": "string" 32 | }, 33 | "instanceCount": { 34 | "defaultValue": 2, 35 | "minValue": 2, 36 | "maxValue": 250, 37 | "type": "int", 38 | "metadata": { 39 | "description": "Number of postgreSQL servers in the cluster." 40 | } 41 | }, 42 | "instanceSize": { 43 | "type": "string", 44 | "allowedValues": [ 45 | "Standard_DS1", 46 | "Standard_DS2", 47 | "Standard_DS3", 48 | "Standard_DS4", 49 | "Standard_DS11", 50 | "Standard_DS12", 51 | "Standard_DS13", 52 | "Standard_DS14", 53 | "Standard_GS1", 54 | "Standard_GS2", 55 | "Standard_GS3", 56 | "Standard_GS4", 57 | "Standard_GS5", 58 | "Standard_DS1_v2", 59 | "Standard_DS2_v2", 60 | "Standard_DS3_v2", 61 | "Standard_DS4_v2", 62 | "Standard_DS5_v2", 63 | "Standard_DS11_v2", 64 | "Standard_DS12_v2", 65 | "Standard_DS13_v2", 66 | "Standard_DS14_v2", 67 | "Standard_DS15_v2" 68 | ], 69 | "defaultValue": "Standard_DS2_v2", 70 | "metadata": { 71 | "description": "Size of the postgreSQL server in the cluster." 72 | } 73 | }, 74 | "dataDiskSizeInGB": { 75 | "type": "int", 76 | "defaultValue": 128, 77 | "allowedValues": [ 78 | 128, 79 | 512, 80 | 1024 81 | ], 82 | "metadata": { 83 | "description": "Defines the size of each data disk (premium storage sizes)" 84 | } 85 | }, 86 | "dataDiscCount": { 87 | "type": "string", 88 | "defaultValue": "4", 89 | "allowedValues": [ 90 | "0", 91 | "1", 92 | "2", 93 | "3", 94 | "4", 95 | "5", 96 | "6", 97 | "7", 98 | "8", 99 | "9", 100 | "10", 101 | "11", 102 | "12", 103 | "13", 104 | "14", 105 | "15", 106 | "16" 107 | ], 108 | "metadata": { 109 | "description": "This parameter allows the user to select the number of disks wanted" 110 | } 111 | }, 112 | "adminUsername": { 113 | "type": "string" 114 | }, 115 | "adminPassword": { 116 | "type": "securestring" 117 | } 118 | }, 119 | "variables": { 120 | "selectedEnvironmentIndex": "[int(replace(replace(parameters('environment'), 'Azure Cloud', '0'), 'Azure German Cloud', '1'))]", 121 | "storageEndpoints": [ 122 | "core.windows.net", 123 | "core.cloudapi.de" 124 | ], 125 | "storageEndpoint": "[variables('storageEndpoints')[variables('selectedEnvironmentIndex')]]", 126 | "lbName": "postgresLoadBalancer", 127 | "lbId": "[resourceId('Microsoft.Network/loadBalancers/', variables('lbName'))]", 128 | "vnetName": "[parameters('vNetName')]", 129 | "vnetId": "[resourceId(parameters('vNetRessourceGroup'), 'Microsoft.Network/virtualNetworks', variables('vnetName'))]", 130 | "zookeeperNetName": "[parameters('vNetSubnetName')]", 131 | "zookeeperNetPrefix": "[parameters('vNetSubnetPrefix')]", 132 | "zookeeperNetStartIP": "[parameters('vNetSubnetStartIP')]", 133 | "zookeeperInstanceCount": 3, 134 | "postgresNetName": "[parameters('vNetSubnetName')]", 135 | "postgresNetPrefix": "[parameters('vNetSubnetPrefix')]", 136 | "postgresNetStartIP": "[add(parameters('vNetSubnetStartIP'), variables('zookeeperInstanceCount'))]", 137 | "postgresInstanceCount": "[parameters('instanceCount')]", 138 | "osType": { 139 | "publisher": "Canonical", 140 | "offer": "UbuntuServer", 141 | "sku": "14.04.4-LTS", 142 | "version": "latest" 143 | }, 144 | "diskCaching": "ReadOnly", 145 | "fileroot": "https://.../public/postgresha/" 146 | }, 147 | "resources": [ 148 | { 149 | "type": "Microsoft.Storage/storageAccounts", 150 | "name": "[concat(copyIndex(), 'zk', parameters('clusterName'))]", 151 | "apiVersion": "2015-06-15", 152 | "location": "[resourceGroup().location]", 153 | "copy": { 154 | "name": "zookeeperStorageLoop", 155 | "count": "[variables('zookeeperInstanceCount')]" 156 | }, 157 | "properties": { 158 | "accountType": "Standard_LRS" 159 | } 160 | }, 161 | { 162 | "type": "Microsoft.Storage/storageAccounts", 163 | "name": "[concat(copyIndex(), 'pg', parameters('clusterName'))]", 164 | "apiVersion": "2015-06-15", 165 | "location": "[resourceGroup().location]", 166 | "copy": { 167 | "name": "postgresStorageLoop", 168 | "count": "[variables('postgresInstanceCount')]" 169 | }, 170 | "properties": { 171 | "accountType": "Premium_LRS" 172 | } 173 | }, 174 | { 175 | "type": "Microsoft.Compute/availabilitySets", 176 | "name": "postgresAvailabilitySet", 177 | "apiVersion": "2015-05-01-preview", 178 | "location": "[resourceGroup().location]", 179 | "properties": { 180 | "platformFaultDomainCount": 3, 181 | "platformUpdateDomainCount": 3 182 | } 183 | }, 184 | { 185 | "type": "Microsoft.Compute/availabilitySets", 186 | "name": "zookeeperAvailabilitySet", 187 | "apiVersion": "2015-05-01-preview", 188 | "location": "[resourceGroup().location]", 189 | "properties": { 190 | "platformFaultDomainCount": 3, 191 | "platformUpdateDomainCount" : 3 192 | } 193 | }, 194 | { 195 | "type": "Microsoft.Network/networkInterfaces", 196 | "name": "[concat('zookeeper', copyIndex(), '-nic')]", 197 | "dependsOn": [ 198 | "[variables('lbId')]" 199 | ], 200 | "apiVersion": "2015-05-01-preview", 201 | "location": "[resourceGroup().location]", 202 | "copy": { 203 | "name": "zookeeperNicLoop", 204 | "count": "[variables('zookeeperInstanceCount')]" 205 | }, 206 | "properties": { 207 | "ipConfigurations": [ 208 | { 209 | "name": "zookeeperIp", 210 | "properties": { 211 | "privateIPAllocationMethod": "Static", 212 | "privateIPAddress": "[concat(variables('zookeeperNetPrefix'), add(variables('zookeeperNetStartIP'), copyIndex()))]", 213 | "subnet": { 214 | "id": "[concat(variables('vnetId'), '/subnets/', variables('zookeeperNetName'))]" 215 | } 216 | } 217 | } 218 | ] 219 | } 220 | }, 221 | { 222 | "type": "Microsoft.Compute/virtualMachines", 223 | "name": "[concat('zookeeper', copyIndex())]", 224 | "apiVersion": "2015-05-01-preview", 225 | "location": "[resourceGroup().location]", 226 | "copy": { 227 | "name": "zookeeperVmLoop", 228 | "count": "[variables('zookeeperInstanceCount')]" 229 | }, 230 | "properties": { 231 | "availabilitySet": { 232 | "id": "[resourceId('Microsoft.Compute/availabilitySets', 'zookeeperAvailabilitySet')]" 233 | }, 234 | "hardwareProfile": { 235 | "vmSize": "Standard_A0" 236 | }, 237 | "osProfile": { 238 | "computerName": "[concat('zookeeper', copyIndex())]", 239 | "adminUsername": "[parameters('adminUserName')]", 240 | "adminPassword": "[parameters('adminPassword')]" 241 | }, 242 | "storageProfile": { 243 | "imageReference": { 244 | "publisher": "Canonical", 245 | "offer": "UbuntuServer", 246 | "sku": "14.04.4-LTS", 247 | "version": "latest" 248 | }, 249 | "osDisk": { 250 | "name": "osdisk", 251 | "vhd": { 252 | "uri": "[concat(reference(concat('Microsoft.Storage/storageAccounts/', copyIndex(), 'zk', parameters('clusterName')), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).primaryEndpoints.blob, 'vhds/', 'zookeeperosdisk', copyIndex(), '.vhd')]" 253 | }, 254 | "caching": "ReadWrite", 255 | "createOption": "FromImage" 256 | } 257 | }, 258 | "networkProfile": { 259 | "networkInterfaces": [ 260 | { 261 | "id": "[resourceId('Microsoft.Network/networkInterfaces', concat('zookeeper', copyIndex(), '-nic'))]" 262 | } 263 | ] 264 | } 265 | }, 266 | "dependsOn": [ 267 | "zookeeperStorageLoop", 268 | "[concat('Microsoft.Storage/storageAccounts/', copyIndex(), 'zk', parameters('clusterName'))]", 269 | "[concat('Microsoft.Network/networkInterfaces/', 'zookeeper', copyIndex(), '-nic')]", 270 | "[concat('Microsoft.Compute/availabilitySets/', 'zookeeperAvailabilitySet')]" 271 | ] 272 | }, 273 | { 274 | "type": "Microsoft.Compute/virtualMachines/extensions", 275 | "name": "[concat('zookeeper', copyIndex(), '/zookeeperExtension')]", 276 | "apiVersion": "2015-05-01-preview", 277 | "location": "[resourceGroup().location]", 278 | "copy": { 279 | "name": "zookeeperExtLoop", 280 | "count": "[variables('zookeeperInstanceCount')]" 281 | }, 282 | "properties": { 283 | "publisher": "Microsoft.OSTCExtensions", 284 | "type": "CustomScriptForLinux", 285 | "typeHandlerVersion": "1.2", 286 | "settings": { 287 | "fileUris": [ 288 | "[concat(variables('fileroot'), 'start-zk.sh')]" 289 | ], 290 | "commandToExecute": "[concat('./start-zk.sh ', copyIndex(), ' ', variables('zookeeperInstanceCount'), ' ', variables('zookeeperNetPrefix'), variables('zookeeperNetStartIP'))]" 291 | } 292 | }, 293 | "dependsOn": [ 294 | "[concat('Microsoft.Compute/virtualMachines/', 'zookeeper', copyIndex())]" 295 | ] 296 | }, 297 | { 298 | "type": "Microsoft.Network/networkInterfaces", 299 | "name": "[concat('postgres', copyIndex(), '-nic')]", 300 | "apiVersion": "2015-05-01-preview", 301 | "location": "[resourceGroup().location]", 302 | "copy": { 303 | "name": "postgresNicLoop", 304 | "count": "[variables('postgresInstanceCount')]" 305 | }, 306 | "properties": { 307 | "ipConfigurations": [ 308 | { 309 | "name": "postgresIp", 310 | "properties": { 311 | "privateIPAllocationMethod": "Static", 312 | "privateIPAddress": "[concat(variables('postgresNetPrefix'), add(variables('postgresNetStartIP'), copyIndex()))]", 313 | "subnet": { 314 | "id": "[concat(variables('vnetId'), '/subnets/', variables('postgresNetName'))]" 315 | }, 316 | "loadBalancerBackendAddressPools": [ 317 | { 318 | "id": "[concat(variables('lbId'), '/backendAddressPools/loadBalancerBackEnd')]" 319 | } 320 | ] 321 | } 322 | } 323 | ] 324 | }, 325 | "dependsOn": [ 326 | "[variables('lbId')]" 327 | ] 328 | }, 329 | { 330 | "apiVersion": "2015-01-01", 331 | "name": "[concat('postgresDataDisksConfigForVM', copyIndex())]", 332 | "type": "Microsoft.Resources/deployments", 333 | "copy": { 334 | "name": "postgresDataDisksConfigLoop", 335 | "count": "[variables('postgresInstanceCount')]" 336 | }, 337 | "properties": { 338 | "mode": "Incremental", 339 | "templateLink": { 340 | "uri": "[concat(variables('fileroot'), 'disksSelector.json')]", 341 | "contentVersion": "1.0.0.0" 342 | }, 343 | "parameters": { 344 | "numDataDisks": { 345 | "value": "[parameters('dataDiscCount')]" 346 | }, 347 | "diskStorageAccountName": { 348 | "value": "[concat(copyIndex(), 'pg', parameters('clusterName'))]" 349 | }, 350 | "diskCaching": { 351 | "value": "[variables('diskCaching')]" 352 | }, 353 | "diskSizeGB": { 354 | "value": "[parameters('dataDiskSizeInGB')]" 355 | }, 356 | "storageUrlSuffix": { 357 | "value": "[variables('storageEndpoint')]" 358 | } 359 | } 360 | } 361 | }, 362 | { 363 | "type": "Microsoft.Compute/virtualMachines", 364 | "name": "[concat('postgres', copyIndex())]", 365 | "apiVersion": "2015-05-01-preview", 366 | "location": "[resourceGroup().location]", 367 | "copy": { 368 | "name": "postgresVmLoop", 369 | "count": "[variables('postgresInstanceCount')]" 370 | }, 371 | "properties": { 372 | "availabilitySet": { 373 | "id": "[resourceId('Microsoft.Compute/availabilitySets', 'postgresAvailabilitySet')]" 374 | }, 375 | "hardwareProfile": { 376 | "vmSize": "[parameters('instanceSize')]" 377 | }, 378 | "osProfile": { 379 | "computerName": "[concat('postgres', copyIndex())]", 380 | "adminUsername": "[parameters('adminUserName')]", 381 | "adminPassword": "[parameters('adminPassword')]" 382 | }, 383 | "storageProfile": { 384 | "imageReference": { 385 | "publisher": "Canonical", 386 | "offer": "UbuntuServer", 387 | "sku": "14.04.4-LTS", 388 | "version": "latest" 389 | }, 390 | "osDisk": { 391 | "name": "osdisk", 392 | "vhd": { 393 | "uri": "[concat(reference(concat('Microsoft.Storage/storageAccounts/', copyIndex(), 'pg', parameters('clusterName')), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).primaryEndpoints.blob, 'vhds/', 'postgresosdisk', copyIndex(), '.vhd')]" 394 | }, 395 | "caching": "ReadWrite", 396 | "createOption": "FromImage" 397 | }, 398 | "dataDisks": "[reference(concat('postgresDataDisksConfigForVM', copyIndex())).outputs.dataDiskArray.value]" 399 | }, 400 | "networkProfile": { 401 | "networkInterfaces": [ 402 | { 403 | "id": "[resourceId('Microsoft.Network/networkInterfaces', concat('postgres', copyIndex(), '-nic'))]" 404 | } 405 | ] 406 | } 407 | }, 408 | "dependsOn": [ 409 | "postgresStorageLoop", 410 | "zookeeperVmLoop", 411 | "zookeeperExtLoop", 412 | "postgresDataDisksConfigLoop", 413 | "[concat('Microsoft.Storage/storageAccounts/', copyIndex(), 'pg', parameters('clusterName'))]", 414 | "[concat('Microsoft.Network/networkInterfaces/', 'postgres', copyIndex(), '-nic')]", 415 | "[concat('Microsoft.Compute/availabilitySets/', 'postgresAvailabilitySet')]" 416 | ] 417 | }, 418 | { 419 | "type": "Microsoft.Compute/virtualMachines/extensions", 420 | "name": "[concat('postgres', copyIndex(), '/postgresExtension')]", 421 | "apiVersion": "2015-05-01-preview", 422 | "location": "[resourceGroup().location]", 423 | "copy": { 424 | "name": "postgresExtLoop", 425 | "count": "[variables('postgresInstanceCount')]" 426 | }, 427 | "properties": { 428 | "publisher": "Microsoft.OSTCExtensions", 429 | "type": "CustomScriptForLinux", 430 | "typeHandlerVersion": "1.2", 431 | "settings": { 432 | "fileUris": [ 433 | "[concat(variables('fileroot'), 'ebs_raid0.sh')]", 434 | "[concat(variables('fileroot'), 'postgres_startup_existing_net.sh')]" 435 | ], 436 | "commandToExecute": "[concat('./postgres_startup_existing_net.sh ', parameters('clusterName'), ' ', variables('zookeeperNetPrefix'), variables('zookeeperNetStartIP'), ' ', variables('zookeeperInstanceCount'), ' ', variables('postgresNetPrefix'), variables('postgresNetStartIP'), ' ', variables('postgresInstanceCount'), ' ', copyIndex(), ' ', parameters('adminUsername'), ' \"', parameters('adminPassword'), '\" ')]" 437 | } 438 | }, 439 | "dependsOn": [ 440 | "[concat('Microsoft.Compute/virtualMachines/', 'postgres', copyIndex())]" 441 | ] 442 | }, 443 | { 444 | "type": "Microsoft.Network/loadBalancers", 445 | "name": "[variables('lbName')]", 446 | "apiVersion": "2015-06-15", 447 | "location": "[resourceGroup().location]", 448 | "properties": { 449 | "availabilitySet": { 450 | "id": "[resourceId('Microsoft.Compute/availabilitySets', 'postgresHaAvailabilitySet')]" 451 | }, 452 | "frontendIPConfigurations": [ 453 | { 454 | "name": "loadBalancerFrontEnd", 455 | "properties": { 456 | "privateIPAllocationMethod": "Static", 457 | "privateIPAddress": "[concat(variables('postgresNetPrefix'), add(variables('postgresNetStartIP'), variables('postgresInstanceCount')))]", 458 | "subnet": { 459 | "id": "[concat(variables('vnetId'), '/subnets/', variables('postgresNetName'))]" 460 | } 461 | 462 | } 463 | } 464 | ], 465 | "backendAddressPools": [ 466 | { 467 | "name": "loadBalancerBackEnd" 468 | } 469 | ], 470 | "loadBalancingRules": [ 471 | { 472 | "name": "postgresLbRule", 473 | "properties": { 474 | "frontendIPConfiguration": { 475 | "id": "[concat(variables('lbId'), '/frontendIPConfigurations/loadBalancerFrontEnd')]" 476 | }, 477 | "backendAddressPool": { 478 | "id": "[concat(variables('lbId'), '/backendAddressPools/loadBalancerBackEnd')]" 479 | }, 480 | "protocol": "Tcp", 481 | "frontendPort": 5432, 482 | "backendPort": 5000, 483 | "probe": { 484 | "id": "[concat(variables('lbId'), '/probes/postgresProbe')]" 485 | } 486 | } 487 | } 488 | ], 489 | "probes": [ 490 | { 491 | "name": "postgresProbe", 492 | "properties": { 493 | "protocol": "Tcp", 494 | "port": 5000, 495 | "intervalInSeconds": 5, 496 | "numberOfProbes": 2 497 | } 498 | } 499 | ] 500 | }, 501 | "dependsOn": [] 502 | } 503 | ], 504 | "outputs": 505 | { 506 | "postgresLoadBalancerIpAddress": { 507 | "type": "string", 508 | "value": "[reference(variables('lbName')).frontendIPConfigurations[0].properties.privateIPAddress]" 509 | }, 510 | "port": { 511 | "type": "int", 512 | "value": 5432 513 | }, 514 | "postgresUser": { 515 | "type": "string", 516 | "value": "admin" 517 | }, 518 | "postgresUserPassword": { 519 | "type": "securestring", 520 | "value": "[parameters('adminPassword')]" 521 | } 522 | } 523 | } -------------------------------------------------------------------------------- /postgresHAinExistingSubnet.parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "environment": { 6 | "value": "Azure German Cloud" 7 | }, 8 | "clusterName": { 9 | "value": "tani0" 10 | }, 11 | "adminUsername": { 12 | "value": "director" 13 | }, 14 | "adminPassword": { 15 | "value": "Start1234!!!" 16 | }, 17 | "vNetName": { 18 | "value": "postgresHAnet" 19 | }, 20 | "vNetSubnetName": { 21 | "value": "default" 22 | }, 23 | "vNetSubnetPrefix": { 24 | "value": "10.1.0." 25 | }, 26 | "vNetRessourceGroup": { 27 | "value": "postgresHAnet" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /postgresHAstandalone.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "environment": { 6 | "type": "string", 7 | "allowedValues": [ 8 | "Azure Cloud", 9 | "Azure German Cloud" 10 | ], 11 | "defaultValue": "Azure Cloud" 12 | }, 13 | "clusterName": { 14 | "type": "string" 15 | }, 16 | "postgresqlInstanceCount": { 17 | "defaultValue": 2, 18 | "minValue": 2, 19 | "maxValue": 10, 20 | "type": "int", 21 | "metadata": { "description": "Number of postgreSQL servers in the cluster." } 22 | }, 23 | "instanceSize": { 24 | "type": "string", 25 | "allowedValues": [ 26 | "Standard_DS1", "Standard_DS2", "Standard_DS3", "Standard_DS4", 27 | "Standard_DS11", "Standard_DS12", "Standard_DS13", "Standard_DS14", 28 | "Standard_GS1", "Standard_GS2", "Standard_GS3", "Standard_GS4", "Standard_GS5", 29 | "Standard_DS1_v2", "Standard_DS2_v2", "Standard_DS3_v2", "Standard_DS4_v2", "Standard_DS5_v2", 30 | "Standard_DS11_v2", "Standard_DS12_v2", "Standard_DS13_v2", "Standard_DS14_v2", "Standard_DS15_v2" 31 | ], 32 | "defaultValue": "Standard_DS2_v2", 33 | "metadata": { 34 | "description": "Size of the postgreSQL server in the cluster." 35 | } 36 | }, 37 | "dataDiskSizeInGB": { 38 | "type": "int", 39 | "defaultValue": 128, 40 | "allowedValues": [ 41 | 128, 42 | 512, 43 | 1024 44 | ], 45 | "metadata": { 46 | "description": "Defines the size of each data disk (premium storage sizes)" 47 | } 48 | }, 49 | "dataDiscCount": { 50 | "type": "string", 51 | "defaultValue": "4", 52 | "allowedValues": [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16" ], 53 | "metadata": { 54 | "description": "This parameter allows the user to select the number of disks wanted" 55 | } 56 | }, 57 | "adminUsername": { 58 | "type": "string" 59 | }, 60 | "adminPassword": { 61 | "type": "securestring" 62 | } 63 | }, 64 | "variables": { 65 | "selectedEnvironmentIndex": "[int(replace(replace(parameters('environment'), 'Azure Cloud', '0'), 'Azure German Cloud', '1'))]", 66 | "storageEndpoints": [ 67 | "core.windows.net", 68 | "core.cloudapi.de" 69 | ], 70 | "storageEndpoint": "[variables('storageEndpoints')[variables('selectedEnvironmentIndex')]]", 71 | "ipName": "[concat(parameters('clusterName'), '-ip')]", 72 | "ipId": "[resourceId('Microsoft.Network/publicIPAddresses/', variables('ipName'))]", 73 | "lbName": "postgresLoadBalancer", 74 | "lbId": "[resourceId('Microsoft.Network/loadBalancers/', variables('lbName'))]", 75 | "vnetName": "[concat(parameters('clusterName'), '-net')]", 76 | "vnetId": "[resourceId('Microsoft.Network/virtualNetworks', variables('vnetName'))]", 77 | "zookeeperNetName": "zookeeper-subnet", 78 | "zookeeperNetPrefix": "10.0.100.", 79 | "zookeeperNetStartIP": 10, 80 | "zookeeperInstanceCount": 3, 81 | "postgresNetName": "postgres-subnet", 82 | "postgresNetPrefix": "10.0.101.", 83 | "postgresNetStartIP": 10, 84 | "postgresInstanceCount": "[parameters('instanceCount')]", 85 | "osType": { 86 | "publisher": "Canonical", 87 | "offer": "UbuntuServer", 88 | "sku": "14.04.4-LTS", 89 | "version": "latest" 90 | }, 91 | "diskCaching": "ReadOnly", 92 | "fileroot": "https://raw.githubusercontent.com/chgeuer/postgres-azure/master/scripts/" 93 | }, 94 | "resources": [ 95 | { 96 | "type": "Microsoft.Network/virtualNetworks", 97 | "name": "[variables('vnetName')]", 98 | "apiVersion": "2015-06-15", 99 | "location": "[resourceGroup().location]", 100 | "tags": { }, 101 | "properties": { 102 | "addressSpace": { 103 | "addressPrefixes": [ 104 | "10.0.0.0/16" 105 | ] 106 | }, 107 | "subnets": [ 108 | { 109 | "name": "[variables('zookeeperNetName')]", 110 | "properties": { 111 | "addressPrefix": "[concat(variables('zookeeperNetPrefix'), '0/24')]" 112 | } 113 | }, 114 | { 115 | "name": "[variables('postgresNetName')]", 116 | "properties": { 117 | "addressPrefix": "[concat(variables('postgresNetPrefix'), '0/24')]" 118 | } 119 | } 120 | ] 121 | }, 122 | "dependsOn": [ ] 123 | }, 124 | { 125 | "type": "Microsoft.Storage/storageAccounts", 126 | "name": "[concat(copyIndex(), 'zk', parameters('clusterName'))]", 127 | "apiVersion": "2015-06-15", 128 | "location": "[resourceGroup().location]", 129 | "copy": { 130 | "name": "zookeeperStorageLoop", 131 | "count": "[variables('zookeeperInstanceCount')]" 132 | }, 133 | "properties": { 134 | "accountType": "Standard_LRS" 135 | } 136 | }, 137 | { 138 | "type": "Microsoft.Storage/storageAccounts", 139 | "name": "[concat(copyIndex(), 'pg', parameters('clusterName'))]", 140 | "apiVersion": "2015-06-15", 141 | "location": "[resourceGroup().location]", 142 | "copy": { 143 | "name": "postgresStorageLoop", 144 | "count": "[variables('postgresInstanceCount')]" 145 | }, 146 | "properties": { 147 | "accountType": "Premium_LRS" 148 | } 149 | }, 150 | { 151 | "type": "Microsoft.Network/publicIPAddresses", 152 | "name": "[variables('ipName')]", 153 | "apiVersion": "2016-03-30", 154 | "location": "[resourceGroup().location]", 155 | "properties": { 156 | "publicIPAllocationMethod": "Dynamic", 157 | "dnsSettings": { 158 | "domainNameLabel": "[parameters('clusterName')]" 159 | } 160 | } 161 | }, 162 | { 163 | "type": "Microsoft.Compute/availabilitySets", 164 | "name": "postgresAvailabilitySet", 165 | "apiVersion": "2015-05-01-preview", 166 | "location": "[resourceGroup().location]", 167 | "properties": { 168 | "platformFaultDomainCount": 3, 169 | "platformUpdateDomainCount": 3 170 | } 171 | }, 172 | { 173 | "type": "Microsoft.Compute/availabilitySets", 174 | "name": "zookeeperAvailabilitySet", 175 | "apiVersion": "2015-05-01-preview", 176 | "location": "[resourceGroup().location]", 177 | "properties": { } 178 | }, 179 | { 180 | "type": "Microsoft.Network/networkInterfaces", 181 | "name": "[concat('zookeeper', copyIndex(), '-nic')]", 182 | "apiVersion": "2015-05-01-preview", 183 | "location": "[resourceGroup().location]", 184 | "copy": { 185 | "name": "zookeeperNicLoop", 186 | "count": "[variables('zookeeperInstanceCount')]" 187 | }, 188 | "properties": { 189 | "ipConfigurations": [ 190 | { 191 | "name": "zookeeperIp", 192 | "properties": { 193 | "privateIPAllocationMethod": "Static", 194 | "privateIPAddress": "[concat(variables('zookeeperNetPrefix'), add(variables('zookeeperNetStartIP'), copyIndex()))]", 195 | "subnet": { 196 | "id": "[concat(variables('vnetId'), '/subnets/', variables('zookeeperNetName'))]" 197 | } 198 | } 199 | } 200 | ] 201 | }, 202 | "dependsOn": [ 203 | "[variables('lbId')]" 204 | ] 205 | }, 206 | { 207 | "type": "Microsoft.Compute/virtualMachines", 208 | "name": "[concat('zookeeper', copyIndex())]", 209 | "apiVersion": "2015-05-01-preview", 210 | "location": "[resourceGroup().location]", 211 | "copy": { 212 | "name": "zookeeperVmLoop", 213 | "count": "[variables('zookeeperInstanceCount')]" 214 | }, 215 | "properties": { 216 | "availabilitySet": { 217 | "id": "[resourceId('Microsoft.Compute/availabilitySets', 'zookeeperAvailabilitySet')]" 218 | }, 219 | "hardwareProfile": { 220 | "vmSize": "Standard_A0" 221 | }, 222 | "osProfile": { 223 | "computerName": "[concat('zookeeper', copyIndex())]", 224 | "adminUsername": "[parameters('adminUserName')]", 225 | "adminPassword": "[parameters('adminPassword')]" 226 | }, 227 | "storageProfile": { 228 | "imageReference": { 229 | "publisher": "Canonical", 230 | "offer": "UbuntuServer", 231 | "sku": "14.04.4-LTS", 232 | "version": "latest" 233 | }, 234 | "osDisk": { 235 | "name": "osdisk", 236 | "vhd": { 237 | "uri": "[concat(reference(concat('Microsoft.Storage/storageAccounts/', copyIndex(), 'zk', parameters('clusterName')), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).primaryEndpoints.blob, 'vhds/', 'zookeeperosdisk', copyIndex(), '.vhd')]" 238 | }, 239 | "caching": "ReadWrite", 240 | "createOption": "FromImage" 241 | } 242 | }, 243 | "networkProfile": { 244 | "networkInterfaces": [ 245 | { 246 | "id": "[resourceId('Microsoft.Network/networkInterfaces', concat('zookeeper', copyIndex(), '-nic'))]" 247 | } 248 | ] 249 | } 250 | }, 251 | "dependsOn": [ 252 | "zookeeperStorageLoop", 253 | "[concat('Microsoft.Storage/storageAccounts/', copyIndex(), 'zk', parameters('clusterName'))]", 254 | "[concat('Microsoft.Network/networkInterfaces/', 'zookeeper', copyIndex(), '-nic')]", 255 | "[concat('Microsoft.Compute/availabilitySets/', 'zookeeperAvailabilitySet')]" 256 | ] 257 | }, 258 | { 259 | "type": "Microsoft.Compute/virtualMachines/extensions", 260 | "name": "[concat('zookeeper', copyIndex(), '/zookeeperExtension')]", 261 | "apiVersion": "2015-05-01-preview", 262 | "location": "[resourceGroup().location]", 263 | "dependsOn": [ 264 | "[concat('Microsoft.Compute/virtualMachines/', 'zookeeper', copyIndex())]" 265 | ], 266 | "copy": { 267 | "name": "zookeeperExtLoop", 268 | "count": "[variables('zookeeperInstanceCount')]" 269 | }, 270 | "properties": { 271 | "publisher": "Microsoft.OSTCExtensions", 272 | "type": "CustomScriptForLinux", 273 | "typeHandlerVersion": "1.2", 274 | "settings": { 275 | "fileUris": [ 276 | "[concat(variables('fileroot'), 'start-zk.sh')]" 277 | ], 278 | "commandToExecute": "[concat('./start-zk.sh ', copyIndex(), ' ', variables('zookeeperInstanceCount'), ' ', variables('zookeeperNetPrefix'), variables('zookeeperNetStartIP'))]" 279 | } 280 | } 281 | }, 282 | { 283 | "type": "Microsoft.Network/networkInterfaces", 284 | "name": "[concat('postgres', copyIndex(), '-nic')]", 285 | "apiVersion": "2015-05-01-preview", 286 | "location": "[resourceGroup().location]", 287 | "copy": { 288 | "name": "postgresNicLoop", 289 | "count": "[variables('postgresInstanceCount')]" 290 | }, 291 | "properties": { 292 | "ipConfigurations": [ 293 | { 294 | "name": "postgresIp", 295 | "properties": { 296 | "privateIPAllocationMethod": "Static", 297 | "privateIPAddress": "[concat(variables('postgresNetPrefix'), add(variables('postgresNetStartIP'), copyIndex()))]", 298 | "subnet": { 299 | "id": "[concat(variables('vnetId'), '/subnets/', variables('postgresNetName'))]" 300 | }, 301 | "loadBalancerBackendAddressPools": [ 302 | { 303 | "id": "[concat(variables('lbId'), '/backendAddressPools/loadBalancerBackEnd')]" 304 | } 305 | ], 306 | "loadBalancerInboundNatRules": [ 307 | { 308 | "id": "[concat(variables('lbId'), '/inboundNatRules/postgresSsh', copyIndex())]" 309 | } 310 | ] 311 | } 312 | } 313 | ] 314 | }, 315 | "dependsOn": [ 316 | "[variables('lbId')]" 317 | ] 318 | }, 319 | { 320 | "apiVersion": "2015-01-01", 321 | "name": "[concat('postgresDataDisksConfigForVM', copyIndex())]", 322 | "type": "Microsoft.Resources/deployments", 323 | "copy": { 324 | "name": "postgresDataDisksConfigLoop", 325 | "count": "[variables('postgresInstanceCount')]" 326 | }, 327 | "properties": { 328 | "mode": "Incremental", 329 | "templateLink": { 330 | "uri": "[concat(variables('fileroot'), 'disksSelector.json')]", 331 | "contentVersion": "1.0.0.0" 332 | }, 333 | "parameters": { 334 | "numDataDisks": { 335 | "value": "[parameters('dataDiscCount')]" 336 | }, 337 | "diskStorageAccountName": { 338 | "value": "[concat(copyIndex(), 'pg', parameters('clusterName'))]" 339 | }, 340 | "diskCaching": { 341 | "value": "[variables('diskCaching')]" 342 | }, 343 | "diskSizeGB": { 344 | "value": "[parameters('dataDiskSizeInGB')]" 345 | }, 346 | "storageUrlSuffix": { 347 | "value": "[variables('storageEndpoint')]" 348 | } 349 | } 350 | } 351 | }, 352 | { 353 | "type": "Microsoft.Compute/virtualMachines", 354 | "name": "[concat('postgres', copyIndex())]", 355 | "apiVersion": "2015-05-01-preview", 356 | "location": "[resourceGroup().location]", 357 | "copy": { 358 | "name": "postgresVmLoop", 359 | "count": "[variables('postgresInstanceCount')]" 360 | }, 361 | "properties": { 362 | "availabilitySet": { 363 | "id": "[resourceId('Microsoft.Compute/availabilitySets', 'postgresAvailabilitySet')]" 364 | }, 365 | "hardwareProfile": { 366 | "vmSize": "[parameters('instanceSize')]" 367 | }, 368 | "osProfile": { 369 | "computerName": "[concat('postgres', copyIndex())]", 370 | "adminUsername": "[parameters('adminUserName')]", 371 | "adminPassword": "[parameters('adminPassword')]" 372 | }, 373 | "storageProfile": { 374 | "imageReference": { 375 | "publisher": "Canonical", 376 | "offer": "UbuntuServer", 377 | "sku": "14.04.4-LTS", 378 | "version": "latest" 379 | }, 380 | "osDisk": { 381 | "name": "osdisk", 382 | "vhd": { 383 | "uri": "[concat(reference(concat('Microsoft.Storage/storageAccounts/', copyIndex(), 'pg', parameters('clusterName')), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).primaryEndpoints.blob, 'vhds/', 'postgresosdisk', copyIndex(), '.vhd')]" 384 | }, 385 | "caching": "ReadWrite", 386 | "createOption": "FromImage" 387 | }, 388 | "dataDisks": "[reference(concat('postgresDataDisksConfigForVM', copyIndex())).outputs.dataDiskArray.value]" 389 | }, 390 | "networkProfile": { 391 | "networkInterfaces": [ 392 | { 393 | "id": "[resourceId('Microsoft.Network/networkInterfaces', concat('postgres', copyIndex(), '-nic'))]" 394 | } 395 | ] 396 | } 397 | }, 398 | "dependsOn": [ 399 | "postgresStorageLoop", 400 | "zookeeperVmLoop", 401 | "zookeeperExtLoop", 402 | "postgresDataDisksConfigLoop", 403 | "[concat('Microsoft.Storage/storageAccounts/', copyIndex(), 'pg', parameters('clusterName'))]", 404 | "[concat('Microsoft.Network/networkInterfaces/', 'postgres', copyIndex(), '-nic')]", 405 | "[concat('Microsoft.Compute/availabilitySets/', 'postgresAvailabilitySet')]" 406 | ] 407 | }, 408 | { 409 | "type": "Microsoft.Compute/virtualMachines/extensions", 410 | "name": "[concat('postgres', copyIndex(), '/postgresExtension')]", 411 | "apiVersion": "2015-05-01-preview", 412 | "location": "[resourceGroup().location]", 413 | "copy": { 414 | "name": "postgresExtLoop", 415 | "count": "[variables('postgresInstanceCount')]" 416 | }, 417 | "properties": { 418 | "publisher": "Microsoft.OSTCExtensions", 419 | "type": "CustomScriptForLinux", 420 | "typeHandlerVersion": "1.2", 421 | "settings": { 422 | "fileUris": [ 423 | "[concat(variables('fileroot'), 'setup-raid.sh')]", 424 | "[concat(variables('fileroot'), 'start-pg.sh')]" 425 | ], 426 | "commandToExecute": "[concat('./start-pg.sh ', parameters('clusterName'), ' ', variables('zookeeperNetPrefix'), variables('zookeeperNetStartIP'), ' ', variables('zookeeperInstanceCount'), ' ', variables('postgresNetPrefix'), variables('postgresNetStartIP'), ' ', variables('postgresInstanceCount'), ' ', copyIndex(), ' ', parameters('adminUsername'), ' \"', parameters('adminPassword'), '\" ')]" 427 | } 428 | }, 429 | "dependsOn": [ 430 | "[concat('Microsoft.Compute/virtualMachines/', 'postgres', copyIndex())]" 431 | ] 432 | }, 433 | { 434 | "type": "Microsoft.Network/loadBalancers", 435 | "name": "[variables('lbName')]", 436 | "apiVersion": "2015-06-15", 437 | "location": "[resourceGroup().location]", 438 | "properties": { 439 | "availabilitySet": { 440 | "id": "[resourceId('Microsoft.Compute/availabilitySets', 'postgresHaAvailabilitySet')]" 441 | }, 442 | "frontendIPConfigurations": [ 443 | { 444 | "name": "loadBalancerFrontEnd", 445 | "properties": { 446 | "publicIPAddress": { 447 | "id": "[variables('ipId')]" 448 | } 449 | } 450 | } 451 | ], 452 | "backendAddressPools": [ 453 | { 454 | "name": "loadBalancerBackEnd" 455 | } 456 | ], 457 | "loadBalancingRules": [ 458 | { 459 | "name": "postgresLbRule", 460 | "properties": { 461 | "frontendIPConfiguration": { 462 | "id": "[concat(variables('lbId'), '/frontendIPConfigurations/loadBalancerFrontEnd')]" 463 | }, 464 | "backendAddressPool": { 465 | "id": "[concat(variables('lbId'), '/backendAddressPools/loadBalancerBackEnd')]" 466 | }, 467 | "protocol": "Tcp", 468 | "frontendPort": 5432, 469 | "backendPort": 5000, 470 | "probe": { 471 | "id": "[concat(variables('lbId'), '/probes/postgresProbe')]" 472 | } 473 | } 474 | } 475 | ], 476 | "probes": [ 477 | { 478 | "name": "postgresProbe", 479 | "properties": { 480 | "protocol": "Tcp", 481 | "port": 5000, 482 | "intervalInSeconds": 5, 483 | "numberOfProbes": 2 484 | } 485 | } 486 | ], 487 | "inboundNatRules": [ 488 | { 489 | "name": "postgresSsh0", 490 | "properties": { 491 | "frontendIPConfiguration": { 492 | "id": "[concat(variables('lbId'), '/frontendIPConfigurations/loadBalancerFrontEnd')]" 493 | }, 494 | "protocol": "Tcp", 495 | "frontendPort": 10110, 496 | "backendPort": 22 497 | } 498 | }, 499 | { 500 | "name": "postgresSsh1", 501 | "properties": { 502 | "frontendIPConfiguration": { 503 | "id": "[concat(variables('lbId'), '/frontendIPConfigurations/loadBalancerFrontEnd')]" 504 | }, 505 | "protocol": "Tcp", 506 | "frontendPort": 10111, 507 | "backendPort": 22 508 | } 509 | }, 510 | { 511 | "name": "postgresSsh2", 512 | "properties": { 513 | "frontendIPConfiguration": { 514 | "id": "[concat(variables('lbId'), '/frontendIPConfigurations/loadBalancerFrontEnd')]" 515 | }, 516 | "protocol": "Tcp", 517 | "frontendPort": 10112, 518 | "backendPort": 22 519 | } 520 | }, 521 | { 522 | "name": "postgresSsh3", 523 | "properties": { 524 | "frontendIPConfiguration": { 525 | "id": "[concat(variables('lbId'), '/frontendIPConfigurations/loadBalancerFrontEnd')]" 526 | }, 527 | "protocol": "Tcp", 528 | "frontendPort": 10113, 529 | "backendPort": 22 530 | } 531 | }, 532 | { 533 | "name": "postgresSsh4", 534 | "properties": { 535 | "frontendIPConfiguration": { 536 | "id": "[concat(variables('lbId'), '/frontendIPConfigurations/loadBalancerFrontEnd')]" 537 | }, 538 | "protocol": "Tcp", 539 | "frontendPort": 10114, 540 | "backendPort": 22 541 | } 542 | }, 543 | { 544 | "name": "postgresSsh5", 545 | "properties": { 546 | "frontendIPConfiguration": { 547 | "id": "[concat(variables('lbId'), '/frontendIPConfigurations/loadBalancerFrontEnd')]" 548 | }, 549 | "protocol": "Tcp", 550 | "frontendPort": 10115, 551 | "backendPort": 22 552 | } 553 | }, 554 | { 555 | "name": "postgresSsh6", 556 | "properties": { 557 | "frontendIPConfiguration": { 558 | "id": "[concat(variables('lbId'), '/frontendIPConfigurations/loadBalancerFrontEnd')]" 559 | }, 560 | "protocol": "Tcp", 561 | "frontendPort": 10116, 562 | "backendPort": 22 563 | } 564 | }, 565 | { 566 | "name": "postgresSsh7", 567 | "properties": { 568 | "frontendIPConfiguration": { 569 | "id": "[concat(variables('lbId'), '/frontendIPConfigurations/loadBalancerFrontEnd')]" 570 | }, 571 | "protocol": "Tcp", 572 | "frontendPort": 10117, 573 | "backendPort": 22 574 | } 575 | }, 576 | { 577 | "name": "postgresSsh8", 578 | "properties": { 579 | "frontendIPConfiguration": { 580 | "id": "[concat(variables('lbId'), '/frontendIPConfigurations/loadBalancerFrontEnd')]" 581 | }, 582 | "protocol": "Tcp", 583 | "frontendPort": 10118, 584 | "backendPort": 22 585 | } 586 | }, 587 | { 588 | "name": "postgresSsh9", 589 | "properties": { 590 | "frontendIPConfiguration": { 591 | "id": "[concat(variables('lbId'), '/frontendIPConfigurations/loadBalancerFrontEnd')]" 592 | }, 593 | "protocol": "Tcp", 594 | "frontendPort": 10119, 595 | "backendPort": 22 596 | } 597 | } 598 | ] 599 | }, 600 | "dependsOn": [ 601 | "[variables('ipId')]" 602 | ] 603 | } 604 | ], 605 | "outputs": 606 | { 607 | "fqdn": { 608 | "type": "string", 609 | "value": "[reference(variables('ipName')).dnsSettings.fqdn]" 610 | }, 611 | "port": { 612 | "type": "int", 613 | "value": 5432 614 | }, 615 | "postgresUser": { 616 | "type": "string", 617 | "value": "admin" 618 | }, 619 | "postgresUserPassword": { 620 | "type": "securestring", 621 | "value": "[parameters('adminPassword')]" 622 | } 623 | } 624 | } -------------------------------------------------------------------------------- /postgresHAstandalone.parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "environment": { 6 | "value": "Azure German Cloud" 7 | }, 8 | "clusterName": { 9 | "value": "tani2" 10 | }, 11 | "instanceSize": { 12 | "value": "Standard_DS2_v2" 13 | }, 14 | "adminUsername": { 15 | "value": "director" 16 | }, 17 | "adminPassword": { 18 | "value": "Start1234!!!" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /scripts/setup-raid.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # ./setup-raid.sh 5 | # 6 | 7 | if [ "$#" -ne 1 ]; then 8 | echo "Illegal number of parameters: ./ebs_raid0.sh " 9 | exit 10 | fi 11 | 12 | mount_point=$1 13 | 14 | drive_names=$(lsblk | grep ^sd | grep -w -v -e sda -e sdb | awk '{print $1;}') 15 | count=0 16 | drives="" 17 | for i in $drive_names; do 18 | drives="$drives /dev/$i" 19 | count=$((count+1)) 20 | done 21 | 22 | mkdir -p "${mount_point}" 23 | 24 | root_drive=$(df -h | grep /dev/sda) 25 | 26 | if [[ "$root_drive" == "" ]]; then 27 | echo "Detected 'xvd' drive naming scheme \(root: $root_drive\)" 28 | DRIVE_SCHEME='xvd' 29 | else 30 | echo "Detected 'sd' drive naming scheme \(root: $root_drive\)" 31 | DRIVE_SCHEME='sd' 32 | fi 33 | 34 | partprobe 35 | mdadm --verbose --create /dev/md1 --level=0 --name=raid -c256 --raid-devices=$count $drives 36 | md=$(grep sdc /proc/mdstat | cut -d':' -f1) 37 | 38 | echo "dev.raid.speed_limit_min = 1000000" >> /etc/sysctl.conf 39 | echo "dev.raid.speed_limit_max = 2000000" >> /etc/sysctl.conf 40 | 41 | echo DEVICE "$drives" | tee /etc/mdadm.conf 42 | mdadm --detail --scan | tee -a /etc/mdadm.conf 43 | 44 | 45 | mkfs.xfs -f -d su=256k,sw=4 -l version=2,su=256k -i size=1024 /dev/$md 46 | mount -t xfs -o rw,uqnoenforce,gqnoenforce,noatime,nodiratime,logbufs=8,logbsize=256k,largeio,inode64,swalloc,allocsize=131072k,nobarrier /dev/$md $mount_point 47 | 48 | for i in $drive_names; do 49 | echo 8192 > "/sys/block/$i/queue/nr_requests" 50 | echo noop > "/sys/block/$i/queue/scheduler" 51 | done 52 | 53 | for i in $md 54 | do 55 | echo noop > "/sys/block/$i/queue/scheduler" 56 | echo 256 > "/sys/block/$i/queue/read_ahead_kb" 57 | echo 0 > "/sys/block/$i/queue/rotational" 58 | done 59 | 60 | echo 30 > /proc/sys/vm/vfs_cache_pressure 61 | echo never > /sys/kernel/mm/transparent_hugepage/enabled 62 | echo 20 > /proc/sys/vm/dirty_ratio 63 | echo 10 > /proc/sys/vm/dirty_background_ratio 64 | 65 | # Remove xvdb/sdb from fstab 66 | chmod 644 /etc/fstab 67 | sed -i "/${DRIVE_SCHEME}b/d" /etc/fstab 68 | 69 | # Make raid appear on reboot 70 | echo "/dev/$md $mount_point xfs noatime 0 0" | tee -a /etc/fstab 71 | -------------------------------------------------------------------------------- /scripts/start-pg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # /var/lib/waagent/Microsoft.OSTCExtensions.CustomScriptForLinux-1.2.2.0/download/0 4 | # /usr/local/patroni-6eb2e2114453545256ac7cbfec55bda285ffb955 5 | 6 | # http://jvns.ca/blog/2017/03/26/bash-quirks/ 7 | # https://google.github.io/styleguide/shell.xml#Checking_Return_Values 8 | # http://www.kfirlavi.com/blog/2012/11/14/defensive-bash-programming/ 9 | 10 | function createIp { 11 | startIpInput=$1 12 | index=$2 13 | 14 | #change internal field separator 15 | oldIFS=$IFS 16 | IFS=. 17 | #parse IP into array 18 | ary=($startIpInput) 19 | #reset internal field separator 20 | IFS=$oldIFS 21 | 22 | #create C-Net of Ip 23 | ip="" 24 | for (( i=0; i<$((${#ary[@]}-1)); i++ )) 25 | do 26 | ip="$ip${ary[$i]}." 27 | done 28 | 29 | #create D-Net of Ip 30 | ip="$ip$((${ary[-1]}+index))" 31 | printf "%s" "$ip" 32 | } 33 | 34 | # curl -L https://raw.githubusercontent.com/chgeuer/postgres-azure/master/scripts/start-pg.sh -o start-pg.sh 35 | # ./0start-pg.sh clustername 10.0.0.10 3 10.0.1.10 2 0 chgeuer supersecret123.- 36 | # lint using https://www.shellcheck.net/# 37 | 38 | # "commandToExecute": "[concat('./start-pg.sh ', 39 | # parameters('clusterName'), ' ', 40 | # concat(variables('commonSettings').vnet.subnet.zookeeper.addressRangePrefix, '.10'), ' ', 41 | # variables('commonSettings').instanceCount.zookeeper, ' ', 42 | # concat(variables('commonSettings').vnet.subnet.postgresql.addressRangePrefix, '.10'), ' ', 43 | # variables('commonSettings').instanceCount.postgresql, ' ', 44 | # copyIndex(), ' ', 45 | # parameters('postgresqlUsername'), ' ', 46 | # concat('\"', parameters('postgresqlPassword'), '\"'), ' ' 47 | # variables('commonSettings').softwareversions.patroni, ' ', 48 | # variables('commonSettings').softwareversions.postgres )]" 49 | 50 | clusterName=$1 51 | startIpZooKeepers=$2 52 | amountZooKeepers=$3 53 | startIpPostgres=$4 54 | amountPostgres=$5 55 | myIndex=$6 56 | postgresqlUsername=$7 57 | postgresqlPassword=$8 58 | patroniversion=$9 59 | pgversion=${10} 60 | 61 | patroniDir="/usr/local/patroni-${patroniversion}" 62 | patroniCfg="${patroniDir}/postgres.yml" 63 | hacfgFile="${patroniDir}/postgresha.cfg" 64 | 65 | cat > startup.log <<-EOF 66 | Cluster name: $clusterName 67 | startIpZooKeepers: $startIpZooKeepers 68 | amountZooKeepers: $amountZooKeepers 69 | startIpPostgres: $startIpPostgres 70 | amountPostgres: $amountPostgres 71 | myIndex: $myIndex 72 | postgresqlUsername: $postgresqlUsername 73 | postgresqlPassword: $postgresqlPassword 74 | pgversion: $pgversion 75 | patroniversion: $patroniversion 76 | EOF 77 | 78 | # 79 | # install & configure saltstack 80 | # 81 | curl -L https://bootstrap.saltstack.com -o bootstrap_salt.sh 82 | sh bootstrap_salt.sh 83 | 84 | cat >> /etc/salt/minion <<-EOF 85 | file_client: local 86 | EOF 87 | 88 | mkdir --parents /srv/salt 89 | 90 | cat > /srv/salt/top.sls <<-EOF 91 | base: 92 | '*': 93 | - webserver 94 | - postgres-pkgs 95 | - patroni 96 | EOF 97 | 98 | cat > /srv/salt/webserver.sls <<-EOF 99 | python-pip: 100 | pkg.installed 101 | webserver: 102 | pkg.installed: 103 | - pkgs: 104 | - mdadm 105 | - xfsprogs 106 | - supervisor 107 | - curl 108 | - jq 109 | - haproxy 110 | - libpq-dev 111 | - python 112 | - python-dev 113 | - python-psycopg2 114 | - python-yaml 115 | - python-requests 116 | - python-six 117 | - python-dateutil 118 | - python-urllib3 119 | - python-dnspython 120 | - python-pip 121 | - python-setuptools 122 | - python-kazoo 123 | - python-prettytable 124 | - python-wheel 125 | pip.installed: 126 | - require: 127 | - pkg: python-pip 128 | - upgrade: 129 | - pip >= 9.0.1 130 | - PyYAML 131 | - requests 132 | - six 133 | - prettytable 134 | - names: 135 | - boto 136 | - psycopg2 137 | - kazoo 138 | - python-etcd == 0.4.3 139 | - python-consul == 0.7.0 140 | - click 141 | - tzlocal 142 | - python-dateutil 143 | - urllib3 144 | - PySocks 145 | EOF 146 | 147 | cat > /srv/salt/postgres-pkgs.sls <<-EOF 148 | postgres-pkgs: 149 | pkg.installed: 150 | - pkgs: 151 | - postgresql-$pgversion 152 | - postgresql-contrib-$pgversion 153 | postgres_repo: 154 | pkgrepo.managed: 155 | - name: "deb http://apt.postgresql.org/pub/repos/apt/ {{ grains['oscodename'] }}-pgdg main" 156 | - file: /etc/apt/sources.list.d/pgdg.list 157 | - key_url: https://www.postgresql.org/media/keys/ACCC4CF8.asc 158 | - require_in: 159 | - pkg: postgres-pkgs 160 | EOF 161 | 162 | cat > /srv/salt/patroni.sls <<-EOF 163 | patroni: 164 | pkg.installed: 165 | - pkgs: 166 | - unzip 167 | cmd.run: 168 | - name: curl -L https://github.com/zalando/patroni/archive/${patroniversion}.zip -o /etc/salt/patroni-${patroniversion}.zip 169 | - creates: /etc/salt/patroni-${patroniversion}.zip 170 | archive.extracted: 171 | - name: /tmp/patroni-${patroniversion} 172 | - source: /etc/salt/patroni-${patroniversion}.zip 173 | - use_cmd_unzip: true 174 | - force: true 175 | file.copy: 176 | - source: /tmp/patroni-${patroniversion}/patroni-${patroniversion} 177 | - name: ${patroniDir} 178 | - force: true 179 | EOF 180 | 181 | salt-call --local state.apply 182 | 183 | export PATH=/usr/lib/postgresql/${pgversion}/bin:$PATH 184 | 185 | # create RAID 186 | 187 | mount_point=/mnt/database 188 | 189 | sh setup-raid.sh "$mount_point" 190 | mkdir "$mount_point/data" 191 | chmod 777 "$mount_point/data" 192 | 193 | # write configuration 194 | echo "START IP PROGRESS $startIpPostgres" 195 | 196 | cat > $patroniCfg <<-EOF 197 | scope: &scope $clusterName 198 | ttl: &ttl 30 199 | loop_wait: &loop_wait 10 200 | EOF 201 | 202 | if [[ $myIndex -eq 0 ]]; then 203 | cat >> $patroniCfg <<-EOF 204 | name: postgres$myIndex 205 | EOF 206 | fi 207 | 208 | cat >> $patroniCfg <<-EOF 209 | restapi: 210 | listen: $(createIp "$startIpPostgres" "$myIndex"):8008 211 | connect_address: $(createIp "$startIpPostgres" "$myIndex"):8008 212 | zookeeper: 213 | scope: *scope 214 | session_timeout: *ttl 215 | reconnect_timeout: *loop_wait 216 | hosts: 217 | EOF 218 | 219 | for i in `seq 0 $((instanceCount-1))` 220 | do 221 | cat >> $patroniCfg <<-EOF 222 | - $(createIp "$startIpZooKeepers" "$i"):2181 223 | EOF 224 | done 225 | 226 | echo "" >> $patroniCfg 227 | 228 | if [[ $myIndex -eq 0 ]]; then 229 | cat >> $patroniCfg <<-EOF 230 | bootstrap: 231 | dcs: 232 | ttl: *ttl 233 | loop_wait: *loop_wait 234 | retry_timeout: *loop_wait 235 | maximum_lag_on_failover: 1048576 236 | postgresql: 237 | use_pg_rewind: true 238 | use_slots: true 239 | parameters: 240 | archive_mode: "on" 241 | archive_timeout: 1800s 242 | archive_command: mkdir -p ../wal_archive && test ! -f ../wal_archive/%f && cp %p ../wal_archive/%f 243 | recovery_conf: 244 | restore_command: cp ../wal_archive/%f %p 245 | initdb: 246 | - encoding: UTF8 247 | - data-checksums 248 | pg_hba: 249 | - host replication all 0.0.0.0/0 md5 250 | - host all all 0.0.0.0/0 md5 251 | users: 252 | admin: 253 | password: "$postgresqlPassword" 254 | options: 255 | - createrole 256 | - createdb 257 | EOF 258 | fi 259 | 260 | cat >> $patroniCfg <<-EOF 261 | tags: 262 | nofailover: false 263 | noloadbalance: false 264 | clonefrom: false 265 | postgresql: 266 | EOF 267 | 268 | if [[ $myIndex -ne 0 ]]; then 269 | cat >> $patroniCfg <<-EOF 270 | name: postgres${myIndex} 271 | EOF 272 | fi 273 | 274 | cat >> $patroniCfg <<-EOF 275 | listen: '*:5433' 276 | connect_address: $(createIp "$startIpPostgres" "$myIndex"):5433 277 | data_dir: ${mount_point}/data/postgresql 278 | pgpass: /tmp/pgpass 279 | EOF 280 | 281 | if [[ $myIndex -ne 0 ]]; then 282 | cat >> $patroniCfg <<-EOF 283 | maximum_lag_on_failover: 1048576 284 | use_slots: true 285 | initdb: 286 | - encoding: UTF8 287 | - data-checksums 288 | pg_rewind: 289 | username: postgres 290 | password: "$postgresqlPassword" 291 | pg_hba: 292 | - host replication all 0.0.0.0/0 md5 293 | - host all all 0.0.0.0/0 md5 294 | replication: 295 | username: replicator 296 | password: "$postgresqlPassword" 297 | superuser: 298 | username: postgres 299 | password: "$postgresqlPassword" 300 | admin: 301 | username: admin 302 | password: "$postgresqlPassword" 303 | create_replica_method: 304 | - basebackup 305 | recovery_conf: 306 | restore_command: cp ../wal_archive/%f %p 307 | parameters: 308 | archive_mode: "on" 309 | wal_level: hot_standby 310 | archive_command: mkdir -r ../wal_archive && test ! -f ../wal_archive/%f && cp %cp ../wal_archive/%f 311 | max_wal_senders: 10 312 | wal_keep_segments: 8 313 | archive_timeout: 1800s 314 | max_replication_slots: 10 315 | hot_standby: "on" 316 | wal_log_hints: "on" 317 | unix_socket_directories: '.' 318 | EOF 319 | else 320 | cat >> $patroniCfg <<-EOF 321 | authentication: 322 | replication: 323 | username: replicator 324 | password: "$postgresqlPassword" 325 | superuser: 326 | username: postgres 327 | password: "$postgresqlPassword" 328 | parameters: 329 | unix_socket_directories: '.' 330 | EOF 331 | fi 332 | 333 | chmod 666 $patroniCfg 334 | 335 | cat > $hacfgFile <<-EOF 336 | global 337 | maxconn 100 338 | defaults 339 | log global 340 | mode tcp 341 | retries 2 342 | timeout client 30m 343 | timeout connect 4s 344 | timeout server 30m 345 | timeout check 5s 346 | frontend ft_postgresql 347 | bind *:5000 348 | default_backend bk_db 349 | backend bk_db 350 | option httpchk 351 | EOF 352 | 353 | for i in `seq 0 $((amountPostgres-1))` 354 | do 355 | cat >> $hacfgFile <<-EOF 356 | server Postgres$i $(createIp "$startIpPostgres" "$i"):5433 maxconn 100 check port 8008 357 | EOF 358 | done 359 | chmod 666 $hacfgFile 360 | 361 | cat > ${patroniDir}/patroni_start.sh <<-EOF 362 | #!/bin/bash 363 | export PATH=/usr/lib/postgresql/${pgversion}/bin:\$PATH 364 | ${patroniDir}/patroni.py ${patroniCfg} 365 | EOF 366 | chmod +x ${patroniDir}/patroni_start.sh 367 | 368 | cat > /etc/supervisor/conf.d/patroni.conf <<-EOF 369 | [program:patroni] 370 | command=${patroniDir}/patroni_start.sh 371 | user=$postgresqlUsername 372 | autostart=true 373 | autorestart=true 374 | stderr_logfile=/var/log/patroni.err.log 375 | stdout_logfile=/var/log/patroni.out.log 376 | EOF 377 | 378 | cat > /etc/supervisor/conf.d/haproxy.conf <<-EOF 379 | [program:haproxy] 380 | command=haproxy -D -f $hacfgFile 381 | autostart=true 382 | autorestart=true 383 | stderr_logfile=/var/log/haproxy.err.log 384 | stdout_logfile=/var/log/haproxy.out.log 385 | EOF 386 | 387 | service supervisor restart 388 | supervisorctl reread 389 | supervisorctl update 390 | 391 | exit 0 392 | -------------------------------------------------------------------------------- /scripts/start-zk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function createIp { 4 | startIpInput=$1 5 | index=$2 6 | 7 | #change internal field separator 8 | oldIFS=$IFS 9 | IFS=. 10 | #parse IP into array 11 | ary=($startIpInput) 12 | #reset internal field separator 13 | IFS=$oldIFS 14 | 15 | #create C-Net of Ip 16 | ip="" 17 | for (( i=0; i<$((${#ary[@]}-1)); i++ )) 18 | do 19 | ip="$ip${ary[$i]}." 20 | done 21 | 22 | #create D-Net of Ip 23 | ip="$ip$((${ary[-1]}+index))" 24 | printf "%s" "$ip" 25 | } 26 | 27 | # "commandToExecute": "[concat('./start-zk.sh', ' ', 28 | # copyIndex(), ' ', 29 | # variables('commonSettings').instanceCount.zookeeper, ' ', 30 | # concat(variables('commonSettings').vnet.subnet.zookeeper.addressRangePrefix, '.10'), ' ', 31 | # variables('commonSettings').softwareversions.zookeeper, ' ', 32 | # variables('commonSettings').softwareversions.java4zookeeper1, ' ', 33 | # variables('commonSettings').softwareversions.java4zookeeper2)]" 34 | 35 | myIndex=$1 36 | zookeeperInstanceCount=$2 37 | startIp=$3 38 | zkversion=$4 39 | javaversion1=$5 40 | javaversion2=$6 41 | 42 | # myIndex=1 43 | # zookeeperInstanceCount=3 44 | # startIp=10.0.0.10 45 | # zkversion=3.4.9 46 | # javaversion1=7u75 47 | # javaversion2=b13 48 | 49 | apt-get -y install jq supervisor 50 | 51 | # 52 | # Install Java 53 | # 54 | # wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie" -c -O "jdk-8u121-linux-x64.tar.gz" "http://download.oracle.com/otn-pub/java/jdk/8u121-b13/e9e7ea248e2c4826b92b3f075a80e441/jdk-8u121-linux-x64.tar.gz" 55 | wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie" -c -O "jdk-${javaversion1}-linux-x64.tar.gz" "http://download.oracle.com/otn-pub/java/jdk/${javaversion1}-${javaversion2}/jdk-${javaversion1}-linux-x64.tar.gz" 56 | 57 | mkdir --parents /usr/lib/jvm/jdk-${javaversion1} 58 | tar -xvf jdk-${javaversion1}-linux-x64.tar.gz --strip-components=1 --directory /usr/lib/jvm/jdk-${javaversion1} 59 | update-alternatives --install "/usr/bin/java" "java" "/usr/lib/jvm/jdk-${javaversion1}/bin/java" 1 60 | update-alternatives --install "/usr/bin/javac" "javac" "/usr/lib/jvm/jdk-${javaversion1}/bin/javac" 1 61 | update-alternatives --install "/usr/bin/javaws" "javaws" "/usr/lib/jvm/jdk-${javaversion1}/bin/javaws" 1 62 | chmod a+x /usr/bin/java 63 | chmod a+x /usr/bin/javac 64 | chmod a+x /usr/bin/javaws 65 | 66 | # 67 | # Install ZooKeeper 68 | # 69 | zkbindir=/usr/local/zookeeper-${zkversion} 70 | zkworkdir=/var/lib/zookeeper 71 | mkdir --parents $zkbindir 72 | mkdir --parents $zkworkdir 73 | mirror=$(curl -s "https://www.apache.org/dyn/closer.cgi?as_json=1" | jq --raw-output .preferred) 74 | curl --get --url "${mirror}zookeeper/zookeeper-${zkversion}/zookeeper-${zkversion}.tar.gz" --output "zookeeper-${zkversion}.tar.gz" 75 | tar -xvf "zookeeper-${zkversion}.tar.gz" --strip-components=1 --directory "$zkbindir" 76 | 77 | cat > "$zkbindir/conf/zoo.cfg" <<-EOF 78 | tickTime=2000 79 | dataDir=${zkworkdir} 80 | clientPort=2181 81 | initLimit=5 82 | syncLimit=2 83 | EOF 84 | 85 | for i in $(seq 1 $zookeeperInstanceCount) 86 | do 87 | cat >> "$zkbindir/conf/zoo.cfg" <<-EOF 88 | server.$i=$(createIp "$startIp" "$((i-1))"):2888:3888 89 | EOF 90 | done 91 | 92 | cat > "${zkworkdir}/myid" <<-EOF 93 | $((myIndex + 1)) 94 | EOF 95 | 96 | # command = /usr/lib/jvm/jdk-${javaversion1}/bin/java -Dzookeeper.log.dir="." -Dzookeeper.root.logger="INFO,CONSOLE" -cp "$zkbindir/build/classes:$zkbindir/build/lib/*.jar:$zkbindir/lib/slf4j-log4j12-1.6.1.jar:$zkbindir/lib/slf4j-api-1.6.1.jar:$zkbindir/lib/netty-3.10.5.Final.jar:$zkbindir/lib/log4j-1.2.16.jar:$zkbindir/lib/jline-0.9.94.jar:$zkbindir/zookeeper-3.4.9.jar:$zkbindir/src/java/lib/*.jar:$zkbindir/conf:" -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=false org.apache.zookeeper.server.quorum.QuorumPeerMain "$zkbindir/conf/zoo.cfg" 97 | 98 | cat > /etc/supervisor/conf.d/zookeeper.conf <<-EOF 99 | [program:zookeeper] 100 | command=$zkbindir/bin/zkServer.sh start-foreground 101 | autostart=true 102 | autorestart=true 103 | stopsignal=KILL 104 | startsecs=10 105 | startretries=999 106 | stderr_logfile=/var/log/zookeeper.err.log 107 | stdout_logfile=/var/log/zookeeper.out.log 108 | logfile_maxbytes=20MB 109 | logfile_backups=10d; 110 | EOF 111 | 112 | service supervisor restart 113 | supervisorctl reread 114 | supervisorctl update 115 | 116 | exit 0 117 | --------------------------------------------------------------------------------