Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions .github/workflows/build-and-push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: "Build and Push"

on:
push:

jobs:
build:
runs-on: ubuntu-latest
name: "Build"
strategy:
matrix:
env: [ "test", "production" ]
hasTag:
- ${{ startsWith(github.ref, 'refs/tags/') }}
exclude:
- hasTag: true
env: "test"
- hasTag: false
env: "production"
env:
APP_ENV: ${{ matrix.env }}
outputs:
dockerImageTag: ${{ steps.docker_meta.outputs.version }}
steps:
- uses: actions/checkout@v4

- name: Setup Go
uses: actions/setup-go@v4

Check failure on line 28 in .github/workflows/build-and-push.yml

View workflow job for this annotation

GitHub Actions / lint-actions

the runner of "actions/setup-go@v4" action is too old to run on GitHub Actions. update the action's version to fix this issue
with:
go-version: '1.23'
cache: true

- name: Build Temporal Server
run: make temporal-server

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1

Check failure on line 37 in .github/workflows/build-and-push.yml

View workflow job for this annotation

GitHub Actions / lint-actions

the runner of "docker/setup-buildx-action@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue

- name: set aws credentials
id: set-aws-credentials
run: |

Check failure on line 41 in .github/workflows/build-and-push.yml

View workflow job for this annotation

GitHub Actions / lint-actions

shellcheck reported issue in this script: SC2129:style:6:5: Consider using { cmd1; cmd2; } >> file instead of individual redirects

Check failure on line 41 in .github/workflows/build-and-push.yml

View workflow job for this annotation

GitHub Actions / lint-actions

shellcheck reported issue in this script: SC2129:style:2:5: Consider using { cmd1; cmd2; } >> file instead of individual redirects
if [[ $APP_ENV == "production" ]]; then
echo "AWS_REGION=us-east-1" >> "$GITHUB_ENV"
echo "AWS_ACCESS_KEY=${{ secrets.AWS_ACCESS_KEY_ID }}" >> "$GITHUB_ENV"
echo "AWS_SECRET_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }}" >> "$GITHUB_ENV"
else
echo "AWS_REGION=ap-south-1" >> "$GITHUB_ENV"
echo "AWS_ACCESS_KEY=${{ secrets.AWS_HEADOUT_TEST_ACCESS_KEY }}" >> "$GITHUB_ENV"
echo "AWS_SECRET_KEY=${{ secrets.AWS_HEADOUT_TEST_SECRET_KEY }}" >> "$GITHUB_ENV"
fi

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1

Check failure on line 53 in .github/workflows/build-and-push.yml

View workflow job for this annotation

GitHub Actions / lint-actions

the runner of "aws-actions/configure-aws-credentials@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue
with:
aws-access-key-id: ${{ env.AWS_ACCESS_KEY }}
aws-secret-access-key: ${{ env.AWS_SECRET_KEY }}
aws-region: ${{ env.AWS_REGION }}

- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1

- name: Docker meta
id: docker_meta
uses: docker/metadata-action@v3

Check failure on line 65 in .github/workflows/build-and-push.yml

View workflow job for this annotation

GitHub Actions / lint-actions

the runner of "docker/metadata-action@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue
with:
images: ${{ steps.login-ecr.outputs.registry }}/container-images/temporal-server
flavour: |
latest=true
Comment on lines +68 to +69

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flavour: is a misspelling of flavor: — the latest=true configuration is silently ignored

GitHub Actions silently ignores unknown with: keys. The correct input name for docker/metadata-action is flavor (American English), not flavour. As written, the latest=true line does nothing. Fix: rename flavour: to flavor:.

Suggested change
flavour: |
latest=true
- name: Docker meta
id: docker_meta
uses: docker/metadata-action@v3
with:
images: ${{ steps.login-ecr.outputs.registry }}/container-images/temporal-server
flavor: |
latest=true
tags: |
type=semver,pattern={{version}}
type=ref,event=branch,prefix=${{ matrix.env }}-,suffix=-{{sha}}-{{date 'YYYYMMDD'}}
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
Prompt for agents
In `.github/workflows/build-and-push.yml` line 68, change `flavour:` to `flavor:`. The `docker/metadata-action` input is named `flavor` (American English); the current British spelling is silently ignored, so `latest=true` has no effect.

tags: |
type=semver,pattern={{version}}
type=ref,event=branch,prefix=${{ matrix.env }}-,suffix=-{{sha}}-{{date 'YYYYMMDD'}}
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}

- name: Build and push
id: docker_build
uses: docker/build-push-action@v2

Check failure on line 77 in .github/workflows/build-and-push.yml

View workflow job for this annotation

GitHub Actions / lint-actions

the runner of "docker/build-push-action@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue
with:
context: .
file: ci/Dockerfile
push: true
tags: ${{ steps.docker_meta.outputs.tags }}
build-args: |
APP_ENV=${{ matrix.env }}
labels: ${{ steps.docker_meta.outputs.labels }}
53 changes: 53 additions & 0 deletions ci/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Dockerfile for Temporal Server with Visibility Caching
# Uses pre-built binary from GitHub Actions

FROM alpine:3.19

ARG APP_ENV=test

# Install runtime dependencies
RUN apk add --no-cache \
ca-certificates \
tzdata \
bash

# Create temporal user and group
RUN addgroup -g 1000 temporal && \
adduser -u 1000 -G temporal -s /bin/sh -D temporal

# Set working directory
WORKDIR /etc/temporal

# Copy the pre-built binary from GitHub Actions build step
COPY temporal-server /usr/local/bin/temporal-server

# Copy default config
COPY config /etc/temporal/config

# Create directories for temporal
RUN mkdir -p /etc/temporal/config /var/log/temporal && \
chown -R temporal:temporal /etc/temporal /var/log/temporal

# Switch to temporal user
USER temporal

# Expose ports
# 7233 - Frontend gRPC
# 7234 - History gRPC
# 7235 - Matching gRPC
# 7239 - Worker gRPC
# 6933 - Frontend membership
# 6934 - History membership
# 6935 - Matching membership
# 6939 - Worker membership
EXPOSE 7233 7234 7235 7239 6933 6934 6935 6939

# Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
CMD ["/usr/local/bin/temporal-server", "--version"]
Comment on lines +46 to +47

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docker HEALTHCHECK uses --version — always passes, never validates service health

The HEALTHCHECK command temporal-server --version is a version-print that exits 0 immediately regardless of service state. It cannot detect that the server is not listening. Replace it with a real connectivity check, for example using tctl or a gRPC health probe. If you don't want to add tctl to the image, a simple TCP check via nc or the grpc-health-probe binary is standard for gRPC services:

HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \
    CMD grpc-health-probe -addr=:7233 || exit 1

Alternatively, install tctl in the image and use tctl --address 127.0.0.1:7233 cluster health.

Prompt for agents
In `ci/Dockerfile` lines 46-47, the HEALTHCHECK command is `temporal-server --version`, which always exits 0 immediately and tells the orchestrator nothing about real service health. Replace it with a check that verifies the gRPC frontend (port 7233) is accepting connections. Option 1: add `grpc-health-probe` to the image (`RUN apk add grpc-health-probe` or download from GitHub releases) and use `CMD grpc-health-probe -addr=:7233`. Option 2: install `temporalcli` and call `temporal cluster health`. Update `start-period` to 60s to give Temporal time to initialize.


# Default entrypoint
ENTRYPOINT ["/usr/local/bin/temporal-server"]

# Default command
CMD ["start"]
185 changes: 185 additions & 0 deletions ci/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# CI/CD Setup for Temporal Server

This directory contains the CI/CD configuration for building and deploying Temporal Server with visibility caching to AWS ECR.

## GitHub Actions Workflow

**File**: `.github/workflows/build-and-push.yml`

### Triggers

- **Push to any branch**: Builds test image
- **Push tag**: Builds production image

### Environments

| Environment | Trigger | AWS Region | ECR Registry |
|-------------|---------|------------|--------------|
| **test** | Push to branch | ap-south-1 | Test ECR |
| **production** | Push tag (e.g., v1.0.0) | us-east-1 | Production ECR |

### Image Tags

Images are tagged with:
- **Branch builds**: `test-<branch>-<sha>-<date>` (e.g., `test-main-a5af156-20260609`)
- **Tag builds**: `<version>` (e.g., `v1.25.0-cache`)
- **Latest**: `latest` (for main branch)

### Secrets Required

Configure these in GitHub repository settings:

**Production (us-east-1):**
- `AWS_ACCESS_KEY_ID`
- `AWS_SECRET_ACCESS_KEY`

**Test (ap-south-1):**
- `AWS_HEADOUT_TEST_ACCESS_KEY`
- `AWS_HEADOUT_TEST_SECRET_KEY`

## Build Process

1. **Checkout code**
2. **Setup Go 1.23**
3. **Build binary**: `make temporal-server`
4. **Setup Docker Buildx**
5. **Configure AWS credentials** (based on environment)
6. **Login to ECR**
7. **Build and push Docker image**

## Docker Image

**Base**: Alpine Linux 3.19
**Size**: ~50-100MB
**User**: Non-root (temporal:1000)
**Binary**: Pre-built in GitHub Actions

### Exposed Ports

- 7233 - Frontend gRPC
- 7234 - History gRPC
- 7235 - Matching gRPC
- 7239 - Worker gRPC
- 6933-6939 - Membership ports

## Deployment

### Test Environment

```bash
# Automatically triggered on push to any branch
git push origin ft/visibility-caching

# Image will be available at:
# <test-ecr-registry>/container-images/temporal-server:test-ft/visibility-caching-<sha>-<date>
```

### Production Environment

```bash
# Create and push a tag
git tag v1.25.0-cache
git push origin v1.25.0-cache

# Image will be available at:
# <prod-ecr-registry>/container-images/temporal-server:v1.25.0-cache
```

## Using the Image

### Kubernetes/Helm

```yaml
server:
image:
repository: <ecr-registry>/container-images/temporal-server
tag: v1.25.0-cache
pullPolicy: IfNotPresent
```

### Docker

```bash
# Pull from ECR
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin <ecr-registry>

docker pull <ecr-registry>/container-images/temporal-server:v1.25.0-cache

# Run
docker run -d \
--name temporal \
-p 7233:7233 \
<ecr-registry>/container-images/temporal-server:v1.25.0-cache
```

## Monitoring Build Status

Check GitHub Actions:
- Go to repository → Actions tab
- Look for "Build and Push" workflow
- View logs for each step

## Troubleshooting

### Build Fails

1. Check GitHub Actions logs
2. Verify Go version compatibility
3. Ensure `make temporal-server` works locally

### Push to ECR Fails

1. Verify AWS credentials are configured
2. Check ECR repository exists: `container-images/temporal-server`
3. Verify IAM permissions for ECR push

### Image Not Found

1. Check ECR console for the image
2. Verify tag format matches expected pattern
3. Ensure workflow completed successfully

## Local Testing

To test the Dockerfile locally:

```bash
# Build the binary
make temporal-server

# Build the Docker image
docker build -f ci/Dockerfile -t temporal-server:local .

# Run
docker run --rm temporal-server:local --version
```

## Configuration

The image includes default configuration from `/config` directory.

To use custom configuration:

```bash
docker run -d \
-v $(pwd)/my-config.yaml:/etc/temporal/config/development.yaml \
<ecr-registry>/container-images/temporal-server:v1.25.0-cache \
start --config /etc/temporal/config/development.yaml
```

## Visibility Caching

The image includes visibility caching support. Enable it in your config:

```yaml
persistence:
datastores:
postgres-visibility:
sql:
visibilityCache:
enabled: true
cacheTTLSeconds: 20
```

See `docs/visibility-cache-config-example.yaml` for full configuration.
2 changes: 2 additions & 0 deletions common/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,8 @@ type (
TaskScanPartitions int `yaml:"taskScanPartitions"`
// TLS is the configuration for TLS connections
TLS *auth.TLS `yaml:"tls"`
// VisibilityCache is the configuration for visibility layer caching
VisibilityCache *VisibilityCacheConfig `yaml:"visibilityCache"`
}

// CustomDatastoreConfig is the configuration for connecting to a custom datastore that is not supported by temporal core
Expand Down
60 changes: 60 additions & 0 deletions common/config/visibility_cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package config

import "time"

type (
VisibilityCacheConfig struct {
Enabled bool `yaml:"enabled"`
CacheTTLSeconds int `yaml:"cacheTTLSeconds"`
}

VisibilityCacheConfigLegacy struct {
Enabled bool `yaml:"enabled"`
Type string `yaml:"type"`
InMemory *InMemoryCacheConfig `yaml:"inMemory"`
Redis *RedisCacheConfig `yaml:"redis"`
}

InMemoryCacheConfig struct {
MaxSize int `yaml:"maxSize"`
TTL time.Duration `yaml:"ttl"`
}

RedisCacheConfig struct {
Endpoints []string `yaml:"endpoints"`
Password string `yaml:"password"`
DB int `yaml:"db"`
TTL time.Duration `yaml:"ttl"`
MaxRetries int `yaml:"maxRetries"`
PoolSize int `yaml:"poolSize"`
}
)

const (
VisibilityCacheTypeInMemory = "inmemory"
VisibilityCacheTypeRedis = "redis"
)

func (c *VisibilityCacheConfig) Validate() error {
return nil
}

func (c *VisibilityCacheConfigLegacy) Validate() error {
if !c.Enabled {
return nil
}

if c.Type != VisibilityCacheTypeInMemory && c.Type != VisibilityCacheTypeRedis {
return ErrPersistenceConfig
}

if c.Type == VisibilityCacheTypeInMemory && c.InMemory == nil {
return ErrPersistenceConfig
}

if c.Type == VisibilityCacheTypeRedis && c.Redis == nil {
return ErrPersistenceConfig
}

return nil
}
Loading
Loading