레이블이 daemon인 게시물을 표시합니다. 모든 게시물 표시
레이블이 daemon인 게시물을 표시합니다. 모든 게시물 표시

failed to start docker service

# docker 서비스 상태를 보면
sudo systemctl status docker.service

# 다음과 같이 failed 상태다.
● docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)
   Active: failed (Result: exit-code) since Tue 2023-11-07 10:37:32 KST; 3min 6s ago
     Docs: https://docs.docker.com
  Process: 3637521 ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock (code=exited, status=1/FAILURE)
 Main PID: 3637521 (code=exited, status=1/FAILURE)
11월 07 10:37:32 ysoftman-dev1 systemd[1]: docker.service: Start request repeated too quickly.
11월 07 10:37:32 ysoftman-dev1 systemd[1]: docker.service: Failed with result 'exit-code'.
11월 07 10:37:32 ysoftman-dev1 systemd[1]: Failed to start Docker Application Container Engine.

# 도커 버전 확인
/usr/bin/dockerd --version
Docker version 24.0.7, build 311b9ff

# 서비스 시작 명령을 수행하면
sudo /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

# 다음과 같이 daemon.json 에 호스트를 설정이 있는데 -H 옵션으로 또 지정해서 문제가 된다.
# -H, --host list (Daemon socket(s) to connect to)
unable to configure the Docker daemon with file /etc/docker/daemon.json: the following directives are specified both as a flag and in the configuration file: hosts: (from flag: [fd://], from file: [unix:///var/run/docker.sock tcp://0.0.0.0:2375])

# /usr/lib/systemd/system/docker.service 파일을 보면 다음과 같이 -H 옵션이 있다.
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

# 찾아보니 같은 이슈가 많이 있다.
# 옵션 중복 허용을 하던가 설정 덮어쓰기를 하던가 수정이 필요해 보이는데 개선되지 않고 있다.
# 현재로선 daemon.json hosts 를 사용한다면 -H 옵션을 제거하는 방식으로 해결한다.

# 다음과 같이 -H 옵션은 제거해 daemon.json 설정만 사용하도록 했다.
ExecStart=/usr/bin/dockerd --containerd=/run/containerd/containerd.sock

jenkins systemctl failed

# jenkins systemctl 상태를 확인하면 Failed 로 나온다.
sudo systemctl status jenkins -n 30 -l

# jenkins 서비스(데몬) 시작시 문제가 되는 부분을 찾기 위해 디버깅
sudo vi /etc/init.d/jenkins
...
case "$1" in
    start)
        echo -n "Starting Jenkins "
        daemon --user "$JENKINS_USER" --pidfile "$JENKINS_PID_FILE" $JAVA_CMD $PARAMS > /dev/null
        RETVAL=$?
        echo "java version: $(/usr/bin/java -version 2>&1 | grep -i version)"
        echo "JENKINS_USER: $JENKINS_USER"
        echo "JENKINS_PID_FILE: $JENKINS_PID_FILE"
        echo "JAVA_CMD: $JAVA_CMD"
        echo "PARAMS: $PARAMS"
        echo "RETVAL: $RETVAL"
        if [ $RETVAL = 0 ]; then
...

# 실제 다음과 같은 명령이 실행된다.
# 참고로 daemon 은 . /etc/init.d/functions 로 로딩한 함수다.
daemon --user ysoftman --pidfile /var/run/jenkins.pid /usr/bin/java -Djava.awt.headless=true -Dcom.sun.jndi.ldap.connect.pool.maxsize=20 -Dcom.sun.jndi.ldap.connect.pool.prefsize=10 -Dcom.sun.jndi.ldap.connect.pool.timeout=5000 -DJENKINS_HOME=/var/lib/jenkins -jar /usr/lib/jenkins/jenkins.war --logfile=/var/log/jenkins/jenkins.log --webroot=/var/cache/jenkins/war --daemon --httpPort=8080 --debug=5 --handlerCountMax=100 --handlerCountMaxIdle=20

# /etc/init.d/jenkins 변경 사항 반영을 위해 다시 로딩
sudo systemctl daemon-reload

# jenkins 서비스 재시작
sudo systemctl restart jenkins

# daemon 함수 실행시 root 가 아니라고 메시지로 실패(RETVAL=1)된다.
Starting Jenkins runuser: may not be used by non-root users

# 해결방법
# /etc/sysconfig/jenkins 에서 다음과 같이 root 로 변경한다.
JENKINS_USER="root"

# 그리고 /etc/init.d/jenkins 의 daemon 실행시 --user 옵션없이 다음과 같이 실행한다.
daemon --pidfile "$JENKINS_PID_FILE" $JAVA_CMD $PARAMS > /dev/null

# 이제 다시 시작하면 아름 스샷과 같이 데몬서비스로 동작한다.
sudo systemctl daemon-reload
sudo systemctl restart jenkins

pid 네임스페이스에 관한 이슈와 해결책

[들어 가기전]
고아(orphan) process : 부모 프로세스가 자식 프로세스보다 먼저 종료되면, 자식 프로세스는 고아 프로세스가 된다. 고아 프로세스는 init(pid 1)에서 거둬 들여, 고아의 부모 프로세스는 init(pid 1)이 된다. 고아 프로세스가 종료되면 init 이 종료 상태를 파악해 좀비 프로세스가 되는것을 막는다.

좀비(zombie/defunct) process : 자식 프로세스는 종료되었지만 부모 프로세스가 wait()등으로 자식 프로세스의 종료 상태를 파악하지 못해 자식 프로세스를 거둬 드릴 수 없는 경우로 process table 에만 남아 있어 kill -9 좀비프로세스PID 로도 삭제되지 않는다.
좀비 프로세스를 삭제 방법
- kill -CHLD PPID (부모 프로세스가 CHLD 시그널을 처리할 수 있는 경우에만 가능)
- kill -KILL PPID (좀비 프로세스의 부모프로세스를 제거한다. 모든 child 가 죽기 때문에 신중해야함)

pid 네임스페이스에 관한 이슈와 해결책을 보고 정리해 봤다

!!! Caution 잘못된 번역이나 이해로 틀릴 수 있음!!! ^^;;


[linux 의 pid(process identifier)]

user space 의 root 프로세스는 init (pid 1, 이것의 부모는 pid 0 으로 커널)이다.
1 번 프로세스가 죽으면 커널이 패닉을 발생해 시스템을 재부팅해야 한다.


[linux namespace 생성]

리눅스 네임스페이스들(uts(host, domain 구분), network, mount, ipc, cgroup(cpu,memory,disk,network 등 리소스 구분), pid)는 unshare 시스템콜로 생성한다. unshare 하면 바로 새로운 네임스페이스로 들어간다.
하지만 pid 네임스페이스의 경우 unshare 로 바로 pid 네임스페이스로 들어가지 않고, fork 로 pid 네임스페이스에 pid 1(init)번를� 만든다. 이 1번 프로세스는 pid 네임스페이스 밖(호스트)에서는 다른 pid 값이다.


[네임스페이스에서의 pid 1]

1. pid 네임스페이스의 init 프로세스는 기본 signal 핸들러가 없어, HUP, INT, QUIT, TERM, KILL .. 등의 시그널 처리를 하지 못한다.(별다른 옵션 없이 docker 컨테이너를 수행하면 ctrl-c 가 동작하지 않는다. 그래서 docker kill 등을 사용한다.)

2. pid 네임스페이스의 init 외 다른 프로세스가 자식 프로세스보다 먼저 죽으면 그 자식 프로세스의 부모는 init (pid 1)이 되고, init 은 프로세스들이 exit 상태를 수집해 커널이 process table 에서 정리할 수 있도록 한다.

3. pid 네임스페이스의 init(pid 1)이 죽으면, 같은 네임스페이스의 모든 프로세스가 강제로 제거되며, pid 네임스페이스는 clean up 된다.


[docker 의 실수]

docker(runc:lightweight docker runtime)는 컨테이너의 entrypoint 를 pid 1 로 하는 새로운 pid 네임스페이스를 만든다. pid 1번에서 signal 처리가 되어 있지 않으면 ctrl-c 와 같은 signal 이 동작하지 않는다. 손자 프로세스가 종료(exit)되기 전에 죽은 자식 프로세스를 fork 하게 되면 zombie(좀비) 프로세스가 컨테이너에 쌓이게 되고, 결국 process table 을 꽉 채우게되는 상황이 발생할 수 있다.
이런 문제를 피하기 위해 fork-exec 를 할 수 있는 특별할 application process 를 실행할 수 있다. 이는 컨테이너가 복잡해지지만 실제 호스트(컨테이너가 아닌)의 init 처럼 만들 수 있다. 사람들은 종속성 격리의 이점을 희생하더라도 여러개의 apt 를 실행(dockerfile 에서 apt install a b c 컨테이너 내에서 패키지를 설치)을 포함시키는 경향이 있다.


[Rkt 해결책]

Rkt(rkt is an application container engine) 로 위 문제를 다소 해결할 수 있다. Rkt 는 사용자가 만든 시작 프로세스(container process)가 init 이 아니라고 가정하고 init (systemd) 를 생성하고 systemd 로 컨테이너에서 동작하는 filesystem 네임스페이스를 만들고 구동시킨다. systemd 는 pid 1번이 되고, (사용자가 만든) container process 는 pid 2번으로 동작한다. 만약 container process 가 init 이라면 이것도 pid 2 로 동작해 또 다른 이슈가 될 수 있다.


[간단한 대체 방법]

fork 후 pid 네임스페이스에서 container process 를 바로 실행하는 대신, spawner 가 다시 fork 한다. 이 두번째 fork 는 container spawner 가 pid 1이 되도록 하고, 모든 child 프로세스들에 signal을 전달할 수 있는 signal handler 를 구성한다. 이렇게 하면 child 프로세스가 죽을때 까지 좀비 프로세스를 거둬들일 수 있고, 이 시점에 container process 의 종료 상태를 수집해 컨테이너화 시스템에 전달한다. 이것은 ctrl-c 와 같은 signal 을 처리할 수 있고 zombie 프로세스도 적절히 거둬들 수 있다는 의미다.
docker 1.13 부터 가능하며 --init 플래그를 사용하면 된다.


[하나의 pod 내에서의 다중 컨테이너]

pod 는 네임스페이스를 공유 하는 컨테이너들의 집합이다. rkt 에서는 파일시스템 네임스페이스를 제외한 모든 네임스페이스들이 공유된다.
앞서 pid 네임스페이스 이슈로 kubernetes(k8s) 는 pod 내 컨테이너들간에 pid 네임스페이스를 공유 하지 않는다. 때문에 같은 pod 내의 container 들 끼리 signal 주고 받을 수 없고, 앞서 언급한 init 문제를 가지고 있어 container processor 는 pid 1로 실행된다.
rkt 방법을 사용하면 컨테이너 안에 init 프로세스를 만들지 않아도 통신이나 signal 을 주고받을 수 있는 컨테이너들을 쉽게 생성할 수 있다. 하지만 이미 존재하는 pod 에 컨테이너를 추가하는 상황에서는 바로 되지는 않는다.


[pod 에 컨테이너 추가하기]

k8s 는 pod sandbox 라는 개념이 있다. k8s 컨테이너 런타임은 컨테이너 시작에 앞서 리소스를 할당하는데, 이는 특히 네트워킹에 유용하고 존재하는 pod 에 컨테이너를 추가하는데도 적용할 수 있다. sandbox pod 를 먼저 생성 후 컨테이너를 하나씩 추가하는 경우, 왜 나중에 컨테이너 추가하는게 허용되지 않을까? 이는 데이터베이스 백업이나 로그 수집과 같은 주기적인 작업에 특히 유용할 것이다.
rkt(에서는 container 를 app 으로 부른다.)는 실험적으로 컨테이너와 독립적으로 pod 생성을 허용하여, 추후에라도 pod 에서 컨테이너를 추가,삭제할 수 있다. 별도 실행되는 유닛(프로세스?)없이 systemd 로 가능하게 한다. 새로운 app(container) 시작 요청이 있으면 pod 의 systemd 와 통신한다. 이러한 모델로 init 프로세스는 다음과 같은 추가적인 특권(privilege)을 가진다.
- 호스트 파일시스템 네임스페이스에 접근할 수 있어서 컨테이너 시작을 위한 파일시스템 네임스페이스를 생성할 수 있다.
- 각 app 시작에 필요한 privilege 들을 알 수 없어 전체 privilege 을 유지해야 한다.
- 컨테이너상의 모든 실행중인 프로세스를 볼 수 있다.
non-sandbox 모델이라면 init 프로세스는 child 프로세스를 시작한 다음 compromise 영향을 최소화 하기 위해 privilege 를 삭제한다.


[sandboxes 와 pid namespaces]

init, sandboxes, pid namespace 를 이용한 방법들 각각 약간의 결점(단점)이 있고, 다음 옵션을 사용할 수 있다.
1. pid namespace 가 sandbox 와 같이 생성되지 않을때, 각 컨테이너는 자신의만의 pid 네임스페이스를 가진다. k8s 에서 사용하는 방식이며, signal 처리를 위한 간단한 init 전략이다. 중요 단점으로는 pod 내 프로세스들간 signal 을 통신을 할 수 없다.
2. pid namespace 가 sandbox 와 같이 생성되지 않을때, sandbox 에서 처음으로 시작되는 컨테이너에 pid namespace 가 생성된다. 이 상태에서는 프로세스간 signal을 주고 받을 수 있다. 단점은 첫번째 프로세스가 pod 의 master 가 되고 이 master 프로세스가 죽으면 컨테이너 내 모든 프로세스는 커널에 의해 종료된다. 때문에 master 프로세스는 pod lifecycle 동안 살아 있어야 한다.
3. pid namespace 가 sandbox와 함께 생성될때, 다른 프로세스를 실행할 수 있는 smart init 프로세스를 포함한다. 이것은 rkt app sandbox 가 동작하는 방식이다. 단점으로는 init 프로세스가 너무 많은 privilege 를 가져 그 만큼 보안 공격에 취약할 수 있다.
4. pid namespace 가 sandbox와 함께 생성될때, sandbox 는 signal을 처리하고 zombie 프로세스를 회수하는 간단한 init 프로세스를 포함한다. 다른 프로세스들은 pid namespace 로 들어갈 수 있지만 부모 프로세스들은 네임스페이스 밖에 있다. init 프로세스는 새로운 컨테이너를 시작하지 않기 때문에, privilege 나 호스트 파일시스템 액세스를 유지할 필요가 없다. 단점은 네임스페이스 내의 각 프로세스들은 pid 0인 부모를 가져 일반적인 process tree 가 깨진다.
5. pid namespace 와 init 이 정확히 위 4번 옵션과 같을때, 다른 프로세스들은 pid namespace 에 들어가고 daemonize 된다. 커널은 위 4번에 pid 0으로 깨진 process tree 에서의 부모를 init 으로 돌릴 것이다. 단점은 daemonize 후 밖에 있는 새로운 프로세스를 모니터링 하기 어렵다. containerizationo system 은 간단하게 프로세스를 waiting 하는 대신 pid 를 통해 프로세스를 추적하도록 강요된다.

(필자는) 위 옵션 중 4번, 5번을 선호한다. 사실 기대하는 프로세스의 lifetime 에 따라 하나를 선택하면 된다. 위 옵션들은 프로세스를 오래 실행하기 위한 좋은 방법들이고 특히 프로세스를 spawner 로 daemonize 프로세스를 종료하는 docker 환경에 좋은 솔루션이다. 만약 간단하게 끝나는 프로세스라면 4번 옵션을 사용하고 프로세스를 pid process tree 와 분리해 유지하면 아주 쉽다. 
어떤 작업들은 k8s 가 init 처럼 동작하는 pause container를 생성하는것과 비슷하다. k8s 에 pid namespace 공유가 지원되면 5번 옵션도 가능할것이다.


[결론]

pid namespace 에는 꽤 숨겨진 복잡도가 존재한다. 오늘날 containerization 시스템은 중요한 단점과 이걸 피할 수 있는 방법들에 따라 선택한다. docker 의 싱글 컨테이너의 단점은 잘 이해되고 적당한 해결방법이 있는 반면, 컨테이너 spawner 가 init 처럼 동작하게 허용되면 컨테이너 빌더 작업이 단순화된다.
그룹 컨테이너의 경우 init 분리 방식이 rkt 가 docker 보다 우수하다. rkt 방식은 현재 k8s pod 모델에서 가능하지 않는 signal 을 통한 프로세스간 통신을 허용한다. 하지만 지연된 시작 컨테이너가 포함되기 때문에 rkt 방식도 몇가지 단점을 보인다.
지연된 시작 컨테이너에 가장 강력한 해결 방법은 pid namespace 와 함께 간단한 init 프로세스를 시작하는것이다. 하지만 새로운 컨테이너를 생성하려면 container spawner 를 통해야 한다. 이는 init 프로세스가 privileges 삭제를 허용하여, 공격을 차단한다. spawner 는 새 프로세스를 daemonize 하고 process tree 를 일관되게 유지하거나 새 프로세스의 부모로 남아 프로세스 관리를 단순화할 수 있다.


docker pull forbidden 해결하기

# private registry 를 사용하는데 docker pull 하면, 다음과 같은 에러가 발생한다.
docker pull ysoftman.test.com/drone/drone:2
Error response from daemon: Get https://ysoftman.test.com/: Forbidden

# docker login 을 해도 forbidden 에러가 발생한다.
sudo docker login -u "ysoftman" -p "password" ysoftman.test.com

# docker daemon 정보를 보면 프록시가 설정되어 있고,
# ysoftman.test.com 이 프록시를 사용해 접근이 안되고 있었다.
docker system info | grep -i proxy
HTTP Proxy: http://ysoftman.proxy.test
HTTPS Proxy: http://ysoftman.proxy.test
No Proxy: localhost,127.0.0.1,aaa

# 프록시를 사용하지 않도록 http-proxy 파일을 수정한다.
sudo vi /etc/systemd/system/docker.service.d/http-proxy.conf
No Proxy: localhost,127.0.0.1,aaa,ysoftman.test.com

# 변경된 설정 파일을 reload 하기 위해서 daemon-reload 을 실행해야 한다.
sudo systemctl daemon-reload

# docker daemon(service) 재시작
sudo systemctl restart docker

# 이제 docker daemon 정보를 보면 no proxy 에 추가된 것을 확인할 수 있고
# docker pull ysoftman.test.com/drone/drone:2 도 잘된다.

docker daemon http_proxy 설정

# 사내 서버에서 aws ECR(elastic Container Registry)에 푸시 하기 위해 
# docker login 을 해야 하는데, 다음과 같이 환경 변수 설정하고
export HTTP_PROXY="http://프록시서버:1111"

# docker login 하면
aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin xxx.ecr.ap-northeast-2.amazonaws.com

# 다음과 같이 docker daemon 에서 타임 아웃이 발생한다.
Error response from daemon: Get https://xxx.ecr.ap-northeast-2.amazonaws.com/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers


# 찾아보니 docker daemon 용 http-proxy.conf 을 사용해야 한다.
# docker daemon http-proxy 파일 생성
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo vi /etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://프록시서버:1111"
Environment="HTTPS_PROXY=http://프록시서버:1111"
Environment="NO_PROXY=localhost,127.0.0.1,127.0.0.0/8,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,.google.com,.naver.com,.daum.net"

# 리로딩 및 재시작
sudo systemctl daemon-reload && sudo systemctl restart docker

# http proxy 설정 확인
sudo systemctl show --property=Environment docker

# 재시작후 sudo 없이 docker 실행하기 위해 docker.sock 소유자 변경
sudo chown -R ysoftman:ysoftman /var/run/docker.sock

# 이제 다시 docker login 하면 성공~
Login Succeeded

linux 데몬 생성하기

# 리눅스에서 데몬 생성(작업)하기

#####
# 방법1 - nohup 명령 사용
# & 백그라운드 작업을 수행할 수 있지만 hangup(터미널 종료) 되면 프로그램도 종료된다.
# 다음과 같이 nohup 명령을 사용하면 hangup 에 대한 면역(immune)으로 터미널 접속을 끊어도 프로그램은 데몬으로 계속 수행된다.
# 종료하려면 해당 프로세스를 kill 시켜야 한다.
# 실행 위치에 nohup.out 파일이 생성되며 쉘 스크립트 stdout 내용이 출력된다.
nohup sh ./ysoftmandaemon.sh


#####
# 방법2 - libslack daemon 프로그램
# centos
curl -OL http://libslack.org/daemon/download/daemon-0.6.4.tar.gz
tar zxvf daemon-0.6.4.tar.gz
cd daemon-0.6.4
make
sudo make install

# ubuntu
sudo apt-get install daemon

# mac
brew install daemon

# 데몬 실행(절대 경로 사용)
daemon --name=ysoftmandaemon --chdir="/home/ysoftman" --pidfile="/home/ysoftman/ysoftman.pid" -- sh ./ysoftmandaemon.sh

# 데몬 중지
daemon --name=ysoftmandaemon --pidfile="/home/ysoftman/ysoftman.pid" --stop


#####
# 방법3 - daemonize 프로그램
# centos
sudo yum install daemonize

# mac
brew install daemonize

# 실행
/usr/sbin/daemonize -a -c /home/ysoftman/myprogram\hello \
  -p /home/ysoftman/myprogram/hello.pid \
  -o /home/ysoftman/myprogram/hello-daemon.log \
  -e /home/ysoftman/myprogram/hello-daemon.err \
  -l /home/ysoftman/myprogram/hello.lock \
  /home/ysoftman/myprogram/hello

install jenkins

# Debian/Ubuntu 에서 패키지 저장소 추가해서 설치하는 경우
# jenkins 접속 키 추가
wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -

# jenkins 저장소 위치 추가
sudo sh -c 'echo deb http://pkg.jenkins-ci.org/debian binary/ > /etc/apt/sources.list.d/jenkins.list'

# apt-get 갱신
sudo apt-get update

# jenkins 설치
sudo apt-get install jenkins

# jenkins.war 위치 파악
dpkg -L jenkins


#####


# Redhat/Centos 에서 rpm 패키지 다운받아 설치하는 경우
wget https://pkg.jenkins.io/redhat/jenkins-2.112-1.1.noarch.rpm
sudo rpm -ivh jenkins-2.112-1.1.noarch.rpm

# jenkins rpm 버전 업데이트시 다음과 같이 설치하면 현재 설치된 jenkins(서비스가 중지된 상태라도)와 충돌 에러가 발생한다.
sudo rpm -ivh jenkins-2.112-1.1.noarch.rpm
jenkins-2.112-1.1.noarch에서 설치되는 /etc/init.d/jenkins 파일은 jenkins-2.40-1.1.noarch 패키지의 파일과 충돌합니다
jenkins-2.112-1.1.noarch에서 설치되는 /etc/sysconfig/jenkins 파일은 jenkins-2.40-1.1.noarch 패키지의 파일과 충돌합니다
jenkins-2.112-1.1.noarch에서 설치되는 /usr/lib/jenkins/jenkins.war 파일은 jenkins-2.40-1.1.noarch 패키지의 파일과 충돌합니다

# jenkins rpm 버전 업데이트시 rpm 옵션으로 -U(업그레이드)하면 된다.
sudo rpm -Uvh jenkins-2.112-1.1.noarch.rpm

# jenkins home 디렉토리 변경
sudo vi /etc/sysconfig/jenkins
JENKINS_HOME="/home/ysoftman/jenkinsdata"

# 기본 포트 변경시
sudo vi /etc/default/jenkins
HTTP_PORT=8080

# 서비스로 jenkins 구동
# 정지할때는 stop 옵션 사용
sudo service jenkins start

# 최신 OS 라면 systemctl 사용
sudo systemctl start jenkins


#####


# mac 에서 패키지로 설치하는 경우
# 설치
brew install jenkins

# 실행
/usr/local/opt/jenkins/bin/jenkins 스크립트 파일에서 아래 명령을 실행한다.
java -jar /usr/local/opt/jenkins/bin/libexec/jenkins.war

# 디폴트 JENKINS_HOME 디렉토리
cd $HOME/.jenkins


#####


# .war 로 구동하는 경우
java -jar /usr/share/jenkins/jenkins.war

# 8080 포트를 이미 다른 프로세스가 사용하고 있다면 다음과 같은 에러 발생
Caused by: java.io.IOException: Failed to listen on port 8080

# 다른 포트를 사용하여 구동
java -jar /usr/share/jenkins/jenkins.war --httpPort=8888


#####


# 구동시 참고 사항
# 로그 위치
vi /var/log/jenkins/jenkins.log

# 또는
https://127.0.0.1:8080/log/all

# 접속해 보기
http://127.0.0.1:8080

# admin 패스워드는 다음 로그 파일에 기록되어있다.
# 로그 파일로 이 파일을 수정후 재시작해도 admin 패스워드는 변경되지 않았다.
cat /var/lib/jenkins/secrets/initialAdminPassword

# 암호 분실시 /var/lib/jenkins/config.xml 에서 다음과 같이 수정하여 재시작
<useSecurity>false</useSecurity>

# jenkins home, jenkins 서버 이전시 홈 디렉토리를 복사(백업/복구)하면 된다.
/var/lib/jenkins

# job 설정은 다음 위치에 config.xml 파일로 저장된다.
/var/lib/jenkins/jobs/job이름/config.xml

# 재시작시 '곧 Jenkins가 종료될 예정입니다.' 메시지 후 재시작 되지 않을때
# /restart 페이지에서 바로 종료 할 수 있다. (rest api 참고)
http://127.0.0.1/restart

C/C++ test

# loop

# 한글 utf-8 출력

# memset

# malloc() 과 calloc() 의 차이

# fopen의 text모드와 binary모드 차이

# STL int string 변환 

# switch 문안에서 변수선언과 동시에 초기화

# 한글 완성형 글자 출력하기

# bit field 테스트

# casting

# Windows 에서 32bit, 64bit 메모리 할당

# 정수형 상수 오버플로우

# 전위,후위 증(감) 연산

# libcurl test

# libjson test

# jsoncpp library 테스트

# linux RWLock test

# 한글 utf-8 출력

# time test

# string 스트림 사용시 첫번째 바이트가 \t \n \r 등 특수문자인 경우 빠지는 문제

# URL 인코딩/디코딩

# linux function test

# 정적 인스턴스와 동적 인스턴스의 차이

# C++ 미리 정의된 매크로

# 파일 헤더로 Unicode(utf-16) 와 utf-8 구분하기

# float, double 과 같은 부동소수점 타입 연산시 유의 사항


# random test

# 실인수(argument) 와 가인수(parameter) 구분

# strtok() 함수를 이용한 연속 토큰 추출

# Linux fork() 를 이용한 데몬 프로그램

# swap 방법들