Skip to content

Commit 53aaef8

Browse files
committed
add layers
1 parent 48289c8 commit 53aaef8

File tree

12 files changed

+381
-21
lines changed

12 files changed

+381
-21
lines changed

.github/workflows/ci.yml

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,30 @@ jobs:
1616
gdal-version: [2.4, 3.1, 3.2]
1717
steps:
1818
- uses: actions/checkout@v2
19+
20+
- name: Set up Python 3.8
21+
uses: actions/setup-python@v2
22+
with:
23+
python-version: 3.8
24+
25+
- name: Install dependencies
26+
run: |
27+
python -m pip install --upgrade pip
28+
python -m pip install docker boto3 click
29+
1930
- name: Login to DockerHub
2031
uses: docker/login-action@v1
2132
with:
2233
username: ${{ secrets.DOCKERHUB_USERNAME }}
2334
password: ${{ secrets.DOCKERHUB_TOKEN }}
2435

36+
- name: Configure AWS Credentials
37+
uses: aws-actions/configure-aws-credentials@v1
38+
with:
39+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
40+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
41+
aws-region: us-east-1
42+
2543
- name: set amazonlinux version
2644
id: amazonlinux
2745
run: echo "::set-output name=version::$(if [ '${{ matrix.image-name }}' == 'base-2' ]; then echo '-al2'; else echo ''; fi)"
@@ -32,7 +50,7 @@ jobs:
3250
id: common-cache
3351
with:
3452
path: /tmp/docker
35-
key: ${{ matrix.image-name }}-${{ hashFiles(format('common/{0}/Dockerfile', matrix.image-name)) }}
53+
key: ${{ matrix.image-name }}-${{ hashFiles(format('dockerfiles/common/{0}/Dockerfile', matrix.image-name)) }}
3654

3755
- name: Restore Common image
3856
if: steps.common-cache.outputs.cache-hit == 'true'
@@ -41,7 +59,7 @@ jobs:
4159
- name: Build and Cache Common image
4260
if: steps.common-cache.outputs.cache-hit != 'true'
4361
run: |
44-
docker build -f common/${{ matrix.image-name }}/Dockerfile -t ${{ matrix.image-name }}:build .
62+
docker build -f dockerfiles/common/${{ matrix.image-name }}/Dockerfile -t ${{ matrix.image-name }}:build .
4563
mkdir -p /tmp/docker
4664
docker image save -o /tmp/docker/${{ matrix.image-name }}.tar ${{ matrix.image-name }}:build
4765
@@ -50,7 +68,7 @@ jobs:
5068
id: gdal-cache
5169
with:
5270
path: /tmp/docker
53-
key: gdal${{ matrix.gdal-version }}-${{ hashFiles(format('gdal{0}/Dockerfile', matrix.gdal-version)) }}-${{ hashFiles(format('common/{0}/Dockerfile', matrix.image-name)) }}
71+
key: gdal${{ matrix.gdal-version }}-${{ hashFiles(format('dockerfiles/gdal{0}/Dockerfile', matrix.gdal-version)) }}-${{ hashFiles(format('dockerfiles/common/{0}/Dockerfile', matrix.image-name)) }}
5472

5573
- name: Restore gdal image
5674
if: steps.gdal-cache.outputs.cache-hit == 'true'
@@ -61,7 +79,7 @@ jobs:
6179
run: |
6280
docker build \
6381
--build-arg IMAGE_VERSION=${{ matrix.image-name }} \
64-
-f gdal${{ matrix.gdal-version }}/Dockerfile \
82+
-f dockerfiles/gdal${{ matrix.gdal-version }}/Dockerfile \
6583
-t lambgeo/lambda-gdal:${{ matrix.gdal-version }}${{ steps.amazonlinux.outputs.version }} .
6684
mkdir -p /tmp/docker
6785
docker image save \
@@ -75,6 +93,10 @@ jobs:
7593
lambgeo/lambda-gdal:${{ matrix.gdal-version }}${{ steps.amazonlinux.outputs.version }} \
7694
sh -c "cd /local/tests && sh tests.sh"
7795
96+
- name: Build and Deploy layers
97+
if: github.ref == 'refs/heads/master'
98+
run: python scripts/deploy.py ${{ matrix.gdal-version }} ${{ matrix.image-name }} --deploy
99+
78100
- name: Push to DockerHub
79101
if: github.ref == 'refs/heads/master'
80102
run: docker push lambgeo/lambda-gdal:${{ matrix.gdal-version }}${{ steps.amazonlinux.outputs.version }}
@@ -86,7 +108,7 @@ jobs:
86108
for runtime in ${runtimes}; do
87109
docker build \
88110
--build-arg GDAL_VERSION=${{ matrix.gdal-version }} \
89-
-f runtimes/${runtime} \
111+
-f dockerfiles/runtimes/${runtime} \
90112
-t lambgeo/lambda-gdal:${{ matrix.gdal-version }}-${runtime} .
91113
docker push lambgeo/lambda-gdal:${{ matrix.gdal-version }}-${runtime}
92114
done

README.md

Lines changed: 167 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,35 +51,63 @@ Based on lambci/lambda-base-2:build (amazonlinux2) for newer runtimes (e.g pytho
5151
1. Dockerfile
5252

5353
```Dockerfile
54-
FROM lambgeo/lambda-gdal:3.2-python3.8
54+
FROM lambgeo/lambda-gdal:3.2-al2 as gdal
55+
56+
# We use lambci docker image for the runtime
57+
FROM lambci/lambda:build-python3.8
5558

5659
ENV PACKAGE_PREFIX=/var/task
5760

61+
# Bring C libs from lambgeo/lambda-gdal image
62+
COPY --from=gdal /opt/lib/ ${PACKAGE_PREFIX}/lib/
63+
COPY --from=gdal /opt/include/ ${PACKAGE_PREFIX}/include/
64+
COPY --from=gdal /opt/share/ ${PACKAGE_PREFIX}/share/
65+
COPY --from=gdal /opt/bin/ ${PACKAGE_PREFIX}/bin/
66+
ENV \
67+
GDAL_DATA=${PACKAGE_PREFIX}/share/gdal \
68+
PROJ_LIB=${PACKAGE_PREFIX}/share/proj \
69+
GDAL_CONFIG=${PACKAGE_PREFIX}/bin/gdal-config \
70+
GEOS_CONFIG=${PACKAGE_PREFIX}/bin/geos-config \
71+
PATH=${PACKAGE_PREFIX}/bin:$PATH
72+
73+
# Set some useful env
74+
ENV \
75+
LANG=en_US.UTF-8 \
76+
LC_ALL=en_US.UTF-8 \
77+
CFLAGS="--std=c99"
78+
5879
# Copy any local files to the package
5980
COPY handler.py ${PACKAGE_PREFIX}/handler.py
6081

61-
# Install some requirements
82+
# This is needed for rasterio
83+
RUN pip3 install cython numpy --no-binary numpy
84+
85+
# Install some requirements to `/var/task` (using `-t` otpion)
6286
RUN pip install numpy rasterio mercantile --no-binary :all: -t ${PACKAGE_PREFIX}/
6387

64-
# Cleanup the package of useless files
65-
RUN rm -rdf $PACKAGE_PREFIX/boto3/ \
66-
&& rm -rdf $PACKAGE_PREFIX/botocore/ \
67-
&& rm -rdf $PACKAGE_PREFIX/docutils/ \
68-
&& rm -rdf $PACKAGE_PREFIX/dateutil/ \
69-
&& rm -rdf $PACKAGE_PREFIX/jmespath/ \
70-
&& rm -rdf $PACKAGE_PREFIX/s3transfer/ \
71-
&& rm -rdf $PACKAGE_PREFIX/numpy/doc/ \
72-
&& rm -rdf $PREFIX/share/doc \
73-
&& rm -rdf $PREFIX/share/man \
74-
&& rm -rdf $PREFIX/share/hdf*
88+
# Reduce size of the C libs
89+
RUN cd $PACKAGE_PREFIX && find lib -name \*.so\* -exec strip {} \;
90+
91+
# Create package.zip
92+
RUN cd $PACKAGE_PREFIX && zip -r9q /tmp/package.zip *
93+
```
94+
95+
Or if you are working with python, you can use lambgeo pre-build docker images:
96+
97+
```Dockerfile
98+
FROM lambgeo/lambda-gdal:3.2-python3.8
99+
100+
# Copy any local files to the package
101+
COPY handler.py ${PACKAGE_PREFIX}/handler.py
102+
103+
# Install some requirements to `/var/task` (using `-t` otpion)
104+
RUN pip install numpy rasterio mercantile --no-binary :all: -t ${PACKAGE_PREFIX}/
75105

76106
# Reduce size of the C libs
77107
RUN cd $PREFIX && find lib -name \*.so\* -exec strip {} \;
78108

79-
# Copy python files
109+
# Create package.zip
80110
RUN cd $PACKAGE_PREFIX && zip -r9q /tmp/package.zip *
81-
82-
# Copy shared libs
83111
RUN cd $PREFIX && zip -r9q --symlinks /tmp/package.zip lib/*.so* share
84112
RUN cd $PREFIX && zip -r9q --symlinks /tmp/package.zip bin/gdal* bin/ogr* bin/geos* bin/nearblack
85113
```
@@ -97,6 +125,7 @@ Package content should be like:
97125
```
98126
package.zip
99127
|
128+
|___ bin/ # GDAL binaries
100129
|___ lib/ # Shared libraries (GDAL, PROJ, GEOS...)
101130
|___ share/ # GDAL/PROJ data directories
102131
|___ rasterio/
@@ -115,6 +144,128 @@ For Rasterio or other libraries to be aware of GDAL/PROJ C libraries, you need t
115144

116145
Starting with gdal3.1 (PROJ 7.1), you can set `PROJ_NETWORK=ON` to use remote grids: https://proj.org/usage/network.html
117146

147+
---
148+
149+
## AWS Lambda Layers
150+
151+
gdal | amazonlinux version| version | size (Mb)| unzipped size (Mb)| arn
152+
---| ---| ---| ---| ---| ---
153+
3.2 | 1| 1| 43.8| 138.8| arn:aws:lambda:{REGION}:524387336408:layer:gdal32:1
154+
3.1 | 1| 1| 43.7| 128.4| arn:aws:lambda:{REGION}:524387336408:layer:gdal31:1
155+
2.4 | 1| 1| 36.3| 121.3| arn:aws:lambda:{REGION}:524387336408:layer:gdal24:1
156+
---| | ---| ---| ---| ---
157+
3.2 | 2| 1| 44.4| 140| arn:aws:lambda:{REGION}:524387336408:layer:gdal32-al2:1
158+
3.1 | 2| 1| 44.3| 139.7| arn:aws:lambda:{REGION}:524387336408:layer:gdal31-al2:1
159+
2.4 | 2| 1| 36.7| 130| arn:aws:lambda:{REGION}:524387336408:layer:gdal24-al2:1
160+
161+
**Layer content:**
162+
163+
```
164+
layer.zip
165+
|
166+
|___ bin/ # Binaries
167+
|___ lib/ # Shared libraries (GDAL, PROJ, GEOS...)
168+
|___ share/ # GDAL/PROJ data directories
169+
```
170+
171+
The layer content will be unzip in `/opt` directory in AWS Lambda. For the python libs to be able to use the C libraries you have to make sure to set 2 important environment variables:
172+
173+
- **GDAL_DATA:** /opt/share/gdal
174+
- **PROJ_LIB:** /opt/share/proj
175+
176+
### How To
177+
178+
There are 2 ways to use the layers:
179+
180+
#### 1. Simple (No dependencies)
181+
182+
If you don't need to add more runtime dependencies, you can just create a lambda package (zip file) with you lambda handler.
183+
184+
```bash
185+
zip -r9q package.zip handler.py
186+
```
187+
188+
**Content:**
189+
190+
```
191+
package.zip
192+
|___ handler.py # aws lambda python handler
193+
```
194+
195+
**AWS Lambda Config:**
196+
- arn: `arn:aws:lambda:us-east-1:524387336408:layer:gdal32:1` (example)
197+
- env:
198+
- **GDAL_DATA:** /opt/share/gdal
199+
- **PROJ_LIB:** /opt/share/proj
200+
- lambda handler: `handler.handler`
201+
202+
203+
#### 2. Advanced (need other python dependencies)
204+
205+
If your lambda handler needs more dependencies you'll have to use the exact same environment. To ease this you can find the docker images for each lambda on docker hub.
206+
207+
- Create a docker file
208+
209+
```dockerfile
210+
FROM lambgeo/lambda-gdal:3.2-al2
211+
212+
# We use lambci docker image for the runtime
213+
FROM lambci/lambda:build-python3.8
214+
215+
# Bring C libs from lambgeo/lambda-gdal image
216+
COPY --from=gdal /opt/lib/ /opt/lib/
217+
COPY --from=gdal /opt/include/ /opt/include/
218+
COPY --from=gdal /opt/share/ /opt/share/
219+
COPY --from=gdal /opt/bin/ /opt/bin/
220+
ENV \
221+
GDAL_DATA=/opt/share/gdal \
222+
PROJ_LIB=/opt/share/proj \
223+
GDAL_CONFIG=/opt/bin/gdal-config \
224+
GEOS_CONFIG=/opt/bin/geos-config \
225+
PATH=/opt/bin:$PATH
226+
227+
# Set some useful env
228+
ENV \
229+
LANG=en_US.UTF-8 \
230+
LC_ALL=en_US.UTF-8 \
231+
CFLAGS="--std=c99"
232+
233+
ENV PYTHONUSERBASE=/var/task
234+
235+
# Install dependencies
236+
COPY handler.py $PYTHONUSERBASE/handler.py
237+
238+
# Here we use the `--user` option to make sure to not replicate modules.
239+
RUN pip install rio-tiler --user
240+
241+
# Move some files around
242+
RUN mv ${PYTHONUSERBASE}/lib/python3.8/site-packages/* ${PYTHONUSERBASE}/
243+
RUN rm -rf ${PYTHONUSERBASE}/lib
244+
245+
echo "Create archive"
246+
RUN cd $PYTHONUSERBASE && zip -r9q /tmp/package.zip *
247+
```
248+
249+
- create package
250+
```bash
251+
docker build --tag package:latest .
252+
docker run --name lambda -w /var/task -itd package:latest bash
253+
docker cp lambda:/tmp/package.zip package.zip
254+
docker stop lambda
255+
docker rm lambda
256+
```
257+
258+
**Content:**
259+
260+
```
261+
package.zip
262+
|___ handler.py # aws lambda python handler
263+
|___ module1/ # dependencies
264+
|___ module2/
265+
|___ module3/
266+
|___ ...
267+
```
268+
118269

119270
### Refactor
120271

scripts/create-layer.sh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/bin/bash
2+
echo "-----------------------"
3+
echo "Creating lambda layer"
4+
echo "-----------------------"
5+
6+
echo "Remove useless files"
7+
rm -rdf $PREFIX/share/doc \
8+
&& rm -rdf $PREFIX/share/man \
9+
&& rm -rdf $PREFIX/share/hdf*
10+
11+
echo "Strip shared libraries"
12+
cd $PREFIX && find lib -name \*.so\* -exec strip {} \;
13+
14+
echo "Create archives"
15+
cd $PREFIX && zip -r9q --symlinks /tmp/package.zip lib/*.so*
16+
cd $PREFIX && zip -r9q --symlinks /tmp/package.zip share
17+
cd $PREFIX && zip -r9q --symlinks /tmp/package.zip bin/gdal* bin/ogr* bin/geos* bin/nearblack
18+
19+
cp /tmp/package.zip /local/package.zip

0 commit comments

Comments
 (0)