문제 설명 보기

>> programmers.co.kr/learn/courses/30/lessons/43238

 

코딩테스트 연습 - 입국심사

n명이 입국심사를 위해 줄을 서서 기다리고 있습니다. 각 입국심사대에 있는 심사관마다 심사하는데 걸리는 시간은 다릅니다. 처음에 모든 심사대는 비어있습니다. 한 심사대에서는 동시에 한

programmers.co.kr

 

해당 문제의 핵심은 이것을 이분탐색법을 사용해 해결할 생각을 하는것이다.

 

문제가 설명한대로 실제 구현을 하게 되면 무조건 time out을 맞게 되어있다.

 

문제가 원하는 반환값은 시간이며, 시간은 항상 오름차순으로 정렬되어있다고 볼 수 있다.

 

이분탐색의 핵심인 정렬되어있는 숫자에서 탐색하는 범위를 절반으로 줄여 나가는 방법으로 O(logN)의 시간복잡도로 해결 할 수 있다.

 

> Solution

class Solution {
    public long solution(int n, int[] times){
        long left = 1;
        long right = (long)1000000000 * (long)1000000000;
        long mid = 0;
        long answer = 1;
        long sum;

        while( right > left ){
            mid = (left + right) / 2;
            sum = 0;
            for(int num : times){
                sum += mid / num;
            }
            if(sum >= n){
                answer = mid;
                right = mid;
            } else if (sum < n){
                left = mid + 1;
            }
        }

        return answer;
    }
}

 

* right는 입국 심사를 기다리는 10억명이 한명을 심사하는데 10억 분 걸리는 심사관 혼자서 심사하는 경우인, 가질 수 있는 가장 큰 숫자를 지정했다. Long.MAX_VALUE - 1을 사용해보았으나 아무래도 12번째 line에서 overflow가 발생하는 듯 싶어 이와같이 수정하였다.

728x90
반응형

C++에서 알고리즘 문제를 해결 할 때는 (x, y)형태의 좌표 또는 (시간, 거리) 등을 Pair 클래스를 유용하게 사용했다.

 

javafx에서 Java도 Pair 객체를 제공하지만 알고리즘 사이트의 컴파일러에서 제공해주지 않거나 해서 해당 클래스를 사용하지 못하게 될 경우 간단하게 Pair Class를 구현 할 수 있다.

 

private class Pair<L, R>{
    L left;
    R right;
    
    private Pair(L left, R right){
    	this.left = left;
        this.right = right;
    }
}

 

private 접근제한자로 만들면 다른 클래스에서 사용할 수 없으니 접근제한자는 필요에 따라 구분해준다.

 

이제 해당 객체를 List 객체를 사용해 관리한다면 다음과 같이 사용할 수 있다.

LinkedList<Pair<Integer, String>> pairArr = new LinkedList<>();

pairArr.add(new Pair<>(1,"aa"));
pairArr.add(new Pair<>(1,"ab"));
pairArr.add(new Pair<>(2,"ac"));
pairArr.add(new Pair<>(2,"ad"));
pairArr.add(new Pair<>(3,"ae"));
pairArr.add(new Pair<>(3,"af"));
pairArr.add(new Pair<>(4,"ag"));
pairArr.add(new Pair<>(4,"ah"));

여기서 LinkedList는 List를 조상 클래스로 두고 있으며 Collections.sort에서 List를 파라미터로 사용하므로 sort를 사용해 커스텀 객체를 편리하게 정렬 할 수 있다.

 

sort 메서드는 Comparable 인터페이스의 compareTo(Object o) 를 기본으로 사용하며, 파라미터로 Comparator 인터페이스의 구현체를 넘겨주는 방식으로 사용할 수 있다.

 

사용방법 첫번째는 Comparable 인터페이스를 구현하는 것이다.

private class Pair<L, R> implements Comparable{
    L left;
    R right;
    
    private Pair(L left, R right){
    	this.left = left;
        this.right = right;
    }
    
    public int compareTo(Object o){
    	return this.left != (Pair)o.left ? (Pair)o.left - this.left : (Pair)o.right.compareTo(this.right)); // 내림차순
    }
}

 

두번째 사용 방법으로 Collections.sort를 호출하여 파라미터에 Pair 객체를 넣어주고 Comparator를 익명 메서드로 작성하여 넣어서 사용해보자.

Comparator를 따로 작성해서 사용해도 된다.

Collections.sort(pairArr, new Comparator<Pair<Integer, String>>() {
	@Override
	public int compare(Pair<Integer, String> o1, Pair<Integer, String> o2) {
		return (o1.left != o2.left ? o2.left - o1.left : o2.right.compareTo(o1.right));
	}
});

 

위와 같이 Collections.sort를 사용하여 Pair의 left를 내림차순으로, Pair의 left가 같다면 right를 String compareTo를 사용해 사전 내림자순으로 정렬하였다.

 

해당 방법으로 Triple 등의 객체를 구현해 정렬하는 방법도 가능하며, sorting 알고리즘에 관련된 문제를 손쉽게 해결 할 방법이 될 수도 있다.

 

[결과]

----------정렬 전
[1, aa]
[1, ab]
[2, ac]
[2, ad]
[3, ae]
[3, af]
[4, ag]
[4, ah]
----------정렬 후
[4, ah]
[4, ag]
[3, af]
[3, ae]
[2, ad]
[2, ac]
[1, ab]
[1, aa]

728x90
반응형

쿠버네티스를 구축하는 방법은 다양하다.

가장 쉬운 방법은 AWS EKS(Elastic Kubernetes Service), GCP GKE(Google Kubernetes Engine), 국산을 좋아한다면 NCP의 Kubernetes Sercie를 이용하는 방법이 있다.

쿠버네티스에서 제공하는 Cloud volume 마운트 기능 등을 각 플랫폼에서 제공하는 방법으로 쉽게 쉽게 Mount를 할 수도 있고 레퍼런스도 많고 무수한 장점이 있겠지만 지속적으로 비용이 발생한다는 단 하나의 단점이 있다.

 

그래서 방구석에서 쿠버네티스 클러스터를 구축해보며 까먹지 않기 위해 기록한다.

 

OS

- Ubuntu 18.04.5 LTS

 

Container Runtime

- Docker

 

컨테이너는 CRI-O 등 대안이 존재하지만 워낙 레퍼런스 찾기가 힘들어 사실상의 표준이라 생각하는 도커를 사용했다.

 

클러스터 구축 툴

- kubeadm

 

오픈소스 프로젝트라 그런지 구축을 결심하자마자 선택지부터 발생했다.

쿠버네티스 클러스터 구축을 도와주는 toolkit으로는 kubeadm, kops, kubespray등의 도구가 있다.

kubeadm을 선택한 이유는 가장 레퍼런스가 많아서.. 이므로 각 툴의 원하는 기능이 있다면 다른 도구를 사용해 구축하는게 편할 것이다.

 

1. Update

$ apt update -y
$ apt upgrade -y

모든 노드의 패키지를 업데이트받는다.

 

2. Swap Memory off

$ sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
$ swapoff -a

쿠버네티스 환경에서는 Swap기능을 off하는게 권장된다.

관련 discussion이 있으니 나중에 확인.

 

3. Docker 설치

$ sudo apt-get update && sudo apt-get install -y
$ apt-transport-https ca-certificates curl software-properties-common gnupg2
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key --keyring /etc/apt/trusted.gpg.d/docker.gpg add -
$ add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
$ apt update

여기까지 진행 한 후 만약 apt install -y containerd.io docker-ce 같은 명령어로 도커를 설치하지 말고 쿠버네티스 버전과 도커 버전을 잘 확인 후 진행하도록 하자..

해당 정보는 여기에서 확인 할 수 있다.

나의 쿠버네티스는 v1.20.2이며 도커는 19.03.12 버전을 설치했다.

$ apt-cache madison docker-ce

위 명령어로 버전을 확인 한 후

$ sudo apt-get install docker-ce=5:19.03.12~3-0~ubuntu-bionic docker-ce-cli=5:19.03.12~3-0~ubuntu-bionic containerd.io

명령으로 도커를 설치했다.

 

$ mkdir -p /etc/systemd/system/docker.service.d
$ tee /etc/docker/daemon.json < {\
"exec-opts": ["native.cgroupdriver=systemd"],\
"log-driver": "json-file",\
"log-opts": {\
"max-size": "100m"\
},\
"storage-driver": "overlay2"\
}
EOF

이후 directory와 configuration을 만들어주고 도커를 재시작한다.

 

$ systemctl daemon-reload
$ systemctl restart docker
$ systemctl enable docker

 

kubelet, kubeadm, kubectl을 설치한다.

$ apt-get update
$ apt-get install -y
$ apt-transport-https curl
$ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
$ echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | tee /etc/apt/sources.list.d/kubernetes.list
$ apt update
$ apt -y install vim git curl wget kubelet kubeadm kubectl
$ apt-mark hold kubelet kubeadm kubectl

 

방화벽 설정 (패킷 포워딩 활성화)

$ modprobe overlay
$ modprobe br_netfilter
$ tee /etc/sysctl.d/kubernetes.conf< net.bridge.bridge-nf-call-ip6tables = 1\
net.bridge.bridge-nf-call-iptables = 1\
net.ipv4.ip_forward = 1\
EOF
$ sysctl --system

 

Kubelet 시작, kubeadm 이미지 업데이트, kubeadm 으로 클러스터 시작

$ systemctl enable kubelet
$ kubeadm config images pull
$ kubeadm init --pod-network-cidr=10.244.0.0/16

클러스터의 pot network 옵션을 위와 같이 지정한건 이후 CNI로 flannel을 사용할 예정이기 때문이다.

flannel은 위와 같이 pod network 옵션을 주는 것을 권장하고 있다.

다른 CNI를 사용한다면 해당 CNI에서 권항하는 ip대역대가 있는지 한번 알아보면 좋을 것 같다.

calico에서는 pot-network 대역으로 192.168.0.0/16 를 권장한다.

 

kubeadm init을 한다면 

kubeadm join master:6443 --token ~~ \
--discovery-token-ca-cert-hash sha256:~~~

이러한 형태의 토큰을 확인 할 수 있는데, 다른 노드에서 해당 토큰으로 쿠버네티스 클러스터에 접근 할 수 있다.

만약 까먹거나 24시간이 지났다면

$ kubeadm token list
$ openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'

다시 확인 할 수 있다.

 

$ mkdir -p $HOME/.kube
$ cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ chown $(id -u):$(id -g) $HOME/.kube/config

위 명령어 3종세트로 kubectl에 대한 권한을 해당 계정에 부여 할 수 있다.

 

coredns loop 해제

$ kubectl edit cm coredns -n kube-system

위 명령어로 실행하여 loop 옵션을 주석처리한다.

 

CNI설치

falnnel

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml 

calico

kubectl create -f https://docs.projectcalico.org/manifests/tigera-operator.yaml

guide: https://docs.projectcalico.org/getting-started/kubernetes/quickstart

 

참고자료

www.liquidweb.com/kb/how-to-install-kubernetes-using-kubeadm-on-ubuntu-18/

728x90
반응형

'OS & Container > Kubernetes' 카테고리의 다른 글

[쿠버네티스] StatefulSet  (0) 2021.07.03
[쿠버네티스] Deployment  (0) 2021.07.02
[쿠버네티스] Service  (0) 2021.07.01
[쿠버네티스] Liveness Probe  (0) 2021.07.01

 

Nginx컨테이너와 API서버 컨테이너를 통신하기 위해 도커의 네트워크 개념을 알아야 한다.

 

도커 네트워크를 확인해보자

docker network ls

도커가 자동으로 생성한 bridge, host, none 네트워크를 확인 할 수 있다.

 

 

도커 컨테이너를 생성 할 때 network옵션을 주지 않으면 기본 bridge에 연결된다.

bridge들은 16 bit prefix를 가진 서브넷 ip를 쓰게 되며, 

같은 bridge 내 컨테이너들은 서로 컨테이너 명으로 통신 할 수 있다.

 

도커 기본 bridge는 모든 컨테이너가 생성될 때 기본적으로 연결되는 network이므로 새로 bridge를 만들어 쓰도록 하자.

docker network create {your_network}

docker network create 명령어로 새로운 bridge를 생성한다.

 

구동중인 컨테이너를 방금 만든 bridge에 연결하자

docker network connect {your_bridge} {your_container_id}

도커를 처음 만들 때 커스텀 브릿지에 연결할 수도 있다.

docker run --netowrk {your_bridge} {docker_image}

또는 docker-compose에 컨테이너별 networks 항목에 명시해주면 된다.

 

 

이제 nginx 컨테이너의 설정파일을 수정해줘야한다.

nginx.conf의 기본 경로는 /etc/nginx/nginx.conf 이다. (다를 수도 있다.)

cat으로 내용을 띄워보면 하단 http 블럭에 'include /etc/nginx/conf.d/*.conf' 라고 씌여있는 걸 볼 수 있다.

nginx.conf 파일을 직접 수정해주어도 되고, 해당 경로의 파일을 수정해도 되고, {your_file}.conf를 해당 경로에 생성해도 된다.

아무튼 http 블럭에 아래 내용을 추가한다.

upstream {your_resolver_name} {
	server {your_API_container_id}:{PORT};
}

server {
    location /api {
    	proxy_pass http://{your_resolver_name}$request_uri
    }
}

nginx가 reverse proxy의 역할을 수행하여 '~/api'로 요청된 요청을 upstream에 명시된 서버로 전달한다.

API서버와는 같은 docker network bridge에 있기 때문에 컨테이너 ID를 ip대신 명시 할 수 있다.

 

그림으로는 위와 같이 구성 할 수 있다.

외부에서 서버로 온 요청은 Docker의 network를 통해 nginx컨테이너로 전달된다.

nginx 컨테이너를 생성할 때 외부에 노출할 port를 80번으로 했기 때문에 80포트로 들어온 요청이 Nginx로 들어오게 된다.

그리고 nginx는 그 중에서도 ~/api uri로 들어온 요청을 API서버로 전달하게 된다.

즉, API 컨테이너는 외부로 노출되는 포트가 없음에도 Nginx의 Reverse Proxy를 통해 client에게 응답해줄 수 있게 된다.

 

 

728x90
반응형

git repository에 있는 react project를 Jenkins를 사용해

 

자동으로 개발서버에 빌드 및 도커 컨테이너로 배포하려 한다.

 

젠킨스 > Jenkins 관리 > 플러그인 관리

docker를 사용하여 컨테이너를 띄울 예정이니 docker 관련 플러그인을 설치해주자.

 

Jenkins에서 파이프라인을 만들어보자

Jenkins > 새로운 Item

새로운 Item을 생성한다.

 

Jenkins > 새로운 Item

'Pipeline', 배관 아이템을 선택한다. 아이템 이름은 필수값이다.

 

Jenkins > 새로운 Item > Pipeline

SCM을 Git으로 선택 후, GitHub의 Repository를 선택해준다.

Credentials에서는 여기서 생성한 Credential을 넣으면 된다.

 

Branches to build에는 master브랜치를 입력해준다.

개발 branch를 따로 지정해주고 싶다면 그렇게 해도 된다.

 

Script Path에는 레파지토리에 사전 작성할 파이프라인 스크립트의 파일명을 입력해주면 된다.

그리고 레파지토리에 Script Path에 해당하는 경로에 Script파일을 작성하면 된다.

나는 다음과 같이 작성했다.

pipeline {
    agent none
    //기본적으로 체크아웃을 하지 않는 옵션
    options { skipDefaultCheckout(true) }
    stages {
        stage('Checkout repository') {
            agent any
            steps {
                checkout scm
            }
        }
        stage('Build') {
            agent {
                docker {
                    image 'node:14.15.2-alpine'
                }
            }
            steps {
                sh 'npm install'
                sh 'npm run build'
            }
        }
        stage('Docker build') {
            agent any
            steps {
                sh 'docker build -t {image_name}:latest .'
            }
        }
        stage('Docker run') {
            agent any
            steps {
                sh 'docker ps -f name=raor_dev -q | xargs --no-run-if-empty docker container stop'
                sh 'docker container ls -a -fname=raor_dev -q | xargs -r docker container rm'
                sh 'docker images --no-trunc --all --quiet --filter="dangling=true" | xargs --no-run-if-empty docker rmi'
                sh 'docker run -d --name {container_name} -p 80:80 {image_name}:latest'
            }
        }
    }
}

Checkout repository에서 레파지토리 내용을 checkout받고 

Build에서 node를 docker agent로 실행해 소스를 build한다.

Docker build에서 도커 이미지를 빌드하고

Docker run에서 기존 컨테이너와 이미지를 지우고 새로운 컨테이너를 실행한다.

 

도커 build stage에서 사용할 Dockerfile은 다음과 같이 작성했다.

FROM nginx:latest
VOLUME /raor_dev_volume
RUN rm -rf /etc/nginx/conf.d/default.conf
ADD ./nginx/default.conf /etc/nginx/conf.d/default.conf
ADD ./build /usr/share/nginx/html

혹시 nginx 컨테이너와 파일 교환이 필요할 때 사용할 volume을 마운트해주고

build stage에서 빌드해놓은 ./build 디렉토리를 컨테이너의 nginx/html로 복사해주면 된다.

 

nginx의 기본 설정 파일의 경로는 /etc/nginx/conf.d/default.conf 이며

레파지토리에 nginx의 설정 파일을 작성해놓고 사용하려 한다. 기존 이미지에 있는 conf 파일은 삭제해준다.

사용자에게 api서버를 노출하지 않고 nginx가 제공하는 reverse proxy를 사용하려면 필수이다.

 

물론 nginx 컨테이너를 사용하지 않고 node 컨테이너를 사용하여 npm serve를 사용해 빌드된 리액트 애플리케이션을 실행 할 수 있다.

위와 같은 방법은 최신버전에서 보안도 많이 개선되었다고 한다. 하지만 nginx는 잘 알려진 훌륭한 웹서버이며 node컨테이너를 웹서버화하여 이미지로 만들어야 하는 번거로움도 없다.

 

이제 젠킨스에서 PIPELINE 실행을 통해 GitHub 레파지토리의 파일을 체크아웃받아 node로 빌드를 하고 nginx 이미지를 통해 빌드된 React application을 웹서버 컨테이너로 실행 할 수 있다.

 

 

 

작성 참조 링크

www.jenkins.io/doc/tutorials/build-a-node-js-and-react-app-with-npm/

twofootdog.github.io/Docker-Docker%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EB%A1%9C-jenkins-%EC%8B%A4%ED%96%89-%ED%9B%84-%EC%86%8C%EC%8A%A4-push-%EC%8B%9C-%EC%9E%90%EB%8F%99-%EB%B9%8C%EB%93%9C%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0(Springboot)/

728x90
반응형

젠킨스 > Jenkins 관리 > 플러그인 관리

젠킨스에서 플러그인 관리 화면으로 진입한다.

 

젠킨스 > Jenkins 관리 > 플러그인 관리

git과 github관련 플러그인의 설치를 확인한다. 설치되어 있지 않다면 설치해야 한다.

 

 

GitHub 계정별 연동

github으로 가자.

 

github > Settings

github > Settings > Developer setting

Developer setting 항목을 찾는다.

 

github > Settings > Developer settings > Personal access tokens

Personal acceess tokens 항목에서 계정의 토큰을 생성 할 수 있다.

Generate new token 버튼으로 토큰을 생성한다.

위 캡쳐에서처럼 admin:org(레파지토리 읽기/쓰기), admin:repo_hook(레파지토리 훅 권한) -깃헙에 push가 일어나면 자동으로 빌드되어 컨테이너를 실행할 수 있도록-, repo(레파지토리 접근) 스콥으로 진행하겠다.

 

젠킨스 > Jenkins 관리 > 환경설정

이후 Jenkins 환경설정에서 Add GitHub Server를 눌러주자.

젠킨스 > Jenkins 관리 > 환경설정

Add버튼으로 Credentials를 등록한다.

GitHub > GitHub Server > Add Credentials

Kind - Secret text

Secret - 토큰

을 입력해주면 된다.

Jenkins > Jenkins 관리 > 환경설정

Test connection버튼으로 연결을 확인하고 환경설정 가장 아래의 저장 또는 Apply버튼으로 세팅을 저장한다.

(사실 Public Repository로 진행한다면 이 과정은 필요없다)

 

 

SSH KEY연동방식

젠킨스와 gitHub 연동시 계정을 통째로 인증하는 방식은 보안상 위험 할 수 있다.

 

따라서 SSH 연동방식또한 소개한다.

 

간단하게, 더 귀찮고 더 보안이 좋은 방식이다.

 

cd /var/jenkins_home
mkdir .ssh
ssh-keygen -t rsa -f /var/jenkins_home/.ssh/github_rsa_ssh_key

먼저 SSH 키를 생성한다.

키의 이름은 github_rsa_ssh_key 으로 하겠다.

 

 

GitHub > Repository > Settings > Deploy keys > Add deploy key

깃헙 레파지토리에서 Setting > Deploy keys > Add deploy key항목을 찾아낸다.

 

GitHub > Repository > Settings > Deploy keys > Add deploy key

방금 생성한 ssh key 중 확장자가 .pub으로 되어있는 public key를 등록해준다.

 

SCM: Git > Repositories > Credentials

이후 Credentials를 등록하는 화면에서 위와 같이 입력한다.

Private Key는 위에서 생성한 SSH키 중 private key를 입력하면 된다.

 

SCM: Git > Repositories

Repository URL은 위와 같이 SSH방식으로 적어야 한다

 

이상 GitHub과 Jenkins를 연동하는 두가지 방법이다.

728x90
반응형

git repository에 있는 spring boot project를 Jenkins를 사용해

 

자동으로 개발서버에 빌드 및 도커 컨테이너로 배포하는 일기다.

 

젠킨스 > Jenkins 관리 > 플러그인 관리

docker를 사용하여 컨테이너를 띄울 예정이니 docker 관련 플러그인을 설치해주자.

 

Jenkins에서 파이프라인을 만들어보자

Jenkins > 새로운 Item

새로운 Item을 생성한다.

 

Jenkins > 새로운 Item

'Pipeline', 배관 아이템을 선택한다. 아이템 이름은 필수값이다.

 

Jenkins > 새로운 Item > Pipeline

SCM을 Git으로 선택 후, GitHub의 Repository를 선택해준다.

Credentials에서는 여기서 생성한 Credential을 넣으면 된다.

 

Branches to build에는 master브랜치를 입력해준다.

개발 branch를 따로 지정해주고 싶다면 그렇게 해도 된다.

 

Script Path에는 레파지토리에 사전 작성할 파이프라인 스크립트의 파일명을 입력해주면 된다.

그리고 레파지토레에 Script Path에 해당하는 경로에 Script파일을 작성하면 된다.

나는 다음과 같이 작성했다.

pipeline {
    agent none
    //기본적으로 체크아웃을 하지 않는 옵션
    options { skipDefaultCheckout(true) }
    stages {
        stage('Checkout repository') {
            agent any
            steps {
                checkout scm
            }
        }
        stage('Build and test') {
            agent {
                docker {
                    image 'gradle:6.6.1-jdk11-openj9'
                    args '-v /root/.m2:/root/.m2'
                }
            }
            steps {
                sh 'gradle clean build -x test'
            }
        }
        stage('Docker build') {
            agent any
            steps {
                sh 'docker build -t {container_name}:latest .'
            }
        }
        stage('Docker run') {
            agent any
            steps {
                sh 'docker ps -f name={container_name} -q | xargs --no-run-if-empty docker container stop'
                sh 'docker container ls -a -fname=sbor_dev -q | xargs -r docker container rm'
                sh 'docker images --no-trunc --all --quiet --filter="dangling=true" | xargs --no-run-if-empty docker rmi'
                sh 'docker run -d --name {container_name} -p 8080:8080 {container_name}:latest'
            }
        }
    }
}

Checkout repository에서 레파지토리 내용을 checkout받고 

Build and test에서 gradle을 docker agent로 실행해 소스를 build한다.

Docker build에서 도커 이미지를 빌드하고

Docker run에서 기존 컨테이너와 이미지를 지우고 새로운 컨테이너를 실행한다.

 

도커 build stage에서 사용할 Dockerfile은 다음과 같이 작성했다.

FROM openjdk:11
ADD {your_application_file} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

open jdk 이미지의 정보는 https://hub.docker.com/_/openjdk 여기서 확인할 수 있으며 컨테이너와 파일교환을 위해 volume을 따로 설정해줘도 좋다.

 

이제 젠킨스에서 PIPELINE 실행을 통해 GitHub 레파지토리의 파일을 체크아웃받아 gradle로 빌드를 하고 OpenJDK11버전 이미지를 통해 SpringBoot Application을 컨테이너로 실행 할 수 있다.

 

 

[Trouble Shooting]

Jenkins

Invalid agent type "docker" specified.

Jenkins에서 docker 관련 플러그인이 없을 경우. 관련 플러그인 설치로 해결.

 

 

container 내부 docker 설치

/var/jenkins_home/workspace/{depends on you}/script.sh: docker: not found

컨테이너 내부에 docker가 설치되어있지 않음을 간과한경우.

도커가 설치되어 있는 컨테이너를 사용해야 한다.

curl -fsSL https://get.docker.com/ | sudo sh

 

 

Jenkins docker container

Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.40/containers/amd64/gradle:6.6.1-jdk11-openj9/json: dial unix /var/run/docker.sock: connect: permission denied

컨테이너에서 docker.sock에 대한 실행권한이 없다. 컨테이너에서 구동되는 jenkins는 jenkins라는 user권한을 사용하기 때문에 docker그룹에 추가해주자.

컨테이너의 root권한 bash를 열어준 뒤 그룹에 추가하면 된다.

이후 도커 데몬을 재기동 해주자.

usermod -aG docker jenkins
service docker restart


docker rmi

docker: "rmi" requires a minimum of 1 argument.

docker rmi 명령어가 삭제할 이미지가 없는 경우다.

docker rmi $(docker images --filter "dangling=true" -q --no-trunc)

위와 같이 잉여 이미지를 정리하는 명령어를 사용 할 때, 이미지가 하나도 없는 깨끗한 상태라면 바로 에러를 뿜게 된다.

따라서 다음과 같이 --no-run-if-empty를 사용하자

docker images --no-trunc --all --quiet --filter="dangling=true" | xargs --no-run-if-empty docker rmi

참조: stackoverflow.com/questions/35416962/error-while-cleaning-images-from-docker

 

 

 

작성 참조 링크

twofootdog.github.io/Docker-Docker%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EB%A1%9C-jenkins-%EC%8B%A4%ED%96%89-%ED%9B%84-%EC%86%8C%EC%8A%A4-push-%EC%8B%9C-%EC%9E%90%EB%8F%99-%EB%B9%8C%EB%93%9C%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0(Springboot)/

tomgregory.com/building-a-spring-boot-application-in-docker-and-jenkins/

 

728x90
반응형

Ubuntu 18.04.5 LTS에서 Docker를 사용해 Jenkins를 띄우려 한다.

 

준비물은 dockerhub 계정.

 

1. 패키지 정보 업데이트

sudo apt-get update

 

2. 도커 설치

curl -fsSL https://get.docker.com/ | sudo sh

Ubuntu의 경우 Docker에서 제공해주는 스크립트로 편하게 설치 할 수 있다.

 

3. 사용자 계정을 docker 그룹에 추가

sudo usermod -aG docker ${계정}

 

 

4. dockerhub에 로그인

docker login [URL]

URL을 생략하게 된다면 기본적으로 dockerhub에 로그인하게 된다.

계정이 준비되어있지 않다면 hub.docker.com 에서 계정을 생성하자.

 

 

이상으로 docker 설치가 완료되었다. docker -v 등의 명령어로 도커가 설치되었나 확인해보자.

 

5. Jenkins 이미지 다운로드

원래 docker pull 명령어를 실행했을때 local에 image가 없으면 docker pull로 가져온다.

하지만 docker 20.10.1, Ubuntu 18.04.5 버전에서 manifest unknown에러가 발생하기 때문에 수동으로 젠킨스 이미지를 받아야 했다.

hub.docker.com/_/jenkins?tab=tags&page=1&ordering=last_updated

 

jenkins Tags - Docker Hub

 

hub.docker.com

위 링크에서 Docker Hub Repository에 있는 최신 도커 이미지를 확인 할 수 있다.

docker pull jenkins:2.60.3

 

6. Host와 컨테이너간 파일 공유를 위한 디렉토리를 생성해야 한다.

mkdir jenkins
mkdir jenkins/jenkins_home

 

7. jenkins 컨테이너를 실행한다.

docker run -d -p 8080:8080 -p 50000:50000 -v $PWD/jenkins/jenkins_home:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock --name jenkins jenkins/jenkins

포트정보와 jenkins setting이 저장될 볼륨은 원하는대로 세팅해도 좋다.

jenkins 컨테이너가 docker 컨테이너를 띄우게 해야 함으로 docker.sock파일도 볼륨으로 지정해준다.

 

-d
Detached Mode. 컨테이너가 백그라운드로 실행된다.

--name ["이름"]
컨테이너의 이름을 설정한다.

-v
데이터 볼륨을 설정한다. host와 공유할 디렉토리를 설정하여 파일을 컨테이너에 저장하지 않고 호스트에 바로 저장한다.

 

-p

컨테이너의 포트를 호스트의 특정 포트와 연결한다. [호스트포트]:[컨테이너포트]

컨테이너 포트만 입력하면 호스트의 포트가 무작위로 연결된다.

 

Jenkins의 Initial Password를 확인해야 한다.

jenkins_home의 공유디렉토리를 설정해주었으니 ./jenkins/jenkins_home/secrets/initialAdminPassword 파일을 통해 확인 할 수 있다.

 

7-1. KST 시간대 설정

dockerhub에서 제공하는 jenkins는 기본적으로 UST 시간대로 설정이 되어 있기 때문에 이것을 KST로 바꿔줬다.

docker exec -itu 0 jenkins /bin/bash
ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime

컨테이너 내에서 'sudo'를 지원하지 않음으로 root유저(0)로 바쉘을 실행한다.

이후 시간대의 링크를 바꿔준 후 date 명령어로 시간대를 확인하면 바뀐것을 알 수 있다.

 

 

8. jenkins 버전 업데이트

애석하게도 dockerhub에서 제공해준 최신 이미지 2.60.3버전에서는 젠킨스 플러그인을 멀쩡하게 사용 할 수 없다.

2.164.3 버전 이상을 요구하니 jenkins를 업데이트해야 한다.

 

mirrors.jenkins-ci.org/

 

Jenkins mirrors

 

mirrors.jenkins-ci.org

위 Jenkins mirrors 페이지에서 최신 jenkins버전을 확인 할 수 있다.

그 중 war-stable의 최신버전인 2.263.1버전으로 해야겠다.

 

root 권한을 이용해 bash을 열자

wget http://updates.jenkins-ci.org/download/war/2.263.1/jenkins.war
mv ./jenkins.war /usr/share/jenkins/
chown jenkins:jenkins /usr/share/jenkins/jenkins.war

 

이후 컨테이너를 restart한다.

 

8. 젠킨스 설정

드디어 docker 컨테이너가 running중인 서버 8081포트로 접속하여 7번과정에서 확인한 initialPassword로 진행하면 된다.

728x90
반응형

+ Recent posts