Skip to main content

BigQuery 데이터를 LangChain 앱에 통합하기

· 3 min read

image

데이터는 모든 AI 솔루션에서 사용할 핵심 재료입니다.

평소 GCP 를 이용하는 편인데 BigQuery 를 통해 많은 로그들도 쌓고 많은 데이터들도 쌓는 편입니다.

요새 더 핫해진 LLM 어플리케이션에 BigQuery를 활용하려면 어떻게 해야 할까요?

이전에 설명했던 LangChain은 LLM과 같은 모델들을 어플리케이션에 쉽게 서빙할 수 있게 해주는 프레임워크입니다.

LangChain을 사용하면 모듈식 아키텍처와 사전 구축된 커넥터를 통해 LLM 개발을 단순화할 수 있습니다. BigQuery의 방대한 데이터를 LLM 모델에 사용해 어플리케이션에 이용할 수 있도록 하기 위해 LangChain의 BigQuery Data Loader를 사용하는 방법을 알아보려고 합니다.

LangChain 시작하기

우선 LangChain과 Vertex AI SDK 를 설치해 줍니다.

pip install --quiet google-cloud-aiplatform langchain

Vertex AI SDK를 시작해 주고 모델을 쿼리합니다.

# Initialize Vertex AI SDK
import vertexai
vertexai.init(project="<your-project-id>", location="us-central1")


# Query the model
from langchain.llms import VertexAI
llm = VertexAI(model_name="text-bison@001", temperature=0)
llm("What's BigQuery?")

LLM에게 BigQuery에 대해 문의하면 BigQuery에 대한 설명을 LLM으로 대신할 수 있습니다.

BigQuery는 기업이 모든 데이터를 매우 빠르게 분석할 수 있도록 지원하는 완전 관리형 페타바이트 규모의 분석 데이터 웨어하우스입니다. 빠른 성능, 확장성, 유연성을 제공하는 클라우드 기반 서비스입니다. BigQuery는 사용하기 쉽고 다른 Google Cloud Platform 서비스와 통합될 수 있습니다.

Data Loader 사용

우선 Big Query Library를 설치합니다.

pip install google-cloud-bigquery

이제 쿼리를 정의해 데이터를 로드해 보면

# Define our query
query = f"""
SELECT table_name, ddl
FROM `bigquery-public-data.thelook_ecommerce.INFORMATION_SCHEMA.TABLES`
WHERE table_type = 'BASE TABLE'
ORDER BY table_name;
"""


# Load the data
loader = BigQueryLoader(query, project="<your-project-id>", metadata_columns="table_name", page_content_columns="ddl")
data = loader.load()

쿼리는 각 테이블에 대한 테이블 이름과 DDL을 추출합니다.

loader.load()를 통해 데이터 변수에 데이터를 할당합니다.

첫 번째 Chain 작성

수행 방법은 다음과 같습니다. LCEL( LangChain Expression Language )을 사용하여 3단계로 체인을 정의합니다.

  1. 각 문서의 page_content (각 테이블의 DDL임을 기억하세요)를 content 라는 문자열로 결합하겠습니다 .
  2. 테이블 메타데이터의 결합된 세트인 콘텐츠를 전달하여 가장 가치 있는 고객을 찾기 위한 프롬프트를 만듭니다 .
  3. 프롬프트를 LLM에 전달합니다.
# Use code generation model
llm = VertexAI(model_name="code-bison@latest", max_output_tokens=2048)


# Define the chain
from langchain.prompts import PromptTemplate
from langchain.schema import format_document
chain = (
{
"content": lambda docs: "\n\n".join(
format_document(doc, PromptTemplate.from_template("{page_content}")) for doc in docs
)
}
| PromptTemplate.from_template("Suggest a query that will help me identify my most valuable customers, with an emphasis on recent sales:\n\n{content}")
| llm
)


# Invoke the chain with the documents, and remove code backticks
result = chain.invoke(data).strip('```')
print(result)

쿼리를 살펴보면

SELECT
users.id AS user_id,
users.first_name AS first_name,
users.last_name AS last_name,
users.email AS email,
SUM(order_items.sale_price) AS total_spend,
users.country AS country
FROM `bigquery-public-data.thelook_ecommerce.users` AS users
JOIN `bigquery-public-data.thelook_ecommerce.orders` AS orders
ON users.id = orders.user_id
JOIN `bigquery-public-data.thelook_ecommerce.order_items` AS order_items
ON orders.order_id = order_items.order_id
WHERE users.country = 'Japan'
GROUP BY users.id, users.first_name, users.last_name, users.email, users.country
ORDER BY total_spend DESC
LIMIT 10;

먼저 dry run 으로 시험해 보는 것이 좋습니다 . 

import google.cloud.bigquery as bq


client = bq.Client(project="<your-project-id>")
client.query(result).result().to_dataframe()

image

이제 BigQuery 데이터를 LLM 솔루션에 통합하는 방법을 살펴보았습니다. 직접 사용해 보려면 Generative AI 샘플 리포지토리 에서 제공되는 노트북을 사용해 실험해 볼 수 있습니다 .

localstorage는 그만 indexedDB 시작!

· 3 min read

image

localStorage는 오래 됐습니다!

image

엄청나게 오래된 것은 아니지만 2009년 쯤에 시작했고 디자인이 별로라 여러 가지 문제점들을 안고 있습니다.

첫번째로 문자열로만 저장할 수 있어서 다른 유형으로 저장하려면 포맷 변경을 항상 해야 하고 검색 시에도 그로 인해 검색 시에도 불편함이 많습니다.

두번째는 느리다는 것인데 데이터를 저장하고 검색하는 것이 느리기 때문에 빈번한 트랜젝션이 필요한 경우 유저 경험을 상당히 추락시킬 수 있습니다.

세번째는 작은 저장공간에 있습니다. 최대 용량이 5MB로서 너무 작습니다.

네번째는 직렬화 문제인데 데이터를 저장할 때 문자열로 항상 직렬화 해야 하고 사용할 때 역직렬화 해야 하는 문제로 여러 오류나 버그를 야기할 수 있습니다.

대안으로서 WebSQL이 있습니다. 이것에 대해 살펴보면,

image

WebSQL은 간단한 웹에서 간단하게 데이터베이스를 구현하기 위해 만들어졌는데 상당한 지원을 받았지만 결국은 다시 하락하고 있습니다.

WebSQL은 왜 하락하고 있는가?

가장 중요한 것은 WebSQL은 단일 공급업체에서 구현했기 때문에 다른 메인 브라우저 공급업체인 Mozila, Microsoft의 지원 부족과 W3C 표준화가 없어 상용 서비스에서 개발자들이 채택하지 않게 됩니다.

또한 IndexedDB와의 경쟁도 있는데 IndexedDB는 WebSQL과 달리 표준 크로스 브라우저 솔루션으로 설계되었기에 비교가 될 수 밖에 없었습니다.

마지막으로 여러 개발자들과 보안 전문가들은 WebSQL의 안전성에 대해 우려를 표명하고 있습니다. 권한 제어가 부족하고 SQL 스타일 취약점을 포함한 다양한 측면들이 존재하기 때문입니다.

쿠키는 또 어떤가?

쿠키는 말할 것도 없이 오래 됐습니다. 1994년에 만들어졌고 localStorage와 비교해서도 많은 문제를 가집니다.

크기가 도메인당 4KB로 제한되고 연결된 모든 도메인에 대한 모든 HTTP 요청과 함께 전송되어 불필요한 오버헤드가 발생하고 대역폭 사용량을 증가시킬 수 있습니다. 보안적으로는 XSS에 취약해 도메인에 대한 모든 요청에 자동으로 포함되어 악성 스크립트의 표적이 되기 쉽습니다.

IndexedDB를 사용해야 하는 이유!

image

IndexedDB는 localStorage와 달리 비동기식으로 작동해 차단 작업을 방지하며 더 나은 성능을 보장합니다.

또한 브라우저나 OS 및 사용 가능한 저장소에 따라 다르지만 localStorage 5MB 한도에 비해 훨씬 큰 저장소를 할당받을 수 있습니다.

그리고 데이터 유형을 강제를 줄이고 구조적으로 복제 알고리즘을 채택해 데이터 무결성을 보장해 신뢰성이 높고 구조화된 데이터로 저장 및 검색에 유리합니다.

하지만 사용 방법은 쉽지 않습니다. 그렇기에 우리는 라이브러리를 사용합니다.

용량이 작고 많은 것을 지원하진 않지만 퀵하게 사용하기 좋은 db64 라이브러리

 [GitHub - julienetie/db64: Practical IndexedDB API

Practical IndexedDB API. Contribute to julienetie/db64 development by creating an account on GitHub.

github.com](https://github.com/julienetie/db64)

버전 관리나 커서가 필요하다면 모든 사례를 잘 다룬 포괄적인 라이브러리인 idb 라이브러리도 있습니다.

결론

속도 이점과 비차단 기능, 여러 타입을 안정적이고 신뢰도 있게 저장할 수 있는 IndexedDB를 사용하세요!

NestJS를 Python으로 만든다면!?

· 4 min read

image

개발자들은 확장 가능하고 유지 관리하기 좋은 API에 대한 수요가 증가하면서 강력한 솔루션을 제공하는 프레임워크를 찾고 있다. Node.js 애플리케이션 구축을 위한 프레임워크인 NestJS는 종속성 주입, 데코레이터, 모듈식 아키텍처를 포함한 강력한 기능으로 인정을 받았다.

동시에 Generative AI는 대규모 언어 모델의 힘을 보여주는 GPT4와 같은 모델을 통해 놀라운 발전을 이루면서, 생성적 AI 애플리케이션에 대한 수요로 인해 다양한 API와 상호 작용해야 하는 확장 가능한 마이크로서비스를 구축하는 데 중점을 두게 됩니다. 라이브러리와 프레임워크로 구성된 광범위한 생태계를 갖춘 Python은 생성 AI 모델을 구현하는 데 탁월한 언어로 부상했고 다재다능함과 유연성 덕분에 이러한 애플리케이션의 요구 사항을 충족하는 확장 가능한 솔루션을 개발하기에 이상적이다. Python을 사용하면 개발자는 생성 AI의 발전을 활용하고 효율적이고 강력한 애플리케이션을 구축할 수 있다.

NestJS에서 영감을 받은 Python 프레임워크 PyNest!

PyNestNestJS의 모듈식 아키텍처FastAPI를 기반으로구축된 Python 프레임워크다. Python 개발자, 데이터 과학자, ML 엔지니어 및 데이터 엔지니어에게 확장 가능하고 유지 관리 가능한 API를 구축하기 위한 직관적이고 강력한 프레임워크를 제공하는 것을 목표로 한다.

핵심은 깨끗하고 체계적인 방식으로 API를 구성하는 문제를 해결한다. NestJS의 모듈식 아키텍처를 채택함으로써 PyNest를 사용하면 개발자가 문제를 분리하고 코드를 모듈, 컨트롤러, 서비스 및 공급자로 구성할 수 있습니다. 이 모듈식 접근 방식은 코드 재사용성, 테스트 가능성 및 유지 관리성을 향상하여 애플리케이션이 발전함에 따라 확장을 촉진하게 됩니다.

image

PyNest로 문제 해결

PyNest는 확장 가능하고 유지 관리 가능한 Python API 구축과 관련된 문제를 해결하는 데 기여하는 몇 가지 주요 기능을 제공한다.

1. Modular Architecture

NestJS에서 영감을 받은 PyNest의 모듈식 아키텍처는 코드 구성에 대한 구조화된 접근 방식을 제공하는데, module, controller, service 및 provider는 문제를 논리적으로 분리하여 코드 중복을 줄이고 유지 관리성을 향상시킨다.

image

2. Dependency Injection

PyNest는 종속성 관리를 단순화하고 테스트 가능성을 높이는 기술인 종속성 주입을 지원해서 개발자는 service와 provider를 controller에 쉽게 주입하여 더욱 깔끔하고 모듈화된 코드를 구현할 수 있다.

3. Decorators

Decorator는 PyNest에서 중요한 역할을 하며 개발자가 경로, 미들웨어 및 기타 애플리케이션 구성 요소를 간결하게 정의할 수 있도록 해준다. 코드 가독성을 향상시키고 개발자가 API의 핵심 기능에 집중할 수 있도록 해다.

4. Type Annotations

PyNest는 FastAPI 유형 주석 메커니즘을 활용하여 더 나은 도구 및 오류 방지 기능을 제공한다. controller, service 및 provider에 유형을 추가하여 개발자는 코드의 견고성과 유지 관리성을 향상시킬 수 있다.

5. Code Generation

PyNest에는 module, controller 및 기타 구성 요소에 대한 상용구 코드 생성을 자동화하는 코드 생성 도구가 포함되어 있어서 개발자의 시간이 절약되고 코드베이스의 필수 부분을 작성하는 데 집중할 수 있다.

6. Database Support

PyNest는 PostgreSQL, MySQL, SQLite와 같은 여러 데이터베이스에서 즉시 사용 가능한 지원을 제공한다.

Getting Started with PyNest

1. 새 프로젝트 생성 및 가상 환경 활성화

python -m venv venv && source venv/bin/activate

2. pip를 사용하여 PyNest를 설치한다.

pip install pynest-api

3. CLI를 사용하여 새 PyNest 프로젝트를 만든다.

pynest create-nest-app -n my_app_name
  • 참고 — 기본적으로 pynest는 SQLite 데이터베이스를 기본으로 하고 PostgreSQL 및 MySQL도 지원한다. 금방 MongoDB 및 기타 데이터베이스도 지원할 예정이다.
  • PostgreSQL로 앱을 생성하려면 아래와 같이 명령하면 된다.
pynest create-nest-app -n my_app_name -db postgresql

4. project directory로 이동한다.

cd my_app_name

5. uvicorn을 사용하여 서버를 실행합니다.

uvicorn "app:app" - host "0.0.0.0" - port "80" - reload

image

PyNest는 애플리케이션 내의 특정 기능을 위한 모듈 생성을 권장한다. 그러니 CLI를 사용해 간단히 모듈을 생성하면 좋다.

pynest generate-module -n module_name

결론

PyNest는 NestJS의 모듈식 아키텍처에서 영감을 받은 강력한 Python 프레임워크다. 이를 통해 Python 개발자는 Python의 강점과 NestJS 확장 가능하고 유지 관리 가능한 API를 구축할 수 있다. PyNest는 종속성 주입, 데코레이터 및 코드 생성을 위한 직관적인 구조와 지원을 통해 확장 가능한 Python API를 개발할 때 직면하는 과제를 해결한다.

생성적 AI에 대한 수요가 계속 증가함에 따라 Python은 생성적 AI 작업에 맞춤화된 마이크로서비스를 구현하기 위한 선택 언어로 부상했는데, PyNest의 모듈식 아키텍처와 확장 가능한 마이크로서비스 지원은 생성 AI 분야의 개발자에게 유용한 도구로 사용하기 좋다.

PyNest를 활용하여 API를 효과적으로 구성하고, pip를 통해 설치하고, Python 프로젝트에서 PyNest의 강력한 기능을 활용해 확장 가능하고 유지 관리가 가능하며 효율적인 API를 구축하길 바란다.

아래는 PyNest 문서를 읽어볼 수 있는 링크이다.

https://pythonnest.github.io/PyNest/

Cloud Armor를 활용한 Ciber Security

· 4 min read

image

서비스를 운영하다 보면 사이버 보안에 민감해지고 필요성을 느끼게 된다.

이전 서비스에서 사이버 보안을 위해 구글 클라우드에서 제공하는 Cloud Armor를 활용했었고 좋은 경험이었다.

끊임없는 사이버 위협의 시대에 디지털 인프라를 보호하는 것이 그 어느 때보다 중요해졌고 Google Cloud Armor는 끊임없이 진화하는 사이버 공격 환경을 방어할 수 있는데 유용했다.

DDoS 공격부터 정교한 웹 애플리케이션 취약점까지 사이버 위협은 많은 기업들에 상당한 위험을 초래하고 있고 공격들은 점점 더 정교한 기술을 사용하면서 포괄적이고 적응 가능한 보안 조치의 필요성이 중요해졌다.

사이버 공격의 유형

사이버 공격의 가장 일반적인 유형은 아래와 같다.

- 악성 코드: 데이터를 훔치거나 시스템을 손상시키거나 인질로 잡는 데 사용할 수 있는 악성 소프트웨어.

- 피싱(Phishing): 이메일, 문자 메시지 또는 웹사이트를 사용하여 사용자를 속여 개인 정보를 공개하거나 악의적인 링크를 클릭하도록 하는 일종의 사회 공학 공격.

- 랜섬웨어(Ransomware): 피해자의 파일을 암호화하고 이를 해독하기 위해 몸값을 요구하는 악성 코드 유형.

- DDoS 공격: 이러한 공격에는 대상 서버를 사용할 수 없도록 하기 위해 트래픽을 너무 많이 보내는 공격.

- SQL INJECTION(SQLi): SQL 데이터베이스의 취약점을 악용하여 데이터를 훔치거나 악성 코드를 실행하는 공격.

Google Cloud Armor의 WAF(웹 애플리케이션 방화벽)는 애플리케이션 계층 공격을 차단하고 SQL INJECTION, XSS(교차 사이트 스크립팅) 및 기타 일반적인 취약점으로 인한 위험을 완화한다.

또한 광범위한 사이버 공격으로부터 조직을 보호하는 데 도움이 된다.

아래 기능들을 보면 이렇습니다.

  • Web application firewall (WAF): WAF는 악성 트래픽을 차단하고 웹 애플리케이션에 대한 보안 정책을 시행합니다.
  • Intrusion detection and prevention system (IDS/IPS): IDS/IPS는 악성 네트워크 트래픽을 탐지하고 차단합니다.
  • DDoS protection: 대규모 공격도 완화하는 데 도움이 되는 강력한 DDoS 보호 기능을 제공합니다.
  • Security insights: 조직의 보안 상태에 대한 통찰력을 제공하여 잠재적인 취약점을 식별하고 해결하는 데 도움이 됩니다.

Cloud Armor 사용방법

Cloud Armor를 사용한 계기가 클라우드에 대한 경험이 적어 익숙치 않던 시절에 활용하게 됐었는데 몇 분 안에 배포할 수 있을 정도로 아주 간단하고 쉽게 사용할 수 있기 때문이었다.

우선 보안 정책을 만들고, 생성한 보안 정책을 보안을 적용하고 싶은 서버나 로드밸런서에 연결하면 끝!

보안 정책에서 아래와 같이 들어오는 요청에 대해 정책을 만들고 우선순위도 정할 수 있다.

image

보안 정책을 사용하면 애플리케이션이 Google Cloud, 하이브리드 배포 또는 다중 배포 환경에 배포되는지 관계없이 DDoS 및 기타 웹 기반 공격으로부터 서비스를 보호할 수 있다. Google Cloud Armor에는 다양한 사용 사례를 포괄하는 사전 구성된 보안 정책도 포함되어 있으니 자세한 내용은 링크를 참조하세요! Google Cloud Armor security policy overview.

PreConfigured WAF Rules

사전 구성된 웹 애플리케이션 방화벽(WAF) 규칙을 제공하는데, 이러한 규칙은 이미 만들어져 있고 업계 표준에서 가져온 수십 개의 공격 탐지 서명을 포함한다. 각 서명은 규칙 세트 내의 특정 공격 탐지 규칙에 해당한다. 이러한 사전 구성된 규칙을 사용하면 각 서명을 개별적으로 정의하는 수고가 줄어 다양한 트래픽 패턴을 간단히 평가할 수 있다.

Google Cloud Armor Managed Protection

Managed Protection은 DDoS 공격 및 기타 인터넷 위협으로부터 웹 애플리케이션과 서비스를 보호하는 데 도움이 되는 관리형 애플리케이션 보호 서비스이다. 로드 밸런서에 대한 상시 보호 기능을 제공하며 WAF 규칙에 대한 액세스를 제공다.

Threat Intelligence

Threat Intelligence를 사용하면 여러 카테고리의 위협 인텔리전스 데이터를 기반으로 전역 외부 Application Load Balancer 및 기본 Application Load Balancer 에 대한 트래픽을 허용하거나 차단하여 트래픽을 보호할 수 있다.

Cloud Armor 추가 팁

  • 보안 정책을 최신 상태로 유지하는 것이 좋다. 새로운 위협이 등장하면 보안 정책을 최신 상태로 유지하는 것이 중요하기 때문에 Cloud Armor는 서명 데이터베이스를 정기적으로 업데이트해 최신 위협으로부터 사용자를 보호한다.
  • 보안 로그 모니터링: Cloud Armor는 잠재적인 공격을 식별하고 조사하는 데 도움이 되는 보안 로그를 제공한다.

이러한 단계를 수행하면 보안이 강화되고 공격의 성공을 어렵게 만들 수 있다.

결론!

사이버 공격은 심각한 위협이지만 올바른 조치를 취하면 이러한 공격으로부터 보호할 수 있다. Cloud Armor는 광범위한 위협으로부터 웹 애플리케이션과 웹 서버를 보호하는 데 도움이 되는 강력한 도구로서 활용할 수 있고 초보자들도 사용할 수 있을만큼 쉽다.

Google App Script를 사용해 Google Spreadsheet에서 사용자 정의 함수를 통해 배열 처리하기!

· 4 min read

image

Google Spreadsheet에서는 Google Apps Script로 생성된 사용자 정의 기능을 사용할 수 있다.

사용자 정의 기능을 사용해 스프레드시트에 내장된 기능을 확장하는 기능을 만들 수 있는데, 어느 날 배열을 사용해 사용자 정의 함수를 실행해야 하는 상황이 발생한다면 어떻게 해야 할까?

상황을 가정하자면 아래와 같다.

  • "A1:A5" 셀에는 각각 "a1", "a2", "a3", "a4" 및 "a5" 값이 있습니다.
  • "B1:B5" 셀에는 각각 "b1", "b2", "b3", "b4" 및 "b5"의 값이 있습니다.

custom script를 보면,

function SAMPLE(argument1, argument2) {
return JSON.stringify({ argument1, argument2 });
}

사용자 정의 함수 =SAMPLE(A1,B1)을 셀 "C1"에 넣으면 다음 결과가 얻어집니다.

image

완전 간단히 function을 만들고 그 function을 바로 사용할 수 있습니다!

배열을 사용하여 사용자 정의 함수

사용자 정의 함수 =ARRAYFORMULA(SAMPLE(A1:A5,B1:B5))를 셀 "C1"에 넣으면 다음 결과가 얻어집니다.

image

이 경우 "A1:A5" 및 "B1:B5"의 값은 ["a1", "a2", "a3", "a4", "a5"] 및 ["b1", "b2", 각각 "b3", "b4", "b5"]입니다. 이에 따라 스크립트의 인수1, 인수2의 인수는 ["a1", "a2", "a3", "a4", "a5"] 및 ["b1", "b2", "b3", "b4입니다. ", "b5"]와 같은 결과가 반환됩니다. 이 결과를 원하시면 커스텀 함수와 스크립트를 수정하지 않아도 사용하실 수 있습니다.

그러나 예상 결과가 {"argument1":"a1","argument2":"b1"}, {"argument1":"a2","argument2":"b2"}, {"argument1":"a3인 경우 ","argument2":"b3"}와 같은 경우 "C1:C5"에서 각각 사용자 정의 함수 또는 스크립트를 수정해야 합니다.

사용자 정의 기능 수정

이 패턴에서는 사용자 정의 함수를 수정하여 예상한 결과를 얻습니다. 이 경우 스크립트를 수정할 필요가 없습니다.

수정된 Custom 함수는 다음과 같습니다.

=MAP(A1:A5,B1:B5,LAMBDA(value1,value2,SAMPLE(value1,value2)))

이 공식을 사용하면 다음과 같은 결과가 나온다.

image

이 이미지에서 MAP 함수를 이용하여 예상한 결과를 얻을 수 있음을 알 수 있다.

사용자 정의 함수의 스크립트 수정

이 패턴에서는 사용자 정의 함수의 원본 스크립트를 수정하여 최종 결과를 얻습니다. 이 경우 =ARRAYFORMULA(SAMPLE(A1:A5,B1:B5))에서 사용자 정의 함수를 수정할 필요가 없습니다.

수정된 스크립트는 다음과 같습니다.

function SAMPLE(argument1, argument2) {
return argument1.map((e, i) =>
JSON.stringify({ argument1: e[0], argument2: argument2[i][0] })
);
}

=ARRAYFORMULA(SAMPLE(A1:A5,B1:B5))의 사용자 정의 함수를 셀 “C1”에 넣으면 다음 결과가 얻어집니다.

image

하지만 이 수정된 스크립트에서 =SAMPLE(A1,B1)을 사용하면 TypeError: Argument1.map is not a function과 같은 오류가 발생합니다. "A1"과 "B1"의 값은 배열이 아니기 때문에 =ARRAYFORMULA(SAMPLE(A1:A5,B1:B5))=SAMPLE(A1,B1)을 모두 사용하려는 경우 다음 수정사항을 사용할 수 있습니다.

function SAMPLE(argument1, argument2) {
if ([argument1, argument2].every((e) => Array.isArray(e))) {
return argument1.map((e, i) =>
JSON.stringify({ argument1: e[0], argument2: argument2[i][0] })
);
}
return JSON.stringify({ argument1, argument2 });
}

이 경우 다음 스크립트를 사용할 수도 있습니다.

function SAMPLE(argument1, argument2) {
if ([argument1, argument2].every((e) => Array.isArray(e))) {
return argument1.map((e, i) => SAMPLE(e[0], argument2[i][0]));
}
return JSON.stringify({ argument1, argument2 });
}

추가정보

추가적으로 안내드리자면 현 단계에서는 현재 스프레드시트 사양으로 인해 셀 개수가 많은 경우 커스텀 기능을 사용할 수 없습니다. Custom 함수 사용 시 셀 개수 관련 오류가 발생하는 경우, 다음과 같이 Custom 함수 대신 Google Apps Script를 직접 사용해 보시기 바랍니다.

const sheetName = "Sheet1"; // Please set your sheet name.

function onEdit(e) {
const { range } = e;
const sheet = range.getSheet();
if (sheet.getSheetName() != sheetName || range.columnStart > 2) return;
myFunction(sheet);
}

function myFunction(sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName)) {
const range = sheet.getRange("A1:B" + sheet.getLastRow());
const values = range.getValues().map(([argument1, argument2]) =>
[[argument1, argument2].includes("") ? [null] : JSON.stringify({ argument1, argument2 })]
);
range.offset(0, 2, values.length, 1).setValues(values);
}

이 스크립트를 테스트할 때 시트 이름을 sheetName으로 설정하고 myFunction을 실행하세요. 이에 따라 결과 값이 "C" 열에 입력됩니다. 또 "A1:A" 및 "B1:B" 셀을 편집하면 단순 트리거의 onEdit 함수에 의해 "C" 열의 값이 업데이트됩니다.

GCP에 wordpress 설치 및 SSL 및 DNS 설정

· 4 min read

image

갑자기 회사에서 워드프레스를 우리 클라우드에서 호스팅하고 이를 마켓팅팀에서 이용하고 싶다 하여 급 워드프레스 구성을 시작하게 됐습니다.
그래서 GCP(구글 클라우드 플랫폼)에 워드프레스를 설치하고 운영하는 방법에 대해 확인해 봤습니다.

GCP에 워드프레스 설치하기

우선 GCP 가입을 합니다.
그런 뒤 상단 검색창에 wordpress를 검색한 뒤 MARKETPLACE 및 API 분류에서 WordPress 를 클릭해 배포를 클릭합니다!

image

이미 배포를 진행했기 때문에 배포보기로 보이지만 원래는 배포 버튼이 보입니다. 그리고 배포 이후에는 배포보기를 눌러 이미 배포한 내용도 확인할 수 있습니다.
배포를 누르면 여러가지 옵션들이 있는데 원한다면 instance의 위치와 컴퓨터 사양도 조정할 수 있습니다. 나머지는 조정하지 않아도 됩니다.
배포 보기를 누르면 배포된 목록들이 보이는데 처음 배포했으면 1개가 있을 겁니다.
그걸 클릭하면 Deployment Manager가 나옵니다.

image

처음 배포를 하면 Site Address, Admin URL에 자동으로 ip 가 할당됩니다.
예를 들어 32.34.108.231 ip가 할당됐다고 하면, Site Address: https://32.34.108.231, Admin URL: https://32.34.108.231/wp-admin 이렇게 싸이트와 싸이트 어드민 주소가 생깁니다.
나중에 설명할 Admin 로그인을 위해 WordPress Admin user, WordPress Admin password를 저장해둡니다.

여기서 wordpress 배포 시 phpadmin enable을 해두었다면 이를 활용해 wordpress 데이터베이스에 wordpress mysql user, password를 이용하면 잘못된 셋팅을 한 경우 wp-admin 페이지에 접속하지 못하게 될 때 데이터베이스로 접속해 셋팅 내용을 변경할 수도 있으니 enable 해 두는 것이 좋습니다.

위에서 말했던 것처럼 https://32.34.108.231 로 바로 접속이 가능하지만 https 설정을 위한 ssl 인증서 설정이 되어 있지 않고 도메인도 설정되어 있지 않습니다. 그래서 싸이트에 접속하면 안전하지 않은 페이지로 이동하겠다는 클릭을 하며 접속해야 하는 불편함이 있습니다. 그래서 인증서와 도메인 설정이 필요합니다.

SSL 인증서 및 도메인 설정하기

GCP의 WordPress 페이지에서 SSH 버튼을 클릭하면 VM으로 간단히 접속할 수 있습니다.
접속 후 무료 ssl 인증서 설정을 위해 certbot 홈페이지로 이동합니다.

image

어떤 웹싸이트를 운영중인지에 따라 instruction이 달라지니 이를 체크하기 위해 아래 명령을 입력해 확인합니다.

cat /etc/*release

위의 명령을 입력하면 아래와 같이 서버의 정보를 볼 수 있습니다.

image

그런 뒤 소프트웨어를 선택해 주는데 저는 Apache를 선택했습니다.

image

근데 여기서 문제가 발생하는데 시스템에 debian 11 이 없습니다.

image

그래서 당황했습니다. 물론 운영중인 서버에 따라 지시사항이 달라지지만 이런 경우 방법을 알 수 없어 debian 11일 때 설치 방법에 대해 찾아본 뒤 적용했습니다.

아래와 같이 터미널에서 명령어를 입력합니다.

# 우선 업데이트를 진행해 주고
$ sudo apt update
# snapd를 설치합니다.
$ sudo apt install snapd
# snap을 이용해 core를 설치해 주고
$ sudo snap install core
# snap 동작을 간단히 확인하기 위해 hello-world를 설치합니다.
$ sudo snap install hello-world
# Hellow World!가 출력되면 정상 설치된 것을 확인할 수 있습니다.
$ hello-world
# snapd가 최종버전으로 업데이트 되었는지 다시 체크하고
$ sudo snap install core; sudo snap refresh core
# snapd를 이용해 certbot을 설치합니다.
$ sudo snap install --classic certbot
# certbot 명령을 user 명령어에 링크해 주고
$ sudo ln -s /snap/bin/certbot /usr/bin/certbot
# 이제 certbot을 설정해 줍니다!
# 명령어를 입력하면 이메일을 중간에 입력하라 나오고 입력해 준 뒤 이용약관 동의 내용도 나오면 Y 해줍니다.
# ssl 인증서와 연결할 도메인 주소를 입력해 줍니다.(example.com www.example.com 이런 식)
# 이렇게 이메일을 입력하면 그 다음으로 아래와 같이 나오는데
# Deploying certificate
#
# We were unable to find a vhost with a ServerName or Address of grablo.co.
# Which virtual host would you like to choose?
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# 1: 000-default.conf               |                       |       | Enabled
# 2: wordpress-https.conf           |                       | HTTPS | Enabled
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# 위에서 HTTPS 인증서만 등록하므로 각각의 도메인마다 HTTPS 2번을 선택해 등록하면 됩니다.
$ sudo certbot --apache
# 인증서 갱신이 가능한지를 테스트 해 갱신이 가능한지 확인합니다.
$ sudo certbot renew --dry-run
# 이 후 인증서 갱신 기한이 되면 아래 명령어를 입력해 진짜 인증서 갱신을 실행하면 됩니다.
$ sudo certbot renew
# 인증서 갱신을 잘 마쳤는지 인증서를 확인하려면 아래 명령어를 입력합니다.
$ sudo certbot certificates

이제 certbot을 이용한 ssl 인증서 발급 및 도메인 맵핑 설정이 완료됐으니 진짜 도메인 설정을 위해 터미널을 끄고 검색창에 CloudDNS를 검색합니다.
우선 example.com으로 도메인을 발급 받았다고 가정합니다.
dns 이름이 example.com 도메인 유형 A인 레코드 세트를 추가하고 여기에 32.34.108.231 (서버ip)주소를 입력해 줍니다. 그럼 32.34.108.231 이 ip 주소와 ssl 인증서가 연결되고 또한 도메인도 맵핑된 상태가 됩니다.
그럼 이제 example.com에 접속하면 왼쪽 상단 url 창에 있는 자물쇠 표시를 클릭해 인증서가 정상적인지 체크할 수 있고 안전하지 않음 옵션 없이 바로 인증된 싸이트에 도메인으로 접속할 수 있는 상태가 됩니다!!

image

이제부터는 나만의 워드프레스 싸이트가 생겼으니 example.com/wp-admin 에 접속해 싸이트 운영을 시작하시면 됩니다 🚀

Cloud Run에서 LangServe와 함께 LangChain 배포하기!

· 2 min read

image

LangChain은 LLM 모델을 사용하는 앱을 빌드하기 쉽게 해주는 프레임워크입니다.
LangChain에서 최근 LangServe를 도입했는데, LangChain 프로젝트를 REST API로 배포하는 것을 제공합니다.
LangServe는 Cloud Run과 Replit에 대한 배포를 모두 지원합니다.

LangChain 등장 배경

Google Bard와 같이 Gen AI 챗봇은 LLM을 기반으로 합니다. 간단히 Prompt를 작성하면 코드도 작성하고 텍스트도 바꾸고 추천도 해주고 논리 문제도 해결합니다.
이런 서비스를 구축하려면 Pipeline(Chain)을 구축이 필요합니다.
이 때 필요한 것이 LangChain입니다.

LangChain으로 Chain 구축

간단하게 LangChain을 이용해 VertexAI PaLM 2 for chat model을 불러오고 농담을 하게 만드는 방법입니다.

from langchain.chat_models import ChatVertexAI
from langchain.prompts import ChatPromptTemplate

_prompt = ChatPromptTemplate.from_template(
"Tell me a joke about Chuck Norris and {text}")
_model = ChatVertexAI()

chain = _prompt | _model

chain.invoke({"text": "Cannelloni"})
# Here's a joke about Chuck Norris and Cannelloni:
# Chuck Norris doesn't eat cannelloni. He eats the can."

LangServe를 활용해 Chain을 API로 제공

위와 같이 Chain을 구축하고 나면 아래 2가지 스텝을 거쳐서 REST API로 노출합니다.

  • Langchain CLI를 사용하여 LangServe 앱 스캐폴딩
  • Chain을 add_routes 를 통해 추가

LangServe에는 체인을 시도하고 디버깅할 수 있는 플레이그라운드 엔드포인트도 함께 제공됩니다.
LangChain은 프로젝트에 실제로 결합하기 좋도록 템플릿을 제공합니다.

데모 시간

Google Cloud 프로젝트가 이미 있다고 가정하고 순서대로 진행합니다.

gcloud config set project [PROJECT-ID]

Vertex AI API 사용 설정

gcloud services enable aiplatform.googleapis.com

localhost에서 Vertex AI PaLM API를 호출 설정

gcloud auth application-default login

LangChain 시작

  • LangChain CLI 설치

pip install langchain-cli

  • LangServe REST API를 스캐폴드하고 다음 명령을 사용하여 Chuck Norris 템플릿을 추가

langchain app new my-demo --package vertexai-chuck-norris

  • 이 명령은 디렉터리를 생성 my-demo하고 --package플래그는 Chuck Norris 템플릿을 설치합니다.
  • 다음으로 Chunk Norris와 app/server.py를 연결한 뒤
from vertexai_chuck_norris.chain import chain as vertexai_chuck_norris_chain

add_routes(app, vertexai_chuck_norris_chain,
path="/vertexai-chuck-norris")
  • LangChain serve

langchain serve

이러면 웹 서버가 http://localhost:8080/vertexai-chuck-norris/playground 에서 확인하고 실행할 수 있습니다.

  • Cloud Run 배포

gcloud run deploy 하면 끝!

gif

Your Jenkins data directory /var/lib/jenkins (AKA JENKINS_HOME) is almost full. You should act on it before it gets completely full.

· One min read

image

갑자기 jenkins 가 빌드가 안된다.

뭔가 가득 찼다는 말 같아, 빌드 서버에 접속해 확인을 해보니(우분투 서버)

df -h 를 입력해 용량을 확인하면 용량이 100% 차 있는 곳이 많이 있다.

build 서버에서 용량이 자꾸 늘어날 이유가 무엇일까 생각 중 로컬 랩탑에서 docker 빌드하면 남는 큰 용량의 dangling image들이 생각났다. 내 랩탑은 매번 disk에서 지워주지만 서버는 신경을 못 썼다...

그래서 확인해 해 봤다! docker image ls

image

끝을 모르고 쌓여 있는 이미지들...
docker rmi $(docker images -f "dangling=true" -q)
를 입력해 모두 제거했다.

image

눈 앞에서 마구 삭제하니 불안한 마음이 생기지만 실행중인 컨테이너도 아니고 이미지니까 괜찮겠지 하며 기다려 본다...

완료 후 확인해 보니 아래와 같이 용량이 확보됐고 빌드가 정상 실행됐다.

python requests InsecureRequestWarning 해결법

· 2 min read

image

python으로 자동으로 request 요청하다 보면 가끔 오류가 나서 살펴보면 같은 곳에 파라미터만 바꿔서 계속 요청중인데도 불구하고 ssl 인증서를 신뢰할 수 없다며 오류를 내뱉는다.

이런 이상한 오류들은 일일이 대응하기엔 시간이 없다.

그래서 requests.post(url=url, headers=headers, json=payload, verify=false, timeout=10) 처럼 verify와 timeout 옵션을 넣어줬다. verify=false로 해두면 인증서를 걍 신뢰하게 하는거다. 위험부담이 있지만 보통 api 사용할 때 믿을만한 곳을 이용하기도 하고 이렇게 같은 곳에 요청해도 어떨 때는 나고 어떨 때는 안 난다면 과감히 이런 조치를 하는 것도 좋다고 생각한다.

이렇게 조치가 끝나나 했는데 다시 아래의 오류가 등장했다. 무한 반복해서 계속 등장한다.😅(필요없는 부분은 ... 처리!)

...lib/python3.10/site-packages/urllib3/connectionpool.py:1061: InsecureRequestWarning: Unverified HTTPS request is being made to host '...'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings

체감상으로는 요청할 때마다 등장하는 것 같은데 괜히 이러다 문제 생기는 건 아닌가 불안해지고 기껏 속도 체크나 중간에 확인을 위해 찍어뒀던 로그들도 안 보일만큼 내 터미널 라인 설정을 훨씬 뛰어넘는 Warning이 무한정 뜨는데 이걸 해결하는 방법은 3가지가 있다!(셋 중 하나만 해도 됨!)

image

  1. urllib3을 이용해 disable하고 싶은 warning만 disable 하는 방법
from urllib3.exceptions import InsecureRequestWarning
from urllib3 import disable_warnings
disable_warnings(InsecureRequestWarning)

이렇게 하면 InsecureRequestWarning은 disable 되서 내 마음을 더 이상 괴롭히지 않는다.😄

  1. requests 에서 warning을 disable하는 방법
import requests
requests.packages.urllib3.disable_warnings()

이렇게 requests 모듈에서도 disable 되게 만들어 줄 수 있다! 👍

  1. 이번엔 최신 CA 파일을 통해 요청하는 방법인데, 이건 귀찮아서 직접 해보진 않았다...;

우선 최신 CA 파일을 여기서 다운받고, 아래와 같이 path를 넣어주면 된다고 한다.

requests.post(url=API_SERVER, headers=headers, data=json.dumps(data), verify='CA_PATH')
requests.get('https://github.com', verify='/path/to/certfile')

image

이 외에도 다른 방법들도 있는데 점점 귀찮아지는 방법들이라 나는 여기까지...

정말 시간 부족한 사람들 많을텐데 조금이나마 도움이 됐으면 좋겠다!!

오픈소스 SecretOps Infisical

· 4 min read

image

이미 모두 알고 있었을 수 있지만 나는 요새 알게 됐는데 유용한 것 같아 공유한다.

infisical 이란 말하기 힘든 이름을 가진 이 오픈소스는 시크릿 관리 플랫폼입니다. (https://infisical.com/)

서비스 하다 보면 당연하게 local 환경에서 개발하고 테스트 환경에서 개발하고 스테이징 환경에서 개발하고 라이브 환경에서 개발하게 되는데 이때마다 다른 데이터베이스를 참조하거나 다른 써드파티를 참조하거나 하게 되면 그 동안은 .env 파일을 .env.dev, .env.stage, .env.prod 등으로 나누어 쓰거나, docker로 감쌀 때 환경변수 들어가게 설정하거나, 아니면 클라우드에서 각각의 환경변수를 등록해 실행될 때 참조하도록 하는 방식을 이용해 왔다.

심지어는 환경변수 파일들을 slack, notion, email을 통해 전달해야 하는 뭔가 꺼림칙한 상황들이 전개되어 왔는데 이걸 사용하면 개발하는 동안 환경변수 파일들을 보지 않고 api 요청하듯 불러와 사용할 수 있다.

image

우선 오픈 소스이니 무료로 사용할 수 있다! 그리고 self-hosting을 통해 private하게 인프라에 넣어 두고 쓸 수도 있고 cloud도 제공해 줘서 무료로 바로 시작해 볼 수 있다.

image

로그인 하게 되면 바로 보이는 화면이다. 여기서 프로젝트를 추가할 수 있고 무료 플랜이니 3개 프로젝트까지 가능하다.

바로 example project의 explore 버튼을 눌러준 뒤 프로젝트 설정에 들어간다!

image

들어가면 프로젝트 이름을 설정할 수 있고 환경을 관리하는 메뉴가 있다. 환경은 처음에 말했듯 로컬, 테스트, 라이브 등의 개발하는 인프라 환경을 말한다. 일단 샘플 프로젝트에서는 dev, staging, prod로 나눠져 있다. 원하는데로 변경하면 된다.

하단에는 secret tags를 추가해 시크릿 정보들에 대해 tag를 입혀서 database 시크릿 정보면 데이터베이스 시크릿 정보로 태그를 나눌 수도 있다.(아마도 시크릿 정보가 많아지면 검색하기 편하라고 이렇게 한 것으로 보임)

그리고 auto capitalization 온오프 기능으로 대문자를 자동으로 쓰겠끔 하는 기능도 있고 End to end encrytion enalbed기능을 통해 End to End 간 암호화 기능을 통해 서버와 시크릿을 암호화된 형태로 서로 해독할 수 있도록 통신한다.

image

이제 시크릿 메뉴로 넘어가면 환경 관리에서 설정한 대로 시크릿 목록이 보인다. 시크릿 중 하나를 클릭하면 이런식으로 각 환경에서의 값들이 ....으로 나타나고 마우스를 가까이 가면 복사할 수도 있다. reveal values를 통해 전체 값을 한번에 볼 수도 있다.

image

explore 버튼을 누르면 아래와 같이 본격 시크릿 값 관리 페이지로 가게 된다.

image

눈 모양을 누르면 시크릿 값이 모두 보이고 이미 저장한 시크릿 값들은 .env 파일로 저장할 수도 있고 또는 .env 파일을 바로 가져와 import 할 수도 있다. 심지어 시크릿 값 변경에 대한 커밋 내역도 확인할 수 있는 것 같은데 이건 무료 플랜 기능은 아니라서 확인하진 못했다. 아무튼 이런식으로 환경변수를 각 환경에 맞게 저장하고 관리하면 된다.

image

맴버로 가면 이제 같이 개발하는 팀원들을 초대한다. add member를 통해 어떤 프로젝트(현재는 example project)에 초대할지 선택하고 이메일을 적어주면 초대가 간다. 그러고 그 팀원에게 ROLE 도 정해줄 수 있다. 각 ROLE은 만들 수 있고 각각이 어떤 권한을 가지게 할지도 세밀하게 설정할 수 있다.

image

여러 써드파티들과 연동도 가능하고 점점 더 쉽게 사용할 수 있도록 빠르게 업데이트 중이다.

image

Secret Rotation을 통해 주기적으로 시크릿들을 관리해야 하는 것들은 관리를 편히 할 수 있고 Secret Approvals를 통해 정책을 만들고 시크릿 변경을 차단할 수도 있다. 심지어 IP 제한도 가능하다. 그리고 Audit Logs에서 모든 변경에 대한 로그도 쌓인다. 하지만 안타깝게도 이 4가지 기능은 또 무료 플랜에서는 안된다.

이제 설정 방법이 거의 끝났는데, 이제 실제 코드에서는 어떻게 사용하냐를 보면!

node js 앱을 예로 들면 아래와 같다.

https://infisical.com/docs/cli/overview 를 참조해 os에 맞게 cli 를 설치해준다.

그러면 이제 쉬워진다.

infisical login을 실행해 웹에서 로그인 하거나 infisical login -i 를 통해 터미널에서 로그인할 수도 있다.

그런 뒤 infisical run --env=dev -- nest start 해주면 터미널에서 시크릿 몇 개를 가져왔고 성공이다 실패다를 알려주고 그 시크릿을 이용해 개발을 시작할 수 있다.

만약 self-hosting하고 있을 경우 infisical login으로 self-hosting 을 선택하고

infisical run --domain=<self hosting server domain/api> --env=dev -- nest start

실행하면 된다.

물론 infisical login을 통해 로그인을 하면 그 다음에는 로그인 안 하고 바로 run 할 수 있지만 그래도 로그인을 패스하고 바로 run 하고 싶다면 아래에서 create token을 눌러 토큰을 생성해 주고 그 토큰 값을 복사해 아래 명령어와 같이 실행해 주면 된다.

image

infisical run --domain=<self hosting server domain/api> --token=st.65659813c1cf250cbaacdc5c.fc0b02ffc470e645aebc7c5484d4bfe2.52f3c5c75c19b92276cd22b1b8ab5edd --env=dev -- nest start

팀원들 간에 간단하고 안전하게 시크릿 값을 공유하고 관리할 수 있는 infisical을 한번 사용해 보는 것도 좋아 보인다.

추천합니다!