도움받은 사이트 : 

https://code.visualstudio.com/docs/remote/ssh

 

난 VirtualBox 로 Ubuntu Server 를 설치하고,

윈도우에서 putty 로 위 Ubuntu 에 SSH 로 접속하여 컴파일하는 것을 선호한다.

 

치명적인 단점은 Break Point 걸어서 디버깅하는 것이 Visual Studio 의 그것보다 못하다는 것이다.

 

뭐, Ubuntu Desktop 깔아서 여러 디버깅 툴(ddd 등) 을 깔아 써도 되지만

이것 때문에 가볍게 돌아가는 Ubuntu Server 를 포기하기엔 아까웠다.

 

윈도우에 MinGW 설치해 요래요래 하는 방법도 많던데, 뭔가 발전이 없어보여 안함.

( gcc 최신버전이 11.4 인데 MinGW 는 8.x 라든지... )

 

그래서 찾아보니, Visual Studio Code 에서 원격으로 접속해서 컴파일 및 디버깅까지

가능하다고 한다. 헐~

 

이에 아래처럼 정리한다.

(이 글 읽을 정도면 개발 초중급 이상은 되리라 보기에 VS code 설치 쪽에 중심을 두고 정리한다.)

 

1. Ubuntu Server 를 실행한다.

 

2. Visual Studio Code 를 설치한다.

 

3. VS Code 에서 Remote-SSH Extensions 을 설치한다.

 

4. VS Code 에서 F1 키를 눌러 아래창이 뜨면, "Remote-SSH : Connected to Host" 를 선택한다.

 

5. 아래처럼 Select configured SSH host or enter user@host 라고 뜨면,

아래 창에 접속할 id 와 호스트(IP) 를 입력한다.

나같은 경우, hahaite@192.168.56.2 이다. 

 

6. 그러면 아래처럼 새 창이 뜨면서 Enter password for hahaite@192.168.56.2 라고 뜬다. password 를 입력해 준다.

 

7. Ubuntu 에 VS Code Server 설치 (자동설치임)

위와 같이 password 를 입력하면 우측 하단에 VS Code server 인가를 세팅 중이라고 안내문구가 뜬다. (스샷 못찍음 ㅠㅠ )

 

설치가 완료되고 정상적으로 연결이 되면, 촤측 하단에 아래처럼 SSH : IP 가 뜬다.

 

이 때, Ubuntu 에서 ps -ef 명령어로 실행 중인 프로세스를 확인해 보면 아래처럼 vscode-server 라고 뭔가 떠있는게 보인다. 이는 윈도우의 vs code 에서 자동으로 설치해 준 것이다.

 

8. 설치

이제 File -> Open File 해보면 접속된 Ubuntu 의 디렉토리가 보인다.

아무 파일이나 열고 Run and Debug 아이콘을 클릭하고 Run and Debug 를 클릭한다.

 

 

아래처럼 뜨면 Install an extension for C++ 을 선택해 준다.

 

그리고 아래처럼 C/C++ IntelliSense, debugging 등을 설치해 준다.

아래는 최초 한번만 설치해 주는 것이며, Ubuntu Server 에 원격으로 설치되는 것으로 보인다.

 

9. 실행

이제 Run and Debug 를 다시 클릭하면 아래처럼 Ubuntu 의 컴파일러가 뜬다.

난 c++ 이니 두번째 g++ 을 선택했다.

 

그리고 VS Code 에서 아래 TERMINAL 을 선택하면 해당 코드가 실행되어 결과를 출력한 것을 확인할 수 있다.

 

10. Break Point

그리고 이 글을 쓰는 목적, Break point 테스트.

Visual Studio 의 단축키 (F9) 와 같게 Break Point 걸고 실행하면,

아래처럼 브레이크 걸려 각 변수값을 확인할 수 있으며,

F10, F11 로 진행할 수 있다. 우왕~

 

 

도움받은 사이트 :

https://leeeegun.tistory.com/3

https://stackoverflow.com/questions/6946217/how-to-access-the-contents-of-a-vector-from-a-pointer-to-the-vector-in-c

 

첫번째 링크 글에서 2차원 벡터 사용법을 쉽게 익힐 수 있었다.

 

그렇게 생성한 2차원 벡터를 포인터로 넘길 일이 있었는데 뜻대로 안되어 살펴보니

포인터로 넘겨진 2차원 벡터는 행렬기호 (대괄호) 를 사용할 수 없고 at() 함수를 통해 접근해야 했다.

(위 스택오버플로우 링크 참조)

 

만약 대괄호를 계속 사용하고 싶으면  레퍼런스(Reference)로 넘겨 사용해야 한다.

 

깔끔하게 레퍼런스로 넘기면 되겠지만,

선언과 동시에 할당해야 하는 레퍼런스 특성상 사용하기 까칠한 상황이 발생했다.

 

어쨌든, 아래처럼 두가지의 경우를 정리해 본다.

#include <cstdio>
#include <vector>

using namespace std ;

int callByPointer(vector<vector<int> >* pVecData)
{
	printf("============== Call by Pointer\n") ;
	printf("Size : [%ld][%ld]\n", pVecData->size(), pVecData->at(0).size()) ;

	for(int ii = 0; ii < pVecData->size(); ii++)
	{
		for(int jj = 0; jj < pVecData->at(0).size(); jj++)
			printf("%d ", pVecData->at(ii).at(jj)) ;
		printf("\n") ;
	}
	return 1 ;
}

int callByReference(vector<vector<int> >& pVecData)
{
	printf("============== Call by Reference\n") ;
	printf("Size : [%ld][%ld]\n", pVecData.size(), pVecData[0].size()) ;

	for(int ii = 0; ii < pVecData.size(); ii++)
	{
		for(int jj = 0; jj < pVecData[0].size(); jj++)
			printf("%d ", pVecData[ii][jj]) ;
		printf("\n") ;
	}
	return 1 ;
}

int main()
{
	vector<vector<int> > vecData ;
	vector<int> vecField ;

	vecData.clear() ;
	vecField.clear() ;

	vecField.push_back(2) ;
	vecField.push_back(3) ;
	vecField.push_back(4) ;
	vecData.push_back(vecField) ;

	vecField.clear() ;
	vecField.push_back(5) ;
	vecField.push_back(6) ;
	vecField.push_back(7) ;
	vecData.push_back(vecField) ;

	printf("==============\n") ;
	printf("size : [%ld][%ld]\n", vecData.size(), vecData[0].size()) ;
	for(int ii = 0; ii < vecData.size(); ii++)
	{
		for(int jj = 0; jj < vecData[0].size(); jj++)
			printf("%d ", vecData[ii][jj]) ;
		printf("\n") ;
	}

	// by Pointer
	callByPointer(&vecData) ;

	// by Reference
	callByReference(vecData) ;
	return 1 ;
}

 

main() 함수의 출력값, callByPointer(), callByReference() 함수에서

동일하게 값을 출력함을 확인하였다.

 

♣ 도움받은 사이트

docker-redmine : https://hub.docker.com/_/redmine

밍구의 코딩레시피 : https://minggu92.tistory.com/81

MeetUp (NHN) : https://meetup.nhncloud.com/posts/277

Docker 공식사이트 : https://docs.docker.com/compose/compose-file/compose-versioning/

Nirsa : https://nirsa.tistory.com/170 ( Docker 부팅 시 자동실행)

 

♣ 환경

 - Ubuntu Server 22

 - MariaDB Server 10.6

 

♣ 목적

Ubuntu Server 에는 이미 MariaDB 가 설치되어 있다.

Redmine 은 Docker 를 사용해 설치하고, DB 연동은 이미 설치된 MariaDB를 사용한다.

Redmine 은 docker-compose 를 사용하여 실행한다.

MariaDB 설치 및 docker 관련 세부 명령어는 이 글에서 다루지 않는다.

 

♣ docker 및 docker redmine 설치

docker, docker-compose 을 설치한다.

$ sudo apt install docker

$ sudo apt install docker-compose

$ sudo apt install docker-compose-v2

 

♣ MariaDB 에 Redmine 계정 생성

Redmine 자료가 저장될 MariaDB 에 Redmine 에서 사용할 계정을 만든다.

$ mysql -u root -p

 

로그인 되면,

1. 사용할 database 를 생성함. (아래 "redmine" 은 예제임. 수정 가능)

2. redmine DB 계정 및 패스워드 생성 (아래 "redmine" 계정 및 PW는 예제임. 수정가능)

3. 계정에 권한 부여

4. flush~

 

MariaDB>  create database redmine ;
MariaDB>  create user redmine@'%' identified by 'qwerty123@' ;
MariaDB>  grant all privileges on redmine.* to 'redmine'@'%' ;
MariaDB>  flush privileges ;

 

♣ docker-compose 작성

compose 가 저장될 디렉토리를 하나 만들어준다. 아래는 예시이다.

$ mkdir docker-redmine

$ cd docker-redmine

 

compose 파일을 작성한다. 파일 생성 후,

$ vi compose.yaml

그리고 아래처럼 작성해 준다.

 

아래 version 은 Compose File 버전이다. 이 값은 설치된 docker 버전을 따른다.

$ docker --version

 

설치된 docker version 은 24.0.5 이다.

이에 따른 compose 포맷 버전은 docker 공식 홈페이지에서 확인 가능하다.

https://docs.docker.com/compose/compose-file/compose-versioning/

 

 

위 글에 따르면 19.03 이상 버전은 3.8 을 따른다.

우리는 24.0 이니 3.8 이라고 썼다.

 

REDMINE_DB_MYSQL : DB 가 설치된 서버의 IP 나 hostname 을 적어준다.

Redmine 접속 시, http://192.168.0.143:8080 으로 접속한다면, 192.168.0.143 이 적혀야 한다.

나머지 항목은 직관적이므로 따로 설명하지 않으며

본인 환경에 맞게 적절히 수정하여 사용한다.

 

(아래는 복사용 )

version: '3.8'  # 도커 버전에 맞는 버전으로 작성

services:
   redmine:
      image: 'redmine'
      restart: always
      ports:
         - '80:3000'
         - '8080:3000'

      environment:
         TZ: Asia/Seoul
         REDMINE_DB_MYSQL: 192.168.0.143
         REDMINE_DB_PORT: 3306
         REDMINE_DB_DATABASE: redmine
         REDMINE_DB_USERNAME: redmine
         REDMINE_DB_PASSWORD: qwerty123@

      volumes:
        - '/home/hahaite/docker-redmine/data/files:/usr/src/redmine/files'
        - '/home/hahaite/docker-redmine/data/configuration.yml:/usr/src/redmine/config/configuration.yml'
        - '/home/hahaite/docker-redmine/data/themes:/usr/src/redmine/public/themes'
        - '/home/hahaite/docker-redmine/data/plugins:/usr/src/redmine/plugins'

#        - '/mesData/docker-redmine/data:/home/redmine/data'
#        - '/etc/localtime:/etc/localtime'

      container_name: 'redmine'

 

 

♣ docker-compose 실행

compose.yaml 파일이 있는 디렉토리에서,

$ sudo docker-compose up

이라고 명령을 치면 docker-redmine 이 실행된다.

 

주로 나오는 에러는 DB 에 접속할 수 없다는 내용인데, IP 및 DB 계정을 확인해 본다.

 

이상이 없으면 MariaDB 의 config 파일을 확인해 본다.

$ cd /etc/mysql/mariadb.conf.d

$ vi 50-server.cnf

 

위 파일을 열어서 bind-address 가 127.0.0.1 인지 확인한다.

만약 위 값이 세팅되어 있으면 주석처리한다.

 

그리고 mariadb 를 재실행하고,

$ service mariadb restart

 

docker-redmine 을 재실행한다.

 

별 에러없이 실행이 되면 아래처럼 접속되는지 확인한다.

 

본 절에선 심플하게 Redmine 을 설치하는 법을 다루었으며,

 

다음엔 보안을 적용해 https 접속을 다룰 예정이다.

♣ 도움받은 사이트

 - Development notes (https://whitekeyboard.tistory.com/730 )

 - 피어몬트, 그곳에 가면... (https://blog.startsomething.dev/2018/10/26/virtualbox-%EC%97%90%EC%84%9C-nat-%EC%99%80-host-only-network-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0/ )

 - WP Diaries ( https://www.wpdiaries.com/virtualbox-for-web-development/ )

 - 타쿠대디 ( https://takudaddy.tistory.com/352 )

 - 코덕 개발자노트 ( https://cjwoov.tistory.com/11 )

 

예전에 VirtualBox 의 브릿지와 NAT 개념 관련 글을 쓴 적이 있는데

그 때만 하더라도 자료가 많치 않았다. 2015년이니 9년 전이네... ㅋ~

Link : https://hahaite.tistory.com/283

 

최근에 관련하여 찾아보니 윗글보다 훨씬 자세하고 쉽게 쓴 글이 수두룩하다.

 Link : https://takudaddy.tistory.com/352

 

Virtual Box 네트워크 설정 정리

[목차] 1. 들어가며 2. 기본 용어 정리 3. 네트워크 방식 설명 4. 참고 [들어가며] 버추얼박스로 취약 서버를 구축해 테스트 및 실습을 해야 할 일이 많은데 가상 서버간의 네트워크 통신과 관련한

takudaddy.tistory.com

 

Link : https://cjwoov.tistory.com/11

 

[VM] VirtualBox 네트워크 설정 상세 분석

본 포스트는 https://technology.amis.nl/2018/07/27/virtualbox-networking-explained를 번역 & 가공하였습니다. 1. 서론 2. 네트워크 옵션 2.1 내부 네트워크 2.2 NAT 2.3 NAT 네트워크 2.4 호스트 전용 어댑터 2.5 브릿지 네

cjwoov.tistory.com

 

관련 개념은 위 두 링크를 참고바라며 아래는 위 개념을 어느정도 알고 있다고 가정하고 쓴다.

 * 호스트 : Windows 11

 * 게스트 : Ubuntu server 22

(한마디로 윈도우에 VirtualBox 설치했고, VirtualBox 사용해서 리눅스 돌리고 있다.)

 

♣ 어댑터에 브릿지

리눅스는 IPTime  과 같은 외부 공유기로부터 IP를 받는다.

제일 깔끔한 방법이나 공유기가 필요하다.

(그래서 출장갈 때 항상 공유기를 챙겼던 시절이 있었다. ㅋ)

 

♣ NAT

NAT 로 설정하면 리눅스는 10.0.2.15 의 IP를 할당받는다.

그리고 리눅스에 접속할 때 아래 192.168.56.1 로 리눅스에 접속할 수 있다.

 

그러나 포트포워딩 귀차니즘이 있고 게스트 간에 통신이 안되는 이슈가 있다.

 

♣ 호스트 전용 어댑터

호스트 전용 어댑터로 설정하면 아래처럼 192.168.56.xxx 로 IP 가 할당된다.

(이 IP는 192.168.56.1 가상 공유기로부터 DHCP 로 할당받는 것으로 알고 있다.)

그리고 호스트에서 이 IP 로 접속하면 된다. (NAT와 달리 포트포워딩 필요없음)

 

하지만 결정적으로 외부 접속 (인터넷 등)이 안된다. OTL

 

♣ NAT + 호스트 전용 어댑터 조합

표 출처 : 코덕 개발자 노트 ( https://cjwoov.tistory.com/11 )

 

VirtualBox 는 복수의 네트워크 어댑터 설정이 가능하다.

즉, 어댑터 1에는 NAT 로, 어댑터 2 에는 호스트 전용 어댑터로 설정하여 상호 단점을 보완하는 것이다.

 

이 후, 게스트 접속은 호스트 전용 어댑터 IP 로 (192.168.56.xxx ) 접속할 수 있으며

게스트의 외부망 접속은 NAT 를 통해 이루어지게 된다.

 

여기까지 하고 ifconfig 해보면 NAT 설정 IP (10.0.2.15) 만 나타나고

호스트 전용 어댑터 (192.168.56.xxx ) 는 안보인다.

 

그런데 ip addr 명령어로 보면 아래처럼 IP 할당이 안된 이더넷이 하나 보인다.

이 놈이 호스트 전용 어댑터의 이더넷임을 알 수 있다.

 

이에 (위의 경우) enp0s8 에 IP 를 할당해 준다.

 

본 글의 예제는 Ubuntu Server 22 환경이므로, 이 환경에 맞게 아래처럼 작업해 준다.

(당연한 얘기지만 각각의 게스트 OS 에 맞게 설정해 준다.)

 

아래는 호스트 전용 어댑터에 192.168.56.5 에 Subnet Mask 255.255.255.0 을 적용한 예이다.

$ cd /etc/netplan

$ sudo vi 00-installer-config.yaml  (파일명은 다를 수 있음)

그리고 아래처럼 호스트 전용 어댑터(enp0s8) 부분을 추가하였다.

그리고 아래 명령어 적용하거나 재부팅하여 ifconfig 해보면,

$ sudo netplan apply

$ ifconfig

 

아래처럼 호스트 전용 어댑터를 확인할 수 있다.

이제 게스트에서 아래 IP 192.168.56.5 로 접속하면 된다.

또한 NAT 어댑터를 통해 외부접속도 가능한 환경이 된다.

 

♣ 덧글

https://blog.startsomething.dev/2018/10/26/virtualbox-%EC%97%90%EC%84%9C-nat-%EC%99%80-host-only-network-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0/

 

위 글에 의하면 VirtualBox 전역도구에서

DHCP 사용 안함 및 "수동으로 어댑터 설정" 으로 설정하라고 하고 있다.

그런데 위 설정 시, DHCP 는 그냥 사용함으로 두어도 됐다.

혹시나 안되면 아래 설정 부분을 수정해 보자.

 

lsblk

♣ 도움받은 사이트

https://sacstory.tistory.com/entry/%ED%95%98%EB%93%9C-%EC%B6%94%EA%B0%80%EC%8B%9C

https://access.redhat.com/documentation/ko-kr/red_hat_enterprise_linux/8/html/security_hardening/encrypting-block-devices-using-luks_security-hardening

https://blog.tinned-software.net/automount-a-luks-encrypted-volume-on-system-start/

https://ko.linux-console.net/?p=10366

https://ko.linux-console.net/?p=439

https://ko.linux-console.net/?p=9102

 

갑님께서 생산용 서버의 Disk or File System 에

Encryption 을 적용해 달라고 요청을 했다.

 

처음 해보는 작업이라 여분의 서버에 1TB 짜리 HDD 하나 꽂고

우분투만 6번을 설치하고 수십번의 재부팅 끝에 아래처럼 정리해 본다.

 

♣ 목적

================================================

Ubuntu Server (22.04) 의 Data 영역을  LUKS 로 암호화한다.

윈도우에서 C에는 프로그램만, D에는 데이터를 저장하는데

D 드라이브만 암호화하는 것과 같다.

================================================

(Linux 초급 영역은 아닌지라 설명이 불친절할 수 있으며 나도 헷갈리는 부분의 용어, 개념 등이 틀릴 수 있다.)

 

♣ 파티션 전체에 LUKS 적용하기

이미 설치된 Ubuntu 전체에 적용할 수도 있으나 절차가 복잡하였다.

Ubuntu 설치 시, 아래처럼 파티션에 LUKS 를 적용하는 옵션이 있었다.

위와 같이 심플하게 전체 영역에 LUKS 를 적용할 수 있다.

이 후, 하위에 생성되는 볼륨은 자동으로 LUKS 가 적용된다.

 

♣ 특정 볼륨에 LUKS 적용하기

Ubuntu 설치 시, LUKS 를 적용하지 않는 케이스이다.

단, LVM 은 적용한다. (위 이미지에서 Set up this disk as an LVM group )

 

저 항목을 선택하면 자동으로 아래처럼 파티션이 설정된다.

보면 1TB 중 100GB 만 할당된다.

프로그램 등을 설치하면 이 100GB 영역에 설치될 것이다.

나는 Ubuntu 설치 후, 나머지 900GB 를 LUKS 로 암호화하고 Data 영역으로 사용할 예정이다.

 

♣ 볼륨 생성 후, LUKS 암호화 적용

Ubuntu 설치 후,

lsblk

하면 아래처럼 100GB 파티션만 표시된다.

이에 나머지 영역에 추가로 볼륨을 생성해 준다.

sudo lvcreate -l 100%FREE -n cryptdata-lv ubuntu-vg

아래처럼 ubuntu-vg 라는 Volume Group 에 cryptdata-lv 라는 Logical Volume 이 생성되었다.

이 볼륨을 LUKS 로 포멧시킨다.

LUKS 는 모두 cryptsetup 명령어로 수행된다. Ubuntu Server 22의 경우, 기본으로 설치되어 있다.

(luksFormat 에서 대문자 주의)

sudo cryptsetup luksFormat /dev/ubuntu-vg/cryptdata-lv

정말 Format할지 물어보는데 대문자로 "YES" 를 입력한다.

이 후, 본 암호화볼륨을 사용하기 위한 패스워드를 설정해준다. (까먹으면 ㅈ된다.)

 

♣ 암호화 볼륨에 접근하기 위한 매핑네임 생성

위 cryptdata-lv 볼륨은 LUKS로 암호화되어 있으므로 바로 사용할 수 없다.

이에 매핑네임을 생성해 준다.

매핑네임은 내가 붙인 이름이다. 본명 볼륨은 아니고, man 을 봐도 그냥 name 으로 되어 있다.

 

위에서 생성한 암호화된 볼륨 cryptdata-lv 에 mesdata 란 매핑네임을 생성해 주었다.

sudo cryptsetup luksOpen /dev/ubuntu-vg/cryptdata-lv mesdata

암호화 볼륨에 접근하기 위한 패스워드를 물어보며 위에서 설정한 패스워드를 입력한다.

이렇게 생성하면 /dev/mapper/mesdata 가 생성된다.

이놈을 원하는 file format 에 맞게 포멧해 준다. 난 xfs 로 포멧하였다.

sudo mkfs.xfs /dev/mapper/mesdata

lsblk 로 확인해 보면, 하위에 뭔가 생성된 것이 보인다.

즉, LUKS 매핑네임 mesdata 는 암호화된 볼륨에 접근하기 위한 통로로 여기면 얼추 맞을 듯 하다.

 

♣ 암호화 영역 mount

이제 위에서 생성한 암호화 영역을 사용하기 위해 특정 디렉토리로 마운트한다.

나는 임의로 루트에 mesData 란 디렉토리를 생성했다.

sudo mkdir /mesData

그리고 위 mesdata 매핑네임을 마운트한다.

sudo mount /dev/mapper/mesdata /mesData

이상없으면 아래처럼 매핑네임이 /mesData 디렉토리로 성공적으로 마운트됐음을 확인할 수 있다.

이제 마운트된 /mesData 에 데이터를 저장하면 데이터는 암호화되어 하드디스크에 저장된다.

누군가 악의를 품고 하드디스크를 훔쳐서 읽으려 해도 암호화되어 있으므로 읽을 수 없다.

 

이 상태에서 재부팅하면, 아래 상태로 돌아간다.

그러면 다시 매핑 네임 생성해주고 -> 비밀번호 입력하고 -> 마운트 후, 사용하면 된다.

 

♣ 부팅 시, 자동 매핑네임 생성 (패스워드 입력모드)

재부팅할 때마다 위와 같이 매번 매핑네임을 생성하는 것은 비효율적이다.

/etc/crypttab 파일을 수정하여 부팅 시, 매핑네임을 생성할 수 있다.

 

1. 우선, 암호화된 볼륨의 UUID 를 얻는다.

cryptdata-lv 에 본인이 생성한 볼륨명을 적어준다.

sudo cryptsetup luksUUID /dev/ubuntu-vg/cryptdata-lv

 

2. /etc/crypttab 파일을 수정한다.

아마도 위 절차를 거쳤으면 /etc/crypttab 파일이 있을 것이다.

여기에, 본인이 생성할 매핑네임, 위 1번에서 얻은 UUID, none, luks 를 적어준다.

아래는 예시이다. UUID= 부분에 주의한다.

mesdata UUID=6c2a7380-9214-4245-a946-29275f58e5d4 none luks

3. 위와 같이 저장하고 재부팅하면 부팅할 때 암호화 볼륨의 패스워드를 물어본다.

입력 후, lsblk 하면 매핑네임이 생성된 것을 확인할 수 있다.

 

4. 이 후, 디렉토리에 마운트하여 사용한다. (자동 마운트는 저 아래에서~ )

 

♣ 부팅 시, 자동 매핑네임 생성 (키 파일 등록모드)

위와 같이 부팅 시, 패스워드를 입력하는 것이 좀 더 강력한 보안이 될 수 있다.

그러나 주어진 환경이 여의치 않을 수 있다.

 

필자는 서버가 베트남 공장에 설치되어 원격으로 작업을 진행한다.

그런데 공장 사정으로 서버를 재부팅할 일이 생기면, 그 때마다 서버 관리자는

원격으로 접속하여 암호화 볼륨의 패스워드를 입력해야 한다.

만약 입력할 수 없는 상황이면, 최악의 경우 생산 라인이 스톱된다.

 

이에 패스워드 입력 없이 매핑네임을 생성하는 방법을 기술한다.

 

1. 키 생성

특정 양식이 있는 것이 아니라 아무 파일이나 된다.

다른 사이트를 살펴봐도 랜덤하게 키를 생성하는 것을 볼 수 있다.

아래는 4k 짜리 랜덤한 파일을 생성하는 명령어이다.

dd if=/dev/urandom of=./keyfile bs=1024 count=4

 

2. 파일을 /etc 밑으로 옮긴다. (꼭 etc 아니어도 상관 없다.)

sudo mv keyfile /etc

 

3. 해당 키를 암호화 블록에 등록한다. (패스워드 입력 필요)

sudo cryptsetup luksAddKey /dev/ubuntu-vg/cryptdata-lv /etc/keyfile

 

아래처럼 키 등록이 됐는지 체크할 수 있다.

sudo cryptsetup luksDump /dev/ubuntu-vg/cryptdata-lv

 

4. crypttab 파일에 키를 기재한다.

/etc/crypttab 파일의 none 위치에 키 위치를 적어준다.

5. 재부팅 후, lsblk 로 매핑네임이 생성됐는지 확인한다.

 

♣ 디렉토리에 자동 마운트

서브 주제이기에 간단히 다룬다. /etc/fstab 을 열어 아래처럼 추가해주고 재부팅하면

매핑네임 mesdata 가 디렉토리 mesData 에 마운트된다.

/dev/mapper/mesdata /mesData xfs defaults 0 0

 

♣ 데이터 저장위치 변경

이 후, 주요 데이터가 저장되는 위치를 /mesData 로 설정하여

데이터 저장 시 암호화를 적용한다.

 

 

 

 

♣ 도움받은 사이트

설치 관련 : https://www.jimmdenton.com/hp-raid-tools-ubuntu/
설치 관련 : https://downloads.linux.hpe.com/SDR/project/mcp/
키 관련 : https://downloads.linux.hpe.com/SDR/keys.html

 

HP 서버에서 HDD 를 관리하는 Smart Storage Administrator(SSA) 툴 설치법입니다.

 

♣ 1. HP PGP Public Key 설치

아래 명령어로 HPE PGP Public Key 를 설치합니다.

 

// sudo 로 비밀번호 한번 입력하기 위한 용도

$ sudo ls 

// 키 다운로드 및 추가하기

$ curl https://downloads.linux.hpe.com/SDR/hpPublicKey2048.pub | sudo apt-key add -
$ curl https://downloads.linux.hpe.com/SDR/hpPublicKey2048_key1.pub | sudo apt-key add -
$ curl https://downloads.linux.hpe.com/SDR/hpePublicKey2048_key1.pub | sudo apt-key add -

 

♣ 2. Source list 에 HPE Repository 추가

아래 focal 은 Ubuntu 버전의 닉네임(?) 입니다.

전 Ubuntu 20.04 (Focal Fossa) 이기에 focal 이라 썼으며 OS 버전에 맞게 수정해야 합니다.

 

$ sudo add-apt-repository 'deb http://downloads.linux.hpe.com/SDR/repo/mcp focal/current non-free'

 

그리고 패키지 업데이트를 진행합니다.

$ sudo apt-get update

 

추가한 Repository 에서 무사히ㅋ 업데이트가 진행됐습니다.

 

♣ 3. 패키지 설치, 진행

아래 명령어로 패키지를 설치합니다.

$ sudo apt-get install ssa ssacli ssaducli

 

아래와 같이 무사히 설치가 됐습니다.

 

♣ 4. 확인

설치 후, 아래 명령어로 HDD 상태를 확인해 봅니다.

$ sudo ssacli ctrl all show config

 

현재 1.2TB 짜리 하나 꾲혀 있으며 Raid 0 으로 세팅됨을 확인할 수 있었습니다.

이상, 설치 성공한 글을 마칩니다. ㅋ

 

프로그램에 파일을 불러들여 BLOB 타입에 Insert, Update 하는 것을

구현하긴 했으나 쿼리문 수정 등 귀찮은 바가 몇개 있었다.

 

용량이 작은 파일을 BLOB에 업데이트할 때 유용할 것으로 보인다.

 

1. 파일의 HEX 값을 구한다.

나같은 경우, 회사에서도 무료인 HxD Hex Editor 를 사용한다.

장점은 파일을 읽은 후, 복사, 붙여넣기 하면 Hex 값만 깔끔하게 가져온다.

 

파일을 읽은 후, 복사 붙여넣기 하면 HEX 값만 얻는다.

5E58410D0A5E4259322C322E305E46530D0A5E5345453A5548414E47554C2E4441545E46530D0A5E4357312C453A4B464F4E54332E464E545E434932365E46530D0A5E464F35302C35305E41314E2C34302C34305E4644C7D1B1DBC3E2B7C2C5D7BDBAC6AE5E46530D0A5E5051322C312C312C595E46530D0A5E585A0D0A

 

이 후, 얻은 HEX 값을 아래 쿼리문에 넣으면 된다.

나는 common_key 테이블 밑에 data 라는 BLOB 에 값을 넣었다.

(원래 데이터보다 짧게 기재했다. 실제는 훨씬 길 것이다.)

 

update common_key set data=UNHEX('5E58410D0A5E425932~~~') where no=10 ;

 

♣ 확인

값을 넣었으니 CRC32 값을 비교하여 올바르게 들어갔는지 확인한다.

Select hex(crc32(data)) from common_key where no=10 ;

 

 

그리고 실제 파일의 CRC32 를 구해본다.

 7-Zip 프로그램을 쓰면 우클릭 시, Checksum 값을 쉽게 얻을 수 있다.

위와 같이, 실제 파일과 DB 값 간에 CRC32값이 일치하였고,

원하는대로 DB에 파일을 업로드 됨을 확인하였다.

 

뭐, 각자 툴 있으면 그거 쓰면 되고, 가끔 귀차니즘 발동할 때 쓸 수 있겠다.

 

인터넷 찾아보면 코드간 변환은 많이 있으나 MultiByte Char 과 WideChar의 설명은 흔치 않는 듯 하다.

이에 정리해 본다.

 

♣ MultiByte Characters

char ch[32] ; 와 같이 배열이 있다 치고, 이 배열에 한글을 입력한다. 라고 심플하게 기억하자.

char[0] 에 한글을 입력할 수 없다.

이에 여러 char 예를들어 char[0] ~ char[2] 와 같이 여러 바이트를 사용해 한글을 저장한다.

이를 MultiByte Char 라고 표현한다.

대표적으로 ANSI (CP949), UTF-8 이 있겠다.

 

 

 

 

♣ Wide Characters

넓은 캐릭터 ㅋ. Wide Char 의 경우, 위와 같이 가변길이가 아니라 모두 동일한 길이를 갖는다.

변수 Type 은 w_char 또는 TCHAR 이 될 것이다.

(TCHAR은 환경에 따라 64비트가 아닐 수도 있으나 대부분의 개발환경이 64비트이므로 TCHAR 도 포함했다.)

 

예를 들면 아래와 같다.

 

 

♣ 인코딩 선택

위 코드값 생성은 메모장을 추천한다.

메모장에서 텍스트 입력 후, 다른이름으로 저장하면 아래 인코딩 방식이 나온다.

 

원하는 타입으로 저장 후, Hex 코드를 보면 된다.

참고로, 나는 윈도우에서 HxD 프로그램을 사용해 Hex 코드를 본다.

무료이고 회사에서도 쓸 수 있다.

 

 

♣ 인코딩 변환

Windows API 는 인코딩 변환에 두 함수를 제공한다.

 - WideCharToMultiByte ( )

 - MultiByteToWideChar ( )

 

위 개념을 이해했다면 이제 함수명을 보고 이게 어떤 의미인지 파악이 쉬울 것이며 아래처럼 정리할 수 있다.

 

UTF-8 -> Unicode : MultiByteToWideChar ( ) 함수 사용

ANSI -> Unicode : MultiByteToWideChar ( ) 함수 사용

 

Unicode -> UTF-8 : WideCharToMultiByte ( ) 함수 사용

Unicode -> ANSI : WideCharToMultiByte ( ) 함수 사용

 

여기서 의문점이 생긴다.

그렇다면 UTF-8 에서 ANSI 는 어떻게 변환하는가, 또 그 반대는 어떻게 변환하는가?

한번에는 변환이 안되고 아래와 같이 Unicode 를 한번 거쳐야 한다 (라고 알고 있다.)

 

UTF-8 -> Unicode -> ANSI : 

ANSI -> Unicode -> UTF-8

 

함수 호출은 MultiByteToWideChar ( ) -> WideCharToMultiByte ( ) 가 된다.

 

 

코드 변환하는 예제는 인터넷에서 찾으면 무궁무진하며

무작정 복붙하지 말고 위 개념을 익힌 후, 코드를 보면 바로 이해가 갈 것이다.

 

삽질은 나 하나로 충분하다. ㅋㅋ

 

'삽질미학 > C,C++' 카테고리의 다른 글

2차원 벡터를 포인터로 넘겨 사용하기  (0) 2024.02.13
함수 strtok() 의 대안, strsep()  (0) 2018.07.12
[C++] 삼각형 구조의 하향식 길찾기  (0) 2017.07.18
[C++] 조합 구하기  (0) 2017.07.13
[C++] 순열 구하기  (0) 2017.07.12

♣ 도움받은 사이트

https://8gwifi.org/docs/python-rsa.jsp

https://stackoverflow.com/questions/21327491/using-pycrypto-how-to-import-a-rsa-public-key-and-use-it-to-encrypt-a-string

 

♣  Python 에서 RSA 를 사용하기 위해 아래와 같이 pycryot를 설치하였다.

1. pycropto 설치 (Linux)

https://pypi.python.org/pypi/pycrypto 에서 모듈을 다운받는다.

아래와 같이 설치한다.

$ sudo python3 setup.py install

 

설치하면... time.clock() 함수가 없다고 뜬다.

찾아보니 Python 3.8 부터 위 함수가 폐기됐다고 한다. 아오~~

결국 Python 2.7 에서 설치하였다.

 

리눅스에서

$ python -> 2.7

$ python3 -> 3.8 로 실행되기에 python 으로 실행했다.

 

♣  Python 에서 Public Key, Private Key 생성하기

Public Key, Priavte 키 생성 (PEM 방식)

from Crypto.PublicKey import RSA

#Export RSA public/private KEY in PEM format
key = RSA.generate(2048)
privKey = key.exportKey('PEM')
pubKey = key.publickey().exportKey('PEM')

 

#save PEM key into the file
with open('private.pem', 'w') as file:
        file.write(privKey)
        file.write('\n')

 

with open('public.pem', 'w') as file:
        file.write(pubKey)
        file.write('\n')

 

테스트를 위해 plain.txt 파일을 만들고 아래와 같이 내용을 추가한다. (변경 가능0

Open SSL, Python Test

 

♣  OpenSSL 로 키 테스트

Python에서 생성한 키로  OpenSSL 에서 테스트해 본다.

 

1. Public key 로 암호화하기

 $ openssl rsautl -encrypt -inkey ./public.pem -pubin -in ./plain.txt -out encrypt.txt

 

2. Private Key 로 복호화하기

 $ openssl rsautl -decrypt -inkey private.pem -in encrypt.txt -out decPlain.txt

 

3. plain.txt 와 decPalin.txt 의 내용이 같은지 확인해 본다.

 

♣  Python 에서 Public Key 로 암호화 하기.

위에서 생성한 public key 로 아래와 같이 암호화 해 본다.

 

Encrypt Plain Text by public key

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5 as Cipher_PKCS1_v1_5

fPri = open('private.pem', 'rb')
fPub = open('public.pem', 'rb')

 

prikey = RSA.importKey(fPri.read())
pubKey = RSA.importKey(fPub.read())

 

#RSA Encryption Using Public Key
fp = open('plain.txt', 'rb')

cipherKey = Cipher_PKCS1_v1_5.new(pubKey)
cipherText = cipherKey.encrypt(fp.read())

 

fEnc = open('encrypt.txt', 'wb')
fEnc.write(cipherText)

 

# archive
#cipherText = pubKey.encrypt(fp.read(),32)
#cipherMsg = cipherText[0]


fPri.close()
fPub.close()
fp.close()
fEnc.close()

위에서 암호화 한 문장 (encrypt.txt) 을 openSSL 에서 복호화할 수 있다.

 

♣ Python 에서 Private Key 로 복호화 하기.

위에서 생성한 Private Key 로 아래와 같이 복호화 한다.

 

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5 as Cipher_PKCS1_v1_5

fPri = open('private.pem', 'rb')

prikey = RSA.importKey(fPri.read())

 

#RSA Encryption Using Public Key
fEnc = open('encrypt.txt', 'rb')

cipher = Cipher_PKCS1_v1_5.new(prikey)
cipherText = cipher.decrypt(fEnc.read(), None).decode()

 

fDec = open('decPlain.txt', 'wb')
fDec.write(cipherText)

 

fPri.close()
fEnc.close()
fDec.close()

저 위의 OpenSSL 에서 암호화 한 후,

Python 에서 위와 같이 복호화하면 동일하게 복호화 되었다.

도움받은 사이트 :

* 도커란 무엇인가?

https://dev-youngjun.tistory.com/2

* Ubuntu 18 에 Docker 설치하기

https://blog.cosmosfarm.com/archives/248/%EC%9A%B0%EB%B6%84%ED%88%AC-18-04-%EB%8F%84%EC%BB%A4-docker-%EC%84%A4%EC%B9%98-%EB%B0%A9%EB%B2%95/

* docker-compose 설치하기

https://sarc.io/index.php/cloud/1701-docker-compose-latest-version-install

 

 

팀 서버에 Redmine 설치하려다 결국 docker 란 놈까지 설치하게 되었다.

 

도커는... 예를 들어면 VMware 나 virtual box 비스무리한 개념으로 이해하면 될 듯 하다.

 

예를 들어,

VirtualBox 에 우분투를 설치하고 열심히 이것저것 설치해서 개발환경을 구축했다고 치자.

그런데 옆에 동료가 나중에 합류해서 또 개발환경을 구축하게 된다면,

내가 했던 삽질을 또 반복할 것이다.

이 때 내가 만든 VirtualBox 이미지만 동료에게 전달하면 더이상 구축할 필요가 없는 것이다.

 

docker 도 이런 비슷한 개념인데 자세한 건 위 사이트에 잘 정리되어 있다.

 

암튼,

Redmine 설치하는 거 은근히 까다롭다. 사전에 설치할 것도 몇개 있고 설정도 해야하고

버전도 맞춰줘야 한다. 열심히 따라 했는데... 안돌아간다. ㅆㅂ

 

그런데 다른 사람이 이미 docker 를 이용해 redmine 을 설치해서 인터넷에 올려놨다. (무식한 표현... ㅋ)

그럼 나는 그 docker-redmine 이미지를 받아 로딩만 하면 된다.

위에 예처럼 말이다.

Redmine 의 경우, sameersbn/redmine 이 유명한 듯 하다.

 

본 글에선 docker 설치를 기술한다.

 

Ubuntu 버전마다 설치 옵션이 살짝 다른 듯 하다.

아래는 Ubuntu 18 의 설치법이며 다른 버전은 인터넷에서 쉽게 찾을 수 있었다.

 

 Docker 설치 (Ubuntu 18)

$ sudo apt update
$ sudo apt install apt-transport-https ca-certificates curl software-properties-common
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
$ sudo apt update
$ apt-cache policy docker-ce

 

$ sudo apt install docker-ce

 

 

여러개의 docker 를 서로 연관지을 수 있고, docker 와 외부 프로그램간 연동도 가능한 듯 하다.

이를 위해서 docker-compose 를 설치해 준다.

 

Docker Compose 설치 

// 1. jq 설치 (아래 버전 확인용)

$ sudo apt-get install jq

 

// 2. docker 의 최신 버전 확인

$ curl --silent https://api.github.com/repos/docker/compose/releases/latest | jq .name -r

 

// 3. 아래 $(VERSION) 부분을 위에서 얻은 최신버전으로 교체 후 설치

$ sudo curl -L https://github.com/docker/compose/releases/download/${VERSION}/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose

 

// 4. 권한 수정

$ sudo chmod 755 /usr/local/bin/docker-compose

 

이제 docker-redmine 을 설치할 준비가 되었다.

 

docker-redmine 은 다음 글에 쓴다.

 

연구소에서 제조사업부로 소속을 옮기니 개발 관련된 건 다 자갈밭이었다.

 

연구소에 있을 땐 후임들이 알아서 삼바 설치하고 운용하고 했는데,

여긴 그 비싼 서버 갖다놓고도 쓰지도 않고 방치...

 

그래서 리눅스 서버에 삼바 설치하고 공용폴더 만들어줬더니

뻥 좀 붙여서 기립박수 받았다. ㅎㅎ

 

암튼, 백만년만에 삼바 설치하면서 나온 이슈 정리해 본다.

 

사용자 : user1, user2, user3

그룹명 : product

 

♣ 사용자 생성하기

$ sudo useradd -m -g product user1

  - m 옵션을 줘야 home 밑에 계정 디렉토리를 생성한다.

  - g 옵션을 주고 그룹을 할당한다. (이거 빼먹었으면 usermod 에서 추가할 수 있다.

 

♣ 신규 사용자의 passwd 설정

$ sudo passwd user1

 

♣ 위 모든 걸 한방에 할 수 있는게

$ sudo adduser user1

 - useradd 가 아니라, adduser 로 하면 이름 등등을 기입하라고 나온다.

 

♣ 사용자 확인

$ sudo vi /etc/passwd

 

♣ 그룹 확인

$ sudo vi /etc/group

 

♣ 기존 사용자를 기존 그룹에 추가하기

$ sudo usermod -a -G product user1

 

♣ 삼바에 신규 사용자 추가하기

$ sudo smbpasswd -a user1

 

♣ 삼바 설정

 - 사용자는 개인 디렉토리와 공용디렉토리 common 만 보인다.

 - common 에서 남이 작성한 파일은 다른 사람이 수정할 수 있어야 한다.

 - 제조팀 이외는 접근할 수 없다.

 

♣ 삼바 개인 설정

/etc/samba/smb.conf 파일을 열고 아래와 같이 팀원들을 추가함.

[user1]
    comment = user1 HOME
    path = /home/user1
    public = no
    writable = yes
    browseable = no
    valid users = user1

 

♣ 삼바 공용 공간 설정

/home/common 디렉토리를 공용 디렉토리로 만들고,

디렉토리의 group 권한을 product 로 둔다.

/etc/samba/smb.conf 에서 아래와 같이 작성하였다.

user1 이 올린 파일을 user2 가 수정할 수 없었는데 force group 으로 해결했다.

 

[common]
    comment = common HOME
    path = /home/common
    public = yes
    writable = yes
    directory mode = 0775
    create mode = 0775
    force group = product
    valid users = user1 user2 user3

 

추가사항

♣ 사용자 삭제

$ sudo userdel [user]  // 계정만 삭제

$ sudo userdel -r [user]  // home directory 포함하여 삭제 (그룹에서도 삭제되려나?)

 

 

출처 : https://www.lesstif.com/pages/viewpage.action?pageId=61899162

 

이래저래 찾은 방법 중에 이 방법이 제일 심플하고 바로 적용이 되었다.

 

전체 타임존 목록 보기

♣ 이 목록을 보고 바꾸려는 timezone 을 확인한다.

 $ timedatectl list-timezones

 

♣ 타임존 바꾸기 (ex) 한국)

 $ timedatectl set-timezone 'Asia/Seoul'

 

♣ GMT 로 설정하기

 $ timedatectl set-timezone 'GMT'

 

♣ 원하는 시간으로 변경되었는지 확인하기

 $ date

 

 

Linux 서버(Ubuntu Server)에 파일을 옮기거나 할 때 FTP를 사용해도 되지만

FTP 설치 등의 귀차니즘이 발동한다면......

 

Linux 서버의 IP : 192.168.15.25 라 하고...

(hahaite 는 제 계정입니다. 예시로 그냥 적음. ㅋ)

 

♣ SFTP 접속 후, 업로드하기

 $ sftp hahaite@192.168.15.25

password : (패스워드 입력)

SFTP > put test.tar

SFTP > quit

 

♣ SFTP 접속하면서로드하기

 $ sftp hahaite@192.168.15.25 <<< $'put test.tar'

 

♣ SSH Private Key 를 사용하여 접속하기

(ssh key 사용법은 본 글에서는 다루지 않는다.)

-I 옵션 뒤에 SSH Private Key 위치를 적어주면 password 없이 접속이 된다.

 $ sftp -i ~/.ssh/hahaite 192.168.15.25  (hahaite 는 제 SSH Private Key 임)

 

만약 private Key 가 ~/.ssh/id_rsa 로 되어 있으면 따로 안적어도 된다.

 $ sftp 192.168.15.25

(접속 끝)

 

 

♣ Batch File 을 이용한 업로드

SFTP 접속 시, Batch File 을 적어주면 접속 후, Batch File 안의 SFTP 명령어를 수행한다.

 

ex) mysftp.sh 란 파일을 만들고 아래 내용을 추가한다.

cd backup

put test.tar

 

그리고 -b 옵션에 위 파일을 기재해 준다.

 $ sftp -b ./mysftp.sh 192.168.15.25

 

♣ SFTP 자동 업로드 구성

매일 새벽 1시에 자동으로 DB 가 백업되고,

이 백업 파일을 다른 서버에 SFTP 로 옮긴다고 가정해 보자.

 

매일 새벽 1시가 되면 backup.sh 란 쉘스크립트가 자동 실행된다.

 

DB Backup 시, 날짜 + 시간이 기록되도록 한다.

(Root2020 은 root 계정의 MariaDB의 비밀번호임)

 

OPTIONS="-u root -pRoot2020 --routines --extended-insert --quick --single-transaction"

BACKUP_DIR=/home/hahaite/MDB_Backup

 

♣ SFTP를 이용한 Database Backup 수행

mysqldump $OPTIONS $DB_NAME > $BACKUP_DIR/DB_$(date "+%Y-%m-%d_%H").sql

# SFTP 에 사용할 Batch File 을 생성한다.

# 이 때, 위에서 언급한 SSH Key 를 사용해 자동 로그인되도록 한다. password 를 물으면 안되니까...

# 첫줄은 > 가 한개, 둘째줄은 > 가 두개 ( >> ) 임을 주의한다.

echo "cd dbTest" > mysftp.sh
echo "put ${DB_NAME}_$(date "+%Y-%m-%d_%H").sql" >> mysftp.sh
cat ./mysftp.sh

 

batch File 을 사용하여 업로드한다.

sftp -b ./mysftp.sh 192.168.15.25

=============================

아래는 내가 만든, 여러개의 DB 를 백업하는 쉘스크립트 예제이다.

 

#!/bin/sh

 

OPTIONS="-u root -pRootPassword --routines --extended-insert --quick --single-transaction"
BACKUP_DIR=/home/hahaite/MDB_Backup

DB_LIST="mydb_1"
DB_LIST="$DB_LIST mydb_2"
DB_LIST="$DB_LIST mydb_3"
DB_LIST="$DB_LIST mydb_4"

for DB_NAME in $DB_LIST
do
        mysqldump $OPTIONS $DB_NAME > $BACKUP_DIR/${DB_NAME}_$(date "+%Y-%m-%d_%H").sql
        echo "cd dbTest" > mysftp.sh
        echo "put ${DB_NAME}_$(date "+%Y-%m-%d_%H").sql" >> mysftp.sh
        cat ./mysftp.sh
        sftp -b ./mysftp.sh 192.168.15.25
done

 

♣ SFTP를 이용한 Database Backup 수행

위 스크립트는 내가 직접 실행하면 문제가 없지만 타이머에 의해서 실행되면 에러가 난다.

내가 직접 실행하면 내 계정으로 실행하는 것이지만,

타이머로 실행되면 root 계정으로 실행되기 때문이다.

 

이에 위 do ~ done 부분을 아래와 같이 수정하여 해결하였다.

-u 옵션에 내 계정(hahaite)를 지정해 주고, ssh private key 를 사용하여 백업서버에 접속하도록 하였다.

 

do
        mysqldump $OPTIONS $DB_NAME > $BACKUP_DIR/${DB_NAME}_$(date "+%Y-%m-%d_%H").sql
        echo "cd dbTest" > mysftp.sh
        echo "put ${DB_NAME}_$(date "+%Y-%m-%d_%H").sql" >> mysftp.sh
        cat ./mysftp.sh
        sudo -uhahaite sftp -i /home/hahaite/.ssh/hahaite -b $BACKUP_DIR/mysftp.sh 192.168.15.25

done

 

은근히 자주 쓰이는 Alter 명령어를 정리함.

아래 없는 내용은 계속 추가할 예정임.

 

앞에 my, old, new 가 붙은 건 사용자가 정의한 값이다.

 

♣ Primary Key 설정

Alter Table my_table Add constraint primary key (my_field) ;

 

♣ 열 삭제

Alter Table my_table Drop my_column ;

 

♣ 열 추가

(뒤에 열 type 은 맞게 수정해 준다)

Alter Table my_table Add new_column VARCHAR(32) ;

 (my_column 뒤에 new_column 을 추가함)

Alter Table my_table Add new_column VARCHAR(32) After my_column ;

 (맨 앞에 new_column 을 추가함)

Alter Table my_table Add new_column VARCHAR(32) First ;

 

 

♣ 열 이름 변경

(새 열 뒤에 type 이 추가돼야 함)

Alter Table my_table Change old_column new_column VARCHAR(32) ;

 

♣ 열 속성 변경

Alter Table my_table Modify my_column VARCHAR(32) ;

 

♣ AUTO_INCREMENT 열 재정렬

아래와 같이 하면 10번부터 입력이 되게 된다.

Alter Table my_table AUTO_INCREMENT=10 ;

 

♣ 인덱스에 새 항목 추가

Alter Table my_table ADD INDEX(my_column) ;

 

 

 

 

 

 

도움받은 사이트 : https://developer.zebra.com/thread/34694

 

ZPL 의 ^GF 명령어를 통해 Bitmap 데이터를 조합해 Zebra 프린터로 출력할 수 있습니다.

아래는 Bitmap 파일을 읽어 Zebra 프린터로 출력하기 위한 ZPL 코드를 생성하는 방법을 정리합니다.

 

테스트는 Visual Sutdio 2015 Express 에서 진행했으며

아래 test() 함수는 BMP 헤더 구조체만 정의하면 리눅스에서도 사용할 수 있을겁니다.

 

 

♣ Bitmap 이미지 생성

Zebra 프린터는 흑백으로 출력합니다.

이에 출력하려는 bmp 이미지를 단색만 사용하도록 설정합니다.

저는 윈도우 그림판에서

  1. 메뉴
  2. 다른이름으로 저장
  3. BMP 그림 선택
  4. 아래 파일형식 : 단색 비트맵 (~~) 선택

으로 저장합니다.

 

 

첨부파일 hosang.bmp 는 위 이미지를 단색으로 저장하였고 크기는 67x32 입니다.

(※ hosang.bmp 파일은 아래 이미지를 우클릭해서 다른이름으로 저장하여 사용하시기 바랍니다.)

 

  -> 이거 우클릭 후, 저장~

 

 

 

♣ Bitmap Padding

단색 Bitmap 의 경우, 한 pixel 을 저장하는데 한 bit 를 소모합니다.

한 Byte는 8bits 이니 위 이미지의 한줄인 67 픽셀을 저장하는데 72 Bits ( 9 Bytes) 가 소모될 겁니다.

 

하지만 실제 파일을 열어보면 80 Bits (10 Bytes) 가 사용됩니다.

즉 단색 bitmap 은 16 bits ( 2 Bytes) 단위로 끊어 처리합니다.

 

 

실제 이미지 크기는 67 x 32 이지만,

bmp 파일에 저장된 이미지 영역은 80 x 32 입니다.

이 때 80 은 16 의 배수입니다.

 

즉 파일의 이미지 데이터 영역은 80x32 / 8 = 320 Bytes 가 됩니다

 

이에 bmp Info 에서 실제 width (67px) 를 얻어 68~80 번째 데이터는 무시하는 작업이 필요합니다.

 

그리고 상하로 뒤집어 저장하는 bmp 의 이미지 데이터를 다시 원래대로 돌려 놓습니다.

 

♣ ZPL Code

ZPL 코드로 Bitmap 처리를 하기 위해선 ^GF 명령어를 이용합니다.

( ^GF 관련 설명은 Zebra 사의 ZPL 설명서 참조)

본 글은 ^GF 의 마지막 항목인 데이터 영역을 만들기 위함입니다.

 

아래 test() 함수는 단색 타입의 bmp 파일을 읽어 ZPL 의 Data 영역을 만드는 함수입니다.

 

 

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
int test()
{
    string strZpl ;
    char szText[128] ;
    char szBmp[32] ;
 
    BITMAPFILEHEADER bmFile ;
    BITMAPINFOHEADER bmInfo ;
 
    int px = 100 ;
    int py = 100 ;
 
    FILE* fpBmp = fopen("./hosang.bmp""rb") ;
 
    if(!fpBmp)
    {
//        MyLog("genBitmap fopen Error\r\n") ;
        return -1 ;
    }
 
    // read BMP Header and Infomation.
    fread(&bmFile, sizeof(BITMAPFILEHEADER), 1, fpBmp) ;
    fread(&bmInfo, sizeof(BITMAPINFOHEADER), 1, fpBmp) ;
 
    // Move Image Data offset.
    fseek(fpBmp, bmFile.bfOffBits, SEEK_SET) ;
 
    strZpl.clear() ;
    strZpl += "^XA" ;
 
    sprintf(szText, "^FO%d,%d", px, py) ;
    strZpl += szText ;
 
    int width = ((bmInfo.biWidth / 32) + 1) * 4 ;
    int mod = (bmInfo.biWidth) % 32 ;
    int realWidth = (bmInfo.biWidth + 7) / 8 ;
 
    int imageSize = realWidth * bmInfo.biHeight ;
    int shiftCount = 0 ;
 
    sprintf(szText, "^GFA,%d,%d,%d,", imageSize, imageSize, realWidth) ;
    strZpl += szText ;
 
    list<string>         listRow ;
    list<string>::iterator     listRowIter ;
 
    string strRow ;
 
    for(int ii = 0; ii < bmInfo.biHeight; ii++)
    {
        strRow.clear() ;
        for(int jj = 1; jj <= width; jj++)
        {
            fread(szBmp, 1, 1, fpBmp) ;
 
            if(jj > realWidth)
                continue ;
 
            if(jj == realWidth)
            {
                shiftCount = 8 - ((bmInfo.biWidth + 8) % 8) ;
                for(int kk = 0; kk < shiftCount; kk++)
                {
                    szBmp[0] |= ((szBmp[0] >> kk) | 0x01) ;
                }
            }
 
            szBmp[0] ^= 0xff ;
            szBmp[1] = '\0' ;
            sprintf(szText, "%02X", szBmp[0] & 0xff) ;
            strRow += szText ;
        }
 
        listRow.push_front(strRow) ;
    }
 
    listRowIter = listRow.begin() ;
    for(; listRowIter != listRow.end(); listRowIter++)
    {
        strZpl += *listRowIter ;
    }
    fclose(fpBmp) ;
 
    strZpl += "^FS";
    strZpl += "^XZ" ;
    
    FILE* fpZpl = fopen("./myZpl.txt""wt") ;
    fprintf(fpZpl, "%s", strZpl.c_str()) ;
    fclose(fpZpl) ;
 
    return 1 ;
}

 

위 함수가 실행되면 myZpl.txt 파일에 ZPL 코드가 아래와 같이 저장됩니다.

보기 편하게 줄로 나누어 봤습니다.

유효한 값에 형광펜을 칠해보면 이미지의 모양이 보입니다.

 

myZpl.txt

------------------------------------------

^XA^FO100,100^GFA,288,288,9,

0007C0000000003C00
0007C000001E003C00
0007C000001E003C00
0007C000001E003C00
1FFFFFF0001E003C00
1FFFFFF0001E003C00
1FFFFFF0001E003C00
1FFFFFF0001F003C00
00000000003F003FFE
001FF000007F803FFE
00FFFE00007FC03FFE
01FFFF0000F3E03FFE
03FFFF8001F1F83C00
07E00FC007E0FE3C00
078003C00FC07C3C00
078003C01F803C3C00
078003C00F00083C00
078007C00400003C00
07E00FC00000000000
03FFFF800001FF0000
01FFFF00000FFFE000
003FF800003FFFF000
0007C000007FFFF800
0007C000007E00FC00
0007C00000F8003C00
FFFFFFFE00F8003C00
FFFFFFFE00F8003C00
FFFFFFFE007E00FC00
FFFFFFFE007FFFF800
00000000003FFFF000
00000000000FFFE000
000000000001FF0000

^FS^XZ

 

위 ZPL 코드를 Zebra 프린터 (필자의 경우, ZM400 300dpi) 로 전송하면 아래와 같이 이미지가 출력됩니다.

 

 

 

 

 

 

 

 

 

 

+ Recent posts