claude-agent-framework 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -0
- package/bin/claude-framework +3 -0
- package/framework/agents/design-lead.md +240 -0
- package/framework/agents/product-owner.md +179 -0
- package/framework/agents/tech-lead.md +226 -0
- package/framework/commands/ayuda.md +127 -0
- package/framework/commands/a/303/261adir.md +98 -0
- package/framework/commands/backup.md +397 -0
- package/framework/commands/cambiar.md +110 -0
- package/framework/commands/cloud.md +457 -0
- package/framework/commands/code.md +142 -0
- package/framework/commands/debug.md +334 -0
- package/framework/commands/deploy.md +383 -0
- package/framework/commands/deshacer.md +120 -0
- package/framework/commands/estado.md +218 -0
- package/framework/commands/explica.md +227 -0
- package/framework/commands/feature.md +120 -0
- package/framework/commands/git.md +427 -0
- package/framework/commands/historial.md +202 -0
- package/framework/commands/learn.md +408 -0
- package/framework/commands/movil.md +245 -0
- package/framework/commands/nuevo.md +118 -0
- package/framework/commands/plan.md +134 -0
- package/framework/commands/prd.md +113 -0
- package/framework/commands/probar.md +148 -0
- package/framework/commands/revisar.md +208 -0
- package/framework/commands/seeds.md +230 -0
- package/framework/commands/seguridad.md +226 -0
- package/framework/commands/tasks.md +157 -0
- package/framework/skills/architecture/algorithms.md +970 -0
- package/framework/skills/architecture/clean-code.md +1080 -0
- package/framework/skills/architecture/design-patterns.md +1984 -0
- package/framework/skills/architecture/functional-programming.md +972 -0
- package/framework/skills/architecture/solid.md +991 -0
- package/framework/skills/cloud/cloud-aws.md +848 -0
- package/framework/skills/cloud/cloud-azure.md +931 -0
- package/framework/skills/cloud/cloud-gcp.md +848 -0
- package/framework/skills/cloud/message-queues.md +1229 -0
- package/framework/skills/core/accessibility.md +401 -0
- package/framework/skills/core/api.md +474 -0
- package/framework/skills/core/authentication.md +306 -0
- package/framework/skills/core/authorization.md +388 -0
- package/framework/skills/core/background-jobs.md +341 -0
- package/framework/skills/core/caching.md +473 -0
- package/framework/skills/core/code-review.md +341 -0
- package/framework/skills/core/controllers.md +290 -0
- package/framework/skills/core/cua.md +285 -0
- package/framework/skills/core/documentation.md +472 -0
- package/framework/skills/core/file-uploads.md +351 -0
- package/framework/skills/core/hotwire-native.md +296 -0
- package/framework/skills/core/hotwire.md +278 -0
- package/framework/skills/core/i18n.md +334 -0
- package/framework/skills/core/imports-exports.md +750 -0
- package/framework/skills/core/infrastructure.md +337 -0
- package/framework/skills/core/models.md +228 -0
- package/framework/skills/core/notifications.md +672 -0
- package/framework/skills/core/payments.md +581 -0
- package/framework/skills/core/performance.md +361 -0
- package/framework/skills/core/rails-scaffold.md +131 -0
- package/framework/skills/core/search.md +518 -0
- package/framework/skills/core/security.md +565 -0
- package/framework/skills/core/seeds.md +307 -0
- package/framework/skills/core/seo.md +542 -0
- package/framework/skills/core/testing.md +393 -0
- package/framework/skills/core/views.md +260 -0
- package/framework/skills/core/websockets.md +564 -0
- package/framework/skills/data/advanced-sql.md +1204 -0
- package/framework/skills/data/nosql.md +1141 -0
- package/framework/skills/devops/containers-advanced.md +1237 -0
- package/framework/skills/devops/debugging.md +834 -0
- package/framework/skills/devops/git-workflow.md +752 -0
- package/framework/skills/devops/networking.md +932 -0
- package/framework/skills/devops/shell-scripting.md +1132 -0
- package/framework/sub-agents/architecture-patterns-agent.md +1450 -0
- package/framework/sub-agents/cloud-agent.md +677 -0
- package/framework/sub-agents/data.md +504 -0
- package/framework/sub-agents/debugging-agent.md +554 -0
- package/framework/sub-agents/devops.md +483 -0
- package/framework/sub-agents/docs.md +176 -0
- package/framework/sub-agents/frontend-dev.md +349 -0
- package/framework/sub-agents/git-workflow-agent.md +697 -0
- package/framework/sub-agents/integrations.md +630 -0
- package/framework/sub-agents/native-dev.md +434 -0
- package/framework/sub-agents/qa.md +138 -0
- package/framework/sub-agents/rails-dev.md +375 -0
- package/framework/sub-agents/security.md +526 -0
- package/framework/sub-agents/ui.md +437 -0
- package/framework/sub-agents/ux.md +284 -0
- package/framework/templates/api-spec.md +500 -0
- package/framework/templates/component-spec.md +248 -0
- package/framework/templates/feature.json +13 -0
- package/framework/templates/model-spec.md +318 -0
- package/framework/templates/prd-template.md +80 -0
- package/framework/templates/task-plan.md +122 -0
- package/framework/templates/task-user-story.md +52 -0
- package/framework/templates/technical-spec.md +260 -0
- package/framework/templates/user-story.md +95 -0
- package/package.json +42 -0
- package/project-templates/CLAUDE.md +42 -0
- package/project-templates/contexts/architecture.md +25 -0
- package/project-templates/contexts/conventions.md +46 -0
- package/project-templates/contexts/design-system.md +47 -0
- package/project-templates/contexts/requirements.md +38 -0
- package/project-templates/contexts/stack.md +30 -0
- package/project-templates/history/active/models.md +11 -0
- package/project-templates/history/changelog.md +15 -0
- package/project-templates/workspace/.gitkeep +0 -0
- package/src/cli.js +52 -0
- package/src/init.js +104 -0
- package/src/status.js +75 -0
- package/src/update.js +88 -0
|
@@ -0,0 +1,848 @@
|
|
|
1
|
+
# Skill: Google Cloud Platform (GCP) para Rails
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Configurar, desplegar y gestionar aplicaciones Rails en Google Cloud Platform, aprovechando los servicios cloud para escalabilidad, rendimiento y seguridad.
|
|
6
|
+
|
|
7
|
+
## Cloud Run
|
|
8
|
+
|
|
9
|
+
### Desplegar Rails en Cloud Run
|
|
10
|
+
|
|
11
|
+
```dockerfile
|
|
12
|
+
# Dockerfile
|
|
13
|
+
FROM ruby:3.3.0-slim
|
|
14
|
+
|
|
15
|
+
# Instalar dependencias
|
|
16
|
+
RUN apt-get update -qq && \
|
|
17
|
+
apt-get install -y build-essential libpq-dev nodejs npm && \
|
|
18
|
+
npm install -g yarn && \
|
|
19
|
+
rm -rf /var/lib/apt/lists/*
|
|
20
|
+
|
|
21
|
+
WORKDIR /app
|
|
22
|
+
|
|
23
|
+
# Copiar Gemfile primero para cache de layers
|
|
24
|
+
COPY Gemfile Gemfile.lock ./
|
|
25
|
+
RUN bundle config set --local deployment 'true' && \
|
|
26
|
+
bundle config set --local without 'development test' && \
|
|
27
|
+
bundle install --jobs 4
|
|
28
|
+
|
|
29
|
+
# Copiar aplicación
|
|
30
|
+
COPY . .
|
|
31
|
+
|
|
32
|
+
# Precompilar assets
|
|
33
|
+
ARG RAILS_MASTER_KEY
|
|
34
|
+
ENV RAILS_ENV=production
|
|
35
|
+
ENV SECRET_KEY_BASE=dummy
|
|
36
|
+
RUN bundle exec rails assets:precompile
|
|
37
|
+
|
|
38
|
+
# Puerto (Cloud Run usa $PORT)
|
|
39
|
+
EXPOSE 8080
|
|
40
|
+
|
|
41
|
+
# Comando
|
|
42
|
+
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
# config/puma.rb
|
|
47
|
+
port ENV.fetch("PORT") { 8080 }
|
|
48
|
+
environment ENV.fetch("RAILS_ENV") { "production" }
|
|
49
|
+
|
|
50
|
+
# Cloud Run: single process, multiple threads
|
|
51
|
+
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i
|
|
52
|
+
threads threads_count, threads_count
|
|
53
|
+
|
|
54
|
+
preload_app!
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Build y push a Container Registry
|
|
59
|
+
gcloud builds submit --tag gcr.io/mi-proyecto/rails-app
|
|
60
|
+
|
|
61
|
+
# Desplegar en Cloud Run
|
|
62
|
+
gcloud run deploy rails-app \
|
|
63
|
+
--image gcr.io/mi-proyecto/rails-app \
|
|
64
|
+
--platform managed \
|
|
65
|
+
--region us-central1 \
|
|
66
|
+
--allow-unauthenticated \
|
|
67
|
+
--set-env-vars "RAILS_ENV=production" \
|
|
68
|
+
--set-secrets "DATABASE_URL=database-url:latest,SECRET_KEY_BASE=secret-key-base:latest" \
|
|
69
|
+
--memory 512Mi \
|
|
70
|
+
--cpu 1 \
|
|
71
|
+
--min-instances 0 \
|
|
72
|
+
--max-instances 10 \
|
|
73
|
+
--concurrency 80
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Cloud Run con Cloud SQL
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Desplegar con conexión a Cloud SQL
|
|
80
|
+
gcloud run deploy rails-app \
|
|
81
|
+
--image gcr.io/mi-proyecto/rails-app \
|
|
82
|
+
--add-cloudsql-instances mi-proyecto:us-central1:rails-db \
|
|
83
|
+
--set-env-vars "RAILS_ENV=production" \
|
|
84
|
+
--set-secrets "DATABASE_URL=database-url:latest"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```yaml
|
|
88
|
+
# config/database.yml para Cloud Run + Cloud SQL
|
|
89
|
+
production:
|
|
90
|
+
adapter: postgresql
|
|
91
|
+
encoding: unicode
|
|
92
|
+
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
|
|
93
|
+
# Cloud Run usa socket Unix para Cloud SQL
|
|
94
|
+
host: /cloudsql/<%= ENV["CLOUD_SQL_CONNECTION_NAME"] %>
|
|
95
|
+
database: <%= ENV["DB_NAME"] %>
|
|
96
|
+
username: <%= ENV["DB_USER"] %>
|
|
97
|
+
password: <%= ENV["DB_PASSWORD"] %>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Autoscaling Configuration
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
# service.yaml para Cloud Run
|
|
104
|
+
apiVersion: serving.knative.dev/v1
|
|
105
|
+
kind: Service
|
|
106
|
+
metadata:
|
|
107
|
+
name: rails-app
|
|
108
|
+
spec:
|
|
109
|
+
template:
|
|
110
|
+
metadata:
|
|
111
|
+
annotations:
|
|
112
|
+
autoscaling.knative.dev/minScale: "1"
|
|
113
|
+
autoscaling.knative.dev/maxScale: "100"
|
|
114
|
+
autoscaling.knative.dev/target: "80"
|
|
115
|
+
run.googleapis.com/cpu-throttling: "false"
|
|
116
|
+
spec:
|
|
117
|
+
containerConcurrency: 80
|
|
118
|
+
timeoutSeconds: 300
|
|
119
|
+
containers:
|
|
120
|
+
- image: gcr.io/mi-proyecto/rails-app
|
|
121
|
+
ports:
|
|
122
|
+
- containerPort: 8080
|
|
123
|
+
resources:
|
|
124
|
+
limits:
|
|
125
|
+
cpu: "2"
|
|
126
|
+
memory: 1Gi
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Compute Engine
|
|
130
|
+
|
|
131
|
+
### Crear VM para Rails
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
# Crear instancia
|
|
135
|
+
gcloud compute instances create rails-server \
|
|
136
|
+
--zone=us-central1-a \
|
|
137
|
+
--machine-type=e2-medium \
|
|
138
|
+
--image-family=ubuntu-2204-lts \
|
|
139
|
+
--image-project=ubuntu-os-cloud \
|
|
140
|
+
--boot-disk-size=20GB \
|
|
141
|
+
--tags=http-server,https-server
|
|
142
|
+
|
|
143
|
+
# Crear regla de firewall
|
|
144
|
+
gcloud compute firewall-rules create allow-rails \
|
|
145
|
+
--allow tcp:3000 \
|
|
146
|
+
--target-tags=http-server
|
|
147
|
+
|
|
148
|
+
# Conectar por SSH
|
|
149
|
+
gcloud compute ssh rails-server --zone=us-central1-a
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Startup Script para Rails
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
#!/bin/bash
|
|
156
|
+
# startup-script.sh
|
|
157
|
+
|
|
158
|
+
# Instalar dependencias
|
|
159
|
+
apt-get update
|
|
160
|
+
apt-get install -y git curl build-essential libssl-dev zlib1g-dev \
|
|
161
|
+
libpq-dev nodejs npm
|
|
162
|
+
|
|
163
|
+
# Instalar rbenv
|
|
164
|
+
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
|
|
165
|
+
export PATH="$HOME/.rbenv/bin:$PATH"
|
|
166
|
+
eval "$(rbenv init -)"
|
|
167
|
+
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
|
|
168
|
+
|
|
169
|
+
# Instalar Ruby
|
|
170
|
+
rbenv install 3.3.0
|
|
171
|
+
rbenv global 3.3.0
|
|
172
|
+
|
|
173
|
+
# Instalar bundler
|
|
174
|
+
gem install bundler
|
|
175
|
+
|
|
176
|
+
# Clonar y configurar app
|
|
177
|
+
git clone https://github.com/tu-usuario/rails-app.git /opt/rails-app
|
|
178
|
+
cd /opt/rails-app
|
|
179
|
+
bundle install --deployment --without development test
|
|
180
|
+
RAILS_ENV=production rails db:migrate
|
|
181
|
+
RAILS_ENV=production rails assets:precompile
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Managed Instance Groups
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
# Crear template de instancia
|
|
188
|
+
gcloud compute instance-templates create rails-template \
|
|
189
|
+
--machine-type=e2-medium \
|
|
190
|
+
--image-family=ubuntu-2204-lts \
|
|
191
|
+
--image-project=ubuntu-os-cloud \
|
|
192
|
+
--metadata-from-file startup-script=startup-script.sh \
|
|
193
|
+
--tags=http-server,https-server
|
|
194
|
+
|
|
195
|
+
# Crear managed instance group
|
|
196
|
+
gcloud compute instance-groups managed create rails-mig \
|
|
197
|
+
--base-instance-name=rails \
|
|
198
|
+
--template=rails-template \
|
|
199
|
+
--size=2 \
|
|
200
|
+
--zone=us-central1-a
|
|
201
|
+
|
|
202
|
+
# Configurar autoscaling
|
|
203
|
+
gcloud compute instance-groups managed set-autoscaling rails-mig \
|
|
204
|
+
--zone=us-central1-a \
|
|
205
|
+
--max-num-replicas=10 \
|
|
206
|
+
--min-num-replicas=2 \
|
|
207
|
+
--target-cpu-utilization=0.7
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Cloud Storage
|
|
211
|
+
|
|
212
|
+
### Configurar Active Storage
|
|
213
|
+
|
|
214
|
+
```ruby
|
|
215
|
+
# Gemfile
|
|
216
|
+
gem "google-cloud-storage", require: false
|
|
217
|
+
|
|
218
|
+
# config/storage.yml
|
|
219
|
+
google:
|
|
220
|
+
service: GCS
|
|
221
|
+
project: <%= ENV["GCP_PROJECT_ID"] %>
|
|
222
|
+
credentials: <%= Rails.root.join("config/gcs-credentials.json") %>
|
|
223
|
+
bucket: <%= ENV.fetch("GCS_BUCKET") { "mi-app-#{Rails.env}" } %>
|
|
224
|
+
|
|
225
|
+
# Para producción con ADC (Application Default Credentials)
|
|
226
|
+
google_production:
|
|
227
|
+
service: GCS
|
|
228
|
+
project: <%= ENV["GCP_PROJECT_ID"] %>
|
|
229
|
+
bucket: <%= ENV["GCS_BUCKET"] %>
|
|
230
|
+
# Sin credentials - usa ADC
|
|
231
|
+
|
|
232
|
+
# config/environments/production.rb
|
|
233
|
+
config.active_storage.service = :google_production
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Signed URLs
|
|
237
|
+
|
|
238
|
+
```ruby
|
|
239
|
+
# app/services/gcs_signer.rb
|
|
240
|
+
class GcsSigner
|
|
241
|
+
def initialize
|
|
242
|
+
@storage = Google::Cloud::Storage.new(
|
|
243
|
+
project_id: ENV["GCP_PROJECT_ID"]
|
|
244
|
+
)
|
|
245
|
+
@bucket = @storage.bucket(ENV["GCS_BUCKET"])
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# URL para subir
|
|
249
|
+
def signed_upload_url(filename, content_type:, expires_in: 15.minutes)
|
|
250
|
+
@bucket.signed_url(
|
|
251
|
+
filename,
|
|
252
|
+
method: "PUT",
|
|
253
|
+
content_type: content_type,
|
|
254
|
+
expires: expires_in.to_i,
|
|
255
|
+
version: :v4
|
|
256
|
+
)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# URL para descargar
|
|
260
|
+
def signed_download_url(filename, expires_in: 1.hour, disposition: nil)
|
|
261
|
+
options = {
|
|
262
|
+
method: "GET",
|
|
263
|
+
expires: expires_in.to_i,
|
|
264
|
+
version: :v4
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if disposition
|
|
268
|
+
options[:query] = {
|
|
269
|
+
"response-content-disposition" => disposition
|
|
270
|
+
}
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
@bucket.signed_url(filename, **options)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# URL para streaming de video
|
|
277
|
+
def signed_streaming_url(filename, expires_in: 4.hours)
|
|
278
|
+
@bucket.signed_url(
|
|
279
|
+
filename,
|
|
280
|
+
method: "GET",
|
|
281
|
+
expires: expires_in.to_i,
|
|
282
|
+
version: :v4,
|
|
283
|
+
headers: { "Range" => "bytes=0-" }
|
|
284
|
+
)
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### CORS Configuration
|
|
290
|
+
|
|
291
|
+
```json
|
|
292
|
+
[
|
|
293
|
+
{
|
|
294
|
+
"origin": ["https://miapp.com", "http://localhost:3000"],
|
|
295
|
+
"method": ["GET", "PUT", "POST", "DELETE"],
|
|
296
|
+
"responseHeader": ["Content-Type", "Content-Range"],
|
|
297
|
+
"maxAgeSeconds": 3600
|
|
298
|
+
}
|
|
299
|
+
]
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
# Aplicar configuración CORS
|
|
304
|
+
gsutil cors set cors.json gs://mi-bucket
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Cloud SQL
|
|
308
|
+
|
|
309
|
+
### Crear instancia PostgreSQL
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
# Crear instancia
|
|
313
|
+
gcloud sql instances create rails-db \
|
|
314
|
+
--database-version=POSTGRES_15 \
|
|
315
|
+
--tier=db-f1-micro \
|
|
316
|
+
--region=us-central1 \
|
|
317
|
+
--storage-type=SSD \
|
|
318
|
+
--storage-size=10GB \
|
|
319
|
+
--backup-start-time=03:00
|
|
320
|
+
|
|
321
|
+
# Crear base de datos
|
|
322
|
+
gcloud sql databases create rails_production \
|
|
323
|
+
--instance=rails-db
|
|
324
|
+
|
|
325
|
+
# Crear usuario
|
|
326
|
+
gcloud sql users create railsuser \
|
|
327
|
+
--instance=rails-db \
|
|
328
|
+
--password=SecurePassword123
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Configurar Rails
|
|
332
|
+
|
|
333
|
+
```yaml
|
|
334
|
+
# config/database.yml
|
|
335
|
+
production:
|
|
336
|
+
adapter: postgresql
|
|
337
|
+
encoding: unicode
|
|
338
|
+
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
|
|
339
|
+
database: <%= ENV["DB_NAME"] %>
|
|
340
|
+
username: <%= ENV["DB_USER"] %>
|
|
341
|
+
password: <%= ENV["DB_PASSWORD"] %>
|
|
342
|
+
|
|
343
|
+
# Conexión directa (con Cloud SQL Proxy)
|
|
344
|
+
host: 127.0.0.1
|
|
345
|
+
port: 5432
|
|
346
|
+
|
|
347
|
+
# O con socket (Cloud Run)
|
|
348
|
+
# host: /cloudsql/proyecto:region:instancia
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Cloud SQL Proxy
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
# Descargar proxy
|
|
355
|
+
curl -o cloud-sql-proxy https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.8.0/cloud-sql-proxy.linux.amd64
|
|
356
|
+
chmod +x cloud-sql-proxy
|
|
357
|
+
|
|
358
|
+
# Iniciar proxy
|
|
359
|
+
./cloud-sql-proxy mi-proyecto:us-central1:rails-db &
|
|
360
|
+
|
|
361
|
+
# Para desarrollo local con credenciales
|
|
362
|
+
./cloud-sql-proxy --credentials-file=service-account.json mi-proyecto:us-central1:rails-db
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Backups y Restauración
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
# Crear backup on-demand
|
|
369
|
+
gcloud sql backups create \
|
|
370
|
+
--instance=rails-db \
|
|
371
|
+
--description="Pre-deploy backup"
|
|
372
|
+
|
|
373
|
+
# Listar backups
|
|
374
|
+
gcloud sql backups list --instance=rails-db
|
|
375
|
+
|
|
376
|
+
# Restaurar backup
|
|
377
|
+
gcloud sql backups restore BACKUP_ID \
|
|
378
|
+
--restore-instance=rails-db \
|
|
379
|
+
--backup-instance=rails-db
|
|
380
|
+
|
|
381
|
+
# Clonar instancia
|
|
382
|
+
gcloud sql instances clone rails-db rails-db-clone
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Read Replicas
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
# Crear réplica
|
|
389
|
+
gcloud sql instances create rails-db-replica \
|
|
390
|
+
--master-instance-name=rails-db \
|
|
391
|
+
--region=us-central1
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
```ruby
|
|
395
|
+
# config/database.yml con réplica
|
|
396
|
+
production:
|
|
397
|
+
primary:
|
|
398
|
+
adapter: postgresql
|
|
399
|
+
database: rails_production
|
|
400
|
+
host: /cloudsql/mi-proyecto:us-central1:rails-db
|
|
401
|
+
|
|
402
|
+
primary_replica:
|
|
403
|
+
adapter: postgresql
|
|
404
|
+
database: rails_production
|
|
405
|
+
host: /cloudsql/mi-proyecto:us-central1:rails-db-replica
|
|
406
|
+
replica: true
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
## Cloud Functions
|
|
410
|
+
|
|
411
|
+
### HTTP Function para Webhooks
|
|
412
|
+
|
|
413
|
+
```ruby
|
|
414
|
+
# app.rb (para Cloud Functions con Ruby)
|
|
415
|
+
require "functions_framework"
|
|
416
|
+
require "json"
|
|
417
|
+
|
|
418
|
+
FunctionsFramework.http "webhook_handler" do |request|
|
|
419
|
+
payload = JSON.parse(request.body.read)
|
|
420
|
+
|
|
421
|
+
# Procesar webhook
|
|
422
|
+
case payload["event"]
|
|
423
|
+
when "payment.completed"
|
|
424
|
+
# Publicar a Pub/Sub para procesamiento async
|
|
425
|
+
pubsub = Google::Cloud::Pubsub.new
|
|
426
|
+
topic = pubsub.topic("payment-events")
|
|
427
|
+
topic.publish(payload.to_json)
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
{ status: "received" }.to_json
|
|
431
|
+
rescue JSON::ParserError
|
|
432
|
+
[400, {}, ["Invalid JSON"]]
|
|
433
|
+
end
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
# Desplegar función
|
|
438
|
+
gcloud functions deploy webhook-handler \
|
|
439
|
+
--runtime ruby32 \
|
|
440
|
+
--trigger-http \
|
|
441
|
+
--allow-unauthenticated \
|
|
442
|
+
--entry-point webhook_handler
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### Pub/Sub Trigger
|
|
446
|
+
|
|
447
|
+
```ruby
|
|
448
|
+
# background_function.rb
|
|
449
|
+
require "functions_framework"
|
|
450
|
+
require "json"
|
|
451
|
+
require "base64"
|
|
452
|
+
|
|
453
|
+
FunctionsFramework.cloud_event "process_payment" do |event|
|
|
454
|
+
data = JSON.parse(Base64.decode64(event.data["message"]["data"]))
|
|
455
|
+
|
|
456
|
+
# Procesar el pago
|
|
457
|
+
PaymentProcessor.new(data).process
|
|
458
|
+
|
|
459
|
+
puts "Processed payment: #{data['id']}"
|
|
460
|
+
end
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
## GKE (Google Kubernetes Engine)
|
|
464
|
+
|
|
465
|
+
### Crear Cluster
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
# Crear cluster autopilot
|
|
469
|
+
gcloud container clusters create-auto rails-cluster \
|
|
470
|
+
--region=us-central1
|
|
471
|
+
|
|
472
|
+
# O cluster standard
|
|
473
|
+
gcloud container clusters create rails-cluster \
|
|
474
|
+
--zone=us-central1-a \
|
|
475
|
+
--num-nodes=3 \
|
|
476
|
+
--machine-type=e2-medium
|
|
477
|
+
|
|
478
|
+
# Obtener credenciales
|
|
479
|
+
gcloud container clusters get-credentials rails-cluster \
|
|
480
|
+
--zone=us-central1-a
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Deployment
|
|
484
|
+
|
|
485
|
+
```yaml
|
|
486
|
+
# kubernetes/deployment.yaml
|
|
487
|
+
apiVersion: apps/v1
|
|
488
|
+
kind: Deployment
|
|
489
|
+
metadata:
|
|
490
|
+
name: rails-app
|
|
491
|
+
spec:
|
|
492
|
+
replicas: 3
|
|
493
|
+
selector:
|
|
494
|
+
matchLabels:
|
|
495
|
+
app: rails-app
|
|
496
|
+
template:
|
|
497
|
+
metadata:
|
|
498
|
+
labels:
|
|
499
|
+
app: rails-app
|
|
500
|
+
spec:
|
|
501
|
+
serviceAccountName: rails-app-sa
|
|
502
|
+
containers:
|
|
503
|
+
- name: rails-app
|
|
504
|
+
image: gcr.io/mi-proyecto/rails-app:latest
|
|
505
|
+
ports:
|
|
506
|
+
- containerPort: 8080
|
|
507
|
+
env:
|
|
508
|
+
- name: RAILS_ENV
|
|
509
|
+
value: production
|
|
510
|
+
- name: DATABASE_URL
|
|
511
|
+
valueFrom:
|
|
512
|
+
secretKeyRef:
|
|
513
|
+
name: rails-secrets
|
|
514
|
+
key: database-url
|
|
515
|
+
resources:
|
|
516
|
+
requests:
|
|
517
|
+
memory: "256Mi"
|
|
518
|
+
cpu: "100m"
|
|
519
|
+
limits:
|
|
520
|
+
memory: "512Mi"
|
|
521
|
+
cpu: "500m"
|
|
522
|
+
livenessProbe:
|
|
523
|
+
httpGet:
|
|
524
|
+
path: /up
|
|
525
|
+
port: 8080
|
|
526
|
+
initialDelaySeconds: 30
|
|
527
|
+
readinessProbe:
|
|
528
|
+
httpGet:
|
|
529
|
+
path: /up
|
|
530
|
+
port: 8080
|
|
531
|
+
initialDelaySeconds: 5
|
|
532
|
+
- name: cloud-sql-proxy
|
|
533
|
+
image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.8.0
|
|
534
|
+
args:
|
|
535
|
+
- "--structured-logs"
|
|
536
|
+
- "mi-proyecto:us-central1:rails-db"
|
|
537
|
+
securityContext:
|
|
538
|
+
runAsNonRoot: true
|
|
539
|
+
---
|
|
540
|
+
apiVersion: v1
|
|
541
|
+
kind: Service
|
|
542
|
+
metadata:
|
|
543
|
+
name: rails-app-service
|
|
544
|
+
spec:
|
|
545
|
+
type: LoadBalancer
|
|
546
|
+
ports:
|
|
547
|
+
- port: 80
|
|
548
|
+
targetPort: 8080
|
|
549
|
+
selector:
|
|
550
|
+
app: rails-app
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
## Cloud CDN
|
|
554
|
+
|
|
555
|
+
### Configurar con Load Balancer
|
|
556
|
+
|
|
557
|
+
```bash
|
|
558
|
+
# Crear backend bucket para assets
|
|
559
|
+
gsutil mb gs://rails-assets-cdn
|
|
560
|
+
|
|
561
|
+
# Hacer público
|
|
562
|
+
gsutil iam ch allUsers:objectViewer gs://rails-assets-cdn
|
|
563
|
+
|
|
564
|
+
# Crear backend bucket
|
|
565
|
+
gcloud compute backend-buckets create rails-assets-backend \
|
|
566
|
+
--gcs-bucket-name=rails-assets-cdn \
|
|
567
|
+
--enable-cdn
|
|
568
|
+
|
|
569
|
+
# Crear URL map
|
|
570
|
+
gcloud compute url-maps create rails-lb \
|
|
571
|
+
--default-service=rails-backend-service
|
|
572
|
+
|
|
573
|
+
# Agregar path para assets
|
|
574
|
+
gcloud compute url-maps add-path-matcher rails-lb \
|
|
575
|
+
--path-matcher-name=assets \
|
|
576
|
+
--default-service=rails-backend-service \
|
|
577
|
+
--backend-bucket-path-rules="/assets/*=rails-assets-backend"
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
```ruby
|
|
581
|
+
# config/environments/production.rb
|
|
582
|
+
config.asset_host = ENV["CDN_HOST"]
|
|
583
|
+
# Ejemplo: "https://storage.googleapis.com/rails-assets-cdn"
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
### Cloud Armor (WAF)
|
|
587
|
+
|
|
588
|
+
```bash
|
|
589
|
+
# Crear política de seguridad
|
|
590
|
+
gcloud compute security-policies create rails-security-policy
|
|
591
|
+
|
|
592
|
+
# Regla para bloquear países
|
|
593
|
+
gcloud compute security-policies rules create 1000 \
|
|
594
|
+
--security-policy=rails-security-policy \
|
|
595
|
+
--expression="origin.region_code == 'XX'" \
|
|
596
|
+
--action=deny-403
|
|
597
|
+
|
|
598
|
+
# Regla para rate limiting
|
|
599
|
+
gcloud compute security-policies rules create 2000 \
|
|
600
|
+
--security-policy=rails-security-policy \
|
|
601
|
+
--expression="true" \
|
|
602
|
+
--action=rate-based-ban \
|
|
603
|
+
--rate-limit-threshold-count=1000 \
|
|
604
|
+
--rate-limit-threshold-interval-sec=60 \
|
|
605
|
+
--ban-duration-sec=600
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
## Cloud DNS
|
|
609
|
+
|
|
610
|
+
### Configurar Zona
|
|
611
|
+
|
|
612
|
+
```bash
|
|
613
|
+
# Crear zona
|
|
614
|
+
gcloud dns managed-zones create mi-zona \
|
|
615
|
+
--dns-name=midominio.com. \
|
|
616
|
+
--description="Zona DNS para mi app"
|
|
617
|
+
|
|
618
|
+
# Agregar registro A
|
|
619
|
+
gcloud dns record-sets create app.midominio.com. \
|
|
620
|
+
--zone=mi-zona \
|
|
621
|
+
--type=A \
|
|
622
|
+
--ttl=300 \
|
|
623
|
+
--rrdatas=34.xx.xx.xx
|
|
624
|
+
|
|
625
|
+
# Agregar CNAME
|
|
626
|
+
gcloud dns record-sets create www.midominio.com. \
|
|
627
|
+
--zone=mi-zona \
|
|
628
|
+
--type=CNAME \
|
|
629
|
+
--ttl=300 \
|
|
630
|
+
--rrdatas=app.midominio.com.
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
## IAM
|
|
634
|
+
|
|
635
|
+
### Service Account para Rails
|
|
636
|
+
|
|
637
|
+
```bash
|
|
638
|
+
# Crear service account
|
|
639
|
+
gcloud iam service-accounts create rails-app-sa \
|
|
640
|
+
--display-name="Rails Application"
|
|
641
|
+
|
|
642
|
+
# Asignar roles mínimos
|
|
643
|
+
gcloud projects add-iam-policy-binding mi-proyecto \
|
|
644
|
+
--member="serviceAccount:rails-app-sa@mi-proyecto.iam.gserviceaccount.com" \
|
|
645
|
+
--role="roles/cloudsql.client"
|
|
646
|
+
|
|
647
|
+
gcloud projects add-iam-policy-binding mi-proyecto \
|
|
648
|
+
--member="serviceAccount:rails-app-sa@mi-proyecto.iam.gserviceaccount.com" \
|
|
649
|
+
--role="roles/storage.objectAdmin"
|
|
650
|
+
|
|
651
|
+
gcloud projects add-iam-policy-binding mi-proyecto \
|
|
652
|
+
--member="serviceAccount:rails-app-sa@mi-proyecto.iam.gserviceaccount.com" \
|
|
653
|
+
--role="roles/secretmanager.secretAccessor"
|
|
654
|
+
|
|
655
|
+
# Generar key (para desarrollo local)
|
|
656
|
+
gcloud iam service-accounts keys create key.json \
|
|
657
|
+
--iam-account=rails-app-sa@mi-proyecto.iam.gserviceaccount.com
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
## Memorystore (Redis)
|
|
661
|
+
|
|
662
|
+
### Configurar
|
|
663
|
+
|
|
664
|
+
```bash
|
|
665
|
+
# Crear instancia Redis
|
|
666
|
+
gcloud redis instances create rails-redis \
|
|
667
|
+
--size=1 \
|
|
668
|
+
--region=us-central1 \
|
|
669
|
+
--redis-version=redis_7_0
|
|
670
|
+
|
|
671
|
+
# Obtener host
|
|
672
|
+
gcloud redis instances describe rails-redis \
|
|
673
|
+
--region=us-central1 \
|
|
674
|
+
--format="value(host)"
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
```ruby
|
|
678
|
+
# config/environments/production.rb
|
|
679
|
+
config.cache_store = :redis_cache_store, {
|
|
680
|
+
url: "redis://#{ENV['REDIS_HOST']}:6379/0"
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
config.session_store :redis_store,
|
|
684
|
+
servers: ["redis://#{ENV['REDIS_HOST']}:6379/1"],
|
|
685
|
+
expire_after: 1.day
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
## Secret Manager
|
|
689
|
+
|
|
690
|
+
### Usar Secrets
|
|
691
|
+
|
|
692
|
+
```bash
|
|
693
|
+
# Crear secret
|
|
694
|
+
echo -n "mi-valor-secreto" | gcloud secrets create my-secret \
|
|
695
|
+
--data-file=-
|
|
696
|
+
|
|
697
|
+
# Agregar versión
|
|
698
|
+
echo -n "nuevo-valor" | gcloud secrets versions add my-secret \
|
|
699
|
+
--data-file=-
|
|
700
|
+
|
|
701
|
+
# Acceder a secret
|
|
702
|
+
gcloud secrets versions access latest --secret=my-secret
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
```ruby
|
|
706
|
+
# config/initializers/gcp_secrets.rb
|
|
707
|
+
if Rails.env.production?
|
|
708
|
+
require "google/cloud/secret_manager"
|
|
709
|
+
|
|
710
|
+
client = Google::Cloud::SecretManager.secret_manager_service
|
|
711
|
+
|
|
712
|
+
secrets = %w[DATABASE_URL SECRET_KEY_BASE REDIS_HOST]
|
|
713
|
+
|
|
714
|
+
secrets.each do |secret_name|
|
|
715
|
+
begin
|
|
716
|
+
name = "projects/#{ENV['GCP_PROJECT_ID']}/secrets/#{secret_name.downcase.tr('_', '-')}/versions/latest"
|
|
717
|
+
version = client.access_secret_version(name: name)
|
|
718
|
+
ENV[secret_name] = version.payload.data unless ENV[secret_name].present?
|
|
719
|
+
rescue Google::Cloud::NotFoundError
|
|
720
|
+
Rails.logger.warn "Secret #{secret_name} not found"
|
|
721
|
+
end
|
|
722
|
+
end
|
|
723
|
+
end
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
## Cloud Logging / Monitoring
|
|
727
|
+
|
|
728
|
+
### Configurar Logging
|
|
729
|
+
|
|
730
|
+
```ruby
|
|
731
|
+
# config/environments/production.rb
|
|
732
|
+
if ENV["K_SERVICE"].present? # Cloud Run
|
|
733
|
+
require "google/cloud/logging"
|
|
734
|
+
|
|
735
|
+
logging = Google::Cloud::Logging.new
|
|
736
|
+
resource = Google::Cloud::Logging::Middleware.build_monitored_resource
|
|
737
|
+
|
|
738
|
+
config.logger = ActiveSupport::Logger.new(
|
|
739
|
+
Google::Cloud::Logging::Logger.new(
|
|
740
|
+
logging,
|
|
741
|
+
"rails-app",
|
|
742
|
+
resource
|
|
743
|
+
)
|
|
744
|
+
)
|
|
745
|
+
end
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
### Structured Logging
|
|
749
|
+
|
|
750
|
+
```ruby
|
|
751
|
+
# app/lib/json_logger.rb
|
|
752
|
+
class JsonLogger < ActiveSupport::Logger
|
|
753
|
+
def format_message(severity, timestamp, progname, msg)
|
|
754
|
+
{
|
|
755
|
+
severity: severity,
|
|
756
|
+
timestamp: timestamp.iso8601,
|
|
757
|
+
message: msg,
|
|
758
|
+
progname: progname
|
|
759
|
+
}.to_json + "\n"
|
|
760
|
+
end
|
|
761
|
+
end
|
|
762
|
+
|
|
763
|
+
# config/environments/production.rb
|
|
764
|
+
config.logger = JsonLogger.new(STDOUT)
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
### Custom Metrics
|
|
768
|
+
|
|
769
|
+
```ruby
|
|
770
|
+
# app/services/cloud_monitoring.rb
|
|
771
|
+
class CloudMonitoring
|
|
772
|
+
def initialize
|
|
773
|
+
@client = Google::Cloud::Monitoring.metric_service
|
|
774
|
+
@project_name = "projects/#{ENV['GCP_PROJECT_ID']}"
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
def record_metric(metric_type, value, labels = {})
|
|
778
|
+
series = Google::Cloud::Monitoring::V3::TimeSeries.new(
|
|
779
|
+
metric: {
|
|
780
|
+
type: "custom.googleapis.com/rails/#{metric_type}",
|
|
781
|
+
labels: labels
|
|
782
|
+
},
|
|
783
|
+
resource: {
|
|
784
|
+
type: "global",
|
|
785
|
+
labels: { project_id: ENV["GCP_PROJECT_ID"] }
|
|
786
|
+
},
|
|
787
|
+
points: [{
|
|
788
|
+
interval: { end_time: Google::Protobuf::Timestamp.new(seconds: Time.current.to_i) },
|
|
789
|
+
value: { double_value: value }
|
|
790
|
+
}]
|
|
791
|
+
)
|
|
792
|
+
|
|
793
|
+
@client.create_time_series(name: @project_name, time_series: [series])
|
|
794
|
+
end
|
|
795
|
+
end
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
## Costos y Optimización
|
|
799
|
+
|
|
800
|
+
### Calculadora
|
|
801
|
+
|
|
802
|
+
```markdown
|
|
803
|
+
## Estimación mensual para app Rails pequeña-mediana
|
|
804
|
+
|
|
805
|
+
| Servicio | Configuración | Costo/mes |
|
|
806
|
+
|----------|--------------|-----------|
|
|
807
|
+
| Cloud Run | 1M requests, 512Mi | ~$20 |
|
|
808
|
+
| Cloud SQL | db-f1-micro | ~$10 |
|
|
809
|
+
| Memorystore | 1GB Redis | ~$35 |
|
|
810
|
+
| Cloud Storage | 50GB | ~$1 |
|
|
811
|
+
| Cloud CDN | 100GB | ~$8 |
|
|
812
|
+
| Secret Manager | 10 secrets | ~$0.06 |
|
|
813
|
+
| **Total** | | **~$75/mes** |
|
|
814
|
+
|
|
815
|
+
## Optimizaciones:
|
|
816
|
+
- Cloud Run: min-instances=0 para desarrollo
|
|
817
|
+
- Committed Use Discounts (1-3 años)
|
|
818
|
+
- Preemptible VMs para workers
|
|
819
|
+
- Cloud Storage Nearline para backups
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
### Committed Use Discounts
|
|
823
|
+
|
|
824
|
+
```bash
|
|
825
|
+
# Ver recomendaciones
|
|
826
|
+
gcloud recommender recommendations list \
|
|
827
|
+
--project=mi-proyecto \
|
|
828
|
+
--location=us-central1 \
|
|
829
|
+
--recommender=google.compute.commitment.UsageCommitmentRecommender
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
## Checklist de Deployment
|
|
833
|
+
|
|
834
|
+
- [ ] Proyecto GCP creado
|
|
835
|
+
- [ ] APIs habilitadas (Cloud Run, SQL, Storage, etc.)
|
|
836
|
+
- [ ] Service Account con roles mínimos
|
|
837
|
+
- [ ] Cloud SQL instancia creada
|
|
838
|
+
- [ ] Cloud Storage bucket configurado
|
|
839
|
+
- [ ] Secret Manager con secrets
|
|
840
|
+
- [ ] Container Registry con imagen
|
|
841
|
+
- [ ] Cloud Run service desplegado
|
|
842
|
+
- [ ] Cloud SQL Proxy o conexión configurada
|
|
843
|
+
- [ ] Load Balancer + CDN (opcional)
|
|
844
|
+
- [ ] Cloud DNS configurado
|
|
845
|
+
- [ ] SSL/TLS con certificado managed
|
|
846
|
+
- [ ] Cloud Logging habilitado
|
|
847
|
+
- [ ] Alertas en Cloud Monitoring
|
|
848
|
+
- [ ] Backups automáticos verificados
|