ç§ãã¡ãåãçµãã§ãããªã³ã©ã€ã³ ãã㪠ã³ã³ãã³ãæšå¥šã·ã¹ãã ã¯éå
¬éã®åæ¥éçºã§ãããæè¡çã«ã¯ç¬èªã®ãªãŒãã³ãœãŒã¹ ã³ã³ããŒãã³ãã®ãã«ãã³ã³ããŒãã³ã ã¯ã©ã¹ã¿ãŒã§ãã ãã®èšäºãæžãç®çã¯ãéãããæéå
ã§ããã»ã¹ã®ç¢ºç«ãããã¯ãŒã¯ãããŒãäžæããããšãªããã¹ããŒãžã³ã° ãµã€ãåãã® Docker swarm ã¯ã©ã¹ã¿ãªã³ã° ã·ã¹ãã ã®å®è£
ã«ã€ããŠèª¬æããããšã§ãã ããªãã®æ³šæãåŒãç©èªã¯ XNUMX ã€ã®éšåã«åãããŠããŸãã æåã®éšåã§ã¯ docker swarm ã䜿çšããåã® CI / CD ã«ã€ããŠèª¬æããXNUMX çªç®ã®éšåã§ã¯ãã®å®è£
ããã»ã¹ã«ã€ããŠèª¬æããŸãã æåã®éšåãèªãããšã«èå³ããªã人ã¯ãå®å
šã« XNUMX çªç®ã®éšåã«é²ãã§ãã ããã
ããŒã1
é ãæãCI/CD ããã»ã¹ãã§ããã ãæ©ãã»ããã¢ããããå¿ èŠããããŸããã æ¡ä»¶ã®XNUMXã€ã¯Dockerã䜿çšããªãããšã§ãã å°å ¥çš ããã€ãã®çç±ããã³ã³ããŒãã³ããéçºããŸããã
- æ¬çªç°å¢ã§ã®ã³ã³ããŒãã³ãã®åäœã®ä¿¡é Œæ§ãšå®å®æ§ãé«ãããã (ã€ãŸããå®éã«ã¯ä»®æ³åã䜿çšããªãèŠä»¶)
- äž»èŠãªéçºè 㯠Docker ã䜿ããããããŸããã§ãã (å¥åŠã§ãããå®éã¯ããã§ãã)
- ç 究éçºç®¡çã®ã€ããªãã®ãŒçèå¯ã«ãããš
MVP ã®ã€ã³ãã©ã¹ãã©ã¯ãã£ãã¹ã¿ãã¯ãããã³ããããã®åæèŠä»¶ã¯æ¬¡ã®ããã«ç€ºãããŠããŸãã
- Debian ãæèŒãã 4 å°ã® Intel® X5650 ãµãŒã㌠(ãã XNUMX å°ã®åŒ·åãªãã·ã³ãå®å šã«éçºãããŠããŸã)
- ç¬èªã®ã«ã¹ã¿ã ã³ã³ããŒãã³ãã®éçºã¯C++ãPython3ã§è¡ãããŸãã
- 䜿çšãããäž»ãªãµãŒãããŒã㣠ããŒã«: KafkaãClickhouseãAirflowãRedisãGrafanaãPostgresqlãMysql ãªã©
- ãããã°ãšãªãªãŒã¹ã®ããã«ã³ã³ããŒãã³ããåå¥ã«æ§ç¯ããã³ãã¹ãããããã®ãã€ãã©ã€ã³
åæ段éã§å¯ŸåŠããå¿ èŠãããæåã®è³ªåã® XNUMX ã€ã¯ãã«ã¹ã¿ã ã³ã³ããŒãã³ããç°å¢ (CI / CD) ã«ã©ã®ããã«ãããã€ãããã§ãã
ç§ãã¡ã¯ããµãŒãããŒãã£ã®ã³ã³ããŒãã³ããã·ã¹ãã çã«ã€ã³ã¹ããŒã«ããã·ã¹ãã çã«æŽæ°ããããšã«ããŸããã C++ ãŸã㯠Python ã§éçºãããã«ã¹ã¿ã ã¢ããªã±ãŒã·ã§ã³ã¯ãããã€ãã®æ¹æ³ã§ãããã€ã§ããŸãã ãã®äžã«ã¯ãããšãã°ãã·ã¹ãã ããã±ãŒãžãäœæãããã«ããããã€ã¡ãŒãžã®ãªããžããªã«éä¿¡ããŠããµãŒããŒã«ã€ã³ã¹ããŒã«ããããšãå«ãŸããŸãã çç±ã¯äžæã§ãããå¥ã®æ¹æ³ãéžæãããŸãããã€ãŸããCI ã䜿çšããŠãã¢ããªã±ãŒã·ã§ã³ã®å®è¡å¯èœãã¡ã€ã«ãã³ã³ãã€ã«ãããä»®æ³ãããžã§ã¯ãç°å¢ãäœæãããpy ã¢ãžã¥ãŒã«ãrequirements.txt ããã€ã³ã¹ããŒã«ãããããããã¹ãŠã®ã¢ãŒãã£ãã¡ã¯ããæ§æãã¹ã¯ãªãããããã³ãµãŒããŒã«ä»éããã¢ããªã±ãŒã·ã§ã³ç°å¢ã 次ã«ãã¢ããªã±ãŒã·ã§ã³ã¯ç®¡çè æš©éã®ãªãä»®æ³ãŠãŒã¶ãŒãšããŠèµ·åãããŸãã
CI/CD ã·ã¹ãã ãšã㊠Gitlab-CI ãéžæãããŸããã çµæã®ãã€ãã©ã€ã³ã¯æ¬¡ã®ããã«ãªããŸãã
gitlab-ci.yml ã®æ§é ã¯æ¬¡ã®ããã«ãªããŸãã
---
variables:
# ЌОМОЌалÑÐœÐ°Ñ Ð²ÐµÑÑÐžÑ ÐŠÐУ Ма ÑеÑвеÑаÑ
, гЎе ÑазвПÑаÑОваеÑÑÑ ÐºÐ»Ð°ÑÑеÑ
CMAKE_CPUTYPE: "westmere"
DEBIAN: "MYREGISTRY:5000/debian:latest"
before_script:
- eval $(ssh-agent -s)
- ssh-add <(echo "$SSH_PRIVATE_KEY")
- mkdir -p ~/.ssh && echo -e "Host *ntStrictHostKeyChecking nonn" > ~/.ssh/config
stages:
- build
- testing
- deploy
debug.debian:
stage: build
image: $DEBIAN
script:
- cd builds/release && ./build.sh
paths:
- bin/
- builds/release/bin/
when: always
release.debian:
stage: build
image: $DEBIAN
script:
- cd builds/release && ./build.sh
paths:
- bin/
- builds/release/bin/
when: always
## testing stage
tests.codestyle:
stage: testing
image: $DEBIAN
dependencies:
- release.debian
script:
- /bin/bash run_tests.sh -t codestyle -b "${CI_COMMIT_REF_NAME}_codestyle"
tests.debug.debian:
stage: testing
image: $DEBIAN
dependencies:
- debug.debian
script:
- /bin/bash run_tests.sh -e codestyle/test_pylint.py -b "${CI_COMMIT_REF_NAME}_debian_debug"
artifacts:
paths:
- run_tests/username/
when: always
expire_in: 1 week
tests.release.debian:
stage: testing
image: $DEBIAN
dependencies:
- release.debian
script:
- /bin/bash run_tests.sh -e codestyle/test_pylint.py -b "${CI_COMMIT_REF_NAME}_debian_release"
artifacts:
paths:
- run_tests/username/
when: always
expire_in: 1 week
## staging stage
deploy_staging:
stage: deploy
environment: staging
image: $DEBIAN
dependencies:
- release.debian
script:
- cd scripts/deploy/ &&
python3 createconfig.py -s $CI_ENVIRONMENT_NAME &&
/bin/bash install_venv.sh -d -r ../../requirements.txt &&
python3 prepare_init.d.py &&
python3 deploy.py -s $CI_ENVIRONMENT_NAME
when: manual
ã¢ã»ã³ããªãšãã¹ãã¯ãå¿ èŠãªãã¹ãŠã®ã·ã¹ãã ããã±ãŒãžããã§ã«ã€ã³ã¹ããŒã«ããããã®ä»ã®èšå®ãè¡ãããŠããç¬èªã®ã€ã¡ãŒãžäžã§å®è¡ãããããšã«æ³šæããŠãã ããã
ãžã§ãå ã®ãããã®ã¹ã¯ãªããã¯ããããèå³æ·±ããã®ã§ãããããããã«ã€ããŠèª¬æãããšéåžžã«æéããããã®ã§ããã¡ãã説æããŸãããããã¯ãã®èšäºã®ç®çã§ã¯ãããŸããã ãããã€ã¡ã³ã段éãäžé£ã®åŒã³åºãã¹ã¯ãªããã§æ§æãããŠãããšããäºå®ã®ã¿ã«æ³šç®ããŠããã ããŸãã
- createconfig.py - åŸç¶ã®å±é (å®çšŒååãå®çšŒåããã¹ããªã©) ã«åããŠãããŸããŸãªç°å¢ã§ã³ã³ããŒãã³ãèšå®ãå«ã settings.ini ãã¡ã€ã«ãäœæããŸãã
- install_venv.sh - ç¹å®ã®ãã£ã¬ã¯ããªã« py ã³ã³ããŒãã³ãã®ä»®æ³ç°å¢ãäœæããããããªã¢ãŒã ãµãŒããŒã«ã³ããŒããŸã
- prepare_init.d.py â ãã³ãã¬ãŒãã«åºã¥ããŠã³ã³ããŒãã³ãã®éå§/åæ¢ã¹ã¯ãªãããæºåããŸã
- ãããã€.py - æ°ããã³ã³ããŒãã³ããå解ããŠåèµ·åããŸã
æã¯éããã ã¹ããŒãžã³ã°æ®µéã¯ããªãããã¯ã·ã§ã³ãšãããã¯ã·ã§ã³ã«çœ®ãæããããŸããã ãã 5 ã€ã®ãã£ã¹ããªãã¥ãŒã·ã§ã³ (CentOS) ã§ã®è£œåã®ãµããŒããè¿œå ãããŸããã ããã«åŒ·å㪠XNUMX å°ã®ç©çãµãŒããŒãš XNUMX å°ã®ä»®æ³ãµãŒããŒãè¿œå ããŸããã ãããŠãéçºè ããã¹ã¿ãŒã«ââãšã£ãŠãå€ããå°ãªããåäœç¶æ ã«è¿ãç°å¢ã§ã¿ã¹ã¯ããã¹ãããããšããŸããŸãå°é£ã«ãªã£ãŠããŸããã ãã®ãšãã圌ãªãã§ã¯äžå¯èœã§ããããšãæããã«ãªããŸãã...
ããŒãII
ãããã£ãŠãç§ãã¡ã®ã¯ã©ã¹ã¿ãŒã¯ãDockerfile ã§ã¯èšè¿°ãããªãæ°åã®åå¥ã®ã³ã³ããŒãã³ããããªãçŽ æŽãããã·ã¹ãã ã§ãã éåžžã¯ãç¹å®ã®ç°å¢ã«å±éããããã«ã®ã¿æ§æã§ããŸãã ç§ãã¡ã®ã¿ã¹ã¯ã¯ããªãªãŒã¹åãã¹ãã®åã«ã¯ã©ã¹ã¿ãŒãã¹ããŒãžã³ã°ç°å¢ã«ãããã€ããŠãã¹ãããããšã§ãã
çè«çã«ã¯ãå®äºç¶æ ãŸãã¯å®äºã«è¿ãã¿ã¹ã¯ã®æ°ãšåæ°ã®è€æ°ã®ã¯ã©ã¹ã¿ãŒãåæã«å®è¡ã§ããŸãã èªç±ã«äœ¿ãããµãŒããŒã®å®¹éã«ãããåãµãŒããŒã§è€æ°ã®ã¯ã©ã¹ã¿ãŒãå®è¡ã§ããŸãã åã¹ããŒãžã³ã° ã¯ã©ã¹ã¿ãŒã¯åé¢ãããŠããå¿ èŠããããŸã (ããŒãããã£ã¬ã¯ããªãªã©ã«äº€å·®ããã£ãŠã¯ãªããŸãã)ã
ç§ãã¡ã®æã貎éãªãªãœãŒã¹ã¯æéã§ãããç§ãã¡ã«ã¯ãããããŸããããŸããã§ããã
ããè¿ éã«éå§ããããã«ããã®ã·ã³ãã«ããšã¢ãŒããã¯ãã£ã®æè»æ§ã«ãã Docker Swarm ãéžæããŸããã æåã«ããªã¢ãŒã ãµãŒããŒäžã«ãããŒãžã£ãŒãšããã€ãã®ããŒããäœæããŸããã
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
kilqc94pi2upzvabttikrfr5d nop-test-1 Ready Active 19.03.2
jilwe56pl2zvabupryuosdj78 nop-test-2 Ready Active 19.03.2
j5a4yz1kr2xke6b1ohoqlnbq5 * nop-test-3 Ready Active Leader 19.03.2
次ã«ããããã¯ãŒã¯ãäœæããŸãã
$ docker network create --driver overlay --subnet 10.10.10.0/24 nw_swarm
次ã«ãCI ããã®ããŒãã®ãªã¢ãŒãå¶åŸ¡ã«é¢ããŠãGitlab-CI ãš Swarm ããŒããæ¥ç¶ããŸãããã€ãŸãã蚌ææžã®ã€ã³ã¹ããŒã«ãã·ãŒã¯ã¬ããå€æ°ã®èšå®ãããã³ã³ã³ãããŒã« ãµãŒããŒã§ã® Docker ãµãŒãã¹ã®ã»ããã¢ãããè¡ããŸããã ããã§ã
次ã«ãã¹ã¿ãã¯ã®äœæãžã§ããšç Žæ£ãžã§ãã .gitlab-ci .yml ã«è¿œå ããŸããã
ããã«ããã€ãã®ãžã§ãã .gitlab-ci .yml ã«è¿œå ãããŸãã
## staging stage
deploy_staging:
stage: testing
before_script:
- echo "override global 'before_script'"
image: "REGISTRY:5000/docker:latest"
environment: staging
dependencies: []
variables:
DOCKER_CERT_PATH: "/certs"
DOCKER_HOST: tcp://10.50.173.107:2376
DOCKER_TLS_VERIFY: 1
CI_BIN_DEPENDENCIES_JOB: "release.centos.7"
script:
- mkdir -p $DOCKER_CERT_PATH
- echo "$TLSCACERT" > $DOCKER_CERT_PATH/ca.pem
- echo "$TLSCERT" > $DOCKER_CERT_PATH/cert.pem
- echo "$TLSKEY" > $DOCKER_CERT_PATH/key.pem
- docker stack deploy -c docker-compose.yml ${CI_ENVIRONMENT_NAME}_${CI_COMMIT_REF_NAME} --with-registry-auth
- rm -rf $DOCKER_CERT_PATH
when: manual
## stop staging stage
stop_staging:
stage: testing
before_script:
- echo "override global 'before_script'"
image: "REGISTRY:5000/docker:latest"
environment: staging
dependencies: []
variables:
DOCKER_CERT_PATH: "/certs"
DOCKER_HOST: tcp://10.50.173.107:2376
DOCKER_TLS_VERIFY: 1
script:
- mkdir -p $DOCKER_CERT_PATH
- echo "$TLSCACERT" > $DOCKER_CERT_PATH/ca.pem
- echo "$TLSCERT" > $DOCKER_CERT_PATH/cert.pem
- echo "$TLSKEY" > $DOCKER_CERT_PATH/key.pem
- docker stack rm ${CI_ENVIRONMENT_NAME}_${CI_COMMIT_REF_NAME}
# TODO: need check that stopped
when: manual
äžèšã®ã³ãŒã ã¹ãããããããXNUMX ã€ã®ãã¿ã³ (deploy_stagingãstop_staging) ããã€ãã©ã€ã³ã«è¿œå ãããŠãããæåæäœãå¿ èŠã§ããããšãããããŸãã
ã¹ã¿ãã¯åã¯ãã©ã³ãåãšäžèŽããŠããããã®äžææ§ã§ååã§ãã ã¹ã¿ãã¯å
ã®ãµãŒãã¹ã¯ãäžæã® IP ã¢ãã¬ã¹ãããŒãããã£ã¬ã¯ããªãªã©ãåãåããŸãã åé¢ãããŸãããã¹ã¿ãã¯éã§åãã§ã (æ§æãã¡ã€ã«ã¯ãã¹ãŠã®ã¹ã¿ãã¯ã§åãã§ãããã) - ç§ãã¡ãæãã§ãããã®ã§ãã 次ã䜿çšããŠã¹ã¿ã㯠(ã¯ã©ã¹ã¿ãŒ) ããããã€ããŸãã docker-compose.ymlãã¯ã©ã¹ã¿ãŒã«ã€ããŠèª¬æããŸãã
docker-compose.yml
---
version: '3'
services:
userprop:
image: redis:alpine
deploy:
replicas: 1
placement:
constraints: [node.id == kilqc94pi2upzvabttikrfr5d]
restart_policy:
condition: none
networks:
nw_swarm:
celery_bcd:
image: redis:alpine
deploy:
replicas: 1
placement:
constraints: [node.id == kilqc94pi2upzvabttikrfr5d]
restart_policy:
condition: none
networks:
nw_swarm:
schedulerdb:
image: mariadb:latest
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
MYSQL_DATABASE: schedulerdb
MYSQL_USER: ****
MYSQL_PASSWORD: ****
command: ['--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci', '--explicit_defaults_for_timestamp=1']
deploy:
replicas: 1
placement:
constraints: [node.id == kilqc94pi2upzvabttikrfr5d]
restart_policy:
condition: none
networks:
nw_swarm:
celerydb:
image: mariadb:latest
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
MYSQL_DATABASE: celerydb
MYSQL_USER: ****
MYSQL_PASSWORD: ****
deploy:
replicas: 1
placement:
constraints: [node.id == kilqc94pi2upzvabttikrfr5d]
restart_policy:
condition: none
networks:
nw_swarm:
cluster:
image: $CENTOS7
environment:
- CENTOS
- CI_ENVIRONMENT_NAME
- CI_API_V4_URL
- CI_REPOSITORY_URL
- CI_PROJECT_ID
- CI_PROJECT_URL
- CI_PROJECT_PATH
- CI_PROJECT_NAME
- CI_COMMIT_REF_NAME
- CI_BIN_DEPENDENCIES_JOB
command: >
sudo -u myusername -H /bin/bash -c ". /etc/profile &&
mkdir -p /storage1/$CI_COMMIT_REF_NAME/$CI_PROJECT_NAME &&
cd /storage1/$CI_COMMIT_REF_NAME/$CI_PROJECT_NAME &&
git clone -b $CI_COMMIT_REF_NAME $CI_REPOSITORY_URL . &&
curl $CI_API_V4_URL/projects/$CI_PROJECT_ID/jobs/artifacts/$CI_COMMIT_REF_NAME/download?job=$CI_BIN_DEPENDENCIES_JOB -o artifacts.zip &&
unzip artifacts.zip ;
cd /storage1/$CI_COMMIT_REF_NAME/$CI_PROJECT_NAME/scripts/deploy/ &&
python3 createconfig.py -s $CI_ENVIRONMENT_NAME &&
/bin/bash install_venv.sh -d -r ../../requirements.txt &&
python3 prepare_init.d.py &&
python3 deploy.py -s $CI_ENVIRONMENT_NAME"
deploy:
replicas: 1
placement:
constraints: [node.id == kilqc94pi2upzvabttikrfr5d]
restart_policy:
condition: none
tty: true
stdin_open: true
networks:
nw_swarm:
networks:
nw_swarm:
external: true
ããã§ã¯ãã³ã³ããŒãã³ãã XNUMX ã€ã®ãããã¯ãŒã¯ (nw_swarm) ã«ãã£ãŠæ¥ç¶ãããŠãããçžäºã«å©çšã§ããããšãããããŸãã
ã·ã¹ãã ã³ã³ããŒãã³ã (redisãmysql ã«åºã¥ã) ã¯ãã«ã¹ã¿ã ã³ã³ããŒãã³ãã®äžè¬çãªããŒã«ããåé¢ãããŸã (ãã©ã³å ã§ãã«ã¹ã¿ã ã³ã³ããŒãã³ãã¯ãµãŒãã¹ãšããŠåå²ãããŸã)ã ã¯ã©ã¹ã¿ãŒã®ãããã€ã¡ã³ã段éã¯ãCMD ã XNUMX ã€ã®å€§èŠæš¡ãªæ§ææžã¿ã€ã¡ãŒãžã«æž¡ãããã«èŠããäžè¬ã«ãããŒã I ã§èª¬æãããããã€ã¡ã³ããšå®è³ªçã«å€ãããŸãããçžéç¹ã匷調ããŸãã
- gitã¯ããŒã³... - ãããã€ã«å¿ èŠãªãã¡ã€ã«ãååŸããŸã (createconfig.pyãinstall_venv.sh ãªã©)ã
- ã«ãŒã«...&& 解å... - ãã«ã ã¢ãŒãã£ãã¡ã¯ã (ã³ã³ãã€ã«ããããŠãŒãã£ãªãã£) ãããŠã³ããŒãããŠè§£åããŸãã
ãŸã 説æãããŠããªãåé¡ã XNUMX ã€ã ããããŸããããã¯ãWeb ã€ã³ã¿ãŒãã§ã€ã¹ãæã€ã³ã³ããŒãã³ãã«éçºè ã®ãã©ãŠã¶ããã¢ã¯ã»ã¹ã§ããªãããšã§ãã ãã®åé¡ã¯ãªããŒã¹ ãããã·ã䜿çšããŠè§£æ±ºããŸãã次ã®ããã«ãªããŸãã
.gitlab-ci.yml ã§ã¯ãã¯ã©ã¹ã¿ãŒ ã¹ã¿ãã¯ããããã€ããåŸããã©ã³ãµãŒããããã€ããè¡ãè¿œå ããŸã (ã³ãããæã«ããã®æ§æã®ã¿ãæŽæ°ãããŸã (ãã³ãã¬ãŒã /etc/nginx/conf ã«åŸã£ãŠæ°ãã nginx æ§æãã¡ã€ã«ãäœæãããŸã)ã d/${CI_COMMIT_REF_NAME}.conf) - docker-compose-nginx.yml ã³ãŒããåç §)
- docker stack deploy -c docker-compose-nginx.yml ${CI_ENVIRONMENT_NAME} --with-registry-auth
docker-compose-nginx.yml
---
version: '3'
services:
nginx:
image: nginx:latest
environment:
CI_COMMIT_REF_NAME: ${CI_COMMIT_REF_NAME}
NGINX_CONFIG: |-
server {
listen 8080;
server_name staging_${CI_COMMIT_REF_NAME}_cluster.dev;
location / {
proxy_pass http://staging_${CI_COMMIT_REF_NAME}_cluster:8080;
}
}
server {
listen 5555;
server_name staging_${CI_COMMIT_REF_NAME}_cluster.dev;
location / {
proxy_pass http://staging_${CI_COMMIT_REF_NAME}_cluster:5555;
}
}
volumes:
- /tmp/staging/nginx:/etc/nginx/conf.d
command:
/bin/bash -c "echo -e "$$NGINX_CONFIG" > /etc/nginx/conf.d/${CI_COMMIT_REF_NAME}.conf;
nginx -g "daemon off;";
/etc/init.d/nginx reload"
ports:
- 8080:8080
- 5555:5555
- 3000:3000
- 443:443
- 80:80
deploy:
replicas: 1
placement:
constraints: [node.id == kilqc94pi2upzvabttikrfr5d]
restart_policy:
condition: none
networks:
nw_swarm:
networks:
nw_swarm:
external: true
éçºçšã³ã³ãã¥ãŒã¿ãŒã§ /etc/hosts ãæŽæ°ããŸãã nginx ã« URL ãæå®ããŸã:
10.50.173.106 staging_BRANCH-1831_cluster.dev
ãã®ãããåé¢ãããã¹ããŒãžã³ã° ã¯ã©ã¹ã¿ãŒã®ãããã€ã¡ã³ããå®è£
ãããéçºè
ã¯ã¿ã¹ã¯ã確èªããã®ã«ååãªæ°ã ãã¯ã©ã¹ã¿ãŒãå®è¡ã§ããããã«ãªããŸããã
ä»åŸã®èšç»ïŒ
- ã³ã³ããŒãã³ãããµãŒãã¹ãšããŠåé¢ãã
- Dockerfileããšã«æã€
- ã¹ã¿ãã¯å ã§è² è·ã®äœãããŒããèªåçã«æ€åºããŸã
- (èšäºã®ããã« ID ã䜿çšããã®ã§ã¯ãªã) ååãã¿ãŒã³ã§ããŒããæå®ããŸãã
- ã¹ã¿ãã¯ãç Žæ£ãããããšã確èªãããã§ãã¯ãè¿œå ãã
- ...
ç¹ã«æè¬ããŸã
åºæïŒ habr.com