יום רביעי, 19 בדצמבר 2012

MyCloud Guide




כולם היום מדברים על עננים אבל לא על אלה שבשמיים, כל חברה בתחום המחשבים וההייטק צורכת או מייצרת שירותים שנגמרים במילה ענן שנהפכה בשנים האחרונות לפופולרית מאוד, בזכות העננים חברות לא צריכות להחזיק שרתים פיזיים והם יכולים לרכוש מחשבים וירטואלים על בסיס צורך ולפי עלות חודשית.

נושא העננים זה לא משהו חדש , חשבו עליו בשנת 1967, מהנדס מ IBM בשם  Gene Amdahl שפיתח את ארכיטקטורת ה Clustering , אבל מימוש Cluster באותה תקופה לא היה אפשרי לכל אחד, נדרשו משאבים רבים ועלויות כבדות, עכשיו אחרי 45 שנה אוטוטו 46 העולם נראה קצת אחרת הטכנולוגיה התקדמה מאוד והדולר נחלש מה שמאפשר לקנות חומרה טובה במחיר טוב ולנצל אותה במלואה בעזרת תוכנות מתוחכמות ומערכות Storage מתקדמות, בעקבות מהפכת הוירטואליזציה העסק הופך לזול וזמין.

Cluster

פעם כשדיברו על Cluster דיברו על סוג של מחשב על , מחשב שמחוברים אליו אלפי מחשבים שתופסים חדר ענק, אם תרשמו ב Google את המילה Cluster תבינו על מה אני מדבר, זה היה עסק מסורבל ויקר שמעטים מבינים בו אבל לאחר ש Linux נכנסה לשוק רשמית בשנת 1991,העסק התחיל לקבל תפנית והיום יש המון פתרונות מבוססי Linux ובחינם.

אז מה זה Cluster?

Cluster (אשכול) זה מספר מחשבים שנותנים שירותים ומתנהגים לפעמים כמחשב אחד,  החיבור בניהם מתבצע בעזרת רשת פנימית והרעיון שיש מחשב שמוגדר כ Master ושאר המחשבים מחוברים אליו כ Slaves , בעזרת שכבה וירטואלית שעוטפת את המחשבים ניתן לייצר כמות אינסופית של תחנות וירטואליות, כאשר רואים שאחד העבדים מתעייף מעבירים את התחנות הוירטואליות לעבד אחר או שמוסיפים עבד חדש.




הפתרונות בשוק מרובות, החברה הפופלרית ביותר בתעשייה היא VmWare שבהחלט פיתחו מערכת יפה, ניתן להוריד גרסה חינמית שאפשר להפעיל על שרת פיזי בודד, אם רוצים יותר ממחשב בודד צריכים לקנות רישיון ופה העסק קצת יקר, קיים פתרון נוסף שנקרא Proxmox שהרישיון שלו יותר גמיש ומאפשר לנו לחבר כמה עבדים שרוצים , התשלום עבור התמיכה בלבד.

נראה לי שחפרתי מספיק, קדימה לעבודה, דבר ראשון יש להיכנס לאתר של Proxmox ולהוריד את הגרסה האחרונה של המוצר Proxmox Ve, לצרוב על דיסק ולהכניס למחשב שאותו אנחנו רוצים להפוך ל Master של ה Cluster שלנו.


ההתקנה מאוד פשוטה, בסיום ההתקנה צריכים להגדיר את הרשת שלנו וזהו, עכשיו נתחיל להגדיר את ה Cluster, ניתן לנהל את ה Cluster דרך אתר מקומי או בעזרת SSH, יש פונקציות מסויימות שניתן לבצע רק דרך ה Console  כמו יצירת Cluster ,הוספת עבדים או מחיקת תחנות וירטואליות.

נכון שמדובר ב Master אבל הוא יכול להתנהג כעבד כמו שאר העבדים וניתן להתקין עליו תחנות וירטואליות למרות שזה לא כל כך מומלץ אחרי הכל הוא ה Master והוא המחשב שמתפעל לנו את כל ה Cluster לכן חשוב לשחרר ממנו משימות מיותרות אז כמו שאמרתי הדרך היחידה לעשות זאת היא דרך SSH.

#:pvecm create <cluster name>
מייצר Cluster על המחשב הנוכחי, מאותו רגע המחשב נהפך ל Master.

#:pvecm add <master address>
לאחר שהגדרנו את ה Master נבנה את העבדים, עושים את אותו תהליך ההתקנה כמו שעשינו עם ה Master רק שכאן אנחנו מצרפים את העבד ל Master.


אחרי שהגדרנו את המחשבים הפיזיים בעזרת ה SSH, מרבית העבודה שלנו תתמקד בממשק ה Web הנחמד שעשו לנו, שם נוכל לעקוב אחרי ה Cluster, לייצר מכונות וירטואליות חדשות , ליזום גיבויים, להעביר תחנות מעבד לעבד ע"פ מצב ה Cluster.


לפני שנעבור לשלב ההתקנות של המכונות הוירטואלית נושא אחסון המידע הוא קריטי וחיוני מאוד ל Cluster לכן צריך להתכונן בהתאם, לצורך הדוגמה יש ברשותי HD חיצוני שמכיל בתוכו כרטיס רשת של חברת WD משהו מאוד פשוט אבל מספיק טוב לדוגמה, לא חובה להשתמש ב Storage חיצוני ניתן להשתמש גם בכוננים שבמכונות הפיזיות.




ב Proxmox קיימות המון דרכים לחבר מערכות אחסון ל Cluster , ניתן להשתמש במערכות NFS, או ISCSI , אני אתמקד בהוספת Directory כ Storage בעזרת פרוטוקול CIFS, גם פה הדרך היחידה לבצע זאת היא דרך ה SSH, ניצור תיקייה חדשה בתיקיית ה Mnt ב Master ונעשה Mount.

#:mkdir /mnt/wdmybook
#:/mount //192.168.1.2/share -t cifs /mnt/wdmybook


בממשק ה Web יש ללכת לתגית Storage וללחוץ על Add -> Directory.



נכניס את הנתיב לתיקייה שעשינו Mount ונבחר איזה סוג של Content ה Storage מאחסן (דיסקים וירטואלים, ISO, גיבויים), אפשר לשלב כמה סוגים של Contents.


על מנת לבנות תחנות וירטואליות צריך קבצי ISO של מערכות ההפעלה, יש ללחוץ על המכונה הרצויה וללחוץ על אחד הכוננים, בתגית Content יש אפשרות להעלות את קבצי ISO לשטח האחסון.

יש 2 סוגי חשבונות במערכת, חשבון ה Root מבוסס על חשבון מסוג Linux PAM standard authentication שמנהל את ה Cluster , מוסיף משתמשים ועבדים למערכת, אבל הוא אינו יכול לגשת למכונות הוירטואליות יש לייצר משתמש חדש עם חשבון מסוג Proxmox VE authentication server ולהכניס לו שם משתמש וסיסמא, אפשר להגדיר גם את ההרשאות של המשתמש.

שלב התקנת המכונות הוירטואליות מאוד דומה לשאר המערכות שקיימות בשוק, בעצם מתחיל Wizard שבו מאפיינים את המכונה, מעבדים,כמות זכרון, כונן קשיח ורשת.

בחירת מערכת ההפעלה.



בחירת ה ISO של ההתקנה שמאוחסן בכונן החיצוני


פה הסיפור קצת מעניין לצורך הדוגמה השתמשתי ב Distribution נחמד של Linux בשם SliTaz, בעצם מדובר על מערכת Linux לכל דבר בגודל 35 Mb בלבד וממשק KDE.

נפח כונן הקשיח.




הגדרת זיכרון.



הגדרות רשת.



זהו, מה שנשאר זה להריץ את המכונה ויש לכם Cluster.




סיכום:

עננים משנים את התפיסה שהתרגלנו להכיר בעשור האחרון, זה תחום חדש שיביא לעולם הרבה חברות חדשות אבל יהרוג חברות ותיקות, כנראה שהעתיד שלנו יהיה בענן ולא יותר אצלנו בבית, בעזרת קווי אינטרנט מהירים והטכנולוגיה עולם המחשבים מתקרב לפריצת דרך רצינית ולמרות שכבר היום ניתן למצוא המון שירותים מבוססי ענן הפוטנציאל הוא אדיר ואנחנו רק בתחילתו של התהליך.

יש עננים?

יום שבת, 1 בדצמבר 2012

Process Network Analyzer .Net


דמיינו מצב שאתם מנהלים שרת אינטרנט וההייתם רוצים לדעת כמה משתמשים יש על השרת באותו רגע, וכמובן באיזה שירותים הם משתמשים, אחת הדרכים הנפוצות לעשות זו היא בעזרת שימוש בכלים קיימים ב Windows כמו ה Task Manager ו NetStat אבל זה תהליך מסורבל ומבלבל (נעבור עליו מיד) , חשבתי לעצמי איך ניתן לכתוב כלי כזה בעצמי ממקום להתחיל להתערבב עם הכלים ולזכור כל מיני מספרים...

השיטה הקלאסית:

נפעיל את חברנו הטוב - Task Manager.


חשוב לזכור שבהגדרות ה Default של טבלת ה Task Manager לא מופיעה העמודה של ה PID - Process Identifier שהיא חשובה לנו בהמשך, על מנת להפעיל את העמודה יש ללחוץ על View -> Select Columns ולסמן את ה CheckBox.


לאחר מכן נפתח Cmd ונרשום netstat -n -a -o ונקבל את רשימת החיבורים הפעילים שבמחשב ובנוסף ה PID של ה Process שאליו קשור החיבור.


עכשיו נקשר בין ה PID שמופיע ב NetStat לבין ה PID שמופיע ב Task Manager  ונדע כמה חיבורים יש לכל Process וכבר אפשר לראות שזה ממש לא נוח וצריכים כלי שיעשה זאת בשבילנו.

Process Analyzer

בסה"כ מדובר על כלי מאוד פשוט, קיימות מחלקות מוכנות ב .Net לניהול Processes שבעזרתם נקבל את רשימת ה Processes שרצים על המערכת, החלק היותר בעייתי איך מוצאים את כל החיבורים עבור כל Process, בעזרת Invoke (קריאה) ל פונקציות ומבנים ב Api נמצא את מה שאנחנו צריכים, אבל זה קצת טריקי, קיימת בעייה לגשת מתוכנית .Net שמוגדרת Managed Code לפונקציות API של Windows שמוגדרים כ UnManaged Code, אתר Pinvoke מכיל חתימות ידועות של פונקציות ב API של Windows שמאפשר לקרוא לפונקציה ישירות מ Net.

יש 2 פונקציות מרכזיות שאותן צריך להפעיל ב API של Windows הראשונה GetExtendedTcpTable והשנייה GetExtendedUdpTable, כל אחת מהפונקציות עובדת עם מבנים קבועים לצורך הדוגמה הפונקציה GetExtendedTcpTable צריכה לקבל מבנים מסוג MIB  שמיוחסים לפרוטוקול TCP כמו MIB_TCPROW_OWNER_PID MIB_TCPROW, MIB_TCPTABLE_OWNER_PID וכו',  בסיום הפונקציה מתמלא Buffer של טבלת החיבורים הפעילים וחוזר Pointer לאותו Buffer, מסביב כל פונקציה יש מעטפת שמכילה בתוכה את כל המבנים הדרושים.

tcpTavble.cs - מבוסס על הפונקציה שרשומה ב Pinvoke.

 public class tcpTable
    {
        //sort values by declaration order in memory
        [StructLayout(LayoutKind.Sequential)]
        public struct MIB_TCPROW_OWNER_PID
        {
            public uint state;
            public uint localAddr;
            public byte localPort1;
            public byte localPort2;
            public byte localPort3;
            public byte localPort4;
            public uint remoteAddr;
            public byte remotePort1;
            public byte remotePort2;
            public byte remotePort3;
            public byte remotePort4;
            public int owningPid;
        }

        //sort values by declaration order in memory
        [StructLayout(LayoutKind.Sequential)]
        public struct MIB_TCPROW
        {
            public uint dwState;
            public uint dwLocalAddr;
            public uint dwLocalPort;
            public uint dwRemoteAddr;
            public uint dwRemotePort;

        }

        //sort values by declartion order in memory
        [StructLayout(LayoutKind.Sequential)]
        public struct MIB_TCPTABLE_OWNER_PID
        {
            public uint dwNumEntries;
            MIB_TCPROW_OWNER_PID table;
        }

        //sort values by declaration order in memory
        //enum of type for tcptable
        enum TCP_TABLE_CLASS
        {
            TCP_TABLE_BASIC_LISTENER,
            TCP_TABLE_BASIC_CONNECTIONS,
            TCP_TABLE_BASIC_ALL,
            TCP_TABLE_OWNER_PID_LISTENER,
            TCP_TABLE_OWNER_PID_CONNECTIONS,
            TCP_TABLE_OWNER_PID_ALL,
            TCP_TABLE_OWNER_MODULE_LISTENER,
            TCP_TABLE_OWNER_MODULE_CONNECTIONS,
            TCP_TABLE_OWNER_MODULE_ALL
        }

        //enum of the state of single row in the table
        public enum TCP_ROW_STATE
        {
            CLOSED,
            LISTEN,
            SENT,
            RCVD,
            ESTAB,
            FIN_WAIT1,
            FIN_WAIT2,
            CLOSE_WAIT,
            CLOSING,
            LAST_ACK,
            TIME_WAIT,
            DELETE_TCB
        }


        //expose signature, importing ip helper api
        [DllImport("iphlpapi.dll", SetLastError = true)]
        static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref int dwOutBufLen, bool sort, int ipVersion, TCP_TABLE_CLASS tblClass, int reserved);

        //return the MIB_TCPROW_OWNER_PID array as connection 
        public MIB_TCPROW_OWNER_PID[] GetAllTcpConnections()
        {
            MIB_TCPROW_OWNER_PID[] tTable;
            int AF_INET = 2;    // IP_v4
            int buffSize = 0;

            // what the size of the memory we need to allocate for the table
            uint ret = GetExtendedTcpTable(IntPtr.Zero, ref buffSize, true, AF_INET, TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL, 0);
            //set pointer to buffer
            IntPtr buffTable = Marshal.AllocHGlobal(buffSize);

            try
            {
                //getting the buffer
                ret = GetExtendedTcpTable(buffTable, ref buffSize, true, AF_INET, TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL, 0);
                if (ret != 0)
                {
                    return null;
                }

                //convert pointer to MIB_TCPTABLE_OWNER_PID pointer
                MIB_TCPTABLE_OWNER_PID tab = (MIB_TCPTABLE_OWNER_PID)Marshal.PtrToStructure(buffTable, typeof(MIB_TCPTABLE_OWNER_PID));
                IntPtr rowPtr = (IntPtr)((long)buffTable + Marshal.SizeOf(tab.dwNumEntries));

                tTable = new MIB_TCPROW_OWNER_PID[tab.dwNumEntries];
 
                //reading the row from buffer using next position pointer
                //size of MIB_TCPROW_OWNER_PID
                for (int i = 0; i < tab.dwNumEntries; i++)
                {
                    //convert pointer to MIB_TCPROW_OWNER_PID pointer
                    MIB_TCPROW_OWNER_PID tcpRow = (MIB_TCPROW_OWNER_PID)Marshal.PtrToStructure(rowPtr, typeof(MIB_TCPROW_OWNER_PID));
                    //save row in table
                    tTable[i] = tcpRow;
                    //go to the next entry.
                    rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(tcpRow));
                }

            }
            finally
            {
                // clear buffer
                Marshal.FreeHGlobal(buffTable);
            }

            return tTable;
        }
    }





הפונקציה GetExtendedUdpTable אומנם לא חשופה ב Pinvoke אבל היא מאוד דומה ל GetExtendedTcpTable.

udpTable.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace processNetwork
{
    public class udpTable
    {
        //sort values by declartion order in memory
        [StructLayout(LayoutKind.Sequential)]
        public struct MIB_UDPROW_OWNER_PID
        {
            public uint LocalAddr;
            public byte localPort1;
            public byte localPort2;
            public byte localPort3;
            public byte localPort4;
            public int owningPid;
        }


        //sort values by declartion order in memory
        [StructLayout(LayoutKind.Sequential)]
        public struct MIB_UDPTABLE_OWNER_PID
        {
            public uint dwNumEntries;
            MIB_UDPROW_OWNER_PID table;
        }



        enum UDP_TABLE_CLASS
        {
            UDP_TABLE_BASIC,
            UDP_TABLE_OWNER_PID,
            UDP_TABLE_OWNER_MODULE
        }

        //expose signature, importing ip helper api
        [DllImport("iphlpapi.dll", SetLastError = true)]
        static extern uint GetExtendedUdpTable(IntPtr pUddpTable, ref int dwOutBufLen, bool sort, int ipVersion, UDP_TABLE_CLASS tblClass, int reserved);

        //return the MIB_UDPROW_OWNER_PID array
        public MIB_UDPROW_OWNER_PID[] GetAllUdpConnections()
        {


            MIB_UDPROW_OWNER_PID[] tTable;
         
            int AF_INET = 2;    // IP_v4
            int buffSize = 0;


            // what the size of the memory we need to allocate for the table?
            uint ret = GetExtendedUdpTable(IntPtr.Zero, ref buffSize, true, AF_INET, UDP_TABLE_CLASS.UDP_TABLE_OWNER_PID, 0);
            //set pointer to buffer
            IntPtr buffTable = Marshal.AllocHGlobal(buffSize);

            try
            {
                //getting the buffer
                ret = GetExtendedUdpTable(buffTable, ref buffSize, true, AF_INET, UDP_TABLE_CLASS.UDP_TABLE_OWNER_PID, 0);
                if (ret != 0)
                {
                    return null;
                }


                //convert pointer to MIB_UDPTABLE_OWNER_PID pointer
                MIB_UDPTABLE_OWNER_PID tab = (MIB_UDPTABLE_OWNER_PID)Marshal.PtrToStructure(buffTable, typeof(MIB_UDPTABLE_OWNER_PID));
             
                IntPtr rowPtr = (IntPtr)((long)buffTable + Marshal.SizeOf(tab.dwNumEntries));
         
                tTable = new MIB_UDPROW_OWNER_PID[tab.dwNumEntries];

                //reading the row from buffer using next position pointer
                //size of MIB_UDPROW_OWNER_PID     
                for (int i = 0; i < tab.dwNumEntries; i++)
                {
                    //convert pointer to MIB_UDPROW_OWNER_PID pointer
                    MIB_UDPROW_OWNER_PID udpRow = (MIB_UDPROW_OWNER_PID)Marshal.PtrToStructure(rowPtr, typeof(MIB_UDPROW_OWNER_PID));
                    //save row in table
                    tTable[i] = udpRow;
                    //go to the next entry.
                    rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(udpRow));
                }

            }
            finally
            {
                // clear buffer
                Marshal.FreeHGlobal(buffTable);
            }

            return tTable;
        }
    }

}


הטבלה שחוזרת לנו מהפונקציה צריכה לעבור מספר המרות, יש להמיר Ip Number ל Ip Address, ולהוציא את ה Port מהאובייקט.

Utils.cs

 public static class utils
 {
        //convert uint to ip
        public static string convert_uint_to_ip(uint ipnum)
        {
            string ipAddress = new IPAddress(BitConverter.GetBytes(ipnum)).ToString();
            return ipAddress;
        }

        //convert bytes to ushort -> port
        public static ushort convert_bytes_to_port(byte p0, byte p1, byte p2, byte p3)
        {
            ushort port;
            byte[] arr = new byte[4];

            //check if the system is litte endian, or big endian and sort the array
            //reading the first 2 bytes
            if (BitConverter.IsLittleEndian)
            {
                arr[0] = p3;
                arr[1] = p2;
                arr[2] = p1;
                arr[3] = p0;
                port = BitConverter.ToUInt16(arr, 2);
            }
            else
            {
                arr[0] = p0;
                arr[1] = p1;
                arr[2] = p2;
                arr[3] = p3;
                port = BitConverter.ToUInt16(arr,0);
            }


            return port;


        }
    }



GUI




הפונקציה שהכי חשובה ב Gui היא init_processStack שאוספת את המידע ממחלקות ה Api שהצגתי וממחלקת ה Process הפנימית של .Net ומחברת בניהם על פי ה Process id ,שאר הפונקציות משמשות לעבודה מול הפקדים ב Gui לכן לא ארחיב עליהם. (פירוט מלא בקוד).

        /// collecting all the processes and the active connections
        private void init_processStack()
        {
            //create table of all the tcp connections
            tcpTable tcptbl = new tcpTable();
            tcpTable.MIB_TCPROW_OWNER_PID[] tcpRow = tcptbl.GetAllTcpConnections();

            //create table of all the udp connections
            udpTable udptbl = new udpTable();
            udpTable.MIB_UDPROW_OWNER_PID[] udpRow = udptbl.GetAllUdpConnections();

            //getting the processes from the machine
            Process[] pro = Process.GetProcesses();

            //add rows from tcp and udp tables inside sysProcess array
            //compate the process id inside the process array and the process id
            //inside the connections tables
            if (pro != null)
            {

                    prcs = new SysProcess[pro.Length];
          


                for (int i = 0; i < pro.Length; i++)
                {
                    prcs[i] = new SysProcess(pro[i]);

                    if (tcpRow != null)
                    {
                        for (int z = 0; z < tcpRow.Length; z++)
                        {
                            if (prcs[i].prc.Id == tcpRow[z].owningPid)
                                prcs[i].tcprows.Add(tcpRow[z]);
                        }
                    }

                    if (udpRow != null)
                    {
                        for (int z = 0; z < udpRow.Length; z++)
                        {
                            if (prcs[i].prc.Id == udpRow[z].owningPid)
                                prcs[i].udpRows.Add(udpRow[z]);
                        }
                    }

                }
            }

        }

סרט דוגמה:





קוד:
https://sourceforge.net/projects/processnetwork/

סיכום:

למדנו להכיר טוב יותר את התהליכים שרצים לנו על המחשב וכיצד ניתן לעקוב אחריהם, קיימים המון כלים בתחום , Mark Russinovich היה בין הראשונים שלקח את ניתוח המערכות של מיקרוספט לקצה וניתן למצוא המון ספרים שלו בתחום, בנוסף אני חייב להזכיר את הפרויקט Process Hacker שבקוד פתוח ב .Net שניתן ללמוד ממנו המון.

תתחילו לעקוב...