반응형
.
├── config
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-39.pyc
│   │   ├── settings.cpython-39.pyc
│   │   ├── urls.cpython-39.pyc
│   │   ├── views.cpython-39.pyc
│   │   └── wsgi.cpython-39.pyc
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   ├── views.py
│   └── wsgi.py
├── db.sqlite3
├── firstapp
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-39.pyc
│   │   ├── urls.cpython-39.pyc
│   │   └── views.cpython-39.pyc
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   ├── urls.py
│   └── views.py
└── manage.py

위와 같이 django 디렉토리가 있을 때 package, module import 하는 방법이 헷갈려서 정리하는 차원이다.

 

바로 하위 디렉토리 config 와 firstapp 은 패키지 이다.

그리고 config 아래의 urls.py 와 같은 python 파일은 모듈이다.

 

import 방법은 다음과 같다.

 

 

import {패키지}.{모듈}

import {패키지}.{모듈}.{함수}

 

from {패지키} import {모듈}

from {패키지}.{모듈} import {함수}

 

 

 

 

 

반응형
Posted by seungkyua@gmail.com
,
반응형

Python 은 다양한 버전의 패키지 라이브러를 사용하여 개발한다. 그렇기 때문에 똑같은 패키지라도 서로 다른 버전을 사용한 프로그램이 있을 수 있어 버전 충돌이 일어날 수 있다.

결국 각 프로그램마다 고유한 개발 환경이 필요한데 그것이 바로 Virtual Envrionment 이다. 여기서는 Mac 에 python 가상 환경을 구축하는 방법을 설명한다.

 

1. Python 설치

 

mac 에서는 brew 로 프로그램 관리를 쉽게 할 수 있는데 brew 로 설치한 python 은 향후 다른 프로그램과 연계할 때 문제가 발생할 수 있어 수동으로 설치하기 위해 이전 brew 설치한 python 은 삭제한 후 아래와 같이 수동으로 다운 받아 설치한다.

(다운로드 후 설치) https://www.python.org/ftp/python/3.9.7/python-3.9.7-macos11.pkg

 

설치한 python 경로에 path 를 확인하고 없으면 추가해 준다. zsh 외에 bash shell 을 사용하면 ~/.bash_profile 을 확인하면 된다.

$ vi ~/.zshrc
export PATH="/Library/Frameworks/Python.framework/Versions/3.9/bin:${PATH}"

$ source ~/.zshrc

 

 

python 설치가 완료되면 아래와 같이 pip 을 설치한다.

$ wget https://bootstrap.pypa.io/get-pip.py
$ python3 get-pip.py

 

 

2. Python 가상 환경 프로그램 설치

 

python 가상 환경은 virtualenv 와 virtualenvwrapper 프로그램을 설치해서 만들 수 있다. 아래와 같이 2개의 툴을 설치한다.

$ sudo pip install virtualenv virtualenvwrapper

 

설치가 완료되면 ~/.zshrc 혹은 ~/.bash_profile 파일에 경로를 추가한다.

$ vi ~/.zshrc

alias python="/usr/local/bin/python3"

export VIRTUALENVWRAPPER_PYTHON=/usr/local/bin/python3
export VIRTUALENVWRAPPER_VIRTUALENV=/Library/Frameworks/Python.framework/Versions/3.9/bin/virtualenv
export WORKON_HOME=~/.virtualenvs
. /Library/Frameworks/Python.framework/Versions/3.9/bin/virtualenvwrapper.sh
export PATH="/Library/Frameworks/Python.framework/Versions/3.9/bin:${PATH}"

$ source ~/.zshrc

 

3. 가상 환경 활용

 

가상 환경은 아래 명령어로 생성할 수 있다. 마지막 python 옵션은 생략 가능하며 pandas-py3.6 이라는 이름으로 생성하였다.

$ mkvirtualenv pandas-py3.6 --python=python3

venv:(pandas-py3.6) $

 

prompt 가 “venv:...” 로 변경되어 가상환경에 들어와 있음을 알 수 있다.
생성한 가상 환경은 아래 디렉토리에 생성되고 앞으로 pip 으로 생성한 python 패키지 파일들은 ~/.virtualenvs/pandas-py3.6 아래에 설치된다.

venv:(pandas-py3.6) $ ls -al ~/.virtualenvs

drwxr-xr-x  21 ahnsk  staff   672  1 27 10:01 .
drwxr-xr-x+ 96 ahnsk  staff  3072  2 10 13:13 ..
-rwxr-xr-x   1 ahnsk  staff   135 11 13  2017 get_env_details
-rw-r--r--   1 ahnsk  staff    96 11 13  2017 initialize
drwxr-xr-x   8 ahnsk  staff   256  1 27 10:08 pandas-py3.6
-rw-r--r--   1 ahnsk  staff    73 11 13  2017 postactivate
-rw-r--r--   1 ahnsk  staff    75 11 13  2017 postdeactivate
-rwxr-xr-x   1 ahnsk  staff    66 11 13  2017 postmkproject
-rw-r--r--   1 ahnsk  staff    73 11 13  2017 postmkvirtualenv
-rwxr-xr-x   1 ahnsk  staff   110 11 13  2017 postrmvirtualenv
-rwxr-xr-x   1 ahnsk  staff    99 11 13  2017 preactivate
-rw-r--r--   1 ahnsk  staff    76 11 13  2017 predeactivate
-rwxr-xr-x   1 ahnsk  staff    91 11 13  2017 premkproject
-rwxr-xr-x   1 ahnsk  staff   130 11 13  2017 premkvirtualenv
-rwxr-xr-x   1 ahnsk  staff   111 11 13  2017 prermvirtualenv

 

가상 환경을 벗어나려면 deactivate 명령어로 빠져 나오면 된다.

venv:(pandas-py3.6) $ deactivate
$ 

 

가상 환경 목록을 확인하려면 lsvirtualenv 명령어로 할 수 있다.

$ lsvirtualenv

pandas-py3.6
============

 

해당 가상 환경으로 들어가려면 workon 명령어로 할 수 있다.

$ workon pandas-py3.6

venv:(pandas-py3.6) $  python
Python 3.9.7 (v3.9.7:1016ef3790, Aug 30 2021, 16:25:35)
[Clang 12.0.5 (clang-1205.0.22.11)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 
반응형
Posted by seungkyua@gmail.com
,
반응형

애플리케이션을 개발하면서 원하는 시점에 자동으로 Docker image 를 build 하고 이를 docker hub 저장소에 push 할 수 있는 방법을 소개한다. 보통은 이를 CI 로 구축하는데 CI 시스템을 따로 구축하기에는 시간과 비용이 들고 이를 유지보수 하기에도 귀찮은 면이 있다. 만약 Github 을 사용하고 있다면 10분 만에 Github Action 으로 이를 쉽게 구현할 수 있다.

 

1. Dockerfile 만들기

가장 먼저 해야할 일은 도커 이미지 빌드를 위한 Dockerfile 을 만드는 것이다. golang 으로 개발하고 있다면 아래와 같이 만들 수 있을 것이다. 어느 정도 최적화를 고려했지만 완전히 최적화한 부분은 아니니 자신의 환경에 맞게 변경해야 한다. Dockerfile 의 위치는 $PROJECT_HOME 디렉토리 바로 아래이다.

FROM golang:1.16.3-stretch AS builder
LABEL AUTHOR Seungkyu Ahn (seungkyua@gmail.com)

RUN mkdir -p /build
WORKDIR /build

COPY . .
RUN go mod tidy && go mod vendor
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/server ./cmd/server

RUN mkdir -p /dist
WORKDIR /dist
RUN cp /build/bin/server ./server


FROM golang:alpine3.13

RUN mkdir -p /app
WORKDIR /app

COPY --chown=0:0 --from=builder /dist /app/
EXPOSE 9111

ENTRYPOINT ["/app/server"]
CMD ["-port", "9110"]

 

2. Github Action 파일 만들기

Github Action 은 규칙에 맞게 yaml 을 파일은 정해진 위치에 넣으면 Github 에서 알아서 실행시켜 준다. Action 을 실행시키는 시점은 소스가 github 에 push 되는 순간이며, 어느 branch 에 push 되었는지, Pull Request 를 했을 때 등의 실행 조건을 정의 할 수 있다.

$PROJECT_HOME 디렉토리에서 다음과 같이 디렉토리를 만든다.

$ mkdir -p .github/workflows

 

github action yaml 은 아래와 같다.

$ vi .github/workflows/deploy-image.yml

name: Build and Push Docker Image
on:
  push:
    branches:
      - main
jobs:
  build-and-push-image:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout
      uses: actions/checkout@v2

    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v1

    - name: Login to DockerHub
      uses: docker/login-action@v1
      with:
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_TOKEN }}

    - name: Build and push
      id: docker_build
      uses: docker/build-push-action@v2
      with:
        push: true
        tags: seungkyua/tks-contract:latest

처음의 on 은 해당 action 이 수행될 조건을 의미한다. push - branches - main 은 main branch 에 push 가 일어나면 수행된다는 의미이다. 그렇기 때문에 요청된 PR 을 merge 하거나 main branch 에 직접 push 하면 해당 action 이 trigger 된다.

모든 branch 의 push 에 대해서 동작하게 하려면 '*' 로 하면 된다.

 

job 은 pipeline 을 구성하는 부분이다. steps 처음을 보면 "name: Checkout" 부분에 uses: actions/checkout@v2 로 되어 있는데 이는 github action 으로 정의된 모듈이다. 실체 소스는 https://github.com/actions/checkout 으로 접속하면 볼 수 있으며 해당 기능은 디폴트로 action 이 수행되는 github repository 의 소스 (현재 project 의 소스)를 내려받는 기능이다. 내려 받을 repository 를 바꿀려면 변경 가능하다. (action/checkout 에 접속하여 사용법을 확인하자)

 

"name: Set up Docker Buildx" 부분은 builder driver 나 platform 등을 세팅하는 부분이다. 역시 자세한 부분은 https://github.com/docker/setup-buildx-action 를 확인하자.

 

"name: Login to DockerHub" 는 docker hub 에 접근하기 위해서 id 와 password 를 지정하는 부분이다. 이를 위해서는 github repository 의 settings 로 들어가서 좌측 하단의 secrets >> Actions 에서 Action 용 secret 을 만들어야 한다.

 

 

 

 

 

 

 

docker hub 에 접속할 id 와 password 에 대한 secret 을 만들고 이를 github action 에서 ${{ secrets.DOCKERHUB_USERNAME }} 와 같이 변수로 가져다 쓰는 방식이다.

 

마지막으로 "name: Build and push" 부분은 docker image 를 build 하고 push 하는 부분이다. 이미지 name 과 tag 는 tags 로 지정했다. Dockerfile 의 위치 및 파일명은 file 키로 변경할 수 있다. 아무런 지정을 하지 않으면 해당 프로젝트 디렉토리에 있는 Dockerfile 을 찾는다. 자세한 내용은 https://github.com/docker/build-push-action 를 참조하자.

 

최종적으로 파일들은 아래와 같이 존재하게 된다.

 

 

 

보너스로 한가지 더.

 

main branch 로 merge 될 때 외에도 PR 을 할 때 docker image 를 build 하고 push 할 수 는 없을까? 이미지의 tag 는 latest 가 아니라 PR 을 요청한 source 의 branch 명으로 만들고 싶을 수 있다.

 

이럴 때는 새로운 파일을 만들고 github 의 내장 환경 변수를 사용하면 된다. 앞부분의 on 부분과 마지막 Build and push 부분의 tags 를 다음과 같이 수정하면 된다.

$ vi .github/workflows/deploy-image-with-branch.yml

name: Create and publish a package
on:
  pull_request:
    branches:
      - main
...
...
  - name: Build and push
      id: docker_build
      uses: docker/build-push-action@v2
      with:
        push: true
        tags: seungkyua/tks-contract:${{ github.head_ref }}
반응형
Posted by seungkyua@gmail.com
,
반응형

Golang 으로 개발된 애플리케이션을 Docker image 로 만드는 Dockerfile 이다.

이미지 사이즈를 줄이기 위해서 빌드 이미지와 실행 이미지를 구분하여 작성하였다.

 

실행 이미지를 더 작게 만들고 싶으면 base를 golang 이미지 대신 alphine 이미지로 만들면 된다.

 

한가지 tip 이라 하면 RUN go mod vendor 를 먼저 하고 나중에 COPY . . 를 하여 소스를 복사한 부분이다.

만약 소스 복사를 먼저하면 변경되 소스로 인해서 (하위 layer 의 변경) go mod vendor 로 다운로드 하는 부분을 수행하게 된다. 하지만 go mod vendor 를 먼저한 후 소스를 복사하면 go mod vendor 에서 수정된 부분이 없으면 해당 layer 를 재사용하기 때문에 매번 다운로드를 하지 않는다.  

 

# vi Dockerfile

FROM golang:1.16.3-stretch AS builder
LABEL AUTHOR Seungkyu Ahn (seungkyua@gmail.com)

RUN mkdir -p /build
WORKDIR /build

COPY go.mod .
COPY go.sum .
RUN go mod vendor
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/tksinfo ./cmd/server.go

RUN mkdir -p /dist
WORKDIR /dist
RUN cp /build/bin/tksinfo ./tksinfo




FROM golang:alpine3.13

RUN mkdir -p /app
WORKDIR /app

COPY --chown=0:0 --from=builder /dist /app/
EXPOSE 9111

ENTRYPOINT ["/app/tksinfo"]
CMD ["--port", "9111"]
반응형
Posted by seungkyua@gmail.com
,
반응형

2021년 4월 Golang 의 최신 버전은 1.16.x 입니다.

 

go 프로그램을 실행파일로 컴파일 하면 default 는 dynamic library 로 만들어 집니다.

 

아무리 OS 와 architecture 에 맞춰서 cross compile 을 했다고 해도 링크된 모듈이 존재하지 않으면 에러가 납니다.

특히 docker 로 실행할 경우 이런 문제를 자주 겪게 됩니다.

 

1.15 버전 이상에서는 CGO_ENABLED=0 환경 변수로 static library 로 만들 수 있습니다. 참고로 1.14 버전 이하에서는 -tags 옵션을 사용할 수 있습니다.

 

아래 Makefile 은 이 부분을 고려해서 만들었습니다.

GOOS는 OS 의 종류를 GOARCH 는 Architecture 에 맞춰 실행파일을 만듭니다.

 

$ vi Makefile

.PHONY: build clean docker

default: build

all: build docker

build: build-darwin build-linux

build-darwin:
	CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o bin/tksinfo-darwin-amd64 ./cmd/server.go
	CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o bin/tksinfo-appclient-darwin-amd64 ./examples/application/client.go

build-linux:
	CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/tksinfo-linux-amd64 ./cmd/server.go
	CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/tksinfo-appclient-linux-amd64 ./examples/application/client.go

clean:
	rm -rf ./bin

docker:
	docker build --no-cache -t seungkyua/tksinfo -f Dockerfile .
반응형
Posted by seungkyua@gmail.com
,
반응형

Kubernetes 는 GoLang 으로 만들어진 대표적인 s/w 입니다. Kubernetes  구조와 비슷하게 GoLang 으로 프로젝트를 만들 때 사용되는 일반적인 디렉토리 구조를 설명하겠습니다.

 

제일 먼저 GOPATH 를 지정하고, bin 디렉토리를 PATH에 추가로 지정합니다.

$ GOPATH=/Users/ahnsk/Documents/go_workspace
$ PATH=/Users/ahnsk/Documents/go_workspace/bin:$PATH

 

다음은 go get 으로 govendor 를 설치합니다. govendor 는 dependency module 을 쉽게 다운받고 관리할 수 있습니다.

$ go get -u github.com/kardianos/govendor
$ govendor -version
v1.0.9

 

이제 본인이 생성할 프로젝트를 만들어 보겠습니다. github.com 에 있는 go 프로젝트를 다운로드 받습니다.

$ mkdir -p /Users/ahnsk/Documents/go_workspace/cookiemonster2
$ GOPATH=/Users/ahnsk/Documents/go_workspace/cookiemonster2
$ go get -u github.com/seungkyua/cookiemonster2

GOPATH 는 각 프로젝트마다 자신만의 용도로 사용하기 위해 각각 지정하는 것이 편리합니다.

 

cookiemonster2 라는 프로젝트는 GOPATH 아래의 src 디렉토리에 패키지 경로(github.com/seungkyua/cookiemonster2)로 다운로드 됩니다.

/Users/ahnsk/Documents/go_workspace/cookiemonster2/src/
github.com/seungkyua/cookiemonster2

 

프로젝트 디렉토리로 이동해서 보면 다음과 같습니다.

$ tree -L 2 .
.
├── Dockerfile.cookiemonster
├── LICENSE
├── Makefile
├── README.md
├── cmd
│   ├── cluster_client.go
│   ├── cluster_client2.go
│   └── server.go
├── config
│   └── config.yaml
├── manifest
│   ├── cookiemonster-cm-config.yaml
│   ├── cookiemonster-deployment.yaml
│   ├── cookiemonster-rbac.yaml
│   └── cookiemonster-service.yaml
├── pkg
│   ├── domain
│   └── handler
└── vendor
    └── ...

 

일반적으로 entrypoint 가 되는 go 파일은 cmd 디렉토리 아래에 위치하고 나머지 go 파일들은 pkg 디렉토리에 패키지 구조로 위치합니다.

vendor 디렉토리는 dependency module 을 다운받은 곳이므로 이 디렉토리는 삭제를 하고, 처음부터 새롭게 구성해 보겠습니다.

$ rm -rf vendor

 

govendor init 으로 vendor 디렉토리를 생성합니다.  새롭게 생성된 vendor 디렉토리에 vendor.json 파일이 보이는데 이것은 향후 dependency module 을 다운 받을 때 활용됩니다.

$ govendor init

$ tree -L 2 .
.
├── Dockerfile.cookiemonster
├── LICENSE
├── Makefile
├── README.md
├── cmd
│   ├── cluster_client.go
│   ├── cluster_client2.go
│   └── server.go
├── config
│   └── config.yaml
├── manifest
│   ├── cookiemonster-cm-config.yaml
│   ├── cookiemonster-deployment.yaml
│   ├── cookiemonster-rbac.yaml
│   └── cookiemonster-service.yaml
├── pkg
│   ├── domain
│   └── handler
└── vendor
    └── vendor.json

 

vendor 디렉토리 밑으로 디펜던시 module 을 다운로드 받습니다.

$ govendor get github.com/seungkyua/cookiemonster2

$ tree -L 2 .
.
├── Dockerfile.cookiemonster
├── LICENSE
├── Makefile
├── README.md
├── cmd
│   ├── cluster_client.go
│   ├── cluster_client2.go
│   └── server.go
├── config
│   └── config.yaml
├── manifest
│   ├── cookiemonster-cm-config.yaml
│   ├── cookiemonster-deployment.yaml
│   ├── cookiemonster-rbac.yaml
│   └── cookiemonster-service.yaml
├── pkg
│   ├── domain
│   └── handler
└── vendor
    ├── appengine
    ├── appengine_internal
    ├── github.com
    ├── golang.org
    ├── google.golang.org
    ├── gopkg.in
    ├── k8s.io
    ├── sigs.k8s.io
    └── vendor.json

 

vendor 디렉토리에 dependency module 의 특정버전까지 다운 받을 수 있으니 관리가 편리해 집니다. 

현재 버전에서 labstack 의 echo v4 모듈에 버그가 있어 v3 로 다시 다운받기 위해 해당 디렉토리를 지우고 다시 다운 받을 수 있습니다. @v3 는 v3  이상의 최신 버전에 해당하는 git 브랜치나 태그를 다운받으라는 의미입니다.

$ rm -rf vendor/github.com/labstack/echo

$ govendor get github.com/labstack/echo@v3

 

여기서 govendor 의 버그로 echo 는 다운받았지만 하위 디렉토리인 echo/middleware 는 다운이 안되었으므로 추가로 vendor.json 을 수정하여 다운로드 받습니다.

$ vi vendor/vendor.json
{
   "checksumSHA1": "ynPXfBgVKquHSKkdFWk3oqSYf+g=",
   "path": "github.com/labstack/echo",
   "revision": "38772c686c76b501f94bd6cd5b77f5842e93b559",
   "revisionTime": "2019-01-28T14:12:53Z",
   "version": "v3.3.10",
   "versionExact": "v3.3.10"
},
{
   "checksumSHA1": "Rp/k+BJKpaeB9vyjEPFBW4LeFP8=",
   "path": "github.com/labstack/echo/middleware",
   "revision": "38772c686c76b501f94bd6cd5b77f5842e93b559",
   "revisionTime": "2019-01-28T14:12:53Z",
   "version": "v3.3.10",
   "versionExact": "v3.3.10"
}

$ govendor fetch github.com/labstack/echo/middleware

 

 

 

 

 

 

 

 

 

 

 

반응형
Posted by seungkyua@gmail.com
,
반응형

Kubespray 와 같이 많은 role 을 가진 ansible playbook 에서는 선언된 변수와 실제 할당된 값을 살펴보는 것이 쉽지 않습니다. ansible 에서는 이러한 변수들에 대한 값, 특히 hostvar 값을 볼 수 가 있습니다.

일단 먼저 inventory 파일인 hosts.ini 로 어떤 host 와 group 이 지정되었는지 확인할 수 있습니다.

$ vi inventory/k1-seungkyua/hosts.ini
[all]
k1-master01 ip=192.168.30.13
k1-master02 ip=192.168.30.14
k1-master03 ip=192.168.30.15
k1-node01 ip=192.168.30.12
k1-node02 ip=192.168.30.17
k1-node03 ip=192.168.30.18
k1-node04 ip=192.168.30.21
k1-node05 ip=192.168.30.20

[etcd]
k1-master01
k1-master02
k1-master03

[kube-master]
k1-master01
k1-master02
k1-master03

[kube-node]
k1-node01
k1-node02
k1-node03
k1-node04
k1-node05

[k8s-cluster:children]
kube-master
kube-node

다양한 그룹이 있음을 볼 수 있는데 맨 마지막의 k8s-cluster 그룹은 하위로 kube-master 와 kube-node 를 가지고 있으므로, k8s-cluster 그룹이 모든 노드를 포함한다는 것을 알 수 있습니다.

이제 ansible 명령어로 하나의 host 노드 "k1-master01" 에 대해 적용된 변수를 볼 수 있습니다.
 
$ ansible -i inventory/k1-seungkyua/hosts.ini -m debug -a "var=hostvars['k1-master01']" k8s-cluster

이에 대한 결과는 다음과 같습니다.

k1-node01 | SUCCESS => {
    "hostvars['k1-master01']": {
        "admin_token": "QVFBTmRVVlh5UmZoTlJBQTMyZTh6Qk5uajV1VElrMDJEbWFwWmc9PQ==",
        "ansible_all_ipv4_addresses": [
            "192.168.230.13",
            "192.168.30.13",
            "172.16.31.64",
            "172.17.0.1",
            "10.233.0.1",
            "10.233.0.3",
            "10.233.55.72",
            "10.233.18.191"
        ],
        "ansible_all_ipv6_addresses": [
            "fe80::3ea8:2aff:fe1c:cfd4",
            "fe80::5eb9:1ff:fe8c:67dc",
            "fe80::42:98ff:fec4:7ad1",
            "fe80::ecee:eeff:feee:eeee",
            "fe80::ecee:eeff:feee:eeee"
        ],
        "ansible_apparmor": {
            "status": "enabled"
        },
        "ansible_architecture": "x86_64",
        "ansible_bios_date": "05/06/2015",
        "ansible_bios_version": "P89",
        "ansible_cali1845c564081": {
            "active": true,
            "device": "cali1845c564081",
            "features": {
                "busy_poll": "off [fixed]",
                "fcoe_mtu": "off [fixed]",
                "generic_receive_offload": "on",
                "generic_segmentation_offload": "on",
                "highdma": "on",
                "hw_tc_offload": "off [fixed]",
                "l2_fwd_offload": "off [fixed]",
                "large_receive_offload": "off [fixed]",
                "loopback": "off [fixed]",
                "netns_local": "off [fixed]",
                "ntuple_filters": "off [fixed]",
                "receive_hashing": "off [fixed]",
                "rx_all": "off [fixed]",
                "rx_checksumming": "on",
                "rx_fcs": "off [fixed]",
                "rx_vlan_filter": "off [fixed]",
                "rx_vlan_offload": "on",
                "rx_vlan_stag_filter": "off [fixed]",
                "rx_vlan_stag_hw_parse": "on",
                "scatter_gather": "on",
                "tcp_segmentation_offload": "on",
                "tx_checksum_fcoe_crc": "off [fixed]",
                "tx_checksum_ip_generic": "on",
                "tx_checksum_ipv4": "off [fixed]",
                "tx_checksum_ipv6": "off [fixed]",
                "tx_checksum_sctp": "off [fixed]",
                "tx_checksumming": "on",
                "tx_fcoe_segmentation": "off [fixed]",
                "tx_gre_segmentation": "on",
                "tx_gso_robust": "off [fixed]",
                "tx_ipip_segmentation": "on",
                "tx_lockless": "on [fixed]",
                "tx_nocache_copy": "off",
                "tx_scatter_gather": "on",
                "tx_scatter_gather_fraglist": "on",
                "tx_sit_segmentation": "on",
                "tx_tcp6_segmentation": "on",
                "tx_tcp_ecn_segmentation": "on",
                "tx_tcp_segmentation": "on",
                "tx_udp_tnl_segmentation": "on",
                "tx_vlan_offload": "on",
                "tx_vlan_stag_hw_insert": "on",
                "udp_fragmentation_offload": "on",
                "vlan_challenged": "off [fixed]"
            },
            "hw_timestamp_filters": [],
            "ipv6": [
                {
                    "address": "fe80::ecee:eeff:feee:eeee",
                    "prefix": "64",
                    "scope": "link"
                }
            ],
            "macaddress": "ee:ee:ee:ee:ee:ee",
            "mtu": 1500,
            "promisc": false,
            "speed": 10000,
            "timestamping": [
                "rx_software",
                "software"
            ],
            "type": "ether"
        },
        "ansible_uptime_seconds": 46351496,
        "ansible_user_dir": "/root",
        "ansible_user_gecos": "root",
        "ansible_user_gid": 0,
        "ansible_user_id": "root",
        "ansible_user_shell": "/bin/bash",
        "ansible_user_uid": 0,
        "ansible_userspace_architecture": "x86_64",
        "ansible_userspace_bits": "64",
        "ansible_verbosity": 0,
        "ansible_version": {
            "full": "2.7.7",
            "major": 2,
            "minor": 7,
            "revision": 7,
            "string": "2.7.7"
        },
        "ansible_virtualization_role": "host",
        "ansible_virtualization_type": "kvm",
        "calico_ip_auto_method": "can-reach=8.8.8.8",
        "ceph_version": "mimic",
        "dashboard_enabled": true,
        "docker_insecure_registries": [
            "tacorepo:5000"
        ],
        "gather_subset": [
            "all"
        ],
        "global_as_num": "65000",
        "group_names": [
            "etcd",
            "k8s-cluster",
            "kube-master"
        ],
        "groups": {
            "all": [
                "k1-master01",
                "k1-master02",
                "k1-master03",
                "k1-node01",
                "k1-node02",
                "k1-node03",
                "k1-node04",
                "k1-node05"
            ],
            "etcd": [
                "k1-master01",
                "k1-master02",
                "k1-master03"
            ],
            "k8s-cluster": [
                "k1-node01",
                "k1-node02",
                "k1-node03",
                "k1-node04",
                "k1-node05",
                "k1-master01",
                "k1-master02",
                "k1-master03"
            ],
            "kube-master": [
                "k1-master01",
                "k1-master02",
                "k1-master03"
            ],
            "kube-node": [
                "k1-node01",
                "k1-node02",
                "k1-node03",
                "k1-node04",
                "k1-node05"
            ],
            "ungrouped": []
        },
        "helm_enabled": true,
        "ingress_nginx_enabled": true,
        "ingress_nginx_host_network": true,
        "ingress_nginx_node_count": 1,
        "ingress_nginx_nodeselector": {
            "node-role.kubernetes.io/ingress": "true"
        },
        "inventory_dir": "/home/seungkyua/deploy/my-kubespray/inventory/k1-seungkyua",
        "inventory_file": "/home/seungkyua/deploy/my-kubespray/inventory/k1-seungkyua/hosts.ini",
        "inventory_hostname": "k1-master01",
        "inventory_hostname_short": "k1-master01",
        "ip": "192.168.30.13",
        "ipip_mode": "Never",
        "kubeadm_enabled": true,
        "kubeconfig_localhost": true,
        "kubectl_localhost": true,
        "local_volume_provisioner_enabled": true,
        "module_setup": true,
        "monitors": "192.168.30.23:6789,192.168.30.24:6789,192.168.30.25:6789",
        "nat_outgoing": true,
        "omit": "__omit_place_holder__4491a572b322dda2550f5ce4d8005946f456f738",
        "override_system_hostname": false,
        "peer_with_router": true,
        "peers": [
            {
                "as": "65000",
                "router_id": "192.168.30.1"
            }
        ],
        "playbook_dir": "/home/seungkyua/deploy/my-kubespray",
        "pool_name": "kubes",
        "populate_inventory_to_hosts_file": false,
        "storageclass_name": "rbd",
        "user_id": "kube",
        "user_secret_namespace": "openstack",
        "user_token": "QVFDdC9CcFlpZ0o3TVJBQTV2eStjbDM5RXNLcFkzQyt0WEVHckE9PQ=="
    }
}




반응형
Posted by seungkyua@gmail.com
,
반응형
변수의 key 값을 가져오기 위해서는 regex_replace 함수를 사용하여 substring 을 할 수 도 있지만 set_fact 를 이용할 수 도 있습니다.

아래 내용은 set_fact 를 활용한 내용입니다.

예제는 ingress controller 가 실행될 node label 을 세팅하기 위한 방법입니다.
node label 은 다음과 같은 명령어로 세팅할 수 있습니다.
$ kubectl label --overwrite node k2-ctrl01 node-role.kubernetes.io/ingress=true


먼저 변수를 세팅하는 defaults/main.yml 파일은 다음과 같습니다.
ingress_nginx_nodeselector:
  node-role.kubernetes.io/ingress: "true"
ingress_nginx_node_count: 1

ingress_nginx_nodeselector 의 dict 값은 node-role.kubernetes.io/ingress: "true" 인데 node-role.kubernetes.io 는 key 값으로 이 값이 변경될 수 있으므로 key 를 사용하기에는 어렵습니다.

key 값을 유동적으로 활용하기 위해서는 set_fact 를 활용합니다.
- name: Set ingress nginx node role
  set_fact:
    ingress_nginx_node_role:
      key: "{{ item.key }}"
      value: "{{ item.value }}"
  with_dict: "{{ ingress_nginx_nodeselector }}"
  when:
    - inventory_hostname == groups['kube-master'][0]

위 태스크가 실행되면 ingress_nginx_node_role 변수에 {"key" : "node-role.kubernetes.io/ingress", "value" : "true"} 라고 저장됩니다.
그럼 이제 쉽게 ingress_nginx_node_role.key 로 해당 값을 가져올 수 있습니다.

- name: Set ingress nginx node list
  set_fact:
    ingress_nginx_node_list:
      - "{{ groups['kube-node'][item|int] }}"
  with_sequence: start=0 end={{ (ingress_nginx_node_count-1)|int }}
  when:
    - inventory_hostname == groups['kube-master'][0]

- name: NGINX Ingress Controller | Set node label
  shell: |
    {{ bin_dir }}/kubectl label --overwrite node {{ item }} {{ ingress_nginx_node_role.key }}={{ ingress_nginx_node_role.value }}
  loop: "{{ ingress_nginx_node_list }}"
  when:
    - inventory_hostname == groups['kube-master'][0]

label 을 세팅하기 전에 node 를 선택해야 하기 때문에 node 를 선택하는 "Set ingress nginx node list" 태스크를 먼저 수행했고 "NGINX Ingress Controller | Set node label" 태스크가 node label 을 지정하는 명령어 입니다.



 




반응형
Posted by seungkyua@gmail.com
,
반응형

전쳬 에제 소스

https://github.com/seungkyua/hello-go


Go 프로그램에서 channel 을 사용할 때 가장 중요한 부분은 channel 을 한 번만 닫아야 한다는 것이다. 그렇지 않고 닫힌 channel 을 중복해서 닫으려고 하면 panic 이 발생한다. 그래서 Discovery Go 책에서는 channel 에 값을 보내는 쪽에서 닫는 패턴을 추천한다.


프로젝트로 완성하는 Go 프로그래밍(Go Programming Blueprints Second Edition) 의 chat 예제를 보면 client 가 종료되어도 client 가 가지고 있는 채널을 닫지 않는 에러가 있어 이를 간단히 수정했다.


client.go 의 client struct 에 필요없는 room 변수는 삭제했다. (나중에 필요하면 추가할지도)

대신 read 메소드에 root 의 forward channel 을 인자로 받는다. 

type client struct {
socket *websocket.Conn
send chan []byte
}


read 메소드에는 socket close 하는 부분이 있는데 socket 에러가 나면 read 가 에러가 날 것이고 이 때 defer 로 channel 닫는 부분을 추가한다. write 메소드에는 중복을 피하기 위해서 channel 닫는 부분을 넣지 않았다.


func (c *client) read(forward chan<- []byte) {
defer func() {
c.socket.Close()
log.Println("Client socker read closed.")
close(c.send)
log.Println("Client send channel closed.")
}()
for {
_, msg, err := c.socket.ReadMessage()
if err != nil {
log.Println("Client Socket ReadMaessage error")
return
}
forward <- msg
}
}

 

 root.go 에서는 쓰이고 있지는 않지만 client 에 leave 할 경우 기존 client channel 닫는 소스 대신 client socket 을 닫는 부분으로 변경했다. range 로 모든 client channel 에 값을 보내고 있기 때문에 여기는 보내는 channel 이지만 channel 을 닫기가 어렵다. 그래서 client 에서 channel 을 닫는 로직을 추가했다.

 

root struct 에서 생성한 forward, join, leave channel 은 처리하지 않았다. 나중에 room 에 client 가 아무도 없을 때의 로직에 추가되면 그 때 닫는 부분을 추가해야 한다.


func (r *room) run() {
go func() {
for {
select {
case client := <-r.join:
r.clients[client] = true
case client := <-r.leave:
delete(r.clients, client)
client.socket.Close()
case msg := <-r.forward:
for client := range r.clients {
client.send <- msg
}
}
}
}()
}








반응형
Posted by seungkyua@gmail.com
,
반응형

10.0.0.171    kafka01 zookeeper01

10.0.0.172    kafka02 zookeeper02

10.0.0.173    kafka03 zookeeper03


192.168.30.171    kafka01 zookeeper01

192.168.30.172    kafka02 zookeeper02

192.168.30.173    kafka03 zookeeper03


## kafka vm 생성

$ openstack flavor create --id ka1 --ram 8192 --disk 160 --vcpus 2 kafka


$ openstack server create --image 7498cf9d-bd2e-4401-9ae9-ca72120272ed \

       --flavor ka1  --nic net-id=03a6de58-9693-4c41-9577-9307c8750141,v4-fixed-ip=10.0.0.171 \

       --key-name magnum-key --security-group default kafka01

$ openstack ip floating create --floating-ip-address 192.168.30.171 public

$ openstack ip floating add 192.168.30.171 kafka01





## Oracle Java 8 설치

$ sudo add-apt-repository ppa:webupd8team/java

$ sudo apt-get update

$ sudo apt-get install oracle-java8-installer


## 여러 버전을 Java 를 설치했을 때 관리

$ sudo update-alternatives --config java





## zookeeper 설치

## https://zookeeper.apache.org/doc/r3.4.9/zookeeperStarted.html

## http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-3.4.9/


$ mkdir -p downloads && cd downloads

$ wget http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-3.4.9/zookeeper-3.4.9.tar.gz


$ sudo tar -C /usr/local -xzvf zookeeper-3.4.9.tar.gz

$ cd /usr/local

$ sudo ln -s zookeeper-3.4.9/ zookeeper


$ vi /usr/local/zookeeper/conf/zoo.cfg

tickTime=2000

dataDir=/var/lib/zookeeper

clientPort=2181

initLimit=5

syncLimit=2

server.1=zookeeper01:2888:3888

server.2=zookeeper02:2888:3888

server.3=zookeeper03:2888:3888


$ vi /usr/local/zookeeper/bin/zkEnv.sh

56     ZOO_LOG_DIR="/var/log/zookeeper"


$ sudo mkdir -p /var/log/zookeeper && sudo chown -R stack.stack /var/log/zookeeper


## zookeeper myid 는 서버마다 지정

$ sudo mkdir -p /var/lib/zookeeper && sudo chown -R stack.stack /var/lib/zookeeper

$ vi /var/lib/zookeeper/myid

1


$ vi ~/.bashrc

export JAVA_HOME=/usr/lib/jvm/java-8-oracle

export ZOOKEEPER_HOME=/usr/local/zookeeper

PATH=$PATH:$ZOOKEEPER_HOME/bin


$ . ~/.bashrc

$ zkServer.sh start


## zookeeper 설치 확인

$ zkCli.sh -server zookeeper01:2181





## Kafka 설치

## https://www.digitalocean.com/community/tutorials/how-to-install-apache-kafka-on-ubuntu-14-04


## https://kafka.apache.org/downloads.html

## https://kafka.apache.org/documentation.html


$ cd downloads

$ wget http://apache.mirror.cdnetworks.com/kafka/0.10.0.1/kafka_2.11-0.10.0.1.tgz

$ sudo tar -C /usr/local -xzvf kafka_2.11-0.10.0.1.tgz

$ cd /usr/local && sudo chown -R stack.stack kafka_2.11-0.10.0.1

$ sudo ln -s kafka_2.11-0.10.0.1/ kafka


## broker id 는 서버마다 고유하게 줘야 함

$ vi /usr/local/kafka/config/server.properties

20 broker.id=0

56 log.dirs=/var/lib/kafka

112 zookeeper.connect=zookeeper01:2181,zookeeper02:2181,zookeeper03:2181

117 delete.topic.enable = true


$ sudo mkdir -p /var/lib/kafka && sudo chown -R stack.stack /var/lib/kafka

$ sudo mkdir -p /var/log/kafka && sudo chown -R stack.stack /var/log/kafka


$ vi ~/.bashrc

export KAFKA_HOME=/usr/local/kafka

PATH=$PATH:$KAFKA_HOME/bin


$ . ~/.bashrc

$ nohup kafka-server-start.sh $KAFKA_HOME/config/server.properties > /var/log/kafka/kafka.log 2>&1 &


## kafkaT : kafka cluster 관리

$ sudo apt-get -y install ruby ruby-dev build-essential

$ sudo gem install kafkat --source https://rubygems.org --no-ri --no-rdoc

$ vi ~/.kafkatcfg

{

  "kafka_path": "/usr/local/kafka",

  "log_path": "/var/lib/kafka",

  "zk_path": "zookeeper01:2181,zookeeper02:2181,zookeeper03:2181"

}


## kafka partition 보기

$ kafkat partitions


## kafka data 테스트

$ echo "Hello, World" | kafka-console-producer.sh --broker-list kafka01:9092,kafka02:9092,kafka03:9092 --topic TutorialTopic > /dev/null

$ kafka-console-consumer.sh --zookeeper zookeeper01:2181,zookeeper02:2181,zookeeper03:2181 --topic TutorialTopic --from-beginning


$ kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test

$ kafka-topics.sh --list --zookeeper localhost:2181


$ kafka-console-producer.sh --broker-list localhost:9092 --topic test

This is a message

This is another message


$ kafka-console-consumer.sh --zookeeper localhost:2181 --topic test --from-beginning


## Replica 3 테스트

$ kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 3 --partitions 1 --topic my-replicated-topic

$ kafka-topics.sh --describe --zookeeper localhost:2181 --topic my-replicated-topic

Topic:my-replicated-topic    PartitionCount:1    ReplicationFactor:3    Configs:

    Topic: my-replicated-topic    Partition: 0    Leader: 0    Replicas: 0,2,1    Isr: 0,2,1


$ kafka-console-producer.sh --broker-list localhost:9092 --topic my-replicated-topic

my test message 1

my test message 2

^C


$ kafka-console-consumer.sh --zookeeper localhost:2181 --from-beginning --topic my-replicated-topic


## 서버 한대 다운

$ kafka-server-stop.sh


$ kafka-topics.sh --describe --zookeeper localhost:2181 --topic my-replicated-topic

Topic:my-replicated-topic    PartitionCount:1    ReplicationFactor:3    Configs:

    Topic: my-replicated-topic    Partition: 0    Leader: 0    Replicas: 0,2,1    Isr: 0,1


$ kafka-console-consumer.sh --zookeeper localhost:2181 --from-beginning --topic my-replicated-topic


## 토픽 삭제

$ kafka-topics.sh --delete --zookeeper localhost:2181 --topic my-replicated-topic

$ kafka-topics.sh --delete --zookeeper localhost:2181 --topic TutorialTopic









반응형
Posted by seungkyua@gmail.com
,