#greyscale");filter:url("data:image/svg+xml;utf8, #greyscale");filter:grey;-webkit-filter:greyscale(100%);-webkit-backface-visibility:hidden;}.shadow-light{-moz-box-shadow:0 10px 20px 0 rgba(152,162,179,0.1),0 2px 6px 0 rgba(152,162,179,0.25) !important;-webkit-box-shadow:0 10px 20px 0 rgba(152,162,179,0.1),0 2px 6px 0 rgba(152,162,179,0.25) !important;-o-box-shadow:0 10px 20px 0 rgba(152,162,179,0.1),0 2px 6px 0 rgba(152,162,179,0.25) !important;-ms-box-shadow:0 10px 20px 0 rgba(152,162,179,0.1),0 2px 6px 0 rgba(152,162,179,0.25) !important;box-shadow:0 10px 20px 0 rgba(152,162,179,0.1),0 2px 6px 0 rgba(152,162,179,0.25) !important;}.shadow-dark{-moz-box-shadow:0 10px 20px 0 rgba(83,89,102,0.1),0 2px 6px 0 rgba(83,89,102,0.25) !important;-webkit-box-shadow:0 10px 20px 0 rgba(83,89,102,0.1),0 2px 6px 0 rgba(83,89,102,0.25) !important;-o-box-shadow:0 10px 20px 0 rgba(83,89,102,0.1),0 2px 6px 0 rgba(83,89,102,0.25) !important;-ms-box-shadow:0 10px 20px 0 rgba(83,89,102,0.1),0 2px 6px 0 rgba(83,89,102,0.25) !important;box-shadow:0 10px 20px 0 rgba(83,89,102,0.1),0 2px 6px 0 rgba(83,89,102,0.25) !important;}figure{margin:0;text-align:center;}figure i,.border-circle{background-position:center center;background-repeat:no-repeat;background-size:120% auto;display:block;height:170px;margin:0 auto;overflow:hidden;width:170px;border-top:6px solid #ccc;border-right:6px solid #ccc;border-bottom:6px solid #ccc;border-left:6px solid #ccc;-webkit-border-radius:340px;-moz-border-radius:340px;-ms-border-radius:340px;border-radius:340px;}figcaption{text-align:center;font-size:14px;line-height:24px;font-style:italic;}.circle-bg{background:#fff;-webkit-border-radius:104px;-moz-border-radius:104px;-ms-border-radius:104px;border-radius:104px;-moz-box-shadow:0px 2px 5px rgba(0,0,0,0.2);-webkit-box-shadow:0px 2px 5px rgba(0,0,0,0.2);-o-box-shadow:0px 2px 5px rgba(0,0,0,0.2);-ms-box-shadow:0px 2px 5px rgba(0,0,0,0.2);box-shadow:0px 2px 5px rgba(0,0,0,0.2);width:104px;height:104px;position:relative;display:inline-block;z-index:1;}.circle-bg img{bottom:0;height:70%;left:0;margin:auto;position:absolute;right:0;top:0;width:70%;}.circle-bg:before{content:" ";vertical-align:middle;height:100%;}.circle-bg .graph-img-center{left:6px;}.image-32-icon{height:32px !important;margin:0 0 16px !important;width:32px !important;}.image-64-icon{height:64px !important;margin:0 auto 16px !important;width:64px !important;}.onlyFadeIn{-webkit-animation:onlyFadeIn 1s;animation:onlyFadeIn 1s;}@-webkit-keyframes onlyFadeIn{0%{opacity:0;}100%{opacity:1;}}@keyframes onlyFadeIn{0%{opacity:0;}100%{opacity:1;}}.img-overflow{max-width:100%;height:auto;}@media (min-width:992px){.img-overflow{max-width:unset;max-height:500px;}}.embed-container iframe,.video iframe{border:0;height:287px;width:100%;}.play-icon{bottom:0;display:block;height:50px;left:0;margin:auto;position:absolute;right:0;top:0;z-index:2;}.video-thumb{position:relative;display:inline-block;max-width:100%;border-radius:10px;overflow:hidden;}.video-thumb .play-btn{bottom:0;height:64px;left:0;margin:auto;position:absolute;right:0;top:0;width:64px;}@media screen and (max-width:600px){.video-thumb{max-width:295px;}}.video-content-wrapper h3{color:var(--color-dark-blue);}.video-content-wrapper h3 a{color:var(--color-dark-blue);}.right-arrow{background-image:url(/static-res/images/right-blue-arrow.png);background-repeat:no-repeat;background-position:right 4px;padding-right:25px !important;color:#00a9e5;font-size:16px;display:inline-block;}.right-arrow:hover{color:#00a9e5;}.right-arrow .upgrade-icon{background-image:url(/static-res/images/refresh-icon.svg);background-repeat:no-repeat;background-position:0px 0px;display:inline-block;height:17px;padding-left:28px;vertical-align:middle;}.right-arrow:before{content:" ";vertical-align:middle;height:100%;}.right-arrow .graph-img-center{left:6px;}.checkmark{height:63px;width:63px;}.position-relative{position:relative;}.position-absolute{position:absolute;}@media screen and (min-width:768px) and (max-width:991px){.container{max-width:100% !important;}}@media screen and (max-width:767px){.container{width:100%;max-width:unset;}.common-container{width:100%;}.no-gutters{margin-right:0;margin-left:0;}.no-gutters>[class*=col-]{padding-right:0;padding-left:0;}}@media screen and (max-width:480px){.table-responsive table,.table-responsive table td{border:1px solid #ccc;}.table-responsive table th,.table-responsive table td th{text-align:center;font-weight:600;font-size:13px;}.table-responsive table td,.table-responsive table td td{padding:5px;font-size:13px;line-height:18px;}.table-responsive table td p a{font-size:13px;line-height:18px;}}a{color:var(--link-color);-webkit-transition:all 300ms ease-in-out;transition:all 300ms ease-in-out;text-decoration:underline;font-weight:600;}a:hover,a:focus{color:var(--link-hover-color);text-decoration:underline;box-shadow:none !important;}a.whiteurl{color:var(--color-white);}a.stretched-link:before{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background:transparent !important;background-color:rgba(0,0,0,0);}.btn-tertiary,.cta-link,a.btn-tertiary:not([href]):not([tabindex]){cursor:pointer;display:inline-block;text-decoration:none;color:var(--button-tertiary-color);font-weight:600;}.btn-tertiary svg,.cta-link svg,a.btn-tertiary:not([href]):not([tabindex]) svg{-webkit-transition:all 100ms ease-in;transition:all 100ms ease-in;left:8px;position:relative;height:inherit;}.btn-tertiary svg path,.cta-link svg path,a.btn-tertiary:not([href]):not([tabindex]) svg path{stroke:var(--button-tertiary-color);}.btn-tertiary:hover,.cta-link:hover,a.btn-tertiary:not([href]):not([tabindex]):hover{text-decoration:none;color:var(--button-tertiary-hover-color);}.btn-tertiary:hover svg,.cta-link:hover svg,a.btn-tertiary:not([href]):not([tabindex]):hover svg{left:14px;}.btn-tertiary:hover svg path,.cta-link:hover svg path,a.btn-tertiary:not([href]):not([tabindex]):hover svg path{stroke:var(--button-tertiary-hover-color);}.btn-tertiary:focus-visible,.cta-link:focus-visible,a.btn-tertiary:not([href]):not([tabindex]):focus-visible{text-decoration:none;color:var(--button-tertiary-hover-color);outline:2px solid var(--button-tertiary-active-offset-color);outline-offset:4px;border-radius:4px;padding-right:12px;}.btn-tertiary:focus-visible svg,.cta-link:focus-visible svg,a.btn-tertiary:not([href]):not([tabindex]):focus-visible svg{stroke:var(--button-tertiary-hover-color);}.btn-tertiary:focus-visible svg path,.cta-link:focus-visible svg path,a.btn-tertiary:not([href]):not([tabindex]):focus-visible svg path{stroke:var(--button-tertiary-hover-color);}.gdpr-text{margin-top:10px;}.gdpr-text p{font-size:12px;line-height:18px;opacity:1;}#marketo-fe-form{position:relative;}#marketo-fe-form .mktoForm{position:relative;width:100% !important;}#marketo-fe-form .mktoForm .mktoOffset{display:none;}#marketo-fe-form .mktoForm .mktoLabel,#marketo-fe-form .mktoForm legend{font-size:14px;font-weight:600;line-height:24px !important;text-align:left;width:auto !important;padding-top:0;margin-left:0;float:none;display:block;margin-bottom:4px;}#marketo-fe-form .mktoForm .mktoLabel[for=tempCheckBoxforForm]{margin-left:23px;width:calc(100% - 23px) !important;font-weight:normal;}#marketo-fe-form .mktoForm p,#marketo-fe-form .mktoForm #gdpr{opacity:1;margin-bottom:8px;}#marketo-fe-form .mktoForm .mktoFormCol{width:100%;min-height:unset;margin-bottom:0 !important;}#marketo-fe-form .mktoForm .mktoFieldWrap{margin-right:0px !important;width:100%;}#marketo-fe-form .mktoForm .mktoFieldWrap input[type=hidden]{display:none;}#marketo-fe-form .mktoForm .mktoFieldWrap input[type=text],#marketo-fe-form .mktoForm .mktoFieldWrap input[type=email],#marketo-fe-form .mktoForm .mktoFieldWrap input[type=number],#marketo-fe-form .mktoForm .mktoFieldWrap input[type=tel],#marketo-fe-form .mktoForm .mktoFieldWrap input[type=url]{-webkit-appearance:none;}#marketo-fe-form .mktoForm .mktoFieldWrap input[type=text],#marketo-fe-form .mktoForm .mktoFieldWrap input[type=email],#marketo-fe-form .mktoForm .mktoFieldWrap input[type=number],#marketo-fe-form .mktoForm .mktoFieldWrap input[type=tel],#marketo-fe-form .mktoForm .mktoFieldWrap input[type=url],#marketo-fe-form .mktoForm .mktoFieldWrap select{width:100% !important;color:var(--color-ink);height:50px;line-height:40px !important;min-width:190px;padding:8px !important;font-size:14px;font-weight:400;border:1px solid var(--color-dark-gray);border-radius:0;position:relative;z-index:1;}#marketo-fe-form .mktoForm .mktoFieldWrap input[type=text]:focus,#marketo-fe-form .mktoForm .mktoFieldWrap input[type=email]:focus,#marketo-fe-form .mktoForm .mktoFieldWrap input[type=number]:focus,#marketo-fe-form .mktoForm .mktoFieldWrap input[type=tel]:focus,#marketo-fe-form .mktoForm .mktoFieldWrap input[type=url]:focus,#marketo-fe-form .mktoForm .mktoFieldWrap select:focus{border-bottom:2px solid var(--color-elastic-blue);outline:none !important;}#marketo-fe-form .mktoForm .mktoFieldWrap input[type=text].mktoInvalid,#marketo-fe-form .mktoForm .mktoFieldWrap input[type=email].mktoInvalid,#marketo-fe-form .mktoForm .mktoFieldWrap input[type=number].mktoInvalid,#marketo-fe-form .mktoForm .mktoFieldWrap input[type=tel].mktoInvalid,#marketo-fe-form .mktoForm .mktoFieldWrap input[type=url].mktoInvalid,#marketo-fe-form .mktoForm .mktoFieldWrap select.mktoInvalid{border-bottom:2px solid var(--color-dark-orange);}#marketo-fe-form .mktoForm .mktoFieldWrap input[type=text].mktoInvalid:focus,#marketo-fe-form .mktoForm .mktoFieldWrap input[type=email].mktoInvalid:focus,#marketo-fe-form .mktoForm .mktoFieldWrap input[type=number].mktoInvalid:focus,#marketo-fe-form .mktoForm .mktoFieldWrap input[type=tel].mktoInvalid:focus,#marketo-fe-form .mktoForm .mktoFieldWrap input[type=url].mktoInvalid:focus,#marketo-fe-form .mktoForm .mktoFieldWrap select.mktoInvalid:focus{border-bottom:2px solid var(--color-elastic-blue);}#marketo-fe-form .mktoForm .mktoFieldWrap select{background-color:#fff;position:relative;line-height:normal !important;padding:8px 32px 8px 8px !important;appearance:none;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;background-image:url("/static-res/images/svg/icon-down-arrow-16-blue.svg");background-repeat:no-repeat;background-position:98% 50%;background-size:16px;}#marketo-fe-form .mktoForm .mktoFieldWrap textarea{border:1px solid var(--color-dark-gray);font-size:14px;height:6em;width:100% !important;padding:8px 16px;margin-bottom:32px;padding:8px 16px;position:relative;z-index:1;}#marketo-fe-form .mktoForm .mktoFieldWrap textarea:focus{outline:none;border-bottom:2px solid var(--color-elastic-blue);}#marketo-fe-form .mktoForm .mktoFieldWrap textarea.mktoInvalid{border-bottom:2px solid var(--color-dark-orange);}#marketo-fe-form .mktoForm .mktoFieldWrap textarea.mktoInvalid:focus{border-bottom:2px solid var(--color-elastic-blue);}#marketo-fe-form .mktoForm .mktoFieldWrap input[type=checkbox]{height:auto !important;width:16px !important;position:relative;z-index:1;}#marketo-fe-form .mktoForm .mktoFieldWrap input[type=checkbox]:after{background-color:var(--color-white);content:"";height:17px;left:-2px;position:absolute;top:-2px;width:17px;border-top:1px solid var(--color-dark-gray);border-right:1px solid var(--color-dark-gray);border-bottom:1px solid var(--color-dark-gray);border-left:1px solid var(--color-dark-gray);}#marketo-fe-form .mktoForm .mktoFieldWrap input[type=checkbox]:checked:after{background-color:var(--color-elastic-blue);border-color:var(--color-elastic-blue);}#marketo-fe-form .mktoForm .mktoFieldWrap input[type=checkbox]:checked:before{content:"";height:12px;left:3px;position:absolute;top:-2px;width:7px;z-index:1;border-right:2px solid rgb(255,255,255);border-bottom:2px solid rgb(255,255,255);-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);}#marketo-fe-form .mktoForm .mktoFieldWrap input[type=checkbox][disabled]:after{border-color:#f8f9fb;}#marketo-fe-form .mktoForm .mktoFieldWrap .mktoRadioList{position:relative;z-index:1;}#marketo-fe-form .mktoForm .mktoFieldWrap .mktoRadioList label{margin-left:28px;margin-bottom:16px;line-height:14px;}#marketo-fe-form .mktoForm .mktoFieldWrap::-webkit-input-placeholder{font-size:14px;line-height:24px;}#marketo-fe-form .mktoForm .mktoFieldWrap::-moz-placeholder{font-size:14px;line-height:24px;}#marketo-fe-form .mktoForm .mktoFieldWrap:-ms-input-placeholder{font-size:14px;line-height:24px;}#marketo-fe-form .mktoForm .mktoFieldWrap:-moz-placeholder{font-size:14px;line-height:24px;}#marketo-fe-form .mktoForm .mktoFieldWrap .mktoCheckboxList{margin:16px 0px 10px 0px;width:100% !important;z-index:1;}#marketo-fe-form .mktoForm .mktoFieldWrap .mktoCheckboxList>label{font-size:14px;margin-bottom:8px;margin-left:32px;min-height:25px;margin-top:-2px;}#marketo-fe-form .mktoForm .mktoFieldWrap .mktoHtmlText{width:100% !important;}#marketo-fe-form .mktoForm .mktoFieldWrap .mktoGutter.mktoHasWidth{display:none;}#marketo-fe-form .mktoForm .mktoFieldWrap .mktoError{left:0;right:unset !important;bottom:unset !important;position:relative !important;z-index:0;}#marketo-fe-form .mktoForm .mktoFieldWrap .mktoError .mktoErrorArrowWrap{display:none;}#marketo-fe-form .mktoForm .mktoFieldWrap .mktoError .mktoErrorMsg{background-image:none !important;background-color:transparent !important;border:none !important;max-width:unset !important;box-shadow:none !important;text-shadow:none !important;color:var(--color-dark-orange) !important;font-size:14px !important;line-height:24px !important;margin-top:4px !important;padding-left:0;clear:both;}#marketo-fe-form .mktoForm .mktoFieldWrap .mktoError .mktoErrorMsg .mktoErrorDetail{display:inline !important;}#marketo-fe-form .mktoForm .mktoOffset,#marketo-fe-form .mktoForm .mktoRequiredField .mktoAsterix{display:none;}#marketo-fe-form .mktoForm .mktoButtonRow{width:100%;}#marketo-fe-form .mktoForm .mktoButtonRow .mktoButtonWrap{margin-left:unset !important;}#marketo-fe-form .mktoForm .mktoButtonRow .mktoButtonWrap .mktoButton{background:var(--color-elastic-blue);color:var(--color-white);display:inline-block;font-size:16px;font-weight:600;font-family:"MierB","Inter",Arial,sans-serif;height:50px;line-height:30px;margin:0;min-width:150px;padding:0 16px !important;text-align:center;text-transform:none;width:100%;border-top:1px solid var(--color-elastic-blue);border-right:1px solid var(--color-elastic-blue);border-bottom:1px solid var(--color-elastic-blue);border-left:1px solid var(--color-elastic-blue);-webkit-border-radius:4px !important;-moz-border-radius:4px !important;-ms-border-radius:4px !important;border-radius:4px !important;-moz-transition:all 200ms ease-in;-webkit-transition:all 200ms ease-in;-o-transition:all 200ms ease-in;-ms-transition:all 200ms ease-in;transition:all 200ms ease-in;}#marketo-fe-form .mktoForm .mktoButtonRow .mktoButtonWrap .mktoButton:hover{background:var(--color-dark-blue);border-color:var(--color-dark-blue);outline:none !important;}#marketo-fe-form .mktoForm .mktoButtonRow .mktoButtonWrap .mktoButton:active,#marketo-fe-form .mktoForm .mktoButtonRow .mktoButtonWrap .mktoButton:focus,#marketo-fe-form .mktoForm .mktoButtonRow .mktoButtonWrap .mktoButton:active:focus{border-color:var(--color-light-blue);outline:none !important;}.error-message{color:var(--color-dark-orange);font-size:14px;}#fallback-form{position:relative;}#fallback-form .fallback-form-title{font-weight:600;padding-bottom:10px;}#fallback-form .debug{border:1px solid red;color:red;position:absolute;top:-20px;display:none;}#fallback-form .input-wrapper p{font-size:12px;line-height:18px;}#fallback-form form.fallback{position:relative;text-align:left;max-width:100%;}#fallback-form form.fallback .input-wrapper input{width:100%;outline:none;}#fallback-form form.fallback .input-wrapper input[type=text],#fallback-form form.fallback .input-wrapper input[type=email],#fallback-form form.fallback .input-wrapper input[type=number],#fallback-form form.fallback .input-wrapper input[type=tel],#fallback-form form.fallback .input-wrapper input[type=url],#fallback-form form.fallback .input-wrapper select{width:100% !important;color:var(--color-ink);height:50px;max-height:50px;line-height:40px !important;min-width:190px;padding-left:8px !important;padding-right:8px !important;font-size:14px;font-weight:400;border-radius:0;border:1px solid var(--color-dark-gray);}#fallback-form form.fallback .input-wrapper input[type=text]:focus,#fallback-form form.fallback .input-wrapper input[type=email]:focus,#fallback-form form.fallback .input-wrapper input[type=number]:focus,#fallback-form form.fallback .input-wrapper input[type=tel]:focus,#fallback-form form.fallback .input-wrapper input[type=url]:focus,#fallback-form form.fallback .input-wrapper select:focus{border-bottom:2px solid var(--color-elastic-blue);outline:none !important;}#fallback-form form.fallback .input-wrapper input[type=text].mktoInvalid,#fallback-form form.fallback .input-wrapper input[type=email].mktoInvalid,#fallback-form form.fallback .input-wrapper input[type=number].mktoInvalid,#fallback-form form.fallback .input-wrapper input[type=tel].mktoInvalid,#fallback-form form.fallback .input-wrapper input[type=url].mktoInvalid,#fallback-form form.fallback .input-wrapper select.mktoInvalid{border-bottom:2px solid var(--color-dark-orange);}#fallback-form form.fallback .input-wrapper input[type=text].mktoInvalid:focus,#fallback-form form.fallback .input-wrapper input[type=email].mktoInvalid:focus,#fallback-form form.fallback .input-wrapper input[type=number].mktoInvalid:focus,#fallback-form form.fallback .input-wrapper input[type=tel].mktoInvalid:focus,#fallback-form form.fallback .input-wrapper input[type=url].mktoInvalid:focus,#fallback-form form.fallback .input-wrapper select.mktoInvalid:focus{border-bottom:2px solid var(--color-elastic-blue);}#fallback-form form.fallback .input-wrapper textarea{border:1px solid var(--color-dark-gray);height:4.5em;width:100% !important;margin-bottom:32px;}#fallback-form form.fallback .input-wrapper textarea:focus{outline:none;border-bottom:2px solid var(--color-elastic-blue);}#fallback-form form.fallback .input-wrapper textarea.mktoInvalid{border-bottom:2px solid var(--color-dark-orange);}#fallback-form form.fallback .input-wrapper textarea.mktoInvalid:focus{border-bottom:2px solid var(--color-elastic-blue);}#fallback-form form.fallback .input-wrapper input[type=checkbox]{height:auto !important;position:relative;width:16px !important;}#fallback-form form.fallback .input-wrapper input[type=checkbox]:after{background-color:var(--color-white);content:"";height:16px;left:0;position:absolute;top:0;width:16px;border-top:1px solid var(--color-dark-gray);border-right:1px solid var(--color-dark-gray);border-bottom:1px solid var(--color-dark-gray);border-left:1px solid var(--color-dark-gray);}#fallback-form form.fallback .input-wrapper input[type=checkbox]:checked:after{background-color:var(--color-elastic-blue);border-color:var(--color-elastic-blue);}#fallback-form form.fallback .input-wrapper input[type=checkbox]:checked:before{content:"";height:12px;left:5px;position:absolute;top:0px;width:7px;z-index:1;border-right:2px solid rgb(255,255,255);border-bottom:2px solid rgb(255,255,255);-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);}#fallback-form form.fallback .input-wrapper::-webkit-input-placeholder{font-size:14px;line-height:24px;}#fallback-form form.fallback .input-wrapper::-moz-placeholder{font-size:14px;line-height:24px;}#fallback-form form.fallback .input-wrapper:-ms-input-placeholder{font-size:14px;line-height:24px;}#fallback-form form.fallback .input-wrapper:-moz-placeholder{font-size:14px;line-height:24px;}#fallback-form form.fallback label{font-size:14px;font-weight:600;line-height:24px !important;text-align:left;width:100% !important;margin-bottom:0;margin-top:24px;}#fallback-form form.fallback .asterix{font-weight:700;color:var(--color-dark-orange);}#fallback-form form.fallback .submit-form{cursor:pointer;min-width:140px;width:max-content;padding-right:16px;padding-left:16px;-webkit-transition:all 200ms ease-in;transition:all 200ms ease-in;background-color:var(--color-elastic-blue);color:var(--color-white);height:50px;font-weight:700;text-align:center;letter-spacing:0.025em;margin:0;border:1px solid var(--color-elastic-blue);border-radius:4px;cursor:pointer;}#fallback-form form.fallback .submit-form:hover{background:var(--color-dark-blue);border-color:var(--color-dark-blue);}#fallback-form form.fallback .submit-form:active{border-color:var(--color-light-blue);}#fallback-form form.fallback .submit-form:focus{box-shadow:none;}.mkto-form-wrapper.long-form #marketo-fe-form .mktoForm .mktoFieldWrap,.marketo-form.long-form #marketo-fe-form .mktoForm .mktoFieldWrap{margin-bottom:24px;}.mkto-form-wrapper.long-form #marketo-fe-form .mktoForm .mktoFieldWrap input[type=text],.mkto-form-wrapper.long-form #marketo-fe-form .mktoForm .mktoFieldWrap input[type=email],.mkto-form-wrapper.long-form #marketo-fe-form .mktoForm .mktoFieldWrap input[type=number],.mkto-form-wrapper.long-form #marketo-fe-form .mktoForm .mktoFieldWrap input[type=tel],.mkto-form-wrapper.long-form #marketo-fe-form .mktoForm .mktoFieldWrap input[type=url],.marketo-form.long-form #marketo-fe-form .mktoForm .mktoFieldWrap input[type=text],.marketo-form.long-form #marketo-fe-form .mktoForm .mktoFieldWrap input[type=email],.marketo-form.long-form #marketo-fe-form .mktoForm .mktoFieldWrap input[type=number],.marketo-form.long-form #marketo-fe-form .mktoForm .mktoFieldWrap input[type=tel],.marketo-form.long-form #marketo-fe-form .mktoForm .mktoFieldWrap input[type=url]{height:40px !important;}.mkto-form-wrapper.long-form #marketo-fe-form .mktoForm .mktoFieldWrap select,.marketo-form.long-form #marketo-fe-form .mktoForm .mktoFieldWrap select{height:auto !important;}.mkto-form-wrapper.inline #marketo-fe-form{position:relative;width:100%;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm{width:100% !important;padding:0px;position:relative;display:inline-flex;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoOffset,.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoAsterix{display:none;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoFormCol{margin-bottom:0 !important;float:none;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoFormCol .mktoFieldWrap{width:100%;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoFormCol .mktoFieldWrap .mktoHtmlText{display:none !important;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoFormCol .mktoFieldWrap .mktoHtmlText p{margin:0 !important;line-height:0 !important;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoFormCol .mktoFieldWrap input[type=email],.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoFormCol .mktoFieldWrap select{min-width:289px;width:100% !important;max-width:350px !important;margin-top:4px;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoFormCol .mktoFieldWrap .mktoError{position:absolute !important;padding-bottom:unset;bottom:-52px !important;width:max-content;z-index:99 !important;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoFormCol .mktoFieldWrap .mktoError .mktoErrorArrowWrap{display:block;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoFormCol .mktoFieldWrap .mktoError .mktoErrorArrowWrap .mktoErrorArrow{background:rgba(255,255,255,0.9);border:1px solid var(--color-dark-orange) !important;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoFormCol .mktoFieldWrap .mktoError .mktoErrorMsg{border:1px solid var(--color-dark-orange) !important;color:var(--color-dark-orange) !important;padding:8px !important;background:rgba(255,255,255,0.9) !important;border-radius:0 !important;width:auto !important;margin-top:7px !important;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoButtonRow{margin-top:4px;margin-left:16px;width:max-content !important;align-self:flex-end;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoButtonRow .mktoButtonWrap{margin-left:0px !important;}.mkto-form-wrapper.inline .success-message{max-width:455px;width:100%;margin:0 auto;}.mkto-form-wrapper.inline.fallback{width:100%;max-width:455px;}.mkto-form-wrapper.inline.fallback #fallback-form form.fallback{display:inline-flex;align-items:end;}.mkto-form-wrapper.inline.fallback #fallback-form form.fallback .input-wrapper input,.mkto-form-wrapper.inline.fallback #fallback-form form.fallback .input-wrapper select{min-width:307px;width:100% !important;max-width:350px !important;margin-top:4px;}.mkto-form-wrapper.inline.fallback #fallback-form form.fallback .button-wrapper{margin-top:4px;margin-left:16px;}.mkto-form-wrapper.inline.center #marketo-fe-form{margin-left:auto;margin-right:auto;}.mkto-form-wrapper.inline.center #marketo-fe-form .mktoForm{justify-content:center;}.mkto-form-wrapper.inline.center.fallback{max-width:455px !important;margin-left:auto;margin-right:auto;}.mkto-form-wrapper.inline.center.fallback #fallback-form{text-align:center;}.mkto-form-wrapper.inline.center .error-message{margin-top:16px;}@media screen and (max-width:1200px){.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoFormCol .mktoFieldWrap input[type=email],.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoFormCol .mktoFieldWrap select{min-width:270px;}.mkto-form-wrapper.inline.fallback #fallback-form form.fallback{width:100%;margin:0;}.mkto-form-wrapper.inline.fallback #fallback-form form.fallback .input-wrapper input,.mkto-form-wrapper.inline.fallback #fallback-form form.fallback .input-wrapper select{min-width:270px;}.mkto-form-wrapper.inline.fallback #fallback-form form.fallback .button-wrapper{width:100%;}}@media screen and (max-width:991px){.react-tabs .react-tabs__tab{font-size:14px;}.mkto-form-wrapper.inline{margin-left:auto;margin-right:auto;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm{position:relative;display:block;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoFormCol .mktoFieldWrap{width:100%;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoFormCol .mktoFieldWrap input[type=email],.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoFormCol .mktoFieldWrap select{width:100% !important;max-width:unset !important;min-width:unset;margin-top:0;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoFormCol .mktoFieldWrap .mktoError{width:auto;position:relative !important;top:8px;bottom:unset !important;padding-bottom:0;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoFormCol .mktoFieldWrap .mktoError .mktoErrorMsg{padding:0 !important;width:auto !important;border:none !important;background:transparent !important;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoFormCol .mktoFieldWrap .mktoError .mktoErrorArrowWrap{display:none !important;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoButtonRow{margin-top:16px;margin-left:0;width:100% !important;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoButtonRow .mktoButtonWrap{margin-left:0px !important;}.mkto-form-wrapper.inline #marketo-fe-form .mktoForm .mktoButtonRow .mktoButtonWrap .mktoButton{width:100% !important;}.mkto-form-wrapper.inline.fallback{max-width:455px;}.mkto-form-wrapper.inline.fallback #fallback-form form.fallback{display:block;}.mkto-form-wrapper.inline.fallback #fallback-form form.fallback .input-wrapper input,.mkto-form-wrapper.inline.fallback #fallback-form form.fallback .input-wrapper select{width:100% !important;max-width:unset !important;min-width:unset;margin-top:0;}.mkto-form-wrapper.inline.fallback #fallback-form form.fallback .button-wrapper{margin:16px 0 0 0;}.mkto-form-wrapper.inline.fallback #fallback-form form.fallback .button-wrapper .submit-form{width:100% !important;}}.success-message{background:var(--color-white);color:var(--color-dark-teal) !important;border:1px solid var(--color-dark-teal) !important;padding:8px;text-align:center;}.success-message a{color:var(--color-dark-teal) !important;}.filter-wrapper{padding:32px 0px;}.filter-wrapper .header{display:flex;justify-content:space-between;border-bottom:1px solid var(--color-dark-gray);}.filter-wrapper .filter-row{width:100%;display:grid;grid-column-gap:32px;grid-row-gap:32px;grid-template-columns:1fr 1fr 1fr 1fr;align-items:baseline;}.filter-wrapper .filter-row .filter-column{width:100%;}.filter-wrapper .filter-row .filter-label{font-size:14px;font-weight:600;margin-bottom:5px;}@media screen and (max-width:991px){.filter-wrapper .container{padding:0 15px;}}@media screen and (max-width:768px){.filter-wrapper .header{padding-bottom:8px;}.filter-wrapper .container{padding:0 15px;}.filter-wrapper .filter-row{grid-template-columns:1fr 1fr;grid-template-rows:auto auto;}}@media screen and (max-width:575px){.filter-wrapper .header{display:flex;flex-direction:column;}.filter-wrapper .filter-row{grid-template-columns:1fr;}.filter-wrapper .filter-row .filter-column{margin:0 auto;width:300px;}}.card-small-padding{padding:8px;}.card-medium-padding{padding:16px;}.card-large-padding{padding:32px;}.card-xlarge-padding{padding:40px;}.card-xxlarge-padding{padding:48px;}.card-grid{display:-ms-grid;display:grid;grid-auto-columns:1fr;grid-template-columns:repeat(2,1fr);grid-template-rows:auto auto;grid-row-gap:32px;grid-column-gap:32px;}.card-grid-1x1{margin-right:auto;margin-left:auto;gap:32px;}@media (min-width:992px){.card-grid-1x1{gap:64px;}}.card-grid-1x1,.card-grid-1x1-sidebar{display:grid;justify-content:stretch;grid-auto-columns:1fr;grid-template-areas:".";grid-template-columns:1fr;grid-template-rows:auto;}.card-grid-1x1.col-sm-8,.card-grid-1x1-sidebar.col-sm-8{padding:0px;}.card-grid-1x1 .card-footer div:first-child,.card-grid-1x1-sidebar .card-footer div:first-child{margin-right:24px;}.card-grid-2x1{display:grid;margin-right:auto;margin-left:auto;justify-content:stretch;grid-auto-columns:1fr;grid-template-areas:".";grid-template-columns:repeat(2,1fr);grid-template-rows:auto;}.card-grid-2x1 .card-footer.align-self-left div:first-child{margin-right:24px;}.card-grid-3x1{display:grid;margin-right:auto;margin-left:auto;padding-left:0px;justify-content:stretch;grid-template-areas:".";grid-template-columns:repeat(3,1fr);grid-auto-columns:1fr;grid-template-rows:auto;}.card-grid-3x1 .card-footer.align-self-left div:first-child{margin-right:24px;}.card-grid-4x1{display:grid;margin-right:auto;margin-left:auto;justify-content:stretch;grid-column-gap:32px;grid-row-gap:32px;grid-template-columns:repeat(4,1fr);grid-template-rows:auto;}@media screen and (max-width:991px){.card-grid-3x1{grid-auto-flow:row;grid-template-areas:".";grid-template-columns:1fr 1fr;grid-template-rows:repeat(2,auto);}.card-grid-4x1{grid-row-gap:32px;grid-template-columns:1fr 1fr;grid-template-rows:auto auto;}}@media screen and (max-width:768px){.card-grid-3x1,.card-grid-4x1{grid-template-columns:1fr 1fr;}}@media screen and (max-width:575px){.card-grid-2x1{grid-template-columns:1fr;grid-template-rows:repeat(2,auto);}.card-grid-3x1{grid-template-columns:1fr;grid-template-rows:repeat(3,auto);}.card-grid-4x1{grid-template-columns:1fr;grid-template-rows:repeat(4,auto);}}.react-tabs .react-tabs__tab{cursor:pointer;margin-bottom:-2px;padding:0px 16px;margin-left:0;margin-right:0;}.react-tabs .react-tabs__tab h2{color:var(--color-black);font-size:16px;line-height:24px;padding-bottom:16px;font-weight:400;}.react-tabs .react-tabs__tab:focus{outline:0px none;}.react-tabs .react-tabs__tab:hover{border-bottom:2px solid var(--color-ink);}.react-tabs .react-tabs__tab--selected{color:var(--color-elastic-blue);cursor:text;display:block;margin-bottom:-2px;border-bottom:4px solid var(--carousel-tab-hover-border-color);}.react-tabs .react-tabs__tab--selected h2{color:var(--color-elastic-blue);}.react-tabs .react-tabs__tab--selected:hover{border-color:var(--color-elastic-blue);}.react-tabs .react-tabs__tab-panel{display:none;}.react-tabs .react-tabs__tab-panel--selected{display:block;}.instruction-module .react-tabs ul{border-bottom:0;}.instruction-module .react-tabs .top-tabs .react-tabs__tab{color:rgb(52,55,65);font-weight:400;height:40px;line-height:40px;margin:0px;padding:0px;text-align:center;text-transform:unset;width:200px;border-width:1px;border-style:solid;border-color:rgb(0,120,160);display:flex;justify-content:center;align-items:center;}.instruction-module .react-tabs .top-tabs .react-tabs__tab h2{padding-bottom:2px;}.instruction-module .react-tabs .top-tabs .react-tabs__tab--selected{background-color:var(--color-elastic-blue);}.instruction-module .react-tabs .top-tabs .react-tabs__tab--selected h2{color:#FFFFFF !important;}.instruction-module .react-tabs .vertical-tabs .react-tabs__tab h2{font-size:18px;line-height:22px;font-family:Inter,arial,sans-serif;}@media screen and (min-width:768px){.react-tabs .vertical-tabs .react-tabs__tab-list{border-bottom:0;border-left:1px solid var(--color-dark-gray);}.react-tabs .vertical-tabs .react-tabs__tab{margin-bottom:0;border-left:2px solid transparent;padding:8px 16px;}.react-tabs .vertical-tabs .react-tabs__tab h2{padding:5px 0;}.react-tabs .vertical-tabs .react-tabs__tab:hover{border-bottom:0;border-left:2px solid var(--color-ink);}.react-tabs .vertical-tabs .react-tabs__tab--selected{border-bottom:0;border-left:2px solid var(--color-elastic-blue);}.react-tabs .vertical-tabs .react-tabs__tab--selected:hover{border-left:2px solid var(--color-elastic-blue);}}@media screen and (max-width:767px){.code-carousel .react-tabs .react-tabs__tab-list{display:flex;flex-wrap:wrap;flex-direction:unset;margin-bottom:10px;}.code-carousel .react-tabs .react-tabs__tab{border-bottom:2px solid transparent;}.code-carousel .react-tabs .react-tabs__tab:hover{border-bottom:2px solid var(--color-ink);}.code-carousel .react-tabs .react-tabs__tab--selected{border-bottom:2px solid var(--carousel-tab-border-color);}.code-carousel .react-tabs .react-tabs__tab--selected:hover{border-bottom:4px solid var(--carousel-tab-hover-border-color);}}.carousel .carousel-one-column .react-tabs__tab,.carousel .carousel-two-column__tab-labels-left .react-tabs__tab{color:var(--carousel-tab-color);cursor:pointer;margin-left:0;margin-right:0;position:relative;font-size:16px;line-height:22px;font-weight:700;font-style:normal;}.carousel .carousel-one-column .react-tabs__tab:focus,.carousel .carousel-two-column__tab-labels-left .react-tabs__tab:focus{outline:0px none;}.carousel .carousel-one-column .react-tabs__tab h2,.carousel .carousel-two-column__tab-labels-left .react-tabs__tab h2{color:var(--carousel-tab-color);font-size:16px;line-height:24px;font-weight:400;padding:0px;}.carousel .carousel-one-column .react-tabs__tab--selected,.carousel .carousel-two-column__tab-labels-left .react-tabs__tab--selected{cursor:text;display:block;cursor:text;}.carousel .carousel-one-column .carousel-asset .card-deck-container-block.container,.carousel .carousel-one-column .carousel-asset .illustration-icon-grid-container-block.container,.carousel .carousel-two-column__tab-labels-left .carousel-asset .card-deck-container-block.container,.carousel .carousel-two-column__tab-labels-left .carousel-asset .illustration-icon-grid-container-block.container{padding:0px;}.carousel .carousel-asset-code .carousel-asset-code-block{background-image:url(https://assets.website-files.com/5d35f5b2989a23dd99c4cb9a/5dd5be9fcd567c46f05e5544_image-dots-browser-16-colored.svg);background-repeat:no-repeat;background-position:32px 32px;background-color:var(--color-light-gray);height:500px;overflow-y:scroll;padding:40px 32px 32px;position:relative;-moz-box-shadow:0 10px 20px 0 rgba(152,162,179,0.25);-webkit-box-shadow:0 10px 20px 0 rgba(152,162,179,0.25);-o-box-shadow:0 10px 20px 0 rgba(152,162,179,0.25);-ms-box-shadow:0 10px 20px 0 rgba(152,162,179,0.25);box-shadow:0 10px 20px 0 rgba(152,162,179,0.25);}.carousel .carousel-asset-code .carousel-asset-code-block .react-tabs__tab-panel{height:484px;overflow:auto;width:100%;}.carousel .carousel-asset-code .prettyprint{background-color:var(--color-light-gray);border:0px none !important;left:0;margin:20px auto 0;padding:16px;position:absolute;right:0;width:675px;}.carousel .carousel-asset-code .prettyprint ol li{background:none;}.carousel .carousel-asset-code .prettyprint .linenums{color:#999;display:contents;}.carousel .carousel-asset-code .prettyprint code{display:inline-block;left:0;overflow:hidden;position:relative;vertical-align:top;white-space:pre-wrap;width:90%;word-break:break-word;}.carousel .carousel-description{width:auto;max-width:854px;text-align:center;padding:0px 16px;margin-right:auto;margin-left:auto;}.carousel .icon-text-card-deck .container{padding-left:0px !important;padding-right:0px !important;}.carousel .icon-text-card.card .card-body{text-align:left;}.carousel .icon-text-card.card .card-body h3{font-size:24px;line-height:28px;}.carousel .icon-text-card.card .card-footer{text-align:left;}.carousel .icon-text-card.card .card-footer .card-footer{margin-top:16px !important;}@media screen and (max-width:991px) and (min-width:768px){.carousel .card.icon-text-card{width:100%;}}@media screen and (max-width:768px){.carousel .carousel .description{width:100%;margin:32px auto 8px auto;}}@media screen and (max-width:575px){.carousel .react-tabs__tab h2{font-size:14px !important;line-height:24px !important;}.carousel .icon-text-card-deck{margin:auto;}}.training .rail>div{position:relative;}.euiComboBox .euiFormControlLayoutIcons{position:absolute !important;}.euiComboBox .euiFormControlLayoutCustomIcon--clickable{border:0;background-color:transparent;padding:0;}.euiComboBox .euiFormControlLayoutClearButton{display:flex;justify-content:center;align-items:center;border:1px solid #98A2B3;}.css-1yifmy7-euiBadge-hollow{display:flex;align-items:center;}.euiBadge__iconButton.css-iqdgtj-euiBadge__iconButton-right{border:0;background:transparent;padding:0;}.euiComboBoxOptionsList__rowWrap{border-radius:6px;}.euiComboBoxOptionsList__rowWrap .euiFilterSelectItem{border:0;background:#fff;border-bottom:1px solid #EEF2F7;font-size:14px;font-weight:400;display:flex;align-items:center;}.euiComboBoxOptionsList__rowWrap .euiFilterSelectItem-isFocused{background-color:rgba(0,119,204,0.1);}@media (max-width:767px){.horizontal-scroll-table{width:100vw;overflow-x:auto;}}.comparison-table{max-width:830px;margin:0 auto;}.comparison-table thead{border-bottom:4px solid #000;}.comparison-table th{text-transform:uppercase;font-weight:700;letter-spacing:0.1em;padding:1rem 1.5rem;text-align:center;}.comparison-table td{padding:1rem 1.5rem;}.comparison-table td:not(:first-child){text-align:center;}.comparison-table tbody{font-size:0.875rem;}.comparison-table tbody tr{border-bottom:1px solid #ccc;}.comparison-table tfoot td{width:100%;color:#ccc;}.pricing-card.bg-dark-teal,.list.bg-dark-teal{--bullet-color:var(--color-dark-teal);}.pricing-card.bg-yellow,.list.bg-yellow{--bullet-color:var(--color-yellow);}.pricing-card.bg-elastic-teal,.list.bg-elastic-teal{--bullet-color:var(--color-elastic-teal);}.pricing-card.bg-pink,.list.bg-pink{--bullet-color:var(--color-pink);}.pricing-card.bg-blurple,.list.bg-blurple{--bullet-color:var(--color-blurple);}.list.dark-teal{--bullet-color:var(--color-dark-teal);}.list.yellow{--bullet-color:var(--color-yellow);}.list.elastic-teal{--bullet-color:var(--color-elastic-teal);}.list.pink{--bullet-color:var(--color-pink);}.list.blurple{--bullet-color:var(--color-blurple);}.serverless-pricing-table{width:100%;border-spacing:32px 0;table-layout:fixed;height:1px;}.serverless-pricing-table h4{letter-spacing:-0.04em;line-height:1.1;}.serverless-pricing-table tr{height:100%;}@media (min-width:767px){.serverless-pricing-table tr{border-bottom:1px solid var(--color-dark-gray);}}.serverless-pricing-table tr:last-child{border:none;}@media (max-width:767px){.serverless-pricing-table tr:not(:first-of-type){display:none;visibility:hidden;}}.serverless-pricing-table th{padding:0 0.5rem;}.serverless-pricing-table th:first-child{padding:0 1rem 1.5rem;}.serverless-pricing-table th:last-child{padding-right:0;}.serverless-pricing-table td,.serverless-pricing-table th{padding:0 0.5rem;vertical-align:top;height:100%;}.serverless-pricing-table td:first-child,.serverless-pricing-table th:first-child{padding:1.5rem 1rem 0 1.5rem;}@media (max-width:767px){.serverless-pricing-table td:first-child,.serverless-pricing-table th:first-child{border-bottom:none;padding:0;margin:0;}}.serverless-pricing-table td:last-child,.serverless-pricing-table th:last-child{padding-right:0;}@media (max-width:767px){.serverless-pricing-table td,.serverless-pricing-table th{display:block;height:initial;padding:0;margin:0 0 1.5rem;border-bottom:1px solid var(--color-dark-gray);}}.serverless-pricing-table td.feature-label,.serverless-pricing-table th.feature-label{font-family:'MierB','Inter',Arial,sans-serif;font-size:1.25rem;font-weight:700;letter-spacing:-0.02em;padding:1.5rem 1rem 1.5rem 1.5rem;}.serverless-pricing-table td.feature-label span:last-child,.serverless-pricing-table th.feature-label span:last-child{margin-top:6px;display:block;font-weight:normal;}.serverless-pricing-table td.feature,.serverless-pricing-table th.feature{vertical-align:middle;text-align:center;}.serverless-pricing-table td.feature .card-content,.serverless-pricing-table th.feature .card-content{padding:24px 0;height:100%;display:flex;flex-direction:column;justify-content:center;}.serverless-pricing-table td.feature .card-content *,.serverless-pricing-table th.feature .card-content *{vertical-align:middle;}.serverless-pricing-table .card-content{border-left:1px solid var(--color-dark-gray);border-right:1px solid var(--color-dark-gray);background:#fff;padding:0 0.5rem;}.serverless-pricing-table .card-content__container{padding:0 0.5rem 1.5rem;display:flex;flex-flow:column nowrap;align-items:center;justify-content:space-between;}.serverless-pricing-table .card-content__container p{margin:0;}.serverless-pricing-table .card-content__container .button{width:max-content;margin:0 auto;align-self:end;}.serverless-pricing-table .card-content__content{flex:1;display:grid;}.serverless-pricing-table .card-content.header{display:flex;flex-flow:column nowrap;justify-content:space-between;}.serverless-pricing-table .button-unstyled{padding:8px 24px;min-height:50px;display:inline-block;border:2px solid transparent;width:max-content;margin:0 auto;align-self:end;}.serverless-pricing-table .header{padding:0;}.serverless-pricing-table .card-content__header,.serverless-pricing-table .card-content__includes{padding:0 0.5rem;}.serverless-pricing-table .card-content__includes{padding:2rem 0.5rem;border-top:1px solid var(--color-dark-gray);}.serverless-pricing-table .border-bottom{border-bottom:1px solid var(--color-dark-gray);border-bottom-left-radius:16px;border-bottom-right-radius:16px;}.serverless-pricing-table .borderless-bottom{border-bottom:none;}.serverless-pricing-table .pricing-card{border-top-left-radius:16px;border-top-right-radius:16px;height:100%;padding-top:16px;}.serverless-pricing-table .pricing-card .card-label{padding:0.875rem 0;}.serverless-pricing-table .pricing-card .header{height:100%;border-top-left-radius:16px;border-top-right-radius:16px;border-top:1px solid var(--color-dark-gray);}.serverless-pricing-table .topic-heading{color:var(--color-light-ink);font-size:1rem;font-weight:700;line-height:22px;letter-spacing:0.1em;text-transform:uppercase;}.serverless-pricing-table .price{font-family:'MierB','Inter',Arial,sans-serif;font-size:1.75rem;font-weight:700;letter-spacing:-0.04em;margin-top:8px;}.serverless-pricing-table .list{max-width:250px;width:max-content;margin:0 auto;text-align:left;text-indent:-1.5rem;}.serverless-pricing-table .list li{font-weight:normal;margin-bottom:0.75rem;}.serverless-pricing-table .list li:before{content:"✓";position:relative;top:-2px;left:0;display:inline-block;background-color:var(--bullet-color,black);border-radius:50%;border-style:none;height:16px;width:16px;font-size:0.75rem;line-height:normal;color:#fff;padding-top:0;padding-left:3px;margin-right:0.5rem;text-indent:0;}.mobile-container{border-top:1px solid var(--color-dark-gray);margin:2rem 0 0;padding:1rem 0 0;}@media (min-width:767px){.mobile-container{display:none;visibility:hidden;}}.mobile-container__item{margin:1rem 0;}.mobile-container .topic-heading{padding-bottom:0.25rem;}.mobile-container__price-heading{font-weight:normal;}.mobile-container__price{font-weight:700;}.card-content--stretch .card-content__includes{min-height:373px;}.feature .pricing{margin:0 0 1rem;}.optional-eyebrow{display:block;font-size:1rem;text-transform:uppercase;color:var(--color-dark-teal);letter-spacing:0.1rem;}.pill>.topic-heading{font-size:0.875rem;}#LbltempCheckBoxforForm{padding-left:20px;}#marketo-fe-form .mktoForm .mktoFieldWrap .mktoCheckboxList{margin:0;}#LbltempCheckbox1{padding-left:20px;}#tempCheckbox1{margin-top:5px;}
ElasticとOpenTelemetryで実現、最先端を行くKubernetesのオブザーバビリティとセキュリティ
Kubernetesを利用すると、その構造化特性により、サービスとアプリケーションのデプロイと管理を再現可能な方法でスケーラブルに行うことができます。そのため、Kubernetesは、さまざまな業界で、オンプレミスモデルとクラウドの両方のデプロイモデルで幅広く採用されるようになりました。しかし、Kubernetesの運用には自律的特性があるため、包括的な完全統合型のオブザーバビリティとセキュリティが必要です。現在、それが唯一可能なのはElasticプラットフォームです。
この記事では、ElasticsearchとOpenTelemetryを使用してKubernetes上のアプリケーションとサービスのワークフローを監視し、セキュリティを確保するためのベストプラクティスについて説明します。以下の方法を取り上げます。
KubernetesクラスターにElastic Agentをデプロイして構成する Elastic Agentを使用して、OpenTelemetryアプリケーショントレース、メトリック、イベントを取り込む Elastic Agentを使用して、Kubernetesコンテナーログ、クラスターメトリック、ネットワークトラフィックパターンを取り込む Elastic Defendを使用して、Kubernetesクラスターにセキュリティ重視の監視と脅威保護を追加する Elasticのすぐに使えるダッシュボードを詳しく知り、運用上の問題の監視、相関付け、根本原因分析を行えるようになる Kubernetesデプロイのトラブルシューティング上の課題はデータサイロ 長い間、アプリケーションオブザーバビリティ、インフラストラクチャーオブザーバビリティ、インフラストラクチャーセキュリティはそれぞれ別の分野だと考えられており、異なるチームが異なるツールを使って対処していました。このモデルは、組織としては便利な場合が多いのですが、よくある問題や見えない障壁にすぐに直面することになります。
アナリスト(人間または機械)は、特定の運用上の問題が、インフラストラクチャーの問題なのか、アプリケーションの欠陥なのか、セキュリティ侵害なのかを判断するために、アプリケーションオブザーバビリティ、インフラストラクチャーオブザーバビリティ、インフラストラクチャーセキュリティの分野それぞれで収集したデータを参考にする必要があります。さらに、その問題の根本原因を探るために、これら3つの分野すべてで信頼性の高いデータにアクセスする必要もあります。
それらに対処するために、良質のオブザーバビリティデータが複数のデータプラットフォームで重複して使用されるという事態がよく起こります。うまくすれば、データ保存、サポート、トレーニングのコストが2-3倍になる程度で済みますが、最悪の場合、アナリストが根本原因を適切に解明するために必要な重要なデータを見落としてしまいます。このような事態は、Kubernetesの動的でスケーラブルな特性によってさらに悪化します。オブザーバビリティプラットフォームとセキュリティプラットフォームを分ける方法は、Kubernetesで実現できる統合型のサービスデプロイモデルに対するアンチパターンであると言えるでしょう。
オブザーバビリティ実現の良いタイミング 突き詰めていくと、開発者、オペレーター、セキュリティアナリストは一様に、アプリケーションとインフラストラクチャーにまたがった、システムのエンドツーエンドの一元的なビューを必要としています。インフラストラクチャーデプロイチームは、可視性を実現し、クラスターのセキュリティを確保するために、一元化されたKubernetesネイティブのデプロイパターンを必要としています。
これを実現するには、以下の3つの条件があります。
すべてのオブザーバビリティデータとセキュリティデータ(アプリケーション、サービス、インフラストラクチャーのログ、トレース、イベント、メトリックを含む)が、検索(相関付けとレイテンシ)と保存(コスト)の両方に最適化された方法で、一元化されたデータプラットフォームに保存されていること。 一元的なデータプラットフォームに、ロールベースでデータの相関付けと提示を行う機能があること。たとえば、アナリストが問題の根本原因を突き止めようとするケースでは、可能性の高い問題にアナリストを導き、データの元の形式やソースにかかわらず、アナリストがデータをシームレスに操作できる機能が必要です。 アプリケーションとインフラストラクチャーのインストルメンテーションがKubernetesの指針(カスタム構成不要の、再現可能でスケーラブルなデプロイ)に合致していること。 Elastic:Kubernetesにうってつけ Elastic、OpenTelemetry、Kubernetes、および最新のコンピューティングハードウェアによるテクノロジーの結集により、本格的なKubernetesネイティブのオブザーバビリティとセキュリティが実現可能になりました。
データ収集モデル 以下の図に示すように、オブザーバビリティデータとセキュリティデータの収集には、ハイブリッドモデルをお勧めします。このモデルでは、Elastic Agentを使用してKubernetesインフラストラクチャーメトリックとアプリケーションコンテナーログを取得し、OpenTelemetry APMエージェントを使用してアプリケーショントレース、トレースイベント、メトリックを取得します。このアプローチをお勧めする理由は、以下で少し詳しく説明します。
アプリケーショントレース、イベント、メトリックデータ アプリケーショントレースとメトリックを生成するには、通常、APMライブラリをアプリケーションコードに直接組み込む必要があります。APM導入にはベンダーロックインが障壁となりますが、OpenTelemetry APMエージェントを利用するとそれを回避できます。OpenTelemetryはこれまでに、アプリケーショントレースとトレースイベントをキャプチャできる強力で標準化されたエージェント実装を実現するために、多大な労力を費やしています。また、アプリケーションメトリックのサポートも新たに行うようになりました。現在では、ほぼ すべての一般的なプログラミング言語 に対して、かなり成熟したエージェントが存在します。多くの場合、自動インストルメンテーション機能を使用して、ほとんどあるいはまったくコーディングすることなく、アプリケーションをインストルメントできます。.NET、Java、NodeJS、またはPythonで作成し、一般的なフレームワークを使用しているアプリケーションの場合、 OpenTelemetry Kubernetesオペレーターを使用して実行時に APMライブラリを挿入することもできます。
根本原因を分析するには、APMデータをアプリケーションログとインフラストラクチャーメトリックに相関付ける必要があります。この相関付けを行うには、識別のための特定のメタデータまたはリソース属性がアプリケーションのトレースとログおよびインフラストラクチャーメトリックの間で共通していなければなりません。Elasticプラットフォームでは、 service.name、pod.uid 、 container.id を使用して、Kubernetesで実行されているアプリケーションのオブザーバビリティデータを相関付けます。これまでは、このメタデータを取得するために、APMライブラリは外部を見にいっていました。この機能は現在、一部のOpenTelemetry APMライブラリ(Javaなど)でサポートされていますが、他のライブラリ(Rustなど)ではサポートされていません。このアプローチは、デプロイがシンプルになりますが、理想から程遠いことは明らかです。(アプリケーションのコンテキストで実行される)APMエージェントは、ランタイム環境(Docker、Kubernetesなど)を判断する必要があるだけでなく、その識別子にアクセスするための十分なアクセス権も必要だからです(例: /proc/self/cgroup やKubernetes APIへのアクセス権)。後者は、明らかにセキュリティ上の懸念事項です。こういった事情から、メタデータを(環境変数として)渡したり、トレースデータの作成後にメタデータを付加したりする際には、外部エンティティを利用するのが理想的です。使用するOpenTelemetry APMライブラリにかかわらずトレースデータに container.id を確実に追加するために、Elasticでは、 OpenTelemetry Collector をデプロイし、ノードのDaemonSetで k8sattributesprocessor を実行します。
APMデータは、関連するKubernetesメタデータでタグを付けられた後、OpenTelemetry Collectorから(同じくノードのDaemonSetで実行されている)Elastic Agentに転送されます。Elastic Agentには、Fleetを通じてAPM統合が構成されています。APM統合を各ノードのDaemonSetに分散することで、APMインジェストの負荷を分散でき、その可用性とサポート対象のアプリケーションの結びつきがより密接になります。また、GRPC/HTTP2トラフィックがDaemonSet自体のローカルに保たれるので、複雑なGRPC/HTTP2ロードバランシングを回避できるというメリットもあります。さらなるメリットとして、セキュリティが簡素化されます。ノードとElasticsearchクラスターの間はFleetで管理するElastic Agent TLSセキュリティによって守られるので、OTLPがノード上で安全でなくても問題ありません。Elastic APM統合により、OpenTelemetryトレース、メトリック、イベントデータは Elasticsearch Common Schema(ECS) に変換されます。その後、最終的なドキュメントがElasticsearchに送信され、インジェストされてインデックスされます。
コンテナーログとKubernetesインフラストラクチャーメトリック OpenTelemetry規格はロギングをサポートしていますが、 その実装はまだドラフト段階 です。そのため、利用できるエージェントの多くは、ロギングフレームワークをフックする機能をまだサポートしていません。現実面では、コンテナーログファイルのインジェストが、アプリケーションのログデータをキャプチャする唯一の実用的な方法です。OpenTelemetry Collectorの filelogreceiver を使うとそれが可能ですが、これは現在アルファリリースの段階であり、本番環境での使用にはまだお勧めしません。Kubernetesインフラストラクチャーメトリックのキャプチャを目的とする k8sclusterreceiver にも同じことが当てはまります。
それらと比較すると、Elastic Kubernetes統合は強力で、実績があり、アプリケーションログとKubernetesインフラストラクチャーメトリックを高い信頼性で収集します。さらに、Elastic Kubernetes統合はKibanaからまとめてリモートで管理できるので、構成が非常に簡単です。アプリケーションのトレースとメトリックとは異なり、Elastic Kubernetes統合はアプリケーションコードの外部で動作するため、ベンダーロックインの懸念が軽減されます。
それに加えて、各ノードのDaemonSetにElastic Agentが存在するため、Kubernetes統合以外にも大きな価値があります。この記事でも後ほど取り上げますが、これと同じElastic Agentインスタンスを利用してElastic Defendをデプロイし、Kubernetesノードを監視してセキュリティを確保できます。
セキュリティイベントとホストデータ 問題の根本原因をアナリストが特定するためには、最新のKubernetesのオブザーバビリティワークフローとセキュリティワークフローが密接に連携する必要があります。Elasticのセキュリティソリューションには、完全に統合された、高性能の、Kubernetes対応 SIEM 、 SOAR 、 XDR ソリューション( エンドポイント保護 を含む)があります。
注目すべきは、ElasticのDefend統合によって、大幅に強化されたKubernetesオブザーバビリティが提供されることです。また、Elasticの Kubernetesセキュリティ態勢管理統合 によって、アプリケーションの構成に潜在的な問題がある場合は、その問題をセキュリティチームが特定する前に、開発、DevOps、DevSecOpsチームに通知できます。さらに、 Kubernetesセキュリティダッシュボード を使用すると、Kubernetesノードでどのようなプロセスが、いつ、どのようなランタイムパラメーターで、どのアカウントを使って実行されたのかを確認できます。このようにランタイムの状況を比類ないレベルで可視化されているため、コンテナー化したアプリケーションで依存関係として使用されるコンテナーレイヤーに発生する予期せぬセキュリティ脅威の検知が可能になります。
ElasticのAPM統合と同様に、このようなセキュリティ統合は、Fleetを使って追加、構成、削除ができ、Kubernetesクラスターの各ノード上のDaemonSetのElastic Agent内で実行されます。
実装に挑戦しよう 要件 まず、アプリ、Elastic Agent、OpenTelemetry Collectorのデプロイ先となるKubernetesクラスターが必要です。私は、ハイパースケーラーでテストクラスターの作成、管理、削除を簡単に行うことができる kOps をよく使います。なお、 AWS EC2 t3.xlarge を1つ用意すれば、OpenTelemetryデモとElastic Agentをデプロイするのに十分です。ここで使うサンプルは、セルフマネージドとマネージド(EKS、GKEなど)のどちらのKubernetesクラスターでも動作します。また、デプロイ先は大手ハイパースケーラーでも、オンプレミス(OpenShiftなど)でもかまいません。理論的には、デスクトップのKubernetesクラスター(MicroK8sやDockerの組み込みKubernetesエンジンなど)でも、その環境にRAMとCPUが十分(4 vCPU、16GB RAMなど)用意されていれば動作します。また、Kubernetesの管理に関する基礎知識(yamlのデプロイ、Podステータスの確認、Podのログファイルの表示など)も必要です。始める前に、Kubernetesコンテキストに適切なクラスターが設定されていることを確認します。
当然ですが、OpenTelemetryでインストルメントできるアプリケーションまたはサービスも必要になります。自分で用意しなくても、 OpenTelemetry Demo のElasticのフォークを使い、それを元に、この記事で説明する、 アプリケーションとサービスをインストルメントするためのベストプラクティス を試してもかまいません。
最後の要件は、最新(8.5以降)のElasticsearchデプロイにKubernetesアプリケーションクラスターからアクセスできることです(ElasticsearchデプロイはElastic Cloudで無料で作成できます)。
ツール このチュートリアルでは、LinuxまたはMacOSベースのホストを使ってKubernetesクラスターを構成することを前提としています。以下のツールがインストールされていることを確認してください。
Elasticsearchクラスターを作成する 最新のElasticsearchデプロイがすでに使える状態の方は、準備万端です。準備がまだの方は、 cloud.elastic.coで無料の試用版クラスターを作成して デプロイをセットアップしましょう。
cloud.elastic.coに移動して 無料トライアルに登録します (クレジットカードは不要です)。 Elasticsearchクラスターに「o11y」などの名前を付けます。他の設定はデフォルト値のままにします。 [Create Deployment](デプロイを作成)をクリックします。 Elasticsearch Cloudに「Your deployment is ready!」(デプロイの準備が完了しました)と表示されるまで待ちます。 [Continue](続行)をクリックしてKibanaにログインします。 DaemonSetにElastic Agentをデプロイする データをElasticsearchに取り込むために、Kubernetesクラスターの各ノード上のDaemonSetにインストールされたElastic Agent(および統合)を使用します。
1. 以下のYAML(詳しくは こちら )をローカルマシンにダウンロードします。このYAMLを使用して、Elastic Agent(とElastic Defend)をKubernetesノードのDaemonSetにデプロイします。
curl -L -O https://raw.githubusercontent.com/elastic/endpoint/main/releases/8.5.0/kubernetes/deploy/elastic-defend.yaml 2. ローカルマシンに以下のパッチをダウンロードします。このパッチは、デフォルトのElastic AgentコンテナーのRAMとCPUの割り当てを引き上げ、今後インストールするすべての統合に余裕を持って対応できるようにするためのものです。本番環境では、以下で取り上げる統合の一部だけをデプロイする判断をしてもかまいません。その場合、この変更を適用しなくても問題ありません。
curl -L -O https://raw.githubusercontent.com/ty-elastic/elastic-otel-k8s/main/agent/8.5.0/elastic-defend.yaml.patch 3. パッチをインプレースで適用します。
patch elastic-defend.yaml elastic-defend.yaml.patch 4. ElasticsearchクラスターのKibanaにログインしていることを確認します。
5. [Management](管理)の[Fleet]に移動します。
6. [Add agent](エージェントを追加)をクリックします。
7. 新しいポリシーに「k8s-apps」などの名前を付けます。
この"k8s-apps"ポリシーは、アプリケーションKubernetesクラスターのノードのDaemonSetに統合をデプロイするために使用します。 8. [Advanced options](高度なオプション)の下にある[Unenrollment timeout](登録解除タイムアウト)を「3600」秒に設定します。
Kubernetesではノードを動的に作成、削除できます。この設定を行うと、Elastic Agentのデプロイ先のノードが削除されたときに、エージェントが自動的に削除されます。 9. [Create policy](ポリシーを作成)をクリックします。
10. [Install Elastic Agent on your host](ホストにElastic Agentをインストール)の下で[Kubernetes]を選択します。
11. "FLEET_URL"変数の値を探してコピーし、以前ダウンロードしてパッチを適用した"elastic-defend.yaml"ファイルの"FLEET_URL"の値に貼り付けます。
12. "FLEET_ENROLLMENT_TOKEN"変数の値を探してコピーし、以前ダウンロードしてパッチを適用した"elastic-defend.yaml"ファイルの"FLEET_ENROLLMENT_TOKEN"の値に貼り付けます。
13. 以下のコマンドを実行して、elastic-defend.yamlをクラスターに適用します。
kubectl apply -f elastic-defend.yaml 14. [Confirm agent enrollment](エージェント登録の確認)に"1 agent has been enrolled"(1個のエージェントが登録されました)と表示されるまで待ちます。
15. [Incoming data confirmed](受信データの確認)に"Incoming data received from 1 of 1 recently enrolled agent"(最近登録された1個のエージェントのうち1個から受信データを受け取りました)と表示されるのを待ちます。
16. [Close](閉じる)をクリックします。
Kubernetesホストオプションを選択したときに、Elasticに事前設定済みのElastic Agentデプロイ用YAMLが用意されていることに気付いたかもしれません。このデプロイYAMLには、Elastic Defendイメージがまだ含まれていません。
Elastic Agent統合をセットアップする ここからは簡単な作業です。DaemonSetにデプロイされた、Fleetによる管理対象のElastic Agentは、Kibana統合インターフェースを使って統合の追加/構成/削除をリモートで簡単に行うことができます。
APM
Elastic APM統合 は、OpenTelemetry APMデータをElasticにインポートするために使用します。
Kibanaで[Management](管理)の[Integrations](統合)に移動します。 「APM」を検索します。 [APM]をクリックします。 [Manage APM integration in Fleet](FleetでAPM統合を管理)をクリックします。 [Add Elastic APM](Elastic APMを追加)をクリックします。 [General](全般)の[Server configuration](サーバー構成)にある[Host](ホスト)を「0.0.0.0:8200」に設定します(これでAPM統合の取り込みがノード上の他のPodに公開されます(同じくDaemonSetで実行されているOpenTelemetry Collectorを含む))。 [Agent authorization](エージェント認証)の[Anonymous Agent access](匿名エージェントアクセス)を無効にします(このように簡素化することで、認可トークンなしでOpenTelemetry CollectorからElastic APM統合にOTELデータを送信できます。これのメリットは、APM統合がノード外部に公開されない点です)。 (任意)[Tail-based sampling](tailベースのサンプリング)を有効にします(これにより、トレースデータのインテリジェントなサブサンプリングが可能になり、異常や全体的なパフォーマンスをキャプチャしながら、ストレージ要件を緩和できます)。 [Where to add this integration](この統合の追加先)を[Existing hosts](既存のホスト)に設定し、[Agent policy](エージェントポリシー)を「k8s-apps」に設定します。 [Save and continue](保存して続行)をクリックします。 [Save and deploy changes](保存して変更をデプロイ)をクリックします。 Kubernetes kube-state-metricsをデプロイする Kubernetes統合を行うには、 kube-state-metrics をKubernetesクラスターで利用できるようにして、デフォルトのKubernetesダッシュボードを有効にする必要があります。
1. kube-state-metrics helmレポジトリを追加します。
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
2. kube-state-metricsをKubernetesクラスターにインストールします。ネームスペースはElastic Agentと同じにします(Elastic Agentの前述のデプロイYAMLで使用したもの。デフォルトは"kube-system")。
helm install --set namespaceOverride=kube-system kube-state-metrics prometheus-community/kube-state-metrics Kubernetes統合をインストールする
Elastic Kubernetes統合 は、KubernetesのメトリックとログをElasticsearchにインポートするために使用します。
Kibanaで[Management](管理)の[Integrations](統合)に移動します。 「Kubernetes」を検索します。 [Kubernetes]を選択します。 [Add Kubernetes](Kubernetesを追加)をクリックします。 [Collect Kubernetes metrics from kube-state-metrics](kube-state-metricsからKubernetesメトリックを収集)を有効にします(まだ設定していない場合)。 [Where to add this integration](この統合の追加先)を[Existing hosts](既存のホスト)に設定し、[Agent policy](エージェントポリシー)を「k8s-apps」に設定します。 [Save and continue](保存して続行)をクリックします。 [Save and deploy changes](保存して変更をデプロイ)をクリックします。 Elastic Defend
Elastic Defend統合 は、Kubernetesノードの保護と、セキュリティ重視のオブザーバビリティデータの追加収集に使用します。
Kibanaで[Management](管理)の[Integrations](統合)に移動します。 「Elastic Defend」を検索します。 [Elastic Defend]を選択します。 [Add Elastic Defend](Elastic Defendを追加)をクリックします。 [Integration name](統合名)を「defend-1」に設定します。 [Select configuration settings](構成設定の選択)に移動して[Select the type of environment you want to protect](保護対象の環境の種類を選択)を[Cloud Workloads (Linux servers or Kubernetes environments)](クラウドワークロード(LinuxサーバーまたはKubernetes環境))に設定し、[To reduce data ingestion volume, select Interactive only](データインジェストボリュームを削減するには[Interactive only](インタラクティブのみ)を選択)を[All events](すべてのイベント)に設定します。 [Where to add this integration](この統合の追加先)を[Existing hosts](既存のホスト)に設定し、[Agent policy](エージェントポリシー)を「k8s-apps」に設定します。 KubernetesクラスターがGKEまたはEKS上で実行されていない場合は、[Advanced options](高度なオプション)で[Namespace](ネームスペース)を「k8sapps」などに設定します。この"k8sapps"は、アプリケーションKubernetesクラスターを識別します(これは、適用対象のKubernetesクラスターの名前でElastic Defendテレメトリーにタグを付けるために必要です)。 [Save and continue](保存して続行)をクリックします。 [Settings](設定)を選択します。 [Elastic Defend version](Elastic Defendバージョン)に移動して[Keep integration policies up to date automatically](統合ポリシーを自動的に最新の状態に保つ)を有効にします。 [Integration policies](統合ポリシー)を選択して、[defend-1]をクリックします。 [Type: Operating system / Event Collection: Linux](タイプ:オペレーティングシステム/イベントコレクション:Linux)で[Capture terminal output](ターミナル出力をキャプチャ)を有効にします。 [Save and continue](保存して続行)をクリックします。 [Save and deploy changes](保存して変更をデプロイ)をクリックします。 特定のElasticsearch Kubernetesダッシュボードを使うには、Kubernetesクラスターの名前とIDを把握しておく必要があります。GKEまたはEKSでクラスターを実行している場合、Elastic Agentが自動的にこのメタデータを取得します。セルフマネージドKubernetesクラスターを実行している場合は、インジェストパイプラインを作成して、前の手順で定義したポリシーネームスペースから自動的に設定される orchestrator.cluster.name フィールドと orchestrator.cluster.id フィールドでセキュリティイベントを装飾できます。
[Management](管理)の[Dev Tools](開発ツール)に移動します。 以下を実行します。
PUT _ingest / pipeline / logs - endpoint . events . process@custom
{
"processors" : [
{
"set" : {
"field" : "orchestrator.cluster.name" ,
"copy_from" : "data_stream.namespace" ,
"ignore_empty_value" : true ,
"ignore_failure" : true
}
},
{
"set" : {
"field" : "orchestrator.cluster.id" ,
"copy_from" : "data_stream.namespace" ,
"ignore_empty_value" : true ,
"ignore_failure" : true
}
}
]
}
Kubernetesセキュリティ態勢管理
Elastic Kubernetesセキュリティ態勢管理統合 は、Kubernetesクラスターとアプリケーションが Center for Internet Security (CIS)で定義されている安全なKubernetes構成のベストプラクティスに従っているかどうかの検証に使用します。この統合により、セキュリティ侵害が発生する前に、開発チームやデプロイチームが誤設定を発見できるようになります。
Kibanaで[Management](管理)の[Integrations](統合)に移動します。 「Kubernetes Security Posture Management」を検索します。 [Kubernetes Security Posture Management](Kubernetesセキュリティ態勢管理)を選択します。 [Add Kubernetes Security Posture Management](Kubernetesセキュリティ態勢管理を追加)をクリックします。 [Kubernetes Deployment](Kubernetesデプロイ)を、セルフマネージドKubernetesの場合は[Unmanaged Kubernetes](アンマネージドKubernetes)に設定し、KubernetesアプリケーションクラスターをAWS EKSで実行している場合は[EKS (Elastic Kubernetes Service)]に設定します。 [Where to add this integration](この統合の追加先)を[Existing hosts](既存のホスト)に設定し、[Agent policy](エージェントポリシー)を「k8s-apps」に設定します。 [Save and continue](保存して続行)をクリックします。 [Save and deploy changes](保存して変更をデプロイ)をクリックします。 ネットワークパケットキャプチャ
Elasticネットワークパケットキャプチャ統合 は、Kubernetesノードで送受信されるネットワークトラフィックに関するインサイトを得るために使用します。
Kibanaで[Management](管理)の[Integrations](統合)に移動します。 「Network Packet Capture」を検索します。 [Network Packet Capture](ネットワークパケットキャプチャ)を選択します。 [Add Network Packet Capture](ネットワークパケットキャプチャを追加)をクリックします。 [Where to add this integration](この統合の追加先)を[Existing hosts](既存のホスト)に設定し、[Agent policy](エージェントポリシー)を「k8s-apps」に設定します。 [Save and continue](保存して続行)をクリックします。 [Save and deploy changes](保存して変更をデプロイ)をクリックします。 OpenTelemetryでインストルメントする OpenTelemetry Demo と OpenTelemetry Helm Charts は、Elasticオブザーバビリティに合わせて最適化してあります( アプリケーションをインストルメント してアプリケーションの変更内容と変更理由を把握する方法に関するドキュメント参照してください)。ElasticがKubernetesのオブザーバビリティとセキュリティにもたらす価値を確認するためにOpenTelemetryデモに含まれるアプリケーションだけを使用する場合は、 OpenTelemetryデモのセットアップ から始めてください。自分で用意したアプリケーションやサービスを監視したい場合は、 アプリケーションのインストルメント に関する説明に従ってください。
OpenTelemetryデモをセットアップする このセクションは、OpenTelemetryデモに含まれるインストルメント済みのアプリケーションをデプロイして監視することを前提としています。 変更済みのOpenTelemetry Helm Charts を使用します。これは、DemoアプリケーションとOpenTelemetry Collectorインスタンスの両方をデプロイします。
1. helmレポジトリを追加します。
helm repo add elastic-open-telemetry https://ty-elastic.github.io/opentelemetry-helm-charts helm repo update 2. DemoアプリとCollectorをKubernetesクラスターにインストールします。
helm install elastic-otel elastic-open-telemetry/opentelemetry-demo 3. 実行中のPodを一覧表示して、インストール結果を確認します。
kubectl get pods 以下のように、すべてのOpenTelemetryデモのPodと1つのOpenTelemetry Collectorインスタンスが表示されるはずです。
> kubectl get pods
NAME READY STATUS RESTARTS AGE
elastic - otel - adservice - 86b5b4f779 - 8lsgf 1 / 1 Running 0 3h28m
elastic - otel - cartservice - 55659bd5f4 - lvtjx 1 / 1 Running 0 3h28m
elastic - otel - checkoutservice - 88bfcf745 - 42nvt 1 / 1 Running 0 3h28m
elastic - otel - currencyservice - 659dd55fc8 - pcrrx 1 / 1 Running 0 3h28m
elastic - otel - emailservice - 64df788455 - mkb56 1 / 1 Running 0 3h28m
elastic - otel - featureflagservice - 6dcf49d84c - n5jtk 1 / 1 Running 0 3h28m
elastic - otel - ffspostgres - 67dcd7596d - htbpm 1 / 1 Running 0 3h28m
elastic - otel - frontend - 674c8fdc74 - zmv8r 1 / 1 Running 0 3h28m
elastic - otel - frontendproxy - 5bd757dc89 - r2728 1 / 1 Running 0 3h28m
elastic - otel - loadgenerator - 5b98bd9656 - 8z8hz 1 / 1 Running 0 3h28m
elastic - otel - otelcol - agent - kbb54 1 / 1 Running 0 3h28m
elastic - otel - paymentservice - 5c4b5c57bd - wkbqj 1 / 1 Running 0 3h28m
elastic - otel - productcatalogservice - 6995496975 - 7wm46 1 / 1 Running 0 3h28m
elastic - otel - quoteservice - 849797dfdd - bkj29 1 / 1 Running 0 3h28m
elastic - otel - recommendationservice - 6cb4476f - zpqqv 1 / 1 Running 0 3h28m
elastic - otel - redis - 5698bf675b - dl2xv 1 / 1 Running 0 3h28m
elastic - otel - shippingservice - 6b9fdcc467 - knlxb 1 / 1 Running 0 3h28m
4. 手順を 検証と監視 まで飛ばし、アプリケーショントレース、メトリック、イベントがElasticsearchに取り込まれていることを確認します。
OpenTelemetryでアプリケーションをインストルメントする このセクションは、自分のアプリケーションをOpenTelemetryでインストルメントすることを前提としています。自分のアプリケーションがこの記事で示すデプロイモデルと連携するためには、アプリケーションに対して以下の作業が必要です。
安定した OpenTelemetry APM Agent リリースでインストルメントします。 特定のOpenTelemetry環境変数でインストルメントします。 手動でインストルメントする場合は、特定のspanプロパティを適切に設定します。 (任意)ログ行に特定のメタデータを追加します。 ログデータをstdoutとstderrに対して生成して、Elastic Kubernetes統合でキャプチャされるようにします。 コンテナー環境変数 Elasticにデフォルトで用意されているAPMダッシュボードを有効にするには、アプリケーショントレース、メトリック、イベントに適切なコンテキストメタデータが含まれている必要があります。このメタデータは、 Kubernetes Downward API から取得して、 OTEL_RESOURCE_ATTRIBUTES 環境変数を通じてメタデータをアプリケーションに渡すことができます。
また、 OTEL_EXPORTER_OTLP_ENDPOINT を設定して、OpenTelemetryデータをOTLPを通じてOpenTelemetry Collectorインスタンスに送信するようにアプリケーションに対して指示する必要もあります。このOpenTelemetry Collectorは後ほど、ノードのDaemonSetにデプロイします。
以下のKubernetesコンテナー構成スニペットを使うと、デプロイYAMLでアプリケーションに適用する必要のある環境変数がセットアップされます。
---
apiVersion : apps / v1
kind : Deployment
...
spec :
...
template :
...
spec :
containers :
...
env :
- name : OTEL_K8S_CONTAINER_NAME
valueFrom :
fieldRef :
apiVersion : v1
fieldPath : "metadata.labels['app.kubernetes.io/component']"
- name : OTEL_K8S_NODE_IP
valueFrom :
fieldRef :
fieldPath : status . hostIP
- name : OTEL_K8S_POD_UID
valueFrom :
fieldRef :
apiVersion : v1
fieldPath : metadata . uid
- name : OTEL_K8S_POD_IP
valueFrom :
fieldRef :
apiVersion : v1
fieldPath : status . podIP
- name : OTEL_SERVICE_NAME
valueFrom :
fieldRef :
apiVersion : v1
fieldPath : "metadata.labels['app.kubernetes.io/component']"
- name : OTEL_K8S_NAMESPACE
valueFrom :
fieldRef :
apiVersion : v1
fieldPath : metadata . namespace
- name : OTEL_K8S_NODE_NAME
valueFrom :
fieldRef :
apiVersion : v1
fieldPath : spec . nodeName
- name : OTEL_K8S_POD_NAME
valueFrom :
fieldRef :
apiVersion : v1
fieldPath : metadata . name
- name : OTEL_EXPORTER_OTLP_ENDPOINT
value : '$(OTEL_K8S_NODE_IP):4317'
- name : OTEL_RESOURCE_ATTRIBUTES
value : service . name = $ ( OTEL_SERVICE_NAME ), k8s . namespace . name = $ ( OTEL_K8S_NAMESPACE ), k8s . node . name = $ ( OTEL_K8S_NODE_NAME ), k8s . pod . name = $ ( OTEL_K8S_POD_NAME ), k8s . pod . uid = $ ( OTEL_K8S_POD_UID ), k8s . pod . ip = $ ( OTEL_K8S_POD_IP ), k8s . container . name = $ ( OTEL_K8S_CONTAINER_NAME ), k8s . container . restart_count = 0
リソース属性の1つとして k8s.container.restart_count=0 を設定している理由が気になるかもしれません。現在のOpenTelemetry Collectorの k8sattributesprocessor が、コンテナーと実行中のコンテナーインスタンスを( k8s.container.name を通じて)照合して、 container.id を取得するために、この部分が必要です。 k8s.container.restart_count=0 を設定するのは単純化のためです。というのも、Pod内で特定のコンテナーがKubernetesによって再起動された回数を追跡して、それを何らかの形で環境変数に設定するのは厄介な作業だからです。通常のシナリオでは、Podの有効期間中、コンテナーは1回しか起動されません。Podによってコンテナーが再起動される場合、この単純化は失敗します。
Spanプロパティ Elastic APMは、spanデータを適切に特徴付け、分類し、可視化するために、特定のspanプロパティが正確に設定されている必要があります。ほとんどのOpenTelemetry APM自動インストルメンテーションライブラリでは、それらのフィールドが自動的に設定されます。アプリケーションを手動でインストルメントする場合は、次のプロパティを正確に設定する必要があります。 SpanKind: Elastic APMが適切にspanを分類するためには、 SpanKind (INTERNAL、SERVER、CLIENTなど)を把握する必要があります。ほとんどのOpenTelemetry APM自動インストルメンテーションライブラリでは、このフィールドが自動的に設定されます。しかし、アプリケーションを手動でインストルメントする場合は、RPCまたはREST呼び出しを受け取るspanのSpanKindはSERVERに、RPC、REST、またはデータベース呼び出しを開始するspanのSpanKindはCLIENTに、サービス内の関数呼び出しはINTERNAL(デフォルト)に設定する必要があります。たとえば、Javaの場合、以下のようなコードを使用して、gRPC呼び出しを受け取るspanのSpanKindをSERVERに設定します。
Span span = tracer . spanBuilder ( "testsystem.TestService/TestFunction" ). setSpanKind ( SpanKind . SERVER ). startSpan ();
RpcSystem/DbSystem: Elastic APMが適切にspanを分類するためには、そのspanが表しているのが RPCトランザクション なのか データベーストランザクション なのか、加えて、どのような種類のRPCまたはDBシステムが使われているのかを把握する必要があります。ほとんどのOpenTelemetry APM自動インストルメンテーションライブラリでは、このフィールドが自動的に設定されます。アプリケーションを手動でインストルメントする場合は、RpcSystemまたはDbSystem span属性を設定する必要があります。たとえば、Rustの場合、以下のようなものを使用して、gRPC呼び出しを受け取るspanのRpcSystemを"grpc"に設定します。
span . set_attribute ( semcov :: trace :: RPC_SYSTEM . string ( "grpc" ));
NetPeerHostとNetPeerPort: Elastic APMがサービス間の依存関係を適切にマップするためには、SpanKindがCLIENTに設定されている場合(つまり、gRPCまたはデータベース呼び出しを送信する場合)、 NetPeerNameおよびNetPeerPort span属性を設定して、想定する呼び出しの受信側を示す必要があります。一部のOpenTelemetry APM自動インストルメンテーションライブラリでは、このフィールドが自動的に設定されます。アプリケーションを手動でインストルメントする場合は、これらの属性を明示的に設定する必要があります。たとえば、JavaScriptの場合、以下のようなコードを使用して、gRPC呼び出しを送信するspanのNetPeerNameとNetPeerPortを設定します。
// this => grpcJs.Client, this.getChannel().getTarget() => "dns:elastic-otel-productcatalogservice:8080"
const URI_REGEX = /(?:([A-Za-z0-9+.-]+):(?:\/\/)?)?(?<name>[A-Za-z0-9+.-]+):(?<port>[0-9+.-]+)$/ ;
const parsedUri = URI_REGEX . exec ( this . getChannel (). getTarget ());
if ( parsedUri != null && parsedUri . groups != null ) {
span . setAttribute ( SemanticAttributes . NET_PEER_NAME , parsedUri . groups [ 'name' ]);
span . setAttribute ( SemanticAttributes . NET_PEER_PORT , parseInt ( parsedUri . groups [ 'port' ]));
}
ログ属性 Elastic APMは、特定のログ行を特定のトレースに相関付けることができます。この機能を有効にするには、アプリケーションで生成されるログ行に span.idとtrace.id のキーバリューペア(状況に応じて)でタグを付ける必要があります。一部のElastic APM Agent(Javaエージェントなど)は、自動的にロギングテンプレートを変更して、このコンテキストに応じたメタデータを追加します。
OpenTelemetry CollectorをDaemonSetにデプロイする OpenTelemetryデモと変更済みの OpenTelemetry Helm Charts を使用している場合、OpenTelemetry Collectorは各ノードのDaemonSetにすでにインストールされています。
自分でアプリケーションをインストルメントする場合、以下のYAMLをベースにOpenTelemetry Collectorを構成し、クラスターに含まれるノードのDaemonSetにデプロイできます。このサンプル構成(ConfigMapを使用してデプロイ)は、アプリケーションのOTLPトレース、メトリック、イベントデータをTCPポート4317(grpc)と4318(http)で取り込みます。CollectorはDaemonSetで実行されているので、そのポートには、同じノード上で実行される他のPodからもノードのIPを通じてアクセスできます。受信OTLPデータは、k8sattributesprocessorを経由して、 container.id が追加されます。受信するログデータはすべて破棄し(このデータはElastic Kubernetes統合を通じて取得していることを思い出してください)、トレース、イベントメトリックは同じくDaemonSetで実行されているElastic APM統合にパススルーします。Elastic APM統合はポート8200でリッスンしています。このポートも、ノードのIPを通じてアクセスできます(Kubernetes Downward APIから取得した環境変数としてここに渡されます)。
1. このサンプルOpenTelemetry CollectorデプロイYAMLをダウンロードします。
curl -L -O https://raw.githubusercontent.com/ty-elastic/elastic-otel-k8s/main/collector/otel-collector.yaml 2. デプロイにあわせて適宜編集します。
3. Kubernetesクラスターに適用します。
kubectl apply -f otel-collector.yaml 検証と監視 このセクションでは、上記で構成したオブザーバビリティとセキュリティデータが期待どおりにElasticsearchクラスターに送信されているかどうかを検証します。この作業には、Elasticsearchにデフォルトで用意されているオブザーバビリティとセキュリティダッシュボードのいくつかをざっと確認する役割もあります。ぜひ、この作業を通じて、Elasticのリッチな可視化と分析機能を体験してください。
APMサービスマップ APMサービスマップでは、サービスとサービス間の関係が可視化されます。サービスで発生した特定の種類のエラーとアラートがこのマップ上で必要に応じてハイライトされます。このマップを起点に、OpenTelemetry(またはElastic独自のAPMエージェント)でインストルメントされたサービスを詳細に確認できます。
Kibanaのナビゲーションサイドバーで、[Observability](オブザーバビリティ)の下にある[APM]を選択します。 [APM]の下にある[Service Map](サービスマップ)を選択します。
APMサービス APMサービスビューには、特定のサービスの概要が表示されます。このダッシュボードで、トレース、ログ、関連するインフラストラクチャーメトリックを簡単にドリルダウンできます。従来は、それらのデータソースの間を大ざっぱに相関付けるには、サイロ化した複数のオブザーバビリティプラットフォームのタイムスタンプとサービス識別子を手動で変換する必要がありました。ElasticとOpenTelemetryを組み合わせると、この相関付けが自動的に行われます。それにより、アナリスト、オペレーター、開発者は、オブザーバビリティツールの微妙な差異に煩わされることなく、根本原因の分析に集中できます。
Kibanaのナビゲーションサイドバーで、[Observability](オブザーバビリティ)の下にある[APM]を選択します。 [APM]の下にある[Service Map](サービスマップ)を選択します。 マップ内の任意のサービスを右クリックします。 [Service Details](サービスの詳細)を選択します。
この同じビューで、ユーザーとサービスをつないだり、サービスを相互につないだりして、トランザクションやspanを簡単に調べることができます。
ヘッダーの[Transactions](トランザクション)を選択します。 [Transactions](トランザクション)サブセクションで、調べたいトランザクションを選択します。
Kubernetesクラスターのメトリック Elastic Kubernetesダッシュボードでは、Kubernetesクラスターの運用状況の概要と詳細の両方を確認できます。このダッシュボードで、クラスター、ノード、Pod、DaemonSet、サービスなどのメトリックを監視できます。
Kibanaのナビゲーションサイドバーで、[Analytics](分析)の下にある[Dashboard](ダッシュボード)を選択します。 [Tags](タグ)メニューから[Kubernetes]を選択します。 [Cluster Overview](クラスターの概要)ダッシュボードを選択します。
Kubernetesプロセスの監視 Elastic Defend統合により、Kubernetesリソース全体にわたるセキュリティ重視のオブザーバビリティが実現します。Kubernetesセキュリティダッシュボードを使用すると、Kubernetesノードでどのようなプロセスが、いつ、どのようなランタイムパラメーターで、どのアカウントを使って実行されたのかを確認できます。
Kibanaのナビゲーションサイドバーで、[Security](セキュリティ)の下にある[Dashboards](ダッシュボード)を選択します。 [Kubernetes]を選択します。
Kubernetesセキュリティ態勢管理 Kubernetesセキュリティ態勢管理(KSPM)ダッシュボードは、セキュアなデプロイのためのKubernetesのベストプラクティスに従っているかどうかを自動的に分析し、修正するための推奨事項を提示します。開発者とDevOps担当者は、このダッシュボードから、本番環境で問題が発生する前に、潜在的な構成の問題を示す貴重な注意喚起を得ることができます。
Kibanaのナビゲーションサイドバーで、[Security](セキュリティ)の下にある[Dashboards](ダッシュボード)を選択します。 [Cloud Posture](クラウドポスチャー)を選択します。
ネットワークトラフィック分析 ネットワークパケットキャプチャ統合を使用すると、Kubernetesノードで送受信されるIPトラフィックを詳しく調査して、異常な動作をしているサービスやセキュリティ侵害を見付けることができます。
Kibanaのナビゲーションサイドバーで、[Security](セキュリティ)の下にある[Explore](調査)を選択します。 [Network](ネットワーク)を選択します。
Elastic:Kubernetesのためのオブザーバビリティとセキュリティ Kubernetesは、多くの企業にとってグリーンフィールドアプリケーションのデプロイモデルとなります。グリーンフィールドデプロイモデルには、でオブザーバビリティとセキュリティに対する、公平で偏りがなく、先進的かつ包括的なアプローチが必要です。ここで説明したように、Elasticプラットフォームは、完全に統合され、機能が充実し、高精度のオブザーバビリティとセキュリティをKubernetesに提供する能力を、業界で唯一備えています。
詳細についてご興味をお持ちの場合、 Elasticのプリセールスチームにお問い合わせ ください。Elasticを利用した開発を始めましょう。