ããã«ã¡ã¯ãã·ã¹ãã ãšã³ãžãã¢ã® Kota ã§ããæ¬èšäºã§ã¯ã ãã³ãºãªã³åœ¢åŒã§ AWS ã« EC2 ãæ§ç¯ããŠãDocker ã install ããHello world! ãšè¡šç€ºãããŠã¿ãããšæããŸãã 察象ã®èªè
éçºç°å¢ã§ Docker ã䜿ã£ãŠãããã©ããããã€ã«ã䜿ãããæ¹ DockerãAWS ã«è§ŠããŠã¿ããæ¹ ã€ã³ãã©ã«èå³ãããæ¹ éå»ã« Docker ã«ã€ããŠã®èšäºãå·çããŠããã®ã§ãåºç€ç¥èã«ã€ããŠã¯ã ãã¡ã ã®èšäºãåç
§ããŠäžããã ãŸãã¯ã宿ã€ã¡ãŒãžãã玹ä»ããŸãã 宿ã€ã¡ãŒãž æé ãšããŠã¯ä»¥äžã®ããã«ãªããŸãã VPCãšãµããããã®æ§ç¯ ã€ã³ã¿ãŒãããã²ãŒããŠã§ã€ïŒã€ã³ã¿ãŒããããžã®åºå
¥å£ïŒãäœæããVPC ã«ã¢ã¿ãããã EC2(ãµãŒããŒ)ãç«ãŠã ã»ãã¥ãªãã£ã°ã«ãŒãïŒãã¡ã€ã¢ãŠã©ãŒã«ïŒãèšå®ãã ã«ãŒãã£ã³ã°ãèšå®ãã SSHã§EC2(ãµãŒããŒ)ã«ã¢ã¯ã»ã¹ãã EC2 ã« Docker ãã€ã³ã¹ããŒã«ãã äœæ¥äžã¯ã宿ã€ã¡ãŒãžãæã£ãŠãããšããçè§£ãæ·±ãŸãããããšæããŸãã ä»åã®äœæ¥ã¯ãAWS ã®ã¢ã«ãŠã³ããå¿
èŠã«ãªããŸãã®ã§ããŸã ãæã¡ã§ãªãæ¹ã¯äžèšã®ãªã³ã¯ããã¢ã«ãŠã³ããäœæããŠãã ããã ç¡æã¢ã«ãŠã³ãäœæ ( 12ã¶æç¡æã§ EC2 ãå©çšã§ããŸããç»é²ã«ã¯ã¯ã¬ãžããã«ãŒããå¿
èŠã§ãã) ã§ã¯ãæ©éãã£ãŠãããŸãããïŒ 1. VPC ãš ãµããããã®æ§ç¯ ãŸãã¯ãVPC ãš ãµããããã®æ§ç¯ã§ãã VPC ãšã¯ãVirtual Private Cloud ã®ç¥ã§ãAWS äžã§ã®èªåã®ãããã¯ãŒã¯ã®é åã®ããšã§ãããµããããã¯ãVPC ã®äžãããã«çްããåå²ããé åã®ããšããããŸããä»åã¯ããã®ãµããããã®äžã«EC2ãç«ãŠããã®ã§ããŸãã¯ããããæ§ç¯ããŠãããŸãã ã€ã¡ãŒãžå³ AWS ã«ãã°ã€ã³ããããããããŒããVPCãæ€çŽ¢ããVPC ããã·ã¥ããŒãã«é·ç§»ããŸãã ãµã€ãããŒããVPC ãéžæããŸãã ãããšãVPCã®äžèЧç»é¢ã«é·ç§»ããŸãã å³äžã®VPCãäœæãã¿ã³ããVPCãäœæããŠãããŸãã äœæç»é¢ã«é·ç§»ãããšãäžèšã®ãããªãã©ãŒã ã衚瀺ãããŸãã ãŸãã¯ãååã¿ã°ã«äœæããVPCã®ååãå
¥ããŠãããŸãã ä»åã¯hello-world-dockerãšããŸãã æ¬¡ã« IPv4 CIDR ãããã¯ã§ãã ããã§ã¯ãIPv4 CIDR ãããã¯ã®è©³ãã説æã¯å²æããŸããã ç°¡åã«èª¬æãããšäœ¿çšããIPã¢ãã¬ã¹ã®ç¯å²ãæå®ããŠããŸãã 192.168.1.0/24 ãšæå®ããããšã§ã192.168.1.0 ~ 192.168.1.255 ãŸã§ã® IPã¢ãã¬ã¹ã䜿çšããããšãå®çŸ©ããŠããŸãã IPv6CIDR ãããã¯ã¯ãªããããã³ã·ãŒã¯ããã©ã«ãã ã¿ã°ã¯ãååã¿ã°ãå
¥åãããšèªåã§å
¥åãããŠããããšæããŸãã å
¥åãå®äºããããå³äžã® VPC ãäœæãã¿ã³ãã¯ãªãã¯ããŸãã äžèšã®ããã«æ£åžžã«äœæãããŸãããšã¡ãã»ãŒãžã衚瀺ãããŸãã äžèЧç»é¢ã«æ»ããšãäœæããVPCã衚瀺ãããŠããã¯ãã§ãã æ¬¡ã«äœæããVPCå
ã®ãµãããããäœæããããããµã€ãããŒãããµãããããéžæããŠäžããã ãµããããã®äžèЧç»é¢ã«é·ç§»ããŸãã®ã§ãå³äžã®ãµãããããäœæãã¿ã³ãããã©ãŒã ã«ç§»åããŸãã VPC ID ã¯å
ã»ã©äœæãã VPC (hello-world-docker) ãéžæããŸãã ãµããããåã¯åãããããããã«ååãã€ããŸãã ä»å㯠hello-world-docker-subnet ãšããŸãã ã¢ãã€ã©ããªãã£ãŸãŒã³ã¯ãã¢ãžã¢ãã·ãã£ãã¯(æ±äº¬) / ap-northaneast-1a ãæå®ããŸãã ã¢ãã€ã©ããªãã£ãŸãŒã³ãšã¯ãAWS ã®ããŒã¿ã»ã³ã¿ãŒã®ããšã§ãã æ¥æ¬ã«ã¯è€æ°ã®ããŒã¿ã»ã³ã¿ãŒãååšããŸããäžè¬çãªãµãŒãã¹ã«ãããŠã¯ãçœå®³ãªã©ã®ãã©ãã«ã«åããã¢ãã€ã©ããªãã£ãŸãŒã³ãåããåé·åãå³ããŸãïŒå¥ã
ã®ããŒã¿ã»ã³ã¿ãŒã«ãããã€ããŠããïŒãåé·åãããããšã§ãäžã€ã®ããŒã¿ã»ã³ã¿ãŒã§ãã©ãã«ãåã£ãå Žåã§ãããµãŒãã¹ãç¶ç¶ããŠæäŸããããšãåºæ¥ãŸããä»åã¯ãããŸã§ã®å¯Ÿå¿ã¯ããŸããã IPv4 CIDR ãããã¯ã¯ã192.168.1.0/25ãã¿ã°ãªãã·ã§ã³ã¯ããµããããåãå
¥åããŠããã°åæ ãããŠããããšæããŸãã å
¥åãæžãã ãããµãããããäœæãã¿ã³ãæŒããŸãã åãã«äœæããVPCå
ã«ãµãããããäœæããããšãåºæ¥ãŸããã 2. ã€ã³ã¿ãŒãããã²ãŒããŠã§ã€ïŒã€ã³ã¿ãŒããããžã®åºå
¥å£ïŒãäœæããVPC ã«ã¢ã¿ãããã æ¬¡ã«ãã€ã³ã¿ãŒãããã²ãŒããŠã§ã€ïŒä»¥éãIGWïŒãäœæããå
ã»ã©æ§ç¯ãã VPC ã«ã¢ã¿ããããŠãããŸãã IGWã¯ãæåéããã€ã³ã¿ãŒããããžã®åºå
¥å£ã«ãªããŸããVPC ã¯ãããã©ã«ãã®ç¶æ
ã ãšãã€ã³ã¿ãŒããããžã®åºå
¥å£ããããŸãããã€ã³ã¿ãŒãããããéé¢ãããããã ã®é åã§ããä»åã¯ãã©ãŠã¶ããEC2 ã«ã¢ã¯ã»ã¹ãããã®ã§ãéé¢ãããVPC ã«åºå
¥å£ãèšããŠãã€ã³ã¿ãŒããããžç¹ããããã«èšå®ããŠãããŸãã ã€ã¡ãŒãžå³ VPCããã·ã¥ããŒãã®ãµã€ãããŒãããIGWãéžæããŸãã éžæãããšãäžèЧç»é¢ã«é·ç§»ããŸãã®ã§ãå³äžã®äœæãã¿ã³ãæŒããŸãã ãã©ãŒã ç»é¢ã«é·ç§»ããŸãã®ã§ãIGWã®ååãå
¥åããŸãã ä»å㯠hello-world-docker-internet-gateway ãšããŸãã å
¥åããããäœæãã¿ã³ãæŒããŸãã ç¡äºäœæã§ããŸããã äžèЧç»é¢ã«è¡ããšå
ã»ã©ã®IGWã衚瀺ãããŠãããšæããŸããæ¬¡ã«ä»äœæãããã®ã 1. ã§äœæããVPCã«ã¢ã¿ããããŠãããããšæããŸãã äžèЧç»é¢ããå
ã»ã©äœæããIGWã®ãã§ãã¯ããã¯ã¹ã«ãã§ãã¯ãå
¥ããå³äžã®ã¢ã¯ã·ã§ã³ãæŒããšVPCã«ã¢ã¿ãããšããé
ç®ã衚瀺ãããã®ã§ãã¯ãªãã¯ããŸãã ãã©ãŒã ç»é¢ã«é·ç§»ããã®ã§ã䜿çšå¯èœãªVPCãã 1. ã§äœæããVPCãéžæããäœæãã¿ã³ãã¯ãªãã¯ããŸãã ããã§ã1. ã® VPC ã«IGWãã¢ã¿ããããããšãã§ããŸããã 3. EC2(ãµãŒããŒ)ãç«ãŠã ã€ã¡ãŒãžå³ IGWã®ã¢ã¿ãããæžãã ããæ¬¡ã« VPC ã®ãµããããå
ã« EC2 ãç«ãŠãŠãããŸãã EC2 ãšã¯ãElastic Compute Cloud ã® ç¥ã§ãAWS äžã§æ§ç¯ã§ããä»®æ³ãµãŒããŒã®ããšã§ãã ãŸãã¯ãããããŒã§EC2ãšæ€çŽ¢ããããã·ã¥ããŒããžé·ç§»ããŸãã ãµã€ãããŒã®ã€ã³ã¹ã¿ã³ã¹ãã¯ãªãã¯ããŸãã ã¡ãªã¿ã«ã€ã³ã¹ã¿ã³ã¹ãšã¯ EC2 ã®åäœã®ããšã§ãã ã€ã³ã¹ã¿ã³ã¹ã®äžèЧç»é¢å³äžã®ã€ã³ã¹ã¿ã³ã¹ãèµ·åãã¯ãªãã¯ããŸãã æåã«AMIãéžæããŸãã AMIãšã¯ãAmazon Machine Images ã® ç¥ã§ãããŒãžã®èª¬æã«ãèšèŒãããéããäœæããã€ã³ã¹ã¿ã³ã¹ã«å¿
èŠãªãœãããŠã§ã¢ãäºããã³ãã¬ãŒããšããŠãçšæããŠãããŠãããã®ã§ãã ä»åã¯äžçªäžã®ç¡ææ ã«ãªã£ãŠãã Amazon Linux 2 AMI (HVM) ãéžæããŸãã æ¬¡ã«ã€ã³ã¹ã¿ã³ã¹ã¿ã€ãã®éžæã§ãã ãã¡ãã¯ã€ã³ã¹ã¿ã³ã¹ã®CPUãã¡ã¢ãªãã¹ãã¬ãŒãžããªã©ã®ã¹ããã¯ãéžæããã¹ãããã§ãããã¡ãã¯ç¡ææ ã«ãªã£ãŠãã t2.micro ãéžæããŸãã ãã§ãã¯ãå
¥ãããå³äžã®æ¬¡ã®ã¹ããããã¯ãªãã¯ããŸãã æ¬¡ã«ã€ã³ã¹ã¿ã³ã¹ã®è©³çްèšå®ã§ãã 现ããèšå®ã§ããŸãããä»åèšå®ããã®ã¯ãèµ€æ ã®éšåã®ã¿èšå®ããŠãããŸãã ãããã¯ãŒã¯ã¯ 1. ã§äœæãã VPC ãéžæããŸãã ãµããããã¯ãVPCå
ã«äžã€ããäœæããŠããªãã®ã§ãVPCãéžæããæç¹ã§ãèªåã§éžæãããŠããããšæããŸãã èªåå²ãåœãŠãããªãã¯IP ã¯æå¹ã«ããŠäžããããã¡ãã®èšå®ãæå¹ã«ããããšã§ãAWS ã®é åããå€ã®ã€ã³ã¿ãŒããããžã¢ã¯ã»ã¹ããããšãã§ããŸãã èšå®ãå®äºããããå³äžã®æ¬¡ã®ã¹ããããã¯ãªãã¯ããŸãã ã¹ãã¬ãŒãžã®è¿œå ã«é¢ããŠã¯ãä»åç¹ã«èšå®ããå¿
èŠããªãã®ã§ããã®ãŸãŸå³äžã®æ¬¡ã®ã¹ããããã¯ãªãã¯ããŸãã ã¿ã°ã®è¿œå ã§ã¯ãEC2 ã«ååãã€ããããã®ã§ãåãããããããã«èšå®ããŠãããŸãã å·Šäžã®ã¿ã°ã®è¿œå ãã¯ãªãã¯ããŸãã ããŒã« Nameãå€ã« hello-world-docker-ec2 ãšå
¥åããŠãããŸãã å
¥åããããæ¬¡ã®ã¹ããããã¯ãªãã¯ããŸãã ãã®ãŸãŸæ¬¡ã®ã»ãã¥ãªãã£ã°ã«ãŒãã®èšå®ãããŠãããŸãã 4. ã»ãã¥ãªãã£ã°ã«ãŒãïŒãã¡ã€ã¢ãŠã©ãŒã«ïŒãèšå®ãã ã€ã¡ãŒãžå³ ããã§ã¯ã»ãã¥ãªãã£ã°ã«ãŒãã®èšå®ãè¡ã£ãŠãããŸãã ã»ãã¥ãªãã£ã°ã«ãŒããšã¯ããããããã¡ã€ã¢ãŠã©ãŒã«ã®ããšã§ãAWSã§ã®åç§°ã§ãããã¡ãã§ã€ã³ã¿ãŒãããïŒAWSã®å€åŽïŒããã®éä¿¡ãèš±å¯ããããããŒãã®èšå®ïŒSSH ã® 22 çªãšãHTTP ã® 80 çªïŒãããŠãããŸãã ãŸããã»ãã¥ãªãã£ã°ã«ãŒãã®å²ãåœãŠã¯ãæ°ããã»ãã¥ãªãã£ã°ã«ãŒããäœæããã«ãã§ãã¯ãå
¥ããŸãããããŠãã»ãã¥ãªãã£ã°ã«ãŒãåã«ã¯åãããããååãä»ããŸãã ä»åã¯ãhello-world-docker-ec2-security-group ãšããŠãããŸãã 説æã«ã¯ã»ãã¥ãªãã£ã°ã«ãŒãã®èª¬æãå
¥åã§ããŸããïŒè±åã®ã¿ïŒ ããã©ã«ãã§èšå®ãããŠãã SSH ã¯ããã®åŸããã¹ããã EC2 ãž SSH æ¥ç¶ãããEC2 ã« Docker ã install ããçºã«ãã®ãŸãŸæ®ããŠãããŸãã æ¬¡ã« http æ¥ç¶ããçºã®èšå®ãããçºãã«ãŒã«ã®è¿œå ãã¯ãªãã¯ããŸãã ã¿ã€ãã¯ãã«ã¹ã¿ã TCPããããã³ã«ã¯ãã®ãŸãŸTCPãããŒãç¯å²ã¯ 80 ããœãŒã¹ã¯ã«ã¹ã¿ã ã® 0.0.0.0/0 ãæå®ããŸãã 0.0.0.0/0 ãšæå®ããããšã§ãå
šãŠã® IP ã¢ãã¬ã¹ããã®ã€ã³ã¹ã¿ã³ã¹ã«ã¢ã¯ã»ã¹ããããšãèš±å¯ãããŸãã æ³šæ ä»åã¯ãããŸã§ãå®éšçšãšã㊠http ã§ port ãèšå®ããŠããŸãããå®éçšãšããŠäœ¿ãå Žåã¯ã https åããŠäžããã èšå®ãå®äºããããå³äžã®ç¢ºèªãšäœæãã¿ã³ãã¯ãªãã¯ããŸãã ã¯ãªãã¯ãããšã ã»ãã¥ãªãã£ã°ã«ãŒã hello-world-docker-ec2-security-group ã¯äžçã«åããŠéãããŠããŸãã ãšããã¡ãã»ãŒãžãšå
ã»ã©ãŸã§èšå®ããŠããå
容ã衚瀺ãããŠããããšæããŸãã å
容ã確èªããããå³äžã®èµ·åãã¿ã³ãã¯ãªãã¯ããŸãã ã¯ãªãã¯ãããšãããŒãã¢éžæãäœæã®ã¢ãŒãã«ã衚瀺ãããŸãã ä»åã¯æ°ããããŒãã¢ãäœæããŠãããŸãããã æ°ããããŒãã¢ã®äœæãéžæããRSAã¿ã€ãã«ãã§ãã¯ãå
¥ããŸãã ããŒãã¢åã¯åãããããããã«ååãã€ããŸãã ä»å㯠hello-world-docker ãšããŸããã å
¥åãæžãã ããããŒãã¢ã®ããŠã³ããŒããã¯ãªãã¯ããŸãã ãã¡ãã¯ãEC2 ã« SSH æ¥ç¶ã«å¿
èŠãªç§å¯éµã«ãªããŸãã®ã§ãå¿
ãããŠã³ããŒããããŠãã ããããŸãããã®ç§å¯éµã¯ãäžåºŠããããŠã³ããŒãã§ããªãçºã倧åã«ä¿ç®¡ããŠäžãããmac ã®å Žåãããã©ã«ãã ãšã~/Downloads ã«ããŠã³ããŒããããããšæããŸããããŠã³ããŒããå®äºãããã .ssh ãã©ã«ããäœæãããã¡ãã«ç§»åãããŠãããŸãããã ã¿ãŒããã«ãéããŠãäžèšã®ã³ãã³ããå®è¡ããŸãã ~ $ mkdir .ssh ~ $ mv ~/Downloads/hello-world-docker.pem ~/.ssh ãããŠãç§å¯éµã®ããŒããã·ã§ã³ãäžèšã®ã³ãã³ãã§å€æŽããŸãã $ chmod 600 ~/.ssh/hello-world-docker.pem äžèšãå®äºãããããã©ãŠã¶ã«æ»ã£ãŠã€ã³ã¹ã¿ã³ã¹ã®äœæãã¯ãªãã¯ããŸãã äžèšã®ç»é¢ã衚瀺ãããã€ã³ã¹ã¿ã³ã¹ã®äœæãé²è¡ããŸãã äžèЧç»é¢ã«æ»ããã¹ããŒã¿ã¹ãã§ãã¯ã®æ¬ãèŠããšãåæåããŠããŸããšè¡šç€ºãããŠãããšæããŸãããã°ããçµã£ãŠãããäžéšã®æŽæ°ãã¿ã³ãæŒãããã§ãã¯ã«åæ ŒããŸãããšè¡šç€ºãå€ããã°ãäœæå®äºã§ãã â ãã°ããããŠãæŽæ°ãã¿ã³ãæŒããšã ããã§ EC2 ã®äœæãšãã»ãã¥ãªãã£ã°ã«ãŒãã®èšå®ãå®äºã§ãã 5. ã«ãŒãã£ã³ã°ãèšå®ãã ããŠãäœæ¥ãçµç€ã«å
¥ã£ãŠããŸãããæ¬¡ã«ã«ãŒãã£ã³ã°ã®èšå®ãããŠãããŸãã ã€ã¡ãŒãžå³ å
ã»ã©ãç«ãŠã EC2 ã«å¯ŸããŠãéä¿¡å
ã®å®çŸ©ãããŠãããŸããç¹°ãè¿ãã«ãªããŸãããä»åã¯ãã©ãŠã¶ããEC2ã«ã¢ã¯ã»ã¹ã㊠Hello world! ãšè¡šç€ºãããããšïŒã¬ã¹ãã³ã¹ãè¿ãããšïŒãç®æšãªã®ã§ãïŒEC2åŽããïŒã€ã³ã¿ãŒããããžéä¿¡ãåããããã«èšå®ãããŠãããŸãã ãŸãã¯ãVPCã®ããã·ã¥ããŒãã«æ»ããŸãããã ããããŒã®ãµãŒãã¹ãã¯ãªãã¯ãããšãä»ãŸã§äœæ¥ãè¡ã£ãããã·ã¥ããŒãã®å±¥æŽã衚瀺ãããŸãã®ã§ãVPC ãã¯ãªãã¯ããŠããã·ã¥ããŒããžæ»ããŸãã ãµã€ãããŒã®ã«ãŒãããŒãã«ãã¯ãªãã¯ããŸãã äžèЧç»é¢ãžé·ç§»ããŸãã®ã§ãå³äžã®äœæãã¿ã³ãã¯ãªãã¯ããŸãã ãã©ãŒã ã«é·ç§»ããŸãã®ã§ã ããŒãã«ã®ååãå
¥åããŸãã ä»åãåãããããããã« hello-world-docker-route ãšããŸãã VPC ã«ã¯ãäœæããã«ãŒãããŒãã«ãé©çšããã VPC ãéžæããŸãã ä»åã¯ãhello-world-docker ã§ãã å
¥åãå®äºããããäœæãã¿ã³ãã¯ãªãã¯ããŸãã äœæãå®äºããããäžèЧç»é¢ãžæ»ããŸãã äœæããã«ãŒãããŒãã«ã«ãã§ãã¯ãå
¥ãããšã詳现ã衚瀺ãããŸãã ã«ãŒãã¿ããæŒããšã«ãŒããç·šéãšãããã¿ã³ã衚瀺ãããŸãã®ã§ããã¡ããã¯ãªãã¯ããŸãã ãã©ãŒã ç»é¢ã«é·ç§»ããŸãã®ã§ãã«ãŒãã远å ãã¯ãªãã¯ããŸãã éä¿¡å
ã¯ãå
šãŠã® ip ã¢ãã¬ã¹ã«å¯ŸããŠãéä¿¡ãè¿ãããã®ã§ã0.0.0.0/0 ãšèšå®ããŸãã ã¿ãŒã²ããã§ã¯ã2. ã§äœæãã IGW ãéžæããŸãã å
¥åãå®äºãããã倿Žãä¿åããŸãã 倿Žãä¿åãããããäžèšã®ç»é¢ã衚瀺ãããŸãã®ã§ããã®ãŸãŸãµããããã®é¢é£ä»ããããŠãããŸãããµããããã®é¢é£ä»ãã¿ããã¯ãªãã¯ãããµããããã®é¢é£ä»ããç·šéãã¯ãªãã¯ããŸãã ããã§ã¯ãäœæããã«ãŒãããŒãã«ãšé¢é£ä»ããVPCå
ã®ã©ã®ãµãããããšé¢é£ä»ããããèšå®ããŸãã ä»åã¯VPCå
ã«äžã€ãããµãããããäœæããŠããŸããã®ã§ã衚瀺ãããŠããhello-world-docker-subnet ã«ãã§ãã¯ãå
¥ããé¢é£ä»ããä¿åããŸãã ããã§äžéãã®ã«ãŒãã£ã³ã°ã®èšå®ãå®äºããŸããã 6. SSH ã§ EC2 ã« ã¢ã¯ã»ã¹ãã å
ã»ã©ãŸã§ã®ã¹ãããã§ãAWS ã®ã³ã³ãœãŒã«ã§è¡ãäœæ¥ã¯å
šãŠå®äºããŸããã ããã§ã¯ããæå
ã®PCããæ§ç¯ãã EC2 ã« SSH æ¥ç¶ããããšæããŸãã ãšããã®åã«å
ã»ã©æ§ç¯ãã EC2 ã® IP ã¢ãã¬ã¹ã確èªãããã®ã§ãããäžåºŠãEC2 ã®ããã·ã¥ããŒãã«ã¢ã¯ã»ã¹ããŸãã äžèЧç»é¢ã§ hello-world-docker-ec2 ã«ãã§ãã¯ãå
¥ãããšã詳现ã衚瀺ãããŸãã èµ€æ ã®ãããªã㯠IPv4 ã¢ãã¬ã¹ãæ§ããŠãããŸãã æ¬¡ã«ã¿ãŒããã«ãéããŸãã å
ã»ã©ããŠã³ããŒãããç§å¯éµã䜿ã£ãŠãEC2 ã« SSH æ¥ç¶ããŸãã $ ssh -i ~/.ssh/hello-world-docker.pem ec2-user@13.113.91.85 -i [ç§å¯éµã®path] ã§ãã®ç§å¯éµã䜿ã£ãŠ SSH æ¥ç¶ãããŸãã ä»åãEC2ãäœæããã«ããã£ãŠ Amazon Linux 2 AMI (HVM) ã䜿ããŸãããAmazon Linux ã®å ŽåãSSH ã§æ¥ç¶ããããã©ã«ãã®ãŠãŒã¶ãŒã¯ ec2-user ãªã®ã§ããã¡ããæå®ããŠããŸãããããŠã@ã«ç¶ããŠãå
ã»ã©æ§ããŠããã IP ã¢ãã¬ã¹ãæå®ããŸãã åããŠæ¥ç¶ããéããã®ãŸãŸæ¥ç¶ãç¶ãããèãããŸãã®ã§ããã®é㯠yes ãå
¥åããŠäžããã ã³ãã³ããå®è¡ããŠäžèšã®ããã«è¡šç€ºãããã°ãæ¥ç¶æåã§ãã 7. EC2 ã« Docker ã install ãã ã€ã¡ãŒãžå³ åã®ã¹ãããã§ãEC2 ã« SSH æ¥ç¶ããããŸãŸã®ç¶æ
ã«ãªã£ãŠããããšæããŸãã®ã§ããã®ãŸãŸ EC2 ã« Docker ã install ããŠãããããšæããŸãã ãŸãã¯ãyum ã® update ãè¡ããŸãã yum ãšã¯ãLinuxã®ããã±ãŒãžç®¡çããŒã«ã®ããšã§ãã EC2ã€ã³ã¹ã¿ã³ã¹ã« install ãããŠããããã±ãŒãžã update ããçºãäžèšã®ã³ãã³ããå®è¡ããŸãã [ec2-user@ip-192-168-1-4 ~]$ sudo yum -y update 次ã«ãDocker ã install ããŸãã äžèšã®ã³ãã³ããå®è¡ããŸãã [ec2-user@ip-192-168-1-4 ~]$ sudo yum -y install docker install ãå®äºããããDocker ãèµ·åããŸãã äžèšã®ã³ãã³ããå®è¡ããŸãã [ec2-user@ip-192-168-1-4 ~]$ sudo service docker start sudo docker info ãšå®è¡ããäžèšã®ãããªå
容ã衚瀺ãããã°ãèµ·åã«æåããŠããŸãã [ec2-user@ip-192-168-1-4 ~]$ sudo docker info Client: Context: default Debug Mode: false Server: Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 0 Server Version: 20.10.7 Storage Driver: overlay2 Backing Filesystem: xfs Supports d_type: true Native Overlay Diff: true userxattr: false Logging Driver: json-file Cgroup Driver: cgroupfs Cgroup Version: 1 Plugins: Volume: local Network: bridge host ipvlan macvlan null overlay Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog Swarm: inactive Runtimes: io.containerd.runc.v2 io.containerd.runtime.v1.linux runc Default Runtime: runc Init Binary: docker-init containerd version: d71fcd7d8303cbf684402823e425e9dd2e99285d runc version: 84113eef6fc27af1b01b3181f31bbaf708715301 init version: de40ad0 Security Options: seccomp Profile: default Kernel Version: 5.10.96-90.460.amzn2.x86_64 Operating System: Amazon Linux 2 OSType: linux Architecture: x86_64 CPUs: 1 Total Memory: 965.5MiB Name: ip-192-168-1-4.ap-northeast-1.compute.internal ID: T3TS:4QOT:KB35:2USQ:JDUZ:WFIZ:DRXS:43VU:3XPG:I2DZ:K4UU:6GB4 Docker Root Dir: /var/lib/docker Debug Mode: false Registry: https://index.docker.io/v1/ Labels: Experimental: false Insecure Registries: 127.0.0.0/8 Live Restore Enabled: false ä»ã®ãŸãŸã ãšãdocker ã³ãã³ããæã€éã«ã sudo ãä»ããªããã°ãªããŸããã ããã¯ãec2-user ã«docker ã³ãã³ãã®æäœæš©éããªãçºã§ãã ec2-user ã«æäœæš©éãäžããçºã以äžã®ã³ãã³ããå®è¡ããŸãã sudo usermod -a -G docker ec2-user äžèšã®ã³ãã³ãã¯ãæäœæš©éãã〠docker ã°ã«ãŒãã«ãec2-user ãå ããã³ãã³ãã§ãã詳ããç¥ãããæ¹ã¯ã å
¬åŒããã¥ã¡ã³ã ãã ãã¡ã ã®èšäºãåç
§ããŠäžããã ã°ã«ãŒãã®è¿œå ã¯ãã·ã§ã«ãèµ·åãçŽããªããšåæ ãããªãã®ã§ãäžåºŠ exit ã§ãµãŒããŒãããã°ã¢ãŠããããã°ã€ã³ãçŽããŠäžããã sudo ãªãã§ã docker info ãšå®è¡ããå
ã»ã©ãšåãå
容ã®åºåããããã°ãã°ã«ãŒããžã®è¿œå ãæåããŠããŸãã ã°ã«ãŒããžã®è¿œå ãã§ããããäœæ¥çšã®ãã£ã¬ã¯ããªãäœæãããã£ã¬ã¯ããªå
ã«ç§»åããŸãã [ec2-user@ip-192-168-1-53]$ mkdir hello-world-docker [ec2-user@ip-192-168-1-53]$ cd hello-world-docker/ ãããŠãHello world! ãšè¡šç€ºãããçºã® html ãã¡ã€ã«ãçšæããŸãã [ec2-user@ip-192-168-1-53 hello-world-docker]$ vi hello-world.html vi ãéããŸãã®ã§ã i ã§ insert mode ã«ãã Hello world! ãšèšè¿°ããŠãesc ããŒã :wq ã§ä¿åããŸãã ls ã³ãã³ãã§ãã£ã¬ã¯ããªã«ä»äœæãã html ãã¡ã€ã«ãäœæãããŠãããšæããŸãã [ec2-user@ip-192-168-1-53 hello-world-docker]$ ls hello-world.html 次ã«Dockerimage ãäœæããçºã«ãDockerfile ãäœæãç·šéããŠãããŸãã ä»åã¯ãwebãµãŒããŒã« nginx ã䜿çšããŸããå
ã»ã©äœæãããhtml ãã¡ã€ã«ã nginx äžã§è¡šç€ºãããŸãã [ec2-user@ip-192-168-1-53 hello-world-docker]$ vi Dockerfile Dockerfile FROM nginx COPY ./hello-world.html /usr/share/nginx/html/ nginx ã§ã¯ãããã©ã«ãã®ç¶æ
ã ãš /usr/share/nginx/html/ é
äžããã©ãŠã¶ããã¢ã¯ã»ã¹ããéã®åæè¡šç€ºã«ãªã£ãŠããã®ã§ã ./hello-world.html ãã³ããŒããŸãã ç·šéåºæ¥ãããå
ã»ã©ãšåãããã«ä¿åããŠäžããã ãããŠãäœæããDockerfile ãå
ã« image ã build ããŸãã äžèšã®ã³ãã³ããå®è¡ããŸãã [ec2-user@ip-192-168-1-53 hello-world-docker]$ docker build -t hello-world-docker-ec2 . build ãå®äºããããcontainer ãèµ·åããŸãã äžèšã®ã³ãã³ããå®è¡ããŠäžããã [ec2-user@ip-192-168-1-53 hello-world-docker]$ docker run --rm -d -p 80:80 hello-world-docker-ec2 ä»åã¯ãããŸã§å®éšãšããŠäœæ¥ãããŠããã®ã§ã --rm ãªãã·ã§ã³ãã€ããŠã³ã³ããçµäºæã«åé€ããŠããŸãããæ¬çªãæ³å®ããå Žåã¯ããªãã·ã§ã³ãä»äžããªããŠãè¯ãããç¥ããŸããã docker ps ã³ãã³ãã§ã³ã³ããã® STATUS ã UP ã«ãªã£ãŠããã°ãèµ·åã«æåããŠããŸãã [ec2-user@ip-192-168-1-53 hello-world-docker]$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 0742571c5bf3 hello-world-docker-ec2 "/docker-entrypoint.âŠ" 14 seconds ago Up 12 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp romantic_greider ãããŸã§ã§ãå
šãŠã®äœæ¥ãå®äºã§ãã ããã§ã¯ãã©ãŠã¶ããã¢ã¯ã»ã¹ããŠã¿ãŸãããã æ§ããŠããã IP ã¢ãã¬ã¹ / hello-world.html ã§ã¢ã¯ã»ã¹ããŸãã ç¡äºã« Hello world! ãšè¡šç€ºãããããšãã§ããŸããïŒ ããŠãåŠäœã ã£ãã§ããããïŒ ä»åã¯ãAWS ã«ãEC2 ãç«ãŠããã®äžã§ãDocker ã³ã³ãããèµ·åããããã©ãŠã¶ããã¢ã¯ã»ã¹ããããšããã³ãºãªã³åœ¢åŒã§ãã£ãŠã¿ãŸãããç§èªèº«ãã€ã³ãã©ã«é¢ããŠããŸã ãŸã å匷äžã§ä»åã®å
容ã¯ãããªãåæ©çãªå
容ã ã£ãããšæããŸããæ¬¡ã®æ©äŒã«ã¯ãããå°ãçºå±ããå
å®¹ãæžããŠã¿ãããšæããŸãã æåŸãŸã§èªãã§é ããããããšãããããŸããïŒ
ã¿ãªããããã«ã¡ã¯ïŒWESEEK æŠç¥äŒç»å®€ ã³ãã¥ããã£ãŒãããŒãžã£ãŒã®å€ªç°ã§ãã ååã®ã GROWI Users Meetupãéå¬ãŸã§ã®éã®ã åç·š ãã«åŒãç¶ããä»åã GROWI Users Meetup ã«ã€ããŠãäž»å¬è
åŽã»ã³ãã¥ããã£ãŒãããŒãžã£ãŒç®ç·ã§èªã£ãŠãããã°ãšæã£ãŠããŸãã åç·šããŸã èªãã§ããªããïŒãšããæ¹ãããã£ããã£ããããã²ãã¡ããèªãã§ããåŸç·šããæ¥œãã¿ããã ããã°ãšæããŸãã ãŠãŒã¶ãŒäŒéå¬ãŸã§ã®æ©ã¿ ïœå€ããå¬ïœ éå¬ã«åŸ
ã£ãïŒããããã®ã¯âŠ æ¥æ¬ããªãªã³ããã¯ã®éå¬ã«ãœã¯ãœã¯ãå§ãã7æé ã Slack ã®ãã£ã³ãã«ãæŽåã§ããããã€ãã³ãã®é²è¡æ¡ãåºãã€ã€ããããååè
ãåéããããªïœãšæã£ãŠããç¢å
ãããã«åŸ
ã£ãïŒããããã®ã¯åœæéæéçºäžã ã£ã GROWI Bot ã§ããã GROWI Botãšã¯ãWESEEK ãæäŸãã GROWI ã®æäœãæ¯æŽããSlack Appã§ããSlack ãš GROWI ã飿ºããããšã§ãããã·ãŒã ã¬ã¹ãªæç« äœæã»å
±æãå¯èœãšãªããŸãã 詳现ã«ã€ããŸããŠã¯ã Slackãšç€Ÿå
wikiã飿ºãããGROWI Botãšã¯ïŒ ã GROWI Docs / Slack飿º ãã芧ãã ããã GROWI Botã®é
åãåºããŠãŒã¶ãŒã®çããã«ãäŒãããããã«ããã æåã® GROWI Users Meetup ã¯ã GROWI Bot ã®ãªãªãŒã¹ãèšå¿µããåã«ãããïŒ ããšããæ¹éãæ±ºå®ã åœæã®è°äºé² GROWI Botã®ãªãªãŒã¹ãåŸ
ã£ãŠ9æåŸåïœ10æãããã«ã¯ãŠãŒã¶ãŒäŒãããããããšåœæã®è°äºé²ã«ã¯èšãããŠãããŸããã ããããGROWI Botã®éçºãäºæ³ãããé
ããŠããŸã£ãããšã§ãGROWI Users Meetupã®éå¬ãã©ãã©ãå
å»¶ã°ãã«âŠã æ°ã¥ãã°å£ç¯ã¯ç§ããå¬ã«å€ãã£ãŠãã£ãã®ã§ãã ã³ãã¥ããã£ãŒãããŒãžã£ãŒãšããŠãŠãŒã¶ãŒäŒãé²ããŠãããªããïŒãšããçŠããšã¯è£è
¹ã«ããªããªãçµãããªãGROWI Botã®éçºã GROWI æè°ã®åºŠã«ããã€é ãªãªãŒã¹ã§ããŸããïŒããšèãããšã«ãªã£ãŠããŸããéçºããŒã ã®çããã«ã¯ã¡ãã£ãšç³ãèš³ãªãã£ãã§ãã ããããããªãªãŒã¹ã§ãããããããã°ãããããããããäœåãèããŸããã GROWI BotãªãªãŒã¹ïŒ ãããŠæšå¹Žã®12æãéã«GROWI Botãæ£åŒã«ãªãªãŒã¹ãããŸããïŒ GROWI Botã®æ©èœã®äžã€ãSlack äžã§ã®GROWIå
šææ€çŽ¢æ©èœ ã¡ãªã¿ã«ãGROWI Botãéçºãã¹ã¿ãŒãããŠããç¡äºãªãªãŒã¹ããããŸã§ã«ã¯ããªïœãã®çŽäœæ²æããã£ãã®ã§ããâŠã ãã®èšé²ã¯ãæ¥é±éå¬ãããWESEEK Tech Conferenceã Slack瀟ã«ãåŽäžããã倿
çSlackbotã®äœãæ¹ ãã§çºè¡šãããããã§ããäžäœGROWI Botã®éçºã«äœãèµ·ããã®ããèå³ããæ¹ã¯æ¯éãåå ãã ããã æ¥ãããã§æºå GROWI Botã®ãªãªãŒã¹ãšåæã« ãã¬ã¹ãªãªãŒã¹ ãå
¬éããããŠãã£ãšæž©ããŠãã GROWI Users Meetup ã®éå¬ãåç¥ããããšã«ã ã¡ãªã¿ã«ãGROWI Users Meetup ã®éå¬äºå®æ¥ã¯ 12æ20æ¥ ã ã£ãã®ã§ãããæåã®åç¥ãè¡ã£ãã®ã¯ãªããš 12æ3æ¥ ïŒ æ¬çªãŸã§æ®ãçŽ2é±éãããªãäžãæ¥ãããã§æºåãé²ããªããŠã¯ãããŸããã ç§äžäººã§ã¯å°åºæºåã§ããªãã®ã§ãGROWIéçºã¡ã³ããŒã®äžããæ
åœè
ãæ±ºããŠåœæ¥ãŸã§ã®æºåãé²ããŠãããŸããã å·Šãã宮沢ããã»å¢å±±ããã»äžæŸãã 第1å GROWI Users Meetupã«ãååããã ããã®ã¯ãã€ã³ã¿ãŒã³çã®å®®æ²¢ããã»å¢å±±ããããããŠç€Ÿå¡ã®äžæŸããïŒ ããªãã¿ã€ããªã¹ã±ãžã¥ãŒã«ã§ã¯ãããŸããããçããæ¥ã
ã®æ¥åã®åãã¹ã©ã€ãäœæãçºè¡šåçš¿ã®äœæããªããŒãµã«ãªã©ã«ååããŠããã ããŸãããããããšãããããŸãã GROWI Users Meetupåœæ¥ã®æ§å ä»åã®GROWI Users Meetupã¯ããã®ãããªããã°ã©ã ã§ãå±ãããŸããã æè¿ã®GROWIã®ã¢ããããŒãïŒä»åŸã®éçºäºå®çŽ¹ä» (宮沢) éçºè
ãèªããGROWI Botã®é
å (å¢å±±) ãªãŒãã³ GROWI æè° (äžæŸ) åœæ¥ã¯çºè¡šè
åŽã®æ§åã Zoom ã§é
ä¿¡ãã€ã€ãåå è
ã®çãããšã¯ Slack ã§äº€æµãè¡ããŸããã çºè¡šãžã®ãªã¢ã¯ã·ã§ã³ãããã³ããçåç¹ãªã©ãªã©ãWESEEKããã®çºè¡šäžããšãŠã掻çºã«ã³ãã¥ãã±ãŒã·ã§ã³ããªãããŠããŸããïŒ GROWI Bot ã®ç޹ä»ã§ã¯å®éã« Slack äžã§Botã®æäœãè¡ããæ€çŽ¢çµæã®å
±æã unfurl ã®è¡šç€ºãå®éã«åå è
ã®æ¹ã«èŠãŠããã ããªããçºè¡šãè¡ããŸããã åœæ¥ã®é
ä¿¡ç»é¢ãã³ãã¥ãã±ãŒã·ã§ã³ã¯Slackã§ïŒ æåŸã®ããã°ã©ã ã§ãã GROWI ãªãŒãã³æè° ã§ã¯ãå®è£
æ€èšäžã®PDFãã¥ãŒã¯ãŒãšã·ã§ãŒãã«ããããŒã«ã€ããŠããŠãŒã¶ãŒã®æ¹ã亀ããŠè°è«ãè¡ããŸããã åœæ¥ã®è°äºé²ã¯ ãã¡ã ããé²èЧã§ããŸãïŒ ååå ã®ãŠãŒã¶ãŒãããäŒè°ã§çºèšãããã®ã¯ã¡ãã£ãšããŒãã«ãé«ãããâŠãšããããšã§ãæ°è»œã«æèŠã衚æã§ãããã PDFãã¥ãŒã¯ãŒã®ãã¶ã€ã³ã«ã€ã㊠Slack äžã§ã¢ã³ã±ãŒãã宿œïŒ æ€èšäžã®PDFãã¬ãã¥ãŒæ©èœã«ã€ããŠãã¢ã³ã±ãŒãã宿œããŸãã ããäžã€ã®ã·ã§ãŒãã«ããããŒã®è©±é¡ã§ã¯ããã£ããå¬ããã·ã§ãŒãã«ããããŒã«ã€ããŠçããããæ§ã
ãªèŠæãããã ããŸããã ãŠãŒã¶ãŒããããããã ããèŠæãå
ã«ãGROWI äžã§ / ãå
¥åãããšæ€çŽ¢çªã«ãã©ãŒã«ã¹ãããã·ã§ãŒãã«ããããŒããŠãŒã¶ãŒäŒã®4æ¥åŸã«æ©é远å ãããŸããïŒ https://github.com/weseek/growi/pull/5006 GROWI Users Meetup ãçµã㊠ãšããããšã§ãæ§æ³ããçŽ1幎ïŒã€ãã«ç¬¬1å GROWI Users Meetupãéå¬ããããšãã§ããŸããã GROWI Users MeetupãéããŠãWESEEK ã®é¢ä¿è
ã¯ãã¡ããããŠãŒã¶ãŒããããã GROWI ã«é¢ããã³ã¡ã³ããããã ããããã¢ã³ã±ãŒãã§è²ŽéãªãæèŠãããã ãããšãã§ããŸããã GROWIã®éçºãè¡ã£ãŠãããšã³ãžãã¢ã®çãããããããŠãŒã¶ãŒã®åå¿ãèŠãããšãã§ããŠæ¥œããã£ãããä»åŸã®éçºãžã®ææ¬²ã沞ããããšããææ³ãããã ããŸããã ãã®ä»ãGROWI Users MeetupãéããŠGROWI éçºé£ã®ããŒã æèã«ãå€åããã£ãããã§ãïŒ ä»£è¡šã®æŠäºããããã³ã¡ã³ããããã ããŸããã æ¥åžžã§ã®ããŒã ã®æèã«ãå€åãèœçããã®ã¯å¯æ¬¡çãªåç©«ã§ããã GROWI ã®éçºã¯ã¢ãžã£ã€ã«ã§ãã£ãŠãããšã¯ãããåèšéçºã®ããã«çŽæããããã®ã§ã¯ãªãã®ã§ã©ãããŠãããŒã ã®ã¹ããªã³ãæèãåžèã«ãªã£ãŠããŸããšãã課é¡ã¯ä»¥åãããããŸããããã£ã±ã远ãç«ãŠããããã®ããªããšäººéæ¥ããªããã§ããã⊠ä»åã® Meetup ãã¹ã±ãžã¥ãŒã«ãããããšã§ãããçš®ã®ææçºè¡šæéãã¡ã³ããŒäžäººäžäººãèšå®ããããã«åããŠç²Ÿåçã«åãçµãå¢ããçãŸããŸããã èªåãã¡ã欲ãããããã¯ããèªåãã¡ã§äœããããã«å
±æããŠããããŠãŒã¶ãŒããã«å±ãããšããåç¹ã®æèããUsers Meetup ãããããšã§å確èªã§ããããããªãããªãšæããŸãã ä»åŸã®äºå® ä»åŸã1ãæã«1åçšåºŠã®éå¬ãç®æããŠãGROWI Users MeetupãéããŠãŠãŒã¶ãŒãããšã®äº€æµãåºããŠãããããšæã£ãŠããŸãã æ¬¡åã®é嬿¥ã¯ãŸã æªå®ãªã®ã§ããããã²çããã ãã¡ã ãã GROWI ã® Slack ã¯ãŒã¯ã¹ããŒã¹ã«åå ããŠãGROWI Users Meetupã®ææ°æ
å ±ããã§ãã¯ããŠã¿ãŠãã ããïŒ
GROWIéçºããŒã ã®çãã ã¿ãªããããã«ã¡ã¯ïŒWESEEKæŠç¥äŒç»å®€ / ã³ãã¥ããã£ãŒãããŒãžã£ãŒã®å€ªç°ã§ãã æ®æ®µã¯ WESEEK Blog ã®æ¹ã§ç€Ÿå
ã€ãã³ãã瀟å
æœçã«ã€ããŠçºä¿¡ãããŠããŸãããä»ååã㊠Tech Blog ã«ãéªéãããŠããã ããŸããïŒ ããŠããšã³ãžãã¢ã§ã¯ãªãç§ãã©ãã㊠Tech Blog ãæŽæ°ããŠããããšèšããŸããšãå
æ¥éå¬ãããŸãã GROWI Users Meetup ã«ã€ããŠãéå¶ã«æºãã£ãã³ãã¥ããã£ãŒãããŒãžã£ãŒã®ç®ç·ã§ç¶Žã£ãŠã¿ãããšæãç«ã£ãããã§ãã ãGROWI Users Meetupãéå¬ãŸã§ã®éã®ãã ãšé¡ããŸããŠãåç·šã»åŸç·šã«åããŠã€ãã³ãã®è£åŽããå±ãããŠãããã°ãšæããŸãïŒ GROWIãšã¯ïŒGROWI Users Meetupãšã¯ïŒ GROWI GROWI ãšã¯ãWESEEK ãäžå¿ãšãªã£ãŠéçºãè¡ã£ãŠãã OSS ã® Wiki ã·ã¹ãã ã§ãã ãšã³ãžãã¢ã®çããã«ãªãã¿æ·±ãããŒã¯ããŠã³åœ¢åŒã§ã®èšè¿°ããããŠæè»ãªéå±€æ§é ã§ã®æ
å ±ç®¡çãå¯èœã§ãã ãªã³ãã¬ãã¹ç°å¢ãåçš®ã¯ã©ãŠã VM ã«å°å
¥ã§ãã瀟å
Wiki ããªãŒãã³ Wiki ãšããŠã掻çšããã ããŠãããŸãã å°éçãªç¥èããªããŠããGROWI ãç°¡åã«éçšã»ç®¡çã§ãããµãŒãã¹ GROWI.cloud ãæäŸããŠãããŸãïŒ GROWI å
¬åŒãµã€ãïŒ https://growi.org/ja/ GROWI.cloud å
¬åŒãµã€ãïŒ https://growi.cloud/ GROWI Users Meetup ãããŠä»åã玹ä»ãã GROWI Users Meetup ã¯ãæè¿ã® GROWI ã®ã¢ããããŒã玹ä»ã»ä»åŸã®éçºäºå®ãªã©ãçºè¡šãã€ã€ããªãŒãã³æè°(äŒè°)ãéã㊠GROWI ã®ãŠãŒã¶ãŒããå士ãéçºè
ã亀æµãæ·±ããããšãã§ããæ©äŒãšããŠäŒç»ãããã€ãã³ãã§ãã ããããã¯ãGROWI ã®ãŠãŒã¶ãŒããã®ã³ãã¥ããã£ã掻çºã«æŽ»åããŠããããšãç®æšãšããŠããŸãïŒ äžè¬çã«ã¯ ãŠãŒã¶ãŒäŒ ãšåŒã°ãããããªãžã£ã³ã«ã®ã€ãã³ããªã®ããªïŒãšæãã®ã§ããããã®ãŸãŸãGROWI ãŠãŒã¶ãŒäŒãã¯ã¡ãã£ãšããµããªïœãšããããšã§ãGROWI User Meetupããšè±èªã«ããŠã¿ãŸããã ãŠãŒã¶ãŒäŒã«å¯ŸããéçºåŽã®æã ãGROWI ã®ãŠãŒã¶ãŒäŒãéå¬ãããïŒãŠãŒã¶ãŒã³ãã¥ããã£ãŒãäœãããïŒããšãã声ã¯çµæ§åãã瀟å
ã§äžãã£ãŠããããã§ãå®ã¯éå»ã«ããŠãŒã¶ãŒããã«åå ããŠããã ãããã㪠GROWI ã®å¬ããéå¬ãããããšããããŸããã GROWI SSO ãŠãŒã¶ãŒããŒãã£ã³ã°ãããŸãïŒ 3/3(ç«) 16:00ïœ on Google Hangouts Meet https://t.co/HLdf8DyQs3 #GROWI éçºã§åã®è©Šã¿ããŠãŒã¶ãŒãããšäžç·ã«èªèšŒåšã(ç¹ã« SAML, SSO)ã®ããããããªã³ã©ã€ã³ã§è©±ãåããŸãã åå ãROM èªç±ã§ã å
¥å®€ãªã¯ãšã¹ãããã ããã°æ¿èªããŸãã â WESEEK, Inc. (@weseek_inc) March 2, 2020 ãã®æã¯ãSSO ã«ã€ããŠãŠãŒã¶ãŒããããåºãæèŠãåããšããããŒãã£ã³ã°ãè¡ã£ãããã§ãã(åœæã®è°äºé²ã¯ ãã¡ã ) ãã®äŒä»¥æ¥ããŠãŒã¶ãŒã®æ¹ã®å£°ãçŽæ¥èãã€ãã³ãã¯ã»ãŒãªããªã£ãŠããŸã£ãã®ã§ããâŠãã§ã¯ãªãä»åãGROWI Users Meetup ãå®çŸããã«è³ã£ãã®ãïŒãã玹ä»ããŠãããŸãã å
·äœçãªæºåã¯ãªããšä»å¹Žã®æ¥ããå§ãŸã£ãŠããŸããã ãŠãŒã¶ãŒäŒéå¬ãŸã§ã®æ©ã¿ ïœæ¥ããå€ïœ ã³ãã¥ããã£ãŒãããŒãžã£ãŒèªç ä»å¹Žã®æ¥ããã GROWIã»GROWI.cloud ã®èªç¥åºŠãé«ããããã®æŽ»å ããã匷åããŠããããšã«ãªããGROWI Users Meetupã¯ãã®äžã®æŽ»åã®äžã€ãšããŠèšç»ãããŠããŸããã ãããŠãã³ãã¥ããã£ãŒãããŒãžã£ãŒãšããŠé¢é£æœçã®äžå¿äººç©ã«ä»»åœãããã®ã4æãã瀟å¡ãšãªã£ãç§ã§ããã 1幎åã€ã³ã¿ãŒã³ããã£ãŠãããšã¯ãããæ¥åã§ GROWI ã GROWI.cloud ã«ã¯ã»ãšãã©é¢ãã£ãŠããªãã£ãã®ã§ãGROWIãšã¯äœãããšãã説æããæºè¶³ã«ã§ããªãç¶æ
ã ã£ãã®ãèŠããŠããŸãâŠã GROWI ãŠãŒã¶ãŒã€ã³ã¿ãã¥ãŒ GROWI Users Meetup ãéå¬ããåã«ã ä»ããŠãŒã¶ãŒã¯ GROWI ã«äœãæ±ããŠããã®ãïŒ ãã調æ»ãã¹ãã4æãã5æé ã«ãããŠãŠãŒã¶ãŒã€ã³ã¿ãã¥ãŒãè¡ããŸããã ãšã³ãžãã¢ãããŒã±ãã£ã³ã°æ
åœã®ã¡ã³ããŒã亀ããŠã€ã³ã¿ãã¥ãŒããããŠããã ããã®ã§ãããã GROWI ããããã£ãп޻çšããŠããŸãïŒããã®æ©èœã䟿å©ã§ãïŒ ããšãããããããã話ãããã ä»ã® Wiki ããŒã«ã«æ¯ã¹ãŠ GROWI ã¯ãããè¶³ããªãããã£ãšãããªæ©èœãéçºããŠã»ãã ããšãããæèŠãŸã§äŒºãããšãã§ããéçºããŒã ã¯ãã¡ãã瀟å
å
šäœãåºæ¿ãåããŠãããªãŒãšæããŠããŸããã å人çã«ã¯ãæ°åŒãæžãã MathJax ã䟿å©ïŒãšãã声ãå€ãã®ãŠãŒã¶ãŒããããèãããããšããšãŠãæå€ã§ããã ã€ã³ã¿ãã¥ãŒã®äžã§ã¯ãGROWIã®æ©èœé¢ã ãã§ã¯ãªã OSS 掻åã«ã€ããŠãã話ã䌺ã£ãŠããã®ã§ããããGROWI ã® OSS éçºè
ãå¢ãããŠããããããšãã WESEEK åŽã®æããšã¯è£è
¹ã«ã GROWI (ãã®ä» OSS ã®ãããžã§ã¯ã) ã«ã¯æ°è»œã«ã³ãããã§ããªã ããšããæèŠããŠãŒã¶ãŒããããå€ãèãããŸããã ããã³ãããããããã»é¢ããããããããžã§ã¯ãã®é°å²æ°ã¥ããã®ããã«ããGROWI Users Meetupã®éå¬ã¯æ¥åã§ããâŠïŒãšããçµè«ã«è³ã£ãã®ã§ãã Slackã®æŽå å°ã話ã¯éžããŠããŸããŸãããGROWI Users Meetup ã®æºåã®äžç°ãšã㊠GROWI ã® Slack ã¯ãŒã¯ã¹ããŒã¹ã®æŽåããã®é ããè¡ã£ãŠããŸããã ãã£ã³ãã«è§£èª¬ããŒãž ä»ã® Slack ã³ãã¥ããã£ãŒã®äºäŸãåèã«ã宣äŒã®ããã®ãã£ã³ãã«ã远å ããããdefault ãã£ã³ãã«ã®è¿œå ãGROWI Users Meetup 宿³çšãã£ã³ãã«ã®è¿œå ãªã©ãªã©ïŒ ãŠãŒã¶ãŒã€ã³ã¿ãã¥ãŒã®çµæãå å³ããŠãããå€éšã®æ¹ãçºèšããããã¯ãŒã¯ã¹ããŒã¹ã«ãªãããã«çŸåšãæŽåãé²ããŠããŸãã ã¡ãªã¿ã«ãGROWI ã® Slack ã¯ãŒã¯ã¹ããŒã¹ã¯ ãã¡ã ããåå ã§ããŸãããã²çãããæ°è»œã« join ããŠã¿ãŠãã ããïœïŒ ãšããããšã§ãã¡ãã£ãšçãã®ã§ããåç·šã¯ãããŸã§ïŒ 次åäºå ç§ãè¿ã¥ããGROWI Users Meetup éã«éå¬ïŒâŠãšæããããéçºäžã®GROWI Botã«ããåŸ
ã£ãããããããå£ç¯ã¯ãã€ããå¬ã«ã Slack 瀟ã«ç¿»åŒããç¶ãã GROWI Bot éçºé£ã®åªåãå®ããéã«ãªãªãŒã¹ã®æã蚪ããã ãã¬ã¹ãªãªãŒã¹ãšåæã«ãã€ãã³ãã®åç¥ãè¡ã£ãã®ã¯ãªããšéå¬2é±éåã æãããŠãGROWI Users Meetup ã¯ç¡äºã«éå¬ã§ããã®ãïŒ ãããŠäœããããGROWI ã®ãŠãŒã¶ãŒã¯ GROWI Users Meetup ã«åå ããŠãããã®ã ãããïŒïŒ 次åã GROWI Users Meetupãéå¬ãŸã§ã®éã®ã åŸç·š ãæ¥é±æŽæ°äºå®ïŒã楜ãã¿ã«ïŒ
ãã®èšäºã¯ã2021/12/23 ã«è¡ã£ã WESEEK Tech Conference #16 ã®å
容ããŸãšãããã®ã§ãã 第16åç®ã®ããŒãã¯ãæ¿çœïŒGROWI.cloudã®å¯çšæ§åäžã®åãçµã¿ã 顧客ã«äœããã®ãµãŒãã¹ãæäŸããäžã§ãæ¥åžžçãªãµãŒãã¹å¯çšæ§ã®ç¶æã»åäžã«é¢ããæ¥åã¯å¿
é 課é¡ãšãªããŸãã æ¬çºè¡šã§ã¯ãå¯çšæ§ç¶æã»åäžã«å¿
èŠãªé
ç®ãæãã€ã€ãåŒç€Ÿãå®éã«æäŸããŠãããµãŒãã¹ã§ãã GROWI.cloud ã§ã®å®æœäŸãã玹ä»ããŸããã ç®æ¬¡ GROWI.cloud ã®çŸç¶ã®éçšèŠæš¡ æ¬çºè¡šæ(2021/12/23 æç¹)ã®ãGROWI.cloud ã®èŠæš¡ã瀺ãããŒã¿ãã玹ä»ããŸããã ç·ããŒãæ°: 56 ãŠãŒã¶ã¢ããªæ° GROWI: 318 HackMD: 111 Keycloak: 15 å¯çšæ§(SLI/éå»1ãæ) éåžžããŒãäžã® GROWI: 99.995% å
šããŒãäžã® GROWI: 99.846% ã¢ãžã§ã³ã ãµãŒãã¹éå¶ã®äžã§å¿
èŠãªç£èŠé
ç®ãšã¯ SRE ã®èãæ¹ãããæ¬åœã«ç£èŠãã¹ãé
ç®ãèšå®ãã ãµãŒãã¹ã®å¯çšæ§ãèšæž¬ã»å¯èŠåããææ³ ã1. ãå®çŸããããã«å¿
èŠãªæ
å ±ãæããææ®µãå®è£
ãã å¯çšæ§ãç¶æ/åäžããããã«å¿
èŠãªåãçµã¿ 1./2. ãçšæããäžã§ãå®éã«ã©ã®ãããªåãçµã¿ã宿œããŠããã 1ç« : ãµãŒãã¹éå¶ã®äžã§å¿
èŠãªç£èŠé
ç®ãšã¯ ç£èŠé
ç® ã¿ãªãããéçšããŠãããµãŒãã¹ã®ç£èŠãã©ã®ããã«å®æœããŠããŸããïŒ äžèšã«ç£èŠé
ç®ãšèšãã©ãã以äžã®ããã«æ§ã
ãªç£èŠãã€ã³ããæãæµ®ãã¶ãšæããŸãã éé²ã«ã¢ã©ãŒããåºããš ã¢ã©ãŒãã®çµ¶å¯Ÿéã人éã®å¯Ÿå¿ã§ããç¯å²ãè¶
ãã ã¢ã©ãŒãã°ããã§å¯ãããªã ãããŸã§é倧ãããªããšæãããã¢ã©ãŒãã§èµ·ãããã ãã®ã¢ã©ãŒãã¯ãã³ãã«é¡§å®¢ãµãŒãã¹ã«çŽæ¥åœ±é¿ããã®ãïŒ çŽæ¥åœ±é¿ããªããã®ãã¢ã©ãŒããšããŠåºãŠããŸã ã»ãšãã©ã¹ã«ãŒããŠãããšããèªèã«ãªããããã察å¿ãããªããªã æ¬åœã«å¯Ÿå¿ããªããšãããªãã¢ã©ãŒããèŠéããã éçšè
ã¯äººé 人éã察å¿ã§ããç¯å²ã®ã¢ã©ãŒãéã«èª¿æŽããå¿
èŠããã ãµãŒãã¹ã®éçšè
ãæ£ãã察å¿ã§ããããã«ããããã®ç£èŠã»ã¢ã©ãŒãããå¿
èŠãªãšãã«å¯Ÿå¿ãããªãã£ãããéçšè
ã®è² æ
ã ããå¢å€§åããèŠå ã«ããŠããŸããŸãã SRE ã®èãæ¹ äžèšã®ãããªäºæ
ãé¿ããããã«ã SRE ãšããæŠå¿µãäœãåºãããŸããã Site Reliability Engineering Google ãæå±ãããšã³ãžãã¢ã®åœ¹å² https://sre.google/ ã·ã¹ãã ã®ä¿¡é Œæ§ã«çŠç¹ã眮ããŠãã å¯çšæ§ ãã·ã¹ãã éçšã«ãããæåã®åææ¡ä»¶ã«ãªã£ãŠãã 以äžã®çšèªãå®çŸ©ãããŠãã SLO (ãµãŒãã¹ã¬ãã«ç®æš/Service Level Objective) ãµãŒãã¹ã¬ãã«ã®ç®æšå€(ex. 99.9%) SLA (ãµãŒãã¹ã¬ãã«å¥çŽ/Service Level Agreement) äžå®æéã§ SLO 以äžã®ã¬ãã«ãæºããããšããå©çšè
ãšã®å¥çŽã«å«ãŸããå€ SLI (ãµãŒãã¹ã¬ãã«ææš/Service Level Indicator) ã·ã¹ãã ã®ç£èŠé
ç®ãæåããé »åºŠ SLO ãäžåãå Žåã¯äœãããã®åŠçœ®ãå¿
èŠ å¯çšæ§ãšã¯ ã§ã¯ãæåã®åææ¡ä»¶ã«ãªã£ãŠãã å¯çšæ§ ãšã¯ãã©ãããå®çŸ©ãªã®ã§ããããã [1] å¯çšæ§ïŒAvailabilityïŒãšã¯ãã·ã¹ãã ãéå®³ïŒæ©åšãããŒãã®æ
éã»çœå®³ã»ã¢ã¯ã·ãã³ããªã©ïŒã§åæ¢ãããããšãªã皌åãç¶ããããšããŸã㯠ãã®ææš ã®ããšããããŸãã é·ãæéãã·ã¹ãã ã皌åãç¶ããããããšãé«å¯çšæ§ïŒHigh AvailabilityïŒãšããããŸãã ä»åã®æèã§äœ¿ãããå¯çšæ§ã¯ããã·ã¹ãã ãé害ã§åæ¢ãããããšãªã皌åãããããã®ææšããšããæå³ã§æãããããã§ããã éçšã§å®ãã¹ãããš SRE ã®èãæ¹ã«æ²¿ããšããµãŒãã¹éçšäžå®ãã¹ãäºé
ã¯ä»¥äžã«ãŸãšããããŸãã é¡§å®¢ã«æäŸããŠãããµãŒãã¹ã SLO 以äžã§æ£åžžã«çšŒåããŠããããš = å¯çšæ§ã SLO ã«ä¿ã€ ãµãŒãã¹ã®è£åŽã§åãããã«ãŠã§ã¢ã®ç¶æ
ããå¿
ãããçŽæ¥é¡§å®¢ãµãŒãã¹ã«åœ±é¿ãããšã¯éããªã ex.) çž®éç¶æ
ã§ããµãŒãã¹ã¯åã(ãã®çºã®åé·å) SLO å€ãç®åºããããã« SLI ãå®çŸ©ããå¿
èŠããã GROWI.cloud ã«ããã SLO GROWI.cloud ã§ã¯ãã©ã³ããšã« 99.4% or 99.9% ãšèšå® Closed β ã§ã®çšŒåç¶æ³ãèŠã€ã€ãæ£åŒãªãªãŒã¹æã«æ±ºå®ãã GCE ããªãšã³ããã£ãã«ã€ã³ã¹ã¿ã³ã¹ãå©çšãããã©ã³ã§ã¯ã24 æé以å
ã«å¿
ãåèµ·åãã WESEEK Tech Conference #2 ã§çºè¡šãè¡ã£ãŠããŸã ã³ã¹ã7岿ž!Kubernetesæ¬çªãµãŒãã¹ç°å¢ã®éçšããŠã㊠以äžã®è¡šãåèã«ãåèµ·åã«ãããæéãèæ
®ãã99.4% ãšããå€ãèšå®ãã åè) å SLO ã«ããã蚱容ããã down æé SLOæé 1æ¥ 1ãæ(30æ¥) 1幎(365æ¥) 99.4% 8å38ç§ çŽ4æé20å çŽ52æéå 99.9% 1å26ç§ çŽ43å çŽ8æé45å GROWI.cloud äžã§ã® SLO è¡šèš GROWI.cloud ã§ã¯ãæ³äººãã©ã³/å人ãã©ã³ããããã§ãåãã©ã³ã«ããã SLO ãå
¬éããŠããŸãã SLO ãéçšè
/å©çšè
ã§å
±æããããšã«ãã£ãŠããµãŒãã¹ã¬ãã«ã®èªèããäºãã«åãããããããã«åªããŠããŸãã GROWI.cloud äŒæ¥åããã©ã³ç޹ä»ããŒãž GROWI.cloud å人åããã©ã³ç޹ä»ããŒãž GROWI.cloud ã«ããã SLI GROWI.cloud äžã® GROWI ã«ã€ããŠã¯ã以äžã®ãããªæ§æã§çšŒåããŠããŸãã GROWI.cloud æ§æå³ SLO ãç®æãããã«å¿
èŠãªææšã§ãã SLI ã«ã€ããŠã¯ãæ§æå³ã®ãã¡ä»¥äžã®éšåããååŸã§ããå€ãå©çšããŠããŸãã GROWI.cloud äžã® SLI é¢ä¿éšå ãŸãšã 1ç« ã§ã¯ãSRE ã®èãæ¹ãçšããGROWI.cloud äžã§ã®éçšæ¹æ³ãèããŠãããŸããã ãµãŒãã¹éçšã§å®ãã¹ãããš = 顧客ããµãŒãã¹ã䜿ããç¶æ
ã§ããããš = å¯çšæ§ãç¶æããããš â ãµãŒãã¹æäŸã«å¿
èŠãªã·ã¹ãã å
šãŠããå®ç§ãªç¶æ
ã§çšŒåããŠããããš å¯çšæ§ã®ææš SLO ãµãŒãã¹ã¬ãã«ã®ç®æšå€ SLI ç£èŠé
ç®ãæåããé »åºŠ å¯çšæ§ã®ææšãéçšã«ä¹ããã°ãããŸããããã 2ç« : ãµãŒãã¹ã®å¯çšæ§ãèšæž¬ã»å¯èŠåããææ³ 1ç« ã§å¶å®ãã SLO/SLI ãšãã£ãææšãæŽ»çšããéçšäœå¶ãæŽããããã2ç« ã§ã¯ã ç¶ç¶çã« SLI ãèšæž¬ã§ããŠãããïŒ ç¶ç¶çã« SLO ãéæã§ããŠãããïŒ ãšããç¹ã«ã€ããŠãããã«ç¢ºèªã§ãããããªç°å¢ãæŽåããææ³ãã玹ä»ããŸããã å¯çšæ§ã®èšæž¬ã»å¯èŠåã«å¿
èŠãªå®æœé
ç® æºåãå¿
èŠãšãªãé
ç®ã¯ä»¥äžã® 2 ç¹ã§ãã ç£èŠã»å¯èŠåããŒã«ã®éžæ ç£èŠããã·ã¥ããŒãã®äœæ äœãã©ã®ããã«å¯èŠåããããæ±ºãã ã·ã¹ãã å
šäœãšåå¥ã®ã³ã³ããŒãã³ããããããã®ã«ããã§èŠãããç¶æ
ãæãŸãã ãŽãŒã«ã¯ãããµãŒãã¹éå¶ã«å¿
èŠãª SLI ãç°¡åã«ç¢ºèªã§ããããšãã§ãã å°å
¥æã®åæ GROWI.cloud ã§ã®å°å
¥æã®æ§æã¯ä»¥äžã®éãã§ããã ç£èŠã»å¯èŠåããŒã«ã®éžæ ç£èŠã»å¯èŠåããŒã«ã®åè£ã¯ãåœå以äžã®ãããªãœãããŠã§ã¢/ãµãŒãã¹ãæããããŸããã ãããããããŒãžããµãŒãã¹ã«ã€ããŠã¯ãããŒãæ°/ã³ã³ããæ°ã«ãã課éäœç³»ã§ãã£ãããšãããåœåããè²»çšããããããªããšããåžæãšåèŽããã«ãåè£ããå€ããŸããã GROWI.cloud ã®äºäŸ GROWI.cloud ã§ã¯ãäžèšã®åè£ã®ãã¡ãPrometheusããéžæããŸããã ãŸããå¯èŠåã«ã¯ Prometheus ããµããŒãããŠãã Grafana ãéžæããŸããã éžæçç±ã¯ä»¥äžã®éãã§ãã Cloud Native çéã§è©±é¡ã«ãªã£ãŠãããã GROWI.cloud ãèŒã£ãŠãã Kubernetes ãšçžæ§ãããããã ã£ããã Service Discovery æ©æ§ãã€ããŠãããã ããã»ã¹ã®åèµ·åãããããšãªããåçã«ç£èŠã¿ãŒã²ãããå¢ããã ãããã€ææ³ãæŽåãããŠãã helm chart(Kubernetes äžã«ãªãœãŒã¹ããããã€ããããã®ãããã§ã¹ãããã±ãŒãž) ãæ¢ã«ãã£ã exporter ãå¢ããããšã§ä»»æã®ã¡ããªãã¯ãæºããã¢ã©ãŒããåºãããšãã§ãããã æ¡çšããŒã«ã®ãçŽ¹ä» GROWI.cloud ãžå°å
¥ããããŒã«ã«ã€ããŠç°¡åã«ã玹ä»ããŸããã Prometheus Grafana ç£èŠæ§æ GROWI ã皌åããŠããåäžã¯ã©ã¹ã¿ã«ç£èŠ/å¯èŠåããŒã«ãå ãã以äžã®ããã«ç£èŠã宿œããããšã«ããŸããã GROWI.cloud äžã§ã®å¯èŠåäŸ GROWI.cloud ã§ã¯ã以äžã®ãããªå¯èŠåã®ããã®ããã·ã¥ããŒããçšæããŠããŸãã ã·ã¹ãã å
šäœã®æ
å ± ãã¡ãã®ããã·ã¥ããŒãã§ã¯ãã·ã¹ãã å
šäœã俯ç°ããŠããã«ç¢ºèªã§ãããããªæ
å ±ã衚瀺ããŠããŸãã 以äžã¯è¡šç€ºããŠããé
ç®ã®äžäŸã§ãã éå» 30 æ¥éã® SLI å€ ããŒãã¿ã€ãããšã«åé¢ããŠåºåããŠãã ã€ã³ã¹ããŒã« GROWI æ° å³äžã®èµ€äžžéšåã«ã1. ã®é
ç®ã衚瀺ãããŠããŸãã åå¥ã³ã³ããŒãã³ãã®æ
å ± ãã¡ãã®ããã·ã¥ããŒãã§ã¯ãåã¢ããªåäœã§ãã现ããææšã確èªã§ãããããªæ
å ±ã衚瀺ããŠããŸãã 以äžã¯è¡šç€ºããŠããé
ç®ã®äžäŸã§ãã å GROWI ããšã® SLI å€ã蚱容ãããæ®ã down æé ããã«ãŠã§ã¢æ
å ±(CPU/ã¡ã¢ãªäœ¿çšé/ã³ãã¯ã·ã§ã³æ°ãªã©) ããŒãæ
å ±(CPU/ã¡ã¢ãªäœ¿çšéãªã©) äžèšã¯ã1. ã®å GROWI ã«é¢ãã詳现æ
å ±ã®è¡šç€ºäŸãšãªã£ãŠããŸãã ãŸãšã 2ç« ã§ã¯ã1ç« ã§å¶å®ãã SLO/SLI ãç¶ç¶çã«ç¢ºèªã§ãããããªç°å¢ãæŽåããã©ã®ãããªæ§æã«è³ã£ãã®ããã©ã®ããã«ç¢ºèªããŠããããã«ã€ããŠã玹ä»ããŸããã ç°å¢æŽåã«å¿
èŠãªå®æœäºé
ç£èŠã»å¯èŠåããŒã«ã®éžæ ç£èŠããã·ã¥ããŒãã®äœæ GROWI.cloud ã§ã¯ä»¥äžã®æ§æã§ç£èŠã宿œããŠãã Prometheus/Grafana ãéžæ å
š GROWI / å GROWI ã«é¢ãã SLI ã®å€ã確èªã§ããããã«ããã·ã¥ããŒããçšæ GROWI ãåèµ·åããŠããŸã£ãåå ãæ¢ããããããããã«ã以äžã®ãããªã°ã©ãã䜵ããŠçšæããŠãã ã¡ã¢ãª/CPU䜿çšç ãªã¯ãšã¹ãæ°ãç 3ç« : å¯çšæ§ãç¶æ/åäžããããã«å¿
èŠãªåãçµã¿ 3ç« ã§ã¯ã1ç« ã2ç« ã§ãããŸã§æããæ
å ±ã䜿ããå®éã«ã©ã®ãããªåãçµã¿ã宿œããŠãããã«ã€ããŠã玹ä»ããŸããã å¯çšæ§ãç¶æããããã«ã¯ SLI ã SLO 以äžã«ç¶æã§ããããã«æèãã察å¿ãã ããã«éããŸã GROWI down ãçºçããå Žåã¯ãããã«æ§åãèŠã ex.) SLO ãäžåããã㪠GROWI ã«ã€ããŠã¯æ©æ¥ã«äœããã®å¯Ÿçãè¬ãã ãããé害ã«ãªãã§ãããäºè±¡ã«ãåãã£ãŠå¯Ÿå¿ãã ex.) ããã«ãŠã§ã¢ã®ã¹ãã¬ãŒãžå®¹éæžå°ãåé·æ§äœäž ãããã®å¯Ÿå¿ã«å¿
èŠãªæ
å ±ããäžç®ã§ããããããªã¢ã©ãŒããèšå®ãã ã¢ã©ãŒã察å¿äŸ ã¢ã©ãŒã察å¿ã®äžäŸã§ãã ã¢ã©ãŒãå¯Ÿå¿æã«èŠãŠãããã® å
è¿°ãã Prometheus/Grafana ããã«æŽ»çšããŠããŸãã ã¢ã©ãŒãããéçšäž SLO/SLI ãããæèã§ããããã«ã以äžã®ãããªææ³ã§èšå®ããŠããŸãã ã¢ã©ãŒãã Prometheus äžã®ã¡ããªãã¯ãããŒã¹ã«åºããŠãã ã¢ã©ãŒãã«ã¯ãåœè©²ã® GROWI ã§ SLO ãéæããããã®èš±å®¹ãããæ®ãããŠã³æéãèšèŒããŠãã äžç®ã§ GROWI ãã©ãããç¶æ
ãªã®ããææ¡ã§ããããã«ããããã®æ
å ±ãåºããŠãã å¯çšæ§ãåäžããããã«ã¯ GROWI.cloud ã§ã¯ãå¯çšæ§ãåäžããããã«ã以äžã® 2 ç¹ã宿œããŠããŸãã å¯çšæ§ã®å€ã鿬¡æ°ã«ããŠãå¯çšæ§ãé«ããããç®æããªããæ¢ãã察åŠãã ãAvailability åäžæœçããšéæã£ãŠãéäžçã«è¡ã£ãææããã£ã é害察å¿å±¥æŽãæ®ã Availability åäžæœçå®äŸ ããªãšã³ããã£ãã«ããŒããå©çšãããã©ã³ã§ã®åé·åã宿œ ã©ã€ããããªã¥ãŒãã©ã³ ãªãªãŒã¹åœåã¯ãããªãšã³ããã£ãã«ããŒãã« 1 GROWI ããä¹ããŠããªãã£ããã99.4% ã® SLO ãä¿ãŠãªãã£ã æœç宿œå SLI: 99.00% æœç宿œåŸ SLI: 99.93% é害察å¿å±¥æŽãæ®ã é害çºçæã«ãæ¯ãè¿ããããã«çµç·¯ãæ®ãã®ã倧å ãã¹ãã¢ãŒãã https://qiita.com/an_sony/items/0565ad980f9097c76d11 èšé²ã¯éèŠïŒ 察å¿è
以å€ã«ãé害å
容ãäŒãããã 鱿¬¡ã§ããŒã å
šäœã«å ±åãã察çå
容ã«ã€ããŠæ€èšã宿œãžç¹ããŠãã æèè
ã®ç¥èŠãåãããã ä»åŸã䌌ããããªé害ãèµ·ããªãããã«ãæ£ãã察çãæãŠã 察å¿åŸã«ãæ¹ããŠç£èŠã§åéããããŒã¿ãšçªãåãããã广çãªå¯Ÿçãž ä»åŸã䌌ããããªé害ãèµ·ããæã«ãèªåã/ä»ã®äººãåèã«ã§ãã èªåãå¿ããªããã ãŸãšã æ¬çºè¡šã®ãŸãšãã§ãã ãµãŒãã¹éå¶ã®äžã§å¿
èŠãªç£èŠé
ç®ãšã¯ çã«ç£èŠããã¹ããã®ã¯äœããæŽãåºã SLO ãèšå®ãã ãµãŒãã¹ã®å¯çšæ§ãèšæž¬ã»å¯èŠåããææ³ SLI ãç®åºã§ããã¡ããªãã¯ãæŽåãã SLI ãç°¡åã«ç¢ºèªã§ãããããªç°å¢ãæŽåãã ex.) ããã·ã¥ããŒããæŽåãããã¢ã©ãŒãå
容ã«èšèŒãããç å¯çšæ§ãç¶æ/åäžããããã«å¿
èŠãªåãçµã¿ å¯Ÿå¿æã¯ã1./2. ã§æŽåãããã®ããã«æŽ»çš å¯Ÿå¿åŸã¯ã察å¿å
å®¹ãæ®ããŠããŒã ã«å
±æ ç¶ç¶çã« SLI ãæ¹åã§ãããããªåããž ä»åŸã® GROWI.cloud éçšå±æ GROWI äžã§å®éã«å©çšããããªã¯ãšã¹ãã®ãšã©ãŒçã§ SLO ãæ±ºå®ã§ããããã«ããã çŸç¶ã¯ããã«ã¹ãã§ãã¯ãšã³ããã€ã³ããžã®ç£èŠæå/倱æã§å€å®ããŠãã ããå©çšè
ç®ç·ã«ç«ã£ãç£èŠãã§ããããã«ãªã èè
ãããã£ãŒã« ä»é ä¿ä» æ ªåŒäŒç€ŸWESEEK / ããã¯ãšã³ããšã³ãžã㢠2013幎ã«WESEEKã«å
¥ç€Ÿã node.js/Kubernetes ãäžå¿ãšããã€ã³ãã©/ã¢ããªã®èšèšã»æ§ç¯ã»éçšã«æºããã GROWI.cloud ã®éçšã«ãªãªãŒã¹åœå(2018ïœ)ããåŸäºã æ ªåŒäŒç€ŸWESEEKã«ã€ããŠ æ ªåŒäŒç€ŸWESEEK ã¯ãã·ã¹ãã éçºã®ãããã§ãã·ã§ãã«éå£ã§ãã ãçŸåšã®äž»ãªäºæ¥ã é信倧æäŒæ¥ã®æ¥åãããŒèªååãããžã§ã¯ã ãœãŒã·ã£ã«ã²ãŒã ã®åèšéçº èªç€ŸçºãªãŒãã³ãœãŒã¹ãããã¯ãã GROWI ãã GROWI.cloud ãã®éçº GROWI GROWI ã¯ãMarkdownèšæ³ã§ããŒãžãèšè¿°ã§ãããªãŒãã³ãœãŒã¹ã®Wikiã·ã¹ãã ã§ãã GROWI.cloud GROWI.cloud ã¯OSSã®GROWIãå°éçç¥èããªããŠãç°¡åã«éçšã»ç®¡çã§ãããæ³äººã»å人åãã®åçšãµãŒãã¹ã§ãã 倧æSIerã»ISPãäžå°äŒæ¥ã倧åŠã®ç 究宀ãªã©æ§ã
ãªå Žæã§ãå©çšããã ããŠãããŸãã ãäž»ãªç¹åŸŽã ããã¹ããå³è¡šãã©ãã©ãæžããã匷åãªç·šéæ©èœ ããŒã æ¡å€§ã«è¿
éã«å¯Ÿå¿ã§ãã管çè
åãæ©èœãæäŸ å
å®ããæ©èœã»ãµããŒãã§ãšã³ã¿ãŒãã©ã€ãºã«ãå¯Ÿå¿ ãå°å
¥äºäŸèšäºã ã€ã³ã¿ãŒããããã«ããã£ãŒãæ ªåŒäŒç€Ÿæ§ [ https://growi.cloud/interviews/mfeed/?utm_source=connpass-top&utm_medium=web-site&utm_campaign=mf:embed:cite ] æ ªåŒäŒç€ŸHIKKYïŒVRæ³äººHIKKYïŒæ§ [ https://growi.cloud/interviews/hikky:embed:cite ] WESEEK Tech Conference WESEEK Tech Conference ã¯ãæ ªåŒäŒç€ŸWESEEKãäž»å¬ãããšã³ãžãã¢åãã®å匷äŒã§ãã WESEEKã«æå±ãããšã³ãžãã¢ãæ§ã
ãªããŒãã§çºè¡šãè¡ãäºå®ã§ãã æ¬¡åã¯ã1/27(æš) 19:00~20:00ã«éå¬äºå®ã§ãã GROWI ãšé£æºã§ãã GROWI bot ã®éçºãéããŠåŠãã ãSlack瀟ãåŽäžããã·ã¹ãã ã®äœãæ¹ããããŠãSlack瀟ãè²ããªã仿§ãã«ã€ããŠã話ããŸãã çŸåšãconnpassãTECH PLAYã§åå åä»äžã§ããçæ§ã®ãåå ããåŸ
ã¡ããŠãããŸãïŒ https://weseek.connpass.com/event/234368/ TECH PLAYã¯ãã¡ããã äžç·ã«åã仲éãåéããŠããŸã æ±äº¬ã®é«ç°éŠ¬å Žãªãã£ã¹ã倧åã«ããå¥åºãµãã©ã€ããªãã£ã¹ã«ãŠãšã³ãžãã¢ãåéããŠãããŸãã äžéæ¡çšã ãã§ã¯ãªããã€ã³ã¿ãŒã³ã·ãããç©æ¥µçã«åãå
¥ããŠããŸãïŒ è©³ããåéèŠé
ã¯ãåŒç€ŸHPã® æ¡çšããŒãž ããã確èªãã ããã
WESEEKãªãªãžãã« ãã©ã³ãã³ã°ããŒã«ãŒ ã¯ãããŸããŠïŒä»åå·çããã®ã¯ãæŠç¥äŒç»å®€ã€ã³ã¿ãŒã³ã®å¡ç°ã§ãããšã³ãžãã¢ã®æ¹ã
ãæžãèšäºãšã¯äžé¢šå€ãã£ããæ°é®®ãªãã€ã¹ãã®èšäºã«ãªãããšæããŸãããæ°æ¥œã«æ¥œããã§é ãããšå¬ããã§ãïŒ ããŠãä»åã®ããŒãã¯ã ãã©ã³ãã³ã°ããŒã«ãŒ ãã§ãã WESEEKã§ã¯ãã¿ã¹ã¯ã®å·¥æ°èŠç©ããã®éã«ãã©ã³ãã³ã°ããŒã«ãŒã䜿çšããŠããŸããå
·äœçã«ã¯âãã€ã³ãä»ãâãšãã£ãŠããããã¹ããŒãªãŒã«ã©ã®ãããã®æéãèŠããã®ããã«ã€ããŠãã¡ã³ããŒãããããã«ãŒãã䜿ã£ãŠåæã«æ°åã瀺ãäœæ¥ãè¡ã£ãŠããŸãããã®å€ãçã§è¿ããªãããã«è©±ãåããããããšã§ãå·¥æ°èŠç©ããã«ãããæèŠã®ããéããé²ãã§ããã®ã§ãã ä»å¹ŽåºŠãWESEEKã¯ç€Ÿå
ã°ããºã®äžã€ãšããŠãã©ã³ãã³ã°ããŒã«ãŒãäœæãããããïŒä»ã®ç€Ÿå
ã°ããºã«ã€ããŠã¯ ãã¡ã ïŒãã®èšäºã§ã¯ãWSæµã®å·¥æ°èŠç©ãããã«ãŒãã®å¶äœç§è©±ã«ã€ããŠãšããããããšæããŸãïŒ WESEEKã«ãšã£ãŠã®ãã©ã³ãã³ã°ããŒã«ãŒ ã¯ããã«ããã©ã³ãã³ã°ããŒã«ãŒã®ã¡ãªããã䜿çšäžå¿ãããŠããããšãªã©ã«ã€ããŠãä»£è¡šã®æŠäºãããšãšã³ãžãã¢ã®çããã«ã€ã³ã¿ãã¥ãŒããŠã¿ãŸããã å·Šããè²æ²¢ãããæŠäºããã倧谷接ãããç°æãã ãŒããããWESEEKã§ã¯ããªãå·¥æ°èŠç©ããã«ãã©ã³ãã³ã°ããŒã«ãŒã䜿çšããŠãããã§ããïŒ ãæŠäºã WESEEKçã«ã¯ããšã³ãžãã¢ãå人ã§åãæã«ã次ã«èªåãäœããããªãããããªãã®ãããããã£ãŠããŠæ¬²ãããŠããã®ããã«æå¹ãªææ®µãšããŠãã©ã³ãã³ã°ããŒã«ãŒãç¶ããŠãã ãç°æã ãã©ã³ãã³ã°ããŒã«ãŒå°å
¥ã®ãã£ããã«ãªãåºæ¥äºã¯ãã£ããã§ããïŒ ãæŠäºã ããªãæã®è©±ã ãã©ãå°äººæ°ã§ææçéã容æã ã£ãé ã¯ãã¹ã¯ã©ã MTGãããã£ãŠããªãã£ããã ãããã ãã©ããæãæ¥åå§èšã®äººããããã€ãªãŒã®MTGããã£ãæ¹ããããã£ãŠææãããŠãå°å
¥ãããçµæ§ããã£ããã ãã ããããã£ããã«ãä»ã¯èŠç¥ã£ã仲ã§ãèªåãã¡ã®ããæ¹ã§é£æºããŠãããã©ãä»ã®äººãå
¥ã£ãŠãããã§ããªããªãå¯èœæ§ããããäžã®äžã§äœ¿ãããŠããææ³ã詊ããŠã¿ãªããã ãããšãªã£ããããã§ãã©ã³ãã³ã°ããŒã«ãŒãå§ããŠã¿ãŸããã ãŒè©Šè¡é¯èª€ã®æ«ã«ç¶ããŠããç©ãªãã§ãããã§ã¯ããããªãã©ã³ãã³ã°ããŒã«ãŒã§ãã€ã³ãä»ããããæã«ãçãããæ°ãã€ããŠããããšã¯ãããŸããïŒ ãæŠäºã å人çã«ã¯ãä»ã®äººãæ°ã¥ããªãããšãææããããã«ããŠãããããããã©ã³ãã³ã°ããŒã«ãŒããã£ãŠãŠæ¥œãããšããã§ãããããããããèŠèœãšããŠãããã£ãŠãããŠçãšéãæ°å€ãåºããŠãããªãã»ã©ã確ãã«ãã£ãŠèšãããããããããçºèšãããããšã§ãPJãžã®ç解床ã瀺ããããã ãã©ãã¿ããªã¯ã©ãã§ããïŒ ãè²æ²¢ã åã¯ã¹ã³ãŒããæ°ã«ããŠçªã£èŸŒãããšããã®ããã¡ã³ããŒåå£«ã§æ°å€ã倧ããéãæã¯ãããèããŠã¿ããšãŽãŒã«ã®å®çŸ©ãããããã§éã£ãããããã ãããããã£ãŠåã
ãæªãããããªããŠããããã察象ãšãªã£ãŠããã¹ããŒãªãŒã®ãŽãŒã«ãäžæçãªããšãåå ãªã®ã§ãããã¯æ°ã«ããããã«ããŠããã ãç°æã æ°ãã€ããŠããããšã§ã¯ãªããã©ãåã®PJã¯ãèŠæ±ãããŠãã仿§ãè€éãªããšãå€ããŠããããã€ã³ãä»ãããããšããŸã äžç¢ºå®ãªèŠçŽ ããã£ããªããšæ°ã¥ãããšãããããã®æ©äŒã®ãããã§ããã仿§ã®æŒããæœ°ãããŠããããè¯ã詊ã¿ã ãšæããŸããã倧谷接ããã¯ïŒ ã倧谷接ã ããããªãéšåãã¯ã£ãããããããã«æ°ãã€ããŠããŸãããçµæ§ã¿ããªæé»ã®äºè§£ã§åã£ã¡ãããã©ããã確èªãããããã§ãªãã£ãã£ãŠããšãããã®ã§ã ãŒãªãã»ã©ãå®éã«äœ¿çšããäžã§ãå°è±¡ã ã£ãåºæ¥äºã¯ãããŸããïŒ ã倧谷接ã ããããåã¯ããã©ã³ãã³ã°ããŒã«ãŒãšããæåãç¥ããªããŠãWESEEKã«å
¥ç€ŸããŠããå§ããŠè§Šãããã§ããããã®æå³ã§å
¥ç€Ÿåœæã¯æ°é®®ã ã£ãããããããªã«éçºè
å士ãè¿ãã§ãäžã€ã®ãŽãŒã«ã«å¯ŸããŠåããã®ãèŠãã®ã¯ãéåžžã«çããæåã ãªããšèšãææ³ãæ±ããŸãããã ㌠ãããªãã§ããããŠã£ãããITäŒæ¥ã ãšã©ã®äŒç€Ÿã§ãåãå
¥ããŠããã®ããšæã£ãŠããŸããã ã倧谷接ã ä»ç€Ÿã ãšãæ©èœãPJããšã«åæ¥ã«ãªã£ãŠãããããã®ã§ã人æ°ãå°ãªããŠå£é ã§ãããåãããæžãããšããããã§ãããã ããããããªé¢šã«ã²ãŒã ã¿ããã«ãäžã€ã®å Žæã«éãŸã£ãŠã¯ã€ã¯ã€ããããšã¯ãªãããã ãæŠäºã åããã¯ãâç¡éã«ãŒãâã«ã€ããŠè©±ããããä»åã®ãªãªãžãã«ã«ãŒãã§ã¯äœããªãã£ããã©ãæåã«è²·ã£ãæ¢è£œåã«ã¯âç¡éã«ãŒãâãšâ?ãã€ã³ãã«ãŒãâããã£ããã ããããããã¯ãèŠç©ãããã§ããªããææè¡šç€ºã®ã«ãŒããªãã ãã©ãåã¯éãã ãšæãã äŸãã°ã仿§ãå
šç¶æ±ºãŸã£ãŠããªããŠãããæ±ºããããªãã§ãããããããŸãããã£ãŠèªä¿¡æã£ãŠåºããªããããã©ãã¿ããªèããŠãã®ã«ããèªåã«ã¯ã¹ãã«ããªãããèŠç©ããã§ããŸãããã¯ãªãã ãããã£ãŠæãããã«ããŠãã ãæåïŒãã€ã³ãã«ãŒããåºããŠã次ã«ã¿ããªãåºããããªæ°åãåºããŠããããåšããåæã«èŠç©ããããŠãããŸãããã ãšãåã¯æ¬åœã«æŠåã«ãªã£ãŠãã®ããšæãããããããã¡ãããšèããããããã«ãç¡éã«ãŒããšïŒãã€ã³ãã«ãŒãã¯ãªãã¹ã䜿ããªãããã«ããããšæç€ºããŠããã ãã®åãçããã£ããã«ãŒããåºãããšã§ããã©ãããã€ãã§ã¢ã«æèŠãèšããå ŽãšããåŽé¢ãããããã ãå€§è°·æŽ¥ã æ®æ®µã¯äžåžã«é æ
®ãã¡ãã人ãããŸããããã§ããã€ã³ãä»ãã§ã¯ããåãèšã£ãŠããããšã¯ããããªããã£ãŠæ®éã«å
茩ã«èšãããã¡ã³ããŒãç察çã«ãã¹ããŒãªãŒã俯ç°ã§ããããæ©äŒã ãšæããŸãã ãŒããã§ã¯æåŸã«ãäŒç»å®€ãšããŠãèããããã®ã§ããâŠãä»åäŒç»å®€ããªãªãžãã«ã®ã«ãŒããäœæããŸãããããªã³ã©ã€ã³ã®ã«ãŒãããããæå
ã«ãã£ãæ¹ããããã®ã§ããïŒ ãè²æ²¢ã ãªã³ã©ã€ã³ã ãšããŒã«ãŒæã¯ãªããããã«ãŒããããªãããã ãæŠäºã ããã ããããšã¯ã察é¢ã ãšãã¡ã³ããŒã«ã«ãŒããåºããŠããã£ãæã«ã衚æ
ãã»ããã§èŠãããèªä¿¡æã£ãŠåºããŠããã®ãããããããšåšãã䌺ã£ãŠããããããã®ãéãããªã ã倧谷接ã éçºã¡ã³ããŒã®è¡šæ
ãåããã®ã¯éåžžã«è¯ãã§ããããã ã§ããç¡æ©è³ªã«ãªããããã®ã§ã ãŒãªãã»ã©ãã³ããçŠã§é£ãããšããããããŸããããã²å¯Ÿé¢ã§äœ¿çšããŠããã ãããã§ããã ãšã³ãžãã¢ã®æ¹ã
ã«çŽæ¥ã話ãèããŠããã©ã³ãã³ã°ããŒã«ãŒã¯ããå人ã®èªç«ãä¿ããšåæã«ãããŒã ã¯ãŒã¯ãåæ»ã«ããåããããããšç¥ããŸããããã©ã³ãã³ã°ããŒã«ãŒãã©ã®ããã«ä»äºã«æŽ»ããã®ãããšã³ãžãã¢ã§ãªãç§ã§ãã€ã¡ãŒãžãæ·±ãŸã£ãŠããã£ãã§ãïŒ å¶äœç§è©± 次ã«ãWESEEKãªãªãžãã«ã®ãã©ã³ãã³ã°ããŒã«ãŒã«ã€ããŠãäŒãããŸãããã©ã³ãã³ã°ããŒã«ãŒãã¯ããã瀟å
ã°ããºã¯äŒç»å®€ã®äž»å°ã§äœæããŸããããã³ã¹ã¢ã¬ã瀟ã®çããã«ãããããåæ·»ããããã ããŸãããã³ã¹ã¢ã¬ã瀟ããã¯ãããã¶ã€ã³ãäž»è»žã«æ®ããã¯ãªãšã€ãã£ãã«ã³ãããŒãã§ãæ®æ®µã®æ¥åã§ããããããµããŒãããŠé ããŠããŸãã ïŒã³ã¹ã¢ã¬ã瀟㮠HP ã¯ãã¡ãïŒ ãšããããšã§ååéçºã®äžå¿ãšãªã£ãæŠç¥äŒç»å®€ã®å€ªç°ãããšãã³ã¹ã¢ã¬ã瀟ãã¶ã€ããŒã®åœ¢éšããã»æŒæšããã®äžäººã«ãã«ãŒãã®å¶äœèæ¯ã«ã€ããŠã話ã䌺ã£ãŠã¿ãŸããã ïŒå·ŠããïŒå€ªç°ãããæŒæšããã圢éšãã ãŒã¯ããã«ããã©ã³ãã³ã°ããŒã«ãŒäœæã«è³ã£ãçç±ãæããŠããã ããŸãããïŒ ã倪ç°ã å
ã
ã¯ãšã³ããã°ãããŒã«ãŒã¿ãããªãäžè¬çãªããã«ãã£ãåè£ã«åºããŠãŸããããã äŒè°ã®éã«ããšã³ãžãã¢ãããã°ããºãšããŠããã©ã³ãã³ã°ããŒã«ãŒã話é¡ã«äžãã£ãŠãäŒç€Ÿã®ããŽãå
¥ããã䜿çšã®åºŠã«ã¢ããŒã«ã«ãªããã忥ããã€ã³ã¿ãŒã³çã«ãã¬ãŒã³ããã§ãããããšããããšã§æ±ºå®ããŸããã ãŒãããªæå³ããã£ããã§ããïŒçŸç©ãèŠãŸããããã·ã³ãã«ãã€WESEEKãããçŽ æµãªååã§ãããã©ã®ããã«ãã®ãã¶ã€ã³ã«æ±ºå®ãããã§ããïŒ ãæŒæšã åœåã¯ãã¶ã€ã³æ¡ãç§ãã¡ãšããšã³ãžãã¢ã®ãããããã®äžäººã§èããŠãããã§ãããã圌女ã¯å
ã
ãã¶ã€ã³ã«èå³ããã£ãã¿ããã§ãæã
ã«çµã¿èŸŒãŸãã圢ã§åå ããŠããŸããã ãããããã®ãã¶ã€ã³ã¯ããªããºãã®ã€ã©ã¹ã颚ãªãã€ã§ã圢éšããã¯çéãªãã®ãç§ã¯ãããçµµã䜿ã£ãŠWSããŽãæ°åã«ããããšèããŠããŸãããçµæ§ããããªæ¹åæ§ããã®ã¢ãããŒããã§ããŠãé¢çœãã£ãã§ãã æŒæšããæ¡ 圢éšããæ¡ ã倪ç°ã äŒç»å®€ãšããŠã¯ãæ°åã®èŠããããéèŠããŠã圢éšããã®æ¡ãæ¡çšããããšããçµè«ã«ãªããŸãããæŒæšããã®æ¡ããããé¢çœããã§ãããæ°åããããšèªã¿åãã¥ãããªãšæããŸãããããã¶ã€ããŒããŒã çã«ã¯ããã®çµæã§çŽåŸãããŸãããïŒ ãæŒæšã ã©ã¡ãã®æ¡ãã¯ãªãªãã£ã«ã¯èªä¿¡ããã£ãã®ã§ãããšã¯ã奜ã¿ã§éžãã§ããã ããã°ãšæã£ãŠããŸãããå®çšæ§ããã¶ã€ã³æ§ãåããã§ãåè
ãéžãã ã®ã ãªãšã ã圢éšã èªåã¯é¡§å®¢æºè¶³åºŠãæèããŠäœã£ãã®ã§ãå®çšæ§ãé«ããã®ã«ãªã£ãã®ããªãã©ãã ãã®ç©ãæ±ããããŠãããããããªãã£ãããå¥ããŠããããšã¯ãã䜿ãããããã¶ã€ã³ã«ä»äžããŸããã ãŒãã®ç¹ã§èšããšããã¶ã€ããŒã¯ãã£ãšåã£ãã°ããºãäœãããã£ããã©ãäŒç»å®€ããããããªã«å·¥æ°ããããªãã§æ¬²ããããšèšãèŠæããã£ããšãã話ãèãããã§ãã⊠ã圢éšã ãã¶ã€ããŒãããããšããå·¥æ°ãæããŠç°¡åãªãã®ã§è¯ããããšããæ³šæã¯èŠæãªãã§ããããã奜ããªã ããã ãã£ãŠæé«ã®ãã®ãäœã£ãŠããããšèšãããæ¹ãå¬ãããããã£ã¡ã§è§£éããã¡ã ã ãã©äŒç»å®€ãšããŠã¯ãã°ããºéçºã«ãªãœãŒã¹ãå²ããããŠæ¬æ¥ã«æ¯éãããããããªãã®ã§ããæäœéã®å·¥æ°ã§è¯ããã®ãããšããè²»çšå¯Ÿå¹æãéèŠããŠãããã ãšæããŸããããã®ãºã¬ã§ãããã ã倪ç°ã ããã§ãããããã«ãäŒç»å®€ãšã³ã¹ã¢ã¬ããåããŠé¢ãã£ãã®ããã®äŒç»ã ã£ãã®ã§ã仿ããšé£æºäžè¶³ã ã£ããªãšããããªã«æ°è»œã«ã€ã³ã¿ãã¥ãŒã«åŒã¹ãçžæã ãšæã£ãŠãªãã£ãããåœåã®æ³å以äžã«ãã¶ã€ããŒã®çãããè²ã
èããŠãã ãã£ãŠâŠããã¡ãããã®ãããã§ããã¶ã€ããŒã®æ¹ã
ã«ããæãã€ããªãæ¡ãããããã§ããŸããããçŽ æµãªã°ããºãã§ããŠæé£ãã£ãã§ãã ãŒã¡ãªã¿ã«ãä»åŸWESEEKã°ããºãäœããšãããäœãäœããããã®ã¯ãããŸããïŒ ãæŒæšã WESEEKå
šäœã§ã¯ãªãGROWIã®ã°ããºãäœã£ãŠã¿ããã§ããä»ããã®ã¯æšå·¥ããŽãšã¹ããã«ãŒã§ããããGROWIããã§äœãã§ããªãããªã ã圢éšã åãå人çã«ããããã®ã¯ãããŒããŠã§ã¢ããPCã®äžã§å®çµãããã®ããªãäŸãã°ãã¢ãŒã·ã§ã³ã°ã©ãã£ãã¯ã䜿ã£ãWESEEKããŽã®ãªãŒããã³ã°ã ãŒããŒããGROWIãµãŒãã¹ã®èª¬æãšãã ã倪ç°ã äŒç»å®€ã§YouTubeãåèãããæœçãèããŠããã®ã§ãYouTubeã®OPãšããã£ããããã§ããã ãŒã話ãèããŠããŠãWESEEKã¡ã³ããŒã®å€ªç°ããã¯ãã¡ããããã¶ã€ããŒã®çãããæ¬åœã«ç±å¿ã§æé£ãã§ãããäœããäžç·ã«ãä»äºãã§ããŠå¬ãããªãšæããŸããããã©ã³ãã³ã°ããŒã«ãŒã¯åããŠã®ã°ããºäœæã§èŠåŽãå€ãã£ããšæããŸããããããæŽ»ãããŠããã«çŽ æµãªã°ããºã瀟å
ã»ç€Ÿå€ã«æäŸããŠããããã§ãã äŒç»å®€ã§ãã©ã³ãã³ã°ããŒã«ãŒãã£ãŠã¿ãïŒ ããŠããããŸã§ãã©ã³ãã³ã°ããŒã«ãŒã«ã€ããŠæžããŠããŸããããå®éã«ãã£ãŠã¿ãªããšããããªãïŒãšããããšã§ãäŒç»å®€å
ã§ããã©ã³ãã³ã°ããŒã«ãŒã«ããå·¥æ°èŠç©ãããäœéšããŠã¿ãããšã«ããŸããã ä»åã®ãé¡ã¯ ãäŒç»å®€ãèšç»ããŠããInstagramã®æçš¿ã ã«ã€ããŠã Twitterã§ã®1æçš¿ãã2ããšããæã«ãInstagramã®1æçš¿ã¯ã©ããããã®æ°åã«ãªãã ãããïŒãšã«ãŒããåºãåããŸããã 1åç®ã®çµæã¯ 5ã»5ã»13 ïŒ äœãšååãã倧ããªå·®ãã€ããŠããŸã£ãã®ã§ãã 3人ããåºãããã«ãŒãã¯âŠïŒ 13ãåºããã€ã³ã¿ãŒã³çã®å®å·ããã¯ãInstagramã®äŒç»æ¡ããŸãã«ç·Žã£ãŠããæäžã 話ãèããŠã¿ããšãèªåããã®æœçãé²ããŠããããšãããã£ãŠã¿ãããšããªãäºãžã®äžå®ãå«ããŠã®ã13ããšããçµæã ãšããããŸããã äžå®ã¯äžæŠçœ®ããŠãããŠãçŽç²ã«æçš¿ã«å¯Ÿããæéã»ã³ã¹ããšããŠåºãåããïŒãš2åç®ãè¡ã£ããšããã 5ã»5ã»8 ã«ã ãäºãã«ãã®æ°åãåºããçç±ã話ãããšãäŸãåãã5ãã§ãèããŠããäºã¯éããã ãªãŒãšããçºèŠããããŸãããã¿ããªã§çŽåŸããäžã§ã5ãš8ãšããé£ãåãæ°å€ã«åæããã®ã§ãä»åã¯ã8ãã€ã³ããã®æ¡çšã§ãïŒ 1åç®ã«13ãåºãæã«ã¯ã³ã£ããã ã£ãã®ã§ããããã®åäžå®ãæããŠããããšãããããŸãããããäºãã«å¿é
ããŠããããšã話ãåãããæ©äŒã«ãªã£ããšæããŸãã æã£ããããç°¡åã«ãããããäºãã®èããå
±æã§ããã®ã§ãä»åŸã¯äŒç»å®€ã§ããã©ã³ãã³ã°ããŒã«ãŒã掻èºããæãå¢ããŠããããïŒ éãšã³ãžãã¢ã®ç§ãæããããš ç§ã¯ãä»åã®èšäºãæžããŸã§ãã©ã³ãã³ã°ããŒã«ãŒã瀟å
ã°ããºãšããèŠç¹ã§ããæããŠããŸããã§ãããã§ããã©ã®ãããªç®çã§äœ¿ãããŠããã®ãããäŒç€Ÿã«ã©ããªã¡ãªãããäžããã®ãããšãããæ¥é ããåãçµãã§ãããšã³ãžãã¢ã®çããã®å§¿å¢ãç¥ã£ãŠãä»äºäžã®éèŠãªéå
·ãšããèªèã«å€ãããŸããã 補äœç§è©±ããä»ã°ããºãäœã£ãŠããç«å ŽãšããŠãšãŠãåèã«ãªããŸããããã äœãã®ã§ã¯ãªããŠã现éšãŸã§ãã ããããšã§çã®å¿ã«æ®ã£ãŠæ¬²ãããšæããŸãã ã€ã³ã¿ãã¥ãŒã«ãååããã ããçãããããããšãããããŸããïŒ ããŸã æåŸã«ãç§ãæºãã£ãŠããååã«ã€ããŠã玹ä»ãããŠãã ããïŒ ãã©ã³ãã³ã°ããŒã«ãŒã¯ãããŸã§ç€Ÿå
åãã®ã°ããºã§ãããWESEEKã§ã¯ç€Ÿå€ã«ãååã販売ããŠããŸããä»è²©å£²ããŠããã®ã¯ãããŒã«ãŒãšãã°ã«ãããã©ã¡ãããšã³ãžãã¢ã®æ¹ã«ããããããªããã ãããè©°ãŸã£ããçŽ æµãªãã¶ã€ã³ã§ãã ãã¡ã ã®èšäºã«ãŠè©³ããåå玹ä»ãããŠããŸãã®ã§ããã²ã芧ãã ãããã ãããŠãèå³ã®ããæ¹ã¯ ãã¡ã ã®è²©å£²ãµã€ãããã賌å
¥ãé¡ãããŸãïŒ ç©èªããŒã«ãŒ Mom's Spaghetti Code Mug
collletã®ãã°ã€ã³åŸã®ããŒã ç»é¢ ãã®æçš¿ã¯ãåŒç€ŸãæäŸãã WESEEK TECHéä¿¡ ã®äžç°ã§ãã WESEEK TECHéä¿¡ãšã¯ãWESEEKã®ãšã³ãžãã¢ããã£ããã¢ããããæè¡ã«é¢ããæ
å ±ããtechããã°ãéããŠå®æçã«çºä¿¡ããŠãããã®ã§ãã ã¯ããã« ããã«ã¡ã¯ãã·ã¹ãã ãšã³ãžãã¢ã®äœè€ã§ãã ãã®èšäºã§ã¯ WESEEK瀟å
ééã§ããWESEEK DOLLERïŒWSDïŒã®æµéãéå§ããŠçŽ1幎çµã¡ããã®äžã§çºçããHyplerledgerFabricã«ãããèšŒææžã®æå¹æéåããã©ãã«ãšæ¯ãè¿ã£ãŠåãã£ãããšãç©èªé¢šã«ç޹ä»ããŸãã WESEEK DOLLER(WSD)ãšã¯WESEEK瀟å
ã§æµéãã貚幣ã®ããšã§ãã詳现ã«ã€ããŠã¯WESEEK Blogã® WESEEK DOLLOR ã 瀟å
ééWSDããªã¹ã¿ãŒã ãã芧ãã ããã WESEEK DOLLOR 瀟å
é貚WSDããªã¹ã¿ãŒãïŒ WSDã¯ãããã¯ãã§ãŒã³ãã¬ãŒã ã¯ãŒã¯HyperledgerFabricã䜿ã£ãŠããŸããHyperledgerFabricã«ã€ããŠã®æŠèŠãã¢ããªã±ãŒã·ã§ã³éçºæ¹æ³ã«ã€ããŠã¯ãQiitaã®èšäºãã芧ãã ããã Hyperledger fabric ã§å§ãããããã¯ãã§ãŒã³ã¢ããªã±ãŒã·ã§ã³ (1/3) Hyperledger fabric ã§å§ãããããã¯ãã§ãŒã³ã¢ããªã±ãŒã·ã§ã³ (2/3) Hyperledger fabric ã§å§ãããããã¯ãã§ãŒã³ã¢ããªã±ãŒã·ã§ã³ (3/3) HLFã³ã³ããŒãã³ãèšŒææžã®æå¹æéåã HyperledgerFabricïŒHLFïŒã«ãããããŒã¿ãéä¿¡ã®ä¿¡é Œã«ã¯èšŒææžãçšããããŸãã èšŒææžã«ã¯åœç¶æå¹æéããããŸãã®ã§ããã®æå¹æéãåããå Žåã«ã¯ã·ã¹ãã ã«åœ±é¿ãåºãŸãã ããæ¥ãããã¯èµ·ãã£ã ããæ¥ã®ããšã ãWSD ãæµéããã¢ããªã±ãŒã·ã§ã³ïŒcollletïŒã«ãã°ã€ã³ãããšããã£ãšããŒãã£ã³ã°ã¢ã€ã³ã³ã衚瀺ããããŸãŸã«ãªã£ãŠããŸããŸããããã ã ãšããé£çµ¡ãåããŸããã collletã§ã¯ãã°ã€ã³ãããšææããWSDã®éé¡ãšããã®ä»ã®æäœç»é¢ãžã®ãªã³ã¯ã衚瀺ãããŸããïŒæ¬¡ã®ç»ååç
§ïŒ collletã®ãã°ã€ã³åŸã®ããŒã ç»é¢ ãã®ç»é¢ã®äžã«ãã"16"ã¯ãèªèº«ãææããWSDã®éé¡ã§ãã èªèº«ãææããWSDã®éé¡ã¯HyperledgerFabricã«ãã©ã³ã¶ã¯ã·ã§ã³ãéã£ãçµæã衚瀺ãããŸãã colllet frontãHyperledgerFabricã«ãã©ã³ã¶ã¯ã·ã§ã³ãéãéä¿¡çµè·¯ã¯æ¬¡ã®ããã«ãªã£ãŠããŸãã # colllet front # â # ïœ(REST APIã¢ã¯ã»ã¹) # â # colllet back # â # ïœ(HLFãã©ã³ã¶ã¯ã·ã§ã³) # â # HyperledgerFabric(Orderer) # â # ïœ(HLFã³ã³ããŒãã³ãééä¿¡) # â # HyperledgerFabric(Peer) # â # â # Chaincode(WSD) ãã°ã確èªãããšãfrontããbackãžã®ã¢ã¯ã»ã¹ãè¡ãããåŸã«HLFãã©ã³ã¶ã¯ã·ã§ã³ãå¿çããŠãããããŸã client identity expired ã®æåããããŸããã ã©ãããèšŒææžã®æå¹æéãåããŠããŸã£ãããã§ãã [comm.grpc.server] 1 -> INFO 1922 streaming call completed grpc.service=orderer.AtomicBroadcast grpc.method=Deliver grpc.peer_address=***:33312 grpc.code=OK grpc.call_duration=928.91µs [common.deliver] deliverBlocks -> WARN 1923 [channel: ***] Client authorization revoked for deliver request from ***:33330: client identity expired 74h41m17.76951635s before HLFã·ã¹ãã ã§çšããèšŒææžã¯è€æ°ãããŸãã ãŠãŒã¶ãŒèšŒææž HLF CAèšŒææž HLF OrdererèšŒææž HLF PeerèšŒææž åèšŒææžã確èªãããšãHLF CAèšŒææžãHLF OrdererèšŒææžãHLF PeerèšŒææžãåããŠããããšãåãããŸããã ïŒWSDã·ã¹ãã ãå°å
¥ããæç¹ã§çºè¡ãããã®éã«æå¹æéã1幎ã§ãã£ãããã«åææã«åããŸãããåœç¶çºè¡ããææãšæå¹æéã«ãã£ãŠã¯ç¶æ³ãå€ãããšæããŸãïŒ HLFã³ã³ããŒãã³ãã®èšŒææžãåãããšãã©ã³ã¶ã¯ã·ã§ã³åŠçãã§ããªããªããŸãã ãã®çµæãcollletã«ãã°ã€ã³ããŠããã®åŸã®ç»é¢ã衚瀺ã§ããªããªã£ãŠããããã§ããã ïŒãã®ãããªå Žåãç»é¢äžã§ãšã©ãŒã衚瀺ããã¹ãã§ããäœããã¿ã®çãã¯ç€Ÿå
å©çšéå®ãšããããšã§æž©ããç®ã§èŠãŠãã ãã ïŒ ãæéããã ããŸããšåç ãšããããèšŒææžã®æŽæ°ãå¿
èŠã§ããããšã¯åãããŸãããWSDã¹ã¿ãŒãæããããããã®æ¥ãæ¥ããšã¯åãã£ãŠãŸãããããã¯ãææ¬ã§ãã èšŒææžãåçºè¡ããã«ããŠãæ€èšŒããæéãæ¬²ããã£ãã®ã§å©çšè
ã«ã¯ãã°ããåŸ
ã£ãŠããã鱿«ã«å¯Ÿå¿ããããšã«ããŸããã HLFã³ã³ããŒãã³ãèšŒææžã®åçºè¡ HLFã§ã¯èšŒææžãçºè¡ããæ¹æ³ãšããŠãenrollãšreenrollããããŸãã enrollã¯èšŒææžã®çºè¡ã§ãããreenrollã¯èšŒææžã®åçºè¡ã§ãã fabric-ca-client ãšããããŒã«ã䜿ããšãããã®åŠçãã§ããŸãããã·ã¹ãã æ§ç¯æã«èšŒææžåããèµ·ãã£ãå Žåãæ³å®ããŠreenrollãå®è¡ããããšããããä»åãããã§è§£æ¶ã§ããã ãããšæã£ãŠãŸãããèšŒææžã®æå¹æéãåãããšãã¯reenrollåºæ¥ãªãããšãåãããŸããã ããèãããšãããããããšæããŸãããenrollãããšãã«ãã¹ã¯ãŒããæå®ããããšããããã®ãã¹ã¯ãŒããæ®ã£ãŠããã°åçºè¡ã§ãããã®ã ãšåéããããŠããŸããã ããŠãreenrollåºæ¥ãªããšãªããšenrollãããããããŸããã ããã§ãHLFã§ã¯åäžIDã«ãããèšŒææžã®çºè¡åæ°ãå¶éãããŠããŸãã fabric-ca-client identity list ã§è¡šç€ºããã"Max Enrollments"ãããã«åœãããŸãã $ fabric-ca-client identity list Name: admin, Type: client, Affiliation: , Max Enrollments: -1, Attributes: [{Name:hf.IntermediateCA Value:1 ECert:false} {Name:hf.GenCRL Value:1 ECert:false} {Name:hf.Registrar.Attributes Value:* ECert:false} {Name:hf.AffiliationMgr Value:1 ECert:false} {Name:hf.Registrar.Roles Value:* ECert:false} {Name:hf.Registrar.DelegateRoles Value:* ECert:false} {Name:hf.Revoker Value:1 ECert:false}] HLFã³ã³ããŒãã³ãã®èšŒææžãçºè¡ããé㯠fabric-ca-client register ã³ãã³ãã䜿ããŸããã ãã®éã«"Max Enrollments"ã¯æå®ããŠããªãã£ãã®ã§CAã«èšå®ãããæå€§çºè¡åæ°ãdefaultå€ã«ãªãã幞ãã«ã"-1"ïŒç¡éïŒã§ããã ã²ãšãŸãenrollããã°èšŒææžã®çºè¡ãã§ããŸãã åŸã¯ã²ãããèšŒææžãçºè¡ããŠã·ã¹ãã ã§å©çšããèšŒææžãšçœ®ãæããã°äœæ¥ã¯å®äºã§ããæéã¯ããããŸããå調ãªäœæ¥ã§ããã æ¯ãè¿ã£ãŠåãã£ãããš å°å³ãªèšŒææžã®çºè¡ãšçœ®ãæããç¹°ãè¿ããŠç¡äºã«collletã埩æ§ããŠãã©ã³ã¶ã¯ã·ã§ã³åŠçãè¡ããããã«ãªããŸããã æ¯ãè¿ã£ãŠåãã£ãããšã¯æ¬¡ã®ãšããã§ãã HLFã³ã³ããŒãã³ãã®èšŒææžæå¹æéãåãããšå
šãŠãŒã¶ãŒããã©ã³ã¶ã¯ã·ã§ã³åŠçãã§ããªããªã èšŒææžã®æå¹æéãåãããšreenrollåºæ¥ãªããªã èšŒææžã®çºè¡åæ°æéã¯"Max Enrollments"ã«ããçºè¡æã«èšå®ããã fabric-ca-client ã³ãã³ãã§registeræã«æå€§çºè¡åæ°ãæå®ã§ãã fabric-ca-client ã³ãã³ãã§æå€§çºè¡åæ°ãæå®ããã«registerããå Žåã¯CAã®æå€§çºè¡åæ°ãdefaultå€ã«ãªã ãŠãŒã¶ãŒèšŒææžã®æå¹æéåã HLF ã³ã³ããŒãã³ãã®ä»ã«ãŠãŒã¶ãŒã«ãèšŒææžãçºè¡ãããŸãã åã³ãããã¯èµ·ãã£ã ããæ¥ãèµ·ãã£ãŠããããéããªãæ¥ãåã³ããã¯èµ·ãããŸããã ããããŠãŒã¶ãŒã§ colllet ã«ãã°ã€ã³åºæ¥ãªããªããŸãããä»ã®ãŠãŒã¶ãŒã§ã¯ãã°ã€ã³ã§ããã®ã§ããäœãåãããŸããã»ã»ã»ïŒ ã ãšããé£çµ¡ãåããŸããã ããã§ãããŠãŒã¶ãŒèšŒææžã®æå¹æéãåããŠããŸã£ãããã§ãã ãŠãŒã¶ãŒèšŒææžãåããå ŽåãHLFã«ãããŠãã©ã³ã¶ã¯ã·ã§ã³åŠçãã§ããªããªããŸããäœããHLF ã³ã³ããŒãã³ãã®èšŒææžãåããå Žåãšç°ãªããæéãåãããŠãŒã¶ãŒã ãããã©ã³ã¶ã¯ã·ã§ã³åŠçãã§ããªããªããŸãã æå¹æéã¯ãŠãŒã¶ãŒãç»é²ãããæ¥ãã1幎éã ã£ãããããŠãŒã¶ãŒã«ãã£ãŠã¯ãã°ã€ã³ã§ããã»ã§ããªããéããŸããã ãã®æã¯å
æ¥ã®HLFã³ã³ããŒãã³ãã®èšŒææžæå¹æéãåãããšãã«æéäžèЧãèŠãŠæ°ãä»ããŠããŸãããã fabric-ca-client ã³ãã³ããéãšã³ãžãã¢ã«å®è¡ããŠããã£ãŠåçºè¡ããã®ã¯çŸå®çã§ã¯ãªãã®ã§ã¢ããªã±ãŒã·ã§ã³åŽã§æ©èœå®è£
ããå¿
èŠããããšèããŠããŸããã ãŠãŒã¶ãŒèšŒææžã®åçºè¡ HyperledgerFabricã«ãããŠã hf.Type ãšããèšŒææžã®æ¡åŒµå±æ§ã䜿ãããšã§åé¡ããããšã¯å¯èœã§ãããåºæ¬çã«ãŠãŒã¶ãŒèšŒææžãHLFã³ã³ããŒãã³ãã®èšŒææžãåãã§ãã ãã®ããããŠãŒã¶ãŒèšŒææžã®çºè¡ã»åçºè¡ãHLFã³ã³ããŒãã³ãã®èšŒææžçºè¡ãšããæ¹ã¯åãã§ãã ããããçºè¡ããæ¹æ³ã®éãã§"Max Enrollments"ãéã£ãã®ã§ãããã collletã§ã¯ã¢ããªã±ãŒã·ã§ã³ã§ãŠãŒã¶ãŒç»é²ãã§ããŸãã colllet backã¯Node.jsã§å®è£
ãããŠããã Hyperledger Fabric SDK for Node.js ã䜿ã£ãŠããŸãã ããã§ãregisterãããéã®èšŒææžã®çºè¡åæ°æéãããã©ã«ãã§ã¯1ã ã£ãã®ã§ããïŒè©²åœããã³ãŒãã¯æ¬¡ã®ãªã³ã¯å
ãåç
§ïŒ https://github.com/hyperledger/fabric-sdk-node/blob/82199a83a2d4ffd026fbcbdb33effaaa88d97950/fabric-ca-client/lib/FabricCAServices.js#L133 ã€ãŸãèšŒææžã®æå¹æéãåããå Žåã¯reenrollåºæ¥ããæŽã«ã¯enrollã¯æ¢ã«è¡ãããŠããã®ã§åã³enrollããããšãã§ããŸããã ã§ã¯ã©ãããã°ããã®ãã調ã¹ãŠãããšããããã®"Max Enrollments"㯠fabric-ca-client identity modify ã䜿ã£ãŠå€æŽåºæ¥ãããšãåãããŸããã ããã§ã以äžã®å¯Ÿå¿ãããããšã«ããŸããã æ¢åãŠãŒã¶ãŒã®"Max Enrollments"ã"-1"ãžå€æŽãã ã¢ããªã±ãŒã·ã§ã³åŽã§ãŠãŒã¶ãŒç»é²æã«"Max Enrollments"ã"-1"ã«ãã ãŠãŒã¶ãŒãã°ã€ã³æã«èšŒææžã®æå¹æéã«å¿ããŠãèšŒææžãå床çºè¡ïŒenrollã§ããreenrollã§ã¯ãªãïŒãã æ¯ãè¿ã£ãŠåãã£ãããš HLFãŠãŒã¶ãŒã®èšŒææžæå¹æéãåãããšè©²åœãŠãŒã¶ãŒã¯ãã©ã³ã¶ã¯ã·ã§ã³åŠçãã§ããªããªã Hyperledger Fabric SDK for Node.jsã®FabricCAServices#registerã¯defaultã§"Max Enrollments"ã"1"ã«ãªã fabric-ca-client identity modify ã«ãã"Max Enrollments"ã¯å€æŽã§ãã $ fabric-ca-client identity modify --maxenrollments -1 [USER] ãããã« HyperledgerFabricã«ãããŠèšŒææžã®æå¹æéãåãããšãã®åœ±é¿ãšå¯Ÿå¿æ¹æ³ã«ã€ããŠç޹ä»ããŸããã ãŸãæ©äŒããããŸããããããã¯ãã§ãŒã³ã®ããŒã¿æ§é ãªã©ã玹ä»ããããšæããŸãã
ãã®èšäºã¯ã2021/11/25 ã«è¡ã£ã WESEEK Tech Conference #15 ã®å
容ããŸãšãããã®ã§ãã ã¯ããã« ãããã£ãæ©ã¿ãçåããæã¡ã®æ¹ã¯ããŸãããïŒ ãµãã¹ã¯ãªãã·ã§ã³å課éã®ãµãŒãã¹ãèããŠãããæ±ºæžãã©ãããã°ããã®ãåãããªã ã¯ã¬ãžããã«ãŒããéè¡ãªã©ã絡ãã§ãããšã決æžã®ç®¡çãå€§å€ ããããå®ææ¯æã£ãŠã©ããã£ãŠå®çŸããã®ïŒ ãããªæ©ã¿ãè§£æ¶ããŠããã Stripe ãšãããµãŒãã¹ãã WESEEKããµãŒãã¹ãæäŸãã GROWI.cloud ãå®äŸãšããŠã玹ä»ããŸãã ãStripeããšã¯ Stripe ã¯ãç±³ Stripe 瀟ãæäŸãããªã³ã©ã€ã³æ±ºæžãµãŒãã¹ã§ãã ã©ã€ãã©ãªã»API ãçšæãããŠããããµãã¹ã¯ãªãã·ã§ã³åã®æ±ºæžãè¡ãããã°ã©ã ãç°¡åã«ãµãŒãã¹ã«çµã¿èŸŒãããšãã§ããŸãã å§ããã®ããšãŠãç°¡å ã¡ãŒã«ã¢ãã¬ã¹ããããã°ã誰ã§ãç°¡åãã€å³æã«ãã¹ãã¢ã«ãŠã³ããçºè¡ãå¯èœã§ãæãç«ã£ããšåæã«ããã«æ€èšŒãéå§ã§ããŸãã ãŸããæ¬çªç°å¢å©çšã®ç³è«ã«ã€ããŠãå¿
é é
ç®ãåããŠéä¿¡ããã ãã§å®äºããã®ã§ãæç1æ¥ã§å©çšéå§ã§ããŸãã Stripeã®ããŒã¿ã®çš®é¡ äž»èŠãªããŒã¿ã®çš®é¡ Stripe ã§ãµãã¹ã¯ãªãã·ã§ã³åœ¢åŒã®èª²éãçµãããã«ãç¥ã£ãŠããå¿
èŠããã5ã€ã®ããŒã¿ã®çš®é¡ã«ã€ããŠã玹ä»ããŸãã 顧客 (Customer) ã»ã»ã» äžé¡§å®¢ã衚ãããŒã¿ åå (Product) ã»ã»ã» æäŸããååã衚ãããŒã¿ æé (Plan) ã»ã»ã»ååã®æé Â¥ã / æ çã®ããŒã¿ ã€ã³ãã€ã¹(Invoice) ã»ã»ã»(æé à 顧客) è«æ±æ
å ±ã衚ã ãµãã¹ã¯ãªãã·ã§ã³(Subscription) ã»ã»ã»å®ææ¯æã衚ã äžèšã® 5 çš®ã®ããŒã¿ã®çµã¿åããã«ãã£ãŠãæçµçã«ãµãã¹ã¯ãªãã·ã§ã³ãæ§æãããŸãã ãµãã¹ã¯ãªãã·ã§ã³(Subscription)ã®ä»æ§ ãããããStripe ã«ããã Subscription ã®ããŒã¿ã®äžèº«ãæåã«ã€ããŠæ·±æãããŠãããŸãã ãµãã¹ã¯ãªãã·ã§ã³ãšããããŒã¿ã®äžèº«ã»æå é¡§å®¢ã®æ
å ±ãæã€ æéãšæéã®åºåããæéãã©ã³ãæã€ æéã®åºåãããšã«æ¯æãæ
å ±ãäœæãã ã€ãŸããã€ã³ãã€ã¹ã宿çã«èªåçæããŠå®ææ¯æãå®çŸããŠããã®ãã ãµãã¹ã¯ãªãã·ã§ã³ ããªã®ã§ãã ãŸãã課éãçºçããªã (0å課é) ã®æéãã©ã³ããµãã¹ã¯ãªãã·ã§ã³ã«èšå®ããå Žåã¯ã顧客æ
å ±ã«æ¯æãæ¹æ³ãç»é²ããå¿
èŠããããŸããã éçºè
åãæ
å ± ãã¡ããã§ããããã¹ãçã¢ã«ãŠã³ãã§ã¯åŒãèœãšããªã©ã¯çºçããŸããã ãã¹ãçã¢ã«ãŠã³ãã§ã¯ãStripe ãçšæãããããŒã®ã«ãŒãã䜿çšã§ããŸãã ãŸããæ¯æãã«æåããå Žåã®ã¿ãæ€èšŒããŠããŠã¯ãå®éã«éçšããããšã¯ã§ããŸããããïŒ ãããã£ãæ¯æãã«å€±æããå Žåã確èªããããã®ãããŒã«ãŒãããããŸãã ãŸãããã®çš®é¡ãè±å¯ã§ã倱æããŠããŸãçç±ãç°ãªããããŒã«ãŒããæ§ã
çšæãããŠããŸãã ããã§Stripeã®ä»æ§ã«ã€ããŠçå Q: æéã®éäžã§é¡§å®¢ããã©ã³ã倿Žãããã©ããªãã§ããããïŒ A: Stripe ãæ¥å²ãèšç®ãè¡ã£ãŠæ¬¡åè«æ±æ
å ±ãèªåæŽæ°ããŠãããŸã ãã£ãŠããµãŒãã¹æäŸè
ã課éåšããã·ãã¢ã«æ°ã«ããå¿
èŠããããŸãã Q: Stripeã®æé¡ã¯ãããïŒ A: æé¡ã®å©çšæã¯ãã·ã ããæ±ºæžæç«ããšã« 3.6% ã®ææ°æãããã èŠæš¡ã®å°ããªã¹ã¿ãŒãã¢ããã®ãµãŒãã¹ã»ã©å°å
¥ãããã SDK&ããã¥ã¡ã³ã Stripe ã§ã¯ãæ§ã
ãªçš®é¡ã®éçºè
ããŒã«ãæäŸãããŠããŸã CLI Node.js ã©ã€ãã©ãª VisualStudio Code ã®æ¡åŒµæ©èœ REST API ããã¥ã¡ã³ããå
å® API ããã¥ã¡ã³ããã¯ããã仿§ã«é¢ããããã¥ã¡ã³ããå
å®ããŠããŸãã æ¥æ¬èªçãçŸåšæŽåããã€ã€ãããŸãããåºæ¬ã¯è±èªçã翻蚳ãããã®ã§ããç¿»èš³ãæªå¯Ÿå¿ã®ç®æãèšèªã«ããè¥å¹²ã®è¡šçŸã®å·®ããããŸãã å®å¿ã®ãµããŒã åè¿°ã§äžå®ãèŠããæ¹ãããã£ããããšæããŸããããå®å¿ãã ããã éçºè
åãã®ãµããŒããæåããããã¥ã¡ã³ããèªãã§äžæãªç¹ãããã°ãæ°è»œã«ãµããŒããžè³ªåã§ããŸãã ãµããŒãã®äœå¶ æ¥æ¬èªã§ã®ãµããŒãã¯ã¡ãŒã«ã§ã®å¯Ÿå¿ãšãªããç¿å¶æ¥æ¥ã«åçããŠããã ããŸãã ãŸãæ¥ãã§ç¢ºèªãããå Žåã¯ãè±èªã§ããã°ãã£ããã§ã®ãµããŒã察å¿ãåããŠããã ããããããã®å Žã§äŒè©±ããªããäžæç¹ã®è§£æ¶ãé²ããããŸãã GROWI.cloudã§ã®å°å
¥äºäŸ GROWI.cloud ã§ã¯ãStripe ãæäŸããŠãã Node.js ã®ã©ã€ãã©ãªãStripe React Element ãšããã³ã³ããŒãã³ãã©ã€ãã©ãªã䜿çšããŠããŸãã ååãšãã©ã³ã®ç®¡çã«ã€ããŠã¯ããã©ã³ããšã®æéèšå®ããã€ã°ã¬ãŒã·ã§ã³çšãã¡ã€ã«ã«ãŸãšããŠäžå
管çããŠããŸãã ååã®è¿œå ãäŸ¡æ Œã®å€æŽãªã©ãçºçããéã«ã¯ã該åœãã¡ã€ã«ãæŽæ°ããã€ã°ã¬ãŒã·ã§ã³ãå®è¡ããŠããŸãã GROWI.cloud ã§ã¯é¡§å®¢ããµãã¹ã¯ãªãã·ã§ã³ãã€ã³ãã€ã¹ãã©ãæ±ã£ãŠããã®ãïŒ é¡§å®¢ã®äœæãããã©ã³ã®ãå¥çŽãŸã§ã®æµããã玹ä»ããŸãã ã¢ã«ãŠã³ãç»é²æã« Stripe äžã®é¡§å®¢æ
å ±ãäœæ const attachStripeCustomer = async(customer, option) => { const stripeCustomer = await stripe.customers.create({ email: customer.email, description: `Customer ID: ${customer.id}`, }); await customer.update({ stripeCustomerId: stripeCustomer.id }, option); }; åæã«èª²éé¡0åã®æéãã©ã³ã§ãµãã¹ã¯ãªãã·ã§ã³ãäœæ const attachStripeSubscription = async(customer, plan, options) => { return stripe.subscriptions.create({ customer: customer.id, items: [{ plan: plan.id }], expand: ['latest_invoice.payment_intent'], ...options, }); }; ç»é²å®äºåŸããŠãŒã¶ãŒã®ä»»æã®ã¿ã€ãã³ã°ã§ãã©ã³å€æŽ const changeSubscription = async(subscriptionId, subscriptionItems, options) => { await stripe.subscriptions.update(subscriptionId, { items: subscriptionItems, expand: ['latest_invoice.payment_intent'], ...options, }); }; æ¯æãæ
å ±ãæªç»é²ã®å Žåã¯ãç»é²å®äºåŸã«ãã©ã³å€æŽãå¯èœ Stripe React Element ãå©çšããŠããŸã Stripeã®äŸ¿å©æ©èœPickUp ããã·ã¥ããŒã Stripe ãèªåéèšããŠãããæ§ã
ãªããŒã¿ãé²èЧå¯èœ MRR (æéæšæºåç) ããã·ã¥ããŒããã確èªå¯èœãªææšã®äžã€ã§ããµãã¹ã¯ãªãã·ã§ã³ãç¶ç¶ããŠããå¥çŽå
šäœã®æåœããã®åçèŠèŸŒã¿ã衚ããŸãã ãµãã¹ã¯ãªãã·ã§ã³ãµãŒãã¹ãæäŸããå Žåãç¶ç¶ãèŠèŸŒããæéã®åçã¯å£²ãäžããšçŽçµããã®ã§ããµãŒãã¹æŠç¥ãèããææšãšããŠãéåžžã«éèŠãªææšã§ãã ã¯ãŒãã³ ç¹å®ã®æ¯æãã«å¯ŸããŠå²åŒãè¡ããã¯ãŒãã³ããçºè¡ããããšãã§ããŸãã å²åŒç 㯠MRR ã«åœ±é¿ããŸããã (æ£èŠã®éé¡éã MRR ã«åæ ããã) äŸ)æ°èŠã«å¥çŽããã 600 å/æ ãã©ã³ã« 50% å²åŒã¯ãŒãã³ãé©çšããå Žå â é¡§å®¢ã®æ¯æéé¡ã¯ 300 å/æ â MRR ã®å€å㯠+600 å ã¯ãŒãã³ã¯å²åŒãããæ¯æããéå®ããããããMRRãã€ãŸããæéæšæºåçããšããæ±ãã®äžã§ã¯ãæšæºã®å€ (ã¯ãŒãã³ã«ããå²åŒãé©çšããªãå€) ãåæ ããã仿§ã ãšèããã®ã劥åœã§ãã ãã©ã€ã¢ã« æéãèšããŠãæéãŸã§å²åŒããé¡ã§è«æ±ãè¡ããã©ã€ã¢ã«æäŸãè¡ãããšãã§ããŸãã ãã©ã€ã¢ã«äžã®ãµãã¹ã¯ãªãã·ã§ã³ 㯠MRR ã«åœ±é¿ããŸããã äŸ)600å/æãã©ã³ã50%å²åŒã§ãã©ã€ã¢ã«éå§ããå Žå â é¡§å®¢ã®æ¯æéé¡ã¯ 300 å/æ â MRR ã®å€å㯠±0 å ãã©ã€ã¢ã«ã¯ãåçã®èŠèŸŒã¿ããŸã ç«ã£ãŠããªãç¶æ
ã§ãåã®è¡šãéããã©ã€ã¢ã«äžãšããç¶æ³ãäœãåºããã®ã§ãã ãŸãããã©ã€ã¢ã«ãéå§ãããããã«ã¯ã顧客ããã£ã³ã»ã«ããããã®æ§ã
ãªæ¡ä»¶ãåæãæç€ºçã«æç€ºããå¿
èŠããããŸãã ããã¯ããã©ã€ã¢ã«äžã®é¡§å®¢ã¯æ£åŒãªé¡§å®¢ãšãªãåã®æ®µéã§ãåçãçŽæãããæ¬å¥çŽã§ã¯ãªããšæ±ããããããªã£ãŠãããšèããããŸãã ãã£ãŠããã©ã€ã¢ã«äžã®ç¶æ
ã§ã¯ãŸã ã(æé)æšæºåçããšããŠæ±ãããšãã§ããªããã MRR ã«åæ ãããªã仿§ã ãšèããã®ã劥åœã§ãã 倱æè«ãšå¯Ÿç åæç¡æãã£ã³ããŒã³ã«ããã倱æ [åé¡ãã®1]çªè²«å®è£
ã«ããèæ
®äžè¶³ã§ãæ¯æã倱æãé£çºããŠããŸã çµç·¯ ä»ãµãŒãã¹ã§åœããåãšãªã£ãŠãããååããç¡æïŒããšããè²©ä¿æœçã«ã€ããŠç€Ÿå
ãã匷ãèŠæããããåªå
ããŠå®è£
ããããšã«ãªããŸãã ããã§æã
㯠Stripe ã®ããã¥ã¡ã³ãã調æ»ããã¯ãŒãã³ãšããæ©èœãããããšãç¥ããŸã ãŸãã調æ»ã®çµæä»¥äžã®ä»æ§ãåãããŸãã ããã%ãªããã®ã¯ãŒãã³ãäœæããããšãã§ãã ã¯ãŒãã³ãé©çšããããšã§ã1åã®æ¯æãã«ãããŠå²åŒãé©çšã§ãã 100%ãªãã¯ãŒãã³ã䜿ãããšã§ãæ¯æãæ¹æ³ã®ç»é²ãªãã®ãŸãŸææãã©ã³ãèšå®ã§ãã å®è£
ãå®äºãææ°æã
ãšåæç¡æãã£ã³ããŒã³éå§ã®ãã¬ã¹ãªãªãŒã¹ãæã¡ãŸããã ãã£ã³ããŒã³ã®å¹æããããåŸã
ã«ãŠãŒã¶ãŒãå¢ããŠããã»ã»ã»ããããã§äºä»¶ãèµ·ãããŸãã ïŒåé¡çºçïŒ 1ãæãçµéããåæç¡æãã£ã³ããŒã³é©çšãŠãŒã¶ãŒãçŸãå§ããããããã®æ¯æãã (ã»ãŒ) å
šãŠå€±æãããšããçŸè±¡ãçºçããŠããŸããŸããã åå æ¯æãæ
å ±ã®ç»é²ãè¡ããã100%ãªãã¯ãŒãã³ã䜿ã仿§ãšããããšã§ãæ¬æ¥ã§ããã°ææã§ãããã©ã³ã次åã®æ¯æããèæ
®ããã«å¥çŽãããŠããŸã£ãããšã â ã©ãããããšãïŒ åããŠè«æ±ãè¡ããåæç¡æãã£ã³ããŒã³ãéå§ã®éã¯æ¯æãæ¹æ³ãç»é²ãããŠããªããšãåé¡ã¯çºçããªãã£ã 2åç®ã®è«æ±ãè¡ã£ãéã«ãæåã®ãã©ã³ãå¥çŽããç¶æ
ã®ãŸãŸæ¯æãæ¹æ³ãç»é²ãããŠããªããã倱æããŠãã ãšãããåçŽãªä»æ§ã®èæ
®æŒãã«æ°ã¥ãããšãã§ããªãã£ãããšãä»åã®æå ã§ããã [åé¡ãã®2]MRRã®æ°å€ã«æªåœ±é¿ãåºãŠããŸã£ã åé¡ç¹ MRR ã®æ°å€ããæœçã®æ¹éãæ€èšããéã®ææãšããŠå©çšããŠããããåæç¡æãã©ã³æäŸéå§åŸãããå®éã«èª²éãããå€ãš ãã¯ãŒãã³ã¯ MRR ã«åœ±é¿ããªãããšããå€ãšå€ãšé¢ä¿æ§ãæ£ããçè§£ããŠããªãã£ãã MRR ã«ã¯ å²åŒãé©çšããªã éé¡ãå ç®ããã ã€ãŸããç¡ææéã ãã詊ãã§å©çšããŠã¿ãã ãã®é¡§å®¢ãå©çšãããã©ã³ã®éé¡ãæå¹ãªãã®ãšããŠæ±ãããããã æ¬æ¥ã®èª²éé¡ãé«ããã©ã³ãå©çšãããŠãŒã¶ãŒãå±
ãã°å±
ãã»ã©ãã©ã³ã§ MRR ãä¹±é«äžããã 察çç·š ãã©ã€ã¢ã«ãšããæ©èœãããããã¡ããé©åã ãšæ°ã¥ã ããã¥ã¡ã³ããããã«èªã¿èŸŒã¿ããã©ã€ã¢ã«ãšããæ©èœã«åºäŒããŸãã ãã©ã€ã¢ã«ãå©çšããããååºŠä»æ§ãç·ŽãçŽãäºã«ããŸããã çµæçã«ãã¯ãŒãã³ãå©çšãããåæç¡æãã£ã³ããŒã³ããããã1ãæç¡æãã©ã€ã¢ã«ãã«ä¹ãæããããšã«ãªããŸãã å®éã®ã³ãŒãå€æŽ å
ã
ã Stripe ããã©ã€ã¢ã«ãšããæ©èœã§æäŸããŠãããã®ã«äŒŒããããªãã®ããã¯ãŒãã³ããšããŠæäŸããããšããŠããããããå
éšã®å®è£
ã¯å€§ããå€ãããå®çŸã§ããŸããã await stripe.subscriptions.update(subscriptionId, { items: subscriptionItems, coupon: âCOUPON_CODE_XXXâ }); ââ await stripe.subscriptions.update(subscriptionId, { items: subscriptionItems, trial_end: addMonths(new Date(), 1) }); çµæ ãã©ã€ã¢ã«ã«ããããšã§ããµãã¹ã¯ãªãã·ã§ã³ãã®ãã®ã MRR ã«åœ±é¿ããªããªã£ãïŒ é·ãéæžå¿µããŠãã MRR ã®ä¹±é«äžãããªãèœã¡çããŸããã ãŸãšã Stripe ã¯ãçŸåšå¢ãã®ãããªã³ã©ã€ã³æ±ºæžãµãŒãã¹ å°å
¥ãç°¡å çŽ æ©ãæè»ã«å€æŽå¯èœ æ£ããçè§£ããŠäœ¿ãã°ãšãŠãäŸ¿å© ãã²å©çšãæ€èšããŠã¿ãŠã¯ãããã§ãããã èè
ãããã£ãŒã« äŒå¢ éä¹ æ ªåŒäŒç€ŸWESEEK / ã·ã¹ãã ãšã³ãžã㢠2013幎8æãã2015幎3æãŸã§ WESEEK ã«åšç± 2017幎7æã« WESEEK åã®ãã«ãªã¢ãŒãã¡ã³ããŒãšã㊠WESEEK ã«åå
¥ç€Ÿ 2018幎5æã«å¥åºãµãã©ã€ããªãã£ã¹ãéèš å¥åºãµãã©ã€ããªãã£ã¹ã®ã¡ã³ããŒ5åãçã㊠GROWI / GROWI.cloud ãéçºã»éçšäž æ ªåŒäŒç€ŸWESEEKã«ã€ããŠ æ ªåŒäŒç€ŸWESEEK ã¯ãã·ã¹ãã éçºã®ãããã§ãã·ã§ãã«éå£ã§ãã ãçŸåšã®äž»ãªäºæ¥ã é信倧æäŒæ¥ã®æ¥åãããŒèªååãããžã§ã¯ã ãœãŒã·ã£ã«ã²ãŒã ã®åèšéçº èªç€ŸçºãªãŒãã³ãœãŒã¹ãããã¯ãã GROWI ãã GROWI.cloud ãã®éçº GROWI GROWI ã¯ãMarkdownèšæ³ã§ããŒãžãèšè¿°ã§ãããªãŒãã³ãœãŒã¹ã®Wikiã·ã¹ãã ã§ãã GROWI.cloud GROWI.cloud ã¯OSSã®GROWIãå°éçç¥èããªããŠãç°¡åã«éçšã»ç®¡çã§ãããæ³äººã»å人åãã®åçšãµãŒãã¹ã§ãã 倧æSIerã»ISPãäžå°äŒæ¥ã倧åŠã®ç 究宀ãªã©æ§ã
ãªå Žæã§ãå©çšããã ããŠãããŸãã ãäž»ãªç¹åŸŽã ããã¹ããå³è¡šãã©ãã©ãæžããã匷åãªç·šéæ©èœ ããŒã æ¡å€§ã«è¿
éã«å¯Ÿå¿ã§ãã管çè
åãæ©èœãæäŸ å
å®ããæ©èœã»ãµããŒãã§ãšã³ã¿ãŒãã©ã€ãºã«ãå¯Ÿå¿ ãå°å
¥äºäŸèšäºã ã€ã³ã¿ãŒããããã«ããã£ãŒãæ ªåŒäŒç€Ÿæ§ [ https://growi.cloud/interviews/mfeed/?utm_source=connpass-top&utm_medium=web-site&utm_campaign=mf:embed:cite ] æ ªåŒäŒç€ŸHIKKYïŒVRæ³äººHIKKYïŒæ§ [ https://growi.cloud/interviews/hikky:embed:cite ] WESEEK Tech Conference WESEEK Tech Conference ã¯ãæ ªåŒäŒç€ŸWESEEKãäž»å¬ãããšã³ãžãã¢åãã®å匷äŒã§ãã æã«2åã»ã©ãWESEEKã«æå±ãããšã³ãžãã¢ãæ§ã
ãªããŒãã§çºè¡šãè¡ãäºå®ã§ãã æ¬¡åã¯ãæ¿çœïŒGROWI.cloudã®å¯çšæ§åäžã®åãçµã¿ã 12/23(æš) 19:00~20:00ã«éå¬äºå®ã§ãã 顧客ã«äœããã®ãµãŒãã¹ãæäŸããäžã§ãæ¥åžžçãªãµãŒãã¹å¯çšæ§ã®ç¶æã»åäžã«é¢ããæ¥åã¯å¿
é ãšãªããŸãã æ¬çºè¡šã§ã¯ãå¯çšæ§ç¶æã»åäžã«å¿
èŠãªé
ç®ãæãã€ã€ãåŒç€Ÿãå®éã«æäŸããŠãããµãŒãã¹ã§ãã GROWI.cloud ã§ã®å®æœäŸãã玹ä»ããããŸãã ã©ã®ãããªåœ¢ã§ SLO ã®çå®ã«è³ã£ãã®ã ã©ã®ãããªæ§æã§å¯çšæ§ãæ°å€åããŠããã®ã ã©ã®ãããªæ¥åã宿œããŠãå¯çšæ§ã®ç¶æã»åäžãéæããŠããã çŸåšãconnpassãTECH PLAYã§åå åä»äžã§ããçæ§ã®ãåå ããåŸ
ã¡ããŠãããŸãïŒ https://weseek.connpass.com/event/231659/ TECH PLAYã¯ãã¡ããã äžç·ã«åã仲éãåéããŠããŸã æ±äº¬ã®é«ç°éŠ¬å Žãªãã£ã¹ã倧åã«ããå¥åºãµãã©ã€ããªãã£ã¹ã«ãŠãšã³ãžãã¢ãåéããŠãããŸãã äžéæ¡çšã ãã§ã¯ãªããã€ã³ã¿ãŒã³ã·ãããç©æ¥µçã«åãå
¥ããŠããŸãïŒ è©³ããåéèŠé
ã¯ãåŒç€ŸHPã® æ¡çšããŒãž ããã確èªãã ããã
ãã®æçš¿ã¯ãåŒç€ŸãæäŸããWESEEK TECHéä¿¡ã®äžç°ã§ãã WESEEK TECHéä¿¡ãšã¯ãWESEEKã®ãšã³ãžãã¢ããã£ããã¢ããããæè¡ã«é¢ããæ
å ±ããtechããã°ãéããŠå®æçã«çºä¿¡ããŠãããã®ã§ãã ã¯ããã« ããã«ã¡ã¯ãã·ã¹ãã ãšã³ãžãã¢ã®ãããã§ãããã®èšäºã§ã¯ãTypeScriptã§çšãããã Enumå ãšUnionåã®åºæ¬çãªäœ¿ãæ¹ãããEnumãé¿ããã¹ããšèšãããŠããçç±ãUnionåãçšããŠEnumã®ããã«æžãæ¹æ³ãªã©ã説æããŠããŸãã Enum (åæå) ãšã¯? ãããã Enum ãšã¯äœãããªãã®ããã«äœ¿ããã®ãªã®ã§ããããã ããã§èª¬æããŠãã Enum ãšã¯ç·ç§°ã§ãããåæåãšãåŒã°ããŸãã以äžã®äŸã¯ TypeScript ã®ã³ãŒãã§ãããä»ã®èšèªã§ãååšããæŠå¿µã»å®è£
ã§ãããè€æ°ã®å®æ°ãäžã€ã«ãŸãšããŠå®çŸ©ããã管çãããããããšãã§ããŸãã enum SIZE { Small, Medium, Large, } const newSize = SIZE.Small; console.log(newSize === SIZE.Small); // true console.log(newSize === SIZE.Large); // false Enum (åæå) ã䜿ãã¡ãªãã äžèšã®äŸãåçŽã«æååãšã㊠"small" ãªã©ã倿°ã§å®çŸ©ããŠæ¯èŒããæ¹æ³ãšæ¯ã¹ãŠã©ããªã¡ãªãããããã®ã§ããããã æã倧ããªæ©æµã¯ãã¹ãã«ãã¹ã倧æåå°æåãªã©ã®äººçãšã©ãŒãé²ãããšãã§ããç¹ã§ãã äŸãã° Visual Studio Code ãªã©ã® TypeScript ã®è£å®ãå¯èœãªãšãã£ã¿äžã§ SIZE. ãšå
¥åãããšã enum SIZE ã§å®çŸ©ãããŠããããŒããµãžã§ã¹ããããŸãã ãŸããEnumã¯åãšããŠãå©çšã§ããã®ã§ãããå
ç¢ãªã·ã¹ãã ã®æ§ç¯ã«åœ¹ç«ã¡ãŸãã JavaScriptãTypeScript ã§ã® enum JavaScript ã«ã¯ enum ã¯ååšããŸããããã®ãããenum ã§ããããããšãå®çŸããã«ã¯ const ã代çšãšãªãã§ãããã const SIZE = { Small: 'small', Medium: 'medium', Large: 'large', }; console.log(SIZE.Small); // small äžæ¹ TypeScript ã«ã¯ enum ã®æ©èœããããŸãããããã以äžã®ããã«å°ã
çããããŸãã // äœãæå®ããªãå Žåã0ããã®çªå·ãå²ãæ¯ãããŠãããŸãã enum SIZE { Small, // 0 Medium, // 1 Large, // 2 } console.log(SIZE.Large); // 2 console.log(SIZE[1]); // Medium å°ãåé·ã§ãããå宿°ã«ä»»æã®æååãä»»æã®æ°åãå²ãåœãŠãããšãã§ããŸãã enum SIZE { Small = "small", Medium = "medium", Large = "large", } console.log(SIZE.Large); // large // äžçªæåã®å®æ°(ããã§ã¯Small)ã«5ãæå®ããå Žåã宿°ã¯5ããã®æ°åãå²ãæ¯ãããŸãã enum SIZE { Small = 5, // 5 Medium, // 6 Large, // 7 } console.log(SIZE.Large); // 7 宿°ã®éäžã§æ°åãæå®ããå Žåãæåã®å®æ°ã¯0ããå§ãŸããéäžããæå®ãããæåããæ°åãå²ãæ¯ãããŸãã enum SIZE { Small, // 0 Medium = 4, // 4 Large, // 5 } console.log(SIZE.Medium) // 4 Enum å ãš Unionåã®æ¯èŒã«ã€ã㊠ãããŸã§è¿°ã¹ãã¡ãªãããèžãŸããäžã§ãå®ã¯çéã§ã¯ TypeScript ã® enum ã«ã€ããŠã¯å©çšã®å察掟(éæšå¥šæŽŸïŒ)ãååšããŸãã ãã®enumåãšãéæšå¥šæŽŸãæšå¥šããunionåã«ã€ããŠã話ãããããšæããŸãã Enum ã䜿çšããéã®æ³šæç¹ 1. æå³ããªãå€ã«ã¢ã¯ã»ã¹ã§ããŠããŸã äžèšã®èª¬æã§ãenumã«æ°å€ãå²ãåœãŠãããŠããå Žåããã®æ°å€ããå€ãååŸããããšãã§ãããšèª¬æããŸããããããå©çšããŠãå²ãåœãŠãããŠããªãæ°å€ããã®å€ã®ååŸãå¯èœãšãªããæå³ããundefinedãè¿ã£ãŠããŠããŸããŸãã enum SIZE { Small, Medium, Large, } console.log(SIZE[1]); // Medium // no error!! console.log(SIZE[5]); // undefined æ°å€ã®enumã«ã¯ããããnumberãå²ãåœãŠãããšãã§ããã®ã§ãåå®å
šæ§ã«æ¬ ããŠããŸããŸãã 2. Tree-shaking ãäœåãããåŒã³åºãããªãäžèŠãªã³ãŒããã³ã³ãã€ã«ãããŠããŸã TypeScriptã§å®çŸ©ãããenumã¯ãJavascriptã«ãã©ã³ã¹ãã€ã«ãããéã«ä»¥äžã®ãããªå³æå®è¡é¢æ°ãšããŠå€æãããŸãã峿å®è¡é¢æ°ãšã¯ããå®çŸ©ããããšããã«å®è¡ããããJavaScriptã®é¢æ°ã®ããšã§ãã var SIZE; (function (SIZE) { SIZE[SIZE["Small"] = 0] = "Small"; SIZE[SIZE["Medium"] = 4] = "Medium"; SIZE[SIZE["Large"] = 5] = "Large"; })(SIZE || (SIZE = {})); // æååãå²ãåœãŠãå Žå var SIZE; (function (SIZE) { SIZE2["Small"] = "small"; SIZE2["Medium"] = "medium"; SIZE2["Large"] = "large"; })(SIZE || (SIZE = {})); ãã®ããã«å³æå®è¡é¢æ°ãšããŠãã©ã³ã¹ãã€ã«ããããšãTree-shakingãäžæãåããªããªã£ãŠããŸããšããåé¡ç¹ããããŸãã Tree-shakingãšã¯....?Tree-shaking ãšã¯ãwebpackãªã©ã®ã¢ãžã¥ãŒã«ãã³ãã©ãJavascriptãã¡ã€ã«ãäžã€ã«ãŸãšããéã«ãå®è¡ãããªãäœåãªã³ãŒããåé€ãããšãã£ãåŠçã®ããšã§ãã æšãæ¯ãããšã§ãäžèŠãªãã®ãèœãšããŠããªã¥ãŒã ãå°ããããã€ã¡ãŒãžã ãšå人çã«è§£éããŸããã 以äžã®ãµã€ãã§ Tree shakingã«ã€ããŠèª¬æãããŠããã®ã§ããããã°ã芧ãã ããã MDN: Web Docs: Tree shaking Webpack rule that may reduce your CSS file size dramatically 3. isolatedModulesãtrueã®å Žåãconst enumäœ¿çšæã«ã³ã³ãã€ã«ãšã©ãŒã«ãªãããšããã --isolatedModulesãªãã·ã§ã³ãæå¹ã«ããå Žåã Transpile each file as a separate module (similar to 'ts.transpileModule'). ãã®ãªãã·ã§ã³ãtrueã«ããå Žåãconst enumã¯ãšã©ãŒãšãªãã³ã³ãã€ã«ã§ããªãã 詳现ã¯ä»¥äžã®èšäºãã芧ãã ãããhttps://www.kabuku.co.jp/developers/good-bye-typescript-enum 以äžã®çç±ãããenumã®å©çšã¯é¿ããã¹ãã§ãããšèšãããŠããŸãã ã§ã¯ãenumã®ãããªæ©èœã䜿ãããå Žåã¯ã©ãããã°è¯ãã®ã§ãããã? ããã¯ãUnionåã䜿ãããšã§å®çŸã§ããŸãã Unionåã«ã€ã㊠Unionåã¯ã以äžã®ããã«2ã€ä»¥äžã®åããã€ãèšå·|ã§ç¹ããŠæžãããšã§ãè€æ°ã®åãæå®ã§ããŸãã const middleName: string | null Enumã®ä»£ããã«ãUnionåãçšãã Enumã䜿ãããšããEnumã®ããã«å®çŸ©ããã«ã¯ã©ãããã°ããã®ã§ãããã?const assertion(as const), keyof, typeof ã䜿ã£ãŠenumã®ãããªå®çŸ©ãããããšãã§ããŸãã // const assertionã䜿ã£ãŠããªãã©ã«åãšããŠæ±ã const SizeType = { SMALL: 'small', MEDIUM: 'medium', LARGE: 'large', } as const; // type SizeType = 'small' | 'medium' | 'large' type SizeType = typeof SizeType[keyof typeof SizeType]; 现ããåè§£ãããšã以äžã®ããã«ãªããŸãã // type SizeType = { // readonly SMALL: "small"; // readonly MEDIUM: "medium"; // readonly LARGE: "large"; // } type SizeType = typeof Size; // type SizeKey = "SMALL" | "MEDIUM" | "LARGE" type SizeKey = keyof SizeType; // type Size = 'small' | 'medium' | 'large' type Size = SizeType[SizeKey]; const assertionããªãã©ã«å, keyof, typeof ã«é¢ããŠã¯ã以äžã®èšäºã§ãããããã説æãããŠããã®ã§ããããã°ã確èªãã ããã constã¢ãµãŒã·ã§ã³ãas constã (const assertion) ãªãã©ã«å typeof, keyof, inã®åäœç¢ºèª unionã®ãªã¹ããæã«å
¥ãããå Žå javaScriptãæäŸãã Object.values颿°ã䜿çšããŠãååŸããããšãã§ããŸãã // const AllSizeType = Object.values(SizeType); const AllSizeType = Object.values(SizeType); MDN Web Docs: Object.values å®æç³»ãã³ãã¬ãŒã WESEEK ã§ã¯ä»¥äžããã³ãã¬ãŒããšããŠäœ¿çšããŠããŸããconst assertion ã«å€ã远å ããã ãã§ union ãšå
šå€ã®ãªã¹ãã export ã§ããã®ã§äŸ¿å©ã§ãã const SizeType = { SMALL: 'small', MEDIUM: 'medium', LARGE: 'large', } as const; // type SizeType = "small" | "medium" | "large" export type SizeType = typeof SizeType[keyof typeof SizeType]; // å
šãŠã®typeãé
åãšããŠååŸ // const AllSizeType: ("small" | "medium" | "large")[] export const AllSizeType = Object.values(SizeType); ãŸãšã ç·ç§°ãšããŠã® Enum (åæå)ã«ã€ããŠ è€æ°ã®å®æ°ããŸãšããŠå®çŸ©ããã管çããã®ã«äŸ¿å© ããããªèšèªã§å©çšã§ããã JavaScript ã«ã¯ãªã Enum ã¯åãšããŠã䜿ããã®ã§ããã°ãé²ãããšã«åœ¹ç«ã€ TypeScript ã® enum vs union ããããªèšè¿°æ¹æ³ããã enum ã¯ãèšè¿°ã¯ç°¡å enum ã®å©çšã«ã¯è€æ°ã®èœãšã穎ãããã®ã§ãunionåãçšããããšãäžéšã§ã¯æšå¥šãããŠãã ãã ããããªããã°ãTypeScript ã§ã¯enumåã§ã¯ãªãunionåãçšããããšãã奚ãããŸãã åèã«ãããŠããã ãããµã€ã MDN Web Docs: Tree shaking Enum ã®ä»£ããã«äœ¿ã union å
ãã®èšäºã¯ã2021/10/28 ã«è¡ã£ã WESEEK Tech Conference #14 ã®å
容ããŸãšãããã®ã§ãã 第14åç®ã®ããŒãã¯ãããªããã§ããïŒGASã§å€æ å
¥åSlack Appæ§ç¯ã å®éã« WESEEK ã§äœ¿ã£ãŠãã倿 å
¥åã·ã¹ãã ãtickrecãã®æ§æãå
ã«ãSlackã§å€æ å
¥åãã§ããã¢ããªã±ãŒã·ã§ã³ã1ããæ§ç¯ããããã®æ¹æ³ãã¬ã¯ãã£ãŒããŸãã æ§æèŠçŽ ã®è§£èª¬ã¯ãã¡ãããå®éã®ç»é¢ã亀ããªããæ§ç¯ããæé ã解説ããŸãã ãããèŠãæã«ã¯ãããªãã倿 å
¥å Slack App ãæ§ç¯ã§ããããã«ãªã£ãŠããïŒïŒã¯ãã 倿 ã·ã¹ãã ããããŸè©± çããã®äŒç€Ÿã§ã¯ãã©ããªå€æ ã·ã¹ãã 䜿ã£ãŠããŸããïŒ Web ã·ã¹ãã ãå°çšã¢ããªãã¯ããŸãçŽã®ç®¡çç°¿ãã¿ã€ã ã¬ã³ãŒããŒãªã©äœ¿ã£ãŠããæ¹ããããšæããŸãã WESEEK ã§ã¯ããŸãå€§èŠæš¡ãªã·ã¹ãã ã¯å°å
¥ããŠããŸãããGoogle Spreadsheet ã䜿ã£ãŠãã·ã³ãã«ã«å
šç€Ÿå¡ã®å€æ ã1ã·ãŒãã§èšé²ããŠããŸãã ãããã Spreadsheet ã«ã¯ Excel çãšåæ§ã« Pivot table ãã¯ãããšããè±å¯ãªéèšæ©èœãæèŒãããŠãããããç®¡çæ¥åã«å¿
èŠãªæã®åèšçšŒåæéãå¹³åå€åæéãã§ããŸãã 倿 管çã®èª²é¡ ã©ããªã·ã¹ãã ãå°å
¥ããŠãããšããŠãã倿 管çã«ãããå
±éã®èª²é¡ãšããã®ãã€ããŸãšãã®ã§ã¯ãªãã§ããããïŒ ãã㯠å
¥åãé¢å ãšããç¹ã§ãã Spreadsheet ã ããã Web ã·ã¹ãã ã ããããåã課é¡ããããŸãã æ£çŽãªãšãããåŸæ¥å¡ããããã°ã倿 å
¥åããããã ãã«ããŒã«ãéãããšããé¢åã§ãã ãã®çµæãåœæ¥äžã®å
¥åå¿ããé »çºããããå Žåã«ãã£ãŠã¯æãå€ãã£ãŠãåæåã®å
¥åæãããã£ããããŠãæç· ãææã«ããã¯ã€ãŒãããŒã ãåå¥ã«ãªãã€ã³ããããªã©ã®äœèšãªæ¥åãçºçãããããŸãã ãããåžžæ
åãããšãææ«ã«ãŸãšããŠé©åœã«å
¥åããè¡çºã暪è¡ãããªã©ããæãããŠæ£ããå€åæå»ãèšé²ãããŠããã®ããããã®ç²ŸåºŠã«çåãæ®ããŸã 倿 å
¥åã®æ¹åæ¹æ³ ãããããã®èª²é¡ã«å¯Ÿããæ¹åæ¹éã¯ã·ã³ãã«ã§ãããšã«ããå
¥åãŸã§ã®stepæ°ãæžããããš ã«å°œããŸãã æ¢åã®å€æ ã·ã¹ãã ã§ãããã®ç¹ãæèãããœãªã¥ãŒã·ã§ã³ãèŠåããããŸãã äŸãã°ãã¢ããªã Web ã·ã¹ãã ã®å Žåã倿 å
¥åã·ã¹ãã ãéããŠã¯ã³ã¹ãããã§æå»å¯èœãª UI ã«ããããšã§æ¹åãããŠããŸãã æŽã«ãããŒããŠã§ã¢ãé§äœ¿ããŠæ¹åãããã£ããœãªã¥ãŒã·ã§ã³ããããŸãã äŸãã° IC ã«ãŒãã§æå»ã§ããã·ã¹ãã ã§ãã è¿å¹Žãã»ãã¥ãªãã£å¯Ÿçã®äžç°ãšããŠãIC ã«ãŒãããããããšã§å·å宀ãè§£é ããã·ã¹ãã ãæ¡çšããäŒç€Ÿãå€ããšæããŸãã ãã®ãããªã·ã¹ãã ãå°å
¥æžã¿ã®å Žåã倿 çš IC ã«ãŒããªãŒããå
¥ãå£è¿ãã«é
眮ããããšã§ãã·ãŒã ã¬ã¹ã«åºå€å³æå»ãå®çŸã§ããŸãã ãŸãè§£é ã·ã¹ãã ãšå€æ ã·ã¹ãã ã飿ºã§ãããœãªã¥ãŒã·ã§ã³ãååšããŸãã æŽã«èžã¿èŸŒãã ãœãªã¥ãŒã·ã§ã³ãšããŠãããŒã³ã³ã§æå»ã§ããã·ã¹ãã ããããŸãã ãã㯠Bluetooth çã®ç¡ç·ã·ã¹ãã ãæŽ»çšããŠãããå瀟å¡ã®ã¹ããã倿 端æ«åå
ã«å
¥ãã ãã§èªåæå»ã§ãããšãããã®ã§ãã ãããŸã§æ¥ããšãåºç€Ÿããã ãã§ãŒãã¹ãããã§æå»ãå¯èœã«ãªããŸãã WESEEK 瀟å
ã®å€æ å
¥åæ¹åãœãªã¥ãŒã·ã§ã³ ãšãå®ã«å€¢ã®åºãã倿 å
¥åãœãªã¥ãŒã·ã§ã³ã®äŸã玹ä»ããŠããŸãããããããã®ã·ã¹ãã ã®å°å
¥ãšç¶æã«ã¯ãããªãã®ã³ã¹ããããããŸãã WESEEK ãšããŠã¯ãããå°ãã³ã¹ãã®è¯ãææ®µãå°å
¥ããããšã«ããŸããã Tickrec ãèªäœ Tickrec ãšåä»ããããŒã«ãäœæããå°å
¥ããŸããã Slack App ã«ãªã£ãŠããã Slack äžã§äžèšã®ãããªæåãå®çŸã§ããŠããŸãã æ¯æã瀟å
Slack ã« DM ã§å€æ ãã¿ã³ãé£ãã§ãã ãã¿ã³ãã¯ãªãã¯ãããšã倿 å
¥åå®äº Google Spreadsheetã«å
¥åããã å°å
¥ã®ã¡ãªãã Tickrec ã®å°å
¥ã«ããã瀟å
Slack ããã¯ã³ã¯ãªãã¯ã§å€æ å
¥åãã§ããããã«ãªããŸããã WESEEK ã§ã¯ã³ãã¥ãã±ãŒã·ã§ã³ããŒã«ãšã㊠Slack ãå°å
¥ããŠãããããåŸæ¥å¡å
šå¡ã Slack ãåžžæèµ·åãããŠããŸãã ãã®ãããæå»å°çšã®ã¢ããªãªã©ããã¡ãã¡éãæéãçããã®ã§ãã ãŸãã Tickrec ã® Slack App éšåã¯ããã ã®ãã¥ãŒãã³ã€ã³ã¿ãã§ãŒã¹ã«åŸ¹ããŠããã倿 ã®èšé²ãã®ãã®ã¯åŸæ¥éã Spreadsheet ã«èšé²ãããŸãã ãã®ãããããã¯ãªãã£ã¹ããŒã ã®æ¥åã倿Žããå¿
èŠããããŸããã æŽã«ã³ã¹ãé¢ã§ã¯ãèªäœãã€ç¡æã§äœ¿ãããµãŒãã¹ã®çµã¿åããã§æ§ç¯ãããŠãããããåºæ¬çã«ã©ã³ãã³ã°ã³ã¹ãã¯ãããŸããïŒ å
éšæ§æ Tickrec ã¯ä»¥äžã®ãããªãµãŒãã¹ã䜿ã£ãŠæ§æããŠããŸãã 䜿çšããŠããã®ã¯ã Slack App ãšã Google Spreadsheet ãšå
±ã«åã Google Apps Script (以éãGAS) ã ãã§ãã ããããå°çšã®ããŒã«ãªã©ãäžèŠã§ãã©ãŠã¶ã§æäœãå®çµã§ãããã€åºæ¬çã«ç¡æã§å©çšã§ããŸãã ãã®ããã«ãä»ããã«ã§ãæ§ç¯ãå§ãããããµãŒãã¹ã®çµã¿åããã ãã§æ§æããŠããŸãã ä»ããæ§ç¯ããŠã¿ããïŒ ãšããããšã§ãããããçããã«ãè¶
ç°¡æçã® Tickrec ãæ§ç¯ããŠãããããšæããŸãã çšæãããã® å¿
èŠãªãã®ã¯äžèšã®éãã§ãã App ã远å ã§ããæš©éã®ãã Slack workspace Google Account ãã®ã¬ã¯ãã£ãŒã®ç®ç ä»åã®ã¬ã¯ãã£ãŒã®äž»ãªç®çã¯ãçããã«è¶
ç°¡æçã® Tickrec ãæ§ç¯ããŠããããããããªãç§ã Slack App ãäœãããïŒããšæã£ãŠããããããšã§ãã å®éã« Tickrec ãæ§ç¯ããã®ã«å¿
èŠãªã®ã¯ãåãµãŒãã¹ã®ã»ããã¢ãããšã Tickrec ã®äžæ¢ã§ããããã°ã©ã ã®å®è£
ã§ãã ã§ãããããã°ã©ã ãäžããæžããšæéãããã£ãŠããŸãã®ã§ãããã°ã©ã ã«ã€ããŠã¯ãµã³ãã«ã³ãŒããçšæããŠãããŸãã ãããã£ãŠãããããã¬ã¯ãã£ãŒããã®ã¯ããµãŒãã¹ã®ã»ããã¢ããæé ãäž»ã§ãããããã°ã©ã ã®æžãæ¹ã®èª¬æã¯å²æããŠããŸãã ãã ãã åæ©èœãå®çŸããããã® GAS ç¹æã®éšåãªã©ã¯ãããŒãã€ã³ããšããŠç޹ä»ããŸãã æ¬æ¥ã®ã¬ã·ã ããããã¯ã以äžã®ã¹ãããã«åŸã£ãŠé²ããŠãããŸããã ä»èŸŒã¿1: GASãããžã§ã¯ããäœã STEP1: GASã§æå»ãã ä»èŸŒã¿2: Slack Appãäœã STEP2: GASã§Slackã«æçš¿ãã STEP3: Slackã®æäœã§GASãåãã STEP4: Slackã§æå»ãã ããã§ã¯æ©éå§ããŸãããã ä»èŸŒã¿1 GASãããžã§ã¯ããã€ãã ãŸãã¯äžæºåãšããŠãSpreadsheet ãš GAS ãããžã§ã¯ããäœæããŸãããã æé Spreadsheet ã®çšæ Spreadsheet ã¯ãµã³ãã«ãçšæããŠããã®ã§ããããããšã«ããŸãã ãè¶
ç°¡æçTickrecåã倿 ã·ãŒããããµã³ãã«ãªã®ã§ããŸãã¯ãã³ããŒãäœæãããŸãã ãããšããªãã® Google Drive ã«ãè¶
ç°¡æçTickrecåã倿 ã·ãŒã ã®ã³ããŒããäœæãããŸãã ããã§ã Spreadsheet ã®æºåãã§ããŸããã GAS ãããžã§ã¯ãã®çšæ æ¬¡ã« GAS ãããžã§ã¯ããäœæããŸãããã å
ã»ã©äœæãããè¶
ç°¡æçTickrecåã倿 ã·ãŒã ã®ã³ããŒããéããŸãã Spreadsheetã®ã¡ãã¥ãŒãããããŒã«ã->ãã¹ã¯ãªãã ãšãã£ã¿ããéããŸã ãããšãç¡é¡ã®ãããžã§ã¯ãããšããååã® GAS ãããžã§ã¯ããäœæãããŸãã GAS ã®ã¹ã¯ãªãããšãã£ã¿ç»é¢ã衚瀺ãããŠããã¯ãã§ãã®ã§ããã®ãç¡é¡ã®ãããžã§ã¯ããã®éšåãã¯ãªãã¯ããã¿ã€ãã«ãåããããããã®ã«å€æŽããŸã ããã§ GAS ãããžã§ã¯ãã®æºåãã§ããŸããã STEP.1 GASã§æå»ãã ãä»èŸŒã¿1ãã§æäœéã®æºåãã§ããŸããã ãã®ç°å¢ã䜿ã£ãŠããŸã㯠GAS ãã€ãã£ãŠæå»ããŠã¿ãŸãããã ãŽãŒã« ãSTEP.1ãã§ã¯äžèšããŽãŒã«ã«ããŠé²ããŠãããŸãããã ã¹ã¯ãªãããšãã£ã¿ã§é¢æ°ãå®è¡ããŠãæå»(éå§æå»ã»çµäºæå»)ãã§ãã æé ã³ãŒãã®å®è£
æ¢ã«å®è£
æžã¿ã®ãµã³ãã«ã³ãŒã GAS_step1.gs ãçšæããŠãããŸãã®ã§ããŸãã¯ãããã³ããŒã GAS ã®ã¹ã¯ãªãããšãã£ã¿ã«è²Œãä»ããŠäžæžãããŸãã ã¹ã¯ãªãããšãã£ã¿ã®ããããžã§ã¯ããä¿åãã¢ã€ã³ã³ãã¯ãªãã¯ããã³ãŒãã®å€æŽãä¿åããŸãã ããã§ GAS ã®ã³ãŒãã¯å®æã§ãã ã¿ã€ã ãŸãŒã³ã®èšå® 次ã«ãGASã®ã¿ã€ã ãŸãŒã³ãèšå®ããŸãã ã¿ã€ã ãŸãŒã³ãèšå®ããã«ã¯ãäžæçã«å€ãã¹ã¯ãªãããšãã£ã¿ãžåãæ¿ããå¿
èŠããããŸãã ã¹ã¯ãªãããšãã£ã¿ã®ã以åã®ãšãã£ã¿ã䜿çšããã¿ã³ãã¯ãªãã¯ããŸãã å€ãã¹ã¯ãªãããšãã£ã¿ç»é¢ã衚瀺ãããããã¡ãã¥ãŒããããã¡ã€ã«ã->ããããžã§ã¯ãã®ããããã£ããéããŸãã éãããããããžã§ã¯ãã®ããããã£ãã®ãã€ã¢ãã°ããã¯ã¹ã§ããæ
å ±ãã¿ãã®ãã¿ã€ã ãŸãŒã³ãããããã£ã®å€ãã(GMT+09:00) ãšæ±äº¬ãã«å€æŽãããä¿åããã¯ãªãã¯ããŸã ãæ°ãããšãã£ã¿ã䜿çšããã¿ã³ãã¯ãªãã¯ããå
ã®ã¹ã¯ãªãããšãã£ã¿ç»é¢ã«æ»ããŸã æ€èšŒ ããã§ã GAS ã䜿ã£ãŠæå»ã§ããããã«ãªã£ãã¯ãã§ãã æ©éãã¹ã¯ãªãããšãã£ã¿ã®ãå®è¡ãæ©èœãã€ãã£ãŠåãã詊ããŠã¿ãŸãããã éå§æéãæå»ãã ãŸãã¯ãéå§æå»ã®å
¥åæ©èœã詊ããŠã¿ãŸãããã ã¹ã¯ãªãããšãã£ã¿ã®äžéšã«ããå®è¡ãæ©èœããããŸãã ãå®è¡ãã颿°ãéžæãéšåã§å®è¡ããã颿°å (ä»å㯠recordAttendance() ) ãéžæããŸãã ç¶ããŠããå®è¡ããã¿ã³ãã¯ãªãã¯ããŸãã ååã®å®è¡æã¯ã¢ã¯ã»ã¹æš©éã®èš±å¯ãå¿
èŠã§ãã ãå®è¡ããã¿ã³ãã¯ãªãã¯åŸããã°ãããããšããæ¿èªãå¿
èŠã§ãããšãããã€ã¢ãã°ããã¯ã¹ã衚瀺ãããŸãã ãæš©éã確èªããã¯ãªãã¯ããŸãã ç¶ãç»é¢ã§èªåã®ã¢ã«ãŠã³ããéžæããŸãã ããã«ç¶ãç»é¢ã§ãèš±å¯ããã¿ã³ãã¯ãªãã¯ããŸãã ãããšãã¢ã¯ã»ã¹æš©éã®èš±å¯ãå®äºãã颿°ã®å®è¡ãåéããŸãã Spreadsheet ãèŠãŠã¿ãŸããããæ°ããªè¡ã«ãå®è¡ããæ¥ä»ãšæå»ãå
¥åãããŠããã¯ãã§ãã çµäºæéãæå»ãã æ¬¡ã¯ãçµäºæå»ã®å
¥åæ©èœã詊ããŠã¿ãŸãããã ã¹ã¯ãªãããšãã£ã¿ã®ãå®è¡ãã颿°ãéžæãéšåã§ recordLeaving() ãéžæãããå®è¡ããã¿ã³ãã¯ãªãã¯ããŸãã ãããš Spreadsheet ã«ä»åºŠã¯çµäºæå»ãå
¥åãããŠããã¯ãã§ãã ããŒãã€ã³ã ããã§ãSTEP.1ãã¯ã¯ãªã¢ã§ãïŒ ãšã¯ããã GAS ã®ã³ãŒãã¯ã³ããããã ãã§ãã®ã§ãå
·äœçã«ã©ããªããžãã¯ã§æå»æ©èœãå®çŸããã®ããç®ã«è§Šããæ©äŒããããŸããã§ãããã ãã£ããã®æ©äŒãªã®ã§ãã©ã®ãããªã³ãŒãã«ãªã£ãŠãããããã£ããèªãã§ã¿ãŠãã ããã ãã®ããã«ããäžèšã§ãSTEP.1ãã®ã³ãŒããšãããå®çŸããããã®èæ¯æè¡ã«ã€ããŠãããŒãã€ã³ãã玹ä»ããŸãã å®è£
ããäž»ãªé¢æ°ã¯äžèšã®éãã§ã recordAttendance() æ¥ä»ãšéå§æå»ãèšé²ãã recordLeaving() çµäºæå»ãèšé²ãã GAS ã§ Spreadsheet ãæäœããã«ã¯ã GAS ã® Spreadsheet Service ã䜿çšããŸãã ä»åã¯ããã䜿ããæžã蟌ã¿å¯Ÿè±¡ã®ã»ã«ã®ç¹å®ããã»ã«ãžã®æžã蟌ã¿ãè¡ã£ãŠããŸãã ãŸãã Logger ã䜿çšãã GAS ã§ãã°åºåãããŠããŸãã ããã䜿çšãããšãä»»æã®ãã°ãã¹ã¯ãªãããšãã£ã¿ã®å®è¡ãã°ã§èŠãããçšã«ãªããŸãã ãµã³ãã«ã³ãŒãã§ã¯ãã©ã®ã»ã«ãéžæããŠããããã°åºåããŠããã®ã§ãã³ãŒãã®æåãçŽè§£ããšãã«åèã«ããŠã¿ãŠäžããã ä»èŸŒã¿2 Slack Appãã€ãã ã€ãã®ã¹ããããžã®äžæºåãšããŠã Slack App ãäœæããŸãããã æé App ãäœã slack api ã® apps page ã§ãCreate an Appããã¿ã³ãã¯ãªãã¯ããŸãã 衚瀺ããããCreate an appããã€ã¢ãã°ããã¯ã¹ã§ãFrom scratchããéžæããŸãã æ¬¡ã«è¡šç€ºããããName app & choose workspaceããã€ã¢ãã°ããã¯ã¹ã§ãã¢ããªã®ååå
¥åãšãéçºã«äœ¿ã workspace ã®éžæãããŸãã æåŸã«ãCreate Appããã¿ã³ãã¯ãªãã¯ããŸãã App ãäœæãããšããã® App ã«é¢ããæ§ã
ãªèšå®ãè¡ããããŒãžã衚瀺ãããŸãã App ã«å¿
èŠãªæš©éãèšå®ãã ç¶ããŠãApp ã«å¿
èŠãªæš©é (scope) ãèšå®ããŸãã ä»å㯠App ãšãŠãŒã¶ãŒéã§ Direct Message (以é DM ãšèšèŒ) ã䜿çšããŠãããšãããŸãã ãã®ããã« DM æžã蟌ã¿ã«å¿
èŠãª scope ãèšå®ããŸãã ãŸããBasic InfomationãããŒãžã§ãBotsããéžæããŸãã ç¶ããŠè¡šç€ºããããApp HomeãããŒãžã§ãReview Scopes to Addããã¿ã³ãã¯ãªãã¯ããŸãã ç¶ããŠè¡šç€ºããããOAuth & PermissionsãããŒãžã®äžéšã«ãããScopeãã«ã€ããŠããBot Token Scopesãã® Oauth Scope ã远å ããŸãã 远å ãã scope ã¯äžèšã®éãã§ãã chat:write im:write scope ã远å ããã«ã¯ããAdd an Oauth Scopeããã¿ã³ãã¯ãªãã¯ããåŸãå¿
èŠãª scope ãéžæããŸãã 远å ããã®ã¯ãUser Token Scopesãã§ã¯ãªããBot Token Scopesããªã®ã§ã泚æããŸãããã App ã workspace ã«ã€ã³ã¹ããŒã«ãã äžèšã§èšå®ãã scope ã§ã workspace ã« App ãå°å
¥ããŸãã ãOAuth & PermissionsãããŒãžäžéšã®ãOauth Tokens for Your Workspaceãéšåã«ãInstall to Workspaceããã¿ã³ãå¢ããŠããã®ã§ãã¯ãªãã¯ããŸãã ãããšã App ã« workspace ã®ã¢ã¯ã»ã¹æš©éèš±å¯ã®ç¢ºèªç»é¢ã衚瀺ãããã®ã§ããèš±å¯ããããã¯ãªãã¯ããŸãã ãããŸã§ã®æäœãå®äºãããšã workspace ã« App ã远å ãããŠããã®ã確èªã§ããããã«ãªããŸãã ãŸãã App ã®èšå®ç»é¢ã§ã¯ãå
ã»ã©ã®ãOAuth & PermissionsãããŒãžã®ãOauth Tokens for Your Workspaceãéšåã«ãBot User OAuth Tokenãã衚瀺ãããŸãã ãã® Toekn ã¯ã GAS ããæçš¿ãããšãã®èªèšŒéµãšããŠåŸã§äœ¿çšããŸãã®ã§ãé ã®çé
ã«å
¥ããŠãããŠäžããã STEP.2 GASã§Slackã«æçš¿ãã ãä»èŸŒã¿2ãã§ Slack App ã䜿ã£ãŠ Slack workspace ã« DM ããæºåãã§ããŸããã ãã®ç°å¢ã䜿ã£ãŠã次㯠GAS ãã Slack ã« DM ããŠã¿ãŸãããã ãŽãŒã« ãSTEP.2ãã§ã¯äžèšããŽãŒã«ã«ããŠé²ããŠãããŸãããã ã¹ã¯ãªãããšãã£ã¿ã§é¢æ°ãå®è¡ããŠãSlackã«DMæçš¿ãã§ãã æé ã³ãŒãã®å®è£
åã³ããµã³ãã«ã³ãŒã GAS_step2.gs ãçšæããŠãããŸãã®ã§ããããã³ããŒã GAS ã®ã¹ã¯ãªãããšãã£ã¿ã«è²Œãä»ããŠäžæžãããŸãã 远èšã§ã¯ãªãäžæžãã§ãã®ã§ã泚æäžããã äžæžãããããã¹ã¯ãªãããšãã£ã¿ã®ããããžã§ã¯ããä¿åãã¢ã€ã³ã³ãã¯ãªãã¯ããã³ãŒãã®å€æŽãä¿åããŸãã ããã§ GAS ã®ã³ãŒãã¯å®æã§ãã GAS ããããã£ã®èšå® 次ã«ãGASã®ããããã£ãèšå®ããŸãã ããããã£ã¯ãã³ãŒããšã¯å¥ã«å¿
èŠãªãã©ã¡ãŒã¿ãªã©ãä¿æããŠããã®ã«äŸ¿å©ãªæ©èœã§ãã ä»åèšå®ãããã©ã¡ãŒã¿ãŒã¯äžèšã®2ã€ã§ãã Bot User OAuth Token Slack ã«æçš¿ãããšãã®èªèšŒéµãšããŠå¿
èŠ Slack User ID DM çžæãç¹å®ããã®ã«å¿
èŠ ãããã®ãã©ã¡ãŒã¿ãŒã¯ã³ãŒãã«çŽæ¥æžããŠããŸãæ¹æ³ããããŸãã ãããããããã®ãã©ã¡ãŒã¿ãŒã¯ãç°å¢(äœã£ã App ã workspace)ããšã«ç°ãªãå€ã«ãªããããã³ãŒãã«çŽæ¥æžããŠããŸããšãã³ãŒãã®åå©çšãæªããªããŸãã ãŸãããããã®ãã©ã¡ãŒã¿ãŒã¯ãä»äººã«æ©å¯ã«ããã¹ãæ
å ±ã®ãããã³ãŒãã«çŽæ¥æžããŠããŸããšããã®ã³ãŒããå€éšã«å
¬éãããšå±éºã«ãªããŸãã åŸã£ãŠããã©ã¡ãŒã¿ãŒã¯ã³ãŒããšåé¢ããŠåãæ±ãã®ããã¹ããã©ã¯ãã£ã¹ã§ãã GASã®ããããã£ãèšå®ããã«ã¯ãäžæçã«å€ãã¹ã¯ãªãããšãã£ã¿ãžåãæ¿ããå¿
èŠããããŸãã ã¿ã€ã ãŸãŒã³ã®èšå®ããããšããšåæ§ã«ãã¹ã¯ãªãããšãã£ã¿ã®ã以åã®ãšãã£ã¿ã䜿çšããã¿ã³ãã¯ãªãã¯ããŸãã å€ãã¹ã¯ãªãããšãã£ã¿ç»é¢ã衚瀺ãããããã¡ãã¥ãŒããããã¡ã€ã«ã->ããããžã§ã¯ãã®ããããã£ããéããŸãã éãããããããžã§ã¯ãã®ããããã£ãã®ãã€ã¢ãã°ããã¯ã¹ã§ããã¹ã¯ãªããã®ããããã£ãã¿ããéžæããŸã 远å ããããããã£ã¯äžèšã®éãã§ãã ããããã£ å€ slackBotUserOAuthToken Bot User OAuth Token ã®å€ slackDMUserId Slack workspace ã®èªåã®ã¡ã³ã㌠ID ããããã£ãšå€ãå
¥åããã«ã¯ãã+è¡ã远å ããã¯ãªãã¯ããåŸã衚瀺ãããå
¥åæ¬ã«ããããå€ãå
¥åããŸãã ãBot User OAuth Token ã®å€ãã¯ãä»èŸŒã¿2ãã®æåŸã§ç»å ŽããããOAuth & PermissionsãããŒãžã®ãOauth Tokens for Your Workspaceãéšåã«è¡šç€ºããããBot User OAuth Tokenãã®å€ãå
¥åããŸãã ãSlack workspace ã®èªåã®ã¡ã³ã㌠IDãã¯ã Slack workspace ããååŸããŸãã workspace ã®å³äžã«ããèªåã®ãããã£ãŒã«åçãã¯ãªãã¯ããããããã£ãŒã«ããã¯ãªãã¯ããŸãã å³åŽã«è¡šç€ºããããããã£ãŒã«ç»é¢ããããã®ä»ããã¯ãªãã¯ããŸãã 衚瀺ãããã¡ãã¥ãŒãããã¡ã³ã㌠ID ã ã³ããŒããã¯ãªãã¯ãããšãã¡ã³ã㌠ID ãã³ããŒãããã®ã§ã GAS ããããã£ã®å€ã«ããŒã¹ãããŸãã 2ã€ã®ããããã£ãå
¥åãçµããã以äžã®ç»é¢ã®ãããªç¶æ
ã«ãªã£ããããä¿åããã¿ã³ãã¯ãªãã¯ããŸãã æåŸã«ãæ°ãããšãã£ã¿ã䜿çšããã¿ã³ãã¯ãªãã¯ããå
ã®ã¹ã¯ãªãããšãã£ã¿ç»é¢ã«æ»ããŸãã æ€èšŒ ããã§ã GAS ã䜿ã£ãŠ DM ã§ããããã«ãªã£ãã¯ãã§ãã æ©éãã¹ã¯ãªãããšãã£ã¿ã®ãå®è¡ãæ©èœãã€ãã£ãŠãåäœããã詊ããŠã¿ãŸãããã ã¹ã¯ãªãããšãã£ã¿ã®ãå®è¡ãã颿°ãéžæãéšåã§ postSampleSlackDM() ãéžæãããå®è¡ããã¿ã³ãã¯ãªãã¯ããŸãã ãããšãå床ã¢ã¯ã»ã¹æš©éã®èš±å¯ãæ±ããããŸãã ãSTEP.1ããšåãèŠé ã§ãèš±å¯ããŠãã ããã ã¢ã¯ã»ã¹æš©éã®èš±å¯ãå®äºãããã颿°ã®å®è¡ãåéããŸãã Slack ã® workspace ãèŠãŠã¿ãŸãããã äœæãã Slack App ãã This is a plain text section block. ãšãã DM ãå±ããŠããã¯ãã§ãã ããŒãã€ã³ã ããã§ãSTEP.2ãã¯ã¯ãªã¢ã§ãïŒ ããŒãã€ã³ãã玹ä»ããŸãã å®è£
ããäž»ãªé¢æ°ã¯äžèšã®éãã§ãã postSampleSlackDM() ä»®ã®æèš(This is a plain text section block.)ãDMæçš¿ãã Slackã«æçš¿ããã«ã¯ã chat.postMessage API ã䜿ããŸãã ãã® API 㯠HTTP ã䜿ã£ãŠã¢ã¯ã»ã¹ããŸãã ãã® Slack API ã䜿ãããã«ã GAS ãã HTTP Request ãããã«ã¯ UrlFetchApp classã䜿ããŸãã ãŸãããã® Slack API ã䜿ãéã«ã¯ãèªèšŒã®ããã«ãBot User OAuth Tokenããšã DM çžæãæå®ããããã«ãã¡ã³ã㌠IDããå¿
èŠã§ãããã®å€ã¯ GAS ããããã£ã«èšå®ããŸãããã GAS ããããã£ã«èšå®ããå€ãåç
§ããã«ã¯ Properties Serviceã䜿ããŸãã æçš¿å
容㯠Block Kit ã䜿ã£ãŠèšè¿°ããã®ãã¢ãã³ã§ãã Block Kit ã§ã¯ãæçš¿ããæèšãšå
±ã«ããã®è¡šç€ºåœ¢åŒãªã©ãæå®ããããã®ã¡ã¿ããŒã¿ãå«ãã æ§é åããŒã¿ãåãæ±ããããåèŠã ãšãšã£ãããã«ããæããããç¥ããŸããã ãããªãšã㯠Block Kit Builder ã䜿ããŸãããã æçš¿å
容ããã¬ãã¥ãŒããªãã Blocks ãçæã§ããã®ã§ã䟿å©ã§ãã STEP.3 Slackã®æäœã§GASãåãã ä»åºŠã¯ã Slack ãæäœã㊠GAS ãåãããŠã¿ãŸãããã ãŽãŒã« ãSTEP.3ãã§ã¯äžèšããŽãŒã«ã«ããŠé²ããŠãããŸãããã ã¹ã¯ãªãããšãã£ã¿ã§é¢æ°ãå®è¡ããŠã Slack ã«ãã¿ã³ã衚瀺ã§ãã ãã¿ã³ãæŒããšã GAS ãå¿çã㊠Slack ã«æ°ããæçš¿ãã§ãã æé ã³ãŒãã®å®è£
ãµã³ãã«ã³ãŒã GAS_step3.gs ãçšæããŠãããŸãã®ã§ããããã³ããŒã GAS ã®ã¹ã¯ãªãããšãã£ã¿ã«è²Œãä»ããŠäžæžãããŸãã 远èšã§ã¯ãªãäžæžãã§ãã®ã§ã泚æäžããã äžæžãããããã¹ã¯ãªãããšãã£ã¿ã®ããããžã§ã¯ããä¿åãã¢ã€ã³ã³ãã¯ãªãã¯ããã³ãŒãã®å€æŽãä¿åããŸãã ããã§ GAS ã®ã³ãŒãã¯å®æã§ãã Web ã¢ããªãšããŠãããã€ãã ä»åã®ã¹ãããã§ã¯ãå®è£
ããã³ãŒãã Web ã¢ããªãšããŠäœ¿çšããŸãã ãã®ããã«ãWeb ã¢ããªãšããŠãããã€ããŸãã ãŸãã¯ãã¹ã¯ãªãããšãã£ã¿ã®äžéšã®ããããã€ãã®ãæ°ãããããã€ããã¯ãªãã¯ããŸãã 衚瀺ããããæ°ãããããã€ããã€ã¢ãã°ããã¯ã¹ã«ãŠããçš®é¡ã®éžæãã®å³èã«ããæ¯è»ã¢ã€ã³ã³ãã¯ãªãã¯ãããŠã§ãã¢ããªããéžæããŸãã ãããšèšå®ã®é
ç®ã衚瀺ãããã®ã§ãäžèšã®éãã«èšå®ãããããã€ããã¯ãªãã¯ããŸãã 説æ é©åœãªèª¬æãèšå
¥ãã æ¬¡ã®ãŠãŒã¶ãŒãšããŠå®è¡ ãèªåã ã¢ã¯ã»ã¹ã§ãããŠãŒã¶ãŒ ãå
šå¡ã ãããã€ãå®äºãããšã以äžã®ãããªç»é¢ã衚瀺ãããŸãã ããŠã§ãã¢ããªãéšåã«è¡šç€ºããã URL ã¯ã次ã®ãSlack App ã® Interactivity ãæå¹åãããã§äœ¿çšããŸãã Slack App ã® Interactivity ãæå¹åãã ç¶ããŠã Slack App ã® Interactivity æ©èœãæå¹åããŸãã ãä»èŸŒã¿2ãã§ç»å Žãã Slack App èšå®ããŒãžã®ãInteractivity & ShortcutsãããŒãžãéããŸãã ãInteractivityãéšåã®å³ç«¯ã«è¡šç€ºãããŠããã¹ã€ããã On ã«ããŸãã ãããšããRequest URLããå
¥åãããã©ãŒã ã衚瀺ãããã®ã§ã GAS ã® Web ã¢ããªããããã€åŸã«è¡šç€ºããã URL ãå
¥åããŸãã æ€èšŒ ããã§ã Slack ãæäœã㊠GAS ãåãããããã«ãªã£ãã¯ãã§ãã æ©éã詊ããŠã¿ãŸãããã åºå€ãã¿ã³ã DM ãã ãŸãã¯ã Slack ã«ãã¿ã³ã衚瀺ã§ããã詊ããŸãããã ã¹ã¯ãªãããšãã£ã¿ã®ãå®è¡ãã颿°ãéžæãéšåã§ postSampleSlackDM() ãéžæãããå®è¡ããã¿ã³ãã¯ãªãã¯ããŸãã Slack ã® workspace ãèŠãŠã¿ãŸãããã äœæãã Slack App ãã ãåºå€ã ãã¿ã³ã DM ã§å±ããŠããã¯ãã§ãã åºå€ãã¿ã³ãã¯ãªãã¯ãã ä»åºŠã¯ããåºå€ããã¿ã³ãæŒããŠã Slack ã«æ°ããæçš¿ããããã詊ããŸãããã å
ã»ã©è¡šç€ºããããåºå€ããã¿ã³ãã¯ãªãã¯ããŸãã ãã°ãããããšããåºå€ããã¿ã³ãæ¶ãã代ããã« ãã¿ã³ã®æŒäžãæ€ç¥ããŸãã. ãšè¡šç€ºãããã¯ãã§ãã ããŒãã€ã³ã ããã§ãSTEP.3ãã¯ã¯ãªã¢ã§ãïŒ ããŒãã€ã³ãã玹ä»ããŸãã å®è£
ããäž»ãªé¢æ°ã¯äžèšã®éãã§ãã postSampleSlackDM() STEP.2 ã§ç»å Žããããä»å㯠Slack ã«ãã¿ã³ãæçš¿ããããã«æ¹ä¿®æžã¿ doPost() GAS ã® Web ã¢ããªæ©èœã§äœ¿çšããç¹å¥ãªé¢æ° Slackããã®éç¥ãåããŠã ãã¿ã³ã®æŒäžãæ€ç¥ããŸãã. ãšæçš¿ãã Slack ã«æçš¿ãããã¿ã³ãæŒããããšãå€éš(GAS)ã«éç¥ããã«ã¯ã Slack ã® Interactivity æ©èœ ã䜿ããŸãã ä»åã¯ã Interactivity æ©èœã«ãã£ãŠéç¥ãããã block_actions payloads ãå©çšããŠããŸãã ãã㯠HTTP Request ã䜿ã£ãŠéç¥ãããŸãã GAS ã§ block_actions payloads çã®å€éš(Slack)ããã® HTTP Request ãåããã«ã¯ã Web ã¢ããªæ©èœ ã䜿ããŸãã HTTP GET method ã®å Žå㯠doGet() function ãã HTTP POST method ã®å Žå㯠doPost() function ã call ãããããã«ãªã£ãŠããŸãã Web ã¢ããªæ©èœã䜿ãã«ã¯ããããã€ãå¿
èŠã§ãã ãŸãã GAS ã®ã³ãŒããæŽæ°ããå ŽåããéœåºŠãããã€ãå¿
èŠãªã®ã§ãã³ãŒãã®è©Šè¡é¯èª€ãããå Žåã¯æ°ãã€ããŸãããã GAS ã® Web ã¢ããªæ©èœã§åãã HTTP Request ã«å¯Ÿãã response å
容ãæå®ããã«ã¯ã Content Service ã䜿çšããŸãã Slack ã® block_actions payloads ã®éç¥æã¯ã空㮠response ãããã°ããããããµã³ãã«ã³ãŒãã§ã¯å€§ããäœ¿ãæ¹ã¯ããŠããŸãããã Content Service ã¯ä»ã®çšéã§ã¯å€§åã«ãªã£ãŠããèŠçŽ ã§ãã®ã§ãèŠããŠãããŸãããã block_actions payloads ã§éç¥ãåããéã«ã æ°ã㪠DM ããããå ŽåããSTEP.2ãã§äœ¿çšãã chat.postMessage API ã§æçš¿ã§ããŸãã ãããã block_actions payloads ã«å«ãŸãã response_url å®ã« HTTP POST request ããææ®µãå©çšã§ããŸãã response_url å®ã« request ããå Žåã Token ã DM å
ã®æå®ãäžèŠãªãããå©äŸ¿æ§ãé«ãã§ãã STEP.4 Slackã§æå»ãã éã«æåŸã®ã¹ãããã§ãã ãã®ã¹ãããã§ã¯ãä»ãŸã§æ©ãã§ããã¹ãããã§ã®çµéšãçãããæçµç®çã§ãããSlackããæå»ãããããšãå®çŸããŸãããã ãŽãŒã« ãSTEP.4ãã§ã¯äžèšããŽãŒã«ã«ããŠé²ããŸãããã ã¹ã¯ãªãããšãã£ã¿ã§é¢æ°ãå®è¡ããŠãåºå€ããã¿ã³ãSlackã«è¡šç€ºã§ãã ãåºå€ããã¿ã³ãæŒããŠSpreadsheetã«éå§æå»ãèšé²ã§ãã éå§æå»ã®èšé²ã«æåããããéå€ããã¿ã³ãSlackã«è¡šç€ºã§ãã ãéå€ããã¿ã³ãæŒããŠSpreadsheetã«çµäºæå»ãèšé²ã§ãã æé ã³ãŒãã®å®è£
ãµã³ãã«ã³ãŒã GAS_step4.gs ãçšæããŠãããŸãã®ã§ããããã³ããŒã GAS ã®ã¹ã¯ãªãããšãã£ã¿ã«è²Œãä»ããŠäžæžãããŸãã 远èšã§ã¯ãªãäžæžãã§ãã®ã§ã泚æäžããã äžæžãããããã¹ã¯ãªãããšãã£ã¿ã®ããããžã§ã¯ããä¿åãã¢ã€ã³ã³ãã¯ãªãã¯ããã³ãŒãã®å€æŽãä¿åããŸãã ããã§ GAS ã®ã³ãŒãã¯å®æã§ãã Web ã¢ããªã®åããã〠ãSTEP.3ãã®ããŒãã€ã³ãã«ãæžãããšããã Web ã¢ããªã«ææ°ã®ã³ãŒããåæ ããã«ã¯å床ãããã€ãå¿
èŠã§ãã ãã ãããæ°ãããããã€ããäœãå¿
èŠã¯ãããŸããã æ¢ã«ååšããããããã€ããç·šéããŠãæ°ããããŒãžã§ã³ã®ã³ãŒããåæ ããŸã ãŸãã¯ãã¹ã¯ãªãããšãã£ã¿ã®äžéšã®ããããã€ãã®ããããã€ã管çããã¯ãªãã¯ããŸãã 衚瀺ãããããããã€ã管çããã€ã¢ãã°ããã¯ã¹ã®å·ŠåŽã®ãã¢ã¯ãã£ããæ ã«è¡šç€ºãããŠãããæ¢åã®ãããã€ãéžæããŸãã å³åŽã®ãèšå®ãã®å³ç«¯ã«è¡šç€ºãããŠãããç·šéãã¢ã€ã³ã³ãã¯ãªãã¯ããŸãã ãããŒãžã§ã³ãã®é
ç®ã§ãæ°ããŒãžã§ã³ããéžæããŸãã ããããã€ããã¿ã³ãã¯ãªãã¯ããŸãã ãããšãæ°ãããããã€ããå®äºãããšãã®ããã«ããããã€ã®æŽæ°ãç»é¢ã§ Web ã¢ã㪠㮠URL ã衚瀺ãããŸãã ãã® URL ã¯ãæ°ãããããã€ããå®äºãããšãã® URL ãšåäžã§ãã Slack ã® Interactivity ã® Request URL ãæŽæ°ããå¿
èŠã¯ãããŸããã æ€èšŒ ããã§ã Slack ããæå»ãã§ããããã«ãªã£ãã¯ãã§ãã æ©éã詊ããŠã¿ãŸãããã åºå€ãã¿ã³ã DM ãã ãŸãã¯ã ãSTEP.3ããšåæ§ã«Slack ã«ãã¿ã³ã衚瀺ã§ããã詊ããŸãããã ã¹ã¯ãªãããšãã£ã¿ã®ãå®è¡ãã颿°ãéžæãéšåã§ postSampleSlackDM() ãéžæãããå®è¡ããã¿ã³ãã¯ãªãã¯ããŸãã Slack ã® workspace ãèŠãŠã¿ãŸãããã äœæãã Slack App ãã ãåºå€ã ãã¿ã³ã DM ã§å±ããŠããã¯ãã§ãã Slack ããéå§æå»ãæå»ãã æ¬¡ã«ããŸããSTEP.3ããšåæ§ã«ããåºå€ããã¿ã³ãæŒããŸãã äœããä»åºŠã¯ãSTEP.3ããšçµæãå€ãããŸãã Slack ã® workspace ãèŠãŠã¿ãŸããããAttendance recorded. ã®æç« ãšå
±ã«ãéå€ããã¿ã³ã衚瀺ãããŠããã¯ãã§ãã ããã« Spreadsheet ãèŠãŠã¿ãŸãããã æ°ããªè¡ã«ãå®è¡ããæ¥ä»ãšæå»ãå
¥åãããŠããã¯ãã§ãã å
ã»ã©è¡šç€ºããããåºå€ããã¿ã³ãã¯ãªãã¯ããŸãã ãã°ãããããšããåºå€ããã¿ã³ãæ¶ãã代ããã« ãã¿ã³ã®æŒäžãæ€ç¥ããŸãã. ãšè¡šç€ºãããã¯ãã§ãã Slack ããçµäºæå»ãæå»ãã æåŸã«ãéå€ããã¿ã³ãæŒããŠã¿ãŸãããã ãããšä»åºŠã¯ Slack ã«ã¯ Leaving recorded. ã®æç« ãšå
±ã«ãåã³ãåºå€ããã¿ã³ã衚瀺ãããŠããã¯ãã§ãã ãã㊠Spreadsheet ã«ã¯çµäºæå»ãå
¥åãããŠããã¯ãã§ãã ããŒãã€ã³ã éã«å
šãŠã®ã¹ããããå®éããŸããïŒ æåŸã®ããŒãã€ã³ãã玹ä»ããŸãã å®è£
ããäž»ãªé¢æ°ã¯äžèšã®éãã§ãã postSampleSlackDM() åºå€ãã¿ã³ãæçš¿ STEP3ã®ã³ãŒããã倿Žãªã doPost() åºå€ãã¿ã³ãæŒãããå Žå recordAttendance() ãå®è¡ åºå€æå»ãå
¥å recordAttendance() èªäœã¯ STEP.1 ã®ã³ãŒããã倿Žãªã å¿çãæçš¿ãã éå€ãã¿ã³ãæçš¿ STEP.3 ã®ãµã³ãã«æç« æçš¿ãããã¿ã³æçš¿ã«å€æŽ éå€ãã¿ã³ãæŒãããå Žå recordLeaving() ãå®è¡ é倿å»ãå
¥å recordLeaving() èªäœã¯ STEP.1 ã®ã³ãŒããã倿Žãªã å¿çãæçš¿ãã åºå€ãã¿ã³ãæçš¿ postSampleSlackDM() ãåèã«ããã°æ¯èŒçç°¡åã«æ¹ä¿®å¯èœ ãŸãšã ããŸãããããŸãããïŒ ä»åã¯ã Slack App ãš Google Spreadsheetã Google Apps Script ã䜿ã£ãŠå€æ å
¥åã·ã¹ãã ãæ§ç¯ããŸããã ãããããã®æ§æã¯å€æ å
¥åã«éãããæ§ã
ãªçšéã«ãå¿çšã§ããŸãã äŸãã°å®¶èšç°¿ã Slack é¢çœçºèšéãªã©ãäœãããã§ãã æ¯éçããããä»åã®æ§ç¯çµéšãçãããŠã䟿å©ãª Slack App ãæ§ç¯ããŠã¿ãŠã¯ãããã§ããããïŒ èè
ãããã£ãŒã« è²æ²¢ äº®ä» æ ªåŒäŒç€ŸWESEEK / ã·ã¹ãã ãšã³ãžã㢠ãããã¯ãŒã¯ SIer ãšããŠãè€æ°ã®å€§å£é¡§å®¢ãããã¯ãŒã¯ã®éçšã»èšèšã»æ§ç¯ãããžã§ã¯ããªã©ã«6幎åŸäºããåŸãããã°ã©ãã³ã°ã®åéã«ãæºãããããšäžå¿µçºèµ·ãã 2016 幎㫠WESEEK ãžå
¥ç€ŸãçŸåšã§ã¯ããããã¯ãŒã¯æè¡ã«æãããšã³ãžãã¢ãšããŠã®ç¹æ§ãçããã倧æIXäºæ¥è
ã®é¡§å®¢ã»èšå管çã·ã¹ãã ããããã¯ãŒã¯ããããžã§ãã³ã°ã·ã¹ãã ã®èšèšã»æ§ç¯ããããžã§ã¯ãç®¡çæ¥åãªã©ã«åŸäºããŠããã æ ªåŒäŒç€ŸWESEEKã«ã€ããŠ æ ªåŒäŒç€ŸWESEEKã¯ãã·ã¹ãã éçºã®ãããã§ãã·ã§ãã«éå£ã§ãã ãçŸåšã®äž»ãªäºæ¥ã é信倧æäŒæ¥ã®æ¥åãããŒèªååãããžã§ã¯ã ãœãŒã·ã£ã«ã²ãŒã ã®åèšéçº èªç€ŸçºãªãŒãã³ãœãŒã¹ãããã¯ããGROWIããGROWI.cloudãã®éçº GROWI GROWIã¯ãMarkdownèšæ³ã§ããŒãžãèšè¿°ã§ãããªãŒãã³ãœãŒã¹ã®Wikiã·ã¹ãã ã§ãã GROWI.cloud GROWI.cloudã¯OSSã®GROWIãå°éçç¥èããªããŠãç°¡åã«éçšã»ç®¡çã§ãããæ³äººã»å人åãã®åçšãµãŒãã¹ã§ãã 倧æSIerã»ISPãäžå°äŒæ¥ã倧åŠã®ç 究宀ãªã©æ§ã
ãªå Žæã§ãå©çšããã ããŠãããŸãã ãäž»ãªç¹åŸŽã ããã¹ããå³è¡šãã©ãã©ãæžããã匷åãªç·šéæ©èœ ããŒã æ¡å€§ã«è¿
éã«å¯Ÿå¿ã§ãã管çè
åãæ©èœãæäŸ å
å®ããæ©èœã»ãµããŒãã§ãšã³ã¿ãŒãã©ã€ãºã«ãå¯Ÿå¿ ãå°å
¥äºäŸèšäºãã€ã³ã¿ãŒããããã«ããã£ãŒãæ ªåŒäŒç€Ÿæ§ æ ªåŒäŒç€ŸHIKKYïŒVRæ³äººHIKKYïŒæ§ WESEEK Tech Conference WESEEK Tech Conferenceã¯ãæ ªåŒäŒç€ŸWESEEKãäž»å¬ãããšã³ãžãã¢åãã®å匷äŒã§ããWESEEKã«æå±ãããšã³ãžãã¢ãæ§ã
ãªããŒãã§çºè¡šãè¡ãäºå®ã§ãã æ¬¡åã¯ã11/25(æš) 19:00~20:00ã«éå¬äºå®ã§ãã WESEEKããµãŒãã¹ãæäŸããŠãã GROWI.cloud ãäŸã«ãStripeã§ç°¡åã«æ§ç¯ã»ã«ã¹ã¿ãã€ãºã§ãããµãã¹ã¯ãªãã·ã§ã³å課éãµãŒãã¹ã®äœãæ¹ãã玹ä»ããŸãïŒ çŸåšãconnpassãTECH PLAYã§åå åä»äžã§ããçæ§ã®ãåå ããåŸ
ã¡ããŠãããŸãïŒconnpassTECH PLAY äžç·ã«åã仲éãåéããŠããŸã æ±äº¬ã®é«ç°éŠ¬å Žãªãã£ã¹ã倧åã«ããå¥åºãµãã©ã€ããªãã£ã¹ã«ãŠãšã³ãžãã¢ãåéããŠãããŸããäžéæ¡çšã ãã§ã¯ãªããã€ã³ã¿ãŒã³ã·ãããç©æ¥µçã«åãå
¥ããŠããŸãïŒ è©³ããåéèŠé
ã¯ãåŒç€ŸHPã®æ¡çšããŒãžããã確èªãã ããã
ãã®æçš¿ã¯ãåŒç€ŸãæäŸããWESEEK TECHéä¿¡ã®äžç°ã§ãã WESEEK TECHéä¿¡ãšã¯ãWESEEKã®ãšã³ãžãã¢ããã£ããã¢ããããæè¡ã«é¢ããæ
å ±ããtechããã°ãéããŠå®æçã«çºä¿¡ããŠãããã®ã§ãã ã¯ããã« ããã«ã¡ã¯ãã·ã¹ãã ãšã³ãžãã¢ã®ãããã§ããæ¬èšäºã§ã¯ãmongoose ã® virtual颿°ã«ã€ããŠã話ãããŸãããã®èšäºã§åãäžããå
容ã¯ä»¥äžã«ãªããŸãã Mongoose ãšã¯ mongooseã®åºç€ç¥èãšäœ¿ãæ¹ã«é¢ããŠã¯ãã¡ãã®èšäºãåç
§ãã ããããŸããåŒã³æ¹ã¯ããã³ã°ãŒã¹ãã§ãã Virtuals ãšã¯ MongooseãæäŸããvirtual(ä»®æ³)颿°ã䜿çšããããšã§ãä»®æ³çã«ãã£ãŒã«ããå®çŸ©ããããšãã§ããŸããããã¯MongoDBã«æ°žç¶å(ä¿å)ãããŸããããããã¥ã¡ã³ãååŸæã«ã¯ä»äžãããŸãã ãäŸã // (1)userSchemaããå®çŸ©ãã const userSchema = new Schema({ name: { first: String, last: String } }); // äžèšã§å®çŸ©ããschemaãçšããŠãUserã¢ãã«ãå®çŸ©ãã const User = mongoose.model('User', userSchema); // ããã¥ã¡ã³ã(Userã€ã³ã¹ã¿ã³ã¹)ãçæãã const user1 = new User({ name: { first: 'Hanako', last: 'Yamada' } }); ãã®æãuserã®ãã«ããŒã ãåºåãããå Žåã以äžã®ããã«ããŸãã // "Hanako Yamada" ãšåºåããã console.log(user1.name.first + ' ' + user1.name.last); ãã ããäžèšã®ããã«æ¯åé£çµããã®ã¯ãé¢åãªå ŽåããããŸãã ãã®æãVirtual颿°ã䜿çšããŠãä»®æ³ãã£ãŒã«ããäœæããããšãã§ããŸãã (fullNameãMongoDBã«æ°žç¶åãããããšã¯ãããŸãã) // ã¹ããŒããå®çŸ©ããåŸã«virtual颿°ã䜿ããä»®æ³ããããã£fullNameãå®çŸ© userSchema.virtual('fullName').get(function() { // firstName ãšlastNameãé£çµãããå€ãè¿ã return this.name.first + ' ' + this.name.last; }); ããããããšã«ãã£ãŠãmongooseã¯fullNameããããã£ã«ã¢ã¯ã»ã¹ãã床ã«ãgetter颿°ãåŒã³åºããŸãã ããã¯ãçµå©ããŠååãå€ããå Žåãªã©ãåçãªããŒã¿ãååŸãããæã«äŸ¿å©ã§ãã console.log(user1.fullName); // Hanako Yamada Virtuals ã®å©ç¹ ã§ã¯ãvirtualã®å©ç¹ãšã¯ãªãã§ããããïŒ ããã§ã¯äž»ã«äºã€ãããŸãã äžã€ç®ã¯ããtoJSON / toObject ã«åæ ãããªãããããã£ã®å®çŸãã§ãã mongooseã§ã¯ãããã¥ã¡ã³ãããªããžã§ã¯ããŸãã¯JSONã«å€æãããšãã«ããã©ã«ãã§ã¯virtualsãå«ã¿ãŸããã å®è£
è
ã«ãã£ãŠvirtualsãå«ãããå«ããªãããéžã¶ããšãã§ããŸãã å«ã¿ããå Žåã以äžã®ããã«ã¹ããŒãã®ãªãã·ã§ã³ã§èšå®ããå¿
èŠããããŸãã schema.set("toJSON", { virtuals: true }); schema.set("toObject", { virtuals: true }); äºã€ç®ã¯ããä¿è·ãããããããã£ã®å®çŸãã§ãã Vitualsã§å®çŸ©ãããä»®æ³ããããã£ã¯ãä¿è·ãããŠããæžãæããã§ããŸããã getter ãš setter ãåããŠå®çŸ©ããæ©èœããã£ãŠããŸãã ãã㯠defineProperty ã«ãã£ãŠå®çŸãããŠããŸãããmongoose ããã¯é¢ããã®ã§ããã®èšäºã§ã¯å²æããŸãã詳ããã¯ä»¥äžã®ãµã€ãããåç
§ãã ããã ãJavaScriptã definePropertyã¡ãœãããšã¯ïŒéåžžã®ããããã£è¿œå ãšã®éã Virtuals ã populate ãã Virtuals ã® populate ãšã¯ã©ãããããšã§ãããã? Mongooseã®å
¬åŒããã¥ã¡ã³ããäŸã«èª¬æããŸãã Author ãš BlogPost ãšããäºã€ã®ã¢ãã«ããããšããŸãã // Authorã¹ããŒãã¯ãèè
åãšè€æ°ã®ããã¯æçš¿ãä¿æãã const AuthorSchema = new Schema({ name: String, posts: [{ type: mongoose.Schema.Types.ObjectId, ref: 'BlogPost' }] }); // BlogPostã¹ããŒãã¯ãã¿ã€ãã«ãšè€æ°ã®ã³ã¡ã³ããä¿æãã const BlogPostSchema = new Schema({ title: String, comments: [{ author: { type: mongoose.Schema.Types.ObjectId, ref: 'Author' }, content: String }] }); const Author = mongoose.model('Author', AuthorSchema, 'Author'); const BlogPost = mongoose.model('BlogPost', BlogPostSchema, 'BlogPost'); äžèšã®äŸã¯æªãã¹ããŒãã®ãã¶ã€ã³ã§ãããªãã§ãããã? 1äžä»¶ãè¶
ããããã°æçš¿ãæžããŠããéåžžã«å€äœãªèè
ãããå Žåããã®äœæè
ã®ããã¥ã¡ã³ãã¯å·šå€§ãªãã®ã«ãªãããµãŒããŒãšã¯ã©ã€ã¢ã³ãã®äž¡æ¹ã§ããã©ãŒãã³ã¹ã®åé¡ãåŒãèµ·ãããŸãã ã«ãŒãã£ããªãã£ã®ååã§ã¯ãäœæè
ããããã°æçš¿ãžã®ãããª1察å€ã®é¢ä¿ã¯ããå€ãåŽã«ä¿åããå¿
èŠããããšè¿°ã¹ãŠããŸãã ã€ãŸããããã°ã®æçš¿ã«ã¯ãäœæè
ããä¿åããå¿
èŠããããäœæè
ã¯ããã¹ãŠã®æçš¿ããä¿åããå¿
èŠã¯ãããŸããã ããã§ã以äžã®ããã«æžãæããŸãã // Authorããã¥ã¡ã³ããããããã°æçš¿ããçãããèè
åãã®ã¿ãä¿æãã const AuthorSchema = new Schema({ name: String }); // BlogPostããã¥ã¡ã³ãã«ãèè
åãã远å ãã const BlogPostSchema = new Schema({ title: String, author: { type: mongoose.Schema.Types.ObjectId, ref: 'Author' }, comments: [{ author: { type: mongoose.Schema.Types.ObjectId, ref: 'Author' }, content: String }] }); ãã ããããã2ã€ã®ã¹ããŒãã¯ãèè
ã®ããã°æçš¿ãªã¹ããžã®å
¥åããµããŒãããŠããŸããã ããã§ãä»®æ³populateãç»å ŽããŸããä»®æ³populateãšã¯ã以äžã«ç€ºãããã«ãrefãªãã·ã§ã³ãæã€ä»®æ³ããããã£ã§populate()ãåŒã³åºãããšãæå³ããŸãã // `ref`ããããã£ã§virtualã®populateãæå¹ã«ããŸã AuthorSchema.virtual('posts', { ref: 'BlogPost', // åç
§ããã¢ãã«(`BlogPost`) localField: '_id', // åç
§å
ã®ããããã£(Authorã®`_Id`ããããã£) foreignField: 'author' // åç
§å
ã¢ãã«ã®ããããã£(BlogPostã®`author`ããããã£) }); const Author = mongoose.model('Author', AuthorSchema, 'Author'); const BlogPost = mongoose.model('BlogPost', BlogPostSchema, 'BlogPost'); ããããããšã«ãã£ãŠã以äžã«ç€ºãããã«ãäœæè
ã®æçš¿ãpopulateã§ããŸãã const author = await Author.findOne().populate('posts'); author.posts[0].title; // æåã«æçš¿ããblogã®ã¿ã€ãã« ãŸãšã Virtualsãšã¯ MongoDBã«ã¯ä¿åãããªãä»®æ³ãã£ãŒã«ããã€ããããšãã§ãã ããã¥ã¡ã³ãååŸæã«åç
§ããããšãã§ãã ref, localField, foreignFieldãæå®ããããšã«ãã£ãŠãvirtualsã®populateãå¯èœã«ãªã ãããã§ããã§ããããã æåŸãŸã§èªãã§ãã ããããããšãããããŸããïŒ åèã«ãããŠããã ããèšäº Mongoose Virtuals Mongoose GitHub MDN Web Docs
ãã®æçš¿ã¯ãåŒç€ŸãæäŸããWESEEK TECHéä¿¡ã®äžç°ã§ãã WESEEK TECHéä¿¡ãšã¯ãWESEEKã®ãšã³ãžãã¢ããã£ããã¢ããããæè¡ã«é¢ããæ
å ±ããtechããã°ãéããŠå®æçã«çºä¿¡ããŠãããã®ã§ãã ã¯ããã« ããã«ã¡ã¯ïŒWESEEKã€ã³ã¿ãŒã³ã®å®®æ²¢ã§ããä»åã®èšäºã§ã¯Javascriptã®ã©ã€ãã©ãªã§ããSocket.ioãçšããŠç°¡åãªãã£ããã¢ããªãäœã£ãŠã¿ãããšæããŸãã Socket.ioãšã¯ å
¬åŒããã¥ã¡ã³ãã«ãããšãããããããã©ãããã©ãŒã ã«å¯Ÿå¿ããåæ¹åã»äœã¬ã€ãã³ã·ãŒéä¿¡ïŒå·çè
èš³ïŒããšãããŸããSocket.ioã¯ãäž»ã«Websocketã䜿ã£ãŠåäœããŸããNode.jsäžã§åäœãããµãŒããŒãµã€ãã©ã€ãã©ãªããã©ãŠã¶äžã§åäœããã¯ã©ã€ã¢ã³ããµã€ãã©ã€ãã©ãªãæäŸãããŠããŸãã WebSocketã¯ãµãŒããŒã»ã¯ã©ã€ã¢ã³ãéãã€ãã³ãé§åã§åæ¹åã«éä¿¡ãå¯èœã«ããæè¡ã§ããWebéä¿¡ãšèšãã°HTTPãšãããããã³ã«ãããè³ã«ãããšæããŸããããã¯1ã€ã®ã³ãã¯ã·ã§ã³ã§1ã€ã®ãªã¯ãšã¹ãããéãããšãã§ããªããããæ°ããæ
å ±ãååŸããããã«ã¯ããŒãžããªããŒãããå¿
èŠããããŸããããã£ããã¢ããªãSNSãªã©ãªã¢ã«ã¿ã€ã ã«æ
å ±ãååŸãããå Žåããã¯äžåãã§ãã察ããŠWebsocketã¯ãµãŒããŒãšã¯ã©ã€ã¢ã³ããäžåºŠã³ãã¯ã·ã§ã³ãè¡ãã°ãªã¢ã«ã¿ã€ã ã«æ
å ±ã®ãããšããå¯èœã«ãªããŸãã ä»åã¯Socket.Ioã®äžã§ãåºæ¬çãªã¡ãœããã§ãã on() ãš emit() ã®ã¿ã«çµã£ãŠã玹ä»ããŸãã // ãµãŒã㌠io.on("connection", (socket) => { // "hello" ãšããã€ãã³ããçºç«ããã"world" ãšããæååãéä¿¡ãã socket.emit("hello", "world"); }); // ã¯ã©ã€ã¢ã³ã // "hello" ãšããã€ãã³ããåä¿¡ã§ããããã«ãã // ååŸããã¡ãã»ãŒãžãåºåãã socket.on("hello", (message) => { console.log(message); }); ç°å¢æ§ç¯ ãã£ããã¢ããªãäœæããã«ããããå¿
èŠãªã©ã€ãã©ãªã®ã€ã³ã¹ããŒã«ããã¡ã€ã«ããã£ã¬ã¯ããªãçšæããŠãããŸãã äžèšã®ãããªãã£ã¬ã¯ããªãŒããã¡ã€ã«ãçšæããŸãã simple_chat/ âââ package.json âââ src âââ index.html âââ index.js package.jsonã«ã¯ä»¥äžã®ããã«èšè¿°ããŸãããã£ããã¢ããªã§å¿
èŠã«ãªãã©ã€ãã©ãªïŒexpressãšsocket.ioïŒã"dependencies"ã«å
¥ããŠãããŸãã { "name": "simple_chat", "version": "1.0.0", "main": "index.js", "license": "MIT", "dependencies": { "express": "^4.17.1", "socket.io": "^4.3.1" } } å¿
èŠãªã©ã€ãã©ãªãŒã®ã€ã³ã¹ããŒã«ãè¡ããŸããnodeãnpmãyarnãèªåã®ç°å¢ã«ã€ã³ã¹ããŒã«ãããŠããå¿
èŠããããŸãã # simple_chatãã£ã¬ã¯ããªã«å
¥ã $ cd simple_chat # ã©ã€ãã©ãªãŒã®ã€ã³ã¹ããŒã« $ yarn install simple_chatãã£ã¬ã¯ããªãŒé
äžã«ãnode_modulesãšyarn.lockãæ°ãã«è¿œå ãããŠããããšã確èªã§ãããšæããŸããã²ãšãŸãããã§ãã£ããã¢ããªãäœæã§ããç¶æ
ã«ãªããŸããã ãã£ããã¢ããªãäœã ãã£ãããã£ããã¢ããªãäœæããŠãããŸãã ç·šéãããã¡ã€ã«ã¯ä»¥äžã®éãã«ãªããŸãã index.jsïŒãµãŒããŒïŒ index.htmlïŒããã³ãïŒ ãŸãã¯expressã䜿ã£ãŠãµãŒããŒãäœæããŸãã index.js const express = require('express'); const http = require('http'); const app = express(); const server = http.Server(app); const PORT = 3000; // ã«ãŒãã£ã³ã°ã®èšå®ã'/' ã«ãªã¯ãšã¹ãããã£ãå Žå src/index.html ãè¿ã app.get('/', (req, res) => { res.sendFile(__dirname + '/index.html'); }); // 3000çªããŒãã§HTTPãµãŒããŒãèµ·å server.listen(PORT, () => { console.log(`listening on port ${PORT}`); }); index.html simple chat ã¿ãŒããã«ãéããŠãµãŒããŒãèµ·åããŸãã $ node src/index.js ãµãŒããŒåŽã«ã¯ listening on port 3000 ãšãããã°ãåºåããããã©ãŠã¶ã§ http://localhost:3000/ ãéã㚠以äžã®ããã«è¡šç€ºãããŠãããšæããŸãã æ¬¡ã«Socket.ioã®åŠçãèšè¿°ããŠãããŸãã index.js const express = require('express'); const http = require('http'); // Socket.ioãã€ã³ããŒã const socketIo = require('socket.io'); const app = express(); const server = http.Server(app); // åæå const io = socketIo(server); const PORT = 3000; app.get('/', (req, res) => { res.sendFile(__dirname + '/index.html'); }); server.listen(PORT, () => { console.log(`listening on port ${PORT}`); }); // ã¯ã©ã€ã¢ã³ããšã®ã³ãã¯ã·ã§ã³ã確ç«ããã'connected'ãšãã衚瀺ããã io.on('connection', (socket) => { console.log('connected'); }); index.html simple chat // ãµãŒããŒãžæ¥ç¶ const socket = io(); ãµãŒããŒãåèµ·åãlocalhost:3000ã«ã¢ã¯ã»ã¹ãããšãµãŒããŒåŽã®ãã°ã«connectedãåºåãããŠãããšæããŸãã ç¶ããŠã¯ã©ã€ã¢ã³ãåŽã§inputã«ããã¹ããå
¥åãéä¿¡ãã¿ã³ãæŒããšãµãŒããŒåŽã«ã¡ãã»ãŒãžãéä¿¡ããã€ãã³ããäœæããŸãã index.js const express = require('express'); const http = require('http'); const socketIo = require('socket.io'); const app = express(); const server = http.Server(app); const io = socketIo(server); const PORT = 3000; app.get('/', (req, res) => { res.sendFile(__dirname + '/index.html'); }); server.listen(PORT, () => { console.log(`listening on port ${PORT}`); }); io.on('connection', (socket) => { console.log('user connected'); // 'sendMessage' ãšããã€ãã³ãåã§åä¿¡ã§ãã // 第äžåŒæ°ã«ã¯åä¿¡ããã¡ãã»ãŒãžãå
¥ãããã°ã«åºåãã socket.on('sendMessage', (message) => { console.log('Message has been sent: ', message); }); }); index.html simple chat const socket = io(); const clearText = () => { document.getElementById('inputText').value = ''; } // éä¿¡ãã¿ã³ã®ã¯ãªãã¯ã€ãã³ããæ€ç¥ document.getElementById('sendButton').addEventListener('click', () => { // inputã«å
¥åãããããã¹ããååŸ let inputMessage = document.getElementById('inputText').value; if (inputMessage === '') { return; } // 'sendMessage' ã€ãã³ããçºç«ãã¡ãã»ãŒãžãéä¿¡ socket.emit('sendMessage', inputMessage); // input å
ã®ããã¹ãã空ã«ãã clearText(); }); ãµãŒããŒãåèµ·åãlocalhost:3000ã«ã¢ã¯ã»ã¹ããŸããinputã«é©åœãªæååãå
¥åãéä¿¡ãã³ã¿ã³ãæŒããšãµãŒããŒåŽã«å
¥åãããæååãåºåãããŠãããšæããŸãã æåŸã«ãµãŒããŒããå
šãŠã®ã¯ã©ã€ã¢ã³ãã«å¯ŸããŠå
¥åããæååã衚瀺ã§ããããã«ããŸãã index.js const express = require('express'); const http = require('http'); const socketIo = require('socket.io'); const app = express(); const server = http.Server(app); const io = socketIo(server); const PORT = 3000; app.get('/', (req, res) => { res.sendFile(__dirname + '/index.html'); }); server.listen(PORT, () => { console.log(`listening on port ${PORT}`); }); io.on('connection', (socket) => { console.log('user connected'); socket.on('sendMessage', (message) => { console.log('Message has been sent: ', message); // 'receiveMessage' ãšããã€ãã³ããçºç«ãåä¿¡ããã¡ãã»ãŒãžãå
šãŠã®ã¯ã©ã€ã¢ã³ãã«å¯ŸããŠéä¿¡ãã io.emit('receiveMessage', message); }); }); index.html simple chat const socket = io(); const clearText = () => { document.getElementById('inputText').value = ''; } const addMessageList = (message) => { const ul = document.getElementById('messageList'); const li = document.createElement('li'); const text = document.createTextNode(message); li.appendChild(text); ul.appendChild(li); }; document.getElementById('sendButton').addEventListener('click', () => { let inputMessage = document.getElementById('inputText').value; if (inputMessage === '') { return; } socket.emit('sendMessage', inputMessage); clearText(); }); // 'receiveMessage' ã€ãã³ãã®çºç«ãæ€ç¥ // 第äžåŒæ°ã«ã¯åä¿¡ããã¡ãã»ãŒãžãå
¥ã socket.on('receiveMessage', (message) => { // åä¿¡ããã¡ãã»ãŒãžãulã¿ã°ã«æ¿å
¥ addMessageList(message); }); æ¬èšäºåé ã®gifã®ãããªæåã«ãªã£ãã宿ã§ãïŒãç²ãæ§ã§ãããä»åæ±ã£ããœãŒã¹ã³ãŒãã¯ãgithubã«äžããŠããã®ã§ãã¯ããŒã³ããŠæ°è»œã«ç«ã¡äžããããšãã§ããŸãã åèã«ãããŠããã ããèšäº Socket.io
ãã®èšäºã¯ã 2021/9/30 ã«è¡ããã WESEEK Tech Conference ã®å
容ã§ãã ç®æ¬¡ ãã¹ãã«ã€ããŠæ®æ®µæãããš ã¿ãªããæ®æ®µãã¹ãã«ã€ããŠæã£ãŠããããšã¯ãããŸããïŒ ãã¹ããã©ãããæžããŠããã°ãããããããªã ãã¹ãã®ç¡ããããžã§ã¯ãã«ã©ããã£ãŠå°å
¥ããã°ããã®ã ãŸããŠããTDDã¯æ·å±
ãé«ãã ãã¹ããæžãæéããªã ãã¹ããæžãããããã¡ãã¡æå
ã§å®è¡ããã®ãé¢å 仿§ãšå®è£
ã®æŽåæ§ãåãããã®ãå€§å€ ãã¹ããæžãéã«ãã®ãããã®ããšã«ã€ããŠæã£ããããããšãããã®ã§ã¯ãªãã§ãããããããã§ãä»åã¯äžèšã«ã€ããŠã話ãããŠããããšæããŸãã ãã¹ããã§ããããã«ãªããŸã§ ããŒã«ã®å°å
¥ ã¯ããã®äžæ© ãã¹ãã®æžãæ¹ Gitã«ããã·ã¥ããããã¹ããèªåã§å®è¡ããæ¹æ³ CI æ«æããªããã¹ãã®å¿åŸ éçºã«ãŒã« 仿§ãšå®è£
ã®æŽåæ§ã楜ã«ãã ã¹ããŒããã¡ãŒã¹ã ä»åã®å
容ã¯ããŸã ãããžã§ã¯ãã«ãã¹ãããŒã«ãå°å
¥ããããšããªãæ¹ããã¹ããæžããããšããªãæ¹åãã®ãå
¥éçãªå
容ãšãªããŸãã ãã¹ããã§ããããã«ãªããŸã§ ããŒã«ã®å°å
¥ ãŸãããã¹ããå®è¡ã§ããããã«ãªãããã®ããŒã«ã®å°å
¥ãè¡ããŸãã ãã¹ããã§ããããã«ãªããŸã§ã«ããããšã¯äžèšã§ãã RSpec ã®å°å
¥ ãã¹ãããŒã¿ããŒã¹ã®èšå® RSpec ã®èµ·åãéãããèšå® RSpec ã®äžŠåå®è¡ã®èšå® RSpec ã®å®è¡ RSpec ã®å°å
¥ Ruby ã§äœ¿ãããã¹ããã¬ãŒã ã¯ãŒã¯ RSpec ãã€ã³ã¹ããŒã«ããŸãã ããã¯ã Gemfile ã«äžèšãèšèŒã㊠bundle install ãè¡ããŸãã group :development, :test do gem 'rspec-rails', '~> 5.0.0' end $ bunlde install ãã¹ãããŒã¿ããŒã¹ã®èšå® ãã¹ããå®è¡ããããã«ãäžæçã«ããŒã¿ãä¿åããããã®ããŒã¿ããŒã¹ãå¿
èŠã§ããããã¯ãç¹ã«èšå®ãããªããšã Rails ã¢ããªã±ãŒã·ã§ã³ãäœæããéã«ãèªåçã«èšå®ãããŠããå Žåãå€ãã§ãã config/database.yml ã«ããŒã¿ããŒã¹ã®èšå®ãèšèŒãããŠããŸãã PostgreSQL/MariaDB ãããŒã¿ããŒã¹ã«äœ¿çšããå Žåã®èšèŒäŸã¯äžèšã§ãã test: ãã¡ã¯ããªãããŠãŒã¶ãŒã¢ãã«ã®ããŒã¿ãäœæãããŠããŸãã ãã¹ãããŒã¿ã®ãã©ã³ã¶ã¯ã·ã§ã³ Rspec ã§ã®ãã¹ãããŒã¿ã®åãæ±ãã«ã€ããŠå°ã説æããŸãã RSpec ã¯ãã¹ãã±ãŒã¹ããšã«ãã©ã³ã¶ã¯ã·ã§ã³ã匵ãããŠããŸãã1ã€ã®ãã¹ããçµãã床ã«ããŒã«ããã¯ããã®ã§ããã¹ãã®ç¬ç«æ§ãä¿ãããŠããŸãã ããè€éãªãã©ã³ã¶ã¯ã·ã§ã³ãå¶åŸ¡ãããå Žåã¯ã Database Cleaner Gem ã䜿ããŸãããåºæ¬çã«ã¯æšæºã®ãŸãŸã§äºè¶³ããŸãã ãã詳ãã RSpec ã®æ§æã«ã€ã㊠ä»åã¯æéãéãããŠããããã RSpec ã®è©³ããæžãæ¹ã«ã€ããŠã¯è§ŠããŸããã§ããã RSpec ã§ã®ãã¹ãã®æžãæ¹ã«ã€ããŠã¯ãã詳ããæžç±ãããããããã¡ãã玹ä»ããŸãã Everyday Rails - RSpecã«ããRailsãã¹ãå
¥é Aaron Sumner, Junichi Ito (äŒè€æ·³äž), AKIMOTO Toshiharu, 鿝æ±https://leanpub.com/everydayrailsrspec-jp Gitã«ããã·ã¥ããããã¹ããèªåã§å®è¡ããæ¹æ³ CI CI ã®å°å
¥ èªåã§ãã¹ããå®è¡ãããŠããªãå Žåãæ¯åãããŒã«ã«ç°å¢ã§ãã¹ããå®è¡ããã®ã¯é¢åã§ãããã¬ãã¥ãŒæã«ãã¹ãããã¹ããŠããå質ã®ãã®ãæ¥ãŠããã®ãäžå®ã«æããŸãã ããã§ã Continuous IntegrationïŒç¶ç¶çã€ã³ãã°ã¬ãŒã·ã§ã³ïŒãå°å
¥ããŸãããã CI ãšã¯ Continuous IntegrationïŒç¶ç¶çã€ã³ãã°ã¬ãŒã·ã§ã³ïŒ ã¯ãGit çãªããžããªãžã®ã³ãŒãã®å€æŽã奿©ã«ãèªååããããã«ãããã¹ããå®è¡ãããç°å¢ãšãã®éçºææ³ã®ããšãèšããŸãã CI ãå®çŸãããã®ã«ã¯ã GitHub Actions, Jenkins, CircleCI ãªã©ããããŸãã ãããã CI ãå°å
¥ãããªã GitHub Actions ã®ã»ããæè»œã§ãã GitHub Actions ã§ã® CI æ§ç¯ ããã§ã¯ãä»å㯠GitHub Actions ã§ CI ãçµãã§ã¿ãŸãã ãŸãã CI ã§äœã宿œãããããèããŸãã ãã¹ãã«å¿
èŠãªããŒã«ã®æºå ruby ã®ã€ã³ã¹ããŒã« DB ã®æºå lint (éçè§£æããŒã«) ã®å®è¡ ãã¹ãã®å®è¡ ããã§ã¯ãæ§ç¯ããŠãããŸãã ãŸãã CI ãæ§ç¯ããã GitHub ãªããžããªã® Actions ã¿ããéžæããŸãã æ¬¡ã« New workflow ãã¿ã³ãæŒããŸãã ãããšããã³ãã¬ãŒãã®äžèЧã衚瀺ãããŸããä»åã¯ã Ruby ã® Set up this workflow ãã¿ã³ãéžæããŸãããã workflow ã®ç·šéç»é¢ã«é·ç§»ããŸãã workflow ã®ååã倿ŽããŠãããŸããããã§ã¯ãå®éã«ç·šéããŠãããŸãã ä»åã¯ãäžèšã®ãã㪠YAML ãäœæããŸããã name: Ruby on Rails CI on: push: branches-ignore: - stable jobs: rails-test: runs-on: ubuntu-latest strategy: matrix: ruby-version: - 2.7.4 env: RAILS_ENV: test PROJECT_DATABASE_HOST: 127.0.0.1 PROJECT_DATABASE_PORT: 3306 PROJECT_DATABASE_DBNAME: db_test PROJECT_DATABASE_USERNAME: user PROJECT_DATABASE_PASSWORD: password TZ: Asia/Tokyo services: mariadb: image: mariadb:10.6.0 env: MYSQL_DATABASE: ${{ env.PROJECT_DATABASE_DBNAME }} MYSQL_USER: ${{ env.PROJECT_DATABASE_USERNAME }} MYSQL_PASSWORD: ${{ env.PROJECT_DATABASE_PASSWORD }} MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' TZ: Asia/Tokyo options: >- --health-cmd "mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 3306:3306 steps: - name: Checkout uses: actions/checkout@v2 # Resolve gems dependencies - name: Set up Ruby ${{ matrix.ruby-version }} uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true # Pre-test setting - name: Setup database run: bundle exec rake db:create db:schema:load # Executing lint - name: Run linters run: bundle exec rubocop # Executing test - name: Run tests run: bundle exec rspec slack-notify: runs-on: ubuntu-latest needs: - rails-test if: always() steps: # run this action to get workflow conclusion # You can get conclusion via env (env.WORKFLOW_CONCLUSION) - name: Receive job statuses uses: technote-space/workflow-conclusion-action@v2 - name: Slack Notification uses: weseek/ghaction-slack-notification@master with: type: ${{ env.WORKFLOW_CONCLUSION }} job_name: '*Test*' channel: '#channel_name' isCompactMode: true url: ${{ secrets.SLACK_WEBHOOK_URL }} åãããã¯ã説æããŠãããŸãã name ã«ã¯ãä»»æã® workflow ã®ååãèšå®ããŸãã on ã¯ããã® workflow ãã©ã®ãããªå¥æ©ã§å®è¡ããã®ãæå®ãããã®ã§ãã push ãæå®ãããªããžããªã«ã³ãŒããããã·ã¥ããã床㫠workflow ãå®è¡ããããã«ããŸãã branches-ignore: stable ãšããããšã§ã stable ãã©ã³ããžã®ããã·ã¥ã®å Žå㯠workflow ãå®è¡ãããªãããã«ããŠããŸãã job ã« rails-test ãšããååãä»ãããã®äžã§ã€ã¡ãŒãžã®æå®ããç°å¢å€æ°ãå®è¡ããã¹ããããå®çŸ©ããŠãããŸãã env ã¯ãã®åŸã«èª¬æãã service ã§äœ¿çšããããã®ç°å¢å€æ°ãå®çŸ©ããŠãããŸãã services ã«ã¯ãã¹ãã®å®è¡ã«å¿
èŠãªã³ã³ãããå®çŸ©ããŸãã ä»åã¯ãã¹ãçšã®ããŒã¿ããŒã¹ã§ãã mariadb ãå®çŸ©ããŸãã steps ã«ã¯ãåè¿°ã®ãCI ã§äœã宿œãããããã§èããå
容ãèšå®ããŠãããŸãã uses ã§ãå®çŸ©æžã¿ã® action ãå©çšã§ããŸãã uses: actions/checkout@v2 ã¯ãœãŒã¹ã³ãŒãããã§ãã¯ã¢ãŠãããããã«äœ¿çšããŠããŸãã uses: ruby/setup-ruby@v1 ã§ ruby ã®ã»ããã¢ãããããŠããŸãã æºåãã§ãããã Setup database, Run linters, Run tests ã®3ã€ã® name ã®ã¹ããããå®çŸ©ãããããããããŒã¿ããŒã¹ã®æºåããéçè§£æã®å®è¡ãããã¹ãã®å®è¡ããè¡ããŸãã run ã§ã¯ãä»»æã®ã³ãã³ããå®è¡ã§ããŸãã run: bundle exec rake db:create db:schema:load ãšæžãããšã§ããã¹ãçšã® DB ãåæåããŠããŸãã slack-notify ã¯ã CI å®äºæã« slack ãžéç¥ããããã®èšå®ãè¡ã£ãŠããŸãã workflow ã®ç·šéãçµãã£ããã Start commit ã§ã³ããããäœæããä¿åããŸããä»å㯠GitHub ã® workflow ç·šéããŒã«ã§ç·šéãè¡ããŸãããã ä»»æã® Git ã¯ã©ã€ã¢ã³ãã§ /.github/workflows/*.yml ãã³ããã&ããã·ã¥ã㊠wofkflow ãäœæããŠã倧äžå€«ã§ãã ãªããžããªã«ããã·ã¥ãè¡ããšèªåçã« workflow ãå®è¡ãããŸãã Actions ã® workflow äžèЧã«ã¯ãå®è¡çµæã衚瀺ãããŸãã äžèЧã®äœããã®çµæãéžæãããšããã®ããã«ãªã£ãŠããŸãã rails-test ãå®è¡ãããæåŸã« slack-notify ãå®è¡ããŠããããšãããããŸãã GitHub Actions 㯠PullRequest ãšã飿ºããŠããã䟿å©ã§ãã該åœãã PullRequest ã« All checks have passed ã®ããã« workflow ã®å®è¡çµæãåæ ãããŸãã æ«æããªããã¹ãã®å¿åŸ éçºã«ãŒã« ãã¹ãããŒã«ã®å°å
¥ãCIã®æ§ç¯ã§ããã¹ããå®è¡ã§ããç°å¢ã¯æºåã§ããŸãããããããã¯æ°æ¥œã«ãã¹ããæžããŠããéçºææ³ã玹ä»ããŠãããããšæããŸãã äœããæžãã°ããã®ã å®éã«ãã¹ããæžããŠããããšããæããäœããæžããŠããã°ããã®ããæ©ã¿ãŸãããã£ãšãã¹ãã®çš®é¡ãæãããšãäžèšã®ãã®ããããŸãã Model specs Controller specs Request specs Feature specs View specs Helper specs Mailer specs Routing specs Job specs System specs ãããã®ãã¹ããæåãããã¹ãŠæŽåããããšãããšãã¢ãããŒã·ã§ã³ãç¶æããã®ãé£ãããã§ããããã§ãæåã¯äžèšã«çµã£ãŠãå°ããå§ããŸãããã Model specs Controller specs Request specs Feature/System specs ãªã©ã¯æåã¯æžãããæåã§ã®ãã¹ãã䜵çšããŸãããã ããèã TDDã§éçºããã»ããããã®ã ãããã TDD ãšã¯äœã§ããããã TDD ã¯ãäžèšã®ãµã€ã¯ã«ãåããŠè¡ãéçºææ³ã§ãã ã¯ããã«ãã¹ãã³ãŒããæžã ãã¹ãããã¹ããã³ãŒããæžã 眪ãç¯ããæžãæ¹ãããŠããã ãªãã¡ã¯ã¿ãªã³ã° ãŸãæåã«ãã¹ãã³ãŒããæžããŸã(ãã¹ããã¡ãŒã¹ã)ã 2çªç®ã®ããã¹ãããã¹ããã³ãŒããæžããã§ã¯ã眪ãç¯ããæžãæ¹ãããŠãããããšããã®ã§ãåãã°ããã³ãŒããæžããŠãåé¡ãããŸããããã®åŸã®ãªãã¡ã¯ã¿ãªã³ã°ã§ãããã«ããŠãããŸãã ããŠã TDD (ãã¹ããã¡ãŒã¹ã) ã§æžããã»ãããããã§ããããã¹ãããŒã«ã®å°å
¥æã¯æ°æ¥œããç¶æããããã«æžããªããŠããããšèããŠããŸããå
ã«æ©èœãå®è£
ããåã PR ãããã¯å°ãé
ããŠãã¹ããæžãããã«ããŸãããããã ãããã¹ããåŸåãã«ãã代ããã«çœªãç¯ããæžãæ¹ã¯æ¥µåããªãããã«ããŸãããã ã©ã®ç²åºŠãŸã§æžãã°ããã®ã ãã§ã«ãããžã§ã¯ããèµ°ã£ãŠãããã®ã«åŸãããã¹ããå°å
¥ããéãã©ããŸã§ãã¹ããæžãã°ããã®ãæ©ã¿ãŸããããã¹ãé§åéçºãã®æžç±ããåŒçšãããšããäžå®ãéå±ã«å€ãããŸã§æžãããšãããŸãã åŸãããã¹ããå°å
¥ããå Žåã¯ãäžå®ã®ããç®æãããã¹ããæžãå§ããããæ°ããå®è£
éšåãããã¹ããæžãå§ããŸãããããã以å€ã®éšåã¯å¯Ÿå¿ã®å¿
èŠããªããªãã°ãã®ãŸãŸã«ããŠãããŸãã ãã¹ãé§åéçº Kent Beck èïŒåç° å人ãèš³https://shop.ohmsha.co.jp/shopdetail/000000004967/ 仿§ãšå®è£
ã®æŽåæ§ã楜ã«ãã ã¹ããŒããã¡ãŒã¹ã ãããã«ãç°¡åã«ã¹ããŒããã¡ãŒã¹ãã«ã€ããŠç޹ä»ããããšæããŸãã Rails ã¢ããªã±ãŒã·ã§ã³ã§ã API ãçµãããšããããšæããŸãããããã®ãã¹ããæžãããšããæã«äŸ¿å©ãªãã®ã§ãã OpenAPI å
ã«ã OpenAPI ã«ã€ããŠèª¬æããŸãã OpenAPI 㯠Rails ãªã© API ãå®è£
ããéã®ä»æ§ããŸãšããããã¥ã¡ã³ãã§ãã committee + OpenAPI OpenAPI ã§ä»æ§ãå®çŸ©ããŠãããšãããã䜿ã£ãŠå®è£
ã仿§ã©ããã«è¡ãããŠããããã¹ãããããªã£ãŠããŸãã ãã¹ããè¡ãããã«ã¯ã committee ãå©çšããŸãã committee ã¯ã assert_response_schema_confirm ã¡ãœãããæäŸãããããå©çšããŠã OpenAPI ããã¥ã¡ã³ãã«åŸã£ãŠãã¬ã¹ãã³ã¹ã® JSON ã¹ããŒããšäžèŽããããã§ãã¯ã§ããããã«ãªããŸãã Gemfile ã«äžèšã远å ãã bundle install ããŸãã group :development do gem 'committee-rails' end $ bundle install äžèšã¯ committee ãå©çšãããã¹ãã³ãŒãã®äŸã§ãã assert_response_schema_confirm(200) ã§ãOpenAPI ãšå®è£
ãåèŽããŠããããã¹ãããŠããŸãã describe 'request spec' do include Committee::Rails::Test::Methods def committee_options @committee_options ||= { schema_path: Rails.root.join('schema', 'schema.json').to_s, query_hash_key: 'rack.request.query_hash', parse_response_by_content_type: false, } end describe 'GET /' do it 'conform json schema' do get '/' assert_response_schema_confirm(200) end end end ãŸãšã åé ã§æ°æ¥œã«æžãããšèšããŸãããããã¹ãå°å
¥æã¯æºåããããšãå€ã倧å€ã§ãã æ«æããªãããã«ãæåããé 匵ããããªãããã«ããŸãã ãã¹ããã¡ãŒã¹ãã§æžããªãã§ãã£ãŠã¿ã æžããåã®ãã¹ããæžããŠãå°ããå§ãã æåãã¹ããšããŸã䜵çšãã äžèšãã§ãããã æåãã¹ããèªååãž TDD ã¹ããŒããã¡ãŒã¹ã çãæ®µéçã«ãã¹ãããŒã«ãå°å
¥ããŠãããŸãããã ãã¹ããæ¬åœã«æ¥œã«ãªã£ãŠããã®ãšããã®æ©æµãåããããããã«ãªãã®ã¯ããããã§ãã èè
ãããã£ãŒã« ç°æ 貎幞 æ ªåŒäŒç€ŸWESEEK / ã·ã¹ãã ãšã³ãžã㢠Webã·ã¹ãã éçºäŒç€Ÿã2瀟çµéšåŸã2019幎8æã«WESEEKã«å
¥ç€Ÿã çŸåšã¯ã倧æIXã®æ¥åèªååã·ã¹ãã ã®æ©èœéçºãã€ã³ãã©ã®èšèšã»æ§ç¯ã«æºããã è¶£å³ã¯ãé»åå·¥äœãšé£ãã æ ªåŒäŒç€ŸWESEEKã«ã€ããŠ æ ªåŒäŒç€ŸWESEEKã¯ãã·ã¹ãã éçºã®ãããã§ãã·ã§ãã«éå£ã§ãã ãçŸåšã®äž»ãªäºæ¥ã é信倧æäŒæ¥ã®æ¥åãããŒèªååãããžã§ã¯ã ãœãŒã·ã£ã«ã²ãŒã ã®åèšéçº èªç€ŸçºãªãŒãã³ãœãŒã¹ãããã¯ããGROWIããGROWI.cloudãã®éçº GROWI GROWIã¯ãMarkdownèšæ³ã§ããŒãžãèšè¿°ã§ãããªãŒãã³ãœãŒã¹ã®Wikiã·ã¹ãã ã§ãã GROWI.cloud GROWI.cloudã¯OSSã®GROWIãå°éçç¥èããªããŠãç°¡åã«éçšã»ç®¡çã§ãããæ³äººã»å人åãã®åçšãµãŒãã¹ã§ãã 倧æSIerã»ISPãäžå°äŒæ¥ã倧åŠã®ç 究宀ãªã©æ§ã
ãªå Žæã§ãå©çšããã ããŠãããŸãã ãäž»ãªç¹åŸŽã ããã¹ããå³è¡šãã©ãã©ãæžããã匷åãªç·šéæ©èœ ããŒã æ¡å€§ã«è¿
éã«å¯Ÿå¿ã§ãã管çè
åãæ©èœãæäŸ å
å®ããæ©èœã»ãµããŒãã§ãšã³ã¿ãŒãã©ã€ãºã«ãå¯Ÿå¿ ãå°å
¥äºäŸèšäºã ã€ã³ã¿ãŒããããã«ããã£ãŒãæ ªåŒäŒç€Ÿæ§ [https://growi.cloud/interviews/mfeed/?utm_source=connpass-top&utm_medium=web-site&utm_campaign=mf:embed:cite] æ ªåŒäŒç€ŸHIKKYïŒVRæ³äººHIKKYïŒæ§ [https://growi.cloud/interviews/hikky:embed:cite] WESEEK Tech Conference WESEEK Tech Conferenceã¯ãæ ªåŒäŒç€ŸWESEEKãäž»å¬ãããšã³ãžãã¢åãã®å匷äŒã§ããWESEEKã«æå±ãããšã³ãžãã¢ãæ§ã
ãªããŒãã§å®æçã«çºè¡šãè¡ãäºå®ã§ãã æ¬¡åã¯ã10/28(æš) 19:00~20:00ã«éå¬äºå®ã§ãã å®éã« WESEEK ã§äœ¿ã£ãŠãã倿 å
¥åã·ã¹ãã ãtickrecãã®æ§æãå
ã«ãSlackã§å€æ å
¥åãã§ããã¢ããªã±ãŒã·ã§ã³ã1ããæ§ç¯ããããã®æ¹æ³ãã¬ã¯ãã£ãŒããŸãã çŸåšãconnpassãTECH PLAYã§åå åä»äžã§ããçæ§ã®ãåå ããåŸ
ã¡ããŠãããŸãïŒhttps://weseek.connpass.com/event/226591/TECH PLAYã¯ãã¡ããã äžç·ã«åã仲éãåéããŠããŸã æ±äº¬ã®é«ç°éŠ¬å Žãªãã£ã¹ã倧åã«ããå¥åºãµãã©ã€ããªãã£ã¹ã«ãŠãšã³ãžãã¢ãåéããŠãããŸããäžéæ¡çšã ãã§ã¯ãªããã€ã³ã¿ãŒã³ã·ãããç©æ¥µçã«åãå
¥ããŠããŸãïŒ è©³ããåéèŠé
ã¯ãåŒç€ŸHPã®æ¡çšããŒãžããã確èªãã ããã
ãã®æçš¿ã¯ãåŒç€ŸãæäŸããWESEEK TECHéä¿¡ã®äžç°ã§ãã WESEEK TECHéä¿¡ãšã¯ãWESEEKã®ãšã³ãžãã¢ããã£ããã¢ããããæè¡ã«é¢ããæ
å ±ããtechããã°ãéããŠå®æçã«çºä¿¡ããŠãããã®ã§ãã ã¯ããã« ããã«ã¡ã¯ãã·ã¹ãã ãšã³ãžãã¢ã®ãããã§ããæ¬èšäºã§ã¯ãNode.jsã®EventEmitterã«ã€ããŠã話ããŸãããã®èšäºã§åãäžããå
容ã¯ä»¥äžã«ãªããŸãã Event Emitter ãšã¯ äœãã®ã€ãã³ããçºçããæã«ããããããªã¬ãŒãšãªã£ãŠããããããç»é²ããŠãããåŠçãå®è¡ããããšãã§ããŸãã Event Emitter ã®äœæ Event Emitterãäœæããã«ã¯ãNode.jsã®eventsã¢ãžã¥ãŒã«ããEventEmitterã€ã³ã¹ã¿ã³ã¹ãçæããå¿
èŠããããŸãã const EventEmitter = require('events'); const myEmitter = new EventEmitter(); // EventEmitterã€ã³ã¹ã¿ã³ã¹ãçæ ã€ãã³ãã®çºåãšãããã«å¯Ÿå¿ããåŠç Event Emitterã®åºæ¬çãªäœ¿ãæ¹ãšããŠãon()ãšemit()颿°ã«ã€ããŠã話ããŸãã on(eventName , listener ) 第äžåŒæ°ã«æå®ããã€ãã³ãã«çŽã¥ããŠãlistenerãç»é²ããŸãã emit(eventName , [args]) ãã®é¢æ°ã§ã€ãã³ããçºåãã第äºåŒæ°ä»¥éã®argsãåŒæ°ãšããŠæž¡ããŸãã emit()颿°ã§ã€ãã³ããçºåããon()颿°ã§ç»é²ãããŠããåŠçãå®è¡ãããŸãã import { EventEmitter } from 'events'; const eventEmitter = new EventEmitter(); // ã€ãã³ããçºåãããæã®åŠçãèšè¿°ãã eventEmitter.on('myEvent', () => { console.log('Emitted Event'); }); // ã€ãã³ããçºåããã eventEmitter.emit('myEvent'); äžèšã®ã³ãŒããå®è¡ãããæã«ã以äžã®ããã«åºåãããŸãã > Emitted Event â»on()ã䜿ã£ãŠç»é²ãããlistenerã¯ããã®ã€ãã³ããçºç«ããã床ã«å®è¡ãããŸãã åæãéåæã Event Emitterã«ãã£ãŠçºçããã€ãã³ãã¯ãåæçã«å®è¡ãããŸãã import { EventEmitter } from 'events'; const eventEmitter = new EventEmitter(); eventEmitter.on('myEvent', (data) => { console.log(data); }); console.log('A'); eventEmitter.emit('myEvent', 'B'); console.log("C"); äžèšã®ã³ãŒããå®è¡ããå Žåã以äžã®ããã«äžããé çªã«åºåãããŸãã > A > B > C listenerã®å®è¡é åº EventEmitterã«ãã£ãŠçºçããã€ãã³ãã¯ãç»é²ãããå
šãŠã®listenerãé çªã«å®è¡ãããŸã import { EventEmitter } from 'events'; const eventEmitter = new EventEmitter(); eventEmitter.on('myEvent', (data) => { console.log(data, 'FIRST'); }); console.log('A'); eventEmitter.on("myEvent", data => { console.log(data, 'SECOND'); }); // ã€ãã³ãã®çºå eventEmitter.emit('myEvent', 'Emitted Statement:'); console.log("B"); äžèšã®ã³ãŒããå®è¡ãããå Žåã以äžã®ããã«åºåãããŸãã > A > Emitted Statement: FIRST > Emitted Statement: SECOND > B ããç¹å®ã®ã€ãã³ãã«å¯ŸããŠåå¿ãããã«ã¯ãåãEventEmitterã€ã³ã¹ã¿ã³ã¹ããemitãå®è¡ããå¿
èŠããã on()ã§ç»é²ããã€ãã³ããã³ãã©ãããªã¬ãŒããã«ã¯ãåãEventEmitterã€ã³ã¹ã¿ã³ã¹ã®emit()ãåŒã³åºãå¿
èŠããããŸãã 以äžã®ã³ãŒãã§ã¯ãå¥ã®EventEmitterã€ã³ã¹ã¿ã³ã¹ã䜿çšãããŠãããããã³ã³ãœãŒã«ã«ã¯äœãåºåãããŸããã import { EventEmitter } from 'events'; const eventEmitter1 = new EventEmitter(); eventEmitter1.on('myEvent', () => { console.log('myEvent emitted'); }); const eventEmitter2 = new EventEmitter(); eventEmitter2.emit('myEvent'); å®éã®å©çšæã«ã¯è€æ°ã®EventEmitterã€ã³ã¹ã¿ã³ã¹ã䜿ãããšã¯çšã ãšæããŸãããã€ãã³ããäŒæããªãçã®çç¶ãèµ·ãã£ãå Žåã¯ãã®èŸºããçã£ãŠã¿ãŸãããã Event Emitter ã¯ã©ã¹ã®é¢æ° EventEmitterã¯ã©ã¹ã®äžã§ãon() emit()以å€ã®é¢æ°ãåãäžããŸãã once(eventName, listener) listenerãäžåºŠã ãå®è¡ãããå Žåã«äœ¿çšãããŸãã ãã®ã¡ãœããã䜿çšãããšãã€ãã³ãããªãã¹ã³ããåŸã«ãlistenerãç Žæ£ãããŸãã addListener(eventName, listener) ããã¯on()ã®ãšã€ãªã¢ã¹ã§ãã removeListener(eventName, listener) ããã¯ãlistenerãåé€ããããã«äœ¿çšãããŸãã removeAllListeners([eventName]) ããã¯ã eventName ã«ç»é²ãããŠããå
šãŠã® listener ãåãé€ããŸãã Event Emitter ã®äœ¿ãæ åŒç€Ÿã§éçºäžã®ãã¬ããžå
±æãµãŒãã¹ãGROWIããäŸã«åºããŠèª¬æããŸãã GROWIã§ã¯ãããŒãžã«ã³ã¡ã³ãã远å ãããæã«DBã«ã³ã¡ã³ãã®ããã¥ã¡ã³ããä¿åãããŸãã DBã«ããŒã¿ãä¿åãããã¿ã€ãã³ã°ã§ç¹å®ã®åŠçããããæã«ã¯ãModelã¹ããŒããæäŸããpost save hookãå©çšããŠå®è¡ããããšãã§ããŸãã ãã ããæ³šæç¹ãšããŠãã¢ãã«ãããµãŒãã¹å±€ãªã©ã§å®çŸ©ããã颿°ãçŽæ¥åŒã³åºãã®ã¯ã¢ãŒããã¯ãã£ã®èšèšçã«ãããããŸããã ããã§ãpost save hookã«ãŠEventEmitterã§ã€ãã³ããçºç«ãããããµãŒãã¹å±€ã§æ€ç¥ããŠç»é²ããŠããåŠçãå®è¡ãããããšãã§ããŸãã React EventEmitter Reactã§ç¶æ
管çã«EventEmitterã䜿çšãã 以äžã®èšäºãåèã«ãªããŸãã®ã§ããããã°ãã§ãã¯ããŠã¿ãŠãã ããã Event Emitter instead of lifting state up in React ãŸãšã EventEmitter㯠äœãããã®ã€ãã³ããããªã¬ãŒãšããŠãåŠçãå®è¡ãã ã€ãã³ãã«ç»é²ãããŠããlistenerãåæçã«åŒã³åºã ã€ãã³ãã«ç»é²ãããŠããlistenerãç»é²ãããé çªã«åŒã³åºã ãããã§ããã§ããããã æåŸãŸã§èªãã§ãã ããããããšãããããŸããïŒ åèã«ãããŠããã ããèšäº Events | Node.js v16.10.0 documentation Node Event Emitters â For Beginners and Experts Node.jsã®EventEmitterã§ã€ãã³ãé§åããã°ã©ãã³ã°ããã
ãã®æçš¿ã¯ãåŒç€ŸãæäŸããWESEEK TECHéä¿¡ã®äžç°ã§ãã WESEEK TECHéä¿¡ãšã¯ãWESEEKã®ãšã³ãžãã¢ããã£ããã¢ããããæè¡ã«é¢ããæ
å ±ããtechããã°ãéããŠå®æçã«çºä¿¡ããŠãããã®ã§ãã ã¯ããã« ä»åã®èšäºã§ã¯ Rails ã®æš©é管ç gem ã§ãã CanCanCan ã«ã€ããŠãå°å
¥æé ãšæ©èœã®è§£èª¬ãè¡ããŸããå°å
¥æé ãé£ã°ããŠæ©èœã®è§£èª¬ãèªã¿ããæ¹ã¯ãã¡ããæŒããŠãã ããã CanCanCan ãšã¯ CanCanCan ãšã¯ãRuby on Rails ã®æš©é管ç gem ã§ãããç¹å®ã®ãŠãŒã¶ãŒãã¢ã¯ã»ã¹ã§ãããªãœãŒã¹ãå¶éããŸãã ã©ãããããšããšèšããšãäŸãã° admin, manager, read_only ãšãã3ã€ã®æš©éããã£ããšããŠãããããã¯ä»¥äžã®ããšãã§ããããã«èšå®ãè¡ãããšãã§ããŸãã admin ãšããæš©éãæã£ãŠãããŠãŒã¶ãŒã¯ç®¡çè
çšã®ç»é¢å«ãå
šãŠã®ç»é¢ãé²èЧå¯èœã§ãããŒãžäžã§å
šãŠã®ã¢ãã«ã®æäœãå¯èœ manager ãšããæš©éãæã€ãŠãŒã¶ãŒã¯ç®¡çè
çšã®ç»é¢ãé²èЧããããšãã§ããªããããã以å€ã®ããŒãžã§ã¢ãã«ã®æäœãå¯èœ read_only ãšããæš©éãæã€ãŠãŒã¶ãŒã¯ã¢ãã«ã®æäœãã§ããé²èЧã®ã¿å¯èœ CanCanCan ãçšããããšã§ãããªæãã®æ©èœãç°¡åã«å®è£
ããããšãã§ããŸãã CanCanCan ã䜿ãããã®æºå CanCanCan ã¯ãã® gem åäœã§çšããããšã¯ã»ãšãã©ãªããå€ãã®å Žå㯠devise ãšãããã°ã€ã³æ©èœãå®è£
ãã gem ãšã»ããã§äœ¿ããŸãã devise ã«ãã远å ããã User ã¢ãã«ã«ä»åæ°ãã远å ãã Role ã¢ãã«ãçŽã¥ããŠãããã CanCanCan ã§è¿œå ãã Ability ã¯ã©ã¹ã§ç®¡çããŠäœ¿ããŸãã ãã®èšäºã§ã¯ãdevise ã«ãã£ãŠãã§ã« User ã¢ãã«ãååšãããšããåæã§è§£èª¬ãããŸãã devise ã«é¢ããŠã¯ä»åºŠæ©äŒãããã°è§£èª¬ã®èšäºãæžãããšæããŸãã ã€ã³ã¹ããŒã« ã§ã¯ãŸããCanCanCan ã®ã€ã³ã¹ããŒã«ãè¡ããŸãã Gemfile ã«ä»¥äžã®èšè¿°ã远å ããbundle install ãå®è¡ããŸãã gem 'cancancan' $ bundle install Gemfile.lock ã確èªããCanCanCan ãã€ã³ã¹ããŒã«ãããŠããããšã確èªããŸãã cancancan (3.3.0) Role ã¢ãã«ã®äœæ ã€ã³ã¹ããŒã«ãå®äºãããããŠãŒã¶ãŒã«ä»äžããæš©éã管çãã Role ãäœæããŠããã®ã§ãããä»åã¯1ã€ã®ãŠãŒã¶ãŒã«è€æ°ã®æš©éãä»äžã§ããããã«ããããšæããŸãã ãªã®ã§ Role ã¢ãã«ãšãUser - Role éã®å€å¯Ÿå€ãå®çŸããããã®äžéããŒãã«ãšããŠæ©èœãã UserRole ã¢ãã«ã®2ã€ãäœæããŸãã $ rails generate migration CreateRoles invoke active_record create db/migrate/20210904000000_create_roles.rb ãã€ã°ã¬ãŒã·ã§ã³ãã¡ã€ã«ãäœæãããã®ã§ããã®ãã¡ã€ã«ã«ä»¥äžã®èšè¿°ã远å ããŸãã ã«ã©ã ã¯æš©éåã管çãã name ãå¿
é ã«ã©ã ãšããŠå®çŸ©ããŸãã class CreateRoles 0.0144s == 20210904000000 CreateRoles: migrated (0.0146s) ============================= == 20210904000000 CreateUserRoles: migrating ================================== -- create_table(:user_roles) -> 0.0362s == 20210904000000 CreateUserRoles: migrated (0.0363s) ========================= ãããããã¢ãã«ãã¡ã€ã«ã®äœæãè¡ããŸãã app/models é
äžã« role.rb ãçšæããŠä»¥äžã®èšè¿°ã远å ããŸãã class Role Role.create(name: 'admin') # { :id => 1, :name => "admin", :created_at => Sat, 04 Sep 2021 09:26:22 UTC +00:00, :updated_at => Sat, 04 Sep 2021 09:26:22 UTC +00:00 } ç¶ã㊠manager, read_only ãäœæããŸãã [2] pry(main)> Role.create(name: 'manager') [3] pry(main)> Role.create(name: 'read_only') ããã§3ã€ã® role ãäœæãããŸããã æš©éããšã«ã«ãŒã«ãèšå®ãã admin ã§ããã°å
šãŠã®ç»é¢ãé²èЧå¯èœãread_only ã§ããã° é²èЧã®ã¿å¯èœãšããããã«ãããããã® Role ã«å¯ŸããŠèšå®ãè¡ãªã£ãŠãããŸãã å
ã»ã©ã® rails generate cancan:ability ã§ app/models é
äžã« ablity.rb ãšãããã¡ã€ã«ãçæãããŠããã¯ããªã®ã§ããã®ãã¡ã€ã«ãéããŸãã class Ability include CanCan::Ability def initialize(user) # Define abilities for the passed in user here. For example: # # user ||= User.new # guest user (not logged in) # if user.admin? # can :manage, :all # else # can :read, :all # end # # The first argument to `can` is the action you are giving the user # permission to do. # If you pass :manage it will apply to every action. Other common actions # here are :read, :create, :update and :destroy. # # The second argument is the resource the user can perform the action on. # If you pass :all it will apply to every resource. Otherwise pass a Ruby # class of the resource. # # The third argument is an optional hash of conditions to further filter the # objects. # For example, here the user can only update published articles. # # can :update, Article, :published => true # # See the wiki for details: # https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities end end ããäœããæ²¢å±±ã®ã³ã¡ã³ããæžãããŠããããã§ãã ãã®ã³ã¡ã³ãã«èšå®æ¹æ³ãèšãããŠãããæèš³ãããšãã®ããã«ãªããŸãã äŸãã°ãã®ããã«ã㊠ability ãå®çŸ©ããŸãã user ||= User.new if user.admin? can :manage, :all else can :read, :all end can ã®ç¬¬äžåŒæ°ã«ã¯ :read, :create, :update, :destroy ãå®çŸ©ããŸãã manage ãæž¡ããšãããã®4ã€å
šãŠãæå¹ã«ãªããŸãã can ã®ç¬¬äºåŒæ°ã«ã¯ã第äžåŒæ°ã§èšå®ããã¢ã¯ã·ã§ã³ãå®è¡ã§ããã¢ãã«ãèšå®ããŸãã :all ãæž¡ããšå
šãŠã®ã¢ãã«ã察象ã«ãªããŸãã can ã®ç¬¬äžåŒæ°ã§ã¯ããã«è¿œå ã®èšå®ãè¡ãããšãã§ããŸãã 詳ãã㯠wiki ãã芧ãã ããã https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities åºæ¬çã«ã¯ç¬¬äžåŒæ°ã«ã¢ã¯ã·ã§ã³ãå®çŸ©ãã第äºåŒæ°ã§ãã®ã¢ã¯ã·ã§ã³ãå®è¡ã§ããã¢ãã«ãå®çŸ©ããŸãã can :read, :all can :update, Book äŸãã°ãã®ãããªèšè¿°ã ãšãåã¢ãã«ã®äžèЧç»é¢ãšè©³çްç»é¢ãé²èЧã§ããBook ã¢ãã«ã ãã¯ç·šéç»é¢ã®é²èЧãšã¢ãã«ã®æŽæ°(update)ã®æäœãè¡ããããã«ãªããŸãã ãã®èª¬æãå
ã«åæš©éã«å¯ŸããŠèšå®ãè¡ããšã以äžã®ããã«ãªããŸãã class Ability include CanCan::Ability def initialize(user) user ||= User.new # role ã admin ã®ãŠãŒã¶ãŒã¯ã¢ãã«ã®æäœãè¡ãããšãã§ããŠç®¡çè
ç»é¢ãé²èЧå¯èœ if user.has_role?(:admin) can :manage, :all can :access_admin_page, :all # role ã manager ã®ãŠãŒã¶ãŒã¯ã¢ãã«ã®æäœãè¡ãããšãã§ãããã管çè
ç»é¢ã¯é²èЧäžå¯èœ elsif user.has_role?(:manager) can :manage, :all cannot :access_admin_page, :all # role ã read_only ã®ãŠãŒã¶ãŒã¯ã¢ãã«ã®æäœãè¡ãããšãã§ããé²èЧã®ã¿å¯èœã管çè
ç»é¢ã¯é²èЧäžå¯èœ elsif user.has_role?(:read_only) can :read, :all cannot :access_admin_page, :all # role ãæã£ãŠããªããŠãŒã¶ãŒã¯å
šãŠã®ç»é¢ãé²èЧäžå¯èœ else cannot :read, :all end end end cannot 㯠can ã®å察ã§ã¢ã¯ã·ã§ã³ãå®è¡ã§ããªãããŸãã äžéšã®ç»é¢ã ãã¢ã¯ã·ã§ã³ãå¶éããããå Žåã«ãcan :manage, :all ãªã©ã®èšè¿°ãšäœµçšããããšã§æ¥œã«èšå®ãè¡ããŸãã authorize_resource ã§ã¢ããªãã£ããã§ãã¯ãã ability.rb ãã¡ã€ã«ã«æš©éããšã«èšå®ãè¡ããŸããããããã ãã§ã¯èšå®ãè¡ãªã£ãã ãã§å®éã«ã¢ããªãã£ã®ãã§ãã¯ãè¡ãããŸããã ãã§ãã¯ãè¡ãã«ã¯ã³ã³ãããŒã©ãŒãã¡ã€ã«ã«èšå®ãèšè¿°ããå¿
èŠããããŸãã 以äžã®äŸã§ã¯ããŠãŒã¶ãŒã edit ã¡ãœãããå®è¡ããæã« :update ã®ã¢ããªãã£ãæã£ãŠããããã§ãã¯ããæã£ãŠããã° edit ç»é¢ã衚瀺ããããã«ããŠããŸãã class BooksController ãšã©ãŒç»é¢ã«ãªãããroot ãã¹ã«ãªãã€ã¬ã¯ããããŠã¡ãã»ãŒãžã衚瀺ãããããšã確èªã§ããŸããã ãã®ä»è©³ãã解説ãšäŸ¿å©ãªæ©èœ ã¢ããªãã£ã®ãšã€ãªã¢ã¹ CanCanCan ã«ã¯ :read, :create, :update, :destroy (ãš :manage) ã®ã¢ããªãã£ãçšæãããŠããŸãããããã«å ã㊠Rails ã® 7 ã€ã® RESTful ã¢ã¯ã·ã§ã³ãšåããšã€ãªã¢ã¹ãèªåçã«èšå®ãããŠããŸãã read: [:index, :show] create: [:new, :create] update: [:edit, :update] destroy: [:destroy] ãã®ãšã€ãªã¢ã¹ãååšãããããã§ authorize_resource äœ¿çšæã«ã¢ã¯ã·ã§ã³ã«å¯Ÿå¿ããã¢ããªãã£ããã§ãã¯ã§ããŸãã ãªãœãŒã¹ã®èªåèªã¿èŸŒã¿ã§ Controller ãã¡ã€ã«ãçç¥ãã CanCanCan ã«ã¯ãªãœãŒã¹ãèªåã§èªã¿èŸŒã䟿å©ãªæ©èœãåãã£ãŠããŸãã ãã®æ©èœãçšããå ŽåãäŸãã° index ã¡ãœããã ãš def index @books = Book.all end show ã¡ãœããã§ããã° def show @book = Book.find(params[:id]) end ãªã©ãšãã£ããªãœãŒã¹ãèªã¿èŸŒãéã«çšããããäžè¬çãªåŠçããController ãã¡ã€ã«ã« load_resource ãèšè¿°ããããšã«ãã£ãŠèªåã§è¡ã£ãŠãããããã«ãªããŸãã ãªãœãŒã¹ã®èªåèªã¿èŸŒã¿ãæå¹ã«ããéãã¢ãã«åã«æºããåŠçãå®è¡ãããŸãã äŸãã° Book ã¢ãã«ã®å Žåã@books ã @book ã®ã€ã³ã¹ã¿ã³ã¹å€æ°ã«ãªãœãŒã¹ãæ ŒçŽãããŸãã ããã«ãã :index, :show, :new, :edit ã®4ã€ã®ã¡ãœãããå®å
šã«çç¥ããããšãã§ããããããã ã® CRUD æ©èœãæã£ãã¢ãã«ã® Controller ãã¡ã€ã«ã¯ä»¥äžã®ããã«æžãæããããŸãã class BooksController
ãã®èšäºã¯ã2021/9/16 ã«è¡ããã WESEEK Tech Conference ã®å
容ã®ãã¡ãåŸç·šã®ãOpenID Connect åºç€ã§è€æ°ã®ããã¯ãšã³ãã®èªèšŒãèªå¯ãçµ±äžãã話ãã®éšåããŸãšããèšäºãšãªããŸãã ç®æ¬¡ å®çŸããããš ãŸããæåã«æ¬èšäºã§å®çŸããããšãã玹ä»ããŸãã èªèšŒèªå¯éšåã®å®è£
ããµãŒãã¹ã®å€ã«åºããå
±éåãã ã©ã®ãããªãµãŒãã¹ã«å¯ŸããŠããOIDC åºç€ãš nautilus äžã«ãããŠãŒã¶ã»æš©éæ
å ±ãå©çšããŠãèªèšŒèªå¯ã远å ã§ããããã«ãªã£ã å
šäœã®æ§æå³ãšããŠã¯ä»¥äžã®ãããªåœ¢ã«ãªããŸããã ã¢ãããŒã·ã§ã³ JPNAP ããŒã¿ã«ãµã€ã(My.JPNAP) ã¯çºè¶³ãã nautilus ãšããããã¯ãšã³ã 1 ã€ã®ç¶æ
nautilus 以å€ã§æäŸããŠããæ
å ±ã顧客åãã«å
¬éããã(远å èŠä»¶) nautilus = My.JPNAP backend ãã©ãã£ãã¯ã°ã©ã å®éã«é¡§å®¢åãã€ã³ã¿ãŒãã§ãŒã¹ã§æµããŠãããã©ãã£ãã¯é Looking Glass etcâŠ åæ®µã« oauth2-proxy ãå°å
¥ããããšã§ãæ¢åã®ãµãŒãã¹ã«æããããã« SSO èªèšŒåºç€ãå©çšããèªèšŒã¯å¯èœã«ãªã£ã èªå¯ã¯ïŒ nautilus ã§ã¯ã¢ããªå
ã«èªåã§èªå¯ããžãã¯ãå
¥ããŠããªã¯ãšã¹ããæ¥ããšãã«ãã§ãã¯ããããã«ããŠãã ãã以å€ã®ãµãŒãã¹ã®å Žåãoauth2-proxy ã ãã§ã¯é«åºŠãªèªå¯ã¯å®çŸã§ããªã ãã§ãã¯å¯Ÿè±¡ã®ãµãŒãã¹ã«ããããURL ããšã«å¿
èŠãªæš©éã®ãã§ãã¯ãã§ããããã«ããã äžèšã®ãããªè¿œå èŠä»¶ã«å¯ŸããŠãå¥ã®ãµãŒãã¹ã«å¯ŸããŠãåãæ çµã¿ã§èªèšŒ/èªå¯ã®ä»çµã¿ãå
¥ãããïŒãšããèŠæãåºãŠããŸããã ã¢ããªå
ã§èªå¯ããžãã¯ãå
¥ããŠããçŸç¶ããä»ã®ãµãŒãã¹ã«å¯ŸããŠã©ã®ããã«å
±éå/å
±æåãå®çŸããããèããŠãããŸããã æè¡æ€èšã§ãããéãå€ããã æ§æç·šãèªå¯ææ³ç·š ã® 2 éšã«åããŠèª¬æããŸãã æè¡æ€èš(æ§æç·š) åææ¡ä»¶ nautilus ãæš©éæ
å ±ãæã£ãŠãã ã©ã®å¥çŽæ
å ±ã«å¯ŸããŠãã©ãããæäœãèš±å¯ããã®ã æ¢ã«ãããµãŒãã¹ã«æãå
¥ãã圢ã«ã¯ããããªã é¢åèŠãªããã°ãããªãã³ãŒãéãå¢ãããããªã éãèšèª/FW ã䜿ã£ãŠãã¢ããªã ãšãå¢ããã®ã¯ã³ãŒãéã ãã§ã¯ãªã OSS ã ã£ããæ¡åŒµããŠãèªåããŒãžã§ã³ãäœããä¿å®ããå¿
èŠããã⊠皌åç°å¢ã¯ Kubernetes ãå©çšããŠãã äžèšã®ãããªåæã®ããšããµãŒãã¹ã«ãªã¯ãšã¹ããå°éããåã«èªå¯ã§ããããã«ã§ããªãããèããŸããã æ§æã€ã¡ãŒãž æ§æã€ã¡ãŒãž/å°å
¥åå°å
¥åŸã®çæ³å/èªå¯ããžãã¯ã¯ãŸã äžæ 課é¡ãŸãšã ãããŸã§åºãŠããèŠä»¶ãåæããŸãšãããšã解決ãã¹ã課é¡ã¯ä»¥äžã® 5 ç¹ã«ãªããŸããã èªå¯ããžãã¯ã ããã©ããã£ãŠæãåºããŠãµãŒãã¹å段ã§å®è¡ããã®ã å©çšè
ããã®ãªã¯ãšã¹ã㯠HTTP ãªã®ã§ãåæ®µã«ãªããŒã¹ãããã·ãå
¥ããïŒ ãããã·ã«ã©ã®å®è£
ã䜿ãã®ã ãããã·ã®èšå®ãã©ããã£ãŠç®¡ç/åæ ããã®ã èªå¯ããžãã¯ãäœã§å®è£
ããã èªå¯æ
å ±ããµãŒãã¹ãžã©ããã£ãŠæž¡ãã®ã æ§æç·šã§ã¯ 1. ïœ 3. ãæ±ãã4./5. ã«ã€ããŠã¯èªå¯ææ³ç·šã§ç޹ä»ããŸãã ææ³æ€èš ã©ã®ãããªææ³ã§å®çŸããããšããç¹ã«ã€ããŠã¯ãKubernetes ãå©çšããŠããããšããããããµãŒãã¹ã¡ãã·ã¥ããšããåèªãããŒã å
ã§åºãŠããããã以äžã® 2 ææ³ããã©ã¡ããéžæããããšãã話ã«ãªããŸããã ãµãŒãã¹ã¡ãã·ã¥ãå°å
¥ãã ã¡ãã·ã¥ã®ãããã·ã«èªå¯ã¬ã€ã€ãŒãæããã æ¢ã« Kubernetes ãå©çšããŠãããããIstio ãããŒã å
ã§ç¥ãããŠãã ãã ãèªå¯ã®ã¬ã€ã€ãŒã远å ããã ãã§ã¯ too much æïŒ ãµãŒãã¹ã®åã«ãããã·ãèªåã§ç«ãŠã ãããã·å®è£
ã®éžæãèšå®ãªã©ãèªåã§èãã管çã»éçšããå¿
èŠããã èªå¯å¯Ÿè±¡ã®ãµãŒãã¹ããšã«ç°ãªãèšå®ä»¥å€ã管çã»éçšããå¿
èŠããã ã©ãããæ§æã§åããã®ããã©ãããèšå®ã«ããã èããªããã°ãããªãããšãå€ã ããŒã ã§æ€èšããçµæãæè¡çã«é¢çœãããèŠä»¶ãæºããããããšããç¹ãã 1. ãéžæããããšã«ãªããŸããã ãµãŒãã¹ã¡ãã·ã¥/Istio ãšã¯ïŒ ãµãŒãã¹ã¡ãã·ã¥ãšã¯ïŒ ãµãŒãã¹ã¡ãã·ã¥ãå°å
¥ããå Žåã®æ§æã¯ããµãŒãã¹æ¬äœããMicroserviceãããããã·ããSidecarããšãã衚çŸã§è¡šãããŸãã åãµãŒãã¹çã®éä¿¡ãåãµãŒãã¹çºã®éä¿¡ã¯ãã¹ãŠ Sidecar ãšããŠçšŒåãããããã·ãééããæ§æãšãªããŸãã ãã®ãããåãµãŒãã¹ã«å°éããåãåãµãŒãã¹ããçºä¿¡ãããšãã«è¡ãããåŠçãå
±éåããèšå®ãšããŠèšèŒãããµãŒãã¹ã¡ãã·ã¥ã§ç®¡çã§ããŸãã å
±éåããèšå®ãšããŠã¯ã代衚çã«ä»¥äžã®ãããªæ©èœãæããããŸãã ãµãŒãã¹éã®éä¿¡ãééçã«æå·åãã(mTLS) rate-limit ã«ããªã¢ãªãªãŒã¹ ãµãŒããããã¬ã€ãã³ã° Observability/Monitoring Istio ãšã¯ïŒ Kubernetes äžã§ãµãŒãã¹ã¡ãã·ã¥ãå®çŸããããã® 1 å®è£
https://istio.io Sidecar proxy ãšã㊠Envoy ãæ¡çš ä»åèªå¯ãå
±éåããããã«å©çšãã External Authorization Filter(ext_authz) ã Envoy ã®äžæ©èœ ã¡ãã·ã¥å
ã®èšå®ã¯å
šãŠ Kubernetes äžã®ãªãœãŒã¹ãšããŠç®¡çããã ä»ã®ãªãœãŒã¹ãšåæ§ã« YAML ãšããŠç®¡çå¯èœ æ¢ã« My.JPNAP ã§ã¯ Git ãªããžããªã§ãªãœãŒã¹ã管çããŠããããéœåããã 課é¡ãŸãšãã®ãã¡è§£æ¶ããé
ç® èª²é¡ãŸãšãã§æ²åºãã 1. ïœ 3. ã«å¯ŸããŠã以äžã®ãããªçããåºãŸããã èªå¯ããžãã¯ã ããã©ããã£ãŠæãåºããŠãµãŒãã¹å段ã§å®è¡ããã®ã å©çšè
ããã®ãªã¯ãšã¹ã㯠HTTP ãªã®ã§ãåæ®µã«ãªããŒã¹ãããã·ãå
¥ãã Istio ãªãå Pod ã« sidecar ãšã㊠Envoy ãå
¥ã£ãŠããã Envoy ã ã£ãã External Authorization Filter ãå·®ã蟌ãã§å®è¡ã§ããã ãããã·ã«ã©ã®å®è£
ã䜿ãã®ã Istio 㯠Envoy äžæ ãããã·ã®èšå®ãã©ããã£ãŠç®¡ç/åæ ããã®ã Istio ã® Custom Resources ã§ãä»ã® Kubernetes ãªãœãŒã¹ãšãšãã« yaml ã§ç®¡çã§ãã æ€èšçµæã®æ§æã€ã¡ãŒãž æè¡æ€èš(èªå¯ææ³ç·š) ç¶ããŠã課é¡ãŸãšã 4./5. ã§æããé
ç®ã«ã€ããŠèããŠãããŸãã èªå¯ããžãã¯ãäœã§å®è£
ããã èªå¯æ
å ±ããµãŒãã¹ãžã©ããã£ãŠæž¡ãã®ã åææ¡ä»¶ã®ç¢ºèª æ§æã¯æ±ºãŸã£ãããå®éã®èªå¯ããžãã¯ã¯ã©ããã£ãŠå®è£
ããïŒ Istio(Envoy) ã«å¯Ÿè±¡ãšãªããµãŒãã¹ã«ãªã¯ãšã¹ããéãåã«ãå€éšã®ãµãŒãã¹ã«åãåãããŠèš±å¯/æåŠããã¢ãžã¥ãŒã«ã¯ãã£ã(External Authorization Filter) èªå¯ããžãã¯ãå®è£
ããå€éšã®ãµãŒãã¹éšåãã©ããããèããå¿
èŠããã èªå¯ããžãã¯å®è£
ã§ãèããå¿
èŠããã£ãäºæ ã¯ã©ã€ã¢ã³ããéã£ãŠãã token ã®æ€èšŒæ¹æ³ èªå¯ããžãã¯ã®å®è£
æ¹æ³ãå©çšå®è£
ãªã¯ãšã¹ãããŠãã人ã®ãã°ã€ã³æ
å ±ããµãŒãã¹ã«æž¡ãæ¹æ³ èªåå®è£
ã¯å¢ãããããªã token æ€èšŒãã©ããããïŒ ã¯ã©ã€ã¢ã³ããéã£ãŠãã access token ã®æ€èšŒæ¹æ³ ORY Oathkeeper ãå©çšããããšã« æ€èšŒã«ã¯ OAuth2.0 Token Introspection(RFC7662) ã䜿ã Hydra/Oathkeeper ã¯å®è£
ãæã£ãŠãã Oathkeeper ãšã¯ïŒ Hydra ãšåãã ORY 瀟ãéçºããŠãã Identity and Access Proxy https://www.ory.sh/oathkeeper/ Reverse-proxy ã¢ãŒã以å€ã«ããHTTP ãªã¯ãšã¹ããéã/éããªãã®å€å®ãããŠããã API ãåãã£ãŠãã authenticator(èªèšŒ)ãauthorizer(èªå¯)ãªã©ããã§ãŒãºããšã«èšå®ã§ãã URL ããšã«ã©ã®ãããªèªèšŒèªå¯ãè¡ããã JSON or YAML ã§æžããŠãã Envoy ãšã®é£æºããµããŒã Oathkeeper èšå®ãµã³ãã« - id: request-authorization-to-status-api version: v0.38.3-beta.1 match: url: ://status.example.com/api/v1/ methods: - GET - POST - PATCH - PUT - DELETE authenticators: - handler: oauth2_introspection authorizer: handler: allow mutators: - handler: id_token errors: - handler: json å¥éãšã³ããã€ã³ãã®èšå®ã¯å¿
èŠã§ãããäžèšã®èšå®ã ãã§ãç¹å®ã® URL/HTTP method ã§æ¥ããªã¯ãšã¹ãã«ã€ããŠãHTTP ãªã¯ãšã¹ãå
ã«å
¥ã£ãŠãã access token ã®æ€èšŒãå¯èœã§ãã èªå¯ããžãã¯ãã©ããããïŒ èªèšŒããžãã¯ã®å®è£
æ¹æ³ãå©çšå®è£
Oathkeeper ã«ã authorizer ã®èšå®ã¯ããããå€éšãµãŒãã¹ã«æ€èšŒãäŸé Œããã®ã䞻㧠Oathkeeper ããèªäœã§ã¯ããžãã¯ã®å®çŸ©ã¯äžå¯èœ OPA ãå©çšããããšã« OPA ãšã¯ïŒ Policy-based control for cloud native environments https://www.openpolicyagent.org/ rego ãšãã軜éèšèªã«ããžãã¯ãèšèŒããŠãã JSON ãå
¥åå€ãšããŠãrule ãšããåäœã§æ¡ä»¶ã«åœãŠã¯ãŸã/åœãŠã¯ãŸããªããæžããŠãã(Policy as Code) Istio ãšã®é£æºããµããŒã https://github.com/open-policy-agent/opa-envoy-plugin/tree/main/examples/istio OPA èšå®ãµã³ãã« # envoyããæž¡ãããhttp_requestãã®ãŸãŸã§ã¯ã»ããããŒã¿ã®ããŒã®ãæ·±ããããã # ã·ã§ãŒãããŒã ã§ã¢ã¯ã»ã¹åºæ¥ãããã«ããŠãã import input.attributes.request.http as http_request # ã«ãŒã«ã®ããã©ã«ãå€ default allow = false (snip) # GET /api/v4/user_groups/:user_groups_id/user_groups/:id allow { some user_group_id, ug_id http_request.method == "GET" input.parsed_path = ["api", "v4", "user_groups", user_group_id, "user_groups", ug_id] is_token_valid ABILITY_MANAGE_USER_GROUP == nautilus_authz_data(user_group_id, MODEL_UG, ug_id)["abilities"][_] } äžèšäŸã§ã¯ã/api/v4/user_groups/:user_groups_id/user_groups/:id ãšãã URL ã«å¯Ÿãã GET ãªã¯ãšã¹ãã«ã€ããŠãtoken ãæ£ããããšãå¿
èŠãªæš©éãååšããããšã確èªã§ããå Žåããªã¯ãšã¹ããèš±å¯ããã«ãŒã«ã«ãªããŸãã 倿° input ã« HTTP ãªã¯ãšã¹ãã«é¢ããæ
å ±ãå
¥ã£ãŠããã®ã§ããããåºã«ã¡ãœãããURL çã®æ
å ±ããæ¡ä»¶ãæžããŠããããšãå¯èœã§ãã ãã°ã€ã³æ
å ±ã®åãæž¡ããã©ããããïŒ ãªã¯ãšã¹ãããŠãã人ã®ãã°ã€ã³æ
å ±ãOPA/ãµãŒãã¹ã«æž¡ãæ¹æ³ Oathkeeper ã® mutator(倿æ©èœ) ã䜿ããJWT ID Token ãçæãæž¡ãããšã« ãµãŒãã¹ã¯ JWT ã®äžèº«ãããªã¯ãšã¹ãããŠããå©çšè
ã®æ
å ±ãæŸãã ãŸã JWT ã«ã¯çœ²åæ©èœãã€ããŠãããããæ€èšŒãè¡ãããšã§ç¡å¹ãª JWT ãæåŠããããšãã§ãã OPA 㯠JWT ã®ãã³ãŒã/æ€èšŒã« built-in ã§å¯Ÿå¿ããŠãã ãµãŒãã¹åŽã§ãã°ã€ã³æ
å ±ãæŸãå Žåã¯ãJWT ã®ãã³ãŒã/æ€èšŒããžãã¯ã®å®è£
ã ã ã©ããªèšèªã§ãéåžžã«èãå®è£
ã«ãªãïŒ èªå¯ã¯ãµãŒãã¹ã«å°éããåã«å®æœæžã¿ãªã®ã§ãèæ
®äžèŠïŒ 課é¡ãŸãšãã®ãã¡è§£æ¶ããé
ç® èª²é¡ãŸãšãã§æ²åºãã 4./5. ã«å¯ŸããŠã以äžã®ãããªçããåºãŸããã èªå¯ããžãã¯ãäœã§å®è£
ããã Oathkeeper ãš OPA ãçµã¿åãããŠå©çšãã Oathkeeper ã§ã¯ access token ã®æ€èšŒãOPA ã§ URL ããšã«èªå¯ã«ãŒã«ãæžããŠãã èªå¯æ
å ±ããµãŒãã¹ãžã©ããã£ãŠæž¡ãã®ã Oathkeeper ã§ JWT ãçºè¡ããŠãèªå¯æ
å ±ãæž¡ã圢ã§å®çŸãã å®ææ§æ æ§æã€ã¡ãŒãž å°å
¥ååŸã®æ§æã€ã¡ãŒãžãæ¯èŒããŠã¿ãŸãã æ§æã€ã¡ãŒãž/å°å
¥åæ§æã€ã¡ãŒãž/å°å
¥åŸ å°å
¥åŸã®ãããŒè©³çް ç¹ç·ã Envoy external authorization filter ãè¡ãéä¿¡ã瀺ããŠããŸãã ãŸããoathkeeper ã§ access token ã®æ€èšŒãJWT ãžã®å€æãè¡ããOPA ã§åçš®èªå¯åŠçãéããšããµãŒãã¹ã«ãªã¯ãšã¹ããå°éãããšããæ§æã«ãªããŸããã ãŸãããµãŒãã¹åŽã§ãã°ã€ã³æ
å ±ãååŸããå¿
èŠãããå Žåã¯ãJWT ããã³ãŒã/æ€èšŒããŠæ
å ±ãåãåºããç¶æ
ã«ãªããŸããã ãµãŒãã¹åŽã§è¿œå å®è£
ããç®æ OPA åãã«ãŠãŒã¶ãæã£ãŠããæš©éãè¿ããšã³ããã€ã³ã(nautilus) OPA ã§èªå¯ãã§ãã¯ãããéã«å®è¡ããã ãªã¯ãšã¹ãããããã¹ããšã«ãå¿
èŠãšãªãæš©éãç°ãªã OPA åŽã§ã¯ãæ¬ãšã³ããã€ã³ããå®è¡ããŠãæš©éãååŸ ãã®åŸãå¿
èŠãªæš©éãããå Žåã¯èš±å¯ããªãå Žåã¯æåŠãããããªããžãã¯ãæžããŠãã JWT ãæ€èšŒ/JWT ãããªã¯ãšã¹ããŠãŒã¶ã®æ
å ±ãåãåºã middleware è¿ãããŒã¿ããµãŒãã¹åŽã§å¶åŸ¡ããã±ãŒã¹ã§å©çšããã ex.) å¥çŽäžèЧãªã©ã® index action ãªã© èŠåŽãããã€ã³ã äžæãåããªãæã«ãèŠãªããšãããªããã°ãå€ãã£ã ãããããªãœãããäžåºŠã«å¢ãããããã1 ã€ã® HTTP ãªã¯ãšã¹ãã®èªå¯ã®çµæãèŠãã«ããããããã®ãœããã®ãã°ãäžåºŠã«èŠãå¿
èŠããã£ã (tmux ã§ãã€ã³ãéããŠè€æ°ã®ã³ã³ããã stern ããŠããâŠ) tmux å®è¡ã€ã¡ãŒãž Envoy ext_authz ã®èšå®ãå
¥ãã Istio ã®èšå®ãç·šã¿åºãã®ã倧å€ã ã£ã ext_authz ã Istio native ã§ãµããŒããããŠããªããããEnvoyFilter ãšãããªãœãŒã¹ã§èšå®ãæžããªããšãããªãã£ã EnvoyFilter 㯠Istio ãçæããŠããã Envoy ã³ã³ãã£ã°äžã®ãã©ã®ç®æã«ãèšå®ã远å ããããšããæžãæ¹ãããå¿
èŠããã åžæã®å Žæã«ã³ã³ãã£ã°ãæã£ãŠããããã®æžãæ¹ãç·šã¿åºãã®ã«æéãããã£ã æåéãã®æåãããŠãããªãã£ãã⊠ex.) INSERT_FIRST ãåããªãã£ã èŠåŽããèšå®ãµã³ãã« configPatches: - applyTo: HTTP_ROUTE match: &config_patches_match context: SIDECAR_INBOUND routeConfiguration: name: inbound|3050|| patch: # ã©ããã£ãŠããINSERT_BEFORE/INSERT_FIRST ã§ default route ã®åã«å€ãå
¥ããããªãã®ã§ã # 仿¹ãªãããŸã MERGE ã§ default ãæžãæãã operation: MERGE value: decorator: &decorator operation: gargant-app.gargant.svc.cluster.local:3050/* match: safe_regex: google_re2: {} regex: (/api/v[0-9]+)?/healthz name: gargant_healthz route: &route cluster: inbound|3050|| maxGrpcTimeout: 0s timeout: 0s typed_per_filter_config: &disable_ext_authz envoy.filters.http.ext_authz: '@type': type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute disabled: true ãŸãšã Istio ãå°å
¥ããOPA/Oathkeeper ãããã«çµã¿åãããããšã§ãèªå¯ããµãŒãã¹ã®å€ã«åºããå
±éåã§ããããã«ãªã£ã èªåå®è£
ãå¢ããããšãªããã»ãšãã©ã®ç®æãèšå®ã§ç®¡çã§ããããã«ã§ãã ã©ã®ãããªãµãŒãã¹ã«å¯ŸããŠããOIDC åºç€ãš nautilus äžã«ãããŠãŒã¶ã»æš©éæ
å ±ãå©çšããŠãèªèšŒèªå¯ã远å ã§ããããã«ãªã£ã æ°ããªèªå¯æ¹åŒãå®è£
ããå ŽåããOPA/Oathkeeper ã§å®çŸã§ããã°ããµãŒãã¹ã«ã¿ããããã«å®è£
å¯èœã«ãªã£ã ex.) API token æ©èœ(å®è£
äž) ãã©ãŠã¶ã«ããèªèšŒäžèŠã§ API ãå®è¡ã§ããæéããªã token hydra ã«ã¯æéãªãã® token ãçºè¡ã§ããæ©æ§ã¯ãªããããèªåå®è£
oathkeeper ã§ token ã®çš®é¡ã®å·®ç°ãåžåããOPA/ãµãŒãã¹åŽã«ã¯åœ±é¿ãäžããªãèšèšã§å®è£
å¯èœãšãªã£ã ä»å玹ä»ããæ§æããããããã察象è
Kubernetes ã¯ã©ã¹ã¿äžã§è€æ°ã®ããã¯ãšã³ããç«ã¡äžããŠéçšããŠãã人ã«ãããã æ§æèŠçŽ ãå©çšæè¡ã¯ãããã«å€ã ããããäžåºŠããŸãåãã°ããã®åŸã®è»¢çšã»æ¡åŒµã¯æ¯èŒçå®¹æ æ§æèŠçŽ ã®å¢å ã«ãããæ¡åŒµãã€ã³ããå¢ãããã èªèšŒèªå¯ã«é¢ããèªåå®è£
ãå¢ããªã ãµãŒãã¹ã¡ãã·ã¥ã«ããããµãŒãã¹ã«ãªã¯ãšã¹ããå°éããåã«ä»»æã®åŠçãæããããã«ãªã ä»é² éçšäžãã©ããã€ã³ã Istio ã®ããŒãžã§ã³ã¢ãããæ©ã 3 ãæã« 1 床ã¡ãžã£ãŒããŒãžã§ã³ã¢ãããã ãµããŒãæéãå幎 ãšã¯ãããGKE ãèªåçã«ããŒãžã§ã³ã¢ããããŠãããããã«å¯Ÿå¿ãã Istio ãäžããããåŸãªãã®ã§ããã£ãšåãããŒãžã§ã³ã䜿ãç¶ãããšããä¿å®çãªäœ¿ãæ¹ã¯ã§ããªã ç¶ç¶çã«ã¢ããããŒãã§ãããããªæ çµã¿ãèããå¿
èŠããã in-place update 㯠1.8 ãããµããŒããããŠããŠãç¡åæ¢ã§ã®ã¢ããããŒãå®çžŸãã åŠç¿ã³ã¹ããé«ã Istio 1ã€ã§ãèšå€§ãªãªãœãŒã¹/ããã¥ã¡ã³ãé ããã« OPA ã oathkeeper ãããã«ä¹ãããããååŠè
ã¯èŠããããšãã©ãããŠãå€ããªã£ãŠããŸã oauth2-proxy ã®è£åŽ å®ã¯ EnvoyFilter ã§ããããŒã®å
¥ãæ¿ããè¡ã£ãŠãã oauth2-proxy ã« OP(OpenID Provider) ããååŸãã access token ã upstream ã«æž¡ããŠããããªãã·ã§ã³ãååšãã https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/overview/#command-line-options å
ã® --pass-access-token ãªãã·ã§ã³ ããã Authorization ããããŒä»¥å€ã«ã€ã(X-Forwarded-Access-Token ããããŒ) ãã®ãããEnvoyFilter ã§ Authorization ããããŒã«å
¥ããªãããŠãoathkeeper ã«ãªã¯ãšã¹ãããããã«èšå®ããŠãã configPatches: - applyTo: HTTP_FILTER match: &match context: SIDECAR_INBOUND listener: filterChain: filter: name: envoy.http_connection_manager subFilter: name: envoy.router portNumber: 8080 patch: operation: INSERT_BEFORE value: name: envoy.filters.http.ext_authz typed_config: '@type': type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz failure_mode_allow: false http_service: authorization_request: headers_to_add: - key: Authorization value: Bearer %REQ(X-FORWARDED-ACCESS-TOKEN)% authorization_response: allowed_upstream_headers: patterns: - exact: authorization path_prefix: /decisions server_uri: cluster: outbound|4456||oathkeeper-api.oathkeeper.svc.cluster.local timeout: 2s uri: http://oathkeeper-api.oathkeeper.svc.cluster.local:4456 status_on_error: code: ServiceUnavailable èè
ãããã£ãŒã« ä»é ä¿ä» æ ªåŒäŒç€ŸWESEEK / ããã¯ãšã³ããšã³ãžã㢠æ ISP ã« 2 幎匱å€åããåŸã2013 幎㫠WESEEK ãžå
¥ç€Ÿã çŸåšã¯ãä»åã玹ä»ããã€ã³ã¿ãŒããããã«ããã£ãŒãæ§éçºæ¡ä»¶ãªã©ãããžã§ã¯ãåãããRails/Kubernetes ãäžå¿ãšããã€ã³ãã©/ã¢ããªã®èšèšã»æ§ç¯ã»éçšã«æºããã æ ªåŒäŒç€ŸWESEEKã«ã€ããŠ æ ªåŒäŒç€ŸWESEEKã¯ãã·ã¹ãã éçºã®ãããã§ãã·ã§ãã«éå£ã§ãã ãçŸåšã®äž»ãªäºæ¥ã é信倧æäŒæ¥ã®æ¥åãããŒèªååãããžã§ã¯ã ãœãŒã·ã£ã«ã²ãŒã ã®åèšéçº èªç€ŸçºãªãŒãã³ãœãŒã¹ãããã¯ããGROWIããGROWI.cloudãã®éçº GROWI GROWIã¯ãMarkdownèšæ³ã§ããŒãžãèšè¿°ã§ãããªãŒãã³ãœãŒã¹ã®Wikiã·ã¹ãã ã§ãã GROWI.cloud GROWI.cloudã¯OSSã®GROWIãå°éçç¥èããªããŠãç°¡åã«éçšã»ç®¡çã§ãããæ³äººã»å人åãã®åçšãµãŒãã¹ã§ãã 倧æSIerã»ISPãäžå°äŒæ¥ã倧åŠã®ç 究宀ãªã©æ§ã
ãªå Žæã§ãå©çšããã ããŠãããŸãã ãäž»ãªç¹åŸŽã ããã¹ããå³è¡šãã©ãã©ãæžããã匷åãªç·šéæ©èœ ããŒã æ¡å€§ã«è¿
éã«å¯Ÿå¿ã§ãã管çè
åãæ©èœãæäŸ å
å®ããæ©èœã»ãµããŒãã§ãšã³ã¿ãŒãã©ã€ãºã«ãå¯Ÿå¿ ãå°å
¥äºäŸèšäºã ã€ã³ã¿ãŒããããã«ããã£ãŒãæ ªåŒäŒç€Ÿæ§ [https://growi.cloud/interviews/mfeed/?utm_source=connpass-top&utm_medium=web-site&utm_campaign=mf:embed:cite] æ ªåŒäŒç€ŸHIKKYïŒVRæ³äººHIKKYïŒæ§ [https://growi.cloud/interviews/hikky:embed:cite] WESEEK Tech Conference WESEEK Tech Conferenceã¯ãæ ªåŒäŒç€ŸWESEEKãäž»å¬ãããšã³ãžãã¢åãã®å匷äŒãWESEEKã«æå±ãããšã³ãžãã¢ãæ§ã
ãªããŒãã§çºè¡šãè¡ãäºå®ã§ãã æ¬¡åã®WESEEK Tech Conferenceã®ããŒãã¯ãRails+RSpecã§æ°è»œã«å§ãããã¹ããïŒ 9/30(æš)ã®19:00~20:00éå¬äºå®ã§ãã ã€ãã€ãæžãã®ãåå«ã«ãªã£ãŠããŸããã¹ãã³ãŒããæ°è»œã«ãã¹ããæžããŠãããã¯ãã®å質ãé«ãããWESEEKæµã®å®è·µæ¹æ³ã玹ä»ããŸãã ãŸããéçºãšæžãããã¹ãã³ãŒãã®å®è¡ãã©ã®ããã«è¡ã£ãŠããã®ããCIã«ã€ããŠãç°¡åã«ã話ããŸãã çŸåšãconnpassãTECH PLAYã§åå åä»äžã§ããçæ§ã®ãåå ããåŸ
ã¡ããŠãããŸãïŒhttps://weseek.connpass.com/event/224673/https://techplay.jp/event/830907 äžç·ã«åã仲éãåéããŠããŸã æ±äº¬ã®é«ç°éŠ¬å Žãªãã£ã¹ã倧åã«ããå¥åºãµãã©ã€ããªãã£ã¹ã«ãŠãšã³ãžãã¢ãåéããŠãããŸããäžéæ¡çšã ãã§ã¯ãªããã€ã³ã¿ãŒã³ã·ãããç©æ¥µçã«åãå
¥ããŠããŸãïŒ è©³ããåéèŠé
ã¯ãåŒç€ŸHPã®æ¡çšããŒãžããã確èªãã ããã