ããã«ã¡ã¯ãSRE ã® @chaspy ã§ãã
ä»åãã¹ãã¼ã¸ã³ã°ç°å¢ã®ãªã½ã¼ã¹ã使ç¨ãã¦ããªãæéã«åæ¸ãããã¨ã§ãã³ã¹ããåæ¸ããã®ã§ãã®äºä¾ãç´¹ä»ãã¾ãã
åæ: Pull Request namespace
Quipper ã§ã¯æ¬çªç°å¢ãéçºç°å¢ã¨ãã« Amazon EKS*1 ã使ç¨ãã¦ãã¾ããã¾ããéçºç°å¢ã¨ãã¦ãPull Request ãã¨ã« namespace ãä½ããã¾ãããã㦠monorepo*2ã«åå¨ãããã¹ã¦ã® Application ã Deploy*3ããã¾ããããã«ãã Pull Request ããã¼ã¸ããåã«åä½ç¢ºèªããããã¨ãã§ãã¾ããDeveloper ã ãã§ãªã Designer ã Product Manager ãå«ãã¦ç¢ºèªã§ãããã®ã§ãQuipper åç©ã¨ãå¼ã¹ãåªããä»çµã¿ã§ãã
ä¸æ¹ã§ããã®ä»çµã¿ã«ããã³ã¹ãã®å¢å ãå¦ãã¾ãããPull Request namespace ã®ãªã½ã¼ã¹ã«é¢ãã¦ã¯ Resource Requests ããªãã¹ãå°ãããã¦ãã¾ãããç©ã¿éãªãã¨çµæ§ãªãªã½ã¼ã¹éãè¦æ±ãããã¨ã«ãªãã¾ãã
ããããå ¨ã¦ã® Application ã Deploy ããã®ã¯å¯è±ªçã§ã¯ãªãããã¨ããã®ã¯ãã¡ããç解ãã¦ãã¾ãããã¾ãã¯æããã«ä½¿ç¨ãã¦ããªãæéãå¤éãé±æ«ã«åæ¸ããæ¹æ³ãèãã¦ã¿ã¾ããã
æ¹æ³1: AutoScalingGroup ã® MaxSize ã 1 ã«ãã
ã·ã³ãã«ãªæ¹æ³ã§ãã以ä¸ã®ãããªã¹ã¯ãªãããæ¸ããMAXSIZE ã Jenkins ããå¤æ´ããã¸ã§ããå®è¡ãã¾ããåã« aws (autoscaling) cli ã®ã©ããã¼ã§ãã
#!/bin/bash set -eu : ${MAX_SIZE} STAGING_CLUSTERS=$(aws eks list-clusters | jq -r '.clusters[] | select(. | contains("staging"))') for CLUSTER in ${STAGING_CLUSTERS}; do NODE_GROUPS=$(aws eks list-nodegroups --cluster-name ${CLUSTER} | jq -r '.nodegroups[]') for NODE_GROUP in ${NODE_GROUPS}; do ASG_NAME=$(aws eks describe-nodegroup --cluster-name ${CLUSTER} --nodegroup-name ${NODE_GROUP} | jq -r '.nodegroup.resources.autoScalingGroups[].name') aws autoscaling update-auto-scaling-group \ --auto-scaling-group-name "${ASG_NAME}" \ --max-size "${MAX_SIZE}" done done
ã¨ã¯ãããç·æ¥æãªã©ãå¤éãé±æ«ã«æ¢ããå¾ã稼åããã¨ãã¯ããã§ãããããã®å ´å㯠MAXSIZE ãå ã«æ»ãã¸ã§ããå®è¡ãã¦ããããã°å éãã«ãªãã¾ãã
ããããæè»æ§ã«æ¬ ããã®ãåé¡ã§ããä»®ã«æ¥ä¸ 50 å°ã® Instance ãè¦æ±ããã¦ããç¶æ³ã§å¤é使ç¨ããå ´åãèãã¦ã¿ã¾ãããããã® Developer ãå¿ è¦ãªã®ã¯ãã£ã1ã¤ã® namespace åã®ãªã½ã¼ã¹ã ãããããã¾ãããã50å°ãã¹ã¦ç¨¼åããã¦ãã¾ããã¨ã«ãªãã¾ãã
ãã®ä»çµã¿ãã¾ãã¯æ¥æ¬ä»¥å¤ã® Global ã®éçºç°å¢ã§å®æ½ãã¾ãããç´ æ´ããããã¨ã«ãç·æ¥ã§å¤éãä¼æ¥ã«å©ç¨ãã¦ããã±ã¼ã¹ã¯ããã¾ããã§ãããã¾ããã¿ã¤ã ã¾ã¼ã³ãã¤ã³ããã·ã¢ã® WIB*5ããã£ãªãã³ã® PHT*6ãããã¦æ¥æ¬ã® JST ã¨ããã¾ã§å·®ããªã"å¤é"ã«ãºã¬ããªããããã¾ãããã¾ããã
æ¹æ³2: Deployment / Rollouts ã® Replicas ã 0 ã«ãã
æ¹æ³1 ã¨åãä»çµã¿ãæ¥æ¬ã®éçºç°å¢ã«å ¥ãããã¨èããã®ã§ãããæ¥æ¬å´ã®éçºè ã«ã¯ PDT*7 ã®ã¿ã¤ã ã¾ã¼ã³ã§åãã¦ããã²ã¨ããã¾ããããã°ã£ããã¯ãããã«"å¤é"ã®å®ç¾©ã大ããç°ãªããããä¸è¨ã®ä»çµã¿ã¯æ©è½ããªãã§ããããnamespace åä½ã§åæ¸ããããã¯å¾©æ´»ãã§ãããããæè»ãªä»çµã¿ãå¿ è¦ã§ãã
ããããããã§ãPull Request namespace ã¨ç¹å¥ãª namespace ã® Deployment / Rollouts*8 ã® replicas ã 0 ã«ããã¹ã¯ãªãããæ¸ãã¾ããã
#!/bin/bash set -ex : "${KUBECONTEXT}" REPLICAS="0" function set_current_replicas_to_annotation () { # When scaling-in, the current replicas is stored in annotation 'quipper/replicas' and eill be used in scaling-out. local kind=$1 local resource=$2 local namespace=$3 local current_replicas current_replicas=$(kubectl get "${kind}" "${resource}" -o=jsonpath='{.spec.replicas}' -n "${namespace}" --context "${KUBECONTEXT}") kubectl annotate --overwrite "${kind}" "${resource}" quipper/replicas="${current_replicas}" -n "${namespace}" --context "${KUBECONTEXT}" & } pr_namespaces=$(kubectl get ns --context "${KUBECONTEXT}" | grep pr- | awk '{print $1}') other_namespaces="release develop" namespaces="${pr_namespaces} ${other_namespaces}" for ns in ${namespaces}; do deployments=$(kubectl get deploy -n "${ns}" --context "${KUBECONTEXT}" | awk 'NR>1{print $1}') for deploy in ${deployments}; do set_current_replicas_to_annotation deploy "${deploy}" "${ns}" kubectl scale --replicas="${REPLICAS}" "deployment/${deploy}" -n "${ns}" --context "${KUBECONTEXT}" & done rollouts=$(kubectl get rollout -n "${ns}" --context "${KUBECONTEXT}" | awk 'NR>1{print $1}') for rollout in ${rollouts}; do set_current_replicas_to_annotation rollout "${rollout}" "${ns}" kubectl patch rollout "${rollout}" --type merge -p "{\"spec\":{\"replicas\":${REPLICAS}}}" -n "${ns}" --context "${KUBECONTEXT}" & done done
ã·ã³ãã«ã§ãããPull Request namespace ã¨å ãã¦ç¹å¥ãª namespace ã対象ã«ãdeployment 㨠rollout ã® replicas ã kubectl scale 㧠0 ã«ãã¦ãã¾ãããã¤ã³ãã¯ç¾å¨ã® replicas ã annotation ã«è¿½å ãã¦ããç¹ã§ããï¼set_current_replicas_to_annotation functionï¼å ã«æ»ãéã«ããã®å¤ãèªã¿åã£ã¦å¾©æ´»ããã¾ãã
ã¹ã±ã¼ã«ã¢ã¦ãã¯ãããªæãã§ãã
#!/bin/bash set -ex : "${KUBECONTEXT}" function get_replicas () { local kind=$1 local resource=$2 local namespace=$3 replicas=$(kubectl get "${kind}" "${resource}" -o=jsonpath='{.metadata.annotations.quipper/replicas}' -n "${namespace}" --context "${KUBECONTEXT}") if [ -z "${replicas}" ]; then echo "1" # If the annotation "quipper/replicas" is not set, return 1 as default. else echo "${replicas}" fi } # If you want to enable only a specific namespace, specify namespace and replicas # If not specified, all pr namespaces are targeted. if [ -z "$pr_namespaces" ]; then pr_namespaces=$(kubectl get ns --context "${KUBECONTEXT}" | grep pr- | awk '{print $1}') other_namespaces="release develop develop-tara edge rdev-1 rdev-2 rdev-3 rdev-4" namespaces="${other_namespaces} ${pr_namespaces}" else namespaces="${pr_namespaces}" fi for ns in ${namespaces}; do deployments=$(kubectl get deploy -n "${ns}" --context "${KUBECONTEXT}" | awk 'NR>1{print $1}') for deploy in ${deployments}; do replicas=$(get_replicas deploy "${deploy}" "${ns}") kubectl scale --replicas="${replicas}" "deployment/${deploy}" -n "${ns}" --context "${KUBECONTEXT}" & done rollouts=$(kubectl get rollout -n "${ns}" --context "${KUBECONTEXT}" | awk 'NR>1{print $1}') for rollout in ${rollouts}; do replicas=$(get_replicas rollout "${rollout}" "${ns}") kubectl patch rollout "${rollout}" --type merge -p "{\"spec\":{\"replicas\":${replicas}}}" -n "${ns}" --context "${KUBECONTEXT}" & done done
ã¹ã±ã¼ã«ã¢ã¦ããè¡ãã¹ã¯ãªããã§ã¯ãpr_namespaces ã¨ããç°å¢å¤æ°ãäºåã«æå®ããã°ããã® namespace ã®ã¿ã¹ã±ã¼ã«ã¢ã¦ãããããã¨ãã§ãã¾ãã
ããã«ãã£ã¦ãæ¥æ¬ã§å¤éã®æéã«ãèªåã使ããã namespace ã ã使ç¨ã§ããæè»æ§ãæã¤ãã¨ãã§ãã¾ããã
å¹æ
ãªãã¨ãããã¨ã§ããããå¤éã¨é±æ«ã¯è¦äºã« Instance æ°ãæ¸ã£ã¦ãã¾ãã
ã³ã¹ã㯠Global å´ã§æé $8800 ãæ¥æ¬å´ã§æé $7000 *9ã®åæ¸ã«æåãã¾ããããã®ãéãããå¹æçãªãã®ã«ä½¿ãã¾ããããããã¼ã
ä»å¾
ç¹å¥ã«ç¶æããã namespace ãã¹ã±ã¼ã«ã¤ã³ããé¤å¤ãã
éçºè ããã®ãªã¯ã¨ã¹ãã¨ãã¦ãè² è·è©¦é¨ã®ããã«ãæ°æ¥ä½¿ãã£ã±ãªãã«ãããã±ã¼ã¹ãããããã§ãã
ããã«å¯¾å¿ããããã«ã¯ãPull Request ã«ç¹å¥ãª Label ãã¤ãã¦ãããããã® namespace ã¯é¤å¤ãããããªå¦çãå ¥ããå¿ è¦ãããã¾ãã
å¤æ´ã®ãªã Application 㯠Deploy ããªã
åè¿°ããããã«ãå Pull Request namespace ã«ã¯ monorepo ã«å«ã¾ãã¦ãããã¹ã¦ã® Application ã Deploy ããã¦ãã¾ããResource Request ãå°éã ã¨ãã¦ãããã®å½±é¿ã¯ããªãæ¯é çã§ãããApplication ãå¢ããã°å¢ããã»ã©ãã®å½±é¿ã¯é¡èã«ãªãã¾ãã
åç´ã« Pull Request ã§ã®ã³ã¼ãå¤æ´æç¡ã§ Deploy ãå¶å¾¡ããã®ã§ããã°ç°¡åã§ãããApplication éã®ä¾åé¢ä¿ãèæ ®ããå¿ è¦ãããã¾ããFrontend Application ãå¤æ´ããã Backend Application ãåããã¦åä½ç¢ºèªãããã§ãããã
ãã¨ã㨠Dependency Graph ãä½æããããã«ãä¾åé¢ä¿ã表ç¾ãã yaml ãä¸å®ã®è¦ç´ã§é ç½®ãã¦ããã®ã§ãããããã¾ãå©ç¨ãããã¨èãã¦ãã¾ãã
namespace ã®çåæéãç縮ãã復活ãç°¡åã«ãã
ç¾å¨ãPull Request namespace ã®çåæéã¯3æ¥ã«è¨å®ããã¦ãã¾ããããã1æ¥ã«ç縮ãããã¨ã§ãããã«ã³ã¹ãã®åæ¸ãè¡ãããã¨èãã¦ãã¾ãããã®ç°å¢ã¯å度 Deploy*10ããã°å¾©æ´»ãã¾ãããå¾ ã¡æéãçºçãã¦ãã¾ãã¾ãã
éçºè ã®çç£æ§ãè½ã¨ããªãããã«ãä¸ç¬ã§å¾©æ´»ã§ããã¹ã¯ãªãããæä¾ãããã¨ã§ãã©ã³ã¹ãåãããã¨èãã¦ãã¾ããåççã«ã¯ CI scripts ã§è¡ã£ã¦ããããã« Image ID ãªã©ããã¤ãã®ç°å¢å¤æ°ãåãè¾¼ã㧠kustomize build ãè¡ããapply ãããã°è¯ãã¯ãã§ãã
ãããã«
ä»åãDeveloper Productivity ããªãã¹ãè½ã¨ããã«ã³ã¹ããåæ¸ãã¾ãããã¤ã³ãã©ã®ã³ã¹ãã¯ãã¸ãã¹ã®ç¶ç¶æ§ã«å½±é¿ãä¸ããéè¦ãªè¦ç´ ã§ããä»å¾ãå®æçã«ã³ã¹ãã®å¤åãã¢ãã¿ãªã³ã°ããåæ¸å¯è½ãªé¨åãæè¡ã§åæ¸ãã¦ããã¾ãã
Quipper ã§ã¯ä¸çã®æã¦ã¾ã§å¦ã³ãå±ããã仲éãåéãã¦ãã¾ãããªããçè ãæå±ãã SRE Team ãåéä¸ã§ããã«ã¸ã¥ã¢ã«é¢è«ãããã¯å¿åãå¾ ã¡ãã¦ãã¾ãã
*1:EKS ã¸ã®ç§»è¡ã«é¢ãã¦ã¯Self-Hosted Cluster ãã EKS ã¸ã®ç§»è¡ã¨ Platform ã® Production Readinessãã覧ãã ãã
*2:ä¸å¤® git ã¬ãã¸ããª
*3:ãã®ä»çµã¿ã«ã¤ãã¦ã¯Kubernetes å°å ¥ã§å®ç¾ãããä¸çã¨ãã®å ã«ãã Microservices / ã¢ãã¬ãã®å°å ¥ãã覧ãã ããã
*4:Cluster åã« "staging" ãå«ã¾ãã¦ãããã®ã jq ã® select ã§ãã£ã«ã¿ãªã³ã°ãã¦ãã¾ãã
*5:Western Indonesian Time (Standard Time)
*6:Philippine Time (Standard Time)
*7:Pacific Daylight Time / Pacific Daylight Saving Time (Daylight Saving Time)
*8:Quipper 㯠Argo Rollouts æ¬çªå°å ¥ä¼æ¥ã§ã
*9:æ¥æ¬å´ã¯10æ17æ¥ããéå§ãã¦ã$3500 åæ¸ãããããæéæç®ã§åç´ã«2åãã¾ãã
*10:CI ã Rerun ãããæ°ã㪠commit ã push ããã°è¯ã