TS7053

3.0 버전이 되면서 HOC 기반으로 변경되며 rules를 상위 컴포넌트에서 확장하게 되었다.
문제는 typescript 기반에서 룰 전체 등록이 TS(7053) 에러를 발생시킨다.

1
2
3
4
5
6
import { ValidationProvider, ValidationObserver, extend } from 'vee-validate';
import * as rules from 'vee-validate/dist/rules';

Object.keys(rules).forEach((rule) => {
extend(rule, rules[rule]); // rules[rule] 에서 타입오류 발생
});
1
2
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'typeof import(".../node_modules/vee-validate/dist/rules")'.
No index signature with a parameter of type 'string' was found on type 'typeof import(".../node_modules/vee-validate/dist/rules")'.ts(7053)

원인

import, export 의 모듈명은 string index 로 쳐지지 않아 발생한다.

해결방법

Object.entriesfor of를 사용해 타입에 안전하게 돌려주면 된다.

1
2
3
4
5
6
7
8
import { extend } from 'vee-validate';
import * as rules from 'vee-validate/dist/rules';

for (let [rule, validation] of Object.entries(rules)) {
extend(rule, {
...validation,
});
}

여담

  • 새로운 구문 (async/await, import/export)를 사용해 돌릴 땐 먼저 for of를 사용하는 습관을 들여야겠다.
  • 머지되어서 다음 사람의 삽질은 없을 듯 하다.

참조

  • Merged docs: added Installing All Rules with typescript
  • vee-validate#installing-all-rules

Helm

Helm is a tool for manaing Kubernetes charts.
Charts are packages of pre-configured Kubernetes resources.

  • 헬름은 쿠버네티스 차트를 관리하기 위한 도구
  • 차트는 사전 구성된 쿠버네티스 리소스의 패키지
  • 같은 어플레케이션을 여러 환경에 배포시 환경 변수, 도메인 등의 manifest 파일을 차트를 통해 관리
  • 차트를 중심으로 하는 쿠버네티스 개발 종합 관리 도구

설치

1
2
3
4
5
6
7
8
9
10
11
# 설치
brew install kubernetes-helm

# 초기화
helm init

# tiller 파드 확인
kubectl -n kube-system get service,deployment,pod --selector app=helm

# 버전 확인
helm version

구성

  • cli와 쿠버네티스 클러스터에 설치되는 서버인 tiller(틸러)로 구성

chart

  • 쿠버네티스는 service, deployment, ingress 등 리소스를 생성하고 manifest 파일을 적용하는 방식으로 어플리케이션을 배포한다. 이 manifest 파일을 생성하는 템플릿을 여러 개 패키징한 것
  • helm repository 에 tgz 파일로 저장

chart 구성

1
2
3
4
5
6
7
8
chart-example
├── charts # 차트가 의존하는 차트 디렉토리
├── templates # manifest 파일 템플릿 디렉토리
│   ├── NOTES.txt # 차트 사용법 등 참조 문서 템플릿
│   ├── _helper.tpl # manifest 렌더링에 사용되는 템플릿 헬퍼
│   └── example.yaml # 각종 K8S 리소스의 manifest 템플릿
├── Chart.yaml # 차트 정보가 정의 파일
└── values.yaml # 차트 기본값 value 파일

차트 설치시 values.yaml 에 override 할 값을 정의한 yaml 파일을 만들면 된다.

repository

  • local: 헬름 클라이언트가 설치된 로컬 리포, 로컬에서 생성한 패키지가 존재
  • stable: stable charts repo, 기본값이며 helm/charts 차트 사용 가능
  • incubator: stable 조건 미달 repo
    • helm repo add incubator https://kubernetes-charts-incubator.storage.googleapis.com/
  • helm search 로 차트 검색 가능

명령어

init

보통은 helm init
실서버에서는 helm init --service-account tiller --node-selectors system --history-max 10
업그레이드는 helm init --upgrade

  • –service-account: 틸러가 사용할 서비스계정
  • –node-selectors: 틸러를 배포할 노드 레이블
  • –upgrade: 틸러 업그레이드
  • –history-max: 리소스 하나당 유지할 최대 히스토리 수

create

helm create 차트명

package

helm package 차트명
차트를 압축파일로 패키징

  • –version: 차트 버전 지정
    • 없을 경우 차트의 버전을 따른다.

helm search 검색어

  • -r, --regexp: 검색어를 정규표현식으로 사용
  • -l, --versions: 버전 목록도 출력

fetch

helm fetch 차트경로

  • –version: 특정 버전 지정

serve

helm serve
로컬 레포지토리로 사용할 웹 서버 시작

  • –address: 서버가 개방할 주소
    • 기본값은 127.0.0.1:8879
  • –repo-path: 차트 레포지토리가 될 로컬 디렉토리

install

helm install 차트명
차트로 애플리케이션 설치

1
2
3
4
5
6
helm install stable/redis --name my-redis

helm install stable/redis --version 3.6.0 \
--name my-redis \
--namespace gracefullight \
-f ./my-redis.yaml
  • –dry-run
  • –name: 릴리즈명
  • –namespace: 설치 대상 네임스페이스
  • -f, --values: yaml 파일 경로 (다수 가능)
  • –version: 차트 버전 지정

list

helm list
helm list --namespace kube-system

  • –deleted: 삭제된 릴리즈 포함
  • –namespace: 해당 네임스페이스만 확인

get

helm get 릴리즈명
설치된 릴리스 상ㅇ세 정보를 yaml 으로 출력

  • –revision: 릴리즈 리비전 확인

delete

helm delete 릴리즈명

  • –purge: 릴리즈 삭제하고 릴리즈명 해제

앞서

쿠버네티스를 이해하는 건 어렵다. DevOps 의 전반적인 플로우를 알아야 이 플로우도 이해가 가기 때문이라 생각한다.
생소한 용어들도 어렵다. 한 판을 정리 했지만 매번 참조해야되는 듯하다.

오브젝트 비교

개념JAVA 클래스쿠버네티스 오브젝트
캡슐화ClassContainer image
인스턴스ObjectContainer
재사용단위jarContainer image
컴포지션Class A Contains Class BSidecar pattern
상속Class A extends Class BA container’s from parent image
배포단위jar, war…Pod
빌드 및 런타임 아이솔레이션Module, Package, ClassNamespace, Pod, Container
초기화ConsturctorInit container
초기화 후 트리거Init methodpostStart
종료 전 트리거DestroypreStop
Cleanupfinalize(), shutdown hookDefer container
비동기, 병렬 실행ThreadPoolExecutor, ForkJoinPoolJob
스케쥴링Timer, ScheduledExecutorServiceCronJob
백그라운드Daemon threadDaemonSet
설정관리System.getenv(), PropertiesConfigMap, Secret

여담

  • 개념 설정 전에 이 표를 알았더라면 훨씬 이해가 쉬웠을텐데

개념

Docker 이미지로 생성된 후에 그 위에서 돌아간다.
자세한 개념은 시간날 때 추가 예정

레파지토리 토큰 발급

여기를 참조해 레파지토리의 secret 으로 등록한다.

서브모듈

themes directory 하위의 테마들은 각각의 repo를 가지고 있다.
이 테마들을 CI 중에 가져오기 위해선 서브모듈로 등록해주고 초기화시켜줘야한다.

1
2
3
4
5
# 서브모듈 테마 추가
git submodule add 테마깃경로 themes/테마명

# 싱크
git submodule update --init --remote

여기서 remote 옵션을 쓰지 않을 경우 최신 마스터를 pull 하지 않는다. 서브모듈을 쓰는 이유는 내가 관리하지 않기 위함이니 꼭 추가해주자.

소스

주석 없어도 하나하나가 무슨 느낌인지는 받아들여질 것 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
name: Node CI

on:
push:
branches:
- master

jobs:
build:
runs-on: ubuntu-latest

strategy:
matrix:
node: [12]

steps:
- uses: actions/checkout@master

- name: SETUP_NODE_${{ matrix.node }}
uses: actions/setup-node@master
with:
node-version: ${{ matrix.node }}

- name: BEFORE_INSTALL
run: npm i -g hexo workbox-cli

- name: BEFORE_SCRIPT
run: |
git config --global user.name 'gracefullight'
git config --global user.email 'gracefullight.dev@gmail.com'
sed -i "s/__GITHUB_TOKEN__/${{ secrets.HEXO_DEPLOY_TOKEN }}/" _config.yml

- name: THEME_INSTALL
run: |
git submodule update --init --remote --recursive

- name: NPM_INSTALL
run: npm install

- name: HEXO_CLEAN
run: hexo clean

- name: HEXO_GENERATE
run: hexo generate

- name: WORKBOX_BUILD
run: workbox injectManifest

- name: HEXO_DEPLOY
run: hexo deploy

GITHUB_TOKEN

secrets.GITHUB_TOKEN 은 예약된 토큰이다.
빌드 이미지에서 현재 repo를 접근하기 위한 토큰임을 알아두자.

결론

travis-ci 안녕

참조

  • Workflow syntax
  • Virtual env

페이지를 가져온 뒤 css, image, font를 차단하면 더 빠른 DOM 액세스가 가능하다.

리소스 차단

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// @types/puppeteer
import { launch, Browser, Request, Page } from 'puppeteer';

const browser: Browser = await launch({
headless: true,
});

const page: Page = await browser.newPage();
await page.setRequestInterception(true);

page.on('request', (req: Request) => {
switch (req.resourceType()) {
case 'stylesheet':
case 'font':
case 'image':
req.abort();
break;
default:
req.continue();
break;
}
});

await page.goto('URL');

발단

서비스워커는 n:m (사이트:디바이스)로 모든 클라이언트 기기에 설치된다.
배포 후 아무 문제 없이 동작하였으나, 190604 이후 웹뷰에서 net::ERR_ABORTED 페이지가 보인다는 버그가 들어오기 시작했다.
랜덤하게 발생되고 있어 추적이 어려웠으나 4일에 Chrome의 메이저 버전이 업데이트 되었다라는 것을 확인했고 디버깅을 시작했다.

디버깅

서비스워커가 내려오지 않은 경우 페이지가 100% 정상동작을 하였다.
서비스워커가 내려온 경우 랜덤하게 페이지가 로드되지 않았다.

문제는 서비스워커로 확인되었고, 세부적인 디버깅 내역은 다음과 같다.

  • fetchListener 내부 cacheStorage 접근 예외처리: 재현
  • fetchListener 제거: 재현 안됨

Chrome 75버전의 웹뷰에서 서비스워커의 fetching 방식이 변경되었다는 걸 확인할 수 있었다.
74, 75버전의 Diff를 찾을 수 있어 서비스워커 코어가 어마어마하게 변경되었다는 걸 확인할 수 있었으나 이 코드를 디버깅하는 것보다 퇴사 후 행복하게 사는 게 멋질 것이란 판단이 들었다.
다행히 구글러와 연락이 닿아 private 버그리포팅을 했고, Chrome은 오픈소스라 구글에서도 일일히 확인하기 힘들다라는 답변을 들을 수 있었다.

웹뷰와 서비스워커

웹뷰의 서비스워커와 브라우저의 서비스워커는 다른 인스턴스이므로 서로 공유되지 않는다.
그렇다면 웹뷰에서 재접속시에 앱 셸을 빠르게 로드하는 이점 뿐이라는 말이다. === 어드벤티지 없음

WebView UA in Lollipop and Above에 따르면 안드로이드 롤리팝 이후부터 User Agent에 wv란 값을 물고 들어온다.

해결

예외처리

Chrome 엔진이 업데이트 된다고 서비스워커가 제거되지 않는다.
따라서 UA 에 android, wv 값이 있는 경우 서비스워커를 설치하지 않을 뿐 아니라 설치된 서비스워커를 제거해주는 로직이 있어야한다.
아니라면 A way to immediately unregister a service worker 기능을 브라우저 벤더들이 개발해줘야한다.

더 특정한 버전을 줘서 예외처리를 한다면 다음과 같을 것이다.

Chrome 75.0.3770.67 ~ 75.0.3770.101 버전의 모든 안드로이드 웹뷰에서 서비스워커 설치를 차단, 이미 설치가 되어있다면 삭제

패치

몇일 뒤에 다른 업체에서 public 하게 버그리포팅을 올렸고 버그를 찾아서 조만간 패치될 예정이다.

This is affecting tens of thousands of our readers 로 보아 나와 같은 빡침이 느껴져서 아련했다.

패치된 코드는 여기서 볼 수 있다.

여담

  • 브라우저에 버그 발생시 대처하는 방법은 거의 불가능하다.
  • 실수는 여기든 저기든 다 똑같구나

맥에서 구글 계정 연동 중에 Google 계정 인증에 실패했다는 오류메세지가 나온다면 다음과 같이 해결하면 된다.

해결

설정 > 일반 > 기본 웹브라우저Safari.app으로 변경한다.

구글링시 아래 기능들을 해제해보라는데 다 쓸모없다.

  • 2차 인증 해제
  • 앱 비밀번호 사용
  • 보안되지 않은 앱 허용

타임존 데이터는 php의 버전을 따라 올라가는데, 실무에선 항상 최신버전을 사용하기 쉽지 않다.
그럴 때 데이터만 업데이트하는 방법을 쓸 수 있다.

설치

2019.06 현재 최신 타임존 데이터베이스는 2019a (2019.01) 버전이다.

perl

perl로 설치되는 일반적인 경우는 아래와 같이 쉽게 설치 가능하다.

1
2
3
4
# perl 로 설치
$ perl install timezonedb

# 끝!

phpize

그렇지 않은 경우 라이브러리를 수동으로 빌드해줘야한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 타임존 데이터 다운로드
$ curl -LO https://pecl.php.net/get/timezonedb-2019.1.tgz

# 압축 해제
$ tar -xvzf timezonedb-2019.1.tgz
$ cd timezonedb-2019.1

# 빌드
$ phpize
$ ./configure --with-php-config=${PHP_CONFIG_PATH}
$ make && make install

# 라이브러리를 extensions 폴더로 이동
$ mv timezonedb.so /usr/local/php/extentions

# 라이브러리 추가
$ vi php.ini
$ extension=timezonedb.so

# 아파치 재시작
$ apachectl restart

# 버전 확인
$ /usr/bin/php -r "echo timezone_version_get();"
2019.01

참조

  • Compiling shared PECL extensions with phpize

usb 디버깅 승인 후 chrome://inspect#devices 탭에 접근하면 기기가 보이면서 디버깅이 가능해야하는데,
그렇지 못한 경우에 아래처럼 진행해주면 된다.

해결

먼저 usb 디버깅 권한을 모두 초기화하고 다시 연결한다.

1
2
3
# 디버깅 서버를 죽인 후 다시 실행한다.
$ adb kill-server
$ adb usb

개발자도구의 More tools > Remote devices의 목록에서 확인할 수 있다.

여담

  • PTP 연결을 하라는데 이건 안드로이드 구버전에 대한거라 필요 없다.
  • 웹뷰 디버깅시에는 디버깅 권한이 있는 앱이여야 가능하다.

매번 용어 찾아보는 게 귀찮아서 정리했다.
어떤 기능을 yaml 로 구성해야하는지 감이 오는 듯하다.

노드

  • 컨테이너가 배치되는 서버
  • 쿠버네티스 클러스터의 관리 대상으로 등록된 도커 호스트
  • 쿠버네티스 클러스터는 마스터와 노드의 그룹으로 구성
  • kubectl get nodes

마스터 노드

  • 스웜의 매니저노드와 비슷한 느낌이다.
  • kube-apiserver: 쿠버네티스 API를 노출하는 컴포넌트 kubectl로부터 리소스를 조작하라는 지시를 받음
  • ectd: 고가용성을 갖춘 분산 Key/Value 스토어로 쿠버네티스 클러스터의 백킹스토어로 쓰인다. (CAS)
  • kube-scheduler: 노드를 모니터링하고 컨테이너를 배치할 적절한 노드탐색
  • kube-controller-manager: 리소스를 제어하는 컨트롤러 실행

일반적인 (non-managed, GKE 등이 없는) 환경에선 마스터 노드서버가 단일 장애지점 (SPOF)가 되지 않도록 마스터를 3대 두는 것이 필수다.

네임스페이스

  • 쿠버네티스 클러스터 안의 가상 클러스터
  • 네임스페이스마다 권한을 줄 수 있으므로 개발팀이 클 때 유용하다.
  • 기본으론 default, docker, kube-public, kube-system 네임스페이스가 있다.
  • kubectl get namespace

파드

  • 컨테이너의 집합 중 가장 작은 단위로, 컨테이너의 실행 방법을 정의
  • 적어도 하나 이상의 컨테이너로 이뤄져있다.
    • nginx reverse proxy + application web server
  • 파드는 노드 안에 들어간다.
  • 함꼐 배포해야 정합성, 일관성이 유지된다면, 같은 파드로 묶어 배포한다.
  • 파드엔 각각 고유의 가상 IP가 할당된다.
  • kubectl get pod

레플리카세트

  • 같은 스펙을 갖는 파드를 여러 개 생성/관리 하는 역할
  • 똑같은 파드의 레플리케이션 개수를 관리하는 리소스이다.
  • 삭제된 파드는 복원할 수 없기 때문에 웹 어플리케이션과 같은 stateless 파드에 사용하기에 좋다.
  • kubectl get replicaset
  • 레플리카셋의 파드명은 container-1b2e3f 처럼 임의로 정해진다.

디플로이먼트

  • 레플리카세트의 리비전을 관리 (빠른 버저닝)
  • 리비전은 컨테이너 이미지가 수정된 경우에 생성된다.
  • kubectl rollout history deployment ..

데몬셋

  • 모든 시스템에서 실행되어야하는 파드와 템플릿 제공

서비스

  • 파드의 집합에 접근하기 위한 경로를 정의
  • 쿠버네티스 클러스터 내에서 파드의 집합에 대한 경로나 서비스 디스커버리를 제공하는 리소스
  • 서비스의 대상이 되는 파드는 서비스에서 정의하는 레이블 셀렉터로 정해진다.
  • 서비스의 접근은 같은 네임스페이스라면 서비스명만으로 접근이 가능하다.
    • http://${svc} => http://${svc}.default => http://${svc}.default.svc.local

ClusterIP 서비스

  • 서비스의 기본 타입
  • 쿠버네티스 클러스터의 내부 IP 주소에 서비스를 공개할 수 있다.
  • 다른 파드 그룹으로 접근할 때 서비스를 거쳐 가도록 할 수 있으며, 서비스명으로 네임 레졸루션이 가능해진다.
  • 외부에서는 접근할 수 없다.

NodePort 서비스

  • 쿠버네티스 클러스터 외부에서 접근할 수 있는 서비스
  • 서비스를 L4 레벨에서 노출하는 것이므로 TCP/UDP 모두 다룰 수 있다.
  • kubectl get svc ...

LoadBanlancer 서비스

  • 로컬 쿠버네티스 환경에선 사용할 수 없다.

ExternalName 서비스

  • 쿠버네티스 클러스터에서 외부 호스트를 네임 레졸루션하기 위한 별칭이다.
  • gracefullighut.dev 를 gracefullight 로 접근 가능하게 만든다.

인그레스

  • 서비스를 쿠버네티스 클러스터 외부로 노출
  • NodePort 서비스는 L4라 HTTP/S의 경로기반 라우팅이 불가능한데, 인그레스는 L7 레벨로 제어가 가능하다.
  • 주로 HTTP/S 서비스를 노출하는 경우 사용한다.
  • 로컬 쿠버네티스 환경에서는 인그레스를 사용해 서비스를 노출시킬 수 없으므로 nginx ingress controller 를 사용한다.

컨피그맵

  • 설정 정보를 정의 후 파드에 전달

퍼시스턴트볼륨

  • 파드가 사용할 스토리지의 크기 및 종류를 정의

퍼시스턴트볼륨 클레임

  • 퍼시스턴트 볼륨을 동적으로 확보

스토리지클래스

  • 퍼시스턴트 볼륨이 확보하는 스토리지의 종류를 정의

스테이트풀세트

  • 같은 스펙으로 모두 동일한 파드를 여러 개 생성하고 관리
  • 스테이트풀셋의 파드명은 backend-0, backend-1 처럼 인덱스로 정해진다.

  • 일회성 파드를 여러 개 생성하고 종료 보장

시크릿

  • 인증 정보 등 비밀 데이터 정의

  • 네임스페이스 안에서 조작 가능한 쿠버네티스 리소스의 규칙 정의
  • 각 쿠버네티스 API 사용 권한 정의
  • 지정된 네임스페이스 내에서만 유효

롤 바인딩

  • 쿠버네티스 리소스 사용자와 롤을 연결

RBAC

  • Role-based access control (RBAC) is a method of regulating access to computer or network resources based on the roles of individual users within an enterprise.
  • Docs

권한 디버깅

1
2
3
4
5
# can-i 문으로 권한 확인 가능
$ kubectl auth can-i ...

# kubectl auth can-i create deployments
# yes

클러스터롤

  • 클러스터 전체적으로 조작 가능한 쿠버네티스 리소스의 규칙 정의
  • 클러스터 전체에서 유효

클러스터롤 바인딩

  • 쿠버네티스 리소스 사용자와 클러스터롤을 연결
  • 일반 사용자 및 그룹/서비스 계정과 클러스터롤을 연결

서비스 계정

  • 파드가 쿠버네티스 리소스를 조작할 때 사용하는 계정
  • 구글의 서비스 계정 비슷한 듯

노드어피니티

  • 노드 셀렉터보다 더 세밀한 선택이 가능 (스케줄링 정책 조건 등)

테인트

  • taint
  • 오염 조건을 추가해 노드 스케줄링에서 제외
  • 파드를 특정 노드로 강제할 수 있다.

컨트롤 플레인

  • kubelet, kubenetes master 등 컨트롤 플레인은 시스템 내 모든 쿠버네티스 오브젝트의 레코드를 유지하면서, 오브젝트의 상태를 관리하는 제어 루프를 지속적으로 구동

Dynamic Admission Control

  • 웹훅으로 동작
  • ValidationWebhookConfiguration
  • MutatingWebhookConfiguration

클러스터 데몬

  • 클러스터 모든 서비스에 검사, 실행 필요시 사용

클러스터어시스턴스

  • SSL 인증 배포 등 자동화
  • cert-manager